arkgate 3.6.1 → 3.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +122 -1145
- package/README.md +59 -19
- package/bin/ark-check-runtime.mjs +1598 -0
- package/bin/ark-check.mjs +32 -1565
- package/bin/ark-layer-match.mjs +2 -1
- package/bin/ark-mcp-runtime.mjs +1976 -0
- package/bin/ark-mcp.mjs +84 -1495
- package/bin/ark-shared.mjs +34 -38
- package/bin/ark.mjs +33 -66
- package/bin/lib/adapter-contract.mjs +161 -9
- package/bin/lib/agent-gates.mjs +1 -0
- package/bin/lib/analysis-completeness.mjs +28 -0
- package/bin/lib/analysis-engine.mjs +8 -8
- package/bin/lib/analysis-policy.mjs +27 -0
- package/bin/lib/architecture-scan.mjs +70 -304
- package/bin/lib/auto-patch.mjs +76 -8
- package/bin/lib/ci-and-commands.mjs +1 -1
- package/bin/lib/codex-home.mjs +43 -16
- package/bin/lib/design-delta.mjs +4 -0
- package/bin/lib/design-smells.mjs +67 -14
- package/bin/lib/doctor-advisories.mjs +23 -7
- package/bin/lib/doctor-plan.mjs +44 -47
- package/bin/lib/enforcement-state.mjs +2 -0
- package/bin/lib/github-enforcement.mjs +443 -0
- package/bin/lib/hook-templates.mjs +12 -148
- package/bin/lib/html-report-advisories.mjs +59 -0
- package/bin/lib/html-report-depth.mjs +9 -0
- package/bin/lib/html-report.mjs +5 -5
- package/bin/lib/install-migrate.mjs +83 -79
- package/bin/lib/managed-upgrade.mjs +622 -0
- package/bin/lib/mcp-adoption.mjs +3 -1
- package/bin/lib/parse-health.mjs +75 -0
- package/bin/lib/port-proof.mjs +2 -2
- package/bin/lib/prepare-change.mjs +68 -38
- package/bin/lib/prepare-write.mjs +7 -1
- package/bin/lib/reshape-decisions.mjs +284 -0
- package/bin/lib/resident-doctor-client.mjs +55 -0
- package/bin/lib/resident-hook.mjs +247 -0
- package/bin/lib/resolved-candidate-facts.mjs +1160 -0
- package/bin/lib/scan-files.mjs +19 -6
- package/bin/lib/snippet-analysis.mjs +119 -0
- package/bin/lib/source-policy.mjs +24 -0
- package/bin/lib/typescript-host.mjs +15 -18
- package/bin/lib/unavailable-analysis.mjs +76 -0
- package/bin/lib/upgrade-command.mjs +115 -0
- package/bin/lib/weakest-link.mjs +21 -179
- package/bin/lib/write-path-capabilities.mjs +167 -16
- package/bin/lib/write-path-detect.mjs +3 -2
- package/dist/eslint/index.cjs +3 -3
- package/dist/eslint/index.d.ts +3 -0
- package/dist/eslint/index.js +3 -3
- package/dist/index.cjs +7 -7
- package/dist/index.d.ts +1073 -141
- package/dist/index.js +7 -7
- package/docs/agent-guide.md +127 -52
- package/docs/ai-gates.md +100 -18
- package/docs/configuration.md +6 -0
- package/docs/demos/01-write-gate-self-correction.md +2 -2
- package/docs/enthusiast/README.md +10 -10
- package/docs/enthusiast/how-to-gallery-starter.md +2 -2
- package/docs/enthusiast/reference-commands.md +18 -1
- package/docs/enthusiast/tutorial-first-project.md +2 -2
- package/docs/package-surface.md +101 -14
- package/docs/typescript-support.md +118 -37
- package/package.json +33 -4
- package/schemas/ark.analysis-result.schema.json +159 -2
- package/schemas/ark.design-delta.schema.json +1 -0
- package/schemas/ark.enforcement-state.schema.json +84 -0
- package/schemas/ark.resolved-candidate-facts.schema.json +1 -0
- package/server.json +2 -2
- package/templates/skills/ark-autopilot.md +12 -0
- package/templates/skills/ark-explore.md +12 -5
- package/templates/skills/ark-fix.md +12 -2
- package/templates/skills/ark-loop.md +14 -1
- package/templates/skills/ark-runtime.md +15 -8
- package/templates/skills/ark-upgrade.md +122 -182
- package/bin/lib/ai-velocity.mjs +0 -293
- package/bin/lib/graph-cycles.mjs +0 -6
- package/bin/lib/safety-diagnostics.mjs +0 -284
- package/bin/lib/ts-resolve.mjs +0 -227
- package/dist/configTypes-DAPvBqK6.d.cts +0 -61
- package/dist/eslint/index.d.cts +0 -146
- package/dist/index.d.cts +0 -986
package/bin/ark-shared.mjs
CHANGED
|
@@ -6,26 +6,18 @@ import {
|
|
|
6
6
|
withArkConfigMetadata,
|
|
7
7
|
} from './lib/config-contract.mjs';
|
|
8
8
|
import { collectForbiddenCapabilityUses } from './lib/analysis-engine.mjs';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
DEFAULT_INTENT_PREFIXES as DOMAIN_DEFAULT_INTENT_PREFIXES,
|
|
11
|
+
looksLikeArkIntent,
|
|
12
|
+
resolveIntentLayer as resolveConfiguredIntentLayer,
|
|
13
|
+
} from './lib/source-policy.mjs';
|
|
10
14
|
|
|
11
15
|
/**
|
|
12
16
|
* Default intent-prefix map shared by both CLIs and the ark-mcp write-path gate. The rule
|
|
13
17
|
* matrix comes from the generated Domain config contract above. Prefixes mirror the runtime
|
|
14
18
|
* profile but stay in this standalone CLI module because the CLIs run without a build step.
|
|
15
19
|
*/
|
|
16
|
-
export const DEFAULT_INTENT_PREFIXES =
|
|
17
|
-
{ layer: 'DomainModel', prefixes: ['Domain.'] },
|
|
18
|
-
{ layer: 'ApplicationOrchestration', prefixes: ['Application.'] },
|
|
19
|
-
{ layer: 'PersistenceAdapters', prefixes: ['Adapter.Persistence.', 'Adapter.Repository.'] },
|
|
20
|
-
{ layer: 'IntegrationAdapters', prefixes: ['Adapter.Integration.', 'Adapter.External.'] },
|
|
21
|
-
{ layer: 'WorkflowSagaEngine', prefixes: ['Workflow.'] },
|
|
22
|
-
{ layer: 'BackgroundJobsScheduling', prefixes: ['Job.'] },
|
|
23
|
-
{ layer: 'PresentationAdapters', prefixes: ['Presentation.', 'Adapter.Presentation.', 'Adapter.Api.'] },
|
|
24
|
-
{ layer: 'ReportingReadModels', prefixes: ['Reporting.'] },
|
|
25
|
-
{ layer: 'ExtensibilityMetadata', prefixes: ['Metadata.'] },
|
|
26
|
-
{ layer: 'SecurityAuditObservability', prefixes: ['Security.', 'Audit.', 'Observability.'] },
|
|
27
|
-
{ layer: 'Kernel', prefixes: ['Kernel.'] },
|
|
28
|
-
];
|
|
20
|
+
export const DEFAULT_INTENT_PREFIXES = DOMAIN_DEFAULT_INTENT_PREFIXES;
|
|
29
21
|
|
|
30
22
|
export const DEFAULT_LAYER_DIRECTORIES = {
|
|
31
23
|
DomainModel: ['domain'],
|
|
@@ -447,10 +439,6 @@ export {
|
|
|
447
439
|
isScanExcludedRelative,
|
|
448
440
|
} from './ark-layer-match.mjs';
|
|
449
441
|
|
|
450
|
-
function normalizePrefix(prefix) {
|
|
451
|
-
return prefix.endsWith('.') ? prefix : `${prefix}.`;
|
|
452
|
-
}
|
|
453
|
-
|
|
454
442
|
/**
|
|
455
443
|
* Resolve an intent name to its layer using the SAME semantics as
|
|
456
444
|
* ArchitectureProfile.resolveLayer in src/kernel/layers/ArchitectureProfile.ts (which the
|
|
@@ -460,16 +448,7 @@ function normalizePrefix(prefix) {
|
|
|
460
448
|
* the write-path gate classify identically. `layers` is an array of { name, prefixes }.
|
|
461
449
|
*/
|
|
462
450
|
export function resolveIntentLayer(intent, layers) {
|
|
463
|
-
|
|
464
|
-
name: layer.name,
|
|
465
|
-
prefixes: (layer.prefixes ?? []).map(normalizePrefix),
|
|
466
|
-
}));
|
|
467
|
-
const sorted = [...normalized].sort((a, b) => {
|
|
468
|
-
const maxA = Math.max(0, ...a.prefixes.map((p) => p.length));
|
|
469
|
-
const maxB = Math.max(0, ...b.prefixes.map((p) => p.length));
|
|
470
|
-
return maxB - maxA;
|
|
471
|
-
});
|
|
472
|
-
return sorted.find((layer) => layer.prefixes.some((prefix) => intent.startsWith(prefix)))?.name;
|
|
451
|
+
return resolveConfiguredIntentLayer(intent, layers);
|
|
473
452
|
}
|
|
474
453
|
|
|
475
454
|
export function looksLikeIntent(value) {
|
|
@@ -489,7 +468,8 @@ export {
|
|
|
489
468
|
|
|
490
469
|
/**
|
|
491
470
|
* Normalize a required/imported TypeScript module for ark-check's host.
|
|
492
|
-
* TS 5/6 expose `sys` on the root export. Early TS 7 /
|
|
471
|
+
* TS 5/6 expose `sys` and the AST helpers used by the scanner on the root export. Early TS 7 /
|
|
472
|
+
* some ESM interop shapes
|
|
493
473
|
* may nest under `.default` or omit `sys` — those are unusable for resolve/scan
|
|
494
474
|
* and must fall through to a JS-API-compatible TypeScript (Ark's own or 5/6).
|
|
495
475
|
*
|
|
@@ -508,7 +488,8 @@ export function usableTypescript(mod) {
|
|
|
508
488
|
ts.sys &&
|
|
509
489
|
typeof ts.sys.fileExists === 'function' &&
|
|
510
490
|
typeof ts.createSourceFile === 'function' &&
|
|
511
|
-
typeof ts.resolveModuleName === 'function'
|
|
491
|
+
typeof ts.resolveModuleName === 'function' &&
|
|
492
|
+
typeof ts.isInTypeQuery === 'function'
|
|
512
493
|
) {
|
|
513
494
|
return ts;
|
|
514
495
|
}
|
|
@@ -537,6 +518,9 @@ export function typescriptUsabilityHint(mod) {
|
|
|
537
518
|
if (typeof ts.sys.fileExists !== 'function') return 'ts.sys.fileExists is not a function';
|
|
538
519
|
if (typeof ts.createSourceFile !== 'function') return 'missing createSourceFile (AST API)';
|
|
539
520
|
if (typeof ts.resolveModuleName !== 'function') return 'missing resolveModuleName';
|
|
521
|
+
if (typeof ts.isInTypeQuery !== 'function') {
|
|
522
|
+
return 'missing isInTypeQuery (requires a TypeScript 5/6 analysis host)';
|
|
523
|
+
}
|
|
540
524
|
return 'unknown shape incompatibility';
|
|
541
525
|
}
|
|
542
526
|
|
|
@@ -1732,14 +1716,26 @@ export function formatArchitectureRecommendationHuman(recommendation) {
|
|
|
1732
1716
|
|
|
1733
1717
|
export const ADOPTION_PLAN_FILENAME = 'ark-adoption-plan.json';
|
|
1734
1718
|
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1719
|
+
function galleryStarter(archetype, directory = archetype, generatedPreset) {
|
|
1720
|
+
return Object.freeze({
|
|
1721
|
+
archetype,
|
|
1722
|
+
directory: `examples/${directory}-starter`,
|
|
1723
|
+
...(generatedPreset ? { generatedPreset } : {}),
|
|
1724
|
+
});
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
export const GALLERY_STARTERS = Object.freeze([
|
|
1728
|
+
galleryStarter('crud-product'),
|
|
1729
|
+
galleryStarter('api-backend'),
|
|
1730
|
+
galleryStarter('worker-pipeline'),
|
|
1731
|
+
galleryStarter('multi-app-workspace'),
|
|
1732
|
+
galleryStarter('vertical-slice-product', 'vertical-slice', 'vertical-slice'),
|
|
1733
|
+
galleryStarter('ddd-bounded-contexts', 'ddd-context', 'ddd-bounded-contexts'),
|
|
1734
|
+
]);
|
|
1735
|
+
|
|
1736
|
+
const GALLERY_STARTER_BY_ARCHETYPE = Object.fromEntries(
|
|
1737
|
+
GALLERY_STARTERS.map(({ archetype, directory }) => [archetype, `${directory}/`])
|
|
1738
|
+
);
|
|
1743
1739
|
|
|
1744
1740
|
/** Enthusiast pack id for a named preset, or null when none ships. */
|
|
1745
1741
|
export function policyPackIdForPreset(preset) {
|
package/bin/ark.mjs
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { spawnSync } from 'node:child_process';
|
|
3
3
|
import fs from 'node:fs';
|
|
4
|
-
import os from 'node:os';
|
|
5
4
|
import path from 'node:path';
|
|
6
5
|
import { fileURLToPath } from 'node:url';
|
|
7
6
|
import readline from 'node:readline/promises';
|
|
@@ -21,8 +20,10 @@ import {
|
|
|
21
20
|
import { pinArkgateDevDependency, FALSE_GREEN_GAP_ID } from './lib/field-install.mjs';
|
|
22
21
|
import { validateHardWriteRequest } from './lib/enforcement-profiles.mjs';
|
|
23
22
|
import { applyStartPreview, planStart, renderStartPreview } from './lib/start-preview.mjs';
|
|
23
|
+
import { runUpgradeCommand } from './lib/upgrade-command.mjs';
|
|
24
24
|
import { detectActiveAgentHost } from './lib/skill-install.mjs';
|
|
25
25
|
import { loadArkConfigContract } from './lib/config-contract.mjs';
|
|
26
|
+
import { loadTypeScript } from './lib/typescript-host.mjs';
|
|
26
27
|
import {
|
|
27
28
|
prepareChangeFromRoot,
|
|
28
29
|
readChangeMapFile,
|
|
@@ -70,6 +71,8 @@ function parseArgs(argv) {
|
|
|
70
71
|
install: true,
|
|
71
72
|
installExplicit: false,
|
|
72
73
|
apply: false,
|
|
74
|
+
acceptConflicts: false,
|
|
75
|
+
planDigest: undefined,
|
|
73
76
|
json: false,
|
|
74
77
|
internalApply: false,
|
|
75
78
|
skipPackageManager: false,
|
|
@@ -104,6 +107,8 @@ function parseArgs(argv) {
|
|
|
104
107
|
args.installExplicit = true;
|
|
105
108
|
}
|
|
106
109
|
else if (arg === '--apply') args.apply = true;
|
|
110
|
+
else if (arg === '--accept-conflicts') args.acceptConflicts = true;
|
|
111
|
+
else if (arg === '--plan-digest') args.planDigest = requireValue(arg, i++);
|
|
107
112
|
else if (arg === '--json') args.json = true;
|
|
108
113
|
else if (arg === '--internal-apply') args.internalApply = true;
|
|
109
114
|
else if (arg === '--skip-package-manager') args.skipPackageManager = true;
|
|
@@ -112,6 +117,8 @@ function parseArgs(argv) {
|
|
|
112
117
|
else if (arg === '--config') args.config = requireValue(arg, i++);
|
|
113
118
|
else if (arg === '--changes') args.changes = requireValue(arg, i++);
|
|
114
119
|
else if (arg === '--change-map') args.changeMap = requireValue(arg, i++);
|
|
120
|
+
else if (arg === '--manifest') args.manifest = requireValue(arg, i++);
|
|
121
|
+
else if (arg === '--tsconfig') args.tsconfig = requireValue(arg, i++);
|
|
115
122
|
else if (arg === '--archetype') args.archetype = requireValue(arg, i++);
|
|
116
123
|
else if (arg === '--tools') args.tools = requireValue(arg, i++);
|
|
117
124
|
else if (arg === '--require-write-hook') {
|
|
@@ -131,17 +138,17 @@ function usage() {
|
|
|
131
138
|
ark start [--root <project>] [--tools <host>] [--require-write-hook <host>] [--install] [--apply] [--json]
|
|
132
139
|
ark init [--root <project>] [--preset hexagonal|layered|feature-sliced|monorepo|ui-surface|vertical-slice|ddd-bounded-contexts|clean-architecture|onion-architecture]
|
|
133
140
|
[--archetype <playbook-id>] [--tools <list>] [--require-write-hook <host>] [--yes] [--force] [--no-strict]
|
|
134
|
-
ark upgrade [--root <project>] [--no-install] [--no-strict]
|
|
135
|
-
ark preflight --changes <change-set.json> [--change-map <map.json>] [--root <project>] [--config ark.config.json] [--json]
|
|
141
|
+
ark upgrade [--root <project>] [--tools <list>] [--apply] [--plan-digest <sha256>] [--accept-conflicts] [--json] [--no-install] [--no-strict]
|
|
142
|
+
ark preflight --changes <change-set.json> [--change-map <map.json>] [--root <project>] [--config ark.config.json] [--manifest <manifest.json>] [--tsconfig <tsconfig.json>] [--json]
|
|
136
143
|
|
|
137
144
|
Commands:
|
|
138
145
|
start New here? Analyze and preview the complete setup. Read-only unless --apply.
|
|
139
146
|
init Configure Ark project enforcement with explicit prompts.
|
|
140
|
-
upgrade
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
preflight Validate one atomic create/update/delete set without writing project files.
|
|
147
|
+
upgrade Preview identity-proven Ark-managed asset updates. With package install,
|
|
148
|
+
--apply bumps to @latest and recomputes the preview; a second explicit
|
|
149
|
+
--apply --no-install applies those exact bytes and verifies them.
|
|
144
150
|
(alias: ark update)
|
|
151
|
+
preflight Validate one atomic create/update/delete set without writing project files.
|
|
145
152
|
|
|
146
153
|
Options:
|
|
147
154
|
--yes Non-interactive defaults: create config if needed, install gate templates, run strict check.
|
|
@@ -150,8 +157,11 @@ Options:
|
|
|
150
157
|
--no-strict Skip the final strict ark-check run.
|
|
151
158
|
--install Add arkgate to package.json explicitly before applying a start plan.
|
|
152
159
|
--no-install Skip adding/installing arkgate as a project devDependency (start/upgrade).
|
|
153
|
-
--apply Apply
|
|
154
|
-
--
|
|
160
|
+
--apply Apply a start plan; for upgrade, update/repreview or apply with --no-install.
|
|
161
|
+
--accept-conflicts
|
|
162
|
+
Allow upgrade to recreate deleted managed assets or replace recorded conflicts.
|
|
163
|
+
--plan-digest Digest emitted by an upgrade preview; required to apply managed bytes.
|
|
164
|
+
--json Emit the start/upgrade preview as deterministic machine-readable JSON.
|
|
155
165
|
--preset Start from a named architecture preset instead of detection.
|
|
156
166
|
--archetype Application shape from templates/architecture-playbook.json (maps to the matching preset).
|
|
157
167
|
Valid ids: crud-product, api-backend, frontend-surface, library-sdk, cli-utility,
|
|
@@ -199,60 +209,6 @@ function runCommand(command, commandArgs, cwd) {
|
|
|
199
209
|
return result.status ?? 1;
|
|
200
210
|
}
|
|
201
211
|
|
|
202
|
-
// `ark upgrade`: the one command that replaces the "install @latest && install-agent-gates
|
|
203
|
-
// --skills-only --force && ... --codex-home --force && ... --migrate-commands && check" chain.
|
|
204
|
-
// Each step reruns ark-check as a fresh process, so the refresh runs from the freshly-installed
|
|
205
|
-
// version, not this (now-older) process.
|
|
206
|
-
async function upgrade(args) {
|
|
207
|
-
const root = args.root;
|
|
208
|
-
console.log('Ark upgrade — updating the package, gates, skills, and command runners.');
|
|
209
|
-
|
|
210
|
-
if (args.install) {
|
|
211
|
-
const [command, commandArgs] = packageInstallArgv(root);
|
|
212
|
-
console.log(`\n1/4 Updating the package: ${command} ${commandArgs.join(' ')}`);
|
|
213
|
-
const status = runCommand(command, commandArgs, root);
|
|
214
|
-
if (status !== 0) {
|
|
215
|
-
console.error(
|
|
216
|
-
`\nPackage update failed (exit ${status}). Fix the install and re-run, or use ` +
|
|
217
|
-
'`ark upgrade --no-install` to refresh gates/skills against the installed version.'
|
|
218
|
-
);
|
|
219
|
-
return status;
|
|
220
|
-
}
|
|
221
|
-
} else {
|
|
222
|
-
console.log('\n1/4 Skipping package install (--no-install).');
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
console.log('\n2/4 Refreshing agent gates + /ark-* skills…');
|
|
226
|
-
let status = runArkCheck(['--root', root, '--install-agent-gates'], { cwd: root });
|
|
227
|
-
if (status !== 0) return status;
|
|
228
|
-
|
|
229
|
-
// Codex home skill catalog is $CODEX_HOME/skills/<name>/SKILL.md (repo uses .agents/skills/).
|
|
230
|
-
// Refresh home skills when a Codex home exists. Project MCP is installed above in
|
|
231
|
-
// .codex/config.toml. Non-fatal: a permission error (e.g. sandbox) shouldn't fail upgrade.
|
|
232
|
-
const codexHomeBase = process.env.CODEX_HOME || path.join(os.homedir(), '.codex');
|
|
233
|
-
if (fs.existsSync(codexHomeBase)) {
|
|
234
|
-
console.log(`\n Refreshing Codex home (${codexHomeBase})…`);
|
|
235
|
-
runArkCheck(
|
|
236
|
-
['--root', root, '--install-agent-gates', '--skills-only', '--codex-home', '--force'],
|
|
237
|
-
{ cwd: root }
|
|
238
|
-
);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
console.log('\n3/4 Migrating command runners to this project’s package manager…');
|
|
242
|
-
status = runArkCheck(['--root', root, '--install-agent-gates', '--migrate-commands'], { cwd: root });
|
|
243
|
-
if (status !== 0) return status;
|
|
244
|
-
|
|
245
|
-
if (!args.strict) {
|
|
246
|
-
console.log('\n4/4 Skipping the strict check (--no-strict). Upgrade complete.');
|
|
247
|
-
return 0;
|
|
248
|
-
}
|
|
249
|
-
console.log('\n4/4 Verifying architecture…');
|
|
250
|
-
return runArkCheck(
|
|
251
|
-
['--root', root, '--config', 'ark.config.json', '--strict-merge'],
|
|
252
|
-
{ cwd: root }
|
|
253
|
-
);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
212
|
function runArkCheck(args, options = {}) {
|
|
257
213
|
const result = spawnSync(process.execPath, [arkCheck, ...args], {
|
|
258
214
|
cwd: options.cwd,
|
|
@@ -790,9 +746,12 @@ async function main() {
|
|
|
790
746
|
}
|
|
791
747
|
if (
|
|
792
748
|
args.command !== 'preflight' &&
|
|
793
|
-
(args.changes || args.changeMap || args.
|
|
749
|
+
(args.changes || args.changeMap || args.manifest || args.tsconfig ||
|
|
750
|
+
args.config !== 'ark.config.json')
|
|
794
751
|
) {
|
|
795
|
-
console.error(
|
|
752
|
+
console.error(
|
|
753
|
+
'--changes, --change-map, --config, --manifest, and --tsconfig are supported by ark preflight.'
|
|
754
|
+
);
|
|
796
755
|
return 2;
|
|
797
756
|
}
|
|
798
757
|
const enforcement = validateHardWriteRequest({
|
|
@@ -830,7 +789,7 @@ async function main() {
|
|
|
830
789
|
|
|
831
790
|
if (args.command === 'upgrade' || args.command === 'update') {
|
|
832
791
|
try {
|
|
833
|
-
return
|
|
792
|
+
return runUpgradeCommand(args, { arkCheck, packageInstallArgv, runArkCheck });
|
|
834
793
|
} catch (error) {
|
|
835
794
|
console.error(error instanceof Error ? error.message : String(error));
|
|
836
795
|
return 2;
|
|
@@ -848,11 +807,19 @@ async function main() {
|
|
|
848
807
|
configPath
|
|
849
808
|
).config;
|
|
850
809
|
const changeMap = args.changeMap ? readChangeMapFile(args.root, args.changeMap) : undefined;
|
|
810
|
+
const manifestPath = args.manifest
|
|
811
|
+
? path.resolve(args.root, args.manifest)
|
|
812
|
+
: undefined;
|
|
813
|
+
const manifest = manifestPath ? JSON.parse(fs.readFileSync(manifestPath, 'utf8')) : undefined;
|
|
814
|
+
const loadedTypeScript = await loadTypeScript(args.root);
|
|
851
815
|
const result = prepareChangeFromRoot({
|
|
852
816
|
root: args.root,
|
|
853
817
|
config,
|
|
854
818
|
configSource: configPath,
|
|
855
819
|
changes: readChangeSetFile(args.root, args.changes),
|
|
820
|
+
ts: loadedTypeScript.ts ?? undefined,
|
|
821
|
+
tsconfig: args.tsconfig,
|
|
822
|
+
manifest,
|
|
856
823
|
...(changeMap ? { changeMap: changeMap.input, changeMapSource: changeMap.source } : {}),
|
|
857
824
|
});
|
|
858
825
|
if (args.json) console.log(JSON.stringify(result, null, 2));
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* Pure CLI helper (bin/lib/adapter-contract.mjs). Zero Node I/O.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
export const ARK_ANALYSIS_RESULT_SCHEMA_VERSION = '1.
|
|
11
|
+
export const ARK_ANALYSIS_RESULT_SCHEMA_VERSION = '1.3';
|
|
12
12
|
function text(value) {
|
|
13
13
|
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
14
14
|
}
|
|
@@ -50,6 +50,23 @@ export function toAdapterDiagnostic(violation, fallbackSeverity = 'error') {
|
|
|
50
50
|
...(text(violation.fromLayer) ? { fromLayer: text(violation.fromLayer) } : {}),
|
|
51
51
|
...(text(violation.toLayer) ? { toLayer: text(violation.toLayer) } : {}),
|
|
52
52
|
...(typeof violation.typeOnly === 'boolean' ? { typeOnly: violation.typeOnly } : {}),
|
|
53
|
+
...(typeof violation.targetTypeOnlyExports === 'boolean'
|
|
54
|
+
? { targetTypeOnlyExports: violation.targetTypeOnlyExports }
|
|
55
|
+
: {}),
|
|
56
|
+
...(typeof violation.sourcePureTypeModule === 'boolean'
|
|
57
|
+
? { sourcePureTypeModule: violation.sourcePureTypeModule }
|
|
58
|
+
: {}),
|
|
59
|
+
...(typeof violation.namedBindingsTypeOnly === 'boolean'
|
|
60
|
+
? { namedBindingsTypeOnly: violation.namedBindingsTypeOnly }
|
|
61
|
+
: {}),
|
|
62
|
+
...(typeof violation.portProofEligible === 'boolean'
|
|
63
|
+
? { portProofEligible: violation.portProofEligible }
|
|
64
|
+
: {}),
|
|
65
|
+
...(typeof violation.peerIsolation === 'boolean'
|
|
66
|
+
? { peerIsolation: violation.peerIsolation }
|
|
67
|
+
: {}),
|
|
68
|
+
...(text(violation.capability) ? { capability: text(violation.capability) } : {}),
|
|
69
|
+
...(text(violation.edgeKind) ? { edgeKind: text(violation.edgeKind) } : {}),
|
|
53
70
|
};
|
|
54
71
|
return {
|
|
55
72
|
ruleId,
|
|
@@ -65,25 +82,153 @@ export function toAdapterDiagnostic(violation, fallbackSeverity = 'error') {
|
|
|
65
82
|
};
|
|
66
83
|
}
|
|
67
84
|
export function createAdapterResult(input) {
|
|
68
|
-
|
|
85
|
+
const completeness = input.completeness ?? 'complete';
|
|
86
|
+
const mode = input.mode ?? 'lexical-compatibility';
|
|
87
|
+
if (completeness === 'complete' && (input.completenessReasons?.length ?? 0) > 0) {
|
|
88
|
+
throw new Error('completenessReasons must be empty when completeness is complete.');
|
|
89
|
+
}
|
|
90
|
+
const completenessReasons = completeness === 'complete'
|
|
91
|
+
? []
|
|
92
|
+
: input.completenessReasons && input.completenessReasons.length > 0
|
|
93
|
+
? input.completenessReasons.map((reason) => ({
|
|
94
|
+
code: text(reason.code) ?? 'ANALYSIS_EVIDENCE_INCOMPLETE',
|
|
95
|
+
message: text(reason.message) ??
|
|
96
|
+
`Analysis ${completeness}: required evidence is incomplete.`,
|
|
97
|
+
...(text(reason.file) ? { file: text(reason.file) } : {}),
|
|
98
|
+
}))
|
|
99
|
+
: [
|
|
100
|
+
{
|
|
101
|
+
code: completeness === 'unavailable'
|
|
102
|
+
? 'ANALYSIS_UNAVAILABLE'
|
|
103
|
+
: 'ANALYSIS_EVIDENCE_INCOMPLETE',
|
|
104
|
+
message: `Analysis ${completeness}: required evidence is incomplete.`,
|
|
105
|
+
},
|
|
106
|
+
];
|
|
107
|
+
const evidence = {
|
|
108
|
+
...(text(input.policyHash) ? { policyHash: text(input.policyHash) } : {}),
|
|
109
|
+
...(text(input.resolverIdentity) ? { resolverIdentity: text(input.resolverIdentity) } : {}),
|
|
110
|
+
...(text(input.factsHash) ? { factsHash: text(input.factsHash) } : {}),
|
|
111
|
+
...(text(input.candidateTreeHash) ? { candidateTreeHash: text(input.candidateTreeHash) } : {}),
|
|
112
|
+
};
|
|
113
|
+
if (mode === 'resolved-candidate-facts' && completeness !== 'unavailable') {
|
|
114
|
+
for (const field of ['policyHash', 'resolverIdentity', 'factsHash', 'candidateTreeHash']) {
|
|
115
|
+
if (!evidence[field]) {
|
|
116
|
+
throw new Error(`${field} is required for resolved ${completeness} adapter evidence.`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const diagnostics = [
|
|
121
|
+
...(input.violations ?? []).map((item) => toAdapterDiagnostic(item, 'error')),
|
|
122
|
+
...(input.warnings ?? []).map((item) => toAdapterDiagnostic(item, 'warning')),
|
|
123
|
+
];
|
|
124
|
+
const base = {
|
|
69
125
|
schemaVersion: ARK_ANALYSIS_RESULT_SCHEMA_VERSION,
|
|
70
|
-
|
|
71
|
-
diagnostics
|
|
72
|
-
...(input.violations ?? []).map((item) => toAdapterDiagnostic(item, 'error')),
|
|
73
|
-
...(input.warnings ?? []).map((item) => toAdapterDiagnostic(item, 'warning')),
|
|
74
|
-
],
|
|
126
|
+
completenessReasons,
|
|
127
|
+
diagnostics,
|
|
75
128
|
};
|
|
129
|
+
if (mode === 'resolved-candidate-facts') {
|
|
130
|
+
if (completeness === 'unavailable') {
|
|
131
|
+
return {
|
|
132
|
+
...base,
|
|
133
|
+
mode,
|
|
134
|
+
valid: false,
|
|
135
|
+
completeness,
|
|
136
|
+
...evidence,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
const resolvedEvidence = {
|
|
140
|
+
policyHash: evidence.policyHash,
|
|
141
|
+
resolverIdentity: evidence.resolverIdentity,
|
|
142
|
+
factsHash: evidence.factsHash,
|
|
143
|
+
candidateTreeHash: evidence.candidateTreeHash,
|
|
144
|
+
};
|
|
145
|
+
if (completeness === 'complete') {
|
|
146
|
+
return {
|
|
147
|
+
...base,
|
|
148
|
+
mode,
|
|
149
|
+
valid: input.valid,
|
|
150
|
+
completeness,
|
|
151
|
+
...resolvedEvidence,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
return {
|
|
155
|
+
...base,
|
|
156
|
+
mode,
|
|
157
|
+
valid: false,
|
|
158
|
+
completeness,
|
|
159
|
+
...resolvedEvidence,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
if (completeness === 'complete') {
|
|
163
|
+
return { ...base, mode, valid: input.valid, completeness, ...evidence };
|
|
164
|
+
}
|
|
165
|
+
return { ...base, mode, valid: false, completeness, ...evidence };
|
|
76
166
|
}
|
|
77
167
|
export const ARK_ANALYSIS_RESULT_SCHEMA = {
|
|
78
168
|
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
|
79
|
-
$id: 'https://unpkg.com/arkgate@
|
|
169
|
+
$id: 'https://unpkg.com/arkgate@3/schemas/ark.analysis-result.schema.json',
|
|
80
170
|
title: 'ArkGate analysis result',
|
|
81
171
|
type: 'object',
|
|
82
172
|
additionalProperties: false,
|
|
83
|
-
required: [
|
|
173
|
+
required: [
|
|
174
|
+
'schemaVersion',
|
|
175
|
+
'mode',
|
|
176
|
+
'valid',
|
|
177
|
+
'completeness',
|
|
178
|
+
'completenessReasons',
|
|
179
|
+
'diagnostics',
|
|
180
|
+
],
|
|
181
|
+
allOf: [
|
|
182
|
+
{
|
|
183
|
+
if: {
|
|
184
|
+
properties: { completeness: { enum: ['partial', 'unavailable'] } },
|
|
185
|
+
required: ['completeness'],
|
|
186
|
+
},
|
|
187
|
+
then: { properties: { valid: { const: false } } },
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
if: {
|
|
191
|
+
properties: {
|
|
192
|
+
mode: { const: 'resolved-candidate-facts' },
|
|
193
|
+
completeness: { enum: ['complete', 'partial'] },
|
|
194
|
+
},
|
|
195
|
+
required: ['mode', 'completeness'],
|
|
196
|
+
},
|
|
197
|
+
then: {
|
|
198
|
+
required: ['policyHash', 'resolverIdentity', 'factsHash', 'candidateTreeHash'],
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
if: {
|
|
203
|
+
properties: { completeness: { const: 'complete' } },
|
|
204
|
+
required: ['completeness'],
|
|
205
|
+
},
|
|
206
|
+
then: { properties: { completenessReasons: { maxItems: 0 } } },
|
|
207
|
+
else: { properties: { completenessReasons: { minItems: 1 } } },
|
|
208
|
+
},
|
|
209
|
+
],
|
|
84
210
|
properties: {
|
|
85
211
|
schemaVersion: { const: ARK_ANALYSIS_RESULT_SCHEMA_VERSION },
|
|
212
|
+
mode: { enum: ['lexical-compatibility', 'resolved-candidate-facts'] },
|
|
86
213
|
valid: { type: 'boolean' },
|
|
214
|
+
completeness: { enum: ['complete', 'partial', 'unavailable'] },
|
|
215
|
+
completenessReasons: {
|
|
216
|
+
type: 'array',
|
|
217
|
+
items: {
|
|
218
|
+
type: 'object',
|
|
219
|
+
additionalProperties: false,
|
|
220
|
+
required: ['code', 'message'],
|
|
221
|
+
properties: {
|
|
222
|
+
code: { type: 'string', minLength: 1 },
|
|
223
|
+
message: { type: 'string', minLength: 1 },
|
|
224
|
+
file: { type: 'string', minLength: 1 },
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
policyHash: { type: 'string', minLength: 1 },
|
|
229
|
+
resolverIdentity: { type: 'string', minLength: 1 },
|
|
230
|
+
factsHash: { type: 'string', minLength: 1 },
|
|
231
|
+
candidateTreeHash: { type: 'string', minLength: 1 },
|
|
87
232
|
diagnostics: {
|
|
88
233
|
type: 'array',
|
|
89
234
|
items: {
|
|
@@ -112,6 +257,13 @@ export const ARK_ANALYSIS_RESULT_SCHEMA = {
|
|
|
112
257
|
fromLayer: { type: 'string' },
|
|
113
258
|
toLayer: { type: 'string' },
|
|
114
259
|
typeOnly: { type: 'boolean' },
|
|
260
|
+
targetTypeOnlyExports: { type: 'boolean' },
|
|
261
|
+
sourcePureTypeModule: { type: 'boolean' },
|
|
262
|
+
namedBindingsTypeOnly: { type: 'boolean' },
|
|
263
|
+
portProofEligible: { type: 'boolean' },
|
|
264
|
+
peerIsolation: { type: 'boolean' },
|
|
265
|
+
capability: { type: 'string', minLength: 1 },
|
|
266
|
+
edgeKind: { type: 'string', minLength: 1 },
|
|
115
267
|
},
|
|
116
268
|
},
|
|
117
269
|
nextAction: { type: 'string', minLength: 1 },
|
package/bin/lib/agent-gates.mjs
CHANGED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** Shared fail-closed completeness semantics for CLI/MCP Tooling surfaces. */
|
|
2
|
+
|
|
3
|
+
export const ANALYSIS_COMPLETENESS = Object.freeze({
|
|
4
|
+
complete: 'complete',
|
|
5
|
+
partial: 'partial',
|
|
6
|
+
unavailable: 'unavailable',
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
export function normalizeAnalysisCompleteness(value) {
|
|
10
|
+
return value === ANALYSIS_COMPLETENESS.complete ||
|
|
11
|
+
value === ANALYSIS_COMPLETENESS.partial ||
|
|
12
|
+
value === ANALYSIS_COMPLETENESS.unavailable
|
|
13
|
+
? value
|
|
14
|
+
: ANALYSIS_COMPLETENESS.unavailable;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function completenessFromParseHealth(parseHealth) {
|
|
18
|
+
if (parseHealth?.available !== true) return ANALYSIS_COMPLETENESS.unavailable;
|
|
19
|
+
return parseHealth.affectedFiles > 0
|
|
20
|
+
? ANALYSIS_COMPLETENESS.partial
|
|
21
|
+
: ANALYSIS_COMPLETENESS.complete;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function analysisIncompleteStatement(completeness) {
|
|
25
|
+
return completeness === ANALYSIS_COMPLETENESS.partial
|
|
26
|
+
? 'Analysis incomplete: governed parse diagnostics prevent a complete architecture verdict.'
|
|
27
|
+
: 'Analysis unavailable: no API-compatible TypeScript host could produce architecture evidence.';
|
|
28
|
+
}
|