@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.
- package/dist/audit.d.ts +23 -7
- package/dist/audit.js +1015 -177
- package/dist/coordination-write.d.ts +170 -0
- package/dist/coordination.d.ts +369 -0
- package/dist/dispatch.d.ts +15 -1
- package/dist/engine.js +4730 -549
- package/dist/index.d.ts +4 -2
- package/dist/iteration.d.ts +33 -1
- package/dist/lease.d.ts +13 -1
- package/dist/migrate.d.ts +48 -1
- package/dist/path.d.ts +15 -6
- package/dist/sdd.d.ts +9 -1
- package/dist/status.d.ts +21 -1
- package/dist/workflow.d.ts +376 -5
- package/package.json +1 -1
|
@@ -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>;
|
package/dist/dispatch.d.ts
CHANGED
|
@@ -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).
|
|
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
|