@friedbotstudio/create-baseline 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -3
- package/obj/template/.claude/commands/grant-push.md +19 -0
- package/obj/template/.claude/commands/init-project.md +26 -4
- package/obj/template/.claude/hooks/consent_gate_grant.mjs +107 -0
- package/obj/template/.claude/hooks/git_commit_guard.mjs +224 -0
- package/obj/template/.claude/hooks/harness_continuation.sh +101 -34
- package/obj/template/.claude/hooks/lib/common.mjs +283 -0
- package/obj/template/.claude/hooks/lib/common.sh +1 -1
- package/obj/template/.claude/hooks/memory_session_start.sh +20 -6
- package/obj/template/.claude/hooks/memory_stop.sh +161 -2
- package/obj/template/.claude/hooks/spec_approval_guard.sh +1 -1
- package/obj/template/.claude/hooks/swarm_approval_guard.sh +1 -1
- package/obj/template/.claude/hooks/tests/fixtures/ac008_byte_equal_reference.txt +7 -7
- package/obj/template/.claude/hooks/tests/fixtures/memory_stop_landmark_baseline.txt +21 -0
- package/obj/template/.claude/hooks/tests/fixtures/regenerate-ac008.sh +47 -0
- package/obj/template/.claude/hooks/tests/memory_session_start_test.sh +7 -3
- package/obj/template/.claude/hooks/tests/memory_stop_intent_test.sh +329 -0
- package/obj/template/.claude/hooks/tests/regenerate_ac008_test.sh +99 -0
- package/obj/template/.claude/memory/README.md +8 -3
- package/obj/template/.claude/memory/backlog.md +12 -0
- package/obj/template/.claude/project.json +6 -1
- package/obj/template/.claude/settings.json +3 -4
- package/obj/template/.claude/skills/audit-baseline/audit.sh +28 -16
- package/obj/template/.claude/skills/audit-baseline/tests/fixtures/_pending_opener_only.md +3 -0
- package/obj/template/.claude/skills/audit-baseline/tests/fixtures/preamble_full_empty_body.md +4 -0
- package/obj/template/.claude/skills/audit-baseline/tests/fixtures/preamble_full_with_entries.md +9 -0
- package/obj/template/.claude/skills/audit-baseline/tests/fixtures/preamble_no_opener.md +3 -0
- package/obj/template/.claude/skills/audit-baseline/tests/fixtures/preamble_opener_only.md +3 -0
- package/obj/template/.claude/skills/audit-baseline/tests/preamble_check_test.sh +147 -0
- package/obj/template/.claude/skills/chore/SKILL.md +5 -3
- package/obj/template/.claude/skills/commit/SKILL.md +5 -4
- package/obj/template/.claude/skills/copywriting/LICENSE +21 -0
- package/obj/template/.claude/skills/copywriting/NOTICE +23 -0
- package/obj/template/.claude/skills/copywriting/SKILL.md +1 -1
- package/obj/template/.claude/skills/design-ui/SKILL.md +23 -5
- package/obj/template/.claude/skills/design-ui/references/design-vs-development.md +26 -5
- package/obj/template/.claude/skills/design-ui/references/orchestration.md +1 -0
- package/obj/template/.claude/skills/design-ui/references/state-machine.md +5 -3
- package/obj/template/.claude/skills/documentation/LICENSE +202 -0
- package/obj/template/.claude/skills/documentation/NOTICE +22 -0
- package/obj/template/.claude/skills/harness/SKILL.md +3 -1
- package/obj/template/.claude/skills/humanizer/LICENSE +21 -0
- package/obj/template/.claude/skills/humanizer/NOTICE +21 -0
- package/obj/template/.claude/skills/impeccable/LICENSE +202 -0
- package/obj/template/.claude/skills/impeccable/NOTICE +24 -0
- package/obj/template/.claude/skills/memory-flush/SKILL.md +20 -4
- package/obj/template/.claude/skills/memory-flush/sweep.py +74 -6
- package/obj/template/.claude/skills/memory-flush/tests/run.sh +300 -1
- package/obj/template/.claude/skills/tdd/SKILL.md +2 -1
- package/obj/template/.claude/skills/tdd/drift_check.py +180 -0
- package/obj/template/.claude/skills/tdd/tests/drift_check_test.sh +190 -0
- package/obj/template/.claude/skills/tdd/tests/run.sh +21 -0
- package/obj/template/.claude/skills/technical-tutorials/LICENSE +21 -0
- package/obj/template/.claude/skills/technical-tutorials/NOTICE +23 -0
- package/obj/template/.claude/skills/technical-tutorials/SKILL.md +1 -1
- package/obj/template/.claude/skills/triage/SKILL.md +8 -3
- package/obj/template/CLAUDE.md +34 -23
- package/obj/template/docs/init/seed.md +36 -21
- package/obj/template/manifest.json +59 -33
- package/package.json +1 -1
- package/src/CLAUDE.template.md +34 -23
- package/src/memory/backlog.template.md +12 -0
- package/src/project.template.json +6 -1
- package/src/seed.template.md +36 -21
- package/src/settings.template.json +3 -4
- package/obj/template/.claude/hooks/consent_gate_grant.sh +0 -89
- package/obj/template/.claude/hooks/git_commit_guard.sh +0 -93
|
@@ -19,6 +19,14 @@ results = [] # (name, status, detail)
|
|
|
19
19
|
def add(name, status, detail=""):
|
|
20
20
|
results.append((name, status, detail))
|
|
21
21
|
|
|
22
|
+
def is_valid_preamble(text):
|
|
23
|
+
if not text.startswith("---"):
|
|
24
|
+
return False, "missing frontmatter"
|
|
25
|
+
remainder = text[3:]
|
|
26
|
+
if "\n---\n" in remainder or remainder.endswith("\n---"):
|
|
27
|
+
return True, ""
|
|
28
|
+
return False, "malformed frontmatter: missing closing separator"
|
|
29
|
+
|
|
22
30
|
# ---------- expected canonical sets (mirror seed.md §4) ----------
|
|
23
31
|
EXPECTED_HOOKS = {
|
|
24
32
|
# Write/Bash boundary guards (17)
|
|
@@ -65,12 +73,12 @@ def read_skill_owner(slug):
|
|
|
65
73
|
m = re.search(r'^owner:\s*(\S+)\s*$', fm.group(1), re.MULTILINE)
|
|
66
74
|
return m.group(1) if m else None
|
|
67
75
|
|
|
68
|
-
EXPECTED_COMMANDS = {"approve-spec", "approve-swarm", "grant-commit", "init-project"}
|
|
76
|
+
EXPECTED_COMMANDS = {"approve-spec", "approve-swarm", "grant-commit", "grant-push", "init-project"}
|
|
69
77
|
|
|
70
78
|
EXPECTED_MEMORY_FILES = {
|
|
71
|
-
# Canonical files (
|
|
79
|
+
# Canonical files (seven)
|
|
72
80
|
"landmarks", "libraries", "decisions", "landmines", "conventions",
|
|
73
|
-
"pending-questions",
|
|
81
|
+
"pending-questions", "backlog",
|
|
74
82
|
# Auto-extraction inbox (one); body gitignored, file committed
|
|
75
83
|
"_pending",
|
|
76
84
|
# Cross-session continuity snapshot (one); written by memory_stop &
|
|
@@ -128,7 +136,7 @@ agents_dir = root / ".claude/agents"
|
|
|
128
136
|
skills_dir = root / ".claude/skills"
|
|
129
137
|
cmds_dir = root / ".claude/commands"
|
|
130
138
|
|
|
131
|
-
disk_hooks = {p.stem for p in hooks_dir.glob("*.sh")} if hooks_dir.exists() else set()
|
|
139
|
+
disk_hooks = ({p.stem for p in hooks_dir.glob("*.sh")} | {p.stem for p in hooks_dir.glob("*.mjs")}) if hooks_dir.exists() else set()
|
|
132
140
|
disk_agents = {p.stem for p in agents_dir.glob("*.md")} if agents_dir.exists() else set()
|
|
133
141
|
disk_skills = {p.name for p in skills_dir.iterdir() if p.is_dir()} if skills_dir.exists() else set()
|
|
134
142
|
disk_commands = {p.stem for p in cmds_dir.glob("*.md")} if cmds_dir.exists() else set()
|
|
@@ -171,7 +179,7 @@ skills_claimed = find_count(
|
|
|
171
179
|
r"\b(\d+|twenty-(?:four|five|six|seven|eight|nine)|"
|
|
172
180
|
r"thirty|thirty-(?:one|two|three|four|five|six|seven|eight|nine)|forty)\s+skills?\b")
|
|
173
181
|
gates_claimed = find_count(r"\b(\d+|three)\s+consent\s+gates?\b")
|
|
174
|
-
cmds_claimed =
|
|
182
|
+
cmds_claimed = 5 if re.search(r"four\s+consent\s+gates?\s*\+\s*one\s+bootstrap", seed, re.IGNORECASE) else None
|
|
175
183
|
|
|
176
184
|
def check_count(label, claimed, actual):
|
|
177
185
|
if claimed is None:
|
|
@@ -295,28 +303,29 @@ else:
|
|
|
295
303
|
add("memory files present", "FAIL", "; ".join(bits))
|
|
296
304
|
else:
|
|
297
305
|
add("memory files present", "PASS", f"{len(disk_memory)} files")
|
|
298
|
-
#
|
|
306
|
+
# Record count is not an audit signal — a freshly-initialized project
|
|
307
|
+
# legitimately has zero entries in every canonical file (overlaid from
|
|
308
|
+
# pristine src/memory/<name>.template.md). Entry count is reported
|
|
309
|
+
# informationally.
|
|
299
310
|
for name in sorted(EXPECTED_MEMORY_FILES):
|
|
300
311
|
p = mem_dir / f"{name}.md"
|
|
301
312
|
if not p.is_file():
|
|
302
313
|
continue
|
|
303
314
|
text = p.read_text(encoding="utf-8", errors="replace")
|
|
304
|
-
|
|
305
|
-
|
|
315
|
+
ok, reason = is_valid_preamble(text)
|
|
316
|
+
if not ok:
|
|
317
|
+
add(f"memory shape: {name}.md", "FAIL", reason)
|
|
306
318
|
continue
|
|
307
|
-
# _pending body may be empty; canonical must have at least one entry.
|
|
308
319
|
if name == "_pending":
|
|
309
320
|
add(f"memory shape: {name}.md", "PASS", "")
|
|
310
321
|
continue
|
|
311
|
-
body = text.split("---", 2)[-1]
|
|
322
|
+
body = text.split("---", 2)[-1]
|
|
312
323
|
# Strip fenced code blocks so example "## <stable key>" lines inside
|
|
313
324
|
# ```markdown ... ``` don't count as entries.
|
|
314
325
|
body_no_fence = re.sub(r"(?ms)^```.*?^```\s*$", "", body)
|
|
315
326
|
entry_count = len(re.findall(r'(?m)^##\s+\S', body_no_fence))
|
|
316
|
-
if entry_count
|
|
317
|
-
|
|
318
|
-
else:
|
|
319
|
-
add(f"memory shape: {name}.md", "PASS", f"{entry_count} entries")
|
|
327
|
+
detail = f"{entry_count} entries" if entry_count > 0 else "empty (preamble-only)"
|
|
328
|
+
add(f"memory shape: {name}.md", "PASS", detail)
|
|
320
329
|
# README inside memory/ is a structural expectation
|
|
321
330
|
add("memory README", "PASS" if (mem_dir / "README.md").is_file() else "FAIL",
|
|
322
331
|
"" if (mem_dir / "README.md").is_file() else "missing .claude/memory/README.md")
|
|
@@ -419,7 +428,7 @@ else:
|
|
|
419
428
|
try:
|
|
420
429
|
s_text = src_settings.read_text(encoding="utf-8")
|
|
421
430
|
json.loads(s_text)
|
|
422
|
-
missing_wired = sorted(h for h in EXPECTED_HOOKS if f"{h}.sh" not in s_text)
|
|
431
|
+
missing_wired = sorted(h for h in EXPECTED_HOOKS if (f"{h}.sh" not in s_text and f"{h}.mjs" not in s_text))
|
|
423
432
|
if missing_wired:
|
|
424
433
|
head = missing_wired[:3]
|
|
425
434
|
tail = f" + {len(missing_wired) - 3} more" if len(missing_wired) > 3 else ""
|
|
@@ -502,7 +511,7 @@ else:
|
|
|
502
511
|
except Exception as e:
|
|
503
512
|
add("settings.json parses", "FAIL", str(e))
|
|
504
513
|
for h in sorted(EXPECTED_HOOKS):
|
|
505
|
-
if f"{h}.sh" in settings_text:
|
|
514
|
+
if f"{h}.sh" in settings_text or f"{h}.mjs" in settings_text:
|
|
506
515
|
add(f"hook wired: {h}", "PASS", "")
|
|
507
516
|
else:
|
|
508
517
|
add(f"hook wired: {h}", "FAIL", "not in settings.json")
|
|
@@ -535,6 +544,9 @@ else:
|
|
|
535
544
|
("swarm.enforced_path_prefixes", ["swarm", "enforced_path_prefixes"]),
|
|
536
545
|
("consent.commit_ttl_seconds", ["consent", "commit_ttl_seconds"]),
|
|
537
546
|
("consent.gate_marker_ttl_seconds", ["consent", "gate_marker_ttl_seconds"]),
|
|
547
|
+
("consent.push_ttl_seconds", ["consent", "push_ttl_seconds"]),
|
|
548
|
+
("git.protected_branches", ["git", "protected_branches"]),
|
|
549
|
+
("git.branch_pattern", ["git", "branch_pattern"]),
|
|
538
550
|
("additions.agents", ["additions", "agents"]),
|
|
539
551
|
("additions.skills", ["additions", "skills"]),
|
|
540
552
|
("additions.hooks", ["additions", "hooks"]),
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Fixture-based integration tests for the audit-baseline preamble validator.
|
|
3
|
+
# Covers the strict-preamble tightening in .claude/skills/audit-baseline/audit.sh.
|
|
4
|
+
#
|
|
5
|
+
# Each test builds a synthetic .claude/memory/ tree under a tempdir (with all
|
|
6
|
+
# 9 expected canonical filenames), substitutes one file with a fixture, then
|
|
7
|
+
# runs audit.sh with CLAUDE_PROJECT_DIR pointed at the tempdir and greps the
|
|
8
|
+
# captured output for the "memory shape: <name>.md" line.
|
|
9
|
+
#
|
|
10
|
+
# The audit will exit non-zero in the stub tree because hook/skill/agent
|
|
11
|
+
# counts won't match — that's expected. We only assert on the specific
|
|
12
|
+
# memory-shape line each test cares about.
|
|
13
|
+
#
|
|
14
|
+
# Not wired into project.json -> test.cmd; run manually during /tdd and
|
|
15
|
+
# /integrate alongside .claude/hooks/tests/memory_session_start_test.sh.
|
|
16
|
+
|
|
17
|
+
set -uo pipefail
|
|
18
|
+
|
|
19
|
+
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
20
|
+
REPO_ROOT="$(cd "$HERE/../../../.." && pwd)"
|
|
21
|
+
AUDIT="$REPO_ROOT/.claude/skills/audit-baseline/audit.sh"
|
|
22
|
+
FIXTURES="$HERE/fixtures"
|
|
23
|
+
|
|
24
|
+
PASS=0; FAIL=0; FAILED=()
|
|
25
|
+
|
|
26
|
+
# --- assertion helpers --------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
fail() { echo " FAIL: $*"; return 1; }
|
|
29
|
+
|
|
30
|
+
# Seed a stub .claude/memory/ tree under $1. The file basename $2 is replaced
|
|
31
|
+
# with the fixture content from $3; the other 8 expected files get a valid
|
|
32
|
+
# synthetic preamble so they don't pollute the audit output we grep against.
|
|
33
|
+
seed_stub_tree() {
|
|
34
|
+
local root="$1" under_test="$2" fixture_path="$3"
|
|
35
|
+
mkdir -p "$root/.claude/memory"
|
|
36
|
+
# README is checked separately by audit.sh; copy the real one so that check
|
|
37
|
+
# passes and doesn't tangle our grep.
|
|
38
|
+
cp "$REPO_ROOT/.claude/memory/README.md" "$root/.claude/memory/README.md"
|
|
39
|
+
local mem_name
|
|
40
|
+
for mem_name in landmarks libraries decisions landmines conventions \
|
|
41
|
+
pending-questions backlog _pending _resume; do
|
|
42
|
+
if [ "$mem_name" = "$under_test" ]; then
|
|
43
|
+
cp "$fixture_path" "$root/.claude/memory/${mem_name}.md"
|
|
44
|
+
else
|
|
45
|
+
cat > "$root/.claude/memory/${mem_name}.md" <<'EOF'
|
|
46
|
+
---
|
|
47
|
+
owners: [test]
|
|
48
|
+
key: test
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
# Synthetic valid preamble
|
|
52
|
+
EOF
|
|
53
|
+
fi
|
|
54
|
+
done
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
# Run audit.sh against the stub tree at $1 and print the line matching
|
|
58
|
+
# "memory shape: $2.md" to stdout. Returns 1 if no such line found.
|
|
59
|
+
audit_memory_shape_line() {
|
|
60
|
+
local root="$1" name="$2"
|
|
61
|
+
CLAUDE_PROJECT_DIR="$root" bash "$AUDIT" 2>&1 \
|
|
62
|
+
| grep -E "^memory shape: ${name}\.md[[:space:]]" \
|
|
63
|
+
| head -1
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
# Assert that the memory-shape line for $2 in the audit run against $1 has
|
|
67
|
+
# status $3 and a detail matching the extended-regex $4.
|
|
68
|
+
assert_memory_shape() {
|
|
69
|
+
local root="$1" name="$2" want_status="$3" want_detail_re="$4"
|
|
70
|
+
local line; line="$(audit_memory_shape_line "$root" "$name")"
|
|
71
|
+
if [ -z "$line" ]; then
|
|
72
|
+
fail "no 'memory shape: ${name}.md' line in audit output"
|
|
73
|
+
return 1
|
|
74
|
+
fi
|
|
75
|
+
if ! printf '%s' "$line" | grep -qE "[[:space:]]${want_status}[[:space:]]"; then
|
|
76
|
+
fail "expected status ${want_status} for ${name}.md; got: ${line}"
|
|
77
|
+
return 1
|
|
78
|
+
fi
|
|
79
|
+
if ! printf '%s' "$line" | grep -qE "${want_detail_re}"; then
|
|
80
|
+
fail "expected detail matching '${want_detail_re}' for ${name}.md; got: ${line}"
|
|
81
|
+
return 1
|
|
82
|
+
fi
|
|
83
|
+
return 0
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
run() {
|
|
87
|
+
local name="$1"
|
|
88
|
+
echo "RUN $name"
|
|
89
|
+
if "$name"; then
|
|
90
|
+
PASS=$((PASS+1)); echo "PASS $name"
|
|
91
|
+
else
|
|
92
|
+
FAIL=$((FAIL+1)); FAILED+=("$name"); echo "FAIL $name"
|
|
93
|
+
fi
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
# --- tests --------------------------------------------------------------------
|
|
97
|
+
|
|
98
|
+
test_when_memory_file_has_opener_only_then_audit_reports_fail() {
|
|
99
|
+
local tmp; tmp="$(mktemp -d)"; trap "rm -rf $tmp" RETURN
|
|
100
|
+
seed_stub_tree "$tmp" "landmarks" "$FIXTURES/preamble_opener_only.md"
|
|
101
|
+
assert_memory_shape "$tmp" "landmarks" "FAIL" \
|
|
102
|
+
"malformed frontmatter: missing closing separator"
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
test_when_memory_file_has_no_opener_then_audit_reports_fail() {
|
|
106
|
+
local tmp; tmp="$(mktemp -d)"; trap "rm -rf $tmp" RETURN
|
|
107
|
+
seed_stub_tree "$tmp" "libraries" "$FIXTURES/preamble_no_opener.md"
|
|
108
|
+
assert_memory_shape "$tmp" "libraries" "FAIL" \
|
|
109
|
+
"missing frontmatter"
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
test_when_memory_file_has_valid_full_preamble_no_body_then_audit_reports_pass_preamble_only() {
|
|
113
|
+
local tmp; tmp="$(mktemp -d)"; trap "rm -rf $tmp" RETURN
|
|
114
|
+
seed_stub_tree "$tmp" "decisions" "$FIXTURES/preamble_full_empty_body.md"
|
|
115
|
+
assert_memory_shape "$tmp" "decisions" "PASS" \
|
|
116
|
+
"empty \\(preamble-only\\)"
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
test_when_memory_file_has_valid_preamble_with_entries_then_audit_reports_pass_with_count() {
|
|
120
|
+
local tmp; tmp="$(mktemp -d)"; trap "rm -rf $tmp" RETURN
|
|
121
|
+
seed_stub_tree "$tmp" "landmines" "$FIXTURES/preamble_full_with_entries.md"
|
|
122
|
+
assert_memory_shape "$tmp" "landmines" "PASS" \
|
|
123
|
+
"1 entries"
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
test_when_pending_file_has_opener_only_then_audit_reports_fail() {
|
|
127
|
+
local tmp; tmp="$(mktemp -d)"; trap "rm -rf $tmp" RETURN
|
|
128
|
+
seed_stub_tree "$tmp" "_pending" "$FIXTURES/_pending_opener_only.md"
|
|
129
|
+
assert_memory_shape "$tmp" "_pending" "FAIL" \
|
|
130
|
+
"malformed frontmatter: missing closing separator"
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
# --- runner -------------------------------------------------------------------
|
|
134
|
+
|
|
135
|
+
run test_when_memory_file_has_opener_only_then_audit_reports_fail
|
|
136
|
+
run test_when_memory_file_has_no_opener_then_audit_reports_fail
|
|
137
|
+
run test_when_memory_file_has_valid_full_preamble_no_body_then_audit_reports_pass_preamble_only
|
|
138
|
+
run test_when_memory_file_has_valid_preamble_with_entries_then_audit_reports_pass_with_count
|
|
139
|
+
run test_when_pending_file_has_opener_only_then_audit_reports_fail
|
|
140
|
+
|
|
141
|
+
echo "----"
|
|
142
|
+
echo "Passed: $PASS Failed: $FAIL"
|
|
143
|
+
if [ "$FAIL" -gt 0 ]; then
|
|
144
|
+
echo "Failed tests:"
|
|
145
|
+
for t in "${FAILED[@]}"; do echo " - $t"; done
|
|
146
|
+
fi
|
|
147
|
+
exit $((FAIL > 0))
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: chore
|
|
3
3
|
owner: baseline
|
|
4
|
-
description: Workflow track for tasks that need no TDD — documentation edits, governance count bumps, vendored-skill content updates, configuration tweaks, formatting, typo fixes, dependency bumps where no project code changes. Skips `/scenario` and `/implement` (no failing test to drive) and runs the work directly. `verify`, `archive`, `/grant-commit`, and `/commit` remain mandatory. `simplify`, `integrate`, and `document` are conditional — required when the diff hits one of the listed triggers, optional otherwise. Chore is a stripped-down pipeline, not a bypass; never silently skip a conditional phase whose triggers apply.
|
|
4
|
+
description: Workflow track for tasks that need no TDD — documentation edits, governance count bumps, vendored-skill content updates, configuration tweaks, formatting, typo fixes, dependency bumps where no project code changes. Skips `/scenario` and `/implement` (no failing test to drive) and runs the work directly. `verify`, `archive`, `memory-flush`, `/grant-commit`, and `/commit` remain mandatory. `simplify`, `integrate`, and `document` are conditional — required when the diff hits one of the listed triggers, optional otherwise. Chore is a stripped-down pipeline, not a bypass; never silently skip a conditional phase whose triggers apply.
|
|
5
5
|
argument-hint: "<one-line description of the chore>"
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -44,7 +44,8 @@ The classification rule is: *if there is no failing test that should exist for t
|
|
|
44
44
|
1. **Edit** — apply the change directly. No `/scenario`, no `/implement` — there is no failing test to drive.
|
|
45
45
|
2. **`verify`** — run the project test command and stamp `.claude/state/last_test_result`. FAIL means stop, surface, and route the user to `/triage` for a proper bugfix track. The verdict is binding (the `verify_pass_guard` hook reads this file).
|
|
46
46
|
3. **`archive`** — empty bundle is fine; `/commit`'s prereq requires `archive` in `completed`.
|
|
47
|
-
4.
|
|
47
|
+
4. **`memory-flush`** — Phase 10.6. Empty pending is fine (fast-path runs Step 0 sweeps and short-circuits). `/commit`'s prereq requires `memory-flush` in `completed`.
|
|
48
|
+
5. **`/grant-commit` then `/commit`** — user-required consent + commit. Same as every other workflow.
|
|
48
49
|
|
|
49
50
|
### Conditional phases (required when triggers apply, optional otherwise)
|
|
50
51
|
|
|
@@ -81,7 +82,8 @@ If a conditional phase is required, run it **before** `/grant-commit`. If you sk
|
|
|
81
82
|
- **Required** → invoke the phase skill and append it to `workflow.json → completed`.
|
|
82
83
|
- **Skipped** → record the rationale in your end-of-chore summary; do not append to `completed`.
|
|
83
84
|
6. Invoke `Skill(archive)` — mandatory.
|
|
84
|
-
|
|
85
|
+
6.5. Invoke `Skill(memory-flush)` — mandatory (Phase 10.6). Runs Step 0 canonical sweeps and, if `_pending.md` is non-empty, full triage. On empty pending the fast-path returns success in ≤ 3 sweep.py invocations.
|
|
86
|
+
7. Append `"chore"`, `"archive"`, `"memory-flush"`, and any conditional phases that ran to `workflow.json → completed`. Update `updated_at` to the current epoch.
|
|
85
87
|
8. **Marker op FIRST, then write `harness_state`, then emit end-of-chore summary.** On `state: "continue"` (more phases follow, e.g. archive is still pending): `echo "<slug>" > .claude/state/.harness_active` to refresh the active marker, then write `.claude/state/harness_state` with `{state: "continue", slug, reason}`. On `state: "done"` (archive just appended and no further phases remain): `rm -f .claude/state/.harness_active`, then write `harness_state` with `{state: "done", slug, reason}`. The state file carries exactly three keys; no `written_at`, no `tick_count`. Then tell the user:
|
|
86
88
|
- "Chore green."
|
|
87
89
|
- Files changed.
|
|
@@ -5,7 +5,7 @@ description: Workflow Phase 11 — Commit Preparation and Execution. Stages and
|
|
|
5
5
|
argument-hint: "[optional commit message; otherwise drafted from the spec/intake]"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
Prereq: `archive` in `completed` (
|
|
8
|
+
Prereq: BOTH `archive` AND `memory-flush` in `completed` (Phase 10.5 moved slug artifacts to `docs/archive/<date>/<slug>/`; Phase 10.6 curated `_pending.md` and applied canonical memory writes) AND a valid consent token at `.claude/state/commit_consent` (the Git Commit Guard hook enforces this independently). On any workflow where `memory-flush` is in `exceptions` (rare), this skill SHALL refuse to proceed unless that exception is explicit in `workflow.json`.
|
|
9
9
|
|
|
10
10
|
**Applicability.** This skill applies only when the project is a git repository. Non-git projects auto-except `commit` at `/triage` time (CLAUDE.md Article IV); the workflow ends after `/archive`.
|
|
11
11
|
|
|
@@ -13,9 +13,10 @@ Steps:
|
|
|
13
13
|
|
|
14
14
|
0. **Git-repo precheck.** Run `git rev-parse --is-inside-work-tree 2>/dev/null`. If exit non-zero, exit cleanly with: "Not a git repository — `/commit` is inapplicable. Per CLAUDE.md Article IV, `commit` is auto-excepted on non-git projects; the workflow ended at `/archive`. Persistence outside git is your responsibility." Do not run any subsequent step.
|
|
15
15
|
1. **Archive `workflow.json` itself.** This is the final piece of the archival bundle, held back until now so phase-ordering checks worked up through this point. Read `.claude/state/workflow.json` to get the slug, then move the file into the already-existing archive bundle: `docs/archive/<date>/<slug>/workflow.json`. Use the bundle's `<date>` directory (the one `/archive` created — inspect `docs/archive/` to find the most recent bundle matching the slug).
|
|
16
|
-
2. Verify workflow prereq:
|
|
16
|
+
2. Verify workflow prereq: memory-flush is the final non-commit entry in `completed`; `archive` is the entry immediately before it; no open consent gates remain.
|
|
17
17
|
3. `git status` + `git diff --stat` to confirm the change set. The diff now includes: production code changes + archive bundle additions + the workflow.json move. Stage named paths explicitly (never `git add -A` / `git add .` — seed.md forbids it).
|
|
18
18
|
4. Draft the commit message from the spec + diff. Conventional-style prefix (`feat:` / `fix:` / `refactor:` / `docs:` / `test:`) followed by a 1-line summary and a short body explaining the WHY. The subject line is a fixed-register one-liner — leave it alone. The body is reviewer-facing prose — pass it through `Skill(humanizer)` before step 5 so AI-writing tells (em-dash overuse, rule of three, inflated verbs, vague attributions) get scrubbed. Keep the brief tight: tell humanizer the register is "factual reviewer-facing commit body — describe the diff faithfully, do not invent rationale, preserve any spec quotes verbatim".
|
|
19
19
|
5. Run `git commit` with the message via HEREDOC. The Git Commit Guard hook will verify consent. If consent is missing/expired, stop and ask the user to run `/grant-commit`.
|
|
20
|
-
6.
|
|
21
|
-
7.
|
|
20
|
+
6. **Stamp source backlog entries (post-commit, only when populated).** Read `workflow.json → source_backlog_keys` BEFORE Step 1 archived the file (or read the archived copy at `docs/archive/<date>/<slug>/workflow.json`). If the array is absent OR empty, skip this step entirely. Otherwise invoke `python3 .claude/skills/memory-flush/sweep.py --mode stamp-closure --memory-dir .claude/memory --backlog-keys <comma-separated keys>`. Parse the JSON report and report `stamped`/`missing`/`already_closed` counts in the terminal message alongside the commit SHA. `sweep.py` is the only writer to `backlog.md`; `commit/SKILL.md` SHALL NOT edit canonical memory files directly. If the stamp invocation fails (filesystem error), surface it but do NOT roll back the commit — the entries can be stamped manually or by the next workflow's `/memory-flush`.
|
|
21
|
+
7. Do NOT run `git push`, `git commit --amend`, or pass `--no-verify`/`--no-gpg-sign` unless the user explicitly named the operation in their current request.
|
|
22
|
+
8. Append `"commit"` to `completed` — but note this only matters for logs; the workflow.json is now in the archive and the live `.claude/state/workflow.json` no longer exists. Report the commit SHA to the user.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) coreyhaines31 (https://github.com/coreyhaines31/marketingskills)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
copywriting
|
|
2
|
+
===========
|
|
3
|
+
|
|
4
|
+
This skill is vendored from the `copywriting` skill in the `marketingskills`
|
|
5
|
+
collection published by coreyhaines31 and is redistributed here under the
|
|
6
|
+
terms of the MIT License (see LICENSE in this directory).
|
|
7
|
+
|
|
8
|
+
Upstream:
|
|
9
|
+
Repository: https://github.com/coreyhaines31/marketingskills
|
|
10
|
+
Author: coreyhaines31
|
|
11
|
+
Skill: copywriting/
|
|
12
|
+
|
|
13
|
+
Vendored into: .claude/skills/copywriting/
|
|
14
|
+
Vendored on: 2026-05-15 (this commit) — prior install date unknown; LICENSE
|
|
15
|
+
+ NOTICE added retroactively as part of the licensing-attribution
|
|
16
|
+
drift fix in the branch-aware-git-policy workflow.
|
|
17
|
+
|
|
18
|
+
Local changes:
|
|
19
|
+
- References to five companion skills (`copy-editing`, `email-sequence`,
|
|
20
|
+
`popup-cro`, etc.) that do not ship in this baseline are flagged in the
|
|
21
|
+
SKILL.md body with explicit "do not ship with this baseline" notes.
|
|
22
|
+
- Frontmatter `owner: baseline` added so the audit-baseline drift check
|
|
23
|
+
tracks this file as a shipped baseline artifact.
|
|
@@ -243,7 +243,7 @@ For headlines and CTAs, provide 2-3 options:
|
|
|
243
243
|
|
|
244
244
|
## Related Skills
|
|
245
245
|
|
|
246
|
-
These five are upstream companion skills
|
|
246
|
+
These five are upstream companion skills from the same source repo as `copywriting` ([`coreyhaines31/marketingskills`](https://github.com/coreyhaines31/marketingskills)) and **do not ship with this baseline**. Listed for reference only — for the capabilities they describe, ask the user inline or vendor them separately following the same MIT terms.
|
|
247
247
|
|
|
248
248
|
- **copy-editing**: For polishing existing copy (use after your draft)
|
|
249
249
|
- **page-cro**: If page structure/strategy needs work, not just copy
|
|
@@ -17,7 +17,7 @@ These are not preferences. They are structural commitments locked by spec `docs/
|
|
|
17
17
|
- **You ALWAYS invoke impeccable.** Every design move goes through `Skill(impeccable, …)`. No exceptions, no shortcuts.
|
|
18
18
|
- **You NEVER pick aesthetic direction.** Register, palette, type scale, motion vocabulary — all decided inside impeccable's subcommands in main context. You decide *which* subcommand to invoke, not *what* design to produce.
|
|
19
19
|
- **You NEVER write product code.** Files under `app/`, `site-src/`, `components/`, `src/` — all flow through impeccable's writing subcommands (`craft`, `polish`, refines, enhances, fixes). You write only thin glue: state JSON, brief snapshots, audit snapshots.
|
|
20
|
-
- **You ALWAYS classify before acting.** A misrouted `task_brief`
|
|
20
|
+
- **You ALWAYS classify before acting.** A misrouted `task_brief` returns at Stage 0 with one of two terminal states: **`not_a_design_task`** (when all surfaces classify as a single non-design lane — pure development or pure copy) with a `correct_lane` pointer, OR **`mixed_brief`** (when target_files span ≥ 2 lanes per the per-concern rule in `references/design-vs-development.md`) with a structured `lane_split` array. Design tasks proceed; everything else stops at Stage 0 without invoking impeccable or writing product code.
|
|
21
21
|
|
|
22
22
|
## Mandatory first step
|
|
23
23
|
|
|
@@ -47,7 +47,9 @@ Decide which lane this `task_brief` belongs to. The classification rule lives in
|
|
|
47
47
|
|
|
48
48
|
Stage 0 evaluates two signals: (1) the intent string against the [`references/intent-table.md`](references/intent-table.md) rows, and (2) the `target_files` extensions as a tie-breaker.
|
|
49
49
|
|
|
50
|
-
If the classification is anything other than **design**, return immediately
|
|
50
|
+
If the classification is anything other than **design**, return immediately with one of two misroute terminals.
|
|
51
|
+
|
|
52
|
+
**Single-lane misroute** (all surfaces classify as pure development OR pure copy):
|
|
51
53
|
|
|
52
54
|
```jsonc
|
|
53
55
|
{
|
|
@@ -58,7 +60,22 @@ If the classification is anything other than **design**, return immediately:
|
|
|
58
60
|
}
|
|
59
61
|
```
|
|
60
62
|
|
|
61
|
-
|
|
63
|
+
**Multi-lane misroute** (target_files span ≥ 2 lanes per the per-concern rule in `references/design-vs-development.md`):
|
|
64
|
+
|
|
65
|
+
```jsonc
|
|
66
|
+
{
|
|
67
|
+
"final_state": "mixed_brief",
|
|
68
|
+
"lane_split": [
|
|
69
|
+
{ "surface": "<path>", "lane": "design" | "development" | "copy", "reason": "<plain-language>" }
|
|
70
|
+
],
|
|
71
|
+
"reason": "task_brief spans <N> lanes",
|
|
72
|
+
"state_file": ".claude/state/design/<slug>.json"
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
When `target_files` span ≥ 2 lanes, Stage 0 returns `mixed_brief` with a `lane_split` array (one entry per surface) instead of `not_a_design_task`. The caller fans out per row; design-ui does NOT execute any lane in this case (no impeccable invocation, no product-code writes). SKILL.md is the canonical source for this contract; `references/design-vs-development.md` mirrors it.
|
|
77
|
+
|
|
78
|
+
design-ui still writes a checkpoint state file even on misroute — the orchestration history is traceable. See `references/state-machine.md` for the sticky-resume rule that applies to both misroute terminals.
|
|
62
79
|
|
|
63
80
|
### Stage 1 — Capture
|
|
64
81
|
|
|
@@ -117,12 +134,13 @@ Return a structured `Report`:
|
|
|
117
134
|
"slug": "<the slug>",
|
|
118
135
|
"intent": "<the intent>",
|
|
119
136
|
"recipe_executed": ["shape", "craft", "audit", "polish"],
|
|
120
|
-
"final_state": "complete" | "needs_human" | "blocked" | "not_a_design_task",
|
|
137
|
+
"final_state": "complete" | "needs_human" | "blocked" | "not_a_design_task" | "mixed_brief",
|
|
121
138
|
"files_touched": ["<path>", ...],
|
|
122
139
|
"verifications": { "audit_score": "19/20", "p0": 0, "p1": 0 },
|
|
123
140
|
"next_actions": ["<human-readable>"],
|
|
124
141
|
"state_file": ".claude/state/design/<slug>.json",
|
|
125
|
-
"thin_glue_written": ["docs/design/<slug>.brief.md", "docs/design/<slug>.audit.md"]
|
|
142
|
+
"thin_glue_written": ["docs/design/<slug>.brief.md", "docs/design/<slug>.audit.md"],
|
|
143
|
+
"lane_split": [ { "surface": "<path>", "lane": "design" | "development" | "copy", "reason": "<plain-language>" } ] // present only when final_state == "mixed_brief"
|
|
126
144
|
}
|
|
127
145
|
```
|
|
128
146
|
|
|
@@ -21,7 +21,7 @@ When a `task_brief` arrives, Stage 0 evaluates **two signals** in order:
|
|
|
21
21
|
- All paths match `tdd.ui_globs` AND no logic-file extensions → **design**.
|
|
22
22
|
- All paths are logic files (`.ts`, `.js`, `.go`, `.py`, `.rs`, etc., excluding `.tsx` / `.jsx` / `.vue` / `.svelte`) → **development**.
|
|
23
23
|
- All paths are `.md` / `.mdx` and the intent mentions "write", "rewrite", "improve", "draft" → **copy**.
|
|
24
|
-
- Mixed →
|
|
24
|
+
- Mixed → Stage 0 returns `final_state: "mixed_brief"` with a `lane_split` array (one entry per surface). See `SKILL.md` (canonical) for the return shape.
|
|
25
25
|
|
|
26
26
|
## Overlap is normal — same file, three lanes
|
|
27
27
|
|
|
@@ -67,7 +67,11 @@ Backend / data-fetching performance, query optimization, memoization for re-rend
|
|
|
67
67
|
|
|
68
68
|
## Misroute handling
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
*`SKILL.md` is the canonical source for Stage 0 misroute prose; this file mirrors it.*
|
|
71
|
+
|
|
72
|
+
Stage 0 has two misroute terminals.
|
|
73
|
+
|
|
74
|
+
**Single-lane misroute** — all surfaces classify as one non-design lane (pure development OR pure copy):
|
|
71
75
|
|
|
72
76
|
```jsonc
|
|
73
77
|
{
|
|
@@ -78,12 +82,29 @@ If Stage 0 classifies the intent as **development** or **copy** (not design), `d
|
|
|
78
82
|
}
|
|
79
83
|
```
|
|
80
84
|
|
|
81
|
-
The caller
|
|
85
|
+
The caller reads `correct_lane` and re-routes.
|
|
86
|
+
|
|
87
|
+
**Multi-lane misroute** — target_files span ≥ 2 lanes:
|
|
88
|
+
|
|
89
|
+
```jsonc
|
|
90
|
+
{
|
|
91
|
+
"final_state": "mixed_brief",
|
|
92
|
+
"lane_split": [
|
|
93
|
+
{ "surface": "<path>", "lane": "design" | "development" | "copy", "reason": "<plain-language>" }
|
|
94
|
+
],
|
|
95
|
+
"reason": "task_brief spans <N> lanes",
|
|
96
|
+
"state_file": ".claude/state/design/<slug>.json"
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
The caller reads `lane_split` and fans out per row; see `references/orchestration.md` caller-policy. `design-ui` never silently passes a non-design brief through to `impeccable` — that would muddy impeccable's contract. On a `mixed_brief`, `design-ui` invokes nothing and writes no product code: the structured `lane_split` is the entire response.
|
|
82
101
|
|
|
83
102
|
## When in doubt
|
|
84
103
|
|
|
85
|
-
|
|
104
|
+
Multi-lane briefs (target_files span ≥ 2 lanes) route automatically to `mixed_brief` — Stage 0 returns the structured `lane_split` without asking the user. The interactive-ask path below is reserved for the rarer **single-lane ambiguity** case: the intent matches no row in `references/intent-table.md` AND `target_files` doesn't disambiguate (e.g., a single `.md` file but the intent reads like a design ask, not a copy ask).
|
|
105
|
+
|
|
106
|
+
In that case, surface to the user with a one-line question:
|
|
86
107
|
|
|
87
|
-
> "This task
|
|
108
|
+
> "This task is ambiguous within a single lane: <intent>. Which concern are you asking about? (a) design — surface, motion, visual a11y; (b) development — behavior, logic, data; (c) copy — prose rewrite."
|
|
88
109
|
|
|
89
110
|
Do not guess. The clean separation is what makes the lanes structural.
|
|
@@ -119,3 +119,4 @@ This is the same cap-3 shape as the polish loop, with `critique` as the gating s
|
|
|
119
119
|
| `needs_human` | Warn and continue. design-ui has surfaced; the user can re-invoke later. /tdd Step 6 does NOT fail. The audit report path goes in /tdd's notes. |
|
|
120
120
|
| `blocked` | Stop /tdd Step 6. Surface the blocker to the user. /tdd's `## 7. Decide on the result` step receives this and decides whether to escalate to a spec change. |
|
|
121
121
|
| `not_a_design_task` | Stage 0 misroute. /tdd surfaces "design-ui returned not_a_design_task — was this design_call mis-classified in the spec?" and stops to reconcile. |
|
|
122
|
+
| `mixed_brief` | Stage 0 multi-lane misroute. Read `lane_split`. For each row: lane=design → re-invoke design-ui with a surface-scoped sub-brief; lane=development → record on `next_actions` and surface to the user; lane=copy → record on `next_actions` and surface to the user. Do NOT auto-invoke `/tdd` or `prose` in this tick — the spec author can split the `## Design calls` row deliberately. /tdd Step 6 surfaces a one-line summary and proceeds. |
|
|
@@ -21,7 +21,7 @@ The first invocation creates the directory if it does not exist (`mkdir -p`). Su
|
|
|
21
21
|
"step_index": <int, 0-based; index of the NEXT step to run>,
|
|
22
22
|
"invocations": [InvocationRecord, ...],
|
|
23
23
|
"verifications": [VerificationRecord, ...],
|
|
24
|
-
"state": "in_progress" | "complete" | "needs_human" | "blocked" | "not_a_design_task",
|
|
24
|
+
"state": "in_progress" | "complete" | "needs_human" | "blocked" | "not_a_design_task" | "mixed_brief",
|
|
25
25
|
"next_actions": ["<human-readable action>", ...]
|
|
26
26
|
}
|
|
27
27
|
```
|
|
@@ -69,7 +69,7 @@ Every state file MUST carry these fields. Stage 3's checkpoint writes them all o
|
|
|
69
69
|
| `step_index` | yes | The position to resume from. 0 = nothing run yet; N = N steps completed. |
|
|
70
70
|
| `invocations` | yes | Append-only list. Each step's invocation appears here. |
|
|
71
71
|
| `verifications` | yes | Subset of invocations[] limited to evaluation steps (audit, critique). |
|
|
72
|
-
| `state` | yes | One of the
|
|
72
|
+
| `state` | yes | One of the six terminal/transitional states. |
|
|
73
73
|
|
|
74
74
|
Optional fields:
|
|
75
75
|
- `register`, `updated_at`, `next_actions` — present when the orchestration has produced them; absent before Stage 1 completes the capture.
|
|
@@ -77,7 +77,7 @@ Optional fields:
|
|
|
77
77
|
|
|
78
78
|
## Terminal states
|
|
79
79
|
|
|
80
|
-
The
|
|
80
|
+
The six values of `state` and their meaning:
|
|
81
81
|
|
|
82
82
|
| State | Meaning | Caller action |
|
|
83
83
|
|---|---|---|
|
|
@@ -86,6 +86,7 @@ The five values of `state` and their meaning:
|
|
|
86
86
|
| `needs_human` | Loop cap fired (3 iterations on audit→polish or critique-driven refine). P1 issues remain unresolved. | Caller decides whether to surface and stop, or warn and continue. Per `/tdd` Step 6 policy: warn and continue. |
|
|
87
87
|
| `blocked` | A gate fired. P0 blockers, register conflict declined, target_files parent missing, malformed state file, user refused recipe. | Caller surfaces the reason and stops the immediate flow. |
|
|
88
88
|
| `not_a_design_task` | Stage 0 classified the intent as development or copy. Set on the first checkpoint write of the orchestration. | Caller routes to `correct_lane` (`/tdd` or `/document`). |
|
|
89
|
+
| `mixed_brief` | Stage 0 classified the task_brief as spanning ≥ 2 lanes (multi-lane misroute). Set on the first checkpoint write of the orchestration; `lane_split` is persisted alongside. | Caller reads `lane_split` and fans out per row; see `references/orchestration.md` caller-policy. |
|
|
89
90
|
|
|
90
91
|
## Resume logic
|
|
91
92
|
|
|
@@ -99,6 +100,7 @@ On any `Skill(design-ui, task_brief)` invocation:
|
|
|
99
100
|
- **Present and `state` is `needs_human`** → caller must signal intent to resume. If `task_brief` is identical to the stored intent and the caller is re-invoking explicitly, restart the loop from `step_index` (which points at the audit that fired the cap). The user is asserting that conditions have changed (e.g., P1 issues were addressed externally) and the loop should re-run.
|
|
100
101
|
- **Present and `state` is `blocked`** → return the existing Report with the blocker reason. User must materially change the input (re-state intent, expand write_set, etc.) before progress.
|
|
101
102
|
- **Present and `state` is `not_a_design_task`** → return the existing Report; the misroute is sticky for this slug.
|
|
103
|
+
- **Present and `state` is `mixed_brief`** → return the existing Report (including the cached `lane_split`); the misroute is sticky for this slug (mirrors `not_a_design_task`). Delete the state file to re-classify.
|
|
102
104
|
- **Present but malformed JSON** → return `Report { final_state: "blocked", reason: "state file malformed", state_file }`. Do NOT overwrite — preserve for human inspection.
|
|
103
105
|
|
|
104
106
|
The resume rule: **skip steps prior to `step_index`; start at `step_index`**. Completed steps are never re-run unless the user explicitly requests it (e.g., by deleting the state file and re-invoking).
|