fix(lanes): expire stale stage detections, cover raw test runs in ship-feature gates
withDetected() rendered lane.detected_stage forever once written — the TTL in recordDetection only stops future writes from advancing past a stale floor, it never clears the row, so a lane gone quiet kept painting amber nodes from activity that was no longer current. Also add the shared test-runner regex to the ship-feature pipeline's `gates` node: Stage 1b's independent-test subagent runs the repo's test command directly, not through `ccam`, and had no detection fallback if `ccam stage tests` was skipped.
This commit is contained in:
@@ -901,6 +901,26 @@ describe("stage detection", () => {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("a stale detection stops rendering as `detected` in pipeline_nodes, even though the DB row still holds it", () => {
|
||||
const { db } = require("../db");
|
||||
const l = lanes.createLane({ cwd: "/tmp/wt-detect-stale-render" });
|
||||
lanes.recordDetection(l.id, { nodeId: "tests" });
|
||||
const oldTime = new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString();
|
||||
db.prepare("UPDATE lanes SET detected_at = ? WHERE id = ?").run(oldTime, l.id);
|
||||
|
||||
const lane = lanes.getLane(l.id);
|
||||
// The TTL only gates future writes; the stale row is still there until a
|
||||
// fresh detection or a real stage transition overwrites it.
|
||||
assert.equal(lane.detected_stage, "tests");
|
||||
|
||||
const nodes = lanes.lanePayload(lane).pipeline_nodes;
|
||||
assert.ok(
|
||||
!nodes.some((n) => n.detected),
|
||||
"a detection older than the TTL must not paint any node amber — it is no longer current information"
|
||||
);
|
||||
lanes.deleteLane(l.id);
|
||||
});
|
||||
});
|
||||
|
||||
describe("the forward-only hold is short enough for one work session", () => {
|
||||
|
||||
@@ -521,6 +521,10 @@ test.describe("ship-feature pipeline template", () => {
|
||||
["implementing", "Write", { file_path: "server/lib/app.js" }],
|
||||
["gates", "Bash", { command: "ccam lanes hook ci-gate" }],
|
||||
["gates", "Bash", { command: "ccam lanes sync-base --check feat/foo" }],
|
||||
// Stage 1b's independent-test subagent runs the repo's raw test command
|
||||
// directly, not through `ccam` — without this rule that pass has no
|
||||
// detection fallback if the agent skips `ccam stage tests`.
|
||||
["gates", "Bash", { command: "npm run test:server" }],
|
||||
["e2e-feature", "Bash", { command: "ccam lanes up --qc" }],
|
||||
["e2e-feature", "Bash", { command: "ccam lanes hook e2e" }],
|
||||
["review", "Skill", { skill: "code-review" }],
|
||||
|
||||
@@ -96,6 +96,10 @@
|
||||
{
|
||||
"tool": "Bash",
|
||||
"match": "\\bccam\\b[^;&|]*(\\bhook ci-gate\\b|\\bsync-base --check\\b)"
|
||||
},
|
||||
{
|
||||
"tool": "Bash",
|
||||
"match": "\\b(npm (run )?test|pytest|vitest|jest|go test|cargo test|node\\s+--test|rspec|phpunit|dotnet test|mvn test|gradle test)\\b"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -466,8 +466,15 @@ function classifyLiveness({ status, stage, ageSec }, deadSec = DEAD_SEC) {
|
||||
* would otherwise render as an inference instead of the blue `current` ring.
|
||||
* Past nodes go through `stageRecords`, which resolves those alias keys — a
|
||||
* node the agent DECLARED must never be painted as merely detected.
|
||||
*
|
||||
* A detection older than the TTL is treated the same as no detection: the TTL
|
||||
* in recordDetection only stops it from holding the forward-only floor for
|
||||
* FUTURE writes, it never clears the row, so without this check a lane that
|
||||
* went quiet would keep painting amber nodes from activity that is no longer
|
||||
* current — the exact stale-progress illusion detection exists to avoid.
|
||||
*/
|
||||
function withDetected(states, pipeline, lane) {
|
||||
if (detectionIsStale(lane.detected_at)) return states.map((n) => ({ ...n, detected: false }));
|
||||
const detectedIdx = phaseIdx(pipeline, lane.detected_stage);
|
||||
if (detectedIdx === -1) return states.map((n) => ({ ...n, detected: false }));
|
||||
const records = stageRecords(pipeline, lane.stages);
|
||||
|
||||
Reference in New Issue
Block a user