* feat(mail): add Microsoft 365 Graph sync source with delta state
Adds a GET-only Microsoft Graph source to bin/mail/sync (client-credentials,
delta query). Wires it into the CLI (--source m365), the worker pool and the
compose stack:
- m365.go: M365Client (token cache, delta pagination, message normalization,
attachment download) + m365Source adapter (per-mailbox folders + delta link)
- sync.go: M365Credentials in SyncConfig; Committer interface so delta links
only advance after a fully successful run (no skips on failure)
- cli.go: --source m365 with M365_/MS_ env passthrough
- Dockerfile: mail-build stage produces /mail-sync into the index image
- docker-entrypoint: mail-sync loop (sync -> import -> index, default 10s)
- compose.yaml: mail-sync service (index image, kb-var volume, secrets ro)
* feat(mail): adapt M365 ETL loop for OO+Gmail bots.
Default compose mail-sync to onlyoffice,gmail every 300s with import on new
mail; gate full --rebuild behind MAIL_SYNC_INDEX. Fix mail-build stage order
and add bin/stack/start-mail-sync.
Store valid_from/valid_to on leafs and filter search by calendar day without
overloading D16 source staleness; stack start/start-assistant wires brain + PicoClaw.
Ladybug 0.19 stays FTS/HNSW queryable on MERGE of new ids; DROP INDEX
was the fatal path. bin/brain/add.go and POST /ingest land facts+info
in one transaction so watch/mail/git can become leafs now (Gitea #14).
Read path is cgo like search. Control questions live in rank so CI can
test them without ladybug. Python bin/kb/{get,stats,eval} stays the
runner fallback.
Commands live at bin/brain/{index,get,stats,eval,watch}.go and
bin/mail/import.go, bin/markdown/import.go, bin/postgres/query.go.
Python remains the Ladybug write worker. index_mail is a deprecation
shim that rebuilds via --with-mail.
* feat(brain): HTTP serve from bin/brain/serve.go, default Go search binary.
Move the HTTP package to internal/httpapi. Default backend is
var/bin/brain-search, not Python. bin/serve.go stays as a deprecation shim.
* feat(httpapi): default search backend is var/bin/brain-search.
bin/brain/serve.go is the command; bin/serve.go stays as a tagged
deprecation shim. Tests fail if the default path still names Python.
Collapse nested kbsearch/chats go.mod into the root module. Ranking stays
cgo-free under internal/brain/rank so CI does not need ladybug. bin/kb/search
is a deprecation wrapper that still sets CGO and builds the binary.
* Add --query to Gmail mail/sync instead of always listing in:inbox.
Callers keep the search string; default remains in:inbox.
* Document Gmail --query on the mail/sync pipeline.
* test(mail): assert Gmail --query reaches ListIDs, not only the CLI flag.
ParseCLI coverage left a hole: an empty query still has to become in:inbox
and a custom q has to be the string the client lists with.
Move serve/ (module) -> bin/server, tools/ -> bin/tools, replace bin/kb-watch
bash with bin/watch Go package; self-executing Go shebangs bin/serve.go and
bin/kb/watch.go; Docker + CI + git/import + docs repointed. Multi-stage image
builds static serve+watch binaries (no Go runtime in container).
- bin/db/psql-yq + bin/web/search + tools/{yamlout,websearch} vendored as real files
- bin/db/ssh-tunnel added (OnlyOffice VM pg on 5433)
- skills reference local bin/ paths; no agent-skills/abs links in git
- pyproject.toml + uv.lock; CI installs via uv sync --frozen
- release-please auto-tags semver from conventional commits when green
- LICENSE MIT, badges/mermaid README