/** * Docs triage — batch System One verdicts over the docs store (T13446). * * Thousands of docs sit in `draft` with empty `topics`/`related_tasks`; manual * triage does not scale. This module runs the registered `docs.triage` * decision site over a batch of docs (default: drafts, oldest first) and * produces PROPOSALS — a keep/update/supersede/archive verdict plus a * staleness score per doc. It never mutates a doc: the only write is the * opt-in `--record` path, which persists each verdict as a typed D#### * decision (`confirmationState: 'proposed'`) via `storeDecision`, leaving the * docs store untouched. * * Each doc gets ONE {@link DecisionRequest} with two questions (`verdict` — * choice, `staleness` — score) and structured state (slug, kind, status, * topics, related tasks, summary, age, body excerpt); every string field is * redacted, then clipped, before it leaves the machine. Requests go out * through `decideBatch` in chunks the provider's batch capability can hold * (layahost: 64 requests / 256 questions). Unconfigured provider ⇒ mode * `off`, zero network, and every proposal is the rule fallback (age and * link/metadata heuristics). Provider `unauthorized` / * `insufficient_credits` answers abort the run: remaining chunks are skipped * and the report carries `aborted: true`. * * Mode comes from `decide.sites.docTriage` (`off | shadow | on`). `shadow` * (the registry default) asks and audits but the proposal that acts is the * heuristic's; the provider's answers ride along on the proposal for review. * `on` uses the decided verdict when it is valid and confident (≥ * {@link DOC_TRIAGE_MIN_CONFIDENCE}). * * @task T13446 * @saga T13339 */ import type { DecisionRequest } from '@cleocode/contracts'; import { type DecideOptions } from '../decide/client.js'; /** Call-site id for the triage decision; keys the audit line. */ export declare const DOC_TRIAGE_SITE: "docs.triage"; /** Config key selecting the System One mode for docs triage. */ export declare const DOC_TRIAGE_MODE_KEY: "decide.sites.docTriage"; /** The verdicts offered to the decision — the doc lifecycle actions a reviewer can take. */ export declare const DOC_TRIAGE_VERDICTS: readonly ["keep", "update", "supersede", "archive"]; /** One of {@link DOC_TRIAGE_VERDICTS}. */ export type DocTriageVerdict = (typeof DOC_TRIAGE_VERDICTS)[number]; /** Staleness levels, lowest first (the `staleness` score question's scale). */ export declare const DOC_TRIAGE_STALENESS_LEVELS: readonly ["current — the doc reflects the system as it is today", "minor drift — small details have aged but the substance holds", "dated — parts no longer match the system; a refresh is due", "stale — substantially outdated or abandoned as a draft", "abandoned — untouched for a year or more with no links or metadata signal"]; /** Minimum confidence the decided answers need before `on` mode uses them. */ export declare const DOC_TRIAGE_MIN_CONFIDENCE = 0.6; /** Default number of docs one triage run scans. */ export declare const DOC_TRIAGE_DEFAULT_LIMIT = 25; /** Per-chunk size for `decideBatch` — inside the layahost 64-request batch cap. */ export declare const DOC_TRIAGE_BATCH_CHUNK = 64; /** Whole-chunk deadline handed to `decideBatch`, in ms. */ export declare const DOC_TRIAGE_BATCH_TIMEOUT_MS = 120000; /** One doc selected for triage, with the metadata the request and heuristic need. */ export interface DocTriageCandidate { /** Attachment id (UUID). */ readonly attachmentId: string; /** Content address. */ readonly sha256: string; /** Stable slug, when the doc has one. */ readonly slug: string | null; /** Taxonomy kind (adr, spec, research …). */ readonly kind: string | null; /** Lifecycle status the candidate was selected by. */ readonly status: string; /** Canonical topic slugs (parsed from `attachments.topics`). */ readonly topics: readonly string[]; /** Related `T####` task ids (parsed from `attachments.related_tasks`). */ readonly relatedTasks: readonly string[]; /** One-sentence summary, when stored. */ readonly summary: string | null; /** ISO 8601 creation timestamp. */ readonly createdAt: string; /** Owning entity (task/session id) from the first attachment ref. */ readonly ownerId: string | null; } /** The rule fallback's verdict for one candidate. */ export interface DocTriageHeuristic { /** Verdict the heuristic proposes. */ readonly verdict: DocTriageVerdict; /** 0-based index into {@link DOC_TRIAGE_STALENESS_LEVELS}. */ readonly staleness: number; /** One-line explanation for audits and rationales. */ readonly rationale: string; } /** One triage proposal. The docs store is never mutated — this is advisory. */ export interface DocTriageProposal { /** Attachment id. */ readonly attachmentId: string; /** Slug, when the doc has one. */ readonly slug: string | null; /** Taxonomy kind. */ readonly kind: string | null; /** The verdict that acts: the heuristic's in `off`/`shadow`, the decision's in `on`. */ readonly verdict: DocTriageVerdict; /** `system-one` only when `on` mode acted on a confident, valid answer. */ readonly source: 'rule' | 'system-one'; /** Confidence of the acting verdict (flat 0.5 for the rule fallback). */ readonly confidence: number; /** Acting staleness level (0-based into {@link DOC_TRIAGE_STALENESS_LEVELS}). */ readonly staleness: number; /** Human label of the acting staleness level. */ readonly stalenessLabel: string; /** Why this verdict was proposed. */ readonly rationale: string; /** The provider's answers, when one answered (shadow-mode visibility). */ readonly decided?: { readonly verdict: DocTriageVerdict | null; readonly verdictConfidence: number; readonly staleness: number | null; readonly stalenessConfidence: number; }; /** The recorded D#### decision id, set only on the `--record` path. */ readonly decisionId?: string; } /** The result of one {@link triageDocs} run. */ export interface DocsTriageReport { /** The decision site that produced the verdicts. */ readonly site: string; /** Effective site mode (`off` when no provider is configured). */ readonly mode: 'off' | 'shadow' | 'on'; /** Lifecycle status that was scanned. */ readonly status: string; /** Candidates selected. */ readonly scanned: number; /** Proposals recorded as D#### decisions (`--record` only). */ readonly recorded: number; /** True when the run aborted early on `unauthorized` / `insufficient_credits`. */ readonly aborted: boolean; /** Why the run aborted, when it did. */ readonly abortReason?: string; /** Fallback-reason histogram across every asked question (empty in `off`). */ readonly fallbackReasons: Record; /** One proposal per scanned doc, oldest first. */ readonly proposals: readonly DocTriageProposal[]; } /** Options for {@link listTriageCandidates}. */ export interface ListTriageCandidatesOptions { /** Lifecycle status to select. Default `'draft'`. */ readonly status?: string; /** Maximum candidates, oldest first. Default {@link DOC_TRIAGE_DEFAULT_LIMIT}. */ readonly limit?: number; /** Project root for the store. Default: the resolved CLEO project root. */ readonly projectRoot?: string; } /** * Select triage candidates from the attachments store: one row per doc with a * slug, filtered by lifecycle status (`draft` by default), oldest first. * * Read-only: this is a drizzle SELECT through the canonical `getDb` * chokepoint, never a write. * * @param opts - Status filter, limit and project root. * @returns The candidates, oldest first. */ export declare function listTriageCandidates(opts?: ListTriageCandidatesOptions): Promise; /** * The rule fallback: a deterministic verdict from age, lifecycle status and * whether the doc carries any topics or related-task links. * * - Non-draft docs and linked drafts that are still fresh: `keep`. * - Drafts missing all metadata, or linked drafts that have drifted: `update`. * - Unlinked drafts untouched for a year or more: `archive`. * * @param doc - The candidate. * @param now - The clock reading the age is measured against. * @returns The heuristic verdict, staleness level and rationale. */ export declare function heuristicDocTriage(doc: DocTriageCandidate, now: Date): DocTriageHeuristic; /** * Build the decision request for one candidate: structured state (every * string redacted, then clipped) plus the `verdict` choice and `staleness` * score questions. * * @param doc - The candidate. * @param bodyExcerpt - Body text excerpt (already fetched), when readable. * @param now - Clock reading for the age field. * @returns The request. */ export declare function buildDocTriageRequest(doc: DocTriageCandidate, bodyExcerpt: string | null, now: Date): DecisionRequest; /** Options for {@link triageDocs}. */ export interface TriageDocsOptions { /** Lifecycle status to scan. Default `'draft'`. */ readonly status?: string; /** Maximum docs to scan, oldest first. Default {@link DOC_TRIAGE_DEFAULT_LIMIT}. */ readonly limit?: number; /** Persist every proposal as a `proposed` D#### decision. Default false (dry report). */ readonly record?: boolean; /** Explicit site mode; wins over config when a provider is configured. */ readonly mode?: 'off' | 'shadow' | 'on'; /** Provider, connection, budget, cache and audit wiring forwarded to `decideBatch()`. */ readonly decide?: DecideOptions; /** Project root for the store, config lookup and the audit sink. */ readonly projectRoot?: string; /** Clock override (tests pin the age heuristic). */ readonly now?: Date; } /** * Batch-triage docs through the `docs.triage` site. * * Produces one proposal per scanned doc and NEVER mutates a doc. With * `record: true` each proposal is persisted as a typed D#### decision * (`confirmationState: 'proposed'`, `decidedBy: 'agent'`) whose text names the * doc slug, so the verdicts stay reviewable and reversible. An * `unauthorized`/`insufficient_credits` answer aborts the run before the next * chunk goes out. * * @param opts - Status filter, limit, record flag, mode and wiring. * @returns The triage report. */ export declare function triageDocs(opts?: TriageDocsOptions): Promise; //# sourceMappingURL=triage.d.ts.map