@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.
Files changed (54) hide show
  1. package/package.json +6 -5
  2. package/src/audit/audit.run.js +1 -1
  3. package/src/public/index.js +18 -0
  4. package/src/public/node.js +1 -0
  5. package/src/schemas/creation.contracts.js +1 -1
  6. package/src/schemas/creation.schemaReferences.js +28 -8
  7. package/src/schemas/schema.reference.js +30 -4
  8. package/src/schemas/tiinex.root.v1.schema.json +2 -1
  9. package/src/tooling/portable/adapters/cli/cli.command-input.js +5 -0
  10. package/src/tooling/portable/adapters/cli/cli.common-output.js +23 -0
  11. package/src/tooling/portable/adapters/cli/cli.handoff-manufacture.js +52 -11
  12. package/src/tooling/portable/adapters/cli/cli.help.js +15 -2
  13. package/src/tooling/portable/adapters/cli/cli.run.js +1 -1
  14. package/src/tooling/portable/adapters/cli/cli.source-frontier-comparison.js +6 -6
  15. package/src/tooling/portable/adapters/cli/cli.source-frontier-reconciliation.js +18 -0
  16. package/src/tooling/portable/adapters/node/bootstrapCarrier.manufacture.js +18 -0
  17. package/src/tooling/portable/adapters/node/handoff.manufacture.enumeration.js +27 -8
  18. package/src/tooling/portable/adapters/node/handoff.manufacture.js +17 -0
  19. package/src/tooling/portable/adapters/node/sourceFrontierComparison.js +2 -1
  20. package/src/tooling/portable/adapters/node/sourceFrontierReconciliationProof.js +23 -0
  21. package/src/tooling/portable/adapters/node/workspaceCarrier.manufacture.js +14 -3
  22. package/src/tooling/portable/audit/audit.capability.js +2 -1
  23. package/src/tooling/portable/bootstrap/tiinex.llm.bootstrap.md +2 -2
  24. package/src/tooling/portable/comparison/sourceFrontierComparison.js +54 -4
  25. package/src/tooling/portable/comparison/sourceFrontierReconciliationProof.js +569 -0
  26. package/src/tooling/portable/draft/draft.create.js +3 -0
  27. package/src/tooling/portable/draft/draft.operations.js +1 -1
  28. package/src/tooling/portable/editor/editor.assistance.js +2 -25
  29. package/src/tooling/portable/handoff/bootstrapCarrier.manufacture.js +56 -0
  30. package/src/tooling/portable/handoff/contracts/tiinex.handoff.package.v1.schema.md +340 -77
  31. package/src/tooling/portable/handoff/manufacture.js +17 -3
  32. package/src/tooling/portable/handoff/pointerEntrypoint.js +4 -1
  33. package/src/tooling/portable/handoff/recipientV2.artifactFirst.build.js +73 -32
  34. package/src/tooling/portable/handoff/recipientV2.artifactFirst.closure.js +1 -1
  35. package/src/tooling/portable/handoff/recipientV2.artifactFirst.inspect.js +9 -1
  36. package/src/tooling/portable/handoff/recipientV2.artifactFirst.materials.js +28 -6
  37. package/src/tooling/portable/handoff/recipientV2.artifactFirst.shared.js +8 -0
  38. package/src/tooling/portable/handoff/recipientV2.coldProjection.js +11 -6
  39. package/src/tooling/portable/handoff/recipientV2.entryContract.js +12 -0
  40. package/src/tooling/portable/handoff/recipientV2.humanOutput.js +48 -6
  41. package/src/tooling/portable/handoff/recipientV2.packageV1.build.js +55 -43
  42. package/src/tooling/portable/handoff/recipientV2.packageV1.contract.js +57 -31
  43. package/src/tooling/portable/handoff/recipientV2.packageV1.inspect.helpers.js +63 -10
  44. package/src/tooling/portable/handoff/recipientV2.packageV1.inspect.js +55 -16
  45. package/src/tooling/portable/handoff/recipientV2.packageV1.workspaceProjection.js +14 -1
  46. package/src/tooling/portable/handoff/recipientV2.topology.js +3 -4
  47. package/src/tooling/portable/handoff/recipientV2.topology.materials.js +14 -0
  48. package/src/tooling/portable/handoff/schemaReferencePreflight.js +26 -0
  49. package/src/tooling/portable/handoff/transportEnvelopeV1.js +5 -0
  50. package/src/tooling/portable/handoff/workspaceCarrier.manufacture.js +42 -13
  51. package/src/tooling/portable/index.js +1 -0
  52. package/src/tooling/portable/operation.catalog.js +8 -0
  53. package/src/tooling/portable/source/sourceEligibility.js +108 -0
  54. package/src/validation/validateArtifact.js +62 -6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiinex/core",
3
- "version": "0.12.0",
3
+ "version": "0.14.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,
@@ -134,6 +134,7 @@
134
134
  "./schema-bootstrap": "./src/tooling/portable/schema/bootstrap/canonical.pack.js",
135
135
  "./node/release": "./src/release/run.mjs",
136
136
  "./node/release-policy": "./src/release/policy.mjs",
137
+ "./tooling/portable/source/sourceEligibility.js": "./src/tooling/portable/source/sourceEligibility.js",
137
138
  "./tooling/portable/comparison/sourceFrontierComparison.js": "./src/tooling/portable/comparison/sourceFrontierComparison.js",
138
139
  "./tooling/portable/adapters/node/sourceFrontierComparison.js": "./src/tooling/portable/adapters/node/sourceFrontierComparison.js"
139
140
  },
@@ -167,12 +168,12 @@
167
168
  "type": "git",
168
169
  "url": "git+https://github.com/Tiinex/core.git"
169
170
  },
170
- "gitHead": "a63df8c4736b3267ac0aefa769b1262f45b78dac",
171
+ "gitHead": "030669b6596196da946edb049d15af044bcd96c6",
171
172
  "tiinexRelease": {
172
173
  "policy": "tiinex.master-npm-release.v1",
173
- "sourceCommit": "a63df8c4736b3267ac0aefa769b1262f45b78dac",
174
- "sourceTree": "d502ba11f95bbaf29fb7b86015df3d7b63bc82eb",
174
+ "sourceCommit": "030669b6596196da946edb049d15af044bcd96c6",
175
+ "sourceTree": "090aaead28df51b2aeb6679018526f54260a1ca8",
175
176
  "repository": "Tiinex/core",
176
- "previousVersion": "0.11.0"
177
+ "previousVersion": "0.13.0"
177
178
  }
178
179
  }
@@ -13,7 +13,7 @@ export function runAudit(scope = {}) {
13
13
  if (isPlainSupportingMarkdown(parsed, record)) return supportingMarkdownAuditResult(record, parsed, availability);
14
14
 
15
15
  const schemaId = parsed.envelope.current.schema.id || record?.schemaId || record?.currentSchemaId || '';
16
- const validationResult = validateArtifact({ parsed, schemaId, record, markdown, schemaReferenceAuthorities: scope.schemaReferenceAuthorities || null, validationContractOverride: scope.validationContractOverride || null, schemaValidationAuthority: scope.schemaValidationAuthority || null });
16
+ const validationResult = validateArtifact({ parsed, schemaId, record, markdown, schemaReferenceAuthorities: scope.schemaReferenceAuthorities || null, schemaReferenceContext: scope.schemaReferenceContext || 'historical', validationContractOverride: scope.validationContractOverride || null, schemaValidationAuthority: scope.schemaValidationAuthority || null });
17
17
  const findings = validationResult.findings;
18
18
  return {
19
19
  status: findings.some((finding) => finding.severity === 'error') ? 'invalid-or-incomplete' : validationResult.resolution.fallbackUsed ? 'degraded' : 'readable',
@@ -24,6 +24,17 @@ export {
24
24
  secureTransportV1AuthenticatedMetadata
25
25
  } from '../transport/secureTransportV1.js';
26
26
 
27
+ export {
28
+ PORTABLE_SOURCE_ELIGIBILITY_SCHEMA_ID,
29
+ PORTABLE_GENERATED_SOURCE_EXCLUDED_DIRECTORY_NAMES,
30
+ PORTABLE_GENERATED_SOURCE_EXCLUDED_FILE_SUFFIXES,
31
+ DEFAULT_PORTABLE_SOURCE_EXCLUDED_DIRECTORIES,
32
+ DEFAULT_PORTABLE_SOURCE_EXCLUDED_RELATIVE_PATHS,
33
+ qualifyPortableSourcePath,
34
+ isPortableSourceEligiblePath,
35
+ portableSourceEligibilityPolicy
36
+ } from '../tooling/portable/source/sourceEligibility.js';
37
+
27
38
  export {
28
39
  PORTABLE_SOURCE_FRONTIER_SCHEMA_ID,
29
40
  PORTABLE_SOURCE_FRONTIER_COMPARISON_SCHEMA_ID,
@@ -35,3 +46,10 @@ export {
35
46
  compareOrReconcilePortableSourceFrontiers,
36
47
  projectPortableSourceFrontierComparisonSummary
37
48
  } from '../tooling/portable/comparison/sourceFrontierComparison.js';
49
+ export {
50
+ PORTABLE_SOURCE_FRONTIER_RECONCILIATION_PROOF_SCHEMA_ID,
51
+ PORTABLE_SOURCE_FRONTIER_RECONCILIATION_SUMMARY_SCHEMA_ID,
52
+ provePortableSourceReconciliation,
53
+ projectPortableSourceReconciliationProofSummary,
54
+ qualifyPortableSourceReconciliationProofForManufacture
55
+ } from '../tooling/portable/comparison/sourceFrontierReconciliationProof.js';
@@ -1,3 +1,4 @@
1
1
  export * from './index.js';
2
2
  export * from '../tooling/portable/index.js';
3
3
  export * from '../tooling/portable/adapters/node/sourceFrontierComparison.js';
4
+ export * from '../tooling/portable/adapters/node/sourceFrontierReconciliationProof.js';
@@ -179,7 +179,7 @@ export function validateArtifactCreationResult(draft = {}, parentRecord = {}, op
179
179
  findings.push(...(representation.findings || []).map((message, index) => error(`creation.continuation-representation.${index + 1}`, message)));
180
180
  findings.push(...validateParentTargetIntegrity(parsed, parentRecord, parentIntegrityTarget));
181
181
  const parentSchemaAuthority = parentRecord.schemaReferenceAuthority || {};
182
- const parentSchemaReference = qualifySchemaReferenceValue(parent.schema?.raw || '', parentSchemaAuthority);
182
+ const parentSchemaReference = qualifySchemaReferenceValue(parent.schema?.raw || '', parentSchemaAuthority, { requireExactTargetWhenQualified: true });
183
183
  for (const [index, message] of (parentSchemaReference.findings || []).entries()) findings.push(error(`creation.parent-schema-reference.${index + 1}`, message));
184
184
  if (parentSchemaAuthority.resolutionState !== 'qualified') findings.push(error('creation.parent-schema-reference.unresolved', 'Exact continuation requires the declared Parent Schema reference authority to be resolver-qualified.'));
185
185
  } else if (!rootCreation) findings.push(error('creation.parent.required', 'Continuation creation requires an exact supplied Parent authority.'));
@@ -3,13 +3,9 @@ import { inspectCreationRepresentation } from './creation.representation.js';
3
3
  import { qualifySchemaReferenceMaterialCoherence, qualifySchemaReferenceValue, schemaReferenceAuthorityFromBinding } from './schema.reference.js';
4
4
 
5
5
  export function schemaReferenceAuthoritiesForCreation(targetModule = null, explicit = null) {
6
- const root = resolveRegisteredSchemaModule({ schemaId: 'tiinex.root.v1' });
7
- const rootModule = root?.fallbackUsed ? null : root?.module || null;
8
- const rootQualification = qualifiedSourceQualification(rootModule);
9
- const targetQualification = qualifiedSourceQualification(targetModule);
10
6
  const defaults = {
11
- envelope: schemaReferenceAuthorityFromBinding('tiinex.root.v1', rootModule?.binding || {}, rootQualification?.authority || null, rootQualification),
12
- current: schemaReferenceAuthorityFromBinding(String(targetModule?.id || ''), targetModule?.binding || {}, targetQualification?.authority || null, targetQualification)
7
+ envelope: schemaReferenceAuthorityForRegisteredSchema('tiinex.root.v1'),
8
+ current: schemaReferenceAuthorityForModule(targetModule)
13
9
  };
14
10
  return Object.freeze({
15
11
  envelope: explicitSchemaReferenceAuthority(explicit?.envelope, defaults.envelope),
@@ -17,6 +13,19 @@ export function schemaReferenceAuthoritiesForCreation(targetModule = null, expli
17
13
  });
18
14
  }
19
15
 
16
+ export function schemaReferenceAuthorityForRegisteredSchema(schemaId = '') {
17
+ const id = String(schemaId || '').trim();
18
+ const resolution = resolveRegisteredSchemaModule({ schemaId: id });
19
+ const module = resolution?.fallbackUsed ? null : resolution?.module || null;
20
+ return schemaReferenceAuthorityForModule(module, id);
21
+ }
22
+
23
+ function schemaReferenceAuthorityForModule(module = null, fallbackSchemaId = '') {
24
+ const id = String(module?.id || fallbackSchemaId || '').trim();
25
+ const qualification = qualifiedSourceQualification(module);
26
+ return schemaReferenceAuthorityFromBinding(id, module?.binding || {}, qualification?.authority || null, qualification);
27
+ }
28
+
20
29
  export function explicitSchemaReferenceAuthority(value = null, fallback = {}) {
21
30
  if (!value) return fallback;
22
31
  const schemaId = String(value.schemaId || fallback.schemaId || '').trim();
@@ -27,7 +36,7 @@ export function explicitSchemaReferenceAuthority(value = null, fallback = {}) {
27
36
  const preferredTarget = String(value.preferredTarget || value.target || exactTargets[0] || '').trim();
28
37
  const fallbackTargets = Object.freeze([...(fallback.exactTargets || [])]);
29
38
  const materialBoundTarget = Boolean(preferredTarget && fallback?.materialBoundTarget === true && fallbackTargets.includes(preferredTarget));
30
- return Object.freeze({
39
+ const candidate = Object.freeze({
31
40
  ...fallback,
32
41
  schemaId,
33
42
  exactTargets: Object.freeze(exactTargets),
@@ -39,6 +48,17 @@ export function explicitSchemaReferenceAuthority(value = null, fallback = {}) {
39
48
  materialBoundTarget,
40
49
  semanticMaterialIdentity: fallback.semanticMaterialIdentity || Object.freeze({ state: 'unavailable' })
41
50
  });
51
+ if (!preferredTarget) return candidate;
52
+ const coherence = qualifySchemaReferenceMaterialCoherence(candidate);
53
+ if (coherence.state === 'qualified') return candidate;
54
+ return Object.freeze({
55
+ ...candidate,
56
+ exactTargets: Object.freeze([]),
57
+ preferredTarget: '',
58
+ resolutionState: 'unresolved',
59
+ rejectedExactTargets: Object.freeze(exactTargets),
60
+ resolutionFindings: Object.freeze([...(coherence.findings || [])])
61
+ });
42
62
  }
43
63
 
44
64
  export function qualifyCreationSchemaReferences(markdown = '', contract = {}) {
@@ -53,7 +73,7 @@ export function qualifyCreationSchemaReferences(markdown = '', contract = {}) {
53
73
  }
54
74
 
55
75
  function qualifyResolvedSchemaReference(value, authority) {
56
- const lexical = qualifySchemaReferenceValue(value, authority);
76
+ const lexical = qualifySchemaReferenceValue(value, authority, { requireExactTargetWhenQualified: true });
57
77
  const material = qualifySchemaReferenceMaterialCoherence(authority);
58
78
  const findings = [...(lexical.findings || []), ...(material.findings || [])];
59
79
  return Object.freeze({ ...lexical, state: findings.length ? 'unavailable' : 'qualified', resolutionState: authority?.resolutionState || '', materialCoherence: material, findings: Object.freeze(findings) });
@@ -6,13 +6,14 @@ export const SCHEMA_REFERENCE_MATERIAL_COHERENCE_SCHEMA_ID = 'tiinex.site.schema
6
6
  export function schemaReferenceAuthorityFromBinding(schemaId = '', binding = {}, sourceAuthority = null, sourceQualification = null) {
7
7
  const id = String(schemaId || binding?.schemaId || '').trim();
8
8
  const localUnpublished = String(binding?.publicationState || '').toLowerCase().includes('unpublished');
9
- const exactSourceTargets = localUnpublished
9
+ const referencePublished = qualifiedImmutableReferencePublication(binding, sourceQualification);
10
+ const exactSourceTargets = localUnpublished && !referencePublished
10
11
  ? Object.freeze({ state: 'unavailable', targets: Object.freeze([]), findings: Object.freeze(['Binding is explicitly unpublished; remote source targets are not creation reference authority.']) })
11
12
  : canonicalGithubSchemaSourceTargets(sourceAuthority);
12
13
  const exactTargets = exactSourceTargets.state === 'qualified' ? [...exactSourceTargets.targets] : [];
13
14
  const semanticMaterialIdentity = normalizeMaterialIdentity(sourceQualification?.materialIdentity || {});
14
15
  const sourceQualified = sourceQualification?.state === 'qualified' && semanticMaterialIdentity.state === 'qualified';
15
- const materialBoundTarget = Boolean(exactTargets.length && sourceQualified && sourceQualification?.bindingMaterialCoherence?.state === 'qualified');
16
+ const materialBoundTarget = Boolean(exactTargets.length && sourceQualified && (sourceQualification?.bindingMaterialCoherence?.state === 'qualified' || referencePublished));
16
17
  const preferredTarget = exactTargets[0] || '';
17
18
  return Object.freeze({
18
19
  schema: SCHEMA_REFERENCE_AUTHORITY_SCHEMA_ID,
@@ -29,6 +30,17 @@ export function schemaReferenceAuthorityFromBinding(schemaId = '', binding = {},
29
30
  });
30
31
  }
31
32
 
33
+ function qualifiedImmutableReferencePublication(binding = {}, sourceQualification = null) {
34
+ if (String(binding?.schemaReferencePublicationState || '').trim() !== 'published-immutable-canonical') return false;
35
+ if (sourceQualification?.state !== 'qualified' || sourceQualification?.materialIdentity?.state !== 'qualified') return false;
36
+ if (sourceQualification?.authority?.providerQualification?.state !== 'qualified') return false;
37
+ const bindingBlobSha = String(binding?.sourceBlobSha || '').trim().toLowerCase();
38
+ const loadedBlobSha = String(sourceQualification?.materialIdentity?.sourceBlobSha || '').trim().toLowerCase();
39
+ const expectedChecksum = String(binding?.checksum?.value || binding?.checksum || '').trim().toLowerCase();
40
+ const loadedChecksum = String(sourceQualification?.materialIdentity?.sha256 || '').trim().toLowerCase();
41
+ return Boolean(bindingBlobSha && loadedBlobSha && bindingBlobSha === loadedBlobSha && expectedChecksum && loadedChecksum && expectedChecksum === loadedChecksum);
42
+ }
43
+
32
44
  export function qualifySchemaReferenceMaterialCoherence(authority = {}) {
33
45
  const semantic = normalizeMaterialIdentity(authority?.semanticMaterialIdentity || {});
34
46
  const target = String(authority?.preferredTarget || authority?.target || '').trim();
@@ -105,14 +117,24 @@ export function parseSchemaReferenceValue(value = '') {
105
117
  export function renderSchemaReference(authority = {}) {
106
118
  const schemaId = String(authority?.schemaId || '').trim();
107
119
  if (!schemaId) throw new Error('schema-reference-id-missing');
108
- const target = String(authority?.preferredTarget || '').trim();
120
+ const target = qualifiedExactSchemaReferenceTarget(authority);
109
121
  return target ? `[${schemaId}](${target})` : schemaId;
110
122
  }
111
123
 
112
- export function qualifySchemaReferenceValue(value = '', authority = {}) {
124
+ export function qualifiedExactSchemaReferenceTarget(authority = {}) {
125
+ const preferredTarget = String(authority?.preferredTarget || '').trim();
126
+ const exactTargets = new Set([...(authority?.exactTargets || [])].map((item) => String(item ?? '')).filter(Boolean));
127
+ if (String(authority?.resolutionState || authority?.state || '') !== 'qualified') return '';
128
+ if (!preferredTarget || !exactTargets.has(preferredTarget)) return '';
129
+ return preferredTarget;
130
+ }
131
+
132
+ export function qualifySchemaReferenceValue(value = '', authority = {}, options = {}) {
113
133
  const observed = parseSchemaReferenceValue(value);
114
134
  const expectedSchemaId = String(authority?.schemaId || '').trim();
115
135
  const exactTargets = new Set([...(authority?.exactTargets || [])].map((item) => String(item ?? '')).filter(Boolean));
136
+ const qualifiedExactTarget = qualifiedExactSchemaReferenceTarget(authority);
137
+ const requireExactTargetWhenQualified = options?.requireExactTargetWhenQualified === true;
116
138
  const findings = [];
117
139
  if (!expectedSchemaId) findings.push('Expected schema identifier authority is unavailable.');
118
140
  if (observed.schemaId !== expectedSchemaId) findings.push(`Schema identifier must be exactly ${expectedSchemaId}; observed ${observed.schemaId || '(empty)'}.`);
@@ -120,12 +142,16 @@ export function qualifySchemaReferenceValue(value = '', authority = {}) {
120
142
  if (observed.form === 'markdown-link') {
121
143
  targetState = exactTargets.has(observed.target) ? 'qualified' : 'unqualified';
122
144
  if (targetState !== 'qualified') findings.push(`Schema reference target is not qualified for ${expectedSchemaId}: ${observed.target || '(empty)'}.`);
145
+ } else if (observed.form === 'plain-schema-id' && requireExactTargetWhenQualified && qualifiedExactTarget) {
146
+ targetState = 'qualified-exact-target-omitted';
147
+ findings.push(`Qualified exact schema-reference authority requires Markdown Link form targeting ${qualifiedExactTarget}; a plain schema id is not sufficient for this new candidate.`);
123
148
  }
124
149
  if (observed.form === 'empty') findings.push('Schema reference value is empty.');
125
150
  return Object.freeze({
126
151
  state: findings.length ? 'unavailable' : 'qualified',
127
152
  schemaIdState: observed.schemaId === expectedSchemaId && expectedSchemaId ? 'qualified' : 'unavailable',
128
153
  targetState,
154
+ qualifiedExactTarget,
129
155
  observed,
130
156
  authority,
131
157
  findings: Object.freeze(findings)
@@ -20,5 +20,6 @@
20
20
  "bindingVersion": "tiinex.web.schema-binding.v1",
21
21
  "originId": "tiinex-docs-39889512-accepted-local",
22
22
  "originTrustRole": "canonical-core",
23
- "publicationState": "accepted-local-unpublished"
23
+ "publicationState": "accepted-local-unpublished",
24
+ "schemaReferencePublicationState": "published-immutable-canonical"
24
25
  }
@@ -11,6 +11,7 @@ import { reductionPreflightCliInput } from './cli.reduction-input.js';
11
11
  import {land} from './cli.land.js';
12
12
  import { OPERATIONS_WITHOUT_EXPLICIT_MATERIAL } from './cli.material-policy.js';
13
13
  import { prepareSourceFrontierComparisonCliInput } from './cli.source-frontier-comparison.js';
14
+ import { prepareSourceFrontierReconciliationCliInput } from './cli.source-frontier-reconciliation.js';
14
15
 
15
16
  export async function commandInput(parsed, runtime = {}) {
16
17
  const flags = parsed.flags;
@@ -140,6 +141,10 @@ export async function commandInput(parsed, runtime = {}) {
140
141
  }
141
142
 
142
143
  if (parsed.command === 'compare-source-frontiers') return prepareSourceFrontierComparisonCliInput(parsed, flags);
144
+ if (parsed.command === 'prove-source-reconciliation') {
145
+ const dispositions = await readOptionalJson(flags.dispositions || flags['disposition-file']);
146
+ return prepareSourceFrontierReconciliationCliInput(flags, dispositions);
147
+ }
143
148
 
144
149
  if (parsed.command === 'manufacture-handoff-package') {
145
150
  return prepareHandoffManufactureCliCommand(parsed, runtime);
@@ -1,4 +1,5 @@
1
1
  import { projectPortableSourceFrontierComparisonSummary } from '../../comparison/sourceFrontierComparison.js';
2
+ import { projectPortableSourceReconciliationProofSummary } from '../../comparison/sourceFrontierReconciliationProof.js';
2
3
 
3
4
  const COMMON_DEFAULT_PROJECTION = 'common-default';
4
5
 
@@ -8,9 +9,31 @@ export function projectCommonCliDefaultOutput(result = {}, parsed = {}) {
8
9
  if (parsed?.command === 'project-grounding-readiness') return projectGroundDefault(result, parsed);
9
10
  if (parsed?.command === 'manufacture-handoff-package' && parsed?.surfaceCommand === 'handoff') return projectHandoffDefault(result, parsed);
10
11
  if (parsed?.command === 'compare-source-frontiers') return projectCompareDefault(result, parsed);
12
+ if (parsed?.command === 'prove-source-reconciliation') return projectReconciliationProofDefault(result, parsed);
11
13
  return result;
12
14
  }
13
15
 
16
+ function projectReconciliationProofDefault(result = {}, parsed = {}) {
17
+ const summary = projectPortableSourceReconciliationProofSummary({ ...result, schema: result.resultSchema || result.schema || '' }, { maxPaths: parsed?.flags?.['max-paths'] || 20 });
18
+ return Object.freeze({
19
+ schema: result.schema,
20
+ operation: result.operation || 'prove-source-reconciliation',
21
+ resultSchema: result.resultSchema,
22
+ projection: COMMON_DEFAULT_PROJECTION,
23
+ status: summary.status,
24
+ state: summary.state,
25
+ counts: summary.counts,
26
+ preservation: summary.preservation,
27
+ workspaces: summary.workspaces,
28
+ proofFingerprint: summary.proofFingerprint,
29
+ manufactureBinding: summary.manufactureBinding,
30
+ findingSummary: summary.findingSummary,
31
+ actionableFindings: summary.actionableFindings,
32
+ detail: Object.freeze({ fullReceipt: Object.freeze({ command: String(parsed.surfaceCommand || 'reconcile'), flag: '--full', requiredForManufacture: true }) }),
33
+ boundary: summary.boundary
34
+ });
35
+ }
36
+
14
37
  function projectCompareDefault(result = {}, parsed = {}) {
15
38
  const summary = projectPortableSourceFrontierComparisonSummary({ ...result, schema: result.resultSchema || result.schema || '' }, { maxPaths: parsed?.flags?.['max-paths'] || 20 });
16
39
  return Object.freeze({
@@ -2,6 +2,7 @@ import { mkdir, readFile, writeFile } from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
  import { prepareNodeHandoffManufacturingInput } from '../node/handoff.manufacture.js';
4
4
  import { prepareNodeWorkspaceCarrierManufacturingInput } from '../node/workspaceCarrier.manufacture.js';
5
+ import { prepareNodeBootstrapCarrierManufacturingInput } from '../node/bootstrapCarrier.manufacture.js';
5
6
  import { projectHandoffHumanOutput } from '../../handoff/carrierProjection.js';
6
7
  import { writePortableRuntimePackageZip } from '../../output/node.zip.js';
7
8
  import { writeRecipientFacingV2PackageZip } from '../../output/recipientV2.zip.js';
@@ -16,8 +17,9 @@ export async function prepareHandoffManufactureCliCommand(parsed = {}, runtime =
16
17
  const flags = parsed.flags || {};
17
18
  const workspaceRoot = flags.workspace || parsed.positionals?.[0] || '.';
18
19
  const carrierMode = String(flags['carrier-mode'] || 'handoff').trim().toLowerCase();
19
- if (!['handoff', 'workspace'].includes(carrierMode)) throw new Error(`portable.cli.handoff-carrier.carrier-mode.invalid:${carrierMode}`);
20
+ if (!['handoff', 'workspace', 'bootstrap'].includes(carrierMode)) throw new Error(`portable.cli.handoff-carrier.carrier-mode.invalid:${carrierMode}`);
20
21
  if (carrierMode === 'workspace') return prepareWorkspaceCarrierCliCommand(flags, workspaceRoot, runtime);
22
+ if (carrierMode === 'bootstrap') return prepareBootstrapCarrierCliCommand(flags, runtime);
21
23
  const continuationState = parsed.surfaceCommand === 'handoff'
22
24
  ? await readGroundContinuationState(workspaceRoot)
23
25
  : {};
@@ -28,6 +30,8 @@ export async function prepareHandoffManufactureCliCommand(parsed = {}, runtime =
28
30
  if (!flags.route && handoffPath) flags.route = handoffPath;
29
31
  if (!flags.output && !flags['output-dir'] && parsed.surfaceCommand === 'handoff' && continuationState.returnOutputDir) flags['output-dir'] = continuationState.returnOutputDir;
30
32
  const materialBindings = await readOptionalJson(flags['material-bindings'] || flags.materials);
33
+ const reconciliationProof = await readOptionalJson(flags['reconciliation-proof']);
34
+ const requireReconciliationProof = Boolean(flags['require-reconciliation-proof']);
31
35
  const packageParentWorkspaceIds = splitFlag(flags['package-parent-workspaces']);
32
36
  const packageParentWorkspaceAliases = await readOptionalJson(flags['package-parent-workspace-aliases']);
33
37
  const operatorCarrierProfile = await readOptionalJson(flags['carrier-profile']);
@@ -129,7 +133,9 @@ export async function prepareHandoffManufactureCliCommand(parsed = {}, runtime =
129
133
  packageParentPath: parentPackagePath ? path.resolve(parentPackagePath) : '',
130
134
  packageParentSha256,
131
135
  packageParentWorkspaceIds,
132
- packageParentWorkspaceAliases
136
+ packageParentWorkspaceAliases,
137
+ reconciliationProof,
138
+ requireReconciliationProof
133
139
  }, runtime);
134
140
  return {
135
141
  input,
@@ -142,13 +148,15 @@ export async function prepareHandoffManufactureCliCommand(parsed = {}, runtime =
142
148
  }
143
149
 
144
150
  export async function materializeHandoffManufactureCliOutput(result = {}, flags = {}) {
145
- const workspaceMode = String(result.carrierProjection?.mode || '') === 'workspace';
146
- let humanOutput = workspaceMode ? projectWorkspaceCarrierHumanOutput(result, flags) : projectHandoffHumanOutput({
151
+ const carrierMode = String(result.carrierProjection?.mode || '');
152
+ const workspaceMode = carrierMode === 'workspace';
153
+ const bootstrapMode = carrierMode === 'bootstrap';
154
+ let humanOutput = workspaceMode ? projectWorkspaceCarrierHumanOutput(result, flags) : bootstrapMode ? projectBootstrapCarrierHumanOutput(result, flags) : projectHandoffHumanOutput({
147
155
  projection: result.carrierProjection || {},
148
156
  route: flags.route || '',
149
157
  collisionInstance: flags['collision-instance'] || 1
150
158
  });
151
- if (!workspaceMode && result.bundle?.transportFormat) humanOutput = projectRecipientV2HumanOutput(humanOutput, result.inspection || {});
159
+ if (result.bundle?.transportFormat) humanOutput = projectRecipientV2HumanOutput(humanOutput, result.inspection || {});
152
160
  const wantsWrite = Boolean(flags.output || flags['output-dir']);
153
161
  const blocked = result.status === 'blocked' || result.transportExecutable === false || Number(result.findingSummary?.counts?.error || 0) > 0;
154
162
  if (!wantsWrite || blocked) return summarizeHandoffManufactureCliOutput(result, {}, humanOutput, null);
@@ -159,7 +167,6 @@ export async function materializeHandoffManufactureCliOutput(result = {}, flags
159
167
  const writeReceipt = writeBundle?.transportFormat
160
168
  ? await writeRecipientFacingV2PackageZip(writeBundle, target, writeBundle === result.bundle ? { inspection: result.inspection } : {})
161
169
  : await writePortableRuntimePackageZip(writeBundle, target);
162
- if (workspaceMode && flags['transport-text']) throw new Error('portable.cli.workspace-carrier.transport-text.unavailable');
163
170
  const transportTextReceipt = flags['transport-text'] ? await writeTransportTextSidecar(humanOutput, target, flags['transport-text']) : null;
164
171
  return summarizeHandoffManufactureCliOutput(result, writeReceipt, humanOutput, transportTextReceipt);
165
172
  }
@@ -170,6 +177,7 @@ async function prepareWorkspaceCarrierCliCommand(flags = {}, workspaceRoot = '.'
170
177
  const expectedToolingBootstrap = await readOptionalJson(flags['tooling-bootstrap-manifest']);
171
178
  const workspaceDescriptorValue = await readOptionalJson(flags['workspace-roots'] || flags['workspace-descriptors']);
172
179
  const workspaceTargetValue = await readOptionalJson(flags['workspace-targets']);
180
+ const workspaceScopeValue = await readOptionalJson(flags['workspace-scopes']);
173
181
  const additionalWorkspaces = [...splitFlag(flags['additional-workspaces']), ...descriptorArray(workspaceDescriptorValue, 'workspaces')];
174
182
  const verifyRoundtrip = !flags['no-roundtrip'];
175
183
  const carrierProfile = selectCarrierProfile({ operator: operatorCarrierProfile, runtime: runtime.defaultCarrierProfile || null });
@@ -181,6 +189,8 @@ async function prepareWorkspaceCarrierCliCommand(flags = {}, workspaceRoot = '.'
181
189
  workspaceTitle: flags['workspace-title'] || flags.title || '',
182
190
  workspaceTargetPath: flags['workspace-target'] || flags['workspace-artifact'] || '',
183
191
  workspaceTargets: workspaceTargetValue,
192
+ workspaceScopes: descriptorArray(workspaceScopeValue, 'scopes').length ? descriptorArray(workspaceScopeValue, 'scopes') : workspaceScopeValue,
193
+ materialRepresentationWorkspaceIds: splitFlag(flags['material-representation-workspaces'] || flags['generic-material-workspaces']),
184
194
  toolingBootstrap: flags['tooling-bootstrap'] || 'embedded',
185
195
  expectedToolingBootstrap,
186
196
  maxFiles: flags['max-files'],
@@ -194,6 +204,37 @@ async function prepareWorkspaceCarrierCliCommand(flags = {}, workspaceRoot = '.'
194
204
  return { input, options: { verifyRoundtrip, packageInput: { builtAt: flags['built-at'] || undefined } } };
195
205
  }
196
206
 
207
+ async function prepareBootstrapCarrierCliCommand(flags = {}, runtime = {}) {
208
+ if (flags.handoff || flags.route || flags.routes || flags['handoff-routes'] || flags['workspace-routes'] || flags.workspace || flags['workspace-target'] || flags['workspace-targets'] || flags['workspace-roots'] || flags['workspace-descriptors']) throw new Error('portable.cli.bootstrap-carrier.source-material-or-route.forbidden');
209
+ const operatorCarrierProfile = await readOptionalJson(flags['carrier-profile']);
210
+ const expectedToolingBootstrap = await readOptionalJson(flags['tooling-bootstrap-manifest']);
211
+ const verifyRoundtrip = !flags['no-roundtrip'];
212
+ const carrierProfile = selectCarrierProfile({ operator: operatorCarrierProfile, runtime: runtime.defaultCarrierProfile || null });
213
+ const input = await prepareNodeBootstrapCarrierManufacturingInput({
214
+ toolingBootstrap: flags['tooling-bootstrap'] || 'embedded', expectedToolingBootstrap, bootstrapMaxFiles: flags['bootstrap-max-files'], verifyRoundtrip, createdAt: flags['built-at'] || undefined,
215
+ carrierLineage: Object.freeze({ ...initialHandoffCarrierLineage(), checkpointKind: 'progression', majorReason: '' }), carrierProfile
216
+ }, runtime);
217
+ return { input, options: { verifyRoundtrip, packageInput: { builtAt: flags['built-at'] || undefined } } };
218
+ }
219
+
220
+ function projectBootstrapCarrierHumanOutput(result = {}, flags = {}) {
221
+ const projection = result.carrierProjection || {};
222
+ const ready = result.status === 'ready' && projection.status === 'ready' && projection.mode === 'bootstrap' && (projection.routes || []).length === 0 && (projection.workspaces || []).length === 0;
223
+ const dimension = String(projection.lineage?.dimension || '001');
224
+ const projectedFilename = String(flags['projected-filename'] || flags.projectedFilename || '').trim();
225
+ const filename = projectedFilename || `tiinex-${dimension}.handoff-package.zip`;
226
+ if (filename !== filename.trim() || !filename.endsWith('.handoff-package.zip') || /[<>:"/\\|?*\x00-\x1f\x7f]/.test(filename) || /[. ]$/.test(filename) || /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/i.test(filename) || new TextEncoder().encode(filename).byteLength > 255) throw new Error('portable.cli.bootstrap-carrier.filename.invalid');
227
+ return Object.freeze({
228
+ schema: 'tiinex.portable.handoff-human-output.v1', status: ready ? 'ready' : 'blocked',
229
+ primary: ready ? Object.freeze({ kind: 'bootstrap-package', filename, dimension, parentDimension: String(projection.lineage?.parentDimension || ''), checkpointKind: String(projection.lineage?.checkpointKind || ''), routeId: '', workspaceId: '', workspaceRelativeHandoffPath: '', collisionInstance: 1, singleHumanTransportChoice: true }) : null,
230
+ normalInlineRouting: ready ? Object.freeze({ kind: 'transport-text', content: '', normalEmission: true, requiredForHumanCompletion: true, placement: 'adjacent-to-primary', authority: 'none' }) : null, sharedRouting: null,
231
+ presentation: Object.freeze({ kind: 'bootstrap-only-carrier', label: 'Bootstrap carrier', authority: 'none', recipientLabel: '' }),
232
+ normalEmissionBoundary: Object.freeze({ allowed: Object.freeze(['package-file', 'generic-start-transport-text']), forbidden: Object.freeze(['workspace-label', 'route-specific-continue-from', 'recipient-label', 'holder-label', 'current-work-label']) }),
233
+ fallbackTransportText: ready ? Object.freeze({ supported: true, filename: filename.replace(/\.handoff-package\.zip$/i, '.transport.txt'), content: '', normalEmission: false, requiredForHumanCompletion: false, authority: 'none' }) : null, selectedRoute: null, findings: Object.freeze([]),
234
+ boundary: 'Bootstrap-only carrier output projection. Generic Start transport text only; no Workspace, Handoff route, recipient, holder, Role, or work projection exists.'
235
+ });
236
+ }
237
+
197
238
  function projectWorkspaceCarrierHumanOutput(result = {}, flags = {}) {
198
239
  const projection = result.carrierProjection || {};
199
240
  const ready = result.status === 'ready' && projection.status === 'ready' && projection.mode === 'workspace' && (projection.routes || []).length === 0;
@@ -209,11 +250,11 @@ function projectWorkspaceCarrierHumanOutput(result = {}, flags = {}) {
209
250
  schema: 'tiinex.portable.handoff-human-output.v1',
210
251
  status: ready ? 'ready' : 'blocked',
211
252
  primary: ready ? Object.freeze({ kind: 'workspace-package', filename, dimension, parentDimension: String(projection.lineage?.parentDimension || ''), checkpointKind: String(projection.lineage?.checkpointKind || ''), routeId: '', workspaceId: '', workspaceRelativeHandoffPath: '', collisionInstance: 1, singleHumanTransportChoice: true }) : null,
212
- normalInlineRouting: null, sharedRouting: null,
213
- presentation: Object.freeze({ kind: 'pointerless-workspace-carrier', label: 'Workspace carrier', authority: 'none' }),
214
- normalEmissionBoundary: Object.freeze({ allowed: Object.freeze(['package-file']), forbidden: Object.freeze(['handoff-routing-text', 'continue-from-pointer']) }),
215
- fallbackTransportText: null, selectedRoute: null, findings: Object.freeze([]),
216
- boundary: 'Pointerless Workspace-carrier output projection only. No Handoff routing text exists because the package role declares no Handoff route.'
253
+ normalInlineRouting: ready ? Object.freeze({ kind: 'transport-text', content: '', normalEmission: true, requiredForHumanCompletion: true, placement: 'adjacent-to-primary', authority: 'none' }) : null, sharedRouting: null,
254
+ presentation: Object.freeze({ kind: 'pointerless-workspace-carrier', label: 'Workspace carrier', authority: 'none', recipientLabel: '' }),
255
+ normalEmissionBoundary: Object.freeze({ allowed: Object.freeze(['package-file', 'generic-start-transport-text']), forbidden: Object.freeze(['route-specific-continue-from', 'recipient-label-from-material']) }),
256
+ fallbackTransportText: ready ? Object.freeze({ supported: true, filename: filename.replace(/\.handoff-package\.zip$/i, '.transport.txt'), content: '', normalEmission: false, requiredForHumanCompletion: false, authority: 'none' }) : null, selectedRoute: null, findings: Object.freeze([]),
257
+ boundary: 'Pointerless Workspace-carrier output projection. Generic Start transport text is permitted; Handoff Continue-from and recipient projection remain absent.'
217
258
  });
218
259
  }
219
260
 
@@ -13,6 +13,7 @@ export function portableCliHelpText(commandPrefix = '', surfaceCommand = '') {
13
13
  `${command} author <workspace-dir> --schema <schema-id> (--path <workspace-relative-artifact> | --directory <workspace-relative-directory>) --body <body.md> [--parent <workspace-relative-or-qualified-parent>] [--parent-source <local-parent-file>] [--title <title>] [--summary <summary>] [--why <why>]`,
14
14
  `${command} handoff <workspace-dir>`,
15
15
  `${command} compare --left-kind <kind> --left <path> --right-kind <kind> --right <path> [side selectors]`,
16
+ `${command} reconcile --base-kind <kind> --base <path> --incoming-kind <kind> --incoming <path> --current-kind <kind> --current <path> --reconciled-kind <kind> --reconciled <path> [--dispositions <json-file>]`,
16
17
  '',
17
18
  `Handoff aliases: ${command} orient <carrier.zip>; ${command} validate <carrier.zip>`,
18
19
  `Advanced/internal catalog: ${command} operations`,
@@ -21,7 +22,7 @@ export function portableCliHelpText(commandPrefix = '', surfaceCommand = '') {
21
22
  '- `orient`, `ground`, and public `handoff` use compact decision-first default projections; add `--full` on the same command for the complete qualified receipt.',
22
23
  '- `ground` is read-only; append `--continue <workspace-dir>` only after `grounded-to-act` to materialize the selected carried Workspace and runtime-only `.tiinex/continuation.json`.',
23
24
  '- `author` uses continuation state to infer ordinary Parent continuity, seal c14n-v2 integrity, audit, and stage; invalid artifacts are not retained.',
24
- '- `handoff` uses continuation state plus the latest qualified authored Handoff to manufacture the canonical return carrier and excludes runtime-only `.tiinex` state. A received `--package-parent` continues carrier lineage only; it never selects parent Workspace source by itself. Advanced manufacture may opt into exact parent snapshots with `--package-parent-workspaces <id,...|all>` and may bind an explicitly selected renamed predecessor with `--package-parent-workspace-aliases`; aliases never infer source identity. Normal operator completion is one Handoff package plus the exact routing text; markdown-capable hosts render that routing in a fenced code block, and do not emit loose Evidence/Handoff markdown as extra transport payloads.',
25
+ '- `handoff` uses continuation state plus the latest qualified authored Handoff to manufacture the canonical return carrier and excludes runtime-only `.tiinex` state. A received `--package-parent` continues carrier lineage only; it never selects parent Workspace source by itself. Advanced manufacture may opt into exact parent snapshots with `--package-parent-workspaces <id,...|all>` and may bind an explicitly selected renamed predecessor with `--package-parent-workspace-aliases`; aliases never infer source identity. Recovery/integration manufacture may require a full `reconcile --full` receipt with `--require-reconciliation-proof --reconciliation-proof <receipt.json>`; manufacture re-enumerates source and fails closed on missing/stale proof. Normal operator completion is one Handoff package plus the exact routing text; markdown-capable hosts render that routing in a fenced code block, and do not emit loose Evidence/Handoff markdown as extra transport payloads.',
25
26
  '- Remote reads/writes remain explicit host concerns. Tooling operation safety does not create or revoke semantic Task/Handoff authority.',
26
27
  '',
27
28
  'Use `<common-command> --help` for focused common-path usage. Use `operations` deliberately for the advanced/internal operation catalog.'
@@ -29,6 +30,18 @@ export function portableCliHelpText(commandPrefix = '', surfaceCommand = '') {
29
30
  }
30
31
 
31
32
  function commonCommandHelp(command, surfaceCommand) {
33
+ if (surfaceCommand === 'reconcile' || surfaceCommand === 'prove-source-reconciliation') return [
34
+ 'Tiinex portable tooling — prove source reconciliation for manufacture',
35
+ '',
36
+ `${command} reconcile --base-kind <local-workspace|local-frontier|handoff-package> --base <path> --incoming-kind <kind> --incoming <path> --current-kind <kind> --current <path> --reconciled-kind <kind> --reconciled <path> [side ids/selectors/roots] [--dispositions <json-file>]`,
37
+ '',
38
+ 'The gate deterministically classifies exact, incoming-only, current-only, same-result-concurrent, deletion-candidate, and conflicting-overlap paths. Deletion/conflict candidates remain blocked until an explicit disposition selects `incoming`, `current`, `base`, `reconciled`, or `delete`. `reconciled` explicitly accepts the candidate bytes at that path and therefore supports a separately decided manual semantic resolution without Tooling deciding that resolution.',
39
+ 'The candidate reconciled frontier must preserve every automatically accepted incoming-only/current-only source identity and match every explicit disposition. Extra ungrounded candidate paths fail closed. Local source uses the same enumeration/exclusion contract as Handoff manufacture.',
40
+ 'Default output is compact. Use `--full` when saving a proof for `handoff --require-reconciliation-proof --reconciliation-proof <receipt.json>`; manufacture re-enumerates the Workspace and rejects stale or mismatched candidate bytes.',
41
+ 'This is mechanical byte/path proof only: no semantic merge correctness, intentional deletion, authority, acceptance, remote acquisition, or source mutation is inferred.',
42
+ '',
43
+ `Advanced/internal catalog: ${command} operations`
44
+ ];
32
45
  if (surfaceCommand === 'compare' || surfaceCommand === 'compare-source-frontiers') return [
33
46
  'Tiinex portable tooling — compare source frontiers',
34
47
  '',
@@ -70,7 +83,7 @@ function commonCommandHelp(command, surfaceCommand) {
70
83
  '',
71
84
  `${command} handoff <workspace-dir>`,
72
85
  '',
73
- 'Infers the latest qualified authored Handoff, selected Workspace identity/target, received package parent as carrier-lineage evidence, canonical projected filename, and return output directory. A non-major continuation that writes a carrier must supply an explicitly coordinated `--package-sibling-index <1..9999>`; Tooling will not silently discover the next sibling because isolated runtimes cannot prove a globally unique reservation. Distinct explicit sibling indexes preserve same-Major parallelism, while byte-identical duplicate transport at one exact output path is idempotent and divergent bytes fail closed. It does not implicitly carry sibling Workspaces from the received package; advanced manufacture must select exact reusable parent snapshots with `--package-parent-workspaces <id,...|all>`. The default receipt keeps output identity, routing text, closure/workspace qualification, verification, and actionable findings compact; add `--full` for the complete manufacture receipt. Normal operator completion is exactly one Handoff package plus the adjacent exact routing text. In markdown-capable hosts render that routing in a fenced code block; do not emit canonical Workspace Evidence/Handoff markdown as additional loose transport files. Runtime-only `.tiinex` state is excluded from canonical manufacture.',
86
+ 'Infers the latest qualified authored Handoff, selected Workspace identity/target, received package parent as carrier-lineage evidence, canonical projected filename, and return output directory. A non-major continuation that writes a carrier must supply an explicitly coordinated `--package-sibling-index <1..9999>`; Tooling will not silently discover the next sibling because isolated runtimes cannot prove a globally unique reservation. Distinct explicit sibling indexes preserve same-Major parallelism, while byte-identical duplicate transport at one exact output path is idempotent and divergent bytes fail closed. It does not implicitly carry sibling Workspaces from the received package; advanced manufacture must select exact reusable parent snapshots with `--package-parent-workspaces <id,...|all>`. Recovery/integration manufacture can bind the reconciled byte proof with `--require-reconciliation-proof --reconciliation-proof <full-reconcile-receipt.json>`; missing, non-ready, edited, or source-stale proof blocks package output. The default receipt keeps output identity, routing text, closure/workspace qualification, verification, and actionable findings compact; add `--full` for the complete manufacture receipt. Normal operator completion is exactly one Handoff package plus the adjacent exact routing text. In markdown-capable hosts render that routing in a fenced code block; do not emit canonical Workspace Evidence/Handoff markdown as additional loose transport files. Runtime-only `.tiinex` state is excluded from canonical manufacture.',
74
87
  '',
75
88
  `Advanced/internal catalog: ${command} operations`
76
89
  ];
@@ -425,7 +425,7 @@ function withCliPhaseTiming(result = {}, timing = {}) {
425
425
  function parseArgs(argv=[]) {
426
426
  const args=[...argv],first=args.shift()||'';
427
427
  if(first==='--help'||first==='-h') return {command:'help',flags:{help:true},positionals:[]};
428
- const command=({orient:'orient-handoff-package',ground:'project-grounding-readiness',receive:'qualify-cold-start',validate:'audit-handoff-package-context',handoff:'manufacture-handoff-package',author:'author',compare:'compare-source-frontiers'})[first]||first;
428
+ const command=({orient:'orient-handoff-package',ground:'project-grounding-readiness',receive:'qualify-cold-start',validate:'audit-handoff-package-context',handoff:'manufacture-handoff-package',author:'author',compare:'compare-source-frontiers',reconcile:'prove-source-reconciliation'})[first]||first;
429
429
  const flags={},positionals=[];
430
430
  while(args.length){const token=args.shift();if(!token.startsWith('--')){positionals.push(token);continue;}const key=token.slice(2);flags[key]=!args.length||args[0].startsWith('--')?true:args.shift();}
431
431
  return {command,flags,positionals,surfaceCommand:first};
@@ -9,20 +9,20 @@ export async function prepareSourceFrontierComparisonCliInput(parsed = {}, flags
9
9
  };
10
10
  if (threeWay) {
11
11
  const request = {
12
- base: descriptorFromFlags('base', flags),
13
- incoming: descriptorFromFlags('incoming', flags),
14
- current: descriptorFromFlags('current', flags)
12
+ base: sourceFrontierDescriptorFromFlags('base', flags),
13
+ incoming: sourceFrontierDescriptorFromFlags('incoming', flags),
14
+ current: sourceFrontierDescriptorFromFlags('current', flags)
15
15
  };
16
16
  return { input: await prepareNodeSourceFrontierComparisonInput(request, descriptorOptions), options: {} };
17
17
  }
18
18
  const request = {
19
- left: descriptorFromFlags('left', flags),
20
- right: descriptorFromFlags('right', flags)
19
+ left: sourceFrontierDescriptorFromFlags('left', flags),
20
+ right: sourceFrontierDescriptorFromFlags('right', flags)
21
21
  };
22
22
  return { input: await prepareNodeSourceFrontierComparisonInput(request, descriptorOptions), options: {} };
23
23
  }
24
24
 
25
- function descriptorFromFlags(prefix, flags) {
25
+ export function sourceFrontierDescriptorFromFlags(prefix, flags) {
26
26
  const kind = String(flags[`${prefix}-kind`] || '').trim();
27
27
  const root = String(flags[prefix] || '').trim();
28
28
  const workspaceId = String(flags[`${prefix}-id`] || flags[`${prefix}-workspace-id`] || '').trim();
@@ -0,0 +1,18 @@
1
+ import { prepareNodeSourceReconciliationProofInput } from '../node/sourceFrontierReconciliationProof.js';
2
+ import { sourceFrontierDescriptorFromFlags } from './cli.source-frontier-comparison.js';
3
+
4
+ export async function prepareSourceFrontierReconciliationCliInput(flags = {}, dispositions = []) {
5
+ const descriptorOptions = {
6
+ maxFiles: flags['max-files'],
7
+ maxCarrierFiles: flags['max-carrier-files'],
8
+ maxTextBytes: flags['max-text-bytes']
9
+ };
10
+ const request = {
11
+ base: sourceFrontierDescriptorFromFlags('base', flags),
12
+ incoming: sourceFrontierDescriptorFromFlags('incoming', flags),
13
+ current: sourceFrontierDescriptorFromFlags('current', flags),
14
+ reconciled: sourceFrontierDescriptorFromFlags('reconciled', flags),
15
+ dispositions: Array.isArray(dispositions) ? dispositions : dispositions?.dispositions || []
16
+ };
17
+ return { input: await prepareNodeSourceReconciliationProofInput(request, descriptorOptions), options: {} };
18
+ }
@@ -0,0 +1,18 @@
1
+ import { buildToolingBootstrapTransportFiles } from './handoff.manufacture.bootstrap.js';
2
+ import { normalizeHandoffCarrierLineage } from '../../handoff/carrierLineage.js';
3
+ import { normalizeHandoffCarrierProfile } from '../../handoff/carrierProfile.js';
4
+
5
+ export async function prepareNodeBootstrapCarrierManufacturingInput(input = {}, options = {}) {
6
+ const toolingBootstrap = await buildToolingBootstrapTransportFiles({
7
+ delivery: input.toolingBootstrap || input.bootstrapDelivery || 'embedded',
8
+ runtimeRoot: input.runtimeRoot || options.runtimeRoot,
9
+ expected: input.expectedToolingBootstrap || null,
10
+ maxFiles: input.bootstrapMaxFiles || options.bootstrapMaxFiles
11
+ });
12
+ return Object.freeze({
13
+ carrierMode: 'bootstrap', createdAt: String(input.createdAt || ''), workspaceMaterializations: Object.freeze([]), workspaceTargets: Object.freeze([]),
14
+ additionalTransportFiles: toolingBootstrap.files,
15
+ carrierLineage: normalizeHandoffCarrierLineage(input.carrierLineage || null), carrierProfile: normalizeHandoffCarrierProfile(input.carrierProfile || null),
16
+ toolingBootstrap: toolingBootstrap.summary, manufacturingEvidence: Object.freeze({ toolingBootstrap: toolingBootstrap.summary }), verifyRoundtrip: input.verifyRoundtrip !== false
17
+ });
18
+ }