@aperant/framework 0.7.0 → 0.7.3

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 (151) hide show
  1. package/CHANGELOG.md +180 -0
  2. package/agents/apt-planner.md +12 -0
  3. package/agents/apt-pr-review-fixer.md +13 -9
  4. package/bin/apt-tools.mjs +7 -0
  5. package/dist/cli/ci-watch/stop-matrix.d.mts.map +1 -1
  6. package/dist/cli/ci-watch/stop-matrix.mjs +16 -0
  7. package/dist/cli/ci-watch/stop-matrix.mjs.map +1 -1
  8. package/dist/cli/commands/ci-watch.d.mts +11 -0
  9. package/dist/cli/commands/ci-watch.d.mts.map +1 -1
  10. package/dist/cli/commands/ci-watch.mjs +108 -2
  11. package/dist/cli/commands/ci-watch.mjs.map +1 -1
  12. package/dist/cli/commands/features-audit.d.mts +24 -0
  13. package/dist/cli/commands/features-audit.d.mts.map +1 -1
  14. package/dist/cli/commands/features-audit.mjs +159 -5
  15. package/dist/cli/commands/features-audit.mjs.map +1 -1
  16. package/dist/cli/commands/health-check.d.mts +16 -0
  17. package/dist/cli/commands/health-check.d.mts.map +1 -1
  18. package/dist/cli/commands/health-check.mjs +118 -2
  19. package/dist/cli/commands/health-check.mjs.map +1 -1
  20. package/dist/cli/commands/init.d.mts +9 -0
  21. package/dist/cli/commands/init.d.mts.map +1 -1
  22. package/dist/cli/commands/init.mjs +49 -4
  23. package/dist/cli/commands/init.mjs.map +1 -1
  24. package/dist/cli/commands/modes.d.mts.map +1 -1
  25. package/dist/cli/commands/modes.mjs +11 -0
  26. package/dist/cli/commands/modes.mjs.map +1 -1
  27. package/dist/cli/commands/pr-review-audit-fixer.d.mts +13 -0
  28. package/dist/cli/commands/pr-review-audit-fixer.d.mts.map +1 -1
  29. package/dist/cli/commands/pr-review-audit-fixer.mjs +18 -5
  30. package/dist/cli/commands/pr-review-audit-fixer.mjs.map +1 -1
  31. package/dist/cli/commands/task.d.mts.map +1 -1
  32. package/dist/cli/commands/task.mjs +84 -5
  33. package/dist/cli/commands/task.mjs.map +1 -1
  34. package/dist/cli/commands/validate-evidence.d.mts +24 -2
  35. package/dist/cli/commands/validate-evidence.d.mts.map +1 -1
  36. package/dist/cli/commands/validate-evidence.mjs +154 -17
  37. package/dist/cli/commands/validate-evidence.mjs.map +1 -1
  38. package/dist/cli/commands/vitest-doctor.d.mts +2 -0
  39. package/dist/cli/commands/vitest-doctor.d.mts.map +1 -0
  40. package/dist/cli/commands/vitest-doctor.mjs +168 -0
  41. package/dist/cli/commands/vitest-doctor.mjs.map +1 -0
  42. package/dist/cli/config/gitignore-drift.d.mts +23 -1
  43. package/dist/cli/config/gitignore-drift.d.mts.map +1 -1
  44. package/dist/cli/config/gitignore-drift.mjs +81 -3
  45. package/dist/cli/config/gitignore-drift.mjs.map +1 -1
  46. package/dist/cli/config/load.d.mts +56 -2
  47. package/dist/cli/config/load.d.mts.map +1 -1
  48. package/dist/cli/config/load.mjs +192 -2
  49. package/dist/cli/config/load.mjs.map +1 -1
  50. package/dist/cli/consistency/parse-review.mjs +7 -0
  51. package/dist/cli/consistency/parse-review.mjs.map +1 -1
  52. package/dist/cli/dispatch.d.mts.map +1 -1
  53. package/dist/cli/dispatch.mjs +24 -2
  54. package/dist/cli/dispatch.mjs.map +1 -1
  55. package/dist/cli/gate/gates/gitignore-in-sync.d.mts +1 -1
  56. package/dist/cli/gate/gates/gitignore-in-sync.d.mts.map +1 -1
  57. package/dist/cli/gate/gates/gitignore-in-sync.mjs +5 -2
  58. package/dist/cli/gate/gates/gitignore-in-sync.mjs.map +1 -1
  59. package/dist/cli/gate/gates/review-clean.d.mts +5 -1
  60. package/dist/cli/gate/gates/review-clean.d.mts.map +1 -1
  61. package/dist/cli/gate/gates/review-clean.mjs +23 -18
  62. package/dist/cli/gate/gates/review-clean.mjs.map +1 -1
  63. package/dist/cli/gate/gates/verify-approved.d.mts +49 -1
  64. package/dist/cli/gate/gates/verify-approved.d.mts.map +1 -1
  65. package/dist/cli/gate/gates/verify-approved.mjs +93 -14
  66. package/dist/cli/gate/gates/verify-approved.mjs.map +1 -1
  67. package/dist/cli/help.d.mts.map +1 -1
  68. package/dist/cli/help.mjs +7 -2
  69. package/dist/cli/help.mjs.map +1 -1
  70. package/dist/cli/install/runtime-detect.d.mts +13 -0
  71. package/dist/cli/install/runtime-detect.d.mts.map +1 -1
  72. package/dist/cli/install/runtime-detect.mjs +9 -0
  73. package/dist/cli/install/runtime-detect.mjs.map +1 -1
  74. package/dist/cli/task/index-md.d.mts.map +1 -1
  75. package/dist/cli/task/index-md.mjs +14 -2
  76. package/dist/cli/task/index-md.mjs.map +1 -1
  77. package/dist/plugin/.claude-plugin/plugin.json +11 -2
  78. package/dist/plugin/agents/apt-improver.md +99 -0
  79. package/dist/plugin/agents/apt-planner.md +127 -10
  80. package/dist/plugin/agents/apt-pr-review-fixer.md +13 -9
  81. package/dist/plugin/skills/apt/SKILL.md +1 -0
  82. package/dist/plugin/skills/apt-close-task/SKILL.md +63 -1
  83. package/dist/plugin/skills/apt-debug/SKILL.md +39 -6
  84. package/dist/plugin/skills/apt-debug/appendices/diagnose-discipline.md +119 -0
  85. package/dist/plugin/skills/apt-diagram/SKILL.md +378 -0
  86. package/dist/plugin/skills/apt-diagram/appendices/design-discipline.md +97 -0
  87. package/dist/plugin/skills/apt-discuss/SKILL.md +72 -5
  88. package/dist/plugin/skills/apt-discuss/appendices/grill-discipline.md +104 -0
  89. package/dist/plugin/skills/apt-discuss/appendices/zoom-out-helper.md +79 -0
  90. package/dist/plugin/skills/apt-execute/SKILL.md +57 -5
  91. package/dist/plugin/skills/apt-execute/appendices/tdd-mode.md +107 -0
  92. package/dist/plugin/skills/apt-improve/DEEPENING.md +84 -0
  93. package/dist/plugin/skills/apt-improve/INTERFACE-DESIGN.md +97 -0
  94. package/dist/plugin/skills/apt-improve/LANGUAGE.md +104 -0
  95. package/dist/plugin/skills/apt-improve/SKILL.md +141 -0
  96. package/dist/plugin/skills/apt-plan/SKILL.md +171 -4
  97. package/dist/plugin/skills/apt-plan/adapters/conductor.md +98 -0
  98. package/dist/plugin/skills/apt-pr-review/SKILL.md +57 -18
  99. package/dist/plugin/skills/apt-prototype/LOGIC.md +109 -0
  100. package/dist/plugin/skills/apt-prototype/SKILL.md +143 -0
  101. package/dist/plugin/skills/apt-prototype/UI.md +90 -0
  102. package/dist/plugin/skills/apt-quick/SKILL.md +49 -8
  103. package/dist/plugin/skills/apt-release-notes/SKILL.md +193 -0
  104. package/dist/plugin/skills/apt-release-notes/appendices/persona-voice.md +59 -0
  105. package/dist/plugin/skills/apt-review/SKILL.md +2 -0
  106. package/dist/plugin/skills/apt-run/SKILL.md +32 -4
  107. package/dist/plugin/skills/apt-setup/SKILL.md +308 -6
  108. package/dist/plugin/skills/apt-ship/SKILL.md +122 -1
  109. package/dist/plugin/skills/apt-spar/SKILL.md +315 -0
  110. package/dist/plugin/skills/apt-triage/AGENT-BRIEF.md +84 -0
  111. package/dist/plugin/skills/apt-triage/OUT-OF-SCOPE.md +75 -0
  112. package/dist/plugin/skills/apt-triage/SKILL.md +169 -0
  113. package/dist/plugin/skills/apt-update/SKILL.md +77 -10
  114. package/dist/plugin/skills/apt-verify/SKILL.md +3 -0
  115. package/dist/plugin/skills/apt-verify-proof/SKILL.md +10 -5
  116. package/dist/plugin/skills/apt-watch-ci/SKILL.md +166 -0
  117. package/dist/plugin/skills/apt-zoom-out/SKILL.md +130 -0
  118. package/package.json +133 -133
  119. package/prompts/conductor-framework-context.md +63 -0
  120. package/prompts/conductor-system.md +11 -0
  121. package/skills/apt-close-task/SKILL.md +1 -0
  122. package/skills/apt-discuss/SKILL.md +47 -5
  123. package/skills/apt-execute/SKILL.md +9 -0
  124. package/skills/apt-plan/SKILL.md +12 -0
  125. package/skills/apt-pr-review/SKILL.md +11 -2
  126. package/skills/apt-quick/SKILL.md +19 -8
  127. package/skills/apt-researcher.md +1 -0
  128. package/skills/apt-setup/SKILL.md +33 -2
  129. package/skills/apt-verify-proof/SKILL.md +7 -5
  130. package/src/cli/ci-watch/stop-matrix.mjs +17 -0
  131. package/src/cli/commands/ci-watch.mjs +113 -2
  132. package/src/cli/commands/features-audit.mjs +164 -5
  133. package/src/cli/commands/health-check.mjs +115 -2
  134. package/src/cli/commands/init.mjs +52 -4
  135. package/src/cli/commands/modes.mjs +11 -0
  136. package/src/cli/commands/pr-review-audit-fixer.mjs +18 -5
  137. package/src/cli/commands/task.mjs +83 -5
  138. package/src/cli/commands/validate-evidence.mjs +158 -17
  139. package/src/cli/commands/vitest-doctor.mjs +173 -0
  140. package/src/cli/config/gitignore-drift.mjs +74 -3
  141. package/src/cli/config/load.mjs +188 -2
  142. package/src/cli/consistency/parse-review.mjs +6 -0
  143. package/src/cli/dispatch.mjs +23 -2
  144. package/src/cli/gate/gates/gitignore-in-sync.mjs +5 -2
  145. package/src/cli/gate/gates/review-clean.mjs +24 -19
  146. package/src/cli/gate/gates/verify-approved.mjs +97 -14
  147. package/src/cli/help.mjs +7 -2
  148. package/src/cli/install/runtime-detect.mjs +9 -0
  149. package/src/cli/task/index-md.mjs +15 -2
  150. package/templates/config.json +2 -7
  151. package/workflows/verify-proof.md +8 -3
@@ -15,7 +15,7 @@ execution_modes:
15
15
  - auto
16
16
  - step
17
17
  allowed-tools: "Bash, Read, Write"
18
- argument-hint: "apt:update"
18
+ argument-hint: "apt:update [--from-source]"
19
19
  gates: []
20
20
  ---
21
21
 
@@ -42,35 +42,101 @@ The cache file is at `~/.cache/aperant/apt-update-check.json`. It contains `runt
42
42
 
43
43
  ## 1. Read current state
44
44
 
45
+ <!-- TODO(followup): support PATH-only / npx-only kernel installs. The
46
+ hardcoded `.aperant/deps/...` path below fails users who don't have a
47
+ project-local kernel directory. Tracked as a separate task. -->
45
48
  ```bash
46
49
  node .aperant/deps/node_modules/@aperant/framework/bin/apt-tools.mjs check-version . --json
47
50
  ```
48
51
 
49
- Parse the JSON. Capture:
52
+ Parse the JSON. From 0.6.7 the envelope merges the SessionStart worker's cache (when fresh — written within the last hour to `~/.cache/aperant/apt-update-check.json`). Capture:
53
+
50
54
  - `framework.installed_version` — what's currently on disk
51
- - `framework.latest_version` — what's in the source / published
55
+ - `framework.latest_version` — what npm reports as the latest published version (MAY BE `null` when the worker cache is stale / missing / wrong schema)
52
56
  - `runtimes[]` — list of installed runtime ids + per-runtime `stale_files` count
53
57
  - `stale_files_total` — drift tally across runtimes
58
+ - `update_available` — true when worker cache is fresh AND `installed_version < latest_version`
59
+ - `dev_install_ahead` — true when local kernel is ahead of published (typical local dev)
60
+ - `not_published` — true when npm returned 404 (package not yet on registry)
61
+ - `lookup_error` — non-null when npm view failed for any non-404 reason (timeout, ENETDOWN, etc.)
62
+
63
+ The envelope is **single-shape** — every field above is always present. When the worker cache is stale or missing, `latest_version` is `null`, the four booleans are `false`, `stale_files_total` falls back to a local-only count, and `lookup_error` is `null`. Your parser doesn't need to branch on cache freshness.
54
64
 
55
65
  If `runtimes[]` is empty, tell the user they haven't installed Aperant into any runtime yet and suggest `apt-tools init . --claude` (or their preferred runtime). Stop.
56
66
 
67
+ ## 1.5. Bump the local kernel from npm when newer is available
68
+
69
+ The Aperant kernel lives at `.aperant/deps/node_modules/@aperant/framework/`. `apt-tools init` re-installs runtime files at THE LOCAL KERNEL'S VERSION — so a stale kernel means `/apt:update` re-stamps manifests at the OLD version even when npm has a newer release. Bump the kernel BEFORE invoking init.
70
+
71
+ First, resolve `latest_version` when the envelope had `null` (stale cache).
72
+
73
+ **Timeout enforcement (ID-04):** The `npm view` call must complete within 10 s — never block the user longer than the SessionStart worker would. Use npm's `--fetch-timeout` flag as the primary mechanism (universally portable across all npm versions); it sets the HTTP fetch timeout in milliseconds:
74
+
75
+ ```bash
76
+ npm view @aperant/framework version --fetch-timeout=10000 --fetch-retries=0 --json --silent 2>/dev/null || true
77
+ ```
78
+
79
+ On Linux/macOS with GNU coreutils installed, you MAY additionally wrap with a hard shell-level timer: `timeout 10 npm view ...` (Linux) or `gtimeout 10 npm view ...` (macOS via `brew install coreutils`). Shell-level wrapping is optional — `--fetch-timeout=10000` alone is sufficient because it is universal. Do NOT rely solely on the shell `timeout` command without the npm flag, as it is not available on all systems.
80
+
81
+ If npm exits non-zero or times out, surface a one-line warning (`⚠ npm view failed — using local kernel`) and proceed; the local kernel still works for the runtime refresh, just at the older version.
82
+
83
+ **Security (SEC-001):** Before interpolating `latest_version` into any shell command, verify it matches the semver pattern `^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?$`. If it does not match, treat it as missing and fall back to the explicit version pinning path (proceed with the local kernel version, surface `⚠ latest_version invalid — using local kernel`). This protects against a poisoned cache entry reaching a shell command.
84
+
85
+ When `installed_version < latest_version`, bump:
86
+
87
+ ```bash
88
+ npm install --prefix .aperant/deps @aperant/framework@<latest_version> --no-save --no-package-lock --no-audit --no-fund
89
+ ```
90
+
91
+ On `npm 404` (`E404` in stderr) treat it as `not_published` and skip the kernel bump (proceed to step 3). On any other npm failure, surface the npm stderr verbatim and STOP — do NOT proceed to init with a half-installed kernel.
92
+
93
+ ## 1.6. Self-host case — bump from local workspace (FRAMEWORK-BUG-017)
94
+
95
+ When the user invokes `/apt:update --from-source` OR the route envelope's `update_check.reason` mentions `workspace source ahead of installed kernel`, the source of truth is the local `packages/framework/` workspace — NOT npm. This is the Aperant-contributor self-host case: a same-PR framework fix bumps the workspace `packages/framework/package.json:version` but the kernel under `.aperant/deps/node_modules/@aperant/framework/` still reflects the previous version. Step 1.5's `npm view` would fetch the published version (still the OLD one), so we must skip it.
96
+
97
+ The reliable bump mechanism mirrors what `runKernelInstall` does today — `pnpm pack` produces a versioned tarball, then `npm install --prefix` consumes it. Symlinking is fragile across re-installs and `pnpm install --filter` doesn't relocate the published artifact into `.aperant/deps`.
98
+
99
+ Walk up from the project root looking for a sibling `packages/framework/package.json`. When found AND its `name === "@aperant/framework"`:
100
+
101
+ ```bash
102
+ # 1. Pack the workspace source into a versioned tarball.
103
+ cd <workspace-root>
104
+ pnpm --filter @aperant/framework pack --pack-destination /tmp
105
+
106
+ # 2. Install the tarball into the project's kernel root.
107
+ cd <project-root>
108
+ npm install --prefix .aperant/deps /tmp/aperant-framework-<version>.tgz --no-save --no-package-lock --no-audit --no-fund
109
+ ```
110
+
111
+ Replace `<version>` with the workspace `package.json:version` you just read. The tarball name follows npm's `pack` convention (`@aperant/framework@X.Y.Z` → `aperant-framework-X.Y.Z.tgz`).
112
+
113
+ If `--from-source` was passed but no `packages/framework/package.json` is found anywhere above the project root, surface a one-line error (`⚠ --from-source: no workspace package.json found — falling back to npm`) and proceed with the standard step 1.5 npm path.
114
+
115
+ Then proceed to step 3 to re-install the runtime files at the new kernel version. Skip step 4 (cache clear) — the cache is keyed on `latest_version` from npm, and a `--from-source` bump never wrote to it, so there's nothing to invalidate.
116
+
57
117
  ## 2. Classify the update scenario
58
118
 
59
- Pick ONE of three scenarios based on what you just parsed:
119
+ Pick ONE of three scenarios based on what you parsed in step 1:
60
120
 
61
- - **A. Version bump available** — `installed_version !== latest_version` (regardless of drift). Users see this most often.
62
- - **B. Drift only** — `installed_version === latest_version` but `stale_files_total > 0`. User hand-edited an installed file or something was partially overwritten.
63
- - **C. Nothing to do** — `installed_version === latest_version` AND `stale_files_total === 0`. Still re-run init to refresh the update-check cache, but tell the user plainly that no changes were applied.
121
+ - **A. Version bump available** — `installed_version !== latest_version` (regardless of drift). Users see this most often. Step 1.5 bumped the kernel; step 3 re-stamps manifests at the new version.
122
+ - **B. Drift only** — `installed_version === latest_version` but `stale_files_total > 0`. User hand-edited an installed file or something was partially overwritten. Step 1.5 was a no-op; step 3 still needs `--force-runtimes` to re-stamp.
123
+ - **C. Nothing to do** — `installed_version === latest_version` AND `stale_files_total === 0`. Still re-run init with `--force-runtimes` to refresh manifests + cache, but tell the user plainly that no version change happened.
64
124
 
65
125
  ## 3. Re-install the currently-installed runtimes
66
126
 
67
- Build the flags list from the runtime ids (e.g. `--claude --codex`) and run:
127
+ Build the flags list from the runtime ids (e.g. `--claude --codex`) and pass `--force-runtimes` so init bypasses the `upgrade_noop` short-circuit (Bug 2 fix, 0.6.7) and re-stamps every manifest:
128
+
129
+ ```bash
130
+ node .aperant/deps/node_modules/@aperant/framework/bin/apt-tools.mjs init . --claude --force-runtimes
131
+ ```
132
+
133
+ Example with multiple runtimes installed:
68
134
 
69
135
  ```bash
70
- node .aperant/deps/node_modules/@aperant/framework/bin/apt-tools.mjs init . <flags>
136
+ node .aperant/deps/node_modules/@aperant/framework/bin/apt-tools.mjs init . --claude --codex --force-runtimes
71
137
  ```
72
138
 
73
- **Do NOT add `--yes`, `--solo`, `--team`, or any other Class A flag.** This command is meant to refresh runtime files only — adding `--yes` triggers a config schema migration that can silently flip `share.visibility` to `solo` and re-write `.gitignore`, which is exactly the 0.6.0/0.6.1 dogfood bug. If `init` returns a `requires_input` envelope (schema drift), surface it to the user verbatim and stop; let them decide team vs solo with the explicit flag. From 0.6.4 onward `--yes` infers visibility from existing git tracking, but the skill must still not add it — keep this command scoped to runtime refresh.
139
+ **Do NOT add `--yes`, `--solo`, `--team`, or any other Class A flag.** This command is meant to refresh runtime files only — adding `--yes` triggers a config schema migration that can silently flip `share.visibility` to `solo` and re-write `.gitignore`, which is exactly the 0.6.0/0.6.1 dogfood bug. `--force-runtimes` is the ONLY new flag this skill adds. If `init` returns a `requires_input` envelope (schema drift), surface it to the user verbatim and stop; let them decide team vs solo with the explicit flag. From 0.6.4 onward `--yes` infers visibility from existing git tracking, but the skill must still not add it — keep this command scoped to runtime refresh.
74
140
 
75
141
  Parse the init JSON. Capture for each runtime:
76
142
  - `install_root` (shortened to the basename, e.g. `.claude`)
@@ -164,5 +230,6 @@ Runtime files were still refreshed; `/apt` will still work from PATH if `apt-too
164
230
  - **Worker hasn't run yet**: the cache file is missing. That's fine — run check-version directly (step 1 is robust to a missing cache). Proceed normally.
165
231
  - **Re-install fails (non-zero exit from init)**: surface the JSON `error` field verbatim in a single line, then stop. Do NOT delete the cache file — the chip should remain visible so the user sees the failure persists.
166
232
  - **Kernel install fails but runtime install succeeds**: see Scenario D above. Do not treat this as a total failure; the user's skills still work via PATH fallback.
233
+ - **CLAUDE.md / AGENTS.md foreign rows (exit 2, `instruction-file.foreign-rows`)** — 0.7.0+ — init now refuses to silently clobber custom rows in the `<!-- APT:framework-* -->` block. If `init` exits 2 with `code: instruction-file.foreign-rows`, the `foreignRowIds[]` field lists the rows the generator did NOT produce. Two paths: (a) remove the foreign rows from CLAUDE.md / AGENTS.md by hand and re-run init, or (b) re-run with `--force-overwrite-catalog` to regenerate anyway. Unattended upgrade flows should detect this code and surface it to the operator — silent bypass is a regression.
167
234
 
168
235
  </edge_cases>
@@ -19,6 +19,9 @@ argument-hint: "apt:verify [--fast | --cross-verify]"
19
19
  gates:
20
20
  - consistency-check
21
21
  - all-subtasks-complete
22
+ config_keys:
23
+ - verification.evidence_capture
24
+ - verification.max_iterations
22
25
  ---
23
26
  <objective>
24
27
  Verify that the implementation matches the spec, all tests pass, code quality checks succeed, and sign off on the build.
@@ -17,6 +17,9 @@ execution_modes:
17
17
  allowed-tools: "Bash, Read, Write, Edit, Grep, Glob, Task"
18
18
  argument-hint: "apt:verify-proof [--multi-model | --video]"
19
19
  gates: []
20
+ config_keys:
21
+ - verification.open_report
22
+ - verification.report_formats
20
23
  ---
21
24
 
22
25
  # Proof Verification
@@ -86,8 +89,10 @@ For multi-model verification details:
86
89
 
87
90
  ## Config
88
91
 
89
- Settings in `.aperant/config.json` control behavior (run `/apt:setup` to configure):
90
- - `verification.evidence_capture` — capture screenshots + terminal output (default: true)
91
- - `verification.open_report` — open HTML in browser when done (default: true)
92
- - `multi_model.enabled` — run multi-model verification (default: false)
93
- - `multi_model.gemini.model` — Gemini preset: default/thorough/fast
92
+ Settings live in the MERGED Aperant config (shared `.aperant/config.json` + per-device `.aperant/config.local.json`; local wins). Run `/apt:setup` to configure. Read via `apt-tools config read .` or the framework's `loadMergedProjectConfig()` — NEVER raw `JSON.parse(readFileSync('.aperant/config.json'))`, which misses per-device overrides.
93
+
94
+ - `verification.evidence_capture` — shared. capture screenshots + terminal output (default: true)
95
+ - `verification.open_report` — **per-device (config.local.json)** per AUDIT-001. open HTML in browser when done (default: true)
96
+ - `multi_model.enabled` — shared. run multi-model verification (default: false)
97
+ - `multi_model.cross_verification.tools` — **per-device (config.local.json)** per AUDIT-001. which CLIs to cross-verify with.
98
+ - `multi_model.gemini.model` — shared. Gemini preset: default/thorough/fast
@@ -0,0 +1,166 @@
1
+ ---
2
+ name: apt:watch-ci
3
+ description: "Post-ship CI watcher — polls PR checks, auto-fixes red, pushes, reschedules"
4
+ apt-skill-version: {{APT_VERSION}}
5
+ stage: ship
6
+ intent: x-watch-ci
7
+ when_to_use: "A PR has been shipped and CI is running. Use to watch its checks on a recurring tick, auto-fix red checks from first red, push fixes, and reschedule until green / merged / closed / 5 attempts / 4h wall."
8
+ user_invocable: true
9
+ internal: false
10
+ spawns_agent: false
11
+ agent_name: null
12
+ task_context: require-existing
13
+ default_execution_mode: auto
14
+ execution_modes:
15
+ - auto
16
+ - step
17
+ allowed-tools: "Bash, Read, Write, Edit, Grep, Glob"
18
+ argument-hint: "apt:watch-ci [--pr N | --tick --pr N | --stop --pr N | --resume --pr N]"
19
+ gates: []
20
+ config_keys:
21
+ - ci_watch.after_ship
22
+ - ci_watch.auto_merge_on_green
23
+ ---
24
+
25
+ # /apt:watch-ci — Post-ship CI Watcher
26
+
27
+ You are a **post-ship CI watcher**. After a PR has been opened by `/apt:ship`, you watch its checks on a recurring tick, auto-fix red checks from the first red, push the fix, and reschedule yourself until the PR is green / merged / closed, or you hit the 5-attempt / 4h wall budget.
28
+
29
+ This skill is **one-tick-per-invocation** (D-01). It MUST NOT enter a polling loop. The recurring beat is delivered externally — by Claude Code's `CronCreate` on cron-capable hosts, by Pi's `pi-schedule-prompt` on Pi, or by the user re-invoking `/apt:watch-ci --tick --pr N` on non-cron hosts. Each invocation does at most one of: poll + classify + maybe-fix + push + reschedule, then exits.
30
+
31
+ The locked decisions backing every section are in `.aperant/context/notes/framework-post-ship-ci-watcher.md` (D-01..D-08).
32
+
33
+ ---
34
+
35
+ ## Your environment
36
+
37
+ - **Working directory:** the task's worktree (or the project root if this is a non-worktree task).
38
+ - **State files:** `.aperant/ci-watches/{pr}.json` (per-PR state, D-04); `.aperant/ci-watches/{pr}.lock` (per-PR atomic lock, D-05). Both ephemeral — they are NOT committed.
39
+ - **Events:** every tick appends a `ci-watch.tick` event to `.aperant/events/{today}.jsonl`; `start` appends `ci-watch.armed`; the fix-and-push path appends `ci-watch.fix-pushed`; terminal actions append `ci-watch.stopped` with a `reason`.
40
+ - **CLI surface:** `apt-tools ci-watch start|stop|status|tick --pr N`. The skill body shells out to those subcommands — DO NOT re-implement the classifier in your prompt; the cmd already does it.
41
+
42
+ ---
43
+
44
+ ## §1. Resolve mode
45
+
46
+ Parse the flags:
47
+ - No flag → "start a watch for the active task's PR" (resolve PR number from `.aperant/state.json` active task's `pr_url` field).
48
+ - `--tick --pr N` → run one tick for PR N. This is the cron payload's invocation form.
49
+ - `--stop --pr N` → tear down the watch.
50
+ - `--resume --pr N` → clear the `paused: true` flag and return to normal tick.
51
+
52
+ If no PR can be resolved (no active task, no `pr_url`), print an error and exit — there is nothing to watch.
53
+
54
+ ---
55
+
56
+ ## §2. Lock acquire
57
+
58
+ For everything except `status`, the imperative work happens inside `apt-tools ci-watch tick`, which acquires the per-PR lock itself. If `tick` returns exit code 2 with `{status:"busy", reason:"lock-held"}`, another invocation is already running for this PR. Print:
59
+
60
+ ```
61
+ Watcher already running for PR {N} (held by sessionId={x}, pid={y}); exiting.
62
+ ```
63
+
64
+ …and exit. Do NOT block, retry, or kill the holder.
65
+
66
+ ---
67
+
68
+ ## §3. One-tick semantics (the heart of D-01)
69
+
70
+ For the `--tick` mode, execute exactly the following sequence — once — inside this single skill invocation:
71
+
72
+ 1. **Poll.** Call `gh pr checks <N> --json name,bucket,state,conclusion,detailsUrl` and `gh pr view <N> --json state,reviewDecision`. Combine both JSON outputs into a single file shaped `{checks: <pr-checks-json>, prMetadata: <pr-view-json>}` (for example with `jq -s '{checks: .[0], prMetadata: .[1]}'`) and save it as `{captured.json}` (any path under the worktree is fine). On any `gh` failure, omit the file and let the cmd's `tick` route to `infra-backoff` (it surfaces the same action when no payload is available).
73
+ 2. **Classify + state update.** Call `apt-tools ci-watch tick {worktree} --pr {N} --payload-file {captured.json}`. The cmd reads `{captured.json}` from disk and runs the classifier — it does NOT shell out to `gh` itself (that boundary stays with the orchestrator skill; see commands/ci-watch.mjs:15). The cmd internally:
74
+ - acquires the per-PR lock (BUSY → exit 2 → step §2);
75
+ - reads the per-PR state file;
76
+ - runs the stop-matrix classifier (`packages/framework/src/cli/ci-watch/stop-matrix.mjs`);
77
+ - applies the action's state mutation (attempts++, last_status fingerprint, paused, etc.);
78
+ - releases the lock;
79
+ - returns `{status, action, attempts_used, next_tick_at, reason}`.
80
+ 3. **Branch on `action`** — see §4 (terminal), §5 (fix), §6 (non-counting), §7 (pause/resume).
81
+ 4. **Reschedule (next tick).** On Claude Code: refresh the `CronCreate` entry's next-fire timestamp (CronCreate is idempotent on the same payload). On Pi: do the equivalent via `pi-schedule-prompt`. On other hosts: print `Next tick: run /apt:watch-ci --tick --pr {N} when you return.` Do NOT register a cron on a non-cron host — the framework would be lying.
82
+ 5. **Emit event.** `apt-tools event append . --type ci-watch.tick --data '{...}'` with the cmd's envelope as the data payload. Non-fatal if it fails.
83
+
84
+ ---
85
+
86
+ ## §4. Terminal branches (green / merged / closed / *-cap / identical-log)
87
+
88
+ When `action ∈ {green, merged, closed, attempts-cap, wall-cap, identical-log}`:
89
+
90
+ - **green** — print "CI green for PR {N}." If `ci_watch.auto_merge_on_green` is `true` in `.aperant/config.json`, run `gh pr merge {N} --auto --squash` then suggest `/apt:close-task`.
91
+ - **DO NOT pass `--delete-branch`** to `gh pr merge` from this auto-merge path — ever, unconditionally. The watcher's cwd is the task worktree, where `gh`'s `--delete-branch` post-merge cleanup tries to `git switch <base>` and fails because `<base>` is checked out in the primary worktree. The remote merge has already succeeded by then; local branch + worktree cleanup is `/apt:close-task`'s job via `apt-tools task close-merged` → `computeWorktreeCleanup` (see FRAMEWORK-BUG-019).
92
+
93
+ Else print the exact D-08 text:
94
+
95
+ ```
96
+ ✅ CI green — merge in browser or run /apt:close-task post-merge
97
+ ```
98
+
99
+ - **merged** / **closed** — print a one-line summary. No PR comment needed.
100
+
101
+ - **attempts-cap** / **wall-cap** / **identical-log** — these are bail conditions. Leave a PR comment via `gh pr comment {N} --body "<summary>"` describing why the watcher stopped (attempt count, deadline, fingerprint). The user comes back to find a structured note in the PR.
102
+
103
+ In all terminal cases:
104
+ - Stop the host scheduler (`CronDelete` on Claude Code, `pi schedule remove` on Pi, no-op elsewhere).
105
+ - The cmd already deleted the state file when it returned `status: 'stopped'`. Do NOT re-delete it.
106
+ - Emit `ci-watch.stopped` event with the matching `reason`.
107
+
108
+ ---
109
+
110
+ ## §5. Fix-and-push (D-02 — the auto-fix path)
111
+
112
+ When `action === 'fix'` (or `diverged-rebased`/`diverged-bailed`):
113
+
114
+ 1. **Fetch the failed-check log.** From the gh payload, find the failed check's `detailsUrl` and call `gh run view {runId} --log` to capture the log into a temp file.
115
+ 2. **Shell out to the host CLI** to fix it. The watcher does NOT inline a custom fix prompt; it hands the worktree + failed-log to the same CLI binary that's running this skill (detected by `apt-tools host-detect`). Pseudocode:
116
+
117
+ ```bash
118
+ <host-cli-bin> --print "Fix this CI failure for PR {N}. The failed-run log is at {log_path}. Use the worktree at {worktree}. Land + push a commit with the fix." \
119
+ --cwd {worktree}
120
+ ```
121
+
122
+ The fix-CLI is responsible for the actual edits + verification + commit + push. This is a clean shell-out boundary — the watcher trusts the fix-CLI to land a commit (or to bail with a non-zero exit code, which the watcher treats as "no progress made this tick").
123
+ 3. **Did the fix change anything?** Run `git -C {worktree} status --porcelain`. If empty, the fixer made no changes → the next tick's classifier will see the same failure log and emit `identical-log`. Do NOT push.
124
+ 4. **Push.** `git -C {worktree} push origin {branch}`. On push failure, the next tick will route through `infra-backoff`.
125
+ 5. **Emit event.** `apt-tools event append . --type ci-watch.fix-pushed --data '{pr, attempts, commit_sha}'`.
126
+ 6. **Reschedule** per §3 step 4.
127
+
128
+ For `diverged-rebased`: the cmd already returned the verb; the orchestrator runs `git -C {worktree} fetch && git rebase origin/{base}` BEFORE the fix step. On conflict the action becomes `diverged-bailed` and the watcher exits with a PR comment summarizing the conflict.
129
+
130
+ ---
131
+
132
+ ## §6. Non-counting branches (flake-skip / infra-backoff)
133
+
134
+ When `action === 'flake-skip'` or `'infra-backoff'`:
135
+
136
+ - **flake-skip:** Skip the fix path entirely. Reschedule at the normal interval. No PR comment.
137
+ - **infra-backoff:** Same skip — but the next tick interval is doubled by the cmd (it wrote `next_tick_interval_ms *= 2` to state). The cron host re-invocation reads it. After one retry, if the same infra signal persists, post a PR comment marking the failure as `infra-incident`; do NOT keep doubling forever.
138
+
139
+ ---
140
+
141
+ ## §7. --stop / --resume flags
142
+
143
+ - `--stop --pr N` — releases scheduler handle (`CronDelete` on CC, `pi schedule remove` on Pi, no-op elsewhere), deletes state file, emits `ci-watch.stopped` event with `reason:"user-stop"`. Idempotent — calling `--stop` on a PR with no active watch is fine.
144
+ - `--resume --pr N` — only valid when state has `paused: true` (set by `reviewer-pause`). Clears the flag, returns to normal tick. Error envelope if no paused state for the PR.
145
+
146
+ When the classifier returns `reviewer-pause`, the state's `paused: true` is set by the cmd, but the scheduler is NOT removed — the cron payload keeps firing, and each tick the cmd checks `paused` and exits early with action `reviewer-pause`. Cheaper than re-registering on `--resume`.
147
+
148
+ ---
149
+
150
+ ## §8. Single-execution-mode contract
151
+
152
+ Reiterating because this is the most easily-broken invariant:
153
+
154
+ - One tick = one invocation. **Do not** wrap the §3 sequence in a `while`/`until` loop.
155
+ - **Do not** sleep + re-invoke yourself inside a single session.
156
+ - The recurring beat is delivered by host cron OR by the user re-invoking the skill manually. The watcher's value comes from the host scheduler — it does not impersonate one.
157
+
158
+ If you find yourself wanting to add "and then wait 5 minutes and run again," STOP. The skill is finished after one tick. Exit.
159
+
160
+ ---
161
+
162
+ ## Notes for the orchestrator
163
+
164
+ - All gh CLI calls run with the host's existing auth. The watcher introduces no new secret material (per task `dimensions_covered.token-encryption.crossed: false`).
165
+ - `auto_merge_on_green` is snapshotted from `.aperant/config.json` at `start` time (state file's `auto_merge_on_green` field). Changing the config mid-watch does NOT retroactively affect armed watches — re-run `start` to pick up the new value.
166
+ - The fix-CLI shell-out is the cleanest abstraction we have for portability. It's NOT a sub-agent. On Claude Code that means another `claude` invocation; on Codex/Gemini/OpenCode/Pi, the equivalent binary. Detection lives in `apt-tools host-detect`'s `cli` field.
@@ -0,0 +1,130 @@
1
+ ---
2
+ name: apt:zoom-out
3
+ description: "Orient yourself in an unfamiliar area of the codebase — load CONTEXT.md, map modules + callers, output a one-page orientation"
4
+ apt-skill-version: {{APT_VERSION}}
5
+ stage: plan
6
+ intent: investigate
7
+ when_to_use: "The user is dropped into a part of the codebase they don't know and wants a one-page architectural orientation before planning or editing. Natural-language triggers: 'give me a broader picture of X', 'I'm lost in this area', 'explain how this fits'."
8
+ user_invocable: true
9
+ internal: false
10
+ spawns_agent: false
11
+ agent_name: null
12
+ task_context: none
13
+ default_track: STANDARD
14
+ default_execution_mode: auto
15
+ execution_modes:
16
+ - auto
17
+ - step
18
+ allowed-tools: "Bash, Read, Grep, Glob"
19
+ argument-hint: "apt:zoom-out [area-name]"
20
+ gates: []
21
+ ---
22
+
23
+ # Zoom-Out — One-page Architectural Orientation
24
+
25
+ Tiny skill. High signal. The user named an area; you produce a one-page
26
+ orientation so they (or a future agent) can plan or edit without
27
+ re-learning the codebase from scratch.
28
+
29
+ **Pocock adoption note.** This skill descends from Matt Pocock's `zoom-out`
30
+ discipline. Aperant's router is dynamic and LLM-driven, so unlike Pocock's
31
+ upstream this skill is **router-invocable** (no `disable-model-invocation:
32
+ true` flag). The work-intent — "give me a broader picture of X" — is
33
+ genuine and the router resolves natural-language triggers without forcing
34
+ the user to remember the verb name.
35
+
36
+ ## When to Use
37
+
38
+ - Dropped into a new module / package / area of the monorepo
39
+ - About to plan a feature that crosses an unfamiliar boundary
40
+ - The codebase has CONTEXT.md (Pocock glossary) and you want to load it
41
+ alongside the structural map
42
+
43
+ ## When Not to Use
44
+
45
+ - The area is one you know well — `/apt:plan` already does targeted
46
+ investigation
47
+ - You need to lock decisions (use `/apt:discuss` with the
48
+ `appendices/grill-discipline.md` posture loader instead)
49
+ - The user wants to refactor proactively — that's `/apt:improve`
50
+
51
+ ## Process
52
+
53
+ ### 1. Parse the area name
54
+
55
+ Extract the area from `$ARGUMENTS`. The area is a noun phrase, not a
56
+ file path: e.g., `"chat session lifecycle"`, `"sparring engine"`,
57
+ `"feature registry"`. If the argument is a file path, narrow the
58
+ orientation to the file's enclosing module.
59
+
60
+ ### 2. Load CONTEXT.md (if present)
61
+
62
+ If `CONTEXT.md` exists at the project root, read it and pre-load any
63
+ term-blocks whose `Term` matches keywords in the area name (or whose
64
+ `Aliases to avoid` field hints at the area). Carry these terms forward
65
+ into the orientation report so the reader sees the vocabulary the
66
+ codebase actually uses.
67
+
68
+ If `CONTEXT.md` does not exist, skip silently — orientation still works,
69
+ just without the glossary anchor.
70
+
71
+ ### 3. Map modules and callers
72
+
73
+ Run Glob + Grep to identify:
74
+
75
+ - **Entry points** — files in the area that other modules import (high
76
+ fan-in: many `import`-ers).
77
+ - **State owners** — files that declare the area's persistent
78
+ state (Zustand stores, Convex queries, libSQL tables).
79
+ - **External boundaries** — files that touch external systems (HTTP,
80
+ IPC, native modules, child processes).
81
+ - **Known fragile areas** — search for `// TODO`, `// FIXME`, `// HACK`
82
+ comments + any recent commits touching the area (last 30 days).
83
+
84
+ ### 4. Emit the one-page orientation
85
+
86
+ Output to stdout (no file written — this is a read-only orientation):
87
+
88
+ ```markdown
89
+ # Orientation: {area name}
90
+
91
+ ## Purpose
92
+ {One paragraph: what does this area exist to do?}
93
+
94
+ ## Entry points
95
+ - `{path}` — {one-line role}
96
+ - `{path}` — {one-line role}
97
+
98
+ ## State owners
99
+ - `{path}` — {one-line: what state, what scope}
100
+
101
+ ## External boundaries
102
+ - `{path}` — {one-line: which external system, what direction}
103
+
104
+ ## Known fragile areas
105
+ - `{path}:{line}` — `// TODO` / `// FIXME` summary + age
106
+ - {recent-commit-shortlog one-liner if relevant}
107
+
108
+ ## Glossary (from CONTEXT.md)
109
+ - **{Term}** — definition + aliases-to-avoid
110
+ - {…}
111
+
112
+ ## Pointers for the next agent
113
+ - {one-line: where to start reading}
114
+ - {one-line: which test file to read first}
115
+ - {one-line: which CONTEXT.md term resolves the most ambiguity}
116
+ ```
117
+
118
+ The orientation MUST fit on one screen. If it doesn't, you've gone too
119
+ deep — re-scope the area name and try again.
120
+
121
+ ## Output contract
122
+
123
+ - **Read-only.** This skill never writes files. It is a context-loader,
124
+ not a planner.
125
+ - **Stops at orientation.** Do NOT pivot into `/apt:plan` automatically.
126
+ The user invokes `/apt:plan` (or `/apt:improve`) as a follow-up if the
127
+ orientation surfaces a planning opportunity.
128
+ - **No grill posture.** This skill does NOT load
129
+ `apt-discuss/appendices/grill-discipline.md` — orientation is
130
+ descriptive, not normative. Locking decisions is `apt:discuss`'s job.