nawabari 0.9.2 → 0.10.2

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 (75) hide show
  1. package/README.md +142 -803
  2. package/dist/cli-command-registry.js +46 -6
  3. package/dist/cli-command-registry.js.map +1 -1
  4. package/dist/cli.d.ts +5 -3
  5. package/dist/cli.js +157 -34
  6. package/dist/cli.js.map +1 -1
  7. package/dist/contract.js +11 -3
  8. package/dist/contract.js.map +1 -1
  9. package/dist/domain/compatibility-runtime-projection.d.ts +24 -0
  10. package/dist/domain/compatibility-runtime-projection.js +197 -0
  11. package/dist/domain/compatibility-runtime-projection.js.map +1 -0
  12. package/dist/domain/doctor.d.ts +2 -2
  13. package/dist/domain/doctor.js +72 -51
  14. package/dist/domain/doctor.js.map +1 -1
  15. package/dist/domain/errors.d.ts +1 -1
  16. package/dist/domain/errors.js +5 -1
  17. package/dist/domain/errors.js.map +1 -1
  18. package/dist/domain/fhs-development-runtime.d.ts +90 -0
  19. package/dist/domain/fhs-development-runtime.js +385 -0
  20. package/dist/domain/fhs-development-runtime.js.map +1 -0
  21. package/dist/domain/fhs-runtime.d.ts +36 -0
  22. package/dist/domain/fhs-runtime.js +776 -0
  23. package/dist/domain/fhs-runtime.js.map +1 -0
  24. package/dist/domain/landlock.d.ts +11 -4
  25. package/dist/domain/landlock.js +44 -21
  26. package/dist/domain/landlock.js.map +1 -1
  27. package/dist/domain/nix-runtime-closure.d.ts +70 -0
  28. package/dist/domain/nix-runtime-closure.js +449 -0
  29. package/dist/domain/nix-runtime-closure.js.map +1 -0
  30. package/dist/domain/pnpm-middleware-backend-materialization.d.ts +117 -0
  31. package/dist/domain/pnpm-middleware-backend-materialization.js +311 -0
  32. package/dist/domain/pnpm-middleware-backend-materialization.js.map +1 -0
  33. package/dist/domain/runtime-executable-projection.d.ts +33 -0
  34. package/dist/domain/runtime-executable-projection.js +204 -0
  35. package/dist/domain/runtime-executable-projection.js.map +1 -0
  36. package/dist/domain/runtime-file-identity.d.ts +37 -0
  37. package/dist/domain/runtime-file-identity.js +114 -0
  38. package/dist/domain/runtime-file-identity.js.map +1 -0
  39. package/dist/domain/runtime-profile.d.ts +66 -0
  40. package/dist/domain/runtime-profile.js +356 -0
  41. package/dist/domain/runtime-profile.js.map +1 -0
  42. package/dist/domain/runtime-projection.d.ts +101 -0
  43. package/dist/domain/runtime-projection.js +316 -0
  44. package/dist/domain/runtime-projection.js.map +1 -0
  45. package/dist/domain/runtime-provider-pnpm-middleware.d.ts +41 -0
  46. package/dist/domain/runtime-provider-pnpm-middleware.js +316 -0
  47. package/dist/domain/runtime-provider-pnpm-middleware.js.map +1 -0
  48. package/dist/domain/runtime-provider-tgrep.d.ts +60 -0
  49. package/dist/domain/runtime-provider-tgrep.js +537 -0
  50. package/dist/domain/runtime-provider-tgrep.js.map +1 -0
  51. package/dist/domain/runtime-resolution.d.ts +57 -0
  52. package/dist/domain/runtime-resolution.js +242 -0
  53. package/dist/domain/runtime-resolution.js.map +1 -0
  54. package/dist/domain/sandbox-launcher.d.ts +7 -0
  55. package/dist/domain/sandbox-launcher.js +380 -56
  56. package/dist/domain/sandbox-launcher.js.map +1 -1
  57. package/dist/domain/sandbox.d.ts +59 -4
  58. package/dist/domain/sandbox.js +100 -5
  59. package/dist/domain/sandbox.js.map +1 -1
  60. package/dist/domain/session-backend.js +20 -1
  61. package/dist/domain/session-backend.js.map +1 -1
  62. package/dist/domain/session.d.ts +3 -2
  63. package/dist/domain/session.js.map +1 -1
  64. package/dist/domain/tgrep-runtime-materialization.d.ts +61 -0
  65. package/dist/domain/tgrep-runtime-materialization.js +172 -0
  66. package/dist/domain/tgrep-runtime-materialization.js.map +1 -0
  67. package/dist/operation-authorization.d.ts +10 -0
  68. package/dist/operation-authorization.js +12 -1
  69. package/dist/operation-authorization.js.map +1 -1
  70. package/dist/presentation.js +6 -3
  71. package/dist/presentation.js.map +1 -1
  72. package/dist/session-registry.d.ts +11 -3
  73. package/dist/session-registry.js +61 -45
  74. package/dist/session-registry.js.map +1 -1
  75. package/package.json +3 -1
package/README.md CHANGED
@@ -1,884 +1,223 @@
1
1
  # Nawabari
2
2
 
3
- Nawabari is a standalone local Git/session ownership layer for parallel coding
4
- agents. It gives each active session one exclusively owned worktree and one
5
- mutable branch. It works without GitHub, `gh`, a network connection, Mottainai,
6
- or a particular agent runtime.
7
-
8
- Nawabari governs operations routed through Nawabari. By default it is an
9
- authorization and ownership boundary, not an operating-system or filesystem
10
- sandbox: a process that already has filesystem permissions can still edit
11
- another worktree directly. This default (legacy) mode is unchanged.
12
-
13
- The opt-in protected session mode defines a Linux-only OS/filesystem/process
14
- enforcement boundary underneath the existing session/worktree/resource
15
- authority (`src/domain/sandbox.ts`, contract
16
- `nawabari.sandbox-execution.v1`). It binds one existing Nawabari session
17
- resolved through the authoritative registry/guard path to a typed sandbox
18
- execution request; it does not create a second session identity. A
19
- machine-readable capability/doctor report distinguishes required Linux
20
- primitives (bubblewrap, user/mount/PID/IPC/UTS namespaces, the versioned
21
- seccomp baseline, and capability reduction) from optional defense-in-depth
22
- primitives (cgroups v2 and Landlock). The sandbox report exposes Landlock's
23
- observed ABI, support, and effective state (`available`, `enforced`,
24
- `reduced-defense`, `incompatible`, or `error`). When protected execution is requested and a required capability
25
- is unavailable or the platform is unsupported, resolution fails closed and
26
- never returns a request that claims the legacy unsandboxed path is
27
- protected. The lower-level contract remains responsible only for capability
28
- detection and the typed request/result shape; `resolveSandboxExecutionRequest()`
29
- returns a request without invoking bubblewrap. The managed `session run`
30
- launcher consumes that request and establishes the protected boundary. Network
31
- mode is honestly reported as `inherited` (shared with the host), not isolated.
32
-
33
- `git nawabari session run --session <id> -- <command> [args...]` is the
34
- managed protected execution entry point. It resolves the existing session
35
- through the normal guard authority and compiles a fixed bubblewrap argv; the
36
- command is passed after an argv terminator and is never interpreted by a
37
- shell. Resolution or launch failure never falls back to the legacy ambient
38
- filesystem view.
39
-
40
- The canonical profile starts from a private root, mounts only the owned
41
- worktree read-write, gives each session private `/tmp`, `/proc`, HOME and
42
- cache state, and exposes no sibling worktree or control-plane path. A small
43
- repository-owned `nawabari/sandbox/shared-home` subtree is the only HOME state
44
- shared between sessions. Selected host user-tool directories (`~/.local/bin`
45
- and pnpm's user bin when present) are read-only; credentials and the rest of
46
- the host HOME are not mounted. `/dev`, system certificates/configuration, and
47
- the detected runtime are explicit read-only/runtime inputs.
48
-
49
- When the host exposes a compatible Landlock ABI, the protected launcher applies
50
- a rule set derived from this same topology inside bubblewrap. An unavailable
51
- or incompatible optional ABI leaves bubblewrap active and reports
52
- `reduced-defense`; an adapter setup failure fails closed, and a profile that
53
- explicitly requires Landlock also fails closed when its adapter cannot be
54
- established. Setup diagnostics are bounded and no ambient/unsandboxed retry is
55
- attempted.
56
-
57
- On standalone Linux the profile uses existing `/usr`, `/bin`, `/lib*` and
58
- selected `/etc` paths only when present. On NixOS it additionally selects
59
- `/nix/store`, `/run/current-system`, `/run/wrappers`, and the per-user profile
60
- when present; broad FHS views (`/usr`, `/bin`, `/lib*`) are not selected when
61
- the NixOS closure roots are present. Missing required paths or namespace
62
- support produces a stable capability/topology error. The protected child uses
63
- `nawabari.seccomp.v1`, a compatibility-first deny-list whose policy denials
64
- return `EPERM` rather than hanging or terminating ordinary development
65
- subprocess trees. Ambient capabilities are empty (`--cap-drop ALL`). Network
66
- remains inherited by design.
67
-
68
- The canonical Mottainai NixOS Runtime fixture can run the opt-in compatibility
69
- conformance matrix as its unprivileged repository principal:
3
+ Nawabari is a local governance layer for parallel coding agents. It gives each agent session an owned Git worktree and branch, records which repository resources that session may use, and checks those boundaries before governed mutations.
70
4
 
71
- ```bash
72
- NAWABARI_NIXOS_RUNTIME_CONFORMANCE=1 \
73
- node --test --import tsx src/domain/nixos-runtime-compat.test.ts
74
- ```
5
+ It is for teams and tools that need several agents to work in one repository without silently sharing a worktree, overwriting one another's files, or guessing whether a session is safe to close. Nawabari is local-first: the session registry, Git observations, and authorization decisions do not require GitHub, `gh`, a network connection, or a particular agent runtime.
75
6
 
76
- This invokes the normal `session run` CLI route and production launcher. It
77
- checks explicit NixOS closure paths, private versus repository-shared HOME
78
- state, Git/checkpoint authority, representative toolchain subprocesses, and
79
- sequential/concurrent session isolation. It does not create a test-only
80
- sandbox or treat Mottainai state as Nawabari authority.
7
+ This README describes the current 0.10.x product model. It is an overview and navigation surface, not a copy of generated contracts or implementation history.
81
8
 
82
- ## Install
9
+ The product model is intentionally small:
83
10
 
84
- Nawabari requires Node.js >=24.
11
+ 1. Create a session. Nawabari provisions a dedicated worktree and branch.
12
+ 2. Claim the resources the session is allowed to use.
13
+ 3. Route governed work through claims, evidence, and mutation authorization.
14
+ 4. Inspect the result, then close the session only when integration is proven.
85
15
 
86
- ```bash
87
- npm install -g nawabari
88
- ```
16
+ Nawabari governs operations routed through Nawabari. A normal governed session is an ownership and authorization boundary, not an operating-system or filesystem sandbox. A process with ambient filesystem permissions can still edit another worktree directly.
17
+
18
+ ## Install
89
19
 
90
- The package installs `nawabari` for direct use and `git-nawabari` for Git's
91
- `git nawabari` external subcommand. Both names use the same entry point.
20
+ Node.js 24 or newer is required.
92
21
 
93
22
  ```bash
94
- git nawabari --help
23
+ npm install -g nawabari
95
24
  nawabari --version
25
+ git nawabari --help
96
26
  ```
97
27
 
98
- ## Standalone machine contract
28
+ The package installs both `nawabari` and `git-nawabari`; `git nawabari ...` works as Git's external subcommand.
29
+
30
+ ## First session
99
31
 
100
- The installed CLI/JSON surface is the integration boundary. An orchestrator
101
- must discover the contract before using the lifecycle:
32
+ Run from the repository's integration worktree. `src/example.ts` below is a placeholder path; substitute a real file that exists in your repository.
102
33
 
103
34
  ```bash
104
35
  nawabari capabilities --json
105
- nawabari --version --json
106
- ```
107
36
 
108
- Both discovery commands work without a Git repository. A compatible
109
- installation reports `contract_id: "nawabari.standalone-execution.v1"` and
110
- `schema_version: 1`. The capability response lists the exact commands,
111
- result-schema versions, identity fields, and stable `failure_codes`. The
112
- package version is release metadata; it is not a substitute for the
113
- machine-contract identifier.
114
-
115
- The `protected-execution` capability separately advertises the versioned
116
- `nawabari.sandbox-execution.v1` contract, its required and optional host
117
- capabilities, the canonical `session run` entry point and `session exec` alias,
118
- `network_mode: "inherited"`, and fail-closed behavior. Use
119
- `nawabari doctor --json` to inspect the current host without creating or
120
- selecting a session. Its `sandbox` report is produced by the same
121
- `sandboxDoctorReport` authority used when resolving protected execution and
122
- contains `platform_supported`, per-capability status, `ready`, and
123
- `missing_required`. An unavailable required capability never implies an
124
- ambient protected fallback.
125
-
126
- The `resource-claims` capability additionally exposes a machine-readable
127
- `claim_set_replacement` object (`commands`, `atomic: true`,
128
- `pairing: "adjacent-resource-mode"`, `idempotent_retry: true`,
129
- `unchanged_on_rejection: true`) describing the atomic multi-claim replacement
130
- surface documented above, so a caller can discover this contract instead of
131
- assuming it from the CLI help text.
132
-
133
- Resource-claim semantics are generation `nawabari.resource-claims.v2` with
134
- claim-record schema `2`. The standalone envelope deliberately remains
135
- `nawabari.standalone-execution.v1`: this is a meaning-compatible top-level
136
- identity, while callers select the child resource-claim generation before
137
- operating. A future meaning-changing claim authorization, conflict, transition,
138
- release, or required-mode change must publish a new resource-claim generation
139
- and identity; the package version alone is never a compatibility decision.
140
- The capability binds every lifecycle command and alias to its result schema,
141
- implementation-owned stable failure vocabulary, transition-matrix identity,
142
- CAS/force and rejected-non-mutation guarantees, and deterministic recovery
143
- action schema.
144
-
145
- The supported standalone sequence is:
37
+ created=$(git nawabari session create --branch feature/example --json)
38
+ session_id=$(printf '%s' "$created" | jq -r .session_id)
39
+ worktree=$(printf '%s' "$created" | jq -r .worktree)
146
40
 
147
- ```text
148
- session create -> session claim(s) -> authorize/checkpoint
149
- -> commit/push -> doctor (reconciliation) -> session close/gc
150
- -> explicit session discard only when the selected work is intentionally disposable
41
+ (cd "$worktree" && git nawabari session claim --session "$session_id" --resource src/example.ts --mode exclusive-write --json)
42
+ (cd "$worktree" && git nawabari guard --session "$session_id" --operation source-write --resource src/example.ts --json)
43
+ (cd "$worktree" && "$EDITOR" src/example.ts)
44
+ (cd "$worktree" && git nawabari checkpoint --session "$session_id" --json)
45
+ (cd "$worktree" && git nawabari commit --session "$session_id" --all-claimed --message "Update example" --json)
151
46
  ```
152
47
 
153
- The JSON envelope is one document on stdout. Success has `ok: true`, a
154
- `command`, and the command's versioned result fields. Failure has `ok: false`,
155
- the `command`, a stable `code`, a bounded human-readable `message`, and
156
- optional structured `details`; JSON mode writes no decorative stderr. Exit
157
- codes are `0` success, `2` usage, `3` rejected/unsafe operation, `4`
158
- unavailable capability, `5` failed doctor checks, and `70` unexpected internal
159
- failure. Consumers must use these fields and codes, never human presentation.
160
-
161
- The result schemas expose the following identities:
162
-
163
- | Surface | Versioned identities |
164
- | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
165
- | session lifecycle | `session_id`, `repository`, `worktree`, `branch`, `state` |
166
- | explicit discard | `previous_head`, `worktree_removed`, `branch_removed`, `released_claims`, `final_state`, final `session.state`/`terminal_operation` |
167
- | claims | `claim_id`, `session_id`, `resource`, `mode`, `claim_set_generation`, `previous_claim_set_generation` |
168
- | authorization | `operation`, `allowed`, `code`, `claim_ids` |
169
- | checkpoint evidence | `head`, `changed`, `staged`, `unstaged`, `untracked`, `in_claim`, `out_of_claim` |
170
- | repository evidence | `session_id`, `session_updated_at`, `base_revision`, `head`, `clean`, `paths.stats`, `evidence_hash` |
171
- | bounded diff | `from_revision`, `to_revision`, `paths`, `stats`, `diagnostics`, `patch`, `evidence_hash` |
172
- | commit/push | `commit_sha`, `remote`, `branch`, `target`, `relation` |
173
- | reconciliation/cleanup | `clean`, `issues`, `candidates`, `cleaned`, `blocked`, `recovery_hints` |
174
-
175
- Git subprocesses are bounded at 10 seconds and 64 KiB of output; checkpoint
176
- evidence is bounded to 4,096 paths. `GIT_SPAWN_FAILED`, `GIT_TIMEOUT`,
177
- `GIT_OUTPUT_LIMIT`, and `GIT_COMMAND_FAILED` remain distinct failure codes.
178
- The local lifecycle requires Git and the repository-local registry/lock only;
179
- it does not require Mottainai, GitHub, `gh`, network access, an LLM, or a
180
- coding-agent runtime.
181
-
182
- ## Package state/contract API
183
-
184
- The CLI/JSON surface above remains the primary integration boundary. For a
185
- Node caller that wants public lifecycle observation/snapshot,
186
- transition-decision data, or machine-contract discovery without spawning the
187
- CLI and parsing its output, the package additionally exports two explicit,
188
- stable entry points:
48
+ `session create` provisions the new worktree under `<repository-parent>/.nawabari/worktrees` by default (discoverable via `status --json` as `managed_worktree_root`). Nawabari creates that managed subdirectory on first default placement. New exact `--worktree` paths must be under the reported root; an absolute path directly under the repository parent remains accepted for compatibility with older callers and persisted sessions.
189
49
 
190
- ```js
191
- import { classifyNawabariState, getNawabariSessionStateSnapshot } from "nawabari/state";
192
- import { nawabariMachineContract } from "nawabari/contract";
193
- ```
50
+ `--all-claimed` is an explicit resource selector. It resolves safely observed Git-changed paths covered by qualifying claims; it does not bypass claim authorization. Use repeated `--resource <path>` when an explicit path list is preferable.
194
51
 
195
- `nawabari/state` exposes the public lifecycle vocabulary
196
- (`NawabariLifecycleState`, `NawabariCommand`), the public observation input
197
- (`NawabariObservation`), and the public projection produced from it
198
- (`NawabariStateSnapshot`, `NawabariTransitionDecision`). `classifyNawabariState`
199
- is a pure, transport-neutral projection function; `getNawabariSessionStateSnapshot`
200
- observes one real, already-provisioned session through the same
201
- `SessionRegistry` Git/filesystem/session-registry authority `session inspect`
202
- uses, without mutating anything. `nawabari/contract` exposes
203
- `nawabariMachineContract()`, a zero-argument wrapper over the same
204
- `capabilities --json` contract that defaults to the installed package's own
205
- version.
206
-
207
- These are the only supported package entry points beyond the CLI binaries.
208
- Raw XState machine/actor internals, actor refs, internal state-node ids, and
209
- private machine context are not exported — `package.json#exports` declares no
210
- other subpath, so a deep import such as `nawabari/dist/state/session/machine.js`
211
- is rejected. `classifyNawabariState` only ever projects a caller-supplied observation into
212
- a decision; it never mutates anything, and no observation or transition
213
- decision it returns grants mutation authority. Mutating a session
214
- (close/discard/claim/commit/push/...) still requires the existing CLI or the
215
- `SessionRegistry` authority directly.
216
-
217
- ## Read-only repository evidence
218
-
219
- The evidence family is session-addressed and has no task, Issue, semantic, or
220
- GitHub interpretation. It is the physical repository authority for one owned
221
- session:
52
+ After reviewing and integrating the session branch:
222
53
 
223
54
  ```bash
224
- git nawabari evidence snapshot --session "$NAWABARI_SESSION_ID" --json
225
- git nawabari diff --session "$NAWABARI_SESSION_ID" --path src/example.ts --json
226
- git nawabari diff --session "$NAWABARI_SESSION_ID" --path src/example.ts \
227
- --patch --max-bytes 32768 --max-hunks 32 --json
55
+ git nawabari session inspect --session "$session_id" --json
56
+ git nawabari session close --session "$session_id" --json
228
57
  ```
229
58
 
230
- `evidence snapshot` verifies the registry's repository/worktree/branch owner,
231
- then reuses checkpoint's exact NUL-safe Git observation for `changed`,
232
- `staged`, `unstaged`, and `untracked` paths. It also reports canonical per-path
233
- stats, `clean`, the current `head`, session state, and an `evidence_hash`.
234
- New sessions persist the exact creation/base revision as `base_revision`;
235
- legacy records that lack this field report `base_revision: null` and
236
- `base_revision_proven: false` rather than inferring it from a mutable ref.
237
-
238
- `session_updated_at` is the UTC timestamp of the last authoritative
239
- session-state mutation, not a general Git filesystem mtime. A successful
240
- Nawabari-managed `commit` is such a mutation, so the registry timestamp is
241
- persisted before the command returns and a subsequent snapshot reports it with
242
- the resulting `head`. Git changes made outside Nawabari do not advance this
243
- field; consumers must use the Git-observed fields for those changes.
244
-
245
- `diff` requires at least one explicit concrete path and never accepts a glob or
246
- an empty repository-wide selection. Stats are returned by default. Patch text
247
- requires `--patch` and is bounded to at most 64 paths, 64 KiB, and 128 hunks;
248
- the caller may request smaller limits. Unrepresentable Git observations fail
249
- with `GIT_STATE_AMBIGUOUS`; a requested path whose stat is not exposed by Git
250
- remains in the result with `available: false` and makes snapshot evidence
251
- `complete: false`, so no path silently disappears. Such paths include a
252
- bounded `diagnostics` entry; a target directly observed as untracked reports
253
- `reason: UNTRACKED_TARGET`, while other unavailable-stat causes remain
254
- `STAT_UNAVAILABLE`. These diagnostics are read-only and do not stage or add
255
- files.
256
-
257
- ## Session lifecycle
258
-
259
- Session IDs are generated automatically as UUIDv7 values. They are immutable
260
- machine identities; labels and branch names are separate display metadata.
59
+ Close is conservative. Unintegrated commits, dirty worktrees, ambiguous Git state, and ownership mismatches remain blocked. For a squash or rebase merge, pass an exact local `--integrated-revision <rev>` so Nawabari can independently re-verify the content.
261
60
 
262
- ```bash
263
- git nawabari session create --branch feature/example --worktree ../example-worktree --json
264
- git nawabari session id --json
265
- git nawabari session show --json
266
- git nawabari session show <session-id> --json
267
- git nawabari session list --json
268
- git nawabari status --json
269
- git nawabari guard --json
270
- git nawabari session close --json
271
- git nawabari session discard --session <session-id> --json
272
- git nawabari gc --dry-run --json
273
- git nawabari doctor --json
274
- ```
61
+ ## Authority model
275
62
 
276
- `status --json` reports the resolved `managed_worktree_root` used when
277
- `session create` omits `--worktree` and `--worktree-root`. `session create
278
- --help --json` describes all create options as optional and reports defaults
279
- for branch, worktree, worktree root, base (`HEAD`), and label.
280
-
281
- `--worktree-root` selects only the parent directory for a new session
282
- worktree; Nawabari still derives the final worktree basename from its own
283
- session-naming contract. It is mutually exclusive with `--worktree`, the
284
- exact-path override. Every session record's `worktree_root` field reports
285
- the resolved parent of that session's worktree.
286
-
287
- `session create` provisions a dedicated worktree and mutable branch atomically
288
- under the repository-scoped mutation lock. The default/integration worktree
289
- and its protected branch cannot be session resources. `session id` and the
290
- other current-session commands resolve ownership from the current worktree;
291
- callers do not need to repeat the session ID for current-owner operations.
292
-
293
- Session-scoped commands use one target grammar: the canonical `--session <id>`
294
- option is accepted everywhere, and `show`, `inspect`, `claim`, `claims`,
295
- `release`, `update`, `close`, and `discard` also accept one positional
296
- `<session-id>` immediately after the subcommand. Supplying both forms is
297
- rejected as ambiguous. `session discard` always requires one explicit target
298
- and never infers the current worktree owner.
299
-
300
- Close is conservative. Dirty worktrees, ambiguous ownership, mismatched Git
301
- state, and commits not proven reachable from the integration branch block
302
- destructive cleanup. A clean close releases only the owned worktree and
303
- branch, and repeating close is idempotent. `gc` detects stale or interrupted
304
- sessions; `--apply` uses the same close safety checks and reports blocked
305
- sessions instead of guessing. `gc --dry-run` performs the same non-mutating
306
- cleanup preflight, reports age/physical/lifecycle suspicion separately from
307
- destructive eligibility, and includes stable blocker codes and
308
- `recovery_hints` for every eligible candidate that is not safe. Cleanup revalidates the physical worktree,
309
- branch, and `HEAD` observations immediately before each destructive Git
310
- operation.
311
-
312
- `session discard` is the sole explicit destructive abandonment path. It
313
- revalidates repository identity, the selected session, exact worktree/branch
314
- ownership, and session/worktree `HEAD` immediately before each Git mutation.
315
- It may destroy the selected session's unintegrated commits and uncommitted
316
- worktree contents, removes only that session's worktree/branch, releases only
317
- its claims, and records `terminal_operation: "discard"` plus the pre-discard
318
- `HEAD`. It never acts as an implicit fallback for `close`, `gc`, `doctor`, or
319
- reconciliation; sibling sessions remain untouched. A partial failure leaves a
320
- retryable closing record and a repeated discard converges or returns an
321
- explicit terminal idempotent result.
322
-
323
- Routine `session list` and `status` output excludes `closed` history and is
324
- limited to 64 records. Use `--all` (or `--history`) for an explicit complete
325
- history view; closed records remain persisted and are never silently deleted
326
- by listing or cleanup.
327
-
328
- `gc` stale eligibility is separate from closed-history retention. Its default
329
- threshold is 24 hours (`86,400,000` ms), measured from persisted `updated_at`.
330
- Elapsed age is diagnostic suspicion only: it never authorizes destructive
331
- cleanup for a physically healthy active session. Records already in `stale` or
332
- `closing` state are eligible, as is an otherwise live record when Git reports
333
- its registered worktree as safely prunable and missing. Ambiguous physical
334
- state remains ineligible and fail-closed. `gc --dry-run` exposes suspicion and
335
- destructive eligibility/reason separately for each candidate; `gc --apply`
336
- uses only eligible candidates. Closed records are never stale cleanup
337
- candidates.
338
-
339
- `doctor` includes a non-destructive `reconciliation` check. It reports
340
- registry/Git ownership drift, including missing or prunable worktrees and
341
- unregistered physical worktrees, without repairing or deleting anything.
342
-
343
- ## Session resource claims
344
-
345
- Resource claims are versioned, session-scoped ownership records stored in the
346
- same repository registry and protected by the same mutation lock. They are
347
- caller declarations; Nawabari does not infer them from task text or source
348
- code. Claim JSON exposes `schema_version`, `claim_id`, `session_id`, the
349
- repository/worktree identities, canonical `resource`, `mode`, and timestamps.
350
- The claim schema version is `2` and supports `read`, `write`, and
351
- `exclusive-write`. Schema v1 records use different overlap semantics and are
352
- not interpreted implicitly. If an upgraded repository reports
353
- `UNSUPPORTED_CLAIM_SCHEMA_VERSION`, run the public migration command:
63
+ Each boundary has one job and one local authority. README summarizes the product contract; executable code and machine-readable projections remain authoritative for exact schemas, transitions, and failure vocabularies.
354
64
 
355
- ```bash
356
- git nawabari migrate --json
357
- ```
65
+ | Boundary | What it answers | Typical commands |
66
+ | ---------------------- | ------------------------------------------------------------------------------------ | ---------------------------------------------------------------------- |
67
+ | Session lifecycle | Which session owns a worktree/branch and whether it can safely progress or terminate | `session create`, `session inspect`, `session close` |
68
+ | Resource Claims | Which session may access a canonical repository resource and at what mode | `session claim`, `session claims`, `session update`, `session release` |
69
+ | Mutation authorization | Whether a concrete operation has sufficient claims and no conflicting owner | `guard`, `authorize`, `commit`, `push` |
70
+ | Repository evidence | What Git can observe about revisions, paths, changes, ancestry, and bounded diffs | `checkpoint`, `evidence snapshot`, `diff` |
71
+ | Protected execution | Whether a command runs inside the opt-in Linux process/filesystem boundary | `session run`, `session exec`, `session shell`, `doctor` |
358
72
 
359
- Migration validates the complete legacy registry under the registry lock and
360
- rewrites it with an atomic replace. It is idempotent and retry-safe; ambiguous
361
- or corrupt state is rejected with bounded diagnostics. Do not hand-edit or
362
- delete the registry. Embedding callers may use the same authority through
363
- `SessionRegistry.migrate()`.
73
+ Claims are not task labels and do not encode GitHub or agent semantics. `write` permits ordinary path changes; `exclusive-write` is required for finalizing operations such as commit and push. Conflicting or ambiguous claims fail closed.
364
74
 
365
- ```bash
366
- git nawabari session claim --session "$NAWABARI_SESSION_ID" \
367
- --resource 'src/**/*.ts' --mode read --json
368
- git nawabari session claims --session "$NAWABARI_SESSION_ID" --json
369
- git nawabari session update --session "$NAWABARI_SESSION_ID" \
370
- --resource 'src/**/*.ts' --mode write --force --json
371
- git nawabari session release --session "$NAWABARI_SESSION_ID" --all --force --json
372
- ```
75
+ The default governance path does not install hooks and does not prevent direct filesystem writes outside Nawabari. Its guarantee is that Nawabari-routed operations consult authoritative session, Git, and claim state before mutation.
373
76
 
374
- `session update` (and its `resource update` alias) atomically replaces a
375
- session's _complete_ claim set in one `updateClaims()` transaction, backed by
376
- the same repository lock as every other mutation. `--resource`/`--mode` are
377
- repeatable to submit a multi-resource desired set in a single call; each
378
- `--resource` must be immediately followed by its own `--mode`, so pairing is
379
- positional adjacency rather than flag order and repeated resources can never
380
- be associated with the wrong mode:
77
+ ## Session and resource lifecycle
381
78
 
382
- ```bash
383
- git nawabari session update --session "$NAWABARI_SESSION_ID" \
384
- --resource src/a.ts --mode exclusive-write \
385
- --resource src/b.ts --mode exclusive-write \
386
- --json
79
+ The normal path is:
80
+
81
+ ```text
82
+ session create
83
+ ↓
84
+ active session → claim resources → guard/checkpoint → commit or push
85
+ ↓ ↓
86
+ inspect readiness integrate the branch
87
+ ↓ ↓
88
+ session close ←────────────────────────────────────┘
387
89
  ```
388
90
 
389
- If any requested claim in the set is invalid or conflicts, the whole update
390
- is rejected and the session's prior claim set is left unchanged; no partial
391
- or empty intermediate claim state is ever observable. Submitting the same
392
- complete desired set again is idempotent. A successful replacement's JSON
393
- exposes the resulting `claims` together with machine-readable `added` and
394
- `released` claims.
91
+ If work is not integrated, `session inspect` reports bounded blockers and safe next actions. Discard is never an implicit fallback for close or garbage collection.
395
92
 
396
- The complete public claim lifecycle is:
93
+ The Session lifecycle is backed by the executable XState authority in 0.10.x and exposed through public state, contract, and manifest projections. Consumers use those projections rather than internal machine nodes or actor objects.
397
94
 
398
- ```text
399
- session claim/resource claim (additive acquire)
400
- -> session transition/resource transition (one exact-resource mode change)
401
- -> session mutate/resource mutate (atomic exact-resource deltas)
402
- -> session release/resource release (--resource, --claim-id, or explicit --all)
403
- -> session update/resource update (atomic complete-set replacement)
404
- ```
95
+ ### Claims
405
96
 
406
- All destructive mutations require exactly one `--if-generation` CAS or
407
- explicit `--force`. A stale CAS returns `STALE_CLAIM_SET` without changing
408
- claims or generation. Additive claim is not replacement; selected release
409
- preserves unrelated claims; `--all` is the unambiguous all-claims selector.
410
- An exact contradictory additive claim remains rejected with
411
- `CONTRADICTORY_CLAIM` and may carry the typed `transition-exact-resource`
412
- recovery action, whose generation is directly usable as the transition CAS.
413
-
414
- The modes have these normative meanings:
415
-
416
- - `read`: a non-mutating access declaration. It is not a consistency lease,
417
- so it may overlap an ordinary `write` claim.
418
- - `write`: ordinary source-modification authority. It may overlap `read`, but
419
- not another writer or any `exclusive-write` claim.
420
- - `exclusive-write`: stronger ownership-sensitive mutation authority. It
421
- excludes every overlapping claim, including `read`.
422
-
423
- Overlapping claims use this complete compatibility matrix; non-overlapping
424
- claims are compatible for every mode:
425
-
426
- | existing \/ requested | read | write | exclusive-write |
427
- | --------------------- | ---------- | ---------- | --------------- |
428
- | read | compatible | compatible | conflict |
429
- | write | compatible | conflict | conflict |
430
- | exclusive-write | conflict | conflict | conflict |
431
-
432
- Claims use canonical repository-relative POSIX paths. Literal path segments,
433
- `*`/`?` segment wildcards, and a complete `**` segment are supported. Empty,
434
- `.`/`..`, absolute, drive-relative, backslash, unsupported-glob, and
435
- symlink-escaping forms are rejected with stable machine-readable codes.
436
- Equivalent claim acquisition and release retries are idempotent. Closing or
437
- garbage-collecting a session releases its claims; no separate claim registry
438
- or claim lock exists. Claims describe ownership state only and do not provide
439
- OS-level filesystem observation or a filesystem sandbox.
440
-
441
- For an existing path, canonicalization follows the physical directory entry,
442
- so a case-insensitive filesystem cannot give `README.md` and `readme.md`
443
- independent claim identities. This is determined from the filesystem entry
444
- itself; paths that do not exist yet, and wildcard portions of a claim, retain
445
- their exact lexical case because their future physical identity is unknown.
446
- On a case-sensitive filesystem an alternate-case spelling is therefore a
447
- distinct (possibly future) path, and no global lowercasing is applied.
448
-
449
- An ordinary source change uses `write` and can proceed while another session
450
- holds a `read` declaration:
97
+ Claims are canonical repository-relative resource records attached to a session. Supported modes are `read`, `write`, and `exclusive-write`.
451
98
 
452
99
  ```bash
453
- git nawabari session claim --session "$NAWABARI_SESSION_ID" \
454
- --resource src/example.ts --mode write --json
455
- git nawabari authorize --session "$NAWABARI_SESSION_ID" \
456
- --operation source-write --resource src/example.ts --json
100
+ git nawabari session claim --session "$session_id" --resource src/example.ts --mode exclusive-write --json
101
+ git nawabari session claims --session "$session_id" --json
102
+ git nawabari session update --session "$session_id" --resource src/example.ts --mode exclusive-write --resource test/example.test.ts --mode write --if-generation 1 --json
103
+ git nawabari session release --session "$session_id" --all --force --json
457
104
  ```
458
105
 
459
- A stronger ownership-sensitive operation uses `exclusive-write` and therefore
460
- requires no overlapping claim:
106
+ Resource aliases are discoverable through help. Use `--help --json` rather than copying option metadata into an integration.
461
107
 
462
- ```bash
463
- git nawabari session claim --session "$NAWABARI_SESSION_ID" \
464
- --resource src/example.ts --mode exclusive-write --json
465
- git nawabari authorize --session "$NAWABARI_SESSION_ID" \
466
- --operation commit --resource src/example.ts --json
467
- ```
108
+ Claim modes are cumulative. The canonical operation requirements are derived from the operation authorization policy and are shown by `session claim --help`:
109
+
110
+ - `read`: no governed operation
111
+ - `write`: `source-write`, `stage`
112
+ - `exclusive-write`: `source-write`, `stage`, `commit`, `branch-mutation`, `push`, `cleanup`
468
113
 
469
- ## Ownership guard
114
+ The policy requires `exclusive-write` for operations that finalize or remove shared state. This mapping is documentation of the executable authority, not a second claim policy; integrations should discover the current values from help or the machine contract.
470
115
 
471
- `git nawabari guard` is a cheap, side-effect-free authorization decision for a
472
- Nawabari-governed mutation. It reads the current repository/worktree/branch and
473
- the same authoritative session registry used by provisioning and lifecycle
474
- operations. An optional `--session` asserts the caller's session identity.
116
+ ### Inspect, close, discard, and garbage collection
117
+
118
+ `session inspect` is read-only and uses the same close/cleanup evidence as `session close`.
475
119
 
476
120
  ```bash
477
- decision=$(git nawabari guard --session "$NAWABARI_SESSION_ID" --json) || {
478
- printf '%s\n' "$decision" >&2
479
- exit 1
480
- }
121
+ git nawabari session inspect --session "$session_id" --json
122
+ git nawabari session discard --session "$session_id" --preview --json
481
123
  ```
482
124
 
483
- An allowed decision has `allowed: true` and `code: "ALLOWED"`. A denied
484
- decision has `allowed: false`, a stable code such as
485
- `WORKTREE_OWNED_BY_OTHER_SESSION`, `PROTECTED_WORKTREE`, `DETACHED_HEAD`,
486
- `WORKTREE_MISMATCH`, or `OWNERSHIP_MISMATCH`, and a non-zero exit status.
487
- Detached, corrupt, missing, or conflicting state fails closed. The guard does
488
- not install hooks and does not prevent direct filesystem writes outside
489
- Nawabari.
490
-
491
- ## Actionable lifecycle rejections
492
-
493
- Stable codes are necessary but not sufficient for a caller to pick a safe
494
- next step without a second, separate inspection. For the codes below,
495
- Nawabari's `details` carry bounded, deterministic evidence and a
496
- `safeActions`/`safe_actions` list of stable, kebab-case next-action
497
- identifiers, in addition to the human-readable `message`. JSON and human
498
- output always render the identical underlying result; only the formatting
499
- differs.
500
-
501
- Lifecycle diagnostics additionally expose `next_action` (and the bounded
502
- `next_actions` list) as typed, non-mutating caller actions. The action schema
503
- is versioned independently and currently includes `retain-session`,
504
- `supply-exact-integrated-revision`,
505
- `retry-close-with-bounded-integration-fetch`, `discard-session`, and
506
- `reconcile-physical-state`. A discard action always carries explicit intent;
507
- ambiguous or terminal states never advertise destructive actions. These
508
- fields are additive to `session-diagnostic.v1`, so existing consumers may
509
- continue using `safe_actions`.
510
-
511
- - **`RESOURCE_CLAIM_CONFLICT`** (`session claim`/`session update`,
512
- `authorize`, `guard --operation`) reports the blocking claim
513
- (`ownerClaimId`, `ownerResource`, `ownerMode`) and the blocking session's
514
- canonical identity (`ownerSessionId`, `ownerWorktree`, `ownerBranch`, and
515
- `ownerLabel` when the session has one) in the same result, so a caller
516
- never needs a second `session list` scan to find the owner.
517
- - **`PROTECTED_WORKTREE`**/**`PROTECTED_BRANCH`** raised by a live command
518
- (not by cleanup) add `phase: "execution"` and distinguish the current
519
- (protected) execution context from the referenced `--session` target:
520
- `requestedSessionId`, `targetWorktree`, `targetBranch`, and `targetState`
521
- when that session exists. `safeActions` names the deterministic fix
522
- (`run-from-managed-session-worktree`, `select-target-session-explicitly`)
523
- instead of the unrelated cleanup-time hint.
524
- - **`INVALID_SESSION_ID`** stays machine-ID based: an invalid `--session`
525
- value is never silently reinterpreted as a label. When it exactly and
526
- unambiguously matches one active session's label, the result adds
527
- `session_id_hint` (the canonical session ID) and
528
- `session_label_match: "unique"` as a non-authoritative hint. An ambiguous
529
- or absent label match never guesses: `session_label_match` reports
530
- `"ambiguous"` (with `session_label_match_count`) or `"none"` instead.
531
- - **`RECOVERABLE_COMMITS`** raised by `session close` carries the same
532
- `close_readiness`/`result_state` classification `session inspect` reports
533
- for the identical state — `external_evidence_required` when ancestry alone
534
- could not prove the branch safe and a `--integrated-revision` proof might
535
- resolve it (e.g. after a squash/rebase merge), versus `blocked` when
536
- supplied evidence failed to prove equivalence, versus `ambiguous` when Git
537
- observation itself was inconclusive. For supplied evidence, bounded
538
- `proofFailure` details distinguish an unauthoritative revision from a
539
- tree-equivalence failure. The bounded evidence also includes
540
- `currentSessionHead`, `suppliedIntegratedRevision`, `resolvedIntegrationSha`,
541
- `lineageProof`/`authorityProof`, and `contentProof`. `safe_actions` includes
542
- `discard-session` only as an explicit user choice; it does not authorize an
543
- implicit cleanup fallback. Both surfaces reuse one authority, so a raw close
544
- rejection and `session inspect` never drift apart.
545
-
546
- None of the above weakens fail-closed behavior, changes an error code's
547
- meaning, or performs any mutation while producing the rejection.
548
-
549
- ## Orchestrator integration
550
-
551
- An external orchestrator can create a session, capture the returned
552
- `session_id`, launch its worker with the returned `worktree` as `cwd`, and
553
- check the guard before each Nawabari-governed mutation:
125
+ `session discard` requires an explicit session ID and may destroy unintegrated commits and uncommitted work in that session's worktree. Preview reports bounded destructive scope without mutation. Actual discard revalidates repository, worktree, branch, `HEAD`, and registry ownership before destructive steps.
554
126
 
555
- ```bash
556
- created=$(git nawabari session create --branch feature/task --json)
557
- session_id=$(printf '%s' "$created" | jq -r .session_id)
558
- worktree=$(printf '%s' "$created" | jq -r .worktree)
127
+ `gc --dry-run` reports stale candidates and blockers. `gc --apply` only cleans candidates passing the same safety checks; elapsed age alone is not destructive authority. `doctor` reports prerequisite and reconciliation state without silently repairing ownership.
559
128
 
560
- (cd "$worktree" && git nawabari guard --session "$session_id" --json)
561
- # run the worker in "$worktree"
562
- git nawabari session close --session "$session_id" --json
563
- ```
129
+ ## Governed Git work
564
130
 
565
- For a worker that must receive the protected filesystem/process boundary, use
566
- the managed launcher instead of starting the worker directly:
131
+ `guard` and `authorize` are separate read-only boundaries:
567
132
 
568
- ```bash
569
- git nawabari session run --session "$session_id" -- node worker.js
570
- ```
133
+ - `guard` without an operation verifies the current physical worktree, branch, and session ownership/context. It does not evaluate resource claims.
134
+ - `authorize` evaluates a named operation against concrete resources, using the canonical operation policy and active claims. It does not grant or persist claims.
135
+ - `guard --operation <name> --resource <path>` remains a compatibility convenience for the combined claim-aware check. Use `authorize` when the operation decision itself is the intended boundary.
571
136
 
572
- The launcher owns only the child sandbox topology and process attachment. The
573
- session registry, worktree/branch ownership, resource claims and lifecycle
574
- remain the authoritative Nawabari domain state.
575
-
576
- The orchestrator owns scheduling, prompts, and worker lifetime; Nawabari owns
577
- only local session identity, worktree/branch ownership, and safe cleanup. No
578
- Mottainai, GitHub, `gh`, network, or agent-runtime dependency is required.
579
-
580
- Mottainai is one optional caller of this contract, not a runtime dependency.
581
- It may retain task semantics, scheduling, validation policy, Issue/PR
582
- governance, and worker lifetime. It must pass concrete local declarations to
583
- Nawabari and retain the returned JSON identities. Nawabari does not import or
584
- execute Mottainai/GitHub workflow code, infer claims from task text, or create
585
- a second registry/database.
586
-
587
- ## Claim-aware operation authorization
588
-
589
- `authorize` is the single decision surface for a governed local operation. Its
590
- versioned vocabulary and required claim access are:
591
-
592
- | operation | required access |
593
- | ----------------- | ----------------- |
594
- | `source-write` | `write` |
595
- | `stage` | `write` |
596
- | `commit` | `exclusive-write` |
597
- | `branch-mutation` | `exclusive-write` |
598
- | `push` | `exclusive-write` |
599
- | `cleanup` | `exclusive-write` |
600
-
601
- The request contains a session identity, an operation, and concrete
602
- repository-relative resources. Nawabari independently verifies the current
603
- repository, owned worktree, branch, active session, and persisted claims;
604
- caller-supplied labels do not weaken that decision. The JSON result is the
605
- automation contract and reports stable allow/deny codes such as
606
- `MISSING_RESOURCE_CLAIM`, `INSUFFICIENT_CLAIM_MODE`, `RESOURCE_CLAIM_CONFLICT`,
607
- `INVALID_RESOURCE`, and
608
- the existing ownership/physical-observation codes.
609
-
610
- `INSUFFICIENT_CLAIM_MODE` means a matching claim exists but its granted mode
611
- is weaker than the operation requires. Its bounded details identify the
612
- resource, required access, and matching granted mode names;
613
- `MISSING_RESOURCE_CLAIM` remains reserved for an absent matching claim.
614
-
615
- **`authorize` returns an authorization decision only; it does NOT execute the
616
- operation itself.** Governed commit and push execution use this same decision
617
- path before invoking bounded Git subprocesses.
137
+ The operation vocabulary is discoverable from the CLI and includes `source-write`, `stage`, `commit`, `branch-mutation`, `push`, and `cleanup`.
138
+
139
+ `checkpoint` and `evidence snapshot` describe Git-observable facts only. They do not infer task meaning, Issue ownership, or review status. `diff` requires explicit concrete paths and bounds optional patch output.
618
140
 
619
141
  ```bash
620
- git nawabari authorize --session "$NAWABARI_SESSION_ID" \
621
- --operation source-write --resource src/example.ts --json
142
+ git nawabari commit --session "$session_id" --message "Update example" --resource src/example.ts --json
143
+ git nawabari commit --session "$session_id" --message "Update example" --all-claimed --json
144
+ git nawabari push --session "$session_id" --remote origin --branch feature/example --all-claimed --json
622
145
  ```
623
146
 
624
- Governed commit accepts only a caller-decided final message and explicit
625
- repository-relative resources. Every resource must be covered by an active
626
- `exclusive-write` claim; all Git-visible changed/staged paths must be in the
627
- explicit list. JSON includes the resulting `commit_sha`.
628
-
629
- Commit evidence distinguishes three sets: the caller's **declared/authorized**
630
- resources (the explicit, claim-covered list a caller passed in), the
631
- **staged** set Git reports as staged immediately before the commit runs, and
632
- the **actual committed** set — read back from the resulting commit itself via
633
- a bounded, NUL-safe `git diff-tree` observation, not inferred from staging
634
- intent. The `resources` field in a successful `commit --json` result is
635
- always the actual committed set, proven equal to or a subset of the
636
- authorized resources; if Git staging/index drift between staging and the
637
- commit (a hook, a concurrent process) causes the actual commit to contain a
638
- path outside the authorized set, the result is not reported as an ordinary
639
- successful commit — it fails with `COMMIT_RESULT_DIVERGED`, which retains the
640
- resulting `commitSha` (the Git commit already happened) alongside the
641
- authorized, actual, and divergent path sets for recovery/reconciliation.
642
-
643
- If Git reports a bounded transport failure (timeout, output limit, or spawn
644
- failure) after the commit invocation, Nawabari re-reads the local `HEAD` and
645
- that commit's bounded changed-path set before classifying the outcome. A
646
- failure whose `HEAD` is unchanged is reported with `outcome: "proven-absent"`
647
- and `retrySafe: true`; a matching new commit is returned as a successful
648
- result with `reconciliation.outcome: "proven-committed"` and its resulting
649
- SHA; if either observation is unavailable or does not match the authorized
650
- paths, the failure carries `outcome: "unresolved"` and `retrySafe: false`.
651
- Unresolved outcomes never authorize a blind retry.
147
+ ## Optional protected execution
148
+
149
+ Protected execution is an opt-in Linux-only mode beneath the existing Nawabari session and claim authority. It does not create a second session identity and does not turn ordinary `session create` work into a sandbox.
652
150
 
653
151
  ```bash
654
- git nawabari commit --session "$NAWABARI_SESSION_ID" \
655
- --message 'record the local change' --resource src/example.ts --json
152
+ git nawabari session run --session "$session_id" --runtime-policy strict -- node worker.js
153
+ git nawabari session exec --session "$session_id" -- npm test
154
+ git nawabari session shell --session "$session_id" --runtime-policy compatibility
656
155
  ```
657
156
 
658
- An optional `--message-pattern <regex>` validates the final message against a
659
- caller-declared rule before Git is invoked; Nawabari does not own or infer
660
- commit-message conventions (such as Conventional Commits) itself, so this
661
- check runs only when a caller explicitly supplies a pattern, and a mismatch
662
- fails with `INVALID_COMMIT_MESSAGE` before anything is staged. The pattern is
663
- bounded to 512 characters and is evaluated before the repository lock is
664
- acquired, so a pathological caller-supplied pattern cannot stall other
665
- sessions' governed operations. A repository's own `commit-msg` Git hook (if
666
- any) still runs normally, since governed commit invokes real `git commit`.
157
+ The `--` terminator is mandatory. The command is passed as argv and is not interpreted by a shell. The canonical profile gives the child a private root, `/tmp`, `/proc`, HOME, and cache state, mounts only the owned worktree read-write, and does not expose sibling worktrees or Nawabari control paths. Network mode is explicitly `inherited`, not isolated. Required Linux capabilities fail closed when unavailable; optional Landlock and cgroups v2 provide defense in depth when available.
158
+
159
+ Protected execution defaults to the strict `development` runtime profile. Only declared Node, Git, and pnpm material is projected through `/nawabari/bin`; `/usr`, `/bin`, `/nix/store`, the host home, and local user-tool directories are not implicitly visible. Compatibility is available only through the explicit `--runtime-policy compatibility` option.
667
160
 
668
161
  ```bash
669
- git nawabari commit --session "$NAWABARI_SESSION_ID" \
670
- --message 'feat: record the local change' --resource src/example.ts \
671
- --message-pattern '^(feat|fix|docs|refactor|test|chore): .+$' --json
162
+ git nawabari doctor --json
672
163
  ```
673
164
 
674
- Governed push requires explicit claim-covered resources and an explicit
675
- `--remote`/`--branch` target. Existing upstream and local/remote relation are
676
- inspected before mutation. A missing upstream requires `--create-upstream`;
677
- behind or diverged history requires explicit `--force`. Force pushes use an
678
- exact `--force-with-lease` bound to the observed remote branch SHA. Ordinary
679
- pushes use the same exact-generation lease, while force authorization remains
680
- separate. A new remote branch uses an empty lease, requiring the target ref to
681
- remain absent. Nawabari fetches only the explicit remote branch into a
682
- disposable ref when local ancestry is missing; it does not update tracking
683
- refs or fetch unrelated branches/tags.
684
- The JSON result includes the immutable `source_sha`, explicit `target_ref`,
685
- observed `observed_remote_sha`, and relation. If a bounded transport failure
686
- may have happened after the remote mutation, Nawabari re-observes only that
687
- exact remote ref. It reports `reconciliation.outcome` as `proven-pushed`,
688
- `proven-absent`, or `unresolved`, with the exact precondition and post-failure
689
- generation evidence. Only a proven-absent outcome is retry-safe; unresolved
690
- outcomes never authorize a blind retry.
165
+ See [Standalone Linux compatibility](docs/standalone-linux-compatibility.md) for platform-specific details.
166
+
167
+ ## CLI and machine-readable discovery
168
+
169
+ The installed CLI is the primary integration surface. Discover its contract rather than hard-coding presentation output:
691
170
 
692
171
  ```bash
693
- git nawabari push --session "$NAWABARI_SESSION_ID" \
694
- --remote origin --branch feature/example --resource src/example.ts --json
172
+ nawabari capabilities --json
173
+ nawabari --version --json
174
+ nawabari session create --help --json
175
+ nawabari commit --help --json
695
176
  ```
696
177
 
697
- `checkpoint --json` captures bounded Git-observable `changed`, `staged`,
698
- `unstaged`, and `untracked` path sets, canonicalizes them through the same
699
- resource model, and reports `in_claim` and `out_of_claim` paths. Checkpoint
700
- evidence is limited to the state Git exposes at that instant. Direct
701
- filesystem activity that is reverted, ignored, or otherwise not observable in
702
- the Git checkpoint is outside Nawabari's guarantee; this feature is not an
703
- OS-level filesystem monitor.
704
-
705
- Checkpoint canonicalization fails closed: a Git-reported path that cannot be
706
- represented as a canonical repository resource (traversal, symlink escape, or
707
- syntax reserved for the claim/glob model) never disappears from evidence.
708
- Checkpoint fails the whole observation with `GIT_STATE_AMBIGUOUS` instead of
709
- silently omitting the path, so a caller can never mistake an unrepresentable
710
- observation for a clean one. This mirrors the strictness governed mutation
711
- already applies to the same Git-observed paths, so checkpoint evidence is
712
- never weaker than mutation authorization.
713
-
714
- ## Physical execution context
715
-
716
- Nawabari treats Git and the canonical filesystem as the authority for every
717
- governed session context. It independently observes the repository common
718
- directory, worktree path, current branch, and current `HEAD`, then compares
719
- those observations with the session registry. Caller-supplied paths and branch
720
- labels are expectations only; they are never used to replace an observation
721
- Git can make.
722
-
723
- The shared verifier fails closed with stable registry reasons for detached
724
- `HEAD`, missing or prunable worktrees, repository/worktree/branch mismatches,
725
- stale or conflicting registry ownership, ambiguous Git state, and unavailable
726
- physical observations. Git process failures remain distinct and bounded:
727
- spawn failure, timeout, output-limit, and non-zero/unexpected exit.
728
-
729
- Provisioning canonicalizes the managed root and every existing path segment
730
- before invoking Git. Traversal, symlink/intermediate-segment escapes, existing
731
- worktree paths, and existing local branches are rejected deterministically;
732
- the repository lock serializes Nawabari provisioning and Git's own ref checks
733
- remain the final collision authority.
734
-
735
- An explicit `--base` ref that is empty, malformed, or does not resolve to a
736
- commit fails with `INVALID_BASE_REF`. The bounded JSON details retain the
737
- rejected ref, identify `HEAD` as the default recovery base, and include the
738
- retry hint to omit `--base`; Nawabari does not enumerate or fuzzy-search refs.
739
-
740
- ## Repository state and concurrency
741
-
742
- The authoritative registry is stored in the repository-common Git directory at
743
- `.git/nawabari/session-registry.json`; linked worktrees therefore share one
744
- registry. It records the schema version, repository identity, immutable session
745
- ID, canonical worktree and branch identities, lifecycle state, and timestamps.
746
-
747
- Ownership-changing writes use an exclusive repository-local lock and a synced
748
- temporary file followed by atomic replacement. Concurrent creation cannot
749
- silently duplicate an active worktree or branch. Lock recovery is conservative
750
- and platform-qualified: stale-lock reclamation is supported only on Linux,
751
- where the lock records a random token, PID, host, and the exact process-start
752
- token from `/proc/<pid>/stat`. An owner is reclaimed only when the same host
753
- proves that exact process identity is dead; elapsed age and PID liveness alone
754
- are never reclaim authority. On non-Linux platforms, Node can run ordinary
755
- Nawabari operations but does not provide a safe process-generation identity,
756
- so stale local locks remain `LOCK_STALE` and require deliberate operator
757
- remediation. Invalid, remote, or otherwise unverifiable lock metadata is never
758
- stolen and fails closed. The same limitation is machine-readable under
759
- `capabilities --json` at the `session-lifecycle.registry_lock_recovery`
760
- contract.
761
-
762
- ### Conformance and extraction boundary
763
-
764
- The packed-package suite exercises the complete standalone sequence, including
765
- cross-process claim conflicts, governed commit/push, reconciliation, retryable
766
- cleanup, and prunable worktree recovery. Native tests additionally cover
767
- process interruption/atomic-write recovery, partial staging or commit failure,
768
- stale physical state, cleanup races, bounded subprocess failures, and
769
- idempotency (`src/registry/store.test.ts`, `src/git-mutation.test.ts`,
770
- `src/cleanup-authority.test.ts`, `src/session-lifecycle.test.ts`, and
771
- `scripts/smoke-test.mjs`).
772
-
773
- The relevant Mottainai #28 execution cases are mapped as follows:
774
-
775
- - repository/worktree identity, provisioning path safety, branch collision,
776
- symlink escape, local staging/commit/push safety, cleanup revalidation, and
777
- reconciliation are Nawabari-native authority and tests;
778
- - task semantics, prompts, validation evidence policy, Conventional Commit and
779
- PR/Issue governance, GitHub operations, and agent hooks remain optional
780
- orchestrator-only semantics and must not move into Nawabari.
781
-
782
- Run `pnpm run test:package` to validate the exact packed tarball and its
783
- installed CLI, or `pnpm run verify` for the complete local conformance gate.
784
-
785
- ## Exact packed standalone protected-execution evidence
786
-
787
- `pnpm run test:package:protected` is the package/evidence gate for the
788
- standalone protected product. It creates one exact `pnpm pack` archive, records its
789
- package/version, filename, byte size, SHA-256, source revision, and host
790
- identity in `test-artifacts/packed-standalone-protected-execution.json`,
791
- validates the archive contents, and installs that archive into a fresh
792
- temporary consumer with `npm install --offline`. The default evidence report
793
- is ignored by Git; pass `--evidence-output <path>` to retain it elsewhere and
794
- `--keep-tarball` to retain the exact archive for inspection.
795
-
796
- The smoke test invokes only the installed `nawabari` bin. Its package allowlist
797
- contains `dist` runtime artifacts, `README.md`, `LICENSE`, and `package.json`;
798
- source, test, and script modules are rejected, and the installed manifest must
799
- not declare runtime dependencies. The lifecycle proof goes through the
800
- installed public CLI for `capabilities --json`, `doctor --json`, session
801
- creation/resolution, `session run`, resource claims, checkpoint, commit,
802
- local-bare-remote push, and close. The gate requires the protected doctor
803
- report to be ready; unavailable protected execution is a failure, not a skip
804
- or an ambient fallback. The fixture uses only a temporary local repository and
805
- local bare remote and has no Mottainai, GitHub, `gh`, LLM, or network
806
- dependency.
807
-
808
- The ordinary `pnpm run test:package` command runs the package/install smoke
809
- without requiring a real protected host, so it retains the existing
810
- compatibility CI job; it still verifies fail-closed rejection when protection
811
- is unavailable. The `pnpm run test:package:protected` command is the #149
812
- evidence command and must be run on a supported Linux host. It requires a
813
- ready protected profile and fails when that prerequisite is unavailable.
814
-
815
- The evidence supports these boundaries only:
816
-
817
- - Process: on Linux with the required capabilities, the canonical launcher
818
- establishes bubblewrap user, mount, PID, IPC, and UTS namespaces, applies
819
- the versioned seccomp profile, and drops ambient capabilities. This is not a
820
- VM or a claim about every host process.
821
- - Filesystem: the protected child receives the authoritative session worktree
822
- read-write, private session HOME/cache/`/tmp`/`/proc`, repository-owned
823
- shared HOME state, and the fixed read-only runtime/tool inputs selected by
824
- the profile. Sibling worktrees and unselected host HOME paths are not part
825
- of the selected topology.
826
- - HOME/cache: the child sees `/home/nawabari`; private state is per session,
827
- while only the repository's selected shared-home subtree is shared. Selected
828
- host tool directories are read-only inputs, not the host HOME.
829
- - Network: `network_mode` is `inherited`; this evidence does not prove egress
830
- isolation.
831
- - Linux prerequisites: use `nawabari doctor --json` on a supported Linux host.
832
- The package's supported Node.js engine, Git, and required bubblewrap,
833
- namespace, seccomp, and capability support must be available and ready;
834
- cgroups v2 and Landlock remain profile-reported optional defenses.
835
- - Failure behavior: protected resolution uses `enforce: true`; missing
836
- required capability, unsupported topology, or launch failure returns a
837
- bounded failure and never retries through ambient execution.
838
-
839
- ## Packed Mottainai preselected-UID handoff evidence
840
-
841
- `pnpm run test:package:mottainai` is the required-CI external-handoff gate for
842
- Issue #150. `scripts/run-mottainai-uid-handoff-gate.mjs` runs the #149
843
- protected package gate exactly once, retains that exact tarball and its
844
- `nawabari.packed-standalone-protected-execution.v1` evidence, and passes only
845
- those exact paths into `scripts/run-mottainai-uid-handoff.mjs`, which installs
846
- that same tarball into a disposable consumer. `run-mottainai-uid-handoff.mjs`
847
- itself has no self-pack path: it always requires an already-produced
848
- `--tarball`/`--artifact-evidence` pair, so #150 can never hide a stale or
849
- mismatched artifact behind a second pack. The fixture invokes the installed
850
- `session run` contract under two distinct preselected unprivileged UIDs
851
- (23001 and 23002 by default) and records bounded
852
- artifact/fixture/UID/session/worktree/resource/Git evidence in
853
- `test-artifacts/mottainai-packed-uid-handoff.json`. This gate runs in required
854
- CI on a Linux host with bubblewrap (`mottainai-uid-handoff` job).
855
-
856
- The checked-in `scripts/test-fixtures/mottainai-preselected-uid-runner.sh` is a
857
- UID-only execution adapter. A real Mottainai Runtime may supply another
858
- executable with the same `--uid`, `--root`, `--cwd`, `--` argv contract:
178
+ `capabilities --json` works without a Git repository. The top-level contract is `nawabari.standalone-execution.v1`, schema version `1`; Resource Claim meaning is separately versioned as `nawabari.resource-claims.v2`. Package version alone is not a compatibility decision.
859
179
 
860
- ```bash
861
- node scripts/run-mottainai-uid-handoff.mjs \
862
- --tarball nawabari-0.7.1.tgz \
863
- --artifact-evidence test-artifacts/packed-standalone-protected-execution.json \
864
- --fixture-runner /path/to/mottainai-uid-runner \
865
- --uids 23001,23002 \
866
- --output test-artifacts/mottainai-packed-uid-handoff.json
180
+ JSON mode emits one bounded document on stdout. Consumers should use machine-readable fields and stable codes rather than parse human-oriented text.
181
+
182
+ The command surface includes Session lifecycle, Resource Claims, authorization/evidence, governed Git commit/push, reconciliation/discovery, and protected execution. Use `--help --json` and `capabilities --json` for the authoritative inventory.
183
+
184
+ ## Stable package exports
185
+
186
+ Node consumers can use public projections without spawning the CLI:
187
+
188
+ ```js
189
+ import {
190
+ availableNawabariCommands,
191
+ classifyNawabariState,
192
+ getNawabariSessionStateSnapshot,
193
+ nawabariTransitionDecision,
194
+ } from "nawabari/state";
195
+
196
+ import { nawabariMachineContract } from "nawabari/contract";
197
+
198
+ import { generateNawabariProductStateManifest, serializeNawabariProductStateManifest } from "nawabari/manifest";
867
199
  ```
868
200
 
869
- The runner supplies only the caller's OS UID/GID view. Nawabari remains the
870
- sole authority for session, worktree, resource claims, protected execution,
871
- checkpoint, commit, push, local Git, and close; the fixture creates no
872
- repository-principal registry and passes no Mottainai task, policy, or
873
- credential semantics. Identical display names, branch names, and resource
874
- paths are used under different canonical repository paths, and the protected
875
- child's session/cwd marker is checked to prevent principal confusion.
201
+ `nawabari/state` provides transport-neutral lifecycle projection and read-only observation of an existing session. `nawabari/contract` provides the installed machine-contract projection. `nawabari/manifest` provides the deterministic Product State Manifest projection.
202
+
203
+ These exports do not grant mutation authority and do not expose raw XState machines, actor references, internal state-node IDs, private context, or `dist/` deep imports. Mutations still go through authoritative runtime paths. See [XState state architecture](docs/architecture/xstate-state-architecture.md).
204
+
205
+ ## Further reading
206
+
207
+ - [XState state architecture](docs/architecture/xstate-state-architecture.md): lifecycle authority, public projections, and integration boundaries.
208
+ - [Standalone Linux compatibility](docs/standalone-linux-compatibility.md): protected-execution compatibility and conformance details.
209
+ - [Release notes](docs/releases/): version-specific changes and migration context.
210
+ - [Contributing](CONTRIBUTING.md): development workflow and repository conventions.
211
+ - [Security policy](SECURITY.md): vulnerability reporting.
876
212
 
877
213
  ## Development
878
214
 
879
215
  ```bash
880
216
  pnpm install --frozen-lockfile
881
- pnpm run verify
217
+ pnpm run format:check
218
+ pnpm run lint
219
+ pnpm run typecheck
220
+ pnpm test
882
221
  ```
883
222
 
884
- See [CONTRIBUTING.md](CONTRIBUTING.md) and [LICENSE](LICENSE).
223
+ `pnpm run verify` runs the complete repository validation, including package and workflow checks.