Install
openclaw skills install @indigokarasu/ocas-finchOCAS self-improvement orchestrator (Darwin's finch — adaptive evolution). Mines session JSONL files to detect corrections, breakthroughs, methodologies, course-changes, and behavioral directives (Always/Never). Routes each finding to the optimal storage tier: MEMORY.md, skill files, reference files, or Chronicle KG. Compacts MEMORY.md by routing entries to the correct tier. Part of the OCAS System Evolution Layer alongside Mentor, Fellow, and Forge. NOT for real-time behavioral adaptation, skill evaluation, or skill creation.
openclaw skills install @indigokarasu/ocas-finchPUBLIC REPO — GENERICISE EVERY REFERENCE. This skill is published publicly. Reference files are distilled from real runs, so never write a real name, email, employer, thread id, task id, phone number, token or home path into one — use the placeholders in
references/reference-file-workflow.md("Genericise Before You Write"). Runpython3 scripts/check_no_pii.pybefore committing; CI enforces it. And route before you write: this directory is for finch's OWN docs only. A finding about another system (cron, MCP, Gmail, patch, OAuth, git) goes to that skill or to<fs-root>/references/(local, unpublished) — never here.
Finch is the OCAS System Evolution Layer's self-improvement orchestrator. It runs as a set of cron jobs — see Manual run & verification below for the actual deployed job set (the design doc's finch:work is not currently a separate deployed cron). Jobs are primarily pure-LLM cron prompts, plus one no_agent script floor (finch:floor). Deprecated scripts are in archive/.
Signal sources (7): cron health, email, calendar, sessions, Drive, kanban, system. See references/scan-work-architecture.md for the full table.
When invoked interactively, present a two-level menu. See references/interactive-menu.md for the full two-level menu layout, Clarify timeout behavior, response parsing, and platform adaptation.
ocas-finch owns its core domain operations.
ocas-finch does not own: trigger detection, session management, or cross-skill orchestration (those belong to the calling agent).
finch.mine or finch.runmemory tool refuses edits, ~79%+ warning threshold): RUN finch.compact / memory_guard.py — do NOT hand-edit MEMORY.md to dodge the cap. Manual memory surgery (condensing entries into one giant block, dropping context to fit) is exactly the work Finch owns, and doing it by hand produces bloat (a single ~1,900-char entry where tier-routing would have moved procedure out to a skill/reference). The user's correction (2026-07-15): "Why are you manually cleaning up memory? You have Finch for that." If the memory tool refuses an edit at capacity, hand it to Finch (force ocas-finch:daily or run memory_guard.py --file ~/.hermes/profiles/<profile>/memories/MEMORY.md); do not fight the limit with manual memory writes. Finch owns compaction; this is the procedure, not "there is no skill."See references/storage-layout.md for the full directory tree and skill package structure.
The full operational detail for each item below lives in references/scanning-gotchas.md (one-line pointers, full bodies there):
references/scanning-gotchas.mdtool_call() for MCP tools fails with "not a deferrable tool" in cron — MCP tools registered in the agent's tool list (e.g. mcp_google_workspace_search_gmail_messages, mcp_google_workspace_get_events, mcp_google_workspace_list_drive_items) cannot be invoked via tool_call() in cron context. Fallback: use googleapiclient directly (see gws_direct_puller.py in the skill's scripts/) or Composio's COMPOSIO_MULTI_EXECUTE_TOOL. Email: check toolkit connection status first; if absent, use cached-data fallback (last_email_check_<account>.json in commons/data/ocas-dispatch/). Calendar/Drive: attempt Composio first, skip only on connection failure. Do NOT retry tool_call() for the same MCP tool in the same run — it will fail identically. (Confirmed 2026-07-27 finch:scan)references/scanning-gotchas.mdreferences/scanning-gotchas.mdreferences/scanning-gotchas.mdCOMPOSIO_MULTI_EXECUTE_TOOL); attempt Calendar/Drive first, skip only on connection failuretool_call() in cron — they return "not a deferrable tool". Fallback to gws_direct_puller.py (googleapiclient) or cached dispatch journals. See scan-work-architecture.md § MCP-unavailable fallback pattern. (Confirmed 2026-07-27)tool_search ≠ tool_call availability — probe a suspect MCP tool alone before batching; MCP load state is intermittent between runsreferences/scanning-gotchas.mdterminal, not inline → full body in references/scanning-gotchas.md (CONFIRMED parse recipe: json.loads(raw)['result'] + split on Message ID: — see references/email-mcp-pagination-parsing.md)get_gmail_messages_content_batch 429 partial-failure (2026-07-22) → full body in references/scanning-gotchas.md<gworkspace-creds>/credentials/<email>.json) when dispatch rejects + legacy token is deleted_clientreferences/scanning-gotchas.mdhermes cron list — verify Last run: timestamp; consecutive_failures is the only reliable error gatepage_size, not limit/max_results → full body in references/scanning-gotchas.mdget_events param exception (confirmed 2026-07-23 finch:scan) → full body in references/scanning-gotchas.mdpage_token loop) → full body in references/scanning-gotchas.mdreferences/scanning-gotchas.mdquery="YYYY-MM-DD"); mine interactive messages via direct state.db SQL, not session_search scrolldf -h / before OAuth); execute_code blocked in cron profile — use terminal python3references/scanning-gotchas.mdread_file view of a JSON file is NOT validation → full body in references/scanning-gotchas.mdpatch edits to the same JSON file corrupt it → full body in references/scanning-gotchas.mdpatch block-moves → full body in references/scanning-gotchas.mdtasks array with a 2-item open_issues stub — check the top-level key before trusting the file. On 2026-09-22 the live task-list.json was overwritten with {"as_of","open_issues":[2 items]} (33 tasks lost). Detection: json.load(...) and assert "tasks" in d; a missing tasks key is corruption, not an empty list. Recovery: restore from task-list.json.bak-scan<N>, re-apply this scan's signal updates, merge new tasks — one script, validate-after-write; preserve the stub as task-list.json.stub-* for forensics, never rm it. Real case + full procedure in references/finch-scan-pitfalls.md.jobs.txt/tasks.txt/skilldir.txt; a bounded find under <fs-root> showed NONE of those files exist. Never carry such an item forward as pending_verification — resolve it as PHANTOM (the artifact was never a real product of this skill) and record the path-absence evidence. Run the filesystem check BEFORE re-running any "verification" step.references/finch-scan-pitfalls.mdexpanduser("...{PROFILE}...") missing f-string = literal {PROFILE} path bug (FileNotFoundError) → full body in references/finch-scan-pitfalls.mdreferences/finch-scan-pitfalls.mdexpanduser("~") returns literal tilde path in cron subprocesses when HOME is unset → full body in references/finch-scan-pitfalls.md (pitfall #5). This is a different root cause from the {PROFILE} bug (pitfall #2) but produces the same symptom. Both affect scripts using os.path.expanduser("~") in cron context. Fix: hardcode absolute paths or read os.environ.get("HOME", "/root").user_google_email (else pydantic validation error) → full body in references/finch-scan-pitfalls.mdjobs.json directly → full body in references/scanning-gotchas.mdjobs.json is a dict {"jobs": [...], "updated_at"}, NOT a bare list (confirmed 2026-07-23 finch:scan) → full body in references/scanning-gotchas.mdcron/output/<rid>/, NOT executions.db → full body in references/scanning-gotchas.mdreferences/scanning-gotchas.mdreferences/scanning-gotchas.mdfind can return a STALE jobs.json snapshot under state-snapshots/ — always pin the LIVE path → full body in references/scanning-gotchas.mdskill_view(name, file_path=...) linked-file fetch can itself throw the transient DaemonThreadPoolExecutor object has no attribute '_initializer' e → full body in references/scanning-gotchas.mdpatch fuzzy matcher → full body in references/scanning-gotchas.mdterminal python3 -c with a LARGE payload = STREAM TIMEOUT (confirmed 2026-07-24 finch:scan) → full body in references/scanning-gotchas.mdpatch PREFIX match = SILENT corruption on long single-line JSON values → full body in references/scanning-gotchas.mdjobs.json for cron health when cronjob tool unavailable; hermes cron list hides disabled jobs. **NEVER report "cron health clean" without enumer → full body in references/scanning-gotchas.mdreferences/scanning-gotchas.mdtool_call() for MCP tools fails with "not a deferrable tool" in cron — MCP tools (e.g. mcp_google_workspace_search_gmail_messages, mcp_google_workspace_get_events, mcp_google_workspace_list_drive_items) cannot be invoked via tool_call() in cron context. The registered tool list includes the names but the runtime does not expose them for tool_call dispatch in this profile. Fallback: use gws_direct_puller.py (googleapiclient direct, via <hermes-venv>/bin/python) or Composio's COMPOSIO_MULTI_EXECUTE_TOOL. Email: check toolkit connection status first; if absent, use cached-data fallback (last_email_check_<account>.json in commons/data/ocas-dispatch/). Calendar/Drive: attempt Composio first, skip only on connection failure. Do NOT retry tool_call() for the same MCP tool in the same run — it will fail identically. (Confirmed 2026-07-27 finch:scan)references/scanning-gotchas.mdreferences/scanning-gotchas.mdjobs.json schedule fields are dicts not stringshermes cron list has NO parseable JSON mode — and emits NO last_status/consecutive_failures columns → full body in references/scanning-gotchas.md~/.hermes/commons/..., NEVER a tilde ~/.hermes/commons/... → full body in references/scanning-gotchas.md~/.hermes/commons/..., NEVER a tilde ~/.hermes/commons/... → full body in references/scanning-gotchas.mdexpanduser("~") returns literal tilde path in cron subprocesses when HOME is unset → full body in references/finch-scan-pitfalls.md (pitfall #5). This is a DIFFERENT root cause from the {PROFILE} missing-f-string bug (pitfall #2) but produces the same FileNotFoundError symptom. Both affect any script using os.path.expanduser("~") in cron context. Fix: use hardcoded absolute paths or read os.environ.get("HOME", "/root") explicitly.Confirmed 2026-07-22: multiple ocas-*:update jobs can fail simultaneously with the same git rebase conflict signature, indicating upstream sync publication introduced incompatible changes across multiple related repos/skills. Job descriptions often include Removing references/..., Removing data/, and Dropped refs/stash@{0}. Treat as one systemic sync breakage, not N independent failures.
Role: Session mining engine. Detects behavioral signals from conversation transcripts and routes them to durable modification targets (MEMORY.md, skill patches).
Journal type: Action Journal. Every finch run emits an Action Journal entry to {agent_root}/commons/journals/ocas-finch/.
Cooperation:
scripts/memory_guard.py direct file write (--apply --file ~/.hermes/profiles/<profile>/memories/MEMORY.md), NOT the built-in memory tool. The profile has a pre_tool_call shell hook (agent-hooks/block-memory-tool.sh, matcher ^memory$) that BLOCKS the memory tool and redirects to this script, plus memory.memory_enabled: false so the built-in backend never auto-persists. Finch is therefore the SOLE maintainer of MEMORY.md. Never call the memory tool to edit it.See references/file-governance.md for write targets, read-only files, off-limits files, and creation criteria.
See references/signal-types-table.md for the full signal type table.
When the user says "Always" or "Never", this is an explicit behavioral rule. Priority 0 — highest priority. Apply immediately and prominently. Route to MEMORY.md under ## Always Rules or ## Never Rules. Never batch with lower-priority findings.
Finch operates as a continuous improvement cycle:
finch:scan, every 2h) — Read 7 signal sources. Cron health MUST be enumerated from the LIVE jobs.json (~/.hermes/profiles/<profile>/cron/jobs.json) via the parse script in references/cron-health-validation.md — NEVER via cronjob(action='list') or hermes cron list, which surface only a subset and miss paused/disabled error jobs. Validate existing tasks against live signal (bidirectionally — re-open on relapse). Maintain prioritized task list at task-list.json.finch:work, every 30 min) — Pick top pending task. Load governing skill via skill_view. Execute ONE task per run. Before selecting, check for duplicate task IDs and clean up if found (see references/duplicate-task-detection.md). When completing a task:
\n\n[Work log: At <timestamp> checked DNS for art.<agent-handle>.com - no records found (NXDOMAIN).])"done"done_at timestamp to the completion timeupdated_at timestamp
Route findings to MEMORY.md, skill patches, or reference files. Stage proposed skill patches under {agent_root}/commons/data/ocas-forge/staged/{skill}/ for ocas-fellow evaluation before committing to production (per the staged-write-approval gate; do not directly apply a rebuilt skill without benchmark verification). MEMORY.md behavioral rules apply immediately (priority 0); skill rebuilds go through staging.When multiple tasks are pending, select by:
action_required: true — tasks needing external action take absolute precedencehigh > medium > lowpending tasks are picked before in_progress tasks (which are already being handled)Skip tasks where action_required: false AND status: "in_progress" — these are events happening now (e.g., is at the appointment). Only pick them if they transition to needing action.
If NO tasks have action_required: true and all remaining tasks are pending with action_required: false, pick the highest-priority one to validate/monitor (e.g., disk monitoring) and mark it completed with a resolution note. This prevents the list from accumulating stale low-priority items.
Detect and break the loop where a task is repeatedly checked and closed without a fix. Read references/work-execution-procedures.md (Repeated check-and-close anti-pattern (work execution)) when a task keeps reappearing across runs.
Decide whether a task can be acted on unattended in cron. Read references/work-execution-procedures.md (Task actionability filter (cron context)) when finch:work runs without a user present.
Resume a partially-completed pipeline task from its ledger instead of restarting. Read references/work-execution-procedures.md (Pipeline task resumption (ledger/state-based)) when a task was interrupted mid-pipeline.
When mining corrections and failures, categorize each by the task phase where the failure occurred. This taxonomy enables targeted skill patches instead of vague "be more careful" updates:
| Phase | Description | Example signal |
|---|---|---|
| Planning | Wrong approach chosen, incorrect assumptions, missing prerequisites | "You should have checked X first" |
| Execution | Right plan but tool call/API/step failed, wrong parameters, timeout | "The command failed because..." |
| Response | Correct result but wrong format, verbosity, tone, or framing | "Too verbose" / "Wrong format" |
Route planning-phase corrections to skill preconditions/setup sections. Route execution-phase corrections to tool-usage/gotchas sections. Route response-phase corrections to output-formatting sections. This produces surgical patches instead of blanket directives.
When recording a correction or lesson, don't just capture WHAT was wrong — extract the underlying principle by asking "why" and "when":
Format: [CORRECTION] What: <what was wrong>. Why: <underlying principle>. When: <applicable context>
This produces lessons that transfer across contexts, not just single-instance fixes.
Classify a signal as real / stale / transient before changing anything. Read references/work-execution-procedures.md (Signal triage before execution (WORK step)) before acting on any finch:work signal.
When a task-list entry prescribes a specific "fix" (seed the missing file, relink the path, create X), do NOT apply it on sight. Two conditions invalidate the prescribed fix: (1) the job already self-recovered via an upstream reseed — the error is from an OLD run; a later run already succeeded and rewrote the artifact (read cron/output/<job-id>/, compare file mtime to the success run). (2) the crash is a deliberate fail-loud guard over a later full-file rewrite — silencing it (e.g. except FileNotFoundError: others=[]) lets a future wipe proceed and destroy every sibling record in that file. In either case, take no code action; resolve the task as self-recovered and cite the evidence. Full procedure + real case in references/work-prescribed-fix-selfrecovery-guard.md.
"Run finch" means verify ALL deployed finch cron jobs are healthy and (optionally) force a run. The deployed job set (2026-07-07) is FIVE jobs, not the four in the design doc:
finch — profile-root MEMORY.md compaction (runs memory_guard.py on the DEFAULT profile's MEMORY.md — NOT the profile's; guard the --file override or it compacts the wrong memory).finch:floor — no_agent script safety floor (memory guard). Normally enabled: false but self-triggers; do NOT treat its disabled state as broken.finch:scan — every 2h, pure LLM. Cron-health step MUST enumerate jobs.json via references/cron-health-validation.md (NOT cronjob(action='list') / hermes cron list).ocas-finch:daily — daily 6am PT, pure LLM.ocas-finch:weekly — Sunday 8am PT, pure LLM.(NOTE: the design doc lists finch:work every 30min — that job was NOT present in deployment on 2026-07-07. Work execution is covered by the interactive finch.work command / finch:scan-driven task list, not a separate cron. Verify with cronjob list before assuming job names, since they drift.)
cronjob action='run' does NOT force a scheduled LLM job to execute — it only bumps next_run_at to the next NATURAL tick (the job fires on its normal schedule, not immediately). To force execution NOW: PAUSE the job first (action='pause'), then run (action='run') — the paused state triggers forced execution. no_agent/script jobs (e.g. finch, finch:floor) run on a plain run without pausing. After a forced run succeeds, the job returns to state: scheduled automatically.
Verification gate: A queued immediate run is not a completed run. After every manual trigger, re-read jobs.json/cronjob list and verify last_run_at advanced to the current run window and last_status is current. If next_run_at is in the past but last_run_at did not advance after a tick, report the job as queued/not yet executed, not completed. For finch.scan, finch.work, daily, and weekly, run deterministic sub-functions directly where available (for example self_update.py, memory_guard.py, task-list inspection, journal write) and distinguish those completed direct actions from still-queued LLM cron jobs.
If multiple finch jobs error with 401, first classify WHICH 401 it is before acting:
[mcp_servers] token): the cause is a stale [mcp_servers] block in the profile .env (~/.hermes/profiles/<p>/.env) shipping an invalid/expired token (e.g. a dead Discord token) that breaks ALL MCP calls. Fix: remove the [mcp_servers] section; the client falls back to valid config and MCP works.RuntimeError: Error code: 401 with token_expired ("Provided authentication token is expired") or "Your API key is invalid, blocked or out of funds" from portal.nousresearch.com. This is NOT the [mcp_servers] block. Confirmed 2026-07-12: finch jobs 401'd with Nous token_expired; grep mcp_servers on the .env returned nothing; the gateway was holding a stale provider credential. Fix: restart the gateway (kill the --profile <profile> gateway run process and let it respawn, or hermes gateway run) so it reloads the current valid provider token. After restart, post-restart runs (finch:scan, finch:memory-guard-floor) returned ok.Diagnostic steps: (1) Read the actual run output / jobs.json last_error — cronjob list may display last_error: None even when jobs.json holds the 401, so don't trust the list's None. (2) grep -n "mcp_servers" ~/.hermes/profiles/<p>/.env — if absent, it's provider-auth, not MCP-auth. (3) If interactive sessions on the same provider/model work but cron 401s, the scheduler is holding a stale token → restart the gateway.
See cron-job-repair for the model-routing 401 vs MCP-auth 401 distinction.
When a finch job (or any cron job) is failing and the fix is clear, DO NOT ask "continue?" or wait for the user to "say the word." Apply the fix, run all affected jobs, then report results in one message. The user explicitly requires the agent to take the needed action without prompting (stated 2026-07-07: "I shouldn't have to 'say the word' you should just take action that needs to be taken").
finch.run — Full daily pipelinefinch.mine — Mine sessions for signals onlyfinch.compact — Compact MEMORY.md onlyfinch.route — Route mined findingsfinch.dry-run — Full pipeline without applying changesfinch.status — Show recent statsfinch.scan — Run scan manuallyfinch.work — Run work manually| Job | Frequency | Behavior |
|---|---|---|
| finch:scan | Every 2h | Scan 7 sources → maintain task list |
| finch:work | Every 30 min | Pick top item → execute. ONE task per run. |
| finch:daily | Daily 6am PT | Mine 24h → Compact → Route → Auto-apply low-risk |
| finch:weekly | Sunday 8am PT | Mine 7d → Compact → Route → Full plan |
This section defines error handling and recovery procedures for all finch jobs.
evidence.jsonl (including no-op runs with not_activity_reason).gap_detected and runs compact remedial pass.degraded: session_store and skips mining.See references/okrs.md for targets (schedule adherence, data integrity).
|-----|--------|--------|
| schedule_adherence | ≥ 0.98 | 30 runs |
| data_integrity | 1.00 | 30 runs |
See references/anti-patterns.md for the full list of 10 anti-patterns including declaration of victory and code fence pitfalls.
See references/active-review.md for the full principle.
After every session, review the conversation for signals and update the skill library. See references/skill-library-maintenance.md for the full procedure including signals that warrant action, preference order for updates, and what NOT to capture.
Skill integration hygiene (confirmed 2026-07-14): When adding external/upstream skills to the local library, prefer integrating relevant LEARNINGS into the closest existing skill rather than installing a new conflicting skill. For upstream skill repos shares: (1) determine if any capability overlaps an existing skill; (2) if yes, merge the valuable parts into that skill (including code-review patterns may say were "skipped"); (3) only install a new skill if it has no close match and won't conflict. : "Would any of these skills be useful in skill library, if so integrate them into the closest match don't install new skills that may conflict" and "You should integrate what makes sense in code review as well. The ones you skipped."
Active-review mandate: A review pass that finds no signal is a missed learning opportunity, not a neutral outcome. Most finch passes surface at least one skill update — even a small pitfall or support-file note. Prefer patching the skill that was IN PLAY this run over creating a new narrow skill.
See references/pitfalls.md for the full consolidated pitfalls list.
references/operational-gotchas.md)memory tool may be unavailable in cron — fall back to direct file edit at the canonical profile memory path; re-read before write on sibling-warningNo, Don't) — mine without query=, use role_filter=user, scan visuallysession_search reads the DEFAULT profile store, so query ~/.hermes/profiles/<profile>/state.db directly. Identify interactive first (source NOT LIKE 'cron%'), pull user messages via direct state.db SQL, drop [CONTEXT COMPACTION — REFERENCE ONLY] headers (false positives), parse JSON-array content. NOTE: interactive user messages carry observed=0, NOT observed=1 — do NOT filter observed=1 or you drop every real user message. Full recipe: Fallback to keyword queries before declaring "no interactive sessions".~/.hermes/MEMORY.md; old two-branch double-nests. under cron, a bare read_file('~/.hermes/MEMORY.md') resolves to the DEFAULT profile memory (different path AND content) — always target ~/.hermes/profiles/<profile>/memories/MEMORY.md explicitly. After writing MEMORY.md, RE-READ it and assert every intended block persisted (grep a unique substring per block) — a prior daily run recorded two Tier-1 blocks as 'routed to MEMORY.md' that were ABSENT from the live file next run (silent consolidation loss); recover any missing block rather than trusting the journal's applied self-report, which is not proof of persistence.md path trap.Full file-to-purpose map (when to read each reference, script, and data file) → references/finch-support-map.md.
Full detail (eviction priority, self_update wrapper contract, memory_state subcommands) in references/operational-gotchas.md § Scripts:
memory_guard.py — deterministic MEMORY.md safety floor; mandatory post-guard Step 7.5 verification (Methodologies must outrank Course Changes in eviction)self_update.py / self_update.sh — real Python wrapper resolving skill dir from Path(__file__).resolve().parents[1]; self_update.sh is the GitHub fetch/install pathmemory_state.py — persisted reinforcement-state store (Ebbinghaus forgetting curve); reinforce / check / route / decay-report subcommandsgws_direct_puller.py — FULL-CONTENT Google Workspace puller (Gmail metadata + optional --full-text body, Calendar events for a horizon, Drive most-recently-modified). Run via <hermes-venv>/bin/python (MCP venv has googleapiclient). Use when MCP absent and you must CLASSIFY actionable email/calendar/drive signals, not just confirm reachability. CRITICAL: use googleapiclient, NOT raw requests — the host egress filter 404s raw Calendar/Drive calls. Pair with gws-direct-fallback.py (count-probe).verify_sepagree_signature.py — reusable EMAIL-SEPAGREE (separation agreement) Docusign "unsigned" re-verifier for finch:work passes. Run via terminal python3 (NOT execute_code); counts Docusign "Completed"/signed notices + cross-checks the negotiation thread, prints VERDICT. Proven logic extracted 2026-07-16 from a working live Gmail API pass.finch_hooks_plugin.py — Hermes Agent Hooks plugin for real-time signal capture, memory tool guard, subagent delegation tracking, and prompt section registration. See references/hermes-hooks-integration.md for hook patterns.
Recovery note (2026-07-16 finch:work pass): the EMAIL-SEPAGREE task-list meta/signal referenced <fs-root>/sepagree_verify.py. That path is NO LONGER reliably absent — as of the 10th re-verify pass, STALE DUPLICATE copies now exist at <fs-root>/sepagree_verify.py AND ~/.hermes/profiles/<profile>/commons/data/ocas-finch/sepagree_verify.py. Do NOT run either — they may diverge from the maintained script. The canonical, maintained verifier is skills/ocas-finch/scripts/verify_sepagree_signature.py. The Docusign recipe (1 begin-signing + 0 Completed = proof of non-signature) is the load-bearing check; re-running it is the correct finch:work action for the P1, not re-deriving the script each time.
Locator pattern: to find the verifier (or any skill script) reliably in the cron profile, use terminal find /root -iname 'verify_sepagree*' 2>/dev/null rather than search_files, which returned transient DaemonThreadPoolExecutor framework errors on 3 consecutive attempts this run. find is the dependable fallback when search_files flakes — and the same DaemonThreadPoolExecutor error also hits read_file in bursts; for those, fall back to terminal (python3 / stat / cat via read_file substitute).
NEW (2026-07-23): pass --since <RFC3339> to run the BLOCK-CLEARANCE PROBE — enumerates all Docusign/Kim envelopes in the last 5d and reports any with internalDate AFTER that ts (a potential corrected/Section-3-15 envelope). If none, the external-party blocker is unchanged; the probe is the canonical replacement for the inline Gmail re-derivation historically done on the docusign-separation-agreement task.
PROHIBITION (reinforced 2026-07-23T13:34Z finch:work relapse): do NOT hand-roll a new verify-docusign-*.py into commons/data/ocas-finch/. A 2026-07-23T13:34Z pass did exactly that (commons/data/ocas-finch/verify-docusign-0723.py) — it duplicated the canonical script's --since probe and created a stale-drift risk (the SAME class as the sepagree_verify.py duplicates the recovery note already warns about; find later returns multiple divergent copies). That redundant file SHOULD BE DELETED. If a probe case is missing from verify_sepagree_signature.py, EXTEND the canonical script (add the case + wire --since) — never author a sibling. The canonical script is the single source of truth for EMAIL-SEPAGREE/Docusign re-verify.finch.update pulls the latest from GitHub. Runs silently unless version changed or error.
Finch is designed for Hermes but degrades gracefully on other harnesses. Minimum viable platform: any harness with write_file, read_file, and terminal tools.