nawabari 0.5.0 → 0.6.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
@@ -94,6 +94,7 @@ The supported standalone sequence is:
94
94
  ```text
95
95
  session create -> session claim(s) -> authorize/checkpoint
96
96
  -> commit/push -> doctor (reconciliation) -> session close/gc
97
+ -> explicit session discard only when the selected work is intentionally disposable
97
98
  ```
98
99
 
99
100
  The JSON envelope is one document on stdout. Success has `ok: true`, a
@@ -106,16 +107,17 @@ failure. Consumers must use these fields and codes, never human presentation.
106
107
 
107
108
  The result schemas expose the following identities:
108
109
 
109
- | Surface | Versioned identities |
110
- | ---------------------- | -------------------------------------------------------------------------------- |
111
- | session lifecycle | `session_id`, `repository`, `worktree`, `branch`, `state` |
112
- | claims | `claim_id`, `session_id`, `resource`, `mode` |
113
- | authorization | `operation`, `allowed`, `code`, `claim_ids` |
114
- | checkpoint evidence | `head`, `changed`, `staged`, `unstaged`, `untracked`, `in_claim`, `out_of_claim` |
115
- | repository evidence | `session_id`, `base_revision`, `head`, `clean`, `paths.stats`, `evidence_hash` |
116
- | bounded diff | `from_revision`, `to_revision`, `paths`, `stats`, `patch`, `evidence_hash` |
117
- | commit/push | `commit_sha`, `remote`, `branch`, `target`, `relation` |
118
- | reconciliation/cleanup | `clean`, `issues`, `candidates`, `cleaned`, `blocked`, `recovery_hints` |
110
+ | Surface | Versioned identities |
111
+ | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
112
+ | session lifecycle | `session_id`, `repository`, `worktree`, `branch`, `state` |
113
+ | explicit discard | `previous_head`, `worktree_removed`, `branch_removed`, `released_claims`, `final_state`, final `session.state`/`terminal_operation` |
114
+ | claims | `claim_id`, `session_id`, `resource`, `mode` |
115
+ | authorization | `operation`, `allowed`, `code`, `claim_ids` |
116
+ | checkpoint evidence | `head`, `changed`, `staged`, `unstaged`, `untracked`, `in_claim`, `out_of_claim` |
117
+ | repository evidence | `session_id`, `base_revision`, `head`, `clean`, `paths.stats`, `evidence_hash` |
118
+ | bounded diff | `from_revision`, `to_revision`, `paths`, `stats`, `patch`, `evidence_hash` |
119
+ | commit/push | `commit_sha`, `remote`, `branch`, `target`, `relation` |
120
+ | reconciliation/cleanup | `clean`, `issues`, `candidates`, `cleaned`, `blocked`, `recovery_hints` |
119
121
 
120
122
  Git subprocesses are bounded at 10 seconds and 64 KiB of output; checkpoint
121
123
  evidence is bounded to 4,096 paths. `GIT_SPAWN_FAILED`, `GIT_TIMEOUT`,
@@ -162,10 +164,12 @@ machine identities; labels and branch names are separate display metadata.
162
164
  git nawabari session create --branch feature/example --worktree ../example-worktree --json
163
165
  git nawabari session id --json
164
166
  git nawabari session show --json
167
+ git nawabari session show <session-id> --json
165
168
  git nawabari session list --json
166
169
  git nawabari status --json
167
170
  git nawabari guard --json
168
171
  git nawabari session close --json
172
+ git nawabari session discard --session <session-id> --json
169
173
  git nawabari gc --dry-run --json
170
174
  git nawabari doctor --json
171
175
  ```
@@ -185,7 +189,14 @@ the resolved parent of that session's worktree.
185
189
  under the repository-scoped mutation lock. The default/integration worktree
186
190
  and its protected branch cannot be session resources. `session id` and the
187
191
  other current-session commands resolve ownership from the current worktree;
188
- callers do not need to repeat the session ID.
192
+ callers do not need to repeat the session ID for current-owner operations.
193
+
194
+ Session-scoped commands use one target grammar: the canonical `--session <id>`
195
+ option is accepted everywhere, and `show`, `inspect`, `claim`, `claims`,
196
+ `release`, `update`, `close`, and `discard` also accept one positional
197
+ `<session-id>` immediately after the subcommand. Supplying both forms is
198
+ rejected as ambiguous. `session discard` always requires one explicit target
199
+ and never infers the current worktree owner.
189
200
 
190
201
  Close is conservative. Dirty worktrees, ambiguous ownership, mismatched Git
191
202
  state, and commits not proven reachable from the integration branch block
@@ -198,6 +209,17 @@ every candidate that is not safe. Cleanup revalidates the physical worktree,
198
209
  branch, and `HEAD` observations immediately before each destructive Git
199
210
  operation.
200
211
 
212
+ `session discard` is the sole explicit destructive abandonment path. It
213
+ revalidates repository identity, the selected session, exact worktree/branch
214
+ ownership, and session/worktree `HEAD` immediately before each Git mutation.
215
+ It may destroy the selected session's unintegrated commits and uncommitted
216
+ worktree contents, removes only that session's worktree/branch, releases only
217
+ its claims, and records `terminal_operation: "discard"` plus the pre-discard
218
+ `HEAD`. It never acts as an implicit fallback for `close`, `gc`, `doctor`, or
219
+ reconciliation; sibling sessions remain untouched. A partial failure leaves a
220
+ retryable closing record and a repeated discard converges or returns an
221
+ explicit terminal idempotent result.
222
+
201
223
  Routine `session list` and `status` output excludes `closed` history and is
202
224
  limited to 64 records. Use `--all` (or `--history`) for an explicit complete
203
225
  history view; closed records remain persisted and are never silently deleted
@@ -363,8 +385,14 @@ differs.
363
385
  could not prove the branch safe and a `--integrated-revision` proof might
364
386
  resolve it (e.g. after a squash/rebase merge), versus `blocked` when
365
387
  supplied evidence failed to prove equivalence, versus `ambiguous` when Git
366
- observation itself was inconclusive. Both surfaces reuse one authority, so
367
- a raw close rejection and `session inspect` never drift apart.
388
+ observation itself was inconclusive. For supplied evidence, bounded
389
+ `proofFailure` details distinguish an unauthoritative revision from a
390
+ tree-equivalence failure. The bounded evidence also includes
391
+ `currentSessionHead`, `suppliedIntegratedRevision`, `resolvedIntegrationSha`,
392
+ `lineageProof`/`authorityProof`, and `contentProof`. `safe_actions` includes
393
+ `discard-session` only as an explicit user choice; it does not authorize an
394
+ implicit cleanup fallback. Both surfaces reuse one authority, so a raw close
395
+ rejection and `session inspect` never drift apart.
368
396
 
369
397
  None of the above weakens fail-closed behavior, changes an error code's
370
398
  meaning, or performs any mutation while producing the rejection.
package/dist/cli.js CHANGED
@@ -48,13 +48,16 @@ const HELP_COMMANDS = [
48
48
  {
49
49
  name: "session show",
50
50
  summary: "Show the current or selected session",
51
- usage: `${CLI_NAME} session show [--session <id>]`,
51
+ usage: `${CLI_NAME} session show [<session-id>|--session <id>]`,
52
52
  options: [option("--session", "Select a session instead of the current worktree owner", { value: "<id>" })],
53
+ notes: [
54
+ "Target grammar is consistent: an optional first positional <session-id> is an alias for --session <id>; do not supply both.",
55
+ ],
53
56
  },
54
57
  {
55
58
  name: "session inspect",
56
59
  summary: "Report side-effect-free close/cleanup readiness for a session",
57
- usage: `${CLI_NAME} session inspect [--session <id>] [--integrated-revision <rev>]`,
60
+ usage: `${CLI_NAME} session inspect [<session-id>|--session <id>] [--integrated-revision <rev>]`,
58
61
  options: [
59
62
  option("--session", "Select a session instead of the current worktree owner", { value: "<id>" }),
60
63
  option("--integrated-revision", "Externally evidenced revision to test for non-ancestry (squash/rebase) integration; independently re-verified via exact Git tree-object equivalence, never trusted blindly", { value: "<rev>" }),
@@ -63,6 +66,7 @@ const HELP_COMMANDS = [
63
66
  "Read-only: never mutates session, claim, Git, worktree, branch, or registry state. Repeated calls are idempotent.",
64
67
  "Derived from the same authoritative close/cleanup Git evidence as session close; does not duplicate or diverge from that logic.",
65
68
  "Nawabari never queries GitHub or any remote provider; --integrated-revision only names a local revision for Nawabari to independently verify.",
69
+ "Target grammar: optional first positional <session-id> is an alias for --session <id>; do not supply both.",
66
70
  ],
67
71
  },
68
72
  {
@@ -88,18 +92,21 @@ const HELP_COMMANDS = [
88
92
  {
89
93
  name: "session claim",
90
94
  summary: "Add a canonical resource claim",
91
- usage: `${CLI_NAME} session claim --resource <path-or-glob> --mode <read|write|exclusive-write> [--session <id>]`,
95
+ usage: `${CLI_NAME} session claim [<session-id>] --resource <path-or-glob> --mode <read|write|exclusive-write>`,
92
96
  options: [
93
97
  option("--resource", "Repository-relative resource", { value: "<path-or-glob>", required: true }),
94
98
  option("--mode", "Granted claim mode", { value: "<read|write|exclusive-write>", required: true }),
95
99
  option("--session", "Target active session; omitted resolves the current owner", { value: "<id>" }),
96
100
  option("--repository", "Expected repository identity", { value: "<id>" }),
97
101
  ],
102
+ notes: [
103
+ "Target grammar: optional first positional <session-id> is an alias for --session <id>; do not supply both.",
104
+ ],
98
105
  },
99
106
  {
100
107
  name: "session update",
101
108
  summary: "Atomically replace a session's complete resource claim set",
102
- usage: `${CLI_NAME} session update --resource <path-or-glob> --mode <read|write|exclusive-write> [--resource <path-or-glob> --mode <read|write|exclusive-write> ...] [--session <id>]`,
109
+ usage: `${CLI_NAME} session update [<session-id>] --resource <path-or-glob> --mode <read|write|exclusive-write> [--resource <path-or-glob> --mode <read|write|exclusive-write> ...]`,
103
110
  options: [
104
111
  option("--resource", "Repository-relative resource; repeatable, each paired with the --mode immediately after it", {
105
112
  value: "<path-or-glob>",
@@ -116,33 +123,43 @@ const HELP_COMMANDS = [
116
123
  "The desired claim set fully replaces the session's current claims in one updateClaims() transaction; " +
117
124
  "on any invalid or conflicting claim the prior set is left unchanged.",
118
125
  "Each --resource must be immediately followed by its own --mode; pairing is positional adjacency, not flag order.",
126
+ "Target grammar: optional first positional <session-id> is an alias for --session <id>; do not supply both.",
119
127
  ],
120
128
  },
121
129
  {
122
130
  name: "session claims",
123
131
  summary: "List canonical resource claims",
124
- usage: `${CLI_NAME} session claims [--session <id>]`,
132
+ usage: `${CLI_NAME} session claims [<session-id>|--session <id>]`,
125
133
  options: [option("--session", "Select a session; omitted lists all claims", { value: "<id>" })],
134
+ notes: [
135
+ "Target grammar: optional first positional <session-id> is an alias for --session <id>; do not supply both.",
136
+ ],
126
137
  },
127
138
  {
128
139
  name: "session release",
129
140
  summary: "Release resource claims",
130
- usage: `${CLI_NAME} session release [--session <id>] [--claim-id <id>]`,
141
+ usage: `${CLI_NAME} session release [<session-id>|--session <id>] [--claim-id <id>]`,
131
142
  options: [
132
143
  option("--session", "Target session; omitted resolves the current owner", { value: "<id>" }),
133
144
  option("--claim-id", "Release only one claim; omitted releases all owned claims", { value: "<id>" }),
134
145
  ],
146
+ notes: [
147
+ "Target grammar: optional first positional <session-id> is an alias for --session <id>; do not supply both.",
148
+ ],
135
149
  },
136
150
  {
137
151
  name: "resource claim",
138
152
  summary: "Add a canonical resource claim (alias)",
139
- usage: `${CLI_NAME} resource claim --resource <path-or-glob> --mode <read|write|exclusive-write>`,
153
+ usage: `${CLI_NAME} resource claim [<session-id>|--session <id>] --resource <path-or-glob> --mode <read|write|exclusive-write>`,
140
154
  options: [],
155
+ notes: [
156
+ "Target grammar matches session claim: optional first positional <session-id> is an alias for --session <id>; do not supply both.",
157
+ ],
141
158
  },
142
159
  {
143
160
  name: "resource update",
144
161
  summary: "Atomically replace a session's complete resource claim set (alias)",
145
- usage: `${CLI_NAME} resource update --resource <path-or-glob> --mode <read|write|exclusive-write> [--resource <path-or-glob> --mode <read|write|exclusive-write> ...] [--session <id>]`,
162
+ usage: `${CLI_NAME} resource update [<session-id>|--session <id>] --resource <path-or-glob> --mode <read|write|exclusive-write> [--resource <path-or-glob> --mode <read|write|exclusive-write> ...]`,
146
163
  options: [
147
164
  option("--resource", "Repository-relative resource; repeatable, each paired with the --mode immediately after it", {
148
165
  value: "<path-or-glob>",
@@ -159,27 +176,34 @@ const HELP_COMMANDS = [
159
176
  "The desired claim set fully replaces the session's current claims in one updateClaims() transaction; " +
160
177
  "on any invalid or conflicting claim the prior set is left unchanged.",
161
178
  "Each --resource must be immediately followed by its own --mode; pairing is positional adjacency, not flag order.",
179
+ "Target grammar matches session update: optional first positional <session-id> is an alias for --session <id>; do not supply both.",
162
180
  ],
163
181
  },
164
182
  {
165
183
  name: "resource list",
166
184
  summary: "List canonical resource claims (alias)",
167
- usage: `${CLI_NAME} resource list [--session <id>]`,
185
+ usage: `${CLI_NAME} resource list [<session-id>|--session <id>]`,
168
186
  options: [option("--session", "Select a session; omitted lists all claims", { value: "<id>" })],
187
+ notes: [
188
+ "Target grammar: optional first positional <session-id> is an alias for --session <id>; do not supply both.",
189
+ ],
169
190
  },
170
191
  {
171
192
  name: "resource release",
172
193
  summary: "Release resource claims (alias)",
173
- usage: `${CLI_NAME} resource release [--session <id>] [--claim-id <id>]`,
194
+ usage: `${CLI_NAME} resource release [<session-id>|--session <id>] [--claim-id <id>]`,
174
195
  options: [
175
196
  option("--session", "Target session; omitted resolves the current owner", { value: "<id>" }),
176
197
  option("--claim-id", "Release only one claim; omitted releases all owned claims", { value: "<id>" }),
177
198
  ],
199
+ notes: [
200
+ "Target grammar: optional first positional <session-id> is an alias for --session <id>; do not supply both.",
201
+ ],
178
202
  },
179
203
  {
180
204
  name: "session close",
181
205
  summary: "Close the current or selected session",
182
- usage: `${CLI_NAME} session close [--session <id>] [--integrated-revision <rev>]`,
206
+ usage: `${CLI_NAME} session close [<session-id>|--session <id>] [--integrated-revision <rev>]`,
183
207
  options: [
184
208
  option("--session", "Select a session instead of the current worktree owner", { value: "<id>" }),
185
209
  option("--integrated-revision", "Externally evidenced revision proving non-ancestry (squash/rebase) integration; independently re-verified via exact Git tree-object equivalence, never trusted blindly", { value: "<rev>" }),
@@ -187,6 +211,24 @@ const HELP_COMMANDS = [
187
211
  notes: [
188
212
  "Ordinary ancestry-based close remains the cheap/default path and requires no flags.",
189
213
  "Nawabari never queries GitHub or any remote provider; --integrated-revision only names a local revision for Nawabari to independently verify.",
214
+ "Target grammar: optional first positional <session-id> is an alias for --session <id>; do not supply both.",
215
+ ],
216
+ },
217
+ {
218
+ name: "session discard",
219
+ summary: "Explicitly discard one selected session and its owned resources",
220
+ usage: `${CLI_NAME} session discard <session-id>|--session <id>`,
221
+ options: [
222
+ option("--session", "Required explicit target; the current session is never inferred", {
223
+ value: "<id>",
224
+ required: true,
225
+ }),
226
+ ],
227
+ notes: [
228
+ "Destructive and explicit: unintegrated commits and uncommitted work in the selected owned worktree may be destroyed.",
229
+ "Discard never changes close, gc, doctor, reconciliation, or integration-lineage proof behavior.",
230
+ "Use exactly one target form: positional <session-id> or --session <id>; do not supply both.",
231
+ "Machine JSON mode is non-interactive and deterministic.",
190
232
  ],
191
233
  },
192
234
  {
@@ -347,6 +389,13 @@ function helpPayload(spec) {
347
389
  commit_options: optionsFor("commit"),
348
390
  push_options: optionsFor("push"),
349
391
  gc_options: optionsFor("gc"),
392
+ session_targeting: {
393
+ canonical: "--session <id>",
394
+ positional_alias: "<session-id> as the first argument after a session-scoped subcommand",
395
+ commands: ["show", "inspect", "claim", "claims", "release", "update", "close", "discard"],
396
+ ambiguity: "supplying both positional and --session is rejected",
397
+ discard_requires_explicit_target: true,
398
+ },
350
399
  };
351
400
  }
352
401
  const options = spec.options.map((candidate) => ({
@@ -579,6 +628,34 @@ function parseOptions(arguments_, allowed) {
579
628
  function noOptions(arguments_) {
580
629
  return parseOptions(arguments_, new Set());
581
630
  }
631
+ /** One unambiguous positional alias shared by every session-targeted command. */
632
+ function splitPositionalSessionTarget(arguments_) {
633
+ const first = arguments_[0];
634
+ if (first === undefined || first.startsWith("-")) {
635
+ return { ok: true, value: { sessionId: null, arguments: arguments_ } };
636
+ }
637
+ return { ok: true, value: { sessionId: first, arguments: arguments_.slice(1) } };
638
+ }
639
+ function parseTargetedOptions(arguments_, allowed, required = false) {
640
+ const positional = splitPositionalSessionTarget(arguments_);
641
+ if (!positional.ok)
642
+ return positional;
643
+ const parsed = parseOptions(positional.value.arguments, allowed);
644
+ if (!parsed.ok)
645
+ return parsed;
646
+ if (positional.value.sessionId !== null && parsed.value.session_id !== null) {
647
+ return failure(usageError("INVALID_ARGUMENT", "Specify a session target either positionally or with --session, not both.", {
648
+ option: "--session",
649
+ }));
650
+ }
651
+ parsed.value.session_id = positional.value.sessionId ?? parsed.value.session_id;
652
+ if (required && parsed.value.session_id === null) {
653
+ return failure(usageError("MISSING_ARGUMENT", "An explicit session target is required; use <session-id> or --session <id>.", {
654
+ option: "--session",
655
+ }));
656
+ }
657
+ return parsed;
658
+ }
582
659
  /**
583
660
  * `update` accepts a complete desired claim set as repeated
584
661
  * `--resource <path> --mode <mode>` pairs. Pairing is by strict local
@@ -588,26 +665,35 @@ function noOptions(arguments_) {
588
665
  * with the wrong mode.
589
666
  */
590
667
  function parseClaimReplacementPairs(arguments_) {
668
+ const positional = splitPositionalSessionTarget(arguments_);
669
+ if (!positional.ok)
670
+ return positional;
591
671
  let sessionId = null;
592
672
  let repository = null;
593
673
  const pairs = [];
594
674
  let pendingResource = null;
595
- for (let index = 0; index < arguments_.length; index += 1) {
596
- const { name, inlineValue } = optionParts(arguments_[index]);
675
+ sessionId = positional.value.sessionId;
676
+ const targetArguments = positional.value.arguments;
677
+ for (let index = 0; index < targetArguments.length; index += 1) {
678
+ const { name, inlineValue } = optionParts(targetArguments[index]);
597
679
  if (name !== "--session" && name !== "--repository" && name !== "--resource" && name !== "--mode") {
598
680
  return failure(usageError("INVALID_ARGUMENT", `Unknown option: ${name}.`, { option: name }));
599
681
  }
600
682
  if (pendingResource !== null && name !== "--mode") {
601
683
  return failure(usageError("INVALID_ARGUMENT", `${name} cannot appear between --resource and its --mode; only --mode is permitted immediately after --resource.`, { option: name }));
602
684
  }
603
- const value = inlineValue ?? arguments_[index + 1];
685
+ const value = inlineValue ?? targetArguments[index + 1];
604
686
  if (value === undefined || value === "" || (inlineValue === null && value.startsWith("-"))) {
605
687
  return failure(usageError("MISSING_ARGUMENT", `${name} requires a value.`, { option: name }));
606
688
  }
607
689
  if (inlineValue === null)
608
690
  index += 1;
609
- if (name === "--session")
691
+ if (name === "--session") {
692
+ if (sessionId !== null) {
693
+ return failure(usageError("INVALID_ARGUMENT", "Specify a session target either positionally or with --session, not both."));
694
+ }
610
695
  sessionId = value;
696
+ }
611
697
  else if (name === "--repository")
612
698
  repository = value;
613
699
  else if (name === "--resource")
@@ -745,7 +831,7 @@ async function executeCommand(commandArguments, dependencies) {
745
831
  return result.ok ? { ok: true, value: result.value } : result;
746
832
  }
747
833
  if (subcommand === "claims") {
748
- const parsed = parseOptions(rest, new Set(["--session"]));
834
+ const parsed = parseTargetedOptions(rest, new Set(["--session"]));
749
835
  if (!parsed.ok)
750
836
  return parsed;
751
837
  if (dependencies.backend.listClaims === undefined)
@@ -754,7 +840,7 @@ async function executeCommand(commandArguments, dependencies) {
754
840
  return result.ok ? { ok: true, value: result.value } : result;
755
841
  }
756
842
  if (subcommand === "release") {
757
- const parsed = parseOptions(rest, new Set(["--session", "--claim-id"]));
843
+ const parsed = parseTargetedOptions(rest, new Set(["--session", "--claim-id"]));
758
844
  if (!parsed.ok)
759
845
  return parsed;
760
846
  if (dependencies.backend.releaseClaims === undefined)
@@ -805,7 +891,7 @@ async function executeCommand(commandArguments, dependencies) {
805
891
  return result.ok ? { ok: true, value: result.value } : result;
806
892
  }
807
893
  if (subcommand === "inspect") {
808
- const parsed = parseOptions(rest, new Set(["--session", "--integrated-revision"]));
894
+ const parsed = parseTargetedOptions(rest, new Set(["--session", "--integrated-revision"]));
809
895
  if (!parsed.ok)
810
896
  return parsed;
811
897
  if (dependencies.backend.sessionDiagnostic === undefined)
@@ -817,6 +903,15 @@ async function executeCommand(commandArguments, dependencies) {
817
903
  const result = await dependencies.backend.sessionDiagnostic(context, options);
818
904
  return result.ok ? { ok: true, value: result.value } : result;
819
905
  }
906
+ if (subcommand === "discard") {
907
+ const parsed = parseTargetedOptions(rest, new Set(["--session"]), true);
908
+ if (!parsed.ok)
909
+ return parsed;
910
+ if (dependencies.backend.discardSession === undefined)
911
+ return sessionDiscardCapabilityUnavailable();
912
+ const result = await dependencies.backend.discardSession(context, parsed.value.session_id);
913
+ return result.ok ? { ok: true, value: result.value } : result;
914
+ }
820
915
  if (subcommand === "id" || subcommand === "show" || subcommand === "close") {
821
916
  if (subcommand === "id") {
822
917
  const parsed = noOptions(rest);
@@ -827,7 +922,7 @@ async function executeCommand(commandArguments, dependencies) {
827
922
  return selected;
828
923
  return { ok: true, value: { session_id: selected.value.session_id } };
829
924
  }
830
- const parsed = parseOptions(rest, new Set(subcommand === "close" ? ["--session", "--integrated-revision"] : ["--session"]));
925
+ const parsed = parseTargetedOptions(rest, new Set(subcommand === "close" ? ["--session", "--integrated-revision"] : ["--session"]));
831
926
  if (!parsed.ok)
832
927
  return parsed;
833
928
  if (subcommand === "close") {
@@ -885,7 +980,7 @@ async function executeCommand(commandArguments, dependencies) {
885
980
  return result.ok ? { ok: true, value: result.value } : result;
886
981
  }
887
982
  if (resourceSubcommand === "list" || resourceSubcommand === "claims") {
888
- const parsed = parseOptions(rest, new Set(["--session"]));
983
+ const parsed = parseTargetedOptions(rest, new Set(["--session"]));
889
984
  if (!parsed.ok)
890
985
  return parsed;
891
986
  if (dependencies.backend.listClaims === undefined)
@@ -894,7 +989,7 @@ async function executeCommand(commandArguments, dependencies) {
894
989
  return result.ok ? { ok: true, value: result.value } : result;
895
990
  }
896
991
  if (resourceSubcommand === "release") {
897
- const parsed = parseOptions(rest, new Set(["--session", "--claim-id"]));
992
+ const parsed = parseTargetedOptions(rest, new Set(["--session", "--claim-id"]));
898
993
  if (!parsed.ok)
899
994
  return parsed;
900
995
  if (dependencies.backend.releaseClaims === undefined)
@@ -1151,6 +1246,11 @@ function sessionDiagnosticCapabilityUnavailable() {
1151
1246
  operation: "session.inspect",
1152
1247
  }));
1153
1248
  }
1249
+ function sessionDiscardCapabilityUnavailable() {
1250
+ return failure(new DomainError("BACKEND_UNAVAILABLE", "Session discard capability is not available.", {
1251
+ operation: "session.discard",
1252
+ }));
1253
+ }
1154
1254
  function repositoryEvidenceCapabilityUnavailable(operation) {
1155
1255
  return failure(new DomainError("BACKEND_UNAVAILABLE", "Repository evidence capability is not available.", { operation }));
1156
1256
  }