@mstar-harness/engine 3.9.3 → 3.9.4

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/index.d.ts CHANGED
@@ -87,3 +87,5 @@ export type { ArtifactDoc, ArtifactKind, ArtifactRef, ArtifactStore } from "./st
87
87
  export { assertFsStorePath, createFsStore, getArtifactStore, loadStoreModule, resolveArtifactPath, setArtifactStore } from "./store.js";
88
88
  export { collectActiveLifecycleBranches, scanActiveLifecycleBranches, type ActiveLifecycleScan } from "./lifecycle-branches.js";
89
89
  export { WorkflowSnapshotValidationError } from "./workflow.js";
90
+ export { CoordinationError, bindPlanSession, mutatePlanCoordination, readCoordinatedArtifact, readPlanCoordination, readSessionEnvelope, replaceCoordinatedArtifact, resolvePlanScope, resolveProcessHarnessDir, } from "./coordination.js";
91
+ export type { BindPlanSessionInput, CoordinatedReplacement, CoordinationRequest, CoordinationResult, CoordinationRole, CoordinationSession, HandoffEvidence, PlanCoordinationOperation, PlanCoordinationView, PlanScopeInput, PrepareCoordinationRequest, ProgressCoordinationRequest, ResidualAddCoordinationRequest, ResidualCloseCoordinationRequest, ResidualInput, ResolvedPlanScope, VersionedArtifact, } from "./coordination.js";
package/dist/lease.d.ts CHANGED
@@ -42,7 +42,7 @@ export type ClaimLeaseFields = {
42
42
  export type LeaseTransition = {
43
43
  ok: boolean;
44
44
  row: PlanRow;
45
- outcome?: "claimed" | "resumed" | "released";
45
+ outcome?: "claimed" | "resumed" | "released" | "transferred";
46
46
  violations: ValidationResult[];
47
47
  };
48
48
  /**
@@ -103,6 +103,18 @@ export declare function claimLease(row: PlanRow, holder: string, fields: ClaimLe
103
103
  * protocol); the caller enforces that ordering around the locked update.
104
104
  */
105
105
  export declare function releaseLease(row: PlanRow, holder: string): LeaseTransition;
106
+ /**
107
+ * Holder transfer for the scoped coordination `accept` transition (spec §D):
108
+ * move an ACTIVE `execution_lease` to another holder verbatim — same
109
+ * `claimed_at`, `worktree_path`, `working_branch`, extra fields preserved;
110
+ * only `holder` changes. Deliberately NOT `claimLease`: there is no
111
+ * Todo/Blocked → InProgress transition and no steal path here. The locked
112
+ * coordination writer has already authenticated the receiving holder as the
113
+ * workflow's coordinator, so `from` must still match the stored holder — a
114
+ * mismatched `from` is `lease.transfer.other-holder` and leaves the row
115
+ * untouched.
116
+ */
117
+ export declare function transferLease(row: PlanRow, from: string, to: string): LeaseTransition;
106
118
  /**
107
119
  * Same-holder resume check (status-and-residuals.md § Claim-before-`InProgress`
108
120
  * #2): `true` iff the stored lease `holder` equals the session `holder`.
package/dist/migrate.d.ts CHANGED
@@ -84,6 +84,14 @@ export type MigratePlan = {
84
84
  */
85
85
  projectDir: string;
86
86
  dryRun: boolean;
87
+ /**
88
+ * Byte version (`sha256:…`, or `"absent"`) of the source `status.json` this
89
+ * plan was derived from (spec §C3 source-version CAS). The executor
90
+ * re-reads the source under the root lock before its first write and
91
+ * refuses a root whose bytes moved after planning — a still-v1 document
92
+ * changed by another writer is never silently replaced.
93
+ */
94
+ sourceVersion: string;
87
95
  /** Root status.json already at `version: 2` -> nothing to plan/apply. */
88
96
  alreadyMigrated: boolean;
89
97
  /** Human message (no-op reason when `alreadyMigrated`). */
package/dist/path.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type ValidationResult } from "./core.js";
1
+ import type { ValidationResult } from "./core.js";
2
2
  /**
3
3
  * Options for `resolveHarnessDir`.
4
4
  */
@@ -164,11 +164,20 @@ export declare function resolveScaffoldDirs(root: string): {
164
164
  * `.mstarc`-declared `project_dir`) with a valid `roadmap.md` + empty
165
165
  * `residuals.json` (plan-conventions § 初始化 Plan 目录;
166
166
  * mstar-project-governance § `_default` 回退). Idempotent: an existing
167
- * non-empty `status.json`, `roadmap.md`, or `residuals.json` is never
168
- * clobbered; re-running on an initialized tree only creates missing
169
- * pieces. Returns the absolute resolved harness dir.
170
- */
171
- export declare function scaffoldHarness(root: string): string;
167
+ * `roadmap.md` is never clobbered, and re-running on an initialized tree only
168
+ * creates missing pieces. Returns the absolute resolved harness dir.
169
+ *
170
+ * The two coordination documents (`status.json`, `_default/residuals.json`)
171
+ * are written create-only through the active `ArtifactStore` inside the
172
+ * private protected-write context, serialized on the target's
173
+ * `withStatusWriteLock` (spec §C4): a concurrent writer's bytes are never
174
+ * replaced, and an existing empty/malformed document fails validation instead
175
+ * of being silently reinitialized. Callers whose target root differs from the
176
+ * active store's root MUST `setArtifactStore(createFsStore(<harnessRoot>))`
177
+ * first (same contract as the other routed writers). No scoped session or
178
+ * coordination record is created here.
179
+ */
180
+ export declare function scaffoldHarness(root: string): Promise<string>;
172
181
  /**
173
182
  * Harness kind for the gitignore fence — the canonical snippet is per
174
183
  * harness layout (plan-conventions § Git 跟踪策略): `.mstar/` (default) and
package/dist/status.d.ts CHANGED
@@ -63,6 +63,18 @@ export type PlanRow = {
63
63
  execution_lease?: unknown;
64
64
  [key: string]: unknown;
65
65
  };
66
+ /**
67
+ * Every plan id a `plans[]` row can be addressed by (status-and-residuals.md
68
+ * § Compatibility: read accepts `id` or the legacy `plan_id`; write prefers
69
+ * `id`). Both non-empty keys are reported, in canonical-first order: a row
70
+ * whose two keys disagree is ambiguous (`status.plan-row.dual-id`), and an
71
+ * ownership guard keyed on one of them would let the other escape protection.
72
+ * Empty strings never count; a non-object or a row carrying neither key has
73
+ * no address.
74
+ */
75
+ export declare function rowPlanIds(row: unknown): string[];
76
+ /** The canonical single plan id of a row — `id`, else legacy `plan_id`. */
77
+ export declare function rowPlanId(row: unknown): string | undefined;
66
78
  /**
67
79
  * Normalize a residual `severity` value for reading/rolling up
68
80
  * (status-and-residuals.md § severity 5 + rollup `norm_sev`):
@@ -156,6 +168,11 @@ export declare const validateStatus: typeof validateStatusV2;
156
168
  * an invalid entry would fail `validateStatusV2` anyway, but the explicit
157
169
  * gate keeps the pre-lock fail-fast contract of `registerWorkflow`).
158
170
  *
171
+ * Replacing an existing entry additionally takes that entry's snapshot lock
172
+ * (spec §C3 root → snapshot order, `withRegisteredSnapshotLock`) and holds it
173
+ * across the root write; callers must therefore hold the root lock only, not
174
+ * a snapshot lock.
175
+ *
159
176
  * Async-only (architect-locked 2026-08-27): the durable write goes through
160
177
  * `getArtifactStore().put({ kind: "status", key: "root", ... })` inside the
161
178
  * caller's lock — the store is the persist backend, never a second lock.
@@ -185,7 +202,10 @@ export declare function registerWorkflow(root: string, entry: WorkflowEntry): Pr
185
202
  * `withStatusWriteLock`, bumping root `updated_at` only when an entry was
186
203
  * actually removed. The final document is validated (removal-at-terminal
187
204
  * invariant included) before the write — a v1 root is refused with the
188
- * `mstar migrate` hint.
205
+ * `mstar migrate` hint. Removing a registered entry additionally takes that
206
+ * entry's snapshot lock (spec §C3 root → snapshot order) and holds it across
207
+ * the root write, so the coordination check cannot be raced by a concurrent
208
+ * bind.
189
209
  *
190
210
  * Fails loud when the active FsStore would resolve its
191
211
  * `status.json` to a path other than the caller's root — the no-op branches
@@ -1,4 +1,5 @@
1
1
  import type { GateResult, ValidationResult } from "./core.js";
2
+ import { type SnapshotCoordination } from "./coordination-write.js";
2
3
  import { type IntegrationMergeLease } from "./lease.js";
3
4
  import { type PlanRow } from "./status.js";
4
5
  /** Snapshot file name inside `workflows/<id>/` ( — writer contract). */
@@ -63,7 +64,17 @@ export type WorkflowSnapshot = {
63
64
  integration_worktree_path?: string;
64
65
  legacy_metadata?: Record<string, unknown>;
65
66
  compass_ref?: string;
67
+ /**
68
+ * Snapshot-level coordination block ( — scoped plan-PM coordination).
69
+ * Present only on a coordinated lifecycle: it carries the workflow's
70
+ * coordinator binding (session id + canonical envelope path). The block
71
+ * is validated strictly (`validateSnapshotCoordination`) and may only be
72
+ * changed by the locked coordination writer.
73
+ */
74
+ coordination?: SnapshotCoordination;
66
75
  };
76
+ /** Stable JSON for change detection (sorted keys, recursive). */
77
+ export declare function stableJson(value: unknown): string;
67
78
  /**
68
79
  * Validate a v3 workflow snapshot document ( — final schema):
69
80
  * enum/type/id checks, `schema_version: 1`, required timestamps, `plans[]`
@@ -114,8 +125,34 @@ export declare class WorkflowSnapshotValidationError extends Error {
114
125
  }
115
126
  export declare function readWorkflowSnapshot(dir: string): WorkflowSnapshotRead;
116
127
  /**
117
- * Write a workflow snapshot as a whole-rewrite of `dir/snapshot.json` under
118
- * `withStatusWriteLock(snapshotPath)` ( the `.status-write.lockdir`
128
+ * Writer contract (spec §C4). `expectedVersion` is the CAS token — the exact
129
+ * on-disk artifact version (`sha256:<64 hex>`), or `"absent"` for
130
+ * create-only. `createOnly` is the locked `absent` shorthand used by the
131
+ * scaffold/migrate/audit writers: an existing document (even an empty or
132
+ * malformed one) is never silently replaced.
133
+ *
134
+ * Omitting `expectedVersion` means create-only, NOT "replace whatever is
135
+ * there": there is no missing-version compatibility fallback, so an existing
136
+ * snapshot still refuses with `coordination.expected-version-required`.
137
+ *
138
+ * The delta allowed here is `phase` + `updated_at` and nothing else — a
139
+ * coordinator persists its phase projection through this writer without ever
140
+ * gaining a backdoor to row owners, leases, lifecycle scalars or branch
141
+ * anchors. Lifecycle terminal changes belong to `closeWorkflow` (spec §C4).
142
+ */
143
+ export type WriteWorkflowSnapshotOptions = {
144
+ expectedVersion?: string;
145
+ createOnly?: boolean;
146
+ /**
147
+ * Canonical coordinator session envelope path (spec §C4). Required when the
148
+ * stored snapshot is coordinated: only the snapshot's own bound coordinator
149
+ * may pass, and the `coordination` block itself is never part of the delta.
150
+ */
151
+ sessionPath?: string;
152
+ };
153
+ /**
154
+ * Write a workflow snapshot as a field-scoped update of `dir/snapshot.json`
155
+ * under `withStatusWriteLock(snapshotPath)` ( — the `.status-write.lockdir`
119
156
  * lands inside `workflows/<id>/`, dirname of the snapshot; no harness-root
120
157
  * pollution). The snapshot is validated first — an invalid snapshot throws
121
158
  * and nothing is written. `dir` is created recursively. The durable write
@@ -128,14 +165,26 @@ export declare function readWorkflowSnapshot(dir: string): WorkflowSnapshotRead;
128
165
  * whose target root differs from the active store's root MUST
129
166
  * `setArtifactStore(createFsStore(root))` first.
130
167
  */
131
- export declare function writeWorkflowSnapshot(snapshot: WorkflowSnapshot, dir: string): Promise<void>;
168
+ export declare function writeWorkflowSnapshot(snapshot: WorkflowSnapshot, dir: string, opts?: WriteWorkflowSnapshotOptions): Promise<void>;
132
169
  /** Terminal enum predicate only; callers validate document shape separately. */
133
170
  export declare function isTerminalSnapshot(doc: WorkflowSnapshot): boolean;
134
171
  export type CloseWorkflowOptions = {
135
172
  endedAt: string;
173
+ /**
174
+ * Canonical coordinator session envelope path (spec §C4). Required when the
175
+ * stored snapshot is coordinated: the close writes the snapshot, so only the
176
+ * snapshot's own bound coordinator may pass. A missing/mismatched envelope
177
+ * refuses the close with `coordination.session-mismatch` before anything is
178
+ * written. Non-coordinated snapshots ignore it; an already-terminal snapshot
179
+ * is returned unchanged (no write, no authorization needed).
180
+ */
181
+ sessionPath?: string;
136
182
  };
137
183
  /**
138
184
  * Complete the latest snapshot under its write lock. Never releases leases.
139
185
  * A valid terminal snapshot is returned unchanged, including failed/stopped.
186
+ * A coordinated snapshot is closed only by its own bound coordinator
187
+ * (spec §C4) — the same envelope seam as `writeWorkflowSnapshot` — so a plan
188
+ * actor or a bare CLI call can never complete a lifecycle it does not own.
140
189
  */
141
190
  export declare function closeWorkflow(workflowId: string, dir: string, opts: CloseWorkflowOptions): Promise<WorkflowSnapshot>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mstar-harness/engine",
3
- "version": "3.9.3",
3
+ "version": "3.9.4",
4
4
  "description": "Morning Star Harness Workflow Engine — deterministic workflow enforcement library (path, status, lease, dispatch, sdd, iteration, lint gates).",
5
5
  "license": "MIT",
6
6
  "repository": {