@telora/daemon 0.22.187 → 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.
- package/build-info.json +6 -3
- package/dist/backends/index.d.ts.map +1 -1
- package/dist/backends/index.js +2 -0
- package/dist/backends/index.js.map +1 -1
- package/dist/completion/completion-fact-readers.d.ts +35 -0
- package/dist/completion/completion-fact-readers.d.ts.map +1 -0
- package/dist/completion/completion-fact-readers.js +70 -0
- package/dist/completion/completion-fact-readers.js.map +1 -0
- package/dist/completion/escalations.d.ts.map +1 -1
- package/dist/completion/escalations.js +2 -1
- package/dist/completion/escalations.js.map +1 -1
- package/dist/completion/event-dep-defaults.d.ts +27 -0
- package/dist/completion/event-dep-defaults.d.ts.map +1 -0
- package/dist/completion/event-dep-defaults.js +30 -0
- package/dist/completion/event-dep-defaults.js.map +1 -0
- package/dist/completion/event-phases.d.ts.map +1 -1
- package/dist/completion/event-phases.js +5 -13
- package/dist/completion/event-phases.js.map +1 -1
- package/dist/completion/event.d.ts +32 -0
- package/dist/completion/event.d.ts.map +1 -1
- package/dist/completion/event.js +31 -25
- package/dist/completion/event.js.map +1 -1
- package/dist/completion/review-exit-phase.d.ts.map +1 -1
- package/dist/completion/review-exit-phase.js +11 -4
- package/dist/completion/review-exit-phase.js.map +1 -1
- package/dist/completion/session-hold.d.ts +13 -1
- package/dist/completion/session-hold.d.ts.map +1 -1
- package/dist/completion/session-hold.js +18 -0
- package/dist/completion/session-hold.js.map +1 -1
- package/dist/completion/standing-approval-convergence.d.ts.map +1 -1
- package/dist/completion/standing-approval-convergence.js +2 -1
- package/dist/completion/standing-approval-convergence.js.map +1 -1
- package/dist/completion/team-phase.d.ts.map +1 -1
- package/dist/completion/team-phase.js +1 -0
- package/dist/completion/team-phase.js.map +1 -1
- package/dist/completion/unclaimed-migration-notice-phase.d.ts +97 -0
- package/dist/completion/unclaimed-migration-notice-phase.d.ts.map +1 -0
- package/dist/completion/unclaimed-migration-notice-phase.js +270 -0
- package/dist/completion/unclaimed-migration-notice-phase.js.map +1 -0
- package/dist/directive/directive-executor.d.ts.map +1 -1
- package/dist/directive/directive-executor.js +1 -20
- package/dist/directive/directive-executor.js.map +1 -1
- package/dist/focus/focus-executor.d.ts +1 -0
- package/dist/focus/focus-executor.d.ts.map +1 -1
- package/dist/focus/focus-executor.js +18 -17
- package/dist/focus/focus-executor.js.map +1 -1
- package/dist/focus/focus-loop-registry.js +2 -2
- package/dist/focus/focus-loop-registry.js.map +1 -1
- package/dist/focus/focus-team-state.js +1 -1
- package/dist/focus/listener-spawn.d.ts.map +1 -1
- package/dist/focus/listener-spawn.js +5 -20
- package/dist/focus/listener-spawn.js.map +1 -1
- package/dist/focus/listener-state.d.ts.map +1 -1
- package/dist/focus/listener-state.js +1 -0
- package/dist/focus/listener-state.js.map +1 -1
- package/dist/focus/listener.d.ts +1 -0
- package/dist/focus/listener.d.ts.map +1 -1
- package/dist/focus/listener.js +9 -0
- package/dist/focus/listener.js.map +1 -1
- package/dist/focus/migration-claim-pass.d.ts +50 -0
- package/dist/focus/migration-claim-pass.d.ts.map +1 -0
- package/dist/focus/migration-claim-pass.js +234 -0
- package/dist/focus/migration-claim-pass.js.map +1 -0
- package/dist/focus/pending-spawn-pass-deps.d.ts.map +1 -1
- package/dist/focus/pending-spawn-pass-deps.js +2 -1
- package/dist/focus/pending-spawn-pass-deps.js.map +1 -1
- package/dist/focus/team-actionable-work.d.ts +21 -0
- package/dist/focus/team-actionable-work.d.ts.map +1 -1
- package/dist/focus/team-actionable-work.js +3 -0
- package/dist/focus/team-actionable-work.js.map +1 -1
- package/dist/git/types.d.ts.map +1 -1
- package/dist/git/types.js +2 -1
- package/dist/git/types.js.map +1 -1
- package/dist/loops/llm-client.d.ts +1 -1
- package/dist/loops/llm-client.d.ts.map +1 -1
- package/dist/loops/llm-client.js +29 -106
- package/dist/loops/llm-client.js.map +1 -1
- package/dist/migrations/migration-claim-lifecycle.d.ts +166 -0
- package/dist/migrations/migration-claim-lifecycle.d.ts.map +1 -0
- package/dist/migrations/migration-claim-lifecycle.js +198 -0
- package/dist/migrations/migration-claim-lifecycle.js.map +1 -0
- package/dist/migrations/migration-held-set.d.ts +171 -0
- package/dist/migrations/migration-held-set.d.ts.map +1 -0
- package/dist/migrations/migration-held-set.js +295 -0
- package/dist/migrations/migration-held-set.js.map +1 -0
- package/dist/migrations/migration-inventory.d.ts +187 -0
- package/dist/migrations/migration-inventory.d.ts.map +1 -0
- package/dist/migrations/migration-inventory.js +253 -0
- package/dist/migrations/migration-inventory.js.map +1 -0
- package/dist/migrations/migration-manifest.d.ts +114 -0
- package/dist/migrations/migration-manifest.d.ts.map +1 -0
- package/dist/migrations/migration-manifest.js +295 -0
- package/dist/migrations/migration-manifest.js.map +1 -0
- package/dist/prompt/team-prompt-base.js +1 -1
- package/dist/prompt-sections/migration-numbering-section.d.ts +58 -0
- package/dist/prompt-sections/migration-numbering-section.d.ts.map +1 -0
- package/dist/prompt-sections/migration-numbering-section.js +70 -0
- package/dist/prompt-sections/migration-numbering-section.js.map +1 -0
- package/dist/prompt-sections/migration-numbering-state.d.ts +30 -0
- package/dist/prompt-sections/migration-numbering-state.d.ts.map +1 -0
- package/dist/prompt-sections/migration-numbering-state.js +24 -0
- package/dist/prompt-sections/migration-numbering-state.js.map +1 -0
- package/dist/prompt-sections/unclaimed-migration-section.d.ts +38 -0
- package/dist/prompt-sections/unclaimed-migration-section.d.ts.map +1 -0
- package/dist/prompt-sections/unclaimed-migration-section.js +57 -0
- package/dist/prompt-sections/unclaimed-migration-section.js.map +1 -0
- package/dist/queries/agent-resolvable-promotion.d.ts +12 -0
- package/dist/queries/agent-resolvable-promotion.d.ts.map +1 -1
- package/dist/queries/agent-resolvable-promotion.js +17 -6
- package/dist/queries/agent-resolvable-promotion.js.map +1 -1
- package/dist/queries/claim-pass-focuses.d.ts +19 -0
- package/dist/queries/claim-pass-focuses.d.ts.map +1 -0
- package/dist/queries/claim-pass-focuses.js +72 -0
- package/dist/queries/claim-pass-focuses.js.map +1 -0
- package/dist/queries/daemon-connection.d.ts.map +1 -1
- package/dist/queries/daemon-connection.js +1 -0
- package/dist/queries/daemon-connection.js.map +1 -1
- package/dist/queries/delivery-gate-state.d.ts +3 -0
- package/dist/queries/delivery-gate-state.d.ts.map +1 -1
- package/dist/queries/delivery-gate-state.js +1 -1
- package/dist/queries/delivery-gate-state.js.map +1 -1
- package/dist/queries/focus-discovery.d.ts +25 -2
- package/dist/queries/focus-discovery.d.ts.map +1 -1
- package/dist/queries/focus-discovery.js +16 -9
- package/dist/queries/focus-discovery.js.map +1 -1
- package/dist/queries/focus-unclaimed-migration.d.ts +146 -0
- package/dist/queries/focus-unclaimed-migration.d.ts.map +1 -0
- package/dist/queries/focus-unclaimed-migration.js +266 -0
- package/dist/queries/focus-unclaimed-migration.js.map +1 -0
- package/dist/queries/live-focus-branches.d.ts +32 -0
- package/dist/queries/live-focus-branches.d.ts.map +1 -0
- package/dist/queries/live-focus-branches.js +66 -0
- package/dist/queries/live-focus-branches.js.map +1 -0
- package/dist/queries/migration-claims.d.ts +25 -0
- package/dist/queries/migration-claims.d.ts.map +1 -0
- package/dist/queries/migration-claims.js +79 -0
- package/dist/queries/migration-claims.js.map +1 -0
- package/dist/queries/schemas.d.ts +2 -0
- package/dist/queries/schemas.d.ts.map +1 -1
- package/dist/queries/schemas.js +6 -0
- package/dist/queries/schemas.js.map +1 -1
- package/dist/queries/shared.js +1 -1
- package/dist/queries/verify-own-holds.d.ts +89 -0
- package/dist/queries/verify-own-holds.d.ts.map +1 -0
- package/dist/queries/verify-own-holds.js +93 -0
- package/dist/queries/verify-own-holds.js.map +1 -0
- package/dist/session/session-lineage.d.ts.map +1 -1
- package/dist/session/session-lineage.js +3 -0
- package/dist/session/session-lineage.js.map +1 -1
- package/dist/shell/heartbeat-metadata.d.ts +48 -2
- package/dist/shell/heartbeat-metadata.d.ts.map +1 -1
- package/dist/shell/heartbeat-metadata.js +80 -3
- package/dist/shell/heartbeat-metadata.js.map +1 -1
- package/dist/shell/heartbeat.d.ts +13 -0
- package/dist/shell/heartbeat.d.ts.map +1 -1
- package/dist/shell/heartbeat.js +31 -10
- package/dist/shell/heartbeat.js.map +1 -1
- package/dist/shell/mcp-resolution.js +1 -1
- package/dist/shell/mcp-resolution.js.map +1 -1
- package/dist/shell/migration-held-set-publish.d.ts +170 -0
- package/dist/shell/migration-held-set-publish.d.ts.map +1 -0
- package/dist/shell/migration-held-set-publish.js +172 -0
- package/dist/shell/migration-held-set-publish.js.map +1 -0
- package/dist/shell/worker-pidfile.d.ts.map +1 -1
- package/dist/shell/worker-pidfile.js +1 -0
- package/dist/shell/worker-pidfile.js.map +1 -1
- package/dist/spawn/helpers.d.ts.map +1 -1
- package/dist/spawn/helpers.js +1 -0
- package/dist/spawn/helpers.js.map +1 -1
- package/dist/spawn/lead-launch.d.ts.map +1 -1
- package/dist/spawn/lead-launch.js +1 -0
- package/dist/spawn/lead-launch.js.map +1 -1
- package/dist/spawn/lead-process-ownership.d.ts.map +1 -1
- package/dist/spawn/lead-process-ownership.js +1 -0
- package/dist/spawn/lead-process-ownership.js.map +1 -1
- package/dist/spawn/one-shot-model.d.ts +145 -0
- package/dist/spawn/one-shot-model.d.ts.map +1 -0
- package/dist/spawn/one-shot-model.js +329 -0
- package/dist/spawn/one-shot-model.js.map +1 -0
- package/dist/spawn/process-group-kill.d.ts.map +1 -1
- package/dist/spawn/process-group-kill.js +1 -0
- package/dist/spawn/process-group-kill.js.map +1 -1
- package/dist/spawn/process-spawn.d.ts +18 -1
- package/dist/spawn/process-spawn.d.ts.map +1 -1
- package/dist/spawn/process-spawn.js +11 -6
- package/dist/spawn/process-spawn.js.map +1 -1
- package/dist/spawn/prompt-assembly.d.ts +115 -18
- package/dist/spawn/prompt-assembly.d.ts.map +1 -1
- package/dist/spawn/prompt-assembly.js +121 -2
- package/dist/spawn/prompt-assembly.js.map +1 -1
- package/dist/spawn/review-hold-park.d.ts.map +1 -1
- package/dist/spawn/review-hold-park.js +7 -3
- package/dist/spawn/review-hold-park.js.map +1 -1
- package/dist/spawn/routing-decider-spawn.d.ts +30 -0
- package/dist/spawn/routing-decider-spawn.d.ts.map +1 -1
- package/dist/spawn/routing-decider-spawn.js +84 -2
- package/dist/spawn/routing-decider-spawn.js.map +1 -1
- package/dist/spawn/routing-decider.d.ts.map +1 -1
- package/dist/spawn/routing-decider.js +22 -0
- package/dist/spawn/routing-decider.js.map +1 -1
- package/dist/spawn/spawn-and-acknowledge.d.ts +61 -0
- package/dist/spawn/spawn-and-acknowledge.d.ts.map +1 -0
- package/dist/spawn/spawn-and-acknowledge.js +117 -0
- package/dist/spawn/spawn-and-acknowledge.js.map +1 -0
- package/dist/spawn/spawn-cooldown.d.ts +15 -1
- package/dist/spawn/spawn-cooldown.d.ts.map +1 -1
- package/dist/spawn/spawn-cooldown.js +67 -19
- package/dist/spawn/spawn-cooldown.js.map +1 -1
- package/dist/spawn/spawn-decision.d.ts +10 -10
- package/dist/spawn/spawn-decision.d.ts.map +1 -1
- package/dist/spawn/spawn-decision.js +33 -5
- package/dist/spawn/spawn-decision.js.map +1 -1
- package/dist/templates/claude-md.d.ts +1 -1
- package/dist/templates/claude-md.d.ts.map +1 -1
- package/dist/templates/claude-md.js +24 -0
- package/dist/templates/claude-md.js.map +1 -1
- package/dist/testing/fixtures/index.js +1 -1
- package/dist/testing/fixtures/migration-namespace-repo.d.ts +63 -0
- package/dist/testing/fixtures/migration-namespace-repo.d.ts.map +1 -0
- package/dist/testing/fixtures/migration-namespace-repo.js +164 -0
- package/dist/testing/fixtures/migration-namespace-repo.js.map +1 -0
- package/dist/testing/fixtures/stubborn-lead-process.d.ts.map +1 -1
- package/dist/testing/fixtures/stubborn-lead-process.js +1 -0
- package/dist/testing/fixtures/stubborn-lead-process.js.map +1 -1
- package/dist/types/index.js +1 -1
- package/dist/verify/ai-inspection-runner.d.ts.map +1 -1
- package/dist/verify/ai-inspection-runner.js +26 -7
- package/dist/verify/ai-inspection-runner.js.map +1 -1
- package/dist/verify/de-restatement.d.ts +9 -2
- package/dist/verify/de-restatement.d.ts.map +1 -1
- package/dist/verify/de-restatement.js +14 -76
- package/dist/verify/de-restatement.js.map +1 -1
- package/dist/verify/default-on-guards.d.ts +5 -2
- package/dist/verify/default-on-guards.d.ts.map +1 -1
- package/dist/verify/default-on-guards.js +5 -3
- package/dist/verify/default-on-guards.js.map +1 -1
- package/dist/verify/no-own-delta-verdict.d.ts +16 -2
- package/dist/verify/no-own-delta-verdict.d.ts.map +1 -1
- package/dist/verify/no-own-delta-verdict.js +18 -2
- package/dist/verify/no-own-delta-verdict.js.map +1 -1
- package/dist/verify/verification-deps-default.d.ts.map +1 -1
- package/dist/verify/verification-deps-default.js +6 -0
- package/dist/verify/verification-deps-default.js.map +1 -1
- package/dist/verify/verification-deps.d.ts +10 -0
- package/dist/verify/verification-deps.d.ts.map +1 -1
- package/dist/verify/verification-deterministic.d.ts +7 -0
- package/dist/verify/verification-deterministic.d.ts.map +1 -1
- package/dist/verify/verification-deterministic.js.map +1 -1
- package/dist/verify/verification-engine.d.ts +1 -20
- package/dist/verify/verification-engine.d.ts.map +1 -1
- package/dist/verify/verification-engine.js +24 -55
- package/dist/verify/verification-engine.js.map +1 -1
- package/dist/verify/verification-evidence-unavailable-feedback.d.ts +3 -1
- package/dist/verify/verification-evidence-unavailable-feedback.d.ts.map +1 -1
- package/dist/verify/verification-evidence-unavailable-feedback.js +3 -1
- package/dist/verify/verification-evidence-unavailable-feedback.js.map +1 -1
- package/dist/verify/verification-feedback.d.ts +4 -12
- package/dist/verify/verification-feedback.d.ts.map +1 -1
- package/dist/verify/verification-feedback.js +7 -12
- package/dist/verify/verification-feedback.js.map +1 -1
- package/dist/verify/verification-history-reset.d.ts.map +1 -1
- package/dist/verify/verification-history-reset.js +11 -3
- package/dist/verify/verification-history-reset.js.map +1 -1
- package/dist/verify/verification-model-unavailable-hold.d.ts +92 -0
- package/dist/verify/verification-model-unavailable-hold.d.ts.map +1 -0
- package/dist/verify/verification-model-unavailable-hold.js +207 -0
- package/dist/verify/verification-model-unavailable-hold.js.map +1 -0
- package/dist/verify/verification-strategies.d.ts +32 -0
- package/dist/verify/verification-strategies.d.ts.map +1 -0
- package/dist/verify/verification-strategies.js +47 -0
- package/dist/verify/verification-strategies.js.map +1 -0
- package/dist/verify/verify-reconciliation.d.ts.map +1 -1
- package/dist/verify/verify-reconciliation.js +2 -1
- package/dist/verify/verify-reconciliation.js.map +1 -1
- package/dist/verify/verify-restatement.d.ts.map +1 -1
- package/dist/verify/verify-restatement.js +21 -8
- package/dist/verify/verify-restatement.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
// PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
|
|
2
|
+
// Pinned by: packages/daemon/src/migrations/claim-end-to-end.test.ts,
|
|
3
|
+
// packages/daemon/src/migrations/concurrent-claim-regression.test.ts,
|
|
4
|
+
// packages/daemon/src/migrations/held-set-grant-effect.test.ts,
|
|
5
|
+
// packages/daemon/src/migrations/migration-held-set.test.ts,
|
|
6
|
+
// packages/daemon/src/shell/migration-held-set-publish.test.ts
|
|
7
|
+
// PINNED-BY-END
|
|
8
|
+
/**
|
|
9
|
+
* What a product's migration numbers are CURRENTLY held by, across every ref
|
|
10
|
+
* that matters -- integration and every live focus branch.
|
|
11
|
+
*
|
|
12
|
+
* WHY THIS LIVES IN THE DAEMON AND THE GRANT DOES NOT
|
|
13
|
+
* ---------------------------------------------------
|
|
14
|
+
* The two halves of claiming a number sit on opposite sides of a boundary, and
|
|
15
|
+
* neither half can move:
|
|
16
|
+
*
|
|
17
|
+
* - Seeing what is held needs the REPO. It is a git-tree read across several
|
|
18
|
+
* refs, and only the daemon has the repository on disk. The product-api
|
|
19
|
+
* edge function has no git (`sessionStateHandler` says as much: "the server
|
|
20
|
+
* has no git access").
|
|
21
|
+
* - Granting a number needs the DATABASE, atomically. Two teams claiming at
|
|
22
|
+
* once must be separated by a constraint, not by timing, and the constraint
|
|
23
|
+
* is the partial unique index on `product_migration_claims`.
|
|
24
|
+
*
|
|
25
|
+
* So the held set has to cross the gap. The daemon PUBLISHES it -- per product,
|
|
26
|
+
* on the heartbeat, the way `metadata.verificationPostures` is published for
|
|
27
|
+
* the same reason ("posture is workstation-local config the API cannot itself
|
|
28
|
+
* derive; the daemon is the single source") -- and the edge handler grants
|
|
29
|
+
* against the published set plus the live claims it can see itself.
|
|
30
|
+
*
|
|
31
|
+
* THE OTHER SHAPE, AND WHY NOT IT. Serving the whole claim from the daemon was
|
|
32
|
+
* the alternative. It collapses: either the daemon reads-then-inserts, which is
|
|
33
|
+
* the two-worktrees race reborn one altitude up, or it still calls an edge
|
|
34
|
+
* handler to do the atomic insert -- in which case that handler exists anyway
|
|
35
|
+
* and the daemon is an extra hop in front of it. The decision is recorded on
|
|
36
|
+
* the delivery.
|
|
37
|
+
*
|
|
38
|
+
* WHY A STALE SNAPSHOT IS SAFE, AND WHERE IT IS NOT
|
|
39
|
+
* -------------------------------------------------
|
|
40
|
+
* The published set is only ever an INPUT TO THE CANDIDATE. It never decides
|
|
41
|
+
* anything: the unique index decides. A snapshot read a minute ago can make the
|
|
42
|
+
* handler propose a number that a concurrent claim just took -- and the insert
|
|
43
|
+
* fails, and the handler recomputes. A stale snapshot costs a retry; it cannot
|
|
44
|
+
* cost a collision.
|
|
45
|
+
*
|
|
46
|
+
* What it CAN cost is a number granted against branch tips that have since
|
|
47
|
+
* moved -- a migration landed on a sibling branch after the snapshot was taken,
|
|
48
|
+
* and no claim was recorded for it (a team that skipped the rule). That is why
|
|
49
|
+
* every reading carries `tips`: the exact sha each ref was at when it was read.
|
|
50
|
+
* The confirm pass re-checks a claim whose snapshot predates the tips its branch
|
|
51
|
+
* actually had. A snapshot with no recorded staleness is a silent read-then-write
|
|
52
|
+
* race wearing a different hat, so the tips are not optional decoration.
|
|
53
|
+
*
|
|
54
|
+
* WHAT AN UNREADABLE REF DOES
|
|
55
|
+
* ---------------------------
|
|
56
|
+
* It makes the whole reading INCOMPLETE, and the reading says so. A ref that
|
|
57
|
+
* could not be read proves nothing about what it holds, and a held set quietly
|
|
58
|
+
* missing one branch's numbers tells the allocator those numbers are free --
|
|
59
|
+
* failing open on exactly the question being asked. An incomplete reading is
|
|
60
|
+
* published as incomplete and the handler refuses to grant against it.
|
|
61
|
+
*
|
|
62
|
+
* A ref that does not EXIST is different and is not incompleteness: a deleted
|
|
63
|
+
* branch holds nothing. That distinction is the reader's (`migration-inventory`)
|
|
64
|
+
* and is simply carried through here.
|
|
65
|
+
*
|
|
66
|
+
* @module migration-held-set
|
|
67
|
+
*/
|
|
68
|
+
import { readMigrationInventory, defaultMigrationInventoryDeps, } from './migration-inventory.js';
|
|
69
|
+
import { declaredOrdinalWidth } from './migration-manifest.js';
|
|
70
|
+
/** Real git, real declaration reader, real clock. */
|
|
71
|
+
export function defaultMigrationHeldSetDeps() {
|
|
72
|
+
return { ...defaultMigrationInventoryDeps(), now: () => Date.now() };
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* The ordinals a ref's JOURNAL registers, normalised to the product's width.
|
|
76
|
+
*
|
|
77
|
+
* WHY THE JOURNAL COUNTS AT ALL. On a hand-registered product the journal is
|
|
78
|
+
* half the record: a migration is its numbered filename AND its `idx` entry. A
|
|
79
|
+
* ref can register an idx whose file is not visible -- mid-rename, a file
|
|
80
|
+
* removed but the entry left, a journal edited ahead of the file -- and a held
|
|
81
|
+
* set built from filenames alone reports that number free. The allocator then
|
|
82
|
+
* grants it, and the product ends with two migrations at one position, which is
|
|
83
|
+
* the failure this whole mechanism exists to remove.
|
|
84
|
+
*
|
|
85
|
+
* WHY NORMALISATION IS REQUIRED, NOT COSMETIC. The journal records `idx` as a
|
|
86
|
+
* NUMBER (352) while the filename carries it zero-padded (`0352`). Unioning the
|
|
87
|
+
* two raw would put both `352` and `0352` in the held set -- two widths -- and
|
|
88
|
+
* the candidate rule refuses a product whose ordinals are not one fixed width.
|
|
89
|
+
* A naive union would therefore break every claim on exactly the products this
|
|
90
|
+
* fixes. So a journal idx is padded to the width the product uses.
|
|
91
|
+
*
|
|
92
|
+
* WHEN IT CANNOT BE NORMALISED, THE READING IS INCOMPLETE. An idx wider than
|
|
93
|
+
* the width, one that is not a plain number, and -- the case this argument was
|
|
94
|
+
* missing -- ANY idx at all when no width is established. Dropping such an
|
|
95
|
+
* entry silently is the fail-open; guessing a placement is worse; and using the
|
|
96
|
+
* raw value is worst, because it is neither dropped nor refused but published
|
|
97
|
+
* as though it were a number on this product. It is reported instead, and the
|
|
98
|
+
* caller refuses to grant against the reading rather than granting around the
|
|
99
|
+
* part it could not read.
|
|
100
|
+
*
|
|
101
|
+
* @param width The product's ordinal width, or null when none is established.
|
|
102
|
+
* See `resolveOrdinalWidth`.
|
|
103
|
+
*/
|
|
104
|
+
export function journalOrdinals(journal, width) {
|
|
105
|
+
if (journal === undefined || journal.length === 0) {
|
|
106
|
+
return { ordinals: [], unnormalisable: [] };
|
|
107
|
+
}
|
|
108
|
+
const ordinals = [];
|
|
109
|
+
const unnormalisable = [];
|
|
110
|
+
for (const entry of journal) {
|
|
111
|
+
const idx = entry.idx.trim();
|
|
112
|
+
if (!/^\d+$/.test(idx) || width === null || idx.length > width) {
|
|
113
|
+
unnormalisable.push(entry.idx);
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
ordinals.push(idx.padStart(width, '0'));
|
|
117
|
+
}
|
|
118
|
+
return { ordinals, unnormalisable };
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* The width this product's ordinals are padded to, across the WHOLE reading.
|
|
122
|
+
*
|
|
123
|
+
* A PROPERTY OF THE PRODUCT, NOT OF ONE REF. It used to be derived per ref,
|
|
124
|
+
* from that ref's own filenames -- so a ref holding only a journal entry had no
|
|
125
|
+
* width, and a ref holding files a moment before the reading had one. The width
|
|
126
|
+
* a number is padded to cannot depend on which branch you happen to be looking
|
|
127
|
+
* at; it is the product's numbering convention, and it is read once for the
|
|
128
|
+
* whole reading.
|
|
129
|
+
*
|
|
130
|
+
* Declaration first (it fixes the width authoritatively -- no inventoried file
|
|
131
|
+
* can disagree with a pattern it had to match), then the files, which must all
|
|
132
|
+
* agree. Null when neither establishes one: a product with no declared width
|
|
133
|
+
* and no files. Every journal entry is then unnormalisable and the reading is
|
|
134
|
+
* incomplete, which is the honest answer -- the alternative was publishing a
|
|
135
|
+
* bare `351` and granting `352` into a four-digit namespace.
|
|
136
|
+
*/
|
|
137
|
+
export function resolveOrdinalWidth(declaredWidth, fileOrdinals) {
|
|
138
|
+
if (declaredWidth !== null)
|
|
139
|
+
return declaredWidth;
|
|
140
|
+
const widths = [...new Set(fileOrdinals.map((o) => o.length))];
|
|
141
|
+
return widths.length === 1 ? (widths[0] ?? null) : null;
|
|
142
|
+
}
|
|
143
|
+
/** The commit `ref` points at, or null when it cannot be resolved. */
|
|
144
|
+
function resolveTip(repoPath, ref, deps) {
|
|
145
|
+
const result = deps.runGit(['rev-parse', '--verify', '--quiet', `${ref}^{commit}`], repoPath);
|
|
146
|
+
if (!result.success)
|
|
147
|
+
return null;
|
|
148
|
+
const tip = result.output.trim();
|
|
149
|
+
return tip === '' ? null : tip;
|
|
150
|
+
}
|
|
151
|
+
/** Ask one ref what it holds, or abort the whole reading. */
|
|
152
|
+
function readRefRaw(repoPath, ref, deps) {
|
|
153
|
+
// RESOLVE FIRST, THEN READ AT THE SHA. A branch is a MUTABLE pointer: read
|
|
154
|
+
// through the name and resolve afterwards, and the tip can name a commit
|
|
155
|
+
// the inventory never saw -- a reading that claims a provenance it does not
|
|
156
|
+
// have, which is worse than no provenance at all, because it looks sound.
|
|
157
|
+
// Reading at the resolved sha makes the two agree by construction: a sha is
|
|
158
|
+
// immutable, so nothing the branch does afterwards can move what was read.
|
|
159
|
+
const tip = resolveTip(repoPath, ref, deps);
|
|
160
|
+
if (tip === null) {
|
|
161
|
+
// No immutable point to read at. Not an absence -- `readMigrationInventory`
|
|
162
|
+
// distinguishes that -- so ask it, and let it say which.
|
|
163
|
+
const unresolved = readMigrationInventory(repoPath, ref, deps);
|
|
164
|
+
if (unresolved.kind === 'refAbsent') {
|
|
165
|
+
return { ok: { ref, tip: null, outcome: 'absent' } };
|
|
166
|
+
}
|
|
167
|
+
if (unresolved.kind === 'undeclared')
|
|
168
|
+
return { abort: { kind: 'undeclared' } };
|
|
169
|
+
if (unresolved.kind === 'declarationUnreadable') {
|
|
170
|
+
return { abort: { kind: 'declarationUnreadable', reason: unresolved.reason } };
|
|
171
|
+
}
|
|
172
|
+
// It read through the mutable name but has no sha: an anchorless reading.
|
|
173
|
+
// Its ordinals still count as TAKEN -- dropping them would be the
|
|
174
|
+
// fail-open this module exists to avoid -- but the reading is incomplete,
|
|
175
|
+
// because a claim granted against it could never be re-checked.
|
|
176
|
+
return {
|
|
177
|
+
ok: {
|
|
178
|
+
ref,
|
|
179
|
+
tip: null,
|
|
180
|
+
outcome: 'unreadable',
|
|
181
|
+
...(unresolved.kind === 'read' ? { inventory: unresolved.inventory } : {}),
|
|
182
|
+
reason: `${ref} carries no resolvable sha, so the reading has no point to `
|
|
183
|
+
+ 're-check a claim against',
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
// Everything below is read AT `tip`, never through `ref`.
|
|
188
|
+
const result = readMigrationInventory(repoPath, tip, deps);
|
|
189
|
+
switch (result.kind) {
|
|
190
|
+
case 'read':
|
|
191
|
+
return { ok: { ref, tip, outcome: 'read', inventory: result.inventory } };
|
|
192
|
+
case 'refAbsent':
|
|
193
|
+
// The sha resolved a moment ago and is gone now (a prune between the
|
|
194
|
+
// two calls). That is not an established absence of the BRANCH, so it
|
|
195
|
+
// is treated as unreadable rather than as "holds nothing".
|
|
196
|
+
return {
|
|
197
|
+
ok: {
|
|
198
|
+
ref,
|
|
199
|
+
tip,
|
|
200
|
+
outcome: 'unreadable',
|
|
201
|
+
reason: `${ref} resolved to ${tip}, which was then unreadable`,
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
case 'refUnreadable':
|
|
205
|
+
return { ok: { ref, tip: null, outcome: 'unreadable', reason: result.reason } };
|
|
206
|
+
case 'undeclared':
|
|
207
|
+
case 'declarationUnreadable':
|
|
208
|
+
// The declaration was read once above; a mid-walk change means the
|
|
209
|
+
// reading can no longer be trusted as a whole.
|
|
210
|
+
return {
|
|
211
|
+
abort: result.kind === 'undeclared'
|
|
212
|
+
? { kind: 'undeclared' }
|
|
213
|
+
: { kind: 'declarationUnreadable', reason: result.reason },
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
/** Pass two: normalise one raw reading against the product's width. */
|
|
218
|
+
function foldRefReading(raw, width) {
|
|
219
|
+
const files = raw.inventory?.migrations.map((m) => m.ordinal) ?? [];
|
|
220
|
+
const journal = journalOrdinals(raw.inventory?.journal, width);
|
|
221
|
+
const ordinals = [...new Set([...files, ...journal.ordinals])].sort();
|
|
222
|
+
// An unnormalisable entry is a registered position this reader cannot place
|
|
223
|
+
// in the sequence. Granting around it would be granting against a partial
|
|
224
|
+
// view, so it makes the reading incomplete exactly as an unreadable ref does.
|
|
225
|
+
const unplaceable = journal.unnormalisable.length > 0
|
|
226
|
+
? (width === null
|
|
227
|
+
? `${raw.ref} registers journal entries (${journal.unnormalisable.join(', ')}) but this `
|
|
228
|
+
+ 'product establishes no ordinal width -- no file to read one from and no fixed '
|
|
229
|
+
+ 'width in its declaration -- so they cannot be placed in the sequence'
|
|
230
|
+
: `${raw.ref} registers journal entries whose idx cannot be placed in `
|
|
231
|
+
+ `this product's numbering (${journal.unnormalisable.join(', ')})`)
|
|
232
|
+
: null;
|
|
233
|
+
const reason = raw.reason ?? unplaceable ?? undefined;
|
|
234
|
+
return {
|
|
235
|
+
reading: {
|
|
236
|
+
ref: raw.ref,
|
|
237
|
+
tip: raw.tip,
|
|
238
|
+
outcome: raw.outcome,
|
|
239
|
+
ordinals,
|
|
240
|
+
...(reason !== undefined ? { reason } : {}),
|
|
241
|
+
},
|
|
242
|
+
complete: raw.outcome !== 'unreadable' && unplaceable === null,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Read every ref and fold what they hold into one set.
|
|
247
|
+
*
|
|
248
|
+
* @param repoPath The product's repo root.
|
|
249
|
+
* @param refs Integration plus every live focus branch. Duplicates are fine
|
|
250
|
+
* (a focus whose branch IS integration reads once).
|
|
251
|
+
*/
|
|
252
|
+
export function computeMigrationHeldSet(repoPath, refs, deps = defaultMigrationHeldSetDeps()) {
|
|
253
|
+
let scheme;
|
|
254
|
+
let declaredWidth;
|
|
255
|
+
try {
|
|
256
|
+
const manifest = deps.readManifest(repoPath);
|
|
257
|
+
if (manifest === undefined)
|
|
258
|
+
return { kind: 'undeclared' };
|
|
259
|
+
scheme = manifest.scheme;
|
|
260
|
+
declaredWidth = declaredOrdinalWidth(manifest.filePattern);
|
|
261
|
+
}
|
|
262
|
+
catch (err) {
|
|
263
|
+
return { kind: 'declarationUnreadable', reason: String(err?.message ?? err) };
|
|
264
|
+
}
|
|
265
|
+
const raws = [];
|
|
266
|
+
for (const ref of [...new Set(refs)]) {
|
|
267
|
+
const outcome = readRefRaw(repoPath, ref, deps);
|
|
268
|
+
if ('abort' in outcome)
|
|
269
|
+
return outcome.abort;
|
|
270
|
+
raws.push(outcome.ok);
|
|
271
|
+
}
|
|
272
|
+
const width = resolveOrdinalWidth(declaredWidth, raws.flatMap((raw) => raw.inventory?.migrations.map((m) => m.ordinal) ?? []));
|
|
273
|
+
const readings = [];
|
|
274
|
+
const held = new Set();
|
|
275
|
+
let complete = true;
|
|
276
|
+
for (const raw of raws) {
|
|
277
|
+
const folded = foldRefReading(raw, width);
|
|
278
|
+
if (!folded.complete)
|
|
279
|
+
complete = false;
|
|
280
|
+
for (const ordinal of folded.reading.ordinals)
|
|
281
|
+
held.add(ordinal);
|
|
282
|
+
readings.push(folded.reading);
|
|
283
|
+
}
|
|
284
|
+
return {
|
|
285
|
+
kind: 'held',
|
|
286
|
+
heldSet: {
|
|
287
|
+
scheme,
|
|
288
|
+
held: [...held].sort(),
|
|
289
|
+
readings,
|
|
290
|
+
complete,
|
|
291
|
+
readAt: deps.now(),
|
|
292
|
+
},
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
//# sourceMappingURL=migration-held-set.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration-held-set.js","sourceRoot":"","sources":["../../src/migrations/migration-held-set.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a git REF holds in a product's declared migration namespace.
|
|
3
|
+
*
|
|
4
|
+
* WHY IT READS THE GIT TREE AND NEVER A WORKING TREE
|
|
5
|
+
* --------------------------------------------------
|
|
6
|
+
* The whole point of this module is to answer the question for a branch that is
|
|
7
|
+
* NOT checked out. Two focuses are armed concurrently on one product, each in
|
|
8
|
+
* its own worktree; the allocator that grants a migration number has to know
|
|
9
|
+
* what the OTHER focus's branch already holds, and the daemon never checks that
|
|
10
|
+
* branch out. So every read here is `git ls-tree` / `git show` against a ref.
|
|
11
|
+
*
|
|
12
|
+
* A reader that looked at a working tree could only ever see the branch it is
|
|
13
|
+
* standing on -- which is exactly the blind spot that lets two teams take the
|
|
14
|
+
* same number.
|
|
15
|
+
*
|
|
16
|
+
* WHY THE OUTCOMES ARE KEPT DISTINCT
|
|
17
|
+
* ----------------------------------
|
|
18
|
+
* This reader answers with one of five things, and collapsing any pair of them
|
|
19
|
+
* breaks a consumer:
|
|
20
|
+
*
|
|
21
|
+
* - `undeclared` -- the product ships no `telora.migrations.json`.
|
|
22
|
+
* It opted out. Its teams keep numbering as they
|
|
23
|
+
* do today; nothing should hold or refuse.
|
|
24
|
+
* - `declarationUnreadable` -- a declaration EXISTS and is broken. A claim
|
|
25
|
+
* against this product must be REFUSED with the
|
|
26
|
+
* reason, not granted a guessed number.
|
|
27
|
+
* - `refAbsent` -- the ref does not exist (a deleted branch, a stale
|
|
28
|
+
* `branch_name`). It CLAIMS NOTHING. Holding here
|
|
29
|
+
* would let one stale row block every future claim
|
|
30
|
+
* on the product.
|
|
31
|
+
* - `refUnreadable` -- the ref exists and could not be read. It PROVES
|
|
32
|
+
* NOTHING. This is the one that must hold: read as
|
|
33
|
+
* an empty inventory it would say "every number is
|
|
34
|
+
* free", failing open on precisely the question
|
|
35
|
+
* this reader exists to answer.
|
|
36
|
+
* - `read` -- the ordinals, tags and journal entries the ref
|
|
37
|
+
* holds. Possibly EMPTY, which is a real answer:
|
|
38
|
+
* "this ref holds no migrations".
|
|
39
|
+
*
|
|
40
|
+
* The last two are the load-bearing pair. `refAbsent` and `refUnreadable` are
|
|
41
|
+
* both "git said no", and a reader that returned one kind for both would force
|
|
42
|
+
* its caller either to hold on a deleted branch forever or to treat an I/O
|
|
43
|
+
* failure as proof of absence. So absence is POSITIVELY ESTABLISHED rather than
|
|
44
|
+
* inferred from a non-zero exit: `probeRefExists` is three-valued, and only a
|
|
45
|
+
* healthy repository that still cannot resolve the ref yields `refAbsent`.
|
|
46
|
+
* Everything else -- including a probe that itself failed -- is `refUnreadable`.
|
|
47
|
+
* Both probes run only on the failure path, so the happy path pays nothing.
|
|
48
|
+
*
|
|
49
|
+
* SCOPE: THIS IS A READER
|
|
50
|
+
* -----------------------
|
|
51
|
+
* It answers "what does this ref hold?" for ONE ref. It does not compare two
|
|
52
|
+
* refs, judge a merge, or render a notice -- a comparison at the merge is the
|
|
53
|
+
* shape this focus abandoned (two teams told to renumber from the same union at
|
|
54
|
+
* the same moment collide again). The allocator consumes this to compute a
|
|
55
|
+
* candidate; nothing here decides anything.
|
|
56
|
+
*
|
|
57
|
+
* WHY THE GIT PRIMITIVE COMES FROM daemon-core
|
|
58
|
+
* --------------------------------------------
|
|
59
|
+
* `runGit` is taken from `@telora/daemon-core` rather than the daemon's own
|
|
60
|
+
* `git/types.ts` wrapper: this area is a rank-0 leaf in
|
|
61
|
+
* `scripts/daemon-layer-ranks.json` and `git/` sits above it, so importing the
|
|
62
|
+
* wrapper would be an upward import. The wrapper adds only a default timeout,
|
|
63
|
+
* which is passed explicitly here.
|
|
64
|
+
*
|
|
65
|
+
* @module migration-inventory
|
|
66
|
+
*/
|
|
67
|
+
import { type GitResult } from '@telora/daemon-core';
|
|
68
|
+
import { type MigrationManifest, type MigrationManifestFs } from './migration-manifest.js';
|
|
69
|
+
/** One migration file a ref holds. */
|
|
70
|
+
export interface MigrationEntry {
|
|
71
|
+
/**
|
|
72
|
+
* The migration's number, captured as a STRING by the declared pattern.
|
|
73
|
+
*
|
|
74
|
+
* Never parsed to a number: both live schemes are zero-padded fixed width
|
|
75
|
+
* (`0352`, `20260614172757`), so string comparison IS ordinal comparison, and
|
|
76
|
+
* a parse could only lose the padding that makes that true.
|
|
77
|
+
*/
|
|
78
|
+
ordinal: string;
|
|
79
|
+
/** The migration's name, recorded alongside the number on a claim. */
|
|
80
|
+
tag: string;
|
|
81
|
+
/** Repo-relative path of the file at the ref. */
|
|
82
|
+
path: string;
|
|
83
|
+
}
|
|
84
|
+
/** One entry the journal registers, when the product keeps a journal. */
|
|
85
|
+
export interface JournalEntry {
|
|
86
|
+
/** The registered index, kept as a string for the same reason as `ordinal`. */
|
|
87
|
+
idx: string;
|
|
88
|
+
/** The tag the journal registers at that index. */
|
|
89
|
+
tag: string;
|
|
90
|
+
}
|
|
91
|
+
/** Everything one ref holds in the declared migration namespace. */
|
|
92
|
+
export interface MigrationInventory {
|
|
93
|
+
/** The ref this was read from, as given (a branch name or a sha). */
|
|
94
|
+
ref: string;
|
|
95
|
+
/** Migrations at the ref, in the order git listed them. May be empty. */
|
|
96
|
+
migrations: MigrationEntry[];
|
|
97
|
+
/** Journal entries at the ref; undefined when the product declares no journal. */
|
|
98
|
+
journal: JournalEntry[] | undefined;
|
|
99
|
+
}
|
|
100
|
+
/** The five distinguishable outcomes of asking a ref what it holds. */
|
|
101
|
+
export type MigrationInventoryResult = {
|
|
102
|
+
kind: 'undeclared';
|
|
103
|
+
} | {
|
|
104
|
+
kind: 'declarationUnreadable';
|
|
105
|
+
reason: string;
|
|
106
|
+
} | {
|
|
107
|
+
kind: 'refAbsent';
|
|
108
|
+
ref: string;
|
|
109
|
+
} | {
|
|
110
|
+
kind: 'refUnreadable';
|
|
111
|
+
ref: string;
|
|
112
|
+
reason: string;
|
|
113
|
+
} | {
|
|
114
|
+
kind: 'read';
|
|
115
|
+
inventory: MigrationInventory;
|
|
116
|
+
};
|
|
117
|
+
/** Injected seams: git and the declaration reader. Tests pass fakes. */
|
|
118
|
+
export interface MigrationInventoryDeps {
|
|
119
|
+
/** Run git in `repoPath`. Must not throw; a failure is reported on the result. */
|
|
120
|
+
runGit: (args: string[], repoPath: string) => GitResult;
|
|
121
|
+
/** Read the product's declaration. Throws when the declaration is malformed. */
|
|
122
|
+
readManifest: (repoPath: string, fs?: MigrationManifestFs) => MigrationManifest | undefined;
|
|
123
|
+
}
|
|
124
|
+
/** Real git + real declaration reader. */
|
|
125
|
+
export declare function defaultMigrationInventoryDeps(): MigrationInventoryDeps;
|
|
126
|
+
/**
|
|
127
|
+
* Parse a journal document into its entries.
|
|
128
|
+
*
|
|
129
|
+
* Accepts the two shapes the hand-written products ship: a bare array of
|
|
130
|
+
* entries, or an object carrying an `entries` array. Anything else -- including
|
|
131
|
+
* an entry missing `idx` or `tag` -- is a parse FAILURE, because a journal we
|
|
132
|
+
* cannot read is the unreadable state, never an empty registration.
|
|
133
|
+
*/
|
|
134
|
+
export declare function parseJournal(text: string): {
|
|
135
|
+
entries: JournalEntry[];
|
|
136
|
+
} | {
|
|
137
|
+
error: string;
|
|
138
|
+
};
|
|
139
|
+
/**
|
|
140
|
+
* Which of a ref's listed files are migrations, read through the declared
|
|
141
|
+
* pattern. Pure.
|
|
142
|
+
*
|
|
143
|
+
* A file in the migration dir that does NOT match the pattern is not a
|
|
144
|
+
* migration (a README, a .gitkeep, a `meta/` subdirectory) -- not a failure.
|
|
145
|
+
*/
|
|
146
|
+
export declare function readMigrationsFromPaths(paths: readonly string[], manifest: MigrationManifest): MigrationEntry[];
|
|
147
|
+
/** What a ref-existence probe could establish. */
|
|
148
|
+
export type RefProbe = 'present' | 'absent' | 'indeterminate';
|
|
149
|
+
/**
|
|
150
|
+
* Does `ref` name a commit in this repo -- and could we even tell?
|
|
151
|
+
*
|
|
152
|
+
* THREE-VALUED ON PURPOSE. `git rev-parse --verify --quiet` exits non-zero for
|
|
153
|
+
* BOTH a missing ref and an operational failure (a corrupt object store, a
|
|
154
|
+
* timeout, a repo that is not there). Collapsing those to a boolean is a
|
|
155
|
+
* fail-OPEN: an unreadable branch is reported as holding nothing, the held set
|
|
156
|
+
* still reads complete, and the allocator grants a number that branch already
|
|
157
|
+
* holds -- which is the exact failure the reader exists to prevent.
|
|
158
|
+
*
|
|
159
|
+
* ABSENCE MUST BE POSITIVELY ESTABLISHED, and that takes two facts: the
|
|
160
|
+
* REPOSITORY answers, and the REF does not. So a failed ref probe is followed
|
|
161
|
+
* by a repo-health probe; only a healthy repo that still cannot resolve the ref
|
|
162
|
+
* proves the ref is gone. Anything else is `indeterminate`, and the caller
|
|
163
|
+
* treats that as unreadable.
|
|
164
|
+
*
|
|
165
|
+
* `--quiet` suppresses git's missing-ref message, so a genuine absence writes
|
|
166
|
+
* NOTHING to stderr. Output on stderr is therefore a second, independent signal
|
|
167
|
+
* that something operational went wrong, and is honoured even if the health
|
|
168
|
+
* probe happens to pass.
|
|
169
|
+
*
|
|
170
|
+
* Called ONLY after a read of the ref has already failed, so the happy path
|
|
171
|
+
* pays nothing for the distinction.
|
|
172
|
+
*/
|
|
173
|
+
export declare function probeRefExists(repoPath: string, ref: string, deps: MigrationInventoryDeps): RefProbe;
|
|
174
|
+
/**
|
|
175
|
+
* What `ref` holds in `repoPath`'s declared migration namespace.
|
|
176
|
+
*
|
|
177
|
+
* Never throws. A malformed declaration, a missing ref, a failed git call and an
|
|
178
|
+
* unparseable journal each come back as their OWN kind, so a caller can refuse
|
|
179
|
+
* on a broken declaration, ignore an absent ref, and hold on an unreadable one
|
|
180
|
+
* without having to guess which it got.
|
|
181
|
+
*
|
|
182
|
+
* @param repoPath The product's repo root (`config.products[].repoPath`).
|
|
183
|
+
* @param ref Any git ref -- a branch name or a sha. Need not be checked out.
|
|
184
|
+
* @param deps Injectable git + declaration seams; defaults to the real ones.
|
|
185
|
+
*/
|
|
186
|
+
export declare function readMigrationInventory(repoPath: string, ref: string, deps?: MigrationInventoryDeps): MigrationInventoryResult;
|
|
187
|
+
//# sourceMappingURL=migration-inventory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration-inventory.d.ts","sourceRoot":"","sources":["../../src/migrations/migration-inventory.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiEG;AAEH,OAAO,EAGL,KAAK,SAAS,EACf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAEL,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACzB,MAAM,yBAAyB,CAAC;AAEjC,sCAAsC;AACtC,MAAM,WAAW,cAAc;IAC7B;;;;;;OAMG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB,sEAAsE;IACtE,GAAG,EAAE,MAAM,CAAC;IACZ,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAC;CACd;AAED,yEAAyE;AACzE,MAAM,WAAW,YAAY;IAC3B,+EAA+E;IAC/E,GAAG,EAAE,MAAM,CAAC;IACZ,mDAAmD;IACnD,GAAG,EAAE,MAAM,CAAC;CACb;AAED,oEAAoE;AACpE,MAAM,WAAW,kBAAkB;IACjC,qEAAqE;IACrE,GAAG,EAAE,MAAM,CAAC;IACZ,yEAAyE;IACzE,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B,kFAAkF;IAClF,OAAO,EAAE,YAAY,EAAE,GAAG,SAAS,CAAC;CACrC;AAED,uEAAuE;AACvE,MAAM,MAAM,wBAAwB,GAChC;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,GACtB;IAAE,IAAI,EAAE,uBAAuB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACjD;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACtD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,kBAAkB,CAAA;CAAE,CAAC;AAEpD,wEAAwE;AACxE,MAAM,WAAW,sBAAsB;IACrC,kFAAkF;IAClF,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,KAAK,SAAS,CAAC;IACxD,gFAAgF;IAChF,YAAY,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,mBAAmB,KAAK,iBAAiB,GAAG,SAAS,CAAC;CAC7F;AAED,0CAA0C;AAC1C,wBAAgB,6BAA6B,IAAI,sBAAsB,CAKtE;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,YAAY,EAAE,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAiC1F;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,QAAQ,EAAE,iBAAiB,GAC1B,cAAc,EAAE,CAYlB;AAED,kDAAkD;AAClD,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,eAAe,CAAC;AAE9D;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,sBAAsB,GAC3B,QAAQ,CAQV;AA6BD;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,sBAAwD,GAC7D,wBAAwB,CAwC1B"}
|