Problem
context_window_hours and default_context_window_hours are the only duration fields in the schema not measured in milliseconds. Every other duration field uses _ms:
heartbeat_delay_ms
polling_interval_ms
interval_ms (backup)
default_heartbeat_ms
context_window_hours, default_context_window_hours, message_max_age_days, and ceremony.event_window_hours are the odd ones out.
This creates a silent footgun: in the YAML editor, if a user types a bare 8 into a unitless/renamed key, it would be 8ms for heartbeat fields but 8 hours for context window — no error, wrong behavior.
The unit suffix was implicitly doing double duty: documentation of the expected scale, and a hint about the valid floor. We need to make both explicit.
Field Analysis
| Field |
Meaning |
Sensible minimum |
context_window_hours / default_context_window_hours |
How far back a persona can see in conversation history |
1h |
message_max_age_days |
When messages become eligible for deletion (if count > message_min_count) |
1h (or 1d in practice) |
ceremony.event_window_hours |
Delay between messages that implies a different "event" occurring |
1h |
The Fix
Phase 1 — Schema rename (this issue)
PersonaEntity.context_window_hours → context_window_ms
HumanSettings.default_context_window_hours → default_context_window_ms
HumanSettings.message_max_age_days → message_max_age_ms
CeremonyConfig.event_window_hours → event_window_ms
- Update all core logic that references these fields (
message-manager.ts, processor.ts, heartbeat-manager.ts, prompt-context-builder.ts)
- Update web frontend (
PersonaEditor.tsx, PersonaSettingsTab.tsx, PersonaIdentityTab.tsx, SettingsModal.tsx, App.tsx)
- Add a startup data migration in the processor's migration block to rename the old fields and convert existing values (hours×3600000, days×86400000) on stored data — without this, any user who has customized these fields loses their value silently on upgrade
Phase 2 — YAML enforcement + key rename (blocked on Phase 1, part of #22 follow-on)
Once everything is _ms in the schema:
- YAML keys drop the unit suffix (e.g.
context_window_ms → context_window, heartbeat_delay_ms → heartbeat_delay)
- All bare numeric values consistently mean milliseconds —
8 is always 8ms, never 8h
parseDuration enforces per-field minimums on input, rejecting nonsensical values:
context_window: minimum 1h
message_max_age: minimum 1h
event_window: minimum 1h
heartbeat_delay: minimum 1m
polling_interval: minimum 1m
formatDuration already picks the broadest whole unit, so 8h stays 8h, 3d stays 3d
Reference
Problem
context_window_hoursanddefault_context_window_hoursare the only duration fields in the schema not measured in milliseconds. Every other duration field uses_ms:heartbeat_delay_mspolling_interval_msinterval_ms(backup)default_heartbeat_mscontext_window_hours,default_context_window_hours,message_max_age_days, andceremony.event_window_hoursare the odd ones out.This creates a silent footgun: in the YAML editor, if a user types a bare
8into a unitless/renamed key, it would be 8ms for heartbeat fields but 8 hours for context window — no error, wrong behavior.The unit suffix was implicitly doing double duty: documentation of the expected scale, and a hint about the valid floor. We need to make both explicit.
Field Analysis
context_window_hours/default_context_window_hoursmessage_max_age_daysmessage_min_count)ceremony.event_window_hoursThe Fix
Phase 1 — Schema rename (this issue)
PersonaEntity.context_window_hours→context_window_msHumanSettings.default_context_window_hours→default_context_window_msHumanSettings.message_max_age_days→message_max_age_msCeremonyConfig.event_window_hours→event_window_msmessage-manager.ts,processor.ts,heartbeat-manager.ts,prompt-context-builder.ts)PersonaEditor.tsx,PersonaSettingsTab.tsx,PersonaIdentityTab.tsx,SettingsModal.tsx,App.tsx)Phase 2 — YAML enforcement + key rename (blocked on Phase 1, part of #22 follow-on)
Once everything is
_msin the schema:context_window_ms→context_window,heartbeat_delay_ms→heartbeat_delay)8is always 8ms, never 8hparseDurationenforces per-field minimums on input, rejecting nonsensical values:context_window: minimum 1hmessage_max_age: minimum 1hevent_window: minimum 1hheartbeat_delay: minimum 1mpolling_interval: minimum 1mformatDurationalready picks the broadest whole unit, so8hstays8h,3dstays3dReference
_msfields in YAML editor)