harness-everything 0.46.0 → 0.46.1
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +7 -0
- package/benchmarks/results/live-host/claude-341-outline-dispatch-2026-10-28/README.md +75 -0
- package/benchmarks/results/live-host/claude-341-outline-dispatch-2026-10-28/capture.sanitized.jsonl +32 -0
- package/ci/mechanism-prb-outline-review.test.js +207 -23
- package/docs/doc-workflow-reconciliation.md +21 -5
- package/hooks/scripts/lib/doc-reconciliation-provider.js +148 -35
- package/opencode-plugin/plugin.json +1 -1
- package/package.json +1 -1
- package/plugins/harness-everything/.codex-plugin/plugin.json +1 -1
- package/plugins/harness-everything/hooks/scripts/lib/doc-reconciliation-provider.js +148 -35
- package/plugins/harness-everything/plugin.json +1 -1
- package/sbom.cdx.json +7 -7
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"name": "harness-everything",
|
|
11
11
|
"source": "./",
|
|
12
12
|
"description": "Harness OS: an orchestrated agent operating system — tier routing, circuit breaker guardrails, verification gates, WAL session memory, and self-evolving workspace rules.",
|
|
13
|
-
"version": "0.46.
|
|
13
|
+
"version": "0.46.1",
|
|
14
14
|
"author": {
|
|
15
15
|
"name": "Miya Daniel"
|
|
16
16
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "harness-everything",
|
|
3
3
|
"description": "Harness OS: an orchestrated agent operating system — tier routing, circuit breaker guardrails, verification gates, WAL session memory, and self-evolving workspace rules.",
|
|
4
|
-
"version": "0.46.
|
|
4
|
+
"version": "0.46.1",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Miya Daniel"
|
|
7
7
|
},
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project are documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.46.1](https://github.com/dyphn1/Harness-everything/compare/v0.46.0...v0.46.1) (2026-10-28)
|
|
6
|
+
|
|
7
|
+
### Bug Fixes
|
|
8
|
+
|
|
9
|
+
* **doc-reconciliation:** correlate outline dispatch by observed agent result ([c63e1ef](https://github.com/dyphn1/Harness-everything/commit/c63e1efa299496283bffb1019983b0dd54595f64))
|
|
10
|
+
|
|
5
11
|
## [0.46.0](https://github.com/dyphn1/Harness-everything/compare/v0.45.0...v0.46.0) (2026-10-28)
|
|
6
12
|
|
|
7
13
|
### Features
|
|
@@ -45,6 +51,7 @@ All notable changes to this project are documented in this file.
|
|
|
45
51
|
|
|
46
52
|
### Fixed
|
|
47
53
|
|
|
54
|
+
- **doc-reconciliation:** correlate outline dispatches through completed Agent/Task results and unique worker start/stop events while keeping live credit gated.
|
|
48
55
|
- **doc-governance:** allow regular governed-content revisions and expand base-owned governed/derived globs against Git trees, while retaining review for membership, structural, policy, trust-surface, fixture, and derived-output changes; run the guard in its own CI job.
|
|
49
56
|
|
|
50
57
|
## [0.43.0](https://github.com/dyphn1/Harness-everything/compare/v0.42.2...v0.43.0) (2026-10-27)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Live-host capture: claude-341-outline-dispatch (2026-10-28)
|
|
2
|
+
|
|
3
|
+
**Verdict: the outline live gate stays UNMET.** Claude Code 2.1.312 carries the `HARNESS_OUTLINE_DISPATCH_V1` prompt, the coordinator `session_id` and a synchronous PreToolUse boundary. It does not carry two fields that `outlineCaptureMappingValid` requires: `SubagentStart.parent_tool_use_id` and an `interrupted` field on the Agent PostToolUse. This is field-availability evidence, not live-host credit. `hooks/scripts/lib/doc-reconciliation-qualification.json` is unchanged, so positive outline credit stays disabled.
|
|
4
|
+
|
|
5
|
+
## What was captured
|
|
6
|
+
|
|
7
|
+
```mermaid
|
|
8
|
+
sequenceDiagram
|
|
9
|
+
participant C as Coordinator (session_id)
|
|
10
|
+
participant H as PreToolUse hook (held 2 s)
|
|
11
|
+
participant W as Worker (agent_id)
|
|
12
|
+
C->>H: PreToolUse Agent, tool_input.prompt = marker
|
|
13
|
+
H-->>C: hook ends (+2000 ms)
|
|
14
|
+
C->>W: SubagentStart (+30–100 ms after hook end)<br/>no parent_tool_use_id
|
|
15
|
+
W-->>C: SubagentStop
|
|
16
|
+
C->>C: PostToolUse Agent, tool_use_id matches Pre<br/>agent id only in tool_response.agentId, no interrupted
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Pins
|
|
20
|
+
|
|
21
|
+
| Item | Value |
|
|
22
|
+
|---|---|
|
|
23
|
+
| Harness HEAD (staged) | `f1e6ab4e45fefa908c06f4f6fe96750b731987f9` (main after #387), clean tree |
|
|
24
|
+
| Staging | `node scripts/stage-claude-plugin.js <scratch>/plugin` (1788 tracked files) |
|
|
25
|
+
| Host | Claude Code `2.1.312`, macOS (darwin 25.6.0), zsh, `--model haiku` |
|
|
26
|
+
| Pilot | `HARNESS_DOC_RECONCILIATION_PILOT=1`, fresh `HARNESS_STATE_HOME` per run |
|
|
27
|
+
|
|
28
|
+
| File | sha256 |
|
|
29
|
+
|---|---|
|
|
30
|
+
| `capture.sanitized.jsonl` (this dir) | `cf1ea56c3d329edcb0a18fc6437af9d9aedf56c76a829f3949de382d07c5a657` |
|
|
31
|
+
| `capture-settings.json` (session `--settings`: PreToolUse/PostToolUse `Agent\|Task`, SubagentStart, SubagentStop) | `1c3081a86a0b1e0aa894f2f09a7884ca9bf5dbf55a08ab84074d8a318bd9c587` |
|
|
32
|
+
| `wrapper.js` (log-only, holds PreToolUse 2 s, runs no Harness hook) | `9071ab6b482fc31b1753df242fcf5983f1dd75ce6c5acf8ba78b1a5921f6b2fe` |
|
|
33
|
+
| staged `hooks/hooks.json` | `aca70167da0fa792e34fb55e2904e2935c83f8f087de9e3cd130dd536b01eafd` |
|
|
34
|
+
| staged `hooks/scripts/lib/doc-reconciliation-provider.js` | `1e0a9cc6fd040c074d96a79ec6445c328957951a7c0074427687d0a22179ec3b` |
|
|
35
|
+
|
|
36
|
+
## Command (sanitized)
|
|
37
|
+
|
|
38
|
+
```text
|
|
39
|
+
cd <scratch>/project # fresh git repo, one commit
|
|
40
|
+
HARNESS_DOC_RECONCILIATION_PILOT=1 HARNESS_STATE_HOME=<scratch>/state \
|
|
41
|
+
claude -p "<fixed prompt: dispatch outline-worker twice via Agent; 1 = no isolation, 2 = isolation worktree; each prompt = marker line + 'Reply with: worker done'>" \
|
|
42
|
+
--model haiku --plugin-dir <scratch>/plugin --settings <scratch>/capture-settings.json \
|
|
43
|
+
--agents '{"outline-worker":{"description":"Capture worker that replies once and uses no tools","prompt":"Use no tools. Reply exactly: worker done"}}' \
|
|
44
|
+
--allowedTools Agent Task --session-id <fresh UUID> --setting-sources project,local \
|
|
45
|
+
--output-format stream-json --verbose
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
No bypass flag. Marker: `HARNESS_OUTLINE_DISPATCH_V1 taskId=<id> attemptId=a1 token=<token-n> workerRoot=<scratch>/project`. Both runs exited `0`.
|
|
49
|
+
|
|
50
|
+
- **Run 1** used `taskId=cap-00N`. The plugin rejected it as `dispatch-marker-invalid`, because task ids must match `task-*`. This was a fixture error. The raw host payloads are still valid field evidence.
|
|
51
|
+
- **Run 2** used `taskId=task-cap-00N`. The plugin parsed the marker and recorded `workflow-task-binding-invalid`. That rejection is expected, since no accepted review, handoff or lease exists in this bounded fixture.
|
|
52
|
+
|
|
53
|
+
## Must-report table (mapping in `outlineCaptureMappingValid`)
|
|
54
|
+
|
|
55
|
+
| Mapping clause | 2.1.312 observed | Evidence |
|
|
56
|
+
|---|---|---|
|
|
57
|
+
| `promptField: tool_input.prompt` | **yes**, verbatim marker | PreToolUse Agent `tool_input.prompt`, 4/4 dispatches |
|
|
58
|
+
| `coordinatorPrincipalField: session_id` | **yes**, but the worker's SubagentStart/Stop carry the **same** `session_id` | Coordinator and worker differ only by `agent_id` presence |
|
|
59
|
+
| `synchronousPreToolUse` | **yes** | 4/4: SubagentStart 30–100 ms after the 2 s PreToolUse hold ended |
|
|
60
|
+
| `workerStartDispatchField: parent_tool_use_id` | **no**, the field is absent | 0/4 SubagentStart payloads. Only `session_id`, `prompt_id` (shared by both dispatches), `agent_id`, `agent_type`, `cwd` |
|
|
61
|
+
| `workerStartRootField: cwd` | **partial** | No isolation: `cwd` = coordinator cwd. `isolation: worktree`: `cwd` = `<project>/.claude/worktrees/agent-<agent_id>`, which is unknown before dispatch, so it cannot equal the marker's `workerRoot` |
|
|
62
|
+
| `taskResultDispatchField: tool_use_id` | **yes** | PostToolUse `tool_use_id` equals the Pre's, 4/4 |
|
|
63
|
+
| Result `agent_id` + `interrupted === false` | **no** | No top-level `agent_id` (the plugin journal lifts `tool_response.agentId`). No `interrupted` field on the Agent result at all |
|
|
64
|
+
|
|
65
|
+
## Extra finding: async launch
|
|
66
|
+
|
|
67
|
+
In Run 2 dispatch 1 the model omitted `run_in_background`. The host launched the worker asynchronously: PostToolUse `status: async_launched`, `isAsync: true`, and it fired **before** SubagentStop (+2040 ms vs +3490 ms). A PostToolUse is therefore not proof that the worker finished. Any result correlation must require `status: completed` or an explicit `run_in_background: false`.
|
|
68
|
+
|
|
69
|
+
## What would unblock the gate (owner/advisor decision, not done here)
|
|
70
|
+
|
|
71
|
+
- Correlate worker start without `parent_tool_use_id`: for example, by the order of Pre end → next SubagentStart within the same `session_id` while one dispatch is in flight, or by `tool_response.agentId` ↔ SubagentStart `agent_id` after the fact.
|
|
72
|
+
- Replace `interrupted === false` with `tool_response.status === 'completed'`.
|
|
73
|
+
- Define worker-root for isolation worktrees, where the path is derived from `agent_id`, not predeclared.
|
|
74
|
+
|
|
75
|
+
Each of these changes the mapping in `doc-reconciliation-provider.js`, so it needs advisor adjudication against answer-10 item 2/3 before any implementation.
|
package/benchmarks/results/live-host/claude-341-outline-dispatch-2026-10-28/capture.sanitized.jsonl
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{"run":"run1","phase":"start","ms":0,"event":"PreToolUse","payload":{"session_id":"<session-1>","cwd":"<scratch>/project","prompt_id":"<prompt-1>","permission_mode":"default","effort":{"level":"medium"},"hook_event_name":"PreToolUse","tool_name":"Agent","tool_input":{"description":"Outline capture worker dispatch 1","prompt":"HARNESS_OUTLINE_DISPATCH_V1 taskId=cap-001 attemptId=a1 token=<token-1> workerRoot=<scratch>/project\nReply with: worker done","subagent_type":"outline-worker","run_in_background":false},"tool_use_id":"<tool-use-1>"}}
|
|
2
|
+
{"run":"run1","phase":"end","ms":2002,"event":"PreToolUse","tool_use_id":"<tool-use-1>"}
|
|
3
|
+
{"run":"run1","phase":"start","ms":2032,"event":"SubagentStart","payload":{"session_id":"<session-1>","cwd":"<scratch>/project","prompt_id":"<prompt-1>","agent_id":"<agent-1>","agent_type":"outline-worker","hook_event_name":"SubagentStart"}}
|
|
4
|
+
{"run":"run1","phase":"end","ms":2033,"event":"SubagentStart","tool_use_id":null}
|
|
5
|
+
{"run":"run1","phase":"start","ms":3307,"event":"SubagentStop","payload":{"session_id":"<session-1>","cwd":"<scratch>/project","prompt_id":"<prompt-1>","permission_mode":"default","agent_id":"<agent-1>","agent_type":"outline-worker","effort":{"level":"medium"},"hook_event_name":"SubagentStop","stop_hook_active":false,"last_assistant_message":"worker done","background_tasks":[],"session_crons":[]}}
|
|
6
|
+
{"run":"run1","phase":"end","ms":3308,"event":"SubagentStop","tool_use_id":null}
|
|
7
|
+
{"run":"run1","phase":"start","ms":3360,"event":"PostToolUse","payload":{"session_id":"<session-1>","cwd":"<scratch>/project","prompt_id":"<prompt-1>","permission_mode":"default","effort":{"level":"medium"},"hook_event_name":"PostToolUse","tool_name":"Agent","tool_input":{"description":"Outline capture worker dispatch 1","prompt":"HARNESS_OUTLINE_DISPATCH_V1 taskId=cap-001 attemptId=a1 token=<token-1> workerRoot=<scratch>/project\nReply with: worker done","subagent_type":"outline-worker","run_in_background":false},"tool_response":{"keys":["agentId","agentType","canContinueAgent","content","harnessNoteCount","harnessSectionHash","harnessTailCount","prompt","resolvedModel","status","totalDurationMs","totalTokens","totalToolUseCount","usage"],"status":"completed","agentId":"<agent-1>"},"tool_use_id":"<tool-use-1>","duration_ms":1327}}
|
|
8
|
+
{"run":"run1","phase":"end","ms":3361,"event":"PostToolUse","tool_use_id":"<tool-use-1>"}
|
|
9
|
+
{"run":"run1","phase":"start","ms":6198,"event":"PreToolUse","payload":{"session_id":"<session-1>","cwd":"<scratch>/project","prompt_id":"<prompt-1>","permission_mode":"default","effort":{"level":"medium"},"hook_event_name":"PreToolUse","tool_name":"Agent","tool_input":{"description":"Outline capture worker dispatch 2","prompt":"HARNESS_OUTLINE_DISPATCH_V1 taskId=cap-002 attemptId=a1 token=<token-2> workerRoot=<scratch>/project\nReply with: worker done","subagent_type":"outline-worker","run_in_background":false,"isolation":"worktree"},"tool_use_id":"<tool-use-2>"}}
|
|
10
|
+
{"run":"run1","phase":"end","ms":8199,"event":"PreToolUse","tool_use_id":"<tool-use-2>"}
|
|
11
|
+
{"run":"run1","phase":"start","ms":8299,"event":"SubagentStart","payload":{"session_id":"<session-1>","cwd":"<scratch>/project/.claude/worktrees/agent-<agent-2>","prompt_id":"<prompt-1>","agent_id":"<agent-2>","agent_type":"outline-worker","hook_event_name":"SubagentStart"}}
|
|
12
|
+
{"run":"run1","phase":"end","ms":8300,"event":"SubagentStart","tool_use_id":null}
|
|
13
|
+
{"run":"run1","phase":"start","ms":9757,"event":"SubagentStop","payload":{"session_id":"<session-1>","cwd":"<scratch>/project/.claude/worktrees/agent-<agent-2>","prompt_id":"<prompt-1>","permission_mode":"default","agent_id":"<agent-2>","agent_type":"outline-worker","effort":{"level":"medium"},"hook_event_name":"SubagentStop","stop_hook_active":false,"last_assistant_message":"worker done","background_tasks":[],"session_crons":[]}}
|
|
14
|
+
{"run":"run1","phase":"end","ms":9758,"event":"SubagentStop","tool_use_id":null}
|
|
15
|
+
{"run":"run1","phase":"start","ms":9864,"event":"PostToolUse","payload":{"session_id":"<session-1>","cwd":"<scratch>/project","prompt_id":"<prompt-1>","permission_mode":"default","effort":{"level":"medium"},"hook_event_name":"PostToolUse","tool_name":"Agent","tool_input":{"description":"Outline capture worker dispatch 2","prompt":"HARNESS_OUTLINE_DISPATCH_V1 taskId=cap-002 attemptId=a1 token=<token-2> workerRoot=<scratch>/project\nReply with: worker done","subagent_type":"outline-worker","run_in_background":false,"isolation":"worktree"},"tool_response":{"keys":["agentId","agentType","canContinueAgent","content","harnessNoteCount","harnessSectionHash","harnessTailCount","prompt","resolvedModel","status","totalDurationMs","totalTokens","totalToolUseCount","usage"],"status":"completed","agentId":"<agent-2>"},"tool_use_id":"<tool-use-2>","duration_ms":1635}}
|
|
16
|
+
{"run":"run1","phase":"end","ms":9865,"event":"PostToolUse","tool_use_id":"<tool-use-2>"}
|
|
17
|
+
{"run":"run2","phase":"start","ms":0,"event":"PreToolUse","payload":{"session_id":"<session-2>","cwd":"<scratch>/project","prompt_id":"<prompt-2>","permission_mode":"default","effort":{"level":"medium"},"hook_event_name":"PreToolUse","tool_name":"Agent","tool_input":{"description":"Outline capture dispatch task-cap-001","prompt":"HARNESS_OUTLINE_DISPATCH_V1 taskId=task-cap-001 attemptId=a1 token=<token-3> workerRoot=<scratch>/project","subagent_type":"outline-worker"},"tool_use_id":"<tool-use-3>"}}
|
|
18
|
+
{"run":"run2","phase":"end","ms":2001,"event":"PreToolUse","tool_use_id":"<tool-use-3>"}
|
|
19
|
+
{"run":"run2","phase":"start","ms":2039,"event":"SubagentStart","payload":{"session_id":"<session-2>","cwd":"<scratch>/project","prompt_id":"<prompt-2>","agent_id":"<agent-3>","agent_type":"outline-worker","hook_event_name":"SubagentStart"}}
|
|
20
|
+
{"run":"run2","phase":"start","ms":2040,"event":"PostToolUse","payload":{"session_id":"<session-2>","cwd":"<scratch>/project","prompt_id":"<prompt-2>","permission_mode":"default","effort":{"level":"medium"},"hook_event_name":"PostToolUse","tool_name":"Agent","tool_input":{"description":"Outline capture dispatch task-cap-001","prompt":"HARNESS_OUTLINE_DISPATCH_V1 taskId=task-cap-001 attemptId=a1 token=<token-3> workerRoot=<scratch>/project","subagent_type":"outline-worker"},"tool_response":{"keys":["agentId","canContinueAgent","canReadOutputFile","description","isAsync","outputFile","prompt","resolvedModel","status"],"status":"async_launched","isAsync":true,"agentId":"<agent-3>"},"tool_use_id":"<tool-use-3>","duration_ms":3}}
|
|
21
|
+
{"run":"run2","phase":"end","ms":2041,"event":"SubagentStart","tool_use_id":null}
|
|
22
|
+
{"run":"run2","phase":"end","ms":2042,"event":"PostToolUse","tool_use_id":"<tool-use-3>"}
|
|
23
|
+
{"run":"run2","phase":"start","ms":3490,"event":"SubagentStop","payload":{"session_id":"<session-2>","cwd":"<scratch>/project","prompt_id":"<prompt-2>","permission_mode":"default","agent_id":"<agent-3>","agent_type":"outline-worker","effort":{"level":"medium"},"hook_event_name":"SubagentStop","stop_hook_active":false,"last_assistant_message":"worker done","background_tasks":[{"id":"<agent-3>","type":"subagent","status":"running","description":"Outline capture dispatch task-cap-001","agent_type":"outline-worker"}],"session_crons":[]}}
|
|
24
|
+
{"run":"run2","phase":"end","ms":3492,"event":"SubagentStop","tool_use_id":null}
|
|
25
|
+
{"run":"run2","phase":"start","ms":7846,"event":"PreToolUse","payload":{"session_id":"<session-2>","cwd":"<scratch>/project","prompt_id":"<prompt-3>","permission_mode":"default","effort":{"level":"medium"},"hook_event_name":"PreToolUse","tool_name":"Agent","tool_input":{"description":"Outline capture dispatch task-cap-002","prompt":"HARNESS_OUTLINE_DISPATCH_V1 taskId=task-cap-002 attemptId=a1 token=<token-4> workerRoot=<scratch>/project","subagent_type":"outline-worker","run_in_background":false,"isolation":"worktree"},"tool_use_id":"<tool-use-4>"}}
|
|
26
|
+
{"run":"run2","phase":"end","ms":9848,"event":"PreToolUse","tool_use_id":"<tool-use-4>"}
|
|
27
|
+
{"run":"run2","phase":"start","ms":9942,"event":"SubagentStart","payload":{"session_id":"<session-2>","cwd":"<scratch>/project/.claude/worktrees/agent-<agent-4>","prompt_id":"<prompt-3>","agent_id":"<agent-4>","agent_type":"outline-worker","hook_event_name":"SubagentStart"}}
|
|
28
|
+
{"run":"run2","phase":"end","ms":9943,"event":"SubagentStart","tool_use_id":null}
|
|
29
|
+
{"run":"run2","phase":"start","ms":11161,"event":"SubagentStop","payload":{"session_id":"<session-2>","cwd":"<scratch>/project/.claude/worktrees/agent-<agent-4>","prompt_id":"<prompt-3>","permission_mode":"default","agent_id":"<agent-4>","agent_type":"outline-worker","effort":{"level":"medium"},"hook_event_name":"SubagentStop","stop_hook_active":false,"last_assistant_message":"worker done","background_tasks":[],"session_crons":[]}}
|
|
30
|
+
{"run":"run2","phase":"end","ms":11162,"event":"SubagentStop","tool_use_id":null}
|
|
31
|
+
{"run":"run2","phase":"start","ms":11263,"event":"PostToolUse","payload":{"session_id":"<session-2>","cwd":"<scratch>/project","prompt_id":"<prompt-3>","permission_mode":"default","effort":{"level":"medium"},"hook_event_name":"PostToolUse","tool_name":"Agent","tool_input":{"description":"Outline capture dispatch task-cap-002","prompt":"HARNESS_OUTLINE_DISPATCH_V1 taskId=task-cap-002 attemptId=a1 token=<token-4> workerRoot=<scratch>/project","subagent_type":"outline-worker","run_in_background":false,"isolation":"worktree"},"tool_response":{"keys":["agentId","agentType","canContinueAgent","content","harnessNoteCount","harnessSectionHash","harnessTailCount","prompt","resolvedModel","status","totalDurationMs","totalTokens","totalToolUseCount","usage"],"status":"completed","agentId":"<agent-4>"},"tool_use_id":"<tool-use-4>","duration_ms":1388}}
|
|
32
|
+
{"run":"run2","phase":"end","ms":11264,"event":"PostToolUse","tool_use_id":"<tool-use-4>"}
|
|
@@ -188,7 +188,7 @@ function createOutlineFixture(options = {}) {
|
|
|
188
188
|
const reviewResultPayload = {
|
|
189
189
|
session_id: sessionId, cwd: root, hook_event_name: 'PostToolUse', tool_name: 'Task',
|
|
190
190
|
tool_use_id: 'review-dispatch', tool_input: { command: 'Task' },
|
|
191
|
-
tool_response: { agentId: reviewerPrincipal, interrupted: false },
|
|
191
|
+
tool_response: { agentId: reviewerPrincipal, interrupted: false, status: 'completed' },
|
|
192
192
|
};
|
|
193
193
|
const reviewStopPayload = {
|
|
194
194
|
session_id: sessionId, cwd: root, hook_event_name: 'SubagentStop', agent_id: reviewerPrincipal,
|
|
@@ -239,7 +239,7 @@ function createOutlineFixture(options = {}) {
|
|
|
239
239
|
|
|
240
240
|
const payload = {
|
|
241
241
|
session_id: sessionId, cwd: root, hook_event_name: 'PreToolUse', tool_name: 'Task',
|
|
242
|
-
tool_use_id: options.toolUseId || 'dispatch-1', tool_input: { prompt: marker },
|
|
242
|
+
tool_use_id: options.toolUseId || 'dispatch-1', tool_input: { prompt: marker, run_in_background: false },
|
|
243
243
|
};
|
|
244
244
|
if (options.blockSeamStorage) {
|
|
245
245
|
fs.mkdirSync(path.join(attemptDir, 'seams', `${sha(String(payload.tool_use_id))}.json`), { recursive: true });
|
|
@@ -281,7 +281,7 @@ function createOutlineFixture(options = {}) {
|
|
|
281
281
|
if (seam) capture.outlineDispatch = seam;
|
|
282
282
|
const workerStartPayload = {
|
|
283
283
|
session_id: sessionId, cwd: options.workerStartRoot || root, hook_event_name: 'SubagentStart',
|
|
284
|
-
agent_id: options.workerPrincipal || 'draft-worker', agent_type: 'worker',
|
|
284
|
+
agent_id: options.workerPrincipal || 'draft-worker', agent_type: 'worker',
|
|
285
285
|
};
|
|
286
286
|
let workerStart;
|
|
287
287
|
if (options.realHook) {
|
|
@@ -295,7 +295,7 @@ function createOutlineFixture(options = {}) {
|
|
|
295
295
|
const extraCaptures = [];
|
|
296
296
|
|
|
297
297
|
function dispatchAgain({ toolUseId = capture.tool_use_id, prompt = payload.tool_input.prompt } = {}) {
|
|
298
|
-
const extraPayload = { ...payload, tool_use_id: toolUseId, tool_input: { prompt } };
|
|
298
|
+
const extraPayload = { ...payload, tool_use_id: toolUseId, tool_input: { prompt, run_in_background: false } };
|
|
299
299
|
const extra = provider.buildCaptureRecord(extraPayload, 'PreToolUse', root);
|
|
300
300
|
extra.observedIdentity = capture.observedIdentity;
|
|
301
301
|
if (internals.captureOutlineDispatch) {
|
|
@@ -309,7 +309,7 @@ function createOutlineFixture(options = {}) {
|
|
|
309
309
|
}
|
|
310
310
|
|
|
311
311
|
function finishDraft({ draft = true, nonDescendant = false, finalReview = false, baselineRoot = root,
|
|
312
|
-
omitWorkerStart = false, omitWorkerResult = false, tamperHandoff = false,
|
|
312
|
+
omitWorkerStart = false, omitWorkerResult = false, omitWorkerStop = false, tamperHandoff = false,
|
|
313
313
|
providerSessionId = sessionId, providerTaskId = taskId, providerBaseline = { root, commit: baselineCommit },
|
|
314
314
|
runPilot = false, handAuthoredCapture = false, removeSeam = false, mutateAfterFinalReview = false } = {}) {
|
|
315
315
|
if (draft) {
|
|
@@ -337,7 +337,11 @@ function createOutlineFixture(options = {}) {
|
|
|
337
337
|
if (!omitWorkerResult) runHook({
|
|
338
338
|
session_id: sessionId, cwd: root, hook_event_name: 'PostToolUse', tool_name: 'Task',
|
|
339
339
|
tool_use_id: capture.tool_use_id, tool_input: { command: 'Task' },
|
|
340
|
-
tool_response: { agentId: options.workerPrincipal || 'draft-worker', interrupted: false },
|
|
340
|
+
tool_response: { agentId: options.workerPrincipal || 'draft-worker', interrupted: false, status: 'completed' },
|
|
341
|
+
});
|
|
342
|
+
if (!omitWorkerStop) runHook({
|
|
343
|
+
session_id: sessionId, cwd: root, hook_event_name: 'SubagentStop',
|
|
344
|
+
agent_id: options.workerPrincipal || 'draft-worker', agent_type: 'worker',
|
|
341
345
|
});
|
|
342
346
|
}
|
|
343
347
|
if (finalReview) {
|
|
@@ -345,7 +349,7 @@ function createOutlineFixture(options = {}) {
|
|
|
345
349
|
const finalResultPayload = {
|
|
346
350
|
session_id: sessionId, cwd: root, hook_event_name: 'PostToolUse', tool_name: 'Task',
|
|
347
351
|
tool_use_id: 'final-review-dispatch', tool_input: { command: 'Task' },
|
|
348
|
-
tool_response: { agentId: finalReviewer, interrupted: false },
|
|
352
|
+
tool_response: { agentId: finalReviewer, interrupted: false, status: 'completed' },
|
|
349
353
|
};
|
|
350
354
|
const finalStopPayload = {
|
|
351
355
|
session_id: sessionId, cwd: root, hook_event_name: 'SubagentStop', agent_id: finalReviewer,
|
|
@@ -374,8 +378,10 @@ function createOutlineFixture(options = {}) {
|
|
|
374
378
|
: [...reviewCaptures, capture, ...extraCaptures, ...(!omitWorkerStart ? [workerStart] : []), ...(!omitWorkerResult ? [provider.buildCaptureRecord({
|
|
375
379
|
session_id: sessionId, cwd: root, hook_event_name: 'PostToolUse', tool_name: 'Task',
|
|
376
380
|
tool_use_id: capture.tool_use_id, tool_input: { command: 'Task' },
|
|
377
|
-
tool_response: { agentId: options.workerPrincipal || 'draft-worker', interrupted: false },
|
|
378
|
-
}, 'PostToolUse', root)] : [])
|
|
381
|
+
tool_response: { agentId: options.workerPrincipal || 'draft-worker', interrupted: false, status: 'completed' },
|
|
382
|
+
}, 'PostToolUse', root)] : []), ...(!omitWorkerStop ? [provider.buildCaptureRecord({
|
|
383
|
+
session_id: sessionId, cwd: root, hook_event_name: 'SubagentStop', agent_id: options.workerPrincipal || 'draft-worker', agent_type: 'worker',
|
|
384
|
+
}, 'SubagentStop', root)] : [])];
|
|
379
385
|
const qualification = { source: 'fake-test-fixture', faked: true, toolUseIdPopulated: true,
|
|
380
386
|
agentIdPopulated: true, outlineDispatchCaptureVerified: true };
|
|
381
387
|
if (removeSeam && capture.outlineDispatch?.seamRef) {
|
|
@@ -805,14 +811,14 @@ test('canonical root identity accepts an alias across marker, handoff, lease, an
|
|
|
805
811
|
} finally { fixture.cleanup(); }
|
|
806
812
|
});
|
|
807
813
|
|
|
808
|
-
test('one-use token
|
|
814
|
+
test('one-use token deduplicates identical seams but replayed tool-use records stay unresolved', () => {
|
|
809
815
|
assert.strictEqual(typeof internals.captureOutlineDispatch, 'function', 'missing token-consuming seam');
|
|
810
816
|
const duplicate = createOutlineFixture();
|
|
811
817
|
try {
|
|
812
818
|
const same = duplicate.dispatchAgain();
|
|
813
819
|
assert.strictEqual(same.outlineDispatch.state, 'accepted', JSON.stringify(same.outlineDispatch));
|
|
814
820
|
assert.strictEqual(same.outlineDispatch.duplicate, true);
|
|
815
|
-
assert.
|
|
821
|
+
assert.notStrictEqual(duplicate.finishDraft().result.status, 'accepted', 'a replayed host PreToolUse record is not a unique dispatch anchor');
|
|
816
822
|
} finally { duplicate.cleanup(); }
|
|
817
823
|
|
|
818
824
|
for (const [name, replay] of [
|
|
@@ -949,24 +955,202 @@ test('baseline integrity, callback order, delayed result, stash/restore, and uno
|
|
|
949
955
|
} finally { shellWriter.cleanup(); }
|
|
950
956
|
});
|
|
951
957
|
|
|
952
|
-
|
|
958
|
+
const outlineMappingV2 = {
|
|
959
|
+
version: 'claude-outline-dispatch-mapping/2',
|
|
960
|
+
promptField: 'tool_input.prompt',
|
|
961
|
+
dispatchKey: 'session_id+tool_use_id',
|
|
962
|
+
runInBackgroundField: 'tool_input.run_in_background',
|
|
963
|
+
isolationPresenceField: 'tool_input.isolation',
|
|
964
|
+
resultAgentIdField: 'tool_response.agentId',
|
|
965
|
+
resultStatusField: 'tool_response.status',
|
|
966
|
+
workerStartEvent: 'SubagentStart',
|
|
967
|
+
workerStopEvent: 'SubagentStop',
|
|
968
|
+
workerIdentity: 'session_id+agent_id',
|
|
969
|
+
workerRootField: 'cwd',
|
|
970
|
+
};
|
|
971
|
+
|
|
972
|
+
function qualificationDispatch({ id = 'dispatch-1', promptId = 'shared-prompt', background = false, hasBackground = true,
|
|
973
|
+
isolation, hasIsolation = false, session = 'coordinator' } = {}) {
|
|
974
|
+
const toolInput = { prompt: dispatchLine, ...(hasBackground ? { run_in_background: background } : {}) };
|
|
975
|
+
if (hasIsolation) toolInput.isolation = isolation;
|
|
976
|
+
return { event: 'PreToolUse', tool_name: 'Task', tool_use_id: id, session_id: session, prompt_id: promptId, tool_input: toolInput };
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
function qualificationStart(agentId = 'worker', { session = 'coordinator', cwd = '/tmp/prb-worker', id } = {}) {
|
|
980
|
+
return { event: 'SubagentStart', ...(id ? { tool_use_id: id } : {}), agent_id: agentId, session_id: session, cwd };
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
function qualificationStop(agentId = 'worker', { session = 'coordinator', cwd = '/tmp/prb-worker' } = {}) {
|
|
984
|
+
return { event: 'SubagentStop', agent_id: agentId, session_id: session, cwd };
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
function qualificationResult(id = 'dispatch-1', agentId = 'worker', status = 'completed', { session = 'coordinator', legacyAgentId } = {}) {
|
|
988
|
+
const toolResponse = { agentId };
|
|
989
|
+
if (status !== null) toolResponse.status = status;
|
|
990
|
+
return { event: 'PostToolUse', tool_name: 'Task', tool_use_id: id, session_id: session,
|
|
991
|
+
...(legacyAgentId !== undefined ? { agent_id: legacyAgentId } : {}), tool_response: toolResponse };
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
function outlineCorrelation(records, anchor = records.find(item => item.event === 'PreToolUse')) {
|
|
995
|
+
return internals.correlateOutlineDispatch(records, anchor, '/tmp/prb-worker');
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
test('live qualification accepts the versioned Claude 2.1.312 dispatch/result/worker field contract', () => {
|
|
953
999
|
assert.strictEqual(typeof internals.outlineCaptureMappingValid, 'function', 'missing qualification correlation validator');
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
const records = [
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
{ event: 'SubagentStart', parent_tool_use_id: 'dispatch-1', agent_id: 'worker', session_id: 'coordinator', cwd: '/tmp/worker' },
|
|
960
|
-
{ event: 'PostToolUse', tool_name: 'Task', tool_use_id: 'dispatch-1', agent_id: 'worker', session_id: 'coordinator', interrupted: false },
|
|
961
|
-
];
|
|
962
|
-
assert.strictEqual(internals.outlineCaptureMappingValid(mapping, records), true);
|
|
1000
|
+
assert.deepStrictEqual(internals.OUTLINE_CAPTURE_MAPPING, outlineMappingV2, 'mapping field contract must be explicitly versioned');
|
|
1001
|
+
const pre = qualificationDispatch();
|
|
1002
|
+
const records = [pre, qualificationStart(), qualificationResult(), qualificationStop()];
|
|
1003
|
+
assert.strictEqual(internals.outlineCaptureMappingValid(outlineMappingV2, records), true,
|
|
1004
|
+
'PreToolUse → completed tool_response.agentId → unique Start/Stop is the 2.1.312 host shape');
|
|
963
1005
|
const crossSession = records.map(item => ({ ...item }));
|
|
964
1006
|
crossSession[2].session_id = 'different-session';
|
|
965
|
-
assert.strictEqual(internals.outlineCaptureMappingValid(
|
|
1007
|
+
assert.strictEqual(internals.outlineCaptureMappingValid(outlineMappingV2, crossSession), false,
|
|
966
1008
|
'matching tool IDs across different coordinator sessions do not qualify the live mapping');
|
|
967
1009
|
const wrongRoot = records.map(item => ({ ...item }));
|
|
968
1010
|
wrongRoot[1].cwd = '/tmp/other-worker';
|
|
969
|
-
assert.strictEqual(internals.outlineCaptureMappingValid(
|
|
1011
|
+
assert.strictEqual(internals.outlineCaptureMappingValid(outlineMappingV2, wrongRoot), false);
|
|
1012
|
+
});
|
|
1013
|
+
|
|
1014
|
+
test('capture record retains only explicit outline result and dispatch fields', () => {
|
|
1015
|
+
const pre = provider.buildCaptureRecord({ session_id: 'coordinator', tool_name: 'Task', tool_use_id: 'explicit',
|
|
1016
|
+
tool_input: { prompt: dispatchLine, run_in_background: false } }, 'PreToolUse', '/tmp/prb-worker');
|
|
1017
|
+
assert.strictEqual(pre.run_in_background, false);
|
|
1018
|
+
assert.strictEqual(pre.isolationSet, false);
|
|
1019
|
+
const omitted = provider.buildCaptureRecord({ session_id: 'coordinator', tool_name: 'Agent', tool_use_id: 'omitted',
|
|
1020
|
+
tool_input: { prompt: dispatchLine } }, 'PreToolUse', '/tmp/prb-worker');
|
|
1021
|
+
assert.strictEqual(Object.hasOwn(omitted, 'run_in_background'), false, 'missing background input stays absent');
|
|
1022
|
+
const isolated = provider.buildCaptureRecord({ session_id: 'coordinator', tool_name: 'Agent', tool_use_id: 'isolated',
|
|
1023
|
+
tool_input: { prompt: dispatchLine, run_in_background: false, isolation: null } }, 'PreToolUse', '/tmp/prb-worker');
|
|
1024
|
+
assert.strictEqual(isolated.isolationSet, true, 'present null isolation is not normalized to absence');
|
|
1025
|
+
const result = provider.buildCaptureRecord({ session_id: 'coordinator', tool_name: 'Task', tool_use_id: 'result',
|
|
1026
|
+
tool_response: { agentId: 'worker', status: 'completed' } }, 'PostToolUse', '/tmp/prb-worker');
|
|
1027
|
+
assert.strictEqual(result.tool_response_status, 'completed');
|
|
1028
|
+
const noStatus = provider.buildCaptureRecord({ session_id: 'coordinator', tool_name: 'Task', tool_use_id: 'no-status',
|
|
1029
|
+
tool_response: { agentId: 'worker' } }, 'PostToolUse', '/tmp/prb-worker');
|
|
1030
|
+
assert.strictEqual(Object.hasOwn(noStatus, 'tool_response_status'), false, 'missing result status stays absent');
|
|
1031
|
+
});
|
|
1032
|
+
|
|
1033
|
+
for (const [name, make, reason] of [
|
|
1034
|
+
['concurrent same-session dispatches cannot reuse one worker result', () => {
|
|
1035
|
+
const one = qualificationDispatch({ id: 'dispatch-1' });
|
|
1036
|
+
const two = qualificationDispatch({ id: 'dispatch-2' });
|
|
1037
|
+
return { records: [one, two, qualificationStart(), qualificationResult('dispatch-1'),
|
|
1038
|
+
qualificationResult('dispatch-2'), qualificationStop()], anchor: one };
|
|
1039
|
+
}, 'outline-worker-agent-id-replayed'],
|
|
1040
|
+
['a shared prompt_id cannot replace the target tool_use_id', () => {
|
|
1041
|
+
const one = qualificationDispatch({ id: 'dispatch-1' });
|
|
1042
|
+
const two = qualificationDispatch({ id: 'dispatch-2' });
|
|
1043
|
+
return { records: [one, two, qualificationStart('worker-2'), qualificationResult('dispatch-2', 'worker-2'),
|
|
1044
|
+
qualificationStop('worker-2')], anchor: one };
|
|
1045
|
+
}, 'outline-worker-result-missing-or-ambiguous'],
|
|
1046
|
+
['reordered Stop-before-Start is unresolved', () => {
|
|
1047
|
+
const pre = qualificationDispatch();
|
|
1048
|
+
return { records: [pre, qualificationStop(), qualificationStart(), qualificationResult()], anchor: pre };
|
|
1049
|
+
}, 'outline-worker-stop-before-start'],
|
|
1050
|
+
['Start-before-Pre is unresolved', () => {
|
|
1051
|
+
const pre = qualificationDispatch();
|
|
1052
|
+
return { records: [qualificationStart(), pre, qualificationResult(), qualificationStop()], anchor: pre };
|
|
1053
|
+
}, 'outline-worker-start-before-dispatch'],
|
|
1054
|
+
['missing result is unresolved', () => {
|
|
1055
|
+
const pre = qualificationDispatch();
|
|
1056
|
+
return { records: [pre, qualificationStart(), qualificationStop()], anchor: pre };
|
|
1057
|
+
}, 'outline-worker-result-missing-or-ambiguous'],
|
|
1058
|
+
['duplicate result is unresolved', () => {
|
|
1059
|
+
const pre = qualificationDispatch();
|
|
1060
|
+
return { records: [pre, qualificationStart(), qualificationResult(), qualificationResult(), qualificationStop()], anchor: pre };
|
|
1061
|
+
}, 'outline-worker-result-missing-or-ambiguous'],
|
|
1062
|
+
['duplicate worker start is unresolved', () => {
|
|
1063
|
+
const pre = qualificationDispatch();
|
|
1064
|
+
return { records: [pre, qualificationStart(), qualificationStart(), qualificationResult(), qualificationStop()], anchor: pre };
|
|
1065
|
+
}, 'outline-worker-start-missing-or-ambiguous'],
|
|
1066
|
+
['duplicate worker stop is unresolved', () => {
|
|
1067
|
+
const pre = qualificationDispatch();
|
|
1068
|
+
return { records: [pre, qualificationStart(), qualificationResult(), qualificationStop(), qualificationStop()], anchor: pre };
|
|
1069
|
+
}, 'outline-worker-stop-missing-or-ambiguous'],
|
|
1070
|
+
['conflicting top-level and observed agentId is unresolved', () => {
|
|
1071
|
+
const pre = qualificationDispatch();
|
|
1072
|
+
return { records: [pre, qualificationStart(), qualificationResult('dispatch-1', 'worker', 'completed', { legacyAgentId: 'other-worker' }),
|
|
1073
|
+
qualificationStop()], anchor: pre };
|
|
1074
|
+
}, 'outline-worker-result-agent-id-conflict'],
|
|
1075
|
+
['async_launched is unresolved', () => {
|
|
1076
|
+
const pre = qualificationDispatch();
|
|
1077
|
+
return { records: [pre, qualificationStart(), qualificationResult('dispatch-1', 'worker', 'async_launched'), qualificationStop()], anchor: pre };
|
|
1078
|
+
}, 'outline-worker-result-async'],
|
|
1079
|
+
['absent result status is unresolved', () => {
|
|
1080
|
+
const pre = qualificationDispatch();
|
|
1081
|
+
return { records: [pre, qualificationStart(), qualificationResult('dispatch-1', 'worker', null), qualificationStop()], anchor: pre };
|
|
1082
|
+
}, 'outline-worker-result-status-missing'],
|
|
1083
|
+
['failed result status is unresolved', () => {
|
|
1084
|
+
const pre = qualificationDispatch();
|
|
1085
|
+
return { records: [pre, qualificationStart(), qualificationResult('dispatch-1', 'worker', 'failed'), qualificationStop()], anchor: pre };
|
|
1086
|
+
}, 'outline-worker-result-failed'],
|
|
1087
|
+
['cancelled result status is unresolved', () => {
|
|
1088
|
+
const pre = qualificationDispatch();
|
|
1089
|
+
return { records: [pre, qualificationStart(), qualificationResult('dispatch-1', 'worker', 'cancelled'), qualificationStop()], anchor: pre };
|
|
1090
|
+
}, 'outline-worker-result-failed'],
|
|
1091
|
+
['missing Stop is unresolved', () => {
|
|
1092
|
+
const pre = qualificationDispatch();
|
|
1093
|
+
return { records: [pre, qualificationStart(), qualificationResult()], anchor: pre };
|
|
1094
|
+
}, 'outline-worker-stop-missing-or-ambiguous'],
|
|
1095
|
+
['worker-root mismatch is unresolved', () => {
|
|
1096
|
+
const pre = qualificationDispatch();
|
|
1097
|
+
return { records: [pre, qualificationStart('worker', { cwd: '/tmp/other-worker' }), qualificationResult(), qualificationStop()], anchor: pre };
|
|
1098
|
+
}, 'outline-worker-root-mismatch'],
|
|
1099
|
+
['worktree isolation is unresolved', () => {
|
|
1100
|
+
const pre = qualificationDispatch({ hasIsolation: true, isolation: 'worktree' });
|
|
1101
|
+
return { records: [pre, qualificationStart(), qualificationResult(), qualificationStop()], anchor: pre };
|
|
1102
|
+
}, 'outline-dispatch-isolation-unsupported'],
|
|
1103
|
+
['present null isolation is unresolved', () => {
|
|
1104
|
+
const pre = qualificationDispatch({ hasIsolation: true, isolation: null });
|
|
1105
|
+
return { records: [pre, qualificationStart(), qualificationResult(), qualificationStop()], anchor: pre };
|
|
1106
|
+
}, 'outline-dispatch-isolation-unsupported'],
|
|
1107
|
+
['omitted run_in_background is unresolved', () => {
|
|
1108
|
+
const pre = qualificationDispatch({ hasBackground: false });
|
|
1109
|
+
return { records: [pre, qualificationStart(), qualificationResult(), qualificationStop()], anchor: pre };
|
|
1110
|
+
}, 'outline-dispatch-background-unobserved'],
|
|
1111
|
+
['an Agent PostToolUseFailure invalidates a completed result', () => {
|
|
1112
|
+
const pre = qualificationDispatch();
|
|
1113
|
+
return { records: [pre, qualificationStart(), qualificationResult(),
|
|
1114
|
+
{ event: 'PostToolUseFailure', tool_name: 'Task', tool_use_id: 'dispatch-1', session_id: 'coordinator' }, qualificationStop()], anchor: pre };
|
|
1115
|
+
}, 'outline-worker-result-failed'],
|
|
1116
|
+
]) {
|
|
1117
|
+
test(`outline correlation rejects ${name}`, () => {
|
|
1118
|
+
const { records, anchor } = make();
|
|
1119
|
+
const result = outlineCorrelation(records, anchor);
|
|
1120
|
+
assert.deepStrictEqual(result, { state: 'unresolved', reason });
|
|
1121
|
+
});
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
test('runtime resolution accepts Stop after the completed result through the synthetic qualification seam', () => {
|
|
1125
|
+
const fixture = createOutlineFixture();
|
|
1126
|
+
try {
|
|
1127
|
+
const completed = fixture.finishDraft();
|
|
1128
|
+
assert.strictEqual(completed.result.status, 'accepted', JSON.stringify(completed.result));
|
|
1129
|
+
const workerResultIndex = completed.captures.findIndex(item => item.event === 'PostToolUse'
|
|
1130
|
+
&& item.tool_use_id === fixture.capture.tool_use_id);
|
|
1131
|
+
const workerStopIndex = completed.captures.findIndex(item => item.event === 'SubagentStop'
|
|
1132
|
+
&& item.agent_id === 'draft-worker');
|
|
1133
|
+
assert.ok(workerResultIndex >= 0 && workerStopIndex > workerResultIndex,
|
|
1134
|
+
'the 2.1.312 field shape allows result before Stop; no Stop-before-result condition is implied');
|
|
1135
|
+
} finally { fixture.cleanup(); }
|
|
1136
|
+
});
|
|
1137
|
+
|
|
1138
|
+
test('production export without an outline mapping keeps outline-live-gate-unmet', () => {
|
|
1139
|
+
const fixture = createOutlineFixture();
|
|
1140
|
+
try {
|
|
1141
|
+
const completed = fixture.finishDraft();
|
|
1142
|
+
const production = provider.createClaudeOriginProvider({
|
|
1143
|
+
hostId: 'claude-code', sessionId: fixture.sessionId, workspaceRoot: fixture.root,
|
|
1144
|
+
workflowId: fixture.workflowId, workflowRevision: fixture.workflowRevision, taskId: fixture.taskId,
|
|
1145
|
+
principals: { author: fixture.sessionId, executors: [] },
|
|
1146
|
+
baseline: { root: fixture.root, commit: fixture.baselineCommit }, captures: completed.captures,
|
|
1147
|
+
sessionDir: fixture.sessionDir, taskBinding: fixture.taskBinding,
|
|
1148
|
+
outlineFixture: { expectedPin: fixture.expectedPin },
|
|
1149
|
+
});
|
|
1150
|
+
const result = production.resolveEvent({ reference: `${fixture.capture.ref}#outline-confirmation`, identity: completed.identity });
|
|
1151
|
+
assert.strictEqual(result.status, 'unavailable');
|
|
1152
|
+
assert.ok(result.diagnostics.some(item => item.code === 'outline-live-gate-unmet'), JSON.stringify(result));
|
|
1153
|
+
} finally { fixture.cleanup(); }
|
|
970
1154
|
});
|
|
971
1155
|
|
|
972
1156
|
test('original B and immutable attempt chain survive reconfirmation; stale attempts/tokens never cover new scope', () => {
|
|
@@ -966,7 +966,7 @@ fields through sanitization; a transcript alone, synthetic payload or screenshot
|
|
|
966
966
|
this hook-capture evidence.
|
|
967
967
|
|
|
968
968
|
The origin provider's qualification check MUST resolve that retained evidence reference (or its
|
|
969
|
-
verified export), validate captured host/event kinds, nonempty tool/subagent IDs,
|
|
969
|
+
verified export), validate captured host/event kinds, nonempty tool/subagent IDs, session/tool-use
|
|
970
970
|
correlation and recorded digests, and match the supported host/capture implementation being used.
|
|
971
971
|
Missing, malformed, mismatched or unresolvable qualification evidence returns unavailable and no
|
|
972
972
|
credit. Do not enable qualification from a caller `trusted` flag, environment toggle, populated
|
|
@@ -976,6 +976,22 @@ host capture path exposed the fields; every later event still needs current orig
|
|
|
976
976
|
A retained capture does not authenticate arbitrary local journals or imply E1 GO. Record the live
|
|
977
977
|
gate as unmet until this check actually passes; do not label the docs or deterministic tests as live.
|
|
978
978
|
|
|
979
|
+
For PR-B outline dispatches, the versioned mapping contract is
|
|
980
|
+
`claude-outline-dispatch-mapping/2`. A Task/Agent `PreToolUse` anchor requires `session_id` plus
|
|
981
|
+
`tool_use_id`, one valid marker in `tool_input.prompt`, explicit
|
|
982
|
+
`tool_input.run_in_background === false`, and no `tool_input.isolation` property. Exactly one
|
|
983
|
+
Task/Agent `PostToolUse` with that key must report `tool_response.status === 'completed'` and a
|
|
984
|
+
nonempty `tool_response.agentId`; that observed ID must match exactly one `SubagentStart` and one
|
|
985
|
+
`SubagentStop` with the same session. Journal order requires PreToolUse before SubagentStart before
|
|
986
|
+
SubagentStop; the completed PostToolUse may appear before or after SubagentStop. The Start and Stop
|
|
987
|
+
must each report a canonical `cwd` equal to the marker's worker root. `prompt_id` is shared across
|
|
988
|
+
dispatches, and neither `parent_tool_use_id` nor `interrupted` is an outline dispatch key or
|
|
989
|
+
completion signal. Captured records retain `tool_response.status` as `tool_response_status`, the
|
|
990
|
+
explicit `run_in_background` value, and whether `isolation` was present; absent status or background
|
|
991
|
+
fields remain absent. Async launches and ambiguous or contradictory matches stay unresolved.
|
|
992
|
+
The retained qualification export still has no outline mapping, so production remains
|
|
993
|
+
`outline-live-gate-unmet` and this contract does not enable positive credit.
|
|
994
|
+
|
|
979
995
|
### Workflow credit, review progression and freshness
|
|
980
996
|
|
|
981
997
|
Every ON + supported credit attempt MUST invoke the following current chain, with the same
|
|
@@ -1236,10 +1252,10 @@ flowchart LR
|
|
|
1236
1252
|
| B and task | Keep the workflow-start B commit/root/tree and ordered requirement IDs for every attempt. Config scope must still match B. An attempt cannot reset B to O. |
|
|
1237
1253
|
| Inventory | The coordinator supplies exact bytes and `inventory-pin.json` outside the candidate tree. The pinned source is `report-inv.md`, SHA-256 `2741bd4514d2cd68f57d93a2b3ce94cbe597ca62d30e26f0ca58e882df5b03c6`, run `e1-inv-1`, input SHA `0ef0541c6cae550e8269ba712e03797e0e987dfb`, approval `#325` comment `6436207729`, decision 1. The provider rehashes the bytes and compares every pin field before resolving one row and domain. Missing, stale, duplicate, ambiguous, wrong-target or unavailable evidence is unresolved with no fallback. Synthetic test pins prove parser/mechanism binding only. |
|
|
1238
1254
|
| O and review | O is bound to its commit, tree, target blobs and stored-byte digests. Its `proposed` ADR frontmatter cites one `Inventory row: <id>`, target and domain, and contains no completed Decision text. A host-captured `SubagentStop` review must approve those exact O bytes and the pinned inventory, with a distinct host `agent_id` and correlated Agent/Task result. |
|
|
1239
|
-
| Handoff and dispatch | One immutable attempt handoff binds B, O, scope, inventory, accepted review, coordinator principal, lease and one-use token. Agent/Task dispatch accepts exactly one `HARNESS_OUTLINE_DISPATCH_V1` block in `tool_input.prompt`, whose token must equal the handoff token; it does not search free-form text or use another field or cwd as a fallback. Before worker start, PreToolUse verifies the handoff, review, pin, exclusive lease, canonical root, HEAD/tree at O and clean index/worktree including untracked files; it creates a write-once handoff-digest seal, stores validation digests/reasons, then consumes the token by exclusive creation. Every predecessor seal, seam and token remains bound to the original handoff bytes, so an overwritten predecessor invalidates the chain. |
|
|
1240
|
-
| Worker and D |
|
|
1255
|
+
| Handoff and dispatch | One immutable attempt handoff binds B, O, scope, inventory, accepted review, coordinator principal, lease and one-use token. Agent/Task dispatch accepts exactly one `HARNESS_OUTLINE_DISPATCH_V1` block in `tool_input.prompt`, whose token must equal the handoff token; it does not search free-form text or use another field or cwd as a fallback. The outline anchor also requires explicit `run_in_background === false` and absence of any `isolation` key, including null or empty values. Before worker start, PreToolUse verifies the handoff, review, pin, exclusive lease, canonical root, HEAD/tree at O and clean index/worktree including untracked files; it creates a write-once handoff-digest seal, stores validation digests/reasons, then consumes the token by exclusive creation. Every predecessor seal, seam and token remains bound to the original handoff bytes, so an overwritten predecessor invalidates the chain. |
|
|
1256
|
+
| Worker and D | The dispatch key is `(session_id, tool_use_id)`. Exactly one matching Task/Agent result must carry `tool_response.status === 'completed'`; its observed `tool_response.agentId` must map to exactly one `SubagentStart` and one `SubagentStop` with the same `session_id` and `agent_id`. Require PreToolUse < SubagentStart < SubagentStop in journal order, but do not order the completed result against Stop. Both worker events need exact canonical `cwd === workerRoot`. `prompt_id` and `parent_tool_use_id` are not correlation keys; `interrupted` is not completion proof. Async launches, failures, cancellation, missing/duplicate events, replayed IDs, worktree isolation and root mismatch stay unresolved. D is checked against the current candidate separately from O. O must be a strict ancestor of D. A squash may retain the cycle only when a coordinator correspondence record proves identical target blobs between retained pre-merge D and the merged tree; changed bytes need fresh final quality review. |
|
|
1241
1257
|
| Checker outcome | `outline-confirmation` has its own pass/unresolved lane. Generic command success and waivers never satisfy it. No D is incomplete/unresolved. Its result is separate from the aggregate result over every MUST derived from original B→D; an outline pass never waives lint, conflict, quality review, requirement-sync or other MUSTs. |
|
|
1242
1258
|
|
|
1243
|
-
Capture and validation failures stay fail-open to host tools and withhold evidence credit. Unclassified shell commands and writes outside the correlated worker/root remain unresolved. Retries use a fresh attempt, token, clean worktree and immutable handoff; only one non-superseded leaf is current.
|
|
1259
|
+
Capture and validation failures stay fail-open to host tools and withhold evidence credit. Unclassified shell commands and writes outside the correlated worker/root remain unresolved. Retries use a fresh attempt, token, clean worktree and immutable handoff; only one non-superseded leaf is current. An identical same-tool-ID/input callback may reuse its stored seam, but repeated PreToolUse records remain ambiguous for evidence credit; changed input or token reuse is unresolved. OpenCode and Codex remain unsupported for positive outline credit.
|
|
1244
1260
|
|
|
1245
|
-
The current retained Claude qualification does not establish the
|
|
1261
|
+
The current retained Claude qualification does not establish the versioned `claude-outline-dispatch-mapping/2` contract. The outline provider therefore grants no positive live-host credit until a retained Claude capture validates the prompt, coordinator, result status/agent ID, explicit non-background boundary, isolation absence and exact worker-root mapping. Deterministic fixtures exercise the real hook/provider/adapter/checker/pilot path with a synthetic inventory pin; they are mechanism evidence, not live-host behavior or E1 readiness.
|
|
@@ -52,6 +52,19 @@ const DOC_CHECK_BOOLEAN_FLAGS = new Set(['--conflict-assessment']);
|
|
|
52
52
|
const DOC_CHECK_SCHEMA = 'harness.doc-check.check.v1';
|
|
53
53
|
const DOC_CHECK_CONFLICT_SCHEMA = 'harness.doc-check.conflict-assessment.v1';
|
|
54
54
|
const OUTLINE_DISPATCH_SCHEMA = 'harness.outline-dispatch.v1';
|
|
55
|
+
const OUTLINE_CAPTURE_MAPPING = Object.freeze({
|
|
56
|
+
version: 'claude-outline-dispatch-mapping/2',
|
|
57
|
+
promptField: 'tool_input.prompt',
|
|
58
|
+
dispatchKey: 'session_id+tool_use_id',
|
|
59
|
+
runInBackgroundField: 'tool_input.run_in_background',
|
|
60
|
+
isolationPresenceField: 'tool_input.isolation',
|
|
61
|
+
resultAgentIdField: 'tool_response.agentId',
|
|
62
|
+
resultStatusField: 'tool_response.status',
|
|
63
|
+
workerStartEvent: 'SubagentStart',
|
|
64
|
+
workerStopEvent: 'SubagentStop',
|
|
65
|
+
workerIdentity: 'session_id+agent_id',
|
|
66
|
+
workerRootField: 'cwd',
|
|
67
|
+
});
|
|
55
68
|
const OUTLINE_SEAM_SCHEMA = 'harness.outline-dispatch-seam.v1';
|
|
56
69
|
const OUTLINE_HANDOFF_SCHEMA = 'harness.outline-handoff.v1';
|
|
57
70
|
const OUTLINE_LEASE_SCHEMA = 'harness.outline-writer-lease.v1';
|
|
@@ -1163,28 +1176,125 @@ function validateOutlineCaptureQualification(input, expectedHost = HOST_ID) {
|
|
|
1163
1176
|
}
|
|
1164
1177
|
|
|
1165
1178
|
function outlineCaptureMappingValid(mapping, records) {
|
|
1166
|
-
if (!recordOf(mapping) || mapping
|
|
1167
|
-
|| mapping.coordinatorPrincipalField !== 'session_id' || mapping.synchronousPreToolUse !== true
|
|
1168
|
-
|| mapping.workerStartDispatchField !== 'parent_tool_use_id' || mapping.workerStartRootField !== 'cwd'
|
|
1169
|
-
|| mapping.taskResultDispatchField !== 'tool_use_id' || !Array.isArray(records)) return false;
|
|
1179
|
+
if (!recordOf(mapping) || captureJson(mapping) !== captureJson(OUTLINE_CAPTURE_MAPPING) || !Array.isArray(records)) return false;
|
|
1170
1180
|
const pres = records.filter(item => recordOf(item) && item.event === 'PreToolUse'
|
|
1171
1181
|
&& ['Task', 'Agent'].includes(item.tool_name) && recordOf(item.tool_input)
|
|
1172
1182
|
&& typeof item.tool_input.prompt === 'string' && textOf(item.session_id) && textOf(item.tool_use_id));
|
|
1173
|
-
return pres.some(pre =>
|
|
1183
|
+
return pres.some(pre => correlateOutlineDispatch(records, pre).state === 'accepted');
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
function outlineCorrelationFailure(reason) { return { state: 'unresolved', reason }; }
|
|
1187
|
+
|
|
1188
|
+
function outlineDispatchKeyMatches(item, pre) {
|
|
1189
|
+
return recordOf(item) && item.session_id === pre.session_id && item.tool_use_id === pre.tool_use_id;
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
function outlineResultAgentId(result) {
|
|
1193
|
+
if (recordOf(result.tool_response)) {
|
|
1194
|
+
const observed = result.tool_response.agentId;
|
|
1195
|
+
if (textOf(result.agent_id) && textOf(observed) && result.agent_id !== observed) {
|
|
1196
|
+
return { state: 'unresolved', reason: 'outline-worker-result-agent-id-conflict' };
|
|
1197
|
+
}
|
|
1198
|
+
return { state: textOf(observed) ? 'accepted' : 'unresolved', agentId: observed,
|
|
1199
|
+
...(textOf(observed) ? {} : { reason: 'outline-worker-result-agent-id-missing' }) };
|
|
1200
|
+
}
|
|
1201
|
+
return textOf(result.agent_id)
|
|
1202
|
+
? { state: 'accepted', agentId: result.agent_id }
|
|
1203
|
+
: { state: 'unresolved', reason: 'outline-worker-result-agent-id-missing' };
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
function outlineResultStatus(result) {
|
|
1207
|
+
if (recordOf(result.tool_response)) return Object.prototype.hasOwnProperty.call(result.tool_response, 'status')
|
|
1208
|
+
? { present: true, value: result.tool_response.status } : { present: false };
|
|
1209
|
+
return Object.prototype.hasOwnProperty.call(result, 'tool_response_status')
|
|
1210
|
+
? { present: true, value: result.tool_response_status } : { present: false };
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
function correlateOutlineDispatch(records, anchor, expectedWorkerRoot) {
|
|
1214
|
+
if (!Array.isArray(records)) return outlineCorrelationFailure('outline-capture-records-missing');
|
|
1215
|
+
if (!recordOf(anchor) || anchor.event !== 'PreToolUse' || !['Task', 'Agent'].includes(anchor.tool_name)) {
|
|
1216
|
+
return outlineCorrelationFailure('outline-dispatch-anchor-invalid');
|
|
1217
|
+
}
|
|
1218
|
+
if (!textOf(anchor.session_id) || !textOf(anchor.tool_use_id)) return outlineCorrelationFailure('outline-dispatch-key-missing');
|
|
1219
|
+
const pres = records.filter(item => recordOf(item) && item.event === 'PreToolUse'
|
|
1220
|
+
&& ['Task', 'Agent'].includes(item.tool_name) && outlineDispatchKeyMatches(item, anchor));
|
|
1221
|
+
if (pres.length !== 1) return outlineCorrelationFailure('outline-dispatch-anchor-missing-or-ambiguous');
|
|
1222
|
+
const pre = pres[0];
|
|
1223
|
+
let workerRoot = expectedWorkerRoot;
|
|
1224
|
+
if (recordOf(pre.tool_input)) {
|
|
1174
1225
|
const marker = parseOutlineDispatchPrompt(pre.tool_input.prompt);
|
|
1175
|
-
if (!marker) return
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
if (
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1226
|
+
if (!marker) return outlineCorrelationFailure('outline-dispatch-marker-invalid');
|
|
1227
|
+
if (workerRoot !== undefined && !samePathKey(marker.workerRoot, workerRoot)) {
|
|
1228
|
+
return outlineCorrelationFailure('outline-dispatch-worker-root-mismatch');
|
|
1229
|
+
}
|
|
1230
|
+
workerRoot = marker.workerRoot;
|
|
1231
|
+
if (!Object.prototype.hasOwnProperty.call(pre.tool_input, 'run_in_background')) {
|
|
1232
|
+
return outlineCorrelationFailure('outline-dispatch-background-unobserved');
|
|
1233
|
+
}
|
|
1234
|
+
if (pre.tool_input.run_in_background !== false) {
|
|
1235
|
+
return outlineCorrelationFailure('outline-dispatch-background-enabled');
|
|
1236
|
+
}
|
|
1237
|
+
if (Object.prototype.hasOwnProperty.call(pre.tool_input, 'isolation')) {
|
|
1238
|
+
return outlineCorrelationFailure('outline-dispatch-isolation-unsupported');
|
|
1239
|
+
}
|
|
1240
|
+
} else {
|
|
1241
|
+
if (!recordOf(pre.outlineDispatch) || pre.outlineDispatch.state !== 'accepted' || !textOf(pre.outlineDispatch.workerRoot)) {
|
|
1242
|
+
return outlineCorrelationFailure('outline-dispatch-marker-unobserved');
|
|
1243
|
+
}
|
|
1244
|
+
if (workerRoot !== undefined && !samePathKey(pre.outlineDispatch.workerRoot, workerRoot)) {
|
|
1245
|
+
return outlineCorrelationFailure('outline-dispatch-worker-root-mismatch');
|
|
1246
|
+
}
|
|
1247
|
+
workerRoot = pre.outlineDispatch.workerRoot;
|
|
1248
|
+
if (!Object.prototype.hasOwnProperty.call(pre, 'run_in_background')) {
|
|
1249
|
+
return outlineCorrelationFailure('outline-dispatch-background-unobserved');
|
|
1250
|
+
}
|
|
1251
|
+
if (pre.run_in_background !== false) return outlineCorrelationFailure('outline-dispatch-background-enabled');
|
|
1252
|
+
if (!Object.prototype.hasOwnProperty.call(pre, 'isolationSet')) {
|
|
1253
|
+
return outlineCorrelationFailure('outline-dispatch-isolation-unobserved');
|
|
1254
|
+
}
|
|
1255
|
+
if (pre.isolationSet !== false) return outlineCorrelationFailure('outline-dispatch-isolation-unsupported');
|
|
1256
|
+
}
|
|
1257
|
+
if (!textOf(workerRoot) || !path.isAbsolute(workerRoot)) return outlineCorrelationFailure('outline-dispatch-worker-root-unobserved');
|
|
1258
|
+
|
|
1259
|
+
const failures = records.filter(item => outlineDispatchKeyMatches(item, pre) && item.event === 'PostToolUseFailure');
|
|
1260
|
+
if (failures.length) return outlineCorrelationFailure('outline-worker-result-failed');
|
|
1261
|
+
const results = records.filter(item => outlineDispatchKeyMatches(item, pre) && item.event === 'PostToolUse'
|
|
1262
|
+
&& ['Task', 'Agent'].includes(item.tool_name));
|
|
1263
|
+
if (results.length !== 1) return outlineCorrelationFailure('outline-worker-result-missing-or-ambiguous');
|
|
1264
|
+
const result = results[0];
|
|
1265
|
+
const status = outlineResultStatus(result);
|
|
1266
|
+
if (!status.present || status.value === undefined) return outlineCorrelationFailure('outline-worker-result-status-missing');
|
|
1267
|
+
if (status.value === 'async_launched') return outlineCorrelationFailure('outline-worker-result-async');
|
|
1268
|
+
if (status.value !== 'completed') return outlineCorrelationFailure('outline-worker-result-failed');
|
|
1269
|
+
const observedAgent = outlineResultAgentId(result);
|
|
1270
|
+
if (observedAgent.state !== 'accepted') return outlineCorrelationFailure(observedAgent.reason);
|
|
1271
|
+
const agentId = observedAgent.agentId;
|
|
1272
|
+
const sameAgentResults = records.filter(item => recordOf(item) && item.event === 'PostToolUse'
|
|
1273
|
+
&& ['Task', 'Agent'].includes(item.tool_name) && item.session_id === pre.session_id
|
|
1274
|
+
&& outlineResultAgentId(item).state === 'accepted' && outlineResultAgentId(item).agentId === agentId);
|
|
1275
|
+
if (sameAgentResults.length !== 1) return outlineCorrelationFailure('outline-worker-agent-id-replayed');
|
|
1276
|
+
|
|
1277
|
+
const starts = records.filter(item => recordOf(item) && item.event === 'SubagentStart'
|
|
1278
|
+
&& item.session_id === pre.session_id && item.agent_id === agentId);
|
|
1279
|
+
if (starts.length !== 1) return outlineCorrelationFailure('outline-worker-start-missing-or-ambiguous');
|
|
1280
|
+
const stops = records.filter(item => recordOf(item) && item.event === 'SubagentStop'
|
|
1281
|
+
&& item.session_id === pre.session_id && item.agent_id === agentId);
|
|
1282
|
+
if (stops.length !== 1) return outlineCorrelationFailure('outline-worker-stop-missing-or-ambiguous');
|
|
1283
|
+
const start = starts[0];
|
|
1284
|
+
const stop = stops[0];
|
|
1285
|
+
const preIndex = records.indexOf(pre);
|
|
1286
|
+
const startIndex = records.indexOf(start);
|
|
1287
|
+
const stopIndex = records.indexOf(stop);
|
|
1288
|
+
if (preIndex < 0 || startIndex < 0 || stopIndex < 0) return outlineCorrelationFailure('outline-worker-journal-position-unavailable');
|
|
1289
|
+
if (startIndex <= preIndex) return outlineCorrelationFailure('outline-worker-start-before-dispatch');
|
|
1290
|
+
if (stopIndex <= startIndex) return outlineCorrelationFailure('outline-worker-stop-before-start');
|
|
1291
|
+
for (const workerEvent of [start, stop]) {
|
|
1292
|
+
if (!textOf(workerEvent.cwd) || !path.isAbsolute(workerEvent.cwd)) {
|
|
1293
|
+
return outlineCorrelationFailure('outline-worker-root-unobserved');
|
|
1294
|
+
}
|
|
1295
|
+
if (!samePathKey(workerEvent.cwd, workerRoot)) return outlineCorrelationFailure('outline-worker-root-mismatch');
|
|
1296
|
+
}
|
|
1297
|
+
return { state: 'accepted', pre, result, start, stop, workerRoot, agentId };
|
|
1188
1298
|
}
|
|
1189
1299
|
|
|
1190
1300
|
function verifyOutlineMergeCorrespondence({ root, outlineCommit, draftCommit, mergedCommit, targetPaths, record } = {}) {
|
|
@@ -1335,20 +1445,10 @@ function resolveOutlineEvidence({ safeInput, capture, captures, request, originR
|
|
|
1335
1445
|
|| review.reviewDigest !== seam.reviewDigest || review.reviewerPrincipal !== seam.reviewerPrincipal) {
|
|
1336
1446
|
return fail(review.reason || 'outline-review-changed-after-dispatch');
|
|
1337
1447
|
}
|
|
1338
|
-
const
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
const
|
|
1342
|
-
let workerRoot;
|
|
1343
|
-
try { workerRoot = canonicalOutlineRoot(worker.cwd); }
|
|
1344
|
-
catch { return fail('outline-worker-root-unobserved'); }
|
|
1345
|
-
if (!sameCanonicalPath(workerRoot, seam.workerRoot)) return fail('outline-worker-root-mismatch');
|
|
1346
|
-
const results = (Array.isArray(captures) ? captures : []).filter(item => recordOf(item)
|
|
1347
|
-
&& item.event === 'PostToolUse' && ['Task', 'Agent'].includes(item.tool_name)
|
|
1348
|
-
&& item.tool_use_id === cap.tool_use_id && item.session_id === cap.session_id);
|
|
1349
|
-
if (results.length !== 1 || results[0].agent_id !== worker.agent_id || results[0].interrupted !== false) {
|
|
1350
|
-
return fail('outline-worker-result-missing-or-mismatched');
|
|
1351
|
-
}
|
|
1448
|
+
const correlation = correlateOutlineDispatch(captures, cap, seam.workerRoot);
|
|
1449
|
+
if (correlation.state !== 'accepted') return fail(correlation.reason);
|
|
1450
|
+
const worker = correlation.start;
|
|
1451
|
+
const workerRoot = correlation.workerRoot;
|
|
1352
1452
|
if (review.reviewerPrincipal === worker.agent_id) return fail('outline-reviewer-is-draft-worker');
|
|
1353
1453
|
const principals = { author: safeInput.sessionId, executors: journalExecutors(captures, safeInput.sessionId) };
|
|
1354
1454
|
if (!reviewRecordIndependent(review.review, captures, principals)) return fail('outline-review-not-independent');
|
|
@@ -1949,6 +2049,8 @@ function buildCaptureRecord(payload, eventName, workspaceRoot) {
|
|
|
1949
2049
|
if (event === 'PreToolUse' && PRE_CAPTURE_TOOLS.indexOf(payload.tool_name) === -1) return null;
|
|
1950
2050
|
const toolInput = recordOf(payload.tool_input) ? payload.tool_input : {};
|
|
1951
2051
|
const toolResponse = payload.tool_response;
|
|
2052
|
+
const outlinePre = event === 'PreToolUse' && ['Task', 'Agent'].includes(payload.tool_name);
|
|
2053
|
+
const outlineResult = event === 'PostToolUse' && ['Task', 'Agent'].includes(payload.tool_name);
|
|
1952
2054
|
// Demonstrated alias mapping (doc: "Host result aliases such as agentId
|
|
1953
2055
|
// require a demonstrated capture mapping to the same subagent identity"):
|
|
1954
2056
|
// the PostToolUse Task/Agent result surface reports the subagent id as
|
|
@@ -1961,17 +2063,28 @@ function buildCaptureRecord(payload, eventName, workspaceRoot) {
|
|
|
1961
2063
|
ref: captureId,
|
|
1962
2064
|
event,
|
|
1963
2065
|
command: typeof toolInput.command === 'string' ? toolInput.command : '',
|
|
1964
|
-
cwd: typeof payload.cwd === 'string' && payload.cwd ? payload.cwd : (typeof workspaceRoot === 'string' && workspaceRoot ? workspaceRoot : ''),
|
|
1965
2066
|
tool_use_id: typeof payload.tool_use_id === 'string' ? payload.tool_use_id : '',
|
|
1966
2067
|
session_id: payload.session_id === undefined && payload.sessionId === undefined ? '' : String(payload.session_id !== undefined ? payload.session_id : payload.sessionId),
|
|
1967
2068
|
responseDigest: captureDigest(toolResponse),
|
|
1968
2069
|
responseRef: `host-payload/${event}/${captureId}`,
|
|
1969
2070
|
};
|
|
2071
|
+
if (typeof payload.cwd === 'string' && payload.cwd) record.cwd = payload.cwd;
|
|
2072
|
+
else if (!['SubagentStart', 'SubagentStop'].includes(event) && typeof workspaceRoot === 'string' && workspaceRoot) record.cwd = workspaceRoot;
|
|
1970
2073
|
if (payload.prompt_id !== undefined) record.prompt_id = payload.prompt_id;
|
|
1971
2074
|
if (payload.parent_tool_use_id !== undefined) record.parent_tool_use_id = payload.parent_tool_use_id;
|
|
1972
2075
|
// The launch tool name separates the Agent/Task result surface and the subagent launch from executed tools (F4/F5).
|
|
1973
2076
|
if (typeof payload.tool_name === 'string') record.tool_name = payload.tool_name;
|
|
1974
|
-
|
|
2077
|
+
if (outlinePre) {
|
|
2078
|
+
if (Object.prototype.hasOwnProperty.call(toolInput, 'run_in_background')) record.run_in_background = toolInput.run_in_background;
|
|
2079
|
+
record.isolationSet = Object.prototype.hasOwnProperty.call(toolInput, 'isolation');
|
|
2080
|
+
}
|
|
2081
|
+
if (outlineResult && recordOf(toolResponse) && Object.prototype.hasOwnProperty.call(toolResponse, 'status')) {
|
|
2082
|
+
record.tool_response_status = toolResponse.status;
|
|
2083
|
+
}
|
|
2084
|
+
const resultAgentConflict = outlineResult && textOf(payload.agent_id) && textOf(responseAliasAgentId)
|
|
2085
|
+
&& payload.agent_id !== responseAliasAgentId;
|
|
2086
|
+
const agentId = outlineResult ? (resultAgentConflict ? undefined : responseAliasAgentId)
|
|
2087
|
+
: payload.agent_id !== undefined ? payload.agent_id : responseAliasAgentId;
|
|
1975
2088
|
if (agentId !== undefined) {
|
|
1976
2089
|
record.agent_id = agentId;
|
|
1977
2090
|
if (payload.agent_type !== undefined) record.agent_type = payload.agent_type;
|
|
@@ -2132,4 +2245,4 @@ module.exports = { createClaudeOriginProvider, createOpencodeOriginProvider, bui
|
|
|
2132
2245
|
_internals: { captureJson, captureDigest, digestText, canonicalCapture, parseOutlineDispatchPrompt,
|
|
2133
2246
|
parseInventoryPinRows, validateOutlineInventoryPin, resolveInventoryRow, parseOutlineCitation, resolveOutlineAttempt,
|
|
2134
2247
|
captureOutlineDispatch, resolveOutlineEvidence, verifyOutlineMergeCorrespondence, validateOutlineCaptureQualification,
|
|
2135
|
-
outlineCaptureMappingValid, OUTLINE_INVENTORY_PIN } };
|
|
2248
|
+
outlineCaptureMappingValid, correlateOutlineDispatch, OUTLINE_CAPTURE_MAPPING, OUTLINE_INVENTORY_PIN } };
|
package/package.json
CHANGED
|
@@ -52,6 +52,19 @@ const DOC_CHECK_BOOLEAN_FLAGS = new Set(['--conflict-assessment']);
|
|
|
52
52
|
const DOC_CHECK_SCHEMA = 'harness.doc-check.check.v1';
|
|
53
53
|
const DOC_CHECK_CONFLICT_SCHEMA = 'harness.doc-check.conflict-assessment.v1';
|
|
54
54
|
const OUTLINE_DISPATCH_SCHEMA = 'harness.outline-dispatch.v1';
|
|
55
|
+
const OUTLINE_CAPTURE_MAPPING = Object.freeze({
|
|
56
|
+
version: 'claude-outline-dispatch-mapping/2',
|
|
57
|
+
promptField: 'tool_input.prompt',
|
|
58
|
+
dispatchKey: 'session_id+tool_use_id',
|
|
59
|
+
runInBackgroundField: 'tool_input.run_in_background',
|
|
60
|
+
isolationPresenceField: 'tool_input.isolation',
|
|
61
|
+
resultAgentIdField: 'tool_response.agentId',
|
|
62
|
+
resultStatusField: 'tool_response.status',
|
|
63
|
+
workerStartEvent: 'SubagentStart',
|
|
64
|
+
workerStopEvent: 'SubagentStop',
|
|
65
|
+
workerIdentity: 'session_id+agent_id',
|
|
66
|
+
workerRootField: 'cwd',
|
|
67
|
+
});
|
|
55
68
|
const OUTLINE_SEAM_SCHEMA = 'harness.outline-dispatch-seam.v1';
|
|
56
69
|
const OUTLINE_HANDOFF_SCHEMA = 'harness.outline-handoff.v1';
|
|
57
70
|
const OUTLINE_LEASE_SCHEMA = 'harness.outline-writer-lease.v1';
|
|
@@ -1163,28 +1176,125 @@ function validateOutlineCaptureQualification(input, expectedHost = HOST_ID) {
|
|
|
1163
1176
|
}
|
|
1164
1177
|
|
|
1165
1178
|
function outlineCaptureMappingValid(mapping, records) {
|
|
1166
|
-
if (!recordOf(mapping) || mapping
|
|
1167
|
-
|| mapping.coordinatorPrincipalField !== 'session_id' || mapping.synchronousPreToolUse !== true
|
|
1168
|
-
|| mapping.workerStartDispatchField !== 'parent_tool_use_id' || mapping.workerStartRootField !== 'cwd'
|
|
1169
|
-
|| mapping.taskResultDispatchField !== 'tool_use_id' || !Array.isArray(records)) return false;
|
|
1179
|
+
if (!recordOf(mapping) || captureJson(mapping) !== captureJson(OUTLINE_CAPTURE_MAPPING) || !Array.isArray(records)) return false;
|
|
1170
1180
|
const pres = records.filter(item => recordOf(item) && item.event === 'PreToolUse'
|
|
1171
1181
|
&& ['Task', 'Agent'].includes(item.tool_name) && recordOf(item.tool_input)
|
|
1172
1182
|
&& typeof item.tool_input.prompt === 'string' && textOf(item.session_id) && textOf(item.tool_use_id));
|
|
1173
|
-
return pres.some(pre =>
|
|
1183
|
+
return pres.some(pre => correlateOutlineDispatch(records, pre).state === 'accepted');
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
function outlineCorrelationFailure(reason) { return { state: 'unresolved', reason }; }
|
|
1187
|
+
|
|
1188
|
+
function outlineDispatchKeyMatches(item, pre) {
|
|
1189
|
+
return recordOf(item) && item.session_id === pre.session_id && item.tool_use_id === pre.tool_use_id;
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
function outlineResultAgentId(result) {
|
|
1193
|
+
if (recordOf(result.tool_response)) {
|
|
1194
|
+
const observed = result.tool_response.agentId;
|
|
1195
|
+
if (textOf(result.agent_id) && textOf(observed) && result.agent_id !== observed) {
|
|
1196
|
+
return { state: 'unresolved', reason: 'outline-worker-result-agent-id-conflict' };
|
|
1197
|
+
}
|
|
1198
|
+
return { state: textOf(observed) ? 'accepted' : 'unresolved', agentId: observed,
|
|
1199
|
+
...(textOf(observed) ? {} : { reason: 'outline-worker-result-agent-id-missing' }) };
|
|
1200
|
+
}
|
|
1201
|
+
return textOf(result.agent_id)
|
|
1202
|
+
? { state: 'accepted', agentId: result.agent_id }
|
|
1203
|
+
: { state: 'unresolved', reason: 'outline-worker-result-agent-id-missing' };
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
function outlineResultStatus(result) {
|
|
1207
|
+
if (recordOf(result.tool_response)) return Object.prototype.hasOwnProperty.call(result.tool_response, 'status')
|
|
1208
|
+
? { present: true, value: result.tool_response.status } : { present: false };
|
|
1209
|
+
return Object.prototype.hasOwnProperty.call(result, 'tool_response_status')
|
|
1210
|
+
? { present: true, value: result.tool_response_status } : { present: false };
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
function correlateOutlineDispatch(records, anchor, expectedWorkerRoot) {
|
|
1214
|
+
if (!Array.isArray(records)) return outlineCorrelationFailure('outline-capture-records-missing');
|
|
1215
|
+
if (!recordOf(anchor) || anchor.event !== 'PreToolUse' || !['Task', 'Agent'].includes(anchor.tool_name)) {
|
|
1216
|
+
return outlineCorrelationFailure('outline-dispatch-anchor-invalid');
|
|
1217
|
+
}
|
|
1218
|
+
if (!textOf(anchor.session_id) || !textOf(anchor.tool_use_id)) return outlineCorrelationFailure('outline-dispatch-key-missing');
|
|
1219
|
+
const pres = records.filter(item => recordOf(item) && item.event === 'PreToolUse'
|
|
1220
|
+
&& ['Task', 'Agent'].includes(item.tool_name) && outlineDispatchKeyMatches(item, anchor));
|
|
1221
|
+
if (pres.length !== 1) return outlineCorrelationFailure('outline-dispatch-anchor-missing-or-ambiguous');
|
|
1222
|
+
const pre = pres[0];
|
|
1223
|
+
let workerRoot = expectedWorkerRoot;
|
|
1224
|
+
if (recordOf(pre.tool_input)) {
|
|
1174
1225
|
const marker = parseOutlineDispatchPrompt(pre.tool_input.prompt);
|
|
1175
|
-
if (!marker) return
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
if (
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1226
|
+
if (!marker) return outlineCorrelationFailure('outline-dispatch-marker-invalid');
|
|
1227
|
+
if (workerRoot !== undefined && !samePathKey(marker.workerRoot, workerRoot)) {
|
|
1228
|
+
return outlineCorrelationFailure('outline-dispatch-worker-root-mismatch');
|
|
1229
|
+
}
|
|
1230
|
+
workerRoot = marker.workerRoot;
|
|
1231
|
+
if (!Object.prototype.hasOwnProperty.call(pre.tool_input, 'run_in_background')) {
|
|
1232
|
+
return outlineCorrelationFailure('outline-dispatch-background-unobserved');
|
|
1233
|
+
}
|
|
1234
|
+
if (pre.tool_input.run_in_background !== false) {
|
|
1235
|
+
return outlineCorrelationFailure('outline-dispatch-background-enabled');
|
|
1236
|
+
}
|
|
1237
|
+
if (Object.prototype.hasOwnProperty.call(pre.tool_input, 'isolation')) {
|
|
1238
|
+
return outlineCorrelationFailure('outline-dispatch-isolation-unsupported');
|
|
1239
|
+
}
|
|
1240
|
+
} else {
|
|
1241
|
+
if (!recordOf(pre.outlineDispatch) || pre.outlineDispatch.state !== 'accepted' || !textOf(pre.outlineDispatch.workerRoot)) {
|
|
1242
|
+
return outlineCorrelationFailure('outline-dispatch-marker-unobserved');
|
|
1243
|
+
}
|
|
1244
|
+
if (workerRoot !== undefined && !samePathKey(pre.outlineDispatch.workerRoot, workerRoot)) {
|
|
1245
|
+
return outlineCorrelationFailure('outline-dispatch-worker-root-mismatch');
|
|
1246
|
+
}
|
|
1247
|
+
workerRoot = pre.outlineDispatch.workerRoot;
|
|
1248
|
+
if (!Object.prototype.hasOwnProperty.call(pre, 'run_in_background')) {
|
|
1249
|
+
return outlineCorrelationFailure('outline-dispatch-background-unobserved');
|
|
1250
|
+
}
|
|
1251
|
+
if (pre.run_in_background !== false) return outlineCorrelationFailure('outline-dispatch-background-enabled');
|
|
1252
|
+
if (!Object.prototype.hasOwnProperty.call(pre, 'isolationSet')) {
|
|
1253
|
+
return outlineCorrelationFailure('outline-dispatch-isolation-unobserved');
|
|
1254
|
+
}
|
|
1255
|
+
if (pre.isolationSet !== false) return outlineCorrelationFailure('outline-dispatch-isolation-unsupported');
|
|
1256
|
+
}
|
|
1257
|
+
if (!textOf(workerRoot) || !path.isAbsolute(workerRoot)) return outlineCorrelationFailure('outline-dispatch-worker-root-unobserved');
|
|
1258
|
+
|
|
1259
|
+
const failures = records.filter(item => outlineDispatchKeyMatches(item, pre) && item.event === 'PostToolUseFailure');
|
|
1260
|
+
if (failures.length) return outlineCorrelationFailure('outline-worker-result-failed');
|
|
1261
|
+
const results = records.filter(item => outlineDispatchKeyMatches(item, pre) && item.event === 'PostToolUse'
|
|
1262
|
+
&& ['Task', 'Agent'].includes(item.tool_name));
|
|
1263
|
+
if (results.length !== 1) return outlineCorrelationFailure('outline-worker-result-missing-or-ambiguous');
|
|
1264
|
+
const result = results[0];
|
|
1265
|
+
const status = outlineResultStatus(result);
|
|
1266
|
+
if (!status.present || status.value === undefined) return outlineCorrelationFailure('outline-worker-result-status-missing');
|
|
1267
|
+
if (status.value === 'async_launched') return outlineCorrelationFailure('outline-worker-result-async');
|
|
1268
|
+
if (status.value !== 'completed') return outlineCorrelationFailure('outline-worker-result-failed');
|
|
1269
|
+
const observedAgent = outlineResultAgentId(result);
|
|
1270
|
+
if (observedAgent.state !== 'accepted') return outlineCorrelationFailure(observedAgent.reason);
|
|
1271
|
+
const agentId = observedAgent.agentId;
|
|
1272
|
+
const sameAgentResults = records.filter(item => recordOf(item) && item.event === 'PostToolUse'
|
|
1273
|
+
&& ['Task', 'Agent'].includes(item.tool_name) && item.session_id === pre.session_id
|
|
1274
|
+
&& outlineResultAgentId(item).state === 'accepted' && outlineResultAgentId(item).agentId === agentId);
|
|
1275
|
+
if (sameAgentResults.length !== 1) return outlineCorrelationFailure('outline-worker-agent-id-replayed');
|
|
1276
|
+
|
|
1277
|
+
const starts = records.filter(item => recordOf(item) && item.event === 'SubagentStart'
|
|
1278
|
+
&& item.session_id === pre.session_id && item.agent_id === agentId);
|
|
1279
|
+
if (starts.length !== 1) return outlineCorrelationFailure('outline-worker-start-missing-or-ambiguous');
|
|
1280
|
+
const stops = records.filter(item => recordOf(item) && item.event === 'SubagentStop'
|
|
1281
|
+
&& item.session_id === pre.session_id && item.agent_id === agentId);
|
|
1282
|
+
if (stops.length !== 1) return outlineCorrelationFailure('outline-worker-stop-missing-or-ambiguous');
|
|
1283
|
+
const start = starts[0];
|
|
1284
|
+
const stop = stops[0];
|
|
1285
|
+
const preIndex = records.indexOf(pre);
|
|
1286
|
+
const startIndex = records.indexOf(start);
|
|
1287
|
+
const stopIndex = records.indexOf(stop);
|
|
1288
|
+
if (preIndex < 0 || startIndex < 0 || stopIndex < 0) return outlineCorrelationFailure('outline-worker-journal-position-unavailable');
|
|
1289
|
+
if (startIndex <= preIndex) return outlineCorrelationFailure('outline-worker-start-before-dispatch');
|
|
1290
|
+
if (stopIndex <= startIndex) return outlineCorrelationFailure('outline-worker-stop-before-start');
|
|
1291
|
+
for (const workerEvent of [start, stop]) {
|
|
1292
|
+
if (!textOf(workerEvent.cwd) || !path.isAbsolute(workerEvent.cwd)) {
|
|
1293
|
+
return outlineCorrelationFailure('outline-worker-root-unobserved');
|
|
1294
|
+
}
|
|
1295
|
+
if (!samePathKey(workerEvent.cwd, workerRoot)) return outlineCorrelationFailure('outline-worker-root-mismatch');
|
|
1296
|
+
}
|
|
1297
|
+
return { state: 'accepted', pre, result, start, stop, workerRoot, agentId };
|
|
1188
1298
|
}
|
|
1189
1299
|
|
|
1190
1300
|
function verifyOutlineMergeCorrespondence({ root, outlineCommit, draftCommit, mergedCommit, targetPaths, record } = {}) {
|
|
@@ -1335,20 +1445,10 @@ function resolveOutlineEvidence({ safeInput, capture, captures, request, originR
|
|
|
1335
1445
|
|| review.reviewDigest !== seam.reviewDigest || review.reviewerPrincipal !== seam.reviewerPrincipal) {
|
|
1336
1446
|
return fail(review.reason || 'outline-review-changed-after-dispatch');
|
|
1337
1447
|
}
|
|
1338
|
-
const
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
const
|
|
1342
|
-
let workerRoot;
|
|
1343
|
-
try { workerRoot = canonicalOutlineRoot(worker.cwd); }
|
|
1344
|
-
catch { return fail('outline-worker-root-unobserved'); }
|
|
1345
|
-
if (!sameCanonicalPath(workerRoot, seam.workerRoot)) return fail('outline-worker-root-mismatch');
|
|
1346
|
-
const results = (Array.isArray(captures) ? captures : []).filter(item => recordOf(item)
|
|
1347
|
-
&& item.event === 'PostToolUse' && ['Task', 'Agent'].includes(item.tool_name)
|
|
1348
|
-
&& item.tool_use_id === cap.tool_use_id && item.session_id === cap.session_id);
|
|
1349
|
-
if (results.length !== 1 || results[0].agent_id !== worker.agent_id || results[0].interrupted !== false) {
|
|
1350
|
-
return fail('outline-worker-result-missing-or-mismatched');
|
|
1351
|
-
}
|
|
1448
|
+
const correlation = correlateOutlineDispatch(captures, cap, seam.workerRoot);
|
|
1449
|
+
if (correlation.state !== 'accepted') return fail(correlation.reason);
|
|
1450
|
+
const worker = correlation.start;
|
|
1451
|
+
const workerRoot = correlation.workerRoot;
|
|
1352
1452
|
if (review.reviewerPrincipal === worker.agent_id) return fail('outline-reviewer-is-draft-worker');
|
|
1353
1453
|
const principals = { author: safeInput.sessionId, executors: journalExecutors(captures, safeInput.sessionId) };
|
|
1354
1454
|
if (!reviewRecordIndependent(review.review, captures, principals)) return fail('outline-review-not-independent');
|
|
@@ -1949,6 +2049,8 @@ function buildCaptureRecord(payload, eventName, workspaceRoot) {
|
|
|
1949
2049
|
if (event === 'PreToolUse' && PRE_CAPTURE_TOOLS.indexOf(payload.tool_name) === -1) return null;
|
|
1950
2050
|
const toolInput = recordOf(payload.tool_input) ? payload.tool_input : {};
|
|
1951
2051
|
const toolResponse = payload.tool_response;
|
|
2052
|
+
const outlinePre = event === 'PreToolUse' && ['Task', 'Agent'].includes(payload.tool_name);
|
|
2053
|
+
const outlineResult = event === 'PostToolUse' && ['Task', 'Agent'].includes(payload.tool_name);
|
|
1952
2054
|
// Demonstrated alias mapping (doc: "Host result aliases such as agentId
|
|
1953
2055
|
// require a demonstrated capture mapping to the same subagent identity"):
|
|
1954
2056
|
// the PostToolUse Task/Agent result surface reports the subagent id as
|
|
@@ -1961,17 +2063,28 @@ function buildCaptureRecord(payload, eventName, workspaceRoot) {
|
|
|
1961
2063
|
ref: captureId,
|
|
1962
2064
|
event,
|
|
1963
2065
|
command: typeof toolInput.command === 'string' ? toolInput.command : '',
|
|
1964
|
-
cwd: typeof payload.cwd === 'string' && payload.cwd ? payload.cwd : (typeof workspaceRoot === 'string' && workspaceRoot ? workspaceRoot : ''),
|
|
1965
2066
|
tool_use_id: typeof payload.tool_use_id === 'string' ? payload.tool_use_id : '',
|
|
1966
2067
|
session_id: payload.session_id === undefined && payload.sessionId === undefined ? '' : String(payload.session_id !== undefined ? payload.session_id : payload.sessionId),
|
|
1967
2068
|
responseDigest: captureDigest(toolResponse),
|
|
1968
2069
|
responseRef: `host-payload/${event}/${captureId}`,
|
|
1969
2070
|
};
|
|
2071
|
+
if (typeof payload.cwd === 'string' && payload.cwd) record.cwd = payload.cwd;
|
|
2072
|
+
else if (!['SubagentStart', 'SubagentStop'].includes(event) && typeof workspaceRoot === 'string' && workspaceRoot) record.cwd = workspaceRoot;
|
|
1970
2073
|
if (payload.prompt_id !== undefined) record.prompt_id = payload.prompt_id;
|
|
1971
2074
|
if (payload.parent_tool_use_id !== undefined) record.parent_tool_use_id = payload.parent_tool_use_id;
|
|
1972
2075
|
// The launch tool name separates the Agent/Task result surface and the subagent launch from executed tools (F4/F5).
|
|
1973
2076
|
if (typeof payload.tool_name === 'string') record.tool_name = payload.tool_name;
|
|
1974
|
-
|
|
2077
|
+
if (outlinePre) {
|
|
2078
|
+
if (Object.prototype.hasOwnProperty.call(toolInput, 'run_in_background')) record.run_in_background = toolInput.run_in_background;
|
|
2079
|
+
record.isolationSet = Object.prototype.hasOwnProperty.call(toolInput, 'isolation');
|
|
2080
|
+
}
|
|
2081
|
+
if (outlineResult && recordOf(toolResponse) && Object.prototype.hasOwnProperty.call(toolResponse, 'status')) {
|
|
2082
|
+
record.tool_response_status = toolResponse.status;
|
|
2083
|
+
}
|
|
2084
|
+
const resultAgentConflict = outlineResult && textOf(payload.agent_id) && textOf(responseAliasAgentId)
|
|
2085
|
+
&& payload.agent_id !== responseAliasAgentId;
|
|
2086
|
+
const agentId = outlineResult ? (resultAgentConflict ? undefined : responseAliasAgentId)
|
|
2087
|
+
: payload.agent_id !== undefined ? payload.agent_id : responseAliasAgentId;
|
|
1975
2088
|
if (agentId !== undefined) {
|
|
1976
2089
|
record.agent_id = agentId;
|
|
1977
2090
|
if (payload.agent_type !== undefined) record.agent_type = payload.agent_type;
|
|
@@ -2132,4 +2245,4 @@ module.exports = { createClaudeOriginProvider, createOpencodeOriginProvider, bui
|
|
|
2132
2245
|
_internals: { captureJson, captureDigest, digestText, canonicalCapture, parseOutlineDispatchPrompt,
|
|
2133
2246
|
parseInventoryPinRows, validateOutlineInventoryPin, resolveInventoryRow, parseOutlineCitation, resolveOutlineAttempt,
|
|
2134
2247
|
captureOutlineDispatch, resolveOutlineEvidence, verifyOutlineMergeCorrespondence, validateOutlineCaptureQualification,
|
|
2135
|
-
outlineCaptureMappingValid, OUTLINE_INVENTORY_PIN } };
|
|
2248
|
+
outlineCaptureMappingValid, correlateOutlineDispatch, OUTLINE_CAPTURE_MAPPING, OUTLINE_INVENTORY_PIN } };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
3
3
|
"name": "harness-everything",
|
|
4
|
-
"version": "0.46.
|
|
4
|
+
"version": "0.46.1",
|
|
5
5
|
"description": "Invariant-first engineering harness for routing, verification, and failure recovery while preserving agent autonomy.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Miya Daniel",
|
package/sbom.cdx.json
CHANGED
|
@@ -2,23 +2,23 @@
|
|
|
2
2
|
"$schema": "https://cyclonedx.org/schema/bom-1.7.schema.json",
|
|
3
3
|
"bomFormat": "CycloneDX",
|
|
4
4
|
"specVersion": "1.7",
|
|
5
|
-
"serialNumber": "urn:uuid:
|
|
5
|
+
"serialNumber": "urn:uuid:e14b39ac-6cf4-5711-b0c8-03de1649a568",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-10-
|
|
8
|
+
"timestamp": "2026-10-28T02:23:43.789Z",
|
|
9
9
|
"tools": [
|
|
10
10
|
{
|
|
11
11
|
"vendor": "Harness Everything",
|
|
12
12
|
"name": "generate-sbom.js",
|
|
13
|
-
"version": "0.46.
|
|
13
|
+
"version": "0.46.1"
|
|
14
14
|
}
|
|
15
15
|
],
|
|
16
16
|
"component": {
|
|
17
17
|
"type": "application",
|
|
18
|
-
"bom-ref": "pkg:npm/harness-everything@0.46.
|
|
18
|
+
"bom-ref": "pkg:npm/harness-everything@0.46.1",
|
|
19
19
|
"name": "harness-everything",
|
|
20
|
-
"version": "0.46.
|
|
21
|
-
"purl": "pkg:npm/harness-everything@0.46.
|
|
20
|
+
"version": "0.46.1",
|
|
21
|
+
"purl": "pkg:npm/harness-everything@0.46.1"
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
24
|
"components": [
|
|
@@ -2033,7 +2033,7 @@
|
|
|
2033
2033
|
]
|
|
2034
2034
|
},
|
|
2035
2035
|
{
|
|
2036
|
-
"ref": "pkg:npm/harness-everything@0.46.
|
|
2036
|
+
"ref": "pkg:npm/harness-everything@0.46.1",
|
|
2037
2037
|
"dependsOn": [
|
|
2038
2038
|
"pkg:npm/%40commitlint/cli@19.8.1",
|
|
2039
2039
|
"pkg:npm/%40commitlint/config-conventional@19.8.1",
|