Skip to the content

Capsid menu

app/lib/operator/descriptors.ts

449 lines and 2 functions in DrDustinEdwards/dustinedwards-info, at commit 1590ba4 on 2026-10-24. This file on GitHub.

Functions

The functions of descriptors.ts
FunctionLinesComplexityCoveredCRAPMutation
isToolName57 to 592No data: no coverage report in this mapNo data: no coverage report in this mapNo data: no mutants in this code
toolNames61 to 631No data: no coverage report in this mapNo data: no coverage report in this mapNo data: no mutants in this code

Imports

It imports no other module of this repo. Imported by app/lib/operator/api.server.ts, app/lib/operator/sync-tools.server.ts, app/lib/operator/upload-media.server.ts.

Source

// The operator tool surface: the tool names, what each takes and returns, and the result shape every
// tool answers with. `GET /api/operator` serves the descriptors; check:destructive reads them.

export type ToolResult =
  | { ok: true; data: unknown }
  | { ok: false; status: number; error: string; detail?: unknown };

const TOOLS = [
  "list_posts",
  "get_post",
  "save_post",
  "delete_post",
  "sync_status",
  "sync_ask",
  "sync_media",
  "sync_posts",
  "sync_procedures",
  "sync_dictionary",
  "sync_pages",
  "sync_publications",
  "sync_llms",
  "sync_cv",
  "sync_cv_pdf",
  "sync_roster",
  "sync_phages",
  "sync_registry",
  "backup_media",
  "backup_d1",
  "upload_media",
  "list_mentions",
  "decide_mention",
  "list_procedures",
  "get_procedure",
  "save_procedure",
  "list_publications",
  "get_publication",
  "refresh_citations",
  "check_publications",
  "list_pages",
  "get_page",
  "get_llms",
  "list_cv",
  "get_cv",
  "list_dictionary",
  "get_dictionary",
  "list_roster",
  "get_roster",
  "list_phages",
  "get_phage",
  "list_registry",
  "get_registry",
  "purge_zero_results",
] as const;

export type ToolName = (typeof TOOLS)[number];

export function isToolName(value: unknown): value is ToolName {
  return typeof value === "string" && (TOOLS as readonly string[]).includes(value);
}

export function toolNames(): readonly ToolName[] {
  return TOOLS;
}

// Keyed by `ToolName`, so a missing or extra descriptor is a typecheck failure.
export const TOOL_DESCRIPTORS: Readonly<
  Record<ToolName, { args: Record<string, string>; returns: string; policy?: string }>
> = {
  list_posts: {
    args: {},
    returns:
      "Every post row in D1, drafts included, with the head sha. `updated` " +
      "is the revision date the row carries.",
  },
  get_post: {
    args: { slug: "string" },
    returns: "The complete markdown file, the head sha, and operatorMayPublish.",
  },
  save_post: {
    args: {
      slug: "string",
      raw: "string, the complete markdown file including frontmatter",
      expectedHeadSha: "string, optional, for editor-style conflict detection",
      isNew: "boolean, optional, inferred from whether the file exists",
    },
    returns:
      "commitSha, and the gate's own message with field and line on rejection. A file identical to the " +
      "committed one commits nothing: unchanged is true and commitSha is the current head.",
    policy:
      "An operator may create, edit, unpublish and republish. It may NOT " +
      "perform a post's first transition to draft:false; that is reserved " +
      "to the human admin and is refused with 403 " +
      "first-publish-requires-admin.",
  },
  delete_post: {
    args: { slug: "string" },
    returns: "commitSha.",
    policy:
      "Deleting a post is reserved to the human admin and is refused with " +
      "403 delete-requires-admin. Unpublish instead (draft: true).",
  },
  sync_status: {
    args: {},
    returns: "Repository, D1 and search index counts, reported separately.",
  },
  sync_ask: {
    args: {},
    returns:
      "Uploads the full corpus to the Ask index and prunes strays. Idempotent. " +
      "expected, present, drift and a converged verdict, all read back after " +
      "the writes rather than taken from the upload's own counters.",
  },
  sync_media: {
    args: {},
    returns:
      "Rebuilds the media index from R2 and the asset manifest, through the " +
      "same derivation the admin button runs. Idempotent. A read-back " +
      "reconciliation: expected, present, missing and extra keys, and a " +
      "converged verdict.",
  },
  sync_posts: {
    args: {},
    returns:
      "Converges D1 to the repository's markdown: re-renders every post " +
      "whose file's blob sha differs from its row (or has no row), removes " +
      "rows whose file is gone, all through the same render door a save " +
      "uses. Idempotent. A read-back reconciliation: expected, present, and " +
      "a converged verdict.",
  },
  sync_procedures: {
    args: {},
    returns:
      "Converges D1 to the repository's content/procedures files: re-compiles " +
      "every procedure whose file's blob sha differs from its row (or has no " +
      "row), removes rows whose file is gone, all through the same compile " +
      "and write doors save_procedure uses. Refuses an empty file set, and " +
      "answers 422 naming any file the validator refuses after converging the " +
      "rest. Idempotent. A read-back reconciliation: expected, present, and a " +
      "converged verdict.",
  },
  sync_dictionary: {
    args: {},
    returns:
      "Converges D1 to the repository's content/dictionary files: re-compiles every entry whose file's blob sha " +
      "differs from its row (or has no row), removes rows whose file is gone, through the same compile and write " +
      "doors a dictionary save uses. Each write also re-derives the page the entry opens (its markdown twin and " +
      "search records) and purges the pages tag, because that page embeds the entry. A file whose path is not a " +
      "registered Software page, or whose clip is not in the repository, never makes a row. Refuses an empty file " +
      "set, and answers 422 naming any file the validator refuses after converging the rest. Idempotent. A " +
      "read-back reconciliation: expected, present, and a converged verdict.",
  },
  sync_pages: {
    args: {},
    returns:
      "Converges D1 to the repository's content/pages files: re-compiles every " +
      "page whose file's blob sha differs from its row (or has no row), removes " +
      "rows whose file is gone with their search records, through the same " +
      "compile and write doors a page save uses. A file whose path is not in " +
      "CONTENT_PAGE_PATHS (or About's own, CONTENT_PAGES_OWN_ROUTE) never makes a row. Refuses an empty file set, and " +
      "answers 422 naming any file the validator refuses after converging the " +
      "rest. Idempotent. A read-back reconciliation: expected, present, and a " +
      "converged verdict.",
  },
  sync_publications: {
    args: {},
    returns:
      "Converges D1 to the repository's content/publications files: re-compiles " +
      "every paper whose file's blob sha differs from its row (or has no row), " +
      "removes rows whose file is gone with their search record and citation row, " +
      "through the same compile and write doors a publication save uses. A " +
      "citation count that exists is never touched; a missing one is seeded from " +
      "the committed OpenAlex snapshot. Refuses an empty file set, and answers 422 " +
      "naming any file the validator refuses after converging the rest. " +
      "Idempotent. A read-back reconciliation: expected, present, and a converged " +
      "verdict.",
  },
  sync_llms: {
    args: {},
    returns:
      "Converges the settings row /llms.txt is served from to the repository's content/llms.txt, through " +
      "the same validator and write door an llms.txt save uses, and purges /llms.txt. Refuses a repository " +
      "with no llms.txt rather than deleting the row, and answers 422 naming the checks a file fails. " +
      "Idempotent. A read-back reconciliation: expected, present, and a converged verdict.",
  },
  sync_cv: {
    args: {},
    returns:
      "Converges D1 to the repository's content/cv files: re-compiles every CV file whose blob sha differs " +
      "from its row (or has no row), removes rows whose file is gone, then rewrites the CV's search records, " +
      "through the same compile and write doors a CV save uses. Refuses an empty file set, and answers 422 " +
      "naming any file the validator refuses after converging the rest. Idempotent. A read-back reconciliation: " +
      "expected, present, and a converged verdict.",
  },
  sync_cv_pdf: {
    args: {},
    returns:
      "Makes the stored CV PDF (one object in the OG bucket, served at /dustin-edwards-cv.pdf) the one for the CV D1 " +
      "holds now: renders it through Browser Run and replaces the object, then purges the cv tag, unless the stored " +
      "object already carries that CV's fingerprint, in which case it renders nothing. A render that fails is an " +
      "error and changes nothing stored. Run sync_cv first when cv-drift also fails. Idempotent. A read-back " +
      "reconciliation: expected, present, and a converged verdict.",
  },
  sync_roster: {
    args: {},
    returns:
      "Converges D1 to the repository's content/roster files: re-compiles every cohort whose file's blob sha " +
      "differs from its row (or has no row), removes rows whose file is gone, through the same compile and write " +
      "doors a roster save uses, then purges the pages that embed the roster. Refuses an empty file set, and " +
      "answers 422 naming any file the validator refuses after converging the rest. Idempotent. A read-back " +
      "reconciliation: expected, present, and a converged verdict.",
  },
  sync_phages: {
    args: {},
    returns:
      "Converges D1 to the repository's content/phages files: re-compiles every phage whose file's blob sha " +
      "differs from its row (or has no row), removes rows whose file is gone, through the same compile and write " +
      "doors a phage save uses, first re-deriving the page /research/phages (its table, twin and search records) from " +
      "the final set, then purges the pages tag. Refuses an empty file set, and answers 422 naming any file the " +
      "validator refuses after converging the rest. Idempotent. A read-back reconciliation: expected, present, and a " +
      "converged verdict.",
  },
  sync_registry: {
    args: {},
    returns:
      "Converges D1 to the repository's content/registry/<kind>/<id>.md files: re-compiles every item whose file's " +
      "blob sha differs from its row (or has no row), removes rows whose file is gone, through the same compile and " +
      "write doors a registry save uses, then purges the registry tag. Refuses an empty file set while rows exist, " +
      "rather than deleting every row; with no files and no rows there is nothing to do and it says so. Answers 422 " +
      "naming any file the validator refuses after converging the rest. Idempotent. A read-back reconciliation: " +
      "expected, present, and a converged verdict.",
  },
  list_mentions: {
    args: { status: "string, optional: unverified, pending, approved, rejected or failed" },
    returns:
      "Received webmentions, newest first, with id, source, target slug, status, " +
      "author, excerpt and the received, verified and decided timestamps. " +
      "Unfiltered when no status is given, which is what the moderation queue " +
      "shows.",
  },
  decide_mention: {
    args: {
      id: "number, the mention row id from list_mentions",
      decision: "string: approve, reject or delete",
    },
    returns:
      "changed, and the target slug when a row moved. Approving or rejecting " +
      "PURGES that post's cached page, so the change reaches readers on the " +
      "next fetch rather than within the ten minute shared-cache lifetime.",
    policy:
      "Approve and reject need write and are reversible. DELETE is refused " +
      "with 403 mention-delete-requires-admin: it removes the only copy of " +
      "what a stranger sent, and there is no repository behind this table. " +
      "Reject instead.",
  },
  list_procedures: {
    args: {},
    returns:
      "Every procedure row in D1 (protocols, recipes, computational procedures), drafts included: " +
      "slug, path, profile, title, draft (boolean), version, updated, the count of recorded gaps and, for a " +
      "protocol, biosafetyLevel (BSL-1, BSL-2 or MISSING until Dustin sets it), with the head sha and the " +
      "number of procedures.",
  },
  get_procedure: {
    args: { slug: "string" },
    returns:
      "The complete procedure file (raw), the head sha, the procedure as structured data (front matter, " +
      "sections, steps with their marks and flags), its recorded gaps, and any validation errors. The " +
      "format is docs/PROCEDURES.md.",
  },
  save_procedure: {
    args: {
      slug: "string",
      raw: "string, the complete procedure file including front matter",
      expectedHeadSha: "string, optional, the headSha get_procedure returned",
      isNew: "boolean, optional, inferred from whether the file exists",
    },
    returns:
      "commitSha, path, draft, gaps and unchanged (true when the file is identical to the committed one: " +
      "nothing is committed or rewritten and commitSha is the current head). The file is validated against its profile by the same code CI " +
      "runs, committed, and written to D1, so the page changes without a deploy. A file that fails is " +
      "refused with 422 and detail.errors, every message the validator gave.",
    policy:
      "An operator may edit, unpublish and republish a procedure. It may NOT publish one for the first " +
      "time (draft true to false, or a new procedure saved as published); that is refused with 403 " +
      "first-publish-requires-admin.",
  },
  list_publications: {
    args: {},
    returns:
      "Every publication row in D1, drafts included: slug, doi (null for a manuscript), stage (published or " +
      "submitted), type, title, year and draft (boolean), with the head sha and the number of publications. " +
      "A publication is written through Carrel (site-api), never through this surface, so there is no save tool.",
  },
  get_publication: {
    args: { slug: "string, the page slug (the DOI with every run of punctuation a hyphen)" },
    returns:
      "The complete publication file (raw), the head sha, draft, the compiled record when the file is valid, " +
      "and every validation error when it is not. The format is docs/PUBLICATIONS.md.",
  },
  refresh_citations: {
    args: { dois: "array of strings, optional: the DOIs to refresh; every published paper's when omitted" },
    returns:
      "refreshed (how many counts were replaced) and failures (doi, stage, detail for each count that could " +
      "not be read). A DOI that fails keeps the count it had. A partial failure is a 502 with the same " +
      "detail, so the caller sees it. The watchdog calls this weekly.",
  },
  check_publications: {
    args: {},
    returns:
      "Reads ORCID and Crossref and compares them with every publication row: notOnSite (DOIs the ORCID " +
      "record or a Crossref deposit naming the ORCID lists that no file carries, with title, year and " +
      "sources), nameMatches (Crossref works of the last 400 days under the owner's name and on no list, to " +
      "confirm by hand), notOnOrcid (published papers missing from the ORCID record), manuscripts (files " +
      "with no DOI yet) and failures. Writes nothing. A source that could not be read is a 502 whose detail " +
      "is the same report from the sources that answered. The watchdog calls this daily.",
  },
  list_pages: {
    args: {},
    returns:
      "Every Research, Teaching and Software prose page row in D1, drafts included: path, slug, title and " +
      "draft, with the count and the head sha. Pages are edited through Carrel, not here.",
  },
  get_page: {
    args: { path: "string, a registered page path such as /research/phages" },
    returns:
      "The complete page file (raw), the head sha, the page as structured data (front matter and body), " +
      "and any validation errors the file has now. The format is docs/PAGES.md. Read only: a page is " +
      "saved through Carrel.",
  },
  get_llms: {
    args: {},
    returns:
      "The complete content/llms.txt (raw) and its size, the head sha, any validation errors the file has " +
      "now, and whether the row /llms.txt is served from holds exactly this file. The format is " +
      "docs/LLMS.md. Read only: it is saved through Carrel, as the document llms.",
  },
  list_cv: {
    args: {},
    returns:
      "Every file of the CV in D1 (the profile, appointments, education, publications, grants, honors, talks, " +
      "courses, mentoring, service, development): slug, type and when D1 last wrote it, with the count and the " +
      "head sha. The CV is edited through Carrel, not here.",
  },
  get_cv: {
    args: { slug: "string, a CV file such as grants or profile" },
    returns:
      "The complete CV file (raw), the head sha, the file as structured data and any validation errors the " +
      "file has now. The format is docs/CV.md. Read only: a CV file is saved through Carrel.",
  },
  list_dictionary: {
    args: {},
    returns:
      "Every dictionary entry row in D1, drafts included: key, path (the Software page it opens), term and draft, " +
      "with the count and the head sha. Entries are edited through Carrel, not here.",
  },
  get_dictionary: {
    args: { key: "string, the entry's file name without .md, such as capsid" },
    returns:
      "The complete entry file (raw), the head sha, draft, the entry as the page reads it when the file is valid, " +
      "and every validation error when it is not. The format is docs/DICTIONARY.md. Read only: an entry is saved " +
      "through Carrel.",
  },
  list_roster: {
    args: {},
    returns:
      "Every Phage Discovery cohort row in D1, newest first: slug (the year), year, the photograph's path and the " +
      "count of names, with the count of cohorts and the head sha. Only what the public roster page already shows. " +
      "The roster is edited through Carrel, not here.",
  },
  get_roster: {
    args: { slug: "string, a cohort's file key: its four-digit year, such as 2025" },
    returns:
      "The cohort the committed file holds (year, photograph and names, the fields the public page shows), the " +
      "head sha, and any validation errors the file has now. The format is docs/ROSTER.md. Read only: a cohort " +
      "is saved through Carrel.",
  },
  list_phages: {
    args: {},
    returns:
      "Every phage row in D1, in the page's order (year, then name): slug, name, year, host, county, PhagesDB record, " +
      "genome-paper slug, former name and note, with the count and the head sha. Only what the public phage table " +
      "already shows. The table is edited through Carrel, not here.",
  },
  get_phage: {
    args: { slug: "string, a phage's file key: its name in lower case, such as acorn15" },
    returns:
      "The phage the committed file holds (the fields the public table shows), its raw file, the head sha, and any " +
      "validation errors the file has now. The format is docs/PHAGES.md. Read only: a phage is saved through Carrel.",
  },
  list_registry: {
    args: { kind: "string, optional: one registry kind, such as primer" },
    returns:
      "Every registry row in D1, in the site's order (kind, then name): kind, id, name, status and the kind's own " +
      "fields, with the count and the head sha. Drafts are included, marked by status. The registry is edited " +
      "through Carrel, not here (docs/REGISTRY.md).",
  },
  get_registry: {
    args: { slug: "string, an item's address <kind>/<id>, such as primer/m13-forward" },
    returns:
      "The item the committed file holds, its raw file, the head sha, the fields recorded as MISSING (what is " +
      "waiting for Dustin), and any validation errors the file has now. The format is docs/REGISTRY.md. Read only: " +
      "an item is saved through Carrel.",
  },
  purge_zero_results: {
    args: {},
    returns:
      "purged, the number of zero-result query rows removed: every row whose last_seen is older than " +
      "ZERO_RESULT_RETENTION_SECONDS. The watchdog calls this daily; there used to be an admin button for it.",
    policy:
      "Takes no argument that names a row, so there is nothing to target beyond the fixed retention " +
      "cutoff: a caller cannot choose what is removed. The rows have no derivation to rebuild them from, " +
      "which is why this is destructive rather than a sync, but there is no identity-based refusal to " +
      "document because every caller gets the same unconditional sweep.",
  },
  backup_media: {
    args: {},
    returns:
      "Copies every MEDIA object that has no byte-identical twin into " +
      "MEDIA_BACKUP. COPIES ONLY: it has no delete branch in either bucket, " +
      "and nothing is ever copied backup to media. Idempotent. A read-back " +
      "reconciliation: objects, twins, missing and mismatched, counted after " +
      "the writes rather than from the loop's own counters.",
  },
  backup_d1: {
    args: {},
    returns:
      "Dumps every D1 table to the BACKUPS bucket under backups/json/<run id>/, checks the dump against " +
      "the backup policy (the required non-derived tables are present, nothing credential-bearing is in it, " +
      "it is not empty), writes the completion marker only if the check passes, then prunes runs outside " +
      "the retention policy (14 daily, 8 weekly, 6 monthly). Returns run_id, exported_at, tables, rows, " +
      "kept, pruned and locked (stale runs an R2 bucket lock kept). A failed check answers an error and leaves the run without its marker. The watchdog " +
      "calls this nightly.",
  },
  upload_media: {
    args: {
      data: "string, optional, base64 bytes or a full data: URI. Either this or url.",
      url: "string, optional, an https URL the API fetches server-side. Either this or data.",
      type: "string, optional, the image MIME type. Defaults to the data: URI's " +
        "own type or the fetched response's Content-Type.",
      name: "string, optional, the filename to record as original_name. Defaults " +
        "to the URL's last path segment, or upload.<ext>.",
    },
    returns:
      "url and key, the same two the editor's upload returns, plus bytes, " +
      "width, height and `recorded`. The url is what goes in the markdown. " +
      "Idempotent: the key is a digest of the bytes, so the same image " +
      "uploaded twice is one object.",
    policy:
      "MEDIA is the irreplaceable bucket and there is no delete tool over " +
      "this token, so an object put here stays until an admin removes it. " +
      "Accepts only the raster image types in ALLOWED (SVG is refused: it " +
      "can carry script), refuses anything over " +
      "MAX_BYTES, and fetches only https URLs.",
  },
};