pi-goal-list-loop-audit 0.29.5 → 0.29.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/extensions/loops/goal.ts +44 -25
- package/package.json +1 -1
package/extensions/loops/goal.ts
CHANGED
|
@@ -1003,6 +1003,43 @@ function updateGoal(patch: Partial<Goal>, ctx: ExtensionContext): void {
|
|
|
1003
1003
|
persistState(ctx);
|
|
1004
1004
|
}
|
|
1005
1005
|
|
|
1006
|
+
// v0.29.6: stacked-state auto-arbitration (user directive: "auto archive /
|
|
1007
|
+
// wipe extra goals/loops/lists … make sure that we only have one"). Dirty
|
|
1008
|
+
// pre-guard states can persist a live loop AND a live goal; the 0.28.21
|
|
1009
|
+
// decision picker asked the user to arbitrate artifacts they didn't
|
|
1010
|
+
// remember at every pi start. Now deterministic: MOST RECENT ACTIVITY
|
|
1011
|
+
// keeps the slot; the loser is ARCHIVED (recoverable), never wiped. The
|
|
1012
|
+
// queued list is a backlog, not a live artifact — untouched.
|
|
1013
|
+
function autoArbitrateStackedState(ctx: ExtensionContext): void {
|
|
1014
|
+
const loop = state.loop?.active ? state.loop : undefined;
|
|
1015
|
+
const goal = state.goal && state.goal.status !== "complete" && state.goal.status !== "aborted" ? state.goal : undefined;
|
|
1016
|
+
if (!loop || !goal) return; // at most one live artifact — the invariant holds
|
|
1017
|
+
const lastMeasure = loop.history.length > 0 ? loop.history[loop.history.length - 1] : undefined;
|
|
1018
|
+
const loopMs = Date.parse(lastMeasure?.at ?? loop.startedAt ?? "") || 0;
|
|
1019
|
+
const goalMs = Date.parse(goal.updatedAt ?? goal.createdAt ?? "") || 0;
|
|
1020
|
+
const keepGoal = goalMs > loopMs; // tie → the loop keeps the slot (0.28.21 default)
|
|
1021
|
+
appendLedger(ctx.cwd, "stacked_state_auto_arbitrated", {
|
|
1022
|
+
kept: keepGoal ? "goal" : "loop",
|
|
1023
|
+
goalId: goal.id,
|
|
1024
|
+
goalMs,
|
|
1025
|
+
loopMs,
|
|
1026
|
+
loopIteration: loop.iteration,
|
|
1027
|
+
loopTarget: loop.target.slice(0, 120),
|
|
1028
|
+
});
|
|
1029
|
+
if (keepGoal) {
|
|
1030
|
+
// Same shape as /loop stop: the loop record stays in state (inactive)
|
|
1031
|
+
// with an honest reason — /loop status still shows it.
|
|
1032
|
+
state = { ...state, loop: { ...loop, active: false, stopReason: "auto-arbitrated on session load: the goal was more recent (one active thing)" } };
|
|
1033
|
+
persistState(ctx);
|
|
1034
|
+
} else {
|
|
1035
|
+
archiveCurrentGoal(ctx, "aborted", "auto-arbitrated on session load: the loop was more recent (one active thing)");
|
|
1036
|
+
}
|
|
1037
|
+
ctx.ui.notify(
|
|
1038
|
+
`Stacked state auto-arbitrated (one active thing): kept the ${keepGoal ? "goal" : "loop"} — more recent activity — and archived the ${keepGoal ? `loop (iter ${loop.iteration}, best ${loop.bestValue ?? "n/a"})` : `goal (${goal.id})`}. Recoverable: /loop status · .pi-glla/archive/ · /glla wipe for a clean slate.`,
|
|
1039
|
+
"info",
|
|
1040
|
+
);
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1006
1043
|
function archiveCurrentGoal(ctx: ExtensionContext, status: Status, stopReason?: string): void {
|
|
1007
1044
|
if (!state.goal) return;
|
|
1008
1045
|
const goal = state.goal;
|
|
@@ -5192,6 +5229,9 @@ export default function (pi: ExtensionAPI): void {
|
|
|
5192
5229
|
} catch (err) {
|
|
5193
5230
|
ctx.ui.notify(`glla subagent override sync failed: ${err instanceof Error ? err.message : String(err)}`, "warning");
|
|
5194
5231
|
}
|
|
5232
|
+
// v0.29.6: stacked-state auto-arbitration FIRST — one live artifact
|
|
5233
|
+
// survives before the restore gate decides hold-vs-resume for it.
|
|
5234
|
+
autoArbitrateStackedState(ctx);
|
|
5195
5235
|
// Restore gate (v0.26.9 tri-state): a human LOADING a session
|
|
5196
5236
|
// ("startup"/"new"/"resume", or no reason) HOLDS — the popup shows what
|
|
5197
5237
|
// is waiting and nothing starts until they resume explicitly. In-session
|
|
@@ -5276,31 +5316,10 @@ export default function (pi: ExtensionAPI): void {
|
|
|
5276
5316
|
ctx.ui.notify(`List has ${listQueue().length} item(s) waiting — /list next to activate the head.`, "info");
|
|
5277
5317
|
}
|
|
5278
5318
|
}
|
|
5279
|
-
// v0.28.21
|
|
5280
|
-
//
|
|
5281
|
-
//
|
|
5282
|
-
//
|
|
5283
|
-
// at most one thing owns the active slot, and nothing auto-starts.
|
|
5284
|
-
if (state.loop && state.goal && state.goal.status === "active") {
|
|
5285
|
-
updateGoal({
|
|
5286
|
-
status: "paused",
|
|
5287
|
-
pauseKind: "decision",
|
|
5288
|
-
// v0.29.3: third option — the wipe escape. Old projects stack a
|
|
5289
|
-
// goal AND a loop AND a list from pre-guard versions; arbitrating
|
|
5290
|
-
// between two artifacts the user doesn't even remember is the odd
|
|
5291
|
-
// part — "i feel like wipe does [make sense]". Wipe keeps its own
|
|
5292
|
-
// Confirm (destructive), so picking it is safe to offer.
|
|
5293
|
-
pauseOptions: ["Stop the loop, then resume the goal (/loop stop)", "Cancel the goal (/goal cancel) — the loop keeps running", "Wipe everything — clean slate for stale leftovers (/glla wipe)"],
|
|
5294
|
-
pauseRecommended: 1,
|
|
5295
|
-
pauseReason: "held on session load — the loop owns the active slot (one active thing at a time)",
|
|
5296
|
-
pauseSuggestedAction: "/loop to work the loop, or /loop stop then /goal resume to work the goal",
|
|
5297
|
-
}, ctx);
|
|
5298
|
-
ctx.ui.notify(
|
|
5299
|
-
`Goal held — a loop also exists; one active thing at a time. /loop to resume the loop, or /loop stop then /goal resume.`,
|
|
5300
|
-
"info",
|
|
5301
|
-
);
|
|
5302
|
-
maybeDecisionPopup(ctx);
|
|
5303
|
-
}
|
|
5319
|
+
// v0.29.6: the 0.28.21 loop-vs-goal decision picker is SUPERSEDED by
|
|
5320
|
+
// auto-arbitration above — stacked states resolve deterministically
|
|
5321
|
+
// (most recent activity keeps the slot; the loser is archived) before
|
|
5322
|
+
// the restore gate, so a live loop and a live goal cannot coexist here.
|
|
5304
5323
|
// Always paint on session load (v0.22.1): the branches above only reach
|
|
5305
5324
|
// refreshUI via persistState, so a goal that was ALREADY paused (or any
|
|
5306
5325
|
// state that doesn't mutate on load) rendered nothing — "can't tell if
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-goal-list-loop-audit",
|
|
3
|
-
"version": "0.29.
|
|
3
|
+
"version": "0.29.6",
|
|
4
4
|
"description": "Goal. Loop. Audit. Done. \u2014 a pi-coding-agent extension that supervises long-running work, with isolated auditor on each completion. Beat bamboozling by design: the auditor runs in a fresh session with no extensions, no skills, no editor \u2014 only the read tools needed to verify your goal.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "dracon",
|