@wairon/cli 5.1.1-dev.11 → 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 +457 -141
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +336 -112
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -65,7 +65,7 @@ var init_defaults = __esm({
|
|
|
65
65
|
copilot: ".github/prompts",
|
|
66
66
|
codex: ".codex/agents"
|
|
67
67
|
};
|
|
68
|
-
WAIRON_VERSION = "5.1.1-dev.
|
|
68
|
+
WAIRON_VERSION = "5.1.1-dev.13";
|
|
69
69
|
GITHUB_REPO = "SYW-Apps/Waffle-AIron";
|
|
70
70
|
ARCHITECT_AGENT_ID = "agent-architect";
|
|
71
71
|
ARCHITECT_TEMPLATE_ID = "architect";
|
|
@@ -3554,17 +3554,43 @@ function isExternalNamespaceRef(ctx, ref) {
|
|
|
3554
3554
|
if (sep9 === -1) return false;
|
|
3555
3555
|
return !ctx.subsystemIds.has(ref.slice(0, sep9));
|
|
3556
3556
|
}
|
|
3557
|
+
function isProvidedBy(snapshot, provider) {
|
|
3558
|
+
return snapshot.projectName === provider || snapshot.projectName.split("::").pop() === provider;
|
|
3559
|
+
}
|
|
3560
|
+
function sameContract(a, b) {
|
|
3561
|
+
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 ?? []);
|
|
3562
|
+
}
|
|
3557
3563
|
function resolveSurfaceRef(ctx, ref, fromSubsystem) {
|
|
3558
|
-
const
|
|
3559
|
-
|
|
3564
|
+
const segments = ref.split("::").filter((seg) => seg && seg !== "super");
|
|
3565
|
+
const local = segments.pop();
|
|
3566
|
+
if (!local) return { kind: "unresolved" };
|
|
3567
|
+
const provider = segments.pop();
|
|
3560
3568
|
const mountPools = fromSubsystem ? enclosingMounts(ctx, fromSubsystem).reverse().map((ns) => ctx.mountSurfaceSnapshots.find((m) => m.namespace === ns)?.snapshots ?? []) : [];
|
|
3561
3569
|
for (const pool of [...mountPools, ctx.surfaceSnapshots]) {
|
|
3570
|
+
const candidates = [];
|
|
3562
3571
|
for (const snapshot of pool) {
|
|
3572
|
+
if (provider !== void 0 && !isProvidedBy(snapshot, provider)) continue;
|
|
3563
3573
|
const entry = snapshot.interfaces.find((e) => e.component === local || e.id === local);
|
|
3564
|
-
if (entry)
|
|
3574
|
+
if (entry) candidates.push({ snapshot, entry });
|
|
3575
|
+
}
|
|
3576
|
+
if (candidates.length === 0) continue;
|
|
3577
|
+
const [first] = candidates;
|
|
3578
|
+
if (candidates.every((c) => sameContract(c.entry, first.entry))) {
|
|
3579
|
+
return { kind: "resolved", ...first };
|
|
3565
3580
|
}
|
|
3581
|
+
return { kind: "ambiguous", providers: [...new Set(candidates.map((c) => c.snapshot.projectName))] };
|
|
3566
3582
|
}
|
|
3567
|
-
return
|
|
3583
|
+
return { kind: "unresolved" };
|
|
3584
|
+
}
|
|
3585
|
+
function reportAmbiguousSurfaceRef(ctx, subject, ref, providers, specId, isDraftContext) {
|
|
3586
|
+
const local = ref.split("::").filter((seg) => seg && seg !== "super").pop() ?? ref;
|
|
3587
|
+
ctx.addIssue(
|
|
3588
|
+
"error",
|
|
3589
|
+
"SURFACE_REF_AMBIGUOUS",
|
|
3590
|
+
`${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.`,
|
|
3591
|
+
specId,
|
|
3592
|
+
isDraftContext
|
|
3593
|
+
);
|
|
3568
3594
|
}
|
|
3569
3595
|
function enclosingMounts(ctx, subsystemId) {
|
|
3570
3596
|
const mounts = [];
|
|
@@ -3655,6 +3681,7 @@ var init_contracts = __esm({
|
|
|
3655
3681
|
{ code: "INVALID_TARGET_COMPONENT_REFERENCE", defaultSeverity: "error", summary: "Call/register step targets a non-existent component" },
|
|
3656
3682
|
{ 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" },
|
|
3657
3683
|
{ code: "SURFACE_REF_NOT_EXPOSED", defaultSeverity: "error", summary: "Cross-tree reference resolves to a surface snapshot that does not expose the called method/capability" },
|
|
3684
|
+
{ code: "SURFACE_REF_AMBIGUOUS", defaultSeverity: "error", summary: "Cross-tree call/dispatch/register target matched by surface snapshots of several providers with different contracts" },
|
|
3658
3685
|
{ code: "UNDECLARED_DEPENDENCY_CALL", defaultSeverity: "error", summary: "Call step targets a component the caller does not depend on or own" },
|
|
3659
3686
|
{ code: "INVALID_TARGET_METHOD_REFERENCE", defaultSeverity: "error", summary: "Call step targets a method not on any target interface" },
|
|
3660
3687
|
{ code: "NARRATIVE_SEMANTIC_UNBACKED", defaultSeverity: "warning", summary: "Narrative asserts a guarantee the called contract does not declare" }
|
|
@@ -3709,7 +3736,18 @@ var init_contracts = __esm({
|
|
|
3709
3736
|
if (!dispatchTarget) {
|
|
3710
3737
|
if (isCrossTreeForm || isCollapsedForm) {
|
|
3711
3738
|
const resolved = resolveSurfaceRef(ctx, step.targetComponent, fromSubsystem);
|
|
3712
|
-
if (resolved) {
|
|
3739
|
+
if (resolved.kind === "ambiguous") {
|
|
3740
|
+
reportAmbiguousSurfaceRef(
|
|
3741
|
+
ctx,
|
|
3742
|
+
`Method "${implMethod.name}" in implementation "${impl.id}" dispatches (step ${step.stepNumber}) through`,
|
|
3743
|
+
step.targetComponent,
|
|
3744
|
+
resolved.providers,
|
|
3745
|
+
impl.id,
|
|
3746
|
+
isDraftCtx
|
|
3747
|
+
);
|
|
3748
|
+
continue;
|
|
3749
|
+
}
|
|
3750
|
+
if (resolved.kind === "resolved") {
|
|
3713
3751
|
if (step.capability && !(resolved.entry.dispatch ?? []).some((b) => b.capability === step.capability)) {
|
|
3714
3752
|
ctx.addIssue(
|
|
3715
3753
|
"error",
|
|
@@ -3769,7 +3807,18 @@ var init_contracts = __esm({
|
|
|
3769
3807
|
if (!targetComp) {
|
|
3770
3808
|
if (isCrossTreeForm || isCollapsedForm) {
|
|
3771
3809
|
const resolved = resolveSurfaceRef(ctx, step.targetComponent, fromSubsystem);
|
|
3772
|
-
if (resolved) {
|
|
3810
|
+
if (resolved.kind === "ambiguous") {
|
|
3811
|
+
reportAmbiguousSurfaceRef(
|
|
3812
|
+
ctx,
|
|
3813
|
+
`Method "${implMethod.name}" in implementation "${impl.id}" ${verb} "${step.targetMethod}" (step ${step.stepNumber}) on`,
|
|
3814
|
+
step.targetComponent,
|
|
3815
|
+
resolved.providers,
|
|
3816
|
+
impl.id,
|
|
3817
|
+
isDraftCtx
|
|
3818
|
+
);
|
|
3819
|
+
continue;
|
|
3820
|
+
}
|
|
3821
|
+
if (resolved.kind === "resolved") {
|
|
3773
3822
|
const surfaceMethod = resolved.entry.methods.find((m) => m.name === step.targetMethod);
|
|
3774
3823
|
if (!surfaceMethod) {
|
|
3775
3824
|
ctx.addIssue(
|
|
@@ -5284,6 +5333,7 @@ var init_stereotype_deps = __esm({
|
|
|
5284
5333
|
codes: [
|
|
5285
5334
|
{ code: "INVALID_DEPENDENCY_REFERENCE", defaultSeverity: "error", summary: "dependsOn names a non-existent component" },
|
|
5286
5335
|
{ code: "CROSS_TREE_REF_UNRESOLVED", defaultSeverity: "warning", summary: "Cross-tree dependsOn (super::/:: form) with no surface snapshot covering it" },
|
|
5336
|
+
{ code: "SURFACE_REF_AMBIGUOUS", defaultSeverity: "error", summary: "Cross-tree dependsOn matched by surface snapshots of several providers with different contracts" },
|
|
5287
5337
|
{ code: "CROSS_SUBSYSTEM_NON_ADAPTER", defaultSeverity: "error", summary: "Non-Adapter component crossing a subsystem boundary" },
|
|
5288
5338
|
{ code: "CROSS_SUBSYSTEM_PRIVATE_ACCESS", defaultSeverity: "error", summary: "Cross-subsystem dependency on an unpublished component" },
|
|
5289
5339
|
{ code: "CROSS_SUBSYSTEM_TARGET_NON_PORTAL", defaultSeverity: "error", summary: "Cross-subsystem hop entering through a non-Portal" },
|
|
@@ -5307,7 +5357,11 @@ var init_stereotype_deps = __esm({
|
|
|
5307
5357
|
const external = isExternalNamespaceRef(ctx, depId);
|
|
5308
5358
|
if (external || isCollapsedCrossTreeRef(ctx, depId, comp.subsystem)) {
|
|
5309
5359
|
const resolved = resolveSurfaceRef(ctx, depId, comp.subsystem);
|
|
5310
|
-
if (resolved) {
|
|
5360
|
+
if (resolved.kind === "ambiguous") {
|
|
5361
|
+
reportAmbiguousSurfaceRef(ctx, `Component "${comp.id}" depends on`, depId, resolved.providers, comp.id, isDraftCtx);
|
|
5362
|
+
continue;
|
|
5363
|
+
}
|
|
5364
|
+
if (resolved.kind === "resolved") {
|
|
5311
5365
|
if (comp.componentType !== "Adapter") {
|
|
5312
5366
|
ctx.addIssue(
|
|
5313
5367
|
"error",
|
|
@@ -5399,12 +5453,12 @@ var init_stereotype_deps = __esm({
|
|
|
5399
5453
|
}
|
|
5400
5454
|
}
|
|
5401
5455
|
if (comp.componentType === "Specialist") {
|
|
5402
|
-
const forbiddenTypes = ["Portal", "Observer", "Orchestrator", "Store", "Supervisor"];
|
|
5456
|
+
const forbiddenTypes = ["Portal", "Observer", "Orchestrator", "Store", "Registry", "Supervisor", "Actor"];
|
|
5403
5457
|
if (forbiddenTypes.includes(depComp.componentType)) {
|
|
5404
5458
|
ctx.addIssue(
|
|
5405
5459
|
"error",
|
|
5406
5460
|
"ARCHITECTURE_VIOLATION_SPECIALIST_DEP",
|
|
5407
|
-
`Architectural violation: Specialist component "${comp.id}" cannot depend on "${depComp.componentType}" component "${depComp.id}". Specialists are
|
|
5461
|
+
`Architectural violation: Specialist component "${comp.id}" cannot depend on "${depComp.componentType}" component "${depComp.id}". Specialists are pure capabilities \u2014 they may use Repository facades, Indexes, and Adapters, but never workflow/runtime blocks (Orchestrators, Supervisors, Actors) and never persistence directly (Stores, Registries \u2014 all storage, even in-memory, is reached through a Repository facade), nor Portals or Observers.` + (depComp.componentType === "Store" ? storeResolutionHint(comp.componentType, depComp.id) : ""),
|
|
5408
5462
|
comp.id,
|
|
5409
5463
|
isDraftCtx || ctx.isComponentDraft(depComp.id)
|
|
5410
5464
|
);
|
|
@@ -8676,7 +8730,7 @@ function buildRuleContext(opts) {
|
|
|
8676
8730
|
for (const i of interfaces) collectAllows(i.id, i.lint);
|
|
8677
8731
|
for (const im of implementations) collectAllows(im.id, im.lint);
|
|
8678
8732
|
for (const t of types) collectAllows(t.id, t.lint);
|
|
8679
|
-
const
|
|
8733
|
+
const knownIssueCodes2 = /* @__PURE__ */ new Set([
|
|
8680
8734
|
...[...SDD_RULES, ...extensions.rules].flatMap((r) => r.codes.map((c) => c.code)),
|
|
8681
8735
|
// Declarative assertions bring their own namespaced codes — lint.allow
|
|
8682
8736
|
// and severity overrides treat them exactly like builtins.
|
|
@@ -8739,7 +8793,7 @@ function buildRuleContext(opts) {
|
|
|
8739
8793
|
mountSurfaceSnapshots: opts.mountSurfaceSnapshots ?? [],
|
|
8740
8794
|
codeModel: opts.codeModel ?? emptyCodeModel(),
|
|
8741
8795
|
lintAllows,
|
|
8742
|
-
knownIssueCodes,
|
|
8796
|
+
knownIssueCodes: knownIssueCodes2,
|
|
8743
8797
|
addIssue
|
|
8744
8798
|
};
|
|
8745
8799
|
}
|
|
@@ -9009,8 +9063,8 @@ function doctrineIdentity(doctrine, gate) {
|
|
|
9009
9063
|
)
|
|
9010
9064
|
};
|
|
9011
9065
|
}
|
|
9012
|
-
function hashGateState(doctrine, gate = {}) {
|
|
9013
|
-
const payload = { tree: loadTree(), doctrine: doctrineIdentity(doctrine, gate) };
|
|
9066
|
+
function hashGateState(doctrine, inputs, gate = {}) {
|
|
9067
|
+
const payload = { tree: loadTree(), doctrine: doctrineIdentity(doctrine, gate), inputs: [...inputs].sort() };
|
|
9014
9068
|
const digest2 = crypto2.createHash("sha256").update(canonicalize(payload)).digest("hex");
|
|
9015
9069
|
return { algorithm: GATE_ALGORITHM, digest: digest2 };
|
|
9016
9070
|
}
|
|
@@ -9041,7 +9095,7 @@ var init_statehash = __esm({
|
|
|
9041
9095
|
init_specs2();
|
|
9042
9096
|
init_rules();
|
|
9043
9097
|
CONTENT_ALGORITHM = "sha256";
|
|
9044
|
-
GATE_ALGORITHM = "sha256+doctrine";
|
|
9098
|
+
GATE_ALGORITHM = "sha256+doctrine+inputs";
|
|
9045
9099
|
}
|
|
9046
9100
|
});
|
|
9047
9101
|
|
|
@@ -14015,6 +14069,9 @@ var MODEL = __MODEL_JSON__;
|
|
|
14015
14069
|
function addRule(rule) {
|
|
14016
14070
|
ruleSet.push(rule);
|
|
14017
14071
|
}
|
|
14072
|
+
function listRules() {
|
|
14073
|
+
return ruleSet;
|
|
14074
|
+
}
|
|
14018
14075
|
function registerBuiltinRules() {
|
|
14019
14076
|
ruleSet = [];
|
|
14020
14077
|
for (const rule of SDD_RULES) addRule(rule);
|
|
@@ -14029,6 +14086,9 @@ function ruleSequence() {
|
|
|
14029
14086
|
function specScopedRules() {
|
|
14030
14087
|
return ruleSequence().filter((r) => r.scope === "spec");
|
|
14031
14088
|
}
|
|
14089
|
+
function knownIssueCodes() {
|
|
14090
|
+
return listRules().flatMap((r) => r.codes);
|
|
14091
|
+
}
|
|
14032
14092
|
var ruleSet;
|
|
14033
14093
|
var init_repository = __esm({
|
|
14034
14094
|
"src/core/rules/repository.ts"() {
|
|
@@ -14680,15 +14740,18 @@ function importSurface(sourcePath, origin) {
|
|
|
14680
14740
|
saveSnapshot(snapshot);
|
|
14681
14741
|
return snapshot;
|
|
14682
14742
|
}
|
|
14683
|
-
function
|
|
14684
|
-
|
|
14685
|
-
if (!parent) return null;
|
|
14686
|
-
const childRoot = getProjectRoot();
|
|
14687
|
-
const projected = runWithProjectRoot(parent.parentRoot, () => {
|
|
14743
|
+
function projectFamilySurfaces(parent) {
|
|
14744
|
+
return runWithProjectRoot(parent.parentRoot, () => {
|
|
14688
14745
|
invalidateSpecCache();
|
|
14689
14746
|
const siblings = loadSubsystemSpecs().filter((s) => !s.id.includes("::") && s.id !== parent.subsystemId);
|
|
14690
14747
|
return [projectChildSurface(), ...siblings.map((s) => projectSubsystemSurface(s.id))];
|
|
14691
14748
|
});
|
|
14749
|
+
}
|
|
14750
|
+
function pinFamilySurfaces() {
|
|
14751
|
+
const parent = resolveChainingParent();
|
|
14752
|
+
if (!parent) return null;
|
|
14753
|
+
const childRoot = getProjectRoot();
|
|
14754
|
+
const projected = projectFamilySurfaces(parent);
|
|
14692
14755
|
const before = new Map(listSnapshots(childRoot).map((s) => [s.projectName, surfaceContentKey(s)]));
|
|
14693
14756
|
const changed = [];
|
|
14694
14757
|
for (const snapshot of projected) {
|
|
@@ -14699,17 +14762,21 @@ function pinFamilySurfaces() {
|
|
|
14699
14762
|
}
|
|
14700
14763
|
return changed;
|
|
14701
14764
|
}
|
|
14702
|
-
function
|
|
14703
|
-
|
|
14765
|
+
function projectedFamilyContent(parent) {
|
|
14766
|
+
try {
|
|
14767
|
+
return new Map(projectFamilySurfaces(parent).map((s) => [s.projectName, surfaceContentKey(s)]));
|
|
14768
|
+
} catch {
|
|
14769
|
+
return null;
|
|
14770
|
+
}
|
|
14704
14771
|
}
|
|
14705
14772
|
function listExternalInterfaces() {
|
|
14706
14773
|
const snapshots = listSnapshots();
|
|
14707
14774
|
const chainingParent = resolveChainingParent();
|
|
14708
|
-
const
|
|
14775
|
+
const projected = chainingParent ? projectedFamilyContent(chainingParent) : null;
|
|
14709
14776
|
return snapshots.map((snapshot) => {
|
|
14710
14777
|
const generated = snapshot.origin === "generated";
|
|
14711
14778
|
const sourceKind = !generated ? "foreign" : snapshot.projectName.includes("::") ? "sibling" : "parent";
|
|
14712
|
-
const freshness = generated &&
|
|
14779
|
+
const freshness = generated && projected ? projected.get(snapshot.projectName) === surfaceContentKey(snapshot) ? "fresh" : "stale" : "unverifiable";
|
|
14713
14780
|
return {
|
|
14714
14781
|
projectName: snapshot.projectName,
|
|
14715
14782
|
origin: snapshot.origin,
|
|
@@ -14848,7 +14915,7 @@ var init_approval = __esm({
|
|
|
14848
14915
|
// src/core/validation.ts
|
|
14849
14916
|
var validation_exports = {};
|
|
14850
14917
|
__export(validation_exports, {
|
|
14851
|
-
listRules: () =>
|
|
14918
|
+
listRules: () => listRules2,
|
|
14852
14919
|
validateAsComplete: () => validateAsComplete,
|
|
14853
14920
|
validateProjectConfig: () => validateProjectConfig,
|
|
14854
14921
|
validateRegistry: () => validateRegistry,
|
|
@@ -14861,6 +14928,32 @@ function projectPackSelections() {
|
|
|
14861
14928
|
return [];
|
|
14862
14929
|
}
|
|
14863
14930
|
}
|
|
14931
|
+
function issueKey(issue2) {
|
|
14932
|
+
return `${issue2.code}|${issue2.specId ?? ""}`;
|
|
14933
|
+
}
|
|
14934
|
+
function worstByKey(issues) {
|
|
14935
|
+
const worst = /* @__PURE__ */ new Map();
|
|
14936
|
+
for (const issue2 of issues) {
|
|
14937
|
+
const key = issueKey(issue2);
|
|
14938
|
+
const seen = worst.get(key);
|
|
14939
|
+
if (!seen || SEVERITY_RANK[issue2.severity] > SEVERITY_RANK[seen]) worst.set(key, issue2.severity);
|
|
14940
|
+
}
|
|
14941
|
+
return worst;
|
|
14942
|
+
}
|
|
14943
|
+
function stricterSeverities(parent, child) {
|
|
14944
|
+
const fromParent = parent?.sddRuleSeverity ?? {};
|
|
14945
|
+
const fromChild = child?.sddRuleSeverity ?? {};
|
|
14946
|
+
const codes = /* @__PURE__ */ new Set([...Object.keys(fromParent), ...Object.keys(fromChild)]);
|
|
14947
|
+
if (codes.size === 0) return parent ?? child;
|
|
14948
|
+
const defaults = new Map(knownIssueCodes().map((rc) => [rc.code, rc.defaultSeverity]));
|
|
14949
|
+
const merged = {};
|
|
14950
|
+
for (const code of codes) {
|
|
14951
|
+
const p = fromParent[code] ?? defaults.get(code);
|
|
14952
|
+
const c = fromChild[code] ?? defaults.get(code);
|
|
14953
|
+
merged[code] = p === void 0 || c === void 0 ? p ?? c : SEVERITY_RANK[p] >= SEVERITY_RANK[c] ? p : c;
|
|
14954
|
+
}
|
|
14955
|
+
return { ...parent ?? child, sddRuleSeverity: merged };
|
|
14956
|
+
}
|
|
14864
14957
|
function issue(severity, code, message, agentId, specId) {
|
|
14865
14958
|
return { severity, code, message, agentId, specId };
|
|
14866
14959
|
}
|
|
@@ -14942,7 +15035,7 @@ function validateProjectConfig(config) {
|
|
|
14942
15035
|
issues
|
|
14943
15036
|
};
|
|
14944
15037
|
}
|
|
14945
|
-
function
|
|
15038
|
+
function listRules2() {
|
|
14946
15039
|
const extensions = loadProjectExtensions();
|
|
14947
15040
|
registerBuiltinRules();
|
|
14948
15041
|
registerPackRules(extensions.rules);
|
|
@@ -15042,21 +15135,22 @@ function validateSddTree(rulesOrOptions, projectType = "backend") {
|
|
|
15042
15135
|
for (const rule of ruleSequence()) {
|
|
15043
15136
|
rule.check(ctx);
|
|
15044
15137
|
}
|
|
15045
|
-
const
|
|
15046
|
-
const chainingParent = crossTree !== "off" && issues.some(
|
|
15047
|
-
const resolution = chainingParent ? resolveThroughParent(getProjectRoot(), treatAllAsComplete) : null;
|
|
15138
|
+
const unresolved = (i) => RESOLUTION_FAILURE_CODES.has(i.code) && !i.surfaceResolved;
|
|
15139
|
+
const chainingParent = crossTree !== "off" && issues.some(unresolved) ? resolveChainingParent() : null;
|
|
15140
|
+
const resolution = chainingParent ? resolveThroughParent(getProjectRoot(), treatAllAsComplete, rules) : null;
|
|
15048
15141
|
if (resolution) {
|
|
15049
|
-
const
|
|
15050
|
-
|
|
15051
|
-
const
|
|
15052
|
-
if (
|
|
15053
|
-
|
|
15054
|
-
|
|
15055
|
-
|
|
15056
|
-
|
|
15057
|
-
|
|
15058
|
-
|
|
15059
|
-
|
|
15142
|
+
const judgedByParent = worstByKey(resolution.issues);
|
|
15143
|
+
const kept = issues.filter((i) => {
|
|
15144
|
+
const judged = judgedByParent.get(issueKey(i));
|
|
15145
|
+
if (judged !== void 0) return SEVERITY_RANK[judged] < SEVERITY_RANK[i.severity];
|
|
15146
|
+
return !unresolved(i);
|
|
15147
|
+
});
|
|
15148
|
+
const keptByChild = worstByKey(kept);
|
|
15149
|
+
const added = resolution.issues.filter((i) => {
|
|
15150
|
+
const own = keptByChild.get(issueKey(i));
|
|
15151
|
+
return own === void 0 || SEVERITY_RANK[own] <= SEVERITY_RANK[i.severity];
|
|
15152
|
+
});
|
|
15153
|
+
const merged = dedupeIssues([...kept, ...added]);
|
|
15060
15154
|
return {
|
|
15061
15155
|
valid: merged.every((i) => i.severity !== "error"),
|
|
15062
15156
|
issues: merged,
|
|
@@ -15073,14 +15167,14 @@ function validateSddTree(rulesOrOptions, projectType = "backend") {
|
|
|
15073
15167
|
});
|
|
15074
15168
|
}
|
|
15075
15169
|
}
|
|
15076
|
-
function resolveThroughParent(boundRoot, treatAllAsComplete) {
|
|
15170
|
+
function resolveThroughParent(boundRoot, treatAllAsComplete, childRules) {
|
|
15077
15171
|
const reach = getRequestParentReach();
|
|
15078
15172
|
if (reach && !reach.parentReach) return null;
|
|
15079
15173
|
const ceiling = reach?.topRoot ? path18.resolve(reach.topRoot) : void 0;
|
|
15080
15174
|
const chain = [];
|
|
15081
15175
|
let top = path18.resolve(boundRoot);
|
|
15082
15176
|
while (top !== ceiling) {
|
|
15083
|
-
const hop = findChainingParent(top);
|
|
15177
|
+
const hop = findChainingParent(top, ceiling);
|
|
15084
15178
|
if (!hop) break;
|
|
15085
15179
|
const next = path18.resolve(hop.parentRoot);
|
|
15086
15180
|
if (ceiling && !isWithinOrEqual(ceiling, next)) break;
|
|
@@ -15091,10 +15185,10 @@ function resolveThroughParent(boundRoot, treatAllAsComplete) {
|
|
|
15091
15185
|
const scope = chain.join("::");
|
|
15092
15186
|
const inner = runWithProjectRoot(top, () => {
|
|
15093
15187
|
invalidateSpecCache();
|
|
15094
|
-
let governing = {};
|
|
15188
|
+
let governing = { rules: stricterSeverities(void 0, childRules) };
|
|
15095
15189
|
try {
|
|
15096
15190
|
const config = loadProjectConfig();
|
|
15097
|
-
governing = { rules: config.rules, projectType: config.projectType };
|
|
15191
|
+
governing = { rules: stricterSeverities(config.rules, childRules), projectType: config.projectType };
|
|
15098
15192
|
} catch {
|
|
15099
15193
|
}
|
|
15100
15194
|
return validateSddTree({
|
|
@@ -15168,7 +15262,7 @@ function settledStatusBearing(loaded) {
|
|
|
15168
15262
|
function validateAsComplete(options) {
|
|
15169
15263
|
return validateSddTree({ ...options ?? {}, treatAllAsComplete: true });
|
|
15170
15264
|
}
|
|
15171
|
-
var path18,
|
|
15265
|
+
var path18, RESOLUTION_FAILURE_CODES, SEVERITY_RANK;
|
|
15172
15266
|
var init_validation = __esm({
|
|
15173
15267
|
"src/core/validation.ts"() {
|
|
15174
15268
|
"use strict";
|
|
@@ -15184,16 +15278,15 @@ var init_validation = __esm({
|
|
|
15184
15278
|
init_fs();
|
|
15185
15279
|
path18 = __toESM(require("path"));
|
|
15186
15280
|
init_approval();
|
|
15187
|
-
|
|
15281
|
+
RESOLUTION_FAILURE_CODES = /* @__PURE__ */ new Set([
|
|
15188
15282
|
"UNDEFINED_TYPE_REFERENCE",
|
|
15189
15283
|
"INVALID_DEPENDENCY_REFERENCE",
|
|
15190
15284
|
"INVALID_TARGET_COMPONENT_REFERENCE",
|
|
15191
15285
|
"INVALID_SUBSYSTEM_REFERENCE",
|
|
15192
|
-
"UNDECLARED_DEPENDENCY_CALL",
|
|
15193
15286
|
"INVALID_TRUSTED_LINK",
|
|
15194
|
-
"CROSS_SUBSYSTEM_NON_ADAPTER",
|
|
15195
15287
|
"CROSS_TREE_REF_UNRESOLVED"
|
|
15196
15288
|
]);
|
|
15289
|
+
SEVERITY_RANK = { off: 0, warning: 1, error: 2 };
|
|
15197
15290
|
}
|
|
15198
15291
|
});
|
|
15199
15292
|
|
|
@@ -15756,6 +15849,7 @@ __export(specs_exports, {
|
|
|
15756
15849
|
getSubprojectPrefix: () => getSubprojectPrefix,
|
|
15757
15850
|
getSubsystemPath: () => getSubsystemPath,
|
|
15758
15851
|
getTypePath: () => getTypePath,
|
|
15852
|
+
inspectChainedRoots: () => inspectChainedRoots,
|
|
15759
15853
|
invalidateSpecCache: () => invalidateSpecCache,
|
|
15760
15854
|
listChainedRoots: () => listChainedRoots,
|
|
15761
15855
|
loadComponentSpec: () => loadComponentSpec,
|
|
@@ -15773,6 +15867,7 @@ __export(specs_exports, {
|
|
|
15773
15867
|
loadTypeSpecs: () => loadTypeSpecs,
|
|
15774
15868
|
normalizeComponentLayout: () => normalizeComponentLayout,
|
|
15775
15869
|
readLockState: () => readLockState,
|
|
15870
|
+
rebaseReference: () => rebaseReference,
|
|
15776
15871
|
resolveChainingParent: () => resolveChainingParent,
|
|
15777
15872
|
resolveSubprojectForNamespace: () => resolveSubprojectForNamespace,
|
|
15778
15873
|
restoreSpecFiles: () => restoreSpecFiles,
|
|
@@ -15828,6 +15923,20 @@ function qualifyId(id, prefix, rootSubsystems) {
|
|
|
15828
15923
|
}
|
|
15829
15924
|
return prefix ? `${prefix}::${id}` : id;
|
|
15830
15925
|
}
|
|
15926
|
+
function qualifyDeclaredId(id, prefix, mountRealization = false) {
|
|
15927
|
+
if (!id || !prefix) return id;
|
|
15928
|
+
if (id.startsWith("::") || id.startsWith("super::")) {
|
|
15929
|
+
return qualifyId(id, prefix, NO_ROOT_SUBSYSTEMS);
|
|
15930
|
+
}
|
|
15931
|
+
if (mountRealization && id === prefix.split("::").pop()) {
|
|
15932
|
+
return prefix;
|
|
15933
|
+
}
|
|
15934
|
+
return `${prefix}::${id}`;
|
|
15935
|
+
}
|
|
15936
|
+
function qualifySubsystemRef(id, prefix, rootSubsystems) {
|
|
15937
|
+
if (prefix && !id.includes("::") && id === prefix.split("::").pop()) return prefix;
|
|
15938
|
+
return qualifyId(id, prefix, rootSubsystems);
|
|
15939
|
+
}
|
|
15831
15940
|
function splitNamespace(qualifiedId2) {
|
|
15832
15941
|
if (!qualifiedId2.includes("::")) {
|
|
15833
15942
|
return { prefix: "", localId: qualifiedId2 };
|
|
@@ -15858,33 +15967,75 @@ function relativizeId(id, prefix) {
|
|
|
15858
15967
|
if (common === idParts.length) common--;
|
|
15859
15968
|
return `${"super::".repeat(prefixParts.length - common)}${idParts.slice(common).join("::")}`;
|
|
15860
15969
|
}
|
|
15970
|
+
function rebaseReference(ref, mount, direction, isMoved = () => false) {
|
|
15971
|
+
if (!ref || ref.startsWith("::")) return ref;
|
|
15972
|
+
const segments = ref.split("::");
|
|
15973
|
+
let hops = 0;
|
|
15974
|
+
while (segments[hops] === "super") hops++;
|
|
15975
|
+
if (hops === segments.length) return ref;
|
|
15976
|
+
const outer = Array.from({ length: hops + 1 }, (_, i) => `<n${i}>`).join("::");
|
|
15977
|
+
const inner = `${outer}::${mount}`;
|
|
15978
|
+
const [from, to] = direction === "into" ? [outer, inner] : [inner, outer];
|
|
15979
|
+
const target = qualifyId(ref, from, NO_ROOT_SUBSYSTEMS);
|
|
15980
|
+
const left = relativizeId(target, from);
|
|
15981
|
+
const travels = !left.startsWith("super::") && (direction === "outOf" || isMoved(left));
|
|
15982
|
+
return relativizeId(travels ? qualifyId(left, to, NO_ROOT_SUBSYSTEMS) : target, to);
|
|
15983
|
+
}
|
|
15861
15984
|
function isWithin(dir, file) {
|
|
15862
15985
|
const d = path19.resolve(dir);
|
|
15863
15986
|
const f = path19.resolve(file);
|
|
15864
15987
|
return f === d || f.startsWith(d + path19.sep);
|
|
15865
15988
|
}
|
|
15989
|
+
function canonicalPath(target) {
|
|
15990
|
+
let existing = path19.resolve(target);
|
|
15991
|
+
const rest = [];
|
|
15992
|
+
for (; ; ) {
|
|
15993
|
+
try {
|
|
15994
|
+
fs13.lstatSync(existing);
|
|
15995
|
+
break;
|
|
15996
|
+
} catch {
|
|
15997
|
+
const up = path19.dirname(existing);
|
|
15998
|
+
if (up === existing) return path19.resolve(target);
|
|
15999
|
+
rest.unshift(path19.basename(existing));
|
|
16000
|
+
existing = up;
|
|
16001
|
+
}
|
|
16002
|
+
}
|
|
16003
|
+
try {
|
|
16004
|
+
return path19.join(fs13.realpathSync.native(existing), ...rest);
|
|
16005
|
+
} catch {
|
|
16006
|
+
return null;
|
|
16007
|
+
}
|
|
16008
|
+
}
|
|
16009
|
+
function chainDirKey(dir) {
|
|
16010
|
+
return canonicalPath(dir) ?? path19.resolve(dir);
|
|
16011
|
+
}
|
|
15866
16012
|
function projectPathEscapesRoot(projectRoot2, projectPath, resolvedChildDir) {
|
|
15867
|
-
|
|
16013
|
+
if (path19.isAbsolute(projectPath) || !isWithin(projectRoot2, resolvedChildDir)) return true;
|
|
16014
|
+
const root = canonicalPath(projectRoot2);
|
|
16015
|
+
const child = canonicalPath(resolvedChildDir);
|
|
16016
|
+
return root === null || child === null || !isWithin(root, child);
|
|
15868
16017
|
}
|
|
15869
16018
|
function assertContainedProjectPath(projectRoot2, projectPath) {
|
|
15870
16019
|
const root = path19.resolve(projectRoot2);
|
|
15871
16020
|
const resolved = path19.resolve(root, projectPath);
|
|
15872
16021
|
if (projectPathEscapesRoot(root, projectPath, resolved)) {
|
|
15873
16022
|
throw new Error(
|
|
15874
|
-
`projectPath "${projectPath}" must resolve within the project root "${root}", but resolves to "${resolved}"; absolute
|
|
16023
|
+
`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.`
|
|
15875
16024
|
);
|
|
15876
16025
|
}
|
|
15877
16026
|
return resolved;
|
|
15878
16027
|
}
|
|
15879
|
-
function findChainingParent(childRoot) {
|
|
16028
|
+
function findChainingParent(childRoot, ceiling) {
|
|
15880
16029
|
let childResolved;
|
|
15881
16030
|
try {
|
|
15882
16031
|
childResolved = path19.resolve(childRoot);
|
|
15883
16032
|
} catch {
|
|
15884
16033
|
return null;
|
|
15885
16034
|
}
|
|
16035
|
+
const bound = ceiling ? path19.resolve(ceiling) : void 0;
|
|
15886
16036
|
let dir = path19.dirname(childResolved);
|
|
15887
16037
|
for (let hops = 0; hops < 32; hops++) {
|
|
16038
|
+
if (bound && !isWithin(bound, dir)) break;
|
|
15888
16039
|
const specsDir = aiPathsAt(dir).specsDir();
|
|
15889
16040
|
if (pathExists(specsDir)) {
|
|
15890
16041
|
for (const file of listFilesRecursive(specsDir, ".yaml")) {
|
|
@@ -15898,7 +16049,8 @@ function findChainingParent(childRoot) {
|
|
|
15898
16049
|
const projectPath = raw.projectPath;
|
|
15899
16050
|
if (typeof projectPath === "string" && projectPath.trim() !== "") {
|
|
15900
16051
|
try {
|
|
15901
|
-
|
|
16052
|
+
const mountDir = path19.resolve(dir, projectPath);
|
|
16053
|
+
if (mountDir === childResolved && !projectPathEscapesRoot(dir, projectPath, mountDir)) {
|
|
15902
16054
|
const id = raw.id;
|
|
15903
16055
|
return { parentRoot: dir, subsystemId: typeof id === "string" ? id : "?" };
|
|
15904
16056
|
}
|
|
@@ -15914,12 +16066,11 @@ function findChainingParent(childRoot) {
|
|
|
15914
16066
|
}
|
|
15915
16067
|
return null;
|
|
15916
16068
|
}
|
|
15917
|
-
function
|
|
16069
|
+
function inspectChainedRoots(rootDir = getProjectRoot()) {
|
|
15918
16070
|
const root = path19.resolve(rootDir);
|
|
15919
|
-
const
|
|
15920
|
-
const
|
|
15921
|
-
const walk = (projectDir, depth) => {
|
|
15922
|
-
if (depth > 32) return;
|
|
16071
|
+
const inspection = { roots: [], skipped: [] };
|
|
16072
|
+
const listed = /* @__PURE__ */ new Set();
|
|
16073
|
+
const walk = (projectDir, prefix, ancestors, depth) => {
|
|
15923
16074
|
const specsDir = aiPathsAt(projectDir).specsDir();
|
|
15924
16075
|
if (!pathExists(specsDir)) return;
|
|
15925
16076
|
for (const file of listFilesRecursive(specsDir, ".yaml")) {
|
|
@@ -15930,24 +16081,37 @@ function listChainedRoots(rootDir = getProjectRoot()) {
|
|
|
15930
16081
|
continue;
|
|
15931
16082
|
}
|
|
15932
16083
|
if (!raw || typeof raw !== "object" || !("parentSystem" in raw)) continue;
|
|
15933
|
-
const projectPath = raw
|
|
16084
|
+
const { id, projectPath } = raw;
|
|
15934
16085
|
if (typeof projectPath !== "string" || projectPath.trim() === "") continue;
|
|
16086
|
+
const localId = typeof id === "string" ? id : "?";
|
|
15935
16087
|
let childDir;
|
|
15936
16088
|
try {
|
|
15937
16089
|
childDir = path19.resolve(projectDir, projectPath);
|
|
15938
16090
|
} catch {
|
|
15939
16091
|
continue;
|
|
15940
16092
|
}
|
|
15941
|
-
|
|
15942
|
-
if (
|
|
15943
|
-
if (
|
|
15944
|
-
|
|
15945
|
-
|
|
15946
|
-
|
|
16093
|
+
let reason;
|
|
16094
|
+
if (projectPathEscapesRoot(projectDir, projectPath, childDir)) reason = "escapes";
|
|
16095
|
+
else if (ancestors.has(chainDirKey(childDir))) reason = "cyclic";
|
|
16096
|
+
else if (!fs13.existsSync(childDir)) reason = "missing";
|
|
16097
|
+
else if (depth >= 32) reason = "too-deep";
|
|
16098
|
+
if (reason) {
|
|
16099
|
+
const mount = prefix ? qualifyDeclaredId(localId, prefix, true) : localId;
|
|
16100
|
+
inspection.skipped.push({ mount, projectPath, reason });
|
|
16101
|
+
continue;
|
|
16102
|
+
}
|
|
16103
|
+
const key = chainDirKey(childDir);
|
|
16104
|
+
if (listed.has(key)) continue;
|
|
16105
|
+
listed.add(key);
|
|
16106
|
+
inspection.roots.push(path19.relative(root, childDir).split(path19.sep).join("/"));
|
|
16107
|
+
walk(childDir, prefix ? `${prefix}::${localId}` : localId, /* @__PURE__ */ new Set([...ancestors, key]), depth + 1);
|
|
15947
16108
|
}
|
|
15948
16109
|
};
|
|
15949
|
-
walk(root, 0);
|
|
15950
|
-
return
|
|
16110
|
+
walk(root, "", /* @__PURE__ */ new Set([chainDirKey(root)]), 0);
|
|
16111
|
+
return inspection;
|
|
16112
|
+
}
|
|
16113
|
+
function listChainedRoots(rootDir = getProjectRoot()) {
|
|
16114
|
+
return inspectChainedRoots(rootDir).roots;
|
|
15951
16115
|
}
|
|
15952
16116
|
function mergeMountRealizations(subs) {
|
|
15953
16117
|
const result = [];
|
|
@@ -16228,7 +16392,26 @@ function buildProjectGraph(level) {
|
|
|
16228
16392
|
return buildGraphModel(level);
|
|
16229
16393
|
}
|
|
16230
16394
|
function resolveChainingParent() {
|
|
16231
|
-
|
|
16395
|
+
const reach = getRequestParentReach();
|
|
16396
|
+
if (reach && !reach.parentReach) return null;
|
|
16397
|
+
return findChainingParent(getProjectRoot(), reach?.topRoot);
|
|
16398
|
+
}
|
|
16399
|
+
function snapshotInputKey(snapshot) {
|
|
16400
|
+
const { stateId, generatedAt, origin, ...content } = snapshot;
|
|
16401
|
+
return canonicalize(content);
|
|
16402
|
+
}
|
|
16403
|
+
function consumedSurfaceInputsAt(rootDir) {
|
|
16404
|
+
const dir = path19.join(rootDir, ".wai", "surfaces");
|
|
16405
|
+
if (!pathExists(dir)) return [];
|
|
16406
|
+
const keys = [];
|
|
16407
|
+
for (const file of fs13.readdirSync(dir)) {
|
|
16408
|
+
if (!file.endsWith(".yaml") && !file.endsWith(".yml")) continue;
|
|
16409
|
+
try {
|
|
16410
|
+
keys.push(snapshotInputKey(SurfaceSnapshotSchema.parse(readYamlFile(path19.join(dir, file)))));
|
|
16411
|
+
} catch {
|
|
16412
|
+
}
|
|
16413
|
+
}
|
|
16414
|
+
return keys;
|
|
16232
16415
|
}
|
|
16233
16416
|
function computeGateStateId() {
|
|
16234
16417
|
let gate = {};
|
|
@@ -16237,7 +16420,10 @@ function computeGateStateId() {
|
|
|
16237
16420
|
gate = { projectType: config.projectType, rules: config.rules };
|
|
16238
16421
|
} catch {
|
|
16239
16422
|
}
|
|
16240
|
-
|
|
16423
|
+
const root = getProjectRoot();
|
|
16424
|
+
const roots = [root, ...listChainedRoots(root).map((rel2) => path19.join(root, rel2))];
|
|
16425
|
+
const inputs = roots.flatMap(consumedSurfaceInputsAt);
|
|
16426
|
+
return hashGateState(loadProjectExtensions(), inputs, gate);
|
|
16241
16427
|
}
|
|
16242
16428
|
function readLockState() {
|
|
16243
16429
|
const record2 = readLockRecord();
|
|
@@ -16293,7 +16479,7 @@ function findLegacySpecFiles() {
|
|
|
16293
16479
|
function updateSpec(kind, id, delta, hooks) {
|
|
16294
16480
|
return current().updateSpec(kind, id, delta, hooks);
|
|
16295
16481
|
}
|
|
16296
|
-
var fs13, path19, SIGNATURE_TTL_MS, SpecWorkspace, workspaces;
|
|
16482
|
+
var fs13, path19, NO_ROOT_SUBSYSTEMS, SIGNATURE_TTL_MS, SpecWorkspace, workspaces;
|
|
16297
16483
|
var init_specs2 = __esm({
|
|
16298
16484
|
"src/core/specs.ts"() {
|
|
16299
16485
|
"use strict";
|
|
@@ -16308,6 +16494,7 @@ var init_specs2 = __esm({
|
|
|
16308
16494
|
init_models();
|
|
16309
16495
|
init_narrative_labels();
|
|
16310
16496
|
init_diagram();
|
|
16497
|
+
NO_ROOT_SUBSYSTEMS = /* @__PURE__ */ new Set();
|
|
16311
16498
|
SIGNATURE_TTL_MS = 2e3;
|
|
16312
16499
|
SpecWorkspace = class {
|
|
16313
16500
|
constructor(rootDir) {
|
|
@@ -16346,7 +16533,7 @@ var init_specs2 = __esm({
|
|
|
16346
16533
|
this.rootSubsystems.clear();
|
|
16347
16534
|
this.cachedRecursive = recursive;
|
|
16348
16535
|
this.scanVisitedSpecDirs = [];
|
|
16349
|
-
const visited = /* @__PURE__ */ new Set([
|
|
16536
|
+
const visited = /* @__PURE__ */ new Set([chainDirKey(this.rootDir)]);
|
|
16350
16537
|
const maxDepth = typeof recursive === "number" ? recursive : recursive ? Infinity : 0;
|
|
16351
16538
|
this.cachedIndex = this.scanSpecsForProject(this.rootDir, "", visited, maxDepth, 0);
|
|
16352
16539
|
this.cachedSpecDirs = this.scanVisitedSpecDirs;
|
|
@@ -16457,7 +16644,7 @@ var init_specs2 = __esm({
|
|
|
16457
16644
|
}
|
|
16458
16645
|
}
|
|
16459
16646
|
index.subsystems = index.subsystems.map((sub) => {
|
|
16460
|
-
const qualifiedSubId = namespacePrefix ?
|
|
16647
|
+
const qualifiedSubId = namespacePrefix ? qualifyDeclaredId(sub.id, namespacePrefix, true) : sub.id;
|
|
16461
16648
|
const componentPrefix = sub.projectPath ? qualifiedSubId : namespacePrefix;
|
|
16462
16649
|
return {
|
|
16463
16650
|
...sub,
|
|
@@ -16476,14 +16663,14 @@ var init_specs2 = __esm({
|
|
|
16476
16663
|
const originalSubsystemPaths = index.paths.subsystem;
|
|
16477
16664
|
index.paths.subsystem = {};
|
|
16478
16665
|
for (const [k, v] of Object.entries(originalSubsystemPaths)) {
|
|
16479
|
-
const qualifiedK = namespacePrefix ?
|
|
16666
|
+
const qualifiedK = namespacePrefix ? qualifyDeclaredId(k, namespacePrefix, true) : k;
|
|
16480
16667
|
index.paths.subsystem[qualifiedK] = v;
|
|
16481
16668
|
}
|
|
16482
16669
|
if (namespacePrefix) {
|
|
16483
16670
|
index.components = index.components.map((comp) => ({
|
|
16484
16671
|
...comp,
|
|
16485
|
-
id:
|
|
16486
|
-
subsystem:
|
|
16672
|
+
id: qualifyDeclaredId(comp.id, namespacePrefix),
|
|
16673
|
+
subsystem: qualifySubsystemRef(comp.subsystem, namespacePrefix, this.rootSubsystems),
|
|
16487
16674
|
owns: comp.owns.map((o) => qualifyId(o, namespacePrefix, this.rootSubsystems)),
|
|
16488
16675
|
dependsOn: comp.dependsOn.map((d) => qualifyId(d, namespacePrefix, this.rootSubsystems)),
|
|
16489
16676
|
dispatch: comp.dispatch?.map((b) => ({
|
|
@@ -16493,12 +16680,12 @@ var init_specs2 = __esm({
|
|
|
16493
16680
|
}));
|
|
16494
16681
|
index.interfaces = index.interfaces.map((intf) => ({
|
|
16495
16682
|
...intf,
|
|
16496
|
-
id:
|
|
16683
|
+
id: qualifyDeclaredId(intf.id, namespacePrefix),
|
|
16497
16684
|
component: qualifyId(intf.component, namespacePrefix, this.rootSubsystems)
|
|
16498
16685
|
}));
|
|
16499
16686
|
index.implementations = index.implementations.map((impl) => ({
|
|
16500
16687
|
...impl,
|
|
16501
|
-
id:
|
|
16688
|
+
id: qualifyDeclaredId(impl.id, namespacePrefix),
|
|
16502
16689
|
contract: qualifyId(impl.contract, namespacePrefix, this.rootSubsystems),
|
|
16503
16690
|
methods: impl.methods.map((m) => ({
|
|
16504
16691
|
...m,
|
|
@@ -16510,13 +16697,13 @@ var init_specs2 = __esm({
|
|
|
16510
16697
|
}));
|
|
16511
16698
|
index.types = index.types.map((t) => ({
|
|
16512
16699
|
...t,
|
|
16513
|
-
id:
|
|
16514
|
-
subsystem: t.subsystem ?
|
|
16700
|
+
id: qualifyDeclaredId(t.id, namespacePrefix),
|
|
16701
|
+
subsystem: t.subsystem ? qualifySubsystemRef(t.subsystem, namespacePrefix, this.rootSubsystems) : void 0,
|
|
16515
16702
|
group: t.group ? qualifyId(t.group, namespacePrefix, this.rootSubsystems) : void 0
|
|
16516
16703
|
}));
|
|
16517
16704
|
index.groups = index.groups.map((g) => ({
|
|
16518
16705
|
...g,
|
|
16519
|
-
id:
|
|
16706
|
+
id: qualifyDeclaredId(g.id, namespacePrefix)
|
|
16520
16707
|
}));
|
|
16521
16708
|
const originalPaths = index.paths;
|
|
16522
16709
|
index.paths = {
|
|
@@ -16528,39 +16715,40 @@ var init_specs2 = __esm({
|
|
|
16528
16715
|
group: {}
|
|
16529
16716
|
};
|
|
16530
16717
|
for (const [k, v] of Object.entries(originalPaths.component)) {
|
|
16531
|
-
index.paths.component[
|
|
16718
|
+
index.paths.component[qualifyDeclaredId(k, namespacePrefix)] = v;
|
|
16532
16719
|
}
|
|
16533
16720
|
for (const [k, v] of Object.entries(originalPaths.interface)) {
|
|
16534
|
-
index.paths.interface[
|
|
16721
|
+
index.paths.interface[qualifyDeclaredId(k, namespacePrefix)] = v;
|
|
16535
16722
|
}
|
|
16536
16723
|
for (const [k, v] of Object.entries(originalPaths.implementation)) {
|
|
16537
|
-
index.paths.implementation[
|
|
16724
|
+
index.paths.implementation[qualifyDeclaredId(k, namespacePrefix)] = v;
|
|
16538
16725
|
}
|
|
16539
16726
|
for (const [k, v] of Object.entries(originalPaths.type)) {
|
|
16540
|
-
index.paths.type[
|
|
16727
|
+
index.paths.type[qualifyDeclaredId(k, namespacePrefix)] = v;
|
|
16541
16728
|
}
|
|
16542
16729
|
for (const [k, v] of Object.entries(originalPaths.group)) {
|
|
16543
|
-
index.paths.group[
|
|
16730
|
+
index.paths.group[qualifyDeclaredId(k, namespacePrefix)] = v;
|
|
16544
16731
|
}
|
|
16545
16732
|
}
|
|
16546
16733
|
if (currentDepth < maxDepth) {
|
|
16547
16734
|
for (const subproj of localSubprojects) {
|
|
16548
16735
|
const childDir = path19.resolve(projectDir, subproj.projectPath);
|
|
16549
|
-
|
|
16736
|
+
const mountId = namespacePrefix ? qualifyDeclaredId(subproj.subsystemId, namespacePrefix, true) : subproj.subsystemId;
|
|
16737
|
+
if (projectPathEscapesRoot(projectDir, subproj.projectPath, childDir)) {
|
|
16550
16738
|
this.loaderIssues.push({
|
|
16551
16739
|
severity: "error",
|
|
16552
16740
|
code: "PROJECTPATH_ESCAPE",
|
|
16553
|
-
message: `Subproject path "${subproj.projectPath}" declared by subsystem "${
|
|
16554
|
-
specId:
|
|
16741
|
+
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.`,
|
|
16742
|
+
specId: mountId
|
|
16555
16743
|
});
|
|
16556
16744
|
continue;
|
|
16557
16745
|
}
|
|
16558
|
-
if (visitedDirs.has(childDir)) {
|
|
16746
|
+
if (visitedDirs.has(chainDirKey(childDir))) {
|
|
16559
16747
|
this.loaderIssues.push({
|
|
16560
16748
|
severity: "error",
|
|
16561
16749
|
code: "CIRCULAR_SUBPROJECT_REFERENCE",
|
|
16562
|
-
message: `Circular reference detected: Subsystem "${
|
|
16563
|
-
specId:
|
|
16750
|
+
message: `Circular reference detected: Subsystem "${mountId}" refers to subproject "${childDir}" which is already loaded.`,
|
|
16751
|
+
specId: mountId
|
|
16564
16752
|
});
|
|
16565
16753
|
continue;
|
|
16566
16754
|
}
|
|
@@ -16568,14 +16756,14 @@ var init_specs2 = __esm({
|
|
|
16568
16756
|
this.loaderIssues.push({
|
|
16569
16757
|
severity: "error",
|
|
16570
16758
|
code: "SUBPROJECT_NOT_FOUND",
|
|
16571
|
-
message: `Subproject directory "${childDir}" declared by subsystem "${
|
|
16572
|
-
specId:
|
|
16759
|
+
message: `Subproject directory "${childDir}" declared by subsystem "${mountId}" does not exist.`,
|
|
16760
|
+
specId: mountId
|
|
16573
16761
|
});
|
|
16574
16762
|
continue;
|
|
16575
16763
|
}
|
|
16576
16764
|
const childNamespace = namespacePrefix ? `${namespacePrefix}::${subproj.subsystemId}` : subproj.subsystemId;
|
|
16577
16765
|
const newVisited = new Set(visitedDirs);
|
|
16578
|
-
newVisited.add(childDir);
|
|
16766
|
+
newVisited.add(chainDirKey(childDir));
|
|
16579
16767
|
const childIndex = this.scanSpecsForProject(childDir, childNamespace, newVisited, maxDepth, currentDepth + 1);
|
|
16580
16768
|
index.subsystems.push(...childIndex.subsystems);
|
|
16581
16769
|
index.components.push(...childIndex.components);
|
|
@@ -16608,11 +16796,11 @@ var init_specs2 = __esm({
|
|
|
16608
16796
|
const sub = index.subsystems.find((s) => s.id === currentPrefix);
|
|
16609
16797
|
if (sub && sub.projectPath) {
|
|
16610
16798
|
const nextDir = path19.resolve(currentDir, sub.projectPath);
|
|
16611
|
-
if (projectPathEscapesRoot(
|
|
16799
|
+
if (projectPathEscapesRoot(currentDir, sub.projectPath, nextDir)) {
|
|
16612
16800
|
this.loaderIssues.push({
|
|
16613
16801
|
severity: "error",
|
|
16614
16802
|
code: "PROJECTPATH_ESCAPE",
|
|
16615
|
-
message: `Subproject path "${sub.projectPath}" declared by subsystem "${currentPrefix}" escapes the project
|
|
16803
|
+
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.`,
|
|
16616
16804
|
specId: currentPrefix
|
|
16617
16805
|
});
|
|
16618
16806
|
continue;
|
|
@@ -18256,6 +18444,7 @@ function externalizeSubsystem(subsystemId, projectPath) {
|
|
|
18256
18444
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
18257
18445
|
});
|
|
18258
18446
|
rewriteRefsInDir(parentSpecsDir, renameMap, fooDir);
|
|
18447
|
+
rebaseMovedRefs(childFooDir, subsystemId, "into");
|
|
18259
18448
|
invalidateSpecCache();
|
|
18260
18449
|
}
|
|
18261
18450
|
function internalizeSubsystem(subsystemId) {
|
|
@@ -18295,6 +18484,7 @@ function internalizeSubsystem(subsystemId) {
|
|
|
18295
18484
|
});
|
|
18296
18485
|
fs14.rmSync(childWai, { recursive: true, force: true });
|
|
18297
18486
|
rewriteRefsInDir(parentSpecsDir, renameMap, fooDir);
|
|
18487
|
+
rebaseMovedRefs(fooDir, subsystemId, "outOf");
|
|
18298
18488
|
invalidateSpecCache();
|
|
18299
18489
|
}
|
|
18300
18490
|
function buildRenameMap(subsystemId, externalize) {
|
|
@@ -18321,7 +18511,27 @@ function buildRenameMap(subsystemId, externalize) {
|
|
|
18321
18511
|
}
|
|
18322
18512
|
function rewriteRefsInDir(specsDir, renameMap, excludeDir) {
|
|
18323
18513
|
if (renameMap.size === 0) return;
|
|
18324
|
-
|
|
18514
|
+
rewriteRefFields(specsDir, (ref) => renameMap.get(ref) ?? ref, excludeDir);
|
|
18515
|
+
}
|
|
18516
|
+
function rebaseMovedRefs(movedDir, mount, direction) {
|
|
18517
|
+
const declared = componentIdsUnder(movedDir);
|
|
18518
|
+
rewriteRefFields(movedDir, (ref, position) => position === "component" ? rebaseReference(ref, mount, direction, (id) => declared.has(id)) : ref);
|
|
18519
|
+
}
|
|
18520
|
+
function componentIdsUnder(dir) {
|
|
18521
|
+
const ids = /* @__PURE__ */ new Set();
|
|
18522
|
+
for (const file of listFilesRecursive(dir, ".yaml")) {
|
|
18523
|
+
let raw;
|
|
18524
|
+
try {
|
|
18525
|
+
raw = readYamlFile(file);
|
|
18526
|
+
} catch {
|
|
18527
|
+
continue;
|
|
18528
|
+
}
|
|
18529
|
+
if (raw && typeof raw === "object" && "componentType" in raw && typeof raw.id === "string") ids.add(raw.id);
|
|
18530
|
+
}
|
|
18531
|
+
return ids;
|
|
18532
|
+
}
|
|
18533
|
+
function rewriteRefFields(specsDir, remap, excludeDir) {
|
|
18534
|
+
const at = (ref, position) => typeof ref === "string" ? remap(ref, position) : ref;
|
|
18325
18535
|
for (const file of listFilesRecursive(specsDir, ".yaml")) {
|
|
18326
18536
|
if (excludeDir && isWithinDir(excludeDir, file)) continue;
|
|
18327
18537
|
let raw;
|
|
@@ -18333,14 +18543,14 @@ function rewriteRefsInDir(specsDir, renameMap, excludeDir) {
|
|
|
18333
18543
|
if (!raw || typeof raw !== "object") continue;
|
|
18334
18544
|
let changed = false;
|
|
18335
18545
|
if ("componentType" in raw && Array.isArray(raw.dependsOn)) {
|
|
18336
|
-
const next = raw.dependsOn.map((d) =>
|
|
18546
|
+
const next = raw.dependsOn.map((d) => at(d, "component"));
|
|
18337
18547
|
if (next.some((v, i) => v !== raw.dependsOn[i])) {
|
|
18338
18548
|
raw.dependsOn = next;
|
|
18339
18549
|
changed = true;
|
|
18340
18550
|
}
|
|
18341
18551
|
if (Array.isArray(raw.dispatch)) {
|
|
18342
18552
|
for (const b of raw.dispatch) {
|
|
18343
|
-
const nc =
|
|
18553
|
+
const nc = at(b.component, "component");
|
|
18344
18554
|
if (nc !== b.component) {
|
|
18345
18555
|
b.component = nc;
|
|
18346
18556
|
changed = true;
|
|
@@ -18349,7 +18559,7 @@ function rewriteRefsInDir(specsDir, renameMap, excludeDir) {
|
|
|
18349
18559
|
}
|
|
18350
18560
|
} else if ("parentSystem" in raw && Array.isArray(raw.lifecycle)) {
|
|
18351
18561
|
for (const le of raw.lifecycle) {
|
|
18352
|
-
const nc =
|
|
18562
|
+
const nc = at(le.component, "component");
|
|
18353
18563
|
if (nc !== le.component) {
|
|
18354
18564
|
le.component = nc;
|
|
18355
18565
|
changed = true;
|
|
@@ -18359,7 +18569,7 @@ function rewriteRefsInDir(specsDir, renameMap, excludeDir) {
|
|
|
18359
18569
|
for (const m of raw.methods) {
|
|
18360
18570
|
if (!Array.isArray(m.params)) continue;
|
|
18361
18571
|
for (const p of m.params) {
|
|
18362
|
-
const nt =
|
|
18572
|
+
const nt = at(p.type, "type");
|
|
18363
18573
|
if (nt !== p.type) {
|
|
18364
18574
|
p.type = nt;
|
|
18365
18575
|
changed = true;
|
|
@@ -18370,7 +18580,7 @@ function rewriteRefsInDir(specsDir, renameMap, excludeDir) {
|
|
|
18370
18580
|
for (const m of raw.methods) {
|
|
18371
18581
|
if (!Array.isArray(m.narrative)) continue;
|
|
18372
18582
|
for (const step of m.narrative) {
|
|
18373
|
-
const nt =
|
|
18583
|
+
const nt = at(step.targetComponent, "component");
|
|
18374
18584
|
if (nt !== step.targetComponent) {
|
|
18375
18585
|
step.targetComponent = nt;
|
|
18376
18586
|
changed = true;
|
|
@@ -18379,7 +18589,7 @@ function rewriteRefsInDir(specsDir, renameMap, excludeDir) {
|
|
|
18379
18589
|
}
|
|
18380
18590
|
} else if ("kind" in raw && Array.isArray(raw.fields)) {
|
|
18381
18591
|
for (const f of raw.fields) {
|
|
18382
|
-
const nt =
|
|
18592
|
+
const nt = at(f.type, "type");
|
|
18383
18593
|
if (nt !== f.type) {
|
|
18384
18594
|
f.type = nt;
|
|
18385
18595
|
changed = true;
|
|
@@ -22768,8 +22978,34 @@ __export(server_exports, {
|
|
|
22768
22978
|
captureBuildStamp: () => captureBuildStamp,
|
|
22769
22979
|
createMcpServer: () => createMcpServer,
|
|
22770
22980
|
isBuildStale: () => isBuildStale,
|
|
22771
|
-
startMcpServer: () => startMcpServer
|
|
22981
|
+
startMcpServer: () => startMcpServer,
|
|
22982
|
+
statusFamilyContext: () => statusFamilyContext
|
|
22772
22983
|
});
|
|
22984
|
+
function statusFamilyContext() {
|
|
22985
|
+
const lines = [];
|
|
22986
|
+
try {
|
|
22987
|
+
const parent = resolveChainingParent();
|
|
22988
|
+
if (parent) {
|
|
22989
|
+
let parentName;
|
|
22990
|
+
try {
|
|
22991
|
+
const system = readYamlFile(aiPathsAt(parent.parentRoot).specsSystem());
|
|
22992
|
+
if (typeof system?.name === "string") parentName = system.name;
|
|
22993
|
+
} catch {
|
|
22994
|
+
}
|
|
22995
|
+
lines.push(
|
|
22996
|
+
`Family: this project is a chained subproject, mounted as subsystem "${parent.subsystemId}" of ${parentName ? `the parent project "${parentName}"` : "its parent project"} \u2014 the surfaces it can consume are listed by sdd_list_external_interfaces.`
|
|
22997
|
+
);
|
|
22998
|
+
}
|
|
22999
|
+
const mounts = loadSubsystemSpecs().filter((s) => s.projectPath && !s.id.includes("::"));
|
|
23000
|
+
if (mounts.length > 0) {
|
|
23001
|
+
lines.push(`Family: chained subprojects mounted here \u2014 ${mounts.map((s) => `${s.id} (${s.projectPath})`).join(", ")}.`);
|
|
23002
|
+
}
|
|
23003
|
+
} catch {
|
|
23004
|
+
}
|
|
23005
|
+
return lines.length > 0 ? `${lines.join("\n")}
|
|
23006
|
+
|
|
23007
|
+
` : "";
|
|
23008
|
+
}
|
|
22773
23009
|
function requireLoader() {
|
|
22774
23010
|
return loader_exports;
|
|
22775
23011
|
}
|
|
@@ -23981,10 +24217,11 @@ NOTICE:
|
|
|
23981
24217
|
},
|
|
23982
24218
|
({ subsystem, recursive }) => {
|
|
23983
24219
|
try {
|
|
23984
|
-
|
|
24220
|
+
const report2 = getStatusReport({
|
|
23985
24221
|
subsystem,
|
|
23986
24222
|
recursive: recursive ?? true
|
|
23987
|
-
})
|
|
24223
|
+
});
|
|
24224
|
+
return text(`${statusFamilyContext()}${report2}`);
|
|
23988
24225
|
} catch (e) {
|
|
23989
24226
|
return errText(String(e));
|
|
23990
24227
|
}
|
|
@@ -24112,8 +24349,10 @@ NOTICE:
|
|
|
24112
24349
|
}
|
|
24113
24350
|
}, hostedStub);
|
|
24114
24351
|
reg(server, "sdd_host_export_tree", {
|
|
24115
|
-
description: "Hosted spec-tree transfer: pack the BOUND project's WHOLE spec tree \u2014 its own .wai plus every chained subproject \u2014 into a .waitree archive, returned as base64 with its roots, file count and state id. The migration counterpart of an import: use it to take a hosted project local, or to move it to another instance. Requires project:read over the project. Bounded by the data-plane body cap; a very large tree exports through the web download route instead.",
|
|
24116
|
-
inputSchema: {
|
|
24352
|
+
description: "Hosted spec-tree transfer: pack the BOUND project's WHOLE spec tree \u2014 its own .wai plus every chained subproject \u2014 into a .waitree archive, returned as base64 with its roots, file count and state id. The migration counterpart of an import: use it to take a hosted project local, or to move it to another instance. Requires project:read over the project. Bounded by the data-plane body cap; a very large tree exports through the web download route instead. Refuses when a chained mount cannot be packed (escaping, missing, cyclic, too deep, or holding no .wai), naming every one and why, unless allowPartial is set \u2014 then the archive is built anyway and the result lists what was skipped.",
|
|
24353
|
+
inputSchema: {
|
|
24354
|
+
allowPartial: import_zod10.z.boolean().optional().describe("Build the archive even when some chained mounts cannot be packed, listing them as skipped; default false (refuse and name them)")
|
|
24355
|
+
}
|
|
24117
24356
|
}, hostedStub);
|
|
24118
24357
|
reg(server, "sdd_host_import_tree", {
|
|
24119
24358
|
description: "Hosted spec-tree transfer: REPLACE the BOUND project's spec tree from a base64 .waitree archive. Requires project:admin over the project (strictly above project:write \u2014 this replaces the whole design, not one spec). Refuses an occupied destination unless replaceExisting is set, always refuses executable entries (rule/code packs install only through the trusted filesystem), and moves the previous tree aside to a backup whose path is returned.",
|
|
@@ -24176,6 +24415,7 @@ var init_server = __esm({
|
|
|
24176
24415
|
path30 = __toESM(require("path"));
|
|
24177
24416
|
import_url = require("url");
|
|
24178
24417
|
init_fs();
|
|
24418
|
+
init_yaml();
|
|
24179
24419
|
init_status();
|
|
24180
24420
|
init_defaults();
|
|
24181
24421
|
init_narrative_labels();
|
|
@@ -25305,18 +25545,29 @@ function discardStagingDir(stagingDir) {
|
|
|
25305
25545
|
} catch {
|
|
25306
25546
|
}
|
|
25307
25547
|
}
|
|
25308
|
-
function exportSpecTree(includeDerived) {
|
|
25548
|
+
function exportSpecTree(includeDerived, allowPartial) {
|
|
25309
25549
|
const system = loadSystemSpec();
|
|
25310
25550
|
if (!system) {
|
|
25311
25551
|
throw new Error("no spec tree to export at this project root");
|
|
25312
25552
|
}
|
|
25313
|
-
const
|
|
25553
|
+
const inspection = inspectChainedRoots();
|
|
25314
25554
|
const root = getProjectRoot();
|
|
25315
|
-
const
|
|
25316
|
-
|
|
25317
|
-
|
|
25318
|
-
|
|
25319
|
-
|
|
25555
|
+
const skipped = [...inspection.skipped];
|
|
25556
|
+
const roots = [{ relativePath: ".", waiDir: aiPathsAt(root).root() }];
|
|
25557
|
+
for (const rel2 of inspection.roots) {
|
|
25558
|
+
const waiDir = aiPathsAt(path28.resolve(root, rel2)).root();
|
|
25559
|
+
if (pathExists(waiDir)) {
|
|
25560
|
+
roots.push({ relativePath: rel2, waiDir });
|
|
25561
|
+
} else {
|
|
25562
|
+
skipped.push({ mount: rel2, projectPath: rel2, reason: "no-spec-tree" });
|
|
25563
|
+
}
|
|
25564
|
+
}
|
|
25565
|
+
if (skipped.length > 0 && !allowPartial) {
|
|
25566
|
+
const detail = skipped.map((s) => `${s.mount} (${s.reason})`).join(", ");
|
|
25567
|
+
throw new Error(
|
|
25568
|
+
`cannot export the whole spec tree \u2014 skipped: ${detail}. Pass allowPartial to export the rest anyway.`
|
|
25569
|
+
);
|
|
25570
|
+
}
|
|
25320
25571
|
const stateId = computeStateId();
|
|
25321
25572
|
const built = buildTreeArchive2(roots, system.name, stateId.digest, includeDerived);
|
|
25322
25573
|
const result = {
|
|
@@ -25325,7 +25576,8 @@ function exportSpecTree(includeDerived) {
|
|
|
25325
25576
|
projectName: built.manifest.projectName,
|
|
25326
25577
|
roots: built.manifest.roots,
|
|
25327
25578
|
fileCount: built.fileCount,
|
|
25328
|
-
stateId: stateId.digest
|
|
25579
|
+
stateId: stateId.digest,
|
|
25580
|
+
skipped
|
|
25329
25581
|
};
|
|
25330
25582
|
return result;
|
|
25331
25583
|
}
|
|
@@ -26254,6 +26506,7 @@ init_loader();
|
|
|
26254
26506
|
init_validation();
|
|
26255
26507
|
function isCiDraftWaivable(issue2) {
|
|
26256
26508
|
if (issue2.severity !== "warning") return false;
|
|
26509
|
+
if (issue2.code === "DRAFT_SUBSYSTEM_WARNING") return true;
|
|
26257
26510
|
if (issue2.code === "DRAFT_COMPONENT_WARNING") return true;
|
|
26258
26511
|
if (issue2.code === "UNUSED_COMPONENT") return issue2.draftContext === true;
|
|
26259
26512
|
return false;
|
|
@@ -27727,7 +27980,7 @@ init_logger();
|
|
|
27727
27980
|
init_validation();
|
|
27728
27981
|
init_extensions();
|
|
27729
27982
|
init_loader();
|
|
27730
|
-
async function
|
|
27983
|
+
async function listRules3() {
|
|
27731
27984
|
let overrides = {};
|
|
27732
27985
|
if (isProjectInitialized()) {
|
|
27733
27986
|
try {
|
|
@@ -27742,7 +27995,7 @@ async function listRules2() {
|
|
|
27742
27995
|
};
|
|
27743
27996
|
const ext = loadProjectExtensions();
|
|
27744
27997
|
const packRuleNames = new Set(ext.rules.map((r) => r.name));
|
|
27745
|
-
const active =
|
|
27998
|
+
const active = listRules2();
|
|
27746
27999
|
const builtin = active.filter((r) => !packRuleNames.has(r.name));
|
|
27747
28000
|
const packRules = active.filter((r) => packRuleNames.has(r.name));
|
|
27748
28001
|
const groups = [
|
|
@@ -30104,7 +30357,19 @@ function excludeLocalFiles() {
|
|
|
30104
30357
|
const excludePath = path48.join(getProjectRoot(), ".git", "info", "exclude");
|
|
30105
30358
|
try {
|
|
30106
30359
|
fs36.mkdirSync(path48.dirname(excludePath), { recursive: true });
|
|
30107
|
-
fs36.
|
|
30360
|
+
const existing = fs36.existsSync(excludePath) ? fs36.readFileSync(excludePath, "utf8") : "";
|
|
30361
|
+
const lines = existing.split(/\r?\n/).filter((line2) => line2.length > 0);
|
|
30362
|
+
const hadLegacyLockExclusion = lines.some((line2) => line2.trim() === ".wai/lock.json");
|
|
30363
|
+
const kept = lines.filter((line2) => line2.trim() !== ".wai/lock.json");
|
|
30364
|
+
if (!kept.some((line2) => line2.trim() === ".wai/git.json")) {
|
|
30365
|
+
kept.push(".wai/git.json");
|
|
30366
|
+
}
|
|
30367
|
+
fs36.writeFileSync(excludePath, kept.join("\n") + "\n");
|
|
30368
|
+
if (hadLegacyLockExclusion) {
|
|
30369
|
+
console.warn(
|
|
30370
|
+
"[wairon git] removed a .wai/lock.json exclusion left by an earlier version \u2014 the approval it records is committed with the rest of .wai/ now, so it reaches the remote."
|
|
30371
|
+
);
|
|
30372
|
+
}
|
|
30108
30373
|
} catch {
|
|
30109
30374
|
}
|
|
30110
30375
|
}
|
|
@@ -31076,13 +31341,13 @@ function listSecrets(_cfg, credential) {
|
|
|
31076
31341
|
requireAdmin(credential);
|
|
31077
31342
|
return listSecretKeys();
|
|
31078
31343
|
}
|
|
31079
|
-
function exportProjectTree(cfg, credential, project2, subproject, includeDerived) {
|
|
31344
|
+
function exportProjectTree(cfg, credential, project2, subproject, includeDerived, allowPartial) {
|
|
31080
31345
|
const principal = requirePrincipal(cfg, credential);
|
|
31081
31346
|
if (authorize(cfg.dataDir, principal, "project:read", "project", project2).value !== "yes") {
|
|
31082
31347
|
throw new AdminAuthError("Forbidden \u2014 exporting a project's spec tree requires project:read over it");
|
|
31083
31348
|
}
|
|
31084
31349
|
const root = boundLifecycleRoot(cfg, project2, subproject);
|
|
31085
|
-
return runWithProjectRoot(root, () => hostCore.exportSpecTree(includeDerived));
|
|
31350
|
+
return runWithProjectRoot(root, () => hostCore.exportSpecTree(includeDerived, allowPartial));
|
|
31086
31351
|
}
|
|
31087
31352
|
function importProjectTree(cfg, credential, project2, archive, subproject, replaceExisting) {
|
|
31088
31353
|
const principal = requirePrincipal(cfg, credential);
|
|
@@ -35787,8 +36052,8 @@ function runPeriodicBackingSync(cfg) {
|
|
|
35787
36052
|
}
|
|
35788
36053
|
|
|
35789
36054
|
// src/server/projectops.ts
|
|
35790
|
-
function exportProjectTree2(cfg, credential, project2, subproject, includeDerived) {
|
|
35791
|
-
return exportProjectTree(cfg, credential, project2, subproject, includeDerived);
|
|
36055
|
+
function exportProjectTree2(cfg, credential, project2, subproject, includeDerived, allowPartial) {
|
|
36056
|
+
return exportProjectTree(cfg, credential, project2, subproject, includeDerived, allowPartial);
|
|
35792
36057
|
}
|
|
35793
36058
|
function importProjectTree2(cfg, credential, project2, archive, subproject, replaceExisting) {
|
|
35794
36059
|
return importProjectTree(cfg, credential, project2, archive, subproject, replaceExisting);
|
|
@@ -39178,7 +39443,8 @@ function opsExportProjectTree(cfg, sessionId, url, res) {
|
|
|
39178
39443
|
sessionId,
|
|
39179
39444
|
q(url, "projectId") ?? "",
|
|
39180
39445
|
void 0,
|
|
39181
|
-
q(url, "includeDerived") === "1"
|
|
39446
|
+
q(url, "includeDerived") === "1",
|
|
39447
|
+
q(url, "allowPartial") === "1"
|
|
39182
39448
|
);
|
|
39183
39449
|
res.writeHead(200, {
|
|
39184
39450
|
"content-type": "application/zip",
|
|
@@ -39902,7 +40168,8 @@ var PROJECT_RECORD_TOOLS = /* @__PURE__ */ new Set([
|
|
|
39902
40168
|
"sdd_host_initialize_project",
|
|
39903
40169
|
"sdd_host_get_approval_status",
|
|
39904
40170
|
"sdd_host_await_approval",
|
|
39905
|
-
...PROJECT_OPS_TOOLS
|
|
40171
|
+
...PROJECT_OPS_TOOLS,
|
|
40172
|
+
...LANDSCAPE_DISCOVERY_TOOLS
|
|
39906
40173
|
]);
|
|
39907
40174
|
function jsonRpcRequests(body) {
|
|
39908
40175
|
const arr = Array.isArray(body) ? body : [body];
|
|
@@ -39930,11 +40197,31 @@ var WRITE_TOOL_PREFIXES = [
|
|
|
39930
40197
|
"sdd_move_"
|
|
39931
40198
|
];
|
|
39932
40199
|
var READ_TOOL_PREFIXES = ["sdd_get_", "sdd_validate_"];
|
|
40200
|
+
var READ_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
40201
|
+
"sdd_list_external_interfaces",
|
|
40202
|
+
"listAgents",
|
|
40203
|
+
"getAgent",
|
|
40204
|
+
"listDomains",
|
|
40205
|
+
"validateTopology",
|
|
40206
|
+
"getProjectConfig"
|
|
40207
|
+
]);
|
|
39933
40208
|
function requiredDataPlaneCapability(toolName) {
|
|
39934
|
-
if (READ_TOOL_PREFIXES.some((p) => toolName.startsWith(p))) return "project:read";
|
|
40209
|
+
if (READ_TOOL_NAMES.has(toolName) || READ_TOOL_PREFIXES.some((p) => toolName.startsWith(p))) return "project:read";
|
|
39935
40210
|
if (WRITE_TOOL_PREFIXES.some((p) => toolName.startsWith(p))) return "project:write";
|
|
39936
40211
|
return "project:write";
|
|
39937
40212
|
}
|
|
40213
|
+
var TREE_SCOPED_HOST_TOOLS = /* @__PURE__ */ new Set([
|
|
40214
|
+
"sdd_host_lock_project",
|
|
40215
|
+
...TREE_TRANSFER_TOOLS
|
|
40216
|
+
]);
|
|
40217
|
+
function toolScope(toolName) {
|
|
40218
|
+
if (PROJECT_RECORD_TOOLS.has(toolName)) return "record";
|
|
40219
|
+
if (TREE_SCOPED_HOST_TOOLS.has(toolName)) return "tree";
|
|
40220
|
+
if (READ_TOOL_NAMES.has(toolName) || READ_TOOL_PREFIXES.some((p) => toolName.startsWith(p)) || WRITE_TOOL_PREFIXES.some((p) => toolName.startsWith(p))) {
|
|
40221
|
+
return "tree";
|
|
40222
|
+
}
|
|
40223
|
+
return void 0;
|
|
40224
|
+
}
|
|
39938
40225
|
var MUTATING_HOST_TOOLS = /* @__PURE__ */ new Set([
|
|
39939
40226
|
"sdd_host_initialize_project",
|
|
39940
40227
|
"sdd_host_lock_project",
|
|
@@ -39975,7 +40262,11 @@ function subprojectConfinementError(projectId, subproject, body) {
|
|
|
39975
40262
|
const msg = jsonRpcRequest(body);
|
|
39976
40263
|
if (!msg || msg.method !== "tools/call") return void 0;
|
|
39977
40264
|
const name = msg.params?.name;
|
|
39978
|
-
if (typeof name !== "string"
|
|
40265
|
+
if (typeof name !== "string") return void 0;
|
|
40266
|
+
const scope = toolScope(name);
|
|
40267
|
+
if (scope === "tree") return void 0;
|
|
40268
|
+
const bound = `${projectId}${SUBPROJECT_SEPARATOR}${subproject}`;
|
|
40269
|
+
const why = scope === "record" ? `${name} acts on the whole project "${projectId}", but this credential is bound to subproject "${bound}". ` : `${name} declares no scope, and this credential is bound to subproject "${bound}", which serves only tools that act on the bound tree. `;
|
|
39979
40270
|
return {
|
|
39980
40271
|
jsonrpc: "2.0",
|
|
39981
40272
|
id: msg.id ?? null,
|
|
@@ -39983,7 +40274,7 @@ function subprojectConfinementError(projectId, subproject, body) {
|
|
|
39983
40274
|
content: [
|
|
39984
40275
|
{
|
|
39985
40276
|
type: "text",
|
|
39986
|
-
text: `Refused \u2014 ${
|
|
40277
|
+
text: `Refused \u2014 ${why}An unqualified credential for "${projectId}" is required to call ${name}.`
|
|
39987
40278
|
}
|
|
39988
40279
|
],
|
|
39989
40280
|
isError: true
|
|
@@ -40068,14 +40359,22 @@ async function dispatchProjectLifecycleTool(cfg, credential, projectId, body, su
|
|
|
40068
40359
|
);
|
|
40069
40360
|
break;
|
|
40070
40361
|
case "sdd_host_export_tree": {
|
|
40071
|
-
const exported = exportProjectTree2(
|
|
40362
|
+
const exported = exportProjectTree2(
|
|
40363
|
+
cfg,
|
|
40364
|
+
credential,
|
|
40365
|
+
projectId,
|
|
40366
|
+
subproject,
|
|
40367
|
+
void 0,
|
|
40368
|
+
args.allowPartial === true
|
|
40369
|
+
);
|
|
40072
40370
|
value = {
|
|
40073
40371
|
projectName: exported.projectName,
|
|
40074
40372
|
roots: exported.roots,
|
|
40075
40373
|
fileCount: exported.fileCount,
|
|
40076
40374
|
stateId: exported.stateId,
|
|
40077
40375
|
suggestedFileName: exported.suggestedFileName,
|
|
40078
|
-
archiveBase64: Buffer.from(exported.archive).toString("base64")
|
|
40376
|
+
archiveBase64: Buffer.from(exported.archive).toString("base64"),
|
|
40377
|
+
skipped: exported.skipped
|
|
40079
40378
|
};
|
|
40080
40379
|
break;
|
|
40081
40380
|
}
|
|
@@ -40663,7 +40962,8 @@ async function routeAdmin(cfg, req, res) {
|
|
|
40663
40962
|
return sendJson(res, 200, { ok: true });
|
|
40664
40963
|
}
|
|
40665
40964
|
if (req.method === "GET" && parts.length === 4 && parts[3] === "tree") {
|
|
40666
|
-
const
|
|
40965
|
+
const allowPartial = url.searchParams.get("allowPartial") === "1";
|
|
40966
|
+
const exported = exportProjectTree(cfg, cred, parts[2], void 0, void 0, allowPartial);
|
|
40667
40967
|
res.writeHead(200, {
|
|
40668
40968
|
"content-type": "application/zip",
|
|
40669
40969
|
"content-disposition": `attachment; filename="${exported.suggestedFileName}"`
|
|
@@ -42115,8 +42415,8 @@ init_provision();
|
|
|
42115
42415
|
function composeAgentBrief3(agentId) {
|
|
42116
42416
|
return composeAgentBrief(agentId);
|
|
42117
42417
|
}
|
|
42118
|
-
function exportSpecTree2(includeDerived) {
|
|
42119
|
-
return exportSpecTree(includeDerived);
|
|
42418
|
+
function exportSpecTree2(includeDerived, allowPartial) {
|
|
42419
|
+
return exportSpecTree(includeDerived, allowPartial);
|
|
42120
42420
|
}
|
|
42121
42421
|
function importSpecTree2(archive, options) {
|
|
42122
42422
|
return importSpecTree(archive, options);
|
|
@@ -42328,8 +42628,8 @@ async function callTool(target, name, args) {
|
|
|
42328
42628
|
return raw;
|
|
42329
42629
|
}
|
|
42330
42630
|
}
|
|
42331
|
-
async function exportRemoteTree(target) {
|
|
42332
|
-
const payload = await callTool(target, "sdd_host_export_tree", {});
|
|
42631
|
+
async function exportRemoteTree(target, allowPartial) {
|
|
42632
|
+
const payload = await callTool(target, "sdd_host_export_tree", { allowPartial: allowPartial === true });
|
|
42333
42633
|
if (!payload || typeof payload.archiveBase64 !== "string" || !payload.archiveBase64) {
|
|
42334
42634
|
throw new WaironError(`The hosted export returned no archive for project "${target.projectId}".`);
|
|
42335
42635
|
}
|
|
@@ -42338,7 +42638,8 @@ async function exportRemoteTree(target) {
|
|
|
42338
42638
|
suggestedFileName: payload.suggestedFileName ?? `${target.projectId}.waitree`,
|
|
42339
42639
|
projectName: payload.projectName ?? target.projectId,
|
|
42340
42640
|
roots: payload.roots ?? ["."],
|
|
42341
|
-
fileCount: payload.fileCount ?? 0
|
|
42641
|
+
fileCount: payload.fileCount ?? 0,
|
|
42642
|
+
skipped: payload.skipped ?? []
|
|
42342
42643
|
};
|
|
42343
42644
|
if (payload.stateId) result.stateId = payload.stateId;
|
|
42344
42645
|
return result;
|
|
@@ -42398,7 +42699,7 @@ async function initializeRemoteProject(target, ownerUnitId) {
|
|
|
42398
42699
|
return record2.summary ?? `created project "${record2.id ?? topProjectId(target.projectId)}"`;
|
|
42399
42700
|
}
|
|
42400
42701
|
async function pushTree(target, options = {}) {
|
|
42401
|
-
const exported = exportSpecTree2(options.includeDerived);
|
|
42702
|
+
const exported = exportSpecTree2(options.includeDerived, options.allowPartial);
|
|
42402
42703
|
const archivePath = writeArchiveIfAsked(exported.archive, options.archivePath, exported.suggestedFileName);
|
|
42403
42704
|
let createdProject = false;
|
|
42404
42705
|
if (options.createUnitId) {
|
|
@@ -42418,10 +42719,11 @@ async function pushTree(target, options = {}) {
|
|
|
42418
42719
|
};
|
|
42419
42720
|
if (imported.backupPath) result.backupPath = imported.backupPath;
|
|
42420
42721
|
if (archivePath) result.archivePath = archivePath;
|
|
42722
|
+
if (exported.skipped.length > 0) result.skipped = exported.skipped;
|
|
42421
42723
|
return result;
|
|
42422
42724
|
}
|
|
42423
42725
|
async function pullTree(target, options = {}) {
|
|
42424
|
-
const exported = await exportRemoteTree(target);
|
|
42726
|
+
const exported = await exportRemoteTree(target, options.allowPartial);
|
|
42425
42727
|
const archivePath = writeArchiveIfAsked(exported.archive, options.archivePath, exported.suggestedFileName);
|
|
42426
42728
|
const imported = importSpecTree2(exported.archive, {
|
|
42427
42729
|
replaceExisting: options.replaceExisting === true,
|
|
@@ -42438,6 +42740,7 @@ async function pullTree(target, options = {}) {
|
|
|
42438
42740
|
};
|
|
42439
42741
|
if (imported.backupPath) result.backupPath = imported.backupPath;
|
|
42440
42742
|
if (archivePath) result.archivePath = archivePath;
|
|
42743
|
+
if (exported.skipped.length > 0) result.skipped = exported.skipped;
|
|
42441
42744
|
return result;
|
|
42442
42745
|
}
|
|
42443
42746
|
function resolveTarget(root, overrides = {}) {
|
|
@@ -42537,7 +42840,8 @@ async function runRemote(action, options = {}) {
|
|
|
42537
42840
|
assertProjectInitialized();
|
|
42538
42841
|
const transfer = {
|
|
42539
42842
|
replaceExisting: options.force === true,
|
|
42540
|
-
includeDerived: options.includeDerived === true
|
|
42843
|
+
includeDerived: options.includeDerived === true,
|
|
42844
|
+
allowPartial: options.allowPartial === true
|
|
42541
42845
|
};
|
|
42542
42846
|
if (options.unit) transfer.createUnitId = options.unit;
|
|
42543
42847
|
if (options.archive) transfer.archivePath = options.archive;
|
|
@@ -42545,7 +42849,10 @@ async function runRemote(action, options = {}) {
|
|
|
42545
42849
|
return;
|
|
42546
42850
|
}
|
|
42547
42851
|
case "pull": {
|
|
42548
|
-
const transfer = {
|
|
42852
|
+
const transfer = {
|
|
42853
|
+
replaceExisting: options.force === true,
|
|
42854
|
+
allowPartial: options.allowPartial === true
|
|
42855
|
+
};
|
|
42549
42856
|
if (options.archive) transfer.archivePath = options.archive;
|
|
42550
42857
|
if (options.dir) transfer.destDir = path68.resolve(options.dir);
|
|
42551
42858
|
report(await pullTree(target, transfer));
|
|
@@ -42654,6 +42961,11 @@ function report(outcome) {
|
|
|
42654
42961
|
if (outcome.backupPath) {
|
|
42655
42962
|
logger.info(`The replaced tree was backed up to ${import_chalk20.default.cyan(outcome.backupPath)} \u2014 restore it by moving it back.`);
|
|
42656
42963
|
}
|
|
42964
|
+
if (outcome.skipped && outcome.skipped.length > 0) {
|
|
42965
|
+
logger.warn(
|
|
42966
|
+
`Skipped ${outcome.skipped.length} mount(s) \u2014 allowPartial let this through: ` + outcome.skipped.map((s) => `${s.mount} (${s.reason})`).join(", ")
|
|
42967
|
+
);
|
|
42968
|
+
}
|
|
42657
42969
|
}
|
|
42658
42970
|
|
|
42659
42971
|
// src/cli/index.ts
|
|
@@ -42885,7 +43197,7 @@ program.command("doctor").description("Health check: flags stale generated guide
|
|
|
42885
43197
|
await runDoctor({ fix: opts.fix });
|
|
42886
43198
|
});
|
|
42887
43199
|
async function runRules() {
|
|
42888
|
-
await
|
|
43200
|
+
await listRules3();
|
|
42889
43201
|
}
|
|
42890
43202
|
async function runPatterns() {
|
|
42891
43203
|
await listPatterns();
|
|
@@ -43108,7 +43420,10 @@ program.command("login <url>").description("store a bearer credential for a host
|
|
|
43108
43420
|
program.command("logout [url]").description("forget a stored credential for a hosted instance (local only \u2014 does NOT revoke it), or list what is stored").action(async (url) => {
|
|
43109
43421
|
await runLogout(url, {});
|
|
43110
43422
|
});
|
|
43111
|
-
program.command("remote <action>").description("hosted instance: push | pull (migrate a spec tree) \xB7 attach | detach | status (bind this checkout)").option("--url <url>", "hosted instance base URL (else WAIRON_REMOTE_URL)").option("--project <id>", "hosted project id, optionally qualified as project::subsystem (else WAIRON_REMOTE_PROJECT)").option("--token <token>", "bearer credential (else WAIRON_REMOTE_TOKEN) \u2014 mint one in the hosted UI under Tokens").option("--unit <id>", "push: create the destination project first, owned by this organization unit").option("--force", "replace an authored spec tree at the destination (it is backed up first)").option("--include-derived", "push: pack regenerable artifacts (diagrams, generated topology) too").option(
|
|
43423
|
+
program.command("remote <action>").description("hosted instance: push | pull (migrate a spec tree) \xB7 attach | detach | status (bind this checkout)").option("--url <url>", "hosted instance base URL (else WAIRON_REMOTE_URL)").option("--project <id>", "hosted project id, optionally qualified as project::subsystem (else WAIRON_REMOTE_PROJECT)").option("--token <token>", "bearer credential (else WAIRON_REMOTE_TOKEN) \u2014 mint one in the hosted UI under Tokens").option("--unit <id>", "push: create the destination project first, owned by this organization unit").option("--force", "replace an authored spec tree at the destination (it is backed up first)").option("--include-derived", "push: pack regenerable artifacts (diagrams, generated topology) too").option(
|
|
43424
|
+
"--allow-partial",
|
|
43425
|
+
"build the archive even when some chained mounts cannot be packed, listing them as skipped (default: refuse)"
|
|
43426
|
+
).option("--archive <path>", "also write the transferred archive to this path (file or directory)").option("--dir <path>", "pull: the local project root to import into (default: this project)").action(async (action, opts) => {
|
|
43112
43427
|
await runRemote(action, {
|
|
43113
43428
|
url: opts.url,
|
|
43114
43429
|
project: opts.project,
|
|
@@ -43116,6 +43431,7 @@ program.command("remote <action>").description("hosted instance: push | pull (mi
|
|
|
43116
43431
|
unit: opts.unit,
|
|
43117
43432
|
force: opts.force,
|
|
43118
43433
|
includeDerived: opts.includeDerived,
|
|
43434
|
+
allowPartial: opts.allowPartial,
|
|
43119
43435
|
archive: opts.archive,
|
|
43120
43436
|
dir: opts.dir
|
|
43121
43437
|
});
|