@ornexus/neocortex 4.60.59 → 4.60.61
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/sbom.cdx.json +241 -82
- package/install.ps1 +1 -1
- package/install.sh +1 -1
- package/package.json +6 -2
- package/packages/client/dist/cli.js +37 -4
- package/packages/client/dist/commands/architecture-answers.d.ts +26 -0
- package/packages/client/dist/commands/architecture-answers.js +7 -0
- package/packages/client/dist/commands/architecture-memory-terminal.d.ts +21 -0
- package/packages/client/dist/commands/architecture-memory-terminal.js +2 -0
- package/packages/client/dist/commands/architecture-root-binding.d.ts +2 -0
- package/packages/client/dist/commands/architecture-root-binding.js +1 -0
- package/packages/client/dist/commands/invoke.d.ts +43 -2
- package/packages/client/dist/commands/invoke.js +98 -70
- package/packages/client/dist/commands/legacy-story-branch.d.ts +10 -0
- package/packages/client/dist/commands/legacy-story-branch.js +1 -0
- package/packages/client/dist/commands/legacy-story-pr.d.ts +18 -0
- package/packages/client/dist/commands/legacy-story-pr.js +1 -0
- package/packages/client/dist/commands/planning-diagnosis.d.ts +4 -0
- package/packages/client/dist/commands/planning-diagnosis.js +1 -0
- package/packages/client/dist/commands/planning-generation.d.ts +2 -0
- package/packages/client/dist/commands/planning-generation.js +1 -0
- package/packages/client/dist/commands/planning-research.d.ts +3 -0
- package/packages/client/dist/commands/planning-research.js +1 -0
- package/packages/client/dist/commands/refresh-memory.d.ts +10 -0
- package/packages/client/dist/commands/refresh-memory.js +3 -1
- package/packages/client/dist/commands/story-checkpoint.d.ts +1 -1
- package/packages/client/dist/commands/story-checkpoint.js +1 -1
- package/packages/client/dist/config/profile-directory.d.ts +2 -0
- package/packages/client/dist/config/profile-directory.js +1 -0
- package/packages/client/dist/config/secure-config.js +5 -5
- package/packages/client/dist/continuity/runner-bootstrap-policy.d.ts +1 -1
- package/packages/client/dist/continuity/runner-bootstrap-policy.js +1 -1
- package/packages/client/dist/generated/command-registry.generated.d.ts +11 -11
- package/packages/client/dist/generated/command-registry.generated.js +1 -1
- package/packages/client/dist/generated/public-projections.generated.d.ts +20 -20
- package/packages/client/dist/generated/public-projections.generated.js +1 -1
- package/packages/client/dist/git/direct-yolo-checkpoint.d.ts +9 -0
- package/packages/client/dist/git/direct-yolo-checkpoint.js +4 -4
- package/packages/client/dist/git/git-lifecycle-step-ids.d.ts +5 -0
- package/packages/client/dist/git/git-lifecycle-step-ids.js +1 -1
- package/packages/client/dist/git/git-lifecycle-store.d.ts +16 -0
- package/packages/client/dist/git/git-lifecycle-store.js +9 -4
- package/packages/client/dist/git/legacy-story-branch-checkpoint.d.ts +38 -0
- package/packages/client/dist/git/legacy-story-branch-checkpoint.js +9 -0
- package/packages/client/dist/git/legacy-story-branch-setup.d.ts +56 -0
- package/packages/client/dist/git/legacy-story-branch-setup.js +4 -0
- package/packages/client/dist/git/legacy-story-pr.d.ts +514 -0
- package/packages/client/dist/git/legacy-story-pr.js +62 -0
- package/packages/client/dist/git/secure-filesystem-boundary.d.ts +2 -0
- package/packages/client/dist/git/secure-filesystem-boundary.js +34 -7
- package/packages/client/dist/machine/fingerprint.js +2 -2
- package/packages/client/dist/memory/project-memory-writer.js +17 -17
- package/packages/client/dist/memory/shared-project-memory-types.d.ts +23 -13
- package/packages/client/dist/memory/shared-project-memory-types.js +2 -2
- package/packages/client/dist/migrations/state-json.d.ts +23 -1
- package/packages/client/dist/migrations/state-json.js +4 -4
- package/packages/client/dist/private-execution/delivery.d.ts +131 -3
- package/packages/client/dist/private-execution/delivery.js +2 -2
- package/packages/client/dist/private-execution/isolated-admission.d.ts +28 -1
- package/packages/client/dist/private-execution/isolated-admission.js +11 -2
- package/packages/client/dist/private-execution/isolated-session.js +2 -2
- package/packages/client/dist/private-execution/isolated-workspace.d.ts +8 -0
- package/packages/client/dist/private-execution/isolated-workspace.js +1 -1
- package/packages/client/dist/private-execution/pact-consumer-runtime.d.ts +19 -0
- package/packages/client/dist/private-execution/pact-consumer-runtime.js +3 -0
- package/packages/client/dist/private-execution/pact-test-source.d.ts +9 -0
- package/packages/client/dist/private-execution/pact-test-source.js +1 -0
- package/packages/client/dist/private-execution/pact-toolchain.d.ts +37 -0
- package/packages/client/dist/private-execution/pact-toolchain.js +3 -0
- package/packages/client/dist/private-execution/pact-write-set.d.ts +24 -0
- package/packages/client/dist/private-execution/pact-write-set.js +1 -0
- package/packages/client/dist/private-execution/planning-diagnosis-checkpoint.d.ts +38 -0
- package/packages/client/dist/private-execution/planning-diagnosis-checkpoint.js +5 -0
- package/packages/client/dist/private-execution/planning-generation-checkpoint.d.ts +34 -0
- package/packages/client/dist/private-execution/planning-generation-checkpoint.js +4 -0
- package/packages/client/dist/private-execution/planning-research-checkpoint.d.ts +38 -0
- package/packages/client/dist/private-execution/planning-research-checkpoint.js +4 -0
- package/packages/client/dist/private-execution/ux-source-validation.d.ts +2 -0
- package/packages/client/dist/private-execution/ux-source-validation.js +1 -0
- package/packages/client/dist/private-execution/ux-write-set.d.ts +30 -0
- package/packages/client/dist/private-execution/ux-write-set.js +2 -0
- package/packages/client/dist/resolvers/remote-resolver.d.ts +5 -22
- package/packages/client/dist/resolvers/remote-resolver.js +1 -1
- package/packages/client/dist/runner/project-identity.js +1 -1
- package/packages/client/dist/runner/scheduler.d.ts +20 -0
- package/packages/client/dist/runner/scheduler.js +7 -7
- package/packages/client/dist/state/project-state-snapshot.js +1 -1
- package/packages/client/dist/yolo/change-evidence-collector.d.ts +8 -0
- package/packages/client/dist/yolo/change-evidence-collector.js +1 -1
- package/packages/client/dist/yolo/spec-envelope-collector.d.ts +2 -0
- package/packages/client/dist/yolo/spec-envelope-collector.js +3 -3
- package/packages/client/dist/yoloop/invoke-hooks.js +7 -7
- package/packages/shared/dist/generated/command-registry.generated.js +12 -10
- package/packages/shared/dist/generated/invocation-schema.generated.js +14 -6
- package/packages/shared/dist/generated/public-projections.generated.js +18 -18
- package/packages/shared/dist/invocation/index.d.ts +1 -1
- package/packages/shared/dist/merge-readiness/c12-remote-observation.js +80 -0
- package/packages/shared/dist/planning/diagnosis-state.js +143 -0
- package/packages/shared/dist/yoloop/branch-unit.js +23 -1
- package/targets-stubs/antigravity/gemini.md +1 -1
- package/targets-stubs/antigravity/skill/SKILL.md +1 -1
- package/targets-stubs/claude-code/neocortex-root.agent.yaml +1 -1
- package/targets-stubs/claude-code/neocortex-root.md +2 -2
- package/targets-stubs/claude-code/neocortex.agent.yaml +1 -1
- package/targets-stubs/claude-code/neocortex.md +2 -2
- package/targets-stubs/codex/AGENTS.md +1 -1
- package/targets-stubs/cursor/agent.md +2 -2
- package/targets-stubs/gemini-cli/agent.md +2 -2
- package/targets-stubs/opencode/neocortex-root.md +1 -1
- package/targets-stubs/vscode/neocortex.agent.md +2 -2
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { PactInteractionV1 } from './pact-write-set.js';
|
|
2
|
+
export interface PactSourceInspectionV1 {
|
|
3
|
+
readonly schemaVersion: 1;
|
|
4
|
+
readonly consumerBinding: 'app-import-candidate' | 'mock-only-scaffold' | 'provider-template';
|
|
5
|
+
readonly executionVerified: false;
|
|
6
|
+
readonly publicSafe: true;
|
|
7
|
+
}
|
|
8
|
+
/** Reject comment-only, malformed and non-Pact files before a root patch effect. */
|
|
9
|
+
export declare function validatePactTestSource(path: string, source: string, brokerUrl: string | null, expectedInteraction?: PactInteractionV1, projectRoot?: string): PactSourceInspectionV1;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{parse as T}from"@babel/parser";import{lstatSync as S,realpathSync as C}from"node:fs";import{join as P,posix as j,resolve as k}from"node:path";const I=256*1024,V=1e5;function c(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function _(e){const n=new Set;if(!Array.isArray(e.specifiers))return n;for(const s of e.specifiers)!c(s)||s.type!=="ImportSpecifier"||!c(s.imported)||typeof s.imported.name!="string"||n.add(s.imported.name);return n}function O(e,n,s){const t=c(e.source)?e.source.value:void 0;if(typeof t!="string"||t.length>240||!/^\.\.?\/[A-Za-z0-9_./-]+$/.test(t)||e.importKind==="type"||!Array.isArray(e.specifiers)||e.specifiers.length<1||e.specifiers.length>16)throw new Error("private-execution-pact-source-invalid");const r=j.normalize(j.join(j.dirname(n),t));if(!/^(?:src|app|lib|packages|clients)\/[A-Za-z0-9_./-]+$/.test(r)||r.split("/").some(o=>o===".."||o==="."))throw new Error("private-execution-pact-source-invalid");const a=k(s);if(C(a)!==a||!S(a).isDirectory())throw new Error("private-execution-pact-source-invalid");const d=/\.(?:ts|tsx|js|mjs)$/.test(r)?[r,...r.endsWith(".js")?[`${r.slice(0,-3)}.ts`,`${r.slice(0,-3)}.tsx`]:[]]:[`${r}.ts`,`${r}.tsx`,`${r}.js`,`${r}.mjs`,`${r}/index.ts`,`${r}/index.tsx`,`${r}/index.js`],l=[];for(const o of d){let h=a;try{const v=o.split("/");for(const[w,E]of v.entries()){h=P(h,E);const m=S(h);if(m.isSymbolicLink()||(w===v.length-1?!m.isFile()||m.size<1||m.size>1024*1024:!m.isDirectory()))throw new Error("private-execution-pact-source-invalid")}l.push(o)}catch(v){if(v.code!=="ENOENT")throw new Error("private-execution-pact-source-invalid")}}if(l.length!==1)throw new Error("private-execution-pact-source-invalid");const y=new Set;for(const o of e.specifiers){if(!c(o)||!["ImportSpecifier","ImportDefaultSpecifier"].includes(o.type)||o.importKind==="type"||!c(o.local)||o.local.type!=="Identifier"||typeof o.local.name!="string")throw new Error("private-execution-pact-source-invalid");y.add(o.local.name)}return y}function b(e){if(!(!c(e)||e.type!=="MemberExpression"||e.computed===!0||!c(e.property)||e.property.type!=="Identifier"))return typeof e.property.name=="string"?e.property.name:void 0}function q(e){if(!c(e)||e.type!=="ObjectExpression"||!Array.isArray(e.properties))return;let n,s;for(const t of e.properties){if(!c(t)||t.type!=="ObjectProperty"||t.computed===!0||!c(t.key)||!c(t.value))return;const r=t.key.type==="Identifier"?t.key.name:t.key.type==="StringLiteral"?t.key.value:void 0;if(r==="method"||r==="path"){if(t.value.type!=="StringLiteral"||typeof t.value.value!="string")return;if(r==="method"){if(n!==void 0)return;n=t.value.value}else{if(s!==void 0)return;s=t.value.value}}}return n!==void 0&&s!==void 0?{method:n,path:s}:void 0}function z(e,n){if(e.length>180||n.length>2048||/[?#]/.test(n))return!1;const s=e.split("/"),t=n.split("/");return s.length!==t.length?!1:s.every((r,a)=>{const d=t[a];if(!r.includes("{")&&!r.includes("}"))return r===d;const l=/^([^{}]*)\{[^{}]+\}([^{}]*)$/.exec(r);return!l||!d.startsWith(l[1])||!d.endsWith(l[2])?!1:d.length>l[1].length+l[2].length})}function D(e,n,s,t,r){const a=e==="tests/pact/provider/verify.test.ts"?"provider":"consumer";if(a==="consumer"&&!/^tests\/pact\/consumer\/[a-z0-9-]+\.test\.ts$/.test(e)||typeof n!="string"||Buffer.byteLength(n,"utf8")<80||Buffer.byteLength(n,"utf8")>I||s!==null&&n.includes(s))throw new Error("private-execution-pact-source-invalid");let d;try{d=T(n,{sourceType:"module",plugins:["typescript"]})}catch{throw new Error("private-execution-pact-source-invalid")}const l=new Set,y=new Set,o=new Set;for(const u of d.program.body){if(u.type!=="ImportDeclaration")continue;const i=u.source.value;if(i!=="@pact-foundation/pact"&&i!=="vitest"){if(a!=="consumer"||!r)throw new Error("private-execution-pact-source-invalid");for(const f of O(u,e,r)){if(o.has(f)||["fetch","describe","it","expect","PactV3","MatchersV3","Verifier"].includes(f))throw new Error("private-execution-pact-source-invalid");o.add(f)}continue}const p=_(u);for(const f of p)(i==="@pact-foundation/pact"?l:y).add(f)}if(!y.has("describe")||!y.has("it")||a==="consumer"&&(!l.has("PactV3")||!y.has("expect"))||a==="provider"&&!l.has("Verifier")||[...o].some(u=>l.has(u)||y.has(u)))throw new Error("private-execution-pact-source-invalid");const h=new Set,v=new Set;let w=!1,E=!1,m=!1;const g=[{node:d.program,inExecuteTest:!1}];let A=0;for(;g.length>0;){const u=g.pop(),i=u.node;if(c(i)){if(++A>V)throw new Error("private-execution-pact-source-invalid");if(i.type==="NewExpression"&&c(i.callee)&&i.callee.type==="Identifier"&&typeof i.callee.name=="string"&&(v.add(i.callee.name),u.inExecuteTest&&o.has(i.callee.name)&&(m=!0)),i.type==="CallExpression"){const p=i.callee;c(p)&&p.type==="Identifier"&&typeof p.name=="string"&&(h.add(p.name),u.inExecuteTest&&o.has(p.name)&&(m=!0));const f=b(p);if(f&&h.add(f),f==="withRequest"&&t&&Array.isArray(i.arguments)){const x=q(i.arguments[0]);E||=x?.method.toUpperCase()===t.method&&z(t.endpointPath,x?.path??"")}}i.type==="MemberExpression"&&b(i)==="PACT_BROKER_URL"&&c(i.object)&&b(i.object)==="env"&&c(i.object.object)&&i.object.object.type==="Identifier"&&i.object.object.name==="process"&&(w=!0);for(const[p,f]of Object.entries(i)){if(p==="loc"||p==="extra"||p==="comments"||p==="leadingComments"||p==="trailingComments"||p==="innerComments")continue;const x=u.inExecuteTest||i.type==="CallExpression"&&b(i.callee)==="executeTest"&&p==="arguments";if(Array.isArray(f))for(const $ of f)g.push({node:$,inExecuteTest:x});else c(f)&&g.push({node:f,inExecuteTest:x})}}}if((a==="consumer"?["describe","it","withRequest","willRespondWith","executeTest","expect"]:["describe","it","verifyProvider"]).some(u=>!h.has(u))||!v.has(a==="consumer"?"PactV3":"Verifier")||a==="consumer"&&t!==void 0&&(t.filePath!==e||!E)||a==="provider"&&s!==null&&!w)throw new Error("private-execution-pact-source-invalid");return Object.freeze({schemaVersion:1,consumerBinding:a==="provider"?"provider-template":m?"app-import-candidate":"mock-only-scaffold",executionVerified:!1,publicSafe:!0})}export{D as validatePactTestSource};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export type PactToolchainReasonCode = 'manifest-missing' | 'manifest-invalid' | 'lockfile-missing' | 'lockfile-invalid' | 'package-manager-unsupported' | 'scripts-missing' | 'dependencies-missing' | 'dependency-spec-unsupported' | 'lockfile-drift' | 'manifest-aligned-unverified';
|
|
2
|
+
export interface PactToolchainInspectionV1 {
|
|
3
|
+
readonly schemaVersion: 1;
|
|
4
|
+
/** Manifest and root lock entries only; neither install nor test run is proven. */
|
|
5
|
+
readonly outcome: 'manifest-aligned' | 'requires-root-integration' | 'unsupported';
|
|
6
|
+
readonly reasonCode: PactToolchainReasonCode;
|
|
7
|
+
readonly executionVerified: false;
|
|
8
|
+
readonly publicSafe: true;
|
|
9
|
+
}
|
|
10
|
+
/** No project mutation, dependency installation, script execution, or private values. */
|
|
11
|
+
export declare function inspectPactToolchain(projectRoot: string, generateProvider: boolean): PactToolchainInspectionV1;
|
|
12
|
+
/** Local-only bytes; never serialize this candidate into a public invoke result. */
|
|
13
|
+
export interface PactToolchainCandidateV1 {
|
|
14
|
+
readonly schemaVersion: 1;
|
|
15
|
+
readonly outcome: 'candidate-unverified';
|
|
16
|
+
readonly sourceManifestSha256: string;
|
|
17
|
+
readonly sourceLockSha256: string | null;
|
|
18
|
+
readonly manifestText: string;
|
|
19
|
+
readonly lockText: string;
|
|
20
|
+
readonly executionVerified: false;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Root-owned manifest proposal. It preserves unrelated keys and refuses
|
|
24
|
+
* conflicting scripts, non-registry dependencies and unsupported workspaces.
|
|
25
|
+
* No filesystem effect occurs here.
|
|
26
|
+
*/
|
|
27
|
+
export declare function planPactToolchainManifest(projectRoot: string, generateProvider: boolean): {
|
|
28
|
+
readonly sourceText: string;
|
|
29
|
+
readonly candidateText: string;
|
|
30
|
+
readonly sourceLockText: string | null;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Resolve a lockfile from metadata alone in a private temporary directory.
|
|
34
|
+
* npm never sees project source, local credentials, .npmrc or generated tests;
|
|
35
|
+
* ignore-scripts and package-lock-only prevent package code execution.
|
|
36
|
+
*/
|
|
37
|
+
export declare function preparePactToolchainCandidate(projectRoot: string, generateProvider: boolean): PactToolchainCandidateV1;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{spawnSync as M}from"node:child_process";import{createHash as R}from"node:crypto";import{constants as j}from"node:fs";import{closeSync as V,fstatSync as P,lstatSync as O,mkdtempSync as I,openSync as $,readFileSync as z,realpathSync as D,rmSync as A,writeFileSync as w}from"node:fs";import{tmpdir as C}from"node:os";import{join as d,resolve as S}from"node:path";import{parseDocument as W}from"yaml";function p(o,r){return Object.freeze({schemaVersion:1,outcome:o,reasonCode:r,executionVerified:!1,publicSafe:!0})}function i(o){return!!o&&typeof o=="object"&&!Array.isArray(o)&&Object.getPrototypeOf(o)===Object.prototype}function m(o,r,e){const t=d(o,r);let n;try{n=$(t,j.O_RDONLY|j.O_NOFOLLOW)}catch(s){if(s.code==="ENOENT")return;throw new Error("pact-toolchain-path-unsafe")}try{const s=P(n);if(!s.isFile()||s.size<1||s.size>e)throw new Error("pact-toolchain-path-unsafe");return new TextDecoder("utf-8",{fatal:!0}).decode(z(n))}finally{V(n)}}function v(o){try{const r=W(o,{strict:!0,uniqueKeys:!0,schema:"core",customTags:[]});if(r.errors.length||r.warnings.length)return;const e=JSON.parse(o);return i(e)?e:void 0}catch{return}}function q(o,r){if(typeof r!="boolean")return p("unsupported","manifest-invalid");const e=S(o);try{if(D(e)!==e||!O(e).isDirectory())return p("unsupported","manifest-invalid");for(const T of["pnpm-lock.yaml","yarn.lock","bun.lock","bun.lockb"])if(m(e,T,8*1024*1024)!==void 0)return p("unsupported","package-manager-unsupported");const t=m(e,"package.json",1024*1024);if(t===void 0)return p("requires-root-integration","manifest-missing");const n=v(t);if(!n)return p("unsupported","manifest-invalid");if(n.packageManager!==void 0&&(typeof n.packageManager!="string"||!/^npm@[0-9]+(?:\.[0-9]+){0,2}$/.test(n.packageManager)))return p("unsupported","package-manager-unsupported");const s=n.scripts;if(!i(s)||typeof s["pact:consumer"]!="string"||!/^vitest run(?: --maxWorkers=1)? tests\/pact\/consumer\/$/.test(s["pact:consumer"])||r&&(typeof s["pact:provider"]!="string"||!/^vitest run(?: --maxWorkers=1)? tests\/pact\/provider\/$/.test(s["pact:provider"])))return p("requires-root-integration","scripts-missing");const c=n.devDependencies;if(!i(c)||typeof c["@pact-foundation/pact"]!="string"||typeof c.vitest!="string")return p("requires-root-integration","dependencies-missing");const u=/^(?:\^|~)?[0-9]+\.[0-9]+\.[0-9]+(?:-[a-zA-Z0-9.-]+)?$/;if(!u.test(c["@pact-foundation/pact"])||!u.test(c.vitest))return p("unsupported","dependency-spec-unsupported");const f=m(e,"package-lock.json",8*1024*1024);if(f===void 0)return p("requires-root-integration","lockfile-missing");const a=v(f);if(!a||!i(a.packages)||!i(a.packages[""])||![2,3].includes(a.lockfileVersion))return p("unsupported","lockfile-invalid");const l=a.packages[""].devDependencies,k=a.packages["node_modules/@pact-foundation/pact"],h=a.packages["node_modules/vitest"];return!i(l)||l["@pact-foundation/pact"]!==c["@pact-foundation/pact"]||l.vitest!==c.vitest||!i(k)||typeof k.version!="string"||!u.test(k.version)||!i(h)||typeof h.version!="string"||!u.test(h.version)?p("requires-root-integration","lockfile-drift"):p("manifest-aligned","manifest-aligned-unverified")}catch{return p("unsupported","manifest-invalid")}}const F="17.1.4",H="3.2.4",_="vitest run --maxWorkers=1 tests/pact/consumer/",N="vitest run --maxWorkers=1 tests/pact/provider/",E=/^(?:\^|~)?[0-9]+\.[0-9]+\.[0-9]+$/;function L(o){const r=v(o);if(!r||![2,3].includes(r.lockfileVersion)||!i(r.packages)||Object.keys(r.packages).length>1e4)return!1;for(const e of Object.values(r.packages)){if(!i(e)||e.link===!0)return!1;if(e.resolved!==void 0){if(typeof e.resolved!="string"||typeof e.integrity!="string"||!/^sha512-[A-Za-z0-9+/]+={0,2}$/.test(e.integrity))return!1;try{const t=new URL(e.resolved);if(t.protocol!=="https:"||t.hostname!=="registry.npmjs.org"||t.port||t.username||t.password||t.search||t.hash||!t.pathname.startsWith("/")||t.pathname.includes(".."))return!1}catch{return!1}}}return!0}function U(o,r){try{if(process.platform!=="linux"||typeof r!="boolean")throw new Error("unsupported");const e=S(o);if(D(e)!==e||!O(e).isDirectory())throw new Error("root");for(const y of[".npmrc","npm-shrinkwrap.json","pnpm-lock.yaml","yarn.lock","bun.lock","bun.lockb"])try{throw O(d(e,y)),new Error("alternate")}catch(g){if(g.code!=="ENOENT")throw g}const t=m(e,"package.json",1024*1024);if(!t)throw new Error("manifest");const n=v(t);if(!n||typeof n.name!="string"||!/^[a-z0-9@][a-z0-9@._/-]{0,213}$/.test(n.name)||typeof n.version!="string"||!E.test(n.version)||n.workspaces!==void 0||n.overrides!==void 0||n.packageManager!==void 0&&(typeof n.packageManager!="string"||!/^npm@[0-9]+(?:\.[0-9]+){0,2}$/.test(n.packageManager)))throw new Error("manifest");for(const y of["dependencies","devDependencies","optionalDependencies","peerDependencies"]){const g=n[y];if(g!==void 0&&(!i(g)||Object.values(g).some(b=>typeof b!="string"||!E.test(b))))throw new Error("dependency")}const s=n.dependencies;if(i(s)&&(s["@pact-foundation/pact"]!==void 0||s.vitest!==void 0))throw new Error("placement");const c=n.scripts;if(c!==void 0&&!i(c))throw new Error("scripts");const u=i(c)?c:{};for(const y of["prepact:consumer","postpact:consumer","prepact:provider","postpact:provider"])if(u[y]!==void 0)throw new Error("lifecycle");if(u["pact:consumer"]!==void 0&&u["pact:consumer"]!==_||r&&u["pact:provider"]!==void 0&&u["pact:provider"]!==N)throw new Error("script-conflict");const f=i(n.devDependencies)?n.devDependencies:{},a=f["@pact-foundation/pact"]??F,l=f.vitest??H;if(typeof a!="string"||!E.test(a)||typeof l!="string"||!E.test(l))throw new Error("version");const k={...n,scripts:{...u,"pact:consumer":_,...r?{"pact:provider":N}:{}},devDependencies:{...f,"@pact-foundation/pact":a,vitest:l}},h=/^\{\r?\n([ \t]+)\S/m.exec(t)?.[1]??" ",T=JSON.stringify(k,null,h)+(t.endsWith(`
|
|
2
|
+
`)?`
|
|
3
|
+
`:""),x=m(e,"package-lock.json",8*1024*1024)??null;if(x!==null&&!L(x))throw new Error("lock");return Object.freeze({sourceText:t,candidateText:T,sourceLockText:x})}catch{throw new Error("pact-toolchain-manifest-unsupported")}}function X(o,r){const e=U(o,r),t=I(d(C(),"p222-pact-toolchain-"));try{w(d(t,"package.json"),e.candidateText,{mode:384}),w(d(t,"user.npmrc"),"",{mode:384}),w(d(t,"global.npmrc"),"",{mode:384}),e.sourceLockText!==null&&w(d(t,"package-lock.json"),e.sourceLockText,{mode:384});const n=M("npm",["install","--package-lock-only","--ignore-scripts","--no-audit","--no-fund","--registry=https://registry.npmjs.org/",`--userconfig=${d(t,"user.npmrc")}`,`--globalconfig=${d(t,"global.npmrc")}`],{cwd:t,encoding:"utf8",timeout:12e4,maxBuffer:256*1024,env:{PATH:process.env.PATH,HOME:t,TMPDIR:t,npm_config_cache:d(t,".npm-cache"),CI:"true",NO_UPDATE_NOTIFIER:"1"},stdio:["ignore","pipe","pipe"]});if(n.error||n.status!==0)throw new Error("npm");const s=m(t,"package.json",1024*1024),c=m(t,"package-lock.json",8*1024*1024),u=q(t,r);if(s!==e.candidateText||!c||!L(c)||u.outcome!=="manifest-aligned")throw new Error("candidate");const f=S(o);if(m(f,"package.json",1024*1024)!==e.sourceText||(m(f,"package-lock.json",8*1024*1024)??null)!==e.sourceLockText)throw new Error("source-raced");const a=l=>R("sha256").update(l).digest("hex");return Object.freeze({schemaVersion:1,outcome:"candidate-unverified",sourceManifestSha256:a(e.sourceText),sourceLockSha256:e.sourceLockText===null?null:a(e.sourceLockText),manifestText:s,lockText:c,executionVerified:!1})}catch{throw new Error("pact-toolchain-candidate-unavailable")}finally{A(t,{recursive:!0,force:!0})}}export{q as inspectPactToolchain,U as planPactToolchainManifest,X as preparePactToolchainCandidate};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface PactWriteSetV1 {
|
|
2
|
+
readonly schemaVersion: 1;
|
|
3
|
+
readonly specSha256: string;
|
|
4
|
+
readonly pathsSha256: string;
|
|
5
|
+
readonly endpointCount: number;
|
|
6
|
+
readonly paths: readonly string[];
|
|
7
|
+
/** Local-only source contract; never project this content into public binding metadata. */
|
|
8
|
+
readonly interactions: readonly PactInteractionV1[];
|
|
9
|
+
readonly providerPath?: 'tests/pact/provider/verify.test.ts';
|
|
10
|
+
}
|
|
11
|
+
export interface PactInteractionV1 {
|
|
12
|
+
readonly filePath: string;
|
|
13
|
+
readonly method: string;
|
|
14
|
+
readonly endpointPath: string;
|
|
15
|
+
}
|
|
16
|
+
export type PactWriteSetProjection = {
|
|
17
|
+
readonly outcome: 'ready';
|
|
18
|
+
readonly writeSet: PactWriteSetV1;
|
|
19
|
+
} | {
|
|
20
|
+
readonly outcome: 'skipped';
|
|
21
|
+
readonly reasonCode: 'PACT_SKIPPED_NO_OPENAPI' | 'PACT_SKIPPED_INVALID_YAML' | 'PACT_SKIPPED_UNSUPPORTED_VERSION' | 'PACT_SKIPPED_EMPTY_PATHS';
|
|
22
|
+
};
|
|
23
|
+
/** No filesystem mutation and no protected content returned. */
|
|
24
|
+
export declare function derivePactWriteSet(projectRoot: string, generateProvider: boolean): PactWriteSetProjection;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createHash as g}from"node:crypto";import{lstatSync as C,readFileSync as D,realpathSync as I}from"node:fs";import{join as _,resolve as k}from"node:path";import{parseDocument as N}from"yaml";const j="docs/api/openapi.yaml",z=5*1024*1024,K=256,L=new Set(["get","post","put","patch","delete","head","options","trace"]);function e(r){return!!r&&typeof r=="object"&&!Array.isArray(r)&&Object.getPrototypeOf(r)===Object.prototype}function M(r,c){let t=r;for(const w of c.split("/")){t=_(t,w);let a;try{a=C(t)}catch(O){if(O.code==="ENOENT")return!1;throw new Error("pact-write-set-path-unsafe")}if(a.isSymbolicLink()||(t===_(r,c)?!a.isFile():!a.isDirectory()))throw new Error("pact-write-set-path-unsafe")}return!0}function $(r,c){const t=r.replace(/^\//,"").replace(/[{}]/g,"").replace(/[^a-zA-Z0-9]+/g,"-").replace(/^-+|-+$/g,"").toLowerCase();return`${c.toLowerCase()}-${t||"root"}`}function v(r,c){if(typeof c!="boolean")throw new Error("pact-write-set-options-invalid");const t=k(r);try{if(I(t)!==t||!C(t).isDirectory())throw new Error("pact-write-set-path-unsafe")}catch{throw new Error("pact-write-set-path-unsafe")}if(!M(t,j))return{outcome:"skipped",reasonCode:"PACT_SKIPPED_NO_OPENAPI"};const w=C(_(t,j));if(w.size<1||w.size>z)throw new Error("pact-write-set-spec-size-invalid");const a=D(_(t,j)),O=g("sha256").update(a).digest("hex");let d;try{const o=new TextDecoder("utf-8",{fatal:!0}).decode(a),s=N(o,{strict:!0,stringKeys:!0,uniqueKeys:!0,schema:"core",customTags:[]});if(s.errors.length>0||s.warnings.length>0)return{outcome:"skipped",reasonCode:"PACT_SKIPPED_INVALID_YAML"};d=s.toJS({maxAliasCount:0})}catch{return{outcome:"skipped",reasonCode:"PACT_SKIPPED_INVALID_YAML"}}if(!e(d)||d.openapi!=="3.1.0")return{outcome:"skipped",reasonCode:"PACT_SKIPPED_UNSUPPORTED_VERSION"};if(!e(d.paths)||Object.keys(d.paths).length===0)return{outcome:"skipped",reasonCode:"PACT_SKIPPED_EMPTY_PATHS"};const l=new Set,A=[];let P=0;for(const[o,s]of Object.entries(d.paths)){if(!o.startsWith("/")||o.length>180||/[\u0000-\u001f\u007f?#]/.test(o)||!e(s)||o.split("/").some(u=>(u.includes("{")||u.includes("}"))&&!/^[^{}]*\{[^{}]+\}[^{}]*$/.test(u)))throw new Error("pact-write-set-spec-shape-invalid");for(const[u,f]of Object.entries(s)){const p=u.toLowerCase();if(!L.has(p))continue;if(!e(f))throw new Error("pact-write-set-spec-shape-invalid");if(p==="trace"||(p==="get"||p==="head")&&f.requestBody!==void 0)throw new Error("pact-write-set-method-unsupported");if(f.requestBody!==void 0){const n=f.requestBody,h=e(n)?n.content:void 0,b=e(h)?h["application/json"]:void 0;if(!e(b)||!e(b.schema))throw new Error("pact-write-set-operation-unsupported")}const m=f.responses,y=e(m)?["200","201",...Object.keys(m).filter(n=>/^[1-5][0-9]{2}$/.test(n)).sort()].find(n=>e(m[n])):void 0;if(!y)throw new Error("pact-write-set-operation-unsupported");const i=m[y];if((y==="204"||p==="head")&&e(i)&&(i.schema!==void 0||e(i.content)&&Object.keys(i.content).length>0))throw new Error("pact-write-set-operation-unsupported");if(y!=="204"&&p!=="head"&&e(i)){const n=i.content;if(n!==void 0){if(!e(n))throw new Error("pact-write-set-operation-unsupported");if(Object.keys(n).length>0){const h=n["application/json"];if(!e(h)||!e(h.schema))throw new Error("pact-write-set-operation-unsupported")}}if(i.schema!==void 0&&!e(i.schema))throw new Error("pact-write-set-operation-unsupported")}if(P+=1,P>K)throw new Error("pact-write-set-endpoint-limit");const S=`tests/pact/consumer/${$(o,u)}.test.ts`;if(S.length>240||l.has(S))throw new Error("pact-write-set-path-collision");l.add(S),A.push(Object.freeze({filePath:S,method:p.toUpperCase(),endpointPath:o}))}}if(P===0)return{outcome:"skipped",reasonCode:"PACT_SKIPPED_EMPTY_PATHS"};const E=c?"tests/pact/provider/verify.test.ts":void 0;E&&l.add(E);const T=Object.freeze([...l].sort());return A.sort((o,s)=>o.filePath<s.filePath?-1:o.filePath>s.filePath?1:0),{outcome:"ready",writeSet:Object.freeze({schemaVersion:1,specSha256:O,pathsSha256:g("sha256").update(JSON.stringify(T)).digest("hex"),endpointCount:P,paths:T,interactions:Object.freeze(A),...E?{providerPath:E}:{}})}}export{v as derivePactWriteSet};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
declare const ARTIFACT_NAMES: readonly ["idea", "diagnosis", "enrichment", "index"];
|
|
2
|
+
export interface PlanningDiagnosisRootReviewV1 {
|
|
3
|
+
readonly schemaVersion: 'PlanningDiagnosisRootReview/1';
|
|
4
|
+
readonly verdict: 'accepted';
|
|
5
|
+
readonly claimId: string;
|
|
6
|
+
readonly epicId: string;
|
|
7
|
+
readonly logicalFingerprint: string;
|
|
8
|
+
readonly sourceHead: string;
|
|
9
|
+
readonly sourceBranch: string;
|
|
10
|
+
readonly sourceStateSha256: string;
|
|
11
|
+
readonly admission: {
|
|
12
|
+
readonly schemaVersion: 1;
|
|
13
|
+
readonly outcome: 'admitted-unverified';
|
|
14
|
+
readonly baseSha: string;
|
|
15
|
+
readonly executionBranch: string;
|
|
16
|
+
readonly changedPaths: readonly string[];
|
|
17
|
+
readonly patchSha256: string;
|
|
18
|
+
};
|
|
19
|
+
readonly artifactDigests: Readonly<Record<typeof ARTIFACT_NAMES[number], string>>;
|
|
20
|
+
/** Explicit, independent root review; this is not inferred from clone admission. */
|
|
21
|
+
readonly qaEvidenceId: string;
|
|
22
|
+
readonly intake: Readonly<Record<string, unknown>>;
|
|
23
|
+
readonly artifactSummary: string;
|
|
24
|
+
}
|
|
25
|
+
export declare function planningPhysicalPatchDigest(root: string, paths: Readonly<Record<string, string>>, ownTemporaryPath?: string, allowPendingSidecar?: boolean): string;
|
|
26
|
+
/** P02 may consume only a physically sealed P01 for the same durable claim. */
|
|
27
|
+
export declare function acceptedPlanningDiagnosisPredecessor(stateInput: unknown, claimId: string, epicId: string, allowResearched?: boolean): {
|
|
28
|
+
readonly intakeRevision: string;
|
|
29
|
+
readonly diagnosisReceiptId: string;
|
|
30
|
+
readonly rawIntent: string;
|
|
31
|
+
};
|
|
32
|
+
/** Root-only state transition after a separately accepted review of the exact admitted patch. */
|
|
33
|
+
export declare function sealPlanningDiagnosis(projectRoot: string, reviewInput: unknown): {
|
|
34
|
+
readonly epicId: string;
|
|
35
|
+
readonly diagnosisReceiptId: string;
|
|
36
|
+
readonly qaEvidenceId: string;
|
|
37
|
+
};
|
|
38
|
+
export {};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import{execFileSync as q,spawnSync as Q}from"node:child_process";import{createHash as Y,randomUUID as v}from"node:crypto";import{closeSync as b,fsyncSync as _,lstatSync as j,mkdirSync as ii,openSync as D,readFileSync as ni,realpathSync as J,renameSync as ei,unlinkSync as z,writeFileSync as L}from"node:fs";import{isAbsolute as ti,join as y,resolve as B}from"node:path";import{isDeepStrictEqual as x}from"node:util";import{canonicalPlanningIntake as H,createPlanningReceiptV1 as N,planningIntakeRevision as C,projectDiagnosedEpicState as M}from"../../../shared/dist/planning/diagnosis-state.js";const k=/^[a-f0-9]{64}$/,si=/^P[1-9][0-9]{0,11}$/,ci=/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/,A=["idea","diagnosis","enrichment","index"],T="acd455ed0e39d98b90fb57391db75e366e4c5604db043e03a89a20e33d643c3c",ai="81f50aaa801c1660eabe46e645501acbfb3e9873410c991e1496c014ca75c369";function o(i){return i!==null&&typeof i=="object"&&!Array.isArray(i)}function P(i){return Y("sha256").update(i).digest("hex")}function $(i,n){return q("git",[...n],{cwd:i,encoding:"utf8",stdio:["ignore","pipe","ignore"],maxBuffer:65536}).trim()}function I(i,n,e){const s=y(i,n),a=j(s);if(!a.isFile()||a.isSymbolicLink()||a.size<1||a.size>e)throw new Error("planning-diagnosis-physical-artifact-invalid");return ni(s)}function G(i,n){return typeof i=="string"&&i.length<=128&&P(i)===n}function U(i,n,e){const s=/^---\n([\s\S]{1,8192}?)\n---\n/.exec(i.replace(/\r\n/g,`
|
|
2
|
+
`))?.[1];if(!s)return!1;const a=s.split(`
|
|
3
|
+
`),r=d=>{const g=a.filter(c=>c.startsWith(`${d}:`));return g.length===1?g[0].slice(d.length+1).trim().replace(/^(?:"(.*)"|'(.*)')$/,"$1$2"):void 0};return r("epic_id")===n&&r("status")==="completed"&&G(r("step"),e)}function ri(i,n,e,s=!0){const a=new Set(Object.values(n)),r=new Set([".neocortex/state.json.planning.lock",...e?[e]:[]]),d=q("git",["status","--porcelain=v1","-z","--untracked-files=all"],{cwd:i,stdio:["ignore","pipe","ignore"],maxBuffer:128*1024}).toString("utf8").split("\0").filter(Boolean),g=[],c=new Set;for(const l of d){const h=l.slice(0,2),t=l.slice(3);if(!(r.has(t)&&h==="??")){if(l[2]!==" "||!a.has(t)&&(!s||t!==".neocortex/pending-epic-claims.json")||t===".neocortex/pending-epic-claims.json"&&h!=="??"||t!==".neocortex/pending-epic-claims.json"&&h!=="??"&&h!==" M"||c.has(t))throw new Error("planning-diagnosis-write-scope-conflict");c.add(t),h==="??"&&a.has(t)&&g.push(t)}}if([...a].some(l=>!c.has(l)))throw new Error("planning-diagnosis-write-scope-conflict");const p=[q("git",["diff","--binary","--no-ext-diff","--no-textconv","HEAD","--"],{cwd:i,stdio:["ignore","pipe","ignore"],maxBuffer:2*1024*1024})];for(const l of g.sort()){const h=Q("git",["diff","--no-index","--binary","--no-ext-diff","--","/dev/null",l],{cwd:i,encoding:"buffer",maxBuffer:2097152});if(h.status!==1||!Buffer.isBuffer(h.stdout))throw new Error("planning-diagnosis-patch-invalid");p.push(h.stdout)}return P(Buffer.concat(p.map(l=>Buffer.concat([l,Buffer.from(`
|
|
4
|
+
`)]))))}function oi(i){if(!o(i)||Object.keys(i).sort().join("|")!==["schemaVersion","verdict","claimId","epicId","logicalFingerprint","sourceHead","sourceBranch","sourceStateSha256","admission","artifactDigests","qaEvidenceId","intake","artifactSummary"].sort().join("|")||i.schemaVersion!=="PlanningDiagnosisRootReview/1"||i.verdict!=="accepted"||typeof i.claimId!="string"||!ci.test(i.claimId)||typeof i.epicId!="string"||!si.test(i.epicId)||typeof i.logicalFingerprint!="string"||!k.test(i.logicalFingerprint)||typeof i.sourceHead!="string"||!/^[a-f0-9]{40}$/.test(i.sourceHead)||typeof i.sourceBranch!="string"||!/^[a-zA-Z0-9][a-zA-Z0-9._/-]{0,127}$/.test(i.sourceBranch)||typeof i.sourceStateSha256!="string"||!k.test(i.sourceStateSha256)||!o(i.admission)||i.admission.schemaVersion!==1||i.admission.outcome!=="admitted-unverified"||i.admission.baseSha!==i.sourceHead||i.admission.executionBranch!==i.sourceBranch||typeof i.admission.patchSha256!="string"||!k.test(i.admission.patchSha256)||typeof i.qaEvidenceId!="string"||!/^qa-[a-z0-9-]{8,80}$/.test(i.qaEvidenceId)||!o(i.artifactDigests)||Object.keys(i.artifactDigests).sort().join("|")!==[...A].sort().join("|")||A.some(n=>typeof i.artifactDigests[n]!="string"||!k.test(i.artifactDigests[n])))throw new Error("planning-diagnosis-review-invalid");H(i.intake),N({kind:"diagnosis",epicId:i.epicId,intakeRevision:C(i.intake),artifactSummary:i.artifactSummary,accepted:!0})}function Z(i,n,e,s){if($(i,["rev-parse","HEAD"])!==n.sourceHead||$(i,["symbolic-ref","--quiet","--short","HEAD"])!==n.sourceBranch)throw new Error("planning-diagnosis-head-conflict");const a=j(y(i,".neocortex")),r=j(y(i,".neocortex/pending-epic-claims.json"));if(!a.isDirectory()||a.isSymbolicLink()||(a.mode&18)!==0||!r.isFile()||r.isSymbolicLink()||r.uid!==process.getuid?.()||(r.mode&63)!==0)throw new Error("planning-diagnosis-claim-boundary-invalid");const d=JSON.parse(I(i,".neocortex/pending-epic-claims.json",128*1024).toString("utf8")),g=o(d)&&o(d.operations)?d.operations[n.logicalFingerprint]:void 0;if(!o(g)||g.claimId!==n.claimId||g.epicId!==n.epicId)throw new Error("planning-diagnosis-claim-conflict");const c=`.neocortex/planning/epic-${n.epicId}`,f={idea:`${c}/idea.json`,diagnosis:`${c}/diagnosis.md`,enrichment:`${c}/enriched-prompt.md`,index:".neocortex/planning/planning.json"};if(!Array.isArray(n.admission.changedPaths)||[...n.admission.changedPaths].sort().join("|")!==Object.values(f).sort().join("|")||e&&ri(i,f,s)!==n.admission.patchSha256)throw new Error("planning-diagnosis-admission-patch-conflict");const p={idea:I(i,f.idea,32*1024),diagnosis:I(i,f.diagnosis,256*1024),enrichment:I(i,f.enrichment,256*1024),index:I(i,f.index,128*1024)};for(const w of A)if(P(p[w])!==n.artifactDigests[w])throw new Error("planning-diagnosis-artifact-conflict");const l=JSON.parse(p.idea.toString("utf8")),h=JSON.parse(p.index.toString("utf8")),t=H(n.intake),m=o(h)&&o(h.epics)?h.epics[n.epicId]:void 0,u=o(m)?m.output_files:void 0;if(!o(l)||l.epic_id!==n.epicId||l.status!=="diagnosed"||l.raw_input!==t.rawIntent||!o(m)||m.id!==n.epicId||m.idea!==t.rawIntent||m.status!=="diagnosed"||!U(p.diagnosis.toString("utf8"),n.epicId,T)||!U(p.enrichment.toString("utf8"),n.epicId,ai)||!Array.isArray(m.steps_completed)||m.steps_completed.length!==1||!G(m.steps_completed[0],T)||!o(u)||!x(u,{diagnosis:f.diagnosis})||typeof m.created_at!="string"||!Number.isFinite(Date.parse(m.created_at))||m.completed_at!==null)throw new Error("planning-diagnosis-artifact-identity-conflict");if(typeof t.goal!="string"||!t.goal.trim()||typeof t.desiredOutcome!="string"||!t.desiredOutcome.trim()||!Array.isArray(t.users)||t.users.length===0||!Array.isArray(t.surfaces)||t.surfaces.length===0)throw new Error("planning-diagnosis-critical-intake-missing");const S=C(t),E=N({kind:"diagnosis",epicId:n.epicId,intakeRevision:S,artifactSummary:n.artifactSummary,accepted:!0});return{checkpoint:{schemaVersion:1,epicId:n.epicId,intake:t,intakeRevision:S,diagnosisReceipt:E},receiptId:E.receiptId}}function W(i,n,e){const s=Z(i,n,!0,e),a=I(i,".neocortex/state.json",8*1024*1024);if(P(a)!==n.sourceStateSha256)throw new Error("planning-diagnosis-state-conflict");const r=JSON.parse(a.toString("utf8"));if(!o(r)||o(r.scope)&&r.scope.kind==="projected")throw new Error("planning-diagnosis-state-invalid");return M(r,{claimId:n.claimId,epicId:n.epicId,checkpoint:s.checkpoint}),{stateRaw:a,state:r,...s}}function X(i,n){const e={schemaVersion:"PlanningDiagnosisPhysicalAcceptance/1",claimId:i.claimId,epicId:i.epicId,diagnosisReceiptId:n,qaEvidenceId:i.qaEvidenceId,sourceHead:i.sourceHead,sourceStateSha256:i.sourceStateSha256,admissionPatchSha256:i.admission.patchSha256,artifactDigests:i.artifactDigests};return{...e,receiptId:P(JSON.stringify(e)),publicSafe:!0}}function ui(i,n,e,s=!1){const a=o(i)?i:void 0,d=(a&&o(a.epics)?a.epics:void 0)?.[e];if(!o(d)||d.id!==e||d.source!=="create-epic"||d.status!=="diagnosed"&&!(s&&d.status==="researched")||!o(d.createEpicPlanningClaim)||d.createEpicPlanningClaim.claimId!==n||d.createEpicPlanningClaim.epicId!==e||!o(d.createEpicPlanning)||!o(d.createEpicPlanningPhysical))throw new Error("planning-research-predecessor-invalid");const g=d.createEpicPlanning,c=d.createEpicPlanningPhysical,f=o(c.artifactDigests)?c.artifactDigests:void 0,p=H(g.intake),l=C(p),h=a&&o(a.epic_id_ledger)?a.epic_id_ledger:void 0,t=o(g.diagnosisReceipt)?g.diagnosisReceipt:void 0;if(g.schemaVersion!==1||g.epicId!==e||g.intakeRevision!==l||!s&&g.researchReceipt!==void 0||typeof p.goal!="string"||!p.goal.trim()||typeof p.desiredOutcome!="string"||!p.desiredOutcome.trim()||typeof p.rawIntent!="string"||!p.rawIntent.trim()||!Array.isArray(p.users)||p.users.length===0||!Array.isArray(p.surfaces)||p.surfaces.length===0||!Array.isArray(h?.reserved_ids)||!h.reserved_ids.includes(e)||!t||t.kind!=="diagnosis"||t.epicId!==e||t.intakeRevision!==l||t.accepted!==!0||typeof t.artifactSummary!="string"||!x(t,N({kind:"diagnosis",epicId:e,intakeRevision:l,artifactSummary:t.artifactSummary,accepted:!0}))||c.schemaVersion!=="PlanningDiagnosisPhysicalAcceptance/1"||c.claimId!==n||c.epicId!==e||c.diagnosisReceiptId!==t.receiptId||typeof c.qaEvidenceId!="string"||!/^qa-[a-z0-9-]{8,80}$/.test(c.qaEvidenceId)||typeof c.sourceHead!="string"||!/^[a-f0-9]{40}$/.test(c.sourceHead)||typeof c.sourceStateSha256!="string"||!k.test(c.sourceStateSha256)||typeof c.admissionPatchSha256!="string"||!k.test(c.admissionPatchSha256)||!f||Object.keys(f).sort().join("|")!==[...A].sort().join("|")||A.some(u=>typeof f[u]!="string"||!k.test(f[u])))throw new Error("planning-research-predecessor-invalid");const m={schemaVersion:c.schemaVersion,claimId:n,epicId:e,diagnosisReceiptId:t.receiptId,qaEvidenceId:c.qaEvidenceId,sourceHead:c.sourceHead,sourceStateSha256:c.sourceStateSha256,admissionPatchSha256:c.admissionPatchSha256,artifactDigests:f};if(!x(c,{...m,receiptId:P(JSON.stringify(m)),publicSafe:!0}))throw new Error("planning-research-predecessor-invalid");return{intakeRevision:l,diagnosisReceiptId:t.receiptId,rawIntent:p.rawIntent}}function di(i,n){const e=Z(i,n,!1),s=JSON.parse(I(i,".neocortex/state.json",8*1024*1024).toString("utf8")),r=(o(s)&&o(s.epics)?s.epics:void 0)?.[n.epicId],d=o(s)&&o(s.epic_id_ledger)?s.epic_id_ledger:void 0;if(!o(r)||!o(r.createEpicPlanning)||!o(r.createEpicPlanningClaim)||r.createEpicPlanningClaim.claimId!==n.claimId||r.createEpicPlanningClaim.epicId!==n.epicId||r.createEpicPlanning.epicId!==n.epicId||r.createEpicPlanning.intakeRevision!==e.checkpoint.intakeRevision||!x(r.createEpicPlanning.intake,e.checkpoint.intake)||!x(r.createEpicPlanning.diagnosisReceipt,e.checkpoint.diagnosisReceipt)||!x(r.createEpicPlanningPhysical,X(n,e.receiptId))||!Array.isArray(d?.reserved_ids)||!d.reserved_ids.includes(n.epicId))throw new Error("planning-diagnosis-replay-conflict");return e.receiptId}function yi(i,n){oi(n);const e=n,s=B(i);if(J(s)!==s||$(s,["rev-parse","--show-toplevel"])!==s)throw new Error("planning-diagnosis-root-invalid");const a=y(s,".neocortex"),r=y(a,"state.json.planning.lock"),d=D(r,"wx",384);let g;try{const c=I(s,".neocortex/state.json",8388608);if(P(c)!==e.sourceStateSha256){const K=di(s,e);return{epicId:e.epicId,diagnosisReceiptId:K,qaEvidenceId:e.qaEvidenceId}}const f=W(s,e),p=M(f.state,{claimId:e.claimId,epicId:e.epicId,checkpoint:f.checkpoint});if(p===f.state)throw new Error("planning-diagnosis-already-sealed");const l=X(e,f.receiptId),h=p.epics,t={...p,epics:{...h,[e.epicId]:{...h[e.epicId],createEpicPlanningPhysical:l}}},m=$(s,["rev-parse","--git-common-dir"]),u=ti(m)?B(m):B(s,m);if(J(u)!==u||j(u).isSymbolicLink())throw new Error("planning-diagnosis-backup-dir-invalid");const S=y(u,"neocortex-planning");ii(S,{recursive:!0,mode:448});const E=j(S);if(!E.isDirectory()||E.isSymbolicLink()||E.uid!==process.getuid?.()||(E.mode&63)!==0)throw new Error("planning-diagnosis-backup-dir-invalid");const R=v(),w=D(y(S,`state-backup-${R}.json`),"wx",384);try{L(w,f.stateRaw),_(w)}finally{b(w)}const F=D(S,"r");try{_(F)}finally{b(F)}g=y(a,`state.json.planning-tmp-${R}`);const O=D(g,"wx",384);try{L(O,`${JSON.stringify(t,null,2)}
|
|
5
|
+
`),_(O)}finally{b(O)}if(!W(s,e,`.neocortex/state.json.planning-tmp-${R}`).stateRaw.equals(f.stateRaw))throw new Error("planning-diagnosis-state-conflict");ei(g,y(a,"state.json")),g=void 0;const V=D(a,"r");try{_(V)}finally{b(V)}return{epicId:e.epicId,diagnosisReceiptId:f.receiptId,qaEvidenceId:e.qaEvidenceId}}finally{if(g)try{z(g)}catch{}b(d),z(r)}}export{ui as acceptedPlanningDiagnosisPredecessor,ri as planningPhysicalPatchDigest,yi as sealPlanningDiagnosis};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export interface PlanningGenerationRootReviewV1 {
|
|
2
|
+
readonly schemaVersion: 'PlanningGenerationRootReview/1';
|
|
3
|
+
readonly verdict: 'accepted';
|
|
4
|
+
readonly claimId: string;
|
|
5
|
+
readonly epicId: string;
|
|
6
|
+
readonly diagnosisReceiptId: string;
|
|
7
|
+
readonly researchReceiptId: string;
|
|
8
|
+
readonly intakeRevision: string;
|
|
9
|
+
readonly sourceHead: string;
|
|
10
|
+
readonly sourceBranch: string;
|
|
11
|
+
readonly sourceStateSha256: string;
|
|
12
|
+
readonly admission: {
|
|
13
|
+
readonly schemaVersion: 1;
|
|
14
|
+
readonly outcome: 'admitted-unverified';
|
|
15
|
+
readonly baseSha: string;
|
|
16
|
+
readonly executionBranch: string;
|
|
17
|
+
readonly changedPaths: readonly string[];
|
|
18
|
+
readonly patchSha256: string;
|
|
19
|
+
};
|
|
20
|
+
readonly artifactDigests: {
|
|
21
|
+
readonly index: string;
|
|
22
|
+
readonly epic: string;
|
|
23
|
+
readonly stories: Readonly<Record<string, string>>;
|
|
24
|
+
};
|
|
25
|
+
readonly qaEvidenceId: string;
|
|
26
|
+
readonly artifactSummary: string;
|
|
27
|
+
}
|
|
28
|
+
/** The root supplies independent QA; this CAS persists the accepted P03 checkpoint. */
|
|
29
|
+
export declare function sealPlanningGeneration(projectRoot: string, reviewInput: unknown): {
|
|
30
|
+
readonly epicId: string;
|
|
31
|
+
readonly physicalAcceptanceReceiptId: string;
|
|
32
|
+
readonly qaEvidenceId: string;
|
|
33
|
+
readonly storyIds: readonly string[];
|
|
34
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{execFileSync as v,spawnSync as tt}from"node:child_process";import{createHash as et,randomUUID as nt}from"node:crypto";import{closeSync as q,fsyncSync as G,lstatSync as N,mkdirSync as it,openSync as H,readFileSync as st,realpathSync as W,renameSync as rt,unlinkSync as Z,writeFileSync as X}from"node:fs";import{isAbsolute as ct,join as m,resolve as z}from"node:path";import{isDeepStrictEqual as T}from"node:util";import{planningPhysicalPatchDigest as ot}from"./planning-diagnosis-checkpoint.js";import{acceptedPlanningResearchPredecessor as at}from"./planning-research-checkpoint.js";const k=/^[a-f0-9]{64}$/,dt=/^P[1-9][0-9]{0,11}$/,pt=/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/,gt="acd455ed0e39d98b90fb57391db75e366e4c5604db043e03a89a20e33d643c3c",ft="fa2194f89c01f07e225fd6f39e98e679f628e072239b6cd0af3ee527f0722518",lt="8636f22ec5950410902448ee1e9cb4947359ac98df518742bddbbb367468e3ae";function s(t){return t!==null&&typeof t=="object"&&!Array.isArray(t)}function h(t){return et("sha256").update(t).digest("hex")}function F(t,e,i=128*1024){return v("git",[...e],{cwd:t,encoding:"utf8",stdio:["ignore","pipe","ignore"],maxBuffer:i}).trim()}function $(t,e,i){const n=N(m(t,e));if(!n.isFile()||n.isSymbolicLink()||n.size<1||n.size>i)throw new Error("planning-generation-physical-artifact-invalid");return st(m(t,e))}function L(t,e,i){const n=tt("git",["show",`HEAD:${e}`],{cwd:t,encoding:"buffer",maxBuffer:i});if(n.status!==0||!Buffer.isBuffer(n.stdout)||n.stdout.length<1||n.stdout.length>i)throw new Error("planning-generation-predecessor-uncommitted");return n.stdout}function K(t,e){for(const i of[".neocortex",".neocortex/planning",`.neocortex/planning/epic-${e}`,"docs","docs/epics","docs/stories"]){const n=N(m(t,i));if(!n.isDirectory()||n.isSymbolicLink()||n.uid!==process.getuid?.()||i===".neocortex"&&(n.mode&18)!==0)throw new Error("planning-generation-directory-boundary-invalid")}}function R(t,e){const n=/^---\n([\s\S]{1,8192}?)\n---\n/.exec(t.replace(/\r\n/g,`
|
|
2
|
+
`))?.[1]?.split(`
|
|
3
|
+
`).filter(a=>a.startsWith(`${e}:`))??[];return n.length===1?n[0].slice(e.length+1).trim().replace(/^(?:"(.*)"|'(.*)')$/,"$1$2"):void 0}function V(t,e){return typeof t=="string"&&t.length<=128&&h(t)===e}function C(t,e){return Array.from({length:e},(i,n)=>`docs/stories/${t}.${String(n+1).padStart(2,"0")}.story.md`)}function ht(t){if(!s(t)||Object.keys(t).sort().join("|")!==["schemaVersion","verdict","claimId","epicId","diagnosisReceiptId","researchReceiptId","intakeRevision","sourceHead","sourceBranch","sourceStateSha256","admission","artifactDigests","qaEvidenceId","artifactSummary"].sort().join("|")||t.schemaVersion!=="PlanningGenerationRootReview/1"||t.verdict!=="accepted"||typeof t.claimId!="string"||!pt.test(t.claimId)||typeof t.epicId!="string"||!dt.test(t.epicId)||typeof t.diagnosisReceiptId!="string"||!k.test(t.diagnosisReceiptId)||typeof t.researchReceiptId!="string"||!k.test(t.researchReceiptId)||typeof t.intakeRevision!="string"||!k.test(t.intakeRevision)||typeof t.sourceHead!="string"||!/^[a-f0-9]{40}$/.test(t.sourceHead)||typeof t.sourceBranch!="string"||!/^[a-zA-Z0-9][a-zA-Z0-9._/-]{0,127}$/.test(t.sourceBranch)||typeof t.sourceStateSha256!="string"||!k.test(t.sourceStateSha256)||!s(t.admission)||Object.keys(t.admission).sort().join("|")!==["schemaVersion","outcome","baseSha","executionBranch","changedPaths","patchSha256"].sort().join("|")||t.admission.schemaVersion!==1||t.admission.outcome!=="admitted-unverified"||t.admission.baseSha!==t.sourceHead||t.admission.executionBranch!==t.sourceBranch||typeof t.admission.patchSha256!="string"||!k.test(t.admission.patchSha256)||!s(t.artifactDigests)||Object.keys(t.artifactDigests).sort().join("|")!=="epic|index|stories"||typeof t.artifactDigests.epic!="string"||!k.test(t.artifactDigests.epic)||typeof t.artifactDigests.index!="string"||!k.test(t.artifactDigests.index)||!s(t.artifactDigests.stories)||Object.keys(t.artifactDigests.stories).length<1||Object.keys(t.artifactDigests.stories).length>24||Object.values(t.artifactDigests.stories).some(i=>typeof i!="string"||!k.test(i))||typeof t.qaEvidenceId!="string"||!/^qa-[a-z0-9-]{8,80}$/.test(t.qaEvidenceId)||typeof t.artifactSummary!="string"||t.artifactSummary.length<16||t.artifactSummary.length>2048)throw new Error("planning-generation-review-invalid");const e=C(t.epicId,Object.keys(t.artifactDigests.stories).length);if(!T(Object.keys(t.artifactDigests.stories).sort(),[...e].sort()))throw new Error("planning-generation-review-invalid")}function mt(t){const e={schemaVersion:"PlanningGenerationPhysicalAcceptance/1",claimId:t.claimId,epicId:t.epicId,diagnosisReceiptId:t.diagnosisReceiptId,researchReceiptId:t.researchReceiptId,intakeRevision:t.intakeRevision,qaEvidenceId:t.qaEvidenceId,sourceHead:t.sourceHead,sourceStateSha256:t.sourceStateSha256,admissionPatchSha256:t.admission.patchSha256,artifactDigests:t.artifactDigests};return{...e,receiptId:h(JSON.stringify(e)),publicSafe:!0}}function M(t,e,i,n,a){if(K(t,e.epicId),F(t,["rev-parse","HEAD"])!==e.sourceHead||F(t,["symbolic-ref","--quiet","--short","HEAD"])!==e.sourceBranch)throw new Error("planning-generation-head-conflict");const f=`.neocortex/planning/epic-${e.epicId}`,y=`docs/epics/epic-${e.epicId}.md`,c=".neocortex/planning/planning.json",r=C(e.epicId,Object.keys(e.artifactDigests.stories).length),A={index:c,epic:y,...Object.fromEntries(r.map((g,l)=>[`story${l}`,g]))};if(!Array.isArray(e.admission.changedPaths)||!T([...e.admission.changedPaths].sort(),Object.values(A).sort())||i&&ot(t,A,n,!1)!==e.admission.patchSha256)throw new Error("planning-generation-admission-patch-conflict");const O=$(t,c,128*1024),j=$(t,y,512*1024),B=r.map(g=>$(t,g,128*1024));if(h(O)!==e.artifactDigests.index||h(j)!==e.artifactDigests.epic||r.some((g,l)=>h(B[l])!==e.artifactDigests.stories[g]))throw new Error("planning-generation-artifact-conflict");const u=j.toString("utf8"),S=R(u,"title");if(R(u,"id")!==e.epicId||R(u,"stories_count")!==String(r.length)||!S||S.length>200||!u.includes("## Stories"))throw new Error("planning-generation-epic-invalid");for(const[g,l]of B.entries()){const D=l.toString("utf8");if(R(D,"id")!==`${e.epicId}.${String(g+1).padStart(2,"0")}`||R(D,"epic")!==e.epicId||R(D,"status")!=="backlog"||!R(D,"title")||!D.includes("## Acceptance Criteria")||!/\bGiven\b[\s\S]*\bWhen\b[\s\S]*\bThen\b/.test(D))throw new Error("planning-generation-story-invalid")}const I=a??$(t,".neocortex/state.json",8*1024*1024),d=JSON.parse(I.toString("utf8"));if(!s(d)||!s(d.epics)||!s(d.stories))throw new Error("planning-generation-state-invalid");const _=at(d,e.claimId,e.epicId);if(_.diagnosisReceiptId!==e.diagnosisReceiptId||_.researchReceiptId!==e.researchReceiptId||_.intakeRevision!==e.intakeRevision||i&&h(I)!==e.sourceStateSha256)throw new Error("planning-generation-state-conflict");const w=d.epics[e.epicId];if(!s(w)||r.some((g,l)=>Object.hasOwn(d.stories,`${e.epicId}.${String(l+1).padStart(2,"0")}`)))throw new Error("planning-generation-story-collision");const P=w.createEpicPlanningPhysical,b=w.createEpicPlanningResearchPhysical;if(!s(P)||!s(b)||!s(P.artifactDigests)||!s(b.artifactDigests))throw new Error("planning-generation-predecessor-invalid");for(const[g,l]of Object.entries({idea:`${f}/idea.json`,diagnosis:`${f}/diagnosis.md`,enrichment:`${f}/enriched-prompt.md`,research:`${f}/research.md`})){const D=g==="research"?b.artifactDigests.research:P.artifactDigests[g],U=$(t,l,g==="idea"?32*1024:256*1024);if(h(U)!==D||!U.equals(L(t,l,512*1024)))throw new Error("planning-generation-predecessor-artifact-drift")}const J=L(t,c,128*1024);if(h(J)!==b.artifactDigests.index)throw new Error("planning-generation-predecessor-index-drift");const E=JSON.parse(J.toString("utf8")),p=JSON.parse(O.toString("utf8"));if(!s(E)||!s(E.epics)||!s(p)||!s(p.epics))throw new Error("planning-generation-index-invalid");const o=E.epics[e.epicId],x=p.epics[e.epicId];if(!s(o)||o.id!==e.epicId||o.status!=="researched"||!Array.isArray(o.steps_completed)||o.steps_completed.length!==2||!V(o.steps_completed[0],gt)||!V(o.steps_completed[1],ft)||!T(o.output_files,{diagnosis:`${f}/diagnosis.md`,research:`${f}/research.md`})||!s(x)||!Array.isArray(x.steps_completed)||!V(x.steps_completed[2],lt))throw new Error("planning-generation-index-predecessor-invalid");const Y={...E,last_scan:p.last_scan,epics:{...E.epics,[e.epicId]:{...o,status:"generated",steps_completed:[...o.steps_completed,x.steps_completed[2]],completed_at:x.completed_at,output_files:{...o.output_files,epic:y,stories:r}}}};if(typeof p.last_scan!="string"||!Number.isFinite(Date.parse(p.last_scan))||typeof x.completed_at!="string"||!Number.isFinite(Date.parse(x.completed_at))||!T(p,Y))throw new Error("planning-generation-index-transition-invalid");return{stateRaw:I,state:d,title:S,stories:r,storyBytes:B,completedAt:x.completed_at}}function Q(t,e,i){const n=t.state,a=t.stories.map(c=>c.slice(13,-9)),f=n.epics[e.epicId],y={...n.stories};for(const[c,r]of a.entries())y[r]={id:r,epic_id:e.epicId,title:R(t.storyBytes[c].toString("utf8"),"title"),status:"backlog"};return{...n,epics:{...n.epics,[e.epicId]:{...f,title:t.title,status:"generated",stories:a,total_stories:a.length,completed_stories:0,createEpicPlanningGenerationPhysical:i}},stories:y,last_updated:t.completedAt}}function Dt(t,e){ht(e);const i=e,n=z(t);if(W(n)!==n||F(n,["rev-parse","--show-toplevel"])!==n)throw new Error("planning-generation-root-invalid");K(n,i.epicId);const a=m(n,".neocortex"),f=m(a,"state.json.planning.lock"),y=H(f,"wx",384);let c;try{const r=mt(i),A=C(i.epicId,Object.keys(i.artifactDigests.stories).length).map(p=>p.slice(13,-9)),O=$(n,".neocortex/state.json",8*1024*1024);if(h(O)!==i.sourceStateSha256){const p=L(n,".neocortex/state.json",8388608);if(h(p)!==i.sourceStateSha256)throw new Error("planning-generation-replay-conflict");const o=M(n,i,!1,void 0,p);if(!T(JSON.parse(O.toString("utf8")),Q(o,i,r)))throw new Error("planning-generation-replay-conflict");return{epicId:i.epicId,physicalAcceptanceReceiptId:r.receiptId,qaEvidenceId:i.qaEvidenceId,storyIds:A}}const j=M(n,i,!0),B=Q(j,i,r),u=F(n,["rev-parse","--git-common-dir"]),S=ct(u)?z(u):z(n,u);if(W(S)!==S||N(S).isSymbolicLink())throw new Error("planning-generation-backup-dir-invalid");const I=m(S,"neocortex-planning");it(I,{recursive:!0,mode:448});const d=N(I);if(!d.isDirectory()||d.isSymbolicLink()||d.uid!==process.getuid?.()||(d.mode&63)!==0)throw new Error("planning-generation-backup-dir-invalid");const _=nt(),w=H(m(I,`state-backup-${_}.json`),"wx",384);try{X(w,j.stateRaw),G(w)}finally{q(w)}const P=H(I,"r");try{G(P)}finally{q(P)}c=m(a,`state.json.planning-tmp-${_}`);const b=H(c,"wx",384);try{X(b,`${JSON.stringify(B,null,2)}
|
|
4
|
+
`),G(b)}finally{q(b)}if(!M(n,i,!0,`.neocortex/state.json.planning-tmp-${_}`).stateRaw.equals(j.stateRaw))throw new Error("planning-generation-state-conflict");rt(c,m(a,"state.json")),c=void 0;const E=H(a,"r");try{G(E)}finally{q(E)}return{epicId:i.epicId,physicalAcceptanceReceiptId:r.receiptId,qaEvidenceId:i.qaEvidenceId,storyIds:A}}finally{if(c)try{Z(c)}catch{}q(y),Z(f)}}export{Dt as sealPlanningGeneration};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export interface PlanningResearchRootReviewV1 {
|
|
2
|
+
readonly schemaVersion: 'PlanningResearchRootReview/1';
|
|
3
|
+
readonly verdict: 'accepted';
|
|
4
|
+
readonly claimId: string;
|
|
5
|
+
readonly epicId: string;
|
|
6
|
+
readonly diagnosisReceiptId: string;
|
|
7
|
+
readonly intakeRevision: string;
|
|
8
|
+
readonly sourceHead: string;
|
|
9
|
+
readonly sourceBranch: string;
|
|
10
|
+
readonly sourceStateSha256: string;
|
|
11
|
+
readonly admission: {
|
|
12
|
+
readonly schemaVersion: 1;
|
|
13
|
+
readonly outcome: 'admitted-unverified';
|
|
14
|
+
readonly baseSha: string;
|
|
15
|
+
readonly executionBranch: string;
|
|
16
|
+
readonly changedPaths: readonly string[];
|
|
17
|
+
readonly patchSha256: string;
|
|
18
|
+
};
|
|
19
|
+
readonly artifactDigests: Readonly<{
|
|
20
|
+
research: string;
|
|
21
|
+
index: string;
|
|
22
|
+
}>;
|
|
23
|
+
readonly qaEvidenceId: string;
|
|
24
|
+
readonly artifactSummary: string;
|
|
25
|
+
}
|
|
26
|
+
/** State-consistent P02 seal for a future P03 lease; physical files remain a separate check. */
|
|
27
|
+
export declare function acceptedPlanningResearchPredecessor(stateInput: unknown, claimId: string, epicId: string): {
|
|
28
|
+
readonly intakeRevision: string;
|
|
29
|
+
readonly diagnosisReceiptId: string;
|
|
30
|
+
readonly researchReceiptId: string;
|
|
31
|
+
readonly physicalAcceptanceReceiptId: string;
|
|
32
|
+
};
|
|
33
|
+
/** A root reviewer accepts exact P02 files, then this CAS persists only the research checkpoint. */
|
|
34
|
+
export declare function sealPlanningResearch(projectRoot: string, reviewInput: unknown): {
|
|
35
|
+
readonly epicId: string;
|
|
36
|
+
readonly researchReceiptId: string;
|
|
37
|
+
readonly qaEvidenceId: string;
|
|
38
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{execFileSync as U,spawnSync as Z}from"node:child_process";import{createHash as W,randomUUID as X}from"node:crypto";import{closeSync as D,fsyncSync as q,lstatSync as A,mkdirSync as K,openSync as j,readFileSync as Q,realpathSync as N,renameSync as Y,unlinkSync as J,writeFileSync as F}from"node:fs";import{isAbsolute as v,join as m,resolve as H}from"node:path";import{isDeepStrictEqual as E}from"node:util";import{createPlanningReceiptV1 as O}from"../../../shared/dist/planning/diagnosis-state.js";import{acceptedPlanningDiagnosisPredecessor as L,planningPhysicalPatchDigest as ee}from"./planning-diagnosis-checkpoint.js";const l=/^[a-f0-9]{64}$/,te=/^P[1-9][0-9]{0,11}$/,ie=/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/,ne="acd455ed0e39d98b90fb57391db75e366e4c5604db043e03a89a20e33d643c3c",se="fa2194f89c01f07e225fd6f39e98e679f628e072239b6cd0af3ee527f0722518";function p(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function y(e){return W("sha256").update(e).digest("hex")}function _(e,t,i=128*1024){return U("git",[...t],{cwd:e,encoding:"utf8",stdio:["ignore","pipe","ignore"],maxBuffer:i}).trim()}function $(e,t,i){const n=A(m(e,t));if(!n.isFile()||n.isSymbolicLink()||n.size<1||n.size>i)throw new Error("planning-research-physical-artifact-invalid");return Q(m(e,t))}function T(e,t){for(const i of[".neocortex",".neocortex/planning",`.neocortex/planning/epic-${t}`]){const n=A(m(e,i));if(!n.isDirectory()||n.isSymbolicLink()||n.uid!==process.getuid?.()||i===".neocortex"&&(n.mode&18)!==0)throw new Error("planning-research-directory-boundary-invalid")}}function C(e,t){return typeof e=="string"&&e.length<=128&&y(e)===t}function ce(e,t){const i=e.replace(/\r\n/g,`
|
|
2
|
+
`),n=/^---\n([\s\S]{1,8192}?)\n---\n/.exec(i)?.[1];if(!n)return;const o=n.split(`
|
|
3
|
+
`),a=c=>{const s=o.filter(r=>r.startsWith(`${c}:`));return s.length===1?s[0].slice(c.length+1).trim().replace(/^(?:"(.*)"|'(.*)')$/,"$1$2"):void 0},f=a("step");return a("epic_id")===t&&a("status")==="completed"&&C(f,se)&&i.slice(n.length+9).trim().length>0?f:void 0}function re(e){if(!p(e)||Object.keys(e).sort().join("|")!==["schemaVersion","verdict","claimId","epicId","diagnosisReceiptId","intakeRevision","sourceHead","sourceBranch","sourceStateSha256","admission","artifactDigests","qaEvidenceId","artifactSummary"].sort().join("|")||e.schemaVersion!=="PlanningResearchRootReview/1"||e.verdict!=="accepted"||typeof e.claimId!="string"||!ie.test(e.claimId)||typeof e.epicId!="string"||!te.test(e.epicId)||typeof e.diagnosisReceiptId!="string"||!l.test(e.diagnosisReceiptId)||typeof e.intakeRevision!="string"||!l.test(e.intakeRevision)||typeof e.sourceHead!="string"||!/^[a-f0-9]{40}$/.test(e.sourceHead)||typeof e.sourceBranch!="string"||!/^[a-zA-Z0-9][a-zA-Z0-9._/-]{0,127}$/.test(e.sourceBranch)||typeof e.sourceStateSha256!="string"||!l.test(e.sourceStateSha256)||!p(e.admission)||Object.keys(e.admission).sort().join("|")!==["schemaVersion","outcome","baseSha","executionBranch","changedPaths","patchSha256"].sort().join("|")||e.admission.schemaVersion!==1||e.admission.outcome!=="admitted-unverified"||e.admission.baseSha!==e.sourceHead||e.admission.executionBranch!==e.sourceBranch||typeof e.admission.patchSha256!="string"||!l.test(e.admission.patchSha256)||!p(e.artifactDigests)||Object.keys(e.artifactDigests).sort().join("|")!=="index|research"||typeof e.artifactDigests.index!="string"||!l.test(e.artifactDigests.index)||typeof e.artifactDigests.research!="string"||!l.test(e.artifactDigests.research)||typeof e.qaEvidenceId!="string"||!/^qa-[a-z0-9-]{8,80}$/.test(e.qaEvidenceId)||typeof e.artifactSummary!="string")throw new Error("planning-research-review-invalid");O({kind:"research",epicId:e.epicId,intakeRevision:e.intakeRevision,predecessorReceiptId:e.diagnosisReceiptId,artifactSummary:e.artifactSummary,accepted:!0})}function G(e,t){const i={schemaVersion:"PlanningResearchPhysicalAcceptance/1",claimId:e.claimId,epicId:e.epicId,diagnosisReceiptId:e.diagnosisReceiptId,researchReceiptId:t,intakeRevision:e.intakeRevision,qaEvidenceId:e.qaEvidenceId,sourceHead:e.sourceHead,sourceStateSha256:e.sourceStateSha256,admissionPatchSha256:e.admission.patchSha256,artifactDigests:e.artifactDigests};return{...i,receiptId:y(JSON.stringify(i)),publicSafe:!0}}function B(e,t,i,n){if(T(e,t.epicId),_(e,["rev-parse","HEAD"])!==t.sourceHead||_(e,["symbolic-ref","--quiet","--short","HEAD"])!==t.sourceBranch)throw new Error("planning-research-head-conflict");const o={research:`.neocortex/planning/epic-${t.epicId}/research.md`,index:".neocortex/planning/planning.json"};if(!Array.isArray(t.admission.changedPaths)||[...t.admission.changedPaths].sort().join("|")!==Object.values(o).sort().join("|")||i&&ee(e,o,n,!1)!==t.admission.patchSha256)throw new Error("planning-research-admission-patch-conflict");const a={research:$(e,o.research,256*1024),index:$(e,o.index,128*1024)},f=ce(a.research.toString("utf8"),t.epicId);if(y(a.research)!==t.artifactDigests.research||y(a.index)!==t.artifactDigests.index||!f)throw new Error("planning-research-artifact-conflict");const c=$(e,".neocortex/state.json",8*1024*1024),s=JSON.parse(c.toString("utf8")),r=L(s,t.claimId,t.epicId,!i);if(r.diagnosisReceiptId!==t.diagnosisReceiptId||r.intakeRevision!==t.intakeRevision||i&&y(c)!==t.sourceStateSha256)throw new Error("planning-research-state-conflict");const w=s.epics[t.epicId].createEpicPlanningPhysical.artifactDigests,I=`.neocortex/planning/epic-${t.epicId}`;for(const[k,b]of Object.entries({idea:`${I}/idea.json`,diagnosis:`${I}/diagnosis.md`,enrichment:`${I}/enriched-prompt.md`}))if(y($(e,b,k==="idea"?32*1024:256*1024))!==w[k])throw new Error("planning-research-predecessor-artifact-drift");const u=Z("git",["show",`HEAD:${o.index}`],{cwd:e,encoding:"buffer",maxBuffer:128*1024});if(u.status!==0||!Buffer.isBuffer(u.stdout)||y(u.stdout)!==w.index)throw new Error("planning-research-predecessor-index-drift");const g=JSON.parse(u.stdout.toString("utf8")),h=JSON.parse(a.index.toString("utf8"));if(!p(g)||!p(g.epics)||!p(h)||!p(h.epics))throw new Error("planning-research-index-invalid");const d=g.epics[t.epicId];if(!p(d)||d.id!==t.epicId||d.idea!==r.rawIntent||d.status!=="diagnosed"||!Array.isArray(d.steps_completed)||d.steps_completed.length!==1||!C(d.steps_completed[0],ne)||!E(d.output_files,{diagnosis:`${I}/diagnosis.md`}))throw new Error("planning-research-index-predecessor-invalid");const x={...g,last_scan:h.last_scan,epics:{...g.epics,[t.epicId]:{...d,status:"researched",steps_completed:[...d.steps_completed,f],output_files:{diagnosis:`${I}/diagnosis.md`,research:o.research}}}};if(typeof h.last_scan!="string"||!Number.isFinite(Date.parse(h.last_scan))||!E(h,x))throw new Error("planning-research-index-transition-invalid");const P=O({kind:"research",epicId:t.epicId,intakeRevision:t.intakeRevision,predecessorReceiptId:t.diagnosisReceiptId,artifactSummary:t.artifactSummary,accepted:!0});return{stateRaw:c,state:s,receipt:P}}function ae(e,t){const i=B(e,t,!1),o=i.state.epics[t.epicId],a=o.createEpicPlanning;if(o.status!=="researched"||!E(a.researchReceipt,i.receipt)||!E(o.createEpicPlanningResearchPhysical,G(t,i.receipt.receiptId)))throw new Error("planning-research-replay-conflict");return i.receipt.receiptId}function ye(e,t,i){const n=L(e,t,i,!0),a=e.epics[i],f=p(a?.createEpicPlanning)?a.createEpicPlanning:void 0,c=p(f?.researchReceipt)?f.researchReceipt:void 0,s=p(a?.createEpicPlanningResearchPhysical)?a.createEpicPlanningResearchPhysical:void 0,r=p(s?.artifactDigests)?s.artifactDigests:void 0;if(a?.status!=="researched"||!c||!s||!r||typeof c.artifactSummary!="string"||!E(c,O({kind:"research",epicId:i,intakeRevision:n.intakeRevision,predecessorReceiptId:n.diagnosisReceiptId,artifactSummary:c.artifactSummary,accepted:!0}))||s.schemaVersion!=="PlanningResearchPhysicalAcceptance/1"||s.claimId!==t||s.epicId!==i||s.diagnosisReceiptId!==n.diagnosisReceiptId||s.researchReceiptId!==c.receiptId||s.intakeRevision!==n.intakeRevision||typeof s.qaEvidenceId!="string"||!/^qa-[a-z0-9-]{8,80}$/.test(s.qaEvidenceId)||typeof s.sourceHead!="string"||!/^[a-f0-9]{40}$/.test(s.sourceHead)||typeof s.sourceStateSha256!="string"||!l.test(s.sourceStateSha256)||typeof s.admissionPatchSha256!="string"||!l.test(s.admissionPatchSha256)||Object.keys(r).sort().join("|")!=="index|research"||typeof r.index!="string"||!l.test(r.index)||typeof r.research!="string"||!l.test(r.research))throw new Error("planning-generation-predecessor-invalid");const S={schemaVersion:s.schemaVersion,claimId:t,epicId:i,diagnosisReceiptId:n.diagnosisReceiptId,researchReceiptId:c.receiptId,intakeRevision:n.intakeRevision,qaEvidenceId:s.qaEvidenceId,sourceHead:s.sourceHead,sourceStateSha256:s.sourceStateSha256,admissionPatchSha256:s.admissionPatchSha256,artifactDigests:r},R={...S,receiptId:y(JSON.stringify(S)),publicSafe:!0};if(!E(s,R))throw new Error("planning-generation-predecessor-invalid");return{intakeRevision:n.intakeRevision,diagnosisReceiptId:n.diagnosisReceiptId,researchReceiptId:c.receiptId,physicalAcceptanceReceiptId:R.receiptId}}function ue(e,t){re(t);const i=t,n=H(e);if(N(n)!==n||_(n,["rev-parse","--show-toplevel"])!==n)throw new Error("planning-research-root-invalid");T(n,i.epicId);const o=m(n,".neocortex"),a=m(o,"state.json.planning.lock"),f=j(a,"wx",384);let c;try{const s=$(n,".neocortex/state.json",8388608);if(y(s)!==i.sourceStateSha256){const M=ae(n,i);return{epicId:i.epicId,researchReceiptId:M,qaEvidenceId:i.qaEvidenceId}}const r=B(n,i,!0),S=r.state.epics,R=S[i.epicId],V=R.createEpicPlanning,w=G(i,r.receipt.receiptId),I={...r.state,epics:{...S,[i.epicId]:{...R,status:"researched",createEpicPlanning:{...V,researchReceipt:r.receipt},createEpicPlanningResearchPhysical:w}}},u=_(n,["rev-parse","--git-common-dir"]),g=v(u)?H(u):H(n,u);if(N(g)!==g||A(g).isSymbolicLink())throw new Error("planning-research-backup-dir-invalid");const h=m(g,"neocortex-planning");K(h,{recursive:!0,mode:448});const d=A(h);if(!d.isDirectory()||d.isSymbolicLink()||d.uid!==process.getuid?.()||(d.mode&63)!==0)throw new Error("planning-research-backup-dir-invalid");const x=X(),P=j(m(h,`state-backup-${x}.json`),"wx",384);try{F(P,r.stateRaw),q(P)}finally{D(P)}const k=j(h,"r");try{q(k)}finally{D(k)}c=m(o,`state.json.planning-tmp-${x}`);const b=j(c,"wx",384);try{F(b,`${JSON.stringify(I,null,2)}
|
|
4
|
+
`),q(b)}finally{D(b)}if(!B(n,i,!0,`.neocortex/state.json.planning-tmp-${x}`).stateRaw.equals(r.stateRaw))throw new Error("planning-research-state-conflict");Y(c,m(o,"state.json")),c=void 0;const z=j(o,"r");try{q(z)}finally{D(z)}return{epicId:i.epicId,researchReceiptId:r.receipt.receiptId,qaEvidenceId:i.qaEvidenceId}}finally{if(c)try{J(c)}catch{}D(f),J(a)}}export{ye as acceptedPlanningResearchPredecessor,ue as sealPlanningResearch};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{parse as H}from"@babel/parser";import{lstatSync as q,readFileSync as G}from"node:fs";import{join as K,posix as V}from"node:path";const Q=1024*1024,ee=1e5,z=/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;function p(){throw new Error("private-execution-ux-source-unverified")}function o(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function j(e,t){const r=q(K(e,t));(!r.isFile()||r.isSymbolicLink()||r.size<1||r.size>Q)&&p();const s=new TextDecoder("utf-8",{fatal:!0}).decode(G(K(e,t)));return(!s.trim()||s.includes("\0"))&&p(),s}function I(e,t){const r=H(j(e,t),{sourceType:"module",plugins:["typescript","jsx"],errorRecovery:!1});let s=0;const i=[r];for(;i.length>0;){const l=i.pop();if(o(l)){++s>ee&&p();for(const[n,f]of Object.entries(l))n==="loc"||n==="tokens"||n==="comments"||n==="errors"||(Array.isArray(f)?i.push(...f):o(f)&&i.push(f))}}return r}function E(e){const t=e.program;return(!o(t)||!Array.isArray(t.body))&&p(),t.body.filter(o)}function m(e,t){if(!o(e)||e.type!=="ObjectExpression"||!Array.isArray(e.properties))return;const r=e.properties.filter(s=>o(s)&&s.type==="ObjectProperty"&&s.computed!==!0&&o(s.key)&&(s.key.name===t||s.key.value===t));return r.length===1&&o(r[0])?r[0].value:void 0}function g(e){return o(e)&&e.type==="StringLiteral"&&typeof e.value=="string"?e.value:void 0}function y(e){return o(e)&&e.type==="Identifier"&&typeof e.name=="string"?e.name:void 0}function x(e){return e.type==="VariableDeclaration"&&Array.isArray(e.declarations)?e.declarations.filter(o):[]}function D(e){let t=e;for(let r=0;r<4&&o(t)&&["TSAsExpression","TSSatisfiesExpression","TSTypeAssertion","ParenthesizedExpression"].includes(String(t.type));r++)t=t.expression;return t}function te(e,t){const r=e.flatMap(x).filter(s=>y(s.id)===t);return r.length===1?D(r[0].init):void 0}function R(e,t){for(const r of e){if(r.type!=="ExportNamedDeclaration"||!o(r.declaration))continue;const s=r.declaration;if(s.type==="FunctionDeclaration"&&o(s.id)&&s.id.name===t)return s;const i=x(s).find(l=>o(l.id)&&l.id.name===t);if(i)return D(i.init)}}function A(e,t){const r=[],s=[e];for(;s.length>0;){const i=s.pop();if(o(i)){i.type===t&&r.push(i);for(const[l,n]of Object.entries(i))l==="loc"||l==="comments"||(Array.isArray(n)?s.push(...n):o(n)&&s.push(n))}}return r}function P(e){return e.screenId.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,64)}function U(e){return e.split(/[^A-Za-z0-9]+/).filter(Boolean).map(t=>`${t.charAt(0).toUpperCase()}${t.slice(1).toLowerCase()}`).join("")}function Z(e,t,r){const s=[];for(const i of e){if(i.type!=="ImportDeclaration"||i.importKind==="type"||!o(i.source)||typeof i.source.value!="string"||!i.source.value.startsWith("./")&&!i.source.value.startsWith("../"))continue;const l=V.normalize(V.join(V.dirname(t),i.source.value));if(!(l!==r.screenViewPath&&l!==r.screenViewPath.replace(/\.tsx$/,"")))for(const n of Array.isArray(i.specifiers)?i.specifiers:[])o(n)&&n.type==="ImportSpecifier"&&o(n.imported)&&n.imported.name===r.screenViewExport&&n.importKind!=="type"&&o(n.local)&&typeof n.local.name=="string"&&s.push(n.local.name)}return s.length!==1&&p(),s[0]}function re(e,t){return A(e,"JSXOpeningElement").some(r=>o(r.name)&&r.name.name===t)}function w(e,t,r){const s=e.filter(i=>i.type==="ImportDeclaration"&&o(i.source)&&i.source.value===t).flatMap(i=>Array.isArray(i.specifiers)?i.specifiers:[]).filter(i=>o(i)&&i.type==="ImportSpecifier"&&o(i.imported)&&i.imported.name===r&&o(i.local)&&typeof i.local.name=="string").map(i=>i.local);return s.length===1?s[0].name:void 0}function Y(e,t,r){return o(e)&&e.type==="CallExpression"&&o(e.callee)&&e.callee.type==="MemberExpression"&&e.callee.computed!==!0&&y(e.callee.object)===t&&y(e.callee.property)===r}function ne(e,t){const r=w(t,"storybook/test","within"),s=w(t,"storybook/test","userEvent"),i=w(t,"storybook/test","expect");if(!r||!s||!i||!Array.isArray(e.params)||!o(e.params[0])||e.params[0].type!=="ObjectPattern"||!Array.isArray(e.params[0].properties)||!e.params[0].properties.some(a=>o(a)&&o(a.key)&&a.key.name==="canvasElement"&&o(a.value)&&a.value.name==="canvasElement"))return!1;const l=A(e.body,"VariableDeclarator"),n=l.filter(a=>o(a.init)&&a.init.type==="CallExpression"&&y(a.init.callee)===r&&Array.isArray(a.init.arguments)&&a.init.arguments.length===1&&y(a.init.arguments[0])==="canvasElement").map(a=>y(a.id)).filter(a=>!!a),f=l.filter(a=>n.some(k=>Y(a.init,k,"getByRole")&&Array.isArray(a.init.arguments)&&g(a.init.arguments[0])==="button"&&typeof g(m(a.init.arguments[1],"name"))=="string"&&!!g(m(a.init.arguments[1],"name"))?.trim())).map(a=>y(a.id)).filter(a=>!!a);if(f.length===0)return!1;const u=A(e.body,"AwaitExpression").map(a=>a.argument),$=u.some(a=>Y(a,s,"tab")&&Array.isArray(a.arguments)&&a.arguments.length===0),h=u.some(a=>o(a)&&a.type==="CallExpression"&&o(a.callee)&&a.callee.type==="MemberExpression"&&y(a.callee.property)==="toHaveFocus"&&o(a.callee.object)&&a.callee.object.type==="CallExpression"&&y(a.callee.object.callee)===i&&Array.isArray(a.callee.object.arguments)&&f.includes(y(a.callee.object.arguments[0])??""));return $&&h}function se(e,t,r){const s=`src/screens/${P(t)}/${P(t)}`,i=I(e,t.screenViewPath),l=R(E(i),t.screenViewExport);(!l||A(l,"JSXElement").length+A(l,"JSXFragment").length===0)&&p();const n=I(e,t.appRoutePath),f=Z(E(n),t.appRoutePath,t);re(n,f)||p();const u=`${s}.stories.tsx`,$=I(e,u),h=E($),a=r==="storybook-react-vite"?"@storybook/react-vite":"@storybook/nextjs-vite";(!w(h,a,"Meta")||!w(h,a,"StoryObj"))&&p();const k=Z(h,u,t),C=h.filter(c=>c.type==="ExportDefaultDeclaration");C.length!==1&&p();const O=D(C[0].declaration),T=y(O)?te(h,y(O)):O;(y(m(T,"component"))!==k||g(m(T,"title"))!==`Screens/${t.screenId}`)&&p();const v=new Set;for(const c of t.states)for(const d of t.viewports){const L=`${U(c)}${U(d)}`;v.add(L);const b=R(h,L);if((!o(b)||b.type!=="ObjectExpression"||g(m(m(b,"args"),"state"))!==c||g(m(m(m(b,"parameters"),"viewport"),"defaultViewport"))!==d)&&p(),t.critical&&(c==="success"||c==="recovery")){const N=m(b,"play");(!o(N)||!["ArrowFunctionExpression","FunctionExpression"].includes(String(N.type))||!ne(N,h))&&p()}}const F=h.filter(c=>c.type==="ExportNamedDeclaration");F.some(c=>!o(c.declaration)||x(c.declaration).length!==1||Array.isArray(c.specifiers)&&c.specifiers.length>0)&&p();const X=F.flatMap(c=>x(c.declaration)).map(c=>y(c.id)).filter(c=>!!c);(X.length!==v.size||X.some(c=>!v.has(c)))&&p();const W=I(e,`${s}.fixtures.ts`);E(W).filter(c=>c.type==="ExportNamedDeclaration").flatMap(c=>o(c.declaration)?x(c.declaration):[]).some(c=>{const d=m(c.init,"synthetic");return o(d)&&d.type==="BooleanLiteral"&&d.value===!0&&m(c.init,"data")!==void 0})||p();const _=I(e,`${s}.handlers.ts`),B=E(_);(!B.some(c=>c.type==="ImportDeclaration"&&o(c.source)&&c.source.value==="msw"&&Array.isArray(c.specifiers)&&c.specifiers.some(d=>o(d)&&o(d.imported)&&d.imported.name==="http"))||!B.some(c=>c.type==="ExportNamedDeclaration"&&o(c.declaration)&&x(c.declaration).some(d=>o(d.init)&&d.init.type==="ArrayExpression"&&Array.isArray(d.init.elements)&&d.init.elements.length>0))||!A(_,"CallExpression").some(c=>o(c.callee)&&c.callee.type==="MemberExpression"&&o(c.callee.object)&&c.callee.object.name==="http"))&&p();const M=j(e,`${s}.mdx`),J=new RegExp(`import\\s+\\*\\s+as\\s+([A-Za-z_$][\\w$]*)\\s+from\\s+['"]\\./${P(t)}\\.stories(?:\\.tsx)?['"];?\\r?\\n(?:[ \\t]*\\r?\\n)+`).exec(M);(!/import\s+\{\s*Meta\s*\}\s+from\s+['"]@storybook\/addon-docs\/blocks['"]/.test(M)||!J||!M.includes(`<Meta of={${J[1]}} />`))&&p()}function S(e){return Array.isArray(e)?`[${e.map(S).join(",")}]`:e&&typeof e=="object"?`{${Object.entries(e).sort(([t],[r])=>t.localeCompare(r)).map(([t,r])=>`${JSON.stringify(t)}:${S(r)}`).join(",")}}`:JSON.stringify(e)??"null"}function oe(e){const t=S(e);let r=2166136261;for(let s=0;s<t.length;s++)r^=t.charCodeAt(s),r=Math.imul(r,16777619);return`uxm-${(r>>>0).toString(16).padStart(8,"0")}`}function ie(e,t){let r;try{r=JSON.parse(j(e,"docs/architecture/ux-coverage-manifest.json"))}catch{p()}(!o(r)||r.schemaVersion!==1||r.manifestId!=="P201-UX-MANIFEST-01"||!Array.isArray(r.entries)||typeof r.fingerprint!="string"||Object.keys(r).sort().join(",")!=="entries,fingerprint,manifestId,schemaVersion"||r.fingerprint!==oe(r.entries))&&p();const s=new Map;for(const n of t.screens)for(const f of n.states)for(const u of n.viewports)s.set(`Screens/${n.screenId}/${f}/${u}`,{screen:n,state:f,viewport:u});r.entries.length!==s.size&&p();const i=new Set;let l="";for(const n of r.entries){(!o(n)||typeof n.storyId!="string"||i.has(n.storyId))&&p(),l&&l.localeCompare(n.storyId)>=0&&p(),l=n.storyId,Object.keys(n).sort().join(",")!=="approvalEvidenceIds,fixtureId,handlerIds,journeyIds,screenId,screenViewPath,state,storyId,viewport"&&p(),i.add(n.storyId);const f=s.get(n.storyId);(!f||n.screenId!==f.screen.screenId||n.state!==f.state||n.viewport!==f.viewport||n.screenViewPath!==f.screen.screenViewPath||!Array.isArray(n.approvalEvidenceIds)||S([...n.approvalEvidenceIds].sort())!==S([...f.screen.approvalEvidenceIds].sort())||!Array.isArray(n.journeyIds)||n.journeyIds.length<1||n.journeyIds.some(u=>typeof u!="string"||!z.test(u))||new Set(n.journeyIds).size!==n.journeyIds.length||typeof n.fixtureId!="string"||!z.test(n.fixtureId)||!Array.isArray(n.handlerIds)||n.handlerIds.length<1||n.handlerIds.some(u=>typeof u!="string"||!z.test(u))||new Set(n.handlerIds).size!==n.handlerIds.length)&&p()}}function fe(e,t){try{for(const[r,s]of[["docs/architecture/ux-design.md","P201-UX-STORYBOOK-01"],["docs/architecture/storybook-ux-contract.md","P201-UX-STORYBOOK-01"]])j(e,r).includes(s)||p();for(const r of t.screens)se(e,r,t.adapterId);ie(e,t)}catch{p()}}export{fe as validateUxCandidateSource};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export declare const UX_WRITE_AUTHORITY_PATH = "docs/architecture/ux-execution-authority.json";
|
|
2
|
+
export interface UxWriteSetV1 {
|
|
3
|
+
readonly schemaVersion: 1;
|
|
4
|
+
readonly targetEpicId: string;
|
|
5
|
+
readonly adapterId: 'storybook-react-vite' | 'storybook-nextjs-vite';
|
|
6
|
+
readonly authorityPath: typeof UX_WRITE_AUTHORITY_PATH;
|
|
7
|
+
readonly authoritySha256: string;
|
|
8
|
+
readonly pathsSha256: string;
|
|
9
|
+
readonly screenCount: number;
|
|
10
|
+
readonly pathCount: number;
|
|
11
|
+
readonly paths: readonly string[];
|
|
12
|
+
/** Local-only approved screen contract; never project into public binding metadata. */
|
|
13
|
+
readonly screens: readonly UxApprovedScreenV1[];
|
|
14
|
+
/** Local-only HEAD content hashes; never project this map into public metadata. */
|
|
15
|
+
readonly baselineSha256: Readonly<Record<string, string | null>>;
|
|
16
|
+
}
|
|
17
|
+
export interface UxApprovedScreenV1 {
|
|
18
|
+
readonly screenId: string;
|
|
19
|
+
readonly critical: boolean;
|
|
20
|
+
readonly route: string;
|
|
21
|
+
readonly appRoutePath: string;
|
|
22
|
+
readonly screenViewPath: string;
|
|
23
|
+
readonly screenViewExport: string;
|
|
24
|
+
readonly approvalEvidenceIds: readonly string[];
|
|
25
|
+
readonly states: readonly string[];
|
|
26
|
+
readonly viewports: readonly string[];
|
|
27
|
+
}
|
|
28
|
+
export declare function deriveUxWriteSet(projectRoot: string, targetEpicId: string): UxWriteSetV1;
|
|
29
|
+
/** Clone-only projection; admission still verifies every effect before applying it to root. */
|
|
30
|
+
export declare function deriveUxCandidateWriteSet(projectRoot: string, targetEpicId: string): UxWriteSetV1;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{execFileSync as I}from"node:child_process";import{createHash as D}from"node:crypto";import{lstatSync as w,readFileSync as b,realpathSync as H}from"node:fs";import{join as h,resolve as k}from"node:path";const d="docs/architecture/ux-execution-authority.json",C=256*1024,x=16*1024*1024,N=48,O=["loading","empty","error","success","permission","recovery"],A=["mobile","tablet","desktop"],v=/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/,W=/^[A-Za-z0-9._@/-]{1,240}$/;function c(){throw new Error("private-execution-ux-write-set-unverified")}function l(t){return D("sha256").update(t).digest("hex")}function z(t){return t!==null&&typeof t=="object"&&!Array.isArray(t)&&Object.getPrototypeOf(t)===Object.prototype}function P(t,r){return Object.keys(t).length===r.length&&r.every(n=>Object.hasOwn(t,n))}function $(t){return typeof t=="string"&&W.test(t)&&t.split("/").every(r=>r!==""&&r!=="."&&r!==".."&&r!==".git")}function j(t,r){return $(t)&&t.endsWith(".tsx")&&(r?/^(?:src\/(?:routes|app|pages)\/|app\/|pages\/)/.test(t):t.startsWith("src/")||t.startsWith("app/"))}function _(t,r){return Array.isArray(t)&&t.length===r.length&&t.every(n=>typeof n=="string")&&new Set(t).size===r.length&&r.every(n=>t.includes(n))}function R(t,r){return Array.isArray(t)&&t.length>0&&t.length<=r.length&&t.every(n=>typeof n=="string"&&r.includes(n))&&new Set(t).size===t.length}function T(t,r){let n=t;const s=r.split("/");for(let a=0;a<s.length;a++){n=h(n,s[a]);try{const p=w(n);if((p.isSymbolicLink()||(a===s.length-1?!p.isFile()||p.size>x:!p.isDirectory()))&&c(),a===s.length-1)return!0}catch(p){if(p.code==="ENOENT")return!1;c()}}return!1}function V(t,r){try{const n=I("git",["ls-tree","HEAD","--",r],{cwd:t,encoding:"utf8",timeout:1e4,maxBuffer:1024});if(!n)return null;const s=/^([0-7]{6}) blob [a-f0-9]{40,64}\t(.+)\n$/.exec(n);return(!s||s[1]!=="100644"&&s[1]!=="100755"||s[2]!==r)&&c(),I("git",["show",`HEAD:${r}`],{cwd:t,timeout:1e4,maxBuffer:x+1})}catch{c()}}function U(t,r,n){/^P[1-9][0-9]{0,11}$/.test(r)||c();const s=k(t);try{(H(s)!==s||!w(s).isDirectory()||!T(s,d))&&c()}catch{c()}const a=b(h(s,d)),p=V(s,d);(!p||a.length<1||a.length>C||!a.equals(p))&&c();let i;try{i=JSON.parse(new TextDecoder("utf-8",{fatal:!0}).decode(a)),`${JSON.stringify(i,null,2)}
|
|
2
|
+
`!==a.toString("utf8")&&c()}catch{c()}(!z(i)||!P(i,["schemaVersion","contractId","targetEpicId","adapterId","screens"])||i.schemaVersion!==1||i.contractId!=="P201-UX-STORYBOOK-01"||i.targetEpicId!==r||!["storybook-react-vite","storybook-nextjs-vite"].includes(String(i.adapterId))||!Array.isArray(i.screens)||i.screens.length<1||i.screens.length>N)&&c();const u=["docs/architecture/ux-design.md","docs/architecture/storybook-ux-contract.md","docs/architecture/ux-coverage-manifest.json"],y=new Set,g=new Set,S=[];for(const e of i.screens){(!z(e)||!P(e,["screenId","critical","route","appRoutePath","screenViewPath","screenViewExport","approvalStatus","approvalEvidenceIds","states","viewports"])||typeof e.screenId!="string"||!v.test(e.screenId)||typeof e.critical!="boolean"||typeof e.route!="string"||!/^\/[A-Za-z0-9._~!$&'()*+,;=:@%/-]*$/.test(e.route)||!j(e.appRoutePath,!0)||!j(e.screenViewPath,!1)||typeof e.screenViewExport!="string"||!/^[A-Za-z_$][A-Za-z0-9_$]{0,127}$/.test(e.screenViewExport)||e.approvalStatus!=="approved"||!Array.isArray(e.approvalEvidenceIds)||e.approvalEvidenceIds.length<1||e.approvalEvidenceIds.length>16||e.approvalEvidenceIds.some(f=>typeof f!="string"||!v.test(f))||new Set(e.approvalEvidenceIds).size!==e.approvalEvidenceIds.length||!(e.critical?_(e.states,O):R(e.states,O))||!(e.critical?_(e.viewports,A):R(e.viewports,A))||g.has(e.screenId)||y.has(e.route))&&c(),g.add(e.screenId),y.add(e.route);const o=e.screenId.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,64);o||c(),S.push(Object.freeze({screenId:e.screenId,critical:e.critical,route:e.route,appRoutePath:e.appRoutePath,screenViewPath:e.screenViewPath,screenViewExport:e.screenViewExport,approvalEvidenceIds:Object.freeze([...e.approvalEvidenceIds]),states:Object.freeze([...e.states]),viewports:Object.freeze([...e.viewports])})),u.push(e.screenViewPath,e.appRoutePath,`src/screens/${o}/${o}.fixtures.ts`,`src/screens/${o}/${o}.handlers.ts`,`src/screens/${o}/${o}.stories.tsx`,`src/screens/${o}/${o}.mdx`)}(u.some(e=>!$(e))||new Set(u.map(e=>e.toLowerCase())).size!==u.length)&&c(),u.sort();const E=Object.create(null);for(const e of u){const o=V(s,e),f=T(s,e);!n&&f!==(o!==null)&&c();const m=f?b(h(s,e)):null;!n&&m&&(!o||!m.equals(o))&&c(),E[e]=o?l(o):null}return Object.freeze({schemaVersion:1,authorityPath:d,targetEpicId:r,adapterId:i.adapterId,authoritySha256:l(a),pathsSha256:l(JSON.stringify(u)),screenCount:i.screens.length,pathCount:u.length,paths:Object.freeze(u),screens:Object.freeze(S),baselineSha256:Object.freeze(E)})}function F(t,r){return U(t,r,!1)}function J(t,r){return U(t,r,!0)}export{d as UX_WRITE_AUTHORITY_PATH,J as deriveUxCandidateWriteSet,F as deriveUxWriteSet};
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
*
|
|
16
16
|
* Resolves pipeline assets from the IP Protection Server via HTTP API.
|
|
17
17
|
* Used in production mode when core/ directory is not available locally.
|
|
18
|
-
* Supports retry with exponential backoff
|
|
18
|
+
* Supports retry with exponential backoff and timeouts. Protected asset
|
|
19
|
+
* content never falls back to an older response after a failed fetch.
|
|
19
20
|
*/
|
|
20
21
|
import type { AssetResolver } from './asset-resolver.js';
|
|
21
22
|
import { ResolverMode, type AssembledPrompt, type PipelineContext, type RemoteResolverOptions, type SkillContent, type StandardContent, type StepContent, type StepRegistry } from '../types/index.js';
|
|
@@ -31,16 +32,10 @@ export declare class RemoteResolver implements AssetResolver {
|
|
|
31
32
|
private readonly timeout;
|
|
32
33
|
private readonly retryCount;
|
|
33
34
|
/**
|
|
34
|
-
* Persistent cache -- used for registry only
|
|
35
|
-
*
|
|
36
|
-
* `assetCache` and only in process memory).
|
|
35
|
+
* Persistent cache -- used for registry metadata only, never protected
|
|
36
|
+
* step/skill/standard content.
|
|
37
37
|
*/
|
|
38
38
|
private readonly cache;
|
|
39
|
-
/**
|
|
40
|
-
* In-memory LRU cache for asset content (Epic P70.06).
|
|
41
|
-
* Volatile: discarded on process exit. Never persisted to disk.
|
|
42
|
-
*/
|
|
43
|
-
private readonly assetCache;
|
|
44
39
|
private readonly licenseClient;
|
|
45
40
|
constructor(options: RemoteResolverOptions);
|
|
46
41
|
resolveStep(stepId: string, _context: PipelineContext): Promise<StepContent>;
|
|
@@ -69,21 +64,9 @@ export declare class RemoteResolver implements AssetResolver {
|
|
|
69
64
|
* on failure. On successful HTTP response, update the cache.
|
|
70
65
|
*
|
|
71
66
|
* P70.06: ONLY the registry uses this path. Asset content (step/skill/
|
|
72
|
-
* standard)
|
|
67
|
+
* standard) is fetched live and never touches disk or a fallback cache.
|
|
73
68
|
*/
|
|
74
69
|
private fetchWithCacheFallback;
|
|
75
|
-
/**
|
|
76
|
-
* P70.06: fetch with VOLATILE in-memory cache fallback.
|
|
77
|
-
*
|
|
78
|
-
* Identical structure to {@link fetchWithCacheFallback} but the backing
|
|
79
|
-
* store is {@link InMemoryAssetCache} -- entries live only in the running
|
|
80
|
-
* process memory, never written to disk.
|
|
81
|
-
*
|
|
82
|
-
* When the server is unreachable and the in-memory cache is empty (e.g.
|
|
83
|
-
* first invocation of a fresh CLI process), the original error is
|
|
84
|
-
* re-thrown instead of silently degrading to a stale disk cache.
|
|
85
|
-
*/
|
|
86
|
-
private fetchWithInMemoryCache;
|
|
87
70
|
/**
|
|
88
71
|
* Sleep for the specified duration.
|
|
89
72
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{ResolverMode as
|
|
1
|
+
import{ResolverMode as u,NoOpCache as d}from"../types/index.js";import{CLIENT_VERSION as m}from"../version.js";const y=3e4,f=3,p=1e3,C=1e4;class i extends Error{statusCode;endpoint;constructor(t,e,r){super(t),this.statusCode=e,this.endpoint=r,this.name="RemoteResolverError"}}class v{mode=u.REMOTE;serverUrl;licenseKey;timeout;retryCount;cache;licenseClient;constructor(t){this.serverUrl=t.serverUrl.replace(/\/+$/,""),this.licenseKey=t.licenseKey,this.timeout=t.timeout??y,this.retryCount=t.retryCount??f,this.cache=t.cacheProvider??new d,this.licenseClient=t.licenseClient??null}async resolveStep(t,e){return this.fetchWithRetry({method:"GET",path:`/api/v1/steps/${encodeURIComponent(t)}`})}async resolveSkill(t,e){return this.fetchWithRetry({method:"GET",path:`/api/v1/skills/${encodeURIComponent(t)}`})}async resolveStandard(t){return this.fetchWithRetry({method:"GET",path:`/api/v1/standards/${encodeURIComponent(t)}`})}async resolveRegistry(){return this.fetchWithCacheFallback("registry",{method:"GET",path:"/api/v1/registry"})}async assemblePrompt(t,e){return this.fetchWithRetry({method:"POST",path:"/api/v1/prompts/assemble",body:{stepId:t,context:e}})}async isAvailable(){try{const t=new AbortController,e=setTimeout(()=>t.abort(),5e3),r=await fetch(`${this.serverUrl}/api/v1/health`,{method:"GET",headers:await this.buildHeaders(),signal:t.signal});return clearTimeout(e),r.ok}catch{return!1}}async dispose(){await this.cache.clear()}async buildHeaders(){const t={"Content-Type":"application/json","X-Client-Version":m};if(this.licenseClient){const e=await this.licenseClient.getToken();e&&(t.Authorization=`Bearer ${e}`)}return t}async fetchWithRetry(t){let e,r=!1;for(let a=0;a<=this.retryCount;a++)try{const o=new AbortController,n=setTimeout(()=>o.abort(),this.timeout),c=`${this.serverUrl}${t.path}`,h={method:t.method,headers:await this.buildHeaders(),signal:o.signal};t.body&&(h.body=JSON.stringify(t.body));const s=await fetch(c,h);if(clearTimeout(n),!s.ok){if(s.status===401&&!r&&this.licenseClient){if(r=!0,await this.licenseClient.forceRefresh()){a--;continue}throw new i(`API error: ${s.status}`,s.status)}throw s.status>=400&&s.status<500&&s.status!==429?new i(`API error: ${s.status}`,s.status):new i(`Server error: ${s.status}`,s.status)}return await s.json()}catch(o){if(e=o instanceof i?o:new i("Request failed"),o instanceof i&&o.statusCode&&o.statusCode>=400&&o.statusCode<500&&o.statusCode!==429)throw o;if(a<this.retryCount){const n=Math.min(p*Math.pow(2,a),C),c=Math.random()*n*.25;await this.sleep(n+c)}}throw e??new i("All retry attempts failed")}async fetchWithCacheFallback(t,e){try{const r=await this.fetchWithRetry(e);return this.cache.set(t,JSON.stringify(r)).catch(()=>{}),r}catch(r){const a=await this.cache.get(t);if(a!==null)try{return JSON.parse(a)}catch{}throw r}}sleep(t){return new Promise(e=>setTimeout(e,t))}}export{v as RemoteResolver,i as RemoteResolverError};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createHash as
|
|
1
|
+
import{createHash as h}from"node:crypto";import{isAbsolute as f,resolve as _}from"node:path";const R=12,y=24,s="project",A="workspace",a=/(?:@|ghp_[A-Za-z0-9_]+|sk-[A-Za-z0-9_-]+|password|passwd|secret|token|api[_-]?key|license[_-]?key|private[_-]?key|credential|customer[_-]?pii|\bpii\b)/i;function B(e){const t=typeof e=="string"?e:"",n=T(t)??_(t||"."),r=g(n),o=g(t||"."),i=I(z(o),s),p=m(i,s,r),c=$(o),l=C(r),d=c?I(c,A):i,L=c&&l?m(d,A,l):p;return{projectId:p,projectLabel:i,workspaceId:L,workspaceLabel:d,publicSafe:!0}}function g(e){let t=e.replace(/\\/g,"/").replace(/\/+$/g,"");return t||(t="/"),t=t.replace(/^([A-Z]):\//,(n,r)=>`${r.toLowerCase()}:/`),t}function z(e){const t=u(e);return t[t.length-1]??s}function b(e){return e.normalize("NFKD").replace(/[\u0300-\u036f]/g,"").toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,y).replace(/-+$/g,"")||s}function m(e,t,n){const r=E(n),o=`${e}-${r}`;return a.test(o)?`${t}-${r}`:o}function E(e){return h("sha256").update(e).digest("hex").slice(0,R)}function I(e,t){if(a.test(e))return t;const n=b(e);return a.test(n)?t:n}function $(e){const t=u(e);if(t.length<2)return;const n=t[t.length-2];if(!/^[A-Za-z]:$/.test(n))return n}function C(e){const t=u(e);if(t.length<2||/^[A-Za-z]:$/.test(t[t.length-2]??""))return;const n=t.slice(0,-1);return/^[A-Za-z]:$/.test(n[0]??"")?n.join("/"):e.startsWith("/")?`/${n.join("/")}`:n.join("/")}function u(e){return e.split("/").filter(Boolean)}function T(e){if(!e||/[\r\n\0]/.test(e))return null;const t=f(e)?e:_(e);return f(t)?t:null}export{R as PROJECT_IDENTITY_HASH_CHARS,y as PROJECT_IDENTITY_LABEL_CHARS,z as basenameForIdentity,B as deriveProjectIdentity,g as normalizeIdentityRoot,b as sanitizeProjectBasename};
|
|
@@ -11,8 +11,10 @@ export declare const SCHEDULER_REASON_CODES: Readonly<{
|
|
|
11
11
|
readonly OK: "continuity-ok";
|
|
12
12
|
readonly SCHEDULER_INSTALL_UNSAFE: "scheduler-install-unsafe";
|
|
13
13
|
readonly PRIMARY_USER_SCHEDULER_UNAVAILABLE: "primary-user-scheduler-unavailable";
|
|
14
|
+
readonly SYSTEMD_USER_BUS_UNAVAILABLE: "systemd-user-bus-unavailable";
|
|
14
15
|
readonly CRON_FALLBACK_SELECTED: "cron-fallback-selected";
|
|
15
16
|
readonly CRON_FALLBACK_NOT_ALLOWED: "cron-fallback-primary-scheduler-available";
|
|
17
|
+
readonly CRON_FALLBACK_NOT_PERMITTED: "cron-fallback-not-permitted";
|
|
16
18
|
readonly SCHEDULER_UNAVAILABLE: "scheduler-unavailable";
|
|
17
19
|
readonly SCHEDULER_COMMAND_FAILED: "scheduler-command-failed";
|
|
18
20
|
readonly RUNNER_BINARY_UNTRUSTED: "runner-binary-untrusted";
|
|
@@ -121,6 +123,8 @@ export interface RunnerSchedulerCommandOptions {
|
|
|
121
123
|
readonly trustCheck?: boolean;
|
|
122
124
|
readonly uid?: number | null;
|
|
123
125
|
readonly commandAvailable?: (command: string) => boolean;
|
|
126
|
+
/** Injectable read-only probe; synthetic callers must not use the host bus. */
|
|
127
|
+
readonly userBusProbe?: (env: NodeJS.ProcessEnv) => boolean;
|
|
124
128
|
readonly commandRunner?: (command: SchedulerControlCommand) => SchedulerCommandRunResult;
|
|
125
129
|
readonly trustChecker?: (executablePath: string | undefined) => SchedulerBinaryTrustResult;
|
|
126
130
|
readonly binaryResolver?: (options: RunnerBinaryResolveOptions) => RunnerBinaryResolution;
|
|
@@ -145,6 +149,21 @@ export interface RunnerSchedulerCommandResult {
|
|
|
145
149
|
readonly applied: boolean;
|
|
146
150
|
readonly publicSafe: true;
|
|
147
151
|
}
|
|
152
|
+
export interface RunnerOperationalStatusV1 {
|
|
153
|
+
readonly schemaVersion: 1;
|
|
154
|
+
readonly command: 'status' | 'doctor';
|
|
155
|
+
readonly projectKey: string;
|
|
156
|
+
readonly platform: NodeJS.Platform;
|
|
157
|
+
readonly primaryStrategy?: SchedulerStrategy;
|
|
158
|
+
readonly selectedStrategy?: SchedulerStrategy;
|
|
159
|
+
readonly operational: boolean;
|
|
160
|
+
readonly status: 'active' | 'inactive' | 'available' | 'unavailable';
|
|
161
|
+
readonly reasonCodes: readonly string[];
|
|
162
|
+
readonly observedAt: string;
|
|
163
|
+
readonly publicSafe: true;
|
|
164
|
+
}
|
|
165
|
+
/** Request-only observation: never exports subprocess output or scheduler paths. */
|
|
166
|
+
export declare function observeRunnerOperationalStatus(command: 'status' | 'doctor', options?: Omit<RunnerSchedulerCommandOptions, 'command'>): Promise<RunnerOperationalStatusV1>;
|
|
148
167
|
export type RunnerSchedulerBootstrapOptions = Omit<RunnerSchedulerCommandOptions, 'command'>;
|
|
149
168
|
export interface RunnerSchedulerBootstrapResult {
|
|
150
169
|
readonly ok: boolean;
|
|
@@ -169,6 +188,7 @@ export declare function detectSchedulerCapability(input?: {
|
|
|
169
188
|
readonly allowCronFallback?: boolean;
|
|
170
189
|
readonly uid?: number | null;
|
|
171
190
|
readonly commandAvailable?: (command: string) => boolean;
|
|
191
|
+
readonly userBusProbe?: (env: NodeJS.ProcessEnv) => boolean;
|
|
172
192
|
}): SchedulerCapabilityReport;
|
|
173
193
|
export declare function renderSchedulerManifests(input: {
|
|
174
194
|
readonly strategy: SchedulerStrategy;
|