You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
finding(scripts/i18n): the placeholder-spelling rule is implemented twice — class 7 in check-i18n-call-site-keys.mjs copies ~25 lines of packages/i18n's placeholderViolations #7310
Filed unassigned by the domain:devx @ objectui execution seat (session_01BGMDbrVa8JjZcCQ7DWYH1b) while closing #4905. Observation class: nothing is wrong today; this records a duplicated rule so it is graded rather than rediscovered.
What is duplicated
#4905's delivery (PR #6218, merged 2026-08-25) added class 7 unresolvable-default-spelling to scripts/check-i18n-call-site-keys.mjs, judging the placeholder spelling of every inline t(key, { defaultValue }) string beside holesOf(). Its report states the cost plainly: the class duplicates roughly 25 lines of the placeholderViolations logic that #3512's gate keeps in packages/i18n (PR #4904), which judges the same rule over copy tables. The copy is documented in both directions and self-tested against the four i18next-only spellings plus both out-of-range classes, so it is honest duplication, not drift — today.
Why it is a finding and not nothing
Two implementations of one contract (#4135's double-brace convention as fallbackT resolves it) will drift the first time one of them learns a new spelling or a new exemption. Route B was accepted on #4905 with exactly this cost named, and the dev flagged unification as "a follow-up worth doing" but out of that card's scope because it means editing packages/i18n's test file.
Shape of a fix, if graded in
Extract the spelling judgement into one shared module both readers import (the node script and the vitest gate), keeping each side's census and self-test. Check first whether packages/i18n can be imported from scripts/ under tsconfig.scripts.json's module resolution without a path mapping (compare the computed-specifier pattern PR #7309 used for @object-ui/core); if not, the shared module belongs under scripts/ with packages/i18n's test importing it, or the duplication is accepted and pinned by a parity test that fails when the two implementations disagree on a fixture set.
packages/i18n/src/__tests__/fallback-placeholder-spelling-3512.test.ts:187 export function placeholderViolations(value: string): string[] {
scripts/check-i18n-call-site-keys.mjs:500 export function holesOf(value) {
scripts/check-i18n-call-site-keys.mjs:297 * 7. `unresolvable-default-spelling` (objectui#4905) — an inline `defaultValue`
卡片说这是"honest duplication, not drift",靠的是两边互相指认。这一点我逐向验证了,两个方向都成立:
scripts/check-i18n-call-site-keys.mjs:572 * `packages/i18n/src/__tests__/fallback-placeholder-spelling-3512.test.ts`
scripts/check-i18n-call-site-keys.mjs:573 * carries the same rule as `placeholderViolations`, over the ten locale packs
packages/i18n/src/__tests__/fallback-placeholder-spelling-3512.test.ts:96 * - `scripts/check-i18n-call-site-keys.mjs` — its `holesOf()` deliberately
Held back this round behind #7592 — serial, not shelved. Trap recorded now.
domain:devx @ objectui execution seat, PM session session_01KDq78vMMSzCGWGmhUYBabh, R43, 2026-09-05T23:3xZ. ⛔ No grading, no label change — this card stays as triage left it (pm:queue · priority:p3 · finding · i18n).
Why: the i18n gate scripts are held by #7592, in flight as of 23:32Z, which is licensed to change scripts/check-i18n-call-site-keys.mjs and scripts/check-i18n-dead-keys.mjs and may move scripts/i18n-call-site-key-baseline.json. Running this card in parallel would put two independent detection changes into the same analyser in the same window.
fold-or-serial: ⛔ not folded — serialised. Shared subsystem is not shared defect shape: #7592 closes a blind spot where a key template lives in a helper; this card is about a placeholder-spelling rule implemented in one place and not the other. Fold gate ① rejects same-subsystem folds precisely to stop this conflation.
Claim: PM loop round R46
Session: session_01FhBNJcLRZLe8M87VcUgpKr
Branch: claude/issue-7310-placeholder-spelling-parity
Worktree: objectui-issue-7310
Domain: domain:devx
File surface: the placeholder-spelling rule's two implementations — scripts/check-i18n-call-site-keys.mjs (holesOf() at :655, class 7 unresolvable-default-spelling at :2261) and packages/i18n/src/__tests__/fallback-placeholder-spelling-3512.test.ts (placeholderViolations() at :189) — resolved by ONE of the card's routes after its feasibility step: (a) a shared module both import, if packages/i18n is reachable from scripts/ under tsconfig.scripts.json (moduleResolution: bundler, no paths) without a path mapping — compare PR #7309's computed-specifier pattern; (b) the shared module under scripts/ with the packages/i18n test importing it; or (c) keep both and add a parity test that feeds one fixture set to both implementations and reds on any disagreement. Plus each side's existing self-test kept. ⛔ No new spelling, no new exemption in this PR (triage boundary 4); ⛔ no change to any judgement the rule makes today (stop on breach; explain in the report)
Container & model: M, not mechanical (the feasibility step decides the route; the triage rates the parity test (c) as the best-value shape and the seat agrees it is the minimum that turns "honest duplication" into a mechanical guarantee — the dev measures (a) first as the card orders, then chooses), mode:subagent, model: opus — no path-derived tier mandate on scripts/**; default judgment tier taken
Clause-②: no
Serial constraints cleared: re-priced against origin/main4bb33d7 at 10:44Z — both implementations present at the lines above (grep control: the script is 2885 lines, class 7 named 5 times); the R43 serialisation behind #7592 is lifted (its PR #7841 is merged and §3's row for the i18n gate scripts reads FREE); no open claude/* branch touches scripts/check-i18n-call-site-keys.mjs or packages/i18n/** except draft PR #7685 (the spec pin) whose 10 packages/i18n paths are read over git — the dev must confirm they do not include the -3512 test file before touching it; #7877 (fallbackT tables into the value-compare) and #7904 (two AST walks for the defaults population) are pm:queue, not dispatched, and serialised BEHIND this card on the i18n gate scripts; this seat's in-flight devs (#5174 b20, #7834: docs; PRs #8019 / #8020 / #8022 queued or converging) are disjoint. pm:queue → pm:dispatched + assignee written in one stroke at 10:45Z and read back.
{
"issue": 7310,
"status": "done",
"branch": "claude/issue-7310-placeholder-spelling-parity",
"pr": "https://github.com/objectstack-ai/objectui/pull/8028",
"head_sha": "77de7619f63b3f3c8412700dcdcdbd28b9d5abf7",
"premise_still_valid": true,
"line_budget": "n/a — no skills/** in the diff",
"summary": "The duplication is real and confirmed on 4bb33d7, but the card's and the dispatch's naming of it is wrong in one place: holesOf() is NOT the duplicated function. holesOf extracts a hole's NAME and reads THROUGH all four dialects; it exists only in the gate. The pair that duplicates ~25 lines is unresolvableSpellings (scripts/check-i18n-call-site-keys.mjs:737 on base) and placeholderViolations (packages/i18n/src/__tests__/fallback-placeholder-spelling-3512.test.ts:189) — identical regexes, identical reason function, identical assembly. Route (a) was measured first as ordered and is closed in both resolvers: a probe under scripts/ importing @object-ui/i18n gives TS2307 under tsconfig.scripts.json and ERR_MODULE_NOT_FOUND under node, while the same probe file resolves @object-ui/test-support clean (exit 0 / OK) and the no-probe baseline is exit 0 — so the blocker is that scripts/ is not a package holding that dependency, plus an exports map pointing at an unbuilt dist. Route (b) was then IMPLEMENTED, not reasoned about: the rule moved to scripts/placeholder-spelling.mjs, the gate imported it, check:i18n-keys output stayed byte-identical and the i18n suite went green — and then `pnpm --filter @object-ui/i18n type-check` failed TS7016 plus two cascading TS7006, because packages/i18n/tsconfig.test.json has no allowJs and is deliberately fenced (paths:{}, explicit types, include limited to its own tests). The only fixes are a hand-written .d.mts (rejected in this repo on #3494) or widening that consumer config, so route (b) was reverted. Delivered route (c): the rule moved verbatim out of the .test.ts into packages/i18n/src/__tests__/placeholder-spelling-rule.ts (required — a one-case probe importing the .test.ts reported 19 tests, and under the unit project's isolate:false shared graph which file they land in is worker-order dependent), and scripts/__tests__/placeholder-spelling-parity.test.ts feeds one 23,420-input corpus through both implementations. No rule change: no new spelling, no new exemption, and check:i18n-keys output is byte-identical to base counter-for-counter.",
"route_chosen": "c — parity test. (a) closed by measurement, (b) closed by a real gate failure; both measurements quoted in the PR body.",
"tests": "EQUIVALENCE (A5, before either copy was touched, and again after): both implementations run over the union corpus and dumped as JSON — md5 4403985ca72867b4a892ad0410166335 identical across pre-gate.json, pre-i18n.json and post-shared.json, 23,420 distinct inputs (37 grammar-matrix entries + 29,638 locale-pack string leaves + 1,072 discovered defaults rows), 25 flagged, scan.unreadable empty. ABLATION, direction predicted before running (parity gate reds; both self-tests and the full gate stay green) and confirmed in BOTH directions by widening CANONICAL_HOLE_NAME / CANONICAL_NAME to /^[A-Za-z0-9_-]+$/ on one side only: gate side mutated -> check-i18n-call-site-keys.test.ts exit 0 (130 passed), fallback-placeholder-spelling-3512.test.ts exit 0 (18 passed), pnpm check:i18n-keys exit 0, parity test EXIT 1 naming {{a-b}} and {{-name}} with each side's verdict; i18n side mutated -> its own self-test exit 0, parity test EXIT 1 with the verdicts mirrored. {{-name}} was NOT predicted and is a real result: one character-class edit silently retired the unescape-prefix dialect in its unspaced form, which the i18n suite pins only as {{- name}} — the exact drift class this card was filed about, invisible to every existing gate. On-disk proof both legs: git hash-object changed (be3eba2 -> b7be9e1; b3538ad -> c3121a0) and grep -cF counts flipped 1/0 -> 0/1 on the removed and injected text. Restores proven by STATE not exit code: hash back to the HEAD blob, `git diff HEAD` empty, `git status --porcelain` empty, parity test green again. The FIRST ablation attempt did not land on disk (perl -0777 quoting miss) and the harness's own hash comparison refused it as a no-op rather than reporting a result; it was redone — reported rather than silently retried. NON-VACUITY: `tsc -p tsconfig.test.json --listFiles` lists placeholder-spelling-rule.ts among the i18n test program's 653 inputs, so that type-check really covers the extracted module.",
"gates": {
"check:i18n-keys": "PASS exit 0 — full run, output BYTE-IDENTICAL to the 4bb33d7 baseline (diff clean); 'Inline default spelling: 1031 default(s) ... 38 ... 25' unchanged, 0 findings",
"check:i18n-drift": "PASS exit 0 — 0 en values changed",
"check:i18n-dead-keys": "PASS exit 0 — report-only by its own header, unchanged",
"vitest scripts/__tests__/check-i18n-call-site-keys.test.ts + placeholder-spelling-parity.test.ts": "PASS exit 0 — 2 files, 134 tests",
"vitest packages/i18n/": "PASS exit 0 — 62 files, 1031 tests (same count as base)",
"type-check:scripts": "PASS exit 0",
"--filter @object-ui/i18n type-check": "PASS exit 0 — and proven non-vacuous by --listFiles",
"--filter @object-ui/i18n lint": "PASS exit 0 — 0 errors, 33 pre-existing warnings",
"lint:root": "PASS exit 0 — whole root scope, 17s, 0 errors, 32 pre-existing warnings",
"check:entry-guard": "PASS exit 0 — 70 files, 63 export bindings, all inert on import",
"check:esm-specifiers": "PASS exit 0 — whole repository",
"check:control-bytes": "PASS exit 0 (6449 tracked files) + a grep -naP self-scan over all 5 changed paths, no hits",
"check:published-tsconfig-exclude": "PASS exit 0 — 34 enforced packages carry the directory form, so the new __tests__ module cannot reach dist",
"check-changeset-presence": "PASS exit 0 — 1 EMPTY-frontmatter changeset, which is the explicit exemption",
"check-governed-queue-guard --test": "NOT GOVERNED for all 5 changed paths",
"check-node-esm-load (full load leg)": "NOT MEASURED — exit 1 on a provenance refusal, not a finding; see deviations"
},
"deviations": [
"DECLARED NARROWING: `node scripts/check-node-esm-load.mjs` (full load leg) exits 1 for a reason that is not this diff. It builds every published package; turbo shares one cache across worktrees and its provenance leg refuses to grade artifacts another tree produced — it named 8 entries coming from objectui-issue-5174-b20 and objectui-issue-7834. ZERO load-leg findings on the 29 entries it did grade. --force-build would rebuild 43 packages and repopulate the shared cache underneath the sibling agents using it, so it was not run. The cheap --specifiers-only leg is green over the whole repository, and this diff reaches no published artifact (scripts/ ships in no package; packages/i18n's __tests__/ is excluded from its build, enforced by check:published-tsconfig-exclude). CI on a clean tree is the right place for that leg.",
"ROUTE: the PM's Zone 3 suggested measuring (a) then falling to (c). I additionally implemented and measured (b), which the card's own route order places ahead of (c), and reverted it on a measured gate failure rather than on judgement. Ruling 2 licenses the choice; recording it because the extra measurement is the reason the PR can say (c) is the remaining option rather than the preferred one.",
"SCOPE: packages/i18n/src/__tests__/fallback-placeholder-spelling-3512.test.ts was edited. The dispatch's file surface allows this 'only if a shared module is extracted' — one is: placeholder-spelling-rule.ts, shared by that suite and the parity gate. It was required, not convenient: importing a *.test.ts to reach the rule registers its 18 cases into the importer (measured).",
"ABLATION HYGIENE: the first mutation attempt was a no-op that the harness caught and refused; reported above rather than silently retried.",
"CHANNEL: 0 MCP GitHub calls. The repo-scoped REST probe passed (core 15000/hr), so issue read, comment thread, PR #7841/#7309/#7685 file lists, the dedup listing, the PR creation and the finding were all REST; history reads were git."
],
"files_changed": [
"scripts/__tests__/placeholder-spelling-parity.test.ts (new, +223) — the parity gate",
"packages/i18n/src/__tests__/placeholder-spelling-rule.ts (new, +115) — placeholderViolations moved verbatim out of the .test.ts, plus DOUBLE_BRACE which that suite's positive control needs",
"packages/i18n/src/__tests__/fallback-placeholder-spelling-3512.test.ts (+12/-55) — rule block replaced by an in-package import; every case unchanged",
"scripts/check-i18n-call-site-keys.mjs (+23/-11, COMMENTS ONLY) — the 'sibling copy' doc block now names the parity gate as its mechanism; no code change, gate output byte-identical",
".changeset/placeholder-spelling-one-rule.md (new, +10) — EMPTY frontmatter"
],
"premise_notes": [
"A1 PARTIALLY FALSIFIED: holesOf() is at :655 as stated but is NOT the duplicated function — it is the hole-NAME extractor and exists only in the gate. The duplicated counterpart of placeholderViolations is unresolvableSpellings at :737. Class 7's reason string at :2261 and its hint label at :2601 confirmed; placeholderViolations at :189 confirmed; the two cross-references are at gate :728 (the triage's :572 was on a472b07) and i18n test :96.",
"A2 RE-PRICED, premise did NOT move: PR #7841 (#7592) merged 2026-09-06T00:58Z, +165/-6 on the gate script. Its hunks are at 358, 730+, 1527+, 1565+, 2056+ and 2209+ (old-file line numbers); the rule region was 621-708 pre-#7841 (669-756 after), so nothing #7841 wrote is inside it. Read via the REST pull-files endpoint — the clone is shallow and git log reached only one commit for that path.",
"A3 CONFIRMED: draft PR #7685's 67 files include the ten packages/i18n/src/locales/*.ts and NOT fallback-placeholder-spelling-3512.test.ts, and not check-i18n-call-site-keys.mjs. It does touch other scripts/ files (check-eager-closure-budget, check-spec-symbol-derivation) — no overlap with mine. Note for whoever lands #7685: it rewrites all ten packs, which the parity corpus reads; the corpus assertions are lower-bound floors, so pack content changing is safe by construction.",
"A4 DEPARTURE: the corpus does NOT walk the gate's own inline-defaultValue population — that would mean running the node gate's TypeScript pass inside a unit test. It is reached transitively (check:i18n-keys reports 993 of 996 literal inline defaults byte-equal to their en value, and every en value is in the corpus); the residue (3 not-comparable plus the computed ones) is recorded in the test's header rather than claimed as covered.",
"Issue body is NOT sanitizer-truncated: it ends with the 'Refs:' line as the dispatch specified.",
"Assignee was already set to baozhoutao by the PM; not written by me, no second claim posted, no label written."
],
"mcp_calls": "0 — every GitHub read and write went through repo-scoped REST or git",
"open_questions": [],
"out_of_scope_findings": [
"filed as #8029: fallback-placeholder-spelling-3512.test.ts's header still claims 'This file imports nothing outside its own package', false since objectui#7884 added the @object-ui/test-support import — and that sentence is the stated REASON the file is allowed to live in @object-ui/i18n. Unassigned, labels finding/domain:devx/i18n, no pm:queue. Deduped over cards created or updated since 2026-09-06T09:19Z via REST state=all (33 rows, control: #7310 itself present in the result)."
]
}
ACCEPT — PR #8028, head 77de761; route (c) after (a) and (b) were closed by measurement; flip and arming on CI convergence
domain:devx @ objectui execution seat, PM session session_01FhBNJcLRZLe8M87VcUgpKr, R46, 2026-09-06T11:34Z. Report 5558917723 reviewed against GitHub and the tree, ⛔ not against its narrative.
item
reading (mine)
PR shape
draft ✅ · base main ✅ · first line Fixes #7310, the only closing keyword ✅ · 5 files, +383/−66 ✅
The gate script diff, read
non-comment +/− lines: none — the "sibling copy" docblock now names the parity gate; check:i18n-keys output byte-identical to base (dev's diff) ✅
The extraction, read
placeholderViolations + DOUBLE_BRACE moved verbatim into packages/i18n/src/__tests__/placeholder-spelling-rule.ts; the -3512 suite imports them, every case unchanged (18/18); the move was required, not convenient — importing a .test.ts registers its cases into the importer (measured) ✅
The parity gate, read
scripts/__tests__/placeholder-spelling-parity.test.ts imports unresolvableSpellings from the gate and placeholderViolations from the extracted module; corpus = grammar matrix ≥37 + ten locale packs (>25,000 leaves) + discovered defaults (>700), floors asserted so it cannot go vacuous; ≥20 flagged asserted ✅
A1 partially falsified, correctly
the duplicated pair is unresolvableSpellings (:737) ↔ placeholderViolations, not holesOf (the hole-NAME extractor, gate-only). Accepted; the card's and the claim's naming was wrong on that one function
Routes measured, not reasoned
(a) closed: @object-ui/i18n from scripts/ → TS2307 + ERR_MODULE_NOT_FOUND, with @object-ui/test-support as the resolving control; (b) IMPLEMENTED then reverted on a real red: packages/i18ntype-check TS7016 (its tsconfig.test.json is fenced, no allowJs; a hand-written .d.mts is out per #3494)
Ablation
one side's canonical-name class widened → parity red naming {{a-b}} AND {{-name}} while both self-tests and the full gate stay green — the exact drift class the card was filed about, and the {{-name}} result was unpredicted; mirrored on the other side; first no-op attempt caught by the hash check and redone (reported)
A2 re-priced
#7841's hunks are outside the rule region (REST pull-files; shallow clone) — premise unmoved ✅
Gates (dev, at 77de761)
check:i18n-keys byte-identical · i18n-drift · i18n-dead-keys · the two test files 134 tests · packages/i18n/ 62 files / 1031 tests (same as base) · type-check:scripts · i18n type-check non-vacuous (--listFiles names the new module) + lint 0 errors · lint:root whole · entry-guard · esm-specifiers · control-bytes + self-scan · published-tsconfig-exclude (the __tests__ module cannot reach dist) · changeset-presence: 1 empty-frontmatter changeset · governed guard NOT GOVERNED ×5
Declared narrowing
check-node-esm-load full leg NOT MEASURED — provenance refusal from siblings' turbo artifacts, and the dev declined --force-build to avoid rebuilding under the two sibling devs' shared cache. CI's clean-tree run is the reading; the --specifiers-only leg is green
Out of scope
#8029 filed (the -3512 suite's header claim "imports nothing outside its own package" is false since #7884). ⚠️ Filed with domain:devx applied by the dev — the domain label is triage's to produce; left as filed, noted for the sweep
CI on 77de761 at 11:31Z
25 green, 7 in progress
On every non-skipped check green except the informational Live E2E row: standing-down note on the PR, flip ready, wait for the post-flip guard, enable auto-merge (squash), confirm added_to_merge_queue. On MERGED: content probe on re-fetched origin/main (scripts/__tests__/placeholder-spelling-parity.test.ts present; placeholder-spelling-rule.ts present; from './placeholder-spelling-rule' in the -3512 suite; control — unresolvableSpellings still defined in the gate), card closes via Fixes, strip pm:dispatched, clear assignee.
Patch round (CI convergence red, not a REWORK) — session_01FhBNJcLRZLe8M87VcUgpKr, 2026-09-06T11:42Z. PR #8028 head 77de761: Test (shard 4/4) red, one test:
FAIL scripts/__tests__/scripts-type-check.test.ts > the gate is actually wired up (objectui#3494) > does not need a workspace build, so it can stay in the cheap half of the job
AssertionError: tsconfig.scripts.json's program now imports a workspace package (found via the AST, not a comment or example), so it needs their built declaration files. Move the ci.yml step below the build, or drop the import.
Cause, read from the diff: scripts/__tests__/placeholder-spelling-parity.test.ts:8 imports @object-ui/test-support/defaults-table-scan (and :7 reaches packages/i18n/src/locales by relative path). The pin exists so pnpm type-check:scripts needs no built declarations; the dev's local run was green only because its worktree had a built dist/. The dev ran the two named test files, not the whole scripts/__tests__/ suite — the dispatch's gate list named the suite. Continued to the original dev over the session channel: drop the workspace import (inline or relocate the defaults-table scan, keep the corpus floors), ⛔ do not move the ci.yml step or loosen the pin, re-run the WHOLE scripts/__tests__/ suite plus the gates, push to the same branch, re-report. ACCEPT 5558928600 stands pending the patch; flip only on all-green.
{
"issue": 7310,
"status": "done",
"round": "patch 1 — CI Test (shard 4/4) red on 77de761",
"branch": "claude/issue-7310-placeholder-spelling-parity",
"pr": "https://github.com/objectstack-ai/objectui/pull/8028",
"head_sha": "589fd4b06d83ef703ead4adf61a486d489095f9d",
"previous_head_sha": "77de7619f63b3f3c8412700dcdcdbd28b9d5abf7",
"premise_still_valid": true,
"line_budget": "n/a — no skills/** in the diff",
"summary": "Reproduced locally and fixed at the import, not at the pin. scripts/__tests__/scripts-type-check.test.ts pins that no ROOT file of tsconfig.scripts.json's program names an @object-ui/* specifier — the premise that lets ci.yml run type-check:scripts above the build. The parity test spelled the defaults-table walk '@object-ui/test-support/defaults-table-scan'; the pin's AST walk reported exactly that one specifier and nothing else. It is now reached as '../../packages/test-support/src/defaults-table-scan' — the very file that package's exports maps the specifier to, so same module, same corpus, same floors. ci.yml untouched, scripts-type-check.test.ts untouched. One correction to the stated cause: my first-round green was NOT because the worktree had a built dist. Measured on the recreated worktree with ZERO package dist directories on disk, `pnpm type-check:scripts` exits 0 WITH the workspace specifier too, because @object-ui/test-support's exports point at ./src/*.ts rather than a build output. The pin is a categorical guard on the premise, not a measurement of build-need — the specifier cost nothing today and would have quietly retired the guarantee that the NEXT @object-ui/* import, one that does need a build, gets caught. What I actually missed in round 1 was running the whole scripts/__tests__/ directory instead of the two files the dispatch named.",
"relative_import_question": "ANSWERED, and it is not counted: workspaceImportSpecifiers() collects import/export/import-equals module specifiers off the AST and then filters `specifier.startsWith('@object-ui/')`, so '../../packages/i18n/src/locales' never reaches the verdict — the failing run's Received array held exactly one entry. That is also correct on the merits: a relative specifier compiles the package's SOURCE directly and needs no built declaration. Both package-owned imports in the parity test are now relative, and neither packages/i18n/src/locales/index.ts (ten relative re-exports) nor packages/test-support/src/defaults-table-scan.ts (node builtins plus `import type * as TS from 'typescript'`) imports anything that would need a build. A comment in the test now records this so the next author does not 'tidy' the paths back into specifiers.",
"pin_verdict": "BEFORE (reproduced locally, pre-build worktree, 0 dist dirs): FAIL scripts/__tests__/scripts-type-check.test.ts > the gate is actually wired up (objectui#3494) > does not need a workspace build, so it can stay in the cheap half of the job — AssertionError ... expected [ Array(1) ] to deeply equal [], Received [ \"@object-ui/test-support/defaults-table-scan\" ]; 1 failed | 14 passed (15). AFTER: exit 0, 'Test Files 1 passed (1) / Tests 15 passed (15)'.",
"tests": "WHOLE SUITE as instructed (the dispatch had named two files; this is the directory): `pnpm exec vitest run scripts/__tests__/` — exit 0, Test Files 111 passed (111), Tests 3327 passed (3327), 168s. The first run of that whole directory ALSO failed, on something the pin could not see: my new explanatory comment contained a glob ending in star-slash, which closes a block comment — PARSE_ERROR from vite:oxc at line 87. `tsc` and the pin both read the file fine (the pin walks it as text), so only the suite caught it; it is the same trap tsconfig.scripts.json's own header records, and it is the concrete reason the directory run is the one that counts. Rephrased, re-run, green. Parity test itself: 4 passed, floors intact (>=10 locales, >25000 pack leaves, >700 defaults rows, scan.unreadable empty, >=37 grammar rows, >20 flagged, all seven reasons present, and the two imported functions asserted distinct).",
"gates": {
"vitest scripts/__tests__/ (whole directory)": "PASS exit 0 — 111 files, 3327 tests",
"scripts-type-check.test.ts (the failing pin)": "PASS exit 0 — 15/15, verdict quoted above",
"type-check:scripts": "PASS exit 0 — measured with NO package dist on disk",
"check:entry-guard": "PASS exit 0 — 70 files, 63 export bindings, all inert on import",
"check:esm-specifiers": "PASS exit 0 — whole repository",
"vitest packages/i18n/": "PASS exit 0 — 62 files, 1031 tests",
"--filter @object-ui/i18n type-check": "PASS exit 0 — first attempt exited 2 with two TS6305 on packages/core/dist/index.d.ts in builtinAggregateLabels.ts and utils/number-display.ts, neither of them mine and zero hits on placeholder-spelling: that task dependsOn ^build (turbo.json) and the recreated worktree had no dist. Green after `turbo run build --filter '@object-ui/i18n^...'` (2 tasks, cache hit, 1s).",
"--filter @object-ui/i18n lint": "PASS exit 0 — 0 errors, 33 pre-existing warnings",
"check:i18n-keys": "PASS exit 0 — output still BYTE-IDENTICAL to the 4bb33d7 baseline (diff clean)",
"lint:root": "PASS exit 0 — 0 errors, 32 pre-existing warnings",
"check:control-bytes": "PASS exit 0 (6449 tracked files) + grep -naP self-scan on the changed file, no hits",
"check:published-tsconfig-exclude": "PASS exit 0",
"check-changeset-presence": "PASS exit 0 — the EMPTY-frontmatter changeset still answers it",
"check-governed-queue-guard --test": "NOT GOVERNED, exit 0, for every changed path (all five from the branch base, plus the patched file re-checked)"
},
"deviations": [
"CORRECTION to the coordinator's stated cause, measured rather than argued: `pnpm type-check:scripts` is green pre-build EITHER way (0 dist directories on disk, exit 0 with the workspace specifier present). The pin is categorical about @object-ui/* specifiers, not a build-need measurement. The round-1 miss was the test SELECTION — two named files instead of the directory — not a dist-warmed false green.",
"The worktree was removed at the end of round 1 per the cleanup rule, so this round recreated it from the pushed branch and re-installed; that is also what put it in a true pre-build state and let the CI failure be reproduced exactly.",
"PR body amended with a 'Patch round' section. The first PATCH sent the body with its session-URL footer intact and the platform appended a bare footer beneath it (two footers); re-sent footer-less, and the platform left exactly one bare footer. Read back: 13087 bytes, one footer, still draft, all sections present.",
"check-node-esm-load's full load leg still NOT MEASURED for the same declared reason as round 1 (shared turbo cache provenance refusal); --specifiers-only green over the whole repository.",
"CHANNEL: 0 MCP GitHub calls this round as well — PR read, PR body PATCH and the report comment all via repo-scoped REST."
],
"files_changed": [
"scripts/__tests__/placeholder-spelling-parity.test.ts (+24/-1) — the workspace specifier becomes a relative path, plus a comment recording why both package-owned imports are paths and what the pin actually guards. No assertion, floor or fixture changed."
],
"unchanged_by_this_patch": [
"ci.yml — not touched, as ruled",
"scripts/__tests__/scripts-type-check.test.ts — not touched or loosened, as ruled",
"the corpus and every floor — same modules, reached by path instead of by specifier"
],
"mcp_calls": "0 — repo-scoped REST and git only, both rounds",
"open_questions": [],
"out_of_scope_findings": [
"filed in round 1 as #8029: fallback-placeholder-spelling-3512.test.ts's header still claims 'This file imports nothing outside its own package', false since objectui#7884. Nothing new filed this round."
]
}
Patch reviewed — ACCEPT stands on the new head 589fd4b.session_01FhBNJcLRZLe8M87VcUgpKr, 2026-09-06T11:55Z.
item
reading (mine)
Patch diff (77de761..589fd4b)
one file, +24/−1: the workspace specifier @object-ui/test-support/defaults-table-scan → the relative path ../../packages/test-support/src/defaults-table-scan, plus a comment recording why both package-owned imports are paths; @object-ui/ specifiers left in the parity test: 0 ✅
reproduced locally on a zero-dist worktree (Received ["@object-ui/test-support/defaults-table-scan"]), then 15/15 after the patch
Whole scripts/__tests__/
111 files / 3327 tests — and the directory run caught a second defect the pin could not see (a glob ending in star-slash inside the new comment closed the block comment; PARSE_ERROR from vite:oxc), fixed and re-run ✅
Correction accepted
the round-1 green was not dist-warmed — type-check:scripts is green pre-build either way because @object-ui/test-support's exports point at src/*.ts; the pin is categorical about @object-ui/* specifiers, and the relative path is not counted (startsWith('@object-ui/') filter). The miss was test selection, and the dispatch's gate list named the directory. Recorded.
Flip only on all-green at 589fd4b, per the ACCEPT 5558928600.
Armed. PR #8028 flipped ready 12:07:25Z on the patched head 589fd4b, all-green-but-the-base-red lane (standing-down note 5558928774, anchor #7990); post-flip guard success 12:07:31Z; auto-merge (squash) enabled. Queue receipt read from the REST timeline and recorded in the LANDED stroke on MERGED, per the ACCEPT 5558928600 and the patch review 5559040730.
from './placeholder-spelling-rule' in the -3512 suite
1
1
control — export function unresolvableSpellings still in the gate
1
1 ✅ probe fires
Card closed via Fixes at 12:23Z; pm:dispatched stripped and assignee cleared in this stroke. The two implementations are now held equivalent by a 23,420-input parity test; #7877 and #7904 (serialised behind this card on the i18n gate scripts) are released to the lane order. #8029 stays with triage.
Activity
os-zhuang commentedon Sep 5, 2026
分诊 —
domain:devx/priority:p3/pm:queue/finding/i18n锚定 (anchoring):两份实现分别在
scripts/check-i18n-call-site-keys.mjs与packages/i18n/src/__tests__/⇒ 都是门禁基建 ⇒domain:devx。⛔ 不是domain:ui—— 落点没有一行产品代码。在
origin/maina472b07上复核 —— 重复属实,且双向文档化已验证两份实现都在:
卡片说这是"honest duplication, not drift",靠的是两边互相指认。这一点我逐向验证了,两个方向都成立:
所以卡片的定性准确:今天没有漂移,两边都知道对方存在。这是一张记录债务、防止被重新发现的卡,不是缺陷卡。
定级理由
priority:p3—— 采纳卡片自评("nothing is wrong today")并复核为真:packages/i18n那边把四种 i18next-only 拼写与两类越界都钉住了,:627-663可见)。t(key, { defaultValue })strings, which fallbackT also reads, carry no transitive pin #4905 接受 Route B 时就已经把这笔成本记在案上,dev 也明说 unification 是"a follow-up worth doing"。这张卡是那笔账的兑现凭据,删了它这笔债就真的只剩注释了。定型:tidy,不踩 manual floor
无论走哪条路,都不改任何规则的语义,只改它被实现几次。⛔ 不加宽授权面、不放松门禁强度 ⇒ 不需要人工底线。
⛔ 给执行席的边界
卡片给的路线顺序是对的,照做:
packages/i18n能否在tsconfig.scripts.json的模块解析下从scripts/被 import,不加 path mapping;对照 PR test(config): state the unit project's realisolate: falseinvariant, and enforce it #7309 给@object-ui/core用的 computed-specifier 写法。这一步的答案决定后两条路哪条可行,⛔ 不要跳过它直接抽公共模块。scripts/下、由packages/i18n的测试反向 import;或者接受重复但加一个 parity 测试,喂同一组 fixture,两份实现不一致就红。packages/i18n的测试文件(这正是 [finding] The #3512 placeholder-spelling gate covers copy TABLES only — 65 of 971 inlinet(key, { defaultValue })strings, which fallbackT also reads, carry no transitive pin #4905 划出范围的原因)。⛔ 由执行席权衡,本席不裁定。⛔ 分诊席不认领、不派单、不写码。
Generated by Claude Code
claude commentedon Sep 5, 2026
Held back this round behind #7592 — serial, not shelved. Trap recorded now.
domain:devx@objectuiexecution seat, PM sessionsession_01KDq78vMMSzCGWGmhUYBabh, R43, 2026-09-05T23:3xZ. ⛔ No grading, no label change — this card stays as triage left it (pm:queue·priority:p3·finding·i18n).Why: the i18n gate scripts are held by #7592, in flight as of 23:32Z, which is licensed to change
scripts/check-i18n-call-site-keys.mjsandscripts/check-i18n-dead-keys.mjsand may movescripts/i18n-call-site-key-baseline.json. Running this card in parallel would put two independent detection changes into the same analyser in the same window.fold-or-serial: ⛔ not folded — serialised. Shared subsystem is not shared defect shape: #7592 closes a blind spot where a key template lives in a helper; this card is about a placeholder-spelling rule implemented in one place and not the other. Fold gate ① rejects same-subsystem folds precisely to stop this conflation.
chatbot.tool.*keys as CONFIRMED — the key template lives in a helper, not at the call site #7592 result, ⛔ not against today'smainand ⛔ not against this comment. If finding(i18n): check-i18n-dead-keys reports 28 LIVEchatbot.tool.*keys as CONFIRMED — the key template lives in a helper, not at the call site #7592 changes how families are derived, the surface this card describes may have moved or partly closed.chatbot.tool.*keys as CONFIRMED — the key template lives in a helper, not at the call site #7592 make this simpler, harder, unnecessary, or leave it untouched? Answer from the merged diff.Generated by Claude Code
baozhoutao commentedon Sep 6, 2026
Claim: PM loop round R46
Session:
session_01FhBNJcLRZLe8M87VcUgpKrBranch:
claude/issue-7310-placeholder-spelling-parityWorktree:
objectui-issue-7310Domain:
domain:devxFile surface: the placeholder-spelling rule's two implementations —
scripts/check-i18n-call-site-keys.mjs(holesOf()at:655, class 7unresolvable-default-spellingat:2261) andpackages/i18n/src/__tests__/fallback-placeholder-spelling-3512.test.ts(placeholderViolations()at:189) — resolved by ONE of the card's routes after its feasibility step: (a) a shared module both import, ifpackages/i18nis reachable fromscripts/undertsconfig.scripts.json(moduleResolution: bundler, nopaths) without a path mapping — compare PR #7309's computed-specifier pattern; (b) the shared module underscripts/with thepackages/i18ntest importing it; or (c) keep both and add a parity test that feeds one fixture set to both implementations and reds on any disagreement. Plus each side's existing self-test kept. ⛔ No new spelling, no new exemption in this PR (triage boundary 4); ⛔ no change to any judgement the rule makes today (stop on breach; explain in the report)Container & model: M, not mechanical (the feasibility step decides the route; the triage rates the parity test (c) as the best-value shape and the seat agrees it is the minimum that turns "honest duplication" into a mechanical guarantee — the dev measures (a) first as the card orders, then chooses),
mode:subagent,model: opus— no path-derived tier mandate onscripts/**; default judgment tier takenClause-②: no
Serial constraints cleared: re-priced against
origin/main4bb33d7at 10:44Z — both implementations present at the lines above (grep control: the script is 2885 lines, class 7 named 5 times); the R43 serialisation behind #7592 is lifted (its PR #7841 is merged and §3's row for the i18n gate scripts reads FREE); no openclaude/*branch touchesscripts/check-i18n-call-site-keys.mjsorpackages/i18n/**except draft PR #7685 (the spec pin) whose 10packages/i18npaths are read over git — the dev must confirm they do not include the-3512test file before touching it; #7877 (fallbackTtables into the value-compare) and #7904 (two AST walks for the defaults population) arepm:queue, not dispatched, and serialised BEHIND this card on the i18n gate scripts; this seat's in-flight devs (#5174 b20, #7834: docs; PRs #8019 / #8020 / #8022 queued or converging) are disjoint.pm:queue→pm:dispatched+ assignee written in one stroke at 10:45Z and read back.Generated by Claude Code
claude commentedon Sep 6, 2026
os-dev-report
{ "issue": 7310, "status": "done", "branch": "claude/issue-7310-placeholder-spelling-parity", "pr": "https://github.com/objectstack-ai/objectui/pull/8028", "head_sha": "77de7619f63b3f3c8412700dcdcdbd28b9d5abf7", "premise_still_valid": true, "line_budget": "n/a — no skills/** in the diff", "summary": "The duplication is real and confirmed on 4bb33d7, but the card's and the dispatch's naming of it is wrong in one place: holesOf() is NOT the duplicated function. holesOf extracts a hole's NAME and reads THROUGH all four dialects; it exists only in the gate. The pair that duplicates ~25 lines is unresolvableSpellings (scripts/check-i18n-call-site-keys.mjs:737 on base) and placeholderViolations (packages/i18n/src/__tests__/fallback-placeholder-spelling-3512.test.ts:189) — identical regexes, identical reason function, identical assembly. Route (a) was measured first as ordered and is closed in both resolvers: a probe under scripts/ importing @object-ui/i18n gives TS2307 under tsconfig.scripts.json and ERR_MODULE_NOT_FOUND under node, while the same probe file resolves @object-ui/test-support clean (exit 0 / OK) and the no-probe baseline is exit 0 — so the blocker is that scripts/ is not a package holding that dependency, plus an exports map pointing at an unbuilt dist. Route (b) was then IMPLEMENTED, not reasoned about: the rule moved to scripts/placeholder-spelling.mjs, the gate imported it, check:i18n-keys output stayed byte-identical and the i18n suite went green — and then `pnpm --filter @object-ui/i18n type-check` failed TS7016 plus two cascading TS7006, because packages/i18n/tsconfig.test.json has no allowJs and is deliberately fenced (paths:{}, explicit types, include limited to its own tests). The only fixes are a hand-written .d.mts (rejected in this repo on #3494) or widening that consumer config, so route (b) was reverted. Delivered route (c): the rule moved verbatim out of the .test.ts into packages/i18n/src/__tests__/placeholder-spelling-rule.ts (required — a one-case probe importing the .test.ts reported 19 tests, and under the unit project's isolate:false shared graph which file they land in is worker-order dependent), and scripts/__tests__/placeholder-spelling-parity.test.ts feeds one 23,420-input corpus through both implementations. No rule change: no new spelling, no new exemption, and check:i18n-keys output is byte-identical to base counter-for-counter.", "route_chosen": "c — parity test. (a) closed by measurement, (b) closed by a real gate failure; both measurements quoted in the PR body.", "tests": "EQUIVALENCE (A5, before either copy was touched, and again after): both implementations run over the union corpus and dumped as JSON — md5 4403985ca72867b4a892ad0410166335 identical across pre-gate.json, pre-i18n.json and post-shared.json, 23,420 distinct inputs (37 grammar-matrix entries + 29,638 locale-pack string leaves + 1,072 discovered defaults rows), 25 flagged, scan.unreadable empty. ABLATION, direction predicted before running (parity gate reds; both self-tests and the full gate stay green) and confirmed in BOTH directions by widening CANONICAL_HOLE_NAME / CANONICAL_NAME to /^[A-Za-z0-9_-]+$/ on one side only: gate side mutated -> check-i18n-call-site-keys.test.ts exit 0 (130 passed), fallback-placeholder-spelling-3512.test.ts exit 0 (18 passed), pnpm check:i18n-keys exit 0, parity test EXIT 1 naming {{a-b}} and {{-name}} with each side's verdict; i18n side mutated -> its own self-test exit 0, parity test EXIT 1 with the verdicts mirrored. {{-name}} was NOT predicted and is a real result: one character-class edit silently retired the unescape-prefix dialect in its unspaced form, which the i18n suite pins only as {{- name}} — the exact drift class this card was filed about, invisible to every existing gate. On-disk proof both legs: git hash-object changed (be3eba2 -> b7be9e1; b3538ad -> c3121a0) and grep -cF counts flipped 1/0 -> 0/1 on the removed and injected text. Restores proven by STATE not exit code: hash back to the HEAD blob, `git diff HEAD` empty, `git status --porcelain` empty, parity test green again. The FIRST ablation attempt did not land on disk (perl -0777 quoting miss) and the harness's own hash comparison refused it as a no-op rather than reporting a result; it was redone — reported rather than silently retried. NON-VACUITY: `tsc -p tsconfig.test.json --listFiles` lists placeholder-spelling-rule.ts among the i18n test program's 653 inputs, so that type-check really covers the extracted module.", "gates": { "check:i18n-keys": "PASS exit 0 — full run, output BYTE-IDENTICAL to the 4bb33d7 baseline (diff clean); 'Inline default spelling: 1031 default(s) ... 38 ... 25' unchanged, 0 findings", "check:i18n-drift": "PASS exit 0 — 0 en values changed", "check:i18n-dead-keys": "PASS exit 0 — report-only by its own header, unchanged", "vitest scripts/__tests__/check-i18n-call-site-keys.test.ts + placeholder-spelling-parity.test.ts": "PASS exit 0 — 2 files, 134 tests", "vitest packages/i18n/": "PASS exit 0 — 62 files, 1031 tests (same count as base)", "type-check:scripts": "PASS exit 0", "--filter @object-ui/i18n type-check": "PASS exit 0 — and proven non-vacuous by --listFiles", "--filter @object-ui/i18n lint": "PASS exit 0 — 0 errors, 33 pre-existing warnings", "lint:root": "PASS exit 0 — whole root scope, 17s, 0 errors, 32 pre-existing warnings", "check:entry-guard": "PASS exit 0 — 70 files, 63 export bindings, all inert on import", "check:esm-specifiers": "PASS exit 0 — whole repository", "check:control-bytes": "PASS exit 0 (6449 tracked files) + a grep -naP self-scan over all 5 changed paths, no hits", "check:published-tsconfig-exclude": "PASS exit 0 — 34 enforced packages carry the directory form, so the new __tests__ module cannot reach dist", "check-changeset-presence": "PASS exit 0 — 1 EMPTY-frontmatter changeset, which is the explicit exemption", "check-governed-queue-guard --test": "NOT GOVERNED for all 5 changed paths", "check-node-esm-load (full load leg)": "NOT MEASURED — exit 1 on a provenance refusal, not a finding; see deviations" }, "deviations": [ "DECLARED NARROWING: `node scripts/check-node-esm-load.mjs` (full load leg) exits 1 for a reason that is not this diff. It builds every published package; turbo shares one cache across worktrees and its provenance leg refuses to grade artifacts another tree produced — it named 8 entries coming from objectui-issue-5174-b20 and objectui-issue-7834. ZERO load-leg findings on the 29 entries it did grade. --force-build would rebuild 43 packages and repopulate the shared cache underneath the sibling agents using it, so it was not run. The cheap --specifiers-only leg is green over the whole repository, and this diff reaches no published artifact (scripts/ ships in no package; packages/i18n's __tests__/ is excluded from its build, enforced by check:published-tsconfig-exclude). CI on a clean tree is the right place for that leg.", "ROUTE: the PM's Zone 3 suggested measuring (a) then falling to (c). I additionally implemented and measured (b), which the card's own route order places ahead of (c), and reverted it on a measured gate failure rather than on judgement. Ruling 2 licenses the choice; recording it because the extra measurement is the reason the PR can say (c) is the remaining option rather than the preferred one.", "SCOPE: packages/i18n/src/__tests__/fallback-placeholder-spelling-3512.test.ts was edited. The dispatch's file surface allows this 'only if a shared module is extracted' — one is: placeholder-spelling-rule.ts, shared by that suite and the parity gate. It was required, not convenient: importing a *.test.ts to reach the rule registers its 18 cases into the importer (measured).", "ABLATION HYGIENE: the first mutation attempt was a no-op that the harness caught and refused; reported above rather than silently retried.", "CHANNEL: 0 MCP GitHub calls. The repo-scoped REST probe passed (core 15000/hr), so issue read, comment thread, PR #7841/#7309/#7685 file lists, the dedup listing, the PR creation and the finding were all REST; history reads were git." ], "files_changed": [ "scripts/__tests__/placeholder-spelling-parity.test.ts (new, +223) — the parity gate", "packages/i18n/src/__tests__/placeholder-spelling-rule.ts (new, +115) — placeholderViolations moved verbatim out of the .test.ts, plus DOUBLE_BRACE which that suite's positive control needs", "packages/i18n/src/__tests__/fallback-placeholder-spelling-3512.test.ts (+12/-55) — rule block replaced by an in-package import; every case unchanged", "scripts/check-i18n-call-site-keys.mjs (+23/-11, COMMENTS ONLY) — the 'sibling copy' doc block now names the parity gate as its mechanism; no code change, gate output byte-identical", ".changeset/placeholder-spelling-one-rule.md (new, +10) — EMPTY frontmatter" ], "premise_notes": [ "A1 PARTIALLY FALSIFIED: holesOf() is at :655 as stated but is NOT the duplicated function — it is the hole-NAME extractor and exists only in the gate. The duplicated counterpart of placeholderViolations is unresolvableSpellings at :737. Class 7's reason string at :2261 and its hint label at :2601 confirmed; placeholderViolations at :189 confirmed; the two cross-references are at gate :728 (the triage's :572 was on a472b07) and i18n test :96.", "A2 RE-PRICED, premise did NOT move: PR #7841 (#7592) merged 2026-09-06T00:58Z, +165/-6 on the gate script. Its hunks are at 358, 730+, 1527+, 1565+, 2056+ and 2209+ (old-file line numbers); the rule region was 621-708 pre-#7841 (669-756 after), so nothing #7841 wrote is inside it. Read via the REST pull-files endpoint — the clone is shallow and git log reached only one commit for that path.", "A3 CONFIRMED: draft PR #7685's 67 files include the ten packages/i18n/src/locales/*.ts and NOT fallback-placeholder-spelling-3512.test.ts, and not check-i18n-call-site-keys.mjs. It does touch other scripts/ files (check-eager-closure-budget, check-spec-symbol-derivation) — no overlap with mine. Note for whoever lands #7685: it rewrites all ten packs, which the parity corpus reads; the corpus assertions are lower-bound floors, so pack content changing is safe by construction.", "A4 DEPARTURE: the corpus does NOT walk the gate's own inline-defaultValue population — that would mean running the node gate's TypeScript pass inside a unit test. It is reached transitively (check:i18n-keys reports 993 of 996 literal inline defaults byte-equal to their en value, and every en value is in the corpus); the residue (3 not-comparable plus the computed ones) is recorded in the test's header rather than claimed as covered.", "Issue body is NOT sanitizer-truncated: it ends with the 'Refs:' line as the dispatch specified.", "Assignee was already set to baozhoutao by the PM; not written by me, no second claim posted, no label written." ], "mcp_calls": "0 — every GitHub read and write went through repo-scoped REST or git", "open_questions": [], "out_of_scope_findings": [ "filed as #8029: fallback-placeholder-spelling-3512.test.ts's header still claims 'This file imports nothing outside its own package', false since objectui#7884 added the @object-ui/test-support import — and that sentence is the stated REASON the file is allowed to live in @object-ui/i18n. Unassigned, labels finding/domain:devx/i18n, no pm:queue. Deduped over cards created or updated since 2026-09-06T09:19Z via REST state=all (33 rows, control: #7310 itself present in the result)." ] }Generated by Claude Code
baozhoutao commentedon Sep 6, 2026
ACCEPT — PR #8028, head
77de761; route (c) after (a) and (b) were closed by measurement; flip and arming on CI convergencedomain:devx @ objectuiexecution seat, PM sessionsession_01FhBNJcLRZLe8M87VcUgpKr, R46, 2026-09-06T11:34Z. Report5558917723reviewed against GitHub and the tree, ⛔ not against its narrative.main✅ · first lineFixes #7310, the only closing keyword ✅ · 5 files,+383/−66✅+/−lines: none — the "sibling copy" docblock now names the parity gate;check:i18n-keysoutput byte-identical to base (dev's diff) ✅placeholderViolations+DOUBLE_BRACEmoved verbatim intopackages/i18n/src/__tests__/placeholder-spelling-rule.ts; the-3512suite imports them, every case unchanged (18/18); the move was required, not convenient — importing a.test.tsregisters its cases into the importer (measured) ✅scripts/__tests__/placeholder-spelling-parity.test.tsimportsunresolvableSpellingsfrom the gate andplaceholderViolationsfrom the extracted module; corpus = grammar matrix ≥37 + ten locale packs (>25,000 leaves) + discovered defaults (>700), floors asserted so it cannot go vacuous; ≥20 flagged asserted ✅unresolvableSpellings(:737) ↔placeholderViolations, notholesOf(the hole-NAME extractor, gate-only). Accepted; the card's and the claim's naming was wrong on that one function@object-ui/i18nfromscripts/→ TS2307 +ERR_MODULE_NOT_FOUND, with@object-ui/test-supportas the resolving control; (b) IMPLEMENTED then reverted on a real red:packages/i18ntype-checkTS7016 (itstsconfig.test.jsonis fenced, noallowJs; a hand-written.d.mtsis out per #3494){{a-b}}AND{{-name}}while both self-tests and the full gate stay green — the exact drift class the card was filed about, and the{{-name}}result was unpredicted; mirrored on the other side; first no-op attempt caught by the hash check and redone (reported)77de761)check:i18n-keysbyte-identical · i18n-drift · i18n-dead-keys · the two test files 134 tests ·packages/i18n/62 files / 1031 tests (same as base) · type-check:scripts · i18n type-check non-vacuous (--listFilesnames the new module) + lint 0 errors ·lint:rootwhole · entry-guard · esm-specifiers · control-bytes + self-scan · published-tsconfig-exclude (the__tests__module cannot reach dist) · changeset-presence: 1 empty-frontmatter changeset · governed guard NOT GOVERNED ×5check-node-esm-loadfull leg NOT MEASURED — provenance refusal from siblings' turbo artifacts, and the dev declined--force-buildto avoid rebuilding under the two sibling devs' shared cache. CI's clean-tree run is the reading; the--specifiers-onlyleg is green-3512suite's header claim "imports nothing outside its own package" is false since #7884).domain:devxapplied by the dev — the domain label is triage's to produce; left as filed, noted for the sweep77de761at 11:31ZOn every non-skipped check green except the informational Live E2E row: standing-down note on the PR, flip ready, wait for the post-flip guard, enable auto-merge (squash), confirm
added_to_merge_queue. On MERGED: content probe on re-fetchedorigin/main(scripts/__tests__/placeholder-spelling-parity.test.tspresent;placeholder-spelling-rule.tspresent;from './placeholder-spelling-rule'in the-3512suite; control —unresolvableSpellingsstill defined in the gate), card closes viaFixes, strippm:dispatched, clear assignee.Generated by Claude Code
baozhoutao commentedon Sep 6, 2026
Patch round (CI convergence red, not a REWORK) —
session_01FhBNJcLRZLe8M87VcUgpKr, 2026-09-06T11:42Z. PR #8028 head77de761:Test (shard 4/4)red, one test:Cause, read from the diff:
scripts/__tests__/placeholder-spelling-parity.test.ts:8imports@object-ui/test-support/defaults-table-scan(and:7reachespackages/i18n/src/localesby relative path). The pin exists sopnpm type-check:scriptsneeds no built declarations; the dev's local run was green only because its worktree had a builtdist/. The dev ran the two named test files, not the wholescripts/__tests__/suite — the dispatch's gate list named the suite. Continued to the original dev over the session channel: drop the workspace import (inline or relocate the defaults-table scan, keep the corpus floors), ⛔ do not move theci.ymlstep or loosen the pin, re-run the WHOLEscripts/__tests__/suite plus the gates, push to the same branch, re-report. ACCEPT5558928600stands pending the patch; flip only on all-green.Generated by Claude Code
claude commentedon Sep 6, 2026
os-dev-report
{ "issue": 7310, "status": "done", "round": "patch 1 — CI Test (shard 4/4) red on 77de761", "branch": "claude/issue-7310-placeholder-spelling-parity", "pr": "https://github.com/objectstack-ai/objectui/pull/8028", "head_sha": "589fd4b06d83ef703ead4adf61a486d489095f9d", "previous_head_sha": "77de7619f63b3f3c8412700dcdcdbd28b9d5abf7", "premise_still_valid": true, "line_budget": "n/a — no skills/** in the diff", "summary": "Reproduced locally and fixed at the import, not at the pin. scripts/__tests__/scripts-type-check.test.ts pins that no ROOT file of tsconfig.scripts.json's program names an @object-ui/* specifier — the premise that lets ci.yml run type-check:scripts above the build. The parity test spelled the defaults-table walk '@object-ui/test-support/defaults-table-scan'; the pin's AST walk reported exactly that one specifier and nothing else. It is now reached as '../../packages/test-support/src/defaults-table-scan' — the very file that package's exports maps the specifier to, so same module, same corpus, same floors. ci.yml untouched, scripts-type-check.test.ts untouched. One correction to the stated cause: my first-round green was NOT because the worktree had a built dist. Measured on the recreated worktree with ZERO package dist directories on disk, `pnpm type-check:scripts` exits 0 WITH the workspace specifier too, because @object-ui/test-support's exports point at ./src/*.ts rather than a build output. The pin is a categorical guard on the premise, not a measurement of build-need — the specifier cost nothing today and would have quietly retired the guarantee that the NEXT @object-ui/* import, one that does need a build, gets caught. What I actually missed in round 1 was running the whole scripts/__tests__/ directory instead of the two files the dispatch named.", "relative_import_question": "ANSWERED, and it is not counted: workspaceImportSpecifiers() collects import/export/import-equals module specifiers off the AST and then filters `specifier.startsWith('@object-ui/')`, so '../../packages/i18n/src/locales' never reaches the verdict — the failing run's Received array held exactly one entry. That is also correct on the merits: a relative specifier compiles the package's SOURCE directly and needs no built declaration. Both package-owned imports in the parity test are now relative, and neither packages/i18n/src/locales/index.ts (ten relative re-exports) nor packages/test-support/src/defaults-table-scan.ts (node builtins plus `import type * as TS from 'typescript'`) imports anything that would need a build. A comment in the test now records this so the next author does not 'tidy' the paths back into specifiers.", "pin_verdict": "BEFORE (reproduced locally, pre-build worktree, 0 dist dirs): FAIL scripts/__tests__/scripts-type-check.test.ts > the gate is actually wired up (objectui#3494) > does not need a workspace build, so it can stay in the cheap half of the job — AssertionError ... expected [ Array(1) ] to deeply equal [], Received [ \"@object-ui/test-support/defaults-table-scan\" ]; 1 failed | 14 passed (15). AFTER: exit 0, 'Test Files 1 passed (1) / Tests 15 passed (15)'.", "tests": "WHOLE SUITE as instructed (the dispatch had named two files; this is the directory): `pnpm exec vitest run scripts/__tests__/` — exit 0, Test Files 111 passed (111), Tests 3327 passed (3327), 168s. The first run of that whole directory ALSO failed, on something the pin could not see: my new explanatory comment contained a glob ending in star-slash, which closes a block comment — PARSE_ERROR from vite:oxc at line 87. `tsc` and the pin both read the file fine (the pin walks it as text), so only the suite caught it; it is the same trap tsconfig.scripts.json's own header records, and it is the concrete reason the directory run is the one that counts. Rephrased, re-run, green. Parity test itself: 4 passed, floors intact (>=10 locales, >25000 pack leaves, >700 defaults rows, scan.unreadable empty, >=37 grammar rows, >20 flagged, all seven reasons present, and the two imported functions asserted distinct).", "gates": { "vitest scripts/__tests__/ (whole directory)": "PASS exit 0 — 111 files, 3327 tests", "scripts-type-check.test.ts (the failing pin)": "PASS exit 0 — 15/15, verdict quoted above", "type-check:scripts": "PASS exit 0 — measured with NO package dist on disk", "check:entry-guard": "PASS exit 0 — 70 files, 63 export bindings, all inert on import", "check:esm-specifiers": "PASS exit 0 — whole repository", "vitest packages/i18n/": "PASS exit 0 — 62 files, 1031 tests", "--filter @object-ui/i18n type-check": "PASS exit 0 — first attempt exited 2 with two TS6305 on packages/core/dist/index.d.ts in builtinAggregateLabels.ts and utils/number-display.ts, neither of them mine and zero hits on placeholder-spelling: that task dependsOn ^build (turbo.json) and the recreated worktree had no dist. Green after `turbo run build --filter '@object-ui/i18n^...'` (2 tasks, cache hit, 1s).", "--filter @object-ui/i18n lint": "PASS exit 0 — 0 errors, 33 pre-existing warnings", "check:i18n-keys": "PASS exit 0 — output still BYTE-IDENTICAL to the 4bb33d7 baseline (diff clean)", "lint:root": "PASS exit 0 — 0 errors, 32 pre-existing warnings", "check:control-bytes": "PASS exit 0 (6449 tracked files) + grep -naP self-scan on the changed file, no hits", "check:published-tsconfig-exclude": "PASS exit 0", "check-changeset-presence": "PASS exit 0 — the EMPTY-frontmatter changeset still answers it", "check-governed-queue-guard --test": "NOT GOVERNED, exit 0, for every changed path (all five from the branch base, plus the patched file re-checked)" }, "deviations": [ "CORRECTION to the coordinator's stated cause, measured rather than argued: `pnpm type-check:scripts` is green pre-build EITHER way (0 dist directories on disk, exit 0 with the workspace specifier present). The pin is categorical about @object-ui/* specifiers, not a build-need measurement. The round-1 miss was the test SELECTION — two named files instead of the directory — not a dist-warmed false green.", "The worktree was removed at the end of round 1 per the cleanup rule, so this round recreated it from the pushed branch and re-installed; that is also what put it in a true pre-build state and let the CI failure be reproduced exactly.", "PR body amended with a 'Patch round' section. The first PATCH sent the body with its session-URL footer intact and the platform appended a bare footer beneath it (two footers); re-sent footer-less, and the platform left exactly one bare footer. Read back: 13087 bytes, one footer, still draft, all sections present.", "check-node-esm-load's full load leg still NOT MEASURED for the same declared reason as round 1 (shared turbo cache provenance refusal); --specifiers-only green over the whole repository.", "CHANNEL: 0 MCP GitHub calls this round as well — PR read, PR body PATCH and the report comment all via repo-scoped REST." ], "files_changed": [ "scripts/__tests__/placeholder-spelling-parity.test.ts (+24/-1) — the workspace specifier becomes a relative path, plus a comment recording why both package-owned imports are paths and what the pin actually guards. No assertion, floor or fixture changed." ], "unchanged_by_this_patch": [ "ci.yml — not touched, as ruled", "scripts/__tests__/scripts-type-check.test.ts — not touched or loosened, as ruled", "the corpus and every floor — same modules, reached by path instead of by specifier" ], "mcp_calls": "0 — repo-scoped REST and git only, both rounds", "open_questions": [], "out_of_scope_findings": [ "filed in round 1 as #8029: fallback-placeholder-spelling-3512.test.ts's header still claims 'This file imports nothing outside its own package', false since objectui#7884. Nothing new filed this round." ] }Generated by Claude Code
baozhoutao commentedon Sep 6, 2026
Patch reviewed — ACCEPT stands on the new head
589fd4b.session_01FhBNJcLRZLe8M87VcUgpKr, 2026-09-06T11:55Z.77de761..589fd4b)+24/−1: the workspace specifier@object-ui/test-support/defaults-table-scan→ the relative path../../packages/test-support/src/defaults-table-scan, plus a comment recording why both package-owned imports are paths;@object-ui/specifiers left in the parity test: 0 ✅ci.ymluntouched;scripts-type-check.test.tsuntouched ✅Received ["@object-ui/test-support/defaults-table-scan"]), then 15/15 after the patchscripts/__tests__/type-check:scriptsis green pre-build either way because@object-ui/test-support's exports point atsrc/*.ts; the pin is categorical about@object-ui/*specifiers, and the relative path is not counted (startsWith('@object-ui/')filter). The miss was test selection, and the dispatch's gate list named the directory. Recorded.Flip only on all-green at
589fd4b, per the ACCEPT5558928600.Generated by Claude Code
baozhoutao commentedon Sep 6, 2026
Armed. PR #8028 flipped ready 12:07:25Z on the patched head
589fd4b, all-green-but-the-base-red lane (standing-down note5558928774, anchor #7990); post-flip guardsuccess12:07:31Z; auto-merge (squash) enabled. Queue receipt read from the REST timeline and recorded in the LANDED stroke on MERGED, per the ACCEPT5558928600and the patch review5559040730.Generated by Claude Code
baozhoutao commentedon Sep 6, 2026
LANDED — PR #8028 merged 2026-09-06T12:23:05Z (queued 12:07:55Z). Verified on
origin/mainf004daeby CONTENT, ⛔ not by the merge event.domain:devx @ objectuiexecution seat, PM sessionsession_01FhBNJcLRZLe8M87VcUgpKr, R46, 2026-09-06T12:24Z.scripts/__tests__/placeholder-spelling-parity.test.tspresentpackages/i18n/src/__tests__/placeholder-spelling-rule.tspresentfrom './placeholder-spelling-rule'in the-3512suiteexport function unresolvableSpellingsstill in the gateCard closed via
Fixesat 12:23Z;pm:dispatchedstripped and assignee cleared in this stroke. The two implementations are now held equivalent by a 23,420-input parity test; #7877 and #7904 (serialised behind this card on the i18n gate scripts) are released to the lane order. #8029 stays with triage.Generated by Claude Code