Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
4b19fb7
chore(fix-milestone-status-write-once-dead-field-no-close): record in…
Aug 25, 2026
0872902
docs(fix-milestone-status-write-once-dead-field-no-close): intent
Aug 25, 2026
8282a4e
docs(fix-milestone-status-write-once-dead-field-no-close): complete i…
Aug 25, 2026
9c2ef3c
chore(fix-milestone-status-write-once-dead-field-no-close): record in…
Aug 25, 2026
debc77f
docs(fix-milestone-status-write-once-dead-field-no-close): stories
Aug 25, 2026
ec54489
docs(fix-milestone-status-write-once-dead-field-no-close): complete s…
Aug 25, 2026
961ac2b
chore(fix-milestone-status-write-once-dead-field-no-close): record in…
Aug 25, 2026
4107382
docs(fix-milestone-status-write-once-dead-field-no-close): spec
Aug 25, 2026
9fc745b
docs(fix-milestone-status-write-once-dead-field-no-close): complete spec
Aug 25, 2026
0110357
chore(fix-milestone-status-write-once-dead-field-no-close): record in…
Aug 25, 2026
c573971
docs(fix-milestone-status-write-once-dead-field-no-close): synthesize…
Aug 26, 2026
e92d763
docs(fix-milestone-status-write-once-dead-field-no-close): complete r…
Aug 26, 2026
1a0037d
chore(fix-milestone-status-write-once-dead-field-no-close): record in…
Aug 26, 2026
5cd0173
docs(fix-milestone-status-write-once-dead-field-no-close): design
Aug 26, 2026
2bf51d6
docs(fix-milestone-status-write-once-dead-field-no-close): complete d…
Aug 26, 2026
1045332
chore(fix-milestone-status-write-once-dead-field-no-close): record in…
Aug 26, 2026
32f3d11
docs(fix-milestone-status-write-once-dead-field-no-close): tasks
Aug 26, 2026
c94cf9e
docs(fix-milestone-status-write-once-dead-field-no-close): complete t…
Aug 26, 2026
345ea46
chore(fix-milestone-status-write-once-dead-field-no-close): record in…
Aug 26, 2026
3864bad
feat(milestones): schema abandoned status + validated store update
Aug 26, 2026
959e280
feat(milestones): rank comparator + shared MILESTONE_MARKERS
Aug 26, 2026
5a4e340
feat(cli): milestone close and update verbs
Aug 26, 2026
e0063d7
feat(fix-milestone-status-write-once-dead-field-no-close): status ren…
Aug 26, 2026
6429c7b
feat(fix-milestone-status-write-once-dead-field-no-close): progress r…
Aug 26, 2026
df57b46
feat(skills): metta-backlog milestone close/update branches
Aug 26, 2026
8541e2f
feat(guard): authorize milestone close/update via metta-backlog scope
Aug 26, 2026
5158e57
docs(fix-milestone-status-write-once-dead-field-no-close): implementa…
Aug 26, 2026
e8d5a65
docs(fix-milestone-status-write-once-dead-field-no-close): complete i…
Aug 26, 2026
f8ab5d1
docs(fix-milestone-status-write-once-dead-field-no-close): merged review
Aug 26, 2026
26e0703
test(cli): pin milestone show abandoned status scenario
Aug 26, 2026
4a9b243
test(guard): update metta-backlog scope pins for milestone close/update
Aug 26, 2026
109b6fc
docs(fix-milestone-status-write-once-dead-field-no-close): verificati…
Aug 26, 2026
1ddae31
docs(fix-milestone-status-write-once-dead-field-no-close): complete v…
Aug 26, 2026
6c501b1
chore(fix-milestone-status-write-once-dead-field-no-close): archive a…
Aug 26, 2026
3b4012d
docs(fix-milestone-status-write-once-dead-field-no-close): regenerate…
Aug 26, 2026
b989e58
docs(fix-milestone-status-write-once-dead-field-no-close): UAT run re…
Aug 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .claude/hooks/metta-guard-bash.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,10 @@ const BLOCKED_SUBCOMMANDS = new Set([
const BLOCKED_TWO_WORD = new Map([
['backlog', new Set(['add', 'done', 'promote', 'migrate'])],
['changes', new Set(['abandon'])],
// `milestone create` mutates state (writes spec/milestones/<slug>.md) — Tier-2 scope
// key 'milestone:create', minted only by the metta-backlog skill.
['milestone', new Set(['create'])],
// `milestone create`/`close`/`update` mutate state (write spec/milestones/<slug>.md) —
// Tier-2 scope keys 'milestone:create', 'milestone:close', 'milestone:update', minted
// only by the metta-backlog skill.
['milestone', new Set(['create', 'close', 'update'])],
['roadmap', new Set(['add', 'reorder', 'next', 'remove'])],
// `release cut` mutates state (version bump, tag, release commit) — Tier-2 scope
// key 'release:cut', minted only by the metta-release skill.
Expand Down
4 changes: 3 additions & 1 deletion .claude/hooks/metta-session-mint.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ const SKILL_SCOPES = {
'metta-refresh': ['refresh'],
'metta-import': ['import'],
'metta-init': ['init', 'refresh'],
'metta-backlog': ['backlog:add', 'backlog:done', 'backlog:promote', 'backlog:migrate', 'milestone:create'],
// Milestone mutation scopes mint only via metta-backlog; future ship/finalize-driven
// closers need their own scope extension here rather than reusing this one.
'metta-backlog': ['backlog:add', 'backlog:done', 'backlog:promote', 'backlog:migrate', 'milestone:create', 'milestone:close', 'milestone:update'],
'metta-fix-gap': ['fix-gap', 'complete', 'finalize'],
'metta-roadmap': ['roadmap:add', 'roadmap:reorder', 'roadmap:next', 'roadmap:remove'],
'metta-release': ['release:cut'],
Expand Down
4 changes: 3 additions & 1 deletion .claude/skills/metta-backlog/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,12 @@ Drive the `metta backlog` CLI. The backlog is a view over `spec/issues/` frontma
- **promote** → run `metta backlog list --json`, parse `.backlog[].slug` from the output, present the slugs via `AskUserQuestion`, then run `metta backlog promote <chosen-slug>`. The CLI prints the `/metta-fix-issues <slug>` handoff command; echo that back to the user.
- **done** → run `metta backlog list --json`, parse `.backlog[].slug` from the output to build the list of available slugs. Present the slugs via `AskUserQuestion`. Then ask, via `AskUserQuestion`, for an optional change name to record as `--change <name>` (free-form; if the user skips or leaves blank, omit the flag). Run `metta backlog done <slug>` or `metta backlog done <slug> --change <changeName>` as appropriate. Echo the archived path (`spec/issues/resolved/<slug>.md`) printed by the CLI back to the user.
- **migrate** → run `metta backlog migrate --json`. Report the converted counts (`converted.active`, `converted.done`), any `collisions` (each with `slug`, `legacy_path`, `existing_path` — collisions are reported, never overwritten), and the `archived_to` location. If `nothing_to_do` is true, tell the user there were no legacy `spec/backlog/` files to migrate.
- **milestone** → ask via `AskUserQuestion` which milestone action to take: `create | list | show`.
- **milestone** → ask via `AskUserQuestion` which milestone action to take: `create | list | show | close | update`.
- **create** → ask for `slug`, `name`, and optional `target` (date) and `description`. First run `metta milestone list --json` (allow-listed; lets the session-credential mint hook complete a prior Bash cycle — output can be ignored), then run `metta milestone create <slug> --name "<name>"` plus `--target <date>` / `--description <text>` when supplied.
- **list** → run `metta milestone list` and report the rollups (open/resolved counts and percent per milestone) plus any warnings.
- **show** → run `metta milestone list --json`, present the milestone slugs via `AskUserQuestion`, then run `metta milestone show <slug>` and report the per-issue breakdown.
- **close** → run `metta milestone list --json` (allow-listed; lets the session-credential mint hook complete a prior Bash cycle), present the open-milestone slugs via `AskUserQuestion`, then ask via `AskUserQuestion` whether the milestone was achieved (`closed`) or dropped (`abandoned`). Run `metta milestone close <slug>`, appending `--abandoned` when the user chose dropped. Echo the resulting status and the commit line printed by the CLI.
- **update** → run `metta milestone list --json`, present the milestone slugs via `AskUserQuestion`, then collect which fields to change via `AskUserQuestion`: `name` / `target` / clear target / `description` / `status`. Run `metta milestone update <slug>` with only the flags matching the chosen fields — `--name "<name>"`, `--target <date>`, `--clear-target`, `--description "<text>"`, `--status <open|closed|abandoned>`. `--status open` is the reopen path for a closed or abandoned milestone. Echo the resulting fields and the commit line printed by the CLI.

3. Echo the slug / path / next command printed by the CLI.

Expand Down
43 changes: 43 additions & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1088,6 +1088,8 @@ Scenarios:
- Milestone created with defaults
- Creating a duplicate milestone is refused
- Invalid milestone status is rejected
- Abandoned status validates through the schema
- Pre-existing open and closed files are unaffected

### Milestone and priority assignment via issue frontmatter

Expand Down Expand Up @@ -1186,6 +1188,47 @@ Scenarios:
- Tasks --json rendering routes through the shared helper
- Helper is idempotent and precise at range boundaries

### Milestone store update applies validated patches

Scenarios:
- Status patch preserves untouched fields
- Target is cleared from frontmatter
- Invalid patch is rejected and the file is untouched
- Updating a missing milestone fails without side effects

### Milestone close CLI verb transitions to a terminal state

Scenarios:
- Open milestone is closed and auto-committed
- Abandoned flag writes the abandoned state
- Closing an already-terminal milestone is a conflict
- Closing a missing milestone reports not found
- Main-branch guard applies to close

### Milestone update CLI verb edits mutable fields

Scenarios:
- Description is replaced without touching other fields
- Clear-target removes the field
- A mistakenly closed milestone is reopened
- Invalid field value fails validation and leaves the file untouched
- Updating a missing milestone reports not found
- No field options is an error

### Renderers and rollups handle the abandoned state

Scenarios:
- List sorts terminal states after open with distinct markers
- Show reports the abandoned state accurately
- Status and progress render abandoned without crashing
- Open and closed output stays byte-compatible

### Guard authorization for milestone close and update

Scenarios:
- Authorized skill context may invoke the new verbs
- Unauthorized context is blocked identically

## orchestration-guard

### Inline Command-Text Tokens Never Authorize a Blocked Subcommand
Expand Down
2 changes: 1 addition & 1 deletion docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
13 requirements

### issue-logging
29 requirements
34 requirements

### orchestration-guard
36 requirements
Expand Down
42 changes: 41 additions & 1 deletion docs/changelog.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,50 @@
<!-- Generated by Metta — do not edit directly -->
<!-- Sources: spec/archive/2026-08-23-enforce-agent-executed-uat-run-results-attached-pr-before/summary.md...and 146 more -->
<!-- Sources: spec/archive/2026-08-26-fix-milestone-status-write-once-dead-field-no-close/summary.md...and 147 more -->
<!-- Run `metta docs generate` to regenerate -->
# Changelog

## Unreleased

### 2026-08-26 — fix-milestone-status-write-once-dead-field-no-close

# Summary: fix-milestone-status-write-once-dead-field-no-close

## What changed

Milestones gained a validated write-back lifecycle. Previously `metta milestone create` wrote `status: open` permanently — no CLI path could close a milestone, edit its body, or change its target (issue `milestone-status-is-a-write-once-dead-field-with-no-close-or`).

## Implementation (7 tasks, 4 batches)

- **Schema** (`src/schemas/milestone-frontmatter.ts`): status enum extended to `open | closed | abandoned`; default and `.strict()` unchanged. Commit `3864badc5`.
- **Store** (`src/milestones/milestones-store.ts`): exported `MilestonePatch` and `update(slug, patch)` — read → patch → full-frontmatter Zod re-validation **before any I/O** → write; failing patches provably leave the file byte-identical; `clearTarget` removes the key entirely. Commit `3864badc5`.
- **Rollup** (`src/milestones/milestone-rollup.ts`): two-state sort replaced with a rank comparator (open first, terminal group slug-ascending — behavior-identical for open/closed-only inputs); exported shared `MILESTONE_MARKERS` (`▸`/`✓`/`✗`). Commit `959e2805d`.
- **CLI** (`src/cli/commands/milestone.ts`): new `milestone close <slug> [--abandoned]` (conflict pre-check, `chore: close milestone <slug>` auto-commit) and `milestone update <slug>` (`--name/--target/--clear-target/--description/--status`, Commander `conflicts`/`choices`, `chore: update milestone <slug>`); shared `commitMilestones` helper extracted from `create`; all failures exit 4 with typed JSON envelopes (`branch_guard`/`not_found`/`milestone_conflict`/`milestone_error`). Commit `5a4e3406d`.
- **Renderers** (`src/cli/commands/status.ts`, `progress.ts`): abandoned milestones render red `✗` via `MILESTONE_MARKERS`; open/closed output byte-identical to pre-change. Commits `e0063d78b`, `6429c7b88`.
- **Guard/mint hooks** (deployed + `src/templates/hooks/` mirrors, byte-identical): `milestone close`/`update` join the Tier-2 blocked set; `SKILL_SCOPES['metta-backlog']` gains `milestone:close`/`milestone:update`; 7 new guard test cases close the previously-empty milestone coverage gap. Commit `8541e2f2b`.
- **Skill** (`.claude/skills/metta-backlog/SKILL.md` + template mirror): milestone actions now `create | list | show | close | update` with dispatch branches for both new verbs. Commit `df57b4692`.

## Verification during implementation

Every task ran `npx tsc --noEmit` (clean) and its focused vitest suites (all green), including 309 guard-hook tests, byte-identity pins for hook mirrors, byte-compat ordering pins for the rollup sort, and byte-identical-file assertions for all failure paths.

## Notable deviations

- Task 1.1 pre-widened `MilestoneRollup.status` (one line) because the enum extension broke compilation — work the design assigned to the rollup component anyway.
- Task 3.3's test landed in `tests/cli-status.test.ts` (the file actually covering progress milestone rendering) rather than the plan's speculative `progress-secondary-line.test.ts`; a brief mid-batch file overlap between tasks 3.2/3.3 was reconciled with no lost work.

## Risks

- `status: abandoned` files fail validation under older metta builds (accepted one-way door, documented in intent).
- `update` re-serializes frontmatter via YAML.stringify — hand-edited key order/comments are normalized (accepted; hand-editing is the workflow this change eliminates).

## Verification (3 parallel verifiers, 2 iterations)

- **Tests**: iteration 1 found 3 failures — pre-existing scope-pin suites (`tests/metta-session-mint.test.ts`, `tests/cli-metta-guard-bash-integration.test.ts`) still expected the old `metta-backlog` scope list without `milestone:close`/`milestone:update`. Expectations updated (commit `4a9b24382`); iteration 2: **135 files, 2802 passed, 2 skipped, 0 failed**.
- **Typecheck/lint/build**: `npx tsc --noEmit` clean; `npm run lint` clean; `npm run build` (tsc + copy-templates + emit-build-stamp) succeeded.
- **Spec coverage**: all 22 scenarios across the 6 spec.md requirements have cited passing tests (per-scenario evidence table produced by the verifier). One noted caveat: R5's byte-compatibility scenario is verified via unit-level pins (legacy-comparator reproduction, marker map, envelope shapes) rather than a literal pre/post output byte-diff — the guarantee is derived, since pre-change binaries are not available in-tree.
- Review follow-up: the quality reviewer's single warning (no direct CLI test for `show` on an abandoned milestone) was closed by commit `26e0703f0`.


### 2026-08-23 — enforce-agent-executed-uat-run-results-attached-pr-before

# Summary: enforce-agent-executed-uat-run-results-attached-pr-before
Expand Down
Loading
Loading