@massa-ai/cursor-plugin 1.19.0 → 1.21.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/.cursor-plugin/plugin.json +1 -1
- package/package.json +1 -1
- package/skills/agents/audit-specialist/SKILL.md +1 -1
- package/skills/agents/context-curator/SKILL.md +1 -1
- package/skills/agents/furps-analyst/SKILL.md +1 -1
- package/skills/agents/investigator/SKILL.md +1 -1
- package/skills/agents/mobile-specialist/SKILL.md +1 -1
- package/skills/agents/navigator/SKILL.md +1 -1
- package/skills/agents/requirements-analyst/SKILL.md +1 -1
- package/skills/agents/reviewer/SKILL.md +1 -1
- package/skills/agents/verification-agent/SKILL.md +1 -1
- package/skills/massa-ai/SKILL.md +10 -0
- package/skills/massa-ai/references/implementation-delivery.md +12 -1
- package/skills/massa-ai/references/spec-driven/coding-principles.md +16 -0
- package/skills/massa-ai/references/spec-driven/design.md +2 -2
- package/skills/massa-ai/references/spec-driven/discuss.md +35 -12
- package/skills/massa-ai/references/spec-driven/execute.md +41 -24
- package/skills/massa-ai/references/spec-driven/memory.md +12 -2
- package/skills/massa-ai/references/spec-driven/specify.md +30 -12
- package/skills/massa-ai/references/spec-driven/sub-agents.md +33 -6
- package/skills/massa-ai/references/spec-driven/tasks.md +8 -6
- package/skills/massa-ai/references/spec-driven/validate.md +15 -10
- package/skills/massa-ai/scripts/check_commit.py +128 -0
- package/skills/massa-ai/scripts/check_specs_delivered.py +137 -0
- package/skills/massa-ai/scripts/lessons.py +44 -4
- package/skills/massa-ai/scripts/validate_spec.py +272 -0
- package/skills/massa-ai/scripts/validate_state.py +183 -0
- package/skills/massa-ai/scripts/validate_tasks.py +302 -0
- package/skills/massa-ai/workflows/exploration.md +1 -1
- package/skills/massa-ai/workflows/spec-driven.md +5 -4
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
validate_tasks.py - deterministic pre-approval checks for a feature tasks.md.
|
|
4
|
+
|
|
5
|
+
Turns the three pre-approval checks (task granularity, diagram-vs-definition
|
|
6
|
+
cross-check, test co-location) into a checkable pass/fail run BEFORE tasks are
|
|
7
|
+
presented for approval, instead of trusting the model to build the tables by
|
|
8
|
+
hand. Pure standard library, zero dependencies. Operates only on the tasks.md
|
|
9
|
+
markdown artifact, so it is stack-agnostic and tool-agnostic.
|
|
10
|
+
|
|
11
|
+
What it checks (heuristic markdown inspection, not a full parser):
|
|
12
|
+
ERROR - a required section is missing
|
|
13
|
+
ERROR - a task is missing its `Tests` or `Gate` field
|
|
14
|
+
ERROR - a task depends on a task in a LATER phase (dependencies point back only)
|
|
15
|
+
ERROR - a dependency edge shown in the diagram has no matching `Depends on`
|
|
16
|
+
(and vice-versa) when both sides are parseable
|
|
17
|
+
WARN - a task's `Where` names multiple files (granularity smell -> split it)
|
|
18
|
+
WARN - a task says `Tests: none` (confirm the coverage matrix agrees)
|
|
19
|
+
WARN - the diagram could not be parsed confidently (cross-check skipped)
|
|
20
|
+
|
|
21
|
+
Usage:
|
|
22
|
+
python3 skills/massa-ai/scripts/validate_tasks.py [target] [--root DIR] [--strict]
|
|
23
|
+
|
|
24
|
+
Invoke with the repo-root-relative script path shown above (matches
|
|
25
|
+
lessons.py's convention), not a project-local copy.
|
|
26
|
+
target Path to a tasks.md, a feature directory, or a project root.
|
|
27
|
+
Omitted -> auto-detect the single feature under <root>/.specs/features/.
|
|
28
|
+
--root Project root that contains .specs/ (default: current dir).
|
|
29
|
+
--strict Treat warnings as errors.
|
|
30
|
+
|
|
31
|
+
Exit codes: 0 pass, 1 errors found (or warnings under --strict), 2 usage error.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
import argparse
|
|
35
|
+
import os
|
|
36
|
+
import re
|
|
37
|
+
import sys
|
|
38
|
+
|
|
39
|
+
REQUIRED_SECTIONS = ["Test Coverage Matrix", "Gate Check Commands", "Execution Plan", "Task Breakdown"]
|
|
40
|
+
# Task ids are `T<n>` plus optional letter prefix (`FT3` for fix tasks): an
|
|
41
|
+
# unrecognized `### FT3:` header would fold its fields into the previous task's
|
|
42
|
+
# record and misreport e.g. a self-dependency (IT2-01).
|
|
43
|
+
TASK_RE = re.compile(r"^#{2,4}\s+([A-Z]*T\d+)\s*:", re.IGNORECASE)
|
|
44
|
+
EDGE_RE = re.compile(r"\b[A-Z]*T\d+\b", re.IGNORECASE)
|
|
45
|
+
FILE_HINT_RE = re.compile(r"[\w./-]+\.\w{1,6}\b")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def resolve_tasks(target, root):
|
|
49
|
+
if target:
|
|
50
|
+
if os.path.isfile(target):
|
|
51
|
+
return target
|
|
52
|
+
if os.path.isdir(target):
|
|
53
|
+
cand = os.path.join(target, "tasks.md")
|
|
54
|
+
if os.path.isfile(cand):
|
|
55
|
+
return cand
|
|
56
|
+
return _autodetect(target)
|
|
57
|
+
# Not a path: treat as a feature name under <root>/.specs/features/<name>/
|
|
58
|
+
cand = os.path.join(root, ".specs", "features", target, "tasks.md")
|
|
59
|
+
if os.path.isfile(cand):
|
|
60
|
+
return cand
|
|
61
|
+
return None
|
|
62
|
+
return _autodetect(root)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _autodetect(root):
|
|
66
|
+
base = os.path.join(root, ".specs", "features")
|
|
67
|
+
if not os.path.isdir(base):
|
|
68
|
+
return None
|
|
69
|
+
features = [d for d in sorted(os.listdir(base)) if os.path.isfile(os.path.join(base, d, "tasks.md"))]
|
|
70
|
+
if len(features) == 1:
|
|
71
|
+
return os.path.join(base, features[0], "tasks.md")
|
|
72
|
+
if len(features) == 0:
|
|
73
|
+
return None
|
|
74
|
+
raise SystemExit(
|
|
75
|
+
"validate_tasks: multiple features found; pass one explicitly:\n "
|
|
76
|
+
+ "\n ".join(os.path.join(base, f, "tasks.md") for f in features)
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def section_present(lines, name):
|
|
81
|
+
return any(re.match(r"^#{1,4}\s+" + re.escape(name) + r"\b", ln.strip()) for ln in lines)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def parse_tasks(lines):
|
|
85
|
+
"""Return a dict: task_id -> {'deps': set, 'tests': str|None, 'gate': str|None, 'where': str}."""
|
|
86
|
+
tasks = {}
|
|
87
|
+
current = None
|
|
88
|
+
for ln in lines:
|
|
89
|
+
m = TASK_RE.match(ln.strip())
|
|
90
|
+
if m:
|
|
91
|
+
current = m.group(1).upper()
|
|
92
|
+
tasks[current] = {"deps": set(), "tests": None, "gate": None, "where": ""}
|
|
93
|
+
continue
|
|
94
|
+
if current is None:
|
|
95
|
+
continue
|
|
96
|
+
stripped = ln.strip()
|
|
97
|
+
dm = re.match(r"^\*{0,2}Depends on\*{0,2}\s*:\s*(.*)$", stripped, re.IGNORECASE)
|
|
98
|
+
if dm:
|
|
99
|
+
body = dm.group(1)
|
|
100
|
+
if "none" not in body.lower():
|
|
101
|
+
for e in EDGE_RE.findall(body.upper()):
|
|
102
|
+
tasks[current]["deps"].add(e)
|
|
103
|
+
wm = re.match(r"^\*{0,2}Where\*{0,2}\s*:\s*(.*)$", stripped, re.IGNORECASE)
|
|
104
|
+
if wm:
|
|
105
|
+
tasks[current]["where"] = wm.group(1)
|
|
106
|
+
tm = re.match(r"^\*{0,2}Tests\*{0,2}\s*:\s*(.*)$", stripped, re.IGNORECASE)
|
|
107
|
+
if tm:
|
|
108
|
+
tasks[current]["tests"] = tm.group(1).strip()
|
|
109
|
+
gm = re.match(r"^\*{0,2}Gate\*{0,2}\s*:\s*(.*)$", stripped, re.IGNORECASE)
|
|
110
|
+
if gm:
|
|
111
|
+
tasks[current]["gate"] = gm.group(1).strip()
|
|
112
|
+
return tasks
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
TASK_BREAKDOWN_RE = re.compile(r"^#{1,4}\s+Task Breakdown\b", re.IGNORECASE)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def parse_phase_membership(lines):
|
|
119
|
+
"""Map task_id -> phase index, read from '### Phase N' headers.
|
|
120
|
+
|
|
121
|
+
massa-ai patch (beyond D1): the reference tasks.md template (and TLC's own)
|
|
122
|
+
puts the Execution Plan (phase headers + a diagram/list of the task IDs in
|
|
123
|
+
each phase) BEFORE the separate Task Breakdown section (a flat list of
|
|
124
|
+
"### Tn:" task headers with no phase sub-headers). Upstream mapped a task
|
|
125
|
+
to a phase only by which "### Tn:" HEADER line followed the most-recently-
|
|
126
|
+
seen "### Phase N" header while scanning the WHOLE file - so with that
|
|
127
|
+
template shape every task header in Task Breakdown inherits the LAST phase
|
|
128
|
+
index left over from Execution Plan, and the forward-phase-dependency
|
|
129
|
+
check (SYNC-01 AC2) never fires. Confirmed against this feature's own
|
|
130
|
+
tasks.md (whose Phase headers happen to sit directly inside Task
|
|
131
|
+
Breakdown, immediately before their tasks) still passing, and a
|
|
132
|
+
template-shaped fixture then found 0/18 forward-phase violations
|
|
133
|
+
detectable when it should catch a deliberately-introduced one.
|
|
134
|
+
|
|
135
|
+
Fix: read membership from the Execution Plan's diagram/plain-list content
|
|
136
|
+
(bare `Tn` tokens under a `### Phase N` heading) as the authoritative
|
|
137
|
+
signal there, and fall back to the header-based signal (`setdefault`,
|
|
138
|
+
never overwriting) once "## Task Breakdown" is reached - which is also
|
|
139
|
+
exactly what the ORIGINAL algorithm already got right for tasks.md files
|
|
140
|
+
(like this feature's own) that interleave phase headers directly inside
|
|
141
|
+
Task Breakdown. Diagram-style scanning is deliberately NOT applied inside
|
|
142
|
+
Task Breakdown, preserving the original comment's concern: a `Depends on:`
|
|
143
|
+
line inside one task's block often names a task from an EARLIER phase and
|
|
144
|
+
must never be misattributed to the current phase.
|
|
145
|
+
"""
|
|
146
|
+
membership = {}
|
|
147
|
+
phase_idx = 0
|
|
148
|
+
in_phase = False
|
|
149
|
+
in_task_breakdown = False
|
|
150
|
+
for ln in lines:
|
|
151
|
+
stripped = ln.strip()
|
|
152
|
+
if TASK_BREAKDOWN_RE.match(stripped):
|
|
153
|
+
in_task_breakdown = True
|
|
154
|
+
pm = re.match(r"^#{2,4}\s+Phase\s+(\d+)", stripped, re.IGNORECASE)
|
|
155
|
+
if pm:
|
|
156
|
+
phase_idx = int(pm.group(1))
|
|
157
|
+
in_phase = True
|
|
158
|
+
continue
|
|
159
|
+
if not in_phase:
|
|
160
|
+
continue
|
|
161
|
+
hm = TASK_RE.match(stripped)
|
|
162
|
+
if hm:
|
|
163
|
+
membership.setdefault(hm.group(1).upper(), phase_idx)
|
|
164
|
+
continue
|
|
165
|
+
if not in_task_breakdown:
|
|
166
|
+
for tid in EDGE_RE.findall(stripped.upper()):
|
|
167
|
+
membership[tid] = phase_idx
|
|
168
|
+
return membership
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def parse_diagram_order(lines):
|
|
172
|
+
"""Best-effort: parse 'Tx -> Ty -> Tz' arrow chains from fenced blocks into
|
|
173
|
+
an ordering position per task (position increases along each chain).
|
|
174
|
+
|
|
175
|
+
massa-ai patch (beyond D1): upstream compared the diagram against `Depends
|
|
176
|
+
on` as an exact edge set (`Tx -> Ty` in the diagram requires literally
|
|
177
|
+
`Depends on: Tx` on Ty and vice-versa). Both massa-ai's tasks.md reference
|
|
178
|
+
template AND TLC's own upstream template violate that under a real fill-in
|
|
179
|
+
- e.g. the upstream example diagrams `T1 -> T2 -> T3` while T3's `Depends
|
|
180
|
+
on` is `T1`, not `T2`, because the diagram documents *execution order*
|
|
181
|
+
("tasks execute sequentially within a phase"), not a literal dependency
|
|
182
|
+
graph; the real graph lives in each task's `Depends on` field. Re-running
|
|
183
|
+
the strict edge check against this feature's own live tasks.md as its T2
|
|
184
|
+
fixture (as this task requires) failed with 16 false positives, confirming
|
|
185
|
+
the defect is not cosmetic. This function instead returns each task's
|
|
186
|
+
position in its diagram chain; `check()` below verifies the weaker, correct
|
|
187
|
+
invariant: every `Depends on` edge inside one phase must point to a task
|
|
188
|
+
that appears no later in that phase's diagram order.
|
|
189
|
+
Returns (positions: dict[str,int], parsed: bool)."""
|
|
190
|
+
positions = {}
|
|
191
|
+
in_fence = False
|
|
192
|
+
found_any_arrow = False
|
|
193
|
+
for ln in lines:
|
|
194
|
+
if ln.strip().startswith("```"):
|
|
195
|
+
in_fence = not in_fence
|
|
196
|
+
continue
|
|
197
|
+
if not in_fence:
|
|
198
|
+
continue
|
|
199
|
+
# normalize arrow glyphs
|
|
200
|
+
norm = ln.replace("→", "->").replace("──", "-").replace("-", "-")
|
|
201
|
+
if "->" not in norm:
|
|
202
|
+
continue
|
|
203
|
+
# only treat as a chain if arrows connect them left-to-right
|
|
204
|
+
segments = [s for s in re.split(r"->", norm)]
|
|
205
|
+
seq = []
|
|
206
|
+
for seg in segments:
|
|
207
|
+
ids = EDGE_RE.findall(seg.upper())
|
|
208
|
+
seq.append(ids[-1] if ids else None)
|
|
209
|
+
seq = [s for s in seq if s]
|
|
210
|
+
if len(seq) >= 2:
|
|
211
|
+
found_any_arrow = True
|
|
212
|
+
for idx, tid in enumerate(seq):
|
|
213
|
+
positions[tid] = idx
|
|
214
|
+
return positions, found_any_arrow
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def check(tasks_path):
|
|
218
|
+
with open(tasks_path, "r", encoding="utf-8") as f:
|
|
219
|
+
lines = f.read().splitlines()
|
|
220
|
+
errors, warnings = [], []
|
|
221
|
+
|
|
222
|
+
for name in REQUIRED_SECTIONS:
|
|
223
|
+
if not section_present(lines, name):
|
|
224
|
+
errors.append(f"missing required section: ## {name}")
|
|
225
|
+
|
|
226
|
+
tasks = parse_tasks(lines)
|
|
227
|
+
if not tasks:
|
|
228
|
+
warnings.append("no tasks (### T1: ...) parsed - is this file filled in?")
|
|
229
|
+
return errors, warnings
|
|
230
|
+
|
|
231
|
+
# Field presence + granularity smell.
|
|
232
|
+
for tid, t in tasks.items():
|
|
233
|
+
if t["tests"] is None:
|
|
234
|
+
errors.append(f"{tid}: missing `Tests` field")
|
|
235
|
+
elif t["tests"].lower().startswith("none"):
|
|
236
|
+
warnings.append(f"{tid}: Tests: none - confirm the Test Coverage Matrix says 'none' for this layer")
|
|
237
|
+
if t["gate"] is None:
|
|
238
|
+
errors.append(f"{tid}: missing `Gate` field")
|
|
239
|
+
files = FILE_HINT_RE.findall(t["where"])
|
|
240
|
+
if len(set(files)) > 1:
|
|
241
|
+
warnings.append(f"{tid}: `Where` names multiple files {sorted(set(files))} - granularity smell, consider splitting")
|
|
242
|
+
|
|
243
|
+
# Forward-phase dependency.
|
|
244
|
+
membership = parse_phase_membership(lines)
|
|
245
|
+
for tid, t in tasks.items():
|
|
246
|
+
p_here = membership.get(tid)
|
|
247
|
+
if p_here is None:
|
|
248
|
+
continue
|
|
249
|
+
for dep in t["deps"]:
|
|
250
|
+
p_dep = membership.get(dep)
|
|
251
|
+
if p_dep is not None and p_dep > p_here:
|
|
252
|
+
errors.append(f"{tid} (phase {p_here}) depends on {dep} (phase {p_dep}) - dependencies must point backward or within the same phase")
|
|
253
|
+
|
|
254
|
+
# Diagram vs definition cross-check (best effort, order-consistency - see
|
|
255
|
+
# parse_diagram_order's docstring for why this is not exact-edge equality).
|
|
256
|
+
positions, parsed = parse_diagram_order(lines)
|
|
257
|
+
if not parsed:
|
|
258
|
+
warnings.append("diagram arrows not parsed confidently - diagram/definition cross-check skipped (verify by hand)")
|
|
259
|
+
else:
|
|
260
|
+
for tid, t in tasks.items():
|
|
261
|
+
p_here = membership.get(tid)
|
|
262
|
+
if tid not in positions:
|
|
263
|
+
continue
|
|
264
|
+
for dep in sorted(t["deps"]):
|
|
265
|
+
if dep not in positions:
|
|
266
|
+
continue
|
|
267
|
+
p_dep = membership.get(dep)
|
|
268
|
+
if p_dep is None or p_here is None or p_dep != p_here:
|
|
269
|
+
continue # cross-phase; forward-phase check above already covers ordering
|
|
270
|
+
if positions[dep] >= positions[tid]:
|
|
271
|
+
errors.append(
|
|
272
|
+
f"{tid} declares `Depends on: {dep}` but the phase diagram shows {dep} "
|
|
273
|
+
f"at or after {tid}, not before it"
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
return errors, warnings
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def main(argv=None):
|
|
280
|
+
p = argparse.ArgumentParser(prog="validate_tasks.py", description="Pre-approval checks for a feature tasks.md.")
|
|
281
|
+
p.add_argument("target", nargs="?", default=None)
|
|
282
|
+
p.add_argument("--root", default=".")
|
|
283
|
+
p.add_argument("--strict", action="store_true")
|
|
284
|
+
args = p.parse_args(argv)
|
|
285
|
+
|
|
286
|
+
tasks_path = resolve_tasks(args.target, args.root)
|
|
287
|
+
if not tasks_path:
|
|
288
|
+
print("validate_tasks: could not locate a tasks.md. Pass a path or run from the project root.", file=sys.stderr)
|
|
289
|
+
return 2
|
|
290
|
+
|
|
291
|
+
errors, warnings = check(tasks_path)
|
|
292
|
+
for w in warnings:
|
|
293
|
+
print(f" WARN {w}")
|
|
294
|
+
for e in errors:
|
|
295
|
+
print(f" ERROR {e}")
|
|
296
|
+
fail = errors or (warnings and args.strict)
|
|
297
|
+
print(f"\nvalidate_tasks: {len(errors)} error(s), {len(warnings)} warning(s) in {tasks_path}")
|
|
298
|
+
return 1 if fail else 0
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
if __name__ == "__main__":
|
|
302
|
+
raise SystemExit(main())
|
|
@@ -22,7 +22,7 @@ When researching or resolving any technical question during exploration, follow
|
|
|
22
22
|
|
|
23
23
|
```
|
|
24
24
|
Step 1: Codebase → existing code, conventions, patterns already in use
|
|
25
|
-
Step 2: Project docs → README, docs/, inline comments, .specs/project/STATE.md (Decisions)
|
|
25
|
+
Step 2: Project docs (leads, not truth) → README, docs/, inline comments, .specs/project/STATE.md (Decisions) — verify against current source before relying
|
|
26
26
|
Step 3: Context7 MCP → resolve library ID, then query for current API/patterns
|
|
27
27
|
Step 4: Web search → official docs, reputable sources, community patterns
|
|
28
28
|
Step 5: Flag as uncertain → "I'm not certain about X — here's my reasoning, but verify"
|
|
@@ -27,6 +27,7 @@ Holds for every task, even if reference files are not opened:
|
|
|
27
27
|
2. The gate must pass (tests pass) before a task is done — the test runner decides, not self-assessment.
|
|
28
28
|
3. One atomic commit per task. Never batch tasks; never weaken, skip, or delete tests to make them pass.
|
|
29
29
|
4. After the last task, a fresh verification-agent always runs automatically (author ≠ verifier) — spec-anchored outcome check plus discrimination sensor. Never optional, never prompted.
|
|
30
|
+
5. **Blast radius (approval ≠ remote authority):** Approving Execute for this feature authorizes local implementation and local commits, and covers one delivery through PR creation — branch push and `gh pr create` — under one explicit go-ahead given at Execute start. Force-push, deploy, production database changes, merges, and any other remote/externally-visible/destructive operation always require a separate explicit go-ahead, even after that authorization.
|
|
30
31
|
|
|
31
32
|
## Auto-Sizing
|
|
32
33
|
|
|
@@ -39,7 +40,7 @@ Complexity determines depth, not a fixed pipeline. Assess scope first, apply onl
|
|
|
39
40
|
| Large | >10 tasks OR multi-component feature | Full spec + requirement IDs | Architecture + components | Full breakdown + deps | Implement + verify per task |
|
|
40
41
|
| Complex | Ambiguity or new domain (unfamiliar vocabulary, no prior pattern) | Full spec + discuss gray areas | Research + architecture | Breakdown + phase plan | Implement + interactive UAT |
|
|
41
42
|
|
|
42
|
-
A "phase" is a group of tasks sharing a dependency boundary or a checkpoint commit — it is distinct from a single task or atomic step. The sub-agent offer fires when a formal `tasks.md`
|
|
43
|
+
A "phase" is a group of tasks sharing a dependency boundary or a checkpoint commit — it is distinct from a single task or atomic step. The sub-agent offer fires when a formal `tasks.md` has more than 3 tasks — packing itself still uses ~7-task batches; a 4–8-task feature is offered as a single batch worker.
|
|
43
44
|
|
|
44
45
|
- Specify and Execute are always required.
|
|
45
46
|
- Design is skipped when straightforward (no architectural decisions, no new patterns).
|
|
@@ -93,7 +94,7 @@ Quick artifacts live under `.specs/quick/NNN-slug/` with a `TASK.md` (one-line i
|
|
|
93
94
|
- Run repo-rules discovery from `references/repo-rules-discovery.md` before the first repository mutation: record the harness sources loaded (or `repo-rules: none present`), and implement so every new or changed file conforms to the target repo's module layout, unit-test location, and testing-area conventions. A repo rule wins over a skill default for placement and gate commands; record any deviation with an explicit reason. Never fabricate rules or create `.claude/`/`.cursor/` directories the repo lacks.
|
|
94
95
|
- Use the Test Coverage Matrix and Gate Check Commands from `tasks.md`, or state their inline equivalents when Tasks was skipped.
|
|
95
96
|
- Ask the MCP and skill question in Tasks or inline Execute when tool choice can change correctness or verification.
|
|
96
|
-
- If a formal `tasks.md`
|
|
97
|
+
- If a formal `tasks.md` has more than 3 tasks, present the sub-agent offer from `references/spec-driven/sub-agents.md` before starting Execute — even when packing yields a single batch (a 4–8-task feature is offered as one batch worker). Offer-then-confirm — never auto-spawn; the user must accept before any sub-agent is dispatched. One worker per batch (~7 tasks, whole phases): each batch worker executes all its tasks in order (implement → gate → atomic commit), then reports a compact summary (tasks done, commit hashes, test counts, deviations). Workers never spawn further sub-agents.
|
|
97
98
|
- Implement one atomic step or approved task at a time.
|
|
98
99
|
- For long-running task sequences, create a checkpoint via `create_checkpoint` at task boundaries with `taskId`, `description`, `progressPercent`, `currentStep`, `nextAction`, `fileChanges`, and `checkpointType: "manual"` so progress is resumable after interruption.
|
|
99
100
|
- If resuming after interruption, call `list_checkpoints` with the `taskId` and `restore_checkpoint` to recover task state before continuing. If `create_checkpoint` is unavailable (e.g. `task_checkpoints` table missing), continue with `.specs/` artifact state as the fallback.
|
|
@@ -116,7 +117,7 @@ Quick artifacts live under `.specs/quick/NNN-slug/` with a `TASK.md` (one-line i
|
|
|
116
117
|
- The verification-agent re-derives coverage independently using evidence-or-zero and does not inherit the author's mental model.
|
|
117
118
|
- The fix → re-verify loop is capped at 3 iterations before escalating to `Blocked`.
|
|
118
119
|
- Distill lesson signals through `references/lessons.md` when validation produces grounded reusable failures.
|
|
119
|
-
7.
|
|
120
|
+
7. Before the delivery chain's Propose stage (PR creation), write and commit `.specs/project/STATE.md`, `.specs/HANDOFF.md`, and `.specs/project/FEATURES.json` on the branch — not merely "after meaningful progress" during Execute, but committed before `gh pr create`. **Deterministic backing (run it, do not eyeball it):** `python3 skills/massa-ai/scripts/check_specs_delivered.py <feature> [--root .]` — a non-zero exit blocks Propose (see `references/implementation-delivery.md` stage 3.5 and GATE-02). If no code-execution tool is available, run the same checks by reading the artifact (graceful degradation preserved). Record `references/spec-driven/memory.md` decisions, blockers, handoff, and completion evidence per that reference's write triggers.
|
|
120
121
|
8. When the user splits planning and implementation across clean chats, resume from the canonical `.specs/` artifacts — `.specs/project/STATE.md`, `.specs/project/FEATURES.json`, `.specs/HANDOFF.md`, and the feature's phase files. This workflow owns the spec phase contracts on both sides of the split; there is no separate save/load procedure.
|
|
121
122
|
9. Complete the configured Plan Challenge Gate for non-trivial plans and complete `references/evidence-gate.md` before claiming completion.
|
|
122
123
|
|
|
@@ -150,7 +151,7 @@ When researching, designing, or making any technical decision, follow this chain
|
|
|
150
151
|
|
|
151
152
|
```
|
|
152
153
|
Step 1: Codebase → existing code, conventions, patterns already in use
|
|
153
|
-
Step 2: Project docs → README, docs/, inline comments, .specs/project/STATE.md (Decisions)
|
|
154
|
+
Step 2: Project docs (leads, not truth) → README, docs/, inline comments, .specs/project/STATE.md (Decisions) — verify against current source before relying
|
|
154
155
|
Step 3: Context7 MCP → resolve library ID, then query for current API/patterns
|
|
155
156
|
Step 4: Web search → official docs, reputable sources, community patterns
|
|
156
157
|
Step 5: Flag as uncertain → "I'm not certain about X — here's my reasoning, but verify"
|