@astrosheep/keiyaku 2.9.9 → 2.9.10
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/README.md +50 -182
- package/build/.tsbuildinfo +1 -1
- package/build/agents/harness/control-types.js +18 -1
- package/build/agents/harness/index.js +2 -0
- package/build/agents/harness/outcome-error.js +81 -0
- package/build/agents/harness/projection.js +2 -1
- package/build/agents/harness/router.js +5 -1
- package/build/agents/providers/claude-agent-sdk/adapter.js +18 -4
- package/build/agents/providers/claude-agent-sdk/registration.js +11 -1
- package/build/agents/providers/claude-agent-sdk/session.js +19 -5
- package/build/agents/providers/codex-app-server/adapter.js +11 -6
- package/build/agents/providers/codex-app-server/events.js +67 -6
- package/build/agents/providers/codex-app-server/registration.js +11 -1
- package/build/agents/providers/opencode-sdk/adapter.js +8 -1
- package/build/agents/providers/opencode-sdk/events.js +24 -2
- package/build/agents/providers/opencode-sdk/registration.js +11 -1
- package/build/agents/providers/pi/checkpoint.js +9 -1
- package/build/agents/providers/pi/events.js +2 -0
- package/build/agents/providers/pi/registration.js +14 -1
- package/build/cli/commands/akuma/akuma/handler.js +1 -1
- package/build/cli/commands/akuma/akuma/meta.js +3 -3
- package/build/cli/commands/akuma/catalog.js +2 -2
- package/build/cli/commands/akuma/{view → show}/handler.js +5 -5
- package/build/cli/commands/akuma/{view → show}/meta.js +3 -3
- package/build/cli/commands/akuma.js +1 -1
- package/build/cli/commands/contract/amend/handler.js +5 -4
- package/build/cli/commands/contract/arc/handler.js +5 -4
- package/build/cli/commands/contract/audit/handler.js +2 -2
- package/build/cli/commands/contract/bind/handler.js +12 -3
- package/build/cli/commands/contract/bind/meta.js +45 -15
- package/build/cli/commands/contract/forfeit/handler.js +9 -5
- package/build/cli/commands/contract/log/handler.js +2 -2
- package/build/cli/commands/contract/petition/handler.js +34 -7
- package/build/cli/commands/contract/renew/handler.js +7 -7
- package/build/cli/commands/contract/renew/meta.js +3 -3
- package/build/cli/commands/metadata.js +10 -6
- package/build/cli/commands/projection/call/meta.js +1 -1
- package/build/cli/commands/projection/catalog.js +2 -0
- package/build/cli/commands/projection/history/handler.js +21 -0
- package/build/cli/commands/projection/history/meta.js +9 -0
- package/build/cli/commands/projection/shared.js +3 -4
- package/build/cli/commands/projection/status/handler.js +3 -3
- package/build/cli/commands/projection/wait/handler.js +20 -11
- package/build/cli/commands/task/add/handler.js +14 -5
- package/build/cli/commands/task/add/meta.js +2 -2
- package/build/cli/commands/task/catalog.js +4 -0
- package/build/cli/commands/task/done/handler.js +1 -4
- package/build/cli/commands/task/done/meta.js +1 -1
- package/build/cli/commands/task/drop/meta.js +1 -1
- package/build/cli/commands/task/hold/handler.js +3 -0
- package/build/cli/commands/task/hold/meta.js +8 -0
- package/build/cli/commands/task/log/handler.js +1 -1
- package/build/cli/commands/task/log/meta.js +2 -2
- package/build/cli/commands/task/resume/handler.js +3 -0
- package/build/cli/commands/task/resume/meta.js +8 -0
- package/build/cli/commands/task/shared.js +39 -14
- package/build/cli/commands/task/show/handler.js +1 -1
- package/build/cli/commands/task/show/meta.js +1 -1
- package/build/cli/commands/task/start/meta.js +1 -1
- package/build/cli/commands/task/stop/handler.js +1 -4
- package/build/cli/commands/task/stop/meta.js +1 -1
- package/build/cli/commands/task/update/handler.js +11 -3
- package/build/cli/commands/task/update/meta.js +3 -3
- package/build/cli/completion.js +3 -2
- package/build/cli/flags.js +51 -14
- package/build/cli/help.js +4 -1
- package/build/cli/index.js +40 -25
- package/build/cli/parse-flags.js +40 -7
- package/build/cli/parse-metadata.js +5 -2
- package/build/cli/parse-selectors.js +2 -3
- package/build/cli/parse.js +40 -6
- package/build/cli/projection-address.js +12 -10
- package/build/cli/render/amendment-warning.js +8 -0
- package/build/cli/render/arc.js +6 -2
- package/build/cli/render/audit.js +400 -81
- package/build/cli/render/kanshi.js +6 -1
- package/build/cli/render/lifecycle.js +56 -0
- package/build/cli/render/misc.js +24 -28
- package/build/cli/render/petition.js +5 -0
- package/build/cli/render/projection-activity.js +3 -2
- package/build/cli/render/projection-history.js +64 -0
- package/build/cli/render/shared.js +23 -9
- package/build/cli/render/status.js +9 -16
- package/build/cli/render/task.js +51 -26
- package/build/cli/render/terminal-failure.js +3 -1
- package/build/cli/render/wait.js +3 -5
- package/build/cli/subagent-guard.js +1 -1
- package/build/cli/types.js +1 -1
- package/build/config/env-keys.js +2 -0
- package/build/config/env.js +23 -0
- package/build/config/settings/schema.js +3 -1
- package/build/core/address-carrier.js +89 -0
- package/build/core/addressing.js +255 -196
- package/build/core/amend.js +115 -38
- package/build/core/amendment-criteria.js +17 -0
- package/build/core/amendment-warning.js +10 -0
- package/build/core/arc.js +184 -83
- package/build/core/audit/candidate.js +110 -9
- package/build/core/audit/coordinates.js +120 -65
- package/build/core/audit/evidence.js +19 -23
- package/build/core/audit/facade.js +34 -11
- package/build/core/audit/report.js +5 -5
- package/build/core/bind-reconciliation.js +390 -0
- package/build/core/bind-workspace.js +12 -20
- package/build/core/bind.js +242 -273
- package/build/core/call/call.js +1 -0
- package/build/core/call/context.js +46 -71
- package/build/core/call/prompt.js +6 -3
- package/build/core/context.js +49 -35
- package/build/core/contract-carrier-runtime.js +408 -0
- package/build/core/contract-carrier.js +270 -0
- package/build/core/contract-view.js +33 -27
- package/build/core/contract.js +5 -1
- package/build/core/derived-replay.js +207 -0
- package/build/core/draft.js +125 -57
- package/build/core/forfeit.js +113 -56
- package/build/core/identifier-slug.js +63 -13
- package/build/core/ids.js +4 -4
- package/build/core/lifecycle-history.js +35 -0
- package/build/core/lifecycle-recovery.js +117 -0
- package/build/core/lifecycle-runner.js +377 -0
- package/build/core/log.js +37 -11
- package/build/core/outcome-base.js +181 -0
- package/build/core/process-group.js +139 -0
- package/build/core/projection/generation/database.js +82 -7
- package/build/core/projection/generation/model.js +9 -36
- package/build/core/projection/generation/projection-generation-continuation.js +10 -22
- package/build/core/projection/generation/projection-generation-execution.js +6 -5
- package/build/core/projection/index.js +5 -4
- package/build/core/projection/projection-activity.js +27 -8
- package/build/core/projection/projection-alias.js +23 -10
- package/build/core/projection/projection-core.js +21 -7
- package/build/core/projection/projection-execution-observer.js +91 -39
- package/build/core/projection/projection-history.js +474 -0
- package/build/core/projection/projection-runner-lock.js +12 -0
- package/build/core/projection/projection-status-observation.js +365 -0
- package/build/core/projection/projection-status.js +21 -358
- package/build/core/projection/projection-terminal-failure.js +13 -8
- package/build/core/projection/projection-wait.js +7 -2
- package/build/core/projection/tell/launch-store.js +244 -0
- package/build/core/projection/tell/model.js +3 -3
- package/build/core/projection/tell/store.js +70 -118
- package/build/core/registry.js +22 -87
- package/build/core/renew-build.js +424 -0
- package/build/core/renew-plan.js +239 -0
- package/build/core/renew-rewrite.js +77 -0
- package/build/core/renew.js +295 -520
- package/build/core/repository-ledger/accepted-fold-read.js +19 -0
- package/build/core/repository-ledger/accepted-tail.js +116 -0
- package/build/core/repository-ledger/admission.js +38 -0
- package/build/core/repository-ledger/authoritative-transaction.js +1 -0
- package/build/core/repository-ledger/claim-evidence.js +147 -0
- package/build/core/repository-ledger/codec.js +297 -0
- package/build/core/repository-ledger/current-state-store.js +853 -0
- package/build/core/repository-ledger/fold-repository.js +375 -0
- package/build/core/repository-ledger/fold.js +248 -0
- package/build/core/repository-ledger/git-object-store.js +243 -0
- package/build/core/repository-ledger/history-read.js +15 -0
- package/build/core/repository-ledger/incremental-read.js +153 -0
- package/build/core/repository-ledger/index.js +15 -0
- package/build/core/repository-ledger/inventory-plan.js +75 -0
- package/build/core/repository-ledger/inventory.js +310 -0
- package/build/core/repository-ledger/publication-admission.js +125 -0
- package/build/core/repository-ledger/publication.js +353 -0
- package/build/core/repository-ledger/read-model.js +469 -0
- package/build/core/repository-ledger/transaction-membership.js +78 -0
- package/build/core/repository-ledger/traversal.js +404 -0
- package/build/core/repository-ledger/write-transaction.js +339 -0
- package/build/core/seal.js +168 -161
- package/build/core/settlement/claim-delivery.js +42 -215
- package/build/core/settlement/claim.js +235 -153
- package/build/core/settlement/index.js +4 -4
- package/build/core/settlement/petition-claim-gates.js +52 -33
- package/build/core/settlement/petition-forfeit.js +12 -5
- package/build/core/settlement/petition-head-guard.js +8 -5
- package/build/core/settlement/petition-preview.js +68 -56
- package/build/core/settlement/petition-window.js +50 -0
- package/build/core/settlement/petition.js +252 -131
- package/build/core/settlement/queue-read-model.js +72 -95
- package/build/core/settlement/queue-seat-allocation.js +35 -126
- package/build/core/settlement/queue.js +1 -1
- package/build/core/settlement/settlement.js +172 -80
- package/build/core/settlement/verdict.js +112 -66
- package/build/core/settlement/verification-coordination.js +305 -0
- package/build/core/settlement/verification-supervisor.js +275 -0
- package/build/core/settlement/verification.js +398 -31
- package/build/core/status/board.js +114 -119
- package/build/core/status/lifecycle.js +7 -80
- package/build/core/structured-query.js +23 -0
- package/build/core/target-ref.js +1 -1
- package/build/core/task/board.js +59 -24
- package/build/core/task/commands.js +181 -33
- package/build/core/task/coordinate.js +56 -0
- package/build/core/task/document.js +25 -9
- package/build/core/task/index.js +7 -4
- package/build/core/task/query.js +17 -27
- package/build/core/task/settlement-git.js +164 -179
- package/build/core/task/settlement-policy.js +15 -11
- package/build/core/task/source-board.js +31 -13
- package/build/core/task/task-bind-preparation.js +193 -0
- package/build/core/task/task-contract.js +102 -193
- package/build/core/task/task-file-transaction.js +171 -0
- package/build/core/task/task-git-runtime.js +76 -14
- package/build/core/task/task-git-store.js +68 -169
- package/build/core/task/task-store-repository.js +13 -6
- package/build/core/task/task-worktree.js +6 -33
- package/build/core/task/task.js +1 -1
- package/build/core/task/tree.js +3 -3
- package/build/core/task/validation.js +4 -3
- package/build/core/transcripts.js +93 -1
- package/build/core/worktree-bootstrap.js +44 -36
- package/build/core/worktree-path.js +210 -77
- package/build/flow-error.js +7 -7
- package/build/generated/version.js +2 -2
- package/build/git/core.js +76 -13
- package/build/git/refs.js +168 -46
- package/build/git/streaming-batch.js +73 -13
- package/package.json +1 -1
- package/skills/keiyaku/SKILL.md +1 -1
- package/skills/keiyaku-akuma/SKILL.md +3 -3
- package/skills/keiyaku-task/SKILL.md +4 -2
- package/skills/keiyaku-workflow/SKILL.md +88 -203
- package/build/core/entry.js +0 -305
- package/build/core/ledger-batch.js +0 -194
- package/build/core/ledger.js +0 -84
- package/build/core/projection/leash.js +0 -87
- package/build/core/ref-log.js +0 -119
- package/build/core/renew-session.js +0 -128
- package/build/core/status/ledger-batch.js +0 -1
- package/build/core/status/reconciliation.js +0 -137
|
@@ -1,126 +1,103 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import { commissionTarget } from "../status/drift.js";
|
|
4
|
-
export function isCurrentPetitionData(data) {
|
|
5
|
-
return "target" in data && "seat" in data;
|
|
6
|
-
}
|
|
7
|
-
function latestPetition(entries) {
|
|
8
|
-
for (let index = entries.length - 1; index >= 0; index -= 1) {
|
|
9
|
-
const entry = entries[index];
|
|
10
|
-
if (entry.kind === "petition")
|
|
11
|
-
return entry;
|
|
12
|
-
}
|
|
13
|
-
return undefined;
|
|
14
|
-
}
|
|
15
|
-
export function deriveQueueReadModel(ledgers) {
|
|
1
|
+
import { lookupCurrentContract, readRepositoryLifecycle, } from "../repository-ledger/read-model.js";
|
|
2
|
+
export function deriveQueueReadModel(read) {
|
|
16
3
|
const seatsByContract = new Map();
|
|
17
4
|
const diagnosticsByContract = new Map();
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
5
|
+
const candidatesByTarget = new Map();
|
|
6
|
+
const occupancyByTarget = new Map();
|
|
7
|
+
for (const contract of read.isolatedContracts) {
|
|
8
|
+
diagnosticsByContract.set(contract, [{
|
|
9
|
+
kind: "lifecycle-disease",
|
|
10
|
+
status: "isolated",
|
|
11
|
+
diagnostics: read.diagnostics.filter((diagnostic) => diagnostic.contract === contract),
|
|
12
|
+
}]);
|
|
13
|
+
}
|
|
14
|
+
for (const model of read.contracts) {
|
|
15
|
+
// Every accepted petition occupies its seat, even if a later transition
|
|
16
|
+
// diseased the contract and removed it from settlement ordering.
|
|
17
|
+
for (const { objectId, entry } of model.history) {
|
|
18
|
+
if (entry.kind !== "petition")
|
|
19
|
+
continue;
|
|
20
|
+
const occupancy = {
|
|
21
|
+
contract: model.contract,
|
|
22
|
+
target: entry.data.target,
|
|
23
|
+
seat: entry.data.seat,
|
|
24
|
+
objectId,
|
|
36
25
|
};
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
petitioned: petition.data.target,
|
|
41
|
-
derived: derived.ref,
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
const bucket = orderedByTarget.get(petition.data.target) ?? [];
|
|
45
|
-
bucket.push(seat);
|
|
46
|
-
orderedByTarget.set(petition.data.target, bucket);
|
|
26
|
+
const occupancies = occupancyByTarget.get(occupancy.target) ?? [];
|
|
27
|
+
occupancies.push(occupancy);
|
|
28
|
+
occupancyByTarget.set(occupancy.target, occupancies);
|
|
47
29
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
30
|
+
// A diseased contract remains visible to status and occupies its persisted
|
|
31
|
+
// seat, but cannot participate in settlement ordering.
|
|
32
|
+
const outcome = lookupCurrentContract(read, model.contract);
|
|
33
|
+
if (outcome.status === "diseased") {
|
|
34
|
+
diagnosticsByContract.set(model.contract, [{
|
|
35
|
+
kind: "lifecycle-disease",
|
|
36
|
+
status: "diseased",
|
|
37
|
+
diagnostics: outcome.diagnostics,
|
|
38
|
+
}]);
|
|
39
|
+
continue;
|
|
56
40
|
}
|
|
57
|
-
|
|
58
|
-
|
|
41
|
+
if (outcome.status !== "healthy")
|
|
42
|
+
continue;
|
|
43
|
+
if (model.phase !== "awaiting-verdict" && model.phase !== "approved")
|
|
44
|
+
continue;
|
|
45
|
+
if (model.petition.state === "none")
|
|
46
|
+
continue;
|
|
47
|
+
const seat = {
|
|
48
|
+
contract: model.contract,
|
|
49
|
+
target: model.petition.target,
|
|
50
|
+
seat: model.petition.seat,
|
|
51
|
+
};
|
|
52
|
+
const candidates = candidatesByTarget.get(seat.target) ?? [];
|
|
53
|
+
candidates.push({ seat, petitionObjectId: model.petition.objectId });
|
|
54
|
+
candidatesByTarget.set(seat.target, candidates);
|
|
55
|
+
seatsByContract.set(model.contract, seat);
|
|
56
|
+
diagnosticsByContract.set(model.contract, []);
|
|
59
57
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
for (const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
bySeat.set(seat.seat, group);
|
|
68
|
-
}
|
|
69
|
-
for (const [seatNumber, group] of bySeat) {
|
|
70
|
-
if (group.length < 2)
|
|
58
|
+
const orderedByTarget = new Map();
|
|
59
|
+
for (const [target, candidates] of candidatesByTarget) {
|
|
60
|
+
for (const candidate of candidates) {
|
|
61
|
+
const collisions = (occupancyByTarget.get(target) ?? [])
|
|
62
|
+
.filter((occupancy) => occupancy.seat === candidate.seat.seat && occupancy.objectId !== candidate.petitionObjectId)
|
|
63
|
+
.sort((left, right) => left.contract.localeCompare(right.contract) || left.objectId.localeCompare(right.objectId));
|
|
64
|
+
if (collisions.length === 0)
|
|
71
65
|
continue;
|
|
72
|
-
const
|
|
73
|
-
for (const
|
|
74
|
-
|
|
75
|
-
const existing = diagnosticsByContract.get(member.contract) ?? [];
|
|
76
|
-
for (const other of others) {
|
|
77
|
-
existing.push({ kind: "duplicate-seat", seat: seatNumber, with: other.contract });
|
|
78
|
-
}
|
|
79
|
-
diagnosticsByContract.set(member.contract, existing);
|
|
66
|
+
const diagnostics = diagnosticsByContract.get(candidate.seat.contract);
|
|
67
|
+
for (const collision of collisions) {
|
|
68
|
+
diagnostics.push({ kind: "duplicate-seat", seat: candidate.seat.seat, with: collision.contract });
|
|
80
69
|
}
|
|
81
70
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
71
|
+
// A duplicated persisted seat is a resource collision, not a deterministic
|
|
72
|
+
// tie-break. Keep the affected petitions visible with diagnostics, but do
|
|
73
|
+
// not let either one participate in settlement ordering.
|
|
74
|
+
const ordered = candidates.map((candidate) => candidate.seat).filter((candidate) => !diagnosticsByContract.get(candidate.contract)?.some((diagnostic) => diagnostic.kind === "duplicate-seat"));
|
|
75
|
+
ordered.sort((a, b) => {
|
|
76
|
+
if (a.seat !== b.seat)
|
|
77
|
+
return a.seat - b.seat;
|
|
87
78
|
return a.contract.localeCompare(b.contract);
|
|
88
79
|
});
|
|
89
|
-
orderedByTarget.set(target,
|
|
80
|
+
orderedByTarget.set(target, ordered);
|
|
90
81
|
}
|
|
91
82
|
const targetQueues = [...orderedByTarget.entries()]
|
|
92
83
|
.sort(([a], [b]) => a.localeCompare(b))
|
|
93
84
|
.map(([target, ordered]) => ({ target, ordered }));
|
|
94
85
|
return {
|
|
86
|
+
observedLedgerHead: read.observedLedgerHead,
|
|
95
87
|
targetQueues,
|
|
96
88
|
seatsByContract,
|
|
97
89
|
diagnosticsByContract,
|
|
90
|
+
unattributedDiagnostics: read.diagnostics.filter((diagnostic) => diagnostic.contract === undefined),
|
|
98
91
|
};
|
|
99
92
|
}
|
|
100
93
|
export async function readQueueReadModel(cwd) {
|
|
101
|
-
|
|
102
|
-
const ledgers = [];
|
|
103
|
-
for (const id of contractIds) {
|
|
104
|
-
const ledger = await readLedger(cwd, id);
|
|
105
|
-
if (ledger)
|
|
106
|
-
ledgers.push(ledger);
|
|
107
|
-
}
|
|
108
|
-
return deriveQueueReadModel(ledgers);
|
|
94
|
+
return deriveQueueReadModel(await readRepositoryLifecycle(cwd));
|
|
109
95
|
}
|
|
110
96
|
export function queueMembership(model, contractId) {
|
|
111
97
|
const seat = model.seatsByContract.get(contractId);
|
|
112
98
|
if (!seat)
|
|
113
99
|
return undefined;
|
|
114
100
|
const diagnostics = model.diagnosticsByContract.get(contractId) ?? [];
|
|
115
|
-
if (seat.legacy) {
|
|
116
|
-
return {
|
|
117
|
-
seat,
|
|
118
|
-
position: null,
|
|
119
|
-
queueLength: 0,
|
|
120
|
-
queueHead: null,
|
|
121
|
-
diagnostics,
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
101
|
const targetQueue = model.targetQueues.find((queue) => queue.target === seat.target);
|
|
125
102
|
if (!targetQueue) {
|
|
126
103
|
return {
|
|
@@ -1,103 +1,51 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
1
|
import { FlowError } from "../../flow-error.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { isCurrentPetitionData, queueMembership, } from "./queue-read-model.js";
|
|
6
|
-
const ISO_UTC_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/;
|
|
2
|
+
import { readRepositoryLifecycle } from "../repository-ledger/read-model.js";
|
|
3
|
+
import { queueMembership, } from "./queue-read-model.js";
|
|
7
4
|
function isPositiveSafeInteger(value) {
|
|
8
5
|
return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
|
|
9
6
|
}
|
|
10
7
|
export function assertClaimQueueHead(model, contractId) {
|
|
8
|
+
const lifecycleDisease = model.diagnosticsByContract.get(contractId)?.find((diagnostic) => diagnostic.kind === "lifecycle-disease");
|
|
9
|
+
if (lifecycleDisease?.kind === "lifecycle-disease") {
|
|
10
|
+
throw new FlowError("INTERNAL_STATE", `contract ${contractId} is unavailable because its repository ledger history is ${lifecycleDisease.status}`, {
|
|
11
|
+
facts: {
|
|
12
|
+
kind: "repository_lifecycle_disease",
|
|
13
|
+
contractId,
|
|
14
|
+
status: lifecycleDisease.status,
|
|
15
|
+
diagnostics: lifecycleDisease.diagnostics.map((diagnostic) => ({ ...diagnostic })),
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
}
|
|
11
19
|
const membership = queueMembership(model, contractId);
|
|
12
20
|
if (!membership) {
|
|
21
|
+
if (model.unattributedDiagnostics.length > 0) {
|
|
22
|
+
throw new FlowError("INTERNAL_STATE", `contract ${contractId} queue membership is indeterminate because the repository ledger is diseased`, {
|
|
23
|
+
facts: {
|
|
24
|
+
kind: "repository_lifecycle_disease",
|
|
25
|
+
contractId,
|
|
26
|
+
status: "indeterminate",
|
|
27
|
+
diagnostics: model.unattributedDiagnostics.map((diagnostic) => ({ ...diagnostic })),
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
}
|
|
13
31
|
throw new FlowError("INTERNAL_STATE", `contract ${contractId} is not queue head; position=not queued`);
|
|
14
32
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const mismatch = membership.diagnostics.find((diagnostic) => diagnostic.kind === "target-mismatch");
|
|
19
|
-
if (mismatch && mismatch.kind === "target-mismatch") {
|
|
20
|
-
throw new FlowError("QUEUE_TARGET_MISMATCH", `contract ${contractId} petition target ${mismatch.petitioned} disagrees with derived target ${mismatch.derived}; reject its petition and petition again`);
|
|
33
|
+
const collision = membership.diagnostics.find((diagnostic) => diagnostic.kind === "duplicate-seat");
|
|
34
|
+
if (collision && collision.kind === "duplicate-seat") {
|
|
35
|
+
throw new FlowError("INTERNAL_STATE", `contract ${contractId} has duplicate settlement seat ${collision.seat} with ${collision.with}; reject its petition and petition again`);
|
|
21
36
|
}
|
|
22
37
|
if (membership.position !== 1) {
|
|
23
38
|
throw new FlowError("INTERNAL_STATE", `contract ${contractId} is not queue head; position=${membership.position ?? "not queued"}`);
|
|
24
39
|
}
|
|
25
40
|
return membership;
|
|
26
41
|
}
|
|
27
|
-
export function seatAllocatorRef(target) {
|
|
28
|
-
const digest = createHash("sha256").update(target, "utf8").digest("hex");
|
|
29
|
-
return `refs/keiyaku/seat/${digest}`;
|
|
30
|
-
}
|
|
31
|
-
function parseSeatAllocatorMessage(message, expectedTarget) {
|
|
32
|
-
const normalized = message.replace(/\r\n/g, "\n");
|
|
33
|
-
const separatorIndex = normalized.indexOf("\n\n");
|
|
34
|
-
if (separatorIndex < 0)
|
|
35
|
-
throw new Error("missing blank line");
|
|
36
|
-
const subject = normalized.slice(0, separatorIndex).trim();
|
|
37
|
-
const body = normalized.slice(separatorIndex + 2).trim();
|
|
38
|
-
const parsed = JSON.parse(body);
|
|
39
|
-
const keys = Object.keys(parsed).sort();
|
|
40
|
-
if (keys.join(",") !== "actor,at,seat,target,v")
|
|
41
|
-
throw new Error("unexpected fields");
|
|
42
|
-
if (parsed.v !== 1)
|
|
43
|
-
throw new Error("bad version");
|
|
44
|
-
if (typeof parsed.target !== "string" || parsed.target !== expectedTarget)
|
|
45
|
-
throw new Error("target mismatch");
|
|
46
|
-
if (!isPositiveSafeInteger(parsed.seat))
|
|
47
|
-
throw new Error("bad seat");
|
|
48
|
-
if (typeof parsed.at !== "string" || !ISO_UTC_RE.test(parsed.at))
|
|
49
|
-
throw new Error("bad at");
|
|
50
|
-
if (typeof parsed.actor !== "string" || parsed.actor.trim().length === 0)
|
|
51
|
-
throw new Error("bad actor");
|
|
52
|
-
if (subject !== `keiyaku seat: ${parsed.seat}`)
|
|
53
|
-
throw new Error("bad subject");
|
|
54
|
-
return {
|
|
55
|
-
v: 1,
|
|
56
|
-
target: parsed.target,
|
|
57
|
-
seat: parsed.seat,
|
|
58
|
-
at: parsed.at,
|
|
59
|
-
actor: parsed.actor,
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
async function parseAllocatorChain(cwd, head, expectedTarget) {
|
|
63
|
-
// Intentional degrade: any rev-list or parse failure treats the chain as corrupt.
|
|
64
|
-
let commits;
|
|
65
|
-
try {
|
|
66
|
-
commits = await listRefLogCommits(cwd, head);
|
|
67
|
-
}
|
|
68
|
-
catch {
|
|
69
|
-
return null;
|
|
70
|
-
}
|
|
71
|
-
if (commits.length === 0)
|
|
72
|
-
return null;
|
|
73
|
-
const entries = [];
|
|
74
|
-
let previousSeat = 0;
|
|
75
|
-
for (const commit of commits) {
|
|
76
|
-
try {
|
|
77
|
-
const entry = parseSeatAllocatorMessage(await readRefLogCommitMessage(cwd, commit), expectedTarget);
|
|
78
|
-
if (entry.seat <= previousSeat)
|
|
79
|
-
return null;
|
|
80
|
-
previousSeat = entry.seat;
|
|
81
|
-
entries.push(entry);
|
|
82
|
-
}
|
|
83
|
-
catch {
|
|
84
|
-
return null;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return { entries, lastSeat: previousSeat };
|
|
88
|
-
}
|
|
89
42
|
async function ledgerSeatFloor(cwd, target) {
|
|
90
|
-
const
|
|
43
|
+
const lifecycle = await readRepositoryLifecycle(cwd);
|
|
91
44
|
let maxSeat = 0;
|
|
92
|
-
for (const
|
|
93
|
-
const
|
|
94
|
-
if (!ledger)
|
|
95
|
-
continue;
|
|
96
|
-
for (const entry of ledger.entries) {
|
|
45
|
+
for (const model of lifecycle.contracts) {
|
|
46
|
+
for (const { entry } of model.history) {
|
|
97
47
|
if (entry.kind !== "petition")
|
|
98
48
|
continue;
|
|
99
|
-
if (!isCurrentPetitionData(entry.data))
|
|
100
|
-
continue;
|
|
101
49
|
if (entry.data.target !== target)
|
|
102
50
|
continue;
|
|
103
51
|
if (entry.data.seat > maxSeat)
|
|
@@ -107,50 +55,11 @@ async function ledgerSeatFloor(cwd, target) {
|
|
|
107
55
|
return maxSeat;
|
|
108
56
|
}
|
|
109
57
|
export async function allocateSettlementSeat(cwd, input) {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const observedHead = await resolveRefLogHead(cwd, ref, { peel: "object" });
|
|
116
|
-
const floor = await ledgerSeatFloor(cwd, input.target);
|
|
117
|
-
const parsed = observedHead ? await parseAllocatorChain(cwd, observedHead, input.target) : null;
|
|
118
|
-
const healthy = parsed !== null && parsed.lastSeat >= floor;
|
|
119
|
-
let seat;
|
|
120
|
-
let parent;
|
|
121
|
-
let expectedOld;
|
|
122
|
-
if (healthy && parsed) {
|
|
123
|
-
seat = parsed.lastSeat + 1;
|
|
124
|
-
parent = observedHead;
|
|
125
|
-
expectedOld = observedHead;
|
|
126
|
-
}
|
|
127
|
-
else {
|
|
128
|
-
// Missing, parse failure, target mismatch, non-monotonic, or stale-behind-ledger:
|
|
129
|
-
// mint a detached no-parent root at floor+1. Leave the bad chain for Git GC.
|
|
130
|
-
seat = floor + 1;
|
|
131
|
-
parent = null;
|
|
132
|
-
expectedOld = observedHead;
|
|
133
|
-
}
|
|
134
|
-
if (!isPositiveSafeInteger(seat)) {
|
|
135
|
-
throw new FlowError("INTERNAL_STATE", `settlement seats exhausted for ${input.target}`);
|
|
136
|
-
}
|
|
137
|
-
const entry = {
|
|
138
|
-
v: 1,
|
|
139
|
-
target: input.target,
|
|
140
|
-
seat,
|
|
141
|
-
at: input.at,
|
|
142
|
-
actor: input.actor,
|
|
143
|
-
};
|
|
144
|
-
const newHead = await createRefLogCommit(cwd, {
|
|
145
|
-
subject: `keiyaku seat: ${entry.seat}`,
|
|
146
|
-
body: JSON.stringify(entry, null, 2),
|
|
147
|
-
parentHead: parent,
|
|
148
|
-
errorLabel: `seat ${entry.seat}`,
|
|
149
|
-
});
|
|
150
|
-
// Intentional degrade: CAS failure returns false; bounded retry re-reads head.
|
|
151
|
-
const casOk = await casUpdateRefLog(cwd, ref, newHead, expectedOld);
|
|
152
|
-
if (casOk)
|
|
153
|
-
return { head: newHead, seat };
|
|
58
|
+
// The candidate is derived from one typed ledger observation. The later
|
|
59
|
+
// repository-ledger transaction owns the only authoritative CAS and commit.
|
|
60
|
+
const seat = (await ledgerSeatFloor(cwd, input.target)) + 1;
|
|
61
|
+
if (!isPositiveSafeInteger(seat)) {
|
|
62
|
+
throw new FlowError("INTERNAL_STATE", `settlement seats exhausted for ${input.target}`);
|
|
154
63
|
}
|
|
155
|
-
|
|
64
|
+
return { seat };
|
|
156
65
|
}
|
|
@@ -3,4 +3,4 @@
|
|
|
3
3
|
* Implementation lives in sibling files; this module preserves the historical import path.
|
|
4
4
|
*/
|
|
5
5
|
export { deriveQueueReadModel, queueMembership, readQueueReadModel, } from "./queue-read-model.js";
|
|
6
|
-
export { allocateSettlementSeat, assertClaimQueueHead,
|
|
6
|
+
export { allocateSettlementSeat, assertClaimQueueHead, } from "./queue-seat-allocation.js";
|