cowork-harness 0.33.0 → 1.0.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.
@@ -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. 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: 0.33.0
7
- tracks-harness: cowork-harness 0.33.0 (baseline desktop-1.20186.1)
6
+ version: 1.0.1
7
+ tracks-harness: cowork-harness 1.0.1 (baseline desktop-1.20186.1)
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 0.33.0` (baseline
25
+ > **Version note:** the facts and `file:line` pointers here track `cowork-harness 1.0.1` (baseline
26
26
  > `desktop-1.20186.1`). 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,9 +39,9 @@ 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 **≥ 0.33.0**. If it's missing or older, prefix every command with the version floor `npx "cowork-harness@>=0.33.0" <cmd>` (Node ≥ 20), or install once with `npm i -g "cowork-harness@>=0.33.0"`. **Pin `@>=0.33.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.
42
+ - **CLI on PATH, recent enough?** Run `cowork-harness --version` — this skill needs **≥ 1.0.1**. If it's missing or older, prefix every command with the version floor `npx "cowork-harness@>=1.0.1" <cmd>` (Node ≥ 20), or install once with `npm i -g "cowork-harness@>=1.0.1"`. **Pin `@>=1.0.1`, 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
- What the ≥ 0.33.0 floor gates, by release:
44
+ What the ≥ 1.0.1 floor gates, by release:
45
45
 
46
46
  - **core set (pre-0.21.0 vintage, or mixed):** `assertions --list`, `scaffold <run-id>`, `trace --view dispatches`, `artifact_json` incl. the `in:` operator (passes when the resolved value deep-equals one of the listed members — value ∈ your list, not the reverse), `verify-cassettes` incl. the `--allow-domain`/`--allow-email`/`--allow-patterns-file` allows (`--allow-patterns-file <path>` is a FILE of patterns, one regex per line — not a path to allow, unlike `--allow <regex>`), batch `record <dir>`/`--rerecord-stale`, `record --concurrency <N>`, record-time redaction, multiSelect/`answer:`, `verify-run` answer-coverage, `record --max-artifact-bytes`, live record-time deciders, scenario `skills:` staleness scoping with `COWORK_HARNESS_AGENT_SCOPE=skill`, `chat --plugin`, and `/help` in the REPL.
47
47
  - **0.21.0:** `verify-cassettes --allow-path` (`path` — local absolute filesystem paths — is the scanner's 4th class), and `hostloop`'s native host/VM process split with its `allow_host_writes:` consent field.
@@ -51,6 +51,7 @@ Before the first command, confirm the CLI is reachable and **fail loud** (never
51
51
  - **0.31.0:** the `lint-skill` (static host-loop footgun + `subagent_type` resolution linter) / `analyze-skill` (advisory `/sessions`-path static scan, `--strict`, `analyze-skill: ignore` marker) / `probe-dispatch` (single-dispatch mechanics probe) commands, `status --latest-for` (resolve a scenario's newest run dir by run time, not directory mtime), the `subagent_dispatch_healthy` composite assertion, and the persisted `result.json` fields `verdict`, `subagents[].referencesRead`/`subagents[].reasoning`, plus the `toolCounts`/`toolErrors`/`toolDurations` shape distinction.
52
52
  - **0.32.0:** `analyze-skill`'s directory scan now covering a skill/plugin's full contract surface (recursive `agents/`/`references/`/`commands/`, plugin-root-aware, symlink-following) with line/block-scoped `analyze-skill: ignore-next-line`/`ignore-start`/`ignore-end` markers and multi-path/glob input, and `lint-skill`'s provable in-plugin `subagent_type` typo now a WARN that gates under `--strict`.
53
53
  - **0.33.0:** the `redacted` marker on display-omitted reasoning — `subagents[].reasoning` and the top-level `thinking[]` now carry `{text:"", redacted:true}` when the model returns a signed-but-empty thought (so "reasoned, text omitted" is distinct from "no thought"), plus the fenced `debug.thinking_display` escape hatch.
54
+ - **1.0.0:** first stable release — the SPEC §12 compatibility contract takes effect (covered CLI/schema/env/Action surfaces are now stable; breaking changes need a major bump). No new author-facing command; the floor simply tracks the 1.0 release.
54
55
  - **Agent binary (sandboxed live tiers — `container`/`microvm`/`hostloop`/`cowork`).** The staged Claude Code agent is **bind-mounted** from a local Claude Desktop install, or point `COWORK_AGENT_BINARY` at a `claude-code-vm/<ver>/claude` ELF. Nothing is bundled. `protocol` (L0) and `replay` need no staged agent; for the sandboxed tiers, no agent → no run; report that, don't skip silently.
55
56
  - **Docker / Lima.** Only `--fidelity protocol` (L0) runs without them. `container` / `microvm` / `hostloop` / `cowork` need Docker (Lima for L2). If they're absent, drop to `--fidelity protocol` and **say so** — a green that never exercised the sandbox is not a sandbox pass.
56
57
  - **Auth.** `CLAUDE_CODE_OAUTH_TOKEN` (preferred), or `ANTHROPIC_API_KEY` / `ANTHROPIC_AUTH_TOKEN`, via env or `.env`. Minting an OAuth token needs the **`claude` CLI** (`npm i -g @anthropic-ai/claude-code`, then `claude setup-token`).
@@ -1,6 +1,6 @@
1
1
  # CI recipe — replay vs live lanes
2
2
 
3
- Self-contained reference. Tracks `cowork-harness 0.33.0` (baseline `desktop-1.20186.1`).
3
+ Self-contained reference. Tracks `cowork-harness 1.0.1` (baseline `desktop-1.20186.1`).
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: "0.33.0"`) for reproducible CI.
16
+ release; pin an exact version (e.g. `version: "1.0.1"`) 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
@@ -57,7 +57,7 @@ sha256-*checked* but not hard-blocking on mismatch — it's advisory for an inte
57
57
  GitHub-hosted runners, no token/Docker/agent:
58
58
 
59
59
  ```yaml
60
- - run: npm i -g "cowork-harness@>=0.33.0"
60
+ - run: npm i -g "cowork-harness@>=1.0.1"
61
61
  - run: cowork-harness lint scenarios/*.yaml # no silent false-greens
62
62
  - run: cowork-harness verify-cassettes cassettes/ # privacy + staleness
63
63
  - run: cowork-harness replay cassettes/ # token-free content/structure
@@ -197,7 +197,7 @@ jobs:
197
197
  with: { node-version: '20' }
198
198
  - uses: actions/setup-python@v5
199
199
  with: { python-version: '3.x' } # python3 only — PyYAML is bundled with the linter
200
- - run: npm i -g "cowork-harness@>=0.33.0"
200
+ - run: npm i -g "cowork-harness@>=1.0.1"
201
201
  - run: cowork-harness lint scenarios/*.yaml # no-silent-false-green (needs python3; PyYAML bundled)
202
202
  - run: cowork-harness verify-cassettes cassettes/ --output-format json # privacy + staleness gate
203
203
  - run: cowork-harness replay cassettes/ --output-format json # token-free content/structure
@@ -226,7 +226,7 @@ jobs:
226
226
  echo "live=true" >> "$GITHUB_OUTPUT"
227
227
  fi
228
228
  - if: steps.guard.outputs.live == 'true'
229
- run: npm i -g "cowork-harness@>=0.33.0"
229
+ run: npm i -g "cowork-harness@>=1.0.1"
230
230
  - if: steps.guard.outputs.live == 'true'
231
231
  run: cowork-harness run scenarios/ --output-format json
232
232
  env:
@@ -1,6 +1,6 @@
1
1
  # Fidelity tiers & answer paths
2
2
 
3
- Self-contained reference. Tracks `cowork-harness 0.33.0` (baseline `desktop-1.20186.1`).
3
+ Self-contained reference. Tracks `cowork-harness 1.0.1` (baseline `desktop-1.20186.1`).
4
4
 
5
5
  ## Fidelity tiers (`fidelity:` in the scenario)
6
6
 
@@ -1,6 +1,6 @@
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 0.33.0`
3
+ Self-contained reference for authoring `cowork-harness` scenarios. Tracks `cowork-harness 1.0.1`
4
4
  (baseline `desktop-1.20186.1`). If your checkout is newer, prefer the live `docs/scenario.md`,
5
5
  `docs/session.md`, and `SPEC.md`.
6
6
 
package/CHANGELOG.md CHANGED
@@ -2,10 +2,65 @@
2
2
 
3
3
  All notable changes to this project are documented here. The format is based on
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). The project uses
5
- [Semantic Versioning](https://semver.org/); pre-1.0 minor versions may include breaking changes.
5
+ [Semantic Versioning](https://semver.org/); as of 1.0.0, a backwards-incompatible change to a covered surface ([SPEC.md §12](./SPEC.md#12-versioning--the-10-compatibility-contract)) requires a major bump.
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.0.1] — 2026-07-14
10
+
11
+ Patch: Action Marketplace branding + a release-tooling fix. No runtime/API change.
12
+
13
+ ### Added
14
+
15
+ - `action.yml` now declares Marketplace `branding` (`shield` / `orange`) so the packaged GitHub Action
16
+ can be listed on the GitHub Actions Marketplace. No change to the Action's inputs, outputs, or runtime.
17
+
18
+ ### Fixed
19
+
20
+ - `npm run bump` now rewrites the bare `` `Pin `@>=X`` `` floor in `SKILL.md` — it previously bumped only
21
+ the `cowork-harness@>=`-prefixed floors, so that one line stayed stale (it had drifted since `0.33.0`
22
+ and shipped stale in `1.0.0`). A new `check:versions` invariant (5b) fails on any `@>=X` in `SKILL.md`
23
+ that doesn't match the floor, and a regression test covers the bump path.
24
+
25
+ ### Changed
26
+
27
+ - `RELEASING.md` documents maintaining the moving `v1` / `v1.0` tags on each release, so
28
+ `uses: yaniv-golan/cowork-harness@v1` resolves to the latest 1.x.
29
+ - Pruned stale pre-1.0 versioning language now that the compatibility contract is in force
30
+ (README, `SPEC.md`, `RELEASING.md`, `docs/maintenance.md`, this file); `SECURITY.md` now states
31
+ that only the latest published release is supported.
32
+
33
+ ## [1.0.0] — 2026-07-13
34
+
35
+ **First stable release.** The compatibility contract in
36
+ [SPEC.md §12](./SPEC.md#12-versioning--the-10-compatibility-contract) is now in force: the covered
37
+ surfaces — the CLI commands + exit codes, the scenario / session / baseline / run-result / cassette /
38
+ protocol schemas, the documented environment variables, and the packaged Action's inputs/outputs — are
39
+ stable, and a breaking change to any of them requires a major version bump. Human-readable text output
40
+ is explicitly not covered. There is **no runtime behavior change from `0.33.0`** — `1.0.0` blesses that
41
+ runtime as stable and adds the release-engineering guards below.
42
+
43
+ ### Added
44
+
45
+ - **Surface-contract guard** (`test/surface-contract.test.ts`, `npm run gen:surface` /
46
+ `npm run check:surface`) — snapshots the structured §12 surfaces (every `schema/*.json`'s field paths
47
+ and enums including exit codes, `action.yml` inputs/outputs, and the documented `COWORK_*` env-var
48
+ set) into `test/fixtures/surface-baseline.json`; CI reds on undocumented drift so a covered-surface
49
+ change can't ship silently. The CLI/exit-code-semantics/`PlatformBaseline` surfaces are frozen via a
50
+ manual review step in `RELEASING.md`.
51
+ - **`npm run bump -- X.Y.Z --write`** — rewrites every version location via targeted patterns (dry-run
52
+ by default), leaving historical release notes, the baseline pin, and the `V=` agent pins untouched;
53
+ self-checks `check:versions`.
54
+ - **`npm run preflight`** — a local pre-release gate (`check:versions`, CHANGELOG heading, unused tag,
55
+ clean tree, live-key reminder); `--for-tag` additionally asserts `HEAD == origin/main` and a green
56
+ push-event CI run for `HEAD`, mechanically preventing a tag on the wrong commit.
57
+
58
+ ### Changed
59
+
60
+ - CI marks a skipped live scenario suite with a loud "NOT live-validated" job-summary banner; the
61
+ release docs (`RELEASING.md`, `release.yml`) now state that a release tag must go on the merge commit
62
+ (the SHA with a push-event CI run), with recovery steps.
63
+
9
64
  ## [0.33.0] — 2026-07-13
10
65
 
11
66
  ### Fixed
package/README.md CHANGED
@@ -91,7 +91,7 @@ node dist/cli.js replay examples/replays/example-pdf-skill.cassette.json
91
91
 
92
92
  > **Installed globally instead?** Once linked/installed, the same command is `cowork-harness replay
93
93
  > <cassette>` — but the relative path above only resolves from a source checkout's `examples/replays/`.
94
- > From a global install (`npm i -g "cowork-harness@>=0.33.0"`), point at the package root instead:
94
+ > From a global install (`npm i -g "cowork-harness@>=1.0.1"`), point at the package root instead:
95
95
  > `cowork-harness replay "$(npm root -g)/cowork-harness/examples/replays/example-pdf-skill.cassette.json"`
96
96
  > (or copy the cassette into your own project and pass that path).
97
97
 
@@ -101,7 +101,7 @@ Live `run`/`skill` need the prerequisites in the next section — note the `prot
101
101
  > - **Replay only (zero setup):** `cowork-harness replay <cassette>` — no token, no Docker, no agent. The command above.
102
102
  > - **`protocol` (real model, no Docker):** needs only the auth token (item 3 below).
103
103
  > - **Live `container` / `microvm` / `hostloop` / `cowork`:** needs Docker (or Lima for `microvm`), a staged agent, and the token — run `cowork-harness doctor` first.
104
- > - **Invocation:** from a source checkout, `node dist/cli.js <cmd>` (or `npm link` to get the `cowork-harness` command); from a global install, `cowork-harness <cmd>`; the companion skill falls back to `npx "cowork-harness@>=0.33.0"`.
104
+ > - **Invocation:** from a source checkout, `node dist/cli.js <cmd>` (or `npm link` to get the `cowork-harness` command); from a global install, `cowork-harness <cmd>`; the companion skill falls back to `npx "cowork-harness@>=1.0.1"`.
105
105
 
106
106
  Two more worked examples worth knowing about: `examples/scenarios/protocol-smoke.yaml` (zero-Docker smoke
107
107
  test) and `examples/scenarios/skill-loads.yaml` (container-tier acceptance check) — see
@@ -126,7 +126,7 @@ claude plugin marketplace add yaniv-golan/cowork-harness
126
126
  claude plugin install cowork-harness@cowork-harness
127
127
  ```
128
128
 
129
- The skill **self-bootstraps the CLI**: if `cowork-harness` isn't on your PATH it falls back to `npx "cowork-harness@>=0.33.0"` (a version floor that fails loud rather than silently fetching a too-old CLI; Node ≥ 20). Tiers above `protocol` still need Docker/Lima and a Claude Desktop agent binary — see the prerequisites below.
129
+ The skill **self-bootstraps the CLI**: if `cowork-harness` isn't on your PATH it falls back to `npx "cowork-harness@>=1.0.1"` (a version floor that fails loud rather than silently fetching a too-old CLI; Node ≥ 20). Tiers above `protocol` still need Docker/Lima and a Claude Desktop agent binary — see the prerequisites below.
130
130
 
131
131
  It also follows the open [Agent Skills](https://agentskills.io) spec, so it installs cross-editor (Cursor, Codex, OpenCode, …) via [`npx skills`](https://github.com/vercel-labs/skills) (Vercel Labs' CLI implementation of that spec):
132
132
 
@@ -147,7 +147,7 @@ A global install is enough for CI `lint`, reading the teaching skill, and replay
147
147
  To `run` the worked examples live or copy them as a starting point, use a source checkout. (The marketplace
148
148
  skill install itself only pulls `.claude/skills/cowork-harness/` — SKILL.md + `references/` + `scenario.py`/
149
149
  assertion keys, per `.claude-plugin/marketplace.json`'s `source` — not the rest of this table; the full set
150
- above becomes available once the skill's first command self-bootstraps `npx "cowork-harness@>=0.33.0"` — see
150
+ above becomes available once the skill's first command self-bootstraps `npx "cowork-harness@>=1.0.1"` — see
151
151
  [above](#drive-it-from-claude-code-companion-skill) — which pulls the same npm package as the global-install row.)
152
152
 
153
153
  ### Prerequisites for anything above `protocol` fidelity
@@ -665,7 +665,7 @@ jobs:
665
665
  anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
666
666
  ```
667
667
 
668
- Every run writes a Markdown verdict table (scenario, pass/fail, signals, cost/turns when available, staleness findings, and the replay-skipped-assertions honesty line) to the job summary. Inputs: `command`, `path` (required), `version` (npm dist-tag/version, default `latest` — intentional so recipes track the current release; pin an exact version for reproducible CI. The companion skill's `@>=0.33.0` floor guidance applies to ad-hoc CLI installs, not this input), `strict` (applies to `replay` (staleness findings), `lint`/`lint-skill` (WARN/INFO), and `analyze-skill` (any advisory finding); IGNORED — not forwarded — for `verify-cassettes`/`run`, which don't accept the flag), `fail-on-skill-drift` (**`replay`-only** — never forwarded to the analyzers), `extra-args`, `summary` (default `true`), `anthropic-api-key` (live lane only). See [`action.yml`](./action.yml) for the full input reference.
668
+ Every run writes a Markdown verdict table (scenario, pass/fail, signals, cost/turns when available, staleness findings, and the replay-skipped-assertions honesty line) to the job summary. Inputs: `command`, `path` (required), `version` (npm dist-tag/version, default `latest` — intentional so recipes track the current release; pin an exact version for reproducible CI. The companion skill's `cowork-harness@>=1.0.1` floor guidance applies to ad-hoc CLI installs, not this input), `strict` (applies to `replay` (staleness findings), `lint`/`lint-skill` (WARN/INFO), and `analyze-skill` (any advisory finding); IGNORED — not forwarded — for `verify-cassettes`/`run`, which don't accept the flag), `fail-on-skill-drift` (**`replay`-only** — never forwarded to the analyzers), `extra-args`, `summary` (default `true`), `anthropic-api-key` (live lane only). See [`action.yml`](./action.yml) for the full input reference.
669
669
 
670
670
  The provided [GitHub Actions workflow](.github/workflows/ci.yml) runs a **six-stage pipeline**. The **unit** stage is the token-free gate you can copy into your skill repo; the `action-self-test`, `python`, `boundary`, `scenarios`, and `parity-drift` stages are this repo's own fidelity self-tests and are not directly portable (they build the harness's Docker image and run harness-specific e2e scenarios — see [`ci-recipe.md`](./.claude/skills/cowork-harness/references/ci-recipe.md) for the skill-repo template):
671
671
 
@@ -821,7 +821,7 @@ From `1.0.0` this project follows [semver](https://semver.org/). What that cover
821
821
  exit codes, the scenario/session/baseline/`RunResult`/cassette/protocol schemas, the documented
822
822
  `COWORK_HARNESS_*` (+ `COWORK_AGENT_BINARY`/`COWORK_AGENT_IMAGE`) env vars, and the packaged Action's
823
823
  inputs/outputs. Human-readable terminal text is explicitly **not** part of the contract — parse the
824
- `--output-format json` envelope, not stdout. Pre-1.0, minor versions may still break any surface.
824
+ `--output-format json` envelope, not stdout. As of 1.0.0, a backwards-incompatible change to a covered surface is a major bump.
825
825
 
826
826
  ## Status
827
827
 
package/RELEASING.md CHANGED
@@ -64,19 +64,52 @@ Never push the tag before CI is green for the exact commit you intend to tag. Th
64
64
  enforces this (`Require ci.yml success for this commit` step), but don't rely on it — tag a green
65
65
  SHA.
66
66
 
67
- ## Versioning (semver, pre-1.0)
68
-
69
- Pre-1.0: **minor** (`0.N+1.0`) = new features and/or behavior changes; **patch** (`0.N.M+1`) =
70
- backwards-compatible bug fixes only. New commands/flags, or changes to existing behavior (e.g. a
71
- stricter privacy gate, a changed cassette/staleness hash), are a **minor**.
72
-
73
- From `1.0.0`, semver is enforced against the **covered surfaces enumerated in
67
+ **Tag the MERGE COMMIT (main HEAD after the merge), never the release-branch head — and here's why.**
68
+ The publish gate (`require-ci-success`) queries `ci.yml` runs with `--event push` for the tagged SHA.
69
+ `ci.yml` only triggers `on: push` for **`main`** (plus `pull_request` / `workflow_dispatch`), so a
70
+ release-branch/PR head has *only* a `pull_request` run — which the `--event push` filter ignores.
71
+ Tagging that SHA makes the gate poll ~30 min and then FAIL. Only the merge commit (produced by
72
+ `gh pr merge`, then `git pull`ed onto `main`) has a push-event `ci.yml` run. This is why Phase 3 tags
73
+ `main` HEAD after the merge — do **not** "optimize" by tagging the branch commit whose PR CI you just
74
+ watched go green.
75
+
76
+ When you query runs by SHA, use the **full 40-char SHA** (`git rev-parse HEAD`) —
77
+ `gh run list --commit <short-sha>` silently returns empty. If you mis-tag: `git push origin
78
+ :refs/tags/vX.Y.Z && git tag -d vX.Y.Z`, re-tag on `main` HEAD, re-push, and cancel the misfired
79
+ release run. Running `npm run preflight -- --for-tag` right before the tag push mechanically catches
80
+ this (it asserts `HEAD == origin/main` and that a push-event `ci.yml` run succeeded for `HEAD`).
81
+
82
+ ## Versioning (semver)
83
+
84
+ As of `1.0.0`, semver is enforced against the **covered surfaces enumerated in
74
85
  [SPEC.md §12](./SPEC.md#12-versioning--the-10-compatibility-contract)** (CLI + exit codes, the
75
86
  scenario/session/baseline/run-result/cassette/protocol schemas, the documented env vars, and the
76
- packaged Action's inputs/outputs). Human-readable text output is explicitly NOT covered.
87
+ packaged Action's inputs/outputs): a backwards-incompatible change to a covered surface is a
88
+ **major**; a new command/flag or other additive change is a **minor**; a backwards-compatible bug
89
+ fix is a **patch**. Human-readable text output is explicitly NOT covered.
90
+
91
+ **Surface drift is partly automated.** `test/surface-contract.test.ts` snapshots the *structured*
92
+ surfaces — every `schema/*.json` (field paths + enums, including exit-code enums), `action.yml`
93
+ inputs/outputs, and the documented `COWORK_*` env-var set — into `test/fixtures/surface-baseline.json`.
94
+ Any change to those reds CI until you regenerate (`npm run gen:surface`) and review the diff; at `1.0.0`
95
+ a *removal or type/enum change* means a **major** bump. `npm run check:surface` prints the
96
+ added/removed/changed breakdown.
97
+
98
+ **1.0.0 surface-freeze review (one-time, MANUAL — the surfaces the snapshot can't cover).** Before
99
+ tagging `1.0.0`, deliberately review and freeze the surfaces with no machine-readable source:
100
+ - **CLI command + flag surface** — walk `cowork-harness --help` per command; confirm no command/flag is
101
+ removed or repurposed vs `0.x` intent. (No structured source exists — `cli-structural-guard`'s `CASES`
102
+ and `cli-help`'s pinned strings are hand-maintained.)
103
+ - **Per-command exit-code semantics** (SPEC §11) — confirm the documented meanings are the ones you
104
+ intend to hold stable.
105
+ - **The `PlatformBaseline` shape** (Zod in `src/types.ts`; no `schema/*.json`).
77
106
 
78
107
  ## Version locations — bump ALL of these to the same `X.Y.Z`
79
108
 
109
+ > **`npm run bump -- X.Y.Z --write` automates this whole section** (targeted patterns + lockfile +
110
+ > `check:versions`). The list below documents *what it touches* — keep it accurate if you add a new
111
+ > version-bearing string, and add that string to `scripts/bump-version.ts` too.
112
+
80
113
  1. `package.json` → `"version"` (then run `npm install` to update `package-lock.json`).
81
114
  2. `.claude-plugin/marketplace.json` → `plugins[0].version`.
82
115
  3. `.claude/skills/cowork-harness/.claude-plugin/plugin.json` → `"version"`.
@@ -92,8 +125,9 @@ packaged Action's inputs/outputs). Human-readable text output is explicitly NOT
92
125
  8. `.claude/skills/cowork-harness/references/ci-recipe.md` → all `npm i -g "cowork-harness@>=X.Y.Z"` floors
93
126
  (currently 3 occurrences).
94
127
  9. `examples/replays/README.md` → the `npm i -g "cowork-harness@>=X.Y.Z"` floor.
95
- 10. `README.md` → the three `npx "cowork-harness@>=X.Y.Z"` bootstrap-fallback floors. The
96
- `check:versions` lockstep guard enforces these match the SKILL.md floor and will red CI otherwise.
128
+ 10. `README.md` → every `cowork-harness@>=X.Y.Z` floor (the bootstrap-fallback `npx`/`npm i -g` lines
129
+ plus the Action-inputs "companion skill's floor guidance" mention). The `check:versions` lockstep
130
+ guard enforces these match the SKILL.md floor and will red CI otherwise.
97
131
 
98
132
  ## Checklist
99
133
 
@@ -101,7 +135,14 @@ packaged Action's inputs/outputs). Human-readable text output is explicitly NOT
101
135
  - [ ] **CHANGELOG.md** — move everything under `## [Unreleased]` into a new
102
136
  `## [X.Y.Z] — YYYY-MM-DD` section; leave an empty `## [Unreleased]` on top. Include any
103
137
  **upgrade notes** (e.g. "re-record cassettes after the staleness-hash change").
104
- - [ ] Bump every version location listed above (items 1–10). `npm install` after bumping `package.json`.
138
+ - [ ] Bump every version location (items 1–10) with **`npm run bump -- X.Y.Z --write`** — it rewrites all
139
+ of them via targeted patterns and updates the lockfile + self-checks `check:versions` (run without
140
+ `--write` first to preview the diff; dry-run is the default). It deliberately does **not** touch the
141
+ CHANGELOG or add the SKILL.md `- **X.Y.Z:**` release-note bullet — do the CHANGELOG move (above) and
142
+ add that bullet by hand.
143
+ - [ ] `npm run preflight` — local pre-release gate (`check:versions`, CHANGELOG heading present + non-empty,
144
+ tag `vX.Y.Z` not already used, clean tree; warns if the `ANTHROPIC_API_KEY` repo secret is missing so
145
+ the push-to-main live suite would need the `SKIP_LIVE_SCENARIOS` override — see §9).
105
146
  - [ ] `npm run format:check` — fix any issues (`npx prettier --write "src/**/*.ts" "test/**/*.ts"`).
106
147
  A format failure is the most common first-pass CI red.
107
148
  - [ ] `npx tsc -p tsconfig.test.json --noEmit` — typecheck including tests.
@@ -128,12 +169,22 @@ packaged Action's inputs/outputs). Human-readable text output is explicitly NOT
128
169
  git checkout main && git pull origin main
129
170
  git push origin main
130
171
  ```
131
- - [ ] **Phase 3 — tag and publish**:
172
+ - [ ] **Phase 3 — tag and publish** (tag the MERGE COMMIT = current `main` HEAD, per the "why" above):
132
173
  ```
174
+ git checkout main && git pull origin main
175
+ npm run preflight -- --for-tag # asserts HEAD==origin/main AND a green push-event ci.yml run for HEAD
176
+ git tag vX.Y.Z # on main HEAD (the merge commit)
133
177
  git push origin vX.Y.Z
134
178
  gh run watch $(gh run list --workflow=release.yml --limit 1 --json databaseId --jq '.[0].databaseId')
135
179
  ```
136
180
  - [ ] **Clean up**: `git push origin --delete release/X.Y.Z && git branch -d release/X.Y.Z`
181
+ - [ ] **Move the major/minor tags** (so `uses: yaniv-golan/cowork-harness@v1` and `@v1.0` resolve to
182
+ this release — the packaged Action's Marketplace consumers pin those):
183
+ ```
184
+ git tag -f vX vX.Y.Z && git tag -f vX.Y vX.Y.Z # e.g. v1 and v1.0 → v1.2.3
185
+ git push -f origin vX vX.Y
186
+ ```
187
+ (Force-moving these ALIAS tags is expected; never force-move the immutable `vX.Y.Z` release tag.)
137
188
  - [ ] Smoke the published artifact: `npx cowork-harness@X.Y.Z --version` and
138
189
  `npx cowork-harness@X.Y.Z doctor --tier protocol`.
139
190
 
package/SECURITY.md CHANGED
@@ -60,4 +60,4 @@ following are enforced:
60
60
 
61
61
  ## Supported versions
62
62
 
63
- Pre-1.0: only the latest `main` is supported. Pin a commit for reproducibility.
63
+ Only the latest published release is supported. Pin an exact version for reproducibility.
package/SPEC.md CHANGED
@@ -678,8 +678,8 @@ entries (CB-5), adding major European, Asian, and Latin American ccTLDs
678
678
 
679
679
  From `1.0.0` the project follows [semver](https://semver.org/). The surfaces below are the **covered
680
680
  contract**: a backwards-incompatible change to any of them is a MAJOR bump. Everything else — most
681
- importantly human-readable text — is explicitly NOT covered and may change in any release. (Pre-1.0,
682
- nothing here is guaranteed; minor versions may break any surface — see [RELEASING.md](./RELEASING.md).)
681
+ importantly human-readable text — is explicitly NOT covered and may change in any release.
682
+ Covered-surface changes follow semver as of `1.0.0` — see [RELEASING.md](./RELEASING.md).
683
683
 
684
684
  **Covered (semver-guaranteed):**
685
685
 
@@ -182,8 +182,8 @@ The sync extractor currently targets macOS paths (`~/Library/Application Support
182
182
  > locations, the branch → PR → tag → publish flow, the checklist) lives in [RELEASING.md](../RELEASING.md).
183
183
  > The notes here only cover how a *parity sync* relates to versioning.
184
184
 
185
- Versioning follows [SemVer](https://semver.org/); pre-1.0 minor bumps may include breaking changes
186
- (baseline-schema or CLI-contract changes count as breaking). A parity-baseline *content* update (a new
185
+ Versioning follows [SemVer](https://semver.org/); as of 1.0.0 a backwards-incompatible change to a
186
+ covered surface (baseline-schema or CLI-contract changes count) is a major bump. A parity-baseline *content* update (a new
187
187
  Desktop release) is **not** a package version bump on its own — it ships in a normal patch/minor.
188
188
 
189
189
  Release flow — CD via `.github/workflows/release.yml`, published with **npm Trusted Publishing (OIDC)**
@@ -16,7 +16,7 @@ DOES exercise a real gate exchange, see `example-multiselect-gate.cassette.json`
16
16
 
17
17
  Run it with:
18
18
 
19
- > Assumes the `cowork-harness` CLI is available — from a source checkout run `npm ci && npm run build && npm link` first, or `npm i -g "cowork-harness@>=0.33.0"`. (`replay` itself needs nothing else — no token, no Docker.)
19
+ > Assumes the `cowork-harness` CLI is available — from a source checkout run `npm ci && npm run build && npm link` first, or `npm i -g "cowork-harness@>=1.0.1"`. (`replay` itself needs nothing else — no token, no Docker.)
20
20
 
21
21
  ```sh
22
22
  cowork-harness replay examples/replays/example-pdf-skill.cassette.json
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cowork-harness",
3
- "version": "0.33.0",
3
+ "version": "1.0.1",
4
4
  "description": "Scriptable, CI-friendly harness for Claude Cowork's runtime contract for testing skills across scenarios — same agent, mounts, egress allowlist, permission protocol, and sandbox limitations.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -91,6 +91,10 @@
91
91
  "prepack": "find .claude/skills/cowork-harness/scripts/_vendor -name __pycache__ -type d -prune -exec rm -rf {} + 2>/dev/null || true",
92
92
  "prepublishOnly": "npm run ci",
93
93
  "check:versions": "tsx scripts/check-versions.ts",
94
+ "bump": "tsx scripts/bump-version.ts",
95
+ "preflight": "tsx scripts/release-preflight.ts",
96
+ "gen:surface": "tsx scripts/gen-surface.ts",
97
+ "check:surface": "tsx scripts/check-surface.ts",
94
98
  "eval-gate": "tsx scripts/eval-gate.ts",
95
99
  "skill-critique": "tsx scripts/skill-critique.ts",
96
100
  "skill-critique-acceptance": "tsx scripts/skill-critique-acceptance.ts"