@caddis/cli 0.4.70 → 0.4.71
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/bundles/antigravity-plugin/plugin.json +1 -1
- package/bundles/antigravity-plugin/scripts/caddis_coordinate.py +1083 -0
- package/bundles/antigravity-plugin/scripts/caddis_upstream_check.py +107 -0
- package/bundles/antigravity-plugin/scripts/schemas/coordinator.schema.json +95 -0
- package/bundles/antigravity-plugin/scripts/schemas/request.schema.json +39 -0
- package/bundles/antigravity-plugin/skills/_registry.md +1 -1
- package/bundles/antigravity-plugin/skills/brainstorming/SKILL.md +1 -1
- package/bundles/antigravity-plugin/skills/feature-plan/SKILL.md +64 -1
- package/bundles/antigravity-plugin/skills/implement/SKILL.md +14 -0
- package/bundles/antigravity-plugin/skills/usage-review/SKILL.md +9 -0
- package/bundles/antigravity-plugin/skills/writing-plans/SKILL.md +15 -375
- package/bundles/codex/.codex/skills/docs/writing-plans/SKILL.md +15 -375
- package/bundles/codex/.codex/skills/workflow/brainstorming/SKILL.md +1 -1
- package/bundles/manifest.json +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""caddis_upstream_check - say when an upstream skill that caddis borrowed ideas from has changed.
|
|
3
|
+
|
|
4
|
+
Why: /caddis:feature-plan and /caddis:implement adapt ideas from obra/superpowers (writing-plans 7.0.0,
|
|
5
|
+
subagent-driven-development 7.0.0, MIT). `.github/upstream-sources.json` records, per borrowed file, the
|
|
6
|
+
upstream version and the sha256 of the file we read. This script compares that record with the copies
|
|
7
|
+
installed in the Claude Code plugin cache (`~/.claude/plugins/cache/<marketplace>/<plugin>/<version>/<path>`),
|
|
8
|
+
so caddis does not fall behind upstream without anyone seeing it. /caddis:usage-review runs it.
|
|
9
|
+
|
|
10
|
+
One line per source:
|
|
11
|
+
UP-TO-DATE <id> <version>
|
|
12
|
+
DRIFT <id> recorded <v> installed <v'> (sha differs): review the upstream diff and port what fits, ...
|
|
13
|
+
NOT-INSTALLED <id>
|
|
14
|
+
`NO-SOURCES <path>` means there is no record (any repo other than the caddis source repo).
|
|
15
|
+
|
|
16
|
+
Advisory only: the exit code is always 0. Stdlib only.
|
|
17
|
+
|
|
18
|
+
Usage:
|
|
19
|
+
python caddis_upstream_check.py [--sources <json>] [--cache-root <dir>] [--json]
|
|
20
|
+
"""
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import argparse
|
|
24
|
+
import hashlib
|
|
25
|
+
import json
|
|
26
|
+
import sys
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
|
|
29
|
+
SOURCES_REL = Path(".github") / "upstream-sources.json"
|
|
30
|
+
DRIFT_ADVICE = "review the upstream diff and port what fits, then update .github/upstream-sources.json"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def default_sources() -> Path:
|
|
34
|
+
"""The record in the current repo; else the one next to this script in a source checkout."""
|
|
35
|
+
here = Path.cwd() / SOURCES_REL
|
|
36
|
+
if here.is_file():
|
|
37
|
+
return here
|
|
38
|
+
return Path(__file__).resolve().parents[1] / SOURCES_REL
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def default_cache_root() -> Path:
|
|
42
|
+
return Path.home() / ".claude" / "plugins" / "cache"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _version_key(version: str) -> tuple:
|
|
46
|
+
"""Numeric parts compare as numbers (7.10.0 > 7.9.0); a non-numeric part sorts before any number."""
|
|
47
|
+
return tuple((1, int(p)) if p.isdigit() else (0, p) for p in version.split("."))
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _sha256(path: Path) -> str:
|
|
51
|
+
return hashlib.sha256(path.read_bytes()).hexdigest()
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def installed_copies(cache_root: Path, plugin: str, rel_path: str) -> list[tuple[str, Path]]:
|
|
55
|
+
"""(version, file) for every installed copy, newest version last."""
|
|
56
|
+
if not cache_root.is_dir():
|
|
57
|
+
return []
|
|
58
|
+
found = [(f.parents[len(Path(rel_path).parts) - 1].name, f)
|
|
59
|
+
for f in cache_root.glob(f"*/{plugin}/*/{rel_path}") if f.is_file()]
|
|
60
|
+
return sorted(found, key=lambda vf: _version_key(vf[0]))
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def check_source(src: dict, cache_root: Path) -> dict:
|
|
64
|
+
sid, recorded = src["id"], src["version"]
|
|
65
|
+
plugin = sid.split(":", 1)[0]
|
|
66
|
+
copies = installed_copies(cache_root, plugin, src["path"])
|
|
67
|
+
row = {"id": sid, "status": "NOT-INSTALLED", "recorded_version": recorded, "installed_version": None,
|
|
68
|
+
"line": f"NOT-INSTALLED {sid}"}
|
|
69
|
+
if not copies:
|
|
70
|
+
return row
|
|
71
|
+
version, newest = copies[-1]
|
|
72
|
+
row["installed_version"] = version
|
|
73
|
+
if _sha256(newest) == src["sha256"]:
|
|
74
|
+
row.update(status="UP-TO-DATE", line=f"UP-TO-DATE {sid} {recorded}")
|
|
75
|
+
else:
|
|
76
|
+
row.update(status="DRIFT",
|
|
77
|
+
line=f"DRIFT {sid} recorded {recorded} installed {version} (sha differs): {DRIFT_ADVICE}")
|
|
78
|
+
return row
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def main(argv: list[str] | None = None) -> int:
|
|
82
|
+
ap = argparse.ArgumentParser(description="Compare borrowed upstream skills with the installed copies.")
|
|
83
|
+
ap.add_argument("--sources", type=Path, default=None, help="path to upstream-sources.json")
|
|
84
|
+
ap.add_argument("--cache-root", type=Path, default=None, help="the Claude Code plugin cache folder")
|
|
85
|
+
ap.add_argument("--json", action="store_true", help="print the rows as a JSON list")
|
|
86
|
+
args = ap.parse_args(argv)
|
|
87
|
+
sources = args.sources or default_sources()
|
|
88
|
+
cache_root = args.cache_root or default_cache_root()
|
|
89
|
+
if not sources.is_file():
|
|
90
|
+
print(f"NO-SOURCES {sources}")
|
|
91
|
+
return 0
|
|
92
|
+
try:
|
|
93
|
+
data = json.loads(sources.read_text(encoding="utf-8"))
|
|
94
|
+
rows = [check_source(src, cache_root) for src in data.get("sources", [])]
|
|
95
|
+
except (OSError, ValueError, KeyError, TypeError) as exc:
|
|
96
|
+
print(f"NO-SOURCES {sources} (unreadable: {exc})")
|
|
97
|
+
return 0
|
|
98
|
+
if args.json:
|
|
99
|
+
print(json.dumps(rows, indent=2))
|
|
100
|
+
else:
|
|
101
|
+
for row in rows:
|
|
102
|
+
print(row["line"])
|
|
103
|
+
return 0
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
if __name__ == "__main__":
|
|
107
|
+
sys.exit(main())
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "caddis hub coordinator state (coordinator.json)",
|
|
4
|
+
"description": "One file per repo id, written only by caddis_coordinate.py. Readers accept schema N and N-1.",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["schema", "repo", "coordinator_session", "heartbeat_at", "queue", "merging_now", "deploy_checks",
|
|
7
|
+
"ledger", "processed_requests"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"schema": {"type": "integer", "enum": [0, 1]},
|
|
10
|
+
"repo": {"type": "string", "minLength": 1},
|
|
11
|
+
"coordinator_session": {
|
|
12
|
+
"type": "object",
|
|
13
|
+
"required": ["name", "sessionId", "pid"],
|
|
14
|
+
"properties": {
|
|
15
|
+
"name": {"type": ["string", "null"]},
|
|
16
|
+
"sessionId": {"type": ["string", "null"]},
|
|
17
|
+
"pid": {"type": ["integer", "null"]}
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"heartbeat_at": {"$ref": "#/$defs/utc"},
|
|
21
|
+
"queue": {
|
|
22
|
+
"type": "array",
|
|
23
|
+
"items": {
|
|
24
|
+
"type": "object",
|
|
25
|
+
"required": ["pr", "head_sha", "title", "state", "why_waiting", "waits_on", "hotfix", "held",
|
|
26
|
+
"entered_ready_at"],
|
|
27
|
+
"properties": {
|
|
28
|
+
"pr": {"type": "integer", "minimum": 1},
|
|
29
|
+
"head_sha": {"type": "string"},
|
|
30
|
+
"title": {"type": "string"},
|
|
31
|
+
"state": {"enum": ["waiting", "ci", "ready", "merging", "blocked", "held"]},
|
|
32
|
+
"why_waiting": {"type": "string"},
|
|
33
|
+
"waits_on": {
|
|
34
|
+
"type": "array",
|
|
35
|
+
"items": {
|
|
36
|
+
"type": "object",
|
|
37
|
+
"required": ["repo", "pr"],
|
|
38
|
+
"properties": {"repo": {"type": "string"}, "pr": {"type": "integer"}}
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"hotfix": {"type": "boolean"},
|
|
42
|
+
"held": {"type": "boolean"},
|
|
43
|
+
"entered_ready_at": {"anyOf": [{"$ref": "#/$defs/utc"}, {"type": "null"}]}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"merging_now": {"type": ["integer", "object", "null"]},
|
|
48
|
+
"deploy_checks": {
|
|
49
|
+
"type": "array",
|
|
50
|
+
"items": {
|
|
51
|
+
"type": "object",
|
|
52
|
+
"required": ["pr", "merge_sha", "deploy_run", "health", "at"],
|
|
53
|
+
"properties": {
|
|
54
|
+
"pr": {"type": "integer"},
|
|
55
|
+
"merge_sha": {"type": "string", "pattern": "^[0-9a-f]{40}$", "description": "lower-cased"},
|
|
56
|
+
"deploy_run": {"type": ["integer", "null"]},
|
|
57
|
+
"health": {"enum": ["pending", "ok", "failed"]},
|
|
58
|
+
"at": {"$ref": "#/$defs/utc"}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"ledger": {
|
|
63
|
+
"type": "array",
|
|
64
|
+
"items": {
|
|
65
|
+
"type": "object",
|
|
66
|
+
"required": ["what", "decision", "approved_by", "reason", "window", "request_id", "at"],
|
|
67
|
+
"properties": {
|
|
68
|
+
"what": {"type": "string", "minLength": 1},
|
|
69
|
+
"decision": {"enum": ["approve", "decline"]},
|
|
70
|
+
"approved_by": {"enum": ["owner", "coordinator"]},
|
|
71
|
+
"reason": {"type": "string", "minLength": 1},
|
|
72
|
+
"window": {"type": ["string", "null"], "description": "the session window the decision was made in; \"hub\" for an owner click"},
|
|
73
|
+
"request_id": {"type": ["string", "null"], "description": "the owner request id; null when not from a request"},
|
|
74
|
+
"at": {"$ref": "#/$defs/utc"}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
"processed_requests": {
|
|
79
|
+
"type": "array",
|
|
80
|
+
"items": {
|
|
81
|
+
"type": "object",
|
|
82
|
+
"required": ["id", "result", "reason", "at"],
|
|
83
|
+
"properties": {
|
|
84
|
+
"id": {"type": "string"},
|
|
85
|
+
"result": {"enum": ["done", "rejected", "expired"]},
|
|
86
|
+
"reason": {"type": "string"},
|
|
87
|
+
"at": {"$ref": "#/$defs/utc"}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
"$defs": {
|
|
93
|
+
"utc": {"type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$"}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "caddis hub owner request (one line of requests.jsonl)",
|
|
4
|
+
"description": "Appended by the hub, applied by caddis_coordinate.py intake. Readers accept v N and N-1. pr may be null only for approve/decline, and then args.what is required (ruling R2).",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["v", "id", "created_at", "expires_at", "repo", "pr", "head_sha", "action", "args"],
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"properties": {
|
|
9
|
+
"v": {"type": "integer", "enum": [0, 1]},
|
|
10
|
+
"id": {"type": "string", "minLength": 1},
|
|
11
|
+
"created_at": {"$ref": "#/$defs/utc"},
|
|
12
|
+
"expires_at": {"$ref": "#/$defs/utc"},
|
|
13
|
+
"repo": {"type": "string", "minLength": 1},
|
|
14
|
+
"pr": {"type": ["integer", "null"], "minimum": 1},
|
|
15
|
+
"head_sha": {"type": ["string", "null"], "description": "40 hex characters when pr is set; compared case-insensitively"},
|
|
16
|
+
"action": {"enum": ["hotfix", "unhotfix", "hold", "release", "approve", "decline"]},
|
|
17
|
+
"args": {
|
|
18
|
+
"type": "object",
|
|
19
|
+
"properties": {"what": {"type": "string", "minLength": 1}}
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"allOf": [
|
|
23
|
+
{
|
|
24
|
+
"if": {"properties": {"pr": {"type": "null"}}, "required": ["pr"]},
|
|
25
|
+
"then": {
|
|
26
|
+
"properties": {
|
|
27
|
+
"action": {"enum": ["approve", "decline"]},
|
|
28
|
+
"args": {"required": ["what"]}
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"else": {
|
|
32
|
+
"properties": {"head_sha": {"type": "string", "pattern": "^[0-9a-fA-F]{40}$"}}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
],
|
|
36
|
+
"$defs": {
|
|
37
|
+
"utc": {"type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$"}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
| Office Documents Official | `office-documents-official/` | Hint for Office and PDF file work. Use when the user asks to create, edit, read or convert a Word .docx, PDF, PowerPoint .pptx or Excel .xlsx file. caddis does not ship these skills; points to Anthropic's official document-skills plugin. |
|
|
59
59
|
| Tech Explainer | `tech-explainer/` | >- |
|
|
60
60
|
| Technical Writing | `technical-writing/` | Documentation best practices for READMEs, API docs, architecture docs, runbooks and developer guides. Use when writing or reviewing docs or setting documentation standards. |
|
|
61
|
-
| Writing Plans | `writing-plans/` | Use when you have a spec or requirements for a multi-step task, before touching code |
|
|
61
|
+
| Writing Plans | `writing-plans/` | Use when you have a spec or requirements for a multi-step task, before touching code. In caddis, plans are written with /caddis:feature-plan, the only plan writer; this skill sends you there. |
|
|
62
62
|
|
|
63
63
|
### Frontend
|
|
64
64
|
|
|
@@ -45,7 +45,7 @@ Start by understanding the current project context, then ask questions one at a
|
|
|
45
45
|
**Implementation (if continuing):**
|
|
46
46
|
- Ask: "Ready to set up for implementation?"
|
|
47
47
|
- Use the `using-git-worktrees` skill (caddis, devops/) to create an isolated workspace
|
|
48
|
-
- Use `/caddis:feature-plan`
|
|
48
|
+
- Use `/caddis:feature-plan` to write the plan (it is the only plan writer in caddis)
|
|
49
49
|
|
|
50
50
|
## Key Principles
|
|
51
51
|
|
|
@@ -12,6 +12,9 @@ If `$ARGUMENTS` is empty, ask what to plan and stop.
|
|
|
12
12
|
The plan file is the **durable spine of the harness** — it must let any future session (or another
|
|
13
13
|
agent, on any tool) resume with zero re-discovery. Optimize for that.
|
|
14
14
|
|
|
15
|
+
This command is the only plan writer in caddis. The task-level shape, Global Constraints, Review Focus and
|
|
16
|
+
the self-review checklist are adapted from obra/superpowers `writing-plans` 7.0.0 (MIT); see NOTICE.md.
|
|
17
|
+
|
|
15
18
|
## Headless mode
|
|
16
19
|
When the invocation contains the marker **`HEADLESS RUN RULES`**, **or `CADDIS_HEADLESS` is set in
|
|
17
20
|
the environment** (the caddis OSS launchers export it on claude's `-p`/`--print`) — a non-interactive
|
|
@@ -186,6 +189,29 @@ internal repo path into stakeholder-facing copy, and set a `live` status flag th
|
|
|
186
189
|
framework's own "Soon" badge). `tsc` clean, 632/632 tests green, cross-review CLEAN, exit gate
|
|
187
190
|
satisfied. A headless run following it literally would have committed both, entirely green.
|
|
188
191
|
|
|
192
|
+
### Choose how each phase's tasks run (`Execution:`)
|
|
193
|
+
Write one `Execution:` line per phase, next to its `Lane:` and model lines. A fixed rule picks it:
|
|
194
|
+
- **`Execution: per-task`** when the phase has 3 or more tasks that consume each other's interfaces,
|
|
195
|
+
OR when a mistake would be costly: security, data loss, money, merges or deploys, concurrency.
|
|
196
|
+
- Otherwise `Execution: inline`.
|
|
197
|
+
|
|
198
|
+
`inline` means today's behaviour: one implementer does the whole phase. `per-task` means the executor
|
|
199
|
+
gives each task a fresh implementer (on the phase's model tier and lane) and a short task review (spec
|
|
200
|
+
compliance, then quality) before the next task starts. The task review does not replace the phase's review by another vendor:
|
|
201
|
+
`review-tier` and `/caddis:cross-review` still run once per phase.
|
|
202
|
+
|
|
203
|
+
A `per-task` phase is split into tasks. Each task must have:
|
|
204
|
+
- **`Files:`** — every file to create, modify and test, with exact paths.
|
|
205
|
+
- **`Interfaces:`** — `Consumes:` (what it uses from earlier tasks) and `Produces:` (what later tasks
|
|
206
|
+
use): exact names, parameters and return types. An implementer sees only its own task; this block is
|
|
207
|
+
how it learns the names that its neighbours use.
|
|
208
|
+
- **Steps** — write the failing test (its name, its assertions and the exact values from the spec); run
|
|
209
|
+
it, expected FAIL; implement the named signature; run it, expected PASS; commit with named files.
|
|
210
|
+
|
|
211
|
+
A task is the smallest unit that has its own test cycle and is worth its own review. Put setup, config
|
|
212
|
+
and docs steps in the task whose deliverable needs them. An `inline` phase may keep the lighter shape of
|
|
213
|
+
the phase template.
|
|
214
|
+
|
|
189
215
|
### Group phases into MILESTONES — only when the plan is big enough
|
|
190
216
|
**Add milestones when the plan has ~8+ phases OR spans multiple sessions.** Below that, phases and a
|
|
191
217
|
Tracker are enough; a three-phase feature gains nothing from ceremony.
|
|
@@ -240,6 +266,16 @@ Creating Model: <model-id>
|
|
|
240
266
|
## Constraints & decisions
|
|
241
267
|
- <key tech decision + rationale>
|
|
242
268
|
|
|
269
|
+
## Global Constraints
|
|
270
|
+
<The project-wide requirements: version floors, dependency limits, naming and copy rules, platform needs.
|
|
271
|
+
One line each, with the exact values copied from the spec or PRD. Every phase's requirements include
|
|
272
|
+
this section.>
|
|
273
|
+
|
|
274
|
+
## Review Focus
|
|
275
|
+
<Up to five inputs or failure modes that the spec implies but no task test exercises, most likely first.
|
|
276
|
+
One line each: the input or condition, and the behaviour a user expects. Each line gets a test, added
|
|
277
|
+
to the phase or task that owns the code. Write "none found" only after you checked.>
|
|
278
|
+
|
|
243
279
|
## Phases
|
|
244
280
|
|
|
245
281
|
### Phase 1 — <name> ⏳
|
|
@@ -249,6 +285,7 @@ Creating Model: <model-id>
|
|
|
249
285
|
**Model:** <tier> (`/model <alias>`) — <one-line rationale tied to this phase's difficulty>
|
|
250
286
|
**Lane:** claude — this session | glm-headless — `claude-glm -p "/caddis:implement <this plan's path> — Phase 1 only"`
|
|
251
287
|
**Type:** backend | hard | ui | tests | docs *(required when the Lane runs through caddis_lanes.py)*
|
|
288
|
+
**Execution:** inline | per-task *(the rule in Step 2; per-task phases list their tasks below)*
|
|
252
289
|
**Session:** continue | **fresh** (start a new session for this phase — note why: lane change / heavy context)
|
|
253
290
|
**Risk:** high — token-verification seam; a wrong answer here is a silent auth bypass *(omit this line when normal)*
|
|
254
291
|
**Goal:** <one sentence>
|
|
@@ -264,6 +301,18 @@ Creating Model: <model-id>
|
|
|
264
301
|
**Then:** update the Tracker row (status + hash). If this phase ends the session, WRITE the handoff
|
|
265
302
|
yourself (relay + Tracker) before stopping — do not hand back "run `/caddis:handoff`" as the next action.
|
|
266
303
|
|
|
304
|
+
#### Task 1.1 — <name> *(per-task phases only)*
|
|
305
|
+
**Files:** create `<path>` · modify `<path>` · test `<test path>`
|
|
306
|
+
**Interfaces:**
|
|
307
|
+
- Consumes: `<name>(<params>) -> <return type>` from Task <N.M>, or "none"
|
|
308
|
+
- Produces: `<name>(<params>) -> <return type>`
|
|
309
|
+
**Steps:**
|
|
310
|
+
1. Write the failing test `<test path>::<case>`: assert `<call>` == `<exact value from the spec>`.
|
|
311
|
+
2. Run `<literal test command>`: expected FAIL (<the reason, not an import error>).
|
|
312
|
+
3. Implement `<name>(<params>) -> <return type>` in `<path>`.
|
|
313
|
+
4. Run `<literal test command>`: expected PASS.
|
|
314
|
+
5. Commit: `git add <named files>` then `git commit -m "<message>" -- <named files>`.
|
|
315
|
+
|
|
267
316
|
### Phase 2 — <name> 🔲
|
|
268
317
|
<same structure>
|
|
269
318
|
|
|
@@ -278,6 +327,8 @@ Three rules: one attempt, one review.
|
|
|
278
327
|
|
|
279
328
|
- One phase at a time: read this plan → run the phase on its **Lane** → RED→GREEN→VERIFY→CROSS-REVIEW→
|
|
280
329
|
COMMIT → update the **Tracker** row. The Tracker is the resume signal for every future session.
|
|
330
|
+
An `Execution: per-task` phase runs task by task: a fresh implementer per task, and a task review
|
|
331
|
+
before the next task. The cross-review still runs once for the phase.
|
|
281
332
|
- **Session boundaries:** the executor WRITES the handoff (durable `relay.md` + Tracker) after any
|
|
282
333
|
phase that ends a sitting — it performs the work, it does not suggest the command. Start a FRESH
|
|
283
334
|
session (or `/clear`) before any phase marked `Session: fresh`, before a lane change, or whenever
|
|
@@ -360,6 +411,8 @@ done:
|
|
|
360
411
|
spawning another one forever; the `Phase N only` suffix is what stops the child running to the end
|
|
361
412
|
of the plan. The command is executed verbatim by a model with no slack to notice a missing flag,
|
|
362
413
|
and `/caddis:implement` now refuses a command without `-p` rather than running it.
|
|
414
|
+
- **Execution named** — every phase has an `Execution:` line set by the rule in Step 2, and every task
|
|
415
|
+
of a `per-task` phase has `Files:`, `Interfaces:` and its five steps.
|
|
363
416
|
- **caddis_lanes command carries --phase** — a `caddis_lanes.py` launch command must name `--phase N`
|
|
364
417
|
and `--type`; lane-check refuses one without `--phase`.
|
|
365
418
|
- **Gate-detectable** — if this phase's instruction were WRONG, its exit gate would go red. A phase
|
|
@@ -369,6 +422,16 @@ done:
|
|
|
369
422
|
|
|
370
423
|
If any phase relies on the implementer *reasoning out* a gap, close the gap in the plan now.
|
|
371
424
|
|
|
425
|
+
**Self-review (run it yourself after you write the plan, then fix what it finds in place):**
|
|
426
|
+
- **Spec coverage** — each requirement in the spec or PRD maps to a phase or task. Add a task for a gap.
|
|
427
|
+
- **Step scan** — no step that decides nothing ("TBD", "handle edge cases", "add validation", a name
|
|
428
|
+
that no task defines). No full function body that the signature and the tests already determine.
|
|
429
|
+
- **Name consistency** — a name, signature or field in a later task matches the one an earlier task's
|
|
430
|
+
`Produces:` defines.
|
|
431
|
+
- **Review Focus coverage** — each Review Focus line has its test in the task that owns the code.
|
|
432
|
+
- **Proportion** — a plan several times longer than its spec is a transcript of the code. Replace the
|
|
433
|
+
bodies with signatures, test names and assertions.
|
|
434
|
+
|
|
372
435
|
A `risk: high` phase gets two independent review runs: run by the roadmap/plan runner automatically (from caddis-next Phase 11), and by hand in an interactive `/caddis:implement` run (`caddis_review.py --plan <plan> --phase <n> --runs 2`, from Phase 10). Only a `blocking` finding fails that review (exit 1); `should-fix` and `nit` findings go on a follow-up list and do not stop the merge. A phase gets 3 completed review rounds (a run that did not complete uses none); the 4th ends with exit 5, "stop, the owner decides". The advisory hub is retired; do not plan a separate validation session.
|
|
373
436
|
|
|
374
437
|
## Programme triggers
|
|
@@ -381,7 +444,7 @@ Small work that does not meet a trigger skips its block.
|
|
|
381
444
|
|
|
382
445
|
## Step 4 — Report
|
|
383
446
|
Output the plan path (`.caddis/plans/<feature-slug>.md`), the phase list (one line each, with each
|
|
384
|
-
phase's model tier + lane), confirm the local-coder gate passed (or list the phases that need
|
|
447
|
+
phase's model tier + lane + execution), confirm the local-coder gate passed (or list the phases that need
|
|
385
448
|
tightening), and: *"To start: `read the plan and implement Phase 1`. To resume later: `/handoff` at
|
|
386
449
|
session end, then `read relay.md` next time. The plan's Execution protocol carries every command —
|
|
387
450
|
nothing to memorize."*
|
|
@@ -236,6 +236,17 @@ report — do not redo completed work.
|
|
|
236
236
|
every phase after the first, which is the original bug wearing a different hat. For a
|
|
237
237
|
`caddis_lanes.py` launch command, follow "Multi-lane phases" above instead of spawning the command
|
|
238
238
|
blind.
|
|
239
|
+
- **EXECUTION** — read the phase's `Execution:` line. A missing line means `inline` (older plans
|
|
240
|
+
have none). `inline`: run the steps below for the whole phase, as before. `Execution: per-task`:
|
|
241
|
+
run the phase's tasks in order. Give each task a fresh implementer: a subagent on the phase's
|
|
242
|
+
lane and model, with a brief that holds the task's `Files:`, `Interfaces:` and steps, the plan's
|
|
243
|
+
Global Constraints and the test-economy lines (Multi-lane item 7b). If you cannot dispatch a
|
|
244
|
+
subagent, implement the task yourself and note that in the review file. After each task, run a
|
|
245
|
+
short task review before the next task: first spec compliance (the task's files, names and tests
|
|
246
|
+
match the plan), then quality (the author self-check). Fix blocking findings, then go on. Record
|
|
247
|
+
each task (name, commit, review result) under `## Task progress` in the review file. Then run the
|
|
248
|
+
phase's normal gates: VERIFY, the `review-tier` review by another vendor, COMMIT and the Tracker.
|
|
249
|
+
The task reviews never replace that review.
|
|
239
250
|
- **RED** — write the failing test(s) the phase names (`<test file>::<case>`). Run them; confirm they
|
|
240
251
|
fail for the right reason (the missing behavior, not an import error). If a phase genuinely has no
|
|
241
252
|
testable surface, say so in the review file and implement the minimal change directly.
|
|
@@ -348,6 +359,9 @@ branch: <current branch>
|
|
|
348
359
|
## Assumptions made
|
|
349
360
|
- <any gap the plan left that you decided — or "none">
|
|
350
361
|
|
|
362
|
+
## Task progress *(only for `Execution: per-task` phases)*
|
|
363
|
+
- <phase N task N.M — commit <hash> — task review: clean | fixed <finding>>
|
|
364
|
+
|
|
351
365
|
## Not done / follow-ups
|
|
352
366
|
- <phases skipped/blocked and why — or "none; all phases complete">
|
|
353
367
|
|
|
@@ -33,6 +33,15 @@ impactful finding** — the one most likely to reduce rate-limit consumption or
|
|
|
33
33
|
|
|
34
34
|
Do NOT paraphrase every finding in prose — the report is already the output.
|
|
35
35
|
|
|
36
|
+
## Step 2b — check the borrowed upstream skills
|
|
37
|
+
|
|
38
|
+
Run `python "<scripts folder>/caddis_upstream_check.py"` from the project root (same folder as
|
|
39
|
+
`usage_review.py`). It compares the upstream skills that caddis borrowed ideas from with the copies
|
|
40
|
+
installed on this machine. Show every `DRIFT` line in the report under **Upstream drift**. A `DRIFT`
|
|
41
|
+
line means: review the upstream diff, port what fits, then update `.github/upstream-sources.json`.
|
|
42
|
+
Say nothing for `UP-TO-DATE`, `NOT-INSTALLED` or `NO-SOURCES` (no record: not the caddis source repo).
|
|
43
|
+
The check never fails the review.
|
|
44
|
+
|
|
36
45
|
## Step 3 — offer to apply
|
|
37
46
|
|
|
38
47
|
For each finding marked **[apply]** in the report, ask the user whether to apply it.
|