@astrosheep/keiyaku 2.9.8 → 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 +10 -7
- 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 +242 -79
- 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 +65 -29
- package/build/cli/render/projection-history.js +64 -0
- package/build/cli/render/shared.js +23 -9
- package/build/cli/render/status.js +13 -35
- package/build/cli/render/task.js +51 -26
- package/build/cli/render/terminal-failure.js +3 -1
- package/build/cli/render/wait.js +45 -69
- package/build/cli/subagent-guard.js +1 -1
- package/build/cli/types.js +1 -1
- package/build/config/env-keys.js +3 -1
- package/build/config/env.js +24 -1
- 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 +13 -6
- package/build/core/projection/index.js +5 -4
- package/build/core/projection/projection-activity.js +29 -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 +35 -337
- 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 +125 -127
- 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,14 +1,19 @@
|
|
|
1
1
|
import { availableParallelism } from "node:os";
|
|
2
|
+
import * as fs from "node:fs/promises";
|
|
3
|
+
import * as path from "node:path";
|
|
2
4
|
import { loadKeiyakuSettings } from "../../config/settings/loader.js";
|
|
3
5
|
import { observeGitRepository } from "../../git/branches.js";
|
|
6
|
+
import { currentLifecycleState } from "../repository-ledger/read-model.js";
|
|
7
|
+
import { parseCarrierIdentity } from "../contract-carrier.js";
|
|
8
|
+
import { KEIYAKU_FILE } from "../../keiyaku.js";
|
|
9
|
+
import { lookupCurrentContract, } from "../repository-ledger/read-model.js";
|
|
4
10
|
import { readExactProjectionStatus, readOperationalProjectionStatusBoard, } from "../projection/index.js";
|
|
5
11
|
import { deriveQueueReadModel, queueMembership, } from "../settlement/index.js";
|
|
6
|
-
import { findOpenArc, getMaxArc, receiptsAfterFence } from "../seal.js";
|
|
7
12
|
import { openTaskStore, readTaskBoard, taskRepositoryFromStore, taskUpdatedAt, } from "../task/index.js";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
13
|
+
import { replayBlockingFact, } from "./lifecycle.js";
|
|
14
|
+
import { findRegisteredContractWorktreePathFromModel, stableRepoRoot } from "../worktree-path.js";
|
|
15
|
+
import { evaluateBaseDrift, resolveDefaultBranchFromSettings, } from "./drift.js";
|
|
16
|
+
import { readRepositoryLifecycleSummaries as readStatusLifecycle } from "../repository-ledger/read-model.js";
|
|
12
17
|
// Git object reads benefit from overlap, but a hard cap avoids process and disk contention.
|
|
13
18
|
const STATUS_DRIFT_MAX_CONCURRENCY = 8;
|
|
14
19
|
const STATUS_DRIFT_CONCURRENCY = Math.min(STATUS_DRIFT_MAX_CONCURRENCY, availableParallelism());
|
|
@@ -24,13 +29,12 @@ function timestampAge(timestamp, now) {
|
|
|
24
29
|
return Number.isFinite(value) ? Math.max(0, now - value) : 0;
|
|
25
30
|
}
|
|
26
31
|
export function deriveTaskStatusSummary(board, now) {
|
|
27
|
-
const eligible = board.tasks.filter((task) => !task.contractId && (task.state === "open" || task.state === "in_progress"));
|
|
32
|
+
const eligible = board.tasks.filter((task) => !task.contractId && (task.state === "open" || task.state === "in_progress" || task.state === "on_hold"));
|
|
28
33
|
const inProgress = eligible.filter((task) => task.state === "in_progress").length;
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
.slice(0, 5)
|
|
34
|
+
const onHold = eligible.filter((task) => task.state === "on_hold").length;
|
|
35
|
+
const topTasks = eligible
|
|
36
|
+
.filter((task) => task.state === "in_progress" || task.state === "on_hold")
|
|
37
|
+
.sort(compareTaskStatusRows)
|
|
34
38
|
.map((task) => {
|
|
35
39
|
const updatedAt = taskUpdatedAt(task);
|
|
36
40
|
return {
|
|
@@ -47,14 +51,13 @@ export function deriveTaskStatusSummary(board, now) {
|
|
|
47
51
|
});
|
|
48
52
|
const ready = eligible.filter((task) => task.state === "open" && !task.blocked).length;
|
|
49
53
|
const blocked = eligible.filter((task) => task.state === "open" && task.blocked).length;
|
|
50
|
-
if (board.tasks.length === 0 && board.diseases.length === 0)
|
|
51
|
-
return undefined;
|
|
52
54
|
return {
|
|
53
|
-
total:
|
|
55
|
+
total: eligible.length,
|
|
54
56
|
inProgress,
|
|
57
|
+
onHold,
|
|
55
58
|
ready,
|
|
56
59
|
blocked,
|
|
57
|
-
omitted:
|
|
60
|
+
omitted: ready + blocked,
|
|
58
61
|
topTasks,
|
|
59
62
|
diseases: board.diseases,
|
|
60
63
|
};
|
|
@@ -68,6 +71,7 @@ async function readTaskStatusSummary(cwd, now) {
|
|
|
68
71
|
return {
|
|
69
72
|
total: 0,
|
|
70
73
|
inProgress: 0,
|
|
74
|
+
onHold: 0,
|
|
71
75
|
ready: 0,
|
|
72
76
|
blocked: 0,
|
|
73
77
|
omitted: 0,
|
|
@@ -81,10 +85,7 @@ function isQueueHeadStall(input) {
|
|
|
81
85
|
return (input.queuePosition === 1 &&
|
|
82
86
|
input.queueLength > 1 &&
|
|
83
87
|
input.state.state === "petitioned" &&
|
|
84
|
-
!
|
|
85
|
-
}
|
|
86
|
-
function findBindEntry(entries) {
|
|
87
|
-
return entries.find((entry) => entry.kind === "bind");
|
|
88
|
+
!input.hasVerdict);
|
|
88
89
|
}
|
|
89
90
|
function contractSlugFromId(id) {
|
|
90
91
|
const dot = id.indexOf(".");
|
|
@@ -92,44 +93,25 @@ function contractSlugFromId(id) {
|
|
|
92
93
|
return id.slice(0, dot);
|
|
93
94
|
return id;
|
|
94
95
|
}
|
|
95
|
-
function deriveCurrentArc(
|
|
96
|
-
// No explicit arc has ever existed: derive a0 from bind objective.
|
|
97
|
-
if (getMaxArc(entries) === 0) {
|
|
98
|
-
const a0Open = lifecycle.state === "bound" || lifecycle.state === "active";
|
|
99
|
-
return {
|
|
100
|
-
number: 0,
|
|
101
|
-
objective: bindObjective ?? "",
|
|
102
|
-
state: a0Open ? "open" : "closed",
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
const arcObjectives = new Map();
|
|
106
|
-
let currentArc = null;
|
|
107
|
-
for (const entry of entries) {
|
|
108
|
-
if (entry.kind === "arc") {
|
|
109
|
-
arcObjectives.set(entry.data.arc, entry.data.objective);
|
|
110
|
-
currentArc = {
|
|
111
|
-
number: entry.data.arc,
|
|
112
|
-
objective: entry.data.objective,
|
|
113
|
-
state: "open",
|
|
114
|
-
};
|
|
115
|
-
continue;
|
|
116
|
-
}
|
|
117
|
-
if (entry.kind === "seal" && entry.data.arc > 0) {
|
|
118
|
-
currentArc = {
|
|
119
|
-
number: entry.data.arc,
|
|
120
|
-
objective: arcObjectives.get(entry.data.arc) ?? bindObjective ?? "",
|
|
121
|
-
state: "closed",
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
if (currentArc)
|
|
126
|
-
return currentArc;
|
|
96
|
+
function deriveCurrentArc(model) {
|
|
127
97
|
return {
|
|
128
|
-
number:
|
|
129
|
-
objective:
|
|
130
|
-
state: "closed",
|
|
98
|
+
number: model.intent.arc,
|
|
99
|
+
objective: model.intent.objective,
|
|
100
|
+
state: model.phase === "active" ? "open" : "closed",
|
|
131
101
|
};
|
|
132
102
|
}
|
|
103
|
+
function bindTitle(model) {
|
|
104
|
+
const bind = model.history.find(({ entry }) => entry.kind === "bind")?.entry;
|
|
105
|
+
return bind?.kind === "bind" ? bind.data.title : model.intent.title;
|
|
106
|
+
}
|
|
107
|
+
function lifecycleDiseaseReason(read, contract) {
|
|
108
|
+
return read.diagnostics.filter((diagnostic) => diagnostic.contract === contract).map((diagnostic) => diagnostic.detail).join("; ");
|
|
109
|
+
}
|
|
110
|
+
function bootstrapReplayBlockers(model) {
|
|
111
|
+
return model.bootstrapReplay.state === "pending"
|
|
112
|
+
? [replayBlockingFact(model.contract, "bootstrap")]
|
|
113
|
+
: [];
|
|
114
|
+
}
|
|
133
115
|
function deriveNextStep(input) {
|
|
134
116
|
if (input.state.state === "corrupt") {
|
|
135
117
|
return { kind: "inspect-log", detail: "inspect log" };
|
|
@@ -156,8 +138,8 @@ function deriveNextStep(input) {
|
|
|
156
138
|
}
|
|
157
139
|
return { kind: "arc", detail: "arc" };
|
|
158
140
|
}
|
|
159
|
-
function deriveLastActiveAge(
|
|
160
|
-
const last =
|
|
141
|
+
function deriveLastActiveAge(model, now) {
|
|
142
|
+
const last = model.history.at(-1)?.entry;
|
|
161
143
|
if (!last)
|
|
162
144
|
return {};
|
|
163
145
|
const atMs = Date.parse(last.at);
|
|
@@ -199,14 +181,16 @@ export async function readKanshiBoard(cwd, now, opts) {
|
|
|
199
181
|
: await readExactProjectionStatus(cwd, now, opts.projectionId);
|
|
200
182
|
return {
|
|
201
183
|
contracts: [],
|
|
184
|
+
contractsObservable: false,
|
|
185
|
+
commissionPlaces: new Map(),
|
|
202
186
|
projections: opts?.projectionId === undefined
|
|
203
187
|
? await readOperationalProjectionStatusBoard(cwd, now)
|
|
204
188
|
: exactProjection ? { rows: [exactProjection] } : null,
|
|
205
189
|
queueLength: 0,
|
|
206
190
|
diseases: loadedSettings.diseases,
|
|
207
|
-
claimReconciliation: null,
|
|
208
191
|
targetFilter,
|
|
209
|
-
|
|
192
|
+
// Task and contract boards are repository organs; a non-repository cwd
|
|
193
|
+
// cannot truthfully turn their absence into a zero count.
|
|
210
194
|
};
|
|
211
195
|
}
|
|
212
196
|
// Status is a pure reader: use the same resolver, but retain its ability to
|
|
@@ -218,107 +202,121 @@ export async function readKanshiBoard(cwd, now, opts) {
|
|
|
218
202
|
catch {
|
|
219
203
|
defaultBranch = null;
|
|
220
204
|
}
|
|
221
|
-
const
|
|
222
|
-
const
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
terminalById.set(ledger.contractId, isTerminalState(deriveContractState(ledger.entries)));
|
|
229
|
-
}
|
|
230
|
-
const queueModel = deriveQueueReadModel(readableLedgers);
|
|
205
|
+
const lifecycle = await readStatusLifecycle(cwd);
|
|
206
|
+
const stableRoot = await stableRepoRoot(cwd);
|
|
207
|
+
const terminalById = new Map(lifecycle.contracts.map((model) => [
|
|
208
|
+
model.contract,
|
|
209
|
+
model.phase === "claimed" || model.phase === "forfeited",
|
|
210
|
+
]));
|
|
211
|
+
const queueModel = deriveQueueReadModel(lifecycle);
|
|
231
212
|
const boardQueueLength = queueModel.targetQueues.reduce((sum, queue) => sum + queue.ordered.length, 0);
|
|
232
|
-
const baseDrifts = await evaluateDistinctBaseDrifts(cwd,
|
|
233
|
-
const
|
|
234
|
-
return
|
|
235
|
-
const base = latestBase(item.entries);
|
|
236
|
-
return base ? [base] : [];
|
|
237
|
-
})();
|
|
213
|
+
const baseDrifts = await evaluateDistinctBaseDrifts(cwd, lifecycle.contracts.flatMap((model) => {
|
|
214
|
+
const outcome = lookupCurrentContract(lifecycle, model.contract);
|
|
215
|
+
return outcome.status === "healthy" && !terminalById.get(model.contract) ? [model.pins.base] : [];
|
|
238
216
|
}), defaultBranch);
|
|
239
217
|
const contracts = [];
|
|
240
|
-
|
|
218
|
+
const commissionPlaces = new Map();
|
|
219
|
+
// Folded target pins are retained only for post-assembly filtering.
|
|
241
220
|
const derivedTargetById = new Map();
|
|
242
|
-
for (const
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
baseDrift: { state: "unknown", label: "n/a (corrupt ledger)" },
|
|
256
|
-
after: [],
|
|
257
|
-
corruptReason: item.error.message,
|
|
258
|
-
bootstrapDiagnostics: [],
|
|
259
|
-
queueDiagnostics: [],
|
|
260
|
-
});
|
|
221
|
+
for (const model of lifecycle.contracts) {
|
|
222
|
+
const outcome = lookupCurrentContract(lifecycle, model.contract);
|
|
223
|
+
const diseaseReason = lifecycleDiseaseReason(lifecycle, model.contract);
|
|
224
|
+
const state = outcome.status === "healthy"
|
|
225
|
+
? currentLifecycleState(model)
|
|
226
|
+
: { state: "corrupt", at: 0, reason: diseaseReason || "isolated lifecycle disease" };
|
|
227
|
+
commissionPlaces.set(model.contract, model.resources.worktree?.place ?? null);
|
|
228
|
+
derivedTargetById.set(model.contract, model.pins.target.ref);
|
|
229
|
+
const base = model.pins.base;
|
|
230
|
+
// Terminal lifecycle facts retire their carrier and worktree projections.
|
|
231
|
+
// Do not interpret a different active workspace-here carrier as terminal
|
|
232
|
+
// replay debt.
|
|
233
|
+
if (model.phase === "claimed" || model.phase === "forfeited")
|
|
261
234
|
continue;
|
|
262
|
-
}
|
|
263
|
-
const
|
|
264
|
-
derivedTargetById.set(item.contractId, commissionTarget(item.entries)?.ref);
|
|
265
|
-
const base = latestBase(item.entries);
|
|
266
|
-
const state = deriveContractState(item.entries);
|
|
267
|
-
if (isTerminalState(state))
|
|
268
|
-
continue;
|
|
269
|
-
const after = (bind?.data.after ?? []).map((id) => ({ id, terminal: terminalById.get(id) ?? false }));
|
|
270
|
-
const currentArc = deriveCurrentArc(item.entries, bind?.data.objective, state);
|
|
235
|
+
const after = model.causal.after.map((id) => ({ id, terminal: terminalById.get(id) ?? false }));
|
|
236
|
+
const currentArc = deriveCurrentArc(model);
|
|
271
237
|
const baseDrift = base
|
|
272
238
|
? baseDrifts.get(base) ?? { state: "unknown", label: "n/a (base drift unavailable)" }
|
|
273
239
|
: { state: "unknown", label: "n/a (no default branch)" };
|
|
274
240
|
const nextStep = deriveNextStep({
|
|
275
|
-
id:
|
|
241
|
+
id: model.contract,
|
|
276
242
|
state,
|
|
277
243
|
currentArc,
|
|
278
244
|
baseDrift,
|
|
279
245
|
after,
|
|
280
246
|
});
|
|
281
247
|
if (nextStep.kind === "renew" && baseDrift.state === "drift") {
|
|
282
|
-
|
|
283
|
-
let receiptCount = 0;
|
|
284
|
-
if (openArc) {
|
|
285
|
-
try {
|
|
286
|
-
receiptCount = (await receiptsAfterFence(cwd, item.entries, `keiyaku/${item.contractId}`)).length;
|
|
287
|
-
}
|
|
288
|
-
catch {
|
|
289
|
-
// A ledger-only here contract may not have a delivery ref yet.
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
const sealForecast = openArc ? `seals a${openArc.data.arc} (${receiptCount} receipts), ` : "";
|
|
293
|
-
nextStep.hint = `run: keiyaku @${item.contractId} renew — ${sealForecast}follows ${baseDrift.defaultBranch} ▲${baseDrift.behindBy}`;
|
|
248
|
+
nextStep.hint = `run: keiyaku @${model.contract} renew — follows ${baseDrift.defaultBranch} ▲${baseDrift.behindBy}`;
|
|
294
249
|
}
|
|
295
|
-
const membership = queueMembership(queueModel,
|
|
250
|
+
const membership = queueMembership(queueModel, model.contract);
|
|
296
251
|
const queuePosition = membership?.position ?? null;
|
|
297
252
|
const targetQueueLength = membership?.queueLength ?? 0;
|
|
253
|
+
const replayBlockers = bootstrapReplayBlockers(model);
|
|
254
|
+
const carrierRoot = model.resources.worktree === null
|
|
255
|
+
? stableRoot
|
|
256
|
+
: await findRegisteredContractWorktreePathFromModel(stableRoot, model);
|
|
257
|
+
if (model.resources.worktree !== null && carrierRoot === undefined) {
|
|
258
|
+
replayBlockers.unshift(replayBlockingFact(model.contract, "worktree"));
|
|
259
|
+
}
|
|
260
|
+
else if (carrierRoot !== undefined) {
|
|
261
|
+
let carrierIdentity = null;
|
|
262
|
+
try {
|
|
263
|
+
carrierIdentity = parseCarrierIdentity(await fs.readFile(path.join(carrierRoot, KEIYAKU_FILE), "utf8"));
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
266
|
+
carrierIdentity = null;
|
|
267
|
+
}
|
|
268
|
+
if (carrierIdentity?.contractId !== model.contract || carrierIdentity.ledgerHead !== model.observedLedgerHead) {
|
|
269
|
+
const reason = carrierIdentity === null
|
|
270
|
+
? "missing-or-invalid"
|
|
271
|
+
: carrierIdentity.contractId !== model.contract
|
|
272
|
+
? "contract-id-mismatch"
|
|
273
|
+
: "ledger-head-mismatch";
|
|
274
|
+
replayBlockers.push(replayBlockingFact(model.contract, "carrier", {
|
|
275
|
+
reason,
|
|
276
|
+
expected: { contractId: model.contract, ledgerHead: model.observedLedgerHead },
|
|
277
|
+
observed: carrierIdentity,
|
|
278
|
+
}));
|
|
279
|
+
}
|
|
280
|
+
}
|
|
298
281
|
contracts.push({
|
|
299
|
-
id:
|
|
300
|
-
slug: contractSlugFromId(
|
|
301
|
-
title:
|
|
282
|
+
id: model.contract,
|
|
283
|
+
slug: contractSlugFromId(model.contract),
|
|
284
|
+
title: bindTitle(model),
|
|
302
285
|
state,
|
|
303
286
|
queuePosition,
|
|
304
287
|
queueHeadStalled: isQueueHeadStall({
|
|
305
|
-
entries: item.entries,
|
|
306
288
|
queuePosition,
|
|
307
289
|
queueLength: targetQueueLength,
|
|
308
290
|
state,
|
|
291
|
+
hasVerdict: model.petition.state === "approved",
|
|
309
292
|
}),
|
|
310
|
-
arcs:
|
|
293
|
+
arcs: model.intent.arc,
|
|
311
294
|
currentArc,
|
|
312
295
|
nextStep,
|
|
313
|
-
...deriveLastActiveAge(
|
|
314
|
-
place:
|
|
296
|
+
...deriveLastActiveAge(model, now),
|
|
297
|
+
place: model.resources.worktree?.place ?? null,
|
|
315
298
|
base,
|
|
316
299
|
baseDrift,
|
|
317
300
|
after,
|
|
318
|
-
|
|
301
|
+
lifecycleDiagnostics: outcome.diagnostics,
|
|
302
|
+
replayBlockers,
|
|
319
303
|
queueDiagnostics: membership ? [...membership.diagnostics] : [],
|
|
320
304
|
});
|
|
321
305
|
}
|
|
306
|
+
for (const contract of lifecycle.isolatedContracts) {
|
|
307
|
+
if (lifecycle.contracts.some((model) => model.contract === contract))
|
|
308
|
+
continue;
|
|
309
|
+
const reason = lifecycleDiseaseReason(lifecycle, contract) || "isolated lifecycle disease";
|
|
310
|
+
derivedTargetById.set(contract, undefined);
|
|
311
|
+
contracts.push({
|
|
312
|
+
id: contract, slug: contractSlugFromId(contract), title: contractSlugFromId(contract),
|
|
313
|
+
state: { state: "corrupt", at: 0, reason }, queueHeadStalled: false, arcs: 0,
|
|
314
|
+
currentArc: { number: 0, objective: "", state: "closed" }, nextStep: { kind: "inspect-log", detail: "inspect log" },
|
|
315
|
+
baseDrift: { state: "unknown", label: "n/a (corrupt ledger)" }, after: [], corruptReason: reason,
|
|
316
|
+
lifecycleDiagnostics: lifecycle.diagnostics.filter((diagnostic) => diagnostic.contract === contract),
|
|
317
|
+
replayBlockers: [], queueDiagnostics: [],
|
|
318
|
+
});
|
|
319
|
+
}
|
|
322
320
|
const filteredContracts = targetFilter === null
|
|
323
321
|
? contracts
|
|
324
322
|
: contracts.filter((contract) => {
|
|
@@ -328,12 +326,13 @@ export async function readKanshiBoard(cwd, now, opts) {
|
|
|
328
326
|
const queueLength = targetFilter === null
|
|
329
327
|
? boardQueueLength
|
|
330
328
|
: (queueModel.targetQueues.find((queue) => queue.target === targetFilter)?.ordered.length ?? 0);
|
|
331
|
-
const claimReconciliation = await computeClaimReconciliation(cwd, defaultBranch);
|
|
332
329
|
const exactProjection = opts?.projectionId === undefined
|
|
333
330
|
? undefined
|
|
334
331
|
: await readExactProjectionStatus(cwd, now, opts.projectionId);
|
|
335
332
|
return {
|
|
336
333
|
contracts: filteredContracts,
|
|
334
|
+
contractsObservable: true,
|
|
335
|
+
commissionPlaces,
|
|
337
336
|
projections: opts?.projectionId === undefined
|
|
338
337
|
? await readOperationalProjectionStatusBoard(cwd, now)
|
|
339
338
|
: exactProjection ? { rows: [exactProjection] } : null,
|
|
@@ -341,7 +340,6 @@ export async function readKanshiBoard(cwd, now, opts) {
|
|
|
341
340
|
defaultBranch: defaultBranch?.branch,
|
|
342
341
|
defaultHead: defaultBranch?.head,
|
|
343
342
|
diseases: loadedSettings.diseases,
|
|
344
|
-
claimReconciliation,
|
|
345
343
|
targetFilter,
|
|
346
344
|
...(tasks ? { tasks } : {}),
|
|
347
345
|
};
|
|
@@ -1,84 +1,11 @@
|
|
|
1
|
-
function transitionFromEntry(current, entry) {
|
|
2
|
-
if (current === "claimed" || current === "forfeited" || current === "corrupt") {
|
|
3
|
-
return "corrupt";
|
|
4
|
-
}
|
|
5
|
-
switch (entry.kind) {
|
|
6
|
-
case "bind":
|
|
7
|
-
return "corrupt";
|
|
8
|
-
case "bootstrap":
|
|
9
|
-
// Placement legality is owned exclusively by deriveContractState.
|
|
10
|
-
return "corrupt";
|
|
11
|
-
case "arc":
|
|
12
|
-
return current === "bound" || current === "active" ? "active" : "corrupt";
|
|
13
|
-
case "seal":
|
|
14
|
-
if (current === "active")
|
|
15
|
-
return "active";
|
|
16
|
-
// Derived default arc a0 may seal from bound (bind/bootstrap → seal0 → petition).
|
|
17
|
-
if (current === "bound" && entry.data.arc === 0)
|
|
18
|
-
return "active";
|
|
19
|
-
return "corrupt";
|
|
20
|
-
case "amend":
|
|
21
|
-
return current === "petitioned" ? "active" : current;
|
|
22
|
-
case "renew":
|
|
23
|
-
return current;
|
|
24
|
-
case "verdict":
|
|
25
|
-
if (current !== "petitioned")
|
|
26
|
-
return "corrupt";
|
|
27
|
-
return entry.data.result === "rejected" ? "active" : "petitioned";
|
|
28
|
-
case "petition":
|
|
29
|
-
return current === "bound" || current === "active" ? "petitioned" : "corrupt";
|
|
30
|
-
case "claim":
|
|
31
|
-
return current === "petitioned" ? "claimed" : "corrupt";
|
|
32
|
-
case "forfeit":
|
|
33
|
-
return "forfeited";
|
|
34
|
-
}
|
|
35
|
-
return "corrupt";
|
|
36
|
-
}
|
|
37
|
-
export function deriveContractState(entries) {
|
|
38
|
-
if (entries.length === 0 || entries[0]?.kind !== "bind") {
|
|
39
|
-
return { state: "corrupt", at: 0, reason: "ledger must start with bind" };
|
|
40
|
-
}
|
|
41
|
-
const bind = entries[0];
|
|
42
|
-
let state = "bound";
|
|
43
|
-
for (let index = 1; index < entries.length; index += 1) {
|
|
44
|
-
const entry = entries[index];
|
|
45
|
-
if (entry.kind === "bootstrap") {
|
|
46
|
-
if (index !== 1 || state !== "bound") {
|
|
47
|
-
return { state: "corrupt", at: index + 1, reason: "bootstrap must immediately follow bind" };
|
|
48
|
-
}
|
|
49
|
-
if (!bind.data.bootstrap) {
|
|
50
|
-
return { state: "corrupt", at: index + 1, reason: "bootstrap without bind plan" };
|
|
51
|
-
}
|
|
52
|
-
// Legal bootstrap is a durable fact and leaves the derived state bound.
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
|
-
const next = transitionFromEntry(state, entry);
|
|
56
|
-
if (next === "corrupt") {
|
|
57
|
-
return { state: "corrupt", at: index + 1, reason: `invalid ${entry.kind} after ${state}` };
|
|
58
|
-
}
|
|
59
|
-
state = next;
|
|
60
|
-
}
|
|
61
|
-
return { state };
|
|
62
|
-
}
|
|
63
|
-
export function deriveBootstrapDiagnostics(entries) {
|
|
64
|
-
const bind = entries.find((entry) => entry.kind === "bind");
|
|
65
|
-
if (!bind?.data.bootstrap)
|
|
66
|
-
return [];
|
|
67
|
-
if (entries.some((entry) => entry.kind === "bootstrap"))
|
|
68
|
-
return [];
|
|
69
|
-
return [{ kind: "plan-without-result" }];
|
|
70
|
-
}
|
|
71
1
|
export function isTerminalState(state) {
|
|
72
2
|
return state.state === "claimed" || state.state === "forfeited";
|
|
73
3
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
return false;
|
|
82
|
-
}
|
|
83
|
-
return false;
|
|
4
|
+
export function replayBlockingFact(contract, surface, carrierMismatch) {
|
|
5
|
+
return {
|
|
6
|
+
kind: "incomplete-replay",
|
|
7
|
+
surface,
|
|
8
|
+
recoveryCommand: `keiyaku bind --contract ${contract}`,
|
|
9
|
+
...(carrierMismatch ? { carrierMismatch } : {}),
|
|
10
|
+
};
|
|
84
11
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export const STRUCTURED_QUERY_GRAMMAR = "FIELD OP VALUE[,VALUE...] [| FIELD OP VALUE[,VALUE...]]...";
|
|
2
|
+
export const STRUCTURED_QUERY_COMPLETION = "FIELD OP VALUE[,VALUE...]|FIELD OP VALUE[,VALUE...]";
|
|
3
|
+
export const STRUCTURED_QUERY_OPERATORS = ["=", "!=", "~", "<", "<=", ">", ">="];
|
|
4
|
+
export function parseStructuredQuery(input) {
|
|
5
|
+
const parsed = [];
|
|
6
|
+
for (const rawTerm of input.expression.split("|")) {
|
|
7
|
+
if (!rawTerm)
|
|
8
|
+
input.invalid(`'${input.expression}' must use ${STRUCTURED_QUERY_GRAMMAR}`, parsed);
|
|
9
|
+
const match = /^([a-z][a-z0-9_-]*)(!=|<=|>=|~|=|<|>)(.*)$/.exec(rawTerm);
|
|
10
|
+
if (!match)
|
|
11
|
+
input.invalid(`'${input.expression}' must use ${STRUCTURED_QUERY_GRAMMAR}`, parsed);
|
|
12
|
+
const field = match[1];
|
|
13
|
+
const operator = match[2];
|
|
14
|
+
if (!input.fields.has(field))
|
|
15
|
+
input.invalid(`unknown field '${field}'`, parsed);
|
|
16
|
+
const values = match[3].split(",");
|
|
17
|
+
if (values.some((value) => !value))
|
|
18
|
+
input.invalid(`field '${field}' has an empty value`, parsed);
|
|
19
|
+
input.validate(field, operator, values, parsed);
|
|
20
|
+
parsed.push({ field, operator, values });
|
|
21
|
+
}
|
|
22
|
+
return parsed;
|
|
23
|
+
}
|
package/build/core/target-ref.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { FlowError } from "../flow-error.js";
|
|
2
|
-
import { fullBranchRefSchema } from "./
|
|
2
|
+
import { fullBranchRefSchema } from "./repository-ledger/codec.js";
|
|
3
3
|
/** Prefix bare branch names and validate the full ref. Sole status/bind normalizer. */
|
|
4
4
|
export function normalizeTargetRef(raw) {
|
|
5
5
|
const ref = raw.startsWith("refs/heads/") ? raw : `refs/heads/${raw}`;
|