pi-goal-list-loop-audit 0.38.69 → 0.38.71
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/CHANGELOG.md +10 -0
- package/docs/INDEX.md +1 -1
- package/extensions/goal-loop-core.ts +19 -1
- package/extensions/goal-loop-display.ts +68 -20
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.38.71 — Decision cards lead with the action, plaque verbs deduped (2026-09-20)
|
|
4
|
+
|
|
5
|
+
- Decision pause cards render the saved/action row before the numbered options, so Pi core tail truncation cuts late options and history instead of the resume path (UI survey finding 1, audit/UI-SURVEY-2026-09-20.md).
|
|
6
|
+
- Durable/defer plaque bodies strip a leading Implement/Keep from the stored fix, fixing "Implement Implement Keep ..." doubling; ledger facts untouched (UI survey finding 3).
|
|
7
|
+
|
|
8
|
+
## 0.38.70 — Paused action cards lead with the action (2026-09-20)
|
|
9
|
+
|
|
10
|
+
- Paused cards render lifecycle/next, banner, reason, and suggested action before tally, provenance, and judgment, so Pi core tail truncation cuts history instead of the instruction (field 20260920_152744).
|
|
11
|
+
- Judgment rides collapsed on action cards (header + selection; full plaques stay in `/goal status`).
|
|
12
|
+
|
|
3
13
|
## 0.38.69 — About and README refresh (2026-09-20)
|
|
4
14
|
|
|
5
15
|
- Refined npm about line and README intro: tighter mission control wording, no em dashes.
|
package/docs/INDEX.md
CHANGED
|
@@ -16,7 +16,7 @@ Policy contracts and recent changes live in the `audit/` directory of the
|
|
|
16
16
|
failback; v0.35.9 hardened cross-version npm tarball checks; v0.35.10
|
|
17
17
|
handles multi-entry npm dry-run reports; v0.35.11 accepts both npm report
|
|
18
18
|
shapes; v0.35.12 supports npm 12's keyed pack reports; v0.35.13 fixes stale-API recovery loops.
|
|
19
|
-
v0.35.14–v0.38.
|
|
19
|
+
v0.35.14–v0.38.71 continue through the supervisor freeze (`/glla pause`),
|
|
20
20
|
load hold, auditor picker parity, Windows launch fix, zombie-watchdog
|
|
21
21
|
subagent carve-out, due-wait backstop, the `/glla agents` visibility panel,
|
|
22
22
|
durable state-root selection, blank-until-resume auditor context, frozen
|
|
@@ -3145,6 +3145,24 @@ export function recommendDurableDeferChoice(input: DurableDeferRecommendationInp
|
|
|
3145
3145
|
return input.durableBlocked === true ? "deferred" : "inline";
|
|
3146
3146
|
}
|
|
3147
3147
|
|
|
3148
|
+
/** v0.38.71 (field UI-SURVEY-2026-09-20): plaque bodies compose a verb
|
|
3149
|
+
* around the stored fix ("Implement X now" / "Keep X as the follow-up"),
|
|
3150
|
+
* so a fix that already starts with that verb doubled ("Implement
|
|
3151
|
+
* Implement Keep ..."). Strip one leading Implement/Keep; the composed
|
|
3152
|
+
* verb carries the meaning either way. */
|
|
3153
|
+
function stripPlaqueVerb(fix: string): string {
|
|
3154
|
+
// Loop twice: fixes arrive as bare phrases ("pin plaque ordering") or
|
|
3155
|
+
// imperative stacks ("Implement Keep ...") — one pass leaves
|
|
3156
|
+
// "Implement Keep ..." doubled against the composed verb.
|
|
3157
|
+
let out = fix;
|
|
3158
|
+
for (let i = 0; i < 2; i++) {
|
|
3159
|
+
const next = out.replace(/^(implement|keep)\s+/i, "");
|
|
3160
|
+
if (next === out) break;
|
|
3161
|
+
out = next;
|
|
3162
|
+
}
|
|
3163
|
+
return out;
|
|
3164
|
+
}
|
|
3165
|
+
|
|
3148
3166
|
/**
|
|
3149
3167
|
* Build the ordered recommendation plaques consumed by prompt and UI
|
|
3150
3168
|
* projections. The returned order is driven by DURABLE_DEFER_PLAQUE_ORDER so
|
|
@@ -3152,7 +3170,7 @@ export function recommendDurableDeferChoice(input: DurableDeferRecommendationInp
|
|
|
3152
3170
|
*/
|
|
3153
3171
|
export function buildDurableDeferRecommendation(input: DurableDeferRecommendationInput): DurableDeferRecommendation {
|
|
3154
3172
|
const facts = normalizeDurableDeferRecommendationInput(input);
|
|
3155
|
-
const durableFix = facts.durableFix || "the durable root-cause fix";
|
|
3173
|
+
const durableFix = stripPlaqueVerb(facts.durableFix || "the durable root-cause fix");
|
|
3156
3174
|
const deferCount = facts.deferRecommendations.length;
|
|
3157
3175
|
const choice = recommendDurableDeferChoice(facts);
|
|
3158
3176
|
const plaques: Record<DurableDeferPlaqueKind, DurableDeferPlaque> = {
|
|
@@ -1112,6 +1112,21 @@ function pausedNextTransition(g: Goal, state: State, now: number): string {
|
|
|
1112
1112
|
}
|
|
1113
1113
|
}
|
|
1114
1114
|
|
|
1115
|
+
/** v0.38.70 (field 20260920_152744): close a paused action card with its
|
|
1116
|
+
* deferred history. The pause body already ended on a `└─` closer; reopen
|
|
1117
|
+
* it to `├─` and hang the tally/provenance/collapsed-judgment rows after
|
|
1118
|
+
* the action, closing on the last history row. Empty history is a no-op,
|
|
1119
|
+
* so cards with nothing deferred keep their exact shape. */
|
|
1120
|
+
function closePausedWithHistory(lines: string[], history: string[], theme?: DisplayTheme): string[] {
|
|
1121
|
+
if (history.length === 0) return lines;
|
|
1122
|
+
const tail = lines[lines.length - 1];
|
|
1123
|
+
if (tail !== undefined) lines[lines.length - 1] = tail.replace(/^└─/, "├─");
|
|
1124
|
+
history.forEach((h, i) => {
|
|
1125
|
+
lines.push(`${i === history.length - 1 ? "└─" : "├─"} ${paint(theme, "dim", h)}`);
|
|
1126
|
+
});
|
|
1127
|
+
return lines;
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1115
1130
|
function pausedLifecycleLines(g: Goal, state: State, extras: WidgetExtras | undefined, now: number): [string, string] {
|
|
1116
1131
|
const queued = state.list?.length ?? 0;
|
|
1117
1132
|
return [
|
|
@@ -1932,9 +1947,20 @@ function goalLines(g: Goal, state: State, audit: AuditDisplayProgress | null | u
|
|
|
1932
1947
|
// v0.38.8: durable verdict tally as a first-class card row — the widget
|
|
1933
1948
|
// is the glance surface, and stored verdicts are the progress evidence
|
|
1934
1949
|
// when no auditor is live. Silent when history is empty.
|
|
1950
|
+
// v0.38.70 (field 20260920_152744): a paused action card must lead with
|
|
1951
|
+
// the action, not history. Pi core truncates the widget tail (~10 rows),
|
|
1952
|
+
// so judgment plaques above the blocked banner pushed the suggested action
|
|
1953
|
+
// below the cut — "... (widget truncated)" with no action visible.
|
|
1954
|
+
// Tally, provenance, and judgment ride AFTER the pause closer instead;
|
|
1955
|
+
// judgment rides collapsed (header + selection; full plaques stay in
|
|
1956
|
+
// /goal status). Interrupted/attention cards keep the existing order.
|
|
1957
|
+
const deferHistory = g.status === "paused" && !!g.pauseReason && !interrupted && !attention;
|
|
1958
|
+
const deferredHistory: string[] = [];
|
|
1935
1959
|
const headTally = formatVerdictTallySegment(auditorVerdictTally(g.auditHistory, now), now);
|
|
1936
1960
|
if (headTally) {
|
|
1937
|
-
|
|
1961
|
+
const tallyRow = `audits: ${truncate(headTally, Math.max(20, (width ?? 80) - 12))}`;
|
|
1962
|
+
if (deferHistory) deferredHistory.push(tallyRow);
|
|
1963
|
+
else lines.push(`├─ ${paint(theme, "dim", tallyRow)}`);
|
|
1938
1964
|
}
|
|
1939
1965
|
if (g.repairTarget) {
|
|
1940
1966
|
lines.push(`├─ ${paint(theme, "warning", `REPLAN REQUIRED · original target: ${truncate(g.repairTarget.objective.replace(/\s+/g, " "), Math.max(30, (width ?? 80) - 28))}`)}`);
|
|
@@ -1967,12 +1993,22 @@ function goalLines(g: Goal, state: State, audit: AuditDisplayProgress | null | u
|
|
|
1967
1993
|
? []
|
|
1968
1994
|
: modelProvenanceLines(extras?.modelProvenance, width);
|
|
1969
1995
|
const provenanceStart = lines.length;
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
}
|
|
1996
|
+
if (deferHistory) {
|
|
1997
|
+
provenance.forEach((line) => deferredHistory.push(line));
|
|
1998
|
+
} else {
|
|
1999
|
+
provenance.forEach((line, i) => {
|
|
2000
|
+
lines.push(`${i === 0 ? "├─" : "│ "} ${paint(theme, "dim", line)}`);
|
|
2001
|
+
});
|
|
2002
|
+
}
|
|
1973
2003
|
if (extras?.durableDeferRecommendation) {
|
|
1974
2004
|
const judgment = buildDurableDeferDecisionLines(extras.durableDeferRecommendation, width);
|
|
1975
|
-
|
|
2005
|
+
// Collapsed on action cards: header + selection only. Plaque bodies
|
|
2006
|
+
// stay in /goal status; the glance card must fit the action.
|
|
2007
|
+
const collapsed = deferHistory && judgment.length > 2
|
|
2008
|
+
? [judgment[0]!, judgment[judgment.length - 1]!]
|
|
2009
|
+
: judgment;
|
|
2010
|
+
if (deferHistory) deferredHistory.push(...collapsed);
|
|
2011
|
+
else judgment.forEach((line) => lines.push(`├─ ${paint(theme, "dim", line)}`));
|
|
1976
2012
|
}
|
|
1977
2013
|
if (interrupted) {
|
|
1978
2014
|
const resumeCmd = isList ? "/list resume" : "/goal resume";
|
|
@@ -2031,7 +2067,7 @@ function goalLines(g: Goal, state: State, audit: AuditDisplayProgress | null | u
|
|
|
2031
2067
|
lines.push(`├─ ${paint(theme, "dim", lifecycle)}`);
|
|
2032
2068
|
lines.push(`│ ${paint(theme, "dim", transition)}`);
|
|
2033
2069
|
lines.push(`└─ ${paint(theme, "warning", sanitizeProviderDisplayText(g.pauseSuggestedAction ?? `The claim is safe; ${isList ? "/list resume" : "/goal resume"} starts exactly one fresh auditor.`))}`);
|
|
2034
|
-
return lines;
|
|
2070
|
+
return closePausedWithHistory(lines, deferredHistory, theme);
|
|
2035
2071
|
}
|
|
2036
2072
|
const [lifecycle, transition] = pausedLifecycleLines(g, state, extras, now);
|
|
2037
2073
|
lines.push(`├─ ${paint(theme, "dim", lifecycle)}`);
|
|
@@ -2100,16 +2136,9 @@ function goalLines(g: Goal, state: State, audit: AuditDisplayProgress | null | u
|
|
|
2100
2136
|
lines.push(`${i === 0 ? "├─" : "│ "} ${paint(theme, reasonPaint, w)}`);
|
|
2101
2137
|
});
|
|
2102
2138
|
}
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
g.pauseOptions.slice(0, 6).forEach((opt, i) => {
|
|
2107
|
-
const rec = g.pauseRecommended === i + 1;
|
|
2108
|
-
const text = `${i + 1}. ${truncate(opt, budget - 4)}${rec ? " ◂ recommended" : ""}`;
|
|
2109
|
-
lines.push(`│ ${paint(theme, rec ? "accent" : "dim", text)}`);
|
|
2110
|
-
});
|
|
2111
|
-
if (g.pauseOptions.length > 6) lines.push(`│ ${paint(theme, "dim", `… and ${g.pauseOptions.length - 6} more`)}`);
|
|
2112
|
-
}
|
|
2139
|
+
const decisionOptions = kind === "decision" && g.pauseOptions && g.pauseOptions.length > 0
|
|
2140
|
+
? g.pauseOptions
|
|
2141
|
+
: undefined;
|
|
2113
2142
|
// v0.28.22: wait countdown — moved into the auto-retrying line above
|
|
2114
2143
|
// (v0.34.64). The old separate `resumes X — or /goal resume now` line
|
|
2115
2144
|
// implied manual rescue was the path; autoResume + the recovery-cleared
|
|
@@ -2128,7 +2157,7 @@ function goalLines(g: Goal, state: State, audit: AuditDisplayProgress | null | u
|
|
|
2128
2157
|
// the card here, closing the previous row as the footer.
|
|
2129
2158
|
const tail = lines[lines.length - 1];
|
|
2130
2159
|
if (tail !== undefined) lines[lines.length - 1] = tail.replace(/^├─|^│\s*/, "└─ ");
|
|
2131
|
-
return lines;
|
|
2160
|
+
return closePausedWithHistory(lines, deferredHistory, theme);
|
|
2132
2161
|
}
|
|
2133
2162
|
const spent: string[] = [];
|
|
2134
2163
|
const tokUsed = g.usage?.tokensUsed ?? 0;
|
|
@@ -2139,16 +2168,35 @@ function goalLines(g: Goal, state: State, audit: AuditDisplayProgress | null | u
|
|
|
2139
2168
|
const savedLine = hasTelemetry
|
|
2140
2169
|
? `saved — ${spent.join(" · ")} · resumes exactly here`
|
|
2141
2170
|
: `awaiting first turn — resumes exactly here`;
|
|
2171
|
+
const optionsFollow = decisionOptions !== undefined;
|
|
2142
2172
|
if (g.pauseSuggestedAction) {
|
|
2143
2173
|
lines.push(`├─ ${paint(theme, "dim", truncate(savedLine, budget))}`);
|
|
2144
2174
|
const wrapped = wrap(sanitizeProviderDisplayText(g.pauseSuggestedAction), budget, 3);
|
|
2145
2175
|
// v0.28.22: for ACTION NEEDED pauses the action is the point — pop it.
|
|
2146
2176
|
const actionPaint = kind === "error" ? "warning" : "dim";
|
|
2147
|
-
|
|
2177
|
+
// v0.38.71 (field UI-SURVEY-2026-09-20 finding 1): the action precedes
|
|
2178
|
+
// the options — a 7-option decision card runs 16 rows and Pi core cuts
|
|
2179
|
+
// the tail, which used to hold the action. Options survive in full in
|
|
2180
|
+
// the ask_user_question dialog; the card keeps the resume path visible.
|
|
2181
|
+
wrapped.forEach((w, i) => lines.push(`${i === wrapped.length - 1 && !optionsFollow ? "└─" : "│ "} ${paint(theme, actionPaint, w)}`));
|
|
2148
2182
|
} else {
|
|
2149
|
-
lines.push(
|
|
2183
|
+
lines.push(`${optionsFollow ? "├─" : "└─"} ${paint(theme, "dim", truncate(savedLine, budget))}`);
|
|
2150
2184
|
}
|
|
2151
|
-
|
|
2185
|
+
// v0.28.22: decision options — one numbered line each (Claude Code /
|
|
2186
|
+
// muselinn-Ask convention), the recommended one accented and flagged.
|
|
2187
|
+
if (decisionOptions) {
|
|
2188
|
+
decisionOptions.slice(0, 6).forEach((opt, i) => {
|
|
2189
|
+
const rec = g.pauseRecommended === i + 1;
|
|
2190
|
+
const text = `${i + 1}. ${truncate(opt, budget - 4)}${rec ? " ◂ recommended" : ""}`;
|
|
2191
|
+
lines.push(`│ ${paint(theme, rec ? "accent" : "dim", text)}`);
|
|
2192
|
+
});
|
|
2193
|
+
if (decisionOptions.length > 6) lines.push(`│ ${paint(theme, "dim", `… and ${decisionOptions.length - 6} more`)}`);
|
|
2194
|
+
// Close the options block; the history helper reopens it when deferred
|
|
2195
|
+
// rows follow.
|
|
2196
|
+
const lastOpt = lines[lines.length - 1];
|
|
2197
|
+
if (lastOpt !== undefined) lines[lines.length - 1] = lastOpt.replace(/^│ /, "└─ ");
|
|
2198
|
+
}
|
|
2199
|
+
return closePausedWithHistory(lines, deferredHistory, theme);
|
|
2152
2200
|
}
|
|
2153
2201
|
if (attention) {
|
|
2154
2202
|
const budget = budgetFor(width, 3, 60);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-goal-list-loop-audit",
|
|
3
|
-
"version": "0.38.
|
|
3
|
+
"version": "0.38.71",
|
|
4
4
|
"description": "Mission control for long running pi work: interview drafted goals, an audited task queue, and metric or audit loops that run for hours. Every completion is rechecked by a detached auditor with raw evidence, while Confirm drafts, decision pauses, and consent gates keep you in charge.",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"author": "dracon",
|