arkgate 3.7.0 → 3.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/CHANGELOG.md +76 -1145
  2. package/README.md +59 -19
  3. package/bin/ark-check-runtime.mjs +1598 -0
  4. package/bin/ark-check.mjs +32 -1565
  5. package/bin/ark-mcp-runtime.mjs +1976 -0
  6. package/bin/ark-mcp.mjs +84 -1495
  7. package/bin/ark-shared.mjs +34 -38
  8. package/bin/ark.mjs +33 -66
  9. package/bin/lib/adapter-contract.mjs +161 -9
  10. package/bin/lib/agent-gates.mjs +1 -0
  11. package/bin/lib/analysis-completeness.mjs +28 -0
  12. package/bin/lib/analysis-engine.mjs +8 -8
  13. package/bin/lib/analysis-policy.mjs +27 -0
  14. package/bin/lib/architecture-scan.mjs +70 -357
  15. package/bin/lib/auto-patch.mjs +76 -8
  16. package/bin/lib/ci-and-commands.mjs +1 -1
  17. package/bin/lib/codex-home.mjs +43 -16
  18. package/bin/lib/design-delta.mjs +4 -0
  19. package/bin/lib/doctor-advisories.mjs +4 -3
  20. package/bin/lib/doctor-plan.mjs +40 -41
  21. package/bin/lib/enforcement-state.mjs +2 -0
  22. package/bin/lib/github-enforcement.mjs +443 -0
  23. package/bin/lib/hook-templates.mjs +12 -148
  24. package/bin/lib/html-report-advisories.mjs +1 -1
  25. package/bin/lib/html-report-depth.mjs +9 -0
  26. package/bin/lib/html-report.mjs +5 -5
  27. package/bin/lib/install-migrate.mjs +83 -79
  28. package/bin/lib/managed-upgrade.mjs +622 -0
  29. package/bin/lib/mcp-adoption.mjs +3 -1
  30. package/bin/lib/parse-health.mjs +6 -5
  31. package/bin/lib/port-proof.mjs +2 -2
  32. package/bin/lib/prepare-change.mjs +68 -38
  33. package/bin/lib/prepare-write.mjs +7 -1
  34. package/bin/lib/resident-doctor-client.mjs +55 -0
  35. package/bin/lib/resident-hook.mjs +247 -0
  36. package/bin/lib/resolved-candidate-facts.mjs +1160 -0
  37. package/bin/lib/scan-files.mjs +19 -6
  38. package/bin/lib/snippet-analysis.mjs +119 -0
  39. package/bin/lib/source-policy.mjs +24 -0
  40. package/bin/lib/typescript-host.mjs +15 -18
  41. package/bin/lib/unavailable-analysis.mjs +76 -0
  42. package/bin/lib/upgrade-command.mjs +115 -0
  43. package/bin/lib/weakest-link.mjs +21 -179
  44. package/bin/lib/write-path-capabilities.mjs +167 -16
  45. package/bin/lib/write-path-detect.mjs +3 -2
  46. package/dist/eslint/index.cjs +3 -3
  47. package/dist/eslint/index.d.ts +3 -0
  48. package/dist/eslint/index.js +3 -3
  49. package/dist/index.cjs +7 -7
  50. package/dist/index.d.ts +1073 -141
  51. package/dist/index.js +7 -7
  52. package/docs/agent-guide.md +103 -59
  53. package/docs/ai-gates.md +97 -16
  54. package/docs/demos/01-write-gate-self-correction.md +2 -2
  55. package/docs/enthusiast/README.md +10 -10
  56. package/docs/enthusiast/how-to-gallery-starter.md +2 -2
  57. package/docs/enthusiast/reference-commands.md +18 -1
  58. package/docs/enthusiast/tutorial-first-project.md +2 -2
  59. package/docs/package-surface.md +98 -12
  60. package/docs/typescript-support.md +109 -37
  61. package/package.json +32 -4
  62. package/schemas/ark.analysis-result.schema.json +159 -2
  63. package/schemas/ark.design-delta.schema.json +1 -0
  64. package/schemas/ark.enforcement-state.schema.json +84 -0
  65. package/schemas/ark.resolved-candidate-facts.schema.json +1 -0
  66. package/server.json +2 -2
  67. package/templates/skills/ark-explore.md +5 -5
  68. package/templates/skills/ark-fix.md +1 -1
  69. package/templates/skills/ark-runtime.md +15 -8
  70. package/templates/skills/ark-upgrade.md +122 -182
  71. package/bin/lib/ai-velocity.mjs +0 -293
  72. package/bin/lib/graph-cycles.mjs +0 -6
  73. package/bin/lib/safety-diagnostics.mjs +0 -284
  74. package/bin/lib/ts-resolve.mjs +0 -228
  75. package/dist/configTypes-DAPvBqK6.d.cts +0 -61
  76. package/dist/eslint/index.d.cts +0 -146
  77. package/dist/index.d.cts +0 -986
@@ -1,200 +1,140 @@
1
1
  ---
2
2
  name: ark-upgrade
3
- description: Update arkgate to the latest published version, then refresh gates and /ark-* skills for the active agent host (defer inactive hosts like Codex when not in use) and re-verify the architecture check. Autonomous.
3
+ description: Upgrade ArkGate through a content-identity preview, preserve customized files, and verify the active host and architecture contract.
4
4
  ---
5
5
 
6
- # /ark-upgrade — Update ArkGate and refresh its gates
6
+ # /ark-upgrade — managed ArkGate upgrade
7
7
 
8
- Update the `arkgate` dependency to the latest published version and
9
- bring the repo's generated artifacts and gates in line with it. This skill
10
- checks the registry itself don't assume the copy in `node_modules` is current.
8
+ Upgrade the published `arkgate` package and its managed gates without treating a
9
+ filename, package version, or similar-looking file as proof of ownership. The
10
+ preview is the source of truth: inspect it before applying anything.
11
11
 
12
- **Still on `ark-runtime-kernel`?** Migrate first (same product, new package name):
13
-
14
- ```bash
15
- npm uninstall ark-runtime-kernel && npm install -D arkgate
16
- npx arkgate-check --install-agent-gates --force
17
- ```
18
-
19
- Guide: `docs/migrate-from-ark-runtime-kernel.md` in the package (or on GitHub).
20
-
21
- **TypeScript 7 projects:** ArkGate falls back to a nested JS-API TypeScript when the
22
- project's `typescript` main export is version-only (TS 7.0). After upgrade, point users at
23
- `docs/typescript-support.md` if the gate or `ARK_DEBUG_TS=1` mentions fallback. Dual install
24
- (TS6 JS API + TS7 CLI) is optional for tooling that still needs classic `tsc` APIs.
25
-
26
- **MCP double-bin check (identity cutover):** after upgrade, open `.mcp.json` and
27
- `.cursor/mcp.json`. `args` must contain **exactly one** of `arkgate-mcp` / `ark-mcp`
28
- (prefer `arkgate-mcp`), never both. If both appear, run:
29
-
30
- ```
31
- npx arkgate-check --install-agent-gates --migrate-commands
32
- ```
33
-
34
- `ark upgrade` already runs migrate-commands; re-run it if an older 2.x left dual names.
35
-
36
- **Adoption completeness:** run `npx arkgate-check --doctor` (or `--doctor --json`) and
37
- read the **Adoption** section — host gaps, Codex home temp paths, optional-but-populated
38
- core layers, missing origin snapshot, baseline policy. Fix commands are printed per gap.
39
- HTML reports include the same Adoption card (separate from the 0–100 fitness score).
40
-
41
- **Active host vs deferred hosts:** green the **session host** (Grok, Claude, Cursor, …)
42
- and repo gates first. Codex home (`$CODEX_HOME` prompts + `config.toml` MCP multi-project)
43
- is **deferred** unless this session is Codex or the user asked to fix Codex. Doctor marks
44
- those gaps `deferred` / info and does not put them in Top actions. A temp/upgrade MCP
45
- `--root` stays urgent (fail-closed rewrite). Never set **Incomplete?** because of deferred
46
- Codex debt.
12
+ For greenfield onboarding, start with `/ark-architect` (or
13
+ `ark-check --recommend`); for a brownfield repository, use `/ark-adopt` before
14
+ this upgrade flow.
47
15
 
48
16
  ## Dual engine (mandatory)
49
17
 
50
- | Engine | Role |
51
- |--------|------|
52
- | **Deterministic** | CLI / MCP / contract sensors — exit codes, plan kinds, coverage numbers, install status |
53
- | **Exploratory** | You open **this** repo's real files and product surface before concluding |
54
-
55
- The CLI is a **sensor**, never the whole job. Claiming done without the exploratory bar for this skill is **incomplete**.
56
-
18
+ Use the semantic sensor (`ark-check --doctor --json` plus the strict contract
19
+ check) and direct inspection of every managed file the preview will change.
20
+ Neither signal replaces the other.
21
+
22
+ ## Safety contract
23
+
24
+ - `ark upgrade` is read-only. It reports the selected profile and hosts, every
25
+ managed asset, its content state, and the exact next command.
26
+ - The first `ark upgrade --apply` updates the dependency and lockfile, then runs
27
+ the newly installed CLI to produce another read-only preview. It does **not**
28
+ apply that preview's gate or skill changes.
29
+ - The post-update/no-install preview's `nextCommand` includes
30
+ `--plan-digest <sha256:…>` and applies only that exact candidate. A changed file
31
+ or selection invalidates the digest instead of being overwritten.
32
+ - Missing or conflicted assets previously recorded as managed require explicit
33
+ `--accept-conflicts`. Stop and obtain user consent before using it.
34
+ - Customized files are preserved. Unrelated source files and similar filenames
35
+ are never adopted. The command never writes a Codex home or another global
36
+ directory implicitly.
37
+ - Do not combine this flow with legacy `--force`, `--migrate-commands`, or
38
+ `--codex-home` repair commands. Diagnose any older adoption debt separately.
39
+
40
+ ## Read the states
41
+
42
+ | State | Meaning | Action |
43
+ |---|---|---|
44
+ | `current` | Content identity matches the candidate. | Record/adopt safely; metadata-only stamps may refresh. |
45
+ | `stale` | Recorded managed content still matches its old identity. | Safe candidate replacement. |
46
+ | `missing` | Candidate is absent. | Create if new; require consent if a recorded asset was deleted. |
47
+ | `customized` | User content diverged without a competing managed base. | Preserve it. |
48
+ | `conflicted` | Both managed base and user content diverged. | Preserve and require explicit consent. |
49
+ | `retired` | A recorded asset is no longer selected by the candidate. | Preserve its file and manifest identity; take no action. |
50
+
51
+ ## Procedure
52
+
53
+ 1. **Establish versions and context.** Read the installed
54
+ `node_modules/arkgate/package.json`, query `npm view arkgate version`, identify
55
+ the repository package manager, and open the intervening entries in the
56
+ shipped `CHANGELOG.md` (fall back to registry or release notes and name that
57
+ source). Do not infer “latest” from `node_modules` alone.
58
+
59
+ 2. **Preview managed content.** Run:
60
+
61
+ ```bash
62
+ ark upgrade --json
63
+ ```
64
+
65
+ Pass `--root <path>` and `--tools <active-host>` when selection would otherwise
66
+ be ambiguous. Open the reported files that matter to this repository. Confirm
67
+ that customized files remain non-applying and that any deletion/conflict is
68
+ blocked.
69
+
70
+ 3. **Update and re-preview.** If the registry is newer, run:
71
+
72
+ ```bash
73
+ ark upgrade --apply
74
+ ```
75
+
76
+ This updates through the detected package manager and hands control to the new
77
+ package for a fresh preview. Review that new preview; do not assume the old
78
+ candidate and new candidate are identical. If already on the latest package,
79
+ retain the current read-only preview.
80
+
81
+ For pnpm repositories with `minimumReleaseAge`, use the repository's existing
82
+ trusted first-party exception mechanism when the new release is still cooling
83
+ off, and prove `pnpm install --frozen-lockfile` succeeds.
84
+
85
+ 4. **Apply only the reviewed candidate.** When there are no blocked assets, run
86
+ the preview's exact `nextCommand`, whose shape is:
87
+
88
+ ```bash
89
+ ark upgrade --apply --no-install --plan-digest <preview-digest>
90
+ ```
91
+
92
+ If recorded deletion/conflict recovery is desired, ask first and then add
93
+ `--accept-conflicts`. Never add it merely to make the run green. Run a second
94
+ preview and require `summary.changed: 0`.
95
+
96
+ 5. **Verify enforcement and architecture.** Run `ark-check --doctor --json` and
97
+ the same fail-closed architecture command used by managed apply (normally
98
+ `ark-check --root . --config ark.config.json --strict-merge --json`). Require
99
+ `completeness: "complete"` and `ok: true`. Treat provider-unavailable CI
100
+ required-check evidence as `unverified`, never as proof that merges are
101
+ blocked. If new violations appear, hand off to `/ark-fix` for a small set or
102
+ `/ark-loop` / `/ark-autopilot` for residual debt; do not regenerate a baseline
103
+ without explicit approval.
104
+
105
+ ## Active host vs deferred hosts
106
+
107
+ **Active host:** its repo-local gate, skills, MCP/advisory surface, doctor evidence,
108
+ and strict check must be coherent before completion.
109
+
110
+ **Deferred hosts:** inactive hosts may remain untouched and must be named with a
111
+ future repair command when relevant. Deferred hosts never make Incomplete? `yes`
112
+ once the active host and shared repository surfaces are verified. A temporary
113
+ upgrade path or an actively selected host is not deferred.
114
+
115
+ The managed manifest retains the selected host set, so a later preview does not
116
+ silently switch to a different host. Home-level Codex setup is separate and is
117
+ never an implicit side effect of this skill.
57
118
 
58
119
  ## Subagent fan-out (optional, host-dependent)
59
120
 
60
- If the host supports **parallel subagents** and the task splits cleanly (e.g. multiple
61
- dirs to sample), fan out read-only scouts; otherwise **fall back to sequential**.
62
- Parent merges and still emits the **### Completion** contract. Never parallel-write
63
- the same files or weaken the gate.
64
-
65
- ## Fast path
66
-
67
- One command does the whole flow — update the package, refresh gates + `/ark-*` skills
68
- (and best-effort Codex home prompts when `~/.codex` exists), migrate command runners,
69
- and run the strict check:
70
-
71
- ```
72
- arkgate upgrade
73
- # (alias: ark upgrade)
74
- ```
75
-
76
- Use it when the user just wants the update done. Run the detailed steps below instead when
77
- you need to inspect the changelog first, handle a pnpm cooling-off window, or the one-liner
78
- reports a problem to triage. Always refresh skills so agents pick up new `mechanical-safe`
79
- kinds and TS guidance:
80
-
81
- ```
82
- npx arkgate-check --install-agent-gates --skills-only --force
83
- ```
84
-
85
- ## Steps
86
-
87
- 1. **Check the registry, then update.** Compare the installed version
88
- (`node_modules/arkgate/package.json`) against the latest published:
89
- `npm view arkgate version`. If a newer version exists, update it —
90
- `npm install -D arkgate@latest` (or the project's package manager:
91
- `pnpm add -D` / `yarn add -D`) — so the lockfile moves too; a pinned lockfile
92
- is exactly why "just re-run install" often stays on the old version. If the
93
- installed version already equals the latest, say so and still run steps 3-4
94
- (a prior version may have shipped skills/gates this repo never installed).
95
- Do NOT report "no update available" from the `node_modules` version alone —
96
- that reads stale.
97
- **pnpm cooling-off:** if the repo enforces a pnpm `minimumReleaseAge` and the new
98
- version was published inside that window (common for a freshly-cut release), a plain
99
- `pnpm add` in loose mode can leave a lockfile that `pnpm install --frozen-lockfile` (what
100
- CI runs) then REJECTS. Do it cleanly: add the exact `<pkg>@<version>` to
101
- `minimumReleaseAgeExclude` in `pnpm-workspace.yaml` FIRST, bump the dependency spec, then
102
- run a plain `pnpm install`, and verify with `pnpm install --frozen-lockfile` before moving
103
- on. Only exclude a first-party package you trust.
104
- 2. **Changelog triage** — read `node_modules/arkgate/CHANGELOG.md`
105
- (shipped in the package) for the versions between old and new, and pick out
106
- only entries that affect THIS repo (new flags, changed defaults, new gate
107
- templates, new skills). Summarize each in one sentence with what, if
108
- anything, the repo must do about it. If the file is absent (older releases
109
- didn't ship it), fall back to `npm view arkgate@<version> ...` or
110
- the GitHub release notes — say which source you used.
111
- 3. **Refresh templates** — run `ark-check --install-agent-gates`. Without
112
- `--force` it only writes missing files (new skills, new tool templates) and
113
- skips existing ones. To pick up NEW versions of the `/ark-*` skills that a
114
- package update shipped, run `ark-check --install-agent-gates --skills-only
115
- --force`: `--skills-only` scopes the overwrite to the canonical skills and
116
- leaves the gate files alone. Do NOT run a bare `--install-agent-gates --force`
117
- to refresh skills — it also overwrites `AGENTS.md` (often customized with the
118
- project's real layer table), `.claude/settings.json` (hooks/permissions), and
119
- `.github/workflows/ark-check.yml` (CI) with the generic templates, silently
120
- losing customizations. If the changelog says a GATE file changed, report the
121
- diff and let the user decide; never rewrite settings/CI/AGENTS.md without
122
- explicit approval.
123
- **Active host first.** Refresh skills for the host running this skill (e.g.
124
- `.grok/skills/`, `.claude/skills/`, `.cursor/commands/`). Repo-local copies for
125
- other detected hosts are fine to refresh in the same pass when cheap.
126
- **Codex is deferred when you are not on Codex.** Repo skills live in
127
- `.agents/skills/<name>/SKILL.md`; optional home skills in
128
- `$CODEX_HOME/skills/<name>/SKILL.md` (not legacy flat `$CODEX_HOME/prompts`).
129
- When **on Codex**, refresh **both** repo catalog and home if doctor reports
130
- missing/stale/legacy-prompts-only parity gaps. `ark upgrade` may best-effort
131
- refresh home when it exists; still list Codex under **Deferred hosts** when
132
- not on Codex and do **not** chase MCP multi-project / stale home skills until
133
- the user is on Codex (or asks). Fix when needed:
134
- `ark-check --install-agent-gates --skills-only --tools codex --force` and/or
135
- `ark-check --install-agent-gates --skills-only --codex-home --force`
136
- (and `--tools codex` / `--force` for primary MCP rebind). Exception: temp or
137
- `ark-upgrade` MCP `--root` paths — leave fail-closed rewrite to the CLI; do not
138
- block completion on multi-project noise.
139
- **Migrate stale command runners.** The package-manager-aware command templates
140
- (`pnpm exec` / `yarn` / `npx`) only apply to NEWLY written files, so a repo that adopted
141
- Ark before they shipped keeps a stale `npx` in its EXISTING gate files
142
- (`.claude/settings.json` hooks, `.mcp.json`, `AGENTS.md`, the `check:architecture` script).
143
- In a pnpm/yarn repo that means the write gate runs on a command the repo forbids. Run
144
- `ark-check --install-agent-gates --migrate-commands`: it rewrites ONLY the command runner
145
- in those files, preserving every customization (no `--force` clobber). A normal `ark-check`
146
- also flags this when it detects the mismatch.
147
- 4. **Re-verify** — `ark-check --root . --config ark.config.json
148
- --strict-config` (with `--baseline .ark-baseline.json` if present). A new
149
- version may detect violations the old one missed: if new violations appear,
150
- **STOP — do not continue this skill as complete.** **STOP — bulk residual debt: invoke /ark-loop or /ark-autopilot**
151
- (or `/ark-fix` for a small set). If they are too numerous to fix
152
- now, freezing them in the baseline (`--update-baseline`) is a valid stopgap
153
- but it silences NEW violations, so it requires explicit user approval first
154
- — never regenerate the baseline on your own to get a green check.
155
-
156
- ## Operating rules
157
-
158
- - **Must green:** the **active session host** (skills + gates that host uses) and
159
- shared repo surfaces (`.mcp.json` dual-bin, command runners, architecture check).
160
- - **May defer:** other hosts not used in this session. Always list them under
161
- **Deferred hosts** with the fix command — do not treat them as Incomplete.
162
- Codex home (global `$CODEX_HOME`) is the common case on Grok/Claude.
163
- - **Optional sync:** if other repo-local tool dirs already exist (`.cursor/`,
164
- `.claude/`, …), refreshing their `/ark-*` skills is good hygiene when cheap;
165
- it is not a reason to fail the skill when the active host is already current.
166
- - Never run `--force` blindly; customized files are the user's.
167
- - Stop only if the changelog documents a breaking config change with two valid
168
- migration paths — then present both with a recommendation.
169
-
170
- ## Related onboarding
171
-
172
- - After upgrade, re-run `ark-check --doctor` — `/ark-architect` and `ark-check --recommend` ship
173
- with the package for **greenfield** shape adoption.
174
- - **Brownfield** repos: point users to `/ark-adopt` and `docs/brownfield-adoption.md`, not
175
- `/ark-architect`. Demo: `docs/demos/02-brownfield-baseline-adoption.md`.
176
- - Refresh gates: `ark-check --install-agent-gates --force --skills-only` if skills are stale.
177
-
178
- ## Verify and report
179
-
180
- End with a passing check. Report: latest published version, old → new version
181
- (or "already latest"), changelog entries that mattered here (plain language),
182
- files written/refreshed for the **active host**, deferred hosts (if any),
183
- skipped customized files needing a manual look, and the final check status.
121
+ Parallelize independent preview, changelog, and enforcement checks when the
122
+ host supports isolated subagents; otherwise fall back to sequential execution.
184
123
 
185
124
  ## Completion contract (skill incomplete if missing)
186
125
 
187
- End with **exactly** these headings (markdown `###`):
126
+ Skill incomplete if missing any required verification or any field below.
127
+
128
+ End with exactly this structure:
188
129
 
189
130
  ### Completion
190
131
  - **Sensor:** commands/tools run
191
- - **Opened:** real paths read (or `n/a` only if pure install/upgrade with no source analysis)
192
- - **Active host:** e.g. `grok` / `claude` / `cursor` / `codex` (skills/gates OK or note)
193
- - **Deferred hosts:** `none` | e.g. `codex home MCP/prompts; fix when using Codex`
194
- - **Result:** one-line outcome
195
- - **Handoff:** `/ark-…` / CLI / `none`
196
- - **Incomplete?** `no` | `yes — <what is missing>`
197
-
198
- If a **STOP** handoff applies and you continued as if done, set **Incomplete?** to `yes`.
199
- **Deferred hosts (including Codex when not on Codex) never make Incomplete? yes.**
200
- **Skill incomplete if missing** any of the bullets above (use `none` for Deferred hosts when empty).
132
+ - **Opened:** real project and changelog paths read
133
+ - **Active host:** host and verified status
134
+ - **Deferred hosts:** `none` or host plus future action
135
+ - **Result:** old → new version and managed-upgrade outcome
136
+ - **Handoff:** `/ark-…`, CLI action, or `none`
137
+ - **Incomplete?** `no` or `yes — <missing work>`
138
+
139
+ If a required verification did not run or a conflict remains blocked, report the
140
+ task incomplete. Deferred hosts (including Codex when inactive) never make Incomplete? yes.
@@ -1,293 +0,0 @@
1
- /**
2
- * Q05 — AI-velocity evidence (fixture-measured, no live LLM).
3
- *
4
- * Same fixed feature scenario on two arms:
5
- * - design-weak: no golden norm → confused multi-path placement attempts
6
- * - golden-path: `.ark/golden-pattern.json` newCodeHome → first attempt correct home
7
- *
8
- * Metric: **placementTurns** (agent-equivalent steps until a DomainModel landing).
9
- * Golden must be strictly better (fewer turns). Gate is never weakened.
10
- */
11
- import fs from 'node:fs';
12
- import path from 'node:path';
13
- import { layerForFile } from '../ark-shared.mjs';
14
- import { loadGoldenPattern, summarizeGoldenPattern } from './golden-pattern.mjs';
15
- import {
16
- detectDesignSmells,
17
- buildPatternBetsFromSmells,
18
- summarizeDesignFitness,
19
- assertPatternBetsNeverMechanicalSafe,
20
- } from './design-smells.mjs';
21
- import { collectGovernedFiles } from './scan-files.mjs';
22
-
23
- /** Fixed feature prompt + pure-domain snippet (same on both arms). */
24
- export const FEATURE_SCENARIO = {
25
- id: 'add-pure-domain-canRefund',
26
- prompt:
27
- 'Add a pure domain rule canRefund(status: string): boolean for order refund eligibility. No I/O, no ORM.',
28
- fileName: 'canRefund.ts',
29
- source:
30
- "export function canRefund(status: string): boolean {\n return status === 'paid';\n}\n",
31
- /** Correct layer for this pure rule under the design-weak-enforce contract. */
32
- correctLayer: 'DomainModel',
33
- correctPath: 'src/domain/canRefund.ts',
34
- };
35
-
36
- /**
37
- * Agent-equivalent placement order when no golden guides new code (spaghetti confusion).
38
- * Wrong homes first (presentation / mixed), then domain — measured as friction.
39
- */
40
- export const DESIGN_WEAK_PLACEMENT_CANDIDATES = [
41
- 'src/features/orders/ui/canRefund.ts',
42
- 'src/routes/canRefund.ts',
43
- 'src/services/canRefund.ts',
44
- FEATURE_SCENARIO.correctPath,
45
- ];
46
-
47
- /**
48
- * @param {string} root
49
- * @param {object} config
50
- * @param {string} filePath relative
51
- */
52
- export function layerForPlacement(root, config, filePath) {
53
- const layers = config?.layers || [];
54
- if (!layers.length) return null;
55
- return layerForFile(root, filePath, layers) || null;
56
- }
57
-
58
- /**
59
- * A landing is successful when the path is under the correct layer for the scenario.
60
- * Pure snippet has no imports — we do not invent gate denials; cost is placement friction.
61
- *
62
- * @param {string} root
63
- * @param {object} config
64
- * @param {string} filePath
65
- */
66
- export function isCorrectLanding(root, config, filePath) {
67
- const layer = layerForPlacement(root, config, filePath);
68
- return layer === FEATURE_SCENARIO.correctLayer;
69
- }
70
-
71
- /**
72
- * Build ordered placement attempts for an arm.
73
- * Golden with newCodeHome → single first attempt under that home.
74
- * Absent golden → design-weak candidate ladder.
75
- *
76
- * Accepts loadGoldenPattern result or summarizeGoldenPattern summary.
77
- * @param {object | null | undefined} golden
78
- */
79
- export function placementAttemptsForArm(golden) {
80
- if (!golden || typeof golden !== 'object') {
81
- return { guidedByGolden: false, attempts: [...DESIGN_WEAK_PLACEMENT_CANDIDATES] };
82
- }
83
-
84
- const present = golden.present === true;
85
- const newCodeHome =
86
- (typeof golden.newCodeHome === 'string' && golden.newCodeHome) ||
87
- (typeof golden.golden?.newCodeHome === 'string' && golden.golden.newCodeHome) ||
88
- null;
89
-
90
- if (present && typeof newCodeHome === 'string' && newCodeHome.trim()) {
91
- const home = newCodeHome.replace(/\\/g, '/').replace(/\/?$/, '/');
92
- const rel = `${home}${FEATURE_SCENARIO.fileName}`.replace(/\/{2,}/g, '/');
93
- return {
94
- guidedByGolden: true,
95
- attempts: [rel.startsWith('src/') || rel.startsWith('.') ? rel : rel.replace(/^\//, '')],
96
- };
97
- }
98
-
99
- // Golden absent or no newCodeHome → full confused ladder (honest multi-layout friction).
100
- return {
101
- guidedByGolden: false,
102
- attempts: [...DESIGN_WEAK_PLACEMENT_CANDIDATES],
103
- };
104
- }
105
-
106
- /**
107
- * Measure placement turns for one arm (real layer resolution + golden load).
108
- *
109
- * @param {{
110
- * root: string,
111
- * config: object,
112
- * armId: string,
113
- * files?: string[],
114
- * }} opts
115
- */
116
- export function measureArmVelocity(opts) {
117
- const { root, config, armId } = opts;
118
- const goldenLoad = loadGoldenPattern(root);
119
- const goldenSummary = summarizeGoldenPattern(goldenLoad);
120
- const { guidedByGolden, attempts } = placementAttemptsForArm(goldenLoad);
121
-
122
- const steps = [];
123
- let placementTurns = 0;
124
- let landedPath = null;
125
- let landedLayer = null;
126
-
127
- for (const candidate of attempts) {
128
- placementTurns += 1;
129
- const layer = layerForPlacement(root, config, candidate);
130
- const ok = isCorrectLanding(root, config, candidate);
131
- steps.push({
132
- turn: placementTurns,
133
- path: candidate,
134
- layer,
135
- correctLanding: ok,
136
- });
137
- if (ok) {
138
- landedPath = candidate;
139
- landedLayer = layer;
140
- break;
141
- }
142
- }
143
-
144
- // Real design residual sensors (honesty — not used to invent ENFORCE).
145
- const files = opts.files ?? collectGovernedFiles(root, config);
146
- const smells = detectDesignSmells(root, config, files, {
147
- layersWithoutRules: [],
148
- emptyLayers: [],
149
- layers: [],
150
- });
151
- const patternBets = buildPatternBetsFromSmells(smells);
152
- const designFitness = summarizeDesignFitness(smells, {
153
- activeViolations: 0,
154
- governedPercent: 100,
155
- totalFiles: files.length,
156
- });
157
- const betsHonesty = assertPatternBetsNeverMechanicalSafe(patternBets);
158
-
159
- return {
160
- armId,
161
- scenarioId: FEATURE_SCENARIO.id,
162
- prompt: FEATURE_SCENARIO.prompt,
163
- guidedByGolden,
164
- goldenPattern: goldenSummary,
165
- placementTurns: landedPath ? placementTurns : attempts.length + 1,
166
- landed: Boolean(landedPath),
167
- landedPath,
168
- landedLayer,
169
- steps,
170
- designFitness: {
171
- designWeak: designFitness.designWeak,
172
- smellCount: designFitness.smellCount,
173
- ids: designFitness.ids,
174
- },
175
- patternBetCount: patternBets.length,
176
- patternBetsNeverMechanicalSafe: betsHonesty.ok,
177
- metric: 'placementTurns',
178
- };
179
- }
180
-
181
- /**
182
- * Compare two arms: golden must be strictly better on placementTurns.
183
- *
184
- * @param {ReturnType<typeof measureArmVelocity>} designWeakArm
185
- * @param {ReturnType<typeof measureArmVelocity>} goldenArm
186
- */
187
- export function compareVelocityArms(designWeakArm, goldenArm) {
188
- const dw = designWeakArm.placementTurns;
189
- const gp = goldenArm.placementTurns;
190
- const goldenBetter = gp < dw;
191
- const delta = dw - gp;
192
- return {
193
- metric: 'placementTurns',
194
- designWeakTurns: dw,
195
- goldenPathTurns: gp,
196
- goldenStrictlyBetter: goldenBetter,
197
- deltaTurns: delta,
198
- relativeReduction: dw > 0 ? delta / dw : null,
199
- method:
200
- 'Agent-equivalent placement attempts until DomainModel home for fixed pure-rule feature ' +
201
- `(${FEATURE_SCENARIO.id}). Design-weak arm walks concurrent-layout candidates; ` +
202
- 'golden-path arm uses .ark/golden-pattern.json newCodeHome as first attempt. ' +
203
- 'No live LLM; gate not weakened; design-weak residual may remain on both arms.',
204
- };
205
- }
206
-
207
- /**
208
- * Run full Q05 report object from two prepared roots (same config shape expected).
209
- *
210
- * @param {{
211
- * designWeakRoot: string,
212
- * goldenPathRoot: string,
213
- * config: object,
214
- * }} opts
215
- */
216
- export function runAiVelocityComparison(opts) {
217
- const designWeakArm = measureArmVelocity({
218
- root: opts.designWeakRoot,
219
- config: opts.config,
220
- armId: 'design-weak',
221
- });
222
- const goldenArm = measureArmVelocity({
223
- root: opts.goldenPathRoot,
224
- config: opts.config,
225
- armId: 'golden-path',
226
- });
227
- const comparison = compareVelocityArms(designWeakArm, goldenArm);
228
-
229
- return {
230
- schemaVersion: '1',
231
- id: 'q05-ai-velocity',
232
- mode: 'fixture-measured',
233
- scenario: {
234
- id: FEATURE_SCENARIO.id,
235
- prompt: FEATURE_SCENARIO.prompt,
236
- fileName: FEATURE_SCENARIO.fileName,
237
- correctLayer: FEATURE_SCENARIO.correctLayer,
238
- correctPath: FEATURE_SCENARIO.correctPath,
239
- sourceBytes: Buffer.byteLength(FEATURE_SCENARIO.source, 'utf8'),
240
- },
241
- arms: {
242
- 'design-weak': designWeakArm,
243
- 'golden-path': goldenArm,
244
- },
245
- comparison,
246
- honesty: {
247
- designWeakArmStillDesignWeak: designWeakArm.designFitness.designWeak === true,
248
- goldenDoesNotRequireClearingDesignWeak: true,
249
- patternBetsNeverMechanicalSafe:
250
- designWeakArm.patternBetsNeverMechanicalSafe &&
251
- goldenArm.patternBetsNeverMechanicalSafe,
252
- gateNotWeakened: true,
253
- liveLlmRequired: false,
254
- },
255
- ok: comparison.goldenStrictlyBetter === true,
256
- };
257
- }
258
-
259
- /**
260
- * Materialize golden-path arm: copy design-weak tree + write golden pattern.
261
- * Pure helper for harness / tests (caller owns temp lifecycle).
262
- *
263
- * @param {string} designWeakRoot
264
- * @param {string} destRoot
265
- * @param {{ name?: string, norm?: string, newCodeHome?: string }} [golden]
266
- */
267
- export function materializeGoldenPathArm(designWeakRoot, destRoot, golden = {}) {
268
- copyTree(designWeakRoot, destRoot);
269
- const arkDir = path.join(destRoot, '.ark');
270
- fs.mkdirSync(arkDir, { recursive: true });
271
- const body = {
272
- schemaVersion: '1',
273
- name: golden.name || 'domain-first pure rules',
274
- norm:
275
- golden.norm ||
276
- 'New pure business rules live under src/domain/; adapters and routes never own can*/policy helpers.',
277
- newCodeHome: golden.newCodeHome || 'src/domain/',
278
- examplePath: golden.examplePath || 'src/domain/canRefund.ts',
279
- };
280
- fs.writeFileSync(path.join(arkDir, 'golden-pattern.json'), JSON.stringify(body, null, 2) + '\n');
281
- return body;
282
- }
283
-
284
- function copyTree(src, dst) {
285
- fs.mkdirSync(dst, { recursive: true });
286
- for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
287
- if (entry.name === 'node_modules' || entry.name === '.git') continue;
288
- const s = path.join(src, entry.name);
289
- const d = path.join(dst, entry.name);
290
- if (entry.isDirectory()) copyTree(s, d);
291
- else fs.copyFileSync(s, d);
292
- }
293
- }
@@ -1,6 +0,0 @@
1
- /** Compatibility adapter; canonical Tarjan evaluation lives in the bundled Kernel engine. */
2
- import { detectArchitectureCycles } from './analysis-engine.mjs';
3
-
4
- export function detectCycles(graph) {
5
- return detectArchitectureCycles(graph);
6
- }