@caddis/cli 0.4.51 → 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 +86 -2
  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
@@ -689,7 +689,49 @@ if [ -f "pyproject.toml" ] || [ -f "requirements.txt" ]; then
689
689
  echo "[gate] interpreter: $PY"
690
690
  py_gate ruff check .
691
691
  if py_sources_exist; then py_gate mypy .; else echo "[skip] mypy: no Python sources"; fi
692
- py_gate pytest -q
692
+ # pytest is OPT-IN here (CADDIS_GATE_PYTEST=1). Found 2026-10-10: the full suite took 25-31 min in this
693
+ # hook on a box whose system disk was slow, while CI ran the same suite in 5 min - and a merge needs green
694
+ # CI anyway. A local gate five times slower than the check it stands in for trains people to --no-verify it.
695
+ # When it does run: CADDIS_GATE_TMP moves TMPDIR/TEMP/TMP (tests write to tmp_path; Python reads TMPDIR
696
+ # first, so set all three), CADDIS_GATE_PYTEST_ARGS adds arguments (for example "-n 4" with pytest-xdist;
697
+ # use a number, not "auto"), and a machine-wide lock (CADDIS_GATE_LOCK, mkdir-based, waits up to
698
+ # CADDIS_GATE_LOCK_WAIT seconds, a stale one is taken over after 60 min) lets only ONE pytest gate run at a time, so several repos pushing together cannot
699
+ # pile their suites on the same disk and CI runners.
700
+ if [ "${CADDIS_GATE_PYTEST:-0}" = "1" ]; then
701
+ if [ -n "${CADDIS_GATE_TMP:-}" ]; then
702
+ mkdir -p "$CADDIS_GATE_TMP" 2>/dev/null || true
703
+ export TMPDIR="$CADDIS_GATE_TMP" TEMP="$CADDIS_GATE_TMP" TMP="$CADDIS_GATE_TMP"
704
+ fi
705
+ LOCK="${CADDIS_GATE_LOCK:-${TMPDIR:-${TEMP:-/tmp}}/caddis-pytest-gate.lock}"
706
+ mkdir -p "$(dirname "$LOCK")" 2>/dev/null || true
707
+ lock_wait="${CADDIS_GATE_LOCK_WAIT:-1800}"
708
+ waited=0
709
+ got_lock=0
710
+ while :; do
711
+ if mkdir "$LOCK" 2>/dev/null; then got_lock=1; break; fi
712
+ # A stale lock is removed, then this round still sleeps and counts: if the removal fails (a lock owned
713
+ # by another user), the loop ends at lock_wait instead of spinning forever.
714
+ if [ -n "$(find "$LOCK" -maxdepth 0 -mmin +60 2>/dev/null)" ]; then
715
+ rmdir "$LOCK" 2>/dev/null || true
716
+ fi
717
+ if [ "$waited" -ge "$lock_wait" ]; then break; fi
718
+ if [ "$waited" -eq 0 ]; then echo "[gate] pytest: waiting for another repo's pytest gate ($LOCK)"; fi
719
+ sleep 1
720
+ waited=$((waited + 1))
721
+ done
722
+ if [ "$got_lock" -eq 1 ]; then
723
+ trap 'rmdir "$LOCK" 2>/dev/null || true' EXIT INT TERM
724
+ # shellcheck disable=SC2086 # CADDIS_GATE_PYTEST_ARGS is meant to word-split
725
+ py_gate pytest -q ${CADDIS_GATE_PYTEST_ARGS:-}
726
+ rmdir "$LOCK" 2>/dev/null || true
727
+ trap - EXIT INT TERM
728
+ else
729
+ echo "[gate] pytest: another pytest gate holds $LOCK for over ${lock_wait}s - cannot run, so the gate fails" >&2
730
+ fail=1
731
+ fi
732
+ else
733
+ echo "[skip] pytest: runs in CI (set CADDIS_GATE_PYTEST=1 to run it here)"
734
+ fi
693
735
  fi
694
736
  fi
695
737
  if [ -f "package.json" ] && command -v npm >/dev/null 2>&1; then
@@ -1054,7 +1096,6 @@ items?" has no answer when one state has three spellings.
1054
1096
  | Work in flight right now | `.caddis/plans/<feature>.md` | A plan is being executed. A parking-lot item is not. |
1055
1097
  | A task interrupted mid-session | `/caddis:digress` stack | That is a pause, not a backlog item. `/caddis:catchup` pops it. |
1056
1098
  | An ask only an outsider can resolve | `.caddis/comms/register.md` | Blocked on a human, not on us. |
1057
- | 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. |
1058
1099
 
1059
1100
  ## Commands
1060
1101
 
@@ -1432,6 +1473,48 @@ def ensure_user_env(dry: bool, home: Path | None = None) -> list[str]:
1432
1473
  + ("" if not raw else " (previous version saved as settings.json.caddis-bak)")]
1433
1474
 
1434
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
+
1435
1518
  def scaffold_artifact_dir(target: Path, dry: bool) -> list[str]:
1436
1519
  """Create the harness-owned .caddis/ artifact tree + a default .gitignore + a config example.
1437
1520
 
@@ -1733,6 +1816,7 @@ def main() -> int:
1733
1816
  print("-- user settings (additive only, never overwrites a value you set)")
1734
1817
  for line in ensure_user_env(args.dry_run):
1735
1818
  print(f" {line}")
1819
+ print(f" {ensure_cleanup_period(args.dry_run)}")
1736
1820
 
1737
1821
  print("-- doc-coverage discipline (DOC-MAP + page guide + checker)")
1738
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.