mandrel 2.31.0 → 2.33.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 +12 -9
- package/.agents/docs/agentrc-reference.json +63 -58
- package/.agents/docs/configuration.md +275 -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 +2 -2
- package/.agents/schemas/agentrc.schema.json +1955 -1410
- 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 +16 -29
- package/.agents/scripts/apply-quality-bootstrap.js +27 -34
- package/.agents/scripts/audit-to-stories.js +160 -41
- 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 +159 -0
- package/.agents/scripts/check-lifecycle-lint.js +108 -124
- 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 +81 -35
- package/.agents/scripts/lib/audit-to-stories/wire-dependencies.js +185 -0
- 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/runners.js +38 -16
- package/.agents/scripts/lib/config/temp-paths.js +6 -5
- package/.agents/scripts/lib/config-settings-schema-delivery.js +245 -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/dependency-parser.js +20 -7
- 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/findings/provenance-field.js +135 -0
- package/.agents/scripts/lib/findings/route-finding.js +57 -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-config-resolver.js +181 -0
- package/.agents/scripts/lib/knip-entry-sync.js +508 -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 +7 -26
- package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +69 -5
- package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +69 -13
- package/.agents/scripts/lib/orchestration/plan-persist/summary.js +49 -0
- package/.agents/scripts/lib/orchestration/{lifecycle/listeners/watcher.js → pr-watch.js} +58 -208
- package/.agents/scripts/lib/orchestration/resolve-stories.js +77 -50
- 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-conflicts.js +116 -1
- package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +1 -1
- package/.agents/scripts/lib/orchestration/ticket-validator.js +38 -0
- 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/footer-block.js +97 -0
- package/.agents/scripts/lib/story-body/story-body.js +18 -81
- 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/wave-runner/footprint.js +306 -0
- package/.agents/scripts/lib/wave-runner/ready-set.js +198 -181
- 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/blocked-by-add.js +25 -10
- 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 +21 -7
- 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/stories-wave-tick.js +192 -9
- 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 +28 -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 +30 -2
- package/.agents/workflows/helpers/deliver-story-reference.md +84 -16
- package/.agents/workflows/helpers/plan-reference.md +76 -0
- 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 +58 -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 +14 -5
- 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
|
@@ -2,30 +2,37 @@
|
|
|
2
2
|
/* node:coverage ignore file */
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* check-lifecycle-lint.js — enforce the
|
|
6
|
-
*
|
|
5
|
+
* check-lifecycle-lint.js — enforce the lifecycle lint rules that biome's
|
|
6
|
+
* stock ruleset cannot express.
|
|
7
7
|
*
|
|
8
|
-
* Rule 1 — "No Promise.all over
|
|
9
|
-
* Files under `.agents/scripts/lib/orchestration/lifecycle/**`
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* `tests/lifecycle/**` are exempt — fixtures that prove the rule
|
|
8
|
+
* Rule 1 — "No Promise.all over the lifecycle surface".
|
|
9
|
+
* Files under `.agents/scripts/lib/orchestration/lifecycle/**` MUST NOT
|
|
10
|
+
* contain `Promise.all(`. The surface is the append-only ledger writer, and
|
|
11
|
+
* record ordering is the whole point of an append-only trail: a parallel
|
|
12
|
+
* write interleaves records and breaks the ordering a reader relies on.
|
|
13
|
+
* Tests under `tests/lifecycle/**` are exempt — fixtures that prove the rule
|
|
14
14
|
* bites need to carry the pattern.
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
16
|
+
* (Story #5024 narrowed the rationale. It was "the bus is a strictly
|
|
17
|
+
* sequential mediator; parallelizing listeners breaks repeatability", which
|
|
18
|
+
* stopped being the reason when the bus was deleted. The rule still guards a
|
|
19
|
+
* real property of what remains.)
|
|
20
|
+
*
|
|
21
|
+
* The old Rule 2 — "wildcard-observer firewall", gating any module under
|
|
22
|
+
* `lifecycle/listeners/**` that called `bus.on('*', …)` — was deleted by
|
|
23
|
+
* Story #5024 along with the bus. Both halves of its predicate became
|
|
24
|
+
* unsatisfiable in the same commit (the directory is gone and there is no
|
|
25
|
+
* `bus` to call `.on` on), so it returned zero violations by construction
|
|
26
|
+
* rather than by verification: a gate that cannot fire, of exactly the class
|
|
27
|
+
* Story #5004 retired `check-gherkin-placeholders.js` for.
|
|
22
28
|
*
|
|
23
29
|
* Rule 3 — "Auto-merge lockout" (Story #2253 / Task #2255, Epic #2172
|
|
24
30
|
* review High-1).
|
|
25
31
|
* String literals containing the substring `gh pr merge` MUST NOT
|
|
26
32
|
* appear in any file under `.agents/scripts/**` EXCEPT
|
|
27
|
-
*
|
|
28
|
-
*
|
|
33
|
+
* `lib/orchestration/single-story-close/phases/auto-merge.js` — the v2
|
|
34
|
+
* close path that replaced the Epic-era `AutomergeArmer` listener the rule
|
|
35
|
+
* was originally written against. The original safety hole was an
|
|
29
36
|
* unconditional `gh pr merge <pr> --auto --squash --delete-branch`
|
|
30
37
|
* call in `epic-deliver-finalize.js` that armed GitHub's native
|
|
31
38
|
* auto-merge BEFORE the framework's automerge predicate evaluated
|
|
@@ -39,6 +46,23 @@
|
|
|
39
46
|
* The exempt path is matched by suffix so it bites even before the
|
|
40
47
|
* armer file lands (Wave 7); pre-existence is not required.
|
|
41
48
|
*
|
|
49
|
+
* Scan root:
|
|
50
|
+
* --root <dir> Scan <dir> instead of this checkout. Both surfaces are
|
|
51
|
+
* derived from it, so one flag moves the whole scan.
|
|
52
|
+
* Defaults to the repository this script ships in, which is
|
|
53
|
+
* what `npm run lint` gets by passing nothing.
|
|
54
|
+
*
|
|
55
|
+
* The seam exists for tests (Story #5052). The CLI test that proves a
|
|
56
|
+
* violation is *caught* has to plant one, and it used to plant into the
|
|
57
|
+
* live `.agents/` tree — which `tests/e2e/sync-prune.integration.test.js`
|
|
58
|
+
* copies with the real binary, so the two raced: the sync either lost the
|
|
59
|
+
* file between enumeration and `copyfile` (ENOENT) or copied it once and
|
|
60
|
+
* pruned it on the second pass, tripping an idempotence assertion. Pointing
|
|
61
|
+
* the planting test at a temp root keeps what that test was written for —
|
|
62
|
+
* discovery is still the CLI's own walk, not an injected file list — while
|
|
63
|
+
* leaving the shared tree untouched. Mirrors the `--root` seam
|
|
64
|
+
* `check-test-temp-hygiene.js` already ships for the same reason.
|
|
65
|
+
*
|
|
42
66
|
* Exit codes:
|
|
43
67
|
* 0 — clean.
|
|
44
68
|
* 1 — at least one violation; offending file + line printed to stderr.
|
|
@@ -48,25 +72,32 @@
|
|
|
48
72
|
* biome + markdownlint; a custom rule fits cleanly alongside.
|
|
49
73
|
*/
|
|
50
74
|
|
|
51
|
-
import {
|
|
75
|
+
import { readFileSync } from 'node:fs';
|
|
52
76
|
import path from 'node:path';
|
|
53
77
|
import { fileURLToPath } from 'node:url';
|
|
54
78
|
|
|
55
79
|
import { runAsCli } from './lib/cli-utils.js';
|
|
80
|
+
import { walkFilesByExtension } from './lib/fs-walk.js';
|
|
56
81
|
|
|
57
82
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
58
83
|
const REPO_ROOT = path.resolve(__dirname, '..', '..');
|
|
59
84
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
85
|
+
/** Rule 1's scan surface for a given repository root. */
|
|
86
|
+
function lifecycleDirFor(root) {
|
|
87
|
+
return path.join(
|
|
88
|
+
root,
|
|
89
|
+
'.agents',
|
|
90
|
+
'scripts',
|
|
91
|
+
'lib',
|
|
92
|
+
'orchestration',
|
|
93
|
+
'lifecycle',
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Rule 3's scan surface for a given repository root. */
|
|
98
|
+
function scriptsDirFor(root) {
|
|
99
|
+
return path.join(root, '.agents', 'scripts');
|
|
100
|
+
}
|
|
70
101
|
|
|
71
102
|
/**
|
|
72
103
|
* Files exempt from the merge-lockout rule. The path is matched by
|
|
@@ -102,52 +133,6 @@ const MERGE_LOCKOUT_INFRASTRUCTURE_SUFFIXES = Object.freeze([
|
|
|
102
133
|
path.join('.agents', 'scripts', 'check-lifecycle-lint.js'),
|
|
103
134
|
]);
|
|
104
135
|
|
|
105
|
-
/**
|
|
106
|
-
* Static blocklist of modules that mutate state under orchestration.
|
|
107
|
-
* Matched by `import … from '<spec>'` specifier suffix so both
|
|
108
|
-
* relative and package imports are caught. The list is small by
|
|
109
|
-
* intent — wildcard observers should not need ANY of these.
|
|
110
|
-
*
|
|
111
|
-
* Maintainers: when a future module joins the "mutates real state"
|
|
112
|
-
* club, add it here. The lint rule is the wildcard-firewall contract;
|
|
113
|
-
* the listeners SHOULD NOT bypass it.
|
|
114
|
-
*/
|
|
115
|
-
const STATE_MUTATING_MODULES = Object.freeze([
|
|
116
|
-
// GitHub state writers
|
|
117
|
-
'update-ticket-state.js',
|
|
118
|
-
'post-structured-comment.js',
|
|
119
|
-
'lib/orchestration/ticketing/state.js',
|
|
120
|
-
'lib/orchestration/ticketing/bulk.js',
|
|
121
|
-
// git / worktree mutators
|
|
122
|
-
'lib/git-utils.js',
|
|
123
|
-
'lib/orchestration/worktree-manager.js',
|
|
124
|
-
// notification writers
|
|
125
|
-
'notify.js',
|
|
126
|
-
]);
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Walk a directory tree synchronously, yielding absolute paths of files
|
|
130
|
-
* matching `.js`. The lifecycle surface is small (< 50 files in the
|
|
131
|
-
* worst case); a streaming walker is unnecessary.
|
|
132
|
-
*/
|
|
133
|
-
function* walkJs(dir) {
|
|
134
|
-
let entries;
|
|
135
|
-
try {
|
|
136
|
-
entries = readdirSync(dir, { withFileTypes: true });
|
|
137
|
-
} catch (err) {
|
|
138
|
-
if (err.code === 'ENOENT') return;
|
|
139
|
-
throw err;
|
|
140
|
-
}
|
|
141
|
-
for (const entry of entries) {
|
|
142
|
-
const p = path.join(dir, entry.name);
|
|
143
|
-
if (entry.isDirectory()) {
|
|
144
|
-
yield* walkJs(p);
|
|
145
|
-
} else if (entry.isFile() && entry.name.endsWith('.js')) {
|
|
146
|
-
yield p;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
136
|
/**
|
|
152
137
|
* Rule 1 enforcement. Returns an array of `{ file, line, hint }`
|
|
153
138
|
* violations. Inline disable comments (`// lint-lifecycle-disable`) on
|
|
@@ -158,7 +143,7 @@ export function findPromiseAllViolations(
|
|
|
158
143
|
{ read = readFileSync } = {},
|
|
159
144
|
) {
|
|
160
145
|
const violations = [];
|
|
161
|
-
for (const file of
|
|
146
|
+
for (const file of walkFilesByExtension(rootDir, '.js')) {
|
|
162
147
|
const text = read(file, 'utf8');
|
|
163
148
|
const lines = text.split('\n');
|
|
164
149
|
for (let i = 0; i < lines.length; i += 1) {
|
|
@@ -169,7 +154,7 @@ export function findPromiseAllViolations(
|
|
|
169
154
|
violations.push({
|
|
170
155
|
file,
|
|
171
156
|
line: i + 1,
|
|
172
|
-
hint: 'Promise.all
|
|
157
|
+
hint: 'Promise.all on the lifecycle surface interleaves ledger writes. The ledger is append-only; writes must stay sequential.',
|
|
173
158
|
});
|
|
174
159
|
}
|
|
175
160
|
}
|
|
@@ -177,45 +162,6 @@ export function findPromiseAllViolations(
|
|
|
177
162
|
return violations;
|
|
178
163
|
}
|
|
179
164
|
|
|
180
|
-
/**
|
|
181
|
-
* Rule 2 enforcement. Returns violations for any file under
|
|
182
|
-
* `listenersDir` that BOTH (a) registers a wildcard observer
|
|
183
|
-
* (`bus.on('*', …)`) AND (b) imports a state-mutating module.
|
|
184
|
-
*
|
|
185
|
-
* Files that don't register a wildcard observer are not gated; files
|
|
186
|
-
* that wildcard-observe but only import safe modules are not gated.
|
|
187
|
-
*/
|
|
188
|
-
export function findWildcardObserverFirewallViolations(
|
|
189
|
-
listenersDir,
|
|
190
|
-
{ read = readFileSync, blocklist = STATE_MUTATING_MODULES } = {},
|
|
191
|
-
) {
|
|
192
|
-
const violations = [];
|
|
193
|
-
for (const file of walkJs(listenersDir)) {
|
|
194
|
-
const text = read(file, 'utf8');
|
|
195
|
-
const hasWildcard = /\bbus\s*\.\s*on\s*\(\s*['"`]\*['"`]/.test(text);
|
|
196
|
-
if (!hasWildcard) continue;
|
|
197
|
-
// Extract imported module specifiers — robust enough for ES module
|
|
198
|
-
// imports without parsing the full AST.
|
|
199
|
-
// `matchAll` returns an iterator of regex match arrays; using it
|
|
200
|
-
// sidesteps the assignment-in-`while` pattern biome flags as
|
|
201
|
-
// confusing.
|
|
202
|
-
for (const match of text.matchAll(/\bfrom\s+['"]([^'"]+)['"]/g)) {
|
|
203
|
-
const spec = match[1];
|
|
204
|
-
for (const banned of blocklist) {
|
|
205
|
-
if (spec === banned || spec.endsWith(`/${banned}`)) {
|
|
206
|
-
violations.push({
|
|
207
|
-
file,
|
|
208
|
-
spec,
|
|
209
|
-
hint: `Wildcard observers must not import state-mutating modules. Saw '${spec}'.`,
|
|
210
|
-
});
|
|
211
|
-
break;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
return violations;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
165
|
/**
|
|
220
166
|
* Strip block (`/* … */`) and line (`// …`) comments from a source
|
|
221
167
|
* string. Pure — exported for tests so the comment-stripping contract
|
|
@@ -310,7 +256,7 @@ export function findMergeLockoutViolations(
|
|
|
310
256
|
// CLI, only the space-delimited form is.
|
|
311
257
|
const FORBIDDEN = 'gh pr merge';
|
|
312
258
|
const lineRe = /(['"`])((?:\\.|(?!\1).)*)\1/g;
|
|
313
|
-
for (const file of
|
|
259
|
+
for (const file of walkFilesByExtension(rootDir, '.js')) {
|
|
314
260
|
// Skip the armer (intentional carrier) and the lint infrastructure.
|
|
315
261
|
const allExempt = [...allowSuffixes, ...infrastructureSuffixes];
|
|
316
262
|
if (allExempt.some((suffix) => file.endsWith(suffix))) continue;
|
|
@@ -347,25 +293,48 @@ export function findMergeLockoutViolations(
|
|
|
347
293
|
return violations;
|
|
348
294
|
}
|
|
349
295
|
|
|
296
|
+
/**
|
|
297
|
+
* Parse the argument vector. The only option is the scan root; anything
|
|
298
|
+
* else is ignored so an extra flag can never silently narrow the scan.
|
|
299
|
+
*
|
|
300
|
+
* @param {string[]} argv Arguments without the node/script entries.
|
|
301
|
+
* @returns {{ root: string }}
|
|
302
|
+
*/
|
|
303
|
+
function parseArgv(argv) {
|
|
304
|
+
let root = REPO_ROOT;
|
|
305
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
306
|
+
if (argv[i] === '--root') {
|
|
307
|
+
i += 1;
|
|
308
|
+
root = path.resolve(String(argv[i] ?? '.'));
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return { root };
|
|
312
|
+
}
|
|
313
|
+
|
|
350
314
|
async function main() {
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
315
|
+
const { root } = parseArgv(process.argv.slice(2));
|
|
316
|
+
// Per-rule discovery. Both rules' exemptions match by absolute-path
|
|
317
|
+
// SUFFIX, which is what lets an injected root keep the same allow-list
|
|
318
|
+
// semantics as the live tree — do not re-anchor them to `root`.
|
|
319
|
+
const v1 = findPromiseAllViolations(lifecycleDirFor(root));
|
|
320
|
+
const v3 = findMergeLockoutViolations(scriptsDirFor(root));
|
|
355
321
|
const all = [
|
|
356
|
-
...v1.map((v) => ({ rule: 'no-promise-all-
|
|
357
|
-
...v2.map((v) => ({ rule: 'wildcard-observer-firewall', ...v })),
|
|
322
|
+
...v1.map((v) => ({ rule: 'no-promise-all-lifecycle', ...v })),
|
|
358
323
|
...v3.map((v) => ({ rule: 'merge-lockout', ...v })),
|
|
359
324
|
];
|
|
360
325
|
if (all.length === 0) {
|
|
361
326
|
process.stdout.write(
|
|
362
|
-
'[lifecycle-lint] clean: no Promise.all
|
|
327
|
+
'[lifecycle-lint] clean: no Promise.all on the lifecycle surface; no merge-lockout violations.\n',
|
|
363
328
|
);
|
|
364
329
|
return 0;
|
|
365
330
|
}
|
|
366
331
|
for (const v of all) {
|
|
367
|
-
|
|
368
|
-
|
|
332
|
+
// Both finders always stamp a 1-based `line`, so there is no file-only
|
|
333
|
+
// fallback to render — the ternary that used to guard this was an
|
|
334
|
+
// unreachable branch (Story #5024).
|
|
335
|
+
process.stderr.write(
|
|
336
|
+
`[lifecycle-lint][${v.rule}] ${v.file}:${v.line}\n ${v.hint}\n`,
|
|
337
|
+
);
|
|
369
338
|
}
|
|
370
339
|
return 1;
|
|
371
340
|
}
|
|
@@ -373,4 +342,19 @@ async function main() {
|
|
|
373
342
|
await runAsCli(import.meta.url, main, {
|
|
374
343
|
source: 'check-lifecycle-lint',
|
|
375
344
|
propagateExitCode: true,
|
|
345
|
+
usage: {
|
|
346
|
+
invocation: 'node .agents/scripts/check-lifecycle-lint.js [--root <dir>]',
|
|
347
|
+
summary:
|
|
348
|
+
'Enforce the two lifecycle lint rules biome cannot express: no Promise.all on the append-only lifecycle surface, and the auto-merge lockout on string literals under .agents/scripts/.',
|
|
349
|
+
flags: [
|
|
350
|
+
[
|
|
351
|
+
'--root <dir>',
|
|
352
|
+
'Repository root to scan (default: the checkout this script ships in). Both rule surfaces derive from it; used by tests so a planted violation never touches the shared tree.',
|
|
353
|
+
],
|
|
354
|
+
],
|
|
355
|
+
notes: [
|
|
356
|
+
'Ships as part of `npm run lint`, which invokes it with no arguments.',
|
|
357
|
+
'Exit codes:\n 0 clean\n 1 at least one violation; offending file and line printed to stderr',
|
|
358
|
+
],
|
|
359
|
+
},
|
|
376
360
|
});
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
* `` `${key}.schema.json` `` template), AND the
|
|
37
37
|
* schema's stem appears literally in a JS source as the
|
|
38
38
|
* key that would produce it. `lifecycle/` is loaded this
|
|
39
|
-
* way by `
|
|
39
|
+
* way by `lifecycle/emit-ledger-event.js`, and
|
|
40
40
|
* `baselines/` by `lib/baselines/envelope.js`.
|
|
41
41
|
*
|
|
42
42
|
* BOTH halves are required. The directory evidence alone
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
import { spawnSync } from 'node:child_process';
|
|
37
37
|
import crypto from 'node:crypto';
|
|
38
38
|
import { constants as osConstants } from 'node:os';
|
|
39
|
+
import { INTERCEPTOR_MAX_BUFFER_BYTES } from './lib/child-exec.js';
|
|
39
40
|
import { getLimits, resolveConfig } from './lib/config-resolver.js';
|
|
40
41
|
import { Logger } from './lib/Logger.js';
|
|
41
42
|
import { appendSignal } from './lib/observability/signals-writer.js';
|
|
@@ -291,10 +292,12 @@ export async function main(args = process.argv.slice(2)) {
|
|
|
291
292
|
const config = resolveConfig();
|
|
292
293
|
const limits = getLimits(config);
|
|
293
294
|
const executionTimeoutMs = limits.executionTimeoutMs;
|
|
294
|
-
//
|
|
295
|
-
//
|
|
296
|
-
//
|
|
297
|
-
|
|
295
|
+
// The interceptor's bound is deliberately *below* the framework-wide
|
|
296
|
+
// `MAX_BUFFER_BYTES` ceiling: it is a reported policy bound whose value the
|
|
297
|
+
// emitted friction row carries (`details.executionMaxBuffer`), not an
|
|
298
|
+
// overflow guard. Story #5009 moved its definition to the shared
|
|
299
|
+
// child-process surface so it is no longer a hand-copied literal here.
|
|
300
|
+
const executionMaxBuffer = INTERCEPTOR_MAX_BUFFER_BYTES;
|
|
298
301
|
|
|
299
302
|
const commandStr = cmdArgs.join(' ');
|
|
300
303
|
Logger.error(`[Diagnostic Interceptor] Executing: ${commandStr}`);
|