You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix: resolve issues found by post-merge review (PR #40) (#40)
* fix(events): split JSONL on newline only, not str.splitlines()
str.splitlines() also breaks on \r, \v, \f, NEL (U+0085) and Unicode line
separators U+2028/U+2029 -- all legal *unescaped* inside JSON strings. A
single event whose text (e.g. ASSISTANT_MESSAGE/TOOL_RESULT) carried one
was one physical line with no \n, but splitlines() shredded it, so replay()
raised a spurious 'corruption' error (whole session failed) and stream()
crashed. Split on \n only and rstrip \r for CRLF tolerance.
Test: a record containing NEL round-trips through replay without shredding.
* fix(events): fsync parent dir on new session file (durability)
With fsync=True, content was fsync'd but a newly-created session file's
directory entry wasn't durably linked, so a crash could lose a just-created
(content-fsync'd) file. fsync the parent dir once on file creation; guarded
for filesystems that don't support directory fsync.
* fix(config): drop dead HIVE_MAX_TURNS / HIVE_SESSION_TIMEOUT env mappings
These mapped to model.max_turns / model.session_timeout, which ModelConfig
does not define -- pydantic silently ignored them, so setting either env
var was a no-op. Removed the dead rows (the remaining mappings all target
real fields).
* fix(daemon): fresh identity in checkpoint + drop duplicate crisis line
- update_narrative reloads/saves its own identity copy, so the in-memory
identity passed to checkpoint.save was stale (missing the just-appended
entry/chapter). Reload before snapshotting.
- Skip the derived mood line when in crisis: the suffering fragment already
emits the crisis directive, so 'overwhelmed' duplicated it in the prompt.
* fix(store): mark only the nudges just read as delivered (by id)
get_pending_nudges did SELECT (delivered=0) then UPDATE delivered=1 for the
whole agent, so a nudge inserted between the two statements (e.g. by another
process) was marked delivered without being returned -- silently lost. Now
it updates only the nudge_ids actually read. Test: a nudge added after a
read is still delivered exactly once on the next read.
* docs(changelog): cover mood, chaptered narrative, narrative-in-prompt + F1 in 0.5.4
The 0.5.4 entry listed only the durability work, but #34/#35/#38/#39 all
merged into this unreleased version. Add Added entries (mood model,
chaptered narrative, narrative-in-prompt, F1 coverage) and a Fixed section
for the review follow-ups. Mirrored in docs/changelog.md.
* test(store): exercise the actual nudge SELECT-vs-UPDATE race (Greptile)
The prior test only validated sequential delivery -- the old
WHERE delivered=0 sweep passed it too. New test commits a competing nudge
from a separate connection in the window between the SELECT and the UPDATE
(via a sync execute wrapper that preserves aiosqlite's await/async-with
duality). It fails against the old sweep (n2 marked delivered and lost) and
passes with the by-id UPDATE.
Copy file name to clipboardExpand all lines: docs/changelog.md
+10-1Lines changed: 10 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,11 +2,20 @@
2
2
3
3
## [0.5.4] -- 2026-06-01
4
4
5
-
Durability & dependency-hardening release. All changes are additive and backward compatible; existing databases upgrade automatically on first open.
5
+
Durability, simulation, and hardening release. All changes are additive and backward compatible; existing databases and identities upgrade automatically.
6
6
7
7
### Added
8
+
-**Derived mood model**: a `CircumplexMood` maps `happiness` + `suffering` onto a valence/arousal circumplex and names the mood (content/motivated/steady/restless/discouraged/anxious/overwhelmed). Pure/derived, swappable via `MoodRegistry`, surfaced in the goal-pursuit prompt.
9
+
-**Chaptered narrative**: the narrative seals into compact `Chapter` summaries (date span + entry count + goal theme) on overflow instead of FIFO-dropping; the preamble shows a "Story so far" section; `AgentIdentity.full_narrative()` exposes the full history.
10
+
-**Identity narrative in the runtime prompt**: the goal-*pursuing* agent now sees its persistent self (name + accumulated narrative), not just goal generation.
8
11
-**Event-log fsync durability (C5)**: `EventLog(fsync=True)` flushes and `os.fsync()`s every append so a power/OS crash can't lose an acknowledged event. Gated by the `event_log_fsync` config option (env `HIVE_EVENT_LOG_FSYNC`), default off to protect the hot heartbeat write path; the daemon honors it. Reads tolerate a torn/partial last line.
9
12
-**`HiveStore.delete_agent()`**: deletes an agent and, via cascade, all of its child rows in one call.
13
+
-**Test coverage (F1)**: first tests for the CLI, the MCP server protocol, and the structured logging writer/reader.
14
+
15
+
### Fixed
16
+
-**Event log no longer shreds records with Unicode line separators**: `replay()`/`stream()` split on `\n` only (not `str.splitlines()`, which also breaks on `\r`/`\f`/NEL/`U+2028`/`U+2029`, all legal unescaped in JSON).
17
+
-**Nudge delivery race**: `get_pending_nudges` marks only the nudges it actually read as delivered, so one inserted concurrently isn't lost.
18
+
- Dropped dead `HIVE_MAX_TURNS`/`HIVE_SESSION_TIMEOUT` env mappings; checkpoints snapshot the freshly-saved identity; the crisis directive isn't duplicated in the pursuit prompt; event-log fsync also fsyncs the parent directory on file creation.
10
19
11
20
### Changed
12
21
-**FK cascades (C3)**: every child table's foreign key to `agents` (`sessions`, `goals`, `nudges`, `schedules`, `sub_agents`, `tasks`, `alarms`) now declares `ON DELETE CASCADE`; a `user_version` 1->2 migration rebuilds existing tables to add it (data preserved). FK enforcement is opt-in per operation, so writing child rows for not-yet-persisted agents keeps working.
0 commit comments