@xenosystem/agent-interface-core 0.1.3

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 (122) hide show
  1. package/LICENSE +67 -0
  2. package/dist/agentDiscovery.d.ts +97 -0
  3. package/dist/agentDiscovery.d.ts.map +1 -0
  4. package/dist/agentDiscovery.js +93 -0
  5. package/dist/agentDiscovery.js.map +1 -0
  6. package/dist/capabilityRouting.d.ts +72 -0
  7. package/dist/capabilityRouting.d.ts.map +1 -0
  8. package/dist/capabilityRouting.js +124 -0
  9. package/dist/capabilityRouting.js.map +1 -0
  10. package/dist/capabilityTier.d.ts +105 -0
  11. package/dist/capabilityTier.d.ts.map +1 -0
  12. package/dist/capabilityTier.js +120 -0
  13. package/dist/capabilityTier.js.map +1 -0
  14. package/dist/combinationVerification.d.ts +101 -0
  15. package/dist/combinationVerification.d.ts.map +1 -0
  16. package/dist/combinationVerification.js +115 -0
  17. package/dist/combinationVerification.js.map +1 -0
  18. package/dist/context/agentAttachedContextPrompt.d.ts +13 -0
  19. package/dist/context/agentAttachedContextPrompt.d.ts.map +1 -0
  20. package/dist/context/agentAttachedContextPrompt.js +50 -0
  21. package/dist/context/agentAttachedContextPrompt.js.map +1 -0
  22. package/dist/context/agentModelContext.d.ts +77 -0
  23. package/dist/context/agentModelContext.d.ts.map +1 -0
  24. package/dist/context/agentModelContext.js +297 -0
  25. package/dist/context/agentModelContext.js.map +1 -0
  26. package/dist/fanOutPlanner.d.ts +65 -0
  27. package/dist/fanOutPlanner.d.ts.map +1 -0
  28. package/dist/fanOutPlanner.js +78 -0
  29. package/dist/fanOutPlanner.js.map +1 -0
  30. package/dist/fleetBoardProjection.d.ts +143 -0
  31. package/dist/fleetBoardProjection.d.ts.map +1 -0
  32. package/dist/fleetBoardProjection.js +156 -0
  33. package/dist/fleetBoardProjection.js.map +1 -0
  34. package/dist/fleetProjection.d.ts +63 -0
  35. package/dist/fleetProjection.d.ts.map +1 -0
  36. package/dist/fleetProjection.js +86 -0
  37. package/dist/fleetProjection.js.map +1 -0
  38. package/dist/hostBackedCapabilities.d.ts +50 -0
  39. package/dist/hostBackedCapabilities.d.ts.map +1 -0
  40. package/dist/hostBackedCapabilities.js +66 -0
  41. package/dist/hostBackedCapabilities.js.map +1 -0
  42. package/dist/index.d.ts +54 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +134 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/intakeBoardProjection.d.ts +75 -0
  47. package/dist/intakeBoardProjection.d.ts.map +1 -0
  48. package/dist/intakeBoardProjection.js +90 -0
  49. package/dist/intakeBoardProjection.js.map +1 -0
  50. package/dist/intakeClaim.d.ts +156 -0
  51. package/dist/intakeClaim.d.ts.map +1 -0
  52. package/dist/intakeClaim.js +190 -0
  53. package/dist/intakeClaim.js.map +1 -0
  54. package/dist/knowledgeBoardProjection.d.ts +57 -0
  55. package/dist/knowledgeBoardProjection.d.ts.map +1 -0
  56. package/dist/knowledgeBoardProjection.js +74 -0
  57. package/dist/knowledgeBoardProjection.js.map +1 -0
  58. package/dist/landingOverlap.d.ts +127 -0
  59. package/dist/landingOverlap.d.ts.map +1 -0
  60. package/dist/landingOverlap.js +107 -0
  61. package/dist/landingOverlap.js.map +1 -0
  62. package/dist/landingQueue.d.ts +152 -0
  63. package/dist/landingQueue.d.ts.map +1 -0
  64. package/dist/landingQueue.js +258 -0
  65. package/dist/landingQueue.js.map +1 -0
  66. package/dist/markdown/markdownWindowsPaths.d.ts +20 -0
  67. package/dist/markdown/markdownWindowsPaths.d.ts.map +1 -0
  68. package/dist/markdown/markdownWindowsPaths.js +208 -0
  69. package/dist/markdown/markdownWindowsPaths.js.map +1 -0
  70. package/dist/reviewProjection.d.ts +118 -0
  71. package/dist/reviewProjection.d.ts.map +1 -0
  72. package/dist/reviewProjection.js +133 -0
  73. package/dist/reviewProjection.js.map +1 -0
  74. package/dist/runDiff.d.ts +86 -0
  75. package/dist/runDiff.d.ts.map +1 -0
  76. package/dist/runDiff.js +103 -0
  77. package/dist/runDiff.js.map +1 -0
  78. package/dist/runtime/agentReasoningEffort.d.ts +22 -0
  79. package/dist/runtime/agentReasoningEffort.d.ts.map +1 -0
  80. package/dist/runtime/agentReasoningEffort.js +76 -0
  81. package/dist/runtime/agentReasoningEffort.js.map +1 -0
  82. package/dist/runtime/agentRuntimeProvider.d.ts +12 -0
  83. package/dist/runtime/agentRuntimeProvider.d.ts.map +1 -0
  84. package/dist/runtime/agentRuntimeProvider.js +294 -0
  85. package/dist/runtime/agentRuntimeProvider.js.map +1 -0
  86. package/dist/runtime/agentRuntimeProviderTypes.d.ts +100 -0
  87. package/dist/runtime/agentRuntimeProviderTypes.d.ts.map +1 -0
  88. package/dist/runtime/agentRuntimeProviderTypes.js +2 -0
  89. package/dist/runtime/agentRuntimeProviderTypes.js.map +1 -0
  90. package/dist/runtime/conversationRuntime.d.ts +53 -0
  91. package/dist/runtime/conversationRuntime.d.ts.map +1 -0
  92. package/dist/runtime/conversationRuntime.js +207 -0
  93. package/dist/runtime/conversationRuntime.js.map +1 -0
  94. package/dist/runtime/legacyAgentRuntimeEvents.d.ts +65 -0
  95. package/dist/runtime/legacyAgentRuntimeEvents.d.ts.map +1 -0
  96. package/dist/runtime/legacyAgentRuntimeEvents.js +104 -0
  97. package/dist/runtime/legacyAgentRuntimeEvents.js.map +1 -0
  98. package/dist/runtimeProjection.d.ts +19 -0
  99. package/dist/runtimeProjection.d.ts.map +1 -0
  100. package/dist/runtimeProjection.js +133 -0
  101. package/dist/runtimeProjection.js.map +1 -0
  102. package/dist/sessionBoardProjection.d.ts +84 -0
  103. package/dist/sessionBoardProjection.d.ts.map +1 -0
  104. package/dist/sessionBoardProjection.js +103 -0
  105. package/dist/sessionBoardProjection.js.map +1 -0
  106. package/dist/sessionMode.d.ts +115 -0
  107. package/dist/sessionMode.d.ts.map +1 -0
  108. package/dist/sessionMode.js +98 -0
  109. package/dist/sessionMode.js.map +1 -0
  110. package/dist/workspace/workspaceSelectionContinuation.d.ts +8 -0
  111. package/dist/workspace/workspaceSelectionContinuation.d.ts.map +1 -0
  112. package/dist/workspace/workspaceSelectionContinuation.js +18 -0
  113. package/dist/workspace/workspaceSelectionContinuation.js.map +1 -0
  114. package/dist/workspaceKnowledge.d.ts +90 -0
  115. package/dist/workspaceKnowledge.d.ts.map +1 -0
  116. package/dist/workspaceKnowledge.js +103 -0
  117. package/dist/workspaceKnowledge.js.map +1 -0
  118. package/dist/writeLease.d.ts +92 -0
  119. package/dist/writeLease.d.ts.map +1 -0
  120. package/dist/writeLease.js +108 -0
  121. package/dist/writeLease.js.map +1 -0
  122. package/package.json +29 -0
@@ -0,0 +1,74 @@
1
+ /**
2
+ * What a knowledge surface may SHOW — ADE §5.8, the presentation half.
3
+ *
4
+ * `knowledge.list` returns every fact ever recorded, including ones a later fact
5
+ * replaced. This decides which are CURRENT, which are history, and what must be
6
+ * said about the store keeping them.
7
+ *
8
+ * ## 🔴 Superseded facts are history, never deleted and never mixed in
9
+ *
10
+ * The store keeps "we use npm" after "we moved to pnpm" on purpose — *"when did
11
+ * this change, and who decided?"* is a question people ask. But a list that
12
+ * showed both as current would be worse than one that dropped the old fact: a
13
+ * reader scanning it comes away believing the workspace holds two contradictory
14
+ * beliefs, which is exactly what supersession exists to prevent.
15
+ *
16
+ * ## The durability warning is not a footnote
17
+ *
18
+ * A surface that renders facts identically whether or not they survive a restart
19
+ * invites someone to teach the workspace something important and lose it. When
20
+ * the store is not durable, that is the first thing on screen.
21
+ */
22
+ export function projectKnowledgeBoard(input) {
23
+ if (!input.available) {
24
+ return {
25
+ current: [],
26
+ history: [],
27
+ notice: input.reason ?? 'This host is not keeping workspace knowledge.',
28
+ durable: false,
29
+ };
30
+ }
31
+ // Latest per subject is current; everything earlier is history. Same rule as
32
+ // `selectWorkspaceKnowledge`, and it has to agree with it — a surface showing
33
+ // a fact as current that the prompt does not carry would be a lie about what
34
+ // the agent knows.
35
+ const currentBySubject = new Map();
36
+ const history = [];
37
+ for (const entry of [...input.entries].sort((left, right) => left.at === right.at ? left.id.localeCompare(right.id) : left.at - right.at)) {
38
+ const existing = currentBySubject.get(entry.subject);
39
+ if (existing)
40
+ history.push(existing);
41
+ currentBySubject.set(entry.subject, entry);
42
+ }
43
+ const current = [...currentBySubject.values()].sort((left, right) => {
44
+ if (Boolean(left.pinned) !== Boolean(right.pinned))
45
+ return left.pinned ? -1 : 1;
46
+ return right.at - left.at;
47
+ });
48
+ return {
49
+ current: current.map(toRow),
50
+ // Newest first: the most recent change is the one being questioned.
51
+ history: history.sort((left, right) => right.at - left.at).map(toRow),
52
+ ...(input.durable
53
+ ? {}
54
+ : {
55
+ notice: 'These facts are held in memory only and will be lost when the app closes.',
56
+ }),
57
+ durable: input.durable,
58
+ };
59
+ }
60
+ function toRow(entry) {
61
+ return {
62
+ id: entry.id,
63
+ subject: entry.subject,
64
+ fact: entry.fact,
65
+ pinned: Boolean(entry.pinned),
66
+ // The provider is named when known, because "which model decided this?" is
67
+ // the first thing anyone asks about a fact they disagree with.
68
+ provenance: entry.providerId
69
+ ? `learned by ${entry.providerId} in run ${entry.runId}`
70
+ : `learned in run ${entry.runId}`,
71
+ at: entry.at,
72
+ };
73
+ }
74
+ //# sourceMappingURL=knowledgeBoardProjection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"knowledgeBoardProjection.js","sourceRoot":"","sources":["../src/knowledgeBoardProjection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAwCH,MAAM,UAAU,qBAAqB,CAAC,KAA0B;IAC9D,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;QACrB,OAAO;YACL,OAAO,EAAE,EAAE;YACX,OAAO,EAAE,EAAE;YACX,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,+CAA+C;YACvE,OAAO,EAAE,KAAK;SACf,CAAA;IACH,CAAC;IAED,6EAA6E;IAC7E,8EAA8E;IAC9E,6EAA6E;IAC7E,mBAAmB;IACnB,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAAoC,CAAA;IACpE,MAAM,OAAO,GAA+B,EAAE,CAAA;IAC9C,KAAK,MAAM,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAC1D,IAAI,CAAC,EAAE,KAAK,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,GAAG,KAAK,CAAC,EAAE,CAC5E,EAAE,CAAC;QACF,MAAM,QAAQ,GAAG,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;QACpD,IAAI,QAAQ;YAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QACpC,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;IAC5C,CAAC;IAED,MAAM,OAAO,GAAG,CAAC,GAAG,gBAAgB,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QAClE,IAAI,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC;YAAE,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QAC/E,OAAO,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,EAAE,CAAA;IAC3B,CAAC,CAAC,CAAA;IAEF,OAAO;QACL,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;QAC3B,oEAAoE;QACpE,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC;QACrE,GAAG,CAAC,KAAK,CAAC,OAAO;YACf,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC;gBACE,MAAM,EACJ,2EAA2E;aAC9E,CAAC;QACN,OAAO,EAAE,KAAK,CAAC,OAAO;KACvB,CAAA;AACH,CAAC;AAED,SAAS,KAAK,CAAC,KAA+B;IAC5C,OAAO;QACL,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,MAAM,EAAE,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC;QAC7B,2EAA2E;QAC3E,+DAA+D;QAC/D,UAAU,EAAE,KAAK,CAAC,UAAU;YAC1B,CAAC,CAAC,cAAc,KAAK,CAAC,UAAU,WAAW,KAAK,CAAC,KAAK,EAAE;YACxD,CAAC,CAAC,kBAAkB,KAAK,CAAC,KAAK,EAAE;QACnC,EAAE,EAAE,KAAK,CAAC,EAAE;KACb,CAAA;AACH,CAAC"}
@@ -0,0 +1,127 @@
1
+ import { type AcceptedChange, type CombinationVerification } from './combinationVerification.js';
2
+ /**
3
+ * Conflict-aware landing — ADE spec §5.7, the differentiator.
4
+ *
5
+ * > "Detect that two runs touched the same region *before* the merge, and
6
+ * > surface it while both agents are still alive to fix it." The failure this
7
+ * > answers is **merge debt** — *"every agent's tests pass and the merge is
8
+ * > still wrong."*
9
+ *
10
+ * ## Why this is computed from OBSERVED diffs, not from claims
11
+ *
12
+ * The coordination spec's claims are *a priori*: a run declares intent to touch
13
+ * a path and the registry refuses a second holder. That is the right primitive
14
+ * for avoiding collisions, and it is **not sufficient for detecting them**,
15
+ * because a run can touch a file it never claimed. Nothing forces an agent to
16
+ * claim before writing, and an agent that surprises us is exactly the one whose
17
+ * overlap matters.
18
+ *
19
+ * Observed change sets are *a posteriori*: they say what actually happened.
20
+ * `RunDiff` produces them continuously, so an overlap is detectable **while both
21
+ * runs are still alive** — which is the whole point. After the merge it is
22
+ * archaeology.
23
+ *
24
+ * The two are complementary and the difference is reported: an overlap on a
25
+ * path neither run claimed was **invisible until now**, while an overlap on a
26
+ * claimed path means the claim was released or bypassed. A reviewer needs to
27
+ * know which, because they imply different fixes.
28
+ *
29
+ * ## Absence of overlap is not evidence of no overlap
30
+ *
31
+ * A run whose observation was degraded or dropped events has a diff with a hole
32
+ * in it. Comparing two such diffs and reporting "no conflicts" is the
33
+ * false-clean answer this codebase keeps removing — so completeness travels
34
+ * with the result, and a caller that ignores it is making a claim the data does
35
+ * not support.
36
+ */
37
+ /** One run's observed change set, as the observation service produces it. */
38
+ export interface RunChangeSet {
39
+ runId: string;
40
+ /** Container-relative paths, with what happened to each. */
41
+ changes: Array<{
42
+ path: string;
43
+ kind: 'added' | 'modified' | 'deleted';
44
+ }>;
45
+ /** False when the run's observation is known to be incomplete. */
46
+ complete: boolean;
47
+ /** Paths this run claimed through the coordination registry, if any. */
48
+ claimedPaths?: string[];
49
+ }
50
+ /**
51
+ * How two runs collided on one path.
52
+ *
53
+ * `delete-vs-edit` is separated from `edit-vs-edit` deliberately: a reviewer's
54
+ * action differs. Two edits usually merge, badly or well; one run deleting what
55
+ * another edited cannot be resolved by a merge tool at all, because the
56
+ * question is whether the file should exist.
57
+ */
58
+ export type OverlapKind = 'edit-vs-edit' | 'delete-vs-edit' | 'add-vs-add';
59
+ export interface LandingOverlap {
60
+ path: string;
61
+ /** Exactly two run ids, sorted, so a pair is reported once rather than twice. */
62
+ runIds: [string, string];
63
+ kind: OverlapKind;
64
+ /**
65
+ * True when NEITHER run claimed the path.
66
+ *
67
+ * The dangerous case: the collision was invisible to the claim registry, so
68
+ * nothing refused it and nobody was told. An overlap on a claimed path means
69
+ * a claim was released or bypassed, which is a different conversation.
70
+ */
71
+ unclaimed: boolean;
72
+ }
73
+ export interface LandingOverlapReport {
74
+ overlaps: LandingOverlap[];
75
+ /**
76
+ * False when any run's diff was incomplete.
77
+ *
78
+ * An empty `overlaps` with `complete: false` means "we found none in what we
79
+ * could see", which is not the same as "there are none" — and a landing gate
80
+ * must not treat them as equivalent.
81
+ */
82
+ complete: boolean;
83
+ /** Runs whose observation had a hole, named so the gap is actionable. */
84
+ incompleteRuns: string[];
85
+ }
86
+ /**
87
+ * Finds every pair of runs that touched the same path.
88
+ *
89
+ * Pairwise by design: a three-way collision surfaces as three pairs, because a
90
+ * reviewer resolves them two at a time and "runs A, B and C all touched this"
91
+ * hides which pair actually disagrees.
92
+ */
93
+ export declare function detectLandingOverlaps(runs: RunChangeSet[]): LandingOverlapReport;
94
+ /**
95
+ * Whether a landing may proceed.
96
+ *
97
+ * 🔴 Refuses on an INCOMPLETE report even with zero overlaps, and that is the
98
+ * point. §5.7's gate is that the system "refuses to land on a red combination";
99
+ * a diff with a hole cannot establish the combination is green, so landing on it
100
+ * would be asserting something unmeasured. Same rule as everywhere else here:
101
+ * "we saw nothing" and "there is nothing" are different answers.
102
+ */
103
+ export declare function mayLand(report: LandingOverlapReport): {
104
+ ok: boolean;
105
+ reason: string;
106
+ };
107
+ /**
108
+ * The whole §5.7 gate: no unresolved overlap AND a verification of exactly this
109
+ * combination.
110
+ *
111
+ * Both halves are required and they catch different failures. Overlap detection
112
+ * asks *did two runs touch the same thing*; combination verification asks *has
113
+ * anyone checked the union*. A landing can pass either one alone and still be
114
+ * the failure this section exists for — two runs editing different files whose
115
+ * combination breaks a third, or one run whose overlap was resolved after the
116
+ * checks last ran.
117
+ *
118
+ * It computes the verdict itself rather than taking one, so a caller cannot
119
+ * satisfy the gate by passing `{ok: true}` — the first version of this function
120
+ * did exactly that, and the unwired-module sweep caught the verification half
121
+ * being reachable from nothing but its own test.
122
+ */
123
+ export declare function mayLandCombination(report: LandingOverlapReport, accepted: AcceptedChange[], verifications: CombinationVerification[]): {
124
+ ok: boolean;
125
+ reason: string;
126
+ };
127
+ //# sourceMappingURL=landingOverlap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"landingOverlap.d.ts","sourceRoot":"","sources":["../src/landingOverlap.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,uBAAuB,EAC7B,MAAM,8BAA8B,CAAA;AAErC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,6EAA6E;AAC7E,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAA;IACb,4DAA4D;IAC5D,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,GAAG,UAAU,GAAG,SAAS,CAAA;KAAE,CAAC,CAAA;IACxE,kEAAkE;IAClE,QAAQ,EAAE,OAAO,CAAA;IACjB,wEAAwE;IACxE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;CACxB;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW,GAAG,cAAc,GAAG,gBAAgB,GAAG,YAAY,CAAA;AAE1E,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,iFAAiF;IACjF,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACxB,IAAI,EAAE,WAAW,CAAA;IACjB;;;;;;OAMG;IACH,SAAS,EAAE,OAAO,CAAA;CACnB;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,cAAc,EAAE,CAAA;IAC1B;;;;;;OAMG;IACH,QAAQ,EAAE,OAAO,CAAA;IACjB,yEAAyE;IACzE,cAAc,EAAE,MAAM,EAAE,CAAA;CACzB;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,YAAY,EAAE,GAAG,oBAAoB,CA4ChF;AAWD;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,oBAAoB,GAAG;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAcrF;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,oBAAoB,EAC5B,QAAQ,EAAE,cAAc,EAAE,EAC1B,aAAa,EAAE,uBAAuB,EAAE,GACvC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAQjC"}
@@ -0,0 +1,107 @@
1
+ import { verdictForCombination, } from './combinationVerification.js';
2
+ /**
3
+ * Finds every pair of runs that touched the same path.
4
+ *
5
+ * Pairwise by design: a three-way collision surfaces as three pairs, because a
6
+ * reviewer resolves them two at a time and "runs A, B and C all touched this"
7
+ * hides which pair actually disagrees.
8
+ */
9
+ export function detectLandingOverlaps(runs) {
10
+ const byPath = new Map();
11
+ for (const run of runs) {
12
+ const claimed = new Set(run.claimedPaths ?? []);
13
+ for (const change of run.changes) {
14
+ const entries = byPath.get(change.path) ?? [];
15
+ entries.push({ runId: run.runId, kind: change.kind, claimed: claimed.has(change.path) });
16
+ byPath.set(change.path, entries);
17
+ }
18
+ }
19
+ const overlaps = [];
20
+ for (const [path, entries] of byPath) {
21
+ if (entries.length < 2)
22
+ continue;
23
+ for (let left = 0; left < entries.length; left += 1) {
24
+ for (let right = left + 1; right < entries.length; right += 1) {
25
+ const a = entries[left];
26
+ const b = entries[right];
27
+ // A run touching its own path twice is not a collision. The observation
28
+ // fold already collapses repeats, but a caller may pass anything.
29
+ if (a.runId === b.runId)
30
+ continue;
31
+ const runIds = a.runId < b.runId ? [a.runId, b.runId] : [b.runId, a.runId];
32
+ overlaps.push({
33
+ path,
34
+ runIds,
35
+ kind: classify(a.kind, b.kind),
36
+ unclaimed: !a.claimed && !b.claimed,
37
+ });
38
+ }
39
+ }
40
+ }
41
+ const incompleteRuns = runs.filter((run) => !run.complete).map((run) => run.runId).sort();
42
+ return {
43
+ // Sorted so two runs of the same landing produce comparable output.
44
+ overlaps: overlaps.sort((left, right) => left.path.localeCompare(right.path) || left.runIds[0].localeCompare(right.runIds[0])),
45
+ complete: incompleteRuns.length === 0,
46
+ incompleteRuns,
47
+ };
48
+ }
49
+ function classify(a, b) {
50
+ if (a === 'deleted' || b === 'deleted')
51
+ return 'delete-vs-edit';
52
+ if (a === 'added' && b === 'added')
53
+ return 'add-vs-add';
54
+ return 'edit-vs-edit';
55
+ }
56
+ /**
57
+ * Whether a landing may proceed.
58
+ *
59
+ * 🔴 Refuses on an INCOMPLETE report even with zero overlaps, and that is the
60
+ * point. §5.7's gate is that the system "refuses to land on a red combination";
61
+ * a diff with a hole cannot establish the combination is green, so landing on it
62
+ * would be asserting something unmeasured. Same rule as everywhere else here:
63
+ * "we saw nothing" and "there is nothing" are different answers.
64
+ */
65
+ export function mayLand(report) {
66
+ if (!report.complete) {
67
+ return {
68
+ ok: false,
69
+ reason: `observation was incomplete for ${report.incompleteRuns.join(', ')}, so overlaps cannot be ruled out`,
70
+ };
71
+ }
72
+ if (report.overlaps.length > 0) {
73
+ return {
74
+ ok: false,
75
+ reason: `${report.overlaps.length} overlapping change(s) must be resolved while both runs are alive`,
76
+ };
77
+ }
78
+ return { ok: true, reason: 'no run touched a path another run touched' };
79
+ }
80
+ /**
81
+ * The whole §5.7 gate: no unresolved overlap AND a verification of exactly this
82
+ * combination.
83
+ *
84
+ * Both halves are required and they catch different failures. Overlap detection
85
+ * asks *did two runs touch the same thing*; combination verification asks *has
86
+ * anyone checked the union*. A landing can pass either one alone and still be
87
+ * the failure this section exists for — two runs editing different files whose
88
+ * combination breaks a third, or one run whose overlap was resolved after the
89
+ * checks last ran.
90
+ *
91
+ * It computes the verdict itself rather than taking one, so a caller cannot
92
+ * satisfy the gate by passing `{ok: true}` — the first version of this function
93
+ * did exactly that, and the unwired-module sweep caught the verification half
94
+ * being reachable from nothing but its own test.
95
+ */
96
+ export function mayLandCombination(report, accepted, verifications) {
97
+ const overlap = mayLand(report);
98
+ // Overlap first: it is the more actionable of the two, and it is what must be
99
+ // fixed while both runs are still alive.
100
+ if (!overlap.ok)
101
+ return overlap;
102
+ const verdict = verdictForCombination(accepted, verifications);
103
+ if (!verdict.ok)
104
+ return { ok: false, reason: verdict.reason };
105
+ return { ok: true, reason: `${overlap.reason}; ${verdict.reason}` };
106
+ }
107
+ //# sourceMappingURL=landingOverlap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"landingOverlap.js","sourceRoot":"","sources":["../src/landingOverlap.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,152 @@
1
+ /**
2
+ * The serialised path to trunk — §5.7's landing queue.
3
+ *
4
+ * Overlap detection says two runs collided. Combination verification says
5
+ * whether the union was checked. Neither of them *sequences* anything, and
6
+ * sequencing is the part that makes the other two mean something: if two runs
7
+ * can land concurrently, the combination each of them verified was not the
8
+ * combination that landed, and the gate is decorative.
9
+ *
10
+ * Pure, and deliberately so. Everything here is a decision about what may land
11
+ * next; running the checks and moving the git refs are side effects that belong
12
+ * to the host. A rule that migrates into the executor stops being testable —
13
+ * that is the split `updaterPolicy.ts` already uses in this repo, for the same
14
+ * reason.
15
+ *
16
+ * ## 🔴 One lands at a time, and that is the whole design
17
+ *
18
+ * The queue admits **one** run to `landing` at a time. Not for throughput and
19
+ * not for tidiness: a verification result is bound to a fingerprint of the
20
+ * accepted set, and a second run landing in parallel changes that set. Both
21
+ * runs would then be landing against a combination neither verified — which is
22
+ * exactly the merge-debt failure this pillar exists to answer, arrived at
23
+ * through the mechanism built to prevent it.
24
+ *
25
+ * ## Accepting a change invalidates every earlier result
26
+ *
27
+ * Uncomfortable and correct. The fingerprint covers content, so a run that kept
28
+ * working after being verified is a different input wearing the same name.
29
+ * `verdictForCombination` reports that as `stale` rather than `failed`, because
30
+ * a human's next action differs: re-run the checks, not debug a failure.
31
+ */
32
+ import { type AcceptedChange, type CombinationVerification } from './combinationVerification.js';
33
+ import { type LandingOverlapReport } from './landingOverlap.js';
34
+ /**
35
+ * Where a run is on its way to trunk.
36
+ *
37
+ * `refused` is separate from `failed` on purpose: refused means the gate said no
38
+ * — the combination was unverified, stale, red, or overlapping — and the run is
39
+ * still perfectly good work awaiting a fix. `failed` means the landing itself
40
+ * broke: the merge conflicted, the push was rejected, the remote was gone. One
41
+ * is a decision, the other is an accident, and they call for different actions.
42
+ */
43
+ export type LandingState = 'queued' | 'landing' | 'landed' | 'refused' | 'failed';
44
+ export interface LandingEntry {
45
+ runId: string;
46
+ /** Changes whenever the run's content changes; feeds the fingerprint. */
47
+ revision: string;
48
+ state: LandingState;
49
+ /** Position in line, 1-based. `undefined` once it is no longer waiting. */
50
+ position?: number;
51
+ /** Why it is in this state, in terms a human can act on. */
52
+ reason?: string;
53
+ enqueuedAt: number;
54
+ updatedAt: number;
55
+ }
56
+ export interface LandingQueueSnapshot {
57
+ entries: LandingEntry[];
58
+ /** The run currently landing, if any. At most one, always. */
59
+ landing?: string;
60
+ /** The fingerprint of what would land if the head went now. */
61
+ fingerprint: string;
62
+ }
63
+ /** What the queue needs to know about the world to decide. */
64
+ export interface LandingContext {
65
+ /** Overlaps among the LIVE runs, from observed diffs. */
66
+ overlaps: LandingOverlapReport;
67
+ /** Results, each carrying the fingerprint of the set it ran against. */
68
+ verifications: CombinationVerification[];
69
+ }
70
+ export interface LandingDecision {
71
+ /** The run that may proceed, when one may. */
72
+ runId?: string;
73
+ ok: boolean;
74
+ reason: string;
75
+ /** The set the caller must verify and land, so it cannot compose its own. */
76
+ accepted: AcceptedChange[];
77
+ fingerprint: string;
78
+ }
79
+ export declare class LandingQueue {
80
+ #private;
81
+ constructor(options?: {
82
+ now?: () => number;
83
+ });
84
+ /**
85
+ * Puts a run in line, or updates the revision of one already waiting.
86
+ *
87
+ * Re-enqueuing a run that moved on is not an error and does not lose its
88
+ * place: an agent that keeps working while queued is the normal case, and
89
+ * sending it to the back would punish the run that is still being improved.
90
+ * The revision changes, so the fingerprint changes, so every verification of
91
+ * the old set correctly goes stale — the ordering does not need to carry that.
92
+ */
93
+ enqueue(runId: string, revision: string): LandingEntry;
94
+ /** Removes a run from the queue entirely — it is no longer trying to land. */
95
+ remove(runId: string): boolean;
96
+ /**
97
+ * The set that would land if the head proceeded.
98
+ *
99
+ * The head plus everything already landed is NOT what this returns — landed
100
+ * work is in trunk and is the baseline, not part of the combination. What is
101
+ * combined is the head with every other run still in flight, because those are
102
+ * the changes a check would have to run against for its result to describe
103
+ * what trunk becomes.
104
+ */
105
+ acceptedFor(runId: string): AcceptedChange[];
106
+ /**
107
+ * Whether the head may land, and against what.
108
+ *
109
+ * 🔴 Computes the verdict itself rather than accepting one. `mayLandCombination`
110
+ * was written the same way after the first version took a pre-computed
111
+ * `{ok: true}` and could therefore be satisfied by a caller asserting success
112
+ * — the gate has to be the thing that decides, or it is a formality.
113
+ */
114
+ next(context: LandingContext): LandingDecision;
115
+ /**
116
+ * Admits the head to `landing`, or records why it was refused.
117
+ *
118
+ * Returns the entry so a caller sees the outcome of its own call rather than
119
+ * having to read the queue back — the same reason `RunObservationService.poll`
120
+ * returns its snapshot.
121
+ */
122
+ admit(context: LandingContext): LandingEntry | undefined;
123
+ /**
124
+ * Records how a landing ended.
125
+ *
126
+ * `landed` is terminal and leaves the queue; the run is in trunk. `failed`
127
+ * keeps the entry so a human can see what happened — a landing that vanished
128
+ * on failure is indistinguishable from one that never started.
129
+ */
130
+ settle(runId: string, outcome: {
131
+ landed: boolean;
132
+ reason?: string;
133
+ }): LandingEntry | undefined;
134
+ /**
135
+ * Puts a refused or failed run back in line.
136
+ *
137
+ * Explicit rather than automatic: a refusal means something has to change —
138
+ * the overlap resolved, the checks re-run — and retrying on a timer would
139
+ * spin against an unchanged verdict while looking like progress.
140
+ */
141
+ requeue(runId: string, revision: string): LandingEntry | undefined;
142
+ snapshot(): LandingQueueSnapshot;
143
+ }
144
+ /**
145
+ * Whether a verification result still describes what would land.
146
+ *
147
+ * Exported because the executor needs the same answer as the queue and must not
148
+ * arrive at it independently: two implementations of "is this result current"
149
+ * is precisely how a stale green gets trusted.
150
+ */
151
+ export declare function verificationIsCurrent(accepted: AcceptedChange[], verifications: CombinationVerification[]): boolean;
152
+ //# sourceMappingURL=landingQueue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"landingQueue.d.ts","sourceRoot":"","sources":["../src/landingQueue.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,uBAAuB,EAC7B,MAAM,8BAA8B,CAAA;AACrC,OAAO,EAAsB,KAAK,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAEnF;;;;;;;;GAQG;AACH,MAAM,MAAM,YAAY,GACpB,QAAQ,GACR,SAAS,GACT,QAAQ,GACR,SAAS,GACT,QAAQ,CAAA;AAEZ,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAA;IACb,yEAAyE;IACzE,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,YAAY,CAAA;IACnB,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,4DAA4D;IAC5D,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,YAAY,EAAE,CAAA;IACvB,8DAA8D;IAC9D,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,+DAA+D;IAC/D,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,8DAA8D;AAC9D,MAAM,WAAW,cAAc;IAC7B,yDAAyD;IACzD,QAAQ,EAAE,oBAAoB,CAAA;IAC9B,wEAAwE;IACxE,aAAa,EAAE,uBAAuB,EAAE,CAAA;CACzC;AAED,MAAM,WAAW,eAAe;IAC9B,8CAA8C;IAC9C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,EAAE,EAAE,OAAO,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;IACd,6EAA6E;IAC7E,QAAQ,EAAE,cAAc,EAAE,CAAA;IAC1B,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,qBAAa,YAAY;;gBAQX,OAAO,GAAE;QAAE,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;KAAO;IAIhD;;;;;;;;OAQG;IACH,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,YAAY;IA2BtD,8EAA8E;IAC9E,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAS9B;;;;;;;;OAQG;IACH,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,EAAE;IAQ5C;;;;;;;OAOG;IACH,IAAI,CAAC,OAAO,EAAE,cAAc,GAAG,eAAe;IAyB9C;;;;;;OAMG;IACH,KAAK,CAAC,OAAO,EAAE,cAAc,GAAG,YAAY,GAAG,SAAS;IA2BxD;;;;;;OAMG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,YAAY,GAAG,SAAS;IAiB9F;;;;;;OAMG;IACH,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAWlE,QAAQ,IAAI,oBAAoB;CA8BjC;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,cAAc,EAAE,EAC1B,aAAa,EAAE,uBAAuB,EAAE,GACvC,OAAO,CAET"}