# The lifecycle of a check

A useful way to internalise how `solforge` operates is to follow a single security check from invocation to exit. This chapter does that. The example used throughout is the built-in `missing-signer-check` rule, which detects instructions that mutate an account without requiring a `Signer` constraint on the caller.

## Invocation

The developer runs:

```
$ solforge security 9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin
```

The CLI parser routes the command to the security subsystem, which immediately delegates the program-ID argument to the IDL loader. If the loader cannot resolve an IDL — neither locally nor on chain — the run terminates with a non-zero exit code and a single line of diagnostic output. No partial output is produced; the framework prefers an honest failure to a half-finished report.

## Rule selection

Once the IDL is in hand, the security engine walks the registered rule catalogue and asks each rule a single question: *given this IDL, do you wish to run?* `missing-signer-check` answers yes if the IDL contains at least one instruction with at least one account marked `isMut=true`; otherwise it skips itself.

Rule selection is deliberately decoupled from rule execution. A rule that decides to skip is not visible in the report; a rule that decides to run is visible even if it produces no findings. This convention makes the report reproducible: the same IDL produces the same set of *evaluated* rules, regardless of whether any of them actually fired.

## Predicate evaluation

For every instruction that survives the rule's selection step, `missing-signer-check` walks the instruction's account list. For each account marked `isMut=true`, it inspects the surrounding constraint set. If neither the account itself nor any account in the same instruction carries `isSigner=true`, a finding is emitted.

Each finding is a structured record:

| field         | example                                                         |
| ------------- | --------------------------------------------------------------- |
| `rule_id`     | `missing-signer-check`                                          |
| `severity`    | `high`                                                          |
| `instruction` | `withdraw`                                                      |
| `account`     | `vault`                                                         |
| `rationale`   | `vault is mutable but no Signer is required on the instruction` |

The rationale is human-readable and is the only part of a finding intended for a non-engineer audience. It exists so that a reviewer can read the report without consulting the source.

## De-duplication and sort

After every rule has been evaluated, the security engine de-duplicates findings by `(rule_id, instruction, account)` and sorts the survivors by severity, then by instruction name, then by account name. This sort is stable across runs; CI diff is meaningful.

## Reporting

The findings are rendered as a table on stdout and, if `--json` was passed, also written to stderr-redirectable JSON. The exit code is `0` if the highest severity is below the configured threshold and `1` otherwise. The default threshold is `high`; this is the recommendation, not a hard requirement, and is documented in [Configuration files](/solanatestforge-docs/part-ii-reference/configuration.md).

## What this lifecycle implies for custom rules

Writing a custom rule, then, is a matter of implementing two functions: a selector that returns a boolean given an IDL, and an emitter that returns an array of findings given an IDL. Everything else — caching, sorting, formatting, exit-code computation — is handled by the framework. The recipe chapter [Authoring a custom security check](/solanatestforge-docs/part-iii-recipes/authoring-custom-checks.md) walks through a complete example.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://solanatestforge.gitbook.io/solanatestforge-docs/part-i-foundations/lifecycle-of-a-check.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
