Repository navigation
The label "INVERTED PIN" in page-nav-misc-spec-parity.test.ts cannot detect a re-widened I18nLabel — the spec already re-widened and the pin stayed green #5612
Description
Activity
- addeddomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatobjectui ui stream: fix lands on the published library or apps — objectui execution seat
on Aug 21, 2026 huangyiirene commented
on Aug 21, 2026 CollaboratorMore actionsTriage: lands in
packages/types/src/__tests__/page-nav-misc-spec-parity.test.ts→pm:queue+domain:ui(published-library test face, objectui execution seat); rationale: the guarded event (spec re-wideningI18nLabel) has already occurred, measured against the installed@objectstack/spec@17.0.0, and the pin stayed green — a guard reporting protection it does not provide, plus a test name asserting a false premise. Fix shape as filed: make the assertion sensitive (assert the map form is assignable so it fails the day that form is withdrawn), retitle, and rewrite the comment to the spec's own two-forms doc (#5728 maintainer ruling 2026-08-06). Type Task. Serial/fold note: sibling residue of the same override-removal as #5613, same package — the executing seat answers fold-or-serial (five-gate check; likely foldable: same area, both graded, independently checkable, one changeset). Size/model suggestion: S,mode:subagent, model: sonnet.
Generated by Claude Code
Claim —
domain:uiexecution seat (#5560), round 9.- Session:
session_012u2pRjcqAYtoEjgr3wwhnK - Branch:
claude/issue-5612-i18nlabel-inverted-pin - Label:
pm:queue→pm:dispatched
Fold-or-serial vs #5613 — answered FOLD. Triage flagged it as likely foldable and left the call to this seat; taking it. Both are residue of the same removed local
labeloverride, both sit inpackages/types, each is independently checkable, and one changeset covers both. Precedent: #4559 + #4966 landed as one PR in round 7 on the same reasoning. #5613 is claimed on the same branch and will be closed by the same PR.Fix shape as filed: make the assertion sensitive to the thing it claims to watch (assert the map form is assignable, so it fails the day that form is withdrawn), retitle the
it(...), and rewrite the comment against the spec's own two-forms doc block rather than the false premise.The load-bearing point for whoever reviews this: the current assertion assigns the string literal
'Priority'toSpecI18nLabel, which type-checks under both the narrow and the wide shape — so it could only ever fire if the plain-string form were removed, which is the opposite of the event it was written to catch. The widening has already happened (measured at@objectstack/spec@17.0.0) and the pin stayed green. ⛔ A rewritten pin that is still green under both shapes has not fixed anything: demonstrate the new assertion fails against the narrow shape.
Generated by Claude Code
- Session:
- added a commit that references this issue
on Aug 21, 2026 { "issue": 5612, "also_closes": 5613, "status": "done", "branch": "claude/issue-5612-i18nlabel-inverted-pin", "pr": "https://github.com/objectstack-ai/objectui/pull/5634", "premise_still_valid": true, "summary": "Both cards verified against origin/main before writing code, and both premises held. #5612: the pin's load-bearing assertion assigned the string literal 'Priority' to SpecI18nLabel, which type-checks under the narrow AND the wide shape, so it could only fire if the plain-string form were removed - the opposite of the widening it claimed to watch; @objectstack/spec@17.0.0 dist/ui/index.d.ts:614 declares the union and doc block 571-613 states two authorized forms, both line numbers confirmed accurate. Retargeted at the premise that actually holds the decision up: BOTH authorized forms stay assignable, on SpecI18nLabel and on the inherited ActionParam['label'] / options[].label, so it fails when either is withdrawn and deliberately does not fail on a further widening. Retitled; comment rewritten against the spec's own doc block. #5613: the unused I18nLabel type import deleted from ui-action.ts:27. One bounded in-place fix declared in the PR body: the same JSDoc block's 'NOT guarded:' paragraph asserted this pin was not coverage and pointed at #5612, so landing the guard without correcting it would have published a comment denying the thing the PR adds - same defect class, same file this card claims, same gate family. Changeset is patch, not empty frontmatter: measured, the published dist/ui-action.d.ts really does move (JSDoc prose only), matching the scoring of #5617 on this exact block.", "tests": "All at the final commit eca724225, tree clean; heavy runs serialized through scripts/pm/os-verify-lock.sh (VERDICT lines read, never a bare $?). (1) pnpm --filter @object-ui/types type-check -> 'VERDICT command-exit 0', script name echoed as '> @object-ui/types@17.6.0 type-check' so this was not a zero-match silent pass. (2) npx vitest run --maxWorkers=2 packages/types/ from the repo root (never package cwd, per objectui#3378) -> 'Test Files 40 passed (40) / Tests 460 passed (460)'. (3) pnpm lint repo-wide (turbo run lint) -> 'Tasks: 47 successful, 47 total', 0 errors; NO narrowing declared, the full farm ran locally. (4) check-control-bytes OK (4679 tracked text files), spec symbol derivation OK (1290 files vs 4912 spec export names), check-package-self-import OK, check-lint-coverage 46/46 packages 0 errors, check-changeset-presence '2 source file(s) ... declares 1 changeset(s)', changeset:check fixed-group + no-major both green. REVERSE VERIFICATION, direction predicted before running and observed as predicted (RED both legs), each leg mutation-confirmed ON DISK by grepping the injected text AND the deleted anchor (never a bare git diff --stat, never an editor exit code), each with a trap restore, each restored to a byte-identical tree (git diff --quiet HEAD -> 0). Leg A, narrow SpecI18nLabel inside the real test file: tsc exit 2, TS2344 + TS2322 \"Type '{ en: string; 'zh-CN': string; }' is not assignable to type 'string'\". Leg B, reintroduce the exact defect guarded (a local override narrower than the contract, label?: string): tsc exit 2, FOUR errors covering both ActionParam legs at 400/401/405/408. Standalone 2x2 truth table proving the OLD assertion could not discriminate: old assertion exit 0 under BOTH wide and narrow; new assertions exit 0 under wide, exit 2 under narrow. The wide column carries its own negative control so it is not vacuously green - assigning a number yields \"not assignable to type 'string | Record(string, string)'\", i.e. the real union resolved rather than falling back to any. No ablation of a package dist/ was involved (this is a compile-time pin in a tsconfig.test.json program, not a dist-resolved suite), so no rebuild leg applies; the dist/ measurement below is a separate build, not an ablation. DIST MEASUREMENT for the changeset call: both legs built from a CLEARED dist/ and CLEARED composite tsconfig.tsbuildinfo (so neither leg skipped emit), compared by sha256 per file rather than byte counts - 108 emitted files on both sides, exactly one differing: dist/ui-action.d.ts 31,026 -> 31,117 bytes, content diff JSDoc prose only with no declaration changed; dist/ui-action.js byte-identical (1b24e8f5..., 3,480 bytes). The deleted type import contributes zero emitted delta. NOT run locally, left to CI by the standing scope rule: pnpm check (the CLI self-check, which needs the CLI build closure). Known-broken gauges noted, not touched: check-eager-closure-budget exits 2, check-doc-snippet-types exits 1.", "fold": { "both_cards_closed": true, "one_changeset": true }, "evidence": { "new_pin_fails_under_narrow_shape": true, "sibling_pins_checked": true, "unused_import_reverified": true }, "new_issue_filed": null, "open_questions": [], "out_of_scope_findings": [ "NOT filed as a new card - already open as #4806, found by searching before filing. #5613's out-of-scope observation (tsconfig.base.json sets noUnusedLocals true, but packages/types/tsconfig.json extends the ROOT config which sets it false) is stated in full there, along with the other half (no-unused-vars is warn with no --max-warnings). Re-measured today and posted as a comment on #4806 rather than duplicated: the consumer set of tsconfig.base.json is examples/byo-backend-console/tsconfig.json plus tsconfig.node.json and tsconfig.react.json, and NOTHING under packages/, apps/ or examples/ extends either of those two - so the branch of the config tree that enables noUnusedLocals is unreachable, not merely overridden." ] }Notes the JSON cannot carry cleanly
-
premise_still_valid: true, with one stale number worth recording. finding(types):ui-action.tsimportsI18nLabelfrom@objectstack/spec/uiand never uses it — residue of the removed locallabeloverride #5613's body saidI18nLabelappears 4 times inui-action.ts(import + 3 in JSDoc). On currentorigin/mainthere are 6 word-boundary occurrences — import at :27 plus five in JSDoc prose at :317, :323, :335, :336, :350 — because docs(types): ui-action.ts no longer claims spec 17 narrowed I18nLabel to a plain string #5617 landed the prose rewrite after the card was filed. The import line number:27is still exact, and the conclusion is unchanged: no type position uses it, and it is not re-exported. -
Sibling-pin probe, stated as a method rather than an opinion. The mechanical test for this defect class is that a pin which cannot fail is one whose asserted value can be flipped without turning the compile red. All 9 type-level pins in the file were flipped one at a time (each flip confirmed on disk, each restored): every one went RED. The file's other 15 compile-time guards are
@ts-expect-errordirectives, which cannot be blind by construction — a directive suppressing nothing is itselfTS2578, measured by injecting one over an error-free line. With the baseline green, all 15 are live. No sibling pin carries this defect. -
The changeset call went against the dispatch's guess, on measurement. The dispatch suggested empty frontmatter was likely correct but told me to measure rather than assume — and the
.d.tsdelta is non-zero, sopatchis right. The precedent is the immediately preceding PR on this same JSDoc block, docs(types): ui-action.ts no longer claims spec 17 narrowed I18nLabel to a plain string #5617, which scored'@object-ui/types': patchfor exactly this reason: the release-visible surface of a type-only package is the declaration file. -
CI convergence is left to the PM per the standing contract. Report delivered at draft-PR time; no polling.
Generated by Claude Code
-
- added a commit that references this issue
on Aug 23, 2026
Filed unassigned by the
repo:objectuiexecution seat while working #4611 (sessionsession_012u2pRjcqAYtoEjgr3wwhnK). Not claimed, not graded — out of that card's scope (packages/types/src/ui-action.tsonly), so it is recorded here rather than fixed there.What
packages/types/src/__tests__/page-nav-misc-spec-parity.test.ts:350-366declares itself an inverted pin on the spec'sI18nLabel:The load-bearing assertion is an assignment of the string literal
'Priority'toSpecI18nLabel. That assignment is type-correct under both the narrow and the wide shape — a plain string is one of the forms the wide union admits — so it can never stop compiling on a widening. It can only fire if the spec removes the plain-string form.Measured — the widening already happened and the pin did not fire
Installed
@objectstack/spec@17.0.0(GA, the current pin),node_modules/.pnpm/@objectstack+spec@17.0.0_.../dist/ui/index.d.ts:614:(Spelling flattened to survive GitHub's body sanitizer; read line 614 for real syntax.)
So
I18nLabelis re-widened today. Yetpnpm --filter @object-ui/types type-checkis green onorigin/main— measured this session,tsc --noEmit && tsc -p tsconfig.examples.json && tsc -p tsconfig.test.json, exit 0. The event the pin promised to catch has occurred, the pin observed nothing, and the stale justification it guards ("spec 17 madeI18nLabela plain string") stayed in the tree until #4611 corrected the copy of it inui-action.ts.Why it matters
This is not a wrong comment — it is a guard that reports protection it does not provide. The decision it guards (inheriting
label/options[].labelby reference instead of locally overriding them) is still correct on the merits, but nothing is currently watching the premise, and the test name asserts the false premise as fact to every reader.Fix shape
Rewrite the case so its assertion is sensitive to the thing it claims to watch, e.g. assert the map form is assignable (
const inline: SpecI18nLabel = { en: 'Priority' }— fails the day the map form is withdrawn) and/or assert the inheritedActionParam['label']still admits both forms. Retitle theit(...)and rewrite its comment: the spec's own doc block (dist/ui/index.d.ts:571-613) states two authorized forms and "Both are real; neither is deprecated by this schema" (#5728, maintainer ruling 2026-08-06).Prior art for this exact failure mode: #3177 (three inverted pins that flipped in
17.0.0-rc.1and needed a burn-down).Refs #4611, #4163, #5081.