mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-20 10:06:54 +03:00
fix: stop cross-project live session leakage and stale-adapter 401s
Field session on a nested SvelteKit app surfaced a self-reinforcing leak: localStorage is per-origin, two projects reused 127.0.0.1:5174, and a React project's leftover cycling session was resumed inside the Svelte project. Its checkpoints then materialized a ghost session in the new project's durable store that kept reattaching after every discard, and a stale adapter module 401'd on live.js, hiding the picker. Four fixes: - Server: only session-creating events (generate, steer) may mint a journal. Progress events (checkpoints, mount acks, accept/discard) for unknown ids are refused with 404 unknown_session and never enqueued, so foreign browser state cannot create ghost sessions. Browser sends are gated so progress never overtakes its own creating POST (the Go-time checkpoint and generate are concurrent fetches; the first sweep caught the out-of-order arrival breaking every SvelteKit flow). Steer checkpoints now follow the steer event for the same reason. - Browser: saved sessions carry the server's appRoot; a session stamped by another project is dropped at load time. Unstamped legacy state is caught by the unknown_session refusal, which clears local state and re-arms the picker with an explanatory toast. - SvelteKit adapter: the layout import carries a token-derived revision query so a helper restart changes the module specifier and no Vite client/SSR cache can serve an adapter with a rotated-out token; live-inject --port reads the running helper's token from server.json instead of writing an unauthenticated live.js URL; script load failures log an actionable console error; and adapter removal is byte-exact (the old regex swallowed the next line's indentation). - live.mjs resolves surface briefs from appRoot, then contextRoot, then repoRoot, matching context.mjs in nested-app repos. Tests: server unknown-session rejection units, adapter revision/ byte-exact-removal units, and a foreign-session e2e scenario that seeds another project's localStorage state and asserts it is cleared, no ghost journal materializes, and picking still works. AI-assisted (Claude Code). Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Code
parent
69456364b2
commit
a83d767cf9
+10
-2
@@ -169,12 +169,20 @@ The agent should then:
|
||||
let surfaceBrief = null;
|
||||
let surfaceBriefPath = null;
|
||||
try {
|
||||
const resolvedBrief = resolveSurfaceBrief(roots.appRoot, liveTarget.absoluteTargetPath || null);
|
||||
if (resolvedBrief?.brief) {
|
||||
// Briefs live under .impeccable/surfaces, which in a nested-app repo sits
|
||||
// at the CONTEXT or repo root, not the app root; context.mjs already finds
|
||||
// them there, and live must not report "no brief" for the same project.
|
||||
const briefRoots = [roots.appRoot, roots.contextRoot, roots.repoRoot]
|
||||
.filter(Boolean)
|
||||
.filter((dir, i, arr) => arr.findIndex((other) => path.resolve(other) === path.resolve(dir)) === i);
|
||||
for (const briefRoot of briefRoots) {
|
||||
const resolvedBrief = resolveSurfaceBrief(briefRoot, liveTarget.absoluteTargetPath || null);
|
||||
if (!resolvedBrief?.brief) continue;
|
||||
surfaceBrief = resolvedBrief.brief.text ?? safeRead(resolvedBrief.brief.path);
|
||||
surfaceBriefPath = resolvedBrief.brief.path
|
||||
? path.relative(liveTarget.originalCwd, resolvedBrief.brief.path)
|
||||
: null;
|
||||
break;
|
||||
}
|
||||
} catch { /* briefs are optional context */ }
|
||||
console.log(JSON.stringify({
|
||||
|
||||
Reference in New Issue
Block a user