Two defects that compound into an unbreakable loop
1. Last rendered: timestamp makes the generated file dirty on every render
-> Last rendered: 2026-08-10T23:53:14.916Z
+> Last rendered: 2026-08-11T00:09:25.444Z
PROJECT.md is generated and committed, so any render — including the one the post-commit hook triggers — rewrites this line and leaves the working tree dirty. There is no state in which git status is clean after a render.
2. The detour log accumulates duplicate rows for the same SHA
Measured in one repo right now — 8 rows, 4 distinct SHAs:
3 | `d249112` | DETOUR-COMMIT
2 | `ea50c52` | DETOUR-COMMIT
2 | `9883d39` | DETOUR-COMMIT
1 | `8e7a257` | DETOUR-COMMIT
ea50c52 is logged three times with different timestamps, twice with an empty note:
| 2026-08-10T16:29:04.686Z | `ea50c52` | DETOUR-COMMIT | ... | chore(pm): re-render PROJECT.md |
| 2026-08-10T22:15:34.211Z | `ea50c52` | DETOUR-COMMIT | ... | |
| 2026-08-10T22:32:31.209Z | `ea50c52` | DETOUR-COMMIT | ... | |
The log appears keyed on time rather than SHA, so running against an unchanged HEAD appends another row instead of being idempotent.
The loop
- Commit anything → the hook logs a DETOUR-COMMIT row and bumps the render timestamp
PROJECT.md is now dirty
- Commit
PROJECT.md → step 1 again, with a new row for the SHA of the render commit itself
Each iteration adds a row and a fresh timestamp. In one working session this produced 8 rows for 4 real commits, several describing commits whose only content was re-rendering the file the row lives in.
Practical effect: git status is never clean, so "commit everything, then verify clean" stops being a usable check — and the detour log, which exists to show what happened during a detour, fills with rows about its own regeneration.
Suggested fixes
- Drop the timestamp from the committed file, or move it to
.conductor/render-stamp.json (which already exists and is the natural home). A generated artifact that changes on every generation cannot be version-controlled cleanly.
- Make detour logging idempotent on SHA — one row per SHA; an unchanged HEAD should be a no-op, not an append.
- Skip logging commits whose only change is
PROJECT.md — they are the plugin's own bookkeeping, not detour work, and they are what closes the loop.
Either (1) or (3) alone breaks the loop; (2) fixes the log independently.
Engine 0.25.0. Related: #79 (add-epic --notes accepted and discarded) and #80 (gate-guard prints a refusal then applies the change) — same family: the plugin reporting one thing while doing another.
Two defects that compound into an unbreakable loop
1.
Last rendered:timestamp makes the generated file dirty on every renderPROJECT.mdis generated and committed, so any render — including the one the post-commit hook triggers — rewrites this line and leaves the working tree dirty. There is no state in whichgit statusis clean after a render.2. The detour log accumulates duplicate rows for the same SHA
Measured in one repo right now — 8 rows, 4 distinct SHAs:
ea50c52is logged three times with different timestamps, twice with an empty note:The log appears keyed on time rather than SHA, so running against an unchanged HEAD appends another row instead of being idempotent.
The loop
PROJECT.mdis now dirtyPROJECT.md→ step 1 again, with a new row for the SHA of the render commit itselfEach iteration adds a row and a fresh timestamp. In one working session this produced 8 rows for 4 real commits, several describing commits whose only content was re-rendering the file the row lives in.
Practical effect:
git statusis never clean, so "commit everything, then verify clean" stops being a usable check — and the detour log, which exists to show what happened during a detour, fills with rows about its own regeneration.Suggested fixes
.conductor/render-stamp.json(which already exists and is the natural home). A generated artifact that changes on every generation cannot be version-controlled cleanly.PROJECT.md— they are the plugin's own bookkeeping, not detour work, and they are what closes the loop.Either (1) or (3) alone breaks the loop; (2) fixes the log independently.
Engine 0.25.0. Related: #79 (
add-epic --notesaccepted and discarded) and #80 (gate-guard prints a refusal then applies the change) — same family: the plugin reporting one thing while doing another.