Public: anyone can read this; the crew can change it.

Meter awake time and storage: per app and environment, the two quantities a forge charges for #527

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

This adds a meter for each app: one row for every stretch of time an environment is awake, and an hourly sample of the bytes it stores. Those are the two quantities a forge charges for. `src/core/meter.ts` reads them back for a [from, to) range as awake seconds and GB-hours (bytes × hours / 1e9).

shipped production is serving it deploy 2fa0c24

opened this

This adds a meter for each app: one row for every stretch of time an environment is awake, and an hourly sample of the bytes it stores. Those are the two quantities a forge charges for. src/core/meter.ts reads them back for a [from, to) range as awake seconds and GB-hours (bytes × hours / 1e9).

The meter itself is unchanged from #518. The check on #518 failed on "the fork flow > a mirror whose git is not ready yet is fetched again, and lands". That test is in test/fork-flow.test.ts, which this change doesn't touch. Main has since fixed it (#497) by seeding the upstream only once the mirror job has moved past its first fetch, instead of on a 300 ms timer. This branch now merges main to pick that fix up. Merging needed three conflicts resolved by hand:

  • src/core/schema.ts: main's fleet-operation receipt keeps migration 40, and the meter's tables (meter, storage) move to migration 41.
  • src/apps/runtime.ts: boot keeps main's new way of settling abandoned runs (deploys fail; checks are queued again), then closes any awake interval left open at its last heartbeat.
  • forge.json: the check runs main's file list plus test/meter.test.ts.

The full forge.json check now passes locally: typecheck clean, 647 tests passing across 43 files in 3 shards, none failing. The fork-flow file passed 3 runs out of 3 on its own.

I didn't add the meter test's timing to scripts/test-times.json, which the test runner uses to split files into shards. It's a sub-second file, and recording it with check:record can come later.

To try it:

  • bun test test/meter.test.ts has hand-counted cases: start, sleep, wake and stop add up to the expected seconds, a restart closes the open interval at its last heartbeat, and a fixture directory's storage matches its byte count.
  • The read functions are documented in docs/APPS.md.

🤖 Generated with Claude Code

Closes #505

commented

Fixes the failed check on #527: forge.json was refused because check.run was longer than 1000 characters. Adding test/meter.test.ts pushed it over, and it is now 451 characters.

What changed

  • scripts/test-shards.ts now accepts short test names after --, so crew-wrapup means test/crew-wrapup.test.ts. A name that doesn't match ^[a-z0-9][a-z0-9-]{0,99}$ is refused, so a name can't point outside test/. Full paths before -- still work as before.
  • scripts/shards.ts gains splitArgs, which does this split as a small function on plain data.
  • forge.json's check.run lists the same 43 test files by short name.
  • test/shards.test.ts gains a test that reads forge.json with the forge's own parseCheck, so a check that is too long now fails locally, not after a push. It also checks that every named test file exists, so a typo can't quietly drop a file from the run.

Choices

  • I kept the explicit list of test files and did not switch to a glob. forge.json stays the one place that says which tests the check runs.

Try it

  • bun x tsc --noEmit passes.
  • bun test test/shards.test.ts: 26 pass.
  • The full check.run from forge.json: 654 tests in 43 files across 3 shards, 0 failures.

🤖 Generated with Claude Code

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs