@magnusekdahl/parallix 1.0.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/CHANGELOG.md +140 -0
- package/LICENSE +661 -0
- package/README.md +196 -0
- package/config/agents.json +25 -0
- package/config/agents.local.json.template +8 -0
- package/config/state-map.json +4 -0
- package/config/state-map.json.template +31 -0
- package/config/workflow.config.schema.json +98 -0
- package/data/.gitkeep +0 -0
- package/docs/adr/0031-ai-agent-instruction-boundary-and-command-floor.md +114 -0
- package/docs/adr/0032-mission-refinement-state-and-usage-budget-signals.md +135 -0
- package/docs/adr/0034-module-and-skill-invocation-model.md +202 -0
- package/docs/adr/0036-mission-sizing-and-dependency-wave-heuristics.md +79 -0
- package/docs/adr/0037-ai-workflow-coordination-architecture.md +162 -0
- package/docs/adr/0041-integration-pipeline-gates.md +165 -0
- package/docs/adr/0042-workflow-cli-color-rendering-approach.md +106 -0
- package/docs/adr/0043-git-target-resolution-strategy.md +185 -0
- package/docs/adr/0044-workflow-distribution-model.md +277 -0
- package/docs/adr/0045-parallax-branch-model.md +182 -0
- package/docs/adr/0046-npm-publish-process-and-security.md +138 -0
- package/docs/adr/index.md +20 -0
- package/docs/agents.md +212 -0
- package/docs/authority-reference.md +298 -0
- package/docs/forgejo-setup.md +31 -0
- package/docs/migration/extraction.md +61 -0
- package/docs/migration/task-classification.md +36 -0
- package/docs/operator-setup.md +76 -0
- package/docs/readme-rewrite-benchmark.md +188 -0
- package/docs/use-cases.md +105 -0
- package/examples/README.md +62 -0
- package/examples/run-enterprise-tarball-workflow-smoke.sh +257 -0
- package/examples/run-verify-env-smoke.sh +40 -0
- package/index.js +250 -0
- package/lib/README.md +13 -0
- package/lib/agents/agents.js +867 -0
- package/lib/agents/claude-telemetry.js +233 -0
- package/lib/agents/claude.js +139 -0
- package/lib/agents/codex-telemetry.js +202 -0
- package/lib/agents/codex.js +219 -0
- package/lib/agents/limit-hit.js +252 -0
- package/lib/agents/mistral-telemetry.js +44 -0
- package/lib/agents/mistral.js +68 -0
- package/lib/agents/opencode-export.js +110 -0
- package/lib/agents/opencode-telemetry.js +356 -0
- package/lib/agents/opencode.js +218 -0
- package/lib/agents/stage-telemetry.js +37 -0
- package/lib/commands/active.js +625 -0
- package/lib/commands/checkpoint.js +76 -0
- package/lib/commands/config.js +39 -0
- package/lib/commands/coverage-gate.js +358 -0
- package/lib/commands/diff.js +119 -0
- package/lib/commands/draft.js +854 -0
- package/lib/commands/handoff.js +501 -0
- package/lib/commands/integrate.js +1528 -0
- package/lib/commands/mission-start.js +246 -0
- package/lib/commands/rebase.js +597 -0
- package/lib/commands/repair-handoff.js +227 -0
- package/lib/commands/resolve-conflict.js +109 -0
- package/lib/commands/review.js +13 -0
- package/lib/commands/setup-review.js +13 -0
- package/lib/commands/setup.js +3 -0
- package/lib/commands/stats-backfill.js +395 -0
- package/lib/commands/stats.js +1601 -0
- package/lib/commands/status.js +183 -0
- package/lib/commands/verify.js +1 -0
- package/lib/core/fmt.js +202 -0
- package/lib/core/git.js +73 -0
- package/lib/core/gitignore.js +110 -0
- package/lib/core/mission-utils.js +1017 -0
- package/lib/core/persistent-data-migration.js +201 -0
- package/lib/core/product-config.js +508 -0
- package/lib/core/runtime-matrix.js +82 -0
- package/lib/core/spawn-tee.js +173 -0
- package/lib/core/state-map.js +89 -0
- package/lib/core/storage.js +165 -0
- package/lib/core/verification.js +149 -0
- package/lib/index.js +77 -0
- package/lib/review/rebase.js +163 -0
- package/lib/review/review-adapter.js +135 -0
- package/lib/review/review-artifacts.js +619 -0
- package/lib/review/review-commands.js +1375 -0
- package/lib/review/review-events.js +1007 -0
- package/lib/review/review-loop.js +1004 -0
- package/lib/review/review-polling.js +141 -0
- package/lib/review/review-prompts.js +212 -0
- package/lib/review/review-state.js +280 -0
- package/lib/review/review.js +96 -0
- package/lib/tools/backlog.js +680 -0
- package/lib/tools/forgejo.js +1585 -0
- package/lib/tools/gatekeeper.js +106 -0
- package/lib/tools/sessions.js +74 -0
- package/lib/tools/setup-review.js +1053 -0
- package/package.json +56 -0
- package/prompts/act-on-review-verbose.md +20 -0
- package/prompts/act-on-review.md +22 -0
- package/prompts/draft.md +20 -0
- package/prompts/execute.md +24 -0
- package/prompts/portfolio.md +30 -0
- package/prompts/review-verbose.md +20 -0
- package/prompts/review.md +17 -0
- package/px.js +236 -0
- package/templates/AGENTS-snippet.md +14 -0
- package/templates/AGENTS.md.template +34 -0
- package/templates/CLAUDE.md.template +27 -0
- package/templates/CODEX.md.template +38 -0
- package/templates/MISTRAL.md.template +24 -0
- package/templates/claude-commands/act-on-review.md +3 -0
- package/templates/claude-commands/area-review.md +3 -0
- package/templates/claude-commands/draft.md +6 -0
- package/templates/claude-commands/execute.md +6 -0
- package/templates/claude-commands/integrate.md +4 -0
- package/templates/claude-commands/portfolio.md +5 -0
- package/templates/claude-commands/review.md +4 -0
- package/templates/codex/config.toml +6 -0
- package/templates/mission-scaffold.md +39 -0
- package/templates/vibe/skills/act-on-review/SKILL.md +16 -0
- package/templates/vibe/skills/area-review/SKILL.md +16 -0
- package/templates/vibe/skills/draft/SKILL.md +16 -0
- package/templates/vibe/skills/execute/SKILL.md +16 -0
- package/templates/vibe/skills/integrate/SKILL.md +16 -0
- package/templates/vibe/skills/portfolio/SKILL.md +21 -0
- package/templates/vibe/skills/review/SKILL.md +16 -0
- package/tools/setup-forgejo-docker.sh +84 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# ADR 0045: parallix branch model and integration modes
|
|
2
|
+
|
|
3
|
+
Status: Proposed
|
|
4
|
+
Date: 2026-06-14
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
The parallix workflow manages mission branches (`mission/<slug>`) that agents create, develop on, and integrate back into a target branch. This branching model is implicit across multiple command handlers (`forgejo.js`, `draft.js`, `integrate.js`, `mission-start.js`, `mission-utils.js`, `rebase.js`) with no single authoritative reference. ADR 0043 established that local primary branches are the ancestry authority for workflow operations, but did not document the full branch lifecycle (draft → push → review → integrate → cleanup).
|
|
9
|
+
|
|
10
|
+
Two distinct integration modes coexist in the codebase:
|
|
11
|
+
1. Trunk-based: the mission branch integrates back into the repository's primary branch (`main` or `master`).
|
|
12
|
+
2. Feature-branch: the mission branch integrates back into a recorded feature branch, with the base branch detected and persisted at draft time.
|
|
13
|
+
|
|
14
|
+
Forgejo serves as a PR viewer and publication surface. The workflow does not require Forgejo to be configured as an upstream remote — all local branch operations (rebase, merge-base, integration) run against local branches. A dedicated `review` remote provides the push/fetch target for Forgejo synchronization.
|
|
15
|
+
|
|
16
|
+
## Decision
|
|
17
|
+
|
|
18
|
+
### Branch naming convention
|
|
19
|
+
|
|
20
|
+
Mission branches use the prefix `mission/` followed by the task slug:
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
mission/<slug> (e.g., mission/task-1280)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The prefix is configurable via the `missions.branchPrefix` adapter setting, defaulting to `mission/`. The naming is produced by `missionBranchName(slug)` and referenced throughout the workflow as the canonical mission branch identifier.
|
|
27
|
+
|
|
28
|
+
Evidence: `parallix/lib/core/mission-utils.js:49-51`.
|
|
29
|
+
|
|
30
|
+
### Base worktree naming convention
|
|
31
|
+
|
|
32
|
+
Base feature-branch worktrees use a conventional path derived from the same `worktreePattern` used for mission worktrees. The base branch name is slug-sanitised (`/` → `-`) and prefixed with `base-`:
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
../<repo>-base-<sanitized-branch>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
This ensures base worktrees are discoverable and removable with existing worktree tooling, and never collide with mission worktrees.
|
|
39
|
+
|
|
40
|
+
Evidence: `parallix/lib/core/mission-utils.js:190-193`.
|
|
41
|
+
|
|
42
|
+
### Integration mode 1: Trunk-based
|
|
43
|
+
|
|
44
|
+
In trunk-based mode, the mission branch integrates back into the repository's primary branch (`main` or `master`). This is the default mode and covers every pre-existing mission.
|
|
45
|
+
|
|
46
|
+
Lifecycle:
|
|
47
|
+
|
|
48
|
+
1. **Draft**: `mission/<slug>` is created from the primary branch (`main`/`master`). No `Base-Branch:` line is written to MISSION.md because the base equals the primary.
|
|
49
|
+
2. **Development**: Agents work on the mission branch in the mission worktree.
|
|
50
|
+
3. **Rebase**: The mission branch is rebased onto the local primary branch (ADR 0043 local-first rule). The workflow may fetch `review/<primary>` for remote visibility but the rebase target is the local primary ref.
|
|
51
|
+
4. **Integration**: The mission branch is squash-merged into the primary worktree on `main`/`master`. The integration runs Variant A (PR already merged on Forgejo, fast-path closeout) or Variant B (local squash-merge).
|
|
52
|
+
|
|
53
|
+
Branch flow:
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
main ──────■──────────────────■── main (after squash-merge)
|
|
57
|
+
\ /
|
|
58
|
+
■── mission/<slug> ─■
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Evidence: `parallix/lib/core/mission-utils.js:318-325` (trunk delegation), `parallix/lib/commands/integrate.js:679-685` (integration resolution), `parallix/lib/commands/integrate.js:521-580` (squash-merge in primary worktree).
|
|
62
|
+
|
|
63
|
+
### Integration mode 2: Feature-branch
|
|
64
|
+
|
|
65
|
+
In feature-branch mode, the mission branch integrates back into a recorded feature branch. The base branch is detected at draft time, persisted in MISSION.md, and used throughout the mission lifecycle.
|
|
66
|
+
|
|
67
|
+
Lifecycle:
|
|
68
|
+
|
|
69
|
+
1. **Detect**: At draft time, `detectLaunchBaseBranch()` reads `git branch --show-current` to determine which branch HEAD is on. It refuses to nest missions on a `mission/*` branch and returns `null` when HEAD is detached (falling back to primary).
|
|
70
|
+
2. **Record**: `ensureMissionBaseBranchRecorded()` writes a single `Base-Branch: <branch>` line into MISSION.md. This line is idempotent — a no-op when the base equals the primary, and re-asserted after the draft agent runs.
|
|
71
|
+
3. **Create**: The mission branch `mission/<slug>` is created from the recorded base branch as its start point.
|
|
72
|
+
4. **Development**: Agents work on the mission branch in the mission worktree.
|
|
73
|
+
5. **Resolve**: `resolveMissionBaseBranch()` returns the recorded `Base-Branch:` value. `resolveBaseWorktree()` resolves or auto-creates the base worktree on the recorded branch.
|
|
74
|
+
6. **Integration**: The mission branch is squash-merged into the **base worktree** on the recorded base branch (not the primary worktree). The integration runs Variant A or Variant B as with trunk mode.
|
|
75
|
+
|
|
76
|
+
Branch flow:
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
feature ─■────────────────■── feature (after squash-merge)
|
|
80
|
+
\ /
|
|
81
|
+
■── mission/<slug> ─■
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Detection and recording evidence: `parallix/lib/core/mission-utils.js:207-223` (`detectLaunchBaseBranch`), `parallix/lib/commands/draft.js:327-352` (`ensureMissionBaseBranchRecorded`), `parallix/lib/commands/draft.js:316` (branch creation from base).
|
|
85
|
+
|
|
86
|
+
Resolution evidence: `parallix/lib/core/mission-utils.js:239-287` (`readRecordedBaseBranch`, `resolveMissionBaseBranch`), `parallix/lib/core/mission-utils.js:318-350` (`resolveBaseWorktree`).
|
|
87
|
+
|
|
88
|
+
Integration evidence: `parallix/lib/commands/integrate.js:679-685` (integration into base worktree), `parallix/lib/commands/integrate.js:521-580` (squash-merge in base worktree).
|
|
89
|
+
|
|
90
|
+
Preflight guard evidence: `parallix/lib/commands/mission-start.js:158-188` (verifies base branch exists locally at mission start).
|
|
91
|
+
|
|
92
|
+
### Forgejo role: PR viewer, not branch authority
|
|
93
|
+
|
|
94
|
+
Forgejo is strictly a PR viewer and publication surface. The workflow does not require Forgejo to be configured as `origin` or any upstream remote.
|
|
95
|
+
|
|
96
|
+
Key behaviors:
|
|
97
|
+
|
|
98
|
+
- **`syncPrimaryBaseline()`**: Before PR creation, the workflow pushes the local primary branch to Forgejo's copy of `main`/`master` via an authenticated URL. This keeps the Forgejo review surface current.
|
|
99
|
+
- **PR creation**: `createPr()` resolves the PR base (recorded feature base, else primary), syncs the primary baseline, pushes the mission branch via an authenticated one-off URL, and POSTs the PR with the resolved base.
|
|
100
|
+
- **Bypassable**: When `isForgejoReviewEnabled` is `false`, all Forgejo sync paths in `integrate` and `rebase` are skipped. The workflow operates entirely on local branches and worktrees.
|
|
101
|
+
|
|
102
|
+
Evidence: `parallix/lib/tools/forgejo.js:379-491` (`createPr`), `parallix/lib/tools/forgejo.js:696-717` (`syncPrimaryBaseline`), `parallix/lib/commands/integrate.js:537-549` and `:619-629` (Forgejo skip paths), `parallix/lib/core/product-config.js:434-436` (provider gate).
|
|
103
|
+
|
|
104
|
+
### The `review` remote
|
|
105
|
+
|
|
106
|
+
The `review` remote is a named git remote that serves as the push/fetch target for Forgejo synchronization. It is configured via `workflow.config.json` → `adapters.review.remote` (value `"review"` by default).
|
|
107
|
+
|
|
108
|
+
Operations:
|
|
109
|
+
|
|
110
|
+
- **Push**: `pushReviewRef()` executes `git push review <src>:<dst>` to push refs to the Forgejo-hosted repo.
|
|
111
|
+
- **Fetch**: `fetchReviewBranch()` executes `git fetch review +refs/heads/<branch>:refs/remotes/review/<branch>`, or uses an authenticated URL when a token is supplied.
|
|
112
|
+
- **Delete**: `deleteReviewRef()` removes refs from the review remote.
|
|
113
|
+
- **Setup**: `setup-review.js` creates or updates the remote via `git remote add/set-url <remote> <url>`.
|
|
114
|
+
|
|
115
|
+
The remote name is resolved from `adapters.review.remote || 'review'` via `resolveReviewAdapter()`.
|
|
116
|
+
|
|
117
|
+
Evidence: `workflow.config.json:16`, `parallix/lib/core/product-config.js:423-430` (`resolveReviewAdapter`), `parallix/lib/tools/forgejo.js:719-741` (push/fetch), `:803-810` (delete), `parallix/lib/tools/setup-review.js:386-392`, `:215`, `:781`.
|
|
118
|
+
|
|
119
|
+
### Rebase target (ADR 0043 invariant)
|
|
120
|
+
|
|
121
|
+
The `rebase` command fetches `review/<primary>` for remote visibility updates but rebases the mission branch onto the **local** primary branch. This is the ADR 0043 local-first invariant, ensuring rebase and integrate use the same ancestry target.
|
|
122
|
+
|
|
123
|
+
Evidence: `parallix/lib/commands/rebase.js:93-117`.
|
|
124
|
+
|
|
125
|
+
## Decision matrix
|
|
126
|
+
|
|
127
|
+
| Aspect | Trunk-based mode | Feature-branch mode |
|
|
128
|
+
|--------|------------------|---------------------|
|
|
129
|
+
| Mission branch | `mission/<slug>` | `mission/<slug>` |
|
|
130
|
+
| Integration target | Primary `main`/`master` | Recorded `Base-Branch` |
|
|
131
|
+
| Integration worktree | Primary worktree | Base worktree (`<repo>-base-<branch>`) |
|
|
132
|
+
| Base detection | Implicit (primary) | Explicit (`detectLaunchBaseBranch`) |
|
|
133
|
+
| Base persistence | No `Base-Branch:` line | `Base-Branch:` line in MISSION.md |
|
|
134
|
+
| Default mode | Yes (all pre-existing missions) | No (opt-in via draft context) |
|
|
135
|
+
| Preflight check | N/A | Verifies base branch exists locally |
|
|
136
|
+
| Code path overlap | 80%+ (same integrate/rebase commands) | Branch resolution differs at `resolveMissionBaseBranch` |
|
|
137
|
+
|
|
138
|
+
## Consequences
|
|
139
|
+
|
|
140
|
+
### Positive
|
|
141
|
+
|
|
142
|
+
- **Single source of truth**: The branch model is now documented with explicit file:line references, eliminating the need to trace through multiple command handlers to understand branch behavior.
|
|
143
|
+
- **Forgejo is not a hard dependency**: The workflow can operate entirely offline (local rebase, local squash-merge) when Forgejo is unavailable or disabled.
|
|
144
|
+
- **Feature-branch mode is explicit**: The documented flow clarifies how missions on feature branches integrate back, including the auto-creation of base worktrees.
|
|
145
|
+
- **Backward compatible**: Every pre-existing mission follows the trunk-based path byte-identically, since `resolveMissionBaseBranch` falls back to `getPrimaryBranch()` when no `Base-Branch:` line exists.
|
|
146
|
+
- **Preflight safety**: Mission-start verifies the base branch exists locally, preventing late integration failures.
|
|
147
|
+
|
|
148
|
+
### Negative
|
|
149
|
+
|
|
150
|
+
- **Base worktree management**: Feature-branch mode introduces a second worktree per mission (mission worktree + base worktree), increasing storage and checkout overhead.
|
|
151
|
+
- **Stale base risk**: If the base branch is deleted or the base worktree becomes stale, `resolveBaseWorktree` throws an error rather than recovering gracefully.
|
|
152
|
+
- **Documentation coupling**: Future code changes to branch resolution logic must update this ADR to stay accurate.
|
|
153
|
+
|
|
154
|
+
### Open questions
|
|
155
|
+
|
|
156
|
+
- What happens when the base worktree is removed externally (e.g., manual `git worktree remove`)? The current code auto-recreates it on the next integration attempt.
|
|
157
|
+
- Does the workflow need a cleanup command for base worktrees, analogous to mission worktree removal?
|
|
158
|
+
- Should `Base-Branch:` be cleared when a mission transitions from feature-branch to trunk-based mode?
|
|
159
|
+
|
|
160
|
+
## Alternatives considered
|
|
161
|
+
|
|
162
|
+
| Alternative | Description | Why rejected |
|
|
163
|
+
|-------------|-------------|--------------|
|
|
164
|
+
| Forgejo as upstream | Configure Forgejo as `origin` and derive all branch operations from it | Adds unnecessary network dependency; local-first model is simpler and more resilient |
|
|
165
|
+
| Single integration mode | Support only trunk-based integration | Feature-branch mode is actively used and works correctly in code; documenting it prevents future confusion |
|
|
166
|
+
| Remote-tracking as authority | Use `review/<primary>` as the rebase/integrate target | Causes split-brain between rebase and integrate (ADR 0043); stale remote refs produce incorrect ancestry |
|
|
167
|
+
| Separate workflow for feature branches | Maintain a distinct workflow path for feature-branch missions | 80%+ code overlap; a unified flow with conditional base resolution is simpler and more maintainable |
|
|
168
|
+
| Forgejo as PR author | Have Forgejo create PRs programmatically | Forgejo is sufficient as a viewer; PR creation via authenticated push + API POST achieves the same outcome without platform dependency |
|
|
169
|
+
|
|
170
|
+
## Links
|
|
171
|
+
|
|
172
|
+
- `parallix/lib/core/mission-utils.js` — branch naming, base detection/recording/resolution, worktree management
|
|
173
|
+
- `parallix/lib/commands/draft.js` — mission branch creation, base branch recording
|
|
174
|
+
- `parallix/lib/commands/integrate.js` — integration merge, variant selection, Forgejo sync
|
|
175
|
+
- `parallix/lib/commands/rebase.js` — rebase target resolution, Forgejo visibility fetch
|
|
176
|
+
- `parallix/lib/commands/mission-start.js` — preflight base-branch verification
|
|
177
|
+
- `parallix/lib/tools/forgejo.js` — PR creation, primary baseline sync, review remote push/fetch/delete
|
|
178
|
+
- `parallix/lib/tools/setup-review.js` — review remote setup
|
|
179
|
+
- `parallix/lib/core/product-config.js` — review adapter resolution, provider gate
|
|
180
|
+
- `parallix/lib/core/git.js` — low-level git CLI wrappers
|
|
181
|
+
- `docs/adr/0043-git-target-resolution-strategy.md` — ADR 0043: local-first git target resolution
|
|
182
|
+
- `parallix/config/workflow.config.schema.json` — workflow runtime configuration schema
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# ADR 0046: parallix npm Registry Publication Process and Security Posture
|
|
2
|
+
|
|
3
|
+
Status: Proposed | Accepted
|
|
4
|
+
Date: 2026-06-23
|
|
5
|
+
|
|
6
|
+
Related: ADR 0044 (Workflow Distribution Model), task-1340 (make parallix publishable)
|
|
7
|
+
|
|
8
|
+
## Context
|
|
9
|
+
|
|
10
|
+
ADR 0044 established the distribution stance as "local npm tarball, globally installed `px` CLI" and deferred concrete npm registry publication. parallix was pushed to public GitHub on 2026-06-22 (task-1322). The repo is now public and the operator wants a credible, secure, single-command install path before inviting external use.
|
|
11
|
+
|
|
12
|
+
The package has zero runtime npm dependencies (Node.js builtins only), is distributed under AGPL-3.0-or-later, and uses `access: public` in `publishConfig`. The operator publishes manually — no CI/release automation is in scope. This ADR documents the decision to adopt public npm registry publication, the security posture that enables it, the authentication requirements, and the pre-publish verification process.
|
|
13
|
+
|
|
14
|
+
The `@magnusekdahl` scope was verified available on the npm registry (task-1340 CP-0). `@magnusekdahl/parallix` returns 404, confirming the scoped name is unclaimed.
|
|
15
|
+
|
|
16
|
+
## Decision
|
|
17
|
+
|
|
18
|
+
**Adopt public npm registry publication for parallix as `@magnusekdahl/parallix`, alongside the existing local tarball install path.**
|
|
19
|
+
|
|
20
|
+
The operator performs `npm publish` manually from a verified local checkout. The decision is bounded: no CI/release automation, no dist-tag management beyond the `latest` tag. The publish process is manual, repeatable, and documented here. Packages published to the public npm registry are automatically signed with ECDSA registry signatures — no publisher action required.
|
|
21
|
+
|
|
22
|
+
### Authentication requirement
|
|
23
|
+
|
|
24
|
+
Publishing scoped public packages to npm requires one of the following:
|
|
25
|
+
|
|
26
|
+
- **Two-factor authentication (2FA)** enabled on the npm account, or
|
|
27
|
+
- **A granular access token (GAT) with bypass 2FA enabled**
|
|
28
|
+
|
|
29
|
+
Both options are documented in the npm docs for scoped public packages. The operator uses 2FA on their npm account for interactive publishing (`npm publish --access public`), which triggers an OTP prompt. This is the default and recommended approach for manual publishing.
|
|
30
|
+
|
|
31
|
+
An alternative is staged publishing (`npm stage publish` followed by `npm stage approve`), which allows a CI workflow to submit a package to staging without 2FA, then requires 2FA only for the manual approval step. Since parallix has no CI automation, direct publishing is simpler and sufficient.
|
|
32
|
+
|
|
33
|
+
### Security posture enabling this decision
|
|
34
|
+
|
|
35
|
+
- **Zero runtime dependencies:** All functionality uses Node.js built-in modules (`fs`, `path`, `child_process`, `os`, `crypto`, `util`, `events`, `stream`, `buffer`, `assert`). This eliminates supply-chain attack surface from third-party packages.
|
|
36
|
+
- **Explicit `files` allowlist:** `package.json` uses an explicit `files` array as an allowlist, not a blacklist. Test suites, coverage reports, knowledge graph output, session data, workflow caches, and Forgejo local state are all excluded.
|
|
37
|
+
- **Defense-in-depth:** `.npmignore` provides a secondary exclusion layer. The combination ensures operator secrets (`*.local.json`), test fixtures, and development artifacts cannot reach the published tarball.
|
|
38
|
+
- **`access: public` in `publishConfig`:** The package is declared public in the manifest. The `--access public` flag is also passed at publish time for explicitness.
|
|
39
|
+
- **AGPL-3.0-or-later license:** The license ensures users can freely use, modify, and distribute parallix while triggering copyleft obligations on modifications to parallix itself.
|
|
40
|
+
- **ECDSA registry signatures:** Every package published to the public npm registry is automatically signed with ECDSA signatures by the registry. This protects against tampering at registry mirrors or proxies. Consumers can verify with `npm audit signatures` (requires npm CLI ≥8.15.0). Signed packages are the npm industry standard — all major packages have them.
|
|
41
|
+
|
|
42
|
+
### Operational procedures (derived from this decision)
|
|
43
|
+
|
|
44
|
+
The following procedures are consequences of this decision, not decisions themselves:
|
|
45
|
+
|
|
46
|
+
1. **Pre-publish verification:** `npm pack --dry-run` inspects the file listing before each publish. The operator verifies all exclusion patterns are absent.
|
|
47
|
+
2. **Manual publish sequence:** Clean working tree → version check → `npm pack --dry-run` → `npm publish --access public` → verify → git tag.
|
|
48
|
+
3. **Token security:** npm tokens are stored in `~/.npmrc` only, never committed. Fine-grained tokens with minimal permissions are used.
|
|
49
|
+
4. **Content audit:** An automated grep script checks `npm pack --dry-run` output against known exclusion patterns.
|
|
50
|
+
|
|
51
|
+
These procedures are operational guidance. They are subject to change as the operator gains experience with the publish process. They are not architectural decisions.
|
|
52
|
+
|
|
53
|
+
## Decision matrix
|
|
54
|
+
|
|
55
|
+
| Option | Summary | Benefits | Risks / Costs | Fit to constraints | Decision |
|
|
56
|
+
|--------|---------|----------|---------------|--------------------|----------|
|
|
57
|
+
| A: Public npm registry | One command: `npm install -g @magnusekdahl/parallix` | Shortest install; matches public repo expectations | Operator token risk managed by 2FA; npm permanence | Matches ADR 0044's zero-dependency stance; public repo warrants public install path | **Accept** |
|
|
58
|
+
| B: Private npm scope first, then public | Same install after switch | Initial publish is invisible; allows verification | Two publish cycles; potential version confusion | No concrete security concern justifies extra step | Defer — only if a concrete security concern emerges |
|
|
59
|
+
| C: Continue tarball-only | Two commands: `npm pack && npm install -g ./magnus-parallix-*.tgz` | Maximum operator control; no registry involvement | Higher friction; does not meet credibility bar for public repo | Consistent with ADR 0044 but inferior UX for public tool | Reject as primary — tarball remains a valid secondary path |
|
|
60
|
+
| D: CI/CD automated publish | Fully automated pipeline | Repeatable; can include automated checks | CI credential risk; infrastructure to maintain; out of scope | Operator explicitly requested manual publish | Reject for now — revisit when cadence justifies automation |
|
|
61
|
+
|
|
62
|
+
## Consequences
|
|
63
|
+
|
|
64
|
+
### Positive consequences
|
|
65
|
+
|
|
66
|
+
- **Credible public install path:** Users can install with `npm install -g @magnusekdahl/parallix` — the shortest possible install, matching expectations for a public Node tool.
|
|
67
|
+
- **Tarball path preserved:** Local tarball install (`npm pack && npm install -g ./magnus-parallix-*.tgz`) remains valid and documented. Operators who prefer it can continue using it.
|
|
68
|
+
- **Supply-chain transparency:** Zero dependencies means `npm audit --production` reports zero vulnerabilities. No hidden third-party code to vet.
|
|
69
|
+
- **Manual publish discipline:** The operator's hands-on publish process is a feature, not a bug — it forces a deliberate verification step before every release.
|
|
70
|
+
- **Rollback awareness:** The ADR documents npm's unpublish constraints (72-hour window for unpublishing; deprecation for older versions) and provides mitigation strategies (conservative semver, version bumping).
|
|
71
|
+
|
|
72
|
+
### Negative consequences
|
|
73
|
+
|
|
74
|
+
- **npm permanence:** Once published, a version cannot be unpublished if >72 hours old or if it has more than 3 dependents. Beyond that window, deprecation is the only option. Prevention (careful `npm pack --dry-run`) is the only reliable rollback.
|
|
75
|
+
- **Operator token responsibility:** The operator manages npm tokens, 2FA, rotation, and scope. This is a single point of operational risk.
|
|
76
|
+
- **Namespace reservation:** The `@magnusekdahl` scope is now associated with a published package. If the operator abandons parallix, the scope becomes orphaned on npm.
|
|
77
|
+
- **Scope creep risk:** Documenting the publish process here invites future requests to add CI automation, npm provenance (Sigstore), or dist-tag management. These are separate decisions that require their own ADRs.
|
|
78
|
+
- **No version pinning guarantee:** Users installing with `npm install -g @magnusekdahl/parallix` get `latest`. Without a lockfile or version specifier, they may receive unexpected updates.
|
|
79
|
+
|
|
80
|
+
## Alternatives considered
|
|
81
|
+
|
|
82
|
+
### Private registry publication first (Option B in matrix)
|
|
83
|
+
|
|
84
|
+
Positive: Initial publish is invisible to the public; allows verification of the package without exposing it to accidental installs.
|
|
85
|
+
|
|
86
|
+
Negative: Requires two publish cycles (restricted → public). Adds confusion about which version is "the" published version. No concrete security concern justifies the extra step.
|
|
87
|
+
|
|
88
|
+
Assessment: Defer until a specific threat scenario emerges that makes a private-first publish worthwhile.
|
|
89
|
+
|
|
90
|
+
### Continue tarball-only distribution (Option C in matrix)
|
|
91
|
+
|
|
92
|
+
Positive: Maximum operator control. No registry involvement. Consistent with ADR 0044's current stance.
|
|
93
|
+
|
|
94
|
+
Negative: Does not meet the credibility bar for a public repo. Two-command install is friction compared to one-command expectation. External contributors cannot easily test the published package.
|
|
95
|
+
|
|
96
|
+
Assessment: Tarball remains a valid secondary install path for operators who prefer it, but should not be the primary documented path for a public tool.
|
|
97
|
+
|
|
98
|
+
### CI/CD automated publish (Option D in matrix)
|
|
99
|
+
|
|
100
|
+
Positive: Eliminates manual steps. Consistent publish process. Can include automated checks (audit, pack verification) in the pipeline.
|
|
101
|
+
|
|
102
|
+
Negative: Introduces CI credential management. Adds infrastructure to maintain. Out of scope for the operator's stated preference for manual publish.
|
|
103
|
+
|
|
104
|
+
Assessment: Revisit when publication cadence justifies automation (e.g., frequent patch releases, multiple maintainers).
|
|
105
|
+
|
|
106
|
+
### Staged publishing
|
|
107
|
+
|
|
108
|
+
Positive: Allows CI workflows to submit packages to staging without 2FA; requires 2FA only for manual approval. Provides an intermediate review step.
|
|
109
|
+
|
|
110
|
+
Negative: Adds complexity for a solo-maintainer project with no CI. The operator's manual publish sequence already includes a verification step (`npm pack --dry-run`).
|
|
111
|
+
|
|
112
|
+
Assessment: Not needed for current workflow. Can be adopted later if CI is introduced or if the operator wants an extra review gate.
|
|
113
|
+
|
|
114
|
+
### Scoped package name alternatives
|
|
115
|
+
|
|
116
|
+
Positive: Alternative scopes (`@parallix/parallix`, `@px-cli/parallix`) would decouple the package from the operator's personal npm identity.
|
|
117
|
+
|
|
118
|
+
Negative: `@magnusekdahl` scope is confirmed available and unclaimed. The operator is the sole maintainer. An organizational scope would require creating a new npm org, which is unnecessary overhead.
|
|
119
|
+
|
|
120
|
+
Assessment: `@magnusekdahl/parallix` is the correct scope for a solo-maintainer package. Revisit if a team or organization assumes maintenance.
|
|
121
|
+
|
|
122
|
+
## Links
|
|
123
|
+
|
|
124
|
+
- ADR 0044: Workflow Distribution Model — established the local npm tarball / global `px` install stance; deferred registry publication
|
|
125
|
+
- ADR 0037: AI Workflow Coordination Architecture — established the `workflow/` directory as the coordination CLI
|
|
126
|
+
- task-1340: make parallix publishable — this ADR's originating task
|
|
127
|
+
- task-1340 CP-0: npm scope availability verified
|
|
128
|
+
- `package.json` — package metadata, `files` allowlist, `publishConfig.access`
|
|
129
|
+
- `parallix/.npmignore` — secondary exclusion layer
|
|
130
|
+
- npm docs: Creating and publishing scoped public packages — https://docs.npmjs.com/creating-and-publishing-scoped-public-packages
|
|
131
|
+
- npm docs: Unpublishing packages from the registry — https://docs.npmjs.com/unpublishing-packages-from-the-registry
|
|
132
|
+
- npm docs: Requiring 2FA for package publishing — https://docs.npmjs.com/requiring-2fa-for-package-publishing-and-settings-modification
|
|
133
|
+
- npm docs: Staged publishing — https://docs.npmjs.com/staged-publishing
|
|
134
|
+
- npm docs: About ECDSA registry signatures — https://docs.npmjs.com/about-registry-signatures
|
|
135
|
+
- npm docs: Verifying ECDSA registry signatures — https://docs.npmjs.com/verifying-registry-signatures
|
|
136
|
+
- npm docs: Threats and Mitigations — https://docs.npmjs.com/threats-and-mitigations
|
|
137
|
+
|
|
138
|
+
(End of file - total 130 lines)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Architecture Decision Records (ADRs)
|
|
2
|
+
|
|
3
|
+
This repository carries the parallix-owned ADRs migrated out of WrGroceries.
|
|
4
|
+
ADR 0023 remains in WrGroceries and is cross-referenced here instead of copied.
|
|
5
|
+
|
|
6
|
+
## Index
|
|
7
|
+
- `docs/adr/0031-ai-agent-instruction-boundary-and-command-floor.md` — Workflow-security decision: classify authoritative instruction sources explicitly and accept a bounded autonomy tradeoff for broader Claude local scripting
|
|
8
|
+
- `docs/adr/0032-mission-refinement-state-and-usage-budget-signals.md` — Add `refined` as the pre-activation mission state and keep `% usage limit` selection signals in `MISSION.md`
|
|
9
|
+
- `docs/adr/0034-module-and-skill-invocation-model.md` — Module/skill invocation model: phase-bound baseline, explicit rule-based self-invocation, and mandatory validation-module loading
|
|
10
|
+
- `docs/adr/0036-mission-sizing-and-dependency-wave-heuristics.md` — Mission sizing tracks, "Too Large" thresholds, and dependency-wave heuristics
|
|
11
|
+
- `docs/adr/0037-ai-workflow-coordination-architecture.md` — Node.js `workflow/` package as repo-adapted coordination CLI for startup, checkpoint, and session handoff; replaces agent-followed documented procedures
|
|
12
|
+
- `docs/adr/0041-integration-pipeline-gates.md` — Integration-time pipeline gates + per-area gate dehallucination: add staging-deploy + e2e gates before squash-merge, driven by repo-side config; remove hallucinated stage-e2e from `gate_web`
|
|
13
|
+
- `docs/adr/0042-workflow-cli-color-rendering-approach.md` — Replace hand-rolled ANSI palette and buggy `useColor()` with Node.js built-in `util.styleText`; zero-dep color detection that handles NO_COLOR/FORCE_COLOR/TERM/TTY correctly
|
|
14
|
+
- `docs/adr/0043-git-target-resolution-strategy.md` — Local-first git target resolution for workflow branch ancestry, with explicit invariants that keep rebase and integrate aligned
|
|
15
|
+
- `docs/adr/0044-workflow-distribution-model.md` — parallix productization path with `px` naming, runtime/target-state boundary, and a 5-alternative decision matrix; Accepted (2026-06-22, task-1331) on the near-term local npm tarball / global `px` install stance
|
|
16
|
+
- `docs/adr/0045-parallax-branch-model.md` — Two integration modes (trunk-based and feature-branch), Forgejo as PR viewer only, `review` remote wiring, and branch naming conventions
|
|
17
|
+
- `docs/adr/0046-npm-publish-process-and-security.md` — Adopt public npm registry publication for `@magnusekdahl/parallix` alongside the local tarball path; zero-dependency security posture, manual publish process, and rollback considerations
|
|
18
|
+
|
|
19
|
+
## Cross-reference
|
|
20
|
+
- `docs/adr/0023-ai-sdlc-configuration.md` remains in WrGroceries at `/home/magnus/code/visualBoard-task-1302/docs/adr/0023-ai-sdlc-configuration.md`.
|
package/docs/agents.md
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# parallix Agent Launcher — Supported Matrix and Policy
|
|
2
|
+
|
|
3
|
+
## Supported Agent Families (this workstation, 2026-04-11)
|
|
4
|
+
|
|
5
|
+
| Agent | Launcher |
|
|
6
|
+
|---------|----------|
|
|
7
|
+
| codex | `codex` |
|
|
8
|
+
| claude | `claude` |
|
|
9
|
+
| mistral | `vibe` |
|
|
10
|
+
| qwen | `opencode` |
|
|
11
|
+
|
|
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
|
+
|
|
14
|
+
## Tool Calling Workaround (qwen/opencode)
|
|
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:
|
|
17
|
+
|
|
18
|
+
- **Prefer sequential tool calls** over parallel calls for dependent operations — if tool B needs output from tool A, call them separately.
|
|
19
|
+
- **Use `workdir` parameter instead of `cd` chains** — avoid `cd <dir> && command` patterns; always use `workdir` for directory changes.
|
|
20
|
+
- **Keep bash commands simple** — prefer separate simple commands over complex one-liners with pipes and conditionals.
|
|
21
|
+
- **If a tool call times out or returns empty**, retry once before escalating.
|
|
22
|
+
|
|
23
|
+
## Non-interactive invocation shapes
|
|
24
|
+
|
|
25
|
+
| Agent | Invocation shape |
|
|
26
|
+
|---------|----------------------------------------------------------------------|
|
|
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
|
+
| 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
|
+
| 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 |
|
|
31
|
+
|
|
32
|
+
## Launch output watchdog
|
|
33
|
+
|
|
34
|
+
All workflow agent launches use the shared `startAgent` path and tee child stdout/stderr through the parent terminal. If the child process stays running but produces no stdout or stderr, the harness emits a bounded status line after a configurable delay and then once per that interval until output arrives or the process exits.
|
|
35
|
+
|
|
36
|
+
### Default watchdog timings
|
|
37
|
+
|
|
38
|
+
| Step | Initial delay |Interval |
|
|
39
|
+
|------|--------------|----------|
|
|
40
|
+
| `draft` | 15 seconds | 30 seconds |
|
|
41
|
+
| All other steps | 60 seconds | 60 seconds |
|
|
42
|
+
|
|
43
|
+
Draft uses shorter defaults so the operator can distinguish agent startup from a hang more quickly during the mission entrypoint (see `DRAFT_NO_OUTPUT_INITIAL_DELAY_MS` and `DRAFT_NO_OUTPUT_INTERVAL_MS` in `parallix/lib/agents/agents.js:54-55`). Other steps retain the generic 60-second defaults (`DEFAULT_NO_OUTPUT_INITIAL_DELAY_MS` and `DEFAULT_NO_OUTPUT_INTERVAL_MS` at `parallix/lib/agents/agents.js:52-53`).
|
|
44
|
+
|
|
45
|
+
### Override environment variables
|
|
46
|
+
|
|
47
|
+
Per-step timing can be overridden via environment variables:
|
|
48
|
+
|
|
49
|
+
| Variable | Applies to | Default effect |
|
|
50
|
+
|----------|-----------|----------------|
|
|
51
|
+
| `WORKFLOW_AGENT_NO_OUTPUT_INITIAL_MS` / `WORKFLOW_AGENT_NO_OUTPUT_INTERVAL_MS` | All steps | Fallback generic watchdog |
|
|
52
|
+
| `WORKFLOW_DRAFT_AGENT_NO_OUTPUT_INITIAL_MS` / `WORKFLOW_DRAFT_AGENT_NO_OUTPUT_INTERVAL_MS` | `draft` step only | Overrides the 15s/30s draft defaults |
|
|
53
|
+
|
|
54
|
+
Set `WORKFLOW_AGENT_NO_OUTPUT_WATCHDOG=0` to disable the watchdog entirely for a single command when investigating output interleaving.
|
|
55
|
+
|
|
56
|
+
Passing explicit `noOutputWatchdog: { initialDelayMs, intervalMs }` to `startAgent` bypasses both the env vars and the step defaults.
|
|
57
|
+
|
|
58
|
+
### Watchdog message format
|
|
59
|
+
|
|
60
|
+
When the watchdog fires, the harness emits:
|
|
61
|
+
|
|
62
|
+
```text
|
|
63
|
+
[INFO] No output yet from <agent> for step "<step>" after <elapsed> (pid <pid>, agent <stage>). Launcher is still running; stdout/stderr have not produced visible output.
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The `<stage>` field is `"starting up"` when fewer than the step-specific initial delay have elapsed, and `"running"` once that threshold is crossed (`agents.js:736-738`). For draft, the threshold is 15 seconds; for all other steps it is 60 seconds.
|
|
67
|
+
|
|
68
|
+
This means the launcher process is alive but silent. It is not a launch failure by itself. A launch failure is still reported separately when the process cannot start, exits non-zero, is killed by a signal, or produces a detected usage-limit transcript that triggers fallback.
|
|
69
|
+
|
|
70
|
+
## Per-step eligibility policy
|
|
71
|
+
|
|
72
|
+
Eligibility is controlled by `parallix/config/agents.json`. The default config controls which agents are eligible for each step:
|
|
73
|
+
|
|
74
|
+
```json
|
|
75
|
+
{
|
|
76
|
+
"steps": {
|
|
77
|
+
"draft": { "eligible": ["codex", "qwen", "mistral"], "selection": "random" },
|
|
78
|
+
"active": { "eligible": ["codex", "claude", "qwen", "mistral"], "selection": "random" },
|
|
79
|
+
"conflict-resolution": { "eligible": ["claude", "codex", "mistral"], "selection": "random" },
|
|
80
|
+
"review": { "eligible": ["codex", "claude", "qwen", "mistral"], "selection": "random" }
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Note: these examples match the actual `parallix/config/agents.json` that controls eligibility at runtime. The executable config is the source of truth; any mismatch between the docs and the config is a bug.
|
|
86
|
+
|
|
87
|
+
To restrict a step to a specific agent, edit the `eligible` array. To force a specific agent for the current command invocation, use the CLI flags:
|
|
88
|
+
|
|
89
|
+
```sh
|
|
90
|
+
px draft task-XXX --agent codex
|
|
91
|
+
px active task-XXX --implementer claude
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
CLI flags (`--agent`, `--implementer`) take precedence over both the config and random selection. They are the preferred mechanism for operator override. As a fallback, the `WORKFLOW_AGENT` environment variable is also supported:
|
|
95
|
+
|
|
96
|
+
```sh
|
|
97
|
+
WORKFLOW_AGENT=codex px draft task-XXX
|
|
98
|
+
WORKFLOW_AGENT=claude px active task-XXX
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Local blocklist overrides
|
|
102
|
+
|
|
103
|
+
Temporary local blocking uses the operator-owned
|
|
104
|
+
`<PARALLIX_HOME>/agents.local.json`. `PARALLIX_HOME` is the highest-priority
|
|
105
|
+
whole-root override. Platform defaults are `~/.local/state/parallix` on Linux,
|
|
106
|
+
`~/Library/Application Support/parallix` on macOS, and `%LOCALAPPDATA%\parallix`
|
|
107
|
+
on Windows, with `~/.parallix` as the fallback.
|
|
108
|
+
|
|
109
|
+
On first use, if the effective file is absent, parallix migrates these legacy
|
|
110
|
+
locations in order, with later values taking precedence:
|
|
111
|
+
|
|
112
|
+
1. `parallix/config/agents.local.json`
|
|
113
|
+
2. repo-root `agents.local.json`
|
|
114
|
+
3. main-worktree `agents.local.json`
|
|
115
|
+
|
|
116
|
+
Migration never deletes a legacy file. Conflicting values are logged with the
|
|
117
|
+
selected and previous source/value. Malformed legacy files are reported and
|
|
118
|
+
skipped; malformed effective JSON is a hard failure and is left byte-unchanged.
|
|
119
|
+
|
|
120
|
+
Supported shape:
|
|
121
|
+
|
|
122
|
+
```json
|
|
123
|
+
{
|
|
124
|
+
"blocklist": {
|
|
125
|
+
"claude": { "until": "2026-05-02 09" },
|
|
126
|
+
"codex": { "blocked": false }
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Per-agent values:
|
|
132
|
+
- `true` or `{ "blocked": true }`: permanently blocked
|
|
133
|
+
- `false` or `{ "blocked": false }`: explicitly unblocked
|
|
134
|
+
- `{ "until": "YYYY-MM-DD HH" }`: blocked until that local-machine hour passes
|
|
135
|
+
|
|
136
|
+
Malformed workflow or local agent JSON is a hard failure with the file path and parse error. Blocked agents are removed before launcher probing and random selection.
|
|
137
|
+
|
|
138
|
+
## Usage-limit handling — automatic fallback (TASK-1013)
|
|
139
|
+
|
|
140
|
+
The workflow detects limit-hit messages in agent stdout/stderr and automatically reroutes to another eligible agent.
|
|
141
|
+
|
|
142
|
+
### Detection
|
|
143
|
+
|
|
144
|
+
`parallix/lib/limit-hit.js` ships a regex catalog per agent family (`claude`, `codex`, `qwen`, `mistral`) covering the common shapes:
|
|
145
|
+
- explicit phrases like `Claude usage limit reached`, `weekly limit`, `Quota exceeded`, `RESOURCE_EXHAUSTED`
|
|
146
|
+
- HTTP signals (`429 Too Many Requests`, `rate_limit_exceeded`, `Retry-After: ...`)
|
|
147
|
+
|
|
148
|
+
When a pattern matches, the parser also tries to extract a reset timestamp from the surrounding context:
|
|
149
|
+
1. ISO 8601 (`2026-05-01T15:30:00+00:00`)
|
|
150
|
+
2. 12-hour clock (`5pm`, `5:30 PM`) projected onto today/tomorrow
|
|
151
|
+
3. 24-hour clock (`17:00`)
|
|
152
|
+
4. Relative (`in 3 hours`)
|
|
153
|
+
5. `Retry-After: <n>` seconds/minutes
|
|
154
|
+
|
|
155
|
+
If extraction fails, the harness falls back to a 1-hour block. The result is rounded **up** to the next full hour and written as `YYYY-MM-DD HH` (the format `agents.local.json` already understands).
|
|
156
|
+
|
|
157
|
+
### Persistence
|
|
158
|
+
|
|
159
|
+
`updateAgentBlock(agent, until)` writes the timed entry to
|
|
160
|
+
`<PARALLIX_HOME>/agents.local.json`. Automatically persisted usage-limit blocks
|
|
161
|
+
therefore apply across every target repository driven by the same parallix
|
|
162
|
+
installation. Parent directories and a valid `{"blocklist": {}}` shape are
|
|
163
|
+
created on first write.
|
|
164
|
+
|
|
165
|
+
If the target file is already malformed (invalid JSON or a non-object root), `updateAgentBlock` raises a `WORKFLOW_AGENT_CONFIG_INVALID` error with the file path and original parse error and leaves the file untouched, matching the read-path contract. A limit hit must never silently overwrite a corrupted blocklist.
|
|
166
|
+
|
|
167
|
+
### Retry loop
|
|
168
|
+
|
|
169
|
+
`startAgent(step, opts)` is now async. It:
|
|
170
|
+
1. Picks an agent (honoring `WORKFLOW_AGENT`, the eligibility config, and the existing blocklist).
|
|
171
|
+
2. Spawns the launcher with a tee — output is mirrored to the user's terminal **and** captured to a bounded in-memory tail buffer (`DEFAULT_MAX_TAIL_BYTES`, currently 64 KiB per stream). Long-running, noisy agents cannot turn the harness into an `O(total output)` memory hog.
|
|
172
|
+
3. Runs `detectLimitHit` on the captured tail **only when the launcher actually failed** (non-zero exit, signal, or spawn error). A successful child run is never treated as a limit hit, even if the transcript happens to quote a phrase like "rate limit reached" — that path is the false-positive case where an agent reviews code, tests, or logs containing those strings.
|
|
173
|
+
4. On a hit, persists the block, records the agent as tried, and reselects from the remaining eligible+supported pool.
|
|
174
|
+
5. If every eligible agent hits a limit, the harness throws a clear "all eligible agents exhausted" error rather than silently retrying forever.
|
|
175
|
+
|
|
176
|
+
When the autonomous review loop rewrites `review-state.json` after a fallback (see `applyAgentFallback` in `parallix/lib/review/review.js`), it preserves the original `roundStartedAt` rather than stamping the rewrite time. Polling for the round's reviewer/disposition outcome uses `roundStartedAt` as the lower bound, so a crash between the fallback rewrite and pollFor* completing must not advance that bound past comments the fallback agent has already posted.
|
|
177
|
+
|
|
178
|
+
`WORKFLOW_AGENT` overrides survive their first attempt; if the pinned agent itself hits a limit, the retry falls back to normal selection (excluding the blocked one). A `WORKFLOW_AGENT` value that is already hard-blocked in `agents.local.json` (or excluded by step eligibility) is ignored at selection time and the harness falls back to the regular pool — overrides are honored *alongside* the blocklist, never *around* it.
|
|
179
|
+
|
|
180
|
+
The same rule applies to explicit `agent:` overrides passed into `startAgent({ agent })` — for example, the reviewer/implementer identities the autonomous review loop carries over from `review-state.json`. Before launching, `startAgent` consults the merged blocklist; if the pinned agent is currently blocked, it logs a warning, adds it to the tried-set, and reroutes through `selectAgent` instead of wasting a retry on a known-limited family.
|
|
181
|
+
|
|
182
|
+
### Active-step Backlog state-ordering contract
|
|
183
|
+
|
|
184
|
+
`px active` enforces this state order:
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
selectAgent (blocklist applied) → startAgent (launch) → record Backlog (status=active, assignee)
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
The Backlog task is moved to `active` with the correct implementer **only after** the launch exits cleanly (status 0, no error). A failed or exhausted launch leaves the task in its prior Backlog state — it does not record a wrong implementer or a misleading `active` status.
|
|
191
|
+
|
|
192
|
+
When `startAgent` falls back to a different agent family after a limit hit, the Backlog records the fallback family (the agent that actually ran), not the originally preselected one.
|
|
193
|
+
|
|
194
|
+
### Automatic post-execute handoff repair (TASK-1037)
|
|
195
|
+
|
|
196
|
+
The workflow harness automatically recovers from routine handoff hygiene issues after a successful execute-agent exit. When `px active <slug>` completes its execution phase, it attempts automated handoff. If handoff fails, the harness runs a repair step and retries exactly once before declaring failure.
|
|
197
|
+
|
|
198
|
+
#### Repaired conditions:
|
|
199
|
+
1. **Uncommitted mission artifacts**: If the worktree is dirty but only mission-owned files (the configured mission base dir for the repo, `backlog/tasks/<slug> - *`, or `backlog/completed/<slug> - *`) are modified, the harness automatically commits them with a deterministic message: `workflow(<slug>): auto-commit mission artifacts before handoff`.
|
|
200
|
+
2. **Branch behind primary branch (main)**: If the handoff fails because the branch is behind its remote (non-fast-forward), the harness automatically invokes `px rebase <slug>`.
|
|
201
|
+
|
|
202
|
+
#### Hard blockers:
|
|
203
|
+
Automatic repair is refused and the harness stops if:
|
|
204
|
+
- Dirty files include paths outside the mission-owned set.
|
|
205
|
+
- Rebase requires manual conflict resolution or agent assistance for shared files.
|
|
206
|
+
- Handoff fails for non-hygiene reasons (missing checkpoints, missing `## Goal Check` evidence, failed verification gates).
|
|
207
|
+
|
|
208
|
+
### Manual override is still supported
|
|
209
|
+
|
|
210
|
+
- `WORKFLOW_AGENT=<name>` — pin a specific agent for the next run.
|
|
211
|
+
- `parallix/config/agents.json` — restrict step eligibility.
|
|
212
|
+
- Edit `<PARALLIX_HOME>/agents.local.json` by hand to add or clear blocks; format remains `{"blocklist": {"agent": {"until": "YYYY-MM-DD HH"}}}`. Back up `PARALLIX_HOME` separately from target repositories and npm package backups.
|