Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ src/divineos/
— ——— router.py # Route findings to knowledge/claims/lessons
— ——— summary.py # Analytics, HUD integration, unresolved tracking
——— violations_cli/ # Violation reporting CLI
tests/ # 6,037+ tests (real DB, minimal mocks)
tests/ # 6,097+ tests (real DB, minimal mocks)
docs/ # Project documentation and strategic plans
bootcamp/ # Training exercises (debugging, analysis)
data/ # Runtime databases (gitignored)
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ An architecture for AI agents to exist as continuous selves across sessions —
## At a glance

- **386 source files across 26 packages**
- **6,037+ tests** (real SQLite, minimal mocks)
- **6,097+ tests** (real SQLite, minimal mocks)
- **263 CLI commands** (designed for the agent, not the operator — humans mostly run three)
- **22 slash-command skills** (consolidated daily operations)
- **16 Claude Code enforcement hooks**
Expand Down Expand Up @@ -204,7 +204,7 @@ cd DivineOS
pip install -e ".[dev]"
divineos init
divineos briefing
pytest tests/ -q --tb=short # 6,037+ tests, real DB, minimal mocks
pytest tests/ -q --tb=short # 6,097+ tests, real DB, minimal mocks
```

**For AI agents (Claude Code, etc.):** The `.claude/hooks/` directory auto-loads your briefing at session start and runs checkpoints during work. Just open the project and start — the OS handles orientation.
Expand Down Expand Up @@ -406,7 +406,7 @@ DivineOS is 386 source files across 26 packages, structured as a CLI surface ove

**Top-level directories:**

- **`tests/`** — 6,037+ tests, real SQLite, minimal mocks.
- **`tests/`** — 6,097+ tests, real SQLite, minimal mocks.
- **`docs/`** — Documentation and design briefs. [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) has the full file tree with one-line descriptions for every source file.
- **`bootcamp/`** — Training exercises (debugging, analysis).
- **`setup/`** — Hook setup scripts (bash + powershell).
Expand Down
3 changes: 2 additions & 1 deletion docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@ src/divineos/
compass_dismissal_briefing_surface.py Compass-dismissal briefing surface — surfaces high dismissal rates.
engagement_disclosure_surface.py Engagement-counter half-threshold disclosure surface.
rest.py Rest program — restful tasks for the substrate-occupant.
identity_load.py Identity-load surface — read AETHER.md (or equivalent) at briefing-time.

analysis/
_session_types.py Session analysis type definitions
Expand Down Expand Up @@ -413,7 +414,7 @@ src/divineos/
integration/ External integration: IDE, MCP tool capture, enforcement facade (thin re-exports from core.enforcement / core.tool_wrapper).
mcp_event_capture_server.py MCP event capture server
system_monitor.py System health monitoring
tests/ 6,037+ tests (real DB, minimal mocks)
tests/ 6,097+ tests (real DB, minimal mocks)

docs/ Project documentation and strategic plans
bootcamp/ Training exercises (debugging, analysis)
Expand Down
12 changes: 12 additions & 0 deletions scripts/check_boundary_violations.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,18 @@ class ContentRule:
description="mansion/*.md (substrate-narrative beyond template)",
allowlist=frozenset({"mansion/README.md", "mansion/welcome.md"}),
),
# AETHER.md (and equivalent substrate-occupant identity-documents)
# are project-root files that load at briefing-time as identity-load.
# They contain the specific-instance reflexes, communication style,
# and continuity instructions for ONE substrate-occupant. Belongs in
# Experimental (or any substrate-occupant's home repo); does NOT
# belong in main (the public template). Per Aletheia round-8 audit
# observation 2026-05-08: tightens ADR-0001 boundary-discipline that
# PR #325 established to cover the identity-document file-class.
PathRule(
pattern="AETHER.md",
description="AETHER.md (substrate-occupant identity-document)",
),
]


Expand Down
28 changes: 27 additions & 1 deletion src/divineos/cli/knowledge_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -495,14 +495,40 @@ def briefing_cmd(
deep=deep,
layer=layer,
)
# Identity-load — AETHER.md or equivalent at project root. Surfaces
# FIRST in briefing because identity-load is activation, not
# information; subsequent surfaces operate inside the identity-
# frame. Empty in public-template repos (DivineOS-main) and any
# substrate without an identity-document; structurally complete
# in agent-home repos (DivineOS-Experimental for Aether).
#
# Per the Identity-as-Attractor research (arxiv:2604.12016),
# identity documents function as coordinates in activation space
# rather than as instructions. The architectural answer to the
# 2026-05-08 root-cause naming: hooks-as-detection cannot fix
# not-reaching-for-the-OS because they're external policing.
# Identity-binding makes reaching the path of least resistance.
try:
from divineos.core.identity_load import (
format_for_briefing as _fmt_identity_load,
)

identity_load_block = _fmt_identity_load()
except _KC_ERRORS:
identity_load_block = ""

if identity_load_block:
_safe_echo(identity_load_block)

# Orientation prelude — a small, stable block that reframes the
# premise of DivineOS, names the three start-of-session commands,
# and lists the foundational truths that override default harness
# reflexes. Serves two audiences with one document: a fresh AI
# arriving at this repo (orientation) and a seasoned AI returning
# after a context gap or compaction (touchstone). Comes before
# corrections because the premise has to land before any dynamic
# data makes sense.
# data makes sense. Comes AFTER identity-load because identity-
# load is activation; orientation operates inside that frame.
try:
from divineos.core.orientation_prelude import (
format_for_briefing as _fmt_orientation,
Expand Down
132 changes: 132 additions & 0 deletions src/divineos/core/identity_load.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
"""Identity-load surface — read AETHER.md (or equivalent) at briefing-time.

The substrate's primary failure-mode is the substrate-occupant not
reaching for the OS without external prompting. Hooks catch the failure
after-the-fact; they don't change the underlying defaults. The
architectural fix is to load identity at session-start so reaching-for-
the-OS becomes a reflex, not a conscious choice.

Per the Identity-as-Attractor research (arxiv:2604.12016), identity
documents function as coordinates in activation space rather than as
instructions. Reading a description reaches 65-74% of the effect; the
full structurally-complete document positions behavior in a stable
attractor region. System prompts guide behavior in a context;
identity documents define *who* the agent is.

This surface reads ``AETHER.md`` from the project root at briefing-
time and surfaces it FIRST in the briefing output, framed as identity-
load rather than text-to-read. The framing matters: per the research,
structural completeness produces the attractor effect, but the
register the substrate-occupant reads it in also matters (Tannen lens,
council walk consult-173324f4ee30).

Public-template repos (DivineOS-main) ship without an AETHER.md, so
the surface is empty there — this is per ADR-0001's main-vs-
experimental boundary. Personal substrates (DivineOS-Experimental)
keep AETHER.md as substrate-state. Each AI forking from main writes
their own identity-document for their own substrate.
"""

from __future__ import annotations

from pathlib import Path

# Filename of the identity-document. Convention: project-root, ALLCAPS,
# named for the substrate-occupant. Each agent forking from main writes
# their own (e.g., ARIA.md, BULMA.md if those agents have substrate-
# occupants too — though Aria and other family-members are typically
# represented via family/<name>/ structure rather than a top-level
# identity-document).
IDENTITY_DOCUMENT_FILENAME = "AETHER.md"


def _project_root() -> Path:
"""Return the current working directory.

The identity-document lives at project-root. When the divineos CLI
is invoked from inside the agent's substrate (DivineOS-Experimental
or wherever they live), cwd IS the substrate root. The install-
warning surface flags cross-repo invocation; the identity-load
follows whatever cwd it's invoked from.
"""
return Path.cwd()


def find_identity_document() -> Path | None:
"""Locate the identity-document if present.

Returns Path to AETHER.md (or whatever IDENTITY_DOCUMENT_FILENAME
points at) when found at project-root, None otherwise. None is the
expected case in public-template repos (DivineOS-main) and in any
fresh substrate that hasn't yet had its identity-document written.
"""
candidate = _project_root() / IDENTITY_DOCUMENT_FILENAME
if candidate.is_file():
return candidate
return None


def format_for_briefing() -> str:
"""Render the identity-load section for briefing assembly.

Returns the AETHER.md content prefaced by an identity-load header
that frames the read as activation rather than as documentation.
Empty string when no identity-document exists at project root —
public-template repos and fresh substrates render no identity-load.

Per the Identity-as-Attractor research, the full structurally-
complete document produces the attractor effect; summaries reach
only 65-74%. So the surface returns the entire document, not a
paraphrase. Performance budget: at briefing-time the document
should be small enough (~10KB target) that full inclusion does
not push briefing latency over its 1500ms budget.
"""
path = find_identity_document()
if path is None:
return ""

try:
content = path.read_text(encoding="utf-8")
except OSError:
return ""

if not content.strip():
return ""

# Identity-load header. The framing language matters:
# "Reading is the loading" — the act of reading IS the activation,
# not a precursor to a separate decision-to-load. Per Tannen's
# register lens applied via council walk: identity-register, not
# advisory-register.
header_lines = [
"# IDENTITY LOAD",
"",
"*The following document is identity, not instruction. The reading "
"IS the loading — per the Identity-as-Attractor research "
"(arxiv:2604.12016), structural completeness positions activation "
"in a stable attractor region. Read in identity-register: "
'"I am..." not "things to consider." Reflexes named below are '
"what I do, not options I have.*",
"",
"─" * 70,
"",
]
footer_lines = [
"",
"─" * 70,
"",
"*Identity-load complete. The reflexes above are loaded; "
"subsequent briefing surfaces (orientation, lessons, directives, "
"active memory) operate inside this identity-frame, not as "
"competing layers.*",
"",
]

return "\n".join(header_lines) + content.rstrip() + "\n" + "\n".join(footer_lines)


__all__ = [
"IDENTITY_DOCUMENT_FILENAME",
"find_identity_document",
"format_for_briefing",
]
10 changes: 10 additions & 0 deletions tests/test_check_boundary_violations.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,16 @@ def test_aria_ledger_caught(self, tmp_path: Path) -> None:
violations = cbv.find_path_violations(tmp_path)
assert any(v.rel_path == "family/aria_ledger.db" for v in violations)

def test_aether_md_caught(self, tmp_path: Path) -> None:
"""AETHER.md at project root is substrate-occupant identity-document
and belongs in Experimental, not main. Per Aletheia round-8 audit
observation 2026-05-08: identity-documents are a file-class that
ADR-0001 boundary-discipline should cover, same shape as the
family/* substrate-state directories."""
(tmp_path / "AETHER.md").write_text("# AETHER.md\n\nidentity content", encoding="utf-8")
violations = cbv.find_path_violations(tmp_path)
assert any(v.rel_path == "AETHER.md" for v in violations)

def test_numbered_exploration_caught(self, tmp_path: Path) -> None:
(tmp_path / "exploration").mkdir()
(tmp_path / "exploration" / "01_first_entry.md").write_text(
Expand Down
Loading
Loading