@ricsam/r5d-worker 0.0.78 → 0.0.80

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 (58) hide show
  1. package/README.md +6 -0
  2. package/dist/cjs/main.cjs +1550 -335
  3. package/dist/cjs/managed-paths.cjs +101 -1
  4. package/dist/cjs/package.json +1 -1
  5. package/dist/cjs/project-workspace-state.cjs +184 -5
  6. package/dist/cjs/project-worktrees.cjs +676 -58
  7. package/dist/cjs/registry-auth.cjs +310 -0
  8. package/dist/cjs/repository-transition-policy.cjs +49 -0
  9. package/dist/cjs/supervisor.cjs +62 -11
  10. package/dist/cjs/working-tree-mirror.cjs +196 -36
  11. package/dist/cjs/workspace-automatic-sync-policy.cjs +69 -0
  12. package/dist/cjs/workspace-branch-incarnation-policy.cjs +37 -0
  13. package/dist/cjs/workspace-command-sync-policy.cjs +18 -0
  14. package/dist/cjs/workspace-git-sync.cjs +1037 -54
  15. package/dist/cjs/workspace-mount-boundary.cjs +71 -0
  16. package/dist/cjs/workspace-path-move.cjs +195 -0
  17. package/dist/cjs/workspace-preserve-only-policy.cjs +36 -0
  18. package/dist/cjs/workspace-project-config-policy.cjs +46 -0
  19. package/dist/cjs/workspace-publication-evidence.cjs +46 -0
  20. package/dist/mjs/main.mjs +1584 -341
  21. package/dist/mjs/managed-paths.mjs +100 -1
  22. package/dist/mjs/package.json +1 -1
  23. package/dist/mjs/project-workspace-state.mjs +181 -5
  24. package/dist/mjs/project-worktrees.mjs +667 -57
  25. package/dist/mjs/registry-auth.mjs +269 -0
  26. package/dist/mjs/repository-transition-policy.mjs +22 -0
  27. package/dist/mjs/supervisor.mjs +61 -11
  28. package/dist/mjs/working-tree-mirror.mjs +195 -36
  29. package/dist/mjs/workspace-automatic-sync-policy.mjs +40 -0
  30. package/dist/mjs/workspace-branch-incarnation-policy.mjs +13 -0
  31. package/dist/mjs/workspace-command-sync-policy.mjs +17 -0
  32. package/dist/mjs/workspace-git-sync.mjs +1032 -54
  33. package/dist/mjs/workspace-mount-boundary.mjs +37 -0
  34. package/dist/mjs/workspace-path-move.mjs +160 -0
  35. package/dist/mjs/workspace-preserve-only-policy.mjs +11 -0
  36. package/dist/mjs/workspace-project-config-policy.mjs +21 -0
  37. package/dist/mjs/workspace-publication-evidence.mjs +21 -0
  38. package/dist/types/credential-authority-lock-fixture.d.ts +1 -0
  39. package/dist/types/main.d.ts +175 -1
  40. package/dist/types/managed-paths.d.ts +11 -0
  41. package/dist/types/project-workspace-state.d.ts +45 -1
  42. package/dist/types/project-worktrees.d.ts +119 -2
  43. package/dist/types/registry-auth.d.ts +47 -0
  44. package/dist/types/repository-transition-policy.d.ts +26 -0
  45. package/dist/types/supervisor-daemonized-fixture.d.ts +1 -0
  46. package/dist/types/supervisor-signal-fixture.d.ts +1 -0
  47. package/dist/types/supervisor.d.ts +6 -4
  48. package/dist/types/working-tree-mirror.d.ts +14 -0
  49. package/dist/types/workspace-automatic-sync-policy.d.ts +37 -0
  50. package/dist/types/workspace-branch-incarnation-policy.d.ts +14 -0
  51. package/dist/types/workspace-command-sync-policy.d.ts +9 -0
  52. package/dist/types/workspace-git-sync.d.ts +33 -3
  53. package/dist/types/workspace-mount-boundary.d.ts +10 -0
  54. package/dist/types/workspace-path-move.d.ts +21 -0
  55. package/dist/types/workspace-preserve-only-policy.d.ts +15 -0
  56. package/dist/types/workspace-project-config-policy.d.ts +34 -0
  57. package/dist/types/workspace-publication-evidence.d.ts +17 -0
  58. package/package.json +1 -1
@@ -1,5 +1,19 @@
1
1
  export declare const PROJECT_WORKTREE_SNAPSHOT_PREFIX = "r5d-project-worktrees-";
2
+ declare const PROJECT_WORKTREE_SNAPSHOT_MANIFEST_VERSION: 1;
3
+ type ProjectWorktreeSnapshotManifest = {
4
+ version: typeof PROJECT_WORKTREE_SNAPSHOT_MANIFEST_VERSION;
5
+ state: "building" | "prepared" | "consumed";
6
+ ownerProcessId: number;
7
+ ownerSessionId: string;
8
+ projectRoot: string;
9
+ branches: Array<{
10
+ branchName: string;
11
+ snapshotPresent: boolean;
12
+ }>;
13
+ createdAt: string;
14
+ };
2
15
  export type ProjectWorktreeBranch = {
16
+ branchId?: string;
3
17
  branchName: string;
4
18
  baseCommitHash: string;
5
19
  sourceBranchName?: string | null;
@@ -12,12 +26,30 @@ export type ProjectWorktreeState = {
12
26
  ahead: number | null;
13
27
  behind: number | null;
14
28
  };
29
+ /**
30
+ * Creation failed after its branch ref may have been written, and rollback
31
+ * could not prove that every local artifact was removed. Callers must retain
32
+ * their durable creation intent so a later cleanup can recover it.
33
+ */
34
+ export declare class ProjectBranchCreationRollbackIncompleteError extends Error {
35
+ readonly branchMayExist: true;
36
+ readonly cleanupFailures: readonly string[];
37
+ constructor(branchName: string, cleanupFailures: readonly string[], cause: unknown);
38
+ }
39
+ export declare function projectBranchMayExistAfterCreateFailure(error: unknown): boolean;
15
40
  export declare function projectWorktreeConfigurationFingerprint(input: {
16
41
  checkoutPathSegments: readonly string[];
17
42
  projectPath: string;
18
43
  repoHttpUrl: string | null;
19
44
  repoAuthHeader: string | null;
20
45
  defaultBranch: string;
46
+ repositoryTransitionId?: string | null;
47
+ executionDisabled?: boolean;
48
+ mirrorWritesDisabled?: boolean;
49
+ preserveOnlyBranches?: readonly {
50
+ branchId?: string;
51
+ branchName: string;
52
+ }[];
21
53
  branches: readonly ProjectWorktreeBranch[];
22
54
  gitIdentity: {
23
55
  name: string;
@@ -30,19 +62,31 @@ export declare function hasLinkedProjectWorktreeLayout(input: {
30
62
  primaryBranchName: string;
31
63
  branches: readonly Pick<ProjectWorktreeBranch, "branchName">[];
32
64
  }): boolean;
33
- export declare function cleanupStaleProjectWorktreeSnapshots(input?: {
65
+ export declare function recoverStaleProjectWorktreeSnapshots(input: {
66
+ projectsRoot: string;
34
67
  temporaryRoot?: string;
35
68
  processAlive?: (processId: number) => boolean;
69
+ currentProcessId?: number;
70
+ currentOwnerSessionId?: string;
36
71
  }): {
72
+ restored: string[];
37
73
  removed: string[];
38
74
  failed: Array<{
39
75
  path: string;
40
76
  error: string;
41
77
  }>;
42
78
  };
79
+ export declare function projectOriginBranchName(input: {
80
+ branchName: string;
81
+ primaryBranchName: string;
82
+ defaultBranch: string;
83
+ }): string;
43
84
  declare function configureRepository(input: {
44
85
  primaryPath: string;
45
86
  originUrl: string;
87
+ primaryBranchName?: string;
88
+ defaultBranch?: string;
89
+ branches?: readonly Pick<ProjectWorktreeBranch, "branchName">[];
46
90
  credentialHelper?: string | null;
47
91
  originCredentialUsername?: string | null;
48
92
  gitIdentity?: {
@@ -53,6 +97,7 @@ declare function configureRepository(input: {
53
97
  export declare function ensureProjectWorktrees(input: {
54
98
  projectRoot: string;
55
99
  primaryBranchName: string;
100
+ defaultBranch?: string;
56
101
  branches: readonly ProjectWorktreeBranch[];
57
102
  originUrl: string;
58
103
  mirrorUrl: string;
@@ -63,6 +108,8 @@ export declare function ensureProjectWorktrees(input: {
63
108
  name: string;
64
109
  email: string;
65
110
  } | null;
111
+ exactMirrorBranches?: ReadonlySet<string>;
112
+ snapshotRoot?: string;
66
113
  }): ProjectWorktreeState[];
67
114
  export declare function projectWorktreeOperationInProgress(checkoutPath: string): boolean;
68
115
  export declare function createLinkedProjectBranch(input: {
@@ -73,6 +120,22 @@ export declare function createLinkedProjectBranch(input: {
73
120
  branchPath: string;
74
121
  baseCommitHash: string;
75
122
  };
123
+ /**
124
+ * A retained pending intent is ownership proof for artifacts left by an
125
+ * interrupted earlier attempt. Remove those artifacts idempotently before
126
+ * recreating; if cleanup still fails, the caller keeps the same intent and can
127
+ * retry again later.
128
+ */
129
+ export declare function createOrRetryLinkedProjectBranch(input: {
130
+ projectRoot: string;
131
+ primaryBranchName: string;
132
+ sourceBranchName: string;
133
+ branchName: string;
134
+ pendingRetry: boolean;
135
+ }): {
136
+ branchPath: string;
137
+ baseCommitHash: string;
138
+ };
76
139
  export declare function deleteLinkedProjectBranch(input: {
77
140
  projectRoot: string;
78
141
  primaryBranchName: string;
@@ -98,27 +161,81 @@ export declare function pushProjectMirrorHeads(input: {
98
161
  credentialHelper?: string | null;
99
162
  credentialUsername?: string | null;
100
163
  onlyBranches?: ReadonlySet<string>;
164
+ /** Exact pre-projection commits to publish after the outer tree is reachable. */
165
+ publicationHeads?: ReadonlyMap<string, string>;
101
166
  }): Array<{
102
167
  branchName: string;
103
168
  head: string;
104
169
  pushed: boolean;
105
170
  reason?: string;
106
171
  }>;
107
- export declare function fastForwardProjectHeadsFromMirror(input: {
172
+ export declare function assertProjectMirrorHeadsPushed(results: readonly {
173
+ branchName: string;
174
+ pushed: boolean;
175
+ reason?: string;
176
+ }[]): void;
177
+ type ProjectMirrorHeadUpdateInput = {
108
178
  projectRoot: string;
109
179
  primaryBranchName: string;
110
180
  branchNames: readonly string[];
111
181
  mirrorUrl: string;
112
182
  credentialHelper?: string | null;
113
183
  credentialUsername?: string | null;
184
+ /** Rechecked after fetch and ancestry inspection, immediately before refs or the index move. */
185
+ branchMutationAllowed?: (branchName: string, checkoutPath: string) => boolean;
186
+ };
187
+ export type ProjectMirrorHeadObservation = {
188
+ branchName: string;
189
+ previousHead: string;
190
+ mirrorHead: string | null;
191
+ mode: "fast_forward" | "reseed";
192
+ shouldMove: boolean;
193
+ };
194
+ /**
195
+ * Fetch and pin the exact hidden-mirror tips that may be applied after a
196
+ * causally later outer-workspace fetch and hydration. Observation deliberately
197
+ * does not mutate a live branch or its index.
198
+ */
199
+ export declare function observeProjectMirrorHeads(input: Omit<ProjectMirrorHeadUpdateInput, "branchMutationAllowed"> & {
200
+ allowNonFastForward: boolean;
201
+ }): ProjectMirrorHeadObservation[];
202
+ /** Apply only previously observed object ids; this function never refetches. */
203
+ export declare function applyObservedProjectMirrorHeads(input: {
204
+ projectRoot: string;
205
+ primaryBranchName: string;
206
+ observations: readonly ProjectMirrorHeadObservation[];
207
+ /** Rechecked immediately before refs or the index move. */
208
+ branchMutationAllowed?: (branchName: string, checkoutPath: string) => boolean;
114
209
  }): Array<{
210
+ branchName: string;
211
+ previousHead: string;
212
+ mirrorHead: string | null;
213
+ moved: boolean;
214
+ }>;
215
+ export declare function fastForwardProjectHeadsFromMirror(input: ProjectMirrorHeadUpdateInput): Array<{
115
216
  branchName: string;
116
217
  previousHead: string;
117
218
  mirrorHead: string | null;
118
219
  fastForwarded: boolean;
119
220
  }>;
221
+ export declare function reseedProjectHeadsFromMirror(input: ProjectMirrorHeadUpdateInput): Array<{
222
+ branchName: string;
223
+ previousHead: string;
224
+ mirrorHead: string | null;
225
+ reseeded: boolean;
226
+ }>;
120
227
  export declare const projectWorktreesTestHarness: {
121
228
  commandArgs: typeof gitCommandArgs;
122
229
  configureRepository: typeof configureRepository;
230
+ createPreparedSnapshot(input: {
231
+ projectRoot: string;
232
+ branches: readonly ProjectWorktreeBranch[];
233
+ temporaryRoot: string;
234
+ }): {
235
+ root: string;
236
+ };
237
+ consumeSnapshot(snapshotRoot: string): void;
238
+ persistProjectAndConsumeSnapshot(snapshotRoot: string, afterProjectTreeFsync?: () => void): void;
239
+ snapshotState(snapshotRoot: string): ProjectWorktreeSnapshotManifest["state"];
123
240
  };
124
241
  export {};
@@ -0,0 +1,47 @@
1
+ export declare const APP_MANAGED_GITHUB_REGISTRY: "ghcr.io";
2
+ export type GitHubRegistryCredential = {
3
+ registry: typeof APP_MANAGED_GITHUB_REGISTRY;
4
+ username: string;
5
+ token: string;
6
+ };
7
+ export declare class RegistryAuthConfigurationError extends Error {
8
+ constructor(cause: unknown);
9
+ }
10
+ export declare function registryAuthHasAppManagedGitHubCredential(filePath: string): boolean;
11
+ type DirectoryFsyncDependencies = {
12
+ platform: NodeJS.Platform;
13
+ open: (directory: string) => number;
14
+ fsync: (descriptor: number) => void;
15
+ close: (descriptor: number) => void;
16
+ };
17
+ declare function fsyncDirectory(directory: string, dependencies?: DirectoryFsyncDependencies): void;
18
+ export type PrivateAuthFileUpdate = {
19
+ filePath: string;
20
+ content: string | null;
21
+ allowSymlinkRemoval?: boolean;
22
+ };
23
+ export type PreparedPrivateAuthFileGeneration = {
24
+ readonly changed: boolean;
25
+ commit(beforeMutation?: (filePath: string, index: number) => void): void;
26
+ discard(): void;
27
+ };
28
+ /**
29
+ * Validate and fsync every replacement before exposing the first new auth
30
+ * byte. Cross-file publication is necessarily sequential, so callers must
31
+ * fence/terminate the previous process generation before commit and treat any
32
+ * commit error as a fatal partial-generation boundary.
33
+ */
34
+ export declare function preparePrivateAuthFileGeneration(updates: readonly PrivateAuthFileUpdate[]): PreparedPrivateAuthFileGeneration;
35
+ export declare function planGitHubRegistryAuthFiles(filePaths: readonly string[], credential: GitHubRegistryCredential | null): PrivateAuthFileUpdate[];
36
+ /**
37
+ * Authoritatively replace the app-owned ghcr.io entry while retaining every
38
+ * unrelated Docker/containers auth entry and top-level setting.
39
+ */
40
+ export declare function updateGitHubRegistryAuthFile(filePath: string, credential: GitHubRegistryCredential | null): void;
41
+ export declare function configureGitHubRegistryAuthFiles(filePaths: readonly string[], credential: GitHubRegistryCredential | null): void;
42
+ export declare const registryAuthTestHarness: {
43
+ fsyncDirectory: typeof fsyncDirectory;
44
+ atomicReplacePrivateFile(filePath: string, content: string, beforeRename?: () => void): void;
45
+ configureFiles(filePaths: readonly string[], credential: GitHubRegistryCredential | null, beforeRename: (filePath: string, index: number) => void): void;
46
+ };
47
+ export {};
@@ -0,0 +1,26 @@
1
+ export type WorkerRepositoryTransitionState = {
2
+ repositoryTransitionId: string | null;
3
+ executionDisabled: boolean;
4
+ mirrorWritesDisabled: boolean;
5
+ };
6
+ export type LastEnabledRepositoryTransition = {
7
+ known: boolean;
8
+ transitionId: string | null;
9
+ };
10
+ export declare function assertRepositoryTransitionState(project: WorkerRepositoryTransitionState & {
11
+ projectId: string;
12
+ }): void;
13
+ /** Disabled repositories retain their local state but expose no execution root. */
14
+ export declare function assertRepositoryExecutionEnabled(project: Pick<WorkerRepositoryTransitionState, "executionDisabled"> & {
15
+ projectId: string;
16
+ }): void;
17
+ /** Every hidden-mirror ref mutation, including deletion, uses this fence. */
18
+ export declare function repositoryMirrorWritesAllowed(project: Pick<WorkerRepositoryTransitionState, "executionDisabled" | "mirrorWritesDisabled"> | undefined): boolean;
19
+ /**
20
+ * A disabled snapshot never consumes the reseed obligation. Missing durable
21
+ * history is treated conservatively after restart and reseeded once enabled.
22
+ */
23
+ export declare function enabledRepositoryTransitionRequiresReseed(input: {
24
+ project: Pick<WorkerRepositoryTransitionState, "repositoryTransitionId" | "executionDisabled">;
25
+ lastEnabled: LastEnabledRepositoryTransition;
26
+ }): boolean;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -1,17 +1,19 @@
1
+ import { type KillableSubprocess } from "./process-tree";
1
2
  export declare const WORKER_RUNTIME_ENV = "R5D_WORKER_INTERNAL_RUNTIME";
2
3
  export declare const WORKER_RELOAD_EXIT_CODE = 75;
3
4
  export declare const WORKER_RECONNECT_EXIT_CODE = 76;
5
+ export declare const WORKER_CREDENTIAL_RESTART_EXIT_CODE = 77;
4
6
  export declare const WORKER_RECONNECT_DELAY_MS = 2000;
5
- type RuntimeProcess = {
6
- exited: Promise<number>;
7
- };
7
+ type RuntimeProcess = KillableSubprocess;
8
8
  export type RuntimeSpawner = (argv: string[], options: {
9
9
  stdin: "inherit";
10
10
  stdout: "inherit";
11
11
  stderr: "inherit";
12
+ detached: true;
12
13
  env: Record<string, string | undefined>;
13
14
  }) => RuntimeProcess;
14
15
  export type RuntimeDelay = (delayMs: number) => Promise<void>;
16
+ export type RuntimeReaper = (runtime: RuntimeProcess) => Promise<void>;
15
17
  export declare function isRetryableWorkerServerStatus(status: number): boolean;
16
- export declare function superviseWorkerRuntime(argv: string[], env?: Record<string, string | undefined>, spawnRuntime?: RuntimeSpawner, waitBeforeReconnect?: RuntimeDelay): Promise<number>;
18
+ export declare function superviseWorkerRuntime(argv: string[], env?: Record<string, string | undefined>, spawnRuntime?: RuntimeSpawner, waitBeforeReconnect?: RuntimeDelay, reapCredentialRuntime?: RuntimeReaper): Promise<number>;
17
19
  export {};
@@ -1,5 +1,6 @@
1
1
  export type WorkingTreeSourceMode = "all" | "git";
2
2
  export type WorkingTreeDeletionMode = "all" | "git";
3
+ export type WorkingTreeMirrorDurability = "per_entry" | "deferred_private_staging";
3
4
  type TreeEntry = {
4
5
  kind: "directory";
5
6
  mode: number;
@@ -12,12 +13,25 @@ type TreeEntry = {
12
13
  mode: number;
13
14
  target: string;
14
15
  };
16
+ /**
17
+ * Destructive native-tree moves cannot silently apply the Git-target filter:
18
+ * doing so would delete legitimate case-sensitive POSIX filenames after the
19
+ * filtered copy. Exact native `.git` metadata is intentionally disposable;
20
+ * every portable alias must be remediated before the old tree is retired.
21
+ */
22
+ export declare function assertWorkingTreeHasNoPortableGitMetadataAliases(root: string): void;
15
23
  export declare function inspectWorkingTree(root: string, mode: WorkingTreeSourceMode): Map<string, TreeEntry>;
16
24
  export declare function mirrorWorkingTree(input: {
17
25
  sourceRoot: string;
18
26
  targetRoot: string;
19
27
  sourceMode: WorkingTreeSourceMode;
20
28
  deletionMode: WorkingTreeDeletionMode;
29
+ /**
30
+ * Defer destination fsync only while building an unpublished private tree.
31
+ * The caller must durably fsync that complete tree before publishing it or
32
+ * mutating the source authority.
33
+ */
34
+ durability?: WorkingTreeMirrorDurability;
21
35
  }): {
22
36
  paths: string[];
23
37
  };
@@ -0,0 +1,37 @@
1
+ type ProjectTarget = {
2
+ type: "project";
3
+ projectId: string;
4
+ branchName: string;
5
+ };
6
+ type WorkspaceTarget = {
7
+ type: "workspace";
8
+ rootProfile: "visible_projects" | "canonical_sync";
9
+ };
10
+ type PendingCreatedBranch = {
11
+ projectId: string;
12
+ branchName: string;
13
+ };
14
+ export declare const PENDING_CREATED_BRANCH_AUTOMATIC_SYNC_GRACE_MS = 120000;
15
+ export type PendingCreatedBranchAutomaticSyncDeferral = {
16
+ kind: "active_target";
17
+ } | {
18
+ kind: "creation_grace";
19
+ retryAfterMs: number;
20
+ };
21
+ export declare function pendingCreatedBranchKey(projectId: string, branchName: string): string;
22
+ export declare function hasActiveVisibleProjectsWorkspaceTarget(activeTargets: Iterable<ProjectTarget | WorkspaceTarget>): boolean;
23
+ export declare function projectBranchHasActiveWorkspaceTarget(projectId: string, branchName: string, activeTargets: Iterable<ProjectTarget | WorkspaceTarget>): boolean;
24
+ /**
25
+ * A workspace sync is global and synchronous enough to monopolize the worker
26
+ * event loop. Keep automatic intent pending while an agent process is active
27
+ * on a creator-local branch, so that process can issue its next command. Its
28
+ * terminal trigger (or a later periodic/connect cycle) publishes afterward.
29
+ */
30
+ export declare function shouldDeferAutomaticWorkspaceSyncForPendingBranch(pendingCreatedBranches: readonly PendingCreatedBranch[], activeTargets: Iterable<ProjectTarget | WorkspaceTarget>): boolean;
31
+ /**
32
+ * Automatic publication yields briefly after local branch creation so the
33
+ * server can durably record the branch and reserve its first agent process.
34
+ * Explicit/reconnect synchronization does not use this policy.
35
+ */
36
+ export declare function pendingCreatedBranchAutomaticSyncDeferral(pendingCreatedBranches: readonly PendingCreatedBranch[], activeTargets: Iterable<ProjectTarget | WorkspaceTarget>, publicationNotBeforeByBranch: ReadonlyMap<string, number>, nowMs?: number): PendingCreatedBranchAutomaticSyncDeferral | null;
37
+ export {};
@@ -0,0 +1,14 @@
1
+ type BranchIncarnation = {
2
+ branchId?: string;
3
+ };
4
+ /**
5
+ * Deletion is idempotent only while no same-name state belongs to another
6
+ * durable row. Call this inside the workspace mutation lease, before creating
7
+ * a tombstone or touching a checkout/ref.
8
+ */
9
+ export declare function assertProjectBranchDeletionIncarnation(input: {
10
+ requiredBranchId: string;
11
+ configuredBranch?: BranchIncarnation;
12
+ pendingLocalBranch?: BranchIncarnation;
13
+ }): void;
14
+ export {};
@@ -8,6 +8,15 @@ type WorkspaceCommandMutationCoordinator = {
8
8
  runMutation<T>(operation: () => Promise<T> | T): Promise<T>;
9
9
  acquireMutation(): Promise<() => void>;
10
10
  };
11
+ type WorkspaceSyncCompletionBarrier = {
12
+ afterCurrent(): Promise<void>;
13
+ };
14
+ /**
15
+ * A visible command must not become active halfway through an outer sync. Wait
16
+ * for the current cycle, then register the target without another await so a
17
+ * later cycle observes the reservation and defers before integration.
18
+ */
19
+ export declare function reserveWorkspaceCommandAfterCurrentSync(target: WorkspaceCommandTarget, coordinator: WorkspaceSyncCompletionBarrier, reserve: () => void): Promise<void>;
11
20
  /**
12
21
  * Commands in visible checkouts behave like independent Git clients and may
13
22
  * overlap periodic synchronization. Canonical remediation commands mutate the
@@ -2,15 +2,35 @@ import { type WorkingTreeSourceMode } from "./working-tree-mirror";
2
2
  export declare const WORKSPACE_GIT_BRANCH = "main";
3
3
  export declare const MAX_WORKSPACE_GIT_DIFF_BYTES: number;
4
4
  export declare const WORKSPACE_GIT_CONFIRMED_LARGE_DIFF_PUSH_OPTION = "r5d-confirm-large-diff-v1";
5
+ export declare const WORKSPACE_GIT_HYDRATED_RECEIPT = "r5d/workspace-hydrated-head";
6
+ export declare const WORKSPACE_GIT_HYDRATION_TRANSACTION = "r5d/workspace-hydration-transaction";
7
+ export declare const WORKSPACE_GIT_CHECKOUT_DURABILITY = "r5d/workspace-checkout-durability";
5
8
  export type WorkspaceGitMount = {
6
9
  id: string;
10
+ /** Durable logical incarnation (for example the project-branch row id). */
11
+ hydrationIncarnationKey: string;
7
12
  sourcePath: string;
13
+ /** Existing managed root through which hydrated source directory entries are durably published. */
14
+ durabilityRootPath: string;
8
15
  workspaceRelativePath: string;
9
16
  sourceMode: WorkingTreeSourceMode;
10
17
  hydrateDeletionMode: "all" | "git";
18
+ /**
19
+ * Only an exact creator-local unpublished mount may treat an absent outer
20
+ * subtree as bootstrap authority before this clone has a hydration receipt.
21
+ * Published/active mounts treat absence as a canonical deletion.
22
+ */
23
+ preserveLocalOnInitialOuterAbsence?: boolean;
24
+ /**
25
+ * Preserve a durably copied local tree across an exact checkout-path move.
26
+ * This is transient and must clear after that move is published.
27
+ */
28
+ preserveLocalOnHydrationBasisChange?: boolean;
11
29
  /** Remove the outer-workspace subtree when the mounted source was intentionally deleted. */
12
30
  deleteWhenSourceMissing?: boolean;
13
31
  busy?: () => boolean;
32
+ /** Activity-only fence used before checkout readiness is established on reconnect. */
33
+ busyForRecovery?: () => boolean;
14
34
  };
15
35
  export type WorkspaceGitSyncOutcome = "no_change" | "updated" | "pushed" | "large_diff_blocked" | "conflict_blocked";
16
36
  export type WorkspaceGitSyncResult = {
@@ -34,6 +54,7 @@ export type WorkspaceGitSyncResult = {
34
54
  };
35
55
  declare function gitCommandArgs(args: string[]): string[];
36
56
  declare function workspaceCloneCommandArgs(args: string[], remoteUrl: string, credentialHelper?: string | null, credentialUsername?: string | null): string[];
57
+ export declare function workspaceGitHydrationIsCurrent(workspacePath: string, mounts?: readonly WorkspaceGitMount[]): boolean;
37
58
  declare function configureWorkspaceRepository(input: {
38
59
  workspacePath: string;
39
60
  remoteUrl: string;
@@ -58,7 +79,16 @@ export declare function ensureWorkspaceGitClone(input: {
58
79
  remoteHead: string | null;
59
80
  };
60
81
  declare function mirrorMountsToWorkspace(workspacePath: string, mounts: readonly WorkspaceGitMount[]): void;
61
- export declare function hydrateWorkspaceGitMounts(workspacePath: string, mounts: readonly WorkspaceGitMount[]): void;
82
+ export declare function hydrateWorkspaceGitMounts(workspacePath: string, mounts: readonly WorkspaceGitMount[], options?: {
83
+ ignoreBusy?: boolean;
84
+ }): {
85
+ hydratedMountIds: string[];
86
+ skippedMountIds: string[];
87
+ };
88
+ export declare function recoverWorkspaceGitHydration(workspacePath: string, mounts: readonly WorkspaceGitMount[], options?: {
89
+ ignoreBusy?: boolean;
90
+ deferMountIds?: ReadonlySet<string>;
91
+ }): void;
62
92
  export declare function resetWorkspaceGit(input: {
63
93
  workspacePath: string;
64
94
  remoteUrl: string;
@@ -74,6 +104,8 @@ export declare function resetWorkspaceGit(input: {
74
104
  localHead: string | null;
75
105
  remoteHead: string | null;
76
106
  discardedPaths: string[];
107
+ activeMountIds: string[];
108
+ skippedMountIds: string[];
77
109
  };
78
110
  export declare function synchronizeWorkspaceGit(input: {
79
111
  attemptId?: string;
@@ -93,8 +125,6 @@ export declare function synchronizeWorkspaceGit(input: {
93
125
  maxPushAttempts?: number;
94
126
  /** Publish edits already made in the outer clone without first projecting visible mounts over them. */
95
127
  skipMountMirror?: boolean;
96
- /** Leave visible mounts untouched after publication (used by remediation commands in the outer clone). */
97
- skipMountHydration?: boolean;
98
128
  afterWorkspacePublished?: (context: {
99
129
  publishedHead: string;
100
130
  activeMountIds: string[];
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Validate every existing path component from a trusted root to a managed
3
+ * directory without following symlinks. Missing suffixes are allowed because
4
+ * hydration and path moves create them after this check.
5
+ */
6
+ export declare function assertManagedDirectoryPath(input: {
7
+ trustedRoot: string;
8
+ candidate: string;
9
+ label: string;
10
+ }): void;
@@ -0,0 +1,21 @@
1
+ export type ProjectCheckoutPathMoveBranch = {
2
+ branchName: string;
3
+ outerWorkspaceRelativePath: string;
4
+ };
5
+ export declare function checkoutPathMovePublicationComplete(requiredMountIds: readonly string[], activeMountIds: ReadonlySet<string>): boolean;
6
+ /**
7
+ * Move the project checkout location without making its old root the only copy
8
+ * of dirty or ignored working state. The canonical outer tree receives the
9
+ * Git-visible projection, while the new root receives every non-.git byte so
10
+ * repository activation can reconstruct Git metadata without losing local
11
+ * files. Both copies are stable before the obsolete worktree root is removed.
12
+ */
13
+ export declare function preserveProjectCheckoutPathMove(input: {
14
+ oldProjectRoot: string;
15
+ newProjectRoot: string;
16
+ projectsDurabilityRoot: string;
17
+ outerWorkspaceRoot: string;
18
+ branches: readonly ProjectCheckoutPathMoveBranch[];
19
+ }): {
20
+ preservedBranchNames: string[];
21
+ };
@@ -0,0 +1,15 @@
1
+ export type WorkerProjectBranchDisposition = "active" | "creator_local" | "preserve_only";
2
+ /** Name equality never grants creator authority across branch incarnations. */
3
+ export declare function creatorLocalProjectBranchIsAuthorized(input: {
4
+ pendingBranchId: string | undefined;
5
+ preserveOnlyBranchId: string | undefined;
6
+ }): boolean;
7
+ /**
8
+ * A globally pending branch is executable only on the worker that durably
9
+ * recorded its local creation. Every other worker treats the same protocol
10
+ * entry as preservation metadata, never as a checkout target.
11
+ */
12
+ export declare function workerProjectBranchDisposition(input: {
13
+ activeConfigured: boolean;
14
+ locallyPendingCreated: boolean;
15
+ }): WorkerProjectBranchDisposition;
@@ -0,0 +1,34 @@
1
+ type ProjectTarget = {
2
+ type: "project";
3
+ projectId: string;
4
+ };
5
+ type WorkspaceTarget = {
6
+ type: "workspace";
7
+ rootProfile: "visible_projects" | "canonical_sync";
8
+ };
9
+ type ProjectConfiguration = {
10
+ projectId: string;
11
+ };
12
+ type ProjectConfigurationWithBranches = ProjectConfiguration & {
13
+ branches: readonly {
14
+ branchName: string;
15
+ }[];
16
+ };
17
+ export declare function deferredProjectConfigurationPendingBranches(projects: readonly ProjectConfigurationWithBranches[]): Array<{
18
+ projectId: string;
19
+ branchName: string;
20
+ }>;
21
+ /**
22
+ * Identify configuration changes that would touch a project while visible
23
+ * checkout activity is active or reserved. A visible-projects target covers
24
+ * every project; canonical-sync activity is isolated in the outer clone.
25
+ */
26
+ export declare function busyProjectConfigurationChangeIds<TProject extends ProjectConfiguration>(input: {
27
+ currentProjects: readonly TProject[];
28
+ incomingProjects: readonly TProject[];
29
+ activeTargets: Iterable<ProjectTarget | WorkspaceTarget>;
30
+ currentFingerprint: (project: TProject) => string;
31
+ incomingFingerprint: (project: TProject) => string;
32
+ currentProjectReady: (project: TProject) => boolean;
33
+ }): string[];
34
+ export {};
@@ -0,0 +1,17 @@
1
+ export type ProjectBranchPublicationEvidence = {
2
+ branchId: string;
3
+ projectId: string;
4
+ branchName: string;
5
+ };
6
+ /** Select only branch mounts that completed this synchronization cycle. */
7
+ export declare function activeProjectBranchNames(projectId: string, branchNames: readonly string[], activeMountIds: readonly string[]): string[];
8
+ /** Bind physical active mounts to the exact configured branch-row snapshot. */
9
+ export declare function activeProjectBranchPublicationEvidence(projects: readonly {
10
+ projectId: string;
11
+ executionDisabled?: boolean;
12
+ mirrorWritesDisabled?: boolean;
13
+ branches: readonly {
14
+ branchId: string;
15
+ branchName: string;
16
+ }[];
17
+ }[], activeMountIds: readonly string[]): ProjectBranchPublicationEvidence[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ricsam/r5d-worker",
3
- "version": "0.0.78",
3
+ "version": "0.0.80",
4
4
  "type": "module",
5
5
  "main": "./dist/cjs/main.cjs",
6
6
  "module": "./dist/mjs/main.mjs",