@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 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;AAiBF,wBAAgB,UAAU,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAuB7E;AAED,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,wBAAgB,WAAW,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,KAAK,EAAE,CA2B7E;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,gBAAgB,CAqDtF"}
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((a, b) => (a.priority ?? 3) - (b.priority ?? 3));
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
- const terminalOutcomes = new Set(["success", "skipped"]);
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));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@femtomc/mu-core",
3
- "version": "26.2.70",
3
+ "version": "26.2.72",
4
4
  "description": "Core primitives for mu: IDs, events, DAG helpers, and persistence interfaces.",
5
5
  "keywords": [
6
6
  "mu",