Blocked-by: #8946
Same package and the same declaration block as #8946, a different glob, and a different mechanism — filed separately because closing #8946 (examples/** radius for cli and lint) leaves this one open. It should land after PR #8991, which is editing the exact @objectstack/cli#test inputs array and the CROSS_PACKAGE_TEST_INPUTS entry this needs.
What was measured
Measured on origin/main @ 8b9eba51b, in a fresh worktree with pnpm install run.
Three packages/cli tests read hand-written docs outside their package:
packages/cli/test/cloud-login-json-ndjson.e2e.test.ts:79 content/docs/deployment/cli.mdx
packages/cli/test/cloud-login-json-ndjson.e2e.test.ts:80 content/docs/deployment/index.mdx
packages/cli/test/login-json-ndjson.e2e.test.ts:75 content/docs/deployment/cli.mdx
packages/cli/test/login-json-noninteractive.e2e.test.ts:64 content/docs/deployment/cli.mdx
They are real assertions, not incidental reads — they enforce the #6730 ruling that the NDJSON exception "stays declared, not just implemented", by requiring the declaration to be present in the prose a script author actually meets.
Three facts that only make sense together:
turbo.json → @objectstack/cli#test declares three cross-package globs (packages/verify/src/**, packages/plugins/plugin-security/src/**, packages/services/service-cluster/src/**). No content/docs glob.pnpm check:cross-package-test-inputs exits OK — "12 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob."- (1) and (2) can only both be true if the gate never saw the reads. Its
--verify layer fails on a package that has a scan and no declaration, so a seen read with no glob would be red.
Why the gate misses them
A lead, not a conclusion — the conclusion above holds regardless of which spelling is the blind spot.
AGENTS.md publishes the recognised seed list, and the first entry is dirname(fileURLToPath(import.meta.url)). All three files seed differently — with resolve(..., '..') in place of dirname:
constHERE=resolve(fileURLToPath(import.meta.url),'..');constREPO_ROOT=resolve(HERE,'../../..');constCLI_DOCS=resolve(REPO_ROOT,'content/docs/deployment/cli.mdx');// two hops
and login-json-noninteractive.e2e.test.ts:64 adds a third shape, a three-argument resolve:
constCLI_DOCS=resolve(HERE,'../../..','content/docs/deployment/cli.mdx');
This is the class #8698 already paid for once with a different spelling (new URL(…, import.meta.url)), and it is the hazard the gate's own header names: an unrecognised spelling produces no flag, which means no declaration, silently.
The consequence, observed today rather than reasoned about
PR #8983 (docs-only, issue #8913) rewrote content/docs/deployment/index.mdx. Every fact the pin protects survived the rewrite — NDJSON named, the CLI anchor kept, "parse line by line" kept — but one assertion is /one\s+per\s+line/i, which needs those three tokens contiguous, and the new prose read "one compact JSON document per line".
Because the read is undeclared, @objectstack/cli#test was not in the affected set: PR CI was green, and the failure surfaced only in the merge queue's full suite (build 31926481115, Test Core (3/3)), which dequeued the PR. That is the same cost shape #8946 describes — the first signal is a red in the shared queue, which stalls every lane rather than the one that made the edit.
Suggested fix (not prescribed)
- Extend the detector to recognise the
resolve(fileURLToPath(import.meta.url), '..') seed and the three-argument resolve(HERE, '../../..', 'rel') shape, with a --self-test case per shape in the same edit — the discipline AGENTS.md already states for this script. - Declare the radius that then becomes visible. It can be narrow — the three files read exactly two pages:
$TURBO_ROOT$/content/docs/deployment/cli.mdx
$TURBO_ROOT$/content/docs/deployment/index.mdx
in CROSS_PACKAGE_TEST_INPUTS['@objectstack/cli'] and mirrored onto @objectstack/cli#test in turbo.json. - Worth a second look while in there: a re-scan after (1) may surface other packages whose reads use the same unrecognised seeds. The count in the gate's OK line ("12 packages") is the before-number to compare against.
Separately and not part of this card: whether a regex requiring literal token adjacency is the right shape for a prose pin at all. It is defensible — the ruling is about the declaration being findable — but /one\s+per\s+line/i fails on a rewording that preserves every fact. Recording it here so the decision is visible; the PR that hit it repaired the phrasing rather than weakening the pin.
Backlinks
Generated by Claude Code
Blocked-by: #8946
Same package and the same declaration block as #8946, a different glob, and a different mechanism — filed separately because closing #8946 (
examples/**radius for cli and lint) leaves this one open. It should land after PR #8991, which is editing the exact@objectstack/cli#testinputs array and theCROSS_PACKAGE_TEST_INPUTSentry this needs.What was measured
Measured on
origin/main@8b9eba51b, in a fresh worktree withpnpm installrun.Three
packages/clitests read hand-written docs outside their package:They are real assertions, not incidental reads — they enforce the #6730 ruling that the NDJSON exception "stays declared, not just implemented", by requiring the declaration to be present in the prose a script author actually meets.
Three facts that only make sense together:
turbo.json→@objectstack/cli#testdeclares three cross-package globs (packages/verify/src/**,packages/plugins/plugin-security/src/**,packages/services/service-cluster/src/**). Nocontent/docsglob.pnpm check:cross-package-test-inputsexits OK — "12 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob."--verifylayer fails on a package that has a scan and no declaration, so a seen read with no glob would be red.Why the gate misses them
A lead, not a conclusion — the conclusion above holds regardless of which spelling is the blind spot.
AGENTS.md publishes the recognised seed list, and the first entry is
dirname(fileURLToPath(import.meta.url)). All three files seed differently — withresolve(..., '..')in place ofdirname:and
login-json-noninteractive.e2e.test.ts:64adds a third shape, a three-argument resolve:This is the class #8698 already paid for once with a different spelling (
new URL(…, import.meta.url)), and it is the hazard the gate's own header names: an unrecognised spelling produces no flag, which means no declaration, silently.The consequence, observed today rather than reasoned about
PR #8983 (docs-only, issue #8913) rewrote
content/docs/deployment/index.mdx. Every fact the pin protects survived the rewrite — NDJSON named, the CLI anchor kept, "parse line by line" kept — but one assertion is/one\s+per\s+line/i, which needs those three tokens contiguous, and the new prose read "one compact JSON document per line".Because the read is undeclared,
@objectstack/cli#testwas not in the affected set: PR CI was green, and the failure surfaced only in the merge queue's full suite (build31926481115,Test Core (3/3)), which dequeued the PR. That is the same cost shape #8946 describes — the first signal is a red in the shared queue, which stalls every lane rather than the one that made the edit.Suggested fix (not prescribed)
resolve(fileURLToPath(import.meta.url), '..')seed and the three-argumentresolve(HERE, '../../..', 'rel')shape, with a--self-testcase per shape in the same edit — the discipline AGENTS.md already states for this script.CROSS_PACKAGE_TEST_INPUTS['@objectstack/cli']and mirrored onto@objectstack/cli#testinturbo.json.Separately and not part of this card: whether a regex requiring literal token adjacency is the right shape for a prose pin at all. It is defensible — the ruling is about the declaration being findable — but
/one\s+per\s+line/ifails on a rewording that preserves every fact. Recording it here so the decision is visible; the PR that hit it repaired the phrasing rather than weakening the pin.Backlinks
examples/**glob; PR Declare the examples/** input radius for the cli and lint test suites #8991 open against itnew URL(…, import.meta.url)seed, so an undeclared cross-package read passes green #8698 — the same detector, a different unrecognised seed, closedmeta.json— lands last #8913 / PR docs(deployment): rewrite the section index around the two lifecycles and re-order meta.json (#8913) #8983 — where this surfacedGenerated by Claude Code