feat(ultraplan-local): pre-compact-flush refreshes session-state.local.json [skip-docs]
Extends the PreCompact hook with a sibling block that refreshes
.session-state.local.json's updated_at when status is in_progress or
partial. Per-project: runs after the existing progress.json mutation,
inside the same loop iteration.
Design:
- Only refreshes existing state files; creation is the writer's job
(ultraexecute Phase 8 / 2.55 / 4 + future helper command).
- Monotonic guard: only updated_at is touched. project, status,
next_session_brief_path, next_session_label remain owned by the writer.
- Skips status in {completed, failed, stopped} — the latter two are
operator-action-required and silently bumping updated_at would mask
alert state.
- Always exit 0; never blocks compaction.
[skip-docs] rationale: README + CLAUDE.md updates land in Step 11.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
e4a11daa68
commit
af67362c68
1 changed files with 38 additions and 2 deletions
|
|
@ -6,6 +6,10 @@
|
|||
// Direct fix for the documented P0 in
|
||||
// docs/ultraexecute-v2-observations-from-config-audit-v4.md.
|
||||
//
|
||||
// v3.3.0: also refreshes sibling .session-state.local.json
|
||||
// (Handover 7) so /ultracontinue can detect a resumable session
|
||||
// even after a compaction event mid-run.
|
||||
//
|
||||
// Behavior:
|
||||
// 1. Locate {cwd}/.claude/projects/* / progress.json (any nested project)
|
||||
// 2. Read progress.json + sibling plan.md
|
||||
|
|
@ -13,12 +17,17 @@
|
|||
// 4. For each commit, match against plan steps' commit_message_pattern
|
||||
// 5. If derived current_step > stored current_step → write fresh checkpoint
|
||||
// atomically (tmp + rename), monotonic only (current_step never decreases).
|
||||
// 6. Always exit 0 — NEVER blocks compaction.
|
||||
// 6. Refresh sibling .session-state.local.json if present and status is
|
||||
// resumable (in_progress | partial) — bumps updated_at only. Never
|
||||
// creates the state file; creation is the writer's job at session-end.
|
||||
// Skips if status is completed/failed/stopped (non-resumable or terminal).
|
||||
// 7. Always exit 0 — NEVER blocks compaction.
|
||||
//
|
||||
// v3.3.0:
|
||||
// - atomicWrite extracted to lib/util/atomic-write.mjs for reuse
|
||||
// - File reformatted (removed pre-existing leading-whitespace syntax error
|
||||
// that silently broke the hook since v3.1.0; PreCompact swallowed it)
|
||||
// - Added Handover 7 sibling-state refresh
|
||||
|
||||
import { readFileSync, existsSync, readdirSync, statSync } from 'node:fs';
|
||||
import { join, dirname } from 'node:path';
|
||||
|
|
@ -104,6 +113,25 @@ function repoRootOf(dir) {
|
|||
} catch { return null; }
|
||||
}
|
||||
|
||||
// Resumable statuses for .session-state.local.json. `completed` is terminal;
|
||||
// `failed`/`stopped` are operator-action-required and should NOT be silently
|
||||
// refreshed by a background hook (would mask the alert). We only bump
|
||||
// updated_at for in_progress | partial — the active-work statuses.
|
||||
const SESSION_STATE_REFRESHABLE = new Set(['in_progress', 'partial']);
|
||||
|
||||
function refreshSessionState(projDir) {
|
||||
const statePath = join(projDir, '.session-state.local.json');
|
||||
if (!existsSync(statePath)) return false;
|
||||
const state = readJson(statePath);
|
||||
if (!state || typeof state !== 'object') return false;
|
||||
if (!SESSION_STATE_REFRESHABLE.has(state.status)) return false;
|
||||
// Monotonic guard: only mutate updated_at. Never touch status, project,
|
||||
// next_session_*. The writer (Phase 8 / helper) owns those fields.
|
||||
state.updated_at = new Date().toISOString();
|
||||
atomicWriteJson(statePath, state);
|
||||
return true;
|
||||
}
|
||||
|
||||
let stdinPayload = '';
|
||||
try { stdinPayload = readFileSync(0, 'utf-8'); } catch { /* fine */ }
|
||||
|
||||
|
|
@ -142,7 +170,15 @@ for (const { projDir, progPath, planPath } of progressFiles) {
|
|||
};
|
||||
}
|
||||
atomicWriteJson(progPath, progress);
|
||||
process.stderr.write(`[ultraplan-local] pre-compact flush: ${progPath} → current_step=${derivedStep}\n`);
|
||||
process.stderr.write(`[ultraplan-local] pre-compact flush: ${progPath} -> current_step=${derivedStep}\n`);
|
||||
mutationsMade++;
|
||||
}
|
||||
|
||||
// Sibling .session-state.local.json refresh (Handover 7). Independent of
|
||||
// progress.json mutation — the state file may exist for a session that
|
||||
// hasn't advanced step yet, and we still want updated_at to track liveness.
|
||||
if (refreshSessionState(projDir)) {
|
||||
process.stderr.write(`[ultraplan-local] pre-compact refresh: ${projDir}/.session-state.local.json\n`);
|
||||
mutationsMade++;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue