@agentskit/harness 0.12.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +95 -0
- package/README.md +8 -3
- package/capabilities/public-surface.json +83 -83
- package/dist/cli.js +309 -66
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +157 -9
- package/dist/index.js +291 -70
- package/dist/index.js.map +1 -1
- package/docs/ADR-0019-human-decision-attestation.md +9 -4
- package/docs/MODULE-BOUNDARIES.md +5 -4
- package/loop.config.example.yaml +29 -2
- package/package.json +12 -8
- package/release/manifest.json +3 -3
- package/release/notes.md +12 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,100 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.14.0] - 2026-09-19
|
|
4
|
+
|
|
5
|
+
A queue that several machines can drain, a review whose strictness matches the risk, and four fixes that
|
|
6
|
+
were sitting unpublished. Motivated by a real 24/7 loop that had been silently doing nothing: the config
|
|
7
|
+
was invalid, the queue asked for the wrong assignee, and the diagnostic misnamed its own subject.
|
|
8
|
+
|
|
9
|
+
### The queue stops being "my issues"
|
|
10
|
+
|
|
11
|
+
- **`linear.queueOwnership`** (`person` | `unassigned`, default `person`, nothing changes unless you opt
|
|
12
|
+
in). Under `unassigned` the queue lists with `--assignee null` and the assignee becomes a **transient
|
|
13
|
+
claim**: written right after a dispatch succeeds, cleared when the item comes back. That is what lets
|
|
14
|
+
several machines share one priority-ordered queue without two of them picking the same issue.
|
|
15
|
+
The claim is non-fatal and gets its own `queue.claim-failed` event — what actually removes an issue from
|
|
16
|
+
the queue is the status transition, so a failed claim must not cost the transition or the comment.
|
|
17
|
+
- **`linear.anyLabels`** — "at least one of these" (OR), because `requireLabels` is AND: listing two
|
|
18
|
+
layers there demands both on the same issue and matches **nothing**. A queue that returns zero is
|
|
19
|
+
indistinguishable from "no work to do", which is the worst failure mode this loop has.
|
|
20
|
+
- `loop doctor` now says **which** queue it read. Under `unassigned` ownership it used to print
|
|
21
|
+
"for \<person\>" — the opposite of what it listed, and that is how an empty queue goes unnoticed.
|
|
22
|
+
|
|
23
|
+
### Review strictness that matches the risk
|
|
24
|
+
|
|
25
|
+
- **`reviewOverrides`** — stricter review for the slices that deserve it, keyed by label. First match
|
|
26
|
+
wins and only the named fields are replaced: an override that raises `votes` must not silently reset
|
|
27
|
+
the deadline or swap the CLI. The matched label travels to the deliver log, because a gate that costs
|
|
28
|
+
more without explaining itself reads as a bug.
|
|
29
|
+
- The labels come from the **dispatch record**, not a fresh Linear read, so editing a label mid-flight
|
|
30
|
+
cannot change the gate a running item is judged by.
|
|
31
|
+
|
|
32
|
+
### Telling the worker the truth about the base branch
|
|
33
|
+
|
|
34
|
+
- **`knownFailures`** — suites already red on the base, declared with the tracking issue (mandatory: a
|
|
35
|
+
quarantine without an owner becomes permanent). The harness does **not** run `verifyCommand` — the
|
|
36
|
+
worker does, in its own worktree — so tolerating known breakage is information in the brief, not output
|
|
37
|
+
parsing. Without it, every item touching a broken package fails verification for someone else's defect.
|
|
38
|
+
|
|
39
|
+
### Memory: recurrence instead of guesswork
|
|
40
|
+
|
|
41
|
+
- Learning ids are content-derived, so a lesson that reappeared was silently deduplicated and a pattern
|
|
42
|
+
looked exactly like a one-off. **`sightings` now counts**, and `loop retro` offers the lessons that hit
|
|
43
|
+
`memory.recurrence.minSightings` with the promote command already filled in.
|
|
44
|
+
- Promotion still requires a human (`HUMAN_APPROVAL_REQUIRED`, ADR-0019). Memory is read into every
|
|
45
|
+
worker brief: a wrong lesson promoted without a human is a wrong instruction on every future task.
|
|
46
|
+
|
|
47
|
+
### One human approval covers the goal's own effects
|
|
48
|
+
|
|
49
|
+
- **`tracking.authorization`** (`goal` | `separate`, default `goal`). Approving the verification result
|
|
50
|
+
now authorizes the declared external effect too, recording `authorization.recorded` at the same
|
|
51
|
+
instant. `separate` keeps the old two-gate behaviour.
|
|
52
|
+
|
|
53
|
+
### Fixes that had never shipped
|
|
54
|
+
|
|
55
|
+
- Phase age and worker age are different numbers: `ageMin` counts from dispatch, `phaseAgeMin` from the
|
|
56
|
+
event that started the phase. An item in review for 10 minutes used to show the dispatch age and looked
|
|
57
|
+
stuck for hours.
|
|
58
|
+
- Stale delivery state is reset on redispatch, dead stage locks recover, queue alerts are ignored during
|
|
59
|
+
scheduled stages, and expected pre-PR delivery gaps stop being reported as problems.
|
|
60
|
+
|
|
61
|
+
## [0.13.0] - 2026-09-14
|
|
62
|
+
|
|
63
|
+
A full-codebase test-coverage sweep (every module in `src/kernel/`, `src/execution/`, `src/adapters/`, and most of
|
|
64
|
+
`src/loop/` brought to 90%+ statements/branches) that surfaced eight real bugs along the way, each fixed in its own
|
|
65
|
+
focused PR rather than folded silently into a test change.
|
|
66
|
+
|
|
67
|
+
- **Per-issue state files are now written atomically**: `dispatch.json`, `delivery.json`, and `contract.json` were
|
|
68
|
+
each written with a plain `writeFileSync` straight to the final path. `tick` and `deliver` run as separate
|
|
69
|
+
scheduled processes against the same state directory, and `readDeliveryState` already treats malformed JSON as
|
|
70
|
+
"no state yet" rather than erroring — so a crash mid-write or a read racing a write could silently reset
|
|
71
|
+
`fixRounds`/`nudges`/`finalOutcome` instead of surfacing the corruption. Consolidated the three duplicated unsafe
|
|
72
|
+
writes into one shared `src/loop/fs-atomic.ts` (temp file + atomic rename).
|
|
73
|
+
- **`events.ndjson` rotation is now serialized**: `appendLoopEvent`'s size-based rotation (`statSync` →
|
|
74
|
+
`renameSync` → `appendFileSync`) had no lock, so two scheduled processes rotating the same file near-
|
|
75
|
+
simultaneously could overwrite one process's archive or drop events. Only the rotation decision is now gated
|
|
76
|
+
behind a lock file; a busy lock skips rotation for that call rather than racing it, and a lock older than 5s is
|
|
77
|
+
treated as an abandoned crash artifact and cleared.
|
|
78
|
+
- **PII scanner recognizes current-format secrets**: added `sk-proj-...` (current OpenAI project keys), fine-
|
|
79
|
+
grained GitHub PATs, Google API keys, Stripe live keys, PEM private-key blocks, and the AWS secret-access-key
|
|
80
|
+
half (previously only the `AKIA` access-key id was matched).
|
|
81
|
+
- **`Ctrl-C` now actually stops `loop watch`**: its SIGINT handler only set `process.exitCode` without calling
|
|
82
|
+
`process.exit()`, so the long-running poll loop (and its `gh`/`orca` shell-outs) kept running in the background
|
|
83
|
+
after a cancelled watch.
|
|
84
|
+
- **Raw throws reclassified as `HarnessError`**: `retro.ts`'s `--since` parsing and `doc-bridge.ts`'s index
|
|
85
|
+
freshness/readability checks threw plain `Error`, so these bad-input/bad-state failures fell through the CLI's
|
|
86
|
+
generic exit-1 catch-all instead of the classified exit-code path every other validation failure uses.
|
|
87
|
+
- **Fixed two `execution/agent.ts` session-recorder bugs**: a dead `executing` Set that could never affect
|
|
88
|
+
control flow (removed), and a structurally-invalid-but-non-throwing runtime result that was being swallowed into
|
|
89
|
+
a generic, retryable `RUNTIME_ERROR` instead of surfacing as its own distinct failure.
|
|
90
|
+
- **`findExecutable` now checks the execute bit**: a non-executable regular file sitting on `PATH` with a matching
|
|
91
|
+
name was reported as a runnable binary, only to fail with `EACCES` at actual spawn time.
|
|
92
|
+
- **`loop debrief --issue X` no longer returns an empty report** for a normal, not-yet-dispatched issue: the
|
|
93
|
+
"always include an explicitly requested issue" fast path was shadowed by an unconditional second skip check
|
|
94
|
+
right after it.
|
|
95
|
+
|
|
96
|
+
Everything else in this release is test-only: no other production behavior changed.
|
|
97
|
+
|
|
3
98
|
## [0.12.0] - 2026-09-13
|
|
4
99
|
|
|
5
100
|
Closes gaps found reusing Orca instead of reinventing it. Orchestration mutations (`run-create`, `task-create`,
|
package/README.md
CHANGED
|
@@ -36,7 +36,7 @@ ak-harness doctor --json
|
|
|
36
36
|
ak-harness plan approved --by human
|
|
37
37
|
ak-harness start
|
|
38
38
|
ak-verify run --json
|
|
39
|
-
ak-verify approve
|
|
39
|
+
ak-verify approve approved --by human --json
|
|
40
40
|
ak-harness cancel <run-id> --by human --reason "Requirements changed"
|
|
41
41
|
ak-harness benchmark --manifest benchmarks/harness-phase-0.json --json
|
|
42
42
|
```
|
|
@@ -90,7 +90,12 @@ that must match the `verification.completed` event before human approval. Human
|
|
|
90
90
|
approvals, rejections, and tracking authorizations are then recorded as
|
|
91
91
|
hash-chained `approval.recorded` or `authorization.recorded` events bound to
|
|
92
92
|
that digest, source revision, and contract hash. The stable `run.json` remains
|
|
93
|
-
the CLI projection and evidence index.
|
|
93
|
+
the CLI projection and evidence index. The run ID and hashes are audit fields,
|
|
94
|
+
not inputs a human needs to provide: `ak-verify approve approved` resolves the
|
|
95
|
+
latest pending run. By default, a declared tracking target is covered by the
|
|
96
|
+
same goal approval and records both lifecycle events. Set
|
|
97
|
+
`tracking.authorization` to `"separate"` only when a project explicitly needs
|
|
98
|
+
a second human decision.
|
|
94
99
|
|
|
95
100
|
Use `ak-harness audit [run-id]` to reconcile a run projection with its verified
|
|
96
101
|
events. `ak-harness status` performs the same reconciliation before reporting
|
|
@@ -157,7 +162,7 @@ contract is frozen:
|
|
|
157
162
|
|
|
158
163
|
`runtime.kind` chooses the executor used by an integration: `process` is a bounded shell-free local child process; `docker` adds the Docker sandbox. The choice is frozen in the resolved contract and therefore changes its hash. Docker remains fail-closed when its daemon or image is unavailable.
|
|
159
164
|
|
|
160
|
-
`autonomy: "yolo"` removes the generic final review only after every applicable check passes, tracking is disabled, and the frozen contract has no ambiguity. It never auto-approves a material decision, external tracking, or a tool rule that requires approval.
|
|
165
|
+
`autonomy: "yolo"` removes the generic final review only after every applicable check passes, tracking is disabled, and the frozen contract has no ambiguity. It never auto-approves a material decision, external tracking, or a tool rule that requires approval. A human goal approval covers declared intermediate work and goal-scoped tracking; it does not waive evidence, freshness, or ambiguity gates.
|
|
161
166
|
|
|
162
167
|
The phase executor applies the same rule to a declarative SDLC profile. A profile
|
|
163
168
|
declares dependencies, inputs/outputs, gates, bounded retries, budgets, and an
|