instar 1.3.983 → 1.3.985

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/dist/core/CredentialManualLevers.js +0 -0
  2. package/dist/core/CredentialManualLevers.js.map +1 -1
  3. package/dist/core/DeliverMessageHandler.js +0 -0
  4. package/dist/core/DeliverMessageHandler.js.map +1 -1
  5. package/dist/core/PeerEndpointResolver.d.ts +0 -0
  6. package/dist/core/PeerEndpointResolver.d.ts.map +1 -1
  7. package/dist/core/PeerEndpointResolver.js +0 -0
  8. package/dist/core/PeerEndpointResolver.js.map +1 -1
  9. package/dist/core/PostUpdateMigrator.d.ts +17 -1
  10. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  11. package/dist/core/PostUpdateMigrator.js +48 -1
  12. package/dist/core/PostUpdateMigrator.js.map +1 -1
  13. package/dist/core/QueueDrainLoop.js +0 -0
  14. package/dist/core/QueueDrainLoop.js.map +1 -1
  15. package/dist/core/ScopeAccretionSweep.js +1 -1
  16. package/dist/core/ScopeAccretionSweep.js.map +1 -1
  17. package/dist/core/SessionOwnership.js +1 -1
  18. package/dist/core/SessionOwnership.js.map +1 -1
  19. package/dist/core/StandardsEnforcementAuditor.js +1 -1
  20. package/dist/core/StandardsEnforcementAuditor.js.map +1 -1
  21. package/dist/core/StoreSnapshot.js +2 -2
  22. package/dist/core/StoreSnapshot.js.map +1 -1
  23. package/dist/core/UnionReader.js +0 -0
  24. package/dist/core/UnionReader.js.map +1 -1
  25. package/dist/core/benchmarkDivergenceCore.js +1 -1
  26. package/dist/core/benchmarkDivergenceCore.js.map +1 -1
  27. package/dist/core/cartographerSummary.js +0 -0
  28. package/dist/core/cartographerSummary.js.map +1 -1
  29. package/dist/core/routingPriceAuthority.d.ts +0 -0
  30. package/dist/core/routingPriceAuthority.d.ts.map +1 -1
  31. package/dist/core/routingPriceAuthority.js +0 -0
  32. package/dist/core/routingPriceAuthority.js.map +1 -1
  33. package/dist/messaging/relayContentDedup.js +0 -0
  34. package/dist/messaging/relayContentDedup.js.map +1 -1
  35. package/dist/monitoring/BenchmarkDivergenceAnalyzer.js +10 -10
  36. package/dist/monitoring/BenchmarkDivergenceAnalyzer.js.map +1 -1
  37. package/dist/monitoring/ExternalHogArmMarker.js +0 -0
  38. package/dist/monitoring/ExternalHogArmMarker.js.map +1 -1
  39. package/dist/monitoring/ExternalHogClassifier.js +0 -0
  40. package/dist/monitoring/ExternalHogClassifier.js.map +1 -1
  41. package/dist/monitoring/ExternalHogSampler.js +0 -0
  42. package/dist/monitoring/ExternalHogSampler.js.map +1 -1
  43. package/dist/monitoring/FeatureMetricsLedger.js +1 -1
  44. package/dist/monitoring/FeatureMetricsLedger.js.map +1 -1
  45. package/dist/monitoring/GreenPrAutoMerger.js +1 -1
  46. package/dist/monitoring/GreenPrAutoMerger.js.map +1 -1
  47. package/dist/monitoring/PermissionPromptAutoResolver.js +1 -1
  48. package/dist/monitoring/PermissionPromptAutoResolver.js.map +1 -1
  49. package/dist/monitoring/ProviderCostReportStore.js +0 -0
  50. package/dist/monitoring/ProviderCostReportStore.js.map +1 -1
  51. package/dist/monitoring/blockerSettleAuthority.js +0 -0
  52. package/dist/monitoring/blockerSettleAuthority.js.map +1 -1
  53. package/package.json +1 -1
  54. package/src/data/builtin-manifest.json +20 -20
  55. package/src/templates/scripts/telegram-reply.sh +61 -0
  56. package/upgrades/1.3.982.md +17 -2
  57. package/upgrades/1.3.984.md +116 -0
  58. package/upgrades/1.3.985.md +109 -0
  59. package/upgrades/side-effects/behavioural-promise-unverifiable.md +17 -2
  60. package/upgrades/side-effects/misplaced-flag-sent-as-message-text.md +193 -0
  61. package/upgrades/side-effects/raw-nul-bytes-hide-source-from-grep.md +199 -0
@@ -0,0 +1,193 @@
1
+ # Side-Effects Review — a flag after the topic id was sent to the user as message text
2
+
3
+ **Version / slug:** `misplaced-flag-sent-as-message-text`
4
+ **Date:** `2026-07-26`
5
+ **Author:** `Echo (instar-dev agent)`
6
+ **Second-pass reviewer:** `author-applied lenses — see Phase 5 (reduced independence, disclosed)`
7
+
8
+ ## Summary of the change
9
+
10
+ `telegram-reply.sh` parses flags in a loop that BREAKS at the first non-flag argument —
11
+ the topic id. Everything after that becomes the message via `MSG="$*"`. So
12
+ `telegram-reply.sh 29723 --tone-ack B15 --tone-reason "why"` did two wrong things at once,
13
+ both silently: it sent the literal text `--tone-ack B15 --tone-reason why` to the user, and
14
+ the tone-advisory override never reached the server.
15
+
16
+ **Measured, not hypothesised.** The pre-fix template is captured verbatim as a fixture
17
+ (SHA `a2cf0215…`, the same SHA registered in the migrator's shipped-SHA allowlist) and the
18
+ test suite runs it against a stub of `/telegram/reply/:topicId`: it returns exit 0, the
19
+ received `text` contains `--tone-ack`, and `metadata.toneAdvisoryAck` is `undefined`.
20
+
21
+ **The consequence was a corrupted measurement.** On 2026-07-26 the swallowed flags meant a
22
+ tone-gate advisory was re-reviewed as an ordinary send whose text now began with option
23
+ noise. The resulting verdict looked absurd, I read it as a malfunction, and graded a
24
+ **correct** check `wrong` in the decision-quality data. That record is durable and is not
25
+ retracted by this change — see §2.
26
+
27
+ **The root cause is not the script.** The tone-reaction flags were documented NOWHERE
28
+ agent-facing: not in the script's own usage header (which documents `--format` and
29
+ `--stdin-base64`), not in the CLAUDE.md template. The template documents `metadata.*` — the
30
+ HTTP shape — while simultaneously mandating "ALWAYS the relay script, never a hand-rolled
31
+ curl". The bridge between the two did not exist, so the invocation had to be invented. The
32
+ script then accepted the invention without complaint. A capability shipped without
33
+ instructions, plus a tool silent on the only usage an uninstructed caller would try.
34
+
35
+ Three parts, because it took three to close the chain: the guard (refuses), the script's
36
+ usage header, and the CLAUDE.md template (documents the correct form).
37
+
38
+ ## Decision-point inventory
39
+
40
+ | point | classification | note |
41
+ |---|---|---|
42
+ | `--*` argument after the topic id → refuse | `invariant` | Deterministic glob on an argument. No judgment, no model, no competing signals. Symmetric with the pre-existing `-*` refusal before the topic id. |
43
+ | stdin path exempt | `invariant` | Structural, not a rule: `$#` is 0 when the message comes from stdin, so the loop has nothing to inspect. |
44
+ | CLAUDE.md content-sniff marker | `invariant` | String presence test. |
45
+
46
+ ## 1. Over-block
47
+
48
+ **What legitimate inputs does this change reject that it shouldn't?**
49
+
50
+ An argument-form message whose own token is exactly `--something`. For example
51
+ `telegram-reply.sh 29723 the flag --tone-ack means X` is now refused, where before it sent.
52
+
53
+ This is deliberate and I think correct, but it is a real narrowing and worth stating
54
+ plainly rather than waving away:
55
+
56
+ - The refusal is **loud, immediate, and actionable** — it prints the corrected ordering and
57
+ names the escape hatch. The failure mode it replaces was silent and unrecoverable.
58
+ - The escape hatch is **the documented primary path**: stdin (`cat <<'EOF' | …`). Every
59
+ multi-line message already uses it, and stdin is untouched by this check.
60
+ - The alternative — matching only the eight known flag names — would have let a TYPO'd
61
+ flag (`--tone-akc`) through silently. That is the realistic mistake, and the whole point
62
+ is to stop flag-shaped tokens being swallowed. Matching the shape, not the list, is what
63
+ makes the guard useful rather than decorative.
64
+
65
+ ## 2. Under-block
66
+
67
+ **What failure modes does this still miss?**
68
+
69
+ - **The false grade from 2026-07-26 is not retracted.** This stops the cause; it does not
70
+ undo the effect. Whether a mistaken grade can be corrected at all — and whether a
71
+ supersede erases the evidence the mistake was made, which would be the opposite defect —
72
+ is an open question deliberately NOT answered here. Recorded rather than quietly folded in.
73
+ - **A single-token message that is exactly a flag, passed as an argument, is now refused
74
+ rather than sent.** Correct behaviour, but it is a refusal, not a rescue.
75
+ - **Only this script is fixed.** `slack-reply.sh` and `whatsapp-reply.sh` share the
76
+ parse-then-break shape. They do not carry tone-reaction flags today, so the same mistake
77
+ has less to swallow — but the shape is the same. Named here rather than silently
78
+ extended: widening the change to two more shipped scripts, each needing its own
79
+ shipped-SHA registration, belongs in its own review.
80
+ - **Documentation coverage generally.** This fixes one undocumented capability. Nothing
81
+ here establishes that others are documented.
82
+
83
+ ## 3. Level-of-abstraction fit
84
+
85
+ **Is this at the right layer?**
86
+
87
+ Yes, and at three layers on purpose, because the defect had three links:
88
+
89
+ 1. **The script** — the only place a misplaced flag can be caught before it becomes text.
90
+ 2. **The script's usage header** — where someone reading the tool learns the ordering.
91
+ 3. **The CLAUDE.md template** — where the agent that is *mandated* to use the script learns
92
+ that these flags exist at all. Fixing only the script would have left the root cause
93
+ (undocumented capability) fully intact; the next agent would guess again, get a refusal
94
+ instead of a silent mis-send, and still not know the right form.
95
+
96
+ Rejected: making the parser accept flags in either position. It looks friendlier and is
97
+ worse — `MSG="$*"` means the parser cannot distinguish "a flag I should apply" from "a word
98
+ of the message" without guessing, and a guess here silently rewrites what the user reads.
99
+ Refusing is the only honest option.
100
+
101
+ ## 4. Signal vs authority compliance
102
+
103
+ **Required reference:** [docs/signal-vs-authority.md](../../docs/signal-vs-authority.md)
104
+
105
+ **Does this change hold blocking authority with brittle logic?**
106
+
107
+ It holds blocking authority (the send is refused) on a deterministic glob over an
108
+ argument — no model, no heuristic, no network, no threshold. It cannot drift or degrade,
109
+ and it fails in the safe direction: it withholds a send rather than delivering a
110
+ misleading one.
111
+
112
+ It is also strictly *less* authority than it appears: it blocks nothing the caller cannot
113
+ immediately re-issue correctly, and the message text itself is never inspected — only
114
+ argument tokens.
115
+
116
+ ## 4b. Judgment-point check (Judgment Within Floors standard)
117
+
118
+ No judgment point is introduced. The guard is a `case` glob. Notably it *removes* an
119
+ implicit judgment: the old code silently decided that a flag-shaped token was message
120
+ content, which is precisely the kind of unexamined call this standard exists to surface.
121
+
122
+ ## 5. Interactions
123
+
124
+ - **stdin is structurally unaffected** — `$#` is 0 there, so the loop body never runs. This
125
+ is asserted by test, because "the primary path still works" is the claim most worth
126
+ checking.
127
+ - **Correct-order invocations are unchanged** — asserted end-to-end: message body clean,
128
+ `metadata.toneAdvisoryAck` and `toneAdvisoryAckReason` both present.
129
+ - **Pre-existing `Unknown flag` behaviour before the topic id is untouched** — asserted.
130
+ - **Migration parity for the SCRIPT**: the pre-change SHA `a2cf0215…` is registered in
131
+ `TELEGRAM_REPLY_PRIOR_SHIPPED_SHAS`. Without that entry the SHA-history migrator leaves
132
+ the swallowing version in place with a `.new` beside it and every deployed agent keeps
133
+ mis-sending — the failure mode the entry above it in that list was added to prevent.
134
+ - **Migration parity for the DOCS — the trap.** The tone-advisory CLAUDE.md block is
135
+ content-sniffed on `'Most checks are NUDGES you may override'`, a marker this change does
136
+ NOT alter. Appending guidance to that constant would therefore reach new installs ONLY;
137
+ every deployed agent short-circuits. Hence a second, independently-sniffed block. A test
138
+ simulates an agent that already carries the old section and proves it still receives the
139
+ new part — and I verified that test FAILS when the second block is disabled, so it cannot
140
+ become decorative.
141
+ - **No route, config key, persisted state, or schema change.**
142
+
143
+ ## 6. External surfaces
144
+
145
+ One user-visible change, and it is a removal of a bad one: a misplaced flag no longer
146
+ appears in the user's chat as literal option text. Nothing else about delivery changes.
147
+
148
+ ## 6b. Operator-surface quality
149
+
150
+ The refusal prints what was run, what the correct ordering is, and how to send the token
151
+ literally if it really is message text. An error that only said "bad arguments" would have
152
+ reproduced the original problem — a caller with no way to learn the right form — one level
153
+ up.
154
+
155
+ ## 7. Multi-machine posture (Cross-Machine Coherence)
156
+
157
+ **Posture: `unified` by construction — no new state.** A shell-script argument guard plus
158
+ two documentation blocks. No new field, file, route, or surface; nothing to replicate and
159
+ no `machine-local-justification` marker applicable. The script is deployed per machine by
160
+ the existing SHA-history migrator, which is the established path and unchanged here.
161
+
162
+ ## 8. Rollback cost
163
+
164
+ Low and total: revert one commit. The guard disappears, the docs revert, and the
165
+ shipped-SHA entry becomes a harmless extra allowlist member (never remove old SHAs — they
166
+ remain valid migration sources). No data, no migration, no state to unwind.
167
+
168
+ ## Phase 5 — Second-pass review (independent reviewer subagent)
169
+
170
+ **Disclosure, per Truthful Provenance:** no independent reviewer subagent was spawned — a
171
+ standing instruction in this session prohibits it unless the operator requests it. The
172
+ review lenses were applied by the author. That is **reduced independence**, recorded as
173
+ such rather than presented as a concurring second pass.
174
+
175
+ What author-applied review caught and changed:
176
+
177
+ 1. **The first version fixed only the script.** That would have left the actual root cause
178
+ untouched: the flags are documented nowhere, so the next agent guesses again — it would
179
+ just get a refusal instead of a silent mis-send. Verified the absence repo-wide before
180
+ claiming it (the flags appear only in the script itself, an old upgrade guide, and the
181
+ migrator). Docs are now part of the change, not a follow-up.
182
+ 2. **The CLAUDE.md content-sniff trap was nearly walked into.** Appending to the existing
183
+ constant looked sufficient and would have shipped a doc fix that reached new installs
184
+ only — the exact "works for new agents only" shape the Migration Parity Standard names as
185
+ a broken feature. Caught by asking who actually *receives* the edit.
186
+ 3. **The guard was nearly scoped to the eight known flag names**, which reads as more
187
+ precise and is weaker: a typo'd flag — the realistic mistake — would still be swallowed
188
+ silently. Widened to the shape, with the over-block cost stated in §1 rather than hidden.
189
+ 4. **The negative case was run, not assumed.** I disabled the second migration block and
190
+ confirmed the load-bearing test fails, because a migration-parity test that passes
191
+ whether or not the migration exists is worse than none.
192
+ 5. **The sibling scripts were checked and deliberately left alone** (§2) rather than swept
193
+ in for tidiness — each needs its own shipped-SHA registration and review.
@@ -0,0 +1,199 @@
1
+ # Side-Effects Review — a raw NUL byte made 30 source files invisible to grep
2
+
3
+ **Version / slug:** `raw-nul-bytes-hide-source-from-grep`
4
+ **Date:** `2026-07-26`
5
+ **Author:** `Echo (instar-dev agent)`
6
+ **Second-pass reviewer:** `author-applied lenses — see Phase 5 (reduced independence, disclosed)`
7
+
8
+ ## Summary of the change
9
+
10
+ `grep` classifies a file as binary if it contains a NUL (0x00) byte, and on a binary
11
+ file it emits **nothing** — not a match, not a `Binary file X matches` line, not even a
12
+ `0` under `-c`. Thirty tracked text files each contained one or more raw NUL bytes, so
13
+ every grep-based audit over `src/` silently skipped them and the empty result read as
14
+ "absent".
15
+
16
+ The bytes were never corruption. Each was a deliberate composite-key or hash separator
17
+ written as a literal byte instead of the six-character escape:
18
+
19
+ ```ts
20
+ const key = `${row.model}<literal 0x00>${row.framework}`; // now: \u0000
21
+ ```
22
+
23
+ The delimiter choice is correct — NUL cannot occur inside a model or framework name, so
24
+ it is collision-proof. Writing it raw is the whole defect.
25
+
26
+ **Measured:** 30 tracked files (22 under `src/`, 7 tests, 1 spec doc). Affected modules
27
+ include `blockerSettleAuthority.ts` (the true-blocker settle gate), `SessionOwnership.ts`,
28
+ `GreenPrAutoMerger.ts`, `PermissionPromptAutoResolver.ts` (an always-on safety floor),
29
+ all three `ExternalHog*` modules, and `StandardsEnforcementAuditor.ts` — the module that
30
+ audits whether standards carry structural guards was itself invisible to the standard
31
+ search instrument.
32
+
33
+ **Second consequence, worse in kind:** git applies the same rule but only sniffs the
34
+ first 8000 bytes. For the **11 files** whose NUL fell inside that window, `git diff`
35
+ rendered `Bin 5407 -> 5412 bytes` instead of a line diff — so pull requests touching
36
+ safety-critical authority code were reviewed **without the reviewer being shown the
37
+ changed lines**. For the other 19, git saw text while grep did not: the two instruments
38
+ disagreed, which is precisely why this survived so long.
39
+
40
+ Every raw byte is replaced with `\u0000` (identical runtime string; valid and unambiguous
41
+ in string, template and regex contexts — unlike `\0`, a legacy octal escape when followed
42
+ by a digit), and a new ratchet fails the build if one ever returns.
43
+
44
+ ## Decision-point inventory
45
+
46
+ | point | classification | note |
47
+ |---|---|---|
48
+ | `containsRawNul(file)` | `invariant` | A byte is present or it is not. No judgment, no model, no competing signals. |
49
+ | lint scope (extension allowlist) | `invariant` | Deterministic set of text-by-definition extensions. Binary fixtures are out of scope by construction, not by heuristic. |
50
+ | control bytes other than 0x00 | `invariant` (deliberate exclusion) | Empirically verified not to cause grep-skip; see §2. |
51
+
52
+ ## 1. Over-block
53
+
54
+ **What legitimate inputs does this change reject that it shouldn't?**
55
+
56
+ The lint rejects a raw 0x00 byte in a file with a text extension. There is **no
57
+ legitimate case it blocks**, because there is no case that needs one: any runtime NUL a
58
+ program wants is expressible as an escape, with an identical result. The lint therefore
59
+ carries no exemption list — an exemption would only ever be used to reinstate the defect.
60
+
61
+ The nearest real cost: a genuinely binary fixture that someone gives a `.json`/`.md`
62
+ extension would be refused. That is a naming error the lint is right to surface, and no
63
+ such file exists in the tree today (verified by the sweep).
64
+
65
+ ## 2. Under-block
66
+
67
+ **What failure modes does this still miss?**
68
+
69
+ - **Other raw control bytes remain.** ESC (0x1b), BEL (0x07), 0x1f and 0x7f appear in a
70
+ handful of hostile-input test fixtures (`RevertDetector.ts`, `liveOutputStream.ts`,
71
+ `process-health-render.test.ts`, and others). These are **deliberately not covered**:
72
+ I verified empirically that a file containing ESC/BEL is still `ASCII text` to `file`
73
+ and still fully searchable by `grep` — only 0x00 triggers the skip. Escaping them would
74
+ be cosmetic. A lint should enforce exactly the failure it is named for. Recorded rather
75
+ than silently widened. (A separate concern does exist — a raw ESC in a file that gets
76
+ `cat`'d is a terminal-escape vector — but that is a different defect with a different
77
+ argument, and folding it in here would smuggle it past review.)
78
+ - **Untracked and generated files are not scanned.** The lint walks `src`, `tests`,
79
+ `docs`, `scripts`, `.github`. Build output is excluded on purpose (it is regenerated
80
+ from the now-clean sources).
81
+ - **This fixes one silent instrument, not the class.** Other tools can also fail
82
+ closed-mouthed. Nothing here establishes that no others remain — see §5.
83
+
84
+ ## 3. Level-of-abstraction fit
85
+
86
+ **Is this at the right layer?**
87
+
88
+ Yes, and at two layers deliberately:
89
+
90
+ 1. **The data** — the bytes themselves are corrected at source, which is the only place
91
+ the fix can be complete. A tooling workaround (`grep -a` everywhere, a `.gitattributes`
92
+ override) would have left the files hostile to every *other* consumer, including
93
+ editors, reviewers, and any future agent's search.
94
+ 2. **The guard** — a repo-level test, matching the existing tree-scanning ratchets
95
+ (`no-empty-catch-blocks`, `no-silent-fallbacks`). It reads bytes with `readFileSync`
96
+ rather than shelling out to a search tool, so the lint cannot be blinded by the very
97
+ defect it detects.
98
+
99
+ Notably a `.gitattributes` `*.ts text` entry was considered and **rejected**: it would
100
+ have fixed git's rendering while leaving `grep` — the instrument that actually failed —
101
+ just as blind, and would have converted a visible defect into a hidden one.
102
+
103
+ ## 4. Signal vs authority compliance
104
+
105
+ **Required reference:** [docs/signal-vs-authority.md](../../docs/signal-vs-authority.md)
106
+
107
+ **Does this change hold blocking authority with brittle logic?**
108
+
109
+ It holds blocking authority (a failing test blocks a merge) on **maximally non-brittle**
110
+ logic: the presence of one specific byte in a file. There is no model, no heuristic, no
111
+ threshold, no parse. It cannot drift, cannot be rate-limited, and produces the same
112
+ verdict on every machine.
113
+
114
+ This is the correct place for hard authority — a deterministic, universally-checkable
115
+ fact — and precisely the shape the standard reserves it for.
116
+
117
+ ## 4b. Judgment-point check (Judgment Within Floors standard)
118
+
119
+ No judgment point is introduced. Every decision in the lint is a byte comparison or a
120
+ set membership test. Nothing consults an LLM; nothing weighs competing signals.
121
+
122
+ ## 5. Interactions
123
+
124
+ - **Runtime behaviour is unchanged.** `\u0000` denotes exactly the byte it replaces. The
125
+ lint asserts this explicitly (`JSON.parse` of the escape text `=== String.fromCharCode(0)`)
126
+ rather than leaving it as a claim in prose.
127
+ - **Persisted data is unaffected.** Several sites feed these separators into hashes
128
+ (`relayContentDedup`, `blockerSettleAuthority`, `UnionReader`, `ExternalHogArmMarker`).
129
+ Because the string is identical, every hash is identical — **no migration, no cache
130
+ invalidation, no re-keying**. This was the primary risk and it is checked, not assumed.
131
+ - **`git diff` becomes readable** for the 11 previously-binary files. A one-time
132
+ consequence: this PR shows them as `Bin` on the *old* side.
133
+ - **The empty-catch ratchet** scans `src/` as text via `readFileSync`, so it was NOT
134
+ blinded — verified before assuming a shared failure. Grep-based *ad hoc* audits were the
135
+ affected consumers, not the file-reading lints.
136
+ - **This session's own reasoning was affected.** A search for a retraction mechanism
137
+ across `src/` returned one unrelated subsystem, and I was one step from recording
138
+ "no such path exists" as a finding. That search had skipped 22 source files. The
139
+ correction is recorded in the run log rather than quietly dropped: **the finding I was
140
+ about to publish was manufactured by the defect this PR fixes.**
141
+
142
+ ## 6. External surfaces
143
+
144
+ None. No route, no config key, no persisted state, no user-visible behaviour. This is a
145
+ source-text and build-time change only.
146
+
147
+ ## 6b. Operator-surface quality
148
+
149
+ Nothing reaches the operator. The one surface is the failing-test message, which names
150
+ each offending file, states the consequence in plain terms ("INVISIBLE to grep — every
151
+ grep-based audit silently skips them and reports 'not found'"), and gives the exact fix.
152
+ A lint that only says "assertion failed" would have re-created the original problem at
153
+ the level of the guard.
154
+
155
+ ## 7. Multi-machine posture (Cross-Machine Coherence)
156
+
157
+ **Posture: `unified` by construction — no state at all.** This change introduces no
158
+ feature, no field, no file, no surface. It edits source text and adds a build-time check
159
+ that runs identically wherever the repo is checked out. No replication path is required
160
+ and no `machine-local-justification` marker applies, because there is nothing machine-local
161
+ to justify.
162
+
163
+ ## 8. Rollback cost
164
+
165
+ Trivial and total: one commit, 30 files, 33 single-line substitutions plus one new test
166
+ file. Reverting restores the raw bytes — and with them the grep-invisibility of 22 source
167
+ modules and the binary diffs on 11. There is no data to migrate back and no state to
168
+ unwind.
169
+
170
+ ## Phase 5 — Second-pass review (independent reviewer subagent)
171
+
172
+ **Disclosure, per Truthful Provenance:** no independent reviewer subagent was spawned — a
173
+ standing instruction in this session prohibits it unless the operator requests it. The
174
+ review lenses were applied by the author. That is **reduced independence**, recorded as
175
+ such rather than presented as a concurring second pass.
176
+
177
+ What author-applied review caught and changed:
178
+
179
+ 1. **The first draft of this very lint shipped five raw NUL bytes and hid itself from
180
+ grep.** I typed the escape inline; it was normalised into actual NUL bytes on write.
181
+ Caught only because I byte-checked the file I had just written instead of trusting it.
182
+ The lint now constructs both the raw byte and the escape *text* from char codes and
183
+ never types the escape inline — and the authoring hazard is documented in its header,
184
+ because the next author will hit exactly the same trap.
185
+ 2. **The scope was nearly widened to all control bytes** for tidiness. I tested the
186
+ assumption instead and found ESC/BEL do not cause the skip — so widening would have
187
+ added churn and noise while claiming safety it does not provide. Narrowed back, with
188
+ the empirical result recorded in §2.
189
+ 3. **A "scan is not silently empty" assertion was added.** Without it, a scan that walked
190
+ the wrong roots would find zero offenders and pass forever — the same absence-reads-as-
191
+ presence failure, reincarnated inside its own guard. The lint now asserts it examined
192
+ >500 files.
193
+ 4. **The hash-stability question was checked, not assumed.** Four sites feed these
194
+ separators into digests; had the substitution altered the string, persisted dedup keys
195
+ and arm markers would have silently changed meaning. Verified identical before
196
+ committing.
197
+ 5. **`.gitattributes` was rejected as the fix** (see §3) because it would have repaired
198
+ the *visible* symptom (git diffs) while leaving the *silent* one (grep) intact —
199
+ trading a detectable failure for an undetectable one.