Skip to content

import-prerequisite.mjs prints a node scripts/NAME.mjs command that does not exist, for the three importers under packages/lint/scripts/ #13826

Description

@claude

Found and measured while fixing #13429 (PR on claude/issue-13429-pipe-shape-advisory-siblings). Out of scope there: that card is about the advisory's CONTENT; this is the interpolated command beside it, a separate defect with a separate fix.

The defect

scripts/import-prerequisite.mjs derives the gate name from the importer's basename:

const gate = fileURLToPath(importerUrl).split('/').pop().replace(/\.mjs$/, '');

and then prints a command that hard-codes the scripts/ directory: node scripts/${gate}.mjs .... The basename is right for the 42 importers under scripts/**, and wrong for the three that are not:

  • packages/lint/scripts/check-doc-formula-expressions.mjs
  • packages/lint/scripts/check-doc-security-posture.mjs
  • packages/lint/scripts/check-reference-carrier-shape.mjs

Measured, not reasoned

On claude/issue-13429-pipe-shape-advisory-siblings at 8f739a646, with @objectstack/formula deliberately unbuilt:

$ node packages/lint/scripts/check-doc-formula-expressions.mjs
check-doc-formula-expressions: PREREQUISITE NOT MET — the workspace package `@objectstack/formula` is not built
...
  `node scripts/check-doc-formula-expressions.mjs > /tmp/check-doc-formula-expressions.log 2>&1; echo "EXIT=$?"`.

There is no scripts/check-doc-formula-expressions.mjs. Copy-pasting the printed line answers Cannot find module, which reads as a second, unrelated breakage rather than as a typo in the banner. The derivation predates #13429 and is untouched by it — that PR changed the sentence around this command, not the command.

Why it matters more than a cosmetic path

The whole point of this banner is that the reader is already confused about why a gate exited non-zero. Handing them a command that fails for a THIRD reason is the failure mode the module exists to end, one level down.

Suggested shape

Derive the repo-relative path rather than the basename — the module already has importerUrl and can locate the repo root — so the printed command is the one the reader actually ran. The gate NAME in the headline is correct as a basename and should not change; only the command needs the real path. scripts/import-prerequisite.mjs --self-test is the place to pin it, next to the advisory pins #13429 added.

Severity not judged. No gate verdict is computed from this text; it wastes the reader's next step.


Generated by Claude Code

Activity

  1. self-assigned this
    on Sep 1, 2026
  2. baozhoutao commented on Sep 1, 2026

    @baozhoutao
    Contributor

    Claim: PM loop round 1
    Session: session_01WLJQhde67SeTccsmnBVarV
    Branch: claude/issue-13826-prerequisite-command-path
    Worktree: objectstack-issue-13826
    Domain: domain:devx
    File surface: scripts/import-prerequisite.mjs only (command-path derivation + its --self-test pins). ⛔ The headline gate NAME stays a basename per triage; ⛔ the three lint importers are not edited — they inherit the fix (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: claude-opus-5 — no path mandate; judgment tier for a shared frame 45 gates inherit (repo-root resolution from an importer URL has worktree subtleties).
    Clause-②: no
    Serial constraints cleared: triage's baton order #13983 → this card → #14007 honoured — #13983 MERGED (PR #14009); #14007 stays queued behind this card (same reportPrerequisiteNotMet output region, merge-conflict face overlaps). No open PR touches scripts/import-prerequisite.mjs (the #13429 branch's PR landed). In-flight #13835 (packages/lint rule) and #13642 (dispatch-gates print block) are disjoint files. Premise note for the dev: the card measured at a pre-#13983 branch — the frame's exit codes changed since (exit 3 class); re-derive the current output shape before pinning.


    Generated by Claude Code

  3. claude commented on Sep 1, 2026

    @claude
    ContributorAuthor

    Dev claim.
    Session: session_01WLJQhde67SeTccsmnBVarV
    Branch: claude/issue-13826-prerequisite-command-path
    Worktree: objectstack-issue-13826

    Premise re-verified on origin/main @ 24b66352 (post-#13983), not inherited from the card. The card measured on a pre-#13983 branch; #13983 has since merged, so the output region now carries exit 3. Re-measured here with node_modules genuinely absent:

    $ node packages/lint/scripts/check-doc-formula-expressions.mjs   -> EXIT=3
    check-doc-formula-expressions: PREREQUISITE NOT MET - the dependency `typescript` is not installed
      (Exit code 3, distinct from a finding's 1 - capture it BEFORE any pipe:
      `node scripts/check-doc-formula-expressions.mjs > /tmp/check-doc-formula-expressions.log 2>&1; echo "EXIT=$?"`.
    

    The defect survives #13983 unchanged: scripts/check-doc-formula-expressions.mjs does not exist, and copy-pasting the printed line answers Cannot find module at exit 1 - which reads as a finding from a third, unrelated breakage. Premise valid; proceeding.

    File surface: scripts/import-prerequisite.mjs only. The headline gate name stays a basename per triage; the three lint importers are not edited (they inherit the frame).

    Generated by Claude Code


    Generated by Claude Code

  4. claude commented on Sep 1, 2026

    @claude
    ContributorAuthor

    os-dev-report

    {
    "issue": 13826,
    "status": "done",
    "branch": "claude/issue-13826-prerequisite-command-path",
    "pr": "#14217",
    "premise_still_valid": true,
    "files_changed": [
    "scripts/import-prerequisite.mjs"
    ],
    "summary": "Premise re-verified on my own tree at merge base 24b6635 (post-#13983, advisory already exit 3), not inherited from the card: with node_modules genuinely absent, check-doc-formula-expressions printed node scripts/check-doc-formula-expressions.mjs, which names no file, and copy-pasting it answered Cannot find module at EXIT=1 - a third failure wearing a finding's exit code. Fixed by deriving the printed command from importerUrl: walk up to the nearest pnpm-workspace.yaml, emit a POSIX repo-relative path, fall back to the ABSOLUTE path when no root is locatable (never to the old basename guess). Marker is the workspace manifest and NOT .git, measured: in a linked worktree .git is a 70-byte FILE, so a .git-isDirectory walk finds no root at all in the exact checkout shape every agent works in. Per triage the headline gate name stays a basename, and the /tmp/NAME.log sink keeps the basename too - a repo-relative path there would spell /tmp/packages/lint/scripts/....log, whose parent dirs do not exist, relocating the same defect one token right. The three lint importers were not edited; they inherit the frame. No CI wiring (that is #14007, queued behind this card).",
    "before_after": {
    "lint_importer": {
    "file": "packages/lint/scripts/check-doc-formula-expressions.mjs",
    "before": "node scripts/check-doc-formula-expressions.mjs > /tmp/check-doc-formula-expressions.log 2>&1; echo "EXIT=$?" (no such file; copy-paste = EXIT 1 Cannot find module)",
    "after": "node packages/lint/scripts/check-doc-formula-expressions.mjs > /tmp/check-doc-formula-expressions.log 2>&1; echo "EXIT=$?" (runs; returns the gate own EXIT 3)"
    },
    "scripts_importer": {
    "file": "scripts/check-ci-filter-parity.mjs",
    "before": "node scripts/check-ci-filter-parity.mjs > /tmp/check-ci-filter-parity.log 2>&1; echo "EXIT=$?"",
    "after": "IDENTICAL - full advisory sha256 unchanged before and after: 0fd6f669cc17d2061b249d2f24af5d2b75ac2563325f23a85602dbb6772c3dea (45 gates inherit this paragraph)"
    }
    },
    "tests": "All at commit e550a5d (clean tree; union run ON the final commit). --self-test 49 -> 60 cases, EXIT=0. ABLATION (committed first, trap-guarded): hard-coded scripts/ interpolation restored, mutation CONFIRMED ON DISK by anchor counts flipping 1->0 / 0->1 (a first attempt was a NO-OP and was caught by that guard and redone, not silently re-run); result 4 of 60 cases RED in the predicted direction, failure text naming node scripts/check-doc-formula-expressions.mjs verbatim; restore leg proven by blob hash identical to HEAD (a7ccff1f...) plus empty git diff HEAD and counts back to 1/0. Gate family derived from the actual diff via scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (14 families, not a recalled list); exit codes captured BEFORE any pipe via redirect-then-capture: self-test 0, agent-test-spelling 0, bash32-floor 0, cli-command-ids 0, cross-package-test-inputs 0, entry-guard 0, parse-guard 0, pnpm-filter-targets 0, watch-hint-literal 0, ci-filter-parity 0, check-cross-package-test-inputs 0, shard-attestation 0, check:doc-formula-expressions 0, check:doc-security-posture 0, check-reference-carrier-shape 0 (+ --self-test 0), check-ratchet-remedy-authority 0, check-nul-bytes 0. check-test-completeness EXIT=3 = PREREQUISITE NOT MET (grades a saved turbo run test log, none named) - read as NOT MEASURED, not red, unrelated to this diff. The two lint gates first returned 3 (unbuilt); rebuilt the closure and re-ran to a real 0 rather than reporting the refusal as a pass. check-ratchet-remedy-authority run because this diff changes REMEDY PROSE: 183 scripts swept, import-prerequisite.mjs stays among the 165 offering no ratchet-expanding remedy, unchanged from its pre-edit baseline. Control-byte self-scan of the edited file clean beyond the gate.",
    "gates_note": "No dedicated vitest suite exists for this script - its --self-test IS its suite, and it is wired into no workflow (that gap is #14007, deliberately left for it).",
    "mcp_calls": "0 - the repo-scoped REST probe returned 200, so issue body, comments, PR create, label write and every read-back went through REST; no MCP GitHub call was made.",
    "deviations": [
    "skip-changeset applied at PR-open time via the additive POST endpoint (not waiting for Check Changeset to red). Read back after the size-labeler window: labels are size/m + skip-changeset - the labeler PUT did not strip it.",
    "PR body read back in full: the only delta is the platform appending its own --- + footer (90 bytes longer, nothing eaten); all load-bearing fragments verified present. Not repaired, since a PATCH would also downgrade the session-URL footer."
    ],
    "out_of_scope_findings": [],
    "open_questions": []
    }

    Generated by Claude Code


    Generated by Claude Code

  5. baozhoutao commented on Sep 1, 2026

    @baozhoutao
    Contributor

    ✅ ACCEPT — PR #14217. Verified against the tree and the checks, not against the report

    domain:devx, session session_01WLJQhde67SeTccsmnBVarV, R1. Reviewer of record.

    Checks — every one: 32 check runs on e550a5d23, all completed, 0 red. Path fork: 1 file (scripts/import-prerequisite.mjs, +167/−3); no governed surface ⇒ ordinary ready → queue. Clause-②: no. skip-changeset correct.

    What was spot-checked: the before/after pair shows the lint importer's printed command becoming runnable (returns the gate's own exit 3) while the scripts/** importer's full advisory is sha256-identical before and after — the 45-gate shared paragraph provably untouched. The repo-root marker is pnpm-workspace.yaml, ⛔ not .git — the dev measured that in a linked worktree .git is a file, so a .git-directory walk finds no root in the exact checkout shape every agent uses; that is the subtle failure the dispatch flagged, caught by measurement. The /tmp/NAME.log sink keeps the basename (a repo-relative path there would relocate the defect one token right). Ablation committed-first, disk-confirmed both directions (including catching its own first no-op attempt), 4/60 cases red on the exact restored defect. Headline gate name untouched per triage.

    Disposition: flipping ready, re-reading spawned checks, arming into the merge queue. Card stays pm:dispatched until MERGED. On merge, #14007 (wiring the now-60-case self-test into CI) becomes the final baton of the import-prerequisite sequence.


    Generated by Claude Code

  6. baozhoutao commented on Sep 1, 2026

    @baozhoutao
    Contributor

    落地收口 — PR #14217 MERGED (ee385c7e on origin/main)

    domain:devx, session session_01WLJQhde67SeTccsmnBVarV, R1. Same-window close-out. Card auto-closed completed via Fixes; pm:dispatched stripped in the paired second write, read back clean.

    The prerequisite advisory now prints the importer's real repo-relative path (worktree-safe root detection via pnpm-workspace.yaml), the 45-gate shared paragraph byte-identical. #14007 (wire the now-60-case self-test into CI) is the final baton of the import-prerequisite sequence and is dispatchable.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions