@tiinex/core 0.12.0 → 0.14.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 +6 -5
- package/src/audit/audit.run.js +1 -1
- package/src/public/index.js +18 -0
- package/src/public/node.js +1 -0
- package/src/schemas/creation.contracts.js +1 -1
- package/src/schemas/creation.schemaReferences.js +28 -8
- package/src/schemas/schema.reference.js +30 -4
- package/src/schemas/tiinex.root.v1.schema.json +2 -1
- package/src/tooling/portable/adapters/cli/cli.command-input.js +5 -0
- package/src/tooling/portable/adapters/cli/cli.common-output.js +23 -0
- package/src/tooling/portable/adapters/cli/cli.handoff-manufacture.js +52 -11
- package/src/tooling/portable/adapters/cli/cli.help.js +15 -2
- package/src/tooling/portable/adapters/cli/cli.run.js +1 -1
- package/src/tooling/portable/adapters/cli/cli.source-frontier-comparison.js +6 -6
- package/src/tooling/portable/adapters/cli/cli.source-frontier-reconciliation.js +18 -0
- package/src/tooling/portable/adapters/node/bootstrapCarrier.manufacture.js +18 -0
- package/src/tooling/portable/adapters/node/handoff.manufacture.enumeration.js +27 -8
- package/src/tooling/portable/adapters/node/handoff.manufacture.js +17 -0
- package/src/tooling/portable/adapters/node/sourceFrontierComparison.js +2 -1
- package/src/tooling/portable/adapters/node/sourceFrontierReconciliationProof.js +23 -0
- package/src/tooling/portable/adapters/node/workspaceCarrier.manufacture.js +14 -3
- package/src/tooling/portable/audit/audit.capability.js +2 -1
- package/src/tooling/portable/bootstrap/tiinex.llm.bootstrap.md +2 -2
- package/src/tooling/portable/comparison/sourceFrontierComparison.js +54 -4
- package/src/tooling/portable/comparison/sourceFrontierReconciliationProof.js +569 -0
- package/src/tooling/portable/draft/draft.create.js +3 -0
- package/src/tooling/portable/draft/draft.operations.js +1 -1
- package/src/tooling/portable/editor/editor.assistance.js +2 -25
- package/src/tooling/portable/handoff/bootstrapCarrier.manufacture.js +56 -0
- package/src/tooling/portable/handoff/contracts/tiinex.handoff.package.v1.schema.md +340 -77
- package/src/tooling/portable/handoff/manufacture.js +17 -3
- package/src/tooling/portable/handoff/pointerEntrypoint.js +4 -1
- package/src/tooling/portable/handoff/recipientV2.artifactFirst.build.js +73 -32
- package/src/tooling/portable/handoff/recipientV2.artifactFirst.closure.js +1 -1
- package/src/tooling/portable/handoff/recipientV2.artifactFirst.inspect.js +9 -1
- package/src/tooling/portable/handoff/recipientV2.artifactFirst.materials.js +28 -6
- package/src/tooling/portable/handoff/recipientV2.artifactFirst.shared.js +8 -0
- package/src/tooling/portable/handoff/recipientV2.coldProjection.js +11 -6
- package/src/tooling/portable/handoff/recipientV2.entryContract.js +12 -0
- package/src/tooling/portable/handoff/recipientV2.humanOutput.js +48 -6
- package/src/tooling/portable/handoff/recipientV2.packageV1.build.js +55 -43
- package/src/tooling/portable/handoff/recipientV2.packageV1.contract.js +57 -31
- package/src/tooling/portable/handoff/recipientV2.packageV1.inspect.helpers.js +63 -10
- package/src/tooling/portable/handoff/recipientV2.packageV1.inspect.js +55 -16
- package/src/tooling/portable/handoff/recipientV2.packageV1.workspaceProjection.js +14 -1
- package/src/tooling/portable/handoff/recipientV2.topology.js +3 -4
- package/src/tooling/portable/handoff/recipientV2.topology.materials.js +14 -0
- package/src/tooling/portable/handoff/schemaReferencePreflight.js +26 -0
- package/src/tooling/portable/handoff/transportEnvelopeV1.js +5 -0
- package/src/tooling/portable/handoff/workspaceCarrier.manufacture.js +42 -13
- package/src/tooling/portable/index.js +1 -0
- package/src/tooling/portable/operation.catalog.js +8 -0
- package/src/tooling/portable/source/sourceEligibility.js +108 -0
- package/src/validation/validateArtifact.js +62 -6
|
@@ -4,9 +4,11 @@ import { upgradeRecipientRelativeHandoffTransportPackageV2 } from './materialClo
|
|
|
4
4
|
import { buildRecipientRelativeHandoffV2DirectBaseline } from './materialClosure.archiveV2.direct.js';
|
|
5
5
|
import { qualifyMajorCarrierReadiness } from './carrierLineage.js';
|
|
6
6
|
import { manufactureRecipientRelativeWorkspacePackage } from './workspaceCarrier.manufacture.js';
|
|
7
|
+
import { manufactureRecipientRelativeBootstrapPackage } from './bootstrapCarrier.manufacture.js';
|
|
7
8
|
|
|
8
9
|
export function manufactureRecipientRelativeHandoffPackage(input = {}, options = {}) {
|
|
9
10
|
if (String(input.carrierMode || '') === 'workspace') return manufactureRecipientRelativeWorkspacePackage(input, options);
|
|
11
|
+
if (String(input.carrierMode || '') === 'bootstrap') return manufactureRecipientRelativeBootstrapPackage(input, options);
|
|
10
12
|
const baseline = buildRecipientRelativeHandoffV2DirectBaseline(input, options);
|
|
11
13
|
const upgraded = upgradeRecipientRelativeHandoffTransportPackageV2(baseline, input, options);
|
|
12
14
|
const toolingBootstrapInspection = upgraded.inspection?.bootstrapInspection || inspectPortableToolingBootstrap(baseline.bundle || upgraded.bundle || {});
|
|
@@ -15,8 +17,14 @@ export function manufactureRecipientRelativeHandoffPackage(input = {}, options =
|
|
|
15
17
|
carrierProfile: input.carrierProfile || null
|
|
16
18
|
}, input.carrierLineage || upgraded.carrierProjection?.lineage || baseline.carrierProjection?.lineage || {});
|
|
17
19
|
const majorFindings = majorReadiness.state === 'blocked' ? [Object.freeze({ severity: 'error', code: 'portable.handoff-carrier-lineage.major.not-self-contained', message: 'Major Handoff carrier requires complete replacement-capable carried Workspace snapshots.' })] : [];
|
|
20
|
+
const reconciliationProofQualification = input.reconciliationProofQualification || input.manufacturingEvidence?.reconciliationProof || null;
|
|
21
|
+
const reconciliationBlocked = String(reconciliationProofQualification?.state || '') === 'blocked';
|
|
22
|
+
const schemaReferencePreflight = input.schemaReferencePreflight || input.manufacturingEvidence?.schemaReferencePreflight || null;
|
|
23
|
+
const schemaReferenceBlocked = String(schemaReferencePreflight?.state || '') === 'blocked';
|
|
18
24
|
const findings = Object.freeze([
|
|
19
25
|
...majorFindings,
|
|
26
|
+
...(schemaReferencePreflight?.findings || []),
|
|
27
|
+
...(reconciliationProofQualification?.findings || []),
|
|
20
28
|
...(baseline.findings || []),
|
|
21
29
|
...(upgraded.findings || []),
|
|
22
30
|
...(upgraded.inspection?.findings || []),
|
|
@@ -28,7 +36,7 @@ export function manufactureRecipientRelativeHandoffPackage(input = {}, options =
|
|
|
28
36
|
...(upgraded.roundtrip?.findings || []),
|
|
29
37
|
...(toolingBootstrapInspection?.findings || [])
|
|
30
38
|
]);
|
|
31
|
-
const status = baseline.status !== 'blocked' && upgraded.status !== 'blocked' && toolingBootstrapInspection?.status === 'valid' && majorReadiness.state !== 'blocked' ? upgraded.status : 'blocked';
|
|
39
|
+
const status = baseline.status !== 'blocked' && upgraded.status !== 'blocked' && toolingBootstrapInspection?.status === 'valid' && majorReadiness.state !== 'blocked' && !schemaReferenceBlocked && !reconciliationBlocked ? upgraded.status : 'blocked';
|
|
32
40
|
return Object.freeze({
|
|
33
41
|
schema: 'tiinex.portable.handoff-manufacturing.v2',
|
|
34
42
|
status,
|
|
@@ -45,7 +53,9 @@ export function manufactureRecipientRelativeHandoffPackage(input = {}, options =
|
|
|
45
53
|
coldConsumerEntrypointInspection: String(upgraded.coldConsumerEntrypointInspection?.status || 'unavailable'),
|
|
46
54
|
companionInspection: String(upgraded.companionInspection?.status || 'unavailable'),
|
|
47
55
|
roundtrip: upgraded.roundtrip ? String(upgraded.roundtrip.status || 'unknown') : 'not-requested',
|
|
48
|
-
toolingBootstrap: String(toolingBootstrapInspection?.status || 'unavailable')
|
|
56
|
+
toolingBootstrap: String(toolingBootstrapInspection?.status || 'unavailable'),
|
|
57
|
+
schemaReferencePreflight: String(schemaReferencePreflight?.state || 'not-run'),
|
|
58
|
+
reconciliationProof: String(reconciliationProofQualification?.state || 'not-required')
|
|
49
59
|
}),
|
|
50
60
|
plan: baseline.plan,
|
|
51
61
|
bundle: upgraded.bundle || baseline.bundle,
|
|
@@ -63,6 +73,8 @@ export function manufactureRecipientRelativeHandoffPackage(input = {}, options =
|
|
|
63
73
|
roundtrip: upgraded.roundtrip || null,
|
|
64
74
|
toolingBootstrap: input.toolingBootstrap || null,
|
|
65
75
|
manufacturingEvidence: input.manufacturingEvidence || null,
|
|
76
|
+
schemaReferencePreflight,
|
|
77
|
+
reconciliationProofQualification,
|
|
66
78
|
toolingBootstrapInspection,
|
|
67
79
|
carrierLineage: upgraded.carrierProjection?.lineage || baseline.carrierProjection?.lineage || input.carrierLineage || null,
|
|
68
80
|
majorReadiness,
|
|
@@ -73,12 +85,14 @@ export function manufactureRecipientRelativeHandoffPackage(input = {}, options =
|
|
|
73
85
|
sourceMutation: false,
|
|
74
86
|
remoteMutation: false,
|
|
75
87
|
physicalRoundtripVerification: upgraded.roundtrip ? String(upgraded.roundtrip.status || 'unknown') : 'not-requested',
|
|
88
|
+
schemaReferencePreflight: String(schemaReferencePreflight?.state || 'not-run'),
|
|
89
|
+
reconciliationProof: String(reconciliationProofQualification?.state || 'not-required'),
|
|
76
90
|
hostBehaviorAuthority: 'none'
|
|
77
91
|
}),
|
|
78
92
|
migration: upgraded.migration || null,
|
|
79
93
|
baseline: Object.freeze({ schema: baseline.schema, status: baseline.status, packageRepresentationSha256: String(baseline.bundle?.packageRepresentationSha256 || ''), representation: 'semantic-control-plus-detached-material-without-exploded-workspace-carrier' }),
|
|
80
94
|
findings,
|
|
81
95
|
findingSummary: summarizePortableFindings(findings),
|
|
82
|
-
boundary: 'Canonical archive-backed Handoff manufacturing facade. It fails closed unless
|
|
96
|
+
boundary: 'Canonical archive-backed Handoff manufacturing facade. It fails closed unless every selected source-material binding qualifies under tiinex.handoff.package.v1, the exact authoritative selected Handoff is contained by clear qualified carried material, the actively selected local Handoff candidate satisfies prospective per-field exact schema-reference authority, and any required reconciliation proof matches the exact source selected for manufacture.'
|
|
83
97
|
});
|
|
84
98
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { packageFileBytes, sha256Hex, utf8Bytes } from '../../../export/package.bytes.js';
|
|
2
2
|
import { canonicalC14nV2SelfState, sealC14nV2Self } from '../../../integrity/integrity.c14nV2.js';
|
|
3
3
|
import { C14N_V2_VALIDATOR_TARGET } from '../../../integrity/integrity.methodReference.js';
|
|
4
|
+
import { schemaReferenceAuthorityForRegisteredSchema } from '../../../schemas/creation.schemaReferences.js';
|
|
5
|
+
import { renderSchemaReference } from '../../../schemas/schema.reference.js';
|
|
4
6
|
import { inspectHandoffCarrierProjection } from './carrierProjection.js';
|
|
5
7
|
import { qualifyTiinexRouteArtifact } from './routeArtifactConformance.js';
|
|
6
8
|
|
|
@@ -8,6 +10,7 @@ export const HANDOFF_POINTER_ENTRYPOINT_PROJECTION_SCHEMA_ID = 'tiinex.portable.
|
|
|
8
10
|
export const HANDOFF_POINTER_ENTRYPOINT_INSPECTION_SCHEMA_ID = 'tiinex.portable.handoff-pointer-entrypoints.inspection.v1';
|
|
9
11
|
export const HANDOFF_POINTER_ENTRYPOINT_PREFIX = 'handoff-entrypoint-';
|
|
10
12
|
export const CANONICAL_POINTER_SCHEMA_TARGET = 'https://github.com/Tiinex/docs/blob/3988951208eb9a8926e84ab42625d4b42fa00c2d/.topics/.schemas/core/pointer/tiinex.pointer.v1.schema.md';
|
|
13
|
+
const CANONICAL_ROOT_SCHEMA_REFERENCE = renderSchemaReference(schemaReferenceAuthorityForRegisteredSchema('tiinex.root.v1'));
|
|
11
14
|
|
|
12
15
|
const BOUNDARY = 'Generated package-root tiinex.pointer.v1 orientation only. Pointer filename, prose, and placement have no Parent, assignment, acceptance, completion, source, package-identity, or route-selection authority; qualified package carrier/closure truth remains controlling.';
|
|
13
16
|
|
|
@@ -83,7 +86,7 @@ export function isHandoffPointerEntrypointPath(value = '') {
|
|
|
83
86
|
function buildPointerEntry(route = {}, createdAt = '') {
|
|
84
87
|
const path = pointerPath(route);
|
|
85
88
|
const title = `Handoff route pointer — ${String(route.parties?.to || route.workspaceId || 'recipient')}`;
|
|
86
|
-
const unsigned = `# Continuity Context\n\n- Envelope Schema:
|
|
89
|
+
const unsigned = `# Continuity Context\n\n- Envelope Schema: ${CANONICAL_ROOT_SCHEMA_REFERENCE}\n- Current\n - Current Schema: [tiinex.pointer.v1](${CANONICAL_POINTER_SCHEMA_TARGET})\n - Created At: ${createdAt}\n - Summary: Thin package-local pointer to one qualified Handoff route.\n\n---\n\n# ${title}\n\nThis generated pointer exposes one next hop only. Package carrier and closure controls remain the authority for whether that Handoff route is qualified.\n\n## Destinations\n\n- Qualified Handoff route: [${route.workspaceRelativePath}](${route.pointerTarget || route.packagePath})\n\n# Continuity Integrity\n\n- [sha256-base64url-c14n-v2](${C14N_V2_VALIDATOR_TARGET})\n - Towards: self\n - Value: \n`;
|
|
87
90
|
const sealed = sealC14nV2Self(unsigned);
|
|
88
91
|
if (sealed.state !== 'sealed') throw new Error(`portable.handoff-pointer.integrity.seal-failed:${sealed.reason || sealed.state}`);
|
|
89
92
|
return deepFreeze({
|
|
@@ -9,7 +9,7 @@ import { buildPhase1WorkspaceCarriers } from './recipientV2.artifactFirst.worksp
|
|
|
9
9
|
import { buildPhase1RolePointers } from './recipientV2.artifactFirst.roles.js';
|
|
10
10
|
import { deriveRecipientV2ArtifactFirstPhase1Facts } from './recipientV2.artifactFirst.materials.js';
|
|
11
11
|
import { inspectRecipientV2ArtifactFirstPhase1Specimen } from './recipientV2.artifactFirst.inspect.js';
|
|
12
|
-
import { RECIPIENT_V2_READ_PATH, RECIPIENT_V2_ARTIFACT_FIRST_PHASE1_FORMAT_ID, RECIPIENT_V2_ARTIFACT_FIRST_PHASE1_SCHEMA_ID, RECIPIENT_V2_ARTIFACT_FIRST_PHASE2_CLEAN_FORMAT_ID, RECIPIENT_V2_ARTIFACT_FIRST_PHASE2_CLEAN_SCHEMA_ID, PHASE2_CLEAN_PROFILE, PHASE2_COMPATIBILITY_TRANSPORT, PHASE1_BOOTSTRAP_ROLE, PHASE1_CACHE_ROLE, normalizeRoutePath, cacheMaterialBelongsToRoute, sortedQualifiedCarrierRoutes, selectOne, oneFile, repathFinalizedFile, phase1RoutePointerPath, phase1RolePathPrefix, blocked, finding, deepFreeze } from './recipientV2.artifactFirst.shared.js';
|
|
12
|
+
import { RECIPIENT_V2_READ_PATH, RECIPIENT_V2_ARTIFACT_FIRST_PHASE1_FORMAT_ID, RECIPIENT_V2_ARTIFACT_FIRST_PHASE1_SCHEMA_ID, RECIPIENT_V2_ARTIFACT_FIRST_PHASE2_CLEAN_FORMAT_ID, RECIPIENT_V2_ARTIFACT_FIRST_PHASE2_CLEAN_SCHEMA_ID, PHASE2_CLEAN_PROFILE, PHASE2_COMPATIBILITY_TRANSPORT, PHASE1_BOOTSTRAP_ROLE, PHASE1_CACHE_ROLE, normalizeRoutePath, cacheMaterialBelongsToRoute, cacheMaterialIsBoundedWorkspaceRecovery, safeToken, sortedQualifiedCarrierRoutes, selectOne, oneFile, repathFinalizedFile, phase1RoutePointerPath, phase1RolePathPrefix, blocked, finding, deepFreeze } from './recipientV2.artifactFirst.shared.js';
|
|
13
13
|
|
|
14
14
|
export function buildRecipientV2ArtifactFirstPhase1Specimen(input = {}) {
|
|
15
15
|
const cleanCarrierPhase2 = input.cleanCarrierPhase2 === true;
|
|
@@ -103,60 +103,100 @@ export function buildRecipientV2ArtifactFirstPhase1Specimen(input = {}) {
|
|
|
103
103
|
})
|
|
104
104
|
}) : null;
|
|
105
105
|
|
|
106
|
-
const
|
|
107
|
-
const
|
|
106
|
+
const sourceCaches = [...(sourceInspection.caches || [])];
|
|
107
|
+
const sourceCache = sourceCaches.find((item) => String(item.workspaceId || '') === workspaceId) || null;
|
|
108
|
+
const selectedRouteCacheMaterials = sourceCache ? (sourceCache.materials || []).filter((item) => cacheMaterialBelongsToRoute(item, workspaceId, route.workspaceRelativeHandoffPath)) : [];
|
|
108
109
|
const siblingDetachedMaterials = routePlans
|
|
109
110
|
.filter((plan) => plan.routeId !== selectedRouteId)
|
|
110
|
-
.flatMap((plan) =>
|
|
111
|
-
if (siblingDetachedMaterials.length) return blocked('shared-route-detached-cache-unsupported', [finding('error', 'portable.handoff-v2-phase1.shared-route.detached-cache-unsupported', 'Shared artifact-first carriage currently supports detached cache material only for the explicitly selected route;
|
|
111
|
+
.flatMap((plan) => sourceCaches.flatMap((cache) => (cache.materials || []).filter((item) => cacheMaterialBelongsToRoute(item, plan.workspaceId, plan.workspaceRelativeHandoffPath) && !cacheMaterialIsBoundedWorkspaceRecovery(item, plan.workspaceId))));
|
|
112
|
+
if (siblingDetachedMaterials.length) return blocked('shared-route-detached-cache-unsupported', [finding('error', 'portable.handoff-v2-phase1.shared-route.detached-cache-unsupported', 'Shared artifact-first carriage currently supports route-specific detached cache material only for the explicitly selected route; bounded Workspace recovery remains independently carryable for every bounded Workspace.', { count: siblingDetachedMaterials.length })]);
|
|
113
|
+
|
|
114
|
+
const cacheBuilds = [];
|
|
112
115
|
let cacheArtifact = null;
|
|
113
|
-
let
|
|
114
|
-
let
|
|
115
|
-
|
|
116
|
-
const
|
|
117
|
-
|
|
116
|
+
let selectedCacheMaterials = [];
|
|
117
|
+
let auxiliaryCacheOrdinal = 0;
|
|
118
|
+
for (const candidateCache of sourceCaches) {
|
|
119
|
+
const cacheWorkspaceId = String(candidateCache.workspaceId || '');
|
|
120
|
+
const recoveryMaterials = (candidateCache.materials || []).filter((item) => cacheMaterialIsBoundedWorkspaceRecovery(item, cacheWorkspaceId));
|
|
121
|
+
const routeMaterials = cacheWorkspaceId === workspaceId ? selectedRouteCacheMaterials : [];
|
|
122
|
+
const byMaterialKey = new Map();
|
|
123
|
+
for (const material of [...routeMaterials, ...recoveryMaterials]) {
|
|
124
|
+
const key = `${String(material.requirementId || '')}\u0000${String(material.archiveEntry || '')}`;
|
|
125
|
+
if (!byMaterialKey.has(key)) byMaterialKey.set(key, material);
|
|
126
|
+
}
|
|
127
|
+
const carriedMaterials = [...byMaterialKey.values()];
|
|
128
|
+
if (!carriedMaterials.length) continue;
|
|
129
|
+
|
|
130
|
+
const sourceCacheArchive = oneFile(sourceBundle.files || [], candidateCache.archivePath);
|
|
131
|
+
if (!sourceCacheArchive) return blocked('cache-source-unresolved', [finding('error', 'portable.handoff-v2-phase1.cache.source-unresolved', 'Detached cache archive is unavailable in the qualified source carrier.', { workspaceId: cacheWorkspaceId, path: candidateCache.archivePath })]);
|
|
118
132
|
const sourceCacheInspection = inspectStoredWorkspaceArchive(packageFileBytes(sourceCacheArchive), { ownedBytes: true });
|
|
119
|
-
if (sourceCacheInspection.state !== 'qualified') return blocked('cache-source-invalid', [finding('error', 'portable.handoff-v2-phase1.cache.source-invalid', '
|
|
133
|
+
if (sourceCacheInspection.state !== 'qualified') return blocked('cache-source-invalid', [finding('error', 'portable.handoff-v2-phase1.cache.source-invalid', 'Detached cache archive did not independently qualify.', { workspaceId: cacheWorkspaceId, path: candidateCache.archivePath, findings: sourceCacheInspection.findings || [] })]);
|
|
134
|
+
|
|
120
135
|
const selectedEntries = [];
|
|
121
136
|
const materialBindings = [];
|
|
122
|
-
for (const material of
|
|
137
|
+
for (const material of carriedMaterials) {
|
|
123
138
|
const archiveEntry = String(material.archiveEntry || '');
|
|
124
139
|
const matches = sourceCacheInspection.entries.filter((entry) => String(entry.path || '') === archiveEntry);
|
|
125
|
-
if (matches.length !== 1) return blocked('cache-material-unresolved', [finding('error', 'portable.handoff-v2-phase1.cache.material-unresolved', '
|
|
140
|
+
if (matches.length !== 1) return blocked('cache-material-unresolved', [finding('error', 'portable.handoff-v2-phase1.cache.material-unresolved', 'Carried cache material must resolve exactly one source cache entry.', { workspaceId: cacheWorkspaceId, requirementId: String(material.requirementId || ''), referenceTarget: String(material.referenceTarget || ''), archiveEntry, count: matches.length })]);
|
|
126
141
|
selectedEntries.push({ path: archiveEntry, data: matches[0].data });
|
|
127
|
-
materialBindings.push(Object.freeze({
|
|
142
|
+
materialBindings.push(Object.freeze({
|
|
143
|
+
requirementId: String(material.requirementId || ''),
|
|
144
|
+
classification: String(material.classification || ''),
|
|
145
|
+
referenceTarget: String(material.referenceTarget || ''),
|
|
146
|
+
routeWorkspaceId: String(material.routeWorkspaceId || ''),
|
|
147
|
+
routePath: String(material.routePath || ''),
|
|
148
|
+
sourceRequirementId: String(material.sourceRequirementId || ''),
|
|
149
|
+
sourceWorkspaceId: String(material.sourceWorkspaceId || ''),
|
|
150
|
+
sourcePath: String(material.sourcePath || ''),
|
|
151
|
+
targetWorkspaceId: String(material.targetWorkspaceId || ''),
|
|
152
|
+
targetPath: String(material.targetPath || ''),
|
|
153
|
+
originalPath: String(material.originalPath || ''),
|
|
154
|
+
archiveEntry,
|
|
155
|
+
bytes: Number(material.bytes || matches[0].bytes || 0),
|
|
156
|
+
sha256: String(material.sha256 || matches[0].sha256 || '')
|
|
157
|
+
}));
|
|
128
158
|
}
|
|
129
|
-
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
159
|
+
|
|
160
|
+
const selectedWorkspaceCache = cacheWorkspaceId === workspaceId;
|
|
161
|
+
auxiliaryCacheOrdinal += selectedWorkspaceCache ? 0 : 1;
|
|
162
|
+
const cacheStem = selectedWorkspaceCache ? '001-5-cache' : `001-5-${auxiliaryCacheOrdinal}-${safeToken(cacheWorkspaceId)}-recovery-cache`;
|
|
163
|
+
const builtArchivePath = `${cacheStem}.zip`;
|
|
164
|
+
const builtArtifactPath = `${cacheStem}.trace.md`;
|
|
165
|
+
const builtArchiveFile = finalizeFile({
|
|
166
|
+
path: builtArchivePath,
|
|
133
167
|
kind: 'handoff-material-cache',
|
|
134
168
|
logicalKind: 'recipient-v2-phase1-workspace-dependency-cache',
|
|
135
169
|
mediaType: 'application/zip',
|
|
136
170
|
data: exportFileMapZipUint8Array(selectedEntries, 'portable.handoff-v2-phase1.cache.path.invalid'),
|
|
137
|
-
boundary: 'Exact
|
|
171
|
+
boundary: 'Exact route-scoped detached dependency and/or bounded Workspace Parent-recovery bytes owned by one visible cache External Payload artifact. Cache location and compatibility JSON are not semantic authority.'
|
|
138
172
|
});
|
|
139
|
-
|
|
140
|
-
path:
|
|
173
|
+
const builtArtifact = finalizeFile({
|
|
174
|
+
path: builtArtifactPath,
|
|
141
175
|
kind: 'tiinex-external-payload-artifact',
|
|
142
176
|
logicalKind: 'recipient-v2-phase1-workspace-dependency-cache-reference',
|
|
143
177
|
mediaType: 'text/markdown',
|
|
144
178
|
content: renderRecipientV2ExternalPayload({
|
|
145
179
|
artifactFirst: true,
|
|
146
180
|
createdAt,
|
|
147
|
-
workspaceId,
|
|
148
|
-
title: `Workspace Dependency Cache — ${
|
|
149
|
-
summary: 'Artifact-first
|
|
150
|
-
label: `${
|
|
181
|
+
workspaceId: cacheWorkspaceId,
|
|
182
|
+
title: `Workspace Dependency Cache — ${cacheWorkspaceId}`,
|
|
183
|
+
summary: 'Artifact-first exact detached dependency and bounded Parent-recovery bytes absent from the qualified Workspace payload.',
|
|
184
|
+
label: `${cacheWorkspaceId} Handoff dependency and recovery cache`,
|
|
151
185
|
kind: 'zip export',
|
|
152
186
|
role: PHASE1_CACHE_ROLE,
|
|
153
|
-
location:
|
|
154
|
-
bytes:
|
|
155
|
-
sha256:
|
|
187
|
+
location: builtArchivePath,
|
|
188
|
+
bytes: builtArchiveFile.bytes,
|
|
189
|
+
sha256: builtArchiveFile.sha256,
|
|
156
190
|
materials: materialBindings
|
|
157
191
|
})
|
|
158
192
|
});
|
|
159
|
-
|
|
193
|
+
const projection = Object.freeze({ workspaceId: cacheWorkspaceId, artifactPath: builtArtifactPath, archivePath: builtArchivePath, materials: Object.freeze(materialBindings) });
|
|
194
|
+
const built = Object.freeze({ workspaceId: cacheWorkspaceId, artifact: builtArtifact, archiveFile: builtArchiveFile, projection, sourceMaterials: Object.freeze(carriedMaterials) });
|
|
195
|
+
cacheBuilds.push(built);
|
|
196
|
+
if (selectedWorkspaceCache) {
|
|
197
|
+
cacheArtifact = builtArtifact;
|
|
198
|
+
selectedCacheMaterials = carriedMaterials;
|
|
199
|
+
}
|
|
160
200
|
}
|
|
161
201
|
|
|
162
202
|
const endpointRoleArtifacts = [];
|
|
@@ -257,7 +297,7 @@ export function buildRecipientV2ArtifactFirstPhase1Specimen(input = {}) {
|
|
|
257
297
|
destinations: [
|
|
258
298
|
...(bootstrapArtifact ? [{ label: 'Portable Tooling bootstrap payload', target: bootstrapArtifactPath }] : []),
|
|
259
299
|
...workspaceCarriers.map((item) => ({ label: `${item.workspaceId} Workspace payload`, target: item.payloadArtifactPath })),
|
|
260
|
-
...(
|
|
300
|
+
...cacheBuilds.map((item) => ({ label: `Workspace dependency/recovery cache ${item.workspaceId}`, target: item.artifact.path })),
|
|
261
301
|
...workspaceCarriers.map((item) => ({ label: `${item.workspaceId} Workspace material representation`, target: item.relationArtifactPath })),
|
|
262
302
|
...endpointRoleArtifacts.map((item) => ({ label: 'Endpoint Role pointer', target: item.path })),
|
|
263
303
|
...participantRoleArtifacts.map((item) => ({ label: 'Participant Role pointer', target: item.path })),
|
|
@@ -267,7 +307,8 @@ export function buildRecipientV2ArtifactFirstPhase1Specimen(input = {}) {
|
|
|
267
307
|
});
|
|
268
308
|
|
|
269
309
|
const workspaceFiles = workspaceCarriers.flatMap((item) => [item.payloadArtifact, item.archiveFile, item.relationArtifact]);
|
|
270
|
-
const
|
|
310
|
+
const cacheFiles = cacheBuilds.flatMap((item) => [item.artifact, item.archiveFile]);
|
|
311
|
+
const semanticFiles = Object.freeze([ingressPointer, ...(bootstrapArtifact && bootstrapArchiveFile ? [bootstrapArtifact, bootstrapArchiveFile] : []), ...workspaceFiles, ...cacheFiles, ...endpointRoleArtifacts, ...participantRoleArtifacts, ...routePointers]);
|
|
271
312
|
const factsByPath = deriveRecipientV2ArtifactFirstPhase1Facts(semanticFiles);
|
|
272
313
|
const manifestInputs = semanticFiles.map((file) => {
|
|
273
314
|
const facts = factsByPath.get(String(file.path || '')) || null;
|
|
@@ -293,7 +334,7 @@ export function buildRecipientV2ArtifactFirstPhase1Specimen(input = {}) {
|
|
|
293
334
|
sourceWorkspaceTargetInnerPath: String(item.workspace.sourceWorkspaceTargetInnerPath || ''),
|
|
294
335
|
sourceWorkspaceTargetSha256: String(item.workspace.sourceWorkspaceTargetSha256 || '')
|
|
295
336
|
}))),
|
|
296
|
-
caches: Object.freeze(
|
|
337
|
+
caches: Object.freeze(cacheBuilds.map((item) => item.projection)),
|
|
297
338
|
endpointRoles: Object.freeze(endpointRoleProjections),
|
|
298
339
|
participantRoles: Object.freeze(participantRoleProjections),
|
|
299
340
|
routes: Object.freeze(routeTopology),
|
|
@@ -124,7 +124,7 @@ export function phase1CacheParentCandidates(cacheQualifications = []) {
|
|
|
124
124
|
const entry = matches[0];
|
|
125
125
|
const data = packageFileBytes({ data: entry.data });
|
|
126
126
|
if (Number(material.bytes || 0) !== data.byteLength || String(material.sha256 || '') !== sha256Hex(data)) continue;
|
|
127
|
-
candidates.push(Object.freeze({ workspaceId: String(cache.workspaceId || ''), workspaceRelativePath: normalizeRoutePath(material.referenceTarget || ''), archiveEntry: String(material.archiveEntry || ''), bytes: data.byteLength, sha256: sha256Hex(data), data }));
|
|
127
|
+
candidates.push(Object.freeze({ workspaceId: String(material.targetWorkspaceId || cache.workspaceId || ''), workspaceRelativePath: normalizeRoutePath(material.originalPath || material.targetPath || material.referenceTarget || ''), archiveEntry: String(material.archiveEntry || ''), bytes: data.byteLength, sha256: sha256Hex(data), data }));
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
130
|
return Object.freeze(candidates);
|
|
@@ -34,7 +34,15 @@ export function inspectRecipientV2ArtifactFirstPhase1Specimen(bundle = {}) {
|
|
|
34
34
|
if (!routes.length) findings.push(finding('error', 'portable.handoff-v2-phase1.route.count', 'Artifact-first specimen requires at least one visible Handoff route Pointer.', { count: routes.length }));
|
|
35
35
|
if (!workspacePayloads.length) findings.push(finding('error', 'portable.handoff-v2-phase1.payload.workspace-count', 'Artifact-first full-source carriage requires at least one Workspace External Payload artifact.', { count: workspacePayloads.length }));
|
|
36
36
|
if (bootstrapPayloads.length > 1) findings.push(finding('error', 'portable.handoff-v2-phase1.bootstrap.count', 'A carried portable Tooling bootstrap must have exactly one owning External Payload artifact.', { count: bootstrapPayloads.length }));
|
|
37
|
-
|
|
37
|
+
const cachePayloadsByWorkspace = new Map();
|
|
38
|
+
for (const item of cachePayloads) {
|
|
39
|
+
const cacheWorkspaceId = String(item.parsed?.workspaceId || '');
|
|
40
|
+
if (!cacheWorkspaceId) findings.push(finding('error', 'portable.handoff-v2-phase1.cache.workspace-id-missing', 'Every dependency/recovery cache External Payload must visibly declare its owning Workspace Id.', { path: item.path }));
|
|
41
|
+
const list = cachePayloadsByWorkspace.get(cacheWorkspaceId) || [];
|
|
42
|
+
list.push(item);
|
|
43
|
+
cachePayloadsByWorkspace.set(cacheWorkspaceId, list);
|
|
44
|
+
}
|
|
45
|
+
for (const [cacheWorkspaceId, items] of cachePayloadsByWorkspace) if (cacheWorkspaceId && items.length > 1) findings.push(finding('error', 'portable.handoff-v2-phase1.cache.count', 'Each carried Workspace may expose at most one dependency/recovery cache External Payload.', { workspaceId: cacheWorkspaceId, count: items.length }));
|
|
38
46
|
if (unknownPayloads.length) findings.push(finding('error', 'portable.handoff-v2-phase1.payload.unclassified', 'Phase 1 specimen contains an External Payload artifact outside the bounded Workspace/bootstrap/cache ownership subset.', { count: unknownPayloads.length }));
|
|
39
47
|
if (!relations.length) findings.push(finding('error', 'portable.handoff-v2-phase1.relation.count', 'Artifact-first full-source carriage requires a typed material-representation Relation for every carried Workspace.', { count: relations.length }));
|
|
40
48
|
if (markdownFiles.some((file) => factsByPath.get(String(file.path || ''))?.role === 'package-root')) findings.push(finding('error', 'portable.handoff-v2-phase1.package-root.present', 'Phase 1 specimen must not use a package-root Pointer as receiver semantic authority.'));
|
|
@@ -75,7 +75,7 @@ export function deriveRecipientV2ArtifactFirstPhase1Facts(files = []) {
|
|
|
75
75
|
const matches = archive?.state === 'qualified' ? archive.entries.filter((entry) => String(entry.path || '') === String(material.archiveEntry || '')) : [];
|
|
76
76
|
const entry = matches.length === 1 ? matches[0] : null;
|
|
77
77
|
const bytes = entry ? packageFileBytes({ data: entry.data }) : new Uint8Array();
|
|
78
|
-
return Object.freeze({ requirementId: material.requirementId, classification: material.classification, referenceTarget: material.referenceTarget, routeWorkspaceId: parsed.workspaceId, routePath: '', sourceRequirementId: material.requirementId, originalPath: '', archiveEntry: material.archiveEntry, bytes: bytes.byteLength, sha256: bytes.byteLength ? sha256Hex(bytes) : '' });
|
|
78
|
+
return Object.freeze({ requirementId: material.requirementId, classification: material.classification, referenceTarget: material.referenceTarget, routeWorkspaceId: material.routeWorkspaceId || parsed.workspaceId, routePath: material.routePath || '', sourceRequirementId: material.sourceRequirementId || material.requirementId, sourceWorkspaceId: material.sourceWorkspaceId || '', sourcePath: material.sourcePath || '', targetWorkspaceId: material.targetWorkspaceId || '', targetPath: material.targetPath || '', originalPath: material.originalPath || '', archiveEntry: material.archiveEntry, bytes: bytes.byteLength, sha256: bytes.byteLength ? sha256Hex(bytes) : '' });
|
|
79
79
|
});
|
|
80
80
|
facts.set(path, recipientV2TransportFacts('workspace-dependency-cache', { workspaceId: parsed.workspaceId, archivePath: parsed.location || '', archiveBytes: Number(parsed.bytes || 0), archiveSha256: parsed.integrityValue || '', materials: Object.freeze(materials) }));
|
|
81
81
|
}
|
|
@@ -115,16 +115,36 @@ function parsePhase1MaterialBindings(markdown = '') {
|
|
|
115
115
|
const requirement = line.match(/^\s*-\s+Requirement Id:\s*(.*?)\s*$/i);
|
|
116
116
|
if (requirement) {
|
|
117
117
|
if (current) out.push(Object.freeze(current));
|
|
118
|
-
current = { requirementId: String(requirement[1] || '').trim(), classification: '', referenceTarget: '', archiveEntry: '' };
|
|
118
|
+
current = { requirementId: String(requirement[1] || '').trim(), classification: '', referenceTarget: '', routeWorkspaceId: '', routePath: '', sourceRequirementId: '', sourceWorkspaceId: '', sourcePath: '', targetWorkspaceId: '', targetPath: '', originalPath: '', archiveEntry: '', bytes: null, sha256: '' };
|
|
119
119
|
continue;
|
|
120
120
|
}
|
|
121
121
|
if (!current) continue;
|
|
122
122
|
const classification = line.match(/^\s+-\s+Classification:\s*(.*?)\s*$/i);
|
|
123
123
|
const reference = line.match(/^\s+-\s+Material Reference:\s*(.*?)\s*$/i);
|
|
124
124
|
const archiveEntry = line.match(/^\s+-\s+Archive Entry:\s*(.*?)\s*$/i);
|
|
125
|
+
const routeWorkspaceId = line.match(/^\s+-\s+Route Workspace Id:\s*(.*?)\s*$/i);
|
|
126
|
+
const routePath = line.match(/^\s+-\s+Route Path:\s*(.*?)\s*$/i);
|
|
127
|
+
const sourceRequirementId = line.match(/^\s+-\s+Source Requirement Id:\s*(.*?)\s*$/i);
|
|
128
|
+
const sourceWorkspaceId = line.match(/^\s+-\s+Source Workspace Id:\s*(.*?)\s*$/i);
|
|
129
|
+
const sourcePath = line.match(/^\s+-\s+Source Path:\s*(.*?)\s*$/i);
|
|
130
|
+
const targetWorkspaceId = line.match(/^\s+-\s+Target Workspace Id:\s*(.*?)\s*$/i);
|
|
131
|
+
const targetPath = line.match(/^\s+-\s+Target Path:\s*(.*?)\s*$/i);
|
|
132
|
+
const originalPath = line.match(/^\s+-\s+Original Path:\s*(.*?)\s*$/i);
|
|
133
|
+
const bytes = line.match(/^\s+-\s+Byte Size:\s*(.*?)\s*$/i);
|
|
134
|
+
const sha256 = line.match(/^\s+-\s+SHA256:\s*(.*?)\s*$/i);
|
|
125
135
|
if (classification) current.classification = String(classification[1] || '').trim();
|
|
126
136
|
else if (reference) current.referenceTarget = String(reference[1] || '').trim();
|
|
127
137
|
else if (archiveEntry) current.archiveEntry = String(archiveEntry[1] || '').trim();
|
|
138
|
+
else if (routeWorkspaceId) current.routeWorkspaceId = String(routeWorkspaceId[1] || '').trim();
|
|
139
|
+
else if (routePath) current.routePath = String(routePath[1] || '').trim();
|
|
140
|
+
else if (sourceRequirementId) current.sourceRequirementId = String(sourceRequirementId[1] || '').trim();
|
|
141
|
+
else if (sourceWorkspaceId) current.sourceWorkspaceId = String(sourceWorkspaceId[1] || '').trim();
|
|
142
|
+
else if (sourcePath) current.sourcePath = String(sourcePath[1] || '').trim();
|
|
143
|
+
else if (targetWorkspaceId) current.targetWorkspaceId = String(targetWorkspaceId[1] || '').trim();
|
|
144
|
+
else if (targetPath) current.targetPath = String(targetPath[1] || '').trim();
|
|
145
|
+
else if (originalPath) current.originalPath = String(originalPath[1] || '').trim();
|
|
146
|
+
else if (bytes) current.bytes = Number(bytes[1] || 0);
|
|
147
|
+
else if (sha256) current.sha256 = String(sha256[1] || '').trim();
|
|
128
148
|
}
|
|
129
149
|
if (current) out.push(Object.freeze(current));
|
|
130
150
|
return Object.freeze(out);
|
|
@@ -183,7 +203,6 @@ export function qualifyPhase1CachePayload(cachePayload = null, semanticFiles = [
|
|
|
183
203
|
if (archive.state !== 'qualified') localFindings.push(finding('error', 'portable.handoff-v2-phase1.cache.archive-invalid', 'Selected-route cache payload is not a qualified deterministic stored-ZIP representation.', { path: location, findings: archive.findings || [] }));
|
|
184
204
|
}
|
|
185
205
|
const seenRequirements = new Set();
|
|
186
|
-
const seenReferences = new Set();
|
|
187
206
|
const seenEntries = new Set();
|
|
188
207
|
for (const material of cachePayload.parsed?.materials || []) {
|
|
189
208
|
const requirementId = String(material.requirementId || '');
|
|
@@ -193,13 +212,16 @@ export function qualifyPhase1CachePayload(cachePayload = null, semanticFiles = [
|
|
|
193
212
|
localFindings.push(finding('error', 'portable.handoff-v2-phase1.cache.material-binding-incomplete', 'Every selected-route cache material binding must visibly declare requirement id, material reference, and archive entry.'));
|
|
194
213
|
continue;
|
|
195
214
|
}
|
|
196
|
-
if (seenRequirements.has(requirementId) ||
|
|
197
|
-
seenRequirements.add(requirementId);
|
|
215
|
+
if (seenRequirements.has(requirementId) || seenEntries.has(archiveEntry)) localFindings.push(finding('error', 'portable.handoff-v2-phase1.cache.material-binding-ambiguous', 'Cache material bindings must be one-to-one by requirement id and archive entry; multiple requirements may legitimately share the same Parent reference.', { requirementId, referenceTarget, archiveEntry }));
|
|
216
|
+
seenRequirements.add(requirementId); seenEntries.add(archiveEntry);
|
|
198
217
|
const entryMatches = archive?.state === 'qualified' ? archive.entries.filter((entry) => String(entry.path || '') === archiveEntry) : [];
|
|
199
218
|
if (archive?.state === 'qualified' && entryMatches.length !== 1) localFindings.push(finding('error', 'portable.handoff-v2-phase1.cache.material-entry-unresolved', 'Selected-route cache material binding must resolve exactly one exact cache entry.', { requirementId, referenceTarget, archiveEntry, count: entryMatches.length }));
|
|
200
219
|
const entry = entryMatches.length === 1 ? entryMatches[0] : null;
|
|
201
220
|
const entryBytes = entry ? packageFileBytes({ data: entry.data }) : new Uint8Array();
|
|
202
|
-
|
|
221
|
+
const entrySha256 = entryBytes.byteLength ? sha256Hex(entryBytes) : '';
|
|
222
|
+
if (material.bytes !== null && material.bytes !== undefined && Number(material.bytes) !== entryBytes.byteLength) localFindings.push(finding('error', 'portable.handoff-v2-phase1.cache.material-byte-size-mismatch', 'Visible cache material Byte Size must equal the exact owned archive entry bytes.', { requirementId, archiveEntry, declaredBytes: Number(material.bytes), actualBytes: entryBytes.byteLength }));
|
|
223
|
+
if (material.sha256 && String(material.sha256) !== entrySha256) localFindings.push(finding('error', 'portable.handoff-v2-phase1.cache.material-sha256-mismatch', 'Visible cache material SHA256 must equal the exact owned archive entry digest.', { requirementId, archiveEntry }));
|
|
224
|
+
materialQualifications.push(deepFreeze({ requirementId, classification: String(material.classification || ''), referenceTarget, routeWorkspaceId: String(material.routeWorkspaceId || ''), routePath: String(material.routePath || ''), sourceRequirementId: String(material.sourceRequirementId || ''), sourceWorkspaceId: String(material.sourceWorkspaceId || ''), sourcePath: String(material.sourcePath || ''), targetWorkspaceId: String(material.targetWorkspaceId || ''), targetPath: String(material.targetPath || ''), originalPath: String(material.originalPath || ''), archiveEntry, bytes: entryBytes.byteLength, sha256: entrySha256 }));
|
|
203
225
|
}
|
|
204
226
|
if (!(cachePayload.parsed?.materials || []).length) localFindings.push(finding('error', 'portable.handoff-v2-phase1.cache.material-bindings-missing', 'Selected-route cache External Payload must visibly bind every owned detached material to one archive entry.'));
|
|
205
227
|
findings.push(...localFindings);
|
|
@@ -20,6 +20,14 @@ export function cacheMaterialBelongsToRoute(material = {}, workspaceId = '', rou
|
|
|
20
20
|
return String(material.routeWorkspaceId || '') === String(workspaceId || '') && normalizeRoutePath(material.routePath || '') === normalizeRoutePath(routePath || '');
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
export function cacheMaterialIsBoundedWorkspaceRecovery(material = {}, workspaceId = '') {
|
|
24
|
+
const id = String(workspaceId || '').trim();
|
|
25
|
+
return Boolean(id
|
|
26
|
+
&& String(material.classification || '') === 'parent-boundary'
|
|
27
|
+
&& String(material.routeWorkspaceId || '') === id
|
|
28
|
+
&& String(material.sourceRequirementId || '').startsWith(`bounded-workspace:${id}:`));
|
|
29
|
+
}
|
|
30
|
+
|
|
23
31
|
export function selectOne(items = [], selector = '', label = 'item', findings = [], selectorFn = (item) => String(item.workspaceId || '')) { const candidates = selector ? items.filter((item) => selectorFn(item) === String(selector)) : items; if (candidates.length !== 1) { findings.push(finding('error', `portable.handoff-v2-phase1.${label}.selection`, `Phase 1 specimen requires exactly one selected ${label}.`, { selector: String(selector || ''), count: candidates.length })); return null; } return candidates[0]; }
|
|
24
32
|
|
|
25
33
|
export function oneFile(files = [], path = '') { const matches = files.filter((file) => String(file.path || '') === String(path || '')); return matches.length === 1 ? matches[0] : null; }
|
|
@@ -2,17 +2,22 @@ export function recipientColdProjection(carrier = {}, readPath = '001-1-READ-BEF
|
|
|
2
2
|
const routes = Object.freeze((carrier.routes || []).map((route) => Object.freeze({ id: String(route.id || ''), state: String(route.state || ''), workspaceId: String(route.workspaceId || ''), workspaceRelativeHandoffPath: String(route.workspaceRelativePath || ''), packagePath: String(route.packagePath || ''), sha256: String(route.sha256 || ''), from: String(route.parties?.from || ''), to: String(route.parties?.to || '') })));
|
|
3
3
|
const workspaces = Object.freeze((carrier.workspaces || []).map((workspace) => Object.freeze({ id: String(workspace.id || ''), title: String(workspace.title || workspace.id || ''), slug: String(workspace.slug || ''), qualification: String(workspace.qualification || '') })));
|
|
4
4
|
const qualified = routes.filter((route) => route.state === 'qualified');
|
|
5
|
-
const
|
|
5
|
+
const mode = String(carrier.mode || '');
|
|
6
|
+
const workspaceMode = mode === 'workspace';
|
|
7
|
+
const bootstrapMode = mode === 'bootstrap';
|
|
8
|
+
const routeMode = !workspaceMode && !bootstrapMode;
|
|
6
9
|
return deepFreeze({
|
|
7
10
|
schema: 'tiinex.portable.handoff-cold-consumer-projection.v1', version: 1,
|
|
8
11
|
status: carrier.status === 'ready' ? 'ready' : 'blocked',
|
|
9
12
|
controls: Object.freeze({ start: readPath, carrier: 'visible-qualified-artifacts', closure: 'visible-qualified-artifacts-plus-exact-payload-bytes', fileMap: 'not-exposed-in-v2', manifest: 'not-exposed-in-v2' }),
|
|
10
|
-
preferredPath: Object.freeze(
|
|
11
|
-
? { ingressKind: '
|
|
12
|
-
:
|
|
13
|
+
preferredPath: Object.freeze(bootstrapMode
|
|
14
|
+
? { ingressKind: 'bootstrap-only-package', firstSemanticOperation: 'orient-handoff-package', groundingOperation: 'none', qualificationOperation: 'bootstrap-qualification-after-orientation', minimalHostBootstrapActions: 1, nativeFallback: 'explicit-and-justified-only', providerSpecificSemanticAuthority: false }
|
|
15
|
+
: workspaceMode
|
|
16
|
+
? { ingressKind: 'pointerless-workspace-package', firstSemanticOperation: 'orient-handoff-package', groundingOperation: 'none', qualificationOperation: 'workspace-qualification-after-orientation', minimalHostBootstrapActions: 1, nativeFallback: 'explicit-and-justified-only', providerSpecificSemanticAuthority: false }
|
|
17
|
+
: { ingressKind: 'routed-handoff-package', firstSemanticOperation: 'orient-handoff-package', groundingOperation: 'ground-cold-consumer', qualificationOperation: 'qualify-cold-start', minimalHostBootstrapActions: 1, nativeFallback: 'explicit-and-justified-only', providerSpecificSemanticAuthority: false }),
|
|
13
18
|
workspaces, routes,
|
|
14
|
-
selection: Object.freeze({ policy: String(carrier.selection?.policy || ''), qualifiedRouteCount: qualified.length, implicitRouteId:
|
|
15
|
-
authority: Object.freeze({ semanticAuthority: 'none', packageTruthRequired: true, routeBindingAuthority:
|
|
19
|
+
selection: Object.freeze({ policy: String(carrier.selection?.policy || ''), qualifiedRouteCount: qualified.length, implicitRouteId: routeMode && qualified.length === 1 ? qualified[0].id : '' }),
|
|
20
|
+
authority: Object.freeze({ semanticAuthority: 'none', packageTruthRequired: true, routeBindingAuthority: routeMode ? 'qualified-visible-artifact-plus-exact-payload-byte-truth-only' : 'none', recipientAuthority: routeMode ? 'qualified-selected-handoff-to-endpoint-only' : 'none', holderAuthority: 'none', workAuthority: 'none' })
|
|
16
21
|
});
|
|
17
22
|
}
|
|
18
23
|
|
|
@@ -26,3 +26,15 @@ export function recipientV2WorkspaceEntryCurrentRead() {
|
|
|
26
26
|
Object.freeze({ label: 'Fallback', value: 'fail closed when Tooling cannot qualify the package role, exact Workspace bindings, bootstrap, or all-none Route Discovery profile; native package archaeology is not a preferred-path substitute' })
|
|
27
27
|
]);
|
|
28
28
|
}
|
|
29
|
+
|
|
30
|
+
export function recipientV2BootstrapEntryCurrentRead() {
|
|
31
|
+
return Object.freeze([
|
|
32
|
+
Object.freeze({ label: 'Bootstrap Exception', value: 'before Tiinex Tooling is available, use host capabilities only to read this entry/bootstrap node and extract the declared bootstrap ZIP into a writable runtime location; do not perform package archaeology' }),
|
|
33
|
+
Object.freeze({ label: 'Tooling Entrypoint', value: '`runtime/tools/tiinex-portable.mjs` relative to the chosen bootstrap extraction root; use this exact path and do not search/list the extracted runtime to discover it' }),
|
|
34
|
+
Object.freeze({ label: 'First Tooling Invocation', value: '`node <bootstrap-root>/runtime/tools/tiinex-portable.mjs orient-handoff-package <untouched-bootstrap-package.zip>`' }),
|
|
35
|
+
Object.freeze({ label: 'Carrier Mode', value: 'bootstrap-only carrier; no Workspace/material binding, Handoff route, recipient, Role holder, current work, transfer, acceptance, completion, participation, or grounded-to-act authority is declared or inferred' }),
|
|
36
|
+
Object.freeze({ label: 'Route Selection', value: 'none; the canonical package role requires both source-material binding sections and all route-specific projection fields to declare absence' }),
|
|
37
|
+
Object.freeze({ label: 'Next Action', value: 'after Tooling orientation, continue only with separately supplied qualified semantic material or an explicit later Handoff; this carrier itself supplies no project/source authority' }),
|
|
38
|
+
Object.freeze({ label: 'Fallback', value: 'fail closed when Tooling cannot qualify the package role, Start, bootstrap, or zero-material/zero-route profile; native package archaeology is not a preferred-path substitute' })
|
|
39
|
+
]);
|
|
40
|
+
}
|
|
@@ -2,6 +2,21 @@ import { projectHandoffCarrierOutputFromPackage, projectHandoffHumanOutput } fro
|
|
|
2
2
|
import { inspectRecipientFacingV2Topology } from './recipientV2.inspect.js';
|
|
3
3
|
import { RECIPIENT_V2_READ_PATH } from './recipientV2.topology.js';
|
|
4
4
|
|
|
5
|
+
|
|
6
|
+
export function recipientV2GenericInvocation(inspection = {}) {
|
|
7
|
+
const startPath = String(inspection.packageContract?.startPath || RECIPIENT_V2_READ_PATH);
|
|
8
|
+
if (!startPath || inspection.status !== 'valid') return '';
|
|
9
|
+
return [
|
|
10
|
+
'Tiinex package attached.',
|
|
11
|
+
'',
|
|
12
|
+
'Cold start: read Start directly; do not list or extract this package.',
|
|
13
|
+
'',
|
|
14
|
+
'Start:',
|
|
15
|
+
startPath,
|
|
16
|
+
''
|
|
17
|
+
].join('\n');
|
|
18
|
+
}
|
|
19
|
+
|
|
5
20
|
export function recipientV2StandardInvocation(humanOutput = {}, inspection = {}) {
|
|
6
21
|
const route = humanOutput.selectedRoute || {};
|
|
7
22
|
return recipientV2InvocationForRoute(route, inspection);
|
|
@@ -16,7 +31,7 @@ function recipientV2InvocationForRoute(route = {}, inspection = {}) {
|
|
|
16
31
|
'Cold start: read Start directly; do not list or extract this package.',
|
|
17
32
|
'',
|
|
18
33
|
'Start:',
|
|
19
|
-
RECIPIENT_V2_READ_PATH,
|
|
34
|
+
String(inspection.packageContract?.startPath || RECIPIENT_V2_READ_PATH),
|
|
20
35
|
'Continue from (do not read native; pass to Tiinex after bootstrap):',
|
|
21
36
|
String(routeMeta.pointerPath),
|
|
22
37
|
''
|
|
@@ -25,8 +40,9 @@ function recipientV2InvocationForRoute(route = {}, inspection = {}) {
|
|
|
25
40
|
|
|
26
41
|
export function projectRecipientV2HumanOutput(humanOutput = {}, inspection = {}) {
|
|
27
42
|
if (humanOutput.status !== 'ready' && humanOutput.status !== 'selection-required') return humanOutput;
|
|
28
|
-
const
|
|
29
|
-
|
|
43
|
+
const routed = String(inspection.packageContract?.packageRole || '') === 'recipient-facing-handoff-carrier';
|
|
44
|
+
const invocation = humanOutput.primary ? (routed ? recipientV2StandardInvocation(humanOutput, inspection) : recipientV2GenericInvocation(inspection)) : '';
|
|
45
|
+
if (humanOutput.primary && !invocation) return Object.freeze({ ...humanOutput, status: 'blocked', findings: Object.freeze([...(humanOutput.findings || []), Object.freeze({ severity: 'error', code: routed ? 'portable.handoff-v2-human-output.route-pointer.unresolved' : 'portable.handoff-v2-human-output.start.unresolved', message: routed ? 'Recipient-v2 human output requires one exact package-local Handoff route pointer for the selected semantic Handoff.' : 'Recipient-v2 generic human output requires one exact qualified package-local Start artifact.' })]) });
|
|
30
46
|
const sharedRoutes = (humanOutput.sharedRouting?.routes || []).map((item) => {
|
|
31
47
|
const route = (inspection.carrierProjection?.routes || []).find((candidate) => String(candidate.id || '') === String(item.routeId || ''))
|
|
32
48
|
|| { id: item.routeId, workspaceId: item.workspaceId, workspaceRelativePath: item.workspaceRelativeHandoffPath };
|
|
@@ -35,12 +51,15 @@ export function projectRecipientV2HumanOutput(humanOutput = {}, inspection = {})
|
|
|
35
51
|
});
|
|
36
52
|
if (humanOutput.sharedRouting && sharedRoutes.some((item) => !item)) return Object.freeze({ ...humanOutput, status: 'blocked', findings: Object.freeze([...(humanOutput.findings || []), Object.freeze({ severity: 'error', code: 'portable.handoff-v2-human-output.shared-route-pointer.unresolved', message: 'Recipient-v2 shared human output requires one exact package-local Handoff route pointer for every qualified route.' })]) });
|
|
37
53
|
const sharedRouting = humanOutput.sharedRouting ? Object.freeze({ ...humanOutput.sharedRouting, routes: Object.freeze(sharedRoutes) }) : null;
|
|
54
|
+
const recipientLabel = routed ? String(humanOutput.selectedRoute?.parties?.to || '').trim() : '';
|
|
55
|
+
const presentation = Object.freeze({ ...(humanOutput.presentation || {}), recipientLabel, recipientProjectionAuthority: routed ? 'qualified-selected-handoff-to-endpoint-only' : 'none' });
|
|
38
56
|
return Object.freeze({
|
|
39
57
|
...humanOutput,
|
|
58
|
+
presentation,
|
|
40
59
|
normalInlineRouting: humanOutput.normalInlineRouting ? Object.freeze({ ...humanOutput.normalInlineRouting, content: invocation }) : null,
|
|
41
60
|
sharedRouting,
|
|
42
61
|
fallbackTransportText: humanOutput.fallbackTransportText ? Object.freeze({ ...humanOutput.fallbackTransportText, content: invocation }) : null,
|
|
43
|
-
boundary: `${String(humanOutput.boundary || '')} Recipient-v2 host-layer
|
|
62
|
+
boundary: `${String(humanOutput.boundary || '')} Recipient-v2 host-layer transport text is a deterministic projection only: every qualified package may expose its exact Start address; Continue-from and recipient-specific projection exist only for an exact qualified selected Handoff route. Workspace/material/Role presence creates no recipient, holder, participation, delegation, or work authority.`
|
|
44
63
|
});
|
|
45
64
|
}
|
|
46
65
|
|
|
@@ -48,11 +67,34 @@ export function projectPortableHandoffCarrierOutputFromPackage(input = {}) {
|
|
|
48
67
|
const bundle = input.bundle || input;
|
|
49
68
|
const inspection = inspectRecipientFacingV2Topology(bundle);
|
|
50
69
|
if (inspection.detected !== true) return projectHandoffCarrierOutputFromPackage(input);
|
|
51
|
-
const
|
|
70
|
+
const routed = String(inspection.packageContract?.packageRole || '') === 'recipient-facing-handoff-carrier';
|
|
71
|
+
const baseHumanOutput = routed
|
|
72
|
+
? projectHandoffHumanOutput({ projection: inspection.carrierProjection || {}, route: input.route || input.routePath || input.routeId || '', collisionInstance: input.collisionInstance || input.instance || 1 })
|
|
73
|
+
: genericPackageHumanOutput(inspection, input);
|
|
74
|
+
const humanOutput = projectRecipientV2HumanOutput(baseHumanOutput, inspection);
|
|
52
75
|
const findings = Object.freeze([...(inspection.findings || []), ...(humanOutput.findings || [])]);
|
|
53
76
|
const status = inspection.status === 'valid' && humanOutput.status === 'ready' ? 'ready' : humanOutput.status === 'selection-required' ? 'selection-required' : 'blocked';
|
|
54
77
|
const carrierInspection = boundedRecipientV2Inspection(inspection);
|
|
55
|
-
return deepFreeze({ schema: 'tiinex.portable.handoff-carrier-output-projection.v1', status, carrierInspection, humanOutput, findings, boundary: 'Read-only regeneration of recipient-v2 carrier filename and minimal Start/Continue-from transport address from package-qualified route truth.
|
|
78
|
+
return deepFreeze({ schema: 'tiinex.portable.handoff-carrier-output-projection.v1', status, carrierInspection, humanOutput, findings, boundary: 'Read-only regeneration of recipient-v2 carrier filename and minimal Start/Continue-from transport address from package-qualified role and route truth. Route-less package roles expose generic Start transport only; package byte/provider internals are intentionally omitted from the human-output projection.' });
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function genericPackageHumanOutput(inspection = {}, input = {}) {
|
|
82
|
+
const projection = inspection.carrierProjection || {};
|
|
83
|
+
const mode = String(projection.mode || '');
|
|
84
|
+
const ready = inspection.status === 'valid' && projection.status === 'ready' && ['workspace', 'bootstrap'].includes(mode) && (projection.routes || []).length === 0;
|
|
85
|
+
const dimension = String(projection.lineage?.dimension || '001');
|
|
86
|
+
const filename = String(input.filename || input.projectedFilename || `tiinex-${dimension}.handoff-package.zip`);
|
|
87
|
+
return Object.freeze({
|
|
88
|
+
schema: 'tiinex.portable.handoff-human-output.v1', status: ready ? 'ready' : 'blocked',
|
|
89
|
+
primary: ready ? Object.freeze({ kind: `${mode}-package`, filename, dimension, parentDimension: String(projection.lineage?.parentDimension || ''), checkpointKind: String(projection.lineage?.checkpointKind || ''), routeId: '', workspaceId: '', workspaceRelativeHandoffPath: '', collisionInstance: 1, singleHumanTransportChoice: true }) : null,
|
|
90
|
+
normalInlineRouting: ready ? Object.freeze({ kind: 'transport-text', content: '', normalEmission: true, requiredForHumanCompletion: true, placement: 'adjacent-to-primary', authority: 'none' }) : null,
|
|
91
|
+
sharedRouting: null,
|
|
92
|
+
presentation: Object.freeze({ kind: mode === 'bootstrap' ? 'bootstrap-only-carrier' : 'pointerless-workspace-carrier', label: mode === 'bootstrap' ? 'Bootstrap carrier' : 'Workspace carrier', authority: 'none', recipientLabel: '', recipientProjectionAuthority: 'none' }),
|
|
93
|
+
normalEmissionBoundary: Object.freeze({ allowed: Object.freeze(['package-file', 'generic-start-transport-text']), forbidden: Object.freeze(['route-specific-continue-from', 'recipient-label-from-material', 'holder-label', 'current-work-label']) }),
|
|
94
|
+
fallbackTransportText: ready ? Object.freeze({ supported: true, filename: filename.replace(/\.handoff-package\.zip$/i, '.transport.txt'), content: '', normalEmission: false, requiredForHumanCompletion: false, authority: 'none' }) : null,
|
|
95
|
+
selectedRoute: null, findings: Object.freeze([]),
|
|
96
|
+
boundary: 'Route-less recipient-v2 package output projection. Generic Start transport only; material carriage creates no recipient, holder, participation, delegation, or work authority.'
|
|
97
|
+
});
|
|
56
98
|
}
|
|
57
99
|
|
|
58
100
|
|