feat(wiki): safe headless wiki-authoring drain — async, throttled, anti-fabrication - #70
Merged
Merged
Conversation
…ti-fabrication Make the headless wiki-authoring drain safe to enable: rewrite the claude -p invocation as async (asyncio.create_subprocess_exec + wait_for), bound it with a per-cycle budget (concurrency / wall-clock / USD caps via CORTEX_HEADLESS_* env knobs), and gate authoring behind a groundable filter so non-derivable scopes (prd/decisions/changelog/ roadmap/accessibility/localization) are never fabricated. Reviewer fixes (REQUEST CHANGES → resolved): - BLOCK-1: _claude_invoke now kills the subprocess in a finally block when returncode is None — CancelledError (a BaseException) escaped the except clauses and leaked a zombie; the timeout path shares the kill. - BLOCK-3: _write_anchor_page mkdir moved inside the try/except OSError so a PermissionError cannot propagate through gather(return_exceptions =False) and abort the whole anchor phase. - BLOCK-2: split the 1766-line module into 6 focused modules (all ≤500), keeping headless_authoring as the public import surface; cycle code resolves monkeypatchable names from the root module at call time. - MAJOR-1: _collect_anchor_candidates wraps _build_registry().repos in its try/except. - MINOR-1/2, NIT-1: document wall_clock_ms ≡ duration_ms; drains_attempted excludes budget-skips; deterministic wall-clock test (monkeypatch CycleBudget.exhausted instead of real-sleep timing). Security controls (B-1) intact: --bare, --tools Read,Glob,Grep, ANTHROPIC_API_KEY fail-closed. 51 tests pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019o58McF4LRfvGNNXaqG2Au
ruff 0.15.20 format pass on the three hand-edited files, plus remove two unused `summary` assignments (F841) and an unused `wc` import (F401) in the throttle tests. No behavior change; 14 throttle tests still pass, ruff format --check . and ruff check . both clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019o58McF4LRfvGNNXaqG2Au
cdeust
added a commit
that referenced
this pull request
Jun 30, 2026
…ster (#72) Lets the headless wiki-authoring drain load the user's full specialist roster (architect, engineer, code-reviewer, …) and delegate read-only codebase analysis to it, instead of a single confined generalist pass. Agents mode (CORTEX_HEADLESS_AGENTS=1, default) builds the ``claude -p`` argv with ``--setting-sources user`` (loads the user roster only — project/local sources stay excluded, so a malicious repo cannot inject settings/hooks) + ``--tools Read,Glob,Grep,Task`` + a hard ``--disallowedTools Write,Edit,Bash,NotebookEdit`` ceiling. Solo mode (=0) keeps the hardened ``--safe-mode`` path. Verified live against claude CLI 2.1.197 (subscription): the roster loads; a delegated ``engineer`` subagent has NO Write tool and creates no file (the deny ceiling propagates to subagents); no file is written to disk. Hooks: ``--setting-sources user`` also loads the user's hooks, which would recurse (SessionEnd → consolidation → authoring → SessionEnd …) and pollute memory. New ``hooks/_headless_guard`` makes every Cortex hook no-op when the worker stamps ``CORTEX_HEADLESS_AUTHORING_CHILD=1`` into the child env. Prompts: per-page-kind delegation hint added to the section/page/anchor builders, gated on the agents knob (omitted in solo mode where Task is absent); the untrusted-source guard is preserved. Root-cause fix surfaced while verifying: the variadic ``--add-dir`` swallowed the trailing positional prompt ("Input must be provided"), so every drain with a source_root had been failing silently since #70. The prompt now travels via STDIN — no argv-ordering fragility. Refactor: argv/env construction extracted to ``claude_cli`` (keeps headless_authoring < 500 lines; carries the full audit-B-1 argument). Tests: agents/solo argv, hard-ceiling env flag, stdin-not-argv regression, guard short-circuit (subprocess), delegation-hint splicing. 672 hooks+handlers tests green; ruff clean. Claude-Session: https://claude.ai/code/session_019o58McF4LRfvGNNXaqG2Au Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Merged
cdeust
added a commit
that referenced
this pull request
Jun 30, 2026
…forgetting (#73) Bumps version across pyproject/server/manifest/marketplace/plugin manifests and records the 3.25.0 CHANGELOG section covering #67–#72: - feat: active forgetting module (#69), safe headless drain (#70), full zetetic agent roster for wiki authoring (#72) - fix: Windows portability (#68), subscription-default billing (#71), variadic --add-dir prompt-swallow root cause (#72) - deps: pydantic-settings 2.14.0→2.14.2 security patch (#67) Claude-Session: https://claude.ai/code/session_019o58McF4LRfvGNNXaqG2Au Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Makes the headless wiki-authoring drain safe to enable end-to-end.
Feature
claude -pinvocation (asyncio.create_subprocess_exec+wait_for) — no longer blocks the event loop.CORTEX_HEADLESS_*env knobs (defaults 4 / 300s / $5).Scope.groundablefilter — non-derivable scopes (prd/decisions/changelog/roadmap/accessibility/localization) are never authored from scratch.Reviewer fixes (code-reviewer REQUEST CHANGES → resolved)
_claude_invokekills the subprocess in afinallywhenreturncode is None.CancelledError(aBaseException) escaped theexceptclauses and leaked a zombie; the timeout path now shares the same kill. New test:test_cancellation_kills_subprocess._write_anchor_pagemkdirmoved inside thetry/except OSError, so aPermissionErrorcan't propagate throughgather(return_exceptions=False)and abort the anchor phase.headless_authoring(459, facade +_claude_invoke+ types/constants),authoring_prompts(411),page_io(392),drain_operations(353),cycle_orchestration(217),candidate_scan(123).headless_authoringstays the public import surface; cycle/scan code resolves monkeypatchable names from the root module at call time (deliberate circular import — preserves the test monkeypatch contract)._collect_anchor_candidateswraps_build_registry().reposin itstry/except.wall_clock_ms ≡ duration_msdocumented onCycleSummary;drains_attemptedexcludes budget-skips; the wall-clock test is now deterministic (monkeypatchesCycleBudget.exhaustedinstead of racing real-sleep timing).Security
B-1 controls intact and reviewer-confirmed:
--bare,--tools Read,Glob,Grep,ANTHROPIC_API_KEYfail-closed.Verification
51 passed—pytest tests_py/handlers/test_headless_authoring_throttle.py tests_py/handlers/test_consolidate.py tests_py/handlers/test_consolidate_telemetry.py tests_py/core/test_wiki_coverage.py🤖 Generated with Claude Code
https://claude.ai/code/session_019o58McF4LRfvGNNXaqG2Au