looply-cli 0.5.4 → 0.5.6
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/.src-hash +1 -1
- package/dist/autonomy-controller.d.ts +14 -1
- package/dist/autonomy-controller.d.ts.map +1 -1
- package/dist/autonomy-controller.js +55 -2
- package/dist/autonomy-controller.js.map +1 -1
- package/dist/autonomy-store.d.ts +35 -2
- package/dist/autonomy-store.d.ts.map +1 -1
- package/dist/autonomy-store.js +27 -2
- package/dist/autonomy-store.js.map +1 -1
- package/dist/autonomy-tick.d.ts +25 -2
- package/dist/autonomy-tick.d.ts.map +1 -1
- package/dist/autonomy-tick.js +122 -4
- package/dist/autonomy-tick.js.map +1 -1
- package/dist/bin.d.ts.map +1 -1
- package/dist/bin.js +2 -0
- package/dist/bin.js.map +1 -1
- package/dist/checkout-carry.d.ts +2 -0
- package/dist/checkout-carry.d.ts.map +1 -1
- package/dist/checkout-carry.js +37 -2
- package/dist/checkout-carry.js.map +1 -1
- package/dist/config.d.ts +41 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +40 -7
- package/dist/config.js.map +1 -1
- package/dist/coordinator-state.d.ts +14 -0
- package/dist/coordinator-state.d.ts.map +1 -1
- package/dist/coordinator-state.js +29 -0
- package/dist/coordinator-state.js.map +1 -1
- package/dist/daemon-cmd.d.ts +1 -1
- package/dist/daemon-cmd.d.ts.map +1 -1
- package/dist/daemon-cmd.js +56 -33
- package/dist/daemon-cmd.js.map +1 -1
- package/dist/git-driver.d.ts +6 -0
- package/dist/git-driver.d.ts.map +1 -1
- package/dist/git-driver.js +12 -0
- package/dist/git-driver.js.map +1 -1
- package/dist/goal-adaptive.d.ts +12 -0
- package/dist/goal-adaptive.d.ts.map +1 -1
- package/dist/goal-adaptive.js +19 -4
- package/dist/goal-adaptive.js.map +1 -1
- package/dist/goal-compile.d.ts +7 -0
- package/dist/goal-compile.d.ts.map +1 -1
- package/dist/goal-compile.js +3 -0
- package/dist/goal-compile.js.map +1 -1
- package/dist/goal-execution-evidence.d.ts +28 -2
- package/dist/goal-execution-evidence.d.ts.map +1 -1
- package/dist/goal-execution-evidence.js +30 -5
- package/dist/goal-execution-evidence.js.map +1 -1
- package/dist/goal-reproof.d.ts +42 -0
- package/dist/goal-reproof.d.ts.map +1 -0
- package/dist/goal-reproof.js +35 -0
- package/dist/goal-reproof.js.map +1 -0
- package/dist/index-for-head.js +1 -1
- package/dist/index-for-head.js.map +1 -1
- package/dist/machine-run-slots.d.ts +59 -0
- package/dist/machine-run-slots.d.ts.map +1 -0
- package/dist/machine-run-slots.js +140 -0
- package/dist/machine-run-slots.js.map +1 -0
- package/dist/plan-recovery.d.ts +70 -0
- package/dist/plan-recovery.d.ts.map +1 -0
- package/dist/plan-recovery.js +131 -0
- package/dist/plan-recovery.js.map +1 -0
- package/dist/programme-mode.d.ts +6 -0
- package/dist/programme-mode.d.ts.map +1 -1
- package/dist/programme-mode.js +23 -0
- package/dist/programme-mode.js.map +1 -1
- package/dist/project-index-store.d.ts +19 -1
- package/dist/project-index-store.d.ts.map +1 -1
- package/dist/project-index-store.js +40 -5
- package/dist/project-index-store.js.map +1 -1
- package/dist/project-terminal.d.ts +25 -0
- package/dist/project-terminal.d.ts.map +1 -0
- package/dist/project-terminal.js +46 -0
- package/dist/project-terminal.js.map +1 -0
- package/dist/pursuit-ending.d.ts +57 -0
- package/dist/pursuit-ending.d.ts.map +1 -0
- package/dist/pursuit-ending.js +128 -0
- package/dist/pursuit-ending.js.map +1 -0
- package/dist/run-base.d.ts +84 -0
- package/dist/run-base.d.ts.map +1 -0
- package/dist/run-base.js +142 -0
- package/dist/run-base.js.map +1 -0
- package/dist/run-cmd.d.ts.map +1 -1
- package/dist/run-cmd.js +110 -14
- package/dist/run-cmd.js.map +1 -1
- package/dist/run-ended.d.ts +16 -0
- package/dist/run-ended.d.ts.map +1 -1
- package/dist/run-ended.js +30 -0
- package/dist/run-ended.js.map +1 -1
- package/dist/run-ending-locator.d.ts +10 -0
- package/dist/run-ending-locator.d.ts.map +1 -1
- package/dist/run-ending-locator.js +45 -0
- package/dist/run-ending-locator.js.map +1 -1
- package/dist/run-launcher.d.ts +17 -1
- package/dist/run-launcher.d.ts.map +1 -1
- package/dist/run-launcher.js +36 -5
- package/dist/run-launcher.js.map +1 -1
- package/dist/server-client.d.ts +5 -1
- package/dist/server-client.d.ts.map +1 -1
- package/dist/server-client.js +1 -0
- package/dist/server-client.js.map +1 -1
- package/dist/setup.d.ts.map +1 -1
- package/dist/setup.js +17 -0
- package/dist/setup.js.map +1 -1
- package/dist/workspace-provisioner.d.ts +2 -0
- package/dist/workspace-provisioner.d.ts.map +1 -1
- package/dist/workspace-provisioner.js +27 -1
- package/dist/workspace-provisioner.js.map +1 -1
- package/package.json +54 -54
- package/win/HandleProbe.cs +116 -116
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Item, type RunScope } from '@looply/core';
|
|
1
|
+
import { type GateStepResult, type Item, type RunScope } from '@looply/core';
|
|
2
2
|
import type { RoundRecord } from './round-records';
|
|
3
3
|
/**
|
|
4
4
|
* KEY 2 — THE HALF OF "THIS GOAL IS MET" THAT CANNOT BE TALKED INTO AN ANSWER.
|
|
@@ -96,6 +96,24 @@ export interface GoalExecutionInput {
|
|
|
96
96
|
* a person's own `git commit` can never qualify.
|
|
97
97
|
*/
|
|
98
98
|
countManualWork?: boolean;
|
|
99
|
+
/**
|
|
100
|
+
* THE GATES, RUN AGAIN AT THE CURRENT HEAD, when the tree moved on since this
|
|
101
|
+
* plan was delivered (`goal-reproof.ts`).
|
|
102
|
+
*
|
|
103
|
+
* 🔴 WHY: TAB-B-57, 63 and 64 (2026-10-15) delivered every step of their plans,
|
|
104
|
+
* and the next Start found another ticket's run had moved this checkout's HEAD
|
|
105
|
+
* in between — stacked on top of their own commits. KEY 2 answered "the code
|
|
106
|
+
* has moved on … what is here now was never checked", which was TRUE, and the
|
|
107
|
+
* goal went back to a person for it. The honest repair is not to relax (2)
|
|
108
|
+
* above but to do the checking: run every required step at the tree that is
|
|
109
|
+
* here now. Accepted only when a committed round of this plan is CONTAINED in
|
|
110
|
+
* that tree (`contains`: the plan's committed shas that are ancestors of HEAD),
|
|
111
|
+
* so a re-proof can never vouch for a tree that lacks the work.
|
|
112
|
+
*/
|
|
113
|
+
rechecked?: {
|
|
114
|
+
readonly contains: ReadonlySet<string>;
|
|
115
|
+
readonly steps: readonly GateStepResult[];
|
|
116
|
+
};
|
|
99
117
|
}
|
|
100
118
|
export type GoalExecutionOutcome = {
|
|
101
119
|
ok: true;
|
|
@@ -116,11 +134,19 @@ export type GoalExecutionOutcome = {
|
|
|
116
134
|
* gate record is the only one that can speak for it.
|
|
117
135
|
*/
|
|
118
136
|
delivered: readonly string[];
|
|
119
|
-
|
|
137
|
+
/** Set when the answer rests on a re-proof at HEAD: the steps that ran there, which speak for this tree. */
|
|
138
|
+
reproved?: readonly GateStepResult[];
|
|
139
|
+
}
|
|
140
|
+
/** `moved`: the only refusal a re-proof at the current tree can answer (see `rechecked`). */
|
|
141
|
+
| {
|
|
120
142
|
ok: false;
|
|
121
143
|
because: string;
|
|
144
|
+
moved?: true;
|
|
122
145
|
};
|
|
146
|
+
export declare const CODE_MOVED_ON = "the code has moved on since this plan was delivered, so what is here now was never checked";
|
|
123
147
|
export declare function confirmGoalExecutionEvidence(input: GoalExecutionInput): GoalExecutionOutcome;
|
|
148
|
+
/** The commits this plan's delivered work made — what a re-proof asks git about (`rechecked.contains`). */
|
|
149
|
+
export declare function committedShasOf(input: Pick<GoalExecutionInput, 'scope' | 'items' | 'rounds' | 'countManualWork'>): string[];
|
|
124
150
|
/** A committed round, as a goal-met Start locates it: its commit and the run that made it. */
|
|
125
151
|
export interface DeliveredTip {
|
|
126
152
|
readonly sha: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"goal-execution-evidence.d.ts","sourceRoot":"","sources":["../src/goal-execution-evidence.ts"],"names":[],"mappings":"AAAA,OAAO,EAA+B,KAAK,IAAI,EAAE,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"goal-execution-evidence.d.ts","sourceRoot":"","sources":["../src/goal-execution-evidence.ts"],"names":[],"mappings":"AAAA,OAAO,EAA+B,KAAK,cAAc,EAAE,KAAK,IAAI,EAAE,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE1G,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEnD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmEG;AAEH,MAAM,WAAW,kBAAkB;IACjC;;;;;;;;;OASG;IACH,KAAK,EAAE,QAAQ,CAAC;IAChB,KAAK,EAAE,SAAS,IAAI,EAAE,CAAC;IACvB,MAAM,EAAE,SAAS,WAAW,EAAE,CAAC;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,0EAA0E;IAC1E,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC;;;;;;;;;OASG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;;;;;;;;;;OAaG;IACH,SAAS,CAAC,EAAE;QACV,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;QACvC,QAAQ,CAAC,KAAK,EAAE,SAAS,cAAc,EAAE,CAAC;KAC3C,CAAC;CACH;AAED,MAAM,MAAM,oBAAoB,GAC5B;IACE,EAAE,EAAE,IAAI,CAAC;IACT,KAAK,EAAE,WAAW,CAAC;IACnB;;;;;;;;;;;;;;OAcG;IACH,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7B,4GAA4G;IAC5G,QAAQ,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;CACtC;AACH,6FAA6F;GAC3F;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,IAAI,CAAA;CAAE,CAAC;AAEjD,eAAO,MAAM,aAAa,+FACoE,CAAC;AAE/F,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,kBAAkB,GAAG,oBAAoB,CAkH5F;AA8CD,2GAA2G;AAC3G,wBAAgB,eAAe,CAAC,KAAK,EAAE,IAAI,CAAC,kBAAkB,EAAE,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,iBAAiB,CAAC,GAAG,MAAM,EAAE,CAM3H;AAED,8FAA8F;AAC9F,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,IAAI,CAAC,kBAAkB,EAAE,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,iBAAiB,CAAC,GAChF,YAAY,GAAG,IAAI,CAOrB"}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CODE_MOVED_ON = void 0;
|
|
3
4
|
exports.confirmGoalExecutionEvidence = confirmGoalExecutionEvidence;
|
|
5
|
+
exports.committedShasOf = committedShasOf;
|
|
4
6
|
exports.deliveredTipOf = deliveredTipOf;
|
|
5
7
|
const core_1 = require("@looply/core");
|
|
6
8
|
const goal_compile_1 = require("./goal-compile");
|
|
9
|
+
exports.CODE_MOVED_ON = 'the code has moved on since this plan was delivered, so what is here now was never checked';
|
|
7
10
|
function confirmGoalExecutionEvidence(input) {
|
|
8
11
|
const { scope, currentHead } = input;
|
|
9
12
|
if (scope === null)
|
|
@@ -49,11 +52,23 @@ function confirmGoalExecutionEvidence(input) {
|
|
|
49
52
|
const atHead = committed.filter((r) => r.sha === currentHead);
|
|
50
53
|
if (atHead.length === 0) {
|
|
51
54
|
// Deliberately not "close enough". Something has moved the repository since
|
|
52
|
-
// this plan landed, and nothing here has checked that the goal survived it
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
// this plan landed, and nothing here has checked that the goal survived it —
|
|
56
|
+
// unless the gates were run again at this tree, and the plan's work is in it.
|
|
57
|
+
const rechecked = input.rechecked;
|
|
58
|
+
if (rechecked === undefined)
|
|
59
|
+
return { ok: false, because: exports.CODE_MOVED_ON, moved: true };
|
|
60
|
+
const contained = committed.filter((r) => {
|
|
61
|
+
const item = mineById.get(r.itemId);
|
|
62
|
+
return r.sha !== undefined && rechecked.contains.has(r.sha) && item !== undefined && !(0, goal_compile_1.isPlanMarker)(item);
|
|
63
|
+
});
|
|
64
|
+
if (contained.length === 0) {
|
|
65
|
+
return { ok: false, because: 'the code has moved on since this plan was delivered, and its work is not in the tree that is here now' };
|
|
66
|
+
}
|
|
67
|
+
const permitted = (0, core_1.mayCommit)({ steps: [...rechecked.steps], requiredSteps: input.requiredSteps, proofEnabled: false });
|
|
68
|
+
if (!permitted.ok) {
|
|
69
|
+
return { ok: false, because: `the gates were run again at the current tree, and ${permitted.reason ?? 'they did not pass'}` };
|
|
70
|
+
}
|
|
71
|
+
return { ok: true, round: contained[contained.length - 1], delivered: [...deliveredIds], reproved: rechecked.steps };
|
|
57
72
|
}
|
|
58
73
|
// An EXISTS, not a FOR-ALL, and that is the whole reachability argument: a
|
|
59
74
|
// plan of five steps produces five commits and only the last is HEAD, so
|
|
@@ -141,6 +156,16 @@ function committedOfPlan(input, scope) {
|
|
|
141
156
|
}
|
|
142
157
|
return { ok: true, committed, deliveredIds, mineById };
|
|
143
158
|
}
|
|
159
|
+
/** The commits this plan's delivered work made — what a re-proof asks git about (`rechecked.contains`). */
|
|
160
|
+
function committedShasOf(input) {
|
|
161
|
+
const { scope } = input;
|
|
162
|
+
if (scope === null || scope.planRevision === undefined)
|
|
163
|
+
return [];
|
|
164
|
+
const plan = committedOfPlan(input, scope);
|
|
165
|
+
if (!plan.ok)
|
|
166
|
+
return [];
|
|
167
|
+
return plan.committed.flatMap((r) => (r.sha === undefined ? [] : [r.sha]));
|
|
168
|
+
}
|
|
144
169
|
/**
|
|
145
170
|
* The plan's NEWEST committed round — the delivered tree a goal-met Start judges while the
|
|
146
171
|
* lane has not moved it (F27). Null wherever KEY 2 would find nothing to confirm, and when
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"goal-execution-evidence.js","sourceRoot":"","sources":["../src/goal-execution-evidence.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"goal-execution-evidence.js","sourceRoot":"","sources":["../src/goal-execution-evidence.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { GateStepResult } from '@looply/core';
|
|
2
|
+
/**
|
|
3
|
+
* RE-CHECK THE TREE THAT IS HERE NOW, RATHER THAN HAND A DELIVERED GOAL BACK.
|
|
4
|
+
*
|
|
5
|
+
* KEY 2 (`goal-execution-evidence.ts`) refuses a "goal met" verdict when HEAD is
|
|
6
|
+
* not the commit the plan's last round made: nothing has checked the tree that
|
|
7
|
+
* is here now. On 2026-10-15 that refusal ended three Tabsy tickets whose every
|
|
8
|
+
* step had been delivered, gated and proved (TAB-B-57, 63, 64) — another
|
|
9
|
+
* ticket's run had been cut on top of their commits in the same checkout, so
|
|
10
|
+
* HEAD had moved on and the goal went back to a person.
|
|
11
|
+
*
|
|
12
|
+
* This does the missing check instead: when the plan's work is contained in
|
|
13
|
+
* HEAD and the working tree is clean, every gate step runs again at HEAD, and
|
|
14
|
+
* KEY 2 is asked again with what those steps say. A tree without the work, a
|
|
15
|
+
* dirty tree, or a red gate still refuses — with the reason.
|
|
16
|
+
*
|
|
17
|
+
* Every verb is injected, so production and the tests run this same code.
|
|
18
|
+
*/
|
|
19
|
+
export interface ReproofDeps {
|
|
20
|
+
/** Uncommitted paths in the working tree. */
|
|
21
|
+
changedPaths(): Promise<readonly string[]>;
|
|
22
|
+
isAncestor(ancestor: string, of: string): Promise<boolean>;
|
|
23
|
+
changedPathsBetween(fromSha: string, toSha: string): Promise<readonly string[]>;
|
|
24
|
+
/** Run the project's gate at the current tree; `changedPaths` feeds the steps' `when:` filters. */
|
|
25
|
+
runGates(changedPaths: readonly string[]): Promise<readonly GateStepResult[]>;
|
|
26
|
+
}
|
|
27
|
+
export type ReproofOutcome = {
|
|
28
|
+
readonly ok: true;
|
|
29
|
+
readonly contains: ReadonlySet<string>;
|
|
30
|
+
readonly steps: readonly GateStepResult[];
|
|
31
|
+
} | {
|
|
32
|
+
readonly ok: false;
|
|
33
|
+
readonly because: string;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* `committed` is the plan's committed shas in ledger order (`committedShasOf`).
|
|
37
|
+
* The `when:` paths are everything from the parent of the plan's first
|
|
38
|
+
* contained commit to HEAD, so a step filtered to the files this plan touched
|
|
39
|
+
* still runs.
|
|
40
|
+
*/
|
|
41
|
+
export declare function reproveAtHead(head: string, committed: readonly string[], deps: ReproofDeps): Promise<ReproofOutcome>;
|
|
42
|
+
//# sourceMappingURL=goal-reproof.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"goal-reproof.d.ts","sourceRoot":"","sources":["../src/goal-reproof.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,WAAW;IAC1B,6CAA6C;IAC7C,YAAY,IAAI,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAAC;IAC3C,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3D,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAAC;IAChF,mGAAmG;IACnG,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,SAAS,cAAc,EAAE,CAAC,CAAC;CAC/E;AAED,MAAM,MAAM,cAAc,GACtB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,cAAc,EAAE,CAAA;CAAE,GACxG;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAErD;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,cAAc,CAAC,CAoB1H"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.reproveAtHead = reproveAtHead;
|
|
4
|
+
/**
|
|
5
|
+
* `committed` is the plan's committed shas in ledger order (`committedShasOf`).
|
|
6
|
+
* The `when:` paths are everything from the parent of the plan's first
|
|
7
|
+
* contained commit to HEAD, so a step filtered to the files this plan touched
|
|
8
|
+
* still runs.
|
|
9
|
+
*/
|
|
10
|
+
async function reproveAtHead(head, committed, deps) {
|
|
11
|
+
if (head === '')
|
|
12
|
+
return { ok: false, because: 'the current commit could not be read' };
|
|
13
|
+
const dirty = await deps.changedPaths();
|
|
14
|
+
if (dirty.length > 0) {
|
|
15
|
+
return { ok: false, because: `the working tree has ${dirty.length} uncommitted change(s), so it is not the tree a gate would check` };
|
|
16
|
+
}
|
|
17
|
+
const contains = [];
|
|
18
|
+
for (const sha of committed) {
|
|
19
|
+
if (sha === head || (await deps.isAncestor(sha, head).catch(() => false)))
|
|
20
|
+
contains.push(sha);
|
|
21
|
+
}
|
|
22
|
+
if (contains.length === 0)
|
|
23
|
+
return { ok: false, because: 'none of this plan’s commits is in the tree that is here now' };
|
|
24
|
+
let paths = [];
|
|
25
|
+
try {
|
|
26
|
+
paths = await deps.changedPathsBetween(`${contains[0]}~1`, head);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// A plan whose first commit is the repository's root has no parent: the plan's own commits still name its files.
|
|
30
|
+
paths = await deps.changedPathsBetween(contains[0], head).catch(() => []);
|
|
31
|
+
}
|
|
32
|
+
const steps = await deps.runGates(paths);
|
|
33
|
+
return { ok: true, contains: new Set(contains), steps };
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=goal-reproof.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"goal-reproof.js","sourceRoot":"","sources":["../src/goal-reproof.ts"],"names":[],"mappings":";;AAsCA,sCAoBC;AA1BD;;;;;GAKG;AACI,KAAK,UAAU,aAAa,CAAC,IAAY,EAAE,SAA4B,EAAE,IAAiB;IAC/F,IAAI,IAAI,KAAK,EAAE;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,sCAAsC,EAAE,CAAC;IACvF,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC;IACxC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,wBAAwB,KAAK,CAAC,MAAM,kEAAkE,EAAE,CAAC;IACxI,CAAC;IACD,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,KAAK,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;QAC5B,IAAI,GAAG,KAAK,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC;YAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAChG,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,6DAA6D,EAAE,CAAC;IACxH,IAAI,KAAK,GAAsB,EAAE,CAAC;IAClC,IAAI,CAAC;QACH,KAAK,GAAG,MAAM,IAAI,CAAC,mBAAmB,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IACpE,CAAC;IAAC,MAAM,CAAC;QACP,iHAAiH;QACjH,KAAK,GAAG,MAAM,IAAI,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC,CAAE,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;IAC7E,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACzC,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,GAAG,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,CAAC;AAC1D,CAAC"}
|
package/dist/index-for-head.js
CHANGED
|
@@ -75,7 +75,7 @@ async function indexForHead(cwd) {
|
|
|
75
75
|
async function indexForTree(cwd, treeSha) {
|
|
76
76
|
const git = new git_driver_1.GitDriver((0, git_driver_1.nodeGitExec)(cwd));
|
|
77
77
|
const packages = packagesFor(cwd);
|
|
78
|
-
const store = new project_index_store_1.ProjectIndexStore((0, git_driver_1.executionStateDir)(cwd), ledger_store_1.nodeLedgerFs);
|
|
78
|
+
const store = new project_index_store_1.ProjectIndexStore((0, git_driver_1.executionStateDir)(cwd), ledger_store_1.nodeLedgerFs, (0, git_driver_1.projectStateDir)(cwd));
|
|
79
79
|
const cached = await store.load(treeSha, packages.map((pkg) => pkg.name));
|
|
80
80
|
if (cached !== null)
|
|
81
81
|
return { ok: true, treeSha, snapshot: cached, cached: true };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-for-head.js","sourceRoot":"","sources":["../src/index-for-head.ts"],"names":[],"mappings":";;;AAuCA,8CAOC;AAcD,oCAUC;AAMD,oCAgBC;AA5FD,qCAAmD;AACnD,yCAAiC;AAEjC,qCAA0D;AAC1D,
|
|
1
|
+
{"version":3,"file":"index-for-head.js","sourceRoot":"","sources":["../src/index-for-head.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type ProbeFn } from './run-lease';
|
|
2
|
+
/**
|
|
3
|
+
* HOW MANY AUTONOMOUS RUNS THIS COMPUTER HAS, ACROSS EVERY REPOSITORY IT SERVES.
|
|
4
|
+
*
|
|
5
|
+
* `looply serve` runs one daemon per repository, and each daemon's coordinator
|
|
6
|
+
* bounds only its own project (`machine.max_parallel_runs`). A PC serving 27
|
|
7
|
+
* repositories at 3 each could otherwise launch 81 agent sessions at once. This
|
|
8
|
+
* is the one number they share: `machine.max_runs_on_machine` (default 4).
|
|
9
|
+
*
|
|
10
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
11
|
+
* ONE FILE PER SLOT, IN `~/.looply/machine-runs/`.
|
|
12
|
+
*
|
|
13
|
+
* A slot is CLAIMED before a launch (pending, holding the daemon's pid) and
|
|
14
|
+
* BOUND to the child's pid once it is spawned. A slot frees itself: a bound
|
|
15
|
+
* slot whose pid is dead, or a pending slot older than `PENDING_TTL_MS`, is not
|
|
16
|
+
* counted and is swept on the next claim. Nothing has to remember to release,
|
|
17
|
+
* so a crashed run or daemon cannot leak capacity for longer than one probe.
|
|
18
|
+
*
|
|
19
|
+
* Claims are serialised by the shared exclusive lock, so two daemons cannot
|
|
20
|
+
* both see the last free slot and both take it.
|
|
21
|
+
*
|
|
22
|
+
* ⚠️ A CLAIM IS ASKED BEFORE A TRY IS SPENT. The launcher's `busyFor` takes it
|
|
23
|
+
* (see `run-launcher.ts`), and `busyFor` runs before `reserveLaunch` in the
|
|
24
|
+
* tick, so "the machine is full" is a queue — never a failed attempt. A claim
|
|
25
|
+
* is idempotent per request: asking again for the same request refreshes it.
|
|
26
|
+
*
|
|
27
|
+
* ⚠️ AN UNKNOWN LIVENESS COUNTS AS ALIVE, the direction that cannot overshoot.
|
|
28
|
+
*/
|
|
29
|
+
/** A claim that never became a run stops holding a slot after this. */
|
|
30
|
+
export declare const PENDING_TTL_MS: number;
|
|
31
|
+
export interface MachineSlot {
|
|
32
|
+
readonly key: string;
|
|
33
|
+
readonly pid: number;
|
|
34
|
+
readonly pending: boolean;
|
|
35
|
+
readonly at: string;
|
|
36
|
+
/** Which repository the run is for — for a person reading the directory. */
|
|
37
|
+
readonly repo?: string;
|
|
38
|
+
}
|
|
39
|
+
export interface MachineRunSlots {
|
|
40
|
+
/** Take (or refresh) a slot for `key`; null when taken, else why the machine is full. */
|
|
41
|
+
claim(key: string): string | null;
|
|
42
|
+
/** The launch happened: the slot now lives as long as `pid` does. */
|
|
43
|
+
bind(key: string, pid: number): void;
|
|
44
|
+
/** The launch did not happen. */
|
|
45
|
+
release(key: string): void;
|
|
46
|
+
/** Live slots right now, `key` excluded when given. */
|
|
47
|
+
inUse(except?: string): number;
|
|
48
|
+
readonly cap: number;
|
|
49
|
+
}
|
|
50
|
+
export interface MachineRunSlotDeps {
|
|
51
|
+
readonly home?: string;
|
|
52
|
+
readonly now?: () => number;
|
|
53
|
+
readonly probe?: ProbeFn;
|
|
54
|
+
readonly pid?: number;
|
|
55
|
+
readonly repo?: string;
|
|
56
|
+
}
|
|
57
|
+
export declare function machineSlotsDir(home?: string): string;
|
|
58
|
+
export declare function machineRunSlots(cap: number, deps?: MachineRunSlotDeps): MachineRunSlots;
|
|
59
|
+
//# sourceMappingURL=machine-run-slots.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"machine-run-slots.d.ts","sourceRoot":"","sources":["../src/machine-run-slots.ts"],"names":[],"mappings":"AAKA,OAAO,EAAa,KAAK,OAAO,EAAE,MAAM,aAAa,CAAC;AAEtD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,uEAAuE;AACvE,eAAO,MAAM,cAAc,QAAa,CAAC;AAEzC,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,4EAA4E;IAC5E,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,eAAe;IAC9B,yFAAyF;IACzF,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAClC,qEAAqE;IACrE,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,iCAAiC;IACjC,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,uDAAuD;IACvD,KAAK,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,eAAe,CAAC,IAAI,GAAE,MAAkB,GAAG,MAAM,CAEhE;AAID,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,kBAAuB,GAAG,eAAe,CA8F3F"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PENDING_TTL_MS = void 0;
|
|
4
|
+
exports.machineSlotsDir = machineSlotsDir;
|
|
5
|
+
exports.machineRunSlots = machineRunSlots;
|
|
6
|
+
const node_crypto_1 = require("node:crypto");
|
|
7
|
+
const node_fs_1 = require("node:fs");
|
|
8
|
+
const node_os_1 = require("node:os");
|
|
9
|
+
const node_path_1 = require("node:path");
|
|
10
|
+
const exclusive_lock_1 = require("./exclusive-lock");
|
|
11
|
+
const run_lease_1 = require("./run-lease");
|
|
12
|
+
/**
|
|
13
|
+
* HOW MANY AUTONOMOUS RUNS THIS COMPUTER HAS, ACROSS EVERY REPOSITORY IT SERVES.
|
|
14
|
+
*
|
|
15
|
+
* `looply serve` runs one daemon per repository, and each daemon's coordinator
|
|
16
|
+
* bounds only its own project (`machine.max_parallel_runs`). A PC serving 27
|
|
17
|
+
* repositories at 3 each could otherwise launch 81 agent sessions at once. This
|
|
18
|
+
* is the one number they share: `machine.max_runs_on_machine` (default 4).
|
|
19
|
+
*
|
|
20
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
21
|
+
* ONE FILE PER SLOT, IN `~/.looply/machine-runs/`.
|
|
22
|
+
*
|
|
23
|
+
* A slot is CLAIMED before a launch (pending, holding the daemon's pid) and
|
|
24
|
+
* BOUND to the child's pid once it is spawned. A slot frees itself: a bound
|
|
25
|
+
* slot whose pid is dead, or a pending slot older than `PENDING_TTL_MS`, is not
|
|
26
|
+
* counted and is swept on the next claim. Nothing has to remember to release,
|
|
27
|
+
* so a crashed run or daemon cannot leak capacity for longer than one probe.
|
|
28
|
+
*
|
|
29
|
+
* Claims are serialised by the shared exclusive lock, so two daemons cannot
|
|
30
|
+
* both see the last free slot and both take it.
|
|
31
|
+
*
|
|
32
|
+
* ⚠️ A CLAIM IS ASKED BEFORE A TRY IS SPENT. The launcher's `busyFor` takes it
|
|
33
|
+
* (see `run-launcher.ts`), and `busyFor` runs before `reserveLaunch` in the
|
|
34
|
+
* tick, so "the machine is full" is a queue — never a failed attempt. A claim
|
|
35
|
+
* is idempotent per request: asking again for the same request refreshes it.
|
|
36
|
+
*
|
|
37
|
+
* ⚠️ AN UNKNOWN LIVENESS COUNTS AS ALIVE, the direction that cannot overshoot.
|
|
38
|
+
*/
|
|
39
|
+
/** A claim that never became a run stops holding a slot after this. */
|
|
40
|
+
exports.PENDING_TTL_MS = 3 * 60_000;
|
|
41
|
+
function machineSlotsDir(home = (0, node_os_1.homedir)()) {
|
|
42
|
+
return (0, node_path_1.join)(home, '.looply', 'machine-runs');
|
|
43
|
+
}
|
|
44
|
+
const fileOf = (key) => `${(0, node_crypto_1.createHash)('sha256').update(key).digest('hex').slice(0, 32)}.json`;
|
|
45
|
+
function machineRunSlots(cap, deps = {}) {
|
|
46
|
+
const dir = machineSlotsDir(deps.home);
|
|
47
|
+
const now = deps.now ?? (() => Date.now());
|
|
48
|
+
const probe = deps.probe ?? run_lease_1.nodeProbe;
|
|
49
|
+
const me = deps.pid ?? process.pid;
|
|
50
|
+
const limit = Math.max(1, Math.floor(cap));
|
|
51
|
+
const read = () => {
|
|
52
|
+
let names;
|
|
53
|
+
try {
|
|
54
|
+
names = (0, node_fs_1.readdirSync)(dir).filter((n) => n.endsWith('.json'));
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
const out = [];
|
|
60
|
+
for (const file of names) {
|
|
61
|
+
try {
|
|
62
|
+
const raw = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(dir, file), 'utf8'));
|
|
63
|
+
if (typeof raw.key !== 'string' || typeof raw.pid !== 'number' || typeof raw.at !== 'string')
|
|
64
|
+
continue;
|
|
65
|
+
out.push({ file, slot: { key: raw.key, pid: raw.pid, pending: raw.pending === true, at: raw.at, ...(raw.repo === undefined ? {} : { repo: raw.repo }) } });
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
// A torn write is not a run. The claim that wrote it rewrites it.
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return out;
|
|
72
|
+
};
|
|
73
|
+
const live = (s) => {
|
|
74
|
+
if (s.pending) {
|
|
75
|
+
const at = Date.parse(s.at);
|
|
76
|
+
if (!Number.isFinite(at) || now() - at > exports.PENDING_TTL_MS)
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
return probe(s.pid) !== 'dead';
|
|
80
|
+
};
|
|
81
|
+
const write = (slot) => {
|
|
82
|
+
(0, node_fs_1.mkdirSync)(dir, { recursive: true });
|
|
83
|
+
const target = (0, node_path_1.join)(dir, fileOf(slot.key));
|
|
84
|
+
const tmp = `${target}.${me}.tmp`;
|
|
85
|
+
(0, node_fs_1.writeFileSync)(tmp, `${JSON.stringify(slot)}\n`, 'utf8');
|
|
86
|
+
(0, node_fs_1.renameSync)(tmp, target);
|
|
87
|
+
};
|
|
88
|
+
const inUse = (except) => read().filter(({ slot }) => slot.key !== except && live(slot)).length;
|
|
89
|
+
return {
|
|
90
|
+
cap: limit,
|
|
91
|
+
inUse,
|
|
92
|
+
claim(key) {
|
|
93
|
+
try {
|
|
94
|
+
(0, node_fs_1.mkdirSync)(dir, { recursive: true });
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// Fall through: the lock below refuses, and a refusal is a wait.
|
|
98
|
+
}
|
|
99
|
+
return (0, exclusive_lock_1.withExclusiveLock)((0, node_path_1.join)(dir, 'claim.lock'), () => {
|
|
100
|
+
const all = read();
|
|
101
|
+
for (const { file, slot } of all) {
|
|
102
|
+
if (!live(slot)) {
|
|
103
|
+
try {
|
|
104
|
+
(0, node_fs_1.rmSync)((0, node_path_1.join)(dir, file), { force: true });
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
/* swept next time */
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const mine = all.find(({ slot }) => slot.key === key);
|
|
112
|
+
if (mine !== undefined && !mine.slot.pending && live(mine.slot))
|
|
113
|
+
return null; // already running
|
|
114
|
+
const others = all.filter(({ slot }) => slot.key !== key && live(slot)).length;
|
|
115
|
+
if (others >= limit) {
|
|
116
|
+
return `this computer already has ${others} autonomous run(s) going across its repositories and its ceiling is ${limit} (machine.max_runs_on_machine)`;
|
|
117
|
+
}
|
|
118
|
+
write({ key, pid: me, pending: true, at: new Date(now()).toISOString(), ...(deps.repo === undefined ? {} : { repo: deps.repo }) });
|
|
119
|
+
return null;
|
|
120
|
+
}, (why) => `the machine-wide run count could not be checked (${why})`);
|
|
121
|
+
},
|
|
122
|
+
bind(key, pid) {
|
|
123
|
+
try {
|
|
124
|
+
write({ key, pid, pending: false, at: new Date(now()).toISOString(), ...(deps.repo === undefined ? {} : { repo: deps.repo }) });
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
// A slot that cannot be bound expires as pending; the run goes on.
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
release(key) {
|
|
131
|
+
try {
|
|
132
|
+
(0, node_fs_1.rmSync)((0, node_path_1.join)(dir, fileOf(key)), { force: true });
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
/* expires on its own */
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=machine-run-slots.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"machine-run-slots.js","sourceRoot":"","sources":["../src/machine-run-slots.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { CheckedPlan, CheckedStep } from './compile-check';
|
|
2
|
+
import { type CompileGoalInput, type CompileOutcome, type PlanStep } from './goal-compile';
|
|
3
|
+
/**
|
|
4
|
+
* A PLAN THAT CANNOT BE USED AS IT CAME BACK IS ASKED AGAIN, NOT HANDED TO A PERSON.
|
|
5
|
+
*
|
|
6
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
7
|
+
* WHY (Tabsy, 2026-10-15)
|
|
8
|
+
*
|
|
9
|
+
* A ticket pasted into a chat with the same model is fixed nine times in ten.
|
|
10
|
+
* The same tickets given to Looply ended `human_required` for reasons that had
|
|
11
|
+
* nothing to do with the work:
|
|
12
|
+
* - TAB-B-60: the one step of the plan cited `TableBillLink.tsx:201`, the line
|
|
13
|
+
* did not exist at the committed tree, the step was refused, nothing was
|
|
14
|
+
* queued, and the run had nothing to do;
|
|
15
|
+
* - TAB-B-45: "shouldn't a fully paid table automatically become closed? —
|
|
16
|
+
* closing a session is only done manually as of now" was read as a remark,
|
|
17
|
+
* the plan came back empty, and "nothing here can move without you".
|
|
18
|
+
*
|
|
19
|
+
* Neither is a question only a person can answer. So:
|
|
20
|
+
* 1. a step whose citation does not hold is RE-PLANNED: the compiler is asked
|
|
21
|
+
* once more, shown each refusal, and told to read the current tree;
|
|
22
|
+
* 2. a step refused again keeps its place WITHOUT the citation that failed —
|
|
23
|
+
* a citation is a pointer, not the work, and the verify pass still confirms
|
|
24
|
+
* every draft against the code before a round may pick it;
|
|
25
|
+
* 3. an empty plan is asked again with the ticket read as an instruction;
|
|
26
|
+
* 4. an empty plan after that becomes ONE direct step — "do what this ticket
|
|
27
|
+
* asks" — and the implementer is handed the person's full wording (it
|
|
28
|
+
* always is, see `buildImplementerPrompt`). It is gated and proved like
|
|
29
|
+
* every other round, so nothing here lowers the bar for what lands.
|
|
30
|
+
*
|
|
31
|
+
* ⚠️ WHAT IS NOT RECOVERED: a compiler that failed, timed out or answered in a
|
|
32
|
+
* shape nobody could read. Those already spend nothing and the next Start asks
|
|
33
|
+
* again, which is the right answer to a transport problem.
|
|
34
|
+
*
|
|
35
|
+
* PURE apart from the two injected calls, so production and the tests run the
|
|
36
|
+
* same code.
|
|
37
|
+
*/
|
|
38
|
+
export interface RecoveryDeps {
|
|
39
|
+
compile(input: CompileGoalInput): Promise<CompileOutcome>;
|
|
40
|
+
check(steps: readonly PlanStep[]): Promise<CheckedPlan>;
|
|
41
|
+
log?(line: string): void;
|
|
42
|
+
}
|
|
43
|
+
export interface RecoveredPlan {
|
|
44
|
+
/** The answer whose steps are the ones to queue (`steps` already recovered). */
|
|
45
|
+
readonly compiled: CompileOutcome;
|
|
46
|
+
/**
|
|
47
|
+
* `admitted` is every step to queue; `refused` is what the FIRST check refused,
|
|
48
|
+
* kept so the compile record shows the claim beside what was there.
|
|
49
|
+
*/
|
|
50
|
+
readonly checked: CheckedPlan | null;
|
|
51
|
+
/** What recovery did, for the log; empty when the first answer was used as it came. */
|
|
52
|
+
readonly notes: readonly string[];
|
|
53
|
+
}
|
|
54
|
+
export declare const DIRECT_STEP_PREFIX = "Do what this ticket asks, as written";
|
|
55
|
+
/** The context for a re-plan after refusals: each refused step, and why. */
|
|
56
|
+
export declare function refusalContext(refused: readonly CheckedStep[]): string;
|
|
57
|
+
/** The context for a re-plan after an empty plan: read the ticket as an instruction. */
|
|
58
|
+
export declare const STATEMENT_CONTEXT: string;
|
|
59
|
+
/** The one step a ticket gets when structured planning produced nothing usable. */
|
|
60
|
+
export declare function directFixStep(goalText: string): PlanStep;
|
|
61
|
+
/** A compile answer made of the direct step alone, for a caller that needs a `CompileOutcome`. */
|
|
62
|
+
export declare function directFixOutcome(goalText: string): CompileOutcome;
|
|
63
|
+
/** A refused step with the citations that failed taken off it. */
|
|
64
|
+
export declare function withoutRefuted(r: CheckedStep): PlanStep;
|
|
65
|
+
/**
|
|
66
|
+
* Compile, check, and recover. `maxSteps` bounds what is queued, whatever the
|
|
67
|
+
* recovery added, so a re-plan never makes a plan bigger than the cap it was asked under.
|
|
68
|
+
*/
|
|
69
|
+
export declare function compileWithRecovery(input: CompileGoalInput, deps: RecoveryDeps, maxSteps: number): Promise<RecoveredPlan>;
|
|
70
|
+
//# sourceMappingURL=plan-recovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-recovery.d.ts","sourceRoot":"","sources":["../src/plan-recovery.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAChE,OAAO,EAAW,KAAK,gBAAgB,EAAE,KAAK,cAAc,EAAE,KAAK,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAEpG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IAC1D,KAAK,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACxD,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,aAAa;IAC5B,gFAAgF;IAChF,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,CAAC;IACrC,uFAAuF;IACvF,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC;AAKD,eAAO,MAAM,kBAAkB,yCAAyC,CAAC;AAEzE,4EAA4E;AAC5E,wBAAgB,cAAc,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,GAAG,MAAM,CAQtE;AAED,wFAAwF;AACxF,eAAO,MAAM,iBAAiB,QAMlB,CAAC;AAOb,mFAAmF;AACnF,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAQxD;AAED,kGAAkG;AAClG,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc,CAUjE;AAED,kEAAkE;AAClE,wBAAgB,cAAc,CAAC,CAAC,EAAE,WAAW,GAAG,QAAQ,CAGvD;AAcD;;;GAGG;AACH,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,gBAAgB,EAAE,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAsD/H"}
|