Skip to content

feat(reports): read the critiques back - #12

Merged
ValentinFigue merged 1 commit into
mainfrom
feat/read-critiques
Jul 31, 2026
Merged

feat(reports): read the critiques back#12
ValentinFigue merged 1 commit into
mainfrom
feat/read-critiques

Conversation

@ValentinFigue

Copy link
Copy Markdown
Owner

The critics wrote and nothing read them. .aether/out/TEMPER.md in one real repository reached 21 reviews across 357 lines, at which point seeing the latest meant:

awk '/^# Review/{n++} n==21' .aether/out/TEMPER.md

And aether review --help answered Unknown: aether review --help.

The asymmetry that explains it

whetstone already solved this. It wraps each critique in an <!-- aether:critique sha=… date=… blockers=N --> block inside the plan — addressable, carrying its own metadata, and the copy the gate hashes.

temper never did. It wrote a # Review — <target> — <date> heading into an append-only log: no delimiter, no end marker, the date as the only field. So two reviews on the same day are indistinguishable — and that is not hypothetical. The oldest two entries in that file are byte-identical:

# Review — staged diff — 2026-05-07
# Review — staged diff — 2026-05-07

What this adds

$ aether review list
.aether/out/TEMPER.md

  #    when               scope       🔴   🟡   🟢
  21   2026-07-31T12:04Z  all          0    1    4
  20   2026-07-29         —            0    0    0
   2   2026-05-07         —            0    1    3
   1   2026-05-07         —            0    1    5

  21 entries.  `aether review show <#>` for one of them.

That is the view cat cannot give: have any of my recent reviews had blockers. Plus review show [N], --raw for piping to glow, and aether plan critique doing the same for whetstone — reading the block inside the plan, which is authoritative, and saying so when the plan has changed since.

temper's reports now carry the same marker, with a timestamp rather than a date. The reader accepts both shapes, which is load-bearing rather than polite: entries written before it are still listed and shown from their heading, with no scope and no time of day, and the table says so. A reader that only understood the marker would make every report already on disk invisible — worse than the awk it replaces.

Three bugs the real file taught, that a fixture would not have

The counts format is not stable, because a model wrote it. Across one history the same three numbers appear three ways:

**Blockers:** 0
**Blockers:** 0 | **Significant:** 3 | **Minor:** 4
**Blockers:** 0 — **Significant:** 1 — **Minor:** 2 — Good to ship; address the gap

Stripping non-digits from the line turned 2 | 3 | 4 into 234 blockers. The parse now reads only the number following each label. This is also the argument for the marker: the list view should not have to parse prose at all.

flush() ended entries at NR - 1 — correct when a new heading closes the previous entry, wrong at end of file, where NR is already the last line. It silently truncated the closing lines of the newest report, which is the one you actually read.

body=$(...); rc=$? aborted under set -e before the case could run, so an out-of-range index printed nothing at all. cmd_check hit this exact trap before; the fix is the same && rc=0 || rc=$? the repo already documents.

A fourth was self-inflicted and worth the note: an apostrophe in a code comment (the entry's final line) closed the single-quoted awk program and handed the rest to bash — the trap enforce-cairn.sh already carries a comment about.

Also

aether review --help, aether plan --help and aether config --help all answered Unknown: … — on exactly the commands whose subcommands you are most likely to be hunting for.

Verification

Against the real file first, since it is what prompted this:

21 entries parsed, counts correct across all three prose formats
aether review show --raw | diff - <(awk '/^# Review/{n++} n==21' …)   → identical

Then tests/test_reports.sh (41 assertions): mixed marked and unmarked in one file, byte-identical same-day headings staying addressable, a # Review line inside a fenced block not splitting the entry, counts read per label, an empty file and a missing file and a report with no counts each saying which, the pre-1.1 .claude/plans/TEMPER.md location being read and then abandoned once .aether/ exists — mirroring the writer's rule rather than inventing one — and plan critique against critiqued, uncritiqued and stale plans.

852 assertions across 14 files; acceptance.sh --full 46/46; aether docs and aether config doctor clean.

The critics wrote and nothing read. One repository reached 21 reviews in 357
lines of .aether/out/TEMPER.md, at which point seeing the latest meant
`awk '/^# Review/{n++} n==21'`, and `aether review --help` answered
`Unknown: aether review --help`.

whetstone already had the right shape: an `<!-- aether:critique … -->` block
inside the plan, addressable and carrying its own metadata. temper had only a
`# Review — <target> — <date>` heading, no delimiter, date as the only field —
so two reviews in a day are indistinguishable. Not hypothetical: the oldest two
entries in that file are byte-identical headings.

  aether review list          the table, with severity counts
  aether review show [N]      one report; --raw for glow
  aether plan critique        whetstone's, from inside the plan
  aether plan critique --history

temper's reports now carry the same marker, with a timestamp rather than a
date. The reader accepts both shapes: entries written before it are still
listed and shown from their heading, with no scope and no time of day, and the
table says so. A reader that only understood the marker would make every
existing report invisible, which is worse than the awk.

Three things the real file taught, none of which a fixture would have:

The counts format is not stable, because a model wrote it. Across one history
the same three numbers appear as `**Blockers:** 0`, as
`**Blockers:** 0 | **Significant:** 3 | **Minor:** 4`, and as
`**Blockers:** 0 — … — Good to ship`. Stripping non-digits from the line turned
2 | 3 | 4 into 234. The parse now reads the number following each label.

flush() ended entries at NR-1, right when a new heading closes the previous one
and wrong at end of file, which silently truncated the newest report — the one
you actually read.

`body=$(...); rc=$?` aborted the function under `set -e` before the case could
run, so an out-of-range index printed nothing. cmd_check hit this exact trap
before; the fix is the same `&& rc=0 || rc=$?`.

Also fixes --help on review, plan and config, which all answered `Unknown:`.

41 new assertions; 852 across 14 files; acceptance 46/46. Verified against the
real 21-entry file: all parsed, and `show --raw` is byte-identical to the awk
one-liner it replaces.
@ValentinFigue
ValentinFigue merged commit 79c7236 into main Jul 31, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant