mrplex 0.0.2 → 0.1.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.
@@ -72,6 +72,49 @@ export function loadPolicyFile(path) {
72
72
  }
73
73
  return parsePolicy(text);
74
74
  }
75
+ /**
76
+ * Render a minimal starter policy. Pure — no I/O. Always parses via
77
+ * `parsePolicy`. Two principals: `admin` (operator — create/delete repos) and
78
+ * the named maintainer (MCP day-to-day — no structural admin).
79
+ */
80
+ export function scaffoldPolicyYaml(opts) {
81
+ const principal = opts.principal.trim();
82
+ const author = opts.author.trim();
83
+ if (!principal)
84
+ throw new PolicyError("scaffold principal id must be non-empty");
85
+ if (!author)
86
+ throw new PolicyError("scaffold author must be non-empty");
87
+ // Keep the template free of quoting footguns: principal ids that aren't
88
+ // plain YAML barewords would need escaping in the rendered file.
89
+ if (!/^[A-Za-z][A-Za-z0-9_-]*$/.test(principal)) {
90
+ throw new PolicyError(`scaffold principal id "${principal}" must start with a letter and contain only letters, digits, _ and -`);
91
+ }
92
+ if (principal === "admin") {
93
+ throw new PolicyError('scaffold principal id cannot be "admin" (reserved for the operator principal)');
94
+ }
95
+ // JSON string literals are valid YAML double-quoted scalars — handles spaces
96
+ // and angle brackets in "Name <email>" authors without a hand-rolled escape.
97
+ const authorYaml = JSON.stringify(author);
98
+ return `roles:
99
+ maintainer:
100
+ grants: [{ repo: "*", read: "**", write: "**" }]
101
+ maintain: true
102
+ operator:
103
+ grants: [{ repo: "*", read: "**", write: "**" }]
104
+ destructive: true
105
+
106
+ # author: prefer "Full Name <email@domain>" (git-style) — stamped on writes
107
+ principals:
108
+ admin:
109
+ author: ${authorYaml}
110
+ roles: [operator]
111
+ keys: []
112
+ ${principal}:
113
+ author: ${authorYaml}
114
+ roles: [maintainer]
115
+ keys: []
116
+ `;
117
+ }
75
118
  function validateGrant(g, where) {
76
119
  if (!isObject(g))
77
120
  throw new PolicyError(`${where} must be a mapping`);
@@ -106,6 +149,9 @@ function validateRoles(raw) {
106
149
  }
107
150
  const grantsRaw = (value.grants ?? []);
108
151
  const grants = grantsRaw.map((g, i) => validateGrant(g, `${where}.grants[${i}]`));
152
+ if (value.maintain !== undefined && typeof value.maintain !== "boolean") {
153
+ throw new PolicyError(`${where}.maintain must be a boolean`);
154
+ }
109
155
  if (value.destructive !== undefined && typeof value.destructive !== "boolean") {
110
156
  throw new PolicyError(`${where}.destructive must be a boolean`);
111
157
  }
@@ -113,6 +159,8 @@ function validateRoles(raw) {
113
159
  throw new PolicyError(`${where}.impersonate must be a boolean`);
114
160
  }
115
161
  const role = { grants };
162
+ if (value.maintain !== undefined)
163
+ role.maintain = value.maintain;
116
164
  if (value.destructive !== undefined)
117
165
  role.destructive = value.destructive;
118
166
  if (value.impersonate !== undefined)
@@ -190,7 +238,9 @@ function validatePrincipals(raw, roles) {
190
238
  * Resolve a principal to its `Entitlement` — the union of its roles' grants and
191
239
  * op booleans. Pure: no I/O, no clock, no globals. A principal's read/write
192
240
  * claim lists are the concatenation of every role's grants (union semantics,
193
- * §8.2); `destructive`/`impersonate` are OR'd across roles.
241
+ * §8.2); `maintain`/`destructive`/`impersonate` are OR'd across roles.
242
+ * `destructive` always implies `maintain` (structural admin may also press
243
+ * the maintenance buttons).
194
244
  *
195
245
  * `author` is `principal.author` when set. For an OIDC principal with no static
196
246
  * author, the front derives `name <email>` from claims and passes it as
@@ -204,6 +254,7 @@ export function compile(policy, principalId, derivedAuthor) {
204
254
  }
205
255
  const read = [];
206
256
  const write = [];
257
+ let maintain = false;
207
258
  let destructive = false;
208
259
  let impersonate = false;
209
260
  for (const roleName of principal.roles) {
@@ -212,6 +263,8 @@ export function compile(policy, principalId, derivedAuthor) {
212
263
  // hand-built Policy object can't slip an unknown role past compile.
213
264
  if (!role)
214
265
  throw new PolicyError(`principal "${principalId}" references unknown role "${roleName}"`);
266
+ if (role.maintain)
267
+ maintain = true;
215
268
  if (role.destructive)
216
269
  destructive = true;
217
270
  if (role.impersonate)
@@ -223,10 +276,12 @@ export function compile(policy, principalId, derivedAuthor) {
223
276
  write.push({ repo: grant.repo, paths: grant.write });
224
277
  }
225
278
  }
279
+ if (destructive)
280
+ maintain = true;
226
281
  const author = principal.author ?? derivedAuthor;
227
282
  if (author === undefined) {
228
283
  throw new PolicyError(`principal "${principalId}" has no static author and none was derived (OIDC login supplies it)`);
229
284
  }
230
- return { author, read, write, destructive, impersonate };
285
+ return { author, read, write, maintain, destructive, impersonate };
231
286
  }
232
287
  //# sourceMappingURL=policy.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"policy.js","sourceRoot":"","sources":["../../src/shell/policy.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"policy.js","sourceRoot":"","sources":["../../src/shell/policy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Shared disk↔kernel write helpers used by push, feed, and reconcile so
3
+ * inbound rebase and outbound ack stay byte-identical (better-sync.plan).
4
+ */
5
+ import type { KernelClient } from "../client/kernel-client.js";
6
+ import type { Version } from "../kernel/wire.js";
7
+ import type { FileStore } from "./reconcile.js";
8
+ export type UserContent = {
9
+ frontmatter_raw: string;
10
+ body: string;
11
+ };
12
+ /** Split a file into stored-shape fields, dropping all `$*` intrinsic lines. */
13
+ export declare function stripToUserContent(text: string): UserContent;
14
+ /**
15
+ * After a successful kernel write, restamp the local file. If the editor saved
16
+ * again during the round-trip, keep those bytes and only update provenance —
17
+ * never write the snapshot we just pushed over newer typing.
18
+ */
19
+ export declare function ackLocalWrite(store: FileStore, path: string, v: Version, pushed: UserContent): Promise<void>;
20
+ /** Optimistic put of local user bytes, then ack provenance on disk. */
21
+ export declare function putAndAck(client: KernelClient, store: FileStore, repo: string, path: string, prevVersionId: string, user: UserContent): Promise<Version>;
22
+ /** Park remote as `<name>-<version_id>.md` with `$sync: ignore` (§4.8). */
23
+ export declare function parkIgnoredSibling(store: FileStore, path: string, version: Version): Promise<void>;
24
+ /** Write remote version bytes at the canonical path (fast-forward / materialize). */
25
+ export declare function materializeAt(store: FileStore, path: string, version: Version, opts?: {
26
+ preserveMtime?: boolean;
27
+ }): Promise<void>;
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Shared disk↔kernel write helpers used by push, feed, and reconcile so
3
+ * inbound rebase and outbound ack stay byte-identical (better-sync.plan).
4
+ */
5
+ import { withVersionSuffix } from "../kernel/deletion.js";
6
+ import { extractSystemProperties, split } from "../markdown/frontmatter.js";
7
+ import { renderIgnoredSibling, renderMaterialized, stampProvenance } from "./intrinsics.js";
8
+ /** Split a file into stored-shape fields, dropping all `$*` intrinsic lines. */
9
+ export function stripToUserContent(text) {
10
+ const lf = text.replace(/\r\n/g, "\n");
11
+ const { frontmatter_raw, body } = split(lf);
12
+ return { frontmatter_raw: extractSystemProperties(frontmatter_raw).raw, body };
13
+ }
14
+ /**
15
+ * After a successful kernel write, restamp the local file. If the editor saved
16
+ * again during the round-trip, keep those bytes and only update provenance —
17
+ * never write the snapshot we just pushed over newer typing.
18
+ */
19
+ export async function ackLocalWrite(store, path, v, pushed) {
20
+ const now = await store.read(path);
21
+ if (now === null)
22
+ return;
23
+ const current = stripToUserContent(now);
24
+ if (current.body === pushed.body && current.frontmatter_raw === pushed.frontmatter_raw) {
25
+ await store.write(path, renderMaterialized(v), { preserveMtime: true });
26
+ return;
27
+ }
28
+ await store.write(path, stampProvenance(now, v.version_id, v.content_hash), {
29
+ preserveMtime: true,
30
+ });
31
+ }
32
+ /** Optimistic put of local user bytes, then ack provenance on disk. */
33
+ export async function putAndAck(client, store, repo, path, prevVersionId, user) {
34
+ const v = await client.docs.put(repo, prevVersionId, path, user);
35
+ await ackLocalWrite(store, path, v, user);
36
+ return v;
37
+ }
38
+ /** Park remote as `<name>-<version_id>.md` with `$sync: ignore` (§4.8). */
39
+ export async function parkIgnoredSibling(store, path, version) {
40
+ await store.write(withVersionSuffix(path, version.version_id), renderIgnoredSibling(version));
41
+ }
42
+ /** Write remote version bytes at the canonical path (fast-forward / materialize). */
43
+ export async function materializeAt(store, path, version, opts) {
44
+ await store.write(path, renderMaterialized(version), opts);
45
+ }
46
+ //# sourceMappingURL=converge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"converge.js","sourceRoot":"","sources":["../../src/sync/converge.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAE1D,OAAO,EAAE,uBAAuB,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAC;AAC5E,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAK5F,gFAAgF;AAChF,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACvC,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC,CAAC;IAC5C,OAAO,EAAE,eAAe,EAAE,uBAAuB,CAAC,eAAe,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;AACjF,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,KAAgB,EAChB,IAAY,EACZ,CAAU,EACV,MAAmB;IAEnB,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnC,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO;IACzB,MAAM,OAAO,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC;IACxC,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,CAAC,IAAI,IAAI,OAAO,CAAC,eAAe,KAAK,MAAM,CAAC,eAAe,EAAE,CAAC;QACvF,MAAM,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,kBAAkB,CAAC,CAAC,CAAC,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QACxE,OAAO;IACT,CAAC;IACD,MAAM,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,eAAe,CAAC,GAAG,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,YAAY,CAAC,EAAE;QAC1E,aAAa,EAAE,IAAI;KACpB,CAAC,CAAC;AACL,CAAC;AAED,uEAAuE;AACvE,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,MAAoB,EACpB,KAAgB,EAChB,IAAY,EACZ,IAAY,EACZ,aAAqB,EACrB,IAAiB;IAEjB,MAAM,CAAC,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IACjE,MAAM,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;IAC1C,OAAO,CAAC,CAAC;AACX,CAAC;AAED,2EAA2E;AAC3E,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,KAAgB,EAChB,IAAY,EACZ,OAAgB;IAEhB,MAAM,KAAK,CAAC,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,OAAO,CAAC,UAAU,CAAC,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC,CAAC;AAChG,CAAC;AAED,qFAAqF;AACrF,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,KAAgB,EAChB,IAAY,EACZ,OAAgB,EAChB,IAAkC;IAElC,MAAM,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,kBAAkB,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC;AAC7D,CAAC"}
@@ -9,6 +9,10 @@
9
9
  * first so unlink+add of a rename is a move, not a delete (§4.7); each
10
10
  * path is still stated, not trusted by event type (§4.6).
11
11
  *
12
+ * better-sync.plan: `--settle` is an mtime-age gate on both directions; inbound
13
+ * conflicts on hot files are held in an in-memory deferred map and retried on
14
+ * later polls (cursor still advances).
15
+ *
12
16
  * chokidar is confined to this module (§4.4). Echo suppression falls out of
13
17
  * self-description (§4.5): our own pushes come back on the feed but the local
14
18
  * file already embeds that version+hash, and our own writes trip the watcher
@@ -9,6 +9,10 @@
9
9
  * first so unlink+add of a rename is a move, not a delete (§4.7); each
10
10
  * path is still stated, not trusted by event type (§4.6).
11
11
  *
12
+ * better-sync.plan: `--settle` is an mtime-age gate on both directions; inbound
13
+ * conflicts on hot files are held in an in-memory deferred map and retried on
14
+ * later polls (cursor still advances).
15
+ *
12
16
  * chokidar is confined to this module (§4.4). Echo suppression falls out of
13
17
  * self-description (§4.5): our own pushes come back on the feed but the local
14
18
  * file already embeds that version+hash, and our own writes trip the watcher
@@ -18,6 +22,7 @@ import { watch } from "chokidar";
18
22
  import { readCursor, sourceFields, writeCursor } from "./cursor.js";
19
23
  import { applyFeed } from "./feed.js";
20
24
  import { createFsStore } from "./fs-store.js";
25
+ import { pathIsHot, retryDeferredEntry, } from "./hot-path.js";
21
26
  import { isIgnored, readFileIntrinsics } from "./intrinsics.js";
22
27
  import { SYNC_DIR, makeScopeFilter, toDocPath } from "./paths.js";
23
28
  import { pushBurst, pushPath } from "./push.js";
@@ -31,8 +36,10 @@ export function startDaemon(client, opts) {
31
36
  const store = createFsStore(opts.root);
32
37
  const scope = makeScopeFilter({ include: opts.include, exclude: opts.exclude });
33
38
  const map = new Map();
39
+ const deferred = new Map();
34
40
  const intervalMs = opts.intervalMs ?? 5000;
35
41
  const debounceMs = opts.debounceMs ?? 5000;
42
+ const settleMs = opts.settleMs ?? 0;
36
43
  let stopped = false;
37
44
  let watcher;
38
45
  const pending = new Set();
@@ -73,22 +80,45 @@ export function startDaemon(client, opts) {
73
80
  }
74
81
  }
75
82
  }
83
+ /** Retry in-memory deferred inbound holds before draining new feed pages. */
84
+ async function retryDeferred() {
85
+ if (deferred.size === 0)
86
+ return;
87
+ for (const [path, entry] of [...deferred.entries()]) {
88
+ if (!scope.matches(path) && entry.ref.op !== "delete") {
89
+ deferred.delete(path);
90
+ continue;
91
+ }
92
+ try {
93
+ const { done } = await retryDeferredEntry(client, store, opts.repo, path, entry, {
94
+ settleMs,
95
+ map,
96
+ deferred,
97
+ });
98
+ if (done)
99
+ deferred.delete(path);
100
+ }
101
+ catch (err) {
102
+ log(`defer retry error\t${path}\t${err.message}`);
103
+ }
104
+ }
105
+ }
76
106
  async function pollFeedOnce() {
107
+ await retryDeferred();
77
108
  const { cursor: next } = await applyFeed(client, store, scope, {
78
109
  repo: opts.repo,
79
110
  since: cursor,
80
111
  log,
81
112
  map,
113
+ settleMs,
114
+ deferred,
82
115
  });
83
116
  if (next !== cursor) {
84
117
  cursor = next;
85
118
  await persistCursor();
86
119
  }
87
120
  }
88
- function schedulePush(docPath) {
89
- if (!scope.matches(docPath))
90
- return;
91
- pending.add(docPath);
121
+ function armBurstTimer() {
92
122
  if (burstTimer)
93
123
  clearTimeout(burstTimer);
94
124
  burstTimer = setTimeout(() => {
@@ -98,10 +128,28 @@ export function startDaemon(client, opts) {
98
128
  void serialize(async () => {
99
129
  if (stopped)
100
130
  return;
101
- await pushBurst(batch, { client, store, repo: opts.repo, map, log });
131
+ const ready = [];
132
+ for (const path of batch) {
133
+ if (settleMs > 0 && (await pathIsHot(store, path, settleMs))) {
134
+ pending.add(path);
135
+ continue;
136
+ }
137
+ ready.push(path);
138
+ }
139
+ if (pending.size > 0)
140
+ armBurstTimer();
141
+ if (ready.length > 0) {
142
+ await pushBurst(ready, { client, store, repo: opts.repo, map, log });
143
+ }
102
144
  });
103
145
  }, debounceMs);
104
146
  }
147
+ function schedulePush(docPath) {
148
+ if (!scope.matches(docPath))
149
+ return;
150
+ pending.add(docPath);
151
+ armBurstTimer();
152
+ }
105
153
  const ready = (async () => {
106
154
  // 1. Startup is deterministic on the cursor marker (§4.9, §7).
107
155
  const existing = await readCursor(opts.root);
@@ -134,8 +182,13 @@ export function startDaemon(client, opts) {
134
182
  ignoreInitial: true,
135
183
  // Prune the sync state dir; the scope filter still guards everything else.
136
184
  ignored: (p) => p.includes(`/${SYNC_DIR}/`) || p.endsWith(`/${SYNC_DIR}`),
137
- ...(opts.settleMs
138
- ? { awaitWriteFinish: { stabilityThreshold: opts.settleMs, pollInterval: 100 } }
185
+ ...(settleMs > 0
186
+ ? {
187
+ awaitWriteFinish: {
188
+ stabilityThreshold: Math.min(settleMs, 2000),
189
+ pollInterval: 100,
190
+ },
191
+ }
139
192
  : {}),
140
193
  });
141
194
  const onEvent = (abs) => {
@@ -1 +1 @@
1
- {"version":3,"file":"daemon.js","sourceRoot":"","sources":["../../src/sync/daemon.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"daemon.js","sourceRoot":"","sources":["../../src/sync/daemon.ts"],"names":[],"mappings":"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"}
@@ -8,8 +8,13 @@
8
8
  * absent file is a no-op; the dirty check prevents a replay from clobbering an
9
9
  * edit made in a crash window. So a crash between apply and cursor-advance
10
10
  * simply replays the batch harmlessly.
11
+ *
12
+ * better-sync.plan: hot files defer inbound effects into an in-memory map
13
+ * (cursor still advances); dirty+stale local files rebase onto remote current
14
+ * before parking a sibling.
11
15
  */
12
16
  import type { KernelClient } from "../client/kernel-client.js";
17
+ import { type DeferredMap } from "./hot-path.js";
13
18
  import type { ScopeFilter } from "./paths.js";
14
19
  import type { RemoteMap } from "./push.js";
15
20
  import type { FileStore } from "./reconcile.js";
@@ -23,12 +28,23 @@ export type ApplyFeedOptions = {
23
28
  * witnessed unlink knows the file's prev_version_id.
24
29
  */
25
30
  map?: RemoteMap;
31
+ /**
32
+ * Minimum mtime age before inbound touch (better-sync `--settle`). When a
33
+ * deferred map is also supplied, hot paths enqueue instead of writing.
34
+ */
35
+ settleMs?: number;
36
+ /** In-memory deferred holds (daemon only). Absent → never defer (e.g. `--once`). */
37
+ deferred?: DeferredMap;
38
+ /** Injectable clock for tests. */
39
+ nowMs?: () => number;
26
40
  };
27
41
  export type ApplyFeedResult = {
28
42
  /** Final resume cursor after draining the currently-safe feed. */
29
43
  cursor: string;
30
44
  /** Number of refs whose filesystem effect was applied. */
31
45
  applied: number;
46
+ /** Number of refs held in the deferred map (hot path). */
47
+ deferred: number;
32
48
  };
33
49
  /**
34
50
  * Drain the safe feed from `since` to the live tip, applying each in-scope ref.
package/dist/sync/feed.js CHANGED
@@ -8,10 +8,14 @@
8
8
  * absent file is a no-op; the dirty check prevents a replay from clobbering an
9
9
  * edit made in a crash window. So a crash between apply and cursor-advance
10
10
  * simply replays the batch harmlessly.
11
+ *
12
+ * better-sync.plan: hot files defer inbound effects into an in-memory map
13
+ * (cursor still advances); dirty+stale local files rebase onto remote current
14
+ * before parking a sibling.
11
15
  */
12
- import { withVersionSuffix } from "../kernel/deletion.js";
13
- import { decodeVersionId } from "../kernel/version-id.js";
14
- import { isDirty, isIgnored, readFileIntrinsics, renderIgnoredSibling, renderMaterialized, } from "./intrinsics.js";
16
+ import { materializeAt } from "./converge.js";
17
+ import { decideInbound, effectInbound, enqueueDeferred, pathIsHot, versionAtOrAhead, } from "./hot-path.js";
18
+ import { isDirty, isIgnored, readFileIntrinsics } from "./intrinsics.js";
15
19
  /**
16
20
  * Drain the safe feed from `since` to the live tip, applying each in-scope ref.
17
21
  * Returns the final cursor to persist. Stops when a poll returns no new refs
@@ -21,11 +25,15 @@ export async function applyFeed(client, store, scope, opts) {
21
25
  const log = opts.log ?? (() => { });
22
26
  let cursor = opts.since;
23
27
  let applied = 0;
28
+ let deferredCount = 0;
24
29
  for (;;) {
25
30
  const page = await client.history.since({ after_version: cursor, repo: opts.repo });
26
31
  for (const ref of page.refs) {
27
- if (await applyRef(client, store, scope, opts.repo, ref, log, opts.map))
32
+ const outcome = await applyRef(client, store, scope, opts, ref, log);
33
+ if (outcome === "applied")
28
34
  applied++;
35
+ else if (outcome === "deferred")
36
+ deferredCount++;
29
37
  }
30
38
  // No forward progress → caught up (or a hot gap). Stop draining.
31
39
  if (page.next_since === cursor || page.refs.length === 0) {
@@ -34,92 +42,109 @@ export async function applyFeed(client, store, scope, opts) {
34
42
  }
35
43
  cursor = page.next_since;
36
44
  }
37
- return { cursor, applied };
45
+ return { cursor, applied, deferred: deferredCount };
38
46
  }
39
- /** Apply one feed ref to the local store. Returns true if it changed a file. */
40
- async function applyRef(client, store, scope, repo, ref, log, map) {
47
+ /** Apply one feed ref to the local store. */
48
+ async function applyRef(client, store, scope, opts, ref, log) {
49
+ const settleMs = opts.settleMs ?? 0;
50
+ const nowMs = opts.nowMs?.() ?? Date.now();
51
+ const canDefer = opts.deferred !== undefined;
41
52
  if (ref.op === "delete") {
42
- // Remove the local file at prev_path IF clean; dirty ⇒ keep (resurrection
43
- // happens on its next local-change cycle). §4.3 delete.
44
53
  const target = ref.prev_path;
45
54
  if (target === null || !scope.matches(target))
46
- return false;
47
- map?.delete(target);
55
+ return "noop";
56
+ opts.map?.delete(target);
48
57
  const text = await store.read(target);
49
58
  if (text === null)
50
- return false;
59
+ return "noop";
51
60
  const intr = readFileIntrinsics(text);
52
61
  if (isIgnored(intr) || isDirty(intr))
53
- return false; // preserve local work
62
+ return "noop"; // preserve local work
63
+ if (canDefer && (await pathIsHot(store, target, settleMs, nowMs))) {
64
+ enqueueDeferred(opts.deferred, target, ref, nowMs);
65
+ return "deferred";
66
+ }
54
67
  await store.remove(target);
55
68
  log(`feed delete\t${target}`);
56
- return true;
69
+ return "applied";
57
70
  }
58
71
  if (ref.op === "move") {
59
72
  const from = ref.prev_path;
60
73
  if (from !== null && scope.matches(from)) {
61
- map?.delete(from);
74
+ opts.map?.delete(from);
62
75
  const text = await store.read(from);
63
76
  if (text !== null) {
64
77
  const intr = readFileIntrinsics(text);
65
- if (!isIgnored(intr) && !isDirty(intr))
78
+ if (!isIgnored(intr) && !isDirty(intr)) {
79
+ if (canDefer && (await pathIsHot(store, from, settleMs, nowMs))) {
80
+ // Hold the whole move (including dest) until source is cold.
81
+ enqueueDeferred(opts.deferred, ref.path, ref, nowMs);
82
+ return "deferred";
83
+ }
66
84
  await store.remove(from);
85
+ }
67
86
  }
68
87
  }
69
88
  // Fall through to materialize at the destination path.
70
89
  }
71
- // create / update / move-destination: materialize at ref.path unless the
72
- // local file already holds these bytes, is at-or-ahead of this ref, is
73
- // dirty against a *newer* remote, or is $sync: ignore.
74
90
  if (!scope.matches(ref.path))
75
- return false;
91
+ return "noop";
76
92
  const existing = await store.read(ref.path);
77
93
  if (existing !== null) {
78
94
  const intr = readFileIntrinsics(existing);
79
95
  if (isIgnored(intr))
80
- return false;
96
+ return "noop";
97
+ // Fast path: bytes already match — provenance repair or defer when hot.
81
98
  if (intr.computed_hash === ref.content_hash) {
82
- // Bytes already present (our own push echoing back, or a vault copy).
83
- map?.set(ref.path, { version_id: ref.version_id, content_hash: ref.content_hash });
84
- // Repair provenance only if the embedded version lags.
99
+ opts.map?.set(ref.path, { version_id: ref.version_id, content_hash: ref.content_hash });
85
100
  if (intr.version === ref.version_id)
86
- return false;
101
+ return "noop";
87
102
  if (versionAtOrAhead(intr.version, ref.version_id))
88
- return false;
89
- const v = await client.docs.get_version(repo, ref.version_id);
90
- await store.write(ref.path, renderMaterialized(v), { preserveMtime: true });
91
- return true;
103
+ return "noop";
104
+ const hot = await pathIsHot(store, ref.path, settleMs, nowMs);
105
+ if (hot && canDefer) {
106
+ enqueueDeferred(opts.deferred, ref.path, ref, nowMs);
107
+ return "deferred";
108
+ }
109
+ const v = await client.docs.get_version(opts.repo, ref.version_id);
110
+ await materializeAt(store, ref.path, v, { preserveMtime: true });
111
+ log(`feed adopt\t${ref.path}`);
112
+ return "applied";
92
113
  }
93
- // Local is at or ahead of this ref (echo of our push, or a replay of an
94
- // older version). Never clobber and never park a sibling of something we
95
- // already have — including when the user has typed more since (dirty).
96
114
  if (versionAtOrAhead(intr.version, ref.version_id))
97
- return false;
98
- if (isDirty(intr)) {
99
- // A local edit collides with a *newer* incoming version → conflict, not
100
- // overwrite. Park the remote as an ignored sibling; keep local bytes.
101
- const v = await client.docs.get_version(repo, ref.version_id);
102
- await store.write(withVersionSuffix(ref.path, ref.version_id), renderIgnoredSibling(v));
115
+ return "noop";
116
+ // Divergent local vs newer remote → decideInbound (rebase / defer / park).
117
+ const hot = await pathIsHot(store, ref.path, settleMs, nowMs);
118
+ const v = await client.docs.get_version(opts.repo, ref.version_id);
119
+ const decision = decideInbound({
120
+ localText: existing,
121
+ remote: v,
122
+ hot,
123
+ canDefer,
124
+ });
125
+ const result = await effectInbound(client, store, opts.repo, ref.path, decision, {
126
+ deferred: opts.deferred,
127
+ ref,
128
+ map: opts.map,
129
+ nowMs,
130
+ });
131
+ if (result === "deferred")
132
+ return "deferred";
133
+ if (result === "noop")
134
+ return "noop";
135
+ if (result === "parked")
103
136
  log(`feed conflict\t${ref.path}`);
104
- return true;
105
- }
137
+ else if (decision.action === "rebase")
138
+ log(`feed rebase\t${ref.path}`);
139
+ else
140
+ log(`feed ${ref.op}\t${ref.path}`);
141
+ return "applied";
106
142
  }
107
- const v = await client.docs.get_version(repo, ref.version_id);
108
- await store.write(ref.path, renderMaterialized(v));
109
- map?.set(ref.path, { version_id: ref.version_id, content_hash: ref.content_hash });
143
+ // Absent locally materialize (unless somehow hot — can't be; no mtime).
144
+ const v = await client.docs.get_version(opts.repo, ref.version_id);
145
+ await materializeAt(store, ref.path, v);
146
+ opts.map?.set(ref.path, { version_id: ref.version_id, content_hash: ref.content_hash });
110
147
  log(`feed ${ref.op}\t${ref.path}`);
111
- return true;
112
- }
113
- /** True when the local embedded version is this ref or a later one. */
114
- function versionAtOrAhead(localVersion, refVersion) {
115
- if (!localVersion)
116
- return false;
117
- if (localVersion === refVersion)
118
- return true;
119
- const local = decodeVersionId(localVersion);
120
- const ref = decodeVersionId(refVersion);
121
- if (local === null || ref === null)
122
- return false;
123
- return local > ref;
148
+ return "applied";
124
149
  }
125
150
  //# sourceMappingURL=feed.js.map