@attalabs/vinaya 0.4.3 → 0.4.4
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/aeg-root/enforcement.md +1 -1
- package/aeg-root/state-machine.md +1 -1
- package/dist/checks/bin/check-branch-topology.js +44 -31
- package/dist/checks/bin/check-brief-shape.js +44 -31
- package/dist/checks/bin/check-closes-n.js +44 -31
- package/dist/checks/bin/check-coherence.js +44 -31
- package/dist/checks/bin/check-dead-branch-push.js +44 -31
- package/dist/checks/bin/check-dispatch-readiness.js +44 -31
- package/dist/checks/bin/check-doc-coverage-push.js +221 -34
- package/dist/checks/bin/check-doc-coverage.js +221 -34
- package/dist/checks/bin/check-first-push-dispatch.js +44 -31
- package/dist/checks/bin/check-issue-assignment.js +44 -31
- package/dist/checks/bin/check-no-disk-state.js +44 -31
- package/dist/checks/bin/check-reader-resolvable-prose.js +44 -31
- package/dist/checks/bin/check-registry-gates.js +44 -31
- package/dist/checks/bin/check-review-gate.js +222 -36
- package/dist/checks/bin/check-single-plan-pr.js +44 -31
- package/dist/checks/bin/check-test-plan.js +44 -31
- package/dist/index.js +116 -34
- package/package.json +1 -1
- package/studio-standalone/.worktrees/fix/bump-0.4.4/.vinaya/projects.md +63 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/aeg-manual-flow.md +216 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/contracts/archivist-tranche-archivist.md +92 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/contracts/brief-developer.md +140 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/contracts/developer-reviewer.md +106 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/contracts/planner-brief.md +130 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/contracts/reviewer-archivist.md +96 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/contracts/tranche-archivist-planner.md +116 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/coordination.md +293 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/documentation-coherence.md +65 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/enforcement.md +161 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/glossary.md +36 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/process.md +371 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/reviewer-prompt.md +68 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/roles/archivist.md +167 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/roles/brief-author.md +108 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/roles/developer.md +429 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/roles/planner.md +285 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/roles/principal.md +107 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/roles/reviewer.md +130 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/roles/security.md +121 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/roles/tranche-archivist.md +257 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/skills/aeg/SKILL.md +90 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/skills/aeg-roles/SKILL.md +58 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/skills/brief-authoring/SKILL.md +497 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/state-machine.md +656 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/templates/brief-template.md +103 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/templates/issue-rationale-template.md +35 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/templates/pr-report-template.md +68 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/tranche-model.md +306 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/tranches/completed/aeg-coherence-v1.md +55 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/tranches/completed/aeg-coherence-v1.tokens.md +13 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/tranches/completed/aeg-consolidation.md +28 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/tranches/completed/aeg-governance-hardening.md +80 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/tranches/completed/aeg-governance-ui-v2.md +168 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/tranches/completed/aeg-governance-ui-v2.tokens.md +18 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/tranches/completed/aeg-studio-cleanup.md +37 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/tranches/completed/aeg-ui-v1.md +176 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/tranches/completed/aeg-ui-v1.tokens.md +17 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/tranches/completed/herald-agents-v2.md +35 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/tranches/completed/herald-agents-v2.tokens.md +11 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/tranches/completed/herald-onto-engine.md +99 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/aeg-root/tranches/completed/vada-agents-v2.md +220 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/apps/vinaya/cli/tests/fixtures/checks/vinaya.config.json +9 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/node_modules/@attalabs/vinaya/tests/fixtures/checks/vinaya.config.json +9 -0
- package/studio-standalone/.worktrees/fix/bump-0.4.4/vinaya.config.json +36 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/.vinaya/projects.md +63 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/aeg-manual-flow.md +216 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/contracts/archivist-tranche-archivist.md +92 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/contracts/brief-developer.md +140 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/contracts/developer-reviewer.md +106 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/contracts/planner-brief.md +130 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/contracts/reviewer-archivist.md +96 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/contracts/tranche-archivist-planner.md +116 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/coordination.md +293 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/documentation-coherence.md +65 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/enforcement.md +161 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/glossary.md +36 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/process.md +371 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/reviewer-prompt.md +68 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/roles/archivist.md +167 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/roles/brief-author.md +108 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/roles/developer.md +429 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/roles/planner.md +285 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/roles/principal.md +107 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/roles/reviewer.md +130 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/roles/security.md +121 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/roles/tranche-archivist.md +257 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/skills/aeg/SKILL.md +90 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/skills/aeg-roles/SKILL.md +58 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/skills/brief-authoring/SKILL.md +497 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/state-machine.md +656 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/templates/brief-template.md +103 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/templates/issue-rationale-template.md +35 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/templates/pr-report-template.md +68 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/tranche-model.md +306 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/tranches/completed/aeg-coherence-v1.md +55 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/tranches/completed/aeg-coherence-v1.tokens.md +13 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/tranches/completed/aeg-consolidation.md +28 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/tranches/completed/aeg-governance-hardening.md +80 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/tranches/completed/aeg-governance-ui-v2.md +168 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/tranches/completed/aeg-governance-ui-v2.tokens.md +18 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/tranches/completed/aeg-studio-cleanup.md +37 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/tranches/completed/aeg-ui-v1.md +176 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/tranches/completed/aeg-ui-v1.tokens.md +17 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/tranches/completed/herald-agents-v2.md +35 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/tranches/completed/herald-agents-v2.tokens.md +11 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/tranches/completed/herald-onto-engine.md +99 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/aeg-root/tranches/completed/vada-agents-v2.md +220 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/apps/vinaya/cli/tests/fixtures/checks/vinaya.config.json +9 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/node_modules/@attalabs/vinaya/tests/fixtures/checks/vinaya.config.json +9 -0
- package/studio-standalone/.worktrees/fix/check-lifecycle-phase/vinaya.config.json +36 -0
- package/studio-standalone/_node_modules/@atta/vinaya-cli/aeg-root/enforcement.md +1 -1
- package/studio-standalone/_node_modules/@atta/vinaya-cli/aeg-root/state-machine.md +1 -1
- package/studio-standalone/_node_modules/@attalabs/vinaya/aeg-root/enforcement.md +1 -1
- package/studio-standalone/_node_modules/@attalabs/vinaya/aeg-root/state-machine.md +1 -1
- package/studio-standalone/apps/vinaya/cli/aeg-root/enforcement.md +1 -1
- package/studio-standalone/apps/vinaya/cli/aeg-root/state-machine.md +1 -1
- package/studio-standalone/apps/vinaya/web/.next/BUILD_ID +1 -1
- package/studio-standalone/apps/vinaya/web/.next/build-manifest.json +3 -3
- package/studio-standalone/apps/vinaya/web/.next/server/app/(site)/cli/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/(site)/config/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/(site)/docs/(standalone)/cli/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/(site)/docs/(standalone)/harness/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/(site)/docs/(standalone)/state-machine/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/(site)/docs/(with-sidebar)/[...slug]/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/(site)/docs/(with-sidebar)/reference/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/(site)/docs/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/(site)/install/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/(site)/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/(site)/roadmap/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/(site)/start/archive/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/(site)/start/brief/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/(site)/start/develop/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/(site)/start/overview/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/(site)/start/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/(site)/start/plan/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/(site)/start/quick/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/(site)/start/review/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/(site)/start/security/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/(site)/start/wrap-up/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/(site)/state-machine/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/(site)/the-harness/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/(site)/the-studio/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/_global-error.html +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/_global-error.rsc +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/api/coherence/route.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/studio/backlog/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/studio/docs/[...slug]/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/studio/docs/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/studio/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/studio/projects/[name]/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/studio/projects/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/app/studio/tranches/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/chunks/ssr/[root-of-the-server]__0kqmrho._.js +1 -1
- package/studio-standalone/apps/vinaya/web/.next/server/chunks/ssr/[root-of-the-server]__1dxaovd._.js +2 -2
- package/studio-standalone/apps/vinaya/web/.next/server/middleware-build-manifest.js +3 -3
- package/studio-standalone/apps/vinaya/web/.next/server/pages/500.html +1 -1
- /package/studio-standalone/apps/vinaya/web/.next/static/{Afs41Lu9uNQ-T6uC-tm4_ → mSHYKKSLd91iaLw52dYpD}/_buildManifest.js +0 -0
- /package/studio-standalone/apps/vinaya/web/.next/static/{Afs41Lu9uNQ-T6uC-tm4_ → mSHYKKSLd91iaLw52dYpD}/_clientMiddlewareManifest.js +0 -0
- /package/studio-standalone/apps/vinaya/web/.next/static/{Afs41Lu9uNQ-T6uC-tm4_ → mSHYKKSLd91iaLw52dYpD}/_ssgManifest.js +0 -0
package/aeg-root/enforcement.md
CHANGED
|
@@ -44,7 +44,7 @@ The same check implementations run at ring 0 and ring 1 — one codebase, two en
|
|
|
44
44
|
| Starting the Dig (before authoring a brief) / starting Step 0 (before executing one) / **every push on a task branch before its PR exists** | Ever started work only to find out halfway it was blocked on something else? | hook | Refuses to start work until every precondition for the task is checked live and found clear. | **`verify-dispatch` CLI** (`packages/aeg-core/bin/verify-dispatch.ts` — mandated by `roles/developer.md` and `skills/brief-authoring/SKILL.md`; **now also hook-automated** — task 25) | Every dispatch precondition is re-checked live against the forge (the task's row/Issue are derived directly from a Milestone + `vinaya/tranche:<slug>`-labeled Issues as of the forge-native migration — no `origin/main` file read involved; leftover-branch detection still fetches `origin/main` for its own commit-count comparison): the task's row and Issue exist; the Issue passes the rationale gate; every `depends-on` PR is merged and no `conflicts-with` PR is open/in-flight; every named project's prior tranche is archived. (2026-07-13: the immediately-prior task's three-predicate archival bar — the row-adjacency predicate — was removed once the provenance-posting signal it existed to protect became automated.) A `NOT READY` result is a stop condition. Every `gh` call this script makes carries an explicit `-R <owner>/<repo>` (task 23) — from a linked `git worktree add` checkout, an untargeted `gh issue list`/`gh issue view`/`gh pr list` silently scopes to the wrong repo (or returns nothing), which previously produced false prior-tranche-archival blockers. **`.husky/pre-push`** now runs this gate itself on a `task/*/*` branch's first push (before a PR exists), refusing on genuine `NOT READY` and failing OPEN — loudly — when a `gh auth status` reachability probe fails or `verify-dispatch`'s own repo/token resolution fails (task 18's fail-open precedent). The gate reads only the `dispatch-readiness:` predicate, not verify-dispatch's combined exit code, which also folds in `leftover-detection` (a pre-Step-0 "safe to branch fresh" advisory that would otherwise false-block every push after the first on a task legitimately mid-flight — discovered live while building this gate). Once a PR exists, later pushes skip the gate: dispatch was already validated once. | `packages/aeg-core/bin/verify-dispatch.ts` |
|
|
45
45
|
| Opening a task PR whose surface includes real code | Ever had a PR's description quietly stop matching what the code does? | event | Refuses a code-carrying pull request whose description no longer matches what it changes. | **Brief Validation — `checkPremiseCoverage`** | The PR body carries a `Premise:` block with at least one assertion (`contains`/`absent`/`sha256`) whose path falls inside the task's surface map — re-assertable at any point via `verify-dispatch --premise` or `verify-task`. | `packages/aeg-core/src/brief-validation.ts` |
|
|
46
46
|
| Opening a task PR (final self-check before creation) | Ever opened a PR and only then discovered the tests were failing? | event | Runs the whole exit check before a pull request is created, so failures surface first. | **`verify-task` CLI** (`packages/aeg-core/bin/verify-task.ts` — mandated by `roles/developer.md`; **now also hook-automated** — task 25) | Typecheck, lint, tests, build, `verify-docs --pr`, and the premise coverage/recheck pair all pass, as one summary, against the PR's actual diff, before `open-pr.ts` is invoked. **`open-pr.ts` now runs this composite itself** for task branches (via `gatePlanForBranch`), invoked wholesale rather than partially re-implemented — non-task branches are unaffected (byte-identical gate set). `verify-docs --pr` runs twice on a task-branch PR-open as an accepted, measured overlap (~4s with a warm turbo cache). | `packages/aeg-core/bin/verify-task.ts` |
|
|
47
|
-
| Spawning a check (`vinaya check`, ring-0 pre-push AND ring-1 CI) | Ever had a check quietly read a secret it had no business seeing, because nothing scoped what it could reach? | hook | Governs which environment variables a spawned check's child process can see, instead of every check inheriting the full parent environment unconditionally. | **Env allowlist** (`CheckSpec['env']`, `apps/vinaya/cli/src/checks/contract.ts`; construction: `buildCheckEnv`, `apps/vinaya/cli/src/checks/runner.ts`) | **⚠️ BREAKING, live (task 3):** a spawned check's child process sees only a fixed baseline (`PATH`, `LANG`, `HOME`, `HTTPS_PROXY`, `HTTP_PROXY`, `NO_PROXY`, `TMPDIR`) plus whatever its own `env` declaration (`true` / `{ optional: true }` / `{ anyOf: [...] }` / a literal string) explicitly forwards — **no longer the full parent environment.** An undeclared variable a check's own code reads is now genuinely invisible to it, not merely warned about; a `true` or unsatisfied `anyOf` declaration missing from the caller's environment synthesizes a `CheckError` and the check never spawns at all. All 15 core checks in `registry.ts` carry an audited declaration, and every core check that spawns `gh` or reaches the forge through the token-resolution chain additionally forwards `GITHUB_TOKEN`/`GH_TOKEN` as `{ optional: true }` — on a CI runner those env vars are `gh`'s only authentication path, so a check that shells out without forwarding them runs unauthenticated there (hard-failing or silently degrading, depending on the bin's failure mode) while passing locally on `gh`'s keyring; the pairing is coupling-tested (`apps/vinaya/cli/tests/checks/registry-env.test.ts` detects `gh` invocations in any call shape — array-form, string-form, and template-literal — and demands both declarations); that detection is source-text pattern matching, so it does not yet catch a check that reaches the forge indirectly through `createForgeSource` (`@atta/vinaya-sources`) with no literal `gh` call of its own — found live: `closes-n` reaches the forge this way and had shipped without either token declared, passing locally under a developer's own `gh` keyring and failing unauthenticated on every CI runner, a known gap in the coupling test's own coverage rather than in the rule it's checking. This repo's own core checks are therefore unaffected — an adopter's **custom** check that reads `process.env` directly with no `env` declared will lose that access at this minor; declare `env` on it (`vinaya.config.json`'s `checks.<name>.env`) before upgrading. `vinaya check`'s prior warn-phase print (task 2) is retired now that the behavior it warned about is live; `vinaya doctor` still carries the same missing-declaration diagnostic permanently, at `info` severity, so a custom check's gap remains visible after upgrade even though `vinaya check` no longer prints it inline. | `apps/vinaya/cli/src/checks/runner.ts` |
|
|
47
|
+
| Spawning a check (`vinaya check`, ring-0 pre-push AND ring-1 CI) | Ever had a check quietly read a secret it had no business seeing, because nothing scoped what it could reach? | hook | Governs which environment variables a spawned check's child process can see, instead of every check inheriting the full parent environment unconditionally. | **Env allowlist** (`CheckSpec['env']`, `apps/vinaya/cli/src/checks/contract.ts`; construction: `buildCheckEnv`, `apps/vinaya/cli/src/checks/runner.ts`) | **⚠️ BREAKING, live (task 3):** a spawned check's child process sees only a fixed baseline (`PATH`, `LANG`, `HOME`, `HTTPS_PROXY`, `HTTP_PROXY`, `NO_PROXY`, `TMPDIR`) plus whatever its own `env` declaration (`true` / `{ optional: true }` / `{ anyOf: [...] }` / a literal string) explicitly forwards — **no longer the full parent environment.** An undeclared variable a check's own code reads is now genuinely invisible to it, not merely warned about; a `true` or unsatisfied `anyOf` declaration missing from the caller's environment synthesizes a `CheckError` and the check never spawns at all. All 15 core checks in `registry.ts` carry an audited declaration, and every core check that spawns `gh` or reaches the forge through the token-resolution chain additionally forwards `GITHUB_TOKEN`/`GH_TOKEN` as `{ optional: true }` — on a CI runner those env vars are `gh`'s only authentication path, so a check that shells out without forwarding them runs unauthenticated there (hard-failing or silently degrading, depending on the bin's failure mode) while passing locally on `gh`'s keyring; the pairing is coupling-tested (`apps/vinaya/cli/tests/checks/registry-env.test.ts` detects `gh` invocations in any call shape — array-form, string-form, and template-literal — and demands both declarations); that detection is source-text pattern matching, so it does not yet catch a check that reaches the forge indirectly through `createForgeSource` (`@atta/vinaya-sources`) with no literal `gh` call of its own — found live: `closes-n` reaches the forge this way and had shipped without either token declared, passing locally under a developer's own `gh` keyring and failing unauthenticated on every CI runner, a known gap in the coupling test's own coverage rather than in the rule it's checking. This repo's own core checks are therefore unaffected — an adopter's **custom** check that reads `process.env` directly with no `env` declared will lose that access at this minor; declare `env` on it (`vinaya.config.json`'s `checks.<name>.env`) before upgrading. `vinaya check`'s prior warn-phase print (task 2) is retired now that the behavior it warned about is live; `vinaya doctor` still carries the same missing-declaration diagnostic permanently, at `info` severity, so a custom check's gap remains visible after upgrade even though `vinaya check` no longer prints it inline. A second field, `requiresOpenPr?: boolean` (same no-privileged-field discipline as `env` — a custom check can declare it exactly like a core one), marks a check that can only evaluate meaningfully once a pull request exists; the generated `pre-commit`/`pre-push` hooks pass a new `--local` flag that skips every check declaring it, while CI (`vinaya-checks.yml`, `pull_request`-triggered) omits the flag and always runs them for real. Found live: `closes-n`/`test-plan` ran unconditionally in both hooks with no `requiresOpenPr` field to opt out on, so the first commit on a fresh task branch could never satisfy either — no PR exists yet at commit or push time, only after. `vinaya.config.json`'s `principals` field (the review-gate/waiver trust anchor, `apps/vinaya/cli/src/lib/config.ts`'s `resolvePrincipalAllowlist`) is resolved via `loadTrustAnchorConfig()` — a `gh api` read of the repository's DEFAULT BRANCH, never local git, the PR's working tree, or any env var. Three successive attempts got this wrong before landing (all caught pre-merge): reading the PR's working tree; reading `git show ${BASE_SHA}:…` where `BASE_SHA` was an env var; and reading `git show origin/main:…`, where `origin/main` is a LOCAL remote-tracking ref the PR's own workflow can `git update-ref`. The rule they establish: **inside a `pull_request`-triggered workflow nothing on the job's own disk or environment is a trust boundary against the PR author**, because the workflow definition itself comes from the PR. `check-doc-coverage.ts`/`check-doc-coverage-push.ts` keep their own, separate, legitimately-overridable `BASE_SHA` for diff-scoping only, never reused for a trust decision. The API read raises the bar but is not itself the boundary — a PR can still delete the gate's step from its own workflow copy; only **branch protection with the check marked required** makes review enforcement unbypassable, which is why `init` prints that command and `doctor` reports its absence. | `apps/vinaya/cli/src/checks/runner.ts` |
|
|
48
48
|
| `vinaya check` / `vinaya check --plan` (a CLI mechanism, not a git hook — this row is `.husky`-free) | Ever had a config entry silently double-run alongside the core check it was meant to replace, with no way to see that from the outside? | event | Resolves core-registered and config-registered checks into one deterministic table before anything runs, instead of letting a config entry run alongside the core check it collides with, unannounced. | **Checks-side resolver** (`resolveChecks`/`isValidNamespacedKey`, `apps/vinaya/cli/src/checks/resolver.ts`) | An exact-key match against a core check ID is an **override** — a complete replacement of the spec, never a merge. A key containing exactly one `/`, both segments matching `[a-z0-9][a-z0-9-]*` and the `vinaya` prefix rejected as an exact segment match, is **additive**. Anything else — a bare key with no `/` matching no core ID — is a loud `FAIL_CLOSED` refusal: never silently dropped, never defaulted, never last-wins. `--plan` never resolves or prints an env *value*, only how each one resolves. **This release feeds `vinaya check --plan` / `--plan --json` only** — `vinaya check`'s real execution (`check.ts`'s flat `[...coreCheckRegistry(), ...customSpecs]` concat) is unaffected until a later task wires it in. | `apps/vinaya/cli/src/checks/resolver.ts` |
|
|
49
49
|
|
|
50
50
|
**Documentation coverage, specifically** (a historical pain point): the code→document ownership rule is enforced at *two* prevention chokepoints — at every push (over the branch's cumulative change set) and again at pull-request creation and editing. A change to owned code cannot be published, let alone turned into a pull request, without its owning document.
|
|
@@ -444,7 +444,7 @@ For each changed code file in the PR, glob-match against every binding. Per **ma
|
|
|
444
444
|
| **Strong-pass** | The in-repo pointer is in the PR diff. |
|
|
445
445
|
| **URL-ack** | The pointer is a URL **and** the PR body carries `Doc-ack: <pointer> — <note>` whose `<pointer>` exactly matches the binding's URL. |
|
|
446
446
|
| **Neutral-edit** | The pointer is in-repo, absent from the diff, **and** the PR body carries `Doc-neutral: <pointer> — <note>` whose `<pointer>` exactly matches the binding's doc, **and** the diff of every matched code file is comment/whitespace-only (see below). Both the declaration and the evidence are required — either alone fails. |
|
|
447
|
-
| **Waiver** | The pull request carries the `vinaya/waiver:docs` label, AND the actor of that label's most recent labeling timeline event is a configured principal. PR-wide, not per-binding — one verified label satisfies every fired binding in the PR. Label presence alone is never sufficient; there is no PR-body waiver field. |
|
|
447
|
+
| **Waiver** | The pull request carries the `vinaya/waiver:docs` label, AND the actor of that label's most recent labeling timeline event is a configured principal (`isPrincipal`, `packages/aeg-core/src/waiver-label.ts` — matched **case-insensitively**, since GitHub logins are; an exact-match test fails closed but invisibly, stranding a real principal's action for a reason nothing states). PR-wide, not per-binding — one verified label satisfies every fired binding in the PR. Label presence alone is never sufficient; there is no PR-body waiver field. |
|
|
448
448
|
|
|
449
449
|
If none of the above hold, C5 fails the PR. A binding whose in-repo pointer **does not exist on disk** is a distinct *dangling* failure (fix the binding or add the doc) — never silently ignored, because a dangling pointer is the failure mode the seam was built to prevent.
|
|
450
450
|
|
|
@@ -1451,12 +1451,16 @@ function isCodeFile(p) {
|
|
|
1451
1451
|
var WAIVER_LABEL = label("waiver-docs");
|
|
1452
1452
|
var WAIVER_LABEL_REVIEW = label("waiver-review");
|
|
1453
1453
|
var PRINCIPAL_ALLOWLIST = ["daniboomerang"];
|
|
1454
|
+
function isPrincipal(login, principalAllowlist) {
|
|
1455
|
+
if (login === null)
|
|
1456
|
+
return false;
|
|
1457
|
+
const normalized = login.toLowerCase();
|
|
1458
|
+
return principalAllowlist.some((p) => p.toLowerCase() === normalized);
|
|
1459
|
+
}
|
|
1454
1460
|
function isWaiverLabelActorVerified(opts) {
|
|
1455
1461
|
if (!opts.labels.includes(opts.label))
|
|
1456
1462
|
return false;
|
|
1457
|
-
|
|
1458
|
-
return false;
|
|
1459
|
-
return opts.principalAllowlist.includes(opts.labelActor);
|
|
1463
|
+
return isPrincipal(opts.labelActor, opts.principalAllowlist);
|
|
1460
1464
|
}
|
|
1461
1465
|
|
|
1462
1466
|
// ../../../packages/aeg-core/src/doc-owners.ts
|
|
@@ -1644,6 +1648,27 @@ function overrideActive(opts) {
|
|
|
1644
1648
|
// ../../../packages/aeg-core/src/premise-check.ts
|
|
1645
1649
|
var ASSERTION_KINDS = new Set(["contains", "absent", "sha256"]);
|
|
1646
1650
|
|
|
1651
|
+
// ../../../packages/aeg-core/src/test-plan-section.ts
|
|
1652
|
+
var HEADING_FORM_RE = /^#{1,4}[ \t]*(?:\d+[a-z]?\.[ \t]*)?\*{0,2}Test Plan\*{0,2}[ \t]*$/im;
|
|
1653
|
+
var INLINE_FORM_RE = /^[ \t]*\*{0,2}Test Plan:?\*{0,2}/im;
|
|
1654
|
+
var NEXT_SECTION_RE = /\n(?:#{1,3}[ \t]|\*\*[A-Z][^*\n]*:\*\*)/;
|
|
1655
|
+
function locateTestPlanSection(body) {
|
|
1656
|
+
const region = anchoredRegion(body, "TEST-PLAN");
|
|
1657
|
+
if (region !== null)
|
|
1658
|
+
return { found: true, section: region };
|
|
1659
|
+
const headingMatch = HEADING_FORM_RE.exec(body);
|
|
1660
|
+
const match = headingMatch ?? INLINE_FORM_RE.exec(body);
|
|
1661
|
+
if (!match || match.index === undefined)
|
|
1662
|
+
return { found: false };
|
|
1663
|
+
const sectionBody = body.slice(match.index);
|
|
1664
|
+
const firstLineEnd = sectionBody.indexOf(`
|
|
1665
|
+
`);
|
|
1666
|
+
const searchFrom = firstLineEnd === -1 ? sectionBody.length : firstLineEnd + 1;
|
|
1667
|
+
const nextMatch = sectionBody.slice(searchFrom).match(NEXT_SECTION_RE);
|
|
1668
|
+
const sectionEnd = nextMatch ? searchFrom + (nextMatch.index ?? 0) : sectionBody.length;
|
|
1669
|
+
return { found: true, section: sectionBody.slice(0, sectionEnd) };
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1647
1672
|
// ../../../packages/aeg-core/src/brief-validation.ts
|
|
1648
1673
|
function headerRegion(prBody) {
|
|
1649
1674
|
const m = prBody.match(/^##\s/m);
|
|
@@ -1682,10 +1707,15 @@ function checkTierField(prBody, readTier) {
|
|
|
1682
1707
|
};
|
|
1683
1708
|
}
|
|
1684
1709
|
var TEST_PLAN_UNIT_TESTS_ONLY_RE = /(?:\*\*)?Test Plan(?:\*\*)?\s*:\s*(?:\*\*)?\s*unit-tests-only/i;
|
|
1710
|
+
function testPlanRegion(prBody) {
|
|
1711
|
+
const located = locateTestPlanSection(prBody);
|
|
1712
|
+
return located.found ? located.section : prBody;
|
|
1713
|
+
}
|
|
1685
1714
|
function checkTestPlan(prBody) {
|
|
1686
|
-
|
|
1715
|
+
const region = testPlanRegion(prBody);
|
|
1716
|
+
if (TEST_PLAN_UNIT_TESTS_ONLY_RE.test(region))
|
|
1687
1717
|
return { status: "pass", errors: [] };
|
|
1688
|
-
if (/\*\*\[(?:agent|principal)\]\*\*/.test(
|
|
1718
|
+
if (/\*\*\[(?:agent|principal)\]\*\*/.test(region))
|
|
1689
1719
|
return { status: "pass", errors: [] };
|
|
1690
1720
|
return {
|
|
1691
1721
|
status: "fail",
|
|
@@ -1695,9 +1725,10 @@ function checkTestPlan(prBody) {
|
|
|
1695
1725
|
};
|
|
1696
1726
|
}
|
|
1697
1727
|
function checkTestPlanExclusivity(prBody) {
|
|
1698
|
-
|
|
1728
|
+
const region = testPlanRegion(prBody);
|
|
1729
|
+
if (!TEST_PLAN_UNIT_TESTS_ONLY_RE.test(region))
|
|
1699
1730
|
return { status: "pass", errors: [] };
|
|
1700
|
-
if (/^-\s*\[[ xX]\]\s*\*{2}\[(?:agent|principal)\]\*{2}/im.test(
|
|
1731
|
+
if (/^-\s*\[[ xX]\]\s*\*{2}\[(?:agent|principal)\]\*{2}/im.test(region)) {
|
|
1701
1732
|
return {
|
|
1702
1733
|
status: "fail",
|
|
1703
1734
|
errors: [
|
|
@@ -1708,8 +1739,9 @@ function checkTestPlanExclusivity(prBody) {
|
|
|
1708
1739
|
return { status: "pass", errors: [] };
|
|
1709
1740
|
}
|
|
1710
1741
|
function checkPrincipalPlaceholder(prBody) {
|
|
1742
|
+
const region = testPlanRegion(prBody);
|
|
1711
1743
|
const lineRe = /^-\s*\[[ xX]\]\s*\*{2}\[principal\]\*{2}(.*)$/gim;
|
|
1712
|
-
for (const m of
|
|
1744
|
+
for (const m of region.matchAll(lineRe)) {
|
|
1713
1745
|
const content = m[1] ?? "";
|
|
1714
1746
|
if (/^\s*None\b/i.test(content)) {
|
|
1715
1747
|
return {
|
|
@@ -2157,11 +2189,12 @@ function isReviewGateExemptBranch(branch) {
|
|
|
2157
2189
|
return branch.startsWith("plan/");
|
|
2158
2190
|
}
|
|
2159
2191
|
function checkReviewGate(input) {
|
|
2192
|
+
const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
|
|
2160
2193
|
const waived = isWaiverLabelActorVerified({
|
|
2161
2194
|
label: WAIVER_LABEL_REVIEW,
|
|
2162
2195
|
labels: input.labels,
|
|
2163
2196
|
labelActor: input.waiverLabelActor,
|
|
2164
|
-
principalAllowlist
|
|
2197
|
+
principalAllowlist
|
|
2165
2198
|
});
|
|
2166
2199
|
if (waived) {
|
|
2167
2200
|
return {
|
|
@@ -2170,8 +2203,8 @@ function checkReviewGate(input) {
|
|
|
2170
2203
|
waived: true
|
|
2171
2204
|
};
|
|
2172
2205
|
}
|
|
2173
|
-
const verified = input.comments.filter((c) => c.author
|
|
2174
|
-
const ignoredCount = input.comments.filter((c) => (c.author
|
|
2206
|
+
const verified = input.comments.filter((c) => isPrincipal(c.author, principalAllowlist));
|
|
2207
|
+
const ignoredCount = input.comments.filter((c) => !isPrincipal(c.author, principalAllowlist) && c.body.includes("VERDICT")).length;
|
|
2175
2208
|
const verifiedBodies = verified.map((c) => c.body);
|
|
2176
2209
|
const codeReview = extractCodeReviewVerdict(verifiedBodies);
|
|
2177
2210
|
const security = extractSecurityReviewVerdict(verifiedBodies);
|
|
@@ -2724,26 +2757,6 @@ function decideIssueAssignment(input) {
|
|
|
2724
2757
|
reason: `First push of \`${branch}\` — assigning Issue #${issue} to @${login} (the authenticated pusher).`
|
|
2725
2758
|
};
|
|
2726
2759
|
}
|
|
2727
|
-
// ../../../packages/aeg-core/src/test-plan-section.ts
|
|
2728
|
-
var HEADING_FORM_RE = /^#{1,4}[ \t]*(?:\d+[a-z]?\.[ \t]*)?\*{0,2}Test Plan\*{0,2}[ \t]*$/im;
|
|
2729
|
-
var INLINE_FORM_RE = /^[ \t]*\*{0,2}Test Plan:?\*{0,2}/im;
|
|
2730
|
-
var NEXT_SECTION_RE = /\n(?:#{1,3}[ \t]|\*\*[A-Z][^*\n]*:\*\*)/;
|
|
2731
|
-
function locateTestPlanSection(body) {
|
|
2732
|
-
const region = anchoredRegion(body, "TEST-PLAN");
|
|
2733
|
-
if (region !== null)
|
|
2734
|
-
return { found: true, section: region };
|
|
2735
|
-
const headingMatch = HEADING_FORM_RE.exec(body);
|
|
2736
|
-
const match = headingMatch ?? INLINE_FORM_RE.exec(body);
|
|
2737
|
-
if (!match || match.index === undefined)
|
|
2738
|
-
return { found: false };
|
|
2739
|
-
const sectionBody = body.slice(match.index);
|
|
2740
|
-
const firstLineEnd = sectionBody.indexOf(`
|
|
2741
|
-
`);
|
|
2742
|
-
const searchFrom = firstLineEnd === -1 ? sectionBody.length : firstLineEnd + 1;
|
|
2743
|
-
const nextMatch = sectionBody.slice(searchFrom).match(NEXT_SECTION_RE);
|
|
2744
|
-
const sectionEnd = nextMatch ? searchFrom + (nextMatch.index ?? 0) : sectionBody.length;
|
|
2745
|
-
return { found: true, section: sectionBody.slice(0, sectionEnd) };
|
|
2746
|
-
}
|
|
2747
2760
|
// ../../../packages/aeg-core/src/test-plan-gate.ts
|
|
2748
2761
|
var TASK_BRANCH_PATTERN = /^task\/[^/]+\/[^/]+$/;
|
|
2749
2762
|
function evaluateTestPlanGate(body, branch) {
|
|
@@ -1448,12 +1448,16 @@ function isCodeFile(p) {
|
|
|
1448
1448
|
var WAIVER_LABEL = label("waiver-docs");
|
|
1449
1449
|
var WAIVER_LABEL_REVIEW = label("waiver-review");
|
|
1450
1450
|
var PRINCIPAL_ALLOWLIST = ["daniboomerang"];
|
|
1451
|
+
function isPrincipal(login, principalAllowlist) {
|
|
1452
|
+
if (login === null)
|
|
1453
|
+
return false;
|
|
1454
|
+
const normalized = login.toLowerCase();
|
|
1455
|
+
return principalAllowlist.some((p) => p.toLowerCase() === normalized);
|
|
1456
|
+
}
|
|
1451
1457
|
function isWaiverLabelActorVerified(opts) {
|
|
1452
1458
|
if (!opts.labels.includes(opts.label))
|
|
1453
1459
|
return false;
|
|
1454
|
-
|
|
1455
|
-
return false;
|
|
1456
|
-
return opts.principalAllowlist.includes(opts.labelActor);
|
|
1460
|
+
return isPrincipal(opts.labelActor, opts.principalAllowlist);
|
|
1457
1461
|
}
|
|
1458
1462
|
|
|
1459
1463
|
// ../../../packages/aeg-core/src/doc-owners.ts
|
|
@@ -1641,6 +1645,27 @@ function overrideActive(opts) {
|
|
|
1641
1645
|
// ../../../packages/aeg-core/src/premise-check.ts
|
|
1642
1646
|
var ASSERTION_KINDS = new Set(["contains", "absent", "sha256"]);
|
|
1643
1647
|
|
|
1648
|
+
// ../../../packages/aeg-core/src/test-plan-section.ts
|
|
1649
|
+
var HEADING_FORM_RE = /^#{1,4}[ \t]*(?:\d+[a-z]?\.[ \t]*)?\*{0,2}Test Plan\*{0,2}[ \t]*$/im;
|
|
1650
|
+
var INLINE_FORM_RE = /^[ \t]*\*{0,2}Test Plan:?\*{0,2}/im;
|
|
1651
|
+
var NEXT_SECTION_RE = /\n(?:#{1,3}[ \t]|\*\*[A-Z][^*\n]*:\*\*)/;
|
|
1652
|
+
function locateTestPlanSection(body) {
|
|
1653
|
+
const region = anchoredRegion(body, "TEST-PLAN");
|
|
1654
|
+
if (region !== null)
|
|
1655
|
+
return { found: true, section: region };
|
|
1656
|
+
const headingMatch = HEADING_FORM_RE.exec(body);
|
|
1657
|
+
const match = headingMatch ?? INLINE_FORM_RE.exec(body);
|
|
1658
|
+
if (!match || match.index === undefined)
|
|
1659
|
+
return { found: false };
|
|
1660
|
+
const sectionBody = body.slice(match.index);
|
|
1661
|
+
const firstLineEnd = sectionBody.indexOf(`
|
|
1662
|
+
`);
|
|
1663
|
+
const searchFrom = firstLineEnd === -1 ? sectionBody.length : firstLineEnd + 1;
|
|
1664
|
+
const nextMatch = sectionBody.slice(searchFrom).match(NEXT_SECTION_RE);
|
|
1665
|
+
const sectionEnd = nextMatch ? searchFrom + (nextMatch.index ?? 0) : sectionBody.length;
|
|
1666
|
+
return { found: true, section: sectionBody.slice(0, sectionEnd) };
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1644
1669
|
// ../../../packages/aeg-core/src/brief-validation.ts
|
|
1645
1670
|
function headerRegion(prBody) {
|
|
1646
1671
|
const m = prBody.match(/^##\s/m);
|
|
@@ -1679,10 +1704,15 @@ function checkTierField(prBody, readTier) {
|
|
|
1679
1704
|
};
|
|
1680
1705
|
}
|
|
1681
1706
|
var TEST_PLAN_UNIT_TESTS_ONLY_RE = /(?:\*\*)?Test Plan(?:\*\*)?\s*:\s*(?:\*\*)?\s*unit-tests-only/i;
|
|
1707
|
+
function testPlanRegion(prBody) {
|
|
1708
|
+
const located = locateTestPlanSection(prBody);
|
|
1709
|
+
return located.found ? located.section : prBody;
|
|
1710
|
+
}
|
|
1682
1711
|
function checkTestPlan(prBody) {
|
|
1683
|
-
|
|
1712
|
+
const region = testPlanRegion(prBody);
|
|
1713
|
+
if (TEST_PLAN_UNIT_TESTS_ONLY_RE.test(region))
|
|
1684
1714
|
return { status: "pass", errors: [] };
|
|
1685
|
-
if (/\*\*\[(?:agent|principal)\]\*\*/.test(
|
|
1715
|
+
if (/\*\*\[(?:agent|principal)\]\*\*/.test(region))
|
|
1686
1716
|
return { status: "pass", errors: [] };
|
|
1687
1717
|
return {
|
|
1688
1718
|
status: "fail",
|
|
@@ -1692,9 +1722,10 @@ function checkTestPlan(prBody) {
|
|
|
1692
1722
|
};
|
|
1693
1723
|
}
|
|
1694
1724
|
function checkTestPlanExclusivity(prBody) {
|
|
1695
|
-
|
|
1725
|
+
const region = testPlanRegion(prBody);
|
|
1726
|
+
if (!TEST_PLAN_UNIT_TESTS_ONLY_RE.test(region))
|
|
1696
1727
|
return { status: "pass", errors: [] };
|
|
1697
|
-
if (/^-\s*\[[ xX]\]\s*\*{2}\[(?:agent|principal)\]\*{2}/im.test(
|
|
1728
|
+
if (/^-\s*\[[ xX]\]\s*\*{2}\[(?:agent|principal)\]\*{2}/im.test(region)) {
|
|
1698
1729
|
return {
|
|
1699
1730
|
status: "fail",
|
|
1700
1731
|
errors: [
|
|
@@ -1705,8 +1736,9 @@ function checkTestPlanExclusivity(prBody) {
|
|
|
1705
1736
|
return { status: "pass", errors: [] };
|
|
1706
1737
|
}
|
|
1707
1738
|
function checkPrincipalPlaceholder(prBody) {
|
|
1739
|
+
const region = testPlanRegion(prBody);
|
|
1708
1740
|
const lineRe = /^-\s*\[[ xX]\]\s*\*{2}\[principal\]\*{2}(.*)$/gim;
|
|
1709
|
-
for (const m of
|
|
1741
|
+
for (const m of region.matchAll(lineRe)) {
|
|
1710
1742
|
const content = m[1] ?? "";
|
|
1711
1743
|
if (/^\s*None\b/i.test(content)) {
|
|
1712
1744
|
return {
|
|
@@ -2154,11 +2186,12 @@ function isReviewGateExemptBranch(branch) {
|
|
|
2154
2186
|
return branch.startsWith("plan/");
|
|
2155
2187
|
}
|
|
2156
2188
|
function checkReviewGate(input) {
|
|
2189
|
+
const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
|
|
2157
2190
|
const waived = isWaiverLabelActorVerified({
|
|
2158
2191
|
label: WAIVER_LABEL_REVIEW,
|
|
2159
2192
|
labels: input.labels,
|
|
2160
2193
|
labelActor: input.waiverLabelActor,
|
|
2161
|
-
principalAllowlist
|
|
2194
|
+
principalAllowlist
|
|
2162
2195
|
});
|
|
2163
2196
|
if (waived) {
|
|
2164
2197
|
return {
|
|
@@ -2167,8 +2200,8 @@ function checkReviewGate(input) {
|
|
|
2167
2200
|
waived: true
|
|
2168
2201
|
};
|
|
2169
2202
|
}
|
|
2170
|
-
const verified = input.comments.filter((c) => c.author
|
|
2171
|
-
const ignoredCount = input.comments.filter((c) => (c.author
|
|
2203
|
+
const verified = input.comments.filter((c) => isPrincipal(c.author, principalAllowlist));
|
|
2204
|
+
const ignoredCount = input.comments.filter((c) => !isPrincipal(c.author, principalAllowlist) && c.body.includes("VERDICT")).length;
|
|
2172
2205
|
const verifiedBodies = verified.map((c) => c.body);
|
|
2173
2206
|
const codeReview = extractCodeReviewVerdict(verifiedBodies);
|
|
2174
2207
|
const security = extractSecurityReviewVerdict(verifiedBodies);
|
|
@@ -2721,26 +2754,6 @@ function decideIssueAssignment(input) {
|
|
|
2721
2754
|
reason: `First push of \`${branch}\` — assigning Issue #${issue} to @${login} (the authenticated pusher).`
|
|
2722
2755
|
};
|
|
2723
2756
|
}
|
|
2724
|
-
// ../../../packages/aeg-core/src/test-plan-section.ts
|
|
2725
|
-
var HEADING_FORM_RE = /^#{1,4}[ \t]*(?:\d+[a-z]?\.[ \t]*)?\*{0,2}Test Plan\*{0,2}[ \t]*$/im;
|
|
2726
|
-
var INLINE_FORM_RE = /^[ \t]*\*{0,2}Test Plan:?\*{0,2}/im;
|
|
2727
|
-
var NEXT_SECTION_RE = /\n(?:#{1,3}[ \t]|\*\*[A-Z][^*\n]*:\*\*)/;
|
|
2728
|
-
function locateTestPlanSection(body) {
|
|
2729
|
-
const region = anchoredRegion(body, "TEST-PLAN");
|
|
2730
|
-
if (region !== null)
|
|
2731
|
-
return { found: true, section: region };
|
|
2732
|
-
const headingMatch = HEADING_FORM_RE.exec(body);
|
|
2733
|
-
const match = headingMatch ?? INLINE_FORM_RE.exec(body);
|
|
2734
|
-
if (!match || match.index === undefined)
|
|
2735
|
-
return { found: false };
|
|
2736
|
-
const sectionBody = body.slice(match.index);
|
|
2737
|
-
const firstLineEnd = sectionBody.indexOf(`
|
|
2738
|
-
`);
|
|
2739
|
-
const searchFrom = firstLineEnd === -1 ? sectionBody.length : firstLineEnd + 1;
|
|
2740
|
-
const nextMatch = sectionBody.slice(searchFrom).match(NEXT_SECTION_RE);
|
|
2741
|
-
const sectionEnd = nextMatch ? searchFrom + (nextMatch.index ?? 0) : sectionBody.length;
|
|
2742
|
-
return { found: true, section: sectionBody.slice(0, sectionEnd) };
|
|
2743
|
-
}
|
|
2744
2757
|
// ../../../packages/aeg-core/src/test-plan-gate.ts
|
|
2745
2758
|
var TASK_BRANCH_PATTERN = /^task\/[^/]+\/[^/]+$/;
|
|
2746
2759
|
function evaluateTestPlanGate(body, branch) {
|
|
@@ -1451,12 +1451,16 @@ function isCodeFile(p) {
|
|
|
1451
1451
|
var WAIVER_LABEL = label("waiver-docs");
|
|
1452
1452
|
var WAIVER_LABEL_REVIEW = label("waiver-review");
|
|
1453
1453
|
var PRINCIPAL_ALLOWLIST = ["daniboomerang"];
|
|
1454
|
+
function isPrincipal(login, principalAllowlist) {
|
|
1455
|
+
if (login === null)
|
|
1456
|
+
return false;
|
|
1457
|
+
const normalized = login.toLowerCase();
|
|
1458
|
+
return principalAllowlist.some((p) => p.toLowerCase() === normalized);
|
|
1459
|
+
}
|
|
1454
1460
|
function isWaiverLabelActorVerified(opts) {
|
|
1455
1461
|
if (!opts.labels.includes(opts.label))
|
|
1456
1462
|
return false;
|
|
1457
|
-
|
|
1458
|
-
return false;
|
|
1459
|
-
return opts.principalAllowlist.includes(opts.labelActor);
|
|
1463
|
+
return isPrincipal(opts.labelActor, opts.principalAllowlist);
|
|
1460
1464
|
}
|
|
1461
1465
|
|
|
1462
1466
|
// ../../../packages/aeg-core/src/doc-owners.ts
|
|
@@ -1644,6 +1648,27 @@ function overrideActive(opts) {
|
|
|
1644
1648
|
// ../../../packages/aeg-core/src/premise-check.ts
|
|
1645
1649
|
var ASSERTION_KINDS = new Set(["contains", "absent", "sha256"]);
|
|
1646
1650
|
|
|
1651
|
+
// ../../../packages/aeg-core/src/test-plan-section.ts
|
|
1652
|
+
var HEADING_FORM_RE = /^#{1,4}[ \t]*(?:\d+[a-z]?\.[ \t]*)?\*{0,2}Test Plan\*{0,2}[ \t]*$/im;
|
|
1653
|
+
var INLINE_FORM_RE = /^[ \t]*\*{0,2}Test Plan:?\*{0,2}/im;
|
|
1654
|
+
var NEXT_SECTION_RE = /\n(?:#{1,3}[ \t]|\*\*[A-Z][^*\n]*:\*\*)/;
|
|
1655
|
+
function locateTestPlanSection(body) {
|
|
1656
|
+
const region = anchoredRegion(body, "TEST-PLAN");
|
|
1657
|
+
if (region !== null)
|
|
1658
|
+
return { found: true, section: region };
|
|
1659
|
+
const headingMatch = HEADING_FORM_RE.exec(body);
|
|
1660
|
+
const match = headingMatch ?? INLINE_FORM_RE.exec(body);
|
|
1661
|
+
if (!match || match.index === undefined)
|
|
1662
|
+
return { found: false };
|
|
1663
|
+
const sectionBody = body.slice(match.index);
|
|
1664
|
+
const firstLineEnd = sectionBody.indexOf(`
|
|
1665
|
+
`);
|
|
1666
|
+
const searchFrom = firstLineEnd === -1 ? sectionBody.length : firstLineEnd + 1;
|
|
1667
|
+
const nextMatch = sectionBody.slice(searchFrom).match(NEXT_SECTION_RE);
|
|
1668
|
+
const sectionEnd = nextMatch ? searchFrom + (nextMatch.index ?? 0) : sectionBody.length;
|
|
1669
|
+
return { found: true, section: sectionBody.slice(0, sectionEnd) };
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1647
1672
|
// ../../../packages/aeg-core/src/brief-validation.ts
|
|
1648
1673
|
function headerRegion(prBody) {
|
|
1649
1674
|
const m = prBody.match(/^##\s/m);
|
|
@@ -1682,10 +1707,15 @@ function checkTierField(prBody, readTier) {
|
|
|
1682
1707
|
};
|
|
1683
1708
|
}
|
|
1684
1709
|
var TEST_PLAN_UNIT_TESTS_ONLY_RE = /(?:\*\*)?Test Plan(?:\*\*)?\s*:\s*(?:\*\*)?\s*unit-tests-only/i;
|
|
1710
|
+
function testPlanRegion(prBody) {
|
|
1711
|
+
const located = locateTestPlanSection(prBody);
|
|
1712
|
+
return located.found ? located.section : prBody;
|
|
1713
|
+
}
|
|
1685
1714
|
function checkTestPlan(prBody) {
|
|
1686
|
-
|
|
1715
|
+
const region = testPlanRegion(prBody);
|
|
1716
|
+
if (TEST_PLAN_UNIT_TESTS_ONLY_RE.test(region))
|
|
1687
1717
|
return { status: "pass", errors: [] };
|
|
1688
|
-
if (/\*\*\[(?:agent|principal)\]\*\*/.test(
|
|
1718
|
+
if (/\*\*\[(?:agent|principal)\]\*\*/.test(region))
|
|
1689
1719
|
return { status: "pass", errors: [] };
|
|
1690
1720
|
return {
|
|
1691
1721
|
status: "fail",
|
|
@@ -1695,9 +1725,10 @@ function checkTestPlan(prBody) {
|
|
|
1695
1725
|
};
|
|
1696
1726
|
}
|
|
1697
1727
|
function checkTestPlanExclusivity(prBody) {
|
|
1698
|
-
|
|
1728
|
+
const region = testPlanRegion(prBody);
|
|
1729
|
+
if (!TEST_PLAN_UNIT_TESTS_ONLY_RE.test(region))
|
|
1699
1730
|
return { status: "pass", errors: [] };
|
|
1700
|
-
if (/^-\s*\[[ xX]\]\s*\*{2}\[(?:agent|principal)\]\*{2}/im.test(
|
|
1731
|
+
if (/^-\s*\[[ xX]\]\s*\*{2}\[(?:agent|principal)\]\*{2}/im.test(region)) {
|
|
1701
1732
|
return {
|
|
1702
1733
|
status: "fail",
|
|
1703
1734
|
errors: [
|
|
@@ -1708,8 +1739,9 @@ function checkTestPlanExclusivity(prBody) {
|
|
|
1708
1739
|
return { status: "pass", errors: [] };
|
|
1709
1740
|
}
|
|
1710
1741
|
function checkPrincipalPlaceholder(prBody) {
|
|
1742
|
+
const region = testPlanRegion(prBody);
|
|
1711
1743
|
const lineRe = /^-\s*\[[ xX]\]\s*\*{2}\[principal\]\*{2}(.*)$/gim;
|
|
1712
|
-
for (const m of
|
|
1744
|
+
for (const m of region.matchAll(lineRe)) {
|
|
1713
1745
|
const content = m[1] ?? "";
|
|
1714
1746
|
if (/^\s*None\b/i.test(content)) {
|
|
1715
1747
|
return {
|
|
@@ -2157,11 +2189,12 @@ function isReviewGateExemptBranch(branch) {
|
|
|
2157
2189
|
return branch.startsWith("plan/");
|
|
2158
2190
|
}
|
|
2159
2191
|
function checkReviewGate(input) {
|
|
2192
|
+
const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
|
|
2160
2193
|
const waived = isWaiverLabelActorVerified({
|
|
2161
2194
|
label: WAIVER_LABEL_REVIEW,
|
|
2162
2195
|
labels: input.labels,
|
|
2163
2196
|
labelActor: input.waiverLabelActor,
|
|
2164
|
-
principalAllowlist
|
|
2197
|
+
principalAllowlist
|
|
2165
2198
|
});
|
|
2166
2199
|
if (waived) {
|
|
2167
2200
|
return {
|
|
@@ -2170,8 +2203,8 @@ function checkReviewGate(input) {
|
|
|
2170
2203
|
waived: true
|
|
2171
2204
|
};
|
|
2172
2205
|
}
|
|
2173
|
-
const verified = input.comments.filter((c) => c.author
|
|
2174
|
-
const ignoredCount = input.comments.filter((c) => (c.author
|
|
2206
|
+
const verified = input.comments.filter((c) => isPrincipal(c.author, principalAllowlist));
|
|
2207
|
+
const ignoredCount = input.comments.filter((c) => !isPrincipal(c.author, principalAllowlist) && c.body.includes("VERDICT")).length;
|
|
2175
2208
|
const verifiedBodies = verified.map((c) => c.body);
|
|
2176
2209
|
const codeReview = extractCodeReviewVerdict(verifiedBodies);
|
|
2177
2210
|
const security = extractSecurityReviewVerdict(verifiedBodies);
|
|
@@ -2724,26 +2757,6 @@ function decideIssueAssignment(input) {
|
|
|
2724
2757
|
reason: `First push of \`${branch}\` — assigning Issue #${issue} to @${login} (the authenticated pusher).`
|
|
2725
2758
|
};
|
|
2726
2759
|
}
|
|
2727
|
-
// ../../../packages/aeg-core/src/test-plan-section.ts
|
|
2728
|
-
var HEADING_FORM_RE = /^#{1,4}[ \t]*(?:\d+[a-z]?\.[ \t]*)?\*{0,2}Test Plan\*{0,2}[ \t]*$/im;
|
|
2729
|
-
var INLINE_FORM_RE = /^[ \t]*\*{0,2}Test Plan:?\*{0,2}/im;
|
|
2730
|
-
var NEXT_SECTION_RE = /\n(?:#{1,3}[ \t]|\*\*[A-Z][^*\n]*:\*\*)/;
|
|
2731
|
-
function locateTestPlanSection(body) {
|
|
2732
|
-
const region = anchoredRegion(body, "TEST-PLAN");
|
|
2733
|
-
if (region !== null)
|
|
2734
|
-
return { found: true, section: region };
|
|
2735
|
-
const headingMatch = HEADING_FORM_RE.exec(body);
|
|
2736
|
-
const match = headingMatch ?? INLINE_FORM_RE.exec(body);
|
|
2737
|
-
if (!match || match.index === undefined)
|
|
2738
|
-
return { found: false };
|
|
2739
|
-
const sectionBody = body.slice(match.index);
|
|
2740
|
-
const firstLineEnd = sectionBody.indexOf(`
|
|
2741
|
-
`);
|
|
2742
|
-
const searchFrom = firstLineEnd === -1 ? sectionBody.length : firstLineEnd + 1;
|
|
2743
|
-
const nextMatch = sectionBody.slice(searchFrom).match(NEXT_SECTION_RE);
|
|
2744
|
-
const sectionEnd = nextMatch ? searchFrom + (nextMatch.index ?? 0) : sectionBody.length;
|
|
2745
|
-
return { found: true, section: sectionBody.slice(0, sectionEnd) };
|
|
2746
|
-
}
|
|
2747
2760
|
// ../../../packages/aeg-core/src/test-plan-gate.ts
|
|
2748
2761
|
var TASK_BRANCH_PATTERN = /^task\/[^/]+\/[^/]+$/;
|
|
2749
2762
|
function evaluateTestPlanGate(body, branch) {
|
|
@@ -1452,12 +1452,16 @@ function isCodeFile(p) {
|
|
|
1452
1452
|
var WAIVER_LABEL = label("waiver-docs");
|
|
1453
1453
|
var WAIVER_LABEL_REVIEW = label("waiver-review");
|
|
1454
1454
|
var PRINCIPAL_ALLOWLIST = ["daniboomerang"];
|
|
1455
|
+
function isPrincipal(login, principalAllowlist) {
|
|
1456
|
+
if (login === null)
|
|
1457
|
+
return false;
|
|
1458
|
+
const normalized = login.toLowerCase();
|
|
1459
|
+
return principalAllowlist.some((p) => p.toLowerCase() === normalized);
|
|
1460
|
+
}
|
|
1455
1461
|
function isWaiverLabelActorVerified(opts) {
|
|
1456
1462
|
if (!opts.labels.includes(opts.label))
|
|
1457
1463
|
return false;
|
|
1458
|
-
|
|
1459
|
-
return false;
|
|
1460
|
-
return opts.principalAllowlist.includes(opts.labelActor);
|
|
1464
|
+
return isPrincipal(opts.labelActor, opts.principalAllowlist);
|
|
1461
1465
|
}
|
|
1462
1466
|
|
|
1463
1467
|
// ../../../packages/aeg-core/src/doc-owners.ts
|
|
@@ -1645,6 +1649,27 @@ function overrideActive(opts) {
|
|
|
1645
1649
|
// ../../../packages/aeg-core/src/premise-check.ts
|
|
1646
1650
|
var ASSERTION_KINDS = new Set(["contains", "absent", "sha256"]);
|
|
1647
1651
|
|
|
1652
|
+
// ../../../packages/aeg-core/src/test-plan-section.ts
|
|
1653
|
+
var HEADING_FORM_RE = /^#{1,4}[ \t]*(?:\d+[a-z]?\.[ \t]*)?\*{0,2}Test Plan\*{0,2}[ \t]*$/im;
|
|
1654
|
+
var INLINE_FORM_RE = /^[ \t]*\*{0,2}Test Plan:?\*{0,2}/im;
|
|
1655
|
+
var NEXT_SECTION_RE = /\n(?:#{1,3}[ \t]|\*\*[A-Z][^*\n]*:\*\*)/;
|
|
1656
|
+
function locateTestPlanSection(body) {
|
|
1657
|
+
const region = anchoredRegion(body, "TEST-PLAN");
|
|
1658
|
+
if (region !== null)
|
|
1659
|
+
return { found: true, section: region };
|
|
1660
|
+
const headingMatch = HEADING_FORM_RE.exec(body);
|
|
1661
|
+
const match = headingMatch ?? INLINE_FORM_RE.exec(body);
|
|
1662
|
+
if (!match || match.index === undefined)
|
|
1663
|
+
return { found: false };
|
|
1664
|
+
const sectionBody = body.slice(match.index);
|
|
1665
|
+
const firstLineEnd = sectionBody.indexOf(`
|
|
1666
|
+
`);
|
|
1667
|
+
const searchFrom = firstLineEnd === -1 ? sectionBody.length : firstLineEnd + 1;
|
|
1668
|
+
const nextMatch = sectionBody.slice(searchFrom).match(NEXT_SECTION_RE);
|
|
1669
|
+
const sectionEnd = nextMatch ? searchFrom + (nextMatch.index ?? 0) : sectionBody.length;
|
|
1670
|
+
return { found: true, section: sectionBody.slice(0, sectionEnd) };
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1648
1673
|
// ../../../packages/aeg-core/src/brief-validation.ts
|
|
1649
1674
|
function headerRegion(prBody) {
|
|
1650
1675
|
const m = prBody.match(/^##\s/m);
|
|
@@ -1683,10 +1708,15 @@ function checkTierField(prBody, readTier) {
|
|
|
1683
1708
|
};
|
|
1684
1709
|
}
|
|
1685
1710
|
var TEST_PLAN_UNIT_TESTS_ONLY_RE = /(?:\*\*)?Test Plan(?:\*\*)?\s*:\s*(?:\*\*)?\s*unit-tests-only/i;
|
|
1711
|
+
function testPlanRegion(prBody) {
|
|
1712
|
+
const located = locateTestPlanSection(prBody);
|
|
1713
|
+
return located.found ? located.section : prBody;
|
|
1714
|
+
}
|
|
1686
1715
|
function checkTestPlan(prBody) {
|
|
1687
|
-
|
|
1716
|
+
const region = testPlanRegion(prBody);
|
|
1717
|
+
if (TEST_PLAN_UNIT_TESTS_ONLY_RE.test(region))
|
|
1688
1718
|
return { status: "pass", errors: [] };
|
|
1689
|
-
if (/\*\*\[(?:agent|principal)\]\*\*/.test(
|
|
1719
|
+
if (/\*\*\[(?:agent|principal)\]\*\*/.test(region))
|
|
1690
1720
|
return { status: "pass", errors: [] };
|
|
1691
1721
|
return {
|
|
1692
1722
|
status: "fail",
|
|
@@ -1696,9 +1726,10 @@ function checkTestPlan(prBody) {
|
|
|
1696
1726
|
};
|
|
1697
1727
|
}
|
|
1698
1728
|
function checkTestPlanExclusivity(prBody) {
|
|
1699
|
-
|
|
1729
|
+
const region = testPlanRegion(prBody);
|
|
1730
|
+
if (!TEST_PLAN_UNIT_TESTS_ONLY_RE.test(region))
|
|
1700
1731
|
return { status: "pass", errors: [] };
|
|
1701
|
-
if (/^-\s*\[[ xX]\]\s*\*{2}\[(?:agent|principal)\]\*{2}/im.test(
|
|
1732
|
+
if (/^-\s*\[[ xX]\]\s*\*{2}\[(?:agent|principal)\]\*{2}/im.test(region)) {
|
|
1702
1733
|
return {
|
|
1703
1734
|
status: "fail",
|
|
1704
1735
|
errors: [
|
|
@@ -1709,8 +1740,9 @@ function checkTestPlanExclusivity(prBody) {
|
|
|
1709
1740
|
return { status: "pass", errors: [] };
|
|
1710
1741
|
}
|
|
1711
1742
|
function checkPrincipalPlaceholder(prBody) {
|
|
1743
|
+
const region = testPlanRegion(prBody);
|
|
1712
1744
|
const lineRe = /^-\s*\[[ xX]\]\s*\*{2}\[principal\]\*{2}(.*)$/gim;
|
|
1713
|
-
for (const m of
|
|
1745
|
+
for (const m of region.matchAll(lineRe)) {
|
|
1714
1746
|
const content = m[1] ?? "";
|
|
1715
1747
|
if (/^\s*None\b/i.test(content)) {
|
|
1716
1748
|
return {
|
|
@@ -2158,11 +2190,12 @@ function isReviewGateExemptBranch(branch) {
|
|
|
2158
2190
|
return branch.startsWith("plan/");
|
|
2159
2191
|
}
|
|
2160
2192
|
function checkReviewGate(input) {
|
|
2193
|
+
const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
|
|
2161
2194
|
const waived = isWaiverLabelActorVerified({
|
|
2162
2195
|
label: WAIVER_LABEL_REVIEW,
|
|
2163
2196
|
labels: input.labels,
|
|
2164
2197
|
labelActor: input.waiverLabelActor,
|
|
2165
|
-
principalAllowlist
|
|
2198
|
+
principalAllowlist
|
|
2166
2199
|
});
|
|
2167
2200
|
if (waived) {
|
|
2168
2201
|
return {
|
|
@@ -2171,8 +2204,8 @@ function checkReviewGate(input) {
|
|
|
2171
2204
|
waived: true
|
|
2172
2205
|
};
|
|
2173
2206
|
}
|
|
2174
|
-
const verified = input.comments.filter((c) => c.author
|
|
2175
|
-
const ignoredCount = input.comments.filter((c) => (c.author
|
|
2207
|
+
const verified = input.comments.filter((c) => isPrincipal(c.author, principalAllowlist));
|
|
2208
|
+
const ignoredCount = input.comments.filter((c) => !isPrincipal(c.author, principalAllowlist) && c.body.includes("VERDICT")).length;
|
|
2176
2209
|
const verifiedBodies = verified.map((c) => c.body);
|
|
2177
2210
|
const codeReview = extractCodeReviewVerdict(verifiedBodies);
|
|
2178
2211
|
const security = extractSecurityReviewVerdict(verifiedBodies);
|
|
@@ -2725,26 +2758,6 @@ function decideIssueAssignment(input) {
|
|
|
2725
2758
|
reason: `First push of \`${branch}\` — assigning Issue #${issue} to @${login} (the authenticated pusher).`
|
|
2726
2759
|
};
|
|
2727
2760
|
}
|
|
2728
|
-
// ../../../packages/aeg-core/src/test-plan-section.ts
|
|
2729
|
-
var HEADING_FORM_RE = /^#{1,4}[ \t]*(?:\d+[a-z]?\.[ \t]*)?\*{0,2}Test Plan\*{0,2}[ \t]*$/im;
|
|
2730
|
-
var INLINE_FORM_RE = /^[ \t]*\*{0,2}Test Plan:?\*{0,2}/im;
|
|
2731
|
-
var NEXT_SECTION_RE = /\n(?:#{1,3}[ \t]|\*\*[A-Z][^*\n]*:\*\*)/;
|
|
2732
|
-
function locateTestPlanSection(body) {
|
|
2733
|
-
const region = anchoredRegion(body, "TEST-PLAN");
|
|
2734
|
-
if (region !== null)
|
|
2735
|
-
return { found: true, section: region };
|
|
2736
|
-
const headingMatch = HEADING_FORM_RE.exec(body);
|
|
2737
|
-
const match = headingMatch ?? INLINE_FORM_RE.exec(body);
|
|
2738
|
-
if (!match || match.index === undefined)
|
|
2739
|
-
return { found: false };
|
|
2740
|
-
const sectionBody = body.slice(match.index);
|
|
2741
|
-
const firstLineEnd = sectionBody.indexOf(`
|
|
2742
|
-
`);
|
|
2743
|
-
const searchFrom = firstLineEnd === -1 ? sectionBody.length : firstLineEnd + 1;
|
|
2744
|
-
const nextMatch = sectionBody.slice(searchFrom).match(NEXT_SECTION_RE);
|
|
2745
|
-
const sectionEnd = nextMatch ? searchFrom + (nextMatch.index ?? 0) : sectionBody.length;
|
|
2746
|
-
return { found: true, section: sectionBody.slice(0, sectionEnd) };
|
|
2747
|
-
}
|
|
2748
2761
|
// ../../../packages/aeg-core/src/test-plan-gate.ts
|
|
2749
2762
|
var TASK_BRANCH_PATTERN = /^task\/[^/]+\/[^/]+$/;
|
|
2750
2763
|
function evaluateTestPlanGate(body, branch) {
|