@caddis/cli 0.4.52 → 0.4.54
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/CADDIS-GLOSSARY.md +1 -0
- package/bundles/antigravity-plugin/guard_agy.py +184 -0
- package/bundles/antigravity-plugin/plugin.json +1 -1
- package/bundles/antigravity-plugin/repo_freshness.py +132 -5
- package/bundles/antigravity-plugin/scripts/caddis_budget.py +283 -0
- package/bundles/antigravity-plugin/scripts/caddis_checkout_guard.py +227 -0
- package/bundles/antigravity-plugin/scripts/caddis_docs_pr.py +284 -0
- package/bundles/antigravity-plugin/scripts/caddis_exit.py +2 -0
- package/bundles/antigravity-plugin/scripts/caddis_fleet.py +439 -0
- package/bundles/antigravity-plugin/scripts/caddis_frontmatter.py +1 -0
- package/bundles/antigravity-plugin/scripts/caddis_gate.py +255 -195
- package/bundles/antigravity-plugin/scripts/caddis_glossary.py +3 -3
- package/bundles/antigravity-plugin/scripts/caddis_governor.py +48 -4
- package/bundles/antigravity-plugin/scripts/caddis_inventory.py +38 -0
- package/bundles/antigravity-plugin/scripts/caddis_lanes.py +411 -127
- package/bundles/antigravity-plugin/scripts/caddis_lineage_publish.py +458 -0
- package/bundles/antigravity-plugin/scripts/caddis_merge_chain.py +237 -0
- package/bundles/antigravity-plugin/scripts/caddis_notify.py +125 -0
- package/bundles/antigravity-plugin/scripts/caddis_prod_check.py +380 -0
- package/bundles/antigravity-plugin/scripts/caddis_redact.py +99 -0
- package/bundles/antigravity-plugin/scripts/caddis_relay_git.py +2 -2
- package/bundles/antigravity-plugin/scripts/caddis_review.py +1309 -0
- package/bundles/antigravity-plugin/scripts/caddis_roadmap.py +2109 -0
- package/bundles/antigravity-plugin/scripts/caddis_roadmap_page.py +676 -0
- package/bundles/antigravity-plugin/scripts/caddis_roadmap_run.py +2605 -0
- package/bundles/antigravity-plugin/scripts/caddis_routing.py +7 -6
- package/bundles/antigravity-plugin/scripts/caddis_rows.py +476 -0
- package/bundles/antigravity-plugin/scripts/caddis_spawn.py +8 -9
- package/bundles/antigravity-plugin/scripts/caddis_spend.py +415 -0
- package/bundles/antigravity-plugin/scripts/caddis_tidy.py +357 -20
- package/bundles/antigravity-plugin/scripts/caddis_todo.py +55 -0
- package/bundles/antigravity-plugin/scripts/caddis_workstreams.py +178 -19
- package/bundles/antigravity-plugin/scripts/caddis_worktree_close.py +349 -0
- package/bundles/antigravity-plugin/scripts/check_doc_coverage.py +0 -56
- package/bundles/antigravity-plugin/scripts/claudster_doctor.py +1 -1
- package/bundles/antigravity-plugin/scripts/enforcement_inventory.py +0 -1
- package/bundles/antigravity-plugin/scripts/oss_review.py +2 -2
- package/bundles/antigravity-plugin/scripts/roadmap-recheck.ps1 +89 -0
- package/bundles/antigravity-plugin/scripts/roadmap-run.ps1 +88 -0
- package/bundles/antigravity-plugin/scripts/setup_project_ai.py +43 -1
- package/bundles/antigravity-plugin/session_end_agy.py +3 -3
- package/bundles/antigravity-plugin/skills/_registry.md +4 -6
- package/bundles/antigravity-plugin/skills/best-practices/agents/best-practices-referencer.md +1 -1
- package/bundles/antigravity-plugin/skills/best-practices/agents/codebase-context-builder.md +1 -1
- package/bundles/antigravity-plugin/skills/best-practices/agents/task-intent-analyzer.md +1 -1
- package/bundles/antigravity-plugin/skills/catchup/SKILL.md +11 -0
- package/bundles/antigravity-plugin/skills/cloud-phases/SKILL.md +1 -1
- package/bundles/antigravity-plugin/skills/cross-review/SKILL.md +5 -2
- package/bundles/antigravity-plugin/skills/feature-plan/SKILL.md +35 -83
- package/bundles/antigravity-plugin/skills/fleet-close/SKILL.md +167 -0
- package/bundles/antigravity-plugin/skills/fleet-open/SKILL.md +110 -0
- package/bundles/antigravity-plugin/skills/gh-cli/SKILL.md +2 -1
- package/bundles/antigravity-plugin/skills/handoff/SKILL.md +177 -48
- package/bundles/antigravity-plugin/skills/implement/SKILL.md +87 -147
- package/bundles/antigravity-plugin/skills/migrate-dir/SKILL.md +4 -3
- package/bundles/antigravity-plugin/skills/mmr/SKILL.md +5 -2
- package/bundles/antigravity-plugin/skills/office-documents-official/SKILL.md +24 -0
- package/bundles/antigravity-plugin/skills/park/SKILL.md +0 -1
- package/bundles/antigravity-plugin/skills/pr-merge/SKILL.md +28 -20
- package/bundles/antigravity-plugin/skills/prd/SKILL.md +2 -10
- package/bundles/antigravity-plugin/skills/refactoring/SKILL.md +2 -1
- package/bundles/antigravity-plugin/skills/review-pr/SKILL.md +1 -1
- package/bundles/antigravity-plugin/skills/roadmap/SKILL.md +260 -0
- package/bundles/antigravity-plugin/skills/roadmap/assets/plan-resume-prompt.md +77 -0
- package/bundles/antigravity-plugin/skills/roadmap/assets/render-check.template.mjs +68 -0
- package/bundles/antigravity-plugin/skills/roadmap/assets/review-protocol.md +78 -0
- package/bundles/antigravity-plugin/skills/roadmap/assets/roadmap.template.md +32 -0
- package/bundles/antigravity-plugin/skills/roadmap/assets/value-check.template.py +161 -0
- package/bundles/antigravity-plugin/skills/ship/SKILL.md +16 -16
- package/bundles/antigravity-plugin/skills/ship-merge/SKILL.md +15 -5
- package/bundles/antigravity-plugin/skills/ship-pr/SKILL.md +36 -16
- package/bundles/antigravity-plugin/skills/spawn-session/SKILL.md +4 -8
- package/bundles/antigravity-plugin/warm_start_agy.py +1 -3
- package/bundles/antigravity-plugin-extras/plugin.json +1 -1
- package/bundles/antigravity-plugin-extras/skills/_registry.md +1 -2
- package/bundles/antigravity-plugin-extras/skills/agent-orchestration/SKILL.md +5 -5
- package/bundles/antigravity-plugin-extras/skills/cross-review/SKILL.md +5 -2
- package/bundles/antigravity-plugin-extras/skills/runner-move/SKILL.md +214 -0
- package/bundles/codex/.codex/skills/coding/refactoring/SKILL.md +2 -1
- package/bundles/codex/.codex/skills/devops/gh-cli/SKILL.md +2 -1
- package/bundles/codex/.codex/skills/docs/office-documents-official/SKILL.md +24 -0
- package/bundles/codex/.codex/skills/workflow/best-practices/agents/best-practices-referencer.md +1 -1
- package/bundles/codex/.codex/skills/workflow/best-practices/agents/codebase-context-builder.md +1 -1
- package/bundles/codex/.codex/skills/workflow/best-practices/agents/task-intent-analyzer.md +1 -1
- package/bundles/codex/.codex/skills/workflow/roadmap/SKILL.md +260 -0
- package/bundles/codex/.codex/skills/workflow/roadmap/assets/plan-resume-prompt.md +77 -0
- package/bundles/codex/.codex/skills/workflow/roadmap/assets/render-check.template.mjs +68 -0
- package/bundles/codex/.codex/skills/workflow/roadmap/assets/review-protocol.md +78 -0
- package/bundles/codex/.codex/skills/workflow/roadmap/assets/roadmap.template.md +32 -0
- package/bundles/codex/.codex/skills/workflow/roadmap/assets/value-check.template.py +161 -0
- package/bundles/codex/AGENTS.md +5 -4
- package/bundles/manifest.json +3 -3
- package/package.json +1 -1
- package/bundles/antigravity-plugin/skills/advisory-hub/SKILL.md +0 -233
- package/bundles/antigravity-plugin/skills/advisory-hub/reference/context-template.md +0 -214
- package/bundles/antigravity-plugin/skills/advisory-hub/reference/hub-handbook.md +0 -233
- package/bundles/antigravity-plugin/skills/advisory-hub/reference/report-contract.md +0 -94
- package/bundles/antigravity-plugin/skills/advisory-hub/reference/succession.md +0 -118
- package/bundles/antigravity-plugin/skills/resume/SKILL.md +0 -6
- package/bundles/antigravity-plugin/skills/review/SKILL.md +0 -6
- package/bundles/antigravity-plugin/skills/spawn-hub/SKILL.md +0 -341
- package/bundles/antigravity-plugin/skills/statusline/SKILL.md +0 -6
- package/bundles/antigravity-plugin/skills/validate-phase/SKILL.md +0 -143
- package/bundles/antigravity-plugin-extras/skills/data-lineage/SKILL.md +0 -158
- package/bundles/antigravity-plugin-extras/skills/data-lineage/scripts/convert_doc_to_confluence.py +0 -1430
- package/bundles/antigravity-plugin-extras/skills/golden-plan/SKILL.md +0 -586
- package/bundles/codex/.codex/skills/workflow/advisory-hub/SKILL.md +0 -233
- package/bundles/codex/.codex/skills/workflow/advisory-hub/reference/context-template.md +0 -214
- package/bundles/codex/.codex/skills/workflow/advisory-hub/reference/hub-handbook.md +0 -233
- package/bundles/codex/.codex/skills/workflow/advisory-hub/reference/report-contract.md +0 -94
- package/bundles/codex/.codex/skills/workflow/advisory-hub/reference/succession.md +0 -118
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Value check template: compare figures from an endpoint with an independent source over a fixed past window.
|
|
3
|
+
|
|
4
|
+
Copy it into the app (for example scripts/check_values.py) so CI and prod checks can run it again.
|
|
5
|
+
|
|
6
|
+
python value-check.template.py --base http://localhost:8000 --expected expected.json
|
|
7
|
+
python value-check.template.py --base http://localhost:8000 --expected expected.json --prove-it-can-fail
|
|
8
|
+
|
|
9
|
+
Expected file:
|
|
10
|
+
{"window": {"start": "2026-09-01", "end": "2026-09-30"},
|
|
11
|
+
"figures": [{"name": "...", "endpoint": "/api/x", "path": "a.b", "expected": 1200,
|
|
12
|
+
"source": "where the expected value came from", "population": "who is counted",
|
|
13
|
+
"denominator": "what the rate divides by, or none", "tolerance": 0.001, "series": "day"}]}
|
|
14
|
+
|
|
15
|
+
Rules:
|
|
16
|
+
- The window must end before today. A moving window ("today") gives a number that changes while you check it.
|
|
17
|
+
- Every figure names its source, population and denominator.
|
|
18
|
+
- A "series": "day" figure has ISO date keys that all fall on one weekday. A weekday gap is not a data gap.
|
|
19
|
+
- --prove-it-can-fail changes the expected values on purpose. The run must then exit 1.
|
|
20
|
+
|
|
21
|
+
Exit codes: 0 all figures pass, 1 a figure fails, 4 bad input or an HTTP error that is not a figure failure.
|
|
22
|
+
"""
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import argparse
|
|
26
|
+
import copy
|
|
27
|
+
import datetime as dt
|
|
28
|
+
import json
|
|
29
|
+
import sys
|
|
30
|
+
import urllib.error
|
|
31
|
+
import urllib.parse
|
|
32
|
+
import urllib.request
|
|
33
|
+
|
|
34
|
+
_MISSING = object()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class BadInput(Exception):
|
|
38
|
+
"""The expected file or the request is wrong. Exit 4."""
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _parse_date(text: str, what: str) -> dt.date:
|
|
42
|
+
try:
|
|
43
|
+
return dt.date.fromisoformat(str(text))
|
|
44
|
+
except ValueError as exc:
|
|
45
|
+
raise BadInput(f"{what} is not an ISO date: {text!r}") from exc
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def validate(spec: dict) -> None:
|
|
49
|
+
window = spec.get("window") or {}
|
|
50
|
+
start, end = _parse_date(window.get("start"), "window.start"), _parse_date(window.get("end"), "window.end")
|
|
51
|
+
if end >= dt.date.today():
|
|
52
|
+
raise BadInput(f"window.end {end} is not before today ({dt.date.today()}): use a fixed past window")
|
|
53
|
+
if start > end:
|
|
54
|
+
raise BadInput("window.start is after window.end")
|
|
55
|
+
figures = spec.get("figures") or []
|
|
56
|
+
if not figures:
|
|
57
|
+
raise BadInput("no figures in the expected file")
|
|
58
|
+
for fig in figures:
|
|
59
|
+
name = fig.get("name") or "<unnamed>"
|
|
60
|
+
for key in ("name", "endpoint", "path", "expected"):
|
|
61
|
+
if key not in fig or fig[key] in ("", None):
|
|
62
|
+
raise BadInput(f"figure {name!r} has no {key}")
|
|
63
|
+
for key in ("source", "population", "denominator"):
|
|
64
|
+
if not str(fig.get(key, "")).strip():
|
|
65
|
+
raise BadInput(f"figure {name!r} has an empty {key}: name it")
|
|
66
|
+
if fig.get("series") == "day":
|
|
67
|
+
if not isinstance(fig["expected"], dict) or not fig["expected"]:
|
|
68
|
+
raise BadInput(f"figure {name!r}: a day series needs a non-empty object of date keys")
|
|
69
|
+
weekdays = {_parse_date(k, f"figure {name!r} key").weekday() for k in fig["expected"]}
|
|
70
|
+
if len(weekdays) > 1:
|
|
71
|
+
raise BadInput(f"figure {name!r}: day series keys fall on more than one weekday")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def prove_it_can_fail(spec: dict) -> dict:
|
|
75
|
+
"""Return a copy with a wrong expected value, so a correct endpoint must fail."""
|
|
76
|
+
spec = copy.deepcopy(spec)
|
|
77
|
+
scalars = [f for f in spec["figures"] if not isinstance(f["expected"], dict)]
|
|
78
|
+
if scalars:
|
|
79
|
+
scalars[0]["expected"] = scalars[0]["expected"] + 1
|
|
80
|
+
series = [f for f in spec["figures"] if isinstance(f["expected"], dict)]
|
|
81
|
+
if series:
|
|
82
|
+
series[0]["expected"].pop(next(iter(series[0]["expected"]))) # a missing date must fail the compare
|
|
83
|
+
series[0]["expected"]["1999-01-04"] = 0 # 1999-01-04 is a Monday; the endpoint has no such key
|
|
84
|
+
if not scalars and not series:
|
|
85
|
+
spec["figures"][0]["path"] += ".missing"
|
|
86
|
+
return spec
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def fetch(base: str, endpoint: str, window: dict, headers: dict) -> tuple[int, object]:
|
|
90
|
+
query = urllib.parse.urlencode({"start": window["start"], "end": window["end"]})
|
|
91
|
+
req = urllib.request.Request(f"{base.rstrip('/')}{endpoint}?{query}", headers=headers)
|
|
92
|
+
try:
|
|
93
|
+
with urllib.request.urlopen(req, timeout=30) as resp:
|
|
94
|
+
return resp.status, json.loads(resp.read().decode("utf-8"))
|
|
95
|
+
except urllib.error.HTTPError as exc:
|
|
96
|
+
return exc.code, None
|
|
97
|
+
except (urllib.error.URLError, TimeoutError, ValueError) as exc:
|
|
98
|
+
raise BadInput(f"cannot read {endpoint}: {exc}") from exc
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def dig(data: object, path: str) -> object:
|
|
102
|
+
for part in path.split("."):
|
|
103
|
+
if isinstance(data, dict) and part in data:
|
|
104
|
+
data = data[part]
|
|
105
|
+
else:
|
|
106
|
+
return _MISSING
|
|
107
|
+
return data
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def same(actual: object, expected: object, tolerance: float) -> bool:
|
|
111
|
+
if isinstance(expected, dict):
|
|
112
|
+
if not isinstance(actual, dict):
|
|
113
|
+
return False
|
|
114
|
+
return all(k in actual and same(actual[k], v, tolerance) for k, v in expected.items())
|
|
115
|
+
if isinstance(expected, bool) or isinstance(actual, bool) or not isinstance(expected, (int, float)):
|
|
116
|
+
return actual == expected
|
|
117
|
+
return isinstance(actual, (int, float)) and abs(actual - expected) <= tolerance
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def check(spec: dict, base: str, headers: dict) -> bool:
|
|
121
|
+
ok = True
|
|
122
|
+
for fig in spec["figures"]:
|
|
123
|
+
status, body = fetch(base, fig["endpoint"], spec["window"], headers)
|
|
124
|
+
actual = dig(body, fig["path"]) if status == 200 else _MISSING
|
|
125
|
+
passed = actual is not _MISSING and same(actual, fig["expected"], float(fig.get("tolerance", 0)))
|
|
126
|
+
shown = "missing" if actual is _MISSING else actual
|
|
127
|
+
if status != 200:
|
|
128
|
+
shown = f"HTTP {status}"
|
|
129
|
+
print(f"{'PASS' if passed else 'FAIL'} {fig['name']}: endpoint {shown} vs {fig['source']} {fig['expected']}; "
|
|
130
|
+
f"population {fig['population']}; denominator {fig['denominator']}")
|
|
131
|
+
ok = ok and passed
|
|
132
|
+
return ok
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def main(argv: list[str] | None = None) -> int:
|
|
136
|
+
ap = argparse.ArgumentParser(description=__doc__.split("\n")[0])
|
|
137
|
+
ap.add_argument("--base", required=True, help="origin of the app, for example http://localhost:8000")
|
|
138
|
+
ap.add_argument("--expected", required=True, help="expected figures JSON file")
|
|
139
|
+
ap.add_argument("--prove-it-can-fail", action="store_true", help="change the expected values; the run must exit 1")
|
|
140
|
+
ap.add_argument("--header", action="append", default=[], metavar="K:V", help="extra request header (repeatable)")
|
|
141
|
+
args = ap.parse_args(argv)
|
|
142
|
+
try:
|
|
143
|
+
with open(args.expected, encoding="utf-8") as fh:
|
|
144
|
+
spec = json.load(fh)
|
|
145
|
+
validate(spec)
|
|
146
|
+
headers = {}
|
|
147
|
+
for h in args.header:
|
|
148
|
+
key, sep, value = h.partition(":")
|
|
149
|
+
if not sep:
|
|
150
|
+
raise BadInput(f"--header needs K:V, got {h!r}")
|
|
151
|
+
headers[key.strip()] = value.strip()
|
|
152
|
+
if args.prove_it_can_fail:
|
|
153
|
+
spec = prove_it_can_fail(spec)
|
|
154
|
+
return 0 if check(spec, args.base, headers) else 1
|
|
155
|
+
except (BadInput, OSError, ValueError) as exc:
|
|
156
|
+
print(f"bad input: {exc}", file=sys.stderr)
|
|
157
|
+
return 4
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
if __name__ == "__main__":
|
|
161
|
+
sys.exit(main())
|
|
@@ -36,7 +36,7 @@ names, branch, and service identifiers — never assume.
|
|
|
36
36
|
- Tests: run only if the user says they're needed or a gate explicitly requires them
|
|
37
37
|
- Fix any failures before proceeding. Do not commit broken code.
|
|
38
38
|
|
|
39
|
-
**2. STAGE + COMMIT** — Stage only the intended files (never `git add -A`
|
|
39
|
+
**2. STAGE + COMMIT** — Stage only the intended files, by name (never `git add -A`, `git add .` or a folder):
|
|
40
40
|
```
|
|
41
41
|
git status --short # confirm what's staged
|
|
42
42
|
git add <specific files>
|
|
@@ -48,20 +48,8 @@ Use conventional commits: `fix:`, `feat:`, `chore:`, `refactor:`, `docs:`. Scope
|
|
|
48
48
|
**2b. CROSS-REVIEW TRIGGER** — `ship` has no review pause at all, which is how sixteen fixes reached production unreviewed in one day.
|
|
49
49
|
|
|
50
50
|
<!-- shared:cross-review-trigger — keep byte-identical across commands; a test pins it -->
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
```bash
|
|
54
|
-
python "${CLAUDE_PLUGIN_ROOT}/scripts/caddis_gate.py" review-trigger --range <base>...HEAD
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
Exit **0** — say nothing, carry on. Exit **2** — it prints which rule fired and on which files.
|
|
58
|
-
Run `/caddis:cross-review`, show the findings, and let the user decide. It **never blocks**: this
|
|
59
|
-
picks when to ask for a second opinion, it is not a verdict, and a review gate that blocks a ship
|
|
60
|
-
gets switched off inside a week.
|
|
61
|
-
|
|
62
|
-
It fires on SQL and repositories, `services/`, caches and refresh jobs, auth and RBAC, or a diff
|
|
63
|
-
over 400 changed lines. No judgement in any of those — deliberately, because judgement is exactly
|
|
64
|
-
what failed.
|
|
51
|
+
Run `python "${CLAUDE_PLUGIN_ROOT}/scripts/caddis_gate.py" review-tier --range <base>...HEAD` and review by its tier (rule 3).
|
|
52
|
+
`<base>` is `origin/<default branch>` (on the default branch itself, the commit before this change). Tier 0 or 1-small: no model review. Tier 1: one `/caddis:cross-review`. Tier 2: two reviewers of two other vendors in one round. No `TIER:` line means tier 2.
|
|
65
53
|
<!-- /shared:cross-review-trigger -->
|
|
66
54
|
|
|
67
55
|
**3. PUSH** — to the repo's default branch (confirm the branch first; don't assume `main`):
|
|
@@ -117,9 +105,21 @@ Health: green ✓ | n/a
|
|
|
117
105
|
Release tag: <tag> → <sha> | n/a
|
|
118
106
|
```
|
|
119
107
|
|
|
108
|
+
## Programme triggers
|
|
109
|
+
|
|
110
|
+
Small work that does not meet a trigger skips its block.
|
|
111
|
+
|
|
112
|
+
**Trigger: Always, when the repo deploys on push.** No merge while a push run on the default branch is in progress. Check `gh run list --branch <default> --event push --status in_progress`; if a run is listed, wait for it. `python "${CLAUDE_PLUGIN_ROOT}/scripts/caddis_merge_chain.py"` does this check.
|
|
113
|
+
|
|
114
|
+
**Trigger: A peer session is known.** A relay or a SessionStart warning names another session on this repo. Before the merge, send each named peer session a message (SendMessage or the peer channel). It is a notice, never a request for permission.
|
|
115
|
+
|
|
116
|
+
**Trigger: Always, when the repo deploys on push.** After the merge, run `python "${CLAUDE_PLUGIN_ROOT}/scripts/caddis_prod_check.py" <sha>` with the repo's profile, `.caddis/prod-check.toml`. The script resolves a short SHA to the full SHA first, or refuses with exit 4. A repo with no profile gets items 1 and 2 only, and the script says so.
|
|
117
|
+
|
|
118
|
+
**Trigger: Two or more reviewed green PRs wait to merge.** Combine them into one release PR (the global rule of 2026-10-10): merge their branches into one release branch, run the gates once, open one release PR that links the components, then close the components with a link.
|
|
119
|
+
|
|
120
120
|
## Rules
|
|
121
121
|
- Never push before preflight gates are green.
|
|
122
|
-
- Never
|
|
122
|
+
- Never `git add -A`, `git add .` or `git add <dir>`, even after reading `git status`: stage named files only (the guard denies the first two).
|
|
123
123
|
- Do not edit a workflow file (`.gitea/workflows/` or `.github/workflows/`) to make a failing gate
|
|
124
124
|
pass — fix the source.
|
|
125
125
|
- Gitea lane: tag-triggered runs after `release_metadata` are expected; verify `deploy_prod` is
|
|
@@ -48,6 +48,12 @@ State plainly, then wait for confirmation:
|
|
|
48
48
|
This is the one irreversible, outward-facing step. Approval here **does not carry over** to any
|
|
49
49
|
future run — ask every time, even for the same PR five minutes later.
|
|
50
50
|
|
|
51
|
+
## Programme triggers
|
|
52
|
+
|
|
53
|
+
Small work that does not meet a trigger skips its block.
|
|
54
|
+
|
|
55
|
+
**Trigger: Always, when the repo deploys on push.** No merge while a push run on the default branch is in progress. Check `gh run list --branch <default> --event push --status in_progress`; if a run is listed, wait for it. `python "${CLAUDE_PLUGIN_ROOT}/scripts/caddis_merge_chain.py"` does this check.
|
|
56
|
+
|
|
51
57
|
## Step 3 — Merge
|
|
52
58
|
|
|
53
59
|
Re-read PR head and merge state before merging (GitHub lane):
|
|
@@ -65,8 +71,9 @@ an explicit argument wins. Options: **squash**, **merge-commit**, **rebase-merge
|
|
|
65
71
|
if detection is inconclusive, say so and use merge-commit (never silently squash someone's history).
|
|
66
72
|
|
|
67
73
|
Then merge:
|
|
68
|
-
- GitHub: `gh pr merge <pr>` with `--squash` (squash), `--merge`
|
|
69
|
-
-
|
|
74
|
+
- GitHub: `gh pr merge <pr> --match-head-commit <head-sha>` (the full SHA) with `--squash` (squash), `--merge`
|
|
75
|
+
(merge-commit) or `--rebase` (rebase-merge). GitHub refuses the merge if the head moved.
|
|
76
|
+
- Gitea: call the Gitea PR merge API with the chosen strategy and `head_commit_id: <head-sha>`.
|
|
70
77
|
|
|
71
78
|
Record the merge SHA.
|
|
72
79
|
|
|
@@ -93,8 +100,11 @@ attempt a silent re-deploy — and **skip cleanup** (Step 6).
|
|
|
93
100
|
## Step 6 — Branch cleanup — only after a confirmed-green deploy
|
|
94
101
|
|
|
95
102
|
**Only after** Step 5 is green:
|
|
96
|
-
-
|
|
97
|
-
|
|
103
|
+
- Run `git status --short`. If it shows changes you did not make, do not switch, stash, reset or commit:
|
|
104
|
+
leave them, report them, and stop cleanup here.
|
|
105
|
+
- `git fetch --prune`, then switch to the default branch and fast-forward it to the merge commit.
|
|
106
|
+
- Delete the **remote** feature branch; delete the **local** feature branch (for a squash merge, compare
|
|
107
|
+
the branch tree with the merged tree first, not ancestry).
|
|
98
108
|
- Drop the `backup/<branch>-preship` ref `/ship-pr` created.
|
|
99
109
|
|
|
100
110
|
If deploy validation failed: **skip cleanup** entirely and say so — never delete a branch whose
|
|
@@ -113,7 +123,7 @@ Cleanup: remote branch ✗ deleted / local ✗ deleted / backup ref droppe
|
|
|
113
123
|
## Rules
|
|
114
124
|
- Never merge without: green required checks **and** the explicit human deploy-confirm.
|
|
115
125
|
- Never delete a branch before its deploy is validated green.
|
|
116
|
-
- Never `git add -A`
|
|
126
|
+
- Never `git add -A`, `git add .` or `git add <dir>`, even after reading `git status`: stage named files only (the guard denies the first two). Never edit a workflow file to make a gate pass.
|
|
117
127
|
- Local-only lane: no PR, no merge — refuse and point at `/ship`.
|
|
118
128
|
|
|
119
129
|
## Skill reference
|
|
@@ -40,6 +40,37 @@ are uncommitted.)
|
|
|
40
40
|
- Fix failures first. **Never open a PR over red preflight** — a PR that arrives red wastes the
|
|
41
41
|
reviewer it exists to serve.
|
|
42
42
|
|
|
43
|
+
## Step 2b — Tidy check on this PR's files (before the PR exists)
|
|
44
|
+
|
|
45
|
+
Finished plans close in the PR that finishes them (`/caddis:implement` step 4b). Check that this
|
|
46
|
+
PR did, on the files it touches only:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
python "${CLAUDE_PLUGIN_ROOT}/scripts/caddis_tidy.py" --repo-root . --check --commit-range origin/<default>..HEAD
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
(`<default>` is the default branch from Step 0. Falls back to `scripts/caddis_tidy.py` from a
|
|
53
|
+
source checkout; skip this step if the script is missing or the repo has no `.caddis/`.)
|
|
54
|
+
|
|
55
|
+
- Exit **0** — carry on.
|
|
56
|
+
- Exit **1** — **stop; do not open the PR.** Show each `CHECK VIOLATION` or `ERROR` line.
|
|
57
|
+
- `plan <path>: tracker done, status <s>` — run
|
|
58
|
+
`caddis_tidy.py --repo-root . --apply --only <path>`, `git add` the moved plan by name,
|
|
59
|
+
commit, and run this step again.
|
|
60
|
+
- `plan <path>: tracker done, but done/<name> already exists (rename one of them)` — `--apply`
|
|
61
|
+
cannot fix this; it changes nothing while the name is taken. Rename the plan or the old
|
|
62
|
+
`done/` copy with `git mv`, commit, and run this step again.
|
|
63
|
+
- Any other violation — fix that file's frontmatter, commit, and run this step again.
|
|
64
|
+
- `ERROR: uncommitted changes in files this range touched: <paths>; commit them first` — the
|
|
65
|
+
check reads the committed files the PR ships, so commit (or discard) those changes first.
|
|
66
|
+
- `ERROR: cannot read commit range <range>: <git error>`, or `commit range has an empty side`
|
|
67
|
+
— the range is wrong, not the files. Fix the range: name both sides, and use the remote and
|
|
68
|
+
default branch this repo really has (for example `upstream/master..HEAD`; check with
|
|
69
|
+
`git remote` and Step 0). No violation list is printed with an `ERROR`, so nothing was
|
|
70
|
+
checked yet.
|
|
71
|
+
- Only files this branch changed are checked, so problems already on the default branch never
|
|
72
|
+
block an unrelated PR. The owner's fleet report (`caddis-fleet-tidy.ps1`) lists those.
|
|
73
|
+
|
|
43
74
|
## Step 3 — Scope-guard pre-check (before the PR exists)
|
|
44
75
|
|
|
45
76
|
- If the repo has a **named scope-guard workflow** (e.g. a Gitea `pr-scope-guard` that blocks PRs
|
|
@@ -53,20 +84,8 @@ are uncommitted.)
|
|
|
53
84
|
## Step 3b — Cross-review trigger (before anything is pushed)
|
|
54
85
|
|
|
55
86
|
<!-- shared:cross-review-trigger — keep byte-identical across commands; a test pins it -->
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
```bash
|
|
59
|
-
python "${CLAUDE_PLUGIN_ROOT}/scripts/caddis_gate.py" review-trigger --range <base>...HEAD
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
Exit **0** — say nothing, carry on. Exit **2** — it prints which rule fired and on which files.
|
|
63
|
-
Run `/caddis:cross-review`, show the findings, and let the user decide. It **never blocks**: this
|
|
64
|
-
picks when to ask for a second opinion, it is not a verdict, and a review gate that blocks a ship
|
|
65
|
-
gets switched off inside a week.
|
|
66
|
-
|
|
67
|
-
It fires on SQL and repositories, `services/`, caches and refresh jobs, auth and RBAC, or a diff
|
|
68
|
-
over 400 changed lines. No judgement in any of those — deliberately, because judgement is exactly
|
|
69
|
-
what failed.
|
|
87
|
+
Run `python "${CLAUDE_PLUGIN_ROOT}/scripts/caddis_gate.py" review-tier --range <base>...HEAD` and review by its tier (rule 3).
|
|
88
|
+
`<base>` is `origin/<default branch>` (on the default branch itself, the commit before this change). Tier 0 or 1-small: no model review. Tier 1: one `/caddis:cross-review`. Tier 2: two reviewers of two other vendors in one round. No `TIER:` line means tier 2.
|
|
70
89
|
<!-- /shared:cross-review-trigger -->
|
|
71
90
|
|
|
72
91
|
> **Why this is mechanical and not "consider a review here".** a fleet app shipped four
|
|
@@ -107,7 +126,8 @@ scope-guard check when present:
|
|
|
107
126
|
```bash
|
|
108
127
|
python "${CLAUDE_PLUGIN_ROOT}/scripts/caddis_lanes.py" ci-watch --pr <pr>
|
|
109
128
|
```
|
|
110
|
-
|
|
129
|
+
`RESULT: pass` means every check of the PR's head commit is complete and green; `HEAD:` names that commit.
|
|
130
|
+
Exit codes: **0** pass → continue; **1** fail → read `FAILING:` and the gh output, classify, apply the minimum source fix, then re-push (through Step 4's safety if history must change); **3** could not tell (also: a check still unfinished, or the head's checks unreadable) → run `ci-watch` again; if it stays 3, stop and report, do not merge.
|
|
111
131
|
Fallback when `caddis_lanes.py` is not available: watch in-session with `gh pr checks <pr> --watch` / `gh run watch` (`gh-cli` skill).
|
|
112
132
|
On a red check, classify the failure and apply the minimum **source** fix, then re-push (through
|
|
113
133
|
Step 4's safety if history must change).
|
|
@@ -126,7 +146,7 @@ human-confirmed door.
|
|
|
126
146
|
|
|
127
147
|
## Rules
|
|
128
148
|
- **Never merges, never deploys** — no merge action exists in this command, by design.
|
|
129
|
-
- Never `git add -A`
|
|
149
|
+
- Never `git add -A`, `git add .` or `git add <dir>`, even after reading `git status`: stage named files only (the guard denies the first two).
|
|
130
150
|
- Never force-push without a `backup/*-preship` recovery ref; force pushes are `--force-with-lease` only.
|
|
131
151
|
- Never edit a workflow file (`.gitea/workflows/`, `.github/workflows/`) to make a gate pass — fix the source.
|
|
132
152
|
- Local-only lane: refuse + point at `/ship`. On the default branch: stop + point at `/ship`.
|
|
@@ -9,9 +9,7 @@ Use this when a session is long enough that its own recall has become the risk.
|
|
|
9
9
|
`/caddis:handoff` for that case: `/handoff` writes down what you remember, and **what you remember
|
|
10
10
|
is the failure mode.**
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
this hands over the WORK. Both run on `scripts/caddis_spawn.py`, so a fix to the machinery lands in
|
|
14
|
-
both.
|
|
12
|
+
It runs on `scripts/caddis_spawn.py`.
|
|
15
13
|
|
|
16
14
|
## The failure this exists to catch
|
|
17
15
|
|
|
@@ -333,9 +331,9 @@ Use a read-only agent that can open files and run commands (`caddis:anchor` fits
|
|
|
333
331
|
verdict, record it, and fix any handover document it proves wrong. On a runtime with no subagents,
|
|
334
332
|
grade in a fresh session and say so. Never grade from a nearly-full context.
|
|
335
333
|
|
|
336
|
-
**ALIVE until `handshake close`; not WRITING, with two named exceptions.**
|
|
337
|
-
|
|
338
|
-
|
|
334
|
+
**ALIVE until `handshake close`; not WRITING, with two named exceptions.** The single-writer rule
|
|
335
|
+
exists because two sessions committed to one repo concurrently and one's work landed inside the
|
|
336
|
+
other's commit. It has exactly two exceptions: your own handover artefacts,
|
|
339
337
|
and a document a verdict proves wrong. **Commit and push each one before you reply.** Never hold an
|
|
340
338
|
uncommitted change while the child is live, because a checkout on its side can take it with no copy
|
|
341
339
|
anywhere.
|
|
@@ -404,8 +402,6 @@ immediately.
|
|
|
404
402
|
**REJECT exists because a confidently wrong successor is more dangerous than a confused one.** In
|
|
405
403
|
this project the failure has never been *"I don't know"* — it has been a fluent wrong answer.
|
|
406
404
|
|
|
407
|
-
The vocabulary matches `/caddis:validate-phase` on purpose: same discipline, different object.
|
|
408
|
-
|
|
409
405
|
**Two REJECTs on one spawn means the HANDOVER is at fault, not the successor. Regenerate it** rather
|
|
410
406
|
than coaching the child through it.
|
|
411
407
|
|
|
@@ -76,9 +76,7 @@ def freshness_lines(root: str) -> list[str]:
|
|
|
76
76
|
|
|
77
77
|
def is_headless() -> bool:
|
|
78
78
|
"""True when no human is watching — a run given its task explicitly needs no resume pointer."""
|
|
79
|
-
|
|
80
|
-
return True
|
|
81
|
-
return bool(os.environ.get("DOCKET_PLAN") or os.environ.get("DOCKET_BRANCH"))
|
|
79
|
+
return str(os.environ.get("CADDIS_HEADLESS", "")).strip().lower() in _TRUTHY
|
|
82
80
|
|
|
83
81
|
|
|
84
82
|
def _first_existing(paths: list[str]) -> str:
|
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
| Changelog Generator | `changelog-generator/` | Create user-facing changelogs and release notes from git commit history, grouping changes and rewriting technical commits in plain language. |
|
|
54
54
|
| Deploy Local | `deploy-local/` | Local deployment loop for Gitea-hosted projects: commit on dev, push, watch the golden CI/build/deploy workflow, check prod, and fix lint/test/pipeline failures until the deploy is healthy. |
|
|
55
55
|
| Monorepo | `monorepo/` | Turborepo and pnpm workspaces: turbo.json tasks, remote caching, shared packages, affected-only CI, apps/ vs packages/ layout, and pitfalls like circular deps, version drift and hoisting. |
|
|
56
|
+
| Runner Move | `runner-move/` | >- |
|
|
56
57
|
| Safe Cleanup | `safe-cleanup/` | Use BEFORE deleting a directory tree, scratchpad or git worktree, or "tidying up" generated folders, especially on Windows, where deleting through a junction or symlink destroys its target; and to verify a venv or node_modules survived. |
|
|
57
58
|
| Update Readme | `update-readme/` | Detect feature commits and update README sections (Features, API, Usage) to match, then stage only the README. Use when features, routes or components were added. |
|
|
58
59
|
|
|
@@ -61,7 +62,6 @@
|
|
|
61
62
|
| Skill | Path | When to Use |
|
|
62
63
|
|-------|------|-------------|
|
|
63
64
|
| Architecture Document | `architecture-document/` | Generate enterprise HLD and LLD documents from the living Architecture.md, as DOCX (editable) or PPTX (executive summary). |
|
|
64
|
-
| Data Lineage | `data-lineage/` | Generates End-to-End Data Lineage and Telemetry Specifications linking UI pages, API routes, database datasets, and ETL/SP producers for watch-sight observability. |
|
|
65
65
|
| Doc Coauthoring | `doc-coauthoring/` | Structured workflow for co-authoring documentation: transfer context, refine by iteration, test it on readers. Use when the user is writing docs, proposals, technical specs or decision docs. |
|
|
66
66
|
| Documentation Analyzer | `documentation-analyzer/` | Analyze a codebase, explain what the code does, and generate documentation such as README files. |
|
|
67
67
|
| Hld | `hld/` | Generate High-Level Design (HLD) Markdown and convert it to Confluence Storage Format XHTML with PlantUML diagrams and legends. |
|
|
@@ -135,7 +135,6 @@
|
|
|
135
135
|
| File Organizer | `file-organizer/` | Organize files and folders by context: find duplicates, suggest better structures, automate cleanup. |
|
|
136
136
|
| Game Changing Features | `game-changing-features/` | Find 10x product opportunities and high-leverage features. Use when the user mentions '10x', says 'what would make this 10x better', 'product strategy', or 'what should we build next'. |
|
|
137
137
|
| Golden Nuggets | `golden-nuggets/` | Extract durable tribal knowledge ('gold nuggets') from a codebase or changed files and route each to an instruction file, runbook, hub or CI review inbox. Use after a feature, fix, sprint, incident or release. |
|
|
138
|
-
| Golden Plan | `golden-plan/` | Use when the user asks for a comprehensive multi-phase plan (full-stack, UI + backend), says 'create a plan for building X', 'plan this project', 'I need a detailed plan', 'build plan', 'implementation plan', or attaches a mockup and asks how to build it. Produces an evidence-gated plan with self-contained phase prompts and validation gates. |
|
|
139
138
|
| Intent Writer | `intent-writer/` | Structure freetext ideas, backlog items, or vague requirements into a formal Intent Document that preserves user intent across the entire agent pipeline chain. |
|
|
140
139
|
| Receiving Code Review | `receiving-code-review/` | Use when receiving code review feedback, before implementing it, especially if it seems unclear or wrong: verify with technical rigor, no performative agreement or blind implementation. |
|
|
141
140
|
| Setup Project Ai | `setup-project-ai/` | Install or refresh the agent-agnostic harness in a project: AGENTS.md rules with CLAUDE.md shims, subagents, commands and settings. Use when setting up AI resources on a project, or the user says 'set up the harness', 'setup-project-ai', 'generate CLAUDE.md', 'onboard this project to Claude Code / Codex / agy', or runs /setup-project-ai. |
|
|
@@ -7,7 +7,7 @@ description: "Blueprint for orchestrating the agent pipeline from spec to planni
|
|
|
7
7
|
|
|
8
8
|
## Purpose
|
|
9
9
|
|
|
10
|
-
Codify the **
|
|
10
|
+
Codify the **operator-led orchestration** methodology: a repeatable workflow where the human operator drives a multi-agent pipeline from raw specification to production-ready code. This skill captures the exact sequence, decision points, and artefacts that emerged from real feature delivery so any future session can follow the same playbook.
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
@@ -22,7 +22,7 @@ Codify the **Advisory Hub** methodology: a repeatable workflow where the human o
|
|
|
22
22
|
|
|
23
23
|
## Core Concepts
|
|
24
24
|
|
|
25
|
-
###
|
|
25
|
+
### Operator-Led Mode
|
|
26
26
|
|
|
27
27
|
The operator (human) acts as **orchestrator**. Agents act as an **advisory board** — each specialised, none acting unilaterally. The operator:
|
|
28
28
|
|
|
@@ -33,7 +33,7 @@ The operator (human) acts as **orchestrator**. Agents act as an **advisory board
|
|
|
33
33
|
|
|
34
34
|
```
|
|
35
35
|
┌─────────────────────────────────────────────────────────┐
|
|
36
|
-
│
|
|
36
|
+
│ OPERATOR-LED PIPELINE │
|
|
37
37
|
│ │
|
|
38
38
|
│ Operator ◄──► PRD Agent (requirements) │
|
|
39
39
|
│ Operator ◄──► Architect Agent (design decisions) │
|
|
@@ -226,7 +226,7 @@ Score each identified risk:
|
|
|
226
226
|
|
|
227
227
|
### Stage 3: Mid-Flight Absorption (When Needed)
|
|
228
228
|
|
|
229
|
-
**Agent:** Planner agent (or operator
|
|
229
|
+
**Agent:** Planner agent (or the operator)
|
|
230
230
|
|
|
231
231
|
**Purpose:** Handle changes that arrive after the plan is created but before or during implementation. This is inevitable in real projects.
|
|
232
232
|
|
|
@@ -548,7 +548,7 @@ Full command/billing decision table: `docs/guide/multi-model-workflow.md`.
|
|
|
548
548
|
```
|
|
549
549
|
Operator opens new chat:
|
|
550
550
|
|
|
551
|
-
"I have a feature spec in <file>. Let's use the
|
|
551
|
+
"I have a feature spec in <file>. Let's use the operator-led pipeline:
|
|
552
552
|
1. Read the spec
|
|
553
553
|
2. Triage quick wins (H1–H3)
|
|
554
554
|
3. Implement quick wins and commit
|
|
@@ -88,7 +88,7 @@ smaller chunks; don't just raise `--max-diff-chars` without knowing the endpoint
|
|
|
88
88
|
- **0 — REVIEW: CLEAN** → no blocking issues. Proceed.
|
|
89
89
|
- **1 — REVIEW: BLOCKING** → the reviewer found blocking issues. **Read the printed findings, then FIX
|
|
90
90
|
each blocking item** (or, if you judge one a false positive, state explicitly why it's safe to ignore).
|
|
91
|
-
|
|
91
|
+
Fix it, then re-review only the fix diff. After 3 rounds, ask the owner (rule 3).
|
|
92
92
|
- **4 — error** → no verdict parsed, a git failure, or an endpoint/parse failure. Read stderr; do not
|
|
93
93
|
treat this as CLEAN — investigate.
|
|
94
94
|
- **3 — misconfigured** → `REVIEW_API_KEY` is unset. Set it (see Prerequisites) and re-run.
|
|
@@ -96,7 +96,10 @@ smaller chunks; don't just raise `--max-diff-chars` without knowing the endpoint
|
|
|
96
96
|
## Three rules: one attempt, one review
|
|
97
97
|
1. **Think before you send.** Before a task goes to any coder, write its edge cases and check the facts it depends on (one query or one file read).
|
|
98
98
|
2. **Run it before review.** The coder runs the same checks as CI (name the exact commands in the task). Then run the change once on real data.
|
|
99
|
-
3. **Review
|
|
99
|
+
3. **Review by tier.** Run `python "${CLAUDE_PLUGIN_ROOT}/scripts/caddis_gate.py" review-tier --range <base>...HEAD` (add `--plan <plan> --phase <N>` inside a plan; no `TIER:` line means tier 2).
|
|
100
|
+
Tier 0: gates only. Tier 1-small: your self-check only. Tier 1: one review by another vendor; stop at
|
|
101
|
+
the first CLEAN. Tier 2: two reviewers of two other vendors, in one round. Fix blocking findings only;
|
|
102
|
+
park the rest. Re-review only the fix diff, and only when the fix changed logic. After 3 rounds, ask the owner.
|
|
100
103
|
|
|
101
104
|
A diff over the batch ceiling is split into batches. Each batch prompt lists every file in the
|
|
102
105
|
change, so a batch does not report the others as missing.
|