Skip to content

Commit 01e6cd9

Browse files
stroland02claude
andcommitted
docs: record B53 landed, queue B54, and hold B52 back on a red suite
The record worth keeping from B53 is not the fix. Its change widened a guard to catch UnicodeDecodeError, which adds a row to the decode-handler inventory, and it registered no driver -- so the gate that landed an hour earlier failed naming sync/index/typescript.py:201. The worker correctly proved that five other failures predated its change and did not notice a sixth was its own. Proving some failures are not yours is not proving that none are. B52 stays in flight rather than landing: its commit adds a required parameter to hold_back and leaves five callers on the old arity, reproduced here at clean 34789db as '5 failed, 10 passed'. B54 is the BOM half, reproduced across four readers before being queued. The requirements.txt case is the one that earns the task -- no error, and a dependency named with the mark attached, so a repository that depends on Stripe is ranked as not depending on it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 83825f6 commit 01e6cd9

1 file changed

Lines changed: 51 additions & 31 deletions

File tree

docs/superpowers/BACKLOG.md

Lines changed: 51 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -44,35 +44,35 @@ corpus stays a deliberate superset of what the rule proposes rather than equal t
4444
result name with a targeted site on the same operation, a fresh generation stops proposing `furever`
4545
`PostPaymentIntents-response`, and all four gate figures are unchanged by the rule change.
4646

47-
### B53 — A non-UTF-8 `package.json` crashes TypeScript adapter selection
48-
49-
`TypeScriptAdapter._declared_dependencies` (`src/sync/index/typescript.py:181`) reads the customer's
50-
manifest with `read_text(encoding="utf-8")` and catches only `json.JSONDecodeError`. A manifest that
51-
is not valid UTF-8 raises `UnicodeDecodeError` from the read, before `json.loads` is reached, and
52-
nothing catches it — so `matches()` raises out of adapter selection instead of answering "no declared
53-
dependency".
54-
55-
The method's own docstring states the contract it fails to keep: *"A customer's manifest is untrusted
56-
input, so an unparseable one answers 'no declared dependency' rather than raising."* An undecodable
57-
file is unparseable in exactly that sense, so this is a gap against stated intent rather than an open
58-
question.
59-
60-
Reproduced with a control, which is the part that makes it trustworthy: a valid UTF-8 manifest gives
61-
`matches()` True, `'{ not json'` gives False by the documented path, and the same JSON as UTF-16
62-
raises `UnicodeDecodeError`. A first attempt at that repro returned False for every input because
63-
`_package` was None — a run whose control also fails proves nothing.
64-
65-
The identical defect on the Python side was fixed as `5cbf7b8`. The TypeScript side never was. Every
66-
fixture in this repository is ASCII, which is why no existing test catches it.
67-
68-
Carries one thing to report rather than build: `PythonAdapter.matches` silently declines a
69-
repository whose manifest cannot be decoded and carries no reason where `intake` reports one. B51
70-
ranked that first among its leave-behinds because silent is worse than loud, but where a reason
71-
should land is a design decision, not a fix.
72-
73-
**Closes when:** `matches()` returns False on a non-UTF-8 `package.json` with a live control proving
74-
the path is reached, the new handler has a driver in `tests/test_decode_handlers.py` so every decode
75-
handler stays entered, and four gates are green.
47+
### B54 — A UTF-8 byte-order mark makes four manifest readers answer wrongly
48+
49+
`EF BB BF` is valid UTF-8, so `read_text(encoding="utf-8")` succeeds and hands every parser
50+
downstream a leading ``. Measured against `83825f6`, four sites, ordered by how hard the
51+
failure is to notice:
52+
53+
1. `TypeScriptAdapter._declared_dependencies` — a BOM'd `package.json` makes `matches()` return
54+
False silently, with no reason anywhere. `json.loads` raises `Unexpected UTF-8 BOM` and the
55+
handler turns it into "no declared dependency".
56+
2. `sync.signals.intake` on `package.json` — declined, with an accurate reason naming the BOM. The
57+
answer is wrong but a human can act on it.
58+
3. `sync.signals.intake` on `pyproject.toml` — declined with a *misleading* reason: `tomllib` says
59+
`Invalid statement (at line 1, column 1)` for a file that is valid TOML.
60+
4. `sync.signals.intake` on `requirements.txt`**no error at all and a wrong value**:
61+
`name='stripe'` instead of `'stripe'`, with `unreadable` empty. The second dependency in
62+
the same file parses correctly, which is why it is easy to miss.
63+
64+
Case 4 is why this is a task rather than a note. Intake decides whether a prospective customer's
65+
repository depends on a vendor we cover, and a BOM'd `requirements.txt` makes it answer no for a
66+
repository that does, silently and unrankably. Windows editors write BOMs by default and this
67+
project runs on Windows.
68+
69+
The fix is `encoding="utf-8-sig"`, which strips a BOM when present and decodes plain UTF-8
70+
unchanged when not, so it does not move the answer for any manifest that works today.
71+
72+
**Closes when:** each fixed case has a test that fails first for the expected reason and a control
73+
proving the plain-UTF-8 answer is unchanged — case 4 asserting on the parsed *value*, since there
74+
is no error to assert on — `DRIVERS` keys updated wherever an edit shifted a handler's line, and
75+
four gates green.
7676

7777
### B7 — The M0 acceptance run has not executed since the pipeline changed underneath it
7878

@@ -100,8 +100,11 @@ recorded with which change broke it.
100100

101101
## In flight
102102

103-
- **B52**`task_15860306d7c7`, worktree `sync-solo-a`.
104-
- **B53**`task_45f3e2d8c875`, worktree `sync-solo-b`.
103+
- **B52**`task_15860306d7c7`, worktree `sync-solo-a`. Its commit `34789db` leaves the suite
104+
red: `hold_back` gained a required `root: Path` and five two-argument callers in
105+
`tests/test_corpus_hold_back.py` were not updated. Reproduced at clean `34789db` in a throwaway
106+
branch — `5 failed, 10 passed`. Sent back to fix before it lands.
107+
- **B54**`task_1ea8ea5094a0`, worktree `sync-solo-b`.
105108

106109
**Briefs go in a file now, not in the dispatch spec.** Long message bodies are being truncated in
107110
delivery — three briefs today, and B52 received a correction paragraph while the four numbered
@@ -625,3 +628,20 @@ is what a reviewer needs and duplicating it here would let the two copies drift.
625628
handler — the arm a line-coverage check would still call covered — and watching the gate name
626629
`sync/signals/intake.py:275` exactly; a bogus driver naming a handler not in `src/` also fired.
627630
Two leave-behinds became B53; the 35 unhandled text decodes still need per-site triage.
631+
632+
- Decline a non-UTF-8 `package.json` instead of crashing on it. Landed `bdabe9c`, with the driver
633+
it omitted at `83825f6`. The worker measured two crash shapes rather than one — a UTF-16 manifest
634+
and a cp1252 `author` field failing on byte `0xe9`, the legacy-encoding case CLAUDE.md predicts —
635+
and both decline after. It also found B52's red suite and proved the five failures predate its own
636+
change by stashing its files at clean `34789db`, which is why that commit did not land with it.
637+
638+
**What it missed is the more useful record.** Its change widened a guard to catch
639+
`UnicodeDecodeError`, which adds a row to `test_decode_handlers.py`'s AST inventory, and it did not
640+
register a driver — so `test_every_decode_handler_has_been_entered` failed naming
641+
`sync/index/typescript.py:201`, one hour after that gate landed. The worker correctly attributed
642+
the five failures it found to another commit and did not notice a sixth was its own. Proving the
643+
five were not its fault is not the same as proving nothing was. The driver was written here and
644+
probed by reverting the fix: the driver's own test then raises `UnicodeDecodeError` at
645+
`typescript.py:200`, so it genuinely enters the arm rather than passing beside it.
646+
647+
Left behind and now B54: a BOM'd manifest, which decodes fine and defeats four readers instead.

0 commit comments

Comments
 (0)