mandrel 2.30.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/ci-remediation.md +44 -1
- 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/push.js +17 -2
- 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 +65 -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 +2 -12
- package/.agents/skills/stack/qa/playwright/SKILL.md +48 -0
- 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 +49 -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
|
@@ -75,11 +75,80 @@ export function readKnipOutput(filePath) {
|
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Sentinel `symbol` recorded for a **whole-file death** row.
|
|
80
|
+
*
|
|
81
|
+
* Knip's `files` category names a module nothing in the graph imports. There
|
|
82
|
+
* is no per-export identity to record — the whole file is the finding — so the
|
|
83
|
+
* ratchet encodes exactly one row per dead file carrying this symbol. `*` is
|
|
84
|
+
* not a legal JavaScript identifier, so a whole-file row can never collide
|
|
85
|
+
* with a real export row for the same path.
|
|
86
|
+
*/
|
|
87
|
+
const WHOLE_FILE_SYMBOL = '*';
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Pull the dead-file paths out of one knip issue's `files` category.
|
|
91
|
+
*
|
|
92
|
+
* Knip emits `files: [{ name: '<path>' }]`, but tolerate a bare string and
|
|
93
|
+
* fall back to the issue's own `file` so a reporter-shape change degrades to
|
|
94
|
+
* "one row for this path" rather than to silence.
|
|
95
|
+
*
|
|
96
|
+
* @param {{ files?: unknown }} issue
|
|
97
|
+
* @param {string} fallbackFile The issue's own `file` path.
|
|
98
|
+
* @returns {string[]}
|
|
99
|
+
*/
|
|
100
|
+
function extractDeadFileNames(issue, fallbackFile) {
|
|
101
|
+
const entries = Array.isArray(issue.files) ? issue.files : [];
|
|
102
|
+
const names = [];
|
|
103
|
+
for (const entry of entries) {
|
|
104
|
+
const name =
|
|
105
|
+
(typeof entry === 'string' && entry) ||
|
|
106
|
+
(entry && typeof entry.name === 'string' && entry.name) ||
|
|
107
|
+
fallbackFile;
|
|
108
|
+
if (typeof name === 'string' && name.length > 0) names.push(name);
|
|
109
|
+
}
|
|
110
|
+
return names;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Pull the dead-export symbol names out of one knip issue's `exports`
|
|
115
|
+
* category. Knip emits `exports: [{ name, ... }]`; older shapes used `symbol`.
|
|
116
|
+
*
|
|
117
|
+
* @param {{ exports?: unknown }} issue
|
|
118
|
+
* @returns {string[]}
|
|
119
|
+
*/
|
|
120
|
+
function extractDeadExportSymbols(issue) {
|
|
121
|
+
const entries = Array.isArray(issue.exports) ? issue.exports : [];
|
|
122
|
+
const symbols = [];
|
|
123
|
+
for (const e of entries) {
|
|
124
|
+
const symbol =
|
|
125
|
+
(e && typeof e.name === 'string' && e.name) ||
|
|
126
|
+
(e && typeof e.symbol === 'string' && e.symbol) ||
|
|
127
|
+
null;
|
|
128
|
+
if (symbol) symbols.push(symbol);
|
|
129
|
+
}
|
|
130
|
+
return symbols;
|
|
131
|
+
}
|
|
132
|
+
|
|
78
133
|
/**
|
|
79
134
|
* Flatten knip's `--reporter json` output into `{ file, symbol }` rows. Knip
|
|
80
|
-
* emits `{ issues: [{ file, exports: [{ name, ... }], ... }
|
|
81
|
-
*
|
|
82
|
-
*
|
|
135
|
+
* emits `{ issues: [{ file, files: [...], exports: [{ name, ... }], ... }] }`.
|
|
136
|
+
*
|
|
137
|
+
* Two categories are mapped, and the ratchet treats their rows identically:
|
|
138
|
+
*
|
|
139
|
+
* - **`exports`** → one row per unused export, `{ file, symbol: '<name>' }`.
|
|
140
|
+
* - **`files`** → one row per module nothing imports,
|
|
141
|
+
* `{ file, symbol: '*' }`. Story #5001 added this leg: mapping only
|
|
142
|
+
* `exports` made the ratchet structurally blind to *whole-file* death, so a
|
|
143
|
+
* module could lose its last caller and every one of its exports go unused
|
|
144
|
+
* without a single row changing. Knip reports such a module once, under
|
|
145
|
+
* `files`, and suppresses its per-export rows — which is exactly why the
|
|
146
|
+
* export-only reading saw nothing.
|
|
147
|
+
*
|
|
148
|
+
* Whole-file rows are de-duplicated by path so the row set is stable across
|
|
149
|
+
* runs regardless of how many issue records mention the same file. Dependency-
|
|
150
|
+
* and duplicate-level issues stay ignored; knip surfaces those under their own
|
|
151
|
+
* `rules` keys and they are not a code-death signal.
|
|
83
152
|
*
|
|
84
153
|
* @param {unknown} knipEnvelope The parsed knip JSON report.
|
|
85
154
|
* @returns {Array<{ file: string, symbol: string }>}
|
|
@@ -88,16 +157,16 @@ export function extractRowsFromKnip(knipEnvelope) {
|
|
|
88
157
|
const rows = [];
|
|
89
158
|
if (!knipEnvelope || typeof knipEnvelope !== 'object') return rows;
|
|
90
159
|
const issues = Array.isArray(knipEnvelope.issues) ? knipEnvelope.issues : [];
|
|
160
|
+
const seenDeadFiles = new Set();
|
|
91
161
|
for (const issue of issues) {
|
|
92
162
|
const file = issue?.file;
|
|
93
163
|
if (typeof file !== 'string' || file.length === 0) continue;
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (!symbol) continue;
|
|
164
|
+
for (const name of extractDeadFileNames(issue, file)) {
|
|
165
|
+
if (seenDeadFiles.has(name)) continue;
|
|
166
|
+
seenDeadFiles.add(name);
|
|
167
|
+
rows.push({ file: name, symbol: WHOLE_FILE_SYMBOL });
|
|
168
|
+
}
|
|
169
|
+
for (const symbol of extractDeadExportSymbols(issue)) {
|
|
101
170
|
rows.push({ file, symbol });
|
|
102
171
|
}
|
|
103
172
|
}
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Tech Spec #819 §"Degraded-mode contract (Story 3)" — three soft-fail sites
|
|
5
5
|
* (audit-suite selector diff-timeout, lint-baseline JSON-parse, baseline-refresh
|
|
6
|
-
* guardrail git-diff) historically returned a silent zero/empty result.
|
|
7
|
-
*
|
|
6
|
+
* guardrail git-diff) historically returned a silent zero/empty result. Two
|
|
7
|
+
* remain; the lint-baseline site left with its CLI. The contract is:
|
|
8
8
|
*
|
|
9
9
|
* { ok: false, degraded: true, reason: <code>, detail: <human> }
|
|
10
10
|
*
|
|
@@ -201,9 +201,9 @@ export function resolveAlwaysLoadedClosure(root, { fs = nodeFs } = {}) {
|
|
|
201
201
|
|
|
202
202
|
/**
|
|
203
203
|
* Read the resolved `project.docsContextFiles` list from a config object,
|
|
204
|
-
* prefixed by `project.paths.docsRoot`.
|
|
205
|
-
* `
|
|
206
|
-
*
|
|
204
|
+
* prefixed by `project.paths.docsRoot`. Resolves new-shape
|
|
205
|
+
* `project.docsContextFiles` first and falls back to the legacy top-level
|
|
206
|
+
* key, so a config written against either shape yields the same list.
|
|
207
207
|
*
|
|
208
208
|
* @param {object} config resolved config (`resolveConfig()` output)
|
|
209
209
|
* @returns {string[]} repo-relative posix doc paths (existence not yet checked)
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* graduator-core.js — shared mechanism for the feedback-loop graduators.
|
|
3
3
|
*
|
|
4
|
-
* Story #3845 / Epic #3823. The audit-results
|
|
5
|
-
*
|
|
6
|
-
* (the since-retired `code-review-graduator.js`) duplicated ~90% of their mechanism: a
|
|
4
|
+
* Story #3845 / Epic #3823. The since-retired audit-results and code-review
|
|
5
|
+
* graduators duplicated ~90% of their mechanism: a
|
|
7
6
|
* `spawn`-based child runner, the `git cat-file` path probe, the
|
|
8
7
|
* `gh search issues` idempotency probe, the `gh issue create` filer, the
|
|
9
8
|
* `isAutoFileEnabled` toggle reader, and the route → probe → file
|
|
10
|
-
* envelope walk. This module folds all of that into one place so
|
|
11
|
-
*
|
|
12
|
-
* differ (the
|
|
9
|
+
* envelope walk. This module folds all of that into one place so a
|
|
10
|
+
* graduator becomes a thin shell that injects the bits that genuinely
|
|
11
|
+
* differ (the body/title/label builders).
|
|
12
|
+
*
|
|
13
|
+
* Story #5003 removed the structured-comment read limb. Both comment-reading
|
|
14
|
+
* graduators were Epic-era — they walked an Epic's `verification-results`
|
|
15
|
+
* comment, and v2 has no Epics — leaving `retro-proposals-graduator.js` and
|
|
16
|
+
* its pre-parsed `findings[]` as the one live caller.
|
|
13
17
|
*
|
|
14
18
|
* The third `runGh` spawn copy in `prior-feedback-fetcher.js` is also
|
|
15
19
|
* collapsed onto the single `runChild` helper here.
|
|
@@ -34,11 +38,10 @@
|
|
|
34
38
|
* - **Per-run filing cap.** `graduate()` stops filing once
|
|
35
39
|
* `maxFilingsPerRun` issues are created and records the excess as
|
|
36
40
|
* skipped `cap-reached`.
|
|
37
|
-
* - **Pre-parsed / path-less seam.** `graduate()`
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* consumes.
|
|
41
|
+
* - **Pre-parsed / path-less seam.** `graduate()` takes its findings as a
|
|
42
|
+
* pre-parsed array, and a path-less finding skips the path-exists gate
|
|
43
|
+
* instead of being misclassified `file-removed` — the seam the retro
|
|
44
|
+
* auto-filer consumes.
|
|
42
45
|
* - **Durable cross-repo deferral.** Cross-repo-deferred findings are
|
|
43
46
|
* upserted into a structured comment on the Epic instead of only a
|
|
44
47
|
* log line.
|
|
@@ -50,34 +53,7 @@ import { createHash } from 'node:crypto';
|
|
|
50
53
|
import { inNodeTestContext } from '../config/temp-paths.js';
|
|
51
54
|
import { LABEL_COLORS } from '../label-constants.js';
|
|
52
55
|
import { classifyPathSource as defaultClassifier } from '../observability/source-classifier.js';
|
|
53
|
-
import {
|
|
54
|
-
structuredCommentMarker,
|
|
55
|
-
upsertStructuredComment,
|
|
56
|
-
} from '../orchestration/ticketing.js';
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* The single structured-comment marker for the unified `verification-results`
|
|
60
|
-
* findings contract (Story #4411, Epic #4405). It replaces the two retired
|
|
61
|
-
* per-graduator markers (the former code-review and audit-results
|
|
62
|
-
* structured-comment markers). Both feedback-loop graduators
|
|
63
|
-
* search the Epic's comments for this one marker so they file follow-ups
|
|
64
|
-
* from the same unified source comment that `runCodeReview` upserts (comment
|
|
65
|
-
* type `verification-results`). Derived from the canonical
|
|
66
|
-
* `structuredCommentMarker` builder so the read-side marker stays byte-stable
|
|
67
|
-
* with the write side rather than being hand-copied.
|
|
68
|
-
*/
|
|
69
|
-
export const VERIFICATION_RESULTS_MARKER = structuredCommentMarker(
|
|
70
|
-
'verification-results',
|
|
71
|
-
);
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* The single "no source comment" skip reason for the unified contract. Both
|
|
75
|
-
* graduators surface this reason when the Epic carries no
|
|
76
|
-
* `verification-results` comment, replacing the two retired
|
|
77
|
-
* `no-code-review-comment` / `no-audit-results-comment` reasons.
|
|
78
|
-
*/
|
|
79
|
-
export const NO_VERIFICATION_RESULTS_COMMENT_REASON =
|
|
80
|
-
'no-verification-results-comment';
|
|
56
|
+
import { upsertStructuredComment } from '../orchestration/ticketing.js';
|
|
81
57
|
|
|
82
58
|
/**
|
|
83
59
|
* Default child-process timeout. A hung `gh`/`git` spawn previously blocked
|
|
@@ -814,28 +790,24 @@ export async function createFollowUpIssue({
|
|
|
814
790
|
}
|
|
815
791
|
|
|
816
792
|
/**
|
|
817
|
-
* Validate the `graduate` preconditions (toggle, epicId,
|
|
818
|
-
*
|
|
819
|
-
*
|
|
820
|
-
*
|
|
821
|
-
*
|
|
793
|
+
* Validate the `graduate` preconditions (toggle, epicId, currentRepo shape).
|
|
794
|
+
* Returns `null` when all preconditions pass, or a `{ skipped?, errors? }`
|
|
795
|
+
* partial-envelope the caller short-circuits on. Story #4075 — extracted from
|
|
796
|
+
* `graduate` so the orchestrating body holds no guard-chain branching.
|
|
797
|
+
*
|
|
798
|
+
* Story #5003 dropped the `provider.getTicketComments` gate with the
|
|
799
|
+
* structured-comment read limb. The provider is now consulted only by the
|
|
800
|
+
* best-effort cross-repo-deferred upsert, which reports its own faults into
|
|
801
|
+
* `errors[]` — gating the whole walk on a shape only that path needs would
|
|
802
|
+
* refuse work the walk can complete.
|
|
822
803
|
*/
|
|
823
|
-
function checkGraduatePreconditions({
|
|
824
|
-
epicId,
|
|
825
|
-
provider,
|
|
826
|
-
currentRepo,
|
|
827
|
-
config,
|
|
828
|
-
spec,
|
|
829
|
-
}) {
|
|
804
|
+
function checkGraduatePreconditions({ epicId, currentRepo, config, spec }) {
|
|
830
805
|
if (!spec.isAutoFileEnabled(config)) {
|
|
831
806
|
return { skipped: [{ reason: 'toggle-disabled' }] };
|
|
832
807
|
}
|
|
833
808
|
if (!Number.isInteger(epicId) || epicId < 1) {
|
|
834
809
|
return { errors: [`${spec.fnName}: missing or invalid epicId`] };
|
|
835
810
|
}
|
|
836
|
-
if (!provider || typeof provider.getTicketComments !== 'function') {
|
|
837
|
-
return { errors: [`${spec.fnName}: provider lacks getTicketComments`] };
|
|
838
|
-
}
|
|
839
811
|
if (
|
|
840
812
|
!currentRepo ||
|
|
841
813
|
typeof currentRepo.owner !== 'string' ||
|
|
@@ -846,36 +818,6 @@ function checkGraduatePreconditions({
|
|
|
846
818
|
return null;
|
|
847
819
|
}
|
|
848
820
|
|
|
849
|
-
/**
|
|
850
|
-
* Read the source structured comment off the Epic and parse its findings.
|
|
851
|
-
* Returns `{ findings }` on success, or `{ skipped?, errors? }` for the
|
|
852
|
-
* no-comment / parse-empty / fetch-error short-circuits. Story #4075 —
|
|
853
|
-
* extracted from `graduate`.
|
|
854
|
-
*/
|
|
855
|
-
async function loadGraduateFindings({ epicId, provider, spec }) {
|
|
856
|
-
let comments;
|
|
857
|
-
try {
|
|
858
|
-
comments = await provider.getTicketComments(epicId);
|
|
859
|
-
} catch (err) {
|
|
860
|
-
return {
|
|
861
|
-
errors: [
|
|
862
|
-
`getTicketComments failed for epic #${epicId}: ${err?.message ?? err}`,
|
|
863
|
-
],
|
|
864
|
-
};
|
|
865
|
-
}
|
|
866
|
-
const matched = (Array.isArray(comments) ? comments : []).filter(
|
|
867
|
-
(c) => typeof c?.body === 'string' && c.body.includes(spec.commentMarker),
|
|
868
|
-
);
|
|
869
|
-
if (matched.length === 0) {
|
|
870
|
-
return { skipped: [{ reason: spec.noCommentReason }] };
|
|
871
|
-
}
|
|
872
|
-
const findings = spec.parseFindings(matched[matched.length - 1].body);
|
|
873
|
-
if (findings.length === 0) {
|
|
874
|
-
return { skipped: [{ reason: 'no-non-blocking-findings' }] };
|
|
875
|
-
}
|
|
876
|
-
return { findings };
|
|
877
|
-
}
|
|
878
|
-
|
|
879
821
|
/**
|
|
880
822
|
* Probe whether a finding was already filed, checking both the current
|
|
881
823
|
* content-hash marker AND the legacy `(epicId, parse-index)` marker so
|
|
@@ -1267,17 +1209,16 @@ async function persistCrossRepoDeferred({
|
|
|
1267
1209
|
}
|
|
1268
1210
|
|
|
1269
1211
|
/**
|
|
1270
|
-
* Parametrized graduator walk.
|
|
1271
|
-
*
|
|
1272
|
-
* each finding runs the shared route → path probe → idempotency probe →
|
|
1212
|
+
* Parametrized graduator walk. Takes a pre-parsed `findings` array and, for
|
|
1213
|
+
* each finding, runs the shared route → path probe → idempotency probe →
|
|
1273
1214
|
* cap → file sequence. Never throws — every failure path is captured in
|
|
1274
1215
|
* `errors[]`.
|
|
1275
1216
|
*
|
|
1217
|
+
* Each finding MUST carry `{ severity, path, summary, index }` and MAY carry
|
|
1218
|
+
* additional fields (e.g. `category`) that the builders use.
|
|
1219
|
+
*
|
|
1276
1220
|
* The per-graduator variation lives entirely in the injected callbacks:
|
|
1277
1221
|
*
|
|
1278
|
-
* - `parseFindings(body)` — turns the rendered comment into findings.
|
|
1279
|
-
* Each finding MUST carry `{ severity, path, summary, index }` and
|
|
1280
|
-
* MAY carry additional fields (e.g. `lens`) that the builder uses.
|
|
1281
1222
|
* - `buildContentMarker(epicId, finding)` — the content-hash HTML-comment
|
|
1282
1223
|
* marker embedded in (and searched for in) follow-up bodies.
|
|
1283
1224
|
* - `buildLegacyMarker(epicId, index)` — the pre-cutover ordinal marker,
|
|
@@ -1299,7 +1240,8 @@ async function persistCrossRepoDeferred({
|
|
|
1299
1240
|
*
|
|
1300
1241
|
* @param {object} opts
|
|
1301
1242
|
* @param {number} opts.epicId
|
|
1302
|
-
* @param {object} opts.provider — exposes `
|
|
1243
|
+
* @param {object} opts.provider — exposes `postComment(ticketId, body)` for
|
|
1244
|
+
* the durable cross-repo-deferred persistence
|
|
1303
1245
|
* @param {object} [opts.config]
|
|
1304
1246
|
* @param {{owner: string, repo: string}} opts.currentRepo
|
|
1305
1247
|
* @param {{owner: string, repo: string}} [opts.frameworkRepo]
|
|
@@ -1310,9 +1252,9 @@ async function persistCrossRepoDeferred({
|
|
|
1310
1252
|
* @param {string} [opts.cwd]
|
|
1311
1253
|
* @param {number} [opts.timeoutMs] — per-spawn watchdog bound
|
|
1312
1254
|
* @param {number} [opts.maxFilingsPerRun] — per-run filing cap
|
|
1313
|
-
* @param {Array<object>}
|
|
1314
|
-
*
|
|
1315
|
-
*
|
|
1255
|
+
* @param {Array<object>} opts.findings — the pre-parsed findings to file.
|
|
1256
|
+
* Required: a non-array is an `errors[]` short-circuit, never a silent
|
|
1257
|
+
* no-op.
|
|
1316
1258
|
* @param {Set<string>} [opts.filedMarkers] — in-process memo of content
|
|
1317
1259
|
* markers filed so far. Pass a shared Set across multiple `graduate()`
|
|
1318
1260
|
* calls in one logical invocation (e.g. the retro graduator's two source
|
|
@@ -1360,23 +1302,23 @@ export async function graduate({
|
|
|
1360
1302
|
|
|
1361
1303
|
const precondition = checkGraduatePreconditions({
|
|
1362
1304
|
epicId,
|
|
1363
|
-
provider,
|
|
1364
1305
|
currentRepo,
|
|
1365
1306
|
config,
|
|
1366
1307
|
spec,
|
|
1367
1308
|
});
|
|
1368
1309
|
if (precondition) return { ...envelope, ...precondition };
|
|
1369
1310
|
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1311
|
+
// Pre-parsed findings are the only source (Story #5003). The
|
|
1312
|
+
// structured-comment read/parse limb went with the audit-results
|
|
1313
|
+
// graduator: it walked an Epic's `verification-results` comment, and v2
|
|
1314
|
+
// has no Epics, so it could only ever resolve zero findings.
|
|
1315
|
+
if (!Array.isArray(preParsedFindings)) {
|
|
1316
|
+
return {
|
|
1317
|
+
...envelope,
|
|
1318
|
+
errors: [`${spec.fnName}: findings[] is required and must be an array`],
|
|
1319
|
+
};
|
|
1379
1320
|
}
|
|
1321
|
+
const findings = preParsedFindings;
|
|
1380
1322
|
|
|
1381
1323
|
// Blast-radius guard (Story #4837): decided ONCE per walk, before the
|
|
1382
1324
|
// first spawn, so a refused context costs no child process at all.
|
|
@@ -138,6 +138,14 @@ function metaSourceLabel(source) {
|
|
|
138
138
|
* a single source so the constant classifier routes every finding in the
|
|
139
139
|
* bucket to the correct repo (and the label reflects that source).
|
|
140
140
|
*
|
|
141
|
+
* The bundle carries **only** builders. It used to declare a
|
|
142
|
+
* `commentMarker` / `noCommentReason` / `parseFindings` trio purely to satisfy
|
|
143
|
+
* the shared walk's shape — this graduator supplies its findings pre-parsed,
|
|
144
|
+
* so the walk never consulted any of the three. Story #5003 deleted that limb
|
|
145
|
+
* from `graduate()` along with the Epic-era graduator that was its only real
|
|
146
|
+
* consumer, and the placeholder fields went with it: a declared-but-unread
|
|
147
|
+
* field is a standing invitation to write a parser nothing will call.
|
|
148
|
+
*
|
|
141
149
|
* @param {'framework'|'consumer'} source
|
|
142
150
|
* @returns {object}
|
|
143
151
|
*/
|
|
@@ -145,12 +153,6 @@ function makeSpec(source) {
|
|
|
145
153
|
return {
|
|
146
154
|
fnName: 'graduateRetroProposals',
|
|
147
155
|
isAutoFileEnabled,
|
|
148
|
-
// Pre-parsed seam: the comment marker / parser are never consulted
|
|
149
|
-
// (findings are supplied directly), but the fields are declared for the
|
|
150
|
-
// shared walk's shape.
|
|
151
|
-
commentMarker: '<!-- structured-comment: retro -->',
|
|
152
|
-
noCommentReason: 'no-retro-comment',
|
|
153
|
-
parseFindings: () => [],
|
|
154
156
|
buildContentMarker,
|
|
155
157
|
buildMatchTokens,
|
|
156
158
|
crossRepoCommentAttrs: { graduator: 'retro-proposals' },
|
|
@@ -210,8 +212,8 @@ function toFinding(item, source, index) {
|
|
|
210
212
|
*
|
|
211
213
|
* @param {object} opts
|
|
212
214
|
* @param {number} opts.epicId
|
|
213
|
-
* @param {object} opts.provider — ticketing provider (
|
|
214
|
-
*
|
|
215
|
+
* @param {object} opts.provider — ticketing provider (`postComment`, for the
|
|
216
|
+
* cross-repo-deferred persistence).
|
|
215
217
|
* @param {object} [opts.config] — resolved agentrc.
|
|
216
218
|
* @param {{owner: string, repo: string}} opts.currentRepo — the repo the
|
|
217
219
|
* retro is running inside (the consumer's own repo); the cross-repo guard's
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fs-walk.js — the one recursive file walker the lint scripts share.
|
|
3
|
+
*
|
|
4
|
+
* `check-lifecycle-lint.js` and `lint-label-vocabulary.js` each carried a
|
|
5
|
+
* private copy of this generator, differing only in the extension they
|
|
6
|
+
* matched — the second one's comment said so outright ("mirrors the walker
|
|
7
|
+
* shape in check-lifecycle-lint.js"). Story #5024 extracted it: shrinking
|
|
8
|
+
* check-lifecycle-lint.js pushed its duplication percentage up on an unchanged
|
|
9
|
+
* clone count, which is the ratchet correctly pointing at boilerplate that
|
|
10
|
+
* should not have been copied twice.
|
|
11
|
+
*
|
|
12
|
+
* A missing directory yields nothing rather than throwing. That is
|
|
13
|
+
* load-bearing, not lenient: the lint surfaces are directories a deletion can
|
|
14
|
+
* legitimately remove (Story #5024 deleted `lifecycle/listeners/`), and a scan
|
|
15
|
+
* of an absent path is vacuously clean, not an error. Every other `readdir`
|
|
16
|
+
* failure — a permission error, a file where a directory was expected —
|
|
17
|
+
* propagates, so a genuinely broken scan cannot read as clean.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { readdirSync } from 'node:fs';
|
|
21
|
+
import path from 'node:path';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Walk a directory tree synchronously, yielding absolute paths of every file
|
|
25
|
+
* whose name ends with `extension`.
|
|
26
|
+
*
|
|
27
|
+
* @param {string} dir Absolute directory to walk.
|
|
28
|
+
* @param {string} extension Extension to match, leading dot included (`.js`).
|
|
29
|
+
* @param {{ readDir?: typeof readdirSync }} [ports] Injectable for tests.
|
|
30
|
+
* @returns {Generator<string, void, void>}
|
|
31
|
+
*/
|
|
32
|
+
export function* walkFilesByExtension(
|
|
33
|
+
dir,
|
|
34
|
+
extension,
|
|
35
|
+
{ readDir = readdirSync } = {},
|
|
36
|
+
) {
|
|
37
|
+
let entries;
|
|
38
|
+
try {
|
|
39
|
+
entries = readDir(dir, { withFileTypes: true });
|
|
40
|
+
} catch (err) {
|
|
41
|
+
if (err.code === 'ENOENT') return;
|
|
42
|
+
throw err;
|
|
43
|
+
}
|
|
44
|
+
for (const entry of entries) {
|
|
45
|
+
const p = path.join(dir, entry.name);
|
|
46
|
+
if (entry.isDirectory()) {
|
|
47
|
+
yield* walkFilesByExtension(p, extension, { readDir });
|
|
48
|
+
} else if (entry.isFile() && entry.name.endsWith(extension)) {
|
|
49
|
+
yield p;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -129,8 +129,8 @@ export function classifyBranchSeed({ localHas, remoteHas }) {
|
|
|
129
129
|
* throws (the v2 standalone path has no concurrent creator to race).
|
|
130
130
|
*
|
|
131
131
|
* The asymmetric surrounding wrappers (merged-sweep, fast-forward,
|
|
132
|
-
* donor-prime, workspace-verify
|
|
133
|
-
* in
|
|
132
|
+
* donor-prime, workspace-verify) are deliberately NOT folded in — they stay
|
|
133
|
+
* in their respective callers.
|
|
134
134
|
*
|
|
135
135
|
* Caller-specific log lines and error text are passed in as the `messages`
|
|
136
136
|
* data bag so behaviour stays byte-identical to the pre-extraction switches.
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
30
|
import { execFileSync, spawnSync } from 'node:child_process';
|
|
31
|
+
import { execFileCapture, spawnCapture } from './child-exec.js';
|
|
31
32
|
|
|
32
33
|
/**
|
|
33
34
|
* Result of a `gitSpawn` invocation. Field semantics match `child_process.spawnSync`
|
|
@@ -40,23 +41,6 @@ import { execFileSync, spawnSync } from 'node:child_process';
|
|
|
40
41
|
* @property {string} stderr - Trimmed stderr.
|
|
41
42
|
*/
|
|
42
43
|
|
|
43
|
-
/**
|
|
44
|
-
* Explicit stdout ceiling for every git invocation in this module.
|
|
45
|
-
*
|
|
46
|
-
* Both child-process runners default `maxBuffer` to 1 MB, at which point the
|
|
47
|
-
* child is killed and the call fails with `ENOBUFS` for a reason unrelated to
|
|
48
|
-
* git. The push path is the sharp edge: `git push` relays the whole `pre-push`
|
|
49
|
-
* hook output, which is unbounded by design — this repo's hook emits a full
|
|
50
|
-
* `check-baselines` envelope, measured at 2,166,643 bytes, so every Story
|
|
51
|
-
* close failed at `phase: push` once hooks became reachable inside worktrees.
|
|
52
|
-
*
|
|
53
|
-
* 64 MB is the bound Story #4914 already set on `baselines/git-base.js` for
|
|
54
|
-
* the identical failure, matching `run-test-profile.js`,
|
|
55
|
-
* `audit-baselines/trend.js` and `audit-baselines/weights.js`. This module was
|
|
56
|
-
* missed by that sweep.
|
|
57
|
-
*/
|
|
58
|
-
const MAX_BUFFER_BYTES = 64 * 1024 * 1024;
|
|
59
|
-
|
|
60
44
|
let _execFileSync = execFileSync;
|
|
61
45
|
let _spawnSync = spawnSync;
|
|
62
46
|
|
|
@@ -129,26 +113,30 @@ export function __setGitRunners(exec, spawn) {
|
|
|
129
113
|
* the interface returned by {@link createGitInterface} route through this —
|
|
130
114
|
* they differ only in which `execFileSync` they hand it.
|
|
131
115
|
*
|
|
116
|
+
* Buffer, shell and encoding policy belong to
|
|
117
|
+
* [`child-exec.js`](child-exec.js); this function contributes only the git
|
|
118
|
+
* specifics (argv prefix, cwd, scrubbed env).
|
|
119
|
+
*
|
|
132
120
|
* @param {typeof execFileSync} exec
|
|
133
121
|
* @param {string} cwd
|
|
134
122
|
* @param {string[]} args
|
|
135
123
|
* @returns {string} Trimmed stdout text.
|
|
136
124
|
*/
|
|
137
125
|
function runGitSync(exec, cwd, args) {
|
|
138
|
-
return
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
126
|
+
return String(
|
|
127
|
+
execFileCapture('git', args, {
|
|
128
|
+
run: exec,
|
|
129
|
+
cwd,
|
|
130
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
131
|
+
env: cleanGitEnv(),
|
|
132
|
+
}),
|
|
133
|
+
).trim();
|
|
146
134
|
}
|
|
147
135
|
|
|
148
136
|
/**
|
|
149
137
|
* The **single** non-throwing git runner — the `spawnSync` counterpart of
|
|
150
138
|
* {@link runGitSync}, normalising `status`/stdout/stderr into a
|
|
151
|
-
* {@link GitResult}.
|
|
139
|
+
* {@link GitResult} via the shared exec surface.
|
|
152
140
|
*
|
|
153
141
|
* @param {typeof spawnSync} spawn
|
|
154
142
|
* @param {string} cwd
|
|
@@ -156,19 +144,11 @@ function runGitSync(exec, cwd, args) {
|
|
|
156
144
|
* @returns {GitResult}
|
|
157
145
|
*/
|
|
158
146
|
function runGitSpawn(spawn, cwd, args) {
|
|
159
|
-
|
|
147
|
+
return spawnCapture('git', args, {
|
|
148
|
+
run: spawn,
|
|
160
149
|
cwd,
|
|
161
|
-
stdio: 'pipe',
|
|
162
|
-
encoding: 'utf-8',
|
|
163
|
-
shell: false,
|
|
164
150
|
env: cleanGitEnv(),
|
|
165
|
-
maxBuffer: MAX_BUFFER_BYTES,
|
|
166
151
|
});
|
|
167
|
-
return {
|
|
168
|
-
status: result.status ?? 1,
|
|
169
|
-
stdout: (result.stdout ?? '').trim(),
|
|
170
|
-
stderr: (result.stderr ?? '').trim(),
|
|
171
|
-
};
|
|
172
152
|
}
|
|
173
153
|
|
|
174
154
|
/**
|