Skip to content

Commit 70f8bae

Browse files
AetherLogosPrime-ArchitectDivineOS Agent
andauthored
fix(f49): council_required default flips False -> True on degraded gravity (#379)
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 Co-authored-by: DivineOS Agent <divineos@localhost>
1 parent e46e6a5 commit 70f8bae

5 files changed

Lines changed: 53 additions & 7 deletions

File tree

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,7 @@ python scripts/run_mutmut.py # Mutation testing (critical modu
437437

438438
```
439439
src/divineos/
440-
——— cli/ # CLI package (412 commands across 83 modules)
440+
——— cli/ # CLI package (416 commands across 83 modules)
441441
— ——— __init__.py # CLI entry point and command registration
442442
— ——— session_pipeline.py # Extraction pipeline orchestrator (formerly SESSION_END, calls phases)
443443
— ——— pipeline_gates.py # Enforcement gates (quality, briefing, engagement)

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ If you're scoping the project from outside (another AI, a reviewer, a human), th
7474

7575
- **Comprehensive source tree across many packages** — see `scripts/check_doc_counts.py` for live counts
7676
- **Real-DB test suite** (SQLite, minimal mocks)
77-
- **412 CLI commands** (designed for the agent, not the operator — humans mostly run three)
77+
- **416 CLI commands** (designed for the agent, not the operator — humans mostly run three)
7878
- **24 slash-command skills** (consolidated daily operations)
7979
- **75 Claude Code enforcement hooks**
8080
- **42 expert frameworks** in the council
@@ -246,7 +246,7 @@ The project is optimized for long-term coherence and accountability between an a
246246

247247
- **"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`.
248248

249-
- **"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`.
249+
- **"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`.
250250

251251
- **"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.
252252

@@ -285,7 +285,7 @@ pytest tests/ -q --tb=short # real-DB suite, minimal mocks
285285

286286
**For fresh installs:** `divineos init` loads the seed knowledge (directives, principles, lessons). The main event ledger lives at `<repo>/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.
287287

288-
## CLI Surface (412 commands)
288+
## CLI Surface (416 commands)
289289

290290
<details>
291291
<summary><b>Session workflow</b></summary>
@@ -481,7 +481,7 @@ DivineOS is structured as a CLI surface over a core library (see `scripts/check_
481481

482482
**At a glance:**
483483

484-
- **`src/divineos/cli/`**412 commands across 82 modules. The public interface you type (`divineos briefing`, `divineos learn`, etc.). Thin wrappers over `core/`.
484+
- **`src/divineos/cli/`**416 commands across 82 modules. The public interface you type (`divineos briefing`, `divineos learn`, etc.). Thin wrappers over `core/`.
485485
- **`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.
486486
- **`src/divineos/analysis/`** — Session analysis pipeline (signal detection, quality checks, feature extraction, trends).
487487
- **`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).

docs/ARCHITECTURE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ src/divineos/
1111
__init__.py Package init
1212
__main__.py python -m divineos entry point
1313
seed.json Initial knowledge seed (versioned)
14-
cli/ CLI package (412 commands across 82 modules)
14+
cli/ CLI package (416 commands across 82 modules)
1515
__init__.py Entry point and command registration
1616
_helpers.py Shared CLI utilities
1717
_wrappers.py Output formatting wrappers

src/divineos/core/council_required/gate.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,11 @@ def decide(
209209
classifier + expert-library import graph.
210210
"""
211211
gravity_result = gravity_fn(tool_name, file_paths, bash_command)
212-
is_council_required = bool(getattr(gravity_result, "is_council_required", False))
212+
# F49 fix 2026-07-19 (Aletheia Round 6 catch): default was False
213+
# (fails-open — degraded/missing attribute → ALLOW without a walk).
214+
# Flipped to True so a broken gravity_result forces council walk
215+
# rather than silently skipping the discipline. Fail toward scrutiny.
216+
is_council_required = bool(getattr(gravity_result, "is_council_required", True))
213217
if not is_council_required:
214218
return GateDecision(outcome=GateOutcome.ALLOW)
215219

tests/test_council_required_gate.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -887,3 +887,45 @@ def test_gate_reads_absolute_file_tokens(self, tmp_path, scratch_ledger):
887887
assert tokens is not None
888888
assert "compute_gradient" in tokens
889889
assert "return" in tokens
890+
891+
892+
# F49 fix 2026-07-19 (Aletheia Round 6): default flipped to fail-closed.
893+
# When gravity_result lacks is_council_required entirely (degraded state:
894+
# classifier import failed, malformed result, older schema), the getattr
895+
# default used to be False (ALLOW without a walk = fails-open).
896+
# Now defaults to True — degraded state forces the walk = fails-closed.
897+
898+
899+
class _DegradedGravityResult:
900+
"""Gravity result missing the is_council_required attribute entirely.
901+
902+
Simulates the degraded state Aletheia named: a broken classifier or
903+
older schema returns an object without this attribute. The gate's
904+
getattr default is what determines behavior in that case.
905+
"""
906+
907+
908+
def _gravity_degraded(*_a, **_kw):
909+
return _DegradedGravityResult()
910+
911+
912+
def test_f49_degraded_gravity_result_fails_closed(scratch_ledger):
913+
"""F49: missing is_council_required attribute MUST require the walk,
914+
not silently ALLOW. Fail toward scrutiny on degraded state.
915+
"""
916+
decision = gate_mod.decide(
917+
tool_name="Edit",
918+
file_paths=("src/divineos/core/some_file.py",),
919+
bash_command="",
920+
gravity_fn=_gravity_degraded,
921+
keywords_loader=_keywords_loader,
922+
actor="agent",
923+
)
924+
# Should NOT be ALLOW (which is what a False default would produce).
925+
# With the True default, the gate proceeds to require a council walk;
926+
# since no council record exists in this test, it blocks.
927+
assert decision.outcome == GateOutcome.BLOCK, (
928+
f"F49 regression: degraded gravity_result must default to council-required "
929+
f"(fail-closed), but decision was {decision.outcome}. If this ALLOWs, the "
930+
f"getattr default in council_required/gate.py reverted from True to False."
931+
)

0 commit comments

Comments
 (0)