devcontour-harness 0.1.0-alpha.11 → 0.1.0-alpha.13

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.
@@ -0,0 +1,478 @@
1
+ ---
2
+ name: harness-setup
3
+ description: Install, configure or upgrade devcontour-harness for a new or existing npm/pnpm Git repository, including pnpm/Turborepo monorepos such as Turbo Starter and task sources such as Relay — decide every routine choice yourself from the repository, ask the user only at real forks (which vendor writes and which accepts, a missing Relay fact, isolation or Docker, rule conflicts, commits, writes to Relay, live model calls), install the exact version in `.harness-tools/`, run `harness init`, and hand over to the `harness` skill; upgrade an installed version with a state copy and a compatibility check.
4
+ ---
5
+
6
+ # Harness setup
7
+
8
+ This skill installs and upgrades the `devcontour-harness` package for one
9
+ repository and leaves it ready for work. Running work orders (plan, run,
10
+ explain, result) is the job of the `harness` skill shipped inside the package
11
+ (`<tooldir>/node_modules/devcontour-harness/skills/harness/SKILL.md`); read it
12
+ once the package is installed.
13
+
14
+ This skill only explains the route. Gates, review, write scope, budgets and
15
+ the reviewer vendor rule are enforced by the harness itself; nothing here
16
+ replaces or relaxes them.
17
+
18
+ Every `harness` command prints one JSON envelope on stdout (`ok` with `data`,
19
+ or `error`); exit codes: 0 success, 2 invalid input, 3 refusal or block,
20
+ 4 infrastructure. Below `$H` is the installed CLI:
21
+ `H=<tooldir>/node_modules/.bin/harness`, and `<pkg>` is
22
+ `<tooldir>/node_modules/devcontour-harness`.
23
+
24
+ Modes: **install** (first setup), **upgrade** (change the installed version),
25
+ **check** (report what is installed; read only). Pick the mode from the
26
+ request; when the repository already has `.harness/` and a pinned or
27
+ installed `$H`, it is upgrade or check, not install.
28
+
29
+ ## Decide or ask
30
+
31
+ Decide everything you can from the repository and the defaults below, and
32
+ report each decision with its reason at the end; the user overrides any of
33
+ them by saying so. Ask only at a fork — a choice that changes who is trusted,
34
+ what leaves the machine, what is spent or which project rule gives way — and
35
+ ask all open forks of a step in one round (multiple choice where the host
36
+ supports it).
37
+
38
+ | Choice | Default (decide yourself) | Ask when |
39
+ | -------------------------- | ------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
40
+ | Writer and reviewer vendor | — | always: which of `claude` / `codex` writes; the other one reviews and accepts |
41
+ | Same vendor for review | never | only one runtime is installed, or the user asked for it |
42
+ | Models and effort | runtime defaults | the user named models |
43
+ | Branch | new branch `harness-setup` from the current branch; an existing `harness/accepted` is reported and left alone | the working tree is not clean |
44
+ | Install location | `<repo>/.harness-tools` | the user asked for a directory outside the repository |
45
+ | Package source | npm `@alpha` resolved to the exact version | the user gave a version or a tarball |
46
+ | Stack profile | built-in by lockfile; a local `.harness/profile.json` when the project does not have its scripts (below) | — |
47
+ | Gates | derived from the project's own commands (step 6), each reported with its command | a check the project or its CI runs would be left out, or no command can prove behaviour without new dependencies |
48
+ | Plan approval | `agent` (`init` default) | the user wants to approve plans themselves (`operator`) |
49
+ | Task source | Relay when the repository or the request names it, otherwise graph files | both are plausible |
50
+ | Relay connection | discovered from the repository and environment | a fact is missing: endpoint or binary/config, `projectId`, which tasks |
51
+ | Relay completion | `review-only` (a human closes the task) | the user wants `complete` |
52
+ | Controller mode | — | the product AGENTS.md tells agents to manage worktrees, commits, merges or Relay history: the overlay then takes precedence over those rules (step 9) |
53
+ | OS isolation | on | prepare or gates fail inside the sandbox (store, network, Docker socket) |
54
+ | Docker environment | none | gates need a database or other service |
55
+ | Project rule conflicts | — | a project rule forbids what setup creates (for example new `.md` files: `.harness/README.md`) |
56
+ | Commit, Relay writes, runs | — | always confirm: committing the setup, `run --source` / `source-deliver` / `source-reconcile`, any live `run` or `review-plan` |
57
+
58
+ ## Install
59
+
60
+ ### 1. Preconditions
61
+
62
+ Node.js ≥ 24.3, `git`, `npm` (or `pnpm` for a pnpm project) and the runtime
63
+ CLIs (`claude --version`, `codex --version`). Whether a runtime is signed in
64
+ is not detectable without a model call: the user confirms it. A missing or
65
+ signed-out runtime is the user's to fix.
66
+
67
+ The package manager version is checked exactly: the built-in profiles pin
68
+ one (`npm-ci` pins npm `11.4.2`), and `package.json#packageManager`
69
+ (`npm@<version>` / `pnpm@<version>`) overrides that pin. Another version is
70
+ `NOT_READY` (`TOOL_VERSION`) at `init`; adding `packageManager` to an existing
71
+ project is the user's decision.
72
+
73
+ ### 2. Repository
74
+
75
+ **Existing.** It must be the top level of a Git repository with at least one
76
+ commit and exactly one lockfile: `package-lock.json` (profile `npm-ci`) or
77
+ `pnpm-lock.yaml` (profile `pnpm-frozen`). With both or none, write a local
78
+ profile (step 6).
79
+
80
+ **New.** Create it only with the user's consent, at the path they gave:
81
+
82
+ ```sh
83
+ git init <repo> && cd <repo>
84
+ npm init -y && npm install # writes package-lock.json
85
+ ```
86
+
87
+ The built-in `npm-ci` profile needs two scripts: `check` and `test`, where
88
+ `test` writes JUnit to `.reports/junit.xml`. A minimal set for Node's test
89
+ runner, with the installed npm version pinned:
90
+
91
+ ```json
92
+ "packageManager": "npm@<npm --version>",
93
+ "scripts": {
94
+ "check": "node --check index.js",
95
+ "test": "mkdir -p .reports && node --test --test-reporter=spec --test-reporter-destination=stdout --test-reporter=junit --test-reporter-destination=.reports/junit.xml"
96
+ }
97
+ ```
98
+
99
+ Add `.reports/` and `node_modules/` to `.gitignore`, a first source file and
100
+ a passing test, then commit. The first commit must pass its own gates: a
101
+ project failing on its base is a separate first task, not a reason to relax
102
+ a gate.
103
+
104
+ ### 3. Branch
105
+
106
+ `git status` must be clean (ask the user about local changes; never stash or
107
+ discard them yourself). Then `git switch -c harness-setup`.
108
+
109
+ `run` creates `refs/heads/harness/accepted` from the `HEAD` of the main
110
+ checkout when that ref does not exist yet. Keep the setup branch checked out,
111
+ with the setup committed (step 11), until the first `run`: otherwise
112
+ `harness/accepted` starts from a commit without it. When `harness/accepted`
113
+ already exists, report its SHA and leave it alone. Accepted work lands in
114
+ `harness/accepted`, not in the setup branch; merging it anywhere is the
115
+ user's decision.
116
+
117
+ ### 4. Install the exact version
118
+
119
+ Resolve `@alpha` to the exact version first and install that:
120
+ `npm view devcontour-harness@alpha version`. For a tarball, compare
121
+ `shasum -a 256 <file.tgz>` with the sha256 the user gave and stop on a
122
+ mismatch.
123
+
124
+ **Inside the project** — `<tooldir>` = `<repo>/.harness-tools`:
125
+
126
+ ```sh
127
+ T=<repo>/.harness-tools
128
+ mkdir -p "$T"
129
+ npm install --prefix "$T" --save-exact --omit=dev --ignore-scripts devcontour-harness@<version>
130
+ printf 'node_modules/\n' > "$T/.gitignore"
131
+ ```
132
+
133
+ The version is pinned by `.harness-tools/package.json` and its
134
+ `package-lock.json`, not by a directory name; both are committed with the
135
+ setup, `node_modules/` is not. The project's own `package.json` and lockfile
136
+ do not change. When the project declares npm `workspaces` or a
137
+ `pnpm-workspace.yaml` whose globs match `.harness-tools`, or lint/type/test
138
+ globs that would read it, tell the user before going on.
139
+
140
+ In a fresh clone, or after `node_modules/` was removed, restore the pinned
141
+ version without changing the pin:
142
+
143
+ ```sh
144
+ npm ci --prefix <repo>/.harness-tools --omit=dev --ignore-scripts
145
+ ```
146
+
147
+ A tarball source records a local `file:` path in `.harness-tools/package.json`
148
+ that does not exist on other machines: install it the same way, but keep the
149
+ whole directory out of Git with `.git/info/exclude` instead of committing it:
150
+
151
+ ```sh
152
+ npm install --prefix "$T" --omit=dev --ignore-scripts <file.tgz>
153
+ grep -qxF '.harness-tools/' <repo>/.git/info/exclude || echo '.harness-tools/' >> <repo>/.git/info/exclude
154
+ ```
155
+
156
+ **Outside the project** — any directory the user names, the same commands
157
+ with `--prefix <tooldir>`; nothing is written into the repository.
158
+
159
+ Then:
160
+
161
+ ```sh
162
+ H=<tooldir>/node_modules/.bin/harness
163
+ $H --version
164
+ $H doctor
165
+ ```
166
+
167
+ Never install the package into the project's own dependencies as part of this
168
+ route, and never put the execution state inside the repository.
169
+
170
+ ### 5. Read the project
171
+
172
+ Before choosing gates read: the root and package `package.json` scripts, the
173
+ lockfile, `pnpm-workspace.yaml`, `turbo.json`, every `AGENTS.md`, CI
174
+ workflows (they show which commands the project trusts), code generators
175
+ (OpenAPI, SDK, Prisma) and Relay traces (`relay.config.json`,
176
+ `relay.workspace.json`, `.artifacts/relay/`, Relay commands in AGENTS.md).
177
+
178
+ ### 6. Stack profile and gates
179
+
180
+ **A built-in profile fits** when the project already has the scripts it
181
+ calls (`npm-ci` / `pnpm-frozen`: `check`, and `test` writing JUnit to
182
+ `.reports/junit.xml`). Then skip to step 7.
183
+
184
+ **Otherwise write `.harness/profile.json`** (`<pkg>/schemas/stack-profile.schema.json`)
185
+ and pass `--profile ./.harness/profile.json` to `init`. This is the usual
186
+ case for a pnpm/Turborepo monorepo; Turbo Starter is the worked example
187
+ (`<pkg>/docs/turbo-starter.md`, `<pkg>/examples/turbo-starter/policy.json`):
188
+
189
+ - `prepare`: `pnpm install --frozen-lockfile`.
190
+ - check gates (`kind: check`): the project's own type and build commands per
191
+ package, called directly — `pnpm --filter <package> run check-types`,
192
+ `… run build`, with `dependsOn` where one needs another's output. Prefer
193
+ the per-package commands a root script runs when the root script also
194
+ does work that cannot run in a worktree (for example an environment check
195
+ that fails on the pinned Node); that keeps every check the root script
196
+ makes. Leaving out a check the project or its CI runs is the user's
197
+ decision. Never call `turbo run` without `--force` (a cache hit proves
198
+ nothing), and `pnpm exec turbo` only with an exactly pinned `turbo`.
199
+ - test gates (`kind: test`, at least one, with
200
+ `report: { type: junit, path: .artifacts/junit/<gate>.xml }`): an existing
201
+ test command that can write JUnit without new dependencies (Node's test
202
+ runner `--test-reporter=junit --test-reporter-destination=…`, Vitest's
203
+ built-in `junit` reporter). When the project has none, or its AGENTS.md
204
+ forbids tests in product code, put acceptance tests in
205
+ `.harness/acceptance/*.test.mjs` run by `node --test`, as the example does.
206
+ When no command can prove behaviour without adding a dependency, ask.
207
+ - code generation (OpenAPI, SDK): a test gate that runs the project's pinned
208
+ generator and fails on any difference in the generated paths
209
+ (`<pkg>/examples/turbo-starter/harness/acceptance/generation.test.mjs`).
210
+ Generated outputs are not `generatedPaths` (that list means "protected");
211
+ orders that change generated code list the outputs in their write scope.
212
+ `generatedPaths` is only for ignored outputs nobody edits (Prisma client).
213
+ - `protectedPaths`: lockfile, every `package.json`, `pnpm-workspace.yaml`,
214
+ `turbo.json`, `.npmrc`, `scripts/`, `infra/`, CI, every `AGENTS.md`,
215
+ generator inputs and configuration, `.harness/`, `.harness-tools/`.
216
+ - `toolchain`: the package manager version from `packageManager`, and
217
+ `node: ">=24.3"`.
218
+ - `environment.values` for variables the commands need to stay local
219
+ (for example `DOTENV_CONFIG_PATH: /dev/null`, `CHECKPOINT_DISABLE: 1`);
220
+ secrets only by name in `environment.secrets`, never their values.
221
+
222
+ A gate that is red on the current base stays: fixing the base is a separate
223
+ first work order. List the gates with the command each one runs in the
224
+ report.
225
+
226
+ ### 7. `harness init`
227
+
228
+ ```sh
229
+ $H init --repository <repo> --writer <claude|codex> --plan
230
+ $H init --repository <repo> --writer <claude|codex>
231
+ ```
232
+
233
+ Add `--profile ./.harness/profile.json` with a local profile. The first call
234
+ writes nothing: read `data.files`, `data.conflicts`,
235
+ `data.toolchain.problems`, `data.facts` and `data.manifest.paths.state`, and
236
+ resolve every conflict or problem with the table of the `harness` skill
237
+ (step 3) before the second call. `init` is create-only: `.harness/.gitignore`,
238
+ `.harness/README.md`, `.harness/project.json` (local, not under Git) and
239
+ `.harness/policy.json`. The state lives outside the repository
240
+ (`HARNESS_STATE_HOME`, `XDG_STATE_HOME` or `~/.local/state/devcontour-harness`).
241
+
242
+ ### 8. Runtimes, approval, isolation
243
+
244
+ Edit `.harness/policy.json` only as the decisions require:
245
+
246
+ - models: `model` on `roles.<role>.writer` and on `review.code.reviewer`,
247
+ `review.plan.reviewer`, `review.contract.reviewer`;
248
+ - effort: `effort` (`low`, `medium`, `high`, `xhigh`) only on bindings whose
249
+ `runtime` is `codex`; on any other runtime it is refused;
250
+ - a reviewer of the writer's vendor (an explicit yes only): on
251
+ `review.code` and/or `review.contract`, set `reviewer.runtime` to the
252
+ writer's runtime and add `"vendor": "same-vendor-allowed"`. Leave
253
+ `review.plan` with the other vendor: `init` reads the saved writer from the
254
+ plan reviewer, so changing it makes every later `init --plan` report
255
+ `content-differs` on `policy.json`. When the user wants it anyway, say so
256
+ and let them decide;
257
+ - operator approval: top-level `"approvalMode": "operator"`;
258
+ - isolation: keep `isolation.mode: os`. When `doctor` or a first `prepare`
259
+ shows the sandbox blocking the package store, the network or the Docker
260
+ socket, show the diagnosis and ask between `isolation.domains` and
261
+ `isolation.mode: none`;
262
+ - with the version pin committed inside the project: `.harness-tools/` in
263
+ `repositories[0].protectedPaths` (already there when the local profile
264
+ lists it).
265
+
266
+ Never remove or weaken anything `init` wrote. Keep the writer runtime as
267
+ `init` wrote it: a different writer vendor is a new `init --writer` decision.
268
+
269
+ Check after editing:
270
+
271
+ ```sh
272
+ $H doctor --policy <repo>/.harness/policy.json
273
+ $H init --repository <repo> --plan
274
+ ```
275
+
276
+ `doctor` checks the policy schema, runtimes and toolchain only; `init --plan`
277
+ must show `data.conflicts` = 0. The cross-checks of the policy (effort on a
278
+ non-codex runtime, the reviewer vendor rule, unknown gates) run in
279
+ `$H validate --graph <graph> --policy <repo>/.harness/policy.json`: run it
280
+ for the first graph before any review or run, and fix the policy, not the
281
+ check, when it fails.
282
+
283
+ **Services.** When gates need a database or another service, ask before
284
+ using Docker. With a yes, copy `<pkg>/examples/turbo-starter/harness/`
285
+ (`compose-bridge.mjs`, `compose.acceptance.yml`, `env/`) into `.harness/`,
286
+ adapt the compose model to the project and add `repositories[0].lifecycle`
287
+ (setup, ready, teardown) and the secret names as in
288
+ `<pkg>/docs/turbo-starter.md`; gates that need the service run through
289
+ `node .harness/compose-bridge.mjs exec -- <command>`.
290
+
291
+ ### 9. Controller mode
292
+
293
+ When the product AGENTS.md tells an agent to create worktrees, commit, merge,
294
+ move tasks or keep Relay history itself, the harness controller does those
295
+ operations instead, and the worker needs the controller-mode overlay
296
+ (`<pkg>/templates/controller-mode/README.md`). The overlay takes precedence
297
+ over those product rules, so ask first: show the rules it overrides and say
298
+ that the overlay text names Turbo Starter commands (`pnpm worktree:create`,
299
+ `pnpm rest-api-sdk:generate`) as examples. Without a yes, keep controller
300
+ mode off; the harness still refuses a worker commit (`scope-violation`).
301
+ With a yes, build the pack from the committed AGENTS.md files:
302
+
303
+ ```sh
304
+ node --input-type=module -e "
305
+ import { controllerContext } from '<pkg>/examples/turbo-starter/setup.mjs';
306
+ import { execFileSync } from 'node:child_process';
307
+ const repo = '<repo>';
308
+ const baselineSha = execFileSync('git', ['rev-parse', 'HEAD'], { cwd: repo }).toString().trim();
309
+ controllerContext({ repo, baselineSha, upstream: null }, repo + '/.harness/work/context');
310
+ "
311
+ ```
312
+
313
+ It writes `.harness/work/context/controller-mode/` (the unchanged overlay,
314
+ `references/` with the product AGENTS.md files, `provenance.json`); a rebuild
315
+ starts from an empty directory and never lists `.harness/`. The pack is not
316
+ committed to the repository (some projects forbid new `.md` files): keep it
317
+ out of Git and rebuild it in a fresh clone with the same command. Its
318
+ `provenance.json` records the `HEAD` it was built on, so a rebuild on another
319
+ commit is a new pack digest and the next Relay snapshot needs a new approval
320
+ even when no AGENTS.md changed.
321
+
322
+ ```sh
323
+ grep -qxF '.harness/work/context/controller-mode/' <repo>/.git/info/exclude \
324
+ || echo '.harness/work/context/controller-mode/' >> <repo>/.git/info/exclude
325
+ ```
326
+
327
+ File graphs list `"context": ["controller-mode"]` on every order; Relay tasks
328
+ list it in `tasks[].context` of the connector (step 10). A changed AGENTS.md
329
+ or overlay means rebuilding the pack, and the next snapshot needs a new
330
+ approval; a result already accepted is still delivered.
331
+
332
+ ### 10. Task source
333
+
334
+ **Graph files.** Nothing more to configure; the `harness` skill plans the
335
+ first graph.
336
+
337
+ **Relay.** Write `.harness/connector.json` from `<pkg>/docs/connectors.md`
338
+ ("Конфигурация"):
339
+
340
+ - `"version": 1` and `"provider": "relay-v1-http"` or `"relay-v2-cli"`,
341
+ always explicit — never guessed from a binary named `relay`;
342
+ - `projectId` and `actor` (`harness-controller`) at the top level;
343
+ `repository.id` is the policy repository id (`app` after `init`);
344
+ - adapter settings nested under `relay`: v1 `relay.endpoint`; Relay-2
345
+ absolute `relay.binary` and `relay.config`, `relay.project` for a workspace,
346
+ `relay.env` that may set `RELAY_HOME` (and on macOS `DYLD_LIBRARY_PATH` to
347
+ SQLite ≥ 3.44) but never `RELAY_CONFIG` or `RELAY_ACTOR`;
348
+ - `tasks[]`: `ref`; `role` and `gates` only from `data.facts` of `init`;
349
+ `writeScope` (required) = the role's `writePaths` from the facts, or, when
350
+ the role has none, `["."]` (the whole repository) or the narrower paths the
351
+ task names (with generated outputs when it changes them); `criteria` mapped
352
+ to a gate and a JUnit `testId`; `"context": ["controller-mode"]` with
353
+ controller mode (step 9) — the pack is read from `contextDir`, by default
354
+ `work/context` next to `connector.json`;
355
+ - `completion: { "mode": "review-only" }` unless the user chose `complete`;
356
+ `eligibility.columns` from the project's board; `history.enabled: true`;
357
+ - no `stateDir` (pass `--state <manifest.paths.state>`), no secret values.
358
+
359
+ Checks that do not write to Relay:
360
+
361
+ ```sh
362
+ $H source-check --source <repo>/.harness/connector.json --policy <repo>/.harness/policy.json --state <state>
363
+ $H source-snapshot --source <repo>/.harness/connector.json --policy <repo>/.harness/policy.json --state <state> --out <dir outside the repo>
364
+ $H validate --graph <dir outside the repo>/graph.json --policy <repo>/.harness/policy.json --context <dir outside the repo>/context.json
365
+ ```
366
+
367
+ `source-check` is read only. `source-snapshot` reads Relay but records the
368
+ snapshot in the connector state under `--state` and writes `graph.json`,
369
+ `context.json` and `manifest.json` to `--out`; its output names the pinned
370
+ local packs (`localContext`) and a context over the limits as
371
+ `LIMIT_EXCEEDED`. `run --source`,
372
+ `source-deliver` and `source-reconcile` write to Relay: run them only after
373
+ the user says so.
374
+
375
+ ### 11. Commit
376
+
377
+ With the user's yes: commit on the setup branch `.harness/.gitignore`,
378
+ `.harness/README.md`, `.harness/policy.json`, `.harness/profile.json`,
379
+ `.harness/connector.json`, `.harness/acceptance/` and the bridge files when
380
+ present, and, for an npm source inside the project,
381
+ `.harness-tools/.gitignore`, `.harness-tools/package.json`,
382
+ `.harness-tools/package-lock.json`. Never `project.json`, `node_modules/` or
383
+ the state or the controller-mode pack. Do not push or merge.
384
+
385
+ ### 12. Report and hand over
386
+
387
+ Report: installed version (and the tarball sha256), `doctor`, every decision
388
+ of the table with its reason, the gates with their commands, the policy
389
+ edits, controller mode, the connector check and snapshot (`graphId`,
390
+ `graphDigest`, eligibility per task), the commit SHA. Then continue with the
391
+ `harness` skill: brief → `plan-request` → draft → `plan-check` → `validate`
392
+ → `run` (for Relay: `review-plan` and `run --source`). A live `run` or
393
+ `review-plan` calls paid models: start it when the user asks.
394
+
395
+ ## Upgrade
396
+
397
+ An upgrade changes only the package. Policy, profiles, graphs, accepted
398
+ results and `.harness/project.json` stay as they are unless the user decides
399
+ otherwise in step 7.
400
+
401
+ 1. **Current and target.** `$H --version`; target from the user, or
402
+ `npm view devcontour-harness dist-tags`. Install the target into a scratch
403
+ directory outside the repository (`N=<scratch>/node_modules/.bin/harness`)
404
+ and read its `docs/release-notes/<version>.md` and the format matrix of
405
+ `docs/upgrade.md` for every version in between.
406
+ 2. **Stop the writers.** `$H status --state <state>`; `$H pause --state <state>`;
407
+ wait for active attempts to finish or ask the user to stop them; stop every
408
+ `run` process on this state. A paused queue alone is not enough.
409
+ 3. **Record.** `git rev-parse refs/heads/harness/accepted` and
410
+ `$H result --state <state> --work <id>` for the accepted orders.
411
+ 4. **Copy the state** while nothing writes: `cp -Rp <state> <state>.bak-<old version>`.
412
+ 5. **Check on a second copy** with the target version: `$N doctor --state <copy>`
413
+ and `$N status --state <copy>`. Hash the copy's files before and after;
414
+ they must not change. Stop and report when `doctor` shows
415
+ `data.state.compatible: false` or `status` fails with
416
+ `STATE_VERSION_UNSUPPORTED`.
417
+ 6. **Install** into the same `<tooldir>`:
418
+ `npm install --prefix <tooldir> --save-exact --omit=dev --ignore-scripts devcontour-harness@<new>`
419
+ (or the new `.tgz` after its sha256 check).
420
+ 7. **Verify.**
421
+ - `$H --version`; `$H doctor --state <state> --policy <repo>/.harness/policy.json`.
422
+ - `$H init --repository <repo> --plan` must show `data.conflicts` = 0.
423
+ `content-differs` on `.harness/project.json` means a saved decision no
424
+ longer matches: the built-in profile digest changed (compare
425
+ `$H profile --profile <id>` with the pin in `project.json`), the
426
+ repository moved (`root`, `projectId`), or the API version changed.
427
+ Show the difference to the user. With their yes, move the manifest out
428
+ of the repository, next to the state copy of step 4
429
+ (`mv <repo>/.harness/project.json <state>.bak-<old version>/project.json`),
430
+ and run
431
+ `$H init --repository <repo> --profile <saved profile.ref> --writer <saved runtimes.writer> --state <saved paths.state>`
432
+ with the values of the moved manifest, so the profile choice and the state
433
+ path are kept; never edit the manifest by hand.
434
+ - The policy is not regenerated. List gates or defaults of the new profile
435
+ that the policy lacks and add them only with the user's yes; never weaken
436
+ the existing ones. A changed policy changes the facts of planning, so
437
+ graphs may need plan review or approval again (`explain` names it).
438
+ - `$H validate --graph <graph> --policy <repo>/.harness/policy.json` for
439
+ every `.harness/work/*.json` graph.
440
+ - Read the release notes for new options the setup should use (for
441
+ example `tasks[].context` of the connector for controller mode) and
442
+ propose them; do not apply them unasked.
443
+ 8. **Resume.** `$H resume --state <state>`, then `run` as before. Attempts that
444
+ were active at the stop are not continued with old tokens; recovery either
445
+ accepts a saved integration SHA or ends the attempt.
446
+ 9. **Commit** the changed `.harness-tools/package.json` and
447
+ `package-lock.json` on a branch, with the user's yes.
448
+
449
+ Rollback: stop the writers again; reinstall the old exact version; when the
450
+ new version already wrote state the old one cannot read, restore the copy of
451
+ step 4 and reconcile `harness/accepted` with the recorded SHAs
452
+ (`docs/upgrade.md`, "Откат"). Never `git reset --hard` the user's repository.
453
+
454
+ ## Check
455
+
456
+ Read only: `$H --version`, `$H doctor`, `$H init --repository <repo> --plan`,
457
+ `$H status --state <paths.state>`, and the pin in
458
+ `.harness-tools/package.json` when present. Report a mismatch between the pin
459
+ and `$H --version`, or a missing `node_modules/` (restore with `npm ci`, step
460
+ 4); do not fix it unasked.
461
+
462
+ ## Don'ts
463
+
464
+ - Do not ask what the repository answers, and do not decide a fork of the
465
+ table for the user: the vendors, a reviewer of the writer's vendor,
466
+ controller mode, a left-out project check, isolation, Docker, project rule
467
+ conflicts, commits, writes to Relay and live runs are theirs.
468
+ - Do not install into the project's dependencies, edit its `package.json` or
469
+ lockfile (except when bootstrapping a new project with consent), or put the
470
+ state inside the repository.
471
+ - Do not weaken gates, budgets, isolation or the reviewer rule, and do not set
472
+ `same-vendor-allowed` without an explicit yes.
473
+ - Do not overwrite `.harness/project.json` or `.harness/policy.json` on a
474
+ `content-differs`; that is a decision to show to the user.
475
+ - Do not write to Relay, call live models, push, tag or publish without being
476
+ asked.
477
+ - Do not upgrade while a controller writes the state, and do not skip the
478
+ state copy.
@@ -27,6 +27,11 @@ not a file committed to the target repository. Some projects forbid new
27
27
  4. Add `"context": ["controller-mode"]` to every order of the graph. Writer
28
28
  and reviewers then see the same pinned bytes, and a changed overlay
29
29
  requires a new plan approval.
30
+ For tasks read from Relay, list the pack in the connector instead:
31
+ `"context": ["controller-mode"]` on each entry of `tasks[]`, with the
32
+ directory at `<contextDir>/controller-mode` (`contextDir` defaults to
33
+ `work/context` next to `connector.json`). The snapshot pins it the same
34
+ way, and a changed overlay needs a new snapshot and approval.
30
35
  5. Protect the product AGENTS.md files and the harness setup through the
31
36
  policy's `protectedPaths`. The worker cannot change the rules it runs
32
37
  under.