@mstar-harness/engine 3.9.3 → 3.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,170 @@
1
+ import type { ValidationResult } from "./core.js";
2
+ /** Stable refusal codes of the scoped coordination surface (spec §C4). */
3
+ export declare const COORDINATION_ERROR_CODES: readonly ["coordination.harness-not-found", "coordination.workflow-not-found", "coordination.plan-not-found", "coordination.scope-mismatch", "coordination.path-mismatch", "coordination.assignment-invalid", "coordination.assignment-stale", "coordination.not-prepared", "coordination.duplicate-holder", "coordination.session-mismatch", "coordination.session-not-found", "coordination.session-role", "coordination.version-conflict", "coordination.expected-version-required", "coordination.invalid-transition", "coordination.invalid-input", "coordination.forbidden-field", "coordination.not-in-git", "coordination.git-unavailable", "coordination.git-proof", "coordination.evidence-stale", "coordination.integration-unresolved", "coordination.integration-diverged", "coordination.local-store-required", "coordination.direct-write-refused", "coordination.scoped-writer-required", "coordination.unknown-operation", "coordination.store", "coordination.prepare-amendment.stale", "coordination.prepare-amendment.invalid-patch", "coordination.prepare-amendment.not-prepare", "coordination.prepare-amendment.execution-started", "coordination.prepare-amendment.duplicate-plan", "coordination.prepare-amendment.invalid-plan", "coordination.prepare-amendment.compass-mismatch", "coordination.prepare-amendment.invalid-worktree"];
4
+ export type CoordinationErrorCode = (typeof COORDINATION_ERROR_CODES)[number];
5
+ /**
6
+ * Stable exception of the coordination surface: `code` is the consumer
7
+ * contract, `details` carries the machine-readable context (path, expected,
8
+ * actual, holder, …). Exported publicly from `coordination.ts`.
9
+ */
10
+ export declare class CoordinationError extends Error {
11
+ readonly code: CoordinationErrorCode;
12
+ readonly details: Record<string, unknown>;
13
+ constructor(code: CoordinationErrorCode, message: string, details?: Record<string, unknown>);
14
+ }
15
+ export declare function isPlainObject(value: unknown): value is Record<string, unknown>;
16
+ /** Non-empty trimmed string predicate. */
17
+ export declare function isNonEmptyString(value: unknown): value is string;
18
+ /** `sha256:<64 lowercase hex>` of the exact bytes handed in. */
19
+ export declare function artifactVersion(bytes: Buffer | string): string;
20
+ /** SHA-256 (bare lowercase hex) of the exact bytes handed in. */
21
+ export declare function sha256Bytes(bytes: Buffer | string): string;
22
+ /** Artifact bytes read once: payload + byte version from that same read. */
23
+ export type ArtifactBytes = {
24
+ payload: unknown;
25
+ version: string;
26
+ };
27
+ /**
28
+ * Read an artifact once and derive both payload and version from the same
29
+ * bytes. Missing file → `undefined` (the caller reports `absent`).
30
+ * Malformed JSON throws (never a silent empty document).
31
+ */
32
+ export declare function readArtifactBytes(filePath: string): ArtifactBytes | undefined;
33
+ /**
34
+ * Canonicalize a target: realpath when it exists (symlinks resolved), else the
35
+ * realpath of its nearest existing ancestor with the missing tail re-attached.
36
+ * Aliases therefore collapse onto the real protected file whether or not the
37
+ * leaf has been created yet, so a `json`/symlink ref can never dodge the
38
+ * boundary. A lexical fallback would do exactly that: a symlinked parent stays
39
+ * unresolved, the alias classifies as unprotected, and `put` creates the
40
+ * protected document through it.
41
+ *
42
+ * The same rule as `path.ts#canonicalizeNearestExisting`, restated here because
43
+ * `path.ts` imports this module (importing back would close an ESM cycle).
44
+ */
45
+ export declare function canonicalTarget(target: string): string;
46
+ /** Protected document class, decided by the caller's resolved path table. */
47
+ export type ProtectedWriteKind = "root" | "snapshot" | "register";
48
+ /**
49
+ * Run `fn` inside the private authorization context for `target`. Only the
50
+ * locked coordination/writer implementation calls this; the context records
51
+ * canonical target + operation, never a caller-supplied boolean, and nested
52
+ * authorizations stack (a residual write authorizes both the snapshot and
53
+ * the register it touches).
54
+ */
55
+ export declare function withProtectedWrite<T>(target: string, operation: "put" | "delete", fn: () => T | Promise<T>): Promise<T>;
56
+ /** `true` when the current async context authorized exactly this target+operation. */
57
+ export declare function isWriteAuthorized(target: string, operation: "put" | "delete"): boolean;
58
+ /**
59
+ * Refuse an un-authorized write to a protected target (spec §C4). The
60
+ * target's class is decided by the store's resolved path table (never by
61
+ * document content, which a caller could shape); the authorization is the
62
+ * private context alone.
63
+ */
64
+ export declare function assertProtectedWriteAuthorized(target: string, operation: "put" | "delete", kind: ProtectedWriteKind): void;
65
+ /** Exact-key contract: unknown keys anywhere are rejected before mutation. */
66
+ export declare function assertExactKeys(value: Record<string, unknown>, allowed: readonly string[], what: string): void;
67
+ /** Row/progress status a plan session may report (`progress` op). */
68
+ export type PlanProgressStatus = "InProgress" | "InReview" | "Blocked";
69
+ /** Progress payload a plan session reports on its own row. */
70
+ export type PlanProgress = {
71
+ status: PlanProgressStatus;
72
+ summary: string;
73
+ /** Canonical absolute artifacts inside the plan's own plan/SDD area. */
74
+ evidence_paths: string[];
75
+ /** L2 track branches reported for this plan (never main/integration). */
76
+ track_branches?: string[];
77
+ };
78
+ /** Hash-pinned reference to a submitted evidence file. */
79
+ export type EvidenceRef = {
80
+ path: string;
81
+ sha256: string;
82
+ };
83
+ /** Handoff lifecycle state (`PlanHandoff.state`). */
84
+ export type HandoffState = "submitted" | "accepted" | "returned" | "integrating" | "merged" | "completed";
85
+ /** QC review outcome recorded on a handoff. */
86
+ export type HandoffQc = {
87
+ decision: string;
88
+ reports: EvidenceRef[];
89
+ consolidated: EvidenceRef;
90
+ };
91
+ /** QA verification outcome recorded on a handoff. */
92
+ export type HandoffQa = {
93
+ gate: string;
94
+ decision: string;
95
+ report: EvidenceRef;
96
+ };
97
+ /** The single integration attempt recorded on a handoff. */
98
+ export type HandoffIntegration = {
99
+ target_branch: string;
100
+ worktree_path: string;
101
+ base_sha: string;
102
+ started_at: string;
103
+ result_sha?: string;
104
+ verified_at?: string;
105
+ };
106
+ /** The durable review package of one plan (spec §B `PlanHandoff`). */
107
+ export type PlanHandoff = {
108
+ id: string;
109
+ attempt: number;
110
+ state: HandoffState;
111
+ submitted_by: string;
112
+ submitted_at: string;
113
+ source_branch: string;
114
+ source_sha: string;
115
+ worktree_path: string;
116
+ review_base: string;
117
+ review_head: string;
118
+ qc: HandoffQc;
119
+ qa: HandoffQa;
120
+ accepted_by?: string;
121
+ accepted_at?: string;
122
+ returned_at?: string;
123
+ return_reason?: string;
124
+ integration?: HandoffIntegration;
125
+ completed_at?: string;
126
+ };
127
+ /** Coordinator-recorded preparation of one plan (spec §D `prepare`). */
128
+ export type PreparedCoordination = {
129
+ assignment_path: string;
130
+ assignment_sha256: string;
131
+ plan_sha256: string;
132
+ qa_gate: string;
133
+ findings_cleanup: string;
134
+ prepared_by: string;
135
+ prepared_at: string;
136
+ };
137
+ /** A bound session: identity + the canonical envelope that proves it. */
138
+ export type CoordinatorBinding = {
139
+ session_id: string;
140
+ session_file: string;
141
+ bound_at: string;
142
+ };
143
+ /** Snapshot-level coordination block (the workflow's coordinator). */
144
+ export type SnapshotCoordination = {
145
+ coordinator: CoordinatorBinding;
146
+ };
147
+ /** Row-level coordination block (one plan). */
148
+ export type RowCoordination = {
149
+ revision: number;
150
+ prepared?: PreparedCoordination;
151
+ session?: CoordinatorBinding;
152
+ progress?: PlanProgress;
153
+ handoff?: PlanHandoff;
154
+ };
155
+ export declare const HANDOFF_STATES: readonly HandoffState[];
156
+ export declare const PLAN_PROGRESS_STATUSES: readonly PlanProgressStatus[];
157
+ /** Validate a stored `PlanProgress` (`status`, `summary`, `evidence_paths`, `track_branches`). */
158
+ export declare function validatePlanProgress(value: unknown, what?: string): ValidationResult[];
159
+ /** Validate a stored `PlanHandoff`, including its state/field coherence. */
160
+ export declare function validatePlanHandoff(value: unknown, what?: string): ValidationResult[];
161
+ /** Validate a stored `PreparedCoordination`. */
162
+ export declare function validatePreparedCoordination(value: unknown, what?: string): ValidationResult[];
163
+ /** Validate one plan row's `coordination` object (spec §C2). */
164
+ export declare function validateRowCoordination(value: unknown, what?: string): ValidationResult[];
165
+ /** Validate a snapshot's top `coordination` block (spec §C2). */
166
+ export declare function validateSnapshotCoordination(value: unknown, what?: string): ValidationResult[];
167
+ /** Hash-pinned evidence reference for an absolute path, read from disk. */
168
+ export declare function evidenceRefOf(filePath: string): EvidenceRef;
169
+ /** Whether `value` is a well-formed `sha256:<64 hex>` artifact version. */
170
+ export declare function isArtifactVersion(value: unknown): value is string;
@@ -0,0 +1,369 @@
1
+ import { CoordinationError, type PlanProgress, type PreparedCoordination } from "./coordination-write.js";
2
+ import { type PlanRow } from "./status.js";
3
+ import { type ArtifactRef } from "./store.js";
4
+ /**
5
+ * Re-exported from the storage layer because this module is the public entry
6
+ * point for the scoped writers: callers catch `CoordinationError` and branch
7
+ * on its `code` without importing the storage layer directly.
8
+ */
9
+ export { CoordinationError };
10
+ /** Bind roles: one coordinator per lifecycle, one plan session per plan. */
11
+ export type CoordinationRole = "plan-pm" | "coordinator";
12
+ /**
13
+ * Scope address, both forms required by spec §B: from a pinned Assignment
14
+ * path, or from a workflow/plan pair resolved through that row's `prepared`
15
+ * block. Both forms resolve to the same `ResolvedPlanScope`.
16
+ */
17
+ export type PlanScopeInput = {
18
+ assignmentPath: string;
19
+ } | {
20
+ workflowId: string;
21
+ planId: string;
22
+ harnessDir?: string;
23
+ };
24
+ /** The fully pinned plan scope (spec §B `ResolvedPlanScope`). */
25
+ export type ResolvedPlanScope = {
26
+ harnessRoot: string;
27
+ workflowId: string;
28
+ planId: string;
29
+ /** `{WORKFLOW_DIR}/<workflow-id>/snapshot.json`. */
30
+ snapshotPath: string;
31
+ /** The plan markdown pinned by the Assignment `Plan Path`. */
32
+ planPath: string;
33
+ assignmentPath: string;
34
+ worktreePath: string;
35
+ workingBranch: string;
36
+ projectId: string;
37
+ sddDir: string;
38
+ };
39
+ /**
40
+ * Session envelope persisted at
41
+ * `{WORKFLOW_DIR}/<workflow-id>/sessions/<session-id>.json` (mode `0600`).
42
+ * The envelope is the durable proof of who holds the session: the snapshot
43
+ * stores its canonical path and every later call must present the same file.
44
+ */
45
+ export type CoordinationSession = {
46
+ schema_version: 1;
47
+ role: CoordinationRole;
48
+ session_id: string;
49
+ workflow_id: string;
50
+ plan_id?: string;
51
+ harness_root: string;
52
+ };
53
+ /**
54
+ * Bind addressing (spec §B). A fresh bind never supplies a session path: the
55
+ * engine generates the session UUID and creates
56
+ * `<workflow-dir>/<workflow-id>/sessions/<session-id>.json` itself. An existing
57
+ * session is reached only through its explicit `resumePath`, which resumes
58
+ * read-only and never writes.
59
+ */
60
+ export type BindPlanSessionInput = {
61
+ scope: PlanScopeInput;
62
+ cwd: string;
63
+ } | {
64
+ coordinator: true;
65
+ workflowId: string;
66
+ harnessDir?: string;
67
+ cwd: string;
68
+ } | {
69
+ resumePath: string;
70
+ cwd: string;
71
+ };
72
+ /** One artifact read: payload plus the byte version it was read at. */
73
+ export type VersionedArtifact = {
74
+ payload: unknown;
75
+ version: string;
76
+ };
77
+ /** Everything `mstar plan show` needs, in one read. */
78
+ export type PlanCoordinationView = {
79
+ /** Row `coordination.revision` (`0` when the row is not yet coordinated). */
80
+ revision: number;
81
+ /** `sha256:…` of the snapshot bytes, or `"absent"`. */
82
+ snapshot_version: string;
83
+ /** `sha256:…` of this plan's project register bytes, or `"absent"`. */
84
+ register_version: string;
85
+ /** `null` while the row carries no `prepared` block. */
86
+ scope: ResolvedPlanScope | null;
87
+ row: PlanRow;
88
+ prepared?: PreparedCoordination;
89
+ session: CoordinationSession;
90
+ session_file: string;
91
+ /** Operations this session may run now — implemented operations only. */
92
+ allowed_operations: string[];
93
+ };
94
+ /** Success shape of a coordination call. */
95
+ export type CoordinationResult = {
96
+ ok: true;
97
+ operation: string;
98
+ session: CoordinationSession;
99
+ session_file: string;
100
+ /** `claimed` / `resumed` / `prepared` / `progressed` / `residual-added` / `residual-closed`. */
101
+ outcome?: string;
102
+ view?: PlanCoordinationView;
103
+ };
104
+ /** One residual being registered: the nine v1 fields (+ optional `detail_doc`). */
105
+ export type ResidualInput = {
106
+ id: string;
107
+ title: string;
108
+ severity: string;
109
+ source: string;
110
+ scope: string;
111
+ decision: string;
112
+ owner: string;
113
+ target: string;
114
+ tracking: string;
115
+ detail_doc?: string;
116
+ };
117
+ export type PrepareCoordinationRequest = {
118
+ kind: "prepare";
119
+ /** The plan's pinned Assignment (absolute). */
120
+ assignmentPath: string;
121
+ expectedRevision: number;
122
+ };
123
+ export type ProgressCoordinationRequest = {
124
+ kind: "progress";
125
+ progress: PlanProgress;
126
+ expectedRevision: number;
127
+ };
128
+ export type ResidualAddCoordinationRequest = {
129
+ kind: "residual-add";
130
+ entries: ResidualInput[];
131
+ expectedRegisterVersion: string;
132
+ /** Optional extra guard: the row revision must still match. */
133
+ expectedRevision?: number;
134
+ };
135
+ export type ResidualCloseCoordinationRequest = {
136
+ kind: "residual-close";
137
+ entryId: string;
138
+ note: string;
139
+ expectedRegisterVersion: string;
140
+ expectedRevision?: number;
141
+ };
142
+ /**
143
+ * Handoff evidence (spec §D). Slice A types it so the union is stable; the
144
+ * operations that consume it arrive with the handoff slice.
145
+ */
146
+ export type HandoffEvidence = {
147
+ source_sha: string;
148
+ review_base: string;
149
+ review_head: string;
150
+ qc: {
151
+ decision: "Approve" | "Approve with residuals";
152
+ reports: string[];
153
+ consolidated: string;
154
+ };
155
+ qa: {
156
+ gate: "mandatory" | "pm-acceptance";
157
+ decision: "pass";
158
+ report: string;
159
+ };
160
+ };
161
+ /**
162
+ * The operation surface (spec §B): every kind is implemented and typed, so an
163
+ * unknown shape is refused as `coordination.invalid-input` rather than
164
+ * silently accepted.
165
+ */
166
+ export type PlanCoordinationOperation = {
167
+ kind: "prepare";
168
+ assignmentPath: string;
169
+ } | {
170
+ kind: "progress";
171
+ progress: PlanProgress;
172
+ } | {
173
+ kind: "residual-add";
174
+ entries: ResidualInput[];
175
+ expectedRegisterVersion: string;
176
+ } | {
177
+ kind: "residual-close";
178
+ entryId: string;
179
+ note: string;
180
+ expectedRegisterVersion: string;
181
+ } | {
182
+ kind: "handoff";
183
+ evidence: HandoffEvidence;
184
+ } | {
185
+ kind: "accept";
186
+ handoffId: string;
187
+ } | {
188
+ kind: "return";
189
+ handoffId: string;
190
+ reason: string;
191
+ } | {
192
+ kind: "integration-start";
193
+ handoffId: string;
194
+ } | {
195
+ kind: "integration-accept";
196
+ handoffId: string;
197
+ } | {
198
+ kind: "complete";
199
+ handoffId: string;
200
+ } | {
201
+ kind: "reconcile";
202
+ handoffId: string;
203
+ };
204
+ /** One whole coordination request: one session, one operation, one precondition. */
205
+ export type CoordinationRequest = {
206
+ sessionPath: string;
207
+ /** Required for a coordinator session; never another plan for a plan session. */
208
+ planId?: string;
209
+ /** The selected row's `coordination.revision` from `show` (absent row = 0). */
210
+ expectedRevision: number;
211
+ operation: PlanCoordinationOperation;
212
+ };
213
+ /** Coordinator replacement of a coordinated artifact (spec §B). */
214
+ export type CoordinatedReplacement = {
215
+ harnessRoot: string;
216
+ ref: ArtifactRef;
217
+ payload: unknown;
218
+ /** Byte version the writer expects (`sha256:…`, or `absent` to create). */
219
+ expectedVersion: string;
220
+ /** Required for snapshot replacement (the coordinator session envelope). */
221
+ sessionPath?: string;
222
+ };
223
+ /**
224
+ * The process-wide harness root (spec §C1): resolved from the **main**
225
+ * worktree's root, never from the checkout the process happens to sit in.
226
+ * This is the fix for the process-root bug — a process inside a linked
227
+ * worktree must not resolve the worktree's own `.mstar`.
228
+ *
229
+ * Returns `null` when no harness root is resolvable (the unscoped CLI keeps
230
+ * its existing non-Git fallback). Throws `coordination.not-in-git` when the
231
+ * cwd is provably a linked checkout (a `.git` **file**) whose main worktree
232
+ * cannot be read — falling back to local artifacts there is exactly the bug.
233
+ */
234
+ export declare function resolveProcessHarnessDir(cwd?: string, harnessDir?: string): string | null;
235
+ /**
236
+ * Resolve the plan scope (spec §B). Form A pins the scope from the Assignment
237
+ * itself; form B starts from `{workflowId, planId}` and reads the pinned
238
+ * Assignment path out of that row's `prepared` block — never "the first
239
+ * unfinished row".
240
+ */
241
+ export declare function resolvePlanScope(input: PlanScopeInput, cwd?: string): Promise<ResolvedPlanScope>;
242
+ /** Read and validate a session envelope (throws `coordination.session-*`). */
243
+ export declare function readSessionEnvelope(sessionPath: string): CoordinationSession;
244
+ /**
245
+ * Read this session's plan coordination view (spec §B). A coordinator session
246
+ * must select a plan; a plan session reads only its own row. A row that is not
247
+ * yet prepared yields `scope: null` and the raw row.
248
+ */
249
+ export declare function readPlanCoordination(sessionPath: string, planId?: string, cwd?: string): Promise<PlanCoordinationView>;
250
+ /**
251
+ * Read one coordinated artifact plus its byte version, from a **single** byte
252
+ * read. `payload` is `undefined` and `version` is `"absent"` when the document
253
+ * does not exist. Snapshot payloads are validated before they are handed out.
254
+ */
255
+ export declare function readCoordinatedArtifact(harnessRoot: string, ref: ArtifactRef): Promise<VersionedArtifact>;
256
+ /**
257
+ * Bind a session (spec §C2). A fresh bind creates the envelope, claims the L1
258
+ * lease and records the binding in the same snapshot commit; an existing
259
+ * envelope is a **read-only** resume that only re-verifies the binding.
260
+ */
261
+ /**
262
+ * Bind a session (spec §B, §C2). Fresh addressing never supplies a session
263
+ * path: the engine generates the UUID and creates the envelope. `resumePath`
264
+ * names an existing envelope and resumes read-only.
265
+ */
266
+ export declare function bindPlanSession(input: BindPlanSessionInput): Promise<CoordinationResult>;
267
+ /**
268
+ * Run one coordinated mutation (spec §B/§D). Every call re-authenticates the
269
+ * session from its envelope, re-checks the Assignment hash, enforces the
270
+ * revision/register precondition under the lock, and writes through
271
+ * `withProtectedWrite`. The operation surface is a closed discriminated union:
272
+ * an unknown key anywhere is rejected before any state is touched.
273
+ */
274
+ export declare function mutatePlanCoordination(request: CoordinationRequest): Promise<CoordinationResult>;
275
+ /**
276
+ * Replace a coordinated artifact with an exact-version precondition (spec §B,
277
+ * §C4 line 156). Snapshot replacement goes through the canonical snapshot
278
+ * writer (coordinator session, phase-only delta, locked CAS); the root status
279
+ * and a project register are written with the same byte-version CAS under
280
+ * root → snapshot → destination locks, refusing any document that carries
281
+ * coordinated ownership. `review`/`json` are not coordinated artifacts, so
282
+ * they refuse explicitly instead of silently no-opping.
283
+ */
284
+ export declare function replaceCoordinatedArtifact(input: CoordinatedReplacement): Promise<VersionedArtifact>;
285
+ /**
286
+ * The guarded, coordinator-authenticated Prepare-stage amendment (§ New API and
287
+ * CLI, frozen). One approved plan row: the plan markdown that carries it plus
288
+ * the metadata the row records. Nothing else is addressable — the verb cannot
289
+ * edit an existing row.
290
+ */
291
+ export type PreparePlanAppend = Readonly<{
292
+ id: string;
293
+ title: string;
294
+ file: string;
295
+ metadata: Readonly<{
296
+ primary_spec: string;
297
+ spec_refs: readonly string[];
298
+ iteration_compass: string;
299
+ iteration_refs: readonly string[];
300
+ working_branch: string;
301
+ spec_integration_branch: string;
302
+ merge_target: string;
303
+ }>;
304
+ }>;
305
+ /**
306
+ * The whole structural delta one amendment may apply (§ Admission and mutation
307
+ * step 5): the caller's main-worktree branch, the approved plan appends, the
308
+ * reviewed integration checkout, and the single approved execution-policy key.
309
+ */
310
+ export type PrepareWorkflowPatch = Readonly<{
311
+ mainWorktreeBranch: string;
312
+ appendPlans: readonly PreparePlanAppend[];
313
+ integrationWorktreePath?: string;
314
+ planParallelism?: "serial" | "parallel";
315
+ }>;
316
+ /** What a coordinator observes about one workflow before amending it. */
317
+ export type PrepareWorkflowView = Readonly<{
318
+ workflowId: string;
319
+ /** `sha256:<64 hex>` of the snapshot bytes (the amendment CAS token). */
320
+ snapshotVersion: string;
321
+ /** `sha256:<64 hex>` of the reviewed compass Markdown bytes (CAS token). */
322
+ compassVersion: string;
323
+ /** Plan ids in row order — including this call's own appends on success. */
324
+ planIds: readonly string[];
325
+ /** `true` when `amendPrepareWorkflow` is admitted with fresh tokens. */
326
+ allowed: boolean;
327
+ /** One `<reason>: <message>` line per admission blocker; empty when allowed. */
328
+ blockers: readonly string[];
329
+ }>;
330
+ /** Success shape of the verb: the existing envelope with a workflow view. */
331
+ export type PrepareWorkflowResult = Omit<CoordinationResult, "view"> & {
332
+ view: PrepareWorkflowView;
333
+ };
334
+ /**
335
+ * Read the workflow-level Prepare view of one coordinator-bound workflow
336
+ * (spec § New API and CLI). Read-only: no snapshot lock, nothing written. The
337
+ * two byte versions are the tokens `amendPrepareWorkflow` requires, and
338
+ * `allowed`/`blockers` report the Prepare/no-execution admission exactly as the
339
+ * mutation evaluates it — an inadmissible *lifecycle state* is readable, not an
340
+ * error, so a caller can inspect a workflow before deciding to amend it.
341
+ * Problems that make the documents or the caller's identity unusable — a
342
+ * missing/foreign/mismatched envelope, an unregistered or unreadable snapshot,
343
+ * an unreadable or borrowed compass — still refuse with their own code, because
344
+ * no trustworthy answer can be produced from them.
345
+ */
346
+ export declare function showPrepareWorkflow(input: Readonly<{
347
+ sessionPath: string;
348
+ cwd?: string;
349
+ }>): Promise<PrepareWorkflowResult>;
350
+ /**
351
+ * Apply one approved Prepare structural amendment (spec § Admission and
352
+ * mutation). The CAS read, both version comparisons, the admission, the whole
353
+ * patch validation, the compass recheck and the single atomic write all run
354
+ * under the canonical snapshot write lock, so two callers presenting the same
355
+ * tokens cannot both succeed: the loser inspects the winner's bytes and
356
+ * refuses as `stale`. Every refusal happens before any write — the protected
357
+ * snapshot, root register, other workflows and the compass stay byte-identical.
358
+ *
359
+ * A lock that cannot be acquired refuses explicitly (the shared
360
+ * `withStatusWriteLock` Blocked error); Git-unavailable probes refuse through
361
+ * the existing `coordination.git-unavailable`.
362
+ */
363
+ export declare function amendPrepareWorkflow(input: Readonly<{
364
+ sessionPath: string;
365
+ cwd?: string;
366
+ expectedSnapshotVersion: string;
367
+ expectedCompassVersion: string;
368
+ patch: PrepareWorkflowPatch;
369
+ }>): Promise<PrepareWorkflowResult>;
@@ -19,6 +19,17 @@ export type AssignmentFields = {
19
19
  * violation code per field so the PM sees exactly which label is missing.
20
20
  */
21
21
  returnShape?: string;
22
+ /**
23
+ * `Task budget (implement / ops rounds)` — the declared capacity of ONE
24
+ * implementer round closing the task's declared Files list and verification
25
+ * gates (capacity contract spec § A1/A2; the value cites the task's
26
+ * `Effort (agent-oriented)` band). Required, presence-only, on the exact
27
+ * complement of the review/audit rounds — implement/ops rounds including
28
+ * non-audit docs/Prepare specialists and orientation roles. The value is
29
+ * prose; it is never parsed numerically (adequacy stays in PM's Prepare
30
+ * check).
31
+ */
32
+ taskBudget?: string;
22
33
  };
23
34
  export type ValidateAssignmentFieldsOptions = {
24
35
  /**
@@ -161,7 +172,10 @@ export declare function isReadOnlyAssignmentRole(roleId: string): boolean;
161
172
  * non-empty values (paste-only shells are caught here — every field missing).
162
173
  * Review-seat and audit rounds must additionally declare both round-bounding
163
174
  * fields, `Budget (review / QC seats)` and `Return shape (review / QC
164
- * seats)` (one violation code per missing label). Writable assignments must
175
+ * seats)` (one violation code per missing label). The complement — every
176
+ * implement/ops round, including non-audit docs/Prepare and orientation
177
+ * roles — must declare `Task budget (implement / ops rounds)`, presence-only.
178
+ * Writable assignments must
165
179
  * carry EXACTLY ONE branch form; `create <new>`
166
180
  * from <base>` without `<base>` (incl. the dangling `create <new> from`
167
181
  * / `create from <base>` typos) and `Branch policy` without branch/reason