@caddis/cli 0.4.52 → 0.4.53

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.
Files changed (100) hide show
  1. package/bundles/antigravity-plugin/CADDIS-GLOSSARY.md +1 -0
  2. package/bundles/antigravity-plugin/plugin.json +1 -1
  3. package/bundles/antigravity-plugin/repo_freshness.py +132 -5
  4. package/bundles/antigravity-plugin/scripts/caddis_checkout_guard.py +223 -0
  5. package/bundles/antigravity-plugin/scripts/caddis_docs_pr.py +284 -0
  6. package/bundles/antigravity-plugin/scripts/caddis_exit.py +2 -0
  7. package/bundles/antigravity-plugin/scripts/caddis_frontmatter.py +1 -0
  8. package/bundles/antigravity-plugin/scripts/caddis_gate.py +226 -195
  9. package/bundles/antigravity-plugin/scripts/caddis_glossary.py +3 -3
  10. package/bundles/antigravity-plugin/scripts/caddis_governor.py +48 -4
  11. package/bundles/antigravity-plugin/scripts/caddis_inventory.py +38 -0
  12. package/bundles/antigravity-plugin/scripts/caddis_lanes.py +294 -127
  13. package/bundles/antigravity-plugin/scripts/caddis_lineage_publish.py +458 -0
  14. package/bundles/antigravity-plugin/scripts/caddis_merge_chain.py +237 -0
  15. package/bundles/antigravity-plugin/scripts/caddis_notify.py +121 -0
  16. package/bundles/antigravity-plugin/scripts/caddis_prod_check.py +380 -0
  17. package/bundles/antigravity-plugin/scripts/caddis_redact.py +99 -0
  18. package/bundles/antigravity-plugin/scripts/caddis_relay_git.py +2 -2
  19. package/bundles/antigravity-plugin/scripts/caddis_review.py +1309 -0
  20. package/bundles/antigravity-plugin/scripts/caddis_roadmap.py +2056 -0
  21. package/bundles/antigravity-plugin/scripts/caddis_roadmap_run.py +2572 -0
  22. package/bundles/antigravity-plugin/scripts/caddis_routing.py +3 -2
  23. package/bundles/antigravity-plugin/scripts/caddis_rows.py +476 -0
  24. package/bundles/antigravity-plugin/scripts/caddis_spawn.py +8 -9
  25. package/bundles/antigravity-plugin/scripts/caddis_spend.py +415 -0
  26. package/bundles/antigravity-plugin/scripts/caddis_tidy.py +357 -20
  27. package/bundles/antigravity-plugin/scripts/caddis_todo.py +55 -0
  28. package/bundles/antigravity-plugin/scripts/caddis_workstreams.py +178 -19
  29. package/bundles/antigravity-plugin/scripts/caddis_worktree_close.py +349 -0
  30. package/bundles/antigravity-plugin/scripts/check_doc_coverage.py +0 -56
  31. package/bundles/antigravity-plugin/scripts/claudster_doctor.py +1 -1
  32. package/bundles/antigravity-plugin/scripts/enforcement_inventory.py +0 -1
  33. package/bundles/antigravity-plugin/scripts/oss_review.py +2 -2
  34. package/bundles/antigravity-plugin/scripts/roadmap-recheck.ps1 +89 -0
  35. package/bundles/antigravity-plugin/scripts/roadmap-run.ps1 +88 -0
  36. package/bundles/antigravity-plugin/scripts/setup_project_ai.py +43 -1
  37. package/bundles/antigravity-plugin/session_end_agy.py +3 -3
  38. package/bundles/antigravity-plugin/skills/_registry.md +2 -6
  39. package/bundles/antigravity-plugin/skills/best-practices/agents/best-practices-referencer.md +1 -1
  40. package/bundles/antigravity-plugin/skills/best-practices/agents/codebase-context-builder.md +1 -1
  41. package/bundles/antigravity-plugin/skills/best-practices/agents/task-intent-analyzer.md +1 -1
  42. package/bundles/antigravity-plugin/skills/catchup/SKILL.md +7 -0
  43. package/bundles/antigravity-plugin/skills/cloud-phases/SKILL.md +1 -1
  44. package/bundles/antigravity-plugin/skills/cross-review/SKILL.md +5 -2
  45. package/bundles/antigravity-plugin/skills/feature-plan/SKILL.md +29 -83
  46. package/bundles/antigravity-plugin/skills/handoff/SKILL.md +77 -48
  47. package/bundles/antigravity-plugin/skills/implement/SKILL.md +87 -147
  48. package/bundles/antigravity-plugin/skills/migrate-dir/SKILL.md +1 -1
  49. package/bundles/antigravity-plugin/skills/mmr/SKILL.md +5 -2
  50. package/bundles/antigravity-plugin/skills/office-documents-official/SKILL.md +24 -0
  51. package/bundles/antigravity-plugin/skills/park/SKILL.md +0 -1
  52. package/bundles/antigravity-plugin/skills/pr-merge/SKILL.md +8 -14
  53. package/bundles/antigravity-plugin/skills/prd/SKILL.md +2 -10
  54. package/bundles/antigravity-plugin/skills/roadmap/SKILL.md +259 -0
  55. package/bundles/antigravity-plugin/skills/roadmap/assets/plan-resume-prompt.md +77 -0
  56. package/bundles/antigravity-plugin/skills/roadmap/assets/render-check.template.mjs +68 -0
  57. package/bundles/antigravity-plugin/skills/roadmap/assets/review-protocol.md +78 -0
  58. package/bundles/antigravity-plugin/skills/roadmap/assets/roadmap.template.md +32 -0
  59. package/bundles/antigravity-plugin/skills/roadmap/assets/value-check.template.py +161 -0
  60. package/bundles/antigravity-plugin/skills/ship/SKILL.md +14 -14
  61. package/bundles/antigravity-plugin/skills/ship-merge/SKILL.md +6 -0
  62. package/bundles/antigravity-plugin/skills/ship-pr/SKILL.md +33 -14
  63. package/bundles/antigravity-plugin/skills/spawn-session/SKILL.md +4 -8
  64. package/bundles/antigravity-plugin/warm_start_agy.py +1 -3
  65. package/bundles/antigravity-plugin-extras/plugin.json +1 -1
  66. package/bundles/antigravity-plugin-extras/skills/_registry.md +1 -2
  67. package/bundles/antigravity-plugin-extras/skills/agent-orchestration/SKILL.md +5 -5
  68. package/bundles/antigravity-plugin-extras/skills/cross-review/SKILL.md +5 -2
  69. package/bundles/antigravity-plugin-extras/skills/runner-move/SKILL.md +214 -0
  70. package/bundles/codex/.codex/skills/docs/office-documents-official/SKILL.md +24 -0
  71. package/bundles/codex/.codex/skills/workflow/best-practices/agents/best-practices-referencer.md +1 -1
  72. package/bundles/codex/.codex/skills/workflow/best-practices/agents/codebase-context-builder.md +1 -1
  73. package/bundles/codex/.codex/skills/workflow/best-practices/agents/task-intent-analyzer.md +1 -1
  74. package/bundles/codex/.codex/skills/workflow/roadmap/SKILL.md +259 -0
  75. package/bundles/codex/.codex/skills/workflow/roadmap/assets/plan-resume-prompt.md +77 -0
  76. package/bundles/codex/.codex/skills/workflow/roadmap/assets/render-check.template.mjs +68 -0
  77. package/bundles/codex/.codex/skills/workflow/roadmap/assets/review-protocol.md +78 -0
  78. package/bundles/codex/.codex/skills/workflow/roadmap/assets/roadmap.template.md +32 -0
  79. package/bundles/codex/.codex/skills/workflow/roadmap/assets/value-check.template.py +161 -0
  80. package/bundles/codex/AGENTS.md +5 -4
  81. package/bundles/manifest.json +3 -3
  82. package/package.json +1 -1
  83. package/bundles/antigravity-plugin/skills/advisory-hub/SKILL.md +0 -233
  84. package/bundles/antigravity-plugin/skills/advisory-hub/reference/context-template.md +0 -214
  85. package/bundles/antigravity-plugin/skills/advisory-hub/reference/hub-handbook.md +0 -233
  86. package/bundles/antigravity-plugin/skills/advisory-hub/reference/report-contract.md +0 -94
  87. package/bundles/antigravity-plugin/skills/advisory-hub/reference/succession.md +0 -118
  88. package/bundles/antigravity-plugin/skills/resume/SKILL.md +0 -6
  89. package/bundles/antigravity-plugin/skills/review/SKILL.md +0 -6
  90. package/bundles/antigravity-plugin/skills/spawn-hub/SKILL.md +0 -341
  91. package/bundles/antigravity-plugin/skills/statusline/SKILL.md +0 -6
  92. package/bundles/antigravity-plugin/skills/validate-phase/SKILL.md +0 -143
  93. package/bundles/antigravity-plugin-extras/skills/data-lineage/SKILL.md +0 -158
  94. package/bundles/antigravity-plugin-extras/skills/data-lineage/scripts/convert_doc_to_confluence.py +0 -1430
  95. package/bundles/antigravity-plugin-extras/skills/golden-plan/SKILL.md +0 -586
  96. package/bundles/codex/.codex/skills/workflow/advisory-hub/SKILL.md +0 -233
  97. package/bundles/codex/.codex/skills/workflow/advisory-hub/reference/context-template.md +0 -214
  98. package/bundles/codex/.codex/skills/workflow/advisory-hub/reference/hub-handbook.md +0 -233
  99. package/bundles/codex/.codex/skills/workflow/advisory-hub/reference/report-contract.md +0 -94
  100. package/bundles/codex/.codex/skills/workflow/advisory-hub/reference/succession.md +0 -118
@@ -139,7 +139,6 @@ def _enforcer_for(rel_path: str, text: str) -> str | None:
139
139
  GATE_SCRIPT = REPO / "scripts" / "caddis_gate.py"
140
140
  GATE_CONCERNS = {
141
141
  "lane-check": ("lane", "spawn", "launch command", "--print", "-p"),
142
- "verdict-gate": ("verdict", "advisory-hub", "blocked-pending-hub", "accept-degraded"),
143
142
  "tracker-vs-git": ("tracker",),
144
143
  }
145
144
 
@@ -416,7 +416,7 @@ def current_branch(cwd: str) -> str:
416
416
 
417
417
  def build_review_prompt(diff_text: str, branch: str, rng: str | None,
418
418
  all_paths: list[str] | None = None) -> str:
419
- """Adversarial review prompt — ported from docket runner._review_prompt (kept in sync).
419
+ """Adversarial review prompt.
420
420
 
421
421
  Self-contained: the criteria are inline so a real review always happens, and it ends with
422
422
  a single machine-parseable verdict line the caller maps to an exit code.
@@ -570,7 +570,7 @@ def main(argv: list[str] | None = None, env: dict[str, str] | None = None) -> in
570
570
  args = parser.parse_args(argv)
571
571
 
572
572
  # --check-config is deliberately NON-INTERACTIVE. This script runs headless — from CI, from a
573
- # docket runner, from a `claude -p` session — where a prompt on stdin does not ask a question,
573
+ # runner, from a `claude -p` session — where a prompt on stdin does not ask a question,
574
574
  # it hangs forever. The ASKING belongs in `/caddis:cross-review`, which is driven by an agent
575
575
  # that can actually talk to a human. This half just answers "is it configured?" cheaply enough
576
576
  # to call before you need it, instead of finding out mid-commit.
@@ -0,0 +1,89 @@
1
+ # roadmap-recheck - the scheduled task for one roadmap (caddis-next Phase 9). It starts
2
+ # `roadmap-run.ps1 -Roadmap <slug>` detached only when ALL of these hold, checked in this order:
3
+ # 1. `--last-stop` exits 0: the runner's last recorded stop is `BLOCKED nothing ready`, there is none, or one of
4
+ # these, each with its own line prefix: `merged:` (the waiting PR merged), `freed:` (the runners that held our
5
+ # batches are gone), `rollup-ready:` (a milestone waits only for its rollup); any FAILED stop
6
+ # (the snapshot guard, a crash), `running` (a killed run), a cap or a STOP file needs the owner, so it prints
7
+ # why and starts nothing;
8
+ # 2. the roadmap runner lock is absent or stale (`--lock-state`); a live lock starts and writes nothing;
9
+ # 3. `caddis_roadmap.py recheck` cleared a blocker (exit 0); in a `merged:`, `freed:` or `rollup-ready:` tick
10
+ # recheck is NOT run (the
11
+ # merge is the change, and a cleared blocker would change the roadmap the restart checks against its hash);
12
+ # 4. agy engine only (the recorded launch options): `caddis_spend.py check` exits 0.
13
+ # It restarts with the launch options the last run recorded (`--restart-args`: engine, model, caps), never the
14
+ # defaults. A run that holds the lock records the last stop `running`, so a run killed without a stop (a crash, a
15
+ # reboot) is never restarted here: it needs the owner.
16
+ # The owner registers it every 2 hours with the command that `roadmap-run.ps1 -Roadmap <slug> -PrintRecheckTask`
17
+ # prints. Every Python call here runs with CADDIS_HEADLESS=1, so no owner-only roadmap change can pass.
18
+ # Test seams: CADDIS_RECHECK_PYTHON (the interpreter) and CADDIS_RECHECK_SCRIPTS (the folder that holds
19
+ # caddis_roadmap.py, caddis_roadmap_run.py, caddis_spend.py and roadmap-run.ps1).
20
+ # Usage: roadmap-recheck.ps1 -Roadmap <slug> [-Repo <path>] [-GhAccount <gh login>]
21
+ # Exit: 0 started the runner, 1 nothing to start, 3 cannot run, 4 bad input.
22
+ param(
23
+ [string]$Roadmap = '',
24
+ [string]$Repo = (Get-Location).Path,
25
+ [string]$GhAccount = ''
26
+ )
27
+ $SlugRule = '^[a-z0-9][a-z0-9-]{0,62}$' # = caddis_roadmap.SLUG_RE_TEXT (a test keeps the three equal)
28
+ if ($Roadmap -cnotmatch $SlugRule) { Write-Error "roadmap-recheck: -Roadmap must match $SlugRule"; exit 4 }
29
+ $py = if ($env:CADDIS_RECHECK_PYTHON) { $env:CADDIS_RECHECK_PYTHON } else { (Get-Command python -ErrorAction SilentlyContinue).Source }
30
+ if (-not $py) { Write-Error "roadmap-recheck: python not found on PATH"; exit 3 }
31
+ $dirs = if ($env:CADDIS_RECHECK_SCRIPTS) { @($env:CADDIS_RECHECK_SCRIPTS) } else { @($PSScriptRoot, (Join-Path $PSScriptRoot '../../scripts')) }
32
+ function Find-Script([string]$name) {
33
+ foreach ($dir in $dirs) {
34
+ $p = Join-Path $dir $name
35
+ if (Test-Path $p) { return (Resolve-Path $p).Path }
36
+ }
37
+ return $null
38
+ }
39
+ $roadmapPy = Find-Script 'caddis_roadmap.py'
40
+ $runnerPy = Find-Script 'caddis_roadmap_run.py'
41
+ $spendPy = Find-Script 'caddis_spend.py'
42
+ $runPs1 = Find-Script 'roadmap-run.ps1'
43
+ if (-not ($roadmapPy -and $runnerPy -and $spendPy -and $runPs1)) {
44
+ Write-Error "roadmap-recheck: caddis_roadmap.py, caddis_roadmap_run.py, caddis_spend.py or roadmap-run.ps1 not found"
45
+ exit 4
46
+ }
47
+ $env:CADDIS_HEADLESS = '1' # inherited by every child below
48
+ # No trailing separator (a trailing `\` before a closing quote escapes it); a drive root keeps `X:\`.
49
+ $Repo = $Repo.TrimEnd('\', '/')
50
+ if ($Repo -match '^[A-Za-z]:$') { $Repo += '\' }
51
+ $repoArg = if ($Repo -match '\\$') { "$Repo\" } else { $Repo } # doubled in front of the closing quote
52
+
53
+ $ghArgs = if ($GhAccount) { @('--gh-account', $GhAccount) } else { @() }
54
+ $why = (& $py $runnerPy --roadmap $Roadmap --repo $Repo --last-stop @ghArgs) -join ' '
55
+ if ($LASTEXITCODE -ne 0) { Write-Output "roadmap-recheck: $why; runner not started"; exit 1 }
56
+
57
+ # A live lock: a runner is starting or running. Start nothing and WRITE nothing (no recheck either: a cleared
58
+ # blocker would change the roadmap under that runner's guard). `--last-stop` above only reads.
59
+ $state = & $py $runnerPy --roadmap $Roadmap --repo $Repo --lock-state
60
+ if ($LASTEXITCODE -ne 0) { Write-Output "roadmap-recheck: runner lock is $state; runner not started"; exit 1 }
61
+
62
+ $merged = $why -match '^(merged|freed|rollup-ready):' # merged PR, holder gone, or a milestone waits for its rollup
63
+ if (-not $merged) {
64
+ # Only after `BLOCKED nothing ready` (or no stop). Never in a `merged:` tick: clearing a blocker now would change
65
+ # the origin roadmap the restarted runner checks against the hash it recorded at publish time.
66
+ & $py $roadmapPy recheck --roadmap $Roadmap --root $Repo
67
+ $rc = $LASTEXITCODE
68
+ if ($rc -ne 0) { Write-Output "roadmap-recheck: no blocker cleared (recheck exit $rc); runner not started"; exit 1 }
69
+ }
70
+
71
+ $options = @(& $py $runnerPy --roadmap $Roadmap --repo $Repo --restart-args | Where-Object { $_ -ne '' })
72
+ if ($LASTEXITCODE -ne 0) { Write-Output "roadmap-recheck: launch options not readable; runner not started"; exit 1 }
73
+ if ($GhAccount -and $options -notcontains '-GhAccount') { $options += @('-GhAccount', $GhAccount) }
74
+
75
+ # The global agy spend cap applies to the agy engine only (a claude run needs no spend-cap file).
76
+ $engineAt = [array]::IndexOf($options, '-Engine')
77
+ $engine = if ($engineAt -ge 0 -and $engineAt + 1 -lt $options.Count) { $options[$engineAt + 1] } else { 'claude' }
78
+ if ($engine -eq 'agy') {
79
+ & $py $spendPy check
80
+ if ($LASTEXITCODE -ne 0) { Write-Output "roadmap-recheck: spend check exit $LASTEXITCODE; runner not started"; exit 1 }
81
+ }
82
+
83
+ $shell = (Get-Process -Id $PID).Path
84
+ $argv = @('-NoProfile', '-File', "`"$runPs1`"", '-Roadmap', $Roadmap, '-Repo', "`"$repoArg`"") + $options
85
+ $start = @{ FilePath = $shell; ArgumentList = $argv }
86
+ if ($IsWindows -or $env:OS -eq 'Windows_NT') { $start['WindowStyle'] = 'Hidden' }
87
+ Start-Process @start
88
+ Write-Output "roadmap-recheck: started roadmap-run.ps1 -Roadmap $Roadmap $($options -join ' ') (lock was $state)"
89
+ exit 0
@@ -0,0 +1,88 @@
1
+ # roadmap-run - start one fresh headless session per batch, until a stop. A thin shim over caddis_roadmap_run.py
2
+ # (design: .caddis/plans/programme-method-design.md sections 8.2 and 8.5; caddis-next Phase 9).
3
+ # -Plan runs a plain plan (plan mode). -Roadmap runs the next batches of a roadmap on origin (roadmap mode). Give one.
4
+ # -PrintRecheckTask prints a PowerShell block (Register-ScheduledTask) that registers roadmap-recheck.ps1 every 2 hours;
5
+ # it registers nothing.
6
+ # Usage: roadmap-run.ps1 (-Plan <slug> | -Roadmap <slug>) [-Repo <path>] [-MaxHours 10] [-MaxSessions 12]
7
+ # [-SessionTimeoutMin 120] [-MaxUsdPerSession 25] [-MaxUsdTotal 150]
8
+ # [-Engine claude] [-Model opus] [-GhAccount <gh login>] [-DryRun]
9
+ # roadmap-run.ps1 -Roadmap <slug> [-Repo <path>] -PrintRecheckTask
10
+ # roadmap-run.ps1 -Roadmap <slug> [-Repo <path>] -AcceptRoadmap [-DryRun] [-Yes] (owner only: accept an owner
11
+ # edit made during
12
+ # a merge wait as the new baseline)
13
+ param(
14
+ [string]$Plan = '',
15
+ [string]$Roadmap = '',
16
+ [string]$Repo = (Get-Location).Path,
17
+ [double]$MaxHours = 10,
18
+ [int]$MaxSessions = 12,
19
+ [double]$SessionTimeoutMin = 120,
20
+ [double]$MaxUsdPerSession = 25,
21
+ [double]$MaxUsdTotal = 150,
22
+ [ValidateSet('claude', 'agy', 'codex')][string]$Engine = 'claude',
23
+ [string]$Model = '',
24
+ [switch]$DryRun,
25
+ [string]$GhAccount = '',
26
+ [switch]$PrintRecheckTask,
27
+ [switch]$AcceptRoadmap,
28
+ [switch]$Yes
29
+ )
30
+ if ([bool]$Plan -eq [bool]$Roadmap) { Write-Error "roadmap-run: give exactly one of -Plan <slug> and -Roadmap <slug>"; exit 4 }
31
+ $SlugRule = '^[a-z0-9][a-z0-9-]{0,62}$' # = caddis_roadmap.SLUG_RE_TEXT (a test keeps the three equal)
32
+ if ($GhAccount -and $GhAccount -notmatch '^[A-Za-z0-9._-]+$') {
33
+ Write-Error "roadmap-run: -GhAccount must be a gh login name (got '$GhAccount')"; exit 4
34
+ }
35
+ if ($Roadmap -and $Roadmap -cnotmatch $SlugRule) {
36
+ Write-Error "roadmap-run: -Roadmap must match $SlugRule (got '$Roadmap')"; exit 4
37
+ }
38
+ if ($PrintRecheckTask) {
39
+ if (-not $Roadmap) { Write-Error "roadmap-run: -PrintRecheckTask needs -Roadmap <slug>"; exit 4 }
40
+ # A PowerShell block for the owner to paste: every value is a single-quoted literal (a ' is doubled), and the
41
+ # task's whole argument line is ONE string, so no shell splits it.
42
+ if (-not (Test-Path -LiteralPath $Repo -PathType Container)) {
43
+ Write-Error "roadmap-run: -Repo '$Repo' is not a folder; nothing printed"; exit 4
44
+ }
45
+ $recheck = (Resolve-Path (Join-Path $PSScriptRoot 'roadmap-recheck.ps1')).Path
46
+ # A full path with no trailing separator: in a quoted command-line argument a trailing `\` would escape the quote. A
47
+ # drive root keeps one `\`, doubled so it stays a backslash in front of the closing quote.
48
+ $repoFull = (Resolve-Path -LiteralPath $Repo).Path.TrimEnd('\', '/')
49
+ if ($repoFull -match '^[A-Za-z]:$') { $repoFull += '\\' }
50
+ $shellExe = (Get-Process -Id $PID).Path
51
+ function Sq([string]$s) { "'" + $s.Replace("'", "''") + "'" }
52
+ $argLine = "-NoProfile -NonInteractive -File `"$recheck`" -Roadmap $Roadmap -Repo `"$repoFull`""
53
+ if ($GhAccount) { $argLine += " -GhAccount $GhAccount" }
54
+ Write-Output "`$action = New-ScheduledTaskAction -Execute $(Sq $shellExe) -Argument $(Sq $argLine)"
55
+ Write-Output "`$trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Hours 2)"
56
+ Write-Output ("Register-ScheduledTask -TaskName $(Sq "caddis-roadmap-recheck-$Roadmap") -Action `$action " +
57
+ "-Trigger `$trigger -Description $(Sq "caddis roadmap $Roadmap recheck, every 2 hours")")
58
+ Write-Output ("# Note: this task runs only while you are logged on. To run it whether you are logged on or not, " +
59
+ "add -User <DOMAIN\user> -Password <password> to Register-ScheduledTask, or set that option in Task Scheduler.")
60
+ exit 0
61
+ }
62
+ $py = (Get-Command python -ErrorAction SilentlyContinue).Source
63
+ if (-not $py) { Write-Error "roadmap-run: python not found on PATH"; exit 3 }
64
+ # Installed plugin: both files sit in scripts/. Source repo: the Python file is in <repo>/scripts.
65
+ $runner = Join-Path $PSScriptRoot 'caddis_roadmap_run.py'
66
+ if (-not (Test-Path $runner)) { $runner = Join-Path $PSScriptRoot '..\..\scripts\caddis_roadmap_run.py' }
67
+ if (-not (Test-Path $runner)) { Write-Error "roadmap-run: caddis_roadmap_run.py not found"; exit 4 }
68
+ $inv = [Globalization.CultureInfo]::InvariantCulture
69
+ $argv = @($runner)
70
+ if ($Plan) { $argv += @('--plan', $Plan) } else { $argv += @('--roadmap', $Roadmap) }
71
+ $argv += @('--repo', $Repo,
72
+ '--max-hours', $MaxHours.ToString($inv), '--max-sessions', $MaxSessions.ToString($inv),
73
+ '--session-timeout-min', $SessionTimeoutMin.ToString($inv),
74
+ '--max-usd-per-session', $MaxUsdPerSession.ToString($inv), '--max-usd-total', $MaxUsdTotal.ToString($inv),
75
+ '--engine', $Engine)
76
+ if ($Model) { $argv += @('--model', $Model) }
77
+ if ($DryRun) { $argv += '--dry-run' }
78
+ if ($AcceptRoadmap) { # owner only: the Python side refuses it headless
79
+ if (-not $Roadmap) { Write-Error "roadmap-run: -AcceptRoadmap needs -Roadmap <slug>"; exit 4 }
80
+ $acceptArgs = @('--roadmap', $Roadmap, '--repo', $Repo, '--accept-roadmap')
81
+ if ($DryRun) { $acceptArgs += '--dry-run' } # print the diff, write nothing
82
+ if ($Yes) { $acceptArgs += '--yes' } # no console confirmation
83
+ & $py $runner @acceptArgs
84
+ exit $LASTEXITCODE
85
+ }
86
+ if ($GhAccount) { $argv += @('--gh-account', $GhAccount) }
87
+ & $py @argv
88
+ exit $LASTEXITCODE
@@ -1096,7 +1096,6 @@ items?" has no answer when one state has three spellings.
1096
1096
  | Work in flight right now | `.caddis/plans/<feature>.md` | A plan is being executed. A parking-lot item is not. |
1097
1097
  | A task interrupted mid-session | `/caddis:digress` stack | That is a pause, not a backlog item. `/caddis:catchup` pops it. |
1098
1098
  | An ask only an outsider can resolve | `.caddis/comms/register.md` | Blocked on a human, not on us. |
1099
- | Board cards | `.caddis/backlog/` | **docket writes that directory.** It is a projection of the board, not a hand-written register. Never edit it by hand. |
1100
1099
 
1101
1100
  ## Commands
1102
1101
 
@@ -1474,6 +1473,48 @@ def ensure_user_env(dry: bool, home: Path | None = None) -> list[str]:
1474
1473
  + ("" if not raw else " (previous version saved as settings.json.caddis-bak)")]
1475
1474
 
1476
1475
 
1476
+ CLEANUP_PERIOD_KEY = "cleanupPeriodDays"
1477
+ CLEANUP_PERIOD_DAYS = 120
1478
+
1479
+
1480
+ def ensure_cleanup_period(dry: bool, home: Path | None = None, days: int = CLEANUP_PERIOD_DAYS) -> str:
1481
+ """Keep Claude Code transcripts `days` days (Claude Code's own default is 30) by adding `cleanupPeriodDays`.
1482
+
1483
+ WHY: caddis measures which skills and commands are used from those transcripts; on 11 Oct 2026 only
1484
+ 35 days were left, too few to decide what to cut (caddis-next checks, section 4.6). Same rules as
1485
+ ensure_user_env: an existing value is ALWAYS left alone, a file that does not parse is never rewritten,
1486
+ a backup is kept and the write is atomic. `CADDIS_NO_SETTINGS_ENV` skips it. Its backup has its own name
1487
+ (`settings.json.caddis-retention-bak`): main() runs ensure_user_env first, and sharing `.caddis-bak` would
1488
+ overwrite the user's original file with that function's output. Known limit, as for ensure_user_env: a
1489
+ write by Claude Code between this read and the replace is lost."""
1490
+ if os.environ.get("CADDIS_NO_SETTINGS_ENV"):
1491
+ return "transcript retention: skipped (CADDIS_NO_SETTINGS_ENV set)"
1492
+ path = _user_settings_path(home)
1493
+ if path.is_file():
1494
+ raw = path.read_text(encoding="utf-8", errors="ignore")
1495
+ try:
1496
+ data = json.loads(raw)
1497
+ except json.JSONDecodeError as exc:
1498
+ return (f"transcript retention: {path} is NOT VALID JSON ({exc.msg}, line {exc.lineno}) — "
1499
+ "left untouched; fix it by hand.")
1500
+ if not isinstance(data, dict):
1501
+ return f"transcript retention: {path} is not a JSON object — left untouched"
1502
+ else:
1503
+ raw, data = "", {}
1504
+ if CLEANUP_PERIOD_KEY in data:
1505
+ return f"transcript retention: {CLEANUP_PERIOD_KEY} already set to {data[CLEANUP_PERIOD_KEY]!r} — left alone"
1506
+ data[CLEANUP_PERIOD_KEY] = days
1507
+ if not dry:
1508
+ path.parent.mkdir(parents=True, exist_ok=True)
1509
+ if raw:
1510
+ path.with_suffix(".json.caddis-retention-bak").write_text(raw, encoding="utf-8")
1511
+ tmp = path.with_suffix(".json.caddis-tmp")
1512
+ tmp.write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8")
1513
+ os.replace(tmp, path)
1514
+ verb = "would set" if dry else "set"
1515
+ return f"transcript retention: {verb} {CLEANUP_PERIOD_KEY}={days} in {path}"
1516
+
1517
+
1477
1518
  def scaffold_artifact_dir(target: Path, dry: bool) -> list[str]:
1478
1519
  """Create the harness-owned .caddis/ artifact tree + a default .gitignore + a config example.
1479
1520
 
@@ -1775,6 +1816,7 @@ def main() -> int:
1775
1816
  print("-- user settings (additive only, never overwrites a value you set)")
1776
1817
  for line in ensure_user_env(args.dry_run):
1777
1818
  print(f" {line}")
1819
+ print(f" {ensure_cleanup_period(args.dry_run)}")
1778
1820
 
1779
1821
  print("-- doc-coverage discipline (DOC-MAP + page guide + checker)")
1780
1822
  for line in emit_doc_discipline(target, ident, args.force, args.dry_run):
@@ -9,7 +9,7 @@ stdout as a `{"decision":…}` object — a stray string trips its "unsupported
9
9
  does a pure side effect and prints NOTHING: empty stdout is agy's "no decision", while
10
10
  `{"decision": "continue"}` would stop the session from ending.
11
11
 
12
- Headless runs (CADDIS_HEADLESS, DOCKET_PLAN or DOCKET_BRANCH set and non-empty) are skipped, as in
12
+ Headless runs (CADDIS_HEADLESS set and non-empty) are skipped, as in
13
13
  the Claude Stop hook, so review and lane runs leave no files. The record is one append with no
14
14
  transcript read, so it stays here rather than moving to /handoff (Phase 13).
15
15
 
@@ -90,11 +90,11 @@ def _workspace_roots(data):
90
90
  return []
91
91
  return [str(r) for r in roots if isinstance(r, (str, bytes)) and str(r).strip()]
92
92
 
93
- _HEADLESS_MARKERS = ("CADDIS_HEADLESS", "DOCKET_PLAN", "DOCKET_BRANCH")
93
+ _HEADLESS_MARKERS = ("CADDIS_HEADLESS",)
94
94
 
95
95
 
96
96
  def _headless() -> bool:
97
- """True when any marker is set to a non-empty value. `CADDIS_HEADLESS=` is not headless."""
97
+ """True when CADDIS_HEADLESS is set to a non-empty value. `CADDIS_HEADLESS=` is not headless."""
98
98
  return any(str(os.environ.get(name, "")).strip() for name in _HEADLESS_MARKERS)
99
99
 
100
100
 
@@ -55,6 +55,7 @@
55
55
  | Explainer Doc | `explainer-doc/` | Hand-off doc for a team that did NOT do the investigation (vendor, contractor, another team) — the "stranger must follow it alone" case. Use when the user says "write a spec for the X team", "document this for someone outside the team", "explainer doc", "implementation spec for a vendor", or "hand this off to Y". Not for readers with full context. |
56
56
  | Hld | `hld/` | Generate High-Level Design (HLD) Markdown documentation and Confluence XHTML with native PlantUML diagrams. |
57
57
  | Lld | `lld/` | Generate Lower-Level Design (LLD) Markdown documentation and Confluence XHTML with native PlantUML diagrams. |
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. |
58
59
  | Tech Explainer | `tech-explainer/` | >- |
59
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. |
60
61
  | Writing Plans | `writing-plans/` | Use when you have a spec or requirements for a multi-step task, before touching code |
@@ -97,20 +98,15 @@
97
98
  | Pr Merge | `pr-merge/` | End-to-end pull request and merge lane — push feature branch, open or update PR, monitor CI to green, merge to default branch, and clean up the feature branch. |
98
99
  | Prd | `prd/` | Capture requirements through structured discovery and write a PRD |
99
100
  | Rca | `rca/` | Record a root cause in one step — .caddis/rca/<date>-<slug>.md — or close one with the fixing commit |
100
- | Resume | `resume/` | Renamed to /caddis:catchup (alias, removed next release) |
101
- | Review | `review/` | Renamed to /caddis:review-pr (alias, removed next release) |
102
101
  | Review Pr | `review-pr/` | Review someone else's OPEN pull request end to end — what changed, what CI says, what is risky — then approve or request changes. Never approves on its own. |
103
102
  | Ship | `ship/` | Commit, push, and monitor the deploy pipeline (auto-detects Gitea, GitHub Actions, or local-only) |
104
103
  | Ship Merge | `ship-merge/` | Merge an already-green, already-reviewed PR behind an explicit deploy-confirm, monitor the deploy, validate prod, then clean up the branch — only on green. |
105
104
  | Ship Pr | `ship-pr/` | Open a reviewed PR for the current feature branch — rebase-safe push, PR create/update, monitor CI, STOP at green. Never merges, never deploys (that's /ship-merge). |
106
- | Spawn Hub | `spawn-hub/` | Hand the Advisory Hub role to a fresh session — run the mechanical audit, then generate the succession prompt with a context self-check the new Hub must pass |
107
105
  | Spawn Session | `spawn-session/` | Hand the WORK to a fresh session — generate the handover from the repo, then validate the successor by re-deriving its answers |
108
- | Statusline | `statusline/` | Renamed to /caddis:install-statusline (alias, removed next release) |
109
106
  | Tdd | `tdd/` | Run a strict red-green-refactor TDD cycle for a unit of behavior |
110
107
  | Ui Brief | `ui-brief/` | Design-first UI brief — lock the aesthetic before writing any code |
111
108
  | Usage Review | `usage-review/` | Review local caddis usage over a window, surface prioritised recommendations, and apply config changes in one step |
112
109
  | Use Model | `use-model/` | Explain (or apply) the model lanes — claude / claude-glm / cross-review — key resolution, and how to add a provider |
113
- | Validate Phase | `validate-phase/` | Advisory Hub phase validation — consume a phase report, RE-DERIVE its claims independently, and return ACCEPT / ACCEPT-WITH-CORRECTION / REJECT |
114
110
  | Version | `version/` | Report the installed caddis version |
115
111
 
116
112
  ### Media
@@ -134,10 +130,10 @@
134
130
 
135
131
  | Skill | Path | When to Use |
136
132
  |-------|------|-------------|
137
- | Advisory Hub | `advisory-hub/` | >- |
138
133
  | Best Practices | `best-practices/` | >- |
139
134
  | Brainstorming | `brainstorming/` | You MUST use this before any creative work - creating features, building components, adding functionality, or modifying behavior. Explores user intent, requirements and design before implementation. |
140
135
  | Context Curator | `context-curator/` | Compress and prioritize codebase context before handing work to reasoning agents, minimizing token waste while preserving the required decision inputs. |
141
136
  | Preflight | `preflight/` | Plan-vs-codebase validation — verifies API contracts, type names, field accuracy, dependencies, and paths before implementation begins |
137
+ | Roadmap | `roadmap/` | Track milestones across plans in .caddis/roadmaps/<slug>/: next batch, typed blockers, rollup. Every change is a docs PR via caddis_roadmap.py. |
142
138
  | Setup Project Ai | `setup-project-ai/` | Install or refresh the agent-agnostic dev harness in this project (AGENTS.md-canonical rules hierarchy + CLAUDE.md @import shims, subagents, commands, settings) |
143
139
  | Skill Creator | `skill-creator/` | Create, edit and improve skills, run evals and benchmarks, and optimize a skill's description for triggering accuracy. |
@@ -44,7 +44,7 @@ description: >-
44
44
  constraints pattern, and surfaces anti-patterns around breaking changes.
45
45
  </commentary>
46
46
  </example>
47
- model: inherit
47
+ model: sonnet
48
48
  ---
49
49
 
50
50
  **Note: The current year is 2026.** Use this when searching for recent documentation and patterns.
@@ -46,7 +46,7 @@ description: >-
46
46
  finds slow query examples, and identifies existing performance tests.
47
47
  </commentary>
48
48
  </example>
49
- model: inherit
49
+ model: sonnet
50
50
  ---
51
51
 
52
52
  **Note: The current year is 2026.** Use this when referencing recent patterns or documentation.
@@ -48,7 +48,7 @@ description: >-
48
48
  considerations: caching, N+1 queries, database indexes, async processing.
49
49
  </commentary>
50
50
  </example>
51
- model: inherit
51
+ model: sonnet
52
52
  ---
53
53
 
54
54
  **Note: The current year is 2026.** Use this when referencing recent patterns or documentation.
@@ -68,6 +68,13 @@ on is worse than one sentence admitting there is nothing to recap.
68
68
 
69
69
  **No argument:** this is the last step of the catchup procedure above, not a separate one.
70
70
  After the list, add the workstream index and the open work:
71
+ 0. The roadmap on origin is the order of work. List the roadmaps, then show each one's status line:
72
+ ```
73
+ python "${CLAUDE_PLUGIN_ROOT}/scripts/caddis_roadmap.py" list
74
+ python "${CLAUDE_PLUGIN_ROOT}/scripts/caddis_roadmap.py" status --roadmap <slug>
75
+ ```
76
+ Run `caddis_roadmap.py list`, then `caddis_roadmap.py status --roadmap <slug>` for each slug it prints, and
77
+ show the `line`. If `list` prints nothing (exit 1), the repo has no roadmap: go on to step 1.
71
78
  1. Refresh and show the index: `python "${CLAUDE_PLUGIN_ROOT}/scripts/caddis_workstreams.py" render-index`,
72
79
  then display `.caddis/relay.md`. If it refuses a legacy `.caddis/relay.md`, display that file
73
80
  as it is and suggest `/handoff`, which migrates it.
@@ -152,7 +152,7 @@ For each PR from a `<prefix>/phase-<NN>` branch, one at a time:
152
152
  1. Read the whole diff (`gh pr diff <N>`). Check it against the phase's Design and Touches. A file
153
153
  outside Touches is a finding.
154
154
  2. Wait for CI: `gh pr checks <N> --watch` (or the ci-watch lane, if this caddis has it).
155
- 3. Review under the three rules: one review, stop at the first CLEAN; after two blocking reviews,
155
+ 3. Review under the three rules (rule 3, review by tier): stop at the first CLEAN; after 3 rounds,
156
156
  ask the owner. A fix goes back to the same cloud session or is made locally on the PR branch.
157
157
  4. Just before merging, re-read `gh pr view <N> --json headRefOid,mergeStateStatus`. Merge only if
158
158
  the head SHA is the one you reviewed and CI passed on it:
@@ -96,14 +96,17 @@ as a diff that silently is not read. Name a provider explicitly only when you ha
96
96
  ## Interpret the exit code
97
97
  - **0 — REVIEW: CLEAN** → no blocking issues. Proceed.
98
98
  - **1 — REVIEW: BLOCKING** → read the printed findings, then **FIX each blocking item** (or explicitly
99
- justify why one is a false positive). Re-run once. A second BLOCKING goes to the owner (rule 3).
99
+ justify why one is a false positive). Fix it, then re-review only the fix diff. After 3 rounds, ask the owner (rule 3).
100
100
  - **4 — error** → no verdict parsed, or a git/endpoint failure. Read stderr; do NOT treat as clean.
101
101
  - **3 — misconfigured** → `REVIEW_API_KEY` is unset. Set it (see Prerequisite) and re-run.
102
102
 
103
103
  ## Three rules: one attempt, one review
104
104
  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).
105
105
  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.
106
- 3. **Review once.** Use whatever reviewer is available. Stop at the first CLEAN. After two blocking reviews, ask the owner.
106
+ 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).
107
+ Tier 0: gates only. Tier 1-small: your self-check only. Tier 1: one review by another vendor; stop at
108
+ the first CLEAN. Tier 2: two reviewers of two other vendors, in one round. Fix blocking findings only;
109
+ park the rest. Re-review only the fix diff, and only when the fix changed logic. After 3 rounds, ask the owner.
107
110
 
108
111
  A diff over the batch ceiling is split into batches. Each batch prompt lists every file in the
109
112
  change, so a batch does not report the others as missing.
@@ -13,9 +13,8 @@ The plan file is the **durable spine of the harness** — it must let any future
13
13
  agent, on any tool) resume with zero re-discovery. Optimize for that.
14
14
 
15
15
  ## Headless mode
16
- When the invocation contains the marker **`HEADLESS RUN RULES`**, **or `CADDIS_HEADLESS` /
17
- `DOCKET_PLAN` / `DOCKET_BRANCH` is set in the environment** (the caddis OSS launchers export
18
- `CADDIS_HEADLESS` on claude's `-p`/`--print`; the docket runner sets the others) — a non-interactive
16
+ When the invocation contains the marker **`HEADLESS RUN RULES`**, **or `CADDIS_HEADLESS` is set in
17
+ the environment** (the caddis OSS launchers export it on claude's `-p`/`--print`) — a non-interactive
19
18
  caller spawned this session and no human is present, so the scope-check questions and any interview are
20
19
  **suspended and forbidden**. This mode exists for one-line ideas: the card may be a short title with **no
21
20
  description**, and there may be **no codebase, PROJECT-FACTS, or prior PRD** to read. That is expected —
@@ -39,13 +38,6 @@ Absolute rules in this mode (they override everything below):
39
38
  a bullet under `## Constraints & decisions` — never a question to the user.
40
39
  - **Honor the caller's output path and slug** (falling back to `.caddis/plans/<feature-slug>.md`);
41
40
  set `feature: <slug>` in the frontmatter to that slug.
42
- - **Also emit a visual companion** — a self-contained, scannable HTML page at `<artifact_dir>/<slug>.html`
43
- presenting the plan visually (a goal card, phases as cards with their steps, an affected-files table,
44
- and a risks/decisions section). **Use inline `<style>` ONLY** — the visual is rendered in a *sandboxed*
45
- iframe with no script execution, so a `<script>`/CDN (e.g. the Tailwind browser CDN) would NOT run and
46
- the page would appear unstyled. Keep it fully portable — no external `<script>`/`<link>`, no local
47
- asset files, no `/`-rooted paths. This is the visual the human reviews in docket alongside the markdown.
48
- Write it after the `.md`; the runner finds it by the matching `<slug>.html` name.
49
41
  - **Always write the plan file, then end with exactly one fenced `json` highlights block** — nothing
50
42
  after it:
51
43
  ```json
@@ -63,7 +55,7 @@ exists instead of assumed. If the work fits comfortably in one session, say so a
63
55
  instead of planning. Only produce a plan for genuinely multi-phase work.
64
56
 
65
57
  ### Evidence gate — do not write plan content while a BLOCKER is unresolved
66
- Absorbed from `golden-plan`, whose strongest idea this is, and the direct counter to the failure
58
+ This is the direct counter to the failure
67
59
  mode that makes a plan dangerous: **you write the spec, so you cannot see its gaps.** A phase built
68
60
  on guessed field names or an imagined API is confidently wrong, passes every completeness check,
69
61
  and only fails at the exit gate — or worse, doesn't.
@@ -164,9 +156,11 @@ of that role and write its Lane line as follows:
164
156
  - `sonnet`: `claude — /model sonnet`
165
157
  - `claude`: `claude — this session`
166
158
  - `codex`: codex — `codex exec --ephemeral --sandbox workspace-write "HEADLESS RUN RULES. Read <plan path> and its Constraints. Implement Phase N only in the current checkout. Do not switch or create branches, commit, push, or edit files outside this phase. Run its exit gate and report the literal result."`
167
- - `agy`: agy — `agy --mode accept-edits --dangerously-skip-permissions --add-dir . --print-timeout 20m --output-format text -p "HEADLESS RUN RULES. Read <plan path> and its Constraints. Implement Phase N only in the current checkout. Do not switch or create branches, commit, push, or edit files outside this phase. Run its exit gate and report the literal result."`
159
+ - `agy`: agy — `python "${CLAUDE_PLUGIN_ROOT}/scripts/caddis_spend.py" run --caller lane-pN -- agy --mode accept-edits --dangerously-skip-permissions --add-dir . --print-timeout 20m --output-format json -p "HEADLESS RUN RULES. Read <plan path> and its Constraints. Implement Phase N only in the current checkout. Do not switch or create branches, commit, push, or edit files outside this phase. Run its exit gate and report the literal result."`
168
160
 
169
- Replace `<plan path>` and `N` with this plan's path and phase number. If the routing command fails
161
+ Replace `<plan path>` and `N` (also in `lane-pN`) with this plan's path and phase number. The agy line
162
+ always starts with `caddis_spend.py run`: it books the call against the global EUR spend cap, and
163
+ `caddis_gate.py lane-check` refuses a bare `agy` Lane line. If the routing command fails
170
164
  or exits 3, use `claude — this session` and state the reason in that phase.
171
165
 
172
166
  #### A phase may take a no-escalation lane only if its exit gate would fail on a spec error
@@ -186,14 +180,9 @@ internal repo path into stakeholder-facing copy, and set a `live` status flag th
186
180
  framework's own "Soon" badge). `tsc` clean, 632/632 tests green, cross-review CLEAN, exit gate
187
181
  satisfied. A headless run following it literally would have committed both, entirely green.
188
182
 
189
- In an Advisory-Hub plan, treat your lane as **provisional**: the Hub writes each `phase-NN.prompt.md`
190
- before the phase runs, and that is the first time anyone but the spec's author reads it. Let that
191
- read confirm or downgrade the lane. It costs nothing — the prompt is being written anyway.
192
-
193
183
  ### Group phases into MILESTONES — only when the plan is big enough
194
184
  **Add milestones when the plan has ~8+ phases OR spans multiple sessions.** Below that, phases and a
195
- Tracker are enough; a three-phase feature gains nothing from ceremony. (Same sizing instinct as the
196
- Advisory-Hub test, deliberately — one threshold to remember, not two.)
185
+ Tracker are enough; a three-phase feature gains nothing from ceremony.
197
186
 
198
187
  A milestone is **a group of phases that ship together**. It answers "what lands when this is done?",
199
188
  which is the question a long plan otherwise leaves the executor guessing at.
@@ -211,9 +200,10 @@ resuming session can see at a glance which group it is in and what remains befor
211
200
  ### Flag high-risk phases (`risk:` — a marker, not a mechanism)
212
201
  Each phase may carry **`risk: normal|high`** (default `normal` — omit the line unless the phase is
213
202
  `high`), meaning only "this is a phase where a wrong answer is expensive" — security/authz, a
214
- correctness-critical algorithm, an ambiguous spec, or an irreversible change. It is **inert**: it
215
- selects no model, spawns no agents, and implies no fan-out — it exists so the executor can decide **at
216
- runtime** whether an extra cross-check is worth it. Do not emit orchestration, lane fan-out, or
203
+ correctness-critical algorithm, an ambiguous spec, or an irreversible change. It selects no model and
204
+ implies no fan-out. It has one effect: the roadmap/plan runner reviews a finished `risk: high` phase
205
+ twice (`caddis_review.py --plan --phase --runs 2`), gives it one fix session on blocking
206
+ findings, and stops `BLOCKED review` when findings remain. Roadmap mode reviews before it publishes the PR. Do not emit orchestration, lane fan-out, or
217
207
  `Workflow` scripts from this command; the runtime decision rules and the intent→model map live in one
218
208
  place, the `agent-orchestration` skill's *Multi-model fan-out* section.
219
209
 
@@ -275,7 +265,10 @@ yourself (relay + Tracker) before stopping — do not hand back "run `/caddis:ha
275
265
  Three rules: one attempt, one review.
276
266
  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).
277
267
  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.
278
- 3. **Review once.** Use whatever reviewer is available. Stop at the first CLEAN. After two blocking reviews, ask the owner.
268
+ 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).
269
+ Tier 0: gates only. Tier 1-small: your self-check only. Tier 1: one review by another vendor; stop at
270
+ the first CLEAN. Tier 2: two reviewers of two other vendors, in one round. Fix blocking findings only;
271
+ park the rest. Re-review only the fix diff, and only when the fix changed logic. After 3 rounds, ask the owner.
279
272
 
280
273
  - One phase at a time: read this plan → run the phase on its **Lane** → RED→GREEN→VERIFY→CROSS-REVIEW→
281
274
  COMMIT → update the **Tracker** row. The Tracker is the resume signal for every future session.
@@ -293,6 +286,10 @@ Three rules: one attempt, one review.
293
286
  normally; the halt stops anything being **built on top of it** before a human has looked. Blast
294
287
  radius stays one reviewable, revertable commit. Halting *beforehand* buys nothing: the phase has
295
288
  to be executed either way, and a well-specced high-risk phase is as executable as any other.
289
+ One exception: when the session prompt says the runner reviews `risk: high` phases (the
290
+ runner's `plan-resume-prompt.md` says so), the runner's review gate replaces this halt: the
291
+ session ends `STATUS: CONTINUE`, and the runner reviews the phase twice before anything builds
292
+ on it. A headless marker (`CADDIS_HEADLESS=1`) alone does not lift the halt.
296
293
  4. **A failed or blocked phase** — tests red, exit gate unmet, or the plan turned out wrong. Stop;
297
294
  do not build on it.
298
295
  - **A lane change is NOT a halt.** `/caddis:implement` spawns the assigned lane itself and verifies
@@ -366,66 +363,15 @@ done:
366
363
 
367
364
  If any phase relies on the implementer *reasoning out* a gap, close the gap in the plan now.
368
365
 
369
- ## Step 3b — Does this plan need an Advisory Hub? (ask once, here)
370
-
371
- The Advisory Hub is **opt-in per plan**, gated on a single file: `<plan-stem>-advisory-context.md`
372
- beside the plan. `/caddis:implement`, `/caddis:handoff` and `/caddis:validate-phase` all check for it;
373
- **no file → no behaviour change, ever.** But nothing has ever *asked* whether to create it, so the
374
- pattern only ever gets used by someone who already knows it exists. This step closes that gap.
375
-
376
- **Ask it here** — at the end of planning is the only moment the phase count, risk levels and session
377
- boundaries are all known.
378
-
379
- > **You MUST call `Skill(advisory-hub)` and read *"When it earns its keep"* before deciding.**
380
- > Do not decide from memory, and do not decide from any summary — including one in this file.
381
- > The test is stated in exactly one place on purpose: two copies of a rule drift on the first edit,
382
- > and the copy that used to live here had already drifted. It compressed the REQUIRED leg to the
383
- > phrase *"expensive-to-reverse"* and dropped the enumeration that defines it — which reads as
384
- > *"can I `git revert` this?"*, and under that reading a plan that publishes € figures to business
385
- > stakeholders scores "no stakes". That is the one leg that fires on reporting, analytics and
386
- > dashboard work, i.e. most of what this fleet builds. Load the skill.
387
-
388
- If the test passes, tell the user plainly what it costs and what it buys, and offer to scaffold the
389
- context doc from the `advisory-hub` skill's template. **Do not create it silently** — an opt-in
390
- pattern that switches itself on is no longer opt-in.
391
-
392
- **In headless mode, never offer and never create.** Headless forbids asking anything, so an "offer"
393
- is impossible there. Instead record the result as a line under `## Constraints & decisions` —
394
- *"Advisory Hub: test passed (N phases, <criteria>) — not scaffolded, headless run; create
395
- `<plan-stem>-advisory-context.md` to enable"* — so the decision reaches a human rather than being
396
- silently dropped or silently taken.
397
-
398
- If it does not pass, say nothing **to the user** — a three-phase feature should never hear about
399
- this. But **always record the determination in the plan, pass or fail**: one line under
400
- `## Constraints & decisions`, naming which REQUIRED trigger was or was not hit.
401
-
402
- > `Advisory Hub: not applicable — no REQUIRED trigger (not a migration / security change /
403
- > production data correctness / numbers published to stakeholders).`
404
-
405
- ### If the test FAILS but this repo already runs a Hub for another plan
406
-
407
- Check for an existing `*-advisory-context.md` in `.caddis/plans/` belonging to a **different** plan.
408
- If one exists, add a second line under `## Constraints & decisions`:
409
-
410
- > `Hub conventions: this repo runs Advisory Hub for <other-plan>. This plan does NOT — if it borrows
411
- > the phase-NN.prompt.md / .report.md / CONTINUE GATE conventions, the lightweight rule applies: the
412
- > session that files a report must not write the next prompt without an independent re-check.`
413
-
414
- **Why this line exists.** A repo with one Hub plan has its artefacts sitting there as the most recent
415
- example of "how we structure a multi-session plan here", and the next plan copies them by analogy.
416
- It inherits the file names and none of the safety, because the safety was never in the names.
417
- Measured once already: an implementing session filed its own batch report and then wrote the next
418
- batch's prompt in the same session, with nobody having looked in between. Nothing was wrong that
419
- time — and nothing checked either.
420
-
421
- This is the cheapest possible place to catch it: when the second plan is created, before any
422
- artefact-mimicry has happened. See *"Failing this test correctly does not mean no discipline"* in the
423
- `advisory-hub` skill for the rule itself. Do not restate the rule here — one home, or the copies
424
- drift.
425
-
426
- Silent to the human, auditable in the artifact. "Say nothing" once meant *leave no trace anywhere*,
427
- and a wrong negative was then invisible — no line in the plan, no line in the chat, nothing for
428
- anyone to challenge. A rule whose failures leave no artifact cannot be reviewed.
366
+ 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.
367
+
368
+ ## Programme triggers
369
+
370
+ Small work that does not meet a trigger skips its block.
371
+
372
+ **Trigger: Always.** Add one open-work row for the plan in `.caddis/open-work.md`. Set its cells with `python "${CLAUDE_PLUGIN_ROOT}/scripts/caddis_rows.py" set --file <open-work.md> --id <row id> --col Status=todo`, never with a regex edit.
373
+
374
+ **Trigger: The plan records a decision.** Record it with `/caddis:decide`.
429
375
 
430
376
  ## Step 4 — Report
431
377
  Output the plan path (`.caddis/plans/<feature-slug>.md`), the phase list (one line each, with each