Skip to content

fix(devx): the cross-package detector sees the seed walked from import.meta.url (#8995) - #9010

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-8995-cli-docs-seed-spelling
Aug 16, 2026
Merged

fix(devx): the cross-package detector sees the seed walked from import.meta.url (#8995)#9010
os-project-manager merged 1 commit into
mainfrom
claude/issue-8995-cli-docs-seed-spelling

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes#8995

Three packages/cli e2e tests read content/docs/** and pin its prose. The gate that exists to keep those reads declared could not see them, so @objectstack/cli#test was outside the affected set: PR #8983 reworded deployment/index.mdx, PR CI was green, and the merge queue was the first signal — it dequeued the PR and took two unrelated PRs down as batch collateral.

Why the gate could not see them

The detector recognises dirname(fileURLToPath(import.meta.url)) — the seed that names the directory. These three files walk to it from the file instead:

constHERE=resolve(fileURLToPath(import.meta.url),'..');

import.meta.url was not a recognised expression, so fileURLToPath(...) unwrapped to it and returned undefined; the whole chain hanging off HERE collapsed with it. No flag, therefore no declaration, silently — the one failure mode the published spelling list exists to prevent.

The fix models the file at hereDepth + 1, one level below its directory. The walked seed then comes out equal to the named seed through the ordinary literal walk rather than needing a case of its own, and it is precisely what Node's resolve/join do with a file argument — treat it as a directory prefix. import.meta.filename is accepted on the same line, for the reason the file already gives for import.meta.dirname: the first author who reaches for it would otherwise get silence.

The card also expected the three-argument resolve(HERE, '../../..', 'rel') to be a separate unrecognised shape. It is not — pathExpression already loops over every trailing literal argument. The seed was the only blind spot; a self-test case pins the three-argument form anyway.

Proof, against the pre-fix state

Detector fixed, before any declaration was added — the reads become visible and the gate goes red:

$ node scripts/check-cross-package-test-inputs.mjs --verify # exit 1
FAIL: cross-package test inputs are not declared consistently.
- @objectstack/cli names path(s) no declared glob covers, so a change to them would not
re-run its tests:
content/docs/deployment/cli.mdx (named in packages/cli/test/cloud-login-json-ndjson.e2e.test.ts)
content/docs/deployment/index.mdx (named in packages/cli/test/cloud-login-json-ndjson.e2e.test.ts)
content/docs/permissions/authentication.mdx (named in packages/cli/test/login-json-ndjson.e2e.test.ts)
scripts/check-nul-bytes.mjs (named in packages/cli/test/login-json-noninteractive.e2e.test.ts)
Widen the package's globs to cover them.

--list-escapes before vs after the detector change — the delta is contained entirely within @objectstack/cli:

 @objectstack/cli (packages/cli)
packages/cli/src/commands/serve-multi-node-cap-advisory.pin.test.ts
packages/cli/src/commands/serve-verify-security-parity.contract.test.ts
+ packages/cli/test/cloud-login-json-ndjson.e2e.test.ts
+ packages/cli/test/login-json-ndjson.e2e.test.ts
+ packages/cli/test/login-json-noninteractive.e2e.test.ts
+ packages/cli/test/serve-capability-identity.test.ts

The package count does not move: 12 before, 12 after. No previously-invisible package became visible, so nothing outside cli needs a new declaration — the re-scan the card asked for came back empty, which is the good outcome.

The declared radius, re-derived rather than assumed

Re-deriving from the detector's own output rather than copying the card's list found four paths, not two:

pathwhy
content/docs/deployment/cli.mdxread by all three e2e tests
content/docs/deployment/index.mdxread by cloud-login-json-ndjson — the page PR #8983 reworded
content/docs/permissions/authentication.mdxread by login-json-ndjson, pinning the device-flow page. Not in the card's two-page list; it is a real readFileSync with real assertions
packages/connectors/connector-mcp/src/connector-mcp-plugin.tsread by serve-capability-identity.test.ts. The gate could not name this one: the test spells the path relative, and the literal-coverage check only collects repo-relative literals

Per-page rather than content/docs/**: docs change far more often than any package here, and a subtree glob would put cli's e2e suite on every documentation PR.

One entry is not a read: scripts/check-nul-bytes.mjs is named in a comment. The literal collector takes quoted paths without parsing, so a mention forces a declaration. That is the designed trade — the file's own header notes over-collection "can only force a WIDER declaration, never a narrower one" — and declaring one rarely-touched file is cheaper than teaching the scanner to tell prose from code, or than rewording a comment to dodge a scanner. The declaration comment says so, so the next person checking the radius against the code does not read it as stale.

Layer A moves, in both directions

Positive — the diff that caused the regression now pulls cli in:

$ ... --union-into ls.json --changed [content/docs/deployment/index.mdx]
+ @objectstack/cli (declared glob matched content/docs/deployment/index.mdx)
+ create-objectstack (declared glob matched content/docs/deployment/index.mdx)

Same for deployment/cli.mdx and permissions/authentication.mdx.

Negative control — content/docs/deployment/troubleshooting.mdx, an undeclared page in the same directory:

 + create-objectstack (declared glob matched content/docs/deployment/troubleshooting.mdx)

cli is correctly absent. create-objectstack declares content/** and legitimately matches both. The negative control landing on an adjacent page is what shows the radius is genuinely narrow rather than a subtree sweep.

Self-test and the published list

RECOGNISED_PATH_SPELLINGS is printed in the failure text and mirrored in AGENTS.md, so both move here. +7 --self-test cases, one per newly recognised shape — 26 to 33 — including the two negative controls that matter: the walked seed climbing into node_modules (the tsx bin these very files resolve) still does not flag, and one case pins that the walked and named seeds agree, which is what fails if the file is ever modelled at its directory's depth.

Verification

All at 35b583cbd, the final commit, working tree clean:

pnpm check:cross-package-test-inputs OK: 12 package(s) ... all declared exit 0
All 33 self-test cases passed
node scripts/check-cross-package-test-inputs.mjs (ci.yml bare form) exit 0
pnpm check:nul-bytes OK (5947 files, no control bytes) exit 0

Gate set derived against the actual changed paths with node scripts/pm/dispatch-gates.mjs scripts/check-cross-package-test-inputs.mjs turbo.json AGENTS.md; it names those two cross-package families and nothing further (AGENTS.md and turbo.json pull no additional gate).

The pins the new declaration protects are green on this tree, so the radius is truthful and carries no pre-existing red — pnpm --filter @objectstack/cli exec vitest run over the four files: 9 docs-pin assertions pass across the three e2e files, and serve-capability-identity's connector-mcp source pin passes.

Out of scope, deliberately

Whether /one\s+per\s+line/i — a regex requiring literal token adjacency — is the right shape for a prose pin is not answered here. #8995 records the question on purpose; the PR that hit it repaired the phrasing rather than weakening the pin. unionInto's hoist (#9000) and the examples/ globs (#8946) are untouched.

No changeset: scripts/, turbo.json and AGENTS.md are not published package source.


Generated by Claude Code

…t.meta.url (#8995)
Three packages/cli e2e tests seed their paths as
`resolve(fileURLToPath(import.meta.url), '..')` -- walking to the directory
from the FILE rather than naming it with `dirname(...)`. `import.meta.url`
was not a recognised expression, so the whole chain resolved to undefined,
their reads of `content/docs/**` produced no flag, and the radius went
undeclared silently. The gate's OK line said "all declared" while it was not.
Model the file at `hereDepth + 1` -- one level below its directory -- so the
walked seed comes out equal to the named seed through the ordinary literal
walk instead of needing a case of its own. This is exactly Node's
resolve/join, which treat a file argument as a directory prefix.
Declare the radius that then becomes visible, re-derived from the detector's
own output rather than assumed: three content/docs pages, connector-mcp's
plugin source (a relative literal the coverage check cannot name), and one
gate script named in a comment.
+7 --self-test cases, one per newly recognised shape (26 -> 33), and the
published RECOGNISED_PATH_SPELLINGS list moves in AGENTS.md in the same edit.
@vercel

vercelBot commented Aug 16, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectstackIgnoredIgnoredAug 16, 2026 5:59am

Request Review

@os-project-manageros-project-manager added tooling skip-changeset PR has no user-facing published change; bypasses the changeset gate size/m and removed size/m tooling labels Aug 16, 2026 — with Claude
@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation tooling labels Aug 16, 2026
@os-project-manager
os-project-manager marked this pull request as ready for review August 16, 2026 06:02
@os-project-manager
os-project-manager added this pull request to the merge queueAug 16, 2026
Merged via the queue into main with commit 601fbc4Aug 16, 2026
38 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-8995-cli-docs-seed-spelling branch August 16, 2026 06:20
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mskip-changesetPR has no user-facing published change; bypasses the changeset gatetooling

Projects

None yet

2 participants

@os-project-manager@claude