Skip to Content
EngineeringDecisions (ADRs)ADR 0030 Skills run on the server as ToolSpecs and worker jobs, recorded in tool_calls, gated by verified references

ADR 0030: Skills run on the server as ToolSpecs and worker jobs, recorded in tool_calls, gated by verified references

Status
Accepted
Date
Deciders
Founder

Context

A skill is a deterministic, versioned procedure a scientist recognises (a FIH dose projection, an NCA, a SAR table, an SOP readiness check). Fact (verified in source): the only existing skill, skills/sop.py, is a container-local tool that leaves no tool_calls row; the server numbers tool_calls.seq per run and allowlists.py returns unknown_tool for any key not in TOOLS_BY_KEY. A skill that runs where the record cannot see it is not a record.

Decision

  • Class A (numeric and checklist) skills are server-side ToolSpec(kind='read', roles=(<specialist run role>,), permission='<product>.agents.use', output_schema=...) with the function in api/app/skills/<name>.py; they inherit check_allowed, live permission, tool_calls, audit, sanitiser and the plan gate. Class B (library) skills run as worker jobs (the chemistry library and the dataset conformance engine; never in the agent image) started by a server-side handler and awaited under the job timeout.
  • tool_calls gains check_logic_version, inputs_hash, result_artifact_id, output_violations. A workbench-started run (POST P/skill-runs) is synchronous and stores a skill_result artifact with run_id NULL (the persona_brief precedent). No skill_run run kind; run kinds stay 13.
  • Typed inputs only, through the vocabulary layer (api/app/skills/vocab.py: UCUM units, species and km factors, routes, dose forms); a value without a unit is refused unit_required.
  • Reference gate: guidance constants live in api/app/skills/references.json with url, sha256, retrieved_on, version, supersession, constants, status; skill-references-check in make check and the release check fails when a preview or available skill references a to_verify entry; every output names the guidance version and retrieval date.
  • Fixed phrases keep outputs honest: “Projection for your decision, not a starting dose”; “PAD/MABEL not computed”; “investigation phase and candidate class”; “checked against <document> <version>”; “Compounds mentioned in patent documents; not claim coverage, not an opinion”.

Consequences

  • resolve_sop_binding is retired once qa.check_readiness lands; no container-local skill remains after R4.
  • tox.fih_dose_projection ships preview in R4 only if its references verify by week 2, else designed. Its three references verified on 2026-09-16 (5 of 13 entries verified); every skill is nonetheless served designed while R4_GATE_PASSED = False in api/app/skills/registry.py, the flag being the release act after the gate list passes (SkillSpec.intended_status is internal).
  • Skills add no permission key: they exercise the product’s existing agents.use.

Alternatives considered

  • Skills as agent prompts: non-deterministic numbers with no record; rejected.
  • A skill_run run kind: a model-less run row for a synchronous call; rejected in favour of the artifact.
  • The chemistry library inside the agent container: the container’s only endpoint is the tool server; rejected.

Follow-ups

  • Golden datasets for the NCA and FIH skills named in references.json. The NCA golden test runs against a constructed one-compartment dataset with analytically known parameters (pk_golden_synthetic_one_compartment, to_verify; recorded 2026-09-16); replace it with a published dataset under an open licence and hash it in references.json.
  • PK/PD simulation as a separate worker container (R6, planned).