@tiinex/core 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiinex/core",
3
- "version": "0.11.0",
3
+ "version": "0.12.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,
@@ -167,12 +167,12 @@
167
167
  "type": "git",
168
168
  "url": "git+https://github.com/Tiinex/core.git"
169
169
  },
170
- "gitHead": "d9fce40b1ec63fb0e4dc0a673161650d1661a25c",
170
+ "gitHead": "a63df8c4736b3267ac0aefa769b1262f45b78dac",
171
171
  "tiinexRelease": {
172
172
  "policy": "tiinex.master-npm-release.v1",
173
- "sourceCommit": "d9fce40b1ec63fb0e4dc0a673161650d1661a25c",
174
- "sourceTree": "1827ddd3975215d019cb809f2d477de6ebc4b300",
173
+ "sourceCommit": "a63df8c4736b3267ac0aefa769b1262f45b78dac",
174
+ "sourceTree": "d502ba11f95bbaf29fb7b86015df3d7b63bc82eb",
175
175
  "repository": "Tiinex/core",
176
- "previousVersion": "0.10.0"
176
+ "previousVersion": "0.11.0"
177
177
  }
178
178
  }
@@ -14,6 +14,7 @@ export async function buildToolingBootstrapTransportFiles(input = {}) {
14
14
  const delivery = normalizeDelivery(input.delivery);
15
15
  const runtimeRoot = path.resolve(String(input.runtimeRoot || DEFAULT_RUNTIME_ROOT));
16
16
  const runtime = await enumerateRuntimeDependencyGraph(runtimeRoot, { maxFiles: input.maxFiles });
17
+ const runtimeIdentity = runtimeIdentityFromEnumeration(runtime);
17
18
  const manifest = Object.freeze({
18
19
  schema: PORTABLE_TOOLING_BOOTSTRAP_MANIFEST_SCHEMA_ID,
19
20
  version: 1,
@@ -30,7 +31,42 @@ export async function buildToolingBootstrapTransportFiles(input = {}) {
30
31
  const summary = Object.freeze({ schema: 'tiinex.portable.tooling-bootstrap.summary.v1', delivery, manifestSha256, representationSha256: runtime.representationSha256, runtimeFiles: runtime.entries.length, runtimeBytes: runtime.totalBytes, status: delivery === 'embedded' ? 'embedded-qualified' : 'persistent-identity-verified', persistentVerification });
31
32
  const files = [transportFile('tiinex.bootstrap/manifest.json', manifestBytes, 'tooling-bootstrap-manifest', 'portable-tooling-bootstrap-control')];
32
33
  if (delivery === 'embedded') for (const entry of runtime.entries) files.push(transportFile(`tiinex.bootstrap/runtime/${entry.path}`, entry.data, 'tooling-bootstrap-runtime', 'portable-tooling-bootstrap-runtime'));
33
- return Object.freeze({ manifest, summary, files: Object.freeze(files) });
34
+ return Object.freeze({ manifest, summary, files: Object.freeze(files), runtimeIdentity });
35
+ }
36
+
37
+ export async function buildToolingBootstrapRuntimeIdentity(input = {}) {
38
+ const runtimeRoot = path.resolve(String(input.runtimeRoot || DEFAULT_RUNTIME_ROOT));
39
+ const runtime = await enumerateRuntimeDependencyGraph(runtimeRoot, { maxFiles: input.maxFiles });
40
+ return runtimeIdentityFromEnumeration(runtime);
41
+ }
42
+
43
+
44
+ function runtimeIdentityFromEnumeration(runtime = {}) {
45
+ const runtimeEntries = runtime.entries || [];
46
+ const packageEntry = runtimeEntries.find((entry) => String(entry?.path || '') === 'package.json');
47
+ let packageName = '';
48
+ let packageVersion = '';
49
+ if (packageEntry?.data) {
50
+ try {
51
+ const parsed = JSON.parse(new TextDecoder().decode(packageEntry.data));
52
+ packageName = String(parsed?.name || '').trim();
53
+ packageVersion = String(parsed?.version || '').trim();
54
+ } catch {}
55
+ }
56
+ const shaFor = (entryPath) => String(runtimeEntries.find((entry) => String(entry?.path || '') === entryPath)?.sha256 || '');
57
+ const sourceEntries = runtimeEntries.filter((entry) => String(entry?.path || '') !== 'package.json');
58
+ const sourceRepresentationSha256 = sha256Text(stableJson(sourceEntries.map(({ path: entryPath, bytes, sha256 }) => ({ path: `runtime/${entryPath}`, bytes, sha256 }))));
59
+ return Object.freeze({
60
+ schema: 'tiinex.portable.tooling-runtime-identity.v1',
61
+ packageName,
62
+ packageVersion,
63
+ representationSha256: String(runtime.representationSha256 || ''),
64
+ sourceRepresentationSha256,
65
+ runtimeFiles: Number(runtimeEntries.length),
66
+ runtimeBytes: Number(runtime.totalBytes || 0),
67
+ entrypointSha256: shaFor('tools/tiinex-portable.mjs'),
68
+ enumerationPolicySha256: shaFor('src/tooling/portable/adapters/node/handoff.manufacture.enumeration.js')
69
+ });
34
70
  }
35
71
 
36
72
  function verifyExpectedPersistentBootstrap(expected, manifest, manifestSha256) {
@@ -2,6 +2,7 @@ import { readFile } from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
  import { inferWorkspaceTitle, normalizeAdditionalWorkspaceDescriptors, normalizeTransportRoute, safeWorkspaceToken } from './handoff.manufacture.multiRoot.js';
4
4
  import { buildToolingBootstrapTransportFiles, PORTABLE_TOOLING_BOOTSTRAP_MANIFEST_SCHEMA_ID } from './handoff.manufacture.bootstrap.js';
5
+ import { qualifyToolingRuntimeSourceAlignment } from './handoff.manufacture.runtimeSource.js';
5
6
  import { normalizeHandoffCarrierLineage } from '../../handoff/carrierLineage.js';
6
7
  import { normalizeHandoffCarrierProfile } from '../../handoff/carrierProfile.js';
7
8
  import { enumerateNodeWorkspace, PORTABLE_NODE_WORKSPACE_ENUMERATION_SCHEMA_ID } from './handoff.manufacture.enumeration.js';
@@ -153,6 +154,14 @@ export async function prepareNodeHandoffManufacturingInput(input = {}, options =
153
154
  const toolingBootstrapResult = await toolingBootstrapPromise;
154
155
  if (toolingBootstrapResult.error) throw toolingBootstrapResult.error;
155
156
  const toolingBootstrap = toolingBootstrapResult.value;
157
+ const runtimeSourceAlignment = await qualifyToolingRuntimeSourceAlignment({
158
+ runtimeIdentity: toolingBootstrap.runtimeIdentity,
159
+ localWorkspaces: [
160
+ Object.freeze({ id: workspaceId, root: workspaceRoot, materialization: primaryMaterialization }),
161
+ ...additionalEnumerations.map(({ id, root, enumerated }) => Object.freeze({ id, root, materialization: enumerated.materialization }))
162
+ ],
163
+ maxFiles: input.bootstrapMaxFiles || options.bootstrapMaxFiles
164
+ });
156
165
  const orientationBootstrap = input.transportBootstrapContent
157
166
  ? Object.freeze({ present: true, path: String(input.transportBootstrapPath || 'tiinex.package/bootstrap.md'), content: String(input.transportBootstrapContent), mediaType: 'text/markdown' })
158
167
  : Object.freeze({ present: false });
@@ -175,6 +184,7 @@ export async function prepareNodeHandoffManufacturingInput(input = {}, options =
175
184
  enumeration: enumeration.evidence,
176
185
  workspaceEnumerations: Object.freeze(workspaceEnumerations),
177
186
  toolingBootstrap: toolingBootstrap.summary,
187
+ runtimeSourceAlignment,
178
188
  packageParentWorkspaceReuse: Object.freeze({
179
189
  state: String(packageParentReuse.state || ''),
180
190
  providerState: String(packageParentReuse.providerState || ''),
@@ -0,0 +1,82 @@
1
+ import { buildToolingBootstrapRuntimeIdentity } from './handoff.manufacture.bootstrap.js';
2
+
3
+ export const PORTABLE_TOOLING_RUNTIME_SOURCE_ALIGNMENT_SCHEMA_ID = 'tiinex.portable.tooling-runtime-source-alignment.v1';
4
+ const CORE_PACKAGE_NAME = '@tiinex/core';
5
+
6
+ export async function qualifyToolingRuntimeSourceAlignment(input = {}) {
7
+ const runtimeIdentity = normalizeIdentity(input.runtimeIdentity);
8
+ if (!runtimeIdentity.sourceRepresentationSha256) throw new Error('portable.tooling-bootstrap.runtime-source.runtime-identity-required');
9
+ const candidates = [];
10
+ for (const item of input.localWorkspaces || input.workspaces || []) {
11
+ const root = String(item?.root || '').trim();
12
+ if (!root) continue;
13
+ const materialization = item?.materialization || item?.enumeration?.materialization || null;
14
+ const packageIdentity = packageIdentityFromMaterialization(materialization);
15
+ if (packageIdentity.name !== CORE_PACKAGE_NAME) continue;
16
+ candidates.push(Object.freeze({
17
+ workspaceId: String(item?.id || materialization?.id || '').trim(),
18
+ root,
19
+ packageIdentity
20
+ }));
21
+ }
22
+ if (!candidates.length) return Object.freeze({
23
+ schema: PORTABLE_TOOLING_RUNTIME_SOURCE_ALIGNMENT_SCHEMA_ID,
24
+ state: 'not-observed',
25
+ coreWorkspaceCount: 0,
26
+ runtime: runtimeIdentity,
27
+ source: null,
28
+ boundary: 'No local Workspace with package identity @tiinex/core was selected, so exact runtime/source equality cannot be asserted from carried source.'
29
+ });
30
+ if (candidates.length !== 1) throw new Error(`portable.tooling-bootstrap.runtime-source.core-workspace-ambiguous:${candidates.map((item) => item.workspaceId || 'unlabeled').join(',')}`);
31
+
32
+ const candidate = candidates[0];
33
+ const sourceIdentity = normalizeIdentity(await buildToolingBootstrapRuntimeIdentity({
34
+ runtimeRoot: candidate.root,
35
+ maxFiles: input.maxFiles
36
+ }));
37
+ const evidence = Object.freeze({
38
+ schema: PORTABLE_TOOLING_RUNTIME_SOURCE_ALIGNMENT_SCHEMA_ID,
39
+ state: sourceIdentity.sourceRepresentationSha256 === runtimeIdentity.sourceRepresentationSha256 ? 'qualified-exact-match' : 'mismatch',
40
+ coreWorkspaceCount: 1,
41
+ workspaceId: candidate.workspaceId,
42
+ runtime: runtimeIdentity,
43
+ source: sourceIdentity,
44
+ boundary: 'Exact Tooling runtime source/data equality is required when a local @tiinex/core Workspace is carried. Release-normalized package.json metadata may differ, but matching package names or versions alone are insufficient.'
45
+ });
46
+ if (evidence.state !== 'qualified-exact-match') {
47
+ throw new Error([
48
+ 'portable.tooling-bootstrap.runtime-source.mismatch',
49
+ `workspace=${candidate.workspaceId || 'unlabeled'}`,
50
+ `runtimeVersion=${runtimeIdentity.packageVersion || 'unknown'}`,
51
+ `sourceVersion=${sourceIdentity.packageVersion || candidate.packageIdentity.version || 'unknown'}`,
52
+ `runtimeSource=${runtimeIdentity.sourceRepresentationSha256}`,
53
+ `sourceSource=${sourceIdentity.sourceRepresentationSha256}`
54
+ ].join(':'));
55
+ }
56
+ return evidence;
57
+ }
58
+
59
+ function packageIdentityFromMaterialization(materialization) {
60
+ const entry = (materialization?.entries || []).find((item) => String(item?.path || '') === 'package.json');
61
+ if (!entry?.data) return Object.freeze({ name: '', version: '' });
62
+ try {
63
+ const parsed = JSON.parse(new TextDecoder().decode(entry.data));
64
+ return Object.freeze({ name: String(parsed?.name || '').trim(), version: String(parsed?.version || '').trim() });
65
+ } catch {
66
+ return Object.freeze({ name: '', version: '' });
67
+ }
68
+ }
69
+
70
+ function normalizeIdentity(value = {}) {
71
+ return Object.freeze({
72
+ schema: String(value?.schema || 'tiinex.portable.tooling-runtime-identity.v1'),
73
+ packageName: String(value?.packageName || ''),
74
+ packageVersion: String(value?.packageVersion || ''),
75
+ representationSha256: String(value?.representationSha256 || ''),
76
+ sourceRepresentationSha256: String(value?.sourceRepresentationSha256 || ''),
77
+ runtimeFiles: Number(value?.runtimeFiles || 0),
78
+ runtimeBytes: Number(value?.runtimeBytes || 0),
79
+ entrypointSha256: String(value?.entrypointSha256 || ''),
80
+ enumerationPolicySha256: String(value?.enumerationPolicySha256 || '')
81
+ });
82
+ }
@@ -1,6 +1,7 @@
1
1
  import path from 'node:path';
2
2
  import { enumerateNodeWorkspace } from './handoff.manufacture.enumeration.js';
3
3
  import { buildToolingBootstrapTransportFiles } from './handoff.manufacture.bootstrap.js';
4
+ import { qualifyToolingRuntimeSourceAlignment } from './handoff.manufacture.runtimeSource.js';
4
5
  import { inferWorkspaceTitle, normalizeAdditionalWorkspaceDescriptors, safeWorkspaceToken } from './handoff.manufacture.multiRoot.js';
5
6
  import { normalizeWorkspaceTargetBindings } from './handoff.manufacture.scope.js';
6
7
  import { normalizeHandoffCarrierLineage } from '../../handoff/carrierLineage.js';
@@ -47,6 +48,11 @@ export async function prepareNodeWorkspaceCarrierManufacturingInput(input = {},
47
48
  expected: input.expectedToolingBootstrap || null,
48
49
  maxFiles: input.bootstrapMaxFiles || options.bootstrapMaxFiles
49
50
  });
51
+ const runtimeSourceAlignment = await qualifyToolingRuntimeSourceAlignment({
52
+ runtimeIdentity: toolingBootstrap.runtimeIdentity,
53
+ localWorkspaces: enumerations.map((item) => Object.freeze({ id: item.materialization.id, root: item.root, materialization: item.materialization })),
54
+ maxFiles: input.bootstrapMaxFiles || options.bootstrapMaxFiles
55
+ });
50
56
  return Object.freeze({
51
57
  carrierMode: 'workspace',
52
58
  createdAt: String(input.createdAt || ''),
@@ -56,7 +62,7 @@ export async function prepareNodeWorkspaceCarrierManufacturingInput(input = {},
56
62
  carrierLineage: normalizeHandoffCarrierLineage(input.carrierLineage || null),
57
63
  carrierProfile: normalizeHandoffCarrierProfile(input.carrierProfile || null),
58
64
  toolingBootstrap: toolingBootstrap.summary,
59
- manufacturingEvidence: Object.freeze({ workspaceEnumerations: Object.freeze(enumerations.map((item) => Object.freeze({ id: item.materialization.id, root: item.root, evidence: item.evidence }))), toolingBootstrap: toolingBootstrap.summary }),
65
+ manufacturingEvidence: Object.freeze({ workspaceEnumerations: Object.freeze(enumerations.map((item) => Object.freeze({ id: item.materialization.id, root: item.root, evidence: item.evidence }))), toolingBootstrap: toolingBootstrap.summary, runtimeSourceAlignment }),
60
66
  verifyRoundtrip: input.verifyRoundtrip !== false
61
67
  });
62
68
  }