@wairon/cli 5.0.2-dev.13 → 5.0.2-dev.15

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/index.js CHANGED
@@ -148,7 +148,7 @@ var init_domain = __esm({
148
148
  });
149
149
 
150
150
  // src/models/project.ts
151
- var import_zod3, BuiltinTargetConfigSchema, CustomTargetConfigSchema, TargetConfigSchema, NamingRuleConfigSchema, DocumentationRuleConfigSchema, ComplexityRuleConfigSchema, DesignDepthSchema, RulesConfigSchema, PathsConfigSchema, ProjectConfigSchema;
151
+ var import_zod3, BuiltinTargetConfigSchema, CustomTargetConfigSchema, TargetConfigSchema, NamingRuleConfigSchema, DocumentationRuleConfigSchema, ComplexityRuleConfigSchema, DesignDepthSchema, RulesConfigSchema, PathsConfigSchema, ProfileSelectionSubjectSchema, ProjectProfileSelectionSchema, ProjectConfigSchema;
152
152
  var init_project = __esm({
153
153
  "src/models/project.ts"() {
154
154
  "use strict";
@@ -272,6 +272,24 @@ var init_project = __esm({
272
272
  /** Base directory containing SDD specification files, relative to project root */
273
273
  specsDir: import_zod3.z.string().default(".wai/specs")
274
274
  });
275
+ ProfileSelectionSubjectSchema = import_zod3.z.object({
276
+ userId: import_zod3.z.string(),
277
+ kind: import_zod3.z.string(),
278
+ issuer: import_zod3.z.string(),
279
+ externalSubject: import_zod3.z.string().optional(),
280
+ displayName: import_zod3.z.string().optional(),
281
+ email: import_zod3.z.string().optional()
282
+ });
283
+ ProjectProfileSelectionSchema = import_zod3.z.object({
284
+ /** Selected architectural profile ids. The first resolvable one is applied as projectType. */
285
+ profileIds: import_zod3.z.array(import_zod3.z.string()).default([]),
286
+ /** Pack names the governing policy requires for this project. */
287
+ requiredPackNames: import_zod3.z.array(import_zod3.z.string()).default([]),
288
+ /** Pack names applied by default unless explicitly overridden. */
289
+ defaultPackNames: import_zod3.z.array(import_zod3.z.string()).optional(),
290
+ selectedBy: ProfileSelectionSubjectSchema.optional(),
291
+ selectedAt: import_zod3.z.string()
292
+ });
275
293
  ProjectConfigSchema = import_zod3.z.object({
276
294
  /**
277
295
  * Schema version — used to detect incompatible config formats in future
@@ -312,6 +330,13 @@ var init_project = __esm({
312
330
  useGlobalPacks: import_zod3.z.boolean().default(true)
313
331
  }).optional(),
314
332
  paths: PathsConfigSchema.default({}),
333
+ /**
334
+ * The profile/pack selection a hosted policy workflow applied to this project.
335
+ * The RECORD of what was chosen; `projectType` above is what actually governs
336
+ * validation. Modeled so the parse/write round trip preserves it (see
337
+ * ProjectProfileSelectionSchema).
338
+ */
339
+ profileSelection: ProjectProfileSelectionSchema.optional(),
315
340
  /**
316
341
  * Path to a directory containing org/user-level default templates.
317
342
  * Resolved before built-in templates but after project-local templates.
@@ -1260,6 +1285,47 @@ var init_errors = __esm({
1260
1285
  }
1261
1286
  });
1262
1287
 
1288
+ // src/core/statehash.ts
1289
+ function computeStateId() {
1290
+ const tree = {
1291
+ system: loadSystemSpec(),
1292
+ subsystems: loadSubsystemSpecs(),
1293
+ components: loadComponentSpecs(),
1294
+ interfaces: loadInterfaceSpecs(),
1295
+ implementations: loadImplementationSpecs(),
1296
+ types: loadTypeSpecs()
1297
+ };
1298
+ const digest = crypto.createHash("sha256").update(canonicalize(tree)).digest("hex");
1299
+ return { algorithm: "sha256", digest };
1300
+ }
1301
+ function stateIdEquals(a, b) {
1302
+ return !!a && !!b && a.algorithm === b.algorithm && a.digest === b.digest;
1303
+ }
1304
+ function canonicalize(value) {
1305
+ return JSON.stringify(sortKeys(value));
1306
+ }
1307
+ function sortKeys(v) {
1308
+ if (Array.isArray(v)) return v.map(sortKeys);
1309
+ if (v && typeof v === "object") {
1310
+ const src = v;
1311
+ const out = {};
1312
+ for (const k of Object.keys(src).sort()) {
1313
+ if (k === "createdAt" || k === "updatedAt") continue;
1314
+ out[k] = sortKeys(src[k]);
1315
+ }
1316
+ return out;
1317
+ }
1318
+ return v;
1319
+ }
1320
+ var crypto;
1321
+ var init_statehash = __esm({
1322
+ "src/core/statehash.ts"() {
1323
+ "use strict";
1324
+ crypto = __toESM(require("crypto"));
1325
+ init_specs2();
1326
+ }
1327
+ });
1328
+
1263
1329
  // src/core/narrative-labels.ts
1264
1330
  function resolveNarrativeLabels(methodName, steps) {
1265
1331
  const errors = [];
@@ -6621,7 +6687,7 @@ var init_extensions = __esm({
6621
6687
  });
6622
6688
 
6623
6689
  // src/core/rules/types.ts
6624
- var BUILTIN_PROFILES;
6690
+ var BUILTIN_PROFILES, PROJECT_KINDS;
6625
6691
  var init_types = __esm({
6626
6692
  "src/core/rules/types.ts"() {
6627
6693
  "use strict";
@@ -6634,6 +6700,7 @@ var init_types = __esm({
6634
6700
  "realtime-embedded",
6635
6701
  "plc-cyclic"
6636
6702
  ];
6703
+ PROJECT_KINDS = ["fullstack", "system-of-systems", "monorepo"];
6637
6704
  }
6638
6705
  });
6639
6706
 
@@ -6824,47 +6891,6 @@ var init_filenames = __esm({
6824
6891
  }
6825
6892
  });
6826
6893
 
6827
- // src/core/statehash.ts
6828
- function computeStateId() {
6829
- const tree = {
6830
- system: loadSystemSpec(),
6831
- subsystems: loadSubsystemSpecs(),
6832
- components: loadComponentSpecs(),
6833
- interfaces: loadInterfaceSpecs(),
6834
- implementations: loadImplementationSpecs(),
6835
- types: loadTypeSpecs()
6836
- };
6837
- const digest = crypto.createHash("sha256").update(canonicalize(tree)).digest("hex");
6838
- return { algorithm: "sha256", digest };
6839
- }
6840
- function stateIdEquals(a, b) {
6841
- return !!a && !!b && a.algorithm === b.algorithm && a.digest === b.digest;
6842
- }
6843
- function canonicalize(value) {
6844
- return JSON.stringify(sortKeys(value));
6845
- }
6846
- function sortKeys(v) {
6847
- if (Array.isArray(v)) return v.map(sortKeys);
6848
- if (v && typeof v === "object") {
6849
- const src = v;
6850
- const out = {};
6851
- for (const k of Object.keys(src).sort()) {
6852
- if (k === "createdAt" || k === "updatedAt") continue;
6853
- out[k] = sortKeys(src[k]);
6854
- }
6855
- return out;
6856
- }
6857
- return v;
6858
- }
6859
- var crypto;
6860
- var init_statehash = __esm({
6861
- "src/core/statehash.ts"() {
6862
- "use strict";
6863
- crypto = __toESM(require("crypto"));
6864
- init_specs2();
6865
- }
6866
- });
6867
-
6868
6894
  // src/core/openapi.ts
6869
6895
  function schemaFor(typeRef, closureIds) {
6870
6896
  const trimmed = typeRef.trim().replace(/^promise\s*<(.+)>$/i, "$1").trim();
@@ -7322,6 +7348,57 @@ function projectOwnSurface(maxAudience) {
7322
7348
  function projectChildSurface() {
7323
7349
  return projectOwnSurface("project");
7324
7350
  }
7351
+ function localName(id) {
7352
+ return id.split("::").pop();
7353
+ }
7354
+ function projectSubsystemSurface(subsystemId) {
7355
+ const system = loadSystemSpec();
7356
+ if (!system) {
7357
+ throw new Error("Cannot project a subsystem surface: the L0 system spec is missing.");
7358
+ }
7359
+ const subsystems = loadSubsystemSpecs();
7360
+ const target = subsystems.find((s) => s.id === subsystemId);
7361
+ if (!target) {
7362
+ throw new Error(`Cannot project a subsystem surface: subsystem "${subsystemId}" does not exist.`);
7363
+ }
7364
+ const components = loadComponentSpecs();
7365
+ const interfaces = loadInterfaceSpecs();
7366
+ const types = loadTypeSpecs();
7367
+ const entries = [];
7368
+ for (const pub of target.publicInterfaces ?? []) {
7369
+ if (!pub.component) continue;
7370
+ const comp = components.find((c) => c.id === pub.component || c.id === `${subsystemId}::${pub.component}`);
7371
+ if (!comp) continue;
7372
+ if (comp.componentType !== "Portal") continue;
7373
+ const compInterfaces = interfaces.filter((i) => i.component === comp.id && (!pub.interface || i.id === pub.interface || i.id === `${subsystemId}::${pub.interface}`));
7374
+ const methods = compInterfaces.flatMap((i) => i.methods);
7375
+ entries.push({
7376
+ id: localName(pub.interface ?? comp.id),
7377
+ name: comp.name,
7378
+ // Family ceiling: a sibling surface is consumable by the system family only.
7379
+ audience: "project",
7380
+ type: pub.type ?? "Custom",
7381
+ // The snapshot carries the LOCAL portal name — consumers resolve cross-tree
7382
+ // refs by their final segment.
7383
+ component: localName(comp.id),
7384
+ methods,
7385
+ ...comp.dispatch && comp.dispatch.length ? { dispatch: comp.dispatch } : {},
7386
+ // Project the backing Portal's auth + basePath so the codec can emit
7387
+ // OpenAPI security + per-portal servers self-contained from the snapshot.
7388
+ ...comp.auth && comp.auth.scheme !== "none" ? { auth: comp.auth } : {},
7389
+ ...comp.basePath ? { basePath: comp.basePath } : {},
7390
+ details: pub.details ?? ""
7391
+ });
7392
+ }
7393
+ return SurfaceSnapshotSchema.parse({
7394
+ projectName: `${system.name}::${subsystemId}`,
7395
+ origin: "generated",
7396
+ stateId: stateIdString(),
7397
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
7398
+ interfaces: entries,
7399
+ types: computeTypeClosure(entries, types)
7400
+ });
7401
+ }
7325
7402
  function listSnapshots(rootDir = getProjectRoot()) {
7326
7403
  const dir = surfacesDir(rootDir);
7327
7404
  if (!fs4.existsSync(dir)) return [];
@@ -7338,18 +7415,37 @@ function listSnapshots(rootDir = getProjectRoot()) {
7338
7415
  function getSnapshot(projectName, rootDir = getProjectRoot()) {
7339
7416
  return listSnapshots(rootDir).find((s) => s.projectName === projectName) ?? null;
7340
7417
  }
7418
+ function snapshotFilename(projectName) {
7419
+ return `${safeFilenamePart(projectName)}.yaml`;
7420
+ }
7341
7421
  function saveSnapshot(snapshot, rootDir = getProjectRoot()) {
7342
7422
  const dir = surfacesDir(rootDir);
7343
7423
  fs4.mkdirSync(dir, { recursive: true });
7344
- const p = path5.join(dir, `${snapshot.projectName}.yaml`);
7424
+ const p = path5.join(dir, snapshotFilename(snapshot.projectName));
7345
7425
  writeYamlFile(p, SurfaceSnapshotSchema.parse(snapshot));
7346
7426
  return p;
7347
7427
  }
7348
7428
  function removeSnapshot(projectName, rootDir = getProjectRoot()) {
7349
- const p = path5.join(surfacesDir(rootDir), `${projectName}.yaml`);
7350
- if (!fs4.existsSync(p)) return false;
7351
- fs4.unlinkSync(p);
7352
- return true;
7429
+ const dir = surfacesDir(rootDir);
7430
+ const direct = path5.join(dir, snapshotFilename(projectName));
7431
+ if (fs4.existsSync(direct)) {
7432
+ fs4.unlinkSync(direct);
7433
+ return true;
7434
+ }
7435
+ if (!fs4.existsSync(dir)) return false;
7436
+ for (const file of fs4.readdirSync(dir)) {
7437
+ if (!file.endsWith(".yaml") && !file.endsWith(".yml")) continue;
7438
+ const p = path5.join(dir, file);
7439
+ try {
7440
+ const snap = SurfaceSnapshotSchema.parse(readYamlFile(p));
7441
+ if (snap.projectName === projectName) {
7442
+ fs4.unlinkSync(p);
7443
+ return true;
7444
+ }
7445
+ } catch {
7446
+ }
7447
+ }
7448
+ return false;
7353
7449
  }
7354
7450
  function loadSurfaceSnapshots() {
7355
7451
  return listSnapshots();
@@ -7420,17 +7516,54 @@ function importSurface(sourcePath, origin) {
7420
7516
  return snapshot;
7421
7517
  }
7422
7518
  function generateChildSnapshots(rootDir = getProjectRoot()) {
7423
- const children = loadSubsystemSpecs().filter((s) => s.projectPath && !s.id.includes("::"));
7519
+ const topLevel = loadSubsystemSpecs().filter((s) => !s.id.includes("::"));
7520
+ const children = topLevel.filter((s) => s.projectPath);
7424
7521
  if (!children.length) return [];
7425
- const snapshot = projectChildSurface();
7522
+ const familySnapshot = projectChildSurface();
7523
+ const siblingSnapshots = /* @__PURE__ */ new Map();
7524
+ const siblingSurface = (subsystemId) => {
7525
+ let snap = siblingSnapshots.get(subsystemId);
7526
+ if (!snap) {
7527
+ snap = projectSubsystemSurface(subsystemId);
7528
+ siblingSnapshots.set(subsystemId, snap);
7529
+ }
7530
+ return snap;
7531
+ };
7426
7532
  const written = [];
7427
7533
  for (const child of children) {
7428
7534
  const childDir = path5.resolve(rootDir, child.projectPath);
7429
7535
  if (!fs4.existsSync(childDir)) continue;
7430
- written.push(saveSnapshot(snapshot, childDir));
7536
+ written.push(saveSnapshot(familySnapshot, childDir));
7537
+ for (const sibling of topLevel) {
7538
+ if (sibling.id === child.id) continue;
7539
+ written.push(saveSnapshot(siblingSurface(sibling.id), childDir));
7540
+ }
7431
7541
  }
7432
7542
  return written;
7433
7543
  }
7544
+ function computeParentStateId(parentRoot) {
7545
+ return computeStateIdAt(parentRoot);
7546
+ }
7547
+ function listExternalInterfaces() {
7548
+ const snapshots = listSnapshots();
7549
+ const chainingParent = resolveChainingParent();
7550
+ const parentStateId = chainingParent ? computeParentStateId(chainingParent.parentRoot) : null;
7551
+ return snapshots.map((snapshot) => {
7552
+ const generated = snapshot.origin === "generated";
7553
+ const sourceKind = !generated ? "foreign" : snapshot.projectName.includes("::") ? "sibling" : "parent";
7554
+ const freshness = generated && parentStateId ? snapshot.stateId === parentStateId ? "fresh" : "stale" : "unverifiable";
7555
+ return {
7556
+ projectName: snapshot.projectName,
7557
+ origin: snapshot.origin,
7558
+ sourceKind,
7559
+ generatedAt: snapshot.generatedAt,
7560
+ ...snapshot.stateId ? { stateId: snapshot.stateId } : {},
7561
+ ...snapshot.version ? { version: snapshot.version } : {},
7562
+ freshness,
7563
+ interfaceIds: snapshot.interfaces.map((e) => e.id)
7564
+ };
7565
+ });
7566
+ }
7434
7567
  function surfaceContentKey(snapshot) {
7435
7568
  const { stateId, generatedAt, origin, ...content } = snapshot;
7436
7569
  return JSON.stringify(content);
@@ -7639,7 +7772,8 @@ var init_contracts = __esm({
7639
7772
  "SURFACE_REF_NOT_EXPOSED",
7640
7773
  `Method "${implMethod.name}" in implementation "${impl.id}" dispatches capability "${step.capability}" through cross-tree portal "${step.targetComponent}" (step ${step.stepNumber}), but the surface snapshot of "${resolved.snapshot.projectName}" does not serve that capability on "${resolved.entry.id}".`,
7641
7774
  impl.id,
7642
- isDraftCtx
7775
+ isDraftCtx,
7776
+ true
7643
7777
  );
7644
7778
  }
7645
7779
  continue;
@@ -7696,7 +7830,8 @@ var init_contracts = __esm({
7696
7830
  "SURFACE_REF_NOT_EXPOSED",
7697
7831
  `Method "${implMethod.name}" in implementation "${impl.id}" calls "${step.targetMethod}" on cross-tree component "${step.targetComponent}" (step ${step.stepNumber}), but the surface snapshot of "${resolved.snapshot.projectName}" does not expose that method on "${resolved.entry.id}".`,
7698
7832
  impl.id,
7699
- isDraftCtx
7833
+ isDraftCtx,
7834
+ true
7700
7835
  );
7701
7836
  } else if (step.assertsGuarantees) {
7702
7837
  const declared = new Set(surfaceMethod.guarantees ?? []);
@@ -7707,7 +7842,8 @@ var init_contracts = __esm({
7707
7842
  "NARRATIVE_SEMANTIC_UNBACKED",
7708
7843
  `Step ${step.stepNumber} of "${implMethod.name}" in implementation "${impl.id}" asserts guarantee "${g}", but the surface snapshot of "${resolved.snapshot.projectName}" does not declare it on "${resolved.entry.id}.${step.targetMethod}".`,
7709
7844
  impl.id,
7710
- isDraftCtx
7845
+ isDraftCtx,
7846
+ true
7711
7847
  );
7712
7848
  }
7713
7849
  }
@@ -9195,7 +9331,8 @@ var init_stereotype_deps = __esm({
9195
9331
  "CROSS_SUBSYSTEM_NON_ADAPTER",
9196
9332
  `Boundary violation: ${comp.componentType} "${comp.id}" depends directly on "${depId}", a surface of project "${resolved.snapshot.projectName}". Only a local client Adapter may cross a project boundary \u2014 route this hop through an Adapter.`,
9197
9333
  comp.id,
9198
- isDraftCtx
9334
+ isDraftCtx,
9335
+ true
9199
9336
  );
9200
9337
  }
9201
9338
  continue;
@@ -9773,14 +9910,14 @@ var init_declarative_assertions = __esm({
9773
9910
  });
9774
9911
 
9775
9912
  // src/core/rules/profiles.ts
9776
- var BACKEND_LIKE, FRONTEND_LIKE, PROJECT_KINDS, profilesRule;
9913
+ var BACKEND_LIKE, FRONTEND_LIKE, PROJECT_KINDS2, profilesRule;
9777
9914
  var init_profiles = __esm({
9778
9915
  "src/core/rules/profiles.ts"() {
9779
9916
  "use strict";
9780
9917
  init_types();
9781
9918
  BACKEND_LIKE = /* @__PURE__ */ new Set(["backend", "lowlevel-os", "game-ecs", "realtime-embedded", "plc-cyclic"]);
9782
9919
  FRONTEND_LIKE = /* @__PURE__ */ new Set(["frontend-reactive", "frontend-controller"]);
9783
- PROJECT_KINDS = /* @__PURE__ */ new Set(["fullstack", "system-of-systems", "monorepo"]);
9920
+ PROJECT_KINDS2 = new Set(PROJECT_KINDS);
9784
9921
  profilesRule = {
9785
9922
  name: "architectural-profiles",
9786
9923
  description: "Per-profile stereotype constraints: View/FeatureComponent/RouterComponent only in frontend profiles; Actor/Supervisor forbidden in plc-cyclic (single scan cycle); Actor/Supervisor in frontend profiles warned. Extension packs may register custom profiles (family + forbidden/discouraged stereotype lists); unknown profile names are flagged.",
@@ -9804,7 +9941,7 @@ var init_profiles = __esm({
9804
9941
  );
9805
9942
  }
9806
9943
  }
9807
- if (!registered.has(ctx.projectType) && !PROJECT_KINDS.has(ctx.projectType)) {
9944
+ if (!registered.has(ctx.projectType) && !PROJECT_KINDS2.has(ctx.projectType)) {
9808
9945
  ctx.addIssue(
9809
9946
  "warning",
9810
9947
  "UNKNOWN_PROFILE",
@@ -12453,9 +12590,15 @@ function buildRuleContext(opts) {
12453
12590
  ...[...SDD_RULES, ...extensions.rules].flatMap((r) => r.codes.map((c) => c.code)),
12454
12591
  // Declarative assertions bring their own namespaced codes — lint.allow
12455
12592
  // and severity overrides treat them exactly like builtins.
12456
- ...extensions.assertions.map((a) => a.fullCode)
12593
+ ...extensions.assertions.map((a) => a.fullCode),
12594
+ // Entry-point emitted codes: validateSddTree's chained-subproject pass
12595
+ // raises these AFTER the rule run (it post-processes the aggregated issue
12596
+ // list), so no registered rule declares them — but lint.allow validation
12597
+ // must still recognize them as real codes.
12598
+ "CHAINED_SUBPROJECT_CONTEXT",
12599
+ "UNVERIFIED_EXTERNAL_REF"
12457
12600
  ]);
12458
- const addIssue = (defaultSeverity, code, message, specId, isDraftContext) => {
12601
+ const addIssue = (defaultSeverity, code, message, specId, isDraftContext, surfaceResolved) => {
12459
12602
  if (scopeSubsystem && specId && !isSpecInScope(specId)) {
12460
12603
  return;
12461
12604
  }
@@ -12473,7 +12616,14 @@ function buildRuleContext(opts) {
12473
12616
  if (severity === "warning") return;
12474
12617
  }
12475
12618
  }
12476
- issues.push({ severity, code, message, specId, ...isDraftContext ? { draftContext: true } : {} });
12619
+ issues.push({
12620
+ severity,
12621
+ code,
12622
+ message,
12623
+ specId,
12624
+ ...isDraftContext ? { draftContext: true } : {},
12625
+ ...surfaceResolved ? { surfaceResolved: true } : {}
12626
+ });
12477
12627
  };
12478
12628
  return {
12479
12629
  system,
@@ -12951,24 +13101,54 @@ function validateSddTree(rulesOrOptions, projectType = "backend") {
12951
13101
  for (const rule of ruleSequence()) {
12952
13102
  rule.check(ctx);
12953
13103
  }
12954
- const hasCrossTreeSuspects = issues.some((i) => SUBPROJECT_LENIENT_CODES.has(i.code));
13104
+ const hasCrossTreeSuspects = issues.some(
13105
+ (i) => SUBPROJECT_REFERENCE_CODES.has(i.code) || SUBPROJECT_CONFORMANCE_CODES.has(i.code)
13106
+ );
12955
13107
  const chainingParent = hasCrossTreeSuspects ? findChainingParent(getProjectRoot()) : null;
12956
13108
  if (chainingParent) {
13109
+ let unverified = 0;
12957
13110
  let downgraded = 0;
12958
- for (const iss of issues) {
12959
- if (!SUBPROJECT_LENIENT_CODES.has(iss.code)) continue;
12960
- if (iss.severity === "error") {
12961
- iss.severity = "warning";
12962
- downgraded++;
13111
+ for (let at = 0; at < issues.length; at++) {
13112
+ const iss = issues[at];
13113
+ if (SUBPROJECT_REFERENCE_CODES.has(iss.code) && !iss.surfaceResolved) {
13114
+ issues[at] = {
13115
+ severity: "warning",
13116
+ code: "UNVERIFIED_EXTERNAL_REF",
13117
+ crossTreeContext: true,
13118
+ // --ci waives it (parent root is authoritative)
13119
+ specId: iss.specId,
13120
+ ...iss.agentId ? { agentId: iss.agentId } : {},
13121
+ ...iss.draftContext ? { draftContext: true } : {},
13122
+ message: `Unverified external reference (${iss.code}): ${iss.message} No vendored surface snapshot covers this reference, so it cannot be verified from this chained subproject standalone \u2014 re-lock the parent so fresh family/sibling snapshots ship, or inspect what this project can consume via \`wairon surface externals\` / sdd_list_external_interfaces.`
13123
+ };
13124
+ unverified++;
13125
+ continue;
13126
+ }
13127
+ if (SUBPROJECT_CONFORMANCE_CODES.has(iss.code)) {
13128
+ if (iss.severity === "error") {
13129
+ iss.severity = "warning";
13130
+ downgraded++;
13131
+ }
13132
+ iss.crossTreeContext = true;
12963
13133
  }
12964
- iss.crossTreeContext = true;
12965
13134
  }
12966
- if (downgraded > 0) {
13135
+ if (unverified > 0 || downgraded > 0) {
13136
+ const notes = [];
13137
+ if (unverified > 0) {
13138
+ notes.push(
13139
+ `${unverified} cross-tree reference(s) have no vendored surface snapshot covering them and were reported as UNVERIFIED_EXTERNAL_REF warnings \u2014 re-lock the parent so fresh family/sibling snapshots ship, or inspect via \`wairon surface externals\` / sdd_list_external_interfaces.`
13140
+ );
13141
+ }
13142
+ if (downgraded > 0) {
13143
+ notes.push(
13144
+ `${downgraded} code\u2194spec conformance finding(s) (parent-root-relative source paths) were downgraded to warnings.`
13145
+ );
13146
+ }
12967
13147
  issues.unshift({
12968
13148
  severity: "warning",
12969
13149
  code: "CHAINED_SUBPROJECT_CONTEXT",
12970
13150
  crossTreeContext: true,
12971
- message: `This project is a chained subproject ("${chainingParent.subsystemId}") of the parent project at "${chainingParent.parentRoot}". ${downgraded} reference(s) resolve only in the parent tree (shared types, sibling subsystems, or cross-tree components that live above this root) and were downgraded to warnings \u2014 validating a subproject standalone cannot verify them. Run validation from the parent root for full cross-tree verification.`
13151
+ message: `This project is a chained subproject ("${chainingParent.subsystemId}") of the parent project at "${chainingParent.parentRoot}". ${notes.join(" ")} Full cross-tree verification runs from the parent root.`
12972
13152
  });
12973
13153
  }
12974
13154
  }
@@ -12985,7 +13165,7 @@ function validateSddTree(rulesOrOptions, projectType = "backend") {
12985
13165
  function validateAsComplete(options) {
12986
13166
  return validateSddTree({ ...options ?? {}, treatAllAsComplete: true });
12987
13167
  }
12988
- var SUBPROJECT_LENIENT_CODES;
13168
+ var SUBPROJECT_REFERENCE_CODES, SUBPROJECT_CONFORMANCE_CODES;
12989
13169
  var init_validation = __esm({
12990
13170
  "src/core/validation.ts"() {
12991
13171
  "use strict";
@@ -12998,8 +13178,7 @@ var init_validation = __esm({
12998
13178
  init_source_analysis();
12999
13179
  init_specs2();
13000
13180
  init_fs();
13001
- SUBPROJECT_LENIENT_CODES = /* @__PURE__ */ new Set([
13002
- // reference resolution
13181
+ SUBPROJECT_REFERENCE_CODES = /* @__PURE__ */ new Set([
13003
13182
  "UNDEFINED_TYPE_REFERENCE",
13004
13183
  "INVALID_DEPENDENCY_REFERENCE",
13005
13184
  "INVALID_TARGET_COMPONENT_REFERENCE",
@@ -13007,8 +13186,9 @@ var init_validation = __esm({
13007
13186
  "UNDECLARED_DEPENDENCY_CALL",
13008
13187
  "INVALID_TRUSTED_LINK",
13009
13188
  "CROSS_SUBSYSTEM_NON_ADAPTER",
13010
- "CROSS_TREE_REF_UNRESOLVED",
13011
- // code↔spec conformance (root-relative sourcePaths / import graph)
13189
+ "CROSS_TREE_REF_UNRESOLVED"
13190
+ ]);
13191
+ SUBPROJECT_CONFORMANCE_CODES = /* @__PURE__ */ new Set([
13012
13192
  "MISSING_SOURCE_FILE",
13013
13193
  "SOURCE_PATH_ESCAPES_ROOT",
13014
13194
  "MISSING_SOURCE_PATH",
@@ -13931,6 +14111,19 @@ function dryRunSerializeSpecs(include) {
13931
14111
  function buildProjectGraph(level) {
13932
14112
  return buildGraphModel(level);
13933
14113
  }
14114
+ function resolveChainingParent() {
14115
+ return findChainingParent(getProjectRoot());
14116
+ }
14117
+ function computeStateIdAt(root) {
14118
+ const resolved = path10.resolve(root);
14119
+ return runWithProjectRoot(resolved, () => {
14120
+ workspaceFor(resolved).invalidate();
14121
+ const system = loadSystemSpec();
14122
+ if (!system) return null;
14123
+ const s = computeStateId();
14124
+ return `${s.algorithm}:${s.digest}`;
14125
+ });
14126
+ }
13934
14127
  function deleteTypeSpec(id) {
13935
14128
  return current().deleteTypeSpec(id);
13936
14129
  }
@@ -13974,6 +14167,7 @@ var init_specs2 = __esm({
13974
14167
  path10 = __toESM(require("path"));
13975
14168
  init_loader();
13976
14169
  init_fs();
14170
+ init_statehash();
13977
14171
  init_yaml();
13978
14172
  init_models();
13979
14173
  init_narrative_labels();
@@ -16068,6 +16262,7 @@ __export(src_exports, {
16068
16262
  OutputTargetSchema: () => OutputTargetSchema,
16069
16263
  PACK_DIR_ENTRIES: () => PACK_DIR_ENTRIES,
16070
16264
  PATTERN_TYPES: () => PATTERN_TYPES,
16265
+ PROJECT_KINDS: () => PROJECT_KINDS,
16071
16266
  PackAssertionSchema: () => PackAssertionSchema,
16072
16267
  PackSkillSchema: () => PackSkillSchema,
16073
16268
  ParallelBranchSchema: () => ParallelBranchSchema,
@@ -16078,8 +16273,10 @@ __export(src_exports, {
16078
16273
  PortalAuthSchemeSchema: () => PortalAuthSchemeSchema,
16079
16274
  PortalTypeSchema: () => PortalTypeSchema,
16080
16275
  ProfileDefSchema: () => ProfileDefSchema,
16276
+ ProfileSelectionSubjectSchema: () => ProfileSelectionSubjectSchema,
16081
16277
  ProjectConfigSchema: () => ProjectConfigSchema,
16082
16278
  ProjectNotInitializedError: () => ProjectNotInitializedError,
16279
+ ProjectProfileSelectionSchema: () => ProjectProfileSelectionSchema,
16083
16280
  PublicInterfaceSchema: () => PublicInterfaceSchema,
16084
16281
  PublicInterfaceTypeSchema: () => PublicInterfaceTypeSchema,
16085
16282
  RegistrySchema: () => RegistrySchema,
@@ -16138,7 +16335,9 @@ __export(src_exports, {
16138
16335
  clearLoaderIssues: () => clearLoaderIssues,
16139
16336
  collectPromotableSpecs: () => collectPromotableSpecs,
16140
16337
  composeRuleSequence: () => composeRuleSequence,
16338
+ computeParentStateId: () => computeParentStateId,
16141
16339
  computeStateId: () => computeStateId,
16340
+ computeStateIdAt: () => computeStateIdAt,
16142
16341
  contextDir: () => contextDir,
16143
16342
  createAgentRecord: () => createAgentRecord,
16144
16343
  createChainedSubsystem: () => createChainedSubsystem,
@@ -16204,6 +16403,7 @@ __export(src_exports, {
16204
16403
  isOpenApiDocument: () => isOpenApiDocument,
16205
16404
  isProjectInitialized: () => isProjectInitialized,
16206
16405
  listDirectChainedSubprojects: () => listDirectChainedSubprojects,
16406
+ listExternalInterfaces: () => listExternalInterfaces,
16207
16407
  listFiles: () => listFiles,
16208
16408
  listFilesRecursive: () => listFilesRecursive,
16209
16409
  listFreeStandingDomains: () => listFreeStandingDomains,
@@ -16249,6 +16449,7 @@ __export(src_exports, {
16249
16449
  pathExists: () => pathExists,
16250
16450
  projectChildSurface: () => projectChildSurface,
16251
16451
  projectOwnSurface: () => projectOwnSurface,
16452
+ projectSubsystemSurface: () => projectSubsystemSurface,
16252
16453
  promoteAllComplete: () => promoteAllComplete,
16253
16454
  provisionProject: () => provisionProject,
16254
16455
  readArchitectureContext: () => readArchitectureContext,
@@ -16268,6 +16469,7 @@ __export(src_exports, {
16268
16469
  renderTemplateInstructions: () => renderTemplateInstructions,
16269
16470
  renderWaironGuide: () => renderWaironGuide,
16270
16471
  resolveAgentTopology: () => resolveAgentTopology,
16472
+ resolveChainingParent: () => resolveChainingParent,
16271
16473
  resolveDomains: () => resolveDomains,
16272
16474
  resolvePackRef: () => resolvePackRef,
16273
16475
  resolveSubprojectForNamespace: () => resolveSubprojectForNamespace,
@@ -16331,7 +16533,7 @@ function defaultTargetConfig(type) {
16331
16533
  enabled: true
16332
16534
  };
16333
16535
  }
16334
- var WAIRON_VERSION = "5.0.2-dev.13";
16536
+ var WAIRON_VERSION = "5.0.2-dev.15";
16335
16537
  var GITHUB_REPO = "SYW-Apps/Waffle-AIron";
16336
16538
  var ARCHITECT_AGENT_ID = "agent-architect";
16337
16539
  var ARCHITECT_TEMPLATE_ID = "architect";
@@ -17655,6 +17857,7 @@ init_yaml();
17655
17857
  OutputTargetSchema,
17656
17858
  PACK_DIR_ENTRIES,
17657
17859
  PATTERN_TYPES,
17860
+ PROJECT_KINDS,
17658
17861
  PackAssertionSchema,
17659
17862
  PackSkillSchema,
17660
17863
  ParallelBranchSchema,
@@ -17665,8 +17868,10 @@ init_yaml();
17665
17868
  PortalAuthSchemeSchema,
17666
17869
  PortalTypeSchema,
17667
17870
  ProfileDefSchema,
17871
+ ProfileSelectionSubjectSchema,
17668
17872
  ProjectConfigSchema,
17669
17873
  ProjectNotInitializedError,
17874
+ ProjectProfileSelectionSchema,
17670
17875
  PublicInterfaceSchema,
17671
17876
  PublicInterfaceTypeSchema,
17672
17877
  RegistrySchema,
@@ -17725,7 +17930,9 @@ init_yaml();
17725
17930
  clearLoaderIssues,
17726
17931
  collectPromotableSpecs,
17727
17932
  composeRuleSequence,
17933
+ computeParentStateId,
17728
17934
  computeStateId,
17935
+ computeStateIdAt,
17729
17936
  contextDir,
17730
17937
  createAgentRecord,
17731
17938
  createChainedSubsystem,
@@ -17791,6 +17998,7 @@ init_yaml();
17791
17998
  isOpenApiDocument,
17792
17999
  isProjectInitialized,
17793
18000
  listDirectChainedSubprojects,
18001
+ listExternalInterfaces,
17794
18002
  listFiles,
17795
18003
  listFilesRecursive,
17796
18004
  listFreeStandingDomains,
@@ -17836,6 +18044,7 @@ init_yaml();
17836
18044
  pathExists,
17837
18045
  projectChildSurface,
17838
18046
  projectOwnSurface,
18047
+ projectSubsystemSurface,
17839
18048
  promoteAllComplete,
17840
18049
  provisionProject,
17841
18050
  readArchitectureContext,
@@ -17855,6 +18064,7 @@ init_yaml();
17855
18064
  renderTemplateInstructions,
17856
18065
  renderWaironGuide,
17857
18066
  resolveAgentTopology,
18067
+ resolveChainingParent,
17858
18068
  resolveDomains,
17859
18069
  resolvePackRef,
17860
18070
  resolveSubprojectForNamespace,