What I found
MEMORY/STATE/events.jsonl is documented as the Unified Event System. Two
shipped handlers write it:
hooks/handlers/MemoryDirIntegrity.ts — path constant :36, append :42
hooks/handlers/KnowledgeConformance.ts — path constant :47, append :53
(new this release)
Programmatic readers: zero, in every release including this one. The write
side gained an emitter at 7.28.3; the read side is still empty.
Around that gap the documentation describes a system that does not exist.
appendEvent() — the hook-facing emitter — has never shipped.
HookSystem.md:1555 says "Hooks call appendEvent() as a secondary write
alongside their existing state writes", :1559-1560 shows the call, :1566
describes a field it auto-injects. No file under hooks/ imports or defines it.
The one shipped function with that exact name is LIFEOS/PULSE/Conduit/store.ts:20,
which writes a per-day Conduit log; LIFEOS/TOOLS/Conveyor/Ledger.ts:142 exports
appendEvents (plural) and writes STATE/content-pipeline/events.jsonl. Neither
is this file.
HookSystem.md:1550 ### Components is a heading with an empty body — two
blank lines, then the next heading.
The event-type table names emitters that do not emit.
HookSystem.md:1575-1589 maps 13 event categories to 11 named hooks. Checked
against the payload:
RebuildSkill (DocRebuild handler) (:1584) has no file anywhere in the
tree. git grep RebuildSkill returns exactly one hit: that table row.
VoiceNotification (:1581) is a handler with no registration of its own; it
is reached through VoiceCompletion.hook.ts, and it writes
MEMORY/VOICE/voice-events.jsonl.
- None of the 11 writes
STATE/events.jsonl. The two handlers that actually
write it appear nowhere in the table.
Two documents contradict each other about the channel, and one contradicts
itself.
HookSystem.md:1616 still says "EventLogger.hook.ts is now the single
observability writer" — EventLogger writes MEMORY/OBSERVABILITY/*.jsonl, a
different store — while :1555, in the same file, describes hooks calling
appendEvent() into this one.
NotificationSystem.md:299 heads a section "Event Log Channel (events.jsonl)"
whose body at :302 describes MEMORY/OBSERVABILITY/*.jsonl written "directly
from each hook via fs.appendFileSync — … no shared transport library", which
is the opposite of the appendEvent() API described one document over.
MemorySystem.md:647 asserts "All hooks listed above also emit typed events to
STATE/events.jsonl via appendEvent()". The table it annotates (:625-645)
contains no writer of this file, and four of its rows name hooks that do not
exist under hooks/ at all (RelationshipMemory, TeammateIdle,
SecurityPipeline, PreCompact).
A phantom hook is presented as installable. ULWorkSync.hook.ts ships in no
release, is in no hooks.json, and Work/WorkSystem.md:69 says so plainly
("Private component — NOT in the public release payload"). Three other places
still present it as shipped: HookSystem.md:121 and :1401 list it in hook
inventories, and HookSystem.md:107 puts it inside a copy-pasteable
hooks.json configuration example, with a timeout. A reader following that
example registers a file they do not have.
Adjacent, same class. TOOLS/IntegrityCheck.ts:5 names /ic as its invoker;
the payload ships four commands (context-search, cs, isdown, lu) and /ic
is not among them. TOOLS/DocCheck.ts has zero invokers while its docstring
(:8-9) claims it replaces DocCrossRefIntegrity and the
IntegrityCheck/SystemIntegrity handlers — which are the handlers still wired
at SessionEnd.
Nothing regressed here. The loop-closing pieces were never built in the public
artifact.
For corroboration from a fork running this code with local instrumentation: 83
DocIntegrity events and 229 memory_dir rows accumulated over 23 days; a finding
set decayed 19 → 16 across that window; none of it was ever seen through this
channel, and the one finding that closed, closed via a commit.
What I propose
There are two coherent answers and I would rather put both to you than assume.
Option A — complete the loop (PR-A implements this).
hooks/lib/events.ts — the appendEvent() your docs describe, plus an
emitFindingSet() and a reader. The emission contract stated normatively: one
event per completed check carrying the full finding set including the empty
set, no event on a skipped check, finding keys free of counts and timestamps.
Without that, a fixed finding can never leave the digest.
hooks/lib/advisory-readback.ts + a fourth LoadContext loader —
SessionStart is where injection works, and the next session's start is the
actionable moment for a finding produced at the previous session's end. Emits
only when the finding set changed, plus a slow re-announce so a real finding
cannot go silent after one impression. Steady state is zero characters.
- The Pulse feed —
observability.ts has no STATE/events.jsonl constant
(its constant block is :76-84), and /api/events/recent (:911, routed at
:4333) merges four other JSONLs. Adding the constant and merging it in is a
drop-in change.
- Doc repair — fill
### Components, cut the event-type table to real
emitters, reconcile :1555 with :1616, fix NotificationSystem.md:299-302
and MemorySystem.md:647, and stop presenting ULWorkSync as installable.
Option B — excise it. Delete the two writers and the Unified Event System
section. If nothing is ever going to read this file, a documented channel that
does not exist costs more than no channel.
I have written A because the machinery is already almost all there and the fix is
small. If B is your answer, say so and I will cut that PR instead — it is
strictly less work.
One thing that changed this release and is worth naming, because it removes an
argument I would otherwise have made: the Pulse pane that promised this file's
contents is gone. LiveEvents.tsx — which said "Events from events.jsonl will
appear here in real-time" — was deleted in the Observability churn, and the string
survives nowhere in the tree, build output included. The current pane
(src/components/activity/ObservabilityDashboard.tsx, on the Agents ACTIVITY tab)
names no file. So there is no broken user-visible surface here any more. What is
left is a write-only file and four documents describing machinery that was never
built, which is a smaller problem than I would have reported a week ago.
Questions I could not answer from the public tree
- (Q-a) Does your private tree have a live
STATE/events.jsonl consumer? That
answer decides Option A vs Option B, and nothing else in this issue depends on it.
- (Q-b) Rotation policy.
hooks/README.md:460 says observability JSONLs are
not auto-rotated; under Option A this file gains readers. PR-A's reader scans
backwards from EOF in bounded windows so growth costs the SessionStart path
nothing, but the file still grows forever.
- (Q-c) Is
TOOLS/DocCheck.ts the intended deterministic replacement for this
family? Its docstring says so, it has no invoker, and the handlers it claims to
replace are the wired ones. If DocCheck is the future, PR-A shrinks to the
emitter plus readback as a stopgap.
Related
What I found
MEMORY/STATE/events.jsonlis documented as the Unified Event System. Twoshipped handlers write it:
hooks/handlers/MemoryDirIntegrity.ts— path constant:36, append:42hooks/handlers/KnowledgeConformance.ts— path constant:47, append:53(new this release)
Programmatic readers: zero, in every release including this one. The write
side gained an emitter at 7.28.3; the read side is still empty.
Around that gap the documentation describes a system that does not exist.
appendEvent()— the hook-facing emitter — has never shipped.HookSystem.md:1555says "Hooks callappendEvent()as a secondary writealongside their existing state writes",
:1559-1560shows the call,:1566describes a field it auto-injects. No file under
hooks/imports or defines it.The one shipped function with that exact name is
LIFEOS/PULSE/Conduit/store.ts:20,which writes a per-day Conduit log;
LIFEOS/TOOLS/Conveyor/Ledger.ts:142exportsappendEvents(plural) and writesSTATE/content-pipeline/events.jsonl. Neitheris this file.
HookSystem.md:1550### Componentsis a heading with an empty body — twoblank lines, then the next heading.
The event-type table names emitters that do not emit.
HookSystem.md:1575-1589maps 13 event categories to 11 named hooks. Checkedagainst the payload:
RebuildSkill (DocRebuild handler)(:1584) has no file anywhere in thetree.
git grep RebuildSkillreturns exactly one hit: that table row.VoiceNotification(:1581) is a handler with no registration of its own; itis reached through
VoiceCompletion.hook.ts, and it writesMEMORY/VOICE/voice-events.jsonl.STATE/events.jsonl. The two handlers that actuallywrite it appear nowhere in the table.
Two documents contradict each other about the channel, and one contradicts
itself.
HookSystem.md:1616still says "EventLogger.hook.tsis now the singleobservability writer" — EventLogger writes
MEMORY/OBSERVABILITY/*.jsonl, adifferent store — while
:1555, in the same file, describes hooks callingappendEvent()into this one.NotificationSystem.md:299heads a section "Event Log Channel (events.jsonl)"whose body at
:302describesMEMORY/OBSERVABILITY/*.jsonlwritten "directlyfrom each hook via
fs.appendFileSync— … no shared transport library", whichis the opposite of the
appendEvent()API described one document over.MemorySystem.md:647asserts "All hooks listed above also emit typed events toSTATE/events.jsonlviaappendEvent()". The table it annotates (:625-645)contains no writer of this file, and four of its rows name hooks that do not
exist under
hooks/at all (RelationshipMemory,TeammateIdle,SecurityPipeline,PreCompact).A phantom hook is presented as installable.
ULWorkSync.hook.tsships in norelease, is in no
hooks.json, andWork/WorkSystem.md:69says so plainly("Private component — NOT in the public release payload"). Three other places
still present it as shipped:
HookSystem.md:121and:1401list it in hookinventories, and
HookSystem.md:107puts it inside a copy-pasteablehooks.jsonconfiguration example, with a timeout. A reader following thatexample registers a file they do not have.
Adjacent, same class.
TOOLS/IntegrityCheck.ts:5names/icas its invoker;the payload ships four commands (
context-search,cs,isdown,lu) and/icis not among them.
TOOLS/DocCheck.tshas zero invokers while its docstring(
:8-9) claims it replacesDocCrossRefIntegrityand theIntegrityCheck/SystemIntegrityhandlers — which are the handlers still wiredat SessionEnd.
Nothing regressed here. The loop-closing pieces were never built in the public
artifact.
For corroboration from a fork running this code with local instrumentation: 83
DocIntegrity events and 229
memory_dirrows accumulated over 23 days; a findingset decayed 19 → 16 across that window; none of it was ever seen through this
channel, and the one finding that closed, closed via a commit.
What I propose
There are two coherent answers and I would rather put both to you than assume.
Option A — complete the loop (PR-A implements this).
hooks/lib/events.ts— theappendEvent()your docs describe, plus anemitFindingSet()and a reader. The emission contract stated normatively: oneevent per completed check carrying the full finding set including the empty
set, no event on a skipped check, finding keys free of counts and timestamps.
Without that, a fixed finding can never leave the digest.
hooks/lib/advisory-readback.ts+ a fourthLoadContextloader —SessionStart is where injection works, and the next session's start is the
actionable moment for a finding produced at the previous session's end. Emits
only when the finding set changed, plus a slow re-announce so a real finding
cannot go silent after one impression. Steady state is zero characters.
observability.tshas noSTATE/events.jsonlconstant(its constant block is
:76-84), and/api/events/recent(:911, routed at:4333) merges four other JSONLs. Adding the constant and merging it in is adrop-in change.
### Components, cut the event-type table to realemitters, reconcile
:1555with:1616, fixNotificationSystem.md:299-302and
MemorySystem.md:647, and stop presentingULWorkSyncas installable.Option B — excise it. Delete the two writers and the Unified Event System
section. If nothing is ever going to read this file, a documented channel that
does not exist costs more than no channel.
I have written A because the machinery is already almost all there and the fix is
small. If B is your answer, say so and I will cut that PR instead — it is
strictly less work.
One thing that changed this release and is worth naming, because it removes an
argument I would otherwise have made: the Pulse pane that promised this file's
contents is gone.
LiveEvents.tsx— which said "Events from events.jsonl willappear here in real-time" — was deleted in the Observability churn, and the string
survives nowhere in the tree, build output included. The current pane
(
src/components/activity/ObservabilityDashboard.tsx, on the Agents ACTIVITY tab)names no file. So there is no broken user-visible surface here any more. What is
left is a write-only file and four documents describing machinery that was never
built, which is a smaller problem than I would have reported a week ago.
Questions I could not answer from the public tree
STATE/events.jsonlconsumer? Thatanswer decides Option A vs Option B, and nothing else in this issue depends on it.
hooks/README.md:460says observability JSONLs arenot auto-rotated; under Option A this file gains readers. PR-A's reader scans
backwards from EOF in bounded windows so growth costs the SessionStart path
nothing, but the file still grows forever.
TOOLS/DocCheck.tsthe intended deterministic replacement for thisfamily? Its docstring says so, it has no invoker, and the handlers it claims to
replace are the wired ones. If DocCheck is the future, PR-A shrinks to the
emitter plus readback as a stopgap.
Related
ULWorkSyncabove is a different shape of the same problem: not an unregisteredhook, an unshipped one presented as registerable.
LoadContextdelivery point from the other direction.