taskforce-loop-engineering 0.10.0 → 0.12.0

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.
@@ -0,0 +1,74 @@
1
+ import assert from 'node:assert/strict';
2
+ import { mkdtemp, readFile } from 'node:fs/promises';
3
+ import { tmpdir } from 'node:os';
4
+ import path from 'node:path';
5
+ import { spawnSync } from 'node:child_process';
6
+ import { fileURLToPath } from 'node:url';
7
+ import { claimAction, markActionUnknown, reserveAction } from '../lib/action-reservations.mjs';
8
+ import { claimTodo, createTodo, decideHandoff, handoffTodo, inspectTodo, recoverTodos, registerAgent, releaseTodo, renewTodo } from '../lib/todo-control-plane.mjs';
9
+
10
+ const root = await mkdtemp(path.join(tmpdir(), 'loop-p2-'));
11
+ const agent = (id, capabilities = ['code'], authority_grants = ['local'], quota = 10) => registerAgent(root, { id, capabilities, authority_grants, quota_grants: { default: quota } });
12
+ const todo = (id, extra = {}) => createTodo(root, { id, title: `Todo ${id}`, required_capabilities: ['code'], authority_class: 'local', acceptance_contract: { checks: ['test'] }, evidence_requirements: ['test-output'], cost: 1, ...extra });
13
+
14
+ await Promise.all([agent('alpha'), agent('beta'), agent('observer', []), agent('poor', ['code'], ['local'], 0)]);
15
+ await todo('race');
16
+ const race = await Promise.all([claimTodo(root, { todoId: 'race', agentId: 'alpha', leaseMs: 1000 }), claimTodo(root, { todoId: 'race', agentId: 'beta', leaseMs: 1000 })]);
17
+ assert.equal(race.filter((item) => item.claimed).length, 1, 'exactly one agent wins an atomic claim');
18
+ const winner = race.find((item) => item.claimed);
19
+ const loser = race.find((item) => !item.claimed);
20
+ assert.match(loser.reason, /state:claimed/);
21
+ await assert.rejects(() => renewTodo(root, { todoId: 'race', agentId: loser === race[0] ? 'alpha' : 'beta', fencingToken: winner.fencing_token }), /Stale or invalid/);
22
+
23
+ await todo('capability');
24
+ assert.equal((await claimTodo(root, { todoId: 'capability', agentId: 'observer' })).reason, 'capability_mismatch');
25
+ await todo('quota');
26
+ assert.equal((await claimTodo(root, { todoId: 'quota', agentId: 'poor' })).reason, 'quota_exhausted');
27
+
28
+ await todo('dependency');
29
+ await todo('dependent', { dependencies: ['dependency'], priority: 100 });
30
+ const blocked = await claimTodo(root, { todoId: 'dependent', agentId: 'alpha' });
31
+ assert.match(blocked.reason, /dependencies/);
32
+ const dependencyClaim = await claimTodo(root, { todoId: 'dependency', agentId: 'alpha' });
33
+ await releaseTodo(root, { todoId: 'dependency', agentId: 'alpha', fencingToken: dependencyClaim.fencing_token, completed: true, evidence: 'passed' });
34
+ assert.equal((await claimTodo(root, { todoId: 'dependent', agentId: 'alpha' })).claimed, true, 'completion unlocks dependencies');
35
+
36
+ await todo('stale');
37
+ const stale = await claimTodo(root, { todoId: 'stale', agentId: 'alpha', leaseMs: 1 });
38
+ await new Promise((resolve) => setTimeout(resolve, 5));
39
+ await assert.rejects(() => renewTodo(root, { todoId: 'stale', agentId: 'alpha', fencingToken: stale.fencing_token }), /expired/);
40
+ assert.equal((await recoverTodos(root)).results.find((item) => item.todo_id === 'stale').outcome, 'runnable');
41
+ const reclaimed = await claimTodo(root, { todoId: 'stale', agentId: 'beta' });
42
+ assert.ok(reclaimed.fencing_token > stale.fencing_token, 'recovered work gets a higher fencing token');
43
+
44
+ await todo('handoff');
45
+ const owned = await claimTodo(root, { todoId: 'handoff', agentId: 'alpha' });
46
+ const packet = await handoffTodo(root, { todoId: 'handoff', agentId: 'alpha', targetAgentId: 'beta', fencingToken: owned.fencing_token });
47
+ assert.deepEqual(packet.idempotency_keys, []);
48
+ const cli = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../bin/loop-engineering.mjs');
49
+ const restarted = spawnSync(process.execPath, [cli, 'todo-inspect', '--todo-id', 'handoff', '--root', root], { encoding: 'utf8' });
50
+ assert.equal(restarted.status, 0);
51
+ assert.equal(JSON.parse(restarted.stdout).state, 'handoff_pending', 'handoff survives a fresh CLI process');
52
+ const accepted = await decideHandoff(root, { handoffId: packet.id, agentId: 'beta', accept: true });
53
+ assert.equal(accepted.todo.claim.owner, 'beta');
54
+ assert.ok(accepted.todo.claim.fencing_token > owned.fencing_token);
55
+ await todo('handoff-reject');
56
+ const rejectOwned = await claimTodo(root, { todoId: 'handoff-reject', agentId: 'alpha' });
57
+ const rejectPacket = await handoffTodo(root, { todoId: 'handoff-reject', agentId: 'alpha', targetAgentId: 'beta', fencingToken: rejectOwned.fencing_token });
58
+ const rejected = await decideHandoff(root, { handoffId: rejectPacket.id, agentId: 'beta', accept: false, reason: 'busy' });
59
+ assert.equal(rejected.todo.claim.owner, 'alpha');
60
+
61
+ await todo('parked', { parked: { state: 'waiting_for_human' } });
62
+ assert.equal((await claimTodo(root, { todoId: 'parked', agentId: 'alpha' })).reason, 'parked_human_gate');
63
+
64
+ await reserveAction(root, { idempotencyKey: 'side-effect', kind: 'external_message', authorizationScope: 'todo:external', request: { body: 'once' } });
65
+ const action = await claimAction(root, { idempotencyKey: 'side-effect', owner: 'alpha', leaseMs: 1000 });
66
+ await markActionUnknown(root, { idempotencyKey: 'side-effect', fencingToken: action.fencingToken, reason: 'worker_crash' });
67
+ await todo('unknown-action', { idempotency_keys: ['side-effect'], authorization: { scope: 'todo:external', grant: 'preserved' } });
68
+ assert.match((await claimTodo(root, { todoId: 'unknown-action', agentId: 'beta' })).reason, /action_reconciliation/);
69
+ assert.equal((await inspectTodo(root, 'unknown-action')).authorization.grant, 'preserved');
70
+
71
+ const audit = await readFile(path.join(root, 'runtime', 'loops', 'control-plane', 'audit.jsonl'), 'utf8');
72
+ assert.match(audit, /todo_claimed/);
73
+ assert.match(audit, /handoff_accepted/);
74
+ console.log(JSON.stringify({ ok: true, root, assertions: ['atomic claim', 'capability', 'dependency', 'quota', 'lease fencing', 'orphan recovery', 'handoff accept/reject', 'parked gate', 'unknown action reconciliation', 'audit'] }));
@@ -0,0 +1 @@
1
+ {"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://loop-engineering.local/schema/operator-projection-v1.json","title":"Loop Engineering read-only operator projection v1","type":"object","required":["schema_version","generated_at","source","health","overview","projects","queues","todos","agents","handoffs","gates","actions","cost"],"properties":{"schema_version":{"const":"1.0.0"},"generated_at":{"type":"string","format":"date-time"},"source":{"type":"object"},"health":{"type":"object"},"overview":{"type":"object"},"projects":{"type":"array"},"queues":{"type":"array"},"todos":{"type":"array"},"agents":{"type":"array"},"handoffs":{"type":"array"},"gates":{"type":"array"},"actions":{"type":"array"},"cost":{"type":"object"}},"additionalProperties":false}
@@ -0,0 +1,28 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://loop-engineering.local/schema/todo-v2.json",
4
+ "title": "Loop Engineering typed todo v2",
5
+ "type": "object",
6
+ "required": ["id", "title", "priority", "risk", "authority_class", "required_capabilities", "acceptance_contract", "evidence_requirements", "cost_envelope", "state"],
7
+ "properties": {
8
+ "id": { "type": "string", "pattern": "^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,199}$" },
9
+ "title": { "type": "string", "minLength": 1 },
10
+ "project_id": { "type": ["string", "null"] },
11
+ "dependencies": { "type": "array", "uniqueItems": true, "items": { "type": "string" } },
12
+ "priority": { "type": "number" },
13
+ "risk": { "enum": ["low", "medium", "high", "critical"] },
14
+ "authority_class": { "type": "string", "minLength": 1 },
15
+ "required_capabilities": { "type": "array", "uniqueItems": true, "items": { "type": "string" } },
16
+ "acceptance_contract": { "type": "object" },
17
+ "evidence_requirements": { "type": "array", "minItems": 1, "uniqueItems": true, "items": { "type": "string" } },
18
+ "cost_envelope": { "type": "object", "required": ["quota", "amount"], "properties": { "quota": { "type": "string" }, "amount": { "type": "number", "minimum": 0 } } },
19
+ "state": { "enum": ["runnable", "blocked", "claimed", "handoff_pending", "completed"] },
20
+ "authorization": {},
21
+ "idempotency_keys": { "type": "array", "uniqueItems": true, "items": { "type": "string" } },
22
+ "lineage": { "type": "object" },
23
+ "context": { "type": "object" },
24
+ "evidence": { "type": "array" },
25
+ "claim": { "type": ["object", "null"] }
26
+ },
27
+ "additionalProperties": true
28
+ }