@magnusekdahl/parallix 1.3.1 → 1.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/config/integration-pipelines.json +5 -0
- package/docs/adr/0048-fail-closed-harness-defense-against-agent-hallucinations.md +161 -0
- package/docs/adr/index.md +2 -0
- package/docs/authority-reference.md +8 -5
- package/lib/agents/agents.js +3 -1
- package/lib/agents/agents.ts +3 -1
- package/lib/agents/claude.js +3 -1
- package/lib/agents/claude.ts +3 -1
- package/lib/agents/codex.js +3 -1
- package/lib/agents/codex.ts +3 -1
- package/lib/agents/mistral-telemetry.js +122 -23
- package/lib/agents/mistral-telemetry.ts +141 -26
- package/lib/agents/mistral.js +1 -1
- package/lib/agents/mistral.ts +1 -1
- package/lib/agents/opencode.js +5 -3
- package/lib/agents/opencode.ts +5 -3
- package/lib/commands/active.js +21 -3
- package/lib/commands/active.ts +7 -2
- package/lib/commands/config.js +6 -1
- package/lib/commands/config.ts +6 -1
- package/lib/commands/coverage-gate.js +19 -1
- package/lib/commands/coverage-gate.ts +6 -1
- package/lib/commands/diff.js +6 -1
- package/lib/commands/diff.ts +6 -1
- package/lib/commands/draft.js +31 -1
- package/lib/commands/draft.ts +6 -1
- package/lib/commands/handoff.js +12 -1
- package/lib/commands/handoff.ts +6 -1
- package/lib/commands/integrate.js +103 -27
- package/lib/commands/integrate.ts +67 -31
- package/lib/commands/mission-start.js +12 -3
- package/lib/commands/mission-start.ts +7 -2
- package/lib/commands/rebase.js +10 -2
- package/lib/commands/rebase.ts +6 -2
- package/lib/commands/repair-handoff.js +13 -3
- package/lib/commands/repair-handoff.ts +7 -2
- package/lib/commands/resolve-conflict.js +8 -2
- package/lib/commands/resolve-conflict.ts +7 -2
- package/lib/commands/review.js +6 -1
- package/lib/commands/review.ts +6 -1
- package/lib/commands/setup-review.js +8 -3
- package/lib/commands/setup-review.ts +8 -3
- package/lib/commands/setup.js +8 -2
- package/lib/commands/setup.ts +7 -2
- package/lib/commands/stats-backfill.js +14 -3
- package/lib/commands/stats-backfill.ts +7 -2
- package/lib/commands/stats.js +33 -1
- package/lib/commands/stats.ts +7 -2
- package/lib/commands/status.js +8 -1
- package/lib/commands/status.ts +6 -1
- package/lib/commands/verify.js +11 -2
- package/lib/commands/verify.ts +7 -2
- package/lib/core/gitignore.js +9 -1
- package/lib/core/gitignore.ts +6 -1
- package/lib/core/persistent-data-migration.js +4 -2
- package/lib/core/persistent-data-migration.ts +4 -2
- package/lib/index.js +36 -36
- package/lib/index.ts +18 -18
- package/lib/review/review-loop.js +12 -7
- package/lib/review/review-loop.ts +10 -7
- package/lib/review/review.js +51 -1
- package/lib/review/review.ts +6 -1
- package/lib/tools/setup-review.js +7 -4
- package/lib/tools/setup-review.ts +2 -2
- package/package.json +1 -1
- package/px.js +30 -14
package/README.md
CHANGED
|
@@ -145,11 +145,13 @@ px active task-042
|
|
|
145
145
|
|
|
146
146
|
# Land it: runs configured integration gates, squash-merges to
|
|
147
147
|
# the primary branch, updates board state, removes the branch
|
|
148
|
-
# and worktree.
|
|
148
|
+
# and worktree. In this repo that means a fast general verifier
|
|
149
|
+
# during earlier phases and a stricter lifecycle E2E gate before
|
|
150
|
+
# integrate lands.
|
|
149
151
|
px integrate task-042
|
|
150
152
|
```
|
|
151
153
|
|
|
152
|
-
The verification gate that runs at each
|
|
154
|
+
The verification gate that runs at each phase is whatever you declare in `workflow.config.json`. In this repo that dispatcher is `./scripts/verify-local.sh {{area}}`: earlier phases use the fast general suite, while `px integrate` calls `verify-local.sh integrate`, which resolves repo-side integration gates from `config/integration-pipelines.json` and runs the stricter pre-merge checks there.
|
|
153
155
|
|
|
154
156
|
## Use cases
|
|
155
157
|
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# ADR 0048: Fail-closed harness defense against agent hallucinations
|
|
2
|
+
|
|
3
|
+
Status: Accepted
|
|
4
|
+
Date: 2026-06-29
|
|
5
|
+
|
|
6
|
+
Related: ADR 0041 (integration pipeline gates), ADR 0047 (NEL budget), task-1268 (shift-left verification), task-1335 (harden publish path)
|
|
7
|
+
|
|
8
|
+
## Context
|
|
9
|
+
|
|
10
|
+
Parallix already has meaningful defenses against agent hallucinations and incomplete work, but they are fragmented across commands and missions rather than expressed as one coherent harness policy. The repo has exact-tree verification proof (task-1335), handoff pre-checks, gatekeeper mandatory-artifact checks (gatekeeper.js), integration-time gates (ADR 0041), and a narrow auto-repair path (repair-handoff.js). It also has open work on shift-left verification (task-1268).
|
|
11
|
+
|
|
12
|
+
What it does not yet have is one explicit answer to a repository-level question: **given a failed or incomplete agent handoff, which failure classes should auto-repair, which should auto-send-back to the implementer, and which genuinely require human intervention?**
|
|
13
|
+
|
|
14
|
+
The current `repair-handoff.js` handles only two mechanical error classes (dirty mission artifacts, branch behind primary) and one relaunchable content error (empty goal-check table). All other failures — including genuine gate failures on code issues, the single largest consumer of human time — strand with a generic "not automatically repairable" message that requires manual re-invocation.
|
|
15
|
+
|
|
16
|
+
This ADR consolidates the existing evidence, classifies the failure modes, and recommends a fully backlog-tracked implementation plan for fail-closed harness behavior. Nothing in the recommended control set is left as an untracked "defer later" idea: every control gets an explicit backlog task, even when the runtime outcome remains "human required".
|
|
17
|
+
|
|
18
|
+
## Inputs
|
|
19
|
+
|
|
20
|
+
This decision draws on four prior Parallix artifacts:
|
|
21
|
+
|
|
22
|
+
1. **Task-1268** (`backlog/tasks/task-1268 - Shift-left.md`): Shift-left verification concept — run the verification gate mechanically before each review round, auto-bounce on failure without consuming a reviewer cycle. Key insight: the machinery exists (`captureVerifiedTreeProof` / `assertVerifiedTreeProof` in `lib/core/verification.js`), but enforcement before review rounds is missing.
|
|
23
|
+
|
|
24
|
+
2. **Task-1335** (`backlog/completed/task-1335 - Harden-parallix-self-hosting-publish-path...md`): Exact-tree verification proof, implemented and completed. Established the principle that a verification proof must be tied to the exact tree being published — a green run from a different checkout, commit, or pre-squash state cannot satisfy the guard.
|
|
25
|
+
|
|
26
|
+
3. **ADR 0041** (`docs/adr/0041-integration-pipeline-gates.md`): Integration-time pipeline gates with per-area dispatch, config-driven gate plan, and `--no-integration-gates` escape hatch. Established the pattern of gate execution before squash-merge.
|
|
27
|
+
|
|
28
|
+
4. **ADR 0047** (`docs/adr/0047-per-mission-change-size-budget.md`): NEL budget with observational capture at handoff. Demonstrates the pattern of observational instrumentation without enforcement — a template for what NOT to do when enforcement is needed.
|
|
29
|
+
|
|
30
|
+
## Inventory of Existing Checks
|
|
31
|
+
|
|
32
|
+
The following checks are currently implemented across the harness lifecycle. Each is mapped to the failure class it catches.
|
|
33
|
+
|
|
34
|
+
### Before Handoff
|
|
35
|
+
|
|
36
|
+
| # | Check | Location | Failure Class |
|
|
37
|
+
|---|-------|----------|---------------|
|
|
38
|
+
| 1 | Verification gate at checkpoint | `lib/commands/checkpoint.js:44` | Unverifiable test claims (Class 1) |
|
|
39
|
+
| 2 | Checkpoint existence validation | `lib/commands/active.js:386-391` | Missing artifacts (Class 3) |
|
|
40
|
+
| 3 | Checkpoint committed check | `lib/commands/active.js:407-414` | Uncommitted state (Class 4) |
|
|
41
|
+
|
|
42
|
+
### During Handoff
|
|
43
|
+
|
|
44
|
+
| # | Check | Location | Failure Class |
|
|
45
|
+
|---|-------|----------|---------------|
|
|
46
|
+
| 4 | Mission branch verification | `lib/commands/handoff.js:37-39` | Git blockers (Class 5) |
|
|
47
|
+
| 5 | MISSION.md existence | `lib/commands/handoff.js:42-44` | Missing artifacts (Class 3) |
|
|
48
|
+
| 6 | MISSION.md uncommitted check | `lib/commands/handoff.js:96-99` | Uncommitted state (Class 4) |
|
|
49
|
+
| 7 | Auto-checkpoint generation | `lib/commands/handoff.js:103-126` | Missing artifacts — auto-repair (Class 3) |
|
|
50
|
+
| 8 | Goal Check heading validation | `lib/commands/handoff.js:141-147` | Incomplete evidence (Class 4) |
|
|
51
|
+
| 9 | Goal Check evidence rows | `lib/commands/handoff.js:152-179` | Incomplete evidence (Class 4) |
|
|
52
|
+
| 10 | Verification gate execution | `lib/commands/handoff.js:200-209` | Gate failure (Class 1, 6) |
|
|
53
|
+
| 11 | Rebase onto primary | `lib/commands/handoff.js:214-229` | Git blockers (Class 5) |
|
|
54
|
+
| 12 | Gatekeeper mandatory artifacts | `lib/commands/handoff.js:322-335` | Missing artifacts (Class 3) |
|
|
55
|
+
| 13 | Declared gates execution | `lib/commands/handoff.js:429-480` | Gate failure (Class 2, 6) |
|
|
56
|
+
|
|
57
|
+
### Before Review
|
|
58
|
+
|
|
59
|
+
| # | Check | Location | Failure Class |
|
|
60
|
+
|---|-------|----------|---------------|
|
|
61
|
+
| 14 | Mission dir + branch + status | `lib/review/review-commands.js:367-401` | State violations (Class 8) |
|
|
62
|
+
| 15 | PR existence and state | `lib/review/review-commands.js:403-430` | Infra blockers (Class 7) |
|
|
63
|
+
| 16 | Verification gate | `lib/review/review-commands.js:438-445` | Gate failure (Class 1, 6) |
|
|
64
|
+
|
|
65
|
+
### During Integration
|
|
66
|
+
|
|
67
|
+
| # | Check | Location | Failure Class |
|
|
68
|
+
|---|-------|----------|---------------|
|
|
69
|
+
| 17 | Integration preflight | `lib/commands/integrate.js:500-504` | Multiple classes |
|
|
70
|
+
| 18 | Integration gates | `lib/commands/integrate.js:507-534` | Gate failure (Class 6) |
|
|
71
|
+
| 19 | Exact-tree proof capture | `lib/commands/integrate.js:742-749` | Unverifiable claims (Class 1) |
|
|
72
|
+
| 20 | Exact-tree proof assertion | `lib/commands/integrate.js:753-757` | Stale proof (Class 1) |
|
|
73
|
+
|
|
74
|
+
### Repair Path
|
|
75
|
+
|
|
76
|
+
| # | Mechanism | Location | Coverage |
|
|
77
|
+
|---|-----------|----------|----------|
|
|
78
|
+
| 21 | Auto-commit mission artifacts | `lib/commands/repair-handoff.js:130-191` | Dirty mission files only (Class 5) |
|
|
79
|
+
| 22 | Auto-rebase | `lib/commands/repair-handoff.js:194-223` | Simple rebase only (Class 5) |
|
|
80
|
+
| 23 | Agent relaunch (empty goal-check) | `lib/commands/active.js:462-483` | Single error sub-class only (Class 4) |
|
|
81
|
+
|
|
82
|
+
**Total: 23 check points across 5 lifecycle phases.**
|
|
83
|
+
|
|
84
|
+
## Failure Classification
|
|
85
|
+
|
|
86
|
+
Eight failure classes have been identified, each classified as auto-repair, auto-send-back, or human-only:
|
|
87
|
+
|
|
88
|
+
| # | Failure Class | Proposed | Rationale |
|
|
89
|
+
|---|---------------|----------|-----------|
|
|
90
|
+
| 1 | Unverifiable "tests passed" claims | **Auto-send-back** | Gate exit code is deterministic; agent prose is never sufficient |
|
|
91
|
+
| 2 | Malformed or non-runnable declared gates | **Auto-repair** | Static validation (file existence, syntax) can catch before execution |
|
|
92
|
+
| 3 | Missing mandatory mission artifacts | **Auto-send-back** | Unambiguously the implementer's responsibility; no human judgment needed |
|
|
93
|
+
| 4 | Incomplete checkpoint evidence | **Auto-send-back** | Agent-fixable content errors; fix prompt already exists for one sub-class |
|
|
94
|
+
| 5 | Mechanical git/handoff blockers | **Auto-repair** (mission-only); **Human-only** (shared files) | Mission conflicts auto-resolvable; shared-file conflicts require judgment |
|
|
95
|
+
| 6 | Genuine gate failure (code issues) | **Auto-send-back** | Gate output sufficient for agent to diagnose; highest-ROI improvement |
|
|
96
|
+
| 7 | Forgejo/infra blockers | **Human-only** | No agent relaunch will fix infrastructure issues |
|
|
97
|
+
| 8 | Task state machine violations | **Human-only** | State confusion requires human determination of correct state |
|
|
98
|
+
|
|
99
|
+
## Decision Matrix: Candidate Harness Controls
|
|
100
|
+
|
|
101
|
+
Seven candidate controls are evaluated and prioritized. The classification column answers backlog treatment, not runtime disposition: every control below is scheduled work with an explicit task.
|
|
102
|
+
|
|
103
|
+
| # | Candidate Control | Complexity | ROI | Risk | Backlog Treatment |
|
|
104
|
+
|---|-------------------|-----------|-----|------|-------------------|
|
|
105
|
+
| C1 | Pre-review-round gate enforcement with auto-bounce | Medium | High | Low — gate machinery already exists; enforcement is the missing piece | **Implement now** (`TASK-1385`) |
|
|
106
|
+
| C2 | Gate-failure auto-send-back with captured output | Low | High | Low — capture stdout/stderr, build fix prompt, relaunch with retry limit | **Implement now** (`TASK-1387`) |
|
|
107
|
+
| C3 | Error classifier and dispatch table replacing generic strand | Medium | Medium | Low — refactor, not new behavior; existing repair-handoff.js is the seam | **Implement now** (`TASK-1389`) |
|
|
108
|
+
| C4 | Declared-gate pre-validation (syntax + file existence) | Low | Medium | Low — static check before execution | **Implement next wave** (`TASK-1386`) |
|
|
109
|
+
| C5 | Gatekeeper auto-send-back with agent relaunch | Low | Medium | Medium — must avoid relaunch loops when artifacts genuinely cannot be created | **Implement next wave** (`TASK-1388`) |
|
|
110
|
+
| C6 | Forgejo/infrastructure blocker classification and operator handoff | Low | Low | Low — mostly classification and operator guidance, but still worth making explicit | **Implement next wave** (`TASK-1392`) |
|
|
111
|
+
| C7 | Pre-review checkpoint evidence reference validation | Medium | Low | Medium — keep the check mechanical and avoid semantic-quality scoring | **Implement next wave** (`TASK-1393`) |
|
|
112
|
+
|
|
113
|
+
### Implement Now (C1, C2, C3)
|
|
114
|
+
|
|
115
|
+
**C1: Pre-review-round gate enforcement** (task-1268 / task-1385). Run the configured verification gate mechanically before each review round. On gate failure, auto-bounce to the implementer with the gate output as a fix prompt. No reviewer cycle consumed. This is the single highest-impact control because it closes the fail-open path where an agent can hand off with a green gate, receive review feedback, "fix" the code, and re-submit without the gate re-running.
|
|
116
|
+
|
|
117
|
+
**C2: Gate-failure auto-send-back** (task-1387). When the verification gate fails at handoff time (`handoff.js:200-209`), capture the gate stdout/stderr, classify the error as "genuine gate failure — code issue", and relaunch the implementer with the captured output. Limit relaunch attempts to 2 to prevent infinite loops. This is the highest-ROI single control because it eliminates the most common human-intervention scenario: manually copying gate output and re-invoking the agent.
|
|
118
|
+
|
|
119
|
+
**C3: Error classifier and dispatch table** (task-1389). Replace the binary `isRelaunchableError` / `isDirtyError` / `isBehind` classification in `repair-handoff.js` with a structured error classifier that maps each error message pattern to a failure class and a dispatch action (auto-repair, auto-send-back with prompt, or human-only with clear message). This is foundational work that makes C1 and C2 cleaner to implement.
|
|
120
|
+
|
|
121
|
+
### Implement Next Wave (C4, C5, C6, C7)
|
|
122
|
+
|
|
123
|
+
**C4: Declared-gate pre-validation** (`TASK-1386`). Validate that gate commands reference existing files and are syntactically valid before executing them. This stays after C1-C3 only because the earlier controls close larger fail-open paths first, not because C4 is optional.
|
|
124
|
+
|
|
125
|
+
**C5: Gatekeeper auto-send-back** (`TASK-1388`). When gatekeeper detects missing mandatory artifacts and the task strands in `active`, auto-send-back to the implementer with explicit artifact creation instructions. The auto-checkpoint generation at `handoff.js:103-126` already covers one sub-case, but the remaining cases still deserve explicit automation and therefore explicit backlog tracking.
|
|
126
|
+
|
|
127
|
+
**C6: Forgejo/infrastructure blocker classification and operator handoff** (`TASK-1392`). Label Forgejo and related infrastructure failures as "infrastructure — human required", preserve the existing runtime outcome, and make the operator message deterministic and actionable. Human-required runtime behavior is still harness work and therefore still gets a backlog task.
|
|
128
|
+
|
|
129
|
+
**C7: Pre-review checkpoint evidence reference validation** (`TASK-1393`). Validate that checkpoint evidence rows cite real file:line references, ADR references, or test names rather than placeholder prose. The task must stay mechanical: it should verify reference shape and existence, not attempt to score evidence quality semantically.
|
|
130
|
+
|
|
131
|
+
## Implementation Order
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
C3 (error classifier) → C2 (gate-failure send-back) → C1 (pre-review gate) → C4 → C5 → C6 → C7
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
C3 first because it provides the dispatch framework that C1 and C2 plug into. C2 before C1 because C2 is lower complexity and higher immediate ROI (handoff-time gate failures are already the blocking point; pre-review-round enforcement adds a new check point). C4-C7 remain sequenced backlog work rather than untracked ideas.
|
|
138
|
+
|
|
139
|
+
## Consequences
|
|
140
|
+
|
|
141
|
+
### Positive
|
|
142
|
+
|
|
143
|
+
- The most common human-intervention scenario (copying gate output and re-invoking agent) is eliminated by C2
|
|
144
|
+
- The fail-open path between review rounds is closed by C1 (task-1268)
|
|
145
|
+
- Error handling moves from binary (repairable / not) to a classified dispatch table (C3)
|
|
146
|
+
- Each failure class has an explicit owner (auto-repair, auto-send-back, or human)
|
|
147
|
+
|
|
148
|
+
### Negative
|
|
149
|
+
|
|
150
|
+
- Automatic relaunching (C2) increases compute cost per failed handoff (bounded by retry limit)
|
|
151
|
+
- The error classifier (C3) adds a maintenance surface — new error patterns must be classified
|
|
152
|
+
- Pre-review gate enforcement (C1) adds wall-time to each review round (bounded by gate duration)
|
|
153
|
+
|
|
154
|
+
## See Also
|
|
155
|
+
|
|
156
|
+
- Task-1268: Shift-left verification concept (backlog)
|
|
157
|
+
- Task-1335: Exact-tree verification proof (completed)
|
|
158
|
+
- ADR 0041: Integration pipeline gates
|
|
159
|
+
- ADR 0047: NEL budget (observational pattern)
|
|
160
|
+
- `lib/commands/repair-handoff.js`: Current repair path
|
|
161
|
+
- `lib/commands/active.js:426-498`: Automated handoff-and-repair flow
|
package/docs/adr/index.md
CHANGED
|
@@ -17,5 +17,7 @@ ADR 0023 remains in WrGroceries and is cross-referenced here instead of copied.
|
|
|
17
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
18
|
- `docs/adr/0047-per-mission-change-size-budget.md` — Change the mission size-estimation basis from agent-usage % to **Net Engineering Lines (NEL)** — code+test diff, excluding docs and workflow/admin bookkeeping (the +0.65 reverse-causation confound). Draft estimate becomes a NEL bucket (0–80 / 81–235 / 235+, the empirical risk terciles); capture actual NEL at handoff to calibrate the estimate. No enforcement until the draft bucket is shown reliable.
|
|
19
19
|
|
|
20
|
+
- `docs/adr/0048-fail-closed-harness-defense-against-agent-hallucinations.md` — Fail-closed harness defense against agent hallucinations: error classification, auto-send-back policy, and prioritized implementation plan for closing fail-open paths in the handoff/review/integrate lifecycle
|
|
21
|
+
|
|
20
22
|
## Cross-reference
|
|
21
23
|
- `docs/adr/0023-ai-sdlc-configuration.md` remains in WrGroceries at `/home/magnus/code/visualBoard-task-1302/docs/adr/0023-ai-sdlc-configuration.md`.
|
|
@@ -153,7 +153,7 @@ Complete when: mission reviewed, landing from the correct integration checkout,
|
|
|
153
153
|
|
|
154
154
|
- **Config location:** `config/integration-pipelines.json`
|
|
155
155
|
- **Schema:** `{"gates": {"<area>": {"command": "<shell-command>", "order": <number>, "run_last": <boolean>}}}`
|
|
156
|
-
- **Supported areas:** `server`, `auth-server`, `web-client`, `web-e2e`
|
|
156
|
+
- **Supported areas:** `lib`, `workflow`, `server`, `auth-server`, `web-client`, `web-e2e`, `docs`, `android`, `kubernetes`
|
|
157
157
|
- **Ordering:** Gates are executed in ascending `order` value; `run_last: true` ensures the gate runs after all others (regardless of order value)
|
|
158
158
|
- **Change detection:** Gates are only invoked for areas with changed files in the mission branch vs the primary branch
|
|
159
159
|
- **Opt-out:** `px integrate <slug> --no-integration-gates` skips all integration gates
|
|
@@ -163,16 +163,19 @@ Example config:
|
|
|
163
163
|
```json
|
|
164
164
|
{
|
|
165
165
|
"gates": {
|
|
166
|
-
"
|
|
167
|
-
"
|
|
168
|
-
"web-client": {"command": "SKIP_E2E=1 ./web-client/updateStaging.sh", "order": 3, "run_last": false},
|
|
169
|
-
"web-e2e": {"command": "./web-client/scripts/run-playwright-stage.sh", "order": 4, "run_last": true}
|
|
166
|
+
"lib": {"command": "./scripts/verify-local.sh static-analysis", "order": 1, "run_last": false},
|
|
167
|
+
"workflow": {"command": "node test/e2e-mission-lifecycle.test.js", "order": 50, "run_last": true}
|
|
170
168
|
}
|
|
171
169
|
}
|
|
172
170
|
```
|
|
173
171
|
|
|
174
172
|
If the config file is missing or empty, `px integrate` logs `integration-gates: no config present, skipping` and proceeds without error.
|
|
175
173
|
|
|
174
|
+
In this repo, `workflow.config.json` points verification at `./scripts/verify-local.sh {{area}}`. That gives Parallix two validation layers:
|
|
175
|
+
|
|
176
|
+
- earlier phases such as draft/active/review run the repo's fast general verifier (`all`, currently `npm test`)
|
|
177
|
+
- `px integrate` invokes `verify-local.sh integrate`, which resolves the stricter integration gate plan from `config/integration-pipelines.json` after the target tree is exact
|
|
178
|
+
|
|
176
179
|
## 5. Checkpoint Model
|
|
177
180
|
|
|
178
181
|
Each completed checkpoint must produce: (1) checkpoint doc under the configured mission base dir for the repo (`missions/<slug>/` in this repo), (2) non-generic `Next action:`, (3) passing relevant gate, (4) commit on `mission/<slug>`. Checkpoint docs make resume and handoff deterministic.
|
package/lib/agents/agents.js
CHANGED
|
@@ -64,9 +64,11 @@ const limit_hit_js_1 = require("./limit-hit.js");
|
|
|
64
64
|
const storage = __importStar(require("../core/storage.js"));
|
|
65
65
|
const product_config_js_1 = require("../core/product-config.js");
|
|
66
66
|
const persistent_data_migration_js_1 = require("../core/persistent-data-migration.js");
|
|
67
|
+
const node_module_1 = require("node:module");
|
|
67
68
|
// tools/sessions is still CJS (not converted in this wave); require keeps it
|
|
68
69
|
// untyped (any) without pulling a non-included .js into the typecheck program.
|
|
69
|
-
const
|
|
70
|
+
const _require = (0, node_module_1.createRequire)(__filename);
|
|
71
|
+
const sessions = _require('../tools/sessions');
|
|
70
72
|
// Launchers whose CLI accepts a per-call resume flag threaded by startAgent.
|
|
71
73
|
// Each launcher outputs a session resume hint at the end of its run (e.g.
|
|
72
74
|
// "codex resume <id>", "opencode -s ses_<id>",
|
package/lib/agents/agents.ts
CHANGED
|
@@ -10,9 +10,11 @@ import { detectLimitHit, formatBlockUntil, DEFAULT_FALLBACK_HOURS } from './limi
|
|
|
10
10
|
import * as storage from '../core/storage.js';
|
|
11
11
|
import { resolveAgentModel } from '../core/product-config.js';
|
|
12
12
|
import { migrateAgentBlocklists } from '../core/persistent-data-migration.js';
|
|
13
|
+
import { createRequire } from 'node:module';
|
|
13
14
|
// tools/sessions is still CJS (not converted in this wave); require keeps it
|
|
14
15
|
// untyped (any) without pulling a non-included .js into the typecheck program.
|
|
15
|
-
const
|
|
16
|
+
const _require = createRequire(__filename);
|
|
17
|
+
const sessions = _require('../tools/sessions');
|
|
16
18
|
|
|
17
19
|
interface LauncherStatus {
|
|
18
20
|
agent: string;
|
package/lib/agents/claude.js
CHANGED
|
@@ -10,9 +10,11 @@ exports.__setSessionsForTest = __setSessionsForTest;
|
|
|
10
10
|
const spawn_tee_js_1 = require("../core/spawn-tee.js");
|
|
11
11
|
const claude_telemetry_js_1 = require("./claude-telemetry.js");
|
|
12
12
|
Object.defineProperty(exports, "extractClaudeTelemetryFromStdout", { enumerable: true, get: function () { return claude_telemetry_js_1.extractClaudeTelemetryFromStdout; } });
|
|
13
|
+
const node_module_1 = require("node:module");
|
|
13
14
|
// tools/sessions is still CJS (not converted in this wave); require keeps it
|
|
14
15
|
// untyped (any) without pulling a non-included .js into the typecheck program.
|
|
15
|
-
const
|
|
16
|
+
const _require = (0, node_module_1.createRequire)(__filename);
|
|
17
|
+
const sessions = _require('../tools/sessions');
|
|
16
18
|
// Injectable I/O for tests. Production uses the real spawn-tee / export capture.
|
|
17
19
|
let _spawnAndTee = spawn_tee_js_1.spawnAndTee;
|
|
18
20
|
let _sessions = sessions;
|
package/lib/agents/claude.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { spawnAndTee } from '../core/spawn-tee.js';
|
|
2
2
|
import { extractClaudeTelemetryFromStdout } from './claude-telemetry.js';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
3
4
|
// tools/sessions is still CJS (not converted in this wave); require keeps it
|
|
4
5
|
// untyped (any) without pulling a non-included .js into the typecheck program.
|
|
5
|
-
const
|
|
6
|
+
const _require = createRequire(__filename);
|
|
7
|
+
const sessions = _require('../tools/sessions');
|
|
6
8
|
|
|
7
9
|
interface ClaudeInvocationOptions {
|
|
8
10
|
prompt: string;
|
package/lib/agents/codex.js
CHANGED
|
@@ -21,9 +21,11 @@ const node_path_1 = __importDefault(require("node:path"));
|
|
|
21
21
|
const spawn_tee_js_1 = require("../core/spawn-tee.js");
|
|
22
22
|
const codex_telemetry_js_1 = require("./codex-telemetry.js");
|
|
23
23
|
Object.defineProperty(exports, "extractCodexTelemetry", { enumerable: true, get: function () { return codex_telemetry_js_1.extractCodexTelemetry; } });
|
|
24
|
+
const node_module_1 = require("node:module");
|
|
24
25
|
// tools/sessions is still CJS (not converted in this wave); require keeps it
|
|
25
26
|
// untyped (any) without pulling a non-included .js into the typecheck program.
|
|
26
|
-
const
|
|
27
|
+
const _require = (0, node_module_1.createRequire)(__filename);
|
|
28
|
+
const sessions = _require('../tools/sessions');
|
|
27
29
|
// Injectable I/O for tests. Production uses the real spawn-tee / export capture.
|
|
28
30
|
let _spawnAndTee = spawn_tee_js_1.spawnAndTee;
|
|
29
31
|
let _sessions = sessions;
|
package/lib/agents/codex.ts
CHANGED
|
@@ -3,9 +3,11 @@ import os from 'node:os';
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { spawnAndTee } from '../core/spawn-tee.js';
|
|
5
5
|
import { extractCodexTelemetry } from './codex-telemetry.js';
|
|
6
|
+
import { createRequire } from 'node:module';
|
|
6
7
|
// tools/sessions is still CJS (not converted in this wave); require keeps it
|
|
7
8
|
// untyped (any) without pulling a non-included .js into the typecheck program.
|
|
8
|
-
const
|
|
9
|
+
const _require = createRequire(__filename);
|
|
10
|
+
const sessions = _require('../tools/sessions');
|
|
9
11
|
|
|
10
12
|
interface CodexInvocationOptions {
|
|
11
13
|
prompt: string;
|
|
@@ -1,40 +1,139 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.DEFAULT_MISTRAL_LOG_DIR = void 0;
|
|
7
|
+
exports.parseMistralMeta = parseMistralMeta;
|
|
8
|
+
exports.extractMistralTelemetry = extractMistralTelemetry;
|
|
9
|
+
exports.getMistralProviderModel = getMistralProviderModel;
|
|
10
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
11
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
12
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
2
13
|
/**
|
|
3
|
-
* Mistral (Vibe) Telemetry
|
|
14
|
+
* Mistral (Vibe) Telemetry Parser
|
|
15
|
+
*
|
|
16
|
+
* Mistral Vibe writes structured token-usage data to per-session meta files:
|
|
17
|
+
* ~/.vibe/logs/session/<session_id>/meta.json
|
|
4
18
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
19
|
+
* Each meta.json contains a `stats` object with token counts:
|
|
20
|
+
* - session_prompt_tokens → inputTokens
|
|
21
|
+
* - session_completion_tokens → outputTokens
|
|
22
|
+
* - session_total_llm_tokens → totalTokens
|
|
9
23
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
24
|
+
* This module scans that directory for the most recent meta.json, parses the
|
|
25
|
+
* stats block, and returns a telemetry object mirroring the shape used by
|
|
26
|
+
* codex-telemetry.ts.
|
|
13
27
|
*
|
|
14
|
-
* See task-
|
|
15
|
-
* verification is tracked as follow-up task-1288 ("Verify vibe/mistral
|
|
16
|
-
* telemetry once usage-unblocked"), which names the missing evidence and the
|
|
17
|
-
* reason it could not be collected in the task-1285 environment.
|
|
28
|
+
* See task-1288 for the discovery that confirmed the structured source.
|
|
18
29
|
*/
|
|
19
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
-
exports.extractMistralTelemetry = extractMistralTelemetry;
|
|
21
|
-
exports.getMistralProviderModel = getMistralProviderModel;
|
|
22
30
|
/**
|
|
23
|
-
*
|
|
24
|
-
*
|
|
31
|
+
* The default directory where Vibe writes its session meta files.
|
|
32
|
+
* Overridden by tests via extractMistralTelemetry(basePath).
|
|
33
|
+
*/
|
|
34
|
+
exports.DEFAULT_MISTRAL_LOG_DIR = node_path_1.default.join(node_os_1.default.homedir(), '.vibe', 'logs', 'session');
|
|
35
|
+
/**
|
|
36
|
+
* Parse the `stats` block from a meta.json file into a telemetry object.
|
|
37
|
+
* Returns null when the content yields no usable signal (missing stats,
|
|
38
|
+
* empty object, or non-object stats).
|
|
39
|
+
*/
|
|
40
|
+
function parseMistralMeta(meta) {
|
|
41
|
+
if (!meta || typeof meta !== 'object') {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
const stats = meta.stats;
|
|
45
|
+
if (!stats || typeof stats !== 'object') {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
const s = stats;
|
|
49
|
+
const inputTokens = Number(s.session_prompt_tokens) || 0;
|
|
50
|
+
const outputTokens = Number(s.session_completion_tokens) || 0;
|
|
51
|
+
const totalTokens = Number(s.session_total_llm_tokens) || 0;
|
|
52
|
+
// Return null when there is no usable signal (all zeros).
|
|
53
|
+
// Mirrors the codex-telemetry pattern where honest zeros still indicate
|
|
54
|
+
// a parseable source was found but contained no actual usage.
|
|
55
|
+
if (!inputTokens && !outputTokens && !totalTokens) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
inputTokens,
|
|
60
|
+
outputTokens,
|
|
61
|
+
totalTokens,
|
|
62
|
+
contextTokens: Number(s.context_tokens) || 0,
|
|
63
|
+
toolCallsAgreed: Number(s.tool_calls_agreed) || 0,
|
|
64
|
+
toolCallsRejected: Number(s.tool_calls_rejected) || 0,
|
|
65
|
+
toolCallsFailed: Number(s.tool_calls_failed) || 0,
|
|
66
|
+
toolCallsSucceeded: Number(s.tool_calls_succeeded) || 0,
|
|
67
|
+
sessionCost: Number(s.session_cost) || 0,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Scan ~/.vibe/logs/session/ for the most recent session meta.json, parse its
|
|
72
|
+
* stats block, and return a telemetry object.
|
|
25
73
|
*
|
|
26
|
-
* @param
|
|
27
|
-
* @
|
|
74
|
+
* @param result - Legacy launcher result object (ignored; kept for API compat)
|
|
75
|
+
* @param basePath - Override the default session log directory. Used by tests.
|
|
28
76
|
*/
|
|
29
|
-
function extractMistralTelemetry() {
|
|
30
|
-
//
|
|
77
|
+
function extractMistralTelemetry(result, basePath) {
|
|
78
|
+
void result; // legacy param, ignored — telemetry comes from on-disk meta.json
|
|
79
|
+
const scanDir = basePath || exports.DEFAULT_MISTRAL_LOG_DIR;
|
|
80
|
+
// Scan session subdirectories for the newest meta.json.
|
|
81
|
+
// Basenames are session_<YYYYMMDD>_<HHMMSS>_<id>, so alphabetical sort = chronological.
|
|
82
|
+
let sessionDirs;
|
|
83
|
+
try {
|
|
84
|
+
sessionDirs = node_fs_1.default.readdirSync(scanDir);
|
|
85
|
+
}
|
|
86
|
+
catch (_) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
const dirs = sessionDirs
|
|
90
|
+
.filter((d) => {
|
|
91
|
+
if (!d.startsWith('session_')) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
const full = node_path_1.default.join(scanDir, d);
|
|
95
|
+
try {
|
|
96
|
+
return node_fs_1.default.statSync(full).isDirectory();
|
|
97
|
+
}
|
|
98
|
+
catch (_) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
})
|
|
102
|
+
.sort();
|
|
103
|
+
if (dirs.length === 0) {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
// Walk newest-first; return the first session that has a parseable meta.json.
|
|
107
|
+
for (let i = dirs.length - 1; i >= 0; i--) {
|
|
108
|
+
const metaPath = node_path_1.default.join(scanDir, dirs[i], 'meta.json');
|
|
109
|
+
if (!node_fs_1.default.existsSync(metaPath)) {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
let content;
|
|
113
|
+
try {
|
|
114
|
+
content = node_fs_1.default.readFileSync(metaPath, 'utf8');
|
|
115
|
+
}
|
|
116
|
+
catch (_) {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
let meta;
|
|
120
|
+
try {
|
|
121
|
+
meta = JSON.parse(content);
|
|
122
|
+
}
|
|
123
|
+
catch (_) {
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
const telemetry = parseMistralMeta(meta);
|
|
127
|
+
if (!telemetry) {
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
return { ...telemetry, path: metaPath };
|
|
131
|
+
}
|
|
31
132
|
return null;
|
|
32
133
|
}
|
|
33
134
|
/**
|
|
34
135
|
* Return the provider/model pair for mistral tasks.
|
|
35
136
|
* Used as fallback when telemetry is null.
|
|
36
|
-
*
|
|
37
|
-
* @returns {{provider: string, model: string}}
|
|
38
137
|
*/
|
|
39
138
|
function getMistralProviderModel() {
|
|
40
139
|
return { provider: 'mistral', model: 'mistral' };
|