os-nightly-tiers — machine-findable marker for this generated card. ⛔ Do not delete this line: it is how the nightly finds this card instead of filing a new one every night.
nightly-tiers: red on main
Swept 2026-09-13T05:54:23.733Z · expected every 24h while this card stands (cron 29 5 * * * UTC) · next by 2026-09-14T05:29Z · run log · commit 7135cf057e90dbe6d9177aef17e525da7277f68b · trigger schedule · tiers job result failure.
The Swept line above is this nightly's heartbeat, and it states the cadence that makes
「stalled」 decidable: while the tiers stay red this card is refreshed on that schedule, so a
timestamp still sitting there past the next by deadline means either the standing caller
died or the nightly went green — nothing here files or closes anything on green, so both
readings end at this card. ⛔ Do not carry a cadence over from a sibling patrol anchor: they
differ by up to 4× and each states its own.
The e2e and live test tiers — the files named *.e2e.test.* and *.live.test.* — run here
nightly on main under OS_TEST_TIERS=nightly and nowhere else (the per-PR and merge-queue
Test Core runs under queue). A red here is a real suite that no pull request will red on;
this card is the channel that sees it. Nothing is blocked by it.
⛔ The remedy is never to rename, skip or delete the failing test to make the nightly green.
Reproduce locally with OS_TEST_TIERS=nightly pnpm --filter <pkg> test, fix what it names, and
let the next nightly refresh this card.
Failing files
packages/cli/test/serve-port-readback.e2e.test.ts
shard-1 — last lines of the test log
… (8117 chars elided) …
082�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m 2b (--strict-body) — ⭐ carries the notice, where `warnings` is empty by construction �[33m 10386�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m 3 (protocol parse) — ⭐ carries the notice, where `warnings` is empty by construction �[33m 10145�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m ⭐ converts nothing — the SAME exit reports `[]`, so the field tracks the run �[33m 10235�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m 3b (author-time rules) — the notice rides the rule gate �[33m 10212�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m 3c (capability preflight) — the notice rides the preflight gate �[33m 9931�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m 3e (access-matrix drift) — the notice rides the drift gate �[33m 10317�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m 3f (package docs) — the notice rides the docs gate �[33m 9909�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m 4b (--no-runtime-bundle) — a late exit past every step carries the notice �[33m 10283�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m the catch-all — a THROWN failure still reports the computed notice �[33m 9870�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m catch-all (throw at load) — `conversions` is PRESENT and empty, because step 2 never ran �[33m 10303�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m REGRESSION GUARD — `warnings` and `conversions` stay separate fields �[33m 10510�[2mms�[22m�[39m
@objectstack/cli:test: �[32m✓�[39m �[30m�[45m integration �[49m�[39m test/lint-eval-generator-load-envelope.e2e.test.ts �[2m(�[22m�[2m9 tests�[22m�[2m)�[22m�[33m 102693�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m a coded failure at import surfaces BOTH carriers on the --json face �[33m 9932�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m an errno thrown at import passes through as `code` �[33m 10258�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m the key set is exactly the carriers — no `conversions`, no filler �[33m 10025�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m the hand-thrown "must default-export a function" stays exactly `{error}` �[33m 10069�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m esbuild's BuildFailure carries neither key, so the payload stays bare �[33m 10020�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m the unresolvable path leaks nothing to stderr — and still refuses on stdout �[33m 10606�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m a generator that only WARNS leaks nothing either — and the human face still shows it �[33m 20857�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m the human path is unchanged: still exit 1, still no JSON document �[33m 10449�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m offline eval is untouched: exit 0 and a report, not an error envelope �[33m 10468�[2mms�[22m�[39m
@objectstack/cli:test: �[32m✓�[39m �[30m�[45m integration �[49m�[39m test/lint-strict-warnings.e2e.test.ts �[2m(�[22m�[2m8 tests�[22m�[2m)�[22m�[33m 112966�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m ⭐ the fixture REALLY warns — ≥1 warning and 0 errors on the run under test �[33m 10317�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m without --strict the same stack exits 0 — the default is unchanged �[33m 10352�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m ⭐ with --strict the same stack exits 1 — passed:false, strict:true, failing === warnings �[33m 10086�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m ⭐ the console face says why — the count and the flag, and the count is the measured one �[33m 20724�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m the console face without the flag exits 0 and claims no strict failure �[33m 9998�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m suggestions stay advisory under --strict �[33m 10106�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m an error still fails with and without the flag, and --strict counts the warnings on top �[33m 20999�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m a clean stack exits 0 under --strict, in both faces �[33m 20377�[2mms�[22m�[39m
@objectstack/cli:test: �[32m✓�[39m �[30m�[45m integration �[49m�[39m test/lint-hook-rules-reach-handler-hooks.e2e.test.ts �[2m(�[22m�[2m10 tests�[22m�[2m)�[22m�[33m 101721�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m INTAKE — a handler-authored hook writing a readonly field is refused (error, exit 1) �[33m 10157�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m CONTROL — the same statement as an explicit body is refused identically �[33m 9931�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m INTAKE — the lowered handler is refused at build, exit 1 — unchanged by this card �[33m 10078�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m CONTROL — the explicit body is refused at build identically �[33m 10608�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m INTAKE — the handler-authored hook IS refused here (error, exit 1) — the red-first leg of #16544 �[33m 10171�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m CONTROL — the explicit body is refused here, before and after, so the intake reading is about the door �[33m 10139�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m NEGATIVE CONTROL — a handler-authored hook the family has nothing to say about still passes (exit 0) �[33m 10489�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m INTAKE — `os validate` accepts the inline action target (exit 0, valid, no invalid_type) �[33m 10095�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m PARITY — `os build` accepts the same config (it lowered before its parse all along) �[33m 9730�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m INTAKE — a nameless `functions` array entry is accepted too (the pass names it `anon_fn`; exit 0, valid) �[33m 10309�[2mms�[22m�[39m
@objectstack/cli:test: �[32m✓�[39m �[30m�[45m integration �[49m�[39m test/lint-conversion-notices.e2e.test.ts �[2m(�[22m�[2m6 tests�[22m�[2m)�[22m�[33m 71094�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m ⭐ the fixture REALLY converts — `--json` publishes the notice the run produced �[33m 9690�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m ⭐ converts nothing — the field is PRESENT and EMPTY, so it tracks the run �[33m 10208�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m ⭐ the human face says it too — the notice is printed, with its expiry �[33m 10712�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m ⭐ the human face stays SILENT when nothing converted �[33m 10077�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m catch-all at load — `[]` honestly, because the normalize step never ran �[33m 10147�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m REGRESSION GUARD — the notice is NOT folded into `issues`, and the counts are unchanged �[33m 20251�[2mms�[22m�[39m
@objectstack/cli:test: �[32m✓�[39m �[30m�[45m integration �[49m�[39m test/migrate-apply-refuses-before-ddl.e2e.test.ts �[2m(�[22m�[2m10 tests�[22m�[2m)�[22m�[33m 33691�[2mms�[22m�[39m
@objectstack/cli:test: �[32m✓�[39m �[30m�[45m integration �[49m�[39m test/lint-eval-empty-generator-refusal.e2e.test.ts �[2m(�[22m�[2m10 tests�[22m�[2m)�[22m�[33m 131809�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m refuses on the human face, naming the flag and the empty value it was given �[33m 9845�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m the --json face stays a machine face — one JSON document, nothing on stderr �[33m 10102�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m nothing is minted — the payload key set is exactly `error` �[33m 10012�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m differs from the no-flag run once the elapsed token is normalised �[33m 20488�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m both refuse: same exit code, refusal on stdout, generator never loaded �[33m 20762�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m both answer the same --json envelope: exit 1 and a lone `error` key �[33m 20600�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m `--eval` with the flag absent still runs the offline eval �[33m 9657�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m `--eval --generator <module>` still loads the generator and runs live �[33m 10192�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m an unresolvable generator path refuses exactly as it always has �[33m 10275�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m a plain project lint is untouched �[33m 9869�[2mms�[22m�[39m
@objectstack/cli:test: �[32m✓�[39m �[30m�[45m integration �[49m�[39m test/lint-eval-json-unscorable-stack.e2e.test.ts �[2m(�[22m�[2m13 tests�[22m�[2m)�[22m�[33m 131008�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m a top-level poisoned getter: stdout is JSON, the case FAILED, the cause is named �[33m 10030�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m a poisoned getter BELOW the top level is caught too — the schema parse walks there �[33m 10236�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m ⛔ a silent swallow would be caught: the unscorable case is not scored as clean �[33m 10202�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m offline mode is untouched: exit 0 and every golden case passes �[33m 10072�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m a generator that THROWS is still a generation error, not a scoring one �[33m 10129�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m ⭐ every generation threw ⇒ meanScore 0 on the --json face, never 100 �[33m 9689�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m off-shape stack manifest-as-string is a SCORED case with schema errors, never a generationError �[33m 10013�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m off-shape stack objects-as-string is a SCORED case with schema errors, never a generationError �[33m 9734�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m off-shape stack objects-as-number is a SCORED case with schema errors, never a generationError �[33m 9912�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m off-shape stack objects-as-null is a SCORED case with schema errors, never a generationError �[33m 10560�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m off-shape stack nested-wrong-types is a SCORED case with schema errors, never a generationError �[33m 9967�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m off-shape stack bare-string is a SCORED case with schema errors, never a generationError �[33m 10301�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m a generator that cannot be loaded still takes the generator-load JSON exit �[33m 10152�[2mms�[22m�[39m
@objectstack/cli:test: �[32m✓�[39m �[30m�[45m integration �[49m�[39m test/artifact-pinned-boot.e2e.test.ts �[2m(�[22m�[2m7 tests�[22m�[2m)�[22m�[33m 81472�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m boots an https:// artifact, overriding the image's OS_ARTIFACT_PATH default �[33m 13658�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m boots a file:// artifact — the volume-mount workflow �[33m 13686�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m boots unpinned, and says so rather than claiming verification �[33m 13798�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m refuses a hash mismatch, naming expected AND actual, and exits non-zero �[33m 9892�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m refuses an unreachable artifact instead of degrading to an empty kernel �[33m 10428�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m never prints the credential of a pre-signed URL — on the real refusal path �[33m 9865�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m refuses an artifact built for another protocol major (acceptance #4) �[33m 10128�[2mms�[22m�[39m
@objectstack/cli:test: �[32m✓�[39m �[30m�[45m integration �[49m�[39m test/run-dev-cwd-tsconfig-redirect.e2e.test.ts �[2m(�[22m�[2m3 tests�[22m�[2m)�[22m�[33m 29700�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m loads its command set anyway, and says what it re-ran �[33m 11816�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m still explains itself when the pin is defeated, and refuses the rebuild remedy �[33m 8268�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m CONTROL — from the repo root nothing is redirected, so nothing is re-exec'd �[33m 9610�[2mms�[22m�[39m
@objectstack/cli:test: �[32m✓�[39m �[30m�[45m integration �[49m�[39m test/build-json-undeclared-key-parity.e2e.test.ts �[2m(�[22m�[2m6 tests�[22m�[2m)�[22m�[33m 82614�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m reports the planted key in the payload, and the stack still builds (exit 0) �[33m 10199�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m carries the SAME undeclared-key set `os validate --json` reports on the same tree �[33m 20765�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m folds them in BESIDE the authoring-rule advisories, in the shape `os validate --json` uses �[33m 9842�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m adds NO new top-level key to the payload — this fills a declared key, it is not a new surface �[33m 11028�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m CONTROL — a clean stack reports no undeclared-key line on either face �[33m 20816�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m leaves the TEXT face saying what it always said �[33m 9957�[2mms�[22m�[39m
@objectstack/cli:test: �[32m✓�[39m �[30m�[45m integration �[49m�[39m test/diff-usage-error-stream.e2e.test.ts �[2m(�[22m�[2m12 tests�[22m�[2m)�[22m�[33m 30342�[2mms�[22m�[39m
@objectstack/cli:test: �[32m✓�[39m �[30m�[45m integration �[49m�[39m test/serve-ready-degraded-boot.e2e.test.ts �[2m(�[22m�[2m2 tests�[22m�[2m)�[22m�[33m 9634�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m prints ready AND the missing core service in the same output, with no unconditional tick �[33m 4356�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m leaves a healthy boot printing exactly the tick it always printed �[33m 5273�[2mms�[22m�[39m
@objectstack/cli:test: �[32m✓�[39m �[30m�[45m integration �[49m�[39m test/json-stdout-purity.e2e.test.ts �[2m(�[22m�[2m41 tests�[22m�[2m)�[22m�[33m 145492�[2mms�[22m�[39m
@objectstack/cli:test: �[32m✓�[39m �[30m�[45m integration �[49m�[39m test/init-created-files-summary.e2e.test.ts �[2m(�[22m�[2m4 tests�[22m�[2m)�[22m�[33m 20174�[2mms�[22m�[39m
@objectstack/cli:test: �[32m✓�[39m �[30m�[45m integration �[49m�[39m test/create-refuses-invalid-project-name.e2e.test.ts �[2m(�[22m�[2m6 tests�[22m�[2m)�[22m�[33m 51339�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m refuses the card's input and writes NOTHING �[33m 10423�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m accepts a valid name — the positive control for the absence check above �[33m 10513�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m refuses a name only the COMPOSED length catches, which `os init` must keep accepting �[33m 30397�[2mms�[22m�[39m
@objectstack/cli:test: �[32m✓�[39m �[30m�[45m integration �[49m�[39m test/compile-artifact-packages.e2e.test.ts �[2m(�[22m�[2m4 tests�[22m�[2m)�[22m�[33m 42312�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m writes `manifest` and NO `packages` key �[33m 11341�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m carries both package manifests through to the written artifact, in order �[33m 10513�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m keeps the singular `manifest` beside it — retained, not replaced �[33m 10488�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m exits non-zero rather than writing an artifact in the unreserved shape �[33m 9962�[2mms�[22m�[39m
@objectstack/cli:test: �[32m✓�[39m �[30m�[45m integration �[49m�[39m test/migrate-plan-exits.e2e.test.ts �[2m(�[22m�[2m1 test�[22m�[2m)�[22m�[33m 7020�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m returns from a composed host stack that armed an unreleasable handle in init() �[33m 7018�[2mms�[22m�[39m
@objectstack/cli:test: �[32m✓�[39m �[30m�[45m integration �[49m�[39m test/i18n-check-platform-bucket.e2e.test.ts �[2m(�[22m�[2m5 tests�[22m�[2m)�[22m�[33m 51969�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m reports 100% and exits 0 under --strict --threshold=100 �[33m 10374�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m is the SAME invocation that failed before — the control is --include-platform �[33m 10232�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m prints the hidden-bucket hint on the console face �[33m 10137�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m ⛔ still gates the package that SHIPS the baseline, with no flag at all �[33m 10631�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m accepts --no-include-platform, so the third state really is three states �[33m 10576�[2mms�[22m�[39m
@objectstack/cli:test: �[32m✓�[39m �[30m�[45m integration �[49m�[39m test/generate-agent-retired.e2e.test.ts �[2m(�[22m�[2m9 tests�[22m�[2m)�[22m�[33m 30061�[2mms�[22m�[39m
@objectstack/cli:test: �[32m✓�[39m �[30m�[45m integration �[49m�[39m test/serve-boot-diagnostics.e2e.test.ts �[2m(�[22m�[2m2 tests�[22m�[2m)�[22m�[33m 26839�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m prints the boot WARN a plugin logged while the banner was being assembled �[33m 13371�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m streams boot output live at --log-level debug instead of hiding it �[33m 13460�[2mms�[22m�[39m
@objectstack/cli:test: �[32m✓�[39m �[30m�[42m unit �[49m�[39m test/vitest-resolution-base-collapse.e2e.test.ts �[2m(�[22m�[2m13 tests�[22m�[2m)�[22m�[32m 200�[2mms�[22m�[39m
@objectstack/cli:test: �[32m✓�[39m �[30m�[45m integration �[49m�[39m test/serve-app-runtime-hooks.e2e.test.ts �[2m(�[22m�[2m1 test�[22m�[2m)�[22m�[33m 19901�[2mms�[22m�[39m
@objectstack/cli:test: �[33m�[2m✓�[22m�[39m runs onEnable, so the declared script action has a handler �[33m 9833�[2mms�[22m�[39m
@objectstack/cli:test: �[32m✓�[39m �[30m�[45m integration �[49m�[39m test/config-miss-stdout-purity.e2e.test.ts �[2m(�[22m�[2m174 tests�[22m�[2m)�[22m�[33m 320327�[2mms�[22m�[39m
@objectstack/cli:test:
@objectstack/cli:test: �[2m Test Files �[22m �[1m�[32m34 passed�[39m�[22m�[90m (34)�[39m
@objectstack/cli:test: �[2m Tests �[22m �[1m�[32m448 passed�[39m�[22m�[90m (448)�[39m
@objectstack/cli:test: �[2m Start at �[22m 05:40:47
@objectstack/cli:test: �[2m Duration �[22m 796.81s�[2m (transform 15.85s, setup 0ms, import 26.86s, tests 2136.83s, environment 4ms)�[22m
@objectstack/cli:test:
@objectstack/cli:test: JSON report written to /home/runner/work/objectstack/objectstack/packages/cli/.nightly-tiers/vitest-report.json
Tasks: 58 successful, 58 total
Cached: 0 cached, 58 total
Time: 19m34.638s
Summary: /home/runner/work/objectstack/objectstack/.turbo/runs/3JGE8MJn40iIBmnVMJsU94iIyWo.json
shard-2 — last lines of the test log
@objectstack/cli:test: �[31m+ // driver here has a `config` property — SqlDriver's is knex-shaped,�[39m
@objectstack/cli:test: �[31m+ // MongoDBDriver's holds a top-level `url`, InMemoryDriver's is `{}` — so�[39m
@objectstack/cli:test: �[31m+ // returning early on "has a config" meant the mongo and in-memory arms�[39m
@objectstack/cli:test: �[31m+ // below never ran and every non-SqlDriver boot banner read `(unknown)`.�[39m
@objectstack/cli:test: �[31m+ // `driver.url` still covers a driver that keeps its DSN on the instance.�[39m
@objectstack/cli:test: �[31m+ const url = describeDriverConnection(cfg)�[39m
@objectstack/cli:test: �[31m+ ?? (typeof driver.url === 'string' && driver.url ? redactConnectionUrl(driver.url) : undefined);�[39m
@objectstack/cli:test: �[31m+�[39m
@objectstack/cli:test: �[31m+ // A memory driver has no address to show — say so, rather than�[39m
@objectstack/cli:test: �[31m+ // `(unknown)`, which reads as "we looked for one and failed".�[39m
@objectstack/cli:test: �[31m+ //�[39m
@objectstack/cli:test: �[31m+ // `sqliteFile` is the same config read a second time asking a DIFFERENT�[39m
@objectstack/cli:test: �[31m+ // question: not "what do I print" but "which file on this filesystem", so�[39m
@objectstack/cli:test: �[31m+ // a caller can `stat` it. Absent for every driver that serves no on-disk�[39m
@objectstack/cli:test: �[31m+ // SQLite file, which is the majority of them.�[39m
@objectstack/cli:test: �[31m+ return {�[39m
@objectstack/cli:test: �[31m+ label,�[39m
@objectstack/cli:test: �[31m+ url: url ?? (name.endsWith('.memory') ? '(in-memory)' : '(unknown)'),�[39m
@objectstack/cli:test: �[31m+ sqliteFile: describeDriverSqliteFile(cfg),�[39m
@objectstack/cli:test: �[31m+ };�[39m
@objectstack/cli:test: �[31m+ }�[39m
@objectstack/cli:test: �[31m+ return null;�[39m
@objectstack/cli:test: �[31m+ }�[39m
@objectstack/cli:test: �[31m+�[39m
@objectstack/cli:test: �[31m+ /**�[39m
@objectstack/cli:test: �[31m+ * Decide what the ready banner's `Config:`/`Artifact:` row should say�[39m
@objectstack/cli:test: �[31m+ * (#8978).�[39m
@objectstack/cli:test: �[31m+ *�[39m
@objectstack/cli:test: �[31m+ * `relativeConfig` is derived from `args.config` at the top of `run()`,�[39m
@objectstack/cli:test: �[31m+ * before the artifact-fallback branch is decided, and was being handed to�[39m
@objectstack/cli:test: �[31m+ * {@link printServerReady} unconditionally. On an `OS_ARTIFACT_URL` boot�[39m
@objectstack/cli:test: �[31m+ * the `objectstack.config.ts` in cwd is deliberately never executed — the�[39m
@objectstack/cli:test: �[31m+ * boot diagnostics say so plainly a few lines above the banner — yet the�[39m
@objectstack/cli:test: �[31m+ * banner still named it. On the plain artifact-fallback path (no config�[39m
@objectstack/cli:test: �[31m+ * authored, booting from `OS_ARTIFACT_PATH` or the `<cwd>/dist/objectstack.json`�[39m
@objectstack/cli:test: �[31m+ * convention) the row named a config file that does not exist on disk at�[39m
@objectstack/cli:test: �[31m+ * all. Both are the same defect: the row is the surface an operator reads�[39m
@objectstack/cli:test: �[31m+ * to answer "what is this container actually running", and naming what�[39m
@objectstack/cli:test: �[31m+ * did NOT boot points them at the wrong app (cloud#1292).�[39m
@objectstack/cli:test: �[31m+ *�[39m
@objectstack/cli:test: �[31m+ * - `pinnedArtifact` set (OS_ARTIFACT_URL, #8368) → report it. `.display`�[39m
@objectstack/cli:test: �[31m+ * is already resolved and already redacted for a pre-signed URL by the�[39m
@objectstack/cli:test: �[31m+ * resolver, so it is safe to print as-is.�[39m
@objectstack/cli:test: �[31m+ * - `useArtifactFallback` set with no `pinnedArtifact` (OS_ARTIFACT_PATH,�[39m
@objectstack/cli:test: �[31m+ * the `dist/objectstack.json` convention, or an empty/quick-start boot)�[39m
@objectstack/cli:test: �[31m+ * → no config was read and there is no safely-redacted display in hand�[39m
@objectstack/cli:test: �[31m+ * here (OS_ARTIFACT_PATH may itself be a credentialed URL) — omit the�[39m
@objectstack/cli:test: �[31m+ * row rather than name a nonexistent file or risk leaking a secret.�[39m
@objectstack/cli:test: �[31m+ * - Neither set → the ordinary config-boot path; report `relativeConfig`�[39m
@objectstack/cli:test: �[31m+ * exactly as before.�[39m
@objectstack/cli:test: �[31m+ */�[39m
@objectstack/cli:test: �[31m+ export function resolveBannerConfigRow(opts: {�[39m
@objectstack/cli:test: �[31m+ relativeConfig: string;�[39m
@objectstack/cli:test: �[31m+ useArtifactFallback: boolean;�[39m
@objectstack/cli:test: �[31m+ pinnedArtifact?: { display: string };�[39m
@objectstack/cli:test: �[31m+ }): { configFile?: string; artifactSource?: string } {�[39m
@objectstack/cli:test: �[31m+ if (opts.pinnedArtifact) return { artifactSource: opts.pinnedArtifact.display };�[39m
@objectstack/cli:test: �[31m+ if (opts.useArtifactFallback) return {};�[39m
@objectstack/cli:test: �[31m+ return { configFile: opts.relativeConfig };�[39m
@objectstack/cli:test: �[31m+ }�[39m
@objectstack/cli:test: �[31m+�[39m
@objectstack/cli:test: �[31m+ /**�[39m
@objectstack/cli:test: �[31m+ * Collect the automation wiring facts for the startup banner (2026-07-17�[39m
@objectstack/cli:test: �[31m+ * third-party eval: a flow that failed to arm emits no log line to find, so the�[39m
@objectstack/cli:test: �[31m+ * banner reads the binding state off the engine instead).�[39m
@objectstack/cli:test: �[31m+ *�[39m
@objectstack/cli:test: �[31m+ * Every probe is feature-detected so an older `@objectstack/service-automation`�[39m
@objectstack/cli:test: �[31m+ * (without `getTriggerBindingAudit` / `getShadowedFlows` / extended runtime�[39m
@objectstack/cli:test: �[31m+ * states) degrades to the plain count line instead of crashing the banner.�[39m
@objectstack/cli:test: �[31m+ * Returns `undefined` when there is nothing automation-related to show at all.�[39m
@objectstack/cli:test: �[31m+ */�[39m
@objectstack/cli:test: �[31m+ export function collectAutomationSummary(�[39m
@objectstack/cli:test: �[31m+ kernel: any,�[39m
@objectstack/cli:test: �[31m+ declaredFlowCount: number,�[39m
@objectstack/cli:test: �[31m+ ): AutomationReadySummary | undefined {�[39m
@objectstack/cli:test: �[31m+ let automation: any;�[39m
@objectstack/cli:test: �[31m+ try { automation = kernel?.getService?.('automation'); } catch { /* not registered */ }�[39m
@objectstack/cli:test: �[31m+�[39m
@objectstack/cli:test: �[31m+ if (!automation) {�[39m
@objectstack/cli:test: �[31m+ return declaredFlowCount > 0�[39m
@objectstack/cli:test: �[31m+ ? {�[39m
@objectstack/cli:test: �[31m+ enabled: false,�[39m
@objectstack/cli:test: �[31m+ declaredFlowCount,�[39m
@objectstack/cli:test: �[31m+ flowCount: 0,�[39m
@objectstack/cli:test: �[31m+ boundCount: 0,�[39m
@objectstack/cli:test: �[31m+ triggerTypes: [],�[39m
@objectstack/cli:test: �[31m+ unbound: [],�[39m
@objectstack/cli:test: �[31m+ unknownObject: [],�[39m
@objectstack/cli:test: �[31m+ shadowed: [],�[39m
@objectstack/cli:test: �[31m+ draftCount: 0,�[39m
@objectstack/cli:test: �[31m+ }�[39m
@objectstack/cli:test: �[31m+ : undefined;�[39m
@objectstack/cli:test: �[31m+ }�[39m
@objectstack/cli:test: �[31m+�[39m
@objectstack/cli:test: �[31m+ /**�[39m
@objectstack/cli:test: �[31m+ * One flow body's provenance, as the engine spells it (`FlowContender` in�[39m
@objectstack/cli:test: �[31m+ * `@objectstack/service-automation`): a code-shipped artifact, or an�[39m
@objectstack/cli:test: �[31m+ * ADR-0005 runtime overlay row in `sys_metadata`.�[39m
@objectstack/cli:test: �[31m+ *�[39m
@objectstack/cli:test: �[31m+ * Declared structurally, like every other shape this function reads off the�[39m
@objectstack/cli:test: �[31m+ * engine. The probes below are feature-detected precisely so a host running�[39m
@objectstack/cli:test: �[31m+ * an OLDER automation package still boots its banner, and a nominal import�[39m
@objectstack/cli:test: �[31m+ * would type these reads against the CURRENT package while the runtime�[39m
@objectstack/cli:test: �[31m+ * deliberately tolerates a previous one.�[39m
@objectstack/cli:test: �[31m+ */�[39m
@objectstack/cli:test: �[31m+ type Contender = { source: 'package' | 'runtime'; packageId?: string };�[39m
@objectstack/cli:test: �[31m+�[39m
@objectstack/cli:test: �[31m+ let states: Array<{�[39m
@objectstack/cli:test: �[31m+ name: string;�[39m
@objectstack/cli:test: �[31m+ enabled: boolean;�[39m
@objectstack/cli:test: �[31m+ bound: boolean;�[39m
@objectstack/cli:test: �[31m+ status?: string;�[39m
@objectstack/cli:test: �[31m+ triggerType?: string;�[39m
@objectstack/cli:test: �[31m+ object?: string;�[39m
@objectstack/cli:test: �[31m+ // [#12028] `getFlowRuntimeStates()` has attached these two per row since�[39m
@objectstack/cli:test: �[31m+ // #11997 whenever a bare name had more than one contender. Named here so a�[39m
@objectstack/cli:test: �[31m+ // later read is type-checked against the row's real shape — casting past�[39m
@objectstack/cli:test: �[31m+ // this annotation is how the field goes back to being declared and unread,�[39m
@objectstack/cli:test: �[31m+ // which is the whole defect this banner line closes.�[39m
@objectstack/cli:test: �[31m+ armedFrom?: Contender;�[39m
@objectstack/cli:test: �[31m+ shadowed?: Contender[];�[39m
@objectstack/cli:test: �[31m+ }> = [];�[39m
@objectstack/cli:test: �[31m+ try { states = automation.getFlowRuntimeStates?.() ?? []; } catch { /* older engine */ }�[39m
@objectstack/cli:test: �[31m+ if (states.length === 0 && declaredFlowCount === 0) return undefined;�[39m
@objectstack/cli:test: �[31m+�[39m
@objectstack/cli:test: �[31m+ let triggerTypes: string[] = [];�[39m
@objectstack/cli:test: �[31m+ try { triggerTypes = automation.getRegisteredTriggerTypes?.() ?? []; } catch { /* older engine */ }�[39m
@objectstack/cli:test: �[31m+�[39m
@objectstack/cli:test: �[31m+ let unbound: Array<{ flowName: string; triggerType: string; reason: string }> = [];�[39m
@objectstack/cli:test: �[31m+ try { unbound = automation.getTriggerBindingAudit?.() ?? []; } catch { /* older engine */ }�[39m
@objectstack/cli:test: �[31m+�[39m
@objectstack/cli:test: �[31m+ // [#12028] Same-named definitions: which body armed, and which lost. Read�[39m
@objectstack/cli:test: �[31m+ // from the engine's dedicated receipt rather than from `states` above, for�[39m
@objectstack/cli:test: �[31m+ // one measured reason — `getFlowRuntimeStates()` can only attach the receipt�[39m
@objectstack/cli:test: �[31m+ // to a row it is already emitting, i.e. to a name still in the flow map,�[39m
@objectstack/cli:test: �[31m+ // whereas `getShadowedFlows()` returns every receipt the boot pull recorded.�[39m
@objectstack/cli:test: �[31m+ // A contested name is worth saying out loud either way.�[39m
@objectstack/cli:test: �[31m+ //�[39m
@objectstack/cli:test: �[31m+ // Feature-detected exactly like the `unbound` probe above and with nothing�[39m
@objectstack/cli:test: �[31m+ // more: optional call, `?? []`, `catch` for an older engine. No extra�[39m
@objectstack/cli:test: �[31m+ // tolerance — the neighbouring read is the standard here.�[39m
@objectstack/cli:test: �[31m+ let shadowing: Array<{ name: string; armed: Contender; shadowed: Contender[] }> = [];�[39m
@objectstack/cli:test: �[31m+ try { shadowing = automation.getShadowedFlows?.() ?? []; } catch { /* older engine */ }�[39m
@objectstack/cli:test: �[31m+�[39m
@objectstack/cli:test: �[31m+ // Dead bindings: a bound record-change flow whose target object nobody�[39m
@objectstack/cli:test: �[31m+ // registered — the hook is filtered to a name that never writes.�[39m
@objectstack/cli:test: �[31m+ const unknownObject: Array<{ flowName: string; object: string }> = [];�[39m
@objectstack/cli:test: �[31m+ let ql: any;�[39m
@objectstack/cli:test: �[31m+ try { ql = kernel?.getService?.('objectql'); } catch { /* absent */ }�[39m
@objectstack/cli:test: �[31m+ if (ql && typeof ql.getObject === 'function') {�[39m
@objectstack/cli:test: �[31m+ for (const s of states) {�[39m
@objectstack/cli:test: �[31m+ if (!s.bound || !s.object || s.triggerType !== 'record_change') continue;�[39m
@objectstack/cli:test: �[31m+ let known: unknown;�[39m
@objectstack/cli:test: �[31m+ try { known = ql.getObject(s.object); } catch { known = undefined; }�[39m
@objectstack/cli:test: �[31m+ if (!known) unknownObject.push({ flowName: s.name, object: s.object });�[39m
@objectstack/cli:test: �[31m+ }�[39m
@objectstack/cli:test: �[31m+ }�[39m
@objectstack/cli:test: �[31m+�[39m
@objectstack/cli:test: �[31m+ return {�[39m
@objectstack/cli:test: �[31m+ enabled: true,�[39m
@objectstack/cli:test: �[31m+ declaredFlowCount,�[39m
@objectstack/cli:test: �[31m+ flowCount: states.length,�[39m
@objectstack/cli:test: �[31m+ boundCount: states.filter((s) => s.bound).length,�[39m
@objectstack/cli:test: �[31m+ triggerTypes,�[39m
@objectstack/cli:test: �[31m+ unbound,�[39m
@objectstack/cli:test: �[31m+ unknownObject,�[39m
@objectstack/cli:test: �[31m+ // A receipt that displaced nothing is not a contested name. The engine�[39m
@objectstack/cli:test: �[31m+ // already refuses to record one, and the banner keeps its own end of that�[39m
@objectstack/cli:test: �[31m+ // guarantee here rather than inheriting it: a benign boot must print no�[39m
@objectstack/cli:test: �[31m+ // shadowing line at all, and this is where "benign" is decided.�[39m
@objectstack/cli:test: �[31m+ shadowed: shadowing�[39m
@objectstack/cli:test: �[31m+ .filter((r) => r.shadowed.length > 0)�[39m
@objectstack/cli:test: �[31m+ .map((r) => ({ flowName: r.name, armed: r.armed, shadowedCount: r.shadowed.length })),�[39m
@objectstack/cli:test: �[31m+ draftCount: states.filter((s) => s.enabled && (s.status ?? 'draft') === 'draft').length,�[39m
@objectstack/cli:test: �[31m+ };�[39m
@objectstack/cli:test: �[31m+ }�[39m
@objectstack/cli:test: �[31m+�[39m
@objectstack/cli:test:
@objectstack/cli:test: �[36m �[2m❯�[22m test/serve-port-readback.e2e.test.ts:�[2m247:9�[22m�[39m
@objectstack/cli:test: �[90m245|�[39m 'the ready banner no longer derives its API row from `resolveA…
@objectstack/cli:test: �[90m246|�[39m + 'if it now uses the REQUESTED port, the #12525 read-back is …
@objectstack/cli:test: �[90m247|�[39m ).toContain('externalBaseOrigin: resolveAuthBaseUrl(boundPort).b…
@objectstack/cli:test: �[90m |�[39m �[31m^�[39m
@objectstack/cli:test: �[90m248|�[39m �[34mexpect�[39m(
@objectstack/cli:test: �[90m249|�[39m serveSource�[33m,�[39m
@objectstack/cli:test:
@objectstack/cli:test: �[31m�[2m⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/2]⎯�[22m�[39m
@objectstack/cli:test:
@objectstack/cli:test: JSON report written to /home/runner/work/objectstack/objectstack/packages/cli/.nightly-tiers/vitest-report.json
@objectstack/cli:test: ELIFECYCLE Test failed. See above for more details.
::error::command (/home/runner/work/objectstack/objectstack/packages/cli) /opt/hostedtoolcache/node/22.23.2/x64/bin/pnpm run test --shard=2/2 --reporter=default --reporter=json --outputFile.json=.nightly-tiers/vitest-report.json exited (1)
@objectstack/cli#test: ERROR command (/home/runner/work/objectstack/objectstack/packages/cli) /opt/hostedtoolcache/node/22.23.2/x64/bin/pnpm run test --shard=2/2 --reporter=default --reporter=json --outputFile.json=.nightly-tiers/vitest-report.json exited (1)
Tasks: 57 successful, 58 total
Cached: 0 cached, 58 total
Time: 16m37.493s
Summary: /home/runner/work/objectstack/objectstack/.turbo/runs/3JGDm1kSQPisDAVgJMEZWOZu5eU.json
Failed: @objectstack/cli#test
ERROR run failed: command exited (1)
Filed by .github/workflows/test-nightly-tiers.yml. Generated by Claude Code
os-nightly-tiers — machine-findable marker for this generated card. ⛔ Do not delete this line: it is how the nightly finds this card instead of filing a new one every night.
nightly-tiers: red on main
Swept 2026-09-13T05:54:23.733Z · expected every 24h while this card stands (cron
29 5 * * *UTC) · next by 2026-09-14T05:29Z · run log · commit7135cf057e90dbe6d9177aef17e525da7277f68b· triggerschedule· tiers job resultfailure.The
Sweptline above is this nightly's heartbeat, and it states the cadence that makes「stalled」 decidable: while the tiers stay red this card is refreshed on that schedule, so a
timestamp still sitting there past the
next bydeadline means either the standing callerdied or the nightly went green — nothing here files or closes anything on green, so both
readings end at this card. ⛔ Do not carry a cadence over from a sibling patrol anchor: they
differ by up to 4× and each states its own.
The
e2eandlivetest tiers — the files named*.e2e.test.*and*.live.test.*— run herenightly on
mainunderOS_TEST_TIERS=nightlyand nowhere else (the per-PR and merge-queueTest Core runs under
queue). A red here is a real suite that no pull request will red on;this card is the channel that sees it. Nothing is blocked by it.
⛔ The remedy is never to rename, skip or delete the failing test to make the nightly green.
Reproduce locally with
OS_TEST_TIERS=nightly pnpm --filter <pkg> test, fix what it names, andlet the next nightly refresh this card.
Failing files
packages/cli/test/serve-port-readback.e2e.test.tsshard-1 — last lines of the test log
shard-2 — last lines of the test log
Filed by
.github/workflows/test-nightly-tiers.yml. Generated by Claude Code