Skip to content

test(rest): opt the OS_REST_LOG suite down, with every fault-log observer declaring its own level and a pairing gate - #18090

Merged
os-warren merged 4 commits into
mainfrom
claude/issue-17865-rest-log-suite-opt-down
Sep 14, 2026
Merged

os-warren merged 4 commits into
mainfrom
claude/issue-17865-rest-log-suite-opt-down

Conversation

@os-warren

Copy link
Copy Markdown
Collaborator

Fixes #17865

Decision batch #128 item 4 ruled option B. All four items of that ruling land here, and items 1 and 3 are in one PR by the ruling's own instruction: 「It lands in the same PR as 1; ⛔ 1 does not merge without 3.」

⚠️ The card's own body argues for option A; it was written before the ruling and is stale on the decision. The ruling comment 5651668684 is the authority. Its measurements are good and were re-taken here rather than inherited.

What landed

  1. packages/rest/vitest.config.ts opts the suite down — OS_REST_LOG: 'silent', in all three blocks. A root-level env is inert for a project run, so both inline projects carry it too. 'silent' is the quiet level and not 'error': logError's own rank is error, so at 'error' the shim still speaks and 100% of the frame population survives.

  2. The 29 fault-log observers declare their own level — 'info', the SHIPPED default, so each file asserts against what a real caller gets:

    beforeAll(() => { vi.stubEnv('OS_REST_LOG', 'info'); });
    afterAll(() => { vi.unstubAllEnvs(); });
  3. scripts/check-rest-log-spy-declared.mjs makes an undeclared observer a finding by name — 23-case self-test, both directions per rule, wired additively (one package.json script, one lint.yml step). Seam location and the level vocabulary are imported from check-rest-log-declared.mjs rather than re-implemented, so a renamed level moves both gates at once.

  4. The shipped default is untouched. REST_LOG_DEFAULT_LEVEL is still 'info' in packages/rest/src/log.ts, still pinned by check:rest-log-declared (green here). Nothing in this PR changes production behaviour.

The population, re-measured

The claim comment flagged one instrument bound: git grep -l "console.error" returns 4, against the card's "~15 files / 28 assertions". Both readings are correct and they measure different things — 26 of the 29 observer files spell the spy vi.spyOn(console, 'error'), which contains no literal console.error substring at all. 4 was a floor of a different measurement, exactly as the claim said.

reading value
test files in packages/rest 193
observers (spy on console.error/warn, install a mock by assignment, or import the shim) 29
of those, spyOn(console, 'error') 26 files / 29 occurrences
of those, spyOn(console, 'warn') 14 occurrences
files that go RED at 'silent' with no per-file declaration 15
test cases that go RED 28
files carrying a NEGATIVE fault-log assertion ("logs NOTHING") 8 (18 assertion lines)
of those 8, inherit the suite level and would go vacuous 7
of those 8, already immune because it sets the level per case 1 — rest-log-declared-level-seam.test.ts
rest-expected-error-logging.test.ts alone 4 expect(unhandledLogs()).toHaveLength(0) + 1 sibling

The control that makes 15/28 a reading rather than a probe that could only answer one way: the same 191 files, the same command, at 'info' — 191 passed, 3,196 tests, 0 failures. So the reds are attributable to the level and not to the tree. And the 15 red files are a strict subset of the 29 observers, which is what says the observer detector is not under-counting: no file outside the detected population goes red.

Volume

Same command (pnpm test in packages/rest), same 191 files, three states:

state files / tests captured lines indented at frames
'info' (shipped default, i.e. main today) 191 passed / 3,196 5,705 2,095 (36.7%)
'silent', no per-file declarations 15 FAILED / 28 failed 2,805 0
'silent' + the 29 declarations (this PR, at 6d699ac6f) 191 passed / 3,196 2,508 (−56.0%) 76 (−96.4%)

The card's 2,095 / 36.7% reproduced exactly.

The residual 76 frames are honest and attributable: every one of them belongs to two declaring files — analytics-filter-refusal-envelope.test.ts (23 own-file frames, identical to its baseline count) and rest-endpoint-surfaces-served-only.test.ts (3) — which log through a window their spy does not cover. That residue is also the leak control: if a per-file vi.stubEnv leaked across files sharing a worker process, non-declaring files would still emit. They emit zero — rest-share-user-message-bypass-exits.test.ts 145 to 0, execctx-consumer-census.test.ts 112 to 0.

Why item 2 is load-bearing — a four-leg ablation

The 15 red files announce themselves. The other half does not, and that is the half the card measured and refused as option C. Ablation on rest-expected-error-logging.test.ts, mutating the production predicate isExpectedRouteError to return false so that every expected 4xx logs [REST] Unhandled error loudly:

leg declaration production the 5 "logs NOTHING" cases
L1 present pristine PASS (7/7 green)
L2 present MUTATED FAIL — a real assertion about the code
L3 REMOVED MUTATED PASS — vacuous
L4 REMOVED pristine PASS — identical verdict to L3

L3 against L4 is the whole point: with the declaration gone, those assertions return the same verdict whether the code logs nothing or logs everything. They are then true of a shim that never spoke. Each leg proved its mutation reached the disk by counting the exact text it touched (a bare git diff --stat would go green on any other edit in the same round), and both files were restored from HEAD and verified byte-identical by git hash-object against the HEAD blob — not by a git diff HEAD that a checkout-from-ref can leave clean.

Gates

node scripts/pm/dispatch-gates.mjs --commands was derived against the actual 33-path change set, all 98 commands were run with exit codes recorded to disk before anything read them, and the run was reconciled with --ran.

  • 96 of 98 exit 0.
  • pnpm lint — the FULL union, not a narrowing — exit 0 at the final head 6d699ac6f, 88s. No narrowing was claimed because none was needed.
  • pnpm --filter @objectstack/rest typecheck exit 0; check:test-typecheck reports 0 file(s) / 0 error(s) in test-typecheck-debt.json, so the 29 edited test files moved no TEST_DEBT number.
  • 2 commands exit 3 — PREREQUISITE NOT MET, which is NOT MEASURED and not a finding: check:dual-build-cjs-loads and check:type-check-debt --re-measure both refuse without a built dist/ for the whole workspace (43 and 6 packages named respectively). That prerequisite is CI's Build Core / lint-job build; declared here rather than absorbed. The half of check:type-check-debt my diff can actually move is the packages/rest test layer, and that is measured green above.

One gate reported a defect in this change, and it was fixed rather than weakened. check:pm-dispatch-gates's own self-test named scripts/check-rest-log-spy-declared.mjs as an unlisted repo-root sweeper: the gate walks the workspace to locate the seam but carried no path literal, so no card could ever be told it reads their surface. Repaired the honest way — it now declares ROOT_DIR_WATCH_HINTS in the sibling's idiom, with five live-tree self-test cases holding the declaration against the walk it actually performs plus an anti-vacuity floor on the observer population (battery 18 to 23). check:pm-dispatch-gates, check:declared-population-live and check:watch-hint-literal are all green after it.

The new gate found nothing beyond the ruling's population — 29 of 193 test file(s) … every one of them declares its own OS_REST_LOG level. No overflow to report.

Changeset: skip-changeset, measured

packages/rest's files[] is ["dist","README.md","CHANGELOG.md"]. Built the package and grepped those paths with a positive control:

symbol hits in the published paths
REST_LOG_DEFAULT_LEVEL (POSITIVE CONTROL — must be present) 2 files
unhandledLogs (only in a changed test file) 0
stubEnv (the declaration this PR adds) 0
check-rest-log-spy (the new gate) 0
any *test* file in dist/ 0

The remaining changed paths are the repo-root package.json (private), .github/workflows/lint.yml and scripts/ — none published by any package. Control hits, subject symbols zero ⇒ no published artefact moves ⇒ skip-changeset.

Acceptance notes

Observations from the surface, noted and deliberately not filed — neither is a reproducible defect, a contract violation, or a metadata-authoring trap:

  • noted, not filed: the brace-matching and env-block readers are now in a third copy across check-registry-log-declared.mjs, check-rest-log-declared.mjs and this gate. The sibling's header already records the extraction as the right follow-up and states why it did not do it (moving that gate's self-test battery floor). This PR imported what mattered (seam location, vocabulary) instead of copying it, so the duplication did not grow in the load-bearing direction. Carrier if it is ever done: whichever PR next edits check-registry-log-declared.mjs's reader.
  • noted, not filed: two declaring files still emit 76 stack frames because their spy does not cover the window in which the fault is logged. Tightening each spy's window would take the suite to 0, but it edits assertions the ruling did not name, on files that are not defective — the frames are correct output at a correctly declared level. Carrier: a future volume card on this suite, if one is filed. No such card exists today.

Related: #15484's execution list reads ruling comment 5651668684 as the authority for its opt-down sentence. That card remains open and is out of scope here.


Generated by Claude Code

Decision batch #128 item 4 ruled option B on this card: the packages/rest
suite is opted down, every test file that observes the fault log declares
its own level, and a pairing gate makes an undeclared observer a finding.

This commit is item 1 alone, committed on purpose so the red population it
produces can be measured against a real base before item 2 lands. The
shipped default in packages/rest/src/log.ts is untouched and still
gate-pinned at 'info'.

Claude-Session: https://claude.ai/code/session_01TbSMtGzMrtPwh925wDEZd5
Co-authored-by: Claude <noreply@anthropic.com>
Ruling item 2. The 29 test files in packages/rest that observe the fault
log -- a console.error/warn spy, or an import of the shim itself -- now
declare 'info', the SHIPPED default, in their own setup instead of
inheriting the suite's level.

Measured on the committed opt-down, same command, same 191 files:
  OS_REST_LOG 'info'   -> 191 passed, 2,095 indented `at ` frames
  OS_REST_LOG 'silent' -> 15 files / 28 tests RED, 0 frames
  + these declarations -> 29/29 observer files green again

Claude-Session: https://claude.ai/code/session_01TbSMtGzMrtPwh925wDEZd5
Co-authored-by: Claude <noreply@anthropic.com>
…ding

Ruling item 3, the half that keeps item 2 true. A test file in the package
that owns the OS_REST_LOG seam is an OBSERVER when it spies on
console.error/warn, installs a mock by assignment, or imports the shim; an
observer must declare OS_REST_LOG in code, at a level loud enough that both
shim sites still emit.

Seam location and the level vocabulary are imported from
check-rest-log-declared.mjs rather than re-implemented, so a renamed level
moves both gates at once. Zero observers in the owning package is a
MEASUREMENT FAILURE, not a clean bill: this detector recognises the
spellings it knows, and a gate that finds nothing passes everything.

18-case self-test, both directions per rule. Wired additively into the root
package.json and lint.yml.

Claude-Session: https://claude.ai/code/session_01TbSMtGzMrtPwh925wDEZd5
Co-authored-by: Claude <noreply@anthropic.com>
check:pm-dispatch-gates named the new gate as an unlisted repo-root sweeper:
its source walks the workspace to locate the seam but carried no path
literal, so no card could be told it reads their surface. It now declares
ROOT_DIR_WATCH_HINTS in the sibling's idiom, and five live-tree self-test
cases hold the declaration against the walk it actually performs plus an
anti-vacuity floor on the observer population.

Self-test battery 18 -> 23 cases.

Claude-Session: https://claude.ai/code/session_01TbSMtGzMrtPwh925wDEZd5
Co-authored-by: Claude <noreply@anthropic.com>
@os-warren os-warren added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 14, 2026 — with Claude
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run.

What this run could not see
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 14 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json b3d6918b86abcc31f82e963d2fabc37308bc4bb7 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 0233f381b0c5240a98467fc2af98e978ec3c8ad7 — the merge of head 6d699ac6fbc49e6135ee89f11c793675fc2e99c1 into base b3d6918b86abcc31f82e963d2fabc37308bc4bb7, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 0233f381b0c5240a98467fc2af98e978ec3c8ad7 && git checkout 0233f381b0c5240a98467fc2af98e978ec3c8ad7
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin b3d6918b86abcc31f82e963d2fabc37308bc4bb7 6d699ac6fbc49e6135ee89f11c793675fc2e99c1 && git checkout -B drift-repro b3d6918b86abcc31f82e963d2fabc37308bc4bb7 && git merge --no-ff 6d699ac6fbc49e6135ee89f11c793675fc2e99c1

node scripts/docs-audit/affected-docs.mjs --json b3d6918b86abcc31f82e963d2fabc37308bc4bb7

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

@github-actions github-actions Bot added size/l ci/cd dependencies Pull requests that update a dependency file tests labels Sep 14, 2026
@os-warren
os-warren marked this pull request as ready for review September 14, 2026 01:31
@os-warren
os-warren enabled auto-merge September 14, 2026 01:32
@os-warren
os-warren added this pull request to the merge queue Sep 14, 2026
Merged via the queue into main with commit a26a114 Sep 14, 2026
41 checks passed
@os-warren
os-warren deleted the claude/issue-17865-rest-log-suite-opt-down branch September 14, 2026 02:04
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 17, 2026
…claring packages (objectstack-ai#18121)

Fixes objectstack-ai#17978

One shared vitest filter preflight, serving all eight packages that
declare vitest `projects`. Direction A was ruled by the `domain:cli`
execution seat under 验证策略 (`5656963807`) and confirmed by triage
(`5657101795`), which also ruled the ownership question:

> **引用或点名一个包不会把卡拉进它的车道,只有编辑它才会。**

## What changed

- **New private workspace package
`packages/qa/vitest-filter-preflight`** — a `domain:cli`-owned home, ⛔
**not** root `scripts/` and ⛔ not inside `packages/spec`. It holds the
ONE transcription of vitest's `TestProject.filterFiles`;
`packages/qa/refd-timer-testkit` is the shape precedent (private, no
build, `exports` straight at `src`).
- **`packages/cli` migrates onto it** and its local copy is deleted.
Triage calls the move "a migration inside the owning lane". Measured
behaviour-equivalent (below).
- **Call sites in all eight configs.** `packages/spec`,
`packages/objectql` and `packages/core` are **call sites only** — one
file each, `vitest.config.ts`, 32 insertions and 0 deletions, no new
export and nothing under `src/`.
- **Two findings this card carried are folded in**: the notice's package
name is now a parameter (it was hardcoded `@objectstack/cli`), and the
no-`--project` typo case is covered and pinned.

## Three measurements that shaped the design

**1. The precedent's shape does not transfer, re-confirmed at
`a26a114d7`.** `Populations` takes concrete paths, so a glob cannot be a
member. 82 package roots under `packages/`, **8** declaring `projects` —
the same eight, no drift. `packages/cli` is 1 of 8 with two exact-path
projects, and only as a by-product of a tier walk it already had (objectstack-ai#13504
/ objectstack-ai#14554). The other 7 each pair one explicit list `L` with a glob
complement of `L`, so the missing component is identical across all
seven. `exactAndGlobPopulations` is that component.

**2. The glob project's population is a deliberate SUPERSET** (the ruled
shape, and it holds): a plain recursive walk of the
`*.{test,spec}.?(c|m)[jt]s?(x)` family minus `node_modules`/`dist`,
minus `L`. Matching nothing in a superset implies matching nothing in
the real set, so a false accusation is structurally impossible and drift
can only under-report. ⛔ Not vitest's own glob engine. Cost of the walk,
measured: 0.3 ms (`core`) to 5.3 ms (`spec`, 507 test files) per config
load.

**3. ⛔ Consumers import the module by RELATIVE PATH, never by its bare
package name — and that is a measurement, not a preference.** Vite
bundles a config's relative imports through esbuild, which transpiles
TypeScript; it externalises bare specifiers and leaves Node to load the
resolved path, which here is a `.ts` file. The bare form works on this
box and prints no warning — because Node 22.22 strips types by default.
Re-run with `NODE_OPTIONS=--no-experimental-strip-types`:

```text
failed to load config from .../packages/core/vitest.probe-bare.config.ts
TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" for .../src/index.ts
```

This repo declares `engines.node: ">=22.0.0"`, so on a supported Node
the bare form turns a silent-drop defect into a total harness outage for
that package. The two escapes are worse: building to `dist` would make
eight harnesses' **config load** depend on build state, and `pnpm
--filter PKG exec vitest run FILE` — the invocation this card is about —
runs no build; authoring it as `.mjs` would drop the types, and
`CliParseResultOptions` is typed structurally precisely so a vitest
upgrade renaming an option is a type error here instead of a silent
decline.

## Per-package before/after — the preflight now speaks where it was
silent

`before` = `origin/main`'s config, run verbatim via `--config
vitest.before.config.ts` beside the real one so `__dirname` still
resolves to the package root. Every run exits **0** in both legs; the
defect is silence, not failure.

| package | glob side: dropped path named | exact side: dropped path
named | no-`--project` typo named | healthy control |
|:--|:--|:--|:--|:--|
| `packages/types` | 0 → 4 | 0 → 4 | 0 → 2 | byte-identical |
| `packages/rest` | 0 → 4 | 0 → 4 | 0 → 2 | byte-identical¹ |
| `packages/runtime` | 0 → 4 | 0 → 4 | 0 → 2 | byte-identical |
| `packages/spec` | 0 → 4 | 0 → 4 | 0 → 2 | byte-identical |
| `packages/objectql` | 0 → 4 | 0 → 4 | 0 → 2 | byte-identical |
| `packages/core` | 0 → 4 | 0 → 4 | 0 → 2 | byte-identical |
| `packages/qa/dogfood` | 0 → 4 | 0 → 4 | 0 → 2 | byte-identical¹ |

¹ the only diff line is the `Duration` decimal.

"`FILTER SELECTED NOTHING` present" reads **0 before, 2 after** in all
three shapes in all seven (twice: once at config load, once from the
`exit` listener). ⭐ This extends the card's reproduction from the 4
packages the first dispatch probed to **7 of 7**, including `spec`,
`objectql` and `core`, which had never been probed — still **zero
negative probes**, vitest **4.1.11**.

`packages/qa/dogfood` also confirms the shape outside the `REPO_TESTS`
idiom: its exact list is the inline `SHARED_SHOWCASE` and its glob
project is `isolated`.

**`packages/cli` — migration equivalence, not before/after.** Its
`before` already carried a preflight, so the reading asked whether the
move changed anything a reader sees. Same notice count (2), same
dropped-path count (4), same `pnpm --filter @objectstack/cli` in the
notice (4), healthy control byte-identical. The lost-run output differs
in exactly **two words, twice**: `tier` → `project`. Deliberate — "tier"
is this package's private vocabulary while "project" is vitest's own and
is correct for all eight.

A healthy narrowed run still contributes **zero bytes**:
`renderLostFilterNotice` returns the empty string and no writer is
called and no `exit` listener is registered.

## Tests, and the ablation that proves they can fail

`pnpm --filter @objectstack/vitest-filter-preflight test` — **2 files,
73 tests, pass**. `typecheck` green, and `tsc --listFiles` puts **4 of
4** of the package's files in the program (`src/index.ts`, both tests,
`vitest.config.ts`) — nothing hidden.

`test/config-wiring-sweep.test.ts` is the anti-phantom sweep, and its
population is **derived, not listed**: it walks `packages/` for
package-root vitest configs, masks their comments, and requires every
one that declares `projects` to invoke the preflight — so a ninth
package is caught on the PR that adds it. The count is asserted as a
floor, so a newcomer fails on its own wiring rather than on a number.

**Ablation** (fix committed first; mutation proven on disk by occurrence
count; `trap`-restored with absolute paths; restore proven by blob hash,
not by an exit code):

| leg | on-disk proof | sweep |
|:--|:--|:--|
| delete the `runFilterPreflight` call from
`packages/types/vitest.config.ts` | `runFilterPreflight({` count 1 → 0;
blob `a5d708a1` → `ef829c5c` | **exit 1**, `1 failed \| 40 passed`,
fails on that package's row |
| point `packageName` at another package | own-name count 1 → 0,
wrong-name 1; blob → `aba7fc56` | **exit 1**, same row |
| restore | — | blob back to `a5d708a1` (= `HEAD`), `git diff HEAD`
empty |

Predicted direction was "turns red" and that is what both legs did. The
second leg is the pin for the carried finding: a shared notice bound to
one package sends every other package's reader to a command that runs
the wrong suite.

## Gates

`node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` against the actual change set, every command
run, exit codes recorded to disk, then reconciled:

```text
Run reconciliation — 91 derived, 91 run, 0 NOT-MEASURED, 0 UNRUN.
✓ dispatch-gates --ran: 91 derived famil(ies) accounted for — 91 run,
  0 NOT-MEASURED (a DERIVED zero — all 91 recorded an exit code and none of them is 3).
```

Plus this lane's standing full union, which the derivation does not
name: **`pnpm lint` exit 0** at `cd75179f35` (`eslint .
--no-inline-config`, the whole repo — not a narrowing).

Two gates needed work rather than just passing:

- **`check:cross-package-test-inputs` went red and is satisfied, not
routed around.** The sweep reads outside its package by construction. It
now declares its radius in `scripts/cross-package-test-inputs.mjs` —
three globs under `packages/` plus three `scripts/` files, with `heldBy`
witnesses for the two globs built from a loop variable that the detector
can see but cannot name — and `turbo.json` carries the matching
`@objectstack/vitest-filter-preflight#test` inputs with
`!**/node_modules/**`. ⭐ Side effect worth having: the suite's turbo
hash now really covers every package's config, so a config change
re-runs the sweep.
- **`check:type-check-debt` first exited 3 (OOM at
`--max-old-space-size=4096` on a shared box) — recorded as NOT MEASURED,
not as a pass**, then re-run at 8192 and green: 77/81 packages
type-checked, 55 raw errors, none above its recorded number, `surplus:
none`. `check:type-check-coverage` green with the new package counted
(81 packages, +1 vs the record).

`check-engine-split-ratio --days 90` also exited 2 first — the gate
refusing to measure on a shallow clone — and is green after `git fetch
--shallow-since`, at 97.8%. Neither of those two numbers is about this
diff; both are recorded so the zero above is real.

No repo-level sweeper was added, so `check:pm-dispatch-gates` does not
fire: the anti-phantom sweep is a vitest test inside the cli-owned home,
which is also what keeps a gate-class file out of root `scripts/`.

## `skip-changeset` — measured, not assumed

Nothing published moves. The new package is `private: true`. For each of
the seven touched published packages, every `files[]` path was grepped
for the subject symbols (`runFilterPreflight`,
`exactAndGlobPopulations`, `matchesVitestFilter`, `testFilesUnder`):
**zero hits in all seven**, with a positive control proving the grep
works (`ObjectLogger` in `packages/core/dist`, `defineStack` in
`packages/spec/dist`). `packages/cli`'s `files[]` is
`["dist","README.md","CHANGELOG.md"]` and the moved module lived at the
package root, outside all three. `packages/spec`'s `files[]` includes
`src/**/*.zod.ts`; this diff touches no `.zod.ts`.

`pnpm-lock.yaml` moved by **12 insertions and 0 deletions** — exactly
one new `importers:` block for the new package, no version change to
anything installed — and `pnpm install --frozen-lockfile` exits 0, which
is the proof it is tool-written rather than hand-edited.

## Acceptance notes

- **The seven new call sites are in no tsc program.** Only
`packages/cli` type-checks its own `vitest.config.ts` (via
`tsconfig.test.json`); the other seven declare `include: ["src/**/*"]`
and their `tsconfig.test.json` siblings declare `["src*"]`, so no
package-root config there is compiled by anything. Pre-existing —
`packages/cli/tsconfig.test.json`'s header records the same state for
cli before objectstack-ai#14554 graduated it — and not widened by this change, but it
does mean a `@ts-expect-error` in any of those seven files would be a
phantom check. Covered here by measurement instead: the wiring sweep
asserts the call shape textually, and every one of the seven configs was
loaded and run in both legs above. Noted, not filed — no declared
contract requires a config to be type-checked, and the carrier is the
`check:type-check-coverage` ratchet's own graduation work.
- The wiring sweep is scoped to `packages/`. A package-root config
outside that tree growing `projects` would not be swept — an
under-report, the direction this card resolves uncertainty in. Scoping
is what keeps the declared radius to one already-open root instead of
opening `examples/` and `apps/` roots in `ci.yml`'s `crosspkg` filter.

## Fences

Module home is `domain:cli`-owned, so the stop-clause did not trigger:
nothing landed in root `scripts/` or inside `packages/spec`. No gate
weakened. `packages/rest`'s `OS_REST_LOG: 'silent'` from `a26a114d7`
(objectstack-ai#18090) is untouched in all three blocks. No file on objectstack-ai#17630's surface
was touched — this diff edits no test file in `packages/qa/dogfood`. No
governed surface is in the PR diff.

---
_Generated by [Claude
Code](https://claude.ai/code/session_01TbSMtGzMrtPwh925wDEZd5)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd dependencies Pull requests that update a dependency file size/l skip-changeset PR has no user-facing published change; bypasses the changeset gate tests

Projects

None yet

2 participants