Task — engineering-spec@1

Every mutation emits the whole file's truth, not just its section's

doneTASK-IMP-116
module improvement · class product · priority p1 · created 2026-07-17 · shipped null
depends on none · blocks none

TASK-IMP-116: Every mutation emits the whole file's truth, not just its section's

Summary

backlog-mutate retallies the section header of the row it touches and leaves the file-top Totals: line alone, by an explicit contract three tests pin. That line is the first number any reader sees, and it has no maintainer: the regenerator that owns it cannot run. Widen the declared mutation from two lines to three, so every write emits the whole file's truth - the completion of TASK-IMP-092's own argument.

Problem

An external review found Totals: 336 draft, 4 ready_to_implement, 176 done at line 10 of a file whose improvement section alone read 67 draft, 9 ready_to_implement, 39 done. The index of truth had a lying header. Consumers reading the header report wrong queue counts.

TASK-IMP-092 replaced incremental +1/-1 header adjustment with a full retally, and its rationale is decisive: "incremental adjustment faithfully preserves an inherited lie forever (the 086 incident's 34 vs true 20), while the retally makes every mutation emit the section's truth." It then stopped at section headers. The same lie was free to grow one line higher, and it did.

The line has no owner. regen_backlog writes it and cannot run - it reads docs/improvement/memory/backlog.yaml, retired in the 2026-07-08 migration. So the only mechanism that maintains Totals: is a script that raises FileNotFoundError.

Proposed Solution

retallyTotals(lines): find ^Totals: , count every parseable row in the file by status, rewrite the line in the file's own convention. Called by both flip and insert after the section retally, reported in the result as totals_line. Absent or unparseable Totals returns null and is not an error - a file without the line is legal.

The contract widens deliberately: a backlog-state-update@2 write now declares one status cell + its section header + the file Totals. That is the amendment this task exists to make, and it is the reason this is a task rather than an edit: three tests pin the current boundary and the skill's contract states it.

Alternatives Considered

Success Metrics

Scope

In scope: retallyTotals in backlog-mutate, its wiring into flip + insert, the backlog-state-update@2 contract amendment, the three pinned tests.

Out of scope / Non-Goals

Dependencies

None. retallyHeader, parseRow, and STATUS_ORDER all exist.

AI Authorship Disclosure

1. Description (normative)

2. Acceptance criteria

3. Edge cases

Audit

§1 - Verdict summary

Spec is 96 lines, 7 §1 clauses, 7 ACs, 6 edge cases. It amends a contract three tests pin, which is the reason it exists as a task rather than an edit. Every claim is verified against the live tree: the P1's numbers, TASK-IMP-092's rationale at backlog-mutate.mjs:146, the three pins, and the regenerator's FileNotFoundError. Passes after 6 findings.

§2 - Findings (all resolved)

ISS-001 - the task could read as overruling a gate rather than amending a contract

Rewriting t06/t07/t11 to let a fix through is how a gate quietly stops gating, and the first attempt at this fix did exactly that before the suite refused it. Resolved: Alternatives records "move the pins without amending the contract" as explicitly rejected; §1 #1.6 makes the new footprint normative and requires the skill's contract to state it; AC 7 verifies the prose moved with the code.

ISS-002 - widening a mutation's footprint invites further widening

Two lines became three; nothing stopped it becoming five. Resolved: §1 #1.6 caps it at exactly three and says "MUST NOT grow further"; AC 3 asserts the footprint, so a fourth line reds.

ISS-003 - a no-op write would churn the file

Rewriting an already-correct Totals on every mutation adds a diff line to every commit that changes nothing - the churn TASK-IMP-082 spent a task removing from the status page. Resolved: §1 #1.7 requires byte-identical + no totals_line report when already correct; AC 5 pins it.

ISS-004 - three counting rules could drift apart

retallyHeader, regen_backlog, and this would each decide what counts as a row. Resolved: §1 #1.5 binds all three to one rule (out-of-enum statuses excluded), and AC 6 tests it - the same single-source discipline TASK-IMP-104's t05 enforces on ver_lt.

ISS-005 - "fix the regenerator instead" was the cheaper option and is not obviously wrong

It keeps the footprint minimal. Resolved: recorded in Alternatives with the operator's reasoning - a number maintained by a ritual rots between rituals, and "nobody ran it" is exactly how the 086 rows sat wrong. Repairing regen_backlog stays a real task; this one removes its urgency rather than its need, and Non-Goals says so.

ISS-006 - a Totals line inside a fenced block would match ^Totals:

A doc example at column 0 would be rewritten. Resolved: §3 names the risk rather than hiding it - BACKLOG.md is a generated index and a column-0 fence there is already a corpus defect. Named, bounded, revisitable. An accepted risk stated is not the same as a risk missed.

§3 - Resolution

All 6 concerns addressed. The machine floor (task-lint) ran FIRST and was clean before any judgment family was applied, per TASK-IMP-084. Score = 10/10.


End of TASK-IMP-116 audit.