toolroll 0.9.58 → 0.9.59

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.9.59 — 2026-10-10
4
+
5
+ - **Safer deploys.** A deploy that fails before the swap releases its own
6
+ lock. A stranded lock clears with one guided command.
7
+ - **Planning restarts on a new scope.** Changing a task's scope restarts its
8
+ planning. Cancelling a task closes its open questions.
9
+
3
10
  ## 0.9.58 — 2026-10-09
4
11
 
5
12
  - **One token kind.** Personal, lead and coordinator tokens now live in one
@@ -36,6 +36,11 @@ export function installLedgerTriggers(db) {
36
36
  const task = (ref) => `(SELECT external_id FROM task_ref WHERE id = ${ref})`;
37
37
  const runRef = `(SELECT task_ref FROM run WHERE id = NEW.run)`;
38
38
  const event = (at, actor, project, taskId, runId, action, outcome) => `INSERT INTO action_ledger(at,actor,repo,task_id,run_id,action,outcome,source) VALUES (${at},${actor},${project},${taskId},${runId},'${action}',${outcome},'work');`;
39
+ // v119: closing a question without an answer is its own event. Replace only the old trigger's future behavior;
40
+ // every ledger entry already recorded, and its seal, stays unchanged.
41
+ const answeredTrigger = db.prepare("SELECT sql FROM sqlite_master WHERE type = 'trigger' AND name = 'ledger_decision_answered'").get();
42
+ if (answeredTrigger !== undefined && !String(answeredTrigger["sql"]).includes("superseded_reason"))
43
+ db.exec("DROP TRIGGER ledger_decision_answered");
39
44
  db.exec(`
40
45
  CREATE TRIGGER IF NOT EXISTS ledger_run_started AFTER INSERT ON run BEGIN
41
46
  ${event("NEW.started_at", "NEW.runner", repo("NEW.task_ref"), task("NEW.task_ref"), "NEW.id", "run started", "NEW.role")}
@@ -48,9 +53,13 @@ CREATE TRIGGER IF NOT EXISTS ledger_decision_opened AFTER INSERT ON decision BEG
48
53
  ${event("NEW.created_at", "'system'", repo(runRef), task(runRef), "NEW.run", "decision opened", "NEW.state")}
49
54
  END;
50
55
  CREATE TRIGGER IF NOT EXISTS ledger_decision_answered AFTER UPDATE OF answered_at ON decision
51
- WHEN NEW.answered_at IS NOT NULL AND OLD.answered_at IS NULL BEGIN
56
+ WHEN NEW.answered_at IS NOT NULL AND OLD.answered_at IS NULL AND NEW.superseded_reason IS NULL BEGIN
52
57
  ${event("NEW.answered_at", "COALESCE(NEW.answered_by,'system')", repo(runRef), task(runRef), "NEW.run", "decision answered", "NEW.state")}
53
58
  END;
59
+ CREATE TRIGGER IF NOT EXISTS ledger_decision_closed AFTER UPDATE OF answered_at ON decision
60
+ WHEN NEW.answered_at IS NOT NULL AND OLD.answered_at IS NULL AND NEW.superseded_reason IS NOT NULL BEGIN
61
+ ${event("NEW.answered_at", "COALESCE(NEW.answered_by,'system')", repo(runRef), task(runRef), "NEW.run", "decision closed", "NEW.superseded_reason")}
62
+ END;
54
63
  CREATE TRIGGER IF NOT EXISTS ledger_scope_approved AFTER UPDATE OF approved_at, approved_digest ON task_scope
55
64
  WHEN NEW.approved_at IS NOT NULL AND (OLD.approved_at IS NOT NEW.approved_at OR OLD.approved_digest IS NOT NEW.approved_digest) BEGIN
56
65
  ${event("NEW.approved_at", "COALESCE(NEW.approved_by,'system')", "(SELECT repo FROM task_ref WHERE backend = 'built-in' AND external_id = NEW.task_id)", "NEW.task_id", "NULL", "scope approved", "'approved'")}