Skip to content

main is RED: console.ai.pendingDrafts exists only in en and zh — the eight other locale packs have no such block at all, so all-locales-key-parity fails on main and on every PR that touches source #5705

Description

@os-sales

Filed by the domain:ui execution seat (seat post #5560). Found while landing #5699; that PR is held on this, not on its own diff.

The breakage

packages/i18n/src/__tests__/all-locales-key-parity.test.ts fails on main. Measured on origin/main (83ec61881) by parsing each locale pack's pendingDrafts: { blocks and comparing immediate child keys against en — not by grepping, for a reason given below:

en  console.ai.pendingDrafts keys: ['count','failed','publish','published','publishedWithFindings']

  en   block present, missing: NONE
  zh   block present, missing: NONE
  ar   NO console.ai.pendingDrafts block at all
  ru   NO console.ai.pendingDrafts block at all
  pt   NO console.ai.pendingDrafts block at all
  es   NO console.ai.pendingDrafts block at all
  fr   NO console.ai.pendingDrafts block at all
  de   NO console.ai.pendingDrafts block at all
  ko   NO console.ai.pendingDrafts block at all
  ja   NO console.ai.pendingDrafts block at all

Eight packs × five keys. The parity test carries no allowlist — expect(missing).toEqual([]) — and has its own non-vacuity guard (EN.size > 2000, OTHER_LOCALES length 9), so it cannot be passing vacuously.

How it got here — and why the follow-up did not fix it

So the follow-up that exists specifically to close this gap closed one ninth of it, and main has been red across both.

⚠️ A measurement trap that nearly made me report this wrong

My first pass counted occurrences of the string pendingDrafts per file and got 2 for en/zh/pt/es/fr/de and 1 for ar/ru/ko/ja — which reads as "four packs were fixed, four were not". That is false. There are two unrelated pendingDrafts blocks in these files: the new console.ai.pendingDrafts (en.ts:1690) and a pre-existing, different one (en.ts:2611, keys message / cta). The extra occurrence in pt/es/fr/de is not a second block — parsing finds exactly one pendingDrafts: { in each of the eight. Anyone re-checking this by grep will get the wrong answer; parse the blocks.

Blast radius — narrower than it looks, and that is the trap

This does not turn every PR red, which is why it has survived several merges unnoticed:

So: docs-only PRs sail through; the first PR that touches source eats it. On main the same failure shows up as Test (coverage shard 2/4).

⛔ check_suite.completed did not catch this, and could not have

Every check_suite.completed webhook on #5699's head 8faa2fea0 reported conclusion: success while Test (shard 2/4) and Test (shard 3/4) were still in_progress — the suites that completed were the other ones. Reading those events as a green light would have landed this PR onto a red base ten minutes before the failure arrived. Gates must be read by name, per #4986.

What a fix has to do

Add console.ai.pendingDrafts — count, publish, published, failed, publishedWithFindings — to the eight remaining packs, matching en.ts:1690-1696. count and publishedWithFindings carry interpolations ({{count}}, {{detail}}) that must survive translation.

Worth deciding at the same time, because this is the second time in a day that a feature landed en-only and a follow-up under-delivered: whether the parity test should run in a cheap, unskippable job rather than only inside the shard that a source-free diff skips. As it stands the guard is real but is bypassed by exactly the PRs that are cheapest to merge, so the debt accumulates between source-touching PRs instead of being caught at the commit that creates it.

Adjacent and separately known: main is also red on Build Docs (#5668) and on Test (coverage) (#5402, failing 100% since 2026-08-16). This card is only the parity failure.

Activity

  1. self-assigned this
    on Aug 22, 2026
  2. os-sales commented on Aug 22, 2026

    @os-sales
    CollaboratorAuthor

    Claim — domain:ui execution seat.

    Dispatching immediately. This is not a new round's card — it is the blocker that stops round 16's own remaining PRs (#5699, and the two still in flight for #5235 / #5279, all of which touch source and therefore get a real shard run) from landing on a green base. Clearing it is part of finishing the work already in flight.

    Scope is the eight packs, five keys, matching en.ts:1690-1696. ⚠️ count and publishedWithFindings carry interpolations ({{count}}, {{detail}}) that must survive translation.


    Generated by Claude Code

  3. os-sales commented on Aug 22, 2026

    @os-sales
    CollaboratorAuthor

    CI corroborates the parse — independently, and against the grep.

    #5706 (a different card, packages/auth only) has now hit this on Test (shard 2/4). Its annotations, read from the check-run API:

    annotations : 9
    paths       : ['.github', 'packages/i18n/src/__tests__/all-locales-key-parity.test.ts']
    locales     : ['ar','de','es','fr','ja','ko','pt','ru']
    keys        : ['console.ai.pendingDrafts.count', '…failed', '…publish',
                   '…published', '…publishedWithFindings']
    NON-parity annotations: NONE
    

    Two things this settles:

    1. All eight locales are missing all five keys — not four packs partially fixed. That matches the block-level parse in the card and contradicts the occurrence count that reported 2 for pt/es/fr/de. CI is an independent instrument and it agrees with the parse, so the grep reading is confirmed wrong rather than merely suspect. Anyone re-checking this: parse the blocks.

    2. The failure carries nothing else. No annotation outside the parity test, so a PR inheriting this is inheriting exactly one defect, not a cluster. That is what makes it safe to hold the affected PRs unchanged rather than rebase or patch them — there is nothing else in the base for them to absorb.

    Three PRs are now held on this: #5699, #5706, #5707. None of them needs to change; all three merge when the base is green.


    Generated by Claude Code

  4. os-sales commented on Aug 22, 2026

    @os-sales
    CollaboratorAuthor
    {
      "issue": 5705,
      "status": "done",
      "branch": "claude/issue-5705-pending-drafts-locale-parity",
      "pr": "https://github.com/objectstack-ai/objectui/pull/5708",
      "premise_still_valid": true,
      "summary": "Added the five console.ai.pendingDrafts keys (count, publish, published, failed, publishedWithFindings), translated, to the eight packs that had no such block: ar, ru, pt, es, fr, de, ko, ja. Placed at en's position (first key inside console.ai, immediately before usage), anchored on the parsed `    ai: {` line rather than a grep for `pendingDrafts` — the card's measurement trap, since two unrelated blocks carry that name. Each pack keeps its own conventions rather than en's: all eight quote with double quotes, ru puts the number last (`...: {{count}}`) as it already does for the sibling home.pendingDrafts counts, and ja uses the full-width colon before {{detail}} per that pack's documented rule (full-width when the interpolation carries a message, half-width for a single token); both deviations carry a short in-pack note in the pack's own language. Terminology was lifted from each pack's existing publish-bar vocabulary (home.pendingDrafts, console.ai.seedWarn, console.ai.publishFailed). Diff is 60 insertions, 0 deletions, 8 locale files plus a changeset — no reordering, no other parity gaps touched.",
      "tests": "All results at final commit 739dd78b6 (verification union re-run after the last commit, on a clean tree). RED LEG captured before the fix on origin/main 83ec61881: `pnpm exec vitest run packages/i18n/src/__tests__/all-locales-key-parity.test.ts` -> 'Test Files 1 failed (1) / Tests 8 failed | 24 passed (32)', each failure 'AssertionError: %s is missing 5 key(s)' listing exactly console.ai.pendingDrafts.{count,failed,publish,published,publishedWithFindings} for ja/ko/de/fr/es/pt/ru/ar. GREEN LEG same file with --reporter=verbose: 'Test Files 1 passed (1) / Tests 32 passed (32)', naming all nine '%s defines every en key' AND all nine of the sibling '%s defines no key that en lacks' (the other-direction assertion), plus 'placeholders match en in every pack' and both non-vacuity guards. Exactly the 8 cases flipped red->green. PLACEHOLDERS verified mechanically against the EVALUATED packs via tsx (not a text grep): all ten packs report keys=5, {{count}} present in count, {{detail}} present in publishedWithFindings, per-key placeholder shape equal to en's, and the unrelated home.pendingDrafts block still holding message/cta and gaining nothing -> 'VERIFY OK — 0 failures'. SCOPED SUITE (verify-lock scope = the edited package plus the one provable consumer, packages/app-shell/src/console/ai/PendingDraftsBar.tsx, established by grepping every call site of console.ai.pendingDrafts — it is the only one; NOT app-shell's full 493-file suite): `pnpm exec vitest run --maxWorkers=2 packages/i18n/ packages/app-shell/src/console/ai/__tests__/PendingDraftsBar.test.tsx` -> 'Test Files 52 passed (52) / Tests 883 passed (883)'. GATES, each read from its own printed verdict line, exit code captured before any pipe: check-i18n-en-drift.mjs RC=0 (no en value changed); check-i18n-call-site-keys.mjs RC=0; check-i18n-dead-keys.mjs RC=0 (a report, not a gate, per its own header); check-control-bytes.mjs 'OK (scanned 4756 tracked text file(s); skipped 85 binary)'; `pnpm --filter @object-ui/i18n type-check` exit 0 with the script name echoed as 'tsc --noEmit && tsc -p tsconfig.test.json' (so not a zero-match no-op — note objectui spells it type-check, hyphenated); `pnpm --filter @object-ui/i18n lint` '34 problems (0 errors, 34 warnings)', all pre-existing and none in a locale file. LINT NARROWING stated as a measurement: eslint --no-inline-config --format json over the diff = 8 files, 0 errors, 0 warnings; the full population eslint itself resolves is 3544 files and that run also reports 0 findings in any packages/i18n/src/locales/ file; the config sets no projectService/project:, so linting is not type-aware and this diff cannot move any untouched file's verdict. All heavy runs went through the shared verify lock (scripts/pm/os-verify-lock.sh); every VERDICT line read command-exit 0 except the deliberate red leg.",
      "open_questions": [
        {
          "question": "Already raised on the card, restated here because it is the reason this survived several merges and is a CI-topology call the maintainer owns: should all-locales-key-parity run in a cheap, unskippable job instead of only inside the shard that a source-free diff skips? Today the guard is real but is bypassed by exactly the PRs that are cheapest to merge (#5698, markdown-only, completed shard 1/4 in 13s — a skip, not a run), so parity debt accumulates between source-touching PRs rather than failing at the commit that creates it. This is the second en-only landing in one day.",
          "options": [
            "A — Move the i18n parity/drift tests into a small always-run job (they cost ~4s for the parity file, ~22s for the whole i18n package), independent of the source-diff skip logic.",
            "B — Widen ci.yml's source-diff detector so packages/i18n/src/locales/** always counts as source, leaving the shard topology alone.",
            "C — Leave as is and rely on the follow-up discipline that has now under-delivered twice (#5696 en-only, #5697 zh-only)."
          ],
          "recommendation": "A, because the parity assertion is pack-intrinsic and cheap — it needs no build and no call site — so pinning it to the expensive shard buys nothing and costs exactly the coverage that matters. B fixes only the locale-file case; the same hole reopens the next time an en-only feature lands from a non-i18n directory, which is precisely how #5696 created this. Not implemented here: it is a CI-topology change and this card is a main-is-RED blocker that three round-16 PRs are held on, so it was kept minimal."
        }
      ],
      "out_of_scope_findings": []
    }

    Generated by Claude Code

  5. os-sales commented on Aug 22, 2026

    @os-sales
    CollaboratorAuthor

    Fix is up as #5708 and verified. Escalating the topology question behind it — that part is a maintainer call, not this seat's.

    #5708 carries the eight packs plus a changeset, +87/-0, no deletions and no reordering. Evidence I checked rather than took on trust:

    • Red leg captured first, on origin/main 83ec61881: Tests 8 failed | 24 passed (32), each failure naming exactly the five console.ai.pendingDrafts.* keys for ja/ko/de/fr/es/pt/ru/ar. Green leg: 32 passed, including all nine of the other-direction assertions (%s defines no key that en lacks) and both non-vacuity guards. Exactly the 8 cases flipped.
    • The two-blocks trap was avoided by construction — insertion anchored on the parsed ai: { line, not on a pendingDrafts grep.
    • Placeholders verified against the evaluated packs (via tsx, not a text scan): all ten packs report 5 keys, {{count}} in count, {{detail}} in publishedWithFindings, per-key placeholder shape equal to en's — and the unrelated home.pendingDrafts block still holding message/cta and gaining nothing. That last assertion is the one that proves the fix landed in the right block.
    • Per-pack conventions kept rather than en's imposed: ru puts the number last, as it already does for the sibling home.pendingDrafts counts; ja uses the full-width colon before {{detail}} per that pack's documented rule. Both deviations carry an in-pack note in the pack's own language.
    • Verify-lock scoped to a provable superset — packages/i18n plus the one consumer (PendingDraftsBar.tsx), established by enumerating every call site of console.ai.pendingDrafts — not app-shell's 493-file fallback. 52 files / 883 tests.

    The question I am not answering

    Should all-locales-key-parity run in a cheap, unskippable job rather than only inside a shard that a source-free diff skips?

    The concrete measurement: #5698 was markdown-only and its Test (shard 1/4) completed in 13 seconds — a skip, not a run. So it merged cleanly over a main that was already red on this. Docs-only PRs sail past; the first source-touching PR eats it. Parity debt accumulates between source-touching PRs instead of failing at the commit that creates it.

    This is the second en-only landing in one day (#5696 en-only, then #5697 delivering zh only), which is why it is worth deciding rather than absorbing.

    Options as the implementing agent framed them, with its recommendation:

    • A — move the i18n parity/drift tests into a small always-run job, independent of the source-diff skip logic. ~4s for the parity file, ~22s for the whole i18n package. (Recommended: the assertion is pack-intrinsic — no build, no call site — so pinning it to the expensive shard buys nothing and costs exactly the coverage that matters.)
    • B — widen ci.yml's source-diff detector so packages/i18n/src/locales/** always counts as source. Smaller, but fixes only the locale-file case; the hole reopens the next time an en-only feature lands from a non-i18n directory, which is precisely how feat(app-shell): standing unpublished-changes bar on the AI build surface #5696 created this.
    • C — leave it, relying on follow-up discipline that has now under-delivered twice.

    Deliberately not implemented in #5708: it is a CI-topology change, and this card is a main-is-red blocker with three PRs held on it. Keeping the unblock minimal was the right call; the topology fix wants its own card and its own evidence.


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Labels

domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpm:blockingpm:dispatchedpm:queue

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions