cowork-harness 1.12.0 → 1.13.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/.claude/skills/cowork-harness/SKILL.md +16 -11
- package/.claude/skills/cowork-harness/references/ci-recipe.md +10 -9
- package/.claude/skills/cowork-harness/references/critique.md +72 -0
- package/.claude/skills/cowork-harness/references/fidelity-and-answers.md +4 -4
- package/.claude/skills/cowork-harness/references/scenario-schema.md +8 -6
- package/.claude/skills/cowork-harness/references/task-recipes.md +4 -3
- package/.claude/skills/cowork-harness/scripts/scenario.py +14 -1
- package/AGENTS.md +6 -1
- package/CHANGELOG.md +156 -0
- package/CONTRIBUTING.md +1 -1
- package/README.md +7 -7
- package/RELEASING.md +3 -2
- package/dist/cli.js +6 -4
- package/dist/critique/command.js +156 -27
- package/dist/critique/evaluator.js +114 -40
- package/dist/critique/limitations.js +4 -4
- package/dist/critique/package-evidence.js +474 -78
- package/dist/io.js +57 -1
- package/dist/run/analyze-skill.js +6 -3
- package/dist/run/cassette.js +6 -5
- package/dist/run/doctor.js +6 -5
- package/dist/run/envelope.js +6 -4
- package/dist/run/execute.js +14 -1
- package/dist/run/renderer.js +4 -3
- package/dist/run/run-index.js +182 -3
- package/dist/run/scenario-tool.js +5 -4
- package/docs/critique.md +31 -17
- package/docs/scenario.md +4 -1
- package/docs/stats.md +44 -5
- package/examples/replays/README.md +1 -1
- package/package.json +2 -1
- package/schema/critique-report.json +117 -16
- package/scripts/bump-version.ts +3 -0
- package/scripts/check-skill-doc-links.ts +105 -0
- package/scripts/check-versions.ts +4 -1
|
@@ -3,8 +3,8 @@ name: cowork-harness
|
|
|
3
3
|
description: Test or debug a Claude Code skill/plugin under Claude Cowork's runtime — sandboxed agent, default-deny egress, the can_use_tool permission/question protocol — using the cowork-harness CLI. Use when validating or regression-testing a skill, authoring or debugging a scenario YAML (prompt + scripted answers + assert:), choosing a fidelity tier, scripting AskUserQuestion / tool-permission answers, or asserting artifacts, egress, or sub-agent dispatch. Especially when a harness run no-ops an assertion, fails on an unanswered gate, false-greens, a steered answer never reaches the model, or a web_fetch is unexpectedly denied or gated. Also when iterating or hardening a skill across fixes, or grounding a skill's self-critique against its own run evidence — including a document-analysis skill (cap table, deck, financial model, transcript) that needs an uploaded file attached to be critiqued at all. NOT for generic unit testing (pytest/vitest of your own scripts) or non-Cowork CI. Covers the skill / run / chat / record / replay / trace / decide / assertions / scaffold commands and the session-vs-scenario split.
|
|
4
4
|
metadata:
|
|
5
5
|
author: cowork-harness
|
|
6
|
-
version: 1.
|
|
7
|
-
tracks-harness: cowork-harness 1.
|
|
6
|
+
version: 1.13.0
|
|
7
|
+
tracks-harness: cowork-harness 1.13.0 (baseline desktop-1.24012.9)
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# cowork-harness
|
|
@@ -22,7 +22,7 @@ flagged with a loud `::warning::`, not silent — auto-answer a gate, observe an
|
|
|
22
22
|
allowlist). This skill exists mostly to keep you out of those traps — the Gotchas section below is
|
|
23
23
|
the highest-value part. Read it.
|
|
24
24
|
|
|
25
|
-
> **Version note:** the facts and `file:line` pointers here track `cowork-harness 1.
|
|
25
|
+
> **Version note:** the facts and `file:line` pointers here track `cowork-harness 1.13.0` (baseline
|
|
26
26
|
> `desktop-1.24012.9`). If your checkout is newer, prefer the live `--help` and — in a repo checkout —
|
|
27
27
|
> `SPEC.md` / `docs/*.md` over this snapshot, and re-run the bundled linter.
|
|
28
28
|
|
|
@@ -39,7 +39,7 @@ Before the first command, confirm the CLI is reachable and **fail loud** (never
|
|
|
39
39
|
|
|
40
40
|
- **One-shot check.** Run `cowork-harness doctor [--tier <tier>]` first — a read-only prerequisite check that inspects Docker, the staged agent, the token, and the baseline in one pass. The bullets below explain each thing it checks (and how to fix it).
|
|
41
41
|
- **Replay-only? Skip `doctor`.** Replaying committed cassettes needs no Docker, no staged agent, and no token — and every tier's `doctor` validates the auth token (the live tiers also Docker + the staged agent), so a ✗ there is expected, not a blocker. Go straight to `cowork-harness replay <cassette>`.
|
|
42
|
-
- **CLI on PATH, recent enough?** Run `cowork-harness --version` — this skill needs **≥ 1.
|
|
42
|
+
- **CLI on PATH, recent enough?** Run `cowork-harness --version` — this skill needs **≥ 1.13.0**. If it's missing or older, prefix every command with the version floor `npx "cowork-harness@>=1.13.0" <cmd>` (Node ≥ 20), or install once with `npm i -g "cowork-harness@>=1.13.0"`. **Pin `@>=1.13.0`, never `@latest`** — `@latest` can silently fetch an older CLI and the new commands fail as "unknown command", whereas the floor **fails loud** if no compatible version is published.
|
|
43
43
|
|
|
44
44
|
This skill documents the CURRENT surface, not release history. If `cowork-harness --version` is
|
|
45
45
|
OLDER than the floor, the per-release record of what you are missing is [CHANGELOG.md](https://github.com/yaniv-golan/cowork-harness/blob/main/CHANGELOG.md)
|
|
@@ -70,7 +70,7 @@ reproducible regression (Part II), and **debug** a run that misbehaved or greene
|
|
|
70
70
|
then `verify-run` to re-check a suspect assertion — all token-free, no Docker, no re-record. This is
|
|
71
71
|
the loop 0.32.0's observability is built for; the *Triage* and *Inspecting a run's observability
|
|
72
72
|
output* sections in **Part III — Debug** are the detail (the fuller human-facing map lives in
|
|
73
|
-
`docs/debugging.md` — repo-only, not shipped with the installed skill).
|
|
73
|
+
[`docs/debugging.md`](https://github.com/yaniv-golan/cowork-harness/blob/main/docs/debugging.md) — repo-only, not shipped with the installed skill).
|
|
74
74
|
- **Multi-turn / interactive reproduction** → `cowork-harness chat` (interactive; gates answered at the
|
|
75
75
|
TTY, **not** an asserted test — see *Debugging with `chat`* in **Part III — Debug**).
|
|
76
76
|
|
|
@@ -363,7 +363,7 @@ cassette — has its own recipe:
|
|
|
363
363
|
skillHash keys the whole MOUNTED plugin, so on a multi-skill plugin the hash alone cross-pairs
|
|
364
364
|
critiques of DIFFERENT skills — pair by the report's `(gradedSkillHash, gradedSkill)` pair. **On a pre-1.5.0 CLI the `skill` lane emits no `fingerprint.skillHash` at all**, so a
|
|
365
365
|
pairing step there silently groups on an absent key instead of erroring — check the field is present, or
|
|
366
|
-
require ≥ 1.5.0. See `docs/debugging.md`
|
|
366
|
+
require ≥ 1.5.0. See [`docs/debugging.md`](https://github.com/yaniv-golan/cowork-harness/blob/main/docs/debugging.md)
|
|
367
367
|
(repo-only) for the full loop.
|
|
368
368
|
|
|
369
369
|
#### Interpreting verdict signals
|
|
@@ -416,7 +416,7 @@ Recognize these before "fixing" a non-bug:
|
|
|
416
416
|
pass or a defect — assert `no_delete_in_outputs` / `transcript_no_host_path` to hard-fail on it instead.
|
|
417
417
|
|
|
418
418
|
The full 14-code signal table (severity + per-signal opt-out) is in
|
|
419
|
-
[`references/scenario-schema.md`](./references/scenario-schema.md); `docs/scenario.md` (repo-only) carries
|
|
419
|
+
[`references/scenario-schema.md`](./references/scenario-schema.md); [`docs/scenario.md`](https://github.com/yaniv-golan/cowork-harness/blob/main/docs/scenario.md) (repo-only) carries
|
|
420
420
|
the fuller narrative.
|
|
421
421
|
|
|
422
422
|
### Checking whether a background run is alive
|
|
@@ -434,7 +434,7 @@ checkable regardless of PID namespace. The harness prints `[status] <outDir>` to
|
|
|
434
434
|
run starts, so capture stderr to get the exact directory — but `<dir>` also accepts the run-dir root
|
|
435
435
|
passed to `--run-dir` (a directory without its own `status.json`): it scans up to two levels down for the
|
|
436
436
|
newest session's `status.json` and reads that. `--follow` fails loud on a timeout/staleness
|
|
437
|
-
rather than hanging forever. (Fuller recipe in `docs/run-status.md` — repo-only, not in the installed
|
|
437
|
+
rather than hanging forever. (Fuller recipe in [`docs/run-status.md`](https://github.com/yaniv-golan/cowork-harness/blob/main/docs/run-status.md) — repo-only, not in the installed
|
|
438
438
|
payload; `cowork-harness status --help` has the flags.)
|
|
439
439
|
|
|
440
440
|
**A multi-minute `record`/`run` outlives a short-lived wrapper.** Don't launch a long record from a
|
|
@@ -457,6 +457,11 @@ afterthought: the run already wrote its evidence, so you **localize the failure
|
|
|
457
457
|
re-run and hope. Start at the triage below, then use the observability output and, when you need to
|
|
458
458
|
reproduce interactively, `chat`.
|
|
459
459
|
|
|
460
|
+
> **"Evidence" below means the run's own record** — events, trace, transcript; what `trace` / `inspect` /
|
|
461
|
+
> `diff` / `verify-run` / `replay --explain` read. `critique`'s **evaluator** grades against a separate,
|
|
462
|
+
> narrower record — `critique-evidence-package.txt`, what a grade was actually computed against — none of
|
|
463
|
+
> the five tools above surface it; see `references/critique.md`.
|
|
464
|
+
|
|
460
465
|
### Triage — a run misbehaved, or a green looks wrong
|
|
461
466
|
|
|
462
467
|
<!-- BEGIN triage-canonical -->
|
|
@@ -479,7 +484,7 @@ already does.
|
|
|
479
484
|
**deliberate fidelity gap** — the harness intentionally does *not* reproduce a few real-Cowork behaviors,
|
|
480
485
|
so a "bug" you see here that real Cowork also has isn't yours to fix. The tier semantics are in
|
|
481
486
|
`references/fidelity-and-answers.md` (shipped); the specific deltas vs. real Cowork and the sandbox
|
|
482
|
-
boundary model live in `docs/fidelity-gaps.md` / `docs/boundary.md` (repo-only, not in the installed
|
|
487
|
+
boundary model live in [`docs/fidelity-gaps.md`](https://github.com/yaniv-golan/cowork-harness/blob/main/docs/fidelity-gaps.md) / [`docs/boundary.md`](https://github.com/yaniv-golan/cowork-harness/blob/main/docs/boundary.md) (repo-only, not in the installed
|
|
483
488
|
payload). If the behavior is on that gap list, it's expected — stop debugging your skill.
|
|
484
489
|
|
|
485
490
|
### Inspecting a run's observability output
|
|
@@ -749,7 +754,7 @@ repeats the assertion/replay-relevant ones alongside the schema (a scoped subset
|
|
|
749
754
|
`resolved-tier` finding (re-record — the recording now exercises the wrong tier); a cassette with no
|
|
750
755
|
`effectiveFidelity` at all, or an unloadable pinned `baseline:`, reports `unverifiable-tier` instead
|
|
751
756
|
(couldn't check — also re-record). Both are `fidelity: cowork`-only; an explicit-tier scenario never
|
|
752
|
-
produces them. (Details: `docs/cassette.md` § tier staleness — repo-only.)
|
|
757
|
+
produces them. (Details: [`docs/cassette.md`](https://github.com/yaniv-golan/cowork-harness/blob/main/docs/cassette.md) § tier staleness — repo-only.)
|
|
753
758
|
|
|
754
759
|
22. **`lint` floods CI with INFO advisories that don't apply to you.** *Why:* two rules —
|
|
755
760
|
`manifest-needs-snapshot` and `gate-needs-controlout` — fire on the mere presence of manifest/gate
|
|
@@ -785,5 +790,5 @@ assertion/replay-relevant ones).
|
|
|
785
790
|
no-silent-false-green invariants (both usable as CI steps), and `resolve-agent-types <plugin-dir>`
|
|
786
791
|
(validates a pinned `subagent_type` against the plugin's own `plugin.json` + `agents/*.md`).
|
|
787
792
|
- Checking a background run's status without `ps aux` — covered in *Checking whether a background run is
|
|
788
|
-
alive* (Part II) above; the fuller recipe is in `docs/run-status.md` (repo-only, not shipped with the
|
|
793
|
+
alive* (Part II) above; the fuller recipe is in [`docs/run-status.md`](https://github.com/yaniv-golan/cowork-harness/blob/main/docs/run-status.md) (repo-only, not shipped with the
|
|
789
794
|
installed skill).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# CI recipe — replay vs live lanes
|
|
2
2
|
|
|
3
|
-
Self-contained reference. Tracks `cowork-harness 1.
|
|
3
|
+
Self-contained reference. Tracks `cowork-harness 1.13.0` (baseline `desktop-1.24012.9`).
|
|
4
4
|
|
|
5
5
|
**Fastest path: the packaged Action.** One step gets you `replay`/`lint`/`verify-cassettes` plus a PR
|
|
6
6
|
job-summary reporter (verdict table, staleness findings, cost/turns when available):
|
|
@@ -13,7 +13,7 @@ job-summary reporter (verdict table, staleness findings, cost/turns when availab
|
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
The Action's `version` input defaults to `latest` — intentional so a copy-pasted recipe tracks the current
|
|
16
|
-
release; pin an exact version (e.g. `version: "1.
|
|
16
|
+
release; pin an exact version (e.g. `version: "1.13.0"`) for reproducible CI.
|
|
17
17
|
|
|
18
18
|
Reach for the manual multi-step form below only when you need per-step control the Action's inputs don't
|
|
19
19
|
cover (a custom flag combination, a different runner matrix per step, or `lint`/`verify-cassettes` gated
|
|
@@ -30,13 +30,14 @@ jobs:
|
|
|
30
30
|
runs-on: [self-hosted, linux, arm64] # needs Docker + this staged ELF; not a stock GitHub-hosted runner
|
|
31
31
|
steps:
|
|
32
32
|
- uses: actions/checkout@v4
|
|
33
|
-
- name: Stage the agent binary (official channel, sha256-verified — see docs/maintenance.md)
|
|
33
|
+
- name: Stage the agent binary (official channel, sha256-verified — see https://github.com/yaniv-golan/cowork-harness/blob/main/docs/maintenance.md)
|
|
34
34
|
run: |
|
|
35
35
|
V=2.1.219 # match your scenario's pinned baseline's agentVersion
|
|
36
36
|
curl -fSL "https://downloads.claude.ai/claude-code-releases/$V/linux-arm64/claude" -o "$RUNNER_TEMP/claude-$V"
|
|
37
37
|
chmod +x "$RUNNER_TEMP/claude-$V"
|
|
38
38
|
# verify against the committed baseline's sha256 (baselines/desktop-*.json → agentBinary.sha256)
|
|
39
|
-
# before trusting it — see
|
|
39
|
+
# before trusting it — see the "Agent-binary provenance" section of
|
|
40
|
+
# https://github.com/yaniv-golan/cowork-harness/blob/main/docs/maintenance.md
|
|
40
41
|
echo "COWORK_AGENT_BINARY=$RUNNER_TEMP/claude-$V" >> "$GITHUB_ENV"
|
|
41
42
|
- uses: yaniv-golan/cowork-harness@main
|
|
42
43
|
with:
|
|
@@ -48,7 +49,7 @@ jobs:
|
|
|
48
49
|
Why this is a step *you* write, not an Action input the harness provides for you: pulling Anthropic's
|
|
49
50
|
binary is a call about your own relationship with their distribution terms — keeping it in your own
|
|
50
51
|
version-controlled workflow keeps that decision and its execution yours, auditable, and outside any
|
|
51
|
-
third-party action's code. The agent-binary provenance runbook (`docs/maintenance.md` in the repo —
|
|
52
|
+
third-party action's code. The agent-binary provenance runbook ([`docs/maintenance.md`](https://github.com/yaniv-golan/cowork-harness/blob/main/docs/maintenance.md) in the repo —
|
|
52
53
|
not shipped with the installed skill) has the full recovery/verification story (including why
|
|
53
54
|
`COWORK_AGENT_BINARY` substitutions are
|
|
54
55
|
sha256-*checked* but not hard-blocking on mismatch — it's advisory for an intentional substitution).
|
|
@@ -57,7 +58,7 @@ sha256-*checked* but not hard-blocking on mismatch — it's advisory for an inte
|
|
|
57
58
|
GitHub-hosted runners, no token/Docker/agent:
|
|
58
59
|
|
|
59
60
|
```yaml
|
|
60
|
-
- run: npm i -g "cowork-harness@>=1.
|
|
61
|
+
- run: npm i -g "cowork-harness@>=1.13.0"
|
|
61
62
|
- run: cowork-harness lint scenarios/*.yaml # no silent false-greens
|
|
62
63
|
- run: cowork-harness verify-cassettes cassettes/ # privacy + staleness
|
|
63
64
|
- run: cowork-harness replay cassettes/ # token-free content/structure
|
|
@@ -103,7 +104,7 @@ The split is not just about tokens — it decides **where each lane can run**:
|
|
|
103
104
|
`allow_stall` (no-op passes); plus the gate keys `question_asked` / `questions_count_max` /
|
|
104
105
|
`gate_answers_delivered` **if** the cassette has `controlOut`). Filesystem/egress assertions are
|
|
105
106
|
skipped on this token-free lane — loudly: replay emits an `::warning::` annotation whenever it drops
|
|
106
|
-
one (see docs/cassette.md). This is your **always-on PR gate**. With `--output-format json`, read each
|
|
107
|
+
one (see [docs/cassette.md](https://github.com/yaniv-golan/cowork-harness/blob/main/docs/cassette.md)). This is your **always-on PR gate**. With `--output-format json`, read each
|
|
107
108
|
`results[].verdict.{pass,signals}` for **per-cassette** pass/fail and the reason (the top-level `ok`
|
|
108
109
|
collapses the whole batch) — e.g. a `stalled` signal fails a cassette whose assertions all passed.
|
|
109
110
|
The PR gate evaluates the assertions **frozen in the cassette**; to re-check against an edited on-disk
|
|
@@ -221,7 +222,7 @@ jobs:
|
|
|
221
222
|
with: { node-version: '20' }
|
|
222
223
|
- uses: actions/setup-python@v5
|
|
223
224
|
with: { python-version: '3.x' } # python3 only — PyYAML is bundled with the linter
|
|
224
|
-
- run: npm i -g "cowork-harness@>=1.
|
|
225
|
+
- run: npm i -g "cowork-harness@>=1.13.0"
|
|
225
226
|
- run: cowork-harness lint scenarios/*.yaml # no-silent-false-green (needs python3; PyYAML bundled)
|
|
226
227
|
- run: cowork-harness verify-cassettes cassettes/ --output-format json # privacy + staleness gate
|
|
227
228
|
- run: cowork-harness replay cassettes/ --output-format json # token-free content/structure
|
|
@@ -250,7 +251,7 @@ jobs:
|
|
|
250
251
|
echo "live=true" >> "$GITHUB_OUTPUT"
|
|
251
252
|
fi
|
|
252
253
|
- if: steps.guard.outputs.live == 'true'
|
|
253
|
-
run: npm i -g "cowork-harness@>=1.
|
|
254
|
+
run: npm i -g "cowork-harness@>=1.13.0"
|
|
254
255
|
- if: steps.guard.outputs.live == 'true'
|
|
255
256
|
run: cowork-harness run scenarios/ --output-format json
|
|
256
257
|
env:
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Critique — the facts a plugin install can't otherwise reach
|
|
2
|
+
|
|
3
|
+
Tracks `cowork-harness 1.13.0` (baseline `desktop-1.24012.9`). This is **not** a trim of the full
|
|
4
|
+
[`docs/critique.md`](https://github.com/yaniv-golan/cowork-harness/blob/main/docs/critique.md) (repo-only —
|
|
5
|
+
flags, cost, reproduction discipline, known limitations all live there). This file covers exactly what a
|
|
6
|
+
plugin install cannot otherwise discover: the run-dir artifact a harvester actually reads, the report's
|
|
7
|
+
real field names, and what the evaluator was and was not shown. See **Recipe 5** in `task-recipes.md` for
|
|
8
|
+
the harvest → reproduce → fix loop this feeds.
|
|
9
|
+
|
|
10
|
+
## The run-dir artifact a harvester reads
|
|
11
|
+
|
|
12
|
+
Beyond `critique-report.json` (the machine-readable report), a critique writes **`critique-evidence-package.txt`**
|
|
13
|
+
at the run-dir root whenever the evaluator ran — the **armored** corpus the evaluator was actually graded
|
|
14
|
+
against. Re-grade a disputed finding **offline** against this exact file instead of re-running the skill: a
|
|
15
|
+
finding's `evidence` excerpt must resolve verbatim against this package, or it landed in `DROPPED`.
|
|
16
|
+
|
|
17
|
+
| File | When | What |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| `critique-report.json` | always | the report a harvester parses |
|
|
20
|
+
| `critique-evidence-package.txt` | evaluator ran | the exact armored corpus graded — re-gradeable offline |
|
|
21
|
+
| `critique-salvage.json` | exit 2 only | raw pre-parse evaluator replies, for salvage without console-scraping |
|
|
22
|
+
|
|
23
|
+
## Cost across critiques — the index, not the reports
|
|
24
|
+
|
|
25
|
+
A critique is FOUR model workloads but only TWO produce a run, so only two produce index rows; the
|
|
26
|
+
evaluator passes produce none. Each critique therefore appends a **roll-up row** (`critiqueRole:"rollup"`)
|
|
27
|
+
carrying `critiqueTotalUsd` — the whole four-workload spend. Its own `costUsd` is the **evaluator passes
|
|
28
|
+
only**, so `sum(costUsd)` over every row is exactly true spend with nothing double-counted or missed. The
|
|
29
|
+
turn rows carry `critiqueRole:"task"` / `"reflection"`.
|
|
30
|
+
|
|
31
|
+
Two things a harvester needs: roll-ups are excluded from `stats` aggregation (they carry no verdict —
|
|
32
|
+
counting them adds a phantom run and drags `passRate` toward 1), so **filter them out of any pass-rate
|
|
33
|
+
computed over raw rows**; and a roll-up with `result:"error"` had an unpriced workload, so its totals
|
|
34
|
+
UNDERCOUNT. The index is the only cost record that survives run-dir pruning.
|
|
35
|
+
|
|
36
|
+
## The report's item shape — no `title`, no `summary`
|
|
37
|
+
|
|
38
|
+
Each `items[]` entry's prose fields are **`idea`** and **`recommendedAction`** — there is no `title` field
|
|
39
|
+
and no `summary` field. A consumer that parses for either gets silently blank output instead of an error.
|
|
40
|
+
Full required set: `source` (`evaluator`|`self-report`), `idea`, `classification`, `evidence` (the cited
|
|
41
|
+
excerpt, verbatim-checked against the evidence package), `recommendedAction`. The optional
|
|
42
|
+
`findingFingerprint` hashes `idea`+`classification`+`recommendedAction` — high-precision, low-recall: a
|
|
43
|
+
match proves the same finding recurred; a mismatch does NOT prove it didn't (the same finding reworded
|
|
44
|
+
fingerprints differently).
|
|
45
|
+
|
|
46
|
+
## What the evaluator was actually shown — `evidenceBudget`
|
|
47
|
+
|
|
48
|
+
Skill-authored content (`SKILL.md`, every `references/**` file, `agents/<skill>.md`) ships **WHOLE, not
|
|
49
|
+
rationed** — up to a **512 KiB combined ceiling** across all three together. A breach cuts **loudly**: the
|
|
50
|
+
named file and byte counts are reported, never silent, never refused. The **transcript** is bounded
|
|
51
|
+
separately at **128 KiB**, cut **head+tail with an elided middle**, so a run's setup and its conclusion
|
|
52
|
+
both survive a cut instead of just one end.
|
|
53
|
+
|
|
54
|
+
The report's `evidenceBudget` object says exactly what was shown — read it instead of inferring budgets
|
|
55
|
+
from `dist/` source:
|
|
56
|
+
|
|
57
|
+
| Field | What |
|
|
58
|
+
|---|---|
|
|
59
|
+
| `corpusBytes` | total skill-content bytes found, BEFORE any cut |
|
|
60
|
+
| `corpusCeiling` | the 512 KiB combined ceiling |
|
|
61
|
+
| `corpusCuts` | per-file cut record — empty on every real skill; non-empty only once the ceiling is actually breached |
|
|
62
|
+
| `corpusExcluded` | skill files present on the host but never delivered to the agent (see below) |
|
|
63
|
+
| `trimRecord` | which section the transcript trim shaved, and by how much |
|
|
64
|
+
| `packageTruncated` | `true` if ANY section was cut — check this, not `corpusCuts`, for "was anything trimmed": a transcript-only cut leaves `corpusCuts` empty and would otherwise read as "nothing cut" |
|
|
65
|
+
|
|
66
|
+
## An untracked skill file is not graded
|
|
67
|
+
|
|
68
|
+
Staging delivers **git-tracked files only** (same rule as a marketplace plugin install) — an untracked
|
|
69
|
+
file under the skill folder was never in the agent's mount, so grading against it would manufacture a
|
|
70
|
+
false `already-covered` verdict. It is named in `corpusExcluded` instead, and an untracked `SKILL.md`
|
|
71
|
+
specifically reports `skillMdStatus: "untracked"`, forcing the mechanical `already-covered` →
|
|
72
|
+
`not-adjudicable` downgrade. `git add` it (or commit before critiquing) if it should count as evidence.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Fidelity tiers & answer paths
|
|
2
2
|
|
|
3
|
-
Self-contained reference. Tracks `cowork-harness 1.
|
|
3
|
+
Self-contained reference. Tracks `cowork-harness 1.13.0` (baseline `desktop-1.24012.9`).
|
|
4
4
|
|
|
5
5
|
## Fidelity tiers (`fidelity:` in the scenario)
|
|
6
6
|
|
|
@@ -131,7 +131,7 @@ So a helper keys its answer on `questions[N].question`, enumerates choices from
|
|
|
131
131
|
The `reply_with` field is a literal fill-in template for the reply shape (array placeholder when
|
|
132
132
|
multiSelect). Notes: `options` is *optional* — a free-text / header-only gate arrives with no
|
|
133
133
|
`options`; key off `question` (falling back to `header`). This is the shared wire model for both
|
|
134
|
-
channels — `docs/decider-dir.md` shows the same shape, and the Python `serve_decider(fn)` adapter
|
|
134
|
+
channels — [`docs/decider-dir.md`](https://github.com/yaniv-golan/cowork-harness/blob/main/docs/decider-dir.md) shows the same shape, and the Python `serve_decider(fn)` adapter
|
|
135
135
|
hands `fn` exactly this dict.
|
|
136
136
|
|
|
137
137
|
### `--on-unanswered` accepted values (precise)
|
|
@@ -207,7 +207,7 @@ up often enough to spell out:
|
|
|
207
207
|
Everything else that *is* evaluated is checked against the **recording**, not fresh behavior — a
|
|
208
208
|
green replay says the skill produced these events when it was recorded, not that it still does
|
|
209
209
|
(`staleness[]` flags skill/baseline drift as a hint; only a live `run` re-confirms current
|
|
210
|
-
behavior). See `docs/cassette.md` § "Still skipped on replay" and `docs/scenario.md` § "Which
|
|
210
|
+
behavior). See [`docs/cassette.md`](https://github.com/yaniv-golan/cowork-harness/blob/main/docs/cassette.md) § "Still skipped on replay" and [`docs/scenario.md`](https://github.com/yaniv-golan/cowork-harness/blob/main/docs/scenario.md) § "Which
|
|
211
211
|
assertions survive replay."
|
|
212
212
|
- **Container-only assertions can't verify off the `container` tier.** `no_scratchpad_leak` and
|
|
213
213
|
`present_files_called` check the `present_files` delivery path, which is served **only** on
|
|
@@ -222,7 +222,7 @@ up often enough to spell out:
|
|
|
222
222
|
class of Cowork bug (a client-side write-back to a relative URL that resolves-but-fails against
|
|
223
223
|
Cowork's own origin, or a broken blob-download fallback) is invisible to any live run, however
|
|
224
224
|
faithfully sandboxed, because it only manifests in a rendered DOM a human is driving. See
|
|
225
|
-
`docs/fidelity-gaps.md` § "Browser↔webview↔human-interaction boundary."
|
|
225
|
+
[`docs/fidelity-gaps.md`](https://github.com/yaniv-golan/cowork-harness/blob/main/docs/fidelity-gaps.md) § "Browser↔webview↔human-interaction boundary."
|
|
226
226
|
|
|
227
227
|
## Relevant environment variables
|
|
228
228
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Scenario & session schema, assertion catalog, web_fetch, full gotchas
|
|
2
2
|
|
|
3
|
-
Self-contained reference for authoring `cowork-harness` scenarios. Tracks `cowork-harness 1.
|
|
4
|
-
(baseline `desktop-1.24012.9`). If your checkout is newer, prefer the live `docs/scenario.md
|
|
5
|
-
`docs/session.md
|
|
3
|
+
Self-contained reference for authoring `cowork-harness` scenarios. Tracks `cowork-harness 1.13.0`
|
|
4
|
+
(baseline `desktop-1.24012.9`). If your checkout is newer, prefer the live [`docs/scenario.md`](https://github.com/yaniv-golan/cowork-harness/blob/main/docs/scenario.md),
|
|
5
|
+
[`docs/session.md`](https://github.com/yaniv-golan/cowork-harness/blob/main/docs/session.md), and `SPEC.md`.
|
|
6
6
|
|
|
7
7
|
**Minimal scenario** — `prompt` is the only required field:
|
|
8
8
|
|
|
@@ -94,10 +94,12 @@ session = your setup.**
|
|
|
94
94
|
# model & reasoning
|
|
95
95
|
model: claude-opus-4-8 # omit for the agent default
|
|
96
96
|
account_name: my-account # OPTIONAL — display name rendered into {{accountName}} / the prompt's
|
|
97
|
-
# "User name:" line; NOT a credential/identity selector (see src/prompt.ts,
|
|
97
|
+
# "User name:" line; NOT a credential/identity selector (see src/prompt.ts,
|
|
98
|
+
# https://github.com/yaniv-golan/cowork-harness/blob/main/docs/session.md)
|
|
98
99
|
effort: high # low | medium | high | xhigh | max (+ extra, normalized to xhigh); validated against
|
|
99
|
-
# the resolved model's offered levels
|
|
100
|
-
#
|
|
100
|
+
# the resolved model's offered levels
|
|
101
|
+
# (https://github.com/yaniv-golan/cowork-harness/blob/main/docs/session.md);
|
|
102
|
+
# omit for Cowork's medium fallback — real Cowork always emits --effort, never omits it
|
|
101
103
|
extended_thinking: true # real Cowork on/off toggle; default true (ON) -> --max-thinking-tokens 31999,
|
|
102
104
|
# or --thinking disabled when false (no arbitrary budget in real Cowork)
|
|
103
105
|
agent_max_turns: 500 # optional turn ceiling -> agent --max-turns; omit for the agent default
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Each recipe composes facts that live scattered across SKILL.md and the other references into one
|
|
4
4
|
decision path. Every one answers a question a real fleet owner had to work out the hard way.
|
|
5
|
-
Tracks `cowork-harness 1.
|
|
5
|
+
Tracks `cowork-harness 1.13.0` (baseline `desktop-1.24012.9`), same as SKILL.md's front-matter. Recipe 2's `resolved-tier`/`unverifiable-tier` staleness classes and
|
|
6
6
|
Recipe 3's `init-redact` shipped in 0.24.0 and are part of the current feature set — no version gate
|
|
7
7
|
needed if your CLI meets SKILL.md's version floor.
|
|
8
8
|
|
|
@@ -178,13 +178,14 @@ Hardening a skill is a loop: run → read what it did → fix → run again. Two
|
|
|
178
178
|
`--resume` (both pin one run dir) and `--decider-cmd`/`--decider-dir` (a driving agent x N is not a
|
|
179
179
|
measurement).
|
|
180
180
|
The full loop (harvest -> reproduce -> fix -> prove freshness -> compare) is written out end-to-end in
|
|
181
|
-
docs/debugging.md under "The whole loop, end to end". The harness now SHIPS a grader — `cowork-harness critique <skill-folder> --prompt "<probe>"` runs the
|
|
181
|
+
[docs/debugging.md](https://github.com/yaniv-golan/cowork-harness/blob/main/docs/debugging.md) under "The whole loop, end to end". The harness now SHIPS a grader — `cowork-harness critique <skill-folder> --prompt "<probe>"` runs the
|
|
182
182
|
skill, asks the agent what confused it, and grades that self-report against a frozen record of the run
|
|
183
183
|
(blinded evaluator + mechanical citation checking). **Critiquing a document-analysis skill?** The probe
|
|
184
184
|
attaches nothing on its own — pass `--upload <path>` (repeatable) or `--folder <dir>` exactly as you
|
|
185
185
|
would to `skill`, or the graded run has no file and "there was no file attached" is the correct finding,
|
|
186
186
|
not a skill defect. Source flags reach both spawned turns automatically — see SKILL.md's
|
|
187
|
-
floor list. See docs/critique.md for the full flag table, cost and limits
|
|
187
|
+
floor list. See [docs/critique.md](https://github.com/yaniv-golan/cowork-harness/blob/main/docs/critique.md) for the full flag table, cost and limits (or the shipped
|
|
188
|
+
[`references/critique.md`](./critique.md) for the report/evidence-package shapes without leaving the plugin payload). If you
|
|
188
189
|
prefer to build your own grader, the substrate is still here:
|
|
189
190
|
- `result.json` → `finalMessage` (the skill's own answer/critique) + `toolResults[]` (tool outputs).
|
|
190
191
|
- `cowork-harness trace <run-dir> --output-format json` → the tool-call stream. Add `--full-results` so
|
|
@@ -1400,7 +1400,11 @@ def main(argv=None):
|
|
|
1400
1400
|
)
|
|
1401
1401
|
# The wrapper (`cowork-harness lint`) sets COWORK_HARNESS_PROG so usage/error lines name a command the
|
|
1402
1402
|
# user can actually run. Default = the script's own basename, because scenario.py is ALSO documented as
|
|
1403
|
-
# directly runnable (SKILL.md
|
|
1403
|
+
# directly runnable (SKILL.md;
|
|
1404
|
+
# https://github.com/yaniv-golan/cowork-harness/blob/main/docs/gotchas.md,
|
|
1405
|
+
# https://github.com/yaniv-golan/cowork-harness/blob/main/docs/plugin-root.md,
|
|
1406
|
+
# https://github.com/yaniv-golan/cowork-harness/blob/main/docs/subagents.md — repo-only, not
|
|
1407
|
+
# shipped with the installed skill) — hardcoding
|
|
1404
1408
|
# "cowork-harness lint" would lie on that path.
|
|
1405
1409
|
prog = os.environ.get("COWORK_HARNESS_PROG")
|
|
1406
1410
|
sub = ap.add_subparsers(dest="command", required=True)
|
|
@@ -1505,6 +1509,15 @@ def main(argv=None):
|
|
|
1505
1509
|
args, extras = ap.parse_known_args(argv)
|
|
1506
1510
|
if extras:
|
|
1507
1511
|
target = sub.choices.get(getattr(args, "command", "") or "")
|
|
1512
|
+
# `--min-severity` is `lint`-only (it filters lint's severity-classed findings; `lint-skill`'s two
|
|
1513
|
+
# footgun checks have no severity ladder to filter). Passing it to `lint-skill` used to fall through
|
|
1514
|
+
# to the bare "unrecognized arguments" message below and leave the user hunting for a flag that DOES
|
|
1515
|
+
# exist, just on the sibling command — name it explicitly, same as the `--output-format` translation
|
|
1516
|
+
# `runLintLike` (src/run/scenario-tool.ts) already does for the other lint/lint-skill flag mismatch.
|
|
1517
|
+
if getattr(args, "command", None) == "lint-skill" and any(
|
|
1518
|
+
e == "--min-severity" or e.startswith("--min-severity=") for e in extras
|
|
1519
|
+
):
|
|
1520
|
+
(target or ap).error("unrecognized arguments: " + " ".join(extras) + " (--min-severity is a `lint` flag, not `lint-skill` — rerun with `cowork-harness lint` instead)")
|
|
1508
1521
|
(target or ap).error("unrecognized arguments: " + " ".join(extras))
|
|
1509
1522
|
return args.func(args)
|
|
1510
1523
|
|
package/AGENTS.md
CHANGED
|
@@ -136,7 +136,12 @@ the narrow case of advancing a branch nobody has checked out — it just cannot
|
|
|
136
136
|
`main` itself, so the base can still move under an in-flight rebase. Separate worktrees fix that half.
|
|
137
137
|
|
|
138
138
|
**Two one-time costs per worktree**, both benign: no `./.env` (use `--dotenv <primary>/.env` — `doctor`
|
|
139
|
-
detects this and prints the remedy, see `docs/gotchas.md`), and no `node_modules`
|
|
139
|
+
detects this and prints the remedy, see `docs/gotchas.md`), and no `node_modules` — run **`npm ci`**.
|
|
140
|
+
|
|
141
|
+
**Do NOT symlink `node_modules` from the primary** to skip that install. `.gitignore`'s first line is
|
|
142
|
+
`node_modules/` — with a trailing slash, so it matches a *directory* only. A symlink is not a directory to
|
|
143
|
+
git, so it shows up as `?? node_modules` and breaks every clean-tree gate, `npm run preflight` included.
|
|
144
|
+
Verified both ways: a real `node_modules/` directory is correctly ignored; a symlink is not.
|
|
140
145
|
|
|
141
146
|
**Structural safety, not just etiquette:** git refuses to check out `main` in a second worktree
|
|
142
147
|
(`fatal: 'main' is already used by worktree at …`), so the integration branch cannot be double-held by
|
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,162 @@ All notable changes to this project are documented here. The format is based on
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [1.13.0] — 2026-07-28
|
|
10
|
+
|
|
11
|
+
**Upgrade notes.**
|
|
12
|
+
|
|
13
|
+
- **`critique` verdicts are not comparable across this release — but the counts still are, and that
|
|
14
|
+
distinction decides what to do with your history.** The evaluator now sees materially more of your skill,
|
|
15
|
+
so a *per-item* verdict may flip for reasons that have nothing to do with your edits: do not diff
|
|
16
|
+
individual findings across the boundary, and re-baseline anything that tracks a skill's progress
|
|
17
|
+
report-to-report. **Do NOT discard your pre-upgrade reports.** Aggregate counts — above all the
|
|
18
|
+
`not-adjudicable` count on identical prompts — remain the right way to measure what this release did,
|
|
19
|
+
and your archived reports are the only "before" that exists. Pair that with the
|
|
20
|
+
`citationResolved:false` (DROPPED) rate, which is the guard in the other direction: a much larger corpus
|
|
21
|
+
could make the evaluator's citations sloppier, and nothing else would show it.
|
|
22
|
+
- **`skillMdTruncated` is gone from `critique-report.json`**, replaced by `evidenceBudget`
|
|
23
|
+
(`corpusBytes` / `corpusCeiling` / `corpusCuts` / `corpusExcluded` / `trimRecord`). A harvester reading
|
|
24
|
+
the old boolean should read `evidenceBudget.corpusCuts` instead — it is empty on every real skill.
|
|
25
|
+
- **An untracked skill file is no longer graded**, and the two cases behave differently — do not read one
|
|
26
|
+
as the other:
|
|
27
|
+
- **Untracked `references/**` or `agents/<skill>.md`** are simply excluded, named in
|
|
28
|
+
`evidenceBudget.corpusExcluded`, and announced with a `::warning::`. **This does NOT force
|
|
29
|
+
`not-adjudicable`** — and it should not: the agent never received those files either, so the
|
|
30
|
+
evaluator's view now MATCHES the agent's. A finding like "the skill never explains X" against an
|
|
31
|
+
excluded reference is correctly `grounded`; before this release the evaluator could see the file the
|
|
32
|
+
agent never got and mark that true finding `already-covered`.
|
|
33
|
+
- **An untracked `SKILL.md`** previously had its content **graded** (the packager read the host directory
|
|
34
|
+
raw). It now reports `skillMdStatus: "untracked"`, withholds the content, and **does** force the same
|
|
35
|
+
`not-adjudicable` downgrade as an unreadable one — coverage claims cannot be judged with no skill source
|
|
36
|
+
at all.
|
|
37
|
+
Either way, `git add` before critiquing to grade as-published.
|
|
38
|
+
|
|
39
|
+
### Fixed
|
|
40
|
+
|
|
41
|
+
- **Every `docs/*.md` pointer in the shipped skill was dead for a plugin install.** A plugin install
|
|
42
|
+
materializes only `.claude/skills/<name>/**`; `docs/` ships in the npm tarball and not in that payload.
|
|
43
|
+
The skill referenced `docs/` pages **25 times across 13 targets** anyway, so a consumer following one
|
|
44
|
+
found nothing. This cost real time: `critique-evidence-package.txt` — the corpus the evaluator actually
|
|
45
|
+
graded — was documented only in `docs/critique.md` and named nowhere a plugin user could see, so its
|
|
46
|
+
behaviour got reverse-engineered from compiled `dist/` instead. All 25 are now repo permalinks that
|
|
47
|
+
resolve for tarball and plugin users alike, a trimmed `references/critique.md` ships inside the payload
|
|
48
|
+
covering what a plugin install cannot otherwise reach, and **`npm run check:skill-doc-links` fails CI on
|
|
49
|
+
any new dangling reference** — the durable half, without which the next release re-creates them.
|
|
50
|
+
Reported by a consumer.
|
|
51
|
+
- **`lint-skill --min-severity` exited 2 with no explanation.** The flag belongs to `lint`; the error now
|
|
52
|
+
names the sibling command instead of leaving you to diff two help texts.
|
|
53
|
+
- **`index.jsonl` under-reported a critique's cost by ~39%.** A critique is four model workloads, but only
|
|
54
|
+
the two graded turns produce a run and therefore a row — the two evaluator passes are direct API calls
|
|
55
|
+
that produce neither. Anything summing the index missed them entirely (measured: $10.17 indexed against
|
|
56
|
+
$16.67 actual across three runs), and the index is the only cost record that survives run-dir pruning, so
|
|
57
|
+
spend trends built from it were systematically light. Each critique now writes one roll-up row carrying
|
|
58
|
+
`critiqueTotalUsd`, and `--reindex` reconstructs it from the run dir's `critique-report.json` so a lost
|
|
59
|
+
index recovers critique costs too. The roll-up's own `costUsd` is the **evaluator passes only**, so
|
|
60
|
+
`sum(costUsd)` across all rows is exactly true spend — neither double-counted nor short. Roll-ups are
|
|
61
|
+
excluded from `stats` aggregation, where they would otherwise add a phantom run and inflate `passRate`.
|
|
62
|
+
Reported by a consumer.
|
|
63
|
+
|
|
64
|
+
**Sharing a runs root across CLI versions:** an *older* CLI's `--reindex` deletes these roll-up rows —
|
|
65
|
+
its supersede clause drops any prior row without a `turn`, which a roll-up is. Re-run `--reindex` with a
|
|
66
|
+
current CLI to restore them from the run dirs.
|
|
67
|
+
- **A critique was indistinguishable from a plain `skill` run in the index**, which recorded the *inner*
|
|
68
|
+
command for both turns and carried no `skill` field despite `--skill`. With three concurrent critiques of
|
|
69
|
+
three skills against one plugin, every row read `scenario: skill-<plugin>` and the only way to tell them
|
|
70
|
+
apart was opening each run dir. Rows now carry `critiqueRole` (`task` / `reflection` / `rollup`) and the roll-up carries
|
|
71
|
+
`skill`. These are **additive-optional fields, not a new `command` value**: the row validator hard-codes
|
|
72
|
+
the command allowlist, so widening it would make an older CLI quarantine every critique row and drop it
|
|
73
|
+
from `stats`. The role derives from critique's own session-id shape, so `--reindex` marks rebuilt rows too.
|
|
74
|
+
- **`critique`'s task-turn timeout was 10 minutes**, which a sub-agent-dispatching skill routinely exceeds.
|
|
75
|
+
The turn is killed *after* its model spend, so the consumer paid for a graded run and received an
|
|
76
|
+
instrument failure instead of a critique — a reported case burned $11.05 that way. Raised to 30 minutes,
|
|
77
|
+
matching the evaluator transport; `--timeout` still raises it further, and the byte cap and process-group
|
|
78
|
+
kill remain the real runaway guards.
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
- **`critique` graded skills against a fraction of their own references.** The evidence package shared one
|
|
82
|
+
8 KiB budget across ALL `references/**` files, filled in filename-sort order — so the alphabetically
|
|
83
|
+
first file took what it needed and every later file was dropped whole. Measured across nine real runs on
|
|
84
|
+
a six-skill plugin: the same first file was the sole survivor in 9 of 9, and **11 of 13 distinct
|
|
85
|
+
reference files had never reached an evaluator in any run**, including a scoring rubric a sub-agent had
|
|
86
|
+
opened in order to do the scoring. Skill-authored content (SKILL.md, every `references/**` file,
|
|
87
|
+
`agents/<skill>.md`) now ships **whole** — bounded only by a **512 KiB sanity ceiling across all three
|
|
88
|
+
combined**, which no real skill approaches (the largest measured is ~164 KB, about a third of it). A skill
|
|
89
|
+
that does breach it is cut **loudly and by name** in `evidenceBudget.corpusCuts` — never silently, and
|
|
90
|
+
never by refusing the run. The transcript is bounded separately at 128 KiB. Reported by a consumer.
|
|
91
|
+
- **The evidence corpus could contain files the agent never received.** Staging delivers git-tracked files
|
|
92
|
+
only, but the packager read the host directory directly — so an uncommitted reference was absent from
|
|
93
|
+
the mount and present in the evaluator's evidence. An agent saying "the skill never explains X" could be
|
|
94
|
+
marked `already-covered` against a file it was never given. The packager now applies staging's own
|
|
95
|
+
tracked-set filter and reports what it excluded.
|
|
96
|
+
- **`references/**` traversal was neither recursive nor symlink-aware**, so nested and symlinked reference
|
|
97
|
+
files were dropped silently, with no omission marker.
|
|
98
|
+
- **The overall package trim did not converge.** It shaved a section by exactly the overflow and then
|
|
99
|
+
appended a truncation marker, leaving the package marker-length over; it then shaved the next section by
|
|
100
|
+
that amount and re-added the same marker, cascading through every section and exiting still over cap
|
|
101
|
+
with the whole document mangled. It remains a belt-and-suspenders path that should never fire — the
|
|
102
|
+
per-section budgets still sum under the cap, now pinned by a test — which is exactly why it had to be
|
|
103
|
+
correct: nothing exercises it until something else has already gone wrong.
|
|
104
|
+
- **The overall trim destroyed the run record first.** It shaved from the last section backwards, which is
|
|
105
|
+
the transcript — so a breach caused by oversized *skill* content was paid for by deleting *run*
|
|
106
|
+
evidence. Trim priority is now explicit and independent of render order.
|
|
107
|
+
- **`writeSync` short writes and `EAGAIN` were unhandled at every stdout/stderr sink.** `verify-cassettes … | tail`
|
|
108
|
+
died with `EAGAIN` instead of printing its verdict, and a short write on a pipe silently dropped the
|
|
109
|
+
remainder of a JSON envelope. Every `writeSync` output site in `src/` now goes through `writeAllSync`, including critique's own JSON report — the largest single payload the tool emits, and the one its own code comment already flagged as at risk when piped to `jq`. (Sites already using async `process.stderr.write`, and two dev-only sinks under `scripts/`, are unchanged — the failure was specific to the synchronous idiom.)
|
|
110
|
+
|
|
111
|
+
### Documentation
|
|
112
|
+
|
|
113
|
+
- **`turn` vs `turns` in `index.jsonl` is now explained.** The names invite reading `turn: 2, turns: 1` as
|
|
114
|
+
"turn 2 of 1". They are unrelated: `turn` is the position within a resumed session, `turns` is the count
|
|
115
|
+
of agent turns *inside* that one run, and neither bounds the other. A critique roll-up carries neither,
|
|
116
|
+
since it accounts for workloads that are not runs. Documented rather than renamed — a rename breaks
|
|
117
|
+
`stats` and would need a schema version bump for no behavioural gain. Reported by a consumer.
|
|
118
|
+
|
|
119
|
+
**Cost note.** Sending the whole corpus costs more per critique, not less: roughly **+5% to +18%** on a
|
|
120
|
+
~$5.60 run, scaling with how much of your skill was previously being cut (a skill whose SKILL.md sat under
|
|
121
|
+
the old 64 KiB cap and had few references sees almost nothing; one that was heavily truncated sees the top
|
|
122
|
+
of the range). Evidence input was only ~14% of an evaluator pass's cost to begin with, which is why the old
|
|
123
|
+
caps were rationing the cheap term — but it is an increase, and a batch budget should carry it.
|
|
124
|
+
|
|
125
|
+
### Added
|
|
126
|
+
|
|
127
|
+
- **`session.json` now identifies its own run.** The resume manifest (written for `--session-id`/`--resume`
|
|
128
|
+
runs) held only opaque ids, so the file whose name makes it the first thing you open in a run dir
|
|
129
|
+
answered nothing — with several concurrent runs against one plugin there was no way to tell which was
|
|
130
|
+
which without opening each turn's `result.json`. It now also carries `scenario` and `prompt`. These are
|
|
131
|
+
**additive-optional**: nothing validates them, and a manifest written before this still resumes.
|
|
132
|
+
`result.json` remains authoritative for identity; this is a signpost, not a second source of truth.
|
|
133
|
+
Reported by a consumer.
|
|
134
|
+
- **`critique` reports progress.** Four `::notice::` lines on stderr at the phase boundaries (task turn →
|
|
135
|
+
reflection turn → evaluator pass 1 → evaluator pass 2). Previously four model calls over 10–20 minutes
|
|
136
|
+
produced no output at all until the finished report appeared, so a working run and a hung one were
|
|
137
|
+
indistinguishable. stdout remains the machine channel.
|
|
138
|
+
- **`costUsd` now reports the evaluator passes' token split** (`evaluatorPass1Tokens` /
|
|
139
|
+
`evaluatorPass2Tokens`, each `{input, output, cacheRead}`). The transport always handed the harness the
|
|
140
|
+
full usage object and it was summed to a dollar figure and discarded — so the report said what a pass
|
|
141
|
+
COST and never why, and "is this money evidence or thinking?" was unanswerable from any artifact the tool
|
|
142
|
+
produced. That is the question that decides whether sending more evidence is cheap, which makes it the
|
|
143
|
+
number to check first when budgeting a batch under the whole-corpus change above.
|
|
144
|
+
- **`evidenceBudget` in `critique-report.json`** — what the evaluator was actually shown, so the budgets
|
|
145
|
+
are discoverable without reading compiled source. Includes `packageTruncated`, which is what carries the
|
|
146
|
+
transcript's head+tail elision: that elision is what adds the evaluator's truncation caveat, and without
|
|
147
|
+
the flag an elided package was indistinguishable from a clean one (`corpusCuts` is empty in that case and
|
|
148
|
+
would otherwise imply nothing had been cut).
|
|
149
|
+
- **A no-reads signal** (`noSkillFilesRead`). When the graded turn Read no `references/` or `scripts/`
|
|
150
|
+
file at all — neither the main agent nor any sub-agent — the report says so. Worded observationally on
|
|
151
|
+
purpose: the underlying predicate counts the `Read` tool only, so `Grep` or `assets/` use produces an
|
|
152
|
+
empty set having demonstrably reached the material, and calling that "progressive disclosure never
|
|
153
|
+
fired" would be a false accusation about someone's skill.
|
|
154
|
+
|
|
155
|
+
### Changed
|
|
156
|
+
|
|
157
|
+
- **The overall evidence-package cap is 144 KiB → 768 KiB** (`critique --help` reports it). It stays a
|
|
158
|
+
belt-and-suspenders bound: the per-section budgets deliberately sum under it, pinned by a test.
|
|
159
|
+
- **The transcript bound is 32 KiB → 128 KiB and now keeps head *and* tail** with an elided middle.
|
|
160
|
+
A tail-only cut is the worst shape for a procedural skill, which puts its workflow steps last.
|
|
161
|
+
- **`critique`'s evaluator passes get a 30-minute transport timeout** (was the decider's 600 s default).
|
|
162
|
+
That timeout is enforced with SIGKILL and no retry, so a kill during pass 1 discarded the whole critique
|
|
163
|
+
*after* the graded turns had already been paid for.
|
|
164
|
+
|
|
9
165
|
## [1.12.0] — 2026-07-25
|
|
10
166
|
|
|
11
167
|
**Upgrade notes.**
|
package/CONTRIBUTING.md
CHANGED
|
@@ -27,7 +27,7 @@ Extra prerequisites for specific stages:
|
|
|
27
27
|
- **Lima (`limactl`, macOS arm64)** — required only for the **L2 `microvm`** tier and the `vm` commands; the guest runs on Apple Virtualization.framework (`vmType: vz`). `microvm` does **not** use Docker. (`cowork-harness doctor --tier microvm` checks for Lima, not Docker.)
|
|
28
28
|
|
|
29
29
|
CI Stage 1 (the `build` job in `.github/workflows/ci.yml`) does **not** invoke `npm run ci`. It runs the
|
|
30
|
-
gate steps individually — e.g. `check:versions`, `format:check`, `typecheck`, `build`, a CLI smoke
|
|
30
|
+
gate steps individually — e.g. `check:versions`, `check:skill-doc-links`, `format:check`, `typecheck`, `build`, a CLI smoke
|
|
31
31
|
(`node dist/cli.js list`), three token-free `replay` gate fixtures, `verify-cassettes`, `lint`, and
|
|
32
32
|
source-guard checks — while the unit suite runs separately as the 4-shard `test` job (see `ci.yml` for
|
|
33
33
|
the authoritative list). Only `release.yml` calls `npm run ci`. For a copy-paste GitHub Actions setup,
|