Repository navigation
Add advisory review and risk checks for the shadow week - #9
Conversation
The review job ran on pull_request with API secrets, so a branch could read them or post its own result. Greptile reviews these PRs instead. Closes gabe4coding/puredori#12 Co-authored-by: Gabe <gpavanello@thefork.com>
Port the risk-gate bypass fixes: the tier job runs on pull_request_target with the base workflow, rules and classifier, and the PR head is data. Python runs with -I, uv with --no-config, and the test job is read-only. Paths come from git diff --name-status -z. A test-only assertion edit or skip is T1, and a file list that disagrees with the diff is T2-hard. Greptile Review must be a success from greptile-apps on the current head. Anything else raises T0 to T1 and never lowers a higher tier. Reviewer config files are T2-hard. Co-authored-by: Gabe <gpavanello@thefork.com>
|
@greptileai review |
[Critical impact] Adds the risk classification system that gates all future merges. Add the missing review workflow before merging; neither promised review pass currently runs.
|
| if __name__ == "__main__": | ||
| sys.exit(main()) |
There was a problem hiding this comment.
| pull_request_target: | ||
| types: [opened, synchronize, reopened, ready_for_review, edited] |
There was a problem hiding this comment.
If this job reads a pending Greptile Review on a docs-only PR, the classifier raises it from T0 to T1. A later successful review triggers none of these events, so the label and check keep reporting the old tier until someone edits, reopens, or updates the PR. Add a trusted refresh path for completed reviews so the shadow-week results reflect the finished review.
| for line in _meaningful(f.added) + _meaningful(f.removed): | ||
| if WORKFLOW_SENSITIVE.search(line): | ||
| return Reason("T2-hard", "workflow-permissions-secrets-triggers", f"`{f.path}`: `{line.strip()[:80]}`") | ||
| added_keys = {_key(l) for l in _meaningful(f.added)} | ||
| for line in _meaningful(f.removed): | ||
| if not (BUMPABLE.search(_key(line)) and _key(line) in added_keys): | ||
| return Reason("T2-hard", "workflow-weakened", f"`{f.path}` removes `{line.strip()[:80]}`") | ||
| return Reason("T2-soft", "workflow-additive", f"`{f.path}`: additive edit or pinned version bump") |
There was a problem hiding this comment.
workflow_reason treats additions as harmless unless a line matches WORKFLOW_SENSITIVE. Adding if: false to the existing contracts job, or continue-on-error: true to its check step, returns T2-soft even though it disables a gate. Adding a trigger below an unchanged on: also escapes the trigger check. Compare the workflow structure before and after the edit so these changes receive T2-hard rather than understating their risk.
| if f.status == "D": | ||
| return Reason("T2-hard", "workflow-weakened", f"deletes workflow `{f.path}`") |
There was a problem hiding this comment.
Moving .github/workflows/test.yml outside .github/workflows/ without changing its contents returns T2-soft. Git records a rename with no added or removed lines, but this function only treats D as a removed workflow. GitHub stops running the moved file, so the rename removes all its checks. Treat moves out of the workflow directory, including moves to an unsupported extension, as workflow removal.
| elif line.startswith("+++ ") or line.startswith("--- ") or line.startswith("@@"): | ||
| continue |
There was a problem hiding this comment.
parse_diff drops changed content that looks like a file header. Removing a SQL or Lua comment such as -- explanation produces --- explanation in the diff, which this branch skips even inside a hunk. Removing 20 such comments therefore misses the comment-pruning HOLD rule. Track whether the parser is inside a hunk before skipping file headers.
A successful Greptile Review check is not a pass on its own. The latest summary from greptile-apps[bot] must score at least 4/5 on the current head, and that bot must not have a P0 or P1 inline comment on that SHA. A non-pass raises the tier one step and stops at T2-hard. The claude-code-mode#47 review (score 0/5, P1 on 5075dbc) is a fixture and is not a pass. Co-authored-by: Gabe <gpavanello@thefork.com>
Intent
Start the shadow week of the validation loop on this repo, the same setup as gabe4coding/puredori#10 and gabe4coding/retroagent#58. Two new checks run on every PR. Both are advisory: they are not required, they never fail a PR, and nothing here merges, approves or changes repo settings.
review(.github/workflows/review.yml): two passes in one job.ci/github/shall-review.yml..github/scripts/general_review.py): one tool-less, isolatedclaude -pcall that checks for bugs, missing or weaker tests, silent behavior changes, PR body vs diff drift and irreversible changes. The PR text is passed as untrusted data. It also records the risk-judgment JSON from the design, which does not change the tier yet.::noticethat names the secret.risk(.github/workflows/risk.yml,.github/scripts/risk_tier.py,.github/risk.yml): the deterministic tiers HOLD / T2-hard / T2-soft / T1 / T0.risk tiercheck run (success for T0/T1, neutral for T2/HOLD, never failure) and as arisk:*label.AUTOMERGEisoff, every PR becomes T2-hard. It is read only.hooks/hooks.jsononly. The design names none for loadout. loadout has no security boundary, and its main risk is hiding items: a wrong event registration can hide tools in every session.hooks/register.tsxandhooks/scope.tsstay T1, because they change in almost every PR and the "keep everything on failure" rule is covered by tests.tests/*.check.mjsare CI gates, so they count as code, not tests..github/scripts/test_risk_tier.pyandtest_general_review.py, run by therisk (classifier tests)job.Secrets to add (Settings > Secrets and variables > Actions):
TYPESAFE_API_KEY: needed for shall's Jev rule selection.ANTHROPIC_API_KEYorCLAUDE_CODE_OAUTH_TOKEN: needed forclaude -pin both passes.This PR is T2-hard by its own rules, because it adds
.github/risk.ymland the review and risk workflows. Gabe reviews and merges it.Evidence
uv run --no-project --python 3.12 --with pyyaml==6.0.2 --with pytest -- python -m pytest -q .github/scriptspasses. actionlint is clean on both workflows.node tests/docs.check.mjsandnode tests/version.check.mjs origin/mainpass.contract.check.mjsneeds Node 24 (it loads.tsfiles), so it runs in CI.T2-hard(hard paths:.github/risk.yml, the scripts and both workflows)..github/workflows/test.yml: additive edit or pinned version bumphooks/hooks.jsonTotals: T1 6, T2-soft 2.
Things to compare by hand this week: whether each T2-soft really needed you, and whether any T1 should have been T2.