@femtomc/mu-core 26.2.70 → 26.2.72
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/dist/dag.d.ts +33 -0
- package/dist/dag.d.ts.map +1 -1
- package/dist/dag.js +74 -2
- package/package.json +1 -1
package/dist/dag.d.ts
CHANGED
|
@@ -3,11 +3,44 @@ export type ValidationResult = {
|
|
|
3
3
|
is_final: boolean;
|
|
4
4
|
reason: string;
|
|
5
5
|
};
|
|
6
|
+
export type RetryableDagCandidate = {
|
|
7
|
+
issue: Issue;
|
|
8
|
+
reason: string;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Deterministic DAG reconcile primitives used by orchestrator reconciliation.
|
|
12
|
+
*
|
|
13
|
+
* Contract: these helpers are pure functions over the provided issue snapshot and must remain
|
|
14
|
+
* side-effect free so reconcile passes are replayable/idempotent.
|
|
15
|
+
*/
|
|
16
|
+
export declare const DAG_RECONCILE_PRIMITIVE_INVARIANTS: readonly ["DAG-RECON-001: `readyLeaves` only returns open, unblocked, leaf issues within the requested subtree scope.", "DAG-RECON-002: `readyLeaves` ordering is deterministic for a fixed input snapshot (priority-ordered candidate set).", "DAG-RECON-003: `validateDag(...).is_final=true` implies no remaining non-expanded open work in the subtree.", "DAG-RECON-004: closed(failure|needs_work) and expanded-without-children are non-final and require reconcile action.", "DAG-RECON-005: `retryableDagCandidates` selection is deterministic and side-effect free for a fixed snapshot + attempts map."];
|
|
6
17
|
export declare function subtreeIds(issues: readonly Issue[], rootId: string): string[];
|
|
18
|
+
/**
|
|
19
|
+
* Reconcile selection primitive: the orchestrator must only dispatch from this ready set (or an
|
|
20
|
+
* equivalent deterministic adapter) to preserve replayability.
|
|
21
|
+
*/
|
|
7
22
|
export declare function readyLeaves(issues: readonly Issue[], opts?: {
|
|
8
23
|
root_id?: string;
|
|
9
24
|
tags?: readonly string[];
|
|
10
25
|
}): Issue[];
|
|
26
|
+
/**
|
|
27
|
+
* Reconcile retry primitive.
|
|
28
|
+
*
|
|
29
|
+
* Produces a deterministic list of closed nodes that are eligible to be reopened for orchestration.
|
|
30
|
+
* The orchestrator decides whether/when to apply the reopen side effect.
|
|
31
|
+
*/
|
|
32
|
+
export declare function retryableDagCandidates(issues: readonly Issue[], opts: {
|
|
33
|
+
root_id: string;
|
|
34
|
+
retry_outcomes?: readonly string[];
|
|
35
|
+
attempts_by_issue_id?: ReadonlyMap<string, number>;
|
|
36
|
+
max_attempts?: number;
|
|
37
|
+
}): RetryableDagCandidate[];
|
|
11
38
|
export declare function collapsible(issues: readonly Issue[], rootId: string): Issue[];
|
|
39
|
+
/**
|
|
40
|
+
* Reconcile termination primitive.
|
|
41
|
+
*
|
|
42
|
+
* `is_final=false` is a hard signal that orchestrator must continue reconciling (or repair invalid
|
|
43
|
+
* expanded state) before the root run can be considered terminal.
|
|
44
|
+
*/
|
|
12
45
|
export declare function validateDag(issues: readonly Issue[], rootId: string): ValidationResult;
|
|
13
46
|
//# sourceMappingURL=dag.d.ts.map
|
package/dist/dag.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dag.d.ts","sourceRoot":"","sources":["../src/dag.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAEvC,MAAM,MAAM,gBAAgB,GAAG;IAC9B,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CACf,CAAC;
|
|
1
|
+
{"version":3,"file":"dag.d.ts","sourceRoot":"","sources":["../src/dag.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAEvC,MAAM,MAAM,gBAAgB,GAAG;IAC9B,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IACnC,KAAK,EAAE,KAAK,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CACf,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,kCAAkC,slBAMrC,CAAC;AA0BX,wBAAgB,UAAU,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAuB7E;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAC1B,MAAM,EAAE,SAAS,KAAK,EAAE,EACxB,IAAI,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAO,GACvD,KAAK,EAAE,CAuCT;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACrC,MAAM,EAAE,SAAS,KAAK,EAAE,EACxB,IAAI,EAAE;IACL,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,oBAAoB,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnD,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB,GACC,qBAAqB,EAAE,CAsCzB;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,KAAK,EAAE,CA6B7E;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,gBAAgB,CAqDtF"}
|
package/dist/dag.js
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic DAG reconcile primitives used by orchestrator reconciliation.
|
|
3
|
+
*
|
|
4
|
+
* Contract: these helpers are pure functions over the provided issue snapshot and must remain
|
|
5
|
+
* side-effect free so reconcile passes are replayable/idempotent.
|
|
6
|
+
*/
|
|
7
|
+
export const DAG_RECONCILE_PRIMITIVE_INVARIANTS = [
|
|
8
|
+
"DAG-RECON-001: `readyLeaves` only returns open, unblocked, leaf issues within the requested subtree scope.",
|
|
9
|
+
"DAG-RECON-002: `readyLeaves` ordering is deterministic for a fixed input snapshot (priority-ordered candidate set).",
|
|
10
|
+
"DAG-RECON-003: `validateDag(...).is_final=true` implies no remaining non-expanded open work in the subtree.",
|
|
11
|
+
"DAG-RECON-004: closed(failure|needs_work) and expanded-without-children are non-final and require reconcile action.",
|
|
12
|
+
"DAG-RECON-005: `retryableDagCandidates` selection is deterministic and side-effect free for a fixed snapshot + attempts map.",
|
|
13
|
+
];
|
|
1
14
|
function childrenByParent(issues) {
|
|
2
15
|
const byParent = new Map();
|
|
3
16
|
for (const issue of issues) {
|
|
@@ -12,6 +25,14 @@ function childrenByParent(issues) {
|
|
|
12
25
|
}
|
|
13
26
|
return byParent;
|
|
14
27
|
}
|
|
28
|
+
function compareByPriorityThenId(a, b) {
|
|
29
|
+
const pa = a.priority ?? 3;
|
|
30
|
+
const pb = b.priority ?? 3;
|
|
31
|
+
if (pa !== pb) {
|
|
32
|
+
return pa - pb;
|
|
33
|
+
}
|
|
34
|
+
return a.id.localeCompare(b.id);
|
|
35
|
+
}
|
|
15
36
|
export function subtreeIds(issues, rootId) {
|
|
16
37
|
const children = childrenByParent(issues);
|
|
17
38
|
const result = [];
|
|
@@ -33,6 +54,10 @@ export function subtreeIds(issues, rootId) {
|
|
|
33
54
|
}
|
|
34
55
|
return result;
|
|
35
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Reconcile selection primitive: the orchestrator must only dispatch from this ready set (or an
|
|
59
|
+
* equivalent deterministic adapter) to preserve replayability.
|
|
60
|
+
*/
|
|
36
61
|
export function readyLeaves(issues, opts = {}) {
|
|
37
62
|
const byId = new Map(issues.map((i) => [i.id, i]));
|
|
38
63
|
const idsInScope = new Set(opts.root_id ? subtreeIds(issues, opts.root_id) : byId.keys());
|
|
@@ -66,14 +91,55 @@ export function readyLeaves(issues, opts = {}) {
|
|
|
66
91
|
}
|
|
67
92
|
result.push(issue);
|
|
68
93
|
}
|
|
69
|
-
result.sort(
|
|
94
|
+
result.sort(compareByPriorityThenId);
|
|
70
95
|
return result;
|
|
71
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* Reconcile retry primitive.
|
|
99
|
+
*
|
|
100
|
+
* Produces a deterministic list of closed nodes that are eligible to be reopened for orchestration.
|
|
101
|
+
* The orchestrator decides whether/when to apply the reopen side effect.
|
|
102
|
+
*/
|
|
103
|
+
export function retryableDagCandidates(issues, opts) {
|
|
104
|
+
const idsInScope = new Set(subtreeIds(issues, opts.root_id));
|
|
105
|
+
const children = childrenByParent(issues);
|
|
106
|
+
const retryOutcomes = new Set(opts.retry_outcomes ?? ["failure", "needs_work"]);
|
|
107
|
+
const maxAttempts = typeof opts.max_attempts === "number" && Number.isFinite(opts.max_attempts)
|
|
108
|
+
? Math.max(1, Math.trunc(opts.max_attempts))
|
|
109
|
+
: Number.POSITIVE_INFINITY;
|
|
110
|
+
const out = [];
|
|
111
|
+
for (const issue of issues) {
|
|
112
|
+
if (!idsInScope.has(issue.id)) {
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (issue.status !== "closed") {
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
const attempts = opts.attempts_by_issue_id?.get(issue.id) ?? 0;
|
|
119
|
+
if (attempts >= maxAttempts) {
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
if (issue.outcome && retryOutcomes.has(issue.outcome)) {
|
|
123
|
+
const hasOpenChildren = (children.get(issue.id) ?? []).some((child) => child.status !== "closed");
|
|
124
|
+
if (!hasOpenChildren) {
|
|
125
|
+
out.push({ issue, reason: `outcome=${issue.outcome}` });
|
|
126
|
+
}
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
if (issue.outcome === "expanded" && (children.get(issue.id)?.length ?? 0) === 0) {
|
|
130
|
+
out.push({ issue, reason: "outcome=expanded_without_children" });
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
out.sort((a, b) => compareByPriorityThenId(a.issue, b.issue));
|
|
134
|
+
return out;
|
|
135
|
+
}
|
|
72
136
|
export function collapsible(issues, rootId) {
|
|
73
137
|
const byId = new Map(issues.map((i) => [i.id, i]));
|
|
74
138
|
const idsInScope = new Set(subtreeIds(issues, rootId));
|
|
75
139
|
const children = childrenByParent(issues);
|
|
76
|
-
|
|
140
|
+
// `refine` is terminal for a closed reviewer node; refinement itself is
|
|
141
|
+
// orchestrated by root-phase reconcile transitions.
|
|
142
|
+
const terminalOutcomes = new Set(["success", "skipped", "refine"]);
|
|
77
143
|
const result = [];
|
|
78
144
|
for (const issueId of idsInScope) {
|
|
79
145
|
const node = byId.get(issueId);
|
|
@@ -94,6 +160,12 @@ export function collapsible(issues, rootId) {
|
|
|
94
160
|
result.sort((a, b) => a.id.localeCompare(b.id));
|
|
95
161
|
return result;
|
|
96
162
|
}
|
|
163
|
+
/**
|
|
164
|
+
* Reconcile termination primitive.
|
|
165
|
+
*
|
|
166
|
+
* `is_final=false` is a hard signal that orchestrator must continue reconciling (or repair invalid
|
|
167
|
+
* expanded state) before the root run can be considered terminal.
|
|
168
|
+
*/
|
|
97
169
|
export function validateDag(issues, rootId) {
|
|
98
170
|
const byId = new Map(issues.map((i) => [i.id, i]));
|
|
99
171
|
const ids = new Set(subtreeIds(issues, rootId));
|