@mstar-harness/engine 3.9.4 → 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/index.d.ts CHANGED
@@ -45,8 +45,8 @@ export type { PlanRow, ResidualEntry, StatusDoc, StatusV2Doc, WorkflowEntry, } f
45
45
  export { normalizeSeverity, registerWorkflow, resolveCompassEnforcement, resolveMstarcEnforcement, resolveRepoEnforcement, unregisterWorkflow, validatePlanRow, validateResidual, validateStatus, validateStatusV2, validateWorkflowEntry, } from "./status.js";
46
46
  export type { ClaimLeaseFields, ExecutionLease, ExecutionLeaseLocations, IntegrationMergeLease, LeaseTransition, LeaseVerifyResult, } from "./lease.js";
47
47
  export { canSteal, claimLease, planExecutionLeaseLocations, releaseLease, sameHolderResume, validateExecutionLease, validateIntegrationMergeLease, verifyPlanExecutionLease, withStatusWriteLock, } from "./lease.js";
48
- export type { CloseWorkflowOptions, WorkflowBranchAnchors, WorkflowExecutionPolicy, WorkflowLifecycleStatus, WorkflowLifecycleType, WorkflowSnapshot, WorkflowSnapshotRead, } from "./workflow.js";
49
- export { closeWorkflow, isTerminalSnapshot, LEGACY_WORKTREE_PATH_CODE, WORKFLOW_LIFECYCLE_STATUSES, WORKFLOW_LIFECYCLE_TYPES, WORKFLOW_SNAPSHOT_FILE, WORKFLOW_TERMINAL_STATUSES, readWorkflowSnapshot, validateWorkflowSnapshot, writeWorkflowSnapshot, } from "./workflow.js";
48
+ export type { CloseWorkflowOptions, DeclareWorkflowDeliveryKindOptions, DeliveryRegistrationEvidence, RecordWorkflowDeliveryOptions, RecordWorkflowDeliveryResult, RegisterPlanWorkflowOptions, RegisterPlanWorkflowResult, WorkflowBranchAnchors, WorkflowCompoundOutcome, WorkflowDeliveryEvidence, WorkflowDeliveryKind, WorkflowExecutionPolicy, WorkflowLifecycleStatus, WorkflowLifecycleType, WorkflowSnapshot, WorkflowSnapshotRead, } from "./workflow.js";
49
+ export { assertDeliveryRegistrationCoherence, closeWorkflow, consultDeliveryEvidence, declareWorkflowDeliveryKind, isTerminalSnapshot, LEGACY_WORKTREE_PATH_CODE, recordWorkflowDelivery, registerPlanWorkflow, WORKFLOW_COMPOUND_OUTCOMES, WORKFLOW_DELIVERY_KINDS, WORKFLOW_LIFECYCLE_STATUSES, WORKFLOW_LIFECYCLE_TYPES, WORKFLOW_SNAPSHOT_FILE, WORKFLOW_TERMINAL_STATUSES, readWorkflowSnapshot, validateWorkflowSnapshot, writeWorkflowSnapshot, } from "./workflow.js";
50
50
  export type { CleanupDecision, CleanupFacts, CleanupTarget, CleanupTargetKind, } from "./cleanup.js";
51
51
  export { planWorktreeCleanup } from "./cleanup.js";
52
52
  export type { EvidenceArtifactFact, EvidenceAssessment, EvidenceCaptureRequest, EvidenceCoverage, EvidenceEnvironmentKey, EvidenceExpectation, EvidenceInputEntry, EvidenceInputSnapshot, EvidenceInputSpec, EvidenceLimits, EvidenceLog, EvidenceOutcome, EvidenceToolFingerprint, SddEvidenceRecord, } from "./evidence.js";
@@ -87,5 +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";
90
+ export { CoordinationError, amendPrepareWorkflow, bindPlanSession, mutatePlanCoordination, readCoordinatedArtifact, readPlanCoordination, readSessionEnvelope, replaceCoordinatedArtifact, resolvePlanScope, resolveProcessHarnessDir, showPrepareWorkflow, } from "./coordination.js";
91
+ export type { BindPlanSessionInput, CoordinatedReplacement, CoordinationRequest, CoordinationResult, CoordinationRole, CoordinationSession, HandoffEvidence, PlanCoordinationOperation, PlanCoordinationView, PlanScopeInput, PrepareCoordinationRequest, PreparePlanAppend, PrepareWorkflowPatch, PrepareWorkflowResult, PrepareWorkflowView, ProgressCoordinationRequest, ResidualAddCoordinationRequest, ResidualCloseCoordinationRequest, ResidualInput, ResolvedPlanScope, VersionedArtifact, } from "./coordination.js";
@@ -120,7 +120,39 @@ export declare function evaluatePhaseGate(snapshotDoc: SnapshotDoc, compassDoc:
120
120
  * `evaluatePhaseGate` / `PhaseGateResult` (Phase 2–5 exit codes stay
121
121
  * intact). Stable machine codes: `PHASE6_NOT_TERMINAL`,
122
122
  * `PHASE6_ROOT_ENTRY_PRESENT`, `PHASE6_DANGLING_LEASE`,
123
- * `PHASE6_INVALID_SNAPSHOT`, `PHASE6_INVALID_ROOT`.
123
+ * `PHASE6_INVALID_SNAPSHOT`, `PHASE6_INVALID_ROOT`,
124
+ * `PHASE6_DELIVERY_KIND_UNREGISTERED`, `PHASE6_DELIVERY_EVIDENCE_INCOMPLETE`,
125
+ * `PHASE6_PLAN_ROW_NOT_DONE`.
126
+ *
127
+ * Plan-type delivery-kind consultation (seam S3, contract §6 S3 + §4g): a
128
+ * terminal `type: plan` snapshot also consults the delivery evidence recorded
129
+ * over the lifecycle — plan rows are the owned plan (no compass input) and
130
+ * remote merge verification stays excluded by contract (§4f is the PM's
131
+ * separate check). The consultation is the shared pure function
132
+ * `consultDeliveryEvidence` (workflow.ts), which `closeWorkflow` runs before
133
+ * writing a terminal snapshot — one implementation, so this read-only gate
134
+ * and the write path can never disagree. It runs for a `completed` close ONLY:
135
+ * `failed`/`stopped` closes are never demanded delivery evidence (§5 — failure
136
+ * closes through its explicit status with a recorded reason and is never
137
+ * treated as delivered), exactly as `closeWorkflow` preserves an
138
+ * already-terminal snapshot unchanged without consulting; the type-generic
139
+ * dangling-lease probe still covers every terminal status. The kind is never
140
+ * inferred (§1): a plan workflow without a registered `delivery_kind` has no
141
+ * close-verifiable delivery evidence — for a legacy terminal snapshot this is
142
+ * outside the gate's automated recovery, because the create-only register
143
+ * cannot backfill terminal bytes, so the remediation names the owner
144
+ * snapshot-amendment path (the audit-promotion grandfather population is
145
+ * disclosed there) instead of the register verb — and a registered kind with
146
+ * incomplete delivery evidence (`development` without its registered
147
+ * source/target branches, its compound disposition, its PR identity or the
148
+ * PM's verified-merge record; `verification/report-only` without the recorded
149
+ * completion policy or its fulfilment record) refuses the same way — missing
150
+ * fields are incomplete registration, not an exempt workflow. A `completed`
151
+ * close additionally requires every owned plan row `Done` (the post-write
152
+ * mirror of `closeWorkflow`'s all-rows-Done guard, §3 terminal stage);
153
+ * `failed`/`stopped` lifecycles keep their statuses and row states (§5 —
154
+ * never rewritten as successfully completed). Every refusal is read-only: the
155
+ * workflow stays registered/resumable.
124
156
  */
125
157
  export declare function evaluatePostMergeClose(snapshotDoc: SnapshotDoc, rootDoc: unknown): GateResult;
126
158
  /**
package/dist/migrate.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { type ProjectRegisterDoc } from "./project.js";
2
2
  import { type StatusV2Doc } from "./status.js";
3
- import { type WorkflowLifecycleStatus, type WorkflowLifecycleType, type WorkflowSnapshot } from "./workflow.js";
3
+ import { type WorkflowDeliveryKind, type WorkflowLifecycleStatus, type WorkflowLifecycleType, type WorkflowSnapshot } from "./workflow.js";
4
4
  /** Root status file name inside `{HARNESS_DIR}` (v1 input / v2 output). */
5
5
  export declare const MIGRATE_STATUS_FILE = "status.json";
6
6
  /** Legacy v1 root copy written by migration (never deleted without it). */
@@ -62,6 +62,21 @@ export type MigrateOptions = {
62
62
  dryRun?: boolean;
63
63
  /** Project id for the register/roadmap home (default `_default`). */
64
64
  projectId?: string;
65
+ /**
66
+ * Delivery kind declared for the ACTIVE standalone plan snapshots this lift
67
+ * produces (contract §1/§4a). A v1 `plans[]` row becomes a `type: plan`
68
+ * lifecycle, so its delivery kind is declared at registration and never
69
+ * inferred: the CLI/API caller passes it explicitly, and a lift that would
70
+ * create an active kind-less plan snapshot without it is refused (see
71
+ * `MigratePlan.deliveryKindRequired`).
72
+ */
73
+ deliveryKind?: WorkflowDeliveryKind;
74
+ /** Delivery source branch, recorded as `branch.source`. Required for `development`. */
75
+ branchSource?: string;
76
+ /** Delivery target branch, recorded as `branch.target`. Required for `development`. */
77
+ branchTarget?: string;
78
+ /** Completion policy for `verification/report-only` lifts. Required for that kind. */
79
+ completionPolicy?: string;
65
80
  };
66
81
  /** Full migration plan: every write is described; the executor applies it. */
67
82
  export type MigratePlan = {
@@ -97,6 +112,30 @@ export type MigratePlan = {
97
112
  /** Human message (no-op reason when `alreadyMigrated`). */
98
113
  message: string;
99
114
  snapshots: MigrateSnapshot[];
115
+ /**
116
+ * Ids of the ACTIVE (running/paused) standalone plan snapshots this lift
117
+ * would create WITHOUT a declared delivery kind — i.e. the v1 rows lifted
118
+ * before the operator passed `opts.deliveryKind`. Non-empty means the plan is
119
+ * NOT applicable: `applyMigratePlan` refuses it (a close path could never
120
+ * complete such a lifecycle), and the CLI reports the missing flag as usage
121
+ * (exit 2) before any write. Terminal (completed) lifted snapshots are exempt
122
+ * — a finished lifecycle declares nothing (§1; the legacy terminal dead end
123
+ * is documented, not amended here).
124
+ */
125
+ deliveryKindRequired: string[];
126
+ /**
127
+ * Ids of the ACTIVE (running/paused) standalone plan snapshots the single
128
+ * `opts.deliveryKind` declaration would be stamped onto when MORE THAN ONE
129
+ * lift qualifies. The declaration is one (kind, anchors, policy) tuple — one
130
+ * delivery identity, and the kind is per-workflow registration evidence (§1)
131
+ * — so it cannot describe several lifecycles at once. Non-empty means the
132
+ * plan is NOT applicable: `applyMigratePlan` refuses it and the CLI reports
133
+ * the ambiguity as usage (exit 2) before any write, so the operator migrates
134
+ * the tree in batches of one declared plan instead of pinning one identity
135
+ * onto several. Empty when no declaration was given or exactly one lift
136
+ * qualifies; terminal lifts are exempt like `deliveryKindRequired`.
137
+ */
138
+ deliveryKindAmbiguous: string[];
100
139
  notesFiles: MigrateNotesFile[];
101
140
  register: MigrateRegister | null;
102
141
  roadmap: MigrateRoadmap | null;
package/dist/sdd.d.ts CHANGED
@@ -253,7 +253,15 @@ export type SddActionKind = "source" | "artifact" | "launch";
253
253
  * the context must then match the verified lease exactly. Without an
254
254
  * active lease (no row, or a non-InProgress row without lease), the
255
255
  * standalone branch policy applies (`assertBranchAlignment`) — an
256
- * InProgress row without lease is the orphan refusal.
256
+ * InProgress row without lease is the orphan refusal. The one admission
257
+ * exception (lifecycle contract §4b/§6 S2): on a register-governed harness
258
+ * root (readable v2 `status.json` `workflows[]`), a plan with NO active
259
+ * registered row is an unregistered normal-route plan — refused with
260
+ * `sdd.context.plan-not-registered` and the register/recovery path; branch
261
+ * alignment never substitutes for registration, and a plan id retained only
262
+ * in a terminal snapshot is refused the same way (a retained row is history,
263
+ * not registration evidence — §4b). Roots without a v2 register keep the
264
+ * standalone policy byte-for-byte.
257
265
  *
258
266
  * Throws `SddScriptError` — exit 2 when the declared context itself is
259
267
  * malformed (non-absolute path, identity/composition mismatch, missing plan
@@ -1,4 +1,4 @@
1
- import type { GateResult, ValidationResult } from "./core.js";
1
+ import { type GateResult, type ValidationResult } from "./core.js";
2
2
  import { type SnapshotCoordination } from "./coordination-write.js";
3
3
  import { type IntegrationMergeLease } from "./lease.js";
4
4
  import { type PlanRow } from "./status.js";
@@ -10,6 +10,58 @@ export declare const WORKFLOW_LIFECYCLE_STATUSES: readonly ["running", "paused",
10
10
  export declare const WORKFLOW_TERMINAL_STATUSES: readonly ["completed", "failed", "stopped"];
11
11
  /** Lifecycle type enum ( — id reuses the orchestration id). */
12
12
  export declare const WORKFLOW_LIFECYCLE_TYPES: readonly ["plan", "iteration"];
13
+ /**
14
+ * Delivery kinds declared at registration (mstar-artifacts/references/plan-workflow-lifecycle-contract.md
15
+ * §1). The declared kind is recorded at registration and never inferred
16
+ * retroactively; `development` carries the full PR/merge delivery lifecycle,
17
+ * `verification/report-only` follows the explicit completion policy recorded
18
+ * alongside it.
19
+ */
20
+ export declare const WORKFLOW_DELIVERY_KINDS: readonly ["development", "verification/report-only"];
21
+ export type WorkflowDeliveryKind = (typeof WORKFLOW_DELIVERY_KINDS)[number];
22
+ /**
23
+ * Compound disposition outcomes (contract §4c): `created` / `updated` /
24
+ * reasoned `skipped`. The disposition is recorded on the workflow before the
25
+ * PR head is finalized, so the delivery evidence names the compound outcome
26
+ * it was taken from.
27
+ */
28
+ export declare const WORKFLOW_COMPOUND_OUTCOMES: readonly ["created", "updated", "skipped"];
29
+ export type WorkflowCompoundOutcome = (typeof WORKFLOW_COMPOUND_OUTCOMES)[number];
30
+ /**
31
+ * Delivery evidence recorded on the snapshot (contract §3 lifecycle stages,
32
+ * §4c/§4d/§4f, seam S3). Each member is recorded by its owner at its own
33
+ * stage through the authorized write seam (`recordWorkflowDelivery`), and the
34
+ * close consultation reads the block for the DECLARED delivery kind — never
35
+ * inferring a kind from which members happen to be present (§1):
36
+ *
37
+ * - `compound` — compound disposition (§4c), recorded before the PR head is
38
+ * finalized; `skipped` carries the mandatory reason;
39
+ * - `pr` — PR identity recorded at submission (§4d): repo/head/target. A
40
+ * local commit or a pre-existing unrelated PR does not satisfy it;
41
+ * - `merge` — the PM's verified-merge record (§4f): the provider evidence
42
+ * they checked. The engine NEVER verifies the remote merge itself;
43
+ * - `completion` — the fulfilment record of the `completion_policy` recorded
44
+ * at registration (§1) for `verification/report-only` workflows.
45
+ */
46
+ export type WorkflowDeliveryEvidence = {
47
+ compound?: {
48
+ outcome: WorkflowCompoundOutcome;
49
+ reason?: string;
50
+ };
51
+ pr?: {
52
+ repo: string;
53
+ head: string;
54
+ target: string;
55
+ };
56
+ merge?: {
57
+ provider: string;
58
+ evidence: string;
59
+ };
60
+ completion?: {
61
+ policy: string;
62
+ evidence: string;
63
+ };
64
+ };
13
65
  export type WorkflowLifecycleStatus = (typeof WORKFLOW_LIFECYCLE_STATUSES)[number];
14
66
  export type WorkflowLifecycleType = (typeof WORKFLOW_LIFECYCLE_TYPES)[number];
15
67
  /**
@@ -24,7 +76,20 @@ export type WorkflowExecutionPolicy = {
24
76
  };
25
77
  /** Iteration branch anchors ( — from root metadata anchors). */
26
78
  export type WorkflowBranchAnchors = {
79
+ /**
80
+ * Protected base anchor: the branch the lifecycle starts from (iteration
81
+ * `iteration_base_branch`). Cleanup Rule 2 never deletes it and L1 uses it
82
+ * as the explicit main-worktree residency fallback — it is NEVER a
83
+ * feature/working branch (`registerPlanWorkflow` records the plan's
84
+ * delivery branch under `source`).
85
+ */
27
86
  base?: string;
87
+ /**
88
+ * Source branch of a standalone `type: plan` delivery, recorded at
89
+ * registration (`--branch-source`). Semantically a delivery branch, not a
90
+ * protected base anchor: cleanup/L1 consumers keep reading `base`.
91
+ */
92
+ source?: string;
28
93
  integration?: string;
29
94
  target?: string;
30
95
  };
@@ -72,6 +137,27 @@ export type WorkflowSnapshot = {
72
137
  * changed by the locked coordination writer.
73
138
  */
74
139
  coordination?: SnapshotCoordination;
140
+ /**
141
+ * Delivery kind declared at registration (mstar-artifacts/references/plan-workflow-lifecycle-contract.md
142
+ * §1). Recorded by the registration producer; never inferred from runtime
143
+ * behavior or from the presence/absence of other fields.
144
+ */
145
+ delivery_kind?: WorkflowDeliveryKind;
146
+ /** Project register id recorded at registration (contract §3 register row). */
147
+ project?: string;
148
+ /**
149
+ * Explicit completion policy for `verification/report-only` workflows,
150
+ * recorded at registration (contract §1): names the evidence that completes
151
+ * the workflow (e.g. acceptance artifacts or the report location).
152
+ */
153
+ completion_policy?: string;
154
+ /**
155
+ * Delivery evidence collected over the lifecycle (contract §3/§4c/§4d/§4f,
156
+ * seam S3). Optional at the schema level — it is populated stage by stage
157
+ * through `recordWorkflowDelivery` and consulted by the close path (and the
158
+ * read-only phase-6 gate) for the declared `delivery_kind`.
159
+ */
160
+ delivery?: WorkflowDeliveryEvidence;
75
161
  };
76
162
  /** Stable JSON for change detection (sorted keys, recursive). */
77
163
  export declare function stableJson(value: unknown): string;
@@ -168,6 +254,55 @@ export type WriteWorkflowSnapshotOptions = {
168
254
  export declare function writeWorkflowSnapshot(snapshot: WorkflowSnapshot, dir: string, opts?: WriteWorkflowSnapshotOptions): Promise<void>;
169
255
  /** Terminal enum predicate only; callers validate document shape separately. */
170
256
  export declare function isTerminalSnapshot(doc: WorkflowSnapshot): boolean;
257
+ /** Delivery-registration inputs every producer declares (contract §1/§4a). */
258
+ export type DeliveryRegistrationEvidence = {
259
+ branchSource?: string;
260
+ branchTarget?: string;
261
+ completionPolicy?: string;
262
+ };
263
+ /**
264
+ * Per-kind registration-evidence coherence (contract §1) — the ONE rule shared
265
+ * by every producer that declares a delivery kind at registration time:
266
+ * `registerPlanWorkflow` (the normal-entry producer), `promoteAuditPlans`
267
+ * (audit promotion) and `migrateHarnessTree` (v1 lift), plus the one-time
268
+ * `declareWorkflowDeliveryKind` backfill for active kind-less snapshots.
269
+ * Lockstep here is the point: a `development` workflow declares BOTH delivery
270
+ * anchors and a `verification/report-only` workflow declares the completion
271
+ * policy that completes it — missing fields are incomplete registration, never
272
+ * an exemption, and a producer that drifted would otherwise mint an
273
+ * unclosable lifecycle. `what` prefixes the refusal (the caller's own name).
274
+ */
275
+ export declare function assertDeliveryRegistrationCoherence(kind: WorkflowDeliveryKind, evidence: DeliveryRegistrationEvidence, what: string): void;
276
+ /**
277
+ * Delivery-kind evidence consultation (seam S3 — contract §4g + §6 S3): the
278
+ * ONE implementation behind both the read-only Phase-6 gate
279
+ * (`evaluatePostMergeClose`) and the close write path (`closeWorkflow`), so
280
+ * the gate's verdict and the close's refusal can never drift apart. Pure,
281
+ * read-only, no writes; the input snapshot is consumed as read.
282
+ *
283
+ * Only `type: plan` lifecycles consult (an iteration declares no delivery
284
+ * kind — §1); WHEN the consultation runs is the caller's rule, and both
285
+ * callers scope it to a delivered lifecycle: `closeWorkflow` consults the
286
+ * running snapshot it is about to complete, and the Phase-6 gate consults a
287
+ * `completed` terminal snapshot. A `failed`/`stopped` close is therefore never
288
+ * demanded delivery evidence (§5). The DECLARED kind decides the required
289
+ * evidence; nothing is inferred from which fields happen to be present, and a
290
+ * missing field is incomplete registration, never an exemption (§1):
291
+ *
292
+ * - no registered `delivery_kind` → `PHASE6_DELIVERY_KIND_UNREGISTERED` (a
293
+ * legacy terminal snapshot cannot be backfilled: the register producer is
294
+ * create-only and preserves the terminal bytes);
295
+ * - `development` → the registered `branch.source`/`branch.target` plus the
296
+ * collected `delivery` evidence: compound disposition (§4c), PR identity
297
+ * (§4d) and the PM's verified-merge record (§4f — the engine never verifies
298
+ * the remote merge itself);
299
+ * - `verification/report-only` → the recorded `completion_policy` plus its
300
+ * fulfilment record, which must name that same policy (§1).
301
+ *
302
+ * Everything missing is named in one refusal (`PHASE6_DELIVERY_EVIDENCE_INCOMPLETE`)
303
+ * whose fix hint points at the authorized recording seam.
304
+ */
305
+ export declare function consultDeliveryEvidence(snapshot: WorkflowSnapshot): ValidationResult[];
171
306
  export type CloseWorkflowOptions = {
172
307
  endedAt: string;
173
308
  /**
@@ -182,9 +317,196 @@ export type CloseWorkflowOptions = {
182
317
  };
183
318
  /**
184
319
  * Complete the latest snapshot under its write lock. Never releases leases.
185
- * A valid terminal snapshot is returned unchanged, including failed/stopped.
320
+ * A valid terminal snapshot is returned unchanged, including failed/stopped
321
+ * (idempotent preservation — nothing is rewritten, not even the timestamp).
186
322
  * A coordinated snapshot is closed only by its own bound coordinator
187
323
  * (spec §C4) — the same envelope seam as `writeWorkflowSnapshot` — so a plan
188
324
  * actor or a bare CLI call can never complete a lifecycle it does not own.
325
+ *
326
+ * Before the terminal write the close consults the registered delivery
327
+ * kind's evidence through `consultDeliveryEvidence` — the SAME pure function
328
+ * the read-only Phase-6 gate runs (contract §4g/§6 S3), so the gate's verdict
329
+ * and this refusal can never disagree. An incomplete delivery (a
330
+ * `development` workflow without its compound disposition / PR identity /
331
+ * verified-merge record, a `verification/report-only` workflow without the
332
+ * fulfilment of its recorded completion policy) throws with every missing
333
+ * item named and ZERO writes: the snapshot stays `running` and the root entry
334
+ * stays registered, so the workflow remains resumable. The local close never
335
+ * verifies a remote merge (§4f keeps that as the PM's separate check) and
336
+ * never releases leases.
189
337
  */
190
338
  export declare function closeWorkflow(workflowId: string, dir: string, opts: CloseWorkflowOptions): Promise<WorkflowSnapshot>;
339
+ export type RecordWorkflowDeliveryOptions = {
340
+ /**
341
+ * Partial delivery-evidence patch: only the named members are merged into
342
+ * the stored block, so the compound disposition, the PR identity and the
343
+ * merge record can be recorded at their own lifecycle stages without
344
+ * rewriting each other. At least one member is required.
345
+ */
346
+ evidence: WorkflowDeliveryEvidence;
347
+ /**
348
+ * Canonical coordinator session envelope path (spec §C4) — the same
349
+ * authority seam `closeWorkflow` uses: a coordinated snapshot is written
350
+ * only by its own bound coordinator. Non-coordinated snapshots have no
351
+ * coordinator to bind and ignore it.
352
+ */
353
+ sessionPath?: string;
354
+ /** Recording timestamp (YYYY-MM-DD or RFC3339). Default: now. */
355
+ at?: string;
356
+ };
357
+ export type RecordWorkflowDeliveryResult = {
358
+ snapshot: WorkflowSnapshot;
359
+ /** `false` when the recorded evidence already matched disk — nothing was written. */
360
+ written: boolean;
361
+ };
362
+ /**
363
+ * Record (or extend) the delivery evidence on a `type: plan` workflow's
364
+ * snapshot, under the snapshot write lock: the close path's consultation is
365
+ * only as good as the evidence recorded here, so this is the seam that makes
366
+ * a `development` close possible at all.
367
+ *
368
+ * Refusals (before any write):
369
+ * - a terminal snapshot: delivery evidence is collected BEFORE the close, and
370
+ * a terminal lifecycle is never amended (§5 — no rewriting a closed
371
+ * lifecycle);
372
+ * - a lifecycle other than `type: plan`, or one without a registered
373
+ * `delivery_kind`: the evidence belongs to the declared kind (§1);
374
+ * - evidence the declared kind does not use (e.g. a completion record on a
375
+ * `development` workflow) — the declared kind is authoritative;
376
+ * - a rewrite of the recorded PR identity (§4d records it once at submission:
377
+ * an identical re-record is idempotent, a different pair is refused);
378
+ * - an empty patch or a malformed member: nothing is silently dropped.
379
+ *
380
+ * Idempotent and re-entrant: re-recording the exact stored evidence performs
381
+ * NO write and returns the snapshot as read (the timestamp is untouched), so
382
+ * a retried recording never produces a spurious revision. The write itself
383
+ * routes through the same protected-writer path as the close (the store's
384
+ * locked put inside the snapshot lock).
385
+ */
386
+ export declare function recordWorkflowDelivery(workflowId: string, dir: string, opts: RecordWorkflowDeliveryOptions): Promise<RecordWorkflowDeliveryResult>;
387
+ export type DeclareWorkflowDeliveryKindOptions = {
388
+ /** The declared kind (contract §1). Required — never inferred. */
389
+ deliveryKind: WorkflowDeliveryKind;
390
+ /** Delivery source branch, recorded as `branch.source`. Required for `development`. */
391
+ branchSource?: string;
392
+ /** Delivery target branch, recorded as `branch.target`. Required for `development`. */
393
+ branchTarget?: string;
394
+ /** Completion policy naming the evidence that completes the workflow. Required for `verification/report-only`. */
395
+ completionPolicy?: string;
396
+ /**
397
+ * Canonical coordinator session envelope path (spec §C4) — the same
398
+ * authority seam `closeWorkflow` and `recordWorkflowDelivery` use: a
399
+ * coordinated snapshot is written only by its own bound coordinator. An
400
+ * uncoordinated snapshot has no coordinator binding to authenticate (the
401
+ * historical audit-promotion / migrate population is exactly that), so it
402
+ * keeps the harness-owner stance the close already applies.
403
+ */
404
+ sessionPath?: string;
405
+ /** Declaration timestamp (YYYY-MM-DD or RFC3339). Default: now. */
406
+ at?: string;
407
+ };
408
+ /**
409
+ * Declare the delivery kind (and its per-kind registration evidence) of an
410
+ * ACTIVE `type: plan` snapshot whose producer declared none — the one-time
411
+ * upgrade seam for the historical population (contract §1/§4a). ONE-TIME by
412
+ * construction: the kind is registration evidence that is never inferred
413
+ * retroactively (§1), so a second declaration is refused even with the same
414
+ * value, and a terminal snapshot is refused outright (§5 — a closed lifecycle
415
+ * is never amended; the legacy terminal dead end keeps its documented
416
+ * owner-amendment path).
417
+ *
418
+ * The declaration carries the kind's own evidence through the shared
419
+ * `assertDeliveryRegistrationCoherence` rule, so it cannot mint an unclosable
420
+ * lifecycle (a `development` kind needs its delivery anchors, a
421
+ * `verification/report-only` kind its completion policy). A supplied delivery
422
+ * anchor either FILLS the missing one or restates the value the snapshot
423
+ * already registers; a contradicting value is refused, so the declaration can
424
+ * never re-point a lifecycle at another delivery. The write lands in the
425
+ * snapshot lock through the same protected writer as the close.
426
+ */
427
+ export declare function declareWorkflowDeliveryKind(workflowId: string, dir: string, opts: DeclareWorkflowDeliveryKindOptions): Promise<WorkflowSnapshot>;
428
+ /** Options for `registerPlanWorkflow`. `harnessDir` is required — the
429
+ * snapshot and `status.json` live under the harness root. */
430
+ export type RegisterPlanWorkflowOptions = {
431
+ /** Absolute harness dir that contains `status.json` + `workflows/`. Required. */
432
+ harnessDir: string;
433
+ /** The owned plan (contract §2: one independently owned plan per workflow on the new normal route). */
434
+ plan: {
435
+ id: string;
436
+ title: string;
437
+ file: string;
438
+ };
439
+ /** Delivery kind declared at registration (contract §1). Required — never inferred. */
440
+ deliveryKind: WorkflowDeliveryKind;
441
+ /** Project register id recorded on the snapshot (contract §3 register row). */
442
+ project?: string;
443
+ /** Source branch of the delivery, recorded as `branch.source`. Required together with `branchTarget` for `development`. */
444
+ branchSource?: string;
445
+ /** Target branch. Required together with `branchSource` for `development`. */
446
+ branchTarget?: string;
447
+ /**
448
+ * Completion policy for `verification/report-only` workflows (contract §1):
449
+ * names the evidence that completes the workflow. Required for that kind.
450
+ */
451
+ completionPolicy?: string;
452
+ /**
453
+ * Pre-existing coordinator binding recorded at registration. Optional —
454
+ * binding a NEW coordinator session stays on the authorized `bind` seam
455
+ * (`coordination.ts`); this only records an already-held binding.
456
+ */
457
+ coordinator?: {
458
+ session_id: string;
459
+ session_file: string;
460
+ };
461
+ /** Registration timestamp (YYYY-MM-DD or RFC3339). Default: now. */
462
+ startedAt?: string;
463
+ };
464
+ export type RegisterPlanWorkflowResult = {
465
+ workflowId: string;
466
+ snapshotPath: string;
467
+ /**
468
+ * True when this call completed a registration whose snapshot already
469
+ * existed (crash between snapshot creation and root registration): the
470
+ * existing snapshot bytes — identity, timestamps, ownership — were kept
471
+ * and only the root entry was written (contract §4b recovery).
472
+ */
473
+ recovered: boolean;
474
+ };
475
+ /**
476
+ * Register a standalone plan workflow (seam S1 — the generic normal-entry
477
+ * producer). Builds the create-only `type: plan` snapshot (workflow id, the
478
+ * owned plan as its single Todo row, project, delivery kind, source/target
479
+ * branches, optional coordinator) and the root `workflows[]` entry, then
480
+ * writes BOTH under one atomic section of the root `withStatusWriteLock`
481
+ * — the same serialization point `registerWorkflow` uses — mirroring
482
+ * `promoteAuditPlans`'s primitive sequence: create-only
483
+ * `writeWorkflowSnapshot` (its snapshot-dir lock nests inside the root lock,
484
+ * root → snapshot is the documented acquisition order) →
485
+ * `registerWorkflowEntryLocked`, with rollback that removes ONLY the exact
486
+ * snapshot version this call created (plus the now-empty workflow dir), so a
487
+ * failed register write is never treated as partial activation success.
488
+ *
489
+ * Refusals (fail-loud, no partial activation):
490
+ * - existing id: the workflow is already registered (snapshot + root entry) —
491
+ * registration is create-only, never a re-registration;
492
+ * - unreadable root: a malformed/v1 `status.json` refuses (the created
493
+ * snapshot is rolled back first);
494
+ * - missing required fields: id/plan/delivery-kind shape per the existing
495
+ * snapshot validators; `development` additionally requires source+target
496
+ * branches (contract §1 — a development workflow with missing branch
497
+ * fields is incomplete registration, not an exempt workflow) and
498
+ * `verification/report-only` requires the completion policy.
499
+ *
500
+ * Crash/retry recovery (contract §4b): a crash between snapshot creation and
501
+ * root registration leaves the snapshot orphaned (no root entry = no
502
+ * activation). Re-running this producer with the same registration identity
503
+ * completes the registration against the EXISTING snapshot bytes — identity,
504
+ * timestamps and ownership are preserved, never rewritten. An existing
505
+ * snapshot with a DIFFERENT registration identity refuses (it belongs to
506
+ * another registration).
507
+ *
508
+ * The caller must pin the artifact store to the harness root first
509
+ * (`setArtifactStore(createFsStore(harnessDir))`) when the active store's
510
+ * root could differ — the routed writers fail loud on a path mismatch.
511
+ */
512
+ export declare function registerPlanWorkflow(workflowId: string, options: RegisterPlanWorkflowOptions): Promise<RegisterPlanWorkflowResult>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mstar-harness/engine",
3
- "version": "3.9.4",
3
+ "version": "3.10.0",
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": {