@wairon/cli 5.1.1-dev.12 → 5.1.1-dev.13
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/dist/cli/index.js +351 -99
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +245 -75
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1312,7 +1312,7 @@ var init_defaults = __esm({
|
|
|
1312
1312
|
copilot: ".github/prompts",
|
|
1313
1313
|
codex: ".codex/agents"
|
|
1314
1314
|
};
|
|
1315
|
-
WAIRON_VERSION = "5.1.1-dev.
|
|
1315
|
+
WAIRON_VERSION = "5.1.1-dev.13";
|
|
1316
1316
|
GITHUB_REPO = "SYW-Apps/Waffle-AIron";
|
|
1317
1317
|
ARCHITECT_AGENT_ID = "agent-architect";
|
|
1318
1318
|
ARCHITECT_TEMPLATE_ID = "architect";
|
|
@@ -2884,17 +2884,43 @@ function isExternalNamespaceRef(ctx, ref) {
|
|
|
2884
2884
|
if (sep6 === -1) return false;
|
|
2885
2885
|
return !ctx.subsystemIds.has(ref.slice(0, sep6));
|
|
2886
2886
|
}
|
|
2887
|
+
function isProvidedBy(snapshot, provider) {
|
|
2888
|
+
return snapshot.projectName === provider || snapshot.projectName.split("::").pop() === provider;
|
|
2889
|
+
}
|
|
2890
|
+
function sameContract(a, b) {
|
|
2891
|
+
return a.component.split("::").pop() === b.component.split("::").pop() && JSON.stringify(a.methods) === JSON.stringify(b.methods) && JSON.stringify(a.dispatch ?? []) === JSON.stringify(b.dispatch ?? []);
|
|
2892
|
+
}
|
|
2887
2893
|
function resolveSurfaceRef(ctx, ref, fromSubsystem) {
|
|
2888
|
-
const
|
|
2889
|
-
|
|
2894
|
+
const segments = ref.split("::").filter((seg) => seg && seg !== "super");
|
|
2895
|
+
const local = segments.pop();
|
|
2896
|
+
if (!local) return { kind: "unresolved" };
|
|
2897
|
+
const provider = segments.pop();
|
|
2890
2898
|
const mountPools = fromSubsystem ? enclosingMounts(ctx, fromSubsystem).reverse().map((ns) => ctx.mountSurfaceSnapshots.find((m) => m.namespace === ns)?.snapshots ?? []) : [];
|
|
2891
2899
|
for (const pool of [...mountPools, ctx.surfaceSnapshots]) {
|
|
2900
|
+
const candidates = [];
|
|
2892
2901
|
for (const snapshot of pool) {
|
|
2902
|
+
if (provider !== void 0 && !isProvidedBy(snapshot, provider)) continue;
|
|
2893
2903
|
const entry = snapshot.interfaces.find((e) => e.component === local || e.id === local);
|
|
2894
|
-
if (entry)
|
|
2904
|
+
if (entry) candidates.push({ snapshot, entry });
|
|
2905
|
+
}
|
|
2906
|
+
if (candidates.length === 0) continue;
|
|
2907
|
+
const [first] = candidates;
|
|
2908
|
+
if (candidates.every((c) => sameContract(c.entry, first.entry))) {
|
|
2909
|
+
return { kind: "resolved", ...first };
|
|
2895
2910
|
}
|
|
2911
|
+
return { kind: "ambiguous", providers: [...new Set(candidates.map((c) => c.snapshot.projectName))] };
|
|
2896
2912
|
}
|
|
2897
|
-
return
|
|
2913
|
+
return { kind: "unresolved" };
|
|
2914
|
+
}
|
|
2915
|
+
function reportAmbiguousSurfaceRef(ctx, subject, ref, providers, specId, isDraftContext) {
|
|
2916
|
+
const local = ref.split("::").filter((seg) => seg && seg !== "super").pop() ?? ref;
|
|
2917
|
+
ctx.addIssue(
|
|
2918
|
+
"error",
|
|
2919
|
+
"SURFACE_REF_AMBIGUOUS",
|
|
2920
|
+
`${subject} cross-tree component "${ref}", which the surface snapshots of ${providers.map((p) => `"${p}"`).join(", ")} expose with different contracts \u2014 the reference matches more than one declared contract, so none of them can judge it. Name the provider it means (super::<provider>::${local}), or remove the snapshot that no longer applies.`,
|
|
2921
|
+
specId,
|
|
2922
|
+
isDraftContext
|
|
2923
|
+
);
|
|
2898
2924
|
}
|
|
2899
2925
|
function enclosingMounts(ctx, subsystemId) {
|
|
2900
2926
|
const mounts = [];
|
|
@@ -2985,6 +3011,7 @@ var init_contracts = __esm({
|
|
|
2985
3011
|
{ code: "INVALID_TARGET_COMPONENT_REFERENCE", defaultSeverity: "error", summary: "Call/register step targets a non-existent component" },
|
|
2986
3012
|
{ code: "CROSS_TREE_REF_UNRESOLVED", defaultSeverity: "warning", summary: "Cross-tree reference (super::/:: form) with no surface snapshot covering it \u2014 only the parent project can verify it" },
|
|
2987
3013
|
{ code: "SURFACE_REF_NOT_EXPOSED", defaultSeverity: "error", summary: "Cross-tree reference resolves to a surface snapshot that does not expose the called method/capability" },
|
|
3014
|
+
{ code: "SURFACE_REF_AMBIGUOUS", defaultSeverity: "error", summary: "Cross-tree call/dispatch/register target matched by surface snapshots of several providers with different contracts" },
|
|
2988
3015
|
{ code: "UNDECLARED_DEPENDENCY_CALL", defaultSeverity: "error", summary: "Call step targets a component the caller does not depend on or own" },
|
|
2989
3016
|
{ code: "INVALID_TARGET_METHOD_REFERENCE", defaultSeverity: "error", summary: "Call step targets a method not on any target interface" },
|
|
2990
3017
|
{ code: "NARRATIVE_SEMANTIC_UNBACKED", defaultSeverity: "warning", summary: "Narrative asserts a guarantee the called contract does not declare" }
|
|
@@ -3039,7 +3066,18 @@ var init_contracts = __esm({
|
|
|
3039
3066
|
if (!dispatchTarget) {
|
|
3040
3067
|
if (isCrossTreeForm || isCollapsedForm) {
|
|
3041
3068
|
const resolved = resolveSurfaceRef(ctx, step.targetComponent, fromSubsystem);
|
|
3042
|
-
if (resolved) {
|
|
3069
|
+
if (resolved.kind === "ambiguous") {
|
|
3070
|
+
reportAmbiguousSurfaceRef(
|
|
3071
|
+
ctx,
|
|
3072
|
+
`Method "${implMethod.name}" in implementation "${impl.id}" dispatches (step ${step.stepNumber}) through`,
|
|
3073
|
+
step.targetComponent,
|
|
3074
|
+
resolved.providers,
|
|
3075
|
+
impl.id,
|
|
3076
|
+
isDraftCtx
|
|
3077
|
+
);
|
|
3078
|
+
continue;
|
|
3079
|
+
}
|
|
3080
|
+
if (resolved.kind === "resolved") {
|
|
3043
3081
|
if (step.capability && !(resolved.entry.dispatch ?? []).some((b) => b.capability === step.capability)) {
|
|
3044
3082
|
ctx.addIssue(
|
|
3045
3083
|
"error",
|
|
@@ -3099,7 +3137,18 @@ var init_contracts = __esm({
|
|
|
3099
3137
|
if (!targetComp) {
|
|
3100
3138
|
if (isCrossTreeForm || isCollapsedForm) {
|
|
3101
3139
|
const resolved = resolveSurfaceRef(ctx, step.targetComponent, fromSubsystem);
|
|
3102
|
-
if (resolved) {
|
|
3140
|
+
if (resolved.kind === "ambiguous") {
|
|
3141
|
+
reportAmbiguousSurfaceRef(
|
|
3142
|
+
ctx,
|
|
3143
|
+
`Method "${implMethod.name}" in implementation "${impl.id}" ${verb} "${step.targetMethod}" (step ${step.stepNumber}) on`,
|
|
3144
|
+
step.targetComponent,
|
|
3145
|
+
resolved.providers,
|
|
3146
|
+
impl.id,
|
|
3147
|
+
isDraftCtx
|
|
3148
|
+
);
|
|
3149
|
+
continue;
|
|
3150
|
+
}
|
|
3151
|
+
if (resolved.kind === "resolved") {
|
|
3103
3152
|
const surfaceMethod = resolved.entry.methods.find((m) => m.name === step.targetMethod);
|
|
3104
3153
|
if (!surfaceMethod) {
|
|
3105
3154
|
ctx.addIssue(
|
|
@@ -4614,6 +4663,7 @@ var init_stereotype_deps = __esm({
|
|
|
4614
4663
|
codes: [
|
|
4615
4664
|
{ code: "INVALID_DEPENDENCY_REFERENCE", defaultSeverity: "error", summary: "dependsOn names a non-existent component" },
|
|
4616
4665
|
{ code: "CROSS_TREE_REF_UNRESOLVED", defaultSeverity: "warning", summary: "Cross-tree dependsOn (super::/:: form) with no surface snapshot covering it" },
|
|
4666
|
+
{ code: "SURFACE_REF_AMBIGUOUS", defaultSeverity: "error", summary: "Cross-tree dependsOn matched by surface snapshots of several providers with different contracts" },
|
|
4617
4667
|
{ code: "CROSS_SUBSYSTEM_NON_ADAPTER", defaultSeverity: "error", summary: "Non-Adapter component crossing a subsystem boundary" },
|
|
4618
4668
|
{ code: "CROSS_SUBSYSTEM_PRIVATE_ACCESS", defaultSeverity: "error", summary: "Cross-subsystem dependency on an unpublished component" },
|
|
4619
4669
|
{ code: "CROSS_SUBSYSTEM_TARGET_NON_PORTAL", defaultSeverity: "error", summary: "Cross-subsystem hop entering through a non-Portal" },
|
|
@@ -4637,7 +4687,11 @@ var init_stereotype_deps = __esm({
|
|
|
4637
4687
|
const external = isExternalNamespaceRef(ctx, depId);
|
|
4638
4688
|
if (external || isCollapsedCrossTreeRef(ctx, depId, comp.subsystem)) {
|
|
4639
4689
|
const resolved = resolveSurfaceRef(ctx, depId, comp.subsystem);
|
|
4640
|
-
if (resolved) {
|
|
4690
|
+
if (resolved.kind === "ambiguous") {
|
|
4691
|
+
reportAmbiguousSurfaceRef(ctx, `Component "${comp.id}" depends on`, depId, resolved.providers, comp.id, isDraftCtx);
|
|
4692
|
+
continue;
|
|
4693
|
+
}
|
|
4694
|
+
if (resolved.kind === "resolved") {
|
|
4641
4695
|
if (comp.componentType !== "Adapter") {
|
|
4642
4696
|
ctx.addIssue(
|
|
4643
4697
|
"error",
|
|
@@ -8343,8 +8397,8 @@ function doctrineIdentity(doctrine, gate) {
|
|
|
8343
8397
|
)
|
|
8344
8398
|
};
|
|
8345
8399
|
}
|
|
8346
|
-
function hashGateState(doctrine, gate = {}) {
|
|
8347
|
-
const payload = { tree: loadTree(), doctrine: doctrineIdentity(doctrine, gate) };
|
|
8400
|
+
function hashGateState(doctrine, inputs, gate = {}) {
|
|
8401
|
+
const payload = { tree: loadTree(), doctrine: doctrineIdentity(doctrine, gate), inputs: [...inputs].sort() };
|
|
8348
8402
|
const digest2 = crypto2.createHash("sha256").update(canonicalize(payload)).digest("hex");
|
|
8349
8403
|
return { algorithm: GATE_ALGORITHM, digest: digest2 };
|
|
8350
8404
|
}
|
|
@@ -8375,7 +8429,7 @@ var init_statehash = __esm({
|
|
|
8375
8429
|
init_specs2();
|
|
8376
8430
|
init_rules();
|
|
8377
8431
|
CONTENT_ALGORITHM = "sha256";
|
|
8378
|
-
GATE_ALGORITHM = "sha256+doctrine";
|
|
8432
|
+
GATE_ALGORITHM = "sha256+doctrine+inputs";
|
|
8379
8433
|
}
|
|
8380
8434
|
});
|
|
8381
8435
|
|
|
@@ -14138,15 +14192,18 @@ function importSurface(sourcePath, origin) {
|
|
|
14138
14192
|
saveSnapshot(snapshot);
|
|
14139
14193
|
return snapshot;
|
|
14140
14194
|
}
|
|
14141
|
-
function
|
|
14142
|
-
|
|
14143
|
-
if (!parent) return null;
|
|
14144
|
-
const childRoot = getProjectRoot();
|
|
14145
|
-
const projected = runWithProjectRoot(parent.parentRoot, () => {
|
|
14195
|
+
function projectFamilySurfaces(parent) {
|
|
14196
|
+
return runWithProjectRoot(parent.parentRoot, () => {
|
|
14146
14197
|
invalidateSpecCache();
|
|
14147
14198
|
const siblings = loadSubsystemSpecs().filter((s) => !s.id.includes("::") && s.id !== parent.subsystemId);
|
|
14148
14199
|
return [projectChildSurface(), ...siblings.map((s) => projectSubsystemSurface(s.id))];
|
|
14149
14200
|
});
|
|
14201
|
+
}
|
|
14202
|
+
function pinFamilySurfaces() {
|
|
14203
|
+
const parent = resolveChainingParent();
|
|
14204
|
+
if (!parent) return null;
|
|
14205
|
+
const childRoot = getProjectRoot();
|
|
14206
|
+
const projected = projectFamilySurfaces(parent);
|
|
14150
14207
|
const before = new Map(listSnapshots(childRoot).map((s) => [s.projectName, surfaceContentKey(s)]));
|
|
14151
14208
|
const changed = [];
|
|
14152
14209
|
for (const snapshot of projected) {
|
|
@@ -14157,17 +14214,21 @@ function pinFamilySurfaces() {
|
|
|
14157
14214
|
}
|
|
14158
14215
|
return changed;
|
|
14159
14216
|
}
|
|
14160
|
-
function
|
|
14161
|
-
|
|
14217
|
+
function projectedFamilyContent(parent) {
|
|
14218
|
+
try {
|
|
14219
|
+
return new Map(projectFamilySurfaces(parent).map((s) => [s.projectName, surfaceContentKey(s)]));
|
|
14220
|
+
} catch {
|
|
14221
|
+
return null;
|
|
14222
|
+
}
|
|
14162
14223
|
}
|
|
14163
14224
|
function listExternalInterfaces() {
|
|
14164
14225
|
const snapshots = listSnapshots();
|
|
14165
14226
|
const chainingParent = resolveChainingParent();
|
|
14166
|
-
const
|
|
14227
|
+
const projected = chainingParent ? projectedFamilyContent(chainingParent) : null;
|
|
14167
14228
|
return snapshots.map((snapshot) => {
|
|
14168
14229
|
const generated = snapshot.origin === "generated";
|
|
14169
14230
|
const sourceKind = !generated ? "foreign" : snapshot.projectName.includes("::") ? "sibling" : "parent";
|
|
14170
|
-
const freshness = generated &&
|
|
14231
|
+
const freshness = generated && projected ? projected.get(snapshot.projectName) === surfaceContentKey(snapshot) ? "fresh" : "stale" : "unverifiable";
|
|
14171
14232
|
return {
|
|
14172
14233
|
projectName: snapshot.projectName,
|
|
14173
14234
|
origin: snapshot.origin,
|
|
@@ -14505,7 +14566,7 @@ function validateSddTree(rulesOrOptions, projectType = "backend") {
|
|
|
14505
14566
|
rule.check(ctx);
|
|
14506
14567
|
}
|
|
14507
14568
|
const unresolved = (i) => RESOLUTION_FAILURE_CODES.has(i.code) && !i.surfaceResolved;
|
|
14508
|
-
const chainingParent = crossTree !== "off" && issues.some(unresolved) ?
|
|
14569
|
+
const chainingParent = crossTree !== "off" && issues.some(unresolved) ? resolveChainingParent() : null;
|
|
14509
14570
|
const resolution = chainingParent ? resolveThroughParent(getProjectRoot(), treatAllAsComplete, rules) : null;
|
|
14510
14571
|
if (resolution) {
|
|
14511
14572
|
const judgedByParent = worstByKey(resolution.issues);
|
|
@@ -14543,7 +14604,7 @@ function resolveThroughParent(boundRoot, treatAllAsComplete, childRules) {
|
|
|
14543
14604
|
const chain = [];
|
|
14544
14605
|
let top = path14.resolve(boundRoot);
|
|
14545
14606
|
while (top !== ceiling) {
|
|
14546
|
-
const hop = findChainingParent(top);
|
|
14607
|
+
const hop = findChainingParent(top, ceiling);
|
|
14547
14608
|
if (!hop) break;
|
|
14548
14609
|
const next = path14.resolve(hop.parentRoot);
|
|
14549
14610
|
if (ceiling && !isWithinOrEqual(ceiling, next)) break;
|
|
@@ -15273,33 +15334,75 @@ function relativizeId(id, prefix) {
|
|
|
15273
15334
|
if (common === idParts.length) common--;
|
|
15274
15335
|
return `${"super::".repeat(prefixParts.length - common)}${idParts.slice(common).join("::")}`;
|
|
15275
15336
|
}
|
|
15337
|
+
function rebaseReference(ref, mount, direction, isMoved = () => false) {
|
|
15338
|
+
if (!ref || ref.startsWith("::")) return ref;
|
|
15339
|
+
const segments = ref.split("::");
|
|
15340
|
+
let hops = 0;
|
|
15341
|
+
while (segments[hops] === "super") hops++;
|
|
15342
|
+
if (hops === segments.length) return ref;
|
|
15343
|
+
const outer = Array.from({ length: hops + 1 }, (_, i) => `<n${i}>`).join("::");
|
|
15344
|
+
const inner = `${outer}::${mount}`;
|
|
15345
|
+
const [from, to] = direction === "into" ? [outer, inner] : [inner, outer];
|
|
15346
|
+
const target = qualifyId(ref, from, NO_ROOT_SUBSYSTEMS);
|
|
15347
|
+
const left = relativizeId(target, from);
|
|
15348
|
+
const travels = !left.startsWith("super::") && (direction === "outOf" || isMoved(left));
|
|
15349
|
+
return relativizeId(travels ? qualifyId(left, to, NO_ROOT_SUBSYSTEMS) : target, to);
|
|
15350
|
+
}
|
|
15276
15351
|
function isWithin(dir, file) {
|
|
15277
15352
|
const d = path15.resolve(dir);
|
|
15278
15353
|
const f = path15.resolve(file);
|
|
15279
15354
|
return f === d || f.startsWith(d + path15.sep);
|
|
15280
15355
|
}
|
|
15356
|
+
function canonicalPath(target) {
|
|
15357
|
+
let existing = path15.resolve(target);
|
|
15358
|
+
const rest = [];
|
|
15359
|
+
for (; ; ) {
|
|
15360
|
+
try {
|
|
15361
|
+
fs9.lstatSync(existing);
|
|
15362
|
+
break;
|
|
15363
|
+
} catch {
|
|
15364
|
+
const up = path15.dirname(existing);
|
|
15365
|
+
if (up === existing) return path15.resolve(target);
|
|
15366
|
+
rest.unshift(path15.basename(existing));
|
|
15367
|
+
existing = up;
|
|
15368
|
+
}
|
|
15369
|
+
}
|
|
15370
|
+
try {
|
|
15371
|
+
return path15.join(fs9.realpathSync.native(existing), ...rest);
|
|
15372
|
+
} catch {
|
|
15373
|
+
return null;
|
|
15374
|
+
}
|
|
15375
|
+
}
|
|
15376
|
+
function chainDirKey(dir) {
|
|
15377
|
+
return canonicalPath(dir) ?? path15.resolve(dir);
|
|
15378
|
+
}
|
|
15281
15379
|
function projectPathEscapesRoot(projectRoot, projectPath, resolvedChildDir) {
|
|
15282
|
-
|
|
15380
|
+
if (path15.isAbsolute(projectPath) || !isWithin(projectRoot, resolvedChildDir)) return true;
|
|
15381
|
+
const root = canonicalPath(projectRoot);
|
|
15382
|
+
const child = canonicalPath(resolvedChildDir);
|
|
15383
|
+
return root === null || child === null || !isWithin(root, child);
|
|
15283
15384
|
}
|
|
15284
15385
|
function assertContainedProjectPath(projectRoot, projectPath) {
|
|
15285
15386
|
const root = path15.resolve(projectRoot);
|
|
15286
15387
|
const resolved = path15.resolve(root, projectPath);
|
|
15287
15388
|
if (projectPathEscapesRoot(root, projectPath, resolved)) {
|
|
15288
15389
|
throw new Error(
|
|
15289
|
-
`projectPath "${projectPath}" must resolve within the project root "${root}", but resolves to "${resolved}"; absolute
|
|
15390
|
+
`projectPath "${projectPath}" must resolve within the project root "${root}", but resolves to "${resolved}"; absolute, ../-escaping and link-escaping paths are rejected so a chained subproject is always contained by its parent.`
|
|
15290
15391
|
);
|
|
15291
15392
|
}
|
|
15292
15393
|
return resolved;
|
|
15293
15394
|
}
|
|
15294
|
-
function findChainingParent(childRoot) {
|
|
15395
|
+
function findChainingParent(childRoot, ceiling) {
|
|
15295
15396
|
let childResolved;
|
|
15296
15397
|
try {
|
|
15297
15398
|
childResolved = path15.resolve(childRoot);
|
|
15298
15399
|
} catch {
|
|
15299
15400
|
return null;
|
|
15300
15401
|
}
|
|
15402
|
+
const bound = ceiling ? path15.resolve(ceiling) : void 0;
|
|
15301
15403
|
let dir = path15.dirname(childResolved);
|
|
15302
15404
|
for (let hops = 0; hops < 32; hops++) {
|
|
15405
|
+
if (bound && !isWithin(bound, dir)) break;
|
|
15303
15406
|
const specsDir = aiPathsAt(dir).specsDir();
|
|
15304
15407
|
if (pathExists(specsDir)) {
|
|
15305
15408
|
for (const file of listFilesRecursive(specsDir, ".yaml")) {
|
|
@@ -15313,7 +15416,8 @@ function findChainingParent(childRoot) {
|
|
|
15313
15416
|
const projectPath = raw.projectPath;
|
|
15314
15417
|
if (typeof projectPath === "string" && projectPath.trim() !== "") {
|
|
15315
15418
|
try {
|
|
15316
|
-
|
|
15419
|
+
const mountDir = path15.resolve(dir, projectPath);
|
|
15420
|
+
if (mountDir === childResolved && !projectPathEscapesRoot(dir, projectPath, mountDir)) {
|
|
15317
15421
|
const id = raw.id;
|
|
15318
15422
|
return { parentRoot: dir, subsystemId: typeof id === "string" ? id : "?" };
|
|
15319
15423
|
}
|
|
@@ -15329,12 +15433,11 @@ function findChainingParent(childRoot) {
|
|
|
15329
15433
|
}
|
|
15330
15434
|
return null;
|
|
15331
15435
|
}
|
|
15332
|
-
function
|
|
15436
|
+
function inspectChainedRoots(rootDir = getProjectRoot()) {
|
|
15333
15437
|
const root = path15.resolve(rootDir);
|
|
15334
|
-
const
|
|
15335
|
-
const
|
|
15336
|
-
const walk = (projectDir, depth) => {
|
|
15337
|
-
if (depth > 32) return;
|
|
15438
|
+
const inspection = { roots: [], skipped: [] };
|
|
15439
|
+
const listed = /* @__PURE__ */ new Set();
|
|
15440
|
+
const walk = (projectDir, prefix, ancestors, depth) => {
|
|
15338
15441
|
const specsDir = aiPathsAt(projectDir).specsDir();
|
|
15339
15442
|
if (!pathExists(specsDir)) return;
|
|
15340
15443
|
for (const file of listFilesRecursive(specsDir, ".yaml")) {
|
|
@@ -15345,24 +15448,37 @@ function listChainedRoots(rootDir = getProjectRoot()) {
|
|
|
15345
15448
|
continue;
|
|
15346
15449
|
}
|
|
15347
15450
|
if (!raw || typeof raw !== "object" || !("parentSystem" in raw)) continue;
|
|
15348
|
-
const projectPath = raw
|
|
15451
|
+
const { id, projectPath } = raw;
|
|
15349
15452
|
if (typeof projectPath !== "string" || projectPath.trim() === "") continue;
|
|
15453
|
+
const localId = typeof id === "string" ? id : "?";
|
|
15350
15454
|
let childDir;
|
|
15351
15455
|
try {
|
|
15352
15456
|
childDir = path15.resolve(projectDir, projectPath);
|
|
15353
15457
|
} catch {
|
|
15354
15458
|
continue;
|
|
15355
15459
|
}
|
|
15356
|
-
|
|
15357
|
-
if (
|
|
15358
|
-
if (
|
|
15359
|
-
|
|
15360
|
-
|
|
15361
|
-
|
|
15460
|
+
let reason;
|
|
15461
|
+
if (projectPathEscapesRoot(projectDir, projectPath, childDir)) reason = "escapes";
|
|
15462
|
+
else if (ancestors.has(chainDirKey(childDir))) reason = "cyclic";
|
|
15463
|
+
else if (!fs9.existsSync(childDir)) reason = "missing";
|
|
15464
|
+
else if (depth >= 32) reason = "too-deep";
|
|
15465
|
+
if (reason) {
|
|
15466
|
+
const mount = prefix ? qualifyDeclaredId(localId, prefix, true) : localId;
|
|
15467
|
+
inspection.skipped.push({ mount, projectPath, reason });
|
|
15468
|
+
continue;
|
|
15469
|
+
}
|
|
15470
|
+
const key = chainDirKey(childDir);
|
|
15471
|
+
if (listed.has(key)) continue;
|
|
15472
|
+
listed.add(key);
|
|
15473
|
+
inspection.roots.push(path15.relative(root, childDir).split(path15.sep).join("/"));
|
|
15474
|
+
walk(childDir, prefix ? `${prefix}::${localId}` : localId, /* @__PURE__ */ new Set([...ancestors, key]), depth + 1);
|
|
15362
15475
|
}
|
|
15363
15476
|
};
|
|
15364
|
-
walk(root, 0);
|
|
15365
|
-
return
|
|
15477
|
+
walk(root, "", /* @__PURE__ */ new Set([chainDirKey(root)]), 0);
|
|
15478
|
+
return inspection;
|
|
15479
|
+
}
|
|
15480
|
+
function listChainedRoots(rootDir = getProjectRoot()) {
|
|
15481
|
+
return inspectChainedRoots(rootDir).roots;
|
|
15366
15482
|
}
|
|
15367
15483
|
function mergeMountRealizations(subs) {
|
|
15368
15484
|
const result = [];
|
|
@@ -15645,12 +15761,24 @@ function buildProjectGraph(level) {
|
|
|
15645
15761
|
function resolveChainingParent() {
|
|
15646
15762
|
const reach = getRequestParentReach();
|
|
15647
15763
|
if (reach && !reach.parentReach) return null;
|
|
15648
|
-
|
|
15649
|
-
|
|
15650
|
-
|
|
15651
|
-
|
|
15764
|
+
return findChainingParent(getProjectRoot(), reach?.topRoot);
|
|
15765
|
+
}
|
|
15766
|
+
function snapshotInputKey(snapshot) {
|
|
15767
|
+
const { stateId, generatedAt, origin, ...content } = snapshot;
|
|
15768
|
+
return canonicalize(content);
|
|
15769
|
+
}
|
|
15770
|
+
function consumedSurfaceInputsAt(rootDir) {
|
|
15771
|
+
const dir = path15.join(rootDir, ".wai", "surfaces");
|
|
15772
|
+
if (!pathExists(dir)) return [];
|
|
15773
|
+
const keys = [];
|
|
15774
|
+
for (const file of fs9.readdirSync(dir)) {
|
|
15775
|
+
if (!file.endsWith(".yaml") && !file.endsWith(".yml")) continue;
|
|
15776
|
+
try {
|
|
15777
|
+
keys.push(snapshotInputKey(SurfaceSnapshotSchema.parse(readYamlFile(path15.join(dir, file)))));
|
|
15778
|
+
} catch {
|
|
15779
|
+
}
|
|
15652
15780
|
}
|
|
15653
|
-
return
|
|
15781
|
+
return keys;
|
|
15654
15782
|
}
|
|
15655
15783
|
function computeGateStateId() {
|
|
15656
15784
|
let gate = {};
|
|
@@ -15659,7 +15787,10 @@ function computeGateStateId() {
|
|
|
15659
15787
|
gate = { projectType: config.projectType, rules: config.rules };
|
|
15660
15788
|
} catch {
|
|
15661
15789
|
}
|
|
15662
|
-
|
|
15790
|
+
const root = getProjectRoot();
|
|
15791
|
+
const roots = [root, ...listChainedRoots(root).map((rel2) => path15.join(root, rel2))];
|
|
15792
|
+
const inputs = roots.flatMap(consumedSurfaceInputsAt);
|
|
15793
|
+
return hashGateState(loadProjectExtensions(), inputs, gate);
|
|
15663
15794
|
}
|
|
15664
15795
|
function readLockState() {
|
|
15665
15796
|
const record = readLockRecord();
|
|
@@ -15769,7 +15900,7 @@ var init_specs2 = __esm({
|
|
|
15769
15900
|
this.rootSubsystems.clear();
|
|
15770
15901
|
this.cachedRecursive = recursive;
|
|
15771
15902
|
this.scanVisitedSpecDirs = [];
|
|
15772
|
-
const visited = /* @__PURE__ */ new Set([
|
|
15903
|
+
const visited = /* @__PURE__ */ new Set([chainDirKey(this.rootDir)]);
|
|
15773
15904
|
const maxDepth = typeof recursive === "number" ? recursive : recursive ? Infinity : 0;
|
|
15774
15905
|
this.cachedIndex = this.scanSpecsForProject(this.rootDir, "", visited, maxDepth, 0);
|
|
15775
15906
|
this.cachedSpecDirs = this.scanVisitedSpecDirs;
|
|
@@ -15969,21 +16100,22 @@ var init_specs2 = __esm({
|
|
|
15969
16100
|
if (currentDepth < maxDepth) {
|
|
15970
16101
|
for (const subproj of localSubprojects) {
|
|
15971
16102
|
const childDir = path15.resolve(projectDir, subproj.projectPath);
|
|
15972
|
-
|
|
16103
|
+
const mountId = namespacePrefix ? qualifyDeclaredId(subproj.subsystemId, namespacePrefix, true) : subproj.subsystemId;
|
|
16104
|
+
if (projectPathEscapesRoot(projectDir, subproj.projectPath, childDir)) {
|
|
15973
16105
|
this.loaderIssues.push({
|
|
15974
16106
|
severity: "error",
|
|
15975
16107
|
code: "PROJECTPATH_ESCAPE",
|
|
15976
|
-
message: `Subproject path "${subproj.projectPath}" declared by subsystem "${
|
|
15977
|
-
specId:
|
|
16108
|
+
message: `Subproject path "${subproj.projectPath}" declared by subsystem "${mountId}" escapes the root of the project declaring it, "${projectDir}" (resolves to "${childDir}"); absolute, ../-escaping and link-escaping projectPaths are rejected. Skipping this subproject.`,
|
|
16109
|
+
specId: mountId
|
|
15978
16110
|
});
|
|
15979
16111
|
continue;
|
|
15980
16112
|
}
|
|
15981
|
-
if (visitedDirs.has(childDir)) {
|
|
16113
|
+
if (visitedDirs.has(chainDirKey(childDir))) {
|
|
15982
16114
|
this.loaderIssues.push({
|
|
15983
16115
|
severity: "error",
|
|
15984
16116
|
code: "CIRCULAR_SUBPROJECT_REFERENCE",
|
|
15985
|
-
message: `Circular reference detected: Subsystem "${
|
|
15986
|
-
specId:
|
|
16117
|
+
message: `Circular reference detected: Subsystem "${mountId}" refers to subproject "${childDir}" which is already loaded.`,
|
|
16118
|
+
specId: mountId
|
|
15987
16119
|
});
|
|
15988
16120
|
continue;
|
|
15989
16121
|
}
|
|
@@ -15991,14 +16123,14 @@ var init_specs2 = __esm({
|
|
|
15991
16123
|
this.loaderIssues.push({
|
|
15992
16124
|
severity: "error",
|
|
15993
16125
|
code: "SUBPROJECT_NOT_FOUND",
|
|
15994
|
-
message: `Subproject directory "${childDir}" declared by subsystem "${
|
|
15995
|
-
specId:
|
|
16126
|
+
message: `Subproject directory "${childDir}" declared by subsystem "${mountId}" does not exist.`,
|
|
16127
|
+
specId: mountId
|
|
15996
16128
|
});
|
|
15997
16129
|
continue;
|
|
15998
16130
|
}
|
|
15999
16131
|
const childNamespace = namespacePrefix ? `${namespacePrefix}::${subproj.subsystemId}` : subproj.subsystemId;
|
|
16000
16132
|
const newVisited = new Set(visitedDirs);
|
|
16001
|
-
newVisited.add(childDir);
|
|
16133
|
+
newVisited.add(chainDirKey(childDir));
|
|
16002
16134
|
const childIndex = this.scanSpecsForProject(childDir, childNamespace, newVisited, maxDepth, currentDepth + 1);
|
|
16003
16135
|
index.subsystems.push(...childIndex.subsystems);
|
|
16004
16136
|
index.components.push(...childIndex.components);
|
|
@@ -16031,11 +16163,11 @@ var init_specs2 = __esm({
|
|
|
16031
16163
|
const sub = index.subsystems.find((s) => s.id === currentPrefix);
|
|
16032
16164
|
if (sub && sub.projectPath) {
|
|
16033
16165
|
const nextDir = path15.resolve(currentDir, sub.projectPath);
|
|
16034
|
-
if (projectPathEscapesRoot(
|
|
16166
|
+
if (projectPathEscapesRoot(currentDir, sub.projectPath, nextDir)) {
|
|
16035
16167
|
this.loaderIssues.push({
|
|
16036
16168
|
severity: "error",
|
|
16037
16169
|
code: "PROJECTPATH_ESCAPE",
|
|
16038
|
-
message: `Subproject path "${sub.projectPath}" declared by subsystem "${currentPrefix}" escapes the project
|
|
16170
|
+
message: `Subproject path "${sub.projectPath}" declared by subsystem "${currentPrefix}" escapes the root of the project declaring it, "${currentDir}" (resolves to "${nextDir}"); absolute, ../-escaping and link-escaping projectPaths are rejected. Skipping this subproject.`,
|
|
16039
16171
|
specId: currentPrefix
|
|
16040
16172
|
});
|
|
16041
16173
|
continue;
|
|
@@ -21508,6 +21640,7 @@ __export(src_exports, {
|
|
|
21508
21640
|
buildRuleContext: () => buildRuleContext,
|
|
21509
21641
|
buildServerInstructions: () => buildServerInstructions2,
|
|
21510
21642
|
buildTreeArchive: () => buildTreeArchive2,
|
|
21643
|
+
canonicalize: () => canonicalize,
|
|
21511
21644
|
captureApprovedSpecs: () => captureApprovedSpecs,
|
|
21512
21645
|
checkSkillFreshness: () => checkSkillFreshness,
|
|
21513
21646
|
clearLoaderIssues: () => clearLoaderIssues,
|
|
@@ -21518,7 +21651,6 @@ __export(src_exports, {
|
|
|
21518
21651
|
composeVariantGuidance: () => composeVariantGuidance,
|
|
21519
21652
|
computeGateStateId: () => computeGateStateId,
|
|
21520
21653
|
computePackDigest: () => computePackDigest,
|
|
21521
|
-
computeParentStateId: () => computeParentStateId,
|
|
21522
21654
|
computeStateId: () => computeStateId,
|
|
21523
21655
|
computeStateIdAt: () => computeStateIdAt,
|
|
21524
21656
|
contextDir: () => contextDir,
|
|
@@ -21595,6 +21727,7 @@ __export(src_exports, {
|
|
|
21595
21727
|
hashGateState: () => hashGateState,
|
|
21596
21728
|
importSpecTree: () => importSpecTree,
|
|
21597
21729
|
importSurface: () => importSurface,
|
|
21730
|
+
inspectChainedRoots: () => inspectChainedRoots,
|
|
21598
21731
|
inspectTreeArchive: () => inspectTreeArchive2,
|
|
21599
21732
|
installPackFromDirectory: () => installPackFromDirectory,
|
|
21600
21733
|
internalizeSubsystem: () => internalizeSubsystem,
|
|
@@ -21673,6 +21806,7 @@ __export(src_exports, {
|
|
|
21673
21806
|
readResource: () => readResource,
|
|
21674
21807
|
readSkillResource: () => readSkillResource,
|
|
21675
21808
|
readYamlFile: () => readYamlFile,
|
|
21809
|
+
rebaseReference: () => rebaseReference,
|
|
21676
21810
|
registerExporter: () => registerExporter,
|
|
21677
21811
|
removeFreeStandingDomain: () => removeFreeStandingDomain,
|
|
21678
21812
|
removeSnapshot: () => removeSnapshot,
|
|
@@ -22166,6 +22300,7 @@ function externalizeSubsystem(subsystemId, projectPath) {
|
|
|
22166
22300
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
22167
22301
|
});
|
|
22168
22302
|
rewriteRefsInDir(parentSpecsDir, renameMap, fooDir);
|
|
22303
|
+
rebaseMovedRefs(childFooDir, subsystemId, "into");
|
|
22169
22304
|
invalidateSpecCache();
|
|
22170
22305
|
}
|
|
22171
22306
|
function internalizeSubsystem(subsystemId) {
|
|
@@ -22205,6 +22340,7 @@ function internalizeSubsystem(subsystemId) {
|
|
|
22205
22340
|
});
|
|
22206
22341
|
fs13.rmSync(childWai, { recursive: true, force: true });
|
|
22207
22342
|
rewriteRefsInDir(parentSpecsDir, renameMap, fooDir);
|
|
22343
|
+
rebaseMovedRefs(fooDir, subsystemId, "outOf");
|
|
22208
22344
|
invalidateSpecCache();
|
|
22209
22345
|
}
|
|
22210
22346
|
function buildRenameMap(subsystemId, externalize) {
|
|
@@ -22231,7 +22367,27 @@ function buildRenameMap(subsystemId, externalize) {
|
|
|
22231
22367
|
}
|
|
22232
22368
|
function rewriteRefsInDir(specsDir, renameMap, excludeDir) {
|
|
22233
22369
|
if (renameMap.size === 0) return;
|
|
22234
|
-
|
|
22370
|
+
rewriteRefFields(specsDir, (ref) => renameMap.get(ref) ?? ref, excludeDir);
|
|
22371
|
+
}
|
|
22372
|
+
function rebaseMovedRefs(movedDir, mount, direction) {
|
|
22373
|
+
const declared = componentIdsUnder(movedDir);
|
|
22374
|
+
rewriteRefFields(movedDir, (ref, position) => position === "component" ? rebaseReference(ref, mount, direction, (id) => declared.has(id)) : ref);
|
|
22375
|
+
}
|
|
22376
|
+
function componentIdsUnder(dir) {
|
|
22377
|
+
const ids = /* @__PURE__ */ new Set();
|
|
22378
|
+
for (const file of listFilesRecursive(dir, ".yaml")) {
|
|
22379
|
+
let raw;
|
|
22380
|
+
try {
|
|
22381
|
+
raw = readYamlFile(file);
|
|
22382
|
+
} catch {
|
|
22383
|
+
continue;
|
|
22384
|
+
}
|
|
22385
|
+
if (raw && typeof raw === "object" && "componentType" in raw && typeof raw.id === "string") ids.add(raw.id);
|
|
22386
|
+
}
|
|
22387
|
+
return ids;
|
|
22388
|
+
}
|
|
22389
|
+
function rewriteRefFields(specsDir, remap, excludeDir) {
|
|
22390
|
+
const at = (ref, position) => typeof ref === "string" ? remap(ref, position) : ref;
|
|
22235
22391
|
for (const file of listFilesRecursive(specsDir, ".yaml")) {
|
|
22236
22392
|
if (excludeDir && isWithinDir(excludeDir, file)) continue;
|
|
22237
22393
|
let raw;
|
|
@@ -22243,14 +22399,14 @@ function rewriteRefsInDir(specsDir, renameMap, excludeDir) {
|
|
|
22243
22399
|
if (!raw || typeof raw !== "object") continue;
|
|
22244
22400
|
let changed = false;
|
|
22245
22401
|
if ("componentType" in raw && Array.isArray(raw.dependsOn)) {
|
|
22246
|
-
const next = raw.dependsOn.map((d) =>
|
|
22402
|
+
const next = raw.dependsOn.map((d) => at(d, "component"));
|
|
22247
22403
|
if (next.some((v, i) => v !== raw.dependsOn[i])) {
|
|
22248
22404
|
raw.dependsOn = next;
|
|
22249
22405
|
changed = true;
|
|
22250
22406
|
}
|
|
22251
22407
|
if (Array.isArray(raw.dispatch)) {
|
|
22252
22408
|
for (const b of raw.dispatch) {
|
|
22253
|
-
const nc =
|
|
22409
|
+
const nc = at(b.component, "component");
|
|
22254
22410
|
if (nc !== b.component) {
|
|
22255
22411
|
b.component = nc;
|
|
22256
22412
|
changed = true;
|
|
@@ -22259,7 +22415,7 @@ function rewriteRefsInDir(specsDir, renameMap, excludeDir) {
|
|
|
22259
22415
|
}
|
|
22260
22416
|
} else if ("parentSystem" in raw && Array.isArray(raw.lifecycle)) {
|
|
22261
22417
|
for (const le of raw.lifecycle) {
|
|
22262
|
-
const nc =
|
|
22418
|
+
const nc = at(le.component, "component");
|
|
22263
22419
|
if (nc !== le.component) {
|
|
22264
22420
|
le.component = nc;
|
|
22265
22421
|
changed = true;
|
|
@@ -22269,7 +22425,7 @@ function rewriteRefsInDir(specsDir, renameMap, excludeDir) {
|
|
|
22269
22425
|
for (const m of raw.methods) {
|
|
22270
22426
|
if (!Array.isArray(m.params)) continue;
|
|
22271
22427
|
for (const p of m.params) {
|
|
22272
|
-
const nt =
|
|
22428
|
+
const nt = at(p.type, "type");
|
|
22273
22429
|
if (nt !== p.type) {
|
|
22274
22430
|
p.type = nt;
|
|
22275
22431
|
changed = true;
|
|
@@ -22280,7 +22436,7 @@ function rewriteRefsInDir(specsDir, renameMap, excludeDir) {
|
|
|
22280
22436
|
for (const m of raw.methods) {
|
|
22281
22437
|
if (!Array.isArray(m.narrative)) continue;
|
|
22282
22438
|
for (const step of m.narrative) {
|
|
22283
|
-
const nt =
|
|
22439
|
+
const nt = at(step.targetComponent, "component");
|
|
22284
22440
|
if (nt !== step.targetComponent) {
|
|
22285
22441
|
step.targetComponent = nt;
|
|
22286
22442
|
changed = true;
|
|
@@ -22289,7 +22445,7 @@ function rewriteRefsInDir(specsDir, renameMap, excludeDir) {
|
|
|
22289
22445
|
}
|
|
22290
22446
|
} else if ("kind" in raw && Array.isArray(raw.fields)) {
|
|
22291
22447
|
for (const f of raw.fields) {
|
|
22292
|
-
const nt =
|
|
22448
|
+
const nt = at(f.type, "type");
|
|
22293
22449
|
if (nt !== f.type) {
|
|
22294
22450
|
f.type = nt;
|
|
22295
22451
|
changed = true;
|
|
@@ -22885,18 +23041,29 @@ function discardStagingDir(stagingDir) {
|
|
|
22885
23041
|
} catch {
|
|
22886
23042
|
}
|
|
22887
23043
|
}
|
|
22888
|
-
function exportSpecTree(includeDerived) {
|
|
23044
|
+
function exportSpecTree(includeDerived, allowPartial) {
|
|
22889
23045
|
const system = loadSystemSpec();
|
|
22890
23046
|
if (!system) {
|
|
22891
23047
|
throw new Error("no spec tree to export at this project root");
|
|
22892
23048
|
}
|
|
22893
|
-
const
|
|
23049
|
+
const inspection = inspectChainedRoots();
|
|
22894
23050
|
const root = getProjectRoot();
|
|
22895
|
-
const
|
|
22896
|
-
|
|
22897
|
-
|
|
22898
|
-
|
|
22899
|
-
|
|
23051
|
+
const skipped = [...inspection.skipped];
|
|
23052
|
+
const roots = [{ relativePath: ".", waiDir: aiPathsAt(root).root() }];
|
|
23053
|
+
for (const rel2 of inspection.roots) {
|
|
23054
|
+
const waiDir = aiPathsAt(path22.resolve(root, rel2)).root();
|
|
23055
|
+
if (pathExists(waiDir)) {
|
|
23056
|
+
roots.push({ relativePath: rel2, waiDir });
|
|
23057
|
+
} else {
|
|
23058
|
+
skipped.push({ mount: rel2, projectPath: rel2, reason: "no-spec-tree" });
|
|
23059
|
+
}
|
|
23060
|
+
}
|
|
23061
|
+
if (skipped.length > 0 && !allowPartial) {
|
|
23062
|
+
const detail = skipped.map((s) => `${s.mount} (${s.reason})`).join(", ");
|
|
23063
|
+
throw new Error(
|
|
23064
|
+
`cannot export the whole spec tree \u2014 skipped: ${detail}. Pass allowPartial to export the rest anyway.`
|
|
23065
|
+
);
|
|
23066
|
+
}
|
|
22900
23067
|
const stateId = computeStateId();
|
|
22901
23068
|
const built = buildTreeArchive2(roots, system.name, stateId.digest, includeDerived);
|
|
22902
23069
|
const result = {
|
|
@@ -22905,7 +23072,8 @@ function exportSpecTree(includeDerived) {
|
|
|
22905
23072
|
projectName: built.manifest.projectName,
|
|
22906
23073
|
roots: built.manifest.roots,
|
|
22907
23074
|
fileCount: built.fileCount,
|
|
22908
|
-
stateId: stateId.digest
|
|
23075
|
+
stateId: stateId.digest,
|
|
23076
|
+
skipped
|
|
22909
23077
|
};
|
|
22910
23078
|
return result;
|
|
22911
23079
|
}
|
|
@@ -23426,6 +23594,7 @@ init_yaml();
|
|
|
23426
23594
|
buildRuleContext,
|
|
23427
23595
|
buildServerInstructions,
|
|
23428
23596
|
buildTreeArchive,
|
|
23597
|
+
canonicalize,
|
|
23429
23598
|
captureApprovedSpecs,
|
|
23430
23599
|
checkSkillFreshness,
|
|
23431
23600
|
clearLoaderIssues,
|
|
@@ -23436,7 +23605,6 @@ init_yaml();
|
|
|
23436
23605
|
composeVariantGuidance,
|
|
23437
23606
|
computeGateStateId,
|
|
23438
23607
|
computePackDigest,
|
|
23439
|
-
computeParentStateId,
|
|
23440
23608
|
computeStateId,
|
|
23441
23609
|
computeStateIdAt,
|
|
23442
23610
|
contextDir,
|
|
@@ -23513,6 +23681,7 @@ init_yaml();
|
|
|
23513
23681
|
hashGateState,
|
|
23514
23682
|
importSpecTree,
|
|
23515
23683
|
importSurface,
|
|
23684
|
+
inspectChainedRoots,
|
|
23516
23685
|
inspectTreeArchive,
|
|
23517
23686
|
installPackFromDirectory,
|
|
23518
23687
|
internalizeSubsystem,
|
|
@@ -23591,6 +23760,7 @@ init_yaml();
|
|
|
23591
23760
|
readResource,
|
|
23592
23761
|
readSkillResource,
|
|
23593
23762
|
readYamlFile,
|
|
23763
|
+
rebaseReference,
|
|
23594
23764
|
registerExporter,
|
|
23595
23765
|
removeFreeStandingDomain,
|
|
23596
23766
|
removeSnapshot,
|