loreforge 0.1.0-alpha.1

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 (77) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +87 -0
  3. package/dist/cli/args.js +287 -0
  4. package/dist/cli/args.js.map +1 -0
  5. package/dist/cli/detect.js +94 -0
  6. package/dist/cli/detect.js.map +1 -0
  7. package/dist/cli/home.js +36 -0
  8. package/dist/cli/home.js.map +1 -0
  9. package/dist/cli/init.js +309 -0
  10. package/dist/cli/init.js.map +1 -0
  11. package/dist/cli/input.js +72 -0
  12. package/dist/cli/input.js.map +1 -0
  13. package/dist/cli/instructions.js +83 -0
  14. package/dist/cli/instructions.js.map +1 -0
  15. package/dist/cli/main.js +250 -0
  16. package/dist/cli/main.js.map +1 -0
  17. package/dist/cli/output.js +108 -0
  18. package/dist/cli/output.js.map +1 -0
  19. package/dist/context/render.js +297 -0
  20. package/dist/context/render.js.map +1 -0
  21. package/dist/core/contracts.js +49 -0
  22. package/dist/core/contracts.js.map +1 -0
  23. package/dist/core/dispatch.js +109 -0
  24. package/dist/core/dispatch.js.map +1 -0
  25. package/dist/core/errors.js +36 -0
  26. package/dist/core/errors.js.map +1 -0
  27. package/dist/core/index.js +40 -0
  28. package/dist/core/index.js.map +1 -0
  29. package/dist/core/operations/claims.js +91 -0
  30. package/dist/core/operations/claims.js.map +1 -0
  31. package/dist/core/operations/common.js +108 -0
  32. package/dist/core/operations/common.js.map +1 -0
  33. package/dist/core/operations/context.js +130 -0
  34. package/dist/core/operations/context.js.map +1 -0
  35. package/dist/core/operations/decisions.js +44 -0
  36. package/dist/core/operations/decisions.js.map +1 -0
  37. package/dist/core/operations/handoffs.js +147 -0
  38. package/dist/core/operations/handoffs.js.map +1 -0
  39. package/dist/core/operations/messages.js +117 -0
  40. package/dist/core/operations/messages.js.map +1 -0
  41. package/dist/core/operations/register.js +51 -0
  42. package/dist/core/operations/register.js.map +1 -0
  43. package/dist/core/operations/tasks.js +54 -0
  44. package/dist/core/operations/tasks.js.map +1 -0
  45. package/dist/core/schemas/common.js +81 -0
  46. package/dist/core/schemas/common.js.map +1 -0
  47. package/dist/core/schemas/context.js +10 -0
  48. package/dist/core/schemas/context.js.map +1 -0
  49. package/dist/core/schemas/decisions.js +11 -0
  50. package/dist/core/schemas/decisions.js.map +1 -0
  51. package/dist/core/schemas/handoffs.js +38 -0
  52. package/dist/core/schemas/handoffs.js.map +1 -0
  53. package/dist/core/schemas/questions.js +22 -0
  54. package/dist/core/schemas/questions.js.map +1 -0
  55. package/dist/core/schemas/registration.js +16 -0
  56. package/dist/core/schemas/registration.js.map +1 -0
  57. package/dist/core/schemas/request.js +103 -0
  58. package/dist/core/schemas/request.js.map +1 -0
  59. package/dist/core/schemas/tasks.js +35 -0
  60. package/dist/core/schemas/tasks.js.map +1 -0
  61. package/dist/evidence/git.js +119 -0
  62. package/dist/evidence/git.js.map +1 -0
  63. package/dist/integrations/index.js +32 -0
  64. package/dist/integrations/index.js.map +1 -0
  65. package/dist/storage/db.js +239 -0
  66. package/dist/storage/db.js.map +1 -0
  67. package/dist/storage/faults.js +12 -0
  68. package/dist/storage/faults.js.map +1 -0
  69. package/dist/storage/receipts.js +82 -0
  70. package/dist/storage/receipts.js.map +1 -0
  71. package/dist/storage/schema.js +119 -0
  72. package/dist/storage/schema.js.map +1 -0
  73. package/docs/integrations.md +71 -0
  74. package/docs/usage.md +405 -0
  75. package/package.json +54 -0
  76. package/templates/instructions.template.md +42 -0
  77. package/templates/session-rule.md +34 -0
@@ -0,0 +1,91 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { OpError } from "../errors.js";
3
+ import { requireAgent, requireProject, toTask, withMutation, } from "./common.js";
4
+ // Attempt-scoped leases: exactly one live owner per task, fixed 2h lease,
5
+ // injected clock only (tests); no daemon, heartbeats, or scheduler.
6
+ export const LEASE_MS = 2 * 60 * 60 * 1000;
7
+ function loadTask(db, projectId, taskId) {
8
+ const row = db.prepare("SELECT * FROM tasks WHERE id = ? AND project_id = ?").get(taskId, projectId);
9
+ if (!row)
10
+ throw OpError.notFound("task not found");
11
+ return row;
12
+ }
13
+ // A claim is live only while the task is running, owned, and unexpired. At
14
+ // exact expiry (leaseUntil <= now) the token is already invalid.
15
+ function isLive(row, nowMs) {
16
+ return (row.status === "running" &&
17
+ row.owner_id !== null &&
18
+ row.claim_token !== null &&
19
+ row.lease_until !== null &&
20
+ row.lease_until > nowMs);
21
+ }
22
+ function matchesClaim(row, actorId, claimToken, nowMs) {
23
+ return (isLive(row, nowMs) && row.owner_id === actorId && row.claim_token === claimToken);
24
+ }
25
+ function dependenciesCompleted(db, taskId) {
26
+ const rows = db
27
+ .prepare("SELECT t.status FROM task_dependencies d JOIN tasks t ON t.id = d.depends_on_task_id WHERE d.task_id = ?")
28
+ .all(taskId);
29
+ return rows.every((row) => row.status === "completed");
30
+ }
31
+ export function claimTask(db, now, request) {
32
+ return withMutation(db, now, request, (nowMs) => {
33
+ requireProject(db, request.projectId);
34
+ requireAgent(db, request.actorId);
35
+ const row = loadTask(db, request.projectId, request.payload.taskId);
36
+ if (row.status === "completed" || row.status === "cancelled" || row.status === "blocked") {
37
+ throw OpError.conflict(`task is ${row.status} and cannot be claimed`);
38
+ }
39
+ if (isLive(row, nowMs)) {
40
+ throw OpError.conflict("task is already claimed");
41
+ }
42
+ if (!dependenciesCompleted(db, row.id)) {
43
+ throw OpError.conflict("task dependencies are not completed");
44
+ }
45
+ const claimToken = randomBytes(32).toString("hex");
46
+ const leaseUntil = nowMs + LEASE_MS;
47
+ db.prepare("UPDATE tasks SET status = 'running', owner_id = ?, claim_token = ?, attempt = attempt + 1, lease_until = ?, updated_at = ? WHERE id = ?").run(request.actorId, claimToken, leaseUntil, nowMs, row.id);
48
+ const updated = loadTask(db, request.projectId, row.id);
49
+ return { task: toTask(db, updated), claimToken };
50
+ });
51
+ }
52
+ export function renewTask(db, now, request) {
53
+ return withMutation(db, now, request, (nowMs) => {
54
+ requireProject(db, request.projectId);
55
+ requireAgent(db, request.actorId);
56
+ const row = loadTask(db, request.projectId, request.payload.taskId);
57
+ if (!matchesClaim(row, request.actorId, request.payload.claimToken, nowMs)) {
58
+ throw OpError.stale("claim token is expired, replaced, or does not match");
59
+ }
60
+ db.prepare("UPDATE tasks SET lease_until = ?, updated_at = ? WHERE id = ?").run(nowMs + LEASE_MS, nowMs, row.id);
61
+ return { task: toTask(db, loadTask(db, request.projectId, row.id)) };
62
+ });
63
+ }
64
+ export function releaseTask(db, now, request) {
65
+ return withMutation(db, now, request, (nowMs) => {
66
+ requireProject(db, request.projectId);
67
+ requireAgent(db, request.actorId);
68
+ const row = loadTask(db, request.projectId, request.payload.taskId);
69
+ if (!matchesClaim(row, request.actorId, request.payload.claimToken, nowMs)) {
70
+ throw OpError.stale("claim token is expired, replaced, or does not match");
71
+ }
72
+ db.prepare("UPDATE tasks SET status = 'open', owner_id = NULL, claim_token = NULL, lease_until = NULL, updated_at = ? WHERE id = ?").run(nowMs, row.id);
73
+ return { task: toTask(db, loadTask(db, request.projectId, row.id)) };
74
+ });
75
+ }
76
+ export function cancelTask(db, now, request) {
77
+ return withMutation(db, now, request, (nowMs) => {
78
+ requireProject(db, request.projectId);
79
+ requireAgent(db, request.actorId);
80
+ const row = loadTask(db, request.projectId, request.payload.taskId);
81
+ // Any registered actor can cancel; coordination, not authorization. New
82
+ // cancellation of a terminal task is CONFLICT; the exact retry of a
83
+ // recorded cancellation still replays success via receipts.
84
+ if (row.status === "completed" || row.status === "cancelled") {
85
+ throw OpError.conflict(`task is already ${row.status}`);
86
+ }
87
+ db.prepare("UPDATE tasks SET status = 'cancelled', owner_id = NULL, claim_token = NULL, lease_until = NULL, updated_at = ? WHERE id = ?").run(nowMs, row.id);
88
+ return { task: toTask(db, loadTask(db, request.projectId, row.id)) };
89
+ });
90
+ }
91
+ //# sourceMappingURL=claims.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claims.js","sourceRoot":"","sources":["../../../src/core/operations/claims.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,108 @@
1
+ import { OpError } from "../errors.js";
2
+ import { withWrite } from "../../storage/db.js";
3
+ import { ReceiptClashError, canonicalHash, findReceipt, isReceiptPkClash, readReceipt, receiptIdentity, storeReceipt, } from "../../storage/receipts.js";
4
+ export function toIso(ms) {
5
+ return new Date(ms).toISOString();
6
+ }
7
+ export function toProject(row) {
8
+ return {
9
+ id: row.id,
10
+ name: row.name,
11
+ root: row.root,
12
+ gitCommonDir: row.git_common_dir,
13
+ createdAt: toIso(row.created_at),
14
+ };
15
+ }
16
+ export function toAgent(row) {
17
+ return { id: row.id, displayName: row.display_name, createdAt: toIso(row.created_at) };
18
+ }
19
+ export function loadDependsOn(db, taskId) {
20
+ const rows = db
21
+ .prepare("SELECT depends_on_task_id FROM task_dependencies WHERE task_id = ?")
22
+ .all(taskId);
23
+ return rows.map((row) => row.depends_on_task_id);
24
+ }
25
+ export function toTask(db, row) {
26
+ return {
27
+ id: row.id,
28
+ projectId: row.project_id,
29
+ title: row.title,
30
+ description: row.description,
31
+ status: row.status,
32
+ dependsOn: loadDependsOn(db, row.id),
33
+ ownerId: row.owner_id,
34
+ attempt: row.attempt,
35
+ leaseUntil: row.lease_until === null ? null : toIso(row.lease_until),
36
+ createdAt: toIso(row.created_at),
37
+ updatedAt: toIso(row.updated_at),
38
+ };
39
+ }
40
+ // Every project-scoped operation starts here: unknown projects (and any ID
41
+ // looked up under the wrong project) are NOT_FOUND without revealing whether
42
+ // the entity exists elsewhere.
43
+ export function requireProject(db, projectId) {
44
+ const row = db.prepare("SELECT * FROM projects WHERE id = ?").get(projectId);
45
+ if (!row)
46
+ throw OpError.notFound("project not found");
47
+ return row;
48
+ }
49
+ export function requireAgent(db, actorId) {
50
+ const row = db.prepare("SELECT * FROM agents WHERE id = ?").get(actorId);
51
+ if (!row)
52
+ throw OpError.notFound("agent not found");
53
+ return row;
54
+ }
55
+ // Transactional core shared by every mutation: recheck, produce, and receipt
56
+ // insert in one BEGIN IMMEDIATE transaction. If our receipt insert loses a
57
+ // cross-process race, the transaction rolls back and retries once; the fresh
58
+ // read then replays the winner's response for identical retries or reports
59
+ // CONFLICT for a reused key.
60
+ //
61
+ // The clock is sampled only after the write transaction is acquired, inside
62
+ // it: a mutation that waits behind another process's lock must judge leases
63
+ // and stamp rows with the acquisition time, never the dispatch time.
64
+ export function transactMutation(db, now, request, produce) {
65
+ for (let attempt = 0;; attempt += 1) {
66
+ try {
67
+ return withWrite(db, () => {
68
+ const again = readReceipt(db, request);
69
+ if (again !== undefined)
70
+ return again;
71
+ const nowMs = now();
72
+ const data = produce(nowMs);
73
+ try {
74
+ storeReceipt(db, request, data, nowMs);
75
+ }
76
+ catch (error) {
77
+ if (isReceiptPkClash(error))
78
+ throw new ReceiptClashError();
79
+ throw error;
80
+ }
81
+ return data;
82
+ });
83
+ }
84
+ catch (error) {
85
+ if (!(error instanceof ReceiptClashError))
86
+ throw error;
87
+ if (attempt === 0)
88
+ continue;
89
+ const identity = receiptIdentity(request);
90
+ const row = findReceipt(db, identity.scope, identity.actorKey, request.requestId);
91
+ if (row &&
92
+ row.operation === request.operation &&
93
+ row.request_hash === canonicalHash(request)) {
94
+ return JSON.parse(row.response_json);
95
+ }
96
+ throw OpError.conflict("requestId was already used with a different operation or payload");
97
+ }
98
+ }
99
+ }
100
+ // Receipt-first mutation wrapper for handlers with no outside-transaction
101
+ // work: early replay check, then transactMutation.
102
+ export function withMutation(db, now, request, produce) {
103
+ const early = readReceipt(db, request);
104
+ if (early !== undefined)
105
+ return early;
106
+ return transactMutation(db, now, request, produce);
107
+ }
108
+ //# sourceMappingURL=common.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"common.js","sourceRoot":"","sources":["../../../src/core/operations/common.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,130 @@
1
+ import { OpError } from "../errors.js";
2
+ import { observeCurrentGit } from "../../evidence/git.js";
3
+ import { withRead } from "../../storage/db.js";
4
+ import { requireProject, toProject, toTask } from "./common.js";
5
+ import { toDecision } from "./decisions.js";
6
+ import { toHandoff } from "./handoffs.js";
7
+ import { toQuestion } from "./messages.js";
8
+ const SELECTION_CAP = 20;
9
+ function placeholders(count) {
10
+ return count === 0 ? "" : new Array(count).fill("?").join(",");
11
+ }
12
+ function toReviewHandoff(handoff) {
13
+ const { summary: _summary, unresolved: _unresolved, nextSteps: _nextSteps, ...rest } = handoff;
14
+ return rest;
15
+ }
16
+ function joinRowToQuestion(row) {
17
+ const { answer_question_id, answer_id, answer_actor_id, answer_body, answer_created_at, ...base } = row;
18
+ const answer = answer_question_id === null ||
19
+ answer_id === null ||
20
+ answer_actor_id === null ||
21
+ answer_body === null ||
22
+ answer_created_at === null
23
+ ? undefined
24
+ : {
25
+ question_id: answer_question_id,
26
+ id: answer_id,
27
+ actor_id: answer_actor_id,
28
+ body: answer_body,
29
+ created_at: answer_created_at,
30
+ };
31
+ return toQuestion(base, answer);
32
+ }
33
+ export function getContext(db, nowMs, request) {
34
+ const project = requireProject(db, request.projectId);
35
+ // Git observation happens outside the read transaction: never hold DB locks
36
+ // during Git. A missing/unreadable checkout yields nulls plus an error.
37
+ const currentGit = observeCurrentGit(project.root, nowMs);
38
+ const mode = request.payload.mode;
39
+ const snapshot = withRead(db, () => {
40
+ const taskRow = db.prepare("SELECT * FROM tasks WHERE id = ? AND project_id = ?").get(request.payload.taskId, request.projectId);
41
+ if (!taskRow)
42
+ throw OpError.notFound("task not found");
43
+ const task = toTask(db, taskRow);
44
+ const depRows = db
45
+ .prepare("SELECT t.* FROM task_dependencies d JOIN tasks t ON t.id = d.depends_on_task_id WHERE d.task_id = ? LIMIT ?")
46
+ .all(taskRow.id, SELECTION_CAP);
47
+ const dependencies = depRows.map((row) => toTask(db, row));
48
+ const scopeIds = [taskRow.id, ...depRows.map((row) => row.id)];
49
+ // Handoffs for the task and its direct dependencies, newest first.
50
+ const handoffScope = placeholders(scopeIds.length);
51
+ const handoffTotal = db
52
+ .prepare(`SELECT COUNT(*) AS n FROM handoffs WHERE task_id IN (${handoffScope})`)
53
+ .get(...scopeIds).n;
54
+ const handoffRows = db
55
+ .prepare(`SELECT * FROM handoffs WHERE task_id IN (${handoffScope}) ORDER BY created_at DESC, id ASC LIMIT ?`)
56
+ .all(...scopeIds, SELECTION_CAP);
57
+ const handoffs = handoffRows.map((row) => mode === "review" ? toReviewHandoff(toHandoff(row)) : toHandoff(row));
58
+ // Questions for the current task only. Review mode omits every question
59
+ // body from the snapshot and reports the policy count instead.
60
+ const questionTotal = db.prepare("SELECT COUNT(*) AS n FROM questions WHERE task_id = ?").get(taskRow.id).n;
61
+ let questions = [];
62
+ let omittedQuestions = 0;
63
+ let policyQuestions = 0;
64
+ if (mode === "review") {
65
+ policyQuestions = questionTotal;
66
+ }
67
+ else {
68
+ const questionRows = db
69
+ .prepare(`SELECT q.*, a.question_id AS answer_question_id, a.id AS answer_id, a.actor_id AS answer_actor_id, a.body AS answer_body, a.created_at AS answer_created_at
70
+ FROM questions q LEFT JOIN answers a ON a.question_id = q.id
71
+ WHERE q.task_id = ?
72
+ ORDER BY CASE WHEN a.question_id IS NULL THEN 0 ELSE 1 END, q.created_at DESC, q.id ASC
73
+ LIMIT ?`)
74
+ .all(taskRow.id, SELECTION_CAP);
75
+ questions = questionRows.map(joinRowToQuestion);
76
+ omittedQuestions = Math.max(0, questionTotal - questions.length);
77
+ }
78
+ // Active project-wide decisions plus active decisions whose paths exactly
79
+ // match a reported file path on a selected handoff. Newest first.
80
+ const filePaths = new Set();
81
+ for (const handoff of handoffs) {
82
+ const full = handoff;
83
+ if (!full.evidence || !Array.isArray(full.evidence.files))
84
+ continue;
85
+ for (const file of full.evidence.files)
86
+ filePaths.add(file.path);
87
+ }
88
+ const wideTotal = db
89
+ .prepare("SELECT COUNT(*) AS n FROM decisions WHERE project_id = ? AND superseded_by_id IS NULL AND paths_json = '[]'")
90
+ .get(request.projectId).n;
91
+ const wideRows = db
92
+ .prepare("SELECT * FROM decisions WHERE project_id = ? AND superseded_by_id IS NULL AND paths_json = '[]' ORDER BY created_at DESC, id ASC LIMIT ?")
93
+ .all(request.projectId, SELECTION_CAP);
94
+ let pathTotal = 0;
95
+ let pathRows = [];
96
+ if (filePaths.size > 0) {
97
+ const paths = [...filePaths];
98
+ const list = placeholders(paths.length);
99
+ pathTotal = db
100
+ .prepare(`SELECT COUNT(*) AS n FROM decisions d WHERE d.project_id = ? AND d.superseded_by_id IS NULL AND d.paths_json != '[]' AND EXISTS (SELECT 1 FROM json_each(d.paths_json) j WHERE j.value IN (${list}))`)
101
+ .get(request.projectId, ...paths).n;
102
+ pathRows = db
103
+ .prepare(`SELECT d.* FROM decisions d WHERE d.project_id = ? AND d.superseded_by_id IS NULL AND d.paths_json != '[]' AND EXISTS (SELECT 1 FROM json_each(d.paths_json) j WHERE j.value IN (${list})) ORDER BY d.created_at DESC, d.id ASC LIMIT ?`)
104
+ .all(request.projectId, ...paths, SELECTION_CAP);
105
+ }
106
+ const merged = [...wideRows, ...pathRows].sort((a, b) => a.created_at !== b.created_at ? b.created_at - a.created_at : a.id < b.id ? -1 : 1);
107
+ const selectedDecisions = merged.slice(0, SELECTION_CAP);
108
+ const decisions = selectedDecisions.map(toDecision);
109
+ return {
110
+ project: toProject(project),
111
+ task,
112
+ dependencies,
113
+ handoffs,
114
+ questions,
115
+ decisions,
116
+ currentGit,
117
+ omitted: {
118
+ handoffs: Math.max(0, handoffTotal - handoffs.length),
119
+ questions: omittedQuestions,
120
+ decisions: Math.max(0, wideTotal + pathTotal - decisions.length),
121
+ policy: mode === "review"
122
+ ? { handoffNarratives: true, questions: policyQuestions }
123
+ : { handoffNarratives: false, questions: 0 },
124
+ },
125
+ mode,
126
+ };
127
+ });
128
+ return { snapshot };
129
+ }
130
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../../src/core/operations/context.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { OpError } from "../errors.js";
3
+ import { requireAgent, requireProject, toIso, withMutation } from "./common.js";
4
+ export function toDecision(row) {
5
+ return {
6
+ id: row.id,
7
+ projectId: row.project_id,
8
+ actorId: row.actor_id,
9
+ body: row.body,
10
+ paths: JSON.parse(row.paths_json),
11
+ supersedesId: row.supersedes_id,
12
+ createdAt: toIso(row.created_at),
13
+ };
14
+ }
15
+ export function recordDecision(db, now, request) {
16
+ return withMutation(db, now, request, (nowMs) => {
17
+ requireProject(db, request.projectId);
18
+ requireAgent(db, request.actorId);
19
+ const target = request.payload.supersedesId;
20
+ if (target !== null) {
21
+ const row = db.prepare("SELECT * FROM decisions WHERE id = ?").get(target);
22
+ // Must identify an active decision in the same project: unknown IDs,
23
+ // foreign-project IDs, and already-superseded IDs all conflict so no
24
+ // second supersession can win and no existence oracle leaks.
25
+ if (!row || row.project_id !== request.projectId || row.superseded_by_id !== null) {
26
+ throw OpError.conflict("superseded decision is not active in this project");
27
+ }
28
+ }
29
+ const id = randomUUID();
30
+ db.prepare("INSERT INTO decisions (id, project_id, actor_id, body, paths_json, supersedes_id, superseded_by_id, created_at) VALUES (?, ?, ?, ?, ?, ?, NULL, ?)").run(id, request.projectId, request.actorId, request.payload.body, JSON.stringify(request.payload.paths), target, nowMs);
31
+ if (target !== null) {
32
+ // Atomic second-supersession guard: exactly one writer flips NULL.
33
+ const flipped = db
34
+ .prepare("UPDATE decisions SET superseded_by_id = ? WHERE id = ? AND superseded_by_id IS NULL")
35
+ .run(id, target);
36
+ if (Number(flipped.changes) !== 1) {
37
+ throw OpError.conflict("superseded decision is not active in this project");
38
+ }
39
+ }
40
+ const row = db.prepare("SELECT * FROM decisions WHERE id = ?").get(id);
41
+ return { decision: toDecision(row) };
42
+ });
43
+ }
44
+ //# sourceMappingURL=decisions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decisions.js","sourceRoot":"","sources":["../../../src/core/operations/decisions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGzC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAehF,MAAM,UAAU,UAAU,CAAC,GAAgB;IACzC,OAAO;QACL,EAAE,EAAE,GAAG,CAAC,EAAE;QACV,SAAS,EAAE,GAAG,CAAC,UAAU;QACzB,OAAO,EAAE,GAAG,CAAC,QAAQ;QACrB,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAa;QAC7C,YAAY,EAAE,GAAG,CAAC,aAAa;QAC/B,SAAS,EAAE,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC;KACjC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,cAAc,CAC5B,EAAgB,EAChB,GAAiB,EACjB,OAA8B;IAE9B,OAAO,YAAY,CAAC,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;QAC9C,cAAc,CAAC,EAAE,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;QACtC,YAAY,CAAC,EAAE,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;QAClC,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC;QAC5C,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YACpB,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,sCAAsC,CAAC,CAAC,GAAG,CAAC,MAAM,CAE5D,CAAC;YACd,qEAAqE;YACrE,qEAAqE;YACrE,6DAA6D;YAC7D,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,UAAU,KAAK,OAAO,CAAC,SAAS,IAAI,GAAG,CAAC,gBAAgB,KAAK,IAAI,EAAE,CAAC;gBAClF,MAAM,OAAO,CAAC,QAAQ,CAAC,mDAAmD,CAAC,CAAC;YAC9E,CAAC;QACH,CAAC;QACD,MAAM,EAAE,GAAG,UAAU,EAAE,CAAC;QACxB,EAAE,CAAC,OAAO,CACR,oJAAoJ,CACrJ,CAAC,GAAG,CACH,EAAE,EACF,OAAO,CAAC,SAAS,EACjB,OAAO,CAAC,OAAO,EACf,OAAO,CAAC,OAAO,CAAC,IAAI,EACpB,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,EACrC,MAAM,EACN,KAAK,CACN,CAAC;QACF,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YACpB,mEAAmE;YACnE,MAAM,OAAO,GAAG,EAAE;iBACf,OAAO,CAAC,qFAAqF,CAAC;iBAC9F,GAAG,CAAC,EAAE,EAAE,MAAM,CAAiC,CAAC;YACnD,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;gBAClC,MAAM,OAAO,CAAC,QAAQ,CAAC,mDAAmD,CAAC,CAAC;YAC9E,CAAC;QACH,CAAC;QACD,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,sCAAsC,CAAC,CAAC,GAAG,CAAC,EAAE,CAA2B,CAAC;QACjG,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;IACvC,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,147 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { OpError } from "../errors.js";
3
+ import { discoverProject, observeCheckout } from "../../evidence/git.js";
4
+ import { faultBoundary } from "../../storage/faults.js";
5
+ import { readReceipt } from "../../storage/receipts.js";
6
+ import { requireAgent, requireProject, toIso, toTask, transactMutation, withMutation, } from "./common.js";
7
+ function loadTask(db, projectId, taskId) {
8
+ const row = db.prepare("SELECT * FROM tasks WHERE id = ? AND project_id = ?").get(taskId, projectId);
9
+ if (!row)
10
+ throw OpError.notFound("task not found");
11
+ return row;
12
+ }
13
+ function isLive(row, nowMs) {
14
+ return (row.status === "running" &&
15
+ row.owner_id !== null &&
16
+ row.claim_token !== null &&
17
+ row.lease_until !== null &&
18
+ row.lease_until > nowMs);
19
+ }
20
+ export function toHandoff(row) {
21
+ return {
22
+ id: row.id,
23
+ projectId: row.project_id,
24
+ taskId: row.task_id,
25
+ actorId: row.actor_id,
26
+ attempt: row.attempt,
27
+ outcome: row.outcome,
28
+ summary: row.summary,
29
+ evidence: JSON.parse(row.evidence_json),
30
+ unresolved: JSON.parse(row.unresolved_json),
31
+ nextSteps: JSON.parse(row.next_steps_json),
32
+ blockingQuestionIds: JSON.parse(row.blocking_ids_json),
33
+ observed: {
34
+ checkoutRoot: row.observed_checkout_root,
35
+ head: row.observed_head,
36
+ dirty: row.observed_dirty !== 0,
37
+ collectedAt: toIso(row.observed_collected_at),
38
+ },
39
+ evidenceSource: "agent_reported",
40
+ createdAt: toIso(row.created_at),
41
+ };
42
+ }
43
+ // Blocked handoffs must name nonempty, unique, same-task, still-unanswered
44
+ // questions; completed handoffs must name none. Unanswered optional questions
45
+ // never block completion.
46
+ function checkBlockingQuestions(db, projectId, taskId, outcome, blockingQuestionIds) {
47
+ if (outcome === "completed") {
48
+ if (blockingQuestionIds.length > 0) {
49
+ throw OpError.conflict("completed handoff cannot name blocking questions");
50
+ }
51
+ return;
52
+ }
53
+ if (blockingQuestionIds.length === 0) {
54
+ throw OpError.conflict("blocked handoff requires blocking questions");
55
+ }
56
+ const questionById = db.prepare("SELECT project_id, task_id FROM questions WHERE id = ?");
57
+ const answerByQuestion = db.prepare("SELECT question_id FROM answers WHERE question_id = ?");
58
+ for (const questionId of blockingQuestionIds) {
59
+ const question = questionById.get(questionId);
60
+ if (!question || question.project_id !== projectId || question.task_id !== taskId) {
61
+ throw OpError.conflict("blocked handoff names an unknown or unrelated question");
62
+ }
63
+ if (answerByQuestion.get(questionId) !== undefined) {
64
+ throw OpError.conflict("blocked handoff names an already answered question");
65
+ }
66
+ }
67
+ }
68
+ export function submitHandoff(db, now, request) {
69
+ const early = readReceipt(db, request);
70
+ if (early !== undefined)
71
+ return early;
72
+ // Git observation is collected outside the transaction, then rechecked
73
+ // inside it via the receipt: a retry still succeeds after the checkout
74
+ // moves or disappears because the dispatch-layer receipt check replays the
75
+ // original response before collection runs again.
76
+ const observedAtMs = now();
77
+ const observed = observeCheckout(request.payload.evidence.checkoutRoot, observedAtMs);
78
+ // The checkout must belong to the registered project: same realpath Git
79
+ // common directory, so linked worktrees pass and unrelated repositories or
80
+ // independent clones fail. Identity collection stays outside the
81
+ // transaction with the other Git subprocesses; only the string comparison
82
+ // runs inside it.
83
+ let checkoutCommonDir;
84
+ try {
85
+ checkoutCommonDir = discoverProject(observed.checkoutRoot).gitCommonDir;
86
+ }
87
+ catch {
88
+ throw OpError.conflict("could not verify the checkout belongs to the registered project");
89
+ }
90
+ return transactMutation(db, now, request, (nowMs) => {
91
+ const project = requireProject(db, request.projectId);
92
+ requireAgent(db, request.actorId);
93
+ const row = loadTask(db, request.projectId, request.payload.taskId);
94
+ if (row.status !== "running" ||
95
+ !isLive(row, nowMs) ||
96
+ row.owner_id !== request.actorId ||
97
+ row.claim_token !== request.payload.claimToken) {
98
+ throw OpError.stale("claim token is expired, replaced, or does not match");
99
+ }
100
+ if (checkoutCommonDir !== project.git_common_dir) {
101
+ throw OpError.conflict("checkout is not part of the registered project");
102
+ }
103
+ checkBlockingQuestions(db, request.projectId, row.id, request.payload.outcome, request.payload.blockingQuestionIds);
104
+ // The core never trusts reported Git state: mismatches reject the handoff
105
+ // and leave the task exactly as claimed.
106
+ if (request.payload.evidence.head.toLowerCase() !== observed.head ||
107
+ request.payload.evidence.dirty !== observed.dirty) {
108
+ throw OpError.conflict("reported head/dirty does not match collected observation");
109
+ }
110
+ const nextStatus = request.payload.outcome === "completed" ? "completed" : "blocked";
111
+ faultBoundary("handoff.task");
112
+ db.prepare("UPDATE tasks SET status = ?, owner_id = NULL, claim_token = NULL, lease_until = NULL, updated_at = ? WHERE id = ?").run(nextStatus, nowMs, row.id);
113
+ const handoffId = randomUUID();
114
+ faultBoundary("handoff.row");
115
+ db.prepare("INSERT INTO handoffs (id, project_id, task_id, actor_id, attempt, outcome, summary, evidence_json, unresolved_json, next_steps_json, blocking_ids_json, observed_checkout_root, observed_head, observed_dirty, observed_collected_at, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)").run(handoffId, request.projectId, row.id, request.actorId, row.attempt, request.payload.outcome, request.payload.summary, JSON.stringify(request.payload.evidence), JSON.stringify(request.payload.unresolved), JSON.stringify(request.payload.nextSteps), JSON.stringify(request.payload.blockingQuestionIds), observed.checkoutRoot, observed.head, observed.dirty ? 1 : 0, observedAtMs, nowMs);
116
+ faultBoundary("handoff.receipt");
117
+ const handoffRow = db.prepare("SELECT * FROM handoffs WHERE id = ?").get(handoffId);
118
+ const task = toTask(db, loadTask(db, request.projectId, row.id));
119
+ return { task, handoff: toHandoff(handoffRow) };
120
+ });
121
+ }
122
+ export function reopenTask(db, now, request) {
123
+ return withMutation(db, now, request, (nowMs) => {
124
+ requireProject(db, request.projectId);
125
+ requireAgent(db, request.actorId);
126
+ const row = loadTask(db, request.projectId, request.payload.taskId);
127
+ if (row.status !== "blocked") {
128
+ throw OpError.conflict(`task is ${row.status} and cannot be reopened`);
129
+ }
130
+ const latest = db
131
+ .prepare("SELECT * FROM handoffs WHERE task_id = ? ORDER BY created_at DESC, id ASC LIMIT 1")
132
+ .get(row.id);
133
+ if (!latest || latest.outcome !== "blocked") {
134
+ throw OpError.conflict("blocked task has no blocked handoff to reopen from");
135
+ }
136
+ const blockingIds = JSON.parse(latest.blocking_ids_json);
137
+ const answerByQuestion = db.prepare("SELECT question_id FROM answers WHERE question_id = ?");
138
+ for (const questionId of blockingIds) {
139
+ if (answerByQuestion.get(questionId) === undefined) {
140
+ throw OpError.conflict("blocked task still has unanswered linked questions");
141
+ }
142
+ }
143
+ db.prepare("UPDATE tasks SET status = 'open', updated_at = ? WHERE id = ?").run(nowMs, row.id);
144
+ return { task: toTask(db, loadTask(db, request.projectId, row.id)) };
145
+ });
146
+ }
147
+ //# sourceMappingURL=handoffs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handoffs.js","sourceRoot":"","sources":["../../../src/core/operations/handoffs.ts"],"names":[],"mappings":"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"}