feat(voyage): S6 — v5.5 brief framing enforcement (brief_version 2.2)
Implements the CLAUDE.md cross-cutting invariant "brief framing must match operator intent" as a controlled brief_version 2.1->2.2 bump (operator option A1). Three defense layers, version-gated at >=2.2 so existing 2.0/2.1 briefs stay valid (forward + backward compatible), mirroring the phase_signals >=2.1 gate: - L1 framing: enum field (preserve|refine|replace|new-direction). Enum-checked on any version when present (BRIEF_INVALID_FRAMING); missing at >=2.2 -> BRIEF_MISSING_FRAMING. /trekbrief Phase 2.5 collects it BEFORE any brief prose (non-skippable, even in --quick). - L2 memory alignment: new brief-reviewer dimension 6 comparing brief Intent/Goal + framing against operator memory for explicit contradictions; degrades to score 5 (N/A) when no memory context is supplied. Wired into Phase 4e gate (memory_alignment.score >= 4). - L3 obligatory ## TL;DR (<=5 content lines) at >=2.2; soft cap -> BRIEF_TLDR_TOO_LONG warning. trekreview briefs are exempt from the framing/TL;DR gate. Handover 1 PUBLIC CONTRACT doc, README "What's new", and the CLAUDE.md invariant + agents table (brief-reviewer 5->6 dimensions) updated to 2.2 (schema axis only; plugin version badge + CHANGELOG remain S10). Iron Law followed: validator tests red->green first. Tests 586 -> 606 (+20, 604 pass / 2 skip). claude plugin validate passes (pre-existing CLAUDE.md root-context warning unchanged). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LqBYc8Ltrk7LipyJmGxXiB
This commit is contained in:
parent
fa23b16443
commit
736ae55d66
10 changed files with 409 additions and 31 deletions
|
|
@ -99,6 +99,41 @@ If the directory already exists and is non-empty, warn and ask:
|
|||
Use `AskUserQuestion` with three options. If "pick new slug", ask for a
|
||||
new slug and restart Phase 2.
|
||||
|
||||
## Phase 2.5 — Framing declaration (v5.5)
|
||||
|
||||
Before gathering ANY brief content, the operator MUST declare how this brief
|
||||
relates to prior operator intent. This is the **first layer of the
|
||||
framing-alignment defense** (CLAUDE.md cross-cutting invariant): the premise is
|
||||
declared explicitly *before* the interview can drift, and long before `/trekplan`
|
||||
can polish a wrong premise with reasoning power.
|
||||
|
||||
**This runs BEFORE any brief prose is drafted.** The committed value is written to
|
||||
brief frontmatter as `framing: <value>` in Step 4a and is REQUIRED for
|
||||
`brief_version: "2.2"` (the validator emits `BRIEF_MISSING_FRAMING` otherwise).
|
||||
|
||||
Ask via `AskUserQuestion` — one question, four canonical options:
|
||||
|
||||
| Option | Maps to `framing:` | Meaning |
|
||||
|--------|--------------------|---------|
|
||||
| **Preserve** | `preserve` | Same intent as before; this brief continues a prior direction unchanged. |
|
||||
| **Refine** | `refine` | Same core intent, sharpened or narrowed scope. |
|
||||
| **Replace** | `replace` | Supersedes a prior brief's approach; intent re-stated from scratch. |
|
||||
| **New direction** | `new-direction` | Net-new intent; no prior brief to anchor against. |
|
||||
|
||||
Commit the answer to `state.framing` immediately.
|
||||
|
||||
**No safe default.** `framing` cannot be guessed — the danger the invariant guards
|
||||
against is exactly "delta from last is a dangerous default anchor after a rejected
|
||||
iteration." Therefore **this question is asked even in `--quick` mode** and is the
|
||||
one dialog that has no skip path. If the operator force-stops here, re-surface the
|
||||
four options once more; the brief cannot be written at `brief_version: "2.2"`
|
||||
without a committed framing value.
|
||||
|
||||
Report:
|
||||
```
|
||||
Framing: {preserve | refine | replace | new-direction}
|
||||
```
|
||||
|
||||
## Phase 3 — Completeness loop
|
||||
|
||||
Phase 3 is a **section-driven completeness loop**. Instead of a numbered
|
||||
|
|
@ -424,7 +459,15 @@ Build the brief text from Phase 3 state by filling the template:
|
|||
- **Frontmatter:** populate `task`, `slug`, `project_dir`, `research_topics`
|
||||
(count of topics), `research_status: pending`, `auto_research: false`
|
||||
(will update in Phase 5 if user opts in), `interview_turns` (total
|
||||
questions asked across Phase 3 + Phase 4), `source: interview`.
|
||||
questions asked across Phase 3 + Phase 4), `source: interview`. Set
|
||||
`brief_version: "2.2"` and `framing: <state.framing>` (committed in Phase
|
||||
2.5 — never omit; the validator blocks a 2.2 brief without it).
|
||||
- **TL;DR (v5.5, required at 2.2):** write a `## TL;DR` section (≤ 5 content
|
||||
lines) at the very top of the body, before `## Intent`. It is the
|
||||
framing-anchored one-glance summary — what the brief asks for and how it
|
||||
relates to prior intent given `framing: <state.framing>`. Drafting it FIRST
|
||||
forces the wrong-premise check before the rest of the prose is written. Keep
|
||||
it to ≤ 5 lines (the validator warns with `BRIEF_TLDR_TOO_LONG` above that).
|
||||
- **Intent:** expand the user's motivation into 3–5 sentences. Load-bearing.
|
||||
- **Goal:** concrete end state.
|
||||
- **Non-Goals:** from state, or "- None explicitly stated" bullet if empty.
|
||||
|
|
@ -446,12 +489,22 @@ final file is only written after the gate passes).
|
|||
|
||||
**Step 4c — Launch brief-reviewer**
|
||||
|
||||
**Gather memory context first (v5.5, layer 2 of the framing defense).** If the
|
||||
operator's environment exposes memory facts (e.g. an auto-memory `MEMORY.md` plus
|
||||
`feedback_*` / `project_*` topic files), collect the paths or excerpts of those
|
||||
relevant to this task. This is best-effort and environment-dependent: if no memory
|
||||
is available, pass nothing — the reviewer scores the memory-alignment dimension
|
||||
`5` (N/A) when no context is supplied.
|
||||
|
||||
Launch the `brief-reviewer` agent (foreground, blocking) with the prompt:
|
||||
|
||||
> "Review this task brief for quality: `{PROJECT_DIR}/brief.md.draft`.
|
||||
> Check completeness, consistency, testability, scope clarity, and
|
||||
> research-plan validity. Report findings, verdict, and the required
|
||||
> machine-readable JSON block."
|
||||
> Check completeness, consistency, testability, scope clarity,
|
||||
> research-plan validity, and memory alignment. Report findings, verdict, and
|
||||
> the required machine-readable JSON block.
|
||||
> Operator memory context (compare the brief's Intent/Goal and its declared
|
||||
> `framing:` value against these for EXPLICIT contradictions only):
|
||||
> {memory paths or excerpts, or "none supplied"}."
|
||||
|
||||
**Step 4d — Parse JSON scores**
|
||||
|
||||
|
|
@ -460,12 +513,13 @@ Extract per-dimension scores:
|
|||
|
||||
```
|
||||
review = {
|
||||
completeness: { score, gaps },
|
||||
consistency: { score, issues },
|
||||
testability: { score, weak_criteria },
|
||||
scope_clarity: { score, unclear_sections },
|
||||
research_plan: { score, invalid_topics },
|
||||
verdict: "PROCEED | PROCEED_WITH_RISKS | REVISE"
|
||||
completeness: { score, gaps },
|
||||
consistency: { score, issues },
|
||||
testability: { score, weak_criteria },
|
||||
scope_clarity: { score, unclear_sections },
|
||||
research_plan: { score, invalid_topics },
|
||||
memory_alignment:{ score, contradictions }, # v5.5 — layer 2
|
||||
verdict: "PROCEED | PROCEED_WITH_RISKS | REVISE"
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -484,6 +538,10 @@ The gate **passes** when all of the following are true:
|
|||
- `testability.score ≥ 4`
|
||||
- `scope_clarity.score ≥ 4`
|
||||
- `research_plan.score == 5`
|
||||
- `memory_alignment.score ≥ 4` (v5.5 — a score ≤ 3 means the reviewer found an
|
||||
EXPLICIT contradiction between the brief and operator memory; this is a
|
||||
framing-alignment blocker, not a wording nit. A `5` is also returned when no
|
||||
memory context was supplied, so this never blocks environments without memory.)
|
||||
|
||||
(Research Plan requires a perfect score because its format is checked
|
||||
mechanically: ends in `?`, `Required for plan steps` filled, scope is
|
||||
|
|
@ -499,9 +557,11 @@ stumble.)
|
|||
|
||||
**If gate fails AND iteration count < 3:**
|
||||
1. Identify the weakest dimension (lowest score; tie broken by priority:
|
||||
research_plan > testability > completeness > consistency > scope_clarity).
|
||||
research_plan > memory_alignment > testability > completeness >
|
||||
consistency > scope_clarity).
|
||||
2. Generate a targeted follow-up question from the dimension's detail
|
||||
field (gaps / issues / weak_criteria / unclear_sections / invalid_topics).
|
||||
field (gaps / issues / weak_criteria / unclear_sections / invalid_topics /
|
||||
contradictions).
|
||||
Example generators:
|
||||
- `completeness.gaps: ["Non-Goals empty, unclear if deliberate"]`
|
||||
→ "You did not specify anything out-of-scope. Is that deliberate, or
|
||||
|
|
@ -514,6 +574,10 @@ stumble.)
|
|||
→ "For research topic 'JWT': which plan steps depend on the answer?
|
||||
Give one or two concrete kinds of step (e.g., 'library selection',
|
||||
'threat model', 'migration strategy')."
|
||||
- `memory_alignment.contradictions: [{"brief_claim":"continue the REST approach","memory_fact":"team decided to move to GraphQL","file":"project_api.md"}]`
|
||||
→ "Your brief's framing says 'preserve', but memory records the team
|
||||
moved off REST to GraphQL. Is this brief intentionally reviving REST,
|
||||
or should the framing be 'replace' / 'new-direction'?"
|
||||
3. Ask via `AskUserQuestion`. Record the answer into Phase 3 state.
|
||||
4. Return to Step 4a with incremented iteration count. The reviewer sees
|
||||
an updated draft, so you MUST re-read the brief and regenerate the
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue