@tiinex/core 0.20.0 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiinex/core",
3
- "version": "0.20.0",
3
+ "version": "0.22.0",
4
4
  "description": "Shared host-neutral Tiinex implementation core for artifacts, schemas, validation, lineage, grounding, Handoffs, provenance and deterministic workflows.",
5
5
  "type": "module",
6
6
  "sideEffects": true,
@@ -172,12 +172,12 @@
172
172
  "type": "git",
173
173
  "url": "git+https://github.com/Tiinex/core.git"
174
174
  },
175
- "gitHead": "75e2e1e324fbe2e652a5c94f877ce6bf68e4a521",
175
+ "gitHead": "8a8e2e2e1b04eae1e5038116c69a449ad8f01349",
176
176
  "tiinexRelease": {
177
177
  "policy": "tiinex.master-npm-release.v1",
178
- "sourceCommit": "75e2e1e324fbe2e652a5c94f877ce6bf68e4a521",
179
- "sourceTree": "3138e1fdb8c064ec3417a55a731310aa8efd66b8",
178
+ "sourceCommit": "8a8e2e2e1b04eae1e5038116c69a449ad8f01349",
179
+ "sourceTree": "4596c4e041fb7c7067854f60c7daf68fd631e1f2",
180
180
  "repository": "Tiinex/core",
181
- "previousVersion": "0.19.0"
181
+ "previousVersion": "0.21.0"
182
182
  }
183
183
  }
@@ -4,6 +4,7 @@ import { sha256Hex } from '../../../export/package.bytes.js';
4
4
  import { integrityMethodReferenceAuthorityForCreation } from '../../../integrity/integrity.methodReference.js';
5
5
  import { inspectPortableLineageIntegrity } from '../lineage/lineage.integrity.plan.js';
6
6
  import { portableFinding } from '../findings.js';
7
+ import { qualifyTiinexRouteArtifact } from '../handoff/routeArtifactConformance.js';
7
8
 
8
9
  export const PORTABLE_EDITOR_ASSISTANCE_SCHEMA_ID = 'tiinex.portable.editor-assistance.v1';
9
10
 
@@ -28,11 +29,26 @@ function projectDocument(record = {}, records = [], lineageInspection = null) {
28
29
  const markdown = String(record.markdown || '');
29
30
  const recordPath = norm(record.path || record.id || '');
30
31
  const lineageFindings = findingsForPath(lineageInspection?.findings || [], recordPath);
31
- const sharedFindings = [...(audit.findings || []), ...lineageFindings];
32
+ const workspaceConformance = String(audit.schemaId || '') === 'tiinex.workspace.v1'
33
+ ? qualifyTiinexRouteArtifact({ markdown, expectedSchemaId: 'tiinex.workspace.v1', requireExactContract: true })
34
+ : null;
35
+ const packageQualifiedWorkspace = workspaceConformance?.status === 'qualified';
36
+ const sharedFindings = [...(audit.findings || []), ...lineageFindings].filter((finding) => !(packageQualifiedWorkspace && String(finding?.code || '') === 'audit.schema-authority.unqualified'));
32
37
  const diagnostics = sharedFindings
33
38
  .filter((item) => item.severity === 'error' || item.severity === 'warning')
34
39
  .map((finding) => projectDiagnostic(finding, markdown));
35
40
  const actions = [];
41
+ const workspacePackagingRepair = deterministicWorkspacePackagingRepair(record, audit, markdown);
42
+ if (workspacePackagingRepair.state === 'ready' && workspacePackagingRepair.markdown !== markdown) actions.push(freeze({
43
+ id: 'normalize-workspace-schema-and-self-integrity',
44
+ title: workspacePackagingRepair.schemaReferenceChanged ? 'Repair Workspace schema reference and self integrity' : 'Repair Workspace self integrity',
45
+ kind: 'replace-document',
46
+ qualification: 'deterministic-shared-core',
47
+ sourceSha256: sha256Hex(new TextEncoder().encode(markdown)),
48
+ replacementMarkdown: workspacePackagingRepair.markdown,
49
+ diagnosticCodes: workspacePackagingRepair.diagnosticCodes,
50
+ boundary: 'Repairs only a tiinex.workspace.v1 artifact whose replacement independently qualifies through the same exact registered Workspace contract and c14n-v2 self-integrity requirements used by Handoff package manufacture. A linked Current Schema target is normalized to the registered schema identifier only when the linked target itself is not qualified; no repository, Workspace, Handoff, Role, or authority meaning is invented.'
51
+ }));
36
52
  const integrityRepair = deterministicIntegrityHygieneRepair(markdown, audit.findings || []);
37
53
  const repairQualification = integrityRepair.state === 'ready'
38
54
  ? qualifyReplacementAgainstSharedGuardrails(record, records, integrityRepair.markdown)
@@ -54,13 +70,13 @@ function projectDocument(record = {}, records = [], lineageInspection = null) {
54
70
  path: String(record.path || record.id || ''),
55
71
  schemaId: String(audit.schemaId || ''),
56
72
  validator: {
57
- state: audit.qualification?.exact && validationAuthority?.state === 'qualified' ? 'qualified-exact' : 'degraded',
73
+ state: packageQualifiedWorkspace || (audit.qualification?.exact && validationAuthority?.state === 'qualified') ? 'qualified-exact' : 'degraded',
58
74
  requestedSchema: String(audit.qualification?.requestedSchema || audit.schemaId || ''),
59
75
  resolvedThrough: String(audit.qualification?.resolvedThrough || ''),
60
76
  fallbackUsed: Boolean(audit.qualification?.fallback?.used),
61
- authorityState: String(validationAuthority?.state || 'unavailable'),
62
- authorityBasis: String(validationAuthority?.currentReference?.basis || ''),
63
- authorityFindings: [...(validationAuthority?.findings || [])]
77
+ authorityState: packageQualifiedWorkspace ? 'qualified-package-conformance' : String(validationAuthority?.state || 'unavailable'),
78
+ authorityBasis: packageQualifiedWorkspace ? 'registered-workspace-contract+self-integrity' : String(validationAuthority?.currentReference?.basis || ''),
79
+ authorityFindings: packageQualifiedWorkspace ? [] : [...(validationAuthority?.findings || [])]
64
80
  },
65
81
  diagnostics,
66
82
  actions
@@ -72,6 +88,37 @@ function findingsForPath(findings = [], path = '') {
72
88
  return (findings || []).filter((finding) => norm(finding?.evidencePath || finding?.ref || '') === wanted);
73
89
  }
74
90
 
91
+ function deterministicWorkspacePackagingRepair(record = {}, audit = {}, markdown = '') {
92
+ if (String(audit.schemaId || '') !== 'tiinex.workspace.v1') return freeze({ state: 'unavailable' });
93
+ const source = String(markdown || '');
94
+ if (!source) return freeze({ state: 'unavailable' });
95
+ const currentMatches = [...source.matchAll(/^(\s*-\s+Current Schema:\s*)(.*)$/gm)];
96
+ if (currentMatches.length !== 1) return freeze({ state: 'unavailable' });
97
+ const currentRaw = String(currentMatches[0][2] || '').trim();
98
+ const linked = currentRaw.match(/^\[tiinex\.workspace\.v1\]\(([^)]+)\)$/);
99
+ const bare = currentRaw === 'tiinex.workspace.v1';
100
+ if (!linked && !bare) return freeze({ state: 'unavailable' });
101
+
102
+ let candidate = source;
103
+ let schemaReferenceChanged = false;
104
+ const schemaAuthorityUnqualified = (audit.findings || []).some((item) => String(item.code || '') === 'audit.schema-authority.unqualified');
105
+ if (linked && schemaAuthorityUnqualified) {
106
+ candidate = candidate.replace(currentMatches[0][0], `${currentMatches[0][1]}tiinex.workspace.v1`);
107
+ schemaReferenceChanged = true;
108
+ }
109
+
110
+ const sealed = sealC14nV2Self(candidate);
111
+ if (sealed.state !== 'sealed' && sealed.state !== 'unchanged') return freeze({ state: 'unavailable' });
112
+ candidate = String(sealed.markdown || candidate);
113
+ const conformance = qualifyTiinexRouteArtifact({ markdown: candidate, expectedSchemaId: 'tiinex.workspace.v1', requireExactContract: true });
114
+ if (conformance.status !== 'qualified') return freeze({ state: 'blocked', reasons: (conformance.findings || []).map((item) => String(item.code || '')) });
115
+ const diagnosticCodes = [...new Set([
116
+ ...(audit.findings || []).filter((item) => /schema-authority|integrity/i.test(String(item.code || ''))).map((item) => String(item.code || '')),
117
+ 'portable.lineage-integrity.child-self-mismatch'
118
+ ].filter(Boolean))];
119
+ return freeze({ state: 'ready', markdown: candidate, schemaReferenceChanged, diagnosticCodes });
120
+ }
121
+
75
122
  function qualifyReplacementAgainstSharedGuardrails(record = {}, records = [], replacementMarkdown = '') {
76
123
  const focusPath = norm(record.path || record.id || '');
77
124
  if (!focusPath || !replacementMarkdown) return freeze({ state: 'unavailable', reason: 'replacement-or-focus-unavailable' });
@@ -99,11 +146,23 @@ function projectDiagnostic(finding = {}, markdown = '') {
99
146
  message: String(finding.message || 'Tiinex validation finding.'),
100
147
  fixability: String(finding.fixability || 'unknown'),
101
148
  line: located.line,
149
+ sourceRange: located.sourceRange,
102
150
  locationState: located.state,
103
151
  locationBasis: located.basis
104
152
  });
105
153
  }
106
154
 
155
+ function locatedLine(lines = [], index = -1, state = 'deterministic', basis = '') {
156
+ if (!Number.isInteger(index) || index < 0 || index >= lines.length) return freeze({ state: 'unresolved', line: null, sourceRange: null, basis: basis || 'line-unavailable' });
157
+ const text = String(lines[index] || '');
158
+ return freeze({
159
+ state,
160
+ line: index + 1,
161
+ sourceRange: { startLine: index + 1, startColumn: 1, endLine: index + 1, endColumn: text.length + 1 },
162
+ basis
163
+ });
164
+ }
165
+
107
166
  export function locateFindingLine(finding = {}, markdown = '') {
108
167
  const lines = String(markdown || '').replace(/\r\n?/g, '\n').split('\n');
109
168
  const params = finding.params || finding;
@@ -113,43 +172,43 @@ export function locateFindingLine(finding = {}, markdown = '') {
113
172
  const group = String(params.group || '').trim();
114
173
  if (field) {
115
174
  const index = lines.findIndex((line) => new RegExp(`^\\s*-\\s+${escapeRegExp(field)}\\s*:`).test(line));
116
- if (index >= 0) return freeze({ state: 'deterministic', line: index + 1, basis: `field:${field}` });
175
+ if (index >= 0) return locatedLine(lines, index, 'deterministic', `field:${field}`);
117
176
  }
118
177
  for (const owner of [section, heading, group].filter(Boolean)) {
119
178
  const sectionIndex = lines.findIndex((line) => new RegExp(`^#{2,6}\\s+${escapeRegExp(owner)}\\s*$`, 'i').test(line));
120
- if (sectionIndex >= 0) return freeze({ state: section || heading ? 'deterministic' : 'deterministic-anchor', line: sectionIndex + 1, basis: `${section || heading ? 'section' : 'owning-section'}:${owner}` });
179
+ if (sectionIndex >= 0) return locatedLine(lines, sectionIndex, section || heading ? 'deterministic' : 'deterministic-anchor', `${section || heading ? 'section' : 'owning-section'}:${owner}`);
121
180
  const envelopeIndex = lines.findIndex((line) => new RegExp(`^\\s*-\\s+${escapeRegExp(owner)}(?:\\s*:.*)?\\s*$`, 'i').test(line));
122
- if (envelopeIndex >= 0) return freeze({ state: 'deterministic-anchor', line: envelopeIndex + 1, basis: `envelope-owner:${owner}` });
181
+ if (envelopeIndex >= 0) return locatedLine(lines, envelopeIndex, 'deterministic-anchor', `envelope-owner:${owner}`);
123
182
  }
124
183
  const code = String(finding.code || '');
125
184
  if (code.includes('schema.') || code.endsWith('.schema.mismatch') || code === 'audit.schema-authority.unqualified') {
126
185
  const index = lines.findIndex((line) => /^\s*-\s+Current Schema\s*:/.test(line));
127
- if (index >= 0) return freeze({ state: 'deterministic', line: index + 1, basis: 'current-schema-field' });
186
+ if (index >= 0) return locatedLine(lines, index, 'deterministic', 'current-schema-field');
128
187
  }
129
188
  if (code === 'integrity.method-reference.unqualified') {
130
189
  const headingIndex = lines.findIndex((line) => line.trim() === '# Continuity Integrity');
131
190
  const methodIndex = lines.findIndex((line, index) => index > headingIndex && /^\s*-\s+\[sha256-base64url-c14n-v2\]\([^)]+\)\s*$/.test(line));
132
- if (methodIndex >= 0) return freeze({ state: 'deterministic', line: methodIndex + 1, basis: 'continuity-integrity-method-reference' });
191
+ if (methodIndex >= 0) return locatedLine(lines, methodIndex, 'deterministic', 'continuity-integrity-method-reference');
133
192
  }
134
193
  if (code.includes('integrity') || /integrity|checksum|digest/i.test(String(finding.message || ''))) {
135
194
  const headingIndex = lines.findIndex((line) => line.trim() === '# Continuity Integrity');
136
195
  if (headingIndex >= 0) {
137
196
  const index = lines.findIndex((line, i) => i > headingIndex && /^\s+-\s+Value\s*:/.test(line));
138
- if (index >= 0) return freeze({ state: 'deterministic', line: index + 1, basis: 'continuity-integrity-value' });
139
- return freeze({ state: 'deterministic-anchor', line: headingIndex + 1, basis: 'continuity-integrity-heading' });
197
+ if (index >= 0) return locatedLine(lines, index, 'deterministic', 'continuity-integrity-value');
198
+ return locatedLine(lines, headingIndex, 'deterministic-anchor', 'continuity-integrity-heading');
140
199
  }
141
200
  }
142
201
  if (/^(portable\.contract\.|root\.|integrity\.)/i.test(code) && (/missing|required|incomplete/i.test(code) || /\bmissing\b|\brequired\b/i.test(String(finding.message || '')))) {
143
202
  const bodyHeading = lines.findIndex((line) => /^#\s+\S/.test(line) && !/^#\s+Continuity (?:Context|Integrity)\s*$/.test(line));
144
- if (bodyHeading >= 0) return freeze({ state: 'deterministic-anchor', line: bodyHeading + 1, basis: section ? `body-heading-for-missing-section:${section}` : field ? `body-heading-for-missing-field:${field}` : 'body-heading-for-missing-required-content' });
203
+ if (bodyHeading >= 0) return locatedLine(lines, bodyHeading, 'deterministic-anchor', section ? `body-heading-for-missing-section:${section}` : field ? `body-heading-for-missing-field:${field}` : 'body-heading-for-missing-required-content');
145
204
  const contextHeading = lines.findIndex((line) => line.trim() === '# Continuity Context');
146
- if (contextHeading >= 0) return freeze({ state: 'deterministic-anchor', line: contextHeading + 1, basis: 'continuity-context-for-missing-required-content' });
205
+ if (contextHeading >= 0) return locatedLine(lines, contextHeading, 'deterministic-anchor', 'continuity-context-for-missing-required-content');
147
206
  }
148
207
  if (/\.body\.|body/i.test(code) || /\bbody\b/i.test(String(finding.message || ''))) {
149
208
  const bodyHeading = lines.findIndex((line) => /^#\s+\S/.test(line) && !/^#\s+Continuity (?:Context|Integrity)\s*$/.test(line));
150
- if (bodyHeading >= 0) return freeze({ state: 'deterministic-anchor', line: bodyHeading + 1, basis: 'body-heading-for-body-finding' });
209
+ if (bodyHeading >= 0) return locatedLine(lines, bodyHeading, 'deterministic-anchor', 'body-heading-for-body-finding');
151
210
  }
152
- return freeze({ state: 'unresolved', line: null, basis: 'shared-finding-has-no-deterministic-line-evidence' });
211
+ return freeze({ state: 'unresolved', line: null, sourceRange: null, basis: 'shared-finding-has-no-deterministic-line-evidence' });
153
212
  }
154
213
 
155
214
  function deterministicIntegrityHygieneRepair(markdown = '', findings = []) {
@@ -15,12 +15,18 @@ export function projectGroundingDelegationArtifactAuthority({ authority = null,
15
15
  const sender = qualifiedRole(senderRole, handoff.fromReference || '');
16
16
  if (!sender) unresolved.push('exact-sender-role-authority-not-established');
17
17
 
18
+ const delegateSelection = transferSelection ? selectForwardDelegate(transferSelection.record, authority) : null;
19
+ if (transferSelection && !delegateSelection?.selector) unresolved.push('forward-delegate-selector-not-established');
20
+ if (delegateSelection?.selector && !delegateSelection?.role) unresolved.push('exact-forward-selected-delegate-role-authority-not-established');
21
+
18
22
  const taskRecord = transferSelection?.record || null;
19
23
  const targetWorkspaceId = String(taskRecord?.path || '').split('/')[0] || '';
20
24
  const workspaceSource = (sourceEvidence?.workspaces || []).find((item) => String(item.workspace || '') === targetWorkspaceId && ['qualified', 'explicit-profile'].includes(String(item.state || ''))) || null;
21
25
  if (!workspaceSource?.repository) unresolved.push('exact-target-repository-authority-not-established');
22
26
 
23
- const delegateCapabilityAuthority = recipient && transferSelection ? delegateProjection(recipient, handoff, transferSelection) : null;
27
+ const delegateCapabilityAuthority = delegateSelection?.role && delegateSelection?.selector && transferSelection
28
+ ? delegateProjection(delegateSelection.role, handoff, transferSelection, delegateSelection.selector)
29
+ : null;
24
30
  if (!delegateCapabilityAuthority) unresolved.push('delegate-capability-artifact-projection-not-established');
25
31
 
26
32
  const processApplicability = sender && transferSelection ? processProjection(sender, handoff, transferSelection) : null;
@@ -56,9 +62,11 @@ export function projectGroundingDelegationArtifactAuthority({ authority = null,
56
62
  controllingTask: taskRecord ? sourceArtifactFromRecord(taskRecord) : null,
57
63
  senderRole: sender ? sender.sourceArtifact : null,
58
64
  recipientRole: recipient ? recipient.sourceArtifact : null,
59
- boundary: 'Projection only. Exact Handoff transfer, exact endpoint Role authority, exact controlling Task and exact Workspace source identity are composed mechanically; Role/cache inventory, filenames, adjacency and arbitrary prose are never searched for delegation meaning.'
65
+ delegateSelector: delegateSelection?.selector || null,
66
+ delegateRole: delegateSelection?.role?.sourceArtifact || null,
67
+ boundary: 'Projection only. The inbound Handoff recipient/current holder and downstream specialist are independent claims. The downstream Role is resolved only after one exact explicit selector declaration on the controlling current Task; route-bounded Role material then qualifies that already-selected Role. Role/cache inventory, endpoint identity, filenames, adjacency and arbitrary prose are never used to choose a delegate.'
60
68
  }),
61
- boundary: 'Artifact-derived delegation closure is available only from the exact selected forward chain. It does not select a delegate, invent a process, create source permission, or treat an endpoint alone as delegation authority.'
69
+ boundary: 'Artifact-derived delegation closure is available only from the exact selected forward chain. The current-work declaration selects the downstream Role; exact Role material qualifies capability. Inbound recipient/holder identity, Role-cache carriage, process applicability, source permission and return authority remain separate.'
62
70
  });
63
71
  }
64
72
 
@@ -96,7 +104,85 @@ function qualifiedRole(role, declaredReference = '') {
96
104
  });
97
105
  }
98
106
 
99
- function delegateProjection(role, handoff, selected) {
107
+ function selectForwardDelegate(taskRecord, authority = {}) {
108
+ const selector = explicitTaskSpecialistSelector(taskRecord);
109
+ if (!selector) return Object.freeze({ selector: null, role: null, candidates: Object.freeze([]) });
110
+ const candidates = exactRouteRoleMaterials(authority).filter((role) => normalize(role.label) === normalize(selector.roleLabel));
111
+ const unique = dedupeRoles(candidates);
112
+ return Object.freeze({ selector, role: unique.length === 1 ? unique[0] : null, candidates: Object.freeze(unique) });
113
+ }
114
+
115
+ function explicitTaskSpecialistSelector(taskRecord = {}) {
116
+ if (String(taskRecord.schemaId || '') !== 'tiinex.task.v1' || !taskRecord.hasContinuityContext || !taskRecord.hasIntegrity) return null;
117
+ const objective = section(taskRecord.markdown || '', 'Objective');
118
+ if (!objective) return null;
119
+ const declarations = objective
120
+ .split(/\r?\n\s*\r?\n/u)
121
+ .map((item) => item.trim())
122
+ .filter(Boolean)
123
+ .flatMap((paragraph) => {
124
+ if (/\r|\n/u.test(paragraph)) return [];
125
+ const match = paragraph.match(/^(.{1,80}?) is the explicitly selected specialist for this (.{1,120}?)\.$/u);
126
+ if (!match) return [];
127
+ const roleLabel = match[1].trim();
128
+ const scope = match[2].trim();
129
+ if (!roleLabel || !scope || /[\r\n]/u.test(roleLabel)) return [];
130
+ return [Object.freeze({
131
+ state: 'explicit-current-work-selector',
132
+ roleLabel,
133
+ selectorKind: 'explicit-specialist-declaration-paragraph',
134
+ section: 'Objective',
135
+ declaration: paragraph,
136
+ scope,
137
+ sourceArtifact: sourceArtifactFromRecord(taskRecord),
138
+ boundary: 'Closed lexical declaration paragraph only. Core does not interpret surrounding prose: no Role label is searched for, guessed from inventory, inferred from endpoint identity, or recovered from near-match text.'
139
+ })];
140
+ });
141
+ return declarations.length === 1 ? declarations[0] : null;
142
+ }
143
+
144
+ function exactRouteRoleMaterials(authority = {}) {
145
+ const roles = [];
146
+ const recipient = qualifiedRole(authority?.role || null, authority?.handoff?.toReference || '');
147
+ const sender = qualifiedRole(authority?.senderRole || null, authority?.handoff?.fromReference || '');
148
+ if (recipient) roles.push(recipient);
149
+ if (sender) roles.push(sender);
150
+ for (const item of authority?.participation?.packageRoleGrounding || []) {
151
+ const role = qualifiedPackageGroundingRole(item);
152
+ if (role) roles.push(role);
153
+ }
154
+ return Object.freeze(dedupeRoles(roles));
155
+ }
156
+
157
+ function qualifiedPackageGroundingRole(entry = {}) {
158
+ const artifact = entry?.roleArtifact || null;
159
+ if (!entry?.groundingOnly || String(entry?.materialQualification || '') !== 'qualified' || !artifact) return null;
160
+ const sha256 = String(artifact.sha256 || '').trim().toLowerCase();
161
+ if (!/^[0-9a-f]{64}$/iu.test(sha256)) return null;
162
+ const reference = String(artifact.reference || artifact.path || '').trim();
163
+ const label = String(artifact.roleLabel || entry.label || '').trim();
164
+ if (!reference || !label || String(artifact.schemaId || '') !== 'tiinex.party.role.v1') return null;
165
+ return Object.freeze({
166
+ label,
167
+ kind: 'role',
168
+ roleKind: String(artifact.roleKind || ''),
169
+ boundary: Object.freeze({ ...(entry.exactBoundaryLoaded || {}) }),
170
+ authority: Object.freeze({ ...(entry.authorityBoundaryLoaded || {}) }),
171
+ sourceArtifact: sourceArtifactFromReference(reference, sha256, String(artifact.schemaId || 'tiinex.party.role.v1')),
172
+ materialResolution: Object.freeze({ pointerPath: String(entry.pointerPath || ''), groundingOnly: true })
173
+ });
174
+ }
175
+
176
+ function dedupeRoles(roles = []) {
177
+ const map = new Map();
178
+ for (const role of roles) {
179
+ const key = `${normalize(role?.label)}\u0000${String(role?.sourceArtifact?.path || '')}\u0000${String(role?.sourceArtifact?.sha256 || '')}`;
180
+ if (!map.has(key)) map.set(key, role);
181
+ }
182
+ return [...map.values()];
183
+ }
184
+
185
+ function delegateProjection(role, handoff, selected, selector) {
100
186
  const capabilities = [
101
187
  role.roleKind ? Object.freeze({ kind: 'role-kind', value: role.roleKind }) : null,
102
188
  role.boundary.inScope ? Object.freeze({ kind: 'role-in-scope', value: role.boundary.inScope }) : null,
@@ -111,10 +197,18 @@ function delegateProjection(role, handoff, selected) {
111
197
  selection: Object.freeze({ state: 'explicit-forward-selected', forwardSelected: true }),
112
198
  sourceArtifact: role.sourceArtifact,
113
199
  facts: Object.freeze([
114
- Object.freeze({ kind: 'selected-handoff-transfer', handoff: qualifiedHandoffPath(handoff), transferId: String(selected.transfer.id || ''), transferKind: String(selected.transfer.transferKind || ''), controllingArtifact: selected.resolvedPath }),
115
- Object.freeze({ kind: 'exact-recipient-role-authority', role: role.label, roleKind: role.roleKind })
200
+ Object.freeze({ kind: 'current-work-forward-delegate-selector', role: selector.roleLabel, section: selector.section, selectorKind: selector.selectorKind, controllingArtifact: selected.resolvedPath }),
201
+ Object.freeze({ kind: 'exact-forward-selected-role-authority', role: role.label, roleKind: role.roleKind }),
202
+ Object.freeze({ kind: 'selected-handoff-current-work-transfer', handoff: qualifiedHandoffPath(handoff), transferId: String(selected.transfer.id || ''), transferKind: String(selected.transfer.transferKind || ''), controllingArtifact: selected.resolvedPath })
116
203
  ]),
117
- provenance: Object.freeze({ source: role.sourceArtifact.path, basis: 'selected-handoff-transfer-to-exact-recipient-role-and-controlling-task', forwardSelector: Object.freeze({ handoff: qualifiedHandoffPath(handoff), transferId: String(selected.transfer.id || ''), controllingArtifact: selected.resolvedPath }) })
204
+ provenance: Object.freeze({
205
+ source: role.sourceArtifact.path,
206
+ basis: 'exact-controlling-task-explicit-specialist-selector-plus-exact-selected-role-material',
207
+ forwardSelector: selector,
208
+ roleSourceArtifact: role.sourceArtifact,
209
+ materialResolution: role.materialResolution || null,
210
+ currentWorkTransfer: Object.freeze({ handoff: qualifiedHandoffPath(handoff), transferId: String(selected.transfer.id || ''), controllingArtifact: selected.resolvedPath })
211
+ })
118
212
  });
119
213
  }
120
214
 
@@ -422,7 +422,18 @@ function resolvePackageParticipantRoles(bundle = {}, orientation = null, selecte
422
422
  packageDeclared: true,
423
423
  groundingOnly: true,
424
424
  semanticParticipant: false,
425
- roleArtifact: Object.freeze({ path: parsed.path, sha256: parsed.sha256, schemaId: parsed.schemaId, roleLabel: parsed.label, roleKind: parsed.roleKind }),
425
+ materialQualification: 'qualified',
426
+ roleArtifact: Object.freeze({
427
+ path: parsed.path,
428
+ reference: String(facts.referenceTarget || parsed.path || ''),
429
+ sha256: parsed.sha256,
430
+ schemaId: parsed.schemaId,
431
+ roleLabel: parsed.label,
432
+ roleKind: parsed.roleKind
433
+ }),
434
+ exactBoundaryLoaded: parsed.boundary,
435
+ authorityBoundaryLoaded: parsed.authorityBoundary,
436
+ interpretationLimitsLoaded: parsed.interpretationLimits,
426
437
  pointerPath
427
438
  }));
428
439
  }