Skip to main content

Write a practice script

A practice script returns one evidence-bounded candidate for one assigned item. It can decide from captured records or ask the review model to decide. Every candidate passes the same server admission.

Start with the built-in behavior​

  1. Open the practice editor.
  2. Open Script.
  3. Enter a default export with meta and run(ctx).
  4. Return the candidate from ctx.review().
  5. Save the practice.
import { definePracticeScript } from "@hephaestus/practice-scripts";

export default definePracticeScript({
meta: {},
async run(ctx) {
return ctx.review();
},
});

A script can import only the script API. For provider evidence, use its validated SCM, chat or docs kit. A null Script runs the built-in script. An invalid custom script stops without fallback.

The context​

The host supplies the practice version, criteria, assigned scope, origin and About. The core item record is unknown until a domain kit validates it. Bigint IDs are decimal strings, not JavaScript numbers. A script cannot choose another workspace, developer or recipient.

The callable ctx.review also carries the frozen review's metadata. Pass leads, facts, directions, prepared context or limitations to its call. Only the final return from run reaches admission. A review call by itself creates no persisted result.

For an ITEM review, scope names that item and version. For a SCAN, scope names one file at the frozen default-branch commit. The product scan concerns its repository. About is null. The previous result is metadata for the same assigned claim, not authority to repeat old proof.

Use a domain kit​

A domain kit validates the item record before it exposes source-specific helpers. This SCM example preserves the normal review and adds a typed fact:

import { definePracticeScript } from "@hephaestus/practice-scripts";
import { scm } from "@hephaestus/practice-scripts/scm";

export default definePracticeScript({
meta: {},
async run(ctx) {
const work = scm(ctx, "scm.pull_request");
return ctx.review({ facts: { changedFiles: work.item.changed_files } });
},
});

The kit states the kinds of work that your script reads, and another kind stops the script. It does not change the selected workspace, item or developer. Do not cast the unknown core item into a provider payload.

To find places in changed code, give each language its rules:

const found = await work.sites({
swift: [{ kind: "forced-try", label: "A changed try! expression.", test: /\btry!/u }],
});
const directions = found.leads.length > 0 ? ["Read whether each one can fail."] : [...found.noMatch];
return ctx.review({ leads: [...found.leads], directions });

A site is on an added line of the change, or anywhere in a scan's file. Comments and strings do not match, unless you set comments: true. Which lines match can improve between releases. Each lead is a place for the review to read, never a verdict.

The script API​

These names are the whole API. They hold for a major version of Hephaestus. A script can import nothing else, also not a Node module or a package.

ImportNames
@hephaestus/practice-scriptsdefinePracticeScript, UnratedError, listWithin
@hephaestus/practice-scripts/scmscm, isTestPath, languageOf, checkableItems, issueReferences, branchIssueReferences, issueShape, subIssueRollup, later, dated
@hephaestus/practice-scripts/chatchat
@hephaestus/practice-scripts/docsdocs

The work object from scm has kind, item, head, change, changedFiles, sources, sites, commits, linkedIssues, inlineComments, conversationComments, reviewRecord and checks. A read that returns null means that the source was not captured. It never means empty. A malformed record stops the script.

A direction or a fact that is too long is folded to one line and cut. An empty fact is dropped. More than 10 directions or 200 leads is an error of the script.

Return a result​

Use Met, Not met, Does not apply or Could not determine. The outcome contract owns their meanings and requirements. Each problem has its own severity, citations and claim basis. Do not return result-level severity. Not met can retain open questions beside established problems. Operational read or model failure does not become an open question.

Return the result of ctx.review, or a result that your code decided, without coverage. The script ends at ctx.review: code after it does not run. Hephaestus measures coverage from the reads of the script, as the pipeline states.

A scan Met needs a trusted complete read of the assigned file. A listing, search match or claimed count alone does not prove that read. Citations must quote authorized captured text. The host verifies location, version and domain basis before the result can support feedback.

A result that your code decided must rest on evidence that your code read. The host refuses it with the reason Decided without its evidence in these cases, even if your code caught the error:

  • A read failed.
  • A record was not captured.
  • A kit could not trust a record.

A failure to read is never a statement about the work. You can still call ctx.review after such a failure. The result's coverage then names what your script could not read. A partial read or a cut search can support Not met, but not Met or Does not apply.

Use the review model​

Keep meta empty. A stored script with any metadata field is invalid. Prepare evidence and call ctx.review to open the review. The script context exposes no model slots or direct model helpers.

The review follows the developer's account-wide AI choice and its stricter model tier. Configure the review model and budget in the workspace. The model guide owns model usage and prices.

Test a script locally​

Use a checkout with the repository's pinned Node and Vite+ versions. Keep the tutorial file in an ignored scratch directory. Use the toolkit's fixture style to supply strict version 3 inputs. Mock models and captured evidence rather than reaching live providers.

vp run test:agents

Run the focused toolkit test file while you develop the script. Then run the agent suite before deploying it. Include both ITEM and SCAN cases when the practice supports scans. Test a refused read and an unavailable required model as operational failures. Never assert that a missing required source produces Could not determine.

Check that it worked​

  1. Request a review of eligible work, as First successful review describes.
  2. Open the review, then open Practice scripts. It shows the status of the script, its model calls, its coverage and the first safe error line.

Withheld means the script or candidate was refused with a recorded reason. Not reached means execution did not reach that practice before the review stopped. Result admission and feedback delivery remain separate facts. Execution details can expire while durable review history remains available.

Upgrade a script from an earlier release​

If the default export has the wrong shape, execution stops with “The script's default export is not a practice script. Write it with definePracticeScript.” A script that imports anything else stops and names the import. Its practice is withheld with Not a runnable script. Use the operator migration entry and the runner contract to upgrade custom scripts.

Limits​

Each script uses the attempt's deadline. No separate time is reserved before review. The host owns provider calls and their budget reservations. Failed or unreported calls retain their reservation. The cost guide owns model usage and prices. The runner contract owns proof size and citation limits.