@telora/daemon 0.22.189 → 0.22.208

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 (189) hide show
  1. package/build-info.json +6 -3
  2. package/dist/backends/index.d.ts.map +1 -1
  3. package/dist/backends/index.js +2 -0
  4. package/dist/backends/index.js.map +1 -1
  5. package/dist/completion/completion-fact-readers.d.ts +35 -0
  6. package/dist/completion/completion-fact-readers.d.ts.map +1 -0
  7. package/dist/completion/completion-fact-readers.js +70 -0
  8. package/dist/completion/completion-fact-readers.js.map +1 -0
  9. package/dist/completion/event-dep-defaults.d.ts +27 -0
  10. package/dist/completion/event-dep-defaults.d.ts.map +1 -0
  11. package/dist/completion/event-dep-defaults.js +30 -0
  12. package/dist/completion/event-dep-defaults.js.map +1 -0
  13. package/dist/completion/event-phases.d.ts.map +1 -1
  14. package/dist/completion/event-phases.js +5 -13
  15. package/dist/completion/event-phases.js.map +1 -1
  16. package/dist/completion/event.d.ts +32 -0
  17. package/dist/completion/event.d.ts.map +1 -1
  18. package/dist/completion/event.js +31 -25
  19. package/dist/completion/event.js.map +1 -1
  20. package/dist/completion/session-hold.d.ts +13 -1
  21. package/dist/completion/session-hold.d.ts.map +1 -1
  22. package/dist/completion/session-hold.js +18 -0
  23. package/dist/completion/session-hold.js.map +1 -1
  24. package/dist/completion/team-phase.d.ts.map +1 -1
  25. package/dist/completion/team-phase.js +1 -0
  26. package/dist/completion/team-phase.js.map +1 -1
  27. package/dist/completion/unclaimed-migration-notice-phase.d.ts +97 -0
  28. package/dist/completion/unclaimed-migration-notice-phase.d.ts.map +1 -0
  29. package/dist/completion/unclaimed-migration-notice-phase.js +270 -0
  30. package/dist/completion/unclaimed-migration-notice-phase.js.map +1 -0
  31. package/dist/directive/directive-executor.d.ts.map +1 -1
  32. package/dist/directive/directive-executor.js +1 -20
  33. package/dist/directive/directive-executor.js.map +1 -1
  34. package/dist/focus/focus-executor.d.ts +1 -0
  35. package/dist/focus/focus-executor.d.ts.map +1 -1
  36. package/dist/focus/focus-executor.js +18 -17
  37. package/dist/focus/focus-executor.js.map +1 -1
  38. package/dist/focus/focus-team-state.js +1 -1
  39. package/dist/focus/listener-spawn.d.ts.map +1 -1
  40. package/dist/focus/listener-spawn.js +5 -20
  41. package/dist/focus/listener-spawn.js.map +1 -1
  42. package/dist/focus/listener-state.d.ts.map +1 -1
  43. package/dist/focus/listener-state.js +1 -0
  44. package/dist/focus/listener-state.js.map +1 -1
  45. package/dist/focus/listener.d.ts +1 -0
  46. package/dist/focus/listener.d.ts.map +1 -1
  47. package/dist/focus/listener.js +9 -0
  48. package/dist/focus/listener.js.map +1 -1
  49. package/dist/focus/migration-claim-pass.d.ts +50 -0
  50. package/dist/focus/migration-claim-pass.d.ts.map +1 -0
  51. package/dist/focus/migration-claim-pass.js +234 -0
  52. package/dist/focus/migration-claim-pass.js.map +1 -0
  53. package/dist/focus/pending-spawn-pass-deps.d.ts.map +1 -1
  54. package/dist/focus/pending-spawn-pass-deps.js +2 -1
  55. package/dist/focus/pending-spawn-pass-deps.js.map +1 -1
  56. package/dist/focus/team-actionable-work.d.ts +21 -0
  57. package/dist/focus/team-actionable-work.d.ts.map +1 -1
  58. package/dist/focus/team-actionable-work.js +3 -0
  59. package/dist/focus/team-actionable-work.js.map +1 -1
  60. package/dist/git/types.d.ts.map +1 -1
  61. package/dist/git/types.js +2 -1
  62. package/dist/git/types.js.map +1 -1
  63. package/dist/loops/llm-client.d.ts.map +1 -1
  64. package/dist/loops/llm-client.js +11 -1
  65. package/dist/loops/llm-client.js.map +1 -1
  66. package/dist/migrations/migration-claim-lifecycle.d.ts +166 -0
  67. package/dist/migrations/migration-claim-lifecycle.d.ts.map +1 -0
  68. package/dist/migrations/migration-claim-lifecycle.js +198 -0
  69. package/dist/migrations/migration-claim-lifecycle.js.map +1 -0
  70. package/dist/migrations/migration-held-set.d.ts +171 -0
  71. package/dist/migrations/migration-held-set.d.ts.map +1 -0
  72. package/dist/migrations/migration-held-set.js +295 -0
  73. package/dist/migrations/migration-held-set.js.map +1 -0
  74. package/dist/migrations/migration-inventory.d.ts +187 -0
  75. package/dist/migrations/migration-inventory.d.ts.map +1 -0
  76. package/dist/migrations/migration-inventory.js +253 -0
  77. package/dist/migrations/migration-inventory.js.map +1 -0
  78. package/dist/migrations/migration-manifest.d.ts +114 -0
  79. package/dist/migrations/migration-manifest.d.ts.map +1 -0
  80. package/dist/migrations/migration-manifest.js +295 -0
  81. package/dist/migrations/migration-manifest.js.map +1 -0
  82. package/dist/prompt/team-prompt-base.js +1 -1
  83. package/dist/prompt-sections/migration-numbering-section.d.ts +58 -0
  84. package/dist/prompt-sections/migration-numbering-section.d.ts.map +1 -0
  85. package/dist/prompt-sections/migration-numbering-section.js +70 -0
  86. package/dist/prompt-sections/migration-numbering-section.js.map +1 -0
  87. package/dist/prompt-sections/migration-numbering-state.d.ts +30 -0
  88. package/dist/prompt-sections/migration-numbering-state.d.ts.map +1 -0
  89. package/dist/prompt-sections/migration-numbering-state.js +24 -0
  90. package/dist/prompt-sections/migration-numbering-state.js.map +1 -0
  91. package/dist/prompt-sections/unclaimed-migration-section.d.ts +38 -0
  92. package/dist/prompt-sections/unclaimed-migration-section.d.ts.map +1 -0
  93. package/dist/prompt-sections/unclaimed-migration-section.js +57 -0
  94. package/dist/prompt-sections/unclaimed-migration-section.js.map +1 -0
  95. package/dist/queries/claim-pass-focuses.d.ts +19 -0
  96. package/dist/queries/claim-pass-focuses.d.ts.map +1 -0
  97. package/dist/queries/claim-pass-focuses.js +72 -0
  98. package/dist/queries/claim-pass-focuses.js.map +1 -0
  99. package/dist/queries/daemon-connection.d.ts.map +1 -1
  100. package/dist/queries/daemon-connection.js +1 -0
  101. package/dist/queries/daemon-connection.js.map +1 -1
  102. package/dist/queries/focus-discovery.d.ts +25 -2
  103. package/dist/queries/focus-discovery.d.ts.map +1 -1
  104. package/dist/queries/focus-discovery.js +16 -9
  105. package/dist/queries/focus-discovery.js.map +1 -1
  106. package/dist/queries/focus-unclaimed-migration.d.ts +146 -0
  107. package/dist/queries/focus-unclaimed-migration.d.ts.map +1 -0
  108. package/dist/queries/focus-unclaimed-migration.js +266 -0
  109. package/dist/queries/focus-unclaimed-migration.js.map +1 -0
  110. package/dist/queries/live-focus-branches.d.ts +32 -0
  111. package/dist/queries/live-focus-branches.d.ts.map +1 -0
  112. package/dist/queries/live-focus-branches.js +66 -0
  113. package/dist/queries/live-focus-branches.js.map +1 -0
  114. package/dist/queries/migration-claims.d.ts +25 -0
  115. package/dist/queries/migration-claims.d.ts.map +1 -0
  116. package/dist/queries/migration-claims.js +79 -0
  117. package/dist/queries/migration-claims.js.map +1 -0
  118. package/dist/queries/schemas.d.ts +2 -0
  119. package/dist/queries/schemas.d.ts.map +1 -1
  120. package/dist/queries/schemas.js +6 -0
  121. package/dist/queries/schemas.js.map +1 -1
  122. package/dist/queries/shared.js +1 -1
  123. package/dist/session/session-lineage.d.ts.map +1 -1
  124. package/dist/session/session-lineage.js +3 -0
  125. package/dist/session/session-lineage.js.map +1 -1
  126. package/dist/shell/heartbeat-metadata.d.ts +48 -2
  127. package/dist/shell/heartbeat-metadata.d.ts.map +1 -1
  128. package/dist/shell/heartbeat-metadata.js +80 -3
  129. package/dist/shell/heartbeat-metadata.js.map +1 -1
  130. package/dist/shell/heartbeat.d.ts +13 -0
  131. package/dist/shell/heartbeat.d.ts.map +1 -1
  132. package/dist/shell/heartbeat.js +31 -10
  133. package/dist/shell/heartbeat.js.map +1 -1
  134. package/dist/shell/mcp-resolution.js +1 -1
  135. package/dist/shell/mcp-resolution.js.map +1 -1
  136. package/dist/shell/migration-held-set-publish.d.ts +170 -0
  137. package/dist/shell/migration-held-set-publish.d.ts.map +1 -0
  138. package/dist/shell/migration-held-set-publish.js +172 -0
  139. package/dist/shell/migration-held-set-publish.js.map +1 -0
  140. package/dist/shell/worker-pidfile.d.ts.map +1 -1
  141. package/dist/shell/worker-pidfile.js +1 -0
  142. package/dist/shell/worker-pidfile.js.map +1 -1
  143. package/dist/spawn/helpers.d.ts.map +1 -1
  144. package/dist/spawn/helpers.js +1 -0
  145. package/dist/spawn/helpers.js.map +1 -1
  146. package/dist/spawn/lead-launch.d.ts.map +1 -1
  147. package/dist/spawn/lead-launch.js +1 -0
  148. package/dist/spawn/lead-launch.js.map +1 -1
  149. package/dist/spawn/lead-process-ownership.d.ts.map +1 -1
  150. package/dist/spawn/lead-process-ownership.js +1 -0
  151. package/dist/spawn/lead-process-ownership.js.map +1 -1
  152. package/dist/spawn/process-group-kill.d.ts.map +1 -1
  153. package/dist/spawn/process-group-kill.js +1 -0
  154. package/dist/spawn/process-group-kill.js.map +1 -1
  155. package/dist/spawn/process-spawn.d.ts +18 -1
  156. package/dist/spawn/process-spawn.d.ts.map +1 -1
  157. package/dist/spawn/process-spawn.js +11 -6
  158. package/dist/spawn/process-spawn.js.map +1 -1
  159. package/dist/spawn/prompt-assembly.d.ts +115 -18
  160. package/dist/spawn/prompt-assembly.d.ts.map +1 -1
  161. package/dist/spawn/prompt-assembly.js +121 -2
  162. package/dist/spawn/prompt-assembly.js.map +1 -1
  163. package/dist/spawn/spawn-and-acknowledge.d.ts +61 -0
  164. package/dist/spawn/spawn-and-acknowledge.d.ts.map +1 -0
  165. package/dist/spawn/spawn-and-acknowledge.js +117 -0
  166. package/dist/spawn/spawn-and-acknowledge.js.map +1 -0
  167. package/dist/spawn/spawn-cooldown.js +1 -1
  168. package/dist/spawn/spawn-decision.d.ts +10 -10
  169. package/dist/spawn/spawn-decision.d.ts.map +1 -1
  170. package/dist/spawn/spawn-decision.js +33 -5
  171. package/dist/spawn/spawn-decision.js.map +1 -1
  172. package/dist/templates/claude-md.d.ts +1 -1
  173. package/dist/templates/claude-md.d.ts.map +1 -1
  174. package/dist/templates/claude-md.js +24 -0
  175. package/dist/templates/claude-md.js.map +1 -1
  176. package/dist/testing/fixtures/index.js +1 -1
  177. package/dist/testing/fixtures/migration-namespace-repo.d.ts +63 -0
  178. package/dist/testing/fixtures/migration-namespace-repo.d.ts.map +1 -0
  179. package/dist/testing/fixtures/migration-namespace-repo.js +164 -0
  180. package/dist/testing/fixtures/migration-namespace-repo.js.map +1 -0
  181. package/dist/testing/fixtures/stubborn-lead-process.d.ts.map +1 -1
  182. package/dist/testing/fixtures/stubborn-lead-process.js +1 -0
  183. package/dist/testing/fixtures/stubborn-lead-process.js.map +1 -1
  184. package/dist/types/index.js +1 -1
  185. package/dist/verify/no-own-delta-verdict.d.ts +16 -2
  186. package/dist/verify/no-own-delta-verdict.d.ts.map +1 -1
  187. package/dist/verify/no-own-delta-verdict.js +18 -2
  188. package/dist/verify/no-own-delta-verdict.js.map +1 -1
  189. package/package.json +2 -2
@@ -0,0 +1,253 @@
1
+ // PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
2
+ // Pinned by: packages/daemon/src/focus/migration-claim-pass.test.ts,
3
+ // packages/daemon/src/migrations/migration-claim-lifecycle.test.ts,
4
+ // packages/daemon/src/migrations/migration-inventory.test.ts
5
+ // PINNED-BY-END
6
+ /**
7
+ * What a git REF holds in a product's declared migration namespace.
8
+ *
9
+ * WHY IT READS THE GIT TREE AND NEVER A WORKING TREE
10
+ * --------------------------------------------------
11
+ * The whole point of this module is to answer the question for a branch that is
12
+ * NOT checked out. Two focuses are armed concurrently on one product, each in
13
+ * its own worktree; the allocator that grants a migration number has to know
14
+ * what the OTHER focus's branch already holds, and the daemon never checks that
15
+ * branch out. So every read here is `git ls-tree` / `git show` against a ref.
16
+ *
17
+ * A reader that looked at a working tree could only ever see the branch it is
18
+ * standing on -- which is exactly the blind spot that lets two teams take the
19
+ * same number.
20
+ *
21
+ * WHY THE OUTCOMES ARE KEPT DISTINCT
22
+ * ----------------------------------
23
+ * This reader answers with one of five things, and collapsing any pair of them
24
+ * breaks a consumer:
25
+ *
26
+ * - `undeclared` -- the product ships no `telora.migrations.json`.
27
+ * It opted out. Its teams keep numbering as they
28
+ * do today; nothing should hold or refuse.
29
+ * - `declarationUnreadable` -- a declaration EXISTS and is broken. A claim
30
+ * against this product must be REFUSED with the
31
+ * reason, not granted a guessed number.
32
+ * - `refAbsent` -- the ref does not exist (a deleted branch, a stale
33
+ * `branch_name`). It CLAIMS NOTHING. Holding here
34
+ * would let one stale row block every future claim
35
+ * on the product.
36
+ * - `refUnreadable` -- the ref exists and could not be read. It PROVES
37
+ * NOTHING. This is the one that must hold: read as
38
+ * an empty inventory it would say "every number is
39
+ * free", failing open on precisely the question
40
+ * this reader exists to answer.
41
+ * - `read` -- the ordinals, tags and journal entries the ref
42
+ * holds. Possibly EMPTY, which is a real answer:
43
+ * "this ref holds no migrations".
44
+ *
45
+ * The last two are the load-bearing pair. `refAbsent` and `refUnreadable` are
46
+ * both "git said no", and a reader that returned one kind for both would force
47
+ * its caller either to hold on a deleted branch forever or to treat an I/O
48
+ * failure as proof of absence. So absence is POSITIVELY ESTABLISHED rather than
49
+ * inferred from a non-zero exit: `probeRefExists` is three-valued, and only a
50
+ * healthy repository that still cannot resolve the ref yields `refAbsent`.
51
+ * Everything else -- including a probe that itself failed -- is `refUnreadable`.
52
+ * Both probes run only on the failure path, so the happy path pays nothing.
53
+ *
54
+ * SCOPE: THIS IS A READER
55
+ * -----------------------
56
+ * It answers "what does this ref hold?" for ONE ref. It does not compare two
57
+ * refs, judge a merge, or render a notice -- a comparison at the merge is the
58
+ * shape this focus abandoned (two teams told to renumber from the same union at
59
+ * the same moment collide again). The allocator consumes this to compute a
60
+ * candidate; nothing here decides anything.
61
+ *
62
+ * WHY THE GIT PRIMITIVE COMES FROM daemon-core
63
+ * --------------------------------------------
64
+ * `runGit` is taken from `@telora/daemon-core` rather than the daemon's own
65
+ * `git/types.ts` wrapper: this area is a rank-0 leaf in
66
+ * `scripts/daemon-layer-ranks.json` and `git/` sits above it, so importing the
67
+ * wrapper would be an upward import. The wrapper adds only a default timeout,
68
+ * which is passed explicitly here.
69
+ *
70
+ * @module migration-inventory
71
+ */
72
+ import { runGit, DEFAULT_GIT_TIMEOUT_MS, } from '@telora/daemon-core';
73
+ import { readMigrationManifest, } from './migration-manifest.js';
74
+ /** Real git + real declaration reader. */
75
+ export function defaultMigrationInventoryDeps() {
76
+ return {
77
+ runGit: (args, repoPath) => runGit(args, repoPath, DEFAULT_GIT_TIMEOUT_MS),
78
+ readManifest: (repoPath, fs) => readMigrationManifest(repoPath, fs),
79
+ };
80
+ }
81
+ /**
82
+ * Parse a journal document into its entries.
83
+ *
84
+ * Accepts the two shapes the hand-written products ship: a bare array of
85
+ * entries, or an object carrying an `entries` array. Anything else -- including
86
+ * an entry missing `idx` or `tag` -- is a parse FAILURE, because a journal we
87
+ * cannot read is the unreadable state, never an empty registration.
88
+ */
89
+ export function parseJournal(text) {
90
+ let parsed;
91
+ try {
92
+ parsed = JSON.parse(text);
93
+ }
94
+ catch (err) {
95
+ return { error: `not valid JSON (${String(err?.message ?? err)})` };
96
+ }
97
+ const rawEntries = Array.isArray(parsed)
98
+ ? parsed
99
+ : typeof parsed === 'object' && parsed !== null
100
+ ? parsed.entries
101
+ : undefined;
102
+ if (!Array.isArray(rawEntries)) {
103
+ return { error: 'expected an array of entries, or an object with an "entries" array' };
104
+ }
105
+ const entries = [];
106
+ for (const [position, raw] of rawEntries.entries()) {
107
+ if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {
108
+ return { error: `entry ${position} is not an object` };
109
+ }
110
+ const entry = raw;
111
+ const { idx, tag } = entry;
112
+ if (typeof idx !== 'number' && typeof idx !== 'string') {
113
+ return { error: `entry ${position} has no usable "idx" (got ${typeof idx})` };
114
+ }
115
+ if (typeof tag !== 'string' || tag.trim() === '') {
116
+ return { error: `entry ${position} has no usable "tag"` };
117
+ }
118
+ entries.push({ idx: String(idx), tag });
119
+ }
120
+ return { entries };
121
+ }
122
+ /**
123
+ * Which of a ref's listed files are migrations, read through the declared
124
+ * pattern. Pure.
125
+ *
126
+ * A file in the migration dir that does NOT match the pattern is not a
127
+ * migration (a README, a .gitkeep, a `meta/` subdirectory) -- not a failure.
128
+ */
129
+ export function readMigrationsFromPaths(paths, manifest) {
130
+ const pattern = new RegExp(manifest.filePattern);
131
+ const migrations = [];
132
+ for (const path of paths) {
133
+ const basename = path.slice(path.lastIndexOf('/') + 1);
134
+ const match = pattern.exec(basename);
135
+ const ordinal = match?.groups?.ordinal;
136
+ const tag = match?.groups?.tag;
137
+ if (ordinal === undefined || tag === undefined)
138
+ continue;
139
+ migrations.push({ ordinal, tag, path });
140
+ }
141
+ return migrations;
142
+ }
143
+ /**
144
+ * Does `ref` name a commit in this repo -- and could we even tell?
145
+ *
146
+ * THREE-VALUED ON PURPOSE. `git rev-parse --verify --quiet` exits non-zero for
147
+ * BOTH a missing ref and an operational failure (a corrupt object store, a
148
+ * timeout, a repo that is not there). Collapsing those to a boolean is a
149
+ * fail-OPEN: an unreadable branch is reported as holding nothing, the held set
150
+ * still reads complete, and the allocator grants a number that branch already
151
+ * holds -- which is the exact failure the reader exists to prevent.
152
+ *
153
+ * ABSENCE MUST BE POSITIVELY ESTABLISHED, and that takes two facts: the
154
+ * REPOSITORY answers, and the REF does not. So a failed ref probe is followed
155
+ * by a repo-health probe; only a healthy repo that still cannot resolve the ref
156
+ * proves the ref is gone. Anything else is `indeterminate`, and the caller
157
+ * treats that as unreadable.
158
+ *
159
+ * `--quiet` suppresses git's missing-ref message, so a genuine absence writes
160
+ * NOTHING to stderr. Output on stderr is therefore a second, independent signal
161
+ * that something operational went wrong, and is honoured even if the health
162
+ * probe happens to pass.
163
+ *
164
+ * Called ONLY after a read of the ref has already failed, so the happy path
165
+ * pays nothing for the distinction.
166
+ */
167
+ export function probeRefExists(repoPath, ref, deps) {
168
+ const probe = deps.runGit(['rev-parse', '--verify', '--quiet', `${ref}^{commit}`], repoPath);
169
+ if (probe.success)
170
+ return 'present';
171
+ // Under --quiet a missing ref is silent; anything git said is operational.
172
+ if (probe.error.trim() !== '')
173
+ return 'indeterminate';
174
+ // The ref did not resolve. Can the REPOSITORY still answer at all?
175
+ const health = deps.runGit(['rev-parse', '--git-dir'], repoPath);
176
+ return health.success ? 'absent' : 'indeterminate';
177
+ }
178
+ /** Everything git printed about a failure, or a generic note when it printed nothing. */
179
+ function gitFailureText(result) {
180
+ return result.error.trim() || result.output.trim() || 'git failed with no output';
181
+ }
182
+ /** Read the journal the declaration names at `ref`; undefined when none is declared. */
183
+ function readJournalAtRef(repoPath, ref, manifest, deps) {
184
+ if (manifest.journal === undefined)
185
+ return { entries: undefined };
186
+ const journalPath = manifest.journal.path;
187
+ const shown = deps.runGit(['show', `${ref}:${journalPath}`], repoPath);
188
+ if (!shown.success) {
189
+ return {
190
+ error: `the journal ${journalPath} could not be read at ${ref} (${gitFailureText(shown)})`,
191
+ };
192
+ }
193
+ const parsed = parseJournal(shown.output);
194
+ if ('error' in parsed) {
195
+ return { error: `the journal ${journalPath} at ${ref} could not be parsed: ${parsed.error}` };
196
+ }
197
+ return { entries: parsed.entries };
198
+ }
199
+ /**
200
+ * What `ref` holds in `repoPath`'s declared migration namespace.
201
+ *
202
+ * Never throws. A malformed declaration, a missing ref, a failed git call and an
203
+ * unparseable journal each come back as their OWN kind, so a caller can refuse
204
+ * on a broken declaration, ignore an absent ref, and hold on an unreadable one
205
+ * without having to guess which it got.
206
+ *
207
+ * @param repoPath The product's repo root (`config.products[].repoPath`).
208
+ * @param ref Any git ref -- a branch name or a sha. Need not be checked out.
209
+ * @param deps Injectable git + declaration seams; defaults to the real ones.
210
+ */
211
+ export function readMigrationInventory(repoPath, ref, deps = defaultMigrationInventoryDeps()) {
212
+ let manifest;
213
+ try {
214
+ manifest = deps.readManifest(repoPath);
215
+ }
216
+ catch (err) {
217
+ return { kind: 'declarationUnreadable', reason: String(err?.message ?? err) };
218
+ }
219
+ if (manifest === undefined)
220
+ return { kind: 'undeclared' };
221
+ const listed = deps.runGit(['ls-tree', '-r', '--name-only', ref, '--', manifest.dir], repoPath);
222
+ if (!listed.success) {
223
+ // git said no. Which no? A branch that is gone holds nothing; a branch that
224
+ // could not be read proves nothing. Only the second may hold a caller -- and
225
+ // ONLY a positively established absence takes the first path. An
226
+ // indeterminate probe is a failure, never an absence.
227
+ const probe = probeRefExists(repoPath, ref, deps);
228
+ if (probe === 'absent')
229
+ return { kind: 'refAbsent', ref };
230
+ return {
231
+ kind: 'refUnreadable',
232
+ ref,
233
+ reason: probe === 'indeterminate'
234
+ ? `${ref} could not be read, and whether it exists could not be established `
235
+ + `(${gitFailureText(listed)})`
236
+ : `the migrations under ${manifest.dir} could not be listed at ${ref} `
237
+ + `(${gitFailureText(listed)})`,
238
+ };
239
+ }
240
+ const paths = listed.output.split('\n').map((line) => line.trim()).filter(Boolean);
241
+ const journal = readJournalAtRef(repoPath, ref, manifest, deps);
242
+ if ('error' in journal)
243
+ return { kind: 'refUnreadable', ref, reason: journal.error };
244
+ return {
245
+ kind: 'read',
246
+ inventory: {
247
+ ref,
248
+ migrations: readMigrationsFromPaths(paths, manifest),
249
+ journal: journal.entries,
250
+ },
251
+ };
252
+ }
253
+ //# sourceMappingURL=migration-inventory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migration-inventory.js","sourceRoot":"","sources":["../../src/migrations/migration-inventory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Filename of the repo-committed migration declaration, at the REPO ROOT.
3
+ *
4
+ * Repo-root for the same reason `telora.verify.json` is: `.gitignore` excludes
5
+ * `.telora/` wholesale and git cannot re-include a file whose parent directory
6
+ * is excluded, so a declaration nested there could never be tracked.
7
+ */
8
+ export declare const MIGRATION_MANIFEST_FILENAME = "telora.migrations.json";
9
+ /**
10
+ * How a product's migration numbers are chosen -- the field the allocator
11
+ * branches on.
12
+ *
13
+ * `sequential`: the number IS the position in a strictly ordered sequence; the
14
+ * next free one is the highest held plus one.
15
+ * `timestamp`: the number is the moment the migration was authored; the next
16
+ * one is the current time in the declared format.
17
+ */
18
+ export type MigrationScheme = 'sequential' | 'timestamp';
19
+ /** Every scheme this reader accepts, in the order an error message lists them. */
20
+ export declare const MIGRATION_SCHEMES: readonly MigrationScheme[];
21
+ /**
22
+ * How a journal file registers migrations, when the repo has one.
23
+ *
24
+ * `path` is repo-relative. The file's CONTENT shape is the drizzle journal the
25
+ * hand-written products already use: either a bare array of entries, or an
26
+ * object with an `entries` array, each entry carrying an `idx` and a `tag`.
27
+ * Reading that content is the ref reader's job, not this module's -- here the
28
+ * repo only says WHERE the journal is.
29
+ */
30
+ export interface MigrationJournalDeclaration {
31
+ /** Repo-relative path of the journal file. */
32
+ path: string;
33
+ }
34
+ /** A repo's declared migration numbering. */
35
+ export interface MigrationManifest {
36
+ /** Repo-relative directory holding the migration files. */
37
+ dir: string;
38
+ /**
39
+ * Pattern matched against each migration's BASENAME, with two named capture
40
+ * groups: `ordinal` (the migration's number) and `tag` (its name).
41
+ */
42
+ filePattern: string;
43
+ /** Whether numbers are positions in a sequence or authoring timestamps. */
44
+ scheme: MigrationScheme;
45
+ /** The journal that registers migrations, when the repo keeps one. */
46
+ journal?: MigrationJournalDeclaration;
47
+ }
48
+ /**
49
+ * Injectable file-system seam (existence + read), mirroring
50
+ * `verification-manifest.ts`. Tests pass fakes; no consumer has to touch a real
51
+ * repo to exercise the reader.
52
+ */
53
+ export interface MigrationManifestFs {
54
+ /** True iff a readable declaration exists at `path`. */
55
+ exists(path: string): boolean;
56
+ /** Read `path` as UTF-8 text. May throw (an unreadable declaration is loud). */
57
+ readText(path: string): string;
58
+ }
59
+ /** Real-fs default for {@link MigrationManifestFs}. */
60
+ export declare const defaultMigrationManifestFs: MigrationManifestFs;
61
+ /** Absolute path of the migration declaration for a repo. */
62
+ export declare function resolveMigrationManifestPath(repoPath: string): string;
63
+ /**
64
+ * Thrown when a declaration EXISTS and cannot be read.
65
+ *
66
+ * A distinct class, not a bare Error, because the difference between "this
67
+ * product declared nothing" and "this product's declaration is broken" has to
68
+ * survive all the way to the team that asked for a number: the first is a
69
+ * product that opted out, the second is a bug someone must fix. A consumer that
70
+ * cannot tell them apart can only collapse both into a guess.
71
+ */
72
+ export declare class MigrationManifestError extends Error {
73
+ /** Absolute path of the declaration that could not be read. */
74
+ readonly manifestPath: string;
75
+ constructor(manifestPath: string, problem: string);
76
+ }
77
+ /**
78
+ * Read the repo's migration declaration.
79
+ *
80
+ * @param repoPath Product repo root (`configForProduct` sets this per product).
81
+ * Undefined => no repo to read => `undefined`.
82
+ * @param fs Injectable fs seam; defaults to real fs.
83
+ * @returns The parsed declaration, or `undefined` when the repo declares no
84
+ * migration numbering at all.
85
+ * @throws {@link MigrationManifestError} naming the file path when the
86
+ * declaration exists but is unreadable / not JSON / not an object /
87
+ * missing `dir`, `scheme` or a `filePattern` carrying both named
88
+ * groups / carrying a malformed `journal`.
89
+ */
90
+ export declare function readMigrationManifest(repoPath: string | undefined, fs?: MigrationManifestFs): MigrationManifest | undefined;
91
+ /**
92
+ * The ordinal width the DECLARATION fixes, or null when it fixes none.
93
+ *
94
+ * WHY THE DECLARATION IS ASKED THIS AT ALL. A journal registers `idx` as a bare
95
+ * number (351) while the filename carries it padded (`0351`), so unioning the
96
+ * two needs a width to normalise to. The filenames were the only source of one
97
+ * -- and a ref whose only surviving evidence is a journal entry (files removed,
98
+ * a journal written ahead of them) has no filenames. The raw `351` then went
99
+ * into the held set, the candidate rule read the width of what it was given,
100
+ * derived 3, and proposed `352` on a product whose migrations must match
101
+ * `^\d{4}`. A number that cannot be the name of a migration on this product is
102
+ * worse than a refusal: it is granted, written, and discovered by whoever runs
103
+ * the migrations.
104
+ *
105
+ * The declaration knew the width the whole time. `^(?<ordinal>\d{4})_` says
106
+ * four digits, and it says it authoritatively: a filename of any other width
107
+ * does not match the pattern, so no inventoried file can disagree with it.
108
+ *
109
+ * Null when the pattern does not FIX a width (`\d+`, a character class, an
110
+ * alternation). Then the filenames are the authority, as they were -- this adds
111
+ * a source, it does not replace one.
112
+ */
113
+ export declare function declaredOrdinalWidth(filePattern: string): number | null;
114
+ //# sourceMappingURL=migration-manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migration-manifest.d.ts","sourceRoot":"","sources":["../../src/migrations/migration-manifest.ts"],"names":[],"mappings":"AA4FA;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,2BAA2B,CAAC;AAEpE;;;;;;;;GAQG;AACH,MAAM,MAAM,eAAe,GAAG,YAAY,GAAG,WAAW,CAAC;AAEzD,kFAAkF;AAClF,eAAO,MAAM,iBAAiB,EAAE,SAAS,eAAe,EAAgC,CAAC;AAEzF;;;;;;;;GAQG;AACH,MAAM,WAAW,2BAA2B;IAC1C,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;CACd;AAED,6CAA6C;AAC7C,MAAM,WAAW,iBAAiB;IAChC,2DAA2D;IAC3D,GAAG,EAAE,MAAM,CAAC;IACZ;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,2EAA2E;IAC3E,MAAM,EAAE,eAAe,CAAC;IACxB,sEAAsE;IACtE,OAAO,CAAC,EAAE,2BAA2B,CAAC;CACvC;AAED;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,wDAAwD;IACxD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC9B,gFAAgF;IAChF,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;CAChC;AAED,uDAAuD;AACvD,eAAO,MAAM,0BAA0B,EAAE,mBAGxC,CAAC;AAEF,6DAA6D;AAC7D,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAErE;AAED;;;;;;;;GAQG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;IAC/C,+DAA+D;IAC/D,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;gBAElB,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAWlD;AAiGD;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,EAAE,GAAE,mBAAgD,GACnD,iBAAiB,GAAG,SAAS,CAkC/B;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAWvE"}