Skip to content

feat(app): revise a groomed change's spec and owned sections through change.groom (change 0445) - #332

Merged
danielhanold merged 14 commits into
mainfrom
feat/revise-a-groomed-change-s-spec-and-owned-sections-through-a
Sep 25, 2026
Merged

danielhanold merged 14 commits into
mainfrom
feat/revise-a-groomed-change-s-spec-and-owned-sections-through-a

Conversation

@danielhanold

@danielhanold danielhanold commented Sep 24, 2026 •

Copy link
Copy Markdown
Owner

↩ Change 0445 — Revise a groomed change's spec and owned sections through a typed operation

Adds a revise outcome to change.groom. You can now adjust an already-groomed proposed change (its linked spec body, its owned proposal sections, or both) in one transaction pinned to the exact version. Before this, the only route was a hand edit plus a plain git commit.

What changed

  • change.groom accepts outcome: revise, but only for a proposed change that already has a spec or a trivial verdict. spec_markdown replaces the whole body of the existing spec file, keeping its path and backlink. sections edits the owned proposal sections. It never writes spec: or trivial:.
  • New refusals: not-revisable, spec-not-linked, spec-file-missing, empty-revise, empty-spec_version, and invalid-spec_version. A spec-body revise must send spec_version, the blob id of the spec the record's spec: links; the plan step checks it, and a stale one returns contended (spec-version-mismatch). A revise that would change nothing returns no-op.
  • spec_markdown that contains a docket:backlink block is refused (invalid-spec_markdown) on both spec and revise.
  • The result reports outcome and prints change NNNN revised — ….
  • docket-groom-next sends an explicit id for an already-groomed change to a revise flow. This replaces the "clear spec: by hand" workaround. docket-new-change points to that flow. The skill word budgets were raised to fit, and the embedded assets were regenerated.

Review (docket-review-deep)

# Severity Finding Disposition
1 blocker A byte-identical record or spec was declared as a mutation, so the engine failed at verify-delta (for example, a same-day spec-only revise) fixed — d2e1adf
2 important The spec file overwritten by a revise was not version-pinned, so a concurrent edit could be silently lost fixed — c0b759e
3 important spec_markdown carrying a backlink block produced a duplicate backlink fixed — ca487e4
4 minor The groom-next contended-retry rule was not adapted for revise fixed — c20ccf3
5 minor The groom-next description did not mention the revise route fixed — c20ccf3
6 minor Refusal-status coverage was incomplete, and there was no receipt spec_path assertion fixed — c20ccf3

Build notes: the first full-suite gate failed on stale embedded skill assets. They were regenerated in 1668bd9, and the gate then passed. The final full suite passed on the head below: 54/54 files.

Results: docs/results/2026-09-24-revise-a-groomed-change-s-spec-and-owned-sections-through-a-results.md

command: go run ./cmd/docket development test
result: green
head_sha: c7cbf91
ran_at: 2026-09-25T06:42:56Z

Post-review revision (5bf6014): dropped the spec_path request field. It could only ever repeat the record's spec: value, so the spec path now comes from the record and only spec_version is sent.

danielhanold added a commit that referenced this pull request Sep 25, 2026
… groom (change 0445)

Human review of PR #332: spec_path could only ever repeat the record's spec:
value (Plan refused anything else), and a wrong value surfaced as a generic
duplicate-expectation error. The linked spec path now comes from the record.

A spec-body revise still requires spec_version, but it is checked in Plan
against the blob at the record's linked spec path on the attempt's base tree
(the engine checks expectations before the record is read; Plan runs on the
same fetched base, so the check is equally exact). A stale spec_version refuses
with spec-version-mismatch, mapped onto contended like the engine's own pin
mismatch. spec_version on any other request is refused (invalid-spec_version)
rather than silently ignored. empty-spec_path and spec-path-mismatch are gone.
Docket-Plan-Path: docs/superpowers/plans/2026-09-24-0445-revise-groomed-change-typed-operation.md
…ts (change 0445)

go generate ./internal/assets/ to refresh the embedded manifest and the
docket-groom-next / docket-new-change SKILL.md copies that 0b88f3a left
stale, restoring TestEmbeddedMatchesAuthored and
TestDevelopmentInstallFreshRenderHandoff.
…ange 0445)

Review blocker: changeGroomOp.Plan unconditionally declared the change
record (and, on a spec-body revise, the linked spec) as MutationReplace.
A spec-only revise over a record whose updated: already equals today and
whose docket:artifacts block is already rendered produced record bytes
identical to the source, and an identical spec body or identical section
text did the same for the spec/record. The engine's verifyActualDelta
rejects a declared path that is not an actual change, so these revises
ended in a failed disposition.

Declare the record only when finalBytes differ from the source, and the
spec replace only when the assembled bytes differ from the existing spec
blob (read once via the new treeBlob helper). When nothing differs the
plan is empty, which the engine already treats as its clean no-op path -
the same skip includeBoard makes for the board (change 0335).

Regression tests: a spec-only revise over a settled record declares only
the spec; identical spec+section, spec-only, and section-only revises
plan zero files. Mutation-tested: defeating either bytes.Equal guard
reddens the new tests.
…ise (change 0445)

Review finding (important): ChangeGroom pinned only the change record, so a
revise carrying spec_markdown whole-body-replaced the spec file unpinned. Two
revises pinned to the same record version (a same-day spec-only revise leaves
the record bytes unchanged) both applied and the second clobbered the first;
a concurrent spec edit was silently lost too.

The request gains spec_path + spec_version (the linked spec's blob id). They
travel as a pair on any outcome and are required on a revise carrying
spec_markdown (empty-spec_path / empty-spec_version shape refusals, registered
in the finding-code vocabulary). A supplied pin becomes a second exact-blob
entity expectation, so drift yields the engine's standard contended outcome,
and Plan refuses spec-path-mismatch when the pin names anything but the
change's linked spec. docket-groom-next's revise exit documents reading the
spec's blob id; embedded assets regenerated.
… block (change 0445)

Review finding (important): a spec or revise spec_markdown that still held
the docket:backlink block passed shape validation, and assembleSpecFile
prepended a second block, committing duplicate marker pairs. Shape
validation now refuses it with invalid-spec_markdown for both outcomes
(refusal, never silent stripping). docket-groom-next's revise exit states
spec_markdown excludes the backlink block; embedded assets regenerated.
…sal/receipt coverage (change 0445)

Review minors 4-6:

- Minor 4: docket-groom-next Step 5 now adapts the contended-retry and
  board clauses for the revise exit: a revise re-reads the spec too (a
  fresh spec_version) and stops only if the change is no longer an
  already-groomed proposed change; a revise keeps the row build-ready.
- Minor 5: the skill's frontmatter description names the revise route
  (revising an already-groomed proposed change by explicit id).
- Minor 6: TestChangeGroomPlanReviseRefusals adds not-revisable rows for
  in-progress, deferred, implemented, done, and killed (spec acceptance
  item 7); the spec-only and sections-only revise tests decode the plan
  receipt and pin spec_path to the existing linked path / empty.
  Mutation-checked in both directions.

Word ceiling for docket-groom-next/SKILL.md raised 1850 -> 1889 with the
house annotation; embedded assets regenerated.
… groom (change 0445)

Human review of PR #332: spec_path could only ever repeat the record's spec:
value (Plan refused anything else), and a wrong value surfaced as a generic
duplicate-expectation error. The linked spec path now comes from the record.

A spec-body revise still requires spec_version, but it is checked in Plan
against the blob at the record's linked spec path on the attempt's base tree
(the engine checks expectations before the record is read; Plan runs on the
same fetched base, so the check is equally exact). A stale spec_version refuses
with spec-version-mismatch, mapped onto contended like the engine's own pin
mismatch. spec_version on any other request is refused (invalid-spec_version)
rather than silently ignored. empty-spec_path and spec-path-mismatch are gone.
@danielhanold
danielhanold force-pushed the feat/revise-a-groomed-change-s-spec-and-owned-sections-through-a branch from 5bf6014 to c7cbf91 Compare September 25, 2026 06:43
@danielhanold
danielhanold merged commit 45c5b82 into main Sep 25, 2026
1 check passed
@danielhanold
danielhanold deleted the feat/revise-a-groomed-change-s-spec-and-owned-sections-through-a branch September 25, 2026 06:43
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