@indigoai-us/hq-cloud 6.14.17 → 6.14.19

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 (124) hide show
  1. package/.github/workflows/ci.yml +35 -0
  2. package/dist/active-company.d.ts +43 -0
  3. package/dist/active-company.d.ts.map +1 -0
  4. package/dist/active-company.js +132 -0
  5. package/dist/active-company.js.map +1 -0
  6. package/dist/active-company.test.d.ts +2 -0
  7. package/dist/active-company.test.d.ts.map +1 -0
  8. package/dist/active-company.test.js +149 -0
  9. package/dist/active-company.test.js.map +1 -0
  10. package/dist/bin/sync-runner-company.d.ts +7 -0
  11. package/dist/bin/sync-runner-company.d.ts.map +1 -1
  12. package/dist/bin/sync-runner-company.js +21 -1
  13. package/dist/bin/sync-runner-company.js.map +1 -1
  14. package/dist/bin/sync-runner-planning.d.ts +27 -1
  15. package/dist/bin/sync-runner-planning.d.ts.map +1 -1
  16. package/dist/bin/sync-runner-planning.js +33 -4
  17. package/dist/bin/sync-runner-planning.js.map +1 -1
  18. package/dist/bin/sync-runner-planning.test.d.ts +2 -0
  19. package/dist/bin/sync-runner-planning.test.d.ts.map +1 -0
  20. package/dist/bin/sync-runner-planning.test.js +115 -0
  21. package/dist/bin/sync-runner-planning.test.js.map +1 -0
  22. package/dist/bin/sync-runner-watch-loop.d.ts +10 -2
  23. package/dist/bin/sync-runner-watch-loop.d.ts.map +1 -1
  24. package/dist/bin/sync-runner-watch-loop.js +201 -25
  25. package/dist/bin/sync-runner-watch-loop.js.map +1 -1
  26. package/dist/bin/sync-runner-watch-routes.d.ts +1 -1
  27. package/dist/bin/sync-runner-watch-routes.d.ts.map +1 -1
  28. package/dist/bin/sync-runner-watch-routes.js +14 -2
  29. package/dist/bin/sync-runner-watch-routes.js.map +1 -1
  30. package/dist/bin/sync-runner.d.ts +39 -9
  31. package/dist/bin/sync-runner.d.ts.map +1 -1
  32. package/dist/bin/sync-runner.js +114 -20
  33. package/dist/bin/sync-runner.js.map +1 -1
  34. package/dist/bin/sync-runner.test.js +852 -8
  35. package/dist/bin/sync-runner.test.js.map +1 -1
  36. package/dist/cli/rescue-core.js +27 -1
  37. package/dist/cli/rescue-core.js.map +1 -1
  38. package/dist/cli/rescue-drop-dir-symlink.test.d.ts +2 -0
  39. package/dist/cli/rescue-drop-dir-symlink.test.d.ts.map +1 -0
  40. package/dist/cli/rescue-drop-dir-symlink.test.js +206 -0
  41. package/dist/cli/rescue-drop-dir-symlink.test.js.map +1 -0
  42. package/dist/cli/share.d.ts +15 -0
  43. package/dist/cli/share.d.ts.map +1 -1
  44. package/dist/cli/share.js +100 -55
  45. package/dist/cli/share.js.map +1 -1
  46. package/dist/cli/share.test.js +119 -6
  47. package/dist/cli/share.test.js.map +1 -1
  48. package/dist/cli/sync.d.ts.map +1 -1
  49. package/dist/cli/sync.js +19 -25
  50. package/dist/cli/sync.js.map +1 -1
  51. package/dist/cli/sync.test.js +121 -0
  52. package/dist/cli/sync.test.js.map +1 -1
  53. package/dist/journal.d.ts +3 -1
  54. package/dist/journal.d.ts.map +1 -1
  55. package/dist/journal.js +13 -2
  56. package/dist/journal.js.map +1 -1
  57. package/dist/journal.test.js +26 -1
  58. package/dist/journal.test.js.map +1 -1
  59. package/dist/manifest-reconcile.d.ts +118 -5
  60. package/dist/manifest-reconcile.d.ts.map +1 -1
  61. package/dist/manifest-reconcile.js +319 -62
  62. package/dist/manifest-reconcile.js.map +1 -1
  63. package/dist/manifest-reconcile.test.js +824 -2
  64. package/dist/manifest-reconcile.test.js.map +1 -1
  65. package/dist/personal-vault.d.ts +12 -0
  66. package/dist/personal-vault.d.ts.map +1 -1
  67. package/dist/personal-vault.js +20 -0
  68. package/dist/personal-vault.js.map +1 -1
  69. package/dist/qmd-reindex.d.ts +88 -11
  70. package/dist/qmd-reindex.d.ts.map +1 -1
  71. package/dist/qmd-reindex.js +354 -65
  72. package/dist/qmd-reindex.js.map +1 -1
  73. package/dist/qmd-reindex.test.d.ts +4 -0
  74. package/dist/qmd-reindex.test.d.ts.map +1 -1
  75. package/dist/qmd-reindex.test.js +285 -3
  76. package/dist/qmd-reindex.test.js.map +1 -1
  77. package/dist/s3.d.ts +13 -0
  78. package/dist/s3.d.ts.map +1 -1
  79. package/dist/s3.js +34 -1
  80. package/dist/s3.js.map +1 -1
  81. package/dist/s3.test.js +60 -1
  82. package/dist/s3.test.js.map +1 -1
  83. package/dist/telemetry.d.ts +22 -0
  84. package/dist/telemetry.d.ts.map +1 -1
  85. package/dist/telemetry.js +79 -0
  86. package/dist/telemetry.js.map +1 -1
  87. package/dist/telemetry.test.js +117 -1
  88. package/dist/telemetry.test.js.map +1 -1
  89. package/dist/watcher.d.ts +26 -1
  90. package/dist/watcher.d.ts.map +1 -1
  91. package/dist/watcher.js +75 -12
  92. package/dist/watcher.js.map +1 -1
  93. package/package.json +1 -1
  94. package/pnpm-workspace.yaml +1 -1
  95. package/src/active-company.test.ts +188 -0
  96. package/src/active-company.ts +168 -0
  97. package/src/bin/sync-runner-company.ts +29 -1
  98. package/src/bin/sync-runner-planning.test.ts +131 -0
  99. package/src/bin/sync-runner-planning.ts +60 -7
  100. package/src/bin/sync-runner-watch-loop.ts +322 -32
  101. package/src/bin/sync-runner-watch-routes.ts +14 -1
  102. package/src/bin/sync-runner.test.ts +1021 -16
  103. package/src/bin/sync-runner.ts +179 -28
  104. package/src/cli/rescue-core.ts +26 -1
  105. package/src/cli/rescue-drop-dir-symlink.test.ts +224 -0
  106. package/src/cli/share.test.ts +150 -6
  107. package/src/cli/share.ts +143 -48
  108. package/src/cli/sync.test.ts +150 -1
  109. package/src/cli/sync.ts +25 -29
  110. package/src/journal.test.ts +45 -0
  111. package/src/journal.ts +18 -1
  112. package/src/manifest-reconcile.test.ts +1019 -3
  113. package/src/manifest-reconcile.ts +424 -66
  114. package/src/personal-vault.ts +23 -0
  115. package/src/qmd-reindex.test.ts +324 -4
  116. package/src/qmd-reindex.ts +414 -76
  117. package/src/s3.test.ts +79 -0
  118. package/src/s3.ts +48 -1
  119. package/src/telemetry.test.ts +128 -0
  120. package/src/telemetry.ts +80 -0
  121. package/src/watcher.ts +111 -14
  122. package/test/e2e/watcher-real-chokidar.test.ts +62 -2
  123. package/test/e2e/watcher-recursive-backend.test.ts +67 -1
  124. package/test/joiner-manifest-reconcile.integration.test.ts +283 -0
@@ -5,85 +5,247 @@
5
5
  * only after the complete fanout has settled. That makes the locally
6
6
  * materialized cloud companies authoritative for their own manifest entries
7
7
  * without discarding entries the personal vault already had.
8
+ *
9
+ * Two layers live here on purpose:
10
+ *
11
+ * - `reconcileManifest(hqRoot, targets)` is the pure reconciler. It takes
12
+ * already-resolved targets and does nothing but validate slugs, merge the
13
+ * fields it owns, and (only when something would actually change) write the
14
+ * manifest.
15
+ * - `reconcileCompanyManifest(options)` is the sync-runner adapter. It decides
16
+ * WHICH targets are eligible (personal mode, fanout completion, on-disk
17
+ * materialization, and — only when an entity resolver is injected — live
18
+ * entity resolution) and then delegates the mutation.
19
+ *
20
+ * The adapter keeps its name and options-object shape because `sync-runner`
21
+ * injects it through `deps.reconcileManifest`. That injection seam is an
22
+ * options-object callback with a different shape from the pure reconciler and
23
+ * must not be conflated with it — renaming the adapter would break the seam.
8
24
  */
9
25
  import { randomUUID } from "node:crypto";
10
26
  import * as fs from "node:fs";
11
27
  import * as path from "node:path";
12
28
  import yaml from "js-yaml";
29
+ import { VaultNotFoundError, VaultPermissionDeniedError, } from "./vault-client.js";
30
+ /**
31
+ * A PLAIN object, not merely "typeof object". js-yaml parses timestamp scalars
32
+ * into `Date`, and a looser check let a `Date` masquerade as the company map (or
33
+ * as an entry), which both defeated the non-record skip and silently destroyed
34
+ * the value via object spread. Arrays, Date, RegExp, and Map are all rejected.
35
+ */
13
36
  function isRecord(value) {
14
- return value !== null && typeof value === "object" && !Array.isArray(value);
37
+ if (value === null || typeof value !== "object")
38
+ return false;
39
+ const proto = Object.getPrototypeOf(value);
40
+ return proto === Object.prototype || proto === null;
15
41
  }
42
+ /**
43
+ * Keys that would traverse or mutate the prototype chain rather than create an
44
+ * own property. `__proto__` in particular resolves to `Object.prototype` on read
45
+ * and invokes the prototype setter on write, so it never persists to YAML and
46
+ * therefore reported drift on every single run — an unbounded rewrite loop on a
47
+ * vault-synced file. None of these is ever a legitimate company directory name.
48
+ */
49
+ const UNSAFE_SLUG_KEYS = new Set(["__proto__", "constructor", "prototype"]);
50
+ /** Own-property read; never falls through to the prototype chain. */
51
+ function ownEntry(companies, slug) {
52
+ return Object.prototype.hasOwnProperty.call(companies, slug) ? companies[slug] : undefined;
53
+ }
54
+ /** Own-property write; never invokes an inherited setter. */
55
+ function setEntry(companies, slug, value) {
56
+ Object.defineProperty(companies, slug, {
57
+ value,
58
+ enumerable: true,
59
+ writable: true,
60
+ configurable: true,
61
+ });
62
+ }
63
+ /**
64
+ * THE liveness rule. Both eligibility paths route through this single function
65
+ * so they cannot drift: an entity qualifies for a manifest entry only if it is
66
+ * an active company.
67
+ *
68
+ * Written to fail closed on `undefined`. That is the whole point — the
69
+ * plan-carried path can legitimately have no idea what the entity was (the
70
+ * degraded lookup branch in `buildFanoutPlan` records nothing), and "no idea"
71
+ * must never read as "active company".
72
+ */
73
+ function isLiveCompanyDescriptor(type, status) {
74
+ return type === "company" && status === "active";
75
+ }
76
+ /**
77
+ * A company is live when the entity still resolves to the uid we routed on and
78
+ * is an active company. `name` and `bucketName` are deliberately NOT required:
79
+ * demanding them dropped otherwise-valid companies from the manifest entirely,
80
+ * which is exactly the joiner bug this module now guards against. Missing
81
+ * fields are omitted from the written entry instead of disqualifying it.
82
+ */
16
83
  function isLiveCompany(entity, uid) {
17
- return (entity.uid === uid &&
18
- entity.type === "company" &&
19
- entity.status === "active" &&
20
- typeof entity.name === "string" &&
21
- entity.name.length > 0 &&
22
- typeof entity.bucketName === "string" &&
23
- entity.bucketName.length > 0);
84
+ return entity.uid === uid && isLiveCompanyDescriptor(entity.type, entity.status);
24
85
  }
25
- function hasCompanyDirectory(companiesDir, slug) {
86
+ /**
87
+ * The same rule applied to liveness captured at plan time instead of to a
88
+ * freshly fetched entity. There is no uid re-check here because there is no
89
+ * second observation to reconcile against — the snapshot came off the very
90
+ * `entity.get(uid)` that produced this target.
91
+ */
92
+ function isLiveTargetSnapshot(target) {
93
+ return isLiveCompanyDescriptor(target.entityType, target.entityStatus);
94
+ }
95
+ /**
96
+ * Is this entity-probe failure the routine kind?
97
+ *
98
+ * "The company is gone" (404) and "the caller can no longer see it" (403) are
99
+ * the two ordinary reasons a target stops resolving, and both correctly mean
100
+ * "skip, write nothing". Anything else — an auth failure, a 5xx, a network
101
+ * fault, or a plain programming error like a TypeError — shares the same
102
+ * control flow but is NOT routine, and swallowing it silently is how a broken
103
+ * resolver disguises itself as "joiners never get manifest entries".
104
+ *
105
+ * The duck-typed `statusCode` arm matters: `buildFanoutPlan` already classifies
106
+ * this same call's failures that way, so a bare `{ statusCode }` rejection is a
107
+ * shape this codebase genuinely produces, not a hypothetical.
108
+ */
109
+ function isExpectedLookupFailure(err) {
110
+ if (err instanceof VaultNotFoundError)
111
+ return true;
112
+ if (err instanceof VaultPermissionDeniedError)
113
+ return true;
114
+ return (typeof err === "object" &&
115
+ err !== null &&
116
+ "statusCode" in err &&
117
+ typeof err.statusCode === "number");
118
+ }
119
+ /** Non-empty string, or undefined. Keys with no value are never written. */
120
+ function optionalField(value) {
121
+ return typeof value === "string" && value.length > 0 ? value : undefined;
122
+ }
123
+ /**
124
+ * Pure path check — a slug must name a direct child of `companies/` and nothing
125
+ * else. This deliberately does NOT require the directory to exist; the on-disk
126
+ * stat check is an eligibility concern and lives in the adapter.
127
+ */
128
+ function isSafeCompanySlug(companiesDir, slug) {
129
+ if (slug.length === 0)
130
+ return false;
131
+ if (slug.includes("/") || slug.includes("\\"))
132
+ return false;
133
+ if (slug === "." || slug === "..")
134
+ return false;
135
+ if (UNSAFE_SLUG_KEYS.has(slug))
136
+ return false;
26
137
  const resolvedCompaniesDir = path.resolve(companiesDir);
27
138
  const companyDir = path.resolve(resolvedCompaniesDir, slug);
28
- if (path.dirname(companyDir) !== resolvedCompaniesDir)
139
+ return path.dirname(companyDir) === resolvedCompaniesDir;
140
+ }
141
+ function hasCompanyDirectory(companiesDir, slug) {
142
+ if (!isSafeCompanySlug(companiesDir, slug))
29
143
  return false;
30
144
  try {
31
- return fs.statSync(companyDir).isDirectory();
145
+ return fs.statSync(path.resolve(companiesDir, slug)).isDirectory();
32
146
  }
33
147
  catch {
34
148
  return false;
35
149
  }
36
150
  }
151
+ /**
152
+ * Read the manifest, or return null when there is nothing safe to merge into.
153
+ * A missing manifest is a no-op, NOT an invitation to create one: this module
154
+ * only ever restores entries into a manifest that already exists.
155
+ */
37
156
  function loadManifest(manifestPath) {
38
157
  try {
39
- const raw = fs.readFileSync(manifestPath, "utf8");
40
- const parsed = yaml.load(raw);
158
+ const parsed = yaml.load(fs.readFileSync(manifestPath, "utf8"));
41
159
  return isRecord(parsed) ? parsed : null;
42
160
  }
43
- catch (err) {
44
- if (err.code === "ENOENT")
45
- return {};
161
+ catch {
46
162
  return null;
47
163
  }
48
164
  }
49
- function writeManifestAtomically(manifestPath, document) {
50
- const temporaryPath = path.join(path.dirname(manifestPath), `.manifest-${process.pid}-${randomUUID()}.yaml`);
51
- fs.writeFileSync(temporaryPath, yaml.dump(document, { lineWidth: -1 }), "utf8");
52
- fs.renameSync(temporaryPath, manifestPath);
165
+ function serializeManifest(document) {
166
+ return yaml.dump(document, { lineWidth: -1 });
53
167
  }
54
168
  /**
55
- * Atomically merge live, successfully pulled company targets into
56
- * `companies/manifest.yaml`. Targets that are personal, incomplete, deleted,
57
- * no longer resolvable, or not materialized on disk are intentionally ignored.
169
+ * Temp-file + rename. The temp file is created in the SAME directory as the
170
+ * target so the rename is a true atomic same-filesystem operation.
171
+ *
172
+ * The `finally` is not cosmetic: without it, a failed write or rename stranded a
173
+ * uniquely-named `.manifest-*.yaml` inside `companies/`, which rides the personal
174
+ * vault to every machine and accumulates one file per failed run.
175
+ *
176
+ * A write failure deliberately PROPAGATES rather than being swallowed into
177
+ * `written: false`. The caller at `sync-runner.ts` already wraps this in a
178
+ * try/catch that emits a `runner.manifest_reconcile.failed` diagnostic; reporting
179
+ * a real I/O failure as a clean no-op would hide it. The AC's "never throws"
180
+ * clause covers manifest READ problems (missing/unreadable/unparseable), all of
181
+ * which are handled as no-ops in `loadManifest`.
58
182
  */
59
- export async function reconcileCompanyManifest(options) {
60
- const companiesDir = path.join(options.hqRoot, "companies");
61
- const candidates = [];
62
- for (const target of options.targets) {
63
- if (target.personalMode === true ||
64
- !options.completedCompanySlugs.has(target.slug) ||
65
- !hasCompanyDirectory(companiesDir, target.slug)) {
66
- continue;
67
- }
183
+ function writeManifestAtomically(manifestPath, serialized) {
184
+ const temporaryPath = path.join(path.dirname(manifestPath), `.manifest-${process.pid}-${randomUUID()}.yaml`);
185
+ try {
186
+ fs.writeFileSync(temporaryPath, serialized, "utf8");
187
+ fs.renameSync(temporaryPath, manifestPath);
188
+ }
189
+ finally {
190
+ // After a successful rename the temp path is already gone, so this is a
191
+ // no-op on the happy path and a cleanup on every failure path.
68
192
  try {
69
- const entity = await options.getEntity(target.uid);
70
- if (isLiveCompany(entity, target.uid)) {
71
- candidates.push({ slug: target.slug, entity });
72
- }
193
+ fs.rmSync(temporaryPath, { force: true });
73
194
  }
74
195
  catch {
75
- // The target is not presently a live, resolvable company. Never mint a
76
- // manifest entry from stale fanout data.
196
+ // Best effort — never mask the original error.
197
+ }
198
+ }
199
+ }
200
+ function emptyResult(skipped = []) {
201
+ return { written: false, added: [], updated: [], skipped };
202
+ }
203
+ /**
204
+ * Merge resolved targets into `companies/manifest.yaml` and report what moved.
205
+ *
206
+ * Only the three fields this module owns are considered: `cloud_uid` (always
207
+ * written — it is the routing key), plus `name` and `bucket_name` when the
208
+ * target carries them. Omitting a field means "do not set this key"; it never
209
+ * means "delete it", so a manifest value already on disk survives a target that
210
+ * has nothing to say about it.
211
+ *
212
+ * The skip-if-unchanged comparison is load-bearing rather than an optimization.
213
+ * `yaml.dump` cannot round-trip comments, so an unconditional rewrite strips the
214
+ * manifest header — and because `manifest.yaml` rides the personal vault, that
215
+ * rewrite propagates to every machine and drives a recurring sync conflict loop.
216
+ * When nothing would change, the file is left byte-for-byte identical and its
217
+ * mtime untouched.
218
+ */
219
+ export function reconcileManifest(hqRoot, targets) {
220
+ const companiesDir = path.join(hqRoot, "companies");
221
+ const skipped = [];
222
+ // Dedupe by slug, last-wins. Two targets for one slug used to flip the change
223
+ // flag on and then back off again, leaving it stuck true while the resulting
224
+ // content was identical — a write (and an mtime bump) on every single run.
225
+ const safeBySlug = new Map();
226
+ for (const target of targets) {
227
+ if (!isSafeCompanySlug(companiesDir, target.slug)) {
228
+ skipped.push(target.slug);
229
+ continue;
77
230
  }
231
+ safeBySlug.set(target.slug, target);
78
232
  }
79
- if (candidates.length === 0)
80
- return;
233
+ if (safeBySlug.size === 0)
234
+ return emptyResult(skipped);
81
235
  const manifestPath = path.join(companiesDir, "manifest.yaml");
82
236
  const document = loadManifest(manifestPath);
83
237
  if (!document)
84
- return;
238
+ return emptyResult(skipped);
239
+ // Pre-image, in the SAME serialization the writer uses. Comparing against the
240
+ // raw file bytes would be wrong: raw text carries comments and formatting that
241
+ // `yaml.dump` cannot reproduce, so every commented manifest would look
242
+ // "changed" and rewrite on every single run.
243
+ const before = serializeManifest(document);
85
244
  let companies;
86
- if (document.companies === undefined) {
245
+ if (document.companies === undefined || document.companies === null) {
246
+ // A missing `companies:` key and a bare `companies:` (which YAML parses as
247
+ // null) mean the same thing — an empty map. Treating them differently would
248
+ // silently deny a joiner their entry on one of the two shapes.
87
249
  companies = {};
88
250
  document.companies = companies;
89
251
  }
@@ -91,30 +253,125 @@ export async function reconcileCompanyManifest(options) {
91
253
  companies = document.companies;
92
254
  }
93
255
  else {
94
- // A malformed `companies` key is user state; refusing to replace it is
95
- // safer than losing a manifest that cannot be interpreted unambiguously.
96
- return;
256
+ // Any other shape (array, scalar, timestamp, string) is malformed user
257
+ // state; refusing to replace it is safer than losing a manifest that cannot
258
+ // be interpreted unambiguously.
259
+ return emptyResult(skipped);
260
+ }
261
+ // Changes are STAGED rather than applied in place, so the pre-image can be
262
+ // serialized for comparison and so a pure no-op costs no serialization at all.
263
+ const pending = [];
264
+ for (const target of safeBySlug.values()) {
265
+ const existing = ownEntry(companies, target.slug);
266
+ if (existing !== undefined && !isRecord(existing)) {
267
+ skipped.push(target.slug);
268
+ continue;
269
+ }
270
+ const desired = { cloud_uid: target.uid };
271
+ const name = optionalField(target.name);
272
+ if (name !== undefined)
273
+ desired.name = name;
274
+ const bucketName = optionalField(target.bucketName);
275
+ if (bucketName !== undefined)
276
+ desired.bucket_name = bucketName;
277
+ if (existing === undefined) {
278
+ pending.push({ slug: target.slug, entry: desired, isNew: true });
279
+ continue;
280
+ }
281
+ // Compare ONLY the fields we would write. Everything else on the entry is
282
+ // someone else's state and never participates in the change decision.
283
+ const drifted = Object.keys(desired).some((key) => existing[key] !== desired[key]);
284
+ if (!drifted)
285
+ continue;
286
+ pending.push({ slug: target.slug, entry: { ...existing, ...desired }, isNew: false });
97
287
  }
98
- let changed = false;
99
- for (const { slug, entity } of candidates) {
100
- const existing = companies[slug];
101
- if (existing !== undefined && !isRecord(existing))
288
+ if (pending.length === 0)
289
+ return emptyResult(skipped);
290
+ // Final-state guard. The write decision rests on the document itself, not on
291
+ // a flag the merge loop could have toggled: apply the staged changes, then
292
+ // write only if the serialized result genuinely differs from the `before`
293
+ // snapshot taken at load time. This is the backstop that stops any
294
+ // order-dependent bug — duplicate targets cancelling out, a sticky flag — from
295
+ // ever reaching a vault-synced file.
296
+ for (const change of pending)
297
+ setEntry(companies, change.slug, change.entry);
298
+ const serialized = serializeManifest(document);
299
+ if (serialized === before)
300
+ return emptyResult(skipped);
301
+ writeManifestAtomically(manifestPath, serialized);
302
+ return {
303
+ written: true,
304
+ added: pending.filter((change) => change.isNew).map((change) => change.slug),
305
+ updated: pending.filter((change) => !change.isNew).map((change) => change.slug),
306
+ skipped,
307
+ };
308
+ }
309
+ /**
310
+ * Atomically merge live, successfully pulled company targets into
311
+ * `companies/manifest.yaml`. Targets that are personal, incomplete, not
312
+ * materialized on disk, or not a verifiably active company are ignored.
313
+ *
314
+ * Every one of those filters applies on BOTH paths. The paths differ only in
315
+ * where the liveness evidence comes from: a fresh `getEntity` fetch when one is
316
+ * injected, or the type/status the fanout plan captured when it is not.
317
+ */
318
+ export async function reconcileCompanyManifest(options) {
319
+ const companiesDir = path.join(options.hqRoot, "companies");
320
+ const resolvedTargets = [];
321
+ const getEntity = options.getEntity;
322
+ for (const target of options.targets) {
323
+ if (target.personalMode === true ||
324
+ !options.completedCompanySlugs.has(target.slug) ||
325
+ !hasCompanyDirectory(companiesDir, target.slug)) {
102
326
  continue;
103
- const entry = existing ?? {};
104
- if (entry.name === entity.name &&
105
- entry.cloud_uid === entity.uid &&
106
- entry.bucket_name === entity.bucketName) {
327
+ }
328
+ if (getEntity === undefined) {
329
+ // Plan-carried path. The liveness rule is NOT relaxed here — it is
330
+ // applied to the type/status the plan captured off `entity.get`. A target
331
+ // with no such evidence fails closed and never reaches the manifest.
332
+ if (!isLiveTargetSnapshot(target))
333
+ continue;
334
+ resolvedTargets.push({
335
+ slug: target.slug,
336
+ uid: target.uid,
337
+ ...(target.name === undefined ? {} : { name: target.name }),
338
+ ...(target.bucketName === undefined ? {} : { bucketName: target.bucketName }),
339
+ });
107
340
  continue;
108
341
  }
109
- companies[slug] = {
110
- ...entry,
111
- name: entity.name,
112
- cloud_uid: entity.uid,
113
- bucket_name: entity.bucketName,
114
- };
115
- changed = true;
342
+ try {
343
+ const entity = await getEntity(target.uid);
344
+ if (isLiveCompany(entity, target.uid)) {
345
+ resolvedTargets.push({
346
+ slug: target.slug,
347
+ uid: entity.uid,
348
+ ...(entity.name === undefined ? {} : { name: entity.name }),
349
+ ...(entity.bucketName === undefined ? {} : { bucketName: entity.bucketName }),
350
+ });
351
+ }
352
+ }
353
+ catch (err) {
354
+ // The target is not presently a live, resolvable company. Never mint a
355
+ // manifest entry from stale fanout data — so the target is skipped
356
+ // regardless of WHY the probe failed.
357
+ //
358
+ // But "entity is gone" and "the resolver is broken" are different
359
+ // stories with identical control flow, and only the first is routine.
360
+ // Staying silent on the second is how a systematic resolver failure
361
+ // disguises itself as "joiners never get manifest entries" — the exact
362
+ // bug class this module exists to prevent.
363
+ if (!isExpectedLookupFailure(err)) {
364
+ options.reportDiagnostic?.({
365
+ event: "runner.manifest_reconcile.entity_probe_failed",
366
+ message: "unexpected error resolving company entity; target skipped",
367
+ err,
368
+ context: { slug: target.slug, uid: target.uid },
369
+ });
370
+ }
371
+ }
116
372
  }
117
- if (changed)
118
- writeManifestAtomically(manifestPath, document);
373
+ if (resolvedTargets.length === 0)
374
+ return emptyResult();
375
+ return reconcileManifest(options.hqRoot, resolvedTargets);
119
376
  }
120
377
  //# sourceMappingURL=manifest-reconcile.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"manifest-reconcile.js","sourceRoot":"","sources":["../src/manifest-reconcile.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,IAAI,MAAM,SAAS,CAAC;AA6B3B,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC;AAED,SAAS,aAAa,CAAC,MAAkB,EAAE,GAAW;IACpD,OAAO,CACL,MAAM,CAAC,GAAG,KAAK,GAAG;QAClB,MAAM,CAAC,IAAI,KAAK,SAAS;QACzB,MAAM,CAAC,MAAM,KAAK,QAAQ;QAC1B,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ;QAC/B,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC;QACtB,OAAO,MAAM,CAAC,UAAU,KAAK,QAAQ;QACrC,MAAM,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAC7B,CAAC;AACJ,CAAC;AAED,SAAS,mBAAmB,CAAC,YAAoB,EAAE,IAAY;IAC7D,MAAM,oBAAoB,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IACxD,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,oBAAoB,EAAE,IAAI,CAAC,CAAC;IAC5D,IAAI,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,oBAAoB;QAAE,OAAO,KAAK,CAAC;IACpE,IAAI,CAAC;QACH,OAAO,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,WAAW,EAAE,CAAC;IAC/C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,SAAS,YAAY,CAAC,YAAoB;IACxC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;QAClD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC9B,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAE,MAA2B,CAAC,CAAC,CAAC,IAAI,CAAC;IAChE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,EAAE,CAAC;QAChE,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,uBAAuB,CAAC,YAAoB,EAAE,QAA0B;IAC/E,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAC7B,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,EAC1B,aAAa,OAAO,CAAC,GAAG,IAAI,UAAU,EAAE,OAAO,CAChD,CAAC;IACF,EAAE,CAAC,aAAa,CAAC,aAAa,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC;IAChF,EAAE,CAAC,UAAU,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC;AAC7C,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,OAAiC;IAEjC,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAC5D,MAAM,UAAU,GAAgD,EAAE,CAAC;IAEnE,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACrC,IACE,MAAM,CAAC,YAAY,KAAK,IAAI;YAC5B,CAAC,OAAO,CAAC,qBAAqB,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC;YAC/C,CAAC,mBAAmB,CAAC,YAAY,EAAE,MAAM,CAAC,IAAI,CAAC,EAC/C,CAAC;YACD,SAAS;QACX,CAAC;QAED,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACnD,IAAI,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;gBACtC,UAAU,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;YACjD,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,uEAAuE;YACvE,yCAAyC;QAC3C,CAAC;IACH,CAAC;IAED,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IAEpC,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,eAAe,CAAC,CAAC;IAC9D,MAAM,QAAQ,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;IAC5C,IAAI,CAAC,QAAQ;QAAE,OAAO;IAEtB,IAAI,SAA0C,CAAC;IAC/C,IAAI,QAAQ,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;QACrC,SAAS,GAAG,EAAE,CAAC;QACf,QAAQ,CAAC,SAAS,GAAG,SAAS,CAAC;IACjC,CAAC;SAAM,IAAI,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QACxC,SAAS,GAAG,QAAQ,CAAC,SAA4C,CAAC;IACpE,CAAC;SAAM,CAAC;QACN,uEAAuE;QACvE,yEAAyE;QACzE,OAAO;IACT,CAAC;IAED,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,UAAU,EAAE,CAAC;QAC1C,MAAM,QAAQ,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;QACjC,IAAI,QAAQ,KAAK,SAAS,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;YAAE,SAAS;QAC5D,MAAM,KAAK,GAAoB,QAAQ,IAAI,EAAE,CAAC;QAC9C,IACE,KAAK,CAAC,IAAI,KAAK,MAAM,CAAC,IAAI;YAC1B,KAAK,CAAC,SAAS,KAAK,MAAM,CAAC,GAAG;YAC9B,KAAK,CAAC,WAAW,KAAK,MAAM,CAAC,UAAU,EACvC,CAAC;YACD,SAAS;QACX,CAAC;QACD,SAAS,CAAC,IAAI,CAAC,GAAG;YAChB,GAAG,KAAK;YACR,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,SAAS,EAAE,MAAM,CAAC,GAAG;YACrB,WAAW,EAAE,MAAM,CAAC,UAAU;SAC/B,CAAC;QACF,OAAO,GAAG,IAAI,CAAC;IACjB,CAAC;IAED,IAAI,OAAO;QAAE,uBAAuB,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;AAC/D,CAAC"}
1
+ {"version":3,"file":"manifest-reconcile.js","sourceRoot":"","sources":["../src/manifest-reconcile.ts"],"names":[],"mappings":"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"}