mandrel 2.31.0 → 2.32.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/.agents/README.md +13 -17
- package/.agents/agents/acceptance-critic.md +1 -2
- package/.agents/docs/SDLC.md +4 -4
- package/.agents/docs/agentrc-reference.json +61 -57
- package/.agents/docs/configuration.md +274 -227
- package/.agents/docs/execution-reference.md +13 -14
- package/.agents/docs/quality-gates.md +195 -23
- package/.agents/instructions.md +2 -5
- package/.agents/rules/git-conventions-reference.md +27 -27
- package/.agents/rules/git-conventions.md +4 -2
- package/.agents/rules/known-tooling-behavior.md +66 -30
- package/.agents/rules/testing-standards.md +35 -71
- package/.agents/runtime-deps.json +0 -1
- package/.agents/schemas/agentrc.schema.json +1939 -1400
- package/.agents/schemas/lifecycle/README.md +21 -14
- package/.agents/schemas/lifecycle/ledger-record.schema.json +76 -22
- package/.agents/schemas/story-deliver-terminal.schema.json +2 -2
- package/.agents/scripts/README.md +7 -29
- package/.agents/scripts/apply-quality-bootstrap.js +27 -34
- package/.agents/scripts/bootstrap.js +28 -26
- package/.agents/scripts/check-baseline-drift.js +73 -13
- package/.agents/scripts/check-baseline-scope.js +362 -0
- package/.agents/scripts/check-dead-exports.js +9 -1
- package/.agents/scripts/check-gherkin-corpus.js +508 -0
- package/.agents/scripts/check-knip-entries.js +136 -0
- package/.agents/scripts/check-lifecycle-lint.js +36 -112
- package/.agents/scripts/check-schema-references.js +1 -1
- package/.agents/scripts/diagnose-friction.js +7 -4
- package/.agents/scripts/generate-config-docs.js +263 -171
- package/.agents/scripts/install-matrix-assert.js +0 -1
- package/.agents/scripts/lib/ITicketingProvider.js +0 -58
- package/.agents/scripts/lib/audit-baselines/staleness.js +6 -6
- package/.agents/scripts/lib/audit-baselines/trend.js +7 -8
- package/.agents/scripts/lib/audit-baselines/weights.js +4 -5
- package/.agents/scripts/lib/audit-suite/checklist-threading.js +1 -1
- package/.agents/scripts/lib/audit-to-stories/build-story-body.js +0 -1
- package/.agents/scripts/lib/baselines/envelope.js +41 -60
- package/.agents/scripts/lib/baselines/git-base.js +30 -37
- package/.agents/scripts/lib/baselines/kinds/_crap-new-method-gate.js +103 -0
- package/.agents/scripts/lib/baselines/kinds/_crap-read.js +150 -0
- package/.agents/scripts/lib/baselines/kinds/crap.js +25 -65
- package/.agents/scripts/lib/baselines/orphan-pruner.js +233 -0
- package/.agents/scripts/lib/baselines/refresh-service.js +6 -8
- package/.agents/scripts/lib/baselines/scope-assert.js +223 -0
- package/.agents/scripts/lib/baselines/scope-inventory.js +314 -0
- package/.agents/scripts/lib/bdd-step-index.js +326 -0
- package/.agents/scripts/lib/bootstrap/install-ledger.js +5 -3
- package/.agents/scripts/lib/bootstrap/issue-forms-template.js +4 -6
- package/.agents/scripts/lib/bootstrap/manifest.js +17 -40
- package/.agents/scripts/lib/bootstrap/project-bootstrap.js +12 -59
- package/.agents/scripts/lib/bootstrap/quality-bootstrap.js +62 -2
- package/.agents/scripts/lib/checks/loop-health.js +9 -37
- package/.agents/scripts/lib/child-exec.js +193 -0
- package/.agents/scripts/lib/cli/standard-args.js +1 -1
- package/.agents/scripts/lib/cli-args.js +64 -0
- package/.agents/scripts/lib/close-validation/gates.js +2 -2
- package/.agents/scripts/lib/close-validation/runner.js +3 -3
- package/.agents/scripts/lib/config/acceptance-eval.js +5 -52
- package/.agents/scripts/lib/config/commands.js +3 -5
- package/.agents/scripts/lib/config/explain.js +5 -7
- package/.agents/scripts/lib/config/gates/bundle-size.schema.js +32 -6
- package/.agents/scripts/lib/config/gates/coverage.schema.js +25 -5
- package/.agents/scripts/lib/config/gates/crap-incremental-coverage.schema.js +12 -2
- package/.agents/scripts/lib/config/gates/crap.schema.js +68 -23
- package/.agents/scripts/lib/config/gates/duplication.schema.js +29 -17
- package/.agents/scripts/lib/config/gates/index.js +5 -2
- package/.agents/scripts/lib/config/gates/lighthouse.schema.js +34 -6
- package/.agents/scripts/lib/config/gates/lint.schema.js +11 -2
- package/.agents/scripts/lib/config/gates/maintainability.schema.js +37 -15
- package/.agents/scripts/lib/config/gates/mutation.schema.js +15 -3
- package/.agents/scripts/lib/config/gates/shared.js +58 -9
- package/.agents/scripts/lib/config/github.js +0 -1
- package/.agents/scripts/lib/config/limits.js +3 -48
- package/.agents/scripts/lib/config/qa.js +105 -0
- package/.agents/scripts/lib/config/temp-paths.js +6 -5
- package/.agents/scripts/lib/config-settings-schema-delivery.js +237 -56
- package/.agents/scripts/lib/config-settings-schema-quality.js +209 -29
- package/.agents/scripts/lib/config-settings-schema.js +386 -39
- package/.agents/scripts/lib/crap-baseline-join.js +126 -9
- package/.agents/scripts/lib/crap-utils.js +84 -520
- package/.agents/scripts/lib/dead-exports-knip.js +79 -10
- package/.agents/scripts/lib/degraded-mode.js +2 -2
- package/.agents/scripts/lib/doc-tiers.js +3 -3
- package/.agents/scripts/lib/feedback-loop/graduator-core.js +46 -104
- package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +10 -8
- package/.agents/scripts/lib/fs-walk.js +52 -0
- package/.agents/scripts/lib/git-branch-lifecycle.js +2 -2
- package/.agents/scripts/lib/git-utils.js +16 -36
- package/.agents/scripts/lib/knip-entry-sync.js +469 -0
- package/.agents/scripts/lib/observability/metrics-ledger.js +1 -1
- package/.agents/scripts/lib/observability/runtime-friction.js +10 -0
- package/.agents/scripts/lib/observability/signal-validator.js +5 -85
- package/.agents/scripts/lib/observability/signals-writer.js +19 -62
- package/.agents/scripts/lib/observability/source-classifier.js +5 -7
- package/.agents/scripts/lib/observability/terse-result.js +3 -3
- package/.agents/scripts/lib/orchestration/behind-recovery.js +114 -0
- package/.agents/scripts/lib/orchestration/ceremony-routing.js +7 -8
- package/.agents/scripts/lib/orchestration/ci-rerun-guard.js +34 -33
- package/.agents/scripts/lib/orchestration/code-review.js +2 -2
- package/.agents/scripts/lib/orchestration/complexity-gate.js +43 -161
- package/.agents/scripts/lib/orchestration/diff-magnitude.js +4 -4
- package/.agents/scripts/lib/orchestration/label-transitions.js +3 -2
- package/.agents/scripts/lib/orchestration/lease-guard-shared.js +12 -38
- package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-unlanded.js +5 -6
- package/.agents/scripts/lib/orchestration/plan-metrics.js +2 -3
- package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +6 -0
- package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +0 -1
- package/.agents/scripts/lib/orchestration/{lifecycle/listeners/watcher.js → pr-watch.js} +58 -208
- package/.agents/scripts/lib/orchestration/resolve-stories.js +5 -15
- package/.agents/scripts/lib/orchestration/review-providers/codex.js +1 -1
- package/.agents/scripts/lib/orchestration/review-providers/mi-exemptions.js +130 -0
- package/.agents/scripts/lib/orchestration/review-providers/native.js +30 -16
- package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +1 -1
- package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +37 -26
- package/.agents/scripts/lib/orchestration/single-story-close/phases/conventional-subject.js +376 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/normalize-pr-title.js +161 -151
- package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +15 -3
- package/.agents/scripts/lib/orchestration/single-story-close/phases/pull-request.js +10 -15
- package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +5 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/review-override.js +157 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/worktree-reap.js +0 -14
- package/.agents/scripts/lib/orchestration/single-story-close/runner.js +59 -25
- package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +20 -31
- package/.agents/scripts/lib/orchestration/spec-spill.js +17 -3
- package/.agents/scripts/lib/orchestration/story-close/phases/review-core.js +7 -6
- package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +2 -1
- package/.agents/scripts/lib/orchestration/task-body-validator.js +4 -1
- package/.agents/scripts/lib/orchestration/ticket-lease.js +28 -127
- package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +1 -1
- package/.agents/scripts/lib/orchestration/ticketing/reads.js +5 -5
- package/.agents/scripts/lib/orchestration/ticketing/transition.js +5 -4
- package/.agents/scripts/lib/orchestration/verify-tier-repair.js +107 -0
- package/.agents/scripts/lib/qa/coverage-verdict.js +5 -87
- package/.agents/scripts/lib/signals/detectors/common.js +1 -1
- package/.agents/scripts/lib/signals/index.js +8 -6
- package/.agents/scripts/lib/signals/schema.js +20 -25
- package/.agents/scripts/lib/signals/write.js +8 -8
- package/.agents/scripts/lib/story-body/story-body.js +12 -59
- package/.agents/scripts/lib/temp-retention.js +1 -1
- package/.agents/scripts/lib/templates/decomposer-prompts.js +16 -14
- package/.agents/scripts/lib/ticket-body-sections.js +4 -5
- package/.agents/scripts/lib/worktree/lifecycle/merge-reachability.js +13 -45
- package/.agents/scripts/lib/worktree/lifecycle/reap.js +4 -5
- package/.agents/scripts/lib/worktree-manager.js +2 -3
- package/.agents/scripts/lint-label-vocabulary.js +2 -24
- package/.agents/scripts/pr-watch-with-update.js +7 -5
- package/.agents/scripts/providers/github/cache.js +2 -2
- package/.agents/scripts/providers/github/comments.js +6 -28
- package/.agents/scripts/providers/github/compose.js +0 -15
- package/.agents/scripts/providers/github/errors.js +10 -27
- package/.agents/scripts/providers/github/request-helpers.js +1 -2
- package/.agents/scripts/providers/github/sub-issues.js +10 -218
- package/.agents/scripts/providers/github.js +4 -7
- package/.agents/scripts/prune-baseline-orphans.js +181 -0
- package/.agents/scripts/resolve-stories.js +0 -2
- package/.agents/scripts/run-lint.js +61 -61
- package/.agents/scripts/run-test-profile.js +6 -6
- package/.agents/scripts/run-verify.js +48 -30
- package/.agents/scripts/single-story-close.js +20 -0
- package/.agents/scripts/single-story-init.js +12 -35
- package/.agents/scripts/update-dead-exports-baseline.js +321 -0
- package/.agents/skills/core/gates-and-baselines/SKILL.md +2 -2
- package/.agents/skills/skills.index.json +1 -11
- package/.agents/workflows/audit-documentation.md +5 -6
- package/.agents/workflows/audit-to-stories.md +2 -2
- package/.agents/workflows/helpers/audit-lens-core.md +11 -12
- package/.agents/workflows/helpers/code-quality-guardrails.md +15 -14
- package/.agents/workflows/helpers/code-review.md +3 -8
- package/.agents/workflows/helpers/deliver-reference.md +2 -1
- package/.agents/workflows/helpers/deliver-story-reference.md +27 -16
- package/.agents/workflows/helpers/worktree-lifecycle.md +1 -2
- package/.agents/workflows/mandrel-update.md +10 -10
- package/.agents/workflows/qa-assist.md +15 -20
- package/.agents/workflows/qa-explore.md +9 -8
- package/README.md +1 -1
- package/docs/CHANGELOG.md +42 -0
- package/lib/migrations/index.js +2 -0
- package/lib/migrations/steps/2.32.0-retire-lint-baseline-command.js +127 -0
- package/package.json +12 -3
- package/.agents/schemas/lifecycle/checkpoint.written.schema.json +0 -13
- package/.agents/schemas/lifecycle/close-validate.end.schema.json +0 -18
- package/.agents/schemas/lifecycle/close-validate.start.schema.json +0 -13
- package/.agents/schemas/lifecycle/code-review.end.schema.json +0 -30
- package/.agents/schemas/lifecycle/code-review.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/intervention.recorded.schema.json +0 -15
- package/.agents/schemas/lifecycle/loop.tick.schema.json +0 -20
- package/.agents/schemas/lifecycle/notification.emitted.schema.json +0 -18
- package/.agents/schemas/lifecycle/pr.created.schema.json +0 -14
- package/.agents/schemas/lifecycle/retro.end.schema.json +0 -16
- package/.agents/schemas/lifecycle/retro.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/story.blocked.schema.json +0 -13
- package/.agents/schemas/lifecycle/story.dispatch.end.schema.json +0 -17
- package/.agents/schemas/lifecycle/story.dispatch.start.schema.json +0 -15
- package/.agents/schemas/lifecycle/story.merged.schema.json +0 -13
- package/.agents/scripts/check-gherkin-placeholders.js +0 -663
- package/.agents/scripts/check-lifecycle-doc-drift.js +0 -411
- package/.agents/scripts/lib/audit-suite/cli.js +0 -64
- package/.agents/scripts/lib/bootstrap/baselines-layout-migration.js +0 -202
- package/.agents/scripts/lib/bootstrap/ci-workflow-template.js +0 -212
- package/.agents/scripts/lib/checks/baseline-drift-main-checkout.js +0 -104
- package/.agents/scripts/lib/checks/push-hook-parity.js +0 -106
- package/.agents/scripts/lib/checks/windows-coverage-noise-floor.js +0 -92
- package/.agents/scripts/lib/checks/worktree-bootstrap-env.js +0 -81
- package/.agents/scripts/lib/checks/worktree-residue-biome.js +0 -55
- package/.agents/scripts/lib/crap-baseline-index.js +0 -46
- package/.agents/scripts/lib/crap-utils-incremental.js +0 -113
- package/.agents/scripts/lib/dynamic-workflow/capability.js +0 -396
- package/.agents/scripts/lib/feedback-loop/audit-results-graduator.js +0 -335
- package/.agents/scripts/lib/mutation/baseline-snapshot.js +0 -239
- package/.agents/scripts/lib/mutation/config-detector.js +0 -119
- package/.agents/scripts/lib/mutation/stryker-runner.js +0 -306
- package/.agents/scripts/lib/mutation/survivor-report.js +0 -160
- package/.agents/scripts/lib/observability/active-story-env.js +0 -170
- package/.agents/scripts/lib/observability/tool-trace-hook.js +0 -456
- package/.agents/scripts/lib/orchestration/acceptance-clusters.js +0 -111
- package/.agents/scripts/lib/orchestration/context-envelope.js +0 -277
- package/.agents/scripts/lib/orchestration/detectors-phase.js +0 -194
- package/.agents/scripts/lib/orchestration/lifecycle/bus.js +0 -309
- package/.agents/scripts/lib/orchestration/lifecycle/emit-loop-tick.js +0 -181
- package/.agents/scripts/lib/orchestration/lifecycle/ledger-writer.js +0 -229
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +0 -54
- package/.agents/scripts/lib/orchestration/lifecycle/trace-logger.js +0 -344
- package/.agents/scripts/lib/orchestration/lint-baseline-service.js +0 -114
- package/.agents/scripts/lib/orchestration/pr-base-guard.js +0 -37
- package/.agents/scripts/lib/orchestration/resolves-token.js +0 -127
- package/.agents/scripts/lib/orchestration/spec-section-validator.js +0 -130
- package/.agents/scripts/lib/orchestration/story-close/emit-blocked.js +0 -55
- package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +0 -211
- package/.agents/scripts/lib/planning-corpus.js +0 -37
- package/.agents/scripts/lib/qa/coverage-report.js +0 -181
- package/.agents/scripts/lib/qa/propose-missing-test.js +0 -95
- package/.agents/scripts/lib/qa/qa-context-hydrator.js +0 -217
- package/.agents/scripts/lib/signals/detectors/index.js +0 -14
- package/.agents/scripts/lib/signals/detectors/retry.js +0 -253
- package/.agents/scripts/lib/signals/detectors/rework.js +0 -167
- package/.agents/scripts/lib/signals/read.js +0 -268
- package/.agents/scripts/lib/signals/span-tree.js +0 -291
- package/.agents/scripts/lib/story-lifecycle.js +0 -194
- package/.agents/scripts/lib/story-plan.js +0 -379
- package/.agents/scripts/lib/util/phase-timer-state.js +0 -72
- package/.agents/scripts/lib/util/phase-timer.js +0 -163
- package/.agents/scripts/lib/workers/combined-mi-crap-worker.js +0 -169
- package/.agents/scripts/lint-baseline.js +0 -507
- package/.agents/scripts/providers/github/prs.js +0 -103
- package/.agents/scripts/signals-view.js +0 -309
- package/.agents/scripts/story-plan.js +0 -370
- package/.agents/scripts/sync-branch-from-base.js +0 -149
- package/.agents/scripts/validate-docs-freshness.js +0 -314
- package/.agents/skills/core/diagnose-friction/SKILL.md +0 -78
- package/.agents/workflows/helpers/signals.md +0 -112
|
@@ -112,17 +112,6 @@ export class ITicketingProvider {
|
|
|
112
112
|
throw new Error('Not implemented: getTicketDependencies');
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
/**
|
|
116
|
-
* Fetch recent comments across the repository.
|
|
117
|
-
* Useful for auditing and visualization of agent telemetry.
|
|
118
|
-
*
|
|
119
|
-
* @param {number} [limit=100]
|
|
120
|
-
* @returns {Promise<object[]>}
|
|
121
|
-
*/
|
|
122
|
-
async getRecentComments(_limit = 100) {
|
|
123
|
-
throw new Error('Not implemented: getRecentComments');
|
|
124
|
-
}
|
|
125
|
-
|
|
126
115
|
/**
|
|
127
116
|
* Fetch all comments for a specific ticket.
|
|
128
117
|
*
|
|
@@ -137,42 +126,6 @@ export class ITicketingProvider {
|
|
|
137
126
|
// Write Operations
|
|
138
127
|
// ---------------------------------------------------------------------------
|
|
139
128
|
|
|
140
|
-
/**
|
|
141
|
-
* Create a child ticket within an Epic's structural hierarchy.
|
|
142
|
-
*
|
|
143
|
-
* @param {number} parentId - GitHub Issue number of the immediate structural parent (e.g. Epic or Story).
|
|
144
|
-
* @param {{
|
|
145
|
-
* epicId: number,
|
|
146
|
-
* title: string,
|
|
147
|
-
* body: string,
|
|
148
|
-
* labels: string[],
|
|
149
|
-
* dependencies: number[]
|
|
150
|
-
* }} ticketData - The ticket content and metadata.
|
|
151
|
-
* @returns {Promise<{ id: number, url: string }>}
|
|
152
|
-
*/
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Link an existing issue as a sub-issue of a parent.
|
|
156
|
-
*
|
|
157
|
-
* @param {number} parentId - GitHub Issue number of the parent.
|
|
158
|
-
* @param {number} childId - GitHub internal database ID of the sub-issue.
|
|
159
|
-
* @returns {Promise<void>}
|
|
160
|
-
*/
|
|
161
|
-
async addSubIssue(_parentId, _childId) {
|
|
162
|
-
throw new Error('Not implemented: addSubIssue');
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* Remove a sub-issue link from a parent.
|
|
167
|
-
*
|
|
168
|
-
* @param {number} parentId - GitHub Issue number of the parent.
|
|
169
|
-
* @param {number} childId - GitHub internal database ID of the sub-issue.
|
|
170
|
-
* @returns {Promise<void>}
|
|
171
|
-
*/
|
|
172
|
-
async removeSubIssue(_parentId, _childId) {
|
|
173
|
-
throw new Error('Not implemented: removeSubIssue');
|
|
174
|
-
}
|
|
175
|
-
|
|
176
129
|
/**
|
|
177
130
|
* Mutate labels, body (tasklist checkboxes), and assignees on a ticket.
|
|
178
131
|
*
|
|
@@ -213,17 +166,6 @@ export class ITicketingProvider {
|
|
|
213
166
|
throw new Error('Not implemented: deleteComment');
|
|
214
167
|
}
|
|
215
168
|
|
|
216
|
-
/**
|
|
217
|
-
* Open a Pull Request linking the specified ticket.
|
|
218
|
-
*
|
|
219
|
-
* @param {string} branchName - The source branch for the PR.
|
|
220
|
-
* @param {number} ticketId - GitHub Issue number to link.
|
|
221
|
-
* @returns {Promise<{ number: number, url: string, htmlUrl: string }>}
|
|
222
|
-
*/
|
|
223
|
-
async createPullRequest(_branchName, _ticketId) {
|
|
224
|
-
throw new Error('Not implemented: createPullRequest');
|
|
225
|
-
}
|
|
226
|
-
|
|
227
169
|
// ---------------------------------------------------------------------------
|
|
228
170
|
// Setup Operations (used by bootstrap)
|
|
229
171
|
// ---------------------------------------------------------------------------
|
|
@@ -16,25 +16,25 @@
|
|
|
16
16
|
* @module lib/audit-baselines/staleness
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import {
|
|
19
|
+
import { execFileCapture } from '../child-exec.js';
|
|
20
20
|
import { KIND_SPECS } from './kinds.js';
|
|
21
21
|
import { ageInDays } from './read.js';
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* Run a git command under `cwd`, returning trimmed stdout, or `null` on any
|
|
25
|
-
* failure or empty result.
|
|
25
|
+
* failure or empty result. Buffer and shell policy come from the shared
|
|
26
|
+
* child-process surface ([`child-exec.js`](../child-exec.js)).
|
|
26
27
|
*
|
|
27
28
|
* @param {string[]} args
|
|
28
29
|
* @param {{ cwd: string, run?: Function }} io
|
|
29
30
|
* @returns {string | null}
|
|
30
31
|
*/
|
|
31
|
-
function git(args, { cwd, run
|
|
32
|
+
function git(args, { cwd, run }) {
|
|
32
33
|
try {
|
|
33
34
|
const stdout = String(
|
|
34
|
-
|
|
35
|
+
execFileCapture('git', args, {
|
|
36
|
+
run,
|
|
35
37
|
cwd,
|
|
36
|
-
encoding: 'utf8',
|
|
37
|
-
maxBuffer: 16 * 1024 * 1024,
|
|
38
38
|
stdio: ['ignore', 'pipe', 'ignore'],
|
|
39
39
|
}),
|
|
40
40
|
).trim();
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* @module lib/audit-baselines/trend
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import {
|
|
19
|
+
import { execFileCapture } from '../child-exec.js';
|
|
20
20
|
import { trendRollupOf } from './kinds.js';
|
|
21
21
|
|
|
22
22
|
/**
|
|
@@ -25,13 +25,13 @@ import { trendRollupOf } from './kinds.js';
|
|
|
25
25
|
* @param {{ cwd: string, relPath: string, limit: number, run?: Function }} args
|
|
26
26
|
* @returns {Array<{ sha: string, committedAt: string }>}
|
|
27
27
|
*/
|
|
28
|
-
function listCommits({ cwd, relPath, limit, run
|
|
28
|
+
function listCommits({ cwd, relPath, limit, run }) {
|
|
29
29
|
let stdout;
|
|
30
30
|
try {
|
|
31
|
-
stdout =
|
|
31
|
+
stdout = execFileCapture(
|
|
32
32
|
'git',
|
|
33
33
|
['log', `-n${limit}`, '--format=%H %cI', '--', relPath],
|
|
34
|
-
{
|
|
34
|
+
{ run, cwd, stdio: ['ignore', 'pipe', 'ignore'] },
|
|
35
35
|
);
|
|
36
36
|
} catch {
|
|
37
37
|
return [];
|
|
@@ -52,13 +52,12 @@ function listCommits({ cwd, relPath, limit, run = execFileSync }) {
|
|
|
52
52
|
* @param {{ cwd: string, kind: string, sha: string, relPath: string, run?: Function }} args
|
|
53
53
|
* @returns {object | null}
|
|
54
54
|
*/
|
|
55
|
-
function rollupAt({ cwd, kind, sha, relPath, run
|
|
55
|
+
function rollupAt({ cwd, kind, sha, relPath, run }) {
|
|
56
56
|
let stdout;
|
|
57
57
|
try {
|
|
58
|
-
stdout =
|
|
58
|
+
stdout = execFileCapture('git', ['show', `${sha}:${relPath}`], {
|
|
59
|
+
run,
|
|
59
60
|
cwd,
|
|
60
|
-
encoding: 'utf8',
|
|
61
|
-
maxBuffer: 64 * 1024 * 1024,
|
|
62
61
|
stdio: ['ignore', 'pipe', 'ignore'],
|
|
63
62
|
});
|
|
64
63
|
} catch {
|
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
* @module lib/audit-baselines/weights
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import { execFileSync } from 'node:child_process';
|
|
20
19
|
import fs from 'node:fs';
|
|
21
20
|
import path from 'node:path';
|
|
21
|
+
import { execFileCapture } from '../child-exec.js';
|
|
22
22
|
import { computeInDegree, resolveRepoGraph } from '../import-graph.js';
|
|
23
23
|
|
|
24
24
|
/** Neutral multiplier every degraded weight collapses to. */
|
|
@@ -47,10 +47,10 @@ function saturate(count, half) {
|
|
|
47
47
|
* `degraded` is true when git could not answer at all — not a git work
|
|
48
48
|
* tree, no commits yet, or the binary is unavailable.
|
|
49
49
|
*/
|
|
50
|
-
export function readChurn({ cwd, windowDays = 180, run
|
|
50
|
+
export function readChurn({ cwd, windowDays = 180, run }) {
|
|
51
51
|
let stdout;
|
|
52
52
|
try {
|
|
53
|
-
stdout =
|
|
53
|
+
stdout = execFileCapture(
|
|
54
54
|
'git',
|
|
55
55
|
[
|
|
56
56
|
'log',
|
|
@@ -60,9 +60,8 @@ export function readChurn({ cwd, windowDays = 180, run = execFileSync }) {
|
|
|
60
60
|
'--no-renames',
|
|
61
61
|
],
|
|
62
62
|
{
|
|
63
|
+
run,
|
|
63
64
|
cwd,
|
|
64
|
-
encoding: 'utf8',
|
|
65
|
-
maxBuffer: 64 * 1024 * 1024,
|
|
66
65
|
// git narrates "not a git repository" on stderr; the degradation is
|
|
67
66
|
// reported in the envelope, not shouted at the operator.
|
|
68
67
|
stdio: ['ignore', 'pipe', 'ignore'],
|
|
@@ -41,7 +41,7 @@ import path from 'node:path';
|
|
|
41
41
|
import { AUDIT_LENSES } from '../audit-to-stories/audit-lenses.js';
|
|
42
42
|
import { getPaths, PROJECT_ROOT, resolveConfig } from '../config-resolver.js';
|
|
43
43
|
import { Logger } from '../Logger.js';
|
|
44
|
-
import { estimateTokens } from '../orchestration/
|
|
44
|
+
import { estimateTokens } from '../orchestration/spec-spill.js';
|
|
45
45
|
import {
|
|
46
46
|
changeSetLacksSiblingTest,
|
|
47
47
|
matchesAnyFilePattern,
|
|
@@ -40,46 +40,26 @@
|
|
|
40
40
|
* @module lib/baselines/envelope
|
|
41
41
|
*/
|
|
42
42
|
|
|
43
|
-
import
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
import addFormats from 'ajv-formats';
|
|
48
|
-
|
|
49
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
43
|
+
import {
|
|
44
|
+
BASELINE_KIND_SCHEMA_FILES,
|
|
45
|
+
buildBaselineSchemaAjv,
|
|
46
|
+
} from '../baseline-schema-registry.js';
|
|
50
47
|
|
|
51
48
|
/**
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
49
|
+
* Canonical list of kinds the shared envelope supports, **derived** from the
|
|
50
|
+
* schema registry rather than restated (Story #5002).
|
|
51
|
+
*
|
|
52
|
+
* It used to be a hand-kept literal that happened to mirror
|
|
53
|
+
* `BASELINE_KIND_SCHEMA_FILES` name for name. Deriving it makes
|
|
54
|
+
* "`kind` is known" and "`kind`'s schema is registered" the same fact, so
|
|
55
|
+
* `getValidator` below cannot be handed a kind the AJV instance never
|
|
56
|
+
* compiled — the not-registered branch is gone because the state is
|
|
57
|
+
* unreachable, not because it was hidden.
|
|
56
58
|
*/
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
'..',
|
|
60
|
-
'..',
|
|
61
|
-
'..',
|
|
62
|
-
'schemas',
|
|
63
|
-
'baselines',
|
|
59
|
+
export const KNOWN_KINDS = Object.freeze(
|
|
60
|
+
BASELINE_KIND_SCHEMA_FILES.map((file) => file.replace(/\.schema\.json$/, '')),
|
|
64
61
|
);
|
|
65
62
|
|
|
66
|
-
/**
|
|
67
|
-
* Canonical list of kinds the shared envelope supports. The writer's
|
|
68
|
-
* per-kind module list lives in `kinds/index.js`; this constant is the
|
|
69
|
-
* envelope's view of the same set, kept here to break the import cycle
|
|
70
|
-
* (`kinds/<kind>` imports envelope; envelope only needs the names).
|
|
71
|
-
*/
|
|
72
|
-
export const KNOWN_KINDS = Object.freeze([
|
|
73
|
-
'lint',
|
|
74
|
-
'coverage',
|
|
75
|
-
'crap',
|
|
76
|
-
'maintainability',
|
|
77
|
-
'mutation',
|
|
78
|
-
'lighthouse',
|
|
79
|
-
'bundle-size',
|
|
80
|
-
'duplication',
|
|
81
|
-
]);
|
|
82
|
-
|
|
83
63
|
function schemaRefFor(kind) {
|
|
84
64
|
return `.agents/schemas/baselines/${kind}.schema.json`;
|
|
85
65
|
}
|
|
@@ -193,36 +173,37 @@ export function buildEnvelope({
|
|
|
193
173
|
}
|
|
194
174
|
|
|
195
175
|
/**
|
|
196
|
-
* Lazy AJV instance
|
|
197
|
-
*
|
|
176
|
+
* Lazy AJV instance, built by the canonical
|
|
177
|
+
* [`baseline-schema-registry`](../baseline-schema-registry.js) — the ONE
|
|
178
|
+
* place a baselines-directory schema is read off disk and compiled
|
|
179
|
+
* (Story #5002). This module used to keep its own `SCHEMAS_DIR` + AJV +
|
|
180
|
+
* per-kind compile cache alongside the registry's, so the writer and
|
|
181
|
+
* `baselines/reader.js` validated the same envelope through two independently
|
|
182
|
+
* configured instances; a registration added to one was invisible to the
|
|
183
|
+
* other. One builder means the writer and the reader cannot disagree about
|
|
184
|
+
* what a valid envelope is.
|
|
185
|
+
*
|
|
186
|
+
* Memoised: building it reads eleven schema files, and a single baseline
|
|
187
|
+
* regeneration calls `assertEnvelope` once per write plus once per re-read.
|
|
198
188
|
*/
|
|
199
189
|
let _ajv = null;
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
addFormats(ajv);
|
|
206
|
-
const envelopeSchema = JSON.parse(
|
|
207
|
-
fs.readFileSync(
|
|
208
|
-
path.join(SCHEMAS_DIR, 'baseline-envelope.schema.json'),
|
|
209
|
-
'utf8',
|
|
210
|
-
),
|
|
211
|
-
);
|
|
212
|
-
ajv.addSchema(envelopeSchema, 'baseline-envelope.schema.json');
|
|
213
|
-
_ajv = ajv;
|
|
214
|
-
return ajv;
|
|
190
|
+
function ajv() {
|
|
191
|
+
if (_ajv === null) {
|
|
192
|
+
_ajv = buildBaselineSchemaAjv();
|
|
193
|
+
}
|
|
194
|
+
return _ajv;
|
|
215
195
|
}
|
|
216
196
|
|
|
197
|
+
/**
|
|
198
|
+
* Look up the pre-registered validator for a per-kind schema. Total over
|
|
199
|
+
* `KNOWN_KINDS` — that list is derived from the registry's own filenames, so
|
|
200
|
+
* every kind reaching here has a compiled schema.
|
|
201
|
+
*
|
|
202
|
+
* @param {string} kind
|
|
203
|
+
* @returns {import('ajv').ValidateFunction}
|
|
204
|
+
*/
|
|
217
205
|
function getValidator(kind) {
|
|
218
|
-
|
|
219
|
-
const ajv = getAjv();
|
|
220
|
-
const schema = JSON.parse(
|
|
221
|
-
fs.readFileSync(path.join(SCHEMAS_DIR, `${kind}.schema.json`), 'utf8'),
|
|
222
|
-
);
|
|
223
|
-
const validate = ajv.compile(schema);
|
|
224
|
-
_validators.set(kind, validate);
|
|
225
|
-
return validate;
|
|
206
|
+
return ajv().getSchema(`${kind}.schema.json`);
|
|
226
207
|
}
|
|
227
208
|
|
|
228
209
|
/**
|
|
@@ -22,6 +22,13 @@
|
|
|
22
22
|
// `ref` and `file` are passed as separate argv tokens, never
|
|
23
23
|
// interpolated into a shell command.
|
|
24
24
|
//
|
|
25
|
+
// Story #5009 moved the spawn itself, the stdout ceiling and the
|
|
26
|
+
// failure-message shape out to `lib/child-exec.js` — the one child-process
|
|
27
|
+
// surface — so this module now owns only what is genuinely its own: the
|
|
28
|
+
// `(ref, file)` LRU, the exit-code semantics below, and the test seam. The
|
|
29
|
+
// `__setSpawnRunner` seam still injects a mock runner; it is threaded through
|
|
30
|
+
// the shared wrapper as `run` rather than calling `spawnSync` directly.
|
|
31
|
+
//
|
|
25
32
|
// Exit semantics are intentionally narrow:
|
|
26
33
|
//
|
|
27
34
|
// - Exit 0: stdout is the file contents, returned as a UTF-8 string.
|
|
@@ -39,7 +46,7 @@
|
|
|
39
46
|
// means "path absent at ref". Callers may therefore treat a throw as a
|
|
40
47
|
// hard failure without having to re-diagnose it.
|
|
41
48
|
|
|
42
|
-
import {
|
|
49
|
+
import { formatChildFailure, spawnChild } from '../child-exec.js';
|
|
43
50
|
|
|
44
51
|
// ---------------------------------------------------------------------------
|
|
45
52
|
// LRU cache. Small footprint — even a fan-out across all seven baseline
|
|
@@ -53,21 +60,13 @@ import { spawnSync } from 'node:child_process';
|
|
|
53
60
|
const DEFAULT_MAX_ENTRIES = 64;
|
|
54
61
|
|
|
55
62
|
/**
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* `
|
|
59
|
-
* baseline legitimately grows past that (a real consumer's crap baseline
|
|
60
|
-
* measured 1,178,910 bytes), at which point the child is killed —
|
|
61
|
-
* `status: null`, `signal: 'SIGTERM'`, `error.code: 'ENOBUFS'` — and the
|
|
62
|
-
* read fails for a reason that has nothing to do with the repository.
|
|
63
|
+
* Injected spawn runner, or `undefined` to use the shared surface's real one.
|
|
64
|
+
* Story #4914's explicit 64 MB ceiling still applies to both git reads below —
|
|
65
|
+
* it is now `child-exec.js`'s `MAX_BUFFER_BYTES`, applied by `spawnChild`.
|
|
63
66
|
*
|
|
64
|
-
*
|
|
65
|
-
* `run-test-profile.js:87`, `audit-baselines/trend.js:61` and
|
|
66
|
-
* `audit-baselines/weights.js:65`. This module was the outlier.
|
|
67
|
+
* @type {Function | undefined}
|
|
67
68
|
*/
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
let _spawnSync = spawnSync;
|
|
69
|
+
let _spawnRunner;
|
|
71
70
|
let _cache = new Map();
|
|
72
71
|
let _maxEntries = DEFAULT_MAX_ENTRIES;
|
|
73
72
|
|
|
@@ -80,12 +79,12 @@ let _maxEntries = DEFAULT_MAX_ENTRIES;
|
|
|
80
79
|
* the contract.
|
|
81
80
|
*
|
|
82
81
|
* @param {object} [opts]
|
|
83
|
-
* @param {
|
|
84
|
-
* @param {number} [opts.maxEntries]
|
|
82
|
+
* @param {Function} [opts.spawn] - Mock spawnSync.
|
|
83
|
+
* @param {number} [opts.maxEntries] - Override LRU capacity.
|
|
85
84
|
*/
|
|
86
85
|
export function __setSpawnRunner(opts = {}) {
|
|
87
86
|
if (opts.spawn) {
|
|
88
|
-
|
|
87
|
+
_spawnRunner = opts.spawn;
|
|
89
88
|
}
|
|
90
89
|
if (typeof opts.maxEntries === 'number') {
|
|
91
90
|
_maxEntries = opts.maxEntries;
|
|
@@ -98,7 +97,7 @@ export function __setSpawnRunner(opts = {}) {
|
|
|
98
97
|
* to keep state from leaking across files.
|
|
99
98
|
*/
|
|
100
99
|
export function __resetForTests() {
|
|
101
|
-
|
|
100
|
+
_spawnRunner = undefined;
|
|
102
101
|
_maxEntries = DEFAULT_MAX_ENTRIES;
|
|
103
102
|
_cache = new Map();
|
|
104
103
|
}
|
|
@@ -179,13 +178,10 @@ export function readBaseFromGit(ref, file, opts = {}) {
|
|
|
179
178
|
|
|
180
179
|
const cwd = opts.cwd ?? process.cwd();
|
|
181
180
|
const spec = `${ref}:${file}`;
|
|
182
|
-
const result =
|
|
181
|
+
const result = spawnChild('git', ['show', spec], {
|
|
182
|
+
run: _spawnRunner,
|
|
183
183
|
cwd,
|
|
184
|
-
stdio: 'pipe',
|
|
185
|
-
encoding: 'utf-8',
|
|
186
|
-
shell: false,
|
|
187
184
|
env: cleanGitEnv(),
|
|
188
|
-
maxBuffer: MAX_BUFFER_BYTES,
|
|
189
185
|
});
|
|
190
186
|
|
|
191
187
|
// `child_process.spawnSync` returns `status: null` when the child died
|
|
@@ -206,9 +202,12 @@ export function readBaseFromGit(ref, file, opts = {}) {
|
|
|
206
202
|
return null;
|
|
207
203
|
}
|
|
208
204
|
|
|
209
|
-
const stderr = (result.stderr || '').toString().trim();
|
|
210
205
|
throw new Error(
|
|
211
|
-
|
|
206
|
+
formatChildFailure({
|
|
207
|
+
label: `readBaseFromGit: git show ${spec}`,
|
|
208
|
+
status,
|
|
209
|
+
stderr: result.stderr,
|
|
210
|
+
}),
|
|
212
211
|
);
|
|
213
212
|
}
|
|
214
213
|
|
|
@@ -222,9 +221,10 @@ export function readBaseFromGit(ref, file, opts = {}) {
|
|
|
222
221
|
* Restricting the log to `-- <file>` means the returned subjects already
|
|
223
222
|
* satisfy the "commit whose diff touches the baseline file" half of the
|
|
224
223
|
* predicate; the caller only has to match the tag substring against each
|
|
225
|
-
* subject. Runs through the same `
|
|
226
|
-
* it inherits the identical env scrubbing
|
|
227
|
-
*
|
|
224
|
+
* subject. Runs through the same shared `spawnChild` surface and injected
|
|
225
|
+
* runner as `readBaseFromGit`, so it inherits the identical env scrubbing
|
|
226
|
+
* (drop inherited `GIT_*`), the same stdout ceiling, and the spawn-not-exec
|
|
227
|
+
* security posture (argv tokens, `shell: false`).
|
|
228
228
|
*
|
|
229
229
|
* Returns an empty array whenever the range cannot be walked (missing base
|
|
230
230
|
* ref, git failure, empty range) — the acknowledgment path treats "no
|
|
@@ -244,17 +244,10 @@ export function readRangeSubjectsTouchingFile(baseRef, file, opts = {}) {
|
|
|
244
244
|
const cwd = opts.cwd ?? process.cwd();
|
|
245
245
|
let result;
|
|
246
246
|
try {
|
|
247
|
-
result =
|
|
247
|
+
result = spawnChild(
|
|
248
248
|
'git',
|
|
249
249
|
['log', `${baseRef}..HEAD`, '--format=%s', '--', file],
|
|
250
|
-
{
|
|
251
|
-
cwd,
|
|
252
|
-
stdio: 'pipe',
|
|
253
|
-
encoding: 'utf-8',
|
|
254
|
-
shell: false,
|
|
255
|
-
env: cleanGitEnv(),
|
|
256
|
-
maxBuffer: MAX_BUFFER_BYTES,
|
|
257
|
-
},
|
|
250
|
+
{ run: _spawnRunner, cwd, env: cleanGitEnv() },
|
|
258
251
|
);
|
|
259
252
|
} catch {
|
|
260
253
|
return [];
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* _crap-new-method-gate.js — the policy `kinds/crap.js#compareCrap` applies to
|
|
3
|
+
* a method that has no baseline row (Story #5002).
|
|
4
|
+
*
|
|
5
|
+
* Underscore-prefixed like `_shared-metric.js`: this is a helper for the
|
|
6
|
+
* per-kind modules in this directory, not a kind of its own.
|
|
7
|
+
*
|
|
8
|
+
* A leaf — it imports nothing, so `kinds/crap.js` can consume it without
|
|
9
|
+
* adding an edge to anything the comparator does not already reach.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The set of scanned files in which **every** measured method resolved 0%
|
|
14
|
+
* coverage — the observable signature of a source no test loads at all.
|
|
15
|
+
*
|
|
16
|
+
* Deliberately derived from the scan rows rather than plumbed down from the
|
|
17
|
+
* coverage artifact: the rows are the only thing both the preview gate and the
|
|
18
|
+
* close-validation projection hand the comparator, and "the whole file is at
|
|
19
|
+
* zero" is exactly what an all-zero istanbul entry produces here.
|
|
20
|
+
*
|
|
21
|
+
* A row carrying `coverage: null` is skipped, not counted as a zero — an
|
|
22
|
+
* absent observation is not a measured one, and letting it vote would hand the
|
|
23
|
+
* relief below to a file nothing measured at all.
|
|
24
|
+
*
|
|
25
|
+
* @param {Array<{file: string, coverage?: number|null}>} currentRows
|
|
26
|
+
* @returns {Set<string>}
|
|
27
|
+
*/
|
|
28
|
+
export function deriveUncoveredFiles(currentRows) {
|
|
29
|
+
const allZeroByFile = new Map();
|
|
30
|
+
for (const row of currentRows ?? []) {
|
|
31
|
+
if (typeof row?.coverage !== 'number') continue;
|
|
32
|
+
const soFar = allZeroByFile.get(row.file);
|
|
33
|
+
allZeroByFile.set(row.file, (soFar ?? true) && row.coverage === 0);
|
|
34
|
+
}
|
|
35
|
+
const uncovered = new Set();
|
|
36
|
+
for (const [file, allZero] of allZeroByFile) {
|
|
37
|
+
if (allZero) uncovered.add(file);
|
|
38
|
+
}
|
|
39
|
+
return uncovered;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The score a **new** method is judged against `newMethodCeiling` with.
|
|
44
|
+
*
|
|
45
|
+
* Normally the measured CRAP. In a file whose every method scored 0%, the
|
|
46
|
+
* complexity term `c` alone.
|
|
47
|
+
*
|
|
48
|
+
* **Why.** `crap = c²·(1 − cov)³ + c` collapses to `c² + c` at zero coverage,
|
|
49
|
+
* so the default ceiling of 30 caps a *brand-new* method in an untested file
|
|
50
|
+
* at `c ≈ 5`. For the code that is untestable by construction — argv parsing,
|
|
51
|
+
* `spawn` wiring, a CLI's top-level `main()` — that is not a quality signal
|
|
52
|
+
* but a fragmentation tax: the only way past it is to shatter cohesive wiring
|
|
53
|
+
* into sub-five-branch fragments, which raises the file's method count and its
|
|
54
|
+
* reader's cost while measuring nothing. The coverage term still governs every
|
|
55
|
+
* method in a file the tests *do* reach, and a genuinely sprawling function is
|
|
56
|
+
* still refused — `c` alone must clear the ceiling.
|
|
57
|
+
*
|
|
58
|
+
* This is a **gate** decision, not a scoring one: the persisted baseline row
|
|
59
|
+
* keeps its measured `c² + c`, so the regression arm on the next run compares
|
|
60
|
+
* like with like.
|
|
61
|
+
*
|
|
62
|
+
* @param {{file: string, crap: number, cyclomatic?: number}} row
|
|
63
|
+
* @param {Set<string>} uncoveredFiles
|
|
64
|
+
* @returns {number}
|
|
65
|
+
*/
|
|
66
|
+
export function newMethodGateScore(row, uncoveredFiles) {
|
|
67
|
+
if (!uncoveredFiles.has(row.file)) return row.crap;
|
|
68
|
+
return Number.isFinite(row.cyclomatic) ? row.cyclomatic : row.crap;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Assemble a new-method violation.
|
|
73
|
+
*
|
|
74
|
+
* `gateScore` is written **only** when the ceiling was judged against
|
|
75
|
+
* something other than the measured `crap` — the same write-only-when-
|
|
76
|
+
* non-default idiom `projectRow` uses for its row markers — so the printer can
|
|
77
|
+
* name the number that actually failed instead of one nothing tested.
|
|
78
|
+
*
|
|
79
|
+
* @param {object} row
|
|
80
|
+
* @param {number} ceiling
|
|
81
|
+
* @param {number} gateScore
|
|
82
|
+
* @returns {object}
|
|
83
|
+
*/
|
|
84
|
+
export function buildNewViolation(row, ceiling, gateScore) {
|
|
85
|
+
return {
|
|
86
|
+
...row,
|
|
87
|
+
kind: 'new',
|
|
88
|
+
baseline: null,
|
|
89
|
+
ceiling,
|
|
90
|
+
...(gateScore === row.crap ? {} : { gateScore }),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Render the measured-vs-gated half of a new-method violation line.
|
|
96
|
+
*
|
|
97
|
+
* @param {{crap: number, gateScore?: number}} v
|
|
98
|
+
* @returns {string}
|
|
99
|
+
*/
|
|
100
|
+
export function formatNewViolationMeasure(v) {
|
|
101
|
+
if (v.gateScore === undefined) return `crap=${v.crap.toFixed(2)}`;
|
|
102
|
+
return `complexity=${v.gateScore} (file has no coverage; crap=${v.crap.toFixed(2)})`;
|
|
103
|
+
}
|