arkgate 3.6.0 → 3.7.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 (40) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/README.md +4 -5
  3. package/bin/ark-check.mjs +3 -3
  4. package/bin/ark-layer-match.mjs +2 -1
  5. package/bin/ark.mjs +2 -2
  6. package/bin/lib/agent-gates.mjs +2 -0
  7. package/bin/lib/analysis-engine.mjs +6 -6
  8. package/bin/lib/architecture-scan.mjs +66 -13
  9. package/bin/lib/ci-and-commands.mjs +5 -8
  10. package/bin/lib/codex-home.mjs +21 -6
  11. package/bin/lib/design-smells.mjs +67 -14
  12. package/bin/lib/doctor-advisories.mjs +20 -5
  13. package/bin/lib/doctor-plan.mjs +5 -7
  14. package/bin/lib/gate-files.mjs +1 -1
  15. package/bin/lib/hook-templates.mjs +21 -1
  16. package/bin/lib/html-report-advisories.mjs +59 -0
  17. package/bin/lib/install-migrate.mjs +26 -11
  18. package/bin/lib/mcp-adoption.mjs +21 -4
  19. package/bin/lib/parse-health.mjs +74 -0
  20. package/bin/lib/reshape-decisions.mjs +284 -0
  21. package/bin/lib/ts-resolve.mjs +3 -2
  22. package/bin/lib/write-path-capabilities.mjs +6 -0
  23. package/dist/eslint/index.cjs +2 -2
  24. package/dist/eslint/index.js +2 -2
  25. package/dist/index.cjs +7 -7
  26. package/dist/index.d.cts +1 -1
  27. package/dist/index.d.ts +1 -1
  28. package/dist/index.js +6 -6
  29. package/docs/agent-guide.md +34 -3
  30. package/docs/ai-gates.md +21 -18
  31. package/docs/configuration.md +6 -0
  32. package/docs/enthusiast/how-to-agent-gates.md +1 -1
  33. package/docs/package-surface.md +6 -5
  34. package/docs/typescript-support.md +9 -0
  35. package/package.json +2 -1
  36. package/server.json +2 -2
  37. package/templates/skills/ark-autopilot.md +12 -0
  38. package/templates/skills/ark-explore.md +8 -1
  39. package/templates/skills/ark-fix.md +11 -1
  40. package/templates/skills/ark-loop.md +14 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,75 @@ All notable changes to ArkGate (`arkgate`; formerly `ark-runtime-kernel`) are do
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## 3.7.0 — 2026-07-17
8
+
9
+ Phase Y turns field feedback into explicit decision memory, more honest design/parse advisories,
10
+ safer skill-driven edits, and one confirmed purity-bypass closure. **No breaking** CLI, MCP tool,
11
+ analysis IR, or `ark.config.json` changes. **No product-policy gate weakening. No automatic
12
+ reshape or codemod.**
13
+
14
+ ### Added
15
+
16
+ - **Recorded reshape decisions (Y01):** `.ark/reshape-decisions.json` can explicitly accept,
17
+ defer, or reject a physical-cohesion pilot with a required reason and optional `reviewBy`.
18
+ Current deferred/rejected decisions suppress only repeated pilot pressure; the underlying
19
+ facts remain visible. Stale, expired, malformed, duplicate, or oversized records suppress
20
+ nothing, and doctor/HTML surfaces render the lifecycle.
21
+ - **Hollow-persistence advisory (Y02):** the existing `handler-in-persistence` smell now sees
22
+ static framework HTTP imports/re-exports, route definitions, and handler shapes inside
23
+ Persistence-role modules. It stays advisory and judgment-only, with a bounded and explicit
24
+ inspection envelope.
25
+ - **Parse-health honesty (Y03):** doctor JSON/human output and the HTML report expose governed
26
+ files with parser diagnostics through additive `parseHealth` totals and a deterministic,
27
+ overflow-aware file list. The existing AST is reused; the verdict and exit code are unchanged.
28
+ - **Exact `process` module dual (Y08):** `forbiddenGlobals: ["process"]` now owns exact value
29
+ imports of `process` and `node:process` across CLI cold/warm cache, pure IR, atomic preflight,
30
+ AICodeGate/MCP, and ESLint. It emits one `FORBIDDEN_GLOBAL` voice; type-only forms within the
31
+ documented TypeScript envelope, subpaths, and `child_process` remain excluded.
32
+
33
+ ### Changed
34
+
35
+ - **Mechanical-edit hygiene (Y04):** `ark-fix`, `ark-autopilot`, and `ark-loop` now require
36
+ injected headers to merge into an existing doc block, typed `defineRoute<…>` calls to retain
37
+ their generics/shape, and convention-only placeholder modules to remain uncreated. A
38
+ deterministic eval guards all three outcomes.
39
+ - **Cycle budgets (Y05):** package and Linux hook/doctor p95 ceilings were re-measured once from
40
+ clean 3.6.1 candidates with evidence-backed headroom, preserving fixed release guardrails.
41
+ - **Confidence budget:** Y08 adds multi-form ESLint regressions while the full-suite branch floor
42
+ is recalibrated from 85% to 84.5%; statement/function/line floors and mutation gates are
43
+ unchanged.
44
+
45
+ ### Fixed
46
+
47
+ - **Portable peer isolation:** slice identities are case-normalized, so mixed-case paths do not
48
+ produce a false cross-slice violation on case-insensitive filesystems. Repositories that differ
49
+ only by path case intentionally share the portable identity.
50
+ - **Convergence remediation direction:** an unplanned removed dependency now tells the user to
51
+ restore it; an unplanned added dependency still tells the user to remove it.
52
+
53
+ ## 3.6.1 — 2026-07-17
54
+
55
+ Codex project-scoped MCP fix. **No breaking** CLI, MCP tool, or `ark.config.json` changes.
56
+ **No gate weaken.**
57
+
58
+ ### Fixed
59
+
60
+ - **Codex MCP follows the active repository:** `--install-agent-gates --tools codex` now writes
61
+ `.codex/config.toml` with relative project paths. Each repository owns its primary `ark`
62
+ binding, so `ark upgrade` no longer leaves Codex resolving `ark://manifest` through another
63
+ permanent project registered in the global home config.
64
+ - **Doctor effective-config honesty:** Codex advisory-MCP capability and adoption checks prefer
65
+ a valid project `.codex/config.toml`; an unrelated `$CODEX_HOME` primary no longer produces a
66
+ false `write-path-none` or `codex-home-multi-project` warning.
67
+ - **Ephemeral worktree cleanup:** roots under `.claude/worktrees`, `.codex/worktrees`, or
68
+ `.grok/worktrees` are treated as temporary instead of permanent global MCP owners.
69
+
70
+ ### Changed
71
+
72
+ - `$CODEX_HOME/config.toml` remains an explicit compatibility fallback via `--codex-home`.
73
+ Normal `--tools codex` installs no longer mutate the global MCP registry, and skills-only
74
+ home refreshes cannot accidentally rebind its primary server.
75
+
7
76
  ## 3.6.0 — 2026-07-17
8
77
 
9
78
  Phase X closes: the doctor learns to see **physical shape** and agents get a governed way to
package/README.md CHANGED
@@ -16,10 +16,9 @@ and makes sure a “green” check means something real.
16
16
 
17
17
  </div>
18
18
 
19
- > **ArkGate 3.4.0** is current stable: opt-in capability walls (`pure: true` or
20
- > `capabilities.deny` per layer, enforced on every adapter), the advisory ambient-state sensor,
21
- > and the measured pre-tool path — completing 3.3's evidence slice.
22
- > [Release notes](docs/releases/3.4.0.md).
19
+ > **ArkGate 3.7.0** is current stable: Phase Y adds explicit reshape decisions, hollow-
20
+ > persistence and parse-health advisories, safer mechanical edits, and exact `process` module-
21
+ > dual enforcement. [Release notes](docs/releases/3.7.0.md).
23
22
 
24
23
  ---
25
24
 
@@ -383,7 +382,7 @@ for real systems. Details: [production-hardening.md](https://github.com/pedrokni
383
382
  | Security reporting | [SECURITY.md](SECURITY.md) |
384
383
  | Demos | [docs/demos/](docs/demos/) |
385
384
  | Examples | [examples/](examples/README.md) |
386
- | Latest release (3.1.0) | [release notes](docs/releases/3.1.0.md) · [3.0.0 baseline](docs/releases/3.0.0.md) |
385
+ | Latest release (3.7.0) | [release notes](docs/releases/3.7.0.md) · [3.0.0 baseline](docs/releases/3.0.0.md) |
387
386
  | Roadmap and decisions | [ROADMAP.md](ROADMAP.md) · [ADRs](docs/adr/) · [Changelog](CHANGELOG.md) |
388
387
 
389
388
  ---
package/bin/ark-check.mjs CHANGED
@@ -1133,7 +1133,7 @@ async function main() {
1133
1133
  );
1134
1134
  }
1135
1135
 
1136
- const { violations, warnings, safety } = runArchitectureScan({
1136
+ const { violations, warnings, safety, parseHealth } = runArchitectureScan({
1137
1137
  root,
1138
1138
  config,
1139
1139
  manifest,
@@ -1148,7 +1148,7 @@ async function main() {
1148
1148
  configPath: path.isAbsolute(args.config) ? args.config : path.join(root, args.config),
1149
1149
  configMissing: !fs.existsSync(path.isAbsolute(args.config) ? args.config : path.join(root, args.config)),
1150
1150
  safety,
1151
- ts,
1151
+ ts, parseHealth,
1152
1152
  });
1153
1153
  return;
1154
1154
  }
@@ -1320,7 +1320,7 @@ async function main() {
1320
1320
  originJustCreated: !existingOrigin,
1321
1321
  adoption: adoptionForReport,
1322
1322
  designDepth,
1323
- advisories: computeDoctorAdvisories(root, config, coverage, rules, files, ts),
1323
+ advisories: computeDoctorAdvisories(root, config, coverage, rules, files, ts, parseHealth),
1324
1324
  };
1325
1325
  const html = args.beginner
1326
1326
  ? renderBeginnerHtmlReport(reportPayload)
@@ -138,6 +138,7 @@ export function layerForRelativePath(relPath, layers) {
138
138
  /**
139
139
  * Extract the slice id under a known folder name.
140
140
  * Includes the parent folder so `features/auth` ≠ `modules/auth`.
141
+ * Identity is case-normalized for portable results across filesystems.
141
142
  * `src/features/auth/api.ts` + folders `["features"]` → `"features/auth"`.
142
143
  */
143
144
  export function sliceIdForPath(relPath, sliceFolders) {
@@ -149,7 +150,7 @@ export function sliceIdForPath(relPath, sliceFolders) {
149
150
  const folders = new Set(sliceFolders.map((s) => String(s).toLowerCase()));
150
151
  for (let i = 0; i < parts.length - 1; i += 1) {
151
152
  if (folders.has(parts[i].toLowerCase())) {
152
- return `${parts[i]}/${parts[i + 1]}`;
153
+ return `${parts[i].toLowerCase()}/${parts[i + 1].toLowerCase()}`;
153
154
  }
154
155
  }
155
156
  return undefined;
package/bin/ark.mjs CHANGED
@@ -227,8 +227,8 @@ async function upgrade(args) {
227
227
  if (status !== 0) return status;
228
228
 
229
229
  // Codex home skill catalog is $CODEX_HOME/skills/<name>/SKILL.md (repo uses .agents/skills/).
230
- // Refresh home when a Codex home exists. --force rewrites temp/upgrade MCP roots to this
231
- // project + arkgate-mcp. Non-fatal: a permission error (e.g. sandbox) shouldn't fail upgrade.
230
+ // Refresh home skills when a Codex home exists. Project MCP is installed above in
231
+ // .codex/config.toml. Non-fatal: a permission error (e.g. sandbox) shouldn't fail upgrade.
232
232
  const codexHomeBase = process.env.CODEX_HOME || path.join(os.homedir(), '.codex');
233
233
  if (fs.existsSync(codexHomeBase)) {
234
234
  console.log(`\n Refreshing Codex home (${codexHomeBase})…`);
@@ -8,6 +8,7 @@ export {
8
8
  codexArkBlockNeedsRewrite,
9
9
  codexConfigPath,
10
10
  codexPrimaryTable,
11
+ codexProjectMcpIsValid,
11
12
  codexProjectSlug,
12
13
  codexPromptsDir,
13
14
  codexSkillsDir,
@@ -23,6 +24,7 @@ export {
23
24
  export {
24
25
  PREFERRED_MCP_BIN,
25
26
  claudeSettings,
27
+ codexProjectConfig,
26
28
  grokHooks,
27
29
  grokProjectConfig,
28
30
  } from './hook-templates.mjs';
@@ -1,10 +1,10 @@
1
1
  // GENERATED from src/kernel/analysis.ts by scripts/generate-analysis-engine.mjs.
2
2
  // Do not edit this file directly.
3
- function $(e){let t=2166136261;for(let n=0;n<e.length;n+=1)t^=e.charCodeAt(n),t=Math.imul(t,16777619);return`fnv1a-${(t>>>0).toString(16).padStart(8,"0")}`}function x(e){if(e===null||typeof e!="object")return JSON.stringify(e);if(Array.isArray(e))return`[${e.map(x).join(",")}]`;let t=e;return`{${Object.keys(t).sort().map(n=>`${JSON.stringify(n)}:${x(t[n])}`).join(",")}}`}var Ae=new Map;function be(e){return/[.*+?^${}()|[\]\\]/.test(e)?`\\${e}`:e}function X(e){let t="";for(let n=0;n<e.length;n+=1){let r=e[n];if(r==="\\"&&n+1<e.length){let i=e[n+1];if("*?{}[],".includes(i)||i==="\\"){t+="\\"+i,n+=1;continue}t+="/";continue}t+=r}return t}function He(e){let t=0;for(let n=0;n<e.length;n+=1){let r=e[n];if(r==="\\"){n+=1;continue}if(r==="{")t+=1;else if(r==="}"&&(t-=1,t<0))return!1}return t===0}function D(e){let t=Ae.get(e);if(t)return t;let n=X(e),r=He(n),i="",a=0;for(let l=0;l<n.length;l+=1){let p=n[l];p==="\\"&&l+1<n.length?(i+=be(n[l+1]),l+=1):p==="*"?n[l+1]==="*"?n[l+2]==="/"?(i+="(?:.*/)?",l+=2):(i+=".*",l+=1):i+="[^/]*":p==="?"?i+="[^/]":p==="{"&&r?(i+="(?:",a+=1):p==="}"&&r&&a>0?(i+=")",a-=1):p===","&&r&&a>0?i+="|":i+=be(p)}let o=new RegExp(`^${i}$`);return Ae.set(e,o),o}function Q(e){let t=X(String(e)),r=t.split("*")[0].split("/").filter(Boolean).length,i=t.replace(/\*/g,"").length;return r*1e4+i}function _(e,t){let n=String(e).split(/[/\\]/).join("/"),r,i=-1;for(let a of t??[])if(!(a.exclude??[]).some(o=>D(o).test(n))){for(let o of a.patterns??[])if(D(o).test(n)){let l=Q(o);l>i&&(i=l,r=a.name)}}return r}function Ce(e,t){if(!t?.length)return;let n=String(e).split(/[/\\]/).filter(Boolean),r=new Set(t.map(i=>String(i).toLowerCase()));for(let i=0;i<n.length-1;i+=1)if(r.has(n[i].toLowerCase()))return`${n[i]}/${n[i+1]}`}function Ge(e){let t=new Set;for(let n of e??[]){let i=X(String(n)).split("/").filter(Boolean);for(let a=0;a<i.length;a+=1){let o=i[a];if((o==="**"||o==="*")&&a>0){let l=i[a-1];l&&!l.includes("*")&&!l.includes("{")&&!l.includes("}")&&t.add(l)}}}return[...t]}function Ue(e,t,n){if(Array.isArray(e.sliceFolders)&&e.sliceFolders.length>0)return e.sliceFolders.filter(i=>typeof i=="string"&&i.length>0);let r=(n??[]).find(i=>i.name===t);return Ge(r?.patterns)}function G(e,t,n,r){for(let i of e??[])if(!(i.from!==t||i.to!==n)&&i.allowed===!1){if(i.peerIsolation){let a=r?.fromPath,o=r?.toPath;if(!a||!o)continue;let l=Ue(i,t,r?.layers);if(l.length===0)continue;let p=Ce(a,l),u=Ce(o,l);if(!p||!u)continue;if(p!==u)return i;continue}if(t!==n)return i}}var ee="1.0";var U=class extends Error{issues;source;constructor(t,n){super(`Invalid architecture change map (${t}):
3
+ function $(e){let t=2166136261;for(let n=0;n<e.length;n+=1)t^=e.charCodeAt(n),t=Math.imul(t,16777619);return`fnv1a-${(t>>>0).toString(16).padStart(8,"0")}`}function x(e){if(e===null||typeof e!="object")return JSON.stringify(e);if(Array.isArray(e))return`[${e.map(x).join(",")}]`;let t=e;return`{${Object.keys(t).sort().map(n=>`${JSON.stringify(n)}:${x(t[n])}`).join(",")}}`}var Ce=new Map;function Ie(e){return/[.*+?^${}()|[\]\\]/.test(e)?`\\${e}`:e}function X(e){let t="";for(let n=0;n<e.length;n+=1){let r=e[n];if(r==="\\"&&n+1<e.length){let i=e[n+1];if("*?{}[],".includes(i)||i==="\\"){t+="\\"+i,n+=1;continue}t+="/";continue}t+=r}return t}function Ue(e){let t=0;for(let n=0;n<e.length;n+=1){let r=e[n];if(r==="\\"){n+=1;continue}if(r==="{")t+=1;else if(r==="}"&&(t-=1,t<0))return!1}return t===0}function D(e){let t=Ce.get(e);if(t)return t;let n=X(e),r=Ue(n),i="",a=0;for(let d=0;d<n.length;d+=1){let l=n[d];l==="\\"&&d+1<n.length?(i+=Ie(n[d+1]),d+=1):l==="*"?n[d+1]==="*"?n[d+2]==="/"?(i+="(?:.*/)?",d+=2):(i+=".*",d+=1):i+="[^/]*":l==="?"?i+="[^/]":l==="{"&&r?(i+="(?:",a+=1):l==="}"&&r&&a>0?(i+=")",a-=1):l===","&&r&&a>0?i+="|":i+=Ie(l)}let o=new RegExp(`^${i}$`);return Ce.set(e,o),o}function Q(e){let t=X(String(e)),r=t.split("*")[0].split("/").filter(Boolean).length,i=t.replace(/\*/g,"").length;return r*1e4+i}function _(e,t){let n=String(e).split(/[/\\]/).join("/"),r,i=-1;for(let a of t??[])if(!(a.exclude??[]).some(o=>D(o).test(n))){for(let o of a.patterns??[])if(D(o).test(n)){let d=Q(o);d>i&&(i=d,r=a.name)}}return r}function ke(e,t){if(!t?.length)return;let n=String(e).split(/[/\\]/).filter(Boolean),r=new Set(t.map(i=>String(i).toLowerCase()));for(let i=0;i<n.length-1;i+=1)if(r.has(n[i].toLowerCase()))return`${n[i].toLowerCase()}/${n[i+1].toLowerCase()}`}function Be(e){let t=new Set;for(let n of e??[]){let i=X(String(n)).split("/").filter(Boolean);for(let a=0;a<i.length;a+=1){let o=i[a];if((o==="**"||o==="*")&&a>0){let d=i[a-1];d&&!d.includes("*")&&!d.includes("{")&&!d.includes("}")&&t.add(d)}}}return[...t]}function ze(e,t,n){if(Array.isArray(e.sliceFolders)&&e.sliceFolders.length>0)return e.sliceFolders.filter(i=>typeof i=="string"&&i.length>0);let r=(n??[]).find(i=>i.name===t);return Be(r?.patterns)}function G(e,t,n,r){for(let i of e??[])if(!(i.from!==t||i.to!==n)&&i.allowed===!1){if(i.peerIsolation){let a=r?.fromPath,o=r?.toPath;if(!a||!o)continue;let d=ze(i,t,r?.layers);if(d.length===0)continue;let l=ke(a,d),u=ke(o,d);if(!l||!u)continue;if(l!==u)return i;continue}if(t!==n)return i}}var ee="1.0";var U=class extends Error{issues;source;constructor(t,n){super(`Invalid architecture change map (${t}):
4
4
  ${n.map(r=>`- ${r.path}: ${r.message}`).join(`
5
- `)}`),this.name="ArchitectureChangeMapValidationError",this.source=t,this.issues=n}};function te(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function ne(e,t,n,r){for(let i of Object.keys(e))t.includes(i)||r.push({path:`${n}.${i}`,message:"unknown field"})}function P(e,t,n,r){let i=e[t];if(typeof i=="string"&&i.length>0)return i;r.push({path:`${n}.${t}`,message:"must be a non-empty string"})}function Be(e){let t=e.replace(/\\/g,"/");if(!t||t.startsWith("/")||/^[A-Za-z]:\//.test(t)||t.includes("\0"))return;let n=[];for(let i of t.split("/"))if(!(!i||i==="."))if(i===".."){if(n.length===0)return;n.pop()}else n.push(i);let r=n.join("/");return r&&r===t?r:void 0}function ze(e,t,n="architecture change map"){let r=[];if(!te(e))throw new U(n,[{path:"$",message:"must be an object"}]);ne(e,["$schema","schemaVersion","files","dependencies"],"$",r);let i=P(e,"$schema","$",r),a=P(e,"schemaVersion","$",r);a&&a!==ee&&r.push({path:"$.schemaVersion",message:`unsupported version ${JSON.stringify(a)}; expected ${ee}`});let o=new Set(t.layers.map(d=>d.name)),l=[],p=new Set;!Array.isArray(e.files)||e.files.length===0?r.push({path:"$.files",message:"must be a non-empty array"}):e.files.forEach((d,f)=>{let y=`$.files[${f}]`;if(!te(d)){r.push({path:y,message:"must be an object"});return}ne(d,["path","operation","layer"],y,r);let h=P(d,"path",y,r),A=P(d,"operation",y,r),C=P(d,"layer",y,r),k=h?Be(h):void 0;h&&!k&&r.push({path:`${y}.path`,message:"must be a canonical project-relative path"}),A&&!["create","update","delete"].includes(A)&&r.push({path:`${y}.operation`,message:"must be create, update, or delete"}),C&&!o.has(C)&&r.push({path:`${y}.layer`,message:`references unknown layer ${JSON.stringify(C)}`}),k&&p.has(k)&&r.push({path:`${y}.path`,message:`duplicates planned path ${k}`}),k&&p.add(k);let Z=k?_(k,t.layers):void 0;k&&C&&o.has(C)&&Z!==C&&r.push({path:`${y}.layer`,message:Z?`${k} resolves to ${Z}, not ${C}`:`${k} is not assigned to an architecture layer`}),k&&A&&["create","update","delete"].includes(A)&&C&&l.push({path:k,operation:A,layer:C})});let u=[],m=new Map(l.map(d=>[d.path,d.operation])),g=new Set,c=e.dependencies??[];if(Array.isArray(c)?c.forEach((d,f)=>{let y=`$.dependencies[${f}]`;if(!te(d)){r.push({path:y,message:"must be an object"});return}ne(d,["from","to"],y,r);let h=P(d,"from",y,r),A=P(d,"to",y,r);h&&!p.has(h)&&r.push({path:`${y}.from`,message:`must reference a planned file path: ${h}`}),A&&!p.has(A)&&r.push({path:`${y}.to`,message:`must reference a planned file path: ${A}`}),h&&m.get(h)==="delete"&&r.push({path:`${y}.from`,message:`cannot depend from deleted file ${h}`}),A&&m.get(A)==="delete"&&r.push({path:`${y}.to`,message:`cannot depend on deleted file ${A}`}),h&&A&&h===A&&r.push({path:y,message:"must not declare a self dependency"});let C=h&&A?`${h}\0${A}`:void 0;C&&g.has(C)&&r.push({path:y,message:`duplicates dependency ${h} -> ${A}`}),C&&g.add(C),h&&A&&u.push({from:h,to:A})}):r.push({path:"$.dependencies",message:"must be an array"}),r.length>0)throw new U(n,r);let s={$schema:i,schemaVersion:ee,files:l.sort((d,f)=>d.path.localeCompare(f.path)),dependencies:u.sort((d,f)=>d.from.localeCompare(f.from)||d.to.localeCompare(f.to))};return{map:s,hash:$(x(s))}}function w(e){return`${e.from}->${e.to}`}function F(e,t,n,r="dependency"){return{id:`${e}:${r}:${w(t)}`,classification:e,subject:"dependency",from:t.from,to:t.to,message:n,...e==="missing"?{nextAction:`Add the planned dependency ${w(t)} to the candidate, then preflight again.`}:e==="contradictory"?{nextAction:`Replace the reverse dependency with ${w(t)}, then preflight again.`}:e==="unplanned"?{nextAction:`Remove the unplanned dependency ${w(t)} from the candidate, then preflight again.`}:{}}}function re(e){let t=[],n=new Map(e.changeMap.map.files.map(c=>[c.path,c])),r=new Map(e.changes.map(c=>[c.path,c])),i=new Map(e.changeMap.map.dependencies.map(c=>[w(c),c])),a=new Map(e.baseDependencies.map(c=>[w(c),c])),o=new Map(e.candidateDependencies.map(c=>[w(c),c]));for(let c of[...n.values()].sort((s,d)=>s.path.localeCompare(d.path))){let s=r.get(c.path);s?s.operation!==c.operation?t.push({id:`contradictory:file:${c.path}`,classification:"contradictory",subject:"file",path:c.path,expectedOperation:c.operation,actualOperation:s.operation,message:`${c.path} was planned as ${c.operation} but the actual operation is ${s.operation}.`,nextAction:`Change ${c.path} to the planned ${c.operation} operation, then preflight again.`}):t.push({id:`satisfied:file:${c.path}`,classification:"satisfied",subject:"file",path:c.path,expectedOperation:c.operation,actualOperation:s.operation,message:`${c.path} matches the planned ${c.operation} operation.`}):t.push({id:`missing:file:${c.path}`,classification:"missing",subject:"file",path:c.path,expectedOperation:c.operation,message:`${c.path} was planned as ${c.operation} but is absent from the actual change.`,nextAction:`${c.operation[0].toUpperCase()}${c.operation.slice(1)} ${c.path} in the complete change set, then preflight again.`})}for(let c of[...r.values()].sort((s,d)=>s.path.localeCompare(d.path)))n.has(c.path)||t.push({id:`unplanned:file:${c.path}`,classification:"unplanned",subject:"file",path:c.path,actualOperation:c.operation,message:`${c.path} has an unplanned ${c.operation} operation.`,nextAction:`Remove ${c.path} from the change set, then preflight again.`});let l=new Set;for(let c of[...i.values()].sort((s,d)=>w(s).localeCompare(w(d)))){if(o.has(w(c))){t.push(F("satisfied",c,`${c.from} -> ${c.to} exists in the candidate architecture.`));continue}let s={from:c.to,to:c.from};o.has(w(s))?(l.add(w(s)),t.push(F("contradictory",c,`${c.from} -> ${c.to} was planned, but the candidate contains the reverse edge.`))):t.push(F("missing",c,`${c.from} -> ${c.to} is absent from the candidate architecture.`))}let p=new Set([...n.keys(),...r.keys()]);for(let[c,s]of[...o].sort(([d],[f])=>d.localeCompare(f)))a.has(c)||i.has(c)||l.has(c)||!p.has(s.from)&&!p.has(s.to)||t.push({...F("unplanned",s,`${s.from} -> ${s.to} was added without a matching planned dependency.`,"dependency-added"),actualOperation:"added"});let u=new Set(e.changeMap.map.files.filter(c=>c.operation==="delete").map(c=>c.path));for(let[c,s]of[...a].sort(([d],[f])=>d.localeCompare(f)))o.has(c)||u.has(s.from)||u.has(s.to)||!p.has(s.from)&&!p.has(s.to)||t.push({...F("unplanned",s,`${s.from} -> ${s.to} was removed without a planned file deletion.`,"dependency-removed"),actualOperation:"removed"});let m={satisfied:0,missing:1,contradictory:2,unplanned:3};t.sort((c,s)=>m[c.classification]-m[s.classification]||(c.subject===s.subject?0:c.subject==="file"?-1:1)||c.id.localeCompare(s.id));let g={satisfied:t.filter(c=>c.classification==="satisfied").length,missing:t.filter(c=>c.classification==="missing").length,contradictory:t.filter(c=>c.classification==="contradictory").length,unplanned:t.filter(c=>c.classification==="unplanned").length};return{schemaVersion:"1.0",readOnly:!0,changeMapHash:e.changeMap.hash,structurallyConverged:g.missing===0&&g.contradictory===0&&g.unplanned===0,behavioralCompletion:"not-evaluated",summary:g,findings:t}}function M(e,t){return t&&e.isStringLiteralLike(t)?t.text:void 0}function we(e,t){return e.getLineAndCharacterOfPosition(t.getStart(e)).line+1}function Ie(e,t){if(e.isImportDeclaration(t)){let n=t.importClause;if(!n)return!1;if(n.isTypeOnly)return!0;let r=n.namedBindings;return!!(r&&e.isNamedImports(r)&&r.elements.length>0&&r.elements.every(i=>i.isTypeOnly))}if(e.isExportDeclaration(t)){if(t.isTypeOnly)return!0;let n=t.exportClause;return!!(n&&e.isNamedExports(n)&&n.elements.length>0&&n.elements.every(r=>r.isTypeOnly))}return!1}function Se(e,t){let n={noLib:!0,noResolve:!0,target:e.ScriptTarget.Latest},r=e.createCompilerHost(n,!0);return r.getSourceFile=i=>i===t.fileName?t:void 0,r.fileExists=i=>i===t.fileName,r.readFile=i=>i===t.fileName?t.text:void 0,e.createProgram([t.fileName],n,r).getTypeChecker()}function ie(e,t){try{return e.getSymbolAtLocation(t)}catch{return}}function ae(e,t,n,r){let i=r.parent&&e.isShorthandPropertyAssignment(r.parent)&&r.parent.name===r,a;try{a=i?t.getShorthandAssignmentValueSymbol(r.parent):ie(t,r)}catch{a=void 0}return!!a?.declarations?.some(o=>o.getSourceFile().fileName===n.fileName)}function se(e,t){let n,r=[],i=(o,l,p,u=!1)=>r.push({specifier:p,kind:l,line:we(t,o),typeOnly:u,unresolved:p===void 0,node:o}),a=o=>{if(e.isImportDeclaration(o))i(o,"import",M(e,o.moduleSpecifier),Ie(e,o));else if(e.isExportDeclaration(o)&&o.moduleSpecifier)i(o,"export",M(e,o.moduleSpecifier),Ie(e,o));else if(e.isImportEqualsDeclaration(o)&&e.isExternalModuleReference(o.moduleReference))i(o,"require",M(e,o.moduleReference.expression));else if(e.isCallExpression(o)){let l=o.expression.kind===e.SyntaxKind.ImportKeyword,u=e.isIdentifier(o.expression)&&o.expression.text==="require"&&!ae(e,n??(n=Se(e,t)),t,o.expression);(l||u)&&i(o,u?"require":"dynamic-import",M(e,o.arguments[0]))}e.forEachChild(o,a)};return a(t),r}function qe(e,t){let n=[],r=t;for(;e.isPropertyAccessExpression(r)||e.isElementAccessExpression(r);){if(e.isPropertyAccessExpression(r))n.unshift(r.name.text);else{let i=M(e,r.argumentExpression);if(i===void 0)return;n.unshift(i)}r=r.expression}if(e.isIdentifier(r))return n.unshift(r.text),{root:r,segments:n}}function Ke(e,t){let n=t.parent;return e.isPropertyAccessExpression(n)||e.isElementAccessExpression(n)?!1:e.isExpressionNode(t)&&!e.isInTypeQuery(t)||e.isShorthandPropertyAssignment(n)&&n.name===t}function ke(e,t){let n=t[0]==="globalThis"?t.slice(1):t;for(let r=n.length;r>=1;r-=1){let i=n.slice(0,r).join(".");if(e.has(i))return i}}function oe(e,t,n){if(n.length===0)return[];let r=new Set(n),i=Se(e,t),a=new Map,o=new Set;for(let c of t.statements)if(e.isVariableStatement(c))for(let s of c.declarationList.declarations)e.isIdentifier(s.name)&&o.add(s.name.text);let l=c=>{let s=qe(e,c);if(!s)return;let d=ie(i,s.root),f=d?a.get(d):void 0;return f?[...f,...s.segments.slice(1)]:ae(e,i,t,s.root)||o.has(s.root.text)?void 0:s.segments};for(let c of t.statements)if(e.isVariableStatement(c))for(let s of c.declarationList.declarations){if(!s.initializer||!e.isIdentifier(s.name))continue;let d=l(s.initializer),f=ie(i,s.name);!d||!f||a.set(f,d)}let p=[],u=new Set,m=(c,s)=>{let d=we(t,s),f=`${c}:${s.getStart(t)}`;u.has(f)||(u.add(f),p.push({name:c,line:d,node:s}))},g=c=>{let s=c.parent&&(e.isPropertyAccessExpression(c.parent)||e.isElementAccessExpression(c.parent))&&c.parent.expression===c;if((e.isPropertyAccessExpression(c)||e.isElementAccessExpression(c))&&!s){let d=l(c),f=d?ke(r,d):void 0;f&&m(f,c)}else e.isIdentifier(c)&&r.has(c.text)&&Ke(e,c)&&!ae(e,i,t,c)&&m(c.text,c);if(e.isVariableDeclaration(c)&&e.isObjectBindingPattern(c.name)&&c.initializer){let d=l(c.initializer);if(d)for(let f of c.name.elements){if(!e.isIdentifier(f.name))continue;let y=f.propertyName?M(e,f.propertyName)??f.propertyName.text:f.name.text,h=ke(r,[...d,y]);h&&m(h,c.initializer)}}e.forEachChild(c,g)};return g(t),p}var ce={RAW_EVENT_PUBLISH:"Publish through a registered intent creator; raw event objects or intent strings bypass Ark contracts and tooling.",PUBLISH_MISSING_SOURCE:"Strict Ark publish calls must include metadata.source."};function xe(e){return/^(Domain|Application|Adapter|Workflow|Job|Presentation|Reporting|Metadata|Security|Audit|Observability|Kernel)\.[A-Za-z0-9_.]+$/.test(e)}function Ye(e){if(!e.publishCall)return[];let t=[];return(e.rawIntentName!==void 0&&xe(e.rawIntentName)||e.objectHasIntent)&&t.push({ruleId:"RAW_EVENT_PUBLISH",message:ce.RAW_EVENT_PUBLISH}),e.arkPublishCandidate&&!e.hasSource&&t.push({ruleId:"PUBLISH_MISSING_SOURCE",message:ce.PUBLISH_MISSING_SOURCE}),t}var pe=Object.freeze(["network","filesystem","clock","randomness","environment","process","persistence"]),B=Object.freeze({fetch:"network",XMLHttpRequest:"network",Date:"clock","Date.now":"clock","Math.random":"randomness","process.env":"environment",process:"process"}),de=Object.freeze(Object.keys(B).sort()),le=Object.freeze({fs:"filesystem","node:fs":"filesystem","fs/promises":"filesystem","node:fs/promises":"filesystem","fs-extra":"filesystem","graceful-fs":"filesystem",memfs:"filesystem",chokidar:"filesystem",http:"network",https:"network",http2:"network",net:"network",tls:"network",dgram:"network",dns:"network","node:http":"network","node:https":"network","node:http2":"network","node:net":"network","node:tls":"network","node:dgram":"network","node:dns":"network",axios:"network",undici:"network","node-fetch":"network",got:"network",ky:"network",superagent:"network",ws:"network",process:"process","node:process":"process",child_process:"process","node:child_process":"process","@prisma/client":"persistence",prisma:"persistence",pg:"persistence",mysql:"persistence",mysql2:"persistence",mongodb:"persistence",mongoose:"persistence",sqlite3:"persistence","better-sqlite3":"persistence",redis:"persistence",ioredis:"persistence",typeorm:"persistence",knex:"persistence","drizzle-orm":"persistence",sequelize:"persistence",kysely:"persistence","@supabase/supabase-js":"persistence"});function j(e){if(!e||e.startsWith(".")||e.startsWith("/"))return null;let t=le[e];if(t)return t;let n=e.indexOf("/");if(n<0)return null;let r=e.slice(0,n),i=le[r];if(i)return i;let a=e.indexOf("/",n+1);return a<0?null:le[e.slice(0,a)]??null}function V(e){let t=e.split(".");for(let n=t.length;n>=1;n-=1){let r=t.slice(0,n).join("."),i=B[r];if(i)return i}return null}function z(e){if(e?.pure===!0)return[...pe].sort();let n=(e?.capabilities?.deny??[]).filter(r=>pe.includes(r));return[...new Set(n)].sort()}function We(e,t){if(t.length===0)return!1;let n=new Set(t),r=e.split(".");for(let i=r.length;i>=1;i-=1)if(n.has(r.slice(0,i).join(".")))return!0;return!1}function q(e){let t=new Set,n=new Set,r=Object.keys(B);for(let i of e?.forbiddenGlobals??[]){let a=r.filter(o=>o===i||o.startsWith(`${i}.`));if(a.length===0)n.add(i);else for(let o of a)t.add(`ambient:${o}`)}for(let i of z(e)){t.add(`import:${i}`);for(let a of r)V(a)===i&&t.add(`ambient:${a}`)}return{atoms:[...t].sort(),rawGlobals:[...n].sort()}}function Je(e){let t=new Set,n=V(e);n&&t.add(n);for(let[r,i]of Object.entries(B))(r===e||r.startsWith(`${e}.`))&&t.add(i);return[...t].sort()}function Ze(e,t){let n=[];for(let r of se(e,t)){if(r.typeOnly||!r.specifier)continue;let i=j(r.specifier);i&&n.push({capability:i,symbol:r.specifier,line:r.line,source:"import-based"})}for(let r of oe(e,t,de)){let i=V(r.name);i&&n.push({capability:i,symbol:r.name,line:r.line,source:"ambient-global"})}return n.sort((r,i)=>r.line-i.line||r.capability.localeCompare(i.capability)||r.symbol.localeCompare(i.symbol))}var ue="https://unpkg.com/arkgate@2/schemas/ark.config.schema.json",Ee=["DomainModel","ApplicationOrchestration","PersistenceAdapters","IntegrationAdapters","WorkflowSagaEngine","BackgroundJobsScheduling","PresentationAdapters","ReportingReadModels","ExtensibilityMetadata","SecurityAuditObservability","Kernel"],Xe=new Set(["PresentationAdapters->ApplicationOrchestration","ApplicationOrchestration->DomainModel","WorkflowSagaEngine->ApplicationOrchestration","WorkflowSagaEngine->DomainModel","BackgroundJobsScheduling->ApplicationOrchestration"]);function Qe(){let e=[];for(let t of Ee)for(let n of Ee)t===n||Xe.has(`${t}->${n}`)||e.push({from:t,to:n,allowed:!1});return e}var Oe=Qe();var O={type:"array",items:{type:"string",minLength:1},uniqueItems:!0},$e={$schema:"https://json-schema.org/draft/2020-12/schema",$id:ue,title:"ArkGate architecture contract",description:"Versioned contract consumed identically by ArkGate CLI, MCP, and ESLint surfaces.",type:"object",additionalProperties:!1,required:["$schema","schemaVersion","include","layers","rules"],properties:{$schema:{type:"string",minLength:1,default:ue,description:"Editor-facing URL or local path for this JSON Schema."},schemaVersion:{type:"string",const:"1.0",default:"1.0"},name:{type:"string",minLength:1},include:{...O,minItems:1,default:["src"]},exclude:{...O,default:[]},excludeGenerated:{type:"boolean",default:!0},frameworkOverlay:{type:"string",minLength:1},layers:{type:"array",default:[],items:{$ref:"#/$defs/layer"}},rules:{type:"array",default:Oe,items:{$ref:"#/$defs/rule"}},cyclePolicy:{type:"string",enum:["strict","soft","framework-soft","off"],default:"strict"},dynamicImportAllowlist:{...O,default:[]},safety:{$ref:"#/$defs/safety",default:{maxTsSuppressions:0,maxAnyCasts:0,allowInMemory:!1,allowDisabledPeerIsolation:!1}}},$defs:{layer:{type:"object",additionalProperties:!1,required:["name","patterns"],properties:{name:{type:"string",minLength:1},patterns:{...O,minItems:1},exclude:O,intentPrefixes:O,description:{type:"string",minLength:1},forbiddenGlobals:O,capabilities:{type:"object",additionalProperties:!1,properties:{deny:{type:"array",uniqueItems:!0,items:{type:"string",enum:["network","filesystem","clock","randomness","environment","process","persistence"]}}}},pure:{type:"boolean"},mayImportInfrastructure:{type:"boolean"},optional:{type:"boolean"}}},rule:{type:"object",additionalProperties:!1,required:["from","to","allowed"],properties:{from:{type:"string",minLength:1},to:{type:"string",minLength:1},allowed:{type:"boolean"},message:{type:"string",minLength:1},peerIsolation:{type:"boolean"},sliceFolders:{...O,minItems:1}}},safety:{type:"object",additionalProperties:!1,properties:{maxTsSuppressions:{type:"integer",minimum:0,default:0},maxAnyCasts:{type:"integer",minimum:0,default:0},allowInMemory:{type:"boolean",default:!1},allowDisabledPeerIsolation:{type:"boolean",default:!1}}}}},T=class extends Error{issues;source;constructor(t,n){super(`Invalid ArkGate config (${t}):
5
+ `)}`),this.name="ArchitectureChangeMapValidationError",this.source=t,this.issues=n}};function te(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function ne(e,t,n,r){for(let i of Object.keys(e))t.includes(i)||r.push({path:`${n}.${i}`,message:"unknown field"})}function R(e,t,n,r){let i=e[t];if(typeof i=="string"&&i.length>0)return i;r.push({path:`${n}.${t}`,message:"must be a non-empty string"})}function qe(e){let t=e.replace(/\\/g,"/");if(!t||t.startsWith("/")||/^[A-Za-z]:\//.test(t)||t.includes("\0"))return;let n=[];for(let i of t.split("/"))if(!(!i||i==="."))if(i===".."){if(n.length===0)return;n.pop()}else n.push(i);let r=n.join("/");return r&&r===t?r:void 0}function Ke(e,t,n="architecture change map"){let r=[];if(!te(e))throw new U(n,[{path:"$",message:"must be an object"}]);ne(e,["$schema","schemaVersion","files","dependencies"],"$",r);let i=R(e,"$schema","$",r),a=R(e,"schemaVersion","$",r);a&&a!==ee&&r.push({path:"$.schemaVersion",message:`unsupported version ${JSON.stringify(a)}; expected ${ee}`});let o=new Set(t.layers.map(p=>p.name)),d=[],l=new Set;!Array.isArray(e.files)||e.files.length===0?r.push({path:"$.files",message:"must be a non-empty array"}):e.files.forEach((p,f)=>{let m=`$.files[${f}]`;if(!te(p)){r.push({path:m,message:"must be an object"});return}ne(p,["path","operation","layer"],m,r);let h=R(p,"path",m,r),A=R(p,"operation",m,r),C=R(p,"layer",m,r),k=h?qe(h):void 0;h&&!k&&r.push({path:`${m}.path`,message:"must be a canonical project-relative path"}),A&&!["create","update","delete"].includes(A)&&r.push({path:`${m}.operation`,message:"must be create, update, or delete"}),C&&!o.has(C)&&r.push({path:`${m}.layer`,message:`references unknown layer ${JSON.stringify(C)}`}),k&&l.has(k)&&r.push({path:`${m}.path`,message:`duplicates planned path ${k}`}),k&&l.add(k);let Z=k?_(k,t.layers):void 0;k&&C&&o.has(C)&&Z!==C&&r.push({path:`${m}.layer`,message:Z?`${k} resolves to ${Z}, not ${C}`:`${k} is not assigned to an architecture layer`}),k&&A&&["create","update","delete"].includes(A)&&C&&d.push({path:k,operation:A,layer:C})});let u=[],y=new Map(d.map(p=>[p.path,p.operation])),g=new Set,c=e.dependencies??[];if(Array.isArray(c)?c.forEach((p,f)=>{let m=`$.dependencies[${f}]`;if(!te(p)){r.push({path:m,message:"must be an object"});return}ne(p,["from","to"],m,r);let h=R(p,"from",m,r),A=R(p,"to",m,r);h&&!l.has(h)&&r.push({path:`${m}.from`,message:`must reference a planned file path: ${h}`}),A&&!l.has(A)&&r.push({path:`${m}.to`,message:`must reference a planned file path: ${A}`}),h&&y.get(h)==="delete"&&r.push({path:`${m}.from`,message:`cannot depend from deleted file ${h}`}),A&&y.get(A)==="delete"&&r.push({path:`${m}.to`,message:`cannot depend on deleted file ${A}`}),h&&A&&h===A&&r.push({path:m,message:"must not declare a self dependency"});let C=h&&A?`${h}\0${A}`:void 0;C&&g.has(C)&&r.push({path:m,message:`duplicates dependency ${h} -> ${A}`}),C&&g.add(C),h&&A&&u.push({from:h,to:A})}):r.push({path:"$.dependencies",message:"must be an array"}),r.length>0)throw new U(n,r);let s={$schema:i,schemaVersion:ee,files:d.sort((p,f)=>p.path.localeCompare(f.path)),dependencies:u.sort((p,f)=>p.from.localeCompare(f.from)||p.to.localeCompare(f.to))};return{map:s,hash:$(x(s))}}function w(e){return`${e.from}->${e.to}`}function F(e,t,n,r="dependency"){return{id:`${e}:${r}:${w(t)}`,classification:e,subject:"dependency",from:t.from,to:t.to,message:n,...e==="missing"?{nextAction:`Add the planned dependency ${w(t)} to the candidate, then preflight again.`}:e==="contradictory"?{nextAction:`Replace the reverse dependency with ${w(t)}, then preflight again.`}:e==="unplanned"?{nextAction:r==="dependency-removed"?`Restore the removed dependency ${w(t)} in the candidate, then preflight again.`:`Remove the unplanned dependency ${w(t)} from the candidate, then preflight again.`}:{}}}function re(e){let t=[],n=new Map(e.changeMap.map.files.map(c=>[c.path,c])),r=new Map(e.changes.map(c=>[c.path,c])),i=new Map(e.changeMap.map.dependencies.map(c=>[w(c),c])),a=new Map(e.baseDependencies.map(c=>[w(c),c])),o=new Map(e.candidateDependencies.map(c=>[w(c),c]));for(let c of[...n.values()].sort((s,p)=>s.path.localeCompare(p.path))){let s=r.get(c.path);s?s.operation!==c.operation?t.push({id:`contradictory:file:${c.path}`,classification:"contradictory",subject:"file",path:c.path,expectedOperation:c.operation,actualOperation:s.operation,message:`${c.path} was planned as ${c.operation} but the actual operation is ${s.operation}.`,nextAction:`Change ${c.path} to the planned ${c.operation} operation, then preflight again.`}):t.push({id:`satisfied:file:${c.path}`,classification:"satisfied",subject:"file",path:c.path,expectedOperation:c.operation,actualOperation:s.operation,message:`${c.path} matches the planned ${c.operation} operation.`}):t.push({id:`missing:file:${c.path}`,classification:"missing",subject:"file",path:c.path,expectedOperation:c.operation,message:`${c.path} was planned as ${c.operation} but is absent from the actual change.`,nextAction:`${c.operation[0].toUpperCase()}${c.operation.slice(1)} ${c.path} in the complete change set, then preflight again.`})}for(let c of[...r.values()].sort((s,p)=>s.path.localeCompare(p.path)))n.has(c.path)||t.push({id:`unplanned:file:${c.path}`,classification:"unplanned",subject:"file",path:c.path,actualOperation:c.operation,message:`${c.path} has an unplanned ${c.operation} operation.`,nextAction:`Remove ${c.path} from the change set, then preflight again.`});let d=new Set;for(let c of[...i.values()].sort((s,p)=>w(s).localeCompare(w(p)))){if(o.has(w(c))){t.push(F("satisfied",c,`${c.from} -> ${c.to} exists in the candidate architecture.`));continue}let s={from:c.to,to:c.from};o.has(w(s))?(d.add(w(s)),t.push(F("contradictory",c,`${c.from} -> ${c.to} was planned, but the candidate contains the reverse edge.`))):t.push(F("missing",c,`${c.from} -> ${c.to} is absent from the candidate architecture.`))}let l=new Set([...n.keys(),...r.keys()]);for(let[c,s]of[...o].sort(([p],[f])=>p.localeCompare(f)))a.has(c)||i.has(c)||d.has(c)||!l.has(s.from)&&!l.has(s.to)||t.push({...F("unplanned",s,`${s.from} -> ${s.to} was added without a matching planned dependency.`,"dependency-added"),actualOperation:"added"});let u=new Set(e.changeMap.map.files.filter(c=>c.operation==="delete").map(c=>c.path));for(let[c,s]of[...a].sort(([p],[f])=>p.localeCompare(f)))o.has(c)||u.has(s.from)||u.has(s.to)||!l.has(s.from)&&!l.has(s.to)||t.push({...F("unplanned",s,`${s.from} -> ${s.to} was removed without a planned file deletion.`,"dependency-removed"),actualOperation:"removed"});let y={satisfied:0,missing:1,contradictory:2,unplanned:3};t.sort((c,s)=>y[c.classification]-y[s.classification]||(c.subject===s.subject?0:c.subject==="file"?-1:1)||c.id.localeCompare(s.id));let g={satisfied:t.filter(c=>c.classification==="satisfied").length,missing:t.filter(c=>c.classification==="missing").length,contradictory:t.filter(c=>c.classification==="contradictory").length,unplanned:t.filter(c=>c.classification==="unplanned").length};return{schemaVersion:"1.0",readOnly:!0,changeMapHash:e.changeMap.hash,structurallyConverged:g.missing===0&&g.contradictory===0&&g.unplanned===0,behavioralCompletion:"not-evaluated",summary:g,findings:t}}function M(e,t){return t&&e.isStringLiteralLike(t)?t.text:void 0}function xe(e,t){return e.getLineAndCharacterOfPosition(t.getStart(e)).line+1}function we(e,t){if(e.isImportDeclaration(t)){let n=t.importClause;if(!n)return!1;if(n.isTypeOnly)return!0;let r=n.namedBindings;return!!(r&&e.isNamedImports(r)&&r.elements.length>0&&r.elements.every(i=>i.isTypeOnly))}if(e.isExportDeclaration(t)){if(t.isTypeOnly)return!0;let n=t.exportClause;return!!(n&&e.isNamedExports(n)&&n.elements.length>0&&n.elements.every(r=>r.isTypeOnly))}return!1}function Ee(e,t){let n={noLib:!0,noResolve:!0,target:e.ScriptTarget.Latest},r=e.createCompilerHost(n,!0);return r.getSourceFile=i=>i===t.fileName?t:void 0,r.fileExists=i=>i===t.fileName,r.readFile=i=>i===t.fileName?t.text:void 0,e.createProgram([t.fileName],n,r).getTypeChecker()}function ie(e,t){try{return e.getSymbolAtLocation(t)}catch{return}}function ae(e,t,n,r){let i=r.parent&&e.isShorthandPropertyAssignment(r.parent)&&r.parent.name===r,a;try{a=i?t.getShorthandAssignmentValueSymbol(r.parent):ie(t,r)}catch{a=void 0}return!!a?.declarations?.some(o=>o.getSourceFile().fileName===n.fileName)}function se(e,t){let n,r=[],i=(o,d,l,u=!1)=>r.push({specifier:l,kind:d,line:xe(t,o),typeOnly:u,unresolved:l===void 0,node:o}),a=o=>{if(e.isImportDeclaration(o))i(o,"import",M(e,o.moduleSpecifier),we(e,o));else if(e.isExportDeclaration(o)&&o.moduleSpecifier)i(o,"export",M(e,o.moduleSpecifier),we(e,o));else if(e.isImportEqualsDeclaration(o)&&e.isExternalModuleReference(o.moduleReference))i(o,"require",M(e,o.moduleReference.expression),o.isTypeOnly===!0);else if(e.isCallExpression(o)){let d=o.expression.kind===e.SyntaxKind.ImportKeyword,u=e.isIdentifier(o.expression)&&o.expression.text==="require"&&!ae(e,n??(n=Ee(e,t)),t,o.expression);(d||u)&&i(o,u?"require":"dynamic-import",M(e,o.arguments[0]))}e.forEachChild(o,a)};return a(t),r}function Ye(e,t){let n=[],r=t;for(;e.isPropertyAccessExpression(r)||e.isElementAccessExpression(r);){if(e.isPropertyAccessExpression(r))n.unshift(r.name.text);else{let i=M(e,r.argumentExpression);if(i===void 0)return;n.unshift(i)}r=r.expression}if(e.isIdentifier(r))return n.unshift(r.text),{root:r,segments:n}}function We(e,t){let n=t.parent;return e.isPropertyAccessExpression(n)||e.isElementAccessExpression(n)?!1:e.isExpressionNode(t)&&!e.isInTypeQuery(t)||e.isShorthandPropertyAssignment(n)&&n.name===t}function Se(e,t){let n=t[0]==="globalThis"?t.slice(1):t;for(let r=n.length;r>=1;r-=1){let i=n.slice(0,r).join(".");if(e.has(i))return i}}function oe(e,t,n){if(n.length===0)return[];let r=new Set(n),i=Ee(e,t),a=new Map,o=new Set;for(let c of t.statements)if(e.isVariableStatement(c))for(let s of c.declarationList.declarations)e.isIdentifier(s.name)&&o.add(s.name.text);let d=c=>{let s=Ye(e,c);if(!s)return;let p=ie(i,s.root),f=p?a.get(p):void 0;return f?[...f,...s.segments.slice(1)]:ae(e,i,t,s.root)||o.has(s.root.text)?void 0:s.segments};for(let c of t.statements)if(e.isVariableStatement(c))for(let s of c.declarationList.declarations){if(!s.initializer||!e.isIdentifier(s.name))continue;let p=d(s.initializer),f=ie(i,s.name);!p||!f||a.set(f,p)}let l=[],u=new Set,y=(c,s)=>{let p=xe(t,s),f=`${c}:${s.getStart(t)}`;u.has(f)||(u.add(f),l.push({name:c,line:p,node:s}))},g=c=>{let s=c.parent&&(e.isPropertyAccessExpression(c.parent)||e.isElementAccessExpression(c.parent))&&c.parent.expression===c;if((e.isPropertyAccessExpression(c)||e.isElementAccessExpression(c))&&!s){let p=d(c),f=p?Se(r,p):void 0;f&&y(f,c)}else e.isIdentifier(c)&&r.has(c.text)&&We(e,c)&&!ae(e,i,t,c)&&y(c.text,c);if(e.isVariableDeclaration(c)&&e.isObjectBindingPattern(c.name)&&c.initializer){let p=d(c.initializer);if(p)for(let f of c.name.elements){if(!e.isIdentifier(f.name))continue;let m=f.propertyName?M(e,f.propertyName)??f.propertyName.text:f.name.text,h=Se(r,[...p,m]);h&&y(h,c.initializer)}}e.forEachChild(c,g)};return g(t),l}var ce={RAW_EVENT_PUBLISH:"Publish through a registered intent creator; raw event objects or intent strings bypass Ark contracts and tooling.",PUBLISH_MISSING_SOURCE:"Strict Ark publish calls must include metadata.source."};function $e(e){return/^(Domain|Application|Adapter|Workflow|Job|Presentation|Reporting|Metadata|Security|Audit|Observability|Kernel)\.[A-Za-z0-9_.]+$/.test(e)}function Je(e){if(!e.publishCall)return[];let t=[];return(e.rawIntentName!==void 0&&$e(e.rawIntentName)||e.objectHasIntent)&&t.push({ruleId:"RAW_EVENT_PUBLISH",message:ce.RAW_EVENT_PUBLISH}),e.arkPublishCandidate&&!e.hasSource&&t.push({ruleId:"PUBLISH_MISSING_SOURCE",message:ce.PUBLISH_MISSING_SOURCE}),t}var de=Object.freeze(["network","filesystem","clock","randomness","environment","process","persistence"]),B=Object.freeze({fetch:"network",XMLHttpRequest:"network",Date:"clock","Date.now":"clock","Math.random":"randomness","process.env":"environment",process:"process"}),fe=Object.freeze(Object.keys(B).sort()),le=Object.freeze({fs:"filesystem","node:fs":"filesystem","fs/promises":"filesystem","node:fs/promises":"filesystem","fs-extra":"filesystem","graceful-fs":"filesystem",memfs:"filesystem",chokidar:"filesystem",http:"network",https:"network",http2:"network",net:"network",tls:"network",dgram:"network",dns:"network","node:http":"network","node:https":"network","node:http2":"network","node:net":"network","node:tls":"network","node:dgram":"network","node:dns":"network",axios:"network",undici:"network","node-fetch":"network",got:"network",ky:"network",superagent:"network",ws:"network",process:"process","node:process":"process",child_process:"process","node:child_process":"process","@prisma/client":"persistence",prisma:"persistence",pg:"persistence",mysql:"persistence",mysql2:"persistence",mongodb:"persistence",mongoose:"persistence",sqlite3:"persistence","better-sqlite3":"persistence",redis:"persistence",ioredis:"persistence",typeorm:"persistence",knex:"persistence","drizzle-orm":"persistence",sequelize:"persistence",kysely:"persistence","@supabase/supabase-js":"persistence"}),pe=Object.freeze({process:Object.freeze(["process","node:process"])});function j(e){if(!e||e.startsWith(".")||e.startsWith("/"))return null;let t=le[e];if(t)return t;let n=e.indexOf("/");if(n<0)return null;let r=e.slice(0,n),i=le[r];if(i)return i;let a=e.indexOf("/",n+1);return a<0?null:le[e.slice(0,a)]??null}function ue(e,t){for(let n of t)if(pe[n]?.includes(e))return n;return null}function V(e){let t=e.split(".");for(let n=t.length;n>=1;n-=1){let r=t.slice(0,n).join("."),i=B[r];if(i)return i}return null}function z(e){if(e?.pure===!0)return[...de].sort();let n=(e?.capabilities?.deny??[]).filter(r=>de.includes(r));return[...new Set(n)].sort()}function Ze(e,t){if(t.length===0)return!1;let n=new Set(t),r=e.split(".");for(let i=r.length;i>=1;i-=1)if(n.has(r.slice(0,i).join(".")))return!0;return!1}function q(e){let t=new Set,n=new Set,r=Object.keys(B);for(let i of e?.forbiddenGlobals??[]){let a=r.filter(o=>o===i||o.startsWith(`${i}.`));if(a.length===0)n.add(i);else for(let o of a)t.add(`ambient:${o}`);for(let o of pe[i]??[])t.add(`import-exact:${o}`)}for(let i of z(e)){if(t.add(`import:${i}`),i==="process")for(let a of pe.process)t.add(`import-exact:${a}`);for(let a of r)V(a)===i&&t.add(`ambient:${a}`)}return{atoms:[...t].sort(),rawGlobals:[...n].sort()}}function Xe(e){let t=new Set,n=V(e);n&&t.add(n);for(let[r,i]of Object.entries(B))(r===e||r.startsWith(`${e}.`))&&t.add(i);return[...t].sort()}function Qe(e,t){let n=[];for(let r of se(e,t)){if(r.typeOnly||!r.specifier)continue;let i=j(r.specifier);i&&n.push({capability:i,symbol:r.specifier,line:r.line,source:"import-based"})}for(let r of oe(e,t,fe)){let i=V(r.name);i&&n.push({capability:i,symbol:r.name,line:r.line,source:"ambient-global"})}return n.sort((r,i)=>r.line-i.line||r.capability.localeCompare(i.capability)||r.symbol.localeCompare(i.symbol))}var ye="https://unpkg.com/arkgate@2/schemas/ark.config.schema.json",Oe=["DomainModel","ApplicationOrchestration","PersistenceAdapters","IntegrationAdapters","WorkflowSagaEngine","BackgroundJobsScheduling","PresentationAdapters","ReportingReadModels","ExtensibilityMetadata","SecurityAuditObservability","Kernel"],et=new Set(["PresentationAdapters->ApplicationOrchestration","ApplicationOrchestration->DomainModel","WorkflowSagaEngine->ApplicationOrchestration","WorkflowSagaEngine->DomainModel","BackgroundJobsScheduling->ApplicationOrchestration"]);function tt(){let e=[];for(let t of Oe)for(let n of Oe)t===n||et.has(`${t}->${n}`)||e.push({from:t,to:n,allowed:!1});return e}var Re=tt();var O={type:"array",items:{type:"string",minLength:1},uniqueItems:!0},Le={$schema:"https://json-schema.org/draft/2020-12/schema",$id:ye,title:"ArkGate architecture contract",description:"Versioned contract consumed identically by ArkGate CLI, MCP, and ESLint surfaces.",type:"object",additionalProperties:!1,required:["$schema","schemaVersion","include","layers","rules"],properties:{$schema:{type:"string",minLength:1,default:ye,description:"Editor-facing URL or local path for this JSON Schema."},schemaVersion:{type:"string",const:"1.0",default:"1.0"},name:{type:"string",minLength:1},include:{...O,minItems:1,default:["src"]},exclude:{...O,default:[]},excludeGenerated:{type:"boolean",default:!0},frameworkOverlay:{type:"string",minLength:1},layers:{type:"array",default:[],items:{$ref:"#/$defs/layer"}},rules:{type:"array",default:Re,items:{$ref:"#/$defs/rule"}},cyclePolicy:{type:"string",enum:["strict","soft","framework-soft","off"],default:"strict"},dynamicImportAllowlist:{...O,default:[]},safety:{$ref:"#/$defs/safety",default:{maxTsSuppressions:0,maxAnyCasts:0,allowInMemory:!1,allowDisabledPeerIsolation:!1}}},$defs:{layer:{type:"object",additionalProperties:!1,required:["name","patterns"],properties:{name:{type:"string",minLength:1},patterns:{...O,minItems:1},exclude:O,intentPrefixes:O,description:{type:"string",minLength:1},forbiddenGlobals:O,capabilities:{type:"object",additionalProperties:!1,properties:{deny:{type:"array",uniqueItems:!0,items:{type:"string",enum:["network","filesystem","clock","randomness","environment","process","persistence"]}}}},pure:{type:"boolean"},mayImportInfrastructure:{type:"boolean"},optional:{type:"boolean"}}},rule:{type:"object",additionalProperties:!1,required:["from","to","allowed"],properties:{from:{type:"string",minLength:1},to:{type:"string",minLength:1},allowed:{type:"boolean"},message:{type:"string",minLength:1},peerIsolation:{type:"boolean"},sliceFolders:{...O,minItems:1}}},safety:{type:"object",additionalProperties:!1,properties:{maxTsSuppressions:{type:"integer",minimum:0,default:0},maxAnyCasts:{type:"integer",minimum:0,default:0},allowInMemory:{type:"boolean",default:!1},allowDisabledPeerIsolation:{type:"boolean",default:!1}}}}},T=class extends Error{issues;source;constructor(t,n){super(`Invalid ArkGate config (${t}):
6
6
  ${n.map(r=>`- ${r.path}: ${r.message}`).join(`
7
- `)}`),this.name="ArkConfigValidationError",this.source=t,this.issues=n}};function Le(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function fe(e,t){return/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(t)?`${e}.${t}`:`${e}[${JSON.stringify(t)}]`}function N(e){return e===null?"null":Array.isArray(e)?"array":typeof e}function et(e,t){let n="#/$defs/";if(e.startsWith(n))return t.$defs[e.slice(n.length)]}function K(e,t,n,r,i){if(t.$ref){let a=et(t.$ref,r);if(!a){i.push({path:n,message:`schema reference ${t.$ref} cannot be resolved`});return}K(e,a,n,r,i);return}if(t.const!==void 0&&!Object.is(e,t.const)){i.push({path:n,message:`must equal ${JSON.stringify(t.const)}`});return}if(t.enum&&!t.enum.some(a=>Object.is(a,e))){i.push({path:n,message:`must be one of ${t.enum.map(String).join(", ")}`});return}if(t.type==="object"){if(!Le(e)){i.push({path:n,message:`must be an object; received ${N(e)}`});return}let a=t.properties??{};for(let o of t.required??[])e[o]===void 0&&i.push({path:fe(n,o),message:"is required"});if(t.additionalProperties===!1)for(let o of Object.keys(e))o in a||i.push({path:fe(n,o),message:"unknown field"});for(let[o,l]of Object.entries(a))e[o]!==void 0&&K(e[o],l,fe(n,o),r,i);return}if(t.type==="array"){if(!Array.isArray(e)){i.push({path:n,message:`must be an array; received ${N(e)}`});return}if(t.minItems!==void 0&&e.length<t.minItems&&i.push({path:n,message:`must contain at least ${t.minItems} item(s)`}),t.uniqueItems){let a=e.map(o=>JSON.stringify(o));new Set(a).size!==a.length&&i.push({path:n,message:"must not contain duplicate items"})}t.items&&e.forEach((a,o)=>K(a,t.items,`${n}[${o}]`,r,i));return}if(t.type==="string"){if(typeof e!="string"){i.push({path:n,message:`must be a string; received ${N(e)}`});return}t.minLength!==void 0&&e.length<t.minLength&&i.push({path:n,message:`must contain at least ${t.minLength} character(s)`});return}if(t.type==="boolean"){typeof e!="boolean"&&i.push({path:n,message:`must be a boolean; received ${N(e)}`});return}if(t.type==="integer"){if(!Number.isInteger(e)){i.push({path:n,message:`must be an integer; received ${N(e)}`});return}t.minimum!==void 0&&e<t.minimum&&i.push({path:n,message:`must be at least ${t.minimum}`})}}function tt(e){return{...e,$schema:e.$schema===void 0?ue:e.$schema,schemaVersion:e.schemaVersion===void 0?"1.0":e.schemaVersion,include:e.include===void 0?["src"]:e.include,layers:e.layers===void 0?[]:e.layers,rules:e.rules===void 0?Oe.map(t=>({...t})):e.rules}}function nt(e,t="ark.config.json"){if(!Le(e))throw new T(t,[{path:"$",message:`must be an object; received ${N(e)}`}]);let n=e.schemaVersion===void 0?"unversioned":null;if(e.schemaVersion!==void 0&&e.schemaVersion!=="1.0")throw new T(t,[{path:"$.schemaVersion",message:`unsupported version ${JSON.stringify(e.schemaVersion)}; expected 1.0`}]);return{candidate:tt(e),migratedFrom:n}}function ge(e,t="ark.config.json"){let{candidate:n,migratedFrom:r}=nt(e,t),i=[];if(K(n,$e,"$",$e,i),i.length>0)throw new T(t,i);return{config:n,migratedFrom:r}}function Pe(e,t="ark.config.json"){let n;try{n=JSON.parse(e)}catch(r){throw new T(t,[{path:"$",message:`invalid JSON: ${r instanceof Error?r.message:String(r)}`}])}return ge(n,t)}var Re="1.0";function b(e,t){e.push({id:`${t.classification}:${t.path}:${t.kind}`,path:t.path,classification:t.classification,message:t.message,...t.classification==="weakening"||t.classification==="judgment-required"?{nextAction:`Restore the previous protection at ${t.path}, then run ArkGate again.`}:{},...t.before===void 0?{}:{before:t.before},...t.after===void 0?{}:{after:t.after}})}function S(e){return[...new Set(e??[])].sort()}function R(e,t,n,r,i){let a=S(n),o=S(r),l=new Set(a),p=new Set(o),u=o.filter(g=>!l.has(g)),m=a.filter(g=>!p.has(g));u.length===0&&m.length===0||(u.length>0&&b(e,{kind:"added",path:t,classification:i.added,message:i.addedMessage,before:a,after:o}),m.length>0&&b(e,{kind:"removed",path:t,classification:i.removed,message:i.removedMessage,before:a,after:o}))}function Y(e,t,n,r,i,a,o){if(n===r)return;b(e,{kind:r?"enabled":"disabled",path:t,classification:r?i:i==="strengthening"?"weakening":"strengthening",message:r?a:o,before:n,after:r})}function W(e,t){let n=new Map,r=new Set;for(let i of e){let a=t(i);n.has(a)?r.add(a):n.set(a,i)}return{values:n,duplicates:[...r].sort()}}function rt(e,t,n){let r=W(t,a=>a.name),i=W(n,a=>a.name);(r.duplicates.length>0||i.duplicates.length>0)&&b(e,{kind:"duplicate-layer",path:"$.layers",classification:"judgment-required",message:"Duplicate layer names make policy ownership ambiguous.",before:r.duplicates,after:i.duplicates});for(let a of[...new Set([...r.values.keys(),...i.values.keys()])].sort()){let o=r.values.get(a),l=i.values.get(a),p=`$.layers[${a}]`;if(!o&&l){b(e,{kind:"layer-added",path:p,classification:"judgment-required",message:"A layer was added; verify overlap, ownership, and rule coverage.",after:l});continue}if(o&&!l){b(e,{kind:"layer-removed",path:p,classification:"weakening",message:"Removing a layer can leave its source paths ungoverned.",before:o});continue}if(!o||!l)continue;R(e,`${p}.patterns`,o.patterns,l.patterns,{added:"strengthening",removed:"weakening",addedMessage:"Additional paths are governed by this layer.",removedMessage:"Paths were removed from this layer and may become ungoverned."}),R(e,`${p}.exclude`,o.exclude,l.exclude,{added:"weakening",removed:"strengthening",addedMessage:"Additional paths are excluded from this layer.",removedMessage:"Fewer paths are excluded from this layer."});let u=q(o),m=q(l);R(e,`${p}.forbiddenGlobals`,u.rawGlobals,m.rawGlobals,{added:"strengthening",removed:"weakening",addedMessage:"Additional forbidden globals are enforced in this layer.",removedMessage:"A forbidden-global protection was removed from this layer."}),R(e,`${p}.capabilities`,u.atoms,m.atoms,{added:"strengthening",removed:"weakening",addedMessage:"Additional ambient/import protection is enforced in this layer (coverage atoms).",removedMessage:"An ambient/import protection was lost from this layer (coverage atoms)."}),S(o.intentPrefixes).join("\0")!==S(l.intentPrefixes).join("\0")&&b(e,{kind:"intent-prefixes-changed",path:`${p}.intentPrefixes`,classification:"judgment-required",message:"Intent ownership changed and must be reviewed against publishers and consumers.",before:S(o.intentPrefixes),after:S(l.intentPrefixes)}),Y(e,`${p}.mayImportInfrastructure`,o.mayImportInfrastructure===!0,l.mayImportInfrastructure===!0,"weakening","The layer may now import infrastructure directly.","Direct infrastructure imports are no longer allowed for this layer."),Y(e,`${p}.optional`,o.optional===!0,l.optional===!0,"weakening","The layer is now optional and can be absent without a strict warning.","The layer is now required when its contract is active.")}}function it(e,t,n){let r=o=>`${o.from}->${o.to}`,i=W(t,r),a=W(n,r);(i.duplicates.length>0||a.duplicates.length>0)&&b(e,{kind:"duplicate-rule",path:"$.rules",classification:"judgment-required",message:"Duplicate rule edges make the effective verdict order-dependent.",before:i.duplicates,after:a.duplicates});for(let o of[...new Set([...i.values.keys(),...a.values.keys()])].sort()){let l=i.values.get(o),p=a.values.get(o),u=`$.rules[${o}]`;if(!l&&p){p.allowed===!1&&b(e,{kind:"deny-added",path:u,classification:"strengthening",message:"A denied dependency edge was added.",after:p});continue}if(l&&!p){l.allowed===!1&&b(e,{kind:"deny-removed",path:u,classification:"weakening",message:"A denied dependency edge was removed.",before:l});continue}if(!l||!p)continue;l.allowed!==p.allowed&&b(e,{kind:p.allowed?"deny-disabled":"deny-enabled",path:`${u}.allowed`,classification:p.allowed?"weakening":"strengthening",message:p.allowed?"A previously denied dependency edge is now allowed.":"A dependency edge is now denied.",before:l.allowed,after:p.allowed});let m=l.peerIsolation===!0,g=p.peerIsolation===!0;if(m!==g){let c=l.from===l.to&&p.from===p.to;b(e,{kind:g?"peer-isolation-enabled":"peer-isolation-disabled",path:`${u}.peerIsolation`,classification:c?g?"strengthening":"weakening":"judgment-required",message:c?g?"Cross-slice dependencies inside this layer are now denied.":"Cross-slice dependencies inside this layer are no longer denied.":"Changing peer isolation on a cross-layer edge changes the denial scope.",before:m,after:g})}S(l.sliceFolders).join("\0")!==S(p.sliceFolders).join("\0")&&b(e,{kind:"slice-folders-changed",path:`${u}.sliceFolders`,classification:"judgment-required",message:"Slice ownership folders changed and can reclassify existing dependencies.",before:S(l.sliceFolders),after:S(p.sliceFolders)})}}function at(e,t,n){let r=t.safety??{},i=n.safety??{};for(let a of["maxTsSuppressions","maxAnyCasts"]){let o=r[a]??0,l=i[a]??0;o!==l&&b(e,{kind:l>o?"threshold-raised":"threshold-lowered",path:`$.safety.${a}`,classification:l>o?"weakening":"strengthening",message:l>o?"The safety threshold allows more violations.":"The safety threshold allows fewer violations.",before:o,after:l})}for(let a of["allowInMemory","allowDisabledPeerIsolation"])Y(e,`$.safety.${a}`,r[a]===!0,i[a]===!0,"weakening","A safety exception was enabled.","A safety exception was disabled.")}function st(e){return e.some(t=>t.classification==="weakening")?"weakening":e.some(t=>t.classification==="judgment-required")?"judgment-required":e.some(t=>t.classification==="strengthening")?"strengthening":"neutral"}function ve(e,t){let n=[];R(n,"$.include",e.include,t.include,{added:"strengthening",removed:"weakening",addedMessage:"Additional project roots are governed.",removedMessage:"Project roots were removed from governance."}),R(n,"$.exclude",e.exclude,t.exclude,{added:"weakening",removed:"strengthening",addedMessage:"Additional project paths are excluded from governance.",removedMessage:"Fewer project paths are excluded from governance."}),R(n,"$.dynamicImportAllowlist",e.dynamicImportAllowlist,t.dynamicImportAllowlist,{added:"weakening",removed:"strengthening",addedMessage:"Additional files may use non-literal dynamic imports.",removedMessage:"Fewer files may use non-literal dynamic imports."}),Y(n,"$.excludeGenerated",e.excludeGenerated!==!1,t.excludeGenerated!==!1,"weakening","Generated source is now excluded from governance.","Generated source is now governed.");let r={off:0,soft:1,"framework-soft":1,strict:2},i=e.cyclePolicy??"strict",a=t.cyclePolicy??"strict";if(i!==a){let o=r[a]===r[i]?"judgment-required":r[a]>r[i]?"strengthening":"weakening";b(n,{kind:"cycle-policy-changed",path:"$.cyclePolicy",classification:o,message:"The cycle enforcement level changed.",before:i,after:a})}return(e.frameworkOverlay??null)!==(t.frameworkOverlay??null)&&b(n,{kind:"framework-overlay-changed",path:"$.frameworkOverlay",classification:"judgment-required",message:"The framework overlay changed and may alter effective layer matching.",before:e.frameworkOverlay??null,after:t.frameworkOverlay??null}),rt(n,e.layers,t.layers),it(n,e.rules,t.rules),at(n,e,t),n.sort((o,l)=>o.path.localeCompare(l.path)||o.id.localeCompare(l.id)),{schemaVersion:Re,classification:st(n),findings:n}}function _e(e,t){if(!e||e.schemaVersion!==Re||typeof e.basePolicyHash!="string"||typeof e.candidatePolicyHash!="string"||typeof e.reason!="string"||!Array.isArray(e.findingIds)||e.findingIds.some(i=>typeof i!="string")||e.reason.trim().length===0||e.basePolicyHash!==t.basePolicyHash||e.candidatePolicyHash!==t.candidatePolicyHash)return!1;let n=S(e.findingIds),r=S(t.findingIds);return n.length===r.length&&n.every((i,a)=>i===r[a])}function v(e){let t=[];for(let n of e.replace(/\\/g,"/").split("/"))!n||n==="."||(n===".."&&t.length>0&&t.at(-1)!==".."?t.pop():t.push(n));return t.join("/")}function Me(e){return e!==void 0&&/[A-Za-z0-9_$]/.test(e)}function L(e,t){for(;t<e.length&&/\s/.test(e[t]);)t+=1;return t}function H(e,t){let n=e[t];if(n!=="'"&&n!=='"')return;let r=t,i="";for(t+=1;t<e.length;t+=1){let a=e[t];if(a===n)return{value:i,offset:r,excerpt:e.slice(r,t+1)};a==="\\"&&t+1<e.length?(i+=e[t+1],t+=1):i+=a}}function E(e,t,n){return e.startsWith(t,n)&&!Me(e[n-1])&&!Me(e[n+t.length])}function ot(e,t){if(t=L(e,t+6),e[t]==="(")return H(e,L(e,t+1));let n=!1;if(E(e,"type",t)){let i=L(e,t+4);e[i]!==","&&!E(e,"from",i)&&(n=!0)}let r=Ne(e,t,!0);return r&&n?{...r,typeOnly:!0}:r}function ct(e,t){t=t+6;let n=L(e,t),r=!1;if(E(e,"type",n)){let a=L(e,n+4);(e[a]==="{"||e[a]==="*")&&(r=!0)}let i=Ne(e,t,!1);return i&&r?{...i,typeOnly:!0}:i}function Ne(e,t,n){for(;t<e.length;t+=1){if(e[t]===";")return;if(E(e,"from",t))return H(e,L(e,t+4));if(n&&(e[t]==="'"||e[t]==='"'))return H(e,t);if(t>0&&(E(e,"import",t)||E(e,"export",t)))return}}function lt(e,t){for(t+=1;t<e.length;t+=1){let n=e[t];if(n==="\\")t+=1;else if(n==="`")return t}return e.length}function pt(e,t){let n=t-1;for(;n>=0&&/\s/.test(e[n]);)n-=1;if(e[n]===".")return;let r=L(e,t+7);if(e[r]!=="(")return;r=L(e,r+1);let i=H(e,r);return i?{...i,requireCall:!0}:void 0}function dt(e){let t=[];for(let n=0;n<e.length;n+=1){let r=e[n];if(r==="/"&&e[n+1]==="/"){if(n=e.indexOf(`
8
- `,n+2),n<0)break;continue}if(r==="/"&&e[n+1]==="*"){let a=e.indexOf("*/",n+2);if(a<0)break;n=a+1;continue}if(r==="`"){n=lt(e,n);continue}if(r==="'"||r==='"'){let a=H(e,n);a&&(n=a.offset+a.excerpt.length-1);continue}let i=r==="i"&&E(e,"import",n)?ot(e,n):r==="e"&&E(e,"export",n)?ct(e,n):r==="r"&&E(e,"require",n)?pt(e,n):void 0;i&&t.push(i)}return t}function Te(e,t){let n=[],r=[];for(let i of dt(e.content)){let a=i.value;if(i.requireCall&&a.startsWith("."))continue;if(!a.startsWith(".")){if(i.typeOnly)continue;let u=j(a);if(!u)continue;let m=e.content.slice(0,i.offset).split(`
9
- `).length;r.push({file:e.path,symbol:a,capability:u,evidence:{kind:"import",file:e.path,line:m,excerpt:i.excerpt}});continue}let o=e.content.slice(0,i.offset).split(`
10
- `).length,l={kind:"import",file:e.path,line:o,excerpt:i.excerpt},p=ft(e.path,a,t);n.push({from:e.path,specifier:a,to:p?.path??null,resolution:p?"resolved":"unresolved",fromLayer:e.layer,toLayer:p?.layer??null,evidence:l})}return{edges:n,capabilityUses:r}}function ft(e,t,n){let r=e.split("/");r.pop();for(let a of t.split("/"))a==="."||a===""||(a===".."?r.pop():r.push(a));let i=r.join("/");for(let a of[i,`${i}.ts`,`${i}.tsx`,`${i}.mts`,`${i}.cts`,`${i}/index.ts`,`${i}/index.tsx`]){let o=n.get(a);if(o)return o}}function De(e,t){let n=[];for(let r of e){if(!r.to||!r.fromLayer||!r.toLayer)continue;let i=G(t.rules,r.fromLayer,r.toLayer,{fromPath:r.from,toPath:r.to,layers:t.layers});i&&n.push({ruleId:`layer-dependency:${i.from}->${i.to}`,message:i.message??`${i.from} must not depend on ${i.to}.`,edge:r,evidence:r.evidence})}return n}function ye(e,t){let n=typeof e=="string"?Pe(e,t):ge(e,t);return{...n,policyHash:$(x(n.config))}}function gt(e){let t=ye(e.baseConfig,e.baseSource??"base ark.config.json"),n=ye(e.candidateConfig,e.candidateSource??"candidate ark.config.json"),r=ve(t.config,n.config),i=r.findings.filter(l=>l.classification==="weakening"||l.classification==="judgment-required").map(l=>l.id).sort(),a=i.length>0,o=a&&_e(e.acknowledgement,{basePolicyHash:t.policyHash,candidatePolicyHash:n.policyHash,findingIds:i});return{schemaVersion:r.schemaVersion,basePolicyHash:t.policyHash,candidatePolicyHash:n.policyHash,classification:r.classification,findings:r.findings,blockingFindingIds:i,requiresAcknowledgement:a,acknowledged:o,valid:!a||o}}function J(e){let t=e.files.map(l=>{let p=v(l.path);return{path:p,content:l.content,contentHash:$(l.content),layer:_(p,e.contract.config.layers)??null}}).sort((l,p)=>l.path.localeCompare(p.path)),n=new Map(t.map(l=>[l.path,l])),r=[],i=[];for(let l of t){let p=Te(l,n);r.push(...p.edges),i.push(...p.capabilityUses)}let a=De(r,e.contract.config),o=new Map(e.contract.config.layers.map(l=>[l.name,new Set(z(l))]));for(let l of i){let p=n.get(l.file)?.layer;!p||!o.get(p)?.has(l.capability)||a.push({ruleId:"CAPABILITY_VIOLATION",message:`${p} denies the ${l.capability} capability; found import of "${l.symbol}".`,capability:l.capability,symbol:l.symbol,evidence:l.evidence})}return{ir:{schemaVersion:"1.0",policyHash:e.contract.policyHash,compilerOptionsHash:$(x(e.compilerOptions??{})),files:t,layers:e.contract.config.layers.map(l=>l.name),edges:r,capabilityUses:i,violations:a}}}function me(e){let t=new Map(e.files.map(n=>[v(n.path),n]));for(let n of e.changes){let r=v(n.path);"delete"in n&&n.delete?t.delete(r):"content"in n&&t.set(r,{path:r,content:n.content})}return J({contract:e.contract,files:[...t.values()],compilerOptions:e.compilerOptions})}function yt(e){let t=`${e.evidence.file}:${e.evidence.line}`;if(!e.edge)return`${e.ruleId} at ${t}: ${e.message}`;let n=e.edge.to??e.edge.specifier;return`${e.ruleId} at ${t}: ${e.edge.from} imports ${n}. ${e.message}`}function Fe(e){let t=0,n=new Map,r=new Map,i=new Set,a=[],o=[],l=p=>{n.set(p,t),r.set(p,t),t+=1,a.push(p),i.add(p);for(let g of[...e.get(p)??[]].sort())e.has(g)&&(n.has(g)?i.has(g)&&r.set(p,Math.min(r.get(p)??0,n.get(g)??0)):(l(g),r.set(p,Math.min(r.get(p)??0,r.get(g)??0))));if(r.get(p)!==n.get(p))return;let u=[],m;do{if(m=a.pop(),m===void 0)break;i.delete(m),u.push(m)}while(m!==p);u.length>1&&o.push(u.sort())};for(let p of[...e.keys()].sort())n.has(p)||l(p);return o.sort((p,u)=>p[0].localeCompare(u[0])).map(p=>({ruleId:"CIRCULAR_DEPENDENCY",file:p[0],line:1,target:p.join(" \u2192 "),message:`Circular dependency among ${p.length} files: ${p.join(" \u2192 ")} \u2192 ${p[0]}.`,cycleKind:"value"}))}function he(e){let t=e.contentViolations.map(a=>({...a})),n=(e.warnings??[]).map(a=>({...a})),r=new Map(e.files.map(a=>[a,new Set]));for(let a of e.edges){if(a.to&&a.to!==a.from&&!a.typeOnly&&r.has(a.from)&&r.get(a.from)?.add(a.to),!a.to||!a.toLayer)continue;let o=G(e.rules,a.fromLayer,a.toLayer,{fromPath:a.from,toPath:a.to,layers:e.config.layers});if(!o)continue;let l=!!o.peerIsolation;t.push({ruleId:"LAYER_IMPORT_VIOLATION",file:a.from,line:a.line,fromLayer:a.fromLayer,toLayer:a.toLayer,target:a.to,...a.typeOnly?{typeOnly:!0}:{},...a.targetTypeOnlyExports?{targetTypeOnlyExports:!0}:{},...a.sourcePureTypeModule?{sourcePureTypeModule:!0}:{},...a.namedBindingsTypeOnly?{namedBindingsTypeOnly:!0}:{},...!l&&a.portProofEligible?{portProofEligible:!0}:{},...a.kind?{edgeKind:a.kind}:{},...l?{peerIsolation:!0}:{},message:o.message??(l?`${a.fromLayer} must not ${a.kind} another slice of ${a.toLayer} (${a.from} \u2192 ${a.to}). Extract shared code or use events/ports across slices.`:`${a.fromLayer} must not ${a.kind} ${a.toLayer}.`)})}let i=String(e.config.cyclePolicy??"strict").toLowerCase();if(i!=="off"){let a=Fe(r);i==="soft"||i==="framework-soft"?n.push(...a.map(o=>({...o,message:`${o.message} (soft cycle policy \u2014 advisory only; set cyclePolicy: "strict" to fail the check)`,failsStrict:!1}))):t.push(...a)}return{violations:t,warnings:n,safety:e.safety}}function je(e){switch(e.ruleId){case"LAYER_IMPORT_VIOLATION":return e.typeOnly||e.targetTypeOnlyExports||e.namedBindingsTypeOnly?"Move the referenced type to a mutually allowed layer, use `import type`, then preflight again.":e.peerIsolation?"Extract the shared dependency to a shared layer, then preflight again.":`Define a port in ${e.fromLayer??"the source layer"}, inject the ${e.toLayer??"outer-layer"} implementation, then preflight again.`;case"FORBIDDEN_GLOBAL":return`Inject ${e.target??"the capability"} through a port, then preflight again.`;case"CAPABILITY_VIOLATION":return`Define a ${String(e.capability??"capability")} port in ${e.fromLayer??"the walled layer"}, bind the implementation outside it, then preflight again.`;case"CIRCULAR_DEPENDENCY":return"Extract the shared dependency into a third module, then preflight again.";case"RAW_EVENT_PUBLISH":return"Publish through a registered intent creator, then run Ark again.";case"PUBLISH_MISSING_SOURCE":return"Add metadata.source to the publish call, then run Ark again.";default:return`Resolve ${typeof e.ruleId=="string"&&e.ruleId.length>0?e.ruleId:"ARK_UNKNOWN"} without weakening ark.config.json, then run Ark again.`}}function Ve(e){return $(x(e.map(({path:t,contentHash:n})=>({path:t,contentHash:n}))))}function mt(e){let t=J(e),n=new Map(t.ir.files.map(s=>[s.path,s])),r=[],i=new Set,a=[];for(let s of e.changes){let d=s.path.replace(/\\/g,"/"),f=v(s.path);if(!f||f===".."||f.startsWith("../")||d.startsWith("/")||/^[A-Za-z]:\//.test(d)||d.includes("\0")){a.push({ruleId:"INVALID_CHANGE_PATH",file:"<change-set>",line:1,message:"Every change requires a safe, non-empty project-relative path."});continue}if(i.has(f)){a.push({ruleId:"DUPLICATE_CHANGE_PATH",file:f,line:1,message:`The atomic change set contains more than one operation for ${f}.`});continue}i.add(f),"delete"in s&&s.delete&&!n.has(f)&&a.push({ruleId:"DELETE_TARGET_MISSING",file:f,line:1,message:`Cannot delete ${f} because it is not present in the supplied base tree.`}),r.push("delete"in s&&s.delete?{path:f,delete:!0}:{path:f,content:"content"in s?s.content:""})}e.changes.length===0&&a.push({ruleId:"CHANGE_SET_EMPTY",file:"<change-set>",line:1,message:"Atomic preflight requires at least one create, update, or delete."});let o=me({...e,changes:r}),l=new Map(o.ir.files.map(s=>[s.path,s])),p=o.ir.violations.filter(s=>s.ruleId==="CAPABILITY_VIOLATION").map(s=>({ruleId:s.ruleId,file:s.evidence.file,line:s.evidence.line,target:s.symbol,capability:s.capability,message:s.message})),u=he({config:e.contract.config,rules:e.contract.config.rules,files:o.ir.files.map(s=>s.path),contentViolations:p,edges:o.ir.edges.filter(s=>!!s.fromLayer).map(s=>({from:s.from,fromLayer:s.fromLayer,...s.to?{to:s.to}:{},...s.toLayer?{toLayer:s.toLayer}:{},line:s.evidence.line,kind:"import"}))}),m=r.map(s=>{let d=v(s.path),f=n.get(d),y=l.get(d);return{path:d,operation:"delete"in s&&s.delete?"delete":f?"update":"create",...f?{beforeContentHash:f.contentHash}:{},...y?{candidateContentHash:y.contentHash}:{}}}).sort((s,d)=>s.path.localeCompare(d.path)),g=[...a,...u.violations].map(s=>({...s,nextAction:je(s)})),c=e.changeMap?re({changeMap:e.changeMap,changes:m,baseDependencies:t.ir.edges.flatMap(s=>s.to?[{from:s.from,to:s.to}]:[]),candidateDependencies:o.ir.edges.flatMap(s=>s.to?[{from:s.from,to:s.to}]:[])}):void 0;return{schemaVersion:"1.0",valid:g.length===0&&(c?.structurallyConverged??!0),readOnly:!0,policyHash:e.contract.policyHash,compilerOptionsHash:o.ir.compilerOptionsHash,baseTreeHash:Ve(t.ir.files),candidateTreeHash:Ve(o.ir.files),...e.changeMap?{changeMapHash:e.changeMap.hash}:{},...c?{convergence:c}:{},changes:m,violations:g,warnings:u.warnings}}function I(e,t,n={}){return{ruleId:e,message:t,...n}}function ht(e){let{config:t,rules:n,files:r,manifest:i}=e,a=[];if(t.dynamicImportAllowlist!==void 0&&(!Array.isArray(t.dynamicImportAllowlist)||t.dynamicImportAllowlist.some(s=>typeof s!="string"))&&a.push(I("CONFIG_INVALID_DYNAMIC_IMPORT_ALLOWLIST","dynamicImportAllowlist must be an array of file globs.")),t.safety!==void 0&&(t.safety===null||typeof t.safety!="object"||Array.isArray(t.safety)))a.push(I("CONFIG_INVALID_SAFETY","safety must be an object."));else if(t.safety)for(let s of["maxTsSuppressions","maxAnyCasts"]){let d=t.safety[s];d!==void 0&&(!Number.isInteger(d)||d<0)&&a.push(I("CONFIG_INVALID_SAFETY_THRESHOLD",`safety.${s} must be a non-negative integer.`))}let o=Array.isArray(t.layers)?t.layers:[],l=Array.isArray(i?.architecture?.layers)?i.architecture.layers:[],p=new Set([...o.map(s=>s.name).filter(Boolean),...l.map(s=>s.name).filter(s=>!!s)]);o.length===0&&a.push(I("CONFIG_NO_LAYERS","No file layers are configured; ark-check cannot classify files for import-boundary enforcement."));let u=new Set,m=new Set;for(let s of o){if(!s.name){a.push(I("CONFIG_LAYER_WITHOUT_NAME","A configured layer is missing a name."));continue}u.has(s.name)&&m.add(s.name),u.add(s.name),s.forbiddenGlobals!==void 0&&(!Array.isArray(s.forbiddenGlobals)||s.forbiddenGlobals.some(f=>typeof f!="string"))&&a.push(I("CONFIG_INVALID_FORBIDDEN_GLOBALS",`Layer "${s.name}" has an invalid forbiddenGlobals value; expected an array of strings (e.g. ["fetch", "Date.now"]). The entry is ignored.`,{layer:s.name}));let d=Array.isArray(s.patterns)?s.patterns:[];if(d.length===0){a.push(I("CONFIG_LAYER_WITHOUT_PATTERNS",`Layer "${s.name}" has no file patterns and will never classify files.`,{layer:s.name}));continue}for(let f of d){let y;try{y=D(f)}catch(h){a.push(I("CONFIG_INVALID_LAYER_PATTERN",`Layer "${s.name}" has an invalid pattern "${f}": ${h instanceof Error?h.message:String(h)}`,{layer:s.name,pattern:f}));continue}!r.some(h=>y.test(h))&&!s.optional&&a.push(I("CONFIG_LAYER_PATTERN_NO_MATCHES",`Layer "${s.name}" pattern "${f}" matched no included files.`,{layer:s.name,pattern:f,failsStrict:!1}))}}for(let s of m)a.push(I("CONFIG_DUPLICATE_LAYER",`Layer "${s}" is configured more than once.`,{layer:s}));if(p.size>0)for(let s of n??[])s.from&&!p.has(s.from)&&a.push(I("CONFIG_RULE_UNKNOWN_FROM_LAYER",`Rule references unknown source layer "${s.from}".`,{fromLayer:s.from,toLayer:s.to})),s.to&&!p.has(s.to)&&a.push(I("CONFIG_RULE_UNKNOWN_TO_LAYER",`Rule references unknown target layer "${s.to}".`,{fromLayer:s.from,toLayer:s.to}));let g=new Set;if(o.length>1)for(let s of r){let d=-1,f=[];for(let y of o)for(let h of y.patterns??[]){if(!D(h).test(s))continue;let A=Q(h);A>d?(d=A,f=[y.name]):A===d&&!f.includes(y.name)&&f.push(y.name)}f.length>1&&g.add([...f].sort().join(" + "))}g.size>0&&a.push(I("CONFIG_AMBIGUOUS_LAYERS",`Some files match multiple layers at equal specificity; classification falls back to declaration order. Disambiguate the overlapping patterns: ${[...g].join(", ")}.`,{pairs:[...g]}));let c=r.filter(s=>!_(s,o));return c.length>0&&a.push(I("CONFIG_UNCLASSIFIED_FILES",`${c.length} included source file(s) are not matched by any configured layer; ark-check will not enforce import rules for those source files.`,{count:c.length,samples:c.slice(0,5)})),a}export{de as AMBIENT_CAPABILITY_ENTRIES,pe as CAPABILITY_IDS,ce as SOURCE_POLICY_MESSAGES,We as ambientCoveredByForbiddenGlobals,re as analyzeArchitectureConvergence,me as analyzeChange,gt as analyzePolicyDelta,J as analyzeProject,V as capabilityForAmbientName,j as capabilityForModuleSpecifier,Ye as classifyPublishFacts,ht as collectAnalysisConfigWarnings,Ze as collectCapabilityUses,oe as collectForbiddenCapabilityUses,Fe as detectArchitectureCycles,z as effectiveCapabilityDeny,he as evaluateArchitectureGraph,yt as explainViolation,se as extractSemanticDependencies,ze as loadArchitectureChangeMap,ye as loadContract,xe as looksLikeArkIntent,Je as lowerForbiddenGlobal,q as loweredLayerCoverage,mt as preflightChange};
7
+ `)}`),this.name="ArkConfigValidationError",this.source=t,this.issues=n}};function Pe(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function ge(e,t){return/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(t)?`${e}.${t}`:`${e}[${JSON.stringify(t)}]`}function N(e){return e===null?"null":Array.isArray(e)?"array":typeof e}function nt(e,t){let n="#/$defs/";if(e.startsWith(n))return t.$defs[e.slice(n.length)]}function K(e,t,n,r,i){if(t.$ref){let a=nt(t.$ref,r);if(!a){i.push({path:n,message:`schema reference ${t.$ref} cannot be resolved`});return}K(e,a,n,r,i);return}if(t.const!==void 0&&!Object.is(e,t.const)){i.push({path:n,message:`must equal ${JSON.stringify(t.const)}`});return}if(t.enum&&!t.enum.some(a=>Object.is(a,e))){i.push({path:n,message:`must be one of ${t.enum.map(String).join(", ")}`});return}if(t.type==="object"){if(!Pe(e)){i.push({path:n,message:`must be an object; received ${N(e)}`});return}let a=t.properties??{};for(let o of t.required??[])e[o]===void 0&&i.push({path:ge(n,o),message:"is required"});if(t.additionalProperties===!1)for(let o of Object.keys(e))o in a||i.push({path:ge(n,o),message:"unknown field"});for(let[o,d]of Object.entries(a))e[o]!==void 0&&K(e[o],d,ge(n,o),r,i);return}if(t.type==="array"){if(!Array.isArray(e)){i.push({path:n,message:`must be an array; received ${N(e)}`});return}if(t.minItems!==void 0&&e.length<t.minItems&&i.push({path:n,message:`must contain at least ${t.minItems} item(s)`}),t.uniqueItems){let a=e.map(o=>JSON.stringify(o));new Set(a).size!==a.length&&i.push({path:n,message:"must not contain duplicate items"})}t.items&&e.forEach((a,o)=>K(a,t.items,`${n}[${o}]`,r,i));return}if(t.type==="string"){if(typeof e!="string"){i.push({path:n,message:`must be a string; received ${N(e)}`});return}t.minLength!==void 0&&e.length<t.minLength&&i.push({path:n,message:`must contain at least ${t.minLength} character(s)`});return}if(t.type==="boolean"){typeof e!="boolean"&&i.push({path:n,message:`must be a boolean; received ${N(e)}`});return}if(t.type==="integer"){if(!Number.isInteger(e)){i.push({path:n,message:`must be an integer; received ${N(e)}`});return}t.minimum!==void 0&&e<t.minimum&&i.push({path:n,message:`must be at least ${t.minimum}`})}}function rt(e){return{...e,$schema:e.$schema===void 0?ye:e.$schema,schemaVersion:e.schemaVersion===void 0?"1.0":e.schemaVersion,include:e.include===void 0?["src"]:e.include,layers:e.layers===void 0?[]:e.layers,rules:e.rules===void 0?Re.map(t=>({...t})):e.rules}}function it(e,t="ark.config.json"){if(!Pe(e))throw new T(t,[{path:"$",message:`must be an object; received ${N(e)}`}]);let n=e.schemaVersion===void 0?"unversioned":null;if(e.schemaVersion!==void 0&&e.schemaVersion!=="1.0")throw new T(t,[{path:"$.schemaVersion",message:`unsupported version ${JSON.stringify(e.schemaVersion)}; expected 1.0`}]);return{candidate:rt(e),migratedFrom:n}}function me(e,t="ark.config.json"){let{candidate:n,migratedFrom:r}=it(e,t),i=[];if(K(n,Le,"$",Le,i),i.length>0)throw new T(t,i);return{config:n,migratedFrom:r}}function ve(e,t="ark.config.json"){let n;try{n=JSON.parse(e)}catch(r){throw new T(t,[{path:"$",message:`invalid JSON: ${r instanceof Error?r.message:String(r)}`}])}return me(n,t)}var _e="1.0";function b(e,t){e.push({id:`${t.classification}:${t.path}:${t.kind}`,path:t.path,classification:t.classification,message:t.message,...t.classification==="weakening"||t.classification==="judgment-required"?{nextAction:`Restore the previous protection at ${t.path}, then run ArkGate again.`}:{},...t.before===void 0?{}:{before:t.before},...t.after===void 0?{}:{after:t.after}})}function S(e){return[...new Set(e??[])].sort()}function P(e,t,n,r,i){let a=S(n),o=S(r),d=new Set(a),l=new Set(o),u=o.filter(g=>!d.has(g)),y=a.filter(g=>!l.has(g));u.length===0&&y.length===0||(u.length>0&&b(e,{kind:"added",path:t,classification:i.added,message:i.addedMessage,before:a,after:o}),y.length>0&&b(e,{kind:"removed",path:t,classification:i.removed,message:i.removedMessage,before:a,after:o}))}function Y(e,t,n,r,i,a,o){if(n===r)return;b(e,{kind:r?"enabled":"disabled",path:t,classification:r?i:i==="strengthening"?"weakening":"strengthening",message:r?a:o,before:n,after:r})}function W(e,t){let n=new Map,r=new Set;for(let i of e){let a=t(i);n.has(a)?r.add(a):n.set(a,i)}return{values:n,duplicates:[...r].sort()}}function at(e,t,n){let r=W(t,a=>a.name),i=W(n,a=>a.name);(r.duplicates.length>0||i.duplicates.length>0)&&b(e,{kind:"duplicate-layer",path:"$.layers",classification:"judgment-required",message:"Duplicate layer names make policy ownership ambiguous.",before:r.duplicates,after:i.duplicates});for(let a of[...new Set([...r.values.keys(),...i.values.keys()])].sort()){let o=r.values.get(a),d=i.values.get(a),l=`$.layers[${a}]`;if(!o&&d){b(e,{kind:"layer-added",path:l,classification:"judgment-required",message:"A layer was added; verify overlap, ownership, and rule coverage.",after:d});continue}if(o&&!d){b(e,{kind:"layer-removed",path:l,classification:"weakening",message:"Removing a layer can leave its source paths ungoverned.",before:o});continue}if(!o||!d)continue;P(e,`${l}.patterns`,o.patterns,d.patterns,{added:"strengthening",removed:"weakening",addedMessage:"Additional paths are governed by this layer.",removedMessage:"Paths were removed from this layer and may become ungoverned."}),P(e,`${l}.exclude`,o.exclude,d.exclude,{added:"weakening",removed:"strengthening",addedMessage:"Additional paths are excluded from this layer.",removedMessage:"Fewer paths are excluded from this layer."});let u=q(o),y=q(d);P(e,`${l}.forbiddenGlobals`,u.rawGlobals,y.rawGlobals,{added:"strengthening",removed:"weakening",addedMessage:"Additional forbidden globals are enforced in this layer.",removedMessage:"A forbidden-global protection was removed from this layer."}),P(e,`${l}.capabilities`,u.atoms,y.atoms,{added:"strengthening",removed:"weakening",addedMessage:"Additional ambient/import protection is enforced in this layer (coverage atoms).",removedMessage:"An ambient/import protection was lost from this layer (coverage atoms)."}),S(o.intentPrefixes).join("\0")!==S(d.intentPrefixes).join("\0")&&b(e,{kind:"intent-prefixes-changed",path:`${l}.intentPrefixes`,classification:"judgment-required",message:"Intent ownership changed and must be reviewed against publishers and consumers.",before:S(o.intentPrefixes),after:S(d.intentPrefixes)}),Y(e,`${l}.mayImportInfrastructure`,o.mayImportInfrastructure===!0,d.mayImportInfrastructure===!0,"weakening","The layer may now import infrastructure directly.","Direct infrastructure imports are no longer allowed for this layer."),Y(e,`${l}.optional`,o.optional===!0,d.optional===!0,"weakening","The layer is now optional and can be absent without a strict warning.","The layer is now required when its contract is active.")}}function st(e,t,n){let r=o=>`${o.from}->${o.to}`,i=W(t,r),a=W(n,r);(i.duplicates.length>0||a.duplicates.length>0)&&b(e,{kind:"duplicate-rule",path:"$.rules",classification:"judgment-required",message:"Duplicate rule edges make the effective verdict order-dependent.",before:i.duplicates,after:a.duplicates});for(let o of[...new Set([...i.values.keys(),...a.values.keys()])].sort()){let d=i.values.get(o),l=a.values.get(o),u=`$.rules[${o}]`;if(!d&&l){l.allowed===!1&&b(e,{kind:"deny-added",path:u,classification:"strengthening",message:"A denied dependency edge was added.",after:l});continue}if(d&&!l){d.allowed===!1&&b(e,{kind:"deny-removed",path:u,classification:"weakening",message:"A denied dependency edge was removed.",before:d});continue}if(!d||!l)continue;d.allowed!==l.allowed&&b(e,{kind:l.allowed?"deny-disabled":"deny-enabled",path:`${u}.allowed`,classification:l.allowed?"weakening":"strengthening",message:l.allowed?"A previously denied dependency edge is now allowed.":"A dependency edge is now denied.",before:d.allowed,after:l.allowed});let y=d.peerIsolation===!0,g=l.peerIsolation===!0;if(y!==g){let c=d.from===d.to&&l.from===l.to;b(e,{kind:g?"peer-isolation-enabled":"peer-isolation-disabled",path:`${u}.peerIsolation`,classification:c?g?"strengthening":"weakening":"judgment-required",message:c?g?"Cross-slice dependencies inside this layer are now denied.":"Cross-slice dependencies inside this layer are no longer denied.":"Changing peer isolation on a cross-layer edge changes the denial scope.",before:y,after:g})}S(d.sliceFolders).join("\0")!==S(l.sliceFolders).join("\0")&&b(e,{kind:"slice-folders-changed",path:`${u}.sliceFolders`,classification:"judgment-required",message:"Slice ownership folders changed and can reclassify existing dependencies.",before:S(d.sliceFolders),after:S(l.sliceFolders)})}}function ot(e,t,n){let r=t.safety??{},i=n.safety??{};for(let a of["maxTsSuppressions","maxAnyCasts"]){let o=r[a]??0,d=i[a]??0;o!==d&&b(e,{kind:d>o?"threshold-raised":"threshold-lowered",path:`$.safety.${a}`,classification:d>o?"weakening":"strengthening",message:d>o?"The safety threshold allows more violations.":"The safety threshold allows fewer violations.",before:o,after:d})}for(let a of["allowInMemory","allowDisabledPeerIsolation"])Y(e,`$.safety.${a}`,r[a]===!0,i[a]===!0,"weakening","A safety exception was enabled.","A safety exception was disabled.")}function ct(e){return e.some(t=>t.classification==="weakening")?"weakening":e.some(t=>t.classification==="judgment-required")?"judgment-required":e.some(t=>t.classification==="strengthening")?"strengthening":"neutral"}function Me(e,t){let n=[];P(n,"$.include",e.include,t.include,{added:"strengthening",removed:"weakening",addedMessage:"Additional project roots are governed.",removedMessage:"Project roots were removed from governance."}),P(n,"$.exclude",e.exclude,t.exclude,{added:"weakening",removed:"strengthening",addedMessage:"Additional project paths are excluded from governance.",removedMessage:"Fewer project paths are excluded from governance."}),P(n,"$.dynamicImportAllowlist",e.dynamicImportAllowlist,t.dynamicImportAllowlist,{added:"weakening",removed:"strengthening",addedMessage:"Additional files may use non-literal dynamic imports.",removedMessage:"Fewer files may use non-literal dynamic imports."}),Y(n,"$.excludeGenerated",e.excludeGenerated!==!1,t.excludeGenerated!==!1,"weakening","Generated source is now excluded from governance.","Generated source is now governed.");let r={off:0,soft:1,"framework-soft":1,strict:2},i=e.cyclePolicy??"strict",a=t.cyclePolicy??"strict";if(i!==a){let o=r[a]===r[i]?"judgment-required":r[a]>r[i]?"strengthening":"weakening";b(n,{kind:"cycle-policy-changed",path:"$.cyclePolicy",classification:o,message:"The cycle enforcement level changed.",before:i,after:a})}return(e.frameworkOverlay??null)!==(t.frameworkOverlay??null)&&b(n,{kind:"framework-overlay-changed",path:"$.frameworkOverlay",classification:"judgment-required",message:"The framework overlay changed and may alter effective layer matching.",before:e.frameworkOverlay??null,after:t.frameworkOverlay??null}),at(n,e.layers,t.layers),st(n,e.rules,t.rules),ot(n,e,t),n.sort((o,d)=>o.path.localeCompare(d.path)||o.id.localeCompare(d.id)),{schemaVersion:_e,classification:ct(n),findings:n}}function Ne(e,t){if(!e||e.schemaVersion!==_e||typeof e.basePolicyHash!="string"||typeof e.candidatePolicyHash!="string"||typeof e.reason!="string"||!Array.isArray(e.findingIds)||e.findingIds.some(i=>typeof i!="string")||e.reason.trim().length===0||e.basePolicyHash!==t.basePolicyHash||e.candidatePolicyHash!==t.candidatePolicyHash)return!1;let n=S(e.findingIds),r=S(t.findingIds);return n.length===r.length&&n.every((i,a)=>i===r[a])}function v(e){let t=[];for(let n of e.replace(/\\/g,"/").split("/"))!n||n==="."||(n===".."&&t.length>0&&t.at(-1)!==".."?t.pop():t.push(n));return t.join("/")}function Te(e){return e!==void 0&&/[A-Za-z0-9_$]/.test(e)}function L(e,t){for(;t<e.length&&/\s/.test(e[t]);)t+=1;return t}function H(e,t){let n=e[t];if(n!=="'"&&n!=='"')return;let r=t,i="";for(t+=1;t<e.length;t+=1){let a=e[t];if(a===n)return{value:i,offset:r,excerpt:e.slice(r,t+1)};a==="\\"&&t+1<e.length?(i+=e[t+1],t+=1):i+=a}}function E(e,t,n){return e.startsWith(t,n)&&!Te(e[n-1])&&!Te(e[n+t.length])}function lt(e,t){if(t=L(e,t+6),e[t]==="(")return H(e,L(e,t+1));let n=!1;if(E(e,"type",t)){let i=L(e,t+4);e[i]!==","&&!E(e,"from",i)&&(n=!0)}let r=De(e,t,!0);return r&&n?{...r,typeOnly:!0}:r}function dt(e,t){t=t+6;let n=L(e,t),r=!1;if(E(e,"type",n)){let a=L(e,n+4);(e[a]==="{"||e[a]==="*")&&(r=!0)}let i=De(e,t,!1);return i&&r?{...i,typeOnly:!0}:i}function De(e,t,n){for(;t<e.length;t+=1){if(e[t]===";")return;if(E(e,"from",t))return H(e,L(e,t+4));if(n&&(e[t]==="'"||e[t]==='"'))return H(e,t);if(t>0&&(E(e,"import",t)||E(e,"export",t)))return}}function pt(e,t){for(t+=1;t<e.length;t+=1){let n=e[t];if(n==="\\")t+=1;else if(n==="`")return t}return e.length}function ft(e,t){let n=t-1;for(;n>=0&&/\s/.test(e[n]);)n-=1;if(e[n]===".")return;let r=L(e,t+7);if(e[r]!=="(")return;r=L(e,r+1);let i=H(e,r);return i?{...i,requireCall:!0}:void 0}function ut(e){let t=[];for(let n=0;n<e.length;n+=1){let r=e[n];if(r==="/"&&e[n+1]==="/"){if(n=e.indexOf(`
8
+ `,n+2),n<0)break;continue}if(r==="/"&&e[n+1]==="*"){let a=e.indexOf("*/",n+2);if(a<0)break;n=a+1;continue}if(r==="`"){n=pt(e,n);continue}if(r==="'"||r==='"'){let a=H(e,n);a&&(n=a.offset+a.excerpt.length-1);continue}let i=r==="i"&&E(e,"import",n)?lt(e,n):r==="e"&&E(e,"export",n)?dt(e,n):r==="r"&&E(e,"require",n)?ft(e,n):void 0;i&&(t.push(i),n=i.offset+i.excerpt.length-1)}return t}function Fe(e,t){let n=[],r=[];for(let i of ut(e.content)){let a=i.value;if(i.requireCall&&a.startsWith("."))continue;if(!a.startsWith(".")){if(i.typeOnly)continue;let u=j(a);if(!u)continue;let y=e.content.slice(0,i.offset).split(`
9
+ `).length;r.push({file:e.path,symbol:a,capability:u,evidence:{kind:"import",file:e.path,line:y,excerpt:i.excerpt}});continue}let o=e.content.slice(0,i.offset).split(`
10
+ `).length,d={kind:"import",file:e.path,line:o,excerpt:i.excerpt},l=gt(e.path,a,t);n.push({from:e.path,specifier:a,to:l?.path??null,resolution:l?"resolved":"unresolved",fromLayer:e.layer,toLayer:l?.layer??null,evidence:d})}return{edges:n,capabilityUses:r}}function gt(e,t,n){let r=e.split("/");r.pop();for(let a of t.split("/"))a==="."||a===""||(a===".."?r.pop():r.push(a));let i=r.join("/");for(let a of[i,`${i}.ts`,`${i}.tsx`,`${i}.mts`,`${i}.cts`,`${i}/index.ts`,`${i}/index.tsx`]){let o=n.get(a);if(o)return o}}function je(e,t){let n=[];for(let r of e){if(!r.to||!r.fromLayer||!r.toLayer)continue;let i=G(t.rules,r.fromLayer,r.toLayer,{fromPath:r.from,toPath:r.to,layers:t.layers});i&&n.push({ruleId:`layer-dependency:${i.from}->${i.to}`,message:i.message??`${i.from} must not depend on ${i.to}.`,edge:r,evidence:r.evidence})}return n}function he(e,t){let n=typeof e=="string"?ve(e,t):me(e,t);return{...n,policyHash:$(x(n.config))}}function mt(e){let t=he(e.baseConfig,e.baseSource??"base ark.config.json"),n=he(e.candidateConfig,e.candidateSource??"candidate ark.config.json"),r=Me(t.config,n.config),i=r.findings.filter(d=>d.classification==="weakening"||d.classification==="judgment-required").map(d=>d.id).sort(),a=i.length>0,o=a&&Ne(e.acknowledgement,{basePolicyHash:t.policyHash,candidatePolicyHash:n.policyHash,findingIds:i});return{schemaVersion:r.schemaVersion,basePolicyHash:t.policyHash,candidatePolicyHash:n.policyHash,classification:r.classification,findings:r.findings,blockingFindingIds:i,requiresAcknowledgement:a,acknowledged:o,valid:!a||o}}function J(e){let t=e.files.map(l=>{let u=v(l.path);return{path:u,content:l.content,contentHash:$(l.content),layer:_(u,e.contract.config.layers)??null}}).sort((l,u)=>l.path.localeCompare(u.path)),n=new Map(t.map(l=>[l.path,l])),r=[],i=[];for(let l of t){let u=Fe(l,n);r.push(...u.edges),i.push(...u.capabilityUses)}let a=je(r,e.contract.config),o=new Map(e.contract.config.layers.map(l=>[l.name,l])),d=new Map(e.contract.config.layers.map(l=>[l.name,new Set(z(l))]));for(let l of i){let u=n.get(l.file)?.layer;if(!u)continue;let y=o.get(u),g=ue(l.symbol,y?.forbiddenGlobals??[]);if(g){a.push({ruleId:"FORBIDDEN_GLOBAL",message:`${u} must not use module "${l.symbol}" because it is the import form of forbidden global "${g}".`,symbol:l.symbol,evidence:l.evidence});continue}d.get(u)?.has(l.capability)&&a.push({ruleId:"CAPABILITY_VIOLATION",message:`${u} denies the ${l.capability} capability; found import of "${l.symbol}".`,capability:l.capability,symbol:l.symbol,evidence:l.evidence})}return{ir:{schemaVersion:"1.0",policyHash:e.contract.policyHash,compilerOptionsHash:$(x(e.compilerOptions??{})),files:t,layers:e.contract.config.layers.map(l=>l.name),edges:r,capabilityUses:i,violations:a}}}function Ae(e){let t=new Map(e.files.map(n=>[v(n.path),n]));for(let n of e.changes){let r=v(n.path);"delete"in n&&n.delete?t.delete(r):"content"in n&&t.set(r,{path:r,content:n.content})}return J({contract:e.contract,files:[...t.values()],compilerOptions:e.compilerOptions})}function ht(e){let t=`${e.evidence.file}:${e.evidence.line}`;if(!e.edge)return`${e.ruleId} at ${t}: ${e.message}`;let n=e.edge.to??e.edge.specifier;return`${e.ruleId} at ${t}: ${e.edge.from} imports ${n}. ${e.message}`}function Ve(e){let t=0,n=new Map,r=new Map,i=new Set,a=[],o=[],d=l=>{n.set(l,t),r.set(l,t),t+=1,a.push(l),i.add(l);for(let g of[...e.get(l)??[]].sort())e.has(g)&&(n.has(g)?i.has(g)&&r.set(l,Math.min(r.get(l)??0,n.get(g)??0)):(d(g),r.set(l,Math.min(r.get(l)??0,r.get(g)??0))));if(r.get(l)!==n.get(l))return;let u=[],y;do{if(y=a.pop(),y===void 0)break;i.delete(y),u.push(y)}while(y!==l);u.length>1&&o.push(u.sort())};for(let l of[...e.keys()].sort())n.has(l)||d(l);return o.sort((l,u)=>l[0].localeCompare(u[0])).map(l=>({ruleId:"CIRCULAR_DEPENDENCY",file:l[0],line:1,target:l.join(" \u2192 "),message:`Circular dependency among ${l.length} files: ${l.join(" \u2192 ")} \u2192 ${l[0]}.`,cycleKind:"value"}))}function be(e){let t=e.contentViolations.map(a=>({...a})),n=(e.warnings??[]).map(a=>({...a})),r=new Map(e.files.map(a=>[a,new Set]));for(let a of e.edges){if(a.to&&a.to!==a.from&&!a.typeOnly&&r.has(a.from)&&r.get(a.from)?.add(a.to),!a.to||!a.toLayer)continue;let o=G(e.rules,a.fromLayer,a.toLayer,{fromPath:a.from,toPath:a.to,layers:e.config.layers});if(!o)continue;let d=!!o.peerIsolation;t.push({ruleId:"LAYER_IMPORT_VIOLATION",file:a.from,line:a.line,fromLayer:a.fromLayer,toLayer:a.toLayer,target:a.to,...a.typeOnly?{typeOnly:!0}:{},...a.targetTypeOnlyExports?{targetTypeOnlyExports:!0}:{},...a.sourcePureTypeModule?{sourcePureTypeModule:!0}:{},...a.namedBindingsTypeOnly?{namedBindingsTypeOnly:!0}:{},...!d&&a.portProofEligible?{portProofEligible:!0}:{},...a.kind?{edgeKind:a.kind}:{},...d?{peerIsolation:!0}:{},message:o.message??(d?`${a.fromLayer} must not ${a.kind} another slice of ${a.toLayer} (${a.from} \u2192 ${a.to}). Extract shared code or use events/ports across slices.`:`${a.fromLayer} must not ${a.kind} ${a.toLayer}.`)})}let i=String(e.config.cyclePolicy??"strict").toLowerCase();if(i!=="off"){let a=Ve(r);i==="soft"||i==="framework-soft"?n.push(...a.map(o=>({...o,message:`${o.message} (soft cycle policy \u2014 advisory only; set cyclePolicy: "strict" to fail the check)`,failsStrict:!1}))):t.push(...a)}return{violations:t,warnings:n,safety:e.safety}}function He(e){switch(e.ruleId){case"LAYER_IMPORT_VIOLATION":return e.typeOnly||e.targetTypeOnlyExports||e.namedBindingsTypeOnly?"Move the referenced type to a mutually allowed layer, use `import type`, then preflight again.":e.peerIsolation?"Extract the shared dependency to a shared layer, then preflight again.":`Define a port in ${e.fromLayer??"the source layer"}, inject the ${e.toLayer??"outer-layer"} implementation, then preflight again.`;case"FORBIDDEN_GLOBAL":return`Inject ${e.target??"the capability"} through a port, then preflight again.`;case"CAPABILITY_VIOLATION":return`Define a ${String(e.capability??"capability")} port in ${e.fromLayer??"the walled layer"}, bind the implementation outside it, then preflight again.`;case"CIRCULAR_DEPENDENCY":return"Extract the shared dependency into a third module, then preflight again.";case"RAW_EVENT_PUBLISH":return"Publish through a registered intent creator, then run Ark again.";case"PUBLISH_MISSING_SOURCE":return"Add metadata.source to the publish call, then run Ark again.";default:return`Resolve ${typeof e.ruleId=="string"&&e.ruleId.length>0?e.ruleId:"ARK_UNKNOWN"} without weakening ark.config.json, then run Ark again.`}}function Ge(e){return $(x(e.map(({path:t,contentHash:n})=>({path:t,contentHash:n}))))}function At(e){let t=J(e),n=new Map(t.ir.files.map(s=>[s.path,s])),r=[],i=new Set,a=[];for(let s of e.changes){let p=s.path.replace(/\\/g,"/"),f=v(s.path);if(!f||f===".."||f.startsWith("../")||p.startsWith("/")||/^[A-Za-z]:\//.test(p)||p.includes("\0")){a.push({ruleId:"INVALID_CHANGE_PATH",file:"<change-set>",line:1,message:"Every change requires a safe, non-empty project-relative path."});continue}if(i.has(f)){a.push({ruleId:"DUPLICATE_CHANGE_PATH",file:f,line:1,message:`The atomic change set contains more than one operation for ${f}.`});continue}i.add(f),"delete"in s&&s.delete&&!n.has(f)&&a.push({ruleId:"DELETE_TARGET_MISSING",file:f,line:1,message:`Cannot delete ${f} because it is not present in the supplied base tree.`}),r.push("delete"in s&&s.delete?{path:f,delete:!0}:{path:f,content:"content"in s?s.content:""})}e.changes.length===0&&a.push({ruleId:"CHANGE_SET_EMPTY",file:"<change-set>",line:1,message:"Atomic preflight requires at least one create, update, or delete."});let o=Ae({...e,changes:r}),d=new Map(o.ir.files.map(s=>[s.path,s])),l=o.ir.violations.filter(s=>s.ruleId==="CAPABILITY_VIOLATION"||s.ruleId==="FORBIDDEN_GLOBAL").map(s=>{let p=d.get(s.evidence.file)?.layer;return{ruleId:s.ruleId,file:s.evidence.file,line:s.evidence.line,target:s.symbol,...s.capability?{capability:s.capability}:{},...p?{fromLayer:p}:{},edgeKind:"import",message:s.message}}),u=be({config:e.contract.config,rules:e.contract.config.rules,files:o.ir.files.map(s=>s.path),contentViolations:l,edges:o.ir.edges.filter(s=>!!s.fromLayer).map(s=>({from:s.from,fromLayer:s.fromLayer,...s.to?{to:s.to}:{},...s.toLayer?{toLayer:s.toLayer}:{},line:s.evidence.line,kind:"import"}))}),y=r.map(s=>{let p=v(s.path),f=n.get(p),m=d.get(p);return{path:p,operation:"delete"in s&&s.delete?"delete":f?"update":"create",...f?{beforeContentHash:f.contentHash}:{},...m?{candidateContentHash:m.contentHash}:{}}}).sort((s,p)=>s.path.localeCompare(p.path)),g=[...a,...u.violations].map(s=>({...s,nextAction:He(s)})),c=e.changeMap?re({changeMap:e.changeMap,changes:y,baseDependencies:t.ir.edges.flatMap(s=>s.to?[{from:s.from,to:s.to}]:[]),candidateDependencies:o.ir.edges.flatMap(s=>s.to?[{from:s.from,to:s.to}]:[])}):void 0;return{schemaVersion:"1.0",valid:g.length===0&&(c?.structurallyConverged??!0),readOnly:!0,policyHash:e.contract.policyHash,compilerOptionsHash:o.ir.compilerOptionsHash,baseTreeHash:Ge(t.ir.files),candidateTreeHash:Ge(o.ir.files),...e.changeMap?{changeMapHash:e.changeMap.hash}:{},...c?{convergence:c}:{},changes:y,violations:g,warnings:u.warnings}}function I(e,t,n={}){return{ruleId:e,message:t,...n}}function bt(e){let{config:t,rules:n,files:r,manifest:i}=e,a=[];if(t.dynamicImportAllowlist!==void 0&&(!Array.isArray(t.dynamicImportAllowlist)||t.dynamicImportAllowlist.some(s=>typeof s!="string"))&&a.push(I("CONFIG_INVALID_DYNAMIC_IMPORT_ALLOWLIST","dynamicImportAllowlist must be an array of file globs.")),t.safety!==void 0&&(t.safety===null||typeof t.safety!="object"||Array.isArray(t.safety)))a.push(I("CONFIG_INVALID_SAFETY","safety must be an object."));else if(t.safety)for(let s of["maxTsSuppressions","maxAnyCasts"]){let p=t.safety[s];p!==void 0&&(!Number.isInteger(p)||p<0)&&a.push(I("CONFIG_INVALID_SAFETY_THRESHOLD",`safety.${s} must be a non-negative integer.`))}let o=Array.isArray(t.layers)?t.layers:[],d=Array.isArray(i?.architecture?.layers)?i.architecture.layers:[],l=new Set([...o.map(s=>s.name).filter(Boolean),...d.map(s=>s.name).filter(s=>!!s)]);o.length===0&&a.push(I("CONFIG_NO_LAYERS","No file layers are configured; ark-check cannot classify files for import-boundary enforcement."));let u=new Set,y=new Set;for(let s of o){if(!s.name){a.push(I("CONFIG_LAYER_WITHOUT_NAME","A configured layer is missing a name."));continue}u.has(s.name)&&y.add(s.name),u.add(s.name),s.forbiddenGlobals!==void 0&&(!Array.isArray(s.forbiddenGlobals)||s.forbiddenGlobals.some(f=>typeof f!="string"))&&a.push(I("CONFIG_INVALID_FORBIDDEN_GLOBALS",`Layer "${s.name}" has an invalid forbiddenGlobals value; expected an array of strings (e.g. ["fetch", "Date.now"]). The entry is ignored.`,{layer:s.name}));let p=Array.isArray(s.patterns)?s.patterns:[];if(p.length===0){a.push(I("CONFIG_LAYER_WITHOUT_PATTERNS",`Layer "${s.name}" has no file patterns and will never classify files.`,{layer:s.name}));continue}for(let f of p){let m;try{m=D(f)}catch(h){a.push(I("CONFIG_INVALID_LAYER_PATTERN",`Layer "${s.name}" has an invalid pattern "${f}": ${h instanceof Error?h.message:String(h)}`,{layer:s.name,pattern:f}));continue}!r.some(h=>m.test(h))&&!s.optional&&a.push(I("CONFIG_LAYER_PATTERN_NO_MATCHES",`Layer "${s.name}" pattern "${f}" matched no included files.`,{layer:s.name,pattern:f,failsStrict:!1}))}}for(let s of y)a.push(I("CONFIG_DUPLICATE_LAYER",`Layer "${s}" is configured more than once.`,{layer:s}));if(l.size>0)for(let s of n??[])s.from&&!l.has(s.from)&&a.push(I("CONFIG_RULE_UNKNOWN_FROM_LAYER",`Rule references unknown source layer "${s.from}".`,{fromLayer:s.from,toLayer:s.to})),s.to&&!l.has(s.to)&&a.push(I("CONFIG_RULE_UNKNOWN_TO_LAYER",`Rule references unknown target layer "${s.to}".`,{fromLayer:s.from,toLayer:s.to}));let g=new Set;if(o.length>1)for(let s of r){let p=-1,f=[];for(let m of o)for(let h of m.patterns??[]){if(!D(h).test(s))continue;let A=Q(h);A>p?(p=A,f=[m.name]):A===p&&!f.includes(m.name)&&f.push(m.name)}f.length>1&&g.add([...f].sort().join(" + "))}g.size>0&&a.push(I("CONFIG_AMBIGUOUS_LAYERS",`Some files match multiple layers at equal specificity; classification falls back to declaration order. Disambiguate the overlapping patterns: ${[...g].join(", ")}.`,{pairs:[...g]}));let c=r.filter(s=>!_(s,o));return c.length>0&&a.push(I("CONFIG_UNCLASSIFIED_FILES",`${c.length} included source file(s) are not matched by any configured layer; ark-check will not enforce import rules for those source files.`,{count:c.length,samples:c.slice(0,5)})),a}export{fe as AMBIENT_CAPABILITY_ENTRIES,de as CAPABILITY_IDS,ce as SOURCE_POLICY_MESSAGES,Ze as ambientCoveredByForbiddenGlobals,re as analyzeArchitectureConvergence,Ae as analyzeChange,mt as analyzePolicyDelta,J as analyzeProject,V as capabilityForAmbientName,j as capabilityForModuleSpecifier,Je as classifyPublishFacts,bt as collectAnalysisConfigWarnings,Qe as collectCapabilityUses,oe as collectForbiddenCapabilityUses,Ve as detectArchitectureCycles,z as effectiveCapabilityDeny,be as evaluateArchitectureGraph,ht as explainViolation,se as extractSemanticDependencies,ue as forbiddenGlobalForModuleSpecifier,Ke as loadArchitectureChangeMap,he as loadContract,$e as looksLikeArkIntent,Xe as lowerForbiddenGlobal,q as loweredLayerCoverage,At as preflightChange};
@@ -22,6 +22,7 @@ import {
22
22
  typeOnlyExportNames,
23
23
  } from './ast-scan.mjs';
24
24
  import { provePortProofInject } from './port-proof.mjs';
25
+ import { summarizeParseHealth } from './parse-health.mjs';
25
26
  import {
26
27
  intentLayersFromManifest,
27
28
  layerForIntent,
@@ -35,6 +36,7 @@ import {
35
36
  effectiveCapabilityDeny,
36
37
  evaluateArchitectureGraph,
37
38
  extractSemanticDependencies,
39
+ forbiddenGlobalForModuleSpecifier,
38
40
  } from './analysis-engine.mjs';
39
41
  import { normalize } from './scan-files.mjs';
40
42
  import { collectSafetyDiagnostics } from './safety-diagnostics.mjs';
@@ -80,8 +82,10 @@ export function scanSourceFile(ts, root, config, rules, manifestIntentLayers, fi
80
82
  for (const use of collectCapabilityUses(ts, sourceFile)) {
81
83
  if (!capabilityDeny.has(use.capability)) continue;
82
84
  if (
83
- use.source === 'ambient-global' &&
84
- ambientCoveredByForbiddenGlobals(use.symbol, forbiddenGlobals)
85
+ (use.source === 'ambient-global' &&
86
+ ambientCoveredByForbiddenGlobals(use.symbol, forbiddenGlobals)) ||
87
+ (use.source === 'import-based' &&
88
+ forbiddenGlobalForModuleSpecifier(use.symbol, forbiddenGlobals))
85
89
  ) {
86
90
  continue;
87
91
  }
@@ -192,6 +196,7 @@ export function scanSourceFile(ts, root, config, rules, manifestIntentLayers, fi
192
196
  return {
193
197
  contentViolations: violations,
194
198
  edges,
199
+ parseDiagnosticCount: sourceFile.parseDiagnostics?.length ?? 0,
195
200
  exportsOnlyTypes: sourceFileExportsOnlyTypes(ts, sourceFile),
196
201
  typeOnlyExportNames: typeOnlyExportNames(ts, sourceFile),
197
202
  hasTopLevelSideEffects: sourceFileHasTopLevelSideEffects(ts, sourceFile),
@@ -210,7 +215,7 @@ export function runArchitectureScan({ root, config, manifest, rules, files, ts,
210
215
  const warnings = collectConfigWarnings(root, config, files, rules, manifest);
211
216
  const safety = collectSafetyDiagnostics(ts, root, config, files);
212
217
  warnings.push(...safety.warnings);
213
- const cacheKey = args.noCache ? undefined : scanCacheKey(root, args);
218
+ const cacheKey = args.noCache ? undefined : scanCacheKey(root, args, ts.version);
214
219
  const cachedFiles = cacheKey ? loadScanCache(root, cacheKey) : undefined;
215
220
  const nextCacheFiles = {};
216
221
 
@@ -223,7 +228,10 @@ export function runArchitectureScan({ root, config, manifest, rules, files, ts,
223
228
  const fileKey = `${stat.mtimeMs}:${stat.size}`;
224
229
  const cached = cachedFiles?.[relFile];
225
230
  const entry =
226
- cached && cached.fileKey === fileKey
231
+ cached &&
232
+ cached.fileKey === fileKey &&
233
+ Number.isSafeInteger(cached.parseDiagnosticCount) &&
234
+ cached.parseDiagnosticCount >= 0
227
235
  ? cached
228
236
  : {
229
237
  fileKey,
@@ -242,8 +250,36 @@ export function runArchitectureScan({ root, config, manifest, rules, files, ts,
242
250
  }
243
251
 
244
252
  const engineEdges = [];
253
+ // Y08 derives exact forbidden-global module duals from dependency facts
254
+ // already stored in every current cache entry. This stays downstream of the
255
+ // cache: no second AST scan, no cache-shape/version bump, and warm pre-Y08
256
+ // entries gain the verdict immediately.
257
+ const forbiddenModuleDualViolations = [];
258
+ const forbiddenModuleDualKeys = new Set();
245
259
  for (const { file, sourceLayer, relFile, entry } of scanned) {
260
+ const layerConfig = config.layers.find((layer) => layer.name === sourceLayer);
261
+ const forbiddenGlobals = Array.isArray(layerConfig?.forbiddenGlobals)
262
+ ? layerConfig.forbiddenGlobals.filter((item) => typeof item === 'string')
263
+ : [];
246
264
  for (const edge of entry.edges) {
265
+ if (!edge.typeOnly) {
266
+ const forbiddenGlobal = forbiddenGlobalForModuleSpecifier(
267
+ edge.specifier,
268
+ forbiddenGlobals
269
+ );
270
+ if (forbiddenGlobal) {
271
+ forbiddenModuleDualKeys.add(`${relFile}\0${edge.specifier}`);
272
+ forbiddenModuleDualViolations.push({
273
+ ruleId: 'FORBIDDEN_GLOBAL',
274
+ file: relFile,
275
+ line: edge.line,
276
+ fromLayer: sourceLayer,
277
+ target: edge.specifier,
278
+ edgeKind: edge.kind,
279
+ message: `${sourceLayer} must not use module "${edge.specifier}" because it is the import form of forbidden global "${forbiddenGlobal}".`,
280
+ });
281
+ }
282
+ }
247
283
  const target = resolveImport(
248
284
  ts,
249
285
  edge.specifier,
@@ -309,13 +345,30 @@ export function runArchitectureScan({ root, config, manifest, rules, files, ts,
309
345
 
310
346
  if (cacheKey) saveScanCache(root, cacheKey, nextCacheFiles);
311
347
 
312
- return evaluateArchitectureGraph({
313
- config,
314
- rules,
315
- files: scanned.map(({ relFile }) => relFile),
316
- contentViolations: scanned.flatMap(({ entry }) => entry.contentViolations),
317
- edges: engineEdges,
318
- warnings,
319
- safety: safety.report,
320
- });
348
+ // A cache written before Y08 can retain the overlapping capability-wall
349
+ // finding for this import. Filter by file+specifier (not line: D7 explicitly
350
+ // does not depend on cross-engine line anchors), then let FORBIDDEN_GLOBAL be
351
+ // the one declared-policy voice.
352
+ const contentViolations = scanned
353
+ .flatMap(({ entry }) => entry.contentViolations)
354
+ .filter(
355
+ (violation) =>
356
+ !(
357
+ violation.ruleId === 'CAPABILITY_VIOLATION' &&
358
+ forbiddenModuleDualKeys.has(`${violation.file}\0${violation.target}`)
359
+ )
360
+ );
361
+
362
+ return {
363
+ ...evaluateArchitectureGraph({
364
+ config,
365
+ rules,
366
+ files: scanned.map(({ relFile }) => relFile),
367
+ contentViolations: [...contentViolations, ...forbiddenModuleDualViolations],
368
+ edges: engineEdges,
369
+ warnings,
370
+ safety: safety.report,
371
+ }),
372
+ parseHealth: summarizeParseHealth(scanned),
373
+ };
321
374
  }
@@ -238,10 +238,8 @@ export function mcpJson(root) {
238
238
  }, null, 2)}\n`;
239
239
  }
240
240
 
241
- // Sample for docs/ `ark-check --install-agent-gates --tools codex` auto-merges the real
242
- // block (with absolute paths) into ~/.codex/config.toml. This copy is a reference only, so
243
- // it flags the two gotchas of hand-editing the global config: absolute paths (config.toml is
244
- // loaded without the project as cwd) and the required restart.
241
+ // Optional home fallback reference. Normal Codex installs write the project-scoped
242
+ // `.codex/config.toml`; `--codex-home` is for older clients or an explicit global binding.
245
243
  export function codexTomlSnippet(root) {
246
244
  const { command, args } = execCommandParts(root, PREFERRED_MCP_BIN, [
247
245
  '--root',
@@ -250,10 +248,9 @@ export function codexTomlSnippet(root) {
250
248
  '/absolute/path/to/project/ark.config.json',
251
249
  ]);
252
250
  const argsToml = args.map((value) => `"${value}"`).join(', ');
253
- return `# Add to ~/.codex/config.toml (or $CODEX_HOME/config.toml), then RESTART Codex
254
- # it does not hot-load MCP servers. Use ABSOLUTE paths: config.toml is global, so
255
- # "." would resolve against Codex's launch dir, not this project. Prefer:
256
- # ark-check --install-agent-gates --tools codex (auto-merges the absolute paths)
251
+ return `# Optional global fallback for older Codex clients. Modern Codex uses the generated
252
+ # project-scoped .codex/config.toml instead. If you install this fallback manually, restart
253
+ # Codex and keep ABSOLUTE paths because $CODEX_HOME/config.toml is global.
257
254
  [mcp_servers.ark]
258
255
  command = "${command}"
259
256
  args = [${argsToml}]