pi-goal-list-loop-audit 0.31.0 → 0.31.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.
@@ -547,7 +547,16 @@ export function listAuditFanoutItemText(finding: string): string {
547
547
  return `Fix audit finding: ${finding} — Done when: the fix is committed on the current branch with the repo's configured identity, and this finding's box in ${AUDIT_FINDINGS_REL} is checked ("- [x] … — fixed in <commit>").`;
548
548
  }
549
549
 
550
+ /** v0.31.1: stacking-detection markers (junk-runner 2026-07-31: a held
551
+ * one-shot audit goal + a running audit loop = two stacked audit initiatives
552
+ * — the held one-shot read as "stalled" for 8h while the loop did all the
553
+ * work, and the agent conflated them). The guards in goal.ts match on these;
554
+ * the unit tests pin that the built targets still contain them.
555
+ */
556
+ export const GOAL_AUDIT_ONESHOT_MARKER = "Run ONE project audit pass and leave the project in a known state";
557
+ export const LOOP_AUDIT_MARKER = "iteration by iteration — FIX-FIRST";
558
+
550
559
  export function projectAuditTarget(focus?: string): string {
551
560
  const scope = focus && focus.trim() ? focus.trim() : "the whole project";
552
- return `Run ONE project audit pass and leave the project in a known state. Scope: ${scope}. (1) Run a FRESH audit pass over the codebase — spawn Explore subagents for breadth — hunting real problems: bugs, broken flows, regressions, drift between docs and code, dead code, security holes. Not style nits, not speculative refactors. (2) Append every NEW finding to ${AUDIT_FINDINGS_REL} (create the file on the first finding; append-only — never delete, rewrite, or reorder existing lines; never re-report a finding already listed), classified: "- [ ] FIX: SEVERITY: short description (file:line)" for bugs and polish — whether to fix these is NOT a decision — and "- [?] DECIDE: short description (what the choice is, what each side costs)" for direction, trade-offs, and scope questions where two reasonable answers exist. (3) Fix every NEW FIX finding from this pass — real fixes, committed with the repo's configured identity on the current branch (no invented identities or branches) — then check the box: "- [x] … — fixed in <commit>". (4) Change NOTHING for DECIDE findings — present them in the completion report instead. (5) Honesty law: never fabricate findings to look busy; never check a box without the fix commit existing; never silently turn a DECIDE into a fix. Done when: the audit pass is complete, every new FIX finding has a fix commit and a checked box in ${AUDIT_FINDINGS_REL}, and every DECIDE finding is listed in the file and presented in the completion report.`;
561
+ return `${GOAL_AUDIT_ONESHOT_MARKER}. Scope: ${scope}. (1) Run a FRESH audit pass over the codebase — spawn Explore subagents for breadth — hunting real problems: bugs, broken flows, regressions, drift between docs and code, dead code, security holes. Not style nits, not speculative refactors. (2) Append every NEW finding to ${AUDIT_FINDINGS_REL} (create the file on the first finding; append-only — never delete, rewrite, or reorder existing lines; never re-report a finding already listed), classified: "- [ ] FIX: SEVERITY: short description (file:line)" for bugs and polish — whether to fix these is NOT a decision — and "- [?] DECIDE: short description (what the choice is, what each side costs)" for direction, trade-offs, and scope questions where two reasonable answers exist. (3) Fix every NEW FIX finding from this pass — real fixes, committed with the repo's configured identity on the current branch (no invented identities or branches) — then check the box: "- [x] … — fixed in <commit>". (4) Change NOTHING for DECIDE findings — present them in the completion report instead. (5) Honesty law: never fabricate findings to look busy; never check a box without the fix commit existing; never silently turn a DECIDE into a fix. Done when: the audit pass is complete, every new FIX finding has a fix commit and a checked box in ${AUDIT_FINDINGS_REL}, and every DECIDE finding is listed in the file and presented in the completion report.`;
553
562
  }
@@ -176,6 +176,8 @@ import {
176
176
  AUDIT_FINDINGS_REL,
177
177
  projectAuditTarget,
178
178
  LIST_AUDIT_COLLECT_MARKER,
179
+ GOAL_AUDIT_ONESHOT_MARKER,
180
+ LOOP_AUDIT_MARKER,
179
181
  listAuditCollectTarget,
180
182
  parseAuditFindingsForFanout,
181
183
  listAuditFanoutItemText,
@@ -1706,6 +1708,15 @@ async function cmdGoal(args: string, ctx: ExtensionContext): Promise<void> {
1706
1708
  // DECIDE findings presented, untouched. Runs as a normal goal through
1707
1709
  // cmdSet — the isolated auditor verifies the finish line.
1708
1710
  if (route.name === "audit") {
1711
+ // v0.31.1: an active audit loop already owns auditing here — the
1712
+ // one-shot duplicates it (same stacking confusion as junk-runner).
1713
+ if (state.loop?.active && state.loop.target.includes(LOOP_AUDIT_MARKER)) {
1714
+ appendLedger(ctx.cwd, "audit_stack_warn", { have: "loop", starting: "goal" });
1715
+ ctx.ui.notify(
1716
+ "An audit loop is already running here — a one-shot /goal audit duplicates its work. /loop status to see it; /loop stop first if you want the one-shot instead.",
1717
+ "warning",
1718
+ );
1719
+ }
1709
1720
  return cmdSet(projectAuditTarget(route.rest || undefined), ctx, true);
1710
1721
  }
1711
1722
  // v0.28.27 (renamed /goal audit → /goal verify in v0.29.8): run the
@@ -2158,6 +2169,12 @@ async function cmdList(args: string, ctx: ExtensionContext): Promise<void> {
2158
2169
  // list drains them fix by fix, each with its own isolated audit.
2159
2170
  // Distinct from /goal audit (fix-in-pass, one audited unit) and
2160
2171
  // /loop audit (forever fix-first cadence).
2172
+ // v0.31.1: an active audit loop is already draining this findings file —
2173
+ // a collect pass would double-hunt the same ground.
2174
+ if (state.loop?.active && state.loop.target.includes(LOOP_AUDIT_MARKER)) {
2175
+ appendLedger(ctx.cwd, "audit_stack_warn", { have: "loop", starting: "list" });
2176
+ ctx.ui.notify("An audit loop is already draining findings here — /list audit would double-hunt the same ground. /loop status to see it.", "warning");
2177
+ }
2161
2178
  const objective = listAuditCollectTarget(rest || undefined);
2162
2179
  const n = enqueueItems(ctx, [objective], "/list audit");
2163
2180
  if (n === 0) return; // zombie-twin guard already explained itself
@@ -3021,6 +3038,18 @@ async function cmdLoop(args: string, ctx: ExtensionContext): Promise<void> {
3021
3038
  ctx.ui.notify("A goal is active — /goal cancel or /goal pause it before starting a loop.", "warning");
3022
3039
  return;
3023
3040
  }
3041
+ // v0.31.1: a paused/active one-shot audit goal + this loop = two stacked
3042
+ // audit initiatives (junk-runner 2026-07-31: the held one-shot read as
3043
+ // "stalled" for 8h while the loop did all the work — the agent conflated
3044
+ // them and proposed completing the goal for the loop's work). Warn, name
3045
+ // the supersession, don't block — the user's agency, the user's call.
3046
+ if (state.goal && state.goal.objective.includes(GOAL_AUDIT_ONESHOT_MARKER)) {
3047
+ appendLedger(ctx.cwd, "audit_stack_warn", { have: "goal", starting: "loop", goalStatus: state.goal.status });
3048
+ ctx.ui.notify(
3049
+ `Heads up: a ${state.goal.status} one-shot audit goal exists in this session — the audit loop SUPERSEDES it (one pass + fixes IS the loop's job). /goal cancel clears it; one audit initiative per session.`,
3050
+ "warning",
3051
+ );
3052
+ }
3024
3053
  if (isLoopActive()) {
3025
3054
  ctx.ui.notify("A loop is already active. /loop stop first.", "warning");
3026
3055
  return;
@@ -5834,11 +5863,24 @@ export default function (pi: ExtensionAPI): void {
5834
5863
  const isListItem = state.goal.policy === "list";
5835
5864
  const resumeCmd = isListItem ? "/list resume" : "/goal resume";
5836
5865
  const resumeHint = `${resumeCmd} to continue${queued > 0 ? ` (+${queued} waiting in the list)` : ""} · /glla autoresume=on to auto-resume on load (global setting)`;
5866
+ // v0.31.1: name the supersession — a held one-shot audit whose work a
5867
+ // live audit loop now owns reads as "stalled" for HOURS otherwise
5868
+ // (junk-runner: 8h21m of "held for explicit resume" on a goal the
5869
+ // loop had superseded). The widget surface must say so.
5870
+ const auditSuperseded =
5871
+ state.goal.objective.includes(GOAL_AUDIT_ONESHOT_MARKER) &&
5872
+ !!state.loop &&
5873
+ (state.loop.active || state.loop.stopReason === HELD_ON_RESTORE) &&
5874
+ !!state.loop.target?.includes(LOOP_AUDIT_MARKER);
5837
5875
  updateGoal({
5838
5876
  status: "paused",
5839
5877
  pauseKind: "blocked",
5840
- pauseReason: "restored on session load — held for explicit resume",
5841
- pauseSuggestedAction: resumeHint,
5878
+ pauseReason: auditSuperseded
5879
+ ? "restored on session load — SUPERSEDED by the audit loop in this session"
5880
+ : "restored on session load — held for explicit resume",
5881
+ pauseSuggestedAction: auditSuperseded
5882
+ ? `/goal cancel clears it (the loop already owns the audit) · ${resumeHint} if you disagree`
5883
+ : resumeHint,
5842
5884
  }, ctx);
5843
5885
  ctx.ui.notify(
5844
5886
  `${isListItem ? "List item" : "Goal"} held on restore: ${state.goal.objective.slice(0, 70)}${queued > 0 ? ` (+${queued} waiting in the list)` : ""} — ${resumeCmd} to continue.`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-goal-list-loop-audit",
3
- "version": "0.31.0",
3
+ "version": "0.31.1",
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",