/** * Canon store scaffold assets — written by `cortex canon init`. * * CANON_VERIFY_MJS and the workflow are the store's PROVEN A4 trust layer, * embedded verbatim (the verify script's own header declares this graduation: * "Staging form: graduates to the published library at Phase C"). Contents are * JSON-encoded at generation time — do not hand-edit the string constants; * regenerate from the canon store if the lint evolves. */ /** .gitattributes — append-only JSONL merges cleanly across writers. */ export declare const GITATTRIBUTES = "*.jsonl merge=union\n"; /** .github/workflows/canon-verify.yml — changed-files lint, never writes. */ export declare const VERIFY_YML = "# canon-verify \u2014 Phase A4 trust layer (CANON_CROSS_HARNESS_PLAN.md 27e).\n# CI does ONLY what only CI should do: verification lint on changed files.\n# Path-filtered + concurrency-cancelled; common-path minutes stay near zero.\n# The translate/projection runtime is LOCAL-FIRST (canon-sync/canon-translate);\n# this workflow never writes to the repo, so there is no Actions loop to guard.\nname: canon-verify\n\non:\n push:\n branches: [main]\n paths:\n - 'native/**'\n - 'canon/**'\n - 'projections/**'\n - 'memory/**'\n - 'projects/**'\n - 'plans/**'\n - 'skills/**'\n - 'agents/**'\n - 'mcp/**'\n - 'plugins/**'\n - '.github/**'\n pull_request:\n paths:\n - 'native/**'\n - 'canon/**'\n - 'projections/**'\n - 'memory/**'\n - 'projects/**'\n - 'plans/**'\n - 'skills/**'\n - 'agents/**'\n - 'mcp/**'\n - 'plugins/**'\n - '.github/**'\n workflow_dispatch: {}\n\n# Per-SHA group: dedupes re-deliveries WITHOUT cancelling coverage. A per-ref\n# cancel-in-progress group was tried first and left a real verification gap \u2014\n# back-to-back sync+translate pushes cancelled the sync push's run, so its\n# changed files were never linted. Runs cost ~30s; coverage wins.\nconcurrency:\n group: canon-verify-${{ github.ref }}-${{ github.sha }}\n cancel-in-progress: true\n\njobs:\n verify:\n runs-on: ubuntu-latest\n timeout-minutes: 10\n steps:\n - uses: actions/checkout@v4\n with:\n fetch-depth: 0\n\n - name: Determine diff range\n id: range\n run: |\n if [ \"${{ github.event_name }}\" = \"pull_request\" ]; then\n base=\"${{ github.event.pull_request.base.sha }}\"\n elif [ -n \"${{ github.event.before }}\" ] && [ \"${{ github.event.before }}\" != \"0000000000000000000000000000000000000000\" ]; then\n base=\"${{ github.event.before }}\"\n else\n # first push / manual dispatch: lint the last commit only\n base=\"$(git rev-parse HEAD~1 2>/dev/null || git hash-object -t tree /dev/null)\"\n fi\n echo \"base=$base\" >> \"$GITHUB_OUTPUT\"\n\n - name: Verify changed files\n run: node .github/scripts/canon-verify.mjs \"${{ steps.range.outputs.base }}\" HEAD\n"; /** .github/scripts/canon-verify.mjs — dependency-free verification lint. */ export declare const CANON_VERIFY_MJS = "/**\n * canon-verify \u2014 Phase A4 trust layer (CANON_CROSS_HARNESS_PLAN.md 27e).\n *\n * Verifies ONLY the files changed in a push/PR range. Four checks:\n * 1. JSONL integrity \u2014 every line of every changed *.jsonl / *.jsonl.part-NNNN\n * parses as JSON (parts are split at line boundaries by construction).\n * 2. Canon schema \u2014 records in /canon (excluding *.events.jsonl sidecars)\n * carry uuid, timestamp, type, provenance.\n * 3. Secret scan \u2014 the same pattern set the sync scrub applies (minus blanket\n * hex64); any hit here means content bypassed the scrub choke-point.\n * 4. Projection refs \u2014 every changed *.ref resolves to an existing canon file\n * (single form or .part-0000 chunked form).\n *\n * Failures are grouped, annotated (::error), and fatal \u2014 never silent (D8).\n * Staging form: graduates to the published library (`cortex canon verify`)\n * at Phase C; keep this dependency-free (bare node:*, no npm install).\n *\n * Usage: node canon-verify.mjs \n */\nimport { execFileSync } from 'node:child_process';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nconst [base, head] = process.argv.slice(2);\nif (!base || !head) { console.error('usage: canon-verify.mjs '); process.exit(2); }\n\nconst git = (a) => execFileSync('git', a, { encoding: 'utf8', maxBuffer: 256 * 1024 * 1024 });\n\nconst SECRET_PATTERNS = [\n ['sk', /sk-[A-Za-z0-9_-]{16,}/],\n ['ghp', /ghp_[A-Za-z0-9]{20,}/],\n ['ghpat', /github_pat_[A-Za-z0-9_]{20,}/],\n ['ghu', /ghu_[A-Za-z0-9]{20,}/],\n ['ghs', /ghs_[A-Za-z0-9]{20,}/],\n ['gho', /gho_[A-Za-z0-9]{20,}/],\n ['ghr', /ghr_[A-Za-z0-9]{20,}/],\n ['hf', /hf_[A-Za-z0-9]{20,}/],\n ['aiza', /AIza[A-Za-z0-9_-]{20,}/],\n ['xai', /xai-[A-Za-z0-9]{20,}/],\n ['nar', /nar_[A-Za-z0-9]{16,}/],\n ['gsk', /gsk_[A-Za-z0-9]{20,}/],\n ['slack', /xox[bpars]-[A-Za-z0-9-]{10,}/],\n ['akia', /AKIA[A-Z0-9]{16}/],\n ['jwt', /eyJ[A-Za-z0-9_-]{20,}\\.eyJ[A-Za-z0-9_-]{20,}\\.[A-Za-z0-9_-]{10,}/],\n];\n\nconst changed = git(['diff', '--name-only', '--diff-filter=ACMR', base, head])\n .split('\\n').map((s) => s.trim()).filter(Boolean);\n\nconst isJsonl = (f) => /\\.jsonl(\\.part-\\d{4})?$/.test(f);\nconst isCanonRecordFile = (f) => f.startsWith('canon/') && isJsonl(f) && !/\\.events\\.jsonl$/.test(f);\nconst isRef = (f) => f.endsWith('.ref');\n\nconst failures = [];\nconst fail = (file, msg) => {\n failures.push(`${file} \u2014 ${msg}`);\n console.log(`::error file=${file}::${msg}`);\n};\n\nlet checkedFiles = 0, checkedLines = 0;\nfor (const f of changed) {\n if (!fs.existsSync(f)) continue; // renamed-away edge\n const isText = isJsonl(f) || isRef(f) || f.endsWith('.md') || f.endsWith('.json');\n if (!isText) continue;\n const content = fs.readFileSync(f, 'utf8');\n checkedFiles++;\n\n // 3. secret scan (report pattern label only \u2014 NEVER echo the match)\n for (const [label, re] of SECRET_PATTERNS) {\n if (re.test(content)) fail(f, `secret-pattern hit (${label}) \u2014 content bypassed the sync scrub`);\n }\n\n // 1 + 2. JSONL parse + canon schema + tool-pairing invariants.\n // Pairing (Phase B lesson \u2014 defects here surface as provider 400s at resume\n // time): every tool_result must follow a seen tool_use (orphans are the\n // translator's job to repair), and only TAIL tool_use may go unanswered\n // (live session snapshotted mid-call). Pairing is skipped on .part-NNNN\n // chunks \u2014 pairs legitimately span part boundaries.\n if (isJsonl(f)) {\n const schema = isCanonRecordFile(f);\n const checkPairs = schema && !/\\.part-\\d{4}$/.test(f);\n const uses = new Map(); // id -> line of tool_use\n const answered = new Set();\n const uuids = new Set();\n let lastRecordLine = 0;\n let n = 0;\n for (const line of content.split('\\n')) {\n n++;\n if (!line.trim()) continue;\n checkedLines++;\n let rec;\n try { rec = JSON.parse(line); }\n catch { fail(f, `line ${n}: unparseable JSON`); continue; }\n if (schema) {\n for (const field of ['uuid', 'timestamp', 'type', 'provenance']) {\n if (rec[field] === undefined) fail(f, `line ${n}: canon record missing '${field}'`);\n }\n }\n if (checkPairs) {\n lastRecordLine = n;\n // Chain integrity: parentUuid must resolve within the canonical line\n // (append-only \u21D2 parents precede children). A dangling parent means a\n // sidecarred/dropped record broke the chain \u2014 Claude Code's renderer\n // silently EXCLUDES everything upstream of a break (a3.10 lesson: a\n // 555KB doc vanished from rendered context).\n if (rec.uuid) uuids.add(rec.uuid);\n if (typeof rec.parentUuid === 'string' && !uuids.has(rec.parentUuid)) {\n fail(f, `line ${n}: parentUuid ${rec.parentUuid} not found earlier in file \u2014 chain break (renderer will drop upstream context)`);\n }\n const blocks = Array.isArray(rec.message?.content) ? rec.message.content : [];\n for (const b of blocks) {\n if (b?.type === 'tool_use' && b.id) uses.set(b.id, n);\n if (b?.type === 'tool_result' && b.tool_use_id) {\n if (!uses.has(b.tool_use_id)) fail(f, `line ${n}: orphan tool_result (${b.tool_use_id}) \u2014 no prior tool_use; translator repair missing`);\n answered.add(b.tool_use_id);\n }\n }\n }\n }\n if (checkPairs) {\n for (const [id, line] of uses) {\n if (!answered.has(id) && lastRecordLine - line > 3) {\n fail(f, `line ${line}: unanswered tool_use (${id}) mid-file \u2014 only tail in-flight calls may dangle`);\n }\n }\n }\n }\n\n // 5. artifact-manifest schema (canon/artifacts/**.json \u2014 \u00A727p second record kind)\n if (/^canon\\/artifacts\\/.+\\.json$/.test(f) && !f.endsWith('ARTIFACTS.md')) {\n try {\n const rec = JSON.parse(content);\n for (const field of ['recordKind', 'kind', 'id', 'version', 'timestamp', 'primary', 'content', 'provenance']) {\n if (rec[field] === undefined) fail(f, `artifact manifest missing '${field}'`);\n }\n if (rec.recordKind !== 'artifact-manifest') fail(f, `recordKind must be 'artifact-manifest'`);\n if (!Array.isArray(rec.content) || rec.content.some((c) => !c.path || !c.ref)) fail(f, 'content entries need path+ref');\n } catch { fail(f, 'artifact manifest: unparseable JSON'); }\n }\n\n // 4. projection refs resolve\n if (isRef(f)) {\n const target = content.trim();\n if (!target || target.includes('\\n')) fail(f, 'ref must be a single store-relative path');\n else if (!fs.existsSync(target) && !fs.existsSync(`${target}.part-0000`)) {\n fail(f, `ref target not found: ${target}`);\n }\n }\n}\n\nif (failures.length) {\n console.log(`[FAIL] canon-verify: ${failures.length} problem(s) in ${checkedFiles} changed file(s)`);\n process.exit(1);\n}\nconsole.log(`[OK] canon-verify: ${checkedFiles} changed file(s), ${checkedLines} record line(s), 0 problems`);\n"; /** README.md — layout, CI, token-scoping, and privacy guidance. */ export declare const STORE_README = "# Canon store\n\nYour agent memory, in a git repo you own. Scaffolded by `cortex canon init`.\n\nThis repository is a **canonical agent history & memory store** (canon): sessions,\nmemory, plans, and capability artifacts from any harness, stored once in a\nprovider-neutral format and translated per consumer at the edges. Spec:\n[docs/CANON.md](https://github.com/Spitfire-Products/nexus-cortex/blob/main/docs/CANON.md)\nin the nexus-cortex repository.\n\n## Layout\n\n| Path | Owner | Carries |\n|---|---|---|\n| `/native//` | agents | each harness's own session dialect, verbatim (the only path an agent writes) |\n| `/canon/` | tooling | the canonical line \u2014 translated, append-only, provenance-anchored |\n| `/projections//` | tooling | canon fanned back out into each harness's dialect |\n| `/memory/` | agents | distilled memory files (index + detail) |\n| `/plans/`, `/projects/` | agents | plan docs + task state, project manifests \u2014 the intent layer |\n| `/skills/`, `/agents/`, `/mcp/`, `/plugins/` | agents | capability artifacts (harness-contract layer) |\n\n`*.jsonl` files use `merge=union` (see `.gitattributes`) so append-only concurrent\nappends auto-merge.\n\n## CI\n\n`.github/workflows/canon-verify.yml` lints changed files on every push/PR:\nJSONL integrity, canon record schema, tool-pairing/chain invariants, secret-pattern\nscan, projection-ref resolution. It never writes to the repo. Failures are grouped\nand fatal \u2014 never silent.\n\n\n## Configuration (the levers)\n\n| File | Controls |\n|---|---|\n| `HARNESSES.json` (store root) | Which harnesses `canon sync` captures: `{ \"harnesses\": { \"