Skip to content

PR-C: Split user_preferences.json from heartbeat_config.json#15

Merged
hanamorix merged 1 commit into
mainfrom
principle-audit-pr-c
Apr 25, 2026
Merged

PR-C: Split user_preferences.json from heartbeat_config.json#15
hanamorix merged 1 commit into
mainfrom
principle-audit-pr-c

Conversation

@hanamorix

Copy link
Copy Markdown
Owner

Summary

Implements PR-C from the principle alignment audit at
docs/superpowers/audits/2026-04-25-principle-alignment-audit.md.

Introduces user_preferences.json as the single GUI-surfaceable cadence file.
heartbeat_config.json is now formally documented as developer-only internal
calibration — decay rates, GC thresholds, gating thresholds, reflex/research
enable flags. The GUI must never read or write that file.

Currently dream_every_hours is the only field in user_preferences.json,
matching the principle: the user picks how often the brain dreams; the brain
owns everything else.

Resolution order

In HeartbeatConfig.load():

  1. heartbeat_config.json's value (or framework default 24.0)
  2. user_preferences.json's value when the field is explicitly present

The "explicitly present" check (via read_raw_keys()) matters for back-compat:
a future user_preferences.json that adds a new field (e.g., growth_every_hours
once Phase 2a lands) must not silently shadow a custom dream_every_hours
sitting in heartbeat_config.json from before this split.

Changes

  • brain/user_preferences.py — new UserPreferences dataclass with atomic
    load/save (.new + os.replace). Module-level read_raw_keys() helper that
    returns the explicit JSON keys, used by HeartbeatConfig.load to do per-field
    precedence rather than whole-object precedence.
  • brain/engines/heartbeat.pyHeartbeatConfig.load() now merges
    user_preferences.json on top after loading heartbeat_config.json. Original
    load body extracted to _load_internal(). Updated docstring documents the
    two-file contract.

Test plan

  • 11 new unit tests for UserPreferences + read_raw_keys (load/save,
    corrupt JSON, wrong-type fallback, atomic save)
  • 5 new merge-precedence tests on HeartbeatConfig.load:
    override / fallback / present-but-omits-field / user-prefs-only / corrupt-prefs
  • Full suite: 492 passed (was 476, +16 new)
  • ruff check clean

Closes

This is the third and final cleanup PR from the principle alignment audit
(PR-A → PR-B → PR-C). After this lands, every routine user surface in the
framework matches the principle: name (persona dir) / cadence
(user_preferences.json) / face-body (out of scope until GUI work) /
generated documents (read-only inspection).

…PR-C)

Per principle audit (PR-C): introduces `user_preferences.json` as the single
GUI-surfaceable cadence file. heartbeat_config.json remains, but is now
formally documented as developer-only internal calibration — decay rates,
GC thresholds, gating thresholds, reflex/research enable flags. The GUI
must never read or write that file.

Currently `dream_every_hours` is the only field in user_preferences.json,
matching the principle: the user picks how often the brain dreams; the
brain owns everything else.

Resolution order in HeartbeatConfig.load():
1. heartbeat_config.json's value (or framework default 24.0)
2. user_preferences.json's value WHEN the field is explicitly present

The "explicitly present" check via `read_raw_keys()` matters for back-compat:
a user_preferences.json that adds a new future field (e.g., growth_every_hours
once Phase 2a lands) must not silently shadow a custom dream_every_hours
sitting in heartbeat_config.json from before this split.

Adds:
- `brain/user_preferences.py` — `UserPreferences` dataclass with atomic
  load/save and a module-level `read_raw_keys()` helper.
- 5 new precedence tests on HeartbeatConfig.load (override / missing /
  present-but-omits-field / only-user-prefs / corrupt-prefs).
- 11 new unit tests on UserPreferences and read_raw_keys.

Net: 492 tests passing (was 476, +16 new).
@hanamorix hanamorix merged commit 48db8c7 into main Apr 25, 2026
3 checks passed
@hanamorix hanamorix deleted the principle-audit-pr-c branch April 25, 2026 15:15
hanamorix added a commit that referenced this pull request May 9, 2026
…PR-C) (#15)

Per principle audit (PR-C): introduces `user_preferences.json` as the single
GUI-surfaceable cadence file. heartbeat_config.json remains, but is now
formally documented as developer-only internal calibration — decay rates,
GC thresholds, gating thresholds, reflex/research enable flags. The GUI
must never read or write that file.

Currently `dream_every_hours` is the only field in user_preferences.json,
matching the principle: the user picks how often the brain dreams; the
brain owns everything else.

Resolution order in HeartbeatConfig.load():
1. heartbeat_config.json's value (or framework default 24.0)
2. user_preferences.json's value WHEN the field is explicitly present

The "explicitly present" check via `read_raw_keys()` matters for back-compat:
a user_preferences.json that adds a new future field (e.g., growth_every_hours
once Phase 2a lands) must not silently shadow a custom dream_every_hours
sitting in heartbeat_config.json from before this split.

Adds:
- `brain/user_preferences.py` — `UserPreferences` dataclass with atomic
  load/save and a module-level `read_raw_keys()` helper.
- 5 new precedence tests on HeartbeatConfig.load (override / missing /
  present-but-omits-field / only-user-prefs / corrupt-prefs).
- 11 new unit tests on UserPreferences and read_raw_keys.

Net: 492 tests passing (was 476, +16 new).

Co-authored-by: Hana <hana@nanoclaw.local>
hanamorix pushed a commit that referenced this pull request Jun 13, 2026
…n gates

Seven items in one cut: #13 activation-baseline WAL, #20 events autouse
conftest, #6 honest image-auth comment, #32 crystallisation dedup,
#33 extractor-scored importance, #18 attunement-emotion closed as
intentional decouple, W10 soft body-energy rest gate (fail-open).
W4/W5/W8 carved out for own brainstorms; #7/#16/#9/#8/#17/#34 deferred
with reason; #15 wontfix.

Gate: 3198 backend + 41 frontend files + pnpm build + ruff clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant