arkgate 4.3.0 → 4.4.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 +36 -10
- package/README.md +10 -7
- package/bin/ark-check-runtime.mjs +16 -1
- package/bin/lib/agent-projection.mjs +1 -1
- package/bin/lib/ci-and-commands.mjs +11 -5
- package/bin/lib/doctor-plan.mjs +25 -0
- package/bin/lib/html-report-advisories.mjs +33 -0
- package/bin/lib/html-report-depth.mjs +24 -0
- package/bin/lib/improvement-compass-doctor.mjs +106 -0
- package/bin/lib/improvement-compass.mjs +630 -0
- package/bin/lib/status-manifest.mjs +38 -1
- package/dist/index.cjs +33 -33
- package/dist/index.d.ts +159 -2
- package/dist/index.js +33 -33
- package/docs/README.md +6 -5
- package/docs/agent-guide.md +20 -4
- package/docs/develop.md +6 -3
- package/docs/diagnostics.md +1 -1
- package/docs/package-surface.md +37 -34
- package/docs/product-voice.md +26 -0
- package/docs/use.md +37 -1
- package/package.json +1 -1
- package/schemas/ark.status-manifest.schema.json +26 -0
- package/server.json +2 -2
- package/templates/agent-skills/README.md +2 -2
- package/templates/agent-skills/ark-adopt/SKILL.md +20 -0
- package/templates/agent-skills/ark-architect/SKILL.md +21 -1
- package/templates/agent-skills/ark-autopilot/SKILL.md +25 -5
- package/templates/agent-skills/ark-contract/SKILL.md +20 -0
- package/templates/agent-skills/ark-coverage/SKILL.md +20 -0
- package/templates/agent-skills/ark-explain/SKILL.md +20 -0
- package/templates/agent-skills/ark-explore/SKILL.md +23 -3
- package/templates/agent-skills/ark-fix/SKILL.md +22 -2
- package/templates/agent-skills/ark-loop/SKILL.md +22 -2
- package/templates/agent-skills/ark-place/SKILL.md +20 -0
- package/templates/agent-skills/ark-runtime/SKILL.md +7 -0
- package/templates/agent-skills/ark-think/SKILL.md +20 -0
- package/templates/agent-skills/ark-upgrade/SKILL.md +20 -0
- package/templates/skills/ark-adopt.md +20 -0
- package/templates/skills/ark-architect.md +21 -1
- package/templates/skills/ark-autopilot.md +25 -5
- package/templates/skills/ark-contract.md +20 -0
- package/templates/skills/ark-coverage.md +20 -0
- package/templates/skills/ark-explain.md +20 -0
- package/templates/skills/ark-explore.md +23 -3
- package/templates/skills/ark-fix.md +22 -2
- package/templates/skills/ark-loop.md +22 -2
- package/templates/skills/ark-place.md +20 -0
- package/templates/skills/ark-runtime.md +7 -0
- package/templates/skills/ark-think.md +20 -0
- package/templates/skills/ark-upgrade.md +20 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,14 +5,41 @@ in the immutable pre-2.0 archive linked below.
|
|
|
5
5
|
|
|
6
6
|
## Unreleased
|
|
7
7
|
|
|
8
|
-
## 4.
|
|
8
|
+
## 4.4.0 — 2026-08-09
|
|
9
9
|
|
|
10
|
-
**Minor** over 4.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
**Minor** over 4.3.0. Improvement compass (architecture lenses as a `notAScore` projection of
|
|
11
|
+
existing sensors), doctor/HTML residual coaching, deeper skills + compact router for vibe-coder
|
|
12
|
+
and full-AI workflows, and product-only public docs. **No required config migration.** Codex remains
|
|
13
|
+
advisory at write time. Skills and AGENTS projection never enforce. **Status: prepared in tree —
|
|
14
|
+
not published on npm until OIDC publish + `npm view` succeed** (see `docs/releases/4.4.0.md`).
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- **Improvement compass:** doctor human section + `doctor.improvementCompass` JSON — closed set of
|
|
19
|
+
architecture **lenses** (SoC, DIP, domain alignment, …) with status `ok` / `residual` /
|
|
20
|
+
`not-instrumented` / `out-of-scope`, evidence refs, optional next action, capped `topResidual`,
|
|
21
|
+
always **`notAScore: true`**. Projected from existing smells, walls, cohesion, ArkRules, and
|
|
22
|
+
design-weak signals. Residual lenses never flip `valid`, strict-merge, or `goal.met`.
|
|
23
|
+
Out-of-scope locked for scalability, app security tooling, and full resilience. Type-only
|
|
24
|
+
placement debt (`failsStrict: false`) maps to **modularity** residual only (not coupling).
|
|
25
|
+
- **Optional status slice:** thin `improvementCompass.topResidual` on the status manifest schema for
|
|
26
|
+
Tooling pass-through — **`ark status` does not compute lenses yet**; agents should read doctor.
|
|
27
|
+
- **HTML report:** advisory section `data-advisory="improvementCompass"` (report parity with doctor).
|
|
28
|
+
- **Skills + compact router:** same **13** skill names deepened for vibe-coder outcomes — compass
|
|
29
|
+
preflight, anti false-done, AI-easy placement cues, out-of-scope honesty, Completion **Compass**
|
|
30
|
+
bullet. Compact start router surfaces residual lenses and forbids “done” on green edges alone.
|
|
31
|
+
- **Docs:** use / develop / agent-guide describe compass + Align/Stabilize/Shape in product language.
|
|
32
|
+
|
|
33
|
+
**Freezes held:** no principle scores/ranks; no new skill names; no new ArkRules sensors; no LLM
|
|
34
|
+
verdict; projection never enforces.
|
|
35
|
+
|
|
36
|
+
## 4.3.0 — 2026-08-09
|
|
37
|
+
|
|
38
|
+
**Minor** over 4.2.1. Agent contract surface: diagnostic code catalog, unified status snapshot,
|
|
39
|
+
version-matched agent projection, Agent Skills packaging of the existing 13 skills, stable finding
|
|
40
|
+
refs, and maintainer placement A/B eval. **No required config migration.** Codex remains advisory
|
|
41
|
+
at write time. Skills and AGENTS projection never enforce. **Status: published**
|
|
42
|
+
(`arkgate@4.3.0` on npm `latest`).
|
|
16
43
|
|
|
17
44
|
### Added
|
|
18
45
|
|
|
@@ -58,15 +85,14 @@ remain open. **Status: prepared** (`arkgate@4.3.0` not published yet; **4.2.1**
|
|
|
58
85
|
### Docs / claims (ACS08)
|
|
59
86
|
|
|
60
87
|
- Claims matrix refreshed for 4.3.0 agent-contract statements (**0 Contradicted** on public
|
|
61
|
-
lanes). Release notes at `docs/releases/4.3.0.md` (**Status:
|
|
62
|
-
the prepared candidate while npm `latest` remains **4.2.1** until Trusted Publishing.
|
|
88
|
+
lanes). Release notes at `docs/releases/4.3.0.md` (**Status: published**). npm `latest` is **4.3.0**.
|
|
63
89
|
|
|
64
90
|
## 4.2.1 — 2026-08-08 (published)
|
|
65
91
|
|
|
66
92
|
**Patch** over 4.2.0. Next.js **16.3** field compatibility: root `proxy.ts` (Next 16 network-boundary
|
|
67
93
|
rename of middleware) is scanned and classified; eval fixtures and release-surface pins track
|
|
68
94
|
`next@16.3.0`. **No required config migration.** Codex remains advisory at write time.
|
|
69
|
-
**Status: published** (`arkgate@4.2.1
|
|
95
|
+
**Status: published** (`arkgate@4.2.1`; superseded by 4.3.0 on `latest`).
|
|
70
96
|
|
|
71
97
|
### Fixed
|
|
72
98
|
|
package/README.md
CHANGED
|
@@ -16,9 +16,8 @@ and makes sure a “green” check means something real.
|
|
|
16
16
|
|
|
17
17
|
</div>
|
|
18
18
|
|
|
19
|
-
> **ArkGate 4.3.0** is
|
|
20
|
-
>
|
|
21
|
-
> [4.3.0 candidate](docs/releases/4.3.0.md) · [4.2.1](docs/releases/4.2.1.md) · [4.2.0](docs/releases/4.2.0.md) · [Docs hub](docs/README.md) · [Product voice](docs/product-voice.md)
|
|
19
|
+
> **ArkGate 4.3.0** is on npm `latest`. Tree is preparing **4.4.0** (improvement compass) — see [4.4.0 notes](docs/releases/4.4.0.md) (Status: prepared until publish).
|
|
20
|
+
> [4.4.0 notes](docs/releases/4.4.0.md) · [4.3.0](docs/releases/4.3.0.md) · [4.2.1](docs/releases/4.2.1.md) · [Docs hub](docs/README.md) · [Product voice](docs/product-voice.md)
|
|
22
21
|
|
|
23
22
|
---
|
|
24
23
|
|
|
@@ -44,10 +43,13 @@ npx arkgate-check --doctor # control plane: status light + primary next a
|
|
|
44
43
|
```
|
|
45
44
|
|
|
46
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).
|
|
47
49
|
|
|
48
50
|
```text
|
|
49
|
-
start → doctor → day-to-day (place + gate)
|
|
50
|
-
|
|
51
|
+
start → doctor (+ compass) → day-to-day (place + gate)
|
|
52
|
+
↘ optional /ark-autopilot after skill pack
|
|
51
53
|
```
|
|
52
54
|
|
|
53
55
|
Aliases `ark` / `ark-check` / `ark-mcp` still work. npm / pnpm / yarn. No install lifecycle scripts.
|
|
@@ -206,8 +208,9 @@ for real systems. Details: [docs/production-hardening.md](docs/production-harden
|
|
|
206
208
|
| Config · package surface · TS | [configuration](docs/configuration.md) · [package-surface](docs/package-surface.md) · [typescript-support](docs/typescript-support.md) |
|
|
207
209
|
| Brownfield | [docs/brownfield-adoption.md](docs/brownfield-adoption.md) |
|
|
208
210
|
| Security | [SECURITY.md](SECURITY.md) |
|
|
209
|
-
|
|
|
210
|
-
|
|
|
211
|
+
| Current release (4.3.0 on npm `latest`) | [docs/releases/4.3.0.md](docs/releases/4.3.0.md) · [CHANGELOG](CHANGELOG.md) |
|
|
212
|
+
| Next prepare (4.4.0) | [docs/releases/4.4.0.md](docs/releases/4.4.0.md) (prepared — not published until npm verify) |
|
|
213
|
+
| Prior (4.2.1) | [docs/releases/4.2.1.md](docs/releases/4.2.1.md) |
|
|
211
214
|
| Previous (4.2.0) | [docs/releases/4.2.0.md](docs/releases/4.2.0.md) |
|
|
212
215
|
| Previous (4.1.1) | [docs/releases/4.1.1.md](docs/releases/4.1.1.md) |
|
|
213
216
|
| Previous (4.1.0) | [docs/releases/4.1.0.md](docs/releases/4.1.0.md) |
|
|
@@ -1576,6 +1576,14 @@ async function main() {
|
|
|
1576
1576
|
: readJsonSafe(path.join(reportsDir(root), 'origin.json'));
|
|
1577
1577
|
// Pass the same baseline split as doctor so productHonesty dirty-freeze matches.
|
|
1578
1578
|
const reportBaseline = readBaseline(root, args.baseline || '.ark-baseline.json');
|
|
1579
|
+
// Doctor parity for improvement compass: stale keys = baseline keys not in current occurrence set.
|
|
1580
|
+
// Only when analysis is complete — partial scans under-count current keys and inflate false stale residual
|
|
1581
|
+
// (same gate as doctor-plan: baselineStale: analysisComplete ? staleBaseline : null).
|
|
1582
|
+
const reportOccurrenceKeys = baselineOccurrenceKeys(violations);
|
|
1583
|
+
const reportCurrentKeys = new Set(reportOccurrenceKeys);
|
|
1584
|
+
const reportBaselineStale = reportBaseline.exists
|
|
1585
|
+
? [...reportBaseline.keys].filter((key) => !reportCurrentKeys.has(key)).length
|
|
1586
|
+
: 0;
|
|
1579
1587
|
const { adoption: adoptionForReport, designDepth } = buildReportDepthPayload(
|
|
1580
1588
|
root,
|
|
1581
1589
|
config,
|
|
@@ -1588,6 +1596,7 @@ async function main() {
|
|
|
1588
1596
|
frozenKeys: reportBaseline.exists ? reportBaseline.keys.size : 0,
|
|
1589
1597
|
activeCount: activeViolations.length,
|
|
1590
1598
|
activeBlockingCount: blockingViolations.length,
|
|
1599
|
+
baselineStale: analysisComplete ? reportBaselineStale : null,
|
|
1591
1600
|
}
|
|
1592
1601
|
);
|
|
1593
1602
|
const reportPayload = {
|
|
@@ -1608,7 +1617,13 @@ async function main() {
|
|
|
1608
1617
|
originJustCreated: !existingOrigin,
|
|
1609
1618
|
adoption: adoptionForReport,
|
|
1610
1619
|
designDepth,
|
|
1611
|
-
advisories:
|
|
1620
|
+
advisories: {
|
|
1621
|
+
...computeDoctorAdvisories(root, config, coverage, rules, files, ts, parseHealth),
|
|
1622
|
+
// Doctor parity: always emit improvement compass when doctor would (reportParity).
|
|
1623
|
+
...(designDepth?.improvementCompass
|
|
1624
|
+
? { improvementCompass: designDepth.improvementCompass }
|
|
1625
|
+
: {}),
|
|
1626
|
+
},
|
|
1612
1627
|
};
|
|
1613
1628
|
const html = args.beginner
|
|
1614
1629
|
? renderBeginnerHtmlReport(reportPayload)
|
|
@@ -148,7 +148,7 @@ export function buildAgentProjectionBody(facts) {
|
|
|
148
148
|
'',
|
|
149
149
|
];
|
|
150
150
|
if (profile === 'compact') {
|
|
151
|
-
lines.push('### Primary path', '', '1. Call `ark_identity` with `project.expectedRoot` at the exact project root; reuse root + `projectId` on Ark MCP calls.', '
|
|
151
|
+
lines.push('### Primary path', '', '1. Run doctor (`ark-check --doctor`) — status light + primary next action.', '2. Read the improvement compass (not a score). Name residual lenses in plain language; never “done” on green edges alone while residual remains.', '3. Call `ark_identity` with `project.expectedRoot` at the exact project root; reuse root + `projectId` on Ark MCP calls.', '4. Read the contract with `ark_manifest` (same expectation). `ark://manifest` is compatibility-only / unverified.', '5. Place files inside configured layers; validate; run the check command above on violations — fix architecture, do not weaken the gate.', '6. Single door: edges debt → fix; design-weak / residual shape lenses → map then guided apply with user OK.', '', '### Contract layers (summary)', '', formatAgentProjectionLayers(layers), '');
|
|
152
152
|
}
|
|
153
153
|
else {
|
|
154
154
|
lines.push('### Contract layers', '', formatAgentProjectionLayers(layers), '', 'When creating a **new** kind of code that no layer covers, update `ark.config.json` first (`/ark-contract`), then place the file.', '', '### Diagnostic codes (short list)', '', formatAgentProjectionCatalogShortList(catalog, docsPath), '', '### Session truth', '', '- Machine snapshot: `ark status --json` (or MCP `ark_status`) — identity, activation honesty, last check, residual counts. **Not a score.**', '- Authoritative contract: `ark_manifest` after a matched `ark_identity` handshake.', '');
|
|
@@ -403,10 +403,16 @@ ${projectionBlock}
|
|
|
403
403
|
|
|
404
404
|
**Primary path (do this):**
|
|
405
405
|
|
|
406
|
-
1. Status anytime: \`${doctorCmd}\` — one status light, one next action (control plane).
|
|
407
|
-
2.
|
|
408
|
-
3.
|
|
409
|
-
4.
|
|
406
|
+
1. Status anytime: \`${doctorCmd}\` — one status light, one primary next action (control plane).
|
|
407
|
+
2. Read the **Improvement compass** section (not a score). Name residual lenses in plain language when present (SoC, DIP, domain, …). Out-of-scope lenses (performance, app security tooling, full resilience) stay honest — do not invent Ark enforcement for them.
|
|
408
|
+
3. Before trusting MCP evidence: call \`ark_identity\` with \`project.expectedRoot\` set to this project's exact absolute root, then reuse that root plus the returned \`projectIdentity.projectId\` on every Ark MCP call. A descendant path is authoritative only with that matching id. Missing tool, non-\`matched\` binding, or wrong root means the process is stale: restart the host and use the local CLI meanwhile.
|
|
409
|
+
4. Day to day: call \`ark_manifest\` with the same project expectation; place new files with \`ark_place\`; validate after edits; run \`${checkCmd}\`. The \`ark://manifest\` resource is compatibility-only and always unverified/non-authoritative. On a gate deny, fix the architecture — do not weaken the contract.
|
|
410
|
+
5. If MCP is unavailable: inspect \`ark.config.json\` and run \`${checkCmd}\`.
|
|
411
|
+
|
|
412
|
+
**Single door when residual remains:**
|
|
413
|
+
- **Edges debt** (import/capability violations) → fix with the gate / plan; skill pack only if doctor names a skill.
|
|
414
|
+
- **Design-weak / residual shape lenses** (compass residual while edges may look green) → map first, then guided apply with user OK — never “you’re done” on green edges alone.
|
|
415
|
+
- Empty plan A + residual lenses / design-weak → **not finished**.
|
|
410
416
|
|
|
411
417
|
The selected host is \`${selectedHost}\`. Host registration and CI are installed with this file.
|
|
412
418
|
This compact router is enough for normal feature work.
|
|
@@ -414,7 +420,7 @@ This compact router is enough for normal feature work.
|
|
|
414
420
|
## Expert depth (optional)
|
|
415
421
|
|
|
416
422
|
Full \`/ark-*\` skills (including guided end-to-end \`/ark-autopilot\`) are **not** the default
|
|
417
|
-
curriculum. Install them only when doctor top action #1 or a STOP handoff names a skill:
|
|
423
|
+
curriculum. Install them only when doctor top action #1, residual compass, or a STOP handoff names a skill:
|
|
418
424
|
|
|
419
425
|
\`${installSkills}\`
|
|
420
426
|
`;
|
package/bin/lib/doctor-plan.mjs
CHANGED
|
@@ -62,6 +62,10 @@ import { computeDoctorAdvisories, printDoctorAdvisories } from './doctor-advisor
|
|
|
62
62
|
import { ANALYSIS_COMPLETENESS, analysisIncompleteStatement, normalizeAnalysisCompleteness } from './analysis-completeness.mjs';
|
|
63
63
|
import { designDeltaDoctorLines } from './design-delta.mjs';
|
|
64
64
|
import { enforcementDoctorLines } from './enforcement-state.mjs';
|
|
65
|
+
import {
|
|
66
|
+
buildDoctorImprovementCompass,
|
|
67
|
+
printImprovementCompassSection,
|
|
68
|
+
} from './improvement-compass-doctor.mjs';
|
|
65
69
|
|
|
66
70
|
const color = {
|
|
67
71
|
green: (s) => `\x1b[32m${s}\x1b[0m`,
|
|
@@ -640,6 +644,23 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
|
|
|
640
644
|
packageVersionTruth?.code === 'PACKAGE_PIN_SELF_HOST',
|
|
641
645
|
});
|
|
642
646
|
|
|
647
|
+
// Improvement compass: projection only — never feeds ok/valid/goal.met.
|
|
648
|
+
const improvementCompass = buildDoctorImprovementCompass({
|
|
649
|
+
designSmells,
|
|
650
|
+
violations,
|
|
651
|
+
designWeak: designFitness.designWeak === true,
|
|
652
|
+
physicalCohesion: doctorAdvisories.physicalCohesion,
|
|
653
|
+
rulesUnderContract,
|
|
654
|
+
baselineExists: baseline.exists,
|
|
655
|
+
baselineStale: analysisComplete ? staleBaseline : null,
|
|
656
|
+
frozenResidual: baseline.exists ? baseline.keys.size : null,
|
|
657
|
+
dirtyBaselineRisk: productHonesty?.reasonIds?.includes?.('dirty-baseline') === true,
|
|
658
|
+
ungovernedDirCount: cov.suggestions?.length ?? 0,
|
|
659
|
+
emptyLayerCount: cov.emptyLayers?.length ?? 0,
|
|
660
|
+
goldenPatternPresent: goldenPattern.present === true,
|
|
661
|
+
arkRulesLoaded: rulesUnderContract?.active === true,
|
|
662
|
+
});
|
|
663
|
+
|
|
643
664
|
if (asJson) {
|
|
644
665
|
(options.writeJson ?? console.log)(
|
|
645
666
|
JSON.stringify(
|
|
@@ -654,6 +675,8 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
|
|
|
654
675
|
// Path-correct ENFORCE can still be design-weak (P02).
|
|
655
676
|
designFitness,
|
|
656
677
|
designSmells,
|
|
678
|
+
// Improvement compass (lenses; notAScore; never a gate input).
|
|
679
|
+
improvementCompass,
|
|
657
680
|
...(options.designDelta ? { designDelta: options.designDelta } : {}),
|
|
658
681
|
// Q01: primary next action when Shape residual dominates (null if not design-weak).
|
|
659
682
|
postGreenPath,
|
|
@@ -846,6 +869,8 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
|
|
|
846
869
|
}
|
|
847
870
|
}
|
|
848
871
|
|
|
872
|
+
printImprovementCompassSection(improvementCompass, { line, warn, ok, color });
|
|
873
|
+
|
|
849
874
|
console.log('');
|
|
850
875
|
console.log(color.bold('Design fitness'));
|
|
851
876
|
if (designSmells.length === 0) {
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import { effectiveCapabilityDeny } from './analysis-engine.mjs';
|
|
11
11
|
import { graphBlindSpotsHtml } from './graph-blind.mjs';
|
|
12
12
|
import { formatRulesUnderContractHtml } from './rules-under-contract.mjs';
|
|
13
|
+
import { primaryImprovementCompassNextAction } from './improvement-compass.mjs';
|
|
13
14
|
|
|
14
15
|
// htmlEscape is injected by the caller (html-report.mjs) — importing it back
|
|
15
16
|
// would be a dependency cycle, and the repo's own gate blocks that. The
|
|
@@ -247,10 +248,42 @@ function rulesUnderContractHtml(section) {
|
|
|
247
248
|
return formatRulesUnderContractHtml(section, esc);
|
|
248
249
|
}
|
|
249
250
|
|
|
251
|
+
/** Improvement compass — advisory lenses only; never a score bar or gate input. */
|
|
252
|
+
function improvementCompassHtml(compass) {
|
|
253
|
+
if (!compass || compass.notAScore !== true || !Array.isArray(compass.lenses)) return '';
|
|
254
|
+
const residual = Array.isArray(compass.topResidual) ? compass.topResidual : [];
|
|
255
|
+
const residualLine =
|
|
256
|
+
residual.length === 0
|
|
257
|
+
? '<p class="muted">Residual: none on instrumented lenses (not a score — green edges ≠ finished design).</p>'
|
|
258
|
+
: `<p><span class="tag warn">residual</span> ${residual
|
|
259
|
+
.map((id) => {
|
|
260
|
+
const lens = compass.lenses.find((l) => l.id === id);
|
|
261
|
+
return `<code>${esc(id)}</code>${lens?.summary ? ` — ${esc(lens.summary)}` : ''}`;
|
|
262
|
+
})
|
|
263
|
+
.join('<br/>')}</p>`;
|
|
264
|
+
const oos = compass.lenses
|
|
265
|
+
.filter((l) => l && l.status === 'out-of-scope')
|
|
266
|
+
.map((l) => `<code>${esc(l.id)}</code>`)
|
|
267
|
+
.join(' · ');
|
|
268
|
+
// Same primary next as doctor human: severity-ordered topResidual, not lens-id order.
|
|
269
|
+
const next = primaryImprovementCompassNextAction(compass);
|
|
270
|
+
const nextLine = next
|
|
271
|
+
? `<p class="muted">Next: <code>${esc(next.ref)}</code> — ${esc(next.summary)}</p>`
|
|
272
|
+
: '';
|
|
273
|
+
return `
|
|
274
|
+
<section class="section card" data-advisory="improvementCompass">
|
|
275
|
+
<h2>Improvement compass <span class="muted">(not a score — projection only; never changes the verdict)</span></h2>
|
|
276
|
+
${residualLine}
|
|
277
|
+
<p class="muted">Out of scope (honest): ${oos || 'scalability · resilience · security'}</p>
|
|
278
|
+
${nextLine}
|
|
279
|
+
</section>`;
|
|
280
|
+
}
|
|
281
|
+
|
|
250
282
|
export function renderAdvisorySections(advisories, escape) {
|
|
251
283
|
if (!advisories || typeof advisories !== 'object') return '';
|
|
252
284
|
if (typeof escape === 'function') esc = escape;
|
|
253
285
|
return [
|
|
286
|
+
improvementCompassHtml(advisories.improvementCompass),
|
|
254
287
|
contractHealthHtml(advisories.contractHealth),
|
|
255
288
|
ambientStateHtml(advisories.ambientState),
|
|
256
289
|
physicalCohesionHtml(advisories.physicalCohesion),
|
|
@@ -21,6 +21,8 @@ import {
|
|
|
21
21
|
import { summarizeRulesUnderContract } from './rules-under-contract.mjs';
|
|
22
22
|
import { readBaseline, baselineOccurrenceKeys } from './violations.mjs';
|
|
23
23
|
import { describePackageVersionDualTruth } from './field-install.mjs';
|
|
24
|
+
import { buildDoctorImprovementCompass } from './improvement-compass-doctor.mjs';
|
|
25
|
+
import { computePhysicalCohesion } from './physical-cohesion.mjs';
|
|
24
26
|
|
|
25
27
|
function esc(value) {
|
|
26
28
|
return String(value)
|
|
@@ -43,6 +45,7 @@ function esc(value) {
|
|
|
43
45
|
* frozenKeys?: number,
|
|
44
46
|
* activeCount?: number,
|
|
45
47
|
* activeBlockingCount?: number,
|
|
48
|
+
* baselineStale?: number | null,
|
|
46
49
|
* }} [baselineSplit] same numbers doctor uses (do not recompute from active-only list)
|
|
47
50
|
*/
|
|
48
51
|
export function buildReportDepthPayload(
|
|
@@ -155,6 +158,26 @@ export function buildReportDepthPayload(
|
|
|
155
158
|
primaryNextAction: postGreenPath?.action ?? dualTruthNext,
|
|
156
159
|
activeBlockingViolations: activeBlockingCount,
|
|
157
160
|
});
|
|
161
|
+
// Doctor parity: same physical-cohesion + baseline stale facts as runDoctor.
|
|
162
|
+
const physicalCohesion = computePhysicalCohesion(root, files);
|
|
163
|
+
const baselineStale =
|
|
164
|
+
typeof baselineSplit.baselineStale === 'number' ? baselineSplit.baselineStale : null;
|
|
165
|
+
// Improvement compass — same projection as doctor; notAScore; never a gate input.
|
|
166
|
+
const improvementCompass = buildDoctorImprovementCompass({
|
|
167
|
+
designSmells,
|
|
168
|
+
violations: activeViolations,
|
|
169
|
+
designWeak: designFitness.designWeak === true,
|
|
170
|
+
physicalCohesion,
|
|
171
|
+
rulesUnderContract,
|
|
172
|
+
baselineExists: baseline.exists || frozenKeys > 0,
|
|
173
|
+
baselineStale,
|
|
174
|
+
frozenResidual: frozenKeys,
|
|
175
|
+
dirtyBaselineRisk: productHonesty?.reasonIds?.includes?.('dirty-baseline') === true,
|
|
176
|
+
ungovernedDirCount: coverage?.suggestions?.length ?? 0,
|
|
177
|
+
emptyLayerCount: coverage?.emptyLayers?.length ?? 0,
|
|
178
|
+
goldenPatternPresent: goldenPattern.present === true,
|
|
179
|
+
arkRulesLoaded: rulesUnderContract?.active === true,
|
|
180
|
+
});
|
|
158
181
|
return {
|
|
159
182
|
adoption,
|
|
160
183
|
designDepth: {
|
|
@@ -166,6 +189,7 @@ export function buildReportDepthPayload(
|
|
|
166
189
|
// P0-B / P1-M — folded into designDepth so --report stays a single payload.
|
|
167
190
|
productHonesty,
|
|
168
191
|
mergePlanes: rulesUnderContract?.mergePlanes ?? null,
|
|
192
|
+
improvementCompass,
|
|
169
193
|
},
|
|
170
194
|
};
|
|
171
195
|
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Doctor adapter for the Domain improvement compass (notAScore projection).
|
|
3
|
+
* Keeps doctor-plan.mjs inside its module budget; pure assembly only.
|
|
4
|
+
*/
|
|
5
|
+
import {
|
|
6
|
+
buildImprovementCompass,
|
|
7
|
+
formatImprovementCompassDoctorLines,
|
|
8
|
+
primaryImprovementCompassNextAction,
|
|
9
|
+
} from './improvement-compass.mjs';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @param {{
|
|
13
|
+
* designSmells?: object[],
|
|
14
|
+
* violations?: object[],
|
|
15
|
+
* designWeak?: boolean,
|
|
16
|
+
* physicalCohesion?: { findings?: object[] } | null,
|
|
17
|
+
* rulesUnderContract?: object | null,
|
|
18
|
+
* baselineExists?: boolean,
|
|
19
|
+
* baselineStale?: number | null,
|
|
20
|
+
* frozenResidual?: number | null,
|
|
21
|
+
* dirtyBaselineRisk?: boolean,
|
|
22
|
+
* ungovernedDirCount?: number,
|
|
23
|
+
* emptyLayerCount?: number,
|
|
24
|
+
* goldenPatternPresent?: boolean,
|
|
25
|
+
* arkRulesLoaded?: boolean,
|
|
26
|
+
* }} input
|
|
27
|
+
*/
|
|
28
|
+
export function buildDoctorImprovementCompass(input = {}) {
|
|
29
|
+
const violations = Array.isArray(input.violations) ? input.violations : [];
|
|
30
|
+
const ruleId = (v) => String(v?.ruleId ?? v?.code ?? '');
|
|
31
|
+
|
|
32
|
+
let cycleCount = 0;
|
|
33
|
+
let peerIsolationCount = 0;
|
|
34
|
+
let pureOrCapabilityResidual = 0;
|
|
35
|
+
let forbiddenGlobalResidual = 0;
|
|
36
|
+
let arkRulesStructureResidual = 0;
|
|
37
|
+
|
|
38
|
+
for (const v of violations) {
|
|
39
|
+
const id = ruleId(v).toUpperCase();
|
|
40
|
+
if (!id) continue;
|
|
41
|
+
if (id.includes('CYCLE') || id === 'CIRCULAR_DEPENDENCY') cycleCount += 1;
|
|
42
|
+
if (id.includes('PEER_ISOLATION')) peerIsolationCount += 1;
|
|
43
|
+
if (id === 'CAPABILITY_VIOLATION') pureOrCapabilityResidual += 1;
|
|
44
|
+
if (id === 'FORBIDDEN_GLOBAL' || id.startsWith('FORBIDDEN_')) forbiddenGlobalResidual += 1;
|
|
45
|
+
if (id.startsWith('ARKRULE_') || id === 'INVARIANT_UNCOVERED') arkRulesStructureResidual += 1;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const pcFindings = input.physicalCohesion?.findings;
|
|
49
|
+
const physicalCohesionFindingCount = Array.isArray(pcFindings) ? pcFindings.length : 0;
|
|
50
|
+
|
|
51
|
+
const arkRulesLoaded =
|
|
52
|
+
input.arkRulesLoaded === true ||
|
|
53
|
+
input.rulesUnderContract?.active === true ||
|
|
54
|
+
(typeof input.rulesUnderContract?.structureRules === 'number' &&
|
|
55
|
+
input.rulesUnderContract.structureRules > 0);
|
|
56
|
+
|
|
57
|
+
return buildImprovementCompass({
|
|
58
|
+
designSmells: Array.isArray(input.designSmells) ? input.designSmells : [],
|
|
59
|
+
violations: violations.map((v) => ({
|
|
60
|
+
ruleId: ruleId(v) || undefined,
|
|
61
|
+
message: typeof v?.message === 'string' ? v.message : undefined,
|
|
62
|
+
file: typeof v?.file === 'string' ? v.file : typeof v?.path === 'string' ? v.path : undefined,
|
|
63
|
+
fromLayer: v?.fromLayer,
|
|
64
|
+
toLayer: v?.toLayer,
|
|
65
|
+
failsStrict: v?.failsStrict,
|
|
66
|
+
typeOnly: v?.typeOnly === true || v?.namedBindingsTypeOnly === true || undefined,
|
|
67
|
+
})),
|
|
68
|
+
cycleCount,
|
|
69
|
+
peerIsolationCount,
|
|
70
|
+
physicalCohesionFindingCount,
|
|
71
|
+
arkRulesLoaded,
|
|
72
|
+
arkRulesStructureResidual,
|
|
73
|
+
designWeak: input.designWeak === true,
|
|
74
|
+
baselineExists: input.baselineExists === true,
|
|
75
|
+
baselineStale: input.baselineStale ?? null,
|
|
76
|
+
frozenResidual: input.frozenResidual ?? null,
|
|
77
|
+
dirtyBaselineRisk: input.dirtyBaselineRisk === true,
|
|
78
|
+
pureOrCapabilityResidual,
|
|
79
|
+
forbiddenGlobalResidual,
|
|
80
|
+
ungovernedDirCount: Number(input.ungovernedDirCount) || 0,
|
|
81
|
+
emptyLayerCount: Number(input.emptyLayerCount) || 0,
|
|
82
|
+
goldenPatternPresent: input.goldenPatternPresent === true,
|
|
83
|
+
// Doctor path is TypeScript-oriented (ArkGate product surface).
|
|
84
|
+
stackKind: 'typescript',
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export {
|
|
89
|
+
formatImprovementCompassDoctorLines,
|
|
90
|
+
primaryImprovementCompassNextAction,
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Human doctor section (never a score bar).
|
|
95
|
+
* @param {import('./improvement-compass.mjs').ImprovementCompass} compass
|
|
96
|
+
* @param {{ line: Function, warn: string, ok: string, color: { bold: Function } }} io
|
|
97
|
+
*/
|
|
98
|
+
export function printImprovementCompassSection(compass, io) {
|
|
99
|
+
const { line, warn, ok, color } = io;
|
|
100
|
+
console.log('');
|
|
101
|
+
console.log(color.bold('Improvement compass (not a score)'));
|
|
102
|
+
const mark = compass.topResidual.length > 0 ? warn : ok;
|
|
103
|
+
for (const text of formatImprovementCompassDoctorLines(compass)) {
|
|
104
|
+
line(mark, text);
|
|
105
|
+
}
|
|
106
|
+
}
|