@tiinex/core 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/package.json +5 -5
- package/src/tooling/portable/adapters/cli/cli.common-author.js +68 -11
- package/src/tooling/portable/adapters/cli/cli.handoff-manufacture.js +3 -1
- package/src/tooling/portable/adapters/cli/cli.help.js +4 -4
- package/src/tooling/portable/adapters/node/handoff.manufacture.js +3 -1
- package/src/tooling/portable/adapters/node/handoff.manufacture.packageParent.js +32 -3
- package/src/transitions/record.transitions.js +52 -3
- package/src/tooling/portable/bootstrap/bootstrap.case.mjs +0 -43
package/README.md
CHANGED
|
@@ -31,15 +31,15 @@ Source paths intentionally follow the same `src/...` conventions used by other T
|
|
|
31
31
|
repositories where the concepts match. npm distribution preserves this source layout
|
|
32
32
|
rather than inventing a second directory vocabulary.
|
|
33
33
|
|
|
34
|
-
##
|
|
34
|
+
## Current implementation frontier
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
`@tiinex/core` is a published package boundary; package publication does not by itself establish browser/product acceptance or semantic authority. App/Site/Verse integration remains separately qualified by those consumers.
|
|
37
37
|
|
|
38
|
-
The explicit package export map
|
|
38
|
+
The explicit package export map exposes the shared module paths used by App. These preserve the mirrored layout, not a second implementation. `tools/tiinex-portable.mjs` remains a transitional compatibility entrypoint while dedicated CLI/Interop hosts are stabilized. See [`docs/ARCHITECTURE-BOUNDARIES.md`](docs/ARCHITECTURE-BOUNDARIES.md) for the carrier-bootstrap, schema-material and external-Interop split.
|
|
39
39
|
|
|
40
40
|
Application data is a declared-data projection: parsing and exact reference resolution are not schema validation, integrity verification or authority qualification. Missing Parent evidence remains unresolved. Companion byte access belongs to registered host readers, not automatic filesystem or network permissions.
|
|
41
41
|
|
|
42
42
|
|
|
43
|
-
##
|
|
43
|
+
## npm release frontier
|
|
44
44
|
|
|
45
|
-
Master-only automatic versioning/publication is implemented through the
|
|
45
|
+
Master-only automatic versioning/publication is implemented through the Core Node release helper. See `docs/NPM-PUBLISH.md`. OIDC/Trusted Publisher configuration is an external repository/package setting and remains separate from source qualification.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiinex/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.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,
|
|
@@ -165,12 +165,12 @@
|
|
|
165
165
|
"type": "git",
|
|
166
166
|
"url": "git+https://github.com/Tiinex/core.git"
|
|
167
167
|
},
|
|
168
|
-
"gitHead": "
|
|
168
|
+
"gitHead": "7d910295b08a55642306e1a96d7ab913cd428099",
|
|
169
169
|
"tiinexRelease": {
|
|
170
170
|
"policy": "tiinex.master-npm-release.v1",
|
|
171
|
-
"sourceCommit": "
|
|
172
|
-
"sourceTree": "
|
|
171
|
+
"sourceCommit": "7d910295b08a55642306e1a96d7ab913cd428099",
|
|
172
|
+
"sourceTree": "901cc01ce64d7565cfae24f832daa84962c7a22f",
|
|
173
173
|
"repository": "Tiinex/core",
|
|
174
|
-
"previousVersion":
|
|
174
|
+
"previousVersion": "0.1.1"
|
|
175
175
|
}
|
|
176
176
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mkdir, readFile, rm, writeFile } from 'node:fs/promises';
|
|
1
|
+
import { mkdir, readFile, readdir, rm, writeFile } from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { parseArtifactMarkdown } from '../../../../artifacts/artifact.parse.js';
|
|
4
4
|
import { buildArtifactCreationContract } from '../../../../schemas/creation.contracts.js';
|
|
@@ -8,6 +8,7 @@ import { sha256Hex, utf8Bytes } from '../../../../export/package.bytes.js';
|
|
|
8
8
|
import { resolveSchemaModule } from '../../../../schemas/resolver.js';
|
|
9
9
|
import { loadNodePortableInput } from '../../input/node.input.js';
|
|
10
10
|
import { runPortableOperation } from '../../operation.catalog.js';
|
|
11
|
+
import { allocateContinuationPath, allocateDirectoryArtifactPath } from '../../../../transitions/record.transitions.js';
|
|
11
12
|
|
|
12
13
|
const STATE_RELATIVE_PATH = '.tiinex/continuation.json';
|
|
13
14
|
|
|
@@ -16,21 +17,27 @@ export async function runCommonAuthorCli(parsed = {}, runtime = {}) {
|
|
|
16
17
|
const workspaceRoot = path.resolve(String(flags.workspace || parsed.positionals?.[0] || '.'));
|
|
17
18
|
const state = await readContinuationState(workspaceRoot);
|
|
18
19
|
const schemaId = String(flags.schema || '').trim();
|
|
19
|
-
const
|
|
20
|
+
const requestedArtifactRelativePath = normalizeWorkspaceRelativePath(flags.path || parsed.positionals?.[1] || '');
|
|
21
|
+
const targetDirectory = normalizeWorkspaceRelativePath(flags.directory || flags.dir || '');
|
|
20
22
|
const bodyPath = String(flags.body || flags.content || '').trim();
|
|
21
23
|
if (!schemaId) throw new Error('portable.cli.author.schema.required');
|
|
22
|
-
if (!
|
|
24
|
+
if (!requestedArtifactRelativePath && !targetDirectory) throw new Error('portable.cli.author.path-or-directory.required');
|
|
23
25
|
if (!bodyPath) throw new Error('portable.cli.author.body.required');
|
|
24
|
-
const artifactPath = safeWorkspaceTarget(workspaceRoot, artifactRelativePath);
|
|
25
26
|
const bodyMarkdown = (await readFile(path.resolve(bodyPath), 'utf8')).trim();
|
|
26
27
|
if (!bodyMarkdown) throw new Error('portable.cli.author.body.empty');
|
|
28
|
+
const title = String(flags.title || firstHeading(bodyMarkdown) || state?.roleLabel || schemaId).trim();
|
|
27
29
|
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
|
|
30
|
+
const parentReference = resolveParentReference(flags, state);
|
|
31
|
+
const parentSource = String(flags['parent-source'] || flags['parent-file'] || '').trim();
|
|
32
|
+
if (isWorkspaceQualifiedReference(parentReference) && !parentSource) throw new Error('portable.cli.author.parent-source.required');
|
|
33
|
+
if (parentSource && !parentReference) throw new Error('portable.cli.author.parent.required');
|
|
34
|
+
if (isWorkspaceQualifiedReference(parentReference) && !requestedArtifactRelativePath && !targetDirectory) throw new Error('portable.cli.author.cross-workspace-parent.target-required');
|
|
35
|
+
const parentPath = parentReference ? (parentSource ? path.resolve(parentSource) : safeWorkspaceTarget(workspaceRoot, parentReference)) : '';
|
|
36
|
+
const artifactRelativePath = requestedArtifactRelativePath || await allocateArtifactRelativePath({ workspaceRoot, targetDirectory, parentRelativePath: parentReference, schemaId, title });
|
|
37
|
+
const artifactPath = safeWorkspaceTarget(workspaceRoot, artifactRelativePath);
|
|
38
|
+
const parentRecord = parentPath ? await parentRecordFromArtifact(parentPath, parentReference, { workspaceRoot, childRelativePath: artifactRelativePath }) : {};
|
|
31
39
|
const transitionType = String(flags.transition || defaultTransition(schemaId, Boolean(parentPath))).trim();
|
|
32
40
|
const contract = buildArtifactCreationContract({ schemaId, transitionType });
|
|
33
|
-
const title = String(flags.title || firstHeading(bodyMarkdown) || state?.roleLabel || schemaId).trim();
|
|
34
41
|
const summary = String(flags.summary || title).trim();
|
|
35
42
|
const authors = String(flags.authors || state?.roleLabel || '').trim();
|
|
36
43
|
const why = Object.prototype.hasOwnProperty.call(flags, 'why') ? String(flags.why || '').trim() : '';
|
|
@@ -94,7 +101,7 @@ export async function runCommonAuthorCli(parsed = {}, runtime = {}) {
|
|
|
94
101
|
schema: 'tiinex.portable.common-author.result.v1',
|
|
95
102
|
operation: 'author',
|
|
96
103
|
status: 'qualified',
|
|
97
|
-
artifact: Object.freeze({ path: artifactRelativePath, absolutePath: artifactPath, schemaId, parentPath:
|
|
104
|
+
artifact: Object.freeze({ path: artifactRelativePath, absolutePath: artifactPath, schemaId, parentPath: parentReference, parentSource: parentSource || '', selfIntegrity: selfIntegrity.state, written: true }),
|
|
98
105
|
qualification: Object.freeze({ audit: audit.status, stage: stage.status, exportReady: Boolean(stage?.stagedArtifact?.qualification?.exportReady) }),
|
|
99
106
|
findingSummary: mergeFindingSummaries(audit?.findingSummary, stage?.findingSummary),
|
|
100
107
|
nextAction: schemaId === 'tiinex.handoff.v1'
|
|
@@ -186,12 +193,62 @@ async function recoverQualifiedLocalSchemaReferenceAuthority(schemaId, context =
|
|
|
186
193
|
});
|
|
187
194
|
}
|
|
188
195
|
|
|
189
|
-
function
|
|
196
|
+
async function allocateArtifactRelativePath({ workspaceRoot, targetDirectory, parentRelativePath, schemaId, title } = {}) {
|
|
197
|
+
const directory = normalizeWorkspaceRelativePath(targetDirectory || (parentRelativePath ? path.posix.dirname(parentRelativePath) : '.topics')) || '.topics';
|
|
198
|
+
const existingPaths = await directoryArtifactPaths(workspaceRoot, directory);
|
|
199
|
+
const allocation = parentRelativePath
|
|
200
|
+
? allocateContinuationPath({ parentRecord: { path: parentRelativePath }, targetId: schemaId, targetLabel: labelFromSchemaId(schemaId), title }, { targetDirectory: directory, existingPaths })
|
|
201
|
+
: allocateDirectoryArtifactPath({ targetDirectory: directory, targetId: schemaId, targetLabel: labelFromSchemaId(schemaId), title }, { existingPaths });
|
|
202
|
+
const allocated = normalizeWorkspaceRelativePath(allocation?.path || '');
|
|
203
|
+
if (!allocated) throw new Error('portable.cli.author.allocation.unavailable');
|
|
204
|
+
return allocated;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async function directoryArtifactPaths(workspaceRoot, directory) {
|
|
208
|
+
const dirPath = safeWorkspaceDirectory(workspaceRoot, directory);
|
|
209
|
+
let entries = [];
|
|
210
|
+
try { entries = await readdir(dirPath, { withFileTypes: true }); }
|
|
211
|
+
catch (error) {
|
|
212
|
+
if (error?.code === 'ENOENT') return [];
|
|
213
|
+
throw error;
|
|
214
|
+
}
|
|
215
|
+
return entries.filter((entry) => entry.isFile()).map((entry) => `${directory}/${entry.name}`);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function safeWorkspaceDirectory(root, relative) {
|
|
219
|
+
const normalized = normalizeWorkspaceRelativePath(relative || '.topics') || '.topics';
|
|
220
|
+
const target = path.resolve(root, normalized);
|
|
221
|
+
const rel = path.relative(root, target);
|
|
222
|
+
if (rel === '..' || rel.startsWith(`..${path.sep}`) || path.isAbsolute(rel)) throw new Error(`portable.cli.author.directory.unsafe:${relative}`);
|
|
223
|
+
return target;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function labelFromSchemaId(id = '') {
|
|
227
|
+
const tail = String(id || '').split('.').filter(Boolean).slice(-2, -1)[0] || String(id || 'artifact');
|
|
228
|
+
return tail.charAt(0).toUpperCase() + tail.slice(1);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function resolveParentReference(flags = {}, state = {}) {
|
|
190
232
|
if (flags['no-parent']) return '';
|
|
191
|
-
if (typeof flags.parent === 'string' && flags.parent.trim()) return
|
|
233
|
+
if (typeof flags.parent === 'string' && flags.parent.trim()) return normalizeParentReference(flags.parent);
|
|
192
234
|
return normalizeWorkspaceRelativePath(state?.lastAuthoredPath || state?.selectedHandoffPath || '');
|
|
193
235
|
}
|
|
194
236
|
|
|
237
|
+
export function normalizeParentReference(value = '') {
|
|
238
|
+
const raw = String(value || '').trim().replace(/\\/g, '/').replace(/^\.\//, '');
|
|
239
|
+
if (!raw || raw.startsWith('/') || /^[A-Za-z]:\//.test(raw)) return '';
|
|
240
|
+
const marker = raw.indexOf('::');
|
|
241
|
+
if (marker < 0) return normalizeWorkspaceRelativePath(raw);
|
|
242
|
+
const workspaceId = raw.slice(0, marker).trim();
|
|
243
|
+
const innerPath = normalizeWorkspaceRelativePath(raw.slice(marker + 2));
|
|
244
|
+
if (!workspaceId || !/^[A-Za-z0-9._-]+$/.test(workspaceId) || !innerPath) return '';
|
|
245
|
+
return `${workspaceId}::${innerPath}`;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function isWorkspaceQualifiedReference(value = '') {
|
|
249
|
+
return /^[A-Za-z0-9._-]+::/.test(String(value || ''));
|
|
250
|
+
}
|
|
251
|
+
|
|
195
252
|
function defaultTransition(schemaId, hasParent) {
|
|
196
253
|
if (!hasParent) return 'create-artifact';
|
|
197
254
|
if (schemaId === 'tiinex.evidence.v1') return 'reference-record';
|
|
@@ -28,6 +28,7 @@ export async function prepareHandoffManufactureCliCommand(parsed = {}, runtime =
|
|
|
28
28
|
if (!flags.route && handoffPath) flags.route = handoffPath;
|
|
29
29
|
if (!flags.output && !flags['output-dir'] && parsed.surfaceCommand === 'handoff' && continuationState.returnOutputDir) flags['output-dir'] = continuationState.returnOutputDir;
|
|
30
30
|
const materialBindings = await readOptionalJson(flags['material-bindings'] || flags.materials);
|
|
31
|
+
const packageParentWorkspaceAliases = await readOptionalJson(flags['package-parent-workspace-aliases']);
|
|
31
32
|
const operatorCarrierProfile = await readOptionalJson(flags['carrier-profile']);
|
|
32
33
|
const expectedToolingBootstrap = await readOptionalJson(flags['tooling-bootstrap-manifest']);
|
|
33
34
|
const workspaceDescriptorValue = await readOptionalJson(flags['workspace-roots'] || flags['workspace-descriptors']);
|
|
@@ -124,7 +125,8 @@ export async function prepareHandoffManufactureCliCommand(parsed = {}, runtime =
|
|
|
124
125
|
carrierProfile,
|
|
125
126
|
packageParentBundle,
|
|
126
127
|
packageParentPath: parentPackagePath ? path.resolve(parentPackagePath) : '',
|
|
127
|
-
packageParentSha256
|
|
128
|
+
packageParentSha256,
|
|
129
|
+
packageParentWorkspaceAliases
|
|
128
130
|
}, runtime);
|
|
129
131
|
return {
|
|
130
132
|
input,
|
|
@@ -10,7 +10,7 @@ export function portableCliHelpText(commandPrefix = '', surfaceCommand = '') {
|
|
|
10
10
|
'Common path (same command for humans and LLMs):',
|
|
11
11
|
`${command} ground <handoff-package.zip> --route <Continue-from> [--holder-role <recipient-role>]`,
|
|
12
12
|
`${command} ground <handoff-package.zip> --route <Continue-from> --holder-role <recipient-role> --continue <workspace-dir>`,
|
|
13
|
-
`${command} author <workspace-dir> --schema <schema-id> --path <workspace-relative-artifact> --body <body.md> [--parent <workspace-relative-parent>] [--title <title>] [--summary <summary>] [--why <why>]`,
|
|
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
|
'',
|
|
16
16
|
`Handoff aliases: ${command} orient <carrier.zip>; ${command} validate <carrier.zip>`,
|
|
@@ -20,7 +20,7 @@ export function portableCliHelpText(commandPrefix = '', surfaceCommand = '') {
|
|
|
20
20
|
'- `orient`, `ground`, and public `handoff` use compact decision-first default projections; add `--full` on the same command for the complete qualified receipt.',
|
|
21
21
|
'- `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`.',
|
|
22
22
|
'- `author` uses continuation state to infer ordinary Parent continuity, seal c14n-v2 integrity, audit, and stage; invalid artifacts are not retained.',
|
|
23
|
-
'- `handoff` uses continuation state plus the latest qualified authored Handoff to manufacture the canonical return carrier and excludes runtime-only `.tiinex` state. 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.',
|
|
23
|
+
'- `handoff` uses continuation state plus the latest qualified authored Handoff to manufacture the canonical return carrier and excludes runtime-only `.tiinex` state. When a package parent carries an older Workspace id that has been explicitly renamed, advanced manufacture may bind that predecessor id to a supplied current Workspace 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.',
|
|
24
24
|
'- Remote reads/writes remain explicit host concerns. Tooling operation safety does not create or revoke semantic Task/Handoff authority.',
|
|
25
25
|
'',
|
|
26
26
|
'Use `<common-command> --help` for focused common-path usage. Use `operations` deliberately for the advanced/internal operation catalog.'
|
|
@@ -43,9 +43,9 @@ function commonCommandHelp(command, surfaceCommand) {
|
|
|
43
43
|
if (surfaceCommand === 'author') return [
|
|
44
44
|
'Tiinex portable tooling — author',
|
|
45
45
|
'',
|
|
46
|
-
`${command} author <workspace-dir> --schema <schema-id> --path <workspace-relative-artifact> --body <body.md> [--parent <workspace-relative-parent>] [--title <title>] [--summary <summary>] [--why <why>]`,
|
|
46
|
+
`${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>]`,
|
|
47
47
|
'',
|
|
48
|
-
'Uses qualified continuation state to infer the ordinary Parent when `--parent` is omitted
|
|
48
|
+
'Uses qualified continuation state to infer the ordinary Parent when `--parent` is omitted. Supply `--path` for an exact requested coordinate or `--directory` to let Tooling allocate inside that directory-local filename namespace. For a Workspace-qualified Parent such as `business::.topics/...`, also supply `--parent-source` so Tooling reads and seals against the exact Parent bytes without treating the foreign address as a local path. Authoring seals c14n-v2 self-integrity, audits, stages, and updates continuation state only after qualification. Invalid output is not retained.',
|
|
49
49
|
'',
|
|
50
50
|
`Advanced/internal catalog: ${command} operations`
|
|
51
51
|
];
|
|
@@ -67,7 +67,8 @@ export async function prepareNodeHandoffManufacturingInput(input = {}, options =
|
|
|
67
67
|
bundle: input.packageParentBundle || null,
|
|
68
68
|
currentWorkspaceIds: [...seenWorkspaceIds],
|
|
69
69
|
parentPackagePath: input.packageParentPath || '',
|
|
70
|
-
parentPackageSha256: input.packageParentSha256 || ''
|
|
70
|
+
parentPackageSha256: input.packageParentSha256 || '',
|
|
71
|
+
workspaceAliases: input.packageParentWorkspaceAliases || input.workspaceAliases || {}
|
|
71
72
|
});
|
|
72
73
|
const additionalEnumerationsPromise = Promise.all(additionalWorkspaceInputs.map(async ({ descriptor, id, root, requestedTitle }) => {
|
|
73
74
|
const enumerated = await enumerateNodeWorkspace(root, {
|
|
@@ -168,6 +169,7 @@ export async function prepareNodeHandoffManufacturingInput(input = {}, options =
|
|
|
168
169
|
state: String(packageParentReuse.state || ''),
|
|
169
170
|
inspectionStatus: String(packageParentReuse.inspectionStatus || ''),
|
|
170
171
|
inheritedWorkspaceIds: Object.freeze((packageParentReuse.inherited || []).map((item) => String(item.id || ''))),
|
|
172
|
+
workspaceAliases: Object.freeze([...(packageParentReuse.workspaceAliases || [])].map((item) => Object.freeze({ ...item }))),
|
|
171
173
|
boundary: String(packageParentReuse.boundary || '')
|
|
172
174
|
}),
|
|
173
175
|
carrierProjection: Object.freeze({ requestedRoutes: transportRoutes.length || 1, carrierLineage: normalizeHandoffCarrierLineage(input.carrierLineage || null), carrierProfile: normalizeHandoffCarrierProfile(input.carrierProfile || null), boundary: 'Routes are qualified later against packaged workspace bytes; adapter text is not authority.' })
|
|
@@ -6,11 +6,13 @@ export function preparePackageParentWorkspaceReuse(input = {}) {
|
|
|
6
6
|
const bundle = input.bundle || null;
|
|
7
7
|
if (!bundle?.files?.length) return emptyReuse('unavailable');
|
|
8
8
|
const currentIds = new Set([...(input.currentWorkspaceIds || [])].map(normalizeId).filter(Boolean));
|
|
9
|
+
const workspaceAliases = normalizePackageParentWorkspaceAliases(input.workspaceAliases || input.packageParentWorkspaceAliases || {});
|
|
10
|
+
for (const alias of workspaceAliases.values()) if (!currentIds.has(alias)) throw new Error(`portable.handoff-manufacture.package-parent.workspace-alias.target-unresolved:${alias}`);
|
|
9
11
|
const inspection = inspectRecipientFacingV2Topology(bundle);
|
|
10
12
|
const declared = declaredPackageWorkspaceBindings(bundle, inspection);
|
|
11
13
|
if (!declared.length) return emptyReuse('unsupported-parent-surface');
|
|
12
14
|
if (inspection.status !== 'valid') throw new Error('portable.handoff-manufacture.package-parent.workspace-provider.invalid');
|
|
13
|
-
const missing = declared.filter((item) => !
|
|
15
|
+
const missing = declared.filter((item) => !packageParentWorkspaceSupersededByCurrent(item.workspaceId, currentIds, workspaceAliases));
|
|
14
16
|
if (!missing.length) return emptyReuse('not-needed');
|
|
15
17
|
|
|
16
18
|
const providerById = new Map((inspection.workspaceByteProvider?.workspaces || []).map((item) => [normalizeId(item.id), item]));
|
|
@@ -39,7 +41,8 @@ export function preparePackageParentWorkspaceReuse(input = {}) {
|
|
|
39
41
|
workspaceTargets: Object.freeze(workspaceTargets),
|
|
40
42
|
inspectionStatus: inspection.status,
|
|
41
43
|
missingWorkspaceIds: Object.freeze(missing.map((item) => normalizeId(item.workspaceId))),
|
|
42
|
-
|
|
44
|
+
workspaceAliases: Object.freeze([...workspaceAliases.entries()].map(([parentWorkspaceId, currentWorkspaceId]) => Object.freeze({ parentWorkspaceId, currentWorkspaceId }))),
|
|
45
|
+
boundary: 'Exact complete Workspace bytes reused from one independently qualified received package parent. Explicit current Workspace roots take precedence by id, and explicit qualified workspace aliases may supersede a renamed parent-carrier Workspace without carrying stale duplicate source. Parent-carrier placement and lineage remain non-semantic.'
|
|
43
46
|
});
|
|
44
47
|
}
|
|
45
48
|
|
|
@@ -121,8 +124,34 @@ function buildInheritedEnumeration(provider = {}, source = {}) {
|
|
|
121
124
|
});
|
|
122
125
|
}
|
|
123
126
|
|
|
127
|
+
export function normalizePackageParentWorkspaceAliases(value = {}) {
|
|
128
|
+
const entries = Array.isArray(value)
|
|
129
|
+
? value.map((item) => [item?.parentWorkspaceId || item?.parent || item?.from, item?.currentWorkspaceId || item?.current || item?.to])
|
|
130
|
+
: Object.entries(value || {});
|
|
131
|
+
const out = new Map();
|
|
132
|
+
const targets = new Set();
|
|
133
|
+
for (const [parentValue, currentValue] of entries) {
|
|
134
|
+
const parentWorkspaceId = normalizeId(parentValue);
|
|
135
|
+
const currentWorkspaceId = normalizeId(currentValue);
|
|
136
|
+
if (!parentWorkspaceId || !currentWorkspaceId) throw new Error('portable.handoff-manufacture.package-parent.workspace-alias.invalid');
|
|
137
|
+
if (parentWorkspaceId === currentWorkspaceId) throw new Error(`portable.handoff-manufacture.package-parent.workspace-alias.identity:${parentWorkspaceId}`);
|
|
138
|
+
if (out.has(parentWorkspaceId)) throw new Error(`portable.handoff-manufacture.package-parent.workspace-alias.duplicate-parent:${parentWorkspaceId}`);
|
|
139
|
+
if (targets.has(currentWorkspaceId)) throw new Error(`portable.handoff-manufacture.package-parent.workspace-alias.duplicate-target:${currentWorkspaceId}`);
|
|
140
|
+
out.set(parentWorkspaceId, currentWorkspaceId);
|
|
141
|
+
targets.add(currentWorkspaceId);
|
|
142
|
+
}
|
|
143
|
+
return out;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function packageParentWorkspaceSupersededByCurrent(parentWorkspaceId = '', currentWorkspaceIds = new Set(), workspaceAliases = new Map()) {
|
|
147
|
+
const id = normalizeId(parentWorkspaceId);
|
|
148
|
+
if (currentWorkspaceIds.has(id)) return true;
|
|
149
|
+
const replacement = workspaceAliases.get(id) || '';
|
|
150
|
+
return Boolean(replacement && currentWorkspaceIds.has(replacement));
|
|
151
|
+
}
|
|
152
|
+
|
|
124
153
|
function emptyReuse(state) {
|
|
125
|
-
return Object.freeze({ state, inherited: Object.freeze([]), workspaceTargets: Object.freeze([]), inspectionStatus: '', missingWorkspaceIds: Object.freeze([]), boundary: 'No package-parent Workspace provider reuse was required.' });
|
|
154
|
+
return Object.freeze({ state, inherited: Object.freeze([]), workspaceTargets: Object.freeze([]), inspectionStatus: '', missingWorkspaceIds: Object.freeze([]), workspaceAliases: Object.freeze([]), boundary: 'No package-parent Workspace provider reuse was required.' });
|
|
126
155
|
}
|
|
127
156
|
function normalizeId(value = '') { return String(value || '').trim().toLowerCase().replace(/[^a-z0-9._-]+/g, '-').replace(/^-+|-+$/g, ''); }
|
|
128
157
|
function mediaTypeForPath(value = '') { const lower = String(value || '').toLowerCase(); if (lower.endsWith('.md')) return 'text/markdown'; if (lower.endsWith('.json')) return 'application/json'; if (/\.(?:m?js|cjs)$/.test(lower)) return 'text/javascript'; if (lower.endsWith('.ts')) return 'text/typescript'; if (lower.endsWith('.css')) return 'text/css'; if (lower.endsWith('.html')) return 'text/html'; if (/\.(?:yml|yaml)$/.test(lower)) return 'text/yaml'; if (lower.endsWith('.txt')) return 'text/plain'; return 'application/octet-stream'; }
|
|
@@ -167,29 +167,59 @@ export function allocateContinuationPath({ parentRecord = {}, targetId = '', tar
|
|
|
167
167
|
if (explicitPath) return { path: uniqueTransitionPath(explicitPath, occupied), policy: pathPolicyForExplicit(explicitPath) };
|
|
168
168
|
const parentPath = externalWebArtifactUrl(parentRecord) ? '' : canonicalLocalPath(parentRecord.path || parentRecord.sourcePath || parentRecord.sourceTarget?.sourceArtifactPath || '');
|
|
169
169
|
const parentDir = parentDirectory(parentPath) || '.topics';
|
|
170
|
+
const requestedDir = canonicalLocalPath(options.targetDirectory || options.directory || '');
|
|
171
|
+
const targetDir = requestedDir || parentDir;
|
|
170
172
|
const parentPrefix = lineagePrefixFromPath(parentPath);
|
|
171
173
|
const labelSlug = slugify(title || parentRecord.title || targetLabel || 'continuation');
|
|
172
174
|
const targetSlug = slugify(targetLabel || labelFromSchemaId(targetId) || 'leaf');
|
|
173
175
|
const extension = '.trace.md';
|
|
176
|
+
const directoryLocal = Boolean(requestedDir && requestedDir !== parentDir);
|
|
174
177
|
const policy = {
|
|
175
178
|
schema: 'tiinex.transition.path-policy.v1',
|
|
176
|
-
kind: 'same-parent-directory',
|
|
179
|
+
kind: directoryLocal ? 'directory-local-continuation' : 'same-parent-directory',
|
|
177
180
|
parentDirectory: parentDir,
|
|
181
|
+
targetDirectory: targetDir,
|
|
178
182
|
parentPath,
|
|
179
183
|
parentLineagePrefix: parentPrefix,
|
|
180
184
|
labelSlug,
|
|
181
185
|
targetSlug,
|
|
182
|
-
extension
|
|
186
|
+
extension,
|
|
187
|
+
allocationAuthority: directoryLocal ? 'target-directory-local-namespace' : 'same-parent-directory-continuation'
|
|
188
|
+
};
|
|
189
|
+
return { path: pathFromPolicy(policy, occupied), policy };
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export function allocateDirectoryArtifactPath({ targetDirectory = '.topics', targetId = '', targetLabel = '', title = '' } = {}, options = {}) {
|
|
193
|
+
const occupied = existingTransitionPaths(options);
|
|
194
|
+
const explicitPath = canonicalLocalPath(options.path || options.draftPath || '');
|
|
195
|
+
if (explicitPath) return { path: uniqueTransitionPath(explicitPath, occupied), policy: pathPolicyForExplicit(explicitPath) };
|
|
196
|
+
const dir = canonicalLocalPath(targetDirectory || '.topics') || '.topics';
|
|
197
|
+
const labelSlug = slugify(title || targetLabel || labelFromSchemaId(targetId) || 'artifact');
|
|
198
|
+
const targetSlug = slugify(targetLabel || labelFromSchemaId(targetId) || 'artifact');
|
|
199
|
+
const extension = '.trace.md';
|
|
200
|
+
const policy = {
|
|
201
|
+
schema: 'tiinex.transition.path-policy.v1',
|
|
202
|
+
kind: 'directory-local-root',
|
|
203
|
+
targetDirectory: dir,
|
|
204
|
+
labelSlug,
|
|
205
|
+
targetSlug,
|
|
206
|
+
extension,
|
|
207
|
+
allocationAuthority: 'target-directory-local-namespace'
|
|
183
208
|
};
|
|
184
209
|
return { path: pathFromPolicy(policy, occupied), policy };
|
|
185
210
|
}
|
|
186
211
|
|
|
187
212
|
function pathFromPolicy(policy = {}, occupied = new Set()) {
|
|
188
|
-
const
|
|
213
|
+
const kind = String(policy.kind || '').trim();
|
|
214
|
+
const dir = canonicalLocalPath(policy.targetDirectory || policy.parentDirectory || '.topics') || '.topics';
|
|
189
215
|
const extension = String(policy.extension || '.trace.md').startsWith('.') ? String(policy.extension || '.trace.md') : `.${policy.extension}`;
|
|
190
216
|
const labelSlug = slugify(policy.labelSlug || 'continuation');
|
|
191
217
|
const targetSlug = slugify(policy.targetSlug || 'leaf');
|
|
192
218
|
const parentPrefix = String(policy.parentLineagePrefix || '').trim();
|
|
219
|
+
if (kind === 'directory-local-root' || kind === 'directory-local-continuation') {
|
|
220
|
+
const rootPrefix = nextDirectoryRootLineagePrefix(dir, occupied);
|
|
221
|
+
return `${dir}/${rootPrefix}-${labelSlug}.${extension.replace(/^\./, '')}`;
|
|
222
|
+
}
|
|
193
223
|
if (parentPrefix) {
|
|
194
224
|
const childPrefix = nextChildLineagePrefix(parentPrefix, dir, occupied);
|
|
195
225
|
return `${dir}/${childPrefix}-${labelSlug}.${extension.replace(/^\./, '')}`;
|
|
@@ -226,6 +256,25 @@ function lineagePrefixFromPath(path = '') {
|
|
|
226
256
|
return '';
|
|
227
257
|
}
|
|
228
258
|
|
|
259
|
+
|
|
260
|
+
function nextDirectoryRootLineagePrefix(dir = '', occupied = new Set()) {
|
|
261
|
+
const numbers = [];
|
|
262
|
+
let width = 3;
|
|
263
|
+
for (const value of occupied || []) {
|
|
264
|
+
const canonical = canonicalLocalPath(value);
|
|
265
|
+
if (parentDirectory(canonical) !== dir) continue;
|
|
266
|
+
const name = basenameWithoutKnownMarkdownExtension(canonical);
|
|
267
|
+
const match = name.match(/^(\d+)(?:-|$)/);
|
|
268
|
+
if (!match || /^20\d{2}$/.test(match[1])) continue;
|
|
269
|
+
const root = match[1].split('-')[0];
|
|
270
|
+
if (!/^\d+$/.test(root)) continue;
|
|
271
|
+
numbers.push(Number(root));
|
|
272
|
+
width = Math.max(width, root.length);
|
|
273
|
+
}
|
|
274
|
+
const next = numbers.length ? Math.max(...numbers) + 1 : 1;
|
|
275
|
+
return String(next).padStart(width, '0');
|
|
276
|
+
}
|
|
277
|
+
|
|
229
278
|
function nextChildLineagePrefix(parentPrefix = '', dir = '', occupied = new Set()) {
|
|
230
279
|
const prefix = String(parentPrefix || '').trim();
|
|
231
280
|
const width = childOrdinalWidth(prefix);
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict';
|
|
2
|
-
import { readFile } from 'node:fs/promises';
|
|
3
|
-
|
|
4
|
-
const pointer = JSON.parse(await readFile(new URL('./tiinex.llm.bootstrap.pointer.json', import.meta.url), 'utf8'));
|
|
5
|
-
const bootstrap = await readFile(new URL('./tiinex.llm.bootstrap.md', import.meta.url), 'utf8');
|
|
6
|
-
|
|
7
|
-
assert.equal(pointer.schema, 'tiinex.portable.bootstrap.pointer.v1');
|
|
8
|
-
assert.equal(pointer.repository, 'Tiinex/site');
|
|
9
|
-
assert.equal(pointer.firstOperations.includes('discover-tooling'), true);
|
|
10
|
-
assert.equal(pointer.firstOperations.includes('plan-host-action'), true);
|
|
11
|
-
assert.equal(pointer.boundary.remoteWrite, false);
|
|
12
|
-
assert.equal(pointer.boundary.sourceMutation, false);
|
|
13
|
-
assert.equal(bootstrap.includes('## Startup Modes'), true);
|
|
14
|
-
assert.equal(bootstrap.includes('Pre-prompt only; no bootstrap source is loaded'), true);
|
|
15
|
-
assert.equal(bootstrap.includes(pointer.bootstrapPath), true);
|
|
16
|
-
assert.equal(bootstrap.includes('do not execute package code'), true);
|
|
17
|
-
assert.equal(bootstrap.includes('## Bind Capabilities To Concrete Host Tools'), true);
|
|
18
|
-
assert.equal(bootstrap.includes('tiinex.portable.host-action-receipt.v1'), true);
|
|
19
|
-
assert.equal(bootstrap.includes('## Manufacture Recipient-Relative Handoff Packages'), true);
|
|
20
|
-
assert.equal(bootstrap.includes('manufacture-handoff-package'), true);
|
|
21
|
-
assert.equal(bootstrap.includes('001-1-READ-BEFORE-PROCEEDING.trace.md'), true);
|
|
22
|
-
assert.equal(bootstrap.includes('001-<package-slug>.trace.md'), true);
|
|
23
|
-
assert.equal(bootstrap.includes('Generated Markdown children declare package-local `Parent` continuity'), true);
|
|
24
|
-
assert.equal(bootstrap.includes('Exact durable source artifacts inside Workspace/cache archives retain their own historical provenance'), true);
|
|
25
|
-
assert.equal(bootstrap.includes('001-0-READ-BEFORE-PROCEEDING.trace.md'), false);
|
|
26
|
-
assert.equal(bootstrap.includes('tiinex.package/START.md'), false);
|
|
27
|
-
assert.equal(bootstrap.includes('orient-handoff-package'), true);
|
|
28
|
-
assert.equal(bootstrap.includes('audit-handoff-package-context'), true);
|
|
29
|
-
assert.equal(bootstrap.includes('inspect ./received --summary --phase-timing'), true);
|
|
30
|
-
assert.equal(bootstrap.includes('audit ./received --summary --phase-timing'), true);
|
|
31
|
-
assert.equal(bootstrap.includes('search-lineage ./received --query \"mobile overflow\" --relation leaf --summary --phase-timing'), true);
|
|
32
|
-
assert.equal(bootstrap.includes('resolve-lineage ./received --depth 3 --direction both --summary --phase-timing'), true);
|
|
33
|
-
assert.equal(bootstrap.includes('npm run tooling:search -- --query'), true);
|
|
34
|
-
assert.equal(bootstrap.includes('--include-legacy-topics'), true);
|
|
35
|
-
assert.equal(bootstrap.includes('--include-legacy-fixtures'), true);
|
|
36
|
-
assert.equal(bootstrap.includes('ground ... --include-required-context all'), true);
|
|
37
|
-
assert.equal(bootstrap.includes('A receipt that omits Required Context bodies is not proof that the model has read those bodies.'), true);
|
|
38
|
-
assert.equal(bootstrap.includes('humanOutput.normalInlineRouting.content'), true);
|
|
39
|
-
assert.equal(bootstrap.includes('sole primary package'), true);
|
|
40
|
-
assert.equal(bootstrap.includes('Filename or co-location under a bootstrap-like path does not grant bootstrap authority.'), true);
|
|
41
|
-
assert.equal(bootstrap.includes('canonical Handoff semantic authoring/validation or a locked canonical package schema'), true);
|
|
42
|
-
|
|
43
|
-
console.log('✓ portable bootstrap pointer and startup-mode contract passed');
|