arkgate 4.8.20 → 4.8.21
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 +124 -5
- package/README.md +107 -117
- package/bin/ark-check-runtime.mjs +3 -4
- package/bin/ark-layer-match.mjs +110 -18
- package/bin/lib/analysis-engine.mjs +13 -6
- package/bin/lib/config-contract.mjs +5 -0
- package/bin/lib/diagnostic-catalog.mjs +1 -0
- package/bin/lib/doctor-advisories.mjs +20 -0
- package/bin/lib/doctor-human.mjs +11 -9
- package/bin/lib/doctor-plan.mjs +15 -14
- package/bin/lib/first-run-help.mjs +7 -1
- package/bin/lib/html-report-advisories.mjs +8 -0
- package/bin/lib/html-report-depth.mjs +9 -8
- package/bin/lib/invariant-coverage.mjs +396 -75
- package/bin/lib/managed-upgrade.mjs +59 -14
- package/bin/lib/pilot-loop.mjs +183 -37
- package/bin/lib/upgrade-behavior-changes.mjs +83 -0
- package/bin/lib/upgrade-command.mjs +69 -31
- package/bin/lib/upgrade-outcome.mjs +359 -0
- package/bin/lib/violations.mjs +109 -0
- package/dist/{configTypes-VVknqcTJ.d.ts → configTypes-CTELo87-.d.ts} +7 -0
- package/dist/{diagnosticCatalog-BPYWNyJR.d.ts → diagnosticCatalog-B7BXlet6.d.ts} +73 -5
- package/dist/eslint/index.cjs +4 -4
- package/dist/eslint/index.d.ts +26 -3
- package/dist/eslint/index.js +4 -4
- package/dist/index.cjs +24 -17
- package/dist/index.d.ts +9 -4
- package/dist/index.js +25 -18
- package/dist/nestjs/index.cjs +9 -9
- package/dist/nestjs/index.d.ts +3 -3
- package/dist/nestjs/index.js +9 -9
- package/dist/runtime/index.cjs +18 -11
- package/dist/runtime/index.d.ts +6 -6
- package/dist/runtime/index.js +18 -11
- package/dist/{types-p_fCp6P-.d.ts → types-fQ9-KrBj.d.ts} +1 -1
- package/dist/{types-Cs-46DRu.d.ts → types-yDNEQ603.d.ts} +1 -1
- package/docs/README.md +4 -3
- package/docs/agent-guide.md +15 -7
- package/docs/brownfield-adoption.md +4 -3
- package/docs/configuration.md +34 -11
- package/docs/diagnostics.md +12 -2
- package/docs/package-surface.md +3 -2
- package/docs/product-voice.md +11 -3
- package/package.json +1 -1
- package/schemas/ark.config.schema.json +8 -0
- package/server.json +2 -2
- package/templates/agent-skills/README.md +1 -1
- package/templates/agent-skills/ark-adopt/SKILL.md +5 -2
- package/templates/agent-skills/ark-architect/SKILL.md +2 -0
- package/templates/agent-skills/ark-autopilot/SKILL.md +6 -4
- package/templates/agent-skills/ark-contract/SKILL.md +2 -0
- package/templates/agent-skills/ark-coverage/SKILL.md +2 -0
- package/templates/agent-skills/ark-explain/SKILL.md +2 -0
- package/templates/agent-skills/ark-explore/SKILL.md +4 -2
- package/templates/agent-skills/ark-fix/SKILL.md +2 -0
- package/templates/agent-skills/ark-loop/SKILL.md +2 -0
- package/templates/agent-skills/ark-order/SKILL.md +2 -0
- package/templates/agent-skills/ark-place/SKILL.md +2 -0
- package/templates/agent-skills/ark-runtime/SKILL.md +2 -0
- package/templates/agent-skills/ark-think/SKILL.md +2 -0
- package/templates/agent-skills/ark-upgrade/SKILL.md +20 -7
- package/templates/skills/ark-adopt.md +5 -2
- package/templates/skills/ark-architect.md +2 -0
- package/templates/skills/ark-autopilot.md +6 -4
- package/templates/skills/ark-contract.md +2 -0
- package/templates/skills/ark-coverage.md +2 -0
- package/templates/skills/ark-explain.md +2 -0
- package/templates/skills/ark-explore.md +4 -2
- package/templates/skills/ark-fix.md +2 -0
- package/templates/skills/ark-loop.md +2 -0
- package/templates/skills/ark-order.md +2 -0
- package/templates/skills/ark-place.md +2 -0
- package/templates/skills/ark-runtime.md +2 -0
- package/templates/skills/ark-think.md +2 -0
- package/templates/skills/ark-upgrade.md +20 -7
|
@@ -214,6 +214,11 @@ export const ARK_CONFIG_SCHEMA = {
|
|
|
214
214
|
message: { type: 'string', minLength: 1 },
|
|
215
215
|
peerIsolation: { type: 'boolean' },
|
|
216
216
|
sliceFolders: { ...stringArraySchema, minItems: 1 },
|
|
217
|
+
sliceIdentity: {
|
|
218
|
+
type: 'string',
|
|
219
|
+
enum: ['path', 'stars'],
|
|
220
|
+
description: 'How a starred sliceFolders prefix is named. Absent and path keep today\'s ids (every literal and star binding). stars keeps the last literal plus the star bindings, so parallel trees share one feature id. Bare names are unchanged.',
|
|
221
|
+
},
|
|
217
222
|
sharedRoots: { ...stringArraySchema, minItems: 1 },
|
|
218
223
|
allowedCrossSlice: {
|
|
219
224
|
type: 'array',
|
|
@@ -120,6 +120,7 @@ export const DIAGNOSTIC_CATALOG = Object.freeze([
|
|
|
120
120
|
entry('CONFIG_AMBIGUOUS_LAYERS', 'config', 'Ambiguous layer classification', 'Some files match multiple layers at equal specificity; classification falls back to declaration order.', 'Disambiguate overlapping patterns so each file has one clear layer owner.', { oftenAdvisory: true }),
|
|
121
121
|
entry('CONFIG_UNCLASSIFIED_FILES', 'config', 'Unclassified included files', 'Included source files match no layer pattern; import rules will not enforce on them.', 'Extend layer patterns or narrow include so every governed file is classified.', { oftenAdvisory: true }),
|
|
122
122
|
entry('CONFIG_LAYER_MISSING_OWNER', 'config', 'Layer missing owner', 'requireLayerOwners is on and this layer has no owners. Writes to that house fail closed, like a folder with no name on the door.', 'Add a GitHub handle or email to that layer’s owners in ark.config.json (/ark-adopt), then re-run. Reserved/allowEmpty houses may stay unnamed. Turn the flag off only if you no longer want owners required.'),
|
|
123
|
+
entry('CONFIG_SLICE_IDENTITY_COLLISION', 'config', 'Starred slice prefixes share one id', 'sliceIdentity is stars and two different sliceFolders prefixes bind as the same slice id. Parallel trees of one feature do that on purpose; unrelated trees that share the last folder name merge by accident.', 'Keep stars when those prefixes are one feature. Otherwise use path, or rename the last literal so the prefixes do not bind as the same id. Doctor names both paths.', { oftenAdvisory: true }),
|
|
123
124
|
// ── literal path drift ───────────────────────────────────────────────────
|
|
124
125
|
entry('LITERAL_PATH_DRIFT', 'drift', 'Literal path moved by a rename', 'A repo path written inside a string, a comment or a docstring no longer resolves, and the rename set says where it went. Nothing in the gate sees this class: `tsc` resolves imports, not strings, and ESLint does not either, so the rename compiles green and the reference lies afterwards. It appears in four forms — the tsconfig alias, a relative literal, a path written without the include-root prefix, and prose — and a hand sweep reliably covers one of them.', 'Apply the suggested replacement, or re-run `npx arkgate-check --path-drift --base-ref <ref> --write` to apply every writable anchored replacement at once. The rewrite is mechanical and one-directional: the destination comes from the rename, it must itself resolve and be path-shaped, and the token is rewritten in the form the author wrote it in. A destination that leaves the alias root of the literal is reported with the target only and must be rewritten by hand.'),
|
|
125
126
|
entry('LITERAL_PATH_UNRESOLVED', 'drift', 'Literal path does not resolve', 'A literal that looks like a repo path does not resolve under this root, and no rename explains where it went. Unlike LITERAL_PATH_DRIFT this is a candidate, not a verdict: with nothing to anchor it, ArkGate cannot tell a dead reference from an illustrative path in a comment, an example in documentation, or a path belonging to another tree.', 'Read the candidate and decide: fix the path, or leave it. Advisory only — it never fails a run and is never rewritten by --write, because there is no destination to propose. Run `--path-drift --all` to list the sweep.', { oftenAdvisory: true }),
|
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
} from './ark-order-doctor.mjs';
|
|
38
38
|
import { composeMergePlanesHonesty } from './extra-merge-teeth.mjs';
|
|
39
39
|
import { collectPrototypeShortcutsResidual } from './prototype-shortcuts.mjs';
|
|
40
|
+
import { sliceIdentityCollisions } from '../ark-layer-match.mjs';
|
|
40
41
|
|
|
41
42
|
export function attachExtraDoctorSections(rulesUnderContract, config, classification, findings) {
|
|
42
43
|
const arkRulesMerge = {
|
|
@@ -122,6 +123,13 @@ export function printCompactExtraDoctorLines(advisories, io) {
|
|
|
122
123
|
if (states.nextAction) io.line(' ', `Next: ${states.nextAction}`);
|
|
123
124
|
}
|
|
124
125
|
}
|
|
126
|
+
const sliceIdentity = advisories?.sliceIdentity;
|
|
127
|
+
if (Array.isArray(sliceIdentity?.collisions) && sliceIdentity.collisions.length > 0) {
|
|
128
|
+
console.log('');
|
|
129
|
+
for (const collision of sliceIdentity.collisions) {
|
|
130
|
+
io.line(io.warn, collision.message);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
125
133
|
const noDomain = advisories?.noDomainFrontend;
|
|
126
134
|
if (noDomain?.ask) {
|
|
127
135
|
console.log('');
|
|
@@ -247,8 +255,12 @@ export function computeDoctorAdvisories(root, config, cov, rules, files, ts, par
|
|
|
247
255
|
coverage: cov,
|
|
248
256
|
files,
|
|
249
257
|
});
|
|
258
|
+
const sliceIdentityHits = sliceIdentityCollisions(rules ?? config?.rules);
|
|
250
259
|
return {
|
|
251
260
|
...(prototypeShortcuts ? { prototypeShortcuts } : {}),
|
|
261
|
+
...(sliceIdentityHits.length > 0
|
|
262
|
+
? { sliceIdentity: { notAScore: true, collisions: sliceIdentityHits } }
|
|
263
|
+
: {}),
|
|
252
264
|
contractHealth: computeContractHealth(root, config, cov, rules),
|
|
253
265
|
ambientState: computeAmbientState(ts, root, config, files),
|
|
254
266
|
physicalCohesion,
|
|
@@ -265,6 +277,14 @@ export function computeDoctorAdvisories(root, config, cov, rules, files, ts, par
|
|
|
265
277
|
}
|
|
266
278
|
|
|
267
279
|
export function printDoctorAdvisories(advisories, io) {
|
|
280
|
+
const sliceIdentity = advisories?.sliceIdentity;
|
|
281
|
+
if (Array.isArray(sliceIdentity?.collisions) && sliceIdentity.collisions.length > 0) {
|
|
282
|
+
console.log('');
|
|
283
|
+
console.log(io.color.bold('Slice identity'));
|
|
284
|
+
for (const collision of sliceIdentity.collisions) {
|
|
285
|
+
io.line(io.warn, collision.message);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
268
288
|
printContractHealthSection(advisories.contractHealth, io);
|
|
269
289
|
printAmbientStateSection(advisories.ambientState, io);
|
|
270
290
|
printPhysicalCohesionSection(
|
package/bin/lib/doctor-human.mjs
CHANGED
|
@@ -14,6 +14,7 @@ import { analysisIncompleteStatement } from './analysis-completeness.mjs';
|
|
|
14
14
|
import { skillGapsForActiveHost, detectCodexHomeGap, codexConcernIsActive, skillGapToolLabel } from './agent-gates.mjs';
|
|
15
15
|
import { agentHomeConcernIsActive } from './agent-homes.mjs';
|
|
16
16
|
import { layerGuidanceLine } from './layer-description.mjs';
|
|
17
|
+
import { printSharedImportsSliceBridgeList } from './violations.mjs';
|
|
17
18
|
import {
|
|
18
19
|
citedGreen,
|
|
19
20
|
ciMergeGreenCites,
|
|
@@ -187,6 +188,7 @@ export function printDoctorCompactHuman(view) {
|
|
|
187
188
|
}
|
|
188
189
|
}
|
|
189
190
|
|
|
191
|
+
printSharedImportsSliceBridgeList(view.warnings);
|
|
190
192
|
console.log('');
|
|
191
193
|
console.log(color.dim('More: --doctor --all'));
|
|
192
194
|
}
|
|
@@ -298,15 +300,15 @@ export function printDoctorDetailsHuman(view) {
|
|
|
298
300
|
line(' ', color.dim(`evidence: ${smell.evidence.slice(0, 4).join(', ')}`));
|
|
299
301
|
}
|
|
300
302
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
)
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
303
|
+
}
|
|
304
|
+
if (pilotLoop?.active && pilotLoop.nextPilot) {
|
|
305
|
+
const np = pilotLoop.nextPilot;
|
|
306
|
+
line(
|
|
307
|
+
warn,
|
|
308
|
+
`Next pilot (one at a time): ${np.pilotTarget || np.pilot} [${np.smellId}] → re-doctor after change`
|
|
309
|
+
);
|
|
310
|
+
line(' ', color.dim(`success: ${np.successSignal}`));
|
|
311
|
+
line(' ', color.dim('never multi-pilot batch; pattern bets are never auto-applied'));
|
|
310
312
|
}
|
|
311
313
|
|
|
312
314
|
if (options.designDelta) {
|
package/bin/lib/doctor-plan.mjs
CHANGED
|
@@ -66,7 +66,7 @@ import {
|
|
|
66
66
|
loadGoldenPattern,
|
|
67
67
|
summarizeGoldenPattern,
|
|
68
68
|
} from './golden-pattern.mjs';
|
|
69
|
-
import { summarizePilotLoop } from './pilot-loop.mjs';
|
|
69
|
+
import { collectPilotCandidates, summarizePilotLoop } from './pilot-loop.mjs';
|
|
70
70
|
import { computeDoctorAdvisories } from './doctor-advisories.mjs';
|
|
71
71
|
import { ANALYSIS_COMPLETENESS, analysisIncompleteStatement, normalizeAnalysisCompleteness } from './analysis-completeness.mjs';
|
|
72
72
|
import { buildDoctorImprovementCompass } from './improvement-compass-doctor.mjs';
|
|
@@ -325,12 +325,8 @@ export function buildRemediationPlan(
|
|
|
325
325
|
governedPercent,
|
|
326
326
|
totalFiles,
|
|
327
327
|
});
|
|
328
|
-
// Q04 —
|
|
329
|
-
const pilotLoop = summarizePilotLoop({
|
|
330
|
-
designWeak,
|
|
331
|
-
patternBets,
|
|
332
|
-
designSmells,
|
|
333
|
-
});
|
|
328
|
+
// Q04 — one candidate at a time. Plan has no cohesion advisory.
|
|
329
|
+
const pilotLoop = summarizePilotLoop(collectPilotCandidates({ designWeak, patternBets, designSmells }));
|
|
334
330
|
const coverageHonesty = buildCoverageHonesty({
|
|
335
331
|
percent: governedPercent,
|
|
336
332
|
totalFiles,
|
|
@@ -604,13 +600,6 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
|
|
|
604
600
|
const goldenPattern = summarizeGoldenPattern(goldenLoad);
|
|
605
601
|
// Y06 — pure-layer opt-in when golden names pure modules but no pure:true layer.
|
|
606
602
|
const pureLayerOptIn = computePureLayerOptInNudge(config, goldenLoad);
|
|
607
|
-
// Q04 — one next pilot (extraction card) when design-weak.
|
|
608
|
-
const patternBetsForLoop = buildPatternBetsFromSmells(designSmells);
|
|
609
|
-
const pilotLoop = summarizePilotLoop({
|
|
610
|
-
designWeak: designFitness.designWeak,
|
|
611
|
-
patternBets: patternBetsForLoop,
|
|
612
|
-
designSmells,
|
|
613
|
-
});
|
|
614
603
|
const activeViolations = baseline.exists
|
|
615
604
|
? violations.filter((_, index) => !baseline.keys.has(occurrenceKeys[index]))
|
|
616
605
|
: violations;
|
|
@@ -625,6 +614,17 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
|
|
|
625
614
|
options.facts ?? options.architectureFacts,
|
|
626
615
|
activeViolations
|
|
627
616
|
);
|
|
617
|
+
// After physical cohesion so a reshape pilot is a candidate (#309).
|
|
618
|
+
const pilotLoop = summarizePilotLoop(
|
|
619
|
+
collectPilotCandidates(
|
|
620
|
+
{
|
|
621
|
+
...designFitness,
|
|
622
|
+
patternBets: buildPatternBetsFromSmells(designSmells),
|
|
623
|
+
designSmells,
|
|
624
|
+
},
|
|
625
|
+
doctorAdvisories
|
|
626
|
+
)
|
|
627
|
+
);
|
|
628
628
|
const layerOwners = collectLayerOwnerResidual(config);
|
|
629
629
|
const rulesUnderContract = doctorAdvisories.rulesUnderContract;
|
|
630
630
|
const arkRun = doctorAdvisories.arkRun;
|
|
@@ -965,6 +965,7 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
|
|
|
965
965
|
activeCount,
|
|
966
966
|
skillGaps,
|
|
967
967
|
agentHomeGaps,
|
|
968
|
+
warnings: options.warnings,
|
|
968
969
|
baseline,
|
|
969
970
|
baselineHonesty,
|
|
970
971
|
staleBaseline,
|
|
@@ -27,6 +27,9 @@ export function upgradeUsage() {
|
|
|
27
27
|
arkgate upgrade --apply apply the previewed bytes (needs --plan-digest when applying managed files)
|
|
28
28
|
|
|
29
29
|
Customized files stay unless you pass --accept-conflicts or --refresh-skills.
|
|
30
|
+
A gitignored missing file (absent-local) is recreated without that flag.
|
|
31
|
+
Exit: 0 applied and green; 3 applied but red; 2 blocked (consent); 1 error.
|
|
32
|
+
Scripts comparing against 1 must switch to non-zero.
|
|
30
33
|
Then: arkgate-check --doctor
|
|
31
34
|
|
|
32
35
|
Every flag: arkgate --help --all
|
|
@@ -77,8 +80,11 @@ Options:
|
|
|
77
80
|
--no-install Skip adding/installing arkgate as a project devDependency (start/upgrade).
|
|
78
81
|
--apply Apply a start plan; for upgrade, update/repreview or apply managed bytes.
|
|
79
82
|
--accept-conflicts
|
|
80
|
-
Allow upgrade to recreate deleted managed
|
|
83
|
+
Allow upgrade to recreate a deleted managed asset that is not gitignored, or replace a recorded conflict.
|
|
84
|
+
A missing gitignored file (absent-local, for example per-machine .mcp.json) is recreated without this flag.
|
|
81
85
|
--plan-digest Digest emitted by an upgrade preview; required to apply managed bytes.
|
|
86
|
+
upgrade --apply exits 0 when applied and the architecture check is green, 3 when applied but red,
|
|
87
|
+
2 when blocked for consent, and 1 on error. Scripts comparing against 1 must switch to non-zero.
|
|
82
88
|
--json Emit the start/upgrade/status/agents-md preview as deterministic machine-readable JSON.
|
|
83
89
|
--write For agents-md: merge the version-matched projection into AGENTS.md.
|
|
84
90
|
--check For agents-md: exit 1 when projection stamp drifts from package version.
|
|
@@ -88,6 +88,13 @@ function ackLifecycleHtml(lc) {
|
|
|
88
88
|
return rows.join('\n');
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
function sliceIdentityHtml(section) {
|
|
92
|
+
const collisions = Array.isArray(section?.collisions) ? section.collisions : [];
|
|
93
|
+
if (collisions.length === 0) return '';
|
|
94
|
+
const items = collisions.map((collision) => `<li>${esc(collision.message ?? '')}</li>`).join('');
|
|
95
|
+
return `<section class="section card" data-advisory="sliceIdentity"><h2>Slice identity <span class="muted">(advisory — not a verdict, not a score)</span></h2><ul>${items}</ul><p class="muted">Two prefixes bind as one slice id under <code>sliceIdentity: "stars"</code>. That is the point when the trees are one feature, and a mistake when they are not.</p></section>`;
|
|
96
|
+
}
|
|
97
|
+
|
|
91
98
|
function contractHealthHtml(health) {
|
|
92
99
|
if (!health) return '';
|
|
93
100
|
const smells = Array.isArray(health.smells) ? health.smells : [];
|
|
@@ -347,6 +354,7 @@ export function renderAdvisorySections(advisories, escape) {
|
|
|
347
354
|
improvementCompassHtml(advisories.improvementCompass),
|
|
348
355
|
deepModuleCoachHtml(advisories.deepModuleCoach),
|
|
349
356
|
stewardNudgeHtml(advisories.stewardNudge),
|
|
357
|
+
sliceIdentityHtml(advisories.sliceIdentity),
|
|
350
358
|
contractHealthHtml(advisories.contractHealth),
|
|
351
359
|
ambientStateHtml(advisories.ambientState),
|
|
352
360
|
physicalCohesionHtml(advisories.physicalCohesion),
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
summarizeDesignFitness,
|
|
9
9
|
buildPatternBetsFromSmells,
|
|
10
10
|
} from './design-smells.mjs';
|
|
11
|
-
import { summarizePilotLoop } from './pilot-loop.mjs';
|
|
11
|
+
import { collectPilotCandidates, summarizePilotLoop } from './pilot-loop.mjs';
|
|
12
12
|
import { buildPostGreenNextAction } from './post-green-path.mjs';
|
|
13
13
|
import { loadGoldenPattern, summarizeGoldenPattern } from './golden-pattern.mjs';
|
|
14
14
|
import { collectAdoptionGaps } from './mcp-adoption.mjs';
|
|
@@ -76,11 +76,14 @@ export function buildReportDepthPayload(
|
|
|
76
76
|
});
|
|
77
77
|
const postGreenPath = buildPostGreenNextAction(designFitness);
|
|
78
78
|
const patternBets = buildPatternBetsFromSmells(designSmells);
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
79
|
+
const physicalCohesion = computePhysicalCohesion(root, files);
|
|
80
|
+
physicalCohesion.reshapeDecisions = computeReshapeDecisionMemory(root, files).summary;
|
|
81
|
+
const pilotLoop = summarizePilotLoop(
|
|
82
|
+
collectPilotCandidates(
|
|
83
|
+
{ ...designFitness, patternBets, designSmells },
|
|
84
|
+
{ physicalCohesion }
|
|
85
|
+
)
|
|
86
|
+
);
|
|
84
87
|
const goldenPattern = summarizeGoldenPattern(loadGoldenPattern(root));
|
|
85
88
|
const adoption = collectAdoptionGaps(root, config, coverage);
|
|
86
89
|
const baseline = readBaseline(root, '.ark-baseline.json');
|
|
@@ -155,8 +158,6 @@ export function buildReportDepthPayload(
|
|
|
155
158
|
: packageVersionTruth?.code === 'PACKAGE_PIN_ABSENT'
|
|
156
159
|
? 'Add arkgate to package.json and install so CI/npx resolve this CLI (PACKAGE_PIN_ABSENT)'
|
|
157
160
|
: null;
|
|
158
|
-
const physicalCohesion = computePhysicalCohesion(root, files);
|
|
159
|
-
physicalCohesion.reshapeDecisions = computeReshapeDecisionMemory(root, files).summary;
|
|
160
161
|
const productHonesty = buildProductHonesty({
|
|
161
162
|
coverageHonesty,
|
|
162
163
|
baselineHonesty,
|