nawabari 0.8.0 → 0.10.1

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