@dev-loops/core 0.9.0 → 1.0.0-rc.1
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/package.json +3 -1
- package/src/analysis/change-classifier.mjs +15 -3
- package/src/analysis/diff-analyzer.mjs +112 -6
- package/src/claude/asset-generation.mjs +34 -17
- package/src/config/config.mjs +178 -6
- package/src/config/extension-defaults.yaml +7 -0
- package/src/debt/shape.mjs +0 -12
- package/src/loop/copilot-loop-state.mjs +38 -6
- package/src/loop/gate-carry-forward.mjs +244 -0
- package/src/loop/policy-constants.mjs +0 -3
- package/src/loop/pr-gate-coordination.mjs +12 -7
- package/src/loop/queue-state.mjs +0 -9
- package/src/loop/steering.mjs +4 -2
- package/src/loop/ui-review-drive.mjs +28 -4
- package/src/loop/ui-review-report.mjs +19 -17
- package/src/loop/ui-review-teardown.mjs +52 -10
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Teardown + side-effect ledger orchestrator for the ui_review route (Stage 5).
|
|
3
3
|
*
|
|
4
4
|
* Terminal cleanup for a running-app review: stop the app booted in Stage 1,
|
|
5
|
-
* drop the dev-DB rows the Stage-2 drive
|
|
5
|
+
* drop the dev-DB rows the Stage-2 drive tagged, and remove the provisioned
|
|
6
6
|
* worktree. The core safety property of this stage is that a side-effect ledger
|
|
7
7
|
* is ALWAYS emitted — enumerating every migration applied, row created/dropped,
|
|
8
8
|
* the worktree path, and any process left running — so nothing the loop touched
|
|
@@ -28,11 +28,12 @@
|
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* reports rows "may remain (untagged)"
|
|
31
|
+
* Row-drop model: Stage 2 stamps each mutating step with a drive-session id and
|
|
32
|
+
* emits a session-tagged row manifest. Given that manifest (and confirmation),
|
|
33
|
+
* this stage drops exactly the rows tagged with that session. Only the fallback
|
|
34
|
+
* case — a drive that mutated but handed in no manifest — CANNOT know which rows
|
|
35
|
+
* to drop and MUST NOT guess: the ledger reports rows "may remain (untagged)"
|
|
36
|
+
* rather than dropping anything.
|
|
36
37
|
*/
|
|
37
38
|
|
|
38
39
|
const ROW_STATUS = Object.freeze({
|
|
@@ -60,6 +61,13 @@ const PROCESS_STATUS = Object.freeze({
|
|
|
60
61
|
SKIPPED: "skipped",
|
|
61
62
|
});
|
|
62
63
|
|
|
64
|
+
const GIST_STATUS = Object.freeze({
|
|
65
|
+
DELETED: "deleted",
|
|
66
|
+
DELETE_FAILED: "delete-failed",
|
|
67
|
+
SKIPPED_UNCONFIRMED: "skipped-unconfirmed",
|
|
68
|
+
NONE: "none",
|
|
69
|
+
});
|
|
70
|
+
|
|
63
71
|
/**
|
|
64
72
|
* Did the Stage-2 drive potentially create dev-DB rows? Without row tagging this
|
|
65
73
|
* is a coarse but honest signal: a drive that actually walked steps exercised
|
|
@@ -82,8 +90,12 @@ function driveMayHaveCreatedRows(driveResult) {
|
|
|
82
90
|
* signal (whether the drive walked mutating steps). Null when no drive ran.
|
|
83
91
|
* @param {Array<object>|null} [input.rowManifest] - Explicit rows to drop, when
|
|
84
92
|
* a session tag/manifest is available. Absent/empty => untagged fallback.
|
|
93
|
+
* @param {{id?:string|null,url?:string|null}|null} [input.gist] - The Stage-4
|
|
94
|
+
* GitHub-native hosting artifact (a secret gist) to prune, when one was
|
|
95
|
+
* published off-Claude. Absent => nothing to prune.
|
|
85
96
|
* @param {boolean} [input.confirm] - Explicit authorization for the destructive
|
|
86
|
-
* steps (row drop, worktree removal). Fail-safe: absent means
|
|
97
|
+
* steps (row drop, worktree removal, gist deletion). Fail-safe: absent means
|
|
98
|
+
* NOT confirmed.
|
|
87
99
|
* @param {boolean} [input.stopApp] - Stop the Stage-1 app (default true). This is
|
|
88
100
|
* a clean shutdown, NOT gated on confirmation.
|
|
89
101
|
* @param {object} seams
|
|
@@ -92,12 +104,14 @@ function driveMayHaveCreatedRows(driveResult) {
|
|
|
92
104
|
* signalling is unsupported) — mapped to MAY_BE_RUNNING (non-fatal), not KILL_FAILED.
|
|
93
105
|
* @param {(a:{rows:Array<object>})=>Promise<{ok:boolean,dropped:number,detail:string}>} seams.dropRows
|
|
94
106
|
* @param {(a:{worktreePath:string})=>Promise<{removed:string|null,ok:boolean,detail:string}>} seams.removeWorktree
|
|
107
|
+
* @param {(a:{id:string})=>Promise<{ok:boolean,detail:string}>} [seams.deleteGist] - Prune the
|
|
108
|
+
* Stage-4 hosting gist. Only invoked when a gist id is present AND confirmed.
|
|
95
109
|
* @param {(msg:string)=>void} [seams.log]
|
|
96
110
|
* @returns {Promise<{ok:boolean,confirmed:boolean,ledger:object,errors:string[],logs:string[]}>}
|
|
97
111
|
*/
|
|
98
112
|
export async function teardown(
|
|
99
|
-
{ provisionResult, driveResult = null, rowManifest = null, confirm = false, stopApp = true },
|
|
100
|
-
{ killProcess, dropRows, removeWorktree, log = () => {} } = {},
|
|
113
|
+
{ provisionResult, driveResult = null, rowManifest = null, gist = null, confirm = false, stopApp = true },
|
|
114
|
+
{ killProcess, dropRows, removeWorktree, deleteGist, log = () => {} } = {},
|
|
101
115
|
) {
|
|
102
116
|
const logs = [];
|
|
103
117
|
const errors = [];
|
|
@@ -229,6 +243,33 @@ export async function teardown(
|
|
|
229
243
|
}
|
|
230
244
|
}
|
|
231
245
|
|
|
246
|
+
// 4. Prune the Stage-4 hosting gist — DESTRUCTIVE, confirmation-gated. A gist
|
|
247
|
+
// accretes one secret entry per run; deleting it keeps the hosting target
|
|
248
|
+
// from piling up. Only ever acts on an explicit gist id from the report
|
|
249
|
+
// result; a missing id is NONE (nothing published, or Claude-hosted).
|
|
250
|
+
const gistId = typeof gist?.id === "string" && gist.id.trim().length > 0 ? gist.id.trim() : null;
|
|
251
|
+
let gistLedger;
|
|
252
|
+
if (!gistId) {
|
|
253
|
+
gistLedger = { id: null, url: gist?.url ?? null, deleted: false, status: GIST_STATUS.NONE, detail: "no hosting gist to prune" };
|
|
254
|
+
} else if (!confirm) {
|
|
255
|
+
gistLedger = { id: gistId, url: gist?.url ?? null, deleted: false, status: GIST_STATUS.SKIPPED_UNCONFIRMED, detail: "hosting gist retained: teardown not confirmed" };
|
|
256
|
+
record(`gist prune skipped (not confirmed): ${gistId} retained`);
|
|
257
|
+
} else {
|
|
258
|
+
try {
|
|
259
|
+
const del = await deleteGist({ id: gistId });
|
|
260
|
+
if (del.ok) {
|
|
261
|
+
gistLedger = { id: gistId, url: gist?.url ?? null, deleted: true, status: GIST_STATUS.DELETED, detail: del.detail };
|
|
262
|
+
record(`hosting gist deleted: ${gistId} (${del.detail})`);
|
|
263
|
+
} else {
|
|
264
|
+
gistLedger = { id: gistId, url: gist?.url ?? null, deleted: false, status: GIST_STATUS.DELETE_FAILED, detail: del.detail };
|
|
265
|
+
fail(`hosting gist delete FAILED: ${gistId} (${del.detail})`);
|
|
266
|
+
}
|
|
267
|
+
} catch (err) {
|
|
268
|
+
gistLedger = { id: gistId, url: gist?.url ?? null, deleted: false, status: GIST_STATUS.DELETE_FAILED, detail: `deleteGist seam threw: ${err?.message ?? err}` };
|
|
269
|
+
fail(`hosting gist delete FAILED: ${gistId} (deleteGist seam threw: ${err?.message ?? err})`);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
232
273
|
// The ledger is ALWAYS emitted (every case), enumerating every known side
|
|
233
274
|
// effect. Migrations are recorded as applied-not-reverted by design.
|
|
234
275
|
const ledger = {
|
|
@@ -241,10 +282,11 @@ export async function teardown(
|
|
|
241
282
|
},
|
|
242
283
|
rows: rowsLedger,
|
|
243
284
|
worktree: worktreeLedger,
|
|
285
|
+
gist: gistLedger,
|
|
244
286
|
process: processLedger,
|
|
245
287
|
};
|
|
246
288
|
|
|
247
289
|
return { ok: errors.length === 0, confirmed: confirm, ledger, errors, logs };
|
|
248
290
|
}
|
|
249
291
|
|
|
250
|
-
export { ROW_STATUS, WORKTREE_STATUS, PROCESS_STATUS };
|
|
292
|
+
export { ROW_STATUS, WORKTREE_STATUS, PROCESS_STATUS, GIST_STATUS };
|