@aperant/framework 0.6.7 → 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.
- package/CHANGELOG.md +240 -0
- package/agents/apt-planner.md +12 -0
- package/agents/apt-pr-review-fixer.md +13 -9
- package/bin/apt-tools.mjs +7 -0
- package/dist/cli/ci-watch/stop-matrix.d.mts.map +1 -1
- package/dist/cli/ci-watch/stop-matrix.mjs +16 -0
- package/dist/cli/ci-watch/stop-matrix.mjs.map +1 -1
- package/dist/cli/commands/ci-watch.d.mts +11 -0
- package/dist/cli/commands/ci-watch.d.mts.map +1 -1
- package/dist/cli/commands/ci-watch.mjs +147 -3
- package/dist/cli/commands/ci-watch.mjs.map +1 -1
- package/dist/cli/commands/features-audit.d.mts +24 -0
- package/dist/cli/commands/features-audit.d.mts.map +1 -1
- package/dist/cli/commands/features-audit.mjs +159 -5
- package/dist/cli/commands/features-audit.mjs.map +1 -1
- package/dist/cli/commands/health-check.d.mts +16 -0
- package/dist/cli/commands/health-check.d.mts.map +1 -1
- package/dist/cli/commands/health-check.mjs +119 -3
- package/dist/cli/commands/health-check.mjs.map +1 -1
- package/dist/cli/commands/init.d.mts +19 -1
- package/dist/cli/commands/init.d.mts.map +1 -1
- package/dist/cli/commands/init.mjs +143 -8
- package/dist/cli/commands/init.mjs.map +1 -1
- package/dist/cli/commands/modes.d.mts.map +1 -1
- package/dist/cli/commands/modes.mjs +11 -0
- package/dist/cli/commands/modes.mjs.map +1 -1
- package/dist/cli/commands/pr-review-audit-fixer.d.mts +13 -0
- package/dist/cli/commands/pr-review-audit-fixer.d.mts.map +1 -1
- package/dist/cli/commands/pr-review-audit-fixer.mjs +18 -5
- package/dist/cli/commands/pr-review-audit-fixer.mjs.map +1 -1
- package/dist/cli/commands/route.d.mts.map +1 -1
- package/dist/cli/commands/route.mjs +37 -2
- package/dist/cli/commands/route.mjs.map +1 -1
- package/dist/cli/commands/task.d.mts.map +1 -1
- package/dist/cli/commands/task.mjs +132 -5
- package/dist/cli/commands/task.mjs.map +1 -1
- package/dist/cli/commands/validate-evidence.d.mts +24 -2
- package/dist/cli/commands/validate-evidence.d.mts.map +1 -1
- package/dist/cli/commands/validate-evidence.mjs +154 -17
- package/dist/cli/commands/validate-evidence.mjs.map +1 -1
- package/dist/cli/commands/vitest-doctor.d.mts +2 -0
- package/dist/cli/commands/vitest-doctor.d.mts.map +1 -0
- package/dist/cli/commands/vitest-doctor.mjs +168 -0
- package/dist/cli/commands/vitest-doctor.mjs.map +1 -0
- package/dist/cli/config/gitignore-drift.d.mts +23 -1
- package/dist/cli/config/gitignore-drift.d.mts.map +1 -1
- package/dist/cli/config/gitignore-drift.mjs +81 -3
- package/dist/cli/config/gitignore-drift.mjs.map +1 -1
- package/dist/cli/config/load.d.mts +56 -2
- package/dist/cli/config/load.d.mts.map +1 -1
- package/dist/cli/config/load.mjs +192 -2
- package/dist/cli/config/load.mjs.map +1 -1
- package/dist/cli/consistency/parse-review.mjs +7 -0
- package/dist/cli/consistency/parse-review.mjs.map +1 -1
- package/dist/cli/dispatch.d.mts.map +1 -1
- package/dist/cli/dispatch.mjs +24 -2
- package/dist/cli/dispatch.mjs.map +1 -1
- package/dist/cli/gate/gates/gitignore-in-sync.d.mts +1 -1
- package/dist/cli/gate/gates/gitignore-in-sync.d.mts.map +1 -1
- package/dist/cli/gate/gates/gitignore-in-sync.mjs +5 -2
- package/dist/cli/gate/gates/gitignore-in-sync.mjs.map +1 -1
- package/dist/cli/gate/gates/review-clean.d.mts +5 -1
- package/dist/cli/gate/gates/review-clean.d.mts.map +1 -1
- package/dist/cli/gate/gates/review-clean.mjs +23 -18
- package/dist/cli/gate/gates/review-clean.mjs.map +1 -1
- package/dist/cli/gate/gates/verify-approved.d.mts +49 -1
- package/dist/cli/gate/gates/verify-approved.d.mts.map +1 -1
- package/dist/cli/gate/gates/verify-approved.mjs +93 -14
- package/dist/cli/gate/gates/verify-approved.mjs.map +1 -1
- package/dist/cli/help.d.mts.map +1 -1
- package/dist/cli/help.mjs +8 -2
- package/dist/cli/help.mjs.map +1 -1
- package/dist/cli/host/detect.d.mts +122 -9
- package/dist/cli/host/detect.d.mts.map +1 -1
- package/dist/cli/host/detect.mjs +132 -0
- package/dist/cli/host/detect.mjs.map +1 -1
- package/dist/cli/install/legacy-paths.d.mts +38 -0
- package/dist/cli/install/legacy-paths.d.mts.map +1 -0
- package/dist/cli/install/legacy-paths.mjs +69 -0
- package/dist/cli/install/legacy-paths.mjs.map +1 -0
- package/dist/cli/install/runtime-detect.d.mts +13 -0
- package/dist/cli/install/runtime-detect.d.mts.map +1 -1
- package/dist/cli/install/runtime-detect.mjs +9 -0
- package/dist/cli/install/runtime-detect.mjs.map +1 -1
- package/dist/cli/install/runtime-migrate.d.mts +84 -0
- package/dist/cli/install/runtime-migrate.d.mts.map +1 -0
- package/dist/cli/install/runtime-migrate.mjs +244 -0
- package/dist/cli/install/runtime-migrate.mjs.map +1 -0
- package/dist/cli/route/drift-detect.d.mts +20 -0
- package/dist/cli/route/drift-detect.d.mts.map +1 -0
- package/dist/cli/route/drift-detect.mjs +107 -0
- package/dist/cli/route/drift-detect.mjs.map +1 -0
- package/dist/cli/task/index-md.d.mts.map +1 -1
- package/dist/cli/task/index-md.mjs +14 -2
- package/dist/cli/task/index-md.mjs.map +1 -1
- package/dist/cli/util/aperant-section.d.mts +34 -0
- package/dist/cli/util/aperant-section.d.mts.map +1 -0
- package/dist/cli/util/aperant-section.mjs +127 -0
- package/dist/cli/util/aperant-section.mjs.map +1 -0
- package/dist/cli/util/copy.d.mts +28 -1
- package/dist/cli/util/copy.d.mts.map +1 -1
- package/dist/cli/util/copy.mjs +43 -55
- package/dist/cli/util/copy.mjs.map +1 -1
- package/dist/cli/util/semver.d.mts +17 -0
- package/dist/cli/util/semver.d.mts.map +1 -0
- package/dist/cli/util/semver.mjs +29 -0
- package/dist/cli/util/semver.mjs.map +1 -0
- package/dist/cli/util/skill-installs.d.mts +65 -9
- package/dist/cli/util/skill-installs.d.mts.map +1 -1
- package/dist/cli/util/skill-installs.mjs +130 -21
- package/dist/cli/util/skill-installs.mjs.map +1 -1
- package/dist/cli/util/version-preflight.d.mts +44 -0
- package/dist/cli/util/version-preflight.d.mts.map +1 -0
- package/dist/cli/util/version-preflight.mjs +66 -0
- package/dist/cli/util/version-preflight.mjs.map +1 -0
- package/dist/plugin/.claude-plugin/plugin.json +11 -2
- package/dist/plugin/agents/apt-improver.md +99 -0
- package/dist/plugin/agents/apt-planner.md +127 -10
- package/dist/plugin/agents/apt-pr-review-fixer.md +13 -9
- package/dist/plugin/skills/apt/SKILL.md +1 -0
- package/dist/plugin/skills/apt-close-task/SKILL.md +63 -1
- package/dist/plugin/skills/apt-debug/SKILL.md +39 -6
- package/dist/plugin/skills/apt-debug/appendices/diagnose-discipline.md +119 -0
- package/dist/plugin/skills/apt-diagram/SKILL.md +378 -0
- package/dist/plugin/skills/apt-diagram/appendices/design-discipline.md +97 -0
- package/dist/plugin/skills/apt-discuss/SKILL.md +72 -5
- package/dist/plugin/skills/apt-discuss/appendices/grill-discipline.md +104 -0
- package/dist/plugin/skills/apt-discuss/appendices/zoom-out-helper.md +79 -0
- package/dist/plugin/skills/apt-execute/SKILL.md +57 -5
- package/dist/plugin/skills/apt-execute/appendices/tdd-mode.md +107 -0
- package/dist/plugin/skills/apt-improve/DEEPENING.md +84 -0
- package/dist/plugin/skills/apt-improve/INTERFACE-DESIGN.md +97 -0
- package/dist/plugin/skills/apt-improve/LANGUAGE.md +104 -0
- package/dist/plugin/skills/apt-improve/SKILL.md +141 -0
- package/dist/plugin/skills/apt-plan/SKILL.md +171 -4
- package/dist/plugin/skills/apt-plan/adapters/conductor.md +98 -0
- package/dist/plugin/skills/apt-pr-review/SKILL.md +57 -18
- package/dist/plugin/skills/apt-prototype/LOGIC.md +109 -0
- package/dist/plugin/skills/apt-prototype/SKILL.md +143 -0
- package/dist/plugin/skills/apt-prototype/UI.md +90 -0
- package/dist/plugin/skills/apt-quick/SKILL.md +49 -8
- package/dist/plugin/skills/apt-release-notes/SKILL.md +193 -0
- package/dist/plugin/skills/apt-release-notes/appendices/persona-voice.md +59 -0
- package/dist/plugin/skills/apt-review/SKILL.md +2 -0
- package/dist/plugin/skills/apt-run/SKILL.md +32 -4
- package/dist/plugin/skills/apt-setup/SKILL.md +308 -6
- package/dist/plugin/skills/apt-ship/SKILL.md +122 -1
- package/dist/plugin/skills/apt-spar/SKILL.md +315 -0
- package/dist/plugin/skills/apt-triage/AGENT-BRIEF.md +84 -0
- package/dist/plugin/skills/apt-triage/OUT-OF-SCOPE.md +75 -0
- package/dist/plugin/skills/apt-triage/SKILL.md +169 -0
- package/dist/plugin/skills/apt-update/SKILL.md +77 -10
- package/dist/plugin/skills/apt-verify/SKILL.md +3 -0
- package/dist/plugin/skills/apt-verify-proof/SKILL.md +10 -5
- package/dist/plugin/skills/apt-watch-ci/SKILL.md +166 -0
- package/dist/plugin/skills/apt-zoom-out/SKILL.md +130 -0
- package/package.json +133 -133
- package/prompts/conductor-framework-context.md +63 -0
- package/prompts/conductor-system.md +11 -0
- package/skills/apt-close-task/SKILL.md +6 -0
- package/skills/apt-discuss/SKILL.md +47 -5
- package/skills/apt-execute/SKILL.md +9 -0
- package/skills/apt-plan/SKILL.md +12 -0
- package/skills/apt-pr-review/SKILL.md +11 -2
- package/skills/apt-quick/SKILL.md +19 -8
- package/skills/apt-researcher.md +1 -0
- package/skills/apt-setup/SKILL.md +33 -2
- package/skills/apt-ship/SKILL.md +16 -4
- package/skills/apt-spar/SKILL.md +36 -11
- package/skills/apt-update/SKILL.md +26 -1
- package/skills/apt-verify-proof/SKILL.md +7 -5
- package/skills/apt-watch-ci/SKILL.md +4 -1
- package/src/cli/ci-watch/stop-matrix.mjs +17 -0
- package/src/cli/commands/ci-watch.mjs +152 -3
- package/src/cli/commands/features-audit.mjs +164 -5
- package/src/cli/commands/health-check.mjs +116 -3
- package/src/cli/commands/init.mjs +154 -6
- package/src/cli/commands/modes.mjs +11 -0
- package/src/cli/commands/pr-review-audit-fixer.mjs +18 -5
- package/src/cli/commands/route.mjs +38 -2
- package/src/cli/commands/task.mjs +132 -5
- package/src/cli/commands/validate-evidence.mjs +158 -17
- package/src/cli/commands/vitest-doctor.mjs +173 -0
- package/src/cli/config/gitignore-drift.mjs +74 -3
- package/src/cli/config/load.mjs +188 -2
- package/src/cli/consistency/parse-review.mjs +6 -0
- package/src/cli/dispatch.mjs +23 -2
- package/src/cli/gate/gates/gitignore-in-sync.mjs +5 -2
- package/src/cli/gate/gates/review-clean.mjs +24 -19
- package/src/cli/gate/gates/verify-approved.mjs +97 -14
- package/src/cli/help.mjs +8 -2
- package/src/cli/host/detect.mjs +135 -0
- package/src/cli/install/legacy-paths.mjs +69 -0
- package/src/cli/install/runtime-detect.mjs +9 -0
- package/src/cli/install/runtime-migrate.mjs +252 -0
- package/src/cli/route/drift-detect.mjs +107 -0
- package/src/cli/task/index-md.mjs +15 -2
- package/src/cli/util/aperant-section.mjs +136 -0
- package/src/cli/util/copy.mjs +43 -56
- package/src/cli/util/semver.mjs +28 -0
- package/src/cli/util/skill-installs.mjs +134 -21
- package/src/cli/util/version-preflight.mjs +65 -0
- package/templates/aperant-claude-md-appendix.md +37 -0
- package/templates/config.json +2 -7
- package/workflows/verify-proof.md +8 -3
|
@@ -1111,6 +1111,34 @@ The selective-edit pass through Batch 9 MUST respect the §1b dispatch — it do
|
|
|
1111
1111
|
|
|
1112
1112
|
## 4. Map Answers to Config
|
|
1113
1113
|
|
|
1114
|
+
**AUDIT-001 — file-routing contract for per-device fields (UPDATED 0.7.2):** the
|
|
1115
|
+
following five fields are PER-DEVICE and MUST land in `.aperant/config.local.json`
|
|
1116
|
+
(gitignored), NOT the shared `.aperant/config.json`:
|
|
1117
|
+
- `verification.open_report` — covered in the "Auto-Open" mapping below.
|
|
1118
|
+
- `multi_model.cross_verification.tools` — covered in the "Cross-Verification" mapping below.
|
|
1119
|
+
- `preferences.date_format` — covered in the "Date Format" mapping below.
|
|
1120
|
+
- `preferences.time_format` — covered in the "Time Format" mapping below.
|
|
1121
|
+
- `preferences.quick_task_post_verify` — covered in apt-quick SKILL config block.
|
|
1122
|
+
|
|
1123
|
+
All other fields (including `verification.evidence_capture`,
|
|
1124
|
+
`multi_model.cross_verification.enabled`, `multi_model.video_review.*`,
|
|
1125
|
+
`preferences.task_index`, every `orchestration.*` value) stay in shared
|
|
1126
|
+
`.aperant/config.json`.
|
|
1127
|
+
|
|
1128
|
+
Implementation: use the framework's `writeLocalConfigField(targetDir, dotPath, value)`
|
|
1129
|
+
helper from `packages/framework/src/cli/config/load.mjs` (or its shell-out
|
|
1130
|
+
equivalent via `apt-tools config write . --local --key <dotpath> --value <json>`)
|
|
1131
|
+
for the five fields above. NEVER hand-roll `JSON.parse(readFileSync('.aperant/config.json'))`
|
|
1132
|
+
followed by `writeFileSync('.aperant/config.json', ...)` for these fields — it
|
|
1133
|
+
will land per-device data in the shared file and re-introduce the
|
|
1134
|
+
shared-config trap AUDIT-001 was designed to close.
|
|
1135
|
+
|
|
1136
|
+
Reads of these fields anywhere in the framework MUST go through
|
|
1137
|
+
`loadMergedProjectConfig(targetDir)` or `readLocalOrSharedField(targetDir, dotPath)`
|
|
1138
|
+
so per-device overrides are honored.
|
|
1139
|
+
|
|
1140
|
+
---
|
|
1141
|
+
|
|
1114
1142
|
Map the user's selections to config values:
|
|
1115
1143
|
|
|
1116
1144
|
**Verify Mode:**
|
|
@@ -1180,13 +1208,16 @@ Note: task ids are slug-first since v0.6.6 — the date-format setting controls
|
|
|
1180
1208
|
|
|
1181
1209
|
## 5. Write Config
|
|
1182
1210
|
|
|
1183
|
-
Write merged config to
|
|
1211
|
+
Write merged config to two files per AUDIT-001 routing (see Step 4 block):
|
|
1212
|
+
|
|
1213
|
+
- Shared `.aperant/config.json` — all project policy values (the bulk of the answers).
|
|
1214
|
+
- Per-device `.aperant/config.local.json` — the five AUDIT-001 fields enumerated in Step 4.
|
|
1184
1215
|
|
|
1185
1216
|
```bash
|
|
1186
1217
|
mkdir -p .aperant
|
|
1187
1218
|
```
|
|
1188
1219
|
|
|
1189
|
-
Write
|
|
1220
|
+
Write each JSON file with 2-space indentation. Bump shared `config.json`'s `version` to 4 if upgrading from an older config (versions 2/3 load forward-compatibly — the new `tdd` block is optional). `config.local.json` is gitignored by `.aperant/.gitignore` — do not check it in.
|
|
1190
1221
|
|
|
1191
1222
|
## 6. Save as Global Defaults (Optional)
|
|
1192
1223
|
|
package/skills/apt-ship/SKILL.md
CHANGED
|
@@ -412,19 +412,30 @@ ci_watch_armed=$(echo "$ship_update" | jq -r '.ci_watch_armed // false')
|
|
|
412
412
|
ci_watch_skip_reason=$(echo "$ship_update" | jq -r '.ci_watch_skip_reason // empty')
|
|
413
413
|
ci_watch_host=$(echo "$ship_update" | jq -r '.ci_watch.host // "unknown"')
|
|
414
414
|
ci_watch_deadline=$(echo "$ship_update" | jq -r '.ci_watch.deadline // empty')
|
|
415
|
+
ci_watch_scheduler_handle=$(echo "$ship_update" | jq -r '.scheduler_handle // empty')
|
|
415
416
|
host_cron=$(node packages/framework/bin/apt-tools.mjs host-detect | jq -r '.host.capabilities.cron')
|
|
416
417
|
```
|
|
417
418
|
|
|
418
419
|
**Autonomy interaction** (apply BEFORE branching, same rule as 7.1): when `autonomy === 3` (YOLO), degrade `ask` → `auto`.
|
|
419
420
|
|
|
420
|
-
**Branch on the task-update envelope
|
|
421
|
+
**Branch on the task-update envelope** (FRAMEWORK-BUG-018 — Track 1: distinguish state-recording from wake-registration):
|
|
421
422
|
|
|
422
|
-
- **`ci_watch_armed=true` AND `host_cron=true
|
|
423
|
+
- **`ci_watch_armed=true` AND `host_cron=true` AND `ci_watch_scheduler_handle` non-empty** — emit:
|
|
423
424
|
|
|
424
425
|
```
|
|
425
426
|
[APT:ship] CI watcher armed (host cron: {ci_watch_host}). Deadline: {ci_watch_deadline}. Stop: /apt:watch-ci --stop --pr {N}.
|
|
426
427
|
```
|
|
427
428
|
|
|
429
|
+
This is the only honest "armed" case — a scheduler handle proves the wake is registered.
|
|
430
|
+
|
|
431
|
+
- **`ci_watch_armed=true` AND `host_cron=true` AND `ci_watch_scheduler_handle` empty** — emit:
|
|
432
|
+
|
|
433
|
+
```
|
|
434
|
+
[APT:ship] CI watcher state created (host: {ci_watch_host}). Register a wake-up to drive ticks: CronCreate every ~3min running '/apt:watch-ci --tick --pr {N}'. Deadline: {ci_watch_deadline}. Stop: /apt:watch-ci --stop --pr {N}.
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
State is recorded but no scheduler is registered — the orchestrator MUST register a wake or ticks will never fire. Track 2 (auto-wake on arm) will populate `scheduler_handle` and route into the first branch instead.
|
|
438
|
+
|
|
428
439
|
- **`ci_watch_armed=true` AND `host_cron=false`** — emit:
|
|
429
440
|
|
|
430
441
|
```
|
|
@@ -442,10 +453,11 @@ host_cron=$(node packages/framework/bin/apt-tools.mjs host-detect | jq -r '.host
|
|
|
442
453
|
On `Y` (or empty enter), call:
|
|
443
454
|
|
|
444
455
|
```bash
|
|
445
|
-
node packages/framework/bin/apt-tools.mjs ci-watch start . --pr {pr_number} --task-id {task-id} --host {cli}
|
|
456
|
+
arm_envelope=$(node packages/framework/bin/apt-tools.mjs ci-watch start . --pr {pr_number} --task-id {task-id} --host {cli})
|
|
457
|
+
ci_watch_scheduler_handle=$(echo "$arm_envelope" | jq -r '.scheduler_handle // empty')
|
|
446
458
|
```
|
|
447
459
|
|
|
448
|
-
Then
|
|
460
|
+
Then route into the same four-way logic above — branch 1 (armed with handle), branch 2 (state-created, needs wake), or branch 3 (host has no cron). On `N` or any other response, proceed silently to Section 8.
|
|
449
461
|
|
|
450
462
|
- **`ci_watch_skip_reason=disabled`** — silent. Proceed directly to Section 8.
|
|
451
463
|
|
package/skills/apt-spar/SKILL.md
CHANGED
|
@@ -129,14 +129,26 @@ If `--with` resolves to the same CLI as the host (e.g. `--with claude` when `hos
|
|
|
129
129
|
|
|
130
130
|
Do NOT proceed; self-sparring is sycophancy by construction (the host cannot objectively challenge itself). This check runs after `host-detect` and before any partner availability check.
|
|
131
131
|
|
|
132
|
-
**Partner
|
|
132
|
+
**Partner transport ladder (FRAMEWORK-BUG-024).** Read `host.partner_transports.<partner>` from the host-detect envelope (`jq '.host.partner_transports.codex'`, etc.). Each partner has a 3-rung ladder evaluated in preference order — **MCP-first** for cleaner tool-call integration, with automatic fallthrough to plugin shim then raw CLI:
|
|
133
|
+
|
|
134
|
+
1. **Rung 1 — MCP tool** (`mcp__<partner>-mcp__*`, e.g. `mcp__codex-mcp__codex`). Detected if the host exposes its deferred-tool manifest via `CLAUDE_MCP_DEFERRED_TOOLS` and the tool is registered. When `detected: "runtime-host-only"` the framework process cannot probe the host's tool registry from outside — treat as candidate and attempt the tool call; if the tool is genuinely unavailable in the host's visible registry, fall through to rung 2.
|
|
135
|
+
2. **Rung 2 — Plugin shim** (`${CLAUDE_PLUGIN_ROOT}/scripts/<partner>-companion.mjs`). Codex only (claude / gemini have no plugin shim).
|
|
136
|
+
3. **Rung 3 — Raw CLI on `$PATH`** (`codex exec`, `claude -p`, `gemini -m`).
|
|
137
|
+
|
|
138
|
+
Walk the ladder in order; pick the first rung with `detected: true` (or `"runtime-host-only"` for MCP). **MCP rung has a hard timeout** — default 60000ms, configurable via `apt-spar.mcp_timeout_ms` in `.aperant/config.json`. Empirical: the Codex MCP `claude_code` tool has been observed to hang past 120s. On MCP timeout, **automatically fall through to rung 2 or rung 3** — do NOT fail the whole spar. Treat MCP timeout as "rung unavailable for this invocation."
|
|
139
|
+
|
|
140
|
+
If all three rungs are unavailable (every rung `detected: false`, or MCP `runtime-host-only` timed out AND rungs 2+3 are `detected: false`), print:
|
|
133
141
|
|
|
134
142
|
```
|
|
135
|
-
[apt:spar] partner `<id>` unavailable.
|
|
136
|
-
-
|
|
137
|
-
-
|
|
143
|
+
[apt:spar] partner `<id>` unavailable. Tried:
|
|
144
|
+
- MCP tool `mcp__<partner>-mcp__*`: <not loaded in this host | runtime-host-only + timed out at <ms>ms>
|
|
145
|
+
- Plugin shim `${CLAUDE_PLUGIN_ROOT}/scripts/<partner>-companion.mjs`: <missing reason>
|
|
146
|
+
- Raw CLI `<bin>` on $PATH: not found
|
|
147
|
+
Install any one of the three or re-run with `--with <other-id>`.
|
|
138
148
|
```
|
|
139
149
|
|
|
150
|
+
The failure message MUST name all three transports tried (`mcp`, `plugin shim`, `raw CLI`) so the user can pick the cheapest fix.
|
|
151
|
+
|
|
140
152
|
Do NOT silently degrade to the host (sparring with yourself is sycophancy by construction).
|
|
141
153
|
|
|
142
154
|
## 4. Frame the Round
|
|
@@ -159,13 +171,26 @@ The frame is one shot per round — no multi-turn conversation with the partner
|
|
|
159
171
|
|
|
160
172
|
**Wrap every partner Bash call with an explicit timeout** (default 90000ms, override via `--timeout-ms`). The empirical reason: the Codex MCP `claude_code` tool has been observed to hang past 120s while the `claude -p` CLI returns in seconds. A hung partner CLI must NOT stall the spar loop.
|
|
161
173
|
|
|
162
|
-
The table below shows the canonical command shape; the actual invocation MUST use the heredoc pattern in the next subsection — never raw double-quoted interpolation.
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
174
|
+
The table below shows the canonical command shape per (partner × rung); the actual invocation for shell-based rungs (plugin shim, raw CLI) MUST use the heredoc pattern in the next subsection — never raw double-quoted interpolation. The MCP rung is a structured tool call — no shell quoting concern applies.
|
|
175
|
+
|
|
176
|
+
<!--
|
|
177
|
+
Future contributor: the `codex exec` flag below is taken from the precedent
|
|
178
|
+
at `packages/framework/skills/apt-pr-review/SKILL.md:1084`. Codex CLI flag
|
|
179
|
+
surface evolves; verify with `codex --help` at execution time and update
|
|
180
|
+
this table if the non-interactive invocation shape changes.
|
|
181
|
+
-->
|
|
182
|
+
|
|
183
|
+
| Partner | Rung | Command | Notes |
|
|
184
|
+
|---|---|---|---|
|
|
185
|
+
| codex | mcp | tool call: `mcp__codex-mcp__codex({prompt})` | Preferred. 60s timeout (configurable `apt-spar.mcp_timeout_ms`); on timeout fall through to plugin then CLI. |
|
|
186
|
+
| codex | plugin | `node "${CLAUDE_PLUGIN_ROOT}/scripts/codex-companion.mjs" task "$(cat <<'APT_SPAR_PROMPT_EOF' ... APT_SPAR_PROMPT_EOF\n)"` | **NO `--write`. NO `--effort` override.** Per SPAR-04 — spar is conversation, not patching. |
|
|
187
|
+
| codex | cli | `codex exec "$(cat <<'APT_SPAR_PROMPT_EOF' ... APT_SPAR_PROMPT_EOF\n)"` | Precedent: `apt-pr-review/SKILL.md:1084`. Verify with `codex --help`. |
|
|
188
|
+
| claude | mcp | tool call: `mcp__claude-code-mcp__claude_code({prompt})` | Preferred. Same 60s timeout + fallthrough rule. |
|
|
189
|
+
| claude | plugin | n/a | No plugin shim exists for Claude. Skip this rung. |
|
|
190
|
+
| claude | cli | `claude -p "$(cat <<'APT_SPAR_PROMPT_EOF' ... APT_SPAR_PROMPT_EOF\n)" --output-format text` | Symmetric. Claude has no `--write` equivalent — asymmetric flags are a smell. |
|
|
191
|
+
| gemini | mcp | tool call: `mcp__gemini-mcp__*({prompt})` | Preferred. Same 60s timeout + fallthrough rule. Any `mcp__gemini-mcp__*` tool satisfies the rung. |
|
|
192
|
+
| gemini | plugin | n/a | No plugin shim exists for Gemini. Skip this rung. |
|
|
193
|
+
| gemini | cli | `gemini -m <model-from-router.llm.providers.gemini-cli> "$(cat <<'APT_SPAR_PROMPT_EOF' ... APT_SPAR_PROMPT_EOF\n)"` | Read the exact model name from `.aperant/config.json` at runtime — do not hardcode. |
|
|
169
194
|
|
|
170
195
|
Set the Bash tool's `timeout` parameter to `--timeout-ms` (default 90000).
|
|
171
196
|
|
|
@@ -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
|
|
|
@@ -90,6 +90,30 @@ npm install --prefix .aperant/deps @aperant/framework@<latest_version> --no-save
|
|
|
90
90
|
|
|
91
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
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
|
+
|
|
93
117
|
## 2. Classify the update scenario
|
|
94
118
|
|
|
95
119
|
Pick ONE of three scenarios based on what you parsed in step 1:
|
|
@@ -206,5 +230,6 @@ Runtime files were still refreshed; `/apt` will still work from PATH if `apt-too
|
|
|
206
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.
|
|
207
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.
|
|
208
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.
|
|
209
234
|
|
|
210
235
|
</edge_cases>
|
|
@@ -89,8 +89,10 @@ For multi-model verification details:
|
|
|
89
89
|
|
|
90
90
|
## Config
|
|
91
91
|
|
|
92
|
-
Settings in `.aperant/config.json`
|
|
93
|
-
|
|
94
|
-
- `verification.
|
|
95
|
-
- `
|
|
96
|
-
- `multi_model.
|
|
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
|
|
@@ -87,7 +87,10 @@ For the `--tick` mode, execute exactly the following sequence — once — insid
|
|
|
87
87
|
|
|
88
88
|
When `action ∈ {green, merged, closed, attempts-cap, wall-cap, identical-log}`:
|
|
89
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`.
|
|
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:
|
|
91
94
|
|
|
92
95
|
```
|
|
93
96
|
✅ CI green — merge in browser or run /apt:close-task post-merge
|
|
@@ -106,6 +106,23 @@ export function classify({ checks, state, prMetadata }) {
|
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
// 6b. BUG-031 defensive wait branch — if no checks are actually
|
|
110
|
+
// failing (everything is success / skipped / in_progress / neutral),
|
|
111
|
+
// never fall through to the default-red 'fix' at the bottom. Placed
|
|
112
|
+
// AFTER reviewer-pause so CHANGES_REQUESTED + no-failures still
|
|
113
|
+
// produces 'reviewer-pause' (locks existing semantics), BEFORE the
|
|
114
|
+
// remaining checks (identical-log, flake-skip, infra-backoff, fix)
|
|
115
|
+
// which all assume at least one failing row. Pre-fix, a PR with a
|
|
116
|
+
// success + a SKIPPED conditional CI job fell through to 'fix' and
|
|
117
|
+
// burned a fix attempt every tick on a non-existent failure.
|
|
118
|
+
if (failed.length === 0) {
|
|
119
|
+
return {
|
|
120
|
+
action: 'wait',
|
|
121
|
+
counts_toward_budget: false,
|
|
122
|
+
reason: 'no failing checks; awaiting in-flight or skipped',
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
109
126
|
// 7. Identical-log → non-converging fixer.
|
|
110
127
|
// Compare aggregate fingerprints (same format as the write side in ci-watch.mjs).
|
|
111
128
|
if (attempts > 0 && st.last_status && failed.length > 0) {
|
|
@@ -24,14 +24,36 @@
|
|
|
24
24
|
* itself never shells out to gh.
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
|
+
import { spawnSync } from 'node:child_process'
|
|
27
28
|
import { randomBytes } from 'node:crypto'
|
|
28
29
|
import { existsSync, readFileSync } from 'node:fs'
|
|
29
|
-
import { resolve } from 'node:path'
|
|
30
|
+
import { dirname, resolve } from 'node:path'
|
|
31
|
+
import { fileURLToPath } from 'node:url'
|
|
30
32
|
import { acquireLock, releaseLock } from '../ci-watch/lock.mjs'
|
|
31
33
|
import { deleteState, listWatches, readState, writeState } from '../ci-watch/state.mjs'
|
|
32
34
|
import { classify, isFailing } from '../ci-watch/stop-matrix.mjs'
|
|
33
35
|
import { parseFlags } from '../util/args.mjs'
|
|
34
36
|
import { err, exitWith, ok } from '../util/result.mjs'
|
|
37
|
+
import { requireMinVersion } from '../util/version-preflight.mjs'
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Installed kernel version — read once at module scope, same pattern as
|
|
41
|
+
* commands/check-version.mjs:108-109. Used by the version-preflight in
|
|
42
|
+
* `doTick` to fail loud (instead of infra-backoff-loop) when a future
|
|
43
|
+
* framework fix introduces a new tick flag and the user is on a stale
|
|
44
|
+
* install. See FRAMEWORK-BUG-017 rung 2 + the helper docstring for the
|
|
45
|
+
* "future-guard" framing.
|
|
46
|
+
*/
|
|
47
|
+
const INSTALLED_KERNEL_VERSION = (() => {
|
|
48
|
+
try {
|
|
49
|
+
const here = dirname(fileURLToPath(import.meta.url))
|
|
50
|
+
const pkgPath = resolve(here, '..', '..', '..', 'package.json')
|
|
51
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'))
|
|
52
|
+
return typeof pkg?.version === 'string' ? pkg.version : null
|
|
53
|
+
} catch {
|
|
54
|
+
return null
|
|
55
|
+
}
|
|
56
|
+
})()
|
|
35
57
|
|
|
36
58
|
const DEFAULT_TICK_INTERVAL_MS = 300_000 // 5 minutes
|
|
37
59
|
const MAX_TICK_INTERVAL_MS = 30 * 60 * 1000 // 30 min
|
|
@@ -73,7 +95,7 @@ function validatePr(pr) {
|
|
|
73
95
|
*/
|
|
74
96
|
export function cmdCiWatch(subcommand, projectDir, extraArgs) {
|
|
75
97
|
if (!subcommand) {
|
|
76
|
-
return err('Usage: apt-tools ci-watch <start|stop|status|tick> <project-dir> [flags]')
|
|
98
|
+
return err('Usage: apt-tools ci-watch <start|stop|status|tick|sweep> <project-dir> [flags]')
|
|
77
99
|
}
|
|
78
100
|
if (!projectDir) {
|
|
79
101
|
return err(`Usage: apt-tools ci-watch ${subcommand} <project-dir> [flags]`)
|
|
@@ -91,9 +113,34 @@ export function cmdCiWatch(subcommand, projectDir, extraArgs) {
|
|
|
91
113
|
return doStatus(root, flags)
|
|
92
114
|
case 'tick':
|
|
93
115
|
return doTick(root, flags)
|
|
116
|
+
case 'sweep':
|
|
117
|
+
return err(
|
|
118
|
+
'sweep is async; call cmdCiWatchSweep directly (the apt-tools dispatcher routes it separately)',
|
|
119
|
+
)
|
|
94
120
|
default:
|
|
95
|
-
return err(
|
|
121
|
+
return err(
|
|
122
|
+
`Unknown ci-watch subcommand: ${subcommand}. Expected: start, stop, status, tick, sweep`,
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Async sibling for the `sweep` subcommand (the rate-limited orphaned-state
|
|
129
|
+
* walker). Split out from the sync `cmdCiWatch` dispatcher so the latter's
|
|
130
|
+
* return type stays `CommandResult` (not a `CommandResult | Promise` union)
|
|
131
|
+
* and the synchronous tests + callers don't need to be wrapped in `await`.
|
|
132
|
+
*
|
|
133
|
+
* @param {string} projectDir
|
|
134
|
+
* @param {string[]} extraArgs
|
|
135
|
+
* @returns {Promise<CommandResult>}
|
|
136
|
+
*/
|
|
137
|
+
export async function cmdCiWatchSweep(projectDir, extraArgs) {
|
|
138
|
+
if (!projectDir) {
|
|
139
|
+
return err('Usage: apt-tools ci-watch sweep <project-dir> [flags]')
|
|
96
140
|
}
|
|
141
|
+
const root = resolve(projectDir)
|
|
142
|
+
const flags = parseFlags(extraArgs)
|
|
143
|
+
return await doSweep(root, flags)
|
|
97
144
|
}
|
|
98
145
|
|
|
99
146
|
// ---------------------------------------------------------------------------
|
|
@@ -119,12 +166,14 @@ function doStart(root, flags) {
|
|
|
119
166
|
command: 'ci-watch-start',
|
|
120
167
|
pr: Number(pr),
|
|
121
168
|
reason: 'already-armed',
|
|
169
|
+
scheduler_handle: existingState.scheduler_handle ?? null,
|
|
122
170
|
existing: {
|
|
123
171
|
task_id: existingState.task_id,
|
|
124
172
|
host: existingState.host,
|
|
125
173
|
attempts: existingState.attempts,
|
|
126
174
|
started_at: existingState.started_at,
|
|
127
175
|
deadline: existingState.deadline,
|
|
176
|
+
scheduler_handle: existingState.scheduler_handle ?? null,
|
|
128
177
|
},
|
|
129
178
|
},
|
|
130
179
|
2,
|
|
@@ -222,6 +271,91 @@ function doStop(root, flags) {
|
|
|
222
271
|
})
|
|
223
272
|
}
|
|
224
273
|
|
|
274
|
+
// ---------------------------------------------------------------------------
|
|
275
|
+
// sweep — BUG-027 (c): manual GC for orphaned ci-watch state files.
|
|
276
|
+
//
|
|
277
|
+
// Enumerates all watcher state files via `listWatches(root)`, shells out
|
|
278
|
+
// to `gh pr view --json state` for each one (rate-limited to ≤5/sec),
|
|
279
|
+
// and removes the state files whose PR state is MERGED or CLOSED. Never
|
|
280
|
+
// auto-runs on startup — opt-in manual subcommand only, per brief.
|
|
281
|
+
// Honors APT_GH_FIXTURE_JSON for hermetic tests (returns the same body
|
|
282
|
+
// for every PR — fine since the test fixtures we ship cover each
|
|
283
|
+
// branch in isolation).
|
|
284
|
+
// ---------------------------------------------------------------------------
|
|
285
|
+
|
|
286
|
+
async function doSweep(root, flags) {
|
|
287
|
+
if (flags.get('orphaned') !== true) {
|
|
288
|
+
return err(
|
|
289
|
+
'ci-watch sweep currently supports only `--orphaned`. Usage: apt-tools ci-watch sweep <project-dir> --orphaned',
|
|
290
|
+
)
|
|
291
|
+
}
|
|
292
|
+
const dryRun = flags.get('dry-run') === true
|
|
293
|
+
const watches = listWatches(root)
|
|
294
|
+
const removed = []
|
|
295
|
+
const skipped = []
|
|
296
|
+
const ghFixture = process.env.APT_GH_FIXTURE_JSON
|
|
297
|
+
for (let i = 0; i < watches.length; i++) {
|
|
298
|
+
const watch = watches[i]
|
|
299
|
+
const pr = watch.pr
|
|
300
|
+
const prUrl = watch.pr_url ?? null
|
|
301
|
+
// Validate pr_url shape before passing to gh: must be an https:// URL
|
|
302
|
+
// or absent. An invalid value (e.g. a flag-injection string from a
|
|
303
|
+
// tampered state file) falls back to the integer pr number.
|
|
304
|
+
const safeUrl =
|
|
305
|
+
typeof prUrl === 'string' && /^https:\/\/.+/.test(prUrl) && !prUrl.startsWith('-')
|
|
306
|
+
? prUrl
|
|
307
|
+
: null
|
|
308
|
+
// Need a PR URL to call `gh pr view`; if absent, fall back to the
|
|
309
|
+
// integer pr arg which gh accepts for the active repo.
|
|
310
|
+
const target = safeUrl ?? String(pr)
|
|
311
|
+
let body = null
|
|
312
|
+
if (ghFixture) {
|
|
313
|
+
body = ghFixture
|
|
314
|
+
} else {
|
|
315
|
+
try {
|
|
316
|
+
const res = spawnSync('gh', ['pr', 'view', target, '--json', 'state'], {
|
|
317
|
+
cwd: root,
|
|
318
|
+
encoding: 'utf-8',
|
|
319
|
+
})
|
|
320
|
+
if (res.status === 0 && res.stdout) body = res.stdout
|
|
321
|
+
} catch {
|
|
322
|
+
/* gh transport error — skip this entry */
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
if (!body) {
|
|
326
|
+
skipped.push({ pr, reason: 'gh-unavailable' })
|
|
327
|
+
continue
|
|
328
|
+
}
|
|
329
|
+
let parsed
|
|
330
|
+
try {
|
|
331
|
+
parsed = JSON.parse(body)
|
|
332
|
+
} catch {
|
|
333
|
+
skipped.push({ pr, reason: 'malformed-gh-json' })
|
|
334
|
+
continue
|
|
335
|
+
}
|
|
336
|
+
const state = parsed?.state ?? null
|
|
337
|
+
if (state !== 'MERGED' && state !== 'CLOSED') {
|
|
338
|
+
skipped.push({ pr, reason: `pr-state-${state ?? 'unknown'}` })
|
|
339
|
+
continue
|
|
340
|
+
}
|
|
341
|
+
if (!dryRun) deleteState(root, pr)
|
|
342
|
+
removed.push({ pr, pr_state: state })
|
|
343
|
+
// Rate-limit ≤5/sec per brief — sleep 200ms between live gh calls.
|
|
344
|
+
// Use async setTimeout (non-blocking) rather than Atomics.wait.
|
|
345
|
+
if (!ghFixture && i < watches.length - 1) {
|
|
346
|
+
await new Promise((resolve) => setTimeout(resolve, 200))
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return ok({
|
|
350
|
+
status: 'ok',
|
|
351
|
+
command: 'ci-watch-sweep',
|
|
352
|
+
dry_run: dryRun,
|
|
353
|
+
removed,
|
|
354
|
+
skipped,
|
|
355
|
+
total_watches: watches.length,
|
|
356
|
+
})
|
|
357
|
+
}
|
|
358
|
+
|
|
225
359
|
// ---------------------------------------------------------------------------
|
|
226
360
|
// status
|
|
227
361
|
// ---------------------------------------------------------------------------
|
|
@@ -261,6 +395,21 @@ function summarize(s) {
|
|
|
261
395
|
// ---------------------------------------------------------------------------
|
|
262
396
|
|
|
263
397
|
function doTick(root, flags) {
|
|
398
|
+
// FRAMEWORK-BUG-017 rung 2 (future-guard): emit a structured
|
|
399
|
+
// version_drift envelope when a future framework fix requires a
|
|
400
|
+
// new tick flag and the installed kernel predates it. The current
|
|
401
|
+
// pin to 0.6.7 (the version that introduced --payload-file) is a
|
|
402
|
+
// no-op on every 0.6.7+ install — but the scaffolding is in place
|
|
403
|
+
// for the NEXT new-flag fix to bump `introducedIn` without
|
|
404
|
+
// re-discovering the silent-infra-backoff failure mode.
|
|
405
|
+
const versionGate = requireMinVersion({
|
|
406
|
+
feature: 'payload-file',
|
|
407
|
+
introducedIn: '0.6.7',
|
|
408
|
+
installedVersion: INSTALLED_KERNEL_VERSION,
|
|
409
|
+
missingFlag: '--payload-file',
|
|
410
|
+
})
|
|
411
|
+
if (versionGate) return exitWith(versionGate, 1)
|
|
412
|
+
|
|
264
413
|
const prCheck = validatePr(flags.get('pr'))
|
|
265
414
|
if (typeof prCheck !== 'string') return prCheck
|
|
266
415
|
const pr = prCheck
|