You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
One retry policy for every adapter's HTTP path. A 429, a gateway 5xx, a 500 whose body names a query or rate limit (TickTick's exceed_query_limit), a 403 carrying Retry-After or an exhausted rate-limit window (GitHub), and dropped connections are retried with jittered exponential backoff that honors Retry-After and x-ratelimit-reset. TickTick, Notion, GitHub, Airtable and Google ride the same @reneza/ats-core/retry; a plain 4xx returns on the first attempt. Knobs: ATS_HTTP_RETRIES (3; 0 disables), ATS_HTTP_RETRY_BASE_MS, ATS_HTTP_RETRY_MAX_MS.
Stale-while-revalidate corpus cache. Past the 5-minute TTL, find answers immediately from the stale copy — the result says corpus.stale: true, revalidating: true — while a detached ats cache sync refreshes the cache for the next call. One refresh lease at a time; concurrent calls share it. ATS_CORPUS_STALE_MAX_MS (24h) is the ceiling past which a read refreshes first; ats find … --fresh always refreshes first. ats cache status and ats doctor show stale / servable / revalidating. The TickTick adapter gains bulkFetch() in retrieval shape, so ats cache sync, find, dedup and garden all read the same cache.
Scoped retrieval: ats find … --project <id|name> / --projects a,b. The corpus is filtered before any branch runs and branches that reach past it (hybrid, native search) are filtered on the way back. A project may be its full id, short id, composite backend:id, or name (leading decorations ignored); a partial name resolves when it names exactly one project, and several matches come back as scope.candidates. The result carries scope: { projects, matched, of }.
A confidence verdict on every find.confidence: { verdict, reason, branchesRun, topAgreement } reads branch agreement on the top hit: strong (two or more branches agree, or the title is the query), weak (several ran, one found it), moderate (one branch ran), none. --min-sources N is the matching precision gate. The verdict lands in the usage log and the ats agent-setup policy block tells agents to read it before acting.
Additive updates and compare-and-swap.ats update … --append "…" / --prepend "…" add to the body that is there; --if-match <hash> lands the write only while the body still carries that contentHash (exit 3 otherwise, with the current hash in the message). ats get and ats update return contentHash; a staged compare-and-swap write is re-verified at apply time. The argument parser binds values that begin with a dash when they are not flag-shaped (--append "- 2026-09-05: shipped") and accepts --key=value.
Idempotent creates.ats create … --if-absent returns the active task that already carries the title in the project instead of creating another; --idempotency-key <k> records what the first call produced — a task, or a staged review item — and a repeat with the same key replays it. Keys age out after ATS_IDEMPOTENCY_TTL_MS (7 days).
One review gate for every write surface.guardWrite in @reneza/ats-core is the enforcement of intent.approvalRequired, security.approvalRequiredFor and ATS_REVIEW_ALL; the CLI and the MCP server's create_task / update_task both stage through it, so a gated target holds whichever surface an agent uses.
Zero-result recovery. An empty ats notes find answers { query, count: 0, notes: [], suggestions, hint } with the nearest items from the fused find; an empty ats tasks search carries the same suggestions; a missed ats get names the nearest items in its error. A hit keeps its shape.
Full provenance in ats kg export --cypher. Every FACT relationship carries status, tValid / tInvalid, confidence, source, proposer, proposal id, ratifier and ratification time, task reference, and — for retracted facts — who retracted it and why. --include-retracted adds the closed facts, so the script is a complete record of what the store believed and when.