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 inapi/app/skills/<name>.py; they inheritcheck_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_callsgainscheck_logic_version, inputs_hash, result_artifact_id, output_violations. A workbench-started run (POST P/skill-runs) is synchronous and stores askill_resultartifact withrun_id NULL(thepersona_briefprecedent). Noskill_runrun 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 refusedunit_required. - Reference gate: guidance constants live in
api/app/skills/references.jsonwithurl, sha256, retrieved_on, version, supersession, constants, status;skill-references-checkinmake checkand the release check fails when aprevieworavailableskill references ato_verifyentry; 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_bindingis retired onceqa.check_readinesslands; no container-local skill remains after R4.tox.fih_dose_projectionshipspreviewin R4 only if its references verify by week 2, elsedesigned. Its three references verified on 2026-09-16 (5 of 13 entries verified); every skill is nonetheless serveddesignedwhileR4_GATE_PASSED = Falseinapi/app/skills/registry.py, the flag being the release act after the gate list passes (SkillSpec.intended_statusis 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_runrun 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 inreferences.json. - PK/PD simulation as a separate worker container (R6,
planned).