Skip to content

Commit 352005d

Browse files
authored
Merge pull request #1083 from AdaWorldAPI/claude/prohibit-destructive-prepend
guardrail: prohibit the destructive-prepend one-liner (P0) + mandatory wc -l post-check
2 parents 82679c3 + ca4bd97 commit 352005d

2 files changed

Lines changed: 62 additions & 0 deletions

File tree

.claude/board/EPIPHANIES.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,46 @@
1+
## 2026-08-30 — E-DESTRUCTIVE-PREPEND-TRUNCATES-BEFORE-READ-1 — the prepend one-liner `open(p, "w").write(entry + open(p).read())` destroyed 5876 lines of PR_ARC_INVENTORY; the pattern is now PROHIBITED
2+
3+
**Status:** FINDING + PROHIBITION (operator-directed, 2026-08-30). Measured on
4+
a real loss, restored same day (#1082, merge `82679c3a`), pattern banned in
5+
`CLAUDE.md` § In-Session Orchestration Discipline in the same PR.
6+
**Confidence:** Certain — the mechanism is Python evaluation order, reproduced
7+
and named, not inferred.
8+
9+
The #1081 board-hygiene commit prepended its arc entry to
10+
`.claude/board/PR_ARC_INVENTORY.md` with
11+
12+
```python
13+
open(p, "w").write(entry + "\n" + open(p).read()) # PROHIBITED
14+
```
15+
16+
Python evaluates `open(p, "w")` — which **truncates the file to zero bytes** —
17+
before the argument expression's `open(p).read()` runs. The read-back returns
18+
the empty string, and the write emits only the new entry: the append-only
19+
ledger collapsed 5876 → 32 lines on main, leaving a dangling #1079
20+
self-reference as the visible wound. The same holds for every
21+
truncate-then-read-same-path shape in any language (`>` shell redirection into
22+
a pipeline that reads the same file, `fs.writeFileSync(p, x + fs.readFileSync(p))`
23+
is safe only because JS evaluates arguments first — do not rely on remembering
24+
which language does which).
25+
26+
**The prohibition (P0, workspace-wide):** never open a file for
27+
writing/truncation in the same expression, pipeline, or statement that still
28+
needs to READ that file. Prepends are three steps: (1) read into a variable,
29+
(2) compose in memory, (3) write — or use the `Edit` tool, which is
30+
read-anchored by construction and the default for board files anyway.
31+
32+
**The falsifier (mandatory after every ledger write):** `wc -l` the file.
33+
An append-only file that got SHORTER is always a defect — no exceptions, no
34+
"it probably deduplicated something".
35+
36+
Cross-refs: census trap 10
37+
(`docs/architecture/COGNITIVE-FABRIC-CENSUS-2026-08-30.md` §8.3); restore PR
38+
#1082; the pre-existing "Read before Write, always" P0 in `CLAUDE.md` — this
39+
entry is its sharper edge: read-before-write applies WITHIN a single
40+
expression's evaluation order, not just across tool calls.
41+
42+
---
43+
144
## 2026-08-30 — E-A-FLOOD-THROTTLE-IS-NOT-A-DISCRIMINATOR-1 — the census equated `hub_indegree` with a specificity test; they are opposite where it matters
245

346
**Status:** FINDING (measured, review-driven). Raised as codex P2 on #1079,

CLAUDE.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1395,6 +1395,25 @@ instead of *built from state*. If you see this on your own commit, you just
13951395
overwrote committed work. Revert with `git restore <path>` and use `Edit`
13961396
for any genuine refinement.
13971397

1398+
**P0 Rule: the destructive-prepend one-liner is PROHIBITED**
1399+
(added 2026-08-30 after it destroyed 5876 lines of `PR_ARC_INVENTORY.md`
1400+
in #1081; restored in #1082; full entry
1401+
`E-DESTRUCTIVE-PREPEND-TRUNCATES-BEFORE-READ-1`, census trap 10):
1402+
1403+
```python
1404+
open(p, "w").write(entry + open(p).read()) # NEVER — truncates before it reads
1405+
```
1406+
1407+
`open(p, "w")` truncates the file BEFORE the argument's `open(p).read()`
1408+
runs; the read-back is empty and the file's history is gone. Prohibited in
1409+
every form: any expression, statement, or shell pipeline that opens a file
1410+
for writing/truncation while it still needs to READ that same file
1411+
(`sort f > f` is the same defect). Prepend in three steps — read into a
1412+
variable, compose in memory, write — or use the `Edit` tool, which is
1413+
read-anchored by construction. **Mandatory post-check after every board /
1414+
ledger write: `wc -l` the file. An append-only file that got SHORTER is
1415+
always a defect.**
1416+
13981417
**Tool-reach reminder for deferred tools.** `AskUserQuestion`, `TodoWrite`,
13991418
`WebSearch`, `WebFetch` are namechecked in the Claude Code system prompt but
14001419
sit behind `ToolSearch` in Opus 4.6 — their schemas are not loaded by default.

0 commit comments

Comments
 (0)