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
54 changes: 54 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,14 @@ jobs:
- name: Run policy attestation freshness gate
run: node scripts/build-policy-attestation.mjs --check

# Risk Surface Guard allowlist validation gate (ADR-047 decision 11): like the
# promotion-safety gates above, this must run BEFORE the base-branch checkout so
# it judges the PR's own proposed allowlist edits, not the base copy. This is an
# author-feedback gate, not the trust boundary -- the trust boundary is the
# base-pinned checkout of the allowlist file and its loader below.
- name: Run risk-surface-guard allowlist validation gate
run: node scripts/check-risk-surface-allowlist.mjs

# Mandatory Base-Branch Checkouts matching check-self-application.mjs criteria
# NOTE: These steps only run on pull requests where github.base_ref is defined.
# On push events, github.base_ref is empty, so these are skipped to prevent
Expand Down Expand Up @@ -124,6 +132,52 @@ jobs:
echo "scripts/check-decisions-authority.mjs not yet on base branch; skipping trust-isolation checkout (first introduction)."
fi

# Risk Surface Guard (ADR-047): base-pinned like guard-ratchet.mjs, so a PR
# cannot weaken the detector in the same diff that adds the risky pattern. This
# step, and the run step right after it, must both happen BEFORE "Load message
# catalog and its integrity gate from the base branch" below: that step's own
# bootstrap condition is already true today (check-message-catalog-integrity.mjs
# pre-exists on main), so on the PR that first introduces this gate it would
# unconditionally revert scripts/lib/message-catalog/index.mjs to the base copy,
# which does not yet import this gate's new catalog partial -- crashing this
# advisory step with "Unknown message id" on its own introducing PR if it ran
# any later. Running here avoids that; every PR after this one merges gets full,
# correct trust isolation either way.
- name: Load risk-surface-guard.mjs and its message catalog entry from the base branch
if: github.event_name == 'pull_request'
run: |
if git cat-file -e "origin/${{ github.base_ref }}:scripts/risk-surface-guard.mjs" 2>/dev/null; then
git checkout "origin/${{ github.base_ref }}" -- scripts/risk-surface-guard.mjs
git checkout "origin/${{ github.base_ref }}" -- scripts/lib/risk-surface-rules.mjs
git checkout "origin/${{ github.base_ref }}" -- scripts/lib/message-catalog/gate/risk-surface-guard.mjs
git checkout "origin/${{ github.base_ref }}" -- scripts/lib/message-catalog/index.mjs
else
echo "scripts/risk-surface-guard.mjs not yet on base branch; skipping trust-isolation checkout (first introduction)."
fi

# The suppression allowlist (ADR-047 decision 9): a separate, independently
# bootstrap-tolerant step rather than folding into the checkout above, because
# that step's own bootstrap condition may already be permanently true by the
# time this lands, which would make an unguarded checkout of the not-yet-on-base
# allowlist file hard-fail this step on its own introducing PR. Both the data
# file and its loader must be pinned: an unpinned loader could be rewritten by a
# PR to ignore expiry or match anything.
- name: Load the risk-surface-guard suppression allowlist from the base branch
if: github.event_name == 'pull_request'
run: |
if git cat-file -e "origin/${{ github.base_ref }}:.modonome/risk-surface-allowlist.json" 2>/dev/null; then
git checkout "origin/${{ github.base_ref }}" -- .modonome/risk-surface-allowlist.json
git checkout "origin/${{ github.base_ref }}" -- scripts/lib/risk-surface-allowlist.mjs
else
echo ".modonome/risk-surface-allowlist.json not yet on base branch; skipping trust-isolation checkout (first introduction)."
fi

# Advisory only: proves the gate is wired into CI (REQUIRED_GATES in
# check-self-application.mjs). Runs in --mode warn, which is contractually
# exit 0 regardless of findings, so it never fails this repository's own build.
- name: Run Risk Surface Guard scan (advisory)
run: node scripts/risk-surface-guard.mjs origin/main --mode warn

# Like check-decisions-authority.mjs above, this gate and the message
# catalog files that define its severity floors are new: the first PR
# that introduces them has no prior version on the base branch to load,
Expand Down
3 changes: 2 additions & 1 deletion .modonome/policy-attestation.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@
"check-promotion-readiness",
"check-regex-safety",
"check-repo-hygiene",
"check-risk-surface-allowlist",
"check-self-application",
"check-style",
"check-work-item-staleness",
Expand All @@ -111,5 +112,5 @@
"autonomy_enabled": false,
"dry_run": true
},
"content_digest": "sha256:68a93f336f3c0d8978dcbedcdcb1fdfae1cdb0fe9d16850ce1c7f7d667d20d88"
"content_digest": "sha256:6ba3670ed541353a52d70a0fb7f645dc1dd1c4d0da9d7c7dfa7fec232936a299"
}
4 changes: 4 additions & 0 deletions .modonome/risk-surface-allowlist.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"schema_version": 1,
"entries": []
}
2 changes: 1 addition & 1 deletion .modonome/snapshot/badge.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"color":"blue","label":"snapshot","message":"1029 files, 135721 tok","schemaVersion":1}
{"color":"blue","label":"snapshot","message":"1094 files, 139595 tok","schemaVersion":1}
2 changes: 1 addition & 1 deletion .modonome/snapshot/map.json

Large diffs are not rendered by default.

Loading
Loading