Skip to content

docs(finalize): make nw-finalize consistent on preserving workspace - #85

Open
tburny wants to merge 6 commits into
nWave-ai:mainfrom
tburny:fix/83-nw-finalize-workspace-contradiction
Open

docs(finalize): make nw-finalize consistent on preserving workspace#85
tburny wants to merge 6 commits into
nWave-ai:mainfrom
tburny:fix/83-nw-finalize-workspace-contradiction

Conversation

@tburny

@tburny tburny commented Aug 8, 2026

Copy link
Copy Markdown

Why

nw-finalize told the agent both to preserve and to remove docs/feature/{feature-id}/, so two runs of the same command on the same feature could legitimately disagree about whether the directory survived. Phase C step 3 of the skill was hardened to preserve it — and states the reason: the wave-status matrix derives feature status from that directory, so removing it makes finalized features disappear from the matrix. That change never reached the six other passages, nor either copy of the command asset, both of which still carried a literal rm -rf docs/feature/{feature-id}/. Whichever passage an agent happened to weight decided whether it destroyed data another part of nWave reads, at the exact moment the feature was declared complete.

This aligns every passage on preserve.

What changed

  • nWave/skills/nw-finalize/SKILL.md — Overview, Phase D step 5, Success Criteria, Example 1, Deliverables and Expected Outputs now state preservation; Phase C/D cleanup is scoped to session markers and resume state only.
  • nWave/tasks/nw/finalize.md — removed the literal rm -rf docs/feature/{feature-id}/; aligned the same six passages.
  • plugins/nw/commands/finalize.md — byte-identical duplicate of the task asset, aligned identically. (Worth noting this file is a full copy of nWave/tasks/nw/finalize.md; it drifted in lockstep here, but nothing enforces that.)
  • tests/bugs/test_issue_83_finalize_workspace_preserved.py — new regression guard, parametrized over all three assets.

Which way the contradiction was resolved, and why

Preserve is treated as canonical. Phase C step 3 is the only passage that names a system-level consequence; the removal passages assert the behaviour without justifying it. Deletion silently destroys data another part of nWave reads, whereas preservation is merely untidy — the asymmetry decides it.

This is not only inference. nWave-ai/nWave-experimental has already resolved the same contradiction the same way: nWave/skills/nw-finalize/SKILL.md there states "docs/feature/{feature-id}/ is the active feature workspace and remains the feature's living history after finalization", consistently across its description, Overview, and Success Criteria. This PR brings the stable track in line with that.

If the intended behaviour is in fact removal, this PR is the wrong shape and should be rejected rather than amended — the fix would then be to delete Phase C step 3 and its stated rationale instead.

Test plan

  • ./.venv/bin/python -m pytest tests/bugs/test_issue_83_finalize_workspace_preserved.py -q — 3 passed (one per asset).
  • Verified the guard genuinely fails at the base commit: 1d0f13c carries 7 removal assertions across the three files, including two literal rm -rf docs/feature/{feature-id}/. At HEAD the same search returns nothing.
  • ./.venv/bin/python -m pytest tests/des/unit -q — unchanged from the pre-existing baseline (1311 passed, 2 failed, 1 xfail). The two failures are present on unmodified 1d0f13c and are unrelated to this change: test_hook_completed_emitted_on_block_path and test_pre_tool_use_blocked_event_carries_hook_id.
  • Not tested: no runtime behaviour is exercised here — these are prompt assets read by an agent, so the guard asserts textual consistency rather than execution.

Note for anyone reproducing this: uv sync currently fails on a fresh clone of this repo (pyproject.toml force-includes lib/python/des, which is untracked here), so the suite was run in a hand-built virtualenv with the repo root on sys.path.

Focus areas

  • The resolution direction is the whole review. Everything else is mechanical. If preserve is right, the diff is uncontroversial; if it is wrong, none of it should land.
  • The guard matches forbidden phrasings by regex, so a future rewording could assert removal in words the patterns miss. It catches regression to the known phrasings, not every conceivable one.
  • plugins/nw/commands/finalize.md duplicating nWave/tasks/nw/finalize.md byte-for-byte is the underlying reason this drift could happen twice. Deduplicating them is out of scope here, but the duplication is the bug behind the bug.

Closes #83

nw-finalize stated both that docs/feature/{feature-id}/ is preserved and
that it is removed. Phase C step 3 of the skill was hardened to preserve
the directory, but the change never reached the Overview, Phase D step 5,
the Success Criteria, Example 1, the Deliverables line, or the Expected
Outputs block -- nor either copy of the command asset, which still carried
a literal `rm -rf docs/feature/{feature-id}/`.

ASSUMPTION (maintainer may reject cheaply): PRESERVE is canonical. Phase C
step 3 is the only passage that names a system-level consequence -- the
wave matrix derives feature status from this directory, so deleting it
makes finalized features disappear from the matrix. Deletion silently
destroys data another part of nWave reads; preservation is merely untidy.
All passages are now aligned on preserve, and Phase C/D delete session
markers and resume state only.

Aligned assets:
- nWave/skills/nw-finalize/SKILL.md
- nWave/tasks/nw/finalize.md
- plugins/nw/commands/finalize.md (byte-identical duplicate of the task)

A regression guard asserts that no finalize asset claims workspace removal
and that each states why the workspace is retained.

Issue: nWave-ai#83
@nwaveai

nwaveai commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

⚠️ This PR cannot be automatically mirrored to nwave-dev.

The following files are on the sensitive-file denylist and cannot be
changed via the external PR flow:

  • plugins/nw/commands/finalize.md (matched rule: plugins/**)

These files are typically managed by the release pipeline or contain
project infrastructure. If you believe this change is legitimate,
please open an issue explaining the intent — a maintainer can assist
or apply the change directly.

This PR remains open for discussion. No mirror branch has been created.

The first pass aligned the body passages but left the two highest-visibility
lines untouched in all three assets: the frontmatter `description` (which
drives skill dispatch) and the one-line Overview both still promised to
"clean up the temporary workspace". A reader who never opens the body sees
only those two lines, so the contradiction survived where it was most likely
to be read.

Wording follows nWave-ai/nWave-experimental, which already resolved this the
same way: "preserves the feature workspace, and cleans session artifacts".
The migration table's "Source (temporary workspace)" header is likewise no
longer temporary.

Issue: nWave-ai#83
@tburny

tburny commented Aug 8, 2026

Copy link
Copy Markdown
Author

Pushed a follow-up commit (9411975).

A skill-quality evaluation of the changed SKILL.md flagged that the first pass aligned the body passages but missed the two most-read lines in all three assets: the frontmatter description — the field that drives skill dispatch — and the one-line Overview both still promised to "clean up the temporary workspace". A reader who never opens the body would have seen only the contradiction.

The wording now follows nWave-experimental, which resolved this the same way: "preserves the feature workspace, and cleans session artifacts". The migration table's Source (temporary workspace) header is updated for the same reason.

A repo-wide search for temporary workspace and cleans up across the three finalize assets now returns nothing. Guard still green (6 passed).

@nwaveai

nwaveai commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

⚠️ This PR cannot be automatically mirrored to nwave-dev.

The following files are on the sensitive-file denylist and cannot be
changed via the external PR flow:

  • plugins/nw/commands/finalize.md (matched rule: plugins/**)

These files are typically managed by the release pipeline or contain
project infrastructure. If you believe this change is legitimate,
please open an issue explaining the intent — a maintainer can assist
or apply the change directly.

This PR remains open for discussion. No mirror branch has been created.

@tburny

tburny commented Aug 8, 2026

Copy link
Copy Markdown
Author

Question on the plugins/nw/** hunks in this PR.

This PR edits the marketplace payload under plugins/nw/** alongside the source, because the two are byte-identical and I could not tell from the public tree whether the payload is regenerated upstream or maintained by hand. The release workflows that would answer that are not synced here — .github/ contains only dispatch-pr.yml.

I kept the payload edits rather than stripping them, deliberately, because the asymmetry favours it: if the payload IS regenerated, these hunks are harmless noise that your pipeline overwrites; if it is NOT, stripping them would ship a marketplace copy contradicting the source — which is exactly the divergence issue #83 describes.

If the payload is generated, say so and I will drop those hunks from this PR. Nothing else in the diff depends on them.

plugins/** is on the repository's sensitive-file denylist and cannot be changed
through the external PR flow -- the mirror bot refuses to create a nwave-dev
branch while those paths are touched. The payload is reverted to its committed
state and the regression guard now covers only the two source assets.

This leaves plugins/nw/commands/finalize.md asserting workspace removal while
its source counterpart asserts preservation. If the payload is regenerated from
nWave/tasks/nw/ at release time that resolves itself; if it is not, it needs a
maintainer-side edit that an external contributor cannot make.

Issue: nWave-ai#83
@nwaveai

nwaveai commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

⚠️ Automatic mirror failed due to a rebase conflict.

This change does not apply cleanly onto the matching private base.
A maintainer will review and either resolve the conflict manually
or request a rebase against the latest base.

No action is required from you right now. We'll comment again once
the mirror is in place or if we need input.

Review found the first pass fixed only half the contradiction. The workspace was
declared preserved while the 'What NOT to Migrate (Discard)' table still listed
deliver/execution-log.json, roadmap.json and the wave-decisions files under a
'Why discard' column introduced by 'disposable after'. An agent following the
table would gut the preserved directory: des-verify-integrity and /nw-continue
both read execution-log.json, and every Success Criteria box would still tick
green because the directory itself existed.

The table is now titled 'What NOT to Migrate (stays in the workspace)' and says
plainly that not-migrated means not-copied, never deleted.

Four further defects the same review surfaced:

- Approval gate. Phase C listed every remaining file, showed that list for
  approval, then deleted a narrower set including .nwave/des/deliver-session.json
  -- a path outside docs/feature/ that could never appear in the listing. The
  step now lists exactly the deletion candidates by path, so the approved set and
  the deleted set are the same set.
- Idempotency. Preserving the workspace makes finalize re-runnable, and the
  'project directory not found' error was the de facto guard against a second
  run. This change removed its effect, so a second run would write a conflicting
  evolution doc and overwrite permanent copies edited since the first. Phase A
  now refuses when an evolution doc for the feature already exists.
- Corrupt-input handling. The completeness gate parses execution-log.json but
  the error table had no row for it being missing, unparseable or empty -- an
  empty log satisfies 'every step is DONE' vacuously. Three BLOCK rows added.
- Unbounded deletion. 'and any temp files' is now 'delete exactly the approved
  paths', with an explicit instruction to leave anything that looks temporary
  but was not approved.

The guard's patterns were verified too narrow: the review showed none of them
matched "On approval: delete docs/feature/{id}/", "Deleted: docs/feature/x" or
a quoted rm -rf. Patterns are broadened to destructive-verb-near-path, plus the
'Why discard'/'disposable after' phrasings that made this ambiguity survivable.
Because a line may legitimately say 'removes session markers only -- docs/feature
is retained', lines carrying an explicit retention marker are exempt; that limit
is documented in the test.

Guard now fires on 11 lines of the base file, up from 7, and is clean at HEAD.

Issue: nWave-ai#83
@tburny
tburny force-pushed the fix/83-nw-finalize-workspace-contradiction branch from 3f0d8f3 to 69d066e Compare August 8, 2026 01:23
@nwaveai

nwaveai commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

⚠️ Automatic mirror failed due to a rebase conflict.

This change does not apply cleanly onto the matching private base.
A maintainer will review and either resolve the conflict manually
or request a rebase against the latest base.

No action is required from you right now. We'll comment again once
the mirror is in place or if we need input.

1 similar comment
@nwaveai

nwaveai commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

⚠️ Automatic mirror failed due to a rebase conflict.

This change does not apply cleanly onto the matching private base.
A maintainer will review and either resolve the conflict manually
or request a rebase against the latest base.

No action is required from you right now. We'll comment again once
the mirror is in place or if we need input.

@tburny
tburny force-pushed the fix/83-nw-finalize-workspace-contradiction branch from 69d066e to 89d08cc Compare August 8, 2026 01:39
@nwaveai

nwaveai commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

⚠️ Automatic mirror failed due to a rebase conflict.

This change does not apply cleanly onto the matching private base.
A maintainer will review and either resolve the conflict manually
or request a rebase against the latest base.

No action is required from you right now. We'll comment again once
the mirror is in place or if we need input.

The nw-finalize frontmatter description was changed on this branch to say
the feature workspace is preserved. Copies of the old wording ("cleans up
the temporary workspace") survived downstream and contradicted it:

- nWave/framework-catalog.yaml (hand-maintained registry)
- docs/reference/skills/nw-finalize.md
- docs/reference/skills/index.md
- docs/reference/commands/index.md

The docs/reference/ pages are GENERATED by scripts/docgen.py and were
hand-edited here rather than regenerated. A full `poe docgen` run inside a
git worktree rewrites the "Source" GitHub URL on all 190 pages using the
checkout directory name, which would corrupt every page with the worktree
path. The three edited lines were verified byte-identical to docgen output
rendered to a scratch directory.

plugins/nw/commands/finalize.md also carries the old wording. It is
release-pipeline-generated and on the mirror denylist, so it is left alone.

Also collapse the duplicated wave-status-matrix rationale in SKILL.md — it
was stated in both the Overview and Phase C step 3. The Overview now states
retention only and points at Phase C, where the action happens.

Also make both regression guards in the issue-83 test consistent: the
preservation test asserted path.is_file() before reading, its removal-scan
sibling did not, so a renamed asset gave a bare FileNotFoundError in one
and an actionable message in the other.

Issue: nWave-ai#83
@tburny

tburny commented Aug 8, 2026

Copy link
Copy Markdown
Author

Pushed fbb8ef1, closing out the remaining review findings.

Downstream copies aligned. The corrected frontmatter description had stale copies in nWave/framework-catalog.yaml, docs/reference/skills/nw-finalize.md, and the two reference index pages, which now contradicted it — the same drift class as the original bug, one level up.

A caution about docs/reference/, worth knowing independently of this PR. Those pages are generated by scripts/docgen.py, so the obvious move is uv run poe docgen. Doing that from a git worktree corrupts all 190 pages: docgen derives each page's **Source:** GitHub URL from the checkout directory name, so in a worktree named after the branch every page gets blob/main/nWave/fix-83-nw-finalize-workspace-contradiction/…. The regen was reverted; only the three description lines were hand-edited, and they were then verified byte-identical to docgen output rendered into a scratch directory. If you would rather these pages be regenerated on your side, they are the only generated files in this PR.

nWave/framework-catalog.yaml is not generated — nothing writes it; docgen and the release scripts only read it — so hand-editing is correct there.

plugins/nw/commands/finalize.md is deliberately untouched and still carries the pre-fix wording, since plugins/** is build output and on the mirror denylist. It needs a maintainer-side regen or edit.

Also collapsed the wave-matrix rationale that this PR had duplicated across the Overview and Phase C, and made the two guard tests consistent about checking is_file() before reading.

One known limitation: the guard matches the rationale by exact phrase, so a future rewording of Phase C step 3 will trip it. Documented in the test rather than loosened, since loosening it would weaken the thing it guards.

@nwaveai

nwaveai commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

⚠️ Automatic mirror failed due to a rebase conflict.

This change does not apply cleanly onto the matching private base.
A maintainer will review and either resolve the conflict manually
or request a rebase against the latest base.

No action is required from you right now. We'll comment again once
the mirror is in place or if we need input.

The earlier passes introduced "feature workspace", which does not occur anywhere
in v3.21.0 -- 0 occurrences across skills, tasks, agents, guides and code. It was
inherited by copying nWave-experimental's already-resolved description, where the
term is well established (10 occurrences in skills, 2 in tasks). That is the
4.0.0 atdd-pure track, a different line with its own vocabulary decisions, so
importing the term backwards makes this file the only place in v3 that uses it.

Only the QUALIFIER was ever wrong. The noun is native here: nw-finalize already
says "Scan workspace", "Preserve workspace", "Commit workspace" and bare
"workspace" at base. What contradicted preservation was "temporary" (4
occurrences in the skill, 4 in the task asset) -- a directory that is retained is
not temporary.

So the qualifier is dropped rather than replaced. The file now says "the
workspace", which is what the surrounding v3 prose already said.

The description propagates into nWave/framework-catalog.yaml and three generated
docs/reference pages, so those move with it; leaving them behind would reinstate
exactly the description drift this PR exists to remove. docgen was not run --
it derives page Source URLs from the checkout directory name and corrupts all
190 pages when run from a worktree -- so the four lines were hand-edited.

For the record, v3's vocabulary for docs/feature/{feature-id}/ is genuinely
fragmented: "feature dir" (4 skills / 1 task / 1 agent / 6 guides / 9 code),
"project directory" (3/3/0/4/8), "feature directory" (1/0/0/4/7), "feature
folder" (4/0/0/0/0). "project directory" is not available as a synonym: it is
the --project-dir CLI flag, and it denotes the deliver subdirectory
(docs/feature/<id>/deliver), not the feature root. Unifying those is out of
scope here.

Issue: nWave-ai#83
@tburny

tburny commented Aug 8, 2026

Copy link
Copy Markdown
Author

Pushed 322133b — vocabulary correction.

Earlier passes used "feature workspace", which has 0 occurrences in v3.21.0. I inherited it by copying nWave-experimental's description; that is the 4.0.0 track, so importing it backwards would make this the only v3 file using it.

Only the qualifier was wrong. "workspace" is native here ("Scan workspace", "Preserve workspace" at base); "temporary" is what contradicted preservation. So the qualifier is dropped, not swapped.

The description propagates into framework-catalog.yaml and three generated reference pages, so those moved with it. docgen was not run — it corrupts all 190 pages from a worktree — so those four lines were hand-edited.

Guard green (4 passed).

@nwaveai

nwaveai commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

⚠️ Automatic mirror failed due to a rebase conflict.

This change does not apply cleanly onto the matching private base.
A maintainer will review and either resolve the conflict manually
or request a rebase against the latest base.

No action is required from you right now. We'll comment again once
the mirror is in place or if we need input.

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.

nw-finalize contradicts itself on removing the workspace

2 participants