nawabari 0.2.0 → 0.3.0

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.
package/README.md CHANGED
@@ -64,6 +64,8 @@ The result schemas expose the following identities:
64
64
  | claims | `claim_id`, `session_id`, `resource`, `mode` |
65
65
  | authorization | `operation`, `allowed`, `code`, `claim_ids` |
66
66
  | checkpoint evidence | `head`, `changed`, `staged`, `unstaged`, `untracked`, `in_claim`, `out_of_claim` |
67
+ | repository evidence | `session_id`, `base_revision`, `head`, `clean`, `paths.stats`, `evidence_hash` |
68
+ | bounded diff | `from_revision`, `to_revision`, `paths`, `stats`, `patch`, `evidence_hash` |
67
69
  | commit/push | `commit_sha`, `remote`, `branch`, `target`, `relation` |
68
70
  | reconciliation/cleanup | `clean`, `issues`, `candidates`, `cleaned`, `blocked`, `recovery_hints` |
69
71
 
@@ -74,6 +76,35 @@ The local lifecycle requires Git and the repository-local registry/lock only;
74
76
  it does not require Mottainai, GitHub, `gh`, network access, an LLM, or a
75
77
  coding-agent runtime.
76
78
 
79
+ ## Read-only repository evidence
80
+
81
+ The evidence family is session-addressed and has no task, Issue, semantic, or
82
+ GitHub interpretation. It is the physical repository authority for one owned
83
+ session:
84
+
85
+ ```bash
86
+ git nawabari evidence snapshot --session "$NAWABARI_SESSION_ID" --json
87
+ git nawabari diff --session "$NAWABARI_SESSION_ID" --path src/example.ts --json
88
+ git nawabari diff --session "$NAWABARI_SESSION_ID" --path src/example.ts \
89
+ --patch --max-bytes 32768 --max-hunks 32 --json
90
+ ```
91
+
92
+ `evidence snapshot` verifies the registry's repository/worktree/branch owner,
93
+ then reuses checkpoint's exact NUL-safe Git observation for `changed`,
94
+ `staged`, `unstaged`, and `untracked` paths. It also reports canonical per-path
95
+ stats, `clean`, the current `head`, session state, and an `evidence_hash`.
96
+ New sessions persist the exact creation/base revision as `base_revision`;
97
+ legacy records that lack this field report `base_revision: null` and
98
+ `base_revision_proven: false` rather than inferring it from a mutable ref.
99
+
100
+ `diff` requires at least one explicit concrete path and never accepts a glob or
101
+ an empty repository-wide selection. Stats are returned by default. Patch text
102
+ requires `--patch` and is bounded to at most 64 paths, 64 KiB, and 128 hunks;
103
+ the caller may request smaller limits. Unrepresentable Git observations fail
104
+ with `GIT_STATE_AMBIGUOUS`; a requested path whose stat is not exposed by Git
105
+ remains in the result with `available: false` and makes snapshot evidence
106
+ `complete: false`, so no path silently disappears.
107
+
77
108
  ## Session lifecycle
78
109
 
79
110
  Session IDs are generated automatically as UUIDv7 values. They are immutable
@@ -91,6 +122,11 @@ git nawabari gc --dry-run --json
91
122
  git nawabari doctor --json
92
123
  ```
93
124
 
125
+ `status --json` reports the resolved `managed_worktree_root` used when
126
+ `session create` omits `--worktree`. `session create --help --json` describes
127
+ all four create options as optional and reports defaults for branch, worktree,
128
+ base (`HEAD`), and label.
129
+
94
130
  `session create` provisions a dedicated worktree and mutable branch atomically
95
131
  under the repository-scoped mutation lock. The default/integration worktree
96
132
  and its protected branch cannot be session resources. `session id` and the
@@ -108,6 +144,19 @@ every candidate that is not safe. Cleanup revalidates the physical worktree,
108
144
  branch, and `HEAD` observations immediately before each destructive Git
109
145
  operation.
110
146
 
147
+ Routine `session list` and `status` output excludes `closed` history and is
148
+ limited to 64 records. Use `--all` (or `--history`) for an explicit complete
149
+ history view; closed records remain persisted and are never silently deleted
150
+ by listing or cleanup.
151
+
152
+ `gc` stale eligibility is separate from closed-history retention. Its default
153
+ threshold is 24 hours (`86,400,000` ms), measured from persisted `updated_at`;
154
+ records already in `stale` or `closing` state are eligible, and an otherwise
155
+ live record is also eligible when Git reports its registered worktree as
156
+ missing or prunable. Physical Git/worktree state is authoritative for that
157
+ check. `gc --dry-run` and `gc --apply` do not treat a closed record as a stale
158
+ cleanup candidate.
159
+
111
160
  `doctor` includes a non-destructive `reconciliation` check. It reports
112
161
  registry/Git ownership drift, including missing or prunable worktrees and
113
162
  unregistered physical worktrees, without repairing or deleting anything.
@@ -119,8 +168,10 @@ same repository registry and protected by the same mutation lock. They are
119
168
  caller declarations; Nawabari does not infer them from task text or source
120
169
  code. Claim JSON exposes `schema_version`, `claim_id`, `session_id`, the
121
170
  repository/worktree identities, canonical `resource`, `mode`, and timestamps.
122
- The initial claim schema version is `1` and supports `read`, `write`, and
123
- `exclusive-write`.
171
+ The claim schema version is `2` and supports `read`, `write`, and
172
+ `exclusive-write`. Schema v1 records use different overlap semantics and are
173
+ not interpreted implicitly: an embedding caller must explicitly run
174
+ `SessionRegistry.migrate()` before using them.
124
175
 
125
176
  ```bash
126
177
  git nawabari session claim --session "$NAWABARI_SESSION_ID" \
@@ -131,14 +182,23 @@ git nawabari session update --session "$NAWABARI_SESSION_ID" \
131
182
  git nawabari session release --session "$NAWABARI_SESSION_ID" --json
132
183
  ```
133
184
 
185
+ The modes have these normative meanings:
186
+
187
+ - `read`: a non-mutating access declaration. It is not a consistency lease,
188
+ so it may overlap an ordinary `write` claim.
189
+ - `write`: ordinary source-modification authority. It may overlap `read`, but
190
+ not another writer or any `exclusive-write` claim.
191
+ - `exclusive-write`: stronger ownership-sensitive mutation authority. It
192
+ excludes every overlapping claim, including `read`.
193
+
134
194
  Overlapping claims use this complete compatibility matrix; non-overlapping
135
195
  claims are compatible for every mode:
136
196
 
137
- | existing \/ requested | read | write | exclusive-write |
138
- | --------------------- | ---------- | -------- | --------------- |
139
- | read | compatible | conflict | conflict |
140
- | write | conflict | conflict | conflict |
141
- | exclusive-write | conflict | conflict | conflict |
197
+ | existing \/ requested | read | write | exclusive-write |
198
+ | --------------------- | ---------- | ---------- | --------------- |
199
+ | read | compatible | compatible | conflict |
200
+ | write | compatible | conflict | conflict |
201
+ | exclusive-write | conflict | conflict | conflict |
142
202
 
143
203
  Claims use canonical repository-relative POSIX paths. Literal path segments,
144
204
  `*`/`?` segment wildcards, and a complete `**` segment are supported. Empty,
@@ -149,6 +209,26 @@ garbage-collecting a session releases its claims; no separate claim registry
149
209
  or claim lock exists. Claims describe ownership state only and do not provide
150
210
  OS-level filesystem observation or a filesystem sandbox.
151
211
 
212
+ An ordinary source change uses `write` and can proceed while another session
213
+ holds a `read` declaration:
214
+
215
+ ```bash
216
+ git nawabari session claim --session "$NAWABARI_SESSION_ID" \
217
+ --resource src/example.ts --mode write --json
218
+ git nawabari authorize --session "$NAWABARI_SESSION_ID" \
219
+ --operation source-write --resource src/example.ts --json
220
+ ```
221
+
222
+ A stronger ownership-sensitive operation uses `exclusive-write` and therefore
223
+ requires no overlapping claim:
224
+
225
+ ```bash
226
+ git nawabari session claim --session "$NAWABARI_SESSION_ID" \
227
+ --resource src/example.ts --mode exclusive-write --json
228
+ git nawabari authorize --session "$NAWABARI_SESSION_ID" \
229
+ --operation commit --resource src/example.ts --json
230
+ ```
231
+
152
232
  ## Ownership guard
153
233
 
154
234
  `git nawabari guard` is a cheap, side-effect-free authorization decision for a
@@ -217,9 +297,15 @@ repository-relative resources. Nawabari independently verifies the current
217
297
  repository, owned worktree, branch, active session, and persisted claims;
218
298
  caller-supplied labels do not weaken that decision. The JSON result is the
219
299
  automation contract and reports stable allow/deny codes such as
220
- `MISSING_RESOURCE_CLAIM`, `RESOURCE_CLAIM_CONFLICT`, `INVALID_RESOURCE`, and
300
+ `MISSING_RESOURCE_CLAIM`, `INSUFFICIENT_CLAIM_MODE`, `RESOURCE_CLAIM_CONFLICT`,
301
+ `INVALID_RESOURCE`, and
221
302
  the existing ownership/physical-observation codes.
222
303
 
304
+ `INSUFFICIENT_CLAIM_MODE` means a matching claim exists but its granted mode
305
+ is weaker than the operation requires. Its bounded details identify the
306
+ resource, required access, and matching granted mode names;
307
+ `MISSING_RESOURCE_CLAIM` remains reserved for an absent matching claim.
308
+
223
309
  **`authorize` returns an authorization decision only; it does NOT execute the
224
310
  operation itself.** Governed commit and push execution use this same decision
225
311
  path before invoking bounded Git subprocesses.
@@ -234,17 +320,51 @@ repository-relative resources. Every resource must be covered by an active
234
320
  `exclusive-write` claim; all Git-visible changed/staged paths must be in the
235
321
  explicit list. JSON includes the resulting `commit_sha`.
236
322
 
323
+ Commit evidence distinguishes three sets: the caller's **declared/authorized**
324
+ resources (the explicit, claim-covered list a caller passed in), the
325
+ **staged** set Git reports as staged immediately before the commit runs, and
326
+ the **actual committed** set — read back from the resulting commit itself via
327
+ a bounded, NUL-safe `git diff-tree` observation, not inferred from staging
328
+ intent. The `resources` field in a successful `commit --json` result is
329
+ always the actual committed set, proven equal to or a subset of the
330
+ authorized resources; if Git staging/index drift between staging and the
331
+ commit (a hook, a concurrent process) causes the actual commit to contain a
332
+ path outside the authorized set, the result is not reported as an ordinary
333
+ successful commit — it fails with `COMMIT_RESULT_DIVERGED`, which retains the
334
+ resulting `commitSha` (the Git commit already happened) alongside the
335
+ authorized, actual, and divergent path sets for recovery/reconciliation.
336
+
237
337
  ```bash
238
338
  git nawabari commit --session "$NAWABARI_SESSION_ID" \
239
339
  --message 'record the local change' --resource src/example.ts --json
240
340
  ```
241
341
 
342
+ An optional `--message-pattern <regex>` validates the final message against a
343
+ caller-declared rule before Git is invoked; Nawabari does not own or infer
344
+ commit-message conventions (such as Conventional Commits) itself, so this
345
+ check runs only when a caller explicitly supplies a pattern, and a mismatch
346
+ fails with `INVALID_COMMIT_MESSAGE` before anything is staged. The pattern is
347
+ bounded to 512 characters and is evaluated before the repository lock is
348
+ acquired, so a pathological caller-supplied pattern cannot stall other
349
+ sessions' governed operations. A repository's own `commit-msg` Git hook (if
350
+ any) still runs normally, since governed commit invokes real `git commit`.
351
+
352
+ ```bash
353
+ git nawabari commit --session "$NAWABARI_SESSION_ID" \
354
+ --message 'feat: record the local change' --resource src/example.ts \
355
+ --message-pattern '^(feat|fix|docs|refactor|test|chore): .+$' --json
356
+ ```
357
+
242
358
  Governed push requires explicit claim-covered resources and an explicit
243
359
  `--remote`/`--branch` target. Existing upstream and local/remote relation are
244
360
  inspected before mutation. A missing upstream requires `--create-upstream`;
245
- behind or diverged history requires explicit `--force`, which uses
246
- `--force-with-lease`. The JSON result identifies the pushed `target` and
247
- reports its relation.
361
+ behind or diverged history requires explicit `--force`. Force pushes use an
362
+ exact `--force-with-lease` bound to the observed remote branch SHA; non-force
363
+ pushes rely on `--no-force` without any lease option. Nawabari fetches only
364
+ the explicit remote branch into a disposable ref when local ancestry is
365
+ missing; it does not update tracking refs or fetch unrelated branches/tags.
366
+ The JSON result includes the immutable `source_sha`, explicit `target_ref`,
367
+ observed `observed_remote_sha`, and relation.
248
368
 
249
369
  ```bash
250
370
  git nawabari push --session "$NAWABARI_SESSION_ID" \
@@ -259,6 +379,15 @@ filesystem activity that is reverted, ignored, or otherwise not observable in
259
379
  the Git checkpoint is outside Nawabari's guarantee; this feature is not an
260
380
  OS-level filesystem monitor.
261
381
 
382
+ Checkpoint canonicalization fails closed: a Git-reported path that cannot be
383
+ represented as a canonical repository resource (traversal, symlink escape, or
384
+ syntax reserved for the claim/glob model) never disappears from evidence.
385
+ Checkpoint fails the whole observation with `GIT_STATE_AMBIGUOUS` instead of
386
+ silently omitting the path, so a caller can never mistake an unrepresentable
387
+ observation for a clean one. This mirrors the strictness governed mutation
388
+ already applies to the same Git-observed paths, so checkpoint evidence is
389
+ never weaker than mutation authorization.
390
+
262
391
  ## Physical execution context
263
392
 
264
393
  Nawabari treats Git and the canonical filesystem as the authority for every
@@ -280,6 +409,11 @@ worktree paths, and existing local branches are rejected deterministically;
280
409
  the repository lock serializes Nawabari provisioning and Git's own ref checks
281
410
  remain the final collision authority.
282
411
 
412
+ An explicit `--base` ref that is empty, malformed, or does not resolve to a
413
+ commit fails with `INVALID_BASE_REF`. The bounded JSON details retain the
414
+ rejected ref, identify `HEAD` as the default recovery base, and include the
415
+ retry hint to omit `--base`; Nawabari does not enumerate or fuzzy-search refs.
416
+
283
417
  ## Repository state and concurrency
284
418
 
285
419
  The authoritative registry is stored in the repository-common Git directory at