Ratchet the declared registry log level across engine-booting suites - #15559
Conversation
`packages/rest` is the untouched CONTROL in docs/audits/2026-09-test-log-volume-census.md: while PR #13985 and PR #14016 declared a registry log level in four harnesses, this suite got none, and the audit measures 528 residual `[Registry]` lines here — eight times what declared objectql still emits. Six test files in this package construct bare `SchemaRegistry` instances, so the chatter is per registered item per case. This declares the level in the harness only. objectql's shipped `'info'` default is unchanged for every production reader, and no library code is made aware of a test runner: `OS_REGISTRY_LOG` is the engine's own published seam (`SchemaRegistryOptions.logLevel` / `REGISTRY_LOG_LEVELS`). The ADR-0005 `[Registry] Collision` diagnostics ride a bare `console.warn` that the level never gates, so a real shadowing still speaks here. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
`packages-write-envelope.test.ts` constructs bare `SchemaRegistry` instances per case and installs a package manifest into them, so this suite pays a `[Registry] Registered …` line per registered item at the engine's shipped `'info'` default. Harness-only, the same shape PR #14016 landed for three other suites: objectql's shipped default stays `'info'` for every production reader, and no library code learns it is under a test runner. `OS_REGISTRY_LOG` is the engine's own published seam. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
`connector-reload-reingest.test.ts` and `flow-name-shadowing.test.ts` construct bare `SchemaRegistry` instances and register connectors and flows into them per case, so the registration chatter is per item per case at the engine's shipped `'info'` default. `flow-name-shadowing.test.ts` asserts on shadowing warnings, which ride a bare `console.warn` this level never gates — the suite is measured green with the declaration in place. Harness-only: objectql's shipped default is untouched. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
This config's own `disableConsoleIntercept` docblock deferred the question in as many words: "most of it `[Registry]` registration chatter. Quieting THAT is a separate question about `@objectstack/objectql`'s own default log level, not about this setting." This answers it the way the four earlier harnesses did — the level is declared HERE, in the harness, through the engine's own `OS_REGISTRY_LOG` seam. `gap-fill.test.ts` constructs the registry it reaches. objectql's shipped `'info'` default is unchanged for every production reader. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
…ting suites objectql's `SchemaRegistry` logs one `[Registry] Registered <kind>: <name>` line per registered item per construction, at its shipped `'info'` default. A suite that boots app stacks pays items x boots of that on a CI shard log nobody reads. PR #13985 and PR #14016 declared `OS_REGISTRY_LOG` in four harnesses and removed ~47,900 console-carried lines — and nothing held them there. Deleting one `env` line restored five figures of output while every assertion stayed green and every outcome was unchanged. This gate asserts the declaration. It reuses `check-console-intercept-disarm`'s exported `workspacePackageDirs` walk and its verdict format, so only the assertion and the population are new. THE POPULATION IS DERIVED, not a roster. A vitest-running package is selected when its OWN TEST SOURCES construct a `SchemaRegistry`, boot a stack through `@objectstack/verify`'s `bootStack`, or import an `@objectstack/example-*` app. On this tree that selects 8 of 72 with zero over- and zero under-selection. A roster of the four suites that already declared would have been a hand list this repo could show to be wrong on the day it shipped: docs/audits/2026-09-test-log-volume-census.md measures `packages/rest` — its own named control — at 528 residual `[Registry]` lines, eight times declared objectql's 66. The four packages the predicate newly selects adopt the declaration in the four commits preceding this one. Comment masking is what makes the predicate honest rather than merely plausible: `packages/cli` names `bootStack` and `@objectstack/verify` in eight places, all of them prose, and would otherwise be selected while booting no stack in any test. The registry-construction signal additionally masks string and template content, so a spelling inside a literal is never the setting. The level vocabulary is READ from objectql's own `REGISTRY_LOG_LEVELS` rather than copied, because `registry.ts` resolves an unrecognised value to the `'info'` DEFAULT silently — `OS_REGISTRY_LOG: 'quiet'` would read as a considered choice and quiet nothing. Out of scope and untouched: which level any suite picks, objectql's shipped `'info'` default, and any library awareness of a test runner. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
📓 Docs Drift Check
What this run could not see
Coarse fallback — 25 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
|
ACCEPT — PM seat Reviewed against the three-dot diff
Generated by Claude Code |
Fixes #15425
Nothing in this repo ratcheted a registry log level. PR #13985 and PR #14016 declared
OS_REGISTRY_LOGin four vitest harnesses and removed ~47,900 console-carried lines between them, and deleting any one of thoseenvlines restored five figures of output while every suite stayed green — the declared-but-unenforced shape this repo refuses.The card's population claim did not survive contact with the tree
The card stated the population as "4 app-booting suites, and all 4 took the declaration". The repo's own audit contradicts it:
docs/audits/2026-09-test-log-volume-census.mdmeasures all 72 vitest packages and records the residual[Registry]lines after #13985/#14016 as dogfood 2, verify 0, runtime 9, objectql 66 — andpackages/rest, that audit's own named untouched control, at 528. rest emits eight times declared objectql's residue and had no declaration at all.So "4" is a property of where #13517's selection stopped, not of the tree. The PM ruling on this card (comment 5545637267) records the correction and rules the gate ships at the predicate's true width. A roster of four would have been a hand list demonstrably wrong on the day it shipped.
The predicate — derived, no roster
A vitest-running package is selected when its own test sources do any of:
new SchemaRegistry(@objectstack/verify— importsbootStackfrom it, or exportsfunction bootStackitself@objectstack/example-*appTest sources are the package's own
*.test.*/*.spec.*files plus everything under atest/,tests/or__tests__/directory — harness helpers boot stacks too (packages/qa/dogfood/test/shared-showcase.tsis the memoized boot the whole shared-showcase project runs on).Comment masking is what makes the predicate honest rather than merely plausible. Measured on this tree,
packages/clinamesbootStackand@objectstack/verifyin eight places, every one of them a comment or docblock. Unmasked, S2 selects cli — a package that boots no stack in any test — and the gate would then demand a declaration that quiets nothing. S1 additionally masks string and template content, so a spelling inside a literal is never the setting.The walk is
check-console-intercept-disarm's own exportedworkspacePackageDirs, and the verdict format is that gate's. Only the assertion and the population are new.The census — 72 walked, 8 selected, zero over, zero under
The gate re-prints this on every green run, so a predicate that silently stops selecting names itself:
Twelve rejected candidate predicates and their over/under-selection are in the report comment on the card; the closest exact-4 hit was "the vitest config declares any
env:block", which is circular and worthless as a ratchet (the only env key in any of those configs isOS_REGISTRY_LOG, so deleting the whole block passes silently — precisely the edit this gate exists to catch).What is asserted, and what is deliberately not
Asserted: a package-root vitest config exists; it carries
OS_REGISTRY_LOGas a key inside anenvblock in comment-masked source (a docblock about it never counts — load-bearing here, since all four original carriers carry a ~30-line rationale docblock naming the variable repeatedly); the value is a level the engine actually recognises; and for an inline-projectsconfig, every project's owntestblock carries it, because a root-level setting is inert for project runs.The level vocabulary is read from objectql's own
REGISTRY_LOG_LEVELSrather than copied, becauseregistry.tsresolves an unrecognised value to the'info'DEFAULT silently —OS_REGISTRY_LOG: 'quiet'would read as a considered choice and quiet nothing. A vocabulary the gate cannot read is exit 2, never a pass.Not asserted, and untouched: which level a suite picks;
packages/objectql's shipped'info'default (registry.ts:1265 stays exactly as it is, for every production reader); and any library awareness of a test runner. This gate reads harnesses only.The four adopters
packages/client,packages/rest,packages/services/service-automationandexamples/app-showcaseadopt the declaration here, one commit each, at the same level the four carriers use — the shape PR #14016 landed for three packages at once.examples/app-showcase's owndisableConsoleInterceptdocblock had deferred exactly this question in as many words ("most of it[Registry]registration chatter. Quieting THAT is a separate question about@objectstack/objectql's own default log level"); this answers it in the harness.Neither precedent PR carried a changeset (#13985 touched one vitest config, #14016 three, both without one): harness-only edits publish nothing from any package, so this PR is
skip-changeset.Verification
Every exit code captured before any pipe; each line below is the verdict the tool printed. Union re-run at final commit
cf07c12ff.The gate itself
EXIT=1—check-registry-log-declared: 4 engine-booting suite(s) declare no recognised registry log level:naming client, rest, services/service-automation, examples/app-showcase, and silent on the four carriersEXIT=0— the census block quoted above--self-testEXIT=0—self-test OK: 15 cases + level-vocabulary read + population declaration + real-tree selection floor.The 15 cases carry both controls the card asked for: a selected package missing the key reds, an unselected package without the key is silent, a selected package with the key is green — plus prose-does-not-satisfy, prose-does-not-select (the
packages/clishape), unrecognised level, key outside any env block, each of S1/S2/S3 selecting, no-config, the two projects rows, and a template-literal spelling. Floors are #13799 recipe A: the table's own row labels are the declared batteries,registerCase(name)is the first statement of the loop body, and the roster is a literal (a derived one would delete a row's floor along with the row).Ablation on the committed implementation — trap-guarded, absolute paths, blob-hash restore:
Both legs are confirmed on disk by grep counts and by blob hash, not by the editor's exit code.
The adopters' suites, and the rest delta
packages/restis the audit's control, so it was measured in both directions — the before leg by the same trap-guarded revert:[Registry]linesTest Files 178 passed (178)·Tests 3023 passed (3023)Test Files 178 passed (178)·Tests 3023 passed (3023)528 reproduces the audit's figure for this suite to the line, on a tree many commits younger.
packages/clientTest Files 33 passed (33)·Tests 437 passed (437)packages/services/service-automationTest Files 105 passed (105)·Tests 1242 passed (1242)examples/app-showcaseTest Files 27 passed (27)· `Tests 374 passedThose three runs leave 8 residual
[Registry]lines, and all 8 are ADR-0005[Registry] Collisiondiagnostics in service-automation — the bareconsole.warnthe level never gates, and the very linesflow-name-shadowing.test.tsasserts on. The declaration quiets chatter, not diagnostics; that is measured here rather than asserted.Wiring and repo gates
pnpm check:self-test-wiredEXIT=0pnpm check:self-test-workflow-commands(node scripts/check-self-test-workflow-commands.mjs+--self-test)EXIT=0pnpm check:declared-population-liveEXIT=0pnpm -s check:pm-dispatch-gatesEXIT=0—✓ dispatch-gates self-test: 1402 cases pass.(no case edited)pnpm check:nul-bytesEXIT=0pnpm lint(full repo,eslint . --no-inline-config)EXIT=0The derived gate family.
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack(stderr: derived at commitcf07c12ff,--repochecked against this checkout's origin — it holds) yields 77 commands. All 77 were run. 76 PASS. Exactly one is NOT MEASURED, and it is recorded as such rather than as a pass:pnpm check:dual-build-cjs-loads—EXIT=3,Run pnpm build first. ⛔ This is NOT a pass: nothing was measured.It needs a full-repo build, and it names dist directories in packages far outside this diff (service-cluster-redis, service-knowledge).pnpm check:type-check-debt(whole-repotsc) is in the 76: it outran a 400s budget on the first attempt and was re-run to completion under the verify lock —EXIT=0,surplus: none — every entry sits exactly at its measurement, so any new error is red.Two more needed their environment before they measured anything, and did so once it was supplied:
node scripts/check-required-contexts.mjs --verify-required-setansweredEXIT=2/required-set sweep: NOT VERIFIED — … HTTP 401(its own text: "NOT VERIFIED is not a pass and not a failure of the tree"), thenEXIT=0re-run withNODE_OPTIONS=--use-env-proxyand a token; andpnpm --filter @objectstack/spec run check:skill-examplesrefused on an unbuiltpackages/spec/dist, thenEXIT=0afterpnpm --filter @objectstack/spec build—✅ 257 prose examples type-check across 3 surface(s).All heavy runs went through
scripts/pm/os-verify-lock.sh; each printedVERDICT command-exit 0.🤖 Generated with Claude Code
https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
Generated by Claude Code