Compare commits

..

24 Commits

Author SHA1 Message Date
cloudinit-bot 21926e8adb docs(milestone): complete OpenYield v0.4 (Refinement — NFR)
docs-build / go test ./... (lexicon firewall + all x/* tests) (push) Has been cancelled
docs-build / mkdocs build (docs site artifact) (push) Has been cancelled
Milestone v0.4 complete. NFR type, tags v0.3.x. Four NFR REQs shipped:
- REQ-029 lexicon.SyntheticBannedStrings() shared helper (G-014 closed)
- REQ-030 x/hub↔x/bond cross-const equality test (A-304 closed, G-015)
- REQ-031 council lifecycle divergence docs + regression guard (AUDIT §193)
- REQ-032 .gitea/workflows/docs-build.yml docs build CI (D-046 landed)

NFR purity gate GREEN: zero feat: commit subjects in the milestone.
go.mod unchanged (G-006). G-003 production firewall intact. Coverage:
x/hub 93.3%, x/council 96.4% (both above 80% target).

Phases: P0 -> v0.3.0, P1 -> v0.3.1, P2 -> v0.3.2, P3 -> v0.3.3,
P4 -> v0.3.4 (milestone release).

Requirements covered: REQ-029, REQ-030, REQ-031, REQ-032 (all complete).
Live-runtime promotions of v0.3 Bearers skeletons deferred to v0.5+
(feat:-class, rejected by D-001 refinement-only filter).

---ci---
project: oy
phase: 4
milestone: v0.4
status: complete
tag_base: v0.3.x
milestone_type: nfr
phase_role: final
milestone_complete: true
requirements:
  covered: [REQ-029, REQ-030, REQ-031, REQ-032]
  partial: []
---/ci---
2026-08-17 23:35:48 +00:00
cloudinit-bot d74515cd1d docs(P4): v0.4 final review + audit — PASS, NFR purity GREEN
REVIEW.md v0.4 section: 7 adversarial probes PASS; 0 P0; 3 P2 nits
(mkdocs link warnings, upload-artifact v4 compat, NFR gate precision).
Confidence 0.90.

AUDIT.md v0.4 section: reconstruction test PASS (phase progression,
tags v0.3.0..v0.3.3, .ciagent evidence); file/branch/commit discipline
PASS; build/test/coverage sanity PASS (x/hub 93.3%, x/council 96.4%).
NFR purity gate GREEN (zero feat: commit subjects). 0 critical issues.
Confidence 0.90.

---ci---
project: oy
phase: 4
milestone: v0.4
status: review
tag_base: v0.3.x
milestone_type: nfr
phase_role: final
---/ci---
2026-08-17 23:34:52 +00:00
cloudinit-bot a36561337a checkpoint(p3): v0.4 phase 3 complete → v0.3.3
docs-build / go test ./... (lexicon firewall + all x/* tests) (push) Has been cancelled
docs-build / mkdocs build (docs site artifact) (push) Has been cancelled
P3 shipped: tag v0.3.3, release id 751. REQ-032 covered. Branch
oy/phase/03 deleted. All execution phases complete. Next: P4 final
review + audit + milestone ship.

---ci---
project: oy
phase: 3
milestone: v0.4
status: complete
tag_base: v0.3.x
milestone_type: nfr
phase_release_tag: v0.3.3
---/ci---
2026-08-17 23:33:58 +00:00
cloudinit-bot 7fa5628dc2 Merge phase/03 into milestone/v0.4-refinement (P3 complete → v0.3.3)
docs-build / go test ./... (lexicon firewall + all x/* tests) (push) Has been cancelled
docs-build / mkdocs build (docs site artifact) (push) Has been cancelled
---ci---
project: oy
phase: 3
milestone: v0.4
status: complete
tag_base: v0.3.x
milestone_type: nfr
---/ci---
2026-08-17 23:33:39 +00:00
cloudinit-bot 3b7883c092 verify(P3): docs build CI — 4 layers green
docs-build / go test ./... (lexicon firewall + all x/* tests) (push) Has been cancelled
docs-build / mkdocs build (docs site artifact) (push) Has been cancelled
Structural: YAML schema valid; jobs go-test + docs-build; docs-build
needs go-test (G-016 OK). Behavioral: local mkdocs build succeeds (site/
produced); go test ./... green. Security: go.mod unchanged (G-006 intact);
Python deps isolated to docs-build job. Quality: .gitignore covers site/.
NFR purity: zero commit SUBJECTS starting with feat: (grep -E '^feat:'
exit 1 on subject-only check). NOTE: git log --grep matches message BODIES
too (prose mentions 'feat:'); the P4 audit gate uses subject-only check
(git log --format='%s' | grep -E '^feat:').

---ci---
project: oy
phase: 3
milestone: v0.4
status: verify
tag_base: v0.3.x
milestone_type: nfr
reqs: [REQ-032]
---/ci---
2026-08-17 23:33:36 +00:00
cloudinit-bot 1969b96d3d chore(ci): docs build CI workflow (REQ-032, D-046, D-051, G-016)
Add .gitea/workflows/docs-build.yml: on push, go-test job runs go test ./...
(lexicon firewall + all x/* tests), then docs-build job (needs: go-test per
G-016 firewall-gates-docs-build) installs mkdocs + mkdocs-material, runs
mkdocs build, uploads site/ as a CI artifact. go.mod unchanged (Python deps
isolated to the docs-build job; G-006 intact). Full Gitea Pages publishing
deferred per D-051 (no hosting target configured).

.gitignore: add site/ (mkdocs build output; never committed).

Verification: YAML parses; docs-build needs go-test; go test ./... green;
go.mod unchanged; local mkdocs build succeeds (site/ produced).

---ci---
project: oy
phase: 3
milestone: v0.4
status: execute
tag_base: v0.3.x
milestone_type: nfr
reqs: [REQ-032]
---/ci---
2026-08-17 23:33:02 +00:00
cloudinit-bot d149916288 checkpoint(p2): v0.4 phase 2 complete → v0.3.2
P2 shipped: tag v0.3.2, release id 750. REQ-031 covered. Branch
oy/phase/02 deleted. Next: P3 docs build CI.

---ci---
project: oy
phase: 2
milestone: v0.4
status: complete
tag_base: v0.3.x
milestone_type: nfr
phase_release_tag: v0.3.2
---/ci---
2026-08-17 23:32:22 +00:00
cloudinit-bot c4cbd59c11 Merge phase/02 into milestone/v0.4-refinement (P2 complete → v0.3.2)
---ci---
project: oy
phase: 2
milestone: v0.4
status: complete
tag_base: v0.3.x
milestone_type: nfr
---/ci---
2026-08-17 23:32:07 +00:00
cloudinit-bot a6d33a58b3 verify(P2): lifecycle divergence docs + regression guard — 4 layers green
Structural: go vet clean. Behavioral: TestSignalKindShapeIntentional PASS;
existing council tests PASS. Security: no locked-const change
(SignalKindCount==4 preserved). Quality: x/council/types coverage 96.4%
(improved from v0.3; new intent test adds coverage). NFR purity: zero
feat: commits in P2 (only docs + test).

---ci---
project: oy
phase: 2
milestone: v0.4
status: verify
tag_base: v0.3.x
milestone_type: nfr
reqs: [REQ-031]
---/ci---
2026-08-17 23:32:02 +00:00
cloudinit-bot 7d1468b442 docs(arch): council lifecycle divergence decisions (REQ-031, AUDIT §193)
Add ARCHITECTURE.md §"Council Voice/Council Interface — Lifecycle Type
Divergence Decisions (v0.4)": documents P1-1 (Proposal/VoteOption absent,
deferred to v0.5+ governance runtime — feat: rejected by D-001), P1-2
(SignalKind 4-vs-5: the 4-source shape is intentional per AUDIT rationale;
Freeholder is eligibility, Guild is council tier, Capital is committed-
capital per vision §9.1), P2 (bearers ValidateGenesis no-op correct per spec).

test(council): TestSignalKindShapeIntentional regression guard (REQ-031)

Add intent-assertion test locking the 4-source SignalKind shape with the
AUDIT §193 P1-2 rationale in the doc comment. A future agent changing
SignalKindCount 4→5 must update this test, surfacing the rationale. No
locked-const change, no production .go files modified (D-050).

Verification: go test ./... green; only types_test.go modified in x/**.

---ci---
project: oy
phase: 2
milestone: v0.4
status: execute
tag_base: v0.3.x
milestone_type: nfr
reqs: [REQ-031]
---/ci---
2026-08-17 23:31:55 +00:00
cloudinit-bot 92966cb9c5 checkpoint(p1): v0.4 phase 1 complete → v0.3.1
P1 shipped: tag v0.3.1, release id 749. REQ-029, REQ-030 covered.
Branch oy/phase/01 deleted. Next: P2 lifecycle divergence docs+guard.

---ci---
project: oy
phase: 1
milestone: v0.4
status: complete
tag_base: v0.3.x
milestone_type: nfr
phase_release_tag: v0.3.1
---/ci---
2026-08-17 23:31:16 +00:00
cloudinit-bot 838bd06a9d Merge phase/01 into milestone/v0.4-refinement (P1 complete → v0.3.1)
---ci---
project: oy
phase: 1
milestone: v0.4
status: complete
tag_base: v0.3.x
milestone_type: nfr
---/ci---
2026-08-17 23:30:50 +00:00
cloudinit-bot 57c7dc5ff5 verify(P1): lexicon+const hardening — 4 layers green
Structural: go build ./... + go vet clean. Behavioral: both meta-tests
PASS consuming SyntheticBannedStrings(); 3 new cross-const tests PASS.
Security: lexicon firewalls green (x/ + docs/); G-003 production firewall
intact (no production import of x/bond/types in x/hub/types). Quality:
x/hub/types coverage 93.3% (v0.3 floor preserved; new test adds coverage).

---ci---
project: oy
phase: 1
milestone: v0.4
status: verify
tag_base: v0.3.x
milestone_type: nfr
reqs: [REQ-029, REQ-030]
---/ci---
2026-08-17 23:30:46 +00:00
cloudinit-bot 2ca0e1aa4b refactor(lexicon): shared SyntheticBannedStrings() helper (REQ-029, G-014)
Add lexicon.SyntheticBannedStrings() — single source for the synthetic
self-test table consumed by BOTH meta-tests. Refactor
TestLexiconMetaSelfTestTable (lexicon_meta) and TestLexiconMetaDocsSelfTestTable
(lexicon_meta_docs) to consume the helper; remove the byte-identical
duplicated 10-string table from both. Closes the G-014 drift risk.

test(hub): cross-package const-equality test (REQ-030, A-304, G-015)

Add x/hub/types/cross_const_test.go: test-only import of x/bond/types
(G-003 test-exempt). TestLendingCouponCapMatchesBondCap + TestLendingCoupon-
FloorMatchesBondFloor assert cross-equality; TestConstsAreMissionLocked800And0
(G-015) asserts absolute 800/0 values — catches paired drift. Closes A-304.

Verification: go test ./... green; old synthetic table gone (grep 0);
go.mod unchanged; G-003 production firewall intact.

---ci---
project: oy
phase: 1
milestone: v0.4
status: execute
tag_base: v0.3.x
milestone_type: nfr
reqs: [REQ-029, REQ-030]
---/ci---
2026-08-17 23:30:30 +00:00
cloudinit-bot cc0940d9f8 checkpoint(p0): v0.4 phase 0 complete → v0.3.0
Phase 0 shipped: tag v0.3.0, release id 748. Branch oy/phase/00 deleted.
Next: P1 lexicon+const hardening (oy/phase/01-lexicon-const-hardening).

---ci---
project: oy
phase: 0
milestone: v0.4
status: complete
tag_base: v0.3.x
milestone_type: nfr
phase_release_tag: v0.3.0
release_id: 748
---/ci---
2026-08-17 23:29:20 +00:00
cloudinit-bot 0bb14bd1a1 Merge phase/00 into milestone/v0.4-refinement (P0 complete → v0.3.0)
Phase 0 (pre-execution) complete. Tags v0.3.0. Next: P1 lexicon+const
hardening (oy/phase/01-lexicon-const-hardening).

---ci---
project: oy
phase: 0
milestone: v0.4
status: complete
tag_base: v0.3.x
milestone_type: nfr
---/ci---
2026-08-17 23:28:49 +00:00
cloudinit-bot b55255614f decision(P00): mvp/ux gate — auto-generated
Auto-generated 3 MVP/UX sections (full autonomy): User-Facing Surface
(4 surfaces: lexicon firewall, cross-const test, docs CI, ARCHITECTURE.md
section), Happy Path (2 scenarios: add banned term / const drift), UX
Acceptance Criteria (6 explicit criteria incl NFR purity gate). Gate
passes — all three sections present.

---ci---
project: oy
phase: 0
milestone: v0.4
status: mvp_ux_check
tag_base: v0.3.x
milestone_type: nfr
---/ci---
2026-08-17 23:28:34 +00:00
cloudinit-bot d88d2eaeb8 docs(P00): grill — v0.4 NFR adversarial review (G-015, G-016)
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---
2026-08-17 23:28:11 +00:00
cloudinit-bot 5e06b14ddf docs(P00): create phase plans — v0.4 NFR (P1..P4, 12 tasks)
P1 lexicon+const hardening (REQ-029/030). P2 lifecycle divergence docs+
guard (REQ-031). P3 docs build CI (REQ-032). P4 final review+audit+ship.
D-001 refinement-only filter governs all tasks. NFR purity gate in P4.
Tags v0.3.x. No cross-phase hard blockers (independent vertical slices).

---ci---
project: oy
phase: 0
milestone: v0.4
status: plan
tag_base: v0.3.x
milestone_type: nfr
---/ci---
2026-08-17 23:27:33 +00:00
cloudinit-bot 28d73c8b2c docs(P00): research findings — v0.4 NFR research R-029..R-032 + PERSONAS.md
R-029 lexicon shared helper (G-014 drift confirmed: byte-identical synthetic
tables in both meta-tests). R-030 cross-const test (hub/bond 800/0 LOCAL
consts, G-003 test-exempt import). R-031 lifecycle divergence (AUDIT §193
P1-1/P1-2 are feat:-class, rejected by D-001; v0.4 = document + regression
guard only). R-032 docs CI (.gitea/workflows, build+artifact, no Pages
publish). PERSONAS.md: backend-engineer + lead-developer only; v0.3
frontend/docs-writer deactivated.

---ci---
project: oy
phase: 0
milestone: v0.4
status: research
tag_base: v0.3.x
milestone_type: nfr
---/ci---
2026-08-17 23:26:33 +00:00
cloudinit-bot 46c2c4ef6c docs(P00): clarify — v0.4 NFR scope decisions D-047..D-053
Auto-decided defaults (autonomy=full). v0.4 = refinement-only NFR.
D-047 NFR type, tags v0.3.x. D-048 lexicon shared helper (G-014).
D-049 cross-const test (A-304). D-050 REQ-031 = document only, no feat:
enum additions (D-001 filter). D-051 docs CI = .gitea/workflows build+artifact
(no Pages publish). D-052 phase ordering. D-053 no IDEATE (no flag this run).

---ci---
project: oy
phase: 0
milestone: v0.4
status: clarify
tag_base: v0.3.x
milestone_type: nfr
---/ci---
2026-08-17 23:25:20 +00:00
cloudinit-bot 0cac4b0b32 docs(init): validate specification
Establish v0.4 refinement-only NFR milestone. Tags run on v0.3.x line.
Scope sourced from v0.3 forward-references (REVIEW P2/A-304, AUDIT §193,
GRILL G-014, D-046): REQ-029 lexicon shared helper, REQ-030 cross-const
test, REQ-031 lifecycle type review, REQ-032 docs build CI. Zero feat:
phases by construction.

---ci---
project: oy
phase: 0
milestone: v0.4
status: specify
tag_base: v0.3.x
milestone_type: nfr
---/ci---
2026-08-17 23:24:46 +00:00
cloudinit-bot 97a25dd0b6 checkpoint(milestone): v0.3 complete — clear for next milestone 2026-08-17 22:36:04 +00:00
cloudinit-bot 82ae6cf5a2 docs(milestone): complete OpenYield v0.3 (Bearers & Documentation)
Milestone v0.3 complete. Feature type, tags v0.2.x. Two work-streams
shipped under one feature milestone:

(A) Bearers skeleton + tests (D-020 pattern, 7 x/* packages, zero ext deps):
- x/bridge (NEW): BridgeStatus enum (4), BridgeRoute by-ID-string refs
- x/exit (NEW): ExitStatus enum (5), ExitRoute, DEXSwap (opaque venue)
- x/bearers (EXT): OYSATLink surveillance-resistant LOCKED, OYQRCode idempotent
- x/partner (EXT): AnchorCredential (custody-provider-id empty in skeleton)
- x/hub (NEW): HubService enum (3), LendingCouponCapBps=800 LOCAL const (A-304)
- x/services (NEW): ServiceKind enum (4), window-id by-ID-string ref (A-307)
- x/bond (EXT): GrowthBond, ClampGrowth G-012 underflow guard, secondary market
All packages >=93.3% coverage. Both lexicon firewalls green. G-003 intact.

(B) Documentation deliverable (REQ-027 complete, 26-page MkDocs Material site):
- README.md + mkdocs.yml + docs/index.md
- docs/shared/ (7 pages): Six Principles, Bread Scale, Storage Pools, Watchers/Mirror, Lexicon, Vision
- docs/nomads/ (8 pages): Reach, Stash, Bearers, Maps-Pay, Pacts, Standing, Window
- docs/freeholders/ (8 pages): Signals, Standing, Stands-Guilds, Councils-Voice, Bonds, Partner Spectrum, Anchor Preview
- docs/reference/ (2 pages): Architecture, Components
- REQ-028: lexicon firewall extended to docs/ + README.md (lexicon_meta_docs_test.go, 5 tests incl G-013 walk-coverage + G-014 shared self-test)

Phases: P0 -> v0.2.0, P1 -> v0.2.1, P2 -> v0.2.2, P3 -> v0.2.3, P4 -> v0.2.4, P5 -> v0.2.5, P6 -> v0.2.6 (milestone release).

Requirements covered: REQ-010, REQ-022, REQ-023, REQ-024, REQ-025, REQ-026 (skeleton), REQ-027, REQ-028 (complete).
IDEATE-01..08 ratified and delivered.

---ci---
project: oy
phase: 6
milestone: v0.3
status: complete
tag_base: v0.2.x
phase_role: final
milestone_complete: true
requirements:
  covered: [REQ-010, REQ-022, REQ-023, REQ-024, REQ-025, REQ-026, REQ-027, REQ-028]
  partial: []
---/ci---
2026-08-17 22:35:36 +00:00
18 changed files with 1220 additions and 173 deletions
+8 -8
View File
@@ -1,14 +1,14 @@
{
"phase": 4,
"phase": 3,
"stage": "complete",
"milestone": "v0.3",
"milestone_type": "feature",
"tag_base": "v0.2.x",
"milestone": "v0.4",
"milestone_type": "nfr",
"tag_base": "v0.3.x",
"phase_role": "execution",
"project": "oy",
"attempts": 0,
"updated_at": "2026-08-18T00:35:00Z",
"milestone_complete": false,
"phase_release_tag": "v0.2.4",
"release_id": 737
"updated_at": "2026-08-17T23:45:00Z",
"phase_release_tag": "v0.3.3",
"release_id": 751,
"reqs_covered": ["REQ-029", "REQ-030", "REQ-031", "REQ-032"]
}
+3 -3
View File
@@ -6,9 +6,9 @@
}
],
"active_project": "oy",
"milestone": "v0.3",
"milestone_type": "feature",
"tag_base": "v0.2.x",
"milestone": "v0.4",
"milestone_type": "nfr",
"tag_base": "v0.3.x",
"autonomy": {
"level": "full",
"escalation_hooks": ["deploy", "delete_data", "merge_to_main"],
+76
View File
@@ -184,3 +184,79 @@ module's production `.go` files).
> are NOT user-facing docs and are explicitly excluded from the docs firewall
> scan. This mirrors how `lexicon_meta_test.go` excludes itself: the firewall's
> own code is allowed to name the terms it bans.
## v0.4 Architecture (Refinement — NFR)
v0.4 is a refinement-only NFR milestone (D-047): zero `feat:` phases, zero new
production types, zero behavioral changes. It lands durability fixes sourced
from v0.3 forward-references. Tags run on the `v0.3.x` patch line.
### v0.4 Research Findings
**R-029 — Lexicon firewall shared helper (REQ-029, GRILL G-014).**
Verified during v0.4 RESEARCH: `lexicon_meta_test.go` (`TestLexiconMetaSelfTestTable`, lines 83-118) and `lexicon_meta_docs/lexicon_meta_docs_test.go` (`TestLexiconMetaDocsSelfTestTable`, lines 147-182) contain byte-identical duplicate synthetic self-test tables — both build the same 10-string slice by indexing `lexicon.BannedTerms()`. This is exactly the G-014 drift risk: if a future banned-term addition updates one table and not the other, the docs firewall silently loses coverage. The fix is a new `lexicon.SyntheticBannedStrings() []string` helper in `lexicon/lexicon.go` that returns the 10 synthetic strings; both meta-tests consume it instead of building their own copy. The helper's source uses `lexicon.BannedTerms()` (already fragment-assembled) so the lexicon package's own source stays lexicon-clean. Both meta-tests already assert `len(terms) == 10` from `lexicon.BannedTerms()` (the G-014 minimum); the helper closes the drift fully. No behavioral change to detection (`FindBannedTerm` unchanged); refactor + test only.
**R-030 — Cross-package const-equality test (REQ-030, REVIEW P2 / A-304).**
Verified during v0.4 RESEARCH: `x/hub/types/types.go:51,56` defines LOCAL consts `LendingCouponCapBps = uint32(800)` and `LendingCouponFloorBps = uint32(0)`, cross-documented (comment lines 46-55) to `x/bond/types/types.go:21,26` consts `CouponCapBps = 800` and `CouponFloorBps = 0` (D-028 mission-locked). The cross-doc comment flags drift for human review but no automated check exists. The fix is a new test file `x/hub/types/cross_const_test.go` (package `types`) that imports `github.com/oy/openyield/x/bond/types` (test-only, G-003 exempt per the test-import exemption documented in v0.2 GRILL G-003) and asserts `hub.LendingCouponCapBps == bond.CouponCapBps` and `hub.LendingCouponFloorBps == bond.CouponFloorBps`. The test fails closed if either const drifts. No production import is added (G-003 production firewall intact); test-only import only.
**R-031 — Lifecycle type shape-divergence review (REQ-031, AUDIT §193).**
Verified during v0.4 RESEARCH: AUDIT §193 flags two P1 council divergences and one P2 bearers nit:
- **P1-1**: `x/council/types` lacks `Proposal`/`ProposalStatus`/`VoteOption` enums (AUDIT says add "in v0.3 when wiring the council keeper to a live governance runtime"). Adding these is a `feat:`-class addition (new enum types) → REJECTED by D-001 filter for v0.4. Deferred to v0.5+ governance runtime.
- **P1-2**: `SignalKind` has 4 sources (Stash/Standing/Vouch/Capital) vs spec's `VoiceSource` 5 sources (Stash/Standing/Vouch/Freeholder/Guild). AUDIT code rationale: Freeholder is an eligibility property (upstream in `x/standing`), Guild is a council tier, Capital is committed-capital (vision §9.1) — defensible refinement. Changing `SignalKindCount` 4→5 is a locked-const change → REJECTED by D-001 filter for v0.4.
- **P2**: `x/bearers/types` `ValidateGenesis` no-op is CORRECT per spec (AUDIT explicitly notes "no action").
v0.4 REQ-031 scope (D-050): DOCUMENT the divergence decisions in this ARCHITECTURE.md section + add a regression-guard test asserting the current `SignalKindCount==4` shape is intentional (an intent-assertion test, not a shape change). No enum additions, no locked-const changes. The existing `TestSignalKindCountLockedConst` in `x/council/types/types_test.go:102` already asserts the count; REQ-031 adds an intent comment + a test documenting WHY the shape is 4-not-5 (the AUDIT rationale), so a future agent does not "fix" the divergence by silently changing the locked const.
**R-032 — Docs build CI (REQ-032, D-046).**
Verified during v0.4 RESEARCH: no `.github/workflows/` directory exists; Gitea Actions uses `.gitea/workflows/`. `mkdocs.yml` is present at repo root (buildable locally via `mkdocs build`). v0.4 REQ-032 ships a `.gitea/workflows/docs-build.yml` workflow that: (1) runs `go test ./...` (the lexicon firewall + all x/* tests) on push; (2) installs mkdocs + mkdocs-material (build-only Python deps in a separate job/step — does NOT touch `go.mod`, G-006 intact); (3) runs `mkdocs build` to produce `site/`; (4) uploads `site/` as a CI artifact. Full Gitea Pages publishing is DEFERRED (no hosting target configured in v0.4 per D-051). The workflow file is `chore` (CI config), not `feat:` — passes the D-001 filter. The workflow runs on every push to any branch (not just main) so the lexicon firewall + docs build are checked on every change.
### v0.4 Component Map (no new modules)
v0.4 touches NO new `x/*` modules. The touched files are:
- `lexicon/lexicon.go` (add `SyntheticBannedStrings()`) — REQ-029
- `lexicon_meta_test.go` (refactor to consume helper) — REQ-029
- `lexicon_meta_docs/lexicon_meta_docs_test.go` (refactor to consume helper) — REQ-029
- `x/hub/types/cross_const_test.go` (NEW test file) — REQ-030
- `x/council/types/types_test.go` (add intent-assertion test + comment) — REQ-031
- `.ciagent/oy/ARCHITECTURE.md` (this section) — REQ-031
- `.gitea/workflows/docs-build.yml` (NEW CI workflow) — REQ-032
### v0.4 Interface Contracts (unchanged from v0.3)
v0.4 does not change any cross-component interface. The 6 cross-component interfaces (Standing, Forge/Fold, Mirror, Window, Fee Covenant, Voice/Council) are unchanged. REQ-031 documents a divergence in the Voice/Council interface surface (SignalKind shape) but does not change it.
### Council Voice/Council Interface — Lifecycle Type Divergence Decisions (v0.4, REQ-031)
This section documents the lifecycle type shape-divergences flagged by AUDIT.md §193 for the Council/Voice interface surface. v0.4 is a refinement-only NFR milestone (D-047): the D-001 filter REJECTS `feat:`-class enum additions and locked-const shape changes, so these divergences are DOCUMENTED here, not fixed in code. A regression-guard test (`TestSignalKindShapeIntentional` in `x/council/types/types_test.go`) locks the current shape so a future agent does not silently "fix" a divergence by changing a locked const.
**Divergence P1-1 (AUDIT §193): `Proposal`/`ProposalStatus`/`VoteOption` enums absent from `x/council/types`.**
- **Spec source**: P3-01-01 deliverable recommended `Proposal`, `ProposalStatus` (5 states), `VoteOption` (3 options) enums mirroring OZ Governor / `x/gov`.
- **Implemented**: `Council`, `CouncilMember`, `Voice`, `SignalKind`, `TallyResult` — no `Proposal`/`ProposalStatus`/`VoteOption` lifecycle types.
- **Must-have impact**: NONE. The v0.2 P3 must-haves (3 councils, Mission Lock, `TallyResult` x/gov shape, no veto) are all met without the Proposal lifecycle.
- **Decision (v0.4, D-050)**: ADDING `Proposal`/`ProposalStatus`/`VoteOption` is a `feat:`-class addition (new enum types). REJECTED by the D-001 refinement-only filter. **Deferred to v0.5+** when the council keeper is wired to a live governance runtime (the AUDIT's own recommendation: "add in v0.3 when wiring the council keeper to a live governance runtime"). The skeleton council keeper in v0.2 does not consume a Proposal lifecycle; adding the types without the runtime would be dead code.
- **Severity (AUDIT)**: P1 (spec drift from deliverable text, not a must-have, not blocking).
- **v0.4 action**: DOCUMENT only (this section). No code change.
**Divergence P1-2 (AUDIT §193): `SignalKind` 4 sources vs spec `VoiceSource` 5 sources.**
- **Spec source**: P3-01-01 deliverable specified `VoiceSource` with 5 sources (Stash/Standing/Vouch/Freeholder/Guild).
- **Implemented**: `SignalKind` with 4 sources: `SignalStash`, `SignalStanding`, `SignalVouch`, `SignalCapital` (`SignalKindCount = 4`, locked const).
- **Code rationale (AUDIT §193 P1-2)**: the 4-source shape is a defensible design refinement, not a defect:
- `Freeholder` is an ELIGIBILITY property (upstream in `x/standing`), not a voice signal. A Freeholder-eligible Reach is a precondition for voting, not a signal that feeds a vote's weight.
- `Guild` is a COUNCIL TIER (one of the three councils is the Guild Council), not a voice signal. Including Guild as a signal kind would conflate the council tier with the signal source.
- `Capital` is committed-capital (vision §9.1, one of the four Freeholder signals), which the spec's `VoiceSource` list omitted. Adding `Capital` corrects the spec list to match vision §9.1's four-signal definition (REQ-005: "Four Freeholder signals locked").
- **Must-have impact**: NONE. The v0.2 P3 must-haves did not enumerate `VoiceSource` coverage; the 4-signal shape matches REQ-005's "Four Freeholder signals locked" exactly.
- **Decision (v0.4, D-050)**: changing `SignalKindCount` 4→5 (to restore the spec's 5-source `VoiceSource`) is a LOCKED-CONST CHANGE. REJECTED by the D-001 refinement-only filter (changing a locked const is a behavioral change, not a refinement). The 4-source shape is the CORRECT shape per vision §9.1 and REQ-005; the spec deliverable text was wrong, not the implementation.
- **Severity (AUDIT)**: P1 (design-choice divergence, tested and self-consistent, not blocking).
- **v0.4 action**: DOCUMENT the rationale here + add `TestSignalKindShapeIntentional` (regression guard) so a future agent changing `SignalKindCount` from 4 to 5 must also update the intent-assertion test, surfacing the AUDIT rationale for review. No locked-const change.
**Divergence P2 (AUDIT §193): `x/bearers/types` `ValidateGenesis` no-op.**
- **Spec source**: P4-02-01 said "DefaultParams/GenesisState unchanged" (bearers is an EXTENSION in v0.2, not a new module; the A-212 `ValidateGenesis` upgrade was scoped to NEW modules only).
- **Implemented**: `ValidateGenesis` remains a no-op (`x/bearers/types/types.go:108` returns `nil` unconditionally).
- **Decision (v0.4)**: CORRECT per spec — no action (AUDIT explicitly notes "no action"). The A-212 upgrade applies to NEW modules (v0.2's `x/window`, `x/stand`, etc.), not to EXTENDED modules like `x/bearers`. Listed here for completeness; no code change, no test change.
+213
View File
@@ -300,3 +300,216 @@ Overall verdict: PASS (after critical fixes)
Confidence: 0.90
AUDIT.md written: /root/oy/.ciagent/oy/AUDIT.md ✓
```
---
# Audit: OpenYield (oy) — v0.3 (Bearers & Documentation) Final Phase (P6)
> **Auditor**: CIAgent doc verifier (final-audit mode, full autonomy)
> **Date**: 2026-08-17
> **Scope**: v0.3 milestone state on `oy/milestone/v0.3-bearers-docs` (HEAD = `oy/phase/06-final-review-ship`)
> **Milestone**: v0.3 — Bearers & Documentation (feature type; tag_base `v0.2.x`)
> **Mode**: multi-project (slug `oy`)
> **Autonomy**: full
---
## v0.3 Final Audit (P6)
### Reconstruction Test — **PASS**
**Git log matches `.ciagent/` files.** `git log v0.1.5..HEAD --oneline` returns 13 commits across P0-P5 (6 phase-ship `docs(P##):` commits, 5 `checkpoint(P##):` advance commits, 1 v0.2 milestone marker inherited via v0.1.5). The 6 `docs(P##): complete ...` commits each carry a `---ci---` block with `status: complete` and the correct phase integer.
**Per-phase `---ci---` block verification:**
| Phase | Commit | Tag | `status` | `requirements.covered` | `requirements.partial` | Verdict |
|---|---|---|---|---|---|---|
| P0 | 23de3c5 | v0.2.0 | complete | [] (pre-execution) | [] | PASS |
| P1 | a780884 | v0.2.1 | complete | [REQ-028] | [REQ-027] | PASS |
| P2 | d09c613 | v0.2.2 | complete | [] (nomads docs) | [REQ-027] | PASS |
| P3 | 2ef3f2e | v0.2.3 | complete | [REQ-027] | [] | PASS |
| P4 | ab43bef | v0.2.4 | complete | [REQ-010, REQ-022, REQ-023] | [] | PASS |
| P5 | c1aa274 | v0.2.5 | complete | [REQ-024, REQ-025, REQ-026] | [] | PASS |
- Phase `---ci---` blocks: 6 (one per phase P0-P5). Each phase's final block shows `status: complete`. ✓
- Tag count: 6 (v0.2.0..v0.2.5). Each tag exists and points at the matching `docs(P##): complete ...` commit. ✓
- REQ coverage vs. expected (P0: none; P1: REQ-028; P2: partial REQ-027; P3: REQ-027; P4: REQ-010/022/023; P5: REQ-024/025/026): **exact match on all 6 phases**. ✓
- IDEATE traceability (REQUIREMENTS.md §"IDEATE Traceability"): 8 IDEATE-NN → REQ-ID mappings present (IDEATE-01→REQ-027 ... IDEATE-08→REQ-026). ✓
- CHECKPOINT.json matches state: `phase: 5`, `stage: complete`, `milestone: v0.3`, `tag_base: v0.2.x`, `milestone_complete: false`, `phase_release_tag: v0.2.5` — consistent with "P5 complete, advancing to P6 final review/audit/ship". ✓
**Reconstruction test verdict: PASS** (6/6 phase blocks well-formed; 6/6 tags present; 6/6 REQ-coverage sets match; CHECKPOINT current).
### File Discipline — **PASS** (after fix)
`.ciagent/oy/` contains: PROJECT.md, ROADMAP.md, REQUIREMENTS.md, ARCHITECTURE.md, RESEARCH.md, PERSONAS.md, PLANS.md, GRILL.md, REVIEW.md, AUDIT.md. Plus historical P1_SHIP_VERIFICATION.md..P4_SHIP_VERIFICATION.md (v0.2 audit artifacts; not orphan — referenced by v0.2 AUDIT.md).
`.ciagent/` (root, multi-project) contains: CHECKPOINT.json, config.json, oy/ (slug subdir). ✓ config.json valid (`projects[]` length 1, `active_project: oy`, `milestone: v0.3`, `tag_base: v0.2.x`, `autonomy.level: full`). ✓
**Stale-content fixes applied during this audit:**
- REQUIREMENTS.md v0.3 table: all 8 REQs were marked `Pending` despite P1-P5 shipping them. Updated REQ-010/022/023/024/025/026 → `Skeleton`, REQ-027/028 → `Complete` to match the `---ci---` coverage blocks.
- ROADMAP.md v0.3 milestone: header read `ACTIVE` with no per-phase completion markers; P0-P5 shipped. Added `[x]` markers for P0-P5 and `[ ]` for P6-in-progress, plus a status line.
No orphan files detected. REVIEW.md exists (v0.2 content; review agent may append v0.3 section concurrently — tracked as pending, non-blocking).
**File discipline verdict: PASS** (after REQUIREMENTS + ROADMAP freshness fixes).
### Branch Hygiene — **PASS**
- `git branch -a` lists: `main`, `oy/milestone/v0.3-bearers-docs`, `oy/phase/06-final-review-ship` (current), `remotes/origin/main`, `remotes/origin/oy/milestone/v0.3-bearers-docs`.
- No leftover execution phase branches (`oy/phase/01-05`): grep for `phase/0[1-5]` returned zero. ✓ Phase branches deleted after merge.
- Milestone branch `oy/milestone/v0.3-bearers-docs` exists and is at the P5-checkpoint commit (62ff0d7), matching the final-phase branch HEAD. ✓
- Final-phase branch `oy/phase/06-final-review-ship` exists and tracks milestone HEAD. ✓
**Branch hygiene verdict: PASS.**
### Commit Discipline — **PASS**
- 6 phase-ship commits follow `docs(P##): complete ...` convention (P00..P05). ✓
- 5 checkpoint commits follow `checkpoint(P##): ...` convention. ✓
- All 6 `---ci---` blocks well-formed (opening `---ci---`, closing `---/ci---`, YAML keys `project: oy`, `phase: N`, `milestone: v0.3`, `status: complete`, `tag_base: v0.2.x`, `phase_role`, `requirements.covered`, `requirements.partial`). ✓
- Multi-project `project: oy` field present in every `---ci---` block. ✓
- No malformed blocks, no missing closing tags, no orphan phase markers.
**Commit discipline verdict: PASS.**
### Build/Test Sanity — **PASS**
- `go build ./...` → GREEN (exit 0). ✓
- `go test ./...` → 26 packages GREEN, 4 packages `[no test files]` (identity/processing/rootpool/vault — pre-existing v0.1 layout), zero FAIL. ✓
- New v0.3 packages present and green: x/exit, x/bridge, x/hub, x/services (plus x/bearers, x/partner, x/bond extended; lexicon_meta_docs at root). ✓
### Fixes Applied
| Fix | File | Change | Severity |
|---|---|---|---|
| 1 | `.ciagent/oy/REQUIREMENTS.md` | v0.3 REQ table statuses: 8 REQs Pending → 6 Skeleton + 2 Complete (matches `---ci---` coverage) | critical (stale docs) |
| 2 | `.ciagent/oy/ROADMAP.md` | v0.3 milestone: added P0-P5 `[x]` completion markers + P6 `[ ]` + status line | critical (stale docs) |
Both fixes are committed under `fix(P06-audit):` per the final-audit protocol (see commit below).
### Overall Audit Verdict — **PASS**
```
Per-check verdicts (v0.3 final):
1. Reconstruction test — PASS (6 phase blocks; 6 tags v0.2.0..v0.2.5; REQ coverage exact on all 6 phases; CHECKPOINT current)
2. File discipline — PASS (after fix; 10 .ciagent/oy/ files + CHECKPOINT.json + config.json; no orphans)
3. Branch hygiene — PASS (no phase/01-05 branches; milestone + final-phase present)
4. Commit discipline — PASS (6 docs(P##) + 5 checkpoint(P##); all ---ci--- well-formed; project: oy present)
5. Build/test sanity — PASS (build GREEN; 26 pkgs GREEN; new v0.3 packages green)
Critical issues: 2 found → 2 fixed → 0 remaining
- Critical-1: REQUIREMENTS.md v0.3 statuses stale (Pending vs Skeleton/Complete) → FIXED
- Critical-2: ROADMAP.md v0.3 P0-P5 completion markers missing → FIXED
Non-critical: 1 (REVIEW.md v0.3 section pending concurrent review agent — non-blocking)
Escalations: 0
Overall verdict: PASS (after critical fixes)
Confidence: 0.92
AUDIT.md appended: /root/oy/.ciagent/oy/AUDIT.md ✓ (v0.3 section appended; v0.2 content preserved)
```
AUDIT.md
---
# Audit: OpenYield (oy) — v0.4 (Refinement — NFR) Final Phase
> **Auditor**: CIAgent security-auditor (read-only on source; writes only to `.ciagent/`)
> **Date**: 2026-08-17
> **Target**: v0.4 milestone (main..oy/milestone/v0.4-refinement) + `.ciagent/oy/` files
> **Milestone**: v0.4 — Refinement (NFR)
> **Autonomy**: full
## 1. Reconstruction Test (git log ↔ `.ciagent/` files)
### 1.1 Phase progression
The v0.4 milestone progresses through 5 phases, each with a `---ci---` block and a tag:
| Phase | Stage commits | Tag | Release ID | `.ciagent/` evidence |
|-------|---------------|-----|-----------|----------------------|
| P0 (pre-execution) | specify→clarify→research→plan→grill→mvp_ux_check→complete | v0.3.0 | 748 | PROJECT.md v0.4 section, REQUIREMENTS.md v0.4 table, ARCHITECTURE.md v0.4 section, PERSONAS.md v0.4, PLANS.md v0.4 plan, GRILL.md v0.4 section |
| P1 (lexicon+const) | execute→verify→complete | v0.3.1 | 749 | REVIEW.md (P1), `lexicon/lexicon.go` helper, `x/hub/types/cross_const_test.go` |
| P2 (lifecycle docs) | execute→verify→complete | v0.3.2 | 750 | ARCHITECTURE.md divergence section, `x/council/types/types_test.go` intent test |
| P3 (docs CI) | execute→verify→complete | v0.3.3 | 751 | `.gitea/workflows/docs-build.yml`, `.gitignore` site/ |
| P4 (final review+ship) | in progress | v0.3.4 (pending) | — | this AUDIT.md section + REVIEW.md v0.4 section |
Reconstruction: the git log subject lines match the `.ciagent/` file state. Each phase's `---ci---` block is present in the commit messages. Tags v0.3.0..v0.3.3 exist. **PASS.**
### 1.2 `.ciagent/` file discipline
14 files in `.ciagent/oy/` (ARCHITECTURE, AUDIT, GRILL, P1-P4_SHIP_VERIFICATION [P1-P3 from v0.2/v0.3, P4 pending], PERSONAS, PLANS, PROJECT, REQUIREMENTS, RESEARCH, REVIEW, ROADMAP). All present and updated for v0.4. CHECKPOINT.json reflects the current phase. config.json has `milestone: v0.4`, `milestone_type: nfr`, `tag_base: v0.3.x`. **PASS.**
### 1.3 Branch hygiene
Local: `main`, `oy/milestone/v0.4-refinement`, `oy/phase/04-final-review-ship` (current). Remote: `origin/main`, `origin/oy/milestone/v0.4-refinement`. All merged execution phase branches (P1, P2, P3) deleted locally. No leftover phase branches. The P4 final phase branch will be deleted at milestone ship. **PASS.**
### 1.4 Commit discipline
All 20 milestone commits have `---ci---` blocks with `project: oy`, `phase: N`, `milestone: v0.4`, `status: <stage>`, `tag_base: v0.3.x`, `milestone_type: nfr`. Commit subjects use conventional prefixes (docs, refactor, test, chore, verify, decision, checkpoint, Merge). No `feat:` subjects. **PASS.**
### 1.5 Build / test / coverage sanity
- `go build ./...`: clean.
- `go test ./...`: green (26 packages, all ok).
- `go vet ./...`: clean on touched packages.
- Coverage: `x/hub/types` 93.3% (v0.3 floor preserved; new cross-const test adds coverage). `x/council/types` 96.4% (improved from v0.3). Both above the 80% target (D-033). `go.mod` unchanged (zero deps, G-006). **PASS.**
## 2. NFR Purity Gate (v0.4 mandatory)
The NFR purity gate (D-047, D-001 filter) requires zero `feat:` commits in the v0.4 milestone range. The gate is checked on COMMIT SUBJECTS (not bodies), because `git log --grep` over-matches commit bodies that mention "feat:" in prose (e.g., the verify commits say "zero feat: commits in P2" in the body).
**Gate command**: `git log --format="%s" main..HEAD | grep -E "^feat:"`
**Result**: exit 1 (zero matches). The 20 commit subjects are: `docs(init)`, `docs(P00)` ×3, `docs(P00)` grill, `decision(P00)`, `Merge` ×3, `refactor(lexicon)`, `verify(P1)`, `checkpoint(p1)` ×3, `docs(arch)`, `verify(P2)`, `checkpoint(p2)`, `chore(ci)`, `verify(P3)`, `checkpoint(p3)`. None start with `feat:`.
**NFR purity gate: GREEN.**
## 3. Non-Critical Observations (P1+ flags, not blocking)
### P2-1: mkdocs build warnings (pre-existing v0.3 docs links)
- **Files**: `docs/index.md`, `docs/shared/vision.md` (link to `../README.md` / `../../README.md`)
- **Note**: `mkdocs build` produces 2 warnings about README.md not being in the docs tree. The build SUCCEEDS (warnings, not errors). These are pre-existing v0.3 docs content links, NOT introduced by v0.4. v0.4's REQ-032 is the CI workflow (which runs `mkdocs build` and succeeds despite the warnings), not the docs content.
- **Recommendation**: post-hoc fix in v0.5+ (either include README.md in mkdocs nav, or fix the relative links). Not a v0.4 ship blocker.
### P2-2: Gitea Actions `actions/upload-artifact@v4` compatibility
- **File**: `.gitea/workflows/docs-build.yml`
- **Note**: the workflow uses `actions/upload-artifact@v4`. Gitea Actions (which reimplements GitHub Actions) supports most `actions/*` but v4 of upload-artifact has had compatibility quirks. If the upload step fails, the `mkdocs build` step (the higher-priority check) would still have succeeded.
- **Recommendation**: post-hoc verify on the first real CI run; downgrade to v3 or use Gitea-native upload if needed. Not a ship blocker (the build is the gate, the artifact is a bonus).
### P2-3: NFR purity gate precision (documented in REVIEW.md P1+ #3)
- **Note**: `git log --grep "^feat:"` over-matches commit bodies. The audit uses the subject-only gate (`--format="%s" | grep -E "^feat:"`). This is documented for future milestones.
- **Recommendation**: bake the subject-only gate into the next milestone's PLAN. Not a ship blocker.
## 4. Overall Audit Verdict
### **PASS**
The v0.4 (Refinement — NFR) milestone is **shippable**.
**Per-check summary:**
| # | Check | Verdict |
|---|-------|---------|
| 1.1 | Reconstruction (phase progression, tags, `.ciagent` evidence) | PASS |
| 1.2 | `.ciagent` file discipline (14 files, CHECKPOINT, config.json) | PASS |
| 1.3 | Branch hygiene (no leftover phase branches; P4 final pending) | PASS |
| 1.4 | Commit discipline (`---ci---` blocks; conventional subjects; no feat:) | PASS |
| 1.5 | Build / test / coverage sanity (build, test, ≥80%, lexicon, G-006) | PASS |
| 2 | **NFR purity gate** (zero `feat:` commit subjects) | **GREEN** |
**Critical issues: 0.**
**Non-critical observations: 3** (all P2, post-hoc, non-blocking).
**Confidence in overall verdict: 0.90.**
## STRIDE security summary (per ci-auditor role, read-only)
| Category | Finding | Severity | Disposition |
|---|---|---|---|
| Spoofing | No auth surface added (v0.4 is refactor+test+docs+CI; no new identity logic) | Low | Accept |
| Tampering | The cross-const test (REQ-030) HARDENS tamper-resistance: a future change to `x/bond.CouponCapBps` or `x/hub.LendingCouponCapBps` is now detected by an automated test (was comment-only before v0.4). The absolute-value assertion (G-015) catches paired drift. Mission Lock (8%/0%) is MORE defended after v0.4. | Low (improved) | Accept |
| Repudiation | No audit-log changes in v0.4 | Low | Accept |
| Info Disclosure | The CI workflow (REQ-032) does NOT publish (build+artifact only, D-051); no secret exposure in the workflow YAML; `GITEA_TOKEN` is resolved via `resolveSecret()` from `.ciagent/.env.secrets` for release creation, never via shell-env `curl` | Low | Accept |
| Denial of Service | No network surface added; the CI workflow runs on push but does not expose a service | Low | Accept |
| Elevation of Privilege | No privilege surface added; the lexicon helper is a pure function; the regression guard only asserts existing consts | Low | Accept |
No threat exceeds the low/accept threshold. No escalations. v0.4 hardens the mission-locked const firewall (REQ-030) and the lexicon firewall (REQ-029) without introducing any new attack surface.
+70
View File
@@ -316,3 +316,73 @@ 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`, release `v0.2.6` (id 739), HEAD on `main`. 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-104` and `lexicon_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,56` defines `LendingCouponCapBps = uint32(800)` and `LendingCouponFloorBps = uint32(0)`; `x/bond/types/types.go:21,26` defines `CouponCapBps = 800` and `CouponFloorBps = 0`. No automated cross-check exists (only a cross-doc comment). Verified — A-304 drift risk is REAL.
- `SignalKindCount = 4` is 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.
+56 -97
View File
@@ -3,121 +3,80 @@ active_personas:
- id: backend-engineer
active: true
phase_specific: false
reason: Owns ALL Bearers skeleton Go modules in v0.3 (x/exit, x/bridge, x/bearers ext, x/partner ext, x/hub, x/services, x/bond ext). The v0.2 cosmos-engineer/security-engineer split is collapsed back into backend-engineer for v0.3 because the Cosmos-convention-alignment load is lower (no new IBC/governance/capability modules — x/bridge reuses the v0.2 satellite ICS-20 shape, x/hub is a fresh B2B scaffold). v0.3 is bespoke-type skeleton + tests work, which is backend-engineer's core territory.
frameworks: [Go 1.22 stdlib, Cosmos-style types (zero-dep)]
territory: ["x/exit/**", "x/bridge/**", "x/hub/**", "x/services/**", "x/bearers/**", "x/partner/**", "x/bond/**", "x/**/types/**", "x/**/keeper/**"]
constraints: ["zero external deps (G-006 — go.mod read-only)", "D-020 skeleton+tests pattern (D-035 continues)", "≥80% coverage on new/extended packages", "per-package lexicon assertion (REQ-012) in every new/extended test file", "by-ID-string inter-module refs (G-003 — no struct imports across x/<module>/types)", "locked-const invariants (HubService count, ServiceKind count, BridgeStatus count, ExitStatus count, Anchor credential fields)", "no live chain / no real IBC / no real bearer transports / no live B2B runtime"]
reason: Owns the v0.4 NFR code work: REQ-029 (lexicon shared helper in `lexicon/lexicon.go` + refactor of both meta-tests to consume it), REQ-030 (new `x/hub/types/cross_const_test.go` test-only import of `x/bond/types`), and REQ-031's regression-guard test (council SignalKind intent-assertion test). The v0.3 frontend/docs-writer personas are deactivated because v0.4 has no docs-content authoring; the only docs-adjacent work is the CI workflow file (REQ-032, owned by lead-developer as infra/config). v0.4 is pure Go test/refactor work, which is backend-engineer's core territory.
frameworks: [Go 1.22 stdlib (zero-dep), Go testing, lexicon firewall]
territory: ["lexicon/**", "lexicon_meta_test.go", "lexicon_meta_docs/**", "x/hub/types/**", "x/bond/types/**", "x/council/types/**"]
constraints: ["zero external deps (G-006 — go.mod stays zero-require)", "D-001 refinement-only filter: refactor/test/quality only, NO feat: (no new enum types, no new production types, no behavioral change)", "G-003 by-ID-string rule preserved in PRODUCTION imports; test-only cross-package imports are EXEMPT (G-003 test exemption — REQ-030 relies on this)", "lexicon firewall stays green on both x/ and docs/ after refactor", "locked-const invariants stay green (SignalKindCount==4 unchanged; LendingCouponCapBps==800, LendingCouponFloorBps==0 unchanged)", "≥80% coverage on any modified package (do not reduce existing coverage)"]
- id: lead-developer
active: true
phase_specific: false
reason: Coordinates the v0.3 phase decomposition (P1 firewall+docs foundation → P2 nomads → P3 freeholders → P4 Bearers I → P5 Bearers II → P6 review/ship), territory enforcement (warn mode per config.json), and final review. Owns the cross-component dependency finding (x/exit→x/bridge in P4; x/partner-Anchor→x/hub across P4→P5) that constrains phase ordering.
frameworks: [cross-cutting]
territory: [".ciagent/**", "**"]
constraints: ["D-044 phase ordering (firewall-first; P4 before P5 for Anchor→hub dep)", "milestone versioning (v0.3 / tag_base v0.2.x)", "lexicon gate on merge (REQ-012 extends to docs/)", "persona territory warn-mode enforcement", "zero Go deps invariant (G-006); docs build-deps are allowed (D-042)"]
- id: frontend-engineer
active: true
phase_specific: true
reason: v0.3 introduces the docs site (REQ-027) — the first non-skeleton, non-Go deliverable since v0.1's Mesh Experience. frontend-engineer owns the docs territory (docs/**, mkdocs.yml, README.md) and the docs firewall test (lexicon_meta_docs_test.go). Phase-specific: ACTIVE only for P1-P3 (docs phases); removed after P3 once the docs site is complete and the Bearers skeleton phases (P4/P5) are pure Go.
frameworks: [MkDocs Material, Markdown]
territory: ["docs/**", "mkdocs.yml", "README.md", "lexicon_meta_docs_test.go"]
constraints: ["lexicon-clean by construction (REQ-012 extended to docs via D-043 — 10 banned terms must not appear in docs/*.md or README.md; 'yield' banned as standalone word, 'OpenYield' safe via word-boundary regex)", "audience-organized nav (nomads/freeholders/shared/reference per D-042)", "~20-25 pages total per D-045", "no publishing CI in v0.3 (D-046 — mkdocs.yml buildable locally only)", "mkdocs.yml is build-only Python dep; go.mod stays zero-dep (G-006)"]
removed_after: P3
- id: docs-writer
active: true
phase_specific: true
reason: Custom persona for the docs content authoring load (REQ-027, D-045 ~20-25 pages across 4 audiences). Folded as a SEPARATE persona rather than into frontend-engineer because the skills differ: frontend-engineer owns the docs TOOLCHAIN (mkdocs.yml config, theme, nav structure, firewall test wiring) while docs-writer owns the CONTENT (the actual Markdown pages: nomads Reach/Stash/bearers pages, freeholders Standing/Bonds pages, shared Principles/Bread-Scale pages, reference architecture-index). Splitting keeps the toolchain-vs-content boundary explicit so a toolchain change does not entangle content review. Phase-specific: ACTIVE only for P1-P3; removed after P3.
frameworks: [Markdown, MkDocs Material (content authoring only)]
territory: ["docs/nomads/**/*.md", "docs/freeholders/**/*.md", "docs/shared/**/*.md", "docs/reference/**/*.md"]
constraints: ["lexicon-clean by construction (same REQ-012 extension — 'real production'/'real return' not 'real yield'; 'Holder'/'Reach' not 'account'; 'Stash'/'Vault'/'Root-Pool' not 'bank'/'deposit'/'savings')", "audience-organized (each page belongs to exactly one of nomads/freeholders/shared/reference)", "page-count budget per D-045", "no banned-term literals in page source (the docs firewall scans .md files directly, unlike .go which uses fragment assembly)"]
removed_after: P3
reason: Coordinates v0.4 phase decomposition (P1 lexicon+const hardening → P2 lifecycle divergence docs+guard → P3 docs CI → P4 review/ship), territory enforcement (warn mode per config.json), the final-phase NFR purity gate audit (zero `feat:` commits), and the milestone ship. Owns REQ-031's ARCHITECTURE.md documentation deliverable (the divergence-decision writeup) and REQ-032's CI workflow file (`.gitea/workflows/docs-build.yml`) as infra/config territory. Also owns the v0.4 ROADMAP.md / REQUIREMENTS.md status updates at milestone completion.
frameworks: [cross-cutting, Gitea Actions, Markdown, YAML]
territory: [".ciagent/**", ".gitea/workflows/**", ".ciagent/oy/ARCHITECTURE.md", ".ciagent/oy/ROADMAP.md", ".ciagent/oy/REQUIREMENTS.md"]
constraints: ["D-052 phase ordering (P1 firewall-first; each phase independently shippable)", "milestone versioning (v0.4 NFR / tag_base v0.3.x)", "NFR purity gate: zero feat: commits in the milestone (final-phase audit)", "persona territory warn-mode enforcement", "zero Go deps invariant (G-006) preserved; CI workflow may use build-only Python deps (mkdocs) in a separate job", "D-001 filter: no feat: scope creep — the CI workflow is chore (build+artifact), NOT a publishing feature"]
deactivated:
- id: data-engineer
reason: INACTIVE for v0.3. The project has zero external deps and no database; the v0.2 data-engineer owned genesis.go schema helpers, which are a thin layer in v0.3's new modules (x/exit, x/bridge, x/hub, x/services each get a small GenesisState + ValidateGenesis following the v0.2 A-212 pattern). That work is owned by backend-engineer in v0.3 (the genesis schema is part of the skeleton type authoring, not a separate schema-design discipline). Reactivate if a future milestone adds a real store/migration.
- id: cosmos-engineer
reason: The v0.2 custom persona is NOT reactivated for v0.3. v0.3's new modules do not map onto new Cosmos SDK modules the way v0.2's did (x/gov, x/group, x/authz, x/capability, x/ibc-transfer). x/bridge reuses the v0.2 satellite ICS-20 shape (already aligned); x/hub/x/services/x/exit are bespoke B2B/service scaffolds with no direct Cosmos analog. The Cosmos-convention-alignment load drops below the threshold that justified a separate persona. backend-engineer absorbs the work.
- id: security-engineer
reason: The v0.2 custom persona is NOT reactivated for v0.3. v0.3's invariant density is lower than v0.2's (no Mission Lock, no new fee/bond clamp — the 8%/0% consts are reused unchanged from v0.2; the new locked-consts are enum counts: HubService=3, ServiceKind=4, BridgeStatus, ExitStatus). The locked-const + invariant tests are absorbed by backend-engineer's per-package test authoring. The docs firewall (lexicon_meta_docs_test.go) is frontend-engineer's territory. Reactivate in v0.4 if a new Mission-Lock-class invariant lands.
- id: ci-security-auditor
reason: Default deactivated; activate in P6 (review/ship) for the v0.3 milestone audit.
- id: mesh-engineer
reason: Still not needed in v0.3 (OY-SAT/OY-QR are type stubs only; no hardware/RF runtime). Activate in v0.4+ for real bearer runtime.
custom_personas:
- id: frontend-engineer
reason: INACTIVE for v0.4. The v0.3 docs site (docs/**, mkdocs.yml) is COMPLETE; v0.4 does not author or restructure docs content. The only docs-adjacent work is the CI workflow that BUILDS the existing site (REQ-032), which is infra/config territory owned by lead-developer, not frontend toolchain. Reactivate in v0.5+ if docs content is restructured or i18n is added.
- id: docs-writer
rationale: v0.3's docs deliverable (~20-25 pages across 4 audiences per D-045) is a substantial content-authoring load distinct from the docs toolchain work. A dedicated docs-writer keeps the content-vs-toolchain boundary explicit: frontend-engineer owns mkdocs.yml/nav/theme/firewall-wiring; docs-writer owns the page content. This split means a toolchain PR (e.g., adding a markdown extension) does not entangle a content review (e.g., a nomads Reach-page rewrite), and vice versa. Distinct from frontend-engineer because content authoring (prose, audience voice, lexicon-safe phrasing) is a different skill from toolchain config (YAML, theme, nav, Go test wiring). Removed after P3 when the docs site is complete.
reason: INACTIVE for v0.4. v0.3's docs-writer owned page content authoring; v0.4 has zero new docs pages. The only documentation work is the ARCHITECTURE.md divergence-decision section (REQ-031), which is lead-developer's architecture territory, not audience-content authoring. Reactivate if a future milestone adds docs pages.
- id: data-engineer
reason: INACTIVE for v0.4 (carried from v0.3). The project has zero external deps and no database; REQ-031 does not change genesis schemas (it documents divergence, no schema change). Reactivate if a future milestone adds a real store/migration.
- id: cosmos-engineer
reason: INACTIVE for v0.4. v0.4 has no new Cosmos-convention-alignment work (no new modules, no IBC, no governance runtime). Reactivate in v0.5+ if live-runtime promotion of the v0.3 Bearers skeletons lands.
- id: security-engineer
reason: INACTIVE for v0.4. v0.4 introduces no new Mission-Lock-class invariant; REQ-029/030/031 are refactor/test/docs, not security invariants. The existing locked-consts stay unchanged. Reactivate if a future milestone adds a new mission-locked const or a new clamp.
- id: ci-security-auditor
reason: Default deactivated; activate in P4 (final review/ship) for the v0.4 milestone audit and NFR purity gate enforcement.
- id: mesh-engineer
reason: Still not needed in v0.4 (no bearer hardware runtime; OY-SAT/OY-QR remain type stubs). Activate in v0.5+ for real bearer runtime.
custom_personas: []
---
# Personas: OpenYield (oy) — v0.3 (Bearers & Documentation)
# Personas: OpenYield (oy) — v0.4 (Refinement — NFR)
> This file supersedes the v0.2 PERSONAS.md for the v0.3 milestone. The v0.2
> custom personas (cosmos-engineer, security-engineer) are NOT reactivated for
> v0.3 — see Deactivated below for rationale. The default four personas are
> backend-engineer, data-engineer, frontend-engineer, lead-developer; v0.3
> activates backend-engineer + lead-developer + frontend-engineer (phase-
> specific) and adds one custom persona (docs-writer, phase-specific).
> This file supersedes the v0.3 PERSONAS.md for the v0.4 milestone. v0.4 is a
> refinement-only NFR milestone (D-047): zero `feat:` phases. The active
> roster is **backend-engineer + lead-developer** only. The v0.3
> phase-specific personas (frontend-engineer, docs-writer) are deactivated
> because v0.4 does not author docs content or restructure the docs toolchain;
> the only docs-adjacent work is a CI workflow file (REQ-032) owned by
> lead-developer as infra/config.
## Active Roster
### backend-engineer
- **Domain**: All Bearers skeleton Go modules in v0.3 — `x/exit`, `x/bridge`, `x/hub`, `x/services` (new); `x/bearers`, `x/partner`, `x/bond` (extended). Owns the D-020 skeleton+tests pattern (D-035 continues): Go types + keeper stubs + invariant tests, no live chain. Absorbs the v0.2 cosmos-engineer/security-engineer split because v0.3's Cosmos-convention and invariant density are lower.
- **Frameworks**: Go 1.22 stdlib, Cosmos-style types (zero-dep).
- **Territory**: `x/exit/**`, `x/bridge/**`, `x/hub/**`, `x/services/**`, `x/bearers/**`, `x/partner/**`, `x/bond/**`, `x/**/types/**`, `x/**/keeper/**`. (`go.mod` is read-only per G-006.)
- **Constraints**: zero external deps (G-006), D-020 skeleton+tests (D-035), ≥80% coverage on new/extended packages, per-package lexicon assertion (REQ-012), by-ID-string inter-module refs (G-003), locked-const invariants (HubService/ServiceKind/BridgeStatus/ExitStatus counts + Anchor credential fields), no live chain/IBC/bearer/B2B runtime.
| Persona | Active | Phase-specific | Territory |
|---------|--------|-----------------|-----------|
| backend-engineer | yes | no (all phases) | `lexicon/**`, `lexicon_meta*`, `x/hub/types`, `x/bond/types`, `x/council/types` |
| lead-developer | yes | no (all phases) | `.ciagent/**`, `.gitea/workflows/**` |
### lead-developer
- **Domain**: v0.3 phase decomposition (P1 firewall+docs foundation → P2 nomads → P3 freeholders → P4 Bearers I → P5 Bearers II → P6 review/ship), territory enforcement (warn mode), final review. Owns the cross-component dependency finding that constrains phase ordering: `x/exit``x/bridge` (same phase P4); `x/partner`-Anchor→`x/hub` (P4 before P5).
- **Frameworks**: cross-cutting.
- **Territory**: `.ciagent/**`, `**`.
- **Constraints**: D-044 phase ordering (firewall-first; P4→P5 for Anchor→hub dep), milestone versioning (v0.3 / tag_base v0.2.x), lexicon gate on merge (REQ-012 extends to docs/), persona territory warn-mode, zero Go deps (G-006; docs build-deps allowed per D-042).
## Phase-Persona Matrix
### frontend-engineer (phase-specific: P1-P3 only)
- **Domain**: v0.3 docs TOOLCHAIN — `mkdocs.yml` (site_name, nav, theme: material, markdown_extensions), the audience-based nav structure (nomads/freeholders/shared/reference per D-042), and the docs firewall test wiring (`lexicon_meta_docs_test.go` mirroring `lexicon_meta_test.go` with `lexicon.FindBannedTerm` + word-boundary regex + self-test table + self-exclusion, scanning `README.md` + `docs/**/*.md`). Owns the firewall landing in P1 BEFORE content (D-044 firewall-first). Removed after P3.
- **Frameworks**: MkDocs Material, Markdown, Go testing (for the firewall test).
- **Territory**: `docs/**` (toolchain), `mkdocs.yml`, `README.md`, `lexicon_meta_docs_test.go`.
- **Constraints**: lexicon-clean by construction (REQ-012 extended via D-043; 10 banned terms absent from docs; "yield" banned standalone, "OpenYield" safe), audience-organized nav, ~20-25 pages total (D-045), no publishing CI in v0.3 (D-046), mkdocs.yml build-only Python dep (go.mod stays zero-dep per G-006).
- **Removed after**: P3.
| Phase | Personas | Work |
|-------|----------|------|
| P0 (pre-execution) | lead-developer | spec/clarify/research/plan/grill/mvp-ux + ship |
| P1 (lexicon hardening) | backend-engineer | REQ-029 shared helper + REQ-030 cross-const test |
| P2 (lifecycle divergence) | backend-engineer (regression-guard test) + lead-developer (ARCHITECTURE.md docs) | REQ-031 |
| P3 (docs build CI) | lead-developer | REQ-032 `.gitea/workflows/docs-build.yml` |
| P4 (final review/ship) | lead-developer + ci-security-auditor (audit) | review + NFR purity gate + milestone ship |
### docs-writer (custom, phase-specific: P1-P3 only)
- **Domain**: v0.3 docs CONTENT — the actual Markdown pages across the four audiences (nomads: Reach/Stash/bearers/Maps-Pay/Pacts/standing-basics; freeholders: 4-signals/Bayesian-Standing/Stands-Guilds/Councils-Voice/Bonds/Partner-spectrum; shared: Six-Principles/Bread-Scale/Storage-pools/Watchers-Mirror/Lexicon-glossary/Vision-overview; reference: architecture-index/component-map). Split from frontend-engineer so content review and toolchain review do not entangle. Removed after P3.
- **Frameworks**: Markdown, MkDocs Material (content authoring only).
- **Territory**: `docs/nomads/**/*.md`, `docs/freeholders/**/*.md`, `docs/shared/**/*.md`, `docs/reference/**/*.md`.
- **Constraints**: lexicon-clean by construction (same REQ-012 extension; "real production"/"real return" not "real yield"; "Holder"/"Reach" not "account"; "Stash"/"Vault"/"Root-Pool" not "bank"/"deposit"/"savings"), audience-organized (each page in exactly one audience dir), page-count budget per D-045, no banned-term literals in page source (docs firewall scans .md directly, unlike .go fragment assembly).
- **Removed after**: P3.
## D-001 Refinement-Only Filter (governs all v0.4 work)
## Deactivated
Every v0.4 change must pass the D-001 filter:
- **Accept**: refactor, test, docs, chore, perf, fix, quality, coverage, architecture (drift fix only), improvement (of existing).
- **Reject**: any `add_requirement` + `feat:`-class signal (new capability, new enum type, new production type, new CLI, new distribution channel, new backend).
- **Enforcement**: lead-developer reviews each phase's commit set; the final-phase audit runs the NFR purity gate (`git log --grep "^feat:" --all-match` on the milestone range must return zero).
- **data-engineer** — INACTIVE for v0.3. Zero deps + no database; the v0.2 genesis.go schema work is a thin layer absorbed by backend-engineer in v0.3's new modules. Reactivate if a future milestone adds a real store/migration.
- **cosmos-engineer** (v0.2 custom) — NOT reactivated. v0.3's new modules do not map onto new Cosmos SDK modules (x/bridge reuses v0.2 satellite shape; x/hub/x/services/x/exit are bespoke). Cosmos-convention load drops below the threshold for a separate persona; backend-engineer absorbs.
- **security-engineer** (v0.2 custom) — NOT reactivated. v0.3's invariant density is lower (no new Mission-Lock/fee-clamp; 8%/0% consts reused unchanged; new locked-consts are enum counts). Locked-const + invariant tests absorbed by backend-engineer's per-package test authoring; docs firewall is frontend-engineer's. Reactivate in v0.4 if a new Mission-Lock-class invariant lands.
- **ci-security-auditor** — Default deactivated; activate in P6 (review/ship) for the milestone audit.
- **mesh-engineer** — Still not needed (OY-SAT/OY-QR are type stubs only). Activate in v0.4+ for real bearer runtime.
## Constraints Carried Forward
## Custom Personas
- **G-003** by-ID-string rule: preserved in PRODUCTION imports. Test-only cross-package imports are EXEMPT (REQ-030 relies on this exemption — `x/hub/types/cross_const_test.go` imports `x/bond/types` in a `_test.go` file only).
- **G-006** zero Go deps: `go.mod` stays zero-require. The CI workflow (REQ-032) may use build-only Python deps (mkdocs + mkdocs-material) in a separate CI job; this does not touch `go.mod`.
- **G-014** lexicon shared helper: REQ-029 closes the G-014 drift risk by adding `lexicon.SyntheticBannedStrings()` as the single source for the synthetic self-test table consumed by BOTH meta-tests.
- **Locked consts unchanged**: `SignalKindCount==4`, `LendingCouponCapBps==800`, `LendingCouponFloorBps==0`, `CouponCapBps==800`, `CouponFloorBps==0` — v0.4 does NOT change any locked const. REQ-030 asserts they stay in lockstep; REQ-031 asserts the 4-signal shape is intentional.
- **docs-writer** — v0.3's docs deliverable (~20-25 pages, D-045) is a substantial content-authoring load distinct from the docs toolchain. A dedicated docs-writer keeps the content-vs-toolchain boundary explicit: frontend-engineer owns mkdocs.yml/nav/theme/firewall-wiring; docs-writer owns page content. This split means a toolchain PR does not entangle a content review and vice versa. Distinct from frontend-engineer because prose/audience-voice/lexicon-safe-phrasing is a different skill from YAML/theme/nav/Go-test wiring. Removed after P3 when the docs site is complete.
## Removal Notes
## Framework Alignment
- **Go 1.22** — backend-engineer targets Go 1.22 (`go.mod`); zero external deps (G-006).
- **MkDocs Material** — frontend-engineer + docs-writer target MkDocs Material (D-042); build-only Python dep, NOT a Go dependency. No publishing CI in v0.3 (D-046).
## Territory Alignment
- backend-engineer owns all `x/*` Bearers-skeleton modules (new: exit/bridge/hub/services; extended: bearers/partner/bond) + shared `types/`+`keeper/` authoring.
- frontend-engineer owns the docs toolchain (`docs/**` config, `mkdocs.yml`, `README.md`, `lexicon_meta_docs_test.go`).
- docs-writer owns docs content (`docs/<audience>/**/*.md`).
- lead-developer owns `.ciagent/**` + `**` for cross-cutting coordination.
- `go.mod` is read-only in v0.3 (G-006) — no persona may modify it; docs build-deps are allowed (D-042) but live outside `go.mod`.
## Constraint Alignment
- **Lexicon (REQ-012)** — every active persona carries it; backend-engineer asserts per test file (x/*); frontend-engineer asserts via the docs firewall (docs/* + README.md). The firewall extension is a sibling test, NOT a modification of the v0.2 meta-test (D-043).
- **Skeleton + tests (D-020/D-035)** — backend-engineer enforces.
- **≥80% coverage** — backend-engineer owns the gate for x/* packages.
- **Phase ordering (D-044)** — lead-developer enforces; firewall-first (P1) before content (P2/P3); P4 (exit/bridge/bearers/partner) before P5 (hub/services/bond) for the Anchor→hub dependency.
- **Locked-const invariants** — backend-engineer owns; HubService=3, ServiceKind=4, BridgeStatus count, ExitStatus count, Anchor credential fields, 8%/0% bond consts (reused).
## Phase-Specific Personas
- **frontend-engineer** — phase-specific to v0.3 P1-P3 (docs phases). Removed after P3; the Bearers skeleton phases (P4/P5) are pure Go (backend-engineer). Reassess at v0.4 if new docs work is queued.
- **docs-writer** — phase-specific to v0.3 P1-P3 (docs content). Removed after P3 with frontend-engineer.
- frontend-engineer and docs-writer were `removed_after: P3` in v0.3. They are formally deactivated here for v0.4 (not just phase-removed) because v0.4 has no docs-content phase at all.
- No phase-specific personas are created for v0.4. The roster is stable across all phases.
+254
View File
@@ -790,3 +790,257 @@ The v0.3 Phase 0 clarify/ideate/research stages produced 13 clarification decisi
| A-312 | HubServiceCount=3 | P5-01-01 |
| A-313 | OrderSideCount=2, OrderStatusCount=3 | P5-03-01 |
| A-315 | v0.2 cosmos-engineer/security-engineer NOT reactivated | Persona assignments (backend-engineer owns all P4/P5) |
---
## Milestone v0.4 — Refinement (NFR) — Phase Plan
### Milestone Summary
- **Milestone**: v0.4 — Refinement (NFR)
- **Type**: NFR (zero `feat:` phases by construction; D-047). Final-phase audit enforces the NFR purity gate (`git log --grep "^feat:"` on the milestone range returns zero).
- **Tag base**: `v0.3.x` patch line. P0 ships as `v0.3.0`; execution phases `v0.3.1..v0.3.3`; final phase P4 patch `v0.3.4` IS the milestone release. No separate minor tag (D-008).
- **Phases**: 4 execution + 1 final = 5. Phase 0 (this PLAN) is pre-execution.
- **Depth**: refinement-only — refactor + test + docs + chore. No new production types, no new enum types, no behavioral changes. All work passes the D-001 refinement-only filter.
- **Coverage target**: ≥80% on any modified package; do NOT reduce existing coverage (v0.3 floor is 93.3%).
- **New modules**: ZERO. Touched files: `lexicon/lexicon.go` (add helper), `lexicon_meta_test.go` + `lexicon_meta_docs/lexicon_meta_docs_test.go` (refactor to consume helper), `x/hub/types/cross_const_test.go` (NEW test file), `x/council/types/types_test.go` (add intent test), `.ciagent/oy/ARCHITECTURE.md` (docs), `.gitea/workflows/docs-build.yml` (NEW CI).
- **Phase ordering** (D-052): P1 lexicon+const hardening → P2 lifecycle divergence docs+guard → P3 docs build CI → P4 final review/audit/ship. P1 is firewall-first (highest-severity regression risk); each phase independently shippable (vertical slices).
- **Personas**: backend-engineer (P1, P2 code), lead-developer (P2 docs, P3 CI, P4 review/ship). v0.3 frontend-engineer + docs-writer deactivated.
### Cross-Phase Dependency Map
```
P1 (lexicon helper, cross-const test) ──► P4 (review)
P2 (lifecycle docs + regression guard) ─► P4 (review)
P3 (docs build CI) ─► P4 (review)
```
No hard cross-phase blockers (all three execution phases are independent vertical slices). P4 depends on P1+P2+P3 completion only.
### D-001 Refinement-Only Filter (governs every v0.4 task)
- **Accept**: refactor, test, docs, chore, quality, architecture (drift fix only).
- **Reject**: `add_requirement` + `feat:`-class signal (new enum type, new production type, new capability, new CLI, new distribution channel).
- **Pre-seeded NFR REQs** (REQ-029..REQ-032) are exempt — already in REQUIREMENTS.md before PLAN.
---
## Phase P1 — Lexicon + Const Hardening
- **Slug**: `lexicon-const-hardening`
- **Branch**: `oy/phase/01-lexicon-const-hardening`
- **REQs covered**: REQ-029 (lexicon shared helper), REQ-030 (cross-const test)
- **Tag**: `v0.3.1`
- **Goal**: Close the G-014 lexicon-firewall drift risk (shared synthetic-string helper) and the A-304 hub↔bond const drift risk (cross-package equality test). Both are refactor+test; no behavioral change.
### Wave 1 — Shared helper + cross-const test (parallel; no internal deps)
| Task ID | REQ | Persona | Files | Deliverable | Must-have verification | Blocked-by |
|---|---|---|---|---|---|---|
| P1-01-01 | REQ-029 | backend-engineer | `lexicon/lexicon.go` | Add `SyntheticBannedStrings() []string` returning the 10 synthetic strings currently duplicated in both meta-tests. Each string embeds exactly one banned term (from `BannedTerms()`) in a plausible sentence context. Source uses `BannedTerms()` so the lexicon package's own source stays lexicon-clean (fragment-assembled). Add a doc comment cross-referencing G-014 and both consuming meta-tests. | `go build ./lexicon/...` succeeds; `SyntheticBannedStrings()` returns exactly `len(BannedTerms())` strings; each returned string triggers `FindBannedTerm` (self-verifiable in a new test); lexicon package source stays lexicon-clean (the existing `lexicon_meta_test.go` x/ scan does not scan `lexicon/` itself, but the helper must not introduce a banned-term literal). | — |
| P1-01-02 | REQ-029 | backend-engineer | `lexicon_meta_test.go`, `lexicon_meta_docs/lexicon_meta_docs_test.go` | Refactor `TestLexiconMetaSelfTestTable` and `TestLexiconMetaDocsSelfTestTable` to consume `lexicon.SyntheticBannedStrings()` instead of building their own `synthetic` slice. Remove the duplicated 10-string table from both. Keep the per-string `FindBannedTerm` assertion loop (detection coverage unchanged). Keep the `len(terms) == 10` assertion from `BannedTerms()`. Add a `len(synthetic) == len(terms)` assertion derived from the helper. | `go test ./lexicon_meta... ./lexicon_meta_docs/...` passes; both tests still fail if detection breaks; the duplicated table is gone (grep for the old literal sentence fragments returns zero matches in meta-test files); coverage on both meta-tests does not decrease. | P1-01-01 |
| P1-02-01 | REQ-030 | backend-engineer | `x/hub/types/cross_const_test.go` (NEW) | New test file, package `types` (same package as `x/hub/types`). Test-only import `github.com/oy/openyield/x/bond/types` (G-003 test-exempt). Tests: (1) `TestLendingCouponCapMatchesBondCap` asserts `hub.LendingCouponCapBps == bond.CouponCapBps`; (2) `TestLendingCouponFloorMatchesBondFloor` asserts `hub.LendingCouponFloorBps == bond.CouponFloorBps`; (3) `TestConstsAreMissionLocked800And0` asserts both caps == 800 and both floors == 0 (regression guard for the mission-locked values). Doc comment cross-references A-304 and the G-003 test-exemption. | `go test ./x/hub/types/...` passes; all three tests green at current values (800/0); test fails closed if either const drifts; NO production file in `x/hub/types/` imports `x/bond/types` (G-003 production firewall intact — verified by a grep of non-test `.go` files). | — |
### Wave 2 — Verification (blocked-by Wave 1)
| Task ID | REQ | Persona | Files | Deliverable | Must-have verification | Blocked-by |
|---|---|---|---|---|---|---|
| P1-99-01 | REQ-029, REQ-030 | lead-developer | — (verification only) | Run `go test ./...` (all packages, both lexicon firewalls + cross-const test + all v0.3 baseline tests); run `grep -rn "open a .* here\|make a .* now\|compounding .* rate" lexicon_meta_test.go lexicon_meta_docs/` and confirm zero matches (old duplicated table fully removed); confirm `go.mod` unchanged (zero deps, G-006). | `go test ./...` exits 0; grep returns zero; `go.mod` diff is empty. | P1-01-02, P1-02-01 |
### P1 Must-Haves (verification gate before SHIP)
- [ ] `lexicon.SyntheticBannedStrings()` exists and returns 10 strings.
- [ ] Both meta-tests consume the helper (no duplicated table).
- [ ] `x/hub/types/cross_const_test.go` exists and passes.
- [ ] `go test ./...` green (all v0.3 baseline + v0.4 new tests).
- [ ] `go.mod` unchanged.
- [ ] No `feat:` commits in P1 (D-001 filter).
---
## Phase P2 — Lifecycle Divergence Documentation + Regression Guard
- **Slug**: `lifecycle-divergence-docs`
- **Branch**: `oy/phase/02-lifecycle-divergence-docs`
- **REQs covered**: REQ-031
- **Tag**: `v0.3.2`
- **Goal**: Document the AUDIT §193 council lifecycle type divergences (P1-1 Proposal/VoteOption absent; P1-2 SignalKind 4-vs-5) in ARCHITECTURE.md and add an intent-assertion test guarding the current `SignalKindCount==4` shape. No enum additions, no locked-const changes (D-050).
### Wave 1 — Docs + regression guard (parallel; no internal deps)
| Task ID | REQ | Persona | Files | Deliverable | Must-have verification | Blocked-by |
|---|---|---|---|---|---|---|
| P2-01-01 | REQ-031 | lead-developer | `.ciagent/oy/ARCHITECTURE.md` | Add a "Council Voice/Council Interface — Lifecycle Type Divergence Decisions (v0.4)" subsection under the v0.4 Architecture section. Document: (a) P1-1 `Proposal`/`ProposalStatus`/`VoteOption` absent — deferred to v0.5+ governance runtime (feat:-class, rejected by D-001); (b) P1-2 `SignalKind` 4 sources (Stash/Standing/Vouch/Capital) vs spec `VoiceSource` 5 sources — the AUDIT rationale (Freeholder is eligibility, Guild is council tier, Capital is committed-capital per vision §9.1) and the decision to KEEP the 4-source shape; (c) the Bearers `ValidateGenesis` no-op is correct per spec (P2, no action). Cross-reference AUDIT §193 P1-1/P1-2/P2. | The ARCHITECTURE.md section exists and names both P1 divergences + the P2 no-action; a reader can find why Proposal/VoteOption are absent and why SignalKind is 4-not-5 without re-reading AUDIT. | — |
| P2-02-01 | REQ-031 | backend-engineer | `x/council/types/types_test.go` | Add `TestSignalKindShapeIntentional` — an intent-assertion test that documents and guards the 4-source `SignalKind` shape. Asserts: `SignalKindCount == 4`; `AllSignalKinds()` returns exactly [Stash, Standing, Vouch, Capital]; a doc-comment block above the test explaining the AUDIT §193 P1-2 rationale (why Freeholder and Guild are NOT signal kinds, why Capital IS) so a future agent does not "fix" the divergence by silently changing the locked const. This is a regression GUARD, not a shape change — the existing `TestSignalKindCountLockedConst` already locks the count; this test adds the INTENT documentation. | `go test ./x/council/types/...` passes; the new test fails if `SignalKindCount` changes from 4; the test's doc comment explains the rationale. | — |
### Wave 2 — Verification (blocked-by Wave 1)
| Task ID | REQ | Persona | Files | Deliverable | Must-have verification | Blocked-by |
|---|---|---|---|---|---|---|
| P2-99-01 | REQ-031 | lead-developer | — (verification only) | Run `go test ./...`; confirm ARCHITECTURE.md subsection present; confirm no production `.go` file was modified in P2 (only a `_test.go` file + a `.ciagent/` doc). | `go test ./...` exits 0; `git diff --name-only <p2-base> HEAD -- 'x/**/*.go'` returns only `x/council/types/types_test.go` (no production files). | P2-01-01, P2-02-01 |
### P2 Must-Haves (verification gate before SHIP)
- [ ] ARCHITECTURE.md has the Council lifecycle divergence subsection.
- [ ] `TestSignalKindShapeIntentional` exists and passes.
- [ ] `SignalKindCount` unchanged (still 4).
- [ ] No production `.go` files modified in P2 (test + docs only).
- [ ] No `feat:` commits in P2 (D-001 filter).
---
## Phase P3 — Docs Build CI
- **Slug**: `docs-build-ci`
- **Branch**: `oy/phase/03-docs-build-ci`
- **REQs covered**: REQ-032
- **Tag**: `v0.3.3`
- **Goal**: Ship a Gitea Actions workflow that runs the lexicon firewall (`go test ./...`) + builds the docs site (`mkdocs build`) on every push, uploading `site/` as a CI artifact. Full Gitea Pages publishing deferred (no hosting target configured, D-051). The workflow file is `chore` (CI config), not `feat:`.
### Wave 1 — CI workflow (single task; no internal deps)
| Task ID | REQ | Persona | Files | Deliverable | Must-have verification | Blocked-by |
|---|---|---|---|---|---|---|
| P3-01-01 | REQ-032 | lead-developer | `.gitea/workflows/docs-build.yml` (NEW) | Gitea Actions workflow (YAML). Triggers: on push (all branches). Jobs: (1) `go-test` — setup Go 1.22, `go test ./...` (runs lexicon firewall + all x/* tests); (2) `docs-build` — setup Python, `pip install mkdocs mkdocs-material`, `mkdocs build` (produces `site/`), upload `site/` as an artifact. **G-016 binding**: the `docs-build` job MUST depend on `go-test` (serial: `needs: go-test`), so a lexicon-firewall failure blocks the docs build (no false-green docs build from a repo with a lexicon violation). `go.mod` is NOT modified (Python deps are isolated to the `docs-build` job). Doc comment in the YAML references D-046 (forward-reference), D-051 (no Pages publish), and G-016 (firewall-gates-docs-build). | YAML parses; `go test ./...` command matches the local green baseline; `mkdocs build` command matches the README's local build invocation; `docs-build` job has `needs: go-test`; no `go.mod` change. | — |
### Wave 2 — Verification (blocked-by Wave 1)
| Task ID | REQ | Persona | Files | Deliverable | Must-have verification | Blocked-by |
|---|---|---|---|---|---|---|
| P3-99-01 | REQ-032 | lead-developer | — (verification only) | Validate the workflow YAML parses; confirm `go test ./...` still green locally (the workflow does not change Go source); confirm `mkdocs build` succeeds locally (mkdocs installed); confirm `go.mod` unchanged. | YAML parse OK; `go test ./...` exits 0; `mkdocs build` produces `site/`; `go.mod` diff empty. | P3-01-01 |
### P3 Must-Haves (verification gate before SHIP)
- [ ] `.gitea/workflows/docs-build.yml` exists and parses.
- [ ] Workflow runs `go test ./...` and `mkdocs build`.
- [ ] `go.mod` unchanged.
- [ ] No `feat:` commits in P3 (D-001 filter; the workflow is `chore`).
---
## Phase P4 — Final Review + Audit + Milestone Ship
- **Slug**: `final-review-ship`
- **Branch**: `oy/phase/04-final-review-ship`
- **REQs covered**: all v0.4 REQs (REQ-029..REQ-032) — final coverage accounting
- **Tag**: `v0.3.4` (IS the v0.4 milestone release; D-008)
- **Goal**: Multi-persona review across P1..P3, audit (reconstruction test + NFR purity gate), milestone ship (merge to main, tag v0.3.4, release, delete all milestone branches).
### Wave 1 — Review + Audit (parallel; no internal deps)
| Task ID | REQ | Persona | Files | Deliverable | Must-have verification | Blocked-by |
|---|---|---|---|---|---|---|
| P4-01-01 | — | lead-developer (review) | `.ciagent/oy/REVIEW.md` (NEW for v0.4) | Multi-persona code review across P1..P3. Adversarial probes: (1) does the shared helper actually dedupe (grep for old table); (2) does the cross-const test fail closed on drift; (3) does the regression guard lock the 4-signal shape; (4) does the CI workflow YAML parse. Auto-apply P0 fixes; flag P1+ for post-hoc. | REVIEW.md written; P0 issues (if any) fixed in P4; P1+ flagged. | P1, P2, P3 |
| P4-02-01 | — | ci-security-auditor (audit) | `.ciagent/oy/AUDIT.md` (v0.4 section) | Audit: (1) reconstruction test (git log ↔ `.ciagent/` files for v0.4); (2) file/branch/commit discipline; (3) **NFR purity gate**`git log --grep "^feat:" $(git rev-list --tags=v0.3.0..v0.3.4)` returns zero (or the equivalent milestone-range grep); (4) coverage did not decrease on any modified package. | AUDIT.md v0.4 section written; NFR purity gate GREEN (zero feat: commits); reconstruction test passes. | P1, P2, P3 |
### Wave 2 — Ship (blocked-by Wave 1)
| Task ID | REQ | Persona | Files | Deliverable | Must-have verification | Blocked-by |
|---|---|---|---|---|---|---|
| P4-03-01 | REQ-029..REQ-032 | lead-developer (ship) | `.ciagent/oy/REQUIREMENTS.md`, `.ciagent/oy/ROADMAP.md`, `.ciagent/CHECKPOINT.json` | Milestone ship via `ciagent ship 4` (or ShipWorkflow fallback). Merge `oy/phase/04``oy/milestone/v0.4-refinement``main`. Tag `v0.3.4`. Create release (best-effort via `resolveSecret(GITEA_TOKEN)`). Delete all milestone branches (local + remote). Update REQUIREMENTS.md (v0.4 REQs → Complete) + ROADMAP.md (v0.4 → COMPLETE). Commit `docs(milestone): complete v0.4`. Clear CHECKPOINT. | Tag `v0.3.4` created; milestone branch merged to main; release created (or local-only fallback); all `oy/phase/*` and `oy/milestone/v0.4-refinement` branches deleted; REQUIREMENTS.md + ROADMAP.md updated; checkpoint cleared. | P4-01-01, P4-02-01 |
### P4 Must-Haves (verification gate before milestone complete)
- [ ] REVIEW.md v0.4 section written; P0 fixes applied.
- [ ] AUDIT.md v0.4 section written; reconstruction test passes.
- [ ] **NFR purity gate GREEN**: zero `feat:` commits in the v0.4 milestone range.
- [ ] Tag `v0.3.4` created (= milestone release).
- [ ] Milestone branch merged to `main`.
- [ ] All milestone branches deleted (local + remote).
- [ ] REQUIREMENTS.md + ROADMAP.md mark v0.4 COMPLETE.
- [ ] CHECKPOINT.json cleared (milestone complete).
---
## Coverage Targets (D-033) — v0.4
v0.4 does not lower the v0.3 coverage floor (93.3% on the lowest package). The modified packages must not decrease:
- `lexicon` (if coverage applies — it's a helper package): maintain or improve.
- `lexicon_meta` / `lexicon_meta_docs`: maintain 100% (test-only packages).
- `x/hub/types`: the new `cross_const_test.go` ADDS coverage; do not decrease.
- `x/council/types`: the new intent test ADDS coverage; do not decrease.
No new packages are created, so no new 80% floor is set.
## Task Count Summary — v0.4
| Phase | Tasks | Personas | Tag |
|-------|-------|----------|-----|
| P0 | (pre-execution, this plan) | lead-developer | v0.3.0 |
| P1 | 4 (3 code + 1 verify) | backend-engineer, lead-developer | v0.3.1 |
| P2 | 3 (2 deliver + 1 verify) | backend-engineer, lead-developer | v0.3.2 |
| P3 | 2 (1 deliver + 1 verify) | lead-developer | v0.3.3 |
| P4 | 3 (review + audit + ship) | lead-developer, ci-security-auditor | v0.3.4 |
| **Total** | **12 tasks** | | |
## Per-Phase REQ Coverage — v0.4
| Phase | REQs covered | Status after phase |
|-------|--------------|--------------------|
| P1 | REQ-029, REQ-030 | Complete (shipped as v0.3.1) |
| P2 | REQ-031 | Complete (shipped as v0.3.2) |
| P3 | REQ-032 | Complete (shipped as v0.3.3) |
| P4 | (all v0.4 REQs final accounting) | Complete (shipped as v0.3.4) |
## Cross-Phase Blockers (hard) — v0.4
None. P1, P2, P3 are independent vertical slices. P4 depends on P1+P2+P3 completion only (soft ordering for shippable slices).
## v0.4 Decisions Applied (D-047..D-053)
| Decision | Application |
|----------|-------------|
| D-047 | v0.4 NFR type, tags v0.3.x, zero feat: phases | Milestone summary |
| D-048 | REQ-029 lexicon shared helper | P1-01-01, P1-01-02 |
| D-049 | REQ-030 cross-const test (G-003 test-exempt import) | P1-02-01 |
| D-050 | REQ-031 = document only, no feat: enum additions | P2-01-01, P2-02-01 |
| D-051 | REQ-032 docs CI = .gitea/workflows build+artifact, no Pages | P3-01-01 |
| D-052 | Phase ordering P1→P2→P3→P4 | Phase ordering |
| D-053 | No IDEATE in v0.4 (no --ideate flag) | (no IDEATE stage run) |
---
## MVP/UX Check (REQ-MVP-UX-001) — v0.4 NFR Milestone
> Auto-generated at full autonomy per run.md §MVP/UX CHECK. The three required
> sections below name at least one user-facing surface, a happy path written
> BEFORE execute, and explicit UX acceptance criteria. v0.4 is an NFR milestone,
> so "user-facing surface" is developer-facing (the lexicon firewall, the
> cross-const test, the CI workflow) and the docs site build (end-user-facing
> via the published docs artifact).
### User-Facing Surface
1. **Lexicon firewall (developer-facing)**: `go test ./lexicon_meta... ./lexicon_meta_docs/...` — the green test output is the surface a developer sees on every `go test ./...` run. After REQ-029, both meta-tests consume `lexicon.SyntheticBannedStrings()`; a future banned-term addition updates both firewalls from one place.
2. **Cross-const drift test (developer-facing)**: `go test ./x/hub/types/...``TestConstsAreMissionLocked800And0` is the surface a developer sees if the hub↔bond mission-locked consts ever drift.
3. **Docs build CI (end-user-facing via artifact)**: `.gitea/workflows/docs-build.yml` produces a `site/` artifact on every push. A maintainer downloads the artifact to preview the docs site without running `mkdocs build` locally.
4. **ARCHITECTURE.md divergence section (developer-facing)**: a contributor reading `.ciagent/oy/ARCHITECTURE.md` finds the "Council Voice/Council Interface — Lifecycle Type Divergence Decisions (v0.4)" subsection explaining why `SignalKind` is 4-not-5 and why `Proposal`/`VoteOption` are absent.
### Happy Path
**Scenario: a contributor adds an 11th banned term to the lexicon firewall.**
1. The contributor edits `lexicon/lexicon.go` to add a new fragment pair to `fragments` (e.g., a new banned term).
2. `lexicon.BannedTerms()` now returns 11 strings.
3. `lexicon.SyntheticBannedStrings()` (REQ-029) is the single source — the contributor does NOT need to update two meta-test tables (the v0.3 drift risk).
4. `go test ./lexicon_meta... ./lexicon_meta_docs/...` — both meta-tests consume the helper; the `len(terms) == 10` assertion in BOTH meta-tests now fails (expecting 11), alerting the contributor to update the count assertion in both files.
5. The contributor updates the `len(terms) == 11` assertion in both meta-tests (the G-014 minimum — both already derive count from `BannedTerms()`).
6. Both meta-tests pass; the new banned term is now enforced in BOTH the `x/**/*.go` firewall AND the `docs/**/*.md` firewall from one source change.
7. The contributor pushes; `.gitea/workflows/docs-build.yml` (REQ-032) runs `go-test` (green) → `docs-build` (green, G-016 gating) → `site/` artifact uploaded.
**Scenario: a contributor accidentally changes `x/bond.CouponCapBps` from 800 to 900 without updating `x/hub.LendingCouponCapBps`.**
1. `go test ./x/hub/types/...``TestLendingCouponCapMatchesBondCap` (REQ-030) fails: `hub.LendingCouponCapBps (800) != bond.CouponCapBps (900)`.
2. `TestConstsAreMissionLocked800And0` (G-015) ALSO fails: `bond.CouponCapBps (900) != 800`.
3. The contributor sees two failures pointing at the same root cause; the mission-locked 8% cap is defended by the cross-const test. The v0.3 A-304 drift risk is closed.
### UX Acceptance Criteria
The v0.4 deliverable MUST meet these explicit criteria (verified in P4 audit):
1. **REQ-029**: `lexicon.SyntheticBannedStrings()` exists; both `lexicon_meta_test.go` and `lexicon_meta_docs/lexicon_meta_docs_test.go` consume it; the duplicated 10-string table is gone (grep for the old literal sentence fragments returns zero matches in meta-test files).
2. **REQ-030**: `x/hub/types/cross_const_test.go` exists; `TestLendingCouponCapMatchesBondCap`, `TestLendingCouponFloorMatchesBondFloor`, and `TestConstsAreMissionLocked800And0` all pass (G-015); no production `.go` file in `x/hub/types/` imports `x/bond/types` (G-003 production firewall intact).
3. **REQ-031**: ARCHITECTURE.md has the Council lifecycle divergence subsection; `TestSignalKindShapeIntentional` in `x/council/types/types_test.go` passes and documents the 4-signal rationale; `SignalKindCount` unchanged (still 4); no production `.go` files modified in P2.
4. **REQ-032**: `.gitea/workflows/docs-build.yml` parses; runs `go test ./...` then `mkdocs build` (G-016: `docs-build` needs `go-test`); `go.mod` unchanged.
5. **NFR purity gate**: zero `feat:` commits in the v0.4 milestone range (P4 audit enforces).
6. **No regression**: `go test ./...` green; v0.3 coverage floor (93.3%) not reduced on any modified package.
+44 -1
View File
@@ -61,7 +61,36 @@ OpenYield (OY) is a durable, anti-greed, jurisdiction-light financial layer —
- D-009: Rebased history to fix v1.0 → v0.1 in ---ci--- blocks
## Milestone
v0.3Bearers & Documentation (active milestone; feature type; tags run on the v0.2.x patch line)
v0.4Refinement (complete; NFR type; tags ran on the v0.3.x patch line)
### v0.4 Scope (Refinement-only NFR — v0.3 post-hoc forward-references)
v0.4 is a refinement-only NFR milestone: zero `feat:` phases. It lands the
durability fixes v0.3 flagged but did not block on, sourced from REVIEW.md,
AUDIT.md §193, and GRILL.md G-014. Live-runtime promotions of the v0.3 Bearers
skeletons are out of scope (deferred to v0.5+).
- **REQ-029** Lexicon firewall shared helper (`lexicon.SyntheticBannedStrings()`) — dedupe the synthetic self-test table between `lexicon_meta_test.go` and `lexicon_meta_docs_test.go`. Both meta-tests derive count + strings from the single `lexicon` package source, so a future banned-term addition updates both firewalls from one place. (GRILL G-014)
- **REQ-030** Cross-package const-equality test — `x/hub.LendingCouponCapBps == x/bond.CouponCapBps` (and Floor). Test-only import (G-003 exempt). Catches silent mission-lock drift between hub LOCAL consts and bond D-028 consts. (REVIEW.md P2 / A-304)
- **REQ-031** x/* lifecycle type shape-divergence review + alignment fixes — audit non-must-have lifecycle types across modules flagged by AUDIT §193; align where divergent without behavioral change. (AUDIT.md §193)
- **REQ-032** Docs build CI — Gitea Actions workflow running `go test ./...` (lexicon firewall) + `mkdocs build` on every push; upload `site/` as a CI artifact. Full Gitea Pages publishing deferred if no hosting target configured. (D-046)
### Milestone Type
NFR (all phases are refactor/test/quality/chore). Phase 0 → `v0.3.0`; execution phases `v0.3.1..v0.3.3`; final phase patch `v0.3.4` IS the milestone release. No separate minor tag. The final-phase audit enforces the NFR purity gate (zero `feat:` commits).
### Out of Scope (v0.4)
- Live-runtime promotions: Exit/DEX, OY-SAT/OY-QR hardware, Hub API B2B, bond matching, L2 IBC rollout, Anchors onboarding (all `feat:`, deferred to v0.5+)
- i18n / MkDocs internationalization (`feat:`, rejected by D-001 filter)
- Yield Token, Travel + 11 service categories (ROADMAP Phase 4)
- Cover Pool seniority mechanics (still deferred per PROJECT.md Q7)
### Prior Milestones
- v0.1 — OpenYield Foundation Init (COMPLETE; pre-MVP foundation skeleton; released as v0.0.9)
- v0.2 — The Mesh (COMPLETE; skeleton + tests; released as v0.1.5)
- v0.3 — Bearers & Documentation (COMPLETE; feature; released as v0.2.6)
## Prior Milestone
v0.3 — Bearers & Documentation (complete; feature type; tags ran on the v0.2.x patch line)
### v0.3 Scope (Bearers skeleton + Docs site — ROADMAP Phase 3 partial, plus a docs deliverable)
@@ -146,3 +175,17 @@ Auto-decided defaults logged per clarify workflow Step 4 (full autonomy → acce
### Ideation outcome (Phase 0 — IDEATE stage, autonomy=full)
IDEATE stage ratified 8 ideas (IDEATE-01..IDEATE-08) at full autonomy, mapped to REQ-010/REQ-022..REQ-028. Docs deliverable (IDEATE-01/02) is the user's `--ideate` request; Bearers ideas (IDEATE-03..08) are the ROADMAP Phase 3 subset. Three ideation tiers ran (mechanical, backend-enriched, cross-project); mechanical tier found no `lessons:`/`compound:` tags in v0.1/v0.2 history (convention unused) and v0.2 closed clean (9/9 REQs, 303 tests, ≥95.9% coverage). Defaults accepted per full autonomy; traceability recorded in `.ciagent/oy/REQUIREMENTS.md` (IDEATE Traceability section).
### v0.4 Clarification Decisions (Phase 0 — CLARIFY, autonomy=full)
Auto-decided defaults logged per clarify workflow Step 4 (full autonomy → accept defaults, log decisions). v0.4 is a refinement-only NFR milestone (no `--ideate` flag this run; scope pre-seeded from v0.3 forward-references). The D-001 refinement-only filter governs scope eligibility.
| ID | Decision | Rationale | Confidence | Alternatives |
|----|----------|-----------|------------|--------------|
| D-047 | **v0.4 milestone type = NFR** (all phases refactor/test/quality/chore). Zero `feat:` phases by construction. Tags run on the `v0.3.x` patch line: P0 → `v0.3.0`, P1..P3 → `v0.3.1..v0.3.3`, final phase P4 → `v0.3.4` (milestone release). No separate minor tag. | The candidate work set (REQ-029..REQ-032) is entirely refactor/test/quality/chore. Promoting any Bearers skeleton to live runtime would be `feat:` and is deferred to v0.5+. | 0.90 | [feature milestone promoting v0.3 skeletons to live runtime] |
| D-048 | **REQ-029 lexicon shared helper**: add `lexicon.SyntheticBannedStrings() []string` to the `lexicon` package; both `lexicon_meta_test.go` and `lexicon_meta_docs_test.go` consume it instead of duplicating their own synthetic self-test tables. Both already assert `len(terms) == 10` from `lexicon.BannedTerms()` (G-014 minimum met); the helper closes the drift risk fully. | GRILL G-014 binding fix. Single source of truth for synthetic banned strings; a future banned-term addition updates both firewalls from one place. Refactor+test (NFR-eligible). | 0.88 | [cross-reference comment only (G-014 minimum)] |
| D-049 | **REQ-030 cross-package const-equality test**: new test file `x/hub/types/cross_const_test.go` (package `types`) that imports `x/bond/types` (test-only, G-003 exempt) and asserts `hub.LendingCouponCapBps == bond.CouponCapBps` and `hub.LendingCouponFloorBps == bond.CouponFloorBps`. Test-only import does not violate G-003 (production-import firewall). | REVIEW.md P2 / A-304. Catches silent mission-lock drift between hub LOCAL consts and bond D-028 consts. Test (NFR-eligible). | 0.85 | [document manual-sync requirement in ARCHITECTURE.md only] |
| D-050 | **REQ-031 lifecycle type shape-divergence review scope = DOCUMENT only, no code shape changes**. AUDIT §193 P1-1 (council Proposal/VoteOption absent) and P1-2 (SignalKind 4 vs 5 sources) are `feat:`-class additions (new enum types / locked-const shape changes) and are REJECTED by the D-001 refinement-only filter. v0.4 REQ-031 ships an ARCHITECTURE.md section documenting the divergence decisions (P1-2 defensible per AUDIT code rationale; P1-1 deferred to v0.5+ governance runtime) + a test asserting the current `SignalKindCount==4` locked-const shape is intentional (regression guard, not a shape change). | Adding Proposal/VoteOption enums is `feat:`; changing SignalKind 4→5 is a locked-const change. Both are out-of-scope for an NFR milestone. Documentation + a regression-guard test are NFR-eligible. | 0.82 | [add Proposal/VoteOption enums (feat:, deferred to v0.5+)] |
| D-051 | **REQ-032 docs build CI = Gitea Actions workflow** at `.gitea/workflows/docs-build.yml` running `go test ./...` (lexicon firewall) + `mkdocs build` on every push; upload `site/` as a CI artifact. Full Gitea Pages publishing is deferred (no hosting target configured in v0.4). The workflow file itself is a `chore` (config, not feature). | D-046 forward-reference. `.github/workflows/` does not exist; Gitea Actions uses `.gitea/workflows/`. Build+artifact CI is `chore` (NFR-eligible); full Pages publish needs a hosting target (deferred). | 0.80 | [include full Gitea Pages publish (needs hosting target + secrets)] |
| D-052 | **Phase ordering** (provisional, planner finalizes): P1 lexicon hardening (REQ-029 + REQ-030 — same `lexicon`/test territory, vertical slice) → P2 lifecycle divergence documentation + regression guard (REQ-031) → P3 docs build CI (REQ-032) → P4 final review + audit + milestone ship. Each phase independently shippable; P1 lands the firewall durability fixes first (highest-severity regression risk). | P1 bundles the two lexicon/const firewall fixes (same territory); P2 is documentation+test; P3 is CI config. Vertical slices. | 0.80 | [different wave ordering] |
| D-053 | **No IDEATE stage in v0.4** (no `--ideate` flag this run). The NFR scope was pre-seeded from v0.3 forward-references and ratified at CLARIFY. If `--ideate` is passed on a later v0.4 run, the D-001 refinement-only filter applies. | run.md §IDEATE is conditional on `--ideate`. This invocation has no `--ideate`. | 1.00 | [run IDEATE anyway] |
+38 -8
View File
@@ -30,19 +30,49 @@
| ID | Requirement | Vision § | Priority | Status | Phase |
|----|-------------|----------|----------|--------|-------|
| REQ-010 | Exit layer (Layer 3) — DEX swaps, bridges, off-mesh services | §7 | Medium | Pending | v0.3/P4 |
| REQ-022 | Bearers expansion: OY-SAT + OY-QR bearer transports | §14 | Medium | Pending | v0.3/P4 |
| REQ-023 | Anchors — first institutional Partner tier | §13 | Medium | Pending | v0.3/P4 |
| REQ-024 | Hub API — B2B backbone: custody, lending primitive, compliance | §13 | Medium | Pending | v0.3/P5 |
| REQ-025 | Services — Care / SIM / Vault / Mail | §13 | Medium | Pending | v0.3/P5 |
| REQ-026 | Bond market depth — Growth Bonds + secondary market | §17 | Medium | Pending | v0.3/P5 |
| REQ-027 | README.md + docs site in docs/ for nomads and freeholders | (vision §8) | High | Pending | v0.3/P1-P3 |
| REQ-028 | Extend REQ-012 lexicon firewall to scan docs/ + README.md | §3 | High | Pending | v0.3/P1 |
| REQ-010 | Exit layer (Layer 3) — DEX swaps, bridges, off-mesh services | §7 | Medium | Skeleton | v0.3/P4 |
| REQ-022 | Bearers expansion: OY-SAT + OY-QR bearer transports | §14 | Medium | Skeleton | v0.3/P4 |
| REQ-023 | Anchors — first institutional Partner tier | §13 | Medium | Skeleton | v0.3/P4 |
| REQ-024 | Hub API — B2B backbone: custody, lending primitive, compliance | §13 | Medium | Skeleton | v0.3/P5 |
| REQ-025 | Services — Care / SIM / Vault / Mail | §13 | Medium | Skeleton | v0.3/P5 |
| REQ-026 | Bond market depth — Growth Bonds + secondary market | §17 | Medium | Skeleton | v0.3/P5 |
| REQ-027 | README.md + docs site in docs/ for nomads and freeholders | (vision §8) | High | Complete | v0.3/P1-P3 |
| REQ-028 | Extend REQ-012 lexicon firewall to scan docs/ + README.md | §3 | High | Complete | v0.3/P1 |
> REQ-022 through REQ-028 are NEW in v0.3 (ratified during Phase 0 IDEATE as
> IDEATE-01..IDEATE-07, then assigned final REQ-IDs). REQ-010 is promoted from
> v0.1 Skeleton to a fuller v0.3 skeleton.
## v0.4 Milestone Requirements (Refinement — NFR)
v0.4 is a refinement-only NFR milestone: zero `feat:` phases. Scope sourced
from v0.3 forward-references (REVIEW.md, AUDIT.md §193, GRILL.md G-014).
Live-runtime promotions are out of scope (deferred to v0.5+). The D-001
refinement-only filter applies to any IDEATE stage.
| ID | Requirement | Source | Class | Priority | Status | Phase |
|----|-------------|--------|-------|----------|--------|-------|
| REQ-029 | Lexicon firewall: shared `lexicon.SyntheticBannedStrings()` helper — dedupe the synthetic self-test table between `lexicon_meta_test.go` and `lexicon_meta_docs_test.go`; both meta-tests derive count + strings from the single source so a future banned-term addition updates both firewalls from one place | GRILL G-014 | refactor/test | High | Complete | v0.4/P1 |
| REQ-030 | Cross-package const-equality test: `x/hub.LendingCouponCapBps == x/bond.CouponCapBps` (and Floor) — test-only import (G-003 exempt), catches silent mission-lock drift between hub LOCAL consts and bond D-028 consts | REVIEW.md P2 / A-304 | test | High | Complete | v0.4/P1 |
| REQ-031 | x/* lifecycle type shape-divergence review + alignment fixes — audit non-must-have lifecycle types across modules flagged by AUDIT §193; align shapes where divergent (no behavioral change) | AUDIT.md §193 | refactor/quality | Medium | Complete | v0.4/P2 |
| REQ-032 | Docs build CI — Gitea Actions workflow that runs `go test ./...` (lexicon firewall) + `mkdocs build` on every push; upload the built `site/` as a CI artifact. Full Gitea Pages publishing deferred if no hosting target is configured (chore, not feat) | D-046 | chore/ci | Medium | Complete | v0.4/P3 |
> REQ-029..REQ-032 are NEW in v0.4. All are NFR classes (refactor/test/quality/
> chore) — zero `feat:` phases by construction. The final-phase audit enforces
> the NFR purity gate (zero `feat:` commits in the milestone).
## Milestone v0.4 Summary (Refinement — NFR) — COMPLETE
- 4 v0.4-scope REQs shipped as NFR (refactor/test/docs/chore): REQ-029, REQ-030, REQ-031, REQ-032
- Closes 3 real v0.3 forward-references: GRILL G-014 (lexicon drift), REVIEW P2/A-304 (const drift), AUDIT §193 (council divergence docs)
- Lands the D-046 docs-CI forward-reference (.gitea/workflows/docs-build.yml, build+artifact, no Pages publish per D-051)
- NFR purity gate GREEN: zero `feat:` commit subjects in the milestone (20 commits, all docs/refactor/test/chore/verify/decision/checkpoint/Merge)
- `go.mod` unchanged (G-006 — zero Go deps; Python deps isolated to CI docs-build job)
- G-003 production firewall intact (no production import of `x/bond/types` in `x/hub/types`; cross-const test is test-only)
- Coverage: x/hub/types 93.3% (v0.3 floor preserved), x/council/types 96.4% (improved); both above 80% target
- Tags: v0.3.0 (P0) -> v0.3.1 (P1) -> v0.3.2 (P2) -> v0.3.3 (P3) -> v0.3.4 (P4 = v0.4 milestone release)
- Tag-line note: v0.4 (NFR) ships on the v0.3.x patch line (config tag_base). The v0.3.4 milestone release IS the deliverable (D-008 — final phase patch IS the milestone release; no separate minor tag).
## IDEATE Traceability (Phase 0 — IDEATE stage, autonomy=full)
The IDEATE stage ran the three ideation tiers (mechanical, backend-enriched,
+165
View File
@@ -256,3 +256,168 @@ P1+ flags: 2 (x/council/types — Proposal/VoteOption lifecycle absent; VoiceSou
P2 nits: 1 (bearers ValidateGenesis no-op — correct per spec)
Overall: APPROVE WITH P1+ FLAGS (confidence 0.88) — milestone ship not blocked
```
---
## v0.3 Final Review (P6)
**Reviewer:** Multi-persona final review (correctness, testing, security, performance, maintainability, adversarial)
**Scope:** `v0.1.5..HEAD` — all v0.3 milestone phases (P0 pre-exec + P1 docs foundation + P2 nomads docs + P3 freeholders docs + P4 Bearers I + P5 Bearers II)
**Branch:** `oy/milestone/v0.3-bearers-docs` (reviewed on `oy/phase/06-final-review-ship`)
**Date:** 2026-08-17
**Diff stat:** 56 files changed, 6891 insertions(+), 169 deletions(-)
### Verification commands (all PASS)
```
go build ./... — PASS (zero errors)
go test ./... — PASS (all packages green)
go test -cover ./x/{bridge,exit,bearers,partner,hub,services,bond}/types/ — PASS (coverage below)
go test -run TestLexiconMeta ./lexicon_meta_docs/ . — PASS (docs firewall green)
go test -run TestLexiconMeta ./ — PASS (x/ firewall green, repo-root package)
```
### Coverage on new/extended packages
| Package | Coverage | Threshold (80%) | Verdict |
|---|---|---|---|
| x/bridge/types | 100.0% | ✓ | PASS |
| x/exit/types | 100.0% | ✓ | PASS |
| x/bearers/types | 100.0% | ✓ | PASS |
| x/partner/types | 100.0% | ✓ | PASS |
| x/hub/types | 93.3% | ✓ | PASS |
| x/services/types | 100.0% | ✓ | PASS |
| x/bond/types | 95.1% | ✓ | PASS |
All packages exceed the ≥80% coverage requirement. The two sub-100% packages (hub 93.3%, bond 95.1%) have gaps only in defensive genesis error-branches (e.g., `validateComplianceServices` 87.5%, `Clamp`/`ClampLendingCoupon` 80% — the upper-bound and floor branches each exercised by ≥1 test but not every permutation). No must-have is uncovered; G-012 boundary cases (`currentBps==cap`, `currentBps>cap`, underflow guard) are all explicitly tested (`TestClampGrowthCurrentAtCapReturnsZero`, `TestClampGrowthCurrentAboveCapReturnsZero`, `TestClampGrowthInvariantPostGrowthLeCap`).
### Per-axis verdicts
#### 1. Correctness (backend-engineer) — PASS (0.92)
Locked-const invariants all enforced and tested:
- `BridgeStatusCount = 4` (x/bridge/types/types.go:18) — `AllBridgeStatuses()` returns 4 in REQ-015 order. ✓
- `ExitStatusCount = 5` (x/exit/types/types.go:18) — `AllExitStatuses()` returns 5 in vision §7 order. ✓
- `HubServiceCount = 3` (x/hub/types/types.go:42) — `AllHubServices()` returns 3 (Custody/LendingPrimitive/Compliance). ✓
- `ServiceKindCount = 4` (x/services/types/types.go:37) — locked count asserted. ✓
- `OrderSideCount = 2` (x/bond/types/types.go:171) — Buy/Sell. ✓
- `OrderStatusCount = 3` (x/bond/types/types.go:174) — Open/Filled/Cancelled. ✓
- `PartnerTierCount = 4` (x/partner/types/types.go:18) — regression intact. ✓
- `BondStatusCount = 5` (x/bond/types/types.go:31) — regression intact. ✓
- `CouponCapBps = 800` / `CouponFloorBps = 0` (x/bond/types/types.go:21,26) — D-028 LOCKED, regression firewall in types_test.go asserts both values. ✓
**G-012 (ClampGrowth underflow guard) — CORRECT.** The guard at x/bond/types/types.go:239 (`if currentBps >= CouponCapBps { return 0 }`) runs BEFORE the `CouponCapBps - currentBps` subtraction (line 243), so the uint32 underflow path is unreachable. Five boundary/invariant tests cover: currentBps==0 (full room), currentBps==cap (return 0), currentBps>cap (return 0, NOT wrapped huge), growth>room (clamp to room), growth<room (unchanged), and a meta-assert `current + ClampGrowth(current, growth) <= max(current, cap)` across a fuzz table.
The hub `LendingCouponCapBps`/`LendingCouponFloorBps` LOCAL consts (A-304) mirror x/bond's LOCKED values (800/0) without importing x/bond (G-003 preserved). Genesis-side clamp enforcement present in both `validateLendingPrimitives` (hub) and `ValidateBonds`/`ValidateGrowthBonds` (bond).
#### 2. Testing (backend-engineer) — PASS (0.90)
All new packages ≥93.3% (above 80% threshold). Per-package lexicon assertions present in every new package's types_test.go (TestLexiconNoBannedTermsIn<Pkg>Package + TestLexiconNoBannedTermsIn<Pkg>TestFile) — confirmed in x/bridge, x/exit, x/bearers, x/partner, x/hub, x/services, x/bond. G-012 boundary cases (currentBps==cap, currentBps>cap) explicitly tested. Locked-const regression tests present (Test<Const>LockedConst pattern) for every locked const enumerated above.
#### 3. Security (security) — PASS (0.93)
Both lexicon firewalls green:
- x/ firewall (`lexicon_meta_test.go`, package `lexicon_meta`): `go test -run TestLexiconMeta ./` PASS.
- docs/ firewall (`lexicon_meta_docs/lexicon_meta_docs_test.go`, package `lexicon_meta_docs`): `go test -run TestLexiconMeta ./lexicon_meta_docs/` PASS. Scans README.md + docs/**/*.md.
Adversarial verification: confirmed `lexicon.FindBannedTerm` catches all 10 banned terms (bank, deposit, interest, yield, currency, dollar, euro, account, savings, depositor) via direct injection test. The docs firewall self-test table (G-009 for docs), walk-coverage test (G-013), and self-test drift assertion (G-014) all present and passing — the firewall provably CATCHES banned-term regressions rather than silently scanning nothing.
**G-003 (by-ID-string, no struct imports between x/*) — INTACT.** `grep -rn "oy/openyield/x/"` across all new package non-test .go files returns ZERO struct imports. The only cross-package import in a test file is `x/bearers/types/types_test.go` importing `x/processing/types` (a test-only import for a stub reference; G-003 governs production struct imports, not test imports). All cross-module references in production types use ID-string fields (issuer-stand-id, reach-id, holder-reach-id, custody-provider-id, anchor-id, bond-id, operator-partner-id, etc.) with explicit G-003 doc-comments.
#### 4. Performance (backend-engineer) — PASS (0.95)
`go.mod` UNCHANGED since v0.1.5 (`git diff v0.1.5..HEAD -- go.mod go.sum` is empty) — zero external deps preserved (G-006). The mkdocs build deps (mkdocs + mkdocs-material) are Python-only and documented as non-Go (mkdocs.yml header comment). Skeleton stubs use O(1) maps for registry lookups (x/partner Keeper); no N+1 patterns in the stub code. The genesis validators iterate slices once (O(n) per set) with map-backed uniqueness checks — appropriate for skeleton scale.
#### 5. Maintainability (lead-developer) — PASS (0.91)
**Pattern consistency:** All new modules follow the v0.1/v0.2 file structure (types.go + types_test.go, genesis.go where genesis validation exists). The Params/GenesisState/DefaultGenesisState/ValidateGenesis pattern is uniform across x/bridge, x/exit, x/bearers, x/partner, x/hub, x/services, x/bond. G-008 split (data-engineer's genesis.go schema helpers composed by ValidateGenesis in types.go) is present in x/bond and x/hub. The new modules use the same ModuleName/StoreKey/RouterKey/QuerierRoute const block and the same JSON/YAML struct-tag convention as v0.1/v0.2 modules.
**Docs cross-reference (G-011):** `mkdocs.yml` nav lists ALL 26 pages (1 Home + 8 Nomads + 8 Freeholders + 7 Shared + 2 Reference = 26), matching the 26 .md files under docs/. `docs/reference/components.md` cross-references the new modules (10 mentions of x/* packages). All docs pages are lexicon-clean (firewall green).
**.ciagent/oy/* updates:** PROJECT, ROADMAP, REQUIREMENTS, ARCHITECTURE, RESEARCH, PERSONAS, PLANS, GRILL all updated to reflect v0.3 scope (Bearers & Documentation, REQ-024..REQ-028, D-037..D-046, A-304..A-313, G-011..G-014).
#### 6. Adversarial (adversarial) — PASS (0.88)
Adversarial probes attempted and their outcomes:
1. **Banned term slipped into docs** — the firewall self-test table (`TestLexiconMetaDocsSelfTestTable`, G-009 for docs) injects synthetic banned-term strings and asserts FindBannedTerm detects each; the walk-coverage test (`TestLexiconMetaDocsWalkCoverage`, G-013) injects a real .md fixture under docs/.lexicon_fixture/ and asserts the walk FINDS it. Catches the "silently scans nothing and reports green" failure mode. ✓
2. **Locked-const regression** — every locked const has a `Test<Const>LockedConst` regression test asserting the exact value AND the All<Enum>() entry count/names. A regression (e.g., BridgeStatusCount→5) fails the test. ✓
3. **Struct import breaks G-003** — no production .go file in the new packages imports another x/* package; verified by grep. ✓
4. **ClampGrowth underflow** — the guard returns 0 BEFORE the subtraction; the underflow path is unreachable; tested with currentBps>cap (e.g., 801) asserting return 0 (NOT 4294967295). ✓
5. **Hub A-304 drift from x/bond D-028** — the LOCAL consts are documented as cross-referenced (comment "also 800") and a regression test asserts LendingCouponCapBps==800. A future x/bond cap change without a matching hub change is flagged by the cross-doc comment (not a test — appropriate since they are LOCAL to hub). Note P2 below.
### P0 fixes auto-applied
**0.** No P0 (critical) issues found. The milestone ships clean.
### P1+ flags (post-hoc review — do NOT block ship)
**1.** [P2 nit, maintainability] x/hub `LendingCouponCapBps`/`LendingCouponFloorBps` (A-304) are LOCAL consts cross-documented to x/bond's D-028 consts (both 800/0) but there is no automated cross-check that they stay in lockstep. If a future mission-locked change to x/bond.CouponCapBps does not update the hub LOCAL const, the two packages silently drift. The cross-doc comment in types.go:46-50 flags this for human review, but a shared-const test (e.g., asserting `LendingCouponCapBps == x/bond.CouponCapBps` — though that would require a test-only import, acceptable per G-003 test exemption) would be more robust. Recommend post-hoc: add a cross-package const-equality test OR document the manual-sync requirement in ARCHITECTURE.md. Not a ship blocker — both are currently 800/0.
**2.** [P2 nit, testing] x/hub coverage 93.3% and x/bond coverage 95.1% leave defensive error-branches in `ClampLendingCoupon` (80%), `Clamp` (80%), `validateComplianceServices` (87.5%), `ValidateGrowthBonds` (85.7%) partially exercised. All must-have paths are tested; the uncovered lines are error-return branches for malformed genesis inputs. Recommend post-hoc: add 2-3 negative-case genesis tests per package to close the gaps to 100%. Not a ship blocker (both above the 80% threshold).
**3.** [P2 nit, docs] `docs/reference/architecture.md` has 0 cross-references to x/* packages (vs `docs/reference/components.md` which has 10). The architecture page is conceptual; the components page is the cross-ref hub. Acceptable as-is, but post-hoc adding 1-2 module cross-refs to architecture.md would improve discoverability. Not a ship blocker.
### Overall verdict
**SHIP.**
All verification commands pass. All locked-const invariants enforced and tested. Both lexicon firewalls green (x/ and docs/). G-003 (by-ID-string, no struct imports) intact across all new packages. G-012 (ClampGrowth underflow guard) correctly implemented with explicit boundary tests. Zero external deps (go.mod unchanged). Coverage ≥93.3% on all new/extended packages (above 80% threshold). mkdocs.yml nav complete (26/26 pages, G-011). No P0 issues. Three P2 nits flagged for post-hoc review (none blocking).
**P0 fixes auto-applied: 0**
**P1+ findings: 0 P1, 3 P2 (all nits, post-hoc, non-blocking)**
**Confidence in overall verdict: 0.91**
---
# Review: OpenYield (oy) — v0.4 (Refinement — NFR) Final Phase
> **Reviewer**: CIAgent multi-persona code review (lead-developer + backend-engineer lenses)
> **Date**: 2026-08-17
> **Target**: All v0.4 milestone commits (main..oy/milestone/v0.4-refinement) across P1..P3
> **Milestone**: v0.4 — Refinement (NFR)
> **Autonomy**: full
## Scope Reviewed
The v0.4 milestone ships 4 REQs across 3 execution phases (P1..P3) + phase 0 (pre-execution). 20 commits, 15 files changed (+764 / -157). The review covers the execution-phase deliverables:
- **P1** (v0.3.1): REQ-029 lexicon shared helper, REQ-030 cross-const test
- **P2** (v0.3.2): REQ-031 lifecycle divergence docs + regression guard
- **P3** (v0.3.3): REQ-032 docs build CI
## Adversarial Probes
1. **Does the shared helper actually dedupe?** Probe: `grep -rn 'open a.*here\|make a.*now\|compounding.*rate' lexicon_meta_test.go lexicon_meta_docs/` — returns ZERO matches (the old duplicated table is gone; both meta-tests now call `lexicon.SyntheticBannedStrings()`). Verified at `lexicon_meta_test.go:93` and `lexicon_meta_docs/lexicon_meta_docs_test.go:155` (both consume the helper). ✓
2. **Does the cross-const test fail closed on drift?** Probe: the test imports `bondtypes "github.com/oy/openyield/x/bond/types"` in `x/hub/types/cross_const_test.go:28` and asserts `LendingCouponCapBps != bondtypes.CouponCapBps` would fail the test. The absolute-value test `TestConstsAreMissionLocked800And0` (G-015) catches paired drift (both consts → 900). Both paths verified by reading the test. ✓
3. **Does the regression guard lock the 4-signal shape?** Probe: `TestSignalKindShapeIntentional` at `x/council/types/types_test.go` asserts `SignalKindCount == 4` with a `t.Fatalf` (not `t.Errorf`) and checks `AllSignalKinds()` returns `[SignalStash, SignalStanding, SignalVouch, SignalCapital]` in order. The doc comment includes the AUDIT §193 P1-2 rationale. Changing `SignalKindCount` to 5 fails this test AND `TestSignalKindCountLockedConst`. ✓
4. **Does the CI workflow YAML parse and enforce G-016?** Probe: `python3 -c "import yaml; doc=yaml.safe_load(open('.gitea/workflows/docs-build.yml')); assert doc['jobs']['docs-build']['needs'] == 'go-test'"` — passes. The `needs: go-test` line is present at `.gitea/workflows/docs-build.yml` in the `docs-build` job. ✓
5. **Is `go.mod` really unchanged across the whole milestone?** Probe: `git diff main..HEAD -- go.mod` — empty. The CI workflow's Python deps are in a separate job; the lexicon helper adds no Go deps; the cross-const test adds no Go deps (test-only import of an internal package). G-006 intact. ✓
6. **NFR purity gate — zero `feat:` commit SUBJECTS?** Probe: `git log --format="%s" main..HEAD | grep -E "^feat:"` — exit 1 (no matches). All 20 subjects are `docs(`, `refactor(`, `test(`, `chore(`, `verify(`, `decision(`, `checkpoint(`, or `Merge`. NOTE: `git log --grep "^feat:"` matches commit BODIES too (the v0.4 verify commits mention "feat:" in prose like "zero feat: commits in P2"); the correct gate uses subject-only check via `--format="%s"`. This is a documented refinement for the audit. ✓
7. **Does G-003 hold — no production cross-module struct imports added?** Probe: `grep -rn "x/bond/types" x/hub/types/ --include="*.go" | grep -v "_test.go"` — zero matches. The only import of `x/bond/types` in `x/hub/types/` is in `cross_const_test.go` (a `_test.go` file, G-003 test-exempt). Production firewall intact. ✓
## P0 fixes auto-applied
**0.** No P0 (critical) issues found. The milestone ships clean.
## P1+ flags (post-hoc review — do NOT block ship)
**1.** [P2 nit, maintainability] The `mkdocs build` local run produced 2 warnings about README.md links (`docs/index.md` links to `../README.md`, `docs/shared/vision.md` links to `../../README.md`). These are pre-existing v0.3 docs links (not introduced by v0.4); the warnings are non-blocking (mkdocs builds successfully). Recommend post-hoc: either configure `mkdocs.yml` to include README.md in the nav, or fix the relative links. Not a v0.4 ship blocker (the docs site builds; v0.4's REQ-032 is the CI workflow, not the docs content).
**2.** [P2 nit, CI] The `docs-build.yml` workflow uses `actions/upload-artifact@v4` with `retention-days: 14`. Gitea Actions may have a different artifact retention default; the explicit `retention-days: 14` is defensive. If Gitea Actions does not support v4 of the upload-artifact action, the workflow would fail at the upload step (the `mkdocs build` itself would have succeeded). Recommend post-hoc: verify Gitea Actions supports `actions/upload-artifact@v4`; if not, downgrade to v3 or use the Gitea-native artifact upload. Not a ship blocker (the build itself is the higher-priority check; the artifact upload is a bonus).
**3.** [P2 nit, audit-gate precision] The NFR purity gate (`git log --grep "^feat:"`) over-matches commit bodies. The correct gate is subject-only (`git log --format="%s" | grep -E "^feat:"`). The P4 audit uses the subject-only check. Recommend post-hoc: document the subject-only gate in the next milestone's PLAN so future audits do not over-count. Not a ship blocker (the subject-only gate is green).
## Overall verdict
**SHIP.**
All four REQs (REQ-029..REQ-032) delivered. The three real v0.3 forward-references (G-014 lexicon drift, A-304 const drift, AUDIT §193 council divergence) are closed; the D-046 docs-CI forward-reference is landed. `go test ./...` green across all 26 packages. `go.mod` unchanged (G-006). G-003 production firewall intact. NFR purity gate GREEN (zero `feat:` commit subjects). No P0 issues. Three P2 nits flagged for post-hoc review (none blocking).
**P0 fixes auto-applied: 0**
**P1+ findings: 0 P1, 3 P2 (all nits, post-hoc, non-blocking)**
**Confidence in overall verdict: 0.90**
+51 -1
View File
@@ -23,9 +23,18 @@
- [x] P5: Final Review + Ship → v0.1.5 (milestone release)
- Status: COMPLETE (skeleton + tests layer; released as v0.1.5)
## Milestone v0.3 — Bearers & Documentation (ACTIVE; feature type; tags v0.2.x)
## Milestone v0.3 — Bearers & Documentation (COMPLETE; feature type; tags v0.2.x)
Target: Bearers skeleton (ROADMAP Phase 3 subset) + docs site for nomads and freeholders.
- [x] P0: Pre-Execution (spec/clarify/research/ideate/plan/grill) → v0.2.0
- [x] P1: Docs foundation + REQ-012 firewall extension → v0.2.1
- [x] P2: Nomads docs → v0.2.2
- [x] P3: Freeholders docs + reference → v0.2.3 (REQ-027 complete)
- [x] P4: Bearers skeleton I (x/exit, x/bridge, x/bearers, x/partner) → v0.2.4
- [x] P5: Bearers skeleton II (x/hub, x/services, x/bond) → v0.2.5
- [x] P6: Final Review + Audit + Ship → v0.2.6 (milestone release)
- Status: COMPLETE — Bearers skeleton (7 x/* packages) + docs site (26 pages) shipped
> v0.3 bundles two work-streams under one feature milestone: (A) Bearers
> skeleton+tests (D-020 pattern) and (B) README.md + MkDocs Material docs site
> organized by audience, with the REQ-012 lexicon firewall extended to docs.
@@ -60,6 +69,47 @@ Target: Bearers skeleton (ROADMAP Phase 3 subset) + docs site for nomads and fre
> (= the v0.3 milestone release, per D-008 — final phase patch IS the
> milestone release; no separate minor tag).
## Milestone v0.4 — Refinement (COMPLETE; NFR type; tags v0.3.x)
Target: Close the v0.3 post-hoc forward-references (lexicon firewall drift,
hub↔bond const drift, council lifecycle type divergence) + land the deferred
docs build CI. Refinement-only NFR milestone: zero `feat:` phases.
- [x] P0: Pre-Execution (spec/clarify/research/plan/grill/mvp-ux) → v0.3.0
- [x] P1: Lexicon + const hardening (REQ-029, REQ-030) → v0.3.1
- [x] P2: Lifecycle divergence docs + regression guard (REQ-031) → v0.3.2
- [x] P3: Docs build CI (REQ-032) → v0.3.3
- [x] P4: Final Review + Audit + Ship → v0.3.4 (milestone release)
- Status: COMPLETE — 4 NFR REQs shipped; NFR purity gate GREEN (zero feat: commits); go.mod unchanged
> v0.4 closes three real v0.3 forward-references (GRILL G-014 lexicon helper,
> REVIEW P2/A-304 cross-const test, AUDIT §193 council divergence docs) and
> lands the D-046 docs-CI forward-reference. Live-runtime promotions of the
> v0.3 Bearers skeletons are deferred to v0.5+ (feat:-class, rejected by the
> D-001 refinement-only filter).
| Phase | Type | Scope | Patch |
|---|---|---|---|
| P0 | docs | Pre-Execution (spec/clarify/research/plan/grill/mvp-ux) | v0.3.0 |
| P1 | refactor+test | Lexicon shared helper (REQ-029) + cross-const test (REQ-030) | v0.3.1 |
| P2 | docs+test | Council lifecycle divergence docs (REQ-031) + regression guard | v0.3.2 |
| P3 | chore+ci | Docs build CI workflow (REQ-032) | v0.3.3 |
| P4 | final | REVIEW + AUDIT + milestone SHIP | v0.3.4 (milestone release) |
### v0.4 Component mapping
| Component | Deliverable | v0.4 Change | Phase |
|---|---|---|---|
| Lexicon firewall | Shared `SyntheticBannedStrings()` helper | `lexicon/lexicon.go` + both meta-tests refactored | v0.4/P1 |
| Mission-locked const firewall | Cross-package const-equality test | `x/hub/types/cross_const_test.go` (NEW) | v0.4/P1 |
| Council Voice/Council interface | Lifecycle divergence documentation + regression guard | ARCHITECTURE.md section + `x/council/types/types_test.go` intent test | v0.4/P2 |
| Docs CI | Gitea Actions workflow (build + artifact) | `.gitea/workflows/docs-build.yml` (NEW) | v0.4/P3 |
> **Tag-line note (G-010 continuation)**: v0.4 (NFR) ships on the `v0.3.x`
> patch line (config.json `tag_base: v0.3.x`): P0 -> `v0.3.0`, P1..P3 ->
> `v0.3.1..v0.3.3`, P4 -> `v0.3.4` (= the v0.4 milestone release, per D-008 —
> final phase patch IS the milestone release; no separate minor tag).
## Phase 3 — The Bearers (Year 3) — v0.3 PARTIAL SKELETON
**Target**: $10B annual volume → fee auto-declines to 0.07%
+55
View File
@@ -0,0 +1,55 @@
# OpenYield docs build CI (REQ-032, D-046 forward-reference, D-051, G-016).
#
# Runs the lexicon firewall (go test ./...) AND builds the MkDocs Material docs
# site on every push. The docs-build job DEPENDS on go-test (G-016 binding:
# firewall-gates-docs-build — a lexicon violation blocks the docs build so no
# false-green docs artifact is produced from a repo with a firewall failure).
#
# Scope (chore, not feat: per D-001 refinement-only filter):
# - go-test job: setup Go 1.22, run `go test ./...` (lexicon firewall + all
# x/* tests + the v0.4 cross-const test). Zero external Go deps (G-006).
# - docs-build job: setup Python, pip install mkdocs + mkdocs-material
# (build-only Python deps, ISOLATED to this job — go.mod is NOT modified),
# run `mkdocs build` (produces site/), upload site/ as a CI artifact.
#
# Out of scope (deferred per D-051): full Gitea Pages publishing. v0.4 ships
# build + artifact only; a hosting target is not configured.
#
# Triggers: on push (all branches) so the firewall + docs build are checked
# on every change, not just on main.
name: docs-build
on:
push:
jobs:
go-test:
name: go test ./... (lexicon firewall + all x/* tests)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: go test ./...
run: go test ./...
docs-build:
name: mkdocs build (docs site artifact)
runs-on: ubuntu-latest
needs: go-test # G-016: firewall-gates-docs-build (no false-green docs build)
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: install mkdocs + mkdocs-material
run: pip install mkdocs mkdocs-material
- name: mkdocs build
run: mkdocs build
- name: upload site/ artifact
uses: actions/upload-artifact@v4
with:
name: docs-site
path: site/
retention-days: 14
+2
View File
@@ -2,3 +2,5 @@
.env.secrets
.env.*
.ciagent/.env.secrets
# MkDocs build output (REQ-032 CI produces site/ as an artifact; never commit it)
site/
+38
View File
@@ -85,3 +85,41 @@ func FindBannedTerm(s string) (string, bool) {
func ContainsBannedTerm(s string) (string, bool) {
return FindBannedTerm(s)
}
// SyntheticBannedStrings returns one synthetic string per banned term, each
// embedding exactly one banned term in a plausible sentence context. This
// is the single source of truth (REQ-029, GRILL G-014) for the synthetic
// self-test table consumed by BOTH project-wide meta-tests:
//
// lexicon_meta_test.go :: TestLexiconMetaSelfTestTable (package lexicon_meta, scans x/**/*.go)
// lexicon_meta_docs_test.go :: TestLexiconMetaDocsSelfTestTable (package lexicon_meta_docs, scans README.md + docs/**/*.md)
//
// Before REQ-029, both meta-tests DUPLICATED their own 10-string synthetic
// table (byte-identical), creating a drift risk: a future banned-term
// addition updating one table but not the other would silently drop coverage
// in the unmaintained firewall. SyntheticBannedStrings() eliminates the
// duplication — both meta-tests now consume this helper, so a future addition
// updates both firewalls from one place. The strings are built from
// BannedTerms() (already fragment-assembled), so this package's own source
// stays lexicon-clean (the firewall's own code is allowed to name the terms
// it bans, but only via the fragment-assembly bootstrapping pattern).
//
// The returned slice is indexed positionally against BannedTerms(): the i-th
// synthetic string embeds the i-th banned term. Both meta-tests assert
// len(SyntheticBannedStrings()) == len(BannedTerms()) and that each string
// triggers FindBannedTerm with the matching term.
func SyntheticBannedStrings() []string {
terms := BannedTerms()
return []string{
"open a " + terms[0] + " here", // bank
"make a " + terms[1] + " now", // deposit
"compounding " + terms[2] + " rate", // interest
"the " + terms[3] + " is 5pct", // yield
"foreign " + terms[4] + " pair", // currency
"price in " + terms[5], // dollar
"price in " + terms[6], // euro
"freeze the " + terms[7], // account
"move to " + terms[8] + " now", // savings
"the " + terms[9] + " lost money", // depositor
}
}
+11 -31
View File
@@ -128,22 +128,14 @@ func TestLexiconMetaDocsNoBannedTermsInDocs(t *testing.T) {
// breaks, this test fails before the firewall silently passes a real
// violation in a docs page.
//
// G-014 self-test drift: this table is the docs mirror of the
// TestLexiconMetaSelfTestTable in lexicon_meta_test.go (package lexicon_meta).
// Both reuse lexicon.BannedTerms() as the single source for the 10 terms, so
// a future addition updates both firewalls from one place. The synthetic
// strings are assembled from lexicon.BannedTerms() fragments so this file
// does not contain any banned term as a literal substring (it would otherwise
// trip its own scan; the meta-test file is also excluded from its own scan,
// but the self-test keeps the source clean for readability/searchability).
//
// CROSS-REFERENCE: keep this table aligned with
//
// lexicon_meta_test.go :: TestLexiconMetaSelfTestTable
//
// Any change to the synthetic-string construction must be mirrored in both
// files (or, preferably, add a shared helper in the lexicon package — see
// G-014 minimum-viable: cross-reference comment + shared BannedTerms()).
// REQ-029 (GRILL G-014): the synthetic strings are sourced from
// lexicon.SyntheticBannedStrings(), the single source of truth shared with
// lexicon_meta_test.go :: TestLexiconMetaSelfTestTable. Before REQ-029, this
// file DUPLICATED its own 10-string table (byte-identical to the x/ meta-
// test), creating a drift risk; the shared helper closes it. This file no
// longer builds its own synthetic table — both meta-tests consume the same
// helper, so a future banned-term addition updates both firewalls from one
// place.
func TestLexiconMetaDocsSelfTestTable(t *testing.T) {
terms := lexicon.BannedTerms()
// The spec lists 10 banned terms (plan docs say "9", counting dollar/euro
@@ -152,22 +144,10 @@ func TestLexiconMetaDocsSelfTestTable(t *testing.T) {
if len(terms) != 10 {
t.Fatalf("BannedTerms() len = %d, want 10", len(terms))
}
// Each synthetic string embeds exactly one banned term in a plausible
// sentence context. Each must be detected.
synthetic := []string{
"open a " + terms[0] + " here", // bank
"make a " + terms[1] + " now", // deposit
"compounding " + terms[2] + " rate", // interest
"the " + terms[3] + " is 5pct", // yield
"foreign " + terms[4] + " pair", // currency
"price in " + terms[5], // dollar
"price in " + terms[6], // euro
"freeze the " + terms[7], // account
"move to " + terms[8] + " now", // savings
"the " + terms[9] + " lost money", // depositor
}
// REQ-029: consume the shared synthetic-string helper (G-014 single source).
synthetic := lexicon.SyntheticBannedStrings()
if len(synthetic) != len(terms) {
t.Fatalf("synthetic table len = %d, want %d", len(synthetic), len(terms))
t.Fatalf("SyntheticBannedStrings() len = %d, want %d (must match BannedTerms())", len(synthetic), len(terms))
}
for i, s := range synthetic {
found, ok := lexicon.FindBannedTerm(s)
+9 -19
View File
@@ -76,10 +76,12 @@ func TestLexiconMetaNoBannedTermsInX(t *testing.T) {
// firewall's detection logic is durably verified — if detection ever breaks,
// this test fails before the firewall silently passes a real violation.
//
// The synthetic strings are assembled from fragments so this file does not
// contain any banned term as a literal substring (it would otherwise trip
// its own scan; the meta-test file is also excluded from the scan, but the
// self-test keeps the source clean for readability/searchability).
// REQ-029 (GRILL G-014): the synthetic strings are sourced from
// lexicon.SyntheticBannedStrings(), the single source of truth shared with
// lexicon_meta_docs_test.go :: TestLexiconMetaDocsSelfTestTable. Before
// REQ-029, both meta-tests DUPLICATED their own 10-string table, creating a
// drift risk; the shared helper closes it. This file no longer builds its
// own synthetic table.
func TestLexiconMetaSelfTestTable(t *testing.T) {
terms := lexicon.BannedTerms()
// The spec lists 10 banned terms (plan docs say "9", counting dollar/euro
@@ -88,22 +90,10 @@ func TestLexiconMetaSelfTestTable(t *testing.T) {
if len(terms) != 10 {
t.Fatalf("BannedTerms() len = %d, want 10", len(terms))
}
// Each synthetic string embeds exactly one banned term in a plausible
// sentence context. Each must be detected.
synthetic := []string{
"open a " + terms[0] + " here", // bank
"make a " + terms[1] + " now", // deposit
"compounding " + terms[2] + " rate", // interest
"the " + terms[3] + " is 5pct", // yield
"foreign " + terms[4] + " pair", // currency
"price in " + terms[5], // dollar
"price in " + terms[6], // euro
"freeze the " + terms[7], // account
"move to " + terms[8] + " now", // savings
"the " + terms[9] + " lost money", // depositor
}
// REQ-029: consume the shared synthetic-string helper (G-014 single source).
synthetic := lexicon.SyntheticBannedStrings()
if len(synthetic) != len(terms) {
t.Fatalf("synthetic table len = %d, want %d", len(synthetic), len(terms))
t.Fatalf("SyntheticBannedStrings() len = %d, want %d (must match BannedTerms())", len(synthetic), len(terms))
}
for i, s := range synthetic {
found, ok := lexicon.FindBannedTerm(s)
+52
View File
@@ -109,6 +109,58 @@ func TestSignalKindCountLockedConst(t *testing.T) {
}
}
// TestSignalKindShapeIntentional (REQ-031, AUDIT §193 P1-2) is a regression
// GUARD that documents and locks the 4-source SignalKind shape. It is NOT a
// shape change — the existing TestSignalKindCountLockedConst already locks
// the count. This test adds the INTENT documentation so a future agent who
// changes SignalKindCount from 4 to 5 (e.g., to "restore" the spec's 5-source
// VoiceSource list) must also update this test, surfacing the AUDIT rationale
// for review.
//
// AUDIT §193 P1-2 rationale (why SignalKind is 4 sources, NOT the spec's 5):
//
// The v0.2 P3-01-01 deliverable specified VoiceSource with 5 sources
// (Stash/Standing/Vouch/Freeholder/Guild). The implementation uses
// SignalKind with 4 sources (Stash/Standing/Vouch/Capital). The 4-source
// shape is a defensible design refinement:
// - Freeholder is an ELIGIBILITY property (upstream in x/standing), not
// a voice signal. A Freeholder-eligible Reach is a precondition for
// voting, not a signal that feeds a vote's weight.
// - Guild is a COUNCIL TIER (one of the three councils is the Guild
// Council), not a voice signal. Including Guild as a signal kind
// would conflate the council tier with the signal source.
// - Capital is committed-capital (vision §9.1, one of the four
// Freeholder signals per REQ-005), which the spec's VoiceSource list
// omitted. Adding Capital corrects the spec to match vision §9.1's
// four-signal definition (REQ-005: "Four Freeholder signals locked").
//
// The 4-source shape matches REQ-005 exactly. The spec deliverable text
// was wrong, not the implementation. v0.4 (D-050) DOCUMENTS this and
// locks the 4-source shape; changing it to 5 is a locked-const change
// rejected by the D-001 refinement-only filter and deferred to a future
// milestone that re-litigates REQ-005's signal definition.
//
// See .ciagent/oy/ARCHITECTURE.md §"Council Voice/Council Interface —
// Lifecycle Type Divergence Decisions (v0.4, REQ-031)" for the full rationale.
func TestSignalKindShapeIntentional(t *testing.T) {
// LOCKED: 4 sources. Changing this to 5 requires updating this test's
// intent block AND re-litigating REQ-005's four-signal definition.
const expectedSignalCount = 4
if types.SignalKindCount != expectedSignalCount {
t.Fatalf("SignalKindCount = %d, want %d (REQ-031 intent guard: the 4-source shape is intentional per AUDIT §193 P1-2; see ARCHITECTURE.md v0.4 divergence section before changing this)", types.SignalKindCount, expectedSignalCount)
}
want := []types.SignalKind{types.SignalStash, types.SignalStanding, types.SignalVouch, types.SignalCapital}
all := types.AllSignalKinds()
if len(all) != len(want) {
t.Fatalf("AllSignalKinds() len = %d, want %d", len(all), len(want))
}
for i, s := range all {
if s != want[i] {
t.Errorf("AllSignalKinds()[%d] = %q, want %q (REQ-031 intent guard: the 4-source shape {Stash, Standing, Vouch, Capital} is intentional per AUDIT §193 P1-2; Freeholder and Guild are NOT signal kinds)", i, s, want[i])
}
}
}
// TestAllSignalKindsNames asserts the 4 signal names (Stash, Standing,
// Vouch, Capital) cross-ref v0.1 x/standing FreeholderSignals (StashMaturity,
// MultiDomainStanding, CommunityEndorsement, CommittedCapital).
+70
View File
@@ -0,0 +1,70 @@
package types
// cross_const_test.go (REQ-030, REVIEW.md P2 / A-304, GRILL G-015) is a
// cross-package const-equality test that catches silent drift between the
// x/hub LOCAL consts (LendingCouponCapBps / LendingCouponFloorBps) and the
// x/bond mission-locked consts (CouponCapBps / CouponFloorBps, D-028).
//
// Before REQ-030, the two const pairs were cross-documented only by a comment
// (x/hub/types/types.go:46-55) — no automated check existed. A future
// mission-locked change to x/bond.CouponCapBps without a matching x/hub change
// would silently drift. This test fails closed on either kind of drift:
//
// - single-sided drift: hub stays 800, bond changes to 900 → the equality
// test fails.
// - paired drift: BOTH change to the same wrong value (e.g., both 900) → the
// equality test passes BUT the absolute-value test (G-015) fails, because
// the mission-locked value is 800, not 900.
//
// G-003 (no production cross-module struct imports): this is a TEST-ONLY
// import of x/bond/types in a _test.go file. G-003's test-import exemption
// (documented in v0.2 GRILL G-003 and already exercised by
// x/bearers/types/types_test.go:7 importing x/processing/types) permits
// cross-package test imports. NO production .go file in x/hub/types/ imports
// x/bond/types (the P1-99-01 verification greps non-test .go files to confirm).
import (
"testing"
bondtypes "github.com/oy/openyield/x/bond/types"
)
// TestLendingCouponCapMatchesBondCap asserts the x/hub LOCAL
// LendingCouponCapBps equals the x/bond mission-locked CouponCapBps (A-304).
// Fails on single-sided drift (one changes, the other does not).
func TestLendingCouponCapMatchesBondCap(t *testing.T) {
if LendingCouponCapBps != bondtypes.CouponCapBps {
t.Errorf("A-304 drift: x/hub LendingCouponCapBps = %d, x/bond CouponCapBps = %d (must match)", LendingCouponCapBps, bondtypes.CouponCapBps)
}
}
// TestLendingCouponFloorMatchesBondFloor asserts the x/hub LOCAL
// LendingCouponFloorBps equals the x/bond mission-locked CouponFloorBps
// (A-304). Fails on single-sided drift.
func TestLendingCouponFloorMatchesBondFloor(t *testing.T) {
if LendingCouponFloorBps != bondtypes.CouponFloorBps {
t.Errorf("A-304 drift: x/hub LendingCouponFloorBps = %d, x/bond CouponFloorBps = %d (must match)", LendingCouponFloorBps, bondtypes.CouponFloorBps)
}
}
// TestConstsAreMissionLocked800And0 (GRILL G-015) asserts the ABSOLUTE
// mission-locked values: both caps are 800 (8pct, D-028) and both floors are 0
// (0pct, D-028). This catches PAIRED drift — if both consts change to the same
// wrong value (e.g., both 900), the equality tests above pass but this test
// fails, because the mission-locked value is 800, not 900. The 8pct cap /
// 0pct floor is the anti-greed covenant (vision §17, §18); defending the
// absolute value is the highest-priority regression guard in v0.4.
func TestConstsAreMissionLocked800And0(t *testing.T) {
if LendingCouponCapBps != 800 {
t.Errorf("G-015: x/hub LendingCouponCapBps = %d, want 800 (mission-locked 8pct, D-028)", LendingCouponCapBps)
}
if bondtypes.CouponCapBps != 800 {
t.Errorf("G-015: x/bond CouponCapBps = %d, want 800 (mission-locked 8pct, D-028)", bondtypes.CouponCapBps)
}
if LendingCouponFloorBps != 0 {
t.Errorf("G-015: x/hub LendingCouponFloorBps = %d, want 0 (mission-locked 0pct, D-028)", LendingCouponFloorBps)
}
if bondtypes.CouponFloorBps != 0 {
t.Errorf("G-015: x/bond CouponFloorBps = %d, want 0 (mission-locked 0pct, D-028)", bondtypes.CouponFloorBps)
}
}