@attalabs/vinaya 0.26.0 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -3
- package/aeg-root/enforcement.md +5 -5
- package/aeg-root/milestone-model.md +2 -0
- package/aeg-root/process.md +1 -1
- package/aeg-root/roles/developer.md +23 -17
- package/aeg-root/roles/planner.md +4 -0
- package/aeg-root/roles/principal.md +8 -2
- package/aeg-root/roles/reviewer.md +9 -5
- package/aeg-root/roles/security.md +9 -5
- package/dist/checks/bin/check-body-bare-digits.js +1038 -257
- package/dist/checks/bin/check-branch-topology.js +963 -199
- package/dist/checks/bin/check-brief-shape.js +1276 -203
- package/dist/checks/bin/check-changeset-coverage.js +1677 -281
- package/dist/checks/bin/check-closes-n.js +967 -203
- package/dist/checks/bin/check-coherence.js +1127 -284
- package/dist/checks/bin/check-dead-branch-push.js +910 -193
- package/dist/checks/bin/check-dispatch-readiness.js +1189 -295
- package/dist/checks/bin/check-doc-coverage-push.js +1673 -277
- package/dist/checks/bin/check-doc-coverage.js +1675 -279
- package/dist/checks/bin/check-doctrine-no-procedures.js +1020 -257
- package/dist/checks/bin/check-doctrine-portability.js +1672 -276
- package/dist/checks/bin/check-evidence-fresh.js +2526 -322
- package/dist/checks/bin/check-exec-bits.js +1670 -274
- package/dist/checks/bin/check-first-push-dispatch.js +1075 -265
- package/dist/checks/bin/check-issue-assignment.js +965 -201
- package/dist/checks/bin/check-issue-milestone-attach.js +5734 -0
- package/dist/checks/bin/check-issue-objectives-numbering.js +5736 -0
- package/dist/checks/bin/check-issue-parts-coverage.js +5736 -0
- package/dist/checks/bin/check-issue-surface-globs.js +6910 -0
- package/dist/checks/bin/check-issue-title-grammar.js +5736 -0
- package/dist/checks/bin/check-issue-tranche-label.js +5736 -0
- package/dist/checks/bin/check-main-branch-refusal.js +910 -193
- package/dist/checks/bin/check-no-disk-state.js +910 -193
- package/dist/checks/bin/check-pr-premise-reassert.js +1020 -257
- package/dist/checks/bin/check-pr-report-density.js +910 -193
- package/dist/checks/bin/check-quoted-command.js +1651 -274
- package/dist/checks/bin/check-reader-resolvable-prose.js +1655 -278
- package/dist/checks/bin/check-registry-gates.js +997 -193
- package/dist/checks/bin/check-retired-vocabulary.js +1649 -272
- package/dist/checks/bin/check-review-gate.js +1163 -316
- package/dist/checks/bin/check-single-plan-pr.js +910 -193
- package/dist/checks/bin/check-surface-scope.js +973 -201
- package/dist/checks/bin/check-test-plan.js +939 -206
- package/dist/checks/bin/check-token-collection-wired.js +910 -193
- package/dist/checks/bin/check-token-report.js +910 -193
- package/dist/checks/bin/check-workspace-escape.js +1668 -272
- package/dist/index.js +10602 -5352
- package/dist/lib/pre-push-changed-files.js +57 -0
- package/dist/lib/pre-push-select-tests.js +1473 -0
- package/package.json +4 -2
|
@@ -56,6 +56,8 @@ A pass started via `vinaya dispatch security --agent <vendor>` carries its role
|
|
|
56
56
|
- The task Issue carries the brief, frozen on its `aeg:brief:v1` comment.
|
|
57
57
|
- Your single question: **could this change leak a secret, widen an attack surface, or misconfigure auth/permissions/agent tooling?**
|
|
58
58
|
|
|
59
|
+
**Dispatched by `vinaya dev-review-loop` (unattended)?** You do not run `vinaya review post` yourself — write `findings.txt` and `report.txt` to the work directory the dispatch names, plus `objectives.txt` (one `O<n>|MET|<evidence>` or `O<n>|NOT MET|<evidence>` line per objective) whenever the task carries objectives; leave `findings.txt` empty if there are none. A work directory still missing a required file after the dispatch is an infrastructure failure, not a clean pass.
|
|
60
|
+
|
|
59
61
|
## Entry gate (self-locating) — refuse if it isn't your turn
|
|
60
62
|
|
|
61
63
|
- **No open PR** → *"Nothing to security-review — no open PR."*
|
|
@@ -91,7 +93,7 @@ When the PR touches agent/skill/hook definitions, MCP configs, or anything under
|
|
|
91
93
|
|
|
92
94
|
## Output format
|
|
93
95
|
|
|
94
|
-
**Run `vinaya review post --role security` with this data; do not hand-type a verdict comment.** The `VERDICT:` line is bare — no bold, no heading, no blockquote — it is machine-read by the pre-merge review gate. So is the `Judged head:` line immediately below it: the gate binds your verdict to the exact commit you reviewed, and a verdict that does not cover the PR's current head does not count as clean, however clean its `VERDICT:` value is (`review-gate.ts`). A third head line, `Objectives version:`, binds your verdict the same way to the objectives list you judged it against — a hash the command computes from the Issue's (or the PR body's) `## Objectives` list; if the Issue's objectives change after you cast a verdict, the gate treats it exactly like a stale head. A verdict also holds for a later head whose patch identity equals the judged head's: the gate compares `git diff <base>...<sha> | git patch-id --stable` on both sides, so a merge from the main branch or a rebase that leaves the PR's own patch untouched keeps your verdict alive rather than costing a round to re-cast it over changes you already read. That comparison ignores whitespace, so a whitespace-only push also keeps your verdict; any change to non-whitespace content does not, and comes back to you. Free-typing this shape into `gh pr comment` is no longer the sanctioned path — a decorated heading or a bolded/blockquoted line the gate's line-anchored parser cannot see reaches the forge looking correct to a human reader and is invisible to `verify-review-gate.ts`, with no pointer back to what was wrong until CI goes red. `vinaya review post` resolves the PR's real head itself (`gh pr view --json headRefOid` — never a self-reported sha), renders every structural line from your validated inputs, posts the comment, and refuses to exit 0 unless its own post re-parses clean through the exact same `extractSecurityReviewVerdict` function the gate calls:
|
|
96
|
+
**Run `vinaya review post --role security` with this data; do not hand-type a verdict comment.** The `VERDICT:` line is bare — no bold, no heading, no blockquote — it is machine-read by the pre-merge review gate. So is the `Judged head:` line immediately below it: the gate binds your verdict to the exact commit you reviewed, and a verdict that does not cover the PR's current head does not count as clean, however clean its `VERDICT:` value is (`review-gate.ts`). A third head line, `Objectives version:`, binds your verdict the same way to the objectives list you judged it against — a hash the command computes from the Issue's (or the PR body's) `## Objectives` list; if the Issue's objectives change after you cast a verdict, the gate treats it exactly like a stale head. A fourth line, `Ruling ordinal:`, renders unconditionally — `0` when the PR carried no principal ruling at cast time — and binds the same way to the newest principal ruling on the PR (`review-validity-v1` task 3): a ruling posted after you cast your verdict turns the gate red exactly like a stale head, until you re-cast against it. A verdict also holds for a later head whose patch identity equals the judged head's: the gate compares `git diff <base>...<sha> | git patch-id --stable` on both sides, so a merge from the main branch or a rebase that leaves the PR's own patch untouched keeps your verdict alive rather than costing a round to re-cast it over changes you already read. That comparison ignores whitespace, so a whitespace-only push also keeps your verdict; any change to non-whitespace content does not, and comes back to you. Free-typing this shape into `gh pr comment` is no longer the sanctioned path — a decorated heading or a bolded/blockquoted line the gate's line-anchored parser cannot see reaches the forge looking correct to a human reader and is invisible to `verify-review-gate.ts`, with no pointer back to what was wrong until CI goes red. `vinaya review post` resolves the PR's real head itself (`gh pr view --json headRefOid` — never a self-reported sha), renders every structural line from your validated inputs, posts the comment, and refuses to exit 0 unless its own post re-parses clean through the exact same `extractSecurityReviewVerdict` function the gate calls:
|
|
95
97
|
|
|
96
98
|
```
|
|
97
99
|
vinaya review post --role security --pr <n> --verdict PASS|FAIL \
|
|
@@ -102,7 +104,7 @@ vinaya review post --role security --pr <n> --verdict PASS|FAIL \
|
|
|
102
104
|
|
|
103
105
|
The findings file is one finding per line, `SEVERITY|file:line|description` (`|`-delimited: `file:line` already contains a colon), severity one of `CRITICAL|HIGH|MEDIUM|LOW`. Omit `--findings-file` for zero findings.
|
|
104
106
|
|
|
105
|
-
The objectives file is one line per objective, `O<n>|MET|<evidence>` or `O<n>|NOT MET|<evidence>` — the same `|`-delimited shape, evidence being the rest of the line. **Judge MET/NOT MET from the diff, never from the Developer's own report.** `--objectives-file` is required whenever the closed Issue (or the PR body's own `## Objectives` section) has a list to judge; its ids must cover that list exactly. An Issue that predates the objectives cutover renders no `Objectives version:` line and no block at all. The command renders this exact shape (kept here so a human or a debugging agent can still read what it produces — this is documentation, not something to write by hand):
|
|
107
|
+
The objectives file is one line per objective, `O<n>|MET|<evidence>` or `O<n>|NOT MET|<evidence>` — the same `|`-delimited shape, evidence being the rest of the line. **Judge MET/NOT MET from the diff, never from the Developer's own report.** **`NOT MET` requires a code or test location as its evidence** — a real `file:line`, the same shape a finding's own location takes, naming where the objective is unmet in the diff. Evidence that names only a PR body section, a comment, or a role file is not a location the objective's own unmetness lives at — the dev-review-loop's own report parser reclassifies such a line `MET (prose note)` before it ever reaches a round's outcome, the same `isProseLocation` predicate (`@attalabs/aeg-core`) the body-located `MINOR` cap already applies to a finding's location, so writing one costs the round nothing but a wasted line: it never blocks, it is never re-litigated next round, and it is not what you intended. If the objective is genuinely unmet, point at the code that fails to meet it. **`NOT MET` means you verified the objective is not met — never a decline.** An objective outside your own lens (a code-reviewer-shaped objective reaching a security verdict, or the reverse) is `MET`, citing the other reviewer's evidence or verifying it yourself directly — never `NOT MET` with an out-of-scope note; a reviewer that declines an objective this way forces a review round over nothing. `--objectives-file` is required whenever the closed Issue (or the PR body's own `## Objectives` section) has a list to judge; its ids must cover that list exactly. An Issue that predates the objectives cutover renders no `Objectives version:` line and no block at all. The command renders this exact shape (kept here so a human or a debugging agent can still read what it produces — this is documentation, not something to write by hand):
|
|
106
108
|
|
|
107
109
|
```
|
|
108
110
|
VERDICT: PASS | FAIL
|
|
@@ -111,6 +113,8 @@ Judged head: <sha>
|
|
|
111
113
|
|
|
112
114
|
Objectives version: <hash>
|
|
113
115
|
|
|
116
|
+
Ruling ordinal: <k>
|
|
117
|
+
|
|
114
118
|
FINDINGS (ordered by severity):
|
|
115
119
|
1. [CRITICAL|HIGH|MEDIUM|LOW] <file:line> — <what and why>
|
|
116
120
|
2. ...
|
|
@@ -126,15 +130,15 @@ SECRETS: [none found | listed above, redacted]
|
|
|
126
130
|
<!-- AEG:CLAIM: packages/aeg-core/src/verdict-extraction.ts contains:function firstFiveLines(comment: string): string { -->
|
|
127
131
|
<!-- AEG:CLAIM: apps/cli/src/commands/review-post.ts contains:export function renderEscalationComment(input: EscalationInput): string { -->
|
|
128
132
|
<!-- AEG:CLAIM: apps/cli/src/commands/review-post.ts contains:export function checkRenderedComment(body: string, expectation: RenderExpectation): RenderCheckResult { -->
|
|
129
|
-
Before its own post reaches the forge, `vinaya review post` refuses to post anything the gate would misread: it runs the exact same `VERDICT:`/`Judged head:`/`Objectives version:` extraction the merge gate uses over the rendered comment, and requires exactly the intended verdict to come back — and refuses outright if you pass a `--verdict PASS` together with any `NOT MET` objective, the same contradiction check `--verdict APPROVE` gets. Free text in a finding, `--config-scan`, `--secrets`, or an objective's evidence can say `VERDICT` or span multiple lines without risk — the extraction reads only a comment's first five lines, and in a code-reviewer or security comment a caller field never opens one of those lines (it only trails a fixed, renderer-owned label); an escalation's `--summary`
|
|
133
|
+
Before its own post reaches the forge, `vinaya review post` refuses to post anything the gate would misread: it runs the exact same `VERDICT:`/`Judged head:`/`Objectives version:`/`Ruling ordinal:` extraction the merge gate uses over the rendered comment, and requires exactly the intended verdict to come back — and refuses outright if you pass a `--verdict PASS` together with any `NOT MET` objective, the same contradiction check `--verdict APPROVE` gets. Free text in a finding, `--config-scan`, `--secrets`, or an objective's evidence can say `VERDICT` or span multiple lines without risk — the extraction reads only a comment's first five lines (the ruling ordinal's own first-seven-line window is wider still), and in a code-reviewer or security comment a caller field never opens one of those lines (it only trails a fixed, renderer-owned label); an escalation's `--summary` occupies line 7 at minimum (`Ruling ordinal:` renders unconditionally ahead of it), which is exactly why this pre-post re-parse exists rather than construction alone.
|
|
130
134
|
|
|
131
135
|
- **CRITICAL** — leaked live credential, auth bypass, key sent to client. Any CRITICAL → FAIL.
|
|
132
136
|
- **HIGH** — likely exploitable misconfig or injection surface.
|
|
133
137
|
- **MEDIUM/LOW** — hardening notes.
|
|
134
138
|
|
|
135
|
-
|
|
139
|
+
CRITICAL always drives VERDICT FAIL. HIGH, MEDIUM, and LOW block only when the repository's policy says so — this repository's own `reviewPolicy.securityThreshold` decides how far down the `CRITICAL > HIGH > MEDIUM > LOW` scale a finding still fails the round. You do not type that decision by hand: `vinaya review post` derives it from the findings file you pass it, against that configured threshold — FAIL iff a finding at or above the threshold is present, PASS otherwise — and refuses before posting anything if `--verdict` disagrees with the derivation, naming the derived value. A finding whose own `file:line` names the PR body, a comment, or a role file is capped to MINOR by the policy evaluator before it counts — MINOR is not on this scale at all, so such a finding never fails the round regardless of the severity you assign it. Write its real severity anyway; the cap is applied for you.
|
|
136
140
|
|
|
137
|
-
A re-pass after the Developer's fixes follows the same re-review rule as the code role: report the state of every prior id (`open`, `fix-claimed`, `reproduced`, `resolved`) in the finding's own description, `F<n> <class> <state>: <text>`, before listing anything new — `vinaya review post` refuses a findings file that drops a prior id with no state token. Every prior objective reappears too — a re-pass's `--objectives-file` that drops a prior `O<n>` is refused before posting, the id read from the prior comment's own `OBJECTIVES:` block. Round two is delta-only for
|
|
141
|
+
A re-pass after the Developer's fixes follows the same re-review rule as the code role: report the state of every prior id (`open`, `fix-claimed`, `reproduced`, `resolved`) in the finding's own description, `F<n> <class> <state>: <text>`, before listing anything new — `vinaya review post` refuses a findings file that drops a prior id with no state token. Every prior objective reappears too — a re-pass's `--objectives-file` that drops a prior `O<n>` is refused before posting, the id read from the prior comment's own `OBJECTIVES:` block. Round two is delta-only for every non-blocking severity under this repository's policy: a finding below the configured `securityThreshold` whose `file:line` falls outside the diff since the previously judged head is refused. A finding at or above the threshold outside the delta still drives the verdict on any round and is always accepted. A prior CRITICAL/HIGH you mark `resolved` keeps its severity in the record but no longer drives the verdict — `vinaya review post` derives the verdict only from findings not marked `resolved`; mark `fix-claimed` or `reproduced` instead if it is not actually fixed.
|
|
138
142
|
|
|
139
143
|
The `SECRETS:` line is evidence-backed, not asserted: the secret scanner's pasted output (check 1) must appear in the verdict comment above it — necessary evidence that the scan ran, never sufficient on its own, since the judgment half of check 1 still stands behind the claim. `SECRETS: none found` with no scan output pasted is an unbacked self-attestation — the exact claim this check exists to catch in others' work, not to commit in your own. `vinaya review post` mechanizes this: passing `--secrets "none found"` without `--secrets-evidence-file <path>` (the actual pasted scanner output) is refused outright.
|
|
140
144
|
|