feat(lanes): add per-feature state + archive core (lane_features) (B)

This commit is contained in:
2026-08-04 14:16:48 +07:00
parent 465dca35e5
commit 181e0f77ff
3 changed files with 373 additions and 0 deletions
+41
View File
@@ -432,6 +432,31 @@ db.exec(`
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
);
CREATE TABLE IF NOT EXISTS lane_features (
id INTEGER PRIMARY KEY AUTOINCREMENT,
lane_id INTEGER NOT NULL,
slug TEXT NOT NULL,
title TEXT NOT NULL DEFAULT '',
branch TEXT,
pipeline TEXT NOT NULL DEFAULT 'default',
stage TEXT NOT NULL DEFAULT 'idle',
stage_since TEXT,
status TEXT NOT NULL DEFAULT 'idle',
gate_decision TEXT,
ci_status TEXT,
qc_dev TEXT,
stages TEXT NOT NULL DEFAULT '{}',
links TEXT NOT NULL DEFAULT '{}',
notes TEXT,
archived_at TEXT,
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
UNIQUE (lane_id, slug),
FOREIGN KEY (lane_id) REFERENCES lanes(id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_lane_features_lane ON lane_features(lane_id);
CREATE INDEX IF NOT EXISTS idx_lanes_session ON lanes(session_id);
`);
@@ -519,6 +544,22 @@ db.prepare(
"CREATE UNIQUE INDEX IF NOT EXISTS idx_lanes_slot ON lanes(slot) WHERE slot IS NOT NULL"
).run();
// Migrate: per-feature state (B). `active_feature_id` points at the
// lane_features row currently "live" (unarchived) for this lane — null for a
// lane that has never called `ccam feature activate`, which is why this
// column is nullable and every downstream reader of a lane row is unaffected
// by its addition. ON DELETE SET NULL, not CASCADE: deleting the ACTIVE
// feature row (which normally only happens via cascade when the LANE itself
// is deleted, at which point this column is moot anyway) must never leave a
// dangling id on a lane row that still exists.
try {
db.prepare("SELECT active_feature_id FROM lanes LIMIT 1").get();
} catch {
db.prepare(
"ALTER TABLE lanes ADD COLUMN active_feature_id INTEGER REFERENCES lane_features(id) ON DELETE SET NULL"
).run();
}
// Migrate: link agent rows to a workflow run. Workflow inner-agents are already
// ingested as subagents (same subagents/ dir); these columns add the grouping +
// phase that the run journal provides. Additive, safe on existing DBs.