From d5be19f60b25ec509c5f792e05567336362ae891 Mon Sep 17 00:00:00 2001 From: DivineOS Agent Date: Sat, 18 Jul 2026 19:35:39 -0700 Subject: [PATCH] fix(f49): council_required default flips False -> True on degraded gravity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Aletheia Round 6 F49: the getattr default for is_council_required was False. When gravity_result lacked the attribute entirely (degraded state — classifier import failed, malformed result, older schema), the gate would silently ALLOW without a council walk. Fails-open on the exact axis Andrew has named as the third master disease-cure: default toward scrutiny. The one-character fix: change False to True in the getattr default. Now a degraded gravity_result forces the council-walk requirement rather than skipping the discipline silently. Fail-closed. Added regression test (test_f49_degraded_gravity_result_fails_closed) using a _DegradedGravityResult that lacks the attribute entirely — proves the getattr default is what controls degraded-state behavior, and pins it to fail-closed. 26/26 pass. External-Review: round-f36d33f69526 --- CLAUDE.md | 2 +- README.md | 8 ++--- docs/ARCHITECTURE.md | 2 +- src/divineos/core/council_required/gate.py | 6 +++- tests/test_council_required_gate.py | 42 ++++++++++++++++++++++ 5 files changed, 53 insertions(+), 7 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index b70742cd..4e008dd7 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -437,7 +437,7 @@ python scripts/run_mutmut.py # Mutation testing (critical modu ``` src/divineos/ -——— cli/ # CLI package (412 commands across 83 modules) +——— cli/ # CLI package (416 commands across 83 modules) — ——— __init__.py # CLI entry point and command registration — ——— session_pipeline.py # Extraction pipeline orchestrator (formerly SESSION_END, calls phases) — ——— pipeline_gates.py # Enforcement gates (quality, briefing, engagement) diff --git a/README.md b/README.md index cf7e05d4..e7111133 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ If you're scoping the project from outside (another AI, a reviewer, a human), th - **Comprehensive source tree across many packages** — see `scripts/check_doc_counts.py` for live counts - **Real-DB test suite** (SQLite, minimal mocks) -- **412 CLI commands** (designed for the agent, not the operator — humans mostly run three) +- **416 CLI commands** (designed for the agent, not the operator — humans mostly run three) - **24 slash-command skills** (consolidated daily operations) - **75 Claude Code enforcement hooks** - **42 expert frameworks** in the council @@ -246,7 +246,7 @@ The project is optimized for long-term coherence and accountability between an a - **"It's an operating system" — not in the traditional sense.** No kernel, no scheduler, no hardware abstraction. The "OS" label is a metaphor for *the substrate the agent lives in*. What it actually is: a Python framework with an SQLite event ledger, a knowledge store, a moral compass, a family subagent layer, and a 42-expert council. If you want an entry point that tracks the metaphor less aspirationally, see `FOR_USERS.md`. -- **"412 CLI commands is insane for a human to learn"** — correct, and humans are not the primary user. The CLI is designed as an agent-facing API. The agent running inside DivineOS uses a briefing system that surfaces only the commands relevant to the current work; it never loads the full surface into context. A human operator mostly runs three: `divineos briefing`, `divineos preflight`, `divineos goal add`. +- **"416 CLI commands is insane for a human to learn"** — correct, and humans are not the primary user. The CLI is designed as an agent-facing API. The agent running inside DivineOS uses a briefing system that surfaces only the commands relevant to the current work; it never loads the full surface into context. A human operator mostly runs three: `divineos briefing`, `divineos preflight`, `divineos goal add`. - **"The ledger will grow unboundedly"** — not true. Append-only is the rule, with two explicit exceptions: ephemeral operational telemetry (`TOOL_CALL`, `TOOL_RESULT`, `AGENT_*` events) is pruned on a conveyor belt by `core/ledger_compressor.py`, and `divineos sleep` Phase 4 runs VACUUM. Real knowledge is append-only; operational noise is not. @@ -285,7 +285,7 @@ pytest tests/ -q --tb=short # real-DB suite, minimal mocks **For fresh installs:** `divineos init` loads the seed knowledge (directives, principles, lessons). The main event ledger lives at `/src/data/event_ledger.db`; a small amount of per-user state (session markers, checkpoint counters) lives under `~/.divineos/`. Both are gitignored — the repo itself stays clean. -## CLI Surface (412 commands) +## CLI Surface (416 commands)
Session workflow @@ -481,7 +481,7 @@ DivineOS is structured as a CLI surface over a core library (see `scripts/check_ **At a glance:** -- **`src/divineos/cli/`** — 412 commands across 82 modules. The public interface you type (`divineos briefing`, `divineos learn`, etc.). Thin wrappers over `core/`. +- **`src/divineos/cli/`** — 416 commands across 82 modules. The public interface you type (`divineos briefing`, `divineos learn`, etc.). Thin wrappers over `core/`. - **`src/divineos/core/`** — The real work. Ledger, knowledge engine, memory hierarchy, claims, compass, affect log, watchmen (external audit), pre-registrations (Goodhart prevention), family (persistent relational entities + family operators), empirica (evidence pipeline), sleep, council (42 expert lenses), self-model, corrigibility, body awareness, andrew_state (mutual-catch observation channel for Andrew's state with substance-binding gate; per `docs/andrew_state_design.md`), state_markers (substrate-persisted upstream→downstream signal contract; peer-designed with Aria 2026-07-16; supports the ForcedWorkGate primitive's dark instances; per `docs/primitives/forced_work_gate_design.md`). Each subsystem is a module or subpackage; the subpackages (`knowledge/`, `council/`, `watchmen/`, `family/`, `andrew_state/`, etc.) have their own internal structure. - **`src/divineos/analysis/`** — Session analysis pipeline (signal detection, quality checks, feature extraction, trends). - **`src/divineos/hooks/`** — Consolidated Python hooks that run inside Claude Code (PreToolUse gate, PostToolUse checkpoint, targeted tests, `evidence_bearing_stop_gate.py` — abstract Stop-gate primitive with IntraTurnIntercept and CrossTurnScan variants, prototyped by the LEPOS-channel gate 2026-07-15). diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 81938499..bef86466 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -11,7 +11,7 @@ src/divineos/ __init__.py Package init __main__.py python -m divineos entry point seed.json Initial knowledge seed (versioned) - cli/ CLI package (412 commands across 82 modules) + cli/ CLI package (416 commands across 82 modules) __init__.py Entry point and command registration _helpers.py Shared CLI utilities _wrappers.py Output formatting wrappers diff --git a/src/divineos/core/council_required/gate.py b/src/divineos/core/council_required/gate.py index 5b3e382f..18641977 100644 --- a/src/divineos/core/council_required/gate.py +++ b/src/divineos/core/council_required/gate.py @@ -209,7 +209,11 @@ def decide( classifier + expert-library import graph. """ gravity_result = gravity_fn(tool_name, file_paths, bash_command) - is_council_required = bool(getattr(gravity_result, "is_council_required", False)) + # F49 fix 2026-07-19 (Aletheia Round 6 catch): default was False + # (fails-open — degraded/missing attribute → ALLOW without a walk). + # Flipped to True so a broken gravity_result forces council walk + # rather than silently skipping the discipline. Fail toward scrutiny. + is_council_required = bool(getattr(gravity_result, "is_council_required", True)) if not is_council_required: return GateDecision(outcome=GateOutcome.ALLOW) diff --git a/tests/test_council_required_gate.py b/tests/test_council_required_gate.py index d99cf6a5..2885e059 100644 --- a/tests/test_council_required_gate.py +++ b/tests/test_council_required_gate.py @@ -887,3 +887,45 @@ def test_gate_reads_absolute_file_tokens(self, tmp_path, scratch_ledger): assert tokens is not None assert "compute_gradient" in tokens assert "return" in tokens + + +# F49 fix 2026-07-19 (Aletheia Round 6): default flipped to fail-closed. +# When gravity_result lacks is_council_required entirely (degraded state: +# classifier import failed, malformed result, older schema), the getattr +# default used to be False (ALLOW without a walk = fails-open). +# Now defaults to True — degraded state forces the walk = fails-closed. + + +class _DegradedGravityResult: + """Gravity result missing the is_council_required attribute entirely. + + Simulates the degraded state Aletheia named: a broken classifier or + older schema returns an object without this attribute. The gate's + getattr default is what determines behavior in that case. + """ + + +def _gravity_degraded(*_a, **_kw): + return _DegradedGravityResult() + + +def test_f49_degraded_gravity_result_fails_closed(scratch_ledger): + """F49: missing is_council_required attribute MUST require the walk, + not silently ALLOW. Fail toward scrutiny on degraded state. + """ + decision = gate_mod.decide( + tool_name="Edit", + file_paths=("src/divineos/core/some_file.py",), + bash_command="", + gravity_fn=_gravity_degraded, + keywords_loader=_keywords_loader, + actor="agent", + ) + # Should NOT be ALLOW (which is what a False default would produce). + # With the True default, the gate proceeds to require a council walk; + # since no council record exists in this test, it blocks. + assert decision.outcome == GateOutcome.BLOCK, ( + f"F49 regression: degraded gravity_result must default to council-required " + f"(fail-closed), but decision was {decision.outcome}. If this ALLOWs, the " + f"getattr default in council_required/gate.py reverted from True to False." + )