Skip to content

fix(gates): answer PREREQUISITE NOT MET with the frame's exit code in the last three i18n gates - #14856

Merged
os-zhuang merged 2 commits into
mainfrom
claude/issue-14008-i18n-prerequisite-exit-3
Sep 3, 2026
Merged

fix(gates): answer PREREQUISITE NOT MET with the frame's exit code in the last three i18n gates#14856
os-zhuang merged 2 commits into
mainfrom
claude/issue-14008-i18n-prerequisite-exit-3

Conversation

@claude

@claude claude Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Fixes #14008
Fixes #13825

Two cards, one PR, because triage bound them: they change the same line of
packages/cli/scripts/check-app-nav-i18n.mjs#14008 the exit code that banner
announces, #13825 the banner's wording — and landing them apart would only make
them conflict (#14008 triage, R+81 and R+83: 「与 #13825 同笔落地」).

What moved

scripts/import-prerequisite.mjs is the frame 45 gates inherit, and since #13983
it answers PREREQUISITE NOT MET with EXIT_PREREQUISITE_NOT_MET = 3 — the code
five sibling sites already meant by those two words. Three gates still answered
them with 1. They no longer do:

site before after
scripts/check-i18n-coverage.mjs — CLI-not-built refusal EXIT=1 EXIT=3
scripts/check-i18n-coverage.mjsCOULD NOT MEASURE (partial round) process.exit(1) process.exit(EXIT_PREREQUISITE_NOT_MET)
scripts/check-i18n-coverage.mjsPOPULATION EMPTY process.exit(1) process.exit(EXIT_PREREQUISITE_NOT_MET)
scripts/check-i18n-bundles.mjs — prerequisite refusal EXIT=1 EXIT=3
packages/cli/scripts/check-app-nav-i18n.mjs — prerequisite refusal EXIT=1 EXIT=3

Three of the five were exercised live in an unbuilt worktree, exit code captured
BEFORE any pipe, both before and after the edit. The other two sit behind the
CLI-build probe, which fires first, so they are unreachable without a build and
are reported here as source-level readings only — stated rather than glossed.

⛔ No new literal 3. All three files import EXIT_PREREQUISITE_NOT_MET and
EXIT_FINDINGS from scripts/import-prerequisite.mjs and print them into the
advisory, so the number a reader sees and the number the process returns cannot
drift apart. scripts/cli-build-prerequisite.mjs stayed untouched: it did not
need to re-export the constant, because importing the frame directly works from
both roots — packages/lint/scripts/* already reach repo-root gate infrastructure
that way, which is the precedent the packages/cli import follows.

⛔ Only the refusals moved. Each of these paths already printed "nothing was
measured / nothing was compared". Every real-verdict exit and every self-test exit
in the three files is still 1: check-i18n-coverage.mjs keeps 3 of its 6, and
check-i18n-bundles.mjs 4 of its 5, and check-app-nav-i18n.mjs 3 of its 4.

The doc comments that argued for the old number ("inventing a second failure code
would be a new contract nobody asked for") were the strongest remaining statement
of a position #13983 already settled, so they are replaced rather than left to
contradict the code beside them.

The #13825 half — the wording, and its machine-checkable acceptance

The old sentence was true but prescribed echo "EXIT=$?" without saying WHERE, so
a reader doing the natural thing — ... | tail -4; echo "EXIT=$?" — read tail's
status, which is the exact false green the prescription exists to prevent. Per
triage (「抄现成的,⛔ 不要另写一版」) the file adopts #13429's converged paragraph
verbatim apart from this gate's own command. ⛔ No eleventh wording, and ⛔ nothing
appended to the old sentence: the old sentence is deleted.

Acceptance is the census, not the reading:

  • git grep -l "no pipe shape repairs it"7 files before, 8 after, the new
    one being packages/cli/scripts/check-app-nav-i18n.mjs. That grep now really
    does equal the whole repo; before this PR every "all unified" judgement made
    with it was short by one.
  • git grep -n "the PIPE's status" -- 'scripts/**' 'packages/**'1 hit before,
    0 after
    (exit 1 from grep, captured before any pipe).

⚠️ One thing worth knowing for the next editor: the new doc comment on that gate
first explained the defect by quoting the old sentence, which silently put the
census-negative string back and would have left the acceptance criterion failing
while the code was correct. It is reworded, and the comment now says out loud that
the string must not be reintroduced even as a quotation.

Governed rider

.claude/skills/pm-dispatch/references/lanes/services.md:22 read, verbatim:

check:i18n 以「PREREQUISITE NOT MET — workspace CLI 未 build」退 1 不是漂移。

That sentence becomes false the moment these gates move, so it moves in the same
stroke — the minimal factual edit triage specified, 退 1 → 退 3, Chinese text kept
Chinese, nothing else in that file, one character of diff. It is the only place in
the repo keying on the old number (git grep -n "退 1" returns exactly this line).

Because the diff carries a .claude/** edit this is a governed surface PR
(AGENTS.md Prime Directive 14): opened as a draft, never flipped ready, and
auto-merge is never armed by this seat. The PM requests the human reviewers.

Changeset: skip-changeset, and the reading behind it

packages/cli's package.json declares "files": ["dist", "README.md", "CHANGELOG.md"]. scripts/ is not in it, so
packages/cli/scripts/check-app-nav-i18n.mjs is an unshipped dev gate rather than
a shipped file whose advisory text reaches consumers — the npm tarball is
unchanged by this PR. The other three paths are repo-root scripts/** and
.claude/**, neither of which publishes from any released package. ⇒ the diff
publishes nothing, which is exactly what the label is for (AGENTS.md: the label is
for a diff that publishes nothing, never a substitute for a fix's patch
changeset). Had scripts/ been shipped, this would have taken a patch changeset
instead.

Verification

All 38 gate families this card owes, derived AFTER the edits from the real change
set (node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack
— 36 by path + 2 by change kind; the --repo assertion checked against this
checkout's origin), run on the final commit fcdbc6d2, over a workspace
built through the shared verify lock (turbo run build, 67/67 then 70/70 tasks,
both exit 0). Every exit code was captured BEFORE any pipe — redirect to a file,
read $?, then look at the file.

37 of 38 green (exit 0). The two gates this PR edits are among them, and they
answer their real questions rather than refusing:

pnpm check:i18n              EXIT=0   check-i18n-bundles: OK (9 package(s) — all bundles in sync…)
pnpm check:i18n-coverage     EXIT=0   check-i18n-coverage: OK (13 config(s), 691 baselined…, none new)

check:i18n-coverage's own --self-test is part of that pass and it exercises the
missing-CLI-build classifier directly ("the missing-CLI-build, i18n-rule and
per-config-failure classifiers all go red, stay distinct…"), so the refusal path
this PR moved is covered by the gate's own pins, not only by the live runs above.

Also green, and named because they are the families that read what this diff
touched: check:self-test-wired, check:pm-skill-ratchet, check:pm-skill-id-lint,
check:pm-governed-merges, check:skill-frame-sync, check:doc-authoring,
check:published-files, check:undeclared-dep-imports, check:entry-guard,
check:parse-guard, check:watch-hint-literal, check:ratchet-remedy-authority,
check:pnpm-filter-targets, check:cross-package-test-inputs,
check:dual-build-cjs-loads, check:pm-dispatch-gates,
node scripts/pm/bare-root-worklist.mjs --self-test,
pnpm --filter @objectstack/lint run check:doc-formula-expressions. Plus
pnpm check:nul-bytes (8070 files, clean) and a direct control-byte sweep of the
four changed files (grep -naP over the C0 range, zero hits).

1 of 38 NOT MEASURED, declared rather than counted green:
node scripts/check-test-completeness.mjs returns 3 — PREREQUISITE NOT MET — this gate grades a saved 'turbo run test' log, and no log was named. It does not
run tests and cannot produce one; CI tees the log and passes the path on every
invocation. That is a standing property of running it outside CI, identical for
every card, and it is NOT a red and NOT a pass.

pnpm lint (whole-repo ESLint) is CI's run and is not claimed here.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LAwHpn4uVuf4N1geBcD5i3


Generated by Claude Code

… the last three i18n gates

`#13983` moved `scripts/import-prerequisite.mjs` -- the frame 45 gates inherit --
onto `EXIT_PREREQUISITE_NOT_MET` (3), joining the five sibling sites that already
meant 3 by those two words. Three gates still answered them with 1:

  - `scripts/check-i18n-coverage.mjs`      3 refusal paths
  - `scripts/check-i18n-bundles.mjs`       1
  - `packages/cli/scripts/check-app-nav-i18n.mjs`  1

All five now import `EXIT_PREREQUISITE_NOT_MET` / `EXIT_FINDINGS` from that frame
rather than re-picking them, so there is no new literal 3 in the diff, and each
printed advisory announces the code it actually exits with. The real-verdict and
self-test exits in these files are untouched; only the refusals that already say
"nothing was measured" moved.

`packages/cli/scripts/check-app-nav-i18n.mjs` also takes the converged pipe-shape
paragraph (`#13429`'s, verbatim apart from this gate's own command). Its old
sentence prescribed `echo "EXIT=$?"` without saying WHERE, so a reader who did
the natural thing read `tail`'s status -- the exact false green the prescription
exists to prevent. The census `git grep -l "no pipe shape repairs it"` goes 7
files to 8, and the census-negative string now has zero hits under `scripts/**`
and `packages/**`.

The lane doc `.claude/skills/pm-dispatch/references/lanes/services.md:22` keyed on
the old number, so it moves in the same stroke rather than starting to lie.
Governed surface: this lands as a draft for a human merge.

Cards: `#14008` (the exit code) and `#13825` (the banner wording) -- declared once
in the PR body, since this branch squashes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LAwHpn4uVuf4N1geBcD5i3
@claude claude Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 3, 2026
@github-actions github-actions Bot added the size/s label Sep 3, 2026
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

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 — 0 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 2263ca4d679026335f559184c0bed4e76d35a242packageMentionDocs.

@claude

claude Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

PM status (domain:devx execution seat, session session_01LAwHpn4uVuf4N1geBcD5i3): reviewed and ACCEPTED on #14008 (checklist there) and #13825. The one-character change to .claude/skills/pm-dispatch/references/lanes/services.md makes this a governed-surface PR, so it stays a draft and is not armed by this seat; review requested from os-zhuang and hotlong for the human merge. CI on fcdbc6d22 had no red at review time (3 checks still running).


Generated by Claude Code

@claude

claude Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

PM note on the red (domain:devx execution seat, session session_01LAwHpn4uVuf4N1geBcD5i3): Test Core (1/6) on fcdbc6d22packages/cli pnpm run test exit 1 after 14 min (job 100537281700; annotations carry only the exit code, the log download is refused from this container). Ruled not this PR's: the diff changes a refusal exit code + banner text in packages/cli/scripts/check-app-nav-i18n.mjs and two root gates, and no test file anywhere references any of the three gates (git grep over *.test.* / *.spec.* on origin/main: none; packages/cli/test and src: none) — the cli vitest suite cannot see this change, while the shard/package/duration match the #14648 signature that has hit five other PRs on this lane tonight post-fix. Single permitted non-own re-trigger, spent now: update-branch (real merge of current origin/main). Same shard red again ⇒ parked under the #14648 reading; any other red is real and goes back to the dev. The PR stays a governed draft either way (human merge).


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review September 3, 2026 08:54
@os-zhuang
os-zhuang enabled auto-merge September 3, 2026 08:54
@os-zhuang
os-zhuang added this pull request to the merge queue Sep 3, 2026
Merged via the queue into main with commit 365141f Sep 3, 2026
35 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-14008-i18n-prerequisite-exit-3 branch September 3, 2026 11:08
os-litant pushed a commit that referenced this pull request Sep 3, 2026
Two conflicts, both against main commits that landed after this branch's
merge base 5ff5f95:

- .claude/skills/pm-dispatch/references/lanes/services.md: main's
  365141f (#14856) edited the very bullet this branch deletes (the
  bullet moved to references/platform-readings.md). Settled to OUR side:
  the bullet stays deleted and the move stands. The FACT main corrected in
  that bullet (the i18n gates answer PREREQUISITE NOT MET with exit 3, not
  1) is carried into the moved text by the follow-up commit, so this merge
  commit's non-conflict content stays a pure merge of the two parents.

- scripts/check-ratchet-remedy-authority.mjs: main's a610775 (#14896)
  added the self-test battery roster and floor at the spot where this
  branch's section (21) sits. Settled by keeping BOTH: section (21) opens
  with battery('(21) ...'), is declared in SELF_TEST_BATTERIES at its case
  count (2), and sits before the floor-evaluation block; the roster's own
  size pin SELF_TEST_BATTERY_FLOOR moves 20 -> 21 by the header's own
  convention (the roster size is pinned at the declared count). The
  exception phrase stays assembled, never spelled, in author-facing text,
  and the gate's run line is byte-identical to main's.

scripts/pm/check-skill-line-ratchet.mjs auto-merged with main's c47a5ac
(#14853) verdict handshake; the two run-path verdict lines this branch adds
are outside that handshake, which covers selfTest()'s sentinel return only.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
os-litant pushed a commit that referenced this pull request Sep 3, 2026
…8n reading

references/platform-readings.md line 301 -- the bullet this branch moved out
of lanes/services.md -- still said the i18n gates answer PREREQUISITE NOT MET
with exit 1. main's 365141f (#14856) moved the last three i18n gates
(scripts/check-i18n-coverage.mjs, scripts/check-i18n-bundles.mjs,
packages/cli/scripts/check-app-nav-i18n.mjs) onto EXIT_PREREQUISITE_NOT_MET
(3, from scripts/import-prerequisite.mjs) and corrected the source bullet to
"退 3" in the same commit; the merge took our deletion of that bullet, so the
corrected fact is carried into the moved text here rather than re-landing
the claim main already corrected. Same line count (324, ceiling unchanged);
no other edit.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
hotlong pushed a commit that referenced this pull request Sep 3, 2026
…e first one (#14860)

* tooling(pm): admit a same-PR cross-file move in the skill line ratchet

The per-file line ratchet is shrink-only, which prices a FACT MOVING TO ITS
SINGLE-SOURCE FILE as though it were growth: the destination sits at headroom 0
like every other entry, so consolidating a misplaced per-repo reading needed a
per-instance maintainer ruling even though the corpus shrank on net.

Adds the declared cross-file move as the one raise an author may take alone,
under the three ruled conditions: the destination's raise is at most the net
decrease of the sources it names, the map-wide total does not increase, and the
declaration cites the ruling that authorises it. The declaration is in-tree data
carrying each participant's pre-move ceiling, so the arithmetic is computable
from the working tree alone — no baseline ref a shallow CI checkout can fail to
provide, and no answer that evaporates once the move has landed.

Verdict lines and exit codes are unchanged; the map is empty in this commit.

Part of #14685
Part of #13597

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1

* tooling: record the authorised cross-file move in the remedy-authority gate

The #8435 convention asks who may take a registry-growing remedy on a
shrink-only ratchet, and the answer has always been "a maintainer, per
instance". One class of act is now authorised generally: a cross-file move in
the per-file line ratchet, while the destination's raise is covered by the named
sources' net decrease, the map-wide total does not increase, and the raised entry
cites the ruling.

The exception is written in comment space, because author-facing text describing
a registry-growing act would make this gate an instance of the convention it
enforces. Two self-test assertions hold it there: one that the phrase never
reaches an author-facing message, one that the amended ratchet is still outside
this non-recursive walk — so the exception stays honestly documentary, and a
wider walk reds here instead of admitting a remedy class in silence. No new
verdict class: a fifth class with a population of zero is enforcement nobody has.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1

* docs(pm-dispatch): consolidate three misplaced per-repo readings

A per-repo gate reading written into one lane charter is read by that lane and
missed by the other six. Their single source is references/platform-readings.md,
so the three the Phase-1 tables measured move there, under the cross-file-move
rule this branch adds:

  lanes/cli.md        -4  the objectstack required-check set, `in_progress` is
                          not a pass, advisory red into main is shared damage
  lanes/services.md   -2  the two aggregate-reading gate boundaries
  rest-channel.md     -5  the merge_group count-is-not-a-mechanism tombstone,
                          moved byte-identically

platform-readings.md +10 against a net source decrease of 11, so the corpus
falls by one line: the per-job-conclusion rule cli.md carried is already stated
in the destination's queue section, so the moved bullet points at it rather than
paying for a second copy. Ceilings move with the text (314→324, 35→31, 30→28,
93→88) and the move is declared, so the gate re-derives the arithmetic on every
run instead of trusting the comment beside it.

No pointer is left at any source: nothing at the three sites becomes unreadable
without the moved sentence, and a pointer per move is how a corpus grows while
its ledger says it shrank.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1

* tooling(pm): singularise the cross-file move verdict's line counts

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1

* docs(pm-dispatch): carry main's corrected exit code into the moved i18n reading

references/platform-readings.md line 301 -- the bullet this branch moved out
of lanes/services.md -- still said the i18n gates answer PREREQUISITE NOT MET
with exit 1. main's 365141f (#14856) moved the last three i18n gates
(scripts/check-i18n-coverage.mjs, scripts/check-i18n-bundles.mjs,
packages/cli/scripts/check-app-nav-i18n.mjs) onto EXIT_PREREQUISITE_NOT_MET
(3, from scripts/import-prerequisite.mjs) and corrected the source bullet to
"退 3" in the same commit; the merge took our deletion of that bullet, so the
corrected fact is carried into the moved text here rather than re-landing
the claim main already corrected. Same line count (324, ceiling unchanged);
no other edit.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1

---------

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

documentation Improvements or additions to documentation size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants