arkgate 3.0.0 → 3.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -1
- package/README.md +16 -14
- package/bin/ark-check.mjs +10 -2
- package/bin/lib/ci-and-commands.mjs +20 -15
- package/bin/lib/design-smells.mjs +434 -0
- package/bin/lib/doctor-plan.mjs +149 -16
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/docs/agent-guide.md +22 -1
- package/docs/ai-gates.md +7 -0
- package/docs/brownfield-adoption.md +52 -1
- package/docs/demos/03-copilot-autopilot.md +14 -7
- package/docs/package-surface.md +3 -1
- package/package.json +1 -1
- package/server.json +2 -2
- package/templates/skills/ark-adopt.md +26 -3
- package/templates/skills/ark-architect.md +7 -0
- package/templates/skills/ark-autopilot.md +37 -20
- package/templates/skills/ark-contract.md +7 -0
- package/templates/skills/ark-coverage.md +44 -45
- package/templates/skills/ark-explain.md +8 -0
- package/templates/skills/ark-explore.md +117 -47
- package/templates/skills/ark-fix.md +22 -0
- package/templates/skills/ark-loop.md +15 -1
- package/templates/skills/ark-place.md +7 -0
- package/templates/skills/ark-think.md +24 -20
package/bin/lib/doctor-plan.mjs
CHANGED
|
@@ -25,6 +25,12 @@ import {
|
|
|
25
25
|
violationEdge,
|
|
26
26
|
} from './violations.mjs';
|
|
27
27
|
import { buildUnclassifiedSuggestions } from './suggestions.mjs';
|
|
28
|
+
import {
|
|
29
|
+
detectDesignSmells,
|
|
30
|
+
buildPatternBetsFromSmells,
|
|
31
|
+
summarizeDesignFitness,
|
|
32
|
+
isDesignWeak,
|
|
33
|
+
} from './design-smells.mjs';
|
|
28
34
|
|
|
29
35
|
const color = {
|
|
30
36
|
green: (s) => `\x1b[32m${s}\x1b[0m`,
|
|
@@ -142,7 +148,25 @@ export function runCoverage(root, config, files, rules, asJson) {
|
|
|
142
148
|
// Co-pilot Phase F — turn active violations into a classified, ordered remediation PLAN with an
|
|
143
149
|
// embedded GOAL. This is the `plan` primitive the future apply-loop (Phase H, `loop`) consumes
|
|
144
150
|
// and the autopilot (Phase I) drives toward the `goal`. Read-only: it changes no files.
|
|
145
|
-
|
|
151
|
+
/**
|
|
152
|
+
* @param {string} root
|
|
153
|
+
* @param {object[]} activeViolations
|
|
154
|
+
* @param {number|null} [governedPercent]
|
|
155
|
+
* @param {number|null} [totalFiles]
|
|
156
|
+
* @param {object} [options]
|
|
157
|
+
* @param {object[]} [options.designSmells]
|
|
158
|
+
* @param {object[]} [options.patternBets]
|
|
159
|
+
* @param {object} [options.config]
|
|
160
|
+
* @param {string[]} [options.files]
|
|
161
|
+
* @param {object} [options.coverage]
|
|
162
|
+
*/
|
|
163
|
+
export function buildRemediationPlan(
|
|
164
|
+
root,
|
|
165
|
+
activeViolations,
|
|
166
|
+
governedPercent = null,
|
|
167
|
+
totalFiles = null,
|
|
168
|
+
options = {}
|
|
169
|
+
) {
|
|
146
170
|
// A plan with 0 violations but ~0% governed (or ZERO files in scope) is a FALSE green:
|
|
147
171
|
// nothing is actually being checked. Treat as "not done — classify / fix include first."
|
|
148
172
|
const governedLow = governedPercent != null && governedPercent < 50;
|
|
@@ -177,20 +201,54 @@ export function buildRemediationPlan(root, activeViolations, governedPercent = n
|
|
|
177
201
|
judgment: countOf('judgment'),
|
|
178
202
|
deferred: countOf('deferred'),
|
|
179
203
|
};
|
|
204
|
+
|
|
205
|
+
// Plan B (pattern bets) — never mechanical-safe; additive within major (P03).
|
|
206
|
+
let designSmells = options.designSmells;
|
|
207
|
+
if (!designSmells && options.config && options.files) {
|
|
208
|
+
designSmells = detectDesignSmells(
|
|
209
|
+
root,
|
|
210
|
+
options.config,
|
|
211
|
+
options.files,
|
|
212
|
+
options.coverage ?? null
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
designSmells = designSmells ?? [];
|
|
216
|
+
const patternBets =
|
|
217
|
+
options.patternBets ?? buildPatternBetsFromSmells(designSmells);
|
|
218
|
+
const edgesMet = activeViolations.length === 0 && !notHonestlyEnforced;
|
|
219
|
+
const designWeak = isDesignWeak(designSmells, {
|
|
220
|
+
activeViolations: activeViolations.length,
|
|
221
|
+
governedPercent,
|
|
222
|
+
totalFiles,
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
let statement =
|
|
226
|
+
activeViolations.length > 0
|
|
227
|
+
? `Resolve ${activeViolations.length} architecture violation(s) without weakening the contract.`
|
|
228
|
+
: emptyScope
|
|
229
|
+
? 'No source files matched the contract include paths — this "clean" result checks nothing. Fix include/layers (monorepo → apps/packages, or /ark-adopt) so Ark has real code to govern.'
|
|
230
|
+
: governedLow
|
|
231
|
+
? `No violations — but Ark governs only ${governedPercent}% of your code, so this "clean" result checks almost nothing. Classify the rest (ark-check --coverage, then /ark-adopt) so it's actually enforced.`
|
|
232
|
+
: 'No active violations — the architecture already meets its contract.';
|
|
233
|
+
if (designWeak) {
|
|
234
|
+
statement =
|
|
235
|
+
'No active edge violations — contract edges are clean, but design smells remain (ENFORCE · design-weak). Shape residual is plan B only; not healthy finished.';
|
|
236
|
+
}
|
|
237
|
+
|
|
180
238
|
return {
|
|
181
239
|
version: '1',
|
|
182
240
|
goal: {
|
|
183
|
-
statement
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
241
|
+
statement,
|
|
242
|
+
// Edge remediation termination (Phase H). Design-weak does NOT flip met false
|
|
243
|
+
// (would break loop semantics) — it is reported separately for honesty.
|
|
244
|
+
met: edgesMet,
|
|
245
|
+
designWeak,
|
|
246
|
+
...(designWeak
|
|
247
|
+
? {
|
|
248
|
+
designWeakLabel:
|
|
249
|
+
'ENFORCE · design-weak — use patternBets / dual-plan B; never auto-apply as mechanical-safe',
|
|
250
|
+
}
|
|
251
|
+
: {}),
|
|
194
252
|
...(governedPercent != null ? { governedPercent } : {}),
|
|
195
253
|
...(totalFiles != null ? { totalFiles } : {}),
|
|
196
254
|
...(emptyScope ? { emptyScope: true } : {}),
|
|
@@ -198,17 +256,38 @@ export function buildRemediationPlan(root, activeViolations, governedPercent = n
|
|
|
198
256
|
autoApplicable: counts.mechanicalSafe,
|
|
199
257
|
needsDecision: counts.judgment,
|
|
200
258
|
deferred: counts.deferred,
|
|
259
|
+
patternBetCount: patternBets.length,
|
|
201
260
|
},
|
|
202
261
|
counts,
|
|
203
262
|
steps,
|
|
263
|
+
// Additive: pattern evolution bets derived from design smells (never auto).
|
|
264
|
+
patternBets,
|
|
265
|
+
designSmells,
|
|
204
266
|
};
|
|
205
267
|
}
|
|
206
268
|
|
|
207
269
|
// `--plan`: print the classified remediation plan. Dual-focus output — a one-line headline
|
|
208
270
|
// anyone can read, then the per-step detail a developer acts on. Read-only.
|
|
209
|
-
|
|
210
|
-
|
|
271
|
+
/**
|
|
272
|
+
* @param {object} [options] optional { config, files, coverage, designSmells, patternBets }
|
|
273
|
+
*/
|
|
274
|
+
export function runPlan(
|
|
275
|
+
root,
|
|
276
|
+
activeViolations,
|
|
277
|
+
asJson,
|
|
278
|
+
governedPercent = null,
|
|
279
|
+
totalFiles = null,
|
|
280
|
+
options = {}
|
|
281
|
+
) {
|
|
282
|
+
const plan = buildRemediationPlan(
|
|
283
|
+
root,
|
|
284
|
+
activeViolations,
|
|
285
|
+
governedPercent,
|
|
286
|
+
totalFiles,
|
|
287
|
+
options
|
|
288
|
+
);
|
|
211
289
|
// Honesty: a zero-violation plan with almost nothing governed is NOT "ok".
|
|
290
|
+
// design-weak still ok:true for edge goal.met, but JSON carries designWeak + patternBets.
|
|
212
291
|
const planOk = plan.goal.met === true;
|
|
213
292
|
if (asJson) {
|
|
214
293
|
console.log(JSON.stringify({ ok: planOk, plan }, null, 2));
|
|
@@ -217,6 +296,13 @@ export function runPlan(root, activeViolations, asJson, governedPercent = null,
|
|
|
217
296
|
console.log(color.bold(`Ark plan — ${path.basename(path.resolve(root)) || '.'}`));
|
|
218
297
|
console.log('');
|
|
219
298
|
console.log(plan.goal.statement);
|
|
299
|
+
if (plan.goal.designWeak) {
|
|
300
|
+
console.log(
|
|
301
|
+
color.yellow(
|
|
302
|
+
` ENFORCE · design-weak — ${plan.patternBets?.length ?? 0} pattern bet(s) (never auto-apply)`
|
|
303
|
+
)
|
|
304
|
+
);
|
|
305
|
+
}
|
|
220
306
|
if (governedPercent != null) {
|
|
221
307
|
const pctLabel =
|
|
222
308
|
governedPercent < 50
|
|
@@ -224,6 +310,14 @@ export function runPlan(root, activeViolations, asJson, governedPercent = null,
|
|
|
224
310
|
: color.dim(`Governed: ${governedPercent}% of in-scope files`);
|
|
225
311
|
console.log(pctLabel);
|
|
226
312
|
}
|
|
313
|
+
if (plan.patternBets?.length && activeViolations.length === 0) {
|
|
314
|
+
console.log('');
|
|
315
|
+
console.log(color.bold('Pattern bets (B) — judgment only'));
|
|
316
|
+
for (const bet of plan.patternBets.slice(0, 5)) {
|
|
317
|
+
console.log(` [decide] ${bet.smellId} ${color.dim(bet.pilot)}`);
|
|
318
|
+
console.log(color.dim(` success: ${bet.successSignal}`));
|
|
319
|
+
}
|
|
320
|
+
}
|
|
227
321
|
if (activeViolations.length === 0) return plan;
|
|
228
322
|
console.log('');
|
|
229
323
|
console.log(
|
|
@@ -245,7 +339,7 @@ export function runPlan(root, activeViolations, asJson, governedPercent = null,
|
|
|
245
339
|
console.log('');
|
|
246
340
|
console.log(
|
|
247
341
|
color.dim(
|
|
248
|
-
'Plan only — no files changed. "auto" = an agent can safely apply it; "decide" = your call.'
|
|
342
|
+
'Plan only — no files changed. "auto" = an agent can safely apply it; "decide" = your call. patternBets are never auto.'
|
|
249
343
|
)
|
|
250
344
|
);
|
|
251
345
|
return plan;
|
|
@@ -283,6 +377,12 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
|
|
|
283
377
|
const activeCount = violations.length - suppressed;
|
|
284
378
|
const missingSkills = skillGaps.reduce((sum, gap) => sum + gap.missing, 0);
|
|
285
379
|
const staleSkills = skillGaps.reduce((sum, gap) => sum + gap.stale, 0);
|
|
380
|
+
const designSmells = detectDesignSmells(root, config, files, cov);
|
|
381
|
+
const designFitness = summarizeDesignFitness(designSmells, {
|
|
382
|
+
activeViolations: activeCount,
|
|
383
|
+
governedPercent: cov.governed.percent,
|
|
384
|
+
totalFiles: cov.governed.totalFiles,
|
|
385
|
+
});
|
|
286
386
|
|
|
287
387
|
if (asJson) {
|
|
288
388
|
console.log(
|
|
@@ -304,6 +404,9 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
|
|
|
304
404
|
return p ? p.files / total : null;
|
|
305
405
|
})(),
|
|
306
406
|
}),
|
|
407
|
+
// Path-correct ENFORCE can still be design-weak (P02).
|
|
408
|
+
designFitness,
|
|
409
|
+
designSmells,
|
|
307
410
|
governed: cov.governed,
|
|
308
411
|
emptyLayers: cov.emptyLayers,
|
|
309
412
|
layersWithoutRules: cov.layersWithoutRules,
|
|
@@ -412,7 +515,18 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
|
|
|
412
515
|
enforce:
|
|
413
516
|
'Guard — contract coverage is honest and checked edges are clean. You do not pick this mode; you arrived here. Next: keep the host-appropriate write path and CI check on; only NEW violations should fail.',
|
|
414
517
|
};
|
|
415
|
-
|
|
518
|
+
const modeTitle =
|
|
519
|
+
mode === 'enforce' && designFitness.designWeak
|
|
520
|
+
? 'ENFORCE · design-weak'
|
|
521
|
+
: mode.toUpperCase();
|
|
522
|
+
line(
|
|
523
|
+
modeMark,
|
|
524
|
+
`${modeTitle} — ${
|
|
525
|
+
designFitness.designWeak
|
|
526
|
+
? 'Guard on edges is honest, but design smells remain (Shape residual). You do not pick this mode. Next: /ark-explore dual-plan B or /ark-autopilot for pattern bets — never treat empty plan A as healthy finished.'
|
|
527
|
+
: modeHelp[mode]
|
|
528
|
+
}`
|
|
529
|
+
);
|
|
416
530
|
if (emptyScope) {
|
|
417
531
|
line(
|
|
418
532
|
bad,
|
|
@@ -420,6 +534,25 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
|
|
|
420
534
|
);
|
|
421
535
|
}
|
|
422
536
|
|
|
537
|
+
console.log('');
|
|
538
|
+
console.log(color.bold('Design fitness'));
|
|
539
|
+
if (designSmells.length === 0) {
|
|
540
|
+
line(ok, designFitness.label);
|
|
541
|
+
} else {
|
|
542
|
+
line(designFitness.designWeak ? warn : warn, designFitness.label);
|
|
543
|
+
for (const smell of designSmells.slice(0, 5)) {
|
|
544
|
+
line(' ', color.dim(`[${smell.id}] ${smell.message}`));
|
|
545
|
+
if (smell.evidence?.length) {
|
|
546
|
+
line(' ', color.dim(`evidence: ${smell.evidence.slice(0, 4).join(', ')}`));
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
if (designFitness.designWeak) {
|
|
550
|
+
actions.push(
|
|
551
|
+
'shape residual: /ark-explore (shape-focus) or /ark-autopilot dual-plan B — pattern bets are never mechanical-safe'
|
|
552
|
+
);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
423
556
|
console.log('');
|
|
424
557
|
console.log(color.bold('Coverage'));
|
|
425
558
|
const govMark =
|
package/dist/index.cjs
CHANGED
package/dist/index.d.cts
CHANGED
|
@@ -2,7 +2,7 @@ import { a as ArkConfigRule, b as ArkConfigLayer, A as ArkConfig, c as ArkConfig
|
|
|
2
2
|
export { d as ARK_CONFIG_SCHEMA, e as ARK_CONFIG_SCHEMA_VERSION, l as loadArkConfigContract, p as parseArkConfigJson } from './configContract-BxSIwVRo.cjs';
|
|
3
3
|
|
|
4
4
|
/** ArkGate library version — single source of truth. */
|
|
5
|
-
declare const version = "3.0.
|
|
5
|
+
declare const version = "3.0.2";
|
|
6
6
|
|
|
7
7
|
/** Versioned public result contract shared by every ArkGate enforcement adapter. */
|
|
8
8
|
declare const ARK_ANALYSIS_RESULT_SCHEMA_VERSION: "1.0";
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { a as ArkConfigRule, b as ArkConfigLayer, A as ArkConfig, c as ArkConfig
|
|
|
2
2
|
export { d as ARK_CONFIG_SCHEMA, e as ARK_CONFIG_SCHEMA_VERSION, l as loadArkConfigContract, p as parseArkConfigJson } from './configContract-BxSIwVRo.js';
|
|
3
3
|
|
|
4
4
|
/** ArkGate library version — single source of truth. */
|
|
5
|
-
declare const version = "3.0.
|
|
5
|
+
declare const version = "3.0.2";
|
|
6
6
|
|
|
7
7
|
/** Versioned public result contract shared by every ArkGate enforcement adapter. */
|
|
8
8
|
declare const ARK_ANALYSIS_RESULT_SCHEMA_VERSION: "1.0";
|
package/dist/index.js
CHANGED
package/docs/agent-guide.md
CHANGED
|
@@ -75,10 +75,31 @@ To remove a compact host integration, preview `ark start --remove-host <host>` a
|
|
|
75
75
|
only after review. Ark removes only its exact compact artifacts, leaves customized files untouched
|
|
76
76
|
as unresolved decisions, and restores the integration with `ark start --tools <host> --apply`.
|
|
77
77
|
|
|
78
|
+
**Skill roles (avoid overlap):** `/ark-explore` = map + dual-plan **seed** + Shape residual
|
|
79
|
+
(no apply). `/ark-coverage` = Ark **fitness** only (governed/gates). `/ark-think` = one decision
|
|
80
|
+
(2–3 options). `/ark-adopt` = brownfield Align/Stabilize + seed Shape B. `/ark-autopilot` =
|
|
81
|
+
explore then apply A + propose/apply-with-ok B. `/ark-loop` = plan A only. Empty plan A is not
|
|
82
|
+
“architecture healthy” if design-weak residual remains. Full routing table: full-install
|
|
83
|
+
`AGENTS.md` / [README skill table](../README.md#other-skills-only-when-you-need-them).
|
|
84
|
+
|
|
85
|
+
**Design fitness (3.0.1+):** after edges are clean, doctor can still report **ENFORCE · design-weak**.
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
npx ark-check --doctor --json # doctor.designFitness + doctor.designSmells[]
|
|
89
|
+
npx ark-check --plan --json # plan.goal.designWeak + plan.patternBets[] (never mechanical-safe)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Smell ids (stable): `io-under-application`, `handler-in-persistence`, `god-module`,
|
|
93
|
+
`domain-logic-in-ui`, `facade-sql-in-routes`, `mixed-pattern-cluster`, `soft-contract`.
|
|
94
|
+
Each has `evidence[]` paths. Plan **B** bets include `pilot`, `successSignal`, `killSwitch`,
|
|
95
|
+
and **`neverMechanicalSafe: true`** — loop/autoPatch must ignore them. For judgment I/O moves
|
|
96
|
+
use **extraction cards** ([brownfield-adoption.md](brownfield-adoption.md) §6). Multi-PR residual
|
|
97
|
+
may optionally be persisted as a short Shape plan under the repo; not a gate requirement.
|
|
98
|
+
|
|
78
99
|
**Full-skill agent co-pilot:** after explicitly installing the `/ark-*` pack, use
|
|
79
100
|
`/ark-autopilot` (explore-first, dual plan A remediation + B pattern bets). Recon without
|
|
80
101
|
applying: `/ark-explore`. The default compact router uses MCP/CLI directly. Never treat empty
|
|
81
|
-
`--plan` as “architecture healthy”
|
|
102
|
+
`--plan` steps as “architecture healthy” when `designWeak` / non-empty `patternBets` remain.
|
|
82
103
|
|
|
83
104
|
`ark init --archetype <id>` maps playbook ids to named presets (`hexagonal`, `layered`,
|
|
84
105
|
`feature-sliced`, `monorepo`). With `--yes` and no archetype, Ark auto-selects from
|
package/docs/ai-gates.md
CHANGED
|
@@ -50,6 +50,13 @@ you pass `--force`, so review and commit only the templates that match your proj
|
|
|
50
50
|
`autoPatch` flags), the supported profile for the active host, and the evidence actually found.
|
|
51
51
|
Supported capability and installed guarantee are deliberately separate.
|
|
52
52
|
|
|
53
|
+
**Design fitness (3.0.1+):** the same doctor JSON may include `doctor.designFitness` and
|
|
54
|
+
`doctor.designSmells[]` (path evidence). Edge-clean `operatingMode: enforce` can still set
|
|
55
|
+
`designFitness.designWeak: true` (**ENFORCE · design-weak**). That is Shape residual, not a
|
|
56
|
+
write-path failure. Companion plan JSON: `plan.patternBets[]` with `neverMechanicalSafe: true`
|
|
57
|
+
— never treat as write-boundary `autoPatch` / mechanical-safe. See
|
|
58
|
+
[package-surface.md](package-surface.md) and [brownfield-adoption.md](brownfield-adoption.md) §6.
|
|
59
|
+
|
|
53
60
|
If your project uses Codex or Grok, treat MCP registration as part of the default
|
|
54
61
|
setup, not an optional extra. Ark works best when the agent can read `ark://manifest`
|
|
55
62
|
before it writes code; that is the fast path to avoiding architecture drift during
|
|
@@ -86,10 +86,61 @@ violations — the ratchet only moves toward zero.
|
|
|
86
86
|
`/ark-fix` resolves each cluster at the root cause; fixing a frozen violation shrinks the
|
|
87
87
|
baseline permanently. Re-freeze lower with `--update-baseline` as you go.
|
|
88
88
|
|
|
89
|
+
## 6. Shape residual — extraction cards (judgment assist, P05)
|
|
90
|
+
|
|
91
|
+
When edges are green (`ark-check --plan` has empty `steps[]`) but doctor reports
|
|
92
|
+
**ENFORCE · design-weak** (`designSmells` / `patternBets`), you are in **Shape** work. Plan A
|
|
93
|
+
is done; plan **B** is not auto-applicable.
|
|
94
|
+
|
|
95
|
+
Use one **extraction card** per pilot (I/O relocate, god-module split, domain-out-of-UI).
|
|
96
|
+
Agents and humans fill the same fields — never invent a codemod engine or silent apply:
|
|
97
|
+
|
|
98
|
+
```text
|
|
99
|
+
### Extraction card
|
|
100
|
+
Pilot: <one directory or feature path>
|
|
101
|
+
Smell: <doctor designSmells[].id or agent-detected id>
|
|
102
|
+
Move: <what moves, e.g. query bytes verbatim → OrderRepository adapter>
|
|
103
|
+
Do not:
|
|
104
|
+
- rewrite queries / touch schema / migrations
|
|
105
|
+
- weaken ark.config.json to silence the smell
|
|
106
|
+
- auto-apply as mechanical-safe or invent new mechanical-safe kinds
|
|
107
|
+
- big-bang the whole monorepo
|
|
108
|
+
Success: <falsifiable signal, e.g. 0 routes import @prisma/client>
|
|
109
|
+
Kill-switch: <when to stop, e.g. if 2 PRs still confuse ownership → stop layer add>
|
|
110
|
+
Next: /ark-fix (one cluster) | /ark-autopilot (user ok on B) | /ark-explore shape-focus
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
CLI sensors:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
ark-check --doctor --json # designFitness.designWeak + designSmells[].evidence
|
|
117
|
+
ark-check --plan --json # patternBets[] with neverMechanicalSafe: true
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Fixture for CI honesty: `tests/fixtures/design-weak-enforce/` (empty plan A + non-empty B).
|
|
121
|
+
|
|
122
|
+
### Optional: durable Shape plan (multi-PR)
|
|
123
|
+
|
|
124
|
+
CLI `patternBets` and extraction cards are enough for a single session. If residual spans
|
|
125
|
+
**multiple PRs or agents**, optionally persist one human-readable plan under the repo
|
|
126
|
+
(e.g. `docs/plans/shape-<pilot>/README.md` or any team path) with:
|
|
127
|
+
|
|
128
|
+
| Field | Source |
|
|
129
|
+
|-------|--------|
|
|
130
|
+
| Phase | Align / Stabilize / **Shape** |
|
|
131
|
+
| Golden vs legacy patterns | explore concurrent-patterns table |
|
|
132
|
+
| Smell ids / patternBets | `ark-check --doctor --json` / `--plan --json` |
|
|
133
|
+
| Extraction cards | §6 template above |
|
|
134
|
+
| Status of pilot | e.g. dual path (legacy + new) → real (only golden) when smells clear |
|
|
135
|
+
|
|
136
|
+
This is **optional narrative**, not a gate. Ark does not require a docs skill or a fixed
|
|
137
|
+
folder layout. Prefer one authority plan; promote or archive it when the pilot is real.
|
|
138
|
+
|
|
89
139
|
## What Ark does NOT do here
|
|
90
140
|
|
|
91
141
|
Ark reorganizes and governs code — it never touches your data model. Migrating raw SQL to a
|
|
92
142
|
repository moves the same query to another file; the schema, migrations, and the database are
|
|
93
143
|
untouched. And the burn-down itself is the team's work (or a codemod, or an agent loop) — Ark
|
|
94
144
|
diagnoses, orders it, and gives you the pattern; it doesn't auto-run hundreds of edits against
|
|
95
|
-
your restricted data layer.
|
|
145
|
+
your restricted data layer. **Extraction cards are judgment assists only** — no general codemod
|
|
146
|
+
and no silent auto-apply of plan B.
|
|
@@ -35,17 +35,22 @@ the layer globs so day-one **governed%** is real (not a false-green empty contra
|
|
|
35
35
|
### 2. See the plan yourself (optional)
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
|
-
npx ark-check --plan # human view (includes Governed: N
|
|
39
|
-
npx ark-check --plan --json # { ok, plan: { goal, counts, steps } }
|
|
38
|
+
npx ark-check --plan # human view (includes Governed: N%; pattern bets when design-weak)
|
|
39
|
+
npx ark-check --plan --json # { ok, plan: { goal, counts, steps, patternBets?, designSmells? } }
|
|
40
|
+
npx ark-check --doctor --json # designFitness / designSmells when residual is design-weak
|
|
40
41
|
```
|
|
41
42
|
|
|
42
|
-
Each step is tagged `mechanical-safe` / `judgment` / `deferred` with a `confidence`,
|
|
43
|
+
Each **A** step is tagged `mechanical-safe` / `judgment` / `deferred` with a `confidence`,
|
|
43
44
|
`rationale`, and often `remediationKind`. Auto-safe kinds: type-only type move, pure-type **file**
|
|
44
45
|
relocate, `import type` of pure-type modules, and named type-export imports from mixed modules
|
|
45
46
|
(`import-type-of-type-exports`). `goal.met` is true only when
|
|
46
47
|
there are no active violations **and** governed coverage is meaningful — so a clean plan that
|
|
47
48
|
checks almost nothing is not "done."
|
|
48
49
|
|
|
50
|
+
When edges are clean but design residual remains, JSON also sets `goal.designWeak` and
|
|
51
|
+
`patternBets[]` (each with `neverMechanicalSafe: true`). Those are **B** (Shape) bets — not
|
|
52
|
+
auto-applied. Extraction cards: [brownfield-adoption.md](../brownfield-adoption.md) §6.
|
|
53
|
+
|
|
49
54
|
### 3. Carry the plan out — the autopilot
|
|
50
55
|
|
|
51
56
|
In your agent, run:
|
|
@@ -54,10 +59,12 @@ In your agent, run:
|
|
|
54
59
|
/ark-autopilot
|
|
55
60
|
```
|
|
56
61
|
|
|
57
|
-
It runs the whole flow (newbie tier):
|
|
58
|
-
`mechanical-safe` steps one at a time — **validating each with
|
|
59
|
-
regression** — proposes each `judgment`
|
|
60
|
-
what was auto-applied vs proposed vs deferred.
|
|
62
|
+
It runs the whole flow (newbie tier): **explore first** (map + dual plan), hands off to
|
|
63
|
+
`/ark-loop` for plan **A** `mechanical-safe` steps one at a time — **validating each with
|
|
64
|
+
`ark-check` and rolling back any regression** — proposes each A `judgment` and each B
|
|
65
|
+
pattern/Shape bet for a yes/no, and reports what was auto-applied vs proposed vs deferred.
|
|
66
|
+
Nothing lands until you review the diff. Empty A with open B is **not** “architecture healthy
|
|
67
|
+
finished.”
|
|
61
68
|
|
|
62
69
|
Expert entry: skip the autopilot and use the pieces — `ark init` / `/ark-contract` to shape the
|
|
63
70
|
contract, `ark-check --plan` for the work, `/ark-fix` for targeted fixes, `ark-check
|
package/docs/package-surface.md
CHANGED
|
@@ -15,11 +15,13 @@ This document is the consumer contract for **what is stable** vs **what is exper
|
|
|
15
15
|
| Surface | How you use it | Stability notes |
|
|
16
16
|
|---------|----------------|-----------------|
|
|
17
17
|
| **CLI** | `arkgate` / `arkgate-check` (aliases `ark` / `ark-check`) | Flags and human text may improve; **JSON output shapes** for `--json` (check, doctor, plan, coverage, recommend) are stable within a major. Additive fields OK; removals/renames are major. |
|
|
18
|
+
| **Doctor design fitness (P02+)** | `ark-check --doctor --json` → `doctor.designFitness`, `doctor.designSmells[]` | Additive. Stable smell `id`s: `io-under-application`, `handler-in-persistence`, `god-module`, `domain-logic-in-ui`, `facade-sql-in-routes`, `mixed-pattern-cluster`, `soft-contract`. Each smell has `evidence[]` paths and `fix`. Does **not** fail the gate by itself. |
|
|
19
|
+
| **Plan pattern B (P03+)** | `ark-check --plan --json` → `plan.patternBets[]`, `plan.goal.designWeak` | Additive. Each bet: `id`, `smellId`, `pilot`, `evidence`, `successSignal`, `killSwitch`, **`neverMechanicalSafe: true`**, `class: "judgment"`. **Never** auto-applied by loop/autoPatch; not a `remediationKind` mechanical-safe. `goal.met` remains edge honesty only. |
|
|
18
20
|
| **MCP tools** | `arkgate-mcp` / `ark://…` resources | Tool names and primary argument shapes are stable within a major. |
|
|
19
21
|
| **`ark.config.json`** | Layer globs, rules, include/exclude, forbiddenGlobals, intent prefixes, `peerIsolation`, `dynamicImportAllowlist`, `safety` thresholds | Versioned by `schemaVersion`; unknown fields fail closed and migrations preserve the previous supported major. |
|
|
20
22
|
| **`arkgate/schema/analysis-result`** | Public CLI/MCP/hook diagnostic envelope (`schemaVersion`, `valid`, `diagnostics`) | Versioned JSON Schema; committed v1 compatibility fixture protects rule, severity, location, and evidence fields. |
|
|
21
23
|
| **Config JSON Schema** | `arkgate/schema` or `arkgate/schema/ark.config.schema.json` | Stable package resource subpaths for editor completion and contract tooling. |
|
|
22
|
-
| **Agent skills** | `/ark-*` templates installed by `--install-agent-gates` | Skill *names* and “default flow” are stable; internal skill prose may evolve (e.g. explore dual-plan seed, day-zero origin order). |
|
|
24
|
+
| **Agent skills** | `/ark-*` templates installed by `--install-agent-gates` | Skill *names* and “default flow” are stable; internal skill prose may evolve (e.g. When/not when, explore Shape dual-plan seed, extraction cards, day-zero origin order). |
|
|
23
25
|
| **ESLint subpath** | `arkgate/eslint` | Config-driven layer/import rules; loads consumer `ark.config.json`. |
|
|
24
26
|
| **GitHub Action** | `pedroknigge/arkgate` (see `action.yml`) | The `uses:` tag/SHA selects the checker source; `version` remains an optional exact npm compatibility override. |
|
|
25
27
|
|
package/package.json
CHANGED
package/server.json
CHANGED
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
"url": "https://github.com/pedroknigge/arkgate",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "3.0.
|
|
9
|
+
"version": "3.0.2",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "arkgate",
|
|
14
|
-
"version": "3.0.
|
|
14
|
+
"version": "3.0.2",
|
|
15
15
|
"runtimeHint": "npx",
|
|
16
16
|
"transport": {
|
|
17
17
|
"type": "stdio"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ark-adopt
|
|
3
|
-
description: Brownfield onboarding —
|
|
3
|
+
description: Brownfield onboarding — match contract to real product code, classify ungoverned dirs, mine business rules, freeze only real debt, seed Shape dual-plan B for spaghetti residual. Deep source analysis required.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# /ark-adopt — Bring Ark into an existing codebase
|
|
@@ -8,6 +8,18 @@ description: Brownfield onboarding — exploratory match of contract to real pro
|
|
|
8
8
|
Goal: contract reflects **product reality**, most code governed, only genuine debt frozen
|
|
9
9
|
with a burn-down. A green check over a wrong contract is a **false green**.
|
|
10
10
|
|
|
11
|
+
**Adopt is Align + Stabilize, then seed Shape.** Freezing debt without a pattern plan leaves
|
|
12
|
+
spaghetti “ENFORCE · design-weak”. Always end with dual-plan **B** seeds (or handoff explore)
|
|
13
|
+
when design smells remain after the contract is honest.
|
|
14
|
+
|
|
15
|
+
## When / not when
|
|
16
|
+
|
|
17
|
+
| Use `/ark-adopt` when… | Do **not** use it when… |
|
|
18
|
+
|------------------------|-------------------------|
|
|
19
|
+
| Existing messy repo; contract ≠ folders | Empty greenfield shape → `/ark-architect` |
|
|
20
|
+
| False-green / concentrated edge needs contract truth | Map-only without writing config/baseline → `/ark-explore` |
|
|
21
|
+
| Mine loose business rules into Domain / intents | Single violation fix → `/ark-fix` |
|
|
22
|
+
| Freeze **real** debt after contract is honest | Grind plan A only → `/ark-loop`; full apply loop → `/ark-autopilot` |
|
|
11
23
|
|
|
12
24
|
## Dual engine (mandatory)
|
|
13
25
|
|
|
@@ -78,22 +90,33 @@ Ark protects the **boundary around** a framework, not its internals. Nest/DI pub
|
|
|
78
90
|
- Deliver section **“Así te lo re-soluciono en el manifiesto”** with before/after contract snippets.
|
|
79
91
|
5. **Freeze only real debt** — `--update-baseline` (zero debt → **no empty baseline file** left behind).
|
|
80
92
|
6. **Gates + skills** — `--install-agent-gates` (CI monorepo-aware when `frontend/package.json` exists).
|
|
81
|
-
7. **Ratchet +
|
|
93
|
+
7. **Ratchet + Shape seed (mandatory exploratory close)** — after freeze/gates:
|
|
94
|
+
- Name phase: **Align** (contract honesty) → **Stabilize** (baseline real) → **Shape** (golden pattern).
|
|
95
|
+
- If plan A is empty but the tree still shows concurrent patterns, god modules, facade SQL,
|
|
96
|
+
domain logic in UI, or semantic false-green: emit **dual-plan B** (3–5 bets) with pilot,
|
|
97
|
+
success signal, kill-switch, and extraction cards for I/O moves — same bar as `/ark-explore` §G.
|
|
98
|
+
- Do **not** claim “adopt complete / healthy” solely because the check is green.
|
|
99
|
+
- Prefer handoff `/ark-autopilot` for B execution with user ok, or `/ark-explore` shape-focus
|
|
100
|
+
if the user only wanted a plan.
|
|
82
101
|
|
|
83
102
|
## Operating modes
|
|
84
103
|
|
|
85
104
|
Explain modes as **detected stages** (Setup / Align / Guard), not user settings.
|
|
105
|
+
**Guard on the contract ≠ Shape done.** Say `ENFORCE · design-weak` when B residual remains.
|
|
86
106
|
|
|
87
107
|
## Verify
|
|
88
108
|
|
|
89
109
|
`ark-check --root . --config ark.config.json --strict-config` (+ baseline only if non-empty file retained).
|
|
90
|
-
Report: governed% before/after, files written, frozen count, false positives avoided, manifest/intent
|
|
110
|
+
Report: governed% before/after, files written, frozen count, false positives avoided, manifest/intent
|
|
111
|
+
proposals applied or deferred, **phase**, **top Shape / design-weak opportunities still open**
|
|
112
|
+
(with success signals).
|
|
91
113
|
|
|
92
114
|
## Never
|
|
93
115
|
|
|
94
116
|
- Freeze false positives to get green.
|
|
95
117
|
- Force runtime kernel over existing Nest/DI.
|
|
96
118
|
- Claim Enforce while governed% is low, cores empty with I/O in Application, or core bags ungoverned.
|
|
119
|
+
- End adopt with only “baseline written” when design-weak residual is visible in files you opened.
|
|
97
120
|
|
|
98
121
|
## Completion contract (skill incomplete if missing)
|
|
99
122
|
|
|
@@ -5,6 +5,13 @@ description: Choose the application shape, adopt phase-1 layers, scaffold direct
|
|
|
5
5
|
|
|
6
6
|
# /ark-architect — Choose your application shape and adopt Ark
|
|
7
7
|
|
|
8
|
+
## When / not when
|
|
9
|
+
|
|
10
|
+
| Use `/ark-architect` when… | Do **not** use it when… |
|
|
11
|
+
|----------------------------|-------------------------|
|
|
12
|
+
| Greenfield / thin tree; pick shape + phase-1 layers | Existing spaghetti brownfield → `/ark-adopt` (+ `/ark-explore` first if map missing) |
|
|
13
|
+
| Enthusiast before heavy codegen | Enforcement residual on mature tree → `/ark-autopilot` |
|
|
14
|
+
|
|
8
15
|
The user is building something new or early in Ark adoption. They may not know
|
|
9
16
|
layered architecture jargon. Your job: translate **what they want to build**
|
|
10
17
|
(application shape, not framework name) into an Ark preset, a phase-1 layer plan,
|