@attalabs/vinaya 0.3.0 → 0.4.1
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 +4 -3
- package/aeg-root/glossary.md +4 -0
- package/aeg-root/roles/reviewer.md +1 -1
- package/aeg-root/roles/security.md +1 -1
- package/dist/checks/bin/check-branch-topology.js +7 -3
- package/dist/checks/bin/check-brief-shape.js +7 -3
- package/dist/checks/bin/check-closes-n.js +7 -3
- package/dist/checks/bin/check-coherence.js +7 -3
- package/dist/checks/bin/check-dead-branch-push.js +7 -3
- package/dist/checks/bin/check-dispatch-readiness.js +7 -3
- package/dist/checks/bin/check-doc-coverage-push.js +7 -3
- package/dist/checks/bin/check-doc-coverage.js +7 -3
- package/dist/checks/bin/check-first-push-dispatch.js +7 -3
- package/dist/checks/bin/check-issue-assignment.js +7 -3
- package/dist/checks/bin/check-no-disk-state.js +7 -3
- package/dist/checks/bin/check-reader-resolvable-prose.js +7 -3
- package/dist/checks/bin/check-registry-gates.js +7 -3
- package/dist/checks/bin/check-review-gate.js +12 -4
- package/dist/checks/bin/check-single-plan-pr.js +7 -3
- package/dist/checks/bin/check-test-plan.js +7 -3
- package/dist/index.js +363 -65
- package/package.json +1 -1
package/aeg-root/enforcement.md
CHANGED
|
@@ -44,7 +44,8 @@ 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, so
|
|
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). 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` |
|
|
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` |
|
|
48
49
|
|
|
49
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.
|
|
50
51
|
|
|
@@ -72,8 +73,8 @@ Every pull request, on open and on every push, re-runs the same checks in CI:
|
|
|
72
73
|
| Test-plan state | Ever merged a PR with an unchecked "did you test this" box? | ci | Re-checks that the pull request’s test plan is genuinely ticked. | Unticked test-plan boxes block merge readiness | `packages/aeg-core/bin/verify-test-plan.ts` |
|
|
73
74
|
| Typecheck + unit tests | Ever had a change silently break something it wasn't even touching? | ci | Re-runs the type checker and the unit tests for every package this change can reach. | Packages affected by this PR's diff, plus their full transitive-dependent set (`turbo --affected`; application builds are verified by the deployment pipeline). A diff touching no package (docs/workflow-only) runs 0 packages — sound, since repo-file gates like `verify-docs`/coherence run in their own jobs regardless (task 27) | `.github/workflows/ci.yml` |
|
|
74
75
|
| Conventions | Ever opened a PR full of inconsistent formatting and naming? | ci | Re-checks formatting and naming against the repo’s own conventions. | Lint/format, commit-message grammar, no hardcoded UI colors | `.github/workflows/forge-lifecycle.yml` |
|
|
75
|
-
| AI review | Ever wished every PR got a second pair of eyes, even at 2am? | ci | Puts an automated reviewer on every pull request, whatever the hour. | Posts a judgment review of the change.
|
|
76
|
-
| Review gate | Ever had a PR merge with nobody actually approving it? | ci | Holds the merge until the required review verdicts actually exist. | Required, blocking: a task-branch PR must carry a clean code-reviewer `APPROVE` verdict comment AND a clean security-review `PASS` verdict comment before merge — reusing `extractCodeReviewVerdict`/`extractSecurityReviewVerdict` (`packages/aeg-core/src/verdict-extraction.ts`), extracted out of the post-merge Archivist's `archive-task.ts` so both the pre-merge gate and the post-merge provenance assembly read the identical detection logic (one implementation, two call sites). Picks the MOST RECENT comment carrying a clear value, not the first comment merely matching the loose marker pattern — fixed live during this task, since the original single-verdict `.find()` broke on real multi-comment PRs (a `REQUEST_CHANGES` verdict followed by fixes and a later clean `APPROVE`, or the post-merge provenance comment's own DANGLING-note prose, which contains the word "verdict" and would otherwise poison a naive first-match search). A principal-actor-verified `vinaya/waiver:review` label (the same `isWaiverLabelActorVerified` pattern, now parameterized by label name and reused — not duplicated — for this second label) skips the requirement for one PR; label presence alone is never sufficient. **Tool-layer protection extended** (`check-forge-gates.sh`): mutating `vinaya/waiver:review` from an agent session is denied, exactly like `vinaya/waiver:docs` — without this, an agent sharing the Principal's own `gh` credential could self-apply the waiver and inherit the Principal's identity on the labeling event, defeating actor verification entirely (confirmed live during this task before the hook was extended). Non-task branches (plan PRs) bypass — a plan PR touching only topology files has no code to review. Going-forward only: never re-evaluates already-merged history. | `packages/aeg-core/bin/verify-review-gate.ts` |
|
|
76
|
+
| AI review | Ever wished every PR got a second pair of eyes, even at 2am? | ci | Puts an automated reviewer on every pull request, whatever the hour. | Posts a judgment review of the change. **Advisory only, including its verdict markers** (ruled 2026-08-09, verdict-author verification): the Review gate below counts a verdict comment only when its author is on the principal allowlist, and this job posts under a bot identity — deliberately excluded, because the job reads the PR's own content, so a crafted PR can steer what it writes; letting its `APPROVE` gate merges would re-open the forged-verdict channel author verification closes. In practice this changes nothing: the gate has only ever flipped on principal-identity verdicts. The bot's review remains visible input to the human/dispatched reviews | `.github/workflows/claude-code-review.yml` |
|
|
77
|
+
| Review gate | Ever had a PR merge with nobody actually approving it? | ci | Holds the merge until the required review verdicts actually exist. | Required, blocking: a task-branch PR must carry a clean code-reviewer `APPROVE` verdict comment AND a clean security-review `PASS` verdict comment before merge — reusing `extractCodeReviewVerdict`/`extractSecurityReviewVerdict` (`packages/aeg-core/src/verdict-extraction.ts`), extracted out of the post-merge Archivist's `archive-task.ts` so both the pre-merge gate and the post-merge provenance assembly read the identical detection logic (one implementation, two call sites). Picks the MOST RECENT comment carrying a clear value, not the first comment merely matching the loose marker pattern — fixed live during this task, since the original single-verdict `.find()` broke on real multi-comment PRs (a `REQUEST_CHANGES` verdict followed by fixes and a later clean `APPROVE`, or the post-merge provenance comment's own DANGLING-note prose, which contains the word "verdict" and would otherwise poison a naive first-match search). **Verdict authors are verified** (2026-08-09): only comments whose author is on the principal allowlist (`PRINCIPAL_ALLOWLIST` — the same trust anchor the waiver actor-check uses) participate in verdict extraction; forged, bot, and unresolvable-author comments are ignored, never fatal — a later forged `APPROVE` can no longer override a real `REQUEST_CHANGES`. A principal-actor-verified `vinaya/waiver:review` label (the same `isWaiverLabelActorVerified` pattern, now parameterized by label name and reused — not duplicated — for this second label) skips the requirement for one PR; label presence alone is never sufficient. **Tool-layer protection extended** (`check-forge-gates.sh`): mutating `vinaya/waiver:review` from an agent session is denied, exactly like `vinaya/waiver:docs` — without this, an agent sharing the Principal's own `gh` credential could self-apply the waiver and inherit the Principal's identity on the labeling event, defeating actor verification entirely (confirmed live during this task before the hook was extended). Non-task branches (plan PRs) bypass — a plan PR touching only topology files has no code to review. Going-forward only: never re-evaluates already-merged history. | `packages/aeg-core/bin/verify-review-gate.ts` |
|
|
77
78
|
| G1 — implementation exists | Ever read about a safeguard that turned out not to actually exist? | ci | Re-checks that every gate the doctrine describes has real code behind it. | Every ring's non-empty `implementation` cell on this very page resolves to a real path on disk — makes this page's registry columns load-bearing instead of decorative. | `packages/aeg-core/bin/verify-registry.ts` |
|
|
78
79
|
| G2 — no orphan hook/CLI | Ever found a script nobody remembers the purpose of? | ci | Re-checks that every hook and CLI in the repo is one the doctrine claims. | Every file under `.husky/*`, `.claude/hooks/*.sh`, `packages/aeg-core/bin/*.ts` is named by some row's `implementation` — the inverse direction of G1: no enforcement mechanism exists off-page. | `packages/aeg-core/bin/verify-registry.ts` |
|
|
79
80
|
| G3 — no seventh way into GitHub | Ever discovered a backdoor that skipped all your checks? | ci | Re-checks that no route into GitHub exists beyond the ones the doctrine gates. | Every file making the exact class of GitHub-mutating call `.claude/hooks/check-forge-gates.sh` itself gates (PR/Issue create, PR/Issue body/title edit, `gh api` create/edit, raw curl/wget writes) is named by some Ring-0 row's `implementation` — a mutating call outside that set would be an unguarded hole in the prevention model. | `packages/aeg-core/bin/verify-registry.ts` |
|
package/aeg-root/glossary.md
CHANGED
|
@@ -21,6 +21,10 @@ Vinaya uses a handful of words in a specific, non-obvious sense — on the pages
|
|
|
21
21
|
|
|
22
22
|
**Ratification** — Sign-off, from the person ultimately accountable for the product, on a decision or a piece of work that would be hard to undo. Higher-impact changes wait for a scheduled sign-off window rather than merging the moment they're technically ready.
|
|
23
23
|
|
|
24
|
+
**Registry ID / rendering ID** — Two names for the same role that can differ. The registry ID is the config key an adopter uses to claim or add a role — namespaced for an additive one. The rendering ID is the role file's own `role_id`, always slash-free, what every diagram and downstream reader actually sees. An override's rendering ID must equal its registry ID exactly; an additive role's rendering ID must equal the key's segment after the `/`.
|
|
25
|
+
|
|
26
|
+
**Resolution state** — One of three states — `default`, `overridden`, or `additive` — that every check or role ID resolves to. `default` means shipped-and-unmodified; `overridden` means a config entry currently claims that ID and satisfies its contract; `additive` means a wholly new, namespaced entry the shipped product has no opinion on.
|
|
27
|
+
|
|
24
28
|
**Seam** — The handoff point between two people or roles, where one produces something — a document, a decision, a record — and the other consumes it. Each seam is written down once, in its own place, so both sides can agree on exactly what crosses it.
|
|
25
29
|
|
|
26
30
|
**Step 0** — The literal first command someone runs before touching any code on a task: the one that creates that task's own isolated copy of the repository and its own branch. Nothing else happens until this command has run.
|
|
@@ -123,7 +123,7 @@ If you discover something that needs a decision above review authority — the b
|
|
|
123
123
|
|
|
124
124
|
Phase 10 (Review) in `process.md`. The order is: **code-reviewer pass (you) → security pass (`roles/security.md`) → Principal code review → Brief Author spec review → merge.** Your verdict feeds the human reviews; it does not replace them.
|
|
125
125
|
|
|
126
|
-
**Your verdict is also a mechanical merge gate (the review-gate tranche, task 1).** A required, blocking CI check (`packages/aeg-core/bin/verify-review-gate.ts`) reads every PR comment for a clean `APPROVE` verdict — `REQUEST CHANGES`, a missing verdict, or an unclear one all fail the check and block merge, same as this repo's own security pass. This is not advisory: it is the same enforcement class as typecheck or lint. A principal can waive it for one PR with an actor-verified `vinaya/waiver:review` label (`aeg-root/enforcement.md`) — label presence alone is never sufficient.
|
|
126
|
+
**Your verdict is also a mechanical merge gate (the review-gate tranche, task 1).** A required, blocking CI check (`packages/aeg-core/bin/verify-review-gate.ts`) reads every PR comment from a **principal-allowlisted author** (verdict-author verification, 2026-08-09 — bot and unknown-author comments are ignored) for a clean `APPROVE` verdict — `REQUEST CHANGES`, a missing verdict, or an unclear one all fail the check and block merge, same as this repo's own security pass. This is not advisory: it is the same enforcement class as typecheck or lint. A principal can waive it for one PR with an actor-verified `vinaya/waiver:review` label (`aeg-root/enforcement.md`) — label presence alone is never sufficient.
|
|
127
127
|
|
|
128
128
|
## Turn-end: report your tokens in the verdict comment
|
|
129
129
|
|
|
@@ -114,7 +114,7 @@ A security finding that implies a product/architecture decision (e.g., "the whol
|
|
|
114
114
|
|
|
115
115
|
Phase 10 (Review) in `process.md`: code-reviewer pass → **security pass (you)** → Principal code review → Brief Author spec review → merge.
|
|
116
116
|
|
|
117
|
-
**Your verdict is also a mechanical merge gate (the review-gate tranche, task 1).** A required, blocking CI check (`packages/aeg-core/bin/verify-review-gate.ts`) reads every PR comment for a clean `PASS` verdict — `FAIL`, a missing verdict, or an unclear one all fail the check and block merge, same as the code-reviewer pass. This is not advisory: it is the same enforcement class as typecheck or lint. A principal can waive it for one PR with an actor-verified `vinaya/waiver:review` label (`aeg-root/enforcement.md`) — label presence alone is never sufficient.
|
|
117
|
+
**Your verdict is also a mechanical merge gate (the review-gate tranche, task 1).** A required, blocking CI check (`packages/aeg-core/bin/verify-review-gate.ts`) reads every PR comment from a **principal-allowlisted author** (verdict-author verification, 2026-08-09 — bot and unknown-author comments are ignored) for a clean `PASS` verdict — `FAIL`, a missing verdict, or an unclear one all fail the check and block merge, same as the code-reviewer pass. This is not advisory: it is the same enforcement class as typecheck or lint. A principal can waive it for one PR with an actor-verified `vinaya/waiver:review` label (`aeg-root/enforcement.md`) — label presence alone is never sufficient.
|
|
118
118
|
|
|
119
119
|
## Turn-end: report your tokens in the verdict comment
|
|
120
120
|
|
|
@@ -2170,8 +2170,11 @@ function checkReviewGate(input) {
|
|
|
2170
2170
|
waived: true
|
|
2171
2171
|
};
|
|
2172
2172
|
}
|
|
2173
|
-
const
|
|
2174
|
-
const
|
|
2173
|
+
const verified = input.comments.filter((c) => c.author !== null && PRINCIPAL_ALLOWLIST.includes(c.author));
|
|
2174
|
+
const ignoredCount = input.comments.filter((c) => (c.author === null || !PRINCIPAL_ALLOWLIST.includes(c.author)) && c.body.includes("VERDICT")).length;
|
|
2175
|
+
const verifiedBodies = verified.map((c) => c.body);
|
|
2176
|
+
const codeReview = extractCodeReviewVerdict(verifiedBodies);
|
|
2177
|
+
const security = extractSecurityReviewVerdict(verifiedBodies);
|
|
2175
2178
|
const codeReviewClean = codeReview.value === "APPROVE";
|
|
2176
2179
|
const securityClean = security.value === "PASS";
|
|
2177
2180
|
if (codeReviewClean && securityClean) {
|
|
@@ -2186,9 +2189,10 @@ function checkReviewGate(input) {
|
|
|
2186
2189
|
problems.push(`code-reviewer verdict is not a clean APPROVE (found: ${codeReview.value})`);
|
|
2187
2190
|
if (!securityClean)
|
|
2188
2191
|
problems.push(`security-review verdict is not a clean PASS (found: ${security.value})`);
|
|
2192
|
+
const ignoredNote = ignoredCount > 0 ? ` ${ignoredCount} verdict-shaped comment(s) from authors outside the principal allowlist were ignored.` : "";
|
|
2189
2193
|
return {
|
|
2190
2194
|
verdict: "fail",
|
|
2191
|
-
reason: `${problems.join("; ")}. A principal can apply an actor-verified \`${WAIVER_LABEL_REVIEW}\` label to skip this requirement, or post the missing/clean verdict comment(s)
|
|
2195
|
+
reason: `${problems.join("; ")}. A principal can apply an actor-verified \`${WAIVER_LABEL_REVIEW}\` label to skip this requirement, or post the missing/clean verdict comment(s).${ignoredNote}`,
|
|
2192
2196
|
waived: false
|
|
2193
2197
|
};
|
|
2194
2198
|
}
|
|
@@ -2167,8 +2167,11 @@ function checkReviewGate(input) {
|
|
|
2167
2167
|
waived: true
|
|
2168
2168
|
};
|
|
2169
2169
|
}
|
|
2170
|
-
const
|
|
2171
|
-
const
|
|
2170
|
+
const verified = input.comments.filter((c) => c.author !== null && PRINCIPAL_ALLOWLIST.includes(c.author));
|
|
2171
|
+
const ignoredCount = input.comments.filter((c) => (c.author === null || !PRINCIPAL_ALLOWLIST.includes(c.author)) && c.body.includes("VERDICT")).length;
|
|
2172
|
+
const verifiedBodies = verified.map((c) => c.body);
|
|
2173
|
+
const codeReview = extractCodeReviewVerdict(verifiedBodies);
|
|
2174
|
+
const security = extractSecurityReviewVerdict(verifiedBodies);
|
|
2172
2175
|
const codeReviewClean = codeReview.value === "APPROVE";
|
|
2173
2176
|
const securityClean = security.value === "PASS";
|
|
2174
2177
|
if (codeReviewClean && securityClean) {
|
|
@@ -2183,9 +2186,10 @@ function checkReviewGate(input) {
|
|
|
2183
2186
|
problems.push(`code-reviewer verdict is not a clean APPROVE (found: ${codeReview.value})`);
|
|
2184
2187
|
if (!securityClean)
|
|
2185
2188
|
problems.push(`security-review verdict is not a clean PASS (found: ${security.value})`);
|
|
2189
|
+
const ignoredNote = ignoredCount > 0 ? ` ${ignoredCount} verdict-shaped comment(s) from authors outside the principal allowlist were ignored.` : "";
|
|
2186
2190
|
return {
|
|
2187
2191
|
verdict: "fail",
|
|
2188
|
-
reason: `${problems.join("; ")}. A principal can apply an actor-verified \`${WAIVER_LABEL_REVIEW}\` label to skip this requirement, or post the missing/clean verdict comment(s)
|
|
2192
|
+
reason: `${problems.join("; ")}. A principal can apply an actor-verified \`${WAIVER_LABEL_REVIEW}\` label to skip this requirement, or post the missing/clean verdict comment(s).${ignoredNote}`,
|
|
2189
2193
|
waived: false
|
|
2190
2194
|
};
|
|
2191
2195
|
}
|
|
@@ -2170,8 +2170,11 @@ function checkReviewGate(input) {
|
|
|
2170
2170
|
waived: true
|
|
2171
2171
|
};
|
|
2172
2172
|
}
|
|
2173
|
-
const
|
|
2174
|
-
const
|
|
2173
|
+
const verified = input.comments.filter((c) => c.author !== null && PRINCIPAL_ALLOWLIST.includes(c.author));
|
|
2174
|
+
const ignoredCount = input.comments.filter((c) => (c.author === null || !PRINCIPAL_ALLOWLIST.includes(c.author)) && c.body.includes("VERDICT")).length;
|
|
2175
|
+
const verifiedBodies = verified.map((c) => c.body);
|
|
2176
|
+
const codeReview = extractCodeReviewVerdict(verifiedBodies);
|
|
2177
|
+
const security = extractSecurityReviewVerdict(verifiedBodies);
|
|
2175
2178
|
const codeReviewClean = codeReview.value === "APPROVE";
|
|
2176
2179
|
const securityClean = security.value === "PASS";
|
|
2177
2180
|
if (codeReviewClean && securityClean) {
|
|
@@ -2186,9 +2189,10 @@ function checkReviewGate(input) {
|
|
|
2186
2189
|
problems.push(`code-reviewer verdict is not a clean APPROVE (found: ${codeReview.value})`);
|
|
2187
2190
|
if (!securityClean)
|
|
2188
2191
|
problems.push(`security-review verdict is not a clean PASS (found: ${security.value})`);
|
|
2192
|
+
const ignoredNote = ignoredCount > 0 ? ` ${ignoredCount} verdict-shaped comment(s) from authors outside the principal allowlist were ignored.` : "";
|
|
2189
2193
|
return {
|
|
2190
2194
|
verdict: "fail",
|
|
2191
|
-
reason: `${problems.join("; ")}. A principal can apply an actor-verified \`${WAIVER_LABEL_REVIEW}\` label to skip this requirement, or post the missing/clean verdict comment(s)
|
|
2195
|
+
reason: `${problems.join("; ")}. A principal can apply an actor-verified \`${WAIVER_LABEL_REVIEW}\` label to skip this requirement, or post the missing/clean verdict comment(s).${ignoredNote}`,
|
|
2192
2196
|
waived: false
|
|
2193
2197
|
};
|
|
2194
2198
|
}
|
|
@@ -2171,8 +2171,11 @@ function checkReviewGate(input) {
|
|
|
2171
2171
|
waived: true
|
|
2172
2172
|
};
|
|
2173
2173
|
}
|
|
2174
|
-
const
|
|
2175
|
-
const
|
|
2174
|
+
const verified = input.comments.filter((c) => c.author !== null && PRINCIPAL_ALLOWLIST.includes(c.author));
|
|
2175
|
+
const ignoredCount = input.comments.filter((c) => (c.author === null || !PRINCIPAL_ALLOWLIST.includes(c.author)) && c.body.includes("VERDICT")).length;
|
|
2176
|
+
const verifiedBodies = verified.map((c) => c.body);
|
|
2177
|
+
const codeReview = extractCodeReviewVerdict(verifiedBodies);
|
|
2178
|
+
const security = extractSecurityReviewVerdict(verifiedBodies);
|
|
2176
2179
|
const codeReviewClean = codeReview.value === "APPROVE";
|
|
2177
2180
|
const securityClean = security.value === "PASS";
|
|
2178
2181
|
if (codeReviewClean && securityClean) {
|
|
@@ -2187,9 +2190,10 @@ function checkReviewGate(input) {
|
|
|
2187
2190
|
problems.push(`code-reviewer verdict is not a clean APPROVE (found: ${codeReview.value})`);
|
|
2188
2191
|
if (!securityClean)
|
|
2189
2192
|
problems.push(`security-review verdict is not a clean PASS (found: ${security.value})`);
|
|
2193
|
+
const ignoredNote = ignoredCount > 0 ? ` ${ignoredCount} verdict-shaped comment(s) from authors outside the principal allowlist were ignored.` : "";
|
|
2190
2194
|
return {
|
|
2191
2195
|
verdict: "fail",
|
|
2192
|
-
reason: `${problems.join("; ")}. A principal can apply an actor-verified \`${WAIVER_LABEL_REVIEW}\` label to skip this requirement, or post the missing/clean verdict comment(s)
|
|
2196
|
+
reason: `${problems.join("; ")}. A principal can apply an actor-verified \`${WAIVER_LABEL_REVIEW}\` label to skip this requirement, or post the missing/clean verdict comment(s).${ignoredNote}`,
|
|
2193
2197
|
waived: false
|
|
2194
2198
|
};
|
|
2195
2199
|
}
|
|
@@ -2170,8 +2170,11 @@ function checkReviewGate(input) {
|
|
|
2170
2170
|
waived: true
|
|
2171
2171
|
};
|
|
2172
2172
|
}
|
|
2173
|
-
const
|
|
2174
|
-
const
|
|
2173
|
+
const verified = input.comments.filter((c) => c.author !== null && PRINCIPAL_ALLOWLIST.includes(c.author));
|
|
2174
|
+
const ignoredCount = input.comments.filter((c) => (c.author === null || !PRINCIPAL_ALLOWLIST.includes(c.author)) && c.body.includes("VERDICT")).length;
|
|
2175
|
+
const verifiedBodies = verified.map((c) => c.body);
|
|
2176
|
+
const codeReview = extractCodeReviewVerdict(verifiedBodies);
|
|
2177
|
+
const security = extractSecurityReviewVerdict(verifiedBodies);
|
|
2175
2178
|
const codeReviewClean = codeReview.value === "APPROVE";
|
|
2176
2179
|
const securityClean = security.value === "PASS";
|
|
2177
2180
|
if (codeReviewClean && securityClean) {
|
|
@@ -2186,9 +2189,10 @@ function checkReviewGate(input) {
|
|
|
2186
2189
|
problems.push(`code-reviewer verdict is not a clean APPROVE (found: ${codeReview.value})`);
|
|
2187
2190
|
if (!securityClean)
|
|
2188
2191
|
problems.push(`security-review verdict is not a clean PASS (found: ${security.value})`);
|
|
2192
|
+
const ignoredNote = ignoredCount > 0 ? ` ${ignoredCount} verdict-shaped comment(s) from authors outside the principal allowlist were ignored.` : "";
|
|
2189
2193
|
return {
|
|
2190
2194
|
verdict: "fail",
|
|
2191
|
-
reason: `${problems.join("; ")}. A principal can apply an actor-verified \`${WAIVER_LABEL_REVIEW}\` label to skip this requirement, or post the missing/clean verdict comment(s)
|
|
2195
|
+
reason: `${problems.join("; ")}. A principal can apply an actor-verified \`${WAIVER_LABEL_REVIEW}\` label to skip this requirement, or post the missing/clean verdict comment(s).${ignoredNote}`,
|
|
2192
2196
|
waived: false
|
|
2193
2197
|
};
|
|
2194
2198
|
}
|
|
@@ -2171,8 +2171,11 @@ function checkReviewGate(input) {
|
|
|
2171
2171
|
waived: true
|
|
2172
2172
|
};
|
|
2173
2173
|
}
|
|
2174
|
-
const
|
|
2175
|
-
const
|
|
2174
|
+
const verified = input.comments.filter((c) => c.author !== null && PRINCIPAL_ALLOWLIST.includes(c.author));
|
|
2175
|
+
const ignoredCount = input.comments.filter((c) => (c.author === null || !PRINCIPAL_ALLOWLIST.includes(c.author)) && c.body.includes("VERDICT")).length;
|
|
2176
|
+
const verifiedBodies = verified.map((c) => c.body);
|
|
2177
|
+
const codeReview = extractCodeReviewVerdict(verifiedBodies);
|
|
2178
|
+
const security = extractSecurityReviewVerdict(verifiedBodies);
|
|
2176
2179
|
const codeReviewClean = codeReview.value === "APPROVE";
|
|
2177
2180
|
const securityClean = security.value === "PASS";
|
|
2178
2181
|
if (codeReviewClean && securityClean) {
|
|
@@ -2187,9 +2190,10 @@ function checkReviewGate(input) {
|
|
|
2187
2190
|
problems.push(`code-reviewer verdict is not a clean APPROVE (found: ${codeReview.value})`);
|
|
2188
2191
|
if (!securityClean)
|
|
2189
2192
|
problems.push(`security-review verdict is not a clean PASS (found: ${security.value})`);
|
|
2193
|
+
const ignoredNote = ignoredCount > 0 ? ` ${ignoredCount} verdict-shaped comment(s) from authors outside the principal allowlist were ignored.` : "";
|
|
2190
2194
|
return {
|
|
2191
2195
|
verdict: "fail",
|
|
2192
|
-
reason: `${problems.join("; ")}. A principal can apply an actor-verified \`${WAIVER_LABEL_REVIEW}\` label to skip this requirement, or post the missing/clean verdict comment(s)
|
|
2196
|
+
reason: `${problems.join("; ")}. A principal can apply an actor-verified \`${WAIVER_LABEL_REVIEW}\` label to skip this requirement, or post the missing/clean verdict comment(s).${ignoredNote}`,
|
|
2193
2197
|
waived: false
|
|
2194
2198
|
};
|
|
2195
2199
|
}
|
|
@@ -2171,8 +2171,11 @@ function checkReviewGate(input) {
|
|
|
2171
2171
|
waived: true
|
|
2172
2172
|
};
|
|
2173
2173
|
}
|
|
2174
|
-
const
|
|
2175
|
-
const
|
|
2174
|
+
const verified = input.comments.filter((c) => c.author !== null && PRINCIPAL_ALLOWLIST.includes(c.author));
|
|
2175
|
+
const ignoredCount = input.comments.filter((c) => (c.author === null || !PRINCIPAL_ALLOWLIST.includes(c.author)) && c.body.includes("VERDICT")).length;
|
|
2176
|
+
const verifiedBodies = verified.map((c) => c.body);
|
|
2177
|
+
const codeReview = extractCodeReviewVerdict(verifiedBodies);
|
|
2178
|
+
const security = extractSecurityReviewVerdict(verifiedBodies);
|
|
2176
2179
|
const codeReviewClean = codeReview.value === "APPROVE";
|
|
2177
2180
|
const securityClean = security.value === "PASS";
|
|
2178
2181
|
if (codeReviewClean && securityClean) {
|
|
@@ -2187,9 +2190,10 @@ function checkReviewGate(input) {
|
|
|
2187
2190
|
problems.push(`code-reviewer verdict is not a clean APPROVE (found: ${codeReview.value})`);
|
|
2188
2191
|
if (!securityClean)
|
|
2189
2192
|
problems.push(`security-review verdict is not a clean PASS (found: ${security.value})`);
|
|
2193
|
+
const ignoredNote = ignoredCount > 0 ? ` ${ignoredCount} verdict-shaped comment(s) from authors outside the principal allowlist were ignored.` : "";
|
|
2190
2194
|
return {
|
|
2191
2195
|
verdict: "fail",
|
|
2192
|
-
reason: `${problems.join("; ")}. A principal can apply an actor-verified \`${WAIVER_LABEL_REVIEW}\` label to skip this requirement, or post the missing/clean verdict comment(s)
|
|
2196
|
+
reason: `${problems.join("; ")}. A principal can apply an actor-verified \`${WAIVER_LABEL_REVIEW}\` label to skip this requirement, or post the missing/clean verdict comment(s).${ignoredNote}`,
|
|
2193
2197
|
waived: false
|
|
2194
2198
|
};
|
|
2195
2199
|
}
|
|
@@ -2172,8 +2172,11 @@ function checkReviewGate(input) {
|
|
|
2172
2172
|
waived: true
|
|
2173
2173
|
};
|
|
2174
2174
|
}
|
|
2175
|
-
const
|
|
2176
|
-
const
|
|
2175
|
+
const verified = input.comments.filter((c) => c.author !== null && PRINCIPAL_ALLOWLIST.includes(c.author));
|
|
2176
|
+
const ignoredCount = input.comments.filter((c) => (c.author === null || !PRINCIPAL_ALLOWLIST.includes(c.author)) && c.body.includes("VERDICT")).length;
|
|
2177
|
+
const verifiedBodies = verified.map((c) => c.body);
|
|
2178
|
+
const codeReview = extractCodeReviewVerdict(verifiedBodies);
|
|
2179
|
+
const security = extractSecurityReviewVerdict(verifiedBodies);
|
|
2177
2180
|
const codeReviewClean = codeReview.value === "APPROVE";
|
|
2178
2181
|
const securityClean = security.value === "PASS";
|
|
2179
2182
|
if (codeReviewClean && securityClean) {
|
|
@@ -2188,9 +2191,10 @@ function checkReviewGate(input) {
|
|
|
2188
2191
|
problems.push(`code-reviewer verdict is not a clean APPROVE (found: ${codeReview.value})`);
|
|
2189
2192
|
if (!securityClean)
|
|
2190
2193
|
problems.push(`security-review verdict is not a clean PASS (found: ${security.value})`);
|
|
2194
|
+
const ignoredNote = ignoredCount > 0 ? ` ${ignoredCount} verdict-shaped comment(s) from authors outside the principal allowlist were ignored.` : "";
|
|
2191
2195
|
return {
|
|
2192
2196
|
verdict: "fail",
|
|
2193
|
-
reason: `${problems.join("; ")}. A principal can apply an actor-verified \`${WAIVER_LABEL_REVIEW}\` label to skip this requirement, or post the missing/clean verdict comment(s)
|
|
2197
|
+
reason: `${problems.join("; ")}. A principal can apply an actor-verified \`${WAIVER_LABEL_REVIEW}\` label to skip this requirement, or post the missing/clean verdict comment(s).${ignoredNote}`,
|
|
2194
2198
|
waived: false
|
|
2195
2199
|
};
|
|
2196
2200
|
}
|
|
@@ -2170,8 +2170,11 @@ function checkReviewGate(input) {
|
|
|
2170
2170
|
waived: true
|
|
2171
2171
|
};
|
|
2172
2172
|
}
|
|
2173
|
-
const
|
|
2174
|
-
const
|
|
2173
|
+
const verified = input.comments.filter((c) => c.author !== null && PRINCIPAL_ALLOWLIST.includes(c.author));
|
|
2174
|
+
const ignoredCount = input.comments.filter((c) => (c.author === null || !PRINCIPAL_ALLOWLIST.includes(c.author)) && c.body.includes("VERDICT")).length;
|
|
2175
|
+
const verifiedBodies = verified.map((c) => c.body);
|
|
2176
|
+
const codeReview = extractCodeReviewVerdict(verifiedBodies);
|
|
2177
|
+
const security = extractSecurityReviewVerdict(verifiedBodies);
|
|
2175
2178
|
const codeReviewClean = codeReview.value === "APPROVE";
|
|
2176
2179
|
const securityClean = security.value === "PASS";
|
|
2177
2180
|
if (codeReviewClean && securityClean) {
|
|
@@ -2186,9 +2189,10 @@ function checkReviewGate(input) {
|
|
|
2186
2189
|
problems.push(`code-reviewer verdict is not a clean APPROVE (found: ${codeReview.value})`);
|
|
2187
2190
|
if (!securityClean)
|
|
2188
2191
|
problems.push(`security-review verdict is not a clean PASS (found: ${security.value})`);
|
|
2192
|
+
const ignoredNote = ignoredCount > 0 ? ` ${ignoredCount} verdict-shaped comment(s) from authors outside the principal allowlist were ignored.` : "";
|
|
2189
2193
|
return {
|
|
2190
2194
|
verdict: "fail",
|
|
2191
|
-
reason: `${problems.join("; ")}. A principal can apply an actor-verified \`${WAIVER_LABEL_REVIEW}\` label to skip this requirement, or post the missing/clean verdict comment(s)
|
|
2195
|
+
reason: `${problems.join("; ")}. A principal can apply an actor-verified \`${WAIVER_LABEL_REVIEW}\` label to skip this requirement, or post the missing/clean verdict comment(s).${ignoredNote}`,
|
|
2192
2196
|
waived: false
|
|
2193
2197
|
};
|
|
2194
2198
|
}
|
|
@@ -2170,8 +2170,11 @@ function checkReviewGate(input) {
|
|
|
2170
2170
|
waived: true
|
|
2171
2171
|
};
|
|
2172
2172
|
}
|
|
2173
|
-
const
|
|
2174
|
-
const
|
|
2173
|
+
const verified = input.comments.filter((c) => c.author !== null && PRINCIPAL_ALLOWLIST.includes(c.author));
|
|
2174
|
+
const ignoredCount = input.comments.filter((c) => (c.author === null || !PRINCIPAL_ALLOWLIST.includes(c.author)) && c.body.includes("VERDICT")).length;
|
|
2175
|
+
const verifiedBodies = verified.map((c) => c.body);
|
|
2176
|
+
const codeReview = extractCodeReviewVerdict(verifiedBodies);
|
|
2177
|
+
const security = extractSecurityReviewVerdict(verifiedBodies);
|
|
2175
2178
|
const codeReviewClean = codeReview.value === "APPROVE";
|
|
2176
2179
|
const securityClean = security.value === "PASS";
|
|
2177
2180
|
if (codeReviewClean && securityClean) {
|
|
@@ -2186,9 +2189,10 @@ function checkReviewGate(input) {
|
|
|
2186
2189
|
problems.push(`code-reviewer verdict is not a clean APPROVE (found: ${codeReview.value})`);
|
|
2187
2190
|
if (!securityClean)
|
|
2188
2191
|
problems.push(`security-review verdict is not a clean PASS (found: ${security.value})`);
|
|
2192
|
+
const ignoredNote = ignoredCount > 0 ? ` ${ignoredCount} verdict-shaped comment(s) from authors outside the principal allowlist were ignored.` : "";
|
|
2189
2193
|
return {
|
|
2190
2194
|
verdict: "fail",
|
|
2191
|
-
reason: `${problems.join("; ")}. A principal can apply an actor-verified \`${WAIVER_LABEL_REVIEW}\` label to skip this requirement, or post the missing/clean verdict comment(s)
|
|
2195
|
+
reason: `${problems.join("; ")}. A principal can apply an actor-verified \`${WAIVER_LABEL_REVIEW}\` label to skip this requirement, or post the missing/clean verdict comment(s).${ignoredNote}`,
|
|
2192
2196
|
waived: false
|
|
2193
2197
|
};
|
|
2194
2198
|
}
|
|
@@ -2170,8 +2170,11 @@ function checkReviewGate(input) {
|
|
|
2170
2170
|
waived: true
|
|
2171
2171
|
};
|
|
2172
2172
|
}
|
|
2173
|
-
const
|
|
2174
|
-
const
|
|
2173
|
+
const verified = input.comments.filter((c) => c.author !== null && PRINCIPAL_ALLOWLIST.includes(c.author));
|
|
2174
|
+
const ignoredCount = input.comments.filter((c) => (c.author === null || !PRINCIPAL_ALLOWLIST.includes(c.author)) && c.body.includes("VERDICT")).length;
|
|
2175
|
+
const verifiedBodies = verified.map((c) => c.body);
|
|
2176
|
+
const codeReview = extractCodeReviewVerdict(verifiedBodies);
|
|
2177
|
+
const security = extractSecurityReviewVerdict(verifiedBodies);
|
|
2175
2178
|
const codeReviewClean = codeReview.value === "APPROVE";
|
|
2176
2179
|
const securityClean = security.value === "PASS";
|
|
2177
2180
|
if (codeReviewClean && securityClean) {
|
|
@@ -2186,9 +2189,10 @@ function checkReviewGate(input) {
|
|
|
2186
2189
|
problems.push(`code-reviewer verdict is not a clean APPROVE (found: ${codeReview.value})`);
|
|
2187
2190
|
if (!securityClean)
|
|
2188
2191
|
problems.push(`security-review verdict is not a clean PASS (found: ${security.value})`);
|
|
2192
|
+
const ignoredNote = ignoredCount > 0 ? ` ${ignoredCount} verdict-shaped comment(s) from authors outside the principal allowlist were ignored.` : "";
|
|
2189
2193
|
return {
|
|
2190
2194
|
verdict: "fail",
|
|
2191
|
-
reason: `${problems.join("; ")}. A principal can apply an actor-verified \`${WAIVER_LABEL_REVIEW}\` label to skip this requirement, or post the missing/clean verdict comment(s)
|
|
2195
|
+
reason: `${problems.join("; ")}. A principal can apply an actor-verified \`${WAIVER_LABEL_REVIEW}\` label to skip this requirement, or post the missing/clean verdict comment(s).${ignoredNote}`,
|
|
2192
2196
|
waived: false
|
|
2193
2197
|
};
|
|
2194
2198
|
}
|
|
@@ -2172,8 +2172,11 @@ function checkReviewGate(input) {
|
|
|
2172
2172
|
waived: true
|
|
2173
2173
|
};
|
|
2174
2174
|
}
|
|
2175
|
-
const
|
|
2176
|
-
const
|
|
2175
|
+
const verified = input.comments.filter((c) => c.author !== null && PRINCIPAL_ALLOWLIST.includes(c.author));
|
|
2176
|
+
const ignoredCount = input.comments.filter((c) => (c.author === null || !PRINCIPAL_ALLOWLIST.includes(c.author)) && c.body.includes("VERDICT")).length;
|
|
2177
|
+
const verifiedBodies = verified.map((c) => c.body);
|
|
2178
|
+
const codeReview = extractCodeReviewVerdict(verifiedBodies);
|
|
2179
|
+
const security = extractSecurityReviewVerdict(verifiedBodies);
|
|
2177
2180
|
const codeReviewClean = codeReview.value === "APPROVE";
|
|
2178
2181
|
const securityClean = security.value === "PASS";
|
|
2179
2182
|
if (codeReviewClean && securityClean) {
|
|
@@ -2188,9 +2191,10 @@ function checkReviewGate(input) {
|
|
|
2188
2191
|
problems.push(`code-reviewer verdict is not a clean APPROVE (found: ${codeReview.value})`);
|
|
2189
2192
|
if (!securityClean)
|
|
2190
2193
|
problems.push(`security-review verdict is not a clean PASS (found: ${security.value})`);
|
|
2194
|
+
const ignoredNote = ignoredCount > 0 ? ` ${ignoredCount} verdict-shaped comment(s) from authors outside the principal allowlist were ignored.` : "";
|
|
2191
2195
|
return {
|
|
2192
2196
|
verdict: "fail",
|
|
2193
|
-
reason: `${problems.join("; ")}. A principal can apply an actor-verified \`${WAIVER_LABEL_REVIEW}\` label to skip this requirement, or post the missing/clean verdict comment(s)
|
|
2197
|
+
reason: `${problems.join("; ")}. A principal can apply an actor-verified \`${WAIVER_LABEL_REVIEW}\` label to skip this requirement, or post the missing/clean verdict comment(s).${ignoredNote}`,
|
|
2194
2198
|
waived: false
|
|
2195
2199
|
};
|
|
2196
2200
|
}
|
|
@@ -2172,8 +2172,11 @@ function checkReviewGate(input) {
|
|
|
2172
2172
|
waived: true
|
|
2173
2173
|
};
|
|
2174
2174
|
}
|
|
2175
|
-
const
|
|
2176
|
-
const
|
|
2175
|
+
const verified = input.comments.filter((c) => c.author !== null && PRINCIPAL_ALLOWLIST.includes(c.author));
|
|
2176
|
+
const ignoredCount = input.comments.filter((c) => (c.author === null || !PRINCIPAL_ALLOWLIST.includes(c.author)) && c.body.includes("VERDICT")).length;
|
|
2177
|
+
const verifiedBodies = verified.map((c) => c.body);
|
|
2178
|
+
const codeReview = extractCodeReviewVerdict(verifiedBodies);
|
|
2179
|
+
const security = extractSecurityReviewVerdict(verifiedBodies);
|
|
2177
2180
|
const codeReviewClean = codeReview.value === "APPROVE";
|
|
2178
2181
|
const securityClean = security.value === "PASS";
|
|
2179
2182
|
if (codeReviewClean && securityClean) {
|
|
@@ -2188,9 +2191,10 @@ function checkReviewGate(input) {
|
|
|
2188
2191
|
problems.push(`code-reviewer verdict is not a clean APPROVE (found: ${codeReview.value})`);
|
|
2189
2192
|
if (!securityClean)
|
|
2190
2193
|
problems.push(`security-review verdict is not a clean PASS (found: ${security.value})`);
|
|
2194
|
+
const ignoredNote = ignoredCount > 0 ? ` ${ignoredCount} verdict-shaped comment(s) from authors outside the principal allowlist were ignored.` : "";
|
|
2191
2195
|
return {
|
|
2192
2196
|
verdict: "fail",
|
|
2193
|
-
reason: `${problems.join("; ")}. A principal can apply an actor-verified \`${WAIVER_LABEL_REVIEW}\` label to skip this requirement, or post the missing/clean verdict comment(s)
|
|
2197
|
+
reason: `${problems.join("; ")}. A principal can apply an actor-verified \`${WAIVER_LABEL_REVIEW}\` label to skip this requirement, or post the missing/clean verdict comment(s).${ignoredNote}`,
|
|
2194
2198
|
waived: false
|
|
2195
2199
|
};
|
|
2196
2200
|
}
|
|
@@ -2170,8 +2170,11 @@ function checkReviewGate(input) {
|
|
|
2170
2170
|
waived: true
|
|
2171
2171
|
};
|
|
2172
2172
|
}
|
|
2173
|
-
const
|
|
2174
|
-
const
|
|
2173
|
+
const verified = input.comments.filter((c) => c.author !== null && PRINCIPAL_ALLOWLIST.includes(c.author));
|
|
2174
|
+
const ignoredCount = input.comments.filter((c) => (c.author === null || !PRINCIPAL_ALLOWLIST.includes(c.author)) && c.body.includes("VERDICT")).length;
|
|
2175
|
+
const verifiedBodies = verified.map((c) => c.body);
|
|
2176
|
+
const codeReview = extractCodeReviewVerdict(verifiedBodies);
|
|
2177
|
+
const security = extractSecurityReviewVerdict(verifiedBodies);
|
|
2175
2178
|
const codeReviewClean = codeReview.value === "APPROVE";
|
|
2176
2179
|
const securityClean = security.value === "PASS";
|
|
2177
2180
|
if (codeReviewClean && securityClean) {
|
|
@@ -2186,9 +2189,10 @@ function checkReviewGate(input) {
|
|
|
2186
2189
|
problems.push(`code-reviewer verdict is not a clean APPROVE (found: ${codeReview.value})`);
|
|
2187
2190
|
if (!securityClean)
|
|
2188
2191
|
problems.push(`security-review verdict is not a clean PASS (found: ${security.value})`);
|
|
2192
|
+
const ignoredNote = ignoredCount > 0 ? ` ${ignoredCount} verdict-shaped comment(s) from authors outside the principal allowlist were ignored.` : "";
|
|
2189
2193
|
return {
|
|
2190
2194
|
verdict: "fail",
|
|
2191
|
-
reason: `${problems.join("; ")}. A principal can apply an actor-verified \`${WAIVER_LABEL_REVIEW}\` label to skip this requirement, or post the missing/clean verdict comment(s)
|
|
2195
|
+
reason: `${problems.join("; ")}. A principal can apply an actor-verified \`${WAIVER_LABEL_REVIEW}\` label to skip this requirement, or post the missing/clean verdict comment(s).${ignoredNote}`,
|
|
2192
2196
|
waived: false
|
|
2193
2197
|
};
|
|
2194
2198
|
}
|
|
@@ -2868,7 +2872,11 @@ function main() {
|
|
|
2868
2872
|
}
|
|
2869
2873
|
const labels = pr.labels.map((l) => l.name);
|
|
2870
2874
|
const waiverLabelActor = labels.includes(WAIVER_LABEL_REVIEW) ? fetchWaiverLabelActor(prNumber, WAIVER_LABEL_REVIEW) : null;
|
|
2871
|
-
const result = checkReviewGate({
|
|
2875
|
+
const result = checkReviewGate({
|
|
2876
|
+
comments: pr.comments.map((c) => ({ body: c.body, author: c.author?.login ?? null })),
|
|
2877
|
+
labels,
|
|
2878
|
+
waiverLabelActor
|
|
2879
|
+
});
|
|
2872
2880
|
if (result.verdict === "fail") {
|
|
2873
2881
|
emitCheckError({
|
|
2874
2882
|
schema: CHECK_SCHEMA_VERSION,
|
|
@@ -2170,8 +2170,11 @@ function checkReviewGate(input) {
|
|
|
2170
2170
|
waived: true
|
|
2171
2171
|
};
|
|
2172
2172
|
}
|
|
2173
|
-
const
|
|
2174
|
-
const
|
|
2173
|
+
const verified = input.comments.filter((c) => c.author !== null && PRINCIPAL_ALLOWLIST.includes(c.author));
|
|
2174
|
+
const ignoredCount = input.comments.filter((c) => (c.author === null || !PRINCIPAL_ALLOWLIST.includes(c.author)) && c.body.includes("VERDICT")).length;
|
|
2175
|
+
const verifiedBodies = verified.map((c) => c.body);
|
|
2176
|
+
const codeReview = extractCodeReviewVerdict(verifiedBodies);
|
|
2177
|
+
const security = extractSecurityReviewVerdict(verifiedBodies);
|
|
2175
2178
|
const codeReviewClean = codeReview.value === "APPROVE";
|
|
2176
2179
|
const securityClean = security.value === "PASS";
|
|
2177
2180
|
if (codeReviewClean && securityClean) {
|
|
@@ -2186,9 +2189,10 @@ function checkReviewGate(input) {
|
|
|
2186
2189
|
problems.push(`code-reviewer verdict is not a clean APPROVE (found: ${codeReview.value})`);
|
|
2187
2190
|
if (!securityClean)
|
|
2188
2191
|
problems.push(`security-review verdict is not a clean PASS (found: ${security.value})`);
|
|
2192
|
+
const ignoredNote = ignoredCount > 0 ? ` ${ignoredCount} verdict-shaped comment(s) from authors outside the principal allowlist were ignored.` : "";
|
|
2189
2193
|
return {
|
|
2190
2194
|
verdict: "fail",
|
|
2191
|
-
reason: `${problems.join("; ")}. A principal can apply an actor-verified \`${WAIVER_LABEL_REVIEW}\` label to skip this requirement, or post the missing/clean verdict comment(s)
|
|
2195
|
+
reason: `${problems.join("; ")}. A principal can apply an actor-verified \`${WAIVER_LABEL_REVIEW}\` label to skip this requirement, or post the missing/clean verdict comment(s).${ignoredNote}`,
|
|
2192
2196
|
waived: false
|
|
2193
2197
|
};
|
|
2194
2198
|
}
|
|
@@ -2167,8 +2167,11 @@ function checkReviewGate(input) {
|
|
|
2167
2167
|
waived: true
|
|
2168
2168
|
};
|
|
2169
2169
|
}
|
|
2170
|
-
const
|
|
2171
|
-
const
|
|
2170
|
+
const verified = input.comments.filter((c) => c.author !== null && PRINCIPAL_ALLOWLIST.includes(c.author));
|
|
2171
|
+
const ignoredCount = input.comments.filter((c) => (c.author === null || !PRINCIPAL_ALLOWLIST.includes(c.author)) && c.body.includes("VERDICT")).length;
|
|
2172
|
+
const verifiedBodies = verified.map((c) => c.body);
|
|
2173
|
+
const codeReview = extractCodeReviewVerdict(verifiedBodies);
|
|
2174
|
+
const security = extractSecurityReviewVerdict(verifiedBodies);
|
|
2172
2175
|
const codeReviewClean = codeReview.value === "APPROVE";
|
|
2173
2176
|
const securityClean = security.value === "PASS";
|
|
2174
2177
|
if (codeReviewClean && securityClean) {
|
|
@@ -2183,9 +2186,10 @@ function checkReviewGate(input) {
|
|
|
2183
2186
|
problems.push(`code-reviewer verdict is not a clean APPROVE (found: ${codeReview.value})`);
|
|
2184
2187
|
if (!securityClean)
|
|
2185
2188
|
problems.push(`security-review verdict is not a clean PASS (found: ${security.value})`);
|
|
2189
|
+
const ignoredNote = ignoredCount > 0 ? ` ${ignoredCount} verdict-shaped comment(s) from authors outside the principal allowlist were ignored.` : "";
|
|
2186
2190
|
return {
|
|
2187
2191
|
verdict: "fail",
|
|
2188
|
-
reason: `${problems.join("; ")}. A principal can apply an actor-verified \`${WAIVER_LABEL_REVIEW}\` label to skip this requirement, or post the missing/clean verdict comment(s)
|
|
2192
|
+
reason: `${problems.join("; ")}. A principal can apply an actor-verified \`${WAIVER_LABEL_REVIEW}\` label to skip this requirement, or post the missing/clean verdict comment(s).${ignoredNote}`,
|
|
2189
2193
|
waived: false
|
|
2190
2194
|
};
|
|
2191
2195
|
}
|