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.
- package/CHANGELOG.md +69 -0
- package/README.md +4 -5
- package/bin/ark-check.mjs +3 -3
- package/bin/ark-layer-match.mjs +2 -1
- package/bin/ark.mjs +2 -2
- package/bin/lib/agent-gates.mjs +2 -0
- package/bin/lib/analysis-engine.mjs +6 -6
- package/bin/lib/architecture-scan.mjs +66 -13
- package/bin/lib/ci-and-commands.mjs +5 -8
- package/bin/lib/codex-home.mjs +21 -6
- package/bin/lib/design-smells.mjs +67 -14
- package/bin/lib/doctor-advisories.mjs +20 -5
- package/bin/lib/doctor-plan.mjs +5 -7
- package/bin/lib/gate-files.mjs +1 -1
- package/bin/lib/hook-templates.mjs +21 -1
- package/bin/lib/html-report-advisories.mjs +59 -0
- package/bin/lib/install-migrate.mjs +26 -11
- package/bin/lib/mcp-adoption.mjs +21 -4
- package/bin/lib/parse-health.mjs +74 -0
- package/bin/lib/reshape-decisions.mjs +284 -0
- package/bin/lib/ts-resolve.mjs +3 -2
- package/bin/lib/write-path-capabilities.mjs +6 -0
- package/dist/eslint/index.cjs +2 -2
- package/dist/eslint/index.js +2 -2
- package/dist/index.cjs +7 -7
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +6 -6
- package/docs/agent-guide.md +34 -3
- package/docs/ai-gates.md +21 -18
- package/docs/configuration.md +6 -0
- package/docs/enthusiast/how-to-agent-gates.md +1 -1
- package/docs/package-surface.md +6 -5
- package/docs/typescript-support.md +9 -0
- package/package.json +2 -1
- package/server.json +2 -2
- package/templates/skills/ark-autopilot.md +12 -0
- package/templates/skills/ark-explore.md +8 -1
- package/templates/skills/ark-fix.md +11 -1
- package/templates/skills/ark-loop.md +14 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
var ct="3.6.0";var lt="1.1";function k(e){return typeof e=="string"&&e.length>0?e:void 0}function Te(e,t){return Number.isInteger(e)&&Number(e)>0?Number(e):t}function pt(e,t,n){return e==="LAYER_IMPORT_VIOLATION"?t.typeOnly||n.targetTypeOnlyExports===!0||n.namedBindingsTypeOnly===!0?"Move the referenced type to a mutually allowed layer, use `import type`, then preflight again.":n.peerIsolation===!0?"Extract the shared dependency to a shared layer, then preflight again.":`Define a port in ${t.fromLayer??"the source layer"}, inject the ${t.toLayer??"outer-layer"} implementation, then preflight again.`:e==="FORBIDDEN_GLOBAL"?`Inject ${t.target??"the capability"} through a port, then preflight again.`:e==="CAPABILITY_VIOLATION"?`Define a ${k(n.capability)??"capability"} port in ${t.fromLayer??"the walled layer"}, bind the implementation outside it, then preflight again.`:e==="CIRCULAR_DEPENDENCY"?"Extract the shared dependency into a third module, then preflight again.":e==="RAW_EVENT_PUBLISH"?"Publish through a registered intent creator, then run Ark again.":e==="PUBLISH_MISSING_SOURCE"?"Add metadata.source to the publish call, then run Ark again.":`Resolve ${e} without weakening ark.config.json, then run Ark again.`}function ge(e,t="error"){let n=k(e.ruleId)??k(e.code)??"ARK_UNKNOWN",r=e.severity==="warning"?"warning":t,i={...k(e.target)?{target:k(e.target)}:{},...k(e.fromLayer)?{fromLayer:k(e.fromLayer)}:{},...k(e.toLayer)?{toLayer:k(e.toLayer)}:{},...typeof e.typeOnly=="boolean"?{typeOnly:e.typeOnly}:{}};return{ruleId:n,severity:r,message:k(e.message)??n,location:{file:k(e.file)??"<unknown>",line:Te(e.line,1),column:Te(e.column,1)},evidence:i,nextAction:k(e.nextAction)??pt(n,i,e)}}function dt(e){return{schemaVersion:"1.1",valid:e.valid,diagnostics:[...(e.violations??[]).map(t=>ge(t,"error")),...(e.warnings??[]).map(t=>ge(t,"warning"))]}}var ft={$schema:"https://json-schema.org/draft/2020-12/schema",$id:"https://unpkg.com/arkgate@2/schemas/ark.analysis-result.schema.json",title:"ArkGate analysis result",type:"object",additionalProperties:!1,required:["schemaVersion","valid","diagnostics"],properties:{schemaVersion:{const:"1.1"},valid:{type:"boolean"},diagnostics:{type:"array",items:{type:"object",additionalProperties:!1,required:["ruleId","severity","message","location","evidence"],properties:{ruleId:{type:"string",minLength:1},severity:{enum:["error","warning"]},message:{type:"string",minLength:1},location:{type:"object",additionalProperties:!1,required:["file","line","column"],properties:{file:{type:"string",minLength:1},line:{type:"integer",minimum:1},column:{type:"integer",minimum:1}}},evidence:{type:"object",additionalProperties:!1,properties:{target:{type:"string"},fromLayer:{type:"string"},toLayer:{type:"string"},typeOnly:{type:"boolean"}}},nextAction:{type:"string",minLength:1}}}}}};var he=Object.freeze(["network","filesystem","clock","randomness","environment","process","persistence"]),Ae=Object.freeze({fetch:"network",XMLHttpRequest:"network",Date:"clock","Date.now":"clock","Math.random":"randomness","process.env":"environment",process:"process"}),be=Object.freeze(Object.keys(Ae).sort()),me=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 U(e){if(!e||e.startsWith(".")||e.startsWith("/"))return null;let t=me[e];if(t)return t;let n=e.indexOf("/");if(n<0)return null;let r=e.slice(0,n),i=me[r];if(i)return i;let a=e.indexOf("/",n+1);return a<0?null:me[e.slice(0,a)]??null}function X(e){let t=e.split(".");for(let n=t.length;n>=1;n-=1){let r=t.slice(0,n).join("."),i=Ae[r];if(i)return i}return null}function ee(e){if(e?.pure===!0)return[...he].sort();let n=(e?.capabilities?.deny??[]).filter(r=>he.includes(r));return[...new Set(n)].sort()}function Ce(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 te(e){let t=new Set,n=new Set,r=Object.keys(Ae);for(let i of e?.forbiddenGlobals??[]){let a=r.filter(s=>s===i||s.startsWith(`${i}.`));if(a.length===0)n.add(i);else for(let s of a)t.add(`ambient:${s}`)}for(let i of ee(e)){t.add(`import:${i}`);for(let a of r)X(a)===i&&t.add(`ambient:${a}`)}return{atoms:[...t].sort(),rawGlobals:[...n].sort()}}var De=new Map;function Fe(e){return/[.*+?^${}()|[\]\\]/.test(e)?`\\${e}`:e}function Ie(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 ut(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 B(e){let t=De.get(e);if(t)return t;let n=Ie(e),r=ut(n),i="",a=0;for(let c=0;c<n.length;c+=1){let p=n[c];p==="\\"&&c+1<n.length?(i+=Fe(n[c+1]),c+=1):p==="*"?n[c+1]==="*"?n[c+2]==="/"?(i+="(?:.*/)?",c+=2):(i+=".*",c+=1):i+="[^/]*":p==="?"?i+="[^/]":p==="{"&&r?(i+="(?:",a+=1):p==="}"&&r&&a>0?(i+=")",a-=1):p===","&&r&&a>0?i+="|":i+=Fe(p)}let s=new RegExp(`^${i}$`);return De.set(e,s),s}function xe(e){let t=Ie(String(e)),r=t.split("*")[0].split("/").filter(Boolean).length,i=t.replace(/\*/g,"").length;return r*1e4+i}function z(e,t){let n=String(e).split(/[/\\]/).join("/"),r,i=-1;for(let a of t??[])if(!(a.exclude??[]).some(s=>B(s).test(n))){for(let s of a.patterns??[])if(B(s).test(n)){let c=xe(s);c>i&&(i=c,r=a.name)}}return r}function je(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 yt(e){let t=new Set;for(let n of e??[]){let i=Ie(String(n)).split("/").filter(Boolean);for(let a=0;a<i.length;a+=1){let s=i[a];if((s==="**"||s==="*")&&a>0){let c=i[a-1];c&&!c.includes("*")&&!c.includes("{")&&!c.includes("}")&&t.add(c)}}}return[...t]}function gt(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 yt(r?.patterns)}function v(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,s=r?.toPath;if(!a||!s)continue;let c=gt(i,t,r?.layers);if(c.length===0)continue;let p=je(a,c),y=je(s,c);if(!p||!y)continue;if(p!==y)return i;continue}if(t!==n)return i}}function D(e,t){return t&&e.isStringLiteralLike(t)?t.text:void 0}function He(e,t){return e.getLineAndCharacterOfPosition(t.getStart(e)).line+1}function Ve(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 Ue(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 Se(e,t){try{return e.getSymbolAtLocation(t)}catch{return}}function ke(e,t,n,r){let i=r.parent&&e.isShorthandPropertyAssignment(r.parent)&&r.parent.name===r,a;try{a=i?t.getShorthandAssignmentValueSymbol(r.parent):Se(t,r)}catch{a=void 0}return!!a?.declarations?.some(s=>s.getSourceFile().fileName===n.fileName)}function F(e,t){let n,r=[],i=(s,c,p,y=!1)=>r.push({specifier:p,kind:c,line:He(t,s),typeOnly:y,unresolved:p===void 0,node:s}),a=s=>{if(e.isImportDeclaration(s))i(s,"import",D(e,s.moduleSpecifier),Ve(e,s));else if(e.isExportDeclaration(s)&&s.moduleSpecifier)i(s,"export",D(e,s.moduleSpecifier),Ve(e,s));else if(e.isImportEqualsDeclaration(s)&&e.isExternalModuleReference(s.moduleReference))i(s,"require",D(e,s.moduleReference.expression));else if(e.isCallExpression(s)){let c=s.expression.kind===e.SyntaxKind.ImportKeyword,y=e.isIdentifier(s.expression)&&s.expression.text==="require"&&!ke(e,n??(n=Ue(e,t)),t,s.expression);(c||y)&&i(s,y?"require":"dynamic-import",D(e,s.arguments[0]))}e.forEachChild(s,a)};return a(t),r}function mt(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=D(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 ht(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 Ge(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 j(e,t,n){if(n.length===0)return[];let r=new Set(n),i=Ue(e,t),a=new Map,s=new Set;for(let l of t.statements)if(e.isVariableStatement(l))for(let o of l.declarationList.declarations)e.isIdentifier(o.name)&&s.add(o.name.text);let c=l=>{let o=mt(e,l);if(!o)return;let g=Se(i,o.root),f=g?a.get(g):void 0;return f?[...f,...o.segments.slice(1)]:ke(e,i,t,o.root)||s.has(o.root.text)?void 0:o.segments};for(let l of t.statements)if(e.isVariableStatement(l))for(let o of l.declarationList.declarations){if(!o.initializer||!e.isIdentifier(o.name))continue;let g=c(o.initializer),f=Se(i,o.name);!g||!f||a.set(f,g)}let p=[],y=new Set,u=(l,o)=>{let g=He(t,o),f=`${l}:${o.getStart(t)}`;y.has(f)||(y.add(f),p.push({name:l,line:g,node:o}))},m=l=>{let o=l.parent&&(e.isPropertyAccessExpression(l.parent)||e.isElementAccessExpression(l.parent))&&l.parent.expression===l;if((e.isPropertyAccessExpression(l)||e.isElementAccessExpression(l))&&!o){let g=c(l),f=g?Ge(r,g):void 0;f&&u(f,l)}else e.isIdentifier(l)&&r.has(l.text)&&ht(e,l)&&!ke(e,i,t,l)&&u(l.text,l);if(e.isVariableDeclaration(l)&&e.isObjectBindingPattern(l.name)&&l.initializer){let g=c(l.initializer);if(g)for(let f of l.name.elements){if(!e.isIdentifier(f.name))continue;let I=f.propertyName?D(e,f.propertyName)??f.propertyName.text:f.name.text,x=Ge(r,[...g,I]);x&&u(x,l.initializer)}}e.forEachChild(l,m)};return m(t),p}function Ee(e,t){let n=[];for(let r of F(e,t)){if(r.typeOnly||!r.specifier)continue;let i=U(r.specifier);i&&n.push({capability:i,symbol:r.specifier,line:r.line,source:"import-based"})}for(let r of j(e,t,be)){let i=X(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 we={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){return/^(Domain|Application|Adapter|Workflow|Job|Presentation|Reporting|Metadata|Security|Audit|Observability|Kernel)\.[A-Za-z0-9_.]+$/.test(e)}function Le(e){if(!e.publishCall)return[];let t=[];return(e.rawIntentName!==void 0&&_(e.rawIntentName)||e.objectHasIntent)&&t.push({ruleId:"RAW_EVENT_PUBLISH",message:we.RAW_EVENT_PUBLISH}),e.arkPublishCandidate&&!e.hasSource&&t.push({ruleId:"PUBLISH_MISSING_SOURCE",message:we.PUBLISH_MISSING_SOURCE}),t}function b(e,t,n){return{ruleId:e,code:e,message:t,...n}}function V(e,t){return e.slice(0,t).split(`
|
|
2
|
-
`).length}function At(e){let t=[],n=/['"`]([A-Za-z][A-Za-z0-9_.]*)['"`]/g,r;for(;(r=n.exec(e))!==null;)t.push({value:r[1],index:r.index});return t}function bt(e){let t=[],n=[{kind:"import",re:/\bimport\s+(?:type\s+)?(?:[^'"]*?\s+from\s*)?['"]([^'"]+)['"]/g},{kind:"export",re:/\bexport\s+(?:type\s+)?[^'"]*?\s+from\s*['"]([^'"]+)['"]/g},{kind:"dynamic-import",re:/\bimport\s*\(\s*['"]([^'"]+)['"]\s*\)/g},{kind:"require",re:/\brequire\s*\(\s*['"]([^'"]+)['"]\s*\)/g}];for(let r of n){let i;for(;(i=r.re.exec(e))!==null;){let a=i.index+i[0].indexOf(i[1]),s=i[0],c=r.kind==="import"&&/\bimport\s+type\b/.test(s)||r.kind==="export"&&/\bexport\s+type\b/.test(s);t.push({value:i[1],index:a,kind:r.kind,typeOnly:c})}}return t.sort((r,i)=>r.index-i.index)}function Ct(e,t){let n=e.createSourceFile("generated.ts",t,e.ScriptTarget.Latest,!0),r=[],i=a=>{e.isStringLiteralLike(a)&&r.push({value:a.text,index:a.getStart(n)}),e.forEachChild(a,i)};return i(n),r}function It(e){let t=e.toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);return["adapter","adapters","infra","infrastructure","persistence","repository","repositories","integration","database","db"].some(n=>t.includes(n))}function xt(e){let t=e.toLowerCase();return["sequelize","prisma","typeorm","mongoose","knex"].some(n=>t===n||t.startsWith(`${n}/`))}function St(e){let t=e.toLowerCase();return["adapter","infra","persistence","repository","repositories","integration","database"].some(n=>t.includes(n))}function q(e,t){return t&&e.isStringLiteralLike(t)?t.text:void 0}function kt(e,t){if(t&&(e.isIdentifier(t)||e.isStringLiteralLike(t)))return t.text}function Be(e,t,n){if(!(!t||!e.isObjectLiteralExpression(t)))return t.properties.find(r=>!e.isPropertyAssignment(r)&&!e.isShorthandPropertyAssignment(r)?!1:kt(e,r.name)===n)}function K(e,t,n){return Be(e,t,n)!==void 0}function Y(e,t,n){let r=Be(e,t,n);return r&&e.isPropertyAssignment(r)?r.initializer:void 0}function Et(e,t){let n=Y(e,t,"metadata");return K(e,n,"source")}function ze(e,t){return t?e.isIdentifier(t)?/^[A-Z]/.test(t.text):e.isPropertyAccessExpression(t)?ze(e,t.name):!1:!1}function wt(e,t){if(!e.isCallExpression(t))return!1;let n=t.expression;return e.isPropertyAccessExpression(n)?n.name.text==="publish":e.isIdentifier(n)&&n.text==="publish"}function Lt(e,t){if(!e.isCallExpression(t))return!1;let n=t.arguments[0],r=q(e,n);return r!==void 0&&_(r)||K(e,n,"intent")||ze(e,n)}function Pt(e,t){if(!e.isCallExpression(t))return!1;let[n,r,i]=t.arguments;return Et(e,n)||K(e,r,"source")||K(e,i,"source")}function Ot(e,t){if(!e.isCallExpression(t))return;let[n,r,i]=t.arguments,a=Y(e,n,"metadata");return q(e,Y(e,a,"source"))??q(e,Y(e,r,"source"))??q(e,Y(e,i,"source"))}function Rt(e,t,n,r){let i=e.createSourceFile("generated.ts",t,e.ScriptTarget.Latest,!0),a=n,s=a?.filePath,c=a?.layer,p=[],y=m=>i.getLineAndCharacterOfPosition(m.getStart(i)).line+1,u=m=>{if(wt(e,m)){let l=m.arguments[0],o=q(e,l);for(let f of Le({publishCall:!0,rawIntentName:o,objectHasIntent:K(e,l,"intent"),arkPublishCandidate:Lt(e,m),hasSource:Pt(e,m)}))p.push(b(f.ruleId,f.message,{line:y(m),filePath:s}));let g=Ot(e,m);if(r&&c&&g&&_(g)){let f=r.resolveLayer(g);f&&f!==c&&p.push(b("PUBLISH_SOURCE_LAYER_MISMATCH",`Publish source "${g}" resolves to ${f}, but the target file is classified as ${c}.`,{line:y(m),filePath:s,target:g,fromLayer:c,toLayer:f}))}}e.forEachChild(m,u)};return u(i),p}function Ye(e={}){let t=new Set((e.intents||[]).map(a=>typeof a=="string"?a:a.name)),n=e.forbiddenPatterns||[],r=new Set(e.infrastructureLayers??[]),i=e.enforceIntentAllowlist??t.size>0;return{validate(a,s){let c=[],p=s,y=p?.filePath,u=p?.layer,m=e.typescript,l=m?m.createSourceFile(y??"generated.ts",a,m.ScriptTarget.Latest,!0):void 0,o=l?F(e.typescript,l):void 0,g=o?o.filter(d=>d.specifier!==void 0).map(d=>({value:d.specifier,index:d.node.getStart(l),kind:d.kind,typeOnly:d.typeOnly})):bt(a),f=e.typescript?Ct(e.typescript,a):At(a);if(e.typescript&&!e.allowNonLiteralDynamicImport?.(y))for(let d of o?.filter(({unresolved:h})=>h)??[]){let h=d.kind==="require";c.push(b(h?"DYNAMIC_REQUIRE_NOT_ALLOWLISTED":"DYNAMIC_IMPORT_NOT_ALLOWLISTED",`Non-literal ${h?"require call":"dynamic import"} cannot be resolved statically; add the reviewed file to dynamicImportAllowlist.`,{line:d.line,filePath:y}))}let I=u!==void 0&&(r.has(u)||St(u)),x=u!==void 0?` If "${u}" is an infrastructure layer, mark it in ark.config.json with "mayImportInfrastructure": true (or name it with an infra token like Adapters/Persistence/Repository).`:"";for(let d of n)if(d instanceof RegExp){d.lastIndex=0;let h=d.exec(a);d.lastIndex=0,h&&c.push(b("FORBIDDEN_PATTERN",`Forbidden pattern matched: ${d}`,{line:h.index===void 0?void 0:V(a,h.index),filePath:y,suggestion:"Remove infrastructure imports from domain/application layers."+x}))}else a.includes(d)&&c.push(b("FORBIDDEN_SUBSTRING",`Forbidden substring: ${d}`,{line:V(a,a.indexOf(d)),filePath:y}));for(let d of g){let h=e.resolveImportTarget?.(d.value,y)??(e.resolveImportLayer?{layer:e.resolveImportLayer(d.value,y)}:void 0),A=typeof y=="string"?e.resolveImportTarget?.(y)??(e.resolveImportLayer?{layer:u,relPath:void 0}:void 0):void 0,T=h?.layer;if(T&&u){let Q=v(e.architectureProfile?.rules,u,T,{fromPath:A?.relPath,toPath:h?.relPath,layers:e.architectureLayers});if(Q){if(d.typeOnly&&!Q.peerIsolation)continue;let ye=!!Q.peerIsolation;c.push(b("LAYER_IMPORT_VIOLATION",Q.message??(ye?`Layer "${u}" must not import across slices into "${T}".`:`Layer "${u}" must not import "${T}".`),{line:V(a,d.index),source:d.value,target:d.value,filePath:y,fromLayer:u,toLayer:T,suggestion:ye?"Extract shared code to a shared layer, or coordinate slices via events/ports \u2014 do not import across feature/context slices.":"Depend on a port/interface owned by an inner layer instead, or move this code to a layer allowed to make this import.",details:{importKind:d.kind,peerIsolation:ye,...d.typeOnly?{typeOnly:!0}:{}}}));continue}if(T!==u)continue}I||d.typeOnly||!It(d.value)&&!xt(d.value)||c.push(b("FORBIDDEN_IMPORT",`Forbidden ${d.kind} target: "${d.value}".`,{line:V(a,d.index),source:d.value,target:d.value,filePath:y,suggestion:"Route infrastructure access through an allowed adapter or port boundary."+x,details:{importKind:d.kind}}))}if(e.policies)for(let d of e.policies){let h=d.check({source:a,context:s});if(h!==!0)if(Array.isArray(h))for(let A of h)c.push(b("POLICY_VIOLATION",A.message,{filePath:y,suggestion:`Fix violation of policy "${d.name}".`}));else h===!1?c.push(b("POLICY_VIOLATION",`Policy ${d.name} failed on generated code`)):c.push(b("POLICY_VIOLATION",h.message))}if(i&&t.size>0)for(let d of f)_(d.value)&&!t.has(d.value)&&c.push(b("UNKNOWN_INTENT",`Unknown intent reference: "${d.value}"`,{line:V(a,d.index),filePath:y,target:d.value,suggestion:`Register intent "${d.value}" via defineIntent() or remove the reference.`}));if(e.architectureProfile&&u)for(let d of f){if(!_(d.value))continue;let h=e.architectureProfile.resolveLayer(d.value);if(!h)continue;let A=v(e.architectureProfile.rules,u,h);A&&c.push(b("LAYER_REFERENCE_VIOLATION",A.message??`Layer "${u}" must not reference "${h}" through "${d.value}".`,{line:V(a,d.index),filePath:y,target:d.value,fromLayer:u,toLayer:h,suggestion:"Route the dependency through an allowed intent, port, or event.",details:{rule:A}}))}if(e.extensions)for(let d of e.extensions)try{let h=d.analyze(a,s);c.push(...h)}catch(h){c.push(b("EXTENSION_ERROR",`Extension "${d.name}" failed: ${h instanceof Error?h.message:String(h)}`))}if(e.typescript&&l&&u&&e.forbiddenGlobals?.[u]?.length)try{c.push(...j(e.typescript,l,e.forbiddenGlobals[u]).map(d=>b("FORBIDDEN_GLOBAL",`${u} must not use the ambient global "${d.name}".`,{line:d.line,filePath:y,target:d.name,fromLayer:u,suggestion:"Inject the capability through a port (e.g. a Clock, IdGenerator, or HttpPort) instead of reaching for the ambient global."})))}catch(d){c.push(b("AST_ANALYZER_ERROR",`TypeScript AST analyzer failed: ${d instanceof Error?d.message:String(d)}`))}if(e.typescript&&l&&u&&e.capabilityWalls?.[u]?.length)try{let d=new Set(e.capabilityWalls[u]),h=e.forbiddenGlobals?.[u]??[];for(let A of Ee(e.typescript,l))d.has(A.capability)&&(A.source==="ambient-global"&&Ce(A.symbol,h)||c.push(b("CAPABILITY_VIOLATION",A.source==="import-based"?`${u} denies the ${A.capability} capability; found import of "${A.symbol}".`:`${u} denies the ${A.capability} capability; found ambient "${A.symbol}".`,{line:A.line,filePath:y,target:A.symbol,capability:A.capability,fromLayer:u,suggestion:"Define a small port (ClockPort, HttpPort, StoragePort) and bind the implementation in an adapter layer."})))}catch(d){c.push(b("AST_ANALYZER_ERROR",`TypeScript AST analyzer failed: ${d instanceof Error?d.message:String(d)}`))}if(e.typescript)try{c.push(...Rt(e.typescript,a,s,e.architectureProfile))}catch(d){c.push(b("AST_ANALYZER_ERROR",`TypeScript AST analyzer failed: ${d instanceof Error?d.message:String(d)}`))}return{valid:c.length===0,violations:c}}}}var $t="1.0",re="https://unpkg.com/arkgate@2/schemas/ark.config.schema.json",qe=["DomainModel","ApplicationOrchestration","PersistenceAdapters","IntegrationAdapters","WorkflowSagaEngine","BackgroundJobsScheduling","PresentationAdapters","ReportingReadModels","ExtensibilityMetadata","SecurityAuditObservability","Kernel"],vt=new Set(["PresentationAdapters->ApplicationOrchestration","ApplicationOrchestration->DomainModel","WorkflowSagaEngine->ApplicationOrchestration","WorkflowSagaEngine->DomainModel","BackgroundJobsScheduling->ApplicationOrchestration"]);function _t(){let e=[];for(let t of qe)for(let n of qe)t===n||vt.has(`${t}->${n}`)||e.push({from:t,to:n,allowed:!1});return e}var ie=_t();var O={type:"array",items:{type:"string",minLength:1},uniqueItems:!0},Oe={$schema:"https://json-schema.org/draft/2020-12/schema",$id:re,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:re,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:ie,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}}}}},H=class extends Error{issues;source;constructor(t,n){super(`Invalid ArkGate config (${t}):
|
|
1
|
+
var pt="3.7.0";var dt="1.1";function E(e){return typeof e=="string"&&e.length>0?e:void 0}function Fe(e,t){return Number.isInteger(e)&&Number(e)>0?Number(e):t}function ft(e,t,n){return e==="LAYER_IMPORT_VIOLATION"?t.typeOnly||n.targetTypeOnlyExports===!0||n.namedBindingsTypeOnly===!0?"Move the referenced type to a mutually allowed layer, use `import type`, then preflight again.":n.peerIsolation===!0?"Extract the shared dependency to a shared layer, then preflight again.":`Define a port in ${t.fromLayer??"the source layer"}, inject the ${t.toLayer??"outer-layer"} implementation, then preflight again.`:e==="FORBIDDEN_GLOBAL"?`Inject ${t.target??"the capability"} through a port, then preflight again.`:e==="CAPABILITY_VIOLATION"?`Define a ${E(n.capability)??"capability"} port in ${t.fromLayer??"the walled layer"}, bind the implementation outside it, then preflight again.`:e==="CIRCULAR_DEPENDENCY"?"Extract the shared dependency into a third module, then preflight again.":e==="RAW_EVENT_PUBLISH"?"Publish through a registered intent creator, then run Ark again.":e==="PUBLISH_MISSING_SOURCE"?"Add metadata.source to the publish call, then run Ark again.":`Resolve ${e} without weakening ark.config.json, then run Ark again.`}function me(e,t="error"){let n=E(e.ruleId)??E(e.code)??"ARK_UNKNOWN",r=e.severity==="warning"?"warning":t,i={...E(e.target)?{target:E(e.target)}:{},...E(e.fromLayer)?{fromLayer:E(e.fromLayer)}:{},...E(e.toLayer)?{toLayer:E(e.toLayer)}:{},...typeof e.typeOnly=="boolean"?{typeOnly:e.typeOnly}:{}};return{ruleId:n,severity:r,message:E(e.message)??n,location:{file:E(e.file)??"<unknown>",line:Fe(e.line,1),column:Fe(e.column,1)},evidence:i,nextAction:E(e.nextAction)??ft(n,i,e)}}function ut(e){return{schemaVersion:"1.1",valid:e.valid,diagnostics:[...(e.violations??[]).map(t=>me(t,"error")),...(e.warnings??[]).map(t=>me(t,"warning"))]}}var yt={$schema:"https://json-schema.org/draft/2020-12/schema",$id:"https://unpkg.com/arkgate@2/schemas/ark.analysis-result.schema.json",title:"ArkGate analysis result",type:"object",additionalProperties:!1,required:["schemaVersion","valid","diagnostics"],properties:{schemaVersion:{const:"1.1"},valid:{type:"boolean"},diagnostics:{type:"array",items:{type:"object",additionalProperties:!1,required:["ruleId","severity","message","location","evidence"],properties:{ruleId:{type:"string",minLength:1},severity:{enum:["error","warning"]},message:{type:"string",minLength:1},location:{type:"object",additionalProperties:!1,required:["file","line","column"],properties:{file:{type:"string",minLength:1},line:{type:"integer",minimum:1},column:{type:"integer",minimum:1}}},evidence:{type:"object",additionalProperties:!1,properties:{target:{type:"string"},fromLayer:{type:"string"},toLayer:{type:"string"},typeOnly:{type:"boolean"}}},nextAction:{type:"string",minLength:1}}}}}};var Ae=Object.freeze(["network","filesystem","clock","randomness","environment","process","persistence"]),Ce=Object.freeze({fetch:"network",XMLHttpRequest:"network",Date:"clock","Date.now":"clock","Math.random":"randomness","process.env":"environment",process:"process"}),Ie=Object.freeze(Object.keys(Ce).sort()),he=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"}),be=Object.freeze({process:Object.freeze(["process","node:process"])});function B(e){if(!e||e.startsWith(".")||e.startsWith("/"))return null;let t=he[e];if(t)return t;let n=e.indexOf("/");if(n<0)return null;let r=e.slice(0,n),i=he[r];if(i)return i;let a=e.indexOf("/",n+1);return a<0?null:he[e.slice(0,a)]??null}function D(e,t){for(let n of t)if(be[n]?.includes(e))return n;return null}function ee(e){let t=e.split(".");for(let n=t.length;n>=1;n-=1){let r=t.slice(0,n).join("."),i=Ce[r];if(i)return i}return null}function te(e){if(e?.pure===!0)return[...Ae].sort();let n=(e?.capabilities?.deny??[]).filter(r=>Ae.includes(r));return[...new Set(n)].sort()}function xe(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 ne(e){let t=new Set,n=new Set,r=Object.keys(Ce);for(let i of e?.forbiddenGlobals??[]){let a=r.filter(s=>s===i||s.startsWith(`${i}.`));if(a.length===0)n.add(i);else for(let s of a)t.add(`ambient:${s}`);for(let s of be[i]??[])t.add(`import-exact:${s}`)}for(let i of te(e)){if(t.add(`import:${i}`),i==="process")for(let a of be.process)t.add(`import-exact:${a}`);for(let a of r)ee(a)===i&&t.add(`ambient:${a}`)}return{atoms:[...t].sort(),rawGlobals:[...n].sort()}}var je=new Map;function Ge(e){return/[.*+?^${}()|[\]\\]/.test(e)?`\\${e}`:e}function Se(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 gt(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 z(e){let t=je.get(e);if(t)return t;let n=Se(e),r=gt(n),i="",a=0;for(let l=0;l<n.length;l+=1){let p=n[l];p==="\\"&&l+1<n.length?(i+=Ge(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+=Ge(p)}let s=new RegExp(`^${i}$`);return je.set(e,s),s}function ke(e){let t=Se(String(e)),r=t.split("*")[0].split("/").filter(Boolean).length,i=t.replace(/\*/g,"").length;return r*1e4+i}function K(e,t){let n=String(e).split(/[/\\]/).join("/"),r,i=-1;for(let a of t??[])if(!(a.exclude??[]).some(s=>z(s).test(n))){for(let s of a.patterns??[])if(z(s).test(n)){let l=ke(s);l>i&&(i=l,r=a.name)}}return r}function Ve(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 mt(e){let t=new Set;for(let n of e??[]){let i=Se(String(n)).split("/").filter(Boolean);for(let a=0;a<i.length;a+=1){let s=i[a];if((s==="**"||s==="*")&&a>0){let l=i[a-1];l&&!l.includes("*")&&!l.includes("{")&&!l.includes("}")&&t.add(l)}}}return[...t]}function ht(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 mt(r?.patterns)}function v(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,s=r?.toPath;if(!a||!s)continue;let l=ht(i,t,r?.layers);if(l.length===0)continue;let p=Ve(a,l),f=Ve(s,l);if(!p||!f)continue;if(p!==f)return i;continue}if(t!==n)return i}}function F(e,t){return t&&e.isStringLiteralLike(t)?t.text:void 0}function Be(e,t){return e.getLineAndCharacterOfPosition(t.getStart(e)).line+1}function He(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 ze(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 Ee(e,t){try{return e.getSymbolAtLocation(t)}catch{return}}function Le(e,t,n,r){let i=r.parent&&e.isShorthandPropertyAssignment(r.parent)&&r.parent.name===r,a;try{a=i?t.getShorthandAssignmentValueSymbol(r.parent):Ee(t,r)}catch{a=void 0}return!!a?.declarations?.some(s=>s.getSourceFile().fileName===n.fileName)}function j(e,t){let n,r=[],i=(s,l,p,f=!1)=>r.push({specifier:p,kind:l,line:Be(t,s),typeOnly:f,unresolved:p===void 0,node:s}),a=s=>{if(e.isImportDeclaration(s))i(s,"import",F(e,s.moduleSpecifier),He(e,s));else if(e.isExportDeclaration(s)&&s.moduleSpecifier)i(s,"export",F(e,s.moduleSpecifier),He(e,s));else if(e.isImportEqualsDeclaration(s)&&e.isExternalModuleReference(s.moduleReference))i(s,"require",F(e,s.moduleReference.expression),s.isTypeOnly===!0);else if(e.isCallExpression(s)){let l=s.expression.kind===e.SyntaxKind.ImportKeyword,f=e.isIdentifier(s.expression)&&s.expression.text==="require"&&!Le(e,n??(n=ze(e,t)),t,s.expression);(l||f)&&i(s,f?"require":"dynamic-import",F(e,s.arguments[0]))}e.forEachChild(s,a)};return a(t),r}function At(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=F(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 bt(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 Ue(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 G(e,t,n){if(n.length===0)return[];let r=new Set(n),i=ze(e,t),a=new Map,s=new Set;for(let c of t.statements)if(e.isVariableStatement(c))for(let o of c.declarationList.declarations)e.isIdentifier(o.name)&&s.add(o.name.text);let l=c=>{let o=At(e,c);if(!o)return;let g=Ee(i,o.root),y=g?a.get(g):void 0;return y?[...y,...o.segments.slice(1)]:Le(e,i,t,o.root)||s.has(o.root.text)?void 0:o.segments};for(let c of t.statements)if(e.isVariableStatement(c))for(let o of c.declarationList.declarations){if(!o.initializer||!e.isIdentifier(o.name))continue;let g=l(o.initializer),y=Ee(i,o.name);!g||!y||a.set(y,g)}let p=[],f=new Set,u=(c,o)=>{let g=Be(t,o),y=`${c}:${o.getStart(t)}`;f.has(y)||(f.add(y),p.push({name:c,line:g,node:o}))},m=c=>{let o=c.parent&&(e.isPropertyAccessExpression(c.parent)||e.isElementAccessExpression(c.parent))&&c.parent.expression===c;if((e.isPropertyAccessExpression(c)||e.isElementAccessExpression(c))&&!o){let g=l(c),y=g?Ue(r,g):void 0;y&&u(y,c)}else e.isIdentifier(c)&&r.has(c.text)&&bt(e,c)&&!Le(e,i,t,c)&&u(c.text,c);if(e.isVariableDeclaration(c)&&e.isObjectBindingPattern(c.name)&&c.initializer){let g=l(c.initializer);if(g)for(let y of c.name.elements){if(!e.isIdentifier(y.name))continue;let I=y.propertyName?F(e,y.propertyName)??y.propertyName.text:y.name.text,x=Ue(r,[...g,I]);x&&u(x,c.initializer)}}e.forEachChild(c,m)};return m(t),p}function we(e,t){let n=[];for(let r of j(e,t)){if(r.typeOnly||!r.specifier)continue;let i=B(r.specifier);i&&n.push({capability:i,symbol:r.specifier,line:r.line,source:"import-based"})}for(let r of G(e,t,Ie)){let i=ee(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 Pe={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){return/^(Domain|Application|Adapter|Workflow|Job|Presentation|Reporting|Metadata|Security|Audit|Observability|Kernel)\.[A-Za-z0-9_.]+$/.test(e)}function Oe(e){if(!e.publishCall)return[];let t=[];return(e.rawIntentName!==void 0&&_(e.rawIntentName)||e.objectHasIntent)&&t.push({ruleId:"RAW_EVENT_PUBLISH",message:Pe.RAW_EVENT_PUBLISH}),e.arkPublishCandidate&&!e.hasSource&&t.push({ruleId:"PUBLISH_MISSING_SOURCE",message:Pe.PUBLISH_MISSING_SOURCE}),t}function b(e,t,n){return{ruleId:e,code:e,message:t,...n}}function V(e,t){return e.slice(0,t).split(`
|
|
2
|
+
`).length}function Ct(e){let t=[],n=/['"`]([A-Za-z][A-Za-z0-9_.]*)['"`]/g,r;for(;(r=n.exec(e))!==null;)t.push({value:r[1],index:r.index});return t}function It(e){let t=[],n=[{kind:"import",re:/\bimport\s+(?:type\s+)?(?:[^'"]*?\s+from\s*)?['"]([^'"]+)['"]/g},{kind:"export",re:/\bexport\s+(?:type\s+)?[^'"]*?\s+from\s*['"]([^'"]+)['"]/g},{kind:"dynamic-import",re:/\bimport\s*\(\s*['"]([^'"]+)['"]\s*\)/g},{kind:"require",re:/\brequire\s*\(\s*['"]([^'"]+)['"]\s*\)/g}];for(let r of n){let i;for(;(i=r.re.exec(e))!==null;){let a=i.index+i[0].indexOf(i[1]),s=i[0],l=r.kind==="import"&&/\bimport\s+type\b/.test(s)||r.kind==="export"&&/\bexport\s+type\b/.test(s);t.push({value:i[1],index:a,kind:r.kind,typeOnly:l})}}return t.sort((r,i)=>r.index-i.index)}function xt(e,t){let n=e.createSourceFile("generated.ts",t,e.ScriptTarget.Latest,!0),r=[],i=a=>{e.isStringLiteralLike(a)&&r.push({value:a.text,index:a.getStart(n)}),e.forEachChild(a,i)};return i(n),r}function St(e){let t=e.toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);return["adapter","adapters","infra","infrastructure","persistence","repository","repositories","integration","database","db"].some(n=>t.includes(n))}function kt(e){let t=e.toLowerCase();return["sequelize","prisma","typeorm","mongoose","knex"].some(n=>t===n||t.startsWith(`${n}/`))}function Et(e){let t=e.toLowerCase();return["adapter","infra","persistence","repository","repositories","integration","database"].some(n=>t.includes(n))}function q(e,t){return t&&e.isStringLiteralLike(t)?t.text:void 0}function Lt(e,t){if(t&&(e.isIdentifier(t)||e.isStringLiteralLike(t)))return t.text}function Ke(e,t,n){if(!(!t||!e.isObjectLiteralExpression(t)))return t.properties.find(r=>!e.isPropertyAssignment(r)&&!e.isShorthandPropertyAssignment(r)?!1:Lt(e,r.name)===n)}function W(e,t,n){return Ke(e,t,n)!==void 0}function Y(e,t,n){let r=Ke(e,t,n);return r&&e.isPropertyAssignment(r)?r.initializer:void 0}function wt(e,t){let n=Y(e,t,"metadata");return W(e,n,"source")}function Ye(e,t){return t?e.isIdentifier(t)?/^[A-Z]/.test(t.text):e.isPropertyAccessExpression(t)?Ye(e,t.name):!1:!1}function Pt(e,t){if(!e.isCallExpression(t))return!1;let n=t.expression;return e.isPropertyAccessExpression(n)?n.name.text==="publish":e.isIdentifier(n)&&n.text==="publish"}function Ot(e,t){if(!e.isCallExpression(t))return!1;let n=t.arguments[0],r=q(e,n);return r!==void 0&&_(r)||W(e,n,"intent")||Ye(e,n)}function Rt(e,t){if(!e.isCallExpression(t))return!1;let[n,r,i]=t.arguments;return wt(e,n)||W(e,r,"source")||W(e,i,"source")}function $t(e,t){if(!e.isCallExpression(t))return;let[n,r,i]=t.arguments,a=Y(e,n,"metadata");return q(e,Y(e,a,"source"))??q(e,Y(e,r,"source"))??q(e,Y(e,i,"source"))}function vt(e,t,n,r){let i=e.createSourceFile("generated.ts",t,e.ScriptTarget.Latest,!0),a=n,s=a?.filePath,l=a?.layer,p=[],f=m=>i.getLineAndCharacterOfPosition(m.getStart(i)).line+1,u=m=>{if(Pt(e,m)){let c=m.arguments[0],o=q(e,c);for(let y of Oe({publishCall:!0,rawIntentName:o,objectHasIntent:W(e,c,"intent"),arkPublishCandidate:Ot(e,m),hasSource:Rt(e,m)}))p.push(b(y.ruleId,y.message,{line:f(m),filePath:s}));let g=$t(e,m);if(r&&l&&g&&_(g)){let y=r.resolveLayer(g);y&&y!==l&&p.push(b("PUBLISH_SOURCE_LAYER_MISMATCH",`Publish source "${g}" resolves to ${y}, but the target file is classified as ${l}.`,{line:f(m),filePath:s,target:g,fromLayer:l,toLayer:y}))}}e.forEachChild(m,u)};return u(i),p}function qe(e={}){let t=new Set((e.intents||[]).map(a=>typeof a=="string"?a:a.name)),n=e.forbiddenPatterns||[],r=new Set(e.infrastructureLayers??[]),i=e.enforceIntentAllowlist??t.size>0;return{validate(a,s){let l=[],p=s,f=p?.filePath,u=p?.layer,m=e.typescript,c=m?m.createSourceFile(f??"generated.ts",a,m.ScriptTarget.Latest,!0):void 0,o=c?j(e.typescript,c):void 0,g=o?o.filter(d=>d.specifier!==void 0).map(d=>({value:d.specifier,index:d.node.getStart(c),kind:d.kind,typeOnly:d.typeOnly})):It(a),y=e.typescript?xt(e.typescript,a):Ct(a);if(e.typescript&&!e.allowNonLiteralDynamicImport?.(f))for(let d of o?.filter(({unresolved:h})=>h)??[]){let h=d.kind==="require";l.push(b(h?"DYNAMIC_REQUIRE_NOT_ALLOWLISTED":"DYNAMIC_IMPORT_NOT_ALLOWLISTED",`Non-literal ${h?"require call":"dynamic import"} cannot be resolved statically; add the reviewed file to dynamicImportAllowlist.`,{line:d.line,filePath:f}))}let I=u!==void 0&&(r.has(u)||Et(u)),x=u!==void 0?` If "${u}" is an infrastructure layer, mark it in ark.config.json with "mayImportInfrastructure": true (or name it with an infra token like Adapters/Persistence/Repository).`:"";for(let d of n)if(d instanceof RegExp){d.lastIndex=0;let h=d.exec(a);d.lastIndex=0,h&&l.push(b("FORBIDDEN_PATTERN",`Forbidden pattern matched: ${d}`,{line:h.index===void 0?void 0:V(a,h.index),filePath:f,suggestion:"Remove infrastructure imports from domain/application layers."+x}))}else a.includes(d)&&l.push(b("FORBIDDEN_SUBSTRING",`Forbidden substring: ${d}`,{line:V(a,a.indexOf(d)),filePath:f}));for(let d of g){let h=e.resolveImportTarget?.(d.value,f)??(e.resolveImportLayer?{layer:e.resolveImportLayer(d.value,f)}:void 0),A=typeof f=="string"?e.resolveImportTarget?.(f)??(e.resolveImportLayer?{layer:u,relPath:void 0}:void 0):void 0,T=h?.layer;if(T&&u){let X=v(e.architectureProfile?.rules,u,T,{fromPath:A?.relPath,toPath:h?.relPath,layers:e.architectureLayers});if(X){if(d.typeOnly&&!X.peerIsolation)continue;let ge=!!X.peerIsolation;l.push(b("LAYER_IMPORT_VIOLATION",X.message??(ge?`Layer "${u}" must not import across slices into "${T}".`:`Layer "${u}" must not import "${T}".`),{line:V(a,d.index),source:d.value,target:d.value,filePath:f,fromLayer:u,toLayer:T,suggestion:ge?"Extract shared code to a shared layer, or coordinate slices via events/ports \u2014 do not import across feature/context slices.":"Depend on a port/interface owned by an inner layer instead, or move this code to a layer allowed to make this import.",details:{importKind:d.kind,peerIsolation:ge,...d.typeOnly?{typeOnly:!0}:{}}}));continue}if(T!==u)continue}I||d.typeOnly||!St(d.value)&&!kt(d.value)||l.push(b("FORBIDDEN_IMPORT",`Forbidden ${d.kind} target: "${d.value}".`,{line:V(a,d.index),source:d.value,target:d.value,filePath:f,suggestion:"Route infrastructure access through an allowed adapter or port boundary."+x,details:{importKind:d.kind}}))}if(e.policies)for(let d of e.policies){let h=d.check({source:a,context:s});if(h!==!0)if(Array.isArray(h))for(let A of h)l.push(b("POLICY_VIOLATION",A.message,{filePath:f,suggestion:`Fix violation of policy "${d.name}".`}));else h===!1?l.push(b("POLICY_VIOLATION",`Policy ${d.name} failed on generated code`)):l.push(b("POLICY_VIOLATION",h.message))}if(i&&t.size>0)for(let d of y)_(d.value)&&!t.has(d.value)&&l.push(b("UNKNOWN_INTENT",`Unknown intent reference: "${d.value}"`,{line:V(a,d.index),filePath:f,target:d.value,suggestion:`Register intent "${d.value}" via defineIntent() or remove the reference.`}));if(e.architectureProfile&&u)for(let d of y){if(!_(d.value))continue;let h=e.architectureProfile.resolveLayer(d.value);if(!h)continue;let A=v(e.architectureProfile.rules,u,h);A&&l.push(b("LAYER_REFERENCE_VIOLATION",A.message??`Layer "${u}" must not reference "${h}" through "${d.value}".`,{line:V(a,d.index),filePath:f,target:d.value,fromLayer:u,toLayer:h,suggestion:"Route the dependency through an allowed intent, port, or event.",details:{rule:A}}))}if(e.extensions)for(let d of e.extensions)try{let h=d.analyze(a,s);l.push(...h)}catch(h){l.push(b("EXTENSION_ERROR",`Extension "${d.name}" failed: ${h instanceof Error?h.message:String(h)}`))}if(e.typescript&&c&&u&&e.forbiddenGlobals?.[u]?.length)try{let d=e.forbiddenGlobals[u];l.push(...G(e.typescript,c,d).map(h=>b("FORBIDDEN_GLOBAL",`${u} must not use the ambient global "${h.name}".`,{line:h.line,filePath:f,target:h.name,fromLayer:u,suggestion:"Inject the capability through a port (e.g. a Clock, IdGenerator, or HttpPort) instead of reaching for the ambient global."})));for(let h of o??[]){if(h.typeOnly||!h.specifier)continue;let A=D(h.specifier,d);A&&l.push(b("FORBIDDEN_GLOBAL",`${u} must not use module "${h.specifier}" because it is the import form of forbidden global "${A}".`,{line:h.line,filePath:f,source:h.specifier,target:h.specifier,fromLayer:u,details:{importKind:h.kind,forbiddenGlobal:A},suggestion:"Inject the capability through a port instead of importing the ambient global module form."}))}}catch(d){l.push(b("AST_ANALYZER_ERROR",`TypeScript AST analyzer failed: ${d instanceof Error?d.message:String(d)}`))}if(e.typescript&&c&&u&&e.capabilityWalls?.[u]?.length)try{let d=new Set(e.capabilityWalls[u]),h=e.forbiddenGlobals?.[u]??[];for(let A of we(e.typescript,c))d.has(A.capability)&&(A.source==="ambient-global"&&xe(A.symbol,h)||A.source==="import-based"&&D(A.symbol,h)||l.push(b("CAPABILITY_VIOLATION",A.source==="import-based"?`${u} denies the ${A.capability} capability; found import of "${A.symbol}".`:`${u} denies the ${A.capability} capability; found ambient "${A.symbol}".`,{line:A.line,filePath:f,target:A.symbol,capability:A.capability,fromLayer:u,suggestion:"Define a small port (ClockPort, HttpPort, StoragePort) and bind the implementation in an adapter layer."})))}catch(d){l.push(b("AST_ANALYZER_ERROR",`TypeScript AST analyzer failed: ${d instanceof Error?d.message:String(d)}`))}if(e.typescript)try{l.push(...vt(e.typescript,a,s,e.architectureProfile))}catch(d){l.push(b("AST_ANALYZER_ERROR",`TypeScript AST analyzer failed: ${d instanceof Error?d.message:String(d)}`))}return{valid:l.length===0,violations:l}}}}var _t="1.0",ie="https://unpkg.com/arkgate@2/schemas/ark.config.schema.json",We=["DomainModel","ApplicationOrchestration","PersistenceAdapters","IntegrationAdapters","WorkflowSagaEngine","BackgroundJobsScheduling","PresentationAdapters","ReportingReadModels","ExtensibilityMetadata","SecurityAuditObservability","Kernel"],Nt=new Set(["PresentationAdapters->ApplicationOrchestration","ApplicationOrchestration->DomainModel","WorkflowSagaEngine->ApplicationOrchestration","WorkflowSagaEngine->DomainModel","BackgroundJobsScheduling->ApplicationOrchestration"]);function Mt(){let e=[];for(let t of We)for(let n of We)t===n||Nt.has(`${t}->${n}`)||e.push({from:t,to:n,allowed:!1});return e}var ae=Mt();var O={type:"array",items:{type:"string",minLength:1},uniqueItems:!0},$e={$schema:"https://json-schema.org/draft/2020-12/schema",$id:ie,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:ie,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:ae,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}}}}},U=class extends Error{issues;source;constructor(t,n){super(`Invalid ArkGate config (${t}):
|
|
3
3
|
${n.map(r=>`- ${r.path}: ${r.message}`).join(`
|
|
4
|
-
`)}`),this.name="ArkConfigValidationError",this.source=t,this.issues=n}};function Ke(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function Pe(e,t){return/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(t)?`${e}.${t}`:`${e}[${JSON.stringify(t)}]`}function G(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 ne(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}ne(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(!Ke(e)){i.push({path:n,message:`must be an object; received ${G(e)}`});return}let a=t.properties??{};for(let s of t.required??[])e[s]===void 0&&i.push({path:Pe(n,s),message:"is required"});if(t.additionalProperties===!1)for(let s of Object.keys(e))s in a||i.push({path:Pe(n,s),message:"unknown field"});for(let[s,c]of Object.entries(a))e[s]!==void 0&&ne(e[s],c,Pe(n,s),r,i);return}if(t.type==="array"){if(!Array.isArray(e)){i.push({path:n,message:`must be an array; received ${G(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(s=>JSON.stringify(s));new Set(a).size!==a.length&&i.push({path:n,message:"must not contain duplicate items"})}t.items&&e.forEach((a,s)=>ne(a,t.items,`${n}[${s}]`,r,i));return}if(t.type==="string"){if(typeof e!="string"){i.push({path:n,message:`must be a string; received ${G(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 ${G(e)}`});return}if(t.type==="integer"){if(!Number.isInteger(e)){i.push({path:n,message:`must be an integer; received ${G(e)}`});return}t.minimum!==void 0&&e<t.minimum&&i.push({path:n,message:`must be at least ${t.minimum}`})}}function Mt(e){return{...e,$schema:e.$schema===void 0?re: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?ie.map(t=>({...t})):e.rules}}function Tt(e,t="ark.config.json"){if(!Ke(e))throw new H(t,[{path:"$",message:`must be an object; received ${G(e)}`}]);let n=e.schemaVersion===void 0?"unversioned":null;if(e.schemaVersion!==void 0&&e.schemaVersion!=="1.0")throw new H(t,[{path:"$.schemaVersion",message:`unsupported version ${JSON.stringify(e.schemaVersion)}; expected 1.0`}]);return{candidate:Mt(e),migratedFrom:n}}function ae(e,t="ark.config.json"){let{candidate:n,migratedFrom:r}=Tt(e,t),i=[];if(ne(n,Oe,"$",Oe,i),i.length>0)throw new H(t,i);return{config:n,migratedFrom:r}}function Re(e,t="ark.config.json"){let n;try{n=JSON.parse(e)}catch(r){throw new H(t,[{path:"$",message:`invalid JSON: ${r instanceof Error?r.message:String(r)}`}])}return ae(n,t)}function We(e){let t={$schema:typeof e.$schema=="string"&&e.$schema.length>0?e.$schema:re,schemaVersion:"1.0"};for(let[n,r]of Object.entries(e))n!=="$schema"&&n!=="schemaVersion"&&(t[n]=r);return t}function Dt(e){return e.endsWith(".")?e:`${e}.`}function Ft(e,t){let n=e.prefixes.length?Math.max(...e.prefixes.map(i=>i.length)):0;return(t.prefixes.length?Math.max(...t.prefixes.map(i=>i.length)):0)-n}function se(e){let t=e.layers.map(i=>({...i,prefixes:i.prefixes.map(Dt)})),n=[...t].sort(Ft),r=[...e.rules??[]];return{name:e.name,layers:t,rules:r,resolveLayer(i){return t.find(a=>a.match?.(i))?.name??n.find(a=>a.prefixes.some(s=>i.startsWith(s)))?.name}}}function Je(e,t={}){return se({name:t.name??e.name??"ark.config.json",layers:e.layers.map((n,r)=>({name:n.name,prefixes:n.intentPrefixes??[],description:n.description,order:r+1})),rules:e.rules??[]})}var jt=[{name:"DomainModel",prefixes:["Domain"],description:"Rich domain model, business rules, and domain events.",order:1},{name:"ApplicationOrchestration",prefixes:["Application"],description:"Use cases and command orchestration.",order:2},{name:"PersistenceAdapters",prefixes:["Adapter.Persistence","Adapter.Repository"],description:"Database, repository, and storage adapters.",order:3},{name:"IntegrationAdapters",prefixes:["Adapter.Integration","Adapter.External"],description:"External systems, APIs, and integration adapters.",order:4},{name:"WorkflowSagaEngine",prefixes:["Workflow"],description:"Sagas, workflows, and long-running processes.",order:5},{name:"BackgroundJobsScheduling",prefixes:["Job"],description:"Background jobs, scheduled work, and async processors.",order:6},{name:"PresentationAdapters",prefixes:["Presentation","Adapter.Presentation","Adapter.Api"],description:"API, UI, controller, and presentation adapters.",order:7},{name:"ReportingReadModels",prefixes:["Reporting"],description:"Read models, projections, and reporting surfaces.",order:8},{name:"ExtensibilityMetadata",prefixes:["Metadata"],description:"Metadata, extensions, and schema contracts.",order:9},{name:"SecurityAuditObservability",prefixes:["Security","Audit","Observability"],description:"Security, audit, and observability concerns.",order:10},{name:"Kernel",prefixes:["Kernel"],description:"Ark-owned governance and kernel signals.",order:11}],oe=se({name:"Ark 11-layer Hexagonal Event-Driven Profile",layers:jt,rules:ie.map(e=>({...e}))}),Vt={DomainModel:["domain"],ApplicationOrchestration:["application","app"],PersistenceAdapters:["adapters/persistence","adapters/repository","repositories","infra/persistence"],IntegrationAdapters:["adapters/integration","adapters/external","integrations"],WorkflowSagaEngine:["workflows","sagas"],BackgroundJobsScheduling:["jobs","schedules"],PresentationAdapters:["presentation","adapters/presentation","adapters/api"],ReportingReadModels:["reporting","read-models","projections"],ExtensibilityMetadata:["metadata","extensions"],SecurityAuditObservability:["security","audit","observability"],Kernel:["kernel"]};function Ze(e={}){let t=e.rootDir??"src",n=e.optionalLayers??!0,r=t==="."?"":`${t}/`;return We({include:e.include??[t],layers:oe.layers.map(i=>({name:i.name,patterns:(Vt[i.name]??[i.name]).map(a=>`${r}${a}/**`),intentPrefixes:i.prefixes,optional:n})),rules:[...oe.rules]})}var $e="1.0";function R(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 L(e){if(e===null||typeof e!="object")return JSON.stringify(e);if(Array.isArray(e))return`[${e.map(L).join(",")}]`;let t=e;return`{${Object.keys(t).sort().map(n=>`${JSON.stringify(n)}:${L(t[n])}`).join(",")}}`}function E(e){return`${e.from}->${e.to}`}function W(e,t,n,r="dependency"){return{id:`${e}:${r}:${E(t)}`,classification:e,subject:"dependency",from:t.from,to:t.to,message:n,...e==="missing"?{nextAction:`Add the planned dependency ${E(t)} to the candidate, then preflight again.`}:e==="contradictory"?{nextAction:`Replace the reverse dependency with ${E(t)}, then preflight again.`}:e==="unplanned"?{nextAction:`Remove the unplanned dependency ${E(t)} from the candidate, then preflight again.`}:{}}}function ce(e){let t=[],n=new Map(e.changeMap.map.files.map(l=>[l.path,l])),r=new Map(e.changes.map(l=>[l.path,l])),i=new Map(e.changeMap.map.dependencies.map(l=>[E(l),l])),a=new Map(e.baseDependencies.map(l=>[E(l),l])),s=new Map(e.candidateDependencies.map(l=>[E(l),l]));for(let l of[...n.values()].sort((o,g)=>o.path.localeCompare(g.path))){let o=r.get(l.path);o?o.operation!==l.operation?t.push({id:`contradictory:file:${l.path}`,classification:"contradictory",subject:"file",path:l.path,expectedOperation:l.operation,actualOperation:o.operation,message:`${l.path} was planned as ${l.operation} but the actual operation is ${o.operation}.`,nextAction:`Change ${l.path} to the planned ${l.operation} operation, then preflight again.`}):t.push({id:`satisfied:file:${l.path}`,classification:"satisfied",subject:"file",path:l.path,expectedOperation:l.operation,actualOperation:o.operation,message:`${l.path} matches the planned ${l.operation} operation.`}):t.push({id:`missing:file:${l.path}`,classification:"missing",subject:"file",path:l.path,expectedOperation:l.operation,message:`${l.path} was planned as ${l.operation} but is absent from the actual change.`,nextAction:`${l.operation[0].toUpperCase()}${l.operation.slice(1)} ${l.path} in the complete change set, then preflight again.`})}for(let l of[...r.values()].sort((o,g)=>o.path.localeCompare(g.path)))n.has(l.path)||t.push({id:`unplanned:file:${l.path}`,classification:"unplanned",subject:"file",path:l.path,actualOperation:l.operation,message:`${l.path} has an unplanned ${l.operation} operation.`,nextAction:`Remove ${l.path} from the change set, then preflight again.`});let c=new Set;for(let l of[...i.values()].sort((o,g)=>E(o).localeCompare(E(g)))){if(s.has(E(l))){t.push(W("satisfied",l,`${l.from} -> ${l.to} exists in the candidate architecture.`));continue}let o={from:l.to,to:l.from};s.has(E(o))?(c.add(E(o)),t.push(W("contradictory",l,`${l.from} -> ${l.to} was planned, but the candidate contains the reverse edge.`))):t.push(W("missing",l,`${l.from} -> ${l.to} is absent from the candidate architecture.`))}let p=new Set([...n.keys(),...r.keys()]);for(let[l,o]of[...s].sort(([g],[f])=>g.localeCompare(f)))a.has(l)||i.has(l)||c.has(l)||!p.has(o.from)&&!p.has(o.to)||t.push({...W("unplanned",o,`${o.from} -> ${o.to} was added without a matching planned dependency.`,"dependency-added"),actualOperation:"added"});let y=new Set(e.changeMap.map.files.filter(l=>l.operation==="delete").map(l=>l.path));for(let[l,o]of[...a].sort(([g],[f])=>g.localeCompare(f)))s.has(l)||y.has(o.from)||y.has(o.to)||!p.has(o.from)&&!p.has(o.to)||t.push({...W("unplanned",o,`${o.from} -> ${o.to} was removed without a planned file deletion.`,"dependency-removed"),actualOperation:"removed"});let u={satisfied:0,missing:1,contradictory:2,unplanned:3};t.sort((l,o)=>u[l.classification]-u[o.classification]||(l.subject===o.subject?0:l.subject==="file"?-1:1)||l.id.localeCompare(o.id));let m={satisfied:t.filter(l=>l.classification==="satisfied").length,missing:t.filter(l=>l.classification==="missing").length,contradictory:t.filter(l=>l.classification==="contradictory").length,unplanned:t.filter(l=>l.classification==="unplanned").length};return{schemaVersion:"1.0",readOnly:!0,changeMapHash:e.changeMap.hash,structurallyConverged:m.missing===0&&m.contradictory===0&&m.unplanned===0,behavioralCompletion:"not-evaluated",summary:m,findings:t}}var ve="1.0";function C(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 w(e){return[...new Set(e??[])].sort()}function N(e,t,n,r,i){let a=w(n),s=w(r),c=new Set(a),p=new Set(s),y=s.filter(m=>!c.has(m)),u=a.filter(m=>!p.has(m));y.length===0&&u.length===0||(y.length>0&&C(e,{kind:"added",path:t,classification:i.added,message:i.addedMessage,before:a,after:s}),u.length>0&&C(e,{kind:"removed",path:t,classification:i.removed,message:i.removedMessage,before:a,after:s}))}function le(e,t,n,r,i,a,s){if(n===r)return;C(e,{kind:r?"enabled":"disabled",path:t,classification:r?i:i==="strengthening"?"weakening":"strengthening",message:r?a:s,before:n,after:r})}function pe(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 Gt(e,t,n){let r=pe(t,a=>a.name),i=pe(n,a=>a.name);(r.duplicates.length>0||i.duplicates.length>0)&&C(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 s=r.values.get(a),c=i.values.get(a),p=`$.layers[${a}]`;if(!s&&c){C(e,{kind:"layer-added",path:p,classification:"judgment-required",message:"A layer was added; verify overlap, ownership, and rule coverage.",after:c});continue}if(s&&!c){C(e,{kind:"layer-removed",path:p,classification:"weakening",message:"Removing a layer can leave its source paths ungoverned.",before:s});continue}if(!s||!c)continue;N(e,`${p}.patterns`,s.patterns,c.patterns,{added:"strengthening",removed:"weakening",addedMessage:"Additional paths are governed by this layer.",removedMessage:"Paths were removed from this layer and may become ungoverned."}),N(e,`${p}.exclude`,s.exclude,c.exclude,{added:"weakening",removed:"strengthening",addedMessage:"Additional paths are excluded from this layer.",removedMessage:"Fewer paths are excluded from this layer."});let y=te(s),u=te(c);N(e,`${p}.forbiddenGlobals`,y.rawGlobals,u.rawGlobals,{added:"strengthening",removed:"weakening",addedMessage:"Additional forbidden globals are enforced in this layer.",removedMessage:"A forbidden-global protection was removed from this layer."}),N(e,`${p}.capabilities`,y.atoms,u.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)."}),w(s.intentPrefixes).join("\0")!==w(c.intentPrefixes).join("\0")&&C(e,{kind:"intent-prefixes-changed",path:`${p}.intentPrefixes`,classification:"judgment-required",message:"Intent ownership changed and must be reviewed against publishers and consumers.",before:w(s.intentPrefixes),after:w(c.intentPrefixes)}),le(e,`${p}.mayImportInfrastructure`,s.mayImportInfrastructure===!0,c.mayImportInfrastructure===!0,"weakening","The layer may now import infrastructure directly.","Direct infrastructure imports are no longer allowed for this layer."),le(e,`${p}.optional`,s.optional===!0,c.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 Ht(e,t,n){let r=s=>`${s.from}->${s.to}`,i=pe(t,r),a=pe(n,r);(i.duplicates.length>0||a.duplicates.length>0)&&C(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 s of[...new Set([...i.values.keys(),...a.values.keys()])].sort()){let c=i.values.get(s),p=a.values.get(s),y=`$.rules[${s}]`;if(!c&&p){p.allowed===!1&&C(e,{kind:"deny-added",path:y,classification:"strengthening",message:"A denied dependency edge was added.",after:p});continue}if(c&&!p){c.allowed===!1&&C(e,{kind:"deny-removed",path:y,classification:"weakening",message:"A denied dependency edge was removed.",before:c});continue}if(!c||!p)continue;c.allowed!==p.allowed&&C(e,{kind:p.allowed?"deny-disabled":"deny-enabled",path:`${y}.allowed`,classification:p.allowed?"weakening":"strengthening",message:p.allowed?"A previously denied dependency edge is now allowed.":"A dependency edge is now denied.",before:c.allowed,after:p.allowed});let u=c.peerIsolation===!0,m=p.peerIsolation===!0;if(u!==m){let l=c.from===c.to&&p.from===p.to;C(e,{kind:m?"peer-isolation-enabled":"peer-isolation-disabled",path:`${y}.peerIsolation`,classification:l?m?"strengthening":"weakening":"judgment-required",message:l?m?"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:u,after:m})}w(c.sliceFolders).join("\0")!==w(p.sliceFolders).join("\0")&&C(e,{kind:"slice-folders-changed",path:`${y}.sliceFolders`,classification:"judgment-required",message:"Slice ownership folders changed and can reclassify existing dependencies.",before:w(c.sliceFolders),after:w(p.sliceFolders)})}}function Ut(e,t,n){let r=t.safety??{},i=n.safety??{};for(let a of["maxTsSuppressions","maxAnyCasts"]){let s=r[a]??0,c=i[a]??0;s!==c&&C(e,{kind:c>s?"threshold-raised":"threshold-lowered",path:`$.safety.${a}`,classification:c>s?"weakening":"strengthening",message:c>s?"The safety threshold allows more violations.":"The safety threshold allows fewer violations.",before:s,after:c})}for(let a of["allowInMemory","allowDisabledPeerIsolation"])le(e,`$.safety.${a}`,r[a]===!0,i[a]===!0,"weakening","A safety exception was enabled.","A safety exception was disabled.")}function Bt(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 _e(e,t){let n=[];N(n,"$.include",e.include,t.include,{added:"strengthening",removed:"weakening",addedMessage:"Additional project roots are governed.",removedMessage:"Project roots were removed from governance."}),N(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."}),N(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."}),le(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 s=r[a]===r[i]?"judgment-required":r[a]>r[i]?"strengthening":"weakening";C(n,{kind:"cycle-policy-changed",path:"$.cyclePolicy",classification:s,message:"The cycle enforcement level changed.",before:i,after:a})}return(e.frameworkOverlay??null)!==(t.frameworkOverlay??null)&&C(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}),Gt(n,e.layers,t.layers),Ht(n,e.rules,t.rules),Ut(n,e,t),n.sort((s,c)=>s.path.localeCompare(c.path)||s.id.localeCompare(c.id)),{schemaVersion:ve,classification:Bt(n),findings:n}}function Ne(e,t){if(!e||e.schemaVersion!==ve||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=w(e.findingIds),r=w(t.findingIds);return n.length===r.length&&n.every((i,a)=>i===r[a])}function M(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 Qe(e){return e!==void 0&&/[A-Za-z0-9_$]/.test(e)}function $(e,t){for(;t<e.length&&/\s/.test(e[t]);)t+=1;return t}function J(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 P(e,t,n){return e.startsWith(t,n)&&!Qe(e[n-1])&&!Qe(e[n+t.length])}function zt(e,t){if(t=$(e,t+6),e[t]==="(")return J(e,$(e,t+1));let n=!1;if(P(e,"type",t)){let i=$(e,t+4);e[i]!==","&&!P(e,"from",i)&&(n=!0)}let r=Xe(e,t,!0);return r&&n?{...r,typeOnly:!0}:r}function Yt(e,t){t=t+6;let n=$(e,t),r=!1;if(P(e,"type",n)){let a=$(e,n+4);(e[a]==="{"||e[a]==="*")&&(r=!0)}let i=Xe(e,t,!1);return i&&r?{...i,typeOnly:!0}:i}function Xe(e,t,n){for(;t<e.length;t+=1){if(e[t]===";")return;if(P(e,"from",t))return J(e,$(e,t+4));if(n&&(e[t]==="'"||e[t]==='"'))return J(e,t);if(t>0&&(P(e,"import",t)||P(e,"export",t)))return}}function qt(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 Kt(e,t){let n=t-1;for(;n>=0&&/\s/.test(e[n]);)n-=1;if(e[n]===".")return;let r=$(e,t+7);if(e[r]!=="(")return;r=$(e,r+1);let i=J(e,r);return i?{...i,requireCall:!0}:void 0}function Wt(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(`
|
|
5
|
-
`,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=
|
|
6
|
-
`).length;r.push({file:e.path,symbol:a,capability:
|
|
7
|
-
`).length,c={kind:"import",file:e.path,line:s,excerpt:i.excerpt},p=Jt(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:c})}return{edges:n,capabilityUses:r}}function Jt(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 s=n.get(a);if(s)return s}}function tt(e,t){let n=[];for(let r of e){if(!r.to||!r.fromLayer||!r.toLayer)continue;let i=v(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 de(e,t){let n=typeof e=="string"?Re(e,t):ae(e,t);return{...n,policyHash:R(L(n.config))}}function nt(e){let t=de(e.baseConfig,e.baseSource??"base ark.config.json"),n=de(e.candidateConfig,e.candidateSource??"candidate ark.config.json"),r=_e(t.config,n.config),i=r.findings.filter(c=>c.classification==="weakening"||c.classification==="judgment-required").map(c=>c.id).sort(),a=i.length>0,s=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:s,valid:!a||s}}function Z(e){let t=e.files.map(c=>{let p=M(c.path);return{path:p,content:c.content,contentHash:R(c.content),layer:z(p,e.contract.config.layers)??null}}).sort((c,p)=>c.path.localeCompare(p.path)),n=new Map(t.map(c=>[c.path,c])),r=[],i=[];for(let c of t){let p=et(c,n);r.push(...p.edges),i.push(...p.capabilityUses)}let a=tt(r,e.contract.config),s=new Map(e.contract.config.layers.map(c=>[c.name,new Set(ee(c))]));for(let c of i){let p=n.get(c.file)?.layer;!p||!s.get(p)?.has(c.capability)||a.push({ruleId:"CAPABILITY_VIOLATION",message:`${p} denies the ${c.capability} capability; found import of "${c.symbol}".`,capability:c.capability,symbol:c.symbol,evidence:c.evidence})}return{ir:{schemaVersion:"1.0",policyHash:e.contract.policyHash,compilerOptionsHash:R(L(e.compilerOptions??{})),files:t,layers:e.contract.config.layers.map(c=>c.name),edges:r,capabilityUses:i,violations:a}}}function fe(e){let t=new Map(e.files.map(n=>[M(n.path),n]));for(let n of e.changes){let r=M(n.path);"delete"in n&&n.delete?t.delete(r):"content"in n&&t.set(r,{path:r,content:n.content})}return Z({contract:e.contract,files:[...t.values()],compilerOptions:e.compilerOptions})}function rt(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 Me(e){let t=0,n=new Map,r=new Map,i=new Set,a=[],s=[],c=p=>{n.set(p,t),r.set(p,t),t+=1,a.push(p),i.add(p);for(let m of[...e.get(p)??[]].sort())e.has(m)&&(n.has(m)?i.has(m)&&r.set(p,Math.min(r.get(p)??0,n.get(m)??0)):(c(m),r.set(p,Math.min(r.get(p)??0,r.get(m)??0))));if(r.get(p)!==n.get(p))return;let y=[],u;do{if(u=a.pop(),u===void 0)break;i.delete(u),y.push(u)}while(u!==p);y.length>1&&s.push(y.sort())};for(let p of[...e.keys()].sort())n.has(p)||c(p);return s.sort((p,y)=>p[0].localeCompare(y[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 ue(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 s=v(e.rules,a.fromLayer,a.toLayer,{fromPath:a.from,toPath:a.to,layers:e.config.layers});if(!s)continue;let c=!!s.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}:{},...!c&&a.portProofEligible?{portProofEligible:!0}:{},...a.kind?{edgeKind:a.kind}:{},...c?{peerIsolation:!0}:{},message:s.message??(c?`${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=Me(r);i==="soft"||i==="framework-soft"?n.push(...a.map(s=>({...s,message:`${s.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 it(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 at(e){return R(L(e.map(({path:t,contentHash:n})=>({path:t,contentHash:n}))))}function ot(e){let t=Z(e),n=new Map(t.ir.files.map(o=>[o.path,o])),r=[],i=new Set,a=[];for(let o of e.changes){let g=o.path.replace(/\\/g,"/"),f=M(o.path);if(!f||f===".."||f.startsWith("../")||g.startsWith("/")||/^[A-Za-z]:\//.test(g)||g.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 o&&o.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 o&&o.delete?{path:f,delete:!0}:{path:f,content:"content"in o?o.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 s=fe({...e,changes:r}),c=new Map(s.ir.files.map(o=>[o.path,o])),p=s.ir.violations.filter(o=>o.ruleId==="CAPABILITY_VIOLATION").map(o=>({ruleId:o.ruleId,file:o.evidence.file,line:o.evidence.line,target:o.symbol,capability:o.capability,message:o.message})),y=ue({config:e.contract.config,rules:e.contract.config.rules,files:s.ir.files.map(o=>o.path),contentViolations:p,edges:s.ir.edges.filter(o=>!!o.fromLayer).map(o=>({from:o.from,fromLayer:o.fromLayer,...o.to?{to:o.to}:{},...o.toLayer?{toLayer:o.toLayer}:{},line:o.evidence.line,kind:"import"}))}),u=r.map(o=>{let g=M(o.path),f=n.get(g),I=c.get(g);return{path:g,operation:"delete"in o&&o.delete?"delete":f?"update":"create",...f?{beforeContentHash:f.contentHash}:{},...I?{candidateContentHash:I.contentHash}:{}}}).sort((o,g)=>o.path.localeCompare(g.path)),m=[...a,...y.violations].map(o=>({...o,nextAction:it(o)})),l=e.changeMap?ce({changeMap:e.changeMap,changes:u,baseDependencies:t.ir.edges.flatMap(o=>o.to?[{from:o.from,to:o.to}]:[]),candidateDependencies:s.ir.edges.flatMap(o=>o.to?[{from:o.from,to:o.to}]:[])}):void 0;return{schemaVersion:"1.0",valid:m.length===0&&(l?.structurallyConverged??!0),readOnly:!0,policyHash:e.contract.policyHash,compilerOptionsHash:s.ir.compilerOptionsHash,baseTreeHash:at(t.ir.files),candidateTreeHash:at(s.ir.files),...e.changeMap?{changeMapHash:e.changeMap.hash}:{},...l?{convergence:l}:{},changes:u,violations:m,warnings:y.warnings}}function S(e,t,n={}){return{ruleId:e,message:t,...n}}function st(e){let{config:t,rules:n,files:r,manifest:i}=e,a=[];if(t.dynamicImportAllowlist!==void 0&&(!Array.isArray(t.dynamicImportAllowlist)||t.dynamicImportAllowlist.some(o=>typeof o!="string"))&&a.push(S("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(S("CONFIG_INVALID_SAFETY","safety must be an object."));else if(t.safety)for(let o of["maxTsSuppressions","maxAnyCasts"]){let g=t.safety[o];g!==void 0&&(!Number.isInteger(g)||g<0)&&a.push(S("CONFIG_INVALID_SAFETY_THRESHOLD",`safety.${o} must be a non-negative integer.`))}let s=Array.isArray(t.layers)?t.layers:[],c=Array.isArray(i?.architecture?.layers)?i.architecture.layers:[],p=new Set([...s.map(o=>o.name).filter(Boolean),...c.map(o=>o.name).filter(o=>!!o)]);s.length===0&&a.push(S("CONFIG_NO_LAYERS","No file layers are configured; ark-check cannot classify files for import-boundary enforcement."));let y=new Set,u=new Set;for(let o of s){if(!o.name){a.push(S("CONFIG_LAYER_WITHOUT_NAME","A configured layer is missing a name."));continue}y.has(o.name)&&u.add(o.name),y.add(o.name),o.forbiddenGlobals!==void 0&&(!Array.isArray(o.forbiddenGlobals)||o.forbiddenGlobals.some(f=>typeof f!="string"))&&a.push(S("CONFIG_INVALID_FORBIDDEN_GLOBALS",`Layer "${o.name}" has an invalid forbiddenGlobals value; expected an array of strings (e.g. ["fetch", "Date.now"]). The entry is ignored.`,{layer:o.name}));let g=Array.isArray(o.patterns)?o.patterns:[];if(g.length===0){a.push(S("CONFIG_LAYER_WITHOUT_PATTERNS",`Layer "${o.name}" has no file patterns and will never classify files.`,{layer:o.name}));continue}for(let f of g){let I;try{I=B(f)}catch(x){a.push(S("CONFIG_INVALID_LAYER_PATTERN",`Layer "${o.name}" has an invalid pattern "${f}": ${x instanceof Error?x.message:String(x)}`,{layer:o.name,pattern:f}));continue}!r.some(x=>I.test(x))&&!o.optional&&a.push(S("CONFIG_LAYER_PATTERN_NO_MATCHES",`Layer "${o.name}" pattern "${f}" matched no included files.`,{layer:o.name,pattern:f,failsStrict:!1}))}}for(let o of u)a.push(S("CONFIG_DUPLICATE_LAYER",`Layer "${o}" is configured more than once.`,{layer:o}));if(p.size>0)for(let o of n??[])o.from&&!p.has(o.from)&&a.push(S("CONFIG_RULE_UNKNOWN_FROM_LAYER",`Rule references unknown source layer "${o.from}".`,{fromLayer:o.from,toLayer:o.to})),o.to&&!p.has(o.to)&&a.push(S("CONFIG_RULE_UNKNOWN_TO_LAYER",`Rule references unknown target layer "${o.to}".`,{fromLayer:o.from,toLayer:o.to}));let m=new Set;if(s.length>1)for(let o of r){let g=-1,f=[];for(let I of s)for(let x of I.patterns??[]){if(!B(x).test(o))continue;let d=xe(x);d>g?(g=d,f=[I.name]):d===g&&!f.includes(I.name)&&f.push(I.name)}f.length>1&&m.add([...f].sort().join(" + "))}m.size>0&&a.push(S("CONFIG_AMBIGUOUS_LAYERS",`Some files match multiple layers at equal specificity; classification falls back to declaration order. Disambiguate the overlapping patterns: ${[...m].join(", ")}.`,{pairs:[...m]}));let l=r.filter(o=>!z(o,s));return l.length>0&&a.push(S("CONFIG_UNCLASSIFIED_FILES",`${l.length} included source file(s) are not matched by any configured layer; ark-check will not enforce import rules for those source files.`,{count:l.length,samples:l.slice(0,5)})),a}export{$e as ANALYSIS_IR_SCHEMA_VERSION,ft as ARK_ANALYSIS_RESULT_SCHEMA,lt as ARK_ANALYSIS_RESULT_SCHEMA_VERSION,Oe as ARK_CONFIG_SCHEMA,$t as ARK_CONFIG_SCHEMA_VERSION,ve as POLICY_DELTA_SCHEMA_VERSION,ce as analyzeArchitectureConvergence,fe as analyzeChange,nt as analyzePolicyDelta,Z as analyzeProject,_e as classifyArkPolicyDelta,st as collectAnalysisConfigWarnings,j as collectForbiddenCapabilityUses,Ye as createAICodeGate,dt as createAdapterResult,se as createArchitectureProfile,Je as createArchitectureProfileFromArkConfig,Ze as createElevenLayerArkConfig,Me as detectArchitectureCycles,R as deterministicHash,oe as elevenLayerProfile,ue as evaluateArchitectureGraph,rt as explainViolation,F as extractSemanticDependencies,ae as loadArkConfigContract,de as loadContract,Re as parseArkConfigJson,Ne as policyDeltaAcknowledgementMatches,ot as preflightChange,L as stableSerialize,ge as toAdapterDiagnostic,ct as version};
|
|
4
|
+
`)}`),this.name="ArkConfigValidationError",this.source=t,this.issues=n}};function Je(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function Re(e,t){return/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(t)?`${e}.${t}`:`${e}[${JSON.stringify(t)}]`}function H(e){return e===null?"null":Array.isArray(e)?"array":typeof e}function Tt(e,t){let n="#/$defs/";if(e.startsWith(n))return t.$defs[e.slice(n.length)]}function re(e,t,n,r,i){if(t.$ref){let a=Tt(t.$ref,r);if(!a){i.push({path:n,message:`schema reference ${t.$ref} cannot be resolved`});return}re(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(!Je(e)){i.push({path:n,message:`must be an object; received ${H(e)}`});return}let a=t.properties??{};for(let s of t.required??[])e[s]===void 0&&i.push({path:Re(n,s),message:"is required"});if(t.additionalProperties===!1)for(let s of Object.keys(e))s in a||i.push({path:Re(n,s),message:"unknown field"});for(let[s,l]of Object.entries(a))e[s]!==void 0&&re(e[s],l,Re(n,s),r,i);return}if(t.type==="array"){if(!Array.isArray(e)){i.push({path:n,message:`must be an array; received ${H(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(s=>JSON.stringify(s));new Set(a).size!==a.length&&i.push({path:n,message:"must not contain duplicate items"})}t.items&&e.forEach((a,s)=>re(a,t.items,`${n}[${s}]`,r,i));return}if(t.type==="string"){if(typeof e!="string"){i.push({path:n,message:`must be a string; received ${H(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 ${H(e)}`});return}if(t.type==="integer"){if(!Number.isInteger(e)){i.push({path:n,message:`must be an integer; received ${H(e)}`});return}t.minimum!==void 0&&e<t.minimum&&i.push({path:n,message:`must be at least ${t.minimum}`})}}function Dt(e){return{...e,$schema:e.$schema===void 0?ie: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?ae.map(t=>({...t})):e.rules}}function Ft(e,t="ark.config.json"){if(!Je(e))throw new U(t,[{path:"$",message:`must be an object; received ${H(e)}`}]);let n=e.schemaVersion===void 0?"unversioned":null;if(e.schemaVersion!==void 0&&e.schemaVersion!=="1.0")throw new U(t,[{path:"$.schemaVersion",message:`unsupported version ${JSON.stringify(e.schemaVersion)}; expected 1.0`}]);return{candidate:Dt(e),migratedFrom:n}}function oe(e,t="ark.config.json"){let{candidate:n,migratedFrom:r}=Ft(e,t),i=[];if(re(n,$e,"$",$e,i),i.length>0)throw new U(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 U(t,[{path:"$",message:`invalid JSON: ${r instanceof Error?r.message:String(r)}`}])}return oe(n,t)}function Ze(e){let t={$schema:typeof e.$schema=="string"&&e.$schema.length>0?e.$schema:ie,schemaVersion:"1.0"};for(let[n,r]of Object.entries(e))n!=="$schema"&&n!=="schemaVersion"&&(t[n]=r);return t}function jt(e){return e.endsWith(".")?e:`${e}.`}function Gt(e,t){let n=e.prefixes.length?Math.max(...e.prefixes.map(i=>i.length)):0;return(t.prefixes.length?Math.max(...t.prefixes.map(i=>i.length)):0)-n}function ce(e){let t=e.layers.map(i=>({...i,prefixes:i.prefixes.map(jt)})),n=[...t].sort(Gt),r=[...e.rules??[]];return{name:e.name,layers:t,rules:r,resolveLayer(i){return t.find(a=>a.match?.(i))?.name??n.find(a=>a.prefixes.some(s=>i.startsWith(s)))?.name}}}function Qe(e,t={}){return ce({name:t.name??e.name??"ark.config.json",layers:e.layers.map((n,r)=>({name:n.name,prefixes:n.intentPrefixes??[],description:n.description,order:r+1})),rules:e.rules??[]})}var Vt=[{name:"DomainModel",prefixes:["Domain"],description:"Rich domain model, business rules, and domain events.",order:1},{name:"ApplicationOrchestration",prefixes:["Application"],description:"Use cases and command orchestration.",order:2},{name:"PersistenceAdapters",prefixes:["Adapter.Persistence","Adapter.Repository"],description:"Database, repository, and storage adapters.",order:3},{name:"IntegrationAdapters",prefixes:["Adapter.Integration","Adapter.External"],description:"External systems, APIs, and integration adapters.",order:4},{name:"WorkflowSagaEngine",prefixes:["Workflow"],description:"Sagas, workflows, and long-running processes.",order:5},{name:"BackgroundJobsScheduling",prefixes:["Job"],description:"Background jobs, scheduled work, and async processors.",order:6},{name:"PresentationAdapters",prefixes:["Presentation","Adapter.Presentation","Adapter.Api"],description:"API, UI, controller, and presentation adapters.",order:7},{name:"ReportingReadModels",prefixes:["Reporting"],description:"Read models, projections, and reporting surfaces.",order:8},{name:"ExtensibilityMetadata",prefixes:["Metadata"],description:"Metadata, extensions, and schema contracts.",order:9},{name:"SecurityAuditObservability",prefixes:["Security","Audit","Observability"],description:"Security, audit, and observability concerns.",order:10},{name:"Kernel",prefixes:["Kernel"],description:"Ark-owned governance and kernel signals.",order:11}],se=ce({name:"Ark 11-layer Hexagonal Event-Driven Profile",layers:Vt,rules:ae.map(e=>({...e}))}),Ht={DomainModel:["domain"],ApplicationOrchestration:["application","app"],PersistenceAdapters:["adapters/persistence","adapters/repository","repositories","infra/persistence"],IntegrationAdapters:["adapters/integration","adapters/external","integrations"],WorkflowSagaEngine:["workflows","sagas"],BackgroundJobsScheduling:["jobs","schedules"],PresentationAdapters:["presentation","adapters/presentation","adapters/api"],ReportingReadModels:["reporting","read-models","projections"],ExtensibilityMetadata:["metadata","extensions"],SecurityAuditObservability:["security","audit","observability"],Kernel:["kernel"]};function Xe(e={}){let t=e.rootDir??"src",n=e.optionalLayers??!0,r=t==="."?"":`${t}/`;return Ze({include:e.include??[t],layers:se.layers.map(i=>({name:i.name,patterns:(Ht[i.name]??[i.name]).map(a=>`${r}${a}/**`),intentPrefixes:i.prefixes,optional:n})),rules:[...se.rules]})}var _e="1.0";function R(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 w(e){if(e===null||typeof e!="object")return JSON.stringify(e);if(Array.isArray(e))return`[${e.map(w).join(",")}]`;let t=e;return`{${Object.keys(t).sort().map(n=>`${JSON.stringify(n)}:${w(t[n])}`).join(",")}}`}function k(e){return`${e.from}->${e.to}`}function J(e,t,n,r="dependency"){return{id:`${e}:${r}:${k(t)}`,classification:e,subject:"dependency",from:t.from,to:t.to,message:n,...e==="missing"?{nextAction:`Add the planned dependency ${k(t)} to the candidate, then preflight again.`}:e==="contradictory"?{nextAction:`Replace the reverse dependency with ${k(t)}, then preflight again.`}:e==="unplanned"?{nextAction:r==="dependency-removed"?`Restore the removed dependency ${k(t)} in the candidate, then preflight again.`:`Remove the unplanned dependency ${k(t)} from the candidate, then preflight again.`}:{}}}function le(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=>[k(c),c])),a=new Map(e.baseDependencies.map(c=>[k(c),c])),s=new Map(e.candidateDependencies.map(c=>[k(c),c]));for(let c of[...n.values()].sort((o,g)=>o.path.localeCompare(g.path))){let o=r.get(c.path);o?o.operation!==c.operation?t.push({id:`contradictory:file:${c.path}`,classification:"contradictory",subject:"file",path:c.path,expectedOperation:c.operation,actualOperation:o.operation,message:`${c.path} was planned as ${c.operation} but the actual operation is ${o.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:o.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((o,g)=>o.path.localeCompare(g.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((o,g)=>k(o).localeCompare(k(g)))){if(s.has(k(c))){t.push(J("satisfied",c,`${c.from} -> ${c.to} exists in the candidate architecture.`));continue}let o={from:c.to,to:c.from};s.has(k(o))?(l.add(k(o)),t.push(J("contradictory",c,`${c.from} -> ${c.to} was planned, but the candidate contains the reverse edge.`))):t.push(J("missing",c,`${c.from} -> ${c.to} is absent from the candidate architecture.`))}let p=new Set([...n.keys(),...r.keys()]);for(let[c,o]of[...s].sort(([g],[y])=>g.localeCompare(y)))a.has(c)||i.has(c)||l.has(c)||!p.has(o.from)&&!p.has(o.to)||t.push({...J("unplanned",o,`${o.from} -> ${o.to} was added without a matching planned dependency.`,"dependency-added"),actualOperation:"added"});let f=new Set(e.changeMap.map.files.filter(c=>c.operation==="delete").map(c=>c.path));for(let[c,o]of[...a].sort(([g],[y])=>g.localeCompare(y)))s.has(c)||f.has(o.from)||f.has(o.to)||!p.has(o.from)&&!p.has(o.to)||t.push({...J("unplanned",o,`${o.from} -> ${o.to} was removed without a planned file deletion.`,"dependency-removed"),actualOperation:"removed"});let u={satisfied:0,missing:1,contradictory:2,unplanned:3};t.sort((c,o)=>u[c.classification]-u[o.classification]||(c.subject===o.subject?0:c.subject==="file"?-1:1)||c.id.localeCompare(o.id));let m={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:m.missing===0&&m.contradictory===0&&m.unplanned===0,behavioralCompletion:"not-evaluated",summary:m,findings:t}}var Ne="1.0";function C(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 L(e){return[...new Set(e??[])].sort()}function N(e,t,n,r,i){let a=L(n),s=L(r),l=new Set(a),p=new Set(s),f=s.filter(m=>!l.has(m)),u=a.filter(m=>!p.has(m));f.length===0&&u.length===0||(f.length>0&&C(e,{kind:"added",path:t,classification:i.added,message:i.addedMessage,before:a,after:s}),u.length>0&&C(e,{kind:"removed",path:t,classification:i.removed,message:i.removedMessage,before:a,after:s}))}function pe(e,t,n,r,i,a,s){if(n===r)return;C(e,{kind:r?"enabled":"disabled",path:t,classification:r?i:i==="strengthening"?"weakening":"strengthening",message:r?a:s,before:n,after:r})}function de(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 Ut(e,t,n){let r=de(t,a=>a.name),i=de(n,a=>a.name);(r.duplicates.length>0||i.duplicates.length>0)&&C(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 s=r.values.get(a),l=i.values.get(a),p=`$.layers[${a}]`;if(!s&&l){C(e,{kind:"layer-added",path:p,classification:"judgment-required",message:"A layer was added; verify overlap, ownership, and rule coverage.",after:l});continue}if(s&&!l){C(e,{kind:"layer-removed",path:p,classification:"weakening",message:"Removing a layer can leave its source paths ungoverned.",before:s});continue}if(!s||!l)continue;N(e,`${p}.patterns`,s.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."}),N(e,`${p}.exclude`,s.exclude,l.exclude,{added:"weakening",removed:"strengthening",addedMessage:"Additional paths are excluded from this layer.",removedMessage:"Fewer paths are excluded from this layer."});let f=ne(s),u=ne(l);N(e,`${p}.forbiddenGlobals`,f.rawGlobals,u.rawGlobals,{added:"strengthening",removed:"weakening",addedMessage:"Additional forbidden globals are enforced in this layer.",removedMessage:"A forbidden-global protection was removed from this layer."}),N(e,`${p}.capabilities`,f.atoms,u.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)."}),L(s.intentPrefixes).join("\0")!==L(l.intentPrefixes).join("\0")&&C(e,{kind:"intent-prefixes-changed",path:`${p}.intentPrefixes`,classification:"judgment-required",message:"Intent ownership changed and must be reviewed against publishers and consumers.",before:L(s.intentPrefixes),after:L(l.intentPrefixes)}),pe(e,`${p}.mayImportInfrastructure`,s.mayImportInfrastructure===!0,l.mayImportInfrastructure===!0,"weakening","The layer may now import infrastructure directly.","Direct infrastructure imports are no longer allowed for this layer."),pe(e,`${p}.optional`,s.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 Bt(e,t,n){let r=s=>`${s.from}->${s.to}`,i=de(t,r),a=de(n,r);(i.duplicates.length>0||a.duplicates.length>0)&&C(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 s of[...new Set([...i.values.keys(),...a.values.keys()])].sort()){let l=i.values.get(s),p=a.values.get(s),f=`$.rules[${s}]`;if(!l&&p){p.allowed===!1&&C(e,{kind:"deny-added",path:f,classification:"strengthening",message:"A denied dependency edge was added.",after:p});continue}if(l&&!p){l.allowed===!1&&C(e,{kind:"deny-removed",path:f,classification:"weakening",message:"A denied dependency edge was removed.",before:l});continue}if(!l||!p)continue;l.allowed!==p.allowed&&C(e,{kind:p.allowed?"deny-disabled":"deny-enabled",path:`${f}.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 u=l.peerIsolation===!0,m=p.peerIsolation===!0;if(u!==m){let c=l.from===l.to&&p.from===p.to;C(e,{kind:m?"peer-isolation-enabled":"peer-isolation-disabled",path:`${f}.peerIsolation`,classification:c?m?"strengthening":"weakening":"judgment-required",message:c?m?"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:u,after:m})}L(l.sliceFolders).join("\0")!==L(p.sliceFolders).join("\0")&&C(e,{kind:"slice-folders-changed",path:`${f}.sliceFolders`,classification:"judgment-required",message:"Slice ownership folders changed and can reclassify existing dependencies.",before:L(l.sliceFolders),after:L(p.sliceFolders)})}}function zt(e,t,n){let r=t.safety??{},i=n.safety??{};for(let a of["maxTsSuppressions","maxAnyCasts"]){let s=r[a]??0,l=i[a]??0;s!==l&&C(e,{kind:l>s?"threshold-raised":"threshold-lowered",path:`$.safety.${a}`,classification:l>s?"weakening":"strengthening",message:l>s?"The safety threshold allows more violations.":"The safety threshold allows fewer violations.",before:s,after:l})}for(let a of["allowInMemory","allowDisabledPeerIsolation"])pe(e,`$.safety.${a}`,r[a]===!0,i[a]===!0,"weakening","A safety exception was enabled.","A safety exception was disabled.")}function Kt(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=[];N(n,"$.include",e.include,t.include,{added:"strengthening",removed:"weakening",addedMessage:"Additional project roots are governed.",removedMessage:"Project roots were removed from governance."}),N(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."}),N(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."}),pe(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 s=r[a]===r[i]?"judgment-required":r[a]>r[i]?"strengthening":"weakening";C(n,{kind:"cycle-policy-changed",path:"$.cyclePolicy",classification:s,message:"The cycle enforcement level changed.",before:i,after:a})}return(e.frameworkOverlay??null)!==(t.frameworkOverlay??null)&&C(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}),Ut(n,e.layers,t.layers),Bt(n,e.rules,t.rules),zt(n,e,t),n.sort((s,l)=>s.path.localeCompare(l.path)||s.id.localeCompare(l.id)),{schemaVersion:Ne,classification:Kt(n),findings:n}}function Te(e,t){if(!e||e.schemaVersion!==Ne||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=L(e.findingIds),r=L(t.findingIds);return n.length===r.length&&n.every((i,a)=>i===r[a])}function M(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 et(e){return e!==void 0&&/[A-Za-z0-9_$]/.test(e)}function $(e,t){for(;t<e.length&&/\s/.test(e[t]);)t+=1;return t}function Z(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 P(e,t,n){return e.startsWith(t,n)&&!et(e[n-1])&&!et(e[n+t.length])}function Yt(e,t){if(t=$(e,t+6),e[t]==="(")return Z(e,$(e,t+1));let n=!1;if(P(e,"type",t)){let i=$(e,t+4);e[i]!==","&&!P(e,"from",i)&&(n=!0)}let r=tt(e,t,!0);return r&&n?{...r,typeOnly:!0}:r}function qt(e,t){t=t+6;let n=$(e,t),r=!1;if(P(e,"type",n)){let a=$(e,n+4);(e[a]==="{"||e[a]==="*")&&(r=!0)}let i=tt(e,t,!1);return i&&r?{...i,typeOnly:!0}:i}function tt(e,t,n){for(;t<e.length;t+=1){if(e[t]===";")return;if(P(e,"from",t))return Z(e,$(e,t+4));if(n&&(e[t]==="'"||e[t]==='"'))return Z(e,t);if(t>0&&(P(e,"import",t)||P(e,"export",t)))return}}function Wt(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 Jt(e,t){let n=t-1;for(;n>=0&&/\s/.test(e[n]);)n-=1;if(e[n]===".")return;let r=$(e,t+7);if(e[r]!=="(")return;r=$(e,r+1);let i=Z(e,r);return i?{...i,requireCall:!0}:void 0}function Zt(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(`
|
|
5
|
+
`,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=Wt(e,n);continue}if(r==="'"||r==='"'){let a=Z(e,n);a&&(n=a.offset+a.excerpt.length-1);continue}let i=r==="i"&&P(e,"import",n)?Yt(e,n):r==="e"&&P(e,"export",n)?qt(e,n):r==="r"&&P(e,"require",n)?Jt(e,n):void 0;i&&(t.push(i),n=i.offset+i.excerpt.length-1)}return t}function nt(e,t){let n=[],r=[];for(let i of Zt(e.content)){let a=i.value;if(i.requireCall&&a.startsWith("."))continue;if(!a.startsWith(".")){if(i.typeOnly)continue;let f=B(a);if(!f)continue;let u=e.content.slice(0,i.offset).split(`
|
|
6
|
+
`).length;r.push({file:e.path,symbol:a,capability:f,evidence:{kind:"import",file:e.path,line:u,excerpt:i.excerpt}});continue}let s=e.content.slice(0,i.offset).split(`
|
|
7
|
+
`).length,l={kind:"import",file:e.path,line:s,excerpt:i.excerpt},p=Qt(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 Qt(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 s=n.get(a);if(s)return s}}function rt(e,t){let n=[];for(let r of e){if(!r.to||!r.fromLayer||!r.toLayer)continue;let i=v(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 fe(e,t){let n=typeof e=="string"?ve(e,t):oe(e,t);return{...n,policyHash:R(w(n.config))}}function it(e){let t=fe(e.baseConfig,e.baseSource??"base ark.config.json"),n=fe(e.candidateConfig,e.candidateSource??"candidate ark.config.json"),r=Me(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,s=a&&Te(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:s,valid:!a||s}}function Q(e){let t=e.files.map(p=>{let f=M(p.path);return{path:f,content:p.content,contentHash:R(p.content),layer:K(f,e.contract.config.layers)??null}}).sort((p,f)=>p.path.localeCompare(f.path)),n=new Map(t.map(p=>[p.path,p])),r=[],i=[];for(let p of t){let f=nt(p,n);r.push(...f.edges),i.push(...f.capabilityUses)}let a=rt(r,e.contract.config),s=new Map(e.contract.config.layers.map(p=>[p.name,p])),l=new Map(e.contract.config.layers.map(p=>[p.name,new Set(te(p))]));for(let p of i){let f=n.get(p.file)?.layer;if(!f)continue;let u=s.get(f),m=D(p.symbol,u?.forbiddenGlobals??[]);if(m){a.push({ruleId:"FORBIDDEN_GLOBAL",message:`${f} must not use module "${p.symbol}" because it is the import form of forbidden global "${m}".`,symbol:p.symbol,evidence:p.evidence});continue}l.get(f)?.has(p.capability)&&a.push({ruleId:"CAPABILITY_VIOLATION",message:`${f} denies the ${p.capability} capability; found import of "${p.symbol}".`,capability:p.capability,symbol:p.symbol,evidence:p.evidence})}return{ir:{schemaVersion:"1.0",policyHash:e.contract.policyHash,compilerOptionsHash:R(w(e.compilerOptions??{})),files:t,layers:e.contract.config.layers.map(p=>p.name),edges:r,capabilityUses:i,violations:a}}}function ue(e){let t=new Map(e.files.map(n=>[M(n.path),n]));for(let n of e.changes){let r=M(n.path);"delete"in n&&n.delete?t.delete(r):"content"in n&&t.set(r,{path:r,content:n.content})}return Q({contract:e.contract,files:[...t.values()],compilerOptions:e.compilerOptions})}function at(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 De(e){let t=0,n=new Map,r=new Map,i=new Set,a=[],s=[],l=p=>{n.set(p,t),r.set(p,t),t+=1,a.push(p),i.add(p);for(let m of[...e.get(p)??[]].sort())e.has(m)&&(n.has(m)?i.has(m)&&r.set(p,Math.min(r.get(p)??0,n.get(m)??0)):(l(m),r.set(p,Math.min(r.get(p)??0,r.get(m)??0))));if(r.get(p)!==n.get(p))return;let f=[],u;do{if(u=a.pop(),u===void 0)break;i.delete(u),f.push(u)}while(u!==p);f.length>1&&s.push(f.sort())};for(let p of[...e.keys()].sort())n.has(p)||l(p);return s.sort((p,f)=>p[0].localeCompare(f[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 ye(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 s=v(e.rules,a.fromLayer,a.toLayer,{fromPath:a.from,toPath:a.to,layers:e.config.layers});if(!s)continue;let l=!!s.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:s.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=De(r);i==="soft"||i==="framework-soft"?n.push(...a.map(s=>({...s,message:`${s.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 ot(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 st(e){return R(w(e.map(({path:t,contentHash:n})=>({path:t,contentHash:n}))))}function ct(e){let t=Q(e),n=new Map(t.ir.files.map(o=>[o.path,o])),r=[],i=new Set,a=[];for(let o of e.changes){let g=o.path.replace(/\\/g,"/"),y=M(o.path);if(!y||y===".."||y.startsWith("../")||g.startsWith("/")||/^[A-Za-z]:\//.test(g)||g.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(y)){a.push({ruleId:"DUPLICATE_CHANGE_PATH",file:y,line:1,message:`The atomic change set contains more than one operation for ${y}.`});continue}i.add(y),"delete"in o&&o.delete&&!n.has(y)&&a.push({ruleId:"DELETE_TARGET_MISSING",file:y,line:1,message:`Cannot delete ${y} because it is not present in the supplied base tree.`}),r.push("delete"in o&&o.delete?{path:y,delete:!0}:{path:y,content:"content"in o?o.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 s=ue({...e,changes:r}),l=new Map(s.ir.files.map(o=>[o.path,o])),p=s.ir.violations.filter(o=>o.ruleId==="CAPABILITY_VIOLATION"||o.ruleId==="FORBIDDEN_GLOBAL").map(o=>{let g=l.get(o.evidence.file)?.layer;return{ruleId:o.ruleId,file:o.evidence.file,line:o.evidence.line,target:o.symbol,...o.capability?{capability:o.capability}:{},...g?{fromLayer:g}:{},edgeKind:"import",message:o.message}}),f=ye({config:e.contract.config,rules:e.contract.config.rules,files:s.ir.files.map(o=>o.path),contentViolations:p,edges:s.ir.edges.filter(o=>!!o.fromLayer).map(o=>({from:o.from,fromLayer:o.fromLayer,...o.to?{to:o.to}:{},...o.toLayer?{toLayer:o.toLayer}:{},line:o.evidence.line,kind:"import"}))}),u=r.map(o=>{let g=M(o.path),y=n.get(g),I=l.get(g);return{path:g,operation:"delete"in o&&o.delete?"delete":y?"update":"create",...y?{beforeContentHash:y.contentHash}:{},...I?{candidateContentHash:I.contentHash}:{}}}).sort((o,g)=>o.path.localeCompare(g.path)),m=[...a,...f.violations].map(o=>({...o,nextAction:ot(o)})),c=e.changeMap?le({changeMap:e.changeMap,changes:u,baseDependencies:t.ir.edges.flatMap(o=>o.to?[{from:o.from,to:o.to}]:[]),candidateDependencies:s.ir.edges.flatMap(o=>o.to?[{from:o.from,to:o.to}]:[])}):void 0;return{schemaVersion:"1.0",valid:m.length===0&&(c?.structurallyConverged??!0),readOnly:!0,policyHash:e.contract.policyHash,compilerOptionsHash:s.ir.compilerOptionsHash,baseTreeHash:st(t.ir.files),candidateTreeHash:st(s.ir.files),...e.changeMap?{changeMapHash:e.changeMap.hash}:{},...c?{convergence:c}:{},changes:u,violations:m,warnings:f.warnings}}function S(e,t,n={}){return{ruleId:e,message:t,...n}}function lt(e){let{config:t,rules:n,files:r,manifest:i}=e,a=[];if(t.dynamicImportAllowlist!==void 0&&(!Array.isArray(t.dynamicImportAllowlist)||t.dynamicImportAllowlist.some(o=>typeof o!="string"))&&a.push(S("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(S("CONFIG_INVALID_SAFETY","safety must be an object."));else if(t.safety)for(let o of["maxTsSuppressions","maxAnyCasts"]){let g=t.safety[o];g!==void 0&&(!Number.isInteger(g)||g<0)&&a.push(S("CONFIG_INVALID_SAFETY_THRESHOLD",`safety.${o} must be a non-negative integer.`))}let s=Array.isArray(t.layers)?t.layers:[],l=Array.isArray(i?.architecture?.layers)?i.architecture.layers:[],p=new Set([...s.map(o=>o.name).filter(Boolean),...l.map(o=>o.name).filter(o=>!!o)]);s.length===0&&a.push(S("CONFIG_NO_LAYERS","No file layers are configured; ark-check cannot classify files for import-boundary enforcement."));let f=new Set,u=new Set;for(let o of s){if(!o.name){a.push(S("CONFIG_LAYER_WITHOUT_NAME","A configured layer is missing a name."));continue}f.has(o.name)&&u.add(o.name),f.add(o.name),o.forbiddenGlobals!==void 0&&(!Array.isArray(o.forbiddenGlobals)||o.forbiddenGlobals.some(y=>typeof y!="string"))&&a.push(S("CONFIG_INVALID_FORBIDDEN_GLOBALS",`Layer "${o.name}" has an invalid forbiddenGlobals value; expected an array of strings (e.g. ["fetch", "Date.now"]). The entry is ignored.`,{layer:o.name}));let g=Array.isArray(o.patterns)?o.patterns:[];if(g.length===0){a.push(S("CONFIG_LAYER_WITHOUT_PATTERNS",`Layer "${o.name}" has no file patterns and will never classify files.`,{layer:o.name}));continue}for(let y of g){let I;try{I=z(y)}catch(x){a.push(S("CONFIG_INVALID_LAYER_PATTERN",`Layer "${o.name}" has an invalid pattern "${y}": ${x instanceof Error?x.message:String(x)}`,{layer:o.name,pattern:y}));continue}!r.some(x=>I.test(x))&&!o.optional&&a.push(S("CONFIG_LAYER_PATTERN_NO_MATCHES",`Layer "${o.name}" pattern "${y}" matched no included files.`,{layer:o.name,pattern:y,failsStrict:!1}))}}for(let o of u)a.push(S("CONFIG_DUPLICATE_LAYER",`Layer "${o}" is configured more than once.`,{layer:o}));if(p.size>0)for(let o of n??[])o.from&&!p.has(o.from)&&a.push(S("CONFIG_RULE_UNKNOWN_FROM_LAYER",`Rule references unknown source layer "${o.from}".`,{fromLayer:o.from,toLayer:o.to})),o.to&&!p.has(o.to)&&a.push(S("CONFIG_RULE_UNKNOWN_TO_LAYER",`Rule references unknown target layer "${o.to}".`,{fromLayer:o.from,toLayer:o.to}));let m=new Set;if(s.length>1)for(let o of r){let g=-1,y=[];for(let I of s)for(let x of I.patterns??[]){if(!z(x).test(o))continue;let d=ke(x);d>g?(g=d,y=[I.name]):d===g&&!y.includes(I.name)&&y.push(I.name)}y.length>1&&m.add([...y].sort().join(" + "))}m.size>0&&a.push(S("CONFIG_AMBIGUOUS_LAYERS",`Some files match multiple layers at equal specificity; classification falls back to declaration order. Disambiguate the overlapping patterns: ${[...m].join(", ")}.`,{pairs:[...m]}));let c=r.filter(o=>!K(o,s));return c.length>0&&a.push(S("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{_e as ANALYSIS_IR_SCHEMA_VERSION,yt as ARK_ANALYSIS_RESULT_SCHEMA,dt as ARK_ANALYSIS_RESULT_SCHEMA_VERSION,$e as ARK_CONFIG_SCHEMA,_t as ARK_CONFIG_SCHEMA_VERSION,Ne as POLICY_DELTA_SCHEMA_VERSION,le as analyzeArchitectureConvergence,ue as analyzeChange,it as analyzePolicyDelta,Q as analyzeProject,Me as classifyArkPolicyDelta,lt as collectAnalysisConfigWarnings,G as collectForbiddenCapabilityUses,qe as createAICodeGate,ut as createAdapterResult,ce as createArchitectureProfile,Qe as createArchitectureProfileFromArkConfig,Xe as createElevenLayerArkConfig,De as detectArchitectureCycles,R as deterministicHash,se as elevenLayerProfile,ye as evaluateArchitectureGraph,at as explainViolation,j as extractSemanticDependencies,oe as loadArkConfigContract,fe as loadContract,ve as parseArkConfigJson,Te as policyDeltaAcknowledgementMatches,ct as preflightChange,w as stableSerialize,me as toAdapterDiagnostic,pt as version};
|
package/docs/agent-guide.md
CHANGED
|
@@ -112,7 +112,7 @@ Smell **ids** (stable JSON) plus **outcome** lines (plain language, Q02) on each
|
|
|
112
112
|
| id | Outcome (what to do / why the AI struggles) |
|
|
113
113
|
|----|-----------------------------------------------|
|
|
114
114
|
| `io-under-application` | Business code reaches DB/APIs directly — put I/O behind a port/adapter |
|
|
115
|
-
| `handler-in-persistence` | HTTP
|
|
115
|
+
| `handler-in-persistence` | Static framework HTTP imports (`next/server`), `defineRoute` calls, or handler bodies under Persistence-role storage folders — move transport to API/UI |
|
|
116
116
|
| `god-module` | Huge multi-job files — split the pilot by concern |
|
|
117
117
|
| `domain-logic-in-ui` | can*/calculate* in UI — move pure rules into Domain |
|
|
118
118
|
| `facade-sql-in-routes` | Routes import ORM/SQL — keep queries in repository/adapter |
|
|
@@ -164,6 +164,29 @@ never move. `reshapePilot.nextPilot` is a **proposed** one-at-a-time card (`move
|
|
|
164
164
|
write gate + atomic preflight; merges are `/ark-architect` judgment cards. `notAScore`, never a
|
|
165
165
|
verdict/`designFitness` input; there is no apply path.
|
|
166
166
|
|
|
167
|
+
**Reshape decision memory (Y01):** when the team accepts, defers, or rejects that target, record
|
|
168
|
+
the explicit verdict in `.ark/reshape-decisions.json` using the card's exact
|
|
169
|
+
`decisionTarget` (`concept` + complete sorted `anchors`), a non-empty `reason`, and optional
|
|
170
|
+
`reviewBy` (`YYYY-MM-DD`). Current rejected/deferred decisions hide only the repeated pilot card;
|
|
171
|
+
the physical facts keep rendering. Accepted keeps the same `/ark-loop` path. Expired/malformed
|
|
172
|
+
dates and decisions whose anchor set changed no longer apply; doctor/report list them. The golden
|
|
173
|
+
pattern is never parsed to infer a decision — cite it in `reason` when it explains the layout.
|
|
174
|
+
|
|
175
|
+
```json
|
|
176
|
+
{
|
|
177
|
+
"schemaVersion": "1",
|
|
178
|
+
"decisions": [
|
|
179
|
+
{
|
|
180
|
+
"concept": "projects",
|
|
181
|
+
"anchors": ["src/app/api", "src/lib/api-handlers", "src/lib/repositories"],
|
|
182
|
+
"verdict": "rejected",
|
|
183
|
+
"reason": "These role directories are our thin-shell-handlers-data golden layout.",
|
|
184
|
+
"reviewBy": "2027-01-31"
|
|
185
|
+
}
|
|
186
|
+
]
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
167
190
|
**Governance weight (W02):** `contractHealth.governanceWeight` reports raw facts (layers, rules,
|
|
168
191
|
governed files, files/layer, rules/layer) plus a fixed band (`heavy` / `typical` / `light` /
|
|
169
192
|
`unknown`) with fixed wording. It is explicitly `notAScore` — never a gate input. Read `heavy` as
|
|
@@ -368,7 +391,7 @@ npx arkgate-check --install-agent-gates --tools claude,cursor,codex,grok
|
|
|
368
391
|
|------|-----------------|-------------|
|
|
369
392
|
| Claude Code | `.claude/settings.json` hook + `.mcp.json` / `claude mcp add` | `.claude/skills/<name>/SKILL.md` |
|
|
370
393
|
| Cursor | `.cursor/mcp.json` + `.cursor/rules/ark.mdc` | `.cursor/commands/` |
|
|
371
|
-
| OpenAI Codex |
|
|
394
|
+
| OpenAI Codex | `.codex/config.toml` (project primary, relative `--root .`); optional legacy `$CODEX_HOME/config.toml` fallback uses absolute roots and scoped secondaries — see [ai-gates.md](ai-gates.md) | **Repo:** `.agents/skills/<name>/SKILL.md`; **home:** `$CODEX_HOME/skills/<name>/SKILL.md` (`--codex-home`) |
|
|
372
395
|
| **Grok Build** | `.grok/hooks/ark-write-gate.json` + `.grok/config.toml` / `.mcp.json` | `.grok/skills/<name>/SKILL.md` |
|
|
373
396
|
|
|
374
397
|
This is a path reference, not a guarantee table. Full copy-paste setups:
|
|
@@ -630,7 +653,15 @@ modules the way your build does, but is intentionally not yet a full type-graph
|
|
|
630
653
|
|
|
631
654
|
Repeat runs are cached in `node_modules/.cache/ark-check.json` — unchanged files skip the
|
|
632
655
|
parse, while import edges always re-resolve against the live filesystem so the cache can
|
|
633
|
-
never hide a new violation.
|
|
656
|
+
never hide a new violation. Cache schema v9 also preserves each file's parse-diagnostic count
|
|
657
|
+
and binds it to the TypeScript parser version, so a warm doctor run cannot silently turn
|
|
658
|
+
unreadable governed syntax into a clean claim after a parser change.
|
|
659
|
+
`--no-cache` disables it.
|
|
660
|
+
|
|
661
|
+
`ark-check --doctor --json` reports that syntax honesty under `doctor.parseHealth`: exact
|
|
662
|
+
scanned/affected/diagnostic totals plus a deterministic top-12 file list and honest overflow.
|
|
663
|
+
It is advisory in this release — it does not change the verdict, design fitness, pattern bets,
|
|
664
|
+
or violations — but an affected governed file must not be described as successfully inspected.
|
|
634
665
|
|
|
635
666
|
`ark-check --json` also reports `warnings` for incomplete governance coverage: missing
|
|
636
667
|
layers, unclassified included files, unmatched layer patterns, duplicate layers, and rules
|
package/docs/ai-gates.md
CHANGED
|
@@ -263,23 +263,22 @@ npx ark-check --install-agent-gates --tools codex
|
|
|
263
263
|
The generated hook includes `--hook-repair`, so a rejected patch carries the same structured
|
|
264
264
|
repair envelope as Claude and Grok. Codex still needs hook trust enabled for the project.
|
|
265
265
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
`.` resolves against the launch directory. Prefer absolute paths, or let Ark write them:
|
|
266
|
+
Modern Codex resolves MCP servers from the active project's `.codex/config.toml`. Ark writes
|
|
267
|
+
that file with relative paths, so every repository owns its primary `ark` binding without
|
|
268
|
+
competing for a global slot:
|
|
270
269
|
|
|
271
270
|
```bash
|
|
272
271
|
npx ark-check --install-agent-gates --tools codex
|
|
273
|
-
# optional: install /ark-* skills
|
|
272
|
+
# optional: install /ark-* skills and a legacy global MCP fallback under $CODEX_HOME
|
|
274
273
|
npx ark-check --install-agent-gates --codex-home
|
|
275
274
|
```
|
|
276
275
|
|
|
277
|
-
|
|
276
|
+
Project-scoped shape:
|
|
278
277
|
|
|
279
278
|
```toml
|
|
280
279
|
[mcp_servers.ark]
|
|
281
280
|
command = "npx"
|
|
282
|
-
args = ["arkgate-mcp", "--root", "
|
|
281
|
+
args = ["arkgate-mcp", "--root", ".", "--config", "ark.config.json"]
|
|
283
282
|
```
|
|
284
283
|
|
|
285
284
|
Then **restart Codex** — it does not hot-load MCP servers. Expect resource `ark://manifest`
|
|
@@ -288,11 +287,12 @@ and tools `validate_code`, `ark_check`, `ark_coverage`, `ark_place`.
|
|
|
288
287
|
Codex uses the best-effort local patch hook plus advisory MCP for discovery/validation and
|
|
289
288
|
`ark-check` as the hard merge backstop. Register all three as soon as the repo is adopted.
|
|
290
289
|
|
|
291
|
-
###
|
|
290
|
+
### Legacy Codex home fallback
|
|
292
291
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
292
|
+
`$CODEX_HOME/config.toml` remains supported for older clients or an explicit global binding.
|
|
293
|
+
Because it is global, its `[mcp_servers.ark]` is a single primary. If project A is already
|
|
294
|
+
registered and you run `--codex-home` for project B without `--force`, Ark preserves A and
|
|
295
|
+
writes a scoped secondary table:
|
|
296
296
|
|
|
297
297
|
```toml
|
|
298
298
|
[mcp_servers.ark] # primary — still project A
|
|
@@ -304,13 +304,15 @@ primary A. It writes a **scoped secondary** table:
|
|
|
304
304
|
|
|
305
305
|
| Goal | Command |
|
|
306
306
|
|------|---------|
|
|
307
|
-
|
|
|
308
|
-
|
|
|
307
|
+
| Install the normal project binding | `ark-check --install-agent-gates --tools codex` |
|
|
308
|
+
| Add B to the legacy home fallback | `ark-check --install-agent-gates --codex-home` |
|
|
309
|
+
| Make B the legacy home primary | `ark-check --install-agent-gates --codex-home --force` |
|
|
309
310
|
| Doctor: primary points at another permanent project | gap id `codex-home-multi-project` (warn if no secondary yet and session host is unknown/Codex; **info + `deferred`** when the session host is known and not Codex — e.g. Grok/Claude/Cursor; info if a scoped secondary is already present) |
|
|
310
|
-
| When using Codex: refresh home skills
|
|
311
|
+
| When using Codex: refresh home skills | `ark-check --install-agent-gates --skills-only --codex-home --force` |
|
|
311
312
|
|
|
312
|
-
|
|
313
|
-
|
|
313
|
+
When a valid project `.codex/config.toml` exists, doctor treats it as the effective binding and
|
|
314
|
+
does not report an unrelated home primary. Without a project binding, doctor surfaces the
|
|
315
|
+
legacy multi-project state. **Deferred (fix when using Codex):**
|
|
314
316
|
non-temp Codex-home gaps (`codex-home-multi-project`, stale `$CODEX_HOME/skills`) are
|
|
315
317
|
severity **info**, marked `deferred: true`, and omitted from Top actions when the session
|
|
316
318
|
host is known and not Codex — `/ark-upgrade` on Grok/Claude is not Incomplete because of
|
|
@@ -444,11 +446,12 @@ export default [
|
|
|
444
446
|
|
|
445
447
|
- Relative imports are resolved to on-disk TS/JS targets; package bare imports are left to CI/TS.
|
|
446
448
|
- Type-only and value forbidden edges both error (same pass/fail as `arkgate-check`).
|
|
447
|
-
- `no-forbidden-globals`
|
|
449
|
+
- `no-forbidden-globals` applies from the file layer’s `forbiddenGlobals`; the `globals` option is only a standalone fallback when no project config applies, never an override that weakens the project contract. Layers without either surface are not inventively restricted. `process` also owns exact value imports of `process` / `node:process`; type-only forms, subpaths, and `child_process` stay excluded. If the same layer also denies the `process` capability, this rule is the single `FORBIDDEN_GLOBAL` voice.
|
|
448
450
|
- Without `ark.config.json`, `no-domain-infra-imports` falls back to a domain→infra path heuristic.
|
|
449
451
|
|
|
450
452
|
Rule ids are `ark/<kebab-name>`. Individual rules are also on `ark.rules` if you wire them by hand.
|
|
451
|
-
Prefer keeping editor + CI on the same `ark.config.json
|
|
453
|
+
Prefer keeping editor + CI on the same `ark.config.json`. Use the rule-local `globals` list only
|
|
454
|
+
for standalone linting where no project contract applies.
|
|
452
455
|
|
|
453
456
|
## CI backstop
|
|
454
457
|
|
package/docs/configuration.md
CHANGED
|
@@ -87,6 +87,12 @@ Layer fields:
|
|
|
87
87
|
(`network`, `filesystem`, `clock`, `randomness`, `environment`, `process`, `persistence`);
|
|
88
88
|
`pure: true` is the shorthand that denies all seven. Absence changes no verdict.
|
|
89
89
|
|
|
90
|
+
`forbiddenGlobals: ["process"]` covers the ambient binding plus exact runtime imports from
|
|
91
|
+
`process` and `node:process`. It does not imply the broader `process` capability wall: subpaths
|
|
92
|
+
and `child_process` remain outside this narrow dual, and statement-level `import type` /
|
|
93
|
+
`export type` declarations are excluded. Declare `capabilities.deny: ["process"]` when the whole
|
|
94
|
+
process module-capability family must be denied.
|
|
95
|
+
|
|
90
96
|
Rule fields:
|
|
91
97
|
|
|
92
98
|
- `from`, `to`, `allowed`, `message`, `peerIsolation`, `sliceFolders`
|
|
@@ -21,7 +21,7 @@ Installs:
|
|
|
21
21
|
|------|-------------|
|
|
22
22
|
| Claude Code | `.claude/settings.json`, `.claude/skills/` |
|
|
23
23
|
| Cursor | `.cursor/mcp.json`, `.cursor/rules/ark.mdc`, `.cursor/commands/` |
|
|
24
|
-
| Codex | `.codex/hooks.json`, `.
|
|
24
|
+
| Codex | `.codex/hooks.json`, `.codex/config.toml`, `.agents/skills/` |
|
|
25
25
|
| **Grok Build** | `.grok/config.toml`, `.grok/hooks/`, `.grok/skills/` |
|
|
26
26
|
|
|
27
27
|
See the [canonical host support matrix](../../README.md#host-enforcement-support) for the exact
|
package/docs/package-surface.md
CHANGED
|
@@ -17,20 +17,21 @@ hardening guide remains repository-hosted rather than duplicated in the gate tar
|
|
|
17
17
|
| Surface | How you use it | Stability notes |
|
|
18
18
|
|---------|----------------|-----------------|
|
|
19
19
|
| **CLI** | `arkgate` / `arkgate-check` (aliases `ark` / `ark-check`) | Flags and human text may improve; **JSON output shapes** for `--json` (check, doctor, plan, coverage, recommend) are stable within a major. Additive fields OK; removals/renames are major. |
|
|
20
|
-
| **Doctor design fitness (P02+)** | `ark-check --doctor --json` → `doctor.designFitness`, `doctor.designSmells[]` | Additive. Stable smell `id`s: `io-under-application`, `handler-in-persistence`, `god-module`, `domain-logic-in-ui`, `facade-sql-in-routes`, `mixed-pattern-cluster`, `soft-contract`. Each smell has `evidence[]`, `fix`, technical `message`, and plain-language **`outcome`** (Q02). Does **not** fail the gate by itself. |
|
|
20
|
+
| **Doctor design fitness (P02+)** | `ark-check --doctor --json` → `doctor.designFitness`, `doctor.designSmells[]` | Additive. Stable smell `id`s: `io-under-application`, `handler-in-persistence`, `god-module`, `domain-logic-in-ui`, `facade-sql-in-routes`, `mixed-pattern-cluster`, `soft-contract`. Y02 extends `handler-in-persistence` to static ES imports/re-exports of framework HTTP surfaces (`next/server`), `defineRoute` calls, and existing handler bodies inside Persistence-role layers or specific persistence paths; `require()` and dynamic `import()` are outside this narrow advisory, and a generic `Infrastructure` role alone is not Persistence. Persistence candidates are filtered and sorted before the bounded content scan so large application prefixes cannot hide the advisory. The detector inspects the first 800 sorted Persistence candidates; later candidates are uninspected, so absence of this advisory is not whole-tree proof above that envelope. Each smell has `evidence[]`, `fix`, technical `message`, and plain-language **`outcome`** (Q02). Does **not** fail the gate by itself. |
|
|
21
21
|
| **Post-green path (Q01)** | `doctor.postGreenPath`, `doctor.primaryNextAction`, `doctor.healthyFinishedForbidden` | Additive when `designFitness.designWeak`. Single Shape door (`id: clarify-for-ai`): explore shape-focus → dual-plan B → autopilot only with OK. Never empty plan A = healthy finished. |
|
|
22
22
|
| **Golden pattern (Q03)** | Optional `.ark/golden-pattern.json`; doctor JSON `doctor.goldenPattern`; MCP `ark_place` / `ark_prepare_write` → `goldenPattern` | Additive, **advisory for NEW code only**. Required fields: `name`, `norm`; optional `newCodeHome`, `examplePath`, `schemaVersion`. **Absent is normal** (no claim). Never ENFORCE; never clears design-weak. Malformed → `invalid: true`, not silent guidance. |
|
|
23
23
|
| **Plan pattern B (P03+)** | `ark-check --plan --json` → `plan.patternBets[]`, `plan.goal.designWeak` | Additive. Each bet: `id`, `smellId`, `pilot`, `evidence`, `successSignal`, `killSwitch`, **`neverMechanicalSafe: true`**, `class: "judgment"`. **Never** auto-applied by loop/autoPatch; not a `remediationKind` mechanical-safe. `goal.met` remains edge honesty only. |
|
|
24
24
|
| **Pilot loop (Q04)** | `plan.pilotLoop` / `doctor.pilotLoop` | Additive. When design-weak: `active`, `oneAtATime`, `neverMechanicalSafe`, **`nextPilot`** extraction-card fields (`pilotTarget`, `smellId`, `move`, `successSignal`, `killSwitch`, `doNot[]`). **One pilot → re-doctor**; never multi-pilot batch; never mechanical-safe. |
|
|
25
25
|
| **AI-velocity eval (Q05)** | `npm run eval:ai-velocity` → `eval/ai-velocity-report.json` | Fixture-measured (no live LLM). Same feature scenario on design-weak vs golden-path arms; metric **`placementTurns`** (agent-equivalent). Golden must be strictly better. Method string lives next to the number. Does not weaken the gate. |
|
|
26
26
|
| **Contract health (W01)** | `ark-check --doctor --json` → `doctor.contractHealth`; optional `.ark/contract-smell-acks.json` | Additive, **advisory only** — meta-lint of the contract itself (layer-name heuristics; imprecision costs a warning line, never a verdict); never changes the verdict, `designFitness`, or `patternBets`. Stable smell ids: `contract-bidirectional-allow`, `contract-peripheral-depends-core`, `contract-lateral-adapter-allow`, `contract-dead-rule`; each smell has `severity`, `evidence[]` (sorted, honest `…(+N more)` truncation), `fix`, `message`, plain-language `outcome`, and `acknowledgedEdges` (acks applied to that id). **X03/X06**: the lateral smell does not fire on an adapter reaching its **own family's infra base** — the target reads `<Family><InfraWords…>` (**every** remaining target token an infra word: `Infra(structure)`/`Base`/`Core`/`Shared`/`Common`/`Kernel`/`Platform`/`Foundation`) and the source carries the family token **anywhere** in its name (X06, field: `HoursPersistenceAdapters -> PersistenceInfrastructure` — mid-name families). `PaymentsCoreAdapters` is still a sibling; cross-family edges, non-infra siblings, and the reverse (base → member) still fire. Acknowledgments live in the bounded sidecar (`{ acks: [{ id, edge, reason, reviewBy? }] }`, ≤64 KB / ≤200 entries; bidirectional edges order-insensitive, exact two segments); `contractHealth.acknowledged` counts **applied** acks only (stale acks count 0). **X02 ack lifecycle**: optional `reviewBy` (`YYYY-MM-DD`, strict round-trip validation — `2026-02-30` is malformed) — past the date the ack **stops applying** and the smell returns with `(ack expired …)` annotated evidence; among dated entries a fresh re-ack wins over a dead one, but once ANY dated ack exists for an edge the dated entries govern — a leftover undated duplicate cannot resurrect an expired exception. `detectContractSmells` defaults `today` to the real clock (pass `null` to disable expiry); `analyzeContractSmells` stays pure (clock injected). `contractHealth.ackLifecycle` reports `{ undated, malformed, expiredCount, expired[], staleCount, stale[] (lists capped at 12) }`; undated acks apply (backward compatible) but surface in doctor, report, and the fossilization note even when every smell is suppressed. **X05**: an ack matching **no detected edge** (orphaned by a fixed contract or quieted heuristic, unknown id, or typo'd edge) is `stale` — it suppresses nothing and doctor/report list the exact entries to fix or delete, even at zero visible smells. Malformed `reviewBy` never applies (fail-loud, like a sloppy edge); non-string `reviewBy` → whole file `invalid`. **Absent is normal**; malformed file or edge grammar → ignored + `ackFile.invalid` where applicable, never silent suppression. |
|
|
27
|
-
| **Effect capabilities (U03)** | Analysis API `collectCapabilityUses(ts, sourceFile)` + Domain vocabulary (`CAPABILITY_IDS`, `capabilityForModuleSpecifier`, `capabilityForAmbientName`, `lowerForbiddenGlobal`); pure-engine `ir.capabilityUses` | Additive within IR `1.0`. Seven **closed** ids: `network`, `filesystem`, `clock`, `randomness`, `environment`, `process`, `persistence` (ADR 0009). Direct evidence only — transitive inference never detects. The symbol-aware collector covers ambient globals (shadowing/type-only/globalThis-alias precision from the S05/C04 machinery) plus imports; the compiler-free IR engine carries **import-based** uses only (exact module or subpath match, never substring; textual `import type`/`export type` erasure — ANY braced named-binding list stays a value import there; template-literal bodies are skipped entirely (specifiers inside `${…}` are the symbol path's job); `require(…)` counts as capability evidence but never creates a graph edge on the pure path). **U04 walls are opt-in:** per-layer `capabilities: { deny: [...] }` or the dual-depth sugar `pure: true` (denies all seven); absence changes no verdict. `CAPABILITY_VIOLATION` is judgment-class (never mechanical-safe) with a port-injection `nextAction`; D7 dedup —
|
|
27
|
+
| **Effect capabilities (U03/Y08)** | Analysis API `collectCapabilityUses(ts, sourceFile)` + Domain vocabulary (`CAPABILITY_IDS`, `capabilityForModuleSpecifier`, `capabilityForAmbientName`, `forbiddenGlobalForModuleSpecifier`, `lowerForbiddenGlobal`); pure-engine `ir.capabilityUses` | Additive within IR `1.0`. Seven **closed** ids: `network`, `filesystem`, `clock`, `randomness`, `environment`, `process`, `persistence` (ADR 0009). Direct evidence only — transitive inference never detects. The symbol-aware collector covers ambient globals (shadowing/type-only/globalThis-alias precision from the S05/C04 machinery) plus imports; the compiler-free IR engine carries **import-based** uses only (exact module or subpath match, never substring; textual `import type`/`export type` erasure — ANY braced named-binding list stays a value import there; template-literal bodies are skipped entirely (specifiers inside `${…}` are the symbol path's job); `require(…)` counts as capability evidence but never creates a graph edge on the pure path). **U04 walls are opt-in:** per-layer `capabilities: { deny: [...] }` or the dual-depth sugar `pure: true` (denies all seven); absence changes no verdict. `CAPABILITY_VIOLATION` is judgment-class (never mechanical-safe) with a port-injection `nextAction`; D7 dedup — evidence already owned by the layer's `forbiddenGlobals` reports only `FORBIDDEN_GLOBAL`. Y08 adds one deliberately narrow import dual: `forbiddenGlobals: ["process"]` owns exact value imports of `process` and `node:process`, but not subpaths or `child_process`; statement-level `import type` / `export type` remains erased on every path (the existing pure-IR all-named limitation above is unchanged). Atomic preflight blocks denied capabilities and that exact dual across a complete multi-file candidate (import-based on the pure path; other ambient evidence adds on the symbol-aware CLI/hook path). T01 policy-delta classifies the surface on **coverage atoms** (`ambient:<entry>` prefix-expanded, narrow `import-exact:<specifier>` duals, and `import:<capability>` for a complete wall): any lost atom is weakening (`fetch`→`XMLHttpRequest`, `Date`→`Date.now`, wall→fg all weaken; finding path `$.layers[name].capabilities`); fg → equivalent-or-stronger wall never needs an acknowledgment; unlowerable custom globals keep raw key comparison. |
|
|
28
28
|
| **Ambient state (U05)** | `ark-check --doctor --json` → `doctor.ambientState`; optional `.ark/ambient-state-acks.json` | Additive, **advisory only and opt-in**: only layers declared `pure: true` are scanned; the MVP shape is module-scope `let`/`var`. Findings carry `file`/`line`/`name`/`kind` (sorted, capped with honest `truncated` count). Acknowledgments live in the bounded sidecar (`{ acks: [{ file, name, reason }] }`, ≤64 KB / ≤200 entries); `acknowledged` counts applied acks; malformed file suppresses nothing. When TypeScript is unavailable the sensor reports `available: false` instead of guessing. **No strict mode exists** — A5: strictness requires a completed corpus and an explicit later decision. |
|
|
29
|
-
| **
|
|
29
|
+
| **Parse health (Y03)** | `ark-check --doctor --json` → `doctor.parseHealth`; report section `data-advisory="parseHealth"` | Additive, **advisory only**. The existing architecture AST contributes only `parseDiagnosticCount` per governed file (cache schema v9, bound to the TypeScript parser version; no raw diagnostics, second parse, or `tsc`). JSON reports `scannedFiles`, `affectedFiles`, `diagnosticCount`, deterministic top-12 `{ file, diagnosticCount }` entries, and honest `truncated`/`overflow`; missing/unsafe counts or unavailable scan evidence produce `available: false`, never a clean claim or a non-JSON-safe number. Parse health never changes the verdict, `ok`, violations, `designFitness`, or `patternBets`. |
|
|
30
|
+
| **Physical cohesion + reshape pilot (X04/Y01)** | `ark-check --doctor --json` → `doctor.physicalCohesion` (`reshapePilot`, `reshapeDecisions`); optional `.ark/reshape-decisions.json`; report section `data-advisory="physicalCohesion"` | Additive, **advisory only** — `notAScore`; never feeds the verdict, `designFitness`, or `patternBets`. Signal is **concentration, not volume**: concept clusters per anchor directory (deterministic path/name tokenization; framework filenames like `route.ts` take the topmost meaningful path segment — ADR 0010 D2). Fixed corpus-calibrated thresholds (`maxCluster ≥ 40` OR ≥2 anchors ≥ 20, ADR 0010 D3); findings ranked and capped (top 5, honest `truncated`). Anchors under `app/`/`pages/` are `fixedByConvention` and never move (D7). `reshapePilot` is **proposed, never applied** (`neverMechanicalSafe`): one Q04-style pilot card at a time targeting the smallest convention-free anchor, with `moveSample`/`movesTotal`, `successSignal`, `killSwitch`, `doNot[]`; real moves run only through the write gate + atomic preflight via `/ark-loop`; merges are `/ark-architect` judgment cards, never a codemod (D6). **Y01 verdict memory:** bounded sidecar `{ schemaVersion?: "1", decisions: [{ concept, anchors, verdict: "accepted"|"deferred"|"rejected", reason, reviewBy? }] }` (≤64 KiB / ≤200 unique targets). Identity is concept + complete sorted anchor set, never counts/change-map evidence. Current rejected/deferred records suppress pilot pressure only; accepted keeps the existing path. Expired/malformed/stale/invalid records suppress nothing; lifecycle and decisions render in doctor/report. Explicit only — golden-pattern prose never infers a verdict. |
|
|
30
31
|
| **Capability walls, every adapter (U04+U06)** | CLI scan, pure IR engine, atomic preflight, `ark-mcp --hook` / MCP gate (`capabilityWalls`), ESLint `ark/no-denied-capabilities` | The same opt-in deny set enforces across every surface: hook/MCP and CLI cover ambient + import evidence (symbol-aware); the pure engine, preflight, and ESLint cover the import dimension (documented envelope). Dual depth everywhere: plain port hint (`FIX_HINTS`/`suggestion`) + stable JSON (`ruleId`, `capability`, `fixClass: inject-port`, deterministic `nextAction`). |
|
|
31
32
|
| **Hook-path budgets (U06)** | `npm run bench:hook-path`; `eval/performance/hook-budgets.v1.json`; CI job "Hook-path end-to-end budgets" | Measures the COMPLETE pre-tool paths as fresh child processes (hook cold/warm, doctor cold) at 1k/10k. D5 method locked: ceilings are Linux-baseline p95 + fixed headroom, set once per cycle, never ratcheted; scenarios without a recorded baseline stay in RECORDING mode and cannot fail CI. |
|
|
32
33
|
| **Governance weight (W02)** | `ark-check --doctor --json` → `doctor.contractHealth.governanceWeight` | Additive, **advisory only** — raw facts (`declaredLayers`, `populatedLayers`, `governedFiles`, `rules`, `deniedEdges`, `allowedEdges`, `filesPerLayer`, `rulesPerLayer`) plus a fixed comparative band `weight: heavy | typical | light | unknown` and its fixed `note`. Fixed deterministic thresholds: **heavy** = fewer than 25 governed files per declared layer AND (6+ layers OR 4+ well-formed rules per layer); **light** = at most 2 layers over 150+ governed files; **unknown** = no layers or no governed files; everything else is **typical** (banding uses raw ratios; the reported ratios are rounded for display). `notAScore: true` is explicit: never a composite score, ranking, or gate input; the heavy note asks to justify NEW layers/rules and never suggests deleting working ones. Human doctor prints a line only for `heavy`/`light`. |
|
|
33
|
-
| **Report parity (X01)** | `ark-check --report` → advisory sections (`data-advisory="contractHealth\|ambientState"`, nested `governanceWeight`) + layer wall badges | The report is a rendering of doctor truth. **Standing rule:** every doctor advisory ships with its report section — enforced by the `reportParity` guard, which enumerates the doctor's advisory keys and fails on any missing section. |
|
|
34
|
+
| **Report parity (X01)** | `ark-check --report` → advisory sections (`data-advisory="contractHealth\|ambientState\|parseHealth"`, nested `governanceWeight`) + layer wall badges | The report is a rendering of doctor truth. **Standing rule:** every doctor advisory ships with its report section — enforced by the `reportParity` guard, which enumerates the doctor's advisory keys and fails on any missing section. |
|
|
34
35
|
| **MCP tools** | `arkgate-mcp` / `ark://…` resources | Tool names and primary argument shapes are stable within a major. |
|
|
35
36
|
| **`ark.config.json`** | Layer globs, rules, include/exclude, forbiddenGlobals, intent prefixes, `peerIsolation`, `dynamicImportAllowlist`, `safety` thresholds | Versioned by `schemaVersion`; unknown fields fail closed and migrations preserve the previous supported major. |
|
|
36
37
|
| **Policy transition analysis (T01, 3.1.0)** | `analyzePolicyDelta(...)`; MCP `ark_policy_delta`; CLI `--policy-base` / `--policy-base-ref` / `--policy-ack`; check JSON `policyDelta` | Additive schema `1.0`. Classifications and finding ids are deterministic. Weakening/judgment requires an acknowledgement bound to both policy hashes and the exact blocking finding set. |
|
|
@@ -100,5 +101,5 @@ production deployment would need to satisfy; it is not a readiness certification
|
|
|
100
101
|
## Release notes (maintainers)
|
|
101
102
|
|
|
102
103
|
Ship notes for a version live under [releases/](./releases/) (latest:
|
|
103
|
-
[3.
|
|
104
|
+
[3.7.0.md](./releases/3.7.0.md)).
|
|
104
105
|
Publish path: signed annotated tag → GitHub Release → `publish-npm.yml` (see [CONTRIBUTING.md](../CONTRIBUTING.md)).
|