@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.
|
|
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": "
|
|
175
|
+
"gitHead": "8a8e2e2e1b04eae1e5038116c69a449ad8f01349",
|
|
176
176
|
"tiinexRelease": {
|
|
177
177
|
"policy": "tiinex.master-npm-release.v1",
|
|
178
|
-
"sourceCommit": "
|
|
179
|
-
"sourceTree": "
|
|
178
|
+
"sourceCommit": "8a8e2e2e1b04eae1e5038116c69a449ad8f01349",
|
|
179
|
+
"sourceTree": "4596c4e041fb7c7067854f60c7daf68fd631e1f2",
|
|
180
180
|
"repository": "Tiinex/core",
|
|
181
|
-
"previousVersion": "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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
139
|
-
return
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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.
|
|
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
|
|
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: '
|
|
115
|
-
Object.freeze({ kind: 'exact-
|
|
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({
|
|
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
|
-
|
|
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
|
}
|