docs: design spec for milestone-close gate (cycle ≠ milestone)
Separate `cycle` (a pipeline-graph round) from `milestone` (a Gitea tracker container that closes only when complete AND functional). `audit` proves drift-clean, never functional, so no audit signal — and no `/boss` done-state — may close a milestone. Introduces a milestone-close gate (complete ∧ functional) defined once in docs/pipeline.md, and a milestone-wide fieldtest as a carrier-scope variant of the existing fieldtest skill. The milestone fieldtest runs curated end-to-end scenarios derived top-down from the milestone's promise — proving the shipped implementation delivers what the milestone as a whole promised, not a mechanical union of per-cycle axes. The actual tracker close stays a manual act; this spec only defines when a milestone is closeable. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,241 @@
|
||||
# Milestone-close gate: separating `cycle` from `milestone`
|
||||
|
||||
**Date:** 2026-05-31
|
||||
**Status:** approved (design)
|
||||
**Topic:** make `audit` unable to imply a milestone is finished;
|
||||
add a milestone-wide fieldtest as the closing gate.
|
||||
|
||||
## Problem
|
||||
|
||||
`audit` runs at cycle close and emits a "clean" signal whose
|
||||
commit body reads `cycle <X> tidy (clean)`. `audit` only proves
|
||||
*drift-clean* — the code matches the design ledger. It is blind
|
||||
to whether the work is *functional* from a downstream
|
||||
consumer's point of view; that is exactly what `fieldtest`
|
||||
measures, and `fieldtest` runs *after* `audit`.
|
||||
|
||||
Two defects follow:
|
||||
|
||||
1. **Conflation of `cycle` and `milestone`.** `README.md`
|
||||
("AILang calls a cycle a *milestone*") and
|
||||
`templates/project-profile.yml:41` (`cycle: # what a
|
||||
top-level work unit is called`) treat a cycle as the
|
||||
top-level work unit. It is not. A **milestone** is a tracker
|
||||
(Gitea) container that closes only when the work is
|
||||
*completely implemented and functional*. A **cycle** is one
|
||||
round in the pipeline graph — a step in the loop. A milestone
|
||||
spans potentially many cycles.
|
||||
|
||||
2. **An audit-clean signal can leak into a milestone close.**
|
||||
Because `audit`'s "clean" reads like "done", an orchestrator
|
||||
can be led to close a Gitea milestone on the strength of an
|
||||
audit, before any fieldtest has demonstrated the work is
|
||||
functional. No skill closes a milestone today, so this is a
|
||||
conceptual hazard rather than a code path — but the wording
|
||||
actively invites the mistake.
|
||||
|
||||
The fix is **not** to swap the `audit → fieldtest` order:
|
||||
`fieldtest` needs the drift-clean state to test against, so
|
||||
`audit` must stay first. The fix is to **decouple the gate** —
|
||||
`audit`'s "clean" never closes anything, and a separate,
|
||||
explicitly defined milestone-close gate requires both
|
||||
completeness and functional validation.
|
||||
|
||||
## Conceptual model
|
||||
|
||||
### Two distinct axes
|
||||
|
||||
- **Cycle** — one round in the pipeline graph
|
||||
(`brainstorm → planner → implement → audit → [per-cycle
|
||||
fieldtest]`). A cycle close is an internal loop step. Many
|
||||
cycles per milestone.
|
||||
- **Milestone** — a tracker (Gitea) container. Closes **only**
|
||||
when *complete* (every cycle drift-clean / ratified, no open
|
||||
iterations) **and** *functional* (milestone fieldtest green).
|
||||
|
||||
### Three disentangled "clean" terms
|
||||
|
||||
| Term | Owner | Means | Closes a milestone? |
|
||||
|------|-------|-------|---------------------|
|
||||
| `drift-clean` | `audit` | code matches the ledger | never |
|
||||
| cycle functionally validated | per-cycle `fieldtest` | cycle-local surface usable | never |
|
||||
| milestone closeable | milestone-close gate | complete ∧ functional | yes (manual act) |
|
||||
|
||||
### Two fieldtest scopes
|
||||
|
||||
The milestone fieldtest is a **scope variant** of the existing
|
||||
`fieldtest` skill — same `fieldtester` agent, a different
|
||||
carrier. No new skill; keeps the surface DRY.
|
||||
|
||||
- **Per-cycle fieldtest** (existing, unchanged): carrier carries
|
||||
`cycle_id` / `cycle_scope`; dispatched after a
|
||||
surface-touching cycle's audit; catches cycle-local friction
|
||||
early; optional / orchestrator-dispatched as today.
|
||||
- **Milestone fieldtest** (new variant): carrier carries
|
||||
`milestone_id` and `milestone_promise` — the milestone's
|
||||
stated goal / acceptance in the tracker (and its spec, if one
|
||||
exists). The fieldtester derives a small set of **curated
|
||||
end-to-end scenarios** that, run as a downstream consumer,
|
||||
prove the shipped implementation delivers **what the milestone
|
||||
as a whole promised**. Scenarios are chosen *top-down from the
|
||||
milestone's promise*, not assembled bottom-up as the
|
||||
mechanical union of per-cycle axes — the per-cycle fieldtests
|
||||
already cover axis-local surface; the milestone fieldtest's
|
||||
job is to demonstrate the promise end to end. Its green status
|
||||
roll-up is the closing gate.
|
||||
|
||||
## The milestone-close gate (authoritative)
|
||||
|
||||
A milestone may be closed in the tracker only when **both** legs
|
||||
hold:
|
||||
|
||||
1. **Complete** — every cycle filed under the milestone is
|
||||
`audit` drift-clean (or its drift explicitly ratified), and
|
||||
the milestone container has no open iterations / issues
|
||||
remaining.
|
||||
2. **Functional** — the milestone fieldtest has run its curated
|
||||
end-to-end scenarios against the milestone's promise and its
|
||||
status roll-up is `clean`: every scenario demonstrably
|
||||
delivers what the milestone promised; no open `bug` findings;
|
||||
`friction` / `spec_gap` findings either resolved or
|
||||
explicitly ratified into the design ledger.
|
||||
|
||||
No `audit` result, and no `/boss` done-state, closes a milestone
|
||||
on its own. `/boss` done-state means "this `/boss` run has
|
||||
nothing left to dispatch" — it is **not** a milestone close.
|
||||
|
||||
The actual close (`tea milestone close`, or the tracker
|
||||
equivalent) stays a **deliberate human / orchestrator act**,
|
||||
performed only after the gate reads green. This design defines
|
||||
*when* a milestone is closeable; it does **not** mechanise the
|
||||
close. (`issue/SKILL.md` is out of scope here.)
|
||||
|
||||
### Edge cases
|
||||
|
||||
- **Non-surface milestone.** A milestone whose entire scope is
|
||||
internal (refactor, infra, docs) touches no user-visible
|
||||
surface. The milestone fieldtest is then *not applicable*
|
||||
(same skip logic as the per-cycle fieldtest, lifted to
|
||||
milestone level). The "functional" leg is satisfied vacuously;
|
||||
the "complete" leg (audit-clean across cycles) suffices.
|
||||
- **Single-cycle milestone.** Nothing special: the milestone
|
||||
fieldtest still runs (if surface-touching), proving the
|
||||
milestone's promise end to end even though that promise
|
||||
happened to ship in a single cycle. The per-cycle and
|
||||
milestone fieldtests may coincide in surface but remain
|
||||
conceptually distinct gates — axis-local validation versus
|
||||
promise-level proof.
|
||||
- **Ratified drift.** A cycle whose drift was ratified
|
||||
(`--update-baseline` + ratify paragraph) counts as satisfying
|
||||
the "complete" leg for that cycle — ratification is the
|
||||
documented form of "complete".
|
||||
|
||||
## Per-file changes
|
||||
|
||||
Single-source discipline (consistent with the recent
|
||||
carrier/handoff single-sourcing): the gate is **defined once**
|
||||
in `docs/pipeline.md`; every other file **references** it rather
|
||||
than restating it, so the files cannot drift.
|
||||
|
||||
### `docs/pipeline.md` (single source)
|
||||
|
||||
- ASCII graph: rename `audit --(clean)` to `audit
|
||||
--(drift-clean)`; annotate `[cycle close]` explicitly as a
|
||||
loop step, not a milestone end.
|
||||
- New subsection **"Cycle vs. milestone"** — defines the two
|
||||
axes authoritatively (a milestone spans many cycles; a cycle
|
||||
is one loop round).
|
||||
- New subsection **"Milestone-close gate"** — the authoritative
|
||||
gate definition above (complete ∧ functional), names the
|
||||
milestone fieldtest as a mandatory precondition for
|
||||
surface-touching milestones, and states plainly that no
|
||||
`audit` and no `/boss` done-state closes a milestone.
|
||||
|
||||
### `audit/SKILL.md`
|
||||
|
||||
- Step 2 table: "Audit can close" → drift-gate wording. `audit`
|
||||
clears the *cycle drift check*; it does not close the cycle or
|
||||
the milestone.
|
||||
- Close-commit wording (`cycle <X> tidy (clean)`): kept, but
|
||||
annotated explicitly as *drift-clean, not a milestone signal*.
|
||||
- New **Red Flag**: "Declaring a milestone closeable on the
|
||||
strength of an audit-clean, while the milestone fieldtest has
|
||||
not run green." Cross-reference the gate in `docs/pipeline.md`.
|
||||
- Overview: clarify `audit` ↔ cycle close (never milestone).
|
||||
|
||||
### `fieldtest/SKILL.md`
|
||||
|
||||
- Document the second trigger variant — the **milestone
|
||||
fieldtest** (milestone-wide, end-to-end): carrier carries
|
||||
`milestone_id` and `milestone_promise`; the fieldtester
|
||||
derives curated end-to-end scenarios from the milestone's
|
||||
promise (top-down, not a union of per-cycle axes) and proves
|
||||
the shipped implementation delivers it; its green status
|
||||
roll-up is the closing gate. Reference the gate in
|
||||
`docs/pipeline.md` rather than restating it.
|
||||
- The "audit first" precondition stays, for both variants.
|
||||
- The milestone-fieldtest skip logic mirrors the per-cycle one,
|
||||
lifted to milestone level (non-surface milestone → exempt).
|
||||
|
||||
### `boss/SKILL.md`
|
||||
|
||||
- Done-state: reference the `docs/pipeline.md` gate instead of
|
||||
restating the formula. Clarify that a `/boss` done-state is
|
||||
**not** an automatic milestone close — the milestone close
|
||||
needs the milestone fieldtest green and is a manual act.
|
||||
|
||||
### `README.md`
|
||||
|
||||
- **Root fix**: correct the `cycle ≡ milestone` conflation
|
||||
(the "AILang calls a cycle a *milestone*" lines). Cycle = a
|
||||
pipeline round; milestone = a tracker container spanning many
|
||||
cycles, closed only when complete ∧ functional.
|
||||
- `fieldtest` row: per-cycle fieldtest stays optional; note the
|
||||
milestone fieldtest as the mandatory gate before a
|
||||
surface-touching milestone closes.
|
||||
|
||||
### `templates/project-profile.yml`
|
||||
|
||||
- Fix the `cycle:` comment ("top-level work unit" is wrong — a
|
||||
cycle is the pipeline round).
|
||||
- Add a `milestone:` vocabulary field for the tracker container.
|
||||
- Add the milestone-fieldtest phase to the pipeline config (so a
|
||||
project can name / gate it), consistent with the existing
|
||||
`pipeline:` configuration model.
|
||||
|
||||
### `docs/profile-schema.md`
|
||||
|
||||
- Same root conflation lives here: the `vocabulary` table
|
||||
(`cycle` = "top-level work unit", with `milestone` as an
|
||||
example value) and the minimal-profile example (`cycle:
|
||||
milestone`). Correct both — document `cycle` as the pipeline
|
||||
round and add the `milestone` vocabulary field as the tracker
|
||||
container.
|
||||
- Document the milestone-fieldtest phase in the `pipeline`
|
||||
section alongside the existing `fieldtest` phase.
|
||||
|
||||
## Scope boundaries
|
||||
|
||||
**In scope:** the gate definition and the wording/decoupling
|
||||
across `docs/pipeline.md`, `audit`, `fieldtest`, `boss`,
|
||||
`README.md`, `templates/project-profile.yml`; the milestone
|
||||
fieldtest as a carrier-scope variant of the existing skill.
|
||||
|
||||
**Out of scope:** mechanising the tracker close (no
|
||||
`issue/SKILL.md` change, no automated `tea milestone close`);
|
||||
reordering `audit → fieldtest`; any change to the per-cycle
|
||||
fieldtest's existing behaviour beyond naming it "per-cycle".
|
||||
|
||||
## Verification
|
||||
|
||||
- Grep sweep: no remaining file equates `cycle` with
|
||||
`milestone`; the only authoritative gate definition lives in
|
||||
`docs/pipeline.md`, and `audit` / `fieldtest` / `boss`
|
||||
reference it without restating it.
|
||||
- `audit/SKILL.md` carries the new Red Flag and drift-gate
|
||||
wording; its close-commit example is annotated as drift-clean.
|
||||
- `fieldtest/SKILL.md` documents both carrier scopes and the
|
||||
milestone-level skip rule.
|
||||
- `templates/project-profile.yml` parses (YAML) with the new
|
||||
`milestone:` vocabulary field and the milestone-fieldtest
|
||||
phase, and `docs/profile-schema.md` stays consistent with it.
|
||||
Reference in New Issue
Block a user