owlrunkit 0.18.3 → 0.18.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.
Files changed (29) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/README.md +125 -52
  3. package/docs/architecture/OWLCODA_RUNKIT_0184_EXTERNAL_DELIVERY_INTAKE_PRODUCT_DECISION_20260812.md +209 -0
  4. package/docs/architecture/OWLCODA_RUNKIT_USAGE_MODE_SELECTION_PRODUCT_DECISION_20260811.md +8 -7
  5. package/integrations/codex/skills/owlcoda-runkit/SKILL.md +109 -77
  6. package/integrations/codex/skills/owlcoda-runkit/assets/templates/config.json +3 -3
  7. package/package.json +1 -1
  8. package/packages/attest/distribution-manifest-v1.json +5 -0
  9. package/packages/attest/src/quick-receipt-contract.mjs +3 -3
  10. package/packages/attest/src/quick.mjs +5 -0
  11. package/packages/attest/src/workspace-snapshot.mjs +10 -1
  12. package/registry-release-policy-v1.json +5 -5
  13. package/sbom.spdx.json +5 -5
  14. package/scripts/runkit-contract/assurance-router.mjs +25 -6
  15. package/scripts/runkit-contract/core-contract.mjs +9 -1
  16. package/scripts/runkit-contract/onboarding-doctor.mjs +1 -1
  17. package/scripts/runkit-contract/profile-onboarding.mjs +263 -15
  18. package/scripts/runkit-contract/project-cli-resolver.mjs +6 -0
  19. package/scripts/runkit-contract/project-external-delivery.mjs +707 -0
  20. package/scripts/runkit-contract/quick-verify.mjs +1 -1
  21. package/scripts/runkit-contract/runkit-bootstrap.mjs +6 -1
  22. package/scripts/runkit-contract/runkit-cli.mjs +278 -140
  23. package/scripts/runkit-contract/schemas/assurance-route-v2.schema.json +26 -0
  24. package/scripts/runkit-contract/schemas/profile-detection-v3.schema.json +170 -0
  25. package/scripts/runkit-contract/schemas/team-project-event-v3.schema.json +146 -0
  26. package/scripts/runkit-contract/schemas/team-project-status-v2.schema.json +85 -0
  27. package/scripts/runkit-contract/schemas/usage-mode-recommendation-v2.schema.json +136 -0
  28. package/scripts/runkit-contract/team-project.mjs +477 -15
  29. package/scripts/runkit-contract/usage-mode.mjs +28 -7
@@ -12,6 +12,11 @@ import path from "node:path";
12
12
 
13
13
  import { validateLeaseOwnedPaths } from "./core-contract.mjs";
14
14
  import { inspectProjectControlState } from "./project-control-state.mjs";
15
+ import { canonicalSourceFingerprint } from "./source-fingerprint.mjs";
16
+ import {
17
+ quickSnapshotFingerprintValid,
18
+ validWorkspaceSnapshotShape,
19
+ } from "../../packages/attest/src/quick-receipt-contract.mjs";
15
20
  import {
16
21
  assertAllowedKeys,
17
22
  isRecord,
@@ -24,7 +29,9 @@ const PROJECT_ROOT = ".owlcoda/runkit/project";
24
29
  const DEFINITION_SCHEMA = "OwlCodaRunKitTeamProjectDefinitionV1";
25
30
  const EVENT_SCHEMA = "OwlCodaRunKitTeamProjectEventV1";
26
31
  const EVENT_SCHEMA_V2 = "OwlCodaRunKitTeamProjectEventV2";
27
- const STATUS_SCHEMA = "OwlCodaRunKitTeamProjectStatusV1";
32
+ const EVENT_SCHEMA_V3 = "OwlCodaRunKitTeamProjectEventV3";
33
+ const STATUS_SCHEMA_V1 = "OwlCodaRunKitTeamProjectStatusV1";
34
+ const STATUS_SCHEMA_V2 = "OwlCodaRunKitTeamProjectStatusV2";
28
35
 
29
36
  const DEFINITION_KEYS = [
30
37
  "schemaVersion",
@@ -92,6 +99,17 @@ const EVENT_KEYS = Object.freeze({
92
99
  "sourceRef", "rights", "inputRef", "outputRef", "decisionRef",
93
100
  "verificationRefs", "outcomeRef", "version",
94
101
  ],
102
+ source_delivery_imported: [
103
+ "schemaVersion", "eventId", "type", "occurredAt", "deliveryId",
104
+ "projectId", "projectDefinitionSha256", "assignmentId", "workItemId",
105
+ "producerId", "targetWorkspaceRef",
106
+ "targetHead", "entrySnapshotRef", "entrySnapshotSha256",
107
+ "deliveryPacketSha256", "deliveryCandidateFingerprint",
108
+ "workspaceSnapshotFingerprint", "overlayStatus", "candidateFiles",
109
+ "changedPaths", "toolCachePaths", "dependencyEnvironmentPaths",
110
+ "unchangedPacketPaths", "outOfScopeSourcePaths", "targetSnapshot",
111
+ "proven", "notProven", "targetWritePerformed", "authorizationGranted",
112
+ ],
95
113
  });
96
114
 
97
115
  function canonical(value) {
@@ -122,6 +140,26 @@ function nullableString(value, label) {
122
140
  return nonEmptyString(value, label);
123
141
  }
124
142
 
143
+ function sha256Ref(value, label) {
144
+ if (typeof value !== "string" || !/^sha256:[a-f0-9]{64}$/u.test(value)) {
145
+ throw new Error(`${label} must be a canonical SHA-256 reference.`);
146
+ }
147
+ return value;
148
+ }
149
+
150
+ function safeSourcePath(value, label) {
151
+ if (typeof value !== "string"
152
+ || value.length === 0
153
+ || path.isAbsolute(value)
154
+ || path.win32.parse(value).root
155
+ || value.includes("\\")
156
+ || value.includes("\0")
157
+ || value.split("/").some(segment => !segment || segment === "." || segment === "..")) {
158
+ throw new Error(`${label} must be a safe repository-relative path.`);
159
+ }
160
+ return value;
161
+ }
162
+
125
163
  function stringArray(value, label, { allowEmpty = true } = {}) {
126
164
  if (!Array.isArray(value)
127
165
  || (!allowEmpty && value.length === 0)
@@ -167,6 +205,30 @@ function withProjectLock(root, operation) {
167
205
  }
168
206
  }
169
207
 
208
+ export function teamProjectDefinitionBindingV1(definition) {
209
+ const normalized = validateDefinition(definition);
210
+ return {
211
+ projectId: normalized.projectId,
212
+ projectDefinitionSha256: `sha256:${sha256(canonical(normalized))}`,
213
+ };
214
+ }
215
+
216
+ export function withTeamProjectControlLockV1({ workspaceRoot, operation }) {
217
+ if (typeof operation !== "function") {
218
+ throw new Error("Team project control operation must be a function.");
219
+ }
220
+ const loaded = loadProject(workspaceRoot);
221
+ return withProjectLock(loaded.projectRoot, () => {
222
+ const refreshed = loadProject(workspaceRoot);
223
+ return operation({
224
+ workspaceRoot: refreshed.root,
225
+ projectRoot: refreshed.projectRoot,
226
+ definition: structuredClone(refreshed.definition),
227
+ definitionBinding: teamProjectDefinitionBindingV1(refreshed.definition),
228
+ });
229
+ });
230
+ }
231
+
170
232
  function uniqueIds(rows, label) {
171
233
  const ids = rows.map(row => safeIdentifier(row.id, `${label} id`));
172
234
  if (new Set(ids).size !== ids.length) throw new Error(`${label} ids must be unique.`);
@@ -420,6 +482,135 @@ function reworkProjection(rework, attempt) {
420
482
  : null;
421
483
  }
422
484
 
485
+ function sourceDeliveryProjection(delivery) {
486
+ return delivery
487
+ ? {
488
+ projectId: delivery.projectId,
489
+ projectDefinitionSha256: delivery.projectDefinitionSha256,
490
+ deliveryId: delivery.deliveryId,
491
+ workItemId: delivery.workItemId,
492
+ assignmentId: delivery.assignmentId,
493
+ producerId: delivery.producerId,
494
+ targetWorkspaceRef: delivery.targetWorkspaceRef,
495
+ targetHead: delivery.targetHead,
496
+ entrySnapshotRef: delivery.entrySnapshotRef,
497
+ entrySnapshotSha256: delivery.entrySnapshotSha256,
498
+ deliveryPacketSha256: delivery.deliveryPacketSha256,
499
+ deliveryCandidateFingerprint: delivery.deliveryCandidateFingerprint,
500
+ workspaceSnapshotFingerprint: delivery.workspaceSnapshotFingerprint,
501
+ overlayStatus: delivery.overlayStatus,
502
+ candidateFiles: delivery.candidateFiles.map(row => ({ ...row })),
503
+ candidatePaths: delivery.candidateFiles.map(row => row.path),
504
+ changedPaths: [...delivery.changedPaths],
505
+ toolCachePaths: [...delivery.toolCachePaths],
506
+ dependencyEnvironmentPaths: [...delivery.dependencyEnvironmentPaths],
507
+ unchangedPacketPaths: [...delivery.unchangedPacketPaths],
508
+ proven: [...delivery.proven],
509
+ notProven: [...delivery.notProven],
510
+ occurredAt: delivery.occurredAt,
511
+ truthRef: delivery.ref,
512
+ targetWritePerformed: false,
513
+ authorizationGranted: false,
514
+ }
515
+ : null;
516
+ }
517
+
518
+ function sourceDeliveryDelta(deliveries) {
519
+ const current = deliveries.at(-1) ?? null;
520
+ if (!current) return null;
521
+ const previous = deliveries.at(-2) ?? null;
522
+ if (!previous) {
523
+ return {
524
+ status: "first_delivery",
525
+ currentDeliveryId: current.deliveryId,
526
+ previousDeliveryId: null,
527
+ addedPaths: current.candidateFiles.map(row => row.path),
528
+ changedPaths: [],
529
+ removedPaths: [],
530
+ unchangedPaths: [],
531
+ };
532
+ }
533
+ const currentFiles = new Map(current.candidateFiles.map(row => [row.path, row.sha256]));
534
+ const previousFiles = new Map(previous.candidateFiles.map(row => [row.path, row.sha256]));
535
+ const allPaths = uniqueSorted([...currentFiles.keys(), ...previousFiles.keys()]);
536
+ const addedPaths = allPaths.filter(filePath => (
537
+ currentFiles.has(filePath) && !previousFiles.has(filePath)
538
+ ));
539
+ const removedPaths = allPaths.filter(filePath => (
540
+ !currentFiles.has(filePath) && previousFiles.has(filePath)
541
+ ));
542
+ const changedPaths = allPaths.filter(filePath => (
543
+ currentFiles.has(filePath)
544
+ && previousFiles.has(filePath)
545
+ && currentFiles.get(filePath) !== previousFiles.get(filePath)
546
+ ));
547
+ const unchangedPaths = allPaths.filter(filePath => (
548
+ currentFiles.has(filePath)
549
+ && previousFiles.has(filePath)
550
+ && currentFiles.get(filePath) === previousFiles.get(filePath)
551
+ ));
552
+ return {
553
+ status: addedPaths.length || removedPaths.length || changedPaths.length
554
+ ? "changed"
555
+ : "unchanged",
556
+ currentDeliveryId: current.deliveryId,
557
+ previousDeliveryId: previous.deliveryId,
558
+ addedPaths,
559
+ changedPaths,
560
+ removedPaths,
561
+ unchangedPaths,
562
+ };
563
+ }
564
+
565
+ function staleHandoffsForTakeover({ loaded, status, agentId, responsibilities }) {
566
+ const responsibilityIds = new Set(responsibilities.map(row => row.workItemId));
567
+ const pendingEventIds = new Set(status.workItems
568
+ .map(row => row.pendingHandoff?.eventId)
569
+ .filter(Boolean));
570
+ return loaded.events
571
+ .filter(({ event }) => (
572
+ event.type === "handoff_recorded"
573
+ && !pendingEventIds.has(event.eventId)
574
+ && (responsibilityIds.has(event.workItemId)
575
+ || event.fromAgentId === agentId
576
+ || event.toAgentId === agentId)
577
+ ))
578
+ .map(({ event, ref }) => {
579
+ const laterFacts = loaded.events.filter(row => (
580
+ row.event.workItemId === event.workItemId
581
+ && compareEventFacts(row.event, event) > 0
582
+ && [
583
+ "agent_assigned",
584
+ "checkpoint_recorded",
585
+ "handoff_recorded",
586
+ "rework_returned",
587
+ "source_delivery_imported",
588
+ ].includes(row.event.type)
589
+ ));
590
+ let reason = "superseded_by_current_truth";
591
+ if (laterFacts.some(row => row.event.type === "handoff_recorded")) {
592
+ reason = "redirected_by_later_handoff";
593
+ } else if (laterFacts.some(row => (
594
+ row.event.type === "agent_assigned" || row.event.type === "rework_returned"
595
+ ))) {
596
+ reason = "superseded_by_assignment";
597
+ } else if (laterFacts.length > 0) {
598
+ reason = "superseded_by_later_work_fact";
599
+ }
600
+ return {
601
+ eventId: event.eventId,
602
+ truthRef: ref,
603
+ assignmentId: event.assignmentId,
604
+ workItemId: event.workItemId,
605
+ fromAgentId: event.fromAgentId,
606
+ toAgentId: event.toAgentId,
607
+ occurredAt: event.occurredAt,
608
+ reason,
609
+ };
610
+ })
611
+ .sort((left, right) => compareIdentifiers(left.eventId, right.eventId));
612
+ }
613
+
423
614
  function validateTimestamp(value) {
424
615
  if (typeof value !== "string"
425
616
  || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/u.test(value)
@@ -434,14 +625,20 @@ function validateEventShape(value, definition) {
434
625
  throw new Error("Team project event type is invalid.");
435
626
  }
436
627
  assertAllowedKeys(value, `Team project event ${value.eventId ?? "unknown"}`, EVENT_KEYS[value.type]);
437
- if (![EVENT_SCHEMA, EVENT_SCHEMA_V2].includes(value.schemaVersion)) {
628
+ if (![EVENT_SCHEMA, EVENT_SCHEMA_V2, EVENT_SCHEMA_V3].includes(value.schemaVersion)) {
438
629
  throw new Error(
439
- `Team project event schemaVersion must be ${EVENT_SCHEMA} or ${EVENT_SCHEMA_V2}.`,
630
+ `Team project event schemaVersion must be ${EVENT_SCHEMA}, ${EVENT_SCHEMA_V2}, or ${EVENT_SCHEMA_V3}.`,
440
631
  );
441
632
  }
442
633
  if (value.type === "rework_returned" && value.schemaVersion !== EVENT_SCHEMA_V2) {
443
634
  throw new Error(`rework_returned requires schemaVersion ${EVENT_SCHEMA_V2}.`);
444
635
  }
636
+ if (value.type === "source_delivery_imported" && value.schemaVersion !== EVENT_SCHEMA_V3) {
637
+ throw new Error(`source_delivery_imported requires schemaVersion ${EVENT_SCHEMA_V3}.`);
638
+ }
639
+ if (value.schemaVersion === EVENT_SCHEMA_V3 && value.type !== "source_delivery_imported") {
640
+ throw new Error(`${EVENT_SCHEMA_V3} is reserved for source_delivery_imported events.`);
641
+ }
445
642
  safeIdentifier(value.eventId, "eventId");
446
643
  validateTimestamp(value.occurredAt);
447
644
  const workItemIds = new Set(definition.workItems.map(row => row.id));
@@ -455,6 +652,7 @@ function validateEventShape(value, definition) {
455
652
  "checkpoint_recorded",
456
653
  "handoff_recorded",
457
654
  "rework_returned",
655
+ "source_delivery_imported",
458
656
  "verification_deferred",
459
657
  ].includes(value.type)) {
460
658
  assertWorkItem(value.workItemId);
@@ -464,6 +662,7 @@ function validateEventShape(value, definition) {
464
662
  "checkpoint_recorded",
465
663
  "handoff_recorded",
466
664
  "rework_returned",
665
+ "source_delivery_imported",
467
666
  ].includes(value.type)) {
468
667
  safeIdentifier(value.assignmentId, "assignmentId");
469
668
  }
@@ -582,6 +781,89 @@ function validateEventShape(value, definition) {
582
781
  stringArray(value.verificationRefs, "Data candidate verificationRefs");
583
782
  nullableString(value.outcomeRef, "Data candidate outcomeRef");
584
783
  nonEmptyString(value.version, "Data candidate version");
784
+ } else if (value.type === "source_delivery_imported") {
785
+ safeIdentifier(value.projectId, "External delivery projectId");
786
+ sha256Ref(value.projectDefinitionSha256, "External delivery projectDefinitionSha256");
787
+ const definitionBinding = teamProjectDefinitionBindingV1(definition);
788
+ if (value.projectId !== definitionBinding.projectId
789
+ || value.projectDefinitionSha256 !== definitionBinding.projectDefinitionSha256) {
790
+ throw new Error("External delivery project identity does not match current project truth.");
791
+ }
792
+ safeIdentifier(value.deliveryId, "deliveryId");
793
+ safeIdentifier(value.producerId, "producerId");
794
+ nonEmptyString(value.targetWorkspaceRef, "External delivery targetWorkspaceRef");
795
+ if (!path.isAbsolute(value.targetWorkspaceRef)) {
796
+ throw new Error("External delivery targetWorkspaceRef must be absolute.");
797
+ }
798
+ if (typeof value.targetHead !== "string"
799
+ || !/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/u.test(value.targetHead)) {
800
+ throw new Error("External delivery targetHead is invalid.");
801
+ }
802
+ if ((value.entrySnapshotRef === null) !== (value.entrySnapshotSha256 === null)) {
803
+ throw new Error("External delivery entry snapshot ref and hash must be supplied together.");
804
+ }
805
+ if (value.entrySnapshotRef !== null) {
806
+ nonEmptyString(value.entrySnapshotRef, "External delivery entrySnapshotRef");
807
+ sha256Ref(value.entrySnapshotSha256, "External delivery entrySnapshotSha256");
808
+ }
809
+ sha256Ref(value.deliveryPacketSha256, "External delivery packet hash");
810
+ if (typeof value.deliveryCandidateFingerprint !== "string"
811
+ || !/^[a-f0-9]{64}$/u.test(value.deliveryCandidateFingerprint)) {
812
+ throw new Error("External delivery candidate fingerprint is invalid.");
813
+ }
814
+ sha256Ref(value.workspaceSnapshotFingerprint, "External delivery workspace snapshot fingerprint");
815
+ if (!["candidate_only", "attested", "attested_with_non_source_changes"].includes(value.overlayStatus)) {
816
+ throw new Error("External delivery overlayStatus is invalid.");
817
+ }
818
+ if (!Array.isArray(value.candidateFiles) || value.candidateFiles.length === 0) {
819
+ throw new Error("External delivery candidateFiles must contain at least one file.");
820
+ }
821
+ const candidateFiles = value.candidateFiles.map((row) => {
822
+ assertAllowedKeys(row, "External delivery candidate file", ["path", "sha256"]);
823
+ const filePath = safeSourcePath(row.path, "External delivery candidate file path");
824
+ if (typeof row.sha256 !== "string" || !/^[a-f0-9]{64}$/u.test(row.sha256)) {
825
+ throw new Error(`External delivery candidate file hash is invalid: ${filePath}.`);
826
+ }
827
+ return { path: filePath, sha256: row.sha256 };
828
+ });
829
+ if (new Set(candidateFiles.map(row => row.path)).size !== candidateFiles.length
830
+ || canonical([...candidateFiles].sort((left, right) => (
831
+ compareIdentifiers(left.path, right.path)
832
+ )))
833
+ !== canonical(candidateFiles)) {
834
+ throw new Error("External delivery candidateFiles must be uniquely sorted by path.");
835
+ }
836
+ if (canonicalSourceFingerprint(Object.fromEntries(
837
+ candidateFiles.map(row => [row.path, row.sha256]),
838
+ )) !== value.deliveryCandidateFingerprint) {
839
+ throw new Error("External delivery candidate fingerprint does not match its file map.");
840
+ }
841
+ for (const [field, label] of [
842
+ ["changedPaths", "External delivery changedPaths"],
843
+ ["toolCachePaths", "External delivery toolCachePaths"],
844
+ ["dependencyEnvironmentPaths", "External delivery dependencyEnvironmentPaths"],
845
+ ["unchangedPacketPaths", "External delivery unchangedPacketPaths"],
846
+ ["outOfScopeSourcePaths", "External delivery outOfScopeSourcePaths"],
847
+ ]) {
848
+ const paths = stringArray(value[field], label);
849
+ for (const filePath of paths) safeSourcePath(filePath, label);
850
+ if (canonical([...paths].sort()) !== canonical(paths)) {
851
+ throw new Error(`${label} must be sorted.`);
852
+ }
853
+ }
854
+ if (value.outOfScopeSourcePaths.length !== 0) {
855
+ throw new Error("An imported external delivery cannot contain out-of-scope source paths.");
856
+ }
857
+ stringArray(value.proven, "External delivery proven statements", { allowEmpty: false });
858
+ stringArray(value.notProven, "External delivery notProven statements", { allowEmpty: false });
859
+ if (!validWorkspaceSnapshotShape(value.targetSnapshot)
860
+ || !quickSnapshotFingerprintValid(value.targetSnapshot)
861
+ || value.targetSnapshot.sourceFingerprint !== value.workspaceSnapshotFingerprint) {
862
+ throw new Error("External delivery targetSnapshot identity is invalid.");
863
+ }
864
+ if (value.targetWritePerformed !== false || value.authorizationGranted !== false) {
865
+ throw new Error("External delivery cannot grant authority or write the target workspace.");
866
+ }
585
867
  }
586
868
  return structuredClone(value);
587
869
  }
@@ -596,9 +878,11 @@ function projectEvents(definition, records) {
596
878
  const gatePasses = new Map();
597
879
  const verifications = new Map();
598
880
  const dataCandidates = [];
881
+ const sourceDeliveries = [];
599
882
  const seenAssignments = new Set();
600
883
  const seenReworks = new Set();
601
884
  const seenDecisions = new Set();
885
+ const seenDeliveries = new Set();
602
886
 
603
887
  for (const { event, ref } of records) {
604
888
  validateEventShape(event, definition);
@@ -692,6 +976,25 @@ function projectEvents(definition, records) {
692
976
  throw new Error(`Duplicate data candidate ${event.candidateId}.`);
693
977
  }
694
978
  dataCandidates.push({ ...event, ref });
979
+ } else if (event.type === "source_delivery_imported") {
980
+ if (seenDeliveries.has(event.deliveryId)) {
981
+ throw new Error(`Duplicate external source delivery ${event.deliveryId}.`);
982
+ }
983
+ const current = assignments.get(event.workItemId) ?? null;
984
+ if (current?.assignmentId !== event.assignmentId) {
985
+ throw new Error(
986
+ `External source delivery ${event.deliveryId} does not bind the current assignment.`,
987
+ );
988
+ }
989
+ const currentCheckpoint = checkpoints.get(event.workItemId) ?? null;
990
+ if (currentCheckpoint?.assignmentId === current.assignmentId
991
+ && ["completed", "failed"].includes(currentCheckpoint.state)) {
992
+ throw new Error(
993
+ `External source delivery ${event.deliveryId} cannot attach to terminal WorkItem truth.`,
994
+ );
995
+ }
996
+ seenDeliveries.add(event.deliveryId);
997
+ sourceDeliveries.push({ ...event, ref });
695
998
  }
696
999
  }
697
1000
  return {
@@ -704,6 +1007,7 @@ function projectEvents(definition, records) {
704
1007
  gatePasses,
705
1008
  verifications,
706
1009
  dataCandidates,
1010
+ sourceDeliveries,
707
1011
  };
708
1012
  }
709
1013
 
@@ -1031,6 +1335,9 @@ function projectStatus(definition, records, workspaceRoot = null) {
1031
1335
  const checkpoint = state.checkpoints.get(item.id) ?? null;
1032
1336
  const handoff = state.handoffs.get(item.id) ?? null;
1033
1337
  const rework = state.reworks.get(item.id) ?? null;
1338
+ const sourceDeliveries = state.sourceDeliveries.filter(row => row.workItemId === item.id);
1339
+ const latestSourceDeliveryFact = sourceDeliveries.at(-1) ?? null;
1340
+ const latestSourceDelivery = sourceDeliveryProjection(sourceDeliveries.at(-1) ?? null);
1034
1341
  const currentRework = rework?.assignmentId === assignment?.assignmentId ? rework : null;
1035
1342
  const latestFact = latestEventFact(latestEventFact(checkpoint, handoff), currentRework);
1036
1343
  const activeHandoff = handoff
@@ -1038,7 +1345,16 @@ function projectStatus(definition, records, workspaceRoot = null) {
1038
1345
  && handoff.fromAgentId === assignment?.agentId
1039
1346
  ? handoff
1040
1347
  : null;
1041
- const pendingHandoff = activeHandoff && latestFact?.eventId === activeHandoff.eventId
1348
+ const latestHandoffFact = latestEventFact(activeHandoff, latestSourceDeliveryFact);
1349
+ const handoffSupersededByDelivery = activeHandoff
1350
+ && latestFact?.eventId === activeHandoff.eventId
1351
+ && latestHandoffFact?.eventId !== activeHandoff.eventId;
1352
+ const projectedFact = handoffSupersededByDelivery
1353
+ ? latestEventFact(checkpoint, currentRework)
1354
+ : latestFact;
1355
+ const pendingHandoff = activeHandoff
1356
+ && latestFact?.eventId === activeHandoff.eventId
1357
+ && latestHandoffFact?.eventId === activeHandoff.eventId
1042
1358
  ? pendingHandoffProjection(activeHandoff)
1043
1359
  : null;
1044
1360
  const executionTruth = executionTruthFor(workspaceRoot, assignment);
@@ -1046,7 +1362,7 @@ function projectStatus(definition, records, workspaceRoot = null) {
1046
1362
  let status = "planned";
1047
1363
  if (assignment) status = unresolvedDependencies.length > 0 ? "waiting_dependency" : "active";
1048
1364
  if (pendingHandoff) status = "planned";
1049
- if (latestFact?.type === "checkpoint_recorded") status = checkpoint.state;
1365
+ if (projectedFact?.type === "checkpoint_recorded") status = checkpoint.state;
1050
1366
  if (executionTruth && (executionTruth.status !== "bound"
1051
1367
  || (executionTruth.lifecycle === "closed"
1052
1368
  && executionTruth.closeoutDecision !== "accepted"))) {
@@ -1075,6 +1391,7 @@ function projectStatus(definition, records, workspaceRoot = null) {
1075
1391
  ...(checkpoint ? [checkpoint.ref] : []),
1076
1392
  ...(handoff ? [handoff.ref] : []),
1077
1393
  ...(rework ? [rework.ref] : []),
1394
+ ...sourceDeliveries.map(row => row.ref),
1078
1395
  ];
1079
1396
  const projectedRework = reworkProjection(
1080
1397
  currentRework,
@@ -1082,9 +1399,9 @@ function projectStatus(definition, records, workspaceRoot = null) {
1082
1399
  ? assignmentEvents.findIndex(row => row.assignmentId === currentRework.assignmentId) + 1
1083
1400
  : null,
1084
1401
  );
1085
- const latestSummary = latestFact?.type === "rework_returned"
1086
- ? latestFact.reason
1087
- : latestFact?.summary ?? null;
1402
+ const latestSummary = projectedFact?.type === "rework_returned"
1403
+ ? projectedFact.reason
1404
+ : projectedFact?.summary ?? null;
1088
1405
  return {
1089
1406
  workItemId: item.id,
1090
1407
  title: item.title,
@@ -1098,17 +1415,18 @@ function projectStatus(definition, records, workspaceRoot = null) {
1098
1415
  executionTruth,
1099
1416
  dependencies: [...item.dependencies],
1100
1417
  unresolvedDependencies,
1101
- blockers: latestFact?.type === "checkpoint_recorded"
1418
+ blockers: projectedFact?.type === "checkpoint_recorded"
1102
1419
  ? checkpoint.blockerRefs
1103
1420
  : unresolvedDependencies.map(id => `work-item:${id}`),
1104
- decisionRefs: latestFact?.type === "checkpoint_recorded" ? checkpoint.decisionRefs : [],
1421
+ decisionRefs: projectedFact?.type === "checkpoint_recorded" ? checkpoint.decisionRefs : [],
1105
1422
  progress: measured,
1106
1423
  summary: latestSummary,
1107
- nextAction: latestFact?.nextAction ?? null,
1424
+ nextAction: projectedFact?.nextAction ?? null,
1108
1425
  pendingHandoff,
1109
1426
  rework: projectedRework,
1427
+ latestSourceDelivery,
1110
1428
  lastCheckpointAt: checkpoint?.occurredAt ?? null,
1111
- evidenceRefs: latestFact?.evidenceRefs ?? [],
1429
+ evidenceRefs: projectedFact?.evidenceRefs ?? [],
1112
1430
  assignmentHistory: history,
1113
1431
  truthRefs,
1114
1432
  };
@@ -1311,6 +1629,7 @@ function projectStatus(definition, records, workspaceRoot = null) {
1311
1629
  truthRefs: [row.ref],
1312
1630
  };
1313
1631
  });
1632
+ const sourceDeliveries = state.sourceDeliveries.map(sourceDeliveryProjection);
1314
1633
  let overall = "planned";
1315
1634
  if (workItems.every(row => row.status === "completed")
1316
1635
  && integrationGates.every(row => row.status === "passed")
@@ -1318,7 +1637,7 @@ function projectStatus(definition, records, workspaceRoot = null) {
1318
1637
  else if (openDecisions.length > 0 || workItems.some(row => row.status === "failed")) overall = "active_with_blockers";
1319
1638
  else if (workItems.some(row => row.status !== "planned" || row.pendingHandoff !== null)) overall = "active";
1320
1639
  const status = {
1321
- schemaVersion: STATUS_SCHEMA,
1640
+ schemaVersion: STATUS_SCHEMA_V2,
1322
1641
  status: "team_project_status",
1323
1642
  projectId: definition.projectId,
1324
1643
  objective: definition.objective,
@@ -1336,6 +1655,7 @@ function projectStatus(definition, records, workspaceRoot = null) {
1336
1655
  openDeferredVerificationIds,
1337
1656
  integrationGates,
1338
1657
  dataCandidates,
1658
+ sourceDeliveries,
1339
1659
  projectTruthHash: sha256(canonical({ definition, events: records.map(row => row.event) })),
1340
1660
  authorizationGranted: false,
1341
1661
  };
@@ -1513,6 +1833,18 @@ function validateEventAgainstState(event, definition, records, workspaceRoot) {
1513
1833
  if (state.dataCandidates.some(row => row.candidateId === normalized.candidateId)) {
1514
1834
  throw new Error(`Data candidate ${normalized.candidateId} already exists.`);
1515
1835
  }
1836
+ } else if (normalized.type === "source_delivery_imported") {
1837
+ if (state.sourceDeliveries.some(row => row.deliveryId === normalized.deliveryId)) {
1838
+ throw new Error(`External source delivery ${normalized.deliveryId} already exists.`);
1839
+ }
1840
+ const current = state.assignments.get(normalized.workItemId) ?? null;
1841
+ if (current?.assignmentId !== normalized.assignmentId) {
1842
+ throw new Error("External source delivery must bind the current assignment.");
1843
+ }
1844
+ const projected = status.workItems.find(row => row.workItemId === normalized.workItemId);
1845
+ if (["completed", "failed"].includes(projected?.status)) {
1846
+ throw new Error("External source delivery requires a non-terminal WorkItem.");
1847
+ }
1516
1848
  }
1517
1849
  return normalized;
1518
1850
  }
@@ -2155,7 +2487,28 @@ export function checkpointTeamProjectV1({
2155
2487
  };
2156
2488
  }
2157
2489
 
2490
+ function legacyWorkItemProjection(workItem) {
2491
+ if (workItem === null) return null;
2492
+ const { latestSourceDelivery: _latestSourceDelivery, ...legacy } = workItem;
2493
+ return legacy;
2494
+ }
2495
+
2158
2496
  export function readTeamProjectStatusV1({ workspaceRoot }) {
2497
+ const loaded = loadProject(workspaceRoot);
2498
+ const { sourceDeliveries: _sourceDeliveries, ...legacy } = projectStatus(
2499
+ loaded.definition,
2500
+ loaded.events,
2501
+ loaded.root,
2502
+ );
2503
+ return {
2504
+ ...legacy,
2505
+ schemaVersion: STATUS_SCHEMA_V1,
2506
+ workItems: legacy.workItems.map(legacyWorkItemProjection),
2507
+ exitCode: 0,
2508
+ };
2509
+ }
2510
+
2511
+ export function readTeamProjectStatusV2({ workspaceRoot }) {
2159
2512
  const loaded = loadProject(workspaceRoot);
2160
2513
  return {
2161
2514
  ...projectStatus(loaded.definition, loaded.events, loaded.root),
@@ -2163,7 +2516,7 @@ export function readTeamProjectStatusV1({ workspaceRoot }) {
2163
2516
  };
2164
2517
  }
2165
2518
 
2166
- export function buildTeamProjectTakeoverV1({ workspaceRoot, agentId }) {
2519
+ export function buildTeamProjectTakeoverV2({ workspaceRoot, agentId }) {
2167
2520
  safeIdentifier(agentId, "agentId");
2168
2521
  const loaded = loadProject(workspaceRoot);
2169
2522
  const status = projectStatus(loaded.definition, loaded.events, loaded.root);
@@ -2180,6 +2533,29 @@ export function buildTeamProjectTakeoverV1({ workspaceRoot, agentId }) {
2180
2533
  row.pendingHandoff?.toAgentId === agentId
2181
2534
  ))?.pendingHandoff ?? null;
2182
2535
  const pendingHandoff = pendingForCurrent ?? pendingForAgent;
2536
+ const staleHandoffs = staleHandoffsForTakeover({
2537
+ loaded,
2538
+ status,
2539
+ agentId,
2540
+ responsibilities,
2541
+ });
2542
+ const latestStaleHandoff = [...staleHandoffs]
2543
+ .sort(compareEventFacts)
2544
+ .at(-1) ?? null;
2545
+ const focusResponsibility = current
2546
+ ?? (pendingHandoff
2547
+ ? status.workItems.find(row => row.workItemId === pendingHandoff.workItemId) ?? null
2548
+ : null)
2549
+ ?? (latestStaleHandoff
2550
+ ? status.workItems.find(row => row.workItemId === latestStaleHandoff.workItemId) ?? null
2551
+ : null);
2552
+ const focusedDeliveries = focusResponsibility
2553
+ ? status.sourceDeliveries.filter(row => (
2554
+ row.workItemId === focusResponsibility.workItemId
2555
+ ))
2556
+ : [];
2557
+ const currentSourceDelivery = focusedDeliveries.at(-1) ?? null;
2558
+ const deliveryDelta = sourceDeliveryDelta(focusedDeliveries);
2183
2559
  const checkpoints = loaded.events
2184
2560
  .filter(row => row.event.type === "checkpoint_recorded"
2185
2561
  && responsibilities.some(item => item.workItemId === row.event.workItemId))
@@ -2189,6 +2565,9 @@ export function buildTeamProjectTakeoverV1({ workspaceRoot, agentId }) {
2189
2565
  ...(lastCheckpoint?.evidenceRefs ?? []),
2190
2566
  ...(current?.evidenceRefs ?? []),
2191
2567
  ...(pendingHandoff?.evidenceRefs ?? []),
2568
+ ...(currentSourceDelivery ? [currentSourceDelivery.truthRef] : []),
2569
+ ...(deliveryDelta?.previousDeliveryId ? [focusedDeliveries.at(-2).truthRef] : []),
2570
+ ...staleHandoffs.map(row => row.truthRef),
2192
2571
  ])].sort();
2193
2572
  const unresolvedDecisionsById = new Map();
2194
2573
  for (const decision of status.openDecisions) {
@@ -2230,7 +2609,7 @@ export function buildTeamProjectTakeoverV1({ workspaceRoot, agentId }) {
2230
2609
  : current ? null : status.readyQueue[0] ? `Take ${status.readyQueue[0]}.` : null;
2231
2610
  }
2232
2611
  return {
2233
- schemaVersion: "OwlCodaRunKitTeamProjectTakeoverV1",
2612
+ schemaVersion: "OwlCodaRunKitTeamProjectTakeoverV2",
2234
2613
  status: "team_project_takeover",
2235
2614
  projectId: status.projectId,
2236
2615
  objective: status.objective,
@@ -2243,6 +2622,9 @@ export function buildTeamProjectTakeoverV1({ workspaceRoot, agentId }) {
2243
2622
  currentResponsibility: current,
2244
2623
  allResponsibilities: responsibilities,
2245
2624
  pendingHandoff,
2625
+ staleHandoffs,
2626
+ currentSourceDelivery,
2627
+ sourceDeliveryDelta: deliveryDelta,
2246
2628
  lastAcceptedCheckpoint: lastCheckpoint
2247
2629
  ? {
2248
2630
  workItemId: lastCheckpoint.workItemId,
@@ -2265,6 +2647,27 @@ export function buildTeamProjectTakeoverV1({ workspaceRoot, agentId }) {
2265
2647
  };
2266
2648
  }
2267
2649
 
2650
+ export function buildTeamProjectTakeoverV1(input) {
2651
+ const {
2652
+ staleHandoffs: _staleHandoffs,
2653
+ currentSourceDelivery: _currentSourceDelivery,
2654
+ sourceDeliveryDelta: _sourceDeliveryDelta,
2655
+ ...legacy
2656
+ } = buildTeamProjectTakeoverV2(input);
2657
+ const v2OnlyEvidenceRefs = new Set([
2658
+ ..._staleHandoffs.map(row => row.truthRef),
2659
+ ...readTeamProjectStatusV2({ workspaceRoot: input.workspaceRoot })
2660
+ .sourceDeliveries.map(row => row.truthRef),
2661
+ ]);
2662
+ return {
2663
+ ...legacy,
2664
+ schemaVersion: "OwlCodaRunKitTeamProjectTakeoverV1",
2665
+ currentResponsibility: legacyWorkItemProjection(legacy.currentResponsibility),
2666
+ allResponsibilities: legacy.allResponsibilities.map(legacyWorkItemProjection),
2667
+ evidenceRefs: legacy.evidenceRefs.filter(ref => !v2OnlyEvidenceRefs.has(ref)),
2668
+ };
2669
+ }
2670
+
2268
2671
  export function formatTeamProjectStatusHumanV1(status) {
2269
2672
  const nextAction = status.nextAction === null ? "none" : singleLine(status.nextAction);
2270
2673
  return [
@@ -2285,6 +2688,65 @@ export function formatTeamProjectStatusHumanV1(status) {
2285
2688
  ].join("\n");
2286
2689
  }
2287
2690
 
2691
+ export function formatTeamProjectStatusHumanV2(status) {
2692
+ const nextAction = status.nextAction === null ? "none" : singleLine(status.nextAction);
2693
+ const latestSourceDelivery = status.sourceDeliveries.at(-1) ?? null;
2694
+ return [
2695
+ status.headline,
2696
+ `Project: ${status.projectId} — ${singleLine(status.objective)}`,
2697
+ `State: ${status.overall}`,
2698
+ `Work: ${status.counts.completed}/${status.counts.total} completed; ${status.counts.active} active; ${status.counts.waiting_dependency + status.counts.waiting_decision} waiting`,
2699
+ `Agents: ${status.agents.map(agent => agent.agentId).join(", ") || "none assigned"}`,
2700
+ `Open decisions: ${status.openDecisions.map(row => row.decisionId).join(", ") || "none"}`,
2701
+ `Deferred verification: ${status.openDeferredVerificationIds.length} open${
2702
+ status.openDeferredVerificationIds.length > 0
2703
+ ? ` (${status.openDeferredVerificationIds.join(", ")})`
2704
+ : ""
2705
+ }`,
2706
+ `Ready queue: ${status.readyQueue.join(", ") || "none"}`,
2707
+ `Source deliveries: ${status.sourceDeliveries.length}${latestSourceDelivery
2708
+ ? `; latest ${latestSourceDelivery.deliveryId} (${latestSourceDelivery.overlayStatus}, ${latestSourceDelivery.deliveryCandidateFingerprint})`
2709
+ : ""}`,
2710
+ `Next: ${nextAction}${status.nextActorId ? ` (owner ${status.nextActorId})` : ""}`,
2711
+ "",
2712
+ ].join("\n");
2713
+ }
2714
+
2715
+ export function formatTeamProjectTakeoverHumanV2(takeover) {
2716
+ const responsibility = takeover.currentResponsibility;
2717
+ const agentNextAction = takeover.nextAction === null
2718
+ ? "none"
2719
+ : singleLine(takeover.nextAction);
2720
+ const projectNextAction = takeover.projectNextAction === null
2721
+ ? "none"
2722
+ : singleLine(takeover.projectNextAction);
2723
+ return [
2724
+ takeover.projectHeadline,
2725
+ `Project: ${takeover.projectId} — ${singleLine(takeover.objective)}`,
2726
+ `Agent: ${takeover.agentId}`,
2727
+ `Responsibility: ${responsibility?.workItemId ?? "none"}`,
2728
+ `Open decisions: ${takeover.unresolvedDecisions.map(row => row.decisionId).join(", ") || "none"}`,
2729
+ `Deferred verification: ${takeover.deferredVerifications
2730
+ .map(row => row.verificationId).join(", ") || "none"}`,
2731
+ `Handoffs: pending ${takeover.pendingHandoff
2732
+ ? `${takeover.pendingHandoff.fromAgentId} -> ${takeover.pendingHandoff.toAgentId} for ${takeover.pendingHandoff.workItemId}`
2733
+ : "none"}; stale ${takeover.staleHandoffs.length > 0
2734
+ ? takeover.staleHandoffs.map(row => `${row.eventId} (${row.reason})`).join(", ")
2735
+ : "none"}`,
2736
+ `Candidate: ${takeover.currentSourceDelivery
2737
+ ? `${takeover.currentSourceDelivery.deliveryId} (${takeover.currentSourceDelivery.overlayStatus}, ${takeover.currentSourceDelivery.deliveryCandidateFingerprint})`
2738
+ : "none"}; delta ${takeover.sourceDeliveryDelta === null
2739
+ ? "none"
2740
+ : takeover.sourceDeliveryDelta.previousDeliveryId === null
2741
+ ? `first delivery; added ${takeover.sourceDeliveryDelta.addedPaths.length}`
2742
+ : `vs ${takeover.sourceDeliveryDelta.previousDeliveryId}; added ${takeover.sourceDeliveryDelta.addedPaths.length}, changed ${takeover.sourceDeliveryDelta.changedPaths.length}, removed ${takeover.sourceDeliveryDelta.removedPaths.length}, unchanged ${takeover.sourceDeliveryDelta.unchangedPaths.length}`}`,
2743
+ `Agent next: ${agentNextAction}`,
2744
+ `Project next: ${projectNextAction}${takeover.projectNextActorId ? ` (owner ${takeover.projectNextActorId})` : ""}`,
2745
+ `State: ${takeover.overall}`,
2746
+ "",
2747
+ ].join("\n");
2748
+ }
2749
+
2288
2750
  export function formatTeamProjectTakeoverHumanV1(takeover) {
2289
2751
  const responsibility = takeover.currentResponsibility;
2290
2752
  const agentNextAction = takeover.nextAction === null