Skip to content

feat(custom-profiles): opt-in domain profiles — Hacker News chronicle + Hermes situational tracker - #21

Merged
dimknaf merged 6 commits into
mainfrom
feat/custom-profiles
Jun 26, 2026
Merged

feat(custom-profiles): opt-in domain profiles — Hacker News chronicle + Hermes situational tracker#21
dimknaf merged 6 commits into
mainfrom
feat/custom-profiles

Conversation

@dimknaf

@dimknaf dimknaf commented Jun 26, 2026

Copy link
Copy Markdown
Owner

What

Adds custom profiles — opt-in, self-contained overlays that reshape what BrainDB ingests and how its
auto-maintained wikis read, with zero effect on defaults when inactive — plus two ready-to-run profiles
and a pair of wiki-writer robustness fixes. Bumps the version to 0.9.0.

Why / what's exciting

The wiki pipeline was one-size-fits-all. Profiles let a deployment teach the maintainer/writer how to
name, structure, and source pages for a domain — from one folder, one env switch — without forking core:

  • hackernews (keyless) — polls Hacker News and turns the firehose into company/project/tech wikis
    with a dated current-developments chronicle that ages gracefully into background.
  • hermes — on a schedule, asks an external web-search Hermes agent a dated, stateless question and
    folds each answer into a live, dashboard-shaped situational page (status · confidence · now ·
    recent-days · recent-weeks · forecast · history — every line dated + [[ref:UUID]]-cited), while the
    other entities each report mentions get their own cross-linked wikis. Dormant by default; a dry-run
    sample exercises the whole pipeline offline.

How

  • New composition layer (braindb/custom_profile.py, braindb/profile_runner.py): .add.md / .replace.md
    prompt fragments + an optional supervised ingestor.py per profile. Core API, schema, and base prompts
    are untouched; everything is additive and gated by CUSTOM_PROFILE.
  • custom-profiles/: example/ template, hackernews/, hermes/, gdrive/. Each profile's real
    .env/state is gitignored; only .env.example templates are tracked.

Also in this release

  • fix(wiki): reject blank/garbage writer bodies before persist (stops empty, high-revision wikis).
  • fix(wiki): raise scheduler write-timeout default 1200 → 2400s for slower local models.
  • feat(wiki): give the maintainer/writer prompts the current date.

dimknaf added 6 commits June 22, 2026 16:02
Add a custom-profiles mechanism with zero impact when no profile is active:
one CUSTOM_PROFILE switch names the active profile(s); each lives in its own
custom-profiles/<name>/ folder. Prompt fragments (<target>.add.md /
<target>.replace.md) shape the wiki maintainer/writer; an optional ingestor.py
feeds data/sources/ via the existing watcher, supervised by a new
profile_runner sidecar that sleeps when no profile is set.

- braindb/custom_profile.py: brace-safe add/replace prompt composition (multi-profile)
- braindb/profile_runner.py: dormant ingestor supervisor (restart-on-exit)
- braindb/routers/wiki.py: wrap maintainer + writer assembly with profile add/replace
- docker-compose.yml: CUSTOM_PROFILE env + profile_runner service
- custom-profiles/SKILL.md + README + example/: profile authoring guide
- custom-profiles/hackernews/: keyless worked example (HN -> tech-entity wikis)
- custom-profiles/gdrive/: Google Drive folder follower, incremental + diff-aware
- tests/test_custom_profile.py
Append a 'Current date (UTC)' line to both the maintainer and writer prompts (additive, in wiki.py) so temporal reasoning (recency, 'as of', dated/bucketed sections) has an anchor. Ground the hackernews chronicle-aging rule to that date and add a consistent dated-line format.
Scheduled ask to an external Hermes web-search agent -> a dated report dropped as a datasource -> a central 'Hormuz status' situational wiki with retrospective now/recent-days/recent-weeks/forecast/history sections. Adds query.md, ingestor.py, wiki_writer.add.md, wiki_maintainer.add.md, sample_answer.md, README, .env.example. .gitignore whitelists the profile but keeps .env/.state/__pycache__ out (the Hermes bearer token stays local).
A weak writer model sometimes emits a body that is serialization garbage
(`""`, `body=""`, `{}`) rather than a real page. The previous gate only
tested `.strip()` (whitespace), so quote/brace junk slipped through, got
written verbatim as the wiki content, and bumped the revision -- which then
self-perpetuated into empty, high-revision pages (a marker-less page can no
longer be section-edited, so every later cycle re-emits junk and re-bumps).

Add a single `_is_blank_body()` definition (matches ONLY pure
punctuation/whitespace, optionally wrapped as `body=...`; any real body has
letters/markup and never matches), use it for the existing empty-body gate,
and add one backstop guard on the final body right before persist -- routing
blank bodies to the existing release_or_fail_jobs path instead of writing
them. The legitimate empty-body cases (attach no-op, attach after section
edits) return earlier and are untouched, so this only rejects genuinely
unpersistable writes. Healing-positive: a rejected entity stays a healable
orphan and is retried next cycle; good pages can no longer be overwritten by
junk.
The scheduler's HTTP read-timeout on /wiki/maintain and /wiki/write (knob
WIKI_AGENT_TIMEOUT, default 1200s / 20 min) is too tight for smaller/slower
quantised local models (e.g. Gemma 12B QAT), whose full-body writes can
exceed 20 min. At 1200s the scheduler abandoned slow-but-completing writes
and forced needless retries. Bump the default to 2400s (40 min) and sync the
.env.example docs. This only controls the scheduler client's patience -- the
API is not bounded by it and a write past the deadline still commits.
@dimknaf
dimknaf merged commit 141f7f8 into main Jun 26, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant