Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/governance-shadow-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ jobs:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- run: node --test scripts/test-governance-shadow.cjs
- run: node --test scripts/test-governance-shadow.cjs scripts/test-risk-gate.cjs
290 changes: 244 additions & 46 deletions .github/workflows/risk-gate.yml

Large diffs are not rendered by default.

7 changes: 5 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ Organization documentation rules are checked by `python scripts/check_style.py`,
- Developer documentation is English by default; Chinese siblings use
`.zh-CN.md` and exist only where a reader needs them.
- New document names are lowercase with hyphens. `README.md`, `AGENTS.md`,
`CLAUDE.md` and other tool-defined names keep their spelling.
`CLAUDE.md` and other tool-defined names keep their spelling. The root
`PROPOSAL.md` keeps the filename explicitly requested for this governance decision.
- Temporary internal task reports, retrospectives, handoffs and progress
snapshots stay outside Git. Sanitized research baselines, design decisions,
financial and accounting contracts, provenance/PIT/license evidence, and
Expand Down Expand Up @@ -89,7 +90,9 @@ Organization documentation rules are checked by `python scripts/check_style.py`,
- Commit and PR titles use professional English, imperative Conventional Commits (`feat`, `fix`, `docs`, `refactor`, `test`, `chore`, `perf`, `ci`) and at most 72 characters. Sentence case is allowed; commitlint ignores merge commits.
- PR descriptions explain the problem, changes, verification and risk. AI-authored PRs identify the tool and the model when available. Labels and approval or risk summaries use professional English.
- Use `risk:low` for documentation, tests and ordinary checks; use `risk:high` and `needs-owner-approval` for protected paths. Classification labels describe risk; the path-based `risk-gate` determines whether approval is required.
- AI-only development uses required automated checks, template validation and independent AI review when available, with zero required human or code-owner approvals. High-risk paths require the owner's `owner-approved` label; agents must never add or remove it.
- AI-only development uses required automated checks, template validation and independent AI review when available, with zero required human or code-owner approvals. Ordinary high-risk paths need no manual label only when the trusted Codex App's latest `codex-review-state:v1` marker is completed for the exact current head with `has_unresolved_findings=false`, and every required check is green. Missing, stale, malformed, unreadable or changing evidence blocks automatic approval.
- The owner-only globs in [PROPOSAL.md](PROPOSAL.md#owner-only-paths) retain the existing owner-applied `owner-approved` requirement. They cover secrets, IAM, deployments, trading/execution and broker credentials, plus Kelly, stop-loss and this gate's trust configuration. Renames check both paths; docs and tests cannot exempt these globs. Agents must never add or remove approval labels.
- Automated path approval satisfies only `risk-gate`; it grants no merge, deployment, trading or configuration authority. The reusable governance shadow observer remains report-only. This proposal affects this repository only until separately adopted elsewhere.
- Preserve required checks and branch protection; merge only through normal checks. Use `<type>/<topic>` PR branches; never push directly to the default branch or force-push a reviewed branch. Commit, push, merge and production actions require authorization; existing authorization remains valid within its scope.

## Scope and authorization
Expand Down
174 changes: 174 additions & 0 deletions PROPOSAL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
# Automated approval for ordinary high-risk paths

Status: proposal for owner decision. Opening this Draft PR does not adopt it.

## Problem and rationale

The current path gate requires an owner-applied label for every high-risk
change, including ordinary strategy, risk and runtime code. It ignores an
already completed, clean Codex review and passing required checks. The proposed
policy removes that repeated manual step while keeping owner control over
sensitive operations and the gate's trust configuration.

## Decision rule

Low-risk paths retain their existing behavior. For other paths, inspect both
current and previous filenames before any docs or tests exemption.

An ordinary high-risk change passes `risk-gate` only when all of these hold:

1. The newest marker from a trusted Codex App is exactly one well-formed
`codex-review-state:v1` comment marker. The App IDs come from the existing
policy at the immutable trusted base revision, never the PR's candidate tree.
2. Its `code_review.commit_sha` equals the current PR head, its status is
`completed`, and `has_unresolved_findings` is the boolean `false`.
`has_any_findings` must also be a boolean; resolved findings are permitted.
A newer running, malformed or unresolved marker supersedes an older clean one.
3. All required contexts from classic branch protection and applicable rulesets
are discovered and successful for the same head. Missing protection evidence,
missing results, skipped/neutral checks, wrong App bindings and API errors
block automatic approval. Legacy statuses cannot prove an App-bound identity
and therefore cannot satisfy such a requirement.
4. No changed or previous path matches the owner-only list below. An old owner
label cannot substitute for missing automated evidence on ordinary high-risk
paths.

`risk-gate` is the output of this decision, so it cannot be its own prerequisite.
The locally validated `pr-template` result is published before the required-check
read. These are the only two self-produced contexts; their expected App binding
is checked. Every other required job stays mandatory. Native branch protection
and the required-check list stay unchanged.

## Owner-only paths

These are the exact implemented globs. Paths are converted from camelCase to
snake_case and lowercased before matching. `*` stays within one path segment;
`**/` matches zero or more directory segments. Broad credential, trading and
infrastructure families intentionally keep ambiguous files under owner control.
There is no docs, tests, examples or fixtures exemption for this list.

```text
**/*secret*
**/*secret*/**
**/*credential*
**/*credential*/**
**/.env*
**/*.pem
**/*.key
**/*.p12
**/*.pfx
**/*iam*
**/*iam*/**
**/*service_account*
**/*service_account*/**
**/*serviceaccount*
**/*serviceaccount*/**
**/*permission*
**/*permission*/**
**/*role_binding*
**/*role_binding*/**
**/*deploy*
**/*deploy*/**
**/*release*
**/*release*/**
**/*.tf
**/*.tfvars
**/dockerfile*
**/docker-compose*
**/compose.y*ml
**/vercel.json
**/wrangler.*
**/firebase.json
**/k8s/**
**/kubernetes/**
**/*trad*
**/*trad*/**
**/*execution*
**/*execution*/**
**/*executor*
**/*executor*/**
**/*order*
**/*order*/**
**/*broker*
**/*broker*/**
**/*kelly*
**/*kelly*/**
**/*stop*loss*
**/*stop*loss*/**
.github/governance/**
.github/workflows/risk-gate.yml
**/codeowners
```

The list includes the gate itself, its trust policy and CODEOWNERS because a PR
must not grant itself authority by modifying those files. Broker, order and
execution paths stay owner-only even when a filename does not say live. Kelly
and stop-loss paths stay protected; this proposal enables no trading feature.
Other workflow changes still require the automated high-risk rule.

Owner-only paths retain the existing verified owner-label event predicate.
This proposal neither strengthens that legacy human-identity proof nor allows
an agent to add/remove a label. A label is not operational authorization to
execute a deployment or trade.

## Execution and evidence freshness

The privileged workflow executes trusted workflow code only. It adds read-only
check access; status publication remains its only write capability. It never
checks out or executes a PR, reads secrets, writes labels, merges or deploys.
The existing shadow observer and its report-only policy are unchanged.

PR/comment/check/status events refresh evidence. The existing native CI workflows
also trigger reevaluation through `workflow_run`, because GitHub Actions check
events do not reliably trigger other workflows. The explicit workflow-name list
must be maintained if required native workflows are renamed or added. A manual
workflow dispatch remains an existing recovery entry, with no new schedule.

A read-only discovery job resolves every event to PR numbers. All event types
then share one concurrency group per PR without cancellation. Replaced pending
events evaluate the same PR; unrelated PRs never replace each other in the queue.
Each evaluation reconstructs evidence after restart; no persisted approval is
reused. It rereads PR head/base/body/labels, changed paths, owner events or
review/check/protection evidence before publishing. A mismatch or read failure
blocks the decision. API exception text and user bodies are never logged.

The final status is a snapshot, not a transaction with GitHub's mutable evidence.
Events refresh it and native branch protection still enforces checks at merge.
No local test proves hosted token permissions, event delivery or runtime adoption.
If protection APIs are unreadable to the installed token, the gate blocks;
this Draft PR requests no settings or permission changes outside workflow scope.

## Scope and adoption

Only this organization's metadata repository changes. Other repositories and
callers pinned to older revisions do not adopt this proposal automatically.
The workflow diff, local rules, tests and this decision document form one review
unit. The root filename `PROPOSAL.md` is explicitly requested for this decision;
the naming checker permits only this root path, while nested uppercase proposal
filenames remain invalid. No security or identifier rule is relaxed.

This PR stays Draft for the owner's decision and the established review bot.
The current base-branch risk gate still governs this PR. Opening it does not
satisfy its own approval gate and does not authorize merge, undraft, labels,
repository/organization settings, deployment or live activation.

## Validation

`node --test scripts/test-risk-gate.cjs` executes the actual embedded workflow
script with fake native API data. It checks the positive decision, invalid or
stale review markers, required checks and App identities, protected paths,
renames, event routing, read failures, redaction, concurrent evaluations and
snapshot drift. The existing CI test job runs these tests alongside the shadow
engine tests. Run all available Python checker/scanner tests and the phase-2 docs
checker before publication. The PR description records the actual results.

## Rollback

Through a separately authorized reviewed change, restore the previous
`risk-gate.yml` and the corresponding AGENTS approval wording, remove the added
risk tests from the CI command and revert this proposal's document/name-only
changes. Keep every existing native required check, the owner-only controls and
branch protection enabled. Reevaluate affected open PRs under the restored gate
using its existing workflow-dispatch input; do not reuse successful statuses
from the proposed policy as proof of legacy owner approval. No deployment,
settings edit, history rewrite or label change is part of this Draft PR.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Organization-wide files for QuantStrategyLab repositories.
| [Secret scanning](docs/secret-scanning.md) | Publication guard, PR check and weekly all-branch scan templates. |
| `profile/README.md` | Text shown on the organization's GitHub profile page. |
| `PULL_REQUEST_TEMPLATE.md` | Default pull request template for repositories without their own. |
| [Automated risk approval proposal](PROPOSAL.md) | Approval conditions, owner-only paths and rollback. |
| `AGENTS.md` | AI contribution and maintenance rules for this repository. |

## Contributing
Expand Down
1 change: 1 addition & 0 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ QuantStrategyLab 各仓库共用的组织级文件。
| [密钥扫描](docs/secret-scanning.md) | 发布前扫描、PR 检查和每周全分支扫描模板。 |
| `profile/README.md` | 组织 GitHub 主页上显示的介绍。 |
| `PULL_REQUEST_TEMPLATE.md` | 没有自带模板的仓库默认使用的 pull request 模板。 |
| [风险门禁自动批准提案](PROPOSAL.md) | 自动批准条件、保留 owner 审批的路径和回退方式。 |
| `AGENTS.md` | 本仓库的 AI 参与与维护规则。 |

## 参与贡献
Expand Down
2 changes: 1 addition & 1 deletion scripts/check_style.py
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ def check_naming(root: Path, report: Report) -> None:
f"{rel}: English is the base name (section 4); .en.md is not a suffix",
)

if core not in CONVENTIONAL_UPPER and core not in GITHUB_TEMPLATE_NAMES:
if rel != "PROPOSAL.md" and core not in CONVENTIONAL_UPPER and core not in GITHUB_TEMPLATE_NAMES:
if core != core.lower():
report.add("naming", f"{rel}: file name must be lowercase (section 7)")
if "_" in core:
Expand Down
Loading
Loading