@tiinex/core 0.19.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 (28) hide show
  1. package/package.json +7 -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.common-author.js +117 -6
  10. package/src/tooling/portable/adapters/cli/cli.common-output.js +19 -0
  11. package/src/tooling/portable/adapters/node/handoff.manufacture.js +26 -4
  12. package/src/tooling/portable/adapters/node/handoff.manufacture.packageParent.js +123 -0
  13. package/src/tooling/portable/audit/audit.capability.js +10 -4
  14. package/src/tooling/portable/grounding/grounding.capsule.js +26 -2
  15. package/src/tooling/portable/grounding/grounding.delegationArtifactAuthority.js +220 -0
  16. package/src/tooling/portable/grounding/grounding.delegationReadiness.js +241 -0
  17. package/src/tooling/portable/grounding/grounding.holderAssignmentModes.js +125 -0
  18. package/src/tooling/portable/grounding/grounding.holderBindingAuthorization.js +49 -50
  19. package/src/tooling/portable/grounding/grounding.readiness.js +19 -3
  20. package/src/tooling/portable/grounding/grounding.readiness.support.js +1 -1
  21. package/src/tooling/portable/handoff/carrierProjection.routeQualification.js +20 -4
  22. package/src/tooling/portable/handoff/coldStartQualification.grounding.js +55 -3
  23. package/src/tooling/portable/handoff/coldStartQualification.materials.js +84 -10
  24. package/src/tooling/portable/handoff/recipientV2.endpointRolePointers.js +1 -1
  25. package/src/tooling/portable/handoff/recipientV2.inspect.projection.js +5 -2
  26. package/src/tooling/portable/handoff/recipientV2.packageV1.build.js +2 -2
  27. package/src/tooling/portable/handoff/recipientV2.topology.js +5 -3
  28. package/src/tooling/portable/handoff/recipientV2.topology.materials.js +0 -0
@@ -1,81 +1,80 @@
1
- import { deepFreeze, normalizeComparable, normalizeToken } from '../handoff/coldStartQualification.shared.js';
1
+ import { deepFreeze, normalizeToken } from '../handoff/coldStartQualification.shared.js';
2
+ import {
3
+ HOLDER_ASSIGNMENT_MODE,
4
+ isCanonicalHolderAssignmentMode,
5
+ projectHolderAssignmentModeAuthority
6
+ } from './grounding.holderAssignmentModes.js';
2
7
 
3
- export const EXPLICIT_SESSION_OR_HANDOFF_HOLDER_STATE = 'assignable per explicit session or Handoff';
4
- export const CURRENT_ROLE_EXPLICIT_SESSION_OR_HANDOFF_HOLDER_STATE = 'assignable per explicit session, role invocation, or Handoff; no permanent holder asserted';
5
- export const ANCHOR_EXPLICIT_SESSION_OR_HANDOFF_HOLDER_STATE = 'assignable per explicit session or Handoff; no permanent holder asserted';
6
-
7
- const AUTHORIZED_EXPLICIT_SESSION_OR_HANDOFF_STATES = new Set([
8
- normalizeComparable(EXPLICIT_SESSION_OR_HANDOFF_HOLDER_STATE),
9
- normalizeComparable(CURRENT_ROLE_EXPLICIT_SESSION_OR_HANDOFF_HOLDER_STATE),
10
- normalizeComparable(ANCHOR_EXPLICIT_SESSION_OR_HANDOFF_HOLDER_STATE)
11
- ]);
12
-
13
- export function projectHolderBindingAuthorization(role = {}) {
8
+ export function projectHolderBindingAuthorization(role = {}, options = {}) {
14
9
  const endpointKind = normalizeToken(role?.endpoint?.kind || '');
10
+ const requestedMode = String(options.assertionMode || options.assignmentMode || HOLDER_ASSIGNMENT_MODE.EXPLICIT_SESSION).trim();
15
11
  if (endpointKind !== 'role') return deepFreeze({
16
12
  state: 'not-applicable',
17
13
  assignmentMode: '',
14
+ authorizedModes: deepFreeze([]),
18
15
  holderState: '',
19
16
  source: 'none',
20
17
  reasonCode: 'recipient-not-role',
18
+ modeAuthority: projectHolderAssignmentModeAuthority(role),
21
19
  provenance: {
22
- basis: 'recipient-not-role',
23
- roleArtifactPath: '',
24
- roleArtifactSha256: '',
25
- section: 'Holder Relationship',
26
- field: 'Holder State',
27
- boundary: 'Holder-assignment authorization is only applicable when the selected Handoff recipient is a Role endpoint.'
20
+ basis: 'recipient-not-role', roleArtifactPath: '', roleArtifactSha256: '', section: 'Holder Relationship', field: 'Assignment Modes', exactValue: '', assertionMode: '', boundary: 'Holder-assignment authorization is only applicable when the selected Handoff recipient is a Role endpoint.'
28
21
  },
29
22
  boundary: 'No Role assignment authorization is required for a non-Role recipient.'
30
23
  });
31
24
 
32
- const material = role?.material?.artifact || null;
33
- const relationship = role?.holderRelationshipLoaded || {};
34
- const holderState = String(relationship.holderState || '').trim();
35
- const qualifiedMaterial = String(role?.state || '') === 'qualified'
36
- && String(role?.material?.state || '') === 'qualified'
37
- && Boolean(material?.path)
38
- && Boolean(material?.sha256);
39
- const provenance = {
40
- basis: qualifiedMaterial ? 'exact-qualified-role-holder-relationship' : 'exact-qualified-role-holder-relationship-not-established',
41
- roleArtifactPath: String(material?.path || ''),
42
- roleArtifactSha256: String(material?.sha256 || ''),
43
- roleSchemaId: String(material?.schemaId || ''),
44
- roleLabel: String(material?.roleLabel || role?.endpoint?.label || ''),
45
- section: 'Holder Relationship',
46
- field: 'Holder State',
47
- exactValue: holderState,
48
- boundary: 'Only exact qualified recipient Role material may authorize the bounded session assignment mode. Handoff endpoints, transport identity, explicit session input, filenames, and package placement do not provide this authority.'
49
- };
25
+ const modeAuthority = projectHolderAssignmentModeAuthority(role);
26
+ const holderState = String(modeAuthority.holderState || role?.holderRelationshipLoaded?.holderState || '').trim();
27
+ const provenance = deepFreeze({
28
+ ...(modeAuthority.provenance || {}),
29
+ assertionMode: requestedMode,
30
+ boundary: 'The bounded binding result compares one explicit assertion mechanism to exact qualified canonical assignment-mode authority. Holder State prose remains diagnostic-only.'
31
+ });
50
32
 
51
- if (!qualifiedMaterial) return deepFreeze({
33
+ if (modeAuthority.state !== 'qualified') return deepFreeze({
52
34
  state: 'unresolved',
53
- assignmentMode: '',
35
+ assignmentMode: requestedMode,
36
+ authorizedModes: deepFreeze([]),
54
37
  holderState,
55
- source: 'none',
56
- reasonCode: 'qualified-role-holder-authority-not-established',
38
+ source: modeAuthority.source || 'none',
39
+ reasonCode: modeAuthority.reasonCode || 'holder-assignment-mode-authority-unresolved',
40
+ modeAuthority,
57
41
  provenance,
58
- boundary: 'A matching session Role assertion remains unauthorized until exact qualified Role material establishes the assignment mode.'
42
+ boundary: 'A matching Role assertion remains unauthorized until exact qualified canonical assignment-mode authority is established.'
59
43
  });
60
44
 
61
- const assignmentAuthorized = AUTHORIZED_EXPLICIT_SESSION_OR_HANDOFF_STATES.has(normalizeComparable(holderState));
62
- if (!assignmentAuthorized) return deepFreeze({
45
+ if (!isCanonicalHolderAssignmentMode(requestedMode)) return deepFreeze({
63
46
  state: 'unresolved',
64
- assignmentMode: '',
47
+ assignmentMode: requestedMode,
48
+ authorizedModes: deepFreeze([...(modeAuthority.modes || [])]),
49
+ holderState,
50
+ source: modeAuthority.source,
51
+ reasonCode: 'holder-binding-assertion-mode-unqualified',
52
+ modeAuthority,
53
+ provenance,
54
+ boundary: 'The asserted binding mechanism is not a canonical mode established by semantic authority. Core does not reinterpret arbitrary assertion labels.'
55
+ });
56
+
57
+ if (!(modeAuthority.modes || []).includes(requestedMode)) return deepFreeze({
58
+ state: 'unresolved',
59
+ assignmentMode: requestedMode,
60
+ authorizedModes: deepFreeze([...(modeAuthority.modes || [])]),
65
61
  holderState,
66
- source: 'qualified-recipient-role-material',
67
- reasonCode: holderState ? 'holder-assignment-mode-not-authorized' : 'holder-assignment-mode-unresolved',
62
+ source: modeAuthority.source,
63
+ reasonCode: 'holder-assignment-mode-not-authorized',
64
+ modeAuthority,
68
65
  provenance,
69
- boundary: 'The exact qualified Role does not establish authorization for explicit-session/Handoff assignment. Core preserves this as unresolved and does not reinterpret other Holder State wording.'
66
+ boundary: 'The exact qualified canonical mode set does not authorize the asserted bounded binding mechanism. No widening between session, user-session, invocation, Handoff, or participation modes is permitted.'
70
67
  });
71
68
 
72
69
  return deepFreeze({
73
70
  state: 'qualified',
74
- assignmentMode: 'explicit-session-or-handoff',
71
+ assignmentMode: requestedMode,
72
+ authorizedModes: deepFreeze([...(modeAuthority.modes || [])]),
75
73
  holderState,
76
- source: 'qualified-recipient-role-material',
74
+ source: modeAuthority.source,
77
75
  reasonCode: 'holder-assignment-mode-authorized',
76
+ modeAuthority,
78
77
  provenance,
79
- boundary: 'Exact qualified Role Holder Relationship authorizes this assignment mode for the bounded current session only; it does not establish durable holder identity or broader participant/process/source authority.'
78
+ boundary: 'Exact qualified canonical assignment-mode authority authorizes only this bounded assertion mechanism; it does not establish durable holder identity or broader participant/process/delegation/source/acceptance authority.'
80
79
  });
81
80
  }
@@ -41,6 +41,7 @@ export function projectPortableGroundingReadiness(input = {}, options = {}) {
41
41
  route,
42
42
  toolingAvailable: true
43
43
  }, { ...options, coldStartMaterialContext: materialContext });
44
+ const authority = withExplicitGroundingAuthorityInputs(grounding, input);
44
45
  const continuation = projectGroundedContinuation({
45
46
  bundle,
46
47
  route,
@@ -62,7 +63,7 @@ export function projectPortableGroundingReadiness(input = {}, options = {}) {
62
63
  });
63
64
  return composeGroundingReadiness({
64
65
  mode: 'routed-handoff-package',
65
- authority: grounding,
66
+ authority,
66
67
  continuation,
67
68
  contextAudit,
68
69
  material,
@@ -140,8 +141,8 @@ export function composeGroundingReadiness({ mode = 'loaded-material', authority
140
141
  known.push(evidence('session-holder-role-binding-authorization', 'qualified', authority?.holderBinding?.authorization?.provenance?.roleArtifactPath || authority?.role?.endpoint?.label || 'recipient Role material'));
141
142
  } else {
142
143
  holderBindingActReady = false;
143
- unresolved.push(evidence('session-holder-role-binding-authorization', holderAuthorizationState || 'unresolved', authority?.holderBinding?.authorization?.holderState || 'exact qualified Role Holder Relationship does not establish the explicit-session/Handoff assignment mode'));
144
- reasons.push(reason('session-holder-role-binding-authorization-unresolved', 'The explicit consuming-session Role assertion matches the selected recipient Role, but exact qualified Role holder-assignment authority does not establish that assignment mode. Matching session input alone cannot make the route act-ready.'));
144
+ unresolved.push(evidence('session-holder-role-binding-authorization', holderAuthorizationState || 'unresolved', authority?.holderBinding?.authorization?.reasonCode || 'exact qualified canonical assignment-mode authority does not authorize the asserted binding mechanism'));
145
+ reasons.push(reason('session-holder-role-binding-authorization-unresolved', 'The explicit consuming-session Role assertion matches the selected recipient Role, but exact qualified canonical assignment-mode authority does not authorize the asserted mechanism. Matching session input alone cannot make the route act-ready.'));
145
146
  }
146
147
  }
147
148
 
@@ -254,6 +255,7 @@ export function composeGroundingReadiness({ mode = 'loaded-material', authority
254
255
  }),
255
256
  continuity,
256
257
  orchestrationReadiness,
258
+ delegationReadiness: capsule.delegationReadiness,
257
259
  capsule,
258
260
  currentWork: Object.freeze({
259
261
  state: topology.currentFrontier.length ? 'current-frontier-resolved' : topology.currentTasks.length ? 'current-candidates-without-frontier' : 'unresolved',
@@ -288,6 +290,20 @@ export function composeGroundingReadiness({ mode = 'loaded-material', authority
288
290
  });
289
291
  }
290
292
 
293
+
294
+ function withExplicitGroundingAuthorityInputs(grounding = {}, input = {}) {
295
+ const context = input.delegationContext && typeof input.delegationContext === 'object' ? input.delegationContext : {};
296
+ const selected = {
297
+ delegateCapabilityAuthority: input.delegateCapabilityAuthority || context.delegateCapabilityAuthority || null,
298
+ processApplicability: input.processApplicability || context.processApplicability || null,
299
+ delegationTargetAuthority: input.delegationTargetAuthority || context.delegationTargetAuthority || context.targetAuthority || null,
300
+ implementationSourceAuthority: input.implementationSourceAuthority || context.implementationSourceAuthority || context.sourceAuthority || null,
301
+ delegationReturnReconciliationExpectation: input.delegationReturnReconciliationExpectation || context.delegationReturnReconciliationExpectation || context.returnReconciliationExpectation || null
302
+ };
303
+ const additions = Object.fromEntries(Object.entries(selected).filter(([, value]) => value && typeof value === 'object'));
304
+ return Object.freeze({ ...(grounding || {}), ...additions, ...(Object.keys(context).length ? { delegationContext: Object.freeze({ ...context }) } : {}) });
305
+ }
306
+
291
307
  function projectWorkspaceActionCoverage(contextAudit = {}) {
292
308
  const workspaces = Array.isArray(contextAudit?.workspaceMaterializations) ? contextAudit.workspaceMaterializations : [];
293
309
  const summaries = workspaces.map((workspace) => {
@@ -174,7 +174,7 @@ export function resolveRequiredContextRecords(requiredContext = [], records = []
174
174
  }
175
175
 
176
176
  function isExactHydratedWorkspaceContext(entry = {}) {
177
- if (String(entry.providerMode || '') !== 'archive') return false;
177
+ if (!['archive', 'cache'].includes(String(entry.providerMode || ''))) return false;
178
178
  if (!['hydrated-text', 'qualified-locator-only'].includes(String(entry.contentState || ''))) return false;
179
179
  const expectedBytes = Number(entry.bytes || 0);
180
180
  const actualBytes = Number(entry.actualBytes || 0);
@@ -71,8 +71,22 @@ function qualifyRequiredRequirement(bundle, descriptor, byteProvider, workspace,
71
71
  let resolution=null;
72
72
  if (!target||target.startsWith('#')) { resolution=resolveDescriptorMaterial(bundle,descriptor,byteProvider,target,requirementId,workspace.id,routePath); if(!resolution) reasons.push('exact-required-material-reference-or-binding-unresolved'); }
73
73
  const qualified=parseWorkspaceQualifiedReference(target);
74
- if (!resolution&&!reasons.length&&qualified) resolution=resolveWorkspaceRequiredMaterial(byteProvider,{id:qualified.workspaceId},qualified.path);
75
- if (!resolution&&!reasons.length&&!qualified&&!isExternalReference(target)) { const resolvedPath=resolveWorkspaceReference(routePath,target); if(!resolvedPath) reasons.push('workspace-reference-outside-or-invalid'); else resolution=resolveWorkspaceRequiredMaterial(byteProvider,workspace,resolvedPath); }
74
+ if (!resolution&&!reasons.length&&qualified) {
75
+ const workspaceResolution=resolveWorkspaceRequiredMaterial(byteProvider,{id:qualified.workspaceId},qualified.path);
76
+ resolution=workspaceResolution.state==='qualified'
77
+ ? workspaceResolution
78
+ : (resolveDescriptorMaterial(bundle,descriptor,byteProvider,target,requirementId,workspace.id,routePath)||workspaceResolution);
79
+ }
80
+ if (!resolution&&!reasons.length&&!qualified&&!isExternalReference(target)) {
81
+ const resolvedPath=resolveWorkspaceReference(routePath,target);
82
+ if(!resolvedPath) reasons.push('workspace-reference-outside-or-invalid');
83
+ else {
84
+ const workspaceResolution=resolveWorkspaceRequiredMaterial(byteProvider,workspace,resolvedPath);
85
+ resolution=workspaceResolution.state==='qualified'
86
+ ? workspaceResolution
87
+ : (resolveDescriptorMaterial(bundle,descriptor,byteProvider,target,requirementId,workspace.id,routePath)||workspaceResolution);
88
+ }
89
+ }
76
90
  if (!resolution&&!reasons.length) resolution=resolveDescriptorMaterial(bundle,descriptor,byteProvider,target,requirementId,workspace.id,routePath);
77
91
  if (!resolution&&!reasons.length) reasons.push('required-material-not-carried');
78
92
  if (resolution?.state!=='qualified'&&resolution?.reason) reasons.push(resolution.reason);
@@ -190,8 +204,10 @@ function resolveRouteParent(bundle, descriptor, byteProvider, workspace, routePa
190
204
  if (!markdown) continue;
191
205
  const self = validatedC14nV2PrimarySelfDigest(markdown);
192
206
  if (self.state !== 'verified' || self.value !== digest) continue;
193
- const key = `${candidate.workspaceId || ''}\u0000${candidate.workspaceRelativePath || ''}\u0000${candidate.packagePath || ''}`;
194
- digestMatches.set(key, Object.freeze({ state: 'qualified', markdown, basis: 'parent-target-digest-candidate', workspaceRelativePath: normalizeWorkspacePath(candidate.workspaceRelativePath || ''), packagePath: String(candidate.packagePath || ''), sha256: sha256Hex(data) }));
207
+ const candidatePath = normalizeWorkspacePath(candidate.workspaceRelativePath || '');
208
+ const candidateSha256 = sha256Hex(data);
209
+ const key = `${candidate.workspaceId || ''}\u0000${candidatePath}\u0000${candidateSha256}`;
210
+ if (!digestMatches.has(key)) digestMatches.set(key, Object.freeze({ state: 'qualified', markdown, basis: 'parent-target-digest-candidate', workspaceRelativePath: candidatePath, packagePath: String(candidate.packagePath || ''), sha256: candidateSha256 }));
195
211
  }
196
212
  if (digestMatches.size === 1) return [...digestMatches.values()][0];
197
213
  if (digestMatches.size > 1) return Object.freeze({ state: 'ambiguous', reason: 'multiple-parent-target-digest-candidates' });
@@ -19,6 +19,7 @@ import {
19
19
  resolveGroundingRouteMarkdown,
20
20
  recipientFactsIndexForColdStart,
21
21
  resolveReferencedRoleMaterial,
22
+ resolveReferencedEndpointRoleMaterial,
22
23
  selectGroundingRoute
23
24
  } from './coldStartQualification.materials.js';
24
25
  import {
@@ -60,6 +61,7 @@ export function groundPortableColdConsumer(input = {}, options = {}) {
60
61
 
61
62
  const bundle = input.bundle || input.package || input;
62
63
  const role = groundRecipientRole(input, handoff, bundle, orientation, selectedRoute, findings, materialContext);
64
+ const senderRole = groundSenderRole(handoff, bundle, orientation, selectedRoute, materialContext);
63
65
  const holderBinding = groundHolderBinding(input, handoff, role, findings);
64
66
  const participation = groundParticipation(input, handoff, bundle, orientation, selectedRoute, findings, materialContext);
65
67
  const interaction = groundInteraction(input, handoff);
@@ -84,6 +86,7 @@ export function groundPortableColdConsumer(input = {}, options = {}) {
84
86
  selectedRoute,
85
87
  handoff,
86
88
  role,
89
+ senderRole,
87
90
  holderBinding,
88
91
  participation,
89
92
  interaction,
@@ -120,6 +123,50 @@ export function groundPortableColdConsumer(input = {}, options = {}) {
120
123
  });
121
124
  }
122
125
 
126
+ function groundSenderRole(handoff, bundle, orientation, selectedRoute, materialContext) {
127
+ const fromKind = normalizeToken(handoff.fromKind);
128
+ if (fromKind && fromKind !== 'role') return deepFreeze({
129
+ state: 'not-applicable',
130
+ endpoint: Object.freeze({ label: handoff.from || '', kind: handoff.fromKind || '', bounded: Boolean(handoff.from) }),
131
+ material: Object.freeze({ state: 'not-required', artifact: null }),
132
+ exactBoundaryLoaded: null,
133
+ authorityBoundaryLoaded: null,
134
+ boundary: 'Sender endpoint is not declared as Role; no sender Role authority is invented.'
135
+ });
136
+ if (!handoff.fromReference) return deepFreeze({
137
+ state: 'unresolved',
138
+ endpoint: Object.freeze({ label: handoff.from || '', kind: handoff.fromKind || (handoff.from ? 'role' : ''), bounded: Boolean(handoff.from) }),
139
+ material: Object.freeze({ state: 'missing-exact-reference', artifact: null }),
140
+ exactBoundaryLoaded: null,
141
+ authorityBoundaryLoaded: null,
142
+ boundary: 'Delegation projection requires an exact Handoff From Reference; Role inventory and endpoint labels are not substituted.'
143
+ });
144
+ const localFindings = [];
145
+ const material = resolveReferencedEndpointRoleMaterial(bundle, handoff, orientation, selectedRoute, localFindings, materialContext, 'from');
146
+ const parsed = material ? parseRoleMaterial(material) : null;
147
+ const target = normalizeComparable(handoff.from || '');
148
+ if (!parsed || (target && normalizeComparable(parsed.label) !== target)) return deepFreeze({
149
+ state: 'unresolved',
150
+ endpoint: Object.freeze({ label: handoff.from || '', kind: handoff.fromKind || (handoff.from ? 'role' : ''), bounded: Boolean(handoff.from) }),
151
+ material: Object.freeze({ state: localFindings.length ? 'unresolved-exact-reference' : 'missing', artifact: null }),
152
+ exactBoundaryLoaded: null,
153
+ authorityBoundaryLoaded: null,
154
+ boundary: 'Exact sender Role material did not qualify; sender authority is not inferred from carried Role inventory.'
155
+ });
156
+ return deepFreeze({
157
+ state: 'qualified',
158
+ endpoint: Object.freeze({ label: handoff.from || parsed.label || '', kind: handoff.fromKind || 'role', bounded: Boolean(handoff.from) }),
159
+ material: Object.freeze({
160
+ state: 'qualified',
161
+ artifact: Object.freeze({ path: parsed.path, sha256: parsed.sha256, schemaId: parsed.schemaId, title: parsed.title, roleLabel: parsed.label, roleKind: parsed.roleKind, reference: handoff.fromReference || '' })
162
+ }),
163
+ exactBoundaryLoaded: parsed.boundary,
164
+ authorityBoundaryLoaded: parsed.authorityBoundary,
165
+ interpretationLimitsLoaded: parsed.interpretationLimits,
166
+ boundary: 'Exact Handoff From Reference Role material only. Sender Role authority remains separate from Handoff transfer and is never inferred from endpoint naming or cache inventory.'
167
+ });
168
+ }
169
+
123
170
  function groundRecipientRole(input, handoff, bundle, orientation, selectedRoute, findings, materialContext) {
124
171
  const toKind = normalizeToken(handoff.toKind);
125
172
  if (toKind && toKind !== 'role') return deepFreeze({
@@ -201,12 +248,14 @@ function groundHolderBinding(input, handoff, role, findings) {
201
248
  const roleRecipient = recipientRoleKind === 'role';
202
249
  const explicitlySupplied = Boolean(roleLabel || holderId);
203
250
  const sourceDetail = holderBindingSourceDetail(input, explicit, explicitlySupplied);
204
- const authorization = projectHolderBindingAuthorization(role);
251
+ const assertionMode = String(explicit.assignmentMode || explicit.bindingMode || explicit.mechanism || input.holderAssignmentMode || input.sessionAssignmentMode || 'explicit-session').trim();
252
+ const authorization = projectHolderBindingAuthorization(role, { assertionMode });
205
253
  const durableIdentity = holderDurableIdentityProjection(holderId);
206
254
 
207
255
  if (!roleRecipient) return deepFreeze({
208
256
  state: 'not-applicable',
209
257
  holderId,
258
+ assertionMode,
210
259
  roleLabel,
211
260
  recipientRoleLabel,
212
261
  recipientCompatibility: 'not-applicable',
@@ -224,6 +273,7 @@ function groundHolderBinding(input, handoff, role, findings) {
224
273
  return deepFreeze({
225
274
  state: 'unresolved',
226
275
  holderId,
276
+ assertionMode,
227
277
  roleLabel: '',
228
278
  recipientRoleLabel,
229
279
  recipientCompatibility: 'unresolved',
@@ -242,6 +292,7 @@ function groundHolderBinding(input, handoff, role, findings) {
242
292
  return deepFreeze({
243
293
  state: 'blocked',
244
294
  holderId,
295
+ assertionMode,
245
296
  roleLabel,
246
297
  recipientRoleLabel,
247
298
  recipientCompatibility: 'mismatch',
@@ -258,6 +309,7 @@ function groundHolderBinding(input, handoff, role, findings) {
258
309
  return deepFreeze({
259
310
  state: 'qualified',
260
311
  holderId,
312
+ assertionMode,
261
313
  roleLabel,
262
314
  recipientRoleLabel,
263
315
  recipientCompatibility: 'matched',
@@ -310,7 +362,7 @@ function holderBindingSourceDetail(input = {}, explicit = {}, explicitlySupplied
310
362
 
311
363
  function groundParticipation(input, handoff, bundle, orientation, selectedRoute, findings, materialContext) {
312
364
  const explicitParticipants = normalizeParticipants(input.participants || input.interaction?.participants || []);
313
- const packageRoleGrounding = resolvePackageParticipantRoles(bundle, orientation, selectedRoute, findings);
365
+ const packageRoleGrounding = resolvePackageParticipantRoles(bundle, orientation, selectedRoute, findings, materialContext);
314
366
  const participants = dedupeGroundedParticipants(explicitParticipants);
315
367
  const contributions = normalizeContributions(input.contributions || input.interaction?.contributions || []);
316
368
  const currentContributionId = String(input.currentContributionId || input.interaction?.currentContributionId || '').trim();
@@ -345,7 +397,7 @@ function resolvePackageParticipantRoles(bundle = {}, orientation = null, selecte
345
397
  const pointerPath = String(pointerPaths[index] || '');
346
398
  const pointerFile = findFile(bundle, pointerPath);
347
399
  if (!pointerFile) { findings.push(portableFinding('error', 'portable.cold-start.participant-role.pointer.missing', 'Selected Handoff route declares Role grounding Pointer ancestry that is not carried.', { pointerPath })); continue; }
348
- const compatibilityFacts = recipientV2FactsIndex(bundle).map.get(pointerPath) || null;
400
+ const compatibilityFacts = recipientFactsIndexForColdStart(bundle, materialContext).map.get(pointerPath) || null;
349
401
  const projectedFacts = (orientation?.participantRoles || []).find((item) => String(item.pointerPath || '') === pointerPath) || null;
350
402
  const facts = compatibilityFacts?.role === 'participant-role'
351
403
  ? compatibilityFacts
@@ -3,6 +3,7 @@ import { packageFileBytes, sha256Hex } from '../../../export/package.bytes.js';
3
3
  import { portableFinding } from '../findings.js';
4
4
  import { inspectStoredWorkspaceArchive } from './workspaceByteProvider.js';
5
5
  import { recipientV2FactsIndex } from './recipientV2.transportManifest.js';
6
+ import { deriveRecipientV2ArtifactFirstPhase1Facts } from './recipientV2.artifactFirst.materials.js';
6
7
  import { parseNamedDeclarationSection } from '../schema/named.declarations.js';
7
8
  import { resolveColdStartRolePointerMaterial } from './coldStartRolePointers.js';
8
9
  import {
@@ -58,7 +59,12 @@ export function collectPackageRoleMaterials(bundle = {}) {
58
59
  }
59
60
 
60
61
  export function resolveReferencedRoleMaterial(bundle = {}, handoff = {}, orientation = null, selectedRoute = null, findings = [], context = null) {
61
- const reference = String(handoff.toReference || '').trim();
62
+ return resolveReferencedEndpointRoleMaterial(bundle, handoff, orientation, selectedRoute, findings, context, 'to');
63
+ }
64
+
65
+ export function resolveReferencedEndpointRoleMaterial(bundle = {}, handoff = {}, orientation = null, selectedRoute = null, findings = [], context = null, endpointParty = 'to') {
66
+ const party = String(endpointParty || 'to').trim().toLowerCase() === 'from' ? 'from' : 'to';
67
+ const reference = String(party === 'from' ? handoff.fromReference : handoff.toReference || '').trim();
62
68
  if (!reference) return null;
63
69
  if (!isExternalReference(reference)) {
64
70
  const workspaceZip = findFile(bundle, String(handoff.packagePath || ''));
@@ -71,7 +77,7 @@ export function resolveReferencedRoleMaterial(bundle = {}, handoff = {}, orienta
71
77
  const markdown = decodeUtf8(matches[0].data || new Uint8Array());
72
78
  if (markdown) return Object.freeze({ path: `${workspaceZip.path}::${resolvedPath}`, markdown, explicit: false, exactReference: reference });
73
79
  }
74
- if (matches.length > 1) findings.push(portableFinding('error', 'portable.cold-start.role.reference-material.ambiguous', 'Handoff To Reference resolves to multiple entries in the selected Workspace archive.', { reference, resolvedPath }));
80
+ if (matches.length > 1) findings.push(portableFinding('error', 'portable.cold-start.role.reference-material.ambiguous', 'Handoff endpoint Reference resolves to multiple entries in the selected Workspace archive.', { reference, resolvedPath }));
75
81
  }
76
82
  }
77
83
  }
@@ -85,14 +91,14 @@ export function resolveReferencedRoleMaterial(bundle = {}, handoff = {}, orienta
85
91
  : projectedFacts
86
92
  ? { role: 'endpoint-role', ...projectedFacts }
87
93
  : {};
88
- if (facts.role !== 'endpoint-role' || String(facts.endpointParty || '').toLowerCase() !== 'to') continue;
94
+ if (facts.role !== 'endpoint-role' || String(facts.endpointParty || '').toLowerCase() !== party) continue;
89
95
  if (facts.referenceTarget && String(facts.referenceTarget) !== reference) continue;
90
96
  const material = resolveColdStartRolePointerMaterial(bundle, facts, findings, String(pointerPath || ''), 'endpoint-role');
91
97
  if (material) endpointPointerMatches.push(Object.freeze({ ...material, exactReference: reference }));
92
98
  }
93
99
  if (endpointPointerMatches.length === 1) return endpointPointerMatches[0];
94
100
  if (endpointPointerMatches.length > 1) {
95
- findings.push(portableFinding('error', 'portable.cold-start.role.reference-material.ambiguous', 'Handoff To Reference resolves through multiple qualified endpoint Role Pointers.', { reference, count: endpointPointerMatches.length }));
101
+ findings.push(portableFinding('error', 'portable.cold-start.role.reference-material.ambiguous', 'Handoff endpoint Reference resolves through multiple qualified endpoint Role Pointers.', { reference, count: endpointPointerMatches.length }));
96
102
  return null;
97
103
  }
98
104
  const cacheMatches = [];
@@ -114,7 +120,7 @@ export function resolveReferencedRoleMaterial(bundle = {}, handoff = {}, orienta
114
120
  }
115
121
  }
116
122
  if (cacheMatches.length === 1) return cacheMatches[0];
117
- if (cacheMatches.length > 1) findings.push(portableFinding('error', 'portable.cold-start.role.reference-material.ambiguous', 'Handoff To Reference resolves to multiple cached exact byte carriers.', { reference, count: cacheMatches.length }));
123
+ if (cacheMatches.length > 1) findings.push(portableFinding('error', 'portable.cold-start.role.reference-material.ambiguous', 'Handoff endpoint Reference resolves to multiple cached exact byte carriers.', { reference, count: cacheMatches.length }));
118
124
  return null;
119
125
  }
120
126
 
@@ -172,9 +178,16 @@ export function parseRoleMaterial(entry) {
172
178
  label,
173
179
  roleKind: sectionField(roleSection, 'Role Kind'),
174
180
  boundary: Object.freeze({ inScope: sectionField(boundarySection, 'In Scope'), outOfScope: sectionField(boundarySection, 'Out Of Scope'), context: sectionField(boundarySection, 'Context') }),
175
- authorityBoundary: Object.freeze({ mayDo: sectionField(authoritySection, 'May Do'), doesNotAuthorize: sectionField(authoritySection, 'Does Not Authorize'), reviewBoundary: sectionField(authoritySection, 'Review Boundary') }),
181
+ authorityBoundary: Object.freeze({
182
+ mayDo: sectionField(authoritySection, 'May Do'),
183
+ doesNotAuthorize: sectionField(authoritySection, 'Does Not Authorize'),
184
+ requiredInstrument: sectionField(authoritySection, 'Required Instrument'),
185
+ delegation: sectionField(authoritySection, 'Delegation'),
186
+ reviewBoundary: sectionField(authoritySection, 'Review Boundary')
187
+ }),
176
188
  holderRelationship: Object.freeze({
177
189
  holderState: sectionField(holderSection, 'Holder State'),
190
+ assignmentModes: sectionField(holderSection, 'Assignment Modes'),
178
191
  currentHolder: sectionField(holderSection, 'Current Holder'),
179
192
  possibleHolder: sectionField(holderSection, 'Possible Holder'),
180
193
  unknownHolder: sectionField(holderSection, 'Unknown Holder'),
@@ -262,7 +275,7 @@ function hydrateRequiredContextEntry(bundle = {}, entry = {}, context = null) {
262
275
  })
263
276
  };
264
277
  if (base.state !== 'qualified') return Object.freeze({ ...base, contentState: 'unavailable', content: '' });
265
- const hydrated = resolveQualifiedMaterialBytes(bundle, resolution, context);
278
+ const hydrated = resolveQualifiedMaterialBytes(bundle, resolution, context, base);
266
279
  if (!hydrated.bytes) return Object.freeze({ ...base, state: 'unresolved', contentState: 'unavailable', content: '' });
267
280
  const actualSha256 = sha256Hex(hydrated.bytes);
268
281
  const identityQualified = (!base.bytes || hydrated.bytes.byteLength === base.bytes) && (!base.sha256 || actualSha256 === base.sha256);
@@ -270,14 +283,21 @@ function hydrateRequiredContextEntry(bundle = {}, entry = {}, context = null) {
270
283
  return Object.freeze({
271
284
  ...base,
272
285
  state: identityQualified ? 'qualified' : 'identity-mismatch',
286
+ workspaceId: String(hydrated.workspaceId || base.workspaceId || ''),
287
+ archivePackagePath: String(hydrated.archivePackagePath || base.archivePackagePath || ''),
288
+ innerPath: String(hydrated.innerPath || base.innerPath || ''),
289
+ packagePath: String(hydrated.packagePath || base.packagePath || ''),
290
+ providerMode: String(hydrated.providerMode || base.providerMode || ''),
291
+ kind: String(hydrated.kind || base.kind || ''),
273
292
  actualBytes: hydrated.bytes.byteLength,
274
293
  actualSha256,
294
+ provenance: Object.freeze({ ...(base.provenance || {}), providerMode: String(hydrated.providerMode || base.providerMode || ''), resolutionKind: String(hydrated.kind || base.kind || '') }),
275
295
  contentState: text ? 'hydrated-text' : identityQualified ? 'qualified-locator-only' : 'unavailable',
276
296
  content: text
277
297
  });
278
298
  }
279
299
 
280
- function resolveQualifiedMaterialBytes(bundle = {}, resolution = {}, context = null) {
300
+ function resolveQualifiedMaterialBytes(bundle = {}, resolution = {}, context = null, requirement = {}) {
281
301
  const kind = String(resolution.kind || '');
282
302
  if (kind === 'workspace-archive-entry' || kind === 'workspace-cache-entry') {
283
303
  const archivePath = String(resolution.archivePackagePath || resolution.packagePath || '');
@@ -293,17 +313,64 @@ function resolveQualifiedMaterialBytes(bundle = {}, resolution = {}, context = n
293
313
  }
294
314
  const packagePath = String(resolution.packagePath || '');
295
315
  const file = packagePath ? findFile(bundle, packagePath) : null;
296
- return Object.freeze({ bytes: file ? packageFileBytes(file) : null });
316
+ if (file) return Object.freeze({ bytes: packageFileBytes(file), packagePath });
317
+ if (kind === 'materialized-required-material') {
318
+ const requirementId = String(requirement.requirementId || '');
319
+ const referenceTarget = String(requirement.referenceTarget || '');
320
+ const matches = [];
321
+ const factsIndex = recipientFactsIndexForColdStart(bundle, context).map;
322
+ const visibleFactsIndex = deriveRecipientV2ArtifactFirstPhase1Facts(bundle.files || []);
323
+ for (const candidateFile of bundle.files || []) {
324
+ const candidatePath = String(candidateFile.path || '');
325
+ const facts = factsIndex.get(candidatePath) || visibleFactsIndex.get(candidatePath) || null;
326
+ if (facts?.role !== 'workspace-dependency-cache' && !/dependency cache/i.test(String(facts?.role || ''))) continue;
327
+ for (const material of facts.materials || []) {
328
+ if (requirementId && String(material.requirementId || '') !== requirementId) continue;
329
+ if (referenceTarget && String(material.referenceTarget || '') !== referenceTarget) continue;
330
+ const archivePath = String(facts.archivePath || '');
331
+ const archiveFile = findFile(bundle, archivePath);
332
+ if (!archiveFile) continue;
333
+ const archive = inspectWorkspaceArchiveForColdStart(archiveFile, context);
334
+ if (archive.state !== 'qualified') continue;
335
+ const entries = (archive.entries || []).filter((entry) => String(entry.path || '') === String(material.archiveEntry || ''));
336
+ if (entries.length !== 1) continue;
337
+ const entry = entries[0];
338
+ const data = packageFileBytes({ data: entry.data });
339
+ const actualSha = sha256Hex(data);
340
+ if (Number(material.bytes || 0) && Number(material.bytes || 0) !== data.byteLength) continue;
341
+ if (String(material.sha256 || '') && String(material.sha256 || '') !== actualSha) continue;
342
+ if (Number(resolution.bytes || 0) && Number(resolution.bytes || 0) !== data.byteLength) continue;
343
+ if (String(resolution.sha256 || '') && String(resolution.sha256 || '') !== actualSha) continue;
344
+ matches.push(Object.freeze({
345
+ bytes: data, providerMode: 'cache', kind: 'workspace-cache-entry',
346
+ workspaceId: String(material.targetWorkspaceId || material.sourceWorkspaceId || facts.workspaceId || ''),
347
+ innerPath: String(material.targetPath || material.originalPath || ''),
348
+ archivePackagePath: archivePath, packagePath: archivePath
349
+ }));
350
+ }
351
+ }
352
+ if (matches.length === 1) return matches[0];
353
+ }
354
+ return Object.freeze({ bytes: null });
297
355
  }
298
356
 
299
357
  export function parseHandoffGrounding(markdown, route) {
300
358
  const parties = sectionText(markdown, 'Handoff Parties');
301
359
  const transferSection = parseNamedDeclarationSection(markdown, '## Transfers');
360
+ const retainedSection = parseNamedDeclarationSection(markdown, '## Retained Responsibilities');
302
361
  const completion = sectionText(markdown, 'Completion Expectation');
303
362
  const transfers = Object.freeze((transferSection.entries || []).filter((entry) => String(entry.name || '').trim().toLowerCase() !== 'none').map((entry) => Object.freeze({
304
363
  id: String(entry.name || ''),
305
364
  transferKind: String(entry.fields?.['Transfer Kind'] || ''),
306
365
  description: String(entry.fields?.Description || ''),
366
+ controllingArtifact: String(entry.fields?.['Controlling Artifact'] || ''),
367
+ controllingArtifactTarget: markdownReferenceTarget(entry.fields?.['Controlling Artifact'] || ''),
368
+ boundary: String(entry.fields?.Boundary || '')
369
+ })));
370
+ const retainedResponsibilities = Object.freeze((retainedSection.entries || []).filter((entry) => String(entry.name || '').trim().toLowerCase() !== 'none').map((entry) => Object.freeze({
371
+ id: String(entry.name || ''),
372
+ retainedBy: String(entry.fields?.['Retained By'] || ''),
373
+ responsibility: String(entry.fields?.Responsibility || ''),
307
374
  boundary: String(entry.fields?.Boundary || '')
308
375
  })));
309
376
  return deepFreeze({
@@ -316,6 +383,7 @@ export function parseHandoffGrounding(markdown, route) {
316
383
  fromReference: sectionReferenceTarget(parties, 'From Reference'),
317
384
  toReference: sectionReferenceTarget(parties, 'To Reference'),
318
385
  transfers,
386
+ retainedResponsibilities,
319
387
  completionExpectation: Object.freeze({
320
388
  signalKind: sectionField(completion, 'Signal Kind'),
321
389
  signalMeaning: sectionField(completion, 'Signal Meaning'),
@@ -330,8 +398,14 @@ export function parseHandoffGrounding(markdown, route) {
330
398
  });
331
399
  }
332
400
 
401
+ function markdownReferenceTarget(value = '') {
402
+ const text = String(value || '').trim();
403
+ const match = text.match(/\[[^\]]*\]\(([^)]+)\)/);
404
+ return match ? String(match[1] || '').trim() : '';
405
+ }
406
+
333
407
  export function emptyHandoffGrounding() {
334
- return deepFreeze({ schemaId: '', purpose: '', from: '', fromKind: '', fromReference: '', to: '', toKind: '', toReference: '', transfers: Object.freeze([]), completionExpectation: Object.freeze({ signalKind: '', signalMeaning: '', returnTo: '' }), routeId: '', workspaceId: '', workspaceRelativePath: '', packagePath: '', sha256: '', boundary: 'No Handoff material supplied.' });
408
+ return deepFreeze({ schemaId: '', purpose: '', from: '', fromKind: '', fromReference: '', to: '', toKind: '', toReference: '', transfers: Object.freeze([]), retainedResponsibilities: Object.freeze([]), completionExpectation: Object.freeze({ signalKind: '', signalMeaning: '', returnTo: '' }), routeId: '', workspaceId: '', workspaceRelativePath: '', packagePath: '', sha256: '', boundary: 'No Handoff material supplied.' });
335
409
  }
336
410
 
337
411
  export function resolveGroundingRouteMarkdown(bundle = {}, selectedRoute = {}, findings = [], context = null) {
@@ -136,7 +136,7 @@ export function buildParticipantRolePointerChain(input = {}) {
136
136
  prose: 'This Pointer is recipient discovery/grounding only. It does not declare semantic participation, change Handoff From/To, prove a human holder, or create Role authority; participation meaning remains owned by authoritative Handoff/Relation/context authority.',
137
137
  currentRead: [
138
138
  { label: 'Route Id', value: `\`${String(input.route?.id || '')}\`` },
139
- { label: 'Grounding Requirement Id', value: `\`${String(requirement.id || '')}\`` },
139
+ { label: 'Participant Requirement Id', value: `\`${String(requirement.id || '')}\`` },
140
140
  ...(roleFacts.roleLabelHint ? [{ label: 'Role Label Hint', value: roleFacts.roleLabelHint }] : []),
141
141
  { label: 'Role Reference', value: roleFacts.referenceTarget ? `\`${roleFacts.referenceTarget}\`` : 'exact carried target' },
142
142
  { label: 'Target Carrier Kind', value: target.carrierKind },
@@ -38,14 +38,17 @@ export function projectRecipientV2EndpointRoles(pointers = []) {
38
38
  export function projectRecipientV2ParticipantRoles(pointers = []) {
39
39
  return Object.freeze(pointers.map((item) => Object.freeze({
40
40
  pointerPath: item.path,
41
- routeId: String(item.facts?.routeId || qualified?.id || ''),
42
- workspaceId: String(item.facts?.workspaceId || ''),
43
41
  routeId: String(item.facts?.routeId || ''),
42
+ workspaceId: String(item.facts?.workspaceId || ''),
43
+ requirementId: String(item.facts?.participantRequirementId || ''),
44
44
  roleLabelHint: String(item.facts?.roleLabelHint || ''),
45
45
  referenceTarget: String(item.facts?.referenceTarget || ''),
46
46
  targetCarrierKind: String(item.facts?.targetCarrierKind || ''),
47
47
  targetWorkspaceId: String(item.facts?.targetWorkspaceId || ''),
48
+ archivePath: String(item.facts?.archivePath || ''),
48
49
  targetInnerPath: String(item.facts?.targetInnerPath || item.facts?.targetArchiveEntry || ''),
50
+ targetArchiveEntry: String(item.facts?.targetArchiveEntry || ''),
51
+ targetBytes: Number(item.facts?.targetBytes || 0),
49
52
  targetSha256: String(item.facts?.targetSha256 || '')
50
53
  })));
51
54
  }
@@ -9,7 +9,7 @@ import { RECIPIENT_V2_ROUTE_SELECTION_AUTHORITY, RECIPIENT_V2_SIBLING_ROUTE_INFE
9
9
  import { recipientV2TransportFacts } from './recipientV2.transportManifest.js';
10
10
  import { buildRecipientV2BootstrapCarrier, buildRecipientV2WorkspaceCarriers, recipientV2ParentAuthority } from './recipientV2.topology.workspaces.js';
11
11
  import { buildEndpointRolePointerChain, buildParticipantRolePointerChain } from './recipientV2.endpointRolePointers.js';
12
- import { bindingForWorkspace, boundedWorkspaceClaimsDetachedRecovery, detachedMaterial, duplicates, finding, roleMaterialTarget, routeClaimsDetachedMaterial, safeToken, uniqueFileIndex } from './recipientV2.topology.materials.js';
12
+ import { bindingForWorkspace, boundedWorkspaceClaimsDetachedRecovery, coalesceDetachedCacheMaterials, detachedMaterial, duplicates, finding, roleMaterialTarget, routeClaimsDetachedMaterial, safeToken, uniqueFileIndex } from './recipientV2.topology.materials.js';
13
13
  import { RECIPIENT_V2_PACKAGE_V1_FORMAT_ID, RECIPIENT_V2_PACKAGE_V1_ROOT_PATH, RECIPIENT_V2_PACKAGE_V1_SCHEMA_ID, RECIPIENT_V2_PACKAGE_V1_SCHEMA_TARGET } from './recipientV2.packageV1.constants.js';
14
14
  import { renderHandoffPackageV1 } from './recipientV2.packageV1.contract.js';
15
15
  import { inspectRecipientFacingV2PackageV1 } from './recipientV2.packageV1.inspect.js';
@@ -136,7 +136,7 @@ function buildRecipientFacingV2PackageV1Prepared(input = {}, sealedByWorkspaceId
136
136
  const workspace = workspaceById.get(plan.workspaceId);
137
137
  if (!workspace) continue;
138
138
  const binding = bindingForWorkspace(descriptor, workspace.workspaceId);
139
- const materials = detached.filter((item) => (workspace.workspaceId === String(route.workspaceId || '') && routeClaimsDetachedMaterial(route, item)) || boundedWorkspaceClaimsDetachedRecovery(binding, item));
139
+ const materials = coalesceDetachedCacheMaterials(detached.filter((item) => (workspace.workspaceId === String(route.workspaceId || '') && routeClaimsDetachedMaterial(route, item)) || boundedWorkspaceClaimsDetachedRecovery(binding, item)));
140
140
  if (!materials.length) continue;
141
141
  const artifactPath = `${plan.prefix}-1-cache.trace.md`;
142
142
  const archivePath = `${plan.prefix}-1-cache.zip`;