@mstar-harness/engine 3.10.2 → 3.10.3

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.js CHANGED
@@ -279,7 +279,7 @@ function validatePlanProgress(value, what = "coordination.progress") {
279
279
  }
280
280
  return violations;
281
281
  }
282
- function validatePlanHandoff(value, what = "coordination.handoff") {
282
+ function validatePlanHandoff(value, what = "coordination.handoff", route = "integration") {
283
283
  if (!isPlainObject2(value))
284
284
  return [invalid("coordination.row.handoff-shape", `${what} must be an object`)];
285
285
  const allowed = [
@@ -419,7 +419,25 @@ function validatePlanHandoff(value, what = "coordination.handoff") {
419
419
  }
420
420
  }
421
421
  if ((value.state === "integrating" || value.state === "merged" || value.state === "completed") && value.integration === undefined) {
422
- violations.push(invalid("coordination.row.handoff-field", `${what}.state ${String(value.state)} requires integration`));
422
+ if (route === "standalone-development" && value.state === "completed") {
423
+ if (value.completed_at === undefined) {
424
+ violations.push(invalid("coordination.row.handoff-field", `${what}.state completed requires completed_at for a standalone handoff`));
425
+ }
426
+ if (!isNonEmptyString(value.accepted_at)) {
427
+ violations.push(invalid("coordination.row.handoff-field", `${what}.accepted_at is required for a standalone completed handoff`));
428
+ }
429
+ if (!isNonEmptyString(value.accepted_by)) {
430
+ violations.push(invalid("coordination.row.handoff-field", `${what}.accepted_by is required for a standalone completed handoff`));
431
+ }
432
+ if (value.qc === undefined) {
433
+ violations.push(invalid("coordination.row.handoff-field", `${what}.qc is required for a standalone completed handoff`));
434
+ }
435
+ if (value.qa === undefined) {
436
+ violations.push(invalid("coordination.row.handoff-field", `${what}.qa is required for a standalone completed handoff`));
437
+ }
438
+ } else {
439
+ violations.push(invalid("coordination.row.handoff-field", `${what}.state ${String(value.state)} requires integration`));
440
+ }
423
441
  }
424
442
  return violations;
425
443
  }
@@ -455,7 +473,7 @@ function validatePreparedCoordination(value, what = "coordination.prepared") {
455
473
  }
456
474
  return violations;
457
475
  }
458
- function validateRowCoordination(value, what = "coordination") {
476
+ function validateRowCoordination(value, what = "coordination", route = "integration") {
459
477
  if (!isPlainObject2(value))
460
478
  return [invalid("coordination.row.shape", `${what} must be an object`)];
461
479
  const allowed = ["revision", "prepared", "session", "progress", "handoff"];
@@ -474,7 +492,7 @@ function validateRowCoordination(value, what = "coordination") {
474
492
  if (value.progress !== undefined)
475
493
  violations.push(...validatePlanProgress(value.progress, `${what}.progress`));
476
494
  if (value.handoff !== undefined)
477
- violations.push(...validatePlanHandoff(value.handoff, `${what}.handoff`));
495
+ violations.push(...validatePlanHandoff(value.handoff, `${what}.handoff`, route));
478
496
  if (value.handoff !== undefined && value.session === undefined) {
479
497
  violations.push(invalid("coordination.row.handoff-field", `${what}.handoff requires a bound plan session`));
480
498
  }
@@ -797,6 +815,45 @@ var WORKFLOW_TERMINAL_STATUSES = ["completed", "failed", "stopped"];
797
815
  var WORKFLOW_LIFECYCLE_TYPES = ["plan", "iteration"];
798
816
  var WORKFLOW_DELIVERY_KINDS = ["development", "verification/report-only"];
799
817
  var WORKFLOW_COMPOUND_OUTCOMES = ["created", "updated", "skipped"];
818
+ function isStandaloneDevelopmentWorkflow(snapshot) {
819
+ return snapshot.type === "plan" && snapshot.delivery_kind === "development" && Array.isArray(snapshot.plans) && snapshot.plans.length === 1;
820
+ }
821
+ function rowValidationRoute(snapshot, row) {
822
+ if (isStandaloneDevelopmentWorkflow(snapshot) && snapshot.plans[0]?.id === row.id) {
823
+ return "standalone-development";
824
+ }
825
+ return "integration";
826
+ }
827
+ function validateStandaloneCompletedCoherence(snapshot, row) {
828
+ const violations = [];
829
+ if (!isStandaloneDevelopmentWorkflow(snapshot) || row.id !== snapshot.plans[0]?.id)
830
+ return violations;
831
+ const coordination = row.coordination;
832
+ if (!isPlainObject2(coordination) || !isPlainObject2(coordination.handoff))
833
+ return violations;
834
+ const handoff = coordination.handoff;
835
+ if (handoff.state !== "completed" || handoff.integration !== undefined)
836
+ return violations;
837
+ if (row.status !== "Done") {
838
+ violations.push(violation2("high", "coordination.row.handoff-field", `standalone completed handoff requires row ${String(row.id)} to be Done`));
839
+ }
840
+ if (row.execution_lease !== undefined) {
841
+ violations.push(violation2("high", "coordination.row.handoff-field", `standalone completed handoff requires no execution lease on row ${String(row.id)}`));
842
+ }
843
+ if (snapshot.integration_merge_lease !== undefined) {
844
+ violations.push(violation2("high", "coordination.row.handoff-field", "standalone completed handoff requires no integration_merge_lease on the snapshot"));
845
+ }
846
+ const source = snapshot.branch?.source;
847
+ const target = snapshot.branch?.target;
848
+ if (!isNonEmptyString(source) || !isNonEmptyString(target)) {
849
+ violations.push(violation2("high", "coordination.row.handoff-field", "standalone completed handoff requires nonblank branch.source and branch.target"));
850
+ } else {
851
+ if (handoff.source_branch !== source) {
852
+ violations.push(violation2("high", "coordination.row.handoff-field", `standalone completed handoff source_branch ${String(handoff.source_branch)} must equal branch.source ${source}`));
853
+ }
854
+ }
855
+ return violations;
856
+ }
800
857
  function stableJson(value) {
801
858
  if (Array.isArray(value))
802
859
  return `[${value.map(stableJson).join(",")}]`;
@@ -916,13 +973,17 @@ function validateWorkflowSnapshot(doc) {
916
973
  } else if (!Array.isArray(doc.plans)) {
917
974
  violations.push(violation2("high", "workflow.snapshot.invalid-plans", "plans must be an array of legacy plan rows"));
918
975
  } else {
976
+ const snapshotDoc = doc;
919
977
  for (const row of doc.plans) {
920
978
  violations.push(...validatePlanRow(row).violations);
921
979
  if (isPlainObject2(row) && row.execution_lease !== undefined) {
922
980
  violations.push(...validateExecutionLease(row.execution_lease).violations);
923
981
  }
924
982
  if (isPlainObject2(row) && row.coordination !== undefined) {
925
- violations.push(...validateRowCoordination(row.coordination, `plans[${String(row.id)}].coordination`));
983
+ const planRow = row;
984
+ const route = rowValidationRoute(snapshotDoc, planRow);
985
+ violations.push(...validateRowCoordination(row.coordination, `plans[${String(row.id)}].coordination`, route));
986
+ violations.push(...validateStandaloneCompletedCoherence(snapshotDoc, planRow));
926
987
  }
927
988
  }
928
989
  }
@@ -1,6 +1,6 @@
1
1
  import type { ValidationResult } from "./core.js";
2
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"];
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", "coordination.delivery-source-repair.unsupported-workflow", "coordination.delivery-source-repair.terminal", "coordination.delivery-source-repair.no-accepted-handoff", "coordination.delivery-source-repair.already-aligned", "coordination.delivery-source-repair.not-legacy-shape", "coordination.delivery-source-repair.pr-conflict"];
4
4
  export type CoordinationErrorCode = (typeof COORDINATION_ERROR_CODES)[number];
5
5
  /**
6
6
  * Stable exception of the coordination surface: `code` is the consumer
@@ -156,12 +156,14 @@ export declare const HANDOFF_STATES: readonly HandoffState[];
156
156
  export declare const PLAN_PROGRESS_STATUSES: readonly PlanProgressStatus[];
157
157
  /** Validate a stored `PlanProgress` (`status`, `summary`, `evidence_paths`, `track_branches`). */
158
158
  export declare function validatePlanProgress(value: unknown, what?: string): ValidationResult[];
159
+ /** Route-aware stored handoff validation (spec A5). Default remains strict integration. */
160
+ export type RowValidationRoute = "integration" | "standalone-development";
159
161
  /** Validate a stored `PlanHandoff`, including its state/field coherence. */
160
- export declare function validatePlanHandoff(value: unknown, what?: string): ValidationResult[];
162
+ export declare function validatePlanHandoff(value: unknown, what?: string, route?: RowValidationRoute): ValidationResult[];
161
163
  /** Validate a stored `PreparedCoordination`. */
162
164
  export declare function validatePreparedCoordination(value: unknown, what?: string): ValidationResult[];
163
165
  /** Validate one plan row's `coordination` object (spec §C2). */
164
- export declare function validateRowCoordination(value: unknown, what?: string): ValidationResult[];
166
+ export declare function validateRowCoordination(value: unknown, what?: string, route?: RowValidationRoute): ValidationResult[];
165
167
  /** Validate a snapshot's top `coordination` block (spec §C2). */
166
168
  export declare function validateSnapshotCoordination(value: unknown, what?: string): ValidationResult[];
167
169
  /** Hash-pinned evidence reference for an absolute path, read from disk. */
@@ -197,6 +197,9 @@ export type PlanCoordinationOperation = {
197
197
  } | {
198
198
  kind: "complete";
199
199
  handoffId: string;
200
+ } | {
201
+ kind: "repair-delivery-source";
202
+ handoffId: string;
200
203
  } | {
201
204
  kind: "reconcile";
202
205
  handoffId: string;
@@ -272,6 +275,8 @@ export declare function bindPlanSession(input: BindPlanSessionInput): Promise<Co
272
275
  * an unknown key anywhere is rejected before any state is touched.
273
276
  */
274
277
  export declare function mutatePlanCoordination(request: CoordinationRequest): Promise<CoordinationResult>;
278
+ /** Test-only hook to observe the precheck→mutate gap in standalone complete. */
279
+ export declare function setCompleteStandaloneMutateGapForTest(callback: (() => void) | undefined): void;
275
280
  /**
276
281
  * Replace a coordinated artifact with an exact-version precondition (spec §B,
277
282
  * §C4 line 156). Snapshot replacement goes through the canonical snapshot
package/dist/engine.js CHANGED
@@ -623,7 +623,7 @@ function validatePlanProgress(value, what = "coordination.progress") {
623
623
  }
624
624
  return violations;
625
625
  }
626
- function validatePlanHandoff(value, what = "coordination.handoff") {
626
+ function validatePlanHandoff(value, what = "coordination.handoff", route = "integration") {
627
627
  if (!isPlainObject2(value))
628
628
  return [invalid("coordination.row.handoff-shape", `${what} must be an object`)];
629
629
  const allowed = [
@@ -763,7 +763,25 @@ function validatePlanHandoff(value, what = "coordination.handoff") {
763
763
  }
764
764
  }
765
765
  if ((value.state === "integrating" || value.state === "merged" || value.state === "completed") && value.integration === undefined) {
766
- violations.push(invalid("coordination.row.handoff-field", `${what}.state ${String(value.state)} requires integration`));
766
+ if (route === "standalone-development" && value.state === "completed") {
767
+ if (value.completed_at === undefined) {
768
+ violations.push(invalid("coordination.row.handoff-field", `${what}.state completed requires completed_at for a standalone handoff`));
769
+ }
770
+ if (!isNonEmptyString(value.accepted_at)) {
771
+ violations.push(invalid("coordination.row.handoff-field", `${what}.accepted_at is required for a standalone completed handoff`));
772
+ }
773
+ if (!isNonEmptyString(value.accepted_by)) {
774
+ violations.push(invalid("coordination.row.handoff-field", `${what}.accepted_by is required for a standalone completed handoff`));
775
+ }
776
+ if (value.qc === undefined) {
777
+ violations.push(invalid("coordination.row.handoff-field", `${what}.qc is required for a standalone completed handoff`));
778
+ }
779
+ if (value.qa === undefined) {
780
+ violations.push(invalid("coordination.row.handoff-field", `${what}.qa is required for a standalone completed handoff`));
781
+ }
782
+ } else {
783
+ violations.push(invalid("coordination.row.handoff-field", `${what}.state ${String(value.state)} requires integration`));
784
+ }
767
785
  }
768
786
  return violations;
769
787
  }
@@ -799,7 +817,7 @@ function validatePreparedCoordination(value, what = "coordination.prepared") {
799
817
  }
800
818
  return violations;
801
819
  }
802
- function validateRowCoordination(value, what = "coordination") {
820
+ function validateRowCoordination(value, what = "coordination", route = "integration") {
803
821
  if (!isPlainObject2(value))
804
822
  return [invalid("coordination.row.shape", `${what} must be an object`)];
805
823
  const allowed = ["revision", "prepared", "session", "progress", "handoff"];
@@ -818,7 +836,7 @@ function validateRowCoordination(value, what = "coordination") {
818
836
  if (value.progress !== undefined)
819
837
  violations.push(...validatePlanProgress(value.progress, `${what}.progress`));
820
838
  if (value.handoff !== undefined)
821
- violations.push(...validatePlanHandoff(value.handoff, `${what}.handoff`));
839
+ violations.push(...validatePlanHandoff(value.handoff, `${what}.handoff`, route));
822
840
  if (value.handoff !== undefined && value.session === undefined) {
823
841
  violations.push(invalid("coordination.row.handoff-field", `${what}.handoff requires a bound plan session`));
824
842
  }
@@ -1331,6 +1349,45 @@ var WORKFLOW_TERMINAL_STATUSES = ["completed", "failed", "stopped"];
1331
1349
  var WORKFLOW_LIFECYCLE_TYPES = ["plan", "iteration"];
1332
1350
  var WORKFLOW_DELIVERY_KINDS = ["development", "verification/report-only"];
1333
1351
  var WORKFLOW_COMPOUND_OUTCOMES = ["created", "updated", "skipped"];
1352
+ function isStandaloneDevelopmentWorkflow(snapshot) {
1353
+ return snapshot.type === "plan" && snapshot.delivery_kind === "development" && Array.isArray(snapshot.plans) && snapshot.plans.length === 1;
1354
+ }
1355
+ function rowValidationRoute(snapshot, row) {
1356
+ if (isStandaloneDevelopmentWorkflow(snapshot) && snapshot.plans[0]?.id === row.id) {
1357
+ return "standalone-development";
1358
+ }
1359
+ return "integration";
1360
+ }
1361
+ function validateStandaloneCompletedCoherence(snapshot, row) {
1362
+ const violations = [];
1363
+ if (!isStandaloneDevelopmentWorkflow(snapshot) || row.id !== snapshot.plans[0]?.id)
1364
+ return violations;
1365
+ const coordination = row.coordination;
1366
+ if (!isPlainObject2(coordination) || !isPlainObject2(coordination.handoff))
1367
+ return violations;
1368
+ const handoff = coordination.handoff;
1369
+ if (handoff.state !== "completed" || handoff.integration !== undefined)
1370
+ return violations;
1371
+ if (row.status !== "Done") {
1372
+ violations.push(violation3("high", "coordination.row.handoff-field", `standalone completed handoff requires row ${String(row.id)} to be Done`));
1373
+ }
1374
+ if (row.execution_lease !== undefined) {
1375
+ violations.push(violation3("high", "coordination.row.handoff-field", `standalone completed handoff requires no execution lease on row ${String(row.id)}`));
1376
+ }
1377
+ if (snapshot.integration_merge_lease !== undefined) {
1378
+ violations.push(violation3("high", "coordination.row.handoff-field", "standalone completed handoff requires no integration_merge_lease on the snapshot"));
1379
+ }
1380
+ const source = snapshot.branch?.source;
1381
+ const target = snapshot.branch?.target;
1382
+ if (!isNonEmptyString(source) || !isNonEmptyString(target)) {
1383
+ violations.push(violation3("high", "coordination.row.handoff-field", "standalone completed handoff requires nonblank branch.source and branch.target"));
1384
+ } else {
1385
+ if (handoff.source_branch !== source) {
1386
+ violations.push(violation3("high", "coordination.row.handoff-field", `standalone completed handoff source_branch ${String(handoff.source_branch)} must equal branch.source ${source}`));
1387
+ }
1388
+ }
1389
+ return violations;
1390
+ }
1334
1391
  function stableJson(value) {
1335
1392
  if (Array.isArray(value))
1336
1393
  return `[${value.map(stableJson).join(",")}]`;
@@ -1450,13 +1507,17 @@ function validateWorkflowSnapshot(doc) {
1450
1507
  } else if (!Array.isArray(doc.plans)) {
1451
1508
  violations.push(violation3("high", "workflow.snapshot.invalid-plans", "plans must be an array of legacy plan rows"));
1452
1509
  } else {
1510
+ const snapshotDoc = doc;
1453
1511
  for (const row of doc.plans) {
1454
1512
  violations.push(...validatePlanRow(row).violations);
1455
1513
  if (isPlainObject2(row) && row.execution_lease !== undefined) {
1456
1514
  violations.push(...validateExecutionLease(row.execution_lease).violations);
1457
1515
  }
1458
1516
  if (isPlainObject2(row) && row.coordination !== undefined) {
1459
- violations.push(...validateRowCoordination(row.coordination, `plans[${String(row.id)}].coordination`));
1517
+ const planRow = row;
1518
+ const route = rowValidationRoute(snapshotDoc, planRow);
1519
+ violations.push(...validateRowCoordination(row.coordination, `plans[${String(row.id)}].coordination`, route));
1520
+ violations.push(...validateStandaloneCompletedCoherence(snapshotDoc, planRow));
1460
1521
  }
1461
1522
  }
1462
1523
  }
@@ -9906,6 +9967,7 @@ var IMPLEMENTED_OPERATIONS = {
9906
9967
  "integration-start": true,
9907
9968
  "integration-accept": true,
9908
9969
  complete: true,
9970
+ "repair-delivery-source": true,
9909
9971
  reconcile: true
9910
9972
  };
9911
9973
  function nowIso() {
@@ -10465,7 +10527,11 @@ function allowedOperations(role, sessionId, snapshot, row) {
10465
10527
  out.push("accept", "return");
10466
10528
  break;
10467
10529
  case "accepted":
10468
- out.push("return", "integration-start");
10530
+ if (isStandaloneDevelopmentWorkflow(snapshot)) {
10531
+ out.push("return", "complete", "repair-delivery-source");
10532
+ } else {
10533
+ out.push("return", "integration-start");
10534
+ }
10469
10535
  break;
10470
10536
  case "integrating":
10471
10537
  out.push("integration-accept", "complete", "reconcile");
@@ -11240,6 +11306,13 @@ async function mutatePlanCoordination(request) {
11240
11306
  expectedRevision
11241
11307
  });
11242
11308
  }
11309
+ case "repair-delivery-source": {
11310
+ assertExactKeys(operation, ["kind", "handoffId"], "repair-delivery-source operation");
11311
+ return mutateRepairDeliverySource(await coordinatorScope(session, request.planId, kind), session, sessionAbs, {
11312
+ handoffId: namedHandoffId(operation),
11313
+ expectedRevision
11314
+ });
11315
+ }
11243
11316
  case "reconcile": {
11244
11317
  assertExactKeys(operation, ["kind", "handoffId"], "reconcile operation");
11245
11318
  return mutateReconcile(await coordinatorScope(session, request.planId, kind), session, sessionAbs, {
@@ -11270,6 +11343,7 @@ var COORDINATOR_OPERATIONS = [
11270
11343
  "integration-start",
11271
11344
  "integration-accept",
11272
11345
  "complete",
11346
+ "repair-delivery-source",
11273
11347
  "reconcile"
11274
11348
  ];
11275
11349
  function assertSessionRole(session, kind) {
@@ -11910,6 +11984,313 @@ async function mutateIntegrationAccept(scope, session, sessionPath, request) {
11910
11984
  view: buildView(scope.harnessRoot, scope.workflowId, scope.projectId, scope, result.snapshot, result.row, session, sessionPath)
11911
11985
  };
11912
11986
  }
11987
+ function validateRowCoordinationInContext(context, coordination, what) {
11988
+ assertViolationFree(validateRowCoordination(coordination, what, rowValidationRoute(context.snapshot, context.row)), what);
11989
+ }
11990
+ function standaloneDeliveryAnchors(snapshot, planId) {
11991
+ const source = snapshot.branch?.source;
11992
+ const target = snapshot.branch?.target;
11993
+ if (!isNonEmptyString(source) || !isNonEmptyString(target)) {
11994
+ throw new CoordinationError("coordination.invalid-transition", `plan ${planId} has no delivery anchors — the snapshot must name branch.source and branch.target`, { plan_id: planId });
11995
+ }
11996
+ return { source, target };
11997
+ }
11998
+ function assertStandaloneRoute(snapshot, planId, what) {
11999
+ if (!isStandaloneDevelopmentWorkflow(snapshot)) {
12000
+ throw new CoordinationError("coordination.invalid-transition", `${what} requires a standalone development workflow for plan ${planId}`, { plan_id: planId });
12001
+ }
12002
+ }
12003
+ function assertNoIntegrationContamination(context, planId, handoff, what, code = "coordination.invalid-transition") {
12004
+ if (handoff.integration !== undefined) {
12005
+ throw new CoordinationError(code, `${what} refuses plan ${planId} because the handoff already carries an integration record`, { plan_id: planId });
12006
+ }
12007
+ if (context.snapshot.integration_worktree_path !== undefined) {
12008
+ throw new CoordinationError(code, `${what} refuses plan ${planId} because the snapshot names integration_worktree_path`, { plan_id: planId });
12009
+ }
12010
+ if (isNonEmptyString(context.snapshot.branch?.integration)) {
12011
+ throw new CoordinationError(code, `${what} refuses plan ${planId} because the snapshot names branch.integration`, { plan_id: planId, integration: context.snapshot.branch?.integration });
12012
+ }
12013
+ if (context.snapshot.integration_merge_lease !== undefined) {
12014
+ throw new CoordinationError(code, `${what} refuses plan ${planId} because the snapshot carries an integration merge lease`, { plan_id: planId });
12015
+ }
12016
+ }
12017
+ function assertAcceptedReviewDecision(handoff, planId, what) {
12018
+ if (handoff.qc.decision !== "Approve" && handoff.qc.decision !== "Approve with residuals") {
12019
+ throw new CoordinationError("coordination.invalid-transition", `${what} requires an accepted QC decision for plan ${planId} — got ${handoff.qc.decision}`, { plan_id: planId, decision: handoff.qc.decision });
12020
+ }
12021
+ if (handoff.qa.decision !== "pass") {
12022
+ throw new CoordinationError("coordination.invalid-transition", `${what} requires QA decision pass for plan ${planId} — got ${handoff.qa.decision}`, { plan_id: planId, decision: handoff.qa.decision });
12023
+ }
12024
+ }
12025
+ function assertStandaloneBranchIdentity(context, scope, handoff, anchors, what, requireLease = true) {
12026
+ const worktree = canonicalTarget(scope.worktreePath);
12027
+ if (handoff.source_branch !== anchors.source) {
12028
+ throw new CoordinationError("coordination.scope-mismatch", `${what} requires handoff.source_branch ${handoff.source_branch} to equal the registered delivery source ${anchors.source}`, { plan_id: scope.planId, expected: anchors.source, actual: handoff.source_branch });
12029
+ }
12030
+ if (requireLease) {
12031
+ const lease = requireExecutionLease(context.row, scope.planId, what);
12032
+ if (scope.workingBranch !== anchors.source) {
12033
+ throw new CoordinationError("coordination.scope-mismatch", `${what} requires the prepared working branch ${scope.workingBranch} to equal the registered delivery source ${anchors.source}`, { plan_id: scope.planId, expected: anchors.source, actual: scope.workingBranch });
12034
+ }
12035
+ if (lease.working_branch !== anchors.source) {
12036
+ throw new CoordinationError("coordination.scope-mismatch", `${what} requires the execution lease working branch ${lease.working_branch} to equal the registered delivery source ${anchors.source}`, { plan_id: scope.planId, expected: anchors.source, actual: lease.working_branch });
12037
+ }
12038
+ if (canonicalTarget(lease.worktree_path) !== worktree) {
12039
+ throw new CoordinationError("coordination.path-mismatch", `${what} requires the execution lease worktree ${lease.worktree_path} to equal the prepared scope ${worktree}`, { plan_id: scope.planId, expected: worktree, actual: lease.worktree_path });
12040
+ }
12041
+ }
12042
+ if (canonicalTarget(handoff.worktree_path) !== worktree) {
12043
+ throw new CoordinationError("coordination.path-mismatch", `${what} requires the handoff worktree ${handoff.worktree_path} to equal the prepared scope ${worktree}`, { plan_id: scope.planId, expected: worktree, actual: handoff.worktree_path });
12044
+ }
12045
+ const metadata = context.row.metadata;
12046
+ if (isPlainObject2(metadata) && metadata.working_branch !== undefined && metadata.working_branch !== anchors.source) {
12047
+ throw new CoordinationError("coordination.scope-mismatch", `${what} requires row metadata.working_branch to equal the registered delivery source ${anchors.source}`, { plan_id: scope.planId, expected: anchors.source, actual: metadata.working_branch });
12048
+ }
12049
+ if (isPlainObject2(metadata) && metadata.worktree_path !== undefined && canonicalTarget(String(metadata.worktree_path)) !== worktree) {
12050
+ throw new CoordinationError("coordination.path-mismatch", `${what} requires row metadata.worktree_path to equal the prepared scope ${worktree}`, { plan_id: scope.planId, expected: worktree, actual: metadata.worktree_path });
12051
+ }
12052
+ }
12053
+ function assertStandaloneSourceGitProof(scope, handoff, sourceBranch, what) {
12054
+ assertFeatureCheckout(scope, handoff.source_sha, what);
12055
+ const branch = gitRead(scope.worktreePath, ["rev-parse", "--abbrev-ref", "HEAD"]);
12056
+ if (branch !== sourceBranch) {
12057
+ throw gitProof(`${what} requires the plan worktree ${scope.worktreePath} to be on ${sourceBranch} — got ${branch || "a detached HEAD"}`, { plan_id: scope.planId, expected: sourceBranch, actual: branch });
12058
+ }
12059
+ const refTip = gitRead(scope.worktreePath, ["rev-parse", `refs/heads/${sourceBranch}`]);
12060
+ if (refTip !== handoff.source_sha) {
12061
+ throw gitProof(`${what} requires refs/heads/${sourceBranch} to resolve to the pinned source ${handoff.source_sha} — got ${refTip || "missing"}`, { plan_id: scope.planId, expected: handoff.source_sha, actual: refTip });
12062
+ }
12063
+ if (handoff.review_head !== handoff.source_sha) {
12064
+ throw gitProof(`${what} requires review_head to be the pinned source ${handoff.source_sha} — got ${handoff.review_head}`, { plan_id: scope.planId, source_sha: handoff.source_sha, review_head: handoff.review_head });
12065
+ }
12066
+ if (!gitObjectExists(scope.worktreePath, handoff.review_base)) {
12067
+ throw gitProof(`${what} review base ${handoff.review_base} is not a commit of ${scope.worktreePath}`, {
12068
+ plan_id: scope.planId,
12069
+ review_base: handoff.review_base
12070
+ });
12071
+ }
12072
+ if (!gitIsAncestor(scope.worktreePath, handoff.review_base, handoff.review_head)) {
12073
+ throw gitProof(`${what} review range ${handoff.review_base}..${handoff.review_head} is not an ancestry`, { plan_id: scope.planId, review_base: handoff.review_base, review_head: handoff.review_head });
12074
+ }
12075
+ }
12076
+ async function assertStandaloneCompletionPrecheck(context, scope, session, handoff) {
12077
+ assertStandaloneRoute(context.snapshot, scope.planId, "complete");
12078
+ if (context.snapshot.status !== "running") {
12079
+ throw new CoordinationError("coordination.invalid-transition", `complete requires workflow ${context.snapshot.id} to still be running — got ${context.snapshot.status}`, { workflow_id: context.snapshot.id, status: context.snapshot.status });
12080
+ }
12081
+ if (handoff.state !== "accepted") {
12082
+ throw new CoordinationError("coordination.invalid-transition", `plan ${scope.planId} handoff is ${handoff.state} — standalone complete requires an accepted handoff`, { plan_id: scope.planId, state: handoff.state });
12083
+ }
12084
+ if (rowStatusOf(context.row) !== "InReview") {
12085
+ throw new CoordinationError("coordination.invalid-transition", `complete requires ${scope.planId} to still be InReview`, { plan_id: scope.planId, status: context.row.status });
12086
+ }
12087
+ const prepared = context.coordination?.prepared;
12088
+ if (prepared === undefined) {
12089
+ throw new CoordinationError("coordination.not-prepared", `plan ${scope.planId} is not prepared in this workflow`, { plan_id: scope.planId });
12090
+ }
12091
+ assertNoIntegrationContamination(context, scope.planId, handoff, "complete");
12092
+ assertEvidenceDigests(handoff);
12093
+ assertAcceptedReviewDecision(handoff, scope.planId, "complete");
12094
+ if (handoff.qa.gate !== prepared.qa_gate) {
12095
+ throw new CoordinationError("coordination.assignment-stale", `complete qa.gate ${handoff.qa.gate} is not the Assignment's QA gate ${prepared.qa_gate}`, { plan_id: scope.planId, expected: prepared.qa_gate, actual: handoff.qa.gate });
12096
+ }
12097
+ await assertFindingsClosed(scope, prepared, "complete");
12098
+ assertExecutionHolder(context.row, session.session_id, scope.planId, "complete");
12099
+ const anchors = standaloneDeliveryAnchors(context.snapshot, scope.planId);
12100
+ assertStandaloneBranchIdentity(context, scope, handoff, anchors, "complete");
12101
+ assertStandaloneSourceGitProof(scope, handoff, anchors.source, "complete");
12102
+ }
12103
+ function requireAcceptedHandoffForRepair(context, planId, namedHandoffId2) {
12104
+ const handoff = context.coordination?.handoff;
12105
+ if (handoff === undefined || handoff.id !== namedHandoffId2 || handoff.state !== "accepted" || rowStatusOf(context.row) !== "InReview") {
12106
+ throw new CoordinationError("coordination.delivery-source-repair.no-accepted-handoff", `repair-delivery-source requires plan ${planId} to carry the named accepted handoff while InReview`, { plan_id: planId, handoff_id: namedHandoffId2, state: handoff?.state, status: context.row.status });
12107
+ }
12108
+ return handoff;
12109
+ }
12110
+ function assertRepairNotTerminal(snapshot) {
12111
+ if (WORKFLOW_TERMINAL_STATUSES.includes(snapshot.status)) {
12112
+ throw new CoordinationError("coordination.delivery-source-repair.terminal", `repair-delivery-source refuses workflow ${snapshot.id} in terminal status ${snapshot.status}`, { workflow_id: snapshot.id, status: snapshot.status });
12113
+ }
12114
+ }
12115
+ function assertLegacyRepairShape(snapshot, planId, handoff) {
12116
+ const source = snapshot.branch?.source;
12117
+ const target = snapshot.branch?.target;
12118
+ if (!isNonEmptyString(source) || !isNonEmptyString(target)) {
12119
+ throw new CoordinationError("coordination.delivery-source-repair.not-legacy-shape", `repair-delivery-source requires nonblank delivery anchors on plan ${planId}`, { plan_id: planId });
12120
+ }
12121
+ const candidateSource = handoff.source_branch;
12122
+ if (!isNonEmptyString(candidateSource)) {
12123
+ throw new CoordinationError("coordination.delivery-source-repair.not-legacy-shape", `repair-delivery-source requires the accepted handoff to name a nonblank source branch for plan ${planId}`, { plan_id: planId });
12124
+ }
12125
+ if (source === candidateSource) {
12126
+ throw new CoordinationError("coordination.delivery-source-repair.already-aligned", `repair-delivery-source refuses plan ${planId} because branch.source already equals the accepted handoff source ${candidateSource}`, { plan_id: planId, source, candidate: candidateSource });
12127
+ }
12128
+ if (source !== target) {
12129
+ throw new CoordinationError("coordination.delivery-source-repair.not-legacy-shape", `repair-delivery-source requires the legacy shape branch.source === branch.target for plan ${planId} — got source ${source} and target ${target}`, { plan_id: planId, source, target });
12130
+ }
12131
+ return { target, candidateSource };
12132
+ }
12133
+ function assertRepairBranchIdentity(context, scope, handoff, candidateSource, what) {
12134
+ const worktree = canonicalTarget(scope.worktreePath);
12135
+ if (handoff.source_branch !== candidateSource) {
12136
+ throw new CoordinationError("coordination.scope-mismatch", `${what} requires handoff.source_branch ${handoff.source_branch} to equal the candidate delivery source ${candidateSource}`, { plan_id: scope.planId, expected: candidateSource, actual: handoff.source_branch });
12137
+ }
12138
+ const lease = requireExecutionLease(context.row, scope.planId, what);
12139
+ if (scope.workingBranch !== candidateSource) {
12140
+ throw new CoordinationError("coordination.scope-mismatch", `${what} requires the prepared working branch ${scope.workingBranch} to equal the candidate delivery source ${candidateSource}`, { plan_id: scope.planId, expected: candidateSource, actual: scope.workingBranch });
12141
+ }
12142
+ if (lease.working_branch !== candidateSource) {
12143
+ throw new CoordinationError("coordination.scope-mismatch", `${what} requires the execution lease working branch ${lease.working_branch} to equal the candidate delivery source ${candidateSource}`, { plan_id: scope.planId, expected: candidateSource, actual: lease.working_branch });
12144
+ }
12145
+ if (canonicalTarget(lease.worktree_path) !== worktree) {
12146
+ throw new CoordinationError("coordination.path-mismatch", `${what} requires the execution lease worktree ${lease.worktree_path} to equal the prepared scope ${worktree}`, { plan_id: scope.planId, expected: worktree, actual: lease.worktree_path });
12147
+ }
12148
+ if (canonicalTarget(handoff.worktree_path) !== worktree) {
12149
+ throw new CoordinationError("coordination.path-mismatch", `${what} requires the handoff worktree ${handoff.worktree_path} to equal the prepared scope ${worktree}`, { plan_id: scope.planId, expected: worktree, actual: handoff.worktree_path });
12150
+ }
12151
+ const metadata = context.row.metadata;
12152
+ if (isPlainObject2(metadata) && metadata.working_branch !== undefined && metadata.working_branch !== candidateSource) {
12153
+ throw new CoordinationError("coordination.scope-mismatch", `${what} requires row metadata.working_branch to equal the candidate delivery source ${candidateSource}`, { plan_id: scope.planId, expected: candidateSource, actual: metadata.working_branch });
12154
+ }
12155
+ if (isPlainObject2(metadata) && metadata.worktree_path !== undefined && canonicalTarget(String(metadata.worktree_path)) !== worktree) {
12156
+ throw new CoordinationError("coordination.path-mismatch", `${what} requires row metadata.worktree_path to equal the prepared scope ${worktree}`, { plan_id: scope.planId, expected: worktree, actual: metadata.worktree_path });
12157
+ }
12158
+ }
12159
+ function assertDeliveryPrCompatible(snapshot, candidateSource, registeredTarget, planId) {
12160
+ const pr = snapshot.delivery?.pr;
12161
+ if (pr === undefined)
12162
+ return;
12163
+ if (pr.head !== candidateSource || pr.target !== registeredTarget) {
12164
+ throw new CoordinationError("coordination.delivery-source-repair.pr-conflict", `repair-delivery-source refuses plan ${planId} because stored PR identity head ${JSON.stringify(pr.head)} target ${JSON.stringify(pr.target)} conflicts with candidate source ${candidateSource} and registered target ${registeredTarget}`, { plan_id: planId, pr_head: pr.head, pr_target: pr.target, candidate: candidateSource, target: registeredTarget });
12165
+ }
12166
+ }
12167
+ function assertRepairDeliverySourceAdmission(context, scope) {
12168
+ if (!isStandaloneDevelopmentWorkflow(context.snapshot)) {
12169
+ throw new CoordinationError("coordination.delivery-source-repair.unsupported-workflow", `repair-delivery-source requires a single-row standalone development workflow for plan ${scope.planId}`, { plan_id: scope.planId, workflow_id: context.snapshot.id });
12170
+ }
12171
+ assertRepairNotTerminal(context.snapshot);
12172
+ if (context.snapshot.status === "paused") {
12173
+ throw new CoordinationError("coordination.invalid-transition", `repair-delivery-source refuses paused workflow ${context.snapshot.id}`, { workflow_id: context.snapshot.id, status: context.snapshot.status });
12174
+ }
12175
+ if (context.snapshot.status !== "running") {
12176
+ throw new CoordinationError("coordination.invalid-transition", `repair-delivery-source requires workflow ${context.snapshot.id} to still be running — got ${context.snapshot.status}`, { workflow_id: context.snapshot.id, status: context.snapshot.status });
12177
+ }
12178
+ }
12179
+ async function assertRepairDeliverySourcePrecheck(context, scope, session, handoff) {
12180
+ assertNoIntegrationContamination(context, scope.planId, handoff, "repair-delivery-source", "coordination.delivery-source-repair.not-legacy-shape");
12181
+ const prepared = context.coordination?.prepared;
12182
+ if (prepared === undefined) {
12183
+ throw new CoordinationError("coordination.not-prepared", `plan ${scope.planId} is not prepared in this workflow`, { plan_id: scope.planId });
12184
+ }
12185
+ assertEvidenceDigests(handoff);
12186
+ assertAcceptedReviewDecision(handoff, scope.planId, "repair-delivery-source");
12187
+ if (handoff.qa.gate !== prepared.qa_gate) {
12188
+ throw new CoordinationError("coordination.assignment-stale", `repair-delivery-source qa.gate ${handoff.qa.gate} is not the Assignment's QA gate ${prepared.qa_gate}`, { plan_id: scope.planId, expected: prepared.qa_gate, actual: handoff.qa.gate });
12189
+ }
12190
+ await assertFindingsClosed(scope, prepared, "repair-delivery-source");
12191
+ assertExecutionHolder(context.row, session.session_id, scope.planId, "repair-delivery-source");
12192
+ const { target, candidateSource } = assertLegacyRepairShape(context.snapshot, scope.planId, handoff);
12193
+ assertRepairBranchIdentity(context, scope, handoff, candidateSource, "repair-delivery-source");
12194
+ assertDeliveryPrCompatible(context.snapshot, candidateSource, target, scope.planId);
12195
+ assertStandaloneSourceGitProof(scope, handoff, candidateSource, "repair-delivery-source");
12196
+ }
12197
+ function repairDeliverySourceRow(context, scope, candidateSource) {
12198
+ const nextCoordination = {
12199
+ ...context.coordination ?? { revision: 0 },
12200
+ revision: context.revision + 1
12201
+ };
12202
+ validateRowCoordinationInContext(context, nextCoordination, `plan ${scope.planId} coordination`);
12203
+ const currentBranch = context.snapshot.branch ?? {};
12204
+ return {
12205
+ row: { ...context.row, coordination: nextCoordination },
12206
+ coordination: nextCoordination,
12207
+ topLevel: {
12208
+ branch: { ...currentBranch, source: candidateSource }
12209
+ }
12210
+ };
12211
+ }
12212
+ async function mutateRepairDeliverySource(scope, session, sessionPath, request) {
12213
+ const result = await withRowCommit(scope, {
12214
+ expectedRevision: request.expectedRevision,
12215
+ precheck: async (context) => {
12216
+ assertCoordinatorBinding(session, sessionPath, context.snapshot);
12217
+ assertRepairDeliverySourceAdmission(context, scope);
12218
+ const handoff = requireAcceptedHandoffForRepair(context, scope.planId, request.handoffId);
12219
+ await assertRepairDeliverySourcePrecheck(context, scope, session, handoff);
12220
+ },
12221
+ mutate: (context) => {
12222
+ const handoff = requireAcceptedHandoffForRepair(context, scope.planId, request.handoffId);
12223
+ const { candidateSource } = assertLegacyRepairShape(context.snapshot, scope.planId, handoff);
12224
+ return repairDeliverySourceRow(context, scope, candidateSource);
12225
+ }
12226
+ });
12227
+ return {
12228
+ ok: true,
12229
+ operation: "repair-delivery-source",
12230
+ session,
12231
+ session_file: sessionPath,
12232
+ outcome: "delivery-source-repaired",
12233
+ view: buildView(scope.harnessRoot, scope.workflowId, scope.projectId, scope, result.snapshot, result.row, session, sessionPath)
12234
+ };
12235
+ }
12236
+ function completeStandaloneRow(context, scope, handoff) {
12237
+ const metadata = isPlainObject2(context.row.metadata) ? { ...context.row.metadata } : {};
12238
+ metadata.working_branch = handoff.source_branch;
12239
+ metadata.worktree_path = handoff.worktree_path;
12240
+ const nextCoordination = {
12241
+ ...context.coordination ?? { revision: 0 },
12242
+ revision: context.revision + 1,
12243
+ handoff: { ...handoff, state: "completed", completed_at: nowIso() }
12244
+ };
12245
+ validateRowCoordinationInContext(context, nextCoordination, `plan ${scope.planId} coordination`);
12246
+ const nextRow = { ...context.row, status: "Done", metadata, coordination: nextCoordination };
12247
+ delete nextRow.execution_lease;
12248
+ return { row: nextRow, coordination: nextCoordination };
12249
+ }
12250
+ function assertStandaloneCompletedReplay(context, scope, handoff) {
12251
+ assertStandaloneRoute(context.snapshot, scope.planId, "reconcile");
12252
+ if (rowStatusOf(context.row) !== "Done") {
12253
+ throw new CoordinationError("coordination.invalid-transition", `reconcile requires ${scope.planId} to be Done for a standalone completed replay`, { plan_id: scope.planId, status: context.row.status });
12254
+ }
12255
+ if (context.row.execution_lease !== undefined) {
12256
+ throw new CoordinationError("coordination.invalid-transition", `reconcile requires no execution lease on ${scope.planId} for a standalone completed replay`, { plan_id: scope.planId });
12257
+ }
12258
+ if (context.snapshot.integration_merge_lease !== undefined) {
12259
+ throw new CoordinationError("coordination.invalid-transition", `reconcile requires no integration merge lease for a standalone completed replay of ${scope.planId}`, { plan_id: scope.planId });
12260
+ }
12261
+ assertNoIntegrationContamination(context, scope.planId, handoff, "reconcile");
12262
+ const storedHandoffViolations = validatePlanHandoff(handoff, `plan ${scope.planId} coordination.handoff`, "standalone-development");
12263
+ const storedHandoffFailure = storedHandoffViolations.find((entry) => !entry.ok);
12264
+ if (storedHandoffFailure !== undefined) {
12265
+ throw new CoordinationError(storedHandoffFailure.code, storedHandoffFailure.message, { plan_id: scope.planId });
12266
+ }
12267
+ const anchors = standaloneDeliveryAnchors(context.snapshot, scope.planId);
12268
+ assertStandaloneBranchIdentity(context, scope, handoff, anchors, "reconcile", false);
12269
+ const repository = proofRepository([handoff.worktree_path, scope.worktreePath, scope.harnessRoot]);
12270
+ if (repository !== undefined && !gitObjectExists(repository, handoff.source_sha)) {
12271
+ throw gitProof(`reconcile cannot re-verify the pinned standalone source ${handoff.source_sha} for plan ${scope.planId}`, { plan_id: scope.planId, source_sha: handoff.source_sha });
12272
+ }
12273
+ }
12274
+ async function assertIterationCompletionPrecheck(context, scope, session, handoff) {
12275
+ if (handoff.state !== "merged") {
12276
+ throw new CoordinationError("coordination.invalid-transition", `plan ${scope.planId} handoff is ${handoff.state} — complete requires a merged attempt`, { plan_id: scope.planId, state: handoff.state });
12277
+ }
12278
+ if (rowStatusOf(context.row) !== "InReview") {
12279
+ throw new CoordinationError("coordination.invalid-transition", `complete requires ${scope.planId} to still be InReview`, { plan_id: scope.planId, status: context.row.status });
12280
+ }
12281
+ const prepared = context.coordination?.prepared;
12282
+ if (prepared === undefined) {
12283
+ throw new CoordinationError("coordination.not-prepared", `plan ${scope.planId} is not prepared in this workflow`, { plan_id: scope.planId });
12284
+ }
12285
+ assertEvidenceDigests(handoff);
12286
+ await assertFindingsClosed(scope, prepared, "complete");
12287
+ assertExecutionHolder(context.row, session.session_id, scope.planId, "complete");
12288
+ assertMergeLease(context.snapshot, session, scope.planId, handoff);
12289
+ const integration = requireIntegration(handoff, scope.planId);
12290
+ const anchors = integrationAnchors(context.snapshot, scope.planId);
12291
+ const checkout = assertIntegrationCheckout(anchors, scope.planId);
12292
+ assertRecordedResult(anchors.worktreePath, scope.planId, integration, handoff.source_sha, checkout.head);
12293
+ }
11913
12294
  function completeRow(context, scope, handoff, resultSha) {
11914
12295
  const integration = requireIntegration(handoff, scope.planId);
11915
12296
  const completed = { ...integration, result_sha: resultSha, verified_at: nowIso() };
@@ -11928,33 +12309,27 @@ function completeRow(context, scope, handoff, resultSha) {
11928
12309
  dropTopLevel: release === undefined ? [] : ["integration_merge_lease"]
11929
12310
  };
11930
12311
  }
12312
+ var completeStandaloneMutateGapForTest;
11931
12313
  async function mutateComplete(scope, session, sessionPath, request) {
11932
12314
  const result = await withRowCommit(scope, {
11933
12315
  expectedRevision: request.expectedRevision,
11934
12316
  precheck: async (context) => {
11935
12317
  assertCoordinatorBinding(session, sessionPath, context.snapshot);
11936
12318
  const handoff = requireHandoff(context, scope.planId, request.handoffId);
11937
- if (handoff.state !== "merged") {
11938
- throw new CoordinationError("coordination.invalid-transition", `plan ${scope.planId} handoff is ${handoff.state} — complete requires a merged attempt`, { plan_id: scope.planId, state: handoff.state });
11939
- }
11940
- if (rowStatusOf(context.row) !== "InReview") {
11941
- throw new CoordinationError("coordination.invalid-transition", `complete requires ${scope.planId} to still be InReview`, { plan_id: scope.planId, status: context.row.status });
11942
- }
11943
- const prepared = context.coordination?.prepared;
11944
- if (prepared === undefined) {
11945
- throw new CoordinationError("coordination.not-prepared", `plan ${scope.planId} is not prepared in this workflow`, { plan_id: scope.planId });
12319
+ if (isStandaloneDevelopmentWorkflow(context.snapshot)) {
12320
+ await assertStandaloneCompletionPrecheck(context, scope, session, handoff);
12321
+ return;
11946
12322
  }
11947
- assertEvidenceDigests(handoff);
11948
- await assertFindingsClosed(scope, prepared, "complete");
11949
- assertExecutionHolder(context.row, session.session_id, scope.planId, "complete");
11950
- assertMergeLease(context.snapshot, session, scope.planId, handoff);
11951
- const integration = requireIntegration(handoff, scope.planId);
11952
- const anchors = integrationAnchors(context.snapshot, scope.planId);
11953
- const checkout = assertIntegrationCheckout(anchors, scope.planId);
11954
- assertRecordedResult(anchors.worktreePath, scope.planId, integration, handoff.source_sha, checkout.head);
12323
+ await assertIterationCompletionPrecheck(context, scope, session, handoff);
11955
12324
  },
11956
12325
  mutate: (context) => {
11957
12326
  const handoff = requireHandoff(context, scope.planId, request.handoffId);
12327
+ if (isStandaloneDevelopmentWorkflow(context.snapshot)) {
12328
+ completeStandaloneMutateGapForTest?.();
12329
+ const anchors = standaloneDeliveryAnchors(context.snapshot, scope.planId);
12330
+ assertStandaloneSourceGitProof(scope, handoff, anchors.source, "complete");
12331
+ return completeStandaloneRow(context, scope, handoff);
12332
+ }
11958
12333
  const integration = requireIntegration(handoff, scope.planId);
11959
12334
  const resultSha = assertRecordedResult(integration.worktree_path, scope.planId, integration, handoff.source_sha, undefined);
11960
12335
  return completeRow(context, scope, handoff, resultSha);
@@ -11978,6 +12353,10 @@ async function classifyReconcile(context, scope, session, handoff, expectedHando
11978
12353
  });
11979
12354
  }
11980
12355
  if (handoff.state === "completed") {
12356
+ if (isStandaloneDevelopmentWorkflow(context.snapshot)) {
12357
+ assertStandaloneCompletedReplay(context, scope, handoff);
12358
+ return { outcome: "already-completed", apply: () => null };
12359
+ }
11981
12360
  const integration = requireIntegration(handoff, planId);
11982
12361
  const repository = proofRepository([integration.worktree_path, handoff.worktree_path, session.harness_root]);
11983
12362
  if (repository === undefined) {
@@ -1,5 +1,5 @@
1
1
  import { type GateResult, type ValidationResult } from "./core.js";
2
- import { type SnapshotCoordination } from "./coordination-write.js";
2
+ import { type RowValidationRoute, type SnapshotCoordination } from "./coordination-write.js";
3
3
  import { type IntegrationMergeLease } from "./lease.js";
4
4
  import { type PlanRow } from "./status.js";
5
5
  /** Snapshot file name inside `workflows/<id>/` ( — writer contract). */
@@ -159,6 +159,10 @@ export type WorkflowSnapshot = {
159
159
  */
160
160
  delivery?: WorkflowDeliveryEvidence;
161
161
  };
162
+ /** True exactly for a single-row standalone development plan workflow (spec A1). */
163
+ export declare function isStandaloneDevelopmentWorkflow(snapshot: WorkflowSnapshot): boolean;
164
+ /** Classify row coordination validation: standalone development vs integration delivery. */
165
+ export declare function rowValidationRoute(snapshot: WorkflowSnapshot, row: PlanRow): RowValidationRoute;
162
166
  /** Stable JSON for change detection (sorted keys, recursive). */
163
167
  export declare function stableJson(value: unknown): string;
164
168
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mstar-harness/engine",
3
- "version": "3.10.2",
3
+ "version": "3.10.3",
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": {