toolroll 0.9.41 → 0.9.42

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.
Files changed (67) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/cli.js +3 -1
  3. package/dist/coding-types.d.ts +2 -24
  4. package/dist/coding-workspace.js +12 -11
  5. package/dist/contracts/cli.d.ts +736 -0
  6. package/dist/contracts/cli.js +223 -0
  7. package/dist/contracts/coding-activity.d.ts +64 -0
  8. package/dist/contracts/coding-activity.js +58 -0
  9. package/dist/contracts/console-api.d.ts +483 -0
  10. package/dist/contracts/console-api.js +273 -0
  11. package/dist/contracts/diff-stat.d.ts +58 -0
  12. package/dist/contracts/diff-stat.js +59 -0
  13. package/dist/contracts/integration-metadata.d.ts +99 -0
  14. package/dist/contracts/integration-metadata.js +101 -0
  15. package/dist/contracts/model-catalog.d.ts +21 -0
  16. package/dist/contracts/model-catalog.js +14 -0
  17. package/dist/contracts/native-census.d.ts +42 -0
  18. package/dist/contracts/native-census.js +36 -0
  19. package/dist/contracts/observation-request.d.ts +37 -0
  20. package/dist/contracts/observation-request.js +25 -0
  21. package/dist/contracts/provider.d.ts +30 -0
  22. package/dist/contracts/provider.js +15 -0
  23. package/dist/contracts/recipes.d.ts +166 -0
  24. package/dist/contracts/recipes.js +52 -0
  25. package/dist/contracts/retention.d.ts +42 -0
  26. package/dist/contracts/retention.js +24 -0
  27. package/dist/contracts/storage.d.ts +23 -0
  28. package/dist/contracts/storage.js +10 -0
  29. package/dist/contracts/store-json.d.ts +315 -0
  30. package/dist/contracts/store-json.js +196 -0
  31. package/dist/contracts/trigger-payloads.d.ts +369 -0
  32. package/dist/contracts/trigger-payloads.js +144 -0
  33. package/dist/contracts/update-journal.d.ts +254 -0
  34. package/dist/contracts/update-journal.js +155 -0
  35. package/dist/contracts/verification-view.d.ts +93 -0
  36. package/dist/contracts/verification-view.js +43 -0
  37. package/dist/control-setup.d.ts +1 -1
  38. package/dist/desktop-update.d.ts +7 -40
  39. package/dist/desktop-update.js +36 -8
  40. package/dist/evidence.d.ts +3 -19
  41. package/dist/flow-triggers.js +61 -60
  42. package/dist/mcp-connect.js +37 -22
  43. package/dist/model-catalog.js +8 -6
  44. package/dist/observations.d.ts +2 -6
  45. package/dist/observations.js +13 -9
  46. package/dist/operate.js +2 -1
  47. package/dist/process-recovery-native.d.ts +2 -13
  48. package/dist/process-recovery-native.js +8 -5
  49. package/dist/project-tools.d.ts +6 -24
  50. package/dist/project-tools.js +22 -18
  51. package/dist/provider.d.ts +3 -8
  52. package/dist/provider.js +15 -11
  53. package/dist/recipes.d.ts +3 -28
  54. package/dist/recipes.js +79 -52
  55. package/dist/retention.d.ts +4 -5
  56. package/dist/retention.js +5 -4
  57. package/dist/serve.js +142 -28
  58. package/dist/storage.d.ts +2 -1
  59. package/dist/storage.js +12 -9
  60. package/dist/store.d.ts +3 -15
  61. package/dist/store.js +80 -158
  62. package/dist/surface.d.ts +6 -54
  63. package/dist/surface.js +6 -1
  64. package/dist/toolroll-update.d.ts +8 -99
  65. package/dist/toolroll-update.js +28 -18
  66. package/dist/verification-evidence.js +27 -12
  67. package/package.json +1 -1
package/dist/store.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { chatSchema, chatTables } from "./contracts/chat-tables.js";
2
+ import { parseStoreColumn, readStoreColumn, readStoreStringifiedList, readStoreTextList } from "./contracts/store-json.js";
2
3
  import { assessmentFromSavedEvidence, verificationEvidence } from "./verification-evidence.js";
3
4
  import { LEARNING_SCHEMA, queueLearning } from "./project-learning.js";
4
5
  import { SKILLS_SCHEMA } from "./project-skills.js";
@@ -504,10 +505,10 @@ const isCheckExitCode = (value) => value === null || (Number.isSafeInteger(value
504
505
  /** Read a stored suite list; a malformed optional summary reads as no suite detail. */
505
506
  export function checkSuitesOf(value) {
506
507
  try {
507
- const parsed = JSON.parse(String(value ?? "[]"));
508
- if (!Array.isArray(parsed))
508
+ const read = readStoreColumn("run_check.suites_json", value ?? "[]");
509
+ if (!read.ok)
509
510
  return [];
510
- return parsed.filter((one) => {
511
+ return read.value.filter((one) => {
511
512
  if (one === null || typeof one !== "object")
512
513
  return false;
513
514
  const row = one;
@@ -620,37 +621,22 @@ function readTeammateMemory(row) {
620
621
  createdBy: String(row["created_by"]), createdAt: String(row["created_at"]), updatedBy: String(row["updated_by"]), updatedAt: String(row["updated_at"]), usedAt: row["used_at"] === null ? null : String(row["used_at"]) };
621
622
  }
622
623
  function readTeammateSuggestion(row) {
623
- const parsed = (key, fallback) => { try {
624
- return JSON.parse(String(row[key]));
625
- }
626
- catch {
627
- return fallback;
628
- } };
629
- return { id: Number(row["id"]), teammate: Number(row["teammate"]), tool: String(row["tool"]), action: String(row["action"]), rule: parsed("rule_json", { use: "ask" }), was: parsed("was_json", { use: "ask" }),
630
- evidence: parsed("evidence_json", []), said: String(row["said"]), state: String(row["state"]),
624
+ const parsed = (column, key, fallback) => { const read = readStoreColumn(column, row[key]); return read.ok ? read.value : fallback; };
625
+ return { id: Number(row["id"]), teammate: Number(row["teammate"]), tool: String(row["tool"]), action: String(row["action"]), rule: parsed("teammate_suggestion.rule_json", "rule_json", { use: "ask" }), was: parsed("teammate_suggestion.was_json", "was_json", { use: "ask" }),
626
+ evidence: parsed("teammate_suggestion.evidence_json", "evidence_json", []), said: String(row["said"]), state: String(row["state"]),
631
627
  decidedBy: row["decided_by"] === null ? null : String(row["decided_by"]), decidedAt: row["decided_at"] === null ? null : String(row["decided_at"]), createdAt: String(row["created_at"]) };
632
628
  }
633
629
  function readTeammateCall(row) {
634
630
  const text = (key) => row[key] === null || row[key] === undefined ? null : String(row[key]);
635
- let input = {};
636
- try {
637
- input = JSON.parse(String(row["input_json"]));
638
- }
639
- catch {
640
- input = {};
641
- }
631
+ const read = readStoreColumn("teammate_call.input_json", row["input_json"]);
632
+ const input = read.ok ? read.value : {};
642
633
  return { id: Number(row["id"]), teammate: Number(row["teammate"]), card: Number(row["card"]), entry: Number(row["entry"]), tool: String(row["tool"]), action: String(row["action"]), input,
643
634
  rule: String(row["rule"]), why: String(row["why"]), state: String(row["state"]), result: text("result"), decidedBy: text("decided_by"), decidedAt: text("decided_at"),
644
635
  createdAt: String(row["created_at"]), doneAt: text("done_at"), undoOf: row["undo_of"] === null || row["undo_of"] === undefined ? null : Number(row["undo_of"]), undoneBy: text("undone_by"), undoneAt: text("undone_at") };
645
636
  }
646
637
  function readTeammateGrant(row) {
647
- const parsed = (key, fallback) => { try {
648
- return JSON.parse(String(row[key]));
649
- }
650
- catch {
651
- return fallback;
652
- } };
653
- return { teammate: Number(row["teammate"]), tool: String(row["tool"]), actions: parsed("actions_json", []), rules: parsed("rules_json", {}),
638
+ const parsed = (column, key, fallback) => { const read = readStoreColumn(column, row[key]); return read.ok ? read.value : fallback; };
639
+ return { teammate: Number(row["teammate"]), tool: String(row["tool"]), actions: parsed("teammate_tool.actions_json", "actions_json", []), rules: parsed("teammate_tool.rules_json", "rules_json", {}),
654
640
  listedAt: row["listed_at"] === null ? null : String(row["listed_at"]), updatedBy: String(row["updated_by"]), updatedAt: String(row["updated_at"]) };
655
641
  }
656
642
  function readTeammateRow(row) {
@@ -663,7 +649,7 @@ function readTeammateRow(row) {
663
649
  function readTeammateQuestion(row) {
664
650
  const text = (key) => row[key] === null || row[key] === undefined ? null : String(row[key]);
665
651
  return { id: Number(row["id"]), teammate: Number(row["teammate"]), card: Number(row["card"]), entry: Number(row["entry"]), question: String(row["question"]),
666
- options: JSON.parse(String(row["options_json"])), askedOf: String(row["asked_of"]), state: String(row["state"]),
652
+ options: parseStoreColumn("teammate_question.options_json", row["options_json"]), askedOf: String(row["asked_of"]), state: String(row["state"]),
667
653
  choice: text("choice"), answer: text("answer"), answeredBy: text("answered_by"), answeredVia: text("answered_via"), answeredAt: text("answered_at"), createdAt: String(row["created_at"]),
668
654
  toolCall: row["tool_call"] === null || row["tool_call"] === undefined ? null : Number(row["tool_call"]),
669
655
  suggestion: row["suggestion"] === null || row["suggestion"] === undefined ? null : Number(row["suggestion"]) };
@@ -697,10 +683,11 @@ function readFlowCardRow(row) {
697
683
  function readFlowCardSource(value) {
698
684
  if (typeof value !== "string")
699
685
  return null;
686
+ const read = readStoreColumn("flow_card.source_json", value);
687
+ if (!read.ok)
688
+ return null;
700
689
  try {
701
- const parsed = JSON.parse(value);
702
- if (typeof parsed["label"] !== "string")
703
- return null;
690
+ const parsed = read.value;
704
691
  const mail = parsed["mail"] !== null && typeof parsed["mail"] === "object" ? parsed["mail"] : null;
705
692
  const chat = parsed["chat"] !== null && typeof parsed["chat"] === "object" ? parsed["chat"] : null;
706
693
  return { kind: String(parsed["kind"] ?? ""), label: parsed["label"], url: typeof parsed["url"] === "string" ? parsed["url"] : null,
@@ -6684,12 +6671,10 @@ function migrateToV24(db) {
6684
6671
  }
6685
6672
  else {
6686
6673
  let touches = [];
6687
- try {
6688
- touches = JSON.parse(row.touches);
6689
- }
6690
- catch {
6691
- touches = [];
6692
- }
6674
+ const read = readStoreColumn("task_scope.touches", row.touches);
6675
+ // Unlike the display reader, v24 passed every valid JSON value to digestOf, including non-arrays.
6676
+ if (read.ok)
6677
+ touches = read.value;
6693
6678
  const recomputed = digestOf({ goal: row.goal, outOfScope: row.outOfScope, touches, budgetMicrousd: row.budget }, resolved.profile);
6694
6679
  stampUnapproved.run(snapshot, recomputed, provenance, row.taskId);
6695
6680
  }
@@ -9086,7 +9071,7 @@ export class Store {
9086
9071
  return { ok: true };
9087
9072
  let terms;
9088
9073
  try {
9089
- terms = JSON.parse(mode.termsJson);
9074
+ terms = parseStoreColumn("operating_mode.terms_json", mode.termsJson);
9090
9075
  }
9091
9076
  catch {
9092
9077
  return { ok: true };
@@ -9243,15 +9228,7 @@ export class Store {
9243
9228
  const row = this.db.prepare("SELECT * FROM approval_policy WHERE repo = ?").get(repo);
9244
9229
  if (row === undefined)
9245
9230
  return { ...NO_RULES, updatedBy: null, updatedAt: null };
9246
- let paths = [];
9247
- try {
9248
- const parsed = JSON.parse(String(row["protected_paths"]));
9249
- if (Array.isArray(parsed))
9250
- paths = parsed.filter((one) => typeof one === "string");
9251
- }
9252
- catch {
9253
- paths = [];
9254
- }
9231
+ const paths = readStoreTextList("approval_policy.protected_paths", row["protected_paths"]);
9255
9232
  return { notRequester: Number(row["not_requester"]) === 1, protectProject: Number(row["protect_project"]) === 1, protectedPaths: paths, updatedBy: String(row["updated_by"]), updatedAt: String(row["updated_at"]) };
9256
9233
  }
9257
9234
  /** Change a project's rules; the ledger keeps before → after. */
@@ -9302,15 +9279,7 @@ export class Store {
9302
9279
  return { verdict: "seal", protectedWork: false };
9303
9280
  const repo = String(ref["repo"]);
9304
9281
  const rules = this.approvalRules(repo);
9305
- let touches = [];
9306
- try {
9307
- const parsed = JSON.parse(String(scope?.["touches"] ?? "[]"));
9308
- if (Array.isArray(parsed))
9309
- touches = parsed.map(String);
9310
- }
9311
- catch {
9312
- touches = [];
9313
- }
9282
+ const touches = readStoreStringifiedList("task_scope.touches", scope?.["touches"] ?? "[]");
9314
9283
  const protectedWork = isProtectedWork(rules, touches);
9315
9284
  if (!rules.notRequester && !protectedWork)
9316
9285
  return { verdict: "seal", protectedWork: false };
@@ -12048,7 +12017,7 @@ export class Store {
12048
12017
  let reviewAuto = false;
12049
12018
  if (mode !== null && mode.digest === String(row["mode_digest"] ?? "")) {
12050
12019
  try {
12051
- reviewAuto = JSON.parse(mode.termsJson).reviewAuto === true;
12020
+ reviewAuto = parseStoreColumn("operating_mode.terms_json", mode.termsJson).reviewAuto === true;
12052
12021
  }
12053
12022
  catch {
12054
12023
  reviewAuto = false;
@@ -13458,13 +13427,10 @@ export class Store {
13458
13427
  const row = this.db.prepare("SELECT entries_json FROM fallback_config WHERE scope = ? AND phase = 'build'").get(scope);
13459
13428
  if (row === undefined)
13460
13429
  return { ok: true, entries: [] };
13461
- let parsed;
13462
- try {
13463
- parsed = JSON.parse(String(row["entries_json"]));
13464
- }
13465
- catch {
13430
+ const read = readStoreColumn("fallback_config.entries_json", row["entries_json"]);
13431
+ if (!read.ok && read.malformed)
13466
13432
  return { ok: false, problem: `the fallback configuration for ${scope} is not valid JSON — set it again with \`config set fallback\`, or clear it` };
13467
- }
13433
+ const parsed = read.ok ? read.value : null;
13468
13434
  if (!Array.isArray(parsed))
13469
13435
  return { ok: false, problem: `the fallback configuration for ${scope} is not a list of entries — set it again with \`config set fallback\`, or clear it` };
13470
13436
  // A PRESENT row carries one to three entries (atomic authority
@@ -13834,7 +13800,7 @@ export class Store {
13834
13800
  .filter(row => {
13835
13801
  if (assignedRunner !== null)
13836
13802
  return String(row["runner"]) === assignedRunner;
13837
- const repos = readJsonArray(row["repos"]);
13803
+ const repos = readStoreTextList("runner.repos", row["repos"]);
13838
13804
  return repo === null || repos.includes(repo);
13839
13805
  })
13840
13806
  .sort((a, b) => Number(alive(b)) - Number(alive(a)));
@@ -14004,7 +13970,7 @@ export class Store {
14004
13970
  if (open !== null) {
14005
13971
  let pinned = null;
14006
13972
  try {
14007
- const terms = JSON.parse(open.termsJson);
13973
+ const terms = parseStoreColumn("attended_authorization.terms_json", open.termsJson);
14008
13974
  pinned = profileFromJson(typeof terms.profileJson === "string" ? terms.profileJson : null);
14009
13975
  }
14010
13976
  catch {
@@ -14695,7 +14661,10 @@ export class Store {
14695
14661
  .all(...admittedRepos);
14696
14662
  const optionsOf = (raw) => {
14697
14663
  try {
14698
- const parsed = JSON.parse(String(raw));
14664
+ const read = readStoreColumn("decision.options", raw);
14665
+ if (!read.ok)
14666
+ return [];
14667
+ const parsed = read.value;
14699
14668
  return Array.isArray(parsed)
14700
14669
  ? parsed.slice(0, 6).map(one => ({ id: String(one?.id ?? ""), label: String(one?.label ?? ""), reversible: one?.reversible === true }))
14701
14670
  : [];
@@ -15856,7 +15825,7 @@ export class Store {
15856
15825
  refuse(`the attended authorization ${live.id} was minted for ${live.runner} at generation ${live.runnerGeneration}, not ${named.generation}`);
15857
15826
  let pinned = null;
15858
15827
  try {
15859
- const terms = JSON.parse(live.termsJson);
15828
+ const terms = parseStoreColumn("attended_authorization.terms_json", live.termsJson);
15860
15829
  pinned = profileFromJson(typeof terms.profileJson === "string" ? terms.profileJson : null);
15861
15830
  }
15862
15831
  catch {
@@ -17421,7 +17390,7 @@ export class Store {
17421
17390
  let quickMint = false;
17422
17391
  if (mode !== null && mode.digest === input.basis.digest && mode.signedBy === input.approver) {
17423
17392
  try {
17424
- quickMint = JSON.parse(mode.termsJson).quickMint === true;
17393
+ quickMint = parseStoreColumn("operating_mode.terms_json", mode.termsJson).quickMint === true;
17425
17394
  }
17426
17395
  catch {
17427
17396
  quickMint = false;
@@ -18808,7 +18777,7 @@ export class Store {
18808
18777
  if (mode !== null) {
18809
18778
  let publication;
18810
18779
  try {
18811
- publication = JSON.parse(mode.termsJson).publication;
18780
+ publication = parseStoreColumn("operating_mode.terms_json", mode.termsJson).publication;
18812
18781
  }
18813
18782
  catch {
18814
18783
  publication = undefined;
@@ -18938,7 +18907,7 @@ export class Store {
18938
18907
  if (basis === "mode") {
18939
18908
  let publication;
18940
18909
  try {
18941
- publication = mode === null ? undefined : JSON.parse(mode.termsJson).publication;
18910
+ publication = mode === null ? undefined : parseStoreColumn("operating_mode.terms_json", mode.termsJson).publication;
18942
18911
  }
18943
18912
  catch {
18944
18913
  publication = undefined;
@@ -18950,7 +18919,7 @@ export class Store {
18950
18919
  else if (basis === "grant" && mode !== null) {
18951
18920
  let publication;
18952
18921
  try {
18953
- publication = JSON.parse(mode.termsJson).publication;
18922
+ publication = parseStoreColumn("operating_mode.terms_json", mode.termsJson).publication;
18954
18923
  }
18955
18924
  catch {
18956
18925
  publication = undefined;
@@ -19486,7 +19455,7 @@ export class Store {
19486
19455
  teammateEvents(teammate, limit, since = null) {
19487
19456
  return this.db.prepare(`SELECT * FROM teammate_event WHERE teammate = ? ${since === null ? "" : "AND at >= ?"} ORDER BY id DESC LIMIT ?`).all(...(since === null ? [teammate, limit] : [teammate, since, limit])).map(row => ({
19488
19457
  id: Number(row["id"]), teammate: Number(row["teammate"]), card: row["card"] === null ? null : Number(row["card"]), entry: row["entry"] === null ? null : Number(row["entry"]),
19489
- kind: String(row["kind"]), said: String(row["said"]), detail: row["detail_json"] === null ? null : JSON.parse(String(row["detail_json"])),
19458
+ kind: String(row["kind"]), said: String(row["said"]), detail: row["detail_json"] === null ? null : parseStoreColumn("teammate_event.detail_json", row["detail_json"]),
19490
19459
  by: row["by"] === null ? null : String(row["by"]), at: String(row["at"])
19491
19460
  }));
19492
19461
  }
@@ -19837,15 +19806,7 @@ export class Store {
19837
19806
  /** A card's discussion, oldest first. */
19838
19807
  flowComments(card) {
19839
19808
  return this.db.prepare("SELECT * FROM flow_comment WHERE card = ? ORDER BY id").all(card).map(row => {
19840
- let mentions = [];
19841
- try {
19842
- const parsed = JSON.parse(String(row["mentions_json"]));
19843
- if (Array.isArray(parsed))
19844
- mentions = parsed.filter((one) => typeof one === "string");
19845
- }
19846
- catch {
19847
- mentions = [];
19848
- }
19809
+ const mentions = readStoreTextList("flow_comment.mentions_json", row["mentions_json"]);
19849
19810
  return { id: Number(row["id"]), card: Number(row["card"]), kind: String(row["kind"]), author: String(row["author"]), body: String(row["body"]), mentions, at: String(row["at"]) };
19850
19811
  });
19851
19812
  }
@@ -19947,13 +19908,8 @@ export class Store {
19947
19908
  const row = this.db.prepare("SELECT tools_json FROM tool_seal WHERE task = ? AND approved_digest = ?").get(taskId, approvedDigest);
19948
19909
  if (row === undefined)
19949
19910
  return null;
19950
- try {
19951
- const parsed = JSON.parse(String(row["tools_json"]));
19952
- return Array.isArray(parsed) ? parsed.filter((one) => typeof one?.name === "string" && typeof one?.digest === "string") : [];
19953
- }
19954
- catch {
19955
- return [];
19956
- }
19911
+ const read = readStoreColumn("tool_seal.tools_json", row["tools_json"]);
19912
+ return read.ok ? read.value.filter((one) => typeof one?.name === "string" && typeof one?.digest === "string") : [];
19957
19913
  }
19958
19914
  /** What one attempt launched with (first launch of the run wins its record; a resumed turn updates it). */
19959
19915
  recordRunTools(run, toolsJson, now) {
@@ -19969,19 +19925,15 @@ export class Store {
19969
19925
  const row = this.db.prepare("SELECT json_extract(tools_json, '$.fence') AS fence FROM run_tool WHERE run = ?").get(run);
19970
19926
  if (row === undefined || row["fence"] === null)
19971
19927
  return null;
19972
- try {
19973
- return JSON.parse(String(row["fence"]));
19974
- }
19975
- catch {
19976
- return null;
19977
- }
19928
+ const read = readStoreColumn("run_tool.tools_json.fence", row["fence"]);
19929
+ return read.ok ? read.value : null;
19978
19930
  }
19979
19931
  runTools(run) {
19980
19932
  const row = this.db.prepare("SELECT tools_json FROM run_tool WHERE run = ?").get(run);
19981
19933
  if (row === undefined)
19982
19934
  return null;
19983
19935
  try {
19984
- const parsed = JSON.parse(String(row["tools_json"]));
19936
+ const parsed = parseStoreColumn("run_tool.tools_json", row["tools_json"]);
19985
19937
  return { tools: Array.isArray(parsed.tools) ? parsed.tools : [], skipped: Array.isArray(parsed.skipped) ? parsed.skipped : [] };
19986
19938
  }
19987
19939
  catch {
@@ -20075,7 +20027,7 @@ export class Store {
20075
20027
  const row = this.db.prepare("SELECT a.* FROM mate_ask a JOIN mate_turn t ON t.id = a.turn WHERE a.turn = ? AND t.state = 'answered'").get(turn);
20076
20028
  if (row === undefined)
20077
20029
  return null;
20078
- const options = JSON.parse(String(row["options_json"]));
20030
+ const options = parseStoreColumn("mate_ask.options_json", row["options_json"]);
20079
20031
  return { turn: Number(row["turn"]), thread: Number(row["thread"]), question: String(row["question"]), options: Array.isArray(options) ? options.map(String) : [], createdAt: String(row["created_at"]) };
20080
20032
  }
20081
20033
  /** What a tap on the question finds: open; answered (its owner has written in its thread since the reply that asked
@@ -22721,13 +22673,7 @@ export class Store {
22721
22673
  const row = this.db.prepare("SELECT about_json FROM lead_config WHERE account = ?").get(account);
22722
22674
  if (row === undefined)
22723
22675
  return [];
22724
- try {
22725
- const lines = JSON.parse(String(row["about_json"]));
22726
- return Array.isArray(lines) ? lines.filter((one) => typeof one === "string") : [];
22727
- }
22728
- catch {
22729
- return [];
22730
- }
22676
+ return readStoreTextList("lead_config.about_json", row["about_json"]);
22731
22677
  }
22732
22678
  /** Save what this person's lead knows about them (checked by the caller: lead-about.ts). A person who never named
22733
22679
  * their lead gets an empty name and persona, so they keep following the defaults. */
@@ -23284,7 +23230,7 @@ export class Store {
23284
23230
  mateRequestReceipt(session, request) {
23285
23231
  const row = this.db.prepare("SELECT result FROM mutation WHERE idempotency_key = ? AND operation = 'mate-send'")
23286
23232
  .get(`mate-send:${session}:${request}`);
23287
- return row === undefined ? null : JSON.parse(String(row["result"]));
23233
+ return row === undefined ? null : parseStoreColumn("mutation.result", row["result"]);
23288
23234
  }
23289
23235
  /**
23290
23236
  * Run `body` once per key, ever.
@@ -23315,7 +23261,7 @@ export class Store {
23315
23261
  .prepare("SELECT result FROM mutation WHERE idempotency_key = ?")
23316
23262
  .get(idempotencyKey);
23317
23263
  if (seen !== undefined)
23318
- return JSON.parse(String(seen["result"]));
23264
+ return parseStoreColumn("mutation.result", seen["result"]);
23319
23265
  const result = body();
23320
23266
  if (!worthRecording(result))
23321
23267
  return result;
@@ -23596,7 +23542,7 @@ function readDecision(row) {
23596
23542
  state: String(row["state"]),
23597
23543
  recap: String(row["recap"]),
23598
23544
  question: String(row["question"]),
23599
- options: JSON.parse(String(row["options"])),
23545
+ options: parseStoreColumn("decision.options", row["options"]),
23600
23546
  recommendation: String(row["recommendation"]),
23601
23547
  assignee: row["assignee"] === null ? null : String(row["assignee"]),
23602
23548
  deadline: row["deadline"] === null ? null : String(row["deadline"]),
@@ -23638,7 +23584,7 @@ function readPlanRevision(row) {
23638
23584
  kind: String(row["kind"]),
23639
23585
  authorityKind: String(row["authority_kind"]),
23640
23586
  authorityDigest: String(row["authority_digest"]),
23641
- changedFields: row["changed_fields"] === null || row["changed_fields"] === undefined ? [] : JSON.parse(String(row["changed_fields"])),
23587
+ changedFields: row["changed_fields"] === null || row["changed_fields"] === undefined ? [] : parseStoreColumn("plan_revision.changed_fields", row["changed_fields"]),
23642
23588
  status: String(row["status"]),
23643
23589
  createdAt: String(row["created_at"]),
23644
23590
  resolvedAt: row["resolved_at"] === null || row["resolved_at"] === undefined ? null : String(row["resolved_at"]),
@@ -23665,7 +23611,7 @@ function readRunCheckpoint(row) {
23665
23611
  run: Number(row["run"]),
23666
23612
  taskRef: Number(row["task_ref"]),
23667
23613
  planRevision: Number(row["plan_revision"]),
23668
- snapshot: JSON.parse(String(row["snapshot_json"])),
23614
+ snapshot: parseStoreColumn("run_checkpoint.snapshot_json", row["snapshot_json"]),
23669
23615
  createdAt: String(row["created_at"]),
23670
23616
  };
23671
23617
  }
@@ -23738,7 +23684,7 @@ function readTelegramConversationPart(row) {
23738
23684
  text: String(row["text"]),
23739
23685
  replyTo: text("reply_to"),
23740
23686
  proposal: row["proposal"] === null || row["proposal"] === undefined ? null : Number(row["proposal"]),
23741
- keyboard: row["keyboard_json"] === null || row["keyboard_json"] === undefined ? null : JSON.parse(String(row["keyboard_json"])),
23687
+ keyboard: row["keyboard_json"] === null || row["keyboard_json"] === undefined ? null : parseStoreColumn("telegram_conversation_part.keyboard_json", row["keyboard_json"]),
23742
23688
  state: String(row["state"]),
23743
23689
  messageId: text("message_id"),
23744
23690
  taskId: text("task_id"),
@@ -23804,7 +23750,7 @@ function readPublicationGrant(row) {
23804
23750
  remote: String(row["remote"]),
23805
23751
  headPrefix: String(row["head_prefix"]),
23806
23752
  base: String(row["base"]),
23807
- capabilities: JSON.parse(String(row["capabilities"])),
23753
+ capabilities: parseStoreColumn("publication_grant.capabilities", row["capabilities"]),
23808
23754
  selector: String(row["selector"]),
23809
23755
  draft: Number(row["draft"]) === 1,
23810
23756
  grantedBy: String(row["granted_by"]),
@@ -23879,8 +23825,8 @@ function readTaskRef(row) {
23879
23825
  ? null
23880
23826
  : String(row["coordinator_cid"]),
23881
23827
  assignedRunner: row["assigned_runner"] === null || row["assigned_runner"] === undefined ? null : String(row["assigned_runner"]),
23882
- zones: readJsonArray(row["zones"]),
23883
- capabilityRequirements: readJsonArray(row["capability_requirements"]),
23828
+ zones: readStoreTextList("task_ref.zones", row["zones"]),
23829
+ capabilityRequirements: readStoreTextList("task_ref.capability_requirements", row["capability_requirements"]),
23884
23830
  parkRate: Number(row["park_rate"]),
23885
23831
  plan: row["plan"] === null || row["plan"] === undefined
23886
23832
  ? null
@@ -23968,9 +23914,9 @@ function readRoutine(row) {
23968
23914
  repo: String(row["repo"]),
23969
23915
  goal: String(row["goal"]),
23970
23916
  outOfScope: row["out_of_scope"] === null ? null : String(row["out_of_scope"]),
23971
- touches: readJsonArray(row["touches"]),
23972
- acceptance: readAcceptance(row["acceptance_json"]),
23973
- requirements: readJsonArray(row["requirements"]),
23917
+ touches: readStoreTextList("routine.touches", row["touches"]),
23918
+ acceptance: readAcceptance("routine.acceptance_json", row["acceptance_json"]),
23919
+ requirements: readStoreTextList("routine.requirements", row["requirements"]),
23974
23920
  schedule: String(row["schedule"]),
23975
23921
  singleFlight: Number(row["single_flight"]) === 1,
23976
23922
  costCeilingUsd: row["cost_ceiling_usd"] === null ? null : Number(row["cost_ceiling_usd"]),
@@ -24004,7 +23950,7 @@ function readTournamentTerms(row) {
24004
23950
  active: Number(row["active"]) === 1,
24005
23951
  kind: String(row["kind"] ?? "race") === "comparison" ? "comparison" : "race",
24006
23952
  raceDigest: String(row["race_digest"]),
24007
- agents: JSON.parse(String(row["agents"])),
23953
+ agents: parseStoreColumn("tournament_terms.agents", row["agents"]),
24008
23954
  n: Number(row["n"]),
24009
23955
  perAgentBudgetMicrousd: Number(row["per_agent_budget_microusd"]),
24010
23956
  overrunReserveMicrousd: Number(row["overrun_reserve_microusd"]),
@@ -24169,12 +24115,12 @@ function readCoordinatorProposal(row) {
24169
24115
  name: String(row["name"]),
24170
24116
  repo: String(row["repo"]),
24171
24117
  kind: String(row["kind"]),
24172
- payload: JSON.parse(String(row["payload_json"])),
24118
+ payload: parseStoreColumn("coordinator_proposal.payload_json", row["payload_json"]),
24173
24119
  state: String(row["state"]),
24174
24120
  createdAt: String(row["created_at"]),
24175
24121
  resolvedAt: row["resolved_at"] === null ? null : String(row["resolved_at"]),
24176
24122
  resolvedBy: row["resolved_by"] === null ? null : String(row["resolved_by"]),
24177
- outcome: row["outcome_json"] === null ? null : JSON.parse(String(row["outcome_json"])),
24123
+ outcome: row["outcome_json"] === null ? null : parseStoreColumn("coordinator_proposal.outcome_json", row["outcome_json"]),
24178
24124
  };
24179
24125
  }
24180
24126
  function readMateProposal(row) {
@@ -24185,13 +24131,13 @@ function readMateProposal(row) {
24185
24131
  thread: Number(row["thread"]),
24186
24132
  turn: Number(row["turn"]),
24187
24133
  kind: String(row["kind"]),
24188
- payload: JSON.parse(String(row["payload_json"])),
24134
+ payload: parseStoreColumn("mate_proposal.payload_json", row["payload_json"]),
24189
24135
  ceilingDigest: String(row["ceiling_digest"]),
24190
24136
  state: String(row["state"]),
24191
24137
  createdAt: String(row["created_at"]),
24192
24138
  resolvedAt: maybe("resolved_at"),
24193
24139
  resolvedBy: maybe("resolved_by"),
24194
- outcome: outcome === null ? null : JSON.parse(outcome),
24140
+ outcome: outcome === null ? null : parseStoreColumn("mate_proposal.outcome_json", outcome),
24195
24141
  };
24196
24142
  }
24197
24143
  function readMateTurn(row) {
@@ -24261,9 +24207,10 @@ function readMatrixJson(raw) {
24261
24207
  if (raw === null || raw === undefined)
24262
24208
  return [];
24263
24209
  try {
24264
- const parsed = JSON.parse(String(raw));
24265
- if (!Array.isArray(parsed))
24210
+ const read = readStoreColumn("proof_verdict.matrix_json", raw);
24211
+ if (!read.ok)
24266
24212
  return [];
24213
+ const parsed = read.value;
24267
24214
  // `answered` and `review` are additive (post-v39/v40 review findings):
24268
24215
  // a row stored before either fix simply has none, never a throw at
24269
24216
  // render.
@@ -24274,15 +24221,7 @@ function readMatrixJson(raw) {
24274
24221
  }
24275
24222
  }
24276
24223
  function readProofVerdict(row) {
24277
- let reasons = [];
24278
- try {
24279
- const parsed = JSON.parse(String(row["reasons_json"]));
24280
- if (Array.isArray(parsed))
24281
- reasons = parsed.map(one => String(one));
24282
- }
24283
- catch {
24284
- reasons = [];
24285
- }
24224
+ const reasons = readStoreStringifiedList("proof_verdict.reasons_json", row["reasons_json"]);
24286
24225
  return {
24287
24226
  run: Number(row["run"]),
24288
24227
  verdict: String(row["verdict"]),
@@ -24296,10 +24235,10 @@ function readReviewBindingList(raw) {
24296
24235
  if (typeof raw !== "string")
24297
24236
  return [];
24298
24237
  try {
24299
- const parsed = JSON.parse(raw);
24300
- if (!Array.isArray(parsed))
24238
+ const read = readStoreColumn("criterion_review.screenshots_json", raw);
24239
+ if (!read.ok)
24301
24240
  return [];
24302
- return parsed
24241
+ return read.value
24303
24242
  .filter((one) => typeof one === "object" && one !== null)
24304
24243
  .map(one => ({
24305
24244
  artifact: Number(one["artifact"]),
@@ -24388,15 +24327,7 @@ export function revisionTermsOf(sourceRef, sourceScope, coverage, permissionDefa
24388
24327
  };
24389
24328
  }
24390
24329
  function readRepairChain(row) {
24391
- let unresolved = [];
24392
- try {
24393
- const parsed = JSON.parse(String(row["unresolved_json"]));
24394
- if (Array.isArray(parsed))
24395
- unresolved = parsed.map(one => String(one));
24396
- }
24397
- catch {
24398
- unresolved = [];
24399
- }
24330
+ const unresolved = readStoreStringifiedList("repair_chain.unresolved_json", row["unresolved_json"]);
24400
24331
  return {
24401
24332
  id: Number(row["id"]),
24402
24333
  rootTask: String(row["root_task"]),
@@ -24457,11 +24388,11 @@ export function contestantProfileOf(provider, model, repairModel, permissionMode
24457
24388
  /** The stored rubric, re-proved through the same strict parser that admits
24458
24389
  * one on the way in — never trusted bytes back out. Malformed or absent
24459
24390
  * reads back as `[]`, matching every rubric-less scope. */
24460
- function readAcceptance(value) {
24391
+ function readAcceptance(column, value) {
24461
24392
  if (value === null || value === undefined)
24462
24393
  return [];
24463
24394
  try {
24464
- return parseAcceptanceCriteria(JSON.parse(String(value))).criteria;
24395
+ return parseAcceptanceCriteria(parseStoreColumn(column, value)).criteria;
24465
24396
  }
24466
24397
  catch {
24467
24398
  return [];
@@ -24522,7 +24453,7 @@ function readScope(row) {
24522
24453
  taskId: String(row["task_id"]),
24523
24454
  goal: String(row["goal"]),
24524
24455
  outOfScope: row["out_of_scope"] === null ? null : String(row["out_of_scope"]),
24525
- touches: readJsonArray(row["touches"]),
24456
+ touches: readStoreTextList("task_scope.touches", row["touches"]),
24526
24457
  proposedAt: String(row["proposed_at"]),
24527
24458
  digest: String(row["digest"]),
24528
24459
  budgetMicrousd: row["budget_microusd"] === null || row["budget_microusd"] === undefined ? null : Number(row["budget_microusd"]),
@@ -24546,7 +24477,7 @@ function readScope(row) {
24546
24477
  proposedChainJson: row["proposed_chain_json"] === null || row["proposed_chain_json"] === undefined ? null : String(row["proposed_chain_json"]),
24547
24478
  approvedChainJson: row["approved_chain_json"] === null || row["approved_chain_json"] === undefined ? null : String(row["approved_chain_json"]),
24548
24479
  approvalKind: String(row["approval_kind"] ?? "profile") === "chain" ? "chain" : "profile",
24549
- acceptance: readAcceptance(row["acceptance_json"]),
24480
+ acceptance: readAcceptance("task_scope.acceptance_json", row["acceptance_json"]),
24550
24481
  qualityMode: row["quality_mode"] === "strict" ? "strict" : "default",
24551
24482
  riskLevel: isRiskLevel(row["risk_level"]) ? row["risk_level"] : "routine",
24552
24483
  proposedRouteJson: row["proposed_route_json"] === null || row["proposed_route_json"] === undefined ? null : String(row["proposed_route_json"]),
@@ -24561,8 +24492,8 @@ function readRunner(row) {
24561
24492
  name: String(row["name"]),
24562
24493
  host: String(row["host"]),
24563
24494
  capacity: Number(row["capacity"]),
24564
- repos: readJsonArray(row["repos"]),
24565
- agents: readJsonArray(row["agents"]),
24495
+ repos: readStoreTextList("runner.repos", row["repos"]),
24496
+ agents: readStoreTextList("runner.agents", row["agents"]),
24566
24497
  registeredAt: String(row["registered_at"]),
24567
24498
  heartbeatAt: String(row["heartbeat_at"]),
24568
24499
  retiredAt: row["retired_at"] === null ? null : String(row["retired_at"]),
@@ -24655,8 +24586,8 @@ function readGrant(row) {
24655
24586
  return {
24656
24587
  repo: String(row["repo"]),
24657
24588
  backend: String(row["backend"]),
24658
- paths: readJsonArray(row["paths"]),
24659
- mutations: readJsonArray(row["mutations"]),
24589
+ paths: readStoreTextList("backend_grant.paths", row["paths"]),
24590
+ mutations: readStoreTextList("backend_grant.mutations", row["mutations"]),
24660
24591
  selector: String(row["selector"]) === "all" ? "all" : "ours",
24661
24592
  credentialScope: row["credential_scope"] === null ? null : String(row["credential_scope"]),
24662
24593
  observedByGit: Number(row["observed_by_git"]) === 1,
@@ -24672,15 +24603,6 @@ function readGrant(row) {
24672
24603
  dispatchBlockedDetail: row["dispatch_blocked_detail"] === null || row["dispatch_blocked_detail"] === undefined ? null : String(row["dispatch_blocked_detail"]),
24673
24604
  };
24674
24605
  }
24675
- function readJsonArray(value) {
24676
- try {
24677
- const parsed = JSON.parse(String(value));
24678
- return Array.isArray(parsed) ? parsed.filter((one) => typeof one === "string") : [];
24679
- }
24680
- catch {
24681
- return [];
24682
- }
24683
- }
24684
24606
  export function verifiedAuthor(name) {
24685
24607
  return name;
24686
24608
  }