arkgate 4.5.7 → 4.6.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 (83) hide show
  1. package/CHANGELOG.md +48 -1
  2. package/README.md +18 -12
  3. package/bin/ark-check-runtime.mjs +107 -8
  4. package/bin/ark-mcp-runtime.mjs +17 -1
  5. package/bin/ark.mjs +5 -3
  6. package/bin/lib/agent-homes.mjs +296 -0
  7. package/bin/lib/agent-projection.mjs +2 -2
  8. package/bin/lib/analysis-engine.mjs +3 -3
  9. package/bin/lib/ci-and-commands.mjs +16 -16
  10. package/bin/lib/config-contract.mjs +2 -0
  11. package/bin/lib/contract-smells.mjs +5 -5
  12. package/bin/lib/design-smells.mjs +4 -8
  13. package/bin/lib/doctor-advisories.mjs +9 -0
  14. package/bin/lib/doctor-plan.mjs +39 -15
  15. package/bin/lib/field-install.mjs +1 -1
  16. package/bin/lib/golden-pattern.mjs +1 -1
  17. package/bin/lib/host-support-matrix.mjs +4 -4
  18. package/bin/lib/html-report-advisories.mjs +20 -0
  19. package/bin/lib/html-report-depth.mjs +7 -8
  20. package/bin/lib/html-report.mjs +2 -1
  21. package/bin/lib/improvement-compass-map.mjs +20 -20
  22. package/bin/lib/install-migrate.mjs +12 -0
  23. package/bin/lib/pilot-loop.mjs +1 -1
  24. package/bin/lib/post-green-path.mjs +5 -4
  25. package/bin/lib/prepare-change.mjs +9 -0
  26. package/bin/lib/product-copy.mjs +32 -0
  27. package/bin/lib/skill-write.mjs +1 -1
  28. package/bin/lib/status-command.mjs +14 -0
  29. package/bin/lib/status-manifest.mjs +17 -0
  30. package/bin/lib/team-parliament-io.mjs +338 -0
  31. package/bin/lib/team-parliament.mjs +383 -0
  32. package/bin/lib/upgrade-whats-new.mjs +32 -0
  33. package/bin/lib/violations.mjs +8 -4
  34. package/dist/{configTypes-CC0FEXoF.d.ts → configTypes-B8uIcLaG.d.ts} +5 -0
  35. package/dist/eslint/index.cjs +2 -2
  36. package/dist/eslint/index.d.ts +1 -1
  37. package/dist/eslint/index.js +2 -2
  38. package/dist/index.cjs +8 -8
  39. package/dist/index.d.ts +61 -2
  40. package/dist/index.js +8 -8
  41. package/docs/README.md +5 -4
  42. package/docs/agent-guide.md +31 -24
  43. package/docs/ai-gates.md +19 -0
  44. package/docs/configuration.md +36 -2
  45. package/docs/develop.md +16 -4
  46. package/docs/enthusiast/README.md +1 -1
  47. package/docs/enthusiast/how-to-agent-gates.md +3 -3
  48. package/docs/enthusiast/how-to-pick-shape.md +2 -2
  49. package/docs/enthusiast/tutorial-first-project.md +4 -3
  50. package/docs/package-surface.md +5 -3
  51. package/docs/product-voice.md +126 -82
  52. package/docs/use.md +13 -6
  53. package/package.json +1 -1
  54. package/schemas/ark.config.schema.json +9 -0
  55. package/schemas/ark.status-manifest.schema.json +51 -0
  56. package/server.json +2 -2
  57. package/templates/agent-skills/README.md +1 -1
  58. package/templates/agent-skills/ark-adopt/SKILL.md +46 -19
  59. package/templates/agent-skills/ark-architect/SKILL.md +24 -145
  60. package/templates/agent-skills/ark-autopilot/SKILL.md +40 -30
  61. package/templates/agent-skills/ark-contract/SKILL.md +21 -105
  62. package/templates/agent-skills/ark-coverage/SKILL.md +3 -0
  63. package/templates/agent-skills/ark-explain/SKILL.md +7 -4
  64. package/templates/agent-skills/ark-explore/SKILL.md +29 -21
  65. package/templates/agent-skills/ark-fix/SKILL.md +34 -157
  66. package/templates/agent-skills/ark-loop/SKILL.md +31 -153
  67. package/templates/agent-skills/ark-place/SKILL.md +25 -13
  68. package/templates/agent-skills/ark-runtime/SKILL.md +3 -3
  69. package/templates/agent-skills/ark-think/SKILL.md +5 -2
  70. package/templates/agent-skills/ark-upgrade/SKILL.md +13 -8
  71. package/templates/skills/ark-adopt.md +46 -19
  72. package/templates/skills/ark-architect.md +24 -145
  73. package/templates/skills/ark-autopilot.md +40 -30
  74. package/templates/skills/ark-contract.md +21 -105
  75. package/templates/skills/ark-coverage.md +3 -0
  76. package/templates/skills/ark-explain.md +7 -4
  77. package/templates/skills/ark-explore.md +29 -21
  78. package/templates/skills/ark-fix.md +34 -157
  79. package/templates/skills/ark-loop.md +31 -153
  80. package/templates/skills/ark-place.md +25 -13
  81. package/templates/skills/ark-runtime.md +3 -3
  82. package/templates/skills/ark-think.md +5 -2
  83. package/templates/skills/ark-upgrade.md +13 -8
package/CHANGELOG.md CHANGED
@@ -5,6 +5,53 @@ in the immutable pre-2.0 archive linked below.
5
5
 
6
6
  ## Unreleased
7
7
 
8
+ ## 4.6.1 — 2026-08-14
9
+
10
+ **Patch** over **4.6.0**. Five-door autonomy (skills write or map in-turn; CLI is sensor +
11
+ gate) plus team parliament (law vs feature: stewards, mixed-PR deny, ratchet vs the merge
12
+ base, cheap `--changed` check). Same 13 skill names. Steward identity is a GitHub handle or
13
+ email, not git `user.name`. **No required config migration.**
14
+
15
+ **Status: prepared** (not yet on npm `latest`; see `docs/releases/4.6.1.md`).
16
+
17
+ ### Added
18
+
19
+ - **Five-door autonomy (SK01–SK05):** `/ark-adopt`, `/ark-place`, `/ark-autopilot`,
20
+ `/ark-explore`, `/ark-upgrade` write or map in the same turn. The other eight names stay
21
+ installed as shortcuts. Invoking a door is the approval; the CLI does not apply the change.
22
+ - **Team parliament (TW01–TW08):** mixed law+product deny; optional `stewards` (GitHub handle
23
+ or email); `--contract-session` / `--contract-diff` / `--changed --base` / `--against` /
24
+ `--persona` / `--author`; `ark status --vs`; doctor `stewardNudge` (ask or show list drift).
25
+ `stewards` is excluded from the policy hash.
26
+
27
+ ### Changed
28
+
29
+ - Doctor, compact router, and public lanes prefer the five doors. Historical changelogs stay
30
+ as shipped.
31
+
32
+ ## 4.6.0 — 2026-08-12
33
+
34
+ **Minor** over **4.5.7**. Understandable Ark: doctor, HTML, skills, and public docs use common
35
+ software words (import rules, leftover design work, pre-write block) while **ArkGate** and
36
+ **ArkRules** stay as product names. Shared Claude/Grok agent homes get the same monotonic
37
+ “always latest” floor Codex already had. **No required config migration.** JSON field names
38
+ and `ruleId`s stay stable. No new skill names, sensors, or scores.
39
+
40
+ **Status: published** (on npm `latest`; see `docs/releases/4.6.0.md`).
41
+
42
+ ### Added
43
+
44
+ - **Shared Claude/Grok home skills:** `--claude-home`, `--grok-home`, and `--agent-homes`
45
+ write monotonic home catalogs (Codex-parity lock + floor). Doctor reports stale
46
+ `~/.claude/skills` / `~/.grok/skills` when those catalogs exist. Temp/upgrade `--root`
47
+ never mutates default user homes.
48
+
49
+ ### Changed
50
+
51
+ - **Human language:** doctor, HTML report, compact router, skills, and public lanes prefer
52
+ common terms. Leftover design work replaces “design-weak” in human copy; JSON `designWeak`
53
+ is unchanged.
54
+
8
55
  ## 4.5.7 — 2026-08-12
9
56
 
10
57
  **Patch** over **4.5.6**. Cursor hard write path: project `.cursor/hooks.json` `preToolUse`
@@ -13,7 +60,7 @@ gate for `Write` / `StrReplace` (failClosed), host matrix + doctor honesty, payl
13
60
  emit; Write `updated_input` reinjection is **not** claimed. Codex/OpenCode stay advisory at
14
61
  write. Shell/Tab/human edits still rely on required CI. **No required config migration.**
15
62
 
16
- **Status: prepared** (not published).
63
+ **Status: published** (on npm `latest`; see `docs/releases/4.5.7.md`).
17
64
 
18
65
  ### Added
19
66
 
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # ArkGate — Architecture Co-pilot for AI TypeScript
4
4
 
5
- **One contract. One gate. One co-pilot.**
5
+ **One architecture config. One check. One coach.**
6
6
 
7
7
  Your AI writes most of the code. ArkGate keeps that work inside an architecture you can trust —
8
8
  and makes sure a “green” check means something real.
@@ -16,8 +16,8 @@ and makes sure a “green” check means something real.
16
16
 
17
17
  </div>
18
18
 
19
- > **ArkGate 4.5.6** is on npm `latest` — field upgrade truth, multi-project MCP honesty, skill drift.
20
- > [4.5.6 notes](docs/releases/4.5.6.md) · [4.5.5](docs/releases/4.5.5.md) · [4.5.0](docs/releases/4.5.0.md) · [Docs hub](docs/README.md) · [Product voice](docs/product-voice.md)
19
+ > **ArkGate 4.6.0** is on npm `latest` — clearer language + shared agent home skills.
20
+ > [4.6.0 notes](docs/releases/4.6.0.md) · [4.5.7](docs/releases/4.5.7.md) · [4.5.6](docs/releases/4.5.6.md) · [Docs hub](docs/README.md) · [Product voice](docs/product-voice.md)
21
21
 
22
22
  ---
23
23
 
@@ -43,15 +43,18 @@ npx arkgate-check --doctor # control plane: status light + primary next a
43
43
  ```
44
44
 
45
45
  That is the product. Doctor is the control plane — when stuck, do **primary next action #1**.
46
- Doctor also shows an **improvement compass** (architecture lenses such as separation of concerns and
47
- dependency inversion — **not a score**). Residual lenses mean Shape work may remain even when edges
48
- are green. Details: [use.md — Improvement compass](docs/use.md#improvement-compass-not-a-score).
46
+ Doctor also shows **what to improve next** (architecture lenses such as separation of concerns and
47
+ dependency inversion — **not a score**). Leftover lenses mean design work may remain even when
48
+ imports are green. Details: [use.md — Improvement compass](docs/use.md#improvement-compass-not-a-score).
49
49
 
50
50
  ```text
51
- start → doctor (+ compass) → day-to-day (place + gate)
52
- optional /ark-autopilot after skill pack
51
+ start → doctor (+ compass) → /ark-adopt (session 0) → day-to-day /ark-place
52
+ /ark-explore then /ark-autopilot when leftover design remains
53
53
  ```
54
54
 
55
+ Teams: keep the constitution out of product PRs. Local gate
56
+ `ark-check --changed --base origin/dev`. Steward law PRs use `--contract-session`.
57
+
55
58
  Aliases `ark` / `ark-check` / `ark-mcp` still work. npm / pnpm / yarn. No install lifecycle scripts.
56
59
 
57
60
  ![Write gate: agent blocked, then self-corrects](docs/assets/ark-write-gate.svg)
@@ -64,15 +67,15 @@ A machine-readable architecture file (`ark.config.json`) plus enforcement:
64
67
 
65
68
  | When | Tool |
66
69
  |------|------|
67
- | **While the AI writes** | Hard PreToolUse on supported hosts; advisory MCP elsewhere |
70
+ | **While the AI writes** | Pre-write block on supported hosts; warning only elsewhere |
68
71
  | **Before merge** | `arkgate-check` as a **required** CI status |
69
72
 
70
73
  ### Two planes (4.0)
71
74
 
72
75
  | Plane | What it guards | Config |
73
76
  |-------|----------------|--------|
74
- | **Layers** (always) | Who may talk to whom — imports, placement, purity, isolation | `ark.config.json` layers + rules |
75
- | **ArkRules** (opt-in) | Habits *inside* a layer — structure sensors + domain invariants as data | `arkRules` → `arkrules/<Layer>.json` |
77
+ | **Layers** (always) | Who may import whom — imports, placement, purity, isolation | `ark.config.json` layers + rules |
78
+ | **ArkRules** (opt-in; structure rules inside a layer) | Habits *inside* a layer — structure sensors + domain invariants as data | `arkRules` → `arkrules/<Layer>.json` |
76
79
 
77
80
  Absence of ArkRules changes no inter-layer verdict. Label residual **`[Layer]`** vs **`[ArkRules]`**.
78
81
  Details: [configuration](docs/configuration.md#arkrules-intra-layer-opt-in) · [use path](docs/use.md).
@@ -175,6 +178,8 @@ npx arkgate-check --plan
175
178
  npx arkgate-check --coverage
176
179
  npx arkgate-check --strict-merge # CI / required status
177
180
  npx arkgate-check --install-agent-gates --tools claude,cursor,codex,grok
181
+ # optional: refresh shared home skills (Claude/Grok/Codex; never downgrades)
182
+ # npx arkgate-check --install-agent-gates --skills-only --agent-homes --force
178
183
  # optional: same 13 skills via Agent Skills ecosystem (no new names)
179
184
  # npx skills add ./node_modules/arkgate/templates/agent-skills
180
185
  ```
@@ -208,7 +213,8 @@ for real systems. Details: [docs/production-hardening.md](docs/production-harden
208
213
  | Config · package surface · TS | [configuration](docs/configuration.md) · [package-surface](docs/package-surface.md) · [typescript-support](docs/typescript-support.md) |
209
214
  | Brownfield | [docs/brownfield-adoption.md](docs/brownfield-adoption.md) |
210
215
  | Security | [SECURITY.md](SECURITY.md) |
211
- | Current release (4.5.6 on npm `latest`) | [docs/releases/4.5.6.md](docs/releases/4.5.6.md) · [CHANGELOG](CHANGELOG.md) |
216
+ | Current release (4.6.0 on npm `latest`) | [docs/releases/4.6.0.md](docs/releases/4.6.0.md) · [CHANGELOG](CHANGELOG.md) |
217
+ | Prior (4.5.7) | [docs/releases/4.5.7.md](docs/releases/4.5.7.md) |
212
218
  | Prior (4.5.0) | [docs/releases/4.5.0.md](docs/releases/4.5.0.md) |
213
219
  | Prior (4.4.0) | [docs/releases/4.4.0.md](docs/releases/4.4.0.md) |
214
220
  | Prior (4.3.0) | [docs/releases/4.3.0.md](docs/releases/4.3.0.md) |
@@ -103,6 +103,13 @@ import { ANALYSIS_COMPLETENESS, analysisIncompleteStatement } from './lib/analys
103
103
  import { reportUnavailableAnalysis } from './lib/unavailable-analysis.mjs';
104
104
  import { validateHardWriteRequest } from './lib/enforcement-profiles.mjs';
105
105
  import { analyzePolicyTransition } from './lib/policy-delta-io.mjs';
106
+ import {
107
+ applyAgainstRatchet,
108
+ bindTeamBaseRefs,
109
+ filterChangedGovernedFiles,
110
+ runTeamPreflight,
111
+ ungovernedDumpMessage,
112
+ } from './lib/team-parliament-io.mjs';
106
113
  import { tryResidentDoctor } from './lib/resident-doctor-client.mjs';
107
114
  import { createDesignDeltaCheck } from './lib/design-delta.mjs';
108
115
  import {
@@ -134,6 +141,14 @@ function parseArgs(argv) {
134
141
  policyBase: undefined,
135
142
  policyBaseRef: undefined,
136
143
  policyAck: undefined, failOnNewSmells: false, baseRef: undefined,
144
+ contractSession: false,
145
+ contractDiff: false,
146
+ changed: false,
147
+ against: undefined,
148
+ base: undefined,
149
+ persona: undefined,
150
+ author: undefined,
151
+ failUngoverned: false,
137
152
  updateBaseline: false,
138
153
  noCache: false,
139
154
  resident: false,
@@ -213,6 +228,14 @@ function parseArgs(argv) {
213
228
  else if (arg === '--watch') args.watch = true;
214
229
  else if (arg === '--beginner') args.beginner = true;
215
230
  else if (arg === '--codex-home') args.codexHome = true;
231
+ else if (arg === '--claude-home') args.claudeHome = true;
232
+ else if (arg === '--grok-home') args.grokHome = true;
233
+ else if (arg === '--agent-homes') {
234
+ args.agentHomes = true;
235
+ args.codexHome = true;
236
+ args.claudeHome = true;
237
+ args.grokHome = true;
238
+ }
216
239
  else if (arg === '--migrate-commands') args.migrateCommands = true;
217
240
  else if (arg === '--no-cache') args.noCache = true;
218
241
  else if (arg === '--resident') args.resident = true;
@@ -233,6 +256,13 @@ function parseArgs(argv) {
233
256
  else if (arg === '--policy-base') args.policyBase = requireValue(arg, i++);
234
257
  else if (arg === '--policy-base-ref') args.policyBaseRef = requireValue(arg, i++);
235
258
  else if (arg === '--policy-ack') args.policyAck = requireValue(arg, i++); else if (arg === '--fail-on-new-smells') args.failOnNewSmells = true; else if (arg === '--base-ref') args.baseRef = requireValue(arg, i++);
259
+ else if (arg === '--contract-session') args.contractSession = true;
260
+ else if (arg === '--contract-diff') args.contractDiff = true;
261
+ else if (arg === '--changed') args.changed = true;
262
+ else if (arg === '--against') args.against = requireValue(arg, i++);
263
+ else if (arg === '--base') args.base = requireValue(arg, i++);
264
+ else if (arg === '--persona') args.persona = requireValue(arg, i++);
265
+ else if (arg === '--author') args.author = requireValue(arg, i++);
236
266
  else if (arg === '--root') args.root = path.resolve(requireValue(arg, i++));
237
267
  else if (arg === '--config') args.config = requireValue(arg, i++);
238
268
  else if (arg === '--manifest') args.manifest = requireValue(arg, i++);
@@ -257,7 +287,7 @@ function usage() {
257
287
  return [
258
288
  'Usage: arkgate-check | ark-check (identical bins; product name ArkGate)',
259
289
  ' ark-check --version',
260
- ' ark-check --root <project> --config <ark.config.json> [--manifest <ark.manifest.json>] [--tsconfig <tsconfig.json>] [--strict-merge | --strict | --strict-config] [--policy-base <file> | --policy-base-ref <git-ref>] [--policy-ack <file>] [--fail-on-new-smells --base-ref <git-ref>] [--require-gates] [--require-write-hook <host>] [--json] [--baseline [file]] [--report [file.html]] [--no-cache]',
290
+ ' ark-check --root <project> --config <ark.config.json> [--manifest <ark.manifest.json>] [--tsconfig <tsconfig.json>] [--strict-merge | --strict | --strict-config] [--policy-base <file> | --policy-base-ref <git-ref>] [--policy-ack <file>] [--fail-on-new-smells --base-ref <git-ref>] [--contract-diff] [--contract-session] [--changed] [--against <git-ref>] [--base <git-ref>] [--persona touch|contributor|agent|steward] [--author <id>] [--require-gates] [--require-write-hook <host>] [--json] [--baseline [file]] [--report [file.html]] [--no-cache]',
261
291
  ' ark-check --doctor [--json] [--resident] [--fail-on-new-smells --base-ref <git-ref>] read-only diagnosis; resident JSON falls back cold',
262
292
  ' ark-check --coverage [--json] per-layer file counts + full unclassified list (report only, exit 0)',
263
293
  ' ark-check --plan [--json] classified remediation plan (mechanical-safe / judgment / deferred) + goal; report only',
@@ -275,7 +305,7 @@ function usage() {
275
305
  ' Best-effort open in browser (local TTY). No-op if open fails. --no-open / ARK_NO_OPEN_REPORT=1 to skip; --open forces open.',
276
306
  ' ark-check --init [--preset hexagonal|layered|feature-sliced|monorepo|ui-surface|vertical-slice|ddd-bounded-contexts|vite-vercel-spa|clean-architecture|onion-architecture] [--force] [--follow-config-root]',
277
307
  ' --follow-config-root On writes (init/install-agent-gates/migrate --write/…), adopt walked-up monorepo config root (default: keep explicit --root)',
278
- ' ark-check --install-agent-gates [--tools claude,cursor,codex,grok] [--require-write-hook <host>] [--skills-only] [--codex-home] [--force]',
308
+ ' ark-check --install-agent-gates [--tools claude,cursor,codex,grok] [--require-write-hook <host>] [--skills-only] [--codex-home] [--claude-home] [--grok-home] [--agent-homes] [--force]',
279
309
  ' ark-check --update-baseline [file] freeze current violations (default .ark-baseline.json)',
280
310
  ' ark-check --print-config eleven-layer',
281
311
  '',
@@ -283,6 +313,11 @@ function usage() {
283
313
  'violations, commit the baseline file, and gate CI with --baseline: only NEW violations',
284
314
  'fail the check, so the ratchet only moves toward zero.',
285
315
  '',
316
+ 'Team parliament: law files (ark.config / arkrules / .ark-baseline.json) cannot ship in',
317
+ 'the same diff as product source. --changed --base <ref> checks touched files only.',
318
+ '--against <ref> ratchets new keys vs that ref\'s baseline. --contract-session is a',
319
+ 'steward law-only PR. Loosen / baseline-grow need stewards[] + --author when set.',
320
+ '',
286
321
  '--init scans the project for the built-in layer directory conventions (src/domain,',
287
322
  'src/application, src/adapters/persistence, ...) and writes an ark.config.json covering',
288
323
  'only the layers that actually exist, with the default rules filtered to those layers.',
@@ -538,7 +573,7 @@ function runApplyPolicyPack(args) {
538
573
 
539
574
  if (fs.existsSync(configPath) && !args.force) {
540
575
  console.error(
541
- `${configPath} already exists. Re-run with --force to overwrite, or use /ark-contract to evolve it.`
576
+ `${configPath} already exists. Re-run with --force to overwrite, or use /ark-adopt to evolve it.`
542
577
  );
543
578
  process.exitCode = 2;
544
579
  return;
@@ -1022,7 +1057,7 @@ function runInit(args) {
1022
1057
  console.log('A green check ignores this code; it is not "clean", it is unchecked.');
1023
1058
  if (recognized.length > 0) {
1024
1059
  console.log('');
1025
- console.log('Proposed layer for each (from the 11-layer profile + presets — apply via /ark-contract):');
1060
+ console.log('Proposed layer for each (from the 11-layer profile + presets — apply via /ark-adopt):');
1026
1061
  for (const p of recognized) {
1027
1062
  const alt = p.alternatives?.length ? ` (or ${p.alternatives.join(' / ')} — confirm)` : '';
1028
1063
  console.log(` ${p.dir}/ → ${p.layer}${alt}`);
@@ -1273,19 +1308,71 @@ async function main() {
1273
1308
  }
1274
1309
 
1275
1310
  const root = args.root;
1311
+ const bound = bindTeamBaseRefs(args, root);
1312
+ Object.assign(args, bound.args);
1276
1313
  const config = readConfig(root, args.config);
1277
1314
  const policyDelta = analyzePolicyTransition({
1278
1315
  root,
1279
1316
  configPath: args.config,
1280
1317
  candidateConfig: config,
1281
- strictMerge: args.strictMerge,
1318
+ strictMerge: args.strictMerge || args.contractDiff,
1282
1319
  basePath: args.policyBase,
1283
1320
  baseRef: args.policyBaseRef,
1284
1321
  acknowledgementPath: args.policyAck,
1285
1322
  });
1323
+ const preflight = runTeamPreflight({
1324
+ root,
1325
+ args,
1326
+ config,
1327
+ policyDelta,
1328
+ teamBase: bound.teamBase,
1329
+ });
1330
+ const teamParliament = preflight.teamParliament;
1331
+ const changedPaths = preflight.changedPaths;
1332
+ if (preflight.halt) {
1333
+ if (args.json) {
1334
+ console.log(
1335
+ JSON.stringify(
1336
+ {
1337
+ ok: preflight.halt.exitCode === 0,
1338
+ ...(preflight.halt.cheap ? { cheap: true } : {}),
1339
+ teamParliament: preflight.halt.teamParliament,
1340
+ ...(policyDelta ? { policyDelta } : {}),
1341
+ },
1342
+ null,
1343
+ 2
1344
+ )
1345
+ );
1346
+ } else if (preflight.halt.exitCode === 0) {
1347
+ console.log('✔ Ark check passed (no governed source or constitution files in the diff).');
1348
+ } else {
1349
+ console.error(preflight.halt.message);
1350
+ }
1351
+ process.exitCode = preflight.halt.exitCode;
1352
+ return;
1353
+ }
1286
1354
  const manifest = readManifest(root, args.manifest);
1287
1355
  const rules = manifest?.architecture?.rules ?? config.rules;
1288
- const files = collectGovernedFiles(root, config);
1356
+ const allGovernedFiles = collectGovernedFiles(root, config);
1357
+ if (args.failUngoverned && teamParliament?.changeSet?.productPaths?.length) {
1358
+ const governedRel = new Set(
1359
+ allGovernedFiles.map((abs) => normalize(path.relative(root, abs)))
1360
+ );
1361
+ const dumped = teamParliament.changeSet.productPaths.filter((rel) => !governedRel.has(rel));
1362
+ if (dumped.length > 0) {
1363
+ const message = ungovernedDumpMessage(dumped);
1364
+ if (args.json) {
1365
+ console.log(JSON.stringify({ ok: false, teamParliament: { ...teamParliament, ungoverned: dumped }, message }, null, 2));
1366
+ } else {
1367
+ console.error(message);
1368
+ }
1369
+ process.exitCode = 1;
1370
+ return;
1371
+ }
1372
+ }
1373
+ const files = args.changed
1374
+ ? filterChangedGovernedFiles(allGovernedFiles, root, changedPaths, normalize)
1375
+ : allGovernedFiles;
1289
1376
 
1290
1377
  // --coverage is a pure glob/report view (no TypeScript resolver), so serve it BEFORE the
1291
1378
  // TS import: the report must work — and exit 0 — even when typescript isn't installed.
@@ -1373,7 +1460,7 @@ async function main() {
1373
1460
  printViolationBreakdown(summary, { toStderr: true });
1374
1461
  console.error('');
1375
1462
  console.error('Freezing this would bury a likely CONTRACT bug as "debt". Fix the contract');
1376
- console.error('first (/ark-contract), then re-run. To freeze anyway: --update-baseline --force.');
1463
+ console.error('first (/ark-adopt), then re-run. To freeze anyway: --update-baseline --force.');
1377
1464
  process.exitCode = 2;
1378
1465
  return;
1379
1466
  }
@@ -1422,7 +1509,18 @@ async function main() {
1422
1509
  let suppressed = [];
1423
1510
  let activeViolations = violations;
1424
1511
  let staleBaselineKeys = 0;
1425
- if (args.baseline) {
1512
+ if (args.against) {
1513
+ const ratcheted = applyAgainstRatchet({
1514
+ violations,
1515
+ againstRef: args.against,
1516
+ root,
1517
+ changed: args.changed,
1518
+ changedPaths,
1519
+ occurrenceKeys: baselineOccurrenceKeys(violations),
1520
+ });
1521
+ activeViolations = ratcheted.activeViolations;
1522
+ suppressed = ratcheted.suppressed;
1523
+ } else if (args.baseline) {
1426
1524
  const baseline = readBaseline(root, args.baseline);
1427
1525
  if (baseline.exists) {
1428
1526
  const occurrenceKeys = baselineOccurrenceKeys(violations);
@@ -1747,6 +1845,7 @@ async function main() {
1747
1845
  }
1748
1846
  : {}),
1749
1847
  ...(codexRepoSkillGap ? { codexRepoSkillGap } : {}), ...(policyDelta ? { policyDelta } : {}), ...(designDelta ? { edgeValid: edgeOk, designDelta } : {}),
1848
+ ...(teamParliament ? { teamParliament } : {}),
1750
1849
  }, null, 2));
1751
1850
  } else {
1752
1851
  for (const warning of warnings) {
@@ -73,6 +73,8 @@ import {
73
73
  } from './lib/prepare-change.mjs';
74
74
  import { detectWritePathCapabilities } from './lib/write-path-detect.mjs';
75
75
  import { collectGovernedFiles, isGovernableSourceFile } from './lib/scan-files.mjs';
76
+ import { classifyChangeSet, evaluateTeamGate } from './lib/team-parliament.mjs';
77
+ import { contractSessionFrom } from './lib/team-parliament-io.mjs';
76
78
  import {
77
79
  canonicalizeCandidateChanges,
78
80
  resolvedCompilerInputPaths,
@@ -657,6 +659,20 @@ function runHookPayload(payload, gate, config, args, ts, attemptContext, output
657
659
  return;
658
660
  }
659
661
  const patchWrites = parsedPatch.writes;
662
+ const patchChangeSet = classifyChangeSet(patchWrites.map((change) => String(change.path)));
663
+ const patchLawGate = evaluateTeamGate({
664
+ changeSet: patchChangeSet,
665
+ contractSession: contractSessionFrom({}),
666
+ });
667
+ if (patchLawGate.deny && patchChangeSet.mixed) {
668
+ const message = patchLawGate.message;
669
+ output.stderr(`Ark architecture gate: ${message}\n`);
670
+ if (grokStyle) {
671
+ output.stdout(`${JSON.stringify({ decision: 'deny', reason: message })}\n`);
672
+ }
673
+ output.status(2);
674
+ return;
675
+ }
660
676
  const sourceWrites = patchWrites.filter((change) =>
661
677
  isGovernableSourceFile(path.basename(String(change.path)))
662
678
  );
@@ -1387,7 +1403,7 @@ function printSessionContext(config, profile, forbiddenGlobals, args, configPath
1387
1403
  const governedPercent = coverage?.coverage?.governed?.percent ?? coverage?.governed?.percent;
1388
1404
  if (shouldShowNewHereNudge(args.root, configPath, governedPercent, false)) {
1389
1405
  lines.push('');
1390
- lines.push('New to Ark? Run /ark-architect or: ark-check --recommend');
1406
+ lines.push('New to Ark? Run /ark-adopt (or /ark-architect) or: ark-check --recommend');
1391
1407
  }
1392
1408
 
1393
1409
  process.stdout.write(`${lines.join('\n')}\n`);
package/bin/ark.mjs CHANGED
@@ -138,6 +138,7 @@ function parseArgs(argv) {
138
138
  }
139
139
  else if (arg === '--expected-root') args.expectedRoot = path.resolve(requireValue(arg, i++));
140
140
  else if (arg === '--expected-project-id') args.expectedProjectId = requireValue(arg, i++);
141
+ else if (arg === '--vs') args.vs = requireValue(arg, i++);
141
142
  else if (arg === '--write') args.write = true;
142
143
  else if (arg === '--check') args.check = true;
143
144
  else if (arg === '--stdout') args.stdout = true;
@@ -157,7 +158,7 @@ function usage() {
157
158
  [--archetype <playbook-id>] [--tools <list>] [--require-write-hook <host>] [--yes] [--force] [--no-strict]
158
159
  ark upgrade [--root <project>] [--tools <list>] [--apply] [--plan-digest <sha256>] [--accept-conflicts] [--refresh-skills] [--json] [--no-install] [--no-strict]
159
160
  ark preflight --changes <change-set.json> [--change-map <map.json>] [--root <project>] [--config ark.config.json] [--manifest <manifest.json>] [--tsconfig <tsconfig.json>] [--json]
160
- ark status [--root <project>] [--config ark.config.json] [--json]
161
+ ark status [--root <project>] [--config ark.config.json] [--json] [--vs <git-ref>]
161
162
  [--expected-root <abs>] [--expected-project-id sha256:…] [--tools <host>]
162
163
  ark agents-md [--root <project>] [--config ark.config.json] [--write] [--check] [--stdout] [--json]
163
164
  [--tools <host>]
@@ -796,7 +797,7 @@ async function start(args) {
796
797
  console.log('');
797
798
  console.log('Next (the only flow you need):');
798
799
  if (falseGreenGap) {
799
- console.log(' 1. In your agent: /ark-adopt (or /ark-contract) — fix the contract first');
800
+ console.log(' 1. In your agent: /ark-adopt — fix the architecture config first');
800
801
  console.log(' → reclassify I/O dirs out of Application; then /ark-autopilot for residual debt.');
801
802
  } else {
802
803
  console.log(' 1. In your agent: /ark-autopilot');
@@ -811,7 +812,7 @@ async function start(args) {
811
812
  }
812
813
  console.log('');
813
814
  console.log('Optional later: ark-check --report ark-report.html (captures a day-zero/evolution report).');
814
- console.log('Optional later: --plan · --coverage · /ark-explore · /ark-fix · /ark-place · ark upgrade');
815
+ console.log('Optional later: --plan · --coverage · /ark-explore · /ark-autopilot · /ark-place · ark upgrade');
815
816
  return 0;
816
817
  } finally {
817
818
  rl?.close();
@@ -941,6 +942,7 @@ async function main() {
941
942
  expectedProjectId: args.expectedProjectId,
942
943
  host: args.tools,
943
944
  arkgateVersion: cliVersion(),
945
+ vs: args.vs,
944
946
  });
945
947
  }
946
948