Skip to content

calibration_profile default holder 'garry' never matches the 'self' holder that consolidate writes → forecasting loop silently no-ops on every non-owner brain #2464

Description

@devty

Summary

The takes/calibration pipeline writes and reads takes under two different hardcoded holder values, so the calibration loop can never see its own data on any brain that isn't the upstream owner's:

  • The dream consolidate phase inserts takes with holder: 'self' — src/core/cycle/phases/consolidate.ts:194 (INSERT into takes(kind='fact', holder='self', …)).
  • The dream calibration_profile phase reads the scorecard with holder ?? 'garry' — src/core/cycle/calibration-profile.ts:229.

Both run in the same cycle. One writes self, the sibling reads garry, so getScorecard({holder:'garry'}) returns resolved: 0, the phase takes its cold-brain branch, and you get:

✓ calibration_profile  holder=garry has only 0 resolved takes (need >=5 for a profile)

…even on a brain with many takes. get_calibration_profile then always returns null.

Why this is a placeholder bug, not config

'garry' is hardcoded as a personal default across the calibration/forecasting surface:

  • src/core/cycle/calibration-profile.ts:98,229
  • src/core/cycle/emotional-weight.ts:50 — export const DEFAULT_USER_HOLDER = 'garry'
  • src/core/think/index.ts:296
  • src/commands/calibration.ts:161,243,248
  • src/commands/doctor.ts:1206 — health check hardcodes WHERE holder = 'garry', so the doctor surface is also blind on forks

A registered config key already exists for exactly this — emotional_weight.user_holder (src/core/config.ts:884), which brainstorm/orchestrator.ts:626 honors (config.emotional_weight?.user_holder ?? 'garry'). The calibration phase, the think path, and doctor ignore it and hardcode the literal.

There's also a third convention: extract-takes-from-pages.ts:128 defaults holder ?? 'system'. So three holder vocabularies (self, garry, system) coexist with no single source of truth.

Reproduction

  1. On any brain that isn't the upstream owner's, let consolidate run (writes holder='self' takes).
  2. Run the calibration_profile phase (or gbrain dream).
  3. Observe the holder=garry has only 0 resolved takes skip despite gbrain takes-list showing many takes with holder='self'.
  4. get_calibration_profile → null forever.

Expected

The phase that reads takes for calibration should query the same holder convention the pipeline writes. The profile should build once ≥5 resolved bets exist, regardless of whose brain it is.

Proposed fix

Single source of truth for "the brain owner's holder," resolved in one place and consumed everywhere (calibration_profile, think, doctor, emotional-weight):

opts.holder ?? config.emotional_weight?.user_holder ?? brainIdentityHolder ?? 'self'

i.e. make consolidate's 'self' and the calibration default agree (either both self, or both resolved from emotional_weight.user_holder / brain identity), and route doctor.ts:1206 + think through the same resolver instead of the bare literal.

Related

Stacked behind #2079 (takes list slug footgun) and #2411 (takes propose promotion path missing) — all three sit between "proposals exist" and "a profile appears." Holder alignment is necessary even after those land. Model-resolution sibling fixes in #2451 / #2452. Verified against master v0.42.53.0.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs-reviewNeeds a human skim to classifyp3P3: low priority / needs glance

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions