arkgate 4.0.1 → 4.1.1

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 (59) hide show
  1. package/CHANGELOG.md +134 -0
  2. package/README.md +13 -13
  3. package/bin/ark-check-runtime.mjs +244 -25
  4. package/bin/ark-check.mjs +10 -1
  5. package/bin/ark-layer-match.mjs +80 -5
  6. package/bin/ark-shared.mjs +170 -9
  7. package/bin/ark.mjs +52 -5
  8. package/bin/lib/adapter-contract.mjs +7 -1
  9. package/bin/lib/agent-gates.mjs +2 -0
  10. package/bin/lib/analysis-engine.mjs +6 -6
  11. package/bin/lib/ark-gitignore.mjs +88 -0
  12. package/bin/lib/arkrules-sensors.mjs +63 -22
  13. package/bin/lib/ci-and-commands.mjs +165 -9
  14. package/bin/lib/core-ratchet.mjs +9 -4
  15. package/bin/lib/doctor-advisories.mjs +8 -1
  16. package/bin/lib/doctor-plan.mjs +287 -61
  17. package/bin/lib/enforcement-honesty.mjs +408 -27
  18. package/bin/lib/enforcement-state.mjs +1 -1
  19. package/bin/lib/field-install.mjs +35 -2
  20. package/bin/lib/github-enforcement.mjs +152 -4
  21. package/bin/lib/host-support-matrix.mjs +91 -17
  22. package/bin/lib/html-report-depth.mjs +178 -3
  23. package/bin/lib/html-report.mjs +19 -13
  24. package/bin/lib/install-migrate.mjs +109 -6
  25. package/bin/lib/managed-upgrade.mjs +99 -0
  26. package/bin/lib/presets.mjs +314 -46
  27. package/bin/lib/project-root.mjs +268 -0
  28. package/bin/lib/remediation.mjs +12 -11
  29. package/bin/lib/rules-inventory.mjs +71 -29
  30. package/bin/lib/rules-under-contract.mjs +134 -4
  31. package/bin/lib/start-preview.mjs +48 -14
  32. package/bin/lib/suggestions.mjs +118 -3
  33. package/bin/lib/unavailable-analysis.mjs +2 -0
  34. package/bin/lib/write-path-capabilities.mjs +38 -9
  35. package/bin/lib/write-path-detect.mjs +2 -2
  36. package/dist/eslint/index.cjs +2 -2
  37. package/dist/eslint/index.d.ts +27 -2
  38. package/dist/eslint/index.js +2 -2
  39. package/dist/index.cjs +16 -14
  40. package/dist/index.d.ts +3 -1
  41. package/dist/index.js +16 -14
  42. package/docs/README.md +5 -5
  43. package/docs/agent-guide.md +5 -3
  44. package/docs/ai-gates.md +45 -18
  45. package/docs/brownfield-adoption.md +36 -0
  46. package/docs/configuration.md +36 -0
  47. package/docs/develop.md +16 -6
  48. package/docs/package-surface.md +5 -3
  49. package/docs/product-voice.md +15 -2
  50. package/docs/typescript-support.md +9 -5
  51. package/docs/use.md +3 -1
  52. package/package.json +3 -1
  53. package/server.json +3 -3
  54. package/templates/architecture-playbook.json +3 -0
  55. package/templates/layers/shared-types.starter.json +29 -0
  56. package/templates/skills/ark-adopt.md +2 -0
  57. package/templates/skills/ark-explain.md +5 -0
  58. package/templates/skills/ark-explore.md +21 -1
  59. package/templates/skills/ark-fix.md +16 -5
package/docs/README.md CHANGED
@@ -52,14 +52,14 @@ These are **not** the day-to-day product path. They stay in the repo for evidenc
52
52
  | Area | Path |
53
53
  |------|------|
54
54
  | Release notes (by version) | [releases/](releases/) · [CHANGELOG.md](../CHANGELOG.md) |
55
- | Epic plans (seeded + shipped) | [plans/](plans/) |
55
+ | Epic plans (seeded + shipped) | [plans/](plans/) · latest: [enforcement-evidence-and-docs-truth](plans/enforcement-evidence-and-docs-truth/README.md) (Phase EH shipped; 4.1.1 prepared) |
56
56
  | Claims audit | [audit/claims-matrix.md](audit/claims-matrix.md) |
57
57
  | Field adoption kit (scaffolding, not closed) | [field/](field/) |
58
58
  | Runtime hardening (experimental) | [production-hardening.md](production-hardening.md) |
59
59
 
60
- Current major on npm `latest`: [releases/4.0.0.md](releases/4.0.0.md) (`arkgate@4.0.0` layers + optional **ArkRules**).
61
- Next prepared: [releases/4.0.1.md](releases/4.0.1.md) (`arkgate@4.0.1` — not yet `latest`).
62
- Previous: [releases/3.9.2.md](releases/3.9.2.md) (`arkgate@3.9.2`).
60
+ Current published: [releases/4.1.0.md](releases/4.1.0.md) (`arkgate@4.1.0` on npm `latest`).
61
+ Prepared next: [releases/4.1.1.md](releases/4.1.1.md) (Phase EH **Status: prepared**, not on npm until publish).
62
+ Previous major: [releases/4.0.0.md](releases/4.0.0.md) (`arkgate@4.0.0`).
63
63
  Config: [configuration.md](configuration.md) · Agent skills dual-plane: [agent-guide.md](agent-guide.md).
64
64
 
65
65
  ---
@@ -68,5 +68,5 @@ Config: [configuration.md](configuration.md) · Agent skills dual-plane: [agent-
68
68
 
69
69
  1. **Audience first** — every page should be use, develop, or contribute.
70
70
  2. **One primary flow** — `start` → doctor → optional guided work.
71
- 3. **Honest hardness** — host write guarantees differ; CI required status is the shared merge boundary.
71
+ 3. **Honest hardness** — host write guarantees differ; a **required GitHub status context** running the merge CLI is the shared hard boundary.
72
72
  4. **History is not the product** — version archaeology lives under `releases/` and `plans/`, not the front door.
@@ -40,9 +40,11 @@ npx ark-check --install-agent-gates --skills-only --force
40
40
 
41
41
  **Write-path honesty:** Claude/Grok/Antigravity can hard-block listed PreToolUse ops when
42
42
  installed and trusted. Cursor/Codex/OpenCode remain **advisory at write**. For every host, the
43
- repository-wide hard boundary is a **required** CI status (`arkgate-check --strict-merge`)
44
- never claim Cursor/Codex/OpenCode hard write. See [ai-gates.md](ai-gates.md) and the README host
45
- matrix.
43
+ repository-wide hard boundary is a **required GitHub status context** that runs
44
+ `arkgate-check --strict-merge` (alias `ark-check --strict-merge`) the CLI name is not the
45
+ status context name. Never claim Cursor/Codex/OpenCode hard write. Soft-write alone does not mean
46
+ the project is unfinished; doctor keeps it as an environment residual. See [ai-gates.md](ai-gates.md)
47
+ and the README host matrix.
46
48
 
47
49
  ## Architecture playbook and `ark-check --recommend`
48
50
 
package/docs/ai-gates.md CHANGED
@@ -307,8 +307,10 @@ required CI status is the hard repository boundary.
307
307
  npx ark-check --install-agent-gates --tools codex
308
308
  ```
309
309
 
310
- The generated hook includes `--hook-repair`, so a rejected patch carries the same structured
311
- repair envelope as Claude and Grok. Codex still needs hook trust enabled for the project.
310
+ The generated hook includes `--hook-repair`, so a rejected patch **may emit** a structured
311
+ repair envelope (same JSON shape as Claude/Grok). **Reinjection is not guaranteed** on Codex
312
+ local write stays advisory/bypassable; the host must re-apply any fix, and required CI remains
313
+ the hard merge boundary. Codex still needs hook trust enabled for the project.
312
314
 
313
315
  Modern Codex resolves MCP servers from the active project's `.codex/config.toml`. Ark writes
314
316
  that file with relative paths, so every repository owns its primary `ark` binding without
@@ -387,7 +389,7 @@ Ark verifies those references against each selected host catalog.
387
389
  `--skills-only --tools codex --force` (repo) or `--codex-home --force` (home) fix.
388
390
  - Codex **write path is advisory**: MCP + best-effort `.codex/hooks.json` is **not** a hard
389
391
  write boundary and is **not** equivalent to Claude/Grok PreToolUse hard-write + repair.
390
- The hard merge backstop is CI `--strict-merge` (or `--strict`) plus a required status check.
392
+ The hard merge backstop is CI `--strict-merge` (or `--strict`) as a **required GitHub status context** (not “workflow file present”).
391
393
  - CI workflows that run ark-check without the fail-closed profile (or with only
392
394
  `--strict-config`) surface gap `enforcement-ci-not-fail-closed`.
393
395
 
@@ -560,21 +562,25 @@ export default [
560
562
 
561
563
  **Exact layer-edge parity envelope:** the linted production source is on disk, inside
562
564
  `include`, outside configured/generated exclusions, parse-clean, and uses a static
563
- `import`/`export` with a relative literal whose target is also on disk. Inside that envelope,
564
- ESLint uses the same layer glob specificity, rule decision, rule id, severity, and evidence as
565
- the resolved CLI. It reloads `ark.config.json` when its content changes and never invents a
566
- not-yet-created target.
565
+ `import`/`export` with a **relative** or **tsconfig `paths` / `baseUrl` alias** (e.g. `@/*`)
566
+ literal whose target is also on disk. Inside that envelope, ESLint uses the same layer glob
567
+ specificity, rule decision, rule id, and evidence as the resolved CLI. It reloads
568
+ `ark.config.json` when its content changes and never invents a not-yet-created target.
567
569
 
568
- Outside that envelope—path aliases, packages/workspaces, symlinks, CommonJS,
569
- `import = require`, dynamic imports, virtual creates/deletes, unresolved targets, or complete
570
- cross-file candidates—ESLint emits no layer-parity verdict. Use `ark preflight`,
571
- `ark_prepare_change`, the complete ApplyPatch hook, or final strict CI; those paths consume the
572
- canonical resolved facts.
570
+ **Path-alias residual (honest):** simple `paths` + `baseUrl` (including one level of relative
571
+ `extends`) are resolved. Not claimed: TypeScript project references, multi-target path arrays
572
+ beyond the first entry, catch-all `*` mappings, package/workspace bare imports, or symlink
573
+ hops. Those stay CI / preflight / write-gate truth.
574
+
575
+ Outside that envelope—packages/workspaces, symlinks, CommonJS, `import = require`, dynamic
576
+ imports, virtual creates/deletes, unresolved targets, or complete cross-file candidates—ESLint
577
+ emits no layer-parity verdict. Use `ark preflight`, `ark_prepare_change`, the complete
578
+ ApplyPatch hook, or final strict CI; those paths consume the canonical resolved facts.
573
579
 
574
580
  Additional rule notes:
575
581
 
576
- - Relative imports are resolved only to existing on-disk TS/JS targets; package bare imports are left to CI/TS.
577
- - Type-only and value forbidden edges both error (same pass/fail as `arkgate-check`).
582
+ - Relative and tsconfig-aliased imports resolve only to existing on-disk TS/JS targets; package bare imports are left to CI/TS.
583
+ - Value forbidden edges error (same pass/fail as `arkgate-check`). Type-only forbidden edges are **placement debt** (reported with `typeOnly`); merge blocking prefers value edges — align with doctor `typeEdgePolicy`.
578
584
  - `no-forbidden-globals` applies from the file layer’s `forbiddenGlobals`; the `globals` option is only a standalone fallback when no project config applies, never an override that weakens the project contract. Layers without either surface are not inventively restricted. `process` also owns exact value imports of `process` / `node:process`; type-only forms, subpaths, and `child_process` stay excluded. If the same layer also denies the `process` capability, this rule is the single `FORBIDDEN_GLOBAL` voice.
579
585
  - Without `ark.config.json`, `no-domain-infra-imports` emits no contract verdict.
580
586
 
@@ -587,13 +593,34 @@ for standalone linting where no project contract applies.
587
593
  Whatever the agent side does, run the merge profile in CI:
588
594
 
589
595
  ```yaml
590
- - run: npx ark-check --root . --config ark.config.json --strict-merge --fail-on-new-smells --base-ref "${{ github.event.pull_request.base.sha || github.event.before }}"
596
+ # EH04: first push may have all-zero github.event.before only pass --base-ref when resolvable.
597
+ - name: Ark architecture check
598
+ env:
599
+ ARK_POLICY_BASE_REF: ${{ github.event.pull_request.base.sha || github.event.before }}
600
+ run: |
601
+ set -euo pipefail
602
+ BASE_REF="${ARK_POLICY_BASE_REF:-}"
603
+ if [[ "$BASE_REF" =~ ^0{40,64}$ ]]; then BASE_REF=""; fi
604
+ if [ -n "$BASE_REF" ] && git cat-file -e "${BASE_REF}^{commit}" 2>/dev/null; then
605
+ export ARK_POLICY_BASE_REF="$BASE_REF"
606
+ npx ark-check --root . --config ark.config.json --strict-merge \
607
+ --fail-on-new-smells --base-ref "$BASE_REF"
608
+ else
609
+ export ARK_POLICY_BASE_REF=""
610
+ npx ark-check --root . --config ark.config.json --strict-merge
611
+ fi
591
612
  ```
592
613
 
593
614
  This explicit brownfield ratchet records schema `1.0` identities, touched paths, and stable
594
- evidence; missing base exits `2`. Its first semantic smell is `domain-logic-in-ui`; residual,
595
- path-only moves, and unrelated work stay green. Generated Claude/Grok hooks share the delta and
596
- golden-pattern repair hint. MCP exposes the result but stays advisory.
615
+ evidence; missing base with `--fail-on-new-smells` exits `2`, so the generated workflow skips the
616
+ delta when the SHA is all-zero or unresolvable while keeping the full merge gate. Its first
617
+ semantic smell is `domain-logic-in-ui`; residual, path-only moves, and unrelated work stay green.
618
+ Generated Claude/Grok hooks share the delta and golden-pattern repair hint. MCP exposes the result
619
+ but stays advisory.
620
+
621
+ **CLI vs required status:** `arkgate-check --strict-merge` / `ark-check --strict-merge` is the
622
+ **command**. The hard merge boundary is making that job a **required GitHub status context** —
623
+ not “workflow file present.”
597
624
 
598
625
  Or use the repository's composite Action at a pinned release or commit:
599
626
 
@@ -20,6 +20,42 @@ every ungoverned directory** (harvested from the 11-layer profile + presets; unr
20
20
  ones are flagged for you to classify, never guessed). Keep an existing config; don't
21
21
  regenerate it unasked.
22
22
 
23
+ ### Next.js honesty (default overlays / ui-surface / monorepo)
24
+
25
+ **App Router API routes are Application / use-case shell by default**, not Presentation/UI:
26
+
27
+ | Path | Default layer |
28
+ |------|----------------|
29
+ | `app/api/**`, `src/app/api/**` | **ApplicationOrchestration** (orchestration shell) |
30
+ | `pages/api/**`, `src/pages/api/**` | **ApplicationOrchestration** |
31
+ | UI routes (`app/(…)`, `page.tsx`, `components/**`) | **PresentationAdapters** |
32
+
33
+ Without this split, broad `**/app/**` Presentation patterns push every API handler into UI and
34
+ create a Presentation→Persistence tsunami. Specificity still wins: more-specific Application
35
+ globs beat Presentation. ArkRules stay **opt-in** (not forced on by start/adopt).
36
+
37
+ ### Composition-root / factory modules (optional Application-only)
38
+
39
+ Many brownfield trees park DI composition roots and object factories next to domain code
40
+ (`src/factories/**`, `src/composition/**`, `src/app/container.ts`). Those modules are
41
+ **wiring**, not domain rules:
42
+
43
+ | Pattern | Recommended layer | Why |
44
+ |---------|-------------------|-----|
45
+ | `**/factories/**`, `**/composition/**`, `**/container.ts`, `**/bootstrap.ts` | **ApplicationOrchestration** (or a dedicated optional CompositionRoot layer if you need it) | Constructs aggregates / wires ports; may import Domain + Persistence adapters |
46
+ | Aggregate `static create` / private ctor factories **inside** domain entities | **DomainModel** | Always-valid factory is a domain rule (ArkRules `always-valid-factory`), not composition |
47
+ | NestJS modules / providers | FrameworkAdapters / Application | Framework shell, not Domain |
48
+
49
+ Do **not** map composition roots into DomainModel solely because a file constructs entities —
50
+ that turns honest Application→Domain edges into false Domain purity failures. Prefer:
51
+
52
+ 1. Application patterns that match `**/factories/**` / `**/composition/**` (higher specificity than broad Domain globs), or
53
+ 2. An optional **CompositionRoot** layer that may depend on Domain + Persistence and is
54
+ forbidden as a dependency *from* Domain (mirror Application rules).
55
+
56
+ Field note (S3.1.3): factory-bag composition roots belong in Application (or CompositionRoot),
57
+ not Domain — document the choice in `ark.config.json` rather than freezing factory noise.
58
+
23
59
  ## 2. Diagnose before you freeze
24
60
 
25
61
  ```bash
@@ -107,6 +107,42 @@ Rule fields:
107
107
  when both paths classify. Missing paths, empty slice folders, or unclassifiable slices
108
108
  **fail closed** (deny — cannot prove same-slice).
109
109
 
110
+ ### Type-only edges (placement debt)
111
+
112
+ `import type` / `export type` and pure type-only named bindings are **type placement debt**, not
113
+ runtime coupling. They still appear on the **violations** list with `typeOnly: true`,
114
+ `failsStrict: false`, and adapter diagnostic **severity: warning** so doctor/HTML keep
115
+ `violations.typeOnly` / `typeEdgePolicy` honest — but they **do not** fail merge/exit, library
116
+ `valid`, or preflight the way **value** edges do. **Exception:** `peerIsolation` slice
117
+ boundaries stay hard even for type-only. A value import of a pure-type barrel is still a value
118
+ edge (not soft-skipped). Prefer placing shared types in a **SharedTypes** (or owning) layer
119
+ both sides may import. Optional starter: [`templates/layers/shared-types.starter.json`](../templates/layers/shared-types.starter.json)
120
+ (layer globs + allow rules). Doctor always emits `violations.typeEdgePolicy`; plan groups type-only
121
+ steps under `plan.typeOnlyGroup` when volume is high.
122
+
123
+ ### Next.js API shell (framework overlay / presets)
124
+
125
+ When Next is detected (or `ui-surface` / monorepo patterns apply), **`app/api/**` and
126
+ `pages/api/**` classify as Application orchestration**, not Presentation. UI routes stay
127
+ Presentation. More-specific Application globs win over broad `**/app/**` Presentation patterns.
128
+ See [brownfield adoption](brownfield-adoption.md#nextjs-honesty-default-overlays--ui-surface--monorepo).
129
+
130
+ ### ArkRules dual plane (when `arkRules` is present)
131
+
132
+ | Plane | What it is | Merge teeth |
133
+ |-------|------------|-------------|
134
+ | **Layers** | Inter-layer import graph | Always on |
135
+ | **Structure sensors** | Intra-layer heuristics | Only `mode: "enforced"` |
136
+ | **Invariants** | Catalog + coverage evidence (not a business runtime) | Only enforced + proven-uncovered |
137
+
138
+ Absence of `arkRules` adds **no** extra merge teeth. **Advisory** structure sensors and advisory
139
+ invariants also add **no** merge teeth (FG-ARKRULES-ADVISORY-ONLY) — packing every starter
140
+ `arkrules/*` file does not make merge fail structure alone. Enforced structure/invariants arm
141
+ `mergePlanes.extraMergeTeeth` only when the layer plane is honestly classified
142
+ (governed ≥ 50% and ≥ 1 populated layer); empty classification never gets structure teeth
143
+ (P1M-EXTRATEETH-EMPTY-GRAPH). Structure and invariants **never** merge into one architecture
144
+ score. Doctor exposes `rulesUnderContract.mergePlanes` for which plane can fail.
145
+
110
146
  Safety fields:
111
147
 
112
148
  - `maxTsSuppressions`, `maxAnyCasts`
package/docs/develop.md CHANGED
@@ -14,14 +14,18 @@ npx arkgate start --apply
14
14
  npx arkgate-check --doctor
15
15
  ```
16
16
 
17
- Make the architecture check a **required** merge status (GitHub/GitLab/etc.):
17
+ Make the architecture check a **required** merge **status context** (GitHub/GitLab/etc.). The CLI
18
+ command is `arkgate-check --strict-merge` / `ark-check --strict-merge` — the hard boundary is
19
+ requiring that job’s status, not merely adding a workflow file:
18
20
 
19
21
  ```yaml
20
22
  - run: npx arkgate-check --root . --config ark.config.json --strict-merge
21
23
  # or: uses: pedroknigge/arkgate@<tag-or-SHA>
22
24
  ```
23
25
 
24
- `--strict-merge` (or compatibility `--strict`) is the repository-wide hard boundary for every agent host.
26
+ Generated workflows also gate `--fail-on-new-smells --base-ref` so a first push with an all-zero
27
+ `github.event.before` still runs the full merge gate without a broken delta (see
28
+ [ai-gates.md](ai-gates.md#ci-backstop)).
25
29
 
26
30
  ---
27
31
 
@@ -31,9 +35,9 @@ Local write hardness **differs by host**. CI required status is the shared hard
31
35
 
32
36
  | Host | Local write | MCP | Merge |
33
37
  |------|-------------|-----|-------|
34
- | Claude · Grok · Antigravity | Hard PreToolUse when installed + trusted | Advisory | Required status |
35
- | Codex · OpenCode | Best-effort / advisory | Advisory | Required status |
36
- | Cursor | Advisory only | Advisory | Required status |
38
+ | Claude · Grok · Antigravity | Hard PreToolUse when installed + trusted | Advisory | Required status context |
39
+ | Codex · OpenCode | Best-effort / advisory | Advisory | Required status context |
40
+ | Cursor | Advisory only | Advisory | Required status context |
37
41
 
38
42
  Full matrix and install commands: [ai-gates.md](ai-gates.md) · canonical table in [README](../README.md#host-enforcement-support).
39
43
 
@@ -46,7 +50,13 @@ npx arkgate-check --install-agent-gates --tools opencode
46
50
  npx arkgate-check --install-agent-gates --skills-only --force
47
51
  ```
48
52
 
49
- Doctor reports what is actually installed and observed (`writePath` / enforcement state). Installed files alone do not imply `hard:true` without runtime evidence where the product requires it.
53
+ Doctor reports what is actually installed and observed (`writePath` / enforcement state). Installed
54
+ files alone do not imply `hard:true` without runtime evidence where the product requires it.
55
+
56
+ **Evidence split (Phase EH):** soft-write hosts keep `soft-write-host` in evidence without forcing
57
+ global **Not finished** when the contract is ready. With `ARK_DOCTOR_GITHUB=1`, successful CI runs
58
+ can show `runtimeObserved: true` even when branch-protection policy is plan-unavailable
59
+ (`unavailable-plan` on GitHub Free private); `hard: false` until the status is required.
50
60
 
51
61
  ---
52
62
 
@@ -37,6 +37,7 @@ hardening guide remains repository-hosted rather than duplicated in the gate tar
37
37
  | **`ark.config.json`** | Layer globs, rules, include/exclude, forbiddenGlobals, intent prefixes, `peerIsolation`, `dynamicImportAllowlist`, `safety` thresholds; optional **`arkRules`** map (schema `1.1+`) | Versioned by `schemaVersion`; unknown fields fail closed and migrations preserve the previous supported major. Absence of `arkRules` is byte-for-byte silent on inter-layer verdicts. |
38
38
  | **ArkRules inventory / under-contract (4.0)** | `ark-check --rules-inventory [--json]`; doctor `rulesUnderContract`; MCP `ark_rules_inventory` | Additive. Honest counts (inventoried / under-contract / frozen) — **never a score**. Structure/invariant diagnostics use adapter `1.4` provenance. |
39
39
  | **Package pin dual-truth (4.0)** | doctor JSON `packageVersionTruth`; upgrade JSON/human note when pin behind CLI | Additive, advisory. Surfaces after `upgrade --no-install` when managed CLI is ahead of package.json. |
40
+ | **Product honesty readiness split (4.1.1 EH)** | doctor JSON `productHonesty` | Additive. `unfinished` / `headline` / `primaryNextAction` / `reasonIds` remain; EH adds `contractReadiness` (`ready`\|`partial`\|`not-ready`), `localWriteBoundary` (`advisory`\|`hard`\|`unverified`\|`unknown`), `architectureReasonIds`, `environmentResidualIds` / `environmentResiduals`. Soft-write hosts stay in evidence without alone forcing global **Not finished**. `notAScore: true` always. |
40
41
  | **Policy transition analysis (T01, 3.1.0)** | `analyzePolicyDelta(...)`; MCP `ark_policy_delta`; CLI `--policy-base` / `--policy-base-ref` / `--policy-ack`; check JSON `policyDelta` | Additive schema `1.0`. Classifications and finding ids are deterministic. Weakening/judgment requires an acknowledgement bound to both policy hashes and the exact blocking finding set. |
41
42
  | **Atomic change preflight (T02, 3.1.0)** | `preflightChange(...)`; CLI `ark preflight --changes <file> --json`; MCP `ark_prepare_change` | Additive schema `1.0`. One complete governed production-source `{path,content}` / `{path,delete:true}` batch; read-only; returns operation, content/tree/policy/compiler fingerprints and stable graph findings. MCP availability alone is advisory. |
42
43
  | **Architecture change map (T03, 3.1.0)** | `arkgate/schema/change-map` or `arkgate/schema/ark.change-map.schema.json`; CLI `ark preflight --change-map <file>`; MCP `ark_prepare_change.changeMap` | Optional strict schema `1.0`. Canonical planned paths + operations + resolved Ark layers + dependencies between planned files. Preflight returns `changeMapHash`; absence is normal and adds no project file. Structural intent only, never behavioral completion. |
@@ -189,7 +190,8 @@ production deployment would need to satisfy; it is not a readiness certification
189
190
  ## Release notes (maintainers)
190
191
 
191
192
  Ship notes for a version live under [releases/](https://github.com/pedroknigge/arkgate/tree/main/docs/releases)
192
- (current on npm: [4.0.0.md](https://github.com/pedroknigge/arkgate/blob/main/docs/releases/4.0.0.md); prepared:
193
- [4.0.1.md](https://github.com/pedroknigge/arkgate/blob/main/docs/releases/4.0.1.md); previous:
194
- [3.9.2.md](https://github.com/pedroknigge/arkgate/blob/main/docs/releases/3.9.2.md)).
193
+ (current published: [4.1.0.md](https://github.com/pedroknigge/arkgate/blob/main/docs/releases/4.1.0.md);
194
+ prepared next: [4.1.1.md](https://github.com/pedroknigge/arkgate/blob/main/docs/releases/4.1.1.md);
195
+ previous: [4.0.1.md](https://github.com/pedroknigge/arkgate/blob/main/docs/releases/4.0.1.md),
196
+ [4.0.0.md](https://github.com/pedroknigge/arkgate/blob/main/docs/releases/4.0.0.md)).
195
197
  Publish path: signed annotated tag → GitHub Release → `publish-npm.yml` (see [CONTRIBUTING.md](https://github.com/pedroknigge/arkgate/blob/main/CONTRIBUTING.md)).
@@ -51,8 +51,11 @@ Examples:
51
51
  | **contract** | `ark.config.json` layers, rules, include — the machine-readable architecture file |
52
52
  | **Layers plane** | Inter-layer edges: imports, placement, purity, isolation |
53
53
  | **ArkRules** (opt-in) | Intra-layer structure sensors + domain invariant catalogs as data (`arkrules/*`) |
54
+ | **advisory ArkRules** | Default sensor mode — **not** merge teeth; does not fail CI/merge alone (FG-ARKRULES-ADVISORY-ONLY) |
55
+ | **extraMergeTeeth** | Only when enforced structure/invariant rules exist **and** classification is honest (≥50% governed, ≥1 populated layer) |
54
56
  | **dual-plane residual** | Label findings **`[Layer]`** vs **`[ArkRules]`** — never blur them |
55
57
  | **rulesUnderContract** | Doctor/inventory counts for ArkRules — **never a score** |
58
+ | **type-only placement debt** | `import type` edges on the violations list with `failsStrict:false` — prefer SharedTypes / owning layer; not runtime coupling |
56
59
  | **gate** / **write gate** | Host boundary that blocks or advises on invalid writes |
57
60
  | **edges** | Allowed import graph (plan **A** / remediation) |
58
61
  | **baseline** | Frozen known debt; does not make a wrong contract honest |
@@ -70,7 +73,9 @@ Examples:
70
73
  | **compact router** | Default onboarding agent instructions (not the full skill pack) |
71
74
  | **hard write** | Non-bypassable PreToolUse block for listed ops (Claude/Grok when installed + trusted) |
72
75
  | **advisory write** | MCP/rules coach only (Cursor/Codex at write time) — not a hard block |
73
- | **required CI** | Merge hard boundary when the repository makes `arkgate-check` a required status |
76
+ | **required CI / status context** | Merge hard boundary when the repository makes the Ark job a **required GitHub status context** (CLI: `arkgate-check --strict-merge` / `ark-check --strict-merge`) |
77
+ | **contract ready** | Project/layers/ArkRules honesty residual clear — not the same as “hard local write” |
78
+ | **environment residual** | Permanent host/provider posture (e.g. soft-write Codex) kept in evidence without forcing global **Not finished** |
74
79
 
75
80
  ## Do (product copy)
76
81
 
@@ -79,10 +84,13 @@ Examples:
79
84
  | Name the status light + plain fact + term + next action | “Enforce · design-weak. Checked edges are honest; design smells remain. Next: one Shape door — explore → dual-plan B → autopilot with OK.” |
80
85
  | Rank one primary door under residual | Doctor **Primary next action** #1; **Also** only for secondary |
81
86
  | Label expert skills as escapes | “Install skill pack only when doctor or a STOP handoff names a skill.” |
82
- | State host write honesty | “Cursor/Codex: advisory write. Required CI is the hard merge boundary.” |
87
+ | State host write honesty | “Cursor/Codex: advisory write. Required GitHub status context is the hard merge boundary.” |
88
+ | Soft-write ≠ unfinished project | “Architecture contract ready; Codex local writes are advisory.” Keep `soft-write-host` in evidence; reserve **Not finished** for contract/project debt. |
83
89
  | Keep Suggest on start → doctor | New-here primary is finish `start`, not a competing recommend/architect curriculum |
84
90
  | Qualify edge-clean under design-weak | “None on checked edges … design residual remains. Not healthy finished.” |
85
91
  | Prefer fail-closed over fake hard | Incomplete analysis, unobserved hooks, and soft MCP never paint as hard green |
92
+ | Honesty clear ≠ architecture healthy | `productHonesty.finished` means residual **architecture** honesty sensors are clear — not a green graph score. Open blocking violations, ADAPT/SUGGEST with debt, dual-truth pin, or design residual keep `unfinished: true`. Permanent soft-write alone does **not**. |
93
+ | Separate CI runtime from provider policy | Successful CI run ≠ required status; GitHub Free plan 403 → `unavailable-plan`, not “CI never ran.” |
86
94
 
87
95
  ## Avoid
88
96
 
@@ -95,9 +103,14 @@ Examples:
95
103
  | “modes you pick” for Suggest/Adapt/Enforce | Those are **status lights**, not settings |
96
104
  | Skill-shopping lists as the default curriculum | Progressive disclosure: one door first |
97
105
  | “Healthy / done” while design-weak | False done |
106
+ | “Honesty clear” as “architecture finished” | Honesty clear only means residual honesty sensors are quiet; graph/mode debt is separate |
107
+ | “Not finished” solely because host is Codex/Cursor | Soft-write is environment residual; do not paint a green whole-tree project as unfinished architecture |
108
+ | Conflating CLI name with required status | `ark-check` is the command; the hard boundary is the GitHub required **status context** |
98
109
  | “ArkRules prove business correctness” | They enforce *declared* structure/coverage evidence, not arbitrary logic or full semantic proof |
99
110
  | “Structure enforced = Domain extraction done” | Structure sensors are **heuristics**; extraction is judgment (`/ark-fix` / pilot) |
100
111
  | “Covered invariant = E2E business tests” | Coverage = symbol/test evidence for a named policy, not a runtime test runner |
112
+ | “Max arkRules packs = merge fails structure” | **Advisory ArkRules ≠ merge teeth.** Only `mode: "enforced"` can add teeth, and only after honest classification (FG-ARKRULES-ADVISORY-ONLY) |
113
+ | “Type-only volume means the gate is broken” | High type-only count is placement debt (behavior OK); group under plan A type-only, offer SharedTypes starter |
101
114
  | Blurring import edges with invariants | Always label **`[Layer]`** vs **`[ArkRules]`** |
102
115
 
103
116
  ---
@@ -176,11 +176,15 @@ The release compatibility workflow packs the candidate first, then tests clean c
176
176
  - **npm / pnpm / Yarn**;
177
177
  - project TypeScript **5.9.3 / 6.0.3 / 7.0.2**.
178
178
 
179
- That is 36 installed-artifact cells. Each cell verifies that the project's `tsc` stays on the
180
- requested project compiler and that ArkGate resolves its fallback as exact 6.0.3 when the project
181
- API is unusable. Reports record `installMode` so the Yarn TS7 linker boundary cannot disappear
182
- behind a green job. All 36 cells passed on source `228dd893` in CI run `29655190747`. Locally, the
183
- repository entry point is:
179
+ That is **36** installed-artifact cells on the **full matrix** path (`push` to `main`, PR labels
180
+ `full-matrix` / `release`, or release-prep branch names such as `feat/4.1*`). Ordinary PRs run a
181
+ **slim** packed smoke: **one** cell (Node 20 + npm) that still exercises TS 5/6/7 in-process.
182
+ See [CONTRIBUTING.md CI profiles](../CONTRIBUTING.md#ci-profiles-pr-slim-vs-full-matrix).
183
+
184
+ Each cell verifies that the project's `tsc` stays on the requested project compiler and that
185
+ ArkGate resolves its fallback as exact 6.0.3 when the project API is unusable. Reports record
186
+ `installMode` so the Yarn TS7 linker boundary cannot disappear behind a green job. All 36 cells
187
+ passed on source `228dd893` in CI run `29655190747`. Locally, the repository entry point is:
184
188
 
185
189
  ```bash
186
190
  npm run test:ts-compat
package/docs/use.md CHANGED
@@ -33,9 +33,11 @@ Full skill pack is **optional** (expert depth). Day-to-day: compact router + doc
33
33
  | When | What happens |
34
34
  |------|----------------|
35
35
  | While the AI writes | Host write gate or advisory MCP (depends on host) |
36
- | Before merge | `arkgate-check` make it a **required** CI status |
36
+ | Before merge | Make the Ark job a **required GitHub status context** running `arkgate-check --strict-merge` (alias `ark-check`) |
37
37
  | Anytime | Doctor: Suggest / Adapt / Enforce (+ design-weak if residual) |
38
38
 
39
+ **Codex / Cursor / OpenCode:** local write stays advisory forever — that is not unfinished architecture. Doctor may say **contract ready** while still reminding you that local writes are advisory; **Not finished** is reserved for real project/contract debt.
40
+
39
41
  ArkGate is **not** a web framework, ORM, or app runtime. It is architecture enforcement + co-pilot for AI TypeScript.
40
42
 
41
43
  ### Two planes (you choose)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arkgate",
3
- "version": "4.0.1",
3
+ "version": "4.1.1",
4
4
  "description": "ArkGate — architecture co-pilot for AI TypeScript (write gate, CI gate, plan/loop; optional ArkRules)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -136,6 +136,7 @@
136
136
  "check:package-files": "node scripts/verify-package-files.mjs",
137
137
  "check:release-artifacts": "node scripts/verify-release-artifacts.mjs",
138
138
  "test:release-artifacts": "vitest run tests/unit/scripts/verifyReleaseArtifacts.test.ts",
139
+ "test:field-dogfood-smoke": "node scripts/field-dogfood/smoke.mjs",
139
140
  "audit:beta-exit": "node scripts/beta-exit-audit.mjs",
140
141
  "clean": "rm -rf dist",
141
142
  "release:npm": "node scripts/release-npm.mjs",
@@ -156,6 +157,7 @@
156
157
  "overrides": {
157
158
  "qs": "6.15.3",
158
159
  "vite": "6.4.3",
160
+ "postcss": "8.5.23",
159
161
  "tsup": {
160
162
  "esbuild": "0.28.1"
161
163
  },
package/server.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
3
  "name": "io.github.pedroknigge/arkgate",
4
- "description": "ArkGate \u2014 architecture co-pilot for AI TypeScript (write gate, CI, plan/loop)",
4
+ "description": "ArkGate architecture co-pilot for AI TypeScript (write gate, CI, plan/loop)",
5
5
  "repository": {
6
6
  "url": "https://github.com/pedroknigge/arkgate",
7
7
  "source": "github"
8
8
  },
9
- "version": "4.0.1",
9
+ "version": "4.1.1",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "arkgate",
14
- "version": "4.0.1",
14
+ "version": "4.1.1",
15
15
  "runtimeHint": "npx",
16
16
  "transport": {
17
17
  "type": "stdio"
@@ -86,6 +86,9 @@
86
86
  "label": "UI-focused repository; backend lives elsewhere or is thin",
87
87
  "preset": "layered",
88
88
  "presetAlternatives": {
89
+ "vite-vercel-spa": {
90
+ "whenSignal": "viteVercelSpaLayout"
91
+ },
89
92
  "feature-sliced": {
90
93
  "whenSignal": "featureSlicedLayout"
91
94
  }
@@ -0,0 +1,29 @@
1
+ {
2
+ "$comment": "Optional SharedTypes layer starter (NEW-TYPEONLY-VOLUME). Copy the layer + allow rules into ark.config.json when type-only placement debt volume is high. Both application and presentation may import SharedTypes; SharedTypes must not import adapters or orchestration.",
3
+ "layer": {
4
+ "name": "SharedTypes",
5
+ "patterns": [
6
+ "src/types/**",
7
+ "src/shared/types/**",
8
+ "packages/shared/src/types/**",
9
+ "lib/types/**"
10
+ ],
11
+ "intentPrefixes": ["Shared.Types."]
12
+ },
13
+ "suggestedRules": [
14
+ { "from": "DomainModel", "to": "SharedTypes", "allowed": true },
15
+ { "from": "ApplicationOrchestration", "to": "SharedTypes", "allowed": true },
16
+ { "from": "PresentationAdapters", "to": "SharedTypes", "allowed": true },
17
+ { "from": "PersistenceAdapters", "to": "SharedTypes", "allowed": true },
18
+ { "from": "SharedTypes", "to": "DomainModel", "allowed": false },
19
+ { "from": "SharedTypes", "to": "ApplicationOrchestration", "allowed": false },
20
+ { "from": "SharedTypes", "to": "PresentationAdapters", "allowed": false },
21
+ { "from": "SharedTypes", "to": "PersistenceAdapters", "allowed": false }
22
+ ],
23
+ "guidance": [
24
+ "Type-only edges (`import type`) are placement debt — failsStrict:false; value edges still block merge.",
25
+ "Move pure type modules both sides need into SharedTypes (or an owning Domain surface).",
26
+ "Do not put runtime logic or I/O in SharedTypes.",
27
+ "Doctor violations.typeEdgePolicy and plan.typeOnlyGroup describe the same policy."
28
+ ]
29
+ }
@@ -99,6 +99,8 @@ Ark protects the **boundary around** a framework, not its internals. Nest/DI pub
99
99
  If the tree is `src/features` + `shared`/`lib` **without** FSD `entities`/`widgets`, prefer
100
100
  `vertical-slice` (or pack `enthusiast-vertical-slice`) — do **not** force hexagonal.
101
101
  If `src/contexts` or `src/bounded-contexts` exists, prefer `ddd-bounded-contexts`.
102
+ **Next.js:** `app/api/**` / `pages/api/**` (and route-group `app/(…)/api/**`) default to
103
+ **ApplicationOrchestration**, not Presentation — do not reclassify API shells as UI.
102
104
  2. **Check + diagnose** — `summary.concentrated` / dominant edge → fix contract first, don’t freeze.
103
105
  Cross-slice / cross-context `peerIsolation` hits are judgment: extract shared or events.
104
106
  If one edge dominates residual debt: **STOP — do not continue this skill as complete.** **STOP — concentrated edge: invoke /ark-contract with source evidence** (do not freeze a wrong contract or grind N freezes).
@@ -139,6 +139,11 @@ When explaining the **plan**, name the four `mechanical-safe` remediation kinds
139
139
  `import-type-of-type-exports` for named type exports from mixed modules; W6 port-proof inject is judgment when proof holds) — everything
140
140
  else is judgment/deferred and must not be auto-applied.
141
141
 
142
+ **Type-edge honesty:** type-only denied edges are **placement debt** (`typeOnly` +
143
+ `failsStrict: false`) — prefer SharedTypes / owning layer; they do not fail merge like value
144
+ edges. Doctor `productHonesty` and `violations.typeEdgePolicy` must not be sold as “all done”
145
+ when residual signals remain.
146
+
142
147
  ## Spoken / written explanation
143
148
 
144
149
  1. **Load the real contract**: `ark.config.json`, `ark://manifest` if available, `AGENTS.md`.
@@ -276,6 +276,25 @@ in parallel. When `pilotLoop.queuedBets > 0`, those bets stay **queued**, not co
276
276
  lines — never silent codemod of plan B. After the pilot lands, re-run doctor; success =
277
277
  reduced evidence on pilot paths.
278
278
 
279
+ ### Plan B one-pilot checklist (mandatory before any B apply)
280
+
281
+ Empty plan A + design-weak is **not** architecture finished. Plan B is judgment only.
282
+
283
+ | Step | Gate | Fail closed if… |
284
+ |------|------|-----------------|
285
+ | 1. Confirm residual | `designFitness.designWeak` **or** non-empty `patternBets` / `designSmells` | You would claim healthy finished because plan A is empty |
286
+ | 2. Pick **one** pilot | `pilotLoop.nextPilot` (preferred) **or** one ranked B bet with a path scope | Multi-pilot batch / “fix all smells this PR” |
287
+ | 3. Write extraction card | Pilot · Smell · Move · Do not · Success · **Kill-switch** · Next | Missing kill-switch or success signal |
288
+ | 4. User OK | Explicit approval before edit (or stay map-only) | Silent auto-apply of plan B |
289
+ | 5. Apply **only** that pilot | Via `/ark-autopilot` (apply B) or `/ark-fix` (one cluster) | Opening a second pilot before re-doctor |
290
+ | 6. Re-doctor | Success = reduced evidence on pilot paths; residual outside pilot may remain | Declaring whole-tree done from one pilot |
291
+
292
+ **Hard lines (never break):**
293
+ - `killSwitch` / kill-switch is **required** on every B card (stop condition if pilot does not reduce confusion).
294
+ - `multiPilotBatchForbidden` — never multi-batch Shape extractions.
295
+ - `autoApplyPlanBForbidden` / `neverMechanicalSafe: true` — never invent mechanical-safe for B.
296
+ - `healthyFinishedForbidden` while design-weak — empty plan A is baseline, not done.
297
+
279
298
  ```text
280
299
  ### Extraction card
281
300
  Pilot: <one dir or feature — or pilotLoop.nextPilot.pilotTarget>
@@ -286,8 +305,9 @@ Do not:
286
305
  - weaken ark.config.json
287
306
  - auto-apply as mechanical-safe or invent new mechanical-safe kinds
288
307
  - big-bang the monorepo
308
+ - open a second pilot before re-doctor
289
309
  Success: <observable / falsifiable — re-doctor>
290
- Kill-switch: <stop condition>
310
+ Kill-switch: <stop condition — e.g. if pilot does not clear smell evidence in 1 PR → stop / re-map>
291
311
  Next: /ark-autopilot (apply with user ok) | /ark-fix (one cluster) | re-doctor
292
312
  ```
293
313
 
@@ -19,16 +19,27 @@ the two files on the violation edge.
19
19
 
20
20
  When the fix is really a **Shape** extraction (I/O out of routes, god module split), write an
21
21
  **extraction card** before editing — same template as `docs/brownfield-adoption.md`
22
- §6 and explore §G. Fixed fields (never mechanical-safe, never silent B apply):
22
+ §6 and explore §G. Fixed fields (never mechanical-safe, never silent B apply).
23
+
24
+ ### Plan B one-pilot checklist (when Shape residual, not a single edge fix)
25
+
26
+ Empty plan A + `designWeak` is **not** architecture finished. One pilot only:
27
+
28
+ 1. **Confirm** doctor `designFitness.designWeak` / non-empty `patternBets` (or agent-detected smell).
29
+ 2. **One pilot** — prefer `pilotLoop.nextPilot`; never multi-batch concurrent Shape extractions.
30
+ 3. **Extraction card** with **Kill-switch** + falsifiable Success (required).
31
+ 4. **User OK** before apply — `autoApplyPlanBForbidden` / never mechanical-safe.
32
+ 5. **Edit only that pilot scope** — then `ark-check` / re-doctor; residual outside pilot may remain.
33
+ 6. **Stop** if kill-switch fires; do not open pilot #2 in the same batch.
23
34
 
24
35
  ```text
25
36
  ### Extraction card
26
- Pilot: <one directory or feature path>
37
+ Pilot: <one directory or feature path — or pilotLoop.nextPilot.pilotTarget>
27
38
  Smell: <doctor designSmells id if present>
28
39
  Move: <verbatim relocate / split>
29
- Do not: rewrite queries; weaken ark.config; invent mechanical-safe kinds; big-bang
30
- Success: <falsifiable>
31
- Kill-switch: <stop condition>
40
+ Do not: rewrite queries; weaken ark.config; invent mechanical-safe kinds; big-bang; multi-pilot batch
41
+ Success: <falsifiable — re-doctor on pilot paths>
42
+ Kill-switch: <stop condition — required>
32
43
  Next: re-run ark-check; shrink baseline if applicable
33
44
  ```
34
45