PromptRunner.Control.Amendment (PromptRunnerSDK v0.10.0)

Copy Markdown View Source

Changing what "done" means for a prompt, on the record.

An amendment adds, removes, or modifies a clause of a prompt's verify contract. It is the one capability in this program that can make a completed prompt mean something other than what the packet says, so it is governed more tightly than steering, which cannot.

Timing is part of the meaning

Claims in these programs are pre-registered by git commit timestamp precisely so nobody can decide what they were proving after seeing the result. An amendment that weakens a contract after a verify failure is exactly the move pre-registration exists to prevent. So the record distinguishes:

  • :pre_verify — before any verify attempt has run. Ordinary scope correction.
  • :post_failure — after a verify failure. Suspect by default.
  • :post_success — after a verify pass. Also after the fact, and named honestly rather than folded into either of the other two.

An amendment log that does not say when, relative to verification is not an audit trail.

Asymmetric by design

Adding a requirement is routine. Removing or relaxing one is the risky direction, and takes a different verb — PromptRunner.Control.relax/3, with an explicit confirmation — never a different argument to the same command.

Run-local by default

The packet file stays authoritative. A future re-run from clean state uses the original contract. Writing back to the packet is a separate explicit act, because a packet is a versioned artifact and editing it is a commit, not a side effect.

Summary

Functions

Appends one amendment to the prompt's amendment log.

Builds an amendment record, refusing the ones that must not be silent.

A readable packet-versus-enforced diff for one prompt.

Applies every recorded amendment for a prompt to its packet contract.

Every amendment recorded for one prompt, oldest first.

Types

phase()

@type phase() :: :pre_verify | :post_failure | :post_success

t()

@type t() :: %{
  :at => DateTime.t(),
  :prompt_id => String.t(),
  :author => String.t() | nil,
  :reason => String.t(),
  :phase => phase(),
  :direction => :add | :relax,
  :clause => String.t(),
  :operation => :add | :drop | :replace,
  optional(:entries) => [term()],
  optional(:run_id) => String.t() | nil,
  optional(:persisted) => boolean()
}

Functions

append(packet_dir, amendment)

@spec append(String.t(), t()) :: :ok | {:error, term()}

Appends one amendment to the prompt's amendment log.

build(opts)

@spec build(keyword()) :: {:ok, t()} | {:error, term()}

Builds an amendment record, refusing the ones that must not be silent.

A missing reason is refused rather than defaulted: an amendment with no stated reason is not an audit trail entry, it is a hole in one.

diff(packet_contract, enforced_contract)

@spec diff(map(), map()) :: [{:same | :added | :removed, String.t(), String.t()}]

A readable packet-versus-enforced diff for one prompt.

If you cannot show the diff, you do not have the audit.

dir(packet_dir)

@spec dir(String.t()) :: String.t()

enforced_contract(packet_contract, amendments)

@spec enforced_contract(map(), [map()]) :: map()

Applies every recorded amendment for a prompt to its packet contract.

This is what the verifier is handed. The packet's own contract is never mutated in place — it is the input to this function, and re-reading the packet gives it back unchanged.

path(packet_dir, prompt_id)

@spec path(String.t(), String.t()) :: String.t()

read(packet_dir, prompt_id)

@spec read(String.t(), String.t()) :: [map()]

Every amendment recorded for one prompt, oldest first.