arkgate 4.6.0 → 4.6.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 +54 -1
- package/README.md +11 -5
- package/bin/ark-check-runtime.mjs +115 -128
- package/bin/ark-mcp-runtime.mjs +86 -48
- package/bin/ark.mjs +21 -78
- package/bin/lib/agent-projection.mjs +1 -1
- package/bin/lib/analysis-engine.mjs +3 -3
- package/bin/lib/ci-and-commands.mjs +11 -11
- package/bin/lib/config-contract.mjs +2 -0
- package/bin/lib/contract-smells.mjs +5 -5
- package/bin/lib/design-smells.mjs +1 -1
- package/bin/lib/doctor-advisories.mjs +9 -0
- package/bin/lib/doctor-next-actions.mjs +92 -0
- package/bin/lib/doctor-plan.mjs +61 -71
- package/bin/lib/field-install.mjs +1 -1
- package/bin/lib/first-run-help.mjs +221 -0
- package/bin/lib/html-report-advisories.mjs +20 -0
- package/bin/lib/improvement-compass-map.mjs +20 -20
- package/bin/lib/pilot-loop.mjs +1 -1
- package/bin/lib/post-green-path.mjs +2 -2
- package/bin/lib/prepare-change.mjs +9 -0
- package/bin/lib/product-copy.mjs +1 -1
- package/bin/lib/start-preview.mjs +17 -10
- package/bin/lib/status-command.mjs +19 -0
- package/bin/lib/status-manifest.mjs +23 -0
- package/bin/lib/team-parliament-io.mjs +338 -0
- package/bin/lib/team-parliament.mjs +383 -0
- package/bin/lib/upgrade-whats-new.mjs +16 -0
- package/bin/lib/violations.mjs +8 -4
- package/dist/{configTypes-CC0FEXoF.d.ts → configTypes-B8uIcLaG.d.ts} +5 -0
- package/dist/eslint/index.cjs +2 -2
- package/dist/eslint/index.d.ts +1 -1
- package/dist/eslint/index.js +2 -2
- package/dist/index.cjs +8 -8
- package/dist/index.d.ts +66 -2
- package/dist/index.js +8 -8
- package/docs/README.md +5 -5
- package/docs/agent-guide.md +21 -17
- package/docs/configuration.md +36 -2
- package/docs/develop.md +13 -1
- package/docs/enthusiast/README.md +1 -1
- package/docs/enthusiast/how-to-agent-gates.md +3 -3
- package/docs/enthusiast/how-to-pick-shape.md +2 -2
- package/docs/enthusiast/tutorial-first-project.md +4 -3
- package/docs/package-surface.md +5 -3
- package/docs/product-voice.md +48 -10
- package/docs/use.md +10 -5
- package/package.json +2 -2
- package/schemas/ark.config.schema.json +9 -0
- package/schemas/ark.status-manifest.schema.json +51 -0
- package/server.json +3 -3
- package/templates/agent-skills/README.md +1 -1
- package/templates/agent-skills/ark-adopt/SKILL.md +59 -23
- package/templates/agent-skills/ark-architect/SKILL.md +24 -145
- package/templates/agent-skills/ark-autopilot/SKILL.md +49 -32
- package/templates/agent-skills/ark-contract/SKILL.md +21 -105
- package/templates/agent-skills/ark-coverage/SKILL.md +7 -3
- package/templates/agent-skills/ark-explain/SKILL.md +8 -4
- package/templates/agent-skills/ark-explore/SKILL.md +38 -21
- package/templates/agent-skills/ark-fix/SKILL.md +34 -157
- package/templates/agent-skills/ark-loop/SKILL.md +31 -153
- package/templates/agent-skills/ark-place/SKILL.md +35 -14
- package/templates/agent-skills/ark-runtime/SKILL.md +3 -3
- package/templates/agent-skills/ark-think/SKILL.md +6 -2
- package/templates/agent-skills/ark-upgrade/SKILL.md +21 -10
- package/templates/skills/ark-adopt.md +59 -23
- package/templates/skills/ark-architect.md +24 -145
- package/templates/skills/ark-autopilot.md +49 -32
- package/templates/skills/ark-contract.md +21 -105
- package/templates/skills/ark-coverage.md +7 -3
- package/templates/skills/ark-explain.md +8 -4
- package/templates/skills/ark-explore.md +38 -21
- package/templates/skills/ark-fix.md +34 -157
- package/templates/skills/ark-loop.md +31 -153
- package/templates/skills/ark-place.md +35 -14
- package/templates/skills/ark-runtime.md +3 -3
- package/templates/skills/ark-think.md +6 -2
- package/templates/skills/ark-upgrade.md +21 -10
|
@@ -311,12 +311,32 @@ function deepModuleCoachHtml(coach) {
|
|
|
311
311
|
</section>`;
|
|
312
312
|
}
|
|
313
313
|
|
|
314
|
+
function stewardNudgeHtml(nudge) {
|
|
315
|
+
if (!nudge || nudge.notAScore !== true) return '';
|
|
316
|
+
const ask =
|
|
317
|
+
(nudge.needsStewards || nudge.drift) && typeof nudge.ask === 'string' && nudge.ask
|
|
318
|
+
? `<p>${esc(nudge.ask)}</p>`
|
|
319
|
+
: '<p class="muted">No steward list gap (advisory).</p>';
|
|
320
|
+
const next =
|
|
321
|
+
typeof nudge.nextAction === 'string' && nudge.nextAction
|
|
322
|
+
? `<p class="muted">Next: ${esc(nudge.nextAction)}</p>`
|
|
323
|
+
: '';
|
|
324
|
+
return `
|
|
325
|
+
<section class="section card" data-advisory="stewardNudge">
|
|
326
|
+
<h2>Stewards <span class="muted">(advisory — never changes the verdict)</span></h2>
|
|
327
|
+
${ask}
|
|
328
|
+
${next}
|
|
329
|
+
<p class="muted">GitHub handle or email. Never invent names. Always <code>notAScore</code>.</p>
|
|
330
|
+
</section>`;
|
|
331
|
+
}
|
|
332
|
+
|
|
314
333
|
export function renderAdvisorySections(advisories, escape) {
|
|
315
334
|
if (!advisories || typeof advisories !== 'object') return '';
|
|
316
335
|
if (typeof escape === 'function') esc = escape;
|
|
317
336
|
return [
|
|
318
337
|
improvementCompassHtml(advisories.improvementCompass),
|
|
319
338
|
deepModuleCoachHtml(advisories.deepModuleCoach),
|
|
339
|
+
stewardNudgeHtml(advisories.stewardNudge),
|
|
320
340
|
contractHealthHtml(advisories.contractHealth),
|
|
321
341
|
ambientStateHtml(advisories.ambientState),
|
|
322
342
|
physicalCohesionHtml(advisories.physicalCohesion),
|
|
@@ -118,11 +118,11 @@ function mapDesignSmells(byId, smells) {
|
|
|
118
118
|
const shapeAction = {
|
|
119
119
|
kind: 'skill',
|
|
120
120
|
ref: '/ark-explore',
|
|
121
|
-
summary: 'Map Shape residual (shape-focus), then one extraction pilot with
|
|
121
|
+
summary: 'Map Shape residual (shape-focus), then apply one extraction pilot with /ark-autopilot.',
|
|
122
122
|
};
|
|
123
123
|
const dipAction = {
|
|
124
124
|
kind: 'skill',
|
|
125
|
-
ref: '/ark-
|
|
125
|
+
ref: '/ark-autopilot',
|
|
126
126
|
summary: 'Inject a port/adapter for I/O; keep domain pure.',
|
|
127
127
|
};
|
|
128
128
|
switch (id) {
|
|
@@ -147,7 +147,7 @@ function mapDesignSmells(byId, smells) {
|
|
|
147
147
|
attach('srp', 'God modules own too many responsibilities — split by concern (one pilot).', {
|
|
148
148
|
kind: 'skill',
|
|
149
149
|
ref: '/ark-autopilot',
|
|
150
|
-
summary: 'One Shape pilot
|
|
150
|
+
summary: 'One Shape pilot this turn — never multi-pilot batch.',
|
|
151
151
|
});
|
|
152
152
|
break;
|
|
153
153
|
case 'mixed-pattern-cluster':
|
|
@@ -161,12 +161,12 @@ function mapDesignSmells(byId, smells) {
|
|
|
161
161
|
case 'soft-contract':
|
|
162
162
|
attach('maintainability', 'Soft contract walls (layers without deny rules) hide maintainability debt.', {
|
|
163
163
|
kind: 'skill',
|
|
164
|
-
ref: '/ark-
|
|
164
|
+
ref: '/ark-adopt',
|
|
165
165
|
summary: 'Add real layer rules so the AI has hard walls.',
|
|
166
166
|
});
|
|
167
167
|
attach('coupling', 'Layers with files but almost no deny rules allow free peer coupling.', {
|
|
168
168
|
kind: 'skill',
|
|
169
|
-
ref: '/ark-
|
|
169
|
+
ref: '/ark-adopt',
|
|
170
170
|
summary: 'Tighten inter-layer allows/denies without weakening enforcement.',
|
|
171
171
|
});
|
|
172
172
|
break;
|
|
@@ -213,7 +213,7 @@ function mapViolations(byId, violations) {
|
|
|
213
213
|
}
|
|
214
214
|
const edgeAction = {
|
|
215
215
|
kind: 'skill',
|
|
216
|
-
ref: '/ark-
|
|
216
|
+
ref: '/ark-autopilot',
|
|
217
217
|
summary: 'Clear the active edge residual, then re-doctor.',
|
|
218
218
|
};
|
|
219
219
|
if (upper === 'LAYER_IMPORT_VIOLATION' ||
|
|
@@ -229,7 +229,7 @@ function mapViolations(byId, violations) {
|
|
|
229
229
|
if (upper.includes('PEER_ISOLATION') || upper === 'PEER_ISOLATION_VIOLATION') {
|
|
230
230
|
attach('coupling', 'Peer isolation residual — slices import each other freely.', {
|
|
231
231
|
kind: 'skill',
|
|
232
|
-
ref: '/ark-
|
|
232
|
+
ref: '/ark-autopilot',
|
|
233
233
|
summary: 'Peer isolation fixes are judgment-class — one cluster at a time.',
|
|
234
234
|
});
|
|
235
235
|
continue;
|
|
@@ -237,12 +237,12 @@ function mapViolations(byId, violations) {
|
|
|
237
237
|
if (upper === 'FORBIDDEN_GLOBAL' || upper.startsWith('FORBIDDEN_')) {
|
|
238
238
|
attach('dip', 'Forbidden globals / effect surfaces break dependency inversion.', {
|
|
239
239
|
kind: 'skill',
|
|
240
|
-
ref: '/ark-
|
|
240
|
+
ref: '/ark-autopilot',
|
|
241
241
|
summary: 'Inject a port instead of the forbidden global.',
|
|
242
242
|
});
|
|
243
243
|
attach('testability', 'Forbidden ambient effects reduce pure-domain testability.', {
|
|
244
244
|
kind: 'skill',
|
|
245
|
-
ref: '/ark-
|
|
245
|
+
ref: '/ark-autopilot',
|
|
246
246
|
summary: 'Replace ambient effects with injectable ports.',
|
|
247
247
|
});
|
|
248
248
|
continue;
|
|
@@ -250,12 +250,12 @@ function mapViolations(byId, violations) {
|
|
|
250
250
|
if (upper === 'CAPABILITY_VIOLATION') {
|
|
251
251
|
attach('dip', 'Denied capability use — invert through an allowed adapter/port.', {
|
|
252
252
|
kind: 'skill',
|
|
253
|
-
ref: '/ark-
|
|
253
|
+
ref: '/ark-autopilot',
|
|
254
254
|
summary: 'Capability walls require port injection (judgment, not mechanical-safe).',
|
|
255
255
|
});
|
|
256
256
|
attach('testability', 'Capability violations couple domain code to I/O — harder to unit-test.', {
|
|
257
257
|
kind: 'skill',
|
|
258
|
-
ref: '/ark-
|
|
258
|
+
ref: '/ark-autopilot',
|
|
259
259
|
summary: 'Keep pure layers free of denied capabilities.',
|
|
260
260
|
});
|
|
261
261
|
continue;
|
|
@@ -263,7 +263,7 @@ function mapViolations(byId, violations) {
|
|
|
263
263
|
if (upper.startsWith('ARKRULE_') || upper === 'INVARIANT_UNCOVERED') {
|
|
264
264
|
attach('encapsulation', 'ArkRules structure / invariant residual inside a layer.', {
|
|
265
265
|
kind: 'skill',
|
|
266
|
-
ref: '/ark-
|
|
266
|
+
ref: '/ark-autopilot',
|
|
267
267
|
summary: 'Label [ArkRules]; structure fixes are judgment — never invent mechanical-safe.',
|
|
268
268
|
});
|
|
269
269
|
attach('domain', 'Intra-layer domain structure or invariant coverage residual.', {
|
|
@@ -282,7 +282,7 @@ function mapCountsAndFlags(byId, facts) {
|
|
|
282
282
|
pushEvidence(lens, 'cycles', `count:${cycleCount}`);
|
|
283
283
|
markResidual(lens, 'Import cycles couple modules tightly.', {
|
|
284
284
|
kind: 'skill',
|
|
285
|
-
ref: '/ark-
|
|
285
|
+
ref: '/ark-autopilot',
|
|
286
286
|
summary: 'Break cycles with a judgment extraction — one pilot.',
|
|
287
287
|
});
|
|
288
288
|
}
|
|
@@ -296,7 +296,7 @@ function mapCountsAndFlags(byId, facts) {
|
|
|
296
296
|
pushEvidence(lens, 'peerIsolation', `count:${peer}`);
|
|
297
297
|
markResidual(lens, 'Peer isolation residual remains.', {
|
|
298
298
|
kind: 'skill',
|
|
299
|
-
ref: '/ark-
|
|
299
|
+
ref: '/ark-autopilot',
|
|
300
300
|
summary: 'Peer isolation is judgment-class residual.',
|
|
301
301
|
});
|
|
302
302
|
}
|
|
@@ -314,7 +314,7 @@ function mapCountsAndFlags(byId, facts) {
|
|
|
314
314
|
markResidual(srp, 'Mirrored clusters suggest split-by-concern residual (architecture SRP).', {
|
|
315
315
|
kind: 'skill',
|
|
316
316
|
ref: '/ark-autopilot',
|
|
317
|
-
summary: 'One reshape/extraction pilot
|
|
317
|
+
summary: 'One reshape/extraction pilot this turn.',
|
|
318
318
|
});
|
|
319
319
|
}
|
|
320
320
|
const pureN = Number(facts.pureOrCapabilityResidual) || 0;
|
|
@@ -327,7 +327,7 @@ function mapCountsAndFlags(byId, facts) {
|
|
|
327
327
|
pushEvidence(dip, 'forbiddenGlobals', `residual:${fgN}`);
|
|
328
328
|
markResidual(dip, 'Pure / capability / forbidden residual weakens dependency inversion.', {
|
|
329
329
|
kind: 'skill',
|
|
330
|
-
ref: '/ark-
|
|
330
|
+
ref: '/ark-autopilot',
|
|
331
331
|
summary: 'Inject ports; keep pure layers free of effects.',
|
|
332
332
|
});
|
|
333
333
|
const test = byId.get('testability');
|
|
@@ -337,7 +337,7 @@ function mapCountsAndFlags(byId, facts) {
|
|
|
337
337
|
pushEvidence(test, 'forbiddenGlobals', `residual:${fgN}`);
|
|
338
338
|
markResidual(test, 'Impure domain or capability residual reduces testability.', {
|
|
339
339
|
kind: 'skill',
|
|
340
|
-
ref: '/ark-
|
|
340
|
+
ref: '/ark-autopilot',
|
|
341
341
|
summary: 'Prefer ports over concrete I/O in pure/domain modules.',
|
|
342
342
|
});
|
|
343
343
|
}
|
|
@@ -347,7 +347,7 @@ function mapCountsAndFlags(byId, facts) {
|
|
|
347
347
|
pushEvidence(enc, 'arkRules', `structureResidual:${arkN}`);
|
|
348
348
|
markResidual(enc, 'ArkRules structure residual — encapsulation inside the layer.', {
|
|
349
349
|
kind: 'skill',
|
|
350
|
-
ref: '/ark-
|
|
350
|
+
ref: '/ark-autopilot',
|
|
351
351
|
summary: 'Fix structure sensors under [ArkRules] without inventing mechanical-safe.',
|
|
352
352
|
});
|
|
353
353
|
const domain = byId.get('domain');
|
|
@@ -368,7 +368,7 @@ function mapCountsAndFlags(byId, facts) {
|
|
|
368
368
|
markResidual(m, 'Design-weak: checked edges may be clean, but design residual remains — not finished.', {
|
|
369
369
|
kind: 'skill',
|
|
370
370
|
ref: '/ark-explore',
|
|
371
|
-
summary: 'Shape door: explore shape-focus → dual-plan B → one pilot
|
|
371
|
+
summary: 'Shape door: explore shape-focus → dual-plan B → one /ark-autopilot pilot.',
|
|
372
372
|
});
|
|
373
373
|
}
|
|
374
374
|
if (facts.dirtyBaselineRisk === true || (Number(facts.baselineStale) || 0) > 0) {
|
|
@@ -409,7 +409,7 @@ function mapCountsAndFlags(byId, facts) {
|
|
|
409
409
|
pushEvidence(mod, 'coverage', `emptyLayers:${emptyL}`);
|
|
410
410
|
markResidual(mod, 'Placement / modularity residual — ungoverned dirs or empty layer globs.', {
|
|
411
411
|
kind: 'skill',
|
|
412
|
-
ref: '/ark-
|
|
412
|
+
ref: '/ark-adopt',
|
|
413
413
|
summary: 'Classify ungoverned paths; fix empty layer patterns.',
|
|
414
414
|
});
|
|
415
415
|
}
|
package/bin/lib/pilot-loop.mjs
CHANGED
|
@@ -115,7 +115,7 @@ export function extractionCardFromBet(bet, preferredFiles) {
|
|
|
115
115
|
reDoctor: 'ark-check --doctor --json',
|
|
116
116
|
rePlan: 'ark-check --plan --json',
|
|
117
117
|
next:
|
|
118
|
-
'/ark-
|
|
118
|
+
'/ark-autopilot (one cluster / one B pilot) | re-doctor after pilot',
|
|
119
119
|
};
|
|
120
120
|
}
|
|
121
121
|
|
|
@@ -16,13 +16,13 @@ export const POST_GREEN_PRIMARY_SKILL = '/ark-explore';
|
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Canonical human / agent next-action string (single door).
|
|
19
|
-
* Chained: explore shape-focus then autopilot
|
|
19
|
+
* Chained: explore shape-focus then autopilot applies one B pilot.
|
|
20
20
|
*/
|
|
21
21
|
export const POST_GREEN_PRIMARY_ACTION = POST_GREEN_HUMAN;
|
|
22
22
|
|
|
23
23
|
/** Short label for tables / metrics. */
|
|
24
24
|
export const POST_GREEN_PRIMARY_SHORT =
|
|
25
|
-
'/ark-explore shape-focus → /ark-autopilot (apply B
|
|
25
|
+
'/ark-explore shape-focus → /ark-autopilot (apply one B pilot) # Shape residual';
|
|
26
26
|
|
|
27
27
|
/** Placement coaching while design residual remains (new code only). */
|
|
28
28
|
export const POST_GREEN_PLACEMENT_COACHING =
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
} from './resolved-candidate-facts.mjs';
|
|
14
14
|
import { effectiveAnalysisConfig } from './analysis-policy.mjs';
|
|
15
15
|
import { isScanExcludedRelative } from '../ark-shared.mjs';
|
|
16
|
+
import { classifyChangeSet, evaluateTeamGate } from './team-parliament.mjs';
|
|
16
17
|
|
|
17
18
|
function candidatePath(value) {
|
|
18
19
|
if (typeof value !== 'string' || value.trim() === '') {
|
|
@@ -102,6 +103,14 @@ export function prepareChangeFromRoot({
|
|
|
102
103
|
const normalizedChanges = normalizeChangeSet(changes);
|
|
103
104
|
const normalizedOverlayChanges =
|
|
104
105
|
overlayChanges === undefined ? normalizedChanges : normalizeChangeSet(overlayChanges);
|
|
106
|
+
const lawGate = evaluateTeamGate({
|
|
107
|
+
changeSet: classifyChangeSet(normalizedChanges.map((change) => change.path)),
|
|
108
|
+
contractSession:
|
|
109
|
+
process.env.ARK_CONTRACT_SESSION === '1' || process.env.ARK_CONTRACT_SESSION === 'true',
|
|
110
|
+
});
|
|
111
|
+
if (lawGate.deny && lawGate.reasonId === 'mixed-law-and-product') {
|
|
112
|
+
throw new Error(lawGate.message);
|
|
113
|
+
}
|
|
105
114
|
const effectiveConfig = effectiveAnalysisConfig(config, manifest);
|
|
106
115
|
for (const change of normalizedChanges) {
|
|
107
116
|
assertInsideProject(root, change.path);
|
package/bin/lib/product-copy.mjs
CHANGED
|
@@ -23,7 +23,7 @@ export function operatingModeTitle(mode, leftoverDesign) {
|
|
|
23
23
|
export const LEFTOVER_DESIGN_BADGE = LEFTOVER_DESIGN_LABEL;
|
|
24
24
|
|
|
25
25
|
export const POST_GREEN_HUMAN =
|
|
26
|
-
'Imports check out, but the design is still messy. Map leftover work with /ark-explore shape-focus, then apply one small refactor via /ark-autopilot
|
|
26
|
+
'Imports check out, but the design is still messy. Map leftover work with /ark-explore shape-focus, then apply one small refactor via /ark-autopilot. A clean import check is not done; pattern bets are never auto-applied.';
|
|
27
27
|
|
|
28
28
|
export const POST_GREEN_LEDE =
|
|
29
29
|
'Import rules are clean, but leftover design work remains. That does not fail the check — it only means “done” is still wrong until you tidy shape.';
|
|
@@ -147,21 +147,16 @@ export function renderStartPreview(preview, options = {}) {
|
|
|
147
147
|
} else {
|
|
148
148
|
console.log('Ark start preview — no files were changed.');
|
|
149
149
|
}
|
|
150
|
+
if (!applying) {
|
|
151
|
+
console.log('Apply this plan with: arkgate start --apply');
|
|
152
|
+
}
|
|
150
153
|
if (preview.analysis) {
|
|
151
154
|
console.log(`Your project looks like: ${preview.analysis.label} (${preview.analysis.archetype}, confidence ${preview.analysis.confidence}).`);
|
|
152
155
|
}
|
|
153
|
-
console.log(`Projected governed coverage: ${preview.projectedCoverage.percent ?? 'unknown'}% (${preview.projectedCoverage.classifiedFiles}/${preview.projectedCoverage.totalFiles} files)`);
|
|
154
|
-
const budget = preview.setupBudget;
|
|
155
|
-
const arkrulesNote =
|
|
156
|
-
budget.arkrulesFiles > 0 ? ` (+${budget.arkrulesFiles} arkrules)` : '';
|
|
157
|
-
const gateCount = budget.gateFiles ?? budget.files;
|
|
158
|
-
console.log(
|
|
159
|
-
`Compact setup budget: ${gateCount}/${budget.maxFiles} gate files${arkrulesNote}, ${budget.bytes}/${budget.maxBytes} bytes${budget.ok ? '' : ' (exceeded)'}.`
|
|
160
|
-
);
|
|
161
156
|
console.log(applying ? 'Files create/edit/delete:' : 'Files to create/edit/delete:');
|
|
162
157
|
if (preview.changes.length === 0) console.log(' (none)');
|
|
163
158
|
for (const change of preview.changes) {
|
|
164
|
-
console.log(` ${change.action.padEnd(6)} ${change.path}
|
|
159
|
+
console.log(` ${change.action.padEnd(6)} ${change.path}`);
|
|
165
160
|
}
|
|
166
161
|
if (!applying) {
|
|
167
162
|
console.log('Commands in the approved setup plan:');
|
|
@@ -179,8 +174,20 @@ export function renderStartPreview(preview, options = {}) {
|
|
|
179
174
|
console.log('Unresolved decisions:');
|
|
180
175
|
for (const decision of preview.unresolvedDecisions) console.log(` ${decision}`);
|
|
181
176
|
}
|
|
177
|
+
console.log('Details (optional):');
|
|
178
|
+
console.log(`Projected governed coverage: ${preview.projectedCoverage.percent ?? 'unknown'}% (${preview.projectedCoverage.classifiedFiles}/${preview.projectedCoverage.totalFiles} files)`);
|
|
179
|
+
const budget = preview.setupBudget;
|
|
180
|
+
const arkrulesNote =
|
|
181
|
+
budget.arkrulesFiles > 0 ? ` (+${budget.arkrulesFiles} arkrules)` : '';
|
|
182
|
+
const gateCount = budget.gateFiles ?? budget.files;
|
|
183
|
+
console.log(
|
|
184
|
+
`Compact setup budget: ${gateCount}/${budget.maxFiles} gate files${arkrulesNote}, ${budget.bytes}/${budget.maxBytes} bytes${budget.ok ? '' : ' (exceeded)'}.`
|
|
185
|
+
);
|
|
186
|
+
for (const change of preview.changes) {
|
|
187
|
+
console.log(` ${change.action.padEnd(6)} ${change.path} ${change.afterHash ?? '(deleted)'}`);
|
|
188
|
+
}
|
|
182
189
|
if (!applying) {
|
|
183
|
-
console.log('Review complete file contents with --json.
|
|
190
|
+
console.log('Review complete file contents with --json.');
|
|
184
191
|
}
|
|
185
192
|
}
|
|
186
193
|
|
|
@@ -25,6 +25,7 @@ import { detectActiveAgentHost } from './skill-install.mjs';
|
|
|
25
25
|
import { readBaseline } from './violations.mjs';
|
|
26
26
|
import { reportsDir, readJsonSafe } from './html-report.mjs';
|
|
27
27
|
import { summarizeRulesUnderContract } from './rules-under-contract.mjs';
|
|
28
|
+
import { collectVsBaseFacts, discoverTeamBaseRef } from './team-parliament-io.mjs';
|
|
28
29
|
|
|
29
30
|
function sha256Hex(value) {
|
|
30
31
|
return createHash('sha256').update(value, 'utf8').digest('hex');
|
|
@@ -381,7 +382,23 @@ export function collectStatusFacts(options = {}) {
|
|
|
381
382
|
: arkRulesLoaded
|
|
382
383
|
? 0
|
|
383
384
|
: null,
|
|
385
|
+
leftoverDesignWork:
|
|
386
|
+
options.leftoverDesignWork === true ||
|
|
387
|
+
latest?.leftoverDesignWork === true ||
|
|
388
|
+
latest?.designFitness?.designWeak === true ||
|
|
389
|
+
latest?.doctor?.designFitness?.designWeak === true,
|
|
384
390
|
improvementCompass,
|
|
391
|
+
vsBase: (() => {
|
|
392
|
+
const vsRef = typeof options.vs === 'string' ? options.vs.trim() : '';
|
|
393
|
+
if (!vsRef) return null;
|
|
394
|
+
const baseRef = discoverTeamBaseRef(resolvedRoot, vsRef);
|
|
395
|
+
if (!baseRef) return null;
|
|
396
|
+
return collectVsBaseFacts({
|
|
397
|
+
root: resolvedRoot,
|
|
398
|
+
baseRef,
|
|
399
|
+
configRel: path.basename(configPath),
|
|
400
|
+
});
|
|
401
|
+
})(),
|
|
385
402
|
};
|
|
386
403
|
}
|
|
387
404
|
|
|
@@ -423,6 +440,7 @@ export function runStatusCommand(args = {}) {
|
|
|
423
440
|
expectedProjectId: args.expectedProjectId,
|
|
424
441
|
host: args.host,
|
|
425
442
|
arkgateVersion: args.arkgateVersion,
|
|
443
|
+
vs: args.vs,
|
|
426
444
|
});
|
|
427
445
|
|
|
428
446
|
if (asJson || args.json) {
|
|
@@ -471,6 +489,7 @@ export function runStatusCommand(args = {}) {
|
|
|
471
489
|
' · not a score'
|
|
472
490
|
);
|
|
473
491
|
}
|
|
492
|
+
if (manifest.vsBase?.line) write(` ${manifest.vsBase.line}`);
|
|
474
493
|
write(` next: [${manifest.nextAction.id}] ${manifest.nextAction.summary}`);
|
|
475
494
|
}
|
|
476
495
|
|
|
@@ -230,6 +230,12 @@ export function resolveStatusNextAction(facts, binding, activation, lastCheck, r
|
|
|
230
230
|
summary: 'Local write is advisory for this host — keep a required GitHub status on arkgate-check --strict-merge as the hard merge boundary.',
|
|
231
231
|
};
|
|
232
232
|
}
|
|
233
|
+
if (facts.leftoverDesignWork === true) {
|
|
234
|
+
return {
|
|
235
|
+
id: 'map-leftover-design',
|
|
236
|
+
summary: 'Leftover design work remains. Map with /ark-explore, then apply one small refactor with /ark-autopilot. Green imports are not done.',
|
|
237
|
+
};
|
|
238
|
+
}
|
|
233
239
|
if (rules.arkRulesLoaded && (rules.frozenResidual ?? 0) > 0) {
|
|
234
240
|
return {
|
|
235
241
|
id: 'review-arkrules-residual',
|
|
@@ -311,6 +317,9 @@ export function buildStatusManifest(facts) {
|
|
|
311
317
|
const compass = normalizeStatusImprovementCompass(facts.improvementCompass);
|
|
312
318
|
if (compass)
|
|
313
319
|
status.improvementCompass = compass;
|
|
320
|
+
if (facts.vsBase && typeof facts.vsBase.baseRef === 'string' && facts.vsBase.baseRef.length > 0) {
|
|
321
|
+
status.vsBase = facts.vsBase;
|
|
322
|
+
}
|
|
314
323
|
return status;
|
|
315
324
|
}
|
|
316
325
|
const STATUS_COMPASS_MODE_SET = new Set(STATUS_COMPASS_MODES);
|
|
@@ -576,5 +585,19 @@ export const ARK_STATUS_MANIFEST_SCHEMA = {
|
|
|
576
585
|
contractHash: { type: 'string', minLength: 1 },
|
|
577
586
|
},
|
|
578
587
|
},
|
|
588
|
+
vsBase: {
|
|
589
|
+
type: 'object',
|
|
590
|
+
description: 'Checkout vs a git base ref: pin, contract identity, baseline grow. Advisory honesty only — never a gate input.',
|
|
591
|
+
additionalProperties: false,
|
|
592
|
+
required: ['baseRef', 'line', 'pinLocal', 'pinBase', 'contractEqual', 'baselineGrew'],
|
|
593
|
+
properties: {
|
|
594
|
+
baseRef: { type: 'string', minLength: 1 },
|
|
595
|
+
line: { type: 'string', minLength: 1 },
|
|
596
|
+
pinLocal: { anyOf: [{ type: 'string', minLength: 1 }, { type: 'null' }] },
|
|
597
|
+
pinBase: { anyOf: [{ type: 'string', minLength: 1 }, { type: 'null' }] },
|
|
598
|
+
contractEqual: { type: 'boolean' },
|
|
599
|
+
baselineGrew: { type: 'boolean' },
|
|
600
|
+
},
|
|
601
|
+
},
|
|
579
602
|
},
|
|
580
603
|
};
|