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
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* planning-corpus.js — corpus-aware context for the standalone-Story
|
|
3
|
-
* planning path (Story #4432).
|
|
4
|
-
*
|
|
5
|
-
* `/plan --seed` drafts a standalone Story from the seed, the body
|
|
6
|
-
* template, and a title-only duplicate scan. This module assembles the
|
|
7
|
-
* inherited context (`corpusContext`) for `story-plan.js`'s
|
|
8
|
-
* `--emit-context` envelope:
|
|
9
|
-
*
|
|
10
|
-
* 1. `docsDigest` — the same per-project docs digest
|
|
11
|
-
* `orchestration/docs-digest.js` builds for `/deliver` Story
|
|
12
|
-
* children, reused here so the standalone path gets the same
|
|
13
|
-
* compact outline instead of re-reading the whole docs set.
|
|
14
|
-
* `null` when `project.docsContextFiles` is not configured.
|
|
15
|
-
* 2. `relevantSections` — always `[]`. This field previously carried
|
|
16
|
-
* ranked Tech Spec excerpts mined from open Epics. v2.0.0 removed
|
|
17
|
-
* the Epic tier, and the provider's Epic-list surface was reduced
|
|
18
|
-
* to a `return []` stub, which made the entire ranking pipeline a
|
|
19
|
-
* permanent no-op. The pipeline has been removed; the field is
|
|
20
|
-
* retained so the envelope shape stays stable for its consumers.
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
import { buildDocsDigest } from './orchestration/docs-digest.js';
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Assemble the `corpusContext` field of the story-plan context envelope.
|
|
27
|
-
*
|
|
28
|
-
* @param {{
|
|
29
|
-
* docsContextFiles?: string[],
|
|
30
|
-
* docsRoot?: string,
|
|
31
|
-
* }} opts
|
|
32
|
-
* @returns {Promise<{ docsDigest: string|null, relevantSections: Array<object> }>}
|
|
33
|
-
*/
|
|
34
|
-
export async function buildCorpusContext({ docsContextFiles, docsRoot }) {
|
|
35
|
-
const docsDigest = await buildDocsDigest({ docsContextFiles, docsRoot });
|
|
36
|
-
return { docsDigest, relevantSections: [] };
|
|
37
|
-
}
|
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
// .agents/scripts/lib/qa/coverage-report.js
|
|
2
|
-
//
|
|
3
|
-
// Render an AC × test-tier coverage matrix to a human-readable markdown
|
|
4
|
-
// report and persist it under `<tempRoot>/qa/`.
|
|
5
|
-
//
|
|
6
|
-
// The matrix shape is produced by `lib/qa/coverage-verdict.js#acceptanceMatrix`
|
|
7
|
-
// — one row per acceptance criterion, each row carrying the per-tier
|
|
8
|
-
// `coverageVerdict` (`{unit, contract, acceptance}` with `present`/`absent`
|
|
9
|
-
// statuses + notes). This module is the presentation + persistence seam:
|
|
10
|
-
// - {@link renderCoverageReport} — pure: matrix → markdown string.
|
|
11
|
-
// - {@link reportPathFor} — the report path under `<tempRoot>/qa/`.
|
|
12
|
-
// - {@link writeCoverageReport} — render + write the markdown to disk.
|
|
13
|
-
//
|
|
14
|
-
// `renderCoverageReport` does no I/O so it stays trivially unit-testable;
|
|
15
|
-
// `writeCoverageReport` accepts an injectable `fsImpl` (default `node:fs`)
|
|
16
|
-
// for the same reason, mirroring `lib/qa/qa-session.js`.
|
|
17
|
-
|
|
18
|
-
import fs from 'node:fs';
|
|
19
|
-
import path from 'node:path';
|
|
20
|
-
|
|
21
|
-
import { tempRootFrom } from '../config/temp-paths.js';
|
|
22
|
-
import { acceptanceMatrix, TIERS } from './coverage-verdict.js';
|
|
23
|
-
|
|
24
|
-
/** Directory segment (under `tempRoot`) that holds QA coverage reports. */
|
|
25
|
-
export const QA_REPORT_DIRNAME = 'qa';
|
|
26
|
-
|
|
27
|
-
/** Cell glyph for each verdict status, used in the markdown matrix table. */
|
|
28
|
-
const STATUS_GLYPH = Object.freeze({
|
|
29
|
-
present: '✅ present',
|
|
30
|
-
absent: '❌ absent',
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
/** Title-case a tier name for the table header (`unit` → `Unit`). */
|
|
34
|
-
function tierHeader(tier) {
|
|
35
|
-
return tier.charAt(0).toUpperCase() + tier.slice(1);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Escape the pipe character so a label never breaks the markdown table, and
|
|
40
|
-
* collapse newlines to spaces so a multi-line criterion stays on one row.
|
|
41
|
-
*/
|
|
42
|
-
function cell(value) {
|
|
43
|
-
return String(value ?? '')
|
|
44
|
-
.replace(/\r?\n/g, ' ')
|
|
45
|
-
.replace(/\|/g, '\\|')
|
|
46
|
-
.trim();
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Resolve the matrix input. Accepts either a pre-built matrix
|
|
51
|
-
* (`{tiers, rows}` from {@link acceptanceMatrix}) or raw criteria (array or
|
|
52
|
-
* keyed object), in which case it is run through {@link acceptanceMatrix}.
|
|
53
|
-
*
|
|
54
|
-
* @param {object|Array} input
|
|
55
|
-
* @returns {{tiers: ReadonlyArray<string>, rows: Array}}
|
|
56
|
-
*/
|
|
57
|
-
function resolveMatrix(input) {
|
|
58
|
-
if (
|
|
59
|
-
input &&
|
|
60
|
-
typeof input === 'object' &&
|
|
61
|
-
!Array.isArray(input) &&
|
|
62
|
-
Array.isArray(input.rows)
|
|
63
|
-
) {
|
|
64
|
-
return { tiers: input.tiers ?? TIERS, rows: input.rows };
|
|
65
|
-
}
|
|
66
|
-
return acceptanceMatrix(input);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Render an AC × test-tier coverage matrix to a markdown report string.
|
|
71
|
-
*
|
|
72
|
-
* The report has a title, a one-line summary of how many criteria are fully
|
|
73
|
-
* covered, the matrix table (one row per AC, one column per tier), and a
|
|
74
|
-
* per-criterion notes section explaining each absent tier.
|
|
75
|
-
*
|
|
76
|
-
* @param {{tiers?:ReadonlyArray<string>,rows:Array}|Array|object} input
|
|
77
|
-
* Either a built matrix (`{tiers, rows}`) or raw criteria to build one from.
|
|
78
|
-
* @param {{ title?: string, generatedAt?: string }} [opts]
|
|
79
|
-
* @returns {string} The markdown report.
|
|
80
|
-
*/
|
|
81
|
-
export function renderCoverageReport(input, opts = {}) {
|
|
82
|
-
const { tiers, rows } = resolveMatrix(input);
|
|
83
|
-
const title =
|
|
84
|
-
typeof opts.title === 'string' && opts.title.trim() !== ''
|
|
85
|
-
? opts.title.trim()
|
|
86
|
-
: 'QA Coverage — AC × Test-Tier Matrix';
|
|
87
|
-
|
|
88
|
-
const fullyCovered = rows.filter((row) =>
|
|
89
|
-
tiers.every((tier) => row.verdict[tier]?.status === 'present'),
|
|
90
|
-
).length;
|
|
91
|
-
|
|
92
|
-
const lines = [];
|
|
93
|
-
lines.push(`# ${title}`);
|
|
94
|
-
lines.push('');
|
|
95
|
-
if (typeof opts.generatedAt === 'string' && opts.generatedAt.trim() !== '') {
|
|
96
|
-
lines.push(`_Generated: ${opts.generatedAt.trim()}_`);
|
|
97
|
-
lines.push('');
|
|
98
|
-
}
|
|
99
|
-
lines.push(
|
|
100
|
-
`${rows.length} acceptance criteri${rows.length === 1 ? 'on' : 'a'}, ` +
|
|
101
|
-
`${fullyCovered} fully covered across all ${tiers.length} tiers.`,
|
|
102
|
-
);
|
|
103
|
-
lines.push('');
|
|
104
|
-
|
|
105
|
-
// Matrix table.
|
|
106
|
-
const headerCells = ['Acceptance Criterion', ...tiers.map(tierHeader)];
|
|
107
|
-
lines.push(`| ${headerCells.join(' | ')} |`);
|
|
108
|
-
lines.push(`| ${headerCells.map(() => '---').join(' | ')} |`);
|
|
109
|
-
for (const row of rows) {
|
|
110
|
-
const acCell = cell(row.label ? `${row.id}: ${row.label}` : row.id);
|
|
111
|
-
const tierCells = tiers.map((tier) => {
|
|
112
|
-
const status = row.verdict[tier]?.status ?? 'absent';
|
|
113
|
-
return STATUS_GLYPH[status] ?? cell(status);
|
|
114
|
-
});
|
|
115
|
-
lines.push(`| ${[acCell, ...tierCells].join(' | ')} |`);
|
|
116
|
-
}
|
|
117
|
-
lines.push('');
|
|
118
|
-
|
|
119
|
-
// Per-criterion notes.
|
|
120
|
-
lines.push('## Notes');
|
|
121
|
-
lines.push('');
|
|
122
|
-
for (const row of rows) {
|
|
123
|
-
lines.push(
|
|
124
|
-
`### ${cell(row.id)}${row.label ? ` — ${cell(row.label)}` : ''}`,
|
|
125
|
-
);
|
|
126
|
-
for (const tier of tiers) {
|
|
127
|
-
const v = row.verdict[tier];
|
|
128
|
-
const status = v?.status ?? 'absent';
|
|
129
|
-
const note = v?.note ?? '';
|
|
130
|
-
lines.push(`- **${tierHeader(tier)}** (${status}): ${cell(note)}`);
|
|
131
|
-
}
|
|
132
|
-
lines.push('');
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
return `${lines.join('\n').trimEnd()}\n`;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* The report path under `<tempRoot>/qa/`.
|
|
140
|
-
*
|
|
141
|
-
* @param {string} [fileName] Report file name (default `coverage-report.md`).
|
|
142
|
-
* Path separators are rejected so a caller cannot escape the `qa/` dir.
|
|
143
|
-
* @param {object} [config] Resolved config bag (for `project.paths.tempRoot`).
|
|
144
|
-
* @returns {string}
|
|
145
|
-
*/
|
|
146
|
-
export function reportPathFor(fileName = 'coverage-report.md', config) {
|
|
147
|
-
const name = String(fileName);
|
|
148
|
-
if (name.includes('/') || name.includes('\\') || name === '..') {
|
|
149
|
-
throw new Error(
|
|
150
|
-
`[coverage-report] file name must not contain path separators; got ${JSON.stringify(name)}`,
|
|
151
|
-
);
|
|
152
|
-
}
|
|
153
|
-
return path.join(tempRootFrom(config), QA_REPORT_DIRNAME, name);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Render the matrix to markdown and write it under `<tempRoot>/qa/`, creating
|
|
158
|
-
* the directory if needed. Returns the resolved path and the rendered body.
|
|
159
|
-
*
|
|
160
|
-
* @param {{tiers?:ReadonlyArray<string>,rows:Array}|Array|object} input
|
|
161
|
-
* Either a built matrix or raw criteria.
|
|
162
|
-
* @param {{
|
|
163
|
-
* fileName?: string,
|
|
164
|
-
* config?: object,
|
|
165
|
-
* title?: string,
|
|
166
|
-
* generatedAt?: string,
|
|
167
|
-
* fsImpl?: typeof fs,
|
|
168
|
-
* }} [opts]
|
|
169
|
-
* @returns {{ path: string, markdown: string }}
|
|
170
|
-
*/
|
|
171
|
-
export function writeCoverageReport(input, opts = {}) {
|
|
172
|
-
const fsImpl = opts.fsImpl ?? fs;
|
|
173
|
-
const markdown = renderCoverageReport(input, {
|
|
174
|
-
title: opts.title,
|
|
175
|
-
generatedAt: opts.generatedAt,
|
|
176
|
-
});
|
|
177
|
-
const reportPath = reportPathFor(opts.fileName, opts.config);
|
|
178
|
-
fsImpl.mkdirSync(path.dirname(reportPath), { recursive: true });
|
|
179
|
-
fsImpl.writeFileSync(reportPath, markdown, 'utf8');
|
|
180
|
-
return { path: reportPath, markdown };
|
|
181
|
-
}
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
// .agents/scripts/lib/qa/propose-missing-test.js
|
|
2
|
-
//
|
|
3
|
-
// Deterministic missing-test proposal from a coverage verdict.
|
|
4
|
-
//
|
|
5
|
-
// Closes the loop on accuracy (f4-accuracy): given the per-tier coverage
|
|
6
|
-
// verdict produced by `lib/qa/coverage-verdict.js`, name the single tier that
|
|
7
|
-
// should have caught a finding and draft a one-line test suggestion for it.
|
|
8
|
-
//
|
|
9
|
-
// The verdict's pyramid order is unit < contract < acceptance. The cheapest
|
|
10
|
-
// tier that is ABSENT is the one a finding "leaked through" — adding a test
|
|
11
|
-
// there is the lowest-cost way to have caught it — so this helper proposes
|
|
12
|
-
// the *lowest* absent tier. When every tier is present there is no gap to
|
|
13
|
-
// fill, so no proposal is returned.
|
|
14
|
-
//
|
|
15
|
-
// Pure logic, no I/O: no network, no child processes, no filesystem or env
|
|
16
|
-
// reads. The companion process skill is `core/qa-coverage-mapping`.
|
|
17
|
-
//
|
|
18
|
-
// Public API:
|
|
19
|
-
//
|
|
20
|
-
// proposeMissingTest(coverageVerdict) -> null | {
|
|
21
|
-
// tier: 'unit' | 'contract' | 'acceptance',
|
|
22
|
-
// description: string, // one-line test suggestion
|
|
23
|
-
// }
|
|
24
|
-
|
|
25
|
-
import { TIERS } from './coverage-verdict.js';
|
|
26
|
-
|
|
27
|
-
const ABSENT = 'absent';
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* One-line test-suggestion templates, keyed by tier. Each takes the absent
|
|
31
|
-
* tier's verdict note (which already explains *why* the tier is uncovered)
|
|
32
|
-
* and frames it as an actionable suggestion.
|
|
33
|
-
*/
|
|
34
|
-
const DESCRIPTION_BY_TIER = Object.freeze({
|
|
35
|
-
unit: (note) =>
|
|
36
|
-
`Add a colocated unit test exercising this surface in isolation — ${note}.`,
|
|
37
|
-
contract: (note) =>
|
|
38
|
-
`Add a contract test asserting this surface’s wire shape or boundary — ${note}.`,
|
|
39
|
-
acceptance: (note) =>
|
|
40
|
-
`Add an acceptance scenario covering the user-visible journey — ${note}.`,
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Read the absent-tier note off a verdict entry, falling back to a generic
|
|
45
|
-
* phrase when the entry omits a usable note.
|
|
46
|
-
*
|
|
47
|
-
* @param {{note?:unknown}} entry
|
|
48
|
-
* @returns {string}
|
|
49
|
-
*/
|
|
50
|
-
function noteFor(entry) {
|
|
51
|
-
return typeof entry.note === 'string' && entry.note.trim() !== ''
|
|
52
|
-
? entry.note.trim()
|
|
53
|
-
: 'no test currently covers this tier';
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Return true when a verdict entry marks its tier ABSENT.
|
|
58
|
-
*
|
|
59
|
-
* @param {unknown} entry
|
|
60
|
-
* @returns {boolean}
|
|
61
|
-
*/
|
|
62
|
-
function isAbsent(entry) {
|
|
63
|
-
return entry !== null && typeof entry === 'object' && entry.status === ABSENT;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Propose the single missing test that should have caught a finding.
|
|
68
|
-
*
|
|
69
|
-
* Walks the tiers in pyramid order (unit → contract → acceptance) and returns
|
|
70
|
-
* a proposal for the first ABSENT tier — the lowest-cost gap. Returns `null`
|
|
71
|
-
* when every tier is present (full coverage, nothing to propose).
|
|
72
|
-
*
|
|
73
|
-
* @param {Record<string,{status?:string,note?:string}>} coverageVerdict - The
|
|
74
|
-
* object produced by `coverageVerdict()` in `lib/qa/coverage-verdict.js`.
|
|
75
|
-
* @returns {null | {tier:string, description:string}}
|
|
76
|
-
*/
|
|
77
|
-
export function proposeMissingTest(coverageVerdict) {
|
|
78
|
-
if (coverageVerdict === null || typeof coverageVerdict !== 'object') {
|
|
79
|
-
throw new TypeError(
|
|
80
|
-
'proposeMissingTest: coverageVerdict must be an object',
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
for (const tier of TIERS) {
|
|
85
|
-
const entry = coverageVerdict[tier];
|
|
86
|
-
if (isAbsent(entry)) {
|
|
87
|
-
return {
|
|
88
|
-
tier,
|
|
89
|
-
description: DESCRIPTION_BY_TIER[tier](noteFor(entry)),
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return null;
|
|
95
|
-
}
|
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* QA context hydrator — Story #3805, Epic #3798 (f1-shared-qa-core).
|
|
3
|
-
*
|
|
4
|
-
* Both QA front-ends (`/qa-explore` and `/qa-run`) need to load the
|
|
5
|
-
* *grounded* surface context for an Epic before they reason about what to test:
|
|
6
|
-
* the Epic body (which carries the folded Tech Spec sections and
|
|
7
|
-
* Acceptance Table — Story #4324 retired the separate context tickets),
|
|
8
|
-
* the project's `.feature` files, the implementation files the surface
|
|
9
|
-
* map points at, and a slice of recent git history. Today a front-end that
|
|
10
|
-
* trusts in-code comments ("this handler lives at …") can be wrong — the path
|
|
11
|
-
* may have moved, or never existed on the base branch at all. This hydrator
|
|
12
|
-
* removes that guesswork: it assembles every surface into one context object
|
|
13
|
-
* and **verifies every surface-map path against the base ref (`main`)**,
|
|
14
|
-
* marking any path absent on `main` as *unverified* rather than trusting it.
|
|
15
|
-
*
|
|
16
|
-
* ## No-network seam
|
|
17
|
-
*
|
|
18
|
-
* Every GitHub and git access flows through an **injected port** so the unit
|
|
19
|
-
* test runs with no network and no real repository:
|
|
20
|
-
* - {@link GithubPort} — `fetchIssue(number) → { number, body, labels }`.
|
|
21
|
-
* - {@link GitPort} — `existsOnRef(path, ref) → boolean` and
|
|
22
|
-
* `recentLog({ maxCount }) → LogEntry[]`.
|
|
23
|
-
* - `fsImpl` — only used to enumerate `.feature` files; defaults
|
|
24
|
-
* to `node:fs`.
|
|
25
|
-
*
|
|
26
|
-
* The hydrator never reaches the network itself; a caller in production wires
|
|
27
|
-
* ports backed by `gh` / `git`, while a test wires in-memory fakes. This
|
|
28
|
-
* mirrors the injected-seam style already used across `lib/qa/` (see
|
|
29
|
-
* `qa-session.js`'s `fsImpl` and `redact-evidence.js`).
|
|
30
|
-
*/
|
|
31
|
-
|
|
32
|
-
import fs from 'node:fs';
|
|
33
|
-
import path from 'node:path';
|
|
34
|
-
|
|
35
|
-
/** The base ref every surface-map path is verified against. */
|
|
36
|
-
export const DEFAULT_BASE_REF = 'main';
|
|
37
|
-
|
|
38
|
-
/** How many recent commits the hydrator pulls into the context object. */
|
|
39
|
-
export const DEFAULT_LOG_MAX_COUNT = 20;
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* @typedef {object} GithubPort
|
|
43
|
-
* @property {(issueNumber: number) => Promise<{
|
|
44
|
-
* number: number,
|
|
45
|
-
* body: string,
|
|
46
|
-
* labels?: string[],
|
|
47
|
-
* }>} fetchIssue Fetch one issue's body + labels.
|
|
48
|
-
*/
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* @typedef {object} GitPort
|
|
52
|
-
* @property {(filePath: string, ref: string) => boolean | Promise<boolean>}
|
|
53
|
-
* existsOnRef True when `filePath` is tracked on `ref` (e.g. `main`).
|
|
54
|
-
* @property {(opts: { maxCount: number }) =>
|
|
55
|
-
* Array<{ sha: string, subject: string }>
|
|
56
|
-
* | Promise<Array<{ sha: string, subject: string }>>}
|
|
57
|
-
* recentLog Recent commit log, newest first.
|
|
58
|
-
*/
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* @typedef {object} SurfaceMapEntry
|
|
62
|
-
* @property {string} path Repo-relative implementation file path.
|
|
63
|
-
* @property {string} [note] Free-form provenance note (e.g. a code comment).
|
|
64
|
-
*/
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Enumerate the `.feature` files under `featureRoot`, returning repo-relative
|
|
68
|
-
* POSIX-style paths sorted for determinism. A missing root yields an empty
|
|
69
|
-
* array (a project that has not authored features is not an error).
|
|
70
|
-
*
|
|
71
|
-
* @param {string | undefined} featureRoot
|
|
72
|
-
* @param {{ fsImpl?: typeof fs }} [opts]
|
|
73
|
-
* @returns {string[]}
|
|
74
|
-
*/
|
|
75
|
-
export function collectFeatureFiles(featureRoot, opts = {}) {
|
|
76
|
-
const fsImpl = opts.fsImpl ?? fs;
|
|
77
|
-
if (!featureRoot || !fsImpl.existsSync(featureRoot)) return [];
|
|
78
|
-
|
|
79
|
-
const found = [];
|
|
80
|
-
const walk = (dir) => {
|
|
81
|
-
const entries = fsImpl.readdirSync(dir, { withFileTypes: true });
|
|
82
|
-
for (const entry of entries) {
|
|
83
|
-
const full = path.join(dir, entry.name);
|
|
84
|
-
if (entry.isDirectory()) {
|
|
85
|
-
walk(full);
|
|
86
|
-
} else if (entry.isFile() && entry.name.endsWith('.feature')) {
|
|
87
|
-
found.push(full.split(path.sep).join('/'));
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
walk(featureRoot);
|
|
92
|
-
return found.sort();
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Verify each surface-map entry against the base ref. An entry whose path is
|
|
97
|
-
* tracked on `baseRef` is `verified: true`; an entry absent on the base ref is
|
|
98
|
-
* `verified: false` — the surface map (or the code comment it came from) named
|
|
99
|
-
* a path that does not exist on `main`, so the front-end must treat it as
|
|
100
|
-
* unverified rather than trusting it.
|
|
101
|
-
*
|
|
102
|
-
* @param {SurfaceMapEntry[]} surfaceMap
|
|
103
|
-
* @param {GitPort} gitPort
|
|
104
|
-
* @param {string} baseRef
|
|
105
|
-
* @returns {Promise<Array<{
|
|
106
|
-
* path: string,
|
|
107
|
-
* note: string | null,
|
|
108
|
-
* verified: boolean,
|
|
109
|
-
* }>>}
|
|
110
|
-
*/
|
|
111
|
-
export async function verifySurfaceMap(surfaceMap, gitPort, baseRef) {
|
|
112
|
-
const entries = Array.isArray(surfaceMap) ? surfaceMap : [];
|
|
113
|
-
const verified = [];
|
|
114
|
-
for (const entry of entries) {
|
|
115
|
-
const filePath = typeof entry === 'string' ? entry : entry?.path;
|
|
116
|
-
if (!filePath) continue;
|
|
117
|
-
const exists = await gitPort.existsOnRef(filePath, baseRef);
|
|
118
|
-
verified.push({
|
|
119
|
-
path: filePath,
|
|
120
|
-
note: typeof entry === 'object' && entry?.note ? entry.note : null,
|
|
121
|
-
verified: Boolean(exists),
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
return verified;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Hydrate the QA context object for an Epic.
|
|
129
|
-
*
|
|
130
|
-
* Assembles, in one object:
|
|
131
|
-
* - `epic` — the Epic's `{ number, body, labels }`. The body is
|
|
132
|
-
* the single planning document (ideation sections +
|
|
133
|
-
* folded Tech Spec sections + Acceptance Table).
|
|
134
|
-
* - `featureFiles` — repo-relative paths of the project's `.feature` files.
|
|
135
|
-
* - `implementation` — the verified surface map (each entry carries
|
|
136
|
-
* `verified` against the base ref).
|
|
137
|
-
* - `gitLog` — recent commits, newest first.
|
|
138
|
-
* - `baseRef` — the ref every path was verified against.
|
|
139
|
-
* - `unverifiedPaths` — the subset of surface-map paths absent on `baseRef`.
|
|
140
|
-
*
|
|
141
|
-
* Every GitHub and git access flows through the injected ports, so this runs
|
|
142
|
-
* with no network when the ports are fakes.
|
|
143
|
-
*
|
|
144
|
-
* @param {{
|
|
145
|
-
* ticketNumber: number,
|
|
146
|
-
* githubPort: GithubPort,
|
|
147
|
-
* gitPort: GitPort,
|
|
148
|
-
* surfaceMap?: SurfaceMapEntry[],
|
|
149
|
-
* featureRoot?: string,
|
|
150
|
-
* baseRef?: string,
|
|
151
|
-
* logMaxCount?: number,
|
|
152
|
-
* fsImpl?: typeof fs,
|
|
153
|
-
* }} opts
|
|
154
|
-
* @returns {Promise<{
|
|
155
|
-
* epic: { number: number, body: string, labels: string[] },
|
|
156
|
-
* featureFiles: string[],
|
|
157
|
-
* implementation: Array<{ path: string, note: string | null, verified: boolean }>,
|
|
158
|
-
* gitLog: Array<{ sha: string, subject: string }>,
|
|
159
|
-
* baseRef: string,
|
|
160
|
-
* unverifiedPaths: string[],
|
|
161
|
-
* }>}
|
|
162
|
-
*/
|
|
163
|
-
export async function hydrateQaContext(opts) {
|
|
164
|
-
const {
|
|
165
|
-
ticketNumber,
|
|
166
|
-
githubPort,
|
|
167
|
-
gitPort,
|
|
168
|
-
surfaceMap = [],
|
|
169
|
-
featureRoot,
|
|
170
|
-
baseRef = DEFAULT_BASE_REF,
|
|
171
|
-
logMaxCount = DEFAULT_LOG_MAX_COUNT,
|
|
172
|
-
fsImpl,
|
|
173
|
-
} = opts ?? {};
|
|
174
|
-
|
|
175
|
-
if (!Number.isInteger(ticketNumber)) {
|
|
176
|
-
throw new Error(
|
|
177
|
-
'hydrateQaContext: `ticketNumber` is required and must be an integer',
|
|
178
|
-
);
|
|
179
|
-
}
|
|
180
|
-
if (!githubPort || typeof githubPort.fetchIssue !== 'function') {
|
|
181
|
-
throw new Error(
|
|
182
|
-
'hydrateQaContext: `githubPort.fetchIssue` is required (inject a port)',
|
|
183
|
-
);
|
|
184
|
-
}
|
|
185
|
-
if (
|
|
186
|
-
!gitPort ||
|
|
187
|
-
typeof gitPort.existsOnRef !== 'function' ||
|
|
188
|
-
typeof gitPort.recentLog !== 'function'
|
|
189
|
-
) {
|
|
190
|
-
throw new Error(
|
|
191
|
-
'hydrateQaContext: `gitPort` must expose `existsOnRef` and `recentLog`',
|
|
192
|
-
);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
const epicIssue = await githubPort.fetchIssue(ticketNumber);
|
|
196
|
-
const epic = {
|
|
197
|
-
number: epicIssue.number,
|
|
198
|
-
body: epicIssue.body ?? '',
|
|
199
|
-
labels: Array.isArray(epicIssue.labels) ? [...epicIssue.labels] : [],
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
const featureFiles = collectFeatureFiles(featureRoot, { fsImpl });
|
|
203
|
-
const implementation = await verifySurfaceMap(surfaceMap, gitPort, baseRef);
|
|
204
|
-
const gitLog = await gitPort.recentLog({ maxCount: logMaxCount });
|
|
205
|
-
const unverifiedPaths = implementation
|
|
206
|
-
.filter((entry) => !entry.verified)
|
|
207
|
-
.map((entry) => entry.path);
|
|
208
|
-
|
|
209
|
-
return {
|
|
210
|
-
epic,
|
|
211
|
-
featureFiles,
|
|
212
|
-
implementation,
|
|
213
|
-
gitLog: Array.isArray(gitLog) ? gitLog : [],
|
|
214
|
-
baseRef,
|
|
215
|
-
unverifiedPaths,
|
|
216
|
-
};
|
|
217
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Detectors barrel (Epic #1721 / Story #1771 / Task #1774).
|
|
3
|
-
*
|
|
4
|
-
* Single import surface for every signal detector. Detector Stories
|
|
5
|
-
* (rework in #1771, retry in #1768) re-export from here so callers
|
|
6
|
-
* (`lib/orchestration/detectors-phase.js`) only ever import from one
|
|
7
|
-
* place. The Epic #1769 hotspot detector was retired in the Epic #4406
|
|
8
|
-
* signal-contract cutover (no live emitter, no consumer).
|
|
9
|
-
*
|
|
10
|
-
* @module lib/signals/detectors
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
export { detectRetry } from './retry.js';
|
|
14
|
-
export { detectRework } from './rework.js';
|