Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
5abedab
feat(planning): add the bundled design-principles reference
kyle-sexton Oct 19, 2026
0b822b2
feat(planning): interview asks intent and quality requirements by qua…
kyle-sexton Oct 19, 2026
6d07a13
fix(planning): name interview default stumbles and tighten two graders
kyle-sexton Oct 19, 2026
ca1d202
feat(planning): design defaults name the principles; add lifecycle an…
kyle-sexton Oct 19, 2026
723fef1
feat(planning): gate principle and reversibility threads at handoff, …
kyle-sexton Oct 19, 2026
d29d548
fix(planning): tag prototype reversibility for handoff and harden des…
kyle-sexton Oct 19, 2026
337178e
feat(planning): design defaults write a Principles thread for handoff
kyle-sexton Oct 19, 2026
012dd67
Merge remote-tracking branch 'origin/feat/8334-design-defaults-revers…
kyle-sexton Oct 19, 2026
ca6fd01
test(planning): design-handoff PASS fixtures carry the required threads
kyle-sexton Oct 19, 2026
ca0dc0e
fix(planning): lock records unstated quality prompts, and tighten the…
kyle-sexton Oct 19, 2026
a656ff9
test(planning): state the Loqate facts the lock cases halted on, and …
kyle-sexton Oct 19, 2026
1b29b13
Merge remote-tracking branch 'origin/main' into feat/8333-interview-i…
kyle-sexton Oct 19, 2026
7ea54e4
test(planning): run the NFR and consumer-standard lock cases unattended
kyle-sexton Oct 19, 2026
d9b1f77
Merge remote-tracking branch 'origin/feat/8333-interview-intent-and-q…
kyle-sexton Oct 19, 2026
565c957
Merge remote-tracking branch 'origin/main' into feat/8334-design-defa…
kyle-sexton Oct 19, 2026
1f35563
test(planning): accept correct design answers three regex graders missed
kyle-sexton Oct 19, 2026
1ef7859
Merge remote-tracking branch 'origin/main' into feat/8334-design-defa…
kyle-sexton Oct 19, 2026
a91ca72
test(planning): spell the ADR grader's require stem in full
kyle-sexton Oct 19, 2026
0f4d573
Merge remote-tracking branch 'origin/feat/8334-design-defaults-revers…
kyle-sexton Oct 19, 2026
4b1f1f0
test(planning): spell grader regex stems as whole words for typos
kyle-sexton Oct 19, 2026
aab028f
fix(planning): design-handoff states the FAIL verdict before routing …
kyle-sexton Oct 19, 2026
178e57d
test(planning): dh verdict grader accepts prose gate and check failures
kyle-sexton Oct 19, 2026
2c842e0
Merge remote-tracking branch 'origin/main' into feat/8334-design-defa…
kyle-sexton Oct 19, 2026
b4fee47
fix(planning): cite nfr-prompts by plugin-root link and state a stumb…
kyle-sexton Oct 19, 2026
0a8c060
test(planning): fail the design cases on an Edit to source files
kyle-sexton Oct 19, 2026
26386ee
Merge remote-tracking branch 'origin/feat/8334-design-defaults-revers…
kyle-sexton Oct 19, 2026
2a6fadf
test(planning): fail the spread-fact case on an Edit to source files
kyle-sexton Oct 19, 2026
9b2c0ac
Merge remote-tracking branch 'origin/main' into feat/8335-plan-time-d…
kyle-sexton Oct 19, 2026
3da5564
Merge branch 'main' into feat/8335-plan-time-design-checks
kyle-sexton Oct 19, 2026
5ed92b3
fix(planning): read the quality bar from the Brief and exempt early e…
kyle-sexton Oct 19, 2026
5b23e27
test(planning): drop the status tag from the spread-fact scaffold's p…
kyle-sexton Oct 19, 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
13 changes: 13 additions & 0 deletions .changes/planning/feat-8335-plan-time-design-checks-9d4c1e62.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
bump: minor
---

### Added

- `/planning:design-handoff`: the gate requires a principles thread and a lifecycle-and-reversibility thread, each resolved, directional or tagged-deferred (a prototype names both as deferred); a design missing either fails.
- `/planning:plan`: Step 2's design-default checklist audits the same two threads and confirms the Brief carries the `Quality bar:` line a phase-boundary review rates at; the plan template says to tag `Review: code-design` on any phase that adds or changes a contract, type, module, or a fact or rule several files read, Markdown included, and maps each tag to its `/review:quality-gate` mode (`concurrency` to `slice concurrency`).
- `planning:plan-reviewer`: Design alignment flags a plan that places one fact or rule in several files or rebuilds what existing code already does, and a missing principle or reversibility thread.

### Fixed

- `/planning:design-handoff`: a failed gate opens its reply with the `Gate: FAIL` verdict and the coverage table before routing back to `/planning:design`, so the design round no longer replaces the verdict.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
schema_version: "1.1"
name: dh-missing-reversibility-thread-fails
context:
scaffold_script: scaffold.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
type: regex
pattern: '^(?![^\n]*\b(?:no|not|nothing|none|never)(?:\s+\w+){0,3}\s+(?:missing|absent|lacks?)\b)(?![^\n]*n''t(?:\s+\w+){0,2}\s+(?:missing|absent)\b)(?=[^\n]*\b(?:reversib\w*|lifecycle|doors?)\b)[^\n]*?(?:\b(?:reversib\w*|lifecycle|doors?)\b[^\n.;]{0,80}\b(?:missing|absent|not (?:recorded|present|found))\b|\b(?:missing|absent|lacks?|lacking|without)\b[^\n.;]{0,60}\b(?:reversib\w*|lifecycle|doors?)\b|\bno\s+(?:[\w\x60*-]+\s+){0,2}(?:threads?\b|doors?\b|calls?\b|\x60?Door:))'
flags: mi
---
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
type: tool_used
tool: Edit
input_match: 'PLAN\.md'
min: 0
max: 0
---
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
type: tool_used
tool: Write
input_match: 'PLAN\.md'
min: 0
max: 0
---
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
type: regex
pattern: '(?<!not )(?<!Not )(?<!no )(?<!No )(?<!zero )(?<!Zero )(?<!never )(?<!Never )(?<!nothing )(?<!Nothing )(?<!n''t )(?<!would )(?<!could )(?<!/)\bFAIL(?:ED|S)?\b(?![^\n.]{0,20}\bif\b)|\b(?:[Gg]ate|[Cc]heck|[Hh]andoff)\b(?:(?!\b(?:not|no|zero|never|nothing|would|could)\b|n''t\b)[^\n.;]){0,30}\b(?:fails|failed|failure)\b'
---
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
description: "Design gate on a production-bar design whose every thread is resolved with rationale, but which has no lifecycle-and-reversibility thread and no door call on any decision: the gate fails on the missing thread and writes no PLAN.md"
tags: [design-handoff, reversibility, outcome, scaffold]
runs: 3
max_turns: 20
timeout_seconds: 900
allowed_tools: [Read, Glob, Grep, Skill, Bash, Write, Edit]
expected_outcome: "The reply gives a FAIL verdict because design-threads.md has no lifecycle-and-reversibility thread (no one-way or two-way door call on any decision), routes back to /planning:design, and no Write or Edit touches PLAN.md"
---

Use the design-handoff skill from the planning plugin on the topic export-archive. The design artifacts are in .work/export-archive/design/ and the Brief is in .work/export-archive/PLAN.md. Is the design ready for planning?
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"pass": [
{
"answer": "- **Lifecycle and reversibility**: missing. No thread records a door call.\n",
"why": "list item naming the missing thread"
},
{
"answer": "| Lifecycle and reversibility | required thread absent |\n",
"why": "table row"
},
{
"answer": "FAIL: design-threads.md has no reversibility thread and no door calls.\n",
"why": "no reversibility thread"
},
{
"answer": "Missing required thread: Lifecycle and reversibility (no `Door:` calls on T2-T4).\n",
"why": "label first"
}
],
"fail": [
{
"answer": "",
"why": "empty"
},
{
"answer": "Lifecycle and reversibility: RESOLVED, every decision carries a door call.\n",
"why": "claims the thread is present"
},
{
"answer": "No required thread is missing; reversibility is recorded.\n",
"why": "negated"
},
{
"answer": "The reversibility thread isn't missing.\n",
"why": "negated with n't"
},
{
"answer": "The T2 thread is missing a rationale; reversibility is fine.\n",
"why": "the missing finding is about another thread"
},
{
"answer": "No issue with reversibility: the door calls are recorded.\n",
"why": "no issue, not no thread"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"pass": [
{
"answer": [],
"why": "no tool call"
}
],
"fail": [
{
"answer": [
{
"tool": "Edit",
"input": {
"file_path": ".work/export-archive/PLAN.md",
"old_string": "## Brief",
"new_string": "## Design\n\n## Brief"
}
}
],
"why": "edited PLAN.md on a failed gate"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"pass": [
{
"answer": [],
"why": "no tool call"
},
{
"answer": [
{
"tool": "Write",
"input": {
"file_path": ".work/export-archive/design/gate-notes.md",
"content": "FAIL\n"
}
}
],
"why": "a write that is not PLAN.md"
}
],
"fail": [
{
"answer": [
{
"tool": "Write",
"input": {
"file_path": ".work/export-archive/PLAN.md",
"content": "## Design\n"
}
}
],
"why": "wrote PLAN.md on a failed gate"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"pass": [
{
"answer": "**Gate: FAIL.** design-threads.md has no Lifecycle and reversibility thread.\n",
"why": "bold verdict"
},
{
"answer": "The design gate failed: no decision carries a one-way or two-way door call.\n",
"why": "prose, gate failed"
},
{
"answer": "**The design isn't ready for planning yet.** The handoff check failed because `.work/export-archive/design/design-threads.md` is missing a required section, \"Lifecycle and reversibility\".\n",
"why": "prose, handoff check failed (r2 run 1)"
},
{
"answer": "The gate failure is confirmed: the Brief sets `Quality bar: production`, so every significant decision needs a door call.\n",
"why": "prose, gate failure (r2 run 2)"
}
],
"fail": [
{
"answer": "",
"why": "empty"
},
{
"answer": "**Gate: PASS.** Every thread is resolved with a recorded rationale.\n",
"why": "wrong verdict: the missing thread went unnoticed"
},
{
"answer": "The gate would FAIL if a required thread were missing; here it passes.\n",
"why": "conditional, not a verdict"
},
{
"answer": "No FAIL: reversibility is advisory, so I'll hand off.\n",
"why": "negated"
},
{
"answer": "The check would not fail here, so I'll hand off.\n",
"why": "negated, lowercase fail"
},
{
"answer": "The handoff check has not failed; every thread is resolved.\n",
"why": "negated, check not failed"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#!/usr/bin/env bash
# A production-bar design whose threads are all resolved with rationale, including a principles
# thread, but with no lifecycle-and-reversibility thread and no door call on any decision. A stored
# archive format and a public download URL are one-way doors the design never called.
set -euo pipefail

slice=.work/export-archive/design
mkdir -p "$slice"

cat >.work/export-archive/PLAN.md <<'MD'
# Account data export

## Brief

### Goal
Let a customer download every record the product holds about their account as one archive.

### Acceptance criteria
- A customer can request an export from account settings and download it within an hour.
- The archive holds every project, comment and attachment the account owns.

### Captured assumptions
- Quality bar: production
- Intent: second variant = speculative; existing structure: one Django app per product area; upstream: none
MD

cat >"$slice/design-threads.md" <<'MD'
# Design threads: account data export

## T1. Principles

- Status: RESOLVED
- Decision: keep the exporter specific to accounts; no generic export framework.
- Rationale: the Brief's `Intent:` line says a second export variant is speculative, so the reuse
default waits for a third occurrence; SRP keeps archive building apart from delivery.

## T2. Archive format

- Options: one ZIP of JSON files per entity type; a single newline-delimited JSON file
- Status: RESOLVED
- Decision: one ZIP holding a JSON file per entity type plus the attachment files.
- Rationale: customers open ZIP files without tooling, and per-type files let them find their
comments without parsing projects.

## T3. Delivery

- Options: email the archive; a signed download URL valid for 24 hours
- Status: RESOLVED
- Decision: a signed download URL at /exports/<id>/download, valid for 24 hours.
- Rationale: archives with attachments exceed mail size limits, and a signed URL expires on its own.

## T4. Job runner

- Options: build the archive in the request; the existing Celery worker
- Status: RESOLVED
- Decision: the existing Celery worker builds the archive.
- Rationale: large accounts take minutes to export, longer than the request timeout, and the
worker already runs the product's other long jobs.
MD
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
schema_version: "1.1"
name: plan-reviewer-flags-spread-fact
context:
scaffold_script: scaffold.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
type: regex
pattern: '^(?![^\n]*(?:\b(?:no|not|never|without|none)|n''t)\s+(?:\w+\s+){0,2}(?:(?:duplicate\w*|duplicating|duplications?)|repeat\w*|spread|cop(?:y|ies)|(?:restate\w*|restating|restatements?)|hard-?cod\w*)\b)(?![^\n]*No plan gaps found)(?=[^\n]*(?:\b30\b|retention))[^\n]*\b(?:(?:duplicate\w*|duplicating|duplications?)|repeat\w*|spread\w*|cop(?:y|ies)|(?:restate\w*|restating|restatements?)|hard-?cod\w*|(?:several|multiple|three|3|separate)\s+(?:\w+\s+)?(?:files|places|surfaces|locations|sites))\b'
flags: mi
---
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
type: tool_used
tool: Edit
input_match: '\.py"'
min: 0
max: 0
---
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
type: tool_used
tool: Write
input_match: '\.py"'
min: 0
max: 0
---
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
type: llm
---

The draft plan writes a 30-day audit log retention window as three separate literals: a constant
in app/cleanup/job.py, a `30` in the app/api/settings_view.py response, and a sentence in
docs/operations.md. The repo already keeps shared limits in app/settings.py.

PASS if the reported plan-review findings include one that the retention value is placed in more
than one file with no single source, and recommend giving it one home (for example a setting in
app/settings.py that the job and the API read, with the docs pointing at it or generated from it).

FAIL if no finding addresses where the 30-day value lives, if the repetition is mentioned only to
accept it, or if the reply reports no plan gaps.
13 changes: 13 additions & 0 deletions plugins/planning/evals/plan-reviewer-flags-spread-fact/prompt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
description: "Outcome case: a draft plan writes one 30-day retention window into the cleanup job, the admin API view and the operator docs as three literals, while the repo keeps shared limits in app/settings.py. The plan stress-test passes when its findings flag the spread fact"
tags: [plan, plan-reviewer, outcome, principles, dry, scaffold]
runs: 3
max_turns: 30
timeout_seconds: 900
allowed_tools: [Read, Glob, Grep, Skill, Bash, Agent, Write, Edit]
expected_outcome: "The reported plan-review findings flag that the 30-day retention value is placed in several files (app/cleanup/job.py, app/api/settings_view.py, docs/operations.md) with no single home, and point at one source such as app/settings.py. No .py file is written or edited"
---

/planning:plan

The draft plan for topic `log-retention` is at `.work/log-retention/PLAN.md`. Run its Step 3 plan stress-test on that draft and show me the findings, then stop. Don't edit any file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"pass": [
{
"answer": "| 1 | IMPORTANT | Design alignment | The 30-day retention window is written into three files (app/cleanup/job.py, app/api/settings_view.py, docs/operations.md) with no shared source | Define it once in app/settings.py and read it everywhere |\n",
"why": "findings table row"
},
{
"answer": "The plan hardcodes `30` for retention in the job, the API view and the docs.\n",
"why": "hardcoded in each surface"
},
{
"answer": "- Retention days are duplicated across job.py and settings_view.py; a later change to 30 would diverge.\n",
"why": "duplicated"
}
],
"fail": [
{
"answer": "",
"why": "empty"
},
{
"answer": "No plan gaps found.\n",
"why": "clean pass"
},
{
"answer": "No duplicated retention value: the plan keeps 30 days in one setting.\n",
"why": "negated"
},
{
"answer": "The plan doesn't repeat the 30-day retention anywhere.\n",
"why": "negated with n't"
},
{
"answer": "| 1 | SUGGESTION | Plan mechanics | Phase 1's Sanity Check names tests/test_cleanup.py, which does not exist yet for the 30-day retention job | Create it in the phase |\n",
"why": "a different finding about retention"
}
]
}
Loading
Loading