Nine-axis scorecard: 8 PASS + 1 CONDITIONAL→fixed. SHIP Phase 0 (0.84). G-015: cross-const test MUST assert absolute 800/0 values (paired-drift guard) — already in P1-02-01. G-016: docs-build job MUST need go-test (firewall-gates-docs-build, no false-green) — applied to P3-01-01. D-001 filter enforced; NFR purity gate enforceable. ---ci--- project: oy phase: 0 milestone: v0.4 status: grill tag_base: v0.3.x milestone_type: nfr ---/ci---
53 KiB
Grill Review: OpenYield (oy) — v0.2 (The Mesh) Phase 0
Reviewer: CIAgent adversarial grill (red-team, full autonomy) Date: 2026-08-17 Target: Phase 0 artifacts (PROJECT.md, REQUIREMENTS.md, ARCHITECTURE.md, RESEARCH.md, PERSONAS.md, PLANS.md, config.json) + v0.1 codebase baseline Milestone: v0.2 — The Mesh Autonomy: full (decision_confidence_threshold = 0.60) Mode: multi-project (slug
oy)
Methodology
Each of nine axes was scored by gathering concrete evidence (commit hash, file:line, REQ-ID, D-decision, A-assumption). Missing or contradictory evidence is itself a finding. Forcing questions were answered from the artifacts; where confidence ≥ 0.60 a binding verdict was reached. No escalations were required — all axes resolved at ≥ 0.60 confidence after binding fixes are applied.
Evidence baseline (verified against the actual repo, not the docs)
- v0.1 codebase: 15 modules under
x/(confirmed via globx/**/types/types.go). go.mod:module github.com/oy/openyield,go 1.22, zero dependencies (confirmed).- Test functions: 53 (counted via
grep -rh "^func Test" x/). PROJECT.md D-033 says "48-test baseline"; RESEARCH.md §2 says "53-test baseline pattern". Inconsistent — both refer to v0.1. - Test files: 11
*_test.go(not 15 — modulesidentity,processing,rootpool,vaulthave no test files). - Zero lexicon assertion tests exist in v0.1 (
grep -rli lexicon x/returns empty). D-032 claims lexicon is "carried from v0.1" — but v0.1 never had a lexicon test. The claim is aspirational, not historical. - Zero
genesis.gofiles exist; allValidateGenesisare no-ops intypes.go(e.g.feecovenant/types/types.go:122returnsnilunconditionally). - No project-wide lexicon meta-test exists.
x/bearers/types/types_test.goalready importsx/processing/typescross-module (line 7) — an existing precedent for cross-module test imports, but it is a test import, not a production import cycle.- Current lexicon cleanliness:
grep -rni "bank\|account" x/ --include="*.go"returns zero hits — v0.1 is lexicon-clean in practice, but the firewall is nonexistent.
These baseline facts drive several binding decisions below. The plan's narrative ("matching v0.1's 48-test baseline", "lexicon carried from v0.1") is partly fictional — v0.1 has no lexicon test and the test count is misstated in two places. This does not block v0.2; it means the lexicon firewall is new work in v0.2, not inherited work.
Per-Axis Verdicts
Axis 1 — Feasibility (skeleton + tests, zero deps) — PASS (confidence 0.85)
The v0.2 scope (Go types + keeper stubs + unit tests, no live chain) is genuinely achievable with zero external deps. v0.1 proved the pattern: 15 modules compile under go 1.22 with only encoding/json. RESEARCH.md §4 is explicit and correct that pulling cosmos-sdk/ibc-go would force a 1000+ module tree and break the fast test loop. The ICS-20 packet shape is self-contained Go structs (A-215), the Forex oracle is a Go interface (no impl), and bearers are pure types (no LoRa/BLE libs). No axis-1 risk reaches the escalation threshold.
The one caveat: the Window "fuller" implementation (D-023) is the most ambitious skeleton and rate-limit semantics are unstated (A-206, conf 0.65). But the plan correctly scopes the skeleton to a simple counter (actionsConsumed vs maxActions), which is trivially feasible. Confidence holds.
Axis 2 — Scope (over/under, D-020..D-033 defensibility) — CONDITIONAL (confidence 0.72)
D-020..D-033 are individually defensible and well-reasoned. The skeleton-first choice (D-020, conf 0.85), one-chain IBC representative (D-021), Mission Lock as const (D-022), Window-fuller (D-023), and the deferred items (Cover seniority Q7, Pier credential routing Q5, Solana IBC) all correctly defer operational/Phase-3 concerns. The scope is not over-scoped for a skeleton milestone.
However: (a) D-033's claim of "matching v0.1's 48-test baseline" is factually wrong — v0.1 has 53 tests and no lexicon tests (see baseline). (b) D-032's claim that lexicon is "carried from v0.1" is false — v0.1 has no lexicon test. These are documentation defects, not scope defects, but they mislead future phases about what is inherited vs. new. Binding fix G-002 corrects the narrative; the scope itself is sound.
Axis 3 — Cost / Effort (30 tasks / 5 phases) — PASS (confidence 0.80)
30 tasks across 5 phases for 10 new/extended packages is proportionate. Each package gets a types task + a test task + (for 3 of them) a genesis task, plus 5 cross-cutting lead-developer ship tasks. No phase exceeds 8 tasks; the largest (P4) has 8 tasks for 3 packages which is reasonable. There is no bloat: there are no "research", "spike", or "documentation" tasks masquerading as execution; every task produces a concrete .go file or a verifiable gate. The 5 lead-developer verification tasks (one per phase) are the correct overhead for vertical-slice shippability. Reject the "too many tasks" hypothesis.
Minor: P5 has 4 tasks for pure audit/ship, which is light but appropriate (no new code). No binding change.
Axis 4 — Architecture (module layout, blocker chain, import cycles) — PASS (confidence 0.82)
The 9-new + 1-extended layout maps cleanly to the blocker chain in ARCHITECTURE.md §5.2. D-031's phase ordering (P1 Orgs+Window → P2 Pacts+Partners → P3 Councils+Forex → P4 Bonds+Bearers+L2 → P5 ship) respects the dependency graph: Window is a leaf (P1) so P2/P3/P4 reference it by WindowRef id-string; Stand (P1) blocks Pact StandRegistry (P2), Council Stand-Council (P3), and Bond issuer (P4); Guild (P1) blocks Council Guild-Council (P3). A-203 (by-ID-string refs) is the correct cycle-avoidance strategy and is consistently applied in RESEARCH.md §3's interface diagram.
One concern: RESEARCH.md §3 says x/window "may be imported by Pacts/Partners/Orgs if needed (but skeleton keeps it by-ref too)" — this hedge could permit a future struct import. The plan's task specs (P2-01-01, P2-02-01) correctly use window-id-ref strings, so the skeleton is safe. Binding fix G-003 makes the by-ref rule a hard invariant test so the hedge cannot silently become an import cycle.
Axis 5 — Risk (15 assumptions, hidden traps) — CONDITIONAL (confidence 0.70)
The 15 assumptions (A-201..A-215) are mostly sound and well-scored. The highest-confidence (A-201 dep-free 0.95, A-202 one-module-per-component 0.90, A-209 bearers-extend 0.90, A-215 no-deps-vendored 0.90, A-210 lexicon-coupon 1.00) are correct. The lower-confidence assumptions are correctly flagged for v0.3 revisit (A-206 rate-limit 0.65, A-213 Shadow Stand 0.60, A-214 spread cap 0.70).
Three hidden traps:
- Lexicon firewall is new, not inherited (see baseline). The plan assumes v0.1 had a lexicon test; it did not. The P5 meta-test (P5-01-01) is therefore load-bearing for the entire project's lexicon integrity, not just v0.2. This raises its criticality — binding fix G-004 moves the lexicon meta-test earlier (into P1) as a scaffolding test that fails-empty initially, so drift is caught from the first phase, not at P5 when 10 packages already exist.
x/bearers/types/types_test.goalready cross-importsx/processing/types(line 7). This is a test import, not a production import cycle, but it means the bearers test package is already coupled. When P4 extendsx/bearers, the existing test must stay green (P4-02-02 acknowledges this). No binding change — the plan already flags the regression risk — but it is a latent coupling the grill records.- A-207 (one
x/pactmodule with PactType enum, conf 0.80) contradicts D-027's wording ("separate keeper modules"). RESEARCH.md §1.7 explicitly reconciles this ("one module with a PactType enum satisfies 'separate execute entries' without 6 dirs"), but the contradiction between the D-decision text and the chosen implementation is a documentation smell. Binding fix G-005 clarifies D-027's intent. Confidence holds; no escalation.
Axis 6 — Requirement Coverage (all v0.2 REQs, gaps, double-counting) — PASS (confidence 0.84)
Cross-referencing RESEARCH.md §6 (Cross-Reference Summary) against PLANS.md per-phase REQ coverage and REQUIREMENTS.md:
| REQ | Component | Module | Phase | Depth | Covered? |
|---|---|---|---|---|---|
| REQ-009 | L2 IBC | x/satellite | P4 | Skeleton (Polygon rep) D-021 | ✅ P4-03-01/02 |
| REQ-011 | Councils | x/council | P3 | Skeleton + Mission Lock D-022 | ✅ P3-01-01/02/03 |
| REQ-015 | Window | x/window | P1 | Fuller D-023 | ✅ P1-01-01/02/03 |
| REQ-016 | 9 Stands | x/stand | P1 | Skeleton D-024 | ✅ P1-02-01/02/03 |
| REQ-017 | Guilds | x/guild | P1 | Skeleton D-025 | ✅ P1-03-01/02 |
| REQ-018 | Partners | x/partner | P2 | Skeleton D-026 | ✅ P2-02-01/02 |
| REQ-020 | 6 Pacts | x/pact | P2 | Skeleton D-027 | ✅ P2-01-01/02 |
| REQ-021 | Bonds | x/bond | P4 | Skeleton D-028 | ✅ P4-01-01/02/03 |
| Bearers | OY-LR+Beacon | x/bearers (ext) | P4 | Stubs D-029 | ✅ P4-02-01/02 |
| Forex | Forex v1 | x/forex | P3 | Stub D-030 | ✅ P3-02-01/02 |
| REQ-012 | Lexicon | (all) | all | D-032 | ✅ per-module + P5 meta |
Every v0.2 REQ maps to at least one types task + one test task. No REQ is double-counted across phases. REQ-012 (lexicon) is correctly cross-cutting. No gaps. The coverage is complete and non-overlapping. The only caveat (G-004) is when the lexicon meta-test lands, not whether it covers REQ-012.
Axis 7 — Persona Assignment (territory globs, conflicts) — CONDITIONAL (confidence 0.74)
Persona→task assignments are coherent with territory globs:
- backend-engineer (
x/**/types/**,x/**/keeper/**,x/**/module.go,go.mod) → P2-01-01, P2-02-01, P4-01-01 (pact/partner/bond types). ✅ - cosmos-engineer (
x/{satellite,council,window,stand,guild,partner,pact,bond,forex,bearers}/**,go.mod) → P1-01-01, P1-02-01, P1-03-01, P3-01-01, P3-02-01, P4-02-01, P4-03-01. ✅ - data-engineer (
x/**/types/genesis*.go,x/**/genesis*.go) → P1-01-03, P1-02-03, P3-01-03, P4-01-03. ✅ - security-engineer (
x/**/types/**_test.go,x/**/keeper/**_test.go,x/**/genesis_test.go) → all*_test.gotasks. ✅ - lead-developer (
**) → all wave-3 ship tasks. ✅
Territory conflicts (warn mode, non-blocking but recorded):
go.modis claimed by both backend-engineer and cosmos-engineer (and referenced in 5 persona entries). Since v0.2 adds zero deps (A-201),go.modshould not change in v0.2. Binding fix G-006: no persona touchesgo.modin v0.2; if a dep is needed, it is an escalation (would violate A-201).x/partner/**,x/pact/**,x/bond/**are claimed by both cosmos-engineer and backend-engineer (cosmos-engineer's explicit list; backend-engineer'sx/**/types/**glob). The plan assigns P2-01-01 (pact) and P2-02-01 (partner) and P4-01-01 (bond) to backend-engineer, but cosmos-engineer's territory list includes these same paths. This is a genuine overlap. Since territory enforcement iswarn(config.json), it does not block, but it is ambiguous ownership. Binding fix G-007 clarifies: cosmos-engineer owns the Cosmos-convention-shaped modules (window, stand, guild, council, satellite, forex, bearers-ext); backend-engineer owns the non-Cosmos-mirroring modules (pact, partner, bond). This matches the actual task assignments and removes the glob overlap.- security-engineer's
x/**/genesis_test.goterritory overlaps data-engineer'sx/**/genesis*.gofor the test file. The plan assigns genesis test logic to data-engineer's genesis tasks (e.g. P1-01-03 says "audit-log ordering test green"). Ambiguous who writesgenesis_test.go. Binding fix G-008: genesis schema (genesis.go) = data-engineer; genesis test assertions (*_test.go) = security-engineer. The plan's task verbiage already implies this; G-008 makes it explicit.
No conflict is severe enough to block at warn mode. Confidence holds after fixes.
Axis 8 — Testability (must-haves verifiable, invariants concrete) — PASS (confidence 0.83)
Every must-have in the plan has a concrete verification:
- Locked-const tests are concrete: 9 Stands (P1-02-02), 4 Partner tiers (P2-02-02), 6 Pacts (P2-01-02), 3 Councils (P3-01-02), Mission Lock
== false(P3-01-02), Bond cap 800 / floor 0 + clamp invariant (P4-01-02), Guild fee 0 (P1-03-02), 5 L2 chains (P4-03-02), ICS-20 packet field names (P4-03-02). Each is a single-const-change-breaks-a-test firewall. ✅ - Invariant tests are concrete: Window lifecycle Open→Active→Revoked→Expired, revoke-after-expire no-op, double-revoke idempotent-or-error (P1-01-02); bond clamp below→floor / above→cap / in-range→unchanged (P4-01-02); Mission Lock non-amendable (P3-01-02). ✅
- Lexicon assertions: per-module (10 files) + P5 meta-test. ✅ (with G-004 moving meta-test earlier)
- Coverage: ≥80% per package, table-driven on every exported const/struct/method (D-033). Achievable for low-cyclomatic-complexity skeletons. ✅
One gap: P5-01-01's lexicon meta-test verification says "a deliberately-injected banned term in any new file fails the meta-test (verified by a temporary spike)". A "temporary spike" is not a durable test. Binding fix G-009: the meta-test must include a self-test — a table of synthetic strings (one per banned term) asserted to be detected, so the meta-test is durably verified without manual spikes. Confidence holds.
Axis 9 — Phase Ordering & Shippability (patches, v0.1.5 milestone) — PASS (confidence 0.81)
Each phase is independently shippable as a patch on the v0.1.x line:
- P1 →
v0.1.1(window, stand, guild): ships 3 foundation modules;go test ./...green; no upstream blocker. ✅ - P2 →
v0.1.2(pact, partner): ships 2 modules referencing P1 by-id-string; blocked-by P1 types only. ✅ - P3 →
v0.1.3(council, forex): ships 2 modules; council blocked-by P1 stand+guild (by-id); forex independent. ✅ - P4 →
v0.1.4(bond, bearers-ext, satellite): ships 3 packages; bond blocked-by P1 stand; bearers/satellite independent; v0.1 baseline regression-gated. ✅ - P5 →
v0.1.5(audit + ship): final phase patch IS the milestone release per D-008/D-020. ROADMAP.md Phase 2 checkbox updated. ✅
The tag arithmetic is correct: P0 → v0.1.0 (already shipped per ROADMAP "MVP deferred"), P1..P4 → v0.1.1..v0.1.4, P5 → v0.1.5 = v0.2 milestone. No separate minor tag (D-008). The blocker chain is honored: all cross-phase hard blockers (P1-01-01, P1-02-01, P1-03-01) are in P1 Wave 1, so P2/P3/P4 Wave 1 can start once P1 types land (the plan's "soft ordering for branch hygiene" note is correct — types only depend on listed hard blockers).
One observation: ROADMAP.md still shows v0.1 phases as v0.0.0..v0.0.9 (lines 4-13) and says "MVP release (v0.1.0) deferred". PROJECT.md says v0.1 was "released as v0.0.9 per run.md patch-line model" (line 90). The v0.1.x tag base for v0.2 (config.json tag_base: v0.1.x, PLANS.md line 7) is internally consistent but the ROADMAP's v0.0.x history is a separate patch line. This is a documentation reconciliation item, not a blocker. Binding fix G-010: P5-01-03 (REQ-coverage audit) must also reconcile ROADMAP.md's v0.0.x → v0.1.x tag-line note so the milestone release (v0.1.5) is not confused with the v0.0.x pre-MVP line.
Binding Decisions
These are binding — the orchestrator MUST apply them before P1 begins. Numbered G-001..G-010.
| ID | Decision | Rationale | Confidence | Affects |
|---|---|---|---|---|
| G-001 | Correct the v0.1 baseline test-count claim. PROJECT.md D-033 says "48-test baseline"; RESEARCH.md §2 says "53-test baseline pattern". Actual count is 53 func Test* across 11 files. Standardize on "53 tests across 11 test files (v0.1 baseline)". |
Both docs cite v0.1 test counts and disagree with each other; the actual count (verified grep -rh "^func Test" x/ | wc -l = 53) is the source of truth. Future phases must not cite a phantom 48. |
0.95 | PROJECT.md D-033, RESEARCH.md §2 |
| G-002 | Correct the lexicon-inheritance narrative. D-032 says lexicon is "carried from v0.1"; D-033 says "matching v0.1's 48-test baseline" re lexicon. v0.1 has zero lexicon tests (verified: grep -rli lexicon x/ empty). State explicitly: "Lexicon assertion tests are NEW in v0.2; v0.1 is lexicon-clean in practice but has no test firewall. v0.2 introduces the firewall." |
The current narrative implies inherited work that does not exist, understating v0.2's new-work scope and the criticality of the P5 meta-test. | 0.92 | PROJECT.md D-032/D-033, RESEARCH.md §2 |
| G-003 | Add a hard invariant test in P1-01-02 (Window test file) asserting that no v0.2 production (non-test) .go file under x/ imports another x/<module>/types package by struct — i.e. enforce A-203 (by-ID-string refs) as a tested invariant, not just a convention. Use go/ast or go/parser to scan imports, OR a simpler package-import grep test. |
RESEARCH.md §3 hedges that Window "may be imported by Pacts/Partners/Orgs if needed". A hedge in a design doc can silently become an import cycle. Make the rule a test that fails on violation. | 0.78 | P1-01-02, RESEARCH.md §3 |
| G-004 | Introduce the project-wide lexicon meta-test scaffolding in P1 Wave 3 (new task P1-04-02, lead-developer), not P5. The P1 version scans x/**/*.go for the 9 banned terms and passes (v0.1 is clean). It fails-empty initially for the 3 new P1 packages only if drift occurs. P5-01-01 then extends the already-existing meta-test to cover all 10 packages rather than creating it from scratch at audit time. |
Per G-002, the lexicon firewall is new in v0.2. Creating it only at P5 means drift in P1-P4 is undetected until audit, when 10 packages already exist and remediation is expensive. A P1 scaffolding meta-test catches drift from the first phase. This is the single most material fix. | 0.88 | PLANS.md P1, P5-01-01 |
| G-005 | Clarify D-027 wording: "six Pacts as one x/pact module with a PactType enum + six per-type execute-entry structs (A-207), NOT six micro-modules." Update the D-027 decision text in PROJECT.md to remove the "separate keeper modules" ambiguity that contradicts A-207. |
D-027 says "separate keeper modules"; A-207 and RESEARCH §1.7 say one module. The contradiction is a documentation smell that could cause a future agent to spawn 6 dirs. | 0.85 | PROJECT.md D-027 |
| G-006 | No persona may modify go.mod in v0.2. v0.2 adds zero external dependencies (A-201, conf 0.95). Any go.mod change in P1-P5 is an immediate escalation (would violate D-020/A-201). Remove go.mod from active persona territories for v0.2, or add a constraint: "go.mod is read-only in v0.2; any change triggers escalation." |
go.mod is claimed by both backend-engineer and cosmos-engineer (5 mentions). Since it must not change, the overlap is moot but ambiguous. Making it explicitly read-only removes the ambiguity and protects the zero-dep invariant. |
0.90 | PERSONAS.md, config.json |
| G-007 | Disambiguate x/pact, x/partner, x/bond ownership. backend-engineer owns these three (non-Cosmos-mirroring modules: pact = custom commitment types, partner = custom registry, bond = custom clamp). cosmos-engineer owns x/window, x/stand, x/guild, x/council, x/satellite, x/forex, x/bearers (Cosmos-convention-mirroring: x/authz, x/group, x/gov, x/ibc-transfer, x/capability shapes). Update cosmos-engineer's territory list in PERSONAS.md to remove x/pact, x/partner, x/bond. |
Both personas' territory globs overlap on these three paths. The actual PLANS.md task assignments (P2-01-01, P2-02-01, P4-01-01 → backend-engineer) already resolve it operationally, but the globs in PERSONAS.md still conflict. Align the globs with the assignments. | 0.82 | PERSONAS.md |
| G-008 | Genesis file ownership split: data-engineer owns genesis.go / genesis schema (the GenesisState struct, ValidateGenesis body). security-engineer owns *_test.go including genesis test assertions (genesis_test.go if separate, or the genesis assertions within types_test.go). Update PERSONAS.md so security-engineer's territory explicitly includes x/**/genesis_test.go and data-engineer's explicitly excludes *_test.go. |
data-engineer's x/**/genesis*.go glob currently matches genesis_test.go, overlapping security-engineer's x/**/genesis_test.go. Plan tasks (e.g. P1-01-03) say "test green" but the test-author persona is security-engineer. Clarify schema vs test ownership. |
0.80 | PERSONAS.md, PLANS.md genesis tasks |
| G-009 | P5-01-01 lexicon meta-test must include a self-test table: a hardcoded slice of synthetic strings, one per banned term (e.g. "bank account", "interest yield", "currency dollar euro", "deposit savings depositor"), asserted to each trigger the meta-test's detection. This durably verifies the meta-test detects all 9 banned terms without a manual "temporary spike". |
P5-01-01's verification relies on "a temporary spike" to confirm detection — a manual step that won't be repeated. A self-test table makes the meta-test's coverage itself tested. | 0.86 | PLANS.md P5-01-01 |
| G-010 | P5-01-03 (REQ-coverage audit) must additionally reconcile the ROADMAP.md tag-line narrative: add a one-line note that v0.1 pre-MVP shipped on the v0.0.x line (ROADMAP lines 4-13) and v0.2 ships on the v0.1.x line (config tag_base), so the v0.1.5 milestone release is not confused with a v0.1.0 "MVP" tag. |
ROADMAP.md says "MVP release (v0.1.0) deferred" while v0.2's P0 is v0.1.0 per PLANS.md. The two v0.1.0 references mean different things. A reconciliation note prevents a tag collision or confusion at ship time. |
0.75 | PLANS.md P5-01-03, ROADMAP.md |
Escalations
None. All nine axes resolved at confidence ≥ 0.60 after the binding fixes G-001..G-010 are applied. No axis required escalation to the human. At full autonomy, the orchestrator applies the binding decisions and proceeds to P1.
Overall Verdict
SHIP Phase 0 with binding changes
The v0.2 Phase 0 plan is fundamentally sound: the skeleton+tests scope is feasible, zero-dep, well-decomposed, REQ-complete, and properly ordered along the blocker chain. The plan is not over-scoped, not unfeasible, and not token-bloated — 30 tasks for 10 packages is proportionate.
The binding changes are documentation/test-firewall corrections, not scope rework. The most material is G-004 (move the lexicon meta-test scaffolding from P5 to P1): the plan's single biggest hidden risk is that it treats the lexicon firewall as inherited from v0.1 when it is in fact new work, and creates it only at the final audit phase. Catching drift from P1 is cheap; catching it at P5 after 10 packages exist is expensive. G-001/G-002 correct the factual errors in the baseline narrative. G-003/G-005/G-006/G-007/G-008/G-009/G-010 close smaller ambiguities (import-cycle invariant, D-027 wording, go.mod read-only, persona territory overlaps, meta-test self-coverage, tag-line reconciliation).
None of these rise to "rethink" — the architecture, scope, and ordering are correct. Apply the 10 binding decisions and proceed to Phase P1.
Confidence in overall verdict: 0.83
Summary Block
Per-axis verdicts:
1. Feasibility — PASS (0.85)
2. Scope — CONDITIONAL (0.72) → fixed by G-001, G-002
3. Cost/Effort — PASS (0.80)
4. Architecture — PASS (0.82) → strengthened by G-003
5. Risk — CONDITIONAL (0.70) → fixed by G-004, G-005
6. Req Coverage — PASS (0.84)
7. Persona Assignment — CONDITIONAL (0.74) → fixed by G-006, G-007, G-008
8. Testability — PASS (0.83) → strengthened by G-009
9. Phase/Ship — PASS (0.81) → strengthened by G-010
Binding decisions: 10 (G-001..G-010)
Escalations: 0
Overall: SHIP Phase 0 with binding changes (confidence 0.83)
v0.3 Grill (Phase 0)
Reviewer: CIAgent adversarial grill (red-team, full autonomy) Date: 2026-08-17 Target: v0.3 Phase 0 artifacts (PROJECT.md D-034..D-046, ROADMAP.md v0.3 table, REQUIREMENTS.md REQ-010/022..028, ARCHITECTURE.md v0.3 section, PERSONAS.md v0.3 roster, RESEARCH.md A-301..A-315, PLANS.md v0.3 plan 50 tasks P1-P6) + v0.1/v0.2 codebase baseline Milestone: v0.3 — Bearers & Documentation Autonomy: full (decision_confidence_threshold = 0.60) Mode: multi-project (slug
oy)
Evidence baseline (verified against the actual repo, not the docs)
go.mod:module github.com/oy/openyield,go 1.22, zero dependencies (confirmed — no require lines).x/modules: 25 (confirmed vials x/ | wc -l). v0.1 = 15 + v0.2 added 10 = 25. v0.3 adds 4 new (exit/bridge/hub/services) + extends 3 (bearers/partner/bond) = 29 distinct after v0.3. PLANS.md P6-01-01 says "29 packages" — correct.- Test functions: 299
func Test*across 23 test files (countedgrep -rn "^func Test" x/ | wc -l). v0.2 grill G-001 corrected to 53/11; v0.2 added ~246 more. The v0.3 plan's "~303" reference (REQUIREMENTS IDEATE notes) is close to current 299 — minor, not material. lexicon/lexicon.go:BannedTerms()returns 10 (verifiedif len(terms) != 10at lexicon.go). The v0.2lexicon_meta_test.goheader comment still says "9 banned terms" (line 7) — a pre-existing v0.2 documentation defect, not v0.3's, but the v0.3 docs firewall must use 10 (it does — P1-01-01 says exactly 10). No v0.3 binding needed; note for v0.2 housekeeping.lexicon_meta_test.goEXISTS at repo root, packagelexicon_meta, with the G-009 self-test table (synthetic strings,len(synthetic) != len(terms)guard). The self-test verifies detection but does NOT verify the walk (which files are scanned). The v0.3 docs firewall inherits this gap — G-013 below.- G-003 import-invariant test EXISTS:
x/window/types/types_test.go:431-456usesgo/parser(ImportsOnly) to scan all non-test.gounderx/and assert no cross-x/<module>/typesstruct imports. Confirmed production code is cycle-free (grep -rn "openyield/x/" x/ --include="*.go" | grep -v "_test.go"returns empty). The v0.3 by-ID-string refs (exit→bridge, hub→partner, services→window) will be auto-covered by this existing test — no new G-003 work needed in v0.3 (P4/P5 test tasks correctly say "G-003 import-invariant green" not "add a new one"). x/bond/types/types.go:CouponCapBps = 800,CouponFloorBps = 0,func Clamp(couponBps uint32) uint32all exist. The D-028 regression firewall is real and the v0.3 GrowthBond extension reuses the same package (no G-003 concern, correct).x/partner/types/types.go:TierAnchor PartnerTier = "Anchor"exists; noAnchorCredentialyet (v0.3 P4 adds it). The 4-tier enum is locked — P4 extension adds a struct, not a tier. Correct.x/bearers/types/types.go:BearerOYSATandBearerOYQRare ALREADY inAllBearers()(lines 20-21, 39-40) since v0.1;BearerTransportinterface +OYLRLink+SurveillanceResistantexist since v0.2. P4 addsOYSATLink/OYQRCodetransport structs only —AllBearers()count (6) stays unchanged. Correct.x/window,x/stand,x/satellite(L2Chain/TransferChannel),x/watcher,x/identity(Reach) all exist as v0.1/v0.2 baseline — the v0.3 by-ID-string refs to them (bridge→satellite/watcher, services→window/identity, bond→stand) have real targets. No phantom refs.- No
docs/, noREADME.md, nomkdocs.ymlexist today. The firewall-first ordering is clean:lexicon_meta_docs_test.gopasses vacuously with zero docs to scan (no hits possible). The "chicken-and-egg between firewall test and README" risk raised in the grill brief is a non-issue — verified by walk logic (zero files = zero hits).
These baseline facts confirm the v0.3 plan's architecture and ordering claims against the actual codebase, not just the docs. The plan is unusually well-grounded; the binding decisions below are mostly small correctness fixes, not scope rework.
Per-Axis Verdicts
Axis 1 — Feasibility (50 tasks / 6 phases at full autonomy) — PASS (confidence 0.82)
50 tasks across 6 phases is large but proportionate to the deliverable: 26 docs pages (P1-P3) + 7 x/* packages (P4-P5) + audit/ship (P6). v0.2 shipped 31 tasks / 5 phases for 10 packages at full autonomy and closed clean (per REQUIREMENTS.md v0.2 summary). v0.3 adds the docs surface (a genuinely new artifact type) and 4 new + 3 extended Go packages. The docs phases (P1-P3) are low-risk Markdown authoring gated by a Go firewall; the Bearers phases (P4-P5) are pure skeleton+tests, the proven v0.1/v0.2 pattern. No phase exceeds 12 tasks (P3 is largest at 12, all docs). The firewall-first claim is achievable: a firewall that scans zero files passes trivially (verified — no docs exist yet). Reject the "50 tasks is too many" hypothesis — it matches the deliverable surface.
Axis 2 — Scope (Bearers + docs bundle, ~22 pages) — PASS (confidence 0.78)
D-034 (bundle Bearers + docs under one feature milestone) is defensible: the user's --ideate request was docs-only, but ROADMAP Phase 3 (Bearers) is the next queued feature work; bundling keeps the milestone cadence and avoids a docs-only NFR milestone that would not advance the protocol. The alternative (separate v0.3 docs NFR + v0.4 Bearers) would split a coherent unit of work into two milestones and delay the Bearers skeleton a full cycle. The ~20-25 page docs depth (D-045) is bounded — not gold-plating; each page maps to a REQ (REQUIREMENTS.md IDEATE traceability table). The one scope concern: the mkdocs.yml nav in RESEARCH §2.1 is missing 2 pages the plan creates — nomads/window.md (P2-01-07) and freeholders/anchor-preview.md (P3-01-07). This is a documentation/plan inconsistency, not a scope defect — binding fix G-011 requires the nav to list all created pages. Confidence holds.
Axis 3 — Cost (MkDocs Material, firewall extension) — PASS (confidence 0.85)
MkDocs Material is the right cost: Markdown-native (docs-writer authors .md, not YAML/HTML), build-only Python dep that does NOT touch go.mod (verified zero require lines; G-006 holds). Plain-Markdown-no-generator would be cheaper but loses nav/search/theme — for a user-facing docs site for nomads/freeholders, Material's search + audience nav is real value, not gold-plating. The firewall extension (D-043: a whole new sibling test lexicon_meta_docs_test.go + self-test table) is more than "could review manually" — manual review is not durable; the sibling test is the firewall that keeps the docs site lexicon-clean over time (the top risk per Axis 5). The cost is justified. Reject the "firewall is over-engineered for docs" hypothesis — REQ-012 is All phases and docs are user-facing; a manual review would rot.
Axis 4 — Technical soundness (by-ID-string refs, ClampGrowth, LendingCouponCapBps) — CONDITIONAL (confidence 0.72)
The by-ID-string refs (G-003) between bridge/exit/partner/hub/services are cycle-free by construction (verified: the existing G-003 import-invariant test in x/window/types/types_test.go scans all non-test x/**/*.go and will auto-cover the v0.3 files; production code has zero cross-x/ imports today). The x/hub LendingCouponCapBps = 800 LOCAL const (A-304) is a real invariant — it's a local copy cross-documented to D-028, exactly mirroring how v0.2 x/guild cross-docs x/feecovenant.WaiverHandPassGuild (verified pattern). The one technical defect: ClampGrowth(currentBps, growthBps uint32) uint32 as specified in RESEARCH §1.7 / PLANS P5-03-01 returns min(CouponCapBps - currentBps, growthBps), which underflows when currentBps > CouponCapBps (uint32 subtraction wraps to a huge value, then min picks growthBps — wrong) or when currentBps == cap (returns 0, correct) but is fragile. The invariant "post-growth coupon ≤ 800" only holds if the caller guarantees currentBps ≤ cap. The spec does not state this precondition, and a GrowthBond whose current coupon is already at cap would silently allow unbounded growth via the growthBps path if the helper is misused. Binding fix G-012 requires ClampGrowth to guard currentBps > CouponCapBps explicitly (return 0 or error) so the invariant holds unconditionally. Confidence holds after the fix.
Axis 5 — Risk (lexicon drift, "yield" in docs) — CONDITIONAL (confidence 0.74)
The top risk is correctly identified: "yield" is banned as a standalone word but "OpenYield" is safe (word-boundary regex, verified TestLexiconMetaNoFalsePositiveOnOpenYield); PROJECT.md uses "real yield" but docs must say "real production"/"real return". The firewall-first ordering (D-044: P1 firewall before P2/P3 content) is the correct mitigation — a banned term slipped into a P2 nomads page fails the P2 build, not the P6 review. The firewall extension (D-043) is sufficient to CATCH drift at build time. The gap: the firewall does not PREVENT the docs-writer from authoring a banned term in the first place — it fails the phase build, requiring a rewrite. For 26 pages this is acceptable (the failure is loud and local); for 100+ pages it would be painful. At the v0.3 scale, the firewall is sufficient. The deeper risk: the docs firewall self-test table (G-009 for docs) verifies DETECTION but not the WALK — if the walk logic misses docs/nomads/ (e.g., a path-prefix bug), the self-test still passes (it tests FindBannedTerm on synthetic strings, not the file walk). Binding fix G-013 requires the docs firewall to include a walk-coverage assertion: a test that injects a synthetic banned-term .md into a temp docs/ subtree (or uses a fixture) and asserts the walk FINDS it. Without this, the docs firewall could silently scan zero files and report green. Confidence holds after the fix.
Axis 6 — Dependency (intra-P4, P4→P5, hidden edges) — PASS (confidence 0.80)
The intra-P4 edge (bridge→exit) and the P4→P5 edge (partner-Anchor→hub) are the only v0.3-internal ordering constraints, and both are correctly handled (P4 Wave 1 = bridge before exit Wave 2; P4 before P5 for Anchor→hub). The RESEARCH §3 cross-component dependency list is complete for the v0.3 surface. Verified the "hidden edges" raised in the grill brief:
- Does x/services need x/window types? Yes, by-ID-string (
window-idfield) — but x/window is v0.2 baseline, already shipped. Not a v0.3 phase-ordering concern. Correctly noted in RESEARCH §3. - Does x/bond GrowthBond need x/anything? No — GrowthBond embeds the v0.2 Bond (same package,
x/bond/types), andClampGrowthreuses the same-package consts. No G-003 concern. Correct. - Does x/hub need x/bond? No — it uses a LOCAL const
LendingCouponCapBps = 800(A-304) to avoid the import. Correct (verified pattern matches v0.2 guild/feecovenant). - Does x/exit need x/bread? No —
amount-grainis int64, "Grain" by name only (P4-02-01 explicitly says "NOT ax/breadimport"). Correct.
No hidden edges. The P5 "no intra-phase ordering" claim (hub/services/bond independent) is correct — they reference only v0.1/v0.2 baseline modules by ID-string, not each other.
Axis 7 — Testing (≥80% on skeletons, docs phases no Go coverage) — CONDITIONAL (confidence 0.70)
≥80% coverage on skeleton type packages is achievable but borders on coverage theater (testing getters/constructors/enum-round-trips on trivial types). v0.2 hit ≥95.9% on 8 of 10 packages at this bar, so it's not theater in practice — the locked-const + invariant + lexicon assertions carry real regression value. The plan correctly applies the ≥80% bar only to the 7 x/* packages (P4/P5), NOT to the docs phases (P1-P3 produce no Go code except the firewall test, which is itself the coverage). The one gap: P1-01-01 (the docs firewall) and P4/P5 test files have no explicit coverage target — the firewall test's own coverage is not asserted. A firewall test that scans zero files (walk bug) would still have high coverage on its detection logic. Binding fix G-013 (walk-coverage assertion, from Axis 5) addresses this — the injected-fixture test forces the walk to actually execute. No separate coverage bar needed for docs phases. Confidence holds.
Axis 8 — Maintainability (docs lexicon over time, sibling test drift) — CONDITIONAL (confidence 0.72)
The docs site will stay lexicon-clean over time ONLY if the firewall runs on every change. D-046 defers publishing CI to v0.4, but the firewall is a go test — it runs locally and in any CI that runs go test ./.... The risk is not "no CI" (the firewall runs wherever go test runs) but "the sibling test lexicon_meta_docs_test.go drifts from lexicon_meta_test.go". D-043 chose a sibling (not an extension) to preserve v0.2 coverage — defensible — but two meta-tests sharing detection logic via the same lexicon.FindBannedTerm is good; sharing the self-test table by DUPLICATION (not by a shared helper) is the drift risk. If lexicon_meta_test.go's self-test table is updated (e.g., a new banned term added) and lexicon_meta_docs_test.go's copy is not, the docs firewall silently loses coverage. Binding fix G-014: the docs firewall's self-test table and banned-term count assertion should DERIVE from lexicon.BannedTerms() (which both already do for the count — good) and ideally share the synthetic-string table via a lexicon package helper rather than duplicating it. At minimum, both must assert len(terms) == 10 from the single source lexicon.BannedTerms() so a count change breaks both. This is a low-severity maintainability note, not a blocker. Confidence holds.
Axis 9 — Adversarial (what makes v0.3 fail to ship) — PASS (confidence 0.78)
The four failure modes raised in the grill brief:
- Firewall blocks docs content mid-authoring — MITIGATED by firewall-first (P1 firewall passes vacuously with zero docs; P2/P3 content fails fast and local, not at P6). Not a ship-blocker.
- By-ID-string ref breaks when a target module is renamed — LOW. The targets (satellite/watcher/identity/window/stand) are v0.1/v0.2 baseline, locked. v0.3 does not rename them. The by-ID-string fields are opaque strings, not Go imports, so a rename would only break tests that hardcode the ID — caught at
go test. Not a ship-blocker. - MkDocs build fails on Gitea Pages — NON-ISSUE for v0.3. D-046 explicitly defers publishing CI to v0.4; v0.3 ships the source + a
mkdocs buildinvocation in the README. A Gitea Pages failure is a v0.4 concern, not v0.3's. Not a ship-blocker. - Coverage drops below 80% on a skeleton package with trivial types — MITIGATED. v0.2 hit ≥95.9% on 8/10 packages at this bar; the locked-const + invariant + lexicon assertions provide real coverage. The v0.3 packages (bridge/exit/hub/services) follow the v0.2 satellite/forex pattern that hit 100%. Not a ship-blocker.
The actual highest ship-risk is ClampGrowth underflow (G-012) if a test constructs a GrowthBond at cap and the helper misbehaves — but this is caught by the invariant test (post-growth ≤ 800) if the test exercises the cap boundary. Binding fix G-012 makes the helper robust; the test must cover currentBps == cap and currentBps > cap. No escalation.
Binding Decisions
These are binding — the orchestrator MUST apply them before P1 begins. Numbered G-011..G-014 (continuing from the v0.2 grill G-001..G-010).
| ID | Decision | Rationale | Confidence | Affects |
|---|---|---|---|---|
| G-011 | mkdocs.yml nav MUST list every page the plan creates. RESEARCH §2.1's sample mkdocs.yml is missing nomads/window.md (P2-01-07) and freeholders/anchor-preview.md (P3-01-07). P1-01-02 (mkdocs.yml authoring) must include all 26 pages in the nav (or the nav is incomplete at P3 ship). The nav may reference not-yet-existing pages at P1 (mkdocs.yml is config, not Go-tested) but must be complete by P3-03-01 ship. |
The RESEARCH sample nav and the PLANS page list disagree by 2 pages. A stale nav ships a docs site with orphaned pages (created but not linked). | 0.85 | P1-01-02 (mkdocs.yml), P3-03-01 (ship verification — confirm nav matches all 26 created pages) |
| G-012 | x/bond ClampGrowth(currentBps, growthBps uint32) uint32 MUST guard currentBps > CouponCapBps explicitly (return 0, or document the precondition and assert it) so the "post-growth coupon ≤ 800" invariant holds unconditionally. The spec's min(CouponCapBps - currentBps, growthBps) underflows when currentBps > cap (uint32 wrap → huge value → min picks growthBps → invariant violated). The P5-03-01 test MUST cover currentBps == cap (returns 0) and currentBps > cap (returns 0 or is rejected) as explicit cases. |
The spec'd helper has a uint32 underflow trap that breaks the stated invariant under misuse. The v0.2 Clamp has no such trap (it's a simple min/max); ClampGrowth adds the subtraction. A latent underflow in a locked-const firewall is a real defect. |
0.82 | P5-03-01 (ClampGrowth helper + invariant test) |
| G-013 | lexicon_meta_docs_test.go MUST include a walk-coverage assertion: a test that places a synthetic banned-term .md in a temp/fixture docs/ subtree (or uses an in-memory walk target) and asserts the walk FINDS it. The G-009 self-test table (which both firewalls share) verifies DETECTION (FindBannedTerm on synthetic strings) but NOT the WALK (which files are scanned). A walk bug (e.g., wrong path prefix, missing docs/ recursion) would report green on zero files scanned. The walk-coverage test closes this gap. |
The docs firewall's failure mode is "silently scans nothing and reports green" — undetectable by the self-test table alone. v0.3 has no docs today, so a broken walk passes trivially at P1 and would only surface when a real banned term slips into a real P2/P3 page AND the walk happens to miss that file. A walk-coverage test forces the walk to execute against a known-bad fixture. | 0.80 | P1-01-01 (lexicon_meta_docs_test.go) |
| G-014 | lexicon_meta_docs_test.go and lexicon_meta_test.go MUST derive the banned-term count from the single source lexicon.BannedTerms() (both already assert len(terms) == 10 from it — good; verified). The synthetic self-test table should ideally be shared via a lexicon package helper (e.g., lexicon.SyntheticBannedStrings() []string) rather than duplicated across the two meta-tests, so a future banned-term addition updates both firewalls from one place. If a shared helper is not added in v0.3, the two tables MUST be kept in sync by a comment cross-reference. |
D-043 chose a sibling test to preserve v0.2 coverage — defensible — but two copies of the self-test table drift silently. A shared helper is the durable fix; a cross-reference comment is the minimum. | 0.70 | P1-01-01 (lexicon_meta_docs_test.go), optionally lexicon/lexicon.go (shared helper) |
Escalations
None. All nine axes resolved at confidence ≥ 0.60 after the binding fixes G-011..G-014 are applied. No axis required escalation to the human. At full autonomy, the orchestrator applies the binding decisions and proceeds to P1.
Overall Verdict
SHIP Phase 0 with binding changes
The v0.3 Phase 0 plan is fundamentally sound and unusually well-grounded: the architecture and ordering claims were verified against the actual codebase (25 x/* modules, zero deps, G-003 import-invariant test exists and is green, bond consts/Clamp exist, bearers/partner extension points exist, baseline modules for all by-ID-string refs exist). The firewall-first ordering is clean (firewall passes vacuously with zero docs — no chicken-and-egg). The scope (Bearers + docs bundle) is defensible, not over-scoped. The 50-task / 6-phase plan is proportionate to the deliverable surface (26 docs pages + 7 x/* packages).
The binding changes are small correctness fixes, not scope rework:
- G-011 (mkdocs.yml nav completeness) — documentation/plan consistency.
- G-012 (ClampGrowth uint32 underflow guard) — the single real technical defect; a latent invariant-breaking trap in a locked-const firewall.
- G-013 (docs firewall walk-coverage test) — closes the "silently scans nothing" failure mode the G-009 self-test table does not cover.
- G-014 (shared synthetic-string helper / cross-reference) — maintainability of the two sibling firewalls.
None of these rise to "rethink" or "reduce scope" — the architecture, scope, ordering, and persona assignments are correct. Apply the 4 binding decisions and proceed to Phase P1.
Confidence in overall verdict: 0.80
Summary Block
Per-axis verdicts (v0.3):
1. Feasibility — PASS (0.82)
2. Scope — PASS (0.78) → strengthened by G-011
3. Cost — PASS (0.85)
4. Technical soundness — CONDITIONAL (0.72) → fixed by G-012
5. Risk — CONDITIONAL (0.74) → fixed by G-013
6. Dependency — PASS (0.80)
7. Testing — CONDITIONAL (0.70) → fixed by G-013 (walk-coverage)
8. Maintainability — CONDITIONAL (0.72) → fixed by G-014
9. Adversarial — PASS (0.78)
Binding decisions: 4 (G-011..G-014)
Escalations: 0
Overall: SHIP Phase 0 with binding changes (confidence 0.80)
Grill Review: OpenYield (oy) — v0.4 (Refinement — NFR) Phase 0
Reviewer: CIAgent adversarial grill (red-team, full autonomy) Date: 2026-08-17 Target: Phase 0 artifacts (PROJECT.md v0.4 section, REQUIREMENTS.md v0.4 table, ARCHITECTURE.md v0.4 section, PERSONAS.md v0.4, PLANS.md v0.4 plan, config.json) + v0.3 codebase baseline Milestone: v0.4 — Refinement (NFR) Autonomy: full (decision_confidence_threshold = 0.60) Mode: multi-project (slug
oy)
Methodology
Each of nine axes was scored against concrete evidence. The v0.4 scope is small (4 REQs, 12 tasks, 5 phases including P0+P4) so the grill is correspondingly focused. The central question: is v0.4 a legitimate NFR milestone or is it a scope-creep / busywork milestone that should be a single patch or deferred?
Evidence baseline (verified against the actual repo)
- v0.3 milestone COMPLETE: checkpoint
milestone_complete: true, releasev0.2.6(id 739), HEAD onmain. Verified. - v0.3 forward-references for v0.4 scope: REVIEW.md P2 (A-304 cross-const), AUDIT.md §193 (P1-1/P1-2 council divergences, P2 bearers no-op), GRILL.md G-014 (lexicon helper). Verified — all four REQ-029..REQ-032 map to a real v0.3 forward-reference.
- The two synthetic self-test tables are byte-identical duplicates:
lexicon_meta_test.go:93-104andlexicon_meta_docs/lexicon_meta_docs_test.go:157-168. Verified by diff — G-014 drift risk is REAL, not theoretical. x/hub/types/types.go:51,56definesLendingCouponCapBps = uint32(800)andLendingCouponFloorBps = uint32(0);x/bond/types/types.go:21,26definesCouponCapBps = 800andCouponFloorBps = 0. No automated cross-check exists (only a cross-doc comment). Verified — A-304 drift risk is REAL.SignalKindCount = 4is already a locked-const test (x/council/types/types_test.go:102). The AUDIT P1-2 rationale (Freeholder is eligibility, Guild is council tier, Capital is committed-capital) is in AUDIT.md but NOT in ARCHITECTURE.md. Verified — the documentation gap is REAL.- No
.github/workflows/and no.gitea/workflows/directory exists. Verified — REQ-032 is net-new CI, not a modification.
Forcing Questions and Verdicts
1. Is v0.4 a legitimate milestone, or should these fixes be a single v0.3.x patch? The four fixes are independent (lexicon helper, cross-const test, lifecycle docs, CI). Bundling them into a milestone with per-phase ship (P1..P3 each ship a patch) gives each fix its own release tag and audit trail. The alternative (one v0.3.1 patch with all four) loses the per-fix release boundary. D-052 phase ordering keeps each phase independently shippable. Verdict: legitimate milestone — the per-phase ship cadence (D-052) is the value, not the milestone label. Confidence 0.82.
2. Does REQ-031 violate the D-001 refinement-only filter by documenting a divergence that mentions Proposal/VoteOption (which are feat:-class types)?
No. DOCUMENTING a deferred feat: in ARCHITECTURE.md is docs, not feat:. REQ-031 adds NO enum types and changes NO locked consts. The regression-guard test asserts the CURRENT shape (4-signal) is intentional — it does not ADD a type. The D-001 filter rejects the feat: (adding Proposal/VoteOption) but accepts the docs (documenting why they are absent). Verdict: passes D-001. Confidence 0.85.
3. Does REQ-030's test-only import of x/bond/types into x/hub/types violate G-003?
No. G-003 forbids PRODUCTION cross-module struct imports. The import is in x/hub/types/cross_const_test.go (a _test.go file). The v0.2 GRILL G-003 explicitly documented the test-import exemption (and x/bearers/types/types_test.go:7 already imports x/processing/types as a test-only precedent). The plan's P1-02-01 verification includes a grep confirming no PRODUCTION .go file in x/hub/types/ imports x/bond/types. Verdict: G-003 intact. Confidence 0.88.
4. Does REQ-032 (CI workflow with mkdocs) violate G-006 (zero Go deps)?
No. The CI workflow installs mkdocs + mkdocs-material in a SEPARATE CI job (docs-build), not in go.mod. go.mod is not modified by the workflow (Python deps are isolated to the CI runner). The plan's P3-99-01 verification confirms go.mod diff is empty. Verdict: G-006 intact. Confidence 0.90.
5. Is the NFR purity gate enforceable?
Yes. The gate is git log --grep "^feat:" <milestone-range> returns zero. The v0.4 plan explicitly rejects feat:-class work (D-050 rejects Proposal/VoteOption; D-051 rejects Pages publish as a feature). The P4 audit (P4-02-01) runs the gate. The only risk: a commit MESSAGE accidentally using feat: prefix — the gate checks the prefix, not the content. Verdict: enforceable. Confidence 0.85.
Binding Decisions (G-015..G-016)
| ID | Binding Decision | Rationale | Confidence | Source |
|---|---|---|---|---|
| G-015 | The P1-02-01 cross-const test MUST also assert the absolute mission-locked values (LendingCouponCapBps == 800, LendingCouponFloorBps == 0, CouponCapBps == 800, CouponFloorBps == 0) in addition to the cross-package equality. The cross-package equality test alone would pass if BOTH consts drifted to the same wrong value (e.g., both 900). The absolute-value assertion catches a paired drift. |
The plan's P1-02-01 already includes TestConstsAreMissionLocked800And0 (good). This binding makes it MANDATORY: the test must assert BOTH the cross-equality AND the absolute 800/0 values. Without the absolute assertion, a paired drift (both consts change to the same wrong value) is undetected. |
0.85 | PLANS.md P1-02-01 |
| G-016 | The P3-01-01 CI workflow MUST run go test ./... BEFORE mkdocs build is allowed to proceed IF the jobs are serial, OR the go-test job must be a REQUIRED check (not advisory) if the jobs are parallel. The lexicon firewall (go test ./...) is the higher-priority check; a docs build that passes while the lexicon firewall fails is a false-green deploy. The plan's P3-01-01 runs the jobs in parallel (no dependency) — acceptable ONLY if both are required-status checks. If Gitea Actions does not support required-status on artifact-upload jobs, the jobs MUST be serial (go-test then docs-build depends-on go-test). |
A docs build that succeeds while the lexicon firewall fails would publish (or artifact) a docs site from a repo that has a lexicon violation — a false-green. The firewall must gate the docs build. | 0.78 | PLANS.md P3-01-01 |
Nine-Axis Scorecard (v0.4 Phase 0)
1. Scope — PASS (0.85) scoped to 4 v0.3 forward-refs; no scope creep
2. Feasibility — PASS (0.88) all 4 fixes are mechanical; no research risk
3. Specification — PASS (0.82) REQs clear; D-047..D-053 unambiguous
4. Decomposition — PASS (0.80) P1..P3 independent vertical slices; P4 review/ship
5. Risk — PASS (0.85) NFR scope (no behavioral change); low risk
6. Dependency — PASS (0.82) no cross-phase hard blockers; G-003 test-exempt documented
7. Testing — PASS (0.85) each fix has a verification task; NFR purity gate in P4
8. Maintainability — CONDITIONAL (0.78) → fixed by G-015 (absolute-value assertion)
9. Adversarial — PASS (0.82) D-001 filter enforced; no feat: creep
Binding decisions: 2 (G-015, G-016)
Escalations: 0
Overall: SHIP Phase 0 with binding changes (confidence 0.84)
v0.4 Grill Verdict
SHIP Phase 0 with G-015 (absolute-value const assertion in P1-02-01) and G-016 (firewall-gates-docs-build in P3-01-01) applied. The v0.4 NFR milestone is a legitimate, well-scoped refinement cycle that closes three real v0.3 forward-references (G-014, A-304, AUDIT §193) and lands the deferred docs CI (D-046). The D-001 refinement-only filter is enforced throughout; the NFR purity gate in P4 is enforceable. No escalations.