@astrosheep/keiyaku 2.9.11 → 2.9.13
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 +79 -85
- package/build/.tsbuildinfo +1 -1
- package/build/agents/call-terms.js +1 -0
- package/build/agents/providers/claude-agent-sdk/adapter.js +17 -4
- package/build/cli/commands/akuma/akuma/handler.js +2 -1
- package/build/cli/commands/akuma/akuma/meta.js +4 -3
- package/build/cli/commands/akuma/catalog.js +2 -0
- package/build/cli/commands/akuma/list/handler.js +1 -1
- package/build/cli/commands/akuma/list/meta-list.js +12 -0
- package/build/cli/commands/akuma/list/meta-ls.js +2 -2
- package/build/cli/commands/akuma.js +5 -0
- package/build/cli/commands/contract/amend/meta.js +4 -3
- package/build/cli/commands/contract/arc/meta.js +2 -0
- package/build/cli/commands/contract/audit/handler.js +28 -2
- package/build/cli/commands/contract/audit/meta.js +4 -3
- package/build/cli/commands/contract/bind/meta.js +4 -4
- package/build/cli/commands/contract/petition/handler.js +2 -1
- package/build/cli/commands/contract/petition/meta.js +2 -2
- package/build/cli/commands/contract/renew/handler.js +44 -3
- package/build/cli/commands/contract/renew/meta.js +5 -4
- package/build/cli/commands/metadata.js +14 -10
- package/build/cli/commands/projection/call/handler.js +1 -1
- package/build/cli/commands/projection/call/meta.js +1 -1
- package/build/cli/commands/projection/catalog.js +2 -2
- package/build/cli/commands/projection/{revive → fork}/handler.js +10 -5
- package/build/cli/commands/projection/fork/meta.js +12 -0
- package/build/cli/commands/projection/history/handler.js +46 -2
- package/build/cli/commands/projection/history/meta.js +3 -3
- package/build/cli/commands/projection/kill/handler.js +4 -5
- package/build/cli/commands/projection/kill/meta.js +2 -2
- package/build/cli/commands/projection/status/handler.js +29 -13
- package/build/cli/commands/projection/status/meta.js +14 -3
- package/build/cli/commands/projection/tell/handler.js +8 -4
- package/build/cli/commands/projection/tell/meta.js +1 -1
- package/build/cli/commands/projection/wait/handler.js +45 -20
- package/build/cli/commands/projection/wait/meta.js +1 -1
- package/build/cli/commands/shared.js +2 -2
- package/build/cli/commands/system/catalog.js +2 -0
- package/build/cli/commands/task/add/handler.js +28 -12
- package/build/cli/commands/task/add/meta.js +18 -6
- package/build/cli/commands/task/catalog.js +4 -0
- package/build/cli/commands/task/compose/handler.js +18 -0
- package/build/cli/commands/task/compose/meta.js +10 -0
- package/build/cli/commands/task/doctor/handler.js +2 -3
- package/build/cli/commands/task/doctor/meta.js +2 -2
- package/build/cli/commands/task/done/meta.js +2 -2
- package/build/cli/commands/task/drop/handler.js +7 -2
- package/build/cli/commands/task/drop/meta.js +6 -3
- package/build/cli/commands/task/hold/meta.js +2 -2
- package/build/cli/commands/task/log/meta.js +2 -2
- package/build/cli/commands/task/ls/meta.js +2 -2
- package/build/cli/commands/task/note/handler.js +6 -0
- package/build/cli/commands/task/note/meta.js +11 -0
- package/build/cli/commands/task/resume/meta.js +2 -2
- package/build/cli/commands/task/shared.js +29 -15
- package/build/cli/commands/task/show/meta.js +2 -2
- package/build/cli/commands/task/start/meta.js +2 -2
- package/build/cli/commands/task/stop/meta.js +2 -2
- package/build/cli/commands/task/update/meta.js +3 -2
- package/build/cli/commands/verification/handler.js +43 -0
- package/build/cli/commands/verification/meta.js +13 -0
- package/build/cli/completion.js +16 -13
- package/build/cli/flags.js +48 -12
- package/build/cli/help.js +29 -30
- package/build/cli/index.js +14 -8
- package/build/cli/parse-flags.js +60 -21
- package/build/cli/parse-metadata.js +10 -4
- package/build/cli/parse-selectors.js +22 -4
- package/build/cli/parse.js +43 -30
- package/build/cli/projection-address.js +7 -7
- package/build/cli/render/arc.js +12 -3
- package/build/cli/render/audit.js +2 -2
- package/build/cli/render/call.js +4 -4
- package/build/cli/render/kanshi.js +122 -85
- package/build/cli/render/misc.js +2 -2
- package/build/cli/render/petition.js +16 -1
- package/build/cli/render/projection-history.js +17 -4
- package/build/cli/render/shared.js +5 -5
- package/build/cli/render/status-indicator.js +47 -0
- package/build/cli/render/status.js +106 -76
- package/build/cli/render/success-response.js +20 -5
- package/build/cli/render/task.js +33 -5
- package/build/cli/render/tell.js +10 -4
- package/build/cli/render/tool-command-normalization.js +137 -0
- package/build/cli/render/tool-presentation.js +2 -1
- package/build/cli/render/verification.js +23 -0
- package/build/cli/render/wait.js +101 -33
- package/build/cli/types.js +2 -1
- package/build/config/akuma-loader.js +21 -1
- package/build/config/settings/knobs.js +7 -1
- package/build/config/settings/schema.js +10 -5
- package/build/core/addressing.js +3 -3
- package/build/core/amend.js +102 -17
- package/build/core/arc.js +4 -4
- package/build/core/audit/candidate.js +10 -18
- package/build/core/audit/coordinates.js +12 -17
- package/build/core/audit/evidence.js +6 -4
- package/build/core/audit/facade.js +3 -3
- package/build/core/audit/report.js +1 -1
- package/build/core/bind-reconciliation.js +42 -23
- package/build/core/bind.js +182 -81
- package/build/core/call/call.js +6 -7
- package/build/core/call/context.js +46 -33
- package/build/core/call/execution.js +21 -6
- package/build/core/call/prompt.js +11 -42
- package/build/core/contract-carrier-runtime.js +209 -105
- package/build/core/contract-carrier.js +156 -73
- package/build/core/contract-view.js +43 -6
- package/build/core/contract.js +2 -1
- package/build/core/derived-replay.js +168 -35
- package/build/core/draft.js +60 -27
- package/build/core/forfeit.js +1 -1
- package/build/core/lifecycle-history.js +13 -0
- package/build/core/lifecycle-recovery.js +32 -27
- package/build/core/lifecycle-runner.js +14 -14
- package/build/core/log.js +1 -1
- package/build/core/outcome-base.js +2 -2
- package/build/core/projection/akuma-name.js +43 -0
- package/build/core/projection/generation/database.js +141 -79
- package/build/core/projection/generation/ledger.js +91 -0
- package/build/core/projection/generation/model.js +19 -14
- package/build/core/projection/generation/projection-generation-continuation.js +1 -1
- package/build/core/projection/generation/projection-generation-doorbell.js +88 -0
- package/build/core/projection/generation/projection-generation-execution.js +24 -0
- package/build/core/projection/generation/projection-generation-identity.js +4 -0
- package/build/core/projection/generation/projection-generation-launcher.js +72 -138
- package/build/core/projection/generation/projection-generation-process.js +16 -15
- package/build/core/projection/generation/projection-generation-runner.js +132 -76
- package/build/core/projection/generation/projection-generation-runtime.js +10 -61
- package/build/core/projection/generation/protocol.js +68 -0
- package/build/core/projection/generation/store.js +17 -7
- package/build/core/projection/generation/transitions.js +24 -14
- package/build/core/projection/index.js +5 -5
- package/build/core/projection/projection-core.js +6 -2
- package/build/core/projection/projection-execution-observer.js +1 -1
- package/build/core/projection/projection-history.js +8 -8
- package/build/core/projection/projection-kill.js +20 -10
- package/build/core/projection/projection-life-observer.js +8 -8
- package/build/core/projection/projection-mint.js +6 -8
- package/build/core/projection/projection-status-observation.js +77 -8
- package/build/core/projection/projection-status.js +151 -4
- package/build/core/projection/projection-wait.js +48 -29
- package/build/core/projection/projection-wake.js +28 -88
- package/build/core/projection/tell/launch-store.js +1 -1
- package/build/core/projection/tell/store.js +1 -1
- package/build/core/renew-build.js +204 -74
- package/build/core/renew-plan.js +34 -9
- package/build/core/renew-rewrite.js +56 -15
- package/build/core/renew.js +39 -35
- package/build/core/repository-ledger/accepted-fold-read.js +6 -3
- package/build/core/repository-ledger/claim-evidence.js +1 -1
- package/build/core/repository-ledger/codec.js +11 -5
- package/build/core/repository-ledger/current-state-store.js +464 -349
- package/build/core/repository-ledger/fold-repository.js +5 -6
- package/build/core/repository-ledger/identity.js +60 -0
- package/build/core/repository-ledger/inventory.js +3 -3
- package/build/core/repository-ledger/read-model.js +57 -9
- package/build/core/repository-ledger/write-transaction.js +24 -17
- package/build/core/run-control/connection-bound-close.js +41 -0
- package/build/core/run-control/detached-lease-bootstrap.js +208 -0
- package/build/core/run-control/detached-process.js +14 -0
- package/build/core/run-control/index.js +6 -0
- package/build/core/{process-group.js → run-control/process-tree.js} +1 -1
- package/build/core/run-control/runner-lease.js +172 -0
- package/build/core/run-control/sqlite-process-lifetime-lock.js +174 -0
- package/build/core/seal.js +44 -32
- package/build/core/settlement/claim-delivery.js +22 -9
- package/build/core/settlement/claim.js +8 -6
- package/build/core/settlement/petition-claim-gates.js +9 -8
- package/build/core/settlement/petition-head-guard.js +5 -6
- package/build/core/settlement/petition-preview.js +21 -5
- package/build/core/settlement/petition.js +51 -17
- package/build/core/settlement/settlement.js +20 -15
- package/build/core/settlement/verdict.js +2 -2
- package/build/core/settlement/verification.js +492 -364
- package/build/core/status/board.js +184 -75
- package/build/core/status/drift.js +8 -10
- package/build/core/task/board.js +1 -0
- package/build/core/task/commands.js +25 -0
- package/build/core/task/compose.js +328 -0
- package/build/core/task/document.js +102 -39
- package/build/core/task/index.js +4 -3
- package/build/core/task/settlement-git.js +70 -30
- package/build/core/task/settlement-policy.js +27 -14
- package/build/core/task/source-board.js +1 -0
- package/build/core/task/task-bind-preparation.js +59 -9
- package/build/core/task/task-contract.js +2 -2
- package/build/core/task/task-git-store.js +1 -0
- package/build/core/task/task-store-repository.js +2 -0
- package/build/core/task/task.js +2 -2
- package/build/core/transcripts.js +4 -4
- package/build/core/verification-declaration.js +95 -0
- package/build/core/worktree-bootstrap.js +1 -1
- package/build/core/worktree-path.js +7 -11
- package/build/flow-error.js +15 -2
- package/build/generated/version.js +2 -2
- package/build/git/branches.js +41 -70
- package/build/git/commits.js +92 -21
- package/build/git/core.js +9 -161
- package/build/git/diff/preview.js +4 -4
- package/build/git/diff/read.js +4 -4
- package/build/git/diff/structured.js +5 -5
- package/build/git/process.js +229 -0
- package/build/git/refs.js +30 -5
- package/build/git/staging.js +39 -115
- package/build/git/streaming-batch.js +9 -16
- package/build/git/worktree.js +104 -91
- package/build/index.js +7 -0
- package/package.json +2 -2
- package/skills/keiyaku-akuma/SKILL.md +27 -21
- package/skills/keiyaku-task/SKILL.md +61 -5
- package/skills/keiyaku-workflow/SKILL.md +81 -26
- package/build/cli/commands/projection/revive/meta.js +0 -12
- package/build/core/projection/projection-life-protocol.js +0 -115
- package/build/core/projection/projection-runner-lock.js +0 -382
- package/build/core/projection/tell/database.js +0 -127
- package/build/core/settlement/verification-coordination.js +0 -305
- package/build/core/settlement/verification-supervisor.js +0 -275
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { availableParallelism } from "node:os";
|
|
2
|
-
import * as fs from "node:fs/promises";
|
|
3
2
|
import * as path from "node:path";
|
|
4
3
|
import { loadKeiyakuSettings } from "../../config/settings/loader.js";
|
|
5
4
|
import { observeGitRepository } from "../../git/branches.js";
|
|
6
5
|
import { currentLifecycleState } from "../repository-ledger/read-model.js";
|
|
7
|
-
import {
|
|
6
|
+
import { verifyCarrier } from "../contract-carrier.js";
|
|
7
|
+
import { resolveExplicitContractAddressFromRead, } from "../addressing.js";
|
|
8
8
|
import { KEIYAKU_FILE } from "../../keiyaku.js";
|
|
9
9
|
import { lookupCurrentContract, } from "../repository-ledger/read-model.js";
|
|
10
|
-
import {
|
|
10
|
+
import { readOperationalProjectionStatusBoard, readProjectionRosterStatusBoard, paginateProjectionStatusRoster, selectProjectionStatus, } from "../projection/index.js";
|
|
11
11
|
import { deriveQueueReadModel, queueMembership, } from "../settlement/index.js";
|
|
12
|
-
import { openTaskStore, readTaskBoard, taskRepositoryFromStore, taskUpdatedAt, } from "../task/index.js";
|
|
12
|
+
import { openTaskStore, readTaskBoard, taskRepositoryFromStore, taskUpdatedAt, taskNamespaceFromScope, } from "../task/index.js";
|
|
13
13
|
import { replayBlockingFact, } from "./lifecycle.js";
|
|
14
14
|
import { findRegisteredContractWorktreePathFromModel, stableRepoRoot } from "../worktree-path.js";
|
|
15
15
|
import { evaluateBaseDrift, resolveDefaultBranchFromSettings, } from "./drift.js";
|
|
@@ -28,27 +28,32 @@ function timestampAge(timestamp, now) {
|
|
|
28
28
|
const value = Date.parse(timestamp);
|
|
29
29
|
return Number.isFinite(value) ? Math.max(0, now - value) : 0;
|
|
30
30
|
}
|
|
31
|
-
|
|
32
|
-
const
|
|
31
|
+
function taskStatusRow(task, now) {
|
|
32
|
+
const updatedAt = taskUpdatedAt(task);
|
|
33
|
+
return {
|
|
34
|
+
id: task.id,
|
|
35
|
+
localId: task.id,
|
|
36
|
+
namespace: task.namespace,
|
|
37
|
+
coordinate: task.coordinate,
|
|
38
|
+
title: task.title,
|
|
39
|
+
pri: task.pri,
|
|
40
|
+
state: task.state,
|
|
41
|
+
blocked: task.blocked,
|
|
42
|
+
createdAt: task.createdAt,
|
|
43
|
+
updatedAt,
|
|
44
|
+
ageMs: timestampAge(task.createdAt, now),
|
|
45
|
+
activityAgeMs: timestampAge(updatedAt, now),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
export function deriveTaskStatusSummary(board, now, attribution = new Map()) {
|
|
49
|
+
const eligible = board.tasks.filter((task) => !attribution.has(task.namespace ?? "")
|
|
50
|
+
&& (task.state === "open" || task.state === "in_progress" || task.state === "on_hold"));
|
|
33
51
|
const inProgress = eligible.filter((task) => task.state === "in_progress").length;
|
|
34
52
|
const onHold = eligible.filter((task) => task.state === "on_hold").length;
|
|
35
53
|
const topTasks = eligible
|
|
36
54
|
.filter((task) => task.state === "in_progress" || task.state === "on_hold")
|
|
37
55
|
.sort(compareTaskStatusRows)
|
|
38
|
-
.map((task) =>
|
|
39
|
-
const updatedAt = taskUpdatedAt(task);
|
|
40
|
-
return {
|
|
41
|
-
id: task.id,
|
|
42
|
-
title: task.title,
|
|
43
|
-
pri: task.pri,
|
|
44
|
-
state: task.state,
|
|
45
|
-
blocked: task.blocked,
|
|
46
|
-
createdAt: task.createdAt,
|
|
47
|
-
updatedAt,
|
|
48
|
-
ageMs: timestampAge(task.createdAt, now),
|
|
49
|
-
activityAgeMs: timestampAge(updatedAt, now),
|
|
50
|
-
};
|
|
51
|
-
});
|
|
56
|
+
.map((task) => taskStatusRow(task, now));
|
|
52
57
|
const ready = eligible.filter((task) => task.state === "open" && !task.blocked).length;
|
|
53
58
|
const blocked = eligible.filter((task) => task.state === "open" && task.blocked).length;
|
|
54
59
|
return {
|
|
@@ -60,12 +65,65 @@ export function deriveTaskStatusSummary(board, now) {
|
|
|
60
65
|
omitted: ready + blocked,
|
|
61
66
|
topTasks,
|
|
62
67
|
diseases: board.diseases,
|
|
68
|
+
contractProgress: deriveContractTaskProgress(board, now, attribution),
|
|
63
69
|
};
|
|
64
70
|
}
|
|
65
|
-
|
|
71
|
+
function deriveContractTaskProgress(board, now, attribution) {
|
|
72
|
+
const grouped = new Map();
|
|
73
|
+
for (const task of board.tasks) {
|
|
74
|
+
const fact = task.namespace === null ? undefined : attribution.get(task.namespace);
|
|
75
|
+
if (!fact || task.state === "drop")
|
|
76
|
+
continue;
|
|
77
|
+
const group = grouped.get(fact.contractId) ?? { fact, tasks: [] };
|
|
78
|
+
group.tasks.push(task);
|
|
79
|
+
grouped.set(fact.contractId, group);
|
|
80
|
+
}
|
|
81
|
+
return [...grouped.values()].map(({ fact, tasks }) => {
|
|
82
|
+
const ordered = [...tasks].sort(compareTaskStatusRows);
|
|
83
|
+
const visible = ordered.map((task) => taskStatusRow(task, now));
|
|
84
|
+
const current = visible.find((task) => task.state === "in_progress" || task.state === "on_hold" || task.state === "open");
|
|
85
|
+
return {
|
|
86
|
+
contractId: fact.contractId,
|
|
87
|
+
namespace: fact.namespace,
|
|
88
|
+
total: tasks.length,
|
|
89
|
+
done: tasks.filter((task) => task.state === "done").length,
|
|
90
|
+
inProgress: tasks.filter((task) => task.state === "in_progress").length,
|
|
91
|
+
onHold: tasks.filter((task) => task.state === "on_hold").length,
|
|
92
|
+
ready: tasks.filter((task) => task.state === "open" && !task.blocked).length,
|
|
93
|
+
blocked: tasks.filter((task) => task.state === "open" && task.blocked).length,
|
|
94
|
+
...(current ? { current } : {}),
|
|
95
|
+
tasks: visible,
|
|
96
|
+
};
|
|
97
|
+
}).sort((left, right) => left.contractId.localeCompare(right.contractId));
|
|
98
|
+
}
|
|
99
|
+
export function deriveContractTaskNamespaceAttribution(facts) {
|
|
100
|
+
const byNamespace = new Map();
|
|
101
|
+
for (const fact of facts) {
|
|
102
|
+
const prior = byNamespace.get(fact.namespace);
|
|
103
|
+
if (prior && prior.contractId !== fact.contractId) {
|
|
104
|
+
throw new Error(`task namespace ${fact.namespace} is attributed to multiple contracts`);
|
|
105
|
+
}
|
|
106
|
+
byNamespace.set(fact.namespace, fact);
|
|
107
|
+
}
|
|
108
|
+
return byNamespace;
|
|
109
|
+
}
|
|
110
|
+
/** Attribute namespaces only from accepted lifecycle scope facts. */
|
|
111
|
+
export function contractTaskNamespaceFacts(models) {
|
|
112
|
+
return models.flatMap((model) => {
|
|
113
|
+
const namespaces = model.effectiveScope.flatMap((term) => taskNamespaceFromScope(term) ?? []);
|
|
114
|
+
if (namespaces.length !== 1)
|
|
115
|
+
return [];
|
|
116
|
+
return [{
|
|
117
|
+
contractId: model.contract,
|
|
118
|
+
namespace: namespaces[0],
|
|
119
|
+
lifecycle: model.phase === "claimed" || model.phase === "forfeited" ? "terminal" : "active",
|
|
120
|
+
}];
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
async function readTaskStatusSummary(cwd, now, attribution = new Map()) {
|
|
66
124
|
try {
|
|
67
125
|
const board = await readTaskBoard(taskRepositoryFromStore(await openTaskStore(cwd)));
|
|
68
|
-
return deriveTaskStatusSummary(board, now);
|
|
126
|
+
return deriveTaskStatusSummary(board, now, attribution);
|
|
69
127
|
}
|
|
70
128
|
catch (error) {
|
|
71
129
|
return {
|
|
@@ -77,6 +135,7 @@ async function readTaskStatusSummary(cwd, now) {
|
|
|
77
135
|
omitted: 0,
|
|
78
136
|
topTasks: [],
|
|
79
137
|
diseases: [],
|
|
138
|
+
contractProgress: [],
|
|
80
139
|
diagnostic: error instanceof Error ? error.message : String(error),
|
|
81
140
|
};
|
|
82
141
|
}
|
|
@@ -113,6 +172,9 @@ function bootstrapReplayBlockers(model) {
|
|
|
113
172
|
: [];
|
|
114
173
|
}
|
|
115
174
|
function deriveNextStep(input) {
|
|
175
|
+
if (input.state.state === "claimed" || input.state.state === "forfeited") {
|
|
176
|
+
return { kind: "terminal", detail: input.state.state };
|
|
177
|
+
}
|
|
116
178
|
if (input.state.state === "corrupt") {
|
|
117
179
|
return { kind: "inspect-log", detail: "inspect log" };
|
|
118
180
|
}
|
|
@@ -123,9 +185,6 @@ function deriveNextStep(input) {
|
|
|
123
185
|
if (input.state.state === "petitioned") {
|
|
124
186
|
return { kind: "await-claim", detail: "await claim" };
|
|
125
187
|
}
|
|
126
|
-
if (input.baseDrift.state === "drift" || input.baseDrift.state === "orphaned") {
|
|
127
|
-
return { kind: "renew", detail: "renew" };
|
|
128
|
-
}
|
|
129
188
|
// Derived a0 (bound/active, no explicit arc) petitions directly; explicit open arcs still suggest arc work.
|
|
130
189
|
if (input.currentArc.state === "open" && input.currentArc.number === 0) {
|
|
131
190
|
return { kind: "petition", detail: "petition" };
|
|
@@ -171,24 +230,24 @@ export async function evaluateDistinctBaseDrifts(cwd, bases, defaultBranch, eval
|
|
|
171
230
|
export async function readKanshiBoard(cwd, now, opts) {
|
|
172
231
|
const targetFilter = opts?.target ?? null;
|
|
173
232
|
const loadedSettings = await loadKeiyakuSettings(cwd);
|
|
174
|
-
const tasks = await readTaskStatusSummary(cwd, now);
|
|
175
233
|
const repository = await observeGitRepository(cwd);
|
|
176
234
|
if (repository.capability === "unknown")
|
|
177
235
|
throw repository.error;
|
|
178
236
|
if (repository.capability !== "available" || repository.coordinate !== "repository") {
|
|
179
|
-
const exactProjection = opts?.
|
|
237
|
+
const exactProjection = opts?.projectionAddress === undefined
|
|
180
238
|
? undefined
|
|
181
|
-
: await
|
|
239
|
+
: await selectProjectionStatus(cwd, now, { kind: "address", token: opts.projectionAddress });
|
|
182
240
|
return {
|
|
183
241
|
contracts: [],
|
|
184
242
|
contractsObservable: false,
|
|
185
243
|
commissionPlaces: new Map(),
|
|
186
|
-
projections: opts?.
|
|
187
|
-
? await readOperationalProjectionStatusBoard(cwd, now)
|
|
188
|
-
: exactProjection
|
|
244
|
+
projections: opts?.projectionAddress === undefined
|
|
245
|
+
? opts?.roster ? await readProjectionRosterStatusBoard(cwd, now) : await readOperationalProjectionStatusBoard(cwd, now)
|
|
246
|
+
: exactProjection?.board ?? null,
|
|
189
247
|
queueLength: 0,
|
|
190
248
|
diseases: loadedSettings.diseases,
|
|
191
249
|
targetFilter,
|
|
250
|
+
tasks: await readTaskStatusSummary(cwd, now),
|
|
192
251
|
// Task and contract boards are repository organs; a non-repository cwd
|
|
193
252
|
// cannot truthfully turn their absence into a zero count.
|
|
194
253
|
};
|
|
@@ -203,7 +262,18 @@ export async function readKanshiBoard(cwd, now, opts) {
|
|
|
203
262
|
defaultBranch = null;
|
|
204
263
|
}
|
|
205
264
|
const lifecycle = await readStatusLifecycle(cwd);
|
|
265
|
+
const taskAttribution = deriveContractTaskNamespaceAttribution(contractTaskNamespaceFacts(lifecycle.contracts));
|
|
266
|
+
const tasks = await readTaskStatusSummary(cwd, now, taskAttribution);
|
|
206
267
|
const stableRoot = await stableRepoRoot(cwd);
|
|
268
|
+
const commissionFilter = opts?.commission === undefined
|
|
269
|
+
? undefined
|
|
270
|
+
: resolveExplicitContractAddressFromRead({
|
|
271
|
+
read: lifecycle,
|
|
272
|
+
stableRoot,
|
|
273
|
+
workdir: cwd,
|
|
274
|
+
raw: opts.commission.raw,
|
|
275
|
+
source: opts.commission.source,
|
|
276
|
+
}).address.binding.commissionId;
|
|
207
277
|
const terminalById = new Map(lifecycle.contracts.map((model) => [
|
|
208
278
|
model.contract,
|
|
209
279
|
model.phase === "claimed" || model.phase === "forfeited",
|
|
@@ -227,16 +297,19 @@ export async function readKanshiBoard(cwd, now, opts) {
|
|
|
227
297
|
commissionPlaces.set(model.contract, model.resources.worktree?.place ?? null);
|
|
228
298
|
derivedTargetById.set(model.contract, model.pins.target.ref);
|
|
229
299
|
const base = model.pins.base;
|
|
230
|
-
|
|
231
|
-
//
|
|
232
|
-
//
|
|
233
|
-
|
|
300
|
+
const terminal = model.phase === "claimed" || model.phase === "forfeited";
|
|
301
|
+
// Terminal contracts retire from the aggregate board. An exact selector
|
|
302
|
+
// may still open the folded row as a read-only carrier for its task
|
|
303
|
+
// namespace, including completed tasks.
|
|
304
|
+
if (terminal && commissionFilter !== model.contract)
|
|
234
305
|
continue;
|
|
235
306
|
const after = model.causal.after.map((id) => ({ id, terminal: terminalById.get(id) ?? false }));
|
|
236
307
|
const currentArc = deriveCurrentArc(model);
|
|
237
|
-
const baseDrift =
|
|
238
|
-
?
|
|
239
|
-
:
|
|
308
|
+
const baseDrift = terminal
|
|
309
|
+
? { state: "unknown", label: "n/a (terminal contract)" }
|
|
310
|
+
: base
|
|
311
|
+
? baseDrifts.get(base) ?? { state: "unknown", label: "n/a (base drift unavailable)" }
|
|
312
|
+
: { state: "unknown", label: "n/a (no default branch)" };
|
|
240
313
|
const nextStep = deriveNextStep({
|
|
241
314
|
id: model.contract,
|
|
242
315
|
state,
|
|
@@ -244,40 +317,39 @@ export async function readKanshiBoard(cwd, now, opts) {
|
|
|
244
317
|
baseDrift,
|
|
245
318
|
after,
|
|
246
319
|
});
|
|
247
|
-
|
|
248
|
-
nextStep.hint = `run: keiyaku @${model.contract} renew — follows ${baseDrift.defaultBranch} ▲${baseDrift.behindBy}`;
|
|
249
|
-
}
|
|
250
|
-
const membership = queueMembership(queueModel, model.contract);
|
|
320
|
+
const membership = terminal ? undefined : queueMembership(queueModel, model.contract);
|
|
251
321
|
const queuePosition = membership?.position ?? null;
|
|
252
322
|
const targetQueueLength = membership?.queueLength ?? 0;
|
|
253
|
-
const replayBlockers = bootstrapReplayBlockers(model);
|
|
254
|
-
const carrierRoot =
|
|
255
|
-
?
|
|
256
|
-
:
|
|
257
|
-
|
|
323
|
+
const replayBlockers = terminal ? [] : bootstrapReplayBlockers(model);
|
|
324
|
+
const carrierRoot = terminal
|
|
325
|
+
? undefined
|
|
326
|
+
: model.resources.worktree === null
|
|
327
|
+
? stableRoot
|
|
328
|
+
: await findRegisteredContractWorktreePathFromModel(stableRoot, model);
|
|
329
|
+
if (!terminal && model.resources.worktree !== null && carrierRoot === undefined) {
|
|
258
330
|
replayBlockers.unshift(replayBlockingFact(model.contract, "worktree"));
|
|
259
331
|
}
|
|
260
|
-
else if (carrierRoot !== undefined) {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
catch {
|
|
266
|
-
carrierIdentity = null;
|
|
267
|
-
}
|
|
268
|
-
if (carrierIdentity?.contractId !== model.contract || carrierIdentity.ledgerHead !== model.observedLedgerHead) {
|
|
269
|
-
const reason = carrierIdentity === null
|
|
332
|
+
else if (!terminal && carrierRoot !== undefined) {
|
|
333
|
+
const verification = await verifyCarrier({ target: path.join(carrierRoot, KEIYAKU_FILE), lifecycle: model });
|
|
334
|
+
if (verification.status !== "current") {
|
|
335
|
+
const actual = "actual" in verification ? verification.actual : null;
|
|
336
|
+
const reason = verification.status === "missing" || verification.status === "invalid-header"
|
|
270
337
|
? "missing-or-invalid"
|
|
271
|
-
:
|
|
272
|
-
? "
|
|
273
|
-
:
|
|
338
|
+
: verification.status === "modified"
|
|
339
|
+
? "carrier-modified"
|
|
340
|
+
: actual?.contractId !== model.contract
|
|
341
|
+
? "contract-id-mismatch"
|
|
342
|
+
: actual?.contractHead !== model.contractHead
|
|
343
|
+
? "contract-head-mismatch"
|
|
344
|
+
: "carrier-digest-mismatch";
|
|
274
345
|
replayBlockers.push(replayBlockingFact(model.contract, "carrier", {
|
|
275
346
|
reason,
|
|
276
|
-
expected:
|
|
277
|
-
observed:
|
|
347
|
+
expected: verification.expected,
|
|
348
|
+
observed: actual,
|
|
278
349
|
}));
|
|
279
350
|
}
|
|
280
351
|
}
|
|
352
|
+
const taskProgress = tasks?.contractProgress.find((progress) => progress.contractId === model.contract);
|
|
281
353
|
contracts.push({
|
|
282
354
|
id: model.contract,
|
|
283
355
|
slug: contractSlugFromId(model.contract),
|
|
@@ -301,6 +373,7 @@ export async function readKanshiBoard(cwd, now, opts) {
|
|
|
301
373
|
lifecycleDiagnostics: outcome.diagnostics,
|
|
302
374
|
replayBlockers,
|
|
303
375
|
queueDiagnostics: membership ? [...membership.diagnostics] : [],
|
|
376
|
+
...(taskProgress ? { tasks: taskProgress } : {}),
|
|
304
377
|
});
|
|
305
378
|
}
|
|
306
379
|
for (const contract of lifecycle.isolatedContracts) {
|
|
@@ -317,30 +390,66 @@ export async function readKanshiBoard(cwd, now, opts) {
|
|
|
317
390
|
replayBlockers: [], queueDiagnostics: [],
|
|
318
391
|
});
|
|
319
392
|
}
|
|
320
|
-
const filteredContracts =
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
return
|
|
325
|
-
|
|
393
|
+
const filteredContracts = contracts.filter((contract) => {
|
|
394
|
+
if (commissionFilter !== undefined && contract.id !== commissionFilter)
|
|
395
|
+
return false;
|
|
396
|
+
if (targetFilter === null)
|
|
397
|
+
return true;
|
|
398
|
+
const derived = derivedTargetById.get(contract.id);
|
|
399
|
+
return derived === undefined || derived === targetFilter;
|
|
400
|
+
});
|
|
326
401
|
const queueLength = targetFilter === null
|
|
327
402
|
? boardQueueLength
|
|
328
403
|
: (queueModel.targetQueues.find((queue) => queue.target === targetFilter)?.ordered.length ?? 0);
|
|
329
|
-
const exactProjection = opts?.
|
|
404
|
+
const exactProjection = opts?.projectionAddress === undefined
|
|
330
405
|
? undefined
|
|
331
|
-
: await
|
|
406
|
+
: await selectProjectionStatus(cwd, now, { kind: "address", token: opts.projectionAddress });
|
|
407
|
+
const operationalProjections = opts?.projectionAddress === undefined
|
|
408
|
+
? opts?.roster ? await readProjectionRosterStatusBoard(cwd, now) : await readOperationalProjectionStatusBoard(cwd, now)
|
|
409
|
+
: undefined;
|
|
410
|
+
// The aggregate reader deliberately folds terminal rows before their identity
|
|
411
|
+
// is opened. A commission scope therefore keeps only attributable live rows,
|
|
412
|
+
// never an aggregate whose counts could include a different commission.
|
|
413
|
+
const selectedProjections = opts?.projectionAddress === undefined
|
|
414
|
+
? commissionFilter === undefined
|
|
415
|
+
? operationalProjections ?? null
|
|
416
|
+
: operationalProjections
|
|
417
|
+
? {
|
|
418
|
+
rows: operationalProjections.rows.filter((row) => row.binding?.kind === "commission" && row.binding.commissionId === commissionFilter),
|
|
419
|
+
}
|
|
420
|
+
: null
|
|
421
|
+
: exactProjection?.board ?? null;
|
|
422
|
+
const projections = opts?.roster && selectedProjections
|
|
423
|
+
? paginateProjectionStatusRoster(selectedProjections, { limit: opts.rosterLimit, offset: opts.rosterOffset })
|
|
424
|
+
: selectedProjections;
|
|
425
|
+
const filteredCommissionPlaces = commissionFilter === undefined
|
|
426
|
+
? commissionPlaces
|
|
427
|
+
: new Map([[commissionFilter, commissionPlaces.get(commissionFilter) ?? null]]);
|
|
332
428
|
return {
|
|
333
429
|
contracts: filteredContracts,
|
|
334
430
|
contractsObservable: true,
|
|
335
|
-
commissionPlaces,
|
|
336
|
-
projections
|
|
337
|
-
? await readOperationalProjectionStatusBoard(cwd, now)
|
|
338
|
-
: exactProjection ? { rows: [exactProjection] } : null,
|
|
431
|
+
commissionPlaces: filteredCommissionPlaces,
|
|
432
|
+
projections,
|
|
339
433
|
queueLength,
|
|
340
434
|
defaultBranch: defaultBranch?.branch,
|
|
341
435
|
defaultHead: defaultBranch?.head,
|
|
342
436
|
diseases: loadedSettings.diseases,
|
|
343
437
|
targetFilter,
|
|
344
|
-
...(tasks ? {
|
|
438
|
+
...(tasks ? {
|
|
439
|
+
tasks: commissionFilter === undefined
|
|
440
|
+
? tasks
|
|
441
|
+
: {
|
|
442
|
+
...tasks,
|
|
443
|
+
total: 0,
|
|
444
|
+
inProgress: 0,
|
|
445
|
+
onHold: 0,
|
|
446
|
+
ready: 0,
|
|
447
|
+
blocked: 0,
|
|
448
|
+
omitted: 0,
|
|
449
|
+
topTasks: [],
|
|
450
|
+
contractProgress: tasks.contractProgress.filter((progress) => progress.contractId === commissionFilter),
|
|
451
|
+
},
|
|
452
|
+
} : {}),
|
|
453
|
+
...(commissionFilter === undefined ? {} : { commissionFilter }),
|
|
345
454
|
};
|
|
346
455
|
}
|
|
@@ -2,7 +2,8 @@ import { assertSettingsKnobUsable } from "../../config/settings/disease.js";
|
|
|
2
2
|
import { loadKeiyakuSettings } from "../../config/settings/loader.js";
|
|
3
3
|
import { FlowError } from "../../flow-error.js";
|
|
4
4
|
import { resolveLocalBranchHead } from "../../git/refs.js";
|
|
5
|
-
import {
|
|
5
|
+
import { readCommitCoordinate, commitCoordinateFailureError } from "../../git/commits.js";
|
|
6
|
+
import { createGit } from "../../git/core.js";
|
|
6
7
|
function findBindEntry(entries) {
|
|
7
8
|
return entries.find((entry) => entry.kind === "bind");
|
|
8
9
|
}
|
|
@@ -26,15 +27,12 @@ export function commissionTarget(entries) {
|
|
|
26
27
|
};
|
|
27
28
|
}
|
|
28
29
|
export async function resolveCommit(cwd, ref) {
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
return
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
return null;
|
|
36
|
-
throw wrapGitError(`rev-parse ${ref}`, error, cwd);
|
|
37
|
-
}
|
|
30
|
+
const read = await readCommitCoordinate(cwd, ref);
|
|
31
|
+
if (read.status === "ok")
|
|
32
|
+
return read.coordinate.commit;
|
|
33
|
+
if (read.status === "missing" || read.status === "non-commit")
|
|
34
|
+
return null;
|
|
35
|
+
throw commitCoordinateFailureError(read, cwd);
|
|
38
36
|
}
|
|
39
37
|
export async function readSettingsDiseases(cwd) {
|
|
40
38
|
const loaded = await loadKeiyakuSettings(cwd);
|
package/build/core/task/board.js
CHANGED
|
@@ -198,6 +198,7 @@ export function buildTaskBoardFromDocuments(documents) {
|
|
|
198
198
|
from: [...task.from],
|
|
199
199
|
supersedes: [...task.supersedes],
|
|
200
200
|
relatesTo: [...task.relatesTo],
|
|
201
|
+
notes: task.notes.map((note) => ({ ...note })),
|
|
201
202
|
createdAt: task.createdAt,
|
|
202
203
|
...(task.updatedAt === undefined ? {} : { updatedAt: task.updatedAt }),
|
|
203
204
|
creator: task.creator,
|
|
@@ -21,6 +21,7 @@ function documentFromView(view) {
|
|
|
21
21
|
from: [...view.from],
|
|
22
22
|
supersedes: [...view.supersedes],
|
|
23
23
|
relatesTo: [...view.relatesTo],
|
|
24
|
+
notes: view.notes.map((note) => ({ ...note })),
|
|
24
25
|
createdAt: view.createdAt,
|
|
25
26
|
...(view.updatedAt === undefined ? {} : { updatedAt: view.updatedAt }),
|
|
26
27
|
creator: view.creator,
|
|
@@ -140,6 +141,7 @@ export async function createTask(repository, input, deps) {
|
|
|
140
141
|
from,
|
|
141
142
|
supersedes,
|
|
142
143
|
relatesTo,
|
|
144
|
+
notes: [],
|
|
143
145
|
createdAt: dateTimestamp("createdAt", now),
|
|
144
146
|
updatedAt: dateTimestamp("updatedAt", now),
|
|
145
147
|
creator: input.creator,
|
|
@@ -322,6 +324,26 @@ export async function doneTask(repository, fullId, now = new Date()) {
|
|
|
322
324
|
}, (id) => `Done task ${id}`, now);
|
|
323
325
|
}
|
|
324
326
|
export async function dropTask(repository, fullId, now = new Date()) {
|
|
327
|
+
return dropTaskWithNote(repository, fullId, undefined, now);
|
|
328
|
+
}
|
|
329
|
+
export async function appendTaskNote(repository, fullId, input, now = new Date()) {
|
|
330
|
+
nonblank("task note actor", input.actor);
|
|
331
|
+
nonblank("task note text", input.text);
|
|
332
|
+
return repository.mutate((board) => {
|
|
333
|
+
requireHealthy(board);
|
|
334
|
+
const id = resolveTaskId(board, fullId);
|
|
335
|
+
const current = requireExisting(board, id);
|
|
336
|
+
const document = documentFromView(current);
|
|
337
|
+
document.notes.push({ actor: input.actor, timestamp: dateTimestamp("task note timestamp", now), text: input.text });
|
|
338
|
+
document.updatedAt = advancedTaskUpdatedAt(current, now);
|
|
339
|
+
return replaceDocument(document, `Note task ${id}`);
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
export async function dropTaskWithNote(repository, fullId, note, now = new Date()) {
|
|
343
|
+
if (note !== undefined) {
|
|
344
|
+
nonblank("task note actor", note.actor);
|
|
345
|
+
nonblank("task note text", note.text);
|
|
346
|
+
}
|
|
325
347
|
return transition(repository, fullId, (document) => {
|
|
326
348
|
rejectBound(document, "drop");
|
|
327
349
|
if (document.state !== "open" && document.state !== "in_progress" && document.state !== "on_hold") {
|
|
@@ -329,6 +351,9 @@ export async function dropTask(repository, fullId, now = new Date()) {
|
|
|
329
351
|
}
|
|
330
352
|
document.state = "drop";
|
|
331
353
|
delete document.startedAt;
|
|
354
|
+
if (note !== undefined) {
|
|
355
|
+
document.notes.push({ actor: note.actor, timestamp: dateTimestamp("task note timestamp", now), text: note.text });
|
|
356
|
+
}
|
|
332
357
|
}, (id) => `Drop task ${id}`, now);
|
|
333
358
|
}
|
|
334
359
|
export function selectSoleInProgress(board) {
|