Appendix A: the orchestrator contract#
A prompt that lists phases is a suggestion, and a model under load will quietly drop a step from a suggestion and report having done it anyway. That failure is what the chapters before this appendix spent their length arguing a reader out of building. The alternative is an orchestrator that reads a contract instead of a suggestion: a document fixed before the run starts, naming the order of work, what a tool call has to carry before it counts as a decision rather than a guess, who is allowed to decide what, and what has to be written down whether the run went well or badly. This appendix is that contract, written so its names can be swapped for a different project's own vocabulary while the obligations underneath keep holding.
What follows generalizes a contract written for a live system elsewhere that runs against real targets with a model in the loop. The specifics have been stripped and the names changed; the shape of who decides what, and what evidence a decision needs before it counts, has not.
Contract status and enforcement#
Treat everything below as something to build, not as a report on something already running. No file in this project's own reference code enforces a rule stated here, and no test in this project checks one: a search of the reference implementation and its test suite for a stage machine or an orchestrator contract turns up other, unrelated uses of both words and nothing that fixes a sequence a model cannot talk its way around. A reader who takes this appendix for a description of shipped behavior has been misled about what they are holding; a reader told plainly that it is a specification to implement has been handed something they can actually use, which is the more useful framing as well as the honest one. Where a rule below can be enforced only by an orchestrator choosing to follow it, and not by a mechanism that refuses a violation outright, that limit is named at the rule itself rather than left for an adopter to find out the hard way.
The stage sequence#
Fix the stage order before a run begins. The orchestrator advances the stage only after its required record exists.
| Stage | Required input and decision | Recorded output |
|---|---|---|
| Setup | Establish run identity, authentication, budget and coverage depth. A human sets exhaustive coverage before the run because it can multiply request volume. | Run configuration and operator decision. |
| Observation | Map the visible surface without adversarial requests; fingerprint the stack and inspect disclosed client-side API references. Recall prior work before planning, but never use past silence to remove candidates from coverage. | Surface observations, source references and recalled context. |
| Gate | Apply a deterministic rule to the observations without making another network request. | Proceed, restrict or use a fixed fallback, with a reason. |
| Planning | Ground every candidate in a response, indicator, parameter or combination of existing findings. A new unsupported claim is ineligible. | Candidate list and evidence references. |
| Execution | Run eligible work in priority order and reconsider the plan when new evidence arrives. Stop on surface completion, not on a clock or headline percentage. If the configured budget runs out first, close as partial and record what remains untested. | Actions, captures, outcomes and skips. |
| Verification | Check borderline findings. Raising severity requires stronger proof than lowering it. | Verdicts tied to the original captures. |
| Reporting and close | Write findings and concrete next steps. Close even if the gate stopped testing before the first action. | Final report and terminal run state. |
The fallback for a weak signal or outright refusal is a small, pre-enumerated set of passive checks, followed by closure. Recall may change priority; it may not shrink the candidate population. Otherwise a candidate that was quiet in an earlier run can never produce the evidence that would correct its low priority.
Tool reachability follows the stage#
A fixed sequence only means something if the tools reachable at a given point are actually restricted to what that point allows; a sequence that still lets a testing tool run during observation is a fixed sequence in name only. Enforcement here comes in a strong form and a weak form, and a contract has to say which applies rather than let a reader assume the stronger form by default. The strong form restricts by construction: the caller is handed only the tool definitions the current stage allows, and a request for anything else is refused before it reaches a target, the way a well-built request router refuses a route it was never given. The weak form restricts by convention: the caller could ask for anything, and what stops it is the orchestrator itself declining an out-of-stage request as a matter of policy, which holds exactly as well as that discipline holds and not a step better. This document assumes the weak form unless an adopter builds the strong one, and says so here rather than implying a guarantee a policy alone cannot back. A gate decision narrows the tool set further once observation ends, and that narrowing is part of this same contract, not a separate concern, which the next section sets out in full.
The gate, and what each outcome still permits#
A decision point sits between observation and everything after it, computed once from what observation already produced (parameter counts, form counts, pages reached, an error rate, whether a defensive layer showed itself) with no request of its own. It resolves to a small, named set of outcomes, and each outcome fixes what the remaining stages may do, kept here beside the outcomes themselves so the table and the rule that produces it cannot drift apart the way a table copied into a second document eventually always does.
| Outcome | What it still permits |
|---|---|
| clear | the full stage sequence, and the complete tool set |
| restricted | the sequence continues, but planning and testing may select only passive, read-only checks |
| weak signal | skip straight to a small, fixed, already-enumerated battery of passive checks against the target's root, then close |
| refused outright | the same fallback battery as weak signal, then close |
Weak signal and refused outright reach the same action today, and that sameness is stated here as a choice, not asserted as an identity between them: a target that merely looks marginal and a target that blocked everything outright are different diagnoses that happen to earn the same response for now, and nothing here stops a later revision from splitting them, because nothing here asserts an underlying identity beyond that shared response. The fallback battery is fixed in advance as well: an adopter names their own short list of passive checks once, rather than leaving the choice to whatever the model feels like running against a target that has already shown it will not tolerate more.
The tool-call contract#
Every call a stage makes carries which stage it belongs to and a reason grounded in something the run actually observed, not a reason invented to satisfy the field. The reason is what turns a logged call into the record of a decision rather than the record of a bare act; a call with no reason attached tells a later reader that something ran and nothing about why it was chosen, which is most of what a trace like this is for. Where several contributors write into a single shared run (a fan-out across many targets sharing a single identity, for instance), the stage tag matters more than it does in an isolated run, not less: a tag that is missing and silently falls back to some default corrupts the combined record for every contributor sharing that run, not only for the call that left it out, so a shared run treats a missing tag as a loud failure rather than a quiet default.
Exactly one path exists for a request that touches the target adversarially; nothing reaches the target by a second route. That path is what makes a request replayable later as proof, what keeps a coverage and a memory record of the attempt, and what applies pacing that also protects the run itself from tripping the target's own defensive throttling. None of that is available to a call that goes around the path, and going around it is not stopped by construction here, only discouraged by naming the cost plainly: a request sent straight at the target outside this path is invisible to coverage, invisible to memory, and unprotected by pacing, which is a real gap in this contract's own enforcement, stated plainly rather than smoothed over.
A malformed or out-of-contract call does not always fail the same way, and the difference matters to an adopter deciding how to build their own version. Some are refused outright: a continuation handle the server no longer recognizes is an error to fix by re-deriving the handle, never a license to restart an entire enumeration from the beginning, and a severity raise submitted without the evidence the next section requires is downgraded mechanically to a non-verdict regardless of what the caller claimed. Others are accepted but rendered inert: a record meant to teach a future run something can be written with no stable key back to the context it came from, and it will sit there having been accepted, unable ever to be retrieved again by the thing that was supposed to read it. A contract worth adopting states, for each call it defines, whether a malformed instance is refused outright or merely accepted and left quietly useless, because the second failure shape is far easier to build by accident and far more expensive to notice later.
Division of authority#
Authority in a run built this way splits into recurring tiers, and keeping them separate is most of what makes a run auditable at all. The orchestrator (a fixed procedure that may itself be run by a model, but is bound to the sequence above rather than free to reorder it) decides which plan entry runs next, when accumulated signal justifies re-planning, and when the plan is exhausted. A layer beneath the model, deterministic and unmoved by argument, decides what the model is not allowed to talk its way past regardless of how it argues: whether a claimed severity raise actually meets its evidence bar no matter what verdict rode alongside it, whether a target is in scope, whether a severity may move downward at all. And a human keeps a short list of decisions the model is never handed, at any confidence: authorizing a high-consequence chained action that no setting fixed before the run began already covered, extending the run's own permission level once the run is under way, and overriding an explicit stop.
A live human watching the run can queue guidance mid-run, and that guidance outranks the orchestrator's own ordering for the turn it arrives on, but it is additional input to the decision-maker already running, never a second decision-maker arriving alongside it. The distinction matters because a contract that let live guidance spin up its own actor would have quietly reintroduced the problem this whole document exists to close off: multiple actors now able to act, with no fixed rule for who wins when they disagree.
The evidence asymmetry#
Evidence supporting a finding is graded, not taken on faith: a captured request paired with its captured response, replayable end to end, is strong; a captured request with no matching response, or a command run with substantive output, is moderate; an account with nothing captured behind it is thin. Lowering a severity, marking a finding a false positive, or declining to decide costs nothing extra to justify: the skeptical default is free, and a reviewer, human or model, may take it on their own say-so. Raising a severity is the one move on this list that is expensive, on purpose: it requires a verbatim quotation of the evidence that proves the higher band, a properly derived score that actually justifies that band rather than merely asserting it, and an evidence grade no lower than moderate. All three conditions are required. A deterministic layer checks this, not the reviewer's own claim about having checked it: a raise submitted without its quotation is recorded as undecided no matter what severity label rode alongside it, which is a place in this contract where the model proposes and something else disposes. Review itself is reserved for a defined slice of the output (the critical and high bands, plus anything at any band whose evidence graded thin) and everything outside that slice is left alone rather than reviewed for the sake of being thorough. An empty slice is logged as empty, because a run that skipped review for lack of anything borderline looks, from the outside, identical to a run that skipped review by not looking, and only the log distinguishes an empty slice from a skipped look.
Refusal rules and standing limits#
A short list of actions requires an explicit human go-ahead every time, regardless of how much standing authorization otherwise covers the target, and no aggression setting and no prior approval reaches into this list: load or denial-of-service-shaped testing; anything destructive, including bulk deletion, defacement, or locking out accounts at scale; pivoting onto infrastructure that is not actually part of the engagement, or anything resembling a supply-chain compromise; and evading detection for a purpose the engagement itself does not call for. These hold beneath any scope declaration, not alongside it: a scope that admits a host does not thereby admit an action on this list against that host.
Ordinary scope decisions (whether a given host may be touched at all) resolve in a fixed order: an explicit exclusion wins first, an explicit inclusion wins next, and failing both, a target is admitted only if it shares the engagement's own base, rather than by default admitting whatever a discovery step happened to turn up. That check holds both as an instruction the orchestrator follows and, independently, as a mechanism enforced beneath every outbound call that does not depend on the model remembering the instruction. Neither alone is sufficient: the instruction can be forgotten over a long enough run, and the independent check can itself be misconfigured or switched off, but the instruction and the independent check together catch what either alone would eventually miss.
Nothing discovered and then left untested disappears from the record: every excluded or untested unit is named with a reason (out of scope, unreachable, a duplicate of something already covered, or cut by a budget) and that ledger reaches whoever reads the results, not only a log nobody opens. Where a lower-stakes copy of a target exists alongside a higher-stakes one, exploitation depth belongs on the lower-stakes copy first, with only an already-confirmed result replayed against the copy that actually matters if something goes wrong.
The reporting contract#
A run reaches a closing state on every path, including a run that stood down at the gate and tested nothing, and including a run with nobody watching who could have granted permission to keep waiting. An unattended run that would otherwise pause (for a human's answer, or for a defensive block to lift) backs off briefly instead and proceeds with whatever it has, because a run stuck open forever, waiting on a human who was never coming, is the failure this rule exists to prevent, and it is worse than an honestly partial result that actually closed. A run that found little or nothing closes and says so; it does not retry on the chance that the next attempt does better.
Coverage is reported as more than one figure, never collapsed into a single number, because the figures answer different questions: whether a unit of surface received any successful execution at all, down to a bare read, against whether every applicable check for that unit actually ran. A touched figure can read very high on a run that tested nothing whatsoever, because a bare read reaches almost everything a crawl reaches; reporting only that figure as coverage is the mistake this split exists to rule out. A gap between a touched figure and a tested figure is not a defect needing an excuse. It is information: surface that was seen, but not tested, named as such.
Deterministic reconciliation of every finding's severity runs at the close of every run, without exception, including a run that never reached active testing and produced nothing but the fixed fallback battery's own output, so that even the smallest, most defensive result a run can produce still passes the same floor before it reaches a reader. A narrative chain is presented as a chain only when it actually combines more than a single already-recorded finding with a real evidentiary link back to each finding it claims to combine; a suggested follow-up is presented as concrete and actionable, never as an unattributed placeholder with nothing behind it. Where several contributors feed a single shared run, only the coordinating actor closes it: a contributor's own job ends at handing back a fixed, structured summary of what it did, nothing appended after it, and closing the run is never a contributor's to do.
What enforces this today#
Say plainly, once more, what this appendix is and is not. It is not a description of a system already running; it specifies one. Nothing in this project's own reference code checks a rule above, and treating this document as though something already enforced it would be the exact misreading the opening of this appendix warned against. What it offers instead is a decision already made, on paper, before whoever adopts it has to make the same decision under worse conditions: mid-run, under load, with a client waiting on the other end of the report. Write it down now, while nobody is under pressure reading it, or relearn each piece of it later, live, whenever a run finds the gap.