@magnusekdahl/parallix 1.0.5 → 1.1.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 CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Parallix is a local-first Git workflow CLI for running AI coding agents in isolated, reviewable missions instead of letting one long-lived agent session mutate your main checkout.**
4
4
 
5
- It is for engineers who already use Git and terminal-first coding agents such as Claude Code, Codex, OpenCode/Qwen, and Vibe/Mistral, and want branch isolation, resumable checkpoints, agent-family failover, and a forced review step without building that harness by hand.
5
+ It is for engineers who already use Git and terminal-first coding agents such as Claude Code, Codex, OpenCode/custom, and Vibe/Mistral, and want branch isolation, resumable checkpoints, agent-family failover, and a forced review step without building that harness by hand.
6
6
 
7
7
  It wraps your existing AI coding workflow without replacing it: each mission gets its own branch and worktree, long runs checkpoint to markdown, review is a separate phase, and integration still goes through your repo's own verification command. A human still chooses the mission, launches each phase, reads the output, and decides what lands.
8
8
 
@@ -12,11 +12,10 @@ It wraps your existing AI coding workflow without replacing it: each mission get
12
12
  npm install -g @magnusekdahl/parallix
13
13
  px draft "create a hello world program"
14
14
  px active
15
- px review
16
15
  px integrate
17
16
  ```
18
17
 
19
- That path shows the whole value: isolate the work on its own branch and worktree, let an agent execute it with checkpoints, run a separate review phase, and only then integrate it back.
18
+ That path shows the whole value: isolate the work on its own branch and worktree, let an agent execute it with checkpoints, autorun a separate review phase, and only then integrate it back when you are satisfied with the result.
20
19
 
21
20
  ## Why Parallix?
22
21
 
@@ -33,14 +32,14 @@ Parallix is a mission-based development workflow that addresses each of these di
33
32
 
34
33
  Each capability below is tied to a use case in [`docs/use-cases.md`](docs/use-cases.md), with the confidence level (Confirmed / Partial) carried through honestly.
35
34
 
36
- - **Run several AI coding agents on one repo without clobbering each other** *(UC-1 — Confirmed mechanic).* Every mission gets its own `mission/<slug>` branch and its own sibling git worktree (`../<repo>-<slug>`) automatically, so N agents make progress independently and each lands by squash-merge.
35
+ - **Run several AI coding agents on one repo without clobbering each other**. Every mission gets its own `mission/<slug>` branch and its own sibling git worktree (`../<repo>-<slug>`) automatically, so N agents make progress independently and each lands by squash-merge.
37
36
  - **Fail over automatically when an agent hits its usage limit** *(UC-2 — Confirmed).* Per-family limit messages are pattern-detected; the agent family is written to a timed blocklist and the run retries with the next eligible, unblocked family. Only when all are exhausted does it fail loudly. Agent usage limits stop a single session; they don't have to stop the mission.
38
- - **Resume a long mission deterministically** *(UC-3 — Confirmed).* Every checkpoint runs the gate, commits a checkpoint document with a literal `Next action:` line, and pushes it — so a later session or a different agent resumes from a written instruction, not a guess.
37
+ - **Resume a long mission deterministically**. Every checkpoint runs the gate, commits a checkpoint document with a literal `Next action:` line, and pushes it — so a later session or a different agent resumes from a written instruction, not a guess.
39
38
  - **Force a second, preferentially-different coding agent review before merge** *(UC-4 — Partial).* Review is a separate step whose reviewer selection excludes the implementer to prefer a different agent family, and a self-approval is code-blocked at the provider. It falls back to the same family when no other agent is runnable, so this forces a second review *attempt* — it does not guarantee a different reviewer.
40
- - **Publish work to a Forgejo reviewer surface without making Forgejo your branch authority** *(Confirmed mechanic).* When the review provider is enabled, Parallix syncs the local baseline to a dedicated `review` remote and opens or updates the PR there; if Forgejo is disabled, the branch/worktree flow still runs locally.
41
- - **Use a repo-local Graphify knowledge graph for smaller codebase context pulls** *(Confirmed mechanic, optional, unproven payoff).* In repositories where the operator has already installed the Graphify skill, the workflow keeps `graphify-out/` isolated per worktree and refreshes it during review/integration, while the installed agent guidance steers codebase questions toward `graphify query` / `path` / `explain` before full reports or raw grep. That should reduce context bloat, but this repo does not currently claim a measured token-usage reduction.
42
- - **Keep your existing verification gate instead of agent self-reporting** *(UC-5 — Confirmed).* The gate is a configured shell command with a no-op default: declare your existing `make` / `npm` / script command in `workflow.config.json` and it runs verbatim; declare nothing and verification is a documented no-op pass, not an invented gate.
43
- - **See which agent family actually pays off across every repo one runtime drives** *(UC-6 — Partial).* A single operator-owned `stats.csv` accumulates per-agent usage telemetry across repositories. Token-cost comparison is complete today only for the families with structured telemetry (codex, claude, opencode/qwen); vibe/mistral record honest zeros by design.
39
+ - **Publish work to a Forgejo reviewer surface without making Forgejo your branch authority**. When the review provider is enabled, Parallix syncs the local baseline to a dedicated `review` remote and opens or updates the PR there; if Forgejo is disabled, the branch/worktree flow still runs locally.
40
+ - **Use a repo-local Graphify knowledge graph for smaller codebase context pulls**. In repositories where the operator has already installed the Graphify skill, the workflow keeps `graphify-out/` isolated per worktree and refreshes it during review/integration, while the installed agent guidance steers codebase questions toward `graphify query` / `path` / `explain` before full reports or raw grep. That reduces token-usage.
41
+ - **Keep your existing verification gate instead of agent self-reporting**. The gate is a configured shell command with a no-op default: declare your existing `make` / `npm` / script command in `workflow.config.json` and it runs verbatim; declare nothing and verification is a documented no-op pass, not an invented gate.
42
+ - **See which agent family actually pays off across every repo one runtime drives** *(UC-6 — Partial).* A single operator-owned `stats.csv` accumulates per-agent usage telemetry across repositories. Token-cost comparison is complete today only for the families with structured telemetry (codex, claude, opencode/local AI/custom); vibe/mistral record honest zeros by design.
44
43
 
45
44
  ## The core workflow
46
45
 
@@ -62,14 +61,12 @@ Install from the public npm registry and run a complete mission:
62
61
 
63
62
  ```sh
64
63
  npm install -g @magnusekdahl/parallix
65
- px --version
66
64
  px draft "hello world"
67
65
  px active
68
- px review
69
66
  px integrate
70
67
  ```
71
68
 
72
- `px draft` creates the mission branch, sibling worktree, mission file, and task record. Then `cd` into the mission worktree and run `px active`, `px review`, and `px integrate` there with no slug; the CLI infers the mission from the current branch/worktree.
69
+ `px draft` creates the mission branch, sibling worktree, mission file, and task record. Then `cd` into the mission worktree and run `px active` and `px integrate` there with no slug; the CLI infers the mission from the current branch/worktree.
73
70
 
74
71
  Other draft entry points are available when you need them:
75
72
 
@@ -145,7 +142,6 @@ px active task-042
145
142
  # A second, preferentially-different agent reviews <main>..HEAD.
146
143
  # If Forgejo review is enabled, the PR is published to the dedicated
147
144
  # review surface; a self-approval by the implementing agent is blocked.
148
- px review task-042
149
145
 
150
146
  # Land it: runs configured integration gates, squash-merges to
151
147
  # the primary branch, updates board state, removes the branch
@@ -165,7 +161,7 @@ The full evidence-backed inventory is in [`docs/use-cases.md`](docs/use-cases.md
165
161
 
166
162
  ## What Parallix is not
167
163
 
168
- - **Not a model and not an AI coding agent.** It does not generate code itself. It coordinates the agents and models you already use (Claude Code, Codex, OpenCode/Qwen, and Vibe/Mistral).
164
+ - **Not a model and not an AI coding agent.** It does not generate code itself. It coordinates the agents and models you already use (Claude Code, Codex, OpenCode/custom, and Vibe/Mistral).
169
165
  - **Not an IDE or an editor plugin.** It is a CLI workflow harness around Git and your existing toolchain — there is no UI, no autocomplete, no inline suggestions.
170
166
  - **Not a magic autonomous engineer.** This is a human-in-the-loop workflow. Nothing merges itself, and the safe operating model is that a human decides what to queue, when to run `px active`, how to respond to review findings, and whether `px integrate` should happen at all.
171
167
  - **Not a guaranteed throughput multiplier.** The observed gain varies with context. In the data we have, it ranges from roughly **+57%** on strict user-value output to about **+1,280%** on total completed-mission throughput in a later productized setup. Those are both real observations, but they are different mission-output measures and should be labeled that way.
@@ -177,8 +173,8 @@ The full evidence-backed inventory is in [`docs/use-cases.md`](docs/use-cases.md
177
173
  - **Distribution:** Published to the public npm registry as `@magnusekdahl/parallix`. Local tarball install (`npm pack`) is also supported. No Homebrew, no Docker image, no standalone binary, and no CI/release automation today.
178
174
  - **Review surface:** Forgejo is supported as the hosted PR viewer/publication surface, but the workflow remains local-first and can run without Forgejo when that provider is disabled.
179
175
  - **Versioning:** `CHANGELOG.md` is the versioning authority; PATCH bumps are the release discipline.
180
- - **Telemetry:** structured token/usage telemetry exists for the codex and claude families; the local-Qwen and mistral paths record honest zeros by design rather than fabricated numbers.
181
- - **Graphify:** the knowledge-graph path is supported for codex, claude, and qwen/opencode after one-time operator setup. It is optional, not a workflow prerequisite. The credible claim today is better-scoped context retrieval, not a proven token-savings benchmark.
176
+ - **Telemetry:** structured token/usage telemetry exists for the codex and claude families; the local-custom and mistral paths record honest zeros by design rather than fabricated numbers.
177
+ - **Graphify:** the knowledge-graph path is supported for codex, claude, and custom/opencode after one-time operator setup. It is optional, not a workflow prerequisite. The credible claim today is better-scoped context retrieval, not a proven token-savings benchmark.
182
178
  - **Review coverage** is best-effort, not guaranteed — see UC-4's caveats in [`docs/use-cases.md`](docs/use-cases.md).
183
179
 
184
180
  This is a tool for a local-first developer workflow on one machine, driven by an operator who reads the caveats.
@@ -188,7 +184,7 @@ This is a tool for a local-first developer workflow on one machine, driven by an
188
184
  - [`docs/use-cases.md`](docs/use-cases.md) — evidence-backed use-case inventory with confidence levels and red-team analysis (primary source of truth for what Parallix actually does today).
189
185
  - [`docs/authority-reference.md`](docs/authority-reference.md) — the internal operator reference: workflow modes, the authority model, agent selection, the layered validation model, checkpoint model, state mapping, command aliases, stats, persistent operator data, and the full public-distribution story.
190
186
  - [`docs/forgejo-setup.md`](docs/forgejo-setup.md) — how the Forgejo review surface, tokens, and `review` remote are bootstrapped.
191
- - [`docs/operator-setup.md`](docs/operator-setup.md) — one-time Graphify skill installation for codex, claude, and qwen/opencode.
187
+ - [`docs/operator-setup.md`](docs/operator-setup.md) — one-time Graphify skill installation for codex, claude, and custom/opencode.
192
188
  - [`docs/readme-rewrite-benchmark.md`](docs/readme-rewrite-benchmark.md) — how comparable developer-tool READMEs are structured, and the decisions behind this one.
193
189
  - [`AGENTS.md`](AGENTS.md) — hard rules, restricted actions, and verification entrypoints.
194
190
  - `docs/adr/` — architecture decision records, including ADR 0044 (distribution model).
@@ -3,11 +3,11 @@
3
3
  "_weights_comment": "Weights are relative integers (not percentages). Agent is selected by weighted random draw from eligible-and-supported set. Omit weights to use equal probability.",
4
4
  "steps": {
5
5
  "draft": {
6
- "eligible": ["codex", "qwen", "mistral"],
6
+ "eligible": ["codex", "custom", "mistral"],
7
7
  "selection": "random"
8
8
  },
9
9
  "active": {
10
- "eligible": ["codex", "claude", "qwen", "mistral"],
10
+ "eligible": ["codex", "claude", "custom", "mistral"],
11
11
  "selection": "random"
12
12
  },
13
13
  "conflict-resolution": {
@@ -15,7 +15,7 @@
15
15
  "selection": "random"
16
16
  },
17
17
  "review": {
18
- "eligible": ["codex", "claude", "qwen", "mistral"],
18
+ "eligible": ["codex", "claude", "custom", "mistral"],
19
19
  "selection": "random"
20
20
  }
21
21
  },
@@ -87,7 +87,7 @@
87
87
  "properties": {
88
88
  "models": {
89
89
  "type": "object",
90
- "description": "Optional per-agent-family LLM model override, keyed by agent family name (e.g. codex, claude, gemini, glm, mistral, qwen). Each value is the model identifier passed to that agent's CLI. A family that is not listed sends no model parameter, so the agent uses its own default. There is no 'default' key.",
90
+ "description": "Optional per-agent-family LLM model override, keyed by agent family name (e.g. codex, claude, gemini, glm, mistral, custom). Each value is the model identifier passed to that agent's CLI. A family that is not listed sends no model parameter, so the agent uses its own default. There is no 'default' key.",
91
91
  "additionalProperties": { "type": "string" }
92
92
  }
93
93
  }
package/docs/agents.md CHANGED
@@ -7,13 +7,13 @@
7
7
  | codex | `codex` |
8
8
  | claude | `claude` |
9
9
  | mistral | `vibe` |
10
- | qwen | `opencode` |
10
+ | custom | `opencode` |
11
11
 
12
12
  All four listed launchers are supported on this workstation. Step eligibility for all workflow steps (`draft`, `active`, `conflict-resolution`, `review`) is controlled by `parallix/config/agents.json`. If a launcher is missing from `PATH`, the harness fails loudly with the exact blocker before launching.
13
13
 
14
- ## Tool Calling Workaround (qwen/opencode)
14
+ ## Tool Calling Workaround (custom/opencode)
15
15
 
16
- Opencode (qwen agent family) may encounter issues with concurrent tool calls or tool call timeouts during long-running workflow sessions. When working with opencode:
16
+ Opencode (custom agent family) may encounter issues with concurrent tool calls or tool call timeouts during long-running workflow sessions. When working with opencode:
17
17
 
18
18
  - **Prefer sequential tool calls** over parallel calls for dependent operations — if tool B needs output from tool A, call them separately.
19
19
  - **Use `workdir` parameter instead of `cd` chains** — avoid `cd <dir> && command` patterns; always use `workdir` for directory changes.
@@ -27,7 +27,7 @@ Opencode (qwen agent family) may encounter issues with concurrent tool calls or
27
27
  | codex | `codex exec --sandbox danger-full-access --cd <worktree> <prompt>` with a worktree-local `HOME` under `.workflow/codex-home`; resume uses `codex exec resume <session-id-or---last> <prompt>`; the launcher also seeds `.workflow/codex-home/.codex/config.toml` with the repo-standard trusted posture and copies `.codex/auth.json` so headless review commands can start and keep localhost Forgejo access |
28
28
  | claude | `claude --dangerously-skip-permissions --output-format stream-json --verbose --include-partial-messages -p <prompt>` (cwd=worktree) — uses `--output-format stream-json --verbose --include-partial-messages` to stream real-time JSONL events (tool calls, assistant text chunks) to the operator's terminal via the spawn-tee mechanism. `--include-partial-messages` is required: without it, the assistant event contains the full response at once and no intermediate progress is emitted. Session-id extraction parses the `result` event from stream-json output, falling back to the `claude --resume <id>` regex on plain text. |
29
29
  | mistral | `vibe --prompt <prompt> --trust --output text` (cwd=worktree) — **Note: NOT resume-capable in current Vibe version**; session management uses internal state in `~/.vibe/logs/session/` but does not emit a parseable resume hint to stdout/stderr. |
30
- | qwen | `opencode run --pure --dangerously-skip-permissions <prompt>` (cwd=worktree); resume uses `-s <session>` when a session id is known or `--continue` when only the family marker is known |
30
+ | custom | `opencode run --pure --dangerously-skip-permissions <prompt>` (cwd=worktree); resume uses `-s <session>` when a session id is known or `--continue` when only the family marker is known |
31
31
 
32
32
  ## Launch output watchdog
33
33
 
@@ -74,10 +74,10 @@ Eligibility is controlled by `parallix/config/agents.json`. The default config c
74
74
  ```json
75
75
  {
76
76
  "steps": {
77
- "draft": { "eligible": ["codex", "qwen", "mistral"], "selection": "random" },
78
- "active": { "eligible": ["codex", "claude", "qwen", "mistral"], "selection": "random" },
77
+ "draft": { "eligible": ["codex", "custom", "mistral"], "selection": "random" },
78
+ "active": { "eligible": ["codex", "claude", "custom", "mistral"], "selection": "random" },
79
79
  "conflict-resolution": { "eligible": ["claude", "codex", "mistral"], "selection": "random" },
80
- "review": { "eligible": ["codex", "claude", "qwen", "mistral"], "selection": "random" }
80
+ "review": { "eligible": ["codex", "claude", "custom", "mistral"], "selection": "random" }
81
81
  }
82
82
  }
83
83
  ```
@@ -141,7 +141,7 @@ The workflow detects limit-hit messages in agent stdout/stderr and automatically
141
141
 
142
142
  ### Detection
143
143
 
144
- `parallix/lib/limit-hit.js` ships a regex catalog per agent family (`claude`, `codex`, `qwen`, `mistral`) covering the common shapes:
144
+ `parallix/lib/limit-hit.js` ships a regex catalog per agent family (`claude`, `codex`, `custom`, `mistral`) covering the common shapes:
145
145
  - explicit phrases like `Claude usage limit reached`, `weekly limit`, `Quota exceeded`, `RESOURCE_EXHAUSTED`
146
146
  - HTTP signals (`429 Too Many Requests`, `rate_limit_exceeded`, `Retry-After: ...`)
147
147
 
@@ -45,7 +45,7 @@ Mission flow: `backlog → draft → ready → active → review → approved
45
45
 
46
46
  ## 3. Agent Selection
47
47
 
48
- Four families: `codex`, `claude`, `mistral`, `qwen`. Step eligibility is in `config/agents.json`. Launchers are resolved from `PATH` by bare executable name.
48
+ Four families: `codex`, `claude`, `mistral`, `custom`. Step eligibility is in `config/agents.json`. Launchers are resolved from `PATH` by bare executable name.
49
49
 
50
50
  `WORKFLOW_AGENT=<name>` overrides random selection only when that agent is eligible and unblocked. The effective blocklist is operator-owned at `<PARALLIX_HOME>/agents.local.json`; on first use parallix non-destructively migrates the legacy runtime-config, repo-root, and main-worktree files in that precedence order. Per-agent values: `true` (permanent block), `{ "until": "YYYY-MM-DD HH" }` (timed), `{ "blocked": false }` (unblock).
51
51
 
@@ -256,7 +256,7 @@ Behavior:
256
256
  Telemetry capture contract (task-1285):
257
257
  - Stage rows are keyed by `(mission, stage)`; `draft.js`, `active.js`, and the review loop each record their phase via `recordStageStats`/`recordActiveStats`/`recordReviewStats`.
258
258
  - Structured sources: Codex (`codex-telemetry.js`, rollout JSONL) and Claude (`claude-telemetry.js`, stdout SSE) populate real token/usage fields.
259
- - `opencode` (local Qwen) exposes no structured usage source, so `opencode-telemetry.js` records honest zeros with provider/model falling back to the agent family — never fabricated numbers.
259
+ - `opencode` (local custom) exposes no structured usage source, so `opencode-telemetry.js` records honest zeros with provider/model falling back to the agent family — never fabricated numbers.
260
260
  - `vibe`/`mistral` telemetry is **blocked** in this environment; `mistral-telemetry.js` records honest zeros and the verification is tracked as follow-up task-1288.
261
261
 
262
262
  ---
@@ -0,0 +1,89 @@
1
+ # Documentation Standard
2
+
3
+ Rules for writing and editing README.md and subdirectory READMEs in this repository. Derived from `docs/readme-rewrite-benchmark.md` (task-1336) and applied to the current product state.
4
+
5
+ ---
6
+
7
+ ## 1. Headline / Tagline
8
+
9
+ - The first line after the H1 must be a **one-line capability statement** that tells a skeptical engineer what the tool does.
10
+ - Zero internal jargon. Do not use terms like `authority stack`, `state-map`, `adapter internals`, `worktree pattern`, or any abstraction that only makes sense to someone who has read the code.
11
+ - Name the audience or the execution context if it matters (e.g., "local-first", "CLI", "for Git operators").
12
+ - **Bad:** "Parallix is an authority-driven mission orchestration engine."
13
+ - **Good:** "Parallix is a local-first Git workflow CLI for running AI coding agents in isolated, reviewable missions."
14
+
15
+ ## 2. Opening Paragraph
16
+
17
+ - After the headline, the first paragraph should be **problem-first or contrast-first**: name the pain or define against the adjacent category before describing the mechanism.
18
+ - Keep it to 2-4 sentences. Do not lead with features, screenshots, or install commands.
19
+ - If the tool has a potentially surprising default behavior, state it early (like Aider stating Git auto-commit behavior, like Cline stating the permission model).
20
+
21
+ ## 3. Quickstart Depth
22
+
23
+ - Provide a **shallow path to value**: 2-3 commands to a first working result.
24
+ - Do not bury the quickstart behind enterprise walkthroughs, multi-provider setups, or configuration steps.
25
+ - The quickstart section must be visually separated (fenced code block) and immediately scannable.
26
+
27
+ ## 4. Caveats and Honesty
28
+
29
+ - Caveats are **trust signals** when stated early and specifically. Alpha status, local-first constraints, limited telemetry, and best-effort guarantees belong in their own visible section ("Current status" or "Limitations").
30
+ - Stating limitations upfront builds more credibility than hiding them in fine print.
31
+ - Every quantitative claim must travel with its caveats and source references.
32
+
33
+ ## 5. Superlative Prohibition
34
+
35
+ - No superlatives ("leading", "best", "fastest", "2x faster") without **adoption data or measured benchmarks** to back them.
36
+ - If a claim cannot be falsified with a specific number and source, do not use it.
37
+ - This is a hard rule for alpha-stage projects with limited measured data.
38
+
39
+ ## 6. "What It Is Not" Section
40
+
41
+ - Every README must include a section titled **"What \<tool\> is not"** (or equivalent).
42
+ - Pre-empt the top 3 objections a skeptical user would have (e.g., "not a model", "not an IDE", "not a magic autonomous engineer").
43
+ - This section should appear after use cases and before current status.
44
+
45
+ ## 7. Structural Ordering
46
+
47
+ The canonical order for this repository's README.md is:
48
+
49
+ 1. H1 title + one-line capability headline (bold)
50
+ 2. Audience + positioning paragraph
51
+ 3. **The first concrete thing you can do** (brief intro to quickstart)
52
+ 4. Quick start (fenced code block, 2-3 commands)
53
+ 5. Why \<tool\>? (problem-first)
54
+ 6. What it does (capabilities with evidence refs)
55
+ 7. The core workflow (diagram or description)
56
+ 8. Example (realistic human-in-the-loop pass)
57
+ 9. Use cases (summary with link to full inventory)
58
+ 10. What \<tool\> is not
59
+ 11. Current status (alpha caveats, distribution, review surface, versioning, telemetry)
60
+ 12. Documentation (links to supporting docs)
61
+ 13. Development (test command)
62
+ 14. License
63
+
64
+ Subdirectory READMEs follow a simplified variant: H1 with capability statement, brief description, and relevant sections.
65
+
66
+ ## 8. Tone and Voice
67
+
68
+ - **Plain, anti-hype, engineer-to-engineer.** Write like an engineer wrote it for engineers, not a marketing team.
69
+ - Precise and sober. Casual is fine but do not drift into jokes or bluntness that undermines credibility with a skeptical engineering manager.
70
+ - Use active voice. Prefer concrete verbs over abstract nouns.
71
+ - When describing capabilities, tie them to evidence: use-case IDs, file paths, test names, or config references.
72
+
73
+ ## 9. Link Hygiene
74
+
75
+ - All relative links in README.md must resolve to existing files.
76
+ - Before editing any README, verify: `docs/use-cases.md`, `docs/authority-reference.md`, `docs/forgejo-setup.md`, `docs/operator-setup.md`, `docs/readme-rewrite-benchmark.md`, `AGENTS.md`, `CHANGELOG.md`, `LICENSE`, `docs/adr/`.
77
+ - Broken links are defects, not acceptable trade-offs.
78
+
79
+ ## 10. Subdirectory READMEs
80
+
81
+ - `lib/README.md`, `examples/README.md`, and any future subdirectory READMEs must begin with a one-line capability statement (no internal jargon).
82
+ - Use consistent heading hierarchy: H1 for the directory name, H2 for sections, H3 for subsections.
83
+ - Keep them concise — they are navigation aids, not deep documentation.
84
+
85
+ ---
86
+
87
+ ## Enforcement Hook
88
+
89
+ These rules are enforced by the hook in `AGENTS.md` under the "Documentation" section. Agents editing any `.md` file in the repo root or `docs/` directory MUST consult this standard before committing changes.
@@ -10,18 +10,42 @@ Standalone workflow installs need three review-surface pieces before `active`, `
10
10
 
11
11
  1. Export the workflow into the repo.
12
12
  2. If you need a local Forgejo instance, run `parallix/tools/setup-forgejo-docker.sh` and start it with Docker Compose.
13
- 3. Run `px setup`.
14
- 4. Choose whether to keep the standard Backlog.md-style layout:
13
+ 3. Create the Forgejo accounts the workflow will use (see [Create the agent accounts](#create-the-agent-accounts-fresh-instance)). On a fresh instance these do not exist yet, and `px setup` only mints *tokens* for accounts that already exist — it does not create the accounts themselves.
14
+ 4. Run `px setup`.
15
+ 5. Choose whether to keep the standard Backlog.md-style layout:
15
16
  - task storage in `backlog/`
16
17
  - missions in `missions/`
17
18
  - `mission/*` branches on `main`
18
19
  - worktrees in `../<repo>-<slug>`
19
20
  - verification via `npm test`
20
- 5. If you keep Forgejo bootstrap enabled, enter the Forgejo password for the login that can create the review repo.
21
- 6. Enter passwords for the agent users you want available on this machine, or leave them blank to skip token creation for that user.
21
+ 6. If you keep Forgejo bootstrap enabled, enter the Forgejo password for the login that can create the review repo.
22
+ 7. Enter passwords for the agent users you want available on this machine, or leave them blank to skip token creation for that user.
22
23
 
23
24
  `setup` writes `workflow.config.json`, writes token files into `.forgejo-local/tokens/`, grants the listed agent users write access to the configured review repo, creates or updates the git `review` remote, and runs `verify-env` so the install is validated before you start missions.
24
25
 
26
+ ## Create the agent accounts (fresh instance)
27
+
28
+ `px setup` / `px setup-review` create **tokens** and grant repo access, but they do **not** create Forgejo user accounts — both the basic-auth token path and the owner-token bootstrap path call `POST /users/<user>/tokens` and `PUT /repos/<repo>/collaborators/<user>`, which require the account to already exist. On a fresh Forgejo you must create the accounts first, or token creation fails with HTTP 404 / 401 for users that don't exist.
29
+
30
+ Create one account per identity the workflow uses:
31
+
32
+ - the **owner** that holds the review repo (default `human`),
33
+ - one account per agent family that runs `active`/`review` steps. The canonical list comes from `suggestedForgejoUsers()` — currently `codex`, `claude`, `custom`, `mistral`. (`custom` is the opencode-backed local-model family; it is a first-class identity just like the hosted agents.)
34
+
35
+ For the bundled Docker instance, create them with the Forgejo admin CLI inside the container (replace `<container>` with your Forgejo container name, e.g. `workflow-forgejo`):
36
+
37
+ ```bash
38
+ for u in human codex claude custom mistral; do
39
+ docker exec -u 1000 <container> forgejo admin user create \
40
+ --username "$u" --email "$u@localhost" \
41
+ --password "CHANGE-ME-$u" --must-change-password=false
42
+ done
43
+ ```
44
+
45
+ Then run `px setup` and enter each account's password so the token files are minted into `.forgejo-local/tokens/`.
46
+
47
+ > **Adding or renaming an agent family later** (e.g. the `qwen` → `custom` rename): the new family name is a new Forgejo identity. Create its account with `forgejo admin user create`, grant it write on the review repo, then re-run `px setup-review` to mint its token. Without this, `integrate`/`review` fail with `no token file found for <family>` even though every other agent works.
48
+
25
49
  ## Notes
26
50
 
27
51
  - The configured review repo is created only if it does not already exist.
@@ -15,7 +15,7 @@
15
15
  | `TASK-1275` | move | workflow/parallix subject from title and mission context |
16
16
  | `TASK-1277` | stay | model-capacity tuning note for GPT family selection; not the parallix workflow tool itself |
17
17
  | `TASK-1281` | move | workflow/parallix subject from title and mission context |
18
- | `TASK-1287` | stay | qwen-9B benchmark note; a model-evaluation task, not a parallix workflow change |
18
+ | `TASK-1287` | stay | custom-9B benchmark note; a model-evaluation task, not a parallix workflow change |
19
19
  | `TASK-1288` | move | workflow/parallix subject from title and mission context |
20
20
  | `TASK-1290` | move | workflow/parallix subject from title and mission context |
21
21
  | `TASK-1294` | move | workflow/parallix subject from title and mission context |
@@ -25,7 +25,7 @@ Run the installer once per agent family. It copies a platform-specific skill (an
25
25
  |--------|---------|-----------------|
26
26
  | claude | `graphify install --platform claude` | `~/.claude/skills/graphify/` + `CLAUDE.md` directive |
27
27
  | codex | `graphify install --platform codex` | `~/.agents/skills/graphify/` |
28
- | qwen/opencode | `graphify install --platform opencode` | `~/.config/opencode/skills/graphify/` |
28
+ | custom/opencode | `graphify install --platform opencode` | `~/.config/opencode/skills/graphify/` |
29
29
 
30
30
  Each command produces a `SKILL.md` file in the target directory. After running all three, verify:
31
31
 
@@ -73,7 +73,7 @@ line-by-line transcription. The decisions column is the actionable output.
73
73
  caveats are light.
74
74
  - **Borrow:** Naming the audience inside the positioning ("for operators comfortable with
75
75
  Git and CLI"); stating provider-agnosticism (Parallix's multi-family support is a
76
- genuine parallel — codex/claude/mistral/qwen).
76
+ genuine parallel — codex/claude/mistral/custom).
77
77
  - **Avoid:** The screenshot-first above-the-fold — Parallix's mission is explicitly *out
78
78
  of scope* for screenshots/demos (task-1336 Out of Scope), and a CLI workflow harness is
79
79
  better shown by a command-flow block than a TUI image. Lead with text and a fenced
package/docs/use-cases.md CHANGED
@@ -24,7 +24,7 @@ Each use case carries the four required parts: **(P) persona/buyer**, **(B) befo
24
24
 
25
25
  ### UC-2 — Don't lose a run when one AI provider hits its usage cap
26
26
 
27
- - **(P)** Anyone driving agents on metered/rate-limited LLM subscriptions (Claude, Codex/GPT, Mistral, local Qwen).
27
+ - **(P)** Anyone driving agents on metered/rate-limited LLM subscriptions (Claude, Codex/GPT, Mistral, local custom).
28
28
  - **(B)** *Before:* the agent prints "usage limit reached", the run dies, and you babysit it — manually restarting later or hand-switching to a different model. *After:* the limit message is pattern-detected, that agent family is written to a timed blocklist, and the run retries with the next eligible, unblocked family; only when all are exhausted does it fail loudly.
29
29
  - **(E)** Per-family limit regexes: `lib/agents/limit-hit.js:8-36`; selection honoring eligibility + blocklist + env override: `lib/agents/agents.js:382` (`selectAgent`), `:340-348` (`isAgentBlocked` for permanent/timed/`blocked:false`). Tested: `test/agents-limit-hit.test.js` — `startAgent persists a block via updateAgentBlock when limit-hit detector fires`, `startAgent throws when every eligible agent hits the limit`, `startAgent does not loop forever when WORKFLOW_AGENT is pinned and that agent hits limit`.
30
30
  - **(C)** **Confirmed** — detection, timed-block persistence, and next-agent retry are each covered by named passing tests.
@@ -55,7 +55,7 @@ Each use case carries the four required parts: **(P) persona/buyer**, **(B) befo
55
55
  - **(P)** Operator/buyer deciding which paid agent subscriptions to keep or cut.
56
56
  - **(B)** *Before:* no durable, cross-repo record of how each agent performs, so the keep/cut decision is a hunch. *After:* a single parallix-owned `stats.csv` accumulates per-agent telemetry (`classification, implementer, pr_fix_rounds`, plus an extended 21-column schema) across every repository one runtime drives, keyed so the same mission in different repos stays distinct.
57
57
  - **(E)** `lib/commands/stats.js:14` (legacy 5-col schema), `:21-30` (extended schema). Tested: `test/stats.test.js` — `upsertStatsRow writes the workflow stats schema and updates existing missions idempotently`, `task-1314: upsertStatsRow keys on (repo, mission, stage) so same mission in different repos stays distinct`. The kind of agent-comparison this enables is demonstrated in `../visualBoard/docs/missions/2026/task-1023/RETROSPECTIVE_P5.md:198-243` (per-family PRs, reviews/PR, durations).
58
- - **(C)** **Partial.** Schema and CSV upsert are tested, but the value is bounded: the richest per-agent comparison in the evidence came from Forgejo PR data, not `stats.csv`, and two of four families record honest zeros for token usage (`opencode`/local Qwen and `mistral`/vibe telemetry are zeroed by design, per `README.md:230-231` describing `opencode-telemetry.js`/`mistral-telemetry.js`). So cross-agent *cost/value* comparison is complete only for `codex` and `claude` today.
58
+ - **(C)** **Partial.** Schema and CSV upsert are tested, but the value is bounded: the richest per-agent comparison in the evidence came from Forgejo PR data, not `stats.csv`, and two of four families record honest zeros for token usage (`opencode`/local custom and `mistral`/vibe telemetry are zeroed by design, per `README.md:230-231` describing `opencode-telemetry.js`/`mistral-telemetry.js`). So cross-agent *cost/value* comparison is complete only for `codex` and `claude` today.
59
59
 
60
60
  ---
61
61
 
@@ -1,4 +1,4 @@
1
- # parallix examples
1
+ # Parallix Examples
2
2
 
3
3
  These examples use the provisional `px` runner from inside a **caller-supplied**
4
4
  target repository. They contain no fixed workstation paths, no sibling worktree
package/index.js CHANGED
@@ -223,6 +223,13 @@ ${fmt.bold('Core Commands:')}
223
223
  rebase [<slug>] [--push] Rebase mission branch onto the primary integration branch (main) with auto-resolution of mission-specific conflicts.
224
224
  diff [<slug>] Launch the primary local diff tool for branch-vs-main review.
225
225
  stats [<csv_file>|--csv-file <path>] [--today YYYY-MM-DD|--from YYYY-MM-DD --to YYYY-MM-DD] [--output <file>] Print parallix weekly or range tables from <PARALLIX_HOME>/stats.csv; legacy retrospective CSVs remain supported.
226
+ config Print the effective configuration (built-in defaults merged with workflow.config.json). Read-only.
227
+ aliases Print the derived command-alias table (state-map virtual states → canonical commands).
228
+
229
+ ${fmt.bold('Utility Commands:')}
230
+ version, --version, -v Print the package version, px path, package root, and Node version.
231
+ shell-init [bash|zsh] Print the shell integration snippet that cds your terminal into the next mission worktree on transitions.
232
+ review-event <slug> --type <type> --actor <actor> --content <text> [--timestamp <stamp>] [--skip-git] Append a review-thread event for a mission.
226
233
 
227
234
  ${fmt.bold('Notes:')}
228
235
  - <slug> is optional if it can be inferred from the current branch, directory name, or git worktree.
@@ -5,7 +5,7 @@ const fmt = require('../core/fmt');
5
5
  const { startCodexDraftAgent, resolveCodexCommand } = require('./codex');
6
6
  const { startClaudeAgent, resolveClaudeCommand } = require('./claude');
7
7
  const { startMistralAgent, resolveMistralCommand } = require('./mistral');
8
- const { startOpencodeAgent, resolveOpencodeCommand } = require('./opencode');
8
+ const { startOpencodeAgent, resolveOpencodeCommand, isSpuriousOpencodeExit } = require('./opencode');
9
9
  const { detectLimitHit, formatBlockUntil, DEFAULT_FALLBACK_HOURS } = require('./limit-hit');
10
10
  const sessions = require('../tools/sessions');
11
11
  const storage = require('../core/storage');
@@ -17,9 +17,9 @@ const { migrateAgentBlocklists } = require('../core/persistent-data-migration');
17
17
  // "codex resume <id>", "opencode -s ses_<id>",
18
18
  // "claude --resume <id>"). The resume flag is only used when the caller
19
19
  // passes slug+role+worktree and the session marker matches the chosen agent.
20
- // qwen (opencode) always uses --continue; claude uses --continue; codex uses
20
+ // custom (opencode) always uses --continue; claude uses --continue; codex uses
21
21
  // `exec resume --last`.
22
- const RESUME_CAPABLE = new Set(['claude', 'codex', 'qwen']);
22
+ const RESUME_CAPABLE = new Set(['claude', 'codex', 'custom']);
23
23
 
24
24
  const CONFIG_PATH = path.join(__dirname, '..', '..', 'config', 'agents.json');
25
25
 
@@ -30,21 +30,21 @@ const LAUNCHERS = {
30
30
  codex: startCodexDraftAgent,
31
31
  claude: startClaudeAgent,
32
32
  mistral: startMistralAgent,
33
- qwen: startOpencodeAgent
33
+ custom: startOpencodeAgent
34
34
  };
35
35
 
36
36
  const RESOLVERS = {
37
37
  codex: resolveCodexCommand,
38
38
  claude: resolveClaudeCommand,
39
39
  mistral: resolveMistralCommand,
40
- qwen: resolveOpencodeCommand
40
+ custom: resolveOpencodeCommand
41
41
  };
42
42
 
43
43
  const HEALTH_PROBE_ARGS = Object.freeze({
44
44
  codex: ['--help'],
45
45
  claude: ['--help'],
46
46
  mistral: ['--help'],
47
- qwen: ['--help']
47
+ custom: ['--help']
48
48
  });
49
49
  const LAUNCHER_HEALTH_TIMEOUT_MS = 3000;
50
50
  const DEFAULT_NO_OUTPUT_INITIAL_DELAY_MS = 60_000;
@@ -800,9 +800,13 @@ async function startAgent(step, opts = {}) {
800
800
  // Only treat `status !== null && status !== 0` or `signal` (with no spawn
801
801
  // error) as a launch failure; `status: null` without signal is ambiguous
802
802
  // (spawn-tee close event can emit null code) and should not trigger a retry.
803
+ // Spurious opencode v2.0.0 JSON-mode exits (exit 1 after a valid
804
+ // "reason":"stop" completion) are excluded — the agent completed, the
805
+ // non-zero code is a post-run cleanup race.
803
806
  const launchFailed = result &&
804
807
  ((result.status !== null && result.status !== 0) || (result.signal && !result.error)) &&
805
- !limitHit;
808
+ !limitHit &&
809
+ !isSpuriousOpencodeExit(result);
806
810
  if (launchFailed) {
807
811
  const exitInfo = result.signal
808
812
  ? `signal ${result.signal}`
@@ -820,10 +824,10 @@ async function startAgent(step, opts = {}) {
820
824
  });
821
825
  tried.add(chosen);
822
826
  launched.add(chosen);
823
- // Block non-qwen agents on non-limit failures so selectAgent excludes them
827
+ // Block non-custom agents on non-limit failures so selectAgent excludes them
824
828
  // on the next retry iteration, and the review-loop fallback path can activate.
825
- // qwen (opencode/local AI) is excluded — exit 1 is a temporary local error.
826
- if (chosen !== 'qwen') {
829
+ // custom (opencode/local AI) is excluded — exit 1 is a temporary local error.
830
+ if (chosen !== 'custom') {
827
831
  const blockUntil = formatBlockUntil(new Date(Date.now() + DEFAULT_FALLBACK_HOURS * 60 * 60 * 1000));
828
832
  try {
829
833
  const blockResult = updateAgentBlockFn(chosen, blockUntil);
@@ -20,7 +20,7 @@ const PATTERN_SETS = Object.freeze({
20
20
  /resource_exhausted/i
21
21
  ],
22
22
 
23
- qwen: [
23
+ custom: [
24
24
  /\b(?:rate|usage|quota)\s*limit\s*(?:reached|exceeded)\b/i,
25
25
  /\b429\b[^\n]*?\b(?:rate|quota|usage)\b/i,
26
26
  /\b429\b/i,
@@ -4,20 +4,20 @@
4
4
  * Opencode Telemetry Parser
5
5
  *
6
6
  * Parses `opencode export` JSON output to extract real token-usage data for
7
- * Qwen sessions. The exported JSON may contain token usage in various shapes
7
+ * custom/opencode sessions. The exported JSON may contain token usage in various shapes
8
8
  * depending on the opencode version, so this parser is resilient to:
9
9
  * - Missing token fields (substitutes 0)
10
10
  * - Nested vs flat structures
11
11
  * - Different field name conventions
12
12
  *
13
- * Provider and model fields are set to "opencode" and "qwen" respectively,
13
+ * Provider and model fields are set to "opencode" and "custom" respectively,
14
14
  * matching the convention used by other telemetry modules.
15
15
  *
16
16
  * See task-1285 for the full telemetry credibility design.
17
17
  */
18
18
 
19
19
  const PROVIDER = 'opencode';
20
- const MODEL = 'qwen';
20
+ const MODEL = 'custom';
21
21
 
22
22
  /**
23
23
  * Safely extract a finite number from a value, defaulting to 0.
@@ -255,10 +255,18 @@ function countToolCalls(parsed) {
255
255
  * Returns null when the content yields no usable token signal (empty string,
256
256
  * non-JSON, or JSON without any token-usage fields).
257
257
  *
258
+ * When the JSON contains token data but no model field, falls back to
259
+ * `fallbackModel` — the model the launcher was configured with for this run
260
+ * (from workflow.config.json) — so telemetry records the actual model id
261
+ * instead of the generic family label. When no model is configured, falls
262
+ * back to the generic family label `MODEL`.
263
+ *
258
264
  * @param {string} jsonString - Raw JSON string from `opencode export`
265
+ * @param {string=} [fallbackModel] - Configured model id for this run, used
266
+ * when the export JSON omits the model field
259
267
  * @returns {object|null} Normalized telemetry object or null
260
268
  */
261
- function extractOpencodeTelemetryFromExport(jsonString) {
269
+ function extractOpencodeTelemetryFromExport(jsonString, fallbackModel) {
262
270
  if (!jsonString || typeof jsonString !== 'string' || !jsonString.trim()) {
263
271
  return null;
264
272
  }
@@ -292,17 +300,17 @@ function extractOpencodeTelemetryFromExport(jsonString) {
292
300
  // total_tokens is set but individual fields are zero — treat total_tokens as the real signal
293
301
  // and derive input/output from it conservatively
294
302
  const totalTokens = num(tokenUsage.total_tokens);
295
- return {
296
- sessionId: extractSessionId(parsed),
297
- provider: PROVIDER,
298
- model: extractModelName(parsed) || MODEL,
299
- inputTokens: 0,
300
- outputTokens: 0,
301
- cachedTokens: 0,
302
- totalTokens,
303
- toolCalls: countToolCalls(parsed),
304
- usagePercent: null,
305
- };
303
+ return {
304
+ sessionId: extractSessionId(parsed),
305
+ provider: PROVIDER,
306
+ model: extractModelName(parsed) || fallbackModel || MODEL,
307
+ inputTokens: 0,
308
+ outputTokens: 0,
309
+ cachedTokens: 0,
310
+ totalTokens,
311
+ toolCalls: countToolCalls(parsed),
312
+ usagePercent: null,
313
+ };
306
314
  }
307
315
 
308
316
  const totalTokens = num(tokenUsage.total_tokens) || (inputTokens + outputTokens);
@@ -310,7 +318,7 @@ function extractOpencodeTelemetryFromExport(jsonString) {
310
318
  return {
311
319
  sessionId: extractSessionId(parsed),
312
320
  provider: PROVIDER,
313
- model: extractModelName(parsed) || MODEL,
321
+ model: extractModelName(parsed) || fallbackModel || MODEL,
314
322
  inputTokens,
315
323
  outputTokens,
316
324
  cachedTokens,
@@ -342,10 +350,12 @@ function extractOpencodeTelemetry(result) {
342
350
  * Return the provider/model pair for opencode tasks.
343
351
  * Used as fallback when telemetry is null.
344
352
  *
353
+ * @param {string=} [defaultModel] - Optional configured model id; falls back
354
+ * to the generic family label when absent.
345
355
  * @returns {{provider: string, model: string}}
346
356
  */
347
- function getOpencodeProviderModel() {
348
- return { provider: PROVIDER, model: MODEL };
357
+ function getOpencodeProviderModel(defaultModel) {
358
+ return { provider: PROVIDER, model: defaultModel || MODEL };
349
359
  }
350
360
 
351
361
  module.exports = {