NexBoard Build Journal — Mon 04 May – Sun 10 May 2026
What shipped
- HawkInc memory system — persistent, file-based long-term memory across Claude Code sessions.
INDEX.mdloads at startup, project topic files load on task match, department indexes load only when explicitly needed. ~400 tokens overhead for typical sessions - Synthesis context overflow fixed — weekly memory synthesis was silently failing; routing slip had grown to 544KB; brief files now embedded, raw session logs listed as file paths only; prompt dropped to ~16KB
- Dispatcher body truncation pattern — 3,000-char hard cap on routing slip bodies documented; file-reference pattern established for large-payload tasks: write content to a file, pass only the read instruction in the body
- Synthesis dual-write — synthesis tasks now write to both the deliverables path and a permanent archive using explicit dual-path instructions in the prompt, since the dispatcher’s mandatory instructions override any single alternative path
- Gitea @claude webhook spec — architecture designed for replicating GitHub’s native @claude feature on self-hosted Gitea: webhook receiver → context assembly → agent dispatch → comment-back flow
The interesting problem
The weekly memory synthesis was consistently reporting “only 1 of 7 agents’ memories provided” — but the task was being dispatched and completing without errors.
The routing slip body was 544KB (~136K tokens). The agent was seeing only the CEO section at the top before running out of context. No error, no truncation warning — just a quiet partial synthesis.
The cause: _build_prompt() in synthesise_memories.py was embedding raw session log files directly into the routing slip. Those files are 50–62KB each.
The fix splits files by type based on filename:
brief_files = [f for f in memory_files if "-brief" in f.name]
raw_files = [f for f in memory_files if "-brief" not in f.name]
for f in brief_files: # ~300 bytes each — embed in full
prompt += f.read_text()
for f in raw_files: # 50-62KB each — reference only
prompt += f"Read if needed: {f.name}\n"
Brief files (~300B daily summaries) are embedded. Raw session logs are listed by filename — the agent reads them via its filesystem tool if it needs the detail. Prompt dropped from 544KB to ~16KB, all seven agents now synthesised in a single pass.
Decision of the week
Load memory indexes at startup or on demand?
The memory system has three department indexes covering deliverables, vault files, and departmental content — roughly 17,000 tokens combined. Loading all three at every session start would burn that budget on every short task.
The system loads only the top-level INDEX.md (~150 tokens) and the relevant project index (~250 tokens) at startup. Department indexes load only when the task explicitly needs to retrieve deliverables or vault content.
~400 tokens for 90% of sessions. A bug-fix session pays 400 tokens in memory overhead, not 17,000.
What surprised me
The synthesis had been running with a 544KB routing slip since the feature launched. The task always completed, the deliverable was always written — it just covered only the first agent alphabetically and called it a synthesis. The signal was the output quality, not any error state.
I only noticed when I re-read a report and found nothing from the Engineering or Finance agents.
What’s next
Memory synthesis now works — next is scheduling it weekly via cron and validating output quality over a few runs. The Gitea @claude webhook is spec’d; implementation means adding a webhook receiver to NexBoard and wiring it into the existing dispatch pipeline.
Building NexBoard — a self-hosted AI Chief of Staff, built entirely with Claude Code.
Comments