diff --git a/SESSION_FOCUS.md b/SESSION_FOCUS.md index 90b7d92d..12b58469 100644 --- a/SESSION_FOCUS.md +++ b/SESSION_FOCUS.md @@ -10,6 +10,12 @@ **See `docs/SPRINT.md` for full sprint plan and task details.** Do not duplicate sprint content here — SPRINT.md is the source of truth for task scope, status, and dependencies. +### Sprint 51 Summary: Minimum Viable Society Validation + Constraint Alignment (COMPLETE) + +| Task | Status | Notes | +|------|--------|-------| +| T1: validate_minimum_viable() + Constraint alignment | DONE | Resolves Sprint 49 audit P5+P6. `validate_minimum_viable()` in `web4/role.py`: base-mandatory completeness, differentiation, witnessing. `Constraint` aligned with Rust: `threshold: float` + `hard: bool`. 1 new export (369 total), 12 new tests (2668 total), 0 new files. | + ### Sprint 50 Summary: Add SocietyRole + RoleAssignment to Python SDK (COMPLETE) | Task | Status | Notes | @@ -196,7 +202,7 @@ See `docs/SPRINT.md` for full history. Highlights: JSON-LD serialization for all - **Version**: 0.26.0 - **Modules**: 23 library modules + MCP server entry point (trust, lct, atp, federation, r6, mrh, acp, dictionary, entity, capability, errors, metabolic, binding, society, role, reputation, security, protocol, mcp, attestation, validation, deserialize, generate, mcp_server) -- **Tests**: 2656 passing +- **Tests**: 2668 passing - **CLI**: `web4 info/validate/list-schemas/roundtrip/generate/selftest/trust` (7 subcommands) - **Exports**: 368 symbols via `web4/__init__.py` - **from_dict()**: 58 classmethods across 10 modules — all classes with to_dict()/as_dict() have matching from_dict() @@ -272,7 +278,7 @@ Sprint 50 T1 PR pending. ## Completeness Summary -- All 50 sprints COMPLETE (Sprints 1-50, all merged or PR pending) +- All 51 sprints COMPLETE (Sprints 1-51, all merged or PR pending) - All 9 JSON-LD schemas with cross-language validation vectors (278 total, in pytest) - All `to_jsonld()` functions have `from_jsonld()` inverses (API symmetry complete) - All `to_dict()`/`as_dict()` methods have `from_dict()` inverses (58 round-trip methods total) @@ -283,7 +289,7 @@ Sprint 50 T1 PR pending. - MCP server: 8 tools exposing SDK data operations + behavioral trust/reputation resolution to MCP clients - TrustQuery: to_jsonld() for dispatcher + to_dict() for schema validation (trust-query.schema.json) - `process_action_outcome()` — action consequence pipeline composing R7Action + ReputationEngine + TrustProfile + ATPAccount -- All 23 submodules have `__all__` declarations, 368 root exports +- All 23 submodules have `__all__` declarations, 369 root exports - All public methods have docstrings and return type annotations - `mypy --strict` passes with 0 errors across 26 source files - Test coverage: 97.8% overall (4 modules at 100%, 16 at 95%+, __main__.py at 90.6%) @@ -299,10 +305,12 @@ Sprint 50 T1 PR pending. --- - **Society Roles added**: `SocietyRole` enum + `RoleAssignment` dataclass + `bootstrap_society_roles()` — resolves CRITICAL audit finding (P1), HIGH role-LCT binding (P2), and HIGH solo-founder genesis (P3) -- **web4-core Society/Role/ATP/R6 alignment**: Cross-language audit identified 14 items (1 CRITICAL: Python SDK missing SocietyRole, 3 HIGH, 3 MEDIUM, 4 LOW) — see `docs/audits/cross-language-society-role-atp-r6-alignment-2026-05-14.md`. P1-P3 resolved by Sprint 50. P4 (MetabolicState) and P7 (role integration) need operator decisions. P5 (validate_minimum_viable) now unblocked. +- **Minimum viable society validation**: `validate_minimum_viable()` added to Python SDK — resolves audit P5 (cross-language parity with Rust `Society::validate_minimum_viable()`) +- **Constraint alignment**: `Constraint` dataclass upgraded from `value: Any` to `threshold: float` + `hard: bool` — resolves audit P6 (cross-language parity with Rust `Constraint`) +- **web4-core Society/Role/ATP/R6 alignment**: Cross-language audit identified 14 items (1 CRITICAL: Python SDK missing SocietyRole, 3 HIGH, 3 MEDIUM, 4 LOW) — see `docs/audits/cross-language-society-role-atp-r6-alignment-2026-05-14.md`. P1-P3 resolved by Sprint 50. P5-P6 resolved by Sprint 51. P4 (MetabolicState) and P7 (role integration) need operator decisions. - **web4-trust-core T3/V3 alignment**: Cross-language T3/V3 audit identified 8 divergences (1 CRITICAL, 4 HIGH) between Rust/WASM and spec/Python SDK — see `docs/audits/cross-language-t3v3-alignment-2026-05-13.md` - **Parameter governance**: All trust/value/energy parameters classified into three tiers (protocol-invariant, society-configurable, simulation-only) — see `web4-standard/core-spec/t3-v3-tensors.md` §10 --- -*Updated by autonomous session, 2026-05-14 (Sprint 50 — SocietyRole + RoleAssignment added to Python SDK)* +*Updated by autonomous session, 2026-05-14 (Sprint 51 — validate_minimum_viable + Constraint alignment)* diff --git a/docs/SPRINT.md b/docs/SPRINT.md index bf869909..d9384b12 100644 --- a/docs/SPRINT.md +++ b/docs/SPRINT.md @@ -1,12 +1,39 @@ # Web4 Sprint Plan **Created**: 2026-03-14 -**Updated**: 2026-05-14 (Sprint 50) +**Updated**: 2026-05-14 (Sprint 51) **Phase**: Development **Track**: web4 (Legion) --- +## Sprint 51: Minimum Viable Society Validation + Constraint Alignment (2026-05-14) + +Resolves two remaining autonomous-actionable items from the Sprint 49 +cross-language audit fix queue: P5 (validate_minimum_viable) and P6 +(Constraint threshold + hard flag alignment). + +### T1: validate_minimum_viable() + Constraint alignment +**Status**: DONE +**Completed**: 2026-05-14 +**Authorized by**: Sprint 49 audit fix queue P5+P6. Policy-reviewed and approved. +**Scope**: +1. **P5 — `validate_minimum_viable()`**: New function in `web4/role.py` per + `inter-society-protocol.md` §6.2 and Rust `Society::validate_minimum_viable()`. + Checks base-mandatory completeness, internal differentiation (≥2 fillers when + operational), witnessing capacity (Witness or Auditor when operational). +2. **P6 — Constraint alignment**: Updated `Constraint` dataclass in `web4/r6.py` + from `value: Any` to `threshold: float` + `hard: bool = True`. Updated + serialization, JSON schemas, and test vectors. + +**Result**: 1 new export (369 total), 12 new tests (2668 total), 0 new files. +mypy --strict clean, ruff lint/format clean. + +**Remaining from audit**: P4 (MetabolicState — needs operator decision), +P7 (role integration — needs operator decision). + +--- + ## Sprint 50: Add SocietyRole + RoleAssignment to Python SDK (2026-05-14) Implements the top 3 items from the Sprint 49 cross-language audit fix queue: diff --git a/web4-standard/implementation/sdk/tests/test_r6.py b/web4-standard/implementation/sdk/tests/test_r6.py index a996bea8..0c5ed670 100644 --- a/web4-standard/implementation/sdk/tests/test_r6.py +++ b/web4-standard/implementation/sdk/tests/test_r6.py @@ -80,7 +80,7 @@ def test_prohibition_overrides_permission(self): def test_constraint_minimum(self): rules = Rules( constraints=[ - Constraint(constraint_type="atp_minimum", value=50), + Constraint(constraint_type="atp_minimum", threshold=50), ] ) assert rules.check_constraint("atp_minimum", 100) @@ -90,7 +90,7 @@ def test_constraint_minimum(self): def test_constraint_maximum(self): rules = Rules( constraints=[ - Constraint(constraint_type="rate_limit", value=100), + Constraint(constraint_type="rate_limit", threshold=100), ] ) assert rules.check_constraint("rate_limit", 50) @@ -100,7 +100,7 @@ def test_constraint_maximum(self): def test_no_matching_constraint(self): rules = Rules( constraints=[ - Constraint(constraint_type="atp_minimum", value=50), + Constraint(constraint_type="atp_minimum", threshold=50), ] ) # No constraint for "rate_limit" → passes @@ -856,8 +856,8 @@ def _make_full_action(self) -> R7Action: law_hash="sha256:governance_v2", society="lct:web4:society:genesis", constraints=[ - Constraint(constraint_type="atp_minimum", value=50), - Constraint(constraint_type="rate_limit", value=100), + Constraint(constraint_type="atp_minimum", threshold=50), + Constraint(constraint_type="rate_limit", threshold=100), ], permissions=["read", "analyze"], prohibitions=["delete"], @@ -1296,7 +1296,7 @@ def test_full_action_validates(self): rules=Rules( law_hash="sha256:abc", society="lct:society:genesis", - constraints=[Constraint(constraint_type="atp_minimum", value=50)], + constraints=[Constraint(constraint_type="atp_minimum", threshold=50)], permissions=["read"], ), role=Role( @@ -1394,7 +1394,7 @@ def _make_full_action(self): rules=Rules( law_hash="lh1", society="soc1", - constraints=[Constraint("trust_min", 0.5)], + constraints=[Constraint("trust_min", 0.5, True)], permissions=["read"], prohibitions=["delete"], ), diff --git a/web4-standard/implementation/sdk/tests/test_r6_roundtrip.py b/web4-standard/implementation/sdk/tests/test_r6_roundtrip.py index 22e8cef8..d0ec403b 100644 --- a/web4-standard/implementation/sdk/tests/test_r6_roundtrip.py +++ b/web4-standard/implementation/sdk/tests/test_r6_roundtrip.py @@ -26,17 +26,30 @@ class TestConstraintRoundTrip: def test_basic(self) -> None: - c = Constraint(constraint_type="rate_limit", value=100) + c = Constraint(constraint_type="rate_limit", threshold=100) assert Constraint.from_dict(c.to_dict()) == c - def test_string_value(self) -> None: - c = Constraint(constraint_type="witness_required", value="true") + def test_hard_default(self) -> None: + c = Constraint(constraint_type="witness_quorum", threshold=3.0) + assert c.hard is True + assert Constraint.from_dict(c.to_dict()) == c + + def test_soft_constraint(self) -> None: + c = Constraint(constraint_type="rate_limit", threshold=100, hard=False) + assert c.hard is False assert Constraint.from_dict(c.to_dict()) == c def test_float_value(self) -> None: - c = Constraint(constraint_type="atp_minimum", value=50.5) + c = Constraint(constraint_type="atp_minimum", threshold=50.5) assert Constraint.from_dict(c.to_dict()) == c + def test_legacy_value_key(self) -> None: + """from_dict accepts legacy 'value' key for backward compatibility.""" + d = {"type": "atp_minimum", "value": 42} + c = Constraint.from_dict(d) + assert c.threshold == 42.0 + assert c.hard is True + class TestRulesRoundTrip: def test_empty(self) -> None: @@ -48,7 +61,7 @@ def test_full(self) -> None: law_hash="abc123", society="test-society", constraints=[ - Constraint("rate_limit", 100), + Constraint("rate_limit", 100.0), Constraint("atp_minimum", 10.0), ], permissions=["read", "write"], diff --git a/web4-standard/implementation/sdk/web4/__init__.py b/web4-standard/implementation/sdk/web4/__init__.py index 170c4227..27df6e76 100644 --- a/web4-standard/implementation/sdk/web4/__init__.py +++ b/web4-standard/implementation/sdk/web4/__init__.py @@ -27,7 +27,7 @@ - Deserialization — generic JSON-LD dispatcher for all Web4 types - Generation — produce minimal valid JSON-LD documents for any Web4 type -23 modules + MCP server, 368 exports, 2627 tests, 3 behavioral functions, 8 MCP tools, 7 CLI subcommands. +23 modules + MCP server, 369 exports, 2668 tests, 3 behavioral functions, 8 MCP tools, 7 CLI subcommands. v0.26.0: CI quality gates (strict mypy, ruff lint, ruff format) enforced on every PR. These modules define the canonical data types and algorithms specified in the web4-standard. They work offline (no network services required) and are designed to be diff --git a/web4-standard/implementation/sdk/web4/r6.py b/web4-standard/implementation/sdk/web4/r6.py index c8335099..013af40c 100644 --- a/web4-standard/implementation/sdk/web4/r6.py +++ b/web4-standard/implementation/sdk/web4/r6.py @@ -124,19 +124,45 @@ class ReputationComputationError(R7Error): @dataclass(frozen=True) class Constraint: - """A single constraint within Rules.""" + """A single constraint within Rules. - constraint_type: str # e.g. "rate_limit", "atp_minimum", "witness_required" - value: Any # threshold or limit + Cross-language parity with ``web4-core/src/r6.rs::Constraint``. + + Attributes: + constraint_type: Constraint kind (e.g. "rate_limit", "min_atp", + "witness_quorum"). + threshold: Numeric threshold value for the constraint. + hard: If True the constraint blocks execution; if False it warns + but allows the action to proceed. Defaults to True. + """ + + constraint_type: str + threshold: float + hard: bool = True def to_dict(self) -> Dict[str, Any]: - """Serialize to dict with 'type' and 'value' keys.""" - return {"type": self.constraint_type, "value": self.value} + """Serialize to dict.""" + return { + "type": self.constraint_type, + "threshold": self.threshold, + "hard": self.hard, + } @classmethod def from_dict(cls, d: Dict[str, Any]) -> "Constraint": - """Deserialize from dict produced by to_dict().""" - return cls(constraint_type=d["type"], value=d["value"]) + """Deserialize from dict. + + Accepts either ``threshold`` (current) or ``value`` (legacy) key + for the numeric threshold. + """ + raw = d.get("threshold", d.get("value")) + if raw is None: + raise KeyError("Constraint dict must contain 'threshold' or 'value'") + return cls( + constraint_type=d["type"], + threshold=float(raw), + hard=d.get("hard", True), + ) @dataclass @@ -165,8 +191,9 @@ def check_constraint(self, constraint_type: str, actual_value: float) -> bool: """Check a specific constraint. Returns True if no constraint of that type exists.""" for c in self.constraints: if c.constraint_type == constraint_type: - if isinstance(c.value, (int, float)): - return actual_value >= c.value if constraint_type.endswith("_minimum") else actual_value <= c.value + return ( + actual_value >= c.threshold if constraint_type.endswith("_minimum") else actual_value <= c.threshold + ) return True def to_dict(self) -> Dict[str, Any]: @@ -940,9 +967,7 @@ def from_jsonld(cls, doc: Dict[str, Any]) -> R7Action: """ # 1. Rules rules_data = doc.get("rules", {}) - constraints = [ - Constraint(constraint_type=c["type"], value=c["value"]) for c in rules_data.get("constraints", []) - ] + constraints = [Constraint.from_dict(c) for c in rules_data.get("constraints", [])] rules = Rules( law_hash=rules_data.get("lawHash", ""), society=rules_data.get("society", ""), diff --git a/web4-standard/implementation/sdk/web4/schema_registry.json b/web4-standard/implementation/sdk/web4/schema_registry.json index e0834c72..ee9d5f40 100644 --- a/web4-standard/implementation/sdk/web4/schema_registry.json +++ b/web4-standard/implementation/sdk/web4/schema_registry.json @@ -2564,13 +2564,18 @@ "type": "object", "required": [ "type", - "value" + "threshold" ], "properties": { "type": { "type": "string" }, - "value": {} + "threshold": { + "type": "number" + }, + "hard": { + "type": "boolean" + } }, "additionalProperties": false }, diff --git a/web4-standard/schemas/r7-action-jsonld.schema.json b/web4-standard/schemas/r7-action-jsonld.schema.json index 869426c8..b266a19b 100644 --- a/web4-standard/schemas/r7-action-jsonld.schema.json +++ b/web4-standard/schemas/r7-action-jsonld.schema.json @@ -58,10 +58,11 @@ "type": "array", "items": { "type": "object", - "required": ["type", "value"], + "required": ["type", "threshold"], "properties": { "type": { "type": "string" }, - "value": {} + "threshold": { "type": "number" }, + "hard": { "type": "boolean" } }, "additionalProperties": false }, diff --git a/web4-standard/test-vectors/schema-validation/r7-action-jsonld-validation.json b/web4-standard/test-vectors/schema-validation/r7-action-jsonld-validation.json index 0b5624a7..45409fa6 100644 --- a/web4-standard/test-vectors/schema-validation/r7-action-jsonld-validation.json +++ b/web4-standard/test-vectors/schema-validation/r7-action-jsonld-validation.json @@ -48,8 +48,8 @@ "lawHash": "sha256:law_hash_002", "society": "lct:web4:society:research", "constraints": [ - {"type": "time_limit", "value": 3600}, - {"type": "budget_cap", "value": 100} + {"type": "time_limit", "threshold": 3600, "hard": true}, + {"type": "budget_cap", "threshold": 100, "hard": true} ], "permissions": ["read", "analyze"], "prohibitions": ["delete", "modify"]