@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
package/build/core/amend.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { FlowError } from "../flow-error.js";
|
|
2
2
|
import { getCurrentBranch, requireGitRepository } from "../git/branches.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { runLifecycle } from "./lifecycle-runner.js";
|
|
4
|
+
import { currentLifecycleState } from "./repository-ledger/read-model.js";
|
|
5
|
+
import { isTerminalState } from "./status/lifecycle.js";
|
|
6
6
|
import { resolveContractAddress, withResolvedAddress } from "./addressing.js";
|
|
7
7
|
import { resolveDefaultBranch } from "./status/drift.js";
|
|
8
8
|
import { markdownSourceSections, normalizeScopePattern, ScopePatternError, validateScopeFragmentLines } from "./scope.js";
|
|
9
9
|
import { normalizeSectionTitle } from "./markdown/titles.js";
|
|
10
|
-
import {
|
|
10
|
+
import { isCompleteLifecycleOutcome } from "./outcome-base.js";
|
|
11
|
+
import { getConfig } from "../config/env.js";
|
|
11
12
|
function amendScopeHeading(name) {
|
|
12
13
|
return `## Scope ${name[0].toUpperCase()}${name.slice(1)}`;
|
|
13
14
|
}
|
|
@@ -79,11 +80,8 @@ function requireAmendment(value) {
|
|
|
79
80
|
throw new FlowError("EMPTY_PARAM", "amendment cannot be empty");
|
|
80
81
|
return value;
|
|
81
82
|
}
|
|
82
|
-
function assertContractOperable(contractId,
|
|
83
|
-
const state =
|
|
84
|
-
if (state.state === "corrupt") {
|
|
85
|
-
throw new FlowError("INTERNAL_STATE", `contract ${contractId} is corrupt at entry ${state.at}: ${state.reason}`);
|
|
86
|
-
}
|
|
83
|
+
function assertContractOperable(contractId, model) {
|
|
84
|
+
const state = currentLifecycleState(model);
|
|
87
85
|
if (isTerminalState(state)) {
|
|
88
86
|
throw new FlowError("INTERNAL_STATE", `contract ${contractId} is terminal (${state.state})`);
|
|
89
87
|
}
|
|
@@ -100,53 +98,131 @@ function normalizeScopeDelta(scopeDelta) {
|
|
|
100
98
|
export async function amendContract(input) {
|
|
101
99
|
const amendment = requireAmendment(input.amendment);
|
|
102
100
|
const scopeDelta = normalizeScopeDelta(input.scopeDelta);
|
|
103
|
-
const renderCwd = input.renderCwd ?? input.cwd;
|
|
104
101
|
const at = new Date(input.nowMs).toISOString();
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
102
|
+
const execution = await runLifecycle({
|
|
103
|
+
cwd: input.cwd,
|
|
104
|
+
contractId: input.contractId,
|
|
105
|
+
mutation: {
|
|
106
|
+
verb: "amend",
|
|
107
|
+
// Amendment content is not a retry key; the containing Lnew is the sole mutation identity.
|
|
108
|
+
matchesAccepted: () => false,
|
|
109
|
+
},
|
|
110
|
+
signal: input.signal,
|
|
111
|
+
admit: (session, replay) => {
|
|
112
|
+
assertContractOperable(input.contractId, session.lifecycle);
|
|
113
|
+
if (session.target.status !== "available")
|
|
114
|
+
throw new FlowError("INTERNAL_STATE", session.target.reason);
|
|
115
|
+
const transaction = replay.state === "verb-committed" ? replay.transaction : undefined;
|
|
116
|
+
const accepted = transaction?.entries.at(-1);
|
|
117
|
+
if (accepted && accepted.kind !== "amend")
|
|
118
|
+
throw new FlowError("INTERNAL_STATE", `accepted transaction ${transaction.transactionObjectId} is not an amend`);
|
|
119
|
+
const existing = accepted?.kind === "amend" ? { transactionObjectId: transaction.transactionObjectId } : undefined;
|
|
120
|
+
const amendmentCount = session.lifecycle.history.filter(({ entry }) => entry.kind === "amend").length;
|
|
121
|
+
return {
|
|
122
|
+
amendmentNumber: amendmentCount + (existing ? 0 : 1),
|
|
123
|
+
targetRoot: session.target.root,
|
|
124
|
+
...(existing ? { existing } : {}),
|
|
125
|
+
};
|
|
126
|
+
},
|
|
127
|
+
prepare: (admission) => {
|
|
128
|
+
if (admission.business.existing) {
|
|
129
|
+
return {
|
|
130
|
+
amendmentNumber: admission.business.amendmentNumber,
|
|
131
|
+
existing: admission.business.existing,
|
|
132
|
+
entry: null,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
amendmentNumber: admission.business.amendmentNumber,
|
|
137
|
+
entry: {
|
|
138
|
+
v: 1,
|
|
139
|
+
kind: "amend",
|
|
140
|
+
contract: input.contractId,
|
|
141
|
+
at,
|
|
142
|
+
actor: input.actor,
|
|
143
|
+
data: { amendment, ...(scopeDelta ? { scopeDelta } : {}) },
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
},
|
|
147
|
+
revalidate: ({ session, admission, prepared }) => {
|
|
148
|
+
assertContractOperable(input.contractId, session.lifecycle);
|
|
149
|
+
if (session.target.status !== "available") {
|
|
150
|
+
return { code: "CARRIER_TARGET_UNAVAILABLE", message: session.target.reason };
|
|
151
|
+
}
|
|
152
|
+
if (prepared.existing) {
|
|
153
|
+
return session.repository.topology.nodes.some((node) => node.objectId === prepared.existing.transactionObjectId)
|
|
154
|
+
? undefined
|
|
155
|
+
: { code: "AMEND_STATE_CHANGED", message: "accepted amend changed during replay preparation" };
|
|
156
|
+
}
|
|
157
|
+
const amendmentNumber = session.lifecycle.history.filter(({ entry }) => entry.kind === "amend").length + 1;
|
|
158
|
+
return amendmentNumber === admission.business.amendmentNumber
|
|
159
|
+
? undefined
|
|
160
|
+
: { code: "AMENDMENT_NUMBER_CHANGED", message: "amendment sequence changed during preparation" };
|
|
161
|
+
},
|
|
162
|
+
effect: async ({ context, admission, prepared }) => {
|
|
163
|
+
const result = prepared.existing
|
|
164
|
+
? await context.replayCommitted({ transactionObjectId: prepared.existing.transactionObjectId })
|
|
165
|
+
: await context.publishAuthoritative({ verb: "amend", admittedLedgerHead: admission.ledgerHead, acceptedLifecycleTail: context.session.lifecycle.history.at(-1).objectId, entries: [prepared.entry] });
|
|
166
|
+
if (result.status === "indeterminate") {
|
|
167
|
+
return { kind: "precommit-effects-only", authoritativeHead: result.currentHead ?? admission.ledgerHead };
|
|
168
|
+
}
|
|
169
|
+
if (result.status !== "committed") {
|
|
170
|
+
return { kind: "refused-before-effects", authoritativeHead: result.currentHead ?? context.session.lifecycle.observedLedgerHead, refusal: { code: "LEDGER_CAS_CONFLICT", message: `contract ${input.contractId} changed during amend` } };
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
kind: "committed",
|
|
174
|
+
commit: result,
|
|
175
|
+
entryKind: "amend",
|
|
176
|
+
value: {
|
|
177
|
+
amendmentNumber: prepared.amendmentNumber,
|
|
178
|
+
amendmentWarning: input.amendmentWarningThreshold === undefined
|
|
179
|
+
? undefined
|
|
180
|
+
: prepared.amendmentNumber > input.amendmentWarningThreshold
|
|
181
|
+
? { amendmentCount: prepared.amendmentNumber, threshold: input.amendmentWarningThreshold }
|
|
182
|
+
: undefined,
|
|
183
|
+
targetRoot: admission.business.targetRoot,
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
},
|
|
121
187
|
});
|
|
122
|
-
if (
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
188
|
+
if (execution.kind === "noncommitted")
|
|
189
|
+
return execution.outcome;
|
|
190
|
+
if (execution.kind === "contained")
|
|
191
|
+
return { ...execution.outcome, contractId: input.contractId };
|
|
192
|
+
return {
|
|
193
|
+
...execution.outcome,
|
|
194
|
+
contractId: input.contractId,
|
|
195
|
+
amendmentNumber: execution.payload.amendmentNumber,
|
|
196
|
+
...(execution.payload.amendmentWarning ? { amendmentWarning: execution.payload.amendmentWarning } : {}),
|
|
197
|
+
ledgerHead: execution.outcome.committed.head,
|
|
198
|
+
carrier: execution.carrier,
|
|
199
|
+
targetRoot: execution.payload.targetRoot,
|
|
200
|
+
};
|
|
130
201
|
}
|
|
131
202
|
export async function amendKeiyaku(input) {
|
|
132
203
|
await requireGitRepository(input.cwd, "amend");
|
|
133
204
|
const address = input.address ?? await resolveContractAddress(input.cwd, input.contractId, input.contractAddressSource);
|
|
134
205
|
return await withResolvedAddress(address, async () => {
|
|
135
206
|
const contractId = address.binding.commissionId;
|
|
136
|
-
const renderCwd = await resolveContractDeliveryCwd(input.cwd, contractId);
|
|
137
207
|
const amendment = requireAmendment(input.amendment);
|
|
138
|
-
const
|
|
139
|
-
const baseBranch = (await resolveDefaultBranch(input.cwd))?.branch ?? currentBranch;
|
|
208
|
+
const amendmentWarningThreshold = getConfig().amendmentWarning.threshold;
|
|
140
209
|
const amended = await amendContract({
|
|
141
210
|
cwd: input.cwd,
|
|
142
|
-
renderCwd,
|
|
143
211
|
contractId,
|
|
144
212
|
actor: "keiyaku",
|
|
145
213
|
nowMs: Date.now(),
|
|
146
214
|
amendment,
|
|
215
|
+
amendmentWarningThreshold,
|
|
147
216
|
scopeDelta: input.scopeDelta,
|
|
217
|
+
signal: input.signal,
|
|
148
218
|
});
|
|
219
|
+
if (!isCompleteLifecycleOutcome(amended))
|
|
220
|
+
return amended;
|
|
221
|
+
const { targetRoot, ...amendedFacts } = amended;
|
|
222
|
+
const currentBranch = await getCurrentBranch(targetRoot);
|
|
223
|
+
const baseBranch = (await resolveDefaultBranch(input.cwd))?.branch ?? currentBranch;
|
|
149
224
|
return {
|
|
225
|
+
...amendedFacts,
|
|
150
226
|
status: "success",
|
|
151
227
|
amendmentNumber: amended.amendmentNumber,
|
|
152
228
|
currentBranch,
|
|
@@ -154,6 +230,7 @@ export async function amendKeiyaku(input) {
|
|
|
154
230
|
ledgerCommit: amended.ledgerHead,
|
|
155
231
|
summary: `Appended Amendment ${amended.amendmentNumber} to contract ledger ${contractId}.`,
|
|
156
232
|
address,
|
|
233
|
+
carrier: amended.carrier,
|
|
157
234
|
};
|
|
158
235
|
});
|
|
159
236
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { parseToAST } from "./markdown/parser.js";
|
|
2
|
+
import { extractListItems } from "./markdown/render.js";
|
|
3
|
+
import { normalizeSectionTitle } from "./markdown/titles.js";
|
|
4
|
+
/** Criteria declared by one lawful amendment, without imposing bind-document grammar. */
|
|
5
|
+
export function amendmentCriteria(amendment) {
|
|
6
|
+
const criteria = [];
|
|
7
|
+
for (const node of parseToAST(amendment).children) {
|
|
8
|
+
if (node.type !== "section" || node.level !== 2 || normalizeSectionTitle(node.title) !== "criteria")
|
|
9
|
+
continue;
|
|
10
|
+
criteria.push(...extractListItems(node).map((item) => item.trim()).filter(Boolean));
|
|
11
|
+
}
|
|
12
|
+
return criteria;
|
|
13
|
+
}
|
|
14
|
+
/** Bind declarations first, then amendment declarations in accepted lifecycle order. */
|
|
15
|
+
export function effectiveCriteria(bindCriteria, amendments) {
|
|
16
|
+
return [...bindCriteria, ...amendments.flatMap(amendmentCriteria)];
|
|
17
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { currentLifecycleState } from "./repository-ledger/read-model.js";
|
|
2
|
+
export function deriveAmendmentWarning(model, threshold) {
|
|
3
|
+
if (model.history[0]?.entry.kind !== "bind")
|
|
4
|
+
return undefined;
|
|
5
|
+
const state = currentLifecycleState(model);
|
|
6
|
+
if (state.state === "claimed" || state.state === "forfeited")
|
|
7
|
+
return undefined;
|
|
8
|
+
const amendmentCount = model.history.filter(({ entry }) => entry.kind === "amend").length;
|
|
9
|
+
return amendmentCount > threshold ? { amendmentCount, threshold } : undefined;
|
|
10
|
+
}
|
package/build/core/arc.js
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
|
-
import * as fs from "node:fs/promises";
|
|
2
|
-
import * as path from "node:path";
|
|
3
1
|
import { requireText, FlowError } from "../flow-error.js";
|
|
4
2
|
import { getCurrentBranch, requireGitRepository } from "../git/branches.js";
|
|
5
3
|
import { assertCleanTrackedWorkingTree } from "../git/worktree.js";
|
|
6
|
-
import {
|
|
4
|
+
import { ACTOR_IDENTITY } from "../keiyaku.js";
|
|
7
5
|
import { normalizeInlineTitle } from "./markdown/titles.js";
|
|
8
6
|
import { parseCommandMarkdown, requireMarkdownTextSection, requireMarkdownTitle, } from "./command-markdown.js";
|
|
7
|
+
import { isCompleteLifecycleOutcome } from "./outcome-base.js";
|
|
9
8
|
import { resolveContractAddress, withResolvedAddress } from "./addressing.js";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
9
|
+
import { runLifecycle } from "./lifecycle-runner.js";
|
|
10
|
+
import { captureFoldedSeal, findOpenArc } from "./seal.js";
|
|
11
|
+
import { normalizeScopePattern } from "./scope.js";
|
|
12
|
+
import { currentLifecycleState } from "./repository-ledger/read-model.js";
|
|
13
|
+
import { isTerminalState } from "./status/lifecycle.js";
|
|
14
14
|
import { resolveDefaultBranch } from "./status/drift.js";
|
|
15
15
|
import { persistResponseHistory } from "./transcripts.js";
|
|
16
|
-
import { resolveContractDeliveryCwd } from "./worktree-path.js";
|
|
17
16
|
export function buildArcViews(entries) {
|
|
18
17
|
const arcs = new Map();
|
|
19
18
|
for (const entry of entries) {
|
|
@@ -40,13 +39,10 @@ export function findOpenArcView(entries) {
|
|
|
40
39
|
return null;
|
|
41
40
|
return buildArcViews(entries).find((arc) => arc.arc === open.data.arc) ?? null;
|
|
42
41
|
}
|
|
43
|
-
function assertArcOpenable(contractId,
|
|
44
|
-
const state =
|
|
42
|
+
function assertArcOpenable(contractId, model) {
|
|
43
|
+
const state = currentLifecycleState(model);
|
|
45
44
|
if (state.state === "bound" || state.state === "active")
|
|
46
45
|
return;
|
|
47
|
-
if (state.state === "corrupt") {
|
|
48
|
-
throw new FlowError("INTERNAL_STATE", `contract ${contractId} cannot open arc from corrupt state at entry ${state.at}`);
|
|
49
|
-
}
|
|
50
46
|
if (isTerminalState(state) || state.state === "petitioned") {
|
|
51
47
|
throw new FlowError("INTERNAL_STATE", `contract ${contractId} cannot open arc from state ${state.state}`);
|
|
52
48
|
}
|
|
@@ -55,50 +51,166 @@ export async function openArc(input) {
|
|
|
55
51
|
const title = requireText("title", input.title);
|
|
56
52
|
const objective = requireText("objective", input.objective);
|
|
57
53
|
const brief = requireText("brief", input.brief);
|
|
58
|
-
const
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
throw new FlowError("INTERNAL_STATE", `contract ${input.contractId} not found or not active`);
|
|
62
|
-
}
|
|
63
|
-
assertArcOpenable(input.contractId, ledger.entries);
|
|
64
|
-
await appendScopeTerms({
|
|
54
|
+
const patterns = input.appendScope?.map(normalizeScopePattern) ?? [];
|
|
55
|
+
const at = new Date(input.nowMs).toISOString();
|
|
56
|
+
const execution = await runLifecycle({
|
|
65
57
|
cwd: input.cwd,
|
|
66
|
-
renderCwd,
|
|
67
58
|
contractId: input.contractId,
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
59
|
+
mutation: {
|
|
60
|
+
verb: "arc",
|
|
61
|
+
matchesAccepted: (transaction) => {
|
|
62
|
+
const accepted = transaction.entries.at(-1);
|
|
63
|
+
const acceptedAmend = transaction.entries.find((entry) => entry.kind === "amend");
|
|
64
|
+
const acceptedPatterns = acceptedAmend?.kind === "amend" ? acceptedAmend.data.scopeDelta?.add ?? [] : [];
|
|
65
|
+
return accepted?.kind === "arc"
|
|
66
|
+
&& accepted.data.title === title
|
|
67
|
+
&& accepted.data.objective === objective
|
|
68
|
+
&& accepted.data.brief === brief
|
|
69
|
+
&& JSON.stringify(acceptedPatterns) === JSON.stringify(patterns);
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
signal: input.signal,
|
|
73
|
+
admit: (session, replay) => {
|
|
74
|
+
assertArcOpenable(input.contractId, session.lifecycle);
|
|
75
|
+
if (session.target.status !== "available")
|
|
76
|
+
throw new FlowError("INTERNAL_STATE", session.target.reason);
|
|
77
|
+
const accepted = replay.state === "verb-committed" ? replay.transaction.entries.at(-1) : undefined;
|
|
78
|
+
let existing;
|
|
79
|
+
if (accepted?.kind === "arc" && replay.state === "verb-committed") {
|
|
80
|
+
const seal = replay.transaction.entries.find((entry) => entry.kind === "seal");
|
|
81
|
+
existing = {
|
|
82
|
+
transactionObjectId: replay.transaction.transactionObjectId,
|
|
83
|
+
arcNumber: accepted.data.arc,
|
|
84
|
+
capture: seal?.kind === "seal" ? {
|
|
85
|
+
arc: seal.data.arc,
|
|
86
|
+
oldFence: seal.data.oldFence,
|
|
87
|
+
newFence: seal.data.newFence,
|
|
88
|
+
receipts: [...seal.data.receipts],
|
|
89
|
+
diffstat: { ...seal.data.diffstat },
|
|
90
|
+
scopeAudit: { violations: [...seal.data.scopeAudit.violations] },
|
|
91
|
+
scopeViolationEvidence: [],
|
|
92
|
+
} : null,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
return { lifecycle: session.lifecycle, targetRoot: session.target.root, ...(existing ? { existing } : {}) };
|
|
96
|
+
},
|
|
97
|
+
prepare: async (admission) => {
|
|
98
|
+
if (admission.business.existing) {
|
|
99
|
+
return {
|
|
100
|
+
additions: [],
|
|
101
|
+
arcNumber: admission.business.existing.arcNumber,
|
|
102
|
+
capture: admission.business.existing.capture,
|
|
103
|
+
existing: admission.business.existing,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
const scopeEntry = patterns.length === 0 ? null : {
|
|
107
|
+
v: 1,
|
|
108
|
+
kind: "amend",
|
|
109
|
+
contract: input.contractId,
|
|
110
|
+
at,
|
|
111
|
+
actor: input.actor,
|
|
112
|
+
data: { amendment: "Scope extended by lifecycle command.", scopeDelta: { add: patterns } },
|
|
113
|
+
};
|
|
114
|
+
const capture = await captureFoldedSeal(admission.business.targetRoot, admission.business.lifecycle, "HEAD", patterns);
|
|
115
|
+
const arcNumber = admission.business.lifecycle.intent.arc + 1;
|
|
116
|
+
let sealEntry = null;
|
|
117
|
+
if (capture) {
|
|
118
|
+
const { scopeViolationEvidence: _evidence, ...durableCapture } = capture;
|
|
119
|
+
sealEntry = {
|
|
120
|
+
v: 2,
|
|
121
|
+
kind: "seal",
|
|
122
|
+
contract: input.contractId,
|
|
123
|
+
at,
|
|
124
|
+
actor: "engine",
|
|
125
|
+
data: { ...durableCapture, sealedBy: "arc" },
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
const arcEntry = {
|
|
129
|
+
v: 1,
|
|
130
|
+
kind: "arc",
|
|
131
|
+
contract: input.contractId,
|
|
132
|
+
at,
|
|
133
|
+
actor: input.actor,
|
|
134
|
+
data: { arc: arcNumber, title, objective, brief },
|
|
135
|
+
};
|
|
136
|
+
const additions = scopeEntry
|
|
137
|
+
? sealEntry ? [scopeEntry, sealEntry, arcEntry] : [scopeEntry, arcEntry]
|
|
138
|
+
: sealEntry ? [sealEntry, arcEntry] : [arcEntry];
|
|
139
|
+
return { additions, arcNumber, capture };
|
|
140
|
+
},
|
|
141
|
+
revalidate: async ({ session, prepared }) => {
|
|
142
|
+
assertArcOpenable(input.contractId, session.lifecycle);
|
|
143
|
+
if (session.target.status !== "available")
|
|
144
|
+
return { code: "CARRIER_TARGET_UNAVAILABLE", message: session.target.reason };
|
|
145
|
+
if (prepared.existing) {
|
|
146
|
+
return session.repository.topology.nodes.some((node) => node.objectId === prepared.existing.transactionObjectId)
|
|
147
|
+
? undefined
|
|
148
|
+
: { code: "ARC_STATE_CHANGED", message: "accepted arc changed during replay preparation" };
|
|
149
|
+
}
|
|
150
|
+
await assertCleanTrackedWorkingTree(session.target.root);
|
|
151
|
+
return session.lifecycle.intent.arc + 1 === prepared.arcNumber
|
|
152
|
+
? undefined
|
|
153
|
+
: { code: "ARC_NUMBER_CHANGED", message: "arc sequence changed during preparation" };
|
|
154
|
+
},
|
|
155
|
+
effect: async ({ context, admission, prepared }) => {
|
|
156
|
+
const result = prepared.existing
|
|
157
|
+
? await context.replayCommitted({ transactionObjectId: prepared.existing.transactionObjectId })
|
|
158
|
+
: await context.publishAuthoritative({
|
|
159
|
+
verb: "arc",
|
|
160
|
+
admittedLedgerHead: admission.ledgerHead,
|
|
161
|
+
acceptedLifecycleTail: context.session.lifecycle.history.at(-1).objectId,
|
|
162
|
+
entries: prepared.additions,
|
|
163
|
+
});
|
|
164
|
+
if (result.status === "indeterminate") {
|
|
165
|
+
return { kind: "precommit-effects-only", authoritativeHead: result.currentHead ?? admission.ledgerHead };
|
|
166
|
+
}
|
|
167
|
+
if (result.status !== "committed" || prepared.arcNumber === 0) {
|
|
168
|
+
return { kind: "refused-before-effects", authoritativeHead: result.status === "committed" ? result.head : result.currentHead ?? context.session.lifecycle.observedLedgerHead, refusal: { code: "LEDGER_CAS_CONFLICT", message: `contract ${input.contractId} changed during arc` } };
|
|
169
|
+
}
|
|
170
|
+
const committedCapture = prepared.capture;
|
|
171
|
+
const sealed = committedCapture && {
|
|
172
|
+
...committedCapture,
|
|
173
|
+
sealedBy: "arc",
|
|
174
|
+
ledgerHead: result.transaction.entryObjectIds[result.transaction.entries.findIndex((entry) => entry.kind === "seal")],
|
|
175
|
+
};
|
|
176
|
+
let response;
|
|
177
|
+
if (input.persistResponse) {
|
|
178
|
+
await context.journal.run({
|
|
179
|
+
effect: "response-history-persistence",
|
|
180
|
+
run: async () => {
|
|
181
|
+
response = await input.persistResponse({ ledgerHead: result.head, sealed });
|
|
182
|
+
return response;
|
|
183
|
+
},
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
kind: "committed",
|
|
188
|
+
commit: result,
|
|
189
|
+
entryKind: "arc",
|
|
190
|
+
value: {
|
|
191
|
+
arc: prepared.arcNumber,
|
|
192
|
+
sealed,
|
|
193
|
+
targetRoot: admission.business.targetRoot,
|
|
194
|
+
...(response ?? {}),
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
},
|
|
71
198
|
});
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
199
|
+
if (execution.kind === "noncommitted")
|
|
200
|
+
return execution.outcome;
|
|
201
|
+
if (execution.kind === "contained")
|
|
202
|
+
return { ...execution.outcome, contractId: input.contractId };
|
|
203
|
+
return {
|
|
204
|
+
...execution.outcome,
|
|
76
205
|
contractId: input.contractId,
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
throw new FlowError("INTERNAL_STATE", `contract ${input.contractId} still has an open arc after sealing`);
|
|
86
|
-
}
|
|
87
|
-
arcNumber = getMaxArc(entries) + 1;
|
|
88
|
-
return {
|
|
89
|
-
v: 1,
|
|
90
|
-
kind: "arc",
|
|
91
|
-
contract: input.contractId,
|
|
92
|
-
at,
|
|
93
|
-
actor: input.actor,
|
|
94
|
-
data: { arc: arcNumber, title, objective, brief },
|
|
95
|
-
};
|
|
96
|
-
});
|
|
97
|
-
if (!result.ok || arcNumber === 0) {
|
|
98
|
-
throw new FlowError("INTERNAL_STATE", `contract ${input.contractId} active ledger disappeared during arc`);
|
|
99
|
-
}
|
|
100
|
-
await refreshContractView({ ledgerCwd: input.cwd, renderCwd, contractId: input.contractId });
|
|
101
|
-
return { contractId: input.contractId, arc: arcNumber, ledgerHead: result.head, sealed };
|
|
206
|
+
arc: execution.payload.arc,
|
|
207
|
+
ledgerHead: execution.outcome.committed.head,
|
|
208
|
+
sealed: execution.payload.sealed,
|
|
209
|
+
carrier: execution.carrier,
|
|
210
|
+
targetRoot: execution.payload.targetRoot,
|
|
211
|
+
...(execution.payload.responsePath ? { responsePath: execution.payload.responsePath } : {}),
|
|
212
|
+
...(execution.payload.artifactId ? { artifactId: execution.payload.artifactId } : {}),
|
|
213
|
+
};
|
|
102
214
|
}
|
|
103
215
|
export function buildArcInput(content, cwd, appendScope = [], contractId, contractAddressSource) {
|
|
104
216
|
const parts = parseCommandMarkdown(content);
|
|
@@ -122,21 +234,15 @@ export async function arcKeiyaku(input) {
|
|
|
122
234
|
const address = input.address ?? await resolveContractAddress(input.cwd, input.contractId, input.contractAddressSource);
|
|
123
235
|
return await withResolvedAddress(address, async () => {
|
|
124
236
|
const contractId = address.binding.commissionId;
|
|
125
|
-
const renderCwd = await resolveContractDeliveryCwd(input.cwd, contractId);
|
|
126
|
-
await assertCleanTrackedWorkingTree(renderCwd);
|
|
127
|
-
try {
|
|
128
|
-
await fs.access(path.join(renderCwd, KEIYAKU_FILE));
|
|
129
|
-
}
|
|
130
|
-
catch {
|
|
131
|
-
throw new FlowError("MISSING_PROTOCOL_FILES", `missing protocol file for current keiyaku (${KEIYAKU_FILE})`);
|
|
132
|
-
}
|
|
133
237
|
const title = normalizeInlineTitle(requireText("title", input.title));
|
|
134
238
|
const objective = requireText("objective", input.objective);
|
|
135
239
|
const brief = requireText("brief", input.brief);
|
|
136
240
|
const nowMs = Date.now();
|
|
241
|
+
const summaryFor = (sealed, arc) => sealed
|
|
242
|
+
? `a${sealed.arc} sealed; a${arc} open: ${title}.`
|
|
243
|
+
: `a${arc} open: ${title}.`;
|
|
137
244
|
const opened = await openArc({
|
|
138
245
|
cwd: input.cwd,
|
|
139
|
-
renderCwd,
|
|
140
246
|
contractId,
|
|
141
247
|
actor: ACTOR_IDENTITY,
|
|
142
248
|
nowMs,
|
|
@@ -144,33 +250,27 @@ export async function arcKeiyaku(input) {
|
|
|
144
250
|
objective,
|
|
145
251
|
brief,
|
|
146
252
|
appendScope: input.appendScope,
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
const summary = opened.sealed
|
|
150
|
-
? `a${opened.sealed.arc} sealed; a${opened.arc} open: ${title}.`
|
|
151
|
-
: `a${opened.arc} open: ${title}.`;
|
|
152
|
-
let responsePath;
|
|
153
|
-
let artifactId;
|
|
154
|
-
try {
|
|
155
|
-
const persisted = await persistResponseHistory({
|
|
253
|
+
signal: input.signal,
|
|
254
|
+
persistResponse: async ({ ledgerHead, sealed }) => persistResponseHistory({
|
|
156
255
|
cwd: input.cwd,
|
|
157
256
|
tool: "arc",
|
|
158
257
|
provenance: { authority: { kind: "repository" }, binding: address.binding, evidenceCwd: input.cwd },
|
|
159
258
|
title,
|
|
160
259
|
objective,
|
|
161
260
|
brief,
|
|
162
|
-
ledgerCommit:
|
|
163
|
-
summary,
|
|
261
|
+
ledgerCommit: ledgerHead,
|
|
262
|
+
summary: summaryFor(sealed, (sealed?.arc ?? 0) + 1),
|
|
164
263
|
emptyDiffLabel: "working tree untouched",
|
|
165
|
-
...(
|
|
166
|
-
})
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
264
|
+
...(sealed ? { sealed } : {}),
|
|
265
|
+
}),
|
|
266
|
+
});
|
|
267
|
+
if (!isCompleteLifecycleOutcome(opened))
|
|
268
|
+
return opened;
|
|
269
|
+
const { targetRoot, ...openedFacts } = opened;
|
|
270
|
+
const { currentBranch, baseBranch } = await readBranchLabels(targetRoot);
|
|
271
|
+
const summary = summaryFor(opened.sealed, opened.arc);
|
|
173
272
|
return {
|
|
273
|
+
...openedFacts,
|
|
174
274
|
status: "success",
|
|
175
275
|
arc: opened.arc,
|
|
176
276
|
currentBranch,
|
|
@@ -184,9 +284,10 @@ export async function arcKeiyaku(input) {
|
|
|
184
284
|
brief,
|
|
185
285
|
ledgerCommit: opened.ledgerHead,
|
|
186
286
|
sealed: opened.sealed ?? undefined,
|
|
187
|
-
responsePath,
|
|
188
|
-
artifactId,
|
|
287
|
+
responsePath: opened.responsePath,
|
|
288
|
+
artifactId: opened.artifactId,
|
|
189
289
|
address,
|
|
290
|
+
carrier: opened.carrier,
|
|
190
291
|
};
|
|
191
292
|
});
|
|
192
293
|
}
|