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
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* GitHub Provider — SubIssueGateway.
|
|
3
3
|
*
|
|
4
|
-
* Owns the native GitHub Sub-Issues
|
|
5
|
-
* field on an Issue,
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Owns the **read** side of the native GitHub Sub-Issues surface: the
|
|
5
|
+
* GraphQL `subIssues` field on an Issue, paginated into a child-number
|
|
6
|
+
* list. Story #5008 removed the write side (`addSubIssue`,
|
|
7
|
+
* `removeSubIssue`) and the `reconcileSubIssueLinks` walker along with the
|
|
8
|
+
* rest of the Epic-hierarchy provider surface — v2 delivery is Story-only,
|
|
9
|
+
* so nothing establishes or repairs parent/child links any more.
|
|
8
10
|
*
|
|
9
|
-
* Extracted from `../github.js` in Story #2462 / Task #2480.
|
|
10
|
-
*
|
|
11
|
-
* `
|
|
11
|
+
* Extracted from `../github.js` in Story #2462 / Task #2480. The read path
|
|
12
|
+
* still reaches `GitHubProvider` as `_getNativeSubIssues`, and
|
|
13
|
+
* `IssuesGateway` calls it directly through its `getNativeSubIssues` hook.
|
|
12
14
|
*
|
|
13
|
-
* Constructed with `{ ghGraphql,
|
|
15
|
+
* Constructed with `{ ghGraphql, cache, classifyGithubError }`:
|
|
14
16
|
* - `ghGraphql(query, variables, opts)` — bound `_ghGraphql` from the parent.
|
|
15
|
-
* - `hooks.getTicket(id, opts)` — bound to the TicketGateway.
|
|
16
|
-
* - `hooks.getTickets(parentId)` — bound to the TicketGateway (for the reconciler).
|
|
17
|
-
* - `hooks.primeTicketCache(tickets)` — bound to the TicketGateway (reconciler priming).
|
|
18
|
-
* - `hooks.invalidateTicket(id)` — bound to the TicketGateway (post-mutation invalidate).
|
|
19
17
|
* - `cache.primeIfAbsent(ticket)` — bound to the shared cache (native-walk priming).
|
|
20
18
|
*
|
|
21
19
|
* The gateway holds **no** transport state of its own — every call goes
|
|
@@ -26,25 +24,14 @@
|
|
|
26
24
|
*/
|
|
27
25
|
|
|
28
26
|
import { Logger } from '../../lib/Logger.js';
|
|
29
|
-
import { concurrentMap } from '../../lib/util/concurrent-map.js';
|
|
30
27
|
import {
|
|
31
|
-
ADD_SUB_ISSUE_MUTATION,
|
|
32
28
|
classifyGithubError as defaultClassifyGithubError,
|
|
33
|
-
REMOVE_SUB_ISSUE_MUTATION,
|
|
34
29
|
SUB_ISSUES_QUERY,
|
|
35
30
|
withTransientRetry,
|
|
36
31
|
} from './errors.js';
|
|
37
32
|
import { subIssueNodeToTicket } from './mappers.js';
|
|
38
33
|
import { defaultRetryWarn } from './request-helpers.js';
|
|
39
34
|
|
|
40
|
-
// Concurrency + retry budgets — preserved from `../github.js` so dispatch
|
|
41
|
-
// fan-out and sub-issue reconciliation keep their established shapes.
|
|
42
|
-
const SUB_ISSUE_RECONCILE_CONCURRENCY = 4;
|
|
43
|
-
const SUB_ISSUE_RETRY_MAX_ATTEMPTS = 6;
|
|
44
|
-
const SUB_ISSUE_RETRY_BASE_DELAY_MS = 1000;
|
|
45
|
-
const SUB_ISSUE_RETRY_MAX_DELAY_MS = 30000;
|
|
46
|
-
const SUB_ISSUE_RETRY_JITTER_MS = 500;
|
|
47
|
-
|
|
48
35
|
// Story #2852: cap the native sub-issue cursor walk so a runaway pagination
|
|
49
36
|
// (e.g. an upstream API regression that never sets `hasNextPage = false`)
|
|
50
37
|
// fails fast instead of looping forever. 50 cursor pages × 100 nodes per
|
|
@@ -55,24 +42,16 @@ export class SubIssueGateway {
|
|
|
55
42
|
/**
|
|
56
43
|
* @param {{
|
|
57
44
|
* ghGraphql: (query: string, variables?: object, opts?: object) => Promise<object>,
|
|
58
|
-
* hooks?: {
|
|
59
|
-
* getTicket?: (id: number, opts?: object) => Promise<object>,
|
|
60
|
-
* getTickets?: (parentId: number) => Promise<object[]>,
|
|
61
|
-
* primeTicketCache?: (tickets: object[]) => void,
|
|
62
|
-
* invalidateTicket?: (id: number) => void,
|
|
63
|
-
* },
|
|
64
45
|
* cache?: { primeIfAbsent: (ticket: object) => void },
|
|
65
46
|
* classifyGithubError?: (err: unknown) => string,
|
|
66
47
|
* }} deps
|
|
67
48
|
*/
|
|
68
49
|
constructor({
|
|
69
50
|
ghGraphql,
|
|
70
|
-
hooks = {},
|
|
71
51
|
cache,
|
|
72
52
|
classifyGithubError = defaultClassifyGithubError,
|
|
73
53
|
} = {}) {
|
|
74
54
|
this._ghGraphql = ghGraphql;
|
|
75
|
-
this._hooks = hooks;
|
|
76
55
|
this._cache = cache;
|
|
77
56
|
this._classify = classifyGithubError;
|
|
78
57
|
}
|
|
@@ -132,191 +111,4 @@ export class SubIssueGateway {
|
|
|
132
111
|
}
|
|
133
112
|
return childIds;
|
|
134
113
|
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Establish the native sub-issue link between `parentNumber` and the
|
|
138
|
-
* child identified by `childNodeId`. Retries on transient errors with
|
|
139
|
-
* jittered exponential backoff before re-throwing.
|
|
140
|
-
*/
|
|
141
|
-
async addSubIssue(
|
|
142
|
-
parentNumber,
|
|
143
|
-
childNodeId,
|
|
144
|
-
opts = { replaceParent: false },
|
|
145
|
-
) {
|
|
146
|
-
const getTicket = this._hooks.getTicket;
|
|
147
|
-
if (typeof getTicket !== 'function') {
|
|
148
|
-
throw new Error(
|
|
149
|
-
'[SubIssueGateway] addSubIssue requires a getTicket hook',
|
|
150
|
-
);
|
|
151
|
-
}
|
|
152
|
-
const parentTicket = await getTicket(parentNumber);
|
|
153
|
-
let lastErr;
|
|
154
|
-
for (let attempt = 0; attempt < SUB_ISSUE_RETRY_MAX_ATTEMPTS; attempt++) {
|
|
155
|
-
try {
|
|
156
|
-
const result = await this._ghGraphql(
|
|
157
|
-
ADD_SUB_ISSUE_MUTATION,
|
|
158
|
-
{
|
|
159
|
-
parentId: parentTicket.nodeId,
|
|
160
|
-
subIssueId: childNodeId,
|
|
161
|
-
replaceParent: opts.replaceParent,
|
|
162
|
-
},
|
|
163
|
-
{ headers: { 'GraphQL-Features': 'sub_issues' } },
|
|
164
|
-
);
|
|
165
|
-
// Sub-issue link mutates the parent's sub-issue list. Invalidate so
|
|
166
|
-
// the next `getTicket(parentNumber)` re-fetches a coherent view.
|
|
167
|
-
if (typeof this._hooks.invalidateTicket === 'function') {
|
|
168
|
-
this._hooks.invalidateTicket(parentNumber);
|
|
169
|
-
}
|
|
170
|
-
return result;
|
|
171
|
-
} catch (err) {
|
|
172
|
-
lastErr = err;
|
|
173
|
-
const category = this._classify(err);
|
|
174
|
-
const isFinalAttempt = attempt === SUB_ISSUE_RETRY_MAX_ATTEMPTS - 1;
|
|
175
|
-
if (category !== 'transient' || isFinalAttempt) throw err;
|
|
176
|
-
const base = Math.min(
|
|
177
|
-
SUB_ISSUE_RETRY_MAX_DELAY_MS,
|
|
178
|
-
SUB_ISSUE_RETRY_BASE_DELAY_MS * 2 ** attempt,
|
|
179
|
-
);
|
|
180
|
-
const delay =
|
|
181
|
-
base + Math.floor(Math.random() * SUB_ISSUE_RETRY_JITTER_MS);
|
|
182
|
-
Logger.warn(
|
|
183
|
-
`[GitHubProvider] sub-issue link transient error for parent #${parentNumber} (attempt ${attempt + 1}/${SUB_ISSUE_RETRY_MAX_ATTEMPTS}); retrying in ${delay}ms: ${err.message}`,
|
|
184
|
-
);
|
|
185
|
-
await new Promise((r) => setTimeout(r, delay));
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
throw lastErr;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
async removeSubIssue(parentNumber, subIssueNumber) {
|
|
192
|
-
const getTicket = this._hooks.getTicket;
|
|
193
|
-
if (typeof getTicket !== 'function') {
|
|
194
|
-
throw new Error(
|
|
195
|
-
'[SubIssueGateway] removeSubIssue requires a getTicket hook',
|
|
196
|
-
);
|
|
197
|
-
}
|
|
198
|
-
const parentTicket = await getTicket(parentNumber);
|
|
199
|
-
const childTicket = await getTicket(subIssueNumber);
|
|
200
|
-
const result = await this._ghGraphql(
|
|
201
|
-
REMOVE_SUB_ISSUE_MUTATION,
|
|
202
|
-
{ parentId: parentTicket.nodeId, subIssueId: childTicket.nodeId },
|
|
203
|
-
{ headers: { 'GraphQL-Features': 'sub_issues' } },
|
|
204
|
-
);
|
|
205
|
-
if (typeof this._hooks.invalidateTicket === 'function') {
|
|
206
|
-
this._hooks.invalidateTicket(parentNumber);
|
|
207
|
-
this._hooks.invalidateTicket(subIssueNumber);
|
|
208
|
-
}
|
|
209
|
-
return result;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* Walk every child of `epicId` whose body footer carries `parent: #N` and
|
|
214
|
-
* verify the native sub-issue link is present. Re-establish missing links
|
|
215
|
-
* via `addSubIssue` (which retries internally). Idempotent.
|
|
216
|
-
*/
|
|
217
|
-
async reconcileSubIssueLinks(epicId) {
|
|
218
|
-
const PARENT_RE = /(?:^|\n)parent:\s*#(\d+)/;
|
|
219
|
-
const getTicket = this._hooks.getTicket;
|
|
220
|
-
const getTickets = this._hooks.getTickets;
|
|
221
|
-
if (typeof getTicket !== 'function' || typeof getTickets !== 'function') {
|
|
222
|
-
throw new Error(
|
|
223
|
-
'[SubIssueGateway] reconcileSubIssueLinks requires getTicket + getTickets hooks',
|
|
224
|
-
);
|
|
225
|
-
}
|
|
226
|
-
const allChildren = await getTickets(epicId);
|
|
227
|
-
const parentByChild = new Map();
|
|
228
|
-
for (const child of allChildren) {
|
|
229
|
-
const match = (child.body ?? '').match(PARENT_RE);
|
|
230
|
-
if (!match) continue;
|
|
231
|
-
parentByChild.set(child.id, Number.parseInt(match[1], 10));
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
const childrenByParent = new Map();
|
|
235
|
-
for (const [childId, parentId] of parentByChild) {
|
|
236
|
-
if (!childrenByParent.has(parentId)) childrenByParent.set(parentId, []);
|
|
237
|
-
childrenByParent.get(parentId).push(childId);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
let alreadyLinked = 0;
|
|
241
|
-
let reconciled = 0;
|
|
242
|
-
let failed = 0;
|
|
243
|
-
const failures = [];
|
|
244
|
-
|
|
245
|
-
const parentEntries = Array.from(childrenByParent.entries());
|
|
246
|
-
|
|
247
|
-
await concurrentMap(
|
|
248
|
-
parentEntries,
|
|
249
|
-
async ([parentId, childIds]) => {
|
|
250
|
-
let parent;
|
|
251
|
-
try {
|
|
252
|
-
parent = await getTicket(parentId);
|
|
253
|
-
} catch (err) {
|
|
254
|
-
for (const childId of childIds) {
|
|
255
|
-
failures.push({ parentId, childId, reason: err.message });
|
|
256
|
-
}
|
|
257
|
-
failed += childIds.length;
|
|
258
|
-
return;
|
|
259
|
-
}
|
|
260
|
-
if (!parent?.nodeId) {
|
|
261
|
-
for (const childId of childIds) {
|
|
262
|
-
failures.push({
|
|
263
|
-
parentId,
|
|
264
|
-
childId,
|
|
265
|
-
reason: 'parent missing nodeId',
|
|
266
|
-
});
|
|
267
|
-
}
|
|
268
|
-
failed += childIds.length;
|
|
269
|
-
return;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
const linked = new Set(
|
|
273
|
-
await this.getNativeSubIssues(parent.nodeId, parentId),
|
|
274
|
-
);
|
|
275
|
-
|
|
276
|
-
await concurrentMap(
|
|
277
|
-
childIds,
|
|
278
|
-
async (childId) => {
|
|
279
|
-
if (linked.has(childId)) {
|
|
280
|
-
alreadyLinked++;
|
|
281
|
-
return;
|
|
282
|
-
}
|
|
283
|
-
let childTicket;
|
|
284
|
-
try {
|
|
285
|
-
childTicket = await getTicket(childId);
|
|
286
|
-
} catch (err) {
|
|
287
|
-
failures.push({ parentId, childId, reason: err.message });
|
|
288
|
-
failed++;
|
|
289
|
-
return;
|
|
290
|
-
}
|
|
291
|
-
if (!childTicket?.nodeId) {
|
|
292
|
-
failures.push({
|
|
293
|
-
parentId,
|
|
294
|
-
childId,
|
|
295
|
-
reason: 'child missing nodeId',
|
|
296
|
-
});
|
|
297
|
-
failed++;
|
|
298
|
-
return;
|
|
299
|
-
}
|
|
300
|
-
try {
|
|
301
|
-
await this.addSubIssue(parentId, childTicket.nodeId);
|
|
302
|
-
reconciled++;
|
|
303
|
-
} catch (err) {
|
|
304
|
-
failures.push({ parentId, childId, reason: err.message });
|
|
305
|
-
failed++;
|
|
306
|
-
}
|
|
307
|
-
},
|
|
308
|
-
{ concurrency: SUB_ISSUE_RECONCILE_CONCURRENCY },
|
|
309
|
-
);
|
|
310
|
-
},
|
|
311
|
-
{ concurrency: SUB_ISSUE_RECONCILE_CONCURRENCY },
|
|
312
|
-
);
|
|
313
|
-
|
|
314
|
-
return {
|
|
315
|
-
totalExpected: parentByChild.size,
|
|
316
|
-
alreadyLinked,
|
|
317
|
-
reconciled,
|
|
318
|
-
failed,
|
|
319
|
-
failures,
|
|
320
|
-
};
|
|
321
|
-
}
|
|
322
114
|
}
|
|
@@ -19,8 +19,10 @@
|
|
|
19
19
|
* module.
|
|
20
20
|
*
|
|
21
21
|
* Gateway map: tickets, sub-issues, comments, labels, branch-protection,
|
|
22
|
-
* merge-methods,
|
|
23
|
-
*
|
|
22
|
+
* merge-methods, project-board, issues (epics + sub-tickets + branch probe
|
|
23
|
+
* + raw GraphQL). Story #5008 retired the `prs` gateway — `single-story-close`
|
|
24
|
+
* drives `gh pr create` directly — along with the sub-issue write surface and
|
|
25
|
+
* the repo-wide recent-comments feed.
|
|
24
26
|
*/
|
|
25
27
|
|
|
26
28
|
import { createGh, gh as defaultGh } from '../lib/gh-exec.js';
|
|
@@ -125,16 +127,11 @@ const DELEGATIONS = [
|
|
|
125
127
|
['updateTicket', 'tickets.updateTicket'],
|
|
126
128
|
['_applyLabelMutations', 'tickets._applyLabelMutations'],
|
|
127
129
|
['_getNativeSubIssues', 'subIssues.getNativeSubIssues'],
|
|
128
|
-
['addSubIssue', 'subIssues.addSubIssue'],
|
|
129
|
-
['removeSubIssue', 'subIssues.removeSubIssue'],
|
|
130
|
-
['reconcileSubIssueLinks', 'subIssues.reconcileSubIssueLinks'],
|
|
131
|
-
['getRecentComments', 'comments.getRecentComments'],
|
|
132
130
|
['getTicketComments', 'comments.getTicketComments'],
|
|
133
131
|
['deleteComment', 'comments.deleteComment'],
|
|
134
132
|
['postComment', 'comments.postComment'],
|
|
135
133
|
['getBranchProtection', 'branchProtection.getBranchProtection'],
|
|
136
134
|
['setBranchProtection', 'branchProtection.setBranchProtection'],
|
|
137
|
-
['createPullRequest', 'pullRequests.createPullRequest'],
|
|
138
135
|
['ensureLabels', 'labels.ensureLabels'],
|
|
139
136
|
['_reconcileLabelsPresence', 'labels._reconcileLabelsPresence'],
|
|
140
137
|
['getMergeMethods', 'mergeMethods.getMergeMethods'],
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// .agents/scripts/prune-baseline-orphans.js — Story #5012.
|
|
4
|
+
//
|
|
5
|
+
// Delete the baseline rows that are provably inert, and nothing else.
|
|
6
|
+
//
|
|
7
|
+
// This is the cheap remedy that makes `check-baseline-scope.js` fair. A gate
|
|
8
|
+
// that hard-fails on a stale row is only defensible while clearing one costs a
|
|
9
|
+
// command rather than a full re-score: the honest alternative — re-derive the
|
|
10
|
+
// whole baseline — spends a coverage run or a full-tree MI pass to express a
|
|
11
|
+
// deletion, which is why stale rows accumulate instead.
|
|
12
|
+
//
|
|
13
|
+
// Two row classes qualify, both decidable without measuring anything:
|
|
14
|
+
//
|
|
15
|
+
// - `absent` — the row's file is gone from disk.
|
|
16
|
+
// - `out-of-scope` — the file is still there, but the gate's own
|
|
17
|
+
// `targetDirs` / `ignoreGlobs` no longer match it.
|
|
18
|
+
//
|
|
19
|
+
// The pruner never adds a row, never restamps `generatedAt` (a fresh stamp
|
|
20
|
+
// over unmeasured rows is the exact failure an age check exists to catch), and
|
|
21
|
+
// recomputes `rollup` through the kind's own arithmetic so the pruned envelope
|
|
22
|
+
// still validates against its schema. An unreadable scope config degrades to
|
|
23
|
+
// orphan-only pruning rather than treating unknown scope as empty scope.
|
|
24
|
+
//
|
|
25
|
+
// Exit codes:
|
|
26
|
+
// 0 nothing to prune (or, without `--check`, the prune was written)
|
|
27
|
+
// 1 `--check` found rows that would be pruned
|
|
28
|
+
// 2 the pruner could not run
|
|
29
|
+
|
|
30
|
+
// Fail-fast if the framework's runtime deps are not installed — must be the
|
|
31
|
+
// first import so the check runs before any third-party-importing sibling
|
|
32
|
+
// module is evaluated (Story #3432).
|
|
33
|
+
import './lib/runtime-deps/ensure-installed.js';
|
|
34
|
+
import { PRUNABLE_KINDS, runPrune } from './lib/baselines/orphan-pruner.js';
|
|
35
|
+
import { runAsCli } from './lib/cli-utils.js';
|
|
36
|
+
import { getQuality } from './lib/config/quality.js';
|
|
37
|
+
import { resolveConfig } from './lib/config-resolver.js';
|
|
38
|
+
|
|
39
|
+
const EXIT_CLEAN = 0;
|
|
40
|
+
const EXIT_WOULD_PRUNE = 1;
|
|
41
|
+
const EXIT_CANNOT_RUN = 2;
|
|
42
|
+
|
|
43
|
+
const HELP = {
|
|
44
|
+
invocation:
|
|
45
|
+
'node .agents/scripts/prune-baseline-orphans.js [--check] [--kind <kind>] [--json]',
|
|
46
|
+
summary:
|
|
47
|
+
"Remove baseline rows whose file is gone from disk or has left the gate's scope. Never adds a row, never restamps generatedAt, never re-scores.",
|
|
48
|
+
flags: [
|
|
49
|
+
['--check', 'Report what would be pruned, write nothing, exit 1 if any.'],
|
|
50
|
+
['--kind <kind>', 'Prune one kind only (repeatable). Default: all.'],
|
|
51
|
+
['--json', 'Emit the report as JSON instead of text.'],
|
|
52
|
+
['--cwd <dir>', 'Repository root to prune. Default: process.cwd().'],
|
|
53
|
+
],
|
|
54
|
+
notes: [
|
|
55
|
+
'Exit codes:\n 0 clean, or pruned\n 1 --check found prunable rows\n 2 the pruner could not run',
|
|
56
|
+
],
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Parse argv into an options bag. An unknown flag is an error, never a silent
|
|
61
|
+
* no-op — a typo'd `--dry-run` must not read as "write the files".
|
|
62
|
+
*
|
|
63
|
+
* @param {string[]} argv
|
|
64
|
+
* @returns {{ kinds: string[], check: boolean, json: boolean, cwd: string }}
|
|
65
|
+
*/
|
|
66
|
+
export function parseArgs(argv = []) {
|
|
67
|
+
const out = { kinds: [], check: false, json: false, cwd: null };
|
|
68
|
+
let i = 0;
|
|
69
|
+
while (i < argv.length) {
|
|
70
|
+
const arg = argv[i];
|
|
71
|
+
const value = argv[i + 1];
|
|
72
|
+
i += 1;
|
|
73
|
+
if (arg === '--check') out.check = true;
|
|
74
|
+
else if (arg === '--json') out.json = true;
|
|
75
|
+
else if (arg === '--kind') {
|
|
76
|
+
out.kinds.push(value);
|
|
77
|
+
i += 1;
|
|
78
|
+
} else if (arg === '--cwd') {
|
|
79
|
+
out.cwd = value;
|
|
80
|
+
i += 1;
|
|
81
|
+
} else throw new Error(`unknown flag "${arg}" (try --help)`);
|
|
82
|
+
}
|
|
83
|
+
const unknown = out.kinds.filter((k) => !PRUNABLE_KINDS.includes(k));
|
|
84
|
+
if (unknown.length > 0) {
|
|
85
|
+
throw new Error(
|
|
86
|
+
`unknown --kind ${unknown.join(', ')}; expected one of ${PRUNABLE_KINDS.join(', ')}`,
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
if (out.kinds.length === 0) out.kinds = [...PRUNABLE_KINDS];
|
|
90
|
+
out.cwd = out.cwd ?? process.cwd();
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Render one kind's outcome as indented text lines.
|
|
96
|
+
*
|
|
97
|
+
* @param {object} entry
|
|
98
|
+
* @param {boolean} check
|
|
99
|
+
* @returns {string[]}
|
|
100
|
+
*/
|
|
101
|
+
function renderKind(entry, check) {
|
|
102
|
+
if (!entry.present) return [];
|
|
103
|
+
if (entry.skipped)
|
|
104
|
+
return [` - ${entry.kind}: skipped (${entry.skipReason})`];
|
|
105
|
+
const suffix = entry.degraded
|
|
106
|
+
? ` [orphan-only: ${entry.degradedReason}]`
|
|
107
|
+
: '';
|
|
108
|
+
if (entry.removed.length === 0) {
|
|
109
|
+
return [` - ${entry.kind}: clean${suffix}`];
|
|
110
|
+
}
|
|
111
|
+
const verb = check ? 'would prune' : 'pruned';
|
|
112
|
+
return [
|
|
113
|
+
` - ${entry.kind}: ${verb} ${entry.removed.length} row(s)${suffix}`,
|
|
114
|
+
...entry.removed.map((row) => ` · ${row.reason}: ${row.path}`),
|
|
115
|
+
];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Render the whole report as text.
|
|
120
|
+
*
|
|
121
|
+
* @param {object} report
|
|
122
|
+
* @returns {string}
|
|
123
|
+
*/
|
|
124
|
+
export function formatReport(report) {
|
|
125
|
+
const verb = report.check ? 'would prune' : 'pruned';
|
|
126
|
+
const lines = [
|
|
127
|
+
`[prune-baseline-orphans] ${verb} ${report.removedCount} row(s) across ` +
|
|
128
|
+
`${report.writtenCount} file(s)`,
|
|
129
|
+
...report.kinds.flatMap((entry) => renderKind(entry, report.check)),
|
|
130
|
+
];
|
|
131
|
+
if (report.check && report.removedCount > 0) {
|
|
132
|
+
lines.push(
|
|
133
|
+
'',
|
|
134
|
+
'Run without --check to write the prune:',
|
|
135
|
+
' node .agents/scripts/prune-baseline-orphans.js',
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
return lines.join('\n');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* CLI entry point. Returns its exit code rather than calling `process.exit()`,
|
|
143
|
+
* so `runAsCli`'s `propagateExitCode` path settles it through `flushStdio` and
|
|
144
|
+
* an unbounded report is not truncated at a pipe boundary (Story #4783).
|
|
145
|
+
*
|
|
146
|
+
* @returns {Promise<number>}
|
|
147
|
+
*/
|
|
148
|
+
async function main() {
|
|
149
|
+
let opts;
|
|
150
|
+
let report;
|
|
151
|
+
try {
|
|
152
|
+
opts = parseArgs(process.argv.slice(2));
|
|
153
|
+
const config = resolveConfig({ cwd: opts.cwd });
|
|
154
|
+
report = runPrune({
|
|
155
|
+
cwd: opts.cwd,
|
|
156
|
+
kinds: opts.kinds,
|
|
157
|
+
check: opts.check,
|
|
158
|
+
quality: getQuality(config) ?? { gates: {} },
|
|
159
|
+
});
|
|
160
|
+
} catch (err) {
|
|
161
|
+
const message = err?.message ?? String(err);
|
|
162
|
+
process.stdout.write(
|
|
163
|
+
`${JSON.stringify({ schemaVersion: '1', error: message }, null, 2)}\n`,
|
|
164
|
+
);
|
|
165
|
+
return EXIT_CANNOT_RUN;
|
|
166
|
+
}
|
|
167
|
+
process.stdout.write(
|
|
168
|
+
opts.json
|
|
169
|
+
? `${JSON.stringify(report, null, 2)}\n`
|
|
170
|
+
: `${formatReport(report)}\n`,
|
|
171
|
+
);
|
|
172
|
+
return report.check && report.removedCount > 0
|
|
173
|
+
? EXIT_WOULD_PRUNE
|
|
174
|
+
: EXIT_CLEAN;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
runAsCli(import.meta.url, main, {
|
|
178
|
+
source: 'prune-baseline-orphans',
|
|
179
|
+
usage: HELP,
|
|
180
|
+
propagateExitCode: true,
|
|
181
|
+
});
|
|
@@ -197,7 +197,6 @@ export async function runResolveStories(
|
|
|
197
197
|
stories,
|
|
198
198
|
nativeEdges,
|
|
199
199
|
warn: (m) => Logger.warn(m),
|
|
200
|
-
config,
|
|
201
200
|
});
|
|
202
201
|
const foreignDone = await resolveForeignDone({
|
|
203
202
|
provider,
|
|
@@ -209,7 +208,6 @@ export async function runResolveStories(
|
|
|
209
208
|
nativeEdges,
|
|
210
209
|
foreignDone,
|
|
211
210
|
warn: () => {},
|
|
212
|
-
config,
|
|
213
211
|
});
|
|
214
212
|
|
|
215
213
|
stdout.write(
|
|
@@ -21,6 +21,32 @@ import process from 'node:process';
|
|
|
21
21
|
// can spawn directly.
|
|
22
22
|
const useShell = process.platform === 'win32';
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* A Node gate spelled `node .agents/scripts/<script>` — the shape six of the
|
|
26
|
+
* eight tasks share. Spelling it once keeps this list readable as what it
|
|
27
|
+
* actually is (a fan-out roster) instead of a wall of spawn tuples, and keeps
|
|
28
|
+
* adding a gate from costing the module a maintainability regression for the
|
|
29
|
+
* boilerplate rather than the behaviour. Mirrors `run-verify.js`'s `gate()`.
|
|
30
|
+
*
|
|
31
|
+
* Use bare `node` (PATH-resolved) rather than `process.execPath`:
|
|
32
|
+
* `process.execPath` on Windows often expands to `C:\Program
|
|
33
|
+
* Files\nodejs\node.exe`, and spawn(..., { shell: true }) does not quote the
|
|
34
|
+
* executable, so the space breaks invocation.
|
|
35
|
+
*
|
|
36
|
+
* The path is passed WHOLE rather than assembled from a basename. Composing
|
|
37
|
+
* it (`` `.agents/scripts/${script}` ``) reads better and is wrong:
|
|
38
|
+
* `check-knip-entries.js` derives which CLIs something actually invokes by
|
|
39
|
+
* scanning this file's source text for literal script paths, and four of the
|
|
40
|
+
* gates below are invoked from nowhere else. Interpolating the path made them
|
|
41
|
+
* invisible to that derivation, so knip called them dead and the ratchet
|
|
42
|
+
* offered to record live gates as expected-dead — the Story #5012 trap.
|
|
43
|
+
*
|
|
44
|
+
* @param {string} name reported in this driver's own error prefix
|
|
45
|
+
* @param {string} script repo-relative path, spelled as one literal
|
|
46
|
+
* @returns {{ name: string, cmd: string, args: string[] }}
|
|
47
|
+
*/
|
|
48
|
+
const nodeGate = (name, script) => ({ name, cmd: 'node', args: [script] });
|
|
49
|
+
|
|
24
50
|
const tasks = [
|
|
25
51
|
{
|
|
26
52
|
name: 'biome',
|
|
@@ -47,67 +73,41 @@ const tasks = [
|
|
|
47
73
|
'!.worktrees/**',
|
|
48
74
|
],
|
|
49
75
|
},
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
// let the original `type/epic` typo land in a since-retired
|
|
86
|
-
// planning workflow helper.
|
|
87
|
-
name: 'label-vocabulary',
|
|
88
|
-
cmd: 'node',
|
|
89
|
-
args: ['.agents/scripts/lint-label-vocabulary.js'],
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
// GitHub Actions job-timeout gate (Story #4936). Enumerates every
|
|
93
|
-
// `jobs.<id>` key across `.github/workflows/*.yml` and fails when one
|
|
94
|
-
// sets no `timeout-minutes` (inheriting GitHub's 360-minute default) or
|
|
95
|
-
// sets one above the ceiling. A deadlocked Windows job burned 44 minutes
|
|
96
|
-
// of a runner and withheld the failing required check's logs for the
|
|
97
|
-
// whole time; nothing but this check would have caught the gap.
|
|
98
|
-
name: 'workflow-timeouts',
|
|
99
|
-
cmd: 'node',
|
|
100
|
-
args: ['.agents/scripts/check-workflow-timeouts.js'],
|
|
101
|
-
},
|
|
102
|
-
{
|
|
103
|
-
// Architecture cycle ratchet (Story #3991). Detects directed import
|
|
104
|
-
// cycles under `.agents/scripts/` and fails on any cycle not in the
|
|
105
|
-
// committed allowlist (`baselines/arch-cycles.json`). Mirrors the
|
|
106
|
-
// ratchet-down semantics of `check-dead-exports.js`.
|
|
107
|
-
name: 'arch-cycles',
|
|
108
|
-
cmd: 'node',
|
|
109
|
-
args: ['.agents/scripts/check-arch-cycles.js'],
|
|
110
|
-
},
|
|
76
|
+
// Lifecycle surface (Story #2227). Enforces (1) no `Promise.all` under
|
|
77
|
+
// `.agents/scripts/lib/orchestration/lifecycle/**` — the ledger is
|
|
78
|
+
// append-only and a parallel write interleaves records; and (2) the
|
|
79
|
+
// auto-merge lockout: the `gh pr merge` literal appears only in
|
|
80
|
+
// `single-story-close/phases/auto-merge.js`. Neither has a biome
|
|
81
|
+
// equivalent. Story #5024 dropped the third rule (the wildcard-observer
|
|
82
|
+
// firewall) with the bus and the `listeners/` directory its predicate
|
|
83
|
+
// required.
|
|
84
|
+
nodeGate('lifecycle-lint', '.agents/scripts/check-lifecycle-lint.js'),
|
|
85
|
+
// Workflow prose surface (Epic #4474 PR5). No workflow may instruct calling
|
|
86
|
+
// an exported library function that has no CLI entrypoint — the measured
|
|
87
|
+
// shim-writing failure mode the /plan collapse killed. See
|
|
88
|
+
// check-workflow-cli-lint.js for the paragraph-level heuristic.
|
|
89
|
+
nodeGate('workflow-cli-lint', '.agents/scripts/check-workflow-cli-lint.js'),
|
|
90
|
+
// Label-vocabulary citations in `.agents/docs/SDLC.md` and
|
|
91
|
+
// `.agents/workflows/**/*.md` (Story #2892). Greps inline backtick code
|
|
92
|
+
// spans for axis-shaped tokens (`type/epic`, etc.) and asserts only the
|
|
93
|
+
// canonical `<axis>::<value>` separator from `lib/label-constants.js`
|
|
94
|
+
// appears. Closes the drift gap that let the original `type/epic` typo land.
|
|
95
|
+
nodeGate('label-vocabulary', '.agents/scripts/lint-label-vocabulary.js'),
|
|
96
|
+
// GitHub Actions job-timeout gate (Story #4936). Fails a `jobs.<id>` that
|
|
97
|
+
// sets no `timeout-minutes` (inheriting GitHub's 360-minute default) or sets
|
|
98
|
+
// one above the ceiling. A deadlocked Windows job burned 44 minutes of a
|
|
99
|
+
// runner and withheld the failing required check's logs for the whole time.
|
|
100
|
+
nodeGate('workflow-timeouts', '.agents/scripts/check-workflow-timeouts.js'),
|
|
101
|
+
// Architecture cycle ratchet (Story #3991). Detects directed import cycles
|
|
102
|
+
// under `.agents/scripts/` and fails on any cycle not in the committed
|
|
103
|
+
// allowlist (`baselines/arch-cycles.json`).
|
|
104
|
+
nodeGate('arch-cycles', '.agents/scripts/check-arch-cycles.js'),
|
|
105
|
+
// Static Gherkin corpus gate (Story #5013): must-compile with the real
|
|
106
|
+
// parser, then must-bind scoped per step root. Opt-in behind
|
|
107
|
+
// `qa.gherkinLint`, so it exits 0 as "not configured" in this repo. Here
|
|
108
|
+
// rather than in run-verify.js so it reaches CI through the same `lint`
|
|
109
|
+
// required check as arch-cycles; listing it in both would double-pay it.
|
|
110
|
+
nodeGate('gherkin-corpus', '.agents/scripts/check-gherkin-corpus.js'),
|
|
111
111
|
];
|
|
112
112
|
|
|
113
113
|
function runTask({ name, cmd, args }) {
|