Repository navigation
Make cross-tenant review findings always Red (CROW-1321) - #1322
Conversation
The crow-review-pr grading rules let a reviewer cap a cross-tenant bug at Green: corveil/corveil#620's review wrote "Org A's keyword/regex rules apply to Org B's traffic", applied the accepted-risk rule, and approved. The bug later blocked every tenant's gateway traffic (corveil#4191). ReviewVerdictPolicy.gradingGuidanceBlock now opens with a cross-tenant floor, placed before the three caps: - Cross-tenant effect or exposure is Red. The accepted-risk, declined-finding and grade-against-the-diff rules don't apply to it, and each of those rules carries the exemption on its own line. - Deferral bar: a deferred finding needs an open tracking issue that this PR doesn't close, a `// DEFERRED(#N):` marker at the site, and an ADR exceptions row where the repo has one. The reviewer checks each one. - Tenant effect checklist: the four questions from the ticket. The skill body (both halves) adds a `### Tenant effect` section to the review format and a third Step 5a check that refuses a draft without the section, or one that says org A can change org B's outcome but has no Red. Step 4b no longer downgrades an unverified cross-tenant Red: when no code scopes the finding, that absence counts as the verification. The drift gate pins the new static prose. 🐦⬛ Generated with Claude Code, orchestrated by Crow Co-Authored-By: Claude <noreply@anthropic.com> Crow-Session: F86887C8-A1D3-415A-83CC-0621A934A4A2
dgershman
left a comment
There was a problem hiding this comment.
Code & Security Review
Critical Issues
None.
Architecture / Existing Patterns
- Existing pathway:
ReviewVerdictPolicy.gradingGuidanceBlock, substituted for{{CROW_REVIEW_GRADING_GUIDANCE}}on every harness. The review-body### Tenant effectsection, the Step 5a check, and the Step 4b sentence are static skill prose, the same shape as the CROW-974 guardrails and the CROW-1062 architecture study. This change extends that path.
The floor, deferral bar, and checklist sit in the policy block, so Claude's copied skill and the inlined prompts receive one text. Both skill halves match, and scripts/check-workspace-custom-instructions.sh passed.
Tenant effect
n/a — the diff is review-rubric prose and string-pin tests. It does not touch tenant-keyed rows, process-wide state built from them, or a function that takes a tenant argument.
Security Review
Strengths:
- The cross-tenant floor is the first grading rule and defines effect and exposure in the ticket's terms (
Packages/CrowCore/Sources/CrowCore/ReviewVerdictPolicy.swift). - Each cap's opening clause exempts a cross-tenant finding. The deferral bar requires an open issue this PR does not close, a
DEFERRED(#N)marker, and an ADR exception row where the repo has one. - Step 4b treats missing tenant-scoping code as the verification, so an unverified cross-tenant Red stays Red.
Concerns:
- Yellow — the declined-finding bullet still ends by instructing Green. See Code Quality.
Code Quality
- Yellow — the declined-finding line still says "leave it Green." Confirmed by reading
Packages/CrowCore/Sources/CrowCore/ReviewVerdictPolicy.swift:224. The floor says that rule does not apply to a cross-tenant finding, and the first clause now says the finding stays Red until it is fixed or clears the deferral bar. The same line then continues, unqualified: "Re-raising the same point at a blocking severity round after round is not permitted; if you remain unconvinced, leave it Green and, if it is worth tracking, file a follow-up issue instead of holding up the PR." That second sentence is the round-2 case this rule exists for — the author answered with a rationale and the reviewer is unconvinced. Read on its own terms, which is the #620 failure mode and the reasoneveryCapExemptsCrossTenantFindingspins the carve-out on the rule's own line, it tells the reviewer to stand down and grade Green. The pin atPackages/CrowCore/Tests/CrowCoreTests/ReviewVerdictPolicyTests.swift:361-362only requiresunless it is a cross-tenant finding, which stays **Red**somewhere on that line, so the trailing instruction still passes. Qualify the trailing sentence the way the grade-against-the-diff bullet qualifies its Green cap (ReviewVerdictPolicy.swift:225): a cross-tenant finding stays Red until it is fixed or clears the deferral bar. The accepted-risk bullet's second sentence (ReviewVerdictPolicy.swift:223) has the same shape — "let the verdict follow the grade, rather than blocking on it" — and should say the verdict follows the Red floor when the finding is cross-tenant. scripts/check-workspace-custom-instructions.shpassed.swift testforReviewVerdictPolicyTestsandSessionServiceReviewPromptTestsdid not run: compilation stopped because the volume was out of space. The pins were checked by reading the block against the assertions.
Summary Table
| Color | Meaning | Verdict effect |
|---|---|---|
| Red | Must fix | Request changes |
| Yellow | Should fix | Request changes |
| Green | Consider | Approve allowed |
Recommendation: Request Changes — driven by [0 Red, 1 Yellow, 0 Green] findings.
…321) Review feedback on #1322: the declined-finding and accepted-risk rules carved out cross-tenant findings in their first sentence, but their trailing instructions still said "leave it Green" and "let the verdict follow the grade" with no qualifier. A reviewer reading that sentence on its own would still stand down, which is the #620 failure mode. Every clause in a cap that grades Green or tells the reviewer to stand down now names the cross-tenant exception itself. A declined cross-tenant finding is restated at Red every round until it is fixed or clears the deferral bar, and a follow-up issue on its own is not a deferral. The new test everyStandDownClauseInACapCarvesOutCrossTenantFindings pins this per clause, not per line. It failed on all three clauses before this change. 🐦⬛ Generated with Claude Code, orchestrated by Crow Co-Authored-By: Claude <noreply@anthropic.com> Crow-Session: F86887C8-A1D3-415A-83CC-0621A934A4A2
dgershman
left a comment
There was a problem hiding this comment.
Code & Security Review
Critical Issues
None.
Architecture / Existing Patterns
- Existing pathway:
ReviewVerdictPolicy.gradingGuidanceBlock, substituted for{{CROW_REVIEW_GRADING_GUIDANCE}}byReviewVerdictPolicy.expandand then handed to both consumers inprepareReviewClone: the copiedSKILL.mdandbuildReviewPrompt. The### Tenant effectsection and the Step 5a check are static skill prose, the same shape as the CROW-974 guardrails. This change extends that path.
The floor, deferral bar, and checklist live in the policy block, so every harness gets one text. Both skill halves match. The follow-up on this PR qualifies each stand-down sentence in the three caps, and everyStandDownClauseInACapCarvesOutCrossTenantFindings passes.
Security Review
Strengths:
- The cross-tenant floor is the first grading rule. It defines effect and exposure, and it says the accepted-risk, declined-finding, and grade-against-the-diff rules do not apply (
Packages/CrowCore/Sources/CrowCore/ReviewVerdictPolicy.swift). - Each cap's own stand-down sentences carve a cross-tenant finding back to Red, including "leave it Green" and "rather than blocking."
- The deferral bar requires an open issue this PR does not close, a
// DEFERRED(#N):marker, and an exceptions-table row where the repo has a tenant-isolation ADR. A missing item keeps the finding Red. - Step 4b treats missing tenant-scoping code as the verification, so an unverified cross-tenant Red stays Red (
skills/crow-review-pr/SKILL.md). - Step 5a refuses a draft with no
### Tenant effectsection, or a "yes" to "Can org A's row change org B's outcome?" without a Red finding or a checked Deferred (#N) entry.
Concerns:
- Green — Step 5a mechanically gates question 2, not question 3. The checklist says a "yes" to "Is any org argument dropped, Nil, or optional?" is a cross-tenant finding unless the reviewer cites code that rejects the missing org or fails closed (
Packages/CrowCore/Sources/CrowCore/ReviewVerdictPolicy.swift). The tenant-effect check keys off question 2 and the words "cross-tenant finding" (skills/crow-review-pr/SKILL.md). A draft can answer question 3 "yes" without that citation and without labeling it a cross-tenant finding, and the check still passes. The ticket asked the verdict check to confirm the section is present; question 3 is the extra case left to the prose.
Code Quality
ReviewVerdictPolicyTests(28) andSessionServiceReviewPromptTests(21) passed.scripts/check-workspace-custom-instructions.shpassed.- The round-1 stand-down wording is fixed: a finding that is not cross-tenant is the only one those sentences tell the reviewer to leave Green.
Summary Table
| Color | Meaning | Verdict effect |
|---|---|---|
| Red | Must fix | Request changes |
| Yellow | Should fix | Request changes |
| Green | Consider | Approve allowed |
Recommendation: Approve — driven by [0 Red, 0 Yellow, 1 Green] findings.
Closes #1321
Part of epic corveil/corveil#4203 (wave 1). Incident: corveil/corveil#4191.
Problem
Two rules in
crow-review-prgraded a real cross-tenant bug Green. In corveil/corveil#620 the review said "Org A's keyword/regex rules apply to Org B's traffic". It capped that at Green under the accepted-risk rule and approved. The bug later blocked every tenant's gateway traffic.Change
This is a prompt-only change. I extended the existing
{{CROW_REVIEW_GRADING_GUIDANCE}}block instead of adding a placeholder. The floor is now the first rule in that block, so it always comes before the caps. It also reaches every harness through the path that already exists: the copiedSKILL.mdfor Claude, and the inlined prompt for Cursor, Codex, OpenCode, Grok, Antigravity and Muse.ReviewVerdictPolicy.gradingGuidanceBlocknow renders, in this order:gh issue view <N> --json state), and not one this PR closes;// DEFERRED(#N):marker at the site;n/a — <why>when the repo has no tenancy or the diff touches no tenant-scoped code. Repo rules fromCLAUDE.mdor an ADR can add to the floor but never lower it.skills/crow-review-pr/SKILL.md(both halves, byte-identical) changes in three places:### Tenant effectsection, required in every review.n/awhile citing tenant-scoped code, or answers "yes" to "Can org A's row change org B's outcome?" without a Red finding or a fully checkedDeferred (#N)entry. This is the semi-mechanical verdict check the ticket asks for.scripts/check-workspace-custom-instructions.shnow requires the new static prose in both halves.The text follows invariant 10 of the ADR 0093 draft (corveil/corveil#4204) but stays generic. Corveil-specific rules arrive through corveil's own
CLAUDE.md(corveil/corveil#4215).Tests
ReviewVerdictPolicyTests(CrowCore): 27 tests pass, 6 of them new.### Tenant effectsection, the Step 5a check and the Step 4b no-downgrade sentence are all present.SessionServiceReviewPromptTests(CrowEngine): 21 tests pass, 1 of them new. For all six inline harnesses, under both the default and red-only policies, the built prompt carries the floor before the accepted-risk rule. The accepted-risk line exempts cross-tenant findings, and the deferral bar and checklist are present.scripts/check-workspace-custom-instructions.shpasses.Not done here
crow-review-prfrom the repo checkout of the runningcrowd, both into{devRoot}/.claude/skills/and into each review clone. I didn't overwrite the installed copy from an unmerged branch, because every live review in the dev root reads it. After merge, rebuild and restartcrowd. The next review session will then show the floor, the### Tenant effectsection and the Step 5a check.reviewBlockingSeverities, which is Red in both the default and red-only policies.Sibling overlap
DEFERRED(#N)→ open-issue rule in corveil's CI. This PR makes the reviewer check the same thing.🤖 Generated with Claude Code