@tiinex/core 0.28.0 → 0.30.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 (23) hide show
  1. package/package.json +5 -5
  2. package/src/schemas/creation.contracts.js +6 -2
  3. package/src/schemas/creation.representation.js +2 -1
  4. package/src/tooling/portable/adapters/cli/cli.command-input.js +1 -0
  5. package/src/tooling/portable/adapters/cli/cli.handoff-manufacture.js +4 -0
  6. package/src/tooling/portable/adapters/cli/cli.handoff-sibling-allocation.js +54 -1
  7. package/src/tooling/portable/adapters/cli/cli.help.js +3 -3
  8. package/src/tooling/portable/adapters/node/handoff.manufacture.js +1 -1
  9. package/src/tooling/portable/adapters/node/handoff.manufacture.scope.js +81 -8
  10. package/src/tooling/portable/draft/draft.exact.js +6 -2
  11. package/src/tooling/portable/editor/authoring.parent.js +15 -2
  12. package/src/tooling/portable/grounding/grounding.participantAuthority.js +1 -1
  13. package/src/tooling/portable/grounding/grounding.readiness.authority.js +7 -1
  14. package/src/tooling/portable/grounding/grounding.readiness.js +5 -5
  15. package/src/tooling/portable/handoff/carrierProjection.routeQualification.js +15 -3
  16. package/src/tooling/portable/handoff/coldStartQualification.grounding.js +84 -56
  17. package/src/tooling/portable/handoff/coldStartQualification.materials.js +2 -1
  18. package/src/tooling/portable/handoff/recipientV2.endpointRolePointers.js +14 -1
  19. package/src/tooling/portable/handoff/recipientV2.inspect.projection.js +1 -1
  20. package/src/tooling/portable/handoff/recipientV2.packageV1.build.js +2 -2
  21. package/src/tooling/portable/handoff/recipientV2.topology.js +14 -14
  22. package/src/tooling/portable/materialization/epistemic.plan.js +21 -7
  23. package/src/transitions/record.transitions.js +20 -11
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiinex/core",
3
- "version": "0.28.0",
3
+ "version": "0.30.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,
@@ -173,12 +173,12 @@
173
173
  "type": "git",
174
174
  "url": "git+https://github.com/Tiinex/core.git"
175
175
  },
176
- "gitHead": "b2f8dfc75545e80da0e9bedfe428e191db131c30",
176
+ "gitHead": "5d51273d4c10f8a15367c32bcef8bbce11484ca8",
177
177
  "tiinexRelease": {
178
178
  "policy": "tiinex.master-npm-release.v1",
179
- "sourceCommit": "b2f8dfc75545e80da0e9bedfe428e191db131c30",
180
- "sourceTree": "f9c5c0634c6dee60d0cc0b5b59b76f8cbb291729",
179
+ "sourceCommit": "5d51273d4c10f8a15367c32bcef8bbce11484ca8",
180
+ "sourceTree": "1372b29b40dbee5e11d2a52d9cac7731f41941a2",
181
181
  "repository": "Tiinex/core",
182
- "previousVersion": "0.27.0"
182
+ "previousVersion": "0.29.0"
183
183
  }
184
184
  }
@@ -173,7 +173,10 @@ export function validateArtifactCreationResult(draft = {}, parentRecord = {}, op
173
173
 
174
174
  if (rootCreation) findings.push(...validateRootCreationRepresentation(draft.markdown || '', contract));
175
175
  else if (parentExpected) {
176
- const relativeReference = relativePath(dirname(options.childPath || draft.path || ''), parentRecord.path || '');
176
+ const recoveryMode = String(parentRecord?.recoveryMode || parentRecord?.parentRecoveryMode || '').trim();
177
+ const relativeReference = recoveryMode === 'workspace-qualified'
178
+ ? String(parentRecord.relativeReference || parentRecord.path || '')
179
+ : relativePath(dirname(options.childPath || draft.path || ''), parentRecord.path || '');
177
180
  const parentIntegrityTarget = qualifiedParentIntegrityTarget(parentRecord, relativeReference);
178
181
  const representation = qualifyContinuationCreationRepresentation(draft.markdown || '', contract, parentRecord, { relativeReference, parentIntegrityTarget });
179
182
  findings.push(...(representation.findings || []).map((message, index) => error(`creation.continuation-representation.${index + 1}`, message)));
@@ -221,7 +224,8 @@ function qualifiedParentIntegrityTarget(parentRecord = {}, relativeReference = '
221
224
  const published = parentRecord?.publishedReference || parentRecord?.browseGitReference || parentRecord?.browseGit || null;
222
225
  const target = typeof published === 'string' ? '' : String(published?.target || published?.url || '');
223
226
  const state = typeof published === 'string' ? 'unresolved' : String(published?.state || published?.resolutionState || 'unresolved');
224
- const recoveryMode = String(parentRecord?.recoveryMode || parentRecord?.parentRecoveryMode || '').trim() === 'external-versioned' ? 'external-versioned' : 'local-relative';
227
+ const declaredRecoveryMode = String(parentRecord?.recoveryMode || parentRecord?.parentRecoveryMode || '').trim();
228
+ const recoveryMode = declaredRecoveryMode === 'external-versioned' ? 'external-versioned' : declaredRecoveryMode === 'workspace-qualified' ? 'workspace-qualified' : 'local-relative';
225
229
  if (recoveryMode === 'external-versioned') return state === 'qualified' && target ? target : '';
226
230
  return state === 'qualified' && target ? target : String(relativeReference || '');
227
231
  }
@@ -217,7 +217,8 @@ export function qualifyContinuationCreationRepresentation(markdown = '', contrac
217
217
  const publishedTarget = typeof publishedReference === 'string' ? '' : String(publishedReference?.target || publishedReference?.url || '');
218
218
  const publishedState = typeof publishedReference === 'string' ? 'unresolved' : String(publishedReference?.state || publishedReference?.resolutionState || 'unresolved');
219
219
  const publishedQualified = Boolean(publishedTarget && publishedState === 'qualified');
220
- const recoveryMode = String(parentRecord?.recoveryMode || parentRecord?.parentRecoveryMode || '').trim() === 'external-versioned' ? 'external-versioned' : 'local-relative';
220
+ const declaredRecoveryMode = String(parentRecord?.recoveryMode || parentRecord?.parentRecoveryMode || '').trim();
221
+ const recoveryMode = declaredRecoveryMode === 'external-versioned' ? 'external-versioned' : declaredRecoveryMode === 'workspace-qualified' ? 'workspace-qualified' : 'local-relative';
221
222
  if (recoveryMode === 'external-versioned') {
222
223
  if (!publishedQualified) findings.push('External Parent recovery requires one qualified version-stable published representation.');
223
224
  if (origins.length !== 1 || relative.length !== 0 || browse.length !== 1) findings.push('External Parent Origin must contain exactly one [browse + git](...) entry and must not fabricate [relative](...).');
@@ -182,6 +182,7 @@ export async function commandInput(parsed, runtime = {}) {
182
182
  options: {}
183
183
  };
184
184
  if(parsed.command==='project-workspace-landing')return land(flags,material,readOptionalJson,splitFlag);
185
+ if (parsed.command === 'project-authoring-parent') return { input: { ...material, reference: flags.reference || '' }, options: {} };
185
186
  const operatorBridgeInput = await prepareOperatorBridgeCliInput(parsed.command, material, flags, readOptionalJson);
186
187
  if (operatorBridgeInput) return operatorBridgeInput;
187
188
  if (parsed.command === 'project-editor-assistance') return prepareEditorAssistanceCliInput(material, flags);
@@ -20,6 +20,7 @@ export async function prepareHandoffManufactureCliCommand(parsed = {}, runtime =
20
20
  if (!['handoff', 'workspace', 'bootstrap'].includes(carrierMode)) throw new Error(`portable.cli.handoff-carrier.carrier-mode.invalid:${carrierMode}`);
21
21
  if (carrierMode === 'workspace') return prepareWorkspaceCarrierCliCommand(flags, workspaceRoot, runtime);
22
22
  if (carrierMode === 'bootstrap') return prepareBootstrapCarrierCliCommand(flags, runtime);
23
+ if (flags['package-major'] && flags['package-consolidation']) throw new Error('portable.cli.handoff-carrier.package-major-consolidation-conflict');
23
24
  const continuationState = parsed.surfaceCommand === 'handoff'
24
25
  ? await readGroundContinuationState(workspaceRoot)
25
26
  : {};
@@ -103,6 +104,7 @@ export async function prepareHandoffManufactureCliCommand(parsed = {}, runtime =
103
104
  selectedRoutePointer: continuationState.selectedRoutePointer || flags['package-parent-route-pointer'] || '',
104
105
  selectedRouteId: continuationState.selectedRouteId || flags['package-parent-route-id'] || '',
105
106
  explicitSiblingIndex: flags['package-sibling-index'],
107
+ consolidation: Boolean(flags['package-consolidation']),
106
108
  enabled: Boolean(flags.output || flags['output-dir'])
107
109
  });
108
110
  carrierAllocation = siblingAllocation;
@@ -118,6 +120,8 @@ export async function prepareHandoffManufactureCliCommand(parsed = {}, runtime =
118
120
  packageParentSha256 = String(carrierLineage.parentPackageSha256 || '');
119
121
  } else if (flags['package-major']) {
120
122
  throw new Error('portable.cli.handoff-carrier.package-major.parent-required');
123
+ } else if (flags['package-consolidation']) {
124
+ throw new Error('portable.cli.handoff-carrier.package-consolidation.parent-required');
121
125
  }
122
126
  const carrierProfile = selectCarrierProfile({
123
127
  operator: operatorCarrierProfile,
@@ -97,10 +97,62 @@ export function deriveHandoffSiblingAllocation({ parentInspection = null, select
97
97
  }
98
98
  }
99
99
 
100
+ export function deriveHandoffConsolidationAllocation({ parentInspection = null, explicitSiblingIndex = null, parentPackagePath = '', parentPackageSha256 = '', parentDimension = '' } = {}) {
101
+ const explicit = normalizeSiblingIndex(explicitSiblingIndex);
102
+ const inspection = parentInspection && typeof parentInspection === 'object' ? parentInspection : null;
103
+ if (!inspection || inspection.detected === false) return freeze({
104
+ state: 'unavailable', siblingIndex: null, allocationMode: 'unavailable',
105
+ reasonCode: 'qualified-parent-route-topology-unavailable-for-consolidation',
106
+ provenance: provenanceBase({ parentPackagePath, parentPackageSha256, parentDimension, explicitSiblingIndex: explicit }),
107
+ boundary: consolidationBoundary()
108
+ });
109
+ if (String(inspection.status || '') !== 'valid') return consolidationBlocked('qualified-parent-route-topology-invalid-for-consolidation');
110
+ const routes = [...(inspection.routes || [])];
111
+ if (!routes.length) return consolidationBlocked('qualified-parent-route-topology-empty-for-consolidation');
112
+
113
+ const topology = deriveHandoffSiblingAllocation({
114
+ parentInspection: inspection,
115
+ selectedRoutePointer: String(routes[0]?.pointerPath || ''),
116
+ parentPackagePath, parentPackageSha256, parentDimension
117
+ });
118
+ if (topology.state !== 'qualified') return consolidationBlocked(topology.reasonCode || 'qualified-parent-route-pointer-order-unresolved');
119
+ const routeCount = Number(topology.provenance?.qualifiedRouteCount || 0);
120
+ const siblingIndex = routeCount + 1;
121
+ if (!Number.isInteger(siblingIndex) || siblingIndex < 2 || siblingIndex > MAX_SIBLING_INDEX) return consolidationBlocked('derived-consolidation-sibling-index-out-of-range');
122
+ if (explicit && explicit !== siblingIndex) return consolidationBlocked('explicit-sibling-index-conflicts-with-qualified-consolidation-topology', { expectedSiblingIndex: siblingIndex });
123
+
124
+ return freeze({
125
+ state: 'qualified', siblingIndex,
126
+ childDimension: parentDimension ? `${String(parentDimension).trim()}-${siblingIndex}` : '',
127
+ allocationMode: 'qualified-parent-route-consolidation-ordinal',
128
+ explicitOverride: explicit ? 'matched-derived-value' : 'not-supplied',
129
+ reasonCode: '',
130
+ provenance: {
131
+ ...provenanceBase({ parentPackagePath, parentPackageSha256, parentDimension, explicitSiblingIndex: explicit }),
132
+ basis: 'qualified-parent-route-consolidation-ordinal',
133
+ qualifiedRouteCount: routeCount,
134
+ consolidationOrdinal: siblingIndex,
135
+ commonFrontierDimension: String(parentDimension || '').trim(),
136
+ pointerOrder: topology.provenance.pointerOrder
137
+ },
138
+ boundary: consolidationBoundary()
139
+ });
140
+
141
+ function consolidationBlocked(reasonCode, extra = {}) {
142
+ return freeze({
143
+ state: 'blocked', siblingIndex: null, allocationMode: 'blocked', reasonCode, ...extra,
144
+ provenance: provenanceBase({ parentPackagePath, parentPackageSha256, parentDimension, explicitSiblingIndex: explicit }),
145
+ boundary: consolidationBoundary()
146
+ });
147
+ }
148
+ }
149
+
100
150
  export async function resolveHandoffSiblingAllocation(input = {}) {
101
- const derived = deriveHandoffSiblingAllocation(input);
151
+ const consolidation = input.consolidation === true;
152
+ const derived = consolidation ? deriveHandoffConsolidationAllocation(input) : deriveHandoffSiblingAllocation(input);
102
153
  if (derived.state === 'qualified') return derived;
103
154
  if (derived.state === 'blocked') throw new Error(`portable.cli.handoff-carrier.sibling-allocation.${derived.reasonCode}`);
155
+ if (consolidation) throw new Error('portable.cli.handoff-carrier.sibling-allocation.qualified-parent-route-topology-required-for-consolidation');
104
156
  const explicit = normalizeSiblingIndex(input.explicitSiblingIndex ?? input.siblingIndex);
105
157
  if (!explicit) throw new Error('portable.cli.handoff-carrier.sibling-allocation.explicit-index-required-when-topology-unavailable');
106
158
  const legacy = await reserveHandoffSiblingIndex({
@@ -172,6 +224,7 @@ function provenanceBase({ parentPackagePath = '', parentPackageSha256 = '', pare
172
224
  explicitSiblingIndex: explicitSiblingIndex ? normalizeSiblingIndex(explicitSiblingIndex) : null
173
225
  };
174
226
  }
227
+ function consolidationBoundary() { return 'Transport-only carrier consolidation allocation. The consolidation sibling is exactly N+1 for N qualified Handoff routes on the common pre-batch carrier frontier. It is never derived from one specialist return, arrival order, retries, output collisions, local allocation files or artifact Parent lineage; explicit Major stabilization remains separate.'; }
175
228
  function allocationBoundary() { return 'Transport-only carrier allocation. A non-Major sibling ordinal may come only from exact qualified package-local Handoff Pointer order for the selected parent route, or from an explicit advanced override when such topology is unavailable. Allocation never creates semantic Parent, Workspace, Role, acceptance, completion, participant, process, or source authority; different carrier prefixes are not coordinated.'; }
176
229
  function normalizeSiblingIndex(value) {
177
230
  if (value === null || value === undefined || value === '') return 0;
@@ -63,9 +63,9 @@ function commonCommandHelp(command, surfaceCommand) {
63
63
  `${command} ground <handoff-package.zip> --route <Continue-from> [--holder-role <recipient-role>]`,
64
64
  `${command} ground <handoff-package.zip> --route <Continue-from> --holder-role <recipient-role> --continue <workspace-dir>`,
65
65
  '',
66
- 'Reads and qualifies the exact selected Handoff route. The default projection keeps readiness, recipient authority boundary, explicit consuming-session holder binding, Required Context closure, continuity/blockers, current Task identity, and exact next action compact; add `--full` for the full qualified receipt.',
66
+ 'Reads and qualifies the exact selected Handoff route. The default projection keeps readiness, recipient authority boundary, consuming-session holder binding, Required Context closure, continuity/blockers, current Task identity, and exact next action compact; add `--full` for the full qualified receipt.',
67
67
  'Add `--include-required-context <requirement-id,name|all>` and/or `--include-current-work` only when exact body text is needed. `ground --continue` includes the bounded current Task body needed to proceed, retains Required Context counts and continuity/recovery state, and does not repeat qualified Required Context item paths or root-detail receipts unless explicitly requested (or `--full` is used).',
68
- 'For a Role recipient, `--holder-role <recipient-role>` is an explicit consuming-session Role-capacity binding; it is never inferred from route selection, provider identity, or assistant/user position. Without it, the holder remains unresolved and grounding stays discussion-only. After `grounded-to-act`, `--continue` materializes the selected carried Workspace into an empty local directory and writes runtime-only `.tiinex/continuation.json`. The grounding operation itself is non-mutating; downstream work authority comes from qualified Handoff/Task/Role artifacts, not from that operation-safety fact.',
68
+ 'For a Role recipient, exact qualified consumption of the selected Handoff may establish the bounded consuming-session Role-capacity binding when the exact qualified recipient Role authorizes canonical Assignment Mode `handoff`. `--holder-role <recipient-role>` remains an explicit consuming-session assertion and must match the selected recipient; mismatches block and never fall back to Handoff assignment. No binding is inferred from package delivery, route orientation alone, provider identity, or assistant/user position. After `grounded-to-act`, `--continue` materializes the selected carried Workspace into an empty local directory and writes runtime-only `.tiinex/continuation.json`. The grounding operation itself is non-mutating; downstream work authority comes from qualified Handoff/Task/Role artifacts, not from that operation-safety fact.',
69
69
  '',
70
70
  `Advanced/internal catalog: ${command} operations`
71
71
  ];
@@ -83,7 +83,7 @@ function commonCommandHelp(command, surfaceCommand) {
83
83
  '',
84
84
  `${command} handoff <workspace-dir>`,
85
85
  '',
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. Ordinary non-Major continuation derives its sibling ordinal from the exact qualified selected Handoff Pointer order in the received parent carrier, so common-path `handoff` does not require `--package-sibling-index` or `--return-package-sibling-index`. A single selected Pointer derives `-1`; parallel qualified Pointers derive dense local ordinals in Pointer order, and each returned branch continues from its own carrier. `--package-sibling-index` remains an advanced compatibility override only when qualified route topology is unavailable, and must match when topology already proves the value; conflicting or ambiguous topology fails closed. `--return-package-major` remains the explicit Major-return declaration. Different carrier prefixes are never globally coordinated. 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.',
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. Ordinary non-Major continuation derives its sibling ordinal from the exact qualified selected Handoff Pointer order in the received parent carrier, so common-path `handoff` does not require `--package-sibling-index` or `--return-package-sibling-index`. A single selected Pointer derives `-1`; parallel qualified Pointers derive dense local ordinals in Pointer order, and each returned branch continues from its own carrier. Advanced manufacture may declare `--package-consolidation` against the common pre-batch parent carrier; with N qualified route Pointers it derives consolidation ordinal N+1 and rejects conflicting overrides or missing qualified common-frontier topology. `--package-sibling-index` remains an advanced compatibility override only when qualified route topology is unavailable for ordinary route returns, and must match when topology already proves the value; conflicting or ambiguous topology fails closed. `--return-package-major` remains the explicit Major-return declaration. Different carrier prefixes are never globally coordinated. 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.',
87
87
  '',
88
88
  `Advanced/internal catalog: ${command} operations`
89
89
  ];
@@ -181,7 +181,7 @@ export async function prepareNodeHandoffManufacturingInput(input = {}, options =
181
181
  const dependencyClosure = await expandPointerDependencyClosure({ requirements, materials, workspaceRuntimeById, bindings: input.materialBindings || {} });
182
182
  requirements = dependencyClosure.requirements;
183
183
  materials = appendMissingRequirementMaterials(dependencyClosure.materials, resolvePackageParentRequirementMaterials(requirements, packageParentExactMaterialProvider));
184
- const routeParentBoundaryClosure = expandRouteParentBoundaryClosure({ requirements, materials, workspaceMaterializations, workspaceRuntimeById, routeSpecs });
184
+ const routeParentBoundaryClosure = expandRouteParentBoundaryClosure({ requirements, materials, workspaceMaterializations, workspaceRuntimeById, routeSpecs, exactMaterialProvider: packageParentExactMaterialProvider });
185
185
  requirements = routeParentBoundaryClosure.requirements;
186
186
  materials = appendMissingRequirementMaterials(routeParentBoundaryClosure.materials, resolvePackageParentRequirementMaterials(requirements, packageParentExactMaterialProvider));
187
187
  const parentBoundaryClosure = expandBoundedParentBoundaryClosure({ requirements, materials, workspaceMaterializations, workspaceRuntimeById });
@@ -166,10 +166,9 @@ export function expandRouteParentBoundaryClosure(input = {}) {
166
166
  const visitKey = `${currentWorkspaceId}\0${currentPath}`;
167
167
  if (visited.has(visitKey)) break;
168
168
  visited.add(visitKey);
169
- const runtime = input.workspaceRuntimeById?.get(currentWorkspaceId);
170
- const currentEntry = runtime ? entryFromEnumeration(runtime.enumeration, currentPath) : null;
171
- if (!currentEntry) break;
172
- const markdown = decodeUtf8(currentEntry.data);
169
+ const currentSource = exactArtifactSource(input, materials, currentWorkspaceId, currentPath);
170
+ if (!currentSource) break;
171
+ const markdown = decodeUtf8(currentSource.data);
173
172
  if (!markdown) break;
174
173
  let parent;
175
174
  try { parent = parseArtifactMarkdown(markdown).envelope?.parent || {}; } catch { break; }
@@ -188,8 +187,7 @@ export function expandRouteParentBoundaryClosure(input = {}) {
188
187
  }
189
188
  if (!targetWorkspaceId || !targetPath) break;
190
189
 
191
- const targetRuntime = input.workspaceRuntimeById?.get(targetWorkspaceId);
192
- const targetEntry = targetRuntime ? entryFromEnumeration(targetRuntime.enumeration, targetPath) : null;
190
+ const targetSource = exactArtifactSource(input, materials, targetWorkspaceId, targetPath);
193
191
  const targetMaterialization = materializationByWorkspaceId.get(targetWorkspaceId);
194
192
  const targetAlreadyCarried = workspaceMaterializationIncludesPath(targetMaterialization, targetPath);
195
193
  const routeTargetKey = `${routeWorkspaceId}\0${routePath}\0${targetWorkspaceId}\0${targetPath}`;
@@ -217,10 +215,10 @@ export function expandRouteParentBoundaryClosure(input = {}) {
217
215
  fields: Object.freeze({ RouteWorkspace: routeWorkspaceId, RoutePath: routePath, SourceWorkspace: currentWorkspaceId, SourcePath: currentPath, TargetWorkspace: targetWorkspaceId, TargetPath: targetPath, Reference: reference })
218
216
  });
219
217
  dependencies.push(requirement);
220
- if (targetEntry) materials.push(materialCandidateFromWorkspaceEntry(requirement, targetWorkspaceId, targetPath, targetEntry, targetRuntime.enumeration, targetRuntime));
218
+ if (targetSource) materials.push(materialCandidateFromExactArtifactSource(requirement, targetSource));
221
219
  }
222
220
  }
223
- if (!targetEntry) break;
221
+ if (!targetSource) break;
224
222
  currentWorkspaceId = targetWorkspaceId;
225
223
  currentPath = targetPath;
226
224
  }
@@ -232,6 +230,81 @@ export function expandRouteParentBoundaryClosure(input = {}) {
232
230
  });
233
231
  }
234
232
 
233
+ function exactArtifactSource(input = {}, materials = [], workspaceIdValue = '', targetPathValue = '') {
234
+ const workspaceId = String(workspaceIdValue || '').trim();
235
+ const targetPath = normalizeRelativePath(targetPathValue);
236
+ if (!workspaceId || !targetPath) return null;
237
+
238
+ // Explicit current/inherited Workspace providers retain precedence over detached cache
239
+ // material. This preserves the existing package-parent precedence boundary while allowing
240
+ // the exact same route traversal to continue when the next ancestor is cache-only.
241
+ const runtime = input.workspaceRuntimeById?.get(workspaceId);
242
+ const entry = runtime ? entryFromEnumeration(runtime.enumeration, targetPath) : null;
243
+ if (entry) return Object.freeze({
244
+ kind: 'workspace-entry', workspaceId, path: targetPath, data: entry.data, bytes: Number(entry.bytes || 0), sha256: String(entry.sha256 || ''),
245
+ mediaType: String(entry.mediaType || 'text/markdown'), entry, runtime
246
+ });
247
+
248
+ const candidates = [];
249
+ for (const material of materials || []) {
250
+ const provenance = material?.provenance || {};
251
+ const candidateWorkspaceId = String(provenance.workspaceId || material.workspaceId || material.targetWorkspaceId || '').trim();
252
+ const candidatePath = normalizeRelativePath(provenance.path || material.path || material.targetPath || '');
253
+ if (candidateWorkspaceId !== workspaceId || candidatePath !== targetPath || !material?.data) continue;
254
+ candidates.push(Object.freeze({
255
+ kind: 'qualified-material', workspaceId, path: targetPath, data: material.data, bytes: Number(material.bytes || 0), sha256: String(material.sha256 || ''),
256
+ mediaType: String(material.mediaType || 'text/markdown'), providerId: String(material.providerId || ''), providerKind: String(material.providerKind || ''),
257
+ provenance: Object.freeze({ ...provenance }), authority: Object.freeze({ ...(material.authority || {}) })
258
+ }));
259
+ }
260
+ for (const material of input.exactMaterialProvider?.entries || []) {
261
+ const provenance = material?.provenance || {};
262
+ const candidateWorkspaceId = String(provenance.workspaceId || '').trim();
263
+ const candidatePath = normalizeRelativePath(provenance.path || '');
264
+ if (candidateWorkspaceId !== workspaceId || candidatePath !== targetPath || !material?.data) continue;
265
+ candidates.push(Object.freeze({
266
+ kind: 'qualified-material', workspaceId, path: targetPath, data: material.data, bytes: Number(material.bytes || 0), sha256: String(material.sha256 || ''),
267
+ mediaType: String(material.mediaType || 'text/markdown'), providerId: String(material.providerId || 'qualified-package-parent-exact-material'),
268
+ providerKind: String(material.providerKind || 'qualified-package-parent-cache-material'), provenance: Object.freeze({ ...provenance }),
269
+ authority: Object.freeze({ packageParentMaterialQualified: true, semanticAuthority: 'none', sourceSelectionAuthority: false })
270
+ }));
271
+ }
272
+ if (!candidates.length) return null;
273
+ const digests = [...new Set(candidates.map((candidate) => String(candidate.sha256 || '')).filter(Boolean))];
274
+ if (digests.length !== 1) return null;
275
+ return candidates.find((candidate) => candidate.sha256 === digests[0]) || null;
276
+ }
277
+
278
+ function materialCandidateFromExactArtifactSource(requirement = {}, source = {}) {
279
+ if (source.kind === 'workspace-entry') {
280
+ return materialCandidateFromWorkspaceEntry(requirement, source.workspaceId, source.path, source.entry, source.runtime.enumeration, source.runtime);
281
+ }
282
+ return Object.freeze({
283
+ requirementId: String(requirement.id || ''),
284
+ referenceTarget: String(requirement.reference?.target || requirement.referenceTarget || ''),
285
+ path: String(source.path || ''),
286
+ data: source.data,
287
+ bytes: Number(source.bytes || 0),
288
+ sha256: String(source.sha256 || ''),
289
+ mediaType: String(source.mediaType || 'text/markdown'),
290
+ providerId: String(source.providerId || 'qualified-exact-material'),
291
+ providerKind: String(source.providerKind || 'qualified-exact-material'),
292
+ provenance: Object.freeze({
293
+ ...(source.provenance || {}),
294
+ workspaceId: String(source.workspaceId || source.provenance?.workspaceId || ''),
295
+ path: String(source.path || source.provenance?.path || ''),
296
+ reboundRequirementId: String(requirement.id || ''),
297
+ reboundClassification: String(requirement.classification || '')
298
+ }),
299
+ authority: Object.freeze({
300
+ ...(source.authority || {}),
301
+ localIdentityQualified: true,
302
+ semanticAuthority: 'none',
303
+ sourceSelectionAuthority: false
304
+ })
305
+ });
306
+ }
307
+
235
308
  function workspaceMaterializationIncludesPath(materialization = null, targetPath = '') {
236
309
  if (!materialization) return false;
237
310
  const state = String(materialization.state || materialization.materialization || '');
@@ -16,6 +16,7 @@ export function normalizePortableParentRecord(parent = {}) {
16
16
  boundary: String(parent.boundary || parent.source?.boundary || ''),
17
17
  sourceMode: String(parent.sourceMode || ''),
18
18
  recoveryMode: String(parent.recoveryMode || parent.parentRecoveryMode || ''),
19
+ relativeReference: String(parent.relativeReference || ''),
19
20
  source: parent.source || null,
20
21
  markdown: String(parent.markdown || ''),
21
22
  integrity: parent.integrity || null,
@@ -108,11 +109,14 @@ export function qualifyPortableRenderedParentRepresentation(markdown = '', paren
108
109
  const observed = parsed.envelope?.parent || {};
109
110
  const hasObserved = Boolean(observed.schema?.id || observed.trace || observed.origin || observed.createdAt || observed.boundary || observed.originEntries?.length);
110
111
  if (rootCreation) return Object.freeze({ state: hasObserved ? 'invalid' : 'qualified', reason: hasObserved ? 'exact-result-parent-unexpected' : '' });
111
- const relative = relativePath(dirname(childPath), parent.path);
112
112
  const published = parent.publishedReference || {};
113
113
  const publishedTarget = String(published.target || '');
114
114
  const publishedQualified = Boolean(publishedTarget && published.state === 'qualified');
115
- const recoveryMode = String(parent.recoveryMode || '').trim() === 'external-versioned' ? 'external-versioned' : 'local-relative';
115
+ const declaredRecoveryMode = String(parent.recoveryMode || '').trim();
116
+ const recoveryMode = declaredRecoveryMode === 'external-versioned' ? 'external-versioned' : declaredRecoveryMode === 'workspace-qualified' ? 'workspace-qualified' : 'local-relative';
117
+ const relative = recoveryMode === 'workspace-qualified'
118
+ ? String(parent.relativeReference || parent.path || '')
119
+ : relativePath(dirname(childPath), parent.path);
116
120
  const schemaTarget = String(parent.schemaReferenceAuthority?.preferredTarget || '');
117
121
  const origins = observed.originEntries || [];
118
122
  const createdAtLines = renderedParentFieldLines(markdown, 'Created At');
@@ -1,5 +1,7 @@
1
1
  import { parseArtifactMarkdown } from '../../../artifacts/artifact.parse.js';
2
2
  import { auditPortableRecord } from '../audit/audit.capability.js';
3
+ import { classifyParentRecoveryReference } from '../../../lineage/parentRecoveryReference.js';
4
+ import { buildArtifactCreationContract } from '../../../schemas/creation.contracts.js';
3
5
 
4
6
  export const PORTABLE_AUTHORING_PARENT_SCHEMA_ID = 'tiinex.portable.authoring-parent.v1';
5
7
 
@@ -15,13 +17,24 @@ export function projectPortableAuthoringParent(input = {}) {
15
17
  const schemaId = String(parsed.envelope?.current?.schema?.id || audit.schemaId || '');
16
18
  const schemaTarget = String(parsed.envelope?.current?.schema?.target || '');
17
19
  const createdAt = String(parsed.envelope?.current?.createdAt || audit.artifact?.createdAt || '');
20
+ const explicitReference = String(input.reference || '').trim();
21
+ const projectedPath = explicitReference || String(record.path || record.id || '');
22
+ const referenceClassification = classifyParentRecoveryReference(projectedPath);
23
+ if (referenceClassification.kind === 'malformed-workspace-qualified') return freeze({ schema: PORTABLE_AUTHORING_PARENT_SCHEMA_ID, status: 'blocked', parentRecord: null, findings: [{ severity: 'error', code: 'portable.authoring-parent.reference.malformed', message: 'Selected Parent reference is malformed and cannot be used for native authoring.' }], operationBoundary: boundary() });
24
+ const recoveryMode = referenceClassification.kind === 'workspace-qualified' ? 'workspace-qualified' : 'local-relative';
25
+ const canonicalCurrent = buildArtifactCreationContract({ schemaId, transitionType: 'continue-from-record' })?.schemaReferences?.current || null;
26
+ const canonicalTargets = new Set([...(canonicalCurrent?.exactTargets || []), String(canonicalCurrent?.preferredTarget || '')].filter(Boolean));
27
+ const schemaReferenceQualified = Boolean(schemaTarget && canonicalCurrent?.resolutionState === 'qualified' && canonicalTargets.has(schemaTarget));
28
+ const schemaReferenceAuthority = schemaReferenceQualified
29
+ ? { ...canonicalCurrent, preferredTarget: schemaTarget, resolutionState: 'qualified', resolutionEvidence: { ...(canonicalCurrent?.resolutionEvidence || {}), basis: 'declared-parent-target-exact-canonical-match' } }
30
+ : { schemaId, preferredTarget: schemaTarget, exactTargets: schemaTarget ? [schemaTarget] : [], resolutionState: 'unresolved', evidence: { basis: 'declared-current-schema-reference-only' } };
18
31
  return freeze({
19
32
  schema: PORTABLE_AUTHORING_PARENT_SCHEMA_ID,
20
33
  status: 'ready',
21
34
  parentRecord: {
22
- id: String(record.path || record.id || ''), path: String(record.path || record.id || ''), schemaId, currentSchemaId: schemaId, currentCreatedAt: createdAt,
35
+ id: projectedPath, path: projectedPath, schemaId, currentSchemaId: schemaId, currentCreatedAt: createdAt, createdAt, recoveryMode, relativeReference: recoveryMode === 'workspace-qualified' ? projectedPath : '',
23
36
  markdown: record.markdown, sourceMode: String(record.sourceMode || 'portable-node-local'),
24
- schemaReferenceAuthority: { schemaId, preferredTarget: schemaTarget, exactTargets: schemaTarget ? [schemaTarget] : [], resolutionState: 'unresolved', evidence: { basis: 'declared-current-schema-reference-only' } }
37
+ schemaReferenceAuthority
25
38
  },
26
39
  findings: [],
27
40
  operationBoundary: boundary(),
@@ -14,7 +14,7 @@ export function projectParticipantAuthority(authority = null) {
14
14
  participantIdentityCreatesAuthority: false,
15
15
  conversationPositionCreatesAuthority: false,
16
16
  universalHumanFeedbackRule: false,
17
- boundary: 'Qualified Role material plus explicit holder binding is the basis. Identity/chat position alone create no authority; no universal human-input-as-feedback rule is imposed.'
17
+ boundary: 'Qualified Role material plus a qualified holder binding is the basis; that binding may be explicit or may come from exact qualified selected-Handoff consumption when the Role authorizes `handoff`. Identity/chat position alone create no authority; no universal human-input-as-feedback rule is imposed.'
18
18
  });
19
19
  }
20
20
  function compact(value = '', limit = 180) { const text = String(value || '').replace(/\s+/g, ' ').trim(); return text.length > limit ? `${text.slice(0, limit - 1).trimEnd()}…` : text; }
@@ -55,12 +55,18 @@ export function projectGroundingAuthority(authority, mode) {
55
55
  explicit: Boolean(authority.holderBinding.explicit),
56
56
  inferredFromTransport: Boolean(authority.holderBinding.inferredFromTransport),
57
57
  provenance: Object.freeze({
58
- basis: authority.holderBinding.explicit ? 'explicit-consuming-session-holder-binding' : 'unresolved-or-non-explicit-holder-binding',
58
+ basis: authority.holderBinding.explicit ? 'explicit-consuming-session-holder-binding' : (authority.holderBinding.source === 'qualified-selected-handoff-consumption' && String(authority.holderBinding.state || '') === 'qualified' ? 'qualified-selected-handoff-consumption-binding' : 'unresolved-or-non-explicit-holder-binding'),
59
59
  source: authority.holderBinding.source || '',
60
60
  sourceKind: authority.holderBinding.sourceDetail?.kind || '',
61
61
  sourceLocator: authority.holderBinding.sourceDetail?.locator || '',
62
62
  semanticAuthorityState: authority.holderBinding.sourceDetail?.semanticAuthorityState || 'not-established',
63
63
  qualifiedMaterialSource: Boolean(authority.holderBinding.sourceDetail?.qualifiedMaterialSource),
64
+ assignmentMode: authority.holderBinding.assertionMode || authority.holderBinding.authorization?.assignmentMode || '',
65
+ routePointerPath: authority.holderBinding.sourceDetail?.routePointerPath || '',
66
+ handoffArtifactPath: authority.holderBinding.sourceDetail?.handoffArtifactPath || '',
67
+ handoffArtifactSha256: authority.holderBinding.sourceDetail?.handoffArtifactSha256 || '',
68
+ roleArtifactPath: authority.holderBinding.sourceDetail?.roleArtifactPath || authority.holderBinding.authorization?.provenance?.roleArtifactPath || '',
69
+ roleArtifactSha256: authority.holderBinding.sourceDetail?.roleArtifactSha256 || authority.holderBinding.authorization?.provenance?.roleArtifactSha256 || '',
64
70
  authorizationState: authority.holderBinding.authorization?.state || 'unresolved',
65
71
  authorizationBasis: authority.holderBinding.authorization?.provenance?.basis || '',
66
72
  boundary: authority.holderBinding.boundary || 'Consuming-session holder identity is never inferred from route transport or recipient position.'
@@ -129,7 +129,7 @@ export function composeGroundingReadiness({ mode = 'loaded-material', authority
129
129
  } else {
130
130
  holderBindingActReady = false;
131
131
  unresolved.push(evidence('session-holder-role-binding', 'unresolved', `recipient Role ${authority?.role?.endpoint?.label || authority?.handoff?.to || 'recipient'} is qualified separately from the consuming session`));
132
- reasons.push(reason('session-holder-role-binding-unresolved', 'The selected recipient Role does not assign itself to this consuming session. Supply an explicit matching session holder Role binding before act-ready continuation.'));
132
+ reasons.push(reason('session-holder-role-binding-unresolved', 'The selected recipient Role is not yet bound to this consuming session. Exact qualified selected-Handoff consumption may bind it only when the exact recipient Role authorizes Assignment Mode `handoff`; otherwise supply an explicit matching session holder Role binding authorized by that Role.'));
133
133
  }
134
134
 
135
135
  if (holderState === 'not-applicable') {
@@ -142,7 +142,7 @@ export function composeGroundingReadiness({ mode = 'loaded-material', authority
142
142
  } else {
143
143
  holderBindingActReady = false;
144
144
  unresolved.push(evidence('session-holder-role-binding-authorization', holderAuthorizationState || 'unresolved', authority?.holderBinding?.authorization?.reasonCode || 'exact qualified canonical assignment-mode authority does not authorize the asserted binding mechanism'));
145
- reasons.push(reason('session-holder-role-binding-authorization-unresolved', 'The explicit consuming-session Role assertion matches the selected recipient Role, but exact qualified canonical assignment-mode authority does not authorize the asserted mechanism. Matching session input alone cannot make the route act-ready.'));
145
+ reasons.push(reason('session-holder-role-binding-authorization-unresolved', 'The consuming-session Role binding matches the selected recipient Role, but exact qualified canonical assignment-mode authority does not authorize the asserted mechanism. Matching session input or Handoff selection alone cannot make the route act-ready.'));
146
146
  }
147
147
  }
148
148
 
@@ -368,12 +368,12 @@ function projectCurrentWork(topology = {}, records = [], includeCurrentWork = fa
368
368
  }
369
369
 
370
370
  function nextActionFor(state, topology, continuity = {}, authority = null) {
371
- if (state === 'grounded-to-act') return Object.freeze({ kind: 'continue-bounded-handoff-work', target: topology.currentFrontier[0]?.path || '', basis: 'qualified authority + explicit session holder Role binding + exact qualified holder-assignment authorization when Role-recipient + required context + cold-start root continuity + selected-route Parent leaf + declared current-work frontier' });
372
- if (state === 'grounded-to-discuss' && String(authority?.holderBinding?.state || 'unresolved') === 'unresolved') return Object.freeze({ kind: 'declare-explicit-session-holder-role-binding', target: authority?.role?.endpoint?.label || authority?.handoff?.to || '', basis: 'recipient Role qualification is separate from consuming-session holder binding; no transport/provider/assistant-user identity inference is permitted' });
371
+ if (state === 'grounded-to-act') return Object.freeze({ kind: 'continue-bounded-handoff-work', target: topology.currentFrontier[0]?.path || '', basis: 'qualified authority + qualified session holder Role binding (explicit or exact selected-Handoff consumption) + exact qualified holder-assignment authorization when Role-recipient + required context + cold-start root continuity + selected-route Parent leaf + declared current-work frontier' });
372
+ if (state === 'grounded-to-discuss' && String(authority?.holderBinding?.state || 'unresolved') === 'unresolved') return Object.freeze({ kind: 'establish-session-holder-role-binding', target: authority?.role?.endpoint?.label || authority?.handoff?.to || '', basis: 'recipient Role qualification is separate from consuming-session holder binding; exact qualified selected-Handoff consumption may establish `handoff` assignment when authorized, otherwise an explicit authorized binding is required; no transport/provider/assistant-user identity inference is permitted' });
373
373
  if (state === 'grounded-to-discuss' && String(authority?.holderBinding?.state || '') === 'qualified' && String(authority?.holderBinding?.authorization?.state || 'unresolved') !== 'qualified') return Object.freeze({
374
374
  kind: 'resolve-session-holder-binding-authorization',
375
375
  target: authority?.holderBinding?.authorization?.provenance?.roleArtifactPath || authority?.role?.endpoint?.label || authority?.handoff?.to || '',
376
- basis: 'a matching explicit session Role assertion is not semantic authorization; exact qualified recipient Role Holder Relationship authority must establish the assignment mode'
376
+ basis: 'a matching session Role binding is not semantic authorization by itself; exact qualified recipient Role Holder Relationship authority must establish the selected assignment mode'
377
377
  });
378
378
  if (state === 'grounded-to-discuss') return Object.freeze({ kind: topology.currentFrontier.length ? 'obtain-bounded-action-authority-or-human-gate' : 'resolve-current-work-frontier', target: topology.currentTasks[0]?.path || '', basis: 'discussion-ready but act-readiness condition is unresolved' });
379
379
  if (continuity?.state === 'unproven') return Object.freeze({
@@ -129,10 +129,22 @@ function resolveDescriptorMaterial(bundle, descriptor, byteProvider, target = ''
129
129
  const expectedRouteWorkspaceId = String(routeWorkspaceId || '');
130
130
  const expectedRoutePath = normalizeWorkspacePath(routePath || '');
131
131
  const matches = (descriptor.materialized || []).filter((entry) => {
132
- if (expectedRequirementId && String(entry.requirementId || '') !== expectedRequirementId) return false;
132
+ const entryRequirementId = String(entry.requirementId || '');
133
+ const entrySourceRequirementId = String(entry.sourceRequirementId || '');
134
+ const entryRouteWorkspaceId = String(entry.routeWorkspaceId || '');
135
+ const entryRoutePath = normalizeWorkspacePath(entry.routePath || '');
136
+ const exactRequirementMatch = !expectedRequirementId || entryRequirementId === expectedRequirementId;
137
+ const routeScopedSourceMatch = Boolean(
138
+ expectedRequirementId &&
139
+ entrySourceRequirementId === expectedRequirementId &&
140
+ expectedRouteWorkspaceId && expectedRoutePath &&
141
+ entryRouteWorkspaceId === expectedRouteWorkspaceId &&
142
+ entryRoutePath === expectedRoutePath
143
+ );
144
+ if (expectedRequirementId && !exactRequirementMatch && !routeScopedSourceMatch) return false;
133
145
  if (expectedTarget && String(entry.referenceTarget || '') !== expectedTarget) return false;
134
- if (expectedRouteWorkspaceId && entry.routeWorkspaceId && String(entry.routeWorkspaceId || '') !== expectedRouteWorkspaceId) return false;
135
- if (expectedRoutePath && entry.routePath && normalizeWorkspacePath(entry.routePath || '') !== expectedRoutePath) return false;
146
+ if (expectedRouteWorkspaceId && entryRouteWorkspaceId && entryRouteWorkspaceId !== expectedRouteWorkspaceId) return false;
147
+ if (expectedRoutePath && entryRoutePath && entryRoutePath !== expectedRoutePath) return false;
136
148
  return Boolean(expectedTarget || expectedRequirementId);
137
149
  });
138
150
  const byRepresentation = new Map();
@@ -247,82 +247,110 @@ function groundHolderBinding(input, handoff, role, findings) {
247
247
  const recipientRoleKind = normalizeToken(handoff.toKind || (recipientRoleLabel ? 'role' : ''));
248
248
  const roleRecipient = recipientRoleKind === 'role';
249
249
  const explicitlySupplied = Boolean(roleLabel || holderId);
250
- const sourceDetail = holderBindingSourceDetail(input, explicit, explicitlySupplied);
251
- const assertionMode = String(explicit.assignmentMode || explicit.bindingMode || explicit.mechanism || input.holderAssignmentMode || input.sessionAssignmentMode || 'explicit-session').trim();
252
- const authorization = projectHolderBindingAuthorization(role, { assertionMode });
253
250
  const durableIdentity = holderDurableIdentityProjection(holderId);
254
251
 
255
- if (!roleRecipient) return deepFreeze({
256
- state: 'not-applicable',
257
- holderId,
258
- assertionMode,
259
- roleLabel,
260
- recipientRoleLabel,
261
- recipientCompatibility: 'not-applicable',
262
- source: explicitlySupplied ? 'explicit-input' : 'none',
263
- sourceDetail,
264
- authorization,
265
- durableIdentity,
266
- explicit: explicitlySupplied,
267
- inferredFromTransport: false,
268
- boundary: 'The selected Handoff recipient is not a Role endpoint, so no consuming-session Role holder binding is required or inferred.'
269
- });
252
+ if (!roleRecipient) {
253
+ const assertionMode = String(explicit.assignmentMode || explicit.bindingMode || explicit.mechanism || input.holderAssignmentMode || input.sessionAssignmentMode || 'explicit-session').trim();
254
+ const sourceDetail = holderBindingSourceDetail(input, explicit, explicitlySupplied);
255
+ const authorization = projectHolderBindingAuthorization(role, { assertionMode });
256
+ return deepFreeze({
257
+ state: 'not-applicable', holderId, assertionMode, roleLabel, recipientRoleLabel,
258
+ recipientCompatibility: 'not-applicable', source: explicitlySupplied ? 'explicit-input' : 'none', sourceDetail,
259
+ authorization, durableIdentity, explicit: explicitlySupplied, inferredFromTransport: false,
260
+ boundary: 'The selected Handoff recipient is not a Role endpoint, so no consuming-session Role holder binding is required or inferred.'
261
+ });
262
+ }
263
+
264
+ if (!explicitlySupplied) {
265
+ const assertionMode = 'handoff';
266
+ const authorization = projectHolderBindingAuthorization(role, { assertionMode });
267
+ const exactHandoff = exactSelectedHandoffAssignmentEvidence(handoff, role);
268
+ const sourceDetail = handoffHolderBindingSourceDetail(handoff, role, exactHandoff, authorization);
269
+ if (exactHandoff && authorization.state === 'qualified') return deepFreeze({
270
+ state: 'qualified', holderId: '', assertionMode, roleLabel: recipientRoleLabel, recipientRoleLabel,
271
+ recipientCompatibility: 'matched', source: 'qualified-selected-handoff-consumption', sourceDetail,
272
+ authorization, durableIdentity, explicit: false, inferredFromTransport: false,
273
+ boundary: 'Qualified consumption of the exact selected Handoff binds this bounded Tooling session to its exact recipient Role only because that exact qualified Role authorizes the canonical `handoff` assignment mode. Package delivery, route position, provider/chat identity and Role inventory alone do not bind a holder; durable identity and broader semantic authority remain unestablished.'
274
+ });
275
+ return deepFreeze({
276
+ state: 'unresolved', holderId: '', assertionMode, roleLabel: '', recipientRoleLabel,
277
+ recipientCompatibility: 'unresolved', source: exactHandoff ? 'qualified-selected-handoff-consumption' : 'none', sourceDetail,
278
+ authorization, durableIdentity, explicit: false, inferredFromTransport: false,
279
+ boundary: 'No explicit holder declaration was supplied, and bounded Handoff assignment can qualify only from the exact selected qualified Handoff plus exact recipient Role material that directly authorizes the canonical `handoff` mode. Transport delivery, endpoint naming, cached Role inventory and assistant/user position are insufficient.'
280
+ });
281
+ }
282
+
283
+ const sourceDetail = holderBindingSourceDetail(input, explicit, true);
284
+ const assertionMode = String(explicit.assignmentMode || explicit.bindingMode || explicit.mechanism || input.holderAssignmentMode || input.sessionAssignmentMode || 'explicit-session').trim();
285
+ const authorization = projectHolderBindingAuthorization(role, { assertionMode });
270
286
 
271
287
  if (!roleLabel) {
272
288
  if (holderId) findings.push(portableFinding('warning', 'portable.cold-start.holder-binding.role-missing', 'A consuming-session holder identifier was supplied without an explicit Role capacity; the holder binding remains unresolved.', { holderId, recipientRole: recipientRoleLabel }));
273
289
  return deepFreeze({
274
- state: 'unresolved',
275
- holderId,
276
- assertionMode,
277
- roleLabel: '',
278
- recipientRoleLabel,
279
- recipientCompatibility: 'unresolved',
280
- source: explicitlySupplied ? 'explicit-input' : 'none',
281
- sourceDetail,
282
- authorization,
283
- durableIdentity,
284
- explicit: explicitlySupplied,
285
- inferredFromTransport: false,
286
- boundary: 'Recipient Role and consuming-session holder are separate. No holder Role is inferred from route selection, transport identity, provider identity, assistant/user position, or participant declarations.'
290
+ state: 'unresolved', holderId, assertionMode, roleLabel: '', recipientRoleLabel,
291
+ recipientCompatibility: 'unresolved', source: 'explicit-input', sourceDetail, authorization, durableIdentity,
292
+ explicit: true, inferredFromTransport: false,
293
+ boundary: 'A partial explicit holder declaration is not completed from the Handoff path. Explicit-session and Handoff assignment remain mode-isolated.'
287
294
  });
288
295
  }
289
296
 
290
297
  if (recipientRoleLabel && normalizeComparable(roleLabel) !== normalizeComparable(recipientRoleLabel)) {
291
298
  findings.push(portableFinding('error', 'portable.cold-start.holder-binding.role-mismatch', 'Explicit consuming-session holder Role does not match the selected Handoff recipient Role.', { holderRole: roleLabel, recipientRole: recipientRoleLabel }));
292
299
  return deepFreeze({
293
- state: 'blocked',
294
- holderId,
295
- assertionMode,
296
- roleLabel,
297
- recipientRoleLabel,
298
- recipientCompatibility: 'mismatch',
299
- source: 'explicit-input',
300
- sourceDetail,
301
- authorization,
302
- durableIdentity,
303
- explicit: true,
304
- inferredFromTransport: false,
305
- boundary: 'An explicit holder Role mismatch is contradictory and blocks act-ready grounding. Tooling does not relabel the session to make the route fit.'
300
+ state: 'blocked', holderId, assertionMode, roleLabel, recipientRoleLabel,
301
+ recipientCompatibility: 'mismatch', source: 'explicit-input', sourceDetail, authorization, durableIdentity,
302
+ explicit: true, inferredFromTransport: false,
303
+ boundary: 'An explicit holder Role mismatch is contradictory and blocks act-ready grounding. Tooling does not relabel the session or fall back to Handoff assignment to make the route fit.'
306
304
  });
307
305
  }
308
306
 
309
307
  return deepFreeze({
310
- state: 'qualified',
311
- holderId,
312
- assertionMode,
313
- roleLabel,
314
- recipientRoleLabel,
315
- recipientCompatibility: 'matched',
316
- source: 'explicit-input',
317
- sourceDetail,
318
- authorization,
319
- durableIdentity,
320
- explicit: true,
321
- inferredFromTransport: false,
308
+ state: 'qualified', holderId, assertionMode, roleLabel, recipientRoleLabel,
309
+ recipientCompatibility: 'matched', source: 'explicit-input', sourceDetail, authorization, durableIdentity,
310
+ explicit: true, inferredFromTransport: false,
322
311
  boundary: 'Explicit consuming-session Role-capacity binding only. This binds the current Tooling invocation/session to the selected recipient Role capacity; it does not prove a human identity, consent, or authority beyond the qualified Handoff/Role/Task boundaries.'
323
312
  });
324
313
  }
325
314
 
315
+ function exactSelectedHandoffAssignmentEvidence(handoff = {}, role = {}) {
316
+ const artifact = role?.material?.artifact || {};
317
+ return String(handoff.schemaId || '') === 'tiinex.handoff.v1'
318
+ && Boolean(String(handoff.routeId || '').trim())
319
+ && Boolean(String(handoff.routePointerPath || '').trim())
320
+ && Boolean(String(handoff.workspaceRelativePath || '').trim())
321
+ && /^[0-9a-f]{64}$/i.test(String(handoff.sha256 || ''))
322
+ && String(role?.state || '') === 'qualified'
323
+ && String(role?.material?.state || '') === 'qualified'
324
+ && Boolean(String(artifact.path || '').trim())
325
+ && /^[0-9a-f]{64}$/i.test(String(artifact.sha256 || ''));
326
+ }
327
+
328
+ function handoffHolderBindingSourceDetail(handoff = {}, role = {}, exactHandoff = false, authorization = {}) {
329
+ const artifact = role?.material?.artifact || {};
330
+ const qualified = exactHandoff && String(authorization?.state || '') === 'qualified';
331
+ return deepFreeze({
332
+ kind: exactHandoff ? 'qualified-selected-handoff-consumption' : 'none',
333
+ locator: exactHandoff ? String(handoff.routePointerPath || handoff.workspaceRelativePath || '') : '',
334
+ authorityClass: exactHandoff ? 'selected-handoff-assignment-evidence' : 'none',
335
+ semanticAuthorityState: qualified ? 'qualified-bounded-handoff-assignment' : 'not-established',
336
+ qualifiedMaterialSource: Boolean(exactHandoff),
337
+ assignmentMode: 'handoff',
338
+ routeId: String(handoff.routeId || ''),
339
+ routePointerPath: String(handoff.routePointerPath || ''),
340
+ handoffArtifactPath: String(handoff.workspaceRelativePath || ''),
341
+ handoffArtifactSha256: String(handoff.sha256 || ''),
342
+ roleArtifactPath: String(artifact.path || ''),
343
+ roleArtifactSha256: String(artifact.sha256 || ''),
344
+ roleSchemaId: String(artifact.schemaId || ''),
345
+ roleLabel: String(artifact.roleLabel || role?.endpoint?.label || ''),
346
+ authorizationState: String(authorization?.state || 'unresolved'),
347
+ authorizationBasis: String(authorization?.provenance?.basis || ''),
348
+ boundary: exactHandoff
349
+ ? 'Exact selected qualified Handoff consumption plus exact qualified recipient Role material are the bounded assignment evidence. This is semantic Handoff/Role evidence, not package-delivery, provider, model, chat-position, filename or neighboring-route inference.'
350
+ : 'Exact selected Handoff/Role assignment evidence is not established; no Handoff-mode holder binding is projected.'
351
+ });
352
+ }
353
+
326
354
  function holderDurableIdentityProjection(holderId = '') {
327
355
  return deepFreeze({
328
356
  state: 'not-established',
@@ -390,6 +390,7 @@ export function parseHandoffGrounding(markdown, route) {
390
390
  returnTo: sectionField(completion, 'Return To')
391
391
  }),
392
392
  routeId: String(route?.id || ''),
393
+ routePointerPath: String(route?.pointerPath || ''),
393
394
  workspaceId: String(route?.workspaceId || ''),
394
395
  workspaceRelativePath: String(route?.workspaceRelativeHandoffPath || route?.workspaceRelativePath || ''),
395
396
  packagePath: String(route?.packagePath || ''),
@@ -405,7 +406,7 @@ function markdownReferenceTarget(value = '') {
405
406
  }
406
407
 
407
408
  export function emptyHandoffGrounding() {
408
- return deepFreeze({ schemaId: '', purpose: '', from: '', fromKind: '', fromReference: '', to: '', toKind: '', toReference: '', transfers: Object.freeze([]), retainedResponsibilities: Object.freeze([]), completionExpectation: Object.freeze({ signalKind: '', signalMeaning: '', returnTo: '' }), routeId: '', workspaceId: '', workspaceRelativePath: '', packagePath: '', sha256: '', boundary: 'No Handoff material supplied.' });
409
+ return deepFreeze({ schemaId: '', purpose: '', from: '', fromKind: '', fromReference: '', to: '', toKind: '', toReference: '', transfers: Object.freeze([]), retainedResponsibilities: Object.freeze([]), completionExpectation: Object.freeze({ signalKind: '', signalMeaning: '', returnTo: '' }), routeId: '', routePointerPath: '', workspaceId: '', workspaceRelativePath: '', packagePath: '', sha256: '', boundary: 'No Handoff material supplied.' });
409
410
  }
410
411
 
411
412
  export function resolveGroundingRouteMarkdown(bundle = {}, selectedRoute = {}, findings = [], context = null) {
@@ -8,7 +8,7 @@ export function buildEndpointRolePointerChain(input = {}) {
8
8
  const findings = [];
9
9
  let lineageParent = input.lineageParent || null;
10
10
  let nextDimension = String(input.nextDimension || '');
11
- for (const requirement of input.requirements || []) {
11
+ for (const requirement of orderedEndpointRequirements(input.requirements || [])) {
12
12
  const target = input.resolveRoleMaterialTarget(requirement, input.descriptor, input.workspaceById, input.cache, input.route);
13
13
  if (target.state !== 'qualified') {
14
14
  findings.push(finding('error', `portable.handoff-v2-surface.endpoint-role.${target.reason || 'unresolved'}`, 'Endpoint Role requirement did not resolve to one exact carried Workspace/cache representation.', { routeId: String(input.route?.id || ''), requirementId: String(requirement.id || '') }));
@@ -175,5 +175,18 @@ export function buildParticipantRolePointerChain(input = {}) {
175
175
  });
176
176
  }
177
177
 
178
+ function orderedEndpointRequirements(requirements = []) {
179
+ return [...requirements].map((item, index) => ({ item, index })).sort((a, b) => {
180
+ const rank = (entry) => {
181
+ const party = String(entry?.party || entry?.fields?.Side || '').trim().toLowerCase();
182
+ if (party === 'from') return 0;
183
+ if (party === 'to') return 1;
184
+ return 2;
185
+ };
186
+ const delta = rank(a.item) - rank(b.item);
187
+ return delta || a.index - b.index;
188
+ }).map(({ item }) => item);
189
+ }
190
+
178
191
  function safeToken(value = '') { return String(value || '').trim().toLowerCase().normalize('NFKD').replace(/[\u0300-\u036f]/g, '').replace(/[^a-z0-9._-]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 80) || 'workspace'; }
179
192
  function finding(severity, code, message, extra = {}) { return Object.freeze({ severity, code, message, ...extra }); }
@@ -5,7 +5,7 @@ export function projectRecipientV2Routes(routePointers = [], endpointPointers =
5
5
  const qualified = (qualifiedRoutes || []).find((route) => String(route.workspaceId || '') === String(item.facts?.workspaceId || '') && String(route.workspaceRelativePath || '') === String(item.facts?.workspaceRelativeHandoffPath || '')) || null;
6
6
  return Object.freeze({
7
7
  pointerPath: item.path,
8
- routeId: String(item.facts?.routeId || qualified?.id || ''),
8
+ routeId: String(item.facts?.routeId || ''),
9
9
  workspaceId: String(item.facts?.workspaceId || ''),
10
10
  workspaceRelativeHandoffPath: String(item.facts?.workspaceRelativeHandoffPath || ''),
11
11
  returnCarrierReservation: item.facts?.returnCarrierReservation || qualified?.returnCarrierReservation || null,
@@ -163,10 +163,10 @@ function buildRecipientFacingV2PackageV1Prepared(input = {}, sealedByWorkspaceId
163
163
  const binding = bindingForWorkspace(descriptor, owningWorkspace.workspaceId);
164
164
  let lineageParent = cache?.parent || owningWorkspace.parent;
165
165
  let nextDimension = cache ? `${plan.prefix}-1-1` : `${plan.prefix}-1`;
166
- const endpointChain = buildEndpointRolePointerChain({ requirements: route.materialRequirements?.endpointRoles || [], descriptor, workspaceById, cache, workspace: owningWorkspace, route, createdAt, lineageParent, nextDimension, resolveRoleMaterialTarget: roleMaterialTarget, parentAuthority: recipientV2ParentAuthority });
167
- files.push(...endpointChain.files); topology.endpointRoles.push(...endpointChain.roles); findings.push(...endpointChain.findings); lineageParent = endpointChain.lineageParent; nextDimension = endpointChain.nextDimension;
168
166
  const participantChain = buildParticipantRolePointerChain({ requirements: route.materialRequirements?.participantRoles || [], descriptor, workspaceById, cache, workspace: owningWorkspace, route, createdAt, lineageParent, nextDimension, resolveRoleMaterialTarget: roleMaterialTarget, parentAuthority: recipientV2ParentAuthority });
169
167
  files.push(...participantChain.files); topology.participantRoles.push(...participantChain.roles); findings.push(...participantChain.findings); lineageParent = participantChain.lineageParent; nextDimension = participantChain.nextDimension;
168
+ const endpointChain = buildEndpointRolePointerChain({ requirements: route.materialRequirements?.endpointRoles || [], descriptor, workspaceById, cache, workspace: owningWorkspace, route, createdAt, lineageParent, nextDimension, resolveRoleMaterialTarget: roleMaterialTarget, parentAuthority: recipientV2ParentAuthority });
169
+ files.push(...endpointChain.files); topology.endpointRoles.push(...endpointChain.roles); findings.push(...endpointChain.findings); lineageParent = endpointChain.lineageParent; nextDimension = endpointChain.nextDimension;
170
170
  const pointerPath = `${nextDimension}-handoff-pointer.trace.md`;
171
171
  const routeEntry = (binding?.entryMap?.entries || []).find((entry) => String(entry.path || '') === String(route.workspaceRelativePath || ''));
172
172
  const pointerFacts = {
@@ -183,8 +183,8 @@ function buildRecipientFacingV2TopologyLegacy(input = {}) {
183
183
  const routeDimension = cache ? `001-${plan.workspace.ordinal}-1-${plan.ordinal}` : `001-${plan.workspace.ordinal}-${plan.ordinal}`;
184
184
  let lineageParent = cache?.parent || workspace.parent;
185
185
  let nextDimension = routeDimension;
186
- const endpointChain = buildEndpointRolePointerChain({
187
- requirements: route.materialRequirements?.endpointRoles || [],
186
+ const participantChain = buildParticipantRolePointerChain({
187
+ requirements: route.materialRequirements?.participantRoles || [],
188
188
  descriptor,
189
189
  workspaceById,
190
190
  cache,
@@ -196,13 +196,13 @@ function buildRecipientFacingV2TopologyLegacy(input = {}) {
196
196
  resolveRoleMaterialTarget: roleMaterialTarget,
197
197
  parentAuthority: recipientV2ParentAuthority
198
198
  });
199
- files.push(...endpointChain.files);
200
- topology.endpointRoles.push(...endpointChain.roles);
201
- findings.push(...endpointChain.findings);
202
- lineageParent = endpointChain.lineageParent;
203
- nextDimension = endpointChain.nextDimension;
204
- const participantChain = buildParticipantRolePointerChain({
205
- requirements: route.materialRequirements?.participantRoles || [],
199
+ files.push(...participantChain.files);
200
+ topology.participantRoles.push(...participantChain.roles);
201
+ findings.push(...participantChain.findings);
202
+ lineageParent = participantChain.lineageParent;
203
+ nextDimension = participantChain.nextDimension;
204
+ const endpointChain = buildEndpointRolePointerChain({
205
+ requirements: route.materialRequirements?.endpointRoles || [],
206
206
  descriptor,
207
207
  workspaceById,
208
208
  cache,
@@ -214,11 +214,11 @@ function buildRecipientFacingV2TopologyLegacy(input = {}) {
214
214
  resolveRoleMaterialTarget: roleMaterialTarget,
215
215
  parentAuthority: recipientV2ParentAuthority
216
216
  });
217
- files.push(...participantChain.files);
218
- topology.participantRoles.push(...participantChain.roles);
219
- findings.push(...participantChain.findings);
220
- lineageParent = participantChain.lineageParent;
221
- nextDimension = participantChain.nextDimension;
217
+ files.push(...endpointChain.files);
218
+ topology.endpointRoles.push(...endpointChain.roles);
219
+ findings.push(...endpointChain.findings);
220
+ lineageParent = endpointChain.lineageParent;
221
+ nextDimension = endpointChain.nextDimension;
222
222
  const pointerPath = `${nextDimension}-handoff-pointer.trace.md`;
223
223
  const pointerFacts = {
224
224
  workspaceId: workspace.workspaceId,
@@ -4,7 +4,8 @@ import { normalizePortableInput } from '../input/portable.input.js';
4
4
  import { portableFinding, summarizePortableFindings } from '../findings.js';
5
5
  import { planPortableArtifact } from '../schema/schema.guide.js';
6
6
  import { indexPortableLoadedParentRecords, projectPortableLoadedParentRecord, resolvePortableLoadedParentReference } from './loaded.parent.js';
7
- import { allocateContinuationPath, allocateRootArtifactPath } from '../../../transitions/record.transitions.js';
7
+ import { allocateContinuationPath, allocateDirectoryArtifactPath, allocateRootArtifactPath } from '../../../transitions/record.transitions.js';
8
+ import { normalizePortableParentRecord, qualifyPortableExactParent } from '../draft/draft.exact.js';
8
9
 
9
10
  export const PORTABLE_EPISTEMIC_PLAN_SCHEMA_ID = 'tiinex.portable.epistemic-materialization-plan.v1';
10
11
 
@@ -69,18 +70,20 @@ function planProposal({ proposal, index, proposals, proposalIds, loadedIndex, ma
69
70
  const clarificationNeeds = [];
70
71
  const contract = buildArtifactCreationContract({ schemaId: proposal.schemaId, transitionType: proposal.parentRef ? 'continue-from-record' : 'create-artifact' });
71
72
  const artifactPlan = planPortableArtifact({ ...material, schemaId: proposal.schemaId, task: proposal.parentRef ? 'continue' : 'create', values: proposal.values || {}, inputs: proposal.values || {} }, options);
72
- const parent = resolveParentReference(proposal.parentRef, { loadedIndex, proposalIds, proposalIndex: index, proposals });
73
+ const parent = resolveParentReference(proposal.parentRef, { loadedIndex, proposalIds, proposalIndex: index, proposals, suppliedParentRecord: proposal.parentRecord });
73
74
  const parentForAllocation = parent.status === 'resolved'
74
75
  ? parent.kind === 'proposal'
75
76
  ? plannedParentRecord(plannedById.get(parent.parent?.proposalId || ''))
76
77
  : parent.parent
77
78
  : null;
78
- const allocationOptions = Object.freeze({ existingPaths: Object.freeze([...occupiedPaths]), path: proposal.path || '' });
79
+ const allocationOptions = Object.freeze({ existingPaths: Object.freeze([...occupiedPaths]), path: proposal.path || '', targetDirectory: proposal.targetDirectory || '' });
79
80
  const allocated = proposal.parentRef && parentForAllocation
80
81
  ? allocateContinuationPath({ parentRecord: parentForAllocation, targetId: proposal.schemaId, targetLabel: proposal.schemaId, title: proposal.title || proposal.summary || proposal.id }, allocationOptions)
81
- : !proposal.parentRef
82
- ? allocateRootArtifactPath({ targetId: proposal.schemaId, targetLabel: proposal.schemaId, title: proposal.title || proposal.summary || proposal.id }, allocationOptions)
83
- : Object.freeze({ path: proposal.path || '', policy: Object.freeze({}) });
82
+ : !proposal.parentRef && proposal.targetDirectory
83
+ ? allocateDirectoryArtifactPath({ targetDirectory: proposal.targetDirectory, targetId: proposal.schemaId, targetLabel: proposal.schemaId, title: proposal.title || proposal.summary || proposal.id }, allocationOptions)
84
+ : !proposal.parentRef
85
+ ? allocateRootArtifactPath({ targetId: proposal.schemaId, targetLabel: proposal.schemaId, title: proposal.title || proposal.summary || proposal.id }, allocationOptions)
86
+ : Object.freeze({ path: proposal.path || '', policy: Object.freeze({}) });
84
87
 
85
88
  if (!proposal.schemaId) {
86
89
  findings.push(portableFinding('error', 'portable.materialization.schema.required', 'Each proposal must declare an implemented schema id; schema meaning is never inferred by the writer.', { proposalId: proposal.id }));
@@ -130,6 +133,7 @@ function planProposal({ proposal, index, proposals, proposalIds, loadedIndex, ma
130
133
  rationale: proposal.rationale,
131
134
  evidenceRefs: proposal.evidenceRefs,
132
135
  parentRef: proposal.parentRef,
136
+ targetDirectory: proposal.targetDirectory,
133
137
  parent: parent.status === 'resolved' ? parent.parent : null,
134
138
  parentKind: parent.status === 'resolved' ? parent.kind : '',
135
139
  creationContract: contract,
@@ -148,6 +152,8 @@ function normalizeProposals(value) {
148
152
  mode: clean(raw.mode || ''),
149
153
  schemaId: clean(raw.schemaId || raw.schema || ''),
150
154
  parentRef: exact(raw.parentRef ?? raw.parent ?? ''),
155
+ parentRecord: raw.parentRecord && typeof raw.parentRecord === 'object' ? Object.freeze(clone(raw.parentRecord)) : null,
156
+ targetDirectory: clean(raw.targetDirectory || raw.directory || ''),
151
157
  path: clean(raw.path || ''),
152
158
  title: clean(raw.title || ''),
153
159
  summary: clean(raw.summary || ''),
@@ -168,9 +174,17 @@ function plannedParentRecord(entry = null) {
168
174
  return Object.freeze({ id: entry.id || '', path: entry.path, schemaId: entry.schemaId || '', title: entry.title || entry.summary || entry.id || '' });
169
175
  }
170
176
 
171
- function resolveParentReference(ref, { loadedIndex, proposalIds, proposalIndex, proposals }) {
177
+ function resolveParentReference(ref, { loadedIndex, proposalIds, proposalIndex, proposals, suppliedParentRecord = null }) {
172
178
  if (!ref) return { status: 'none', kind: 'none', parent: null };
173
179
  const reference = exact(ref);
180
+ if (suppliedParentRecord && typeof suppliedParentRecord === 'object') {
181
+ const normalized = normalizePortableParentRecord(suppliedParentRecord);
182
+ const declared = new Set([exact(normalized.id), exact(normalized.path)].filter(Boolean));
183
+ if (!declared.has(reference)) return { status: 'mismatch', code: 'portable.materialization.parent.supplied-reference-mismatch', message: 'The supplied Parent record does not identify the declared Parent reference.', candidates: [...declared] };
184
+ const qualification = qualifyPortableExactParent(normalized, 'continue-from-record');
185
+ if (!['qualified', 'qualified-local-continuity'].includes(String(qualification.state || ''))) return { status: 'unqualified', code: `portable.materialization.parent.supplied-${qualification.reason || 'unqualified'}`, message: 'The supplied Parent record is not qualified for continuation authoring.', candidates: [] };
186
+ return { status: 'resolved', kind: 'supplied-record', parent: qualification.snapshot };
187
+ }
174
188
  const proposalRef = reference.startsWith('proposal:') ? reference.slice('proposal:'.length) : proposalIds.has(reference) ? reference : '';
175
189
  if (proposalRef) {
176
190
  const targetIndex = proposals.findIndex((proposal) => proposal.id === proposalRef);
@@ -169,19 +169,21 @@ export function allocateContinuationPath({ parentRecord = {}, targetId = '', tar
169
169
  const explicitPath = canonicalLocalPath(options.path || options.draftPath || '');
170
170
  if (explicitPath) return { path: uniqueTransitionPath(explicitPath, occupied), policy: pathPolicyForExplicit(explicitPath) };
171
171
  const parentPath = externalWebArtifactUrl(parentRecord) ? '' : canonicalLocalPath(parentRecord.path || parentRecord.sourcePath || parentRecord.sourceTarget?.sourceArtifactPath || '');
172
- const parentDir = parentDirectory(parentPath) || '.topics';
173
- const requestedDir = canonicalLocalPath(options.targetDirectory || options.directory || '');
174
- const targetDir = requestedDir || parentDir;
172
+ const parentDir = parentDirectory(parentPath);
173
+ const requestedRaw = String(options.targetDirectory || options.directory || '').trim();
174
+ const requestedExplicit = Boolean(requestedRaw);
175
+ const requestedDir = requestedRaw === '.' ? '' : canonicalLocalPath(requestedRaw);
176
+ const targetDir = requestedExplicit ? requestedDir : (parentDir || '.topics');
175
177
  const parentPrefix = lineagePrefixFromPath(parentPath);
176
178
  const labelSlug = slugify(title || parentRecord.title || targetLabel || 'continuation');
177
179
  const targetSlug = slugify(targetLabel || labelFromSchemaId(targetId) || 'leaf');
178
180
  const extension = '.trace.md';
179
- const directoryLocal = Boolean(requestedDir && requestedDir !== parentDir);
181
+ const directoryLocal = Boolean(requestedExplicit && requestedDir !== parentDir);
180
182
  const policy = {
181
183
  schema: 'tiinex.transition.path-policy.v1',
182
184
  kind: directoryLocal ? 'directory-local-continuation' : 'same-parent-directory',
183
185
  parentDirectory: parentDir,
184
- targetDirectory: targetDir,
186
+ targetDirectory: requestedExplicit && requestedDir === '' ? '.' : targetDir,
185
187
  parentPath,
186
188
  parentLineagePrefix: parentPrefix,
187
189
  labelSlug,
@@ -196,14 +198,16 @@ export function allocateDirectoryArtifactPath({ targetDirectory = '.topics', tar
196
198
  const occupied = existingTransitionPaths(options);
197
199
  const explicitPath = canonicalLocalPath(options.path || options.draftPath || '');
198
200
  if (explicitPath) return { path: uniqueTransitionPath(explicitPath, occupied), policy: pathPolicyForExplicit(explicitPath) };
199
- const dir = canonicalLocalPath(targetDirectory || '.topics') || '.topics';
201
+ const targetRaw = String(targetDirectory ?? '').trim();
202
+ const explicitRoot = targetRaw === '.';
203
+ const dir = explicitRoot ? '' : (canonicalLocalPath(targetRaw || '.topics') || '.topics');
200
204
  const labelSlug = slugify(title || targetLabel || labelFromSchemaId(targetId) || 'artifact');
201
205
  const targetSlug = slugify(targetLabel || labelFromSchemaId(targetId) || 'artifact');
202
206
  const extension = '.trace.md';
203
207
  const policy = {
204
208
  schema: 'tiinex.transition.path-policy.v1',
205
209
  kind: 'directory-local-root',
206
- targetDirectory: dir,
210
+ targetDirectory: explicitRoot ? '.' : dir,
207
211
  labelSlug,
208
212
  targetSlug,
209
213
  extension,
@@ -214,20 +218,25 @@ export function allocateDirectoryArtifactPath({ targetDirectory = '.topics', tar
214
218
 
215
219
  function pathFromPolicy(policy = {}, occupied = new Set()) {
216
220
  const kind = String(policy.kind || '').trim();
217
- const dir = canonicalLocalPath(policy.targetDirectory || policy.parentDirectory || '.topics') || '.topics';
221
+ const hasTargetDirectory = Object.prototype.hasOwnProperty.call(policy, 'targetDirectory');
222
+ const hasParentDirectory = Object.prototype.hasOwnProperty.call(policy, 'parentDirectory');
223
+ const rawDirectory = hasTargetDirectory ? String(policy.targetDirectory ?? '') : hasParentDirectory ? String(policy.parentDirectory ?? '') : '.topics';
224
+ const rootDirectory = rawDirectory === '.' || ((hasTargetDirectory || hasParentDirectory) && rawDirectory === '');
225
+ const dir = rootDirectory ? '' : (canonicalLocalPath(rawDirectory || '.topics') || '.topics');
226
+ const pathPrefix = dir ? `${dir}/` : '';
218
227
  const extension = String(policy.extension || '.trace.md').startsWith('.') ? String(policy.extension || '.trace.md') : `.${policy.extension}`;
219
228
  const labelSlug = slugify(policy.labelSlug || 'continuation');
220
229
  const targetSlug = slugify(policy.targetSlug || 'leaf');
221
230
  const parentPrefix = String(policy.parentLineagePrefix || '').trim();
222
231
  if (kind === 'directory-local-root' || kind === 'directory-local-continuation') {
223
232
  const rootPrefix = nextDirectoryRootLineagePrefix(dir, occupied);
224
- return `${dir}/${rootPrefix}-${labelSlug}.${extension.replace(/^\./, '')}`;
233
+ return `${pathPrefix}${rootPrefix}-${labelSlug}.${extension.replace(/^\./, '')}`;
225
234
  }
226
235
  if (parentPrefix) {
227
236
  const childPrefix = nextChildLineagePrefix(parentPrefix, dir, occupied);
228
- return `${dir}/${childPrefix}-${labelSlug}.${extension.replace(/^\./, '')}`;
237
+ return `${pathPrefix}${childPrefix}-${labelSlug}.${extension.replace(/^\./, '')}`;
229
238
  }
230
- return uniqueTransitionPath(`${dir}/${labelSlug}--${targetSlug}${extension}`, occupied);
239
+ return uniqueTransitionPath(`${pathPrefix}${labelSlug}--${targetSlug}${extension}`, occupied);
231
240
  }
232
241
 
233
242
  function pathPolicyForExplicit(path = '') {