diff --git a/.ciagent/oy/ARCHITECTURE.md b/.ciagent/oy/ARCHITECTURE.md index 9b7344c..c7a99e1 100644 --- a/.ciagent/oy/ARCHITECTURE.md +++ b/.ciagent/oy/ARCHITECTURE.md @@ -227,4 +227,36 @@ v0.4 touches NO new `x/*` modules. The touched files are: ### 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. \ No newline at end of file +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. \ No newline at end of file diff --git a/x/council/types/types_test.go b/x/council/types/types_test.go index 1a34fd4..bdcbb01 100644 --- a/x/council/types/types_test.go +++ b/x/council/types/types_test.go @@ -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).