Orchestrata Ultimate › Plans › briefs › 2026-10-13-ast-daemon-publishes-snapshot
Brief: the resident AST daemon publishes the snapshot; post-edit hooks stop re-serializing; the duplicate hook is retired
Source: plans/briefs/2026-10-13-ast-daemon-publishes-snapshot.md at 8a2cb42ad · last changed 2026-10-13 · first committed 2026-10-13 · 20 lines.
Prose pending.
Plan text
Brief: the resident AST daemon publishes the snapshot; post-edit hooks stop re-serializing; the duplicate hook is retired
Date: 2026-10-13 · DESIGN_WAIVER: ADR-0097 (Accepted ruling; the entire scope executes it) · Executor: builder (Sonnet) in an orchestrata worktree off origin/master · One slice, stop and report
GOAL: In a checkout served by orchestrata-ast-daemon@, one Edit leads to exactly one snapshot write performed by the daemon from its warm cache within seconds, the freshness pin reports fresh, and no serializer process is spawned by the hooks; checkouts without a daemon keep today's fallback.
FILES:
scripts/ast_daemon.py— add apublishsocket command and a watcher-driven republish after debounced invalidation; writeplans/ast-<name>.jsonatomically (temp +os.replace) using the serializer's own writer so the artifact is byte-compatible withserialize_codebase_ast.py serialize --incrementalof the same tree (same schema, samefile_meta); record last publish time and content hash in daemon state (exposed by the existing status/health command).plugins/orchestrata/project-hooks/ast-incremental-refresh.shand.claude/hooks/ast-incremental-refresh.sh(keep identical) — if<repo>/.claude/run/ast-daemon.sockis live, sendpublish(or no-op if the daemon reports it already republished after the edited file's mtime) and exit; else today's detached serialize.claude/hooks/ast_sync_hook.pyandplugins/orchestrata/hooks/ast_sync_hook.py(keep identical) — stop serializing; run the Prolog KB reindex only when the snapshot content hash changed since the last reindex (read the hash the daemon records); when no daemon is live, keep today's behaviour.- Tests (real daemon process over a real socket in a temp repo; no doubles): extend
tests/scripts/test_ast_daemon.py(publish writes a byte-compatible snapshot; watcher republishes after a.pychange;file_metahash of the edited file updated; atomic write leaves no partial file), addtests/claude/test_ast_incremental_refresh_hook.py(hook with live socket spawns no serializer and the snapshot is fresh; hook without socket falls back), extendtests/claude/test_start_ast_daemon_hook.pyonly if the start hook needs a change.@tracesto the SPEC that owns the AST daemon (find with the RTM toolkit; add a REQ line for "daemon publishes the snapshot"). - Read-only:
scripts/serialize_codebase_ast.py(writer andfile_meta; do not change its CLI),plugins/orchestrata/project-hooks/ast-freshness-pin.sh(its STALE logic must keep working unchanged),scripts/gates.py(stays the explicit full refresh;ast-sync+ reaugment ordering untouched),docs/adr/0097-*.md.
CONSTRAINTS: Every command through direnv exec <worktree> uv run … after direnv exec <worktree> uv sync --frozen (the worktree's .envrc must be allowed; if direnv exec says blocked, STOP and report — do not source files by hand, do not run direnv allow). Never system python, never .venv/bin/... by path. No test doubles. Do not touch plans/ast-ultimate.json by hand (your own daemon publish may rewrite it; that is the feature). Snapshot schema unchanged; code_intel untouched; nothing in codetools. The bare ast-sync runtime-calls zeroing hazard (see the pin hook) must not regress: a publish from the daemon must carry the same runtime-call augmentation state the serializer would, or leave gates.py's reaugment as the owner and say so in the report. Mohamed authorship, no AI footers, no push (I ship). timeout 300 per test command; observed pytest (pytest-live); --basetemp=<worktree>/.bt/<name>. ruff, mypy on changed .py, check_one_class_per_file green. Errors root-caused and fixed in-slice with fix windows noted.
DONE WHEN: (1) in a temp repo with a real daemon, an edit produces one snapshot write by the daemon within the debounce window and the edited file's file_meta hash is updated; (2) the refresh hook with a live socket spawns no serializer (prove with a process-list capture in the test) and the freshness pin reads fresh; (3) the hook without a socket still refreshes as today; (4) the sync hook reindexes only on hash change; (5) a measured number: wall from edit to fresh snapshot in the worktree itself, against 64 s; (6) tests green, gates green, commits on the branch, worktree clean.
VERIFY: direnv exec <worktree> uv run pytest-live tests/scripts/test_ast_daemon.py tests/claude/test_ast_incremental_refresh_hook.py -q green; in the worktree, after a trivial edit to one .py file (reverted afterwards): direnv exec <worktree> uv run python scripts/serialize_codebase_ast.py symbols --pattern <touched symbol> answers fresh and .cache/ast-refresh.log shows no new serialize line; bash plugins/orchestrata/project-hooks/ast-freshness-pin.sh (via _dispatch.sh) prints fresh.
Report with: the measured edit-to-fresh wall, the publish mechanism (command and watcher), test counts, what happened to runtime-call augmentation, defects with windows, and the branch name.