agents-handoff 0.0.0-stage → 2.0.3

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.
Files changed (56) hide show
  1. package/CHANGELOG.md +192 -0
  2. package/LICENSE +21 -0
  3. package/README.md +150 -2
  4. package/SKILL.md +147 -0
  5. package/capability-registry.json +27 -0
  6. package/docs/ARCHITECTURE.md +187 -0
  7. package/docs/CHANGELOG.md +196 -0
  8. package/docs/CLI.md +299 -0
  9. package/docs/COMPATIBILITY.md +124 -0
  10. package/docs/CONTRIBUTING.md +134 -0
  11. package/docs/FORMAT.md +185 -0
  12. package/docs/INSTALL.md +394 -0
  13. package/docs/INTEGRATION.md +188 -0
  14. package/docs/LEVEL4.md +202 -0
  15. package/docs/LEVEL5.md +96 -0
  16. package/docs/PERMISSIONS.md +145 -0
  17. package/docs/PROVENANCE.md +110 -0
  18. package/docs/SECURITY.md +93 -0
  19. package/docs/SESSIONS.md +97 -0
  20. package/docs/TROUBLESHOOTING.md +158 -0
  21. package/docs/UNINSTALL.md +148 -0
  22. package/docs/UPGRADE.md +177 -0
  23. package/docs/_config.yml +18 -0
  24. package/docs/_data/nav.yml +36 -0
  25. package/docs/_layouts/default.html +31 -0
  26. package/docs/assets/style.css +88 -0
  27. package/docs/index.md +92 -0
  28. package/docs/sessions.json +34 -0
  29. package/handoff.config.example.json +35 -0
  30. package/handoff.config.schema.json +117 -0
  31. package/install/CHANGELOG.md +48 -0
  32. package/install/README.md +76 -0
  33. package/install/install.mjs +1455 -0
  34. package/install/package.json +39 -0
  35. package/package.json +66 -4
  36. package/permission-policy.json +33 -0
  37. package/refs/ADAPTERS.md +33 -0
  38. package/refs/bootstrap.md +59 -0
  39. package/refs/brief-checklist.md +79 -0
  40. package/refs/handbook.md +58 -0
  41. package/refs/protocol.md +117 -0
  42. package/refs/roles.md +75 -0
  43. package/refs/validator.md +73 -0
  44. package/schemas/handoff.schema.json +275 -0
  45. package/skill.json +147 -0
  46. package/templates/HANDOFF.llm.schema.json +144 -0
  47. package/templates/HANDOFF.template.md +40 -0
  48. package/tests/acceptance/acceptance.yaml +209 -0
  49. package/tests/fixtures/minimal-transcript.jsonl +2 -0
  50. package/tools/agent-handoff.mjs +22 -0
  51. package/tools/agents-handoff.mjs +410 -0
  52. package/tools/capability-registry.mjs +120 -0
  53. package/tools/handoff.mjs +398 -0
  54. package/tools/handoff.test.mjs +668 -0
  55. package/tools/lib/handoff-root.mjs +161 -0
  56. package/tools/runtime-engine.mjs +330 -0
package/docs/CLI.md ADDED
@@ -0,0 +1,299 @@
1
+ ---
2
+ title: Command reference
3
+ ---
4
+
5
+ # Command reference
6
+
7
+ Every executable in this repository. Each one is a standalone Node script with zero
8
+ dependencies; there is no build step and nothing to install to run them from a checkout.
9
+
10
+ | Executable | Role | Invoked as |
11
+ |---|---|---|
12
+ | [`tools/handoff.mjs`](https://github.com/Alot1z/agent-handoff/blob/main/tools/handoff.mjs) | Capture engine: transcript in, handoff folder out. | `node tools/handoff.mjs <verb>`, or the published `agents-handoff` bin |
13
+ | [`tools/agents-handoff.mjs`](https://github.com/Alot1z/agent-handoff/blob/main/tools/agents-handoff.mjs) | Runtime layer: acts on the state of the store. | `node tools/agents-handoff.mjs <verb>` |
14
+ | [`tools/agent-handoff.mjs`](https://github.com/Alot1z/agent-handoff/blob/main/tools/agent-handoff.mjs) | Compatibility forwarder: the runtime layer's pre-rename path, kept so older notes and hooks keep working. | `node tools/agent-handoff.mjs <verb>` (forwards to `tools/agents-handoff.mjs`) |
15
+ | [`tools/runtime-engine.mjs`](https://github.com/Alot1z/agent-handoff/blob/main/tools/runtime-engine.mjs) | Bounded execution: evaluates an operation against the policy before running it. | `node tools/runtime-engine.mjs <verb>` |
16
+ | [`tools/capability-registry.mjs`](https://github.com/Alot1z/agent-handoff/blob/main/tools/capability-registry.mjs) | Health probes for declared capabilities. | `node tools/capability-registry.mjs <verb>` |
17
+ | [`install/install.mjs`](https://github.com/Alot1z/agent-handoff/blob/main/install/install.mjs) | Installs, updates and removes the skill. | `npx agents-handoff <verb>` |
18
+
19
+ Requires Node.js 18 or newer.
20
+
21
+ ## `tools/handoff.mjs` — capture engine
22
+
23
+ Reads a transcript and writes a handoff directory. The source is never written; every
24
+ output file is a render of it. The folder layout, the manifest fields and the provenance
25
+ chain are in [FORMAT.md](FORMAT.md).
26
+
27
+ | Verb | Effect |
28
+ |---|---|
29
+ | `build --source <file>` | Build or update a handoff from a transcript. |
30
+ | `--handoff --source <file>` | Alias of `build`. |
31
+ | `list [project-or-prefix]` | List sessions, newest first. |
32
+ | `show <id-prefix>` | Print the rendered brief. |
33
+ | `verify <id-prefix>` | Re-check the provenance chain and the file set. |
34
+ | `rename <id-prefix> <new-project>` | Move a session to another project. |
35
+ | `retitle <id-prefix> <new-name>` | Give a session a readable directory name. |
36
+ | `config` | Report the resolved store root, the rule that chose it, the config file, the schema path and the configured project. |
37
+
38
+ ### `build` flags
39
+
40
+ | Flag | Meaning |
41
+ |---|---|
42
+ | `--source <file>` | Required. A `.jsonl` file is read line by line; any other extension is read as text. |
43
+ | `--session <id>` | Session id. Defaults to the `session` field on the first JSONL line, else the source file name without its extension. |
44
+ | `--harness <name>` | Harness name recorded in the manifest. Defaults to the source's own field, else `unknown`. |
45
+ | `--model <name>` | Model recorded in the manifest. Defaults to the source's own field, else empty. |
46
+ | `--project <name>` | Project slug. The session directory is created under `projects/<project>/`. |
47
+ | `--objective <text>` | Objective line for the brief. |
48
+
49
+ `build` is incremental. It appends the turns with `seq` above the manifest's `watermark`,
50
+ sets the watermark to the highest `seq` seen, and increments `revisions`. `timeline.jsonl`
51
+ is append-only; the renders are rewritten. A rebuild with no new turns and an unchanged
52
+ source hash writes nothing and prints `handoff: up-to-date`.
53
+
54
+ ### Exit codes
55
+
56
+ | Code | Meaning |
57
+ |---|---|
58
+ | 0 | Success. For `verify`, all checks passed. |
59
+ | 1 | `verify` failed: the manifest hash, the timeline line count or the LLM payload does not match. |
60
+ | 2 | Usage error, for example `build` without `--source`. |
61
+ | 3 | The id prefix matched more than one session, or none. |
62
+ | 4 | No match for `show`/`verify`/`rename`/`retitle`; also `build` when no turn could be parsed from the source. |
63
+
64
+ ## `tools/agents-handoff.mjs` — runtime layer
65
+
66
+ Acts on the state of the store rather than being invoked per file. Every mutating command
67
+ takes a lock in `<root>/.locks/`, so two runs cannot capture or merge the same session at
68
+ once. Behaviour per command is described in [LEVEL4.md](LEVEL4.md).
69
+
70
+ | Verb | Effect |
71
+ |---|---|
72
+ | `auto --source <file>` | Build only when the source is newer than the stored manifest; skip within `--min-fresh-ms` (default 60000). |
73
+ | `verify-gate <id-prefix>` | Five checks: `sha`, `counts`, `payload`, `contract`, `evidence`. |
74
+ | `promote <id-prefix>` | Stamp `promoted_at` and `promoted_by` on the manifest. |
75
+ | `merge <a> <b>` | Compose two sessions of one project into `<a>+merge+<b>`. |
76
+ | `dispatch <id-prefix> --task <objective>` | Hand the continuation to a worker. See [LEVEL5.md](LEVEL5.md). |
77
+ | `federated-merge --from <root> [--from <root> …] [--dry-run]` | Import sessions from another store root. |
78
+ | `self-improve` | Scan for brief shortfalls and write a rules candidate file. |
79
+ | `index` | Rebuild `INDEX.json` and report sessions with no `HANDOFF.md`. |
80
+
81
+ `tools/agent-handoff.mjs` is a forwarder, not a second implementation: it spawns
82
+ `tools/agents-handoff.mjs`, passes every argument through, and exits with the same code. Its
83
+ stdout and stderr are the runtime's own. Use the new path in anything you write today; the
84
+ old one exists only so a command that already names it does not break.
85
+
86
+ `auto` flags: `--source` (required), `--session`, `--harness`, `--project`, `--min-fresh-ms`.
87
+ `dispatch` flags: `--task` (required), `--role` (default `implementation-agent`), `--parent`
88
+ (default `handoff:<id>`), `--broker <root>`, `--live`.
89
+
90
+ `verify-gate` exits 0 for both `VERIFIED` and `REJECTED`. Read `ok` or `verdict` from the
91
+ JSON; the exit code alone does not report a rejection. `promote` prints the gate result but
92
+ does not enforce it — run `verify-gate` and branch on the verdict first.
93
+
94
+ ### Exit codes
95
+
96
+ | Code | Meaning |
97
+ |---|---|
98
+ | 0 | Success. |
99
+ | 1 | A mutating command failed; the lock was released. |
100
+ | 2 | Usage or configuration error, including an unresolvable store root. |
101
+ | 3 | A lock is held, an id prefix is ambiguous or missing, or a referenced path is missing. |
102
+ | 4 | No session matches the prefix. |
103
+
104
+ ## `tools/runtime-engine.mjs` — bounded execution
105
+
106
+ Evaluates every operation against [permission-policy.json](https://github.com/Alot1z/agent-handoff/blob/main/permission-policy.json)
107
+ before it runs, and records the decision whether it was allowed, refused or failed. Levels,
108
+ risk classes and grants are in [PERMISSIONS.md](PERMISSIONS.md).
109
+
110
+ | Verb | Effect |
111
+ |---|---|
112
+ | `evaluate --risk R0..R4 [--target <path>] [--json]` | Classify the target and return the verdict without running anything. |
113
+ | `run --risk R0\|R1 --op read --target <path>` | Evaluate, then read the file when allowed. |
114
+ | `run --risk R1 --op spawn --args "<node args>"` | Evaluate, then run the local node binary with a 15000 ms timeout. |
115
+ | `job --session <s> --steps <n> [--fail-at <k>] [--json]` | Run a checkpointed job of `n` steps, gated at `R1`. |
116
+ | `resume --session <s> --steps <n> [--json]` | Continue from the last sealed checkpoint. |
117
+ | `status --session <s> [--json]` | Report the durable state of a session's job. |
118
+ | `policy` | Print the loaded policy. |
119
+
120
+ Verdicts are `ALLOWED`, `DENIED` and `NEEDS_AUTH`. A denied or personal-data target is
121
+ refused at any risk class and is never executed.
122
+
123
+ | Code | Meaning |
124
+ |---|---|
125
+ | 0 | Allowed, executed, or completed. |
126
+ | 1 | Execution error, or the child produced no exit code. |
127
+ | 2 | Usage or configuration error. |
128
+ | 3 | Denied. |
129
+ | 4 | Needs authorization, or no checkpoint to resume from. |
130
+ | 5 | Checkpoint corrupt, or its integrity seal does not match. |
131
+ | 137 | Simulated abrupt kill (`--fail-at`). |
132
+ | other | The captured exit code of the spawned child. |
133
+
134
+ ## `tools/capability-registry.mjs` — capability probes
135
+
136
+ Runs a real probe per declared capability and reports what it observed. An unknown probe
137
+ kind, a probe error and an unreadable target are reported as `unknown` or `unhealthy` with
138
+ the evidence — never interpreted into a passing verdict.
139
+
140
+ | Verb | Effect |
141
+ |---|---|
142
+ | `check [--registry <file>] [--json] [<id>]` | Probe every capability, or one by id; write the state file. |
143
+ | `list [--registry <file>] [--json]` | Show declared capabilities with the verdict from the last check. |
144
+
145
+ Probe kinds: `file-exists`, `dir-writable`, `command` (with `command` and `args`; the token
146
+ `<node>` means the running node binary).
147
+
148
+ | Code | Meaning |
149
+ |---|---|
150
+ | 0 | Every required capability is `healthy`. |
151
+ | 1 | A required capability is `unhealthy` or `unknown` — `unknown` is never accepted as healthy. |
152
+ | 2 | The registry file is missing, not valid JSON, or has no `capabilities` array. |
153
+ | 4 | Unknown capability id, or a usage error. |
154
+
155
+ State is written to `<state>/capability-state.json`, where `<state>` is
156
+ `AGENT_HANDOFF_STATE_DIR` or `.agents-handoff/`.
157
+
158
+ ## `install/install.mjs` — installer
159
+
160
+ Published as `agents-handoff`. Location resolution is documented in
161
+ [INSTALL.md](INSTALL.md).
162
+
163
+ A bare verb and its flag form are the same command: `npx agents-handoff update` and
164
+ `npx agents-handoff --update` behave identically.
165
+
166
+ | Verb | Flag form | Effect |
167
+ |---|---|---|
168
+ | `install` (default), `i` | `--install` | Install the skill into every selected target. |
169
+ | `update`, `u` | `--update` | Update to the latest or a named version. |
170
+ | `remove`, `rm` | `--remove` | Remove the installation, keeping everything that is not the installer's. |
171
+ | `verify`, `v` | `--verify` | Verify each installation against the record written when it was installed. |
172
+ | `verify-package`, `vp` | `--verify-package` | Verify each installation against the tarball npm publishes for its version. |
173
+ | `list`, `ls` | `--list` | List every installed location, with its provenance state. |
174
+ | `where` | — | Show the resolved global root and why it was chosen. |
175
+ | `doctor` | `--doctor` | Report which harnesses exist here, what is installed where, and whether each installation still matches its record. Read-only: it never installs. |
176
+ | `help` | `--help`, `-h` | Print the usage block. |
177
+
178
+ Harness targets. With no harness flag the installer keeps its historical single target
179
+ (`--location` / `--path`); any of these switches to named targets, and several may be combined
180
+ in one run — each target gets its own copy and its own install record.
181
+
182
+ | Flag | Target |
183
+ |---|---|
184
+ | `--claude` | `~/.claude/skills/` — Claude Code personal skills. |
185
+ | `--codex` | `~/.codex/skills/` — Codex CLI personal skills. |
186
+ | `--agents` | `~/.agents/skills/` — the harness-neutral store. |
187
+ | `--harness <a,b>` | Named harnesses, comma separated; repeatable. |
188
+ | `--all` | Every harness whose directory exists on this machine. |
189
+ | `--skills-dir <dir>` | Any other stack, exactly; repeatable. |
190
+ | `--project` | Use the per-repository form of the harness directories (`./.claude/skills`, `./.codex/skills`). |
191
+
192
+ | Flag | Meaning |
193
+ |---|---|
194
+ | `--location global\|local\|project` | Target location. Default `global`. |
195
+ | `--path <dir>` | Install to an exact directory. |
196
+ | `--version latest\|<v>` | Version to install. Default `latest`. |
197
+ | `--force`, `-f` | Skip confirmations and overwrite. |
198
+ | `--provenance` | With `verify`: print the install record the check was run against. |
199
+ | `--record` | With `verify-package`: store the fetched tarball hashes in the install record. |
200
+
201
+ ### `update` and `verify` with no harness flag
202
+
203
+ A machine can hold the same skill in several places at once — `~/.claude/skills` for Claude
204
+ Code, `~/.agents/skills` for a neutral store, an account-skill store a desktop client reads.
205
+ With no harness flag, `update` and `verify` act on **every** installation they can find rather
206
+ than on the single target the resolver happens to pick, because updating only the resolved one
207
+ is how a second harness keeps an old engine without anyone noticing.
208
+
209
+ The search covers the resolved global root, `./local/skills`, `./skills`, `~/.claude/skills`,
210
+ `~/.codex/skills`, `~/.agents/skills`, each harness's per-repository form, and every
211
+ account-skill root discovered under the platform's application-data directories. `update`
212
+ reports one line per installation, with the version before and after; `verify` fails the run
213
+ when any copy fails. Neither touches a store, `handoff.config.json` or any file the install
214
+ manifest does not name.
215
+
216
+ ### Exit codes
217
+
218
+ | Code | Meaning |
219
+ |---|---|
220
+ | 0 | The command ran and everything it checked passed. |
221
+ | 1 | Any failure: a usage error, nothing installed to act on, a verification that did not match, or a `verify-package` mismatch. |
222
+
223
+ ### The install record
224
+
225
+ Every install writes `.agents-handoff-install.json` into the copy it just made. [FORMAT.md](FORMAT.md)
226
+ lists the fields and [PROVENANCE.md](PROVENANCE.md) states what the record does and does not prove.
227
+
228
+ | Field | Contents |
229
+ |---|---|
230
+ | `schema_version` | `1.0-install-provenance`. |
231
+ | `product`, `version` | `agents-handoff`, and the version read back out of the installed `SKILL.md`. |
232
+ | `installer_version` | The installer package's own version. |
233
+ | `installed_at` | ISO timestamp of the write. |
234
+ | `harness` | The harness the copy went into, or `null` for the resolved single target. |
235
+ | `target` | Absolute install directory. |
236
+ | `source` | What the copy was made from: `{kind: "tree", path}` for a checkout or release archive, or `{kind: "archive", ref, label, archive_url, archive_sha256}` for a fetched tag archive. |
237
+ | `package` | The npm identity this install should match: `name`, `version`, `registry`, `tarball`, `sha256`, `sha512`, `integrity`, `shasum`, `verified_at`. The hashes are `null` until `verify-package --record` fills them, because a copy made from a tree is not the published tarball. |
238
+ | `file_count`, `manifest_entries` | Files present, and entries in the installer's manifest. |
239
+ | `files_sha256` | One sha256 folded over every manifest path together with that file's own hash. |
240
+ | `files` | Per-path sha256, or `missing` for a path this version does not have. |
241
+
242
+ ### What `verify` compares
243
+
244
+ For each installation: that every manifest file exists (a path the version never had is
245
+ reported as not part of that version rather than failed), that `SKILL.md` carries a name and a
246
+ version, that the engine actually runs — `tools/handoff.mjs config` must exit 0 with its
247
+ resolved-root marker, which also proves the import graph resolves — and that a recomputed
248
+ `files_sha256` equals the recorded one. An installation made before this record existed is
249
+ reported as having none, never silently passed.
250
+
251
+ ### What `verify-package` checks, and in what order
252
+
253
+ Each step makes the next one meaningful:
254
+
255
+ 1. **The registry's own hashes.** The tarball is downloaded for the installed version and must
256
+ match the `integrity` (sha512) and `shasum` (sha1) the registry declares for it; otherwise
257
+ "the published package" is just whatever the network handed over.
258
+ 2. **Per-file identity.** The manifest is compared file by file against the extracted tarball,
259
+ and every difference is named (`not installed`, `not in the published package`, or
260
+ `content differs`).
261
+ 3. **The recorded hash.** When the install record already holds a package `sha256`, it must
262
+ match too.
263
+
264
+ Without `--record` nothing is written; with it, the fetched `sha256`, `sha512`, `integrity`
265
+ and `shasum` are stored in `package`. A version that is not on the registry cannot pass this
266
+ check, and it says so rather than reporting success.
267
+
268
+ ### What `remove` deletes
269
+
270
+ `remove` deletes what the install manifest owns, plus the record it wrote itself and the
271
+ `private` `package.json` stub it created — and nothing else. Store directories, notes, a
272
+ `handoff.config.json`, and any file a later version treats as user data survive by default,
273
+ and every entry that was left is printed under `Kept — not the installer's to delete:`.
274
+
275
+ ## Environment variables
276
+
277
+ | Variable | Read by | Effect |
278
+ |---|---|---|
279
+ | `HANDOFFS_ROOT` | capture engine, runtime layer | Store root. Always wins over every other rule. |
280
+ | `AGENT_HANDOFF_STATE_DIR` | runtime engine, capability registry | State directory for executions, checkpoints, jobs and capability state. Default `<repo>/.agents-handoff`. |
281
+ | `AGENT_HANDOFF_GLOBAL_DIR` | installer | Overrides the resolved global install root. |
282
+
283
+ ## Files written
284
+
285
+ | Path | Written by |
286
+ |---|---|
287
+ | `<root>/INDEX.json` | `build`, `index` |
288
+ | `<root>/projects/<project>/<session>/` | `build`, `merge`, `federated-merge`, `retitle`, `rename` |
289
+ | `<root>/links/<project>.md` | `merge` |
290
+ | `<root>/.locks/<hash>.lock` | every mutating runtime command |
291
+ | `<state>/executions/<id>.json` | `runtime-engine run`, `job` |
292
+ | `<state>/checkpoints/<session>.json` | `runtime-engine job`, `resume` |
293
+ | `<state>/jobs/<session>/work.log` | `runtime-engine job` |
294
+ | `<state>/capability-state.json` | `capability-registry check` |
295
+ | `<skill>/docs/self-improve-candidates.json` | `self-improve` |
296
+ | `<install>/.agents-handoff-install.json` | `install`, `update`, `verify-package --record` |
297
+
298
+ `self-improve` writes inside the skill directory on purpose: the candidate file describes the
299
+ skill's brief rules, so a configured store never collects rule candidates.
@@ -0,0 +1,124 @@
1
+ ---
2
+ title: Compatibility
3
+ ---
4
+
5
+ # Compatibility
6
+
7
+ ## Platforms
8
+
9
+ | Platform | Level | Notes |
10
+ |---|---|---|
11
+ | Windows 10 / 11 | SUPPORTED | Primary development platform |
12
+ | macOS 12+ | SUPPORTED | Uses only portable Node APIs |
13
+ | Linux | SUPPORTED | Uses only portable Node APIs |
14
+
15
+ ## Runtime
16
+
17
+ | Requirement | Version | Why |
18
+ |---|---|---|
19
+ | Node.js | 18.0.0 or newer | ES modules and `node:` built-ins |
20
+ | npm | 9.0.0 or newer | Only if you install from the registry |
21
+
22
+ The engine and the runtime install no packages at run time. `package.json` declares no
23
+ dependencies.
24
+
25
+ ## Architectures
26
+
27
+ | Architecture | Level | Notes |
28
+ |---|---|---|
29
+ | x64 | SUPPORTED | Primary |
30
+ | arm64 (Apple silicon) | TESTED | macOS |
31
+ | arm64 (Linux) | UNTESTED | Expected to work; no CI runner exercises it |
32
+
33
+ ## Node version matrix
34
+
35
+ | Version | Level |
36
+ |---|---|
37
+ | 18.x | SUPPORTED (minimum) |
38
+ | 20.x | SUPPORTED |
39
+ | 22.x | SUPPORTED |
40
+ | < 18 | UNSUPPORTED |
41
+
42
+ ## Input formats the engine parses
43
+
44
+ | Format | How it is detected | Fields read |
45
+ |---|---|---|
46
+ | JSONL | `.jsonl` extension | `seq`, `ts`/`timestamp`, `role`, `kind`, and the text from `text`, `content`, or `parts[].text` |
47
+ | Plain text | Any other extension | A line opening with `user:`, `human:`, `assistant:`, `ai:`, `system:` or `tool:` (or `>` instead of `:`); following lines are appended to that turn |
48
+
49
+ Turn classification, in this order: `kind` containing `tool`, or `role: "tool"`, becomes `TOOL`;
50
+ `kind` containing `reason` or `think` becomes `THOUGHT`; `role: "user"` (or `kind: "human"`) becomes
51
+ `USER`; `role: "assistant"` (or `kind: "ai"`) becomes `AGENT`; everything else becomes `OTHER`.
52
+ Malformed JSONL lines and turns with empty text are skipped.
53
+
54
+ ## Session sources
55
+
56
+ Harness stores are read by adapters that normalise a store into the canonical JSONL shape; the
57
+ engine has no harness-specific code. See [../refs/ADAPTERS.md](https://github.com/Alot1z/agent-handoff/blob/main/refs/ADAPTERS.md).
58
+
59
+ | Source shape | Route |
60
+ |---|---|
61
+ | JSONL session directory | Adapter projects each session file to canonical JSONL |
62
+ | SQLite session database | Adapter reads the database read-only and projects rows to canonical JSONL |
63
+ | Exported JSONL | Passed directly to `build --source` |
64
+ | Plain text or Markdown log | Role-marker parser built into the engine |
65
+
66
+ ## Filesystem
67
+
68
+ | Feature | Windows | macOS | Linux |
69
+ |---|---|---|---|
70
+ | Path separators | `\` and `/` | `/` | `/` |
71
+ | Case sensitivity | Insensitive (typical) | Sensitive | Sensitive |
72
+ | Symlinks | Limited | Full | Full |
73
+ | Permissions | ACLs | POSIX | POSIX |
74
+
75
+ Path handling goes through `node:path`; no path is hard-coded in the engine.
76
+
77
+ ## Environment variables
78
+
79
+ | Variable | Required | Effect |
80
+ |---|---|---|
81
+ | `HANDOFFS_ROOT` | No | Sets the store root and takes precedence over every other rule |
82
+
83
+ ## Configuration
84
+
85
+ `handoff.config.json` is optional. It is discovered by walking up from the current directory, a
86
+ maximum of 10 levels, and validated against `handoff.config.schema.json`. The keys the engine acts
87
+ on are `storage.path`, `handoff_dir`, `project_name` and `linking.enabled`. A relative
88
+ `handoff_dir` resolves against the directory that holds the config.
89
+
90
+ Root resolution order, first match wins: `HANDOFFS_ROOT`, then a config-declared directory, then a
91
+ `handoffs/` directory found by the same upward walk, then the skill directory itself. Run
92
+ `node tools/handoff.mjs config` to see which rule applied.
93
+
94
+ ## Exit codes
95
+
96
+ | Code | Meaning |
97
+ |---|---|
98
+ | 0 | Success |
99
+ | 1 | Integrity failure (manifest mismatch, corrupted manifest) or an unexpected error |
100
+ | 2 | Usage error, missing source file, or an invalid config |
101
+ | 3 | Ambiguous session-id prefix |
102
+ | 4 | No parsable turns, no handoffs, or no match for the prefix |
103
+
104
+ ## CI
105
+
106
+ The suite is `node tools/handoff.test.mjs`. It needs no install step, so a CI job is checkout plus
107
+ Node 18/20/22. GitHub Actions workflows are under `.github/workflows/`.
108
+
109
+ | Platform | Level | Notes |
110
+ |---|---|---|
111
+ | GitHub Actions | SUPPORTED | Workflows included |
112
+ | GitLab CI, Azure DevOps, Jenkins | UNTESTED | Any runner with Node 18+ works |
113
+
114
+ ## Offline use
115
+
116
+ The engine makes no network requests. The installer is the one component that reaches the
117
+ network: run from a checkout or an unpacked archive it copies the files beside it, and run as the
118
+ published package it downloads that version's archive from this repository. Either way the
119
+ network is needed once, while installing; after that the installed skill is offline.
120
+
121
+ ## MCP
122
+
123
+ The engine is a CLI with text and JSON output, so an MCP server can wrap it. No MCP server ships
124
+ with this repository.
@@ -0,0 +1,134 @@
1
+ ---
2
+ title: Contributing
3
+ ---
4
+
5
+ # Contributing
6
+
7
+ ## Requirements
8
+
9
+ Node.js 18 or newer. The tools import `node:*` only, so there is no runtime dependency to
10
+ install and no lockfile to keep in sync.
11
+
12
+ ## Run the suite
13
+
14
+ ```
15
+ node tools/handoff.test.mjs
16
+ ```
17
+
18
+ `npm test` runs the same command. The suite uses `node:test` and `node:assert` only, and
19
+ drives the real CLIs through `spawnSync`. It is hermetic: every test builds into a temporary
20
+ `HANDOFFS_ROOT`, batch runtime state goes to a temporary `AGENT_HANDOFF_STATE_DIR`, and both
21
+ are removed afterwards. A bare run leaves no files behind.
22
+
23
+ | Area | Asserted |
24
+ |---|---|
25
+ | Engine: build, verify, list | Build from the shipped fixture, `verify` prints `PASS` and exits 0, a tampered manifest fails `verify` with exit 1, `list` shows the session under its project, a malformed source exits 4, a missing source exits 2, usage errors exit 2, a rebuild of identical input stays up to date without a new revision. |
26
+ | Capability registry | Required capabilities probe healthy, an unknown capability id exits 4, an unknown probe kind is never reported healthy, a missing registry file exits 2. |
27
+ | Permission engine | Evaluation precedes execution (records carry `enforced_before_execution`), a personal-data read is denied with exit 3 and never executed, `R2` on the workspace is `NEEDS_AUTH` with exit 4, a bounded spawn propagates the child's exit code, an unknown risk class is default-denied, an approved workspace outranks a broad personal-data root, a traversal target is denied. |
28
+ | Checkpoint and resume | A job killed mid-run resumes from disk alone with no step duplicated or lost, a missing checkpoint exits 4, a tampered checkpoint fails its seal with exit 5, a second job refuses an existing session checkpoint, a mismatched `--steps` exits 2. |
29
+
30
+ ## Drive a CLI by hand
31
+
32
+ ```
33
+ node tools/handoff.mjs build --source tests/fixtures/minimal-transcript.jsonl --project demo
34
+ node tools/handoff.mjs list
35
+ node tools/handoff.mjs show minimal-transcript
36
+ node tools/handoff.mjs verify minimal-transcript
37
+ node tools/handoff.mjs config
38
+
39
+ node tools/runtime-engine.mjs policy
40
+ node tools/runtime-engine.mjs evaluate --risk R0 --target tests/fixtures/minimal-transcript.jsonl
41
+
42
+ node tools/capability-registry.mjs check --registry capability-registry.json
43
+ ```
44
+
45
+ `build`, `rename` and `retitle` write into the resolved store. Point `HANDOFFS_ROOT` at a
46
+ temporary directory while experimenting so a test session is not added to your own store:
47
+
48
+ ```
49
+ HANDOFFS_ROOT=/tmp/ah-scratch node tools/handoff.mjs build --source tests/fixtures/minimal-transcript.jsonl
50
+ ```
51
+
52
+ On Windows use `set HANDOFFS_ROOT=...` in cmd, or `$env:HANDOFFS_ROOT="..."` in PowerShell.
53
+
54
+ | Tool | Exit codes |
55
+ |---|---|
56
+ | `tools/handoff.mjs` | 0 success, 1 verification failure or I/O error, 2 usage or invalid config, 3 ambiguous id prefix, 4 no matching handoff or no usable turns |
57
+ | `tools/runtime-engine.mjs` | 0 allowed, 2 usage or config error, 3 denied, 4 needs authorization or missing checkpoint, 5 corrupt checkpoint, 137 simulated kill, otherwise the child's exit code |
58
+ | `tools/capability-registry.mjs` | 0 all required capabilities healthy, 1 a required capability is unknown or unhealthy, 2 registry missing or invalid, 4 unknown capability id |
59
+
60
+ ## Add a harness adapter
61
+
62
+ The engine consumes one canonical input shape, so an adapter is anything that turns a
63
+ session store into that shape. One JSONL line:
64
+
65
+ ```json
66
+ {"seq":0,"ts":"<epoch or ISO>","harness":"<name>","source":"<origin path>","session":"<id>",
67
+ "thread":"<project/thread>","role":"user|assistant|system|tool","kind":"<reasoning|tool_use|text>",
68
+ "text":"<message body>"}
69
+ ```
70
+
71
+ Rules the parser applies. Text is taken from `text`, then `content`, then `parts[].text`. A
72
+ JSONL line that does not parse, or whose text is blank, is skipped. The class comes from
73
+ `kind` and `role`: `tool` in `kind`, or `role: tool`, is `TOOL`; `reason` or `think` in
74
+ `kind` is `THOUGHT`; `role: user` or `kind: human` is `USER`; `role: assistant` or `kind: ai`
75
+ is `AGENT`; anything else is `OTHER`. A `.txt` or `.md` source is read as role-marked text
76
+ instead (`user:`, `assistant:`, `tool:`, optionally prefixed with `#`).
77
+
78
+ Checklist:
79
+
80
+ 1. Produce the canonical JSONL from the store, without modifying the store.
81
+ 2. Build from it with `HANDOFFS_ROOT` set to a temporary directory and read the result.
82
+ 3. Add a row to [ADAPTERS.md](https://github.com/Alot1z/agent-handoff/blob/main/refs/ADAPTERS.md): store path, adapter route, status. Claim
83
+ `VERIFIED` only for a source you actually ran.
84
+ 4. If the parser changed, add a fixture under `tests/fixtures/` and a test to
85
+ `tools/handoff.test.mjs`.
86
+
87
+ ## Rules for a change
88
+
89
+ - Add no runtime dependency. The engine stays importable and runnable with a bare Node
90
+ installation.
91
+ - Keep the exit codes. They are contracts (see the table above and
92
+ [INTEGRATION.md](INTEGRATION.md)).
93
+ - Never commit session data or credentials: no `handoffs/`, `projects/`, `links/`,
94
+ `.agents-handoff/`, no `*.key`, `*.pem`, `*.token`, and no transcript copied from a real
95
+ session. Test input belongs in `tests/fixtures/`.
96
+ - A behaviour change comes with a test in `tools/handoff.test.mjs` and an update to the
97
+ document that owns the contract: [FORMAT.md](FORMAT.md) for the files and fields,
98
+ [PERMISSIONS.md](PERMISSIONS.md) for policy semantics, [INTEGRATION.md](INTEGRATION.md)
99
+ for the command surface.
100
+ - Run the suite before proposing the change and report its actual result, including
101
+ failures you could not fix.
102
+
103
+ ## Repository layout
104
+
105
+ | Path | Contents |
106
+ |---|---|
107
+ | `SKILL.md` | The skill definition a harness reads: when to use it, its commands and its rules. |
108
+ | `docs/` | These documents. |
109
+ | `install/` | `install.mjs`, the `npx` installer, and its own README. |
110
+ | `tools/handoff.mjs` | The handoff engine: build, list, show, verify, rename, retitle, config. |
111
+ | `tools/agents-handoff.mjs` | The runtime verbs layered over the engine. |
112
+ | `tools/runtime-engine.mjs` | Permission policy, bounded execution, checkpoints and resume. |
113
+ | `tools/capability-registry.mjs` | Probes declared capabilities and reports honest verdicts. |
114
+ | `tools/handoff.test.mjs` | The test suite. |
115
+ | `tools/lib/handoff-root.mjs` | The single definition of where handoffs are stored. |
116
+ | `schemas/` | `handoff.schema.json`, the portable payload contract. |
117
+ | `templates/` | The handoff render template. |
118
+ | `refs/` | Reference documents: harness adapters, protocol, roles, validator, brief checklist. |
119
+ | `tests/fixtures/` | Deterministic inputs for the suite. |
120
+ | `permission-policy.json` | The default permission policy. |
121
+ | `capability-registry.json` | The default capability declarations. |
122
+ | `handoff.config.schema.json`, `handoff.config.example.json` | Schema and example for `handoff.config.json`. |
123
+ | `.github/workflows/` | Continuous integration and release workflows. |
124
+
125
+ ## Documentation is part of the contract
126
+
127
+ A claim in these documents is expected to be checkable against the code. If you change a
128
+ file layout, a field, a flag or an exit code, update the document that states it in the same
129
+ change. Do not document an option that a tool does not implement; if something is reserved
130
+ but not implemented, say so, as `handoff.config.schema.json` does for `auto_capture`.
131
+
132
+ ## License
133
+
134
+ MIT. By contributing you agree your contribution is distributed under it.