nawabari 0.3.0 → 0.5.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.
@@ -1,3 +1,4 @@
1
+ import { RegistryError } from "./errors.js";
1
2
  export interface AtomicWritePaths {
2
3
  temporaryPath: string;
3
4
  targetPath: string;
@@ -10,6 +11,29 @@ export interface AtomicWriteOptions {
10
11
  ensureParent?: boolean;
11
12
  hooks?: AtomicWriteHooks;
12
13
  }
14
+ /**
15
+ * A failure observed after the target file was already renamed into place.
16
+ * Callers must not read this as "the write succeeded" — durability past
17
+ * that point is unproven — but they must also not treat it as an ordinary
18
+ * pre-effect failure: the renamed document may already be the one readers
19
+ * observe. `cause` always carries the original thrown value verbatim
20
+ * (whatever it was — an Error, a frozen object, a primitive), since a
21
+ * classification that depended on mutating or reading properties off the
22
+ * original thrown value would silently fail for values that reject writes
23
+ * (frozen/non-extensible objects) or carry no properties at all
24
+ * (primitives). Wrapping in a fresh, always-extensible error is fail-safe
25
+ * regardless of what the underlying I/O call happened to throw.
26
+ */
27
+ export declare class AtomicWritePostRenameFailure extends RegistryError {
28
+ constructor(paths: AtomicWritePaths, cause: unknown);
29
+ }
30
+ /**
31
+ * True when `writeJsonAtomically`/`writeJsonAtomicallySync` failed only
32
+ * after the rename had already committed the new document. Recovery must
33
+ * reconcile authoritative state instead of assuming the mutation never
34
+ * happened.
35
+ */
36
+ export declare function isPostRenameFailure(error: unknown): error is AtomicWritePostRenameFailure;
13
37
  /**
14
38
  * Persist JSON by writing and syncing a same-directory temporary file, then
15
39
  * replacing the target with rename. Readers therefore observe either the
@@ -14,6 +14,37 @@ function isUnsupportedDirectorySync(error) {
14
14
  const code = errorCode(error);
15
15
  return code === "EINVAL" || code === "ENOTSUP" || code === "EISDIR";
16
16
  }
17
+ /**
18
+ * A failure observed after the target file was already renamed into place.
19
+ * Callers must not read this as "the write succeeded" — durability past
20
+ * that point is unproven — but they must also not treat it as an ordinary
21
+ * pre-effect failure: the renamed document may already be the one readers
22
+ * observe. `cause` always carries the original thrown value verbatim
23
+ * (whatever it was — an Error, a frozen object, a primitive), since a
24
+ * classification that depended on mutating or reading properties off the
25
+ * original thrown value would silently fail for values that reject writes
26
+ * (frozen/non-extensible objects) or carry no properties at all
27
+ * (primitives). Wrapping in a fresh, always-extensible error is fail-safe
28
+ * regardless of what the underlying I/O call happened to throw.
29
+ */
30
+ export class AtomicWritePostRenameFailure extends RegistryError {
31
+ constructor(paths, cause) {
32
+ super("REGISTRY_DURABILITY_UNCERTAIN", `Rename to ${paths.targetPath} may have already committed, but durable persistence past that point could not be proven`, { ...paths }, { cause });
33
+ this.name = "AtomicWritePostRenameFailure";
34
+ }
35
+ }
36
+ function toPostRenameFailure(error, paths) {
37
+ return new AtomicWritePostRenameFailure(paths, error);
38
+ }
39
+ /**
40
+ * True when `writeJsonAtomically`/`writeJsonAtomicallySync` failed only
41
+ * after the rename had already committed the new document. Recovery must
42
+ * reconcile authoritative state instead of assuming the mutation never
43
+ * happened.
44
+ */
45
+ export function isPostRenameFailure(error) {
46
+ return error instanceof AtomicWritePostRenameFailure;
47
+ }
17
48
  async function syncDirectory(directory) {
18
49
  let handle;
19
50
  try {
@@ -86,8 +117,9 @@ export async function writeJsonAtomically(targetPath, value, options = {}) {
86
117
  }
87
118
  if (!renamed) {
88
119
  await rm(temporaryPath, { force: true });
120
+ throw error;
89
121
  }
90
- throw error;
122
+ throw toPostRenameFailure(error, { temporaryPath, targetPath });
91
123
  }
92
124
  }
93
125
  /** Synchronous counterpart used by the legacy synchronous registry boundary. */
@@ -121,8 +153,9 @@ export function writeJsonAtomicallySync(targetPath, value, options = {}) {
121
153
  }
122
154
  if (!renamed) {
123
155
  fs.rmSync(temporaryPath, { force: true });
156
+ throw error;
124
157
  }
125
- throw error;
158
+ throw toPostRenameFailure(error, { temporaryPath, targetPath });
126
159
  }
127
160
  }
128
161
  //# sourceMappingURL=atomic.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"atomic.js","sourceRoot":"","sources":["../../src/registry/atomic.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"atomic.js","sourceRoot":"","sources":["../../src/registry/atomic.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- export declare const REGISTRY_ERROR_CODES: readonly ["REGISTRY_NOT_FOUND", "REGISTRY_CORRUPT", "REGISTRY_UNSUPPORTED_SCHEMA", "REGISTRY_REPOSITORY_MISMATCH", "REGISTRY_DUPLICATE_SESSION", "REGISTRY_DUPLICATE_WORKTREE", "REGISTRY_DUPLICATE_BRANCH", "REGISTRY_IO_ERROR", "REGISTRY_MUTATION_FAILED", "LOCK_BUSY", "LOCK_STALE", "LOCK_INVALID", "LOCK_IO_ERROR", "LOCK_RELEASE_FAILED"];
1
+ export declare const REGISTRY_ERROR_CODES: readonly ["REGISTRY_NOT_FOUND", "REGISTRY_CORRUPT", "REGISTRY_UNSUPPORTED_SCHEMA", "REGISTRY_REPOSITORY_MISMATCH", "REGISTRY_DUPLICATE_SESSION", "REGISTRY_DUPLICATE_WORKTREE", "REGISTRY_DUPLICATE_BRANCH", "REGISTRY_IO_ERROR", "REGISTRY_DURABILITY_UNCERTAIN", "REGISTRY_MUTATION_FAILED", "LOCK_BUSY", "LOCK_STALE", "LOCK_INVALID", "LOCK_IO_ERROR", "LOCK_RELEASE_FAILED"];
2
2
  export type RegistryErrorCode = (typeof REGISTRY_ERROR_CODES)[number];
3
3
  export declare class RegistryError extends Error {
4
4
  readonly code: RegistryErrorCode;
@@ -7,6 +7,7 @@ export const REGISTRY_ERROR_CODES = [
7
7
  "REGISTRY_DUPLICATE_WORKTREE",
8
8
  "REGISTRY_DUPLICATE_BRANCH",
9
9
  "REGISTRY_IO_ERROR",
10
+ "REGISTRY_DURABILITY_UNCERTAIN",
10
11
  "REGISTRY_MUTATION_FAILED",
11
12
  "LOCK_BUSY",
12
13
  "LOCK_STALE",
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/registry/errors.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,oBAAoB;IACpB,kBAAkB;IAClB,6BAA6B;IAC7B,8BAA8B;IAC9B,4BAA4B;IAC5B,6BAA6B;IAC7B,2BAA2B;IAC3B,mBAAmB;IACnB,0BAA0B;IAC1B,WAAW;IACX,YAAY;IACZ,cAAc;IACd,eAAe;IACf,qBAAqB;CACb,CAAC;AAIX,MAAM,OAAO,aAAc,SAAQ,KAAK;IACtB,IAAI,CAAoB;IACxB,OAAO,CAAoC;IAE3D,YACE,IAAuB,EACvB,OAAe,EACf,UAA6C,EAAE,EAC/C,OAAsB;QAEtB,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;QAC5B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;CACF;AAED,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,OAAO,KAAK,YAAY,aAAa,CAAC;AACxC,CAAC"}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/registry/errors.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,oBAAoB;IACpB,kBAAkB;IAClB,6BAA6B;IAC7B,8BAA8B;IAC9B,4BAA4B;IAC5B,6BAA6B;IAC7B,2BAA2B;IAC3B,mBAAmB;IACnB,+BAA+B;IAC/B,0BAA0B;IAC1B,WAAW;IACX,YAAY;IACZ,cAAc;IACd,eAAe;IACf,qBAAqB;CACb,CAAC;AAIX,MAAM,OAAO,aAAc,SAAQ,KAAK;IACtB,IAAI,CAAoB;IACxB,OAAO,CAAoC;IAE3D,YACE,IAAuB,EACvB,OAAe,EACf,UAA6C,EAAE,EAC/C,OAAsB;QAEtB,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;QAC5B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;CACF;AAED,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,OAAO,KAAK,YAAY,aAAa,CAAC;AACxC,CAAC"}
@@ -14,6 +14,7 @@ export declare const REGISTRY_LOCK_FILE_NAME = "session-registry.lock";
14
14
  export declare const DEFAULT_STALE_AFTER_MS: number;
15
15
  export declare const CLEANUP_DECISION_SCHEMA_VERSION: 1;
16
16
  export declare const RECONCILIATION_SCHEMA_VERSION: 1;
17
+ export declare const SESSION_DIAGNOSTIC_SCHEMA_VERSION: 1;
17
18
  /** Bounds a caller-declared commit-message pattern before it is compiled. */
18
19
  export declare const MAX_MESSAGE_PATTERN_LENGTH: 512;
19
20
  export type RegistrySchemaVersion = typeof REGISTRY_SCHEMA_VERSION;
@@ -40,6 +41,7 @@ export interface CreateSessionOptions {
40
41
  }
41
42
  export interface ProvisionSessionOptions {
42
43
  readonly worktreePath?: string;
44
+ readonly worktreeRoot?: string;
43
45
  readonly branchName?: string;
44
46
  readonly baseRef?: string;
45
47
  readonly label?: string;
@@ -52,10 +54,23 @@ export interface CloseSessionResult {
52
54
  readonly worktreeRemoved: boolean;
53
55
  readonly branchRemoved: boolean;
54
56
  readonly idempotent: boolean;
57
+ readonly integrationProof?: IntegrationProof;
55
58
  }
56
59
  export interface CloseSessionOptions {
57
60
  readonly sessionId?: string | null;
58
61
  readonly session_id?: string | null;
62
+ /** Caller-supplied authoritative revision proving non-ancestry integration (e.g. a squash-merge commit). Independently re-verified by exact Git tree-object equivalence; never trusted blindly. */
63
+ readonly integratedRevision?: string | null;
64
+ readonly integrated_revision?: string | null;
65
+ }
66
+ /** Caller-supplied non-ancestry integration evidence, already normalized to camelCase. */
67
+ export interface IntegrationEvidenceInput {
68
+ readonly integratedRevision: string;
69
+ }
70
+ /** Deterministic Git-native evidence proving a branch is safely reclaimable. */
71
+ export interface IntegrationProof {
72
+ readonly method: "ancestor" | "tree-equivalence";
73
+ readonly integratedRevision?: string;
59
74
  }
60
75
  export interface GarbageCollectOptions {
61
76
  readonly apply?: boolean;
@@ -94,6 +109,44 @@ export interface CleanupDecision {
94
109
  readonly blockers: readonly CleanupBlocker[];
95
110
  readonly recoveryHints: readonly string[];
96
111
  }
112
+ /**
113
+ * Explicit close/cleanup readiness states. `external_evidence_required`
114
+ * marks the #123 non-ancestry-integration case: ancestry alone could not
115
+ * prove the branch safe to reclaim, and a caller-supplied integration
116
+ * revision (independently re-verified, never trusted blindly) may resolve
117
+ * it. `not_due` only applies to cleanup readiness: the session is otherwise
118
+ * safely closable but has not yet met the garbage-collection staleness
119
+ * threshold.
120
+ */
121
+ export type ReadinessState = "ready" | "not_due" | "blocked" | "external_evidence_required" | "ambiguous";
122
+ /** How complete/certain this diagnostic snapshot is, independent of readiness. */
123
+ export type DiagnosticCompleteness = "complete" | "ambiguous" | "stale" | "external_evidence_required";
124
+ export interface SessionDiagnosticBlocker extends CleanupBlocker {
125
+ /** Stable, kebab-case next-action identifiers; reusable by orchestrators. */
126
+ readonly safeActions: readonly string[];
127
+ }
128
+ export interface SessionDiagnosticIntegrationEvidence {
129
+ readonly supplied: boolean;
130
+ readonly integratedRevision?: string;
131
+ readonly proof?: IntegrationProof;
132
+ }
133
+ export interface SessionDiagnostic {
134
+ readonly schemaVersion: typeof SESSION_DIAGNOSTIC_SCHEMA_VERSION;
135
+ readonly operation: "diagnostic";
136
+ readonly repositoryId: string;
137
+ readonly worktreePath: string;
138
+ readonly branchName: string;
139
+ readonly session: SessionRecord;
140
+ readonly claims: readonly ResourceClaim[];
141
+ readonly physicalState: string;
142
+ readonly closeReadiness: ReadinessState;
143
+ readonly cleanupReadiness: ReadinessState;
144
+ readonly resultState: DiagnosticCompleteness;
145
+ readonly idempotent: boolean;
146
+ readonly blockers: readonly SessionDiagnosticBlocker[];
147
+ readonly safeActions: readonly string[];
148
+ readonly integrationEvidence: SessionDiagnosticIntegrationEvidence;
149
+ }
97
150
  export type ReconciliationSessionStatus = "healthy" | "candidate" | "drift" | "closed";
98
151
  export interface ReconciliationSession {
99
152
  readonly session: SessionRecord;
@@ -321,6 +374,22 @@ export declare class SessionRegistry {
321
374
  createSession(options?: CreateSessionOptions): SessionRecord;
322
375
  /** Provision one isolated Git worktree and commit its ownership atomically. */
323
376
  provision(options?: ProvisionSessionOptions): SessionRecord;
377
+ /**
378
+ * Decide whether it is safe to destroy the just-created worktree/branch
379
+ * after a provisioning failure. Only an ordinary (non-durability-uncertain)
380
+ * failure, or a durability-uncertain failure whose registry re-read
381
+ * positively proves the session record absent, licenses a rollback.
382
+ *
383
+ * A durability-uncertain write means the rename may have already
384
+ * committed this session as the registry's visible ownership record:
385
+ * rolling back the worktree/branch would then strand a registry entry
386
+ * pointing at resources we just deleted. Absence must be *proven*, not
387
+ * merely unconfirmed — if the reconciliation read itself cannot complete
388
+ * (I/O failure, corrupt registry state, or any other exception), that is
389
+ * not proof of absence, so no rollback happens and the original
390
+ * durability-uncertain outcome propagates unchanged.
391
+ */
392
+ private absenceProvenAfterProvisioningFailure;
324
393
  provisionSession(options?: ProvisionSessionOptions): SessionRecord;
325
394
  createProvisionedSession(options?: ProvisionSessionOptions): SessionRecord;
326
395
  register(record: SessionRecord): SessionRecord;
@@ -336,6 +405,16 @@ export declare class SessionRegistry {
336
405
  private verifyEvidenceSession;
337
406
  /** Shared physical/protected/ownership path for every governed decision. */
338
407
  private verifyGovernedExecutionContext;
408
+ /**
409
+ * Distinguish the current (protected) command execution context from the
410
+ * referenced/target session, so a caller does not have to separately
411
+ * re-discover where the operation should actually run. `phase: "execution"`
412
+ * marks this live-command rejection distinctly from the cleanup-time
413
+ * PROTECTED_WORKTREE/PROTECTED_BRANCH block, which `safeActionsForCode`
414
+ * and `recoveryHintsForCode` key on for a different, non-mutating safe
415
+ * action set.
416
+ */
417
+ private protectedExecutionContextError;
339
418
  private resolveSessionOwnership;
340
419
  /**
341
420
  * Evaluate the current worktree as a Nawabari mutation context without taking
@@ -380,6 +459,14 @@ export declare class SessionRegistry {
380
459
  cleanupDecision(sessionIdOrOptions?: string | null | CloseSessionOptions): CleanupDecision;
381
460
  /** Compatibility alias for callers that name the same authority an assessment. */
382
461
  assessCleanup(sessionIdOrOptions?: string | null | CloseSessionOptions): CleanupDecision;
462
+ /**
463
+ * Side-effect-free close/cleanup readiness diagnostic for one session. This
464
+ * observes the exact same authoritative Git/session/claim truth `close()`
465
+ * uses (including the #123 non-ancestry integration proof contract when
466
+ * `integratedRevision` evidence is supplied) without mutating any session,
467
+ * claim, Git, or registry state.
468
+ */
469
+ diagnose(sessionIdOrOptions?: string | null | CloseSessionOptions): SessionDiagnostic;
383
470
  /**
384
471
  * Reconcile registry ownership with Git's physical worktree inventory.
385
472
  * This is deliberately diagnostic-only: no stale metadata is repaired and
@@ -402,15 +489,48 @@ export declare class SessionRegistry {
402
489
  private closeUnsafe;
403
490
  private inspectCleanupResources;
404
491
  private cleanupDecisionUnsafe;
492
+ private diagnoseUnsafe;
405
493
  private assertCleanupAllowed;
406
494
  private reconcileSessionUnsafe;
407
495
  private protectedWorktree;
408
496
  private protectedBranch;
497
+ /**
498
+ * Prove a session branch is safe to reclaim. Ordinary ancestry is the
499
+ * cheap/default path. When ancestry fails, a caller may supply bounded
500
+ * non-ancestry integration evidence (e.g. a squash/rebase-merge commit);
501
+ * Nawabari never trusts that assertion blindly and instead independently
502
+ * re-derives a deterministic, byte-exact Git tree-object equivalence proof
503
+ * before treating the branch as safely integrated.
504
+ */
409
505
  private branchIsReachableFromIntegration;
506
+ /**
507
+ * Independently verify caller-supplied non-ancestry integration evidence.
508
+ * The caller's assertion only selects which revision to compare; the
509
+ * proof itself is re-derived from local Git truth. For every path the
510
+ * session branch touched relative to its own base, the exact tree-object
511
+ * identity (file mode, object type, and content-addressed blob/tree SHA)
512
+ * at the branch head must match the same path's identity at the supplied
513
+ * integrated revision — both absent, or byte-identical. Blob SHAs are
514
+ * content hashes, so this can never mistake a whitespace-only or other
515
+ * textual difference for equivalence the way a patch/diff comparison
516
+ * could; it is exact Git tree semantics, not textual normalization.
517
+ * Returns undefined, never false, so a missing proof always falls through
518
+ * to a fail-closed RECOVERABLE_COMMITS at the call site.
519
+ */
520
+ private proveNonAncestryIntegration;
410
521
  private resolveProvisioningResources;
411
522
  private resolveCreationResources;
412
523
  private readStateUnsafe;
413
524
  private readUnsafe;
525
+ /**
526
+ * The single authoritative persistence path for session/claim registry
527
+ * state, sharing its durability policy with `registry/atomic.ts`: the
528
+ * temporary file and target directory are fsynced around an atomic
529
+ * rename, and only known unsupported-directory-fsync conditions are
530
+ * tolerated. A failure observed after rename is reported as a distinct
531
+ * durability-uncertain outcome rather than an ordinary write failure,
532
+ * since the renamed document may already be the one readers observe.
533
+ */
414
534
  private writeUnsafe;
415
535
  private mutate;
416
536
  private withLock;