@githolon/dsl 0.105.5 → 0.105.6
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/dart/CHANGELOG.md +27 -0
- package/dart/lib/src/runner/node-entry.mjs +155 -31
- package/dart/lib/src/runner/offline-install.mjs +82 -27
- package/dart/lib/src/webview_bridge.dart +115 -14
- package/dart/pubspec.lock +1 -1
- package/dart/pubspec.yaml +2 -2
- package/dart/test/acquire_session_test.dart +37 -0
- package/dart/test/residency_refusal_test.dart +49 -0
- package/dart/test/sealing_client_test.dart +1 -1
- package/dart/test/webview_bridge_test.dart +19 -0
- package/dart_types/CHANGELOG.md +5 -0
- package/dart_types/lib/src/subscriptions.dart +5 -1
- package/dart_types/pubspec.yaml +1 -1
- package/package.json +1 -1
- package/runtime/generated-application-host.js +138 -30
- package/src/check_package_main.ts +25 -54
- package/src/codegen_ts.ts +7 -0
- package/src/compat_package_main.ts +36 -30
- package/src/compile_package_main.ts +40 -39
- package/src/decl_shapes.ts +1 -1
- package/src/engine_entry.ts +2 -2
- package/src/evolve_preview.ts +7 -7
- package/src/identity_model.ts +123 -0
- package/src/identity_usda.ts +548 -0
- package/src/law_identity.ts +15 -0
- package/src/lsp_server.ts +14 -37
- package/src/stable_ids.ts +1 -1
- package/src/stable_id_lockfile.ts +0 -278
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* discovery), runs {@link checkDomainModules}, prints the unified report, and exits
|
|
11
11
|
* non-zero on any REFUSE-level finding. `compile` runs the same twins implicitly.
|
|
12
12
|
*/
|
|
13
|
-
import { existsSync, readFileSync,
|
|
13
|
+
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
14
14
|
import path from "node:path";
|
|
15
15
|
import { pathToFileURL } from "node:url";
|
|
16
16
|
|
|
@@ -22,12 +22,14 @@ import { LawSourceMap } from "./law_source_map.js";
|
|
|
22
22
|
import {
|
|
23
23
|
currentEvolveLaw,
|
|
24
24
|
evolveDiff,
|
|
25
|
-
evolveLawFromInterface,
|
|
26
25
|
evolveLawFromLockfile,
|
|
27
26
|
type EvolveLaw,
|
|
28
|
-
type
|
|
27
|
+
type CompilerIdentityModel,
|
|
29
28
|
} from "./evolve_preview.js";
|
|
30
|
-
import {
|
|
29
|
+
import {
|
|
30
|
+
NOMOS_IDENTITY_FILENAME,
|
|
31
|
+
parseIdentityUsda,
|
|
32
|
+
} from "./identity_usda.js";
|
|
31
33
|
import {
|
|
32
34
|
validateNomosPackageConfig,
|
|
33
35
|
type NormalizedNomosPackageConfig,
|
|
@@ -136,77 +138,46 @@ export async function loadComposedModulesWithSources(
|
|
|
136
138
|
}
|
|
137
139
|
|
|
138
140
|
/**
|
|
139
|
-
* Load the PRIOR
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
* A live `https://…` prior is NOT supported (the installed package's stable-ids aren't retrievable
|
|
143
|
-
* from GET /domains); point `--against` at the deployed source's interface and lock sidecars instead.
|
|
141
|
+
* Load the PRIOR identity surface for `--against <path>` (the evolve preview's baseline). The one
|
|
142
|
+
* supported artifact is the committed `nomos.identity.usda` layer, directly or via its package dir.
|
|
143
|
+
* There is no interface/JSON sidecar lane: prior executable-law inspection belongs to the holon.
|
|
144
144
|
*/
|
|
145
145
|
function loadPriorEvolveLaw(arg: string): { law: EvolveLaw; source: string; note?: string } {
|
|
146
146
|
if (/^https?:\/\//.test(arg)) {
|
|
147
147
|
fail(
|
|
148
148
|
`--against does not support a live URL (${arg}) — the installed package's #58 stable-ids are ` +
|
|
149
|
-
|
|
149
|
+
`not retrievable from GET /domains. Point --against at the deployed source's ${NOMOS_IDENTITY_FILENAME}.`,
|
|
150
150
|
);
|
|
151
151
|
}
|
|
152
152
|
const abs = path.resolve(process.cwd(), arg);
|
|
153
153
|
if (!existsSync(abs)) fail(`--against: prior not found: ${abs}`);
|
|
154
|
-
// Resolve a directory to its
|
|
154
|
+
// Resolve a directory to its one committed identity layer.
|
|
155
155
|
let file = abs;
|
|
156
156
|
if (statSync(abs).isDirectory()) {
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
else if (existsSync(lock)) file = lock;
|
|
161
|
-
else fail(`--against: no *.interface.json or nomos.stable-ids.json under ${abs}`);
|
|
157
|
+
const identity = path.join(abs, NOMOS_IDENTITY_FILENAME);
|
|
158
|
+
if (existsSync(identity)) file = identity;
|
|
159
|
+
else fail(`--against: no ${NOMOS_IDENTITY_FILENAME} under ${abs}`);
|
|
162
160
|
}
|
|
163
|
-
let
|
|
161
|
+
let lock: CompilerIdentityModel;
|
|
164
162
|
try {
|
|
165
|
-
|
|
163
|
+
lock = parseIdentityUsda(readFileSync(file, "utf8"));
|
|
166
164
|
} catch (e) {
|
|
167
165
|
fail(`--against: could not parse ${file}: ${(e as Error).message}`);
|
|
168
166
|
}
|
|
169
|
-
if (path.basename(file) === "nomos.stable-ids.json") {
|
|
170
|
-
return {
|
|
171
|
-
law: evolveLawFromLockfile(parseStableIdLockfile(parsed)),
|
|
172
|
-
source: file,
|
|
173
|
-
note: `prior is a lockfile — aggregate/field identity is diffed, but directive payload changes require the sibling interface sidecar.`,
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
const lockCandidates = [
|
|
177
|
-
path.join(path.dirname(file), "nomos.stable-ids.json"),
|
|
178
|
-
path.join(path.dirname(path.dirname(file)), "nomos.stable-ids.json"),
|
|
179
|
-
];
|
|
180
|
-
const lockFile = lockCandidates.find((candidate) => existsSync(candidate));
|
|
181
|
-
const lock = lockFile === undefined
|
|
182
|
-
? undefined
|
|
183
|
-
: parseStableIdLockfile(JSON.parse(readFileSync(lockFile, "utf8")));
|
|
184
167
|
return {
|
|
185
|
-
law:
|
|
186
|
-
source:
|
|
187
|
-
|
|
188
|
-
? { note: `prior has no stable-id lock beside it — directive shapes are checked, but driver and contract evolution evidence is incomplete.` }
|
|
189
|
-
: {}),
|
|
190
|
-
};
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/** Find a compiler diagnostic `*.interface.json` directly or one level down in `build/`. */
|
|
194
|
-
function findInterfaceJson(dir: string): string | undefined {
|
|
195
|
-
const scan = (d: string): string | undefined => {
|
|
196
|
-
if (!existsSync(d)) return undefined;
|
|
197
|
-
const hit = readdirSync(d).find((f) => f.endsWith(".interface.json"));
|
|
198
|
-
return hit !== undefined ? path.join(d, hit) : undefined;
|
|
168
|
+
law: evolveLawFromLockfile(lock),
|
|
169
|
+
source: file,
|
|
170
|
+
note: `prior identity is diffed by stable id; directive payload compatibility is negotiated from installed USDA by the holon, never a sidecar.`,
|
|
199
171
|
};
|
|
200
|
-
return scan(dir) ?? scan(path.join(dir, "build"));
|
|
201
172
|
}
|
|
202
173
|
|
|
203
|
-
/** Read the committed
|
|
204
|
-
function loadCwdLockfile(configArg: string):
|
|
174
|
+
/** Read the committed typed identity layer beside the config (the CURRENT identity). */
|
|
175
|
+
function loadCwdLockfile(configArg: string): CompilerIdentityModel | undefined {
|
|
205
176
|
const cfgDir = path.dirname(path.resolve(process.cwd(), configArg));
|
|
206
|
-
const lockPath = path.join(cfgDir,
|
|
177
|
+
const lockPath = path.join(cfgDir, NOMOS_IDENTITY_FILENAME);
|
|
207
178
|
if (!existsSync(lockPath)) return undefined;
|
|
208
179
|
try {
|
|
209
|
-
return
|
|
180
|
+
return parseIdentityUsda(readFileSync(lockPath, "utf8"));
|
|
210
181
|
} catch {
|
|
211
182
|
return undefined;
|
|
212
183
|
}
|
|
@@ -233,12 +204,12 @@ async function main(): Promise<void> {
|
|
|
233
204
|
let evolveDispositions: Record<string, unknown> | undefined;
|
|
234
205
|
if (againstIdx >= 0 || againstArg !== undefined) {
|
|
235
206
|
if (againstArg === undefined || againstArg.startsWith("--")) {
|
|
236
|
-
fail(`--against needs a path: --against
|
|
207
|
+
fail(`--against needs a path: --against <${NOMOS_IDENTITY_FILENAME} | package dir>`);
|
|
237
208
|
}
|
|
238
209
|
const lock = loadCwdLockfile(configArg);
|
|
239
210
|
if (lock === undefined && !asJson) {
|
|
240
211
|
console.log(
|
|
241
|
-
|
|
212
|
+
`githolon check: --against — no ${NOMOS_IDENTITY_FILENAME} in the config dir; the current law's stable ids ` +
|
|
242
213
|
"fall back to NAMES (a rename would read as removal+add). Run `githolon compile` first for a sid-accurate preview.",
|
|
243
214
|
);
|
|
244
215
|
}
|
package/src/codegen_ts.ts
CHANGED
|
@@ -4078,6 +4078,13 @@ export function generateTsClient(modules: readonly DomainModule[], opts: TsClien
|
|
|
4078
4078
|
` (holon, user) => _nomosBind${applicationName}(holon as NomosHolon, user),`,
|
|
4079
4079
|
` );`,
|
|
4080
4080
|
`}`,
|
|
4081
|
+
...(clientInterface === undefined
|
|
4082
|
+
? []
|
|
4083
|
+
: [
|
|
4084
|
+
`// Compiler tooling compares the requirement carried by the client that will actually ship.`,
|
|
4085
|
+
`// A global symbol keeps this off tenant autocomplete while avoiding a duplicate sidecar.`,
|
|
4086
|
+
`Object.defineProperty(${applicationOpener}, Symbol.for("nomos.generated-interface"), { value: ${interfaceConstName(opts.packageName)}, enumerable: false });`,
|
|
4087
|
+
]),
|
|
4081
4088
|
``,
|
|
4082
4089
|
);
|
|
4083
4090
|
}
|
|
@@ -5,17 +5,17 @@
|
|
|
5
5
|
/**
|
|
6
6
|
* `githolon compat <target> [--config …] [--domain …] [--json]` — THE CI GATE (task #31, the compat superpower).
|
|
7
7
|
*
|
|
8
|
-
* Compares the CURRENT
|
|
9
|
-
*
|
|
8
|
+
* Compares the CURRENT generated client's carried structural REQUIREMENT against a TARGET's
|
|
9
|
+
* installed law (the workspace OFFER), PER MEMBER, via the #72
|
|
10
10
|
* subtype relation (`memberVerdicts`). Exits NON-ZERO on any incompatible member — so a client/law
|
|
11
11
|
* mismatch is caught in CI, before it ships. The target is:
|
|
12
|
-
* •
|
|
12
|
+
* • a generated `.client.mjs` (or a build/package DIR containing one);
|
|
13
13
|
* • a LIVE workspace URL (`https://host/v2/workspaces/<ws>` or `https://host --workspace <ws>`) —
|
|
14
14
|
* connect()s and reads the on-ledger offer (`holon.interfaceOffer()`).
|
|
15
15
|
*
|
|
16
16
|
* Human report by default; `--json` emits the machine surface (the per-member array + verdict).
|
|
17
17
|
*/
|
|
18
|
-
import { existsSync,
|
|
18
|
+
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
19
19
|
import path from "node:path";
|
|
20
20
|
|
|
21
21
|
import { memberVerdicts, type DomainInterface, type MemberVerdict } from "./shape_compat.js";
|
|
@@ -41,7 +41,7 @@ function mergeInterfaces(byDomain: InterfaceMap | undefined): DomainInterface {
|
|
|
41
41
|
return out;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
/** The package name from `nomos.project.ts` (only field we need — to locate
|
|
44
|
+
/** The package name from `nomos.project.ts` (only field we need — to locate its generated client). */
|
|
45
45
|
async function loadPackageName(configArg: string): Promise<{ name: string; dir: string }> {
|
|
46
46
|
const configPath = path.resolve(process.cwd(), configArg);
|
|
47
47
|
if (!existsSync(configPath)) fail(`config not found: ${configPath} (pass the package config, or run from the package dir)`);
|
|
@@ -51,45 +51,51 @@ async function loadPackageName(configArg: string): Promise<{ name: string; dir:
|
|
|
51
51
|
return { name: cfg.name, dir: path.dirname(configPath) };
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
const GENERATED_INTERFACE = Symbol.for("nomos.generated-interface");
|
|
55
|
+
|
|
56
|
+
async function loadClientRequirement(file: string): Promise<InterfaceMap> {
|
|
57
|
+
const { pathToFileURL } = await import("node:url");
|
|
58
|
+
let generated: Record<string, unknown>;
|
|
59
|
+
try {
|
|
60
|
+
generated = await import(`${pathToFileURL(file).href}?compat=${Date.now()}`) as Record<string, unknown>;
|
|
61
|
+
} catch (error) {
|
|
62
|
+
fail(`could not load generated client ${file}: ${(error as Error).message}`);
|
|
63
|
+
}
|
|
64
|
+
for (const value of Object.values(generated)) {
|
|
65
|
+
if (typeof value !== "function") continue;
|
|
66
|
+
const requirement = (value as unknown as Record<PropertyKey, unknown>)[GENERATED_INTERFACE];
|
|
67
|
+
if (requirement !== null && typeof requirement === "object") return requirement as InterfaceMap;
|
|
68
|
+
}
|
|
69
|
+
fail(`generated client ${file} carries no Nomos structural requirement; recompile it with the current @githolon/dsl`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** The CURRENT requirement carried by the generated client that will ship. */
|
|
73
|
+
async function loadCurrentInterface(pkgDir: string, name: string): Promise<InterfaceMap> {
|
|
74
|
+
const p = path.join(pkgDir, "build", `${name}.client.mjs`);
|
|
57
75
|
if (!existsSync(p)) {
|
|
58
76
|
fail(
|
|
59
|
-
`no current
|
|
77
|
+
`no current generated client at ${p} — run \`githolon compile\` first.`,
|
|
60
78
|
);
|
|
61
79
|
}
|
|
62
|
-
|
|
63
|
-
return JSON.parse(readFileSync(p, "utf8")) as InterfaceMap;
|
|
64
|
-
} catch (e) {
|
|
65
|
-
fail(`could not parse ${p}: ${(e as Error).message}`);
|
|
66
|
-
}
|
|
80
|
+
return loadClientRequirement(p);
|
|
67
81
|
}
|
|
68
82
|
|
|
69
|
-
/** Read a TARGET
|
|
70
|
-
function loadFileOffer(arg: string): { offer: InterfaceMap; source: string } {
|
|
83
|
+
/** Read a TARGET requirement from the generated client it will ship. */
|
|
84
|
+
async function loadFileOffer(arg: string): Promise<{ offer: InterfaceMap; source: string }> {
|
|
71
85
|
const abs = path.resolve(process.cwd(), arg);
|
|
72
86
|
if (!existsSync(abs)) fail(`target not found: ${abs}`);
|
|
73
87
|
let file = abs;
|
|
74
88
|
if (statSync(abs).isDirectory()) {
|
|
75
89
|
const scan = (d: string): string | undefined => {
|
|
76
90
|
if (!existsSync(d)) return undefined;
|
|
77
|
-
const hit = readdirSync(d).find((f) => f.endsWith(".
|
|
91
|
+
const hit = readdirSync(d).find((f) => f.endsWith(".client.mjs"));
|
|
78
92
|
return hit !== undefined ? path.join(d, hit) : undefined;
|
|
79
93
|
};
|
|
80
94
|
const found = scan(abs) ?? scan(path.join(abs, "build"));
|
|
81
|
-
if (found === undefined) fail(`no *.
|
|
95
|
+
if (found === undefined) fail(`no *.client.mjs under ${abs}`);
|
|
82
96
|
file = found;
|
|
83
97
|
}
|
|
84
|
-
|
|
85
|
-
try {
|
|
86
|
-
parsed = JSON.parse(readFileSync(file, "utf8"));
|
|
87
|
-
} catch (e) {
|
|
88
|
-
fail(`could not parse ${file}: ${(e as Error).message}`);
|
|
89
|
-
}
|
|
90
|
-
const offer = parsed as InterfaceMap;
|
|
91
|
-
if (offer === null || typeof offer !== "object") fail(`target ${file} carries no structural interface`);
|
|
92
|
-
return { offer, source: file };
|
|
98
|
+
return { offer: await loadClientRequirement(file), source: file };
|
|
93
99
|
}
|
|
94
100
|
|
|
95
101
|
/** Read a LIVE workspace's on-ledger offer through the compiler-private runtime bundled with the DSL. */
|
|
@@ -155,10 +161,10 @@ async function main(): Promise<void> {
|
|
|
155
161
|
// The positional target = the first non-flag arg that isn't a flag VALUE.
|
|
156
162
|
const flagsWithVals = new Set(["--config", "--workspace", "--domain"]);
|
|
157
163
|
const target = argv.find((a, i) => !a.startsWith("--") && !(i > 0 && flagsWithVals.has(argv[i - 1]!)));
|
|
158
|
-
if (!target) fail(`usage: githolon compat <workspace-url |
|
|
164
|
+
if (!target) fail(`usage: githolon compat <workspace-url | client.mjs | dir> [--config <cfg>] [--workspace <ws>] [--domain <name,...>] [--json]`);
|
|
159
165
|
|
|
160
166
|
const { name, dir } = await loadPackageName(configArg);
|
|
161
|
-
const allCurrent = loadCurrentInterface(dir, name);
|
|
167
|
+
const allCurrent = await loadCurrentInterface(dir, name);
|
|
162
168
|
const requestedDomains = domainFlag?.split(",").map((domain) => domain.trim()).filter(Boolean) ?? [];
|
|
163
169
|
for (const domain of requestedDomains) {
|
|
164
170
|
if (allCurrent[domain] === undefined) fail(`current package '${name}' has no domain '${domain}'`);
|
|
@@ -169,7 +175,7 @@ async function main(): Promise<void> {
|
|
|
169
175
|
|
|
170
176
|
const { offer, source } = /^https?:\/\//.test(target)
|
|
171
177
|
? await loadLiveOffer(target, workspaceFlag)
|
|
172
|
-
: loadFileOffer(target);
|
|
178
|
+
: await loadFileOffer(target);
|
|
173
179
|
|
|
174
180
|
// The subtype check is PER MEMBER over the merged interfaces (directives keyed by id, aggregates by label).
|
|
175
181
|
const verdicts = memberVerdicts(mergeInterfaces(current), mergeInterfaces(offer));
|
|
@@ -145,11 +145,14 @@ import {
|
|
|
145
145
|
type StableIds,
|
|
146
146
|
} from "./stable_ids.js";
|
|
147
147
|
import {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
148
|
+
identityRegistryFromModel,
|
|
149
|
+
type CompilerIdentityModel,
|
|
150
|
+
} from "./identity_model.js";
|
|
151
|
+
import {
|
|
152
|
+
NOMOS_IDENTITY_FILENAME,
|
|
153
|
+
parseIdentityUsda,
|
|
154
|
+
serializeIdentityUsda,
|
|
155
|
+
} from "./identity_usda.js";
|
|
153
156
|
import { reconcileStableIdsWithIdentity } from "./stable_id_identity_bridge.js";
|
|
154
157
|
import { discoverLawGraph, type LawGraph } from "./law_graph.js";
|
|
155
158
|
import { emitFrameworkLawUsda, emitSparseLawGraphUsda } from "./law_graph_usda.js";
|
|
@@ -657,7 +660,7 @@ function firstLawDifference(expected: unknown, actual: unknown, at = "$law"): st
|
|
|
657
660
|
// ── decl auto-discovery (by SHAPE, mirroring the engine entry's duck-types) ───────
|
|
658
661
|
// The query/workspace-invariant/spatial/sum/count predicates + `discover` live in the SHARED
|
|
659
662
|
// bundle-safe `decl_shapes.ts` (imported above) — the lump's self-describe verb discovers by the
|
|
660
|
-
// EXACT same shape so the kernel-served OpenAPI
|
|
663
|
+
// EXACT same shape so the kernel-served OpenAPI is a deterministic projection of installed law.
|
|
661
664
|
|
|
662
665
|
function isDerivedDecl(v: unknown): v is DerivedDecl {
|
|
663
666
|
return (
|
|
@@ -1590,8 +1593,8 @@ function emitDart(
|
|
|
1590
1593
|
// The generated clients import their runtime support directly. Keeping it out of the public barrel is
|
|
1591
1594
|
// the surface firewall: a tenant import receives business types and application APIs, never raw runtime.
|
|
1592
1595
|
supportImport === "vendored"
|
|
1593
|
-
? `export '${genRel}/src/${VENDORED_TYPES_SHIM}' show NomosRef, NomosOptionalRef, VersionedNomosRef, ReferenceVersionStatus, ReferenceVersionCurrent, ReferenceVersionOutdated, ReferenceVersionUnavailable${sha256DigestExport}${gitObjectIdExport};`
|
|
1594
|
-
: `export 'package:nomos_types/nomos_types.dart' show NomosRef, NomosOptionalRef, VersionedNomosRef, ReferenceVersionStatus, ReferenceVersionCurrent, ReferenceVersionOutdated, ReferenceVersionUnavailable${sha256DigestExport}${gitObjectIdExport};`,
|
|
1596
|
+
? `export '${genRel}/src/${VENDORED_TYPES_SHIM}' show NomosRef, NomosOptionalRef, NomosSlot, VersionedNomosRef, ReferenceVersionStatus, ReferenceVersionCurrent, ReferenceVersionOutdated, ReferenceVersionUnavailable${sha256DigestExport}${gitObjectIdExport};`
|
|
1597
|
+
: `export 'package:nomos_types/nomos_types.dart' show NomosRef, NomosOptionalRef, NomosSlot, VersionedNomosRef, ReferenceVersionStatus, ReferenceVersionCurrent, ReferenceVersionOutdated, ReferenceVersionUnavailable${sha256DigestExport}${gitObjectIdExport};`,
|
|
1595
1598
|
...(contractsImportUri !== undefined
|
|
1596
1599
|
? tenantDartExport(`${genRel}/${CONTRACTS_FILE}.dart`, sourceByFile.get(CONTRACTS_FILE) ?? "")
|
|
1597
1600
|
: []),
|
|
@@ -1653,12 +1656,12 @@ const dartTypesImportFor = (generatedSource: string): string => {
|
|
|
1653
1656
|
* just the floor generated code needs — a lagging pin forces tenants into dependency_overrides. */
|
|
1654
1657
|
// 0.3.0: SurfacedValue<T>/SurfacedObservation/PendingConflictRow — generated Surfaced-field typing +
|
|
1655
1658
|
// the pendingConflicts read rows.
|
|
1656
|
-
const NOMOS_TYPES_DEP = "^0.8.
|
|
1659
|
+
const NOMOS_TYPES_DEP = "^0.8.7";
|
|
1657
1660
|
// 0.48.x: session-bound clients (XClient.bind/NomosClientInterface/DomainClientSessionMismatch/
|
|
1658
1661
|
// DomainNotInstalledForSession, engine.follow, pendingConflicts lanes) — the generated client
|
|
1659
1662
|
// REQUIRES this surface. 0.48.1 fixes NomosSession.watchPendingConflicts delegation.
|
|
1660
|
-
const NOMOS_CLIENT_DEP = "^0.64.
|
|
1661
|
-
const NOMOS_FLUTTER_DEP = "^0.63.
|
|
1663
|
+
const NOMOS_CLIENT_DEP = "^0.64.23";
|
|
1664
|
+
const NOMOS_FLUTTER_DEP = "^0.63.44";
|
|
1662
1665
|
// DRIFT TRIPWIRE: pubspec_pins.test.ts asserts these equal the CURRENT versions in dsl/dart/pubspec.yaml
|
|
1663
1666
|
// and dsl/dart_types/pubspec.yaml — a Dart release without this bump fails the suite, so tenants never
|
|
1664
1667
|
// land back in dependency_overrides (the exact co2 report this closes, third occurrence).
|
|
@@ -2403,7 +2406,7 @@ function emitDartPackages(
|
|
|
2403
2406
|
businessSharedPath,
|
|
2404
2407
|
[
|
|
2405
2408
|
"// GENERATED by nomos-compile — shared business values and activity; no runtime machinery.",
|
|
2406
|
-
`export 'package:nomos_types/nomos_types.dart' show NomosRef, NomosOptionalRef, VersionedNomosRef, ReferenceVersionStatus, ReferenceVersionCurrent, ReferenceVersionOutdated, ReferenceVersionUnavailable${sha256DigestExport}${gitObjectIdExport}, Identity, User, Recipient, Invitation, ActivityEvent, ActivityRef, ActivitySeverity, SurfacedObservation, SurfacedValue, SurfacedResolved, SurfacedPending, SurfacedEvolutionRequired;`,
|
|
2409
|
+
`export 'package:nomos_types/nomos_types.dart' show NomosRef, NomosOptionalRef, NomosSlot, VersionedNomosRef, ReferenceVersionStatus, ReferenceVersionCurrent, ReferenceVersionOutdated, ReferenceVersionUnavailable${sha256DigestExport}${gitObjectIdExport}, Identity, User, Recipient, Invitation, ActivityEvent, ActivityRef, ActivitySeverity, SurfacedObservation, SurfacedValue, SurfacedResolved, SurfacedPending, SurfacedEvolutionRequired;`,
|
|
2407
2410
|
...(sharedBusinessTypeNames.length === 0
|
|
2408
2411
|
? []
|
|
2409
2412
|
: [`export '${FRAMEWORK_FILE}.dart' show ${sharedBusinessTypeNames.join(", ")};`]),
|
|
@@ -3021,6 +3024,8 @@ async function main(): Promise<void> {
|
|
|
3021
3024
|
`${cfg.name}.layered.usda`,
|
|
3022
3025
|
`${cfg.name}.law-closure.json`,
|
|
3023
3026
|
`${cfg.name}.deploy.json`,
|
|
3027
|
+
`${cfg.name}.interface.json`,
|
|
3028
|
+
`${cfg.name}.openapi.json`,
|
|
3024
3029
|
]) rmSync(path.join(outDir, retired), { force: true });
|
|
3025
3030
|
for (const retiredDir of ["layers", "v3-layers", `${cfg.name}.relation-layers`]) {
|
|
3026
3031
|
rmSync(path.join(outDir, retiredDir), { recursive: true, force: true });
|
|
@@ -3069,7 +3074,7 @@ async function main(): Promise<void> {
|
|
|
3069
3074
|
// lump's `globalThis.describe` needs that it cannot rediscover by shape alone (mins/maxes are
|
|
3070
3075
|
// config-named; sharding sums are framework-appended). Plain JSON (finishCount/Sum/Extremum
|
|
3071
3076
|
// pass finished decls through), serialized into the generated entry as `openapiDecls`, so the
|
|
3072
|
-
// kernel-run describe reproduces
|
|
3077
|
+
// kernel-run describe reproduces this compile's public description deterministically.
|
|
3073
3078
|
const openapiDeclsByDomain: Record<string, unknown> = {};
|
|
3074
3079
|
// A tenant module may import declarations from another configured law module (business-birth
|
|
3075
3080
|
// composition is the common case). Ordinary ES evaluation then initializes that entire closure
|
|
@@ -3738,25 +3743,25 @@ async function main(): Promise<void> {
|
|
|
3738
3743
|
|
|
3739
3744
|
// ── STABLE-ID CONTINUITY (#58 — names are labels, identity is minted) ──
|
|
3740
3745
|
// Derive each composed domain's stable ids from the PRIOR compile: the committed
|
|
3741
|
-
// `nomos.
|
|
3746
|
+
// `nomos.identity.usda` layer beside the config (the durable source — survives
|
|
3742
3747
|
// a clean clone), else the previous build's identity manifests. Match by name;
|
|
3743
3748
|
// exactly ONE disappeared + ONE appeared name of the same kind/shape is an inferred
|
|
3744
3749
|
// RENAME (sid carried, lineage recorded, printed below); anything else mints fresh
|
|
3745
3750
|
// at first appearance (a leftover disappearance is THE EVOLVE GATE's typed question
|
|
3746
3751
|
// at deploy — answered in the upgrade intent's disposition). The resolved continuity
|
|
3747
3752
|
// is attached to the module so the canonical manifest + USD-IR carry it.
|
|
3748
|
-
const lockPath = path.join(cfgDir,
|
|
3749
|
-
const priorLock:
|
|
3750
|
-
?
|
|
3753
|
+
const lockPath = path.join(cfgDir, NOMOS_IDENTITY_FILENAME);
|
|
3754
|
+
const priorLock: CompilerIdentityModel | undefined = existsSync(lockPath)
|
|
3755
|
+
? parseIdentityUsda(readFileSync(lockPath, "utf8"))
|
|
3751
3756
|
: undefined;
|
|
3752
3757
|
const inferredRenames: { domain: string; renames: InferredRename[] }[] = [];
|
|
3753
|
-
let nextIdentity =
|
|
3758
|
+
let nextIdentity = identityRegistryFromModel(priorLock);
|
|
3754
3759
|
const typedLawGraphs = new Map<string, LawGraph>();
|
|
3755
3760
|
// The lock is project-wide, not package-local. Several package configs commonly live beside one
|
|
3756
3761
|
// application stage; compiling one must not erase the stable identity of domains owned by its siblings.
|
|
3757
3762
|
// Current package domains are replaced below, while absent domains remain only as continuity metadata
|
|
3758
3763
|
// and never leak into this package's interface, law, clients or deployment.
|
|
3759
|
-
const nextLock:
|
|
3764
|
+
const nextLock: CompilerIdentityModel = {
|
|
3760
3765
|
v: 1,
|
|
3761
3766
|
domains: { ...(priorLock?.domains ?? {}) },
|
|
3762
3767
|
identity: nextIdentity,
|
|
@@ -3790,7 +3795,7 @@ async function main(): Promise<void> {
|
|
|
3790
3795
|
// it carries no stable ids either.
|
|
3791
3796
|
continue;
|
|
3792
3797
|
}
|
|
3793
|
-
// Continuity source: the committed nomos.
|
|
3798
|
+
// Continuity source: the committed nomos.identity.usda layer (the canonical identity ledger).
|
|
3794
3799
|
// (The old "else the prior build's domain_identity_manifests.json" fallback is gone with that sidecar.)
|
|
3795
3800
|
let prior: StableIds | undefined;
|
|
3796
3801
|
let priorSchemas: Record<string, WireSchema> | undefined;
|
|
@@ -3886,11 +3891,11 @@ async function main(): Promise<void> {
|
|
|
3886
3891
|
schemas: Object.fromEntries(currentAggs.map((a) => [a.id, a.schema])),
|
|
3887
3892
|
};
|
|
3888
3893
|
}
|
|
3889
|
-
// Persist the
|
|
3894
|
+
// Persist the typed identity layer (commit it with the law: it is how a rename keeps its
|
|
3890
3895
|
// identity across clean clones). Existing project-domain order is retained, so once sibling
|
|
3891
3896
|
// packages are registered, recompiling them in any order rewrites the same bytes without a
|
|
3892
3897
|
// giant formatting-only diff in an established lock.
|
|
3893
|
-
writeFileSync(lockPath,
|
|
3898
|
+
writeFileSync(lockPath, serializeIdentityUsda(nextLock), "utf8");
|
|
3894
3899
|
|
|
3895
3900
|
// ── THE STRUCTURAL INTERFACE (#72) — directives (payload shapes) + aggregates (read
|
|
3896
3901
|
// shapes, sid-keyed), keyed by DOMAIN. The SAME object is (a) baked into the generated
|
|
@@ -3938,11 +3943,12 @@ async function main(): Promise<void> {
|
|
|
3938
3943
|
}
|
|
3939
3944
|
: di;
|
|
3940
3945
|
}
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
+
// NO INTERFACE SIDECAR. The structural interface is an in-memory codegen model only: it is baked
|
|
3947
|
+
// INTO the generated clients where compiler tooling can inspect the requirement that will ship.
|
|
3948
|
+
// Emitting it as JSON created a second representation of law that DRIFTED: the prior sidecar
|
|
3949
|
+
// omitted `principal` and `bornAt`, two fields the USDA declares
|
|
3950
|
+
// `nomos:required = true`, so anything reading it saw a contract the kernel would refuse.
|
|
3951
|
+
// One fact, one representation: the typed USDA closure is the contract.
|
|
3946
3952
|
|
|
3947
3953
|
// ── RECURSIVE-PAYLOAD NOTES (z.lazy) — a self-referencing tree/JSON payload field is FULLY
|
|
3948
3954
|
// supported: each codegen layer mints a named recursive type for it (TS `type <Name>`, Dart
|
|
@@ -5115,16 +5121,11 @@ async function main(): Promise<void> {
|
|
|
5115
5121
|
requiresKernel,
|
|
5116
5122
|
};
|
|
5117
5123
|
|
|
5118
|
-
// ── 3b.
|
|
5119
|
-
//
|
|
5120
|
-
//
|
|
5121
|
-
//
|
|
5122
|
-
|
|
5123
|
-
presentationDomainModules.map((m) => generateOpenApiFragment(m, { lawHash: domainHash })),
|
|
5124
|
-
{ title: `${cfg.name} (nomos package)`, lawHash: domainHash },
|
|
5125
|
-
);
|
|
5126
|
-
const openapiPath = path.join(outDir, `${cfg.name}.openapi.json`);
|
|
5127
|
-
writeFileSync(openapiPath, JSON.stringify(publicOpenapi, null, 2) + "\n", "utf8");
|
|
5124
|
+
// ── 3b. NO OPENAPI SIDECAR. OpenAPI remains the sanctioned standard for describing this package's
|
|
5125
|
+
// API — but it is SERVED, not stored: the kernel derives it from the installed typed USDA IR
|
|
5126
|
+
// (`GET /v2/workspaces/<ws>/openapi.json`), so the description cannot drift from the law it
|
|
5127
|
+
// describes. Writing a build-time copy created exactly the drift the representation rule forbids
|
|
5128
|
+
// — a derived second copy a reader could believe. Ask the holon that runs the law.
|
|
5128
5129
|
|
|
5129
5130
|
// ── 4. human build summary (diagnostic only; never deployment input) ──
|
|
5130
5131
|
const summary = {
|
|
@@ -5886,7 +5887,7 @@ async function main(): Promise<void> {
|
|
|
5886
5887
|
// STABLE IDS (#58): the minted/carried identity surface + every INFERRED rename —
|
|
5887
5888
|
// the silent lane made visible (if an inference is wrong — you meant remove+add —
|
|
5888
5889
|
// split the edit into two compiles, or answer the evolve gate with a disposition).
|
|
5889
|
-
console.log(`
|
|
5890
|
+
console.log(` identity ${rel(lockPath)} (committed typed USDA — identity across renames and retirements)`);
|
|
5890
5891
|
for (const { domain, renames } of inferredRenames) {
|
|
5891
5892
|
for (const r of renames) {
|
|
5892
5893
|
console.log(
|
|
@@ -5903,7 +5904,7 @@ async function main(): Promise<void> {
|
|
|
5903
5904
|
}
|
|
5904
5905
|
console.log(` client ${rel(clientPath)} (typed TS client — payloads, read models, query accessors)`);
|
|
5905
5906
|
for (const note of recursiveNotes) console.log(` note ${note}`);
|
|
5906
|
-
console.log(` openapi
|
|
5907
|
+
console.log(` openapi served by the holon from installed USDA (GET /v2/workspaces/<ws>/openapi.json) — no build-time copy to drift`);
|
|
5907
5908
|
if (proofPath !== undefined) {
|
|
5908
5909
|
console.log(` proof ${rel(proofPath)} (a runnable e2e GENERATED from your law — run: githolon proof)`);
|
|
5909
5910
|
} else {
|
package/src/decl_shapes.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* Why shared: the kernel-served OpenAPI (G1 — openapi residue removal) is produced by the lump
|
|
11
11
|
* running `generateOpenApiFragment` over the SAME modules it dispatches; if the lump discovered
|
|
12
12
|
* queries/counts/sums/spatials by a DIFFERENT shape than the compiler did when it wrote
|
|
13
|
-
*
|
|
13
|
+
* the kernel-served OpenAPI, runtime description would drift from installed law. One predicate
|
|
14
14
|
* set ⇒ one shape ⇒ byte-identity.
|
|
15
15
|
*
|
|
16
16
|
* ENGINE-BUNDLE-SAFE: imports NO node builtin (only DSL types) — it bundles under esbuild
|
package/src/engine_entry.ts
CHANGED
|
@@ -175,7 +175,7 @@ export interface EngineEntryConfig {
|
|
|
175
175
|
readonly openapiDocument?: OpenApiDoc;
|
|
176
176
|
/**
|
|
177
177
|
* The package's OpenAPI document title (the compiler's `"<name> (nomos package)"`). Baked into
|
|
178
|
-
* the lump so `describe` reproduces the compiler's
|
|
178
|
+
* the lump so `describe` reproduces the compiler's public description byte-for-byte without
|
|
179
179
|
* the kernel knowing the package name. The kernel MAY override it (the workspace-level title).
|
|
180
180
|
*/
|
|
181
181
|
readonly packageTitle?: string;
|
|
@@ -1075,7 +1075,7 @@ export function registerEngine(config: EngineEntryConfig): RegisteredEngine {
|
|
|
1075
1075
|
* dispatches. The kernel selects the content-addressed describe plan from the native USD
|
|
1076
1076
|
* package in custody, so the workspace's API surface is a pure,
|
|
1077
1077
|
* deterministic function of installed law — NOT a host-staged blob. Its public `paths` reproduce
|
|
1078
|
-
* the
|
|
1078
|
+
* the public description the compiler derived; describe also carries a private, law-derived
|
|
1079
1079
|
* framework routing manifest which the host consumes internally and never publishes.
|
|
1080
1080
|
* `opts.lawHash`/`opts.title` are injected by the kernel at describe-time (the version stamp +
|
|
1081
1081
|
* package title), matching the compiler's `domainHash`/`"<name> (nomos package)"`.
|
package/src/evolve_preview.ts
CHANGED
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
* law get typed 422s until regenerated (directives are not sid-tracked).
|
|
27
27
|
*
|
|
28
28
|
* The preview NEVER needs to emit a `rebinds` fragment: a rename is detected by a MATCHED sid
|
|
29
|
-
* (the compiler carries it via the nomos.
|
|
29
|
+
* (the compiler carries it via the nomos.identity.usda lockfile), so it is already a silent
|
|
30
30
|
* admit — rebinds is only for the hand-repair path where the sid was lost, which this preview,
|
|
31
31
|
* reading the carried sids, never produces.
|
|
32
32
|
*/
|
|
@@ -34,8 +34,8 @@ import type { Finding } from "./law_check.js";
|
|
|
34
34
|
import type { DirectivePayloadShape, DomainInterface } from "./shape_compat.js";
|
|
35
35
|
import { domainInterface } from "./shape_compat.js";
|
|
36
36
|
import type { StableIds } from "./stable_ids_types.js";
|
|
37
|
-
import type {
|
|
38
|
-
export type {
|
|
37
|
+
import type { CompilerIdentityModel } from "./identity_model.js";
|
|
38
|
+
export type { CompilerIdentityModel } from "./identity_model.js";
|
|
39
39
|
import type { DomainModule } from "./codegen_dart.js";
|
|
40
40
|
import type { WireSchema } from "./wire.js";
|
|
41
41
|
|
|
@@ -137,7 +137,7 @@ function evolveDomainFromInterface(iface: DomainInterface): EvolveDomain {
|
|
|
137
137
|
* by the kernel; `.law.usdz` is only its lossless one-file transport envelope. */
|
|
138
138
|
export function evolveLawFromInterface(
|
|
139
139
|
ifaces: Record<string, DomainInterface>,
|
|
140
|
-
lock?:
|
|
140
|
+
lock?: CompilerIdentityModel,
|
|
141
141
|
): EvolveLaw {
|
|
142
142
|
const law: EvolveLaw = {};
|
|
143
143
|
for (const [key, iface] of Object.entries(ifaces)) {
|
|
@@ -180,10 +180,10 @@ export function evolveLawFromInterface(
|
|
|
180
180
|
return law;
|
|
181
181
|
}
|
|
182
182
|
|
|
183
|
-
/** Build an EvolveLaw from a `nomos.
|
|
183
|
+
/** Build an EvolveLaw from a `nomos.identity.usda` lockfile — aggregate identity + drivers, but
|
|
184
184
|
* NO directive payloads (a lockfile carries no interface). Directive-level warnings are then
|
|
185
185
|
* unavailable; the caller notes it. */
|
|
186
|
-
export function evolveLawFromLockfile(lock:
|
|
186
|
+
export function evolveLawFromLockfile(lock: CompilerIdentityModel): EvolveLaw {
|
|
187
187
|
const law: EvolveLaw = {};
|
|
188
188
|
for (const [key, d] of Object.entries(lock.domains ?? {})) {
|
|
189
189
|
law[key] = {
|
|
@@ -201,7 +201,7 @@ export function evolveLawFromLockfile(lock: StableIdLockfile): EvolveLaw {
|
|
|
201
201
|
* pre-#58 fallback) — a rename then reads as removal+add; the caller warns to compile first. */
|
|
202
202
|
export function currentEvolveLaw(
|
|
203
203
|
modules: readonly DomainModule[],
|
|
204
|
-
lock?:
|
|
204
|
+
lock?: CompilerIdentityModel,
|
|
205
205
|
): EvolveLaw {
|
|
206
206
|
const law: EvolveLaw = {};
|
|
207
207
|
for (const mod of modules) {
|