fix(lanes): clear stale detection on real stage transitions, exclude scratch dirs from implement detect
setStage() left detected_stage/signal/at untouched across a real transition, so a prior task's leftover inference (e.g. `tests` from earlier work) both misrepresented a fresh task's progress and — because recordDetection is forward-only — silently rejected every real detection behind it until the old one aged past DETECTION_TTL_MS. A real stage change now clears the detection columns; a same-stage heartbeat leaves a live detection alone. Also excludes `.superpowers/` from the `implement` node's Write detect rule - brainstorm-companion scratch files were being counted as implementation work. fix(client): disable Node's default --experimental-webstorage in tests Node 25 enables --experimental-webstorage by default, which defines a broken global `localStorage` (no backing file configured) ahead of jsdom's own full polyfill. Every test touching real localStorage failed with "localStorage.clear/getItem is not a function" — not flaky, not test-specific, just this one Node default. NODE_OPTIONS=--no-experimental-webstorage on the test scripts lets jsdom's polyfill take over as before.
This commit is contained in:
+15
-3
@@ -189,24 +189,36 @@ function deleteLane(id) {
|
||||
* Record a stage transition. `stage_since` moves ONLY when the stage value
|
||||
* actually changes, so the UI's time-on-phase is real; a re-report of the same
|
||||
* stage (a heartbeat, an added note) leaves it alone.
|
||||
*
|
||||
* A real transition also clears `detected_stage`/`detected_signal`/`detected_at`.
|
||||
* Inference tracks progress relative to whatever the agent last declared; once
|
||||
* the agent declares again, any older detection is either stale (a prior task's
|
||||
* leftover, e.g. `tests` from earlier work bleeding into a fresh `plan`) or
|
||||
* redundant (the agent's own claim now covers it). Left in place it would both
|
||||
* paint stale progress in the UI AND — because recordDetection is forward-only
|
||||
* — silently reject every real detection for the new stage until the old one
|
||||
* ages past DETECTION_TTL_MS.
|
||||
*/
|
||||
function setStage(id, { stage, status, evidence, note, result } = {}) {
|
||||
const lane = getLane(id);
|
||||
if (!lane) throw Object.assign(new Error(`no lane ${id}`), { code: "ENOLANE" });
|
||||
const next = stage || lane.stage;
|
||||
const changed = next !== lane.stage;
|
||||
const stages = { ...lane.stages };
|
||||
const prev = stages[next] || {};
|
||||
stages[next] = {
|
||||
enteredAt: next === lane.stage && prev.enteredAt ? prev.enteredAt : nowIso(),
|
||||
enteredAt: !changed && prev.enteredAt ? prev.enteredAt : nowIso(),
|
||||
evidence: evidence !== undefined ? evidence : prev.evidence || null,
|
||||
result: result !== undefined ? result : prev.result || null,
|
||||
};
|
||||
db.prepare(
|
||||
`UPDATE lanes SET stage = ?, stage_since = ?, status = ?, stages = ?, notes = ?, updated_at = ?
|
||||
`UPDATE lanes SET stage = ?, stage_since = ?, status = ?, stages = ?, notes = ?, updated_at = ?${
|
||||
changed ? ", detected_stage = NULL, detected_signal = NULL, detected_at = NULL" : ""
|
||||
}
|
||||
WHERE id = ?`
|
||||
).run(
|
||||
next,
|
||||
next === lane.stage ? lane.stage_since || nowIso() : nowIso(),
|
||||
changed ? nowIso() : lane.stage_since || nowIso(),
|
||||
status || lane.status,
|
||||
JSON.stringify(stages),
|
||||
note !== undefined ? note : lane.notes,
|
||||
|
||||
Reference in New Issue
Block a user