@tiinex/core 0.18.0 → 0.20.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.
Files changed (38) hide show
  1. package/package.json +8 -6
  2. package/src/schemas/creation.contracts.js +1 -1
  3. package/src/schemas/party/role/tiinex.party.role.v1.schema.js +5 -0
  4. package/src/schemas/party/role/tiinex.party.role.v1.schema.json +2 -2
  5. package/src/schemas/party/role/tiinex.party.role.v1.schema.md +313 -0
  6. package/src/schemas/party/role/tiinex.party.role.v1.schema.runtime.json +18 -6
  7. package/src/schemas/party/role/tiinex.party.role.v1.validate.js +36 -0
  8. package/src/schemas/schema.reference.js +29 -0
  9. package/src/tooling/portable/adapters/cli/cli.command-input.js +8 -1
  10. package/src/tooling/portable/adapters/cli/cli.common-author.js +117 -6
  11. package/src/tooling/portable/adapters/cli/cli.common-output.js +26 -0
  12. package/src/tooling/portable/adapters/cli/cli.handoff-manufacture.js +33 -17
  13. package/src/tooling/portable/adapters/cli/cli.handoff-sibling-allocation.js +139 -2
  14. package/src/tooling/portable/adapters/cli/cli.help.js +1 -1
  15. package/src/tooling/portable/adapters/node/handoff.manufacture.js +30 -5
  16. package/src/tooling/portable/adapters/node/handoff.manufacture.packageParent.js +123 -0
  17. package/src/tooling/portable/audit/audit.capability.js +10 -4
  18. package/src/tooling/portable/grounding/grounding.capsule.js +33 -2
  19. package/src/tooling/portable/grounding/grounding.delegationArtifactAuthority.js +220 -0
  20. package/src/tooling/portable/grounding/grounding.delegationReadiness.js +241 -0
  21. package/src/tooling/portable/grounding/grounding.holderAssignmentModes.js +125 -0
  22. package/src/tooling/portable/grounding/grounding.holderBindingAuthorization.js +80 -0
  23. package/src/tooling/portable/grounding/grounding.implementationSourceAuthority.js +109 -0
  24. package/src/tooling/portable/grounding/grounding.participantAuthority.js +1 -1
  25. package/src/tooling/portable/grounding/grounding.readiness.authority.js +15 -0
  26. package/src/tooling/portable/grounding/grounding.readiness.js +86 -13
  27. package/src/tooling/portable/grounding/grounding.readiness.support.js +2 -2
  28. package/src/tooling/portable/handoff/carrierProjection.routeQualification.js +20 -4
  29. package/src/tooling/portable/handoff/coldStartQualification.grounding.js +116 -5
  30. package/src/tooling/portable/handoff/coldStartQualification.materials.js +92 -10
  31. package/src/tooling/portable/handoff/delegationReturnReservation.js +4 -2
  32. package/src/tooling/portable/handoff/manufacture.js +1 -0
  33. package/src/tooling/portable/handoff/recipientV2.endpointRolePointers.js +1 -1
  34. package/src/tooling/portable/handoff/recipientV2.inspect.projection.js +7 -1
  35. package/src/tooling/portable/handoff/recipientV2.packageV1.build.js +2 -2
  36. package/src/tooling/portable/handoff/recipientV2.packageV1.inspect.js +1 -1
  37. package/src/tooling/portable/handoff/recipientV2.topology.js +5 -3
  38. package/src/tooling/portable/handoff/recipientV2.topology.materials.js +0 -0
@@ -11,6 +11,8 @@ import { runPortableOperation } from '../../operation.catalog.js';
11
11
  import { markPortableBootstrapCanonicalSource } from '../../providers/schema.bootstrap.provenance.js';
12
12
  import { allocateContinuationPath, allocateDirectoryArtifactPath } from '../../../../transitions/record.transitions.js';
13
13
  import { classifyParentRecoveryReference } from '../../../../lineage/parentRecoveryReference.js';
14
+ import { creationSchemaReferenceValidationContext } from '../../draft/draft.validation-context.js';
15
+ import { isQualifiedIdentifierOnlyHistoricalSchemaReferenceAuthority, qualifiedIdentifierOnlyHistoricalSchemaReferenceAuthority } from '../../../../schemas/schema.reference.js';
14
16
 
15
17
  const STATE_RELATIVE_PATH = '.tiinex/continuation.json';
16
18
 
@@ -66,16 +68,21 @@ export async function runCommonAuthorCli(parsed = {}, runtime = {}) {
66
68
  await writeFile(artifactPath, markdown, { encoding: 'utf8', flag: flags.overwrite ? 'w' : 'wx' });
67
69
  wrote = true;
68
70
  const auditMaterial = await loadNodePortableInput(
69
- [artifactPath, ...(parentPath ? [parentPath] : [])],
71
+ [artifactPath],
70
72
  { maxFiles: flags['max-files'], maxTextBytes: flags['max-text-bytes'] }
71
73
  );
72
- const audit = await runPortableOperation('audit', auditMaterial, {});
74
+ const candidateAudit = await runPortableOperation('audit', auditMaterial, {});
75
+ const parentAudit = parentPath
76
+ ? await runHistoricalParentAudit(parentPath, parentRecord, contract, flags)
77
+ : null;
78
+ const audit = mergeAuthorAuditResults(candidateAudit, parentAudit);
73
79
  const stageMaterial = await loadNodePortableInput(
74
80
  normalizeRuntimePaths(runtime.defaultSchemaMaterialPaths),
75
81
  { maxFiles: flags['max-files'], maxTextBytes: flags['max-text-bytes'] }
76
82
  );
77
83
  const stage = await runPortableOperation('stage-draft', {
78
84
  ...stageMaterial,
85
+ schemaReferenceAuthorities: creationSchemaReferenceValidationContext(contract, parentRecord),
79
86
  draft: { path: artifactRelativePath, markdown, schemaId, sourceMode: 'local-common-author', source: null }
80
87
  }, {});
81
88
  const blocking = Number(audit?.findingSummary?.counts?.error || 0) + Number(stage?.findingSummary?.counts?.error || 0);
@@ -87,7 +94,7 @@ export async function runCommonAuthorCli(parsed = {}, runtime = {}) {
87
94
  schema: 'tiinex.portable.common-author.result.v1',
88
95
  operation: 'author',
89
96
  status: 'blocked',
90
- artifact: Object.freeze({ path: artifactRelativePath, schemaId, written: false }),
97
+ artifact: Object.freeze({ path: artifactRelativePath, schemaId, written: false, parentPath: parentReference, parentSource: parentSource || '', parentSchemaReferenceAuthority: projectHistoricalParentReferenceAuthority(parentRecord) }),
91
98
  audit,
92
99
  stage,
93
100
  findingSummary: mergeFindingSummaries(audit?.findingSummary, stage?.findingSummary),
@@ -105,7 +112,7 @@ export async function runCommonAuthorCli(parsed = {}, runtime = {}) {
105
112
  schema: 'tiinex.portable.common-author.result.v1',
106
113
  operation: 'author',
107
114
  status: 'qualified',
108
- artifact: Object.freeze({ path: artifactRelativePath, absolutePath: artifactPath, schemaId, parentPath: parentReference, parentSource: parentSource || '', selfIntegrity: selfIntegrity.state, written: true }),
115
+ artifact: Object.freeze({ path: artifactRelativePath, absolutePath: artifactPath, schemaId, parentPath: parentReference, parentSource: parentSource || '', parentSchemaReferenceAuthority: projectHistoricalParentReferenceAuthority(parentRecord), selfIntegrity: selfIntegrity.state, written: true }),
109
116
  qualification: Object.freeze({ audit: audit.status, stage: stage.status, exportReady: Boolean(stage?.stagedArtifact?.qualification?.exportReady) }),
110
117
  findingSummary: mergeFindingSummaries(audit?.findingSummary, stage?.findingSummary),
111
118
  nextAction: schemaId === 'tiinex.handoff.v1'
@@ -120,6 +127,88 @@ export async function runCommonAuthorCli(parsed = {}, runtime = {}) {
120
127
  }
121
128
  }
122
129
 
130
+ async function runHistoricalParentAudit(parentPath, parentRecord = {}, contract = {}, flags = {}) {
131
+ const material = await loadNodePortableInput(
132
+ [parentPath],
133
+ { maxFiles: flags['max-files'], maxTextBytes: flags['max-text-bytes'] }
134
+ );
135
+ const exactCurrentMatch = sameExactSchemaReferenceAuthority(
136
+ parentRecord.schemaReferenceAuthority || null,
137
+ contract?.schemaReferences?.current || null,
138
+ parentRecord.schemaId || parentRecord.currentSchemaId || ''
139
+ );
140
+ return runPortableOperation('audit', material, {
141
+ schemaReferenceContext: 'historical',
142
+ schemaReferenceAuthorities: Object.freeze({ current: parentRecord.schemaReferenceAuthority || null }),
143
+ ...(exactCurrentMatch ? {} : {
144
+ schemaValidationAuthority: historicalParentSchemaValidationAuthority(parentRecord)
145
+ })
146
+ });
147
+ }
148
+
149
+ export function sameExactSchemaReferenceAuthority(parentAuthority = null, currentAuthority = null, schemaId = '') {
150
+ if (!parentAuthority || !currentAuthority) return false;
151
+ const expectedSchemaId = String(schemaId || '').trim();
152
+ const parentSchemaId = String(parentAuthority.schemaId || '').trim();
153
+ const currentSchemaId = String(currentAuthority.schemaId || '').trim();
154
+ if (!expectedSchemaId || parentSchemaId !== expectedSchemaId || currentSchemaId !== expectedSchemaId) return false;
155
+ if (String(parentAuthority.resolutionState || parentAuthority.state || '') !== 'qualified') return false;
156
+ if (String(currentAuthority.resolutionState || currentAuthority.state || '') !== 'qualified') return false;
157
+ const parentTargets = exactSchemaReferenceTargets(parentAuthority);
158
+ const currentTargets = new Set(exactSchemaReferenceTargets(currentAuthority));
159
+ return parentTargets.length > 0 && parentTargets.some((target) => currentTargets.has(target));
160
+ }
161
+
162
+ function exactSchemaReferenceTargets(authority = {}) {
163
+ return [...new Set([
164
+ ...(Array.isArray(authority.exactTargets) ? authority.exactTargets : []),
165
+ authority.preferredTarget || authority.target || ''
166
+ ].map((item) => String(item || '').trim()).filter(Boolean))];
167
+ }
168
+
169
+ function historicalParentSchemaValidationAuthority(parentRecord = {}) {
170
+ const schemaId = String(parentRecord.schemaId || parentRecord.currentSchemaId || '').trim();
171
+ const target = String(parentRecord.schemaReferenceAuthority?.preferredTarget || parentRecord.schemaReferenceAuthority?.target || '').trim();
172
+ const identifierOnly = isQualifiedIdentifierOnlyHistoricalSchemaReferenceAuthority(parentRecord.schemaReferenceAuthority || {});
173
+ return Object.freeze({
174
+ schema: 'tiinex.portable.historical-parent-schema-validation-authority.v1',
175
+ state: 'unavailable',
176
+ reason: identifierOnly ? 'historical-parent-schema-revision-unresolved-identifier-only' : 'historical-parent-schema-revision-not-qualified-as-current-candidate-authority',
177
+ schemaId,
178
+ currentReference: Object.freeze({
179
+ state: identifierOnly ? 'historical-identifier-only-preserved' : target ? 'historical-exact-reference-preserved' : 'unavailable',
180
+ basis: identifierOnly ? 'historical-parent-declared-schema-identifier-only' : 'historical-parent-declared-schema-reference',
181
+ raw: '',
182
+ target,
183
+ exactRevisionState: identifierOnly ? 'unresolved' : target ? 'declared-exact-reference' : 'unavailable'
184
+ }),
185
+ lineage: Object.freeze([]),
186
+ findings: Object.freeze([
187
+ identifierOnly
188
+ ? `Historical Parent ${parentRecord.path || parentRecord.id || '(parent)'} preserves only the declared ${schemaId || 'Current Schema'} identifier; exact historical schema revision remains unresolved and current candidate schema validation is withheld for the Parent.`
189
+ : `Historical Parent ${parentRecord.path || parentRecord.id || '(parent)'} is preserved under its exact declared ${schemaId || 'Current Schema'} reference${target ? ` ${target}` : ''}; current candidate schema validation is withheld because exact same-revision current authority is not qualified.`
190
+ ]),
191
+ compiledContract: null,
192
+ boundary: 'Historical Parent validation still runs Root readability, exact declared schema-identifier/reference-shape checks, recovery and integrity. Identifier-only authority never becomes exact historical schema-revision authority; current schema-specific machine/companion validation is applied only when the Parent and current creation contract share exact qualified schema-revision authority.'
193
+ });
194
+ }
195
+
196
+ function mergeAuthorAuditResults(candidateAudit = {}, parentAudit = null) {
197
+ if (!parentAudit) return candidateAudit;
198
+ const audits = Object.freeze([...(candidateAudit.audits || []), ...(parentAudit.audits || [])]);
199
+ const findings = Object.freeze([...(candidateAudit.findings || []), ...(parentAudit.findings || [])]);
200
+ return Object.freeze({
201
+ ...candidateAudit,
202
+ audits,
203
+ findings,
204
+ findingSummary: mergeFindingSummaries(candidateAudit.findingSummary, parentAudit.findingSummary),
205
+ boundary: Object.freeze({
206
+ ...(candidateAudit.boundary || {}),
207
+ historicalParentAudit: 'separate exact-byte historical audit; current schema-specific validation only when exact same-revision authority is qualified'
208
+ })
209
+ });
210
+ }
211
+
123
212
  async function parentRecordFromArtifact(parentPath, parentRelativePath, context = {}) {
124
213
  const markdown = await readFile(parentPath, 'utf8');
125
214
  const parsed = parseArtifactMarkdown(markdown);
@@ -131,8 +220,11 @@ async function parentRecordFromArtifact(parentPath, parentRelativePath, context
131
220
  if (self.state !== 'verified') throw new Error(`portable.cli.author.parent.integrity.${self.reason || self.state}`);
132
221
  const schemaReferenceAuthority = schemaTarget
133
222
  ? exactDeclaredSchemaReferenceAuthority(schemaId, schemaTarget)
134
- : await recoverQualifiedRuntimeSchemaReferenceAuthority(schemaId, context.runtime || {});
135
- if (!schemaReferenceAuthority) throw new Error(`portable.cli.author.parent.schema-authority.required: Parent schema ${schemaId} lacks an exact qualified schema-reference authority. Supply Parent bytes with an exact Current Schema target or qualified runtime canonical schema material; Tooling will not invent the schema target.`);
223
+ : qualifiedIdentifierOnlyHistoricalSchemaReferenceAuthority(schemaId, {
224
+ basis: 'exact-historical-parent-current-schema-identifier',
225
+ parentPath: String(parentRelativePath || ''),
226
+ parentSha256: sha256Hex(utf8Bytes(markdown))
227
+ });
136
228
  return Object.freeze({
137
229
  id: parentRelativePath,
138
230
  path: parentRelativePath,
@@ -146,6 +238,25 @@ async function parentRecordFromArtifact(parentPath, parentRelativePath, context
146
238
  });
147
239
  }
148
240
 
241
+ function projectHistoricalParentReferenceAuthority(parentRecord = {}) {
242
+ const authority = parentRecord?.schemaReferenceAuthority || null;
243
+ if (!authority || !String(parentRecord?.schemaId || parentRecord?.currentSchemaId || '').trim()) return null;
244
+ const schemaId = String(parentRecord.schemaId || parentRecord.currentSchemaId || '').trim();
245
+ const target = String(authority.preferredTarget || authority.target || '').trim();
246
+ const identifierOnly = isQualifiedIdentifierOnlyHistoricalSchemaReferenceAuthority(authority);
247
+ return Object.freeze({
248
+ state: String(authority.resolutionState || authority.state || 'unavailable'),
249
+ kind: identifierOnly ? 'identifier-only' : target ? 'exact-target' : 'unresolved',
250
+ schemaId,
251
+ target,
252
+ exactRevisionState: identifierOnly ? 'unresolved' : target ? 'declared-exact-reference' : 'unresolved',
253
+ provenance: Object.freeze({ ...(authority.resolutionEvidence || authority.evidence || {}) }),
254
+ boundary: identifierOnly
255
+ ? 'Exact historical Parent artifact identity/integrity and declared schema identifier are qualified; no exact historical schema representation target is claimed or inferred.'
256
+ : 'Historical Parent schema-reference authority is projected only at the exact strength carried by the supplied Parent bytes.'
257
+ });
258
+ }
259
+
149
260
  function projectAuthorActionableFindings(audit = {}, stage = {}) {
150
261
  const findings = [
151
262
  ...(audit.findings || audit.actionableFindings || []),
@@ -104,6 +104,7 @@ function projectGroundDefault(result = {}, parsed = {}) {
104
104
  readiness: compactReadiness(result.readiness),
105
105
  authority: compactGroundAuthority(result.authority),
106
106
  orchestrationReadiness: compactOrchestrationReadiness(result.orchestrationReadiness),
107
+ delegationReadiness: compactDelegationReadiness(result.delegationReadiness),
107
108
  requiredContext: Object.freeze({
108
109
  declared: Number(required.declared || 0),
109
110
  matchedInWorkspaceSnapshots: Number(required.matchedInWorkspaceSnapshots || 0),
@@ -163,6 +164,7 @@ function projectHandoffDefault(result = {}, parsed = {}) {
163
164
  status: String(carrier.status || ''),
164
165
  mode: String(carrier.mode || ''),
165
166
  lineage: carrier.lineage ? compactCarrierLineage(carrier.lineage) : null,
167
+ allocation: result.carrierAllocation ? Object.freeze({ ...result.carrierAllocation }) : null,
166
168
  route: Object.freeze({
167
169
  id: String(route.id || ''),
168
170
  state: String(route.state || ''),
@@ -275,11 +277,17 @@ function compactGroundAuthority(authority = {}) {
275
277
  }),
276
278
  holderBinding: Object.freeze({
277
279
  state: String(holderBinding.state || ''),
280
+ bindingPresent: Boolean(holderBinding.bindingPresent),
281
+ declarationPresent: Boolean(holderBinding.declarationPresent),
278
282
  holderId: String(holderBinding.holderId || ''),
279
283
  roleLabel: String(holderBinding.roleLabel || ''),
280
284
  recipientRoleLabel: String(holderBinding.recipientRoleLabel || ''),
281
285
  recipientCompatibility: String(holderBinding.recipientCompatibility || ''),
282
286
  source: String(holderBinding.source || ''),
287
+ sourceDetail: holderBinding.sourceDetail ? Object.freeze({ ...holderBinding.sourceDetail }) : null,
288
+ authorization: holderBinding.authorization ? Object.freeze({ ...holderBinding.authorization, provenance: holderBinding.authorization.provenance ? Object.freeze({ ...holderBinding.authorization.provenance }) : null }) : null,
289
+ durableIdentity: holderBinding.durableIdentity ? Object.freeze({ ...holderBinding.durableIdentity }) : Object.freeze({ state: 'not-established' }),
290
+ semanticAuthorityState: String(holderBinding.semanticAuthorityState || 'not-established'),
283
291
  explicit: Boolean(holderBinding.explicit),
284
292
  inferredFromTransport: Boolean(holderBinding.inferredFromTransport),
285
293
  provenance: holderBinding.provenance ? Object.freeze({ ...holderBinding.provenance }) : null
@@ -293,6 +301,24 @@ function compactGroundAuthority(authority = {}) {
293
301
  });
294
302
  }
295
303
 
304
+
305
+ function compactDelegationReadiness(value = {}) {
306
+ if (!value || typeof value !== 'object') return null;
307
+ return Object.freeze({
308
+ state: String(value.state || ''),
309
+ delegateCapabilityAuthority: value.delegateCapabilityAuthority ? Object.freeze({ ...value.delegateCapabilityAuthority }) : null,
310
+ processApplicability: value.processApplicability ? Object.freeze({ ...value.processApplicability }) : null,
311
+ targetAuthority: value.targetAuthority ? Object.freeze({ ...value.targetAuthority }) : null,
312
+ sourceAuthority: value.sourceAuthority ? Object.freeze({ ...value.sourceAuthority }) : null,
313
+ returnReconciliationExpectation: value.returnReconciliationExpectation ? Object.freeze({ ...value.returnReconciliationExpectation }) : null,
314
+ blockers: Object.freeze((value.blockers || []).slice(0, 8).map((item) => Object.freeze({ ...item }))),
315
+ plainChatFallbackPermitted: Boolean(value.plainChatFallbackPermitted),
316
+ repositoryScanningFallbackPermitted: Boolean(value.repositoryScanningFallbackPermitted),
317
+ nextOperations: Object.freeze((value.nextOperations || []).slice(0, 4).map((item) => Object.freeze({ ...item }))),
318
+ boundary: String(value.boundary || '')
319
+ });
320
+ }
321
+
296
322
  function compactOrchestrationReadiness(value = {}) {
297
323
  if (!value || typeof value !== 'object') return null;
298
324
  const wider = value.widerOrchestration || {};
@@ -10,7 +10,7 @@ import { projectRecipientV2HumanOutput } from '../../handoff/recipientV2.humanOu
10
10
  import { inspectRecipientFacingV2Topology } from '../../handoff/recipientV2.inspect.js';
11
11
  import { carrierLineageFromCliParent, initialHandoffCarrierLineage, parentHandoffCarrierLineageFromBundle, parentHandoffCarrierProfileFromBundle } from '../../handoff/carrierLineage.js';
12
12
  import { loadNodePortableInput } from '../../input/node.input.js';
13
- import { reserveHandoffSiblingIndex } from './cli.handoff-sibling-allocation.js';
13
+ import { resolveHandoffSiblingAllocation } from './cli.handoff-sibling-allocation.js';
14
14
  import { normalizeHandoffCarrierProfile } from '../../handoff/carrierProfile.js';
15
15
 
16
16
  export async function prepareHandoffManufactureCliCommand(parsed = {}, runtime = {}) {
@@ -26,7 +26,6 @@ export async function prepareHandoffManufactureCliCommand(parsed = {}, runtime =
26
26
  const handoffPath = flags.handoff || parsed.positionals?.[1] || continuationState.returnHandoffPath || '';
27
27
  if (!flags['workspace-id'] && continuationState.workspaceId) flags['workspace-id'] = continuationState.workspaceId;
28
28
  if (!flags['workspace-target'] && continuationState.workspaceTarget) flags['workspace-target'] = continuationState.workspaceTarget;
29
- if (!flags['package-sibling-index'] && continuationState.returnPackageSiblingIndex) flags['package-sibling-index'] = continuationState.returnPackageSiblingIndex;
30
29
  if (!flags['package-parent'] && continuationState.packageParentPath) flags['package-parent'] = continuationState.packageParentPath;
31
30
  if (!flags.route && handoffPath) flags.route = handoffPath;
32
31
  if (!flags.output && !flags['output-dir'] && parsed.surfaceCommand === 'handoff' && continuationState.returnOutputDir) flags['output-dir'] = continuationState.returnOutputDir;
@@ -55,6 +54,7 @@ export async function prepareHandoffManufactureCliCommand(parsed = {}, runtime =
55
54
  let packageParentSha256 = '';
56
55
  let carrierLineage = initialHandoffCarrierLineage();
57
56
  let inheritedCarrierProfile = normalizeHandoffCarrierProfile(null);
57
+ let carrierAllocation = Object.freeze({ state: 'root', allocationMode: 'initial-root', siblingIndex: null, provenance: Object.freeze({ basis: 'initial-carrier-root' }) });
58
58
  if (parentPackagePath) {
59
59
  const resolvedParent = path.resolve(parentPackagePath);
60
60
  const parentBytes = new Uint8Array(await readFile(resolvedParent));
@@ -86,21 +86,35 @@ export async function prepareHandoffManufactureCliCommand(parsed = {}, runtime =
86
86
  major: Boolean(flags['package-major']),
87
87
  majorReason: flags['major-reason'] || ''
88
88
  });
89
- const siblingAllocation = await reserveHandoffSiblingIndex({
90
- parentPackagePath: resolvedParent,
91
- parentPackageSha256: provisionalLineage.parentPackageSha256,
92
- parentDimension: provisionalLineage.parentDimension,
93
- enabled: !flags['package-major'] && Boolean(flags.output || flags['output-dir']),
94
- siblingIndex: flags['package-sibling-index']
95
- });
96
- carrierLineage = flags['package-major'] ? provisionalLineage : carrierLineageFromCliParent({
97
- bundle: parentBundle,
98
- parentPath: resolvedParent,
99
- parentBytes,
100
- routeDimensions,
101
- qualifiedParentLineage: parentLineage,
102
- siblingIndex: siblingAllocation.siblingIndex
103
- });
89
+ if (flags['package-major']) {
90
+ carrierLineage = provisionalLineage;
91
+ carrierAllocation = Object.freeze({
92
+ state: 'qualified', allocationMode: 'explicit-major', siblingIndex: null,
93
+ provenance: Object.freeze({ basis: 'explicit-major-request', parentPackagePath: resolvedParent, parentPackageSha256: provisionalLineage.parentPackageSha256, parentDimension: provisionalLineage.parentDimension }),
94
+ boundary: 'Carrier Major creation remains explicit and separate from non-Major pointer-order allocation.'
95
+ });
96
+ } else {
97
+ const parentInspection = inspectRecipientFacingV2Topology(parentBundle);
98
+ const siblingAllocation = await resolveHandoffSiblingAllocation({
99
+ parentInspection,
100
+ parentPackagePath: resolvedParent,
101
+ parentPackageSha256: provisionalLineage.parentPackageSha256,
102
+ parentDimension: provisionalLineage.parentDimension,
103
+ selectedRoutePointer: continuationState.selectedRoutePointer || flags['package-parent-route-pointer'] || '',
104
+ selectedRouteId: continuationState.selectedRouteId || flags['package-parent-route-id'] || '',
105
+ explicitSiblingIndex: flags['package-sibling-index'],
106
+ enabled: Boolean(flags.output || flags['output-dir'])
107
+ });
108
+ carrierAllocation = siblingAllocation;
109
+ carrierLineage = carrierLineageFromCliParent({
110
+ bundle: parentBundle,
111
+ parentPath: resolvedParent,
112
+ parentBytes,
113
+ routeDimensions,
114
+ qualifiedParentLineage: parentLineage,
115
+ siblingIndex: siblingAllocation.siblingIndex
116
+ });
117
+ }
104
118
  packageParentSha256 = String(carrierLineage.parentPackageSha256 || '');
105
119
  } else if (flags['package-major']) {
106
120
  throw new Error('portable.cli.handoff-carrier.package-major.parent-required');
@@ -129,6 +143,7 @@ export async function prepareHandoffManufactureCliCommand(parsed = {}, runtime =
129
143
  verifyRoundtrip,
130
144
  recipientRouteSelector: flags.route || '',
131
145
  carrierLineage,
146
+ carrierAllocation,
132
147
  carrierProfile,
133
148
  packageParentBundle,
134
149
  packageParentPath: parentPackagePath ? path.resolve(parentPackagePath) : '',
@@ -329,6 +344,7 @@ export function summarizeHandoffManufactureCliOutput(result = {}, writeReceipt =
329
344
  }) : null,
330
345
  toolingBootstrap: result.toolingBootstrap || null,
331
346
  carrierLineage: result.carrierLineage || projection.lineage || null,
347
+ carrierAllocation: result.carrierAllocation || result.manufacturingEvidence?.carrierAllocation || null,
332
348
  majorReadiness: result.majorReadiness || null,
333
349
  operationBoundary: result.operationBoundary ? Object.freeze({ ...result.operationBoundary }) : null,
334
350
  manufacturingEvidence: result.manufacturingEvidence || null,
@@ -2,6 +2,125 @@ import { mkdir, open, readFile } from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
 
4
4
  const MAX_SIBLING_INDEX = 9999;
5
+ const POINTER_PATH_RE = /^(\d{3}(?:-\d+)*)-handoff-pointer\.trace\.md$/;
6
+
7
+ export function deriveHandoffSiblingAllocation({ parentInspection = null, selectedRoutePointer = '', selectedRouteId = '', explicitSiblingIndex = null, parentPackagePath = '', parentPackageSha256 = '', parentDimension = '' } = {}) {
8
+ const explicit = normalizeSiblingIndex(explicitSiblingIndex);
9
+ const inspection = parentInspection && typeof parentInspection === 'object' ? parentInspection : null;
10
+ if (!inspection || inspection.detected === false) return freeze({
11
+ state: 'unavailable',
12
+ siblingIndex: explicit || null,
13
+ allocationMode: explicit ? 'explicit-override-only' : 'unavailable',
14
+ reasonCode: 'qualified-parent-route-topology-unavailable',
15
+ provenance: provenanceBase({ parentPackagePath, parentPackageSha256, parentDimension, explicitSiblingIndex: explicit }),
16
+ boundary: allocationBoundary()
17
+ });
18
+ if (String(inspection.status || '') !== 'valid') return freeze({
19
+ state: 'blocked',
20
+ siblingIndex: null,
21
+ allocationMode: 'blocked',
22
+ reasonCode: 'qualified-parent-route-topology-invalid',
23
+ provenance: provenanceBase({ parentPackagePath, parentPackageSha256, parentDimension, explicitSiblingIndex: explicit }),
24
+ boundary: allocationBoundary()
25
+ });
26
+
27
+ const routes = [...(inspection.routes || [])].map((route) => ({
28
+ pointerPath: String(route.pointerPath || '').trim(),
29
+ routeId: String(route.routeId || '').trim(),
30
+ workspaceId: String(route.workspaceId || '').trim(),
31
+ workspaceRelativeHandoffPath: String(route.workspaceRelativeHandoffPath || '').trim()
32
+ }));
33
+ if (!routes.length) return blocked('qualified-parent-route-topology-empty');
34
+
35
+ const qualified = [];
36
+ const seenDimensions = new Set();
37
+ for (const route of routes) {
38
+ const match = route.pointerPath.match(POINTER_PATH_RE);
39
+ if (!match) return blocked('qualified-parent-route-pointer-order-unresolved');
40
+ const dimension = match[1];
41
+ if (seenDimensions.has(dimension)) return blocked('qualified-parent-route-pointer-order-ambiguous');
42
+ seenDimensions.add(dimension);
43
+ qualified.push({ ...route, pointerDimension: dimension, pointerSegments: dimension.split('-').map((value) => Number.parseInt(value, 10)) });
44
+ }
45
+ qualified.sort(comparePointerOrder);
46
+
47
+ const pointerSelector = String(selectedRoutePointer || '').trim();
48
+ const routeIdSelector = String(selectedRouteId || '').trim();
49
+ let selected = null;
50
+ if (pointerSelector) {
51
+ const matches = qualified.filter((route) => route.pointerPath === pointerSelector);
52
+ if (matches.length !== 1) return blocked(matches.length ? 'selected-parent-route-pointer-ambiguous' : 'selected-parent-route-pointer-unqualified');
53
+ selected = matches[0];
54
+ }
55
+ if (routeIdSelector) {
56
+ const matches = qualified.filter((route) => route.routeId === routeIdSelector);
57
+ if (matches.length !== 1) return blocked(matches.length ? 'selected-parent-route-id-ambiguous' : 'selected-parent-route-id-unqualified');
58
+ if (selected && selected.pointerPath !== matches[0].pointerPath) return blocked('selected-parent-route-selector-conflict');
59
+ selected = matches[0];
60
+ }
61
+ if (!selected) {
62
+ if (qualified.length !== 1) return blocked('selected-parent-route-required-for-parallel-topology');
63
+ selected = qualified[0];
64
+ }
65
+
66
+ const siblingIndex = qualified.findIndex((route) => route.pointerPath === selected.pointerPath) + 1;
67
+ if (!Number.isInteger(siblingIndex) || siblingIndex < 1 || siblingIndex > MAX_SIBLING_INDEX) return blocked('derived-sibling-index-out-of-range');
68
+ if (explicit && explicit !== siblingIndex) return blocked('explicit-sibling-index-conflicts-with-qualified-topology', { expectedSiblingIndex: siblingIndex });
69
+
70
+ return freeze({
71
+ state: 'qualified',
72
+ siblingIndex,
73
+ childDimension: parentDimension ? `${String(parentDimension).trim()}-${siblingIndex}` : '',
74
+ allocationMode: 'qualified-parent-route-pointer-ordinal',
75
+ explicitOverride: explicit ? 'matched-derived-value' : 'not-supplied',
76
+ reasonCode: '',
77
+ provenance: {
78
+ ...provenanceBase({ parentPackagePath, parentPackageSha256, parentDimension, explicitSiblingIndex: explicit }),
79
+ basis: 'qualified-parent-route-pointer-ordinal',
80
+ selectedRoutePointer: selected.pointerPath,
81
+ selectedRouteId: selected.routeId,
82
+ selectedWorkspaceId: selected.workspaceId,
83
+ selectedWorkspaceRelativeHandoffPath: selected.workspaceRelativeHandoffPath,
84
+ routeOrdinal: siblingIndex,
85
+ qualifiedRouteCount: qualified.length,
86
+ pointerOrder: qualified.map((route, index) => Object.freeze({ ordinal: index + 1, pointerPath: route.pointerPath, routeId: route.routeId }))
87
+ },
88
+ boundary: allocationBoundary()
89
+ });
90
+
91
+ function blocked(reasonCode, extra = {}) {
92
+ return freeze({
93
+ state: 'blocked', siblingIndex: null, allocationMode: 'blocked', reasonCode, ...extra,
94
+ provenance: provenanceBase({ parentPackagePath, parentPackageSha256, parentDimension, explicitSiblingIndex: explicit }),
95
+ boundary: allocationBoundary()
96
+ });
97
+ }
98
+ }
99
+
100
+ export async function resolveHandoffSiblingAllocation(input = {}) {
101
+ const derived = deriveHandoffSiblingAllocation(input);
102
+ if (derived.state === 'qualified') return derived;
103
+ if (derived.state === 'blocked') throw new Error(`portable.cli.handoff-carrier.sibling-allocation.${derived.reasonCode}`);
104
+ const explicit = normalizeSiblingIndex(input.explicitSiblingIndex ?? input.siblingIndex);
105
+ if (!explicit) throw new Error('portable.cli.handoff-carrier.sibling-allocation.explicit-index-required-when-topology-unavailable');
106
+ const legacy = await reserveHandoffSiblingIndex({
107
+ parentPackagePath: input.parentPackagePath,
108
+ parentPackageSha256: input.parentPackageSha256,
109
+ parentDimension: input.parentDimension,
110
+ enabled: input.enabled !== false,
111
+ siblingIndex: explicit
112
+ });
113
+ return freeze({
114
+ ...legacy,
115
+ allocationMode: 'explicit-advanced-override',
116
+ explicitOverride: 'required-because-qualified-topology-unavailable',
117
+ provenance: {
118
+ ...provenanceBase({ ...input, explicitSiblingIndex: explicit }),
119
+ basis: 'explicit-advanced-override-without-qualified-route-topology'
120
+ },
121
+ boundary: allocationBoundary()
122
+ });
123
+ }
5
124
 
6
125
  export async function reserveHandoffSiblingIndex({ parentPackagePath = '', parentPackageSha256 = '', parentDimension = '', enabled = true, siblingIndex = null } = {}) {
7
126
  const requestedSiblingIndex = normalizeSiblingIndex(siblingIndex);
@@ -35,21 +154,39 @@ export async function reserveHandoffSiblingIndex({ parentPackagePath = '', paren
35
154
  }
36
155
  }
37
156
 
157
+ function comparePointerOrder(left, right) {
158
+ const a = left.pointerSegments || [], b = right.pointerSegments || [];
159
+ for (let index = 0; index < Math.max(a.length, b.length); index += 1) {
160
+ if (a[index] === undefined) return -1;
161
+ if (b[index] === undefined) return 1;
162
+ if (a[index] !== b[index]) return a[index] - b[index];
163
+ }
164
+ return left.pointerPath.localeCompare(right.pointerPath);
165
+ }
166
+
167
+ function provenanceBase({ parentPackagePath = '', parentPackageSha256 = '', parentDimension = '', explicitSiblingIndex = null } = {}) {
168
+ return {
169
+ parentPackagePath: String(parentPackagePath || ''),
170
+ parentPackageSha256: String(parentPackageSha256 || '').trim().toLowerCase(),
171
+ parentDimension: String(parentDimension || '').trim(),
172
+ explicitSiblingIndex: explicitSiblingIndex ? normalizeSiblingIndex(explicitSiblingIndex) : null
173
+ };
174
+ }
175
+ function allocationBoundary() { return 'Transport-only carrier allocation. A non-Major sibling ordinal may come only from exact qualified package-local Handoff Pointer order for the selected parent route, or from an explicit advanced override when such topology is unavailable. Allocation never creates semantic Parent, Workspace, Role, acceptance, completion, participant, process, or source authority; different carrier prefixes are not coordinated.'; }
38
176
  function normalizeSiblingIndex(value) {
39
177
  if (value === null || value === undefined || value === '') return 0;
40
178
  const parsed = Number(value);
41
179
  if (!Number.isInteger(parsed) || parsed < 1 || parsed > MAX_SIBLING_INDEX) throw new Error('portable.cli.handoff-carrier.sibling-allocation.index-invalid');
42
180
  return parsed;
43
181
  }
44
-
45
182
  async function readAllocation(allocationPath) {
46
183
  try { return JSON.parse(await readFile(allocationPath, 'utf8')); }
47
184
  catch { throw new Error('portable.cli.handoff-carrier.sibling-allocation.existing-reservation-invalid'); }
48
185
  }
49
-
50
186
  function sameAllocation(left = {}, right = {}) {
51
187
  return String(left.parentPackageSha256 || '') === String(right.parentPackageSha256 || '')
52
188
  && String(left.parentDimension || '') === String(right.parentDimension || '')
53
189
  && Number(left.siblingIndex || 0) === Number(right.siblingIndex || 0)
54
190
  && String(left.childDimension || '') === String(right.childDimension || '');
55
191
  }
192
+ function freeze(value) { if (Array.isArray(value)) return Object.freeze(value.map(freeze)); if (!value || typeof value !== 'object' || Object.isFrozen(value)) return value; return Object.freeze(Object.fromEntries(Object.entries(value).map(([key, item]) => [key, freeze(item)]))); }
@@ -83,7 +83,7 @@ function commonCommandHelp(command, surfaceCommand) {
83
83
  '',
84
84
  `${command} handoff <workspace-dir>`,
85
85
  '',
86
- 'Infers the latest qualified authored Handoff, selected Workspace identity/target, received package parent as carrier-lineage evidence, canonical projected filename, and return output directory. When the outgoing Handoff declares `Signal Kind: return`, delegation manufacture is transport-not-ready unless the delegator supplies `--return-package-sibling-index <1..9999>` for the expected non-Major return, or explicitly declares `--return-package-major`; the qualified reservation is carried in the route pointer and later continuation state. A non-major continuation that writes a carrier must supply an explicitly coordinated `--package-sibling-index <1..9999>`; Tooling will not silently discover the next sibling because isolated runtimes cannot prove a globally unique reservation. Distinct explicit sibling indexes preserve same-Major parallelism, while byte-identical duplicate transport at one exact output path is idempotent and divergent bytes fail closed. It does not implicitly carry sibling Workspaces from the received package; advanced manufacture must select exact reusable parent snapshots with `--package-parent-workspaces <id,...|all>`. Recovery/integration manufacture can bind the reconciled byte proof with `--require-reconciliation-proof --reconciliation-proof <full-reconcile-receipt.json>`; missing, non-ready, edited, or source-stale proof blocks package output. The default receipt keeps output identity, routing text, closure/workspace qualification, verification, and actionable findings compact; add `--full` for the complete manufacture receipt. Normal operator completion is exactly one Handoff package plus the adjacent exact routing text. In markdown-capable hosts render that routing in a fenced code block; do not emit canonical Workspace Evidence/Handoff markdown as additional loose transport files. Runtime-only `.tiinex` state is excluded from canonical manufacture.',
86
+ 'Infers the latest qualified authored Handoff, selected Workspace identity/target, received package parent as carrier-lineage evidence, canonical projected filename, and return output directory. Ordinary non-Major continuation derives its sibling ordinal from the exact qualified selected Handoff Pointer order in the received parent carrier, so common-path `handoff` does not require `--package-sibling-index` or `--return-package-sibling-index`. A single selected Pointer derives `-1`; parallel qualified Pointers derive dense local ordinals in Pointer order, and each returned branch continues from its own carrier. `--package-sibling-index` remains an advanced compatibility override only when qualified route topology is unavailable, and must match when topology already proves the value; conflicting or ambiguous topology fails closed. `--return-package-major` remains the explicit Major-return declaration. Different carrier prefixes are never globally coordinated. Byte-identical duplicate transport at one exact output path is idempotent and divergent bytes fail closed. It does not implicitly carry sibling Workspaces from the received package; advanced manufacture must select exact reusable parent snapshots with `--package-parent-workspaces <id,...|all>`. Recovery/integration manufacture can bind the reconciled byte proof with `--require-reconciliation-proof --reconciliation-proof <full-reconcile-receipt.json>`; missing, non-ready, edited, or source-stale proof blocks package output. The default receipt keeps output identity, routing text, closure/workspace qualification, verification, and actionable findings compact; add `--full` for the complete manufacture receipt. Normal operator completion is exactly one Handoff package plus the adjacent exact routing text. In markdown-capable hosts render that routing in a fenced code block; do not emit canonical Workspace Evidence/Handoff markdown as additional loose transport files. Runtime-only `.tiinex` state is excluded from canonical manufacture.',
87
87
  '',
88
88
  `Advanced/internal catalog: ${command} operations`
89
89
  ];
@@ -6,7 +6,7 @@ import { qualifyToolingRuntimeSourceAlignment } from './handoff.manufacture.runt
6
6
  import { normalizeHandoffCarrierLineage } from '../../handoff/carrierLineage.js';
7
7
  import { normalizeHandoffCarrierProfile } from '../../handoff/carrierProfile.js';
8
8
  import { enumerateNodeWorkspace, PORTABLE_NODE_WORKSPACE_ENUMERATION_SCHEMA_ID } from './handoff.manufacture.enumeration.js';
9
- import { preparePackageParentWorkspaceReuse, projectRequiredContextWorkspaceSelectionPreflight } from './handoff.manufacture.packageParent.js';
9
+ import { preparePackageParentExactMaterialProvider, preparePackageParentWorkspaceReuse, projectPackageParentMaterialClosurePreflight, projectRequiredContextWorkspaceSelectionPreflight, resolvePackageParentRequirementMaterials } from './handoff.manufacture.packageParent.js';
10
10
  import { qualifyPortableSourceReconciliationProofForManufacture } from '../../comparison/sourceFrontierReconciliationProof.js';
11
11
  import { qualifyPortableManufactureSchemaReferenceCandidate } from '../../handoff/schemaReferencePreflight.js';
12
12
  import { qualifyDelegationReturnReservation } from '../../handoff/delegationReturnReservation.js';
@@ -69,6 +69,12 @@ export async function prepareNodeHandoffManufacturingInput(input = {}, options =
69
69
  workspaceIds: input.packageParentWorkspaceIds || input.reusePackageParentWorkspaceIds || [],
70
70
  workspaceAliases: input.packageParentWorkspaceAliases || input.workspaceAliases || {}
71
71
  });
72
+ const packageParentExactMaterialProvider = preparePackageParentExactMaterialProvider({
73
+ bundle: input.packageParentBundle || null,
74
+ currentWorkspaceIds: [...seenWorkspaceIds],
75
+ parentPackagePath: input.packageParentPath || '',
76
+ parentPackageSha256: input.packageParentSha256 || ''
77
+ });
72
78
  const handoffMarkdown = await handoffMarkdownPromise;
73
79
  const requiredContextWorkspaceSelectionPreflight = projectRequiredContextWorkspaceSelectionPreflight({
74
80
  handoffMarkdown,
@@ -140,7 +146,8 @@ export async function prepareNodeHandoffManufacturingInput(input = {}, options =
140
146
  }
141
147
  const suppliedTransportRoutes = [...(input.transportRoutes || input.handoffRoutes || [])].map((route) => normalizeTransportRoute(route, workspaceId)).filter(Boolean);
142
148
  const reservationProjection = returnCarrierReservationPreflight.state === 'qualified' && returnCarrierReservationPreflight.returnExpected
143
- ? Object.freeze({ carrierKind: returnCarrierReservationPreflight.carrierKind, siblingIndex: returnCarrierReservationPreflight.siblingIndex })
149
+ && (returnCarrierReservationPreflight.carrierKind === 'major' || Number.isInteger(returnCarrierReservationPreflight.siblingIndex))
150
+ ? Object.freeze({ carrierKind: returnCarrierReservationPreflight.carrierKind, ...(Number.isInteger(returnCarrierReservationPreflight.siblingIndex) ? { siblingIndex: returnCarrierReservationPreflight.siblingIndex } : {}) })
144
151
  : null;
145
152
  const transportRoutes = Object.freeze((suppliedTransportRoutes.length ? suppliedTransportRoutes : [Object.freeze({ workspaceId, path: handoffPath })]).map((route) => Object.freeze({ ...route, ...(route.path === handoffPath && String(route.workspaceId || '') === workspaceId && reservationProjection ? { returnCarrierReservation: reservationProjection } : {}) })));
146
153
  const workspaceTargets = mergeWorkspaceTargetBindings(normalizeWorkspaceTargetBindings({
@@ -168,16 +175,18 @@ export async function prepareNodeHandoffManufacturingInput(input = {}, options =
168
175
 
169
176
  const routeSpecs = transportRoutes.length ? transportRoutes : Object.freeze([{ workspaceId, path: handoffPath }]);
170
177
  let requirements = await projectManufacturingRequirements({ handoff, workspaceId, handoffPath, routeSpecs, workspaceRuntimeById });
178
+ const packageParentMaterialClosurePreflight = projectPackageParentMaterialClosurePreflight(requirements, packageParentExactMaterialProvider);
171
179
  let materials = await resolveWorkspaceRequirementMaterials(requirements, workspaceRuntimeById, input.materialBindings || {});
180
+ materials = appendMissingRequirementMaterials(materials, resolvePackageParentRequirementMaterials(requirements, packageParentExactMaterialProvider));
172
181
  const dependencyClosure = await expandPointerDependencyClosure({ requirements, materials, workspaceRuntimeById, bindings: input.materialBindings || {} });
173
182
  requirements = dependencyClosure.requirements;
174
- materials = dependencyClosure.materials;
183
+ materials = appendMissingRequirementMaterials(dependencyClosure.materials, resolvePackageParentRequirementMaterials(requirements, packageParentExactMaterialProvider));
175
184
  const routeParentBoundaryClosure = expandRouteParentBoundaryClosure({ requirements, materials, workspaceMaterializations, workspaceRuntimeById, routeSpecs });
176
185
  requirements = routeParentBoundaryClosure.requirements;
177
- materials = routeParentBoundaryClosure.materials;
186
+ materials = appendMissingRequirementMaterials(routeParentBoundaryClosure.materials, resolvePackageParentRequirementMaterials(requirements, packageParentExactMaterialProvider));
178
187
  const parentBoundaryClosure = expandBoundedParentBoundaryClosure({ requirements, materials, workspaceMaterializations, workspaceRuntimeById });
179
188
  requirements = parentBoundaryClosure.requirements;
180
- materials = parentBoundaryClosure.materials;
189
+ materials = appendMissingRequirementMaterials(parentBoundaryClosure.materials, resolvePackageParentRequirementMaterials(requirements, packageParentExactMaterialProvider));
181
190
  const toolingBootstrapResult = await toolingBootstrapPromise;
182
191
  if (toolingBootstrapResult.error) throw toolingBootstrapResult.error;
183
192
  const toolingBootstrap = toolingBootstrapResult.value;
@@ -205,6 +214,7 @@ export async function prepareNodeHandoffManufacturingInput(input = {}, options =
205
214
  transportRoutes,
206
215
  workspaceTargets,
207
216
  carrierLineage: normalizeHandoffCarrierLineage(input.carrierLineage || null),
217
+ carrierAllocation: input.carrierAllocation ? Object.freeze({ ...input.carrierAllocation }) : null,
208
218
  carrierProfile: normalizeHandoffCarrierProfile(input.carrierProfile || null),
209
219
  toolingBootstrap: toolingBootstrap.summary,
210
220
  reconciliationProofQualification,
@@ -218,6 +228,7 @@ export async function prepareNodeHandoffManufacturingInput(input = {}, options =
218
228
  reconciliationProof: reconciliationProofQualification,
219
229
  schemaReferencePreflight,
220
230
  returnCarrierReservationPreflight,
231
+ carrierAllocation: input.carrierAllocation ? Object.freeze({ ...input.carrierAllocation }) : null,
221
232
  packageParentWorkspaceReuse: Object.freeze({
222
233
  state: String(packageParentReuse.state || ''),
223
234
  providerState: String(packageParentReuse.providerState || ''),
@@ -230,6 +241,7 @@ export async function prepareNodeHandoffManufacturingInput(input = {}, options =
230
241
  workspaceAliases: Object.freeze([...(packageParentReuse.workspaceAliases || [])].map((item) => Object.freeze({ ...item }))),
231
242
  boundary: String(packageParentReuse.boundary || '')
232
243
  }),
244
+ packageParentMaterialClosurePreflight,
233
245
  requiredContextWorkspaceSelectionPreflight,
234
246
  carrierProjection: Object.freeze({ requestedRoutes: transportRoutes.length || 1, carrierLineage: normalizeHandoffCarrierLineage(input.carrierLineage || null), carrierProfile: normalizeHandoffCarrierProfile(input.carrierProfile || null), boundary: 'Routes are qualified later against packaged workspace bytes; adapter text is not authority.' })
235
247
  }),
@@ -238,6 +250,19 @@ export async function prepareNodeHandoffManufacturingInput(input = {}, options =
238
250
  }
239
251
 
240
252
 
253
+ function appendMissingRequirementMaterials(existing = [], additional = []) {
254
+ const out = [...(existing || [])];
255
+ const alreadyBound = new Set(out.map((item) => String(item.requirementId || '')).filter(Boolean));
256
+ for (const item of additional || []) {
257
+ const id = String(item.requirementId || '');
258
+ if (id && alreadyBound.has(id)) continue;
259
+ out.push(item);
260
+ if (id) alreadyBound.add(id);
261
+ }
262
+ return Object.freeze(out);
263
+ }
264
+
265
+
241
266
  function mergeWorkspaceTargetBindings(explicit = [], inherited = []) {
242
267
  const out = [];
243
268
  const seen = new Set();