pan-wizard 3.10.0 → 3.12.0
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/README.md +76 -8
- package/agents/pan-conductor.md +14 -1
- package/agents/pan-release.md +58 -0
- package/assets/pan-avatar.png +0 -0
- package/assets/pan-developer.png +0 -0
- package/assets/pan-docs-header.png +0 -0
- package/assets/pan-hero.png +0 -0
- package/assets/pan-logo-2000-transparent.svg +11 -30
- package/assets/pan-logo-2000.svg +12 -43
- package/assets/pan-logo-lockup.svg +11 -0
- package/assets/pan-mark.svg +7 -0
- package/assets/pan-orchestration.png +0 -0
- package/assets/pan-readme-hero.png +0 -0
- package/assets/terminal.svg +39 -119
- package/commands/pan/army.md +169 -0
- package/commands/pan/dashboard.md +25 -0
- package/commands/pan/focus-auto.md +32 -4
- package/commands/pan/hud.md +91 -0
- package/package.json +1 -1
- package/pan-wizard-core/bin/lib/campaign.cjs +198 -0
- package/pan-wizard-core/bin/lib/constants.cjs +8 -0
- package/pan-wizard-core/bin/lib/core.cjs +11 -0
- package/pan-wizard-core/bin/lib/focus.cjs +13 -1
- package/pan-wizard-core/bin/lib/hud.cjs +887 -0
- package/pan-wizard-core/bin/lib/squads.cjs +152 -0
- package/pan-wizard-core/bin/lib/worktree.cjs +123 -0
- package/pan-wizard-core/bin/pan-tools.cjs +68 -0
- package/pan-wizard-core/learnings/universal/autonomous-loop.md +56 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: army
|
|
3
|
+
group: Army
|
|
4
|
+
description: Bot-army campaign — Mission Control (Opus conductor) delegates a whole-project goal to squads (architecture / build / quality / release), each squad working branch-per-agent worktrees under a hard safety harness, gated by CI + a human merge, looping plan→delegate→execute→review→integrate→learn until the goal ships or a stop condition fires.
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- Read
|
|
7
|
+
- Write
|
|
8
|
+
- Edit
|
|
9
|
+
- Bash
|
|
10
|
+
- Grep
|
|
11
|
+
- Glob
|
|
12
|
+
- Agent
|
|
13
|
+
- Task
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# /pan:army — Bot-Army Campaign (mission control → squads → ship)
|
|
17
|
+
|
|
18
|
+
Run a whole-project delivery as a coordinated bot army (ADR-0032 squads · ADR-0033 campaign). **Mission Control** — the Opus `pan-conductor`, elevated to campaign scope — plans the mission, delegates to **squads** over the Agent toolset, and never writes code itself. Each squad owns its lifecycle role; the Build squad parallelizes by giving every builder its own `army/<task>` branch in an isolated git worktree. Nothing reaches a protected branch without green CI and a human's approval. $ARGUMENTS
|
|
19
|
+
|
|
20
|
+
The army is the campaign-scale sibling of `/pan:exec-phase --hierarchical` (one phase) and `/pan:focus-auto` (a category/backlog loop). It composes both: the conductor harness bounds it, the focus-auto loop drives it, the squads structure it.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Tiers (from `pan-tools squad list`)
|
|
25
|
+
|
|
26
|
+
| Tier | Who | Model | Access |
|
|
27
|
+
|------|-----|-------|--------|
|
|
28
|
+
| 0 · Mission Control | `pan-conductor` | Opus 4.8 | delegation-only (Agent toolset) — never codes |
|
|
29
|
+
| 1 · Architecture | roadmapper · planner · plan-checker · researchers | Sonnet (reasoning) | read-only |
|
|
30
|
+
| 1 · Build | `pan-executor` | Sonnet (reasoning) | read / write / bash — one branch+worktree per agent |
|
|
31
|
+
| 1 · Quality | reviewer · hardener · meta · verifier · integration · debugger | Sonnet/Haiku (mid) | read-only, adversarial |
|
|
32
|
+
| 1 · Release | `pan-release` | Sonnet (mid) | always-ask — human gate |
|
|
33
|
+
| 2 · Workers | document_code · distiller | Haiku (fast) | narrow, high-volume jobs |
|
|
34
|
+
|
|
35
|
+
Resolve the roster at runtime — never hardcode it: `pan-tools squad list` and `pan-tools squad show <name>`.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Concurrency model (LOAD-BEARING)
|
|
40
|
+
|
|
41
|
+
Read-only stages fan out; the mutating stage stays isolated, never shared:
|
|
42
|
+
|
|
43
|
+
| Stage | Concurrency |
|
|
44
|
+
|-------|-------------|
|
|
45
|
+
| Research (Architecture, Quality) | **Parallel** — read-only, mutate nothing |
|
|
46
|
+
| Build | **Parallel across agents, but each in its OWN worktree** — `pan-tools worktree create <task>`; two agents never share a tree or a file |
|
|
47
|
+
| Integrate / Release | **Serial, human-gated** — one merge at a time, `always-ask` |
|
|
48
|
+
|
|
49
|
+
If the project declares `concurrency.serial_build: true` in `.planning/config.json`, builds additionally run one-at-a-time even across worktrees (for build trees that corrupt under concurrency). Off by default.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Safety harness (inherited from pan-conductor — mandatory)
|
|
54
|
+
|
|
55
|
+
Every cap the conductor enforces applies to the campaign, scaled up:
|
|
56
|
+
|
|
57
|
+
| Cap | Mechanism |
|
|
58
|
+
|-----|-----------|
|
|
59
|
+
| Nesting depth 2 | Mission Control spawns squad agents; squad agents MUST NOT spawn further |
|
|
60
|
+
| Spawn / budget ceiling | per-cycle spawn cap + `--total-budget`; stop when the next spawn exceeds remaining budget |
|
|
61
|
+
| Abort kill-switch | `.planning/orchestration/abort` present → stop immediately, preserve state |
|
|
62
|
+
| Protected main | no direct push; merge is an `always-ask` human gate |
|
|
63
|
+
| Worktree isolation | branch-per-agent — parallel builders cannot collide |
|
|
64
|
+
| Rollback never rewrite | recovery = `git revert` / previous tag; never force-push |
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Arguments
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
/pan:army "<goal>" [--source scan|backlog] [--max-cycles N] [--total-budget N]
|
|
72
|
+
[--squads a,b,c] [--no-build-worktrees] [--push] [--clean-seal]
|
|
73
|
+
[--schedule <cadence>] [--daily-budget N]
|
|
74
|
+
[--dry-run] [--continue] [--stop] [--status]
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
| Flag | Default | Effect |
|
|
78
|
+
|------|---------|--------|
|
|
79
|
+
| `--source` | `backlog` | Work selection (delegates to focus-auto): `backlog` = ranked roadmap/requirements items; `scan` = category code-scan. |
|
|
80
|
+
| `--max-cycles` | 5 | Mission items landed before stopping. |
|
|
81
|
+
| `--total-budget` | 300 | Cumulative point ceiling. |
|
|
82
|
+
| `--squads` | all | Restrict to a subset, e.g. `--squads architecture,build,quality`. |
|
|
83
|
+
| `--no-build-worktrees` | off | Build in the main tree instead of branch-per-agent worktrees (small/serial projects). |
|
|
84
|
+
| `--push` | off | Push approved merges to origin (still human-gated). |
|
|
85
|
+
| `--clean-seal` | off | One clean build + full verification after the last item (commands from config). |
|
|
86
|
+
| `--schedule` | off | Arm a self-resuming campaign at this cadence (`hourly`/`daily`/`weekly`/`Nh`/`Nd`) instead of running once — writes the schedule descriptor (ADR-0034). Pair with `--daily-budget`. |
|
|
87
|
+
| `--daily-budget` | 300 | Per-day point ceiling for a scheduled campaign; the day's run stops when reached, resumes next day. |
|
|
88
|
+
| `--dry-run` | off | Plan + squad delegation preview only; STOP. |
|
|
89
|
+
| `--continue` / `--stop` / `--status` | — | Resume / halt / report from `.planning/orchestration/` + focus-auto state. |
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Pipeline — the six-phase loop
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
/pan:army
|
|
97
|
+
Phase 0 MUSTER — squad list + roster validate · cache prime · baseline · loop-state · abort-file clear
|
|
98
|
+
Phase 1 PLAN — Mission Control (Opus, extended thinking) decomposes the goal into dependency-ordered missions
|
|
99
|
+
Phase 2 DELEGATE — pick the next item (focus-auto --source) · route to the owning squad over the Agent toolset
|
|
100
|
+
Phase 3 EXECUTE — Build squad: one army/<task> worktree per agent (parallel); Architecture/Quality research in parallel (read-only)
|
|
101
|
+
Phase 4 REVIEW — Quality squad on the built tree: reviewer + hardener + meta → verdict ladder; a block is a hard gate
|
|
102
|
+
Phase 5 INTEGRATE— Release squad: prepare squash-merge → CI/verification → ALWAYS-ASK human approval → tag → deploy hand-off
|
|
103
|
+
Phase 6 LEARN — summaries return to Mission Control; retro/learn writes patterns to memory (this is "Dreaming")
|
|
104
|
+
→ loop to Phase 2 until a stop condition; --clean-seal once at the end
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Phase 0 — Muster (once)
|
|
108
|
+
1. **Onboarding gate (existing projects).** Run `pan-tools init new-project` to detect state. If `is_brownfield` (existing code) and `needs_codebase_map` (no `.planning/codebase/`), the army cannot plan blind — STOP and route through onboarding first: `/pan:map-codebase` (Architecture squad's `pan-document_code` maps the existing system into `.planning/codebase/`), then `/pan:new-project` to build `roadmap.md` + `requirements.md` *against the existing system*. Re-run `/pan:army` once a backlog exists. If a codebase map + roadmap already exist, continue.
|
|
109
|
+
2. `pan-tools squad list` and validate the roster is healthy.
|
|
110
|
+
3. Prime the cache; capture baseline (`git status` clean of project source; tests green or STOP). On a brownfield repo, the baseline is the current `main` — every `army/<task>` branch forks from it, so the existing code is never edited in place.
|
|
111
|
+
4. Ensure `.planning/orchestration/` exists; clear any stale `abort` file; init loop-state.
|
|
112
|
+
5. `--dry-run` → print the plan + per-squad delegation and STOP.
|
|
113
|
+
|
|
114
|
+
### Phase 1 — Plan (Mission Control)
|
|
115
|
+
Spawn the conductor in campaign mode (it plans, it does not code). It decomposes the goal into ordered, dependency-aware missions and assigns each to a squad.
|
|
116
|
+
|
|
117
|
+
### Phase 2–3 — Delegate + Execute
|
|
118
|
+
- Select the next item via `focus-auto --source {source}`.
|
|
119
|
+
- Architecture squad (read-only, parallel) produces the contract for a design-heavy item.
|
|
120
|
+
- Build squad: for each independent task, `pan-tools worktree create "<task>"` → spawn one `pan-executor` per worktree (parallel, isolated). Honor the spawn/budget caps before every spawn.
|
|
121
|
+
|
|
122
|
+
### Phase 4 — Review (Quality)
|
|
123
|
+
Spawn the Quality squad on the built tree (parallel, read-only). Merge findings into one verdict (`/pan:review-deep` if available). `block` / `review_required` → fix serially, then re-review. Never integrate red.
|
|
124
|
+
|
|
125
|
+
### Phase 5 — Integrate (Release, human-gated)
|
|
126
|
+
Spawn `pan-release`. It prepares the squash-merge, runs the configured `verification`, and surfaces an **always-ask** approval request. A human approves the merge to the protected branch; release then tags and records the rollback target. `--push` pushes the approved result.
|
|
127
|
+
|
|
128
|
+
### Phase 6 — Learn (Dreaming)
|
|
129
|
+
Squad summaries return to Mission Control. Run `/pan:retro --write-memory` (and `/pan:learn` if traces exist) so recurring patterns persist into agent memory for the next mission. Strike the landed item; update loop-state. For a scheduled campaign, also `pan-tools campaign record-run --items <n> --points <p>` so the next-due time and the day's spend advance.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Scheduled, self-resuming campaigns (ADR-0034)
|
|
134
|
+
|
|
135
|
+
PAN is not a daemon — it cannot wake itself while the session is closed. `--schedule` arms a campaign and lets an external trigger drive it; the human merge gate is never relaxed.
|
|
136
|
+
|
|
137
|
+
- **Arm:** `/pan:army "<goal>" --schedule daily --daily-budget 200` writes `.planning/orchestration/schedule.json` (cadence, daily budget, next-due) instead of running once.
|
|
138
|
+
- **The trigger (you wire one):** a host scheduler (Claude Code routines / cron / scheduled-tasks) or a `/loop` runs `pan-tools campaign due` and, when it reports due, invokes `/pan:army --continue`. On next session open, a due campaign is surfaced as a nudge.
|
|
139
|
+
- **Resume (`--continue`):** read the schedule + `.planning/orchestration/` + focus-auto state. If `campaign due` is true and the day's `--daily-budget` isn't spent, run the next mission(s), then `campaign record-run` (advances next-due, accrues the day's spend). If not due or budget-spent, report next-due and STOP.
|
|
140
|
+
- **Bounded spend:** the per-day budget caps each day's run; the per-run `--total-budget` and the conductor caps still bound each cycle. A scheduled campaign runs the backlog down to staged, reviewed, green PRs over days — and still waits for a human at every merge.
|
|
141
|
+
|
|
142
|
+
Manage it: `pan-tools campaign status` (active/paused, spent today, next-due), `campaign schedule --pause` / `--resume` / `--disable`.
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Completion contract
|
|
147
|
+
The campaign is complete when ANY holds: `--max-cycles` reached · `--total-budget` exhausted · backlog empty · abort file present · context < 25% · a mission cannot pass Quality and can't be cleanly reverted (HARD STOP — preserve state, report). Always run `--clean-seal` (unless omitted) after the last item.
|
|
148
|
+
|
|
149
|
+
## NEVER DO
|
|
150
|
+
- Let Mission Control write code, or let a squad agent spawn further agents (depth cap).
|
|
151
|
+
- Run two builders in the same worktree, or merge to a protected branch without the human gate.
|
|
152
|
+
- Force-push or rewrite history; recovery is revert / previous tag only.
|
|
153
|
+
- Hardcode the squad roster — read it from `pan-tools squad list`.
|
|
154
|
+
- Integrate a mission that hasn't passed Quality green.
|
|
155
|
+
|
|
156
|
+
## ALWAYS DO
|
|
157
|
+
- Plan on Opus, delegate over the Agent toolset, keep each squad's return a tight summary.
|
|
158
|
+
- One worktree per Build agent; parallel research/verify; serial human-gated integrate.
|
|
159
|
+
- Check the abort file + spawn/budget caps before every spawn.
|
|
160
|
+
- Finish with the clean-build seal; write learnings back to memory.
|
|
161
|
+
|
|
162
|
+
## Examples
|
|
163
|
+
```
|
|
164
|
+
/pan:army "ship the v1 reporting module" --source backlog --max-cycles 5
|
|
165
|
+
/pan:army "harden auth across the app" --squads architecture,build,quality --clean-seal
|
|
166
|
+
/pan:army "<goal>" --dry-run # show the plan + squad delegation, no code
|
|
167
|
+
/pan:army --status # campaign progress
|
|
168
|
+
/pan:army --stop # graceful halt, state preserved
|
|
169
|
+
```
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pan:dashboard
|
|
3
|
+
group: Observability
|
|
4
|
+
description: Alias for /pan:hud — generate the single-page HTML dashboard of the bot army and project
|
|
5
|
+
argument-hint: "[--out <file>] [--open] [--stdout]"
|
|
6
|
+
allowed-tools:
|
|
7
|
+
- Read
|
|
8
|
+
- Bash
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
<objective>
|
|
12
|
+
`/pan:dashboard` is a discoverability alias for **`/pan:hud`** (ADR-0035). It generates the same single-page, self-contained HTML dashboard of the project and its bot army.
|
|
13
|
+
|
|
14
|
+
Run it exactly like `/pan:hud`:
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
pan-tools hud [--out <file>] [--open] [--stdout]
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
See **`/pan:hud`** for the full panel list, flags, JSON result shape, and runtime compatibility. The two commands are interchangeable.
|
|
21
|
+
</objective>
|
|
22
|
+
|
|
23
|
+
<execution_context>
|
|
24
|
+
@~/.claude/pan-wizard-core/bin/lib/hud.cjs
|
|
25
|
+
</execution_context>
|
|
@@ -87,14 +87,16 @@ Wait for the user's reply before proceeding. Do not guess or pick a default cate
|
|
|
87
87
|
## Arguments
|
|
88
88
|
|
|
89
89
|
```
|
|
90
|
-
/pan:focus-auto [--category CAT] [--mode MODE] [--budget N]
|
|
91
|
-
[--total-budget N] [--continue] [--stop] [--status]
|
|
92
|
-
[--deep-review]
|
|
90
|
+
/pan:focus-auto [--source scan|backlog] [--category CAT] [--mode MODE] [--budget N]
|
|
91
|
+
[--max-cycles N] [--total-budget N] [--continue] [--stop] [--status]
|
|
92
|
+
[--dry-run] [--deep-review]
|
|
93
|
+
[--parallel-research] [--parallel-verify] [--clean-seal]
|
|
93
94
|
```
|
|
94
95
|
|
|
95
96
|
| Flag | Default | Description |
|
|
96
97
|
|------|---------|-------------|
|
|
97
|
-
| `--
|
|
98
|
+
| `--source` | `scan` | Work selection. `scan` = category-scoped code scan (below). `backlog` = rank actionable items from `roadmap.md` / `requirements.md` (ADR-0031). |
|
|
99
|
+
| `--category` | null (all) | cleanup, tests, stability, features, docs, optimize, prompts, security, distill. Applies to `--source scan`. |
|
|
98
100
|
| `--mode` | category-dependent | bugfix, balanced, features, full |
|
|
99
101
|
| `--budget` | category-dependent | Points per cycle (5-100) |
|
|
100
102
|
| `--max-cycles` | 10 | Maximum iterations (1-50) |
|
|
@@ -104,6 +106,9 @@ Wait for the user's reply before proceeding. Do not guess or pick a default cate
|
|
|
104
106
|
| `--status` | — | Show current campaign progress |
|
|
105
107
|
| `--dry-run` | — | Show plan without executing |
|
|
106
108
|
| `--deep-review` | off | After every exec cycle, run inline OWASP security check on changed files. Verdict `block` or `review_required` stops the campaign (6th safety harness). Works with all categories. |
|
|
109
|
+
| `--parallel-research` | off | Fan out the per-item *research* stage via the Workflow tool (read-only agents). No-op fallback to sequential where the host has no Workflow tool. (ADR-0031) |
|
|
110
|
+
| `--parallel-verify` | off | Fan out the per-item *verify* stage via the Workflow tool (read-only). The implement/exec stage always stays a single agent. (ADR-0031) |
|
|
111
|
+
| `--clean-seal` | off | After the loop's last item, run one clean build + full verification (commands from `config.json → build`/`verification`) to catch cross-item orphans. (ADR-0031) |
|
|
107
112
|
|
|
108
113
|
## Category Defaults
|
|
109
114
|
|
|
@@ -118,6 +123,29 @@ Wait for the user's reply before proceeding. Do not guess or pick a default cate
|
|
|
118
123
|
| prompts | P0-P6 | balanced | 100 |
|
|
119
124
|
| security | P0-P2 | bugfix | 40 |
|
|
120
125
|
|
|
126
|
+
## Backlog source (`--source backlog`, ADR-0031)
|
|
127
|
+
|
|
128
|
+
When `--source backlog` is set, work is selected from the **curated planning surface** instead of a code scan — for campaigns that work a human-prioritized roadmap rather than whatever grep finds.
|
|
129
|
+
|
|
130
|
+
1. **Read the backlog once** at Phase 0: actionable items are unchecked rows in `roadmap.md` (phase/plan checkboxes) and unmet `requirements.md` REQ rows. Skip anything struck, completed, or marked blocked.
|
|
131
|
+
2. **Score from the CURRENT document — never a hardcoded ID list.** For each item, derive `RS = (UserValue + TimeCriticality + RiskReduction) / Effort` (1–5 each; Effort from the row's size tag). Sort by wave/priority ascending → RS descending → effort ascending. This re-derives the order from whatever the roadmap says today, so it never goes stale.
|
|
132
|
+
3. **Pop the top survivor each cycle**; re-rank only if a landing changed a dependency. The same budget/cycle/context stops and safety harness apply unchanged.
|
|
133
|
+
4. The backlog ranker reads only PAN's planning files — it embeds **no** project-specific item IDs, test counts, or build commands. A project with no actionable backlog items is a clean stop (`scan returns zero items` equivalent).
|
|
134
|
+
|
|
135
|
+
## Concurrency model (when `--parallel-research` / `--parallel-verify`, ADR-0031)
|
|
136
|
+
|
|
137
|
+
The proven shape is **parallel read-only research → exactly ONE serial implement/exec → parallel read-only verify**:
|
|
138
|
+
|
|
139
|
+
| Stage | Concurrency | Why |
|
|
140
|
+
|-------|-------------|-----|
|
|
141
|
+
| Research | Parallel (Workflow fan-out, read-only) when `--parallel-research` | Reads source/specs; mutates nothing. |
|
|
142
|
+
| Implement / exec | **Single agent, always** | Mutates the tree; never fanned out. |
|
|
143
|
+
| Verify | Parallel (Workflow fan-out, read-only) when `--parallel-verify` | Runs against the already-built tree; mutates nothing. |
|
|
144
|
+
|
|
145
|
+
**Serial-build constraint:** if `.planning/config.json → concurrency.serial_build` is `true`, the runner additionally guarantees at most one build process at any instant across the whole loop (for projects whose build trees corrupt under concurrency). This is **off by default** — most projects build in parallel safely. PAN does not assume it.
|
|
146
|
+
|
|
147
|
+
**Commit-quality gates** (advisory, always on): a *staging-miss guard* (no exec-touched file left unstaged) and an *orphan audit* (HEAD must not reference a symbol defined only in an uncommitted file). With `--clean-seal`, a single clean build + full verification runs after the last item to catch cross-item orphans the per-cycle incremental commits hid.
|
|
148
|
+
|
|
121
149
|
## Pipeline
|
|
122
150
|
|
|
123
151
|
### Phase 0: Initialization
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pan:hud
|
|
3
|
+
group: Observability
|
|
4
|
+
description: Generate a single-page, self-contained HTML dashboard of the bot army and the project's current state
|
|
5
|
+
argument-hint: "[--out <file>] [--open] [--stdout]"
|
|
6
|
+
allowed-tools:
|
|
7
|
+
- Read
|
|
8
|
+
- Bash
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
<objective>
|
|
12
|
+
Render one **self-contained HTML page** — no server, no network, no external CSS or JS — that shows the whole picture of a PAN project and its bot army in their current state.
|
|
13
|
+
|
|
14
|
+
It is a *view*, not a new source of truth: every panel aggregates state PAN already tracks (`state.md`, the roadmap/phases on disk, the squad registry, the campaign schedule, army worktrees, the cost ledger, `requirements.md`, verification artifacts, and git history). The command writes only the rendered file, so it can never corrupt planning data.
|
|
15
|
+
|
|
16
|
+
Default output is `.planning/hud.html`. Open it in any browser. Re-run any time for a fresh snapshot.
|
|
17
|
+
</objective>
|
|
18
|
+
|
|
19
|
+
<execution_context>
|
|
20
|
+
@~/.claude/pan-wizard-core/bin/lib/hud.cjs
|
|
21
|
+
</execution_context>
|
|
22
|
+
|
|
23
|
+
<usage>
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
pan-tools hud [--out <file>] [--open] [--stdout]
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**Flags:**
|
|
30
|
+
- `--out <file>` — write to a custom path instead of `.planning/hud.html` (relative paths resolve against the project root).
|
|
31
|
+
- `--open` — best-effort: launch the file in the default browser after writing (cross-platform; silently no-ops if no opener is available).
|
|
32
|
+
- `--stdout` — print the HTML to stdout instead of writing a file (for piping into another tool or a web response).
|
|
33
|
+
|
|
34
|
+
**JSON result shape** (default, when not `--stdout`):
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"path": ".planning/hud.html",
|
|
38
|
+
"bytes": 18234,
|
|
39
|
+
"army_active": true,
|
|
40
|
+
"sections": ["mission", "command-stack", "campaign", "safety-harness", "worktrees", "roadmap", "telemetry", "requirements-quality", "activity"],
|
|
41
|
+
"opened": false
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
</usage>
|
|
46
|
+
|
|
47
|
+
<panels>
|
|
48
|
+
|
|
49
|
+
The dashboard is composed of up to ten panels. Panels render only when they have data — a plain (non-army) project still gets a complete, useful page.
|
|
50
|
+
|
|
51
|
+
| Panel | What it shows | Source |
|
|
52
|
+
|-------|---------------|--------|
|
|
53
|
+
| **Mission banner** | Project, core value, status, version/milestone + metric cards (progress, phase, requirements, spend) | `package.json`, `project.md`, `state.md`, phase scan, cost ledger |
|
|
54
|
+
| **Command stack** *(army)* | Mission Control over the four squads with per-squad agent drill-down (active / idle, calls, tokens) | squad registry + cost ledger |
|
|
55
|
+
| **Campaign** *(army)* | Cadence, next-due, daily-budget bar, last run, run history | `schedule.json` |
|
|
56
|
+
| **Safety harness** *(army)* | Merge gate, abort switch (pause), active worktrees, daily budget, concurrency | config + pause file + worktrees + schedule |
|
|
57
|
+
| **Worktrees** *(army)* | Active `army/*` branches and their paths | `git worktree list` |
|
|
58
|
+
| **Roadmap** | Every phase with status + completion | phases on disk |
|
|
59
|
+
| **Telemetry** | Total spend, tokens, cache-hit rate, by-squad breakdown | cost ledger |
|
|
60
|
+
| **Requirements & quality** | Requirements done/open + last verification artifacts | `requirements.md`, phase `*-verification.md` / `*-uat.md` |
|
|
61
|
+
| **Recent activity** | Last commits (the army's committed output) | `git log` |
|
|
62
|
+
|
|
63
|
+
*(army)* panels appear only when a campaign is scheduled or army worktrees exist — graceful degradation per ADR-0035.
|
|
64
|
+
|
|
65
|
+
</panels>
|
|
66
|
+
|
|
67
|
+
<workflow>
|
|
68
|
+
|
|
69
|
+
**Glance check:** run `/pan:hud --open` to see the project and army at a glance in your browser.
|
|
70
|
+
|
|
71
|
+
**Share status:** the file is fully self-contained — send `.planning/hud.html` to anyone; it opens with no dependencies.
|
|
72
|
+
|
|
73
|
+
**During a campaign:** re-run after each cycle to watch squads, budget, worktrees, and committed output evolve. Pair with `/pan:army` and `pan-tools campaign status`.
|
|
74
|
+
|
|
75
|
+
**Pipe it:** `pan-tools hud --stdout > dashboard.html` or feed the JSON result into another tool.
|
|
76
|
+
|
|
77
|
+
</workflow>
|
|
78
|
+
|
|
79
|
+
<runtime_compatibility>
|
|
80
|
+
|
|
81
|
+
| Runtime | Support |
|
|
82
|
+
|---------|---------|
|
|
83
|
+
| Claude Code | Full |
|
|
84
|
+
| OpenCode | Full |
|
|
85
|
+
| Gemini | Full |
|
|
86
|
+
| Codex | Full |
|
|
87
|
+
| Copilot CLI | Full |
|
|
88
|
+
|
|
89
|
+
The aggregator and renderer are pure, zero-dependency, runtime-agnostic CommonJS — the dashboard is identical across all five runtimes. `--open` depends on the host OS having a default browser opener.
|
|
90
|
+
|
|
91
|
+
</runtime_compatibility>
|
package/package.json
CHANGED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Campaign — scheduled, self-resuming bot-army campaigns (ADR-0034).
|
|
3
|
+
*
|
|
4
|
+
* PAN is not a daemon: this module owns the schedule DESCRIPTOR and the
|
|
5
|
+
* decision of whether a run is DUE. An external trigger (host scheduler,
|
|
6
|
+
* cron, /loop, or a human) polls `campaign due` and fires `/pan:army
|
|
7
|
+
* --continue`. The always-ask human merge gate is never affected by
|
|
8
|
+
* scheduling. Pure + synchronous; `now` is injected for testability.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
const { output, error } = require('./core.cjs');
|
|
16
|
+
const { PLANNING_DIR } = require('./constants.cjs');
|
|
17
|
+
|
|
18
|
+
const ORCH_DIR = 'orchestration';
|
|
19
|
+
const SCHEDULE_FILE = 'schedule.json';
|
|
20
|
+
const HISTORY_CAP = 50;
|
|
21
|
+
const DAY_MS = 86400000;
|
|
22
|
+
|
|
23
|
+
function schedulePath(cwd) {
|
|
24
|
+
return path.join(cwd, PLANNING_DIR, ORCH_DIR, SCHEDULE_FILE);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Parse a cadence string into milliseconds.
|
|
29
|
+
* @param {string} c - 'hourly' | 'daily' | 'weekly' | 'Nh' | 'Nd'
|
|
30
|
+
* @returns {number|null} ms, or null if unparseable
|
|
31
|
+
*/
|
|
32
|
+
function parseCadence(c) {
|
|
33
|
+
if (!c || typeof c !== 'string') return null;
|
|
34
|
+
const s = c.trim().toLowerCase();
|
|
35
|
+
if (s === 'hourly') return 3600000;
|
|
36
|
+
if (s === 'daily') return DAY_MS;
|
|
37
|
+
if (s === 'weekly') return 7 * DAY_MS;
|
|
38
|
+
const m = s.match(/^(\d+)\s*([hd])$/);
|
|
39
|
+
if (!m) return null;
|
|
40
|
+
const n = parseInt(m[1], 10);
|
|
41
|
+
if (n <= 0) return null;
|
|
42
|
+
return m[2] === 'h' ? n * 3600000 : n * DAY_MS;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** @returns {object|null} the schedule descriptor, or null if none/unreadable */
|
|
46
|
+
function readSchedule(cwd) {
|
|
47
|
+
try {
|
|
48
|
+
return JSON.parse(fs.readFileSync(schedulePath(cwd), 'utf8'));
|
|
49
|
+
} catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function writeScheduleFile(cwd, schedule) {
|
|
55
|
+
const p = schedulePath(cwd);
|
|
56
|
+
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
57
|
+
fs.writeFileSync(p, JSON.stringify(schedule, null, 2) + '\n', 'utf8');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Arm or update a campaign schedule.
|
|
62
|
+
* @param {string} cwd
|
|
63
|
+
* @param {object} opts - { goal, source, cadence, daily_budget, enabled, paused }
|
|
64
|
+
* @param {Date} now
|
|
65
|
+
* @returns {object|{error}} the written descriptor
|
|
66
|
+
*/
|
|
67
|
+
function writeSchedule(cwd, opts, now) {
|
|
68
|
+
const cadence = opts.cadence || 'daily';
|
|
69
|
+
if (parseCadence(cadence) === null) {
|
|
70
|
+
return { error: `Invalid cadence "${cadence}". Use hourly | daily | weekly | Nh | Nd.` };
|
|
71
|
+
}
|
|
72
|
+
const at = now || new Date();
|
|
73
|
+
const existing = readSchedule(cwd) || {};
|
|
74
|
+
const schedule = {
|
|
75
|
+
goal: opts.goal ?? existing.goal ?? null,
|
|
76
|
+
source: opts.source ?? existing.source ?? 'backlog',
|
|
77
|
+
cadence,
|
|
78
|
+
daily_budget: opts.daily_budget != null ? Number(opts.daily_budget) : (existing.daily_budget ?? 300),
|
|
79
|
+
enabled: opts.enabled != null ? Boolean(opts.enabled) : (existing.enabled ?? true),
|
|
80
|
+
paused: opts.paused != null ? Boolean(opts.paused) : (existing.paused ?? false),
|
|
81
|
+
next_due: existing.next_due ?? at.toISOString(),
|
|
82
|
+
last_run: existing.last_run ?? null,
|
|
83
|
+
history: Array.isArray(existing.history) ? existing.history : [],
|
|
84
|
+
};
|
|
85
|
+
writeScheduleFile(cwd, schedule);
|
|
86
|
+
return schedule;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function sameUtcDay(a, b) {
|
|
90
|
+
return a.getUTCFullYear() === b.getUTCFullYear()
|
|
91
|
+
&& a.getUTCMonth() === b.getUTCMonth()
|
|
92
|
+
&& a.getUTCDate() === b.getUTCDate();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function spentToday(schedule, now) {
|
|
96
|
+
return (schedule.history || [])
|
|
97
|
+
.filter(h => { const t = new Date(h.ts); return !isNaN(t) && sameUtcDay(t, now); })
|
|
98
|
+
.reduce((sum, h) => sum + (Number(h.points_used) || 0), 0);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Is a scheduled run due right now?
|
|
103
|
+
* @returns {{due: boolean, reason: string, next_due: string|null, spent_today: number}}
|
|
104
|
+
*/
|
|
105
|
+
function isRunDue(schedule, now) {
|
|
106
|
+
const at = now || new Date();
|
|
107
|
+
if (!schedule) return { due: false, reason: 'no_schedule', next_due: null, spent_today: 0 };
|
|
108
|
+
const spent = spentToday(schedule, at);
|
|
109
|
+
if (!schedule.enabled) return { due: false, reason: 'disabled', next_due: schedule.next_due, spent_today: spent };
|
|
110
|
+
if (schedule.paused) return { due: false, reason: 'paused', next_due: schedule.next_due, spent_today: spent };
|
|
111
|
+
if (schedule.daily_budget != null && spent >= schedule.daily_budget) {
|
|
112
|
+
return { due: false, reason: 'budget_exhausted_today', next_due: schedule.next_due, spent_today: spent };
|
|
113
|
+
}
|
|
114
|
+
const due = new Date(schedule.next_due);
|
|
115
|
+
if (isNaN(due) || at.getTime() >= due.getTime()) {
|
|
116
|
+
return { due: true, reason: 'due', next_due: schedule.next_due, spent_today: spent };
|
|
117
|
+
}
|
|
118
|
+
return { due: false, reason: 'not_yet', next_due: schedule.next_due, spent_today: spent };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Record a completed campaign run and advance next_due.
|
|
123
|
+
* @returns {object|{error}} updated descriptor
|
|
124
|
+
*/
|
|
125
|
+
function recordRun(cwd, run, now) {
|
|
126
|
+
const schedule = readSchedule(cwd);
|
|
127
|
+
if (!schedule) return { error: 'No campaign schedule to record against' };
|
|
128
|
+
const at = now || new Date();
|
|
129
|
+
const ts = run?.ts || at.toISOString();
|
|
130
|
+
schedule.history = (schedule.history || []).concat([{
|
|
131
|
+
ts,
|
|
132
|
+
items_landed: Number(run?.items_landed) || 0,
|
|
133
|
+
points_used: Number(run?.points_used) || 0,
|
|
134
|
+
}]).slice(-HISTORY_CAP);
|
|
135
|
+
schedule.last_run = ts;
|
|
136
|
+
const step = parseCadence(schedule.cadence) || DAY_MS;
|
|
137
|
+
schedule.next_due = new Date(at.getTime() + step).toISOString();
|
|
138
|
+
writeScheduleFile(cwd, schedule);
|
|
139
|
+
return schedule;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Is the retro/learn ("dream") step due? Default: once per calendar day that
|
|
144
|
+
* had activity — curate memory between missions, not after every cycle.
|
|
145
|
+
*/
|
|
146
|
+
function isDreamDue(schedule, now) {
|
|
147
|
+
if (!schedule || !schedule.enabled || schedule.paused) return false;
|
|
148
|
+
const at = now || new Date();
|
|
149
|
+
if (!schedule.last_run) return false;
|
|
150
|
+
const last = new Date(schedule.last_run);
|
|
151
|
+
if (isNaN(last)) return false;
|
|
152
|
+
return !sameUtcDay(last, at) || (schedule.history || []).length > 0;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// ─── CLI ─────────────────────────────────────────────────────────────────────
|
|
156
|
+
|
|
157
|
+
function cmdCampaignSchedule(cwd, opts, raw) {
|
|
158
|
+
const r = writeSchedule(cwd, opts, opts.now);
|
|
159
|
+
if (r.error) return error(r.error);
|
|
160
|
+
const human = `Campaign scheduled: ${r.cadence} · budget ${r.daily_budget}/day · next ${r.next_due}${r.goal ? ` · goal: ${r.goal}` : ''}`;
|
|
161
|
+
output(r, raw, human);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function cmdCampaignStatus(cwd, raw) {
|
|
165
|
+
const schedule = readSchedule(cwd);
|
|
166
|
+
if (!schedule) return output({ scheduled: false }, raw, 'No campaign scheduled');
|
|
167
|
+
const at = new Date();
|
|
168
|
+
const d = isRunDue(schedule, at);
|
|
169
|
+
const result = {
|
|
170
|
+
scheduled: true, enabled: schedule.enabled, paused: schedule.paused,
|
|
171
|
+
cadence: schedule.cadence, daily_budget: schedule.daily_budget,
|
|
172
|
+
next_due: schedule.next_due, last_run: schedule.last_run,
|
|
173
|
+
spent_today: d.spent_today, runs: (schedule.history || []).length,
|
|
174
|
+
due: d.due, reason: d.reason,
|
|
175
|
+
};
|
|
176
|
+
const human = `Campaign ${schedule.enabled ? (schedule.paused ? 'paused' : 'active') : 'disabled'} · ${schedule.cadence} · spent ${d.spent_today}/${schedule.daily_budget} today · next ${schedule.next_due} · ${d.due ? 'DUE NOW' : d.reason}`;
|
|
177
|
+
output(result, raw, human);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function cmdCampaignDue(cwd, raw) {
|
|
181
|
+
const schedule = readSchedule(cwd);
|
|
182
|
+
const d = isRunDue(schedule, new Date());
|
|
183
|
+
// exit-coded so a host scheduler can gate: 0 = due, 1 = not due
|
|
184
|
+
output({ due: d.due, reason: d.reason, next_due: d.next_due }, raw, d.due ? 'due' : `not due (${d.reason})`);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
module.exports = {
|
|
188
|
+
parseCadence,
|
|
189
|
+
readSchedule,
|
|
190
|
+
writeSchedule,
|
|
191
|
+
isRunDue,
|
|
192
|
+
recordRun,
|
|
193
|
+
isDreamDue,
|
|
194
|
+
cmdCampaignSchedule,
|
|
195
|
+
cmdCampaignStatus,
|
|
196
|
+
cmdCampaignDue,
|
|
197
|
+
SCHEDULE_FILE,
|
|
198
|
+
};
|
|
@@ -126,6 +126,13 @@ const AUTO_RUN_FILE = 'auto-run.json';
|
|
|
126
126
|
/** Focus auto-runner categories */
|
|
127
127
|
const FOCUS_CATEGORIES = ['cleanup', 'tests', 'stability', 'features', 'docs', 'optimize', 'prompts', 'security', 'distill'];
|
|
128
128
|
|
|
129
|
+
/**
|
|
130
|
+
* Focus auto-runner work sources (ADR-0031):
|
|
131
|
+
* - scan: category-scoped code scan (default; today's behavior)
|
|
132
|
+
* - backlog: rank actionable items from roadmap.md / requirements.md
|
|
133
|
+
*/
|
|
134
|
+
const FOCUS_SOURCES = ['scan', 'backlog'];
|
|
135
|
+
|
|
129
136
|
/** Category → priority index range (indices into PRIORITY_LEVELS) */
|
|
130
137
|
const CATEGORY_PRIORITY_RANGE = {
|
|
131
138
|
cleanup: { min: 3, max: 5 }, // P3-P5
|
|
@@ -681,6 +688,7 @@ module.exports = {
|
|
|
681
688
|
FOCUS_DIR,
|
|
682
689
|
AUTO_RUN_FILE,
|
|
683
690
|
FOCUS_CATEGORIES,
|
|
691
|
+
FOCUS_SOURCES,
|
|
684
692
|
DOC_SYNC_FILES,
|
|
685
693
|
COMMAND_RENAME_MAP,
|
|
686
694
|
CATEGORY_PRIORITY_RANGE,
|
|
@@ -73,6 +73,8 @@ const MODEL_PROFILES = {
|
|
|
73
73
|
'pan-distiller': { quality: 'reasoning', balanced: 'fast', budget: 'fast' },
|
|
74
74
|
// v3.7.0 self-improvement loop — observation-only watchdog
|
|
75
75
|
'pan-experiment-runner': { quality: 'reasoning', balanced: 'fast', budget: 'fast' },
|
|
76
|
+
// ADR-0033 bot-army — Release squad
|
|
77
|
+
'pan-release': { quality: 'reasoning', balanced: 'mid', budget: 'fast' },
|
|
76
78
|
};
|
|
77
79
|
|
|
78
80
|
// ─── Effort Profiles (2026-06, adaptive-thinking era) ───────────────────────
|
|
@@ -104,6 +106,7 @@ const AGENT_BASE_EFFORT = {
|
|
|
104
106
|
'pan-previewer': 'high',
|
|
105
107
|
'pan-experiment-runner': 'high',
|
|
106
108
|
'pan-optimizer': 'high',
|
|
109
|
+
'pan-release': 'high',
|
|
107
110
|
// Research/synthesis/review — moderate depth
|
|
108
111
|
'pan-phase-researcher': 'medium',
|
|
109
112
|
'pan-project-researcher': 'medium',
|
|
@@ -279,6 +282,11 @@ function loadConfig(cwd) {
|
|
|
279
282
|
model_overrides: parsed.model_overrides || {},
|
|
280
283
|
effort_overrides: parsed.effort_overrides || {},
|
|
281
284
|
routing: parsed.routing || { strategy: 'static', provider: 'auto' },
|
|
285
|
+
// ADR-0031: project build/verification commands. null = not configured
|
|
286
|
+
// (focus-auto --clean-seal then asks or skips rather than guessing).
|
|
287
|
+
build: parsed.build || null,
|
|
288
|
+
verification: parsed.verification || null,
|
|
289
|
+
concurrency: parsed.concurrency || { serial_build: false },
|
|
282
290
|
};
|
|
283
291
|
} catch { // Config missing or malformed — use defaults
|
|
284
292
|
return {
|
|
@@ -290,6 +298,9 @@ function loadConfig(cwd) {
|
|
|
290
298
|
model_overrides: {},
|
|
291
299
|
effort_overrides: {},
|
|
292
300
|
routing: { strategy: 'static', provider: 'auto' },
|
|
301
|
+
build: null,
|
|
302
|
+
verification: null,
|
|
303
|
+
concurrency: { serial_build: false },
|
|
293
304
|
};
|
|
294
305
|
}
|
|
295
306
|
}
|
|
@@ -14,7 +14,7 @@ const {
|
|
|
14
14
|
FOCUS_MODES, FOCUS_TIERS, FOCUS_DIR,
|
|
15
15
|
BUDGET_LIMIT_BUGFIX, BUDGET_LIMIT_FULL, STABILITY_RATIO, FEATURE_RATIO,
|
|
16
16
|
DIMINISHING_RETURNS_THRESHOLD,
|
|
17
|
-
AUTO_RUN_FILE, FOCUS_CATEGORIES, CATEGORY_PRIORITY_RANGE, CATEGORY_DEFAULTS,
|
|
17
|
+
AUTO_RUN_FILE, FOCUS_CATEGORIES, FOCUS_SOURCES, CATEGORY_PRIORITY_RANGE, CATEGORY_DEFAULTS,
|
|
18
18
|
DEFAULT_MAX_CYCLES, DEFAULT_TOTAL_BUDGET,
|
|
19
19
|
BUDGET_MIN, BUDGET_MAX, MAX_CYCLES_MIN, MAX_CYCLES_MAX, TOTAL_BUDGET_MIN, TOTAL_BUDGET_MAX,
|
|
20
20
|
AUTORUN_STATUSES, DOC_SYNC_FILES, COMMAND_RENAME_MAP,
|
|
@@ -829,6 +829,14 @@ function focusAutoInit(cwd, raw, getVal, hasFlag) {
|
|
|
829
829
|
return error(`Category must be one of: ${FOCUS_CATEGORIES.join(', ')}`);
|
|
830
830
|
}
|
|
831
831
|
|
|
832
|
+
// ADR-0031: work source — 'scan' (category code-scan, default) or 'backlog'
|
|
833
|
+
// (rank actionable roadmap.md / requirements.md items). Category applies to
|
|
834
|
+
// scan mode; backlog mode ranks the whole actionable backlog.
|
|
835
|
+
const source = getVal('--source', 'scan');
|
|
836
|
+
if (!FOCUS_SOURCES.includes(source)) {
|
|
837
|
+
return error(`Source must be one of: ${FOCUS_SOURCES.join(', ')}`);
|
|
838
|
+
}
|
|
839
|
+
|
|
832
840
|
const existing = readAutoRun(cwd);
|
|
833
841
|
if (existing && (existing.status === AUTORUN_STATUSES.IN_PROGRESS || existing.status === AUTORUN_STATUSES.INITIALIZED)) {
|
|
834
842
|
return error('Auto-run already in progress. Use --stop to end it, or --continue to resume.');
|
|
@@ -848,8 +856,12 @@ function focusAutoInit(cwd, raw, getVal, hasFlag) {
|
|
|
848
856
|
const runData = {
|
|
849
857
|
run_id: generateRunId(cwd),
|
|
850
858
|
status: AUTORUN_STATUSES.INITIALIZED,
|
|
859
|
+
source: source,
|
|
851
860
|
category: category,
|
|
852
861
|
mode: mode,
|
|
862
|
+
parallel_research: hasFlag('--parallel-research'),
|
|
863
|
+
parallel_verify: hasFlag('--parallel-verify'),
|
|
864
|
+
clean_seal: hasFlag('--clean-seal'),
|
|
853
865
|
budget_per_cycle: budget,
|
|
854
866
|
max_cycles: maxCycles,
|
|
855
867
|
total_budget: totalBudget,
|