arkgate 2.8.3 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/README.md +2 -1
  3. package/bin/ark-check.mjs +2 -2
  4. package/bin/ark-layer-match.mjs +88 -5
  5. package/bin/ark-mcp.mjs +7 -70
  6. package/bin/ark-shared.mjs +88 -8
  7. package/bin/ark.mjs +3 -2
  8. package/bin/lib/architecture-scan.mjs +16 -4
  9. package/bin/lib/config-warnings.mjs +11 -2
  10. package/bin/lib/doctor-plan.mjs +20 -0
  11. package/bin/lib/import-resolve.mjs +133 -0
  12. package/bin/lib/presets.mjs +207 -11
  13. package/bin/lib/remediation.mjs +15 -0
  14. package/bin/lib/suggestions.mjs +8 -3
  15. package/dist/eslint/index.cjs +63 -5
  16. package/dist/eslint/index.cjs.map +1 -1
  17. package/dist/eslint/index.d.cts +33 -1
  18. package/dist/eslint/index.d.ts +33 -1
  19. package/dist/eslint/index.js +63 -5
  20. package/dist/eslint/index.js.map +1 -1
  21. package/dist/index.cjs +103 -14
  22. package/dist/index.cjs.map +1 -1
  23. package/dist/index.d.cts +21 -9
  24. package/dist/index.d.ts +21 -9
  25. package/dist/index.js +103 -14
  26. package/dist/index.js.map +1 -1
  27. package/dist/nestjs/index.cjs +78 -4
  28. package/dist/nestjs/index.cjs.map +1 -1
  29. package/dist/nestjs/index.d.cts +1 -1
  30. package/dist/nestjs/index.d.ts +1 -1
  31. package/dist/nestjs/index.js +78 -4
  32. package/dist/nestjs/index.js.map +1 -1
  33. package/dist/runtime/index.cjs +103 -14
  34. package/dist/runtime/index.cjs.map +1 -1
  35. package/dist/runtime/index.d.cts +1 -1
  36. package/dist/runtime/index.d.ts +1 -1
  37. package/dist/runtime/index.js +103 -14
  38. package/dist/runtime/index.js.map +1 -1
  39. package/dist/{types-CSJhEOk2.d.cts → types-D6Q8WHes.d.cts} +7 -0
  40. package/dist/{types-CSJhEOk2.d.ts → types-D6Q8WHes.d.ts} +7 -0
  41. package/docs/agent-guide.md +55 -4
  42. package/package.json +3 -1
  43. package/server.json +2 -2
  44. package/templates/architecture-playbook.json +65 -1
  45. package/templates/policy-packs/enthusiast-ddd-bounded-contexts.json +19 -0
  46. package/templates/policy-packs/enthusiast-ui-surface.json +18 -0
  47. package/templates/policy-packs/enthusiast-vertical-slice.json +18 -0
  48. package/templates/skills/ark-adopt.md +4 -0
  49. package/templates/skills/ark-architect.md +5 -1
  50. package/templates/skills/ark-autopilot.md +3 -1
  51. package/templates/skills/ark-fix.md +3 -0
  52. package/templates/skills/ark-place.md +7 -0
  53. package/templates/skills/ark-think.md +43 -0
package/dist/index.d.cts CHANGED
@@ -1,8 +1,8 @@
1
- import { I as IntentRegistry, a as IntentName, D as DefineIntentOptions, b as IntentCreator, P as PolicyViolation, c as PolicySeverity, d as PolicyEnforcementMode, e as Policy, f as CreateArchitectureProfileOptions, g as ArchitectureProfile, h as ArkCheckConfig, i as CreateArchitectureProfileFromArkConfigOptions, j as CreateElevenLayerArkConfigOptions, k as DependencyGraph, G as GraphEdge, l as IntentRelationship, m as AuditStore, n as AuditRecord, o as AuditQuery, p as CreateAuditTrailOptions, q as AuditTrail, E as EventContractRegistry, r as EventContract, s as DomainEvent, t as EventContractValidationResult, O as OutboxStore, u as OutboxRecord, v as OutboxStatus, w as EventBusOptions, x as EventBus, y as CreateObservabilityReporterOptions, z as ObservabilityReporter, M as MetadataRegistry, R as ReadModelStore, B as CreateProjectionRegistryOptions, F as ProjectionRegistry, H as PolicyEngine, J as ArkManifest, W as WorkflowStore, S as SagaContext, K as WorkflowSnapshot, L as SagaDefinition, N as CreateWorkflowEngineOptions, Q as SagaInstance, T as WorkflowEngine, C as CreateArkKernelOptions, A as ArkKernel, U as ArkKernelConfig, V as CreateArkKernelFromConfigOptions, X as TraceRecordType, Y as TraceRecord, Z as ObservabilityDriftReport } from './types-CSJhEOk2.cjs';
2
- export { _ as ArchitectureLayer, $ as ArchitectureLayerConfig, a0 as ArchitectureRule, a1 as ArkManifestArchitecture, a2 as ArkManifestData, a3 as ArkManifestEntityLink, a4 as ArkManifestGraph, a5 as ArkManifestIntent, a6 as ArkManifestPolicy, a7 as ArkManifestProjection, a8 as AuditRecordInput, a9 as AuditRecordType, aa as CorrelationId, ab as EntityMeta, ac as EventContractIssue, ad as EventHandler, ae as EventInterceptionInfo, af as EventInterceptor, ag as EventInterceptorContext, ah as EventMetadata, ai as EventPayloadPatch, aj as EventPayloadSchema, ak as EventPublisher, al as EventSchemaField, am as EventSchemaFieldType, an as FieldMeta, ao as GraphNode, ap as IntentRelationshipKind, aq as ObservabilityFlow, ar as ObservedLayerFlowMode, as as PolicyEvaluationResult, at as ProjectionCheckpoint, au as ProjectionDefinition, av as PublishedEventRecord, aw as RetryPolicy, ax as SagaStatus, ay as SagaStep, az as TraceSink, aA as Unsubscribe, aB as WorkflowDefinition, aC as WorkflowStatus, aD as WorkflowStep } from './types-CSJhEOk2.cjs';
1
+ import { I as IntentRegistry, a as IntentName, D as DefineIntentOptions, b as IntentCreator, P as PolicyViolation, c as PolicySeverity, d as PolicyEnforcementMode, e as Policy, f as CreateArchitectureProfileOptions, g as ArchitectureProfile, h as ArkCheckConfig, i as CreateArchitectureProfileFromArkConfigOptions, j as CreateElevenLayerArkConfigOptions, k as DependencyGraph, G as GraphEdge, l as IntentRelationship, m as AuditStore, n as AuditRecord, o as AuditQuery, p as CreateAuditTrailOptions, q as AuditTrail, E as EventContractRegistry, r as EventContract, s as DomainEvent, t as EventContractValidationResult, O as OutboxStore, u as OutboxRecord, v as OutboxStatus, w as EventBusOptions, x as EventBus, y as CreateObservabilityReporterOptions, z as ObservabilityReporter, M as MetadataRegistry, R as ReadModelStore, B as CreateProjectionRegistryOptions, F as ProjectionRegistry, H as PolicyEngine, J as ArkManifest, W as WorkflowStore, S as SagaContext, K as WorkflowSnapshot, L as SagaDefinition, N as CreateWorkflowEngineOptions, Q as SagaInstance, T as WorkflowEngine, C as CreateArkKernelOptions, A as ArkKernel, U as ArkKernelConfig, V as CreateArkKernelFromConfigOptions, X as TraceRecordType, Y as TraceRecord, Z as ObservabilityDriftReport } from './types-D6Q8WHes.cjs';
2
+ export { _ as ArchitectureLayer, $ as ArchitectureLayerConfig, a0 as ArchitectureRule, a1 as ArkManifestArchitecture, a2 as ArkManifestData, a3 as ArkManifestEntityLink, a4 as ArkManifestGraph, a5 as ArkManifestIntent, a6 as ArkManifestPolicy, a7 as ArkManifestProjection, a8 as AuditRecordInput, a9 as AuditRecordType, aa as CorrelationId, ab as EntityMeta, ac as EventContractIssue, ad as EventHandler, ae as EventInterceptionInfo, af as EventInterceptor, ag as EventInterceptorContext, ah as EventMetadata, ai as EventPayloadPatch, aj as EventPayloadSchema, ak as EventPublisher, al as EventSchemaField, am as EventSchemaFieldType, an as FieldMeta, ao as GraphNode, ap as IntentRelationshipKind, aq as ObservabilityFlow, ar as ObservedLayerFlowMode, as as PolicyEvaluationResult, at as ProjectionCheckpoint, au as ProjectionDefinition, av as PublishedEventRecord, aw as RetryPolicy, ax as SagaStatus, ay as SagaStep, az as TraceSink, aA as Unsubscribe, aB as WorkflowDefinition, aC as WorkflowStatus, aD as WorkflowStep } from './types-D6Q8WHes.cjs';
3
3
 
4
4
  /** ArkGate library version — single source of truth. */
5
- declare const version = "2.8.3";
5
+ declare const version = "2.9.0";
6
6
 
7
7
  /**
8
8
  * defineIntent
@@ -583,14 +583,26 @@ interface AICodeGateOptions<Context = AICodeGateContext> {
583
583
  */
584
584
  infrastructureLayers?: string[];
585
585
  /**
586
- * Resolve an import specifier to the architecture layer of its TARGET file, if that target
587
- * is governed by a declared layer (else undefined). When provided, the gate lets the
588
- * layer RULES decide a governed edge instead of the infrastructure path-heuristic — so the
589
- * write gate honors `ark.config.json` exactly like `ark-check`, and the heuristic only
590
- * applies to ungoverned targets (external packages, paths no layer covers). The caller
591
- * (ark-mcp) supplies this using the config's layer globs + tsconfig path aliases.
586
+ * Preferred single resolve step: import specifier or absolute source file
587
+ * `{ layer, relPath }` for contract + peerIsolation. Prefer this over the
588
+ * legacy layer-only callback.
589
+ */
590
+ resolveImportTarget?: (specifierOrFilePath: string, fromFilePath?: string) => {
591
+ layer?: string;
592
+ relPath?: string;
593
+ } | undefined;
594
+ /**
595
+ * @deprecated Prefer resolveImportTarget. Layer-only resolve for governed imports.
592
596
  */
593
597
  resolveImportLayer?: (specifier: string, fromFilePath?: string) => string | undefined;
598
+ /**
599
+ * Layer configs (patterns) used to infer sliceFolders when a peerIsolation rule
600
+ * omits an explicit list.
601
+ */
602
+ architectureLayers?: Array<{
603
+ name: string;
604
+ patterns?: string[];
605
+ }>;
594
606
  }
595
607
  declare function createAICodeGate<Context = AICodeGateContext>(options?: AICodeGateOptions<Context>): AICodeGate<Context>;
596
608
 
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { I as IntentRegistry, a as IntentName, D as DefineIntentOptions, b as IntentCreator, P as PolicyViolation, c as PolicySeverity, d as PolicyEnforcementMode, e as Policy, f as CreateArchitectureProfileOptions, g as ArchitectureProfile, h as ArkCheckConfig, i as CreateArchitectureProfileFromArkConfigOptions, j as CreateElevenLayerArkConfigOptions, k as DependencyGraph, G as GraphEdge, l as IntentRelationship, m as AuditStore, n as AuditRecord, o as AuditQuery, p as CreateAuditTrailOptions, q as AuditTrail, E as EventContractRegistry, r as EventContract, s as DomainEvent, t as EventContractValidationResult, O as OutboxStore, u as OutboxRecord, v as OutboxStatus, w as EventBusOptions, x as EventBus, y as CreateObservabilityReporterOptions, z as ObservabilityReporter, M as MetadataRegistry, R as ReadModelStore, B as CreateProjectionRegistryOptions, F as ProjectionRegistry, H as PolicyEngine, J as ArkManifest, W as WorkflowStore, S as SagaContext, K as WorkflowSnapshot, L as SagaDefinition, N as CreateWorkflowEngineOptions, Q as SagaInstance, T as WorkflowEngine, C as CreateArkKernelOptions, A as ArkKernel, U as ArkKernelConfig, V as CreateArkKernelFromConfigOptions, X as TraceRecordType, Y as TraceRecord, Z as ObservabilityDriftReport } from './types-CSJhEOk2.js';
2
- export { _ as ArchitectureLayer, $ as ArchitectureLayerConfig, a0 as ArchitectureRule, a1 as ArkManifestArchitecture, a2 as ArkManifestData, a3 as ArkManifestEntityLink, a4 as ArkManifestGraph, a5 as ArkManifestIntent, a6 as ArkManifestPolicy, a7 as ArkManifestProjection, a8 as AuditRecordInput, a9 as AuditRecordType, aa as CorrelationId, ab as EntityMeta, ac as EventContractIssue, ad as EventHandler, ae as EventInterceptionInfo, af as EventInterceptor, ag as EventInterceptorContext, ah as EventMetadata, ai as EventPayloadPatch, aj as EventPayloadSchema, ak as EventPublisher, al as EventSchemaField, am as EventSchemaFieldType, an as FieldMeta, ao as GraphNode, ap as IntentRelationshipKind, aq as ObservabilityFlow, ar as ObservedLayerFlowMode, as as PolicyEvaluationResult, at as ProjectionCheckpoint, au as ProjectionDefinition, av as PublishedEventRecord, aw as RetryPolicy, ax as SagaStatus, ay as SagaStep, az as TraceSink, aA as Unsubscribe, aB as WorkflowDefinition, aC as WorkflowStatus, aD as WorkflowStep } from './types-CSJhEOk2.js';
1
+ import { I as IntentRegistry, a as IntentName, D as DefineIntentOptions, b as IntentCreator, P as PolicyViolation, c as PolicySeverity, d as PolicyEnforcementMode, e as Policy, f as CreateArchitectureProfileOptions, g as ArchitectureProfile, h as ArkCheckConfig, i as CreateArchitectureProfileFromArkConfigOptions, j as CreateElevenLayerArkConfigOptions, k as DependencyGraph, G as GraphEdge, l as IntentRelationship, m as AuditStore, n as AuditRecord, o as AuditQuery, p as CreateAuditTrailOptions, q as AuditTrail, E as EventContractRegistry, r as EventContract, s as DomainEvent, t as EventContractValidationResult, O as OutboxStore, u as OutboxRecord, v as OutboxStatus, w as EventBusOptions, x as EventBus, y as CreateObservabilityReporterOptions, z as ObservabilityReporter, M as MetadataRegistry, R as ReadModelStore, B as CreateProjectionRegistryOptions, F as ProjectionRegistry, H as PolicyEngine, J as ArkManifest, W as WorkflowStore, S as SagaContext, K as WorkflowSnapshot, L as SagaDefinition, N as CreateWorkflowEngineOptions, Q as SagaInstance, T as WorkflowEngine, C as CreateArkKernelOptions, A as ArkKernel, U as ArkKernelConfig, V as CreateArkKernelFromConfigOptions, X as TraceRecordType, Y as TraceRecord, Z as ObservabilityDriftReport } from './types-D6Q8WHes.js';
2
+ export { _ as ArchitectureLayer, $ as ArchitectureLayerConfig, a0 as ArchitectureRule, a1 as ArkManifestArchitecture, a2 as ArkManifestData, a3 as ArkManifestEntityLink, a4 as ArkManifestGraph, a5 as ArkManifestIntent, a6 as ArkManifestPolicy, a7 as ArkManifestProjection, a8 as AuditRecordInput, a9 as AuditRecordType, aa as CorrelationId, ab as EntityMeta, ac as EventContractIssue, ad as EventHandler, ae as EventInterceptionInfo, af as EventInterceptor, ag as EventInterceptorContext, ah as EventMetadata, ai as EventPayloadPatch, aj as EventPayloadSchema, ak as EventPublisher, al as EventSchemaField, am as EventSchemaFieldType, an as FieldMeta, ao as GraphNode, ap as IntentRelationshipKind, aq as ObservabilityFlow, ar as ObservedLayerFlowMode, as as PolicyEvaluationResult, at as ProjectionCheckpoint, au as ProjectionDefinition, av as PublishedEventRecord, aw as RetryPolicy, ax as SagaStatus, ay as SagaStep, az as TraceSink, aA as Unsubscribe, aB as WorkflowDefinition, aC as WorkflowStatus, aD as WorkflowStep } from './types-D6Q8WHes.js';
3
3
 
4
4
  /** ArkGate library version — single source of truth. */
5
- declare const version = "2.8.3";
5
+ declare const version = "2.9.0";
6
6
 
7
7
  /**
8
8
  * defineIntent
@@ -583,14 +583,26 @@ interface AICodeGateOptions<Context = AICodeGateContext> {
583
583
  */
584
584
  infrastructureLayers?: string[];
585
585
  /**
586
- * Resolve an import specifier to the architecture layer of its TARGET file, if that target
587
- * is governed by a declared layer (else undefined). When provided, the gate lets the
588
- * layer RULES decide a governed edge instead of the infrastructure path-heuristic — so the
589
- * write gate honors `ark.config.json` exactly like `ark-check`, and the heuristic only
590
- * applies to ungoverned targets (external packages, paths no layer covers). The caller
591
- * (ark-mcp) supplies this using the config's layer globs + tsconfig path aliases.
586
+ * Preferred single resolve step: import specifier or absolute source file
587
+ * `{ layer, relPath }` for contract + peerIsolation. Prefer this over the
588
+ * legacy layer-only callback.
589
+ */
590
+ resolveImportTarget?: (specifierOrFilePath: string, fromFilePath?: string) => {
591
+ layer?: string;
592
+ relPath?: string;
593
+ } | undefined;
594
+ /**
595
+ * @deprecated Prefer resolveImportTarget. Layer-only resolve for governed imports.
592
596
  */
593
597
  resolveImportLayer?: (specifier: string, fromFilePath?: string) => string | undefined;
598
+ /**
599
+ * Layer configs (patterns) used to infer sliceFolders when a peerIsolation rule
600
+ * omits an explicit list.
601
+ */
602
+ architectureLayers?: Array<{
603
+ name: string;
604
+ patterns?: string[];
605
+ }>;
594
606
  }
595
607
  declare function createAICodeGate<Context = AICodeGateContext>(options?: AICodeGateOptions<Context>): AICodeGate<Context>;
596
608
 
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/version.ts
2
- var version = "2.8.3";
2
+ var version = "2.9.0";
3
3
 
4
4
  // src/kernel/intent/IntentRegistry.ts
5
5
  var IntentRegistry = class {
@@ -662,6 +662,82 @@ async function recordInterceptorError(interceptor, event, error, deps) {
662
662
  });
663
663
  }
664
664
 
665
+ // src/domain/layerMatch.ts
666
+ function normalizeGlobSeparators(pattern) {
667
+ let out = "";
668
+ for (let i = 0; i < pattern.length; i += 1) {
669
+ const c = pattern[i];
670
+ if (c === "\\" && i + 1 < pattern.length) {
671
+ const next = pattern[i + 1];
672
+ if ("*?{}[],".includes(next) || next === "\\") {
673
+ out += "\\" + next;
674
+ i += 1;
675
+ continue;
676
+ }
677
+ out += "/";
678
+ continue;
679
+ }
680
+ out += c;
681
+ }
682
+ return out;
683
+ }
684
+ function sliceIdForPath(relPath, sliceFolders) {
685
+ if (!sliceFolders?.length) return void 0;
686
+ const parts = String(relPath).split(/[/\\]/).filter(Boolean);
687
+ const folders = new Set(sliceFolders.map((s) => String(s).toLowerCase()));
688
+ for (let i = 0; i < parts.length - 1; i += 1) {
689
+ if (folders.has(parts[i].toLowerCase())) {
690
+ return `${parts[i]}/${parts[i + 1]}`;
691
+ }
692
+ }
693
+ return void 0;
694
+ }
695
+ function inferSliceFoldersFromPatterns(patterns) {
696
+ const out = /* @__PURE__ */ new Set();
697
+ for (const pattern of patterns ?? []) {
698
+ const glob = normalizeGlobSeparators(String(pattern));
699
+ const parts = glob.split("/").filter(Boolean);
700
+ for (let i = 0; i < parts.length; i += 1) {
701
+ const part = parts[i];
702
+ if ((part === "**" || part === "*") && i > 0) {
703
+ const prev = parts[i - 1];
704
+ if (prev && !prev.includes("*") && !prev.includes("{") && !prev.includes("}")) {
705
+ out.add(prev);
706
+ }
707
+ }
708
+ }
709
+ }
710
+ return [...out];
711
+ }
712
+ function resolveSliceFolders(rule, layerName, layers) {
713
+ if (Array.isArray(rule.sliceFolders) && rule.sliceFolders.length > 0) {
714
+ return rule.sliceFolders.filter((s) => typeof s === "string" && s.length > 0);
715
+ }
716
+ const layer = (layers ?? []).find((l) => l.name === layerName);
717
+ return inferSliceFoldersFromPatterns(layer?.patterns);
718
+ }
719
+ function findDeniedEdgeRule(rules, from, to, options) {
720
+ for (const rule of rules ?? []) {
721
+ if (rule.from !== from || rule.to !== to) continue;
722
+ if (rule.allowed !== false) continue;
723
+ if (rule.peerIsolation) {
724
+ const fromPath = options?.fromPath;
725
+ const toPath = options?.toPath;
726
+ if (!fromPath || !toPath) continue;
727
+ const folders = resolveSliceFolders(rule, from, options?.layers);
728
+ if (folders.length === 0) continue;
729
+ const fromSlice = sliceIdForPath(fromPath, folders);
730
+ const toSlice = sliceIdForPath(toPath, folders);
731
+ if (!fromSlice || !toSlice) continue;
732
+ if (fromSlice !== toSlice) return rule;
733
+ continue;
734
+ }
735
+ if (from === to) continue;
736
+ return rule;
737
+ }
738
+ return void 0;
739
+ }
740
+
665
741
  // src/kernel/event-bus/observedLayerFlow.ts
666
742
  async function assertObservedLayerFlowAllowed(event, deps) {
667
743
  if (deps.mode === "off" || !deps.architectureProfile) {
@@ -673,9 +749,7 @@ async function assertObservedLayerFlowAllowed(event, deps) {
673
749
  const fromLayer = profile.resolveLayer(source);
674
750
  const toLayer = profile.resolveLayer(event.intent);
675
751
  if (!fromLayer || !toLayer) return;
676
- const blocked = profile.rules.find(
677
- (rule) => !rule.allowed && rule.from === fromLayer && rule.to === toLayer
678
- );
752
+ const blocked = findDeniedEdgeRule(profile.rules, fromLayer, toLayer);
679
753
  if (!blocked) return;
680
754
  const severity = deps.mode;
681
755
  const message = blocked.message ?? `Observed layer violation: "${source}" (${fromLayer}) must not produce "${event.intent}" (${toLayer}).`;
@@ -2325,16 +2399,26 @@ function createAICodeGate(options = {}) {
2325
2399
  }
2326
2400
  }
2327
2401
  for (const specifier of extractModuleSpecifiers(source)) {
2328
- const targetLayer = options.resolveImportLayer?.(specifier.value, filePath);
2329
- if (targetLayer && contextLayer && targetLayer !== contextLayer) {
2330
- const blocked = options.architectureProfile?.rules.find(
2331
- (rule) => !rule.allowed && rule.from === contextLayer && rule.to === targetLayer
2402
+ const targetHit = options.resolveImportTarget?.(specifier.value, filePath) ?? (options.resolveImportLayer ? { layer: options.resolveImportLayer(specifier.value, filePath) } : void 0);
2403
+ const sourceHit = typeof filePath === "string" ? options.resolveImportTarget?.(filePath) ?? (options.resolveImportLayer ? { layer: contextLayer, relPath: void 0 } : void 0) : void 0;
2404
+ const targetLayer = targetHit?.layer;
2405
+ if (targetLayer && contextLayer) {
2406
+ const blocked = findDeniedEdgeRule(
2407
+ options.architectureProfile?.rules,
2408
+ contextLayer,
2409
+ targetLayer,
2410
+ {
2411
+ fromPath: sourceHit?.relPath,
2412
+ toPath: targetHit?.relPath,
2413
+ layers: options.architectureLayers
2414
+ }
2332
2415
  );
2333
2416
  if (blocked) {
2417
+ const peer = Boolean(blocked.peerIsolation);
2334
2418
  violations.push(
2335
2419
  violation(
2336
2420
  "LAYER_IMPORT_VIOLATION",
2337
- blocked.message ?? `Layer "${contextLayer}" must not import "${targetLayer}".`,
2421
+ blocked.message ?? (peer ? `Layer "${contextLayer}" must not import across slices into "${targetLayer}".` : `Layer "${contextLayer}" must not import "${targetLayer}".`),
2338
2422
  {
2339
2423
  line: lineOf(source, specifier.index),
2340
2424
  source: specifier.value,
@@ -2342,13 +2426,16 @@ function createAICodeGate(options = {}) {
2342
2426
  filePath,
2343
2427
  fromLayer: contextLayer,
2344
2428
  toLayer: targetLayer,
2345
- suggestion: "Depend on a port/interface owned by an inner layer instead, or move this code to a layer allowed to make this import.",
2346
- details: { importKind: specifier.kind }
2429
+ suggestion: peer ? "Extract shared code to a shared layer, or coordinate slices via events/ports \u2014 do not import across feature/context slices." : "Depend on a port/interface owned by an inner layer instead, or move this code to a layer allowed to make this import.",
2430
+ details: { importKind: specifier.kind, peerIsolation: peer }
2347
2431
  }
2348
2432
  )
2349
2433
  );
2434
+ continue;
2435
+ }
2436
+ if (targetLayer !== contextLayer) {
2437
+ continue;
2350
2438
  }
2351
- continue;
2352
2439
  }
2353
2440
  if (exemptFromInfraHeuristics) continue;
2354
2441
  if (!hasInfrastructureToken(specifier.value) && !isKnownInfrastructurePackage(specifier.value)) {
@@ -2417,8 +2504,10 @@ function createAICodeGate(options = {}) {
2417
2504
  if (!looksLikeIntentName(literal.value)) continue;
2418
2505
  const targetLayer = options.architectureProfile.resolveLayer(literal.value);
2419
2506
  if (!targetLayer) continue;
2420
- const blocked = options.architectureProfile.rules.find(
2421
- (rule) => !rule.allowed && rule.from === contextLayer && rule.to === targetLayer
2507
+ const blocked = findDeniedEdgeRule(
2508
+ options.architectureProfile.rules,
2509
+ contextLayer,
2510
+ targetLayer
2422
2511
  );
2423
2512
  if (blocked) {
2424
2513
  violations.push(