test(v0): D20 architectural boundary lint (Swiss Cheese Layer 6) #55
No reviewers
Labels
No labels
agent/claude-code
agent/codex
agent/gemini
agent/hermes
agent/iskra
agent/ollama
agent/patchwarden
area:business-model
area:competitive
area:discovery
area:forgejo
area:metrics
area:product-strategy
area:v0-core
cagan-grade-approved
client:platform
dependency/blocked
dependency/blocks-others
dependency/cross-repo
dependency/needs-confirmation
domain:agents
domain:ci
domain:docs
domain:forgejo
domain:infra
domain:memory
domain:runtime
domain:signal
domain:ux
flow/architecture
flow/blocked
flow/deployed
flow/done
flow/implementation
flow/intake
flow/maintained
flow/observed
flow/ready
flow/refining
flow/retired
flow/review
judge/codex-candidate
judge/hermes-candidate
judge/low-confidence
judge/needs-refinement
judge/operator-needed
judge/p0
judge/p1
judge/p2
judge/p3
judge/park
judge/patchwarden-candidate
judge/stale-priority
kind/adr
kind/bug
kind/chore
kind/feature
kind/infra
kind/ops
kind/refactor
kind/research
kind:artifact
kind:decision
kind:dogfood
kind:epic
kind:implementation
kind:research
merge/auto
merge/manual
merge/manual-dependency-conflict
merge/manual-failing-tests
merge/manual-merge-conflict
merge/manual-missing-review
merge/manual-operator-preference
merge/manual-red-zone
merge/manual-security-sensitive
merge/manual-unclear-scope
merge/manual-unknown
mode:operator-only
mode:patchwarden-iskra-approved
mode:safe-auto
observed/erroring
observed/needs-followup
observed/pending
observed/retire-candidate
observed/unused
observed/used
priority:p0
priority:p1
priority:p2
priority:p3
ready-for-agent
review:claude-reviewed
review:codex-reviewed
review:dziadek-reviewed
review:needs-human
safety:external-write
safety:no-prod-mutation
safety:prod-impact
safety:secret-touch
size/large
size/medium
size/small
size/tiny
size/unknown
source/adr
source/agent-generated
source/manual
source/operator-chat
source/voice-note
status:blocked
status:blocked-on-discovery
status:cagan-grade-review-pending
status:codex-ready
status:merged:pending-evidence
status:needs-evidence
status:needs-operator-decision
status:operator-needed
status:parked
tier:0-anchor
tier:0-platform-substrate
tier:1-core
tier:1-iskra-value-layer
tier:2-supporting
tier:2-tools-products-modules
type:bug
type:chore
type:docs
type:feat
type:policy
type:research
wave:1-foundation
wave:2-positioning
wave:3-validation
wave:4-economics
wave:5-operating
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
pdurlej/patchwarden!55
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "claude/patchwarden-d20-architectural-lint"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What
Closes Swiss Cheese Layer 6 gap (Scenariusz C z
docs/operations/code-vs-vision-snapshot-2026-05-27.md).Before this PR, D20 boundary was enforced by absence + convention —
forgejo_client.pysimply didn't definemerge_pull_request/submit_approval/create_pr_review, relying on every future cousin (or sub-agent) to remember the rule. That's fragile: a Sonnet sub-agent could plausibly addforgejo_client.merge_pull_request"to be helpful" and no automated check would catch it until a human noticed in the PR diff.This PR converts that into a build-time test. If the rule drifts, the lint fails loudly.
This is PR B of 3 defense-hardening PRs spun off the 2026-05-27 crosscheck.
What the lint checks
tests/test_d20_architectural_boundary.py(+216 lines):D20BoundaryLintTests— 4 tests against real sourcetest_src_dir_was_foundtest_no_module_calls_forbidden_function*.pycallingmerge_pull_request(,submit_approval(,create_pr_review(, or passingevent="APPROVED"test_no_module_uses_forbidden_url/mergeURL substring — blocks urllib bypasstest_forgejo_client_defines_no_forbidden_helpersforgejo_client.pyspecifically must not containdef merge_pull_request/def submit_approval/def create_pr_reviewD20BoundarySelfTest— 4 meta-tests (regex regression)These prevent vacuous green: a regex typo could make all lint tests pass silently. Self-tests prove the patterns actually match real violations.
test_lint_would_catch_a_synthetic_merge_callmerge_pull_request(123)matchestest_lint_would_catch_a_synthetic_approvalevent="APPROVED"matchestest_lint_would_catch_a_synthetic_merge_url.../pulls/1/mergematchestest_lint_ignores_safe_word_merge_basemerge_base(legit field) does NOT triggerComment-stripping crudity
_strip_line_commentsremoves everything after#per line so bare mentions in# NEVER call merge_pull_requestdon't false-positive. Limitation: a#inside a string literal would split the line incorrectly — but call patterns require(after the symbol anyway, so this is belt-and-suspenders, not the primary defense. False-negatives are acceptable; false-positives would be annoying.Why this matters
D20 boundary is a fundamental architectural choice — "Patchwarden never merges, never APPROVES." Before this PR:
docs/architecture.mdprose + the absence of helpersAfter this PR:
forgejo_client.pydefines a forbidden helperThis is precisely the kind of architectural enforcement that Bet 3 (Swiss Cheese stack defensibility) depends on.
Tests
144 → 152 green (+8 new, all pass on first run including the 4 self-tests).
Atomic per ADR-0017
re+pathlib)base=main, no stackingSwiss Cheese context
NOT breaking M2 gate
Test-only PR over existing v0. No new feature. Per
docs/decisions.mdD16 + M2 milestone notes, hardening existing capability is in-scope; this is exactly that.Token-accounting
~3-4% weekly Opus. Wrote regex + 4 self-tests directly without sub-agent — small enough that brief overhead would exceed delegation savings.