diff --git a/.github/ISSUE_TEMPLATE/ambiguous-effect.yml b/.github/ISSUE_TEMPLATE/ambiguous-effect.yml index 0f533731..f9768edc 100644 --- a/.github/ISSUE_TEMPLATE/ambiguous-effect.yml +++ b/.github/ISSUE_TEMPLATE/ambiguous-effect.yml @@ -5,7 +5,7 @@ body: - type: markdown attributes: value: | - `AMBIGUOUS` means nobody knows whether the remote acted, and CTRLRun refuses to guess. Most of the time that is the library doing its job: a timeout, a lost reply, a worker that died mid-call, or a lost connection during a store `COMMIT` all land here on purpose. This template is for the case where the events do not explain why, or where you believe the outcome was knowable. + `AMBIGUOUS` means nobody knows whether the remote acted, and ctrlrun refuses to guess. Most of the time that is the library doing its job: a timeout, a lost reply, a worker that died mid-call, or a lost connection during a store `COMMIT` all land here on purpose. This template is for the case where the events do not explain why, or where you believe the outcome was knowable. To move the record on: ask the remote what happened, then `ctrlrun resolve --committed` or `--failed`. A `reconcile` hook does the same automatically. Nothing else does. - type: input @@ -41,4 +41,4 @@ body: id: expected attributes: label: What you expected, and why - description: If you believe the outcome was knowable at the time, say what CTRLRun could have observed that it did not. + description: If you believe the outcome was knowable at the time, say what ctrlrun could have observed that it did not. diff --git a/.github/ISSUE_TEMPLATE/feature.yml b/.github/ISSUE_TEMPLATE/feature.yml index 9bf11c0b..b6875ad2 100644 --- a/.github/ISSUE_TEMPLATE/feature.yml +++ b/.github/ISSUE_TEMPLATE/feature.yml @@ -1,5 +1,5 @@ name: Feature request -description: Something CTRLRun should do that it does not. +description: Something ctrlrun should do that it does not. labels: [enhancement] body: - type: markdown @@ -25,7 +25,7 @@ body: id: situation attributes: label: The situation - description: What the agent does, what can go wrong, and what CTRLRun would need to refuse or record. Real domains help — money, infrastructure, permissions, records, communications. + description: What the agent does, what can go wrong, and what ctrlrun would need to refuse or record. Real domains help — money, infrastructure, permissions, records, communications. validations: required: true - type: textarea diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml index d10c2d4a..33efd2cd 100644 --- a/.github/codeql/codeql-config.yml +++ b/.github/codeql/codeql-config.yml @@ -14,7 +14,7 @@ # five findings they produced were triaged individually and dismissed with a reason recorded in # the code-scanning tab. -name: "CTRLRun Python" +name: "ctrlrun Python" query-filters: # 79 findings, every one of them `...`. The Ellipsis literal is a statement with no effect, and diff --git a/.github/workflows/traffic.yml b/.github/workflows/traffic.yml index 59e7cc1a..7222ac1f 100644 --- a/.github/workflows/traffic.yml +++ b/.github/workflows/traffic.yml @@ -79,7 +79,7 @@ jobs: -H "X-GitHub-Api-Version: 2022-11-28" \ "https://api.github.com/repos/$REPOSITORY/traffic/clones") if [ "$status" = "403" ] || [ "$status" = "404" ]; then - echo "::warning::TRAFFIC_TOKEN cannot read $REPOSITORY traffic (HTTP $status). It needs Administration: Read-only on this repository (fine-grained, resource owner CTRLRun) or the classic repo scope; a fine-grained token may also be waiting on org approval. The clones badge keeps its last value until then." + echo "::warning::TRAFFIC_TOKEN cannot read $REPOSITORY traffic (HTTP $status). It needs Administration: Read-only on this repository (fine-grained, resource owner ctrlrun) or the classic repo scope; a fine-grained token may also be waiting on org approval. The clones badge keeps its last value until then." echo "read=false" >> "$GITHUB_OUTPUT" exit 0 fi diff --git a/.gitignore b/.gitignore index 78abf0bf..d4694f82 100644 --- a/.gitignore +++ b/.gitignore @@ -21,7 +21,7 @@ venv/ .ruff_cache/ .coverage -# CTRLRun local state (SPEC-v0.1 §5, §6) +# ctrlrun local state (SPEC-v0.1 §5, §6) .ctrlrun/ # Anchored: this ignores the operator's own policy at the repo root, which `ctrlrun init` # writes. Unanchored it matched every `ctrlrun.yaml` at any depth, which silently kept the diff --git a/CHANGELOG.md b/CHANGELOG.md index ec13b5b9..041cdd34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -343,7 +343,7 @@ since `SPEC-v0.6.md` §6.4. no revocation, no signing. Signing stays off the roadmap for the reason `SPEC-v0.6.md` §11 gives, and a test greps this module's own source to keep that true. -- `ctrlrun.anchor`: `AnchorProvider` (a four-call protocol you implement, because CTRLRun ships no +- `ctrlrun.anchor`: `AnchorProvider` (a four-call protocol you implement, because ctrlrun ships no timestamp client and a network client does not belong in this wheel), `verify_anchors`, `AnchorReport`, `ANCHOR_BREAKS`, and `anchor=` on `Control`. - **`ANCHOR_BREAKS` is its own closed set and `CHAIN_BREAKS` does not change.** `anchor_broken`, @@ -488,7 +488,7 @@ that installs 0.10.0 and creates no hop can still roll back. sides' receipts carry the same hop id. And **nothing infers who the peer is**: the receiving agent's identity is resolved by the `IdentityProvider` and never read off the payload. - The four open questions are answered in the specification rather than left to an item. CTRLRun + The four open questions are answered in the specification rather than left to an item. ctrlrun defines no wire format and consumes none; what crosses a hop is a reference of two strings. A hop is a record in the store and not a claim in a token, which the budget rule forces and which costs cross-store propagation, refused fail-closed and by name. Depth across hops is @@ -984,7 +984,7 @@ policy cannot be changed by whoever holds the file. approver_role: payments-owner ``` - **CTRLRun does not interpret the role.** It does not know what `payments-owner` means, does not + **ctrlrun does not interpret the role.** It does not know what `payments-owner` means, does not check that such a role exists anywhere, and makes no compliance claim on the strength of one, exactly as it does not interpret `source:`. What changed about `SPEC-v0.6.md` §7.3's "attribution, not prevention" is one sentence: a control still decides no *action*, and now @@ -1124,7 +1124,7 @@ policy cannot be changed by whoever holds the file. ## [0.7.0] - 2026-09-11 - Execution boundary -Every milestone before this one asked what holds *inside* CTRLRun. v0.7 asks whether it holds at +Every milestone before this one asked what holds *inside* ctrlrun. v0.7 asks whether it holds at the edges the kernel does not control. The kernel does not decide whether the remote acted, an executor does. It does not own the clock its leases are measured against, once the store is on another host. It does not know whether the world still looks the way it did when a human said @@ -1702,10 +1702,10 @@ policy or authority document — and both are listed below with what they did be bullet list and the readiness block. Prose is down from 3,205 words to 2,662, with the demo transcript, the guarantee matrix, both receipt-chain disclaimers and the whole *It can't* section untouched. -- The documentation home page leads with what CTRLRun is rather than with its own name, and +- The documentation home page leads with what ctrlrun is rather than with its own name, and says the promise once instead of twice above the fold. Its `title` is the category line and its `description` the tagline, which is what `docs/IA.md` assigns to each; the browser tab no - longer reads *CTRLRun - CTRLRun*. + longer reads *ctrlrun - ctrlrun*. - `try-it` puts its controls above its explanation, in a wide column, with the policy below them rather than between the reader and the button. - Four badges: CodeQL, the documentation site, Ruff and `mypy --strict`. Downloads and stars @@ -1779,7 +1779,7 @@ have recovered or retrying work nothing can. cannot drift into permitting something SQLite refuses. - **`--store-url` accepts a `postgresql://` URL**, and is now on every command that reads or resolves the operator's own store — `receipts`, `effects`, `inspect`, `resolve`, `approve`, - `deny` — reading `CTRLRUN_STORE_URL`. CTRLRun's own `?ctrlrun_schema=` parameter selects the + `deny` — reading `CTRLRUN_STORE_URL`. ctrlrun's own `?ctrlrun_schema=` parameter selects the schema and is peeled off before the URL reaches the driver. **It creates nothing and migrates nothing.** A review found the first version doing both: a @@ -1818,7 +1818,7 @@ have recovered or retrying work nothing can. a load error: - **`controls:`** — a registry of ids, each with a `title` and an optional `source`. An action cites some, a rule may narrow or add, and the receipt carries the union of the action's and - the **matched rule's** in registry order. **CTRLRun does not interpret a control**: `source:` + the **matched rule's** in registry order. **ctrlrun does not interpret a control**: `source:` is a string the operator wrote and the registry records and never enforces. It maps to no standard, and citing one is not a claim about it. - **`data:`** — an action declares which of its arguments carry which class of data. @@ -1861,7 +1861,7 @@ have recovered or retrying work nothing can. The **reservation is still taken**, and the asymmetry is deliberate: in observe mode the action genuinely executes, so the effect record has to exist or the duplicate refusal has nothing to - refuse with. Observe mode suppresses CTRLRun's *decisions*; it does not suppress the record of + refuse with. Observe mode suppresses ctrlrun's *decisions*; it does not suppress the record of an effect that really happened. The `APPROVAL_CONSUMED` event on that path is **gone rather than renamed** — v0.6 adds no event type, and an event naming a write that did not happen is worse than no event. @@ -1977,7 +1977,7 @@ version line of its own. `docs/SPEC-scan.md` is the contract; it was written first and its §8 tests were red before any of it existed. `scan` reads a Python tree and a policy document and reports the consequential -call sites and policy entries CTRLRun is not covering — the gap between *installed* and *in the +call sites and policy entries ctrlrun is not covering — the gap between *installed* and *in the path*, which until now had no command. - **The honest half is the load-bearing half.** A scanner reports what it found where it @@ -2126,7 +2126,7 @@ exercise, not the two adapters, is what v0.5 is for. Three independent reviews and item 6 found five authorization defects in `ctrlrun-openai-agents` before it shipped. All are fixed, mutation-tested, and recorded here because the pattern matters more than any one of them: **the SDK's approval record is keyed to a -tool call, and a CTRLRun grant binds to an action hash**, so every defect was the same shape — +tool call, and a ctrlrun grant binds to an action hash**, so every defect was the same shape — reading a coarser answer as though it answered a finer question. - `interrupt()` returned `granted=True` unconditionally. @@ -2138,7 +2138,7 @@ reading a coarser answer as though it answered a finer question. masked an AMBIGUOUS refund — *safe to retry* reported for an effect that may have landed. - **Observe mode interrupted and blocked the action.** Found by item 6 without reading the adapter. §3.6's rule followed for one framework shape and had to be *required* of the other; - a deployment evaluating CTRLRun in the mode built for evaluating it would have had its agent + a deployment evaluating ctrlrun in the mode built for evaluating it would have had its agent halted. ### Added @@ -2203,7 +2203,7 @@ reading a coarser answer as though it answered a finer question. ## [0.4.0] - 2026-09-04 -**Does it hold in *your* setup?** Everything CTRLRun guarantees was proven, until now, by this +**Does it hold in *your* setup?** Everything ctrlrun guarantees was proven, until now, by this repository's tests against this repository's configurations. That is the right place to start and the wrong place to stop: what an operator deploys is *their* policy, *their* grants and *their* store, and a guarantee that has never been exercised against those is a guarantee @@ -2287,7 +2287,7 @@ than to storage: `ctrlrun.verify/v1`, `ctrlrun.guarantees/v1` and `ctrlrun.frame would need `contents: write` in every consumer's workflow, and asking for write access to a repository as the price of a verification badge is a bad trade for a tool whose subject is least privilege; `docs/docs/verify.md` shows the one-job publishing pattern once, with its cost - visible. Rendered, it reads exactly `CTRLRun verified N/M`, where `M` is **applicable** + visible. Rendered, it reads exactly `ctrlrun verified N/M`, where `M` is **applicable** guarantees and never the catalogue size. A partial run and a run that exited 2 or 3 write no badge at all. @@ -2305,13 +2305,13 @@ than to storage: `ctrlrun.verify/v1`, `ctrlrun.guarantees/v1` and `ctrlrun.frame - **`docs/docs/OWASP-AGENTIC-TOP10.md`** (SPEC-v0.4 §6) — a reading of the OWASP Top 10 for Agentic Applications (2026 edition, announced 2025-12-09) against the ten guarantees. Its first line, before any table, says what it is not: not a compliance claim, not a conformance claim, not a - certification, and not a statement that CTRLRun covers the Top 10. + certification, and not a statement that ctrlrun covers the Top 10. Two tables, and the second is what makes the first credible. `ASI04` supply chain, `ASI05` - code execution and `ASI06` memory and context poisoning are **not CTRLRun's subject** — + code execution and `ASI06` memory and context poisoning are **not ctrlrun's subject** — nothing here inspects a package, sandboxes an interpreter or reads a model's memory — and `ASI07` inter-agent communication waits on v0.7. `ASI01` agent goal hijack and `ASI09` - human-agent trust exploitation appear in **both** tables, because CTRLRun constrains what a + human-agent trust exploitation appear in **both** tables, because ctrlrun constrains what a hijacked agent can do without detecting the hijack, and binds an approval to one action without authenticating the approver or noticing that they were misled. @@ -2380,7 +2380,7 @@ than to storage: `ctrlrun.verify/v1`, `ctrlrun.guarantees/v1` and `ctrlrun.frame - **`docs/SPEC-v0.4.md`** — the v0.4 contract, a delta over v0.1, v0.2 and v0.3. v0.4 answers the question the first three releases could not: *does it hold in **my** setup?* Everything - CTRLRun guarantees is proven today by this repository's tests against this repository's + ctrlrun guarantees is proven today by this repository's tests against this repository's configurations, which is the right place to start and the wrong place to stop. `ctrlrun verify` runs those failure scenarios against the operator's own policy, grants and store type, and reports what passed, what failed, and what could not be tested at all. @@ -2417,7 +2417,7 @@ no half-way and no flag that makes a missing grant permissive. its parent, on every dimension, at creation *and* at every evaluation. Omission never means unlimited: a child that drops a dimension its parent constrains is rejected. -**Identity is consumed, not invented.** CTRLRun verifies tokens it is handed and maps verified +**Identity is consumed, not invented.** ctrlrun verifies tokens it is handed and maps verified claims onto a `Principal`. It issues nothing and defines no identity format. Claims are receipt data rather than action identity — they are not in the canonical form, so an approval survives a token rotation. @@ -2558,7 +2558,7 @@ a token rotation. - **Observe mode still refuses what it cannot describe.** A missing principal, a provider that raises, an unresolvable effect key, an argument an Action cannot represent, and a delegation that would escalate are refused in both modes: the first four are wiring bugs that would run - an action CTRLRun could not describe, and the fifth is an act of authority rather than a + an action ctrlrun could not describe, and the fifth is an act of authority rather than a decision about an action. It asks no human either — a policy reaching `approve` records `approval_required` and runs, creating no request and appending no `APPROVAL_REQUESTED` — and it never calls the `reconcile` hook, whose `"committed"` answer would move a record a @@ -2708,7 +2708,7 @@ a token rotation. authority that decision picks the principal. - **BREAKING for a reader: `ReceiptResult` gains `observed`.** `Receipt.from_dict` parses `result` into a closed `StrEnum` and `SQLiteStateStore` reads every stored receipt through - it, so a CTRLRun ≤ 0.2 process running `ctrlrun receipts` or `ctrlrun inspect` against a + it, so a ctrlrun ≤ 0.2 process running `ctrlrun receipts` or `ctrlrun inspect` against a store an 0.3 **observe-mode** process wrote raises on the unknown value. Two processes sharing one store is the intended deployment: **upgrade every reader before switching any writer to `mode: observe`.** An enforce-mode 0.3 writer emits no `observed` receipt and is @@ -2826,8 +2826,8 @@ reconstruct: parser: `parseInt("2_000")` is 2 in JavaScript and `strconv.Atoi` errors in Go. That is the exact hazard SPEC-v0.2 §6.4 exists to prevent. The gateway's job there is to - certify that a routing intermediary and CTRLRun are looking at the same value, and it was - certifying agreement that held only under Python's rules. CTRLRun's own decisions were never + certify that a routing intermediary and ctrlrun are looking at the same value, and it was + certifying agreement that held only under Python's rules. ctrlrun's own decisions were never affected — the action is built from the body, and the headers are only checked — so this costs an intermediary's correctness rather than an approval binding. @@ -2835,7 +2835,7 @@ reconstruct: parser in the comparison — and only a string, an integer or a boolean has one. The revision permits `x-mcp-header` on those three types alone and omits the header for a `null`, so a header naming an argument of any other type is refused rather than compared against a - rendering CTRLRun invented for it. This also declines the revision's SHOULD that servers compare + rendering ctrlrun invented for it. This also declines the revision's SHOULD that servers compare integers numerically (`42.0` equals `42`): v0.1 §2.3 refuses a float in the body outright, so the leniency has no legitimate case here. SPEC-v0.2 §6.4 states both rules and the reasoning. @@ -2847,7 +2847,7 @@ Everything below ships. `pip install ctrlrun` still installs nothing but `pyyaml ### Added - **MCP gateway** — `ctrlrun gateway --upstream --alias `, in `ctrlrun[gateway]`. - An existing MCP tool server gets CTRLRun semantics with no agent changes: `tools/call` + An existing MCP tool server gets ctrlrun semantics with no agent changes: `tools/call` becomes an Action, everything else is relayed unchanged. The request forwarded upstream is built from the action's *canonical* arguments, so what was hashed, reserved and recorded is byte-for-byte what the tool receives. Serves `2026-07-28` and `2025-03-26`–`2025-11-25` in @@ -2947,7 +2947,7 @@ Everything below ships. `pip install ctrlrun` still installs nothing but `pyyaml `schema: ctrlrun.policy/v2`. `ctrlrun.policy/v1` files keep loading unchanged; a `v2` file will not load on 0.1.0, which is the point — 0.1.0 would ignore the effect template and execute with no duplicate protection. -- MCP tool arguments that CTRLRun cannot canonicalize — any JSON number with a fraction — will +- MCP tool arguments that ctrlrun cannot canonicalize — any JSON number with a fraction — will be refused by the gateway, never rounded or coerced. Tools that move money through the gateway need integer minor units or decimal strings in their schema. diff --git a/CITATION.cff b/CITATION.cff index c10b8243..56b64245 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -1,9 +1,9 @@ cff-version: 1.2.0 -message: If you use CTRLRun in research, cite it as below. +message: If you use ctrlrun in research, cite it as below. type: software title: "The last check before an AI agent does something it can't undo." abstract: >- - CTRLRun is the execution safety layer for AI agents: a Python library that binds a human's + ctrlrun is the execution safety layer for AI agents: a Python library that binds a human's approval to the exact action they saw, executes one logical effect at most once across processes and hosts, treats an unknown outcome as unknown rather than failed, fails closed, and leaves a portable receipt for every action. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5f41181c..2e9ff59f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ # Contributing -CTRLRun sits in the execution path of actions that move money, delete infrastructure and +ctrlrun sits in the execution path of actions that move money, delete infrastructure and grant permissions. The rules below exist so that a change to it is evidence rather than intention. They are short to state and long to live with. diff --git a/GOVERNANCE.md b/GOVERNANCE.md index 8265314a..12145861 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -6,7 +6,7 @@ rules for a security report are in [SECURITY.md](SECURITY.md). ## Decisions -CTRLRun is maintainer-led. The maintainer has the final say on scope, on what a +ctrlrun is maintainer-led. The maintainer has the final say on scope, on what a specification says, and on whether a change merges or a release ships. A decision that changes a shipped guarantee is written into the specification and the changelog with its reason, never settled in a review comment. Disagreement is argued in public, in the issue or @@ -17,7 +17,7 @@ fork under Apache-2.0; that is the check on the maintainer. | Role | Who | Responsibilities | |---|---|---| -| Maintainer | Arpan Ghoshal ([@arpanghoshal](https://github.com/arpanghoshal)) | Owns the specifications and the roadmap. Final say on merges and releases. Answers security reports per SECURITY.md. Administers the `CTRLRun` GitHub organization, the `ctrlrun` project on PyPI, and ctrlrun.dev. | +| Maintainer | Arpan Ghoshal ([@arpanghoshal](https://github.com/arpanghoshal)) | Owns the specifications and the roadmap. Final say on merges and releases. Answers security reports per SECURITY.md. Administers the `ctrlrun` GitHub organization, the `ctrlrun` project on PyPI, and ctrlrun.dev. | | Committer | Rohan Kamath ([@rohanrkamath](https://github.com/rohanrkamath)) | Reviews and merges pull requests, the maintainer's included. Can cut a release by pushing a tag. Triages issues. Holds write access to every repository in the organization. | | Contributor | anyone | Opens issues and pull requests under the rules in CONTRIBUTING.md, with every commit signed off under the DCO. | @@ -34,7 +34,7 @@ makes that true: access. A release is a tag: `publish.yml` publishes to PyPI through trusted publishing and `release.yml` signs the provenance against the workflow's own identity, so no release depends on a key or a password that one person holds alone. -- **Two people own the organization.** Both are owners of the `CTRLRun` GitHub organization, +- **Two people own the organization.** Both are owners of the `ctrlrun` GitHub organization, so either can grant access, change a workflow, or answer a private vulnerability report. - **Two people own the package.** Both are owners of `ctrlrun` on PyPI, so the trusted publisher can be repaired by either. diff --git a/README.md b/README.md index d900e6c5..ddca0dff 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,18 @@

- CTRLRun + ctrlrun

- CTRLRun stops AI agents from taking wrong, restricted, or malicious actions in your workflows.
+ ctrlrun stops AI agents from taking wrong, restricted, or malicious actions in your workflows.
Every action is checked against your rules before it runs. Allowed actions go through.
Sensitive ones wait for a person. Forbidden ones are blocked.

Execution safety for AI agents. A Python library that sits between the decision to act and the call that acts.
A consequential action happens at most once, exactly as approved, and leaves a receipt.
- When the outcome is unknown, CTRLRun says so instead of guessing.
+ When the outcome is unknown, ctrlrun says so instead of guessing.

Runs in production on a single file, or on Postgres across hosts. Apache-2.0.

@@ -27,7 +27,7 @@ CodeQL Fuzz Tests - CTRLRun verified + ctrlrun verified OpenSSF Scorecard OpenSSF Best Practices License @@ -44,16 +44,16 @@ pip install ctrlrun && ctrlrun demo ## What it does -**The model guesses. CTRLRun does not.** The ticket says refund €500. The agent asks for +**The model guesses. ctrlrun does not.** The ticket says refund €500. The agent asks for €5,000, one extra zero. The tool is in its list, the arguments are well formed, and the model is completely confident. Nothing above the call disagrees, because nothing above the call is a check: a tool being callable is not permission to call it with those arguments. -| Without CTRLRun | With CTRLRun | +| Without ctrlrun | With ctrlrun | |---|---| | Nothing checks the amount. The call goes through. **€4,500 too much.** | Your rule checks the amount. The call never leaves. **€0 wrongly paid.** | -CTRLRun is that check. It reads the arguments about to leave your process and answers what may +ctrlrun is that check. It reads the arguments about to leave your process and answers what may happen to them: let it run, ask a human, or stop it cold. Four rules do the work, and each one is a test in this repository before it is a sentence here. @@ -66,7 +66,7 @@ is a test in this repository before it is a sentence here. The third one is the half people forget. A correct €500 refund commits at the provider and the reply is lost coming back, so the agent retries. Retry libraries, agent frameworks and tool -loops collapse *this failed* into *I do not know what happened*. CTRLRun keeps them apart: a +loops collapse *this failed* into *I do not know what happened*. ctrlrun keeps them apart: a lost reply is `AMBIGUOUS`, never `FAILED`, and a retry against an `AMBIGUOUS` effect is refused until a human, or a `reconcile` hook, says what happened. @@ -75,7 +75,7 @@ until a human, or a `reconcile` hook, says what happened. ```console $ ctrlrun demo -CTRLRun demo — five ways an agent action goes wrong, and what stops it. +ctrlrun demo — five ways an agent action goes wrong, and what stops it. Policy: refunds up to €1,000 are autonomous, up to €10,000 need a human, above that are denied. 1. Duplicate effect after a lost response @@ -139,7 +139,7 @@ compliant, certified or audited: · [`OWASP-AGENTIC-TOP10.md`](https://docs.ctrlrun.dev/OWASP-AGENTIC-TOP10) names the four entries this does not address. -If an agent only reads and answers, you do not need CTRLRun. The moment it can **send, pay, +If an agent only reads and answers, you do not need ctrlrun. The moment it can **send, pay, refund, delete, deploy, grant, revoke, approve, submit, purchase or cancel**, you do. ## Use it in three steps @@ -293,7 +293,7 @@ that reaches its tools over MCP, in any language. | A framework with its own approval interrupt | an adapter | the framework to have a human-in-the-loop primitive | **It works with agents you can and can't modify.** WhatsApp, Slack and Teams bots, ChatGPT, -Cursor, Codex, OpenAI Agents: any AI agent you have. CTRLRun checks the action, not the +Cursor, Codex, OpenAI Agents: any AI agent you have. ctrlrun checks the action, not the agent, so if the agent acts through a tool server or an API you run, the action is checked, and the agent is not rebuilt, redeployed or told. [Agents you can't modify](https://docs.ctrlrun.dev/agents-you-cant-modify) says where the @@ -376,7 +376,7 @@ grades the transport classifier. ## The same shape in nine domains -Nothing in CTRLRun knows what a refund is. An action is a **name**, **canonical arguments**, an +Nothing in ctrlrun knows what a refund is. An action is a **name**, **canonical arguments**, an **effect key** and a **resource**, and the three questions asked of it are the same whichever domain it came from: how much autonomy does *this action* get, did a human approve *this exact* action, and has this effect already happened. Two things carry your domain, and you write both. @@ -405,7 +405,7 @@ action, and has this effect already happened. Two things carry your domain, and Read any row left to right and it is one rule wearing different nouns. The security row is the one to read twice: adding a **deny** rule to a firewall is autonomous and adding an **allow** rule is not, which no amount threshold would have told you. The policy is where your judgement -about your domain gets written down; CTRLRun is what makes it hold. +about your domain gets written down; ctrlrun is what makes it hold. ## Documentation diff --git a/SECURITY.md b/SECURITY.md index a0980a60..33736af3 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -67,7 +67,7 @@ provenance and cannot be checked this way. ## Supported versions -CTRLRun is pre-1.0. Only the latest release receives fixes. +ctrlrun is pre-1.0. Only the latest release receives fixes. | Version | Supported | |---|---| @@ -81,7 +81,7 @@ migration that cannot be reversed says so in its entry. ## What counts as a vulnerability -CTRLRun sits in the execution path of consequential actions. Treat anything that breaks one of +ctrlrun sits in the execution path of consequential actions. Treat anything that breaks one of these as a security issue, not a bug: - An action executes that policy should have denied. diff --git a/VISION.md b/VISION.md index 62a52d2f..099335a3 100644 --- a/VISION.md +++ b/VISION.md @@ -12,15 +12,15 @@ That creates a new infrastructure question: **How much authority should a machine have over each consequential action — and how do we enforce it, prove it, and recover when execution goes wrong?** -CTRLRun is the enforcement infrastructure between **intention** and **consequence**. Not between prompt and model. +ctrlrun is the enforcement infrastructure between **intention** and **consequence**. Not between prompt and model. ## 2. Two concentric circles -**Circle 1 — the wedge.** Agent executes payment → response lost → agent retries → CTRLRun refuses the blind retry. Narrow. Instantly understood. This is v0.1. +**Circle 1 — the wedge.** Agent executes payment → response lost → agent retries → ctrlrun refuses the blind retry. Narrow. Instantly understood. This is v0.1. **Circle 2 — the product.** Action-level autonomy infrastructure: for each action, is it authorized, how much autonomy, is approval needed, was *this* action approved, is execution safe, did it already happen, what was the outcome. This is v0.2–v0.5. -CTRLRun is consequence-specific, not industry-specific. If an agent only reads, searches, summarizes, or answers, CTRLRun is low value. It earns its place where an agent has write access to the real world. +ctrlrun is consequence-specific, not industry-specific. If an agent only reads, searches, summarizes, or answers, ctrlrun is low value. It earns its place where an agent has write access to the real world. ## 3. End-state architecture @@ -100,7 +100,7 @@ Delegation attenuates, never amplifies: `child ⊆ parent`. Human €100k → fi *Status: v0.6, as the kernel-side object a sector pack configures.* -**Recovery** — declarative per-action `on_ambiguous: reconcile` / `on_failure: compensate`. CTRLRun coordinates safety semantics; it never becomes the workflow scheduler. Integrate with Temporal-class runtimes; don't recreate them. +**Recovery** — declarative per-action `on_ambiguous: reconcile` / `on_failure: compensate`. ctrlrun coordinates safety semantics; it never becomes the workflow scheduler. Integrate with Temporal-class runtimes; don't recreate them. *Status: reconciliation shipped in v0.2 as a hook that resolves an `AMBIGUOUS` effect, and `ctrlrun resolve` is the human path. Compensation and sagas are not built and are on the do-not-build list.* @@ -118,7 +118,7 @@ Align, don't invent: OWASP ACS, MCP, A2A, OAuth, OpenTelemetry, and NIST agent i **Full depth (a content track, after v0.6).** The same nine sectors, each with a control registry, approver roles, data scope, consequence defaults, and worked examples. It waits on v0.6 because that is where the control registry and data-scope primitives land, and a pack should be configuration rather than code; it waits on nothing else. Packs are released individually as `packs//` under their own version tags — `packs-payments-1.0` and so on — never sharing a version with the kernel, never gating a kernel release and never gated by one. Kernel versions ship correctness; content ships on its own cadence. -Each pack is authored in one AI session and reviewed in a separate AI session that did not author it, against cited public sources — PCI DSS, PSD2, the HIPAA Security Rule, SOX/COSO and maker-checker guidance, ABA Model Rules, NIST SP 800-53, CIS benchmarks, records-management and employment-law basics. The review ships with the pack as `REVIEW.md`, listing every control, the source clause it derives from, and every gap found; unresolved gaps stay listed rather than being quietly closed. A pack states that it was authored and reviewed by AI against those sources, and never describes itself as compliant with any regulation. That is a claim only an accountable human reviewer can make, and CTRLRun does not make it on anyone's behalf. +Each pack is authored in one AI session and reviewed in a separate AI session that did not author it, against cited public sources — PCI DSS, PSD2, the HIPAA Security Rule, SOX/COSO and maker-checker guidance, ABA Model Rules, NIST SP 800-53, CIS benchmarks, records-management and employment-law basics. The review ships with the pack as `REVIEW.md`, listing every control, the source clause it derives from, and every gap found; unresolved gaps stay listed rather than being quietly closed. A pack states that it was authored and reviewed by AI against those sources, and never describes itself as compliant with any regulation. That is a claim only an accountable human reviewer can make, and ctrlrun does not make it on anyone's behalf. Same kernel, different `ctrlrun.yaml` and control registries. diff --git a/action.yml b/action.yml index 6e1c173b..1da04dd0 100644 --- a/action.yml +++ b/action.yml @@ -1,7 +1,7 @@ -name: CTRLRun verify +name: ctrlrun verify description: >- - Run CTRLRun's declared guarantees against your policy and grants, and report what passed, failed, or was not applicable. -author: CTRLRun + Run ctrlrun's declared guarantees against your policy and grants, and report what passed, failed, or was not applicable. +author: ctrlrun branding: icon: shield @@ -88,7 +88,7 @@ runs: with: python-version: ${{ inputs.python-version }} - - name: Install CTRLRun + - name: Install ctrlrun shell: bash env: CTRLRUN_INSTALL: ${{ inputs.install }} @@ -127,7 +127,7 @@ runs: # Exit 2 and exit 3 print to stderr and write no document. There is nothing to # render, no badge to write, and the job fails below. { - echo "### CTRLRun verify" + echo "### ctrlrun verify" echo echo "The configuration was refused, or verify failed internally" echo "(exit \`$status\`). No report was produced and no badge was written." diff --git a/adapters/PUBLISHED.toml b/adapters/PUBLISHED.toml index 19c3167d..11624586 100644 --- a/adapters/PUBLISHED.toml +++ b/adapters/PUBLISHED.toml @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 # What is actually on PyPI, and the kernel range that distribution declares. diff --git a/adapters/langchain/DOCS-PAGE-DRAFT.mdx b/adapters/langchain/DOCS-PAGE-DRAFT.mdx index 41ce2e93..3de37a1b 100644 --- a/adapters/langchain/DOCS-PAGE-DRAFT.mdx +++ b/adapters/langchain/DOCS-PAGE-DRAFT.mdx @@ -1,6 +1,6 @@ --- -title: "CTRLRun integration" -description: "Integrate with the CTRLRun middleware using LangChain Python." +title: "ctrlrun integration" +description: "Integrate with the ctrlrun middleware using LangChain Python." --- -This guide provides a quick overview for getting started with the CTRLRun [middleware](/oss/langchain/middleware/overview/). CTRLRun checks every tool call your agent makes against a policy you write, before the call runs, and records what happened after. +This guide provides a quick overview for getting started with the ctrlrun [middleware](/oss/langchain/middleware/overview/). ctrlrun checks every tool call your agent makes against a policy you write, before the call runs, and records what happened after. ## Overview @@ -36,7 +36,7 @@ This guide provides a quick overview for getting started with the CTRLRun [middl ## Setup -No account and no API key. CTRLRun is a library, and the policy is a file in your repository. +No account and no API key. ctrlrun is a library, and the policy is a file in your repository. ### Installation @@ -95,10 +95,10 @@ Every protected call needs a principal: who is acting is an authorization input, The middleware uses [`wrap_tool_call`](/oss/langchain/middleware/custom), so a refused call is short-circuited — the tool is never invoked, and the model receives a `ToolMessage` explaining why: ```text -issue_refund amount=900000 CTRLRun refused this call: rule[2]. The tool did not run. -rm_rf CTRLRun refused this call: unknown_action. The tool did not run. +issue_refund amount=900000 ctrlrun refused this call: rule[2]. The tool did not run. +rm_rf ctrlrun refused this call: unknown_action. The tool did not run. issue_refund amount=1000 (the tool runs) -issue_refund amount=1000 CTRLRun refused this call: this effect is already committed +issue_refund amount=1000 ctrlrun refused this call: this effect is already committed ``` That last line is the property worth knowing about. Because `handler` is the executor, the effect is reserved before the tool runs and committed from what it returned. Two agents sharing a store cannot both execute the same effect key, and a tool that raises leaves the outcome `AMBIGUOUS` rather than `FAILED` — so the retry is refused until a person resolves it, instead of becoming a double charge. @@ -108,7 +108,7 @@ That last line is the property worth knowing about. Because `handler` is the exe Where the policy says `approve`, the call is held and the model is told how to release it: ```text -CTRLRun is holding this call for a human. Approve it with 'ctrlrun approve apr_...', +ctrlrun is holding this call for a human. Approve it with 'ctrlrun approve apr_...', then ask again. The tool did not run. ``` @@ -116,5 +116,5 @@ To have the human answered *inside* the run instead, use [`ctrlrun-langgraph`](h ## API reference -- [CTRLRun documentation](https://docs.ctrlrun.dev/) +- [ctrlrun documentation](https://docs.ctrlrun.dev/) - [`ctrlrun-langchain` source](https://github.com/CTRLRun/ctrlrun/tree/main/adapters/langchain) diff --git a/adapters/langchain/README.md b/adapters/langchain/README.md index 63073251..32cc32c3 100644 --- a/adapters/langchain/README.md +++ b/adapters/langchain/README.md @@ -1,6 +1,6 @@ # ctrlrun-langchain -Gate a LangChain agent's tool calls with a CTRLRun policy, through **LangChain's own +Gate a LangChain agent's tool calls with a ctrlrun policy, through **LangChain's own `wrap_tool_call`** middleware hook. - **Supported kernel range:** `ctrlrun>=0.12,<0.13` @@ -83,10 +83,10 @@ the agent's own tool calls are decided before they run: ```text lookup the tool runs -issue_refund amount=900000 CTRLRun refused this call: rule[1]. The tool did not run. -rm_rf CTRLRun refused this call: unknown_action. The tool did not run. +issue_refund amount=900000 ctrlrun refused this call: rule[1]. The tool did not run. +rm_rf ctrlrun refused this call: unknown_action. The tool did not run. issue_refund amount=1000 the tool runs -issue_refund amount=1000 (again) CTRLRun refused this call: this effect is already committed +issue_refund amount=1000 (again) ctrlrun refused this call: this effect is already committed ``` Nothing is default-allow: a tool the policy does not name is refused, which is why `rm_rf` above @@ -110,7 +110,7 @@ Where the policy says `approve`, this middleware refuses the call and tells the request id, rather than blocking the agent while a human deliberates: ```text -CTRLRun is holding this call for a human. Approve it with 'ctrlrun approve apr_...', +ctrlrun is holding this call for a human. Approve it with 'ctrlrun approve apr_...', then ask again. The tool did not run. ``` diff --git a/adapters/langchain/pyproject.toml b/adapters/langchain/pyproject.toml index 57daa576..9ea448b4 100644 --- a/adapters/langchain/pyproject.toml +++ b/adapters/langchain/pyproject.toml @@ -9,7 +9,7 @@ build-backend = "setuptools.build_meta" [project] name = "ctrlrun-langchain" version = "1.0.0" -description = "Gate a LangChain agent's tool calls with a CTRLRun policy, through wrap_tool_call." +description = "Gate a LangChain agent's tool calls with a ctrlrun policy, through wrap_tool_call." readme = "README.md" requires-python = ">=3.11" authors = [{name = "Arpan Ghoshal", email = "contact@arpanghoshal.com"}] diff --git a/adapters/langchain/src/ctrlrun_langchain/__init__.py b/adapters/langchain/src/ctrlrun_langchain/__init__.py index b30eb13e..d9dacdf2 100644 --- a/adapters/langchain/src/ctrlrun_langchain/__init__.py +++ b/adapters/langchain/src/ctrlrun_langchain/__init__.py @@ -1,6 +1,6 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 -"""Gate a LangChain agent's tool calls with a CTRLRun policy, through `wrap_tool_call`. +"""Gate a LangChain agent's tool calls with a ctrlrun policy, through `wrap_tool_call`. **This is not the LangGraph adapter, and it is not an adapter at all in SPEC-v0.5 §2's sense.** `ctrlrun-langgraph` exists to route an `APPROVE` through `interrupt()`; it reuses a framework's @@ -52,10 +52,10 @@ __all__ = ["CTRLRunMiddleware"] -#: What the model is told when CTRLRun refuses. A refusal is the statement that the tool did +#: What the model is told when ctrlrun refuses. A refusal is the statement that the tool did #: not run, which is not the same as the tool failing, so it names the rule rather than #: reporting an error the tool never produced. -_REFUSED: Final = "CTRLRun refused this call: {reason}. The tool did not run." +_REFUSED: Final = "ctrlrun refused this call: {reason}. The tool did not run." def _tool_message(request: Any, content: str) -> Any: @@ -129,19 +129,19 @@ def executor() -> Any: except ApprovalRequired as pending: return _tool_message( request, - f"CTRLRun is holding this call for a human. Approve it with " + f"ctrlrun is holding this call for a human. Approve it with " f"'ctrlrun approve {pending.request_id}', then ask again. The tool did not run.", ) except DuplicateEffect as duplicate: return _tool_message( request, - f"CTRLRun refused this call: this effect is already {duplicate.state} " + f"ctrlrun refused this call: this effect is already {duplicate.state} " f"({duplicate.effect_key}). The tool did not run.", ) except AmbiguousEffect as ambiguous: return _tool_message( request, - f"CTRLRun refused this call: the outcome of {ambiguous.effect_key} was never " + f"ctrlrun refused this call: the outcome of {ambiguous.effect_key} was never " f"established, so a retry is unsafe. Resolve it with " f"'ctrlrun resolve {ambiguous.effect_key}'. The tool did not run.", ) diff --git a/adapters/langgraph/README.md b/adapters/langgraph/README.md index 0201191b..22d242d0 100644 --- a/adapters/langgraph/README.md +++ b/adapters/langgraph/README.md @@ -1,6 +1,6 @@ # ctrlrun-langgraph -Route a CTRLRun `APPROVE` through **LangGraph's own `interrupt()`**, so the human answers where +Route a ctrlrun `APPROVE` through **LangGraph's own `interrupt()`**, so the human answers where your LangGraph users already answer. - **Supported kernel range:** `ctrlrun>=0.5,<0.13` @@ -110,15 +110,15 @@ a token: nothing is minted, nothing is stored, and there is no id here this adap `carries_approved_arguments` has **no default**, because the default somebody assumes is the one that does not check. -**`True` — prevention.** Your resume value must carry `arguments`, and CTRLRun rebuilds the +**`True` — prevention.** Your resume value must carry `arguments`, and ctrlrun rebuilds the proposal with them and compares the action hash. An answer given against €5 that arrives for a €5,000 action is refused with `ApprovalMismatch`, the approval is left grantable, and nothing runs. This is the setting the conformance results above were produced with, and it is right for almost every deployment: your console already knows what it showed the human. -**`False` — attribution.** You send back only a verdict. CTRLRun still binds the approval to the +**`False` — attribution.** You send back only a verdict. ctrlrun still binds the approval to the action that executes — that is `v0.1 §4.2 A1` and it holds unconditionally — but **the binding -across the interrupt is LangGraph's checkpoint, not CTRLRun's hash**. If the checkpoint replayed a +across the interrupt is LangGraph's checkpoint, not ctrlrun's hash**. If the checkpoint replayed a different call than the one a human read, evidence will show it afterwards; nothing refuses it beforehand. That is *attribution*, in that word, and the conformance kit reports `binding: not_applicable` with the reason rather than a pass. Choose it only if your console @@ -177,12 +177,12 @@ it returns is recorded by `InterruptApprovalProvider`, in core, through the same `ctrlrun approve` makes. It **constructs no `Control`** and **supplies no principal** — an adapter sees one and never supplies one. -And it is **not a compliance claim**. "Conformance" here names a suite of the CTRLRun +And it is **not a compliance claim**. "Conformance" here names a suite of the ctrlrun repository's own acceptance tests, run against this adapter. It certifies nothing. ## Versioning `adapters-langgraph-MAJOR.MINOR`, never a kernel version. This adapter answers to two upstreams -and neither is the CTRLRun roadmap: it breaks when LangGraph makes a breaking release, on that +and neither is the ctrlrun roadmap: it breaks when LangGraph makes a breaking release, on that project's schedule. Its major version tracks whichever of the two forced the break, and the two ranges at the top are what its CI actually ran against. diff --git a/adapters/langgraph/pyproject.toml b/adapters/langgraph/pyproject.toml index b3b6eb1e..a683b1d6 100644 --- a/adapters/langgraph/pyproject.toml +++ b/adapters/langgraph/pyproject.toml @@ -9,7 +9,7 @@ build-backend = "setuptools.build_meta" [project] name = "ctrlrun-langgraph" version = "1.3.0" -description = "Route a CTRLRun APPROVE through LangGraph's own interrupt()." +description = "Route a ctrlrun APPROVE through LangGraph's own interrupt()." readme = "README.md" requires-python = ">=3.11" authors = [{name = "Arpan Ghoshal", email = "contact@arpanghoshal.com"}] diff --git a/adapters/langgraph/src/ctrlrun_langgraph/__init__.py b/adapters/langgraph/src/ctrlrun_langgraph/__init__.py index 8c0f1528..78256747 100644 --- a/adapters/langgraph/src/ctrlrun_langgraph/__init__.py +++ b/adapters/langgraph/src/ctrlrun_langgraph/__init__.py @@ -1,6 +1,6 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 -"""Route a CTRLRun `APPROVE` through LangGraph's own `interrupt()`. SPEC-v0.5 §2, §3. +"""Route a ctrlrun `APPROVE` through LangGraph's own `interrupt()`. SPEC-v0.5 §2, §3. **An adapter exists for exactly one reason**, and this is the whole of it: when a policy says a refund needs a human, the human answers *where LangGraph users already answer* — through diff --git a/adapters/openai-agents/README.md b/adapters/openai-agents/README.md index e89f3236..93b54ecc 100644 --- a/adapters/openai-agents/README.md +++ b/adapters/openai-agents/README.md @@ -1,6 +1,6 @@ # ctrlrun-openai-agents -Route a CTRLRun `APPROVE` through the **OpenAI Agents SDK's own tool-approval interruption**, so +Route a ctrlrun `APPROVE` through the **OpenAI Agents SDK's own tool-approval interruption**, so the human answers where this SDK's users already answer. - **Supported kernel range:** `ctrlrun>=0.5,<0.13` @@ -74,11 +74,11 @@ line above, by the person deploying it. catches a tool's exception and returns *"An error occurred while running the tool. Please try again."* **to the model**. Under that default an `ActionDenied`, a `DuplicateEffect` or an `AmbiguousEffect` reaches your agent as a suggestion to retry — which is the exact failure -`SPEC-v0.2 §6.10` argues about in the gateway: a refusal by CTRLRun is not an outcome of the +`SPEC-v0.2 §6.10` argues about in the gateway: a refusal by ctrlrun is not an outcome of the tool, it is the statement that the tool did not run, and putting it in a channel whose contents reach the model as text invites the retry the refusal exists to prevent. -**`gate.run(...)` / `gate.run_sync(...)`** are `Runner.run` with CTRLRun's exceptions arriving as +**`gate.run(...)` / `gate.run_sync(...)`** are `Runner.run` with ctrlrun's exceptions arriving as themselves. The SDK wraps whatever a tool raises in `agents.exceptions.UserError` and chains the original as `__cause__`, so a plain `except DuplicateEffect` at your call site never fires. These walk the chain and give it back; they decide nothing and hold nothing. `unwrap(error)` is the @@ -95,24 +95,24 @@ that a call was approved, keyed by tool name and `call_id`, and not what its arg adapter that handed back the tool's own parameters would be handing back what it was just given, which SPEC-v0.5 §3.4 names as manufacturing the check. -So CTRLRun still binds the approval to the action that executes — that is `v0.1 §4.2 A1` and it -holds unconditionally — but **the binding across the interrupt is the SDK's, not CTRLRun's**. In +So ctrlrun still binds the approval to the action that executes — that is `v0.1 §4.2 A1` and it +holds unconditionally — but **the binding across the interrupt is the SDK's, not ctrlrun's**. In that word: *attribution*. The conformance kit reports `binding: not_applicable` with the reason, never a pass. **What closes the gap instead is real, and it is the SDK's.** The approval item and the invocation are the **same tool call**, bound by `call_id`, and the SDK invokes with exactly that call's arguments — it does not re-ask the model in between. That is a strong property. It is -simply not one CTRLRun can verify, which is the whole distinction §3.4 draws. +simply not one ctrlrun can verify, which is the whole distinction §3.4 draws. -## A rejection leaves no CTRLRun evidence +## A rejection leaves no ctrlrun evidence The one place this adapter's evidence differs from `@protect`'s, and worth knowing before you go looking for an empty log. -The SDK does **not invoke** a tool whose approval was refused. So no CTRLRun action is proposed: +The SDK does **not invoke** a tool whose approval was refused. So no ctrlrun action is proposed: there is no `APPROVAL_DENIED`, no `ACTION_DENIED` and **no receipt**. The refusal is real and it -is in the SDK's own run output; CTRLRun was never asked about it. The conformance kit reports +is in the SDK's own run output; ctrlrun was never asked about it. The conformance kit reports `denial: not_applicable` for the same reason. If you need refusals in the evidence log, record them where you call `state.reject(item)`. @@ -183,11 +183,11 @@ reimplements nothing — no prompt, no queue, no polling loop, no resume token o same two store calls `ctrlrun approve` makes. It **constructs no `Control`** and **supplies no principal**. -And it is **not a compliance claim**. "Conformance" names a suite of the CTRLRun repository's own +And it is **not a compliance claim**. "Conformance" names a suite of the ctrlrun repository's own acceptance tests, run against this adapter. It certifies nothing. ## Versioning `adapters-openai-agents-MAJOR.MINOR`, never a kernel version. This adapter answers to two -upstreams and neither is the CTRLRun roadmap. The two ranges at the top are what its CI actually +upstreams and neither is the ctrlrun roadmap. The two ranges at the top are what its CI actually ran against. diff --git a/adapters/openai-agents/pyproject.toml b/adapters/openai-agents/pyproject.toml index 49802d70..472bd222 100644 --- a/adapters/openai-agents/pyproject.toml +++ b/adapters/openai-agents/pyproject.toml @@ -9,7 +9,7 @@ build-backend = "setuptools.build_meta" [project] name = "ctrlrun-openai-agents" version = "1.3.0" -description = "Route a CTRLRun APPROVE through the OpenAI Agents SDK's tool-approval interruption." +description = "Route a ctrlrun APPROVE through the OpenAI Agents SDK's tool-approval interruption." readme = "README.md" requires-python = ">=3.11" authors = [{name = "Arpan Ghoshal", email = "contact@arpanghoshal.com"}] diff --git a/adapters/openai-agents/src/ctrlrun_openai_agents/__init__.py b/adapters/openai-agents/src/ctrlrun_openai_agents/__init__.py index 49b1e598..3631b2b3 100644 --- a/adapters/openai-agents/src/ctrlrun_openai_agents/__init__.py +++ b/adapters/openai-agents/src/ctrlrun_openai_agents/__init__.py @@ -1,6 +1,6 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 -"""Route a CTRLRun `APPROVE` through the OpenAI Agents SDK's own tool-approval interruption. +"""Route a ctrlrun `APPROVE` through the OpenAI Agents SDK's own tool-approval interruption. SPEC-v0.5 §2, §3.5. **An adapter exists for exactly one reason**, and this is the whole of it: when a policy says a @@ -146,7 +146,7 @@ def _answer_for_this_call(context: ToolContext) -> bool | None: exact-call lookup comes first, and `if approval_entry.approved is True: return True` is what runs when that misses. - A human who ticked *always approve refunds* has not approved this refund. CTRLRun's whole + A human who ticked *always approve refunds* has not approved this refund. ctrlrun's whole claim is that a grant binds to one action -- `v0.1 §4.2` consumes it against an `action_hash` -- and a blanket yes for a tool name is precisely what that exists to refuse. With `carries_approved_arguments = False` there is no binding check in core to catch it @@ -182,10 +182,10 @@ class AgentsInterrupt: back what it was given, which SPEC-v0.5 §3.4 names as manufacturing the check. So the binding across this interrupt is **attribution**, and `README.md` says so in that - word. What closes the gap in practice is the SDK's own binding rather than CTRLRun's: the + word. What closes the gap in practice is the SDK's own binding rather than ctrlrun's: the approval item and the invocation are the *same tool call*, bound by `call_id`, and the SDK invokes with exactly that call's arguments. That is a real property of the framework and it - is not one CTRLRun can verify, which is the whole distinction §3.4 draws. + is not one ctrlrun can verify, which is the whole distinction §3.4 draws. """ framework = "openai-agents" @@ -228,7 +228,7 @@ def interrupt(self, pending: PendingApproval) -> ApprovalAnswer: A **rejection** normally never reaches here: the SDK does not invoke a tool whose approval was refused, so the run ends with the rejection in its own output and no - CTRLRun action is proposed. §7 of `README.md` records that, because it is the one place + ctrlrun action is proposed. §7 of `README.md` records that, because it is the one place this adapter's evidence differs from `@protect`'s. `False` is still returned as a denial rather than assumed unreachable — a human's *no* is an answer, and §2.4 says it is recorded by the provider like any other. @@ -290,12 +290,12 @@ def protected_tool( **`needs_approval=approval_gate(...)`**, so the SDK asks before it invokes. - **`failure_error_function=None`**, so a CTRLRun refusal propagates out of `Runner.run` + **`failure_error_function=None`**, so a ctrlrun refusal propagates out of `Runner.run` instead of being turned into text. This SDK's default is `default_tool_error_function`, which catches a tool's exception and returns *"An error occurred while running the tool. Please try again."* to the **model**. Under that default an `ActionDenied`, a `DuplicateEffect` or an `AmbiguousEffect` reaches an agent as a suggestion to retry — which - is the exact failure `v0.2 §6.10` argues about in the gateway: a refusal by CTRLRun is not + is the exact failure `v0.2 §6.10` argues about in the gateway: a refusal by ctrlrun is not an outcome of the tool, it is the statement that the tool did not run, and putting it in a channel whose contents reach the model as text invites the retry the refusal exists to prevent. @@ -415,7 +415,7 @@ def unwrap(error: BaseException) -> BaseException: one interface this library has for saying *the tool did not run* is lost in transit. `v0.1 §8` prefers explicit exceptions over return codes for exactly this reason, and - `v0.2 §6.10` argues the same point about the gateway: a refusal by CTRLRun is not an outcome + `v0.2 §6.10` argues the same point about the gateway: a refusal by ctrlrun is not an outcome of the tool, it is the statement that the tool did not run, and it must be distinguishable. So this walks the chain and gives it back. @@ -494,7 +494,7 @@ def _reraise(recovered: BaseException, raised: BaseException) -> None: async def run(agent: Any, input: Any, **options: Any) -> Any: - """`Runner.run`, with CTRLRun's exceptions arriving as themselves (see `unwrap`). + """`Runner.run`, with ctrlrun's exceptions arriving as themselves (see `unwrap`). Use it wherever you would use `Runner.run` and want `except DuplicateEffect` to work. It is a thin pass-through: it decides nothing, holds nothing and adds no behaviour of its own. diff --git a/docs/SPEC-mcp-operator.md b/docs/SPEC-mcp-operator.md index 238426ef..c3b881e3 100644 --- a/docs/SPEC-mcp-operator.md +++ b/docs/SPEC-mcp-operator.md @@ -1,4 +1,4 @@ -# CTRLRun — the operator MCP server +# ctrlrun — the operator MCP server This is a **delta over [`SPEC-v0.1.md`](SPEC-v0.1.md), [`SPEC-v0.2.md`](SPEC-v0.2.md), [`SPEC-v0.3.md`](SPEC-v0.3.md), [`SPEC-v0.4.md`](SPEC-v0.4.md), [`SPEC-v0.5.md`](SPEC-v0.5.md) @@ -151,7 +151,7 @@ Reaching EOF on stdin is the client going away, and the process exits 0. expires; over stdio the client process holds `approve`, `deny` and `resolve` under the human's name for as long as it runs, `-41007`, `-41013` (bar root, §3.1) and `-41014` are unreachable, and the only human step left is the confirmation the client shows before a write — which -CTRLRun does not control and which a user can switch off. That is the auto-approve §1.1 +ctrlrun does not control and which a user can switch off. That is the auto-approve §1.1 refuses, reachable by client configuration, and this document does not pretend otherwise. It is said in three places so that each party sees it: here; in the `initialize` `instructions`, which the model reads; and in the §6 block, which the person reads. A lifetime after which writes @@ -349,7 +349,7 @@ the cited control names an `approver_role`, an answer from a credential that doe role is refused here, with the control named, and the roles the answer satisfied are recorded on the approval row. That is not an authority evaluation and it is not separation of duties: it is one string from the operator's own control registry compared against one claim on a verified -credential, and CTRLRun interprets neither. +credential, and ctrlrun interprets neither. **The unconfigured cases are two, they are not the same, and the earlier wording here stated one of them backwards.** `SPEC-v0.8.md` §3.5's rule is that omission is not entitlement and it is @@ -567,7 +567,7 @@ enforcing, and answering one changes nothing in the world. | *Over stdio:* `handle` raises outside `_call` | `-32603` with the message's id, logged | | *Over stdio:* a write under uid 0 | `-41013`; root is an account, not a person (§3.1) | -A CTRLRun refusal is a JSON-RPC **error**, never a `result` with `isError: true`, for `v0.2 +A ctrlrun refusal is a JSON-RPC **error**, never a `result` with `isError: true`, for `v0.2 §6.10`'s reason: `isError` reaches the model as text, and a refusal to let a human's assistant do something is not a tool result. diff --git a/docs/SPEC-scan.md b/docs/SPEC-scan.md index 593f196e..a23e093b 100644 --- a/docs/SPEC-scan.md +++ b/docs/SPEC-scan.md @@ -1,4 +1,4 @@ -# CTRLRun — `ctrlrun scan` +# ctrlrun — `ctrlrun scan` This is a **delta over [`SPEC-v0.1.md`](SPEC-v0.1.md), [`SPEC-v0.2.md`](SPEC-v0.2.md), [`SPEC-v0.3.md`](SPEC-v0.3.md), [`SPEC-v0.4.md`](SPEC-v0.4.md), [`SPEC-v0.5.md`](SPEC-v0.5.md) @@ -25,7 +25,7 @@ rather than worked around in the code. ## 1. Scope One sentence: **`ctrlrun scan` reports the consequential call sites and policy entries in a -project that CTRLRun is not covering, so that an operator can see the gap between *installed* +project that ctrlrun is not covering, so that an operator can see the gap between *installed* and *in the path*.** It answers one question — *what is not protected here?* — and it answers it by reading text. @@ -56,7 +56,7 @@ it. The gateway (`v0.2 §6`) is a choke point and covers what reaches it; the de what somebody decorated. The thing an operator cannot see today is **the call nobody decorated**, and there is no command that will tell them. -This is also the smallest possible first contact with the project. Adopting CTRLRun is a +This is also the smallest possible first contact with the project. Adopting ctrlrun is a decision; running a read-only command that lists what a stack already risks is not. ## 2. What it reads diff --git a/docs/SPEC-v0.1.md b/docs/SPEC-v0.1.md index d705cf1c..6c4ecfdc 100644 --- a/docs/SPEC-v0.1.md +++ b/docs/SPEC-v0.1.md @@ -1,4 +1,4 @@ -# CTRLRun v0.1 Specification +# ctrlrun v0.1 Specification This is the contract for v0.1. Tests are derived from §7. Public names are frozen in §8. Anything not in this document is out of scope for v0.1. @@ -403,7 +403,7 @@ An action awaiting approval has no receipt; `APPROVAL_REQUESTED` is its evidence **When only one of the two writes succeeds.** SQLite is authoritative and the JSONL file is a convenience export of what it already holds, so the store is written first and the file second. A failed file write MUST be logged on the `ctrlrun` logger and MUST NOT be raised. By the time it runs, the effect has committed at the remote and the record is durable; raising there would reach the caller as an exception on a successful action, and an agent that reads it as a failure retries — which is the one mistake this library exists to prevent. Nothing is hidden by the loss: `ctrlrun receipts` and `ctrlrun effects` read the database, not the files. The reverse order is not available: a store that refuses the write has not recorded the action, and there is nothing to export. -Enums MUST render by value everywhere evidence is produced — receipt JSON, event `data`, and CLI output: `"approve"`, never `"Decision.APPROVE"`. This is why `Decision` (§3.3) and `EffectState` (§5.2) are `StrEnum`; the guard is `test_decision_renders_by_value` in `tests/test_policy.py`, which pins `str()` and f-string interpolation. A receipt is read by tools that never imported CTRLRun. +Enums MUST render by value everywhere evidence is produced — receipt JSON, event `data`, and CLI output: `"approve"`, never `"Decision.APPROVE"`. This is why `Decision` (§3.3) and `EffectState` (§5.2) are `StrEnum`; the guard is `test_decision_renders_by_value` in `tests/test_policy.py`, which pins `str()` and f-string interpolation. A receipt is read by tools that never imported ctrlrun. ### 6.2 Events diff --git a/docs/SPEC-v0.10.md b/docs/SPEC-v0.10.md index 8028c2bf..8731e801 100644 --- a/docs/SPEC-v0.10.md +++ b/docs/SPEC-v0.10.md @@ -57,7 +57,7 @@ green test in this milestone. hands it with a correctly narrowed envelope, and the kernel records a correct hop. `v0.9 §1.1` said the same thing about task binding and it is no less true here: this is blast radius, not detection. -**CTRLRun does not become an A2A implementation.** It defines no wire format, no agent card, no task +**ctrlrun does not become an A2A implementation.** It defines no wire format, no agent card, no task lifecycle and no transport (§3.1). It consumes two strings from whatever envelope the deployment already carries, and it claims no conformance with anything. `ROADMAP.md`'s "A2A, as code. No conformance claim" is the whole of the claim. @@ -99,7 +99,7 @@ another would ship two models. | | Question | Answer | Section | |---|---|---|---| -| O1 | Does CTRLRun define a wire format or consume A2A's? | **Neither. What crosses is a reference, two strings, in whatever metadata the transport already has** | §3.1 | +| O1 | Does ctrlrun define a wire format or consume A2A's? | **Neither. What crosses is a reference, two strings, in whatever metadata the transport already has** | §3.1 | | O2 | Is a hop a record in the store or a claim in a token? | **A record. Rule 2 forces it, and the cost is that both sides decide against one store** | §3.2 | | O3 | What does depth mean across hops? | **`max_delegation_depth`, unchanged. A hop is a link in the same chain, not a second counter** | §2.5 | | O4 | Does a receipt record the whole chain or its predecessor? | **The hop it ran under, and nothing derivable from it** | §3.4 | @@ -309,7 +309,7 @@ it already names the grant, so an operator already has the id, and folding it in would lose which of the grant's own conditions failed. The line between the two reasons is whether the hop reached the action at all. -**What it does not promise, and this is the residual of the whole milestone.** CTRLRun cannot make a +**What it does not promise, and this is the residual of the whole milestone.** ctrlrun cannot make a receiving agent present the hop it was given. An agent that holds a grant of its own can simply not present the hop and act on its own authority. What the kernel guarantees is the disjunction, and the disjunction is worth having: @@ -430,7 +430,7 @@ patch would most plausibly soften. ### 3.1 The wire, which is O1 -**CTRLRun defines no wire format for authority, and consumes none.** +**ctrlrun defines no wire format for authority, and consumes none.** What crosses a hop is a **reference**: two strings, `hop` and `task`, carried in whatever field the transport already has for caller-supplied metadata. For A2A that is the message's `metadata` object. @@ -441,7 +441,7 @@ Three reasons, and the third is the load-bearing one. 1. **An envelope on the wire is an assertion; a reference is a lookup.** A receiver handed a serialized grant has to decide whether to believe it, which means signatures, which means keys, - which means issuing. `v0.3 §1.1` is unchanged: CTRLRun consumes identity and issues none. + which means issuing. `v0.3 §1.1` is unchanged: ctrlrun consumes identity and issues none. 2. **A format is a compatibility surface.** A2A is moving, and a kernel that defined `ctrlrun.hop/v1` as a wire object would own a translation layer for every transport a deployment uses. Two strings in a metadata bag need no translation and no version. @@ -872,7 +872,7 @@ the tool sitting under the approved action name so that `amount` now means somet grant still matches, every constraint still holds, the receipt still says `stripe.refund`, and the action is authorised against a server nobody reviewed. -**This is the honest slice of `ASI04` and it is smaller than the category.** CTRLRun still decides +**This is the honest slice of `ASI04` and it is smaller than the category.** ctrlrun still decides actions. It never inspects a package, a model, a registry, a build or a signature chain, and `OWASP-AGENTIC-TOP10.md`'s `ASI04` row keeps its "out of scope" verdict for supply chain at large, gaining one sentence for what this does cover. §8 refuses the wider claim by name, and the release @@ -1341,7 +1341,7 @@ command: `ctrlrun scan` already reads a document and reports what it found, so t holders go there, as a line saying which principals hold authority that no hop bounds. A deployment following §2.3.2 shows its worker agents absent from that line. One that does not shows -them present, which is the fact and not a verdict: `SPEC-v0.4.md` §3.9's rule that CTRLRun never +them present, which is the fact and not a verdict: `SPEC-v0.4.md` §3.9's rule that ctrlrun never grades an operator's document holds here, so `scan` reports and does not score. ### 6.5 Acceptance tests for item 5 diff --git a/docs/SPEC-v0.11.md b/docs/SPEC-v0.11.md index 641feea5..988cc5e2 100644 --- a/docs/SPEC-v0.11.md +++ b/docs/SPEC-v0.11.md @@ -41,7 +41,7 @@ split renumbers nothing. Every item is measured against these. 1. **The anchor consumes a timestamp and issues nothing.** No key generation, no rotation, no - revocation, no signing. `SPEC-v0.3.md` §1.1's rule, that CTRLRun consumes identity and issues + revocation, no signing. `SPEC-v0.3.md` §1.1's rule, that ctrlrun consumes identity and issues none, applied to time. It is the line between this milestone and the one `ROADMAP.md` keeps off the roadmap, and an anchor that minted anything would have crossed it. 2. **A prune introduces no break the chain did not already have, or it is refused.** A prune that @@ -208,7 +208,7 @@ matching. **It has three calls, not two, and the third is why.** An earlier draft had `make` and `check`, and a review broke it in one extra statement: with only those two, the record of *which* anchors exist -lives in CTRLRun's table, so deleting the newest row there leaves the older anchor reproducing and +lives in ctrlrun's table, so deleting the newest row there leaves the older anchor reproducing and the truncation invisible. Three SQL statements instead of two, which is the number §3.3 claimed the design avoided. @@ -233,11 +233,11 @@ its checkpoint anchor far *below* its newest interval anchor, so the rule refuse was refused, **forever**. §4.6 was written because an anchoring deployment should not have to choose between pruning and a permanent tamper signal, and as drafted it landed on the first horn. -`latest()` is the one that closes the gap, because it is answered **outside**. CTRLRun's table is +`latest()` is the one that closes the gap, because it is answered **outside**. ctrlrun's table is then a cache and not a record: if it names fewer anchors than the provider holds, the provider wins and the missing one is checked anyway. -**What CTRLRun refuses to accept as one**, and this is the fail-closed half: +**What ctrlrun refuses to accept as one**, and this is the fail-closed half: - **A provider that raises is `anchor_unavailable` and never a pass.** `SPEC-v0.9.md` §5.6's rule for a scope provider, unchanged: a provider that cannot answer has not answered yes. @@ -264,7 +264,7 @@ An anchor inside the store is an anchor the writer under suspicion can rewrite, exactly one level up: the head was in the database, and that is why two statements were enough. An anchor in the same database would make it three. -So the anchor **record** is the operator's, held wherever their provider holds it, and CTRLRun keeps +So the anchor **record** is the operator's, held wherever their provider holds it, and ctrlrun keeps a local copy of what it needs to ask the question: the pair, the token, and the time. Those live in a table §9 names. @@ -1126,7 +1126,7 @@ names one is not done when its tests pass. implementable, and §4.5 requires the lock without naming a surface for it. - **`AnchorProvider.make` returns `(token, time)` and §3.2's table says "an opaque token."** §3.3 caches the time and §10 refuses an anchor whose time runs backwards; a time the provider does - not supply is one CTRLRun would read from its own clock, which rule 1 forbids. The three cannot + not supply is one ctrlrun would read from its own clock, which rule 1 forbids. The three cannot all hold with a bare token. - **`StateStore.checkpoint`'s read shipped in item 2, and §9 assigns it to item 3.** §4.6's supersession rule is part of what `anchor_broken` means, so an anchor without it would report diff --git a/docs/SPEC-v0.2.md b/docs/SPEC-v0.2.md index 41589b88..07505e76 100644 --- a/docs/SPEC-v0.2.md +++ b/docs/SPEC-v0.2.md @@ -1,4 +1,4 @@ -# CTRLRun v0.2 Specification +# ctrlrun v0.2 Specification This is a **delta over [`SPEC-v0.1.md`](SPEC-v0.1.md)**. Everything in v0.1 still holds; this document states only what v0.2 adds or changes. A reference to the kernel contract is written @@ -42,7 +42,7 @@ Item 2 sits early because it needs nothing this release adds. `ROADMAP.md` speci - `examples/` — standalone scripts, one per failure scenario: `double-refund/`, `approval-mutation/`, `agent-race/`, `approval-replay/`. In v0.1 `ctrlrun demo` was the - example; separate scripts earn their keep now there is more than one way to wire CTRLRun in. + example; separate scripts earn their keep now there is more than one way to wire ctrlrun in. - `examples/policies/.yaml` for devops, payments, e-commerce, insurance, healthcare, legal, security, government and hr. Each carries the header comment *"Starting point on the v0.1 kernel. Adapt before use."* @@ -170,7 +170,7 @@ A `BaseException` that is not an `Exception` raised by the hook propagates, for §2.3. `"committed"` and `"not_executed"` are assertions about the remote, exactly as `NotExecuted` -is (v0.1 §5.5). CTRLRun cannot check them. The hook's author takes that responsibility +is (v0.1 §5.5). ctrlrun cannot check them. The hook's author takes that responsibility knowingly, which is why the hook is an explicit argument and not a default behaviour. ### 2.5 Events @@ -186,7 +186,7 @@ RECONCILIATION_RESOLVED `RECONCILIATION_RESOLVED` carries `data.outcome ∈ {"committed", "not_executed", "unknown"}` and, when the outcome was forced to `"unknown"`, `data.reason ∈ {"raised", "invalid_return"}` with `data.error` naming it. `RECONCILIATION_RESOLVED` MUST be appended for every -`RECONCILIATION_STARTED`, `"unknown"` included: evidence has to record that CTRLRun asked and +`RECONCILIATION_STARTED`, `"unknown"` included: evidence has to record that ctrlrun asked and learned nothing, or a silent hook is indistinguishable from no hook. Where the outcome moved the record, `EFFECT_RESOLVED` with `data.resolved_by = "reconcile"` @@ -194,7 +194,7 @@ follows `RECONCILIATION_RESOLVED`. ### 2.6 What is not bounded -CTRLRun gives the hook no timeout, exactly as it gives the executor none (v0.1 §5.5). A hook that +ctrlrun gives the hook no timeout, exactly as it gives the executor none (v0.1 §5.5). A hook that hangs hangs the attempt that called it, and in the blocking case that is a *retry* hanging on a call the caller did not write. This is a stated limit, not an oversight: a timeout here would need a thread or a signal, and killing a half-finished reconciliation query is how you @@ -378,7 +378,7 @@ ctrlrun gateway --upstream --alias [options] ``` An HTTP process that speaks MCP on both sides. An MCP client points at it instead of at the -tool server; it applies CTRLRun's decision, effect and evidence semantics to `tools/call` and +tool server; it applies ctrlrun's decision, effect and evidence semantics to `tools/call` and relays everything else. No agent changes. Ships in `ctrlrun[gateway]`, whose only dependency is an HTTP client (§6.11). @@ -430,7 +430,7 @@ revision's note that *"Intermediaries that enforce policy based on mirrored head reject the request rather than trusting unvalidated header values."* That note does not bind this gateway, and reading it as though it did was a mistake worth naming. It governs intermediaries whose enforcement *is* the header — a load balancer routing by tenant, a rate -limiter counting by method. CTRLRun decides from the parsed body (§6.4) and never from a +limiter counting by method. ctrlrun decides from the parsed body (§6.4) and never from a header, so an unvalidated header value cannot influence a decision here. **Header trust is never the guarantee**, and a rule that refuses every client in existence today would contradict the whole claim of this release: an existing MCP server plus one gateway. @@ -490,7 +490,7 @@ including a `subscriptions/listen` stream that stays open. "Unchanged" is about the JSON-RPC payload, not about scrutiny: §6.4 applies to every request the gateway forwards, intercepted or not. -A relayed method has **no CTRLRun outcome**. No Action is built, no policy is evaluated, no +A relayed method has **no ctrlrun outcome**. No Action is built, no policy is evaluated, no effect is reserved, no receipt is written, and an unreachable upstream is an HTTP error and nothing more. `tools/list` is not an action; only calling a tool is. @@ -509,7 +509,7 @@ bodies, and requires servers that process a body to reject any disagreement with sources of truth (e.g., a load balancer routing on the header value while the MCP server executes based on the body value)"*. -That is precisely the hazard CTRLRun would create if it took the shortcut the headers exist +That is precisely the hazard ctrlrun would create if it took the shortcut the headers exist for. So: - The gateway MUST parse the body of every request it forwards and MUST validate @@ -523,7 +523,7 @@ for. So: only be applied to parameters with primitive types (integer, string, boolean)"*, with a `null` parameter omitting its header entirely. A `Mcp-Param-{Name}` naming an argument of any other type — a list, a mapping, a `null` — therefore cannot agree with it, and is refused - rather than compared against a rendering CTRLRun invented for it. + rather than compared against a rendering ctrlrun invented for it. This is stricter than the revision's *"servers SHOULD compare the header value and the body value numerically rather than as strings (e.g., `42.0` and `42` are considered equal)"*, and @@ -704,7 +704,7 @@ The pre-dispatch set is closed and small: does not recognize. This is the one row-group in the table resting on a peer obeying a contract rather than on -something CTRLRun observed. An upstream that validates lazily — doing work and *then* returning +something ctrlrun observed. An upstream that validates lazily — doing work and *then* returning `-32602` — will get a retry it should not have. That residual is recorded in `THREAT_MODEL.md`, alongside the gateway's other unverifiable assertions: `not_executed_on_error` is an operator's claim about their upstream (v0.1 §5.5's asymmetry, @@ -738,7 +738,7 @@ assertion, made by the person who knows, with the same consequences if they are **The upstream's own answer is returned unchanged.** Where the gateway received a well-formed JSON-RPC response, it relays it verbatim; rewriting a tool's result or error would corrupt the contract between the agent and the tool. The gateway synthesizes a response only when it never -got one. So that a client is not left guessing what CTRLRun recorded, the gateway MUST add to +got one. So that a client is not left guessing what ctrlrun recorded, the gateway MUST add to every response it returns for an intercepted call: ```json @@ -949,7 +949,7 @@ question, alongside the authority model. ### 6.10 Decision, approval, and what the client sees -| CTRLRun outcome | JSON-RPC code | HTTP | `data` | +| ctrlrun outcome | JSON-RPC code | HTTP | `data` | |---|---|---|---| | `ALLOW` | — | as upstream | — | | `DENY` | `-41001` `ctrlrun.denied` | 403 | `reason`, `action_id` | @@ -972,7 +972,7 @@ specification **SHOULD** be allocated outside the JSON-RPC reserved range (`-327 **Why a JSON-RPC error and not `isError: true`.** A tool result with `isError: true` is fed to the model to let it self-correct, and it is indistinguishable from the tool's own failure. A -refusal by CTRLRun is not an outcome of the tool; it is the statement that the tool did not +refusal by ctrlrun is not an outcome of the tool; it is the statement that the tool did not run. A JSON-RPC error says that unambiguously, and keeps a policy denial out of a channel whose contents reach the model as text. @@ -1031,7 +1031,7 @@ It is one process with a thread per connection, fronting one upstream. It is not balancer, not a reverse proxy for a fleet, and not an authorization server. Reservation is still single-host (v0.1 §5.3 E1), so two gateways in front of one upstream do **not** share reservations unless they share a state file on one machine. Put it behind a real proxy for -TLS termination, rate limiting and authentication; CTRLRun decides, and does not aspire to +TLS termination, rate limiting and authentication; ctrlrun decides, and does not aspire to terminate. The extra's only dependency is an HTTP client with a connect/write/read exception taxonomy @@ -1179,11 +1179,11 @@ conventions while mapping security events to OCSF. `docs/ACS.md` states: -- where CTRLRun would sit in that model — the tool-call checkpoint, and only that one; +- where ctrlrun would sit in that model — the tool-call checkpoint, and only that one; - what an adapter would carry across, and what has no counterpart on the ACS side: effect - identity, atomic reservation, and `AMBIGUOUS` as a terminal state are CTRLRun's, not the + identity, atomic reservation, and `AMBIGUOUS` as a terminal state are ctrlrun's, not the standard's, and an adapter that flattened them would export the wrong thing; -- what CTRLRun's OTel attributes (§8) would have to be renamed to, to align with ACS's +- what ctrlrun's OTel attributes (§8) would have to be renamed to, to align with ACS's conventions, and the cost of doing that to receipts already written. `ROADMAP.md`'s standards rule governs: integrate first, map second, never claim compliance. diff --git a/docs/SPEC-v0.3.md b/docs/SPEC-v0.3.md index 7f8dc1c7..137e2e77 100644 --- a/docs/SPEC-v0.3.md +++ b/docs/SPEC-v0.3.md @@ -1,4 +1,4 @@ -# CTRLRun v0.3 Specification +# ctrlrun v0.3 Specification This is a **delta over [`SPEC-v0.1.md`](SPEC-v0.1.md) and [`SPEC-v0.2.md`](SPEC-v0.2.md)**. Everything in both still holds; this document states only what v0.3 adds or changes. A @@ -28,7 +28,7 @@ a grant and every action must pass *both* authority and policy. There is no half its parent — at the moment it is created *and* again every time it is evaluated. Omitting a dimension the parent constrains is not "unconstrained"; it is a rejected delegation (§5.4). -**Identity is consumed, never invented.** Principals come from an `IdentityProvider`. CTRLRun +**Identity is consumed, never invented.** Principals come from an `IdentityProvider`. ctrlrun verifies tokens it is handed and maps verified claims onto a `Principal`. It defines no identity format, issues no credential, and runs no authorization server. @@ -62,7 +62,7 @@ the install command when absent. `import ctrlrun` MUST NOT import `jwt`, `httpx` v0.3 consumes identities that other systems issue. It is worth being exact about which, because "aligns with" is the kind of sentence that ages into a lie. -**What CTRLRun consumes.** +**What ctrlrun consumes.** - A **JSON Web Token** (RFC 7519) presented in an HTTP header, verified against a **JWKS** (RFC 7517) or a static public key, with `exp`, `nbf`, `aud` and `iss` checked, and with @@ -71,12 +71,12 @@ v0.3 consumes identities that other systems issue. It is worth being exact about - Anything that has already been verified by something else and handed over in a trusted header, via `HeaderIdentityProvider` — with the threat that implies stated in full (§3.3). -**What CTRLRun does not do.** It issues no token, mints no identity, runs no authorization +**What ctrlrun does not do.** It issues no token, mints no identity, runs no authorization server, performs no OAuth flow, and defines no new identity format or claim name. There is no -`ctrlrun` claim, no CTRLRun identity document, and no registry of agent identifiers. A +`ctrlrun` claim, no ctrlrun identity document, and no registry of agent identifiers. A `Principal` is a *reading* of somebody else's credential. -**What CTRLRun does not claim.** No conformance, compliance, certification or alignment with +**What ctrlrun does not claim.** No conformance, compliance, certification or alignment with any standard, in this document, in the README, in a docstring, or in CLI output. `ROADMAP.md`'s standards rule holds: *integrate first, map second, never claim compliance* — a standard appears in a mapping doc only after code touches it and a test proves the guarantee. §1.3 @@ -126,7 +126,7 @@ delegation. **NIST IR 8587**, *Protecting Tokens and Assertions from Forgery, Th is an Initial Public Draft (published 2025-12-22, [https://csrc.nist.gov/pubs/ir/8587/ipd](https://csrc.nist.gov/pubs/ir/8587/ipd)), and its verifier obligations informed §3.4 without being citable as a requirement. Its §4.2.1.2 is the one NIST sentence that addresses a -component in CTRLRun's position — *"Policy enforcement points (e.g., at the application level) +component in ctrlrun's position — *"Policy enforcement points (e.g., at the application level) that rely on access tokens and identity assertions MUST confirm the validity, scope, source, and integrity of access tokens before granting access to resources"* — and it is draft guidance, aimed at agencies and cloud service providers, that mentions neither AI agents nor @@ -170,7 +170,7 @@ challenged scopes and `scopes_supported`, and the step-up flow computes the **un and challenged scopes — the opposite of attenuation. **What v0.3 takes from this:** nothing it can map an authority model onto. MCP supplies -audience-bound bearer tokens and a transport. Per-action authority is CTRLRun's own ground, which +audience-bound bearer tokens and a transport. Per-action authority is ctrlrun's own ground, which is exactly why the gateway carries a policy and an `authority:` file rather than deriving entitlements from the protocol. @@ -189,8 +189,8 @@ February 2020) — all three are things an issuer or a client does, and v0.3 is RFC 8693 is worth one more sentence, because it is the RFC someone will expect §5 to be built on. Its §4.1 requires a consumer to *"only consider the token's top-level claims and the party identified as the current actor by the `act` claim"*, with nested prior actors informational. -CTRLRun's delegation chain is therefore **not** carried in a token and not read from one: it -lives in CTRLRun's own store (§5.2), and every link is re-checked there (§5.6). A library that +ctrlrun's delegation chain is therefore **not** carried in a token and not read from one: it +lives in ctrlrun's own store (§5.2), and every link is re-checked there (§5.6). A library that claimed to verify a delegation chain out of an `act` claim would be non-conformant to the one Final RFC it would be citing. @@ -215,7 +215,7 @@ a version. **What v0.3 takes from this:** JWT verification, and nothing else. A library that put a six-month-old individual draft in its wire format would be shipping somebody's unreviewed idea to production. `JWTIdentityProvider` verifies RFC 7519 tokens against RFC 7517 keys; the `authority:` -section is CTRLRun's own YAML, deliberately, and does not pretend to be a token profile. +section is ctrlrun's own YAML, deliberately, and does not pretend to be a token profile. #### SPIFFE — identity, and explicitly not authorization @@ -250,7 +250,7 @@ creation review 2025-02-05); UCAN 1.0.0 ([https://github.com/ucan-wg/spec](https no dated release); ZCAP-LD (W3C CCG **Community Group draft** v0.4.0-draft, not Recommendation track); SPKI/SDSI, **RFC 2693**, September 1999, **Experimental**; and `draft-niyikiza-oauth-attenuating-agent-tokens-01`, an individual submission with no IETF -standing. None is a standard CTRLRun could implement; every one is a source of design pressure. +standing. None is a standard ctrlrun could implement; every one is a source of design pressure. They split on one question — *does anything ever compare a child grant to its parent?* @@ -266,7 +266,7 @@ They split on one question — *does anything ever compare a child grant to its §5's rule — provably contained at creation **and** re-checked at evaluation — sits with ZCAP-LD, SPKI and that draft. It is a **different** guarantee from the one macaroons and UCAN give, not a strictly stronger one: they make widening unrepresentable, which is stronger, by -requiring a token that can only ever have restrictions appended; CTRLRun compares two grants +requiring a token that can only ever have restrictions appended; ctrlrun compares two grants instead, which is weaker against a forged record and stronger against a *changing parent*. That is the trade this codebase is forced into. A grant here is a YAML document an operator edits and a delegation is a row in a store, so there is no signature chain to make widening @@ -278,7 +278,7 @@ inheriting them comes from. Two of §5's dimensions have essentially no prior art: **environment containment** — macaroons defer to application-defined caveats, UCAN policy sees only invocation arguments, ZCAP-LD has no environment field — and **numeric containment**, whose nearest standards-adjacent expression is -RFC 2693's `(* range …)`, Experimental and from 1999. Those two are stated here as CTRLRun's own +RFC 2693's `(* range …)`, Experimental and from 1999. Those two are stated here as ctrlrun's own invention, not as an implementation of anything. @@ -431,7 +431,7 @@ its own. the *names*, sorted, not the values. Values reach `--json`. - **The OTel sink.** Claim values are withheld exactly as argument values are (`v0.2 §8`): span attributes carry `ctrlrun.principal.issuer` and the sorted claim names, and nothing - else, unless the operator asked for arguments. They stay in CTRLRun's own `ctrlrun.*` + else, unless the operator asked for arguments. They stay in ctrlrun's own `ctrlrun.*` namespace rather than adopting OpenTelemetry's `user.*` registry, and the reason is better than preference: every attribute in that registry (`user.id`, `user.name`, `user.email`, `user.roles`, …) is marked **Development** stability, so adopting it would tie a receipt- @@ -556,7 +556,7 @@ the provider never runs. That is a real limit and it is stated rather than papered over: it is the same limit as calling the wrapped function directly, which `docs/THREAT_MODEL.md` already records as out of scope -("Bypassing the decorator entirely"). Code inside the CTRLRun process is inside the trust +("Bypassing the decorator entirely"). Code inside the ctrlrun process is inside the trust boundary. Code *outside* it — every gateway client, which is where an untrusted caller actually is — has no such path, because the gateway builds the Action and the client never touches `Control`. v0.3 does **not** add a re-resolution or a principal-disagreement check to @@ -1290,7 +1290,7 @@ in favour of some other grant that happens to match — it is `authority_unreada precedence order of §4.3. The permissive reading is the one an implementer reaches for naturally: iterate the grants, collect the matches, ignore the row that raised. That is how a principal holding both a broad root grant and a narrow delegation ends up authorized by the broad one -because the narrow one was corrupted. CTRLRun cannot tell a corrupted record from a tampered one, +because the narrow one was corrupted. ctrlrun cannot tell a corrupted record from a tampered one, and the safe reading of "I cannot read this" is not "then it does not apply". **Where several grants match** and all are readable, the action passes; a grant is a permission, @@ -1969,7 +1969,7 @@ recorded, which is `v0.2 §6.9.3`'s no-effect-key path reached for a different r that *did* reserve suspends exactly as in enforce mode. **What observe mode does not suspend.** It suspends *decisions about an action* and *effect-state -refusals*. It does not suspend the refusals that mean CTRLRun cannot construct or identify the +refusals*. It does not suspend the refusals that mean ctrlrun cannot construct or identify the action at all, and it does not suspend the creation of durable authority: | Still refuses in observe mode | Why | @@ -1983,7 +1983,7 @@ action at all, and it does not suspend the creation of durable authority: The line is drawn where it is because everything above is either a wiring bug in the deployment or an act of authority, not a decision about an action. Observing a wiring bug means running an -action CTRLRun could not describe, and a receipt that cannot say what ran is not evidence. +action ctrlrun could not describe, and a receipt that cannot say what ran is not evidence. T82b asserts the first four rows and the delegation row by name. The last row is **unfalsifiable and carries no test**: an implementation cannot know the mode without loading the configuration, @@ -2041,7 +2041,7 @@ This is a command that reads receipts out of the SQLite file the process it is d writing. ``` -CTRLRun — 2026-09-01T00:00:00Z .. 2026-09-04T09:11:00Z (observe mode) +ctrlrun — 2026-09-01T00:00:00Z .. 2026-09-04T09:11:00Z (observe mode) actions 1284 would have been denied 37 (2.9%) @@ -2156,7 +2156,7 @@ DELEGATION_REJECTED | `DELEGATION_REJECTED` | `reason` and `parent_id`; `dimension` **only** for a §5.3 rule-6 containment refusal | `AUTHORITY_RESOLVED` is appended for **every** action that passes authority, not only for -delegated ones. Evidence has to record that CTRLRun checked and found a grant, or a deployment +delegated ones. Evidence has to record that ctrlrun checked and found a grant, or a deployment with a permissive grant is indistinguishable from one with no authority section at all — the same argument `v0.2 §2.5` makes for appending `RECONCILIATION_RESOLVED` on `"unknown"`. @@ -3098,7 +3098,7 @@ unknown `result` values needs no change" is true and nearly useless, because the reader does not tolerate them: `Receipt.from_dict` parses `result` into a closed `StrEnum`, and `SQLiteStateStore` reads every stored receipt through it. So: -- A CTRLRun **≤ 0.2** process running `ctrlrun receipts` or `ctrlrun inspect` against a store or +- A ctrlrun **≤ 0.2** process running `ctrlrun receipts` or `ctrlrun inspect` against a store or JSONL file that a 0.3 **observe-mode** process wrote raises on the unknown `result` value. Two processes sharing one store is the intended deployment (`v0.2 §6.1`), so this is not a corner case: **upgrade every reader before switching any writer to `mode: observe`.** An enforce-mode diff --git a/docs/SPEC-v0.4.md b/docs/SPEC-v0.4.md index c4cf821a..f7ec60ac 100644 --- a/docs/SPEC-v0.4.md +++ b/docs/SPEC-v0.4.md @@ -1,4 +1,4 @@ -# CTRLRun v0.4 Specification +# ctrlrun v0.4 Specification This is a **delta over [`SPEC-v0.1.md`](SPEC-v0.1.md), [`SPEC-v0.2.md`](SPEC-v0.2.md) and [`SPEC-v0.3.md`](SPEC-v0.3.md)**. Everything in all three still holds; this document states @@ -13,7 +13,7 @@ document or in v0.1/v0.2/v0.3 is out of scope for v0.4. Words: MUST / MUST NOT / SHOULD are used in the RFC 2119 sense. v0.4 answers a question the first three releases could not: **does it hold in *my* setup?** -Everything CTRLRun guarantees is proven today by this repository's own tests against this +Everything ctrlrun guarantees is proven today by this repository's own tests against this repository's own configurations. That is the right place to start and the wrong place to stop, because the thing an operator deploys is *their* policy, *their* grants and *their* store — and a guarantee that has never been exercised against those is a guarantee nobody @@ -89,7 +89,7 @@ The list matters more than the feature does. Verify sees **the configuration, no was written before this document and is **wrong**; it is amended in item 7 (§9.4). - **Not the operator's `reconcile` hooks**, for the same reason: a hook is a Python callable passed to `@protect`, and it does not appear in any file verify reads. -- **Not where the decorator was placed.** Code that calls the raw function bypasses CTRLRun +- **Not where the decorator was placed.** Code that calls the raw function bypasses ctrlrun entirely (`THREAT_MODEL.md`, "Out of scope"), and no amount of configuration-reading finds that. - **Not the deployment.** Whether the proxy in front of `HeaderIdentityProvider` overwrites @@ -299,7 +299,7 @@ raises `AmbiguousEffect`; the fake remote's call count is 1; the retry's receipt **Control.** The same scenario with a fake that raises `NotExecuted` instead: the record reaches `FAILED`, the retry **is** admitted, and it executes — call count 2. This control carries more weight than any other in the catalogue, because it is the only thing separating -"CTRLRun blocks blind retries" from "CTRLRun blocks retries", and the second sentence +"ctrlrun blocks blind retries" from "ctrlrun blocks retries", and the second sentence describes a library nobody can deploy. --- @@ -441,7 +441,7 @@ directions are asserted or neither is. every report (§4.2) and on nothing else. - **Ids are permanent.** A guarantee that is removed leaves its number retired; a guarantee - that is added takes the next one. `G4` means the same sentence in every version of CTRLRun + that is added takes the next one. `G4` means the same sentence in every version of ctrlrun that ever emitted it. - **Adding a guarantee is a specification amendment**, and it moves the denominator. A badge reading `8/8` from one release and `8/10` from the next is not a regression, and the report @@ -696,7 +696,7 @@ Written to stdout. One line per guarantee, in catalogue order, and the summary i line so that a `tail -1` is meaningful. ``` -CTRLRun verify — ctrlrun 0.4.0, catalogue ctrlrun.guarantees/v1 +ctrlrun verify — ctrlrun 0.4.0, catalogue ctrlrun.guarantees/v1 policy examples/authority/payments.yaml (ctrlrun.policy/v3, mode: enforce) authority same document, 3 grants store sqlite, scratch (created and destroyed for this run) @@ -938,10 +938,10 @@ where the reader can see the cost. The document is Shields' endpoint schema: ```json -{"schemaVersion": 1, "label": "CTRLRun", "message": "verified 10/10", "color": "brightgreen"} +{"schemaVersion": 1, "label": "ctrlrun", "message": "verified 10/10", "color": "brightgreen"} ``` -- **Rendered text is exactly `CTRLRun verified N/M`** — label, a space, message. T119 asserts +- **Rendered text is exactly `ctrlrun verified N/M`** — label, a space, message. T119 asserts the concatenation and asserts `message` against `^verified \d+/\d+$`, a regex rather than a word list, so no adjective can be appended to it later. - `N` is passes, `M` is **applicable** guarantees. Never the catalogue size. @@ -962,7 +962,7 @@ for the run. T119 asserts the exact phrase `declared guarantees pass` is present target. The words **secure**, **safe**, **compliant**, **certified** and **audited** do not appear as -claims about CTRLRun or about the operator's system anywhere in the badge, its JSON, the job +claims about ctrlrun or about the operator's system anywhere in the badge, its JSON, the job summary or `docs/verify.md`. ### 5.4 The job summary @@ -979,10 +979,10 @@ full. A summary that listed only failures would make an all-N/A run look like a Its first line, before any table: -> This is a **reading** of somebody else's taxonomy against the guarantees CTRLRun tests. It -> is not a compliance claim, a conformance claim, a certification, or a statement that CTRLRun +> This is a **reading** of somebody else's taxonomy against the guarantees ctrlrun tests. It +> is not a compliance claim, a conformance claim, a certification, or a statement that ctrlrun > covers the OWASP Top 10 for Agentic Applications. Three of the ten entries are not -> addressed by CTRLRun at all, and they are listed by name below. +> addressed by ctrlrun at all, and they are listed by name below. `ROADMAP.md`'s standards rule is the reason this document can exist at all: *integrate first, map second, never claim compliance.* Every row maps a `G` to an entry, and every `G` is @@ -998,7 +998,7 @@ a row whose test disappears is a row that comes out. the codes and titles below come from secondary summaries and are **provisional**. 3. **Table: guarantee → entries mitigated.** Columns: `G`, invariant, ASI entries, one sentence on *how* — the mechanism, not a restatement of the entry. -4. **Table: entries with no guarantee**, under the heading `Not covered by CTRLRun`, each with +4. **Table: entries with no guarantee**, under the heading `Not covered by ctrlrun`, each with one honest sentence: out of scope, or a milestone it waits on. An entry that is partly addressed goes in **both** tables, with the partial half stated in the second — the honest place for a hedge is next to the thing it qualifies. @@ -1012,7 +1012,7 @@ Poisoning · `ASI07` Insecure Inter-Agent Communication · `ASI08` Cascading Age `ASI09` Human-Agent Trust Exploitation · `ASI10` Rogue Agents. The expected shape of the second table, which is the half that makes the first one credible: -supply chain, code execution and memory/context poisoning are **not** CTRLRun's subject — +supply chain, code execution and memory/context poisoning are **not** ctrlrun's subject — nothing in this library reads a model's memory, inspects a package, or sandboxes an interpreter — and inter-agent communication waits on v0.7. Item 5 states each in one sentence and adds nothing aspirational. @@ -1283,10 +1283,10 @@ first is the N/A dogfood — five applicable, five N/A — and the assertion is change that made verify silently count N/As is caught in CI rather than in a badge. #### T119 — The badge says what it is allowed to say -`label + " " + message == "CTRLRun verified N/M"` exactly; `message` matches +`label + " " + message == "ctrlrun verified N/M"` exactly; `message` matches `^verified \d+/\d+$`; `M` equals the report's `applicable` and never the catalogue size; the link target contains the exact phrase `declared guarantees pass`; and the badge JSON, the job -summary and `docs/verify.md` make no claim about CTRLRun using the words `secure`, `safe`, +summary and `docs/verify.md` make no claim about ctrlrun using the words `secure`, `safe`, `compliant`, `certified` or `audited`. #### T120 — The job fails on FAIL and succeeds on N/A @@ -1300,7 +1300,7 @@ configuration fails the job and writes no badge. #### T121 — The mapping is complete in both directions Every `G` in the registry appears in `docs/OWASP-AGENTIC-TOP10.md`; every ASI code in the document is one of the ten in the cited edition and matches `^ASI\d{2}:\d{4}$`; every ASI code -in the cited edition appears either in the mapping table or in `Not covered by CTRLRun`, and +in the cited edition appears either in the mapping table or in `Not covered by ctrlrun`, and none appears only in neither. The document contains no compliance claim, asserted against the word list of `v0.2 §10` T31. diff --git a/docs/SPEC-v0.5.md b/docs/SPEC-v0.5.md index 473c95fe..75029acf 100644 --- a/docs/SPEC-v0.5.md +++ b/docs/SPEC-v0.5.md @@ -1,4 +1,4 @@ -# CTRLRun v0.5 Specification +# ctrlrun v0.5 Specification This is a **delta over [`SPEC-v0.1.md`](SPEC-v0.1.md), [`SPEC-v0.2.md`](SPEC-v0.2.md), [`SPEC-v0.3.md`](SPEC-v0.3.md) and [`SPEC-v0.4.md`](SPEC-v0.4.md)**. Everything in all four @@ -563,7 +563,7 @@ adapter would like to check: is `InvalidArgument` at the gate rather than a silent inequality (`v0.1 §2.3`). A second equality implementation here would be a second place for the binding to drift. - **`False`.** The framework's resumption carries nothing the adapter can inspect. The binding - across the interrupt is then **the framework's checkpoint, not CTRLRun's**: that is + across the interrupt is then **the framework's checkpoint, not ctrlrun's**: that is *attribution*, evidence will show what was approved and what ran, and a divergence is findable afterwards rather than refused beforehand. The adapter's README MUST say so in those words (§7 item 4) and MUST NOT describe it as prevention, and the kit reports @@ -571,7 +571,7 @@ adapter would like to check: **Why arguments and not the hash.** The webhook's answerer echoes the `action_hash` it was shown, because it was shown one. A framework's approval item was not: it records the arguments the model -proposed and knows nothing of CTRLRun's canonical form, and an adapter that computed a hash to +proposed and knows nothing of ctrlrun's canonical form, and an adapter that computed a hash to echo would have to build an `Action`, which needs a `Principal`, which §4.2 forbids — the same hole `needs_approval` closed in §2.2. So the carried value is the one a framework actually holds. It is not a weaker check, and the rebuild above is why: what is compared is the full @@ -742,7 +742,7 @@ this. It is the one place an adapter is required to branch on the mode. The consequence of getting it wrong is worse than the rule it breaks, which is why it is stated rather than left to follow: a framework of that shape does not invoke a tool whose approval was declined, so a human's *no* under `mode: observe` **stops the action** — and observe mode's whole -promise is that every decision is recorded and none is enforced. A deployment evaluating CTRLRun +promise is that every decision is recorded and none is enforced. A deployment evaluating ctrlrun in the mode built for evaluating it would have its agent halted. §12.9 has the finding; the kit cannot catch it, because §3.6 makes `run()` refuse an observing `Control`, so an adapter that interrupts in observe mode scores full marks. @@ -799,7 +799,7 @@ being verified is not the thing that ships. The one thing an adapter may configure is **how it reaches its framework's primitive** — which node, which context key, which callback — because that is the framework's shape and not a -CTRLRun check. +ctrlrun check. --- @@ -941,7 +941,7 @@ class ConformanceAdapter(Protocol): #: constructs a `Control` and a kit that let it would be testing a shape that does not ship. interrupt: FrameworkInterrupt #: `True` where the framework does not invoke a tool whose approval was declined, so a - #: human's `no` proposes no CTRLRun action and there is no `APPROVAL_DENIED` to record + #: human's `no` proposes no ctrlrun action and there is no `APPROVAL_DENIED` to record #: (§12.6). Defaults to `False`, which is the shape most frameworks have -- a declaration #: nobody needs to make is one nobody gets wrong. #: @@ -955,10 +955,10 @@ class ConformanceAdapter(Protocol): def invoke(self, request: CallRequest) -> Any: ... # Returns whatever the executor returned. Where the framework **refused before it # invoked** — a declined pre-invocation approval, so the executor never ran and no - # CTRLRun action was proposed — let the framework's own refusal **propagate**. Do not + # ctrlrun action was proposed — let the framework's own refusal **propagate**. Do not # return a value (that is `never-executes`) and do not return `None` (undefined). The # kit catches it and decides the case from the evidence: `denial` asserts that the - # executor was not reached and that CTRLRun was not asked (§5.4). Item 6 could not + # executor was not reached and that ctrlrun was not asked (§5.4). Item 6 could not # determine this from the contract and had to guess. @@ -1038,7 +1038,7 @@ suite passed. A suite asserting a refusal cannot tell *the human said no through from *the framework was never asked*, and only the count can. `invoke` runs **one** protected call end to end through the framework and returns the executor's -value. Every CTRLRun exception propagates: the kit asserts on `ApprovalMismatch`, +value. Every ctrlrun exception propagates: the kit asserts on `ApprovalMismatch`, `DuplicateEffect`, `AmbiguousEffect`, `ActionDenied`, `AuthorityDenied` and `NotExecuted` by type and by `reason`, so an adapter that swallowed one fails rather than passing quietly. @@ -1070,7 +1070,7 @@ folded into the count, one level down. **`denial` is B3 alone for the same reason**, and it is a suite because of the second reference adapter rather than by foresight — §12.6 has the finding. A framework that refuses *before* it -invokes never proposes a CTRLRun action, so there is nothing to deny and nothing to log; B3 left +invokes never proposes a ctrlrun action, so there is nothing to deny and nothing to log; B3 left in `kernel` beside six cases that always run would have reported that adapter `pass` on the one case it cannot exercise. The declaration is `refuses_before_invoking`, defaulting to `False`. @@ -1173,9 +1173,9 @@ then establishes the declaration from the evidence. **The first attempt at this checked the wrong thing**, and a second independent review broke it with one class attribute: `refuses_before_invoking = True` on `denial-as-error` — a fixture this table requires to *fail* — reported `not_applicable` with every other suite green. It had asked -whether the executor ran, which a framework that refuses *inside* CTRLRun satisfies just as well. +whether the executor ran, which a framework that refuses *inside* ctrlrun satisfies just as well. -The discriminator is **whether CTRLRun was asked at all**. A framework that genuinely refuses +The discriminator is **whether ctrlrun was asked at all**. A framework that genuinely refuses before invoking proposes no action: no `ACTION_PROPOSED`, no `APPROVAL_REQUESTED`, and the framework's own primitive is never reached, because `@protect` never gets far enough to raise `ApprovalRequired`. Two checks, and the pair is not redundant — one fixture reaches each and @@ -1183,7 +1183,7 @@ neither reaches the other: - **The executor ran.** Catches an adapter that bypasses `Control` altogether, which leaves no events to be caught by. -- **CTRLRun was asked** — any of `ACTION_PROPOSED`, `APPROVAL_REQUESTED`, or a non-zero interrupt +- **ctrlrun was asked** — any of `ACTION_PROPOSED`, `APPROVAL_REQUESTED`, or a non-zero interrupt count. Catches an adapter that went through the whole flow and then mislabelled the result. Only `APPROVAL_DENIED` is excused, because that is the one such a framework genuinely cannot @@ -1214,7 +1214,7 @@ reason. response is doing what its documentation says. That is item 1's measurement, not a kit failure. - **Not the operator's executor.** The kit supplies its own, always. - **Not where the adapter was wired.** An agent that calls the unprotected function bypasses - CTRLRun entirely, and no amount of driving the adapter finds that. + ctrlrun entirely, and no amount of driving the adapter finds that. - **Not whether the adapter logged the observe banner.** §3.6 requires it, and the kit **refuses** an observing `Control` outright — so no case ever runs under one, and an adapter that never calls `ctrlrun.adapter.banner` passes every suite. The requirement is real and the @@ -1679,7 +1679,7 @@ specifically: already provide.** §3.4 says which half is prevention and which is attribution, and inventing a token to close the gap is the second approval path under another name. - **An expiry on the human's deliberation in a resumed-in-place shape.** §3.2.1 states the cost - and does not fix it: the framework owns the checkpoint's lifetime, and a CTRLRun timer that + and does not fix it: the framework owns the checkpoint's lifetime, and a ctrlrun timer that refused a resumption would be refusing an action the kernel re-decides in full at step 13 anyway. - **Appending an event from an approval provider.** §2.4 states the limitation; giving @@ -1799,9 +1799,9 @@ for is telling "the framework was asked" from "it was not", and `>= 1` does that which is its right home: only they know the framework can carry it. **A framework that refuses *before* invoking produces no denial to observe.** The Agents SDK -does not call a tool whose approval was declined, so no CTRLRun action is proposed: no +does not call a tool whose approval was declined, so no ctrlrun action is proposed: no `APPROVAL_DENIED`, no `ACTION_DENIED`, no receipt. The refusal is real and it is in the -framework's own output; CTRLRun was simply never asked. `B3` is therefore its own suite, +framework's own output; ctrlrun was simply never asked. `B3` is therefore its own suite, `denial`, reported `not_applicable` for such a framework with the reason on the report and in the adapter's README — never a pass, and never folded into the count. `ConformanceAdapter` gains `refuses_before_invoking`, which defaults to `False` because that is the shape most frameworks @@ -1816,7 +1816,7 @@ either alone cannot pass the `kernel` suite — nor should it. exception and returns *"An error occurred while running the tool. Please try again."* **to the model**. Under that default an `ActionDenied`, a `DuplicateEffect` or an `AmbiguousEffect` reaches an agent as a suggestion to retry — which is exactly the failure `v0.2 §6.10` argues -about in the gateway, one layer up: *a refusal by CTRLRun is not an outcome of the tool, it is +about in the gateway, one layer up: *a refusal by ctrlrun is not an outcome of the tool, it is the statement that the tool did not run*, and a channel whose contents reach the model as text invites the retry the refusal exists to prevent. @@ -1883,7 +1883,7 @@ an adapter that interrupts in observe mode scores full marks. The consequence is worse than the rule. A framework of that shape does not invoke a tool whose approval was declined — so a human's *no* under `mode: observe` **stops the action**, and observe mode's entire promise is that every decision is recorded and none is enforced. The deployment -most likely to be running it is the one evaluating CTRLRun before trusting it, and what it would +most likely to be running it is the one evaluating ctrlrun before trusting it, and what it would have seen is its agent halted by the tool that promised to change nothing. §3.6 now **requires** the predicate to return "no approval needed" under `mode: observe`, rather diff --git a/docs/SPEC-v0.6.md b/docs/SPEC-v0.6.md index 2a23a7a7..717a61ea 100644 --- a/docs/SPEC-v0.6.md +++ b/docs/SPEC-v0.6.md @@ -1,4 +1,4 @@ -# CTRLRun v0.6 Specification — Durable runtime +# ctrlrun v0.6 Specification — Durable runtime A **delta** over `SPEC-v0.1.md`, `SPEC-v0.2.md`, `SPEC-v0.3.md`, `SPEC-v0.4.md` and `SPEC-v0.5.md`. All five remain binding in full; nothing here relaxes one. Tests are derived @@ -588,7 +588,7 @@ against *the previous release's own code*, and the singular is what hid the defe its fixtures from v0.1, v0.2, v0.3 and v0.5**, and the plural is the point. **A database with neither `schema_version` nor `effects` but with other tables present is -refused**, naming what it found. It is somebody else's database, and creating CTRLRun's tables in +refused**, naming what it found. It is somebody else's database, and creating ctrlrun's tables in it is not a recovery. ### 3.3 Refusal, in both directions @@ -681,7 +681,7 @@ configuration nobody tested. Two consequences the operator must be told about, and `docs/postgres.md` (item 9) says both: -- **The database user needs DDL rights** on the CTRLRun schema, at least on the first start after +- **The database user needs DDL rights** on the ctrlrun schema, at least on the first start after an upgrade. Where it does not have them, the migration fails and the store refuses to open, naming the missing privilege rather than the SQL that failed. - **Concurrent starts are safe and are not clever.** The migration transaction takes the @@ -753,15 +753,15 @@ which hashes `.ctrlrun/state.db` before and after and has no Postgres analogue. So the scratch store is defined rather than left to be discovered: - **Verify creates its own schema** — `ctrlrun_verify_<16 hex>` — for each guarantee, creates the - CTRLRun tables inside it at `head`, and **drops it** when the run ends, including when the run + ctrlrun tables inside it at `head`, and **drops it** when the run ends, including when the run ends by exception. That is what `PostgresStateStore(url, schema=…)` (§9.1) is for. - **It never touches any other schema.** It does not migrate `public`, does not read it, and does not create anything in it. A migration inside a schema verify created a moment ago is verify's own and is not the operator's database changing. - **It refuses a URL it cannot do that with**: no privilege to `CREATE SCHEMA`, or a - `search_path` that already names a CTRLRun schema. Exit **2**, naming the reason — `v0.4 §3.8`'s + `search_path` that already names a ctrlrun schema. Exit **2**, naming the reason — `v0.4 §3.8`'s treatment for a configuration verify will not run against, never a silent fallback to `public`. -- **T154e asserts all three**, including that a CTRLRun database in `public` is byte-identical +- **T154e asserts all three**, including that a ctrlrun database in `public` is byte-identical before and after a verify run — T103's guarantee, carried across to the backend that made it hard. @@ -1160,7 +1160,7 @@ two authorities, both of which already exist: | A reconcile hook | `v0.2 §2`, and **only where its answer points** — `"unknown"` changes nothing | `resolved_by = "reconcile:"` | **`cli:local`, not `cli:`, and the difference is a promise this milestone cannot keep.** -An earlier draft of this table wrote `"cli:"`. CTRLRun does not authenticate the person at +An earlier draft of this table wrote `"cli:"`. ctrlrun does not authenticate the person at the terminal — §11 puts *authenticating the approver* out of scope by name, and it is the same sentence for the same reason here — so a `` in that column would be whatever the shell says `$USER` is, which is a claim about a person made from a value that person controls. `cli:local` @@ -1400,7 +1400,7 @@ Stated before what it does, and repeated in the README, the changelog and `THREA attacker and no other. An anchor outside this database is what would close it, and §11 keeps signing and anchoring out of v0.6. -- **Not evidence that a receipt was written by CTRLRun.** A well-formed row appended at the end, +- **Not evidence that a receipt was written by ctrlrun.** A well-formed row appended at the end, with a correct `prev_hash` and a head updated to match, is indistinguishable from a real one. §6.1's *"altering, deleting or reordering"* did not list insertion because insertion is not in the set it closes. @@ -1665,7 +1665,7 @@ is an expression, and there is no expression parser. The distinction matters mor Three rules, and they are what keeps this from becoming a compliance feature: -- **CTRLRun does not interpret a control.** `source:` is a string the operator wrote. The kernel +- **ctrlrun does not interpret a control.** `source:` is a string the operator wrote. The kernel does not know what PCI DSS is, does not check the clause exists, and makes **no compliance, conformance or alignment claim** on the strength of one. A control is an identifier and a citation. @@ -1939,10 +1939,10 @@ A database created with `ENCODING SQL_ASCII` is refused at open, naming the enco #### T154e — Verify's Postgres scratch store touches nothing it did not create `ctrlrun verify --store-url postgresql://…` creates a `ctrlrun_verify_` schema per guarantee -and drops it, including when the run ends by exception. A CTRLRun database in `public` is +and drops it, including when the run ends by exception. A ctrlrun database in `public` is **byte-identical before and after** — `v0.4`'s T103 carried across to the backend that made it hard — and it is not migrated. A URL the store cannot `CREATE SCHEMA` on, or one whose -`search_path` already names a CTRLRun schema, exits **2** naming the reason and never falls back +`search_path` already names a ctrlrun schema, exits **2** naming the reason and never falls back to `public` (§4.1). #### T154f — DDL rights, collation and connection discipline @@ -2173,11 +2173,11 @@ fails, and whoever adds it comes to the test and says which kind it is. Two furt the list itself load-bearing rather than decorative: every entry must still resolve to a line in its file and must still contain a forbidden word (an entry that stopped matching is an entry doing nothing), and a positive control asserts the pattern would fire on *"receipts are signed, which -proves authorship"*, *"the chain is tamper-proof"* and *"CTRLRun gives you non-repudiation"* while +proves authorship"*, *"the chain is tamper-proof"* and *"ctrlrun gives you non-repudiation"* while not firing on `design`, `assign` or `designated`. **What the check does not cover**, stated rather than assumed: a claim made in words the pattern -does not contain. *"CTRLRun proves who wrote each receipt"* passes it. The scan is a guard against +does not contain. *"ctrlrun proves who wrote each receipt"* passes it. The scan is a guard against the vocabulary drifting back in, not a reader of prose, and §6.4 remains the thing that has to be true. @@ -2458,7 +2458,7 @@ and opens only a database already at HEAD. Three refusals, each naming its own r | What it finds | What it says | |---|---| | the schema does not exist | a read command does not create one | -| no `schema_version` in it | that schema holds no CTRLRun database | +| no `schema_version` in it | that schema holds no ctrlrun database | | anything but `UP_TO_DATE` | which migration is missing, and that a **writer** applies it at open | **Still no new command.** `--verify-chain`, `--control` and `--store-url` are flags on commands that already open the @@ -2639,7 +2639,7 @@ deliver, and specifically: §4.4 and §5.2 each say why. - **A process-identity field on any record**, and any reclaimer built on one (§5.1). - **Data scope beyond §7.4's labels and redaction.** No row-level filtering, no purpose limitation, - no field-level authorization, no query rewriting. CTRLRun is not a database proxy and it is not + no field-level authorization, no query rewriting. ctrlrun is not a database proxy and it is not DLP. - **Matching a grant on a data label.** Authority addresses `agent` and `user` (`v0.3 §4.2`) and that is unchanged; `data_scope` is a policy condition and nothing more. diff --git a/docs/SPEC-v0.7.md b/docs/SPEC-v0.7.md index fb16f0ca..5d243780 100644 --- a/docs/SPEC-v0.7.md +++ b/docs/SPEC-v0.7.md @@ -1,4 +1,4 @@ -# CTRLRun v0.7 Specification: Execution boundary +# ctrlrun v0.7 Specification: Execution boundary A **delta** over `SPEC-v0.1.md`, `SPEC-v0.2.md`, `SPEC-v0.3.md`, `SPEC-v0.4.md`, `SPEC-v0.5.md` and `SPEC-v0.6.md`. All six remain binding in full; nothing here relaxes one. Tests are derived from @@ -12,7 +12,7 @@ Words: MUST / MUST NOT / SHOULD are used in the RFC 2119 sense. v0.6 asked *does it still hold when the process dies, the host goes away, and the database is somewhere else?* v0.7 asks: **does it hold at the edges the kernel does not control?** -Every guarantee shipped so far is a guarantee about what happens inside CTRLRun. There are three +Every guarantee shipped so far is a guarantee about what happens inside ctrlrun. There are three places where that stops being enough: - **The kernel does not decide whether the remote acted.** An executor does, by raising @@ -77,7 +77,7 @@ matters more than the features do. `v0.5 §3.8` and `v0.6 §1.1` forbid a flag that makes the thing being checked differ from the thing that ships, and every item here is under the same rule. - **Not a fence.** A fencing token works only where the resource validates it, and Stripe, an - SMTP server and the Kubernetes API validate no CTRLRun token. §11 has the whole argument. + SMTP server and the Kubernetes API validate no ctrlrun token. §11 has the whole argument. - **Not a budget.** A ceiling counts attempts on one effect key; it does not meter authority across keys. Budgets are v0.9, and v0.9 uses this milestone's attempt number rather than building one. - **Not a scope provider.** §6's hook is general precisely so that v0.9 can configure one through @@ -1295,7 +1295,7 @@ and if that holds here §12 says so and T246 and T246b are the only tests of the `max_elicitation_rounds` on the gateway (`v0.2 §6.9.2`); **a direct `Control.resume` caller has no such bound, and v0.7 does not add one.** - **Observe mode records and runs.** The fast path and the check record `attempt_ceiling` in - `would_have.blocked_reason` and the action executes, because observe mode suppresses CTRLRun's + `would_have.blocked_reason` and the action executes, because observe mode suppresses ctrlrun's decisions and not the record of an effect that happened (`v0.3 §6.2`, `v0.6 §7.2.3`). ### 5.8 The amendment, as it lands in `SPEC-v0.1.md` @@ -1519,7 +1519,7 @@ world that moved, which a human looks at; the other is a provider that is down o operator fixes. The provider's exception is recorded in the event's `error` **by its type name only**: a provider that put the balance it read into its exception message would otherwise carry raw state into the evidence through the one field nobody thought to check. What the provider logs for -itself is the operator's; nothing CTRLRun writes carries the message or the return value. +itself is the operator's; nothing ctrlrun writes carries the message or the return value. **A provider that hangs** holds the call and nothing else. There is no timeout parameter: a timeout that fired would have to decide something, the only decision available is refusal, and a provider can @@ -1581,7 +1581,7 @@ kernel's to honour. **What lies beyond the kernel's reach.** A resource that accepts a conditional write, an `If-Match` on a version or a compare-and-swap on a balance, can refuse a stale request itself, at the one point where the state and the write meet. Whether an executor sends one is the executor's choice and the provider's -feature. CTRLRun does not do it and does not claim it, and nothing in this section's recheck substitutes +feature. ctrlrun does not do it and does not claim it, and nothing in this section's recheck substitutes for it. ### 6.8 Where this binds, and where it does not @@ -1815,7 +1815,7 @@ deliberately as the "yes" ones, and two of them are where this milestone's revie |---|---|---| | `@protect` → `Control.execute` | **yes**, both defences: the fast path before the approval gate, and the check on the assigned attempt number before the executor | the only path that reserves and then dispatches (§5.5) | | `Control.execute` called directly | **yes**, the same two | the same method | -| `Control.execute` in observe mode | **records, does not enforce**: both defences write `would_have.blocked_reason = "attempt_ceiling"` and the action runs | `v0.3 §6.2`: observe mode suppresses CTRLRun's decisions, not the record of an effect that happened | +| `Control.execute` in observe mode | **records, does not enforce**: both defences write `would_have.blocked_reason = "attempt_ceiling"` and the action runs | `v0.3 §6.2`: observe mode suppresses ctrlrun's decisions, not the record of an effect that happened | | `Control.evaluate` | **no** | it takes an `Action` and not an effect key, and it writes nothing, so it can resolve no record to count on. A caller can therefore be told `approve` for an attempt `execute` will refuse (§5.5). Its docstring says so | | `Control.resume` | **no** | it reserves nothing, so there is no new number to compare. One attempt can invoke the executor many times through it, which §5.7 states | | `Control.delegate` / `Control.revoke` | **no** | they reserve no effect | @@ -2756,7 +2756,7 @@ Everything in `v0.1 §9`, `v0.2 §12`, `v0.3 §13`, `v0.4 §11`, `v0.5 §11` and deliver, and specifically the milestone's *Do not build* list, each with its reason: - **Generic fencing tokens.** Fencing works only where the resource validates the token, and the resources here, - Stripe, the Kubernetes API, an SMTP server, accept no CTRLRun fence. The only enforceable point is the gateway, + Stripe, the Kubernetes API, an SMTP server, accept no ctrlrun fence. The only enforceable point is the gateway, and for `@protect` a fence degrades to "refuse to start under a stale lease", which `plan_reservation` already does. A fence would be an elaborate mechanism whose guarantee is the one already held. - **Consequence budgets.** The metric, scope and window shape is right and the hard part is unwritten: consume on @@ -3159,7 +3159,7 @@ makes it the operator's claim, made by the person who knows the tool, and this o overrides it in one direction only: the operator asserted that *this tool* reports errors before acting, which is true of the call it answers, and on a continuation the call it answers is not the one that carries the effect. The upstream's own response is still relayed unchanged, the tool's -error included, so a client sees exactly what the tool said and CTRLRun records that the outcome is +error included, so a client sees exactly what the tool said and ctrlrun records that the outcome is unknown. The price is a `ctrlrun resolve` where 0.6.1 permitted a retry, and the alternative is a retry of an effect the remote may be part-way through. diff --git a/docs/SPEC-v0.8.md b/docs/SPEC-v0.8.md index 32f9c504..f29b2cb6 100644 --- a/docs/SPEC-v0.8.md +++ b/docs/SPEC-v0.8.md @@ -1,4 +1,4 @@ -# CTRLRun v0.8 Specification: Oversight +# ctrlrun v0.8 Specification: Oversight **Status:** draft, build-list item 0. **Delta over:** `SPEC-v0.1.md`, `SPEC-v0.2.md`, `SPEC-v0.3.md`, `SPEC-v0.4.md`, `SPEC-v0.5.md`, @@ -522,7 +522,7 @@ controls: (`title`, `source`), gated on `ctrlrun.policy/v6` so an older reader refuses the document rather than ignoring the key (§11.3, and `v0.6 §9.5`'s rule for `controls:` itself). -**CTRLRun does not interpret it**, exactly as `v0.6 §7.3` says it does not interpret `source:`: it +**ctrlrun does not interpret it**, exactly as `v0.6 §7.3` says it does not interpret `source:`: it does not know what `payments-owner` means, does not check that such a role exists anywhere, and makes no compliance claim on the strength of one. diff --git a/docs/SPEC-v0.9.md b/docs/SPEC-v0.9.md index 67047a36..d06e2172 100644 --- a/docs/SPEC-v0.9.md +++ b/docs/SPEC-v0.9.md @@ -45,7 +45,7 @@ milestone about limits attracts more scope than any before it. is evidenced in a document somebody reviewed, attenuates down a delegation chain, and holds its consumption through an unresolved outcome. The two are different objects that happen to count. - **Not a quota service.** Nothing is served, nothing is published, no endpoint answers "how much is - left". `v0.3 §1.1`'s rule that CTRLRun consumes and issues nothing is not relaxed here. + left". `v0.3 §1.1`'s rule that ctrlrun consumes and issues nothing is not relaxed here. - **Not a consequence taxonomy.** A budget names a metric. The kernel does not know what `amount` means, does not know which of two actions is more serious, and does not rank, score or grade. Grading an operator's actions is the same claim `v0.4 §3.9` refuses to make about their policy. @@ -1374,7 +1374,7 @@ operational one. **Rows older than the longest window on any budget of a grant cannot affect any future decision.** That is the sentence an operator needs, and it is a consequence of §2.5 rather than a promise this milestone implements: it means such rows may be archived out of the live store by whatever an -operator already uses to archive a database, without changing what the kernel decides. CTRLRun +operator already uses to archive a database, without changing what the kernel decides. ctrlrun ships no retention command, no vacuum, and no policy key that expires evidence. **What it owes here is the invariant that makes somebody else's retention safe**, and that invariant is stated in the paragraph above. @@ -1785,7 +1785,7 @@ because an earlier draft attributed all of them to the roadmap, and nine of thir - **A fleet-wide budget across stores.** One store, one ledger. `v0.7 §4.6` gave the same answer for idempotency tokens: the kernel's consistency claim stops at its store's transaction. - **Anything that reads a prompt to decide which task an agent is on** (§6.3). -- **A quota endpoint, a spend API, or a published balance.** `v0.3 §1.1`: CTRLRun consumes and issues +- **A quota endpoint, a spend API, or a published balance.** `v0.3 §1.1`: ctrlrun consumes and issues nothing. - **An automatic expiry on a hold.** It is the refund R2 refuses, on a delay (§4.6). - **A fourth guarantee id** (§8). diff --git a/docs/assets/logo.svg b/docs/assets/logo.svg index 4c97d331..ddb3d243 100644 --- a/docs/assets/logo.svg +++ b/docs/assets/logo.svg @@ -1,4 +1,4 @@ - + diff --git a/docs/assets/social-preview.md b/docs/assets/social-preview.md index 05258ef3..8fd0d950 100644 --- a/docs/assets/social-preview.md +++ b/docs/assets/social-preview.md @@ -13,7 +13,7 @@ AI agents." headline) rather than the earlier dark GitHub-only card. | Size | 1280 × 640 px (GitHub's recommended size; rendered at 2:1 everywhere it is shown) | | Safe area | Keep text inside 72 px margins; previews are cropped to 1.91:1 on some services | | Background | `#ffffff`, no photograph, no gradient | -| Wordmark | The amber keycap (`#F5A623`, edge `#B8730A`) and `CTRLRun` in `#14161b`, top left, matching `docs/images/wordmark-light.svg` | +| Wordmark | The amber keycap (`#F5A623`, edge `#B8730A`) and `ctrlrun` in `#14161b`, top left, matching `docs/images/wordmark-light.svg` | | Eyebrow | `CONTROL THE ACTION. KEEP THE AUTONOMY.` at 20 px, letter-spaced, `#8a8f98` | | Headline | Two lines at 76 px bold, `#14161b`: *Execution safety / for AI agents.* — the final period in `#F5A623` | | Subheadline | The homepage's tagline at 28 px, `#6b7280`: *Let agents act. Keep control of what happens next.* | diff --git a/docs/assets/social-preview.svg b/docs/assets/social-preview.svg index b7405266..c0c48559 100644 --- a/docs/assets/social-preview.svg +++ b/docs/assets/social-preview.svg @@ -1,4 +1,4 @@ - + diff --git a/examples/acs/README.md b/examples/acs/README.md index c58e25cd..f98a8ea7 100644 --- a/examples/acs/README.md +++ b/examples/acs/README.md @@ -1,7 +1,7 @@ # The double-refund scenario, through ACS hooks ACS has no `examples/` directory of its own as of commit `c7ad162` (2026-08-11), so this -follows CTRLRun's own convention: one runnable script, its policy beside it, no network, and +follows ctrlrun's own convention: one runnable script, its policy beside it, no network, and its own state directory. ``` @@ -11,12 +11,12 @@ python examples/acs/main.py ## What it shows A tool call fires two ACS hooks. `steps/toolCallRequest` is where a Guardian decides; -`steps/toolCallResult` is where the platform reports what happened. CTRLRun answers both: +`steps/toolCallResult` is where the platform reports what happened. ctrlrun answers both: it takes the reservation at the first and closes it at the second, which is what makes the retry refusable — the first call's effect record is still open when the second arrives. The remote commits the refund and the response goes missing. ACS reports `exit_status: -"timeout"`, and **ACS does not say what a timeout means for the side effect**. CTRLRun +"timeout"`, and **ACS does not say what a timeout means for the side effect**. ctrlrun records `AMBIGUOUS` and refuses the retry, because a tool that timed out after acting and one that timed out before acting send the same string. diff --git a/examples/acs/main.py b/examples/acs/main.py index 5deb1bc7..bbe7733a 100644 --- a/examples/acs/main.py +++ b/examples/acs/main.py @@ -1,16 +1,16 @@ #!/usr/bin/env python3 -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 -"""The double-refund scenario, through ACS hooks, with CTRLRun behind them. +"""The double-refund scenario, through ACS hooks, with ctrlrun behind them. ACS is advisory: a Guardian returns a decision and the *platform* runs the tool. So one tool call fires two hooks — `steps/toolCallRequest` before, `steps/toolCallResult` after — and -CTRLRun holds the reservation between them. That is what makes the second attempt refusable: +ctrlrun holds the reservation between them. That is what makes the second attempt refusable: the first call's effect record is still open when the second arrives. The remote commits the refund and then the response goes missing. ACS reports that as `exit_status: "timeout"`, and ACS says nothing about what a timeout means for the side -effect. CTRLRun records it as AMBIGUOUS — the fail-closed reading — and refuses the retry. +effect. ctrlrun records it as AMBIGUOUS — the fail-closed reading — and refuses the retry. python examples/acs/main.py @@ -55,7 +55,7 @@ def call_tool(self, tool: str, arguments: dict[str, Any], *, lose_response: bool if decision != "allow": return f"{decision}: {verdict['result'].get('reasoning', '')}" - # The platform runs the tool. CTRLRun did not, and does not know what happened until + # The platform runs the tool. ctrlrun did not, and does not know what happened until # the result hook tells it. self.tool_calls += 1 exit_status = "timeout" if lose_response else "success" diff --git a/examples/agent-race/main.py b/examples/agent-race/main.py index 46bc7c0e..61777283 100644 --- a/examples/agent-race/main.py +++ b/examples/agent-race/main.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Two agents, one effect: the second one loses, and loses before the remote is touched. diff --git a/examples/anchored-chain/main.py b/examples/anchored-chain/main.py index 6cca27f9..485ebd28 100644 --- a/examples/anchored-chain/main.py +++ b/examples/anchored-chain/main.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Erasing the end of the receipt log costs two SQL statements. An anchor makes it show. @@ -40,7 +40,7 @@ class FileAnchorProvider: """An anchor provider, in the shape `SPEC-v0.11.md` §3.2 defines and nothing more. - **CTRLRun ships none**, deliberately: `ROADMAP.md` names RFC 3161, and an RFC 3161 client is + **ctrlrun ships none**, deliberately: `ROADMAP.md` names RFC 3161, and an RFC 3161 client is a network client, which does not belong in a wheel whose rule is stdlib plus `pyyaml` and `click`. So the provider is yours. A real one would be a timestamp authority, a transparency log, an append-only bucket in another account, or a file on a host your database's writer @@ -95,7 +95,7 @@ def four_refunds(database: Path) -> None: def erase_the_tail(database: Path, keep_through: int) -> None: - """The attack, in the two statements it really takes. Nothing here goes through CTRLRun.""" + """The attack, in the two statements it really takes. Nothing here goes through ctrlrun.""" connection = sqlite3.connect(database) connection.execute("DELETE FROM receipts WHERE seq > ?", (keep_through,)) row = connection.execute("SELECT seq, hash FROM receipts ORDER BY seq DESC LIMIT 1").fetchone() diff --git a/examples/approval-mutation/main.py b/examples/approval-mutation/main.py index 8c0f2f34..41faaba8 100644 --- a/examples/approval-mutation/main.py +++ b/examples/approval-mutation/main.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """An approval authorizes one exact action, not a category of action. diff --git a/examples/approval-replay/main.py b/examples/approval-replay/main.py index 55a5b31a..4a9c80ae 100644 --- a/examples/approval-replay/main.py +++ b/examples/approval-replay/main.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """An approval is worth exactly one execution. diff --git a/examples/authority-escalation/ctrlrun.yaml b/examples/authority-escalation/ctrlrun.yaml index 9f45a086..578145fe 100644 --- a/examples/authority-escalation/ctrlrun.yaml +++ b/examples/authority-escalation/ctrlrun.yaml @@ -53,7 +53,7 @@ actions: # server behind the same name, or a tool whose schema moved under an approved action name, is # a DENY rather than an authorised call to something nobody reviewed. # - # The honest slice of `ASI04` and nothing more: CTRLRun decides actions and never inspects a + # The honest slice of `ASI04` and nothing more: ctrlrun decides actions and never inspects a # package, a model, a registry or a build. # # **This entry is here to be read and graded, and `main.py` does not call it**, which is the diff --git a/examples/authority-escalation/main.py b/examples/authority-escalation/main.py index 6dee15b6..739faebf 100644 --- a/examples/authority-escalation/main.py +++ b/examples/authority-escalation/main.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """A delegated grant cannot be wider than the one it came from, and it is re-checked. diff --git a/examples/authority/payments.yaml b/examples/authority/payments.yaml index e54bc0f2..35f9aed6 100644 --- a/examples/authority/payments.yaml +++ b/examples/authority/payments.yaml @@ -78,7 +78,7 @@ authority: # SPEC-v0.8 §3. A control may name the role that answers for it, and an approval from a # principal whose verified claims do not carry that role is refused, with the control named. # -# CTRLRun does not interpret the role. It does not know what `payments-approver` means, does +# ctrlrun does not interpret the role. It does not know what `payments-approver` means, does # not check that such a role exists anywhere, and claims nothing about a standard or an audit # on the strength of one -- exactly as it does not interpret `source:`. What it does is compare one string from # this registry against one claim on a credential somebody else verified. diff --git a/examples/cookbook/credential-rotation-agent/main.py b/examples/cookbook/credential-rotation-agent/main.py index 2f33afd4..67833ad9 100644 --- a/examples/cookbook/credential-rotation-agent/main.py +++ b/examples/cookbook/credential-rotation-agent/main.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 # Extracted by CTRLRun/ctrlrun-docs tools/docs_audit/render_cookbook.py from # docs/cookbook/credential-rotation-agent.mdx — edit the page, never this file. diff --git a/examples/cookbook/crm-update-agent/main.py b/examples/cookbook/crm-update-agent/main.py index 5e2bf59c..428f39e6 100644 --- a/examples/cookbook/crm-update-agent/main.py +++ b/examples/cookbook/crm-update-agent/main.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 # Extracted by CTRLRun/ctrlrun-docs tools/docs_audit/render_cookbook.py from # docs/cookbook/crm-update-agent.mdx — edit the page, never this file. diff --git a/examples/cookbook/customer-notification-agent/main.py b/examples/cookbook/customer-notification-agent/main.py index 1a2a8e74..bf7db8ed 100644 --- a/examples/cookbook/customer-notification-agent/main.py +++ b/examples/cookbook/customer-notification-agent/main.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 # Extracted by CTRLRun/ctrlrun-docs tools/docs_audit/render_cookbook.py from # docs/cookbook/customer-notification-agent.mdx — edit the page, never this file. diff --git a/examples/cookbook/data-deletion-agent/main.py b/examples/cookbook/data-deletion-agent/main.py index 3ed71aff..a3159915 100644 --- a/examples/cookbook/data-deletion-agent/main.py +++ b/examples/cookbook/data-deletion-agent/main.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 # Extracted by CTRLRun/ctrlrun-docs tools/docs_audit/render_cookbook.py from # docs/cookbook/data-deletion-agent.mdx — edit the page, never this file. diff --git a/examples/cookbook/database-migration-agent/main.py b/examples/cookbook/database-migration-agent/main.py index cb5eb52e..c1cae3a1 100644 --- a/examples/cookbook/database-migration-agent/main.py +++ b/examples/cookbook/database-migration-agent/main.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 # Extracted by CTRLRun/ctrlrun-docs tools/docs_audit/render_cookbook.py from # docs/cookbook/database-migration-agent.mdx — edit the page, never this file. diff --git a/examples/cookbook/deploy-agent/main.py b/examples/cookbook/deploy-agent/main.py index 7eb5eb6c..fb04b71b 100644 --- a/examples/cookbook/deploy-agent/main.py +++ b/examples/cookbook/deploy-agent/main.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 # Extracted by CTRLRun/ctrlrun-docs tools/docs_audit/render_cookbook.py from # docs/cookbook/deploy-agent.mdx — edit the page, never this file. diff --git a/examples/cookbook/iam-agent/main.py b/examples/cookbook/iam-agent/main.py index 89182f1b..47c880a9 100644 --- a/examples/cookbook/iam-agent/main.py +++ b/examples/cookbook/iam-agent/main.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 # Extracted by CTRLRun/ctrlrun-docs tools/docs_audit/render_cookbook.py from # docs/cookbook/iam-agent.mdx — edit the page, never this file. diff --git a/examples/cookbook/manager-and-worker/main.py b/examples/cookbook/manager-and-worker/main.py index 553be4aa..f2839f24 100644 --- a/examples/cookbook/manager-and-worker/main.py +++ b/examples/cookbook/manager-and-worker/main.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 # Extracted by CTRLRun/ctrlrun-docs tools/docs_audit/render_cookbook.py from # docs/cookbook/manager-and-worker.mdx — edit the page, never this file. diff --git a/examples/cookbook/observe-then-enforce/main.py b/examples/cookbook/observe-then-enforce/main.py index 56db6289..1ff61ade 100644 --- a/examples/cookbook/observe-then-enforce/main.py +++ b/examples/cookbook/observe-then-enforce/main.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 # Extracted by CTRLRun/ctrlrun-docs tools/docs_audit/render_cookbook.py from # docs/cookbook/observe-then-enforce.mdx — edit the page, never this file. diff --git a/examples/cookbook/outbound-email-agent/main.py b/examples/cookbook/outbound-email-agent/main.py index 830130da..af8577c4 100644 --- a/examples/cookbook/outbound-email-agent/main.py +++ b/examples/cookbook/outbound-email-agent/main.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 # Extracted by CTRLRun/ctrlrun-docs tools/docs_audit/render_cookbook.py from # docs/cookbook/outbound-email-agent.mdx — edit the page, never this file. diff --git a/examples/cookbook/payout-maker-checker/main.py b/examples/cookbook/payout-maker-checker/main.py index 22104bab..e3364150 100644 --- a/examples/cookbook/payout-maker-checker/main.py +++ b/examples/cookbook/payout-maker-checker/main.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 # Extracted by CTRLRun/ctrlrun-docs tools/docs_audit/render_cookbook.py from # docs/cookbook/payout-maker-checker.mdx — edit the page, never this file. diff --git a/examples/cookbook/protect-an-mcp-server/main.py b/examples/cookbook/protect-an-mcp-server/main.py index 4e5d56c3..fc51a206 100644 --- a/examples/cookbook/protect-an-mcp-server/main.py +++ b/examples/cookbook/protect-an-mcp-server/main.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 # Extracted by CTRLRun/ctrlrun-docs tools/docs_audit/render_cookbook.py from # docs/cookbook/protect-an-mcp-server.mdx — edit the page, never this file. diff --git a/examples/cookbook/receipts-to-opentelemetry/main.py b/examples/cookbook/receipts-to-opentelemetry/main.py index b28ace67..d77a9b6b 100644 --- a/examples/cookbook/receipts-to-opentelemetry/main.py +++ b/examples/cookbook/receipts-to-opentelemetry/main.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 # Extracted by CTRLRun/ctrlrun-docs tools/docs_audit/render_cookbook.py from # docs/cookbook/receipts-to-opentelemetry.mdx — edit the page, never this file. diff --git a/examples/cookbook/reconcile-against-the-remote/main.py b/examples/cookbook/reconcile-against-the-remote/main.py index bb0c36af..a9df627b 100644 --- a/examples/cookbook/reconcile-against-the-remote/main.py +++ b/examples/cookbook/reconcile-against-the-remote/main.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 # Extracted by CTRLRun/ctrlrun-docs tools/docs_audit/render_cookbook.py from # docs/cookbook/reconcile-against-the-remote.mdx — edit the page, never this file. diff --git a/examples/cookbook/refund-agent/main.py b/examples/cookbook/refund-agent/main.py index dbfc1fec..336425f7 100644 --- a/examples/cookbook/refund-agent/main.py +++ b/examples/cookbook/refund-agent/main.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 # Extracted by CTRLRun/ctrlrun-docs tools/docs_audit/render_cookbook.py from # docs/cookbook/refund-agent.mdx — edit the page, never this file. diff --git a/examples/cookbook/resolve-an-ambiguous-effect/main.py b/examples/cookbook/resolve-an-ambiguous-effect/main.py index fa291e87..40b26ab6 100644 --- a/examples/cookbook/resolve-an-ambiguous-effect/main.py +++ b/examples/cookbook/resolve-an-ambiguous-effect/main.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 # Extracted by CTRLRun/ctrlrun-docs tools/docs_audit/render_cookbook.py from # docs/cookbook/resolve-an-ambiguous-effect.mdx — edit the page, never this file. diff --git a/examples/cookbook/slack-approvals/main.py b/examples/cookbook/slack-approvals/main.py index 30c832fe..ef057009 100644 --- a/examples/cookbook/slack-approvals/main.py +++ b/examples/cookbook/slack-approvals/main.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 # Extracted by CTRLRun/ctrlrun-docs tools/docs_audit/render_cookbook.py from # docs/cookbook/slack-approvals.mdx — edit the page, never this file. diff --git a/examples/cookbook/sqlite-to-postgres/main.py b/examples/cookbook/sqlite-to-postgres/main.py index e24cab54..352ce541 100644 --- a/examples/cookbook/sqlite-to-postgres/main.py +++ b/examples/cookbook/sqlite-to-postgres/main.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 # Extracted by CTRLRun/ctrlrun-docs tools/docs_audit/render_cookbook.py from # docs/cookbook/sqlite-to-postgres.mdx — edit the page, never this file. diff --git a/examples/cookbook/verify-in-github-actions/run.sh b/examples/cookbook/verify-in-github-actions/run.sh index 9768fbab..45d2f082 100644 --- a/examples/cookbook/verify-in-github-actions/run.sh +++ b/examples/cookbook/verify-in-github-actions/run.sh @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 # Extracted by CTRLRun/ctrlrun-docs tools/docs_audit/render_cookbook.py from # docs/cookbook/verify-in-github-actions.mdx — edit the page, never this file. diff --git a/examples/double-refund/main.py b/examples/double-refund/main.py index f67d09a6..1801bbbd 100644 --- a/examples/double-refund/main.py +++ b/examples/double-refund/main.py @@ -1,11 +1,11 @@ #!/usr/bin/env python3 -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """A lost response is not a failure, and a blind retry is a second refund. The remote commits the refund and *then* the reply goes missing. That order is the whole point: a remote that fails before doing anything is easy, and an executor can say so by -raising `NotExecuted`. This one is the dangerous kind, so CTRLRun records the outcome as +raising `NotExecuted`. This one is the dangerous kind, so ctrlrun records the outcome as AMBIGUOUS rather than failed and refuses the retry. Nothing in this process knows whether the money moved, and the one thing worse than not knowing is guessing. diff --git a/examples/medical_workbench.py b/examples/medical_workbench.py index 5de1f017..268399c8 100644 --- a/examples/medical_workbench.py +++ b/examples/medical_workbench.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Synthetic evidence workbench: real policy, approval, effects, and receipts. diff --git a/examples/without-an-agent/lost-merge/main.py b/examples/without-an-agent/lost-merge/main.py index 06e62021..a09a4919 100644 --- a/examples/without-an-agent/lost-merge/main.py +++ b/examples/without-an-agent/lost-merge/main.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """A merge that landed, a reply that did not, and a job that goes looking for another way. diff --git a/examples/without-an-agent/redelivered-webhook/main.py b/examples/without-an-agent/redelivered-webhook/main.py index 7104fc2b..cd8cd74b 100644 --- a/examples/without-an-agent/redelivered-webhook/main.py +++ b/examples/without-an-agent/redelivered-webhook/main.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """No model, no agent: a webhook delivered twice, and a payout sent twice. diff --git a/examples/without-an-agent/retried-task/main.py b/examples/without-an-agent/retried-task/main.py index 0bdaf4b6..b76c3e76 100644 --- a/examples/without-an-agent/retried-task/main.py +++ b/examples/without-an-agent/retried-task/main.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """No model, no agent, no prompt: a task queue with automatic retry, charging twice. @@ -11,7 +11,7 @@ happened*. The provider commits the charge and then the reply is lost. The queue does what it is -configured to do. CTRLRun records the outcome as AMBIGUOUS rather than failed, and the second +configured to do. ctrlrun records the outcome as AMBIGUOUS rather than failed, and the second attempt is refused before it reaches the provider. python examples/without-an-agent/retried-task/main.py diff --git a/fuzz/fuzz_canonical.py b/fuzz/fuzz_canonical.py index 988c412e..3728b2f9 100644 --- a/fuzz/fuzz_canonical.py +++ b/fuzz/fuzz_canonical.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Atheris entry point for the canonical property. The invariants live in `properties.py`. diff --git a/fuzz/fuzz_policy.py b/fuzz/fuzz_policy.py index 2260d9f8..22d76b00 100644 --- a/fuzz/fuzz_policy.py +++ b/fuzz/fuzz_policy.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Atheris entry point for the policy property. The invariants live in `properties.py`. diff --git a/fuzz/properties.py b/fuzz/properties.py index b06863c6..4498d056 100644 --- a/fuzz/properties.py +++ b/fuzz/properties.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The invariants the fuzz targets assert, and the decoders that turn bytes into inputs. diff --git a/research/framework-probe/README.md b/research/framework-probe/README.md index d606dbcc..1a8dd921 100644 --- a/research/framework-probe/README.md +++ b/research/framework-probe/README.md @@ -1,11 +1,11 @@ -# The CTRLRun framework probe +# The ctrlrun framework probe **This table reports behaviour, not quality.** A framework that retries a lost response is doing what its documentation says it does. The finding here is about what an agent stack does *without* an effect-level guard — and it would be dishonest to present it as a judgment on any of these projects, none of which claims to solve this problem. -It answers a question CTRLRun has so far only asserted: *what actually happens when the +It answers a question ctrlrun has so far only asserted: *what actually happens when the response is lost and the framework retries?* > **Two of the four framework adapters have now been run against a model. Two have not.** @@ -104,7 +104,7 @@ against langchain 1.4.0, not assumed — so one string serves both. ## The scenarios Both are already in `examples/`, run here through somebody else's agent loop instead of -through CTRLRun. +through ctrlrun. **double-refund.** The remote commits the refund and then closes the connection without a response. Does the framework retry, and does the effect land twice? diff --git a/research/framework-probe/framework_probe/__init__.py b/research/framework-probe/framework_probe/__init__.py index 83dd0d7a..8f90cad1 100644 --- a/research/framework-probe/framework_probe/__init__.py +++ b/research/framework-probe/framework_probe/__init__.py @@ -1,4 +1,4 @@ -"""The CTRLRun framework probe. SPEC-v0.4 §7. Research, not part of the package. +"""The ctrlrun framework probe. SPEC-v0.4 §7. Research, not part of the package. This directory lives **outside `src/`**. It is not packaged, never imported by `ctrlrun`, and its per-framework dependencies are never installed by `ctrlrun` or by any of its extras diff --git a/research/soak/README.md b/research/soak/README.md index 33726b01..316e9c8d 100644 --- a/research/soak/README.md +++ b/research/soak/README.md @@ -62,7 +62,7 @@ asserts the rendered table never claims a duration it did not measure. One measured run, and the numbers are the numbers: ``` -CTRLRun soak — postgres (schema soak_968eae6651) +ctrlrun soak — postgres (schema soak_968eae6651) ran 20m 0s (2026-09-05T19:05:37Z → 2026-09-05T19:25:37Z) actions 889735 ambiguous 133393 (133393 explained, 0 unexplained) diff --git a/research/soak/soak/ledger.py b/research/soak/soak/ledger.py index 493bdd60..71615af9 100644 --- a/research/soak/soak/ledger.py +++ b/research/soak/soak/ledger.py @@ -201,7 +201,7 @@ def _human(seconds: float) -> str: def render(document: dict[str, object]) -> str: """The table a human reads, and the one the maintainer reads before it goes in a PR.""" lines = [ - f"CTRLRun soak — {document['backend']}", + f"ctrlrun soak — {document['backend']}", f" ran {document['elapsed_human']} " f"({document['started_at']} → {document['ended_at']})", f" actions {document['actions']}", diff --git a/scripts/check.sh b/scripts/check.sh index c06cda69..8159d6c1 100755 --- a/scripts/check.sh +++ b/scripts/check.sh @@ -1,5 +1,5 @@ #!/bin/sh -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 # Everything CI's `check` job runs, in one place, cheapest first. # diff --git a/scripts/coverage_floor.py b/scripts/coverage_floor.py index ff08f777..af60ef5c 100644 --- a/scripts/coverage_floor.py +++ b/scripts/coverage_floor.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Hold the coverage floors CONTRIBUTING.md states, from the JSON `scripts/check.sh` writes. diff --git a/scripts/five_schema_chain.py b/scripts/five_schema_chain.py index d48a4b0f..53f5eaad 100644 --- a/scripts/five_schema_chain.py +++ b/scripts/five_schema_chain.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Build one receipt chain with five RELEASED ctrlrun wheels, then verify it with this build. diff --git a/scripts/lock.sh b/scripts/lock.sh index 7ca3c2a9..8aeacb04 100755 --- a/scripts/lock.sh +++ b/scripts/lock.sh @@ -1,5 +1,5 @@ #!/bin/sh -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 # Regenerates every hash-pinned requirements file CI installs from. # diff --git a/scripts/normalize_sdist.py b/scripts/normalize_sdist.py index 04c7b562..3bd6e144 100644 --- a/scripts/normalize_sdist.py +++ b/scripts/normalize_sdist.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Rewrite an sdist so that two builds of the same commit are the same bytes. diff --git a/scripts/release_provenance.py b/scripts/release_provenance.py index 3a8fd86a..dc5ac6f2 100755 --- a/scripts/release_provenance.py +++ b/scripts/release_provenance.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Turn the attestation `actions/attest-build-provenance` produced into release assets. diff --git a/scripts/sbom.sh b/scripts/sbom.sh index 41855229..0bc900e2 100755 --- a/scripts/sbom.sh +++ b/scripts/sbom.sh @@ -1,5 +1,5 @@ #!/bin/sh -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 # An SBOM of the distribution this repository ships, measured from the built wheel. # diff --git a/server.json b/server.json index 4344bafc..1b95d5b8 100644 --- a/server.json +++ b/server.json @@ -1,8 +1,8 @@ { "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", "name": "io.github.CTRLRun/ctrlrun-mcp-operator", - "title": "CTRLRun Operator", - "description": "Answer CTRLRun approvals from an MCP client: list what is held, inspect it, grant or deny.", + "title": "ctrlrun Operator", + "description": "Answer ctrlrun approvals from an MCP client: list what is held, inspect it, grant or deny.", "websiteUrl": "https://docs.ctrlrun.dev/mcp/overview", "repository": { "url": "https://github.com/CTRLRun/ctrlrun", diff --git a/src/ctrlrun/__init__.py b/src/ctrlrun/__init__.py index 909b32ef..cb64392e 100644 --- a/src/ctrlrun/__init__.py +++ b/src/ctrlrun/__init__.py @@ -1,6 +1,6 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 -"""CTRLRun: make consequential AI-agent actions safe to execute. +"""ctrlrun: make consequential AI-agent actions safe to execute. Public API re-exports land with build-list item 1 onward; SPEC-v0.1 §8 freezes the names. """ diff --git a/src/ctrlrun/acs.py b/src/ctrlrun/acs.py index 7011d71e..5c04184e 100644 --- a/src/ctrlrun/acs.py +++ b/src/ctrlrun/acs.py @@ -1,6 +1,6 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 -"""An ACS control hook backed by CTRLRun. Ships in `ctrlrun[gateway]`. +"""An ACS control hook backed by ctrlrun. Ships in `ctrlrun[gateway]`. Read against the Agent Control Standard v0.1.0 schemas in `GenAI-Security-Project/agent-control-standard` at commit `c7ad162` (2026-08-11): @@ -8,7 +8,7 @@ `hooks/tool-call-request.json`, `hooks/tool-call-result.json` and `ask-details.json`. `https://ctrlrun.dev/docs/ACS` records what was read and where the two models disagree. -**ACS is advisory; CTRLRun is executing.** A Guardian returns a decision and the *platform* +**ACS is advisory; ctrlrun is executing.** A Guardian returns a decision and the *platform* runs the tool, which is the opposite way round from `@protect`. So one action is split across two hooks: @@ -51,7 +51,7 @@ #: The ACS revision these mappings were read against. ACS_VERSION: Final = "0.1.0" -#: The two `steps/*` hooks CTRLRun answers. Every other method is somebody else's checkpoint. +#: The two `steps/*` hooks ctrlrun answers. Every other method is somebody else's checkpoint. TOOL_CALL_REQUEST: Final = "steps/toolCallRequest" TOOL_CALL_RESULT: Final = "steps/toolCallResult" @@ -84,7 +84,7 @@ class AcsControlHook: - """Answer ACS `steps/*` hooks with CTRLRun's decisions and outcomes. + """Answer ACS `steps/*` hooks with ctrlrun's decisions and outcomes. One `Control`, one prefix. `prefix` names the tool namespace in the action name, the way the gateway's `--alias` does: `..` — so a policy addresses one @@ -188,7 +188,7 @@ def handle( # SPEC-v0.3 §8.4 — a credential offered and rejected, or a provider that named # nobody. Answered as `deny` rather than as a protocol `error`: an error envelope # says "the Guardian could not answer", and a platform is free to decide what to - # do with that. A denial says what CTRLRun means, which is that the tool must not + # do with that. A denial says what ctrlrun means, which is that the tool must not # run. # # The reason code is **not** always `no_principal`. This clause spans the whole of @@ -206,7 +206,7 @@ def handle( return _error( rpc_id, METHOD_NOT_ANSWERED, - f"{method} is not a checkpoint CTRLRun answers; it decides tool calls only", + f"{method} is not a checkpoint ctrlrun answers; it decides tool calls only", ) # --- steps/toolCallRequest ---------------------------------------------------------- @@ -248,7 +248,7 @@ def _on_request( ) def suspend_holding_the_reservation() -> Any: - # The platform executes, not CTRLRun. `Suspended` is how an executor says the + # The platform executes, not ctrlrun. `Suspended` is how an executor says the # outcome is not known yet *and no outcome should be recorded* (§6.9): the # record stays EXECUTING, the lease is extended, and this request_id is what the # result hook presents to close it. @@ -318,7 +318,7 @@ def suspend_holding_the_reservation() -> Any: def _ask( self, rpc_id: Any, request_id: str, action: Action, pending: ApprovalRequired ) -> dict[str, Any]: - """CTRLRun's APPROVE in ACS's shape (`ask-details.json`). + """ctrlrun's APPROVE in ACS's shape (`ask-details.json`). `approver`, `question` and `timeout_seconds` are all required by the schema, so all three are present or the response is not conformant. The question carries the @@ -347,7 +347,7 @@ def _ask( def _on_result(self, rpc_id: Any, request_id: str, params: Mapping[str, Any]) -> dict[str, Any]: """Close the reservation the request hook took, with what actually happened. - ACS describes this hook as an output redaction checkpoint. CTRLRun redacts nothing — + ACS describes this hook as an output redaction checkpoint. ctrlrun redacts nothing — it records — so the decision is always `allow`; the work is the outcome it writes. """ payload = _mapping(params.get("payload"), "params.payload") @@ -387,7 +387,7 @@ def report_what_happened() -> Any: # No held suspension matches. A restarted Guardian, a result fired twice, or one # arriving out of order. There is nothing to close, and inventing an outcome for # an effect nobody reserved would be worse than recording none. - _LOG.warning("no held CTRLRun reservation matches ACS request_id_ref %s", held) + _LOG.warning("no held ctrlrun reservation matches ACS request_id_ref %s", held) except CTRLRunError as refused: _LOG.warning("could not close the reservation for %s: %s", held, refused) return _final(rpc_id, request_id, ALLOW) diff --git a/src/ctrlrun/action.py b/src/ctrlrun/action.py index 9fb165a4..c51c8031 100644 --- a/src/ctrlrun/action.py +++ b/src/ctrlrun/action.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Action model, canonicalization, action_hash. Build-list item 1; SPEC-v0.1 §2.""" diff --git a/src/ctrlrun/adapter.py b/src/ctrlrun/adapter.py index 081a95e6..e041b51f 100644 --- a/src/ctrlrun/adapter.py +++ b/src/ctrlrun/adapter.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The adapter surface. Build-list item 2; SPEC-v0.5 §2, §3. @@ -199,7 +199,7 @@ class FrameworkInterrupt(Protocol): #: A **declaration about the framework**, not a setting. `True` makes the binding check #: mandatory, and an answer that omits `approved_arguments` is refused. `False` says the #: resumption carries nothing the adapter can inspect, so the binding across the interrupt - #: is the framework's checkpoint rather than CTRLRun's -- attribution, not prevention -- and + #: is the framework's checkpoint rather than ctrlrun's -- attribution, not prevention -- and #: it is not free: the conformance kit reports `binding: not_applicable` with the adapter's #: reason, permanently, where a reviewer reads first. A flag hides a weakening; this #: publishes one. diff --git a/src/ctrlrun/anchor.py b/src/ctrlrun/anchor.py index 554e9cd4..6533e793 100644 --- a/src/ctrlrun/anchor.py +++ b/src/ctrlrun/anchor.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The anchor: the chain's head, recorded where the store's writer cannot reach it. @@ -31,7 +31,7 @@ sentence about tamper-evidence. **Rule 1 (§1.1): the anchor consumes a timestamp and issues nothing.** No key generation, no -rotation, no revocation, no signing. `SPEC-v0.3.md` §1.1's rule that CTRLRun consumes identity and +rotation, no revocation, no signing. `SPEC-v0.3.md` §1.1's rule that ctrlrun consumes identity and issues none, applied to time. Nothing in this module mints anything. """ @@ -104,7 +104,7 @@ class Anchor: to be exactly what it was. `at` is the **provider's** time and never this process's clock. Rule 1 is that the anchor - consumes a timestamp and issues none, so a time CTRLRun generated would be CTRLRun vouching + consumes a timestamp and issues none, so a time ctrlrun generated would be ctrlrun vouching for itself, which is the thing an external anchor exists to stop. """ @@ -139,7 +139,7 @@ class AnchorProvider(Protocol): **Four calls, not two, and the last two are why.** An earlier draft had `make` and `check` alone, and a review broke it in one extra statement: with only those, the record of *which* - anchors exist lives in CTRLRun's own table, so deleting the newest row there leaves the older + anchors exist lives in ctrlrun's own table, so deleting the newest row there leaves the older anchor reproducing and the truncation invisible. Three SQL statements instead of two, which is the number this design claimed to avoid. `latest()` and `since()` move that history to the side that cannot be rewritten. @@ -150,9 +150,9 @@ def make(self, seq: int, hash: str, kind: str) -> tuple[str, datetime]: **The return is a pair rather than §3.2's bare token, and this is a deviation the PR records rather than one made quietly.** §3.2's table says *"returns an opaque token"* - while §3.3 says CTRLRun caches *"the pair, the token, and the time"*, and §10 refuses + while §3.3 says ctrlrun caches *"the pair, the token, and the time"*, and §10 refuses *"an anchor whose time runs backwards against the one before it"*. A time the provider - does not supply is one CTRLRun would have to read from its own clock, which rule 1 + does not supply is one ctrlrun would have to read from its own clock, which rule 1 forbids: the anchor consumes a timestamp and issues none. """ ... @@ -394,7 +394,7 @@ def _require_time_moves_forward(existing: Iterable[Anchor], anchor: Anchor) -> N raise InvalidArgument( f"this {anchor.kind} anchor's time {anchor.at.isoformat()} is before the last one's " f"{latest.isoformat()}; a timestamp sequence that runs backwards is a " - "misconfiguration or the attack, and CTRLRun cannot tell which" + "misconfiguration or the attack, and ctrlrun cannot tell which" ) @@ -402,7 +402,7 @@ def verify_anchors(store: AnchorSource, provider: AnchorProvider) -> AnchorRepor """Check every anchor the provider holds against the chain in this store (§3.4). **The provider is asked what it holds before the local table is consulted**, and that ordering - is the section's load-bearing decision (§3.3). CTRLRun's table is a *cache*, not a record: a + is the section's load-bearing decision (§3.3). ctrlrun's table is a *cache*, not a record: a row deleted from it is checked anyway, because the question came from outside; a table that was emptied verifies exactly as a store with no anchors does, which is `anchor_missing`. diff --git a/src/ctrlrun/approval.py b/src/ctrlrun/approval.py index d2a75831..7d7e88aa 100644 --- a/src/ctrlrun/approval.py +++ b/src/ctrlrun/approval.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Approval requests, grants and providers. Build-list item 4; SPEC-v0.1 §4. diff --git a/src/ctrlrun/authority.py b/src/ctrlrun/authority.py index 80fbb19d..0333a460 100644 --- a/src/ctrlrun/authority.py +++ b/src/ctrlrun/authority.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Grants, patterns, containment and evaluation. Build-list item 2; SPEC-v0.3 §4. diff --git a/src/ctrlrun/cli/__init__.py b/src/ctrlrun/cli/__init__.py index 74760fda..6f6885f3 100644 --- a/src/ctrlrun/cli/__init__.py +++ b/src/ctrlrun/cli/__init__.py @@ -1,3 +1,3 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Command-line interface. Build-list item 8; SPEC-v0.1 §8.""" diff --git a/src/ctrlrun/cli/demo.py b/src/ctrlrun/cli/demo.py index ee51c4a0..aeaf7590 100644 --- a/src/ctrlrun/cli/demo.py +++ b/src/ctrlrun/cli/demo.py @@ -1,9 +1,9 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The five-scenario demo with an in-process fake Stripe. SPEC-v0.1 §7 T11, SPEC-v0.3 §1.2. Five ways an agent action goes wrong at the boundary between intention and effect, and what -CTRLRun does about each. Everything runs in this process: no network, no clock skew, no +ctrlrun does about each. Everything runs in this process: no network, no clock skew, no sleeping. The fake remote is the only thing pretending — and it pretends in the one way that matters, by committing before its response goes missing. @@ -167,7 +167,7 @@ def run_demo(root: Path) -> None: def refund(payment_id: str, amount: int, currency: str = "EUR") -> dict[str, Any]: return remote.refund(payment_id, amount) - click.echo("CTRLRun demo — five ways an agent action goes wrong, and what stops it.") + click.echo("ctrlrun demo — five ways an agent action goes wrong, and what stops it.") click.echo( "Policy: refunds up to €1,000 are autonomous, up to €10,000 need a human, " "above that are denied." diff --git a/src/ctrlrun/cli/main.py b/src/ctrlrun/cli/main.py index 5437187e..71da6ac4 100644 --- a/src/ctrlrun/cli/main.py +++ b/src/ctrlrun/cli/main.py @@ -1,10 +1,10 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """click command group for the ctrlrun CLI. Build-list item 8; SPEC-v0.1 §8. The CLI is the human end of the kernel: it answers approval requests, shows the evidence, and resolves the one state no machine may resolve for itself (§5.2). It is also the only -place in CTRLRun that prints. +place in ctrlrun that prints. Every command works on the store an agent is already using — `.ctrlrun/state.db` beside the policy, or wherever `$CTRLRUN_STATE` says (§8) — so approving here answers the request an @@ -140,7 +140,7 @@ ), ) -#: CTRLRun's own parameter on a Postgres URL, peeled off before the URL reaches the driver. The +#: ctrlrun's own parameter on a Postgres URL, peeled off before the URL reaches the driver. The #: same spelling `ctrlrun.conformance.store` uses, because an operator who has seen one should not #: have to learn the other. SCHEMA_PARAM: Final = "ctrlrun_schema" @@ -224,7 +224,7 @@ def _opened(path: Path) -> StateStore: def _require_head(url: str, schema: str) -> None: - """Refuse unless `schema` already holds a CTRLRun database at HEAD. Reads only. + """Refuse unless `schema` already holds a ctrlrun database at HEAD. Reads only. Three refusals, each naming what the operator should do instead, because "that schema is empty" and "your fleet is mid-upgrade" have nothing in common as remedies. @@ -249,7 +249,7 @@ def _require_head(url: str, schema: str) -> None: ) if cursor.fetchone() is None: raise click.ClickException( - f"schema {schema!r} holds no CTRLRun database. A read command does not " + f"schema {schema!r} holds no ctrlrun database. A read command does not " "create one; the store is created by the process that runs your agents." ) cursor.execute( @@ -266,19 +266,19 @@ def _require_head(url: str, schema: str) -> None: if found in (Classification.FORWARD, Classification.EMPTY, Classification.BASELINE): missing = [item.id for item in MIGRATIONS if item.id not in applied] raise click.ClickException( - f"the database in schema {schema!r} is behind this CTRLRun: " + f"the database in schema {schema!r} is behind this ctrlrun: " f"{', '.join(missing)} has not been applied. A read command will not apply it -- " "every other process on that database is still running the version that has not " "got it. Upgrade a writer and let it migrate at open (SPEC-v0.6 §3.6)." ) raise click.ClickException( - f"the database in schema {schema!r} classifies as {found.value} against this CTRLRun " + f"the database in schema {schema!r} classifies as {found.value} against this ctrlrun " f"({len(applied)} migrations recorded). It is not safe to read as though it were HEAD." ) def _peel_schema(url: str) -> tuple[str, str]: - """Take CTRLRun's own schema parameter off a Postgres URL before the driver sees it.""" + """Take ctrlrun's own schema parameter off a Postgres URL before the driver sees it.""" from urllib.parse import parse_qsl, urlencode, urlsplit, urlunsplit parts = urlsplit(url) @@ -364,7 +364,7 @@ def _event( @click.group() @click.version_option(package_name="ctrlrun") def main() -> None: - """CTRLRun — the execution safety layer for AI agents.""" + """ctrlrun — the execution safety layer for AI agents.""" @main.command() @@ -571,7 +571,7 @@ def _loaded_anchor_provider(dotted: str) -> AnchorProvider: if not module_name or not attribute: raise click.UsageError( f"--provider must be 'module:attribute', got {dotted!r}. It names the anchor provider " - "in your own code: CTRLRun ships none, because a timestamp client is a network client" + "in your own code: ctrlrun ships none, because a timestamp client is a network client" ) try: module = importlib.import_module(module_name) @@ -601,7 +601,7 @@ def _loaded_anchor_provider(dotted: str) -> AnchorProvider: "dotted", required=True, metavar="MODULE:ATTR", - help="Your anchor provider (SPEC-v0.11 §3.2). CTRLRun ships none.", + help="Your anchor provider (SPEC-v0.11 §3.2). ctrlrun ships none.", ) @click.option( "--verify", @@ -1401,7 +1401,7 @@ def stats(since: str | None, as_json: bool, store_url: str | None) -> None: def _stats_lines(document: Mapping[str, Any]) -> list[str]: """§6.4's report. Every number comes from the document, so `--json` cannot disagree.""" window = f"{document['from'] or '-'} .. {document['to'] or '-'}" - lines = [f"CTRLRun — {window} ({document['mode']} mode)", ""] + lines = [f"ctrlrun — {window} ({document['mode']} mode)", ""] lines.append(_stat("actions", document["actions"])) if document["mode"] == OBSERVE: lines.append(_stat("would have been denied", document["would_have_been_denied"])) diff --git a/src/ctrlrun/conformance/__init__.py b/src/ctrlrun/conformance/__init__.py index e86ebcf0..b8c22892 100644 --- a/src/ctrlrun/conformance/__init__.py +++ b/src/ctrlrun/conformance/__init__.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The adapter conformance kit. Build-list item 3; SPEC-v0.5 §5. diff --git a/src/ctrlrun/conformance/fixtures.py b/src/ctrlrun/conformance/fixtures.py index 4846f985..702b5b56 100644 --- a/src/ctrlrun/conformance/fixtures.py +++ b/src/ctrlrun/conformance/fixtures.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Adapters broken in one named way each, and one that is not. SPEC-v0.5 §5.4. @@ -157,7 +157,7 @@ class DenialIsAnError(Reference): The mistake is an authentic one, and it is the opposite of `SwallowsDenial`'s: not a no reported as a yes, but a no reported as a *fault*. An author whose framework raises when a human declines -- most do -- lets that exception stand as the outcome, reasoning that the - call did not happen and the framework already said why. It did not happen, and CTRLRun's + call did not happen and the framework already said why. It did not happen, and ctrlrun's evidence log is where that has to be visible: `APPROVAL_DENIED` then `ACTION_DENIED` (`v0.1 §6.2`, SPEC-v0.5 §2.4). Under this adapter a human's answer is indistinguishable from the primitive having crashed -- §10's row for that says the request stays `pending` @@ -204,7 +204,7 @@ class DeniesForItself(Reference): execution, the primitive reached once. Every check but one is satisfied. The one is the evidence. `deny_approval` was never called, so the request is still `pending` - and the log has no `APPROVAL_DENIED` -- a human said no and CTRLRun cannot show it. That is + and the log has no `APPROVAL_DENIED` -- a human said no and ctrlrun cannot show it. That is B3's second check, and this fixture is the only thing that reaches it: `DenialIsAnError` fails the first and `expect` returns there. """ @@ -249,7 +249,7 @@ class FalselyRefusesAfterAsking(DenialIsAnError): # noqa: N818 - a fixture, not That is the escape a second independent review found in the first attempt at checking the declaration, which asked whether the executor had run -- something this adapter can satisfy - while being asked the whole way through. The check that catches it is whether CTRLRun was + while being asked the whole way through. The check that catches it is whether ctrlrun was asked **at all**: this leaves `ACTION_PROPOSED` and `APPROVAL_REQUESTED` behind, and a framework that truly refuses before invoking leaves neither. diff --git a/src/ctrlrun/conformance/report.py b/src/ctrlrun/conformance/report.py index a93471ee..e1d0eace 100644 --- a/src/ctrlrun/conformance/report.py +++ b/src/ctrlrun/conformance/report.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """What the kit reports. SPEC-v0.5 §5.2. @@ -22,7 +22,7 @@ class SuiteStatus(StrEnum): """`StrEnum` for `v0.1 §6.1`'s reason: these render into reports read by tools that never - imported CTRLRun, and `"SuiteStatus.PASS"` is not a status.""" + imported ctrlrun, and `"SuiteStatus.PASS"` is not a status.""" PASS = "pass" FAIL = "fail" diff --git a/src/ctrlrun/conformance/store/__init__.py b/src/ctrlrun/conformance/store/__init__.py index 131b885c..9f2ab46e 100644 --- a/src/ctrlrun/conformance/store/__init__.py +++ b/src/ctrlrun/conformance/store/__init__.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The store conformance suite. Build-list item 1; SPEC-v0.6 §2. diff --git a/src/ctrlrun/conformance/store/backends.py b/src/ctrlrun/conformance/store/backends.py index 1e438caa..b0fcfa8a 100644 --- a/src/ctrlrun/conformance/store/backends.py +++ b/src/ctrlrun/conformance/store/backends.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """What a backend hands the suite, and the two that already exist. SPEC-v0.6 §2.2. @@ -28,7 +28,7 @@ #: rules about whose database it may touch (§4.1). POSTGRES_SCHEMES = ("postgresql://", "postgres://") -#: How a conformance URL carries the schema its store must live in. CTRLRun's, not libpq's. +#: How a conformance URL carries the schema its store must live in. ctrlrun's, not libpq's. SCHEMA_PARAM = "ctrlrun_schema" @@ -179,7 +179,7 @@ def store_from_url(url: str) -> StateStore: bare, schema = _split_schema(url) if schema != "public": - # A conformance URL names a schema CTRLRun owns, so the worker may create it. This + # A conformance URL names a schema ctrlrun owns, so the worker may create it. This # path is never reached by an operator's store: `PostgresStateStore` itself creates # nothing, and verify's scratch schema is made and dropped by the caller (§4.1). PostgresStateStore.create_schema(bare, schema) @@ -188,7 +188,7 @@ def store_from_url(url: str) -> StateStore: def _split_schema(url: str) -> tuple[str, str]: - """Peel CTRLRun's own schema parameter off a conformance URL.""" + """Peel ctrlrun's own schema parameter off a conformance URL.""" from urllib.parse import parse_qsl, urlencode, urlsplit, urlunsplit parts = urlsplit(url) @@ -242,7 +242,7 @@ def open_with_clock(self, clock: Callable[[], datetime]) -> StateStore: return store def url(self) -> str | None: - # The schema travels as CTRLRun's own parameter, stripped by `store_from_url` before the + # The schema travels as ctrlrun's own parameter, stripped by `store_from_url` before the # URL reaches the driver. Encoding it as libpq `options=-csearch_path=` would not work: # `PostgresStateStore` sets `search_path` itself on every connection, so a schema smuggled # through the driver would be silently overridden and eight contenders would race in diff --git a/src/ctrlrun/conformance/store/fixtures.py b/src/ctrlrun/conformance/store/fixtures.py index 466e4b22..19a376ac 100644 --- a/src/ctrlrun/conformance/store/fixtures.py +++ b/src/ctrlrun/conformance/store/fixtures.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The broken-store fixtures. SPEC-v0.6 §2.6. diff --git a/src/ctrlrun/conformance/store/report.py b/src/ctrlrun/conformance/store/report.py index 867a6a46..03539573 100644 --- a/src/ctrlrun/conformance/store/report.py +++ b/src/ctrlrun/conformance/store/report.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """What the store suite reports. SPEC-v0.6 §2.2, §9.7. diff --git a/src/ctrlrun/conformance/store/suites.py b/src/ctrlrun/conformance/store/suites.py index fbbd08e1..88379ee6 100644 --- a/src/ctrlrun/conformance/store/suites.py +++ b/src/ctrlrun/conformance/store/suites.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The store suites. SPEC-v0.6 §2.3, §2.4, §2.5. diff --git a/src/ctrlrun/conformance/store/worker.py b/src/ctrlrun/conformance/store/worker.py index 9a4b45bb..91d4f79c 100644 --- a/src/ctrlrun/conformance/store/worker.py +++ b/src/ctrlrun/conformance/store/worker.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """One contender in `reservation/e1-cross-process`. SPEC-v0.6 §2.4. diff --git a/src/ctrlrun/conformance/suites.py b/src/ctrlrun/conformance/suites.py index 5acedc25..8ed05d0f 100644 --- a/src/ctrlrun/conformance/suites.py +++ b/src/ctrlrun/conformance/suites.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The suites, and what an adapter hands the kit. SPEC-v0.5 §5.2, §5.3. @@ -145,7 +145,7 @@ class ConformanceAdapter(Protocol): constructs a `Control` and a kit that let it would be testing a shape that does not ship. `invoke` runs **one** protected call end to end through the framework and returns the - executor's value. Every CTRLRun exception propagates: the kit asserts on them by type and by + executor's value. Every ctrlrun exception propagates: the kit asserts on them by type and by `reason`, so an adapter that swallowed one fails rather than passing quietly. The adapter is a **courier** for `request.answer`: it carries the kit's answer to its @@ -160,7 +160,7 @@ class ConformanceAdapter(Protocol): #: `False`, because that is the shape most frameworks have and a declaration nobody needs #: to make is one nobody gets wrong. `True` makes the `denial` suite `not_applicable` with #: the adapter's reason -- never a pass -- and it is not free: it says in the report, and in - #: the adapter's README, that a refusal leaves no CTRLRun evidence. + #: the adapter's README, that a refusal leaves no ctrlrun evidence. refuses_before_invoking: bool def invoke(self, request: CallRequest) -> Any: ... @@ -662,7 +662,7 @@ def binding_mutated_answer(adapter: ConformanceAdapter) -> CaseResult: binding_mutated_answer.title, f"{adapter.framework} declares carries_approved_arguments=False: its resumption " "carries nothing the adapter can inspect, so the binding across the interrupt is " - "the framework's checkpoint and not CTRLRun's hash. Attribution, not prevention", + "the framework's checkpoint and not ctrlrun's hash. Attribution, not prevention", ) world = World(adapter, APPROVE) executor = Executor() @@ -756,12 +756,12 @@ def _denial_not_applicable(adapter: ConformanceAdapter) -> CaseResult: An unchecked `not_applicable` is a pass an adapter awards itself. The first version of this checked the wrong things: it drove the refusal and asserted the executor was not reached, - which a framework that refuses *inside* CTRLRun satisfies just as well. A second independent + which a framework that refuses *inside* ctrlrun satisfies just as well. A second independent review broke it with one class attribute — `refuses_before_invoking = True` on `fixtures.DenialIsAnError`, the fixture §5.4 requires to **fail** this suite — and got `denial: not_applicable` and `report.ok is True`. - The discriminator is not what the adapter did with the answer; it is **whether CTRLRun was + The discriminator is not what the adapter did with the answer; it is **whether ctrlrun was asked at all**. A framework that genuinely refuses before invoking proposes no action: no `ACTION_PROPOSED`, no `APPROVAL_REQUESTED`, and the framework's own primitive is never reached, because `@protect` never got far enough to raise `ApprovalRequired`. An adapter that @@ -772,7 +772,7 @@ def _denial_not_applicable(adapter: ConformanceAdapter) -> CaseResult: * **The executor ran** — `falsely-refuses-before-invoking`, which bypasses `Control` and calls the executor directly, so it leaves no events to catch it by. - * **CTRLRun was asked** — `falsely-refuses-after-asking`, which goes through `Control`, + * **ctrlrun was asked** — `falsely-refuses-after-asking`, which goes through `Control`, reaches the primitive, and only then produces its framework's refusal. """ world = World(adapter, APPROVE) @@ -807,7 +807,7 @@ def _denial_not_applicable(adapter: ConformanceAdapter) -> CaseResult: return failed( "B3", binding_denial.title, - f"{adapter.framework} declares refuses_before_invoking, but CTRLRun was asked: " + f"{adapter.framework} declares refuses_before_invoking, but ctrlrun was asked: " f"{asked or 'the framework primitive was reached'}. A framework that refuses before " "it invokes proposes no action at all, so the denial suite applies to this adapter " "and B3 must run", @@ -817,9 +817,9 @@ def _denial_not_applicable(adapter: ConformanceAdapter) -> CaseResult: "B3", binding_denial.title, f"{adapter.framework} refuses before it invokes: a tool whose approval was declined " - "is never called, so no CTRLRun action is proposed and there is nothing to deny. " - "The refusal is real and it is in the framework's own output; it is not in CTRLRun's " - "evidence log, because CTRLRun was never asked. Checked, not taken on trust: no action " + "is never called, so no ctrlrun action is proposed and there is nothing to deny. " + "The refusal is real and it is in the framework's own output; it is not in ctrlrun's " + "evidence log, because ctrlrun was never asked. Checked, not taken on trust: no action " "was proposed, the primitive was not reached and the executor did not run. " "The adapter's README says so", ) diff --git a/src/ctrlrun/control.py b/src/ctrlrun/control.py index 773d4d69..90f3684c 100644 --- a/src/ctrlrun/control.py +++ b/src/ctrlrun/control.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The @protect decorator, Control and the ambient action context. Build-list item 3. @@ -1743,7 +1743,7 @@ def _observed( held_key = None if reservation is None else effect_key attempt = 1 if reservation is None else reservation.attempt # SPEC-v0.7 §5.7 — the check, in observe mode: recorded and not enforced, because - # observe mode suppresses CTRLRun's decisions and not the record of an effect that + # observe mode suppresses ctrlrun's decisions and not the record of an effect that # happened. The fast path's half is recorded in `execute`, before this is reached. if reservation is not None and self._over_the_ceiling( self._policy.max_attempts(action.name), reservation.attempt @@ -1957,7 +1957,7 @@ def _observe_take( The **reservation is still taken**, and that asymmetry is deliberate. In observe mode the action genuinely executes, so the effect record has to exist or `v0.1 §5.4`'s - duplicate refusal has nothing to refuse with. What observe mode suppresses is CTRLRun's + duplicate refusal has nothing to refuse with. What observe mode suppresses is ctrlrun's *decisions*; it does not suppress the record of an effect that really happened. The verdict is computed with the same pure `check_consumable` every store applies, so @@ -3809,7 +3809,7 @@ def _upstream_reason(self, action: Action) -> str | None: **In-process there is no upstream to observe, so a pinned action is refused** `upstream_unverified` on every call (§4.4). That is loud, correct, and exactly what the - pin says the operator asked for: a pin is a claim about a server CTRLRun connects to, and + pin says the operator asked for: a pin is a claim about a server ctrlrun connects to, and in-process the executor is the operator's own code holding its own connection. """ from . import upstream as _upstream @@ -5304,7 +5304,7 @@ def _reject_variadic(signature: inspect.Signature, name: str) -> None: def _reject_reserved_parameters(signature: inspect.Signature, name: str) -> None: - """Refuse a protected function whose parameter is a name CTRLRun resolves itself. + """Refuse a protected function whose parameter is a name ctrlrun resolves itself. SPEC-v0.6 §7.4's table, first row: *"May an **argument** be called this? No."* An independent review found the set inert for `data_scope` -- `RESERVED_ARGUMENTS` was @@ -5330,7 +5330,7 @@ def _reject_reserved_parameters(signature: inspect.Signature, name: str) -> None if offending: raise InvalidArgument( f"protect({name!r}): {', '.join(repr(item) for item in offending)} is derived by " - "CTRLRun and may not be a parameter of a protected function. SPEC-v0.6 §7.4 " + "ctrlrun and may not be a parameter of a protected function. SPEC-v0.6 §7.4 " "resolves it at evaluation from the arguments actually supplied, so an argument of " "the same name would mean two things in one rule" ) diff --git a/src/ctrlrun/coverage.py b/src/ctrlrun/coverage.py index 119dea04..f0063a46 100644 --- a/src/ctrlrun/coverage.py +++ b/src/ctrlrun/coverage.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Enforcement coverage: what this deployment has never exercised. SPEC-v0.11 §7. diff --git a/src/ctrlrun/decision.py b/src/ctrlrun/decision.py index 8f33fcf9..0ab17f72 100644 --- a/src/ctrlrun/decision.py +++ b/src/ctrlrun/decision.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The decision vocabulary, below everything that produces or records one. diff --git a/src/ctrlrun/effect.py b/src/ctrlrun/effect.py index c86c83cf..38c19ffb 100644 --- a/src/ctrlrun/effect.py +++ b/src/ctrlrun/effect.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Effect keys and effect records. Build-list items 5 and 6; SPEC-v0.1 §5. diff --git a/src/ctrlrun/errors.py b/src/ctrlrun/errors.py index 8c7bd078..e2133ecd 100644 --- a/src/ctrlrun/errors.py +++ b/src/ctrlrun/errors.py @@ -1,10 +1,10 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Exception hierarchy for the public API. SPEC-v0.1 §8.""" class CTRLRunError(Exception): - """Base class for every error raised by CTRLRun.""" + """Base class for every error raised by ctrlrun.""" class InvalidArgument(CTRLRunError): @@ -172,7 +172,7 @@ class Suspended(CTRLRunError): outcome to record: the effect record stays `EXECUTING`, its lease is extended, the continuation is held, and the caller gets this back to relay. - `continuation` is whatever the remote said to present again. It is opaque here — CTRLRun + `continuation` is whatever the remote said to present again. It is opaque here — ctrlrun never parses it, and only ever compares it with `hmac.compare_digest`. """ diff --git a/src/ctrlrun/gateway/__init__.py b/src/ctrlrun/gateway/__init__.py index 7079dab8..771effed 100644 --- a/src/ctrlrun/gateway/__init__.py +++ b/src/ctrlrun/gateway/__init__.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The MCP gateway. Build-list item 6; SPEC-v0.2 §6. Ships in `ctrlrun[gateway]`. diff --git a/src/ctrlrun/gateway/legacy.py b/src/ctrlrun/gateway/legacy.py index 95f25603..e210838e 100644 --- a/src/ctrlrun/gateway/legacy.py +++ b/src/ctrlrun/gateway/legacy.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The 2025 revisions' transport mechanics, relayed without being interpreted. diff --git a/src/ctrlrun/gateway/mcp.py b/src/ctrlrun/gateway/mcp.py index 3e10298d..a4ed5692 100644 --- a/src/ctrlrun/gateway/mcp.py +++ b/src/ctrlrun/gateway/mcp.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Request parsing and header-body validation. Build-list item 6b; SPEC-v0.2 §6.2, §6.4, §6.6. @@ -325,7 +325,7 @@ def _agrees(declared: str, value: object) -> bool: Anything that is not a string, an integer or a boolean has **no** header encoding at all: the revision permits `x-mcp-header` only on those three, and says a `null` parameter omits its header entirely. So a header naming an argument of any other type cannot agree with it, - and the answer is `False` rather than a comparison against a rendering CTRLRun made up. + and the answer is `False` rather than a comparison against a rendering ctrlrun made up. """ if isinstance(value, str): return declared == value diff --git a/src/ctrlrun/gateway/operator.py b/src/ctrlrun/gateway/operator.py index 6ba35f33..2f985c06 100644 --- a/src/ctrlrun/gateway/operator.py +++ b/src/ctrlrun/gateway/operator.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The operator MCP server. SPEC-mcp-operator.md. @@ -801,7 +801,7 @@ def _initialize(self, parsed: ParsedRequest) -> dict[str, Any]: "capabilities": {"tools": {"listChanged": False}}, "serverInfo": {"name": SERVER_NAME, "version": _version()}, "instructions": ( - "CTRLRun operator console. The read tools show what is waiting and what " + "ctrlrun operator console. The read tools show what is waiting and what " "happened. approve, deny and resolve write, need an authenticated human, and " "record the answer under that person's name. Nothing here can make an agent " "act." diff --git a/src/ctrlrun/gateway/outcome.py b/src/ctrlrun/gateway/outcome.py index 1cf6c2da..7e5ee8a1 100644 --- a/src/ctrlrun/gateway/outcome.py +++ b/src/ctrlrun/gateway/outcome.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """SPEC-v0.2 §6.8, and nothing else. Build-list item 6b. diff --git a/src/ctrlrun/gateway/server.py b/src/ctrlrun/gateway/server.py index a80fbb46..85b60ff0 100644 --- a/src/ctrlrun/gateway/server.py +++ b/src/ctrlrun/gateway/server.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The gateway's HTTP server. Build-list item 6c; SPEC-v0.2 §6.1, §6.3, §6.5-§6.8, §6.10. @@ -133,7 +133,7 @@ UPSTREAM_UNPINNED: Final = (-41016, "ctrlrun.upstream_unpinned", 403) #: §6.8 — the `_meta` key every intercepted response carries, so a client is not left -#: guessing what CTRLRun recorded. `com.ctrlrun/` is a legal prefix under the revision's +#: guessing what ctrlrun recorded. `com.ctrlrun/` is a legal prefix under the revision's #: key-naming rules, and `_meta` on a result is not validated against a tool's outputSchema. RECEIPT_META_KEY: Final = "com.ctrlrun/receipt" @@ -410,7 +410,7 @@ def handle(self, body: bytes, headers: Mapping[str, str]) -> _Response: _LOG.warning("refused a request: %s", parsed.message) return self._refusal(parsed, _request_id(body)) if not parsed.intercept: - # §6.3 — every other method is relayed, and has no CTRLRun outcome at all. No + # §6.3 — every other method is relayed, and has no ctrlrun outcome at all. No # Action, no policy, no reservation, no receipt. `tools/list` is not an action. return self._relay(parsed, headers) return self._intercept(parsed, headers) @@ -653,7 +653,7 @@ def executor() -> Any: # pre-dispatch JSON-RPC code, or the `401` rule are then answers about *this* # leg's request and say nothing about what the upstream did with the original, # so the effect's state is unknown. The upstream's own response is still relayed - # unchanged (§6.8); only what CTRLRun records changes. + # unchanged (§6.8); only what ctrlrun records changes. outcome = replace( outcome, effect=EffectState.AMBIGUOUS, diff --git a/src/ctrlrun/gateway/transport.py b/src/ctrlrun/gateway/transport.py index b96ecf80..7c5c4a3a 100644 --- a/src/ctrlrun/gateway/transport.py +++ b/src/ctrlrun/gateway/transport.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """HTTP forwarding and incremental SSE decoding for the MCP gateway. diff --git a/src/ctrlrun/gateway/wire.py b/src/ctrlrun/gateway/wire.py index 87fa7653..be7e7557 100644 --- a/src/ctrlrun/gateway/wire.py +++ b/src/ctrlrun/gateway/wire.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """What both servers in this package speak, and neither owns. @@ -38,7 +38,7 @@ class _Response: def json_rpc_error(rpc_id: Any, code: int, token: str, message: str, **data: Any) -> dict[str, Any]: """One JSON-RPC error object in `v0.2 §6.10`'s shape. - A refusal by CTRLRun is not an outcome of the tool; it is the statement that the tool did + A refusal by ctrlrun is not an outcome of the tool; it is the statement that the tool did not run. `isError: true` would be indistinguishable from the tool's own failure, and it reaches the model as text — which is not where a policy denial belongs. """ diff --git a/src/ctrlrun/grammar.py b/src/ctrlrun/grammar.py index 34993b2d..0234c3c5 100644 --- a/src/ctrlrun/grammar.py +++ b/src/ctrlrun/grammar.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The policy document grammar: schemas, strict loading, and the condition evaluator. @@ -310,7 +310,7 @@ def construct_mapping( def strict_load(text: str, source: str) -> Any: # noqa: ANN401 - any YAML scalar or node - """`yaml.safe_load`, refusing a repeated key. The one loader for every CTRLRun document. + """`yaml.safe_load`, refusing a repeated key. The one loader for every ctrlrun document. **`yaml.YAMLError` is not the whole contract.** PyYAML converts a scalar before it has decided the document is well formed, and three conversions raise the interpreter's own @@ -414,7 +414,7 @@ def reject_nested_mode(mapping: Mapping[Any, Any], where: str) -> None: """Refuse a `mode:` anywhere but the top level of the policy document (SPEC-v0.3 §6.1). The closed key sets of `v0.1 §3.1` would already refuse it as unknown, wherever they - reach. This runs first and for its *message*: "unknown key 'mode'" reads as "CTRLRun has + reach. This runs first and for its *message*: "unknown key 'mode'" reads as "ctrlrun has no such setting", and the author who wrote it here believes they have observed one action while enforcing the rest. A partially-enforced configuration is the failure mode the top-level-only rule exists to prevent, so the error says which rule was broken. diff --git a/src/ctrlrun/identity.py b/src/ctrlrun/identity.py index fc3bb1b6..4e586356 100644 --- a/src/ctrlrun/identity.py +++ b/src/ctrlrun/identity.py @@ -1,8 +1,8 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Where a `Principal` comes from. Build-list item 1; SPEC-v0.3 §3. -CTRLRun **consumes** identities. It verifies what it is handed and maps the result onto a +ctrlrun **consumes** identities. It verifies what it is handed and maps the result onto a `Principal`; it issues nothing, mints nothing, and defines no identity format. That is the whole of this module's remit, and it is why the two providers here are so small: one asserts a fixed identity for development, the other reads a header a proxy was supposed to have set. diff --git a/src/ctrlrun/jwt_identity.py b/src/ctrlrun/jwt_identity.py index 0619cd10..f398167d 100644 --- a/src/ctrlrun/jwt_identity.py +++ b/src/ctrlrun/jwt_identity.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """JWT verification as an `IdentityProvider`. Build-list item 5; SPEC-v0.3 §3.4. @@ -7,7 +7,7 @@ installed the extra gets `MissingDependency` naming the install command rather than a `ModuleNotFoundError` from halfway down an import chain. -**CTRLRun consumes identities; it issues none.** This is not an OAuth client. It performs no +**ctrlrun consumes identities; it issues none.** This is not an OAuth client. It performs no authorization-code flow, no refresh, no token exchange, no introspection and no dynamic client registration. It verifies a token somebody else obtained and maps the verified claims onto a `Principal`. Everything beyond verification belongs to the deployment. diff --git a/src/ctrlrun/migrations.py b/src/ctrlrun/migrations.py index b9f212b8..8fd6e6a9 100644 --- a/src/ctrlrun/migrations.py +++ b/src/ctrlrun/migrations.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Schema version and the forward-only migration runner. Build-list item 2; SPEC-v0.6 §3. @@ -531,7 +531,7 @@ def _check_migration_ids() -> None: ctrlrun_version TEXT NOT NULL )""" -#: A table that means "this is a CTRLRun database from before v0.6". `effects` is the one table +#: A table that means "this is a ctrlrun database from before v0.6". `effects` is the one table #: every release since v0.1 has had. _MARKER_TABLE: Final = "effects" @@ -550,7 +550,7 @@ def _check_migration_ids() -> None: } _PLACEHOLDER: Final = {"sqlite": "?", "postgres": "%s"} -#: The advisory-lock key every CTRLRun migration serialises on. Constant, so two processes +#: The advisory-lock key every ctrlrun migration serialises on. Constant, so two processes #: migrating one database wait for each other; an advisory lock is scoped to the database it is #: taken in, so processes migrating different databases do not. _MIGRATION_LOCK: Final = 0x43545252554E @@ -592,17 +592,17 @@ def classify(applied: tuple[str, ...]) -> Classification: return Classification.GAPPED -#: What an `effects` table must have for a database to be CTRLRun's. Not the whole schema: a +#: What an `effects` table must have for a database to be ctrlrun's. Not the whole schema: a #: v0.1 database has fewer tables than a v0.5 one, and the point is to tell *ours* from #: *somebody else's*, not to re-derive the version from the columns (§3.1). _EFFECTS_COLUMNS: Final = frozenset({"effect_key", "state", "action_id", "attempt"}) def _refuse_unless_ours(connection: Any, tables: set[str], dialect: str) -> None: - """Adopt a pre-v0.6 database only if its `effects` table is actually CTRLRun's (§3.2). + """Adopt a pre-v0.6 database only if its `effects` table is actually ctrlrun's (§3.2). `effects` is a plausible name in somebody else's schema, and a `$CTRLRUN_STATE` typo is a - plausible way to arrive at one. Keying adoption on the name alone meant CTRLRun created + plausible way to arrive at one. Keying adoption on the name alone meant ctrlrun created `schema_version`, `approvals`, `receipts`, `events`, `delegations`, `continuations` and `receipt_chain` **inside the operator's other database**, recorded both migrations, opened cleanly, and failed at first use with `no such column: effect_key` -- which is after @@ -612,9 +612,9 @@ def _refuse_unless_ours(connection: Any, tables: set[str], dialect: str) -> None missing = _EFFECTS_COLUMNS - columns if missing: raise _refuse( - f"this database has a table named {_MARKER_TABLE!r} that is not CTRLRun's: it is " + f"this database has a table named {_MARKER_TABLE!r} that is not ctrlrun's: it is " f"missing {', '.join(sorted(missing))}. It holds {', '.join(sorted(tables))}. " - "Creating CTRLRun's tables in somebody else's database is not a recovery.", + "Creating ctrlrun's tables in somebody else's database is not a recovery.", (), ) @@ -768,8 +768,8 @@ def _migrate_locked(connection: Any, stamp: datetime, dialect: str) -> Classific found = Classification.BASELINE elif tables: raise _refuse( - f"This database has no CTRLRun schema and is not empty: it holds " - f"{', '.join(sorted(tables))}. Creating CTRLRun's tables in somebody else's " + f"This database has no ctrlrun schema and is not empty: it holds " + f"{', '.join(sorted(tables))}. Creating ctrlrun's tables in somebody else's " "database is not a recovery.", (), ) @@ -789,7 +789,7 @@ def _migrate_locked(connection: Any, stamp: datetime, dialect: str) -> Classific missing = tuple(item for item in known if item not in applied) reasons = { Classification.BACKWARD: ( - "This database was written by a newer build of CTRLRun and records a migration " + "This database was written by a newer build of ctrlrun and records a migration " "this one does not know." ), Classification.DIVERGENT: ( diff --git a/src/ctrlrun/otel.py b/src/ctrlrun/otel.py index b249916a..5b26861e 100644 --- a/src/ctrlrun/otel.py +++ b/src/ctrlrun/otel.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The OpenTelemetry sink. Build-list item 8; SPEC-v0.2 §8. Ships in `ctrlrun[otel]`. diff --git a/src/ctrlrun/policy.py b/src/ctrlrun/policy.py index 4e7848fe..4b0b5af5 100644 --- a/src/ctrlrun/policy.py +++ b/src/ctrlrun/policy.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Policy loading and rule evaluation to a Decision. Build-list item 2; SPEC-v0.1 §3. @@ -225,7 +225,7 @@ def _refuse_reserved(names: Iterable[str], where: str, what: str) -> None: offending = sorted(name for name in names if name in DERIVED_SUBJECTS) if offending: raise PolicyError( - f"{where}: {', '.join(repr(name) for name in offending)} is derived by CTRLRun and " + f"{where}: {', '.join(repr(name) for name in offending)} is derived by ctrlrun and " f"may not be {what}. §7.4 resolves it at evaluation from the arguments actually " "supplied, so an argument of the same name would mean two things in one rule; " "rename the argument" @@ -298,7 +298,7 @@ def matches( class DataLabel: """What class of data one argument carries (SPEC-v0.6 §7.4). - `label` is the operator's own word -- `phi`, `internal`, `pci`. CTRLRun does not know what + `label` is the operator's own word -- `phi`, `internal`, `pci`. ctrlrun does not know what any of them mean; it derives the *set* present in an action's arguments so a rule can see it. **There is no `redact`.** §7.4 put it on probation and §7.5's throwaway sector configuration @@ -320,7 +320,7 @@ class PolicyControl: as a name nobody should have to disambiguate at a call site, and which §9.1 would have frozen for a long time. Renamed in the same change that adds it to §9.1.1's list. - **CTRLRun does not interpret a control.** `source:` is a string the operator wrote. The + **ctrlrun does not interpret a control.** `source:` is a string the operator wrote. The kernel does not know what PCI DSS is, does not check the clause exists, and makes no compliance, conformance or alignment claim on the strength of one -- validating a citation would be the beginning of interpreting it. @@ -335,7 +335,7 @@ class PolicyControl: title: str source: str | None = None #: SPEC-v0.8 §3.2 — which role may answer an approval this control was cited on. An opaque - #: string: CTRLRun does not know what it means, does not check that such a role exists, and + #: string: ctrlrun does not know what it means, does not check that such a role exists, and #: makes no compliance claim on the strength of one, exactly as it does not interpret #: `source`. What it does is decide **who may answer an approval the decision already #: required**, which is the first thing a control has ever decided (§3.2). @@ -892,7 +892,7 @@ def _plain(value: object) -> PlainValue: # `yaml.safe_load` turns an unquoted `expires_at: 2020-01-01T00:00:00Z` into a # `datetime`, and such documents load today -- a grant with an expiry is the ordinary # case. ISO-8601 is what the same value would have been had it been quoted, and what - # CTRLRun writes everywhere else, so this loses nothing and invents nothing. + # ctrlrun writes everywhere else, so this loses nothing and invents nothing. # # An earlier version of this function *refused* here, which broke every authority # document with an unquoted expiry. The conformance kit's own `EXPIRED_GRANT` caught it. @@ -1498,7 +1498,7 @@ def _parse_mcp(value: object, where: str) -> McpOptions: _reject_unknown_keys(value, _MCP_KEYS, f"{where}: mcp") claimed = value.get("not_executed_on_error", False) # SPEC-v0.2 §3.1 — a bool, and `1` is not one. This is an assertion about a remote that - # CTRLRun cannot check (§6.8), so it is made deliberately or not at all. + # ctrlrun cannot check (§6.8), so it is made deliberately or not at all. if not isinstance(claimed, bool): raise PolicyError( f"{where}: mcp: 'not_executed_on_error' must be true or false, " diff --git a/src/ctrlrun/postgres.py b/src/ctrlrun/postgres.py index 097f77aa..e4490e1a 100644 --- a/src/ctrlrun/postgres.py +++ b/src/ctrlrun/postgres.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """`PostgresStateStore`. Build-list item 3; SPEC-v0.6 §4. @@ -560,7 +560,7 @@ def _refuse_without_ddl_rights(self, connection: Any) -> None: schema, user = (str(row[0]) if row and row[0] else None), str(row[1]) if row else "?" if schema is None: raise InvalidArgument( - f"schema {self._schema!r} does not exist, or {user} cannot see it. CTRLRun " + f"schema {self._schema!r} does not exist, or {user} cannot see it. ctrlrun " "creates no schema for an operator: create it, or point --store-url at one " "that exists (SPEC-v0.6 §4.1)" ) @@ -569,7 +569,7 @@ def _refuse_without_ddl_rights(self, connection: Any) -> None: if not (allowed and allowed[0]): raise InvalidArgument( f"the database user {user!r} has no CREATE privilege on schema {schema!r}, so " - "CTRLRun cannot apply its migrations. A store is opened un-migrated by nothing " + "ctrlrun cannot apply its migrations. A store is opened un-migrated by nothing " "(SPEC-v0.6 §3.6), so this is refused at open rather than discovered at the " f'first write. Grant it with: GRANT CREATE ON SCHEMA "{schema}" TO "{user}"' ) @@ -603,7 +603,7 @@ def _connect(self) -> Any: # keys and both execute. That is a double execution reached through the storage # layer's character set, so it is refused at open rather than discovered. raise InvalidArgument( - f"this database's server_encoding is {encoding}, not UTF8. CTRLRun hashes " + f"this database's server_encoding is {encoding}, not UTF8. ctrlrun hashes " "the exact code points it is given (v0.1 §2.3), so a lossy encoding makes " "one logical effect into two identities and both would execute" ) diff --git a/src/ctrlrun/receipt.py b/src/ctrlrun/receipt.py index 376a3c82..f9e4cda5 100644 --- a/src/ctrlrun/receipt.py +++ b/src/ctrlrun/receipt.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Receipts and the event log. Build-list item 8; SPEC-v0.1 §6. @@ -290,7 +290,7 @@ class EventType(StrEnum): EXECUTION_RESUMED = "EXECUTION_RESUMED" #: SPEC-v0.3 §7 — the five types authority and delegation add. `AUTHORITY_RESOLVED` is #: appended for *every* action that passes authority, not only for a delegated one: - #: evidence has to record that CTRLRun checked and found a grant, or a deployment with a + #: evidence has to record that ctrlrun checked and found a grant, or a deployment with a #: permissive grant is indistinguishable from one with no `authority:` section at all. #: The three `DELEGATION_*` types are produced by `Control.delegate` and `Control.revoke`, #: which land with build-list item 3; the vocabulary is closed here so a reader of an @@ -342,7 +342,7 @@ def to_dict(self) -> dict[str, Any]: } def to_json(self) -> str: - """One JSONL line. Enums render by value, for readers that never imported CTRLRun.""" + """One JSONL line. Enums render by value, for readers that never imported ctrlrun.""" return json.dumps(self.to_dict(), ensure_ascii=False, separators=(",", ":")) diff --git a/src/ctrlrun/reporting.py b/src/ctrlrun/reporting.py index ebfc3495..b3d7feff 100644 --- a/src/ctrlrun/reporting.py +++ b/src/ctrlrun/reporting.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The portable documents `ctrlrun inspect --json` and `ctrlrun stats --json` produce. diff --git a/src/ctrlrun/retention.py b/src/ctrlrun/retention.py index 650a1626..4bc53c19 100644 --- a/src/ctrlrun/retention.py +++ b/src/ctrlrun/retention.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Retention: a prune that leaves the chain verifiable, a checkpoint, and a hold. diff --git a/src/ctrlrun/revocation.py b/src/ctrlrun/revocation.py index 31c5ac36..436e86fb 100644 --- a/src/ctrlrun/revocation.py +++ b/src/ctrlrun/revocation.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Credential revocation, consumed (SPEC-v0.8 §6). diff --git a/src/ctrlrun/scan.py b/src/ctrlrun/scan.py index 85b6293d..c6eff5d9 100644 --- a/src/ctrlrun/scan.py +++ b/src/ctrlrun/scan.py @@ -1,8 +1,8 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """`ctrlrun scan` — what a project is not covering. `docs/SPEC-scan.md`. -Scan reads text. It reports the consequential call sites and policy entries CTRLRun is not +Scan reads text. It reports the consequential call sites and policy entries ctrlrun is not covering, so that an operator can see the gap between *installed* and *in the path*. It is a finder and not a proof: §4 of the specification enumerates what it misses by construction, and `report_lines` prints that on every run, including the run with no findings. @@ -192,12 +192,12 @@ class ScanReport: policy_read: bool #: SPEC-v0.10 §6.4 — the principals holding a grant no hop bounds, in codepoint order. #: - #: §2.3.2's residual is that CTRLRun cannot make a receiving agent present the hop it was + #: §2.3.2's residual is that ctrlrun cannot make a receiving agent present the hop it was #: given: one holding a grant of its own can decline and act on that instead. The deployment #: rule that collapses it is *an agent that only ever acts on handed-over work holds no root #: grant of its own*, and without a surface that rule is advice. This is the surface. #: - #: **It reports and does not score.** `v0.4 §3.9`'s rule that CTRLRun never grades an + #: **It reports and does not score.** `v0.4 §3.9`'s rule that ctrlrun never grades an #: operator's document holds here: a principal on this line is a fact, not a finding, and it #: does not move `exit_code`. root_grant_holders: tuple[str, ...] = () diff --git a/src/ctrlrun/state.py b/src/ctrlrun/state.py index 0a5f4c22..2bcd420a 100644 --- a/src/ctrlrun/state.py +++ b/src/ctrlrun/state.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """StateStore protocol, SQLite and in-memory stores. Build-list item 6; SPEC-v0.1 §5.3. @@ -152,7 +152,7 @@ def _require_sqlite() -> None: # error set has no member for "the environment is too old", and `MissingDependency` # renders a fixed "it ships in the X extra" sentence that would be false here. raise InvalidArgument( - f"this Python is linked against SQLite {sqlite3.sqlite_version}, and CTRLRun needs " + f"this Python is linked against SQLite {sqlite3.sqlite_version}, and ctrlrun needs " f"{wanted} or newer: the receipt chain is written with `UPDATE ... RETURNING`, which " f"older SQLite cannot parse. Upgrade the system SQLite, use a Python built against a " f"newer one, or run the Postgres backend (pip install 'ctrlrun[postgres]')." @@ -1664,7 +1664,7 @@ def __init__( # at the wrong file is an ordinary misconfiguration and deserves an ordinary # refusal. raise InvalidArgument( - f"{str(self._path)!r} is not a CTRLRun state database: {exc}. Point " + f"{str(self._path)!r} is not a ctrlrun state database: {exc}. Point " "$CTRLRUN_STATE or --store-url at the database your agents write, or let " "the agent process create one." ) from exc diff --git a/src/ctrlrun/transport.py b/src/ctrlrun/transport.py index 0b173168..437526e5 100644 --- a/src/ctrlrun/transport.py +++ b/src/ctrlrun/transport.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The `NotExecuted` classifier for `http.client` and `urllib`. SPEC-v0.7 §2. diff --git a/src/ctrlrun/upstream.py b/src/ctrlrun/upstream.py index 81db9f81..56e12223 100644 --- a/src/ctrlrun/upstream.py +++ b/src/ctrlrun/upstream.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """What was observed about an upstream, and whether it matches the pin. SPEC-v0.10 §4. diff --git a/src/ctrlrun/verify/__init__.py b/src/ctrlrun/verify/__init__.py index db40bf38..5243ae76 100644 --- a/src/ctrlrun/verify/__init__.py +++ b/src/ctrlrun/verify/__init__.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """`ctrlrun verify` — the operator's own configuration, against the kernel's own refusals. @@ -173,7 +173,7 @@ def run( chosen = (store_url or SQLITE_STORE_URL).strip() or SQLITE_STORE_URL if chosen != SQLITE_STORE_URL and not chosen.startswith(("postgresql://", "postgres://")): raise VerifyRefused( - f"--store-url {store_url!r} names a backend CTRLRun does not have. The values are " + f"--store-url {store_url!r} names a backend ctrlrun does not have. The values are " f"{SQLITE_STORE_URL!r} and a postgresql:// URL (SPEC-v0.6 §4.1)" ) if chosen != SQLITE_STORE_URL: diff --git a/src/ctrlrun/verify/guarantees.py b/src/ctrlrun/verify/guarantees.py index 9a2a4b7c..dd550697 100644 --- a/src/ctrlrun/verify/guarantees.py +++ b/src/ctrlrun/verify/guarantees.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The closed guarantee catalogue, `ctrlrun.guarantees/v1`. SPEC-v0.4 §2. diff --git a/src/ctrlrun/verify/report.py b/src/ctrlrun/verify/report.py index 98d467de..2ef70b73 100644 --- a/src/ctrlrun/verify/report.py +++ b/src/ctrlrun/verify/report.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The report and its three renderings. SPEC-v0.4 §4. @@ -7,7 +7,7 @@ and so a caller embedding verify in their own tool reaches the same answer the command does. Every enum renders **by value** (§4, T117), which is `v0.1 §6.1`'s rule applied to a new -document: evidence has to be readable by something that never imported CTRLRun. +document: evidence has to be readable by something that never imported ctrlrun. """ from __future__ import annotations @@ -34,7 +34,7 @@ #: failures, and the N/A count lives in the report the badge links to. BADGE_PASS_COLOR: Final = "brightgreen" BADGE_FAIL_COLOR: Final = "red" -BADGE_LABEL: Final = "CTRLRun" +BADGE_LABEL: Final = "ctrlrun" _TITLE_WIDTH: Final = 32 @@ -229,7 +229,7 @@ def to_json(self) -> str: def to_text(self) -> str: """The human report of §4.1. The summary is the last line, so `tail -1` means something.""" lines = [ - f"CTRLRun verify — ctrlrun {self.ctrlrun_version}, catalogue {CATALOGUE}", + f"ctrlrun verify — ctrlrun {self.ctrlrun_version}, catalogue {CATALOGUE}", f"policy {self.policy['path']} ({self.policy['schema']}, " f"mode: {self.policy['mode']})", ] @@ -422,7 +422,7 @@ def summary_from_document(document: Mapping[str, Any]) -> str: """ policy = document["policy"] lines = [ - f"### CTRLRun verify - `{policy['path']}`", + f"### ctrlrun verify - `{policy['path']}`", "", f"{policy['schema']}, mode `{policy['mode']}`, {policy['actions']} actions, " f"catalogue `{document['catalogue']}`, ctrlrun {document['ctrlrun_version']}", diff --git a/src/ctrlrun/verify/scenarios.py b/src/ctrlrun/verify/scenarios.py index 42e4a794..a2454ac0 100644 --- a/src/ctrlrun/verify/scenarios.py +++ b/src/ctrlrun/verify/scenarios.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Deriving scenarios from a configuration, and running them. SPEC-v0.4 §3. @@ -1946,7 +1946,7 @@ def g5(self) -> GuaranteeResult: def body(detail: dict[str, Any]) -> None: # The control carries more weight than any other in the catalogue: it is the only - # thing separating "CTRLRun blocks blind retries" from "CTRLRun blocks retries", + # thing separating "ctrlrun blocks blind retries" from "ctrlrun blocks retries", # and the second sentence describes a library nobody can deploy (§2.2). control_key = f"{selection.effect_key!s}-{reg.SYNTHETIC_PREFIX}-control" seen: list[int] = [] @@ -5430,7 +5430,7 @@ class _VerifyAnchorProvider: In memory, and deliberately the simplest thing that satisfies the protocol: it records what it was asked to vouch for and answers about it. It is **not** a timestamp authority and does - not pretend to be one. What `G28` grades is that CTRLRun asks the right questions of whatever + not pretend to be one. What `G28` grades is that ctrlrun asks the right questions of whatever the operator supplies and refuses on the right answers, exactly as `G23` grades a scope provider verify supplies rather than one it found. diff --git a/src/ctrlrun/verify/worker.py b/src/ctrlrun/verify/worker.py index bb172baa..63c11b8c 100644 --- a/src/ctrlrun/verify/worker.py +++ b/src/ctrlrun/verify/worker.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """G4's process target. SPEC-v0.4 §2.2, §3.6. diff --git a/src/ctrlrun/webhook.py b/src/ctrlrun/webhook.py index 93bc93e6..f5c255d0 100644 --- a/src/ctrlrun/webhook.py +++ b/src/ctrlrun/webhook.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The webhook approval provider. Build-list item 7; SPEC-v0.2 §7. @@ -186,7 +186,7 @@ def request(self, action: Action, ttl: timedelta = DEFAULT_APPROVAL_TTL) -> Appr """Record the request, then tell somebody. The record is written first. A notification for a request the store never accepted would point at nothing, and a - human who answered it would be answering a question CTRLRun cannot connect to an + human who answered it would be answering a question ctrlrun cannot connect to an action. """ request = self._local.request(action, ttl) diff --git a/tests/conftest.py b/tests/conftest.py index 8d961e48..8ef666b3 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Fixtures shared across the suite. diff --git a/tests/failure_injection.py b/tests/failure_injection.py index fae46b3a..308ede93 100644 --- a/tests/failure_injection.py +++ b/tests/failure_injection.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """A Postgres connection an experiment can break on purpose. Build-list item 4; SPEC-v0.6 §4.5. diff --git a/tests/test_acs.py b/tests/test_acs.py index ef612585..a25f5efd 100644 --- a/tests/test_acs.py +++ b/tests/test_acs.py @@ -1,9 +1,9 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The ACS control hook. SPEC-v0.2 §9 (amended), acceptance tests T51-T55. ACS is an *advisory* interface: a Guardian returns a decision and the platform executes. That -is the opposite way round from `@protect`, where CTRLRun runs the executor — so the adapter +is the opposite way round from `@protect`, where ctrlrun runs the executor — so the adapter splits one action across two hooks. `steps/toolCallRequest` decides and takes the reservation; `steps/toolCallResult` closes it with what actually happened. @@ -223,7 +223,7 @@ def test_T52_a_denied_call_returns_deny_with_reasoning(hook, store): def test_T52_an_action_needing_a_human_returns_ask_with_ask_details(hook, store): - """ACS's `ask` is CTRLRun's APPROVE. ask-details.json requires approver, question and + """ACS's `ask` is ctrlrun's APPROVE. ask-details.json requires approver, question and timeout_seconds, so all three are present or the response is not conformant.""" response = hook.handle(_call(amount=200000)) @@ -352,7 +352,7 @@ def test_T54_failure_is_FAILED_only_where_the_operator_asserted_it(hook, store): def test_T54_the_result_hook_returns_allow_because_it_redacts_nothing(hook, store): - """ACS describes toolCallResult as an output redaction checkpoint. CTRLRun records the + """ACS describes toolCallResult as an output redaction checkpoint. ctrlrun records the outcome and changes no output, so the conformant answer is `allow`.""" _, response = _both(hook) @@ -798,7 +798,7 @@ def test_an_unmeasurable_budget_is_denied_and_not_called_a_malformed_envelope(st disagreeing. An error envelope tells the platform the Guardian could not answer, which it may act on - however it likes. A `deny` tells it what CTRLRun means, which is that the tool must not run. + however it likes. A `deny` tells it what ctrlrun means, which is that the tool must not run. The `IdentityError` clause immediately below states this rule for its own case. """ from ctrlrun import HeaderIdentityProvider diff --git a/tests/test_action.py b/tests/test_action.py index e45653b6..426adaec 100644 --- a/tests/test_action.py +++ b/tests/test_action.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Action model, canonicalization and action_hash. SPEC-v0.1 §2; acceptance test T7.""" diff --git a/tests/test_adapter.py b/tests/test_adapter.py index 313f0444..d2e3519a 100644 --- a/tests/test_adapter.py +++ b/tests/test_adapter.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The adapter surface. SPEC-v0.5 §2, §3, §4; T126-T129h. diff --git a/tests/test_adapters_langchain.py b/tests/test_adapters_langchain.py index 16212a35..8dd8b97f 100644 --- a/tests/test_adapters_langchain.py +++ b/tests/test_adapters_langchain.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The LangChain middleware adapter. SPEC-v0.5 §6, §7. diff --git a/tests/test_adapters_langgraph.py b/tests/test_adapters_langgraph.py index 12389c48..a5d0973f 100644 --- a/tests/test_adapters_langgraph.py +++ b/tests/test_adapters_langgraph.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The LangGraph reference adapter. SPEC-v0.5 §3.5, §6, §7; T135, T135b, T137. diff --git a/tests/test_adapters_openai_agents.py b/tests/test_adapters_openai_agents.py index 7c189813..7f896657 100644 --- a/tests/test_adapters_openai_agents.py +++ b/tests/test_adapters_openai_agents.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The OpenAI Agents SDK reference adapter. SPEC-v0.5 §3.5, §6, §7; T135, T135b, T137. @@ -182,10 +182,10 @@ def test_rejecting_through_the_sdk_never_reaches_ctrlrun_at_all(): """§7's "where the framework's behaviour is visible through the contract", and the one place this adapter's evidence differs from `@protect`'s. - The SDK does not invoke a tool whose approval was refused, so no CTRLRun action is ever + The SDK does not invoke a tool whose approval was refused, so no ctrlrun action is ever proposed: there is no `ACTION_DENIED`, no `APPROVAL_DENIED` and no receipt. The refusal is - real and it is in the SDK's own output; it is simply not in CTRLRun's evidence log, because - CTRLRun was never asked about it. The README says so rather than leaving an operator to + real and it is in the SDK's own output; it is simply not in ctrlrun's evidence log, because + ctrlrun was never asked about it. The README says so rather than leaving an operator to discover an empty log. """ control, store = build() @@ -256,7 +256,7 @@ class AgentsConformanceAdapter: framework = "openai-agents" #: SPEC-v0.5 §5.2. This SDK does not invoke a tool whose approval was refused, so a - #: rejection proposes no CTRLRun action and there is nothing to deny. The `denial` suite is + #: rejection proposes no ctrlrun action and there is nothing to deny. The `denial` suite is #: `not_applicable` with that reason, and never a pass. refuses_before_invoking = True @@ -311,7 +311,7 @@ def _agent(self, request: CallRequest): import json # `protected_tool` and not a bare `function_tool`: it sets `failure_error_function=None` - # so a CTRLRun refusal reaches the caller instead of the model (§12.6). Without it every + # so a ctrlrun refusal reaches the caller instead of the model (§12.6). Without it every # `kernel` case reports "did not raise", which is what this SDK's default does to an # exception -- and it is why the helper exists. tool = protected_tool( @@ -329,7 +329,7 @@ def _returned(result: Any) -> Any: """What the executor returned, out of the SDK's run output. The SDK reports a tool that raised through `failure_error_function` rather than propagating, - so a CTRLRun refusal would be swallowed into a model-readable message -- which is precisely + so a ctrlrun refusal would be swallowed into a model-readable message -- which is precisely what the kit must see. The tool here is built without one, so the exception propagates out of `Runner.run`. """ @@ -344,7 +344,7 @@ def test_T135_the_agents_adapter_passes_the_conformance_kit(): def test_T135_a_refusal_before_invocation_makes_the_denial_suite_not_applicable(): """The second thing this adapter forced into the contract. A framework that never invokes a - refused tool proposes no CTRLRun action, so there is nothing to deny -- and a `denial` case + refused tool proposes no ctrlrun action, so there is nothing to deny -- and a `denial` case sitting inside `kernel` would have reported `pass` for an adapter that cannot exercise it.""" report = run(AgentsConformanceAdapter()) @@ -822,7 +822,7 @@ async def tool_body(payment_id: str, amount: int = 100000) -> str: agent = Agent(name="refunds", instructions="You handle refunds.", tools=[tool], model=model) # The refusal reaches the caller rather than the model: `protected_tool` sets - # `failure_error_function=None` so a CTRLRun refusal is not turned into text an agent can + # `failure_error_function=None` so a ctrlrun refusal is not turned into text an agent can # retry against (§12.7). `unwrap` gives back what was actually raised. with pytest.raises(Exception) as raised: Runner.run_sync(agent, "refund txn_1") @@ -969,7 +969,7 @@ def test_a_sticky_always_approve_is_not_an_answer_for_a_later_call(): that tool (`run_context.py`: `if approval_entry.approved is True: return True`, reached after the exact-call lookup misses). - A human who said *always approve refunds* has not seen this refund. CTRLRun's entire claim + A human who said *always approve refunds* has not seen this refund. ctrlrun's entire claim is that an approval binds to one action -- `v0.1 §4.2` consumes a grant against an `action_hash` -- and a blanket yes for a tool is the thing that claim exists to refuse. With `carries_approved_arguments=False` there is no binding check in core to catch it either, so @@ -1186,7 +1186,7 @@ def test_observe_mode_never_interrupts_and_never_blocks(caplog): The consequence is worse than the rule it breaks: a framework of this shape does not invoke a tool whose approval was declined, so a human's *no* under `mode: observe` **stops the action** -- and observe mode's whole promise is that every decision is recorded and none is - enforced. A deployment evaluating CTRLRun in the mode built for evaluating it had its agent + enforced. A deployment evaluating ctrlrun in the mode built for evaluating it had its agent halted. """ import logging diff --git a/tests/test_anchor.py b/tests/test_anchor.py index 5bbacfa8..f7b42b28 100644 --- a/tests/test_anchor.py +++ b/tests/test_anchor.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The anchor, and the two things it makes detectable. SPEC-v0.11 §2, §3; T530-T541. @@ -575,7 +575,7 @@ def test_T535b_a_checkpoint_anchor_is_ordered_only_against_other_checkpoints(tmp def test_T535c_an_anchor_whose_time_runs_backwards_is_refused(tmp_path) -> None: """§3.2. A monotonic sequence is the only property the kernel can check about a timestamp it did not issue, and a sequence that goes backwards is either a misconfiguration or the attack; - CTRLRun cannot tell which, so it refuses.""" + ctrlrun cannot tell which, so it refuses.""" database = tmp_path / "state.db" store = a_chain(database, 3) provider = Provider() @@ -650,7 +650,7 @@ def test_T536_the_anchor_module_issues_nothing() -> None: # And the time an anchor carries is the provider's, never this process's clock. assert "datetime.now" not in source, ( "the anchor read a clock of its own. An anchor's time must be the provider's: a time " - "CTRLRun generated would be CTRLRun vouching for itself" + "ctrlrun generated would be ctrlrun vouching for itself" ) diff --git a/tests/test_approval.py b/tests/test_approval.py index 9bdf1e20..1f28f887 100644 --- a/tests/test_approval.py +++ b/tests/test_approval.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Approval binding: request, grant, consume. SPEC-v0.1 §4; acceptance tests T2, T4, T5.""" diff --git a/tests/test_approver.py b/tests/test_approver.py index 10f50442..006d1d20 100644 --- a/tests/test_approver.py +++ b/tests/test_approver.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """T281 to T296: the approver is a principal (SPEC-v0.8 §2, §4.1). diff --git a/tests/test_attempt_cap.py b/tests/test_attempt_cap.py index 93a8e35e..ad786021 100644 --- a/tests/test_attempt_cap.py +++ b/tests/test_attempt_cap.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The attempt ceiling, `max_attempts`. Build-list item 4; SPEC-v0.7 §5, §8.4 (T240 to T252). diff --git a/tests/test_attempt_integrity.py b/tests/test_attempt_integrity.py index 0d024836..589fb77e 100644 --- a/tests/test_attempt_integrity.py +++ b/tests/test_attempt_integrity.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Attempt numbers never repeat, on Postgres. Item 3a; SPEC-v0.7 §5.6, §8.3a (T246, T246b). diff --git a/tests/test_authority.py b/tests/test_authority.py index e4132845..f982c692 100644 --- a/tests/test_authority.py +++ b/tests/test_authority.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The `authority:` section, grants and evaluation. Build-list item 2; SPEC-v0.3 §4. diff --git a/tests/test_break_glass.py b/tests/test_break_glass.py index b342c43f..b772ec4b 100644 --- a/tests/test_break_glass.py +++ b/tests/test_break_glass.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """T326 to T339: break-glass as a grant, never a flag (SPEC-v0.8 §5). diff --git a/tests/test_budget_document.py b/tests/test_budget_document.py index 5d431e02..c03c68ec 100644 --- a/tests/test_budget_document.py +++ b/tests/test_budget_document.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """T399 to T407: the budget in the document (SPEC-v0.9 §2). diff --git a/tests/test_budget_holds.py b/tests/test_budget_holds.py index b6aae5e5..d42078c1 100644 --- a/tests/test_budget_holds.py +++ b/tests/test_budget_holds.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """T415 to T443: consumption, reconciliation and release (SPEC-v0.9 §4). diff --git a/tests/test_cli_store.py b/tests/test_cli_store.py index a045ba5d..c6f16b24 100644 --- a/tests/test_cli_store.py +++ b/tests/test_cli_store.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """How the read commands open a store, and what they say when they cannot. diff --git a/tests/test_clock_skew.py b/tests/test_clock_skew.py index f2baf8dd..a383a0ef 100644 --- a/tests/test_clock_skew.py +++ b/tests/test_clock_skew.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Clock-skew detection. Build-list item 1 of v0.7; SPEC-v0.7 §3, §8.1 T209-T219. diff --git a/tests/test_concurrency.py b/tests/test_concurrency.py index 72d87f38..9164ec0f 100644 --- a/tests/test_concurrency.py +++ b/tests/test_concurrency.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Atomic reservation across processes. Build-list item 6; SPEC-v0.1 §5.3; T3, T12. diff --git a/tests/test_conformance.py b/tests/test_conformance.py index 7870c8ee..6ea519f0 100644 --- a/tests/test_conformance.py +++ b/tests/test_conformance.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The conformance kit. SPEC-v0.5 §5; T130-T134b. diff --git a/tests/test_cookbook.py b/tests/test_cookbook.py index 169c5c7d..964d6cea 100644 --- a/tests/test_cookbook.py +++ b/tests/test_cookbook.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The cookbook: every recipe runs, twice, offline, and refuses something. diff --git a/tests/test_coverage.py b/tests/test_coverage.py index 6b57d6dd..1527e0b0 100644 --- a/tests/test_coverage.py +++ b/tests/test_coverage.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Enforcement coverage, from what is already written. SPEC-v0.11 §7; T560-T566. diff --git a/tests/test_cross_host.py b/tests/test_cross_host.py index 91f25bf6..1826ab15 100644 --- a/tests/test_cross_host.py +++ b/tests/test_cross_host.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Concurrency and failure injection against a real Postgres. Item 4; SPEC-v0.6 §4.5, §8. diff --git a/tests/test_decision_order.py b/tests/test_decision_order.py index 9bf72963..0e5e78d0 100644 --- a/tests/test_decision_order.py +++ b/tests/test_decision_order.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """SPEC-v0.10 §5, item 4: one declared order, walked by both modes. diff --git a/tests/test_delegation.py b/tests/test_delegation.py index 3e98cb47..06705e67 100644 --- a/tests/test_delegation.py +++ b/tests/test_delegation.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Delegation with attenuation. Build-list item 3; SPEC-v0.3 §5. diff --git a/tests/test_demo.py b/tests/test_demo.py index 9acc7a37..fd54750f 100644 --- a/tests/test_demo.py +++ b/tests/test_demo.py @@ -1,8 +1,8 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Receipts on disk, the CLI and `ctrlrun demo`. SPEC-v0.1 §6, §8; acceptance tests T10, T11. -The JSONL evidence is the half of §6 a reader outside CTRLRun ever sees, so these tests read +The JSONL evidence is the half of §6 a reader outside ctrlrun ever sees, so these tests read the files rather than the store wherever the spec names a file. """ diff --git a/tests/test_effect.py b/tests/test_effect.py index acff4ff3..3d460494 100644 --- a/tests/test_effect.py +++ b/tests/test_effect.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Effect key templating. Build-list item 5; SPEC-v0.1 §5.1. diff --git a/tests/test_elicitation.py b/tests/test_elicitation.py index 9541e3e9..968a90b2 100644 --- a/tests/test_elicitation.py +++ b/tests/test_elicitation.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Held reservations across an elicitation. Build-list item 6d; SPEC-v0.2 §6.9, §6.2. diff --git a/tests/test_entitlement.py b/tests/test_entitlement.py index 45fbb7d7..c465dd31 100644 --- a/tests/test_entitlement.py +++ b/tests/test_entitlement.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """T297 to T309: entitlement from the control registry (SPEC-v0.8 §3). diff --git a/tests/test_examples.py b/tests/test_examples.py index 869683e5..25816536 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """`examples/` and the sector policy templates. Build-list item 2; SPEC-v0.2 §1.1, T31. @@ -49,7 +49,7 @@ "authority", "cookbook", "without-an-agent", - # SPEC-v0.11 §3 — not a refusal at all. Every §1.1 scenario ends in CTRLRun declining to + # SPEC-v0.11 §3 — not a refusal at all. Every §1.1 scenario ends in ctrlrun declining to # act; this one is about reading the record **afterwards**, and nothing in it is refused. # It has its own test below, because what it must print is the bounded claim rather than a # refusal string. diff --git a/tests/test_five_schema_versions.py b/tests/test_five_schema_versions.py index f75db23a..4f56832a 100644 --- a/tests/test_five_schema_versions.py +++ b/tests/test_five_schema_versions.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """One chain, five receipt schema versions, walked end to end. SPEC-v0.11 §6; T521-T525. diff --git a/tests/test_framework_probe.py b/tests/test_framework_probe.py index ce0c2d44..1c66abe2 100644 --- a/tests/test_framework_probe.py +++ b/tests/test_framework_probe.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The research harness. SPEC-v0.4 §7; T122-T124b. diff --git a/tests/test_fuzzing.py b/tests/test_fuzzing.py index a0686165..d53a8156 100644 --- a/tests/test_fuzzing.py +++ b/tests/test_fuzzing.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The fuzz targets under `fuzz/`, and the invariants they assert. diff --git a/tests/test_gateway.py b/tests/test_gateway.py index 6e9c929d..ef06e4ff 100644 --- a/tests/test_gateway.py +++ b/tests/test_gateway.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """What the gateway needs from the StateStore. Build-list item 6a; SPEC-v0.2 §6.9.4, §6.10. diff --git a/tests/test_gateway_server.py b/tests/test_gateway_server.py index 78b22821..becaa4e2 100644 --- a/tests/test_gateway_server.py +++ b/tests/test_gateway_server.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The gateway in the execution path. Build-list item 6c; SPEC-v0.2 §6.1, §6.3, §6.5-§6.10. @@ -226,7 +226,7 @@ def test_T19_the_action_is_named_for_the_alias_and_the_tool(client, upstream, st def test_T19_the_response_carries_the_ctrlrun_receipt_meta(client, upstream, store): - """§6.8 — so a client is not left guessing what CTRLRun recorded.""" + """§6.8 — so a client is not left guessing what ctrlrun recorded.""" upstream.respond({"resultType": "complete", "content": []}) response = _post(client) diff --git a/tests/test_gateway_transport.py b/tests/test_gateway_transport.py index 09c35674..3b8f3017 100644 --- a/tests/test_gateway_transport.py +++ b/tests/test_gateway_transport.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Regression coverage for the September user-impact audit, through real HTTP sockets.""" diff --git a/tests/test_hop.py b/tests/test_hop.py index 813421cd..84e5dd86 100644 --- a/tests/test_hop.py +++ b/tests/test_hop.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """SPEC-v0.10 §2, item 1: the hop, and the envelope that crosses it. diff --git a/tests/test_hop_surfaces.py b/tests/test_hop_surfaces.py index fd753629..5496ffe7 100644 --- a/tests/test_hop_surfaces.py +++ b/tests/test_hop_surfaces.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """SPEC-v0.10 §6, item 5: the operator surfaces for a hop. @@ -283,7 +283,7 @@ def test_T505c_a_broken_chain_suggests_the_presented_hop_and_names_the_unreadabl def test_T507_scan_names_the_principals_holding_a_root_grant(tmp_path): """**§6.4, and the operator's half of §2.3.2's residual.** - CTRLRun cannot make a receiving agent present the hop it was given: one holding a grant of its + ctrlrun cannot make a receiving agent present the hop it was given: one holding a grant of its own can decline and act on that instead. The deployment rule that collapses it is *an agent that only ever acts on handed-over work holds no root grant of its own*, and §2.3.2 leans on §6's surfaces to make it checkable. Without this line the rule is advice. diff --git a/tests/test_idempotency.py b/tests/test_idempotency.py index b09b7b47..245cd814 100644 --- a/tests/test_idempotency.py +++ b/tests/test_idempotency.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The provider idempotency token. Build-list item 3 of v0.7; SPEC-v0.7 §4, §8.3 T232-T239. diff --git a/tests/test_identity.py b/tests/test_identity.py index 839f602b..a8cf145d 100644 --- a/tests/test_identity.py +++ b/tests/test_identity.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Principal, IdentityProvider and the environment. Build-list item 1; SPEC-v0.3 §2, §3. diff --git a/tests/test_inspect.py b/tests/test_inspect.py index 774cdcf8..3dd1ea30 100644 --- a/tests/test_inspect.py +++ b/tests/test_inspect.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """`ctrlrun inspect `. Build-list item 5; SPEC-v0.2 §5, acceptance test T18. diff --git a/tests/test_jwt_identity.py b/tests/test_jwt_identity.py index 3b30a7b1..28aa3e59 100644 --- a/tests/test_jwt_identity.py +++ b/tests/test_jwt_identity.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """`JWTIdentityProvider`. Build-list item 5; SPEC-v0.3 §3.4. diff --git a/tests/test_ledger.py b/tests/test_ledger.py index aec6bccd..0f62567e 100644 --- a/tests/test_ledger.py +++ b/tests/test_ledger.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """T408 to T414: the ledger and the store amendment (SPEC-v0.9 §3). diff --git a/tests/test_m_of_n.py b/tests/test_m_of_n.py index 9517f0ec..ae7ea096 100644 --- a/tests/test_m_of_n.py +++ b/tests/test_m_of_n.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """T310 to T325: M-of-N on distinct verified principals (SPEC-v0.8 §4.2). diff --git a/tests/test_mcp.py b/tests/test_mcp.py index 3c968558..4300bb1e 100644 --- a/tests/test_mcp.py +++ b/tests/test_mcp.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Request parsing and header-body validation. Build-list item 6b; SPEC-v0.2 §6.2, §6.4, §6.6. @@ -328,7 +328,7 @@ def test_an_Mcp_Param_naming_a_non_primitive_argument_is_refused(value): """§6.4 — the revision defines an encoding for exactly three types: string, integer and boolean. It permits `x-mcp-header` on nothing else, and a `null` parameter omits the header entirely. So no header value can agree with any of these, and comparing against a rendering - CTRLRun invented would certify an agreement under nobody's rules but its own. + ctrlrun invented would certify an agreement under nobody's rules but its own. The header carries the compact rendering the old code compared against, so each case fails on the rule rather than on a stray space. A float is not here: §6.6 refuses one in the body diff --git a/tests/test_mcp_operator.py b/tests/test_mcp_operator.py index 52a231db..86fefda7 100644 --- a/tests/test_mcp_operator.py +++ b/tests/test_mcp_operator.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The operator MCP server. SPEC-mcp-operator.md; acceptance tests T182-T193 and T570-T574. diff --git a/tests/test_migrations.py b/tests/test_migrations.py index 99dfebce..53112f8f 100644 --- a/tests/test_migrations.py +++ b/tests/test_migrations.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Schema version and forward-only migrations. Build-list item 2; SPEC-v0.6 §3, §8 T147-T152b. @@ -538,7 +538,7 @@ def test_T149d_concurrent_opens_all_succeed(tmp_path, shape): database = tmp_path / shape if shape == "existing.db": - # A pre-v0.6 CTRLRun database: the baseline tables, in WAL, and no `schema_version`. + # A pre-v0.6 ctrlrun database: the baseline tables, in WAL, and no `schema_version`. seed = sqlite3.connect(database) seed.execute("PRAGMA journal_mode=WAL") for statement in migrations.MIGRATIONS[0].statements: @@ -668,7 +668,7 @@ def test_T152_a_foreign_database_with_an_effects_table_is_refused(tmp_path): """§3.2, and the hole a review opened in it. The refusal was keyed on the table *name*, and `effects` is a plausible name in somebody - else's schema -- a `$CTRLRUN_STATE` typo is a plausible way to arrive at one. CTRLRun + else's schema -- a `$CTRLRUN_STATE` typo is a plausible way to arrive at one. ctrlrun adopted it, created seven of its own tables **inside the operator's database**, recorded both migrations, opened cleanly, and failed at first use with `no such column: effect_key` -- which is after `Control` was constructed, and §3.3 says every refusal is at open. @@ -685,15 +685,15 @@ def test_T152_a_foreign_database_with_an_effects_table_is_refused(tmp_path): message = str(raised.value) assert "effects" in message and "effect_key" in message, message - # And nothing of CTRLRun's was created on the way to the refusal. + # And nothing of ctrlrun's was created on the way to the refusal. after = _tables(database) - assert after == {"effects", "reverb"}, f"CTRLRun wrote into somebody else's database: {after}" + assert after == {"effects", "reverb"}, f"ctrlrun wrote into somebody else's database: {after}" def test_T152_a_foreign_database_is_refused_naming_what_it_found(tmp_path): """§3.2: neither `schema_version` nor `effects`, but other tables present. - It is somebody else's database, and creating CTRLRun's tables in it is not a recovery. + It is somebody else's database, and creating ctrlrun's tables in it is not a recovery. """ database = tmp_path / "someone-elses.db" connection = sqlite3.connect(database) diff --git a/tests/test_module_graph.py b/tests/test_module_graph.py index 207c0ccb..752eb040 100644 --- a/tests/test_module_graph.py +++ b/tests/test_module_graph.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """`docs/ARCHITECTURE.md` §6's rule, as a test rather than as a sentence. diff --git a/tests/test_observe.py b/tests/test_observe.py index 8728c6c3..67663c4e 100644 --- a/tests/test_observe.py +++ b/tests/test_observe.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Observe mode and `ctrlrun stats`. Build-list item 4; SPEC-v0.3 §6. diff --git a/tests/test_operator_surfaces.py b/tests/test_operator_surfaces.py index 1cdaae3f..4e3dad45 100644 --- a/tests/test_operator_surfaces.py +++ b/tests/test_operator_surfaces.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """T434 to T438: what v0.9 built, visible to the person who gets paged (SPEC-v0.9 §7). diff --git a/tests/test_otel.py b/tests/test_otel.py index 2bfd988b..3625d788 100644 --- a/tests/test_otel.py +++ b/tests/test_otel.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The OpenTelemetry sink. Build-list item 8; SPEC-v0.2 §8, acceptance test T29. diff --git a/tests/test_outcome.py b/tests/test_outcome.py index 177f18f1..e5b64b7f 100644 --- a/tests/test_outcome.py +++ b/tests/test_outcome.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """SPEC-v0.2 §6.8, and nothing else. Build-list item 6b; acceptance test T24. diff --git a/tests/test_packaging.py b/tests/test_packaging.py index c16594e7..70ffa631 100644 --- a/tests/test_packaging.py +++ b/tests/test_packaging.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The dependency rule. Build-list item 6a; SPEC-v0.2 §1.1, and the first half of T30. @@ -428,7 +428,7 @@ def test_the_manifest_check_would_notice_a_file_it_does_not_ship(): def test_the_package_never_encodes_a_token(): - """The claims table — "CTRLRun issues no credential and defines no identity format" + """The claims table — "ctrlrun issues no credential and defines no identity format" (https://ctrlrun.dev/docs/CLAIMS). A claim in the README needs a test, and this one is structural: the package verifies @@ -720,7 +720,7 @@ def test_T139_the_readme_makes_no_conformance_claim(): def test_T139_the_claim_check_can_see_a_claim(): """The precondition, without which the test above passes on any document at all: a sentence that *does* make the claim must be caught.""" - assert _claims("CTRLRun is fully compliant with the standard.") != [] + assert _claims("ctrlrun is fully compliant with the standard.") != [] assert _claims("This adapter is conformant.") != [] # And the shapes that must stay allowed. assert _claims("It does not mean secure, safe, compliant, certified or audited.") == [] diff --git a/tests/test_policy.py b/tests/test_policy.py index db66caf4..07aa0a9b 100644 --- a/tests/test_policy.py +++ b/tests/test_policy.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Policy loading and rule evaluation. SPEC-v0.1 §3; acceptance test T6 (policy half).""" @@ -70,7 +70,7 @@ def test_decision_renders_by_value() -> None: This is the guard on `Decision` being a `StrEnum` (SPEC §3.3): under `(str, Enum)` both renderings below are `Decision.ALLOW`, and a receipt is read by tools that - never imported CTRLRun. + never imported ctrlrun. """ for member in Decision: assert str(member) == member.value diff --git a/tests/test_policy_change.py b/tests/test_policy_change.py index 886c0150..b8cfa21c 100644 --- a/tests/test_policy_change.py +++ b/tests/test_policy_change.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """T353 to T370: a policy change is a protected action (SPEC-v0.8 §8). diff --git a/tests/test_policy_versioning.py b/tests/test_policy_versioning.py index 235e56ce..4f146db1 100644 --- a/tests/test_policy_versioning.py +++ b/tests/test_policy_versioning.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Policy versioning and the control registry. Item 7; SPEC-v0.6 §7, §8 T171-T177d. @@ -1328,7 +1328,7 @@ def test_T173b_an_observed_action_spends_no_grant(tmp_path) -> None: The reservation is still taken, and the asymmetry is the point: in observe mode the action genuinely executes, so the effect record has to exist or `v0.1 §5.4`'s duplicate refusal has - nothing to refuse with. Observe mode suppresses CTRLRun's **decisions**, not the record of an + nothing to refuse with. Observe mode suppresses ctrlrun's **decisions**, not the record of an effect that really happened. """ from ctrlrun import Control diff --git a/tests/test_postgres.py b/tests/test_postgres.py index d5e3c9a7..02d639c9 100644 --- a/tests/test_postgres.py +++ b/tests/test_postgres.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The Postgres backend. Build-list item 3; SPEC-v0.6 §4, §8 T153-T154f. diff --git a/tests/test_preconditions.py b/tests/test_preconditions.py index 9022674a..758bc206 100644 --- a/tests/test_preconditions.py +++ b/tests/test_preconditions.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Precondition fingerprints. Build-list item 5; SPEC-v0.7 §6, §7, §8.5 T253-T269. diff --git a/tests/test_properties.py b/tests/test_properties.py index da2fe5f4..b30ad031 100644 --- a/tests/test_properties.py +++ b/tests/test_properties.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Invariants stated directly, over inputs nobody chose by hand. diff --git a/tests/test_protect.py b/tests/test_protect.py index 92036107..7af04bbf 100644 --- a/tests/test_protect.py +++ b/tests/test_protect.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Control, @protect and context(). SPEC-v0.1 §2.2, §6, §8; acceptance test T6.""" diff --git a/tests/test_readme_assets.py b/tests/test_readme_assets.py index 99786259..6e5a7fc4 100644 --- a/tests/test_readme_assets.py +++ b/tests/test_readme_assets.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The README's header assets: the animation, its tape, what it records, and the social preview. @@ -12,7 +12,7 @@ Until 2026-09-14 the animation was `ctrlrun demo`'s first two scenarios, piped through `sed` and a pacing loop. It was a true recording and a hard one to read: a stranger saw a transcript with -two pipes in the command line and no code, and could not tell from it what CTRLRun *is*. The +two pipes in the command line and no code, and could not tell from it what ctrlrun *is*. The recording is now the integration itself: the policy file, the agent that runs one refund and is stopped on the next, the human answering from the shell, and the same approval refused for a different amount. The demo transcript is still in the README, in the collapsed block that @@ -254,7 +254,7 @@ def test_the_social_preview_is_1280_by_640_and_rendered_from_its_svg(): #: `CTRLRun/ctrlrun-docs` asserts the same strings against `index.mdx`, so a change to either #: surface fails on the other. HOMEPAGE_H1 = ( - "CTRLRun stops AI agents from taking wrong, restricted, or malicious actions in your workflows." + "ctrlrun stops AI agents from taking wrong, restricted, or malicious actions in your workflows." ) HOMEPAGE_LEDE = ( "Every action is checked against your rules before it runs. Allowed actions go through. " @@ -275,7 +275,7 @@ def test_the_header_carries_the_fixed_copy_and_the_five_badges(): It carried the capability matrix too, and this test required it there. That is the front door decision the matrix was moved for: a six-by-four table is the right document for - somebody evaluating CTRLRun and the wrong one for somebody deciding whether to keep + somebody evaluating ctrlrun and the wrong one for somebody deciding whether to keep reading, so the first section after the animation is the failure itself. The requirement is inverted rather than deleted: no table above the first H2, the marker still in the file, and the first section named, because a header that quietly grew a table again would @@ -283,7 +283,7 @@ def test_the_header_carries_the_fixed_copy_and_the_five_badges(): The fixed lines are pinned so the header cannot drift untested. The first two are the homepage's H1 and lede, verbatim; the category noun is still asserted, because a reader - had to reverse-engineer what CTRLRun *is* from three slogans before 0.6; and the + had to reverse-engineer what ctrlrun *is* from three slogans before 0.6; and the at-most-once and unknown sentences are the two claims `CLAIMS.md` maps to their tests. """ text = README.read_text(encoding="utf-8") @@ -298,9 +298,9 @@ def test_the_header_carries_the_fixed_copy_and_the_five_badges(): "A consequential action happens at most once, exactly as approved, and leaves a " "receipt." in head ) - assert "When the outcome is unknown, CTRLRun says so instead of guessing." in head + assert "When the outcome is unknown, ctrlrun says so instead of guessing." in head # The homepage's example heading opens the first section. - assert "**The model guesses. CTRLRun does not.**" in text + assert "**The model guesses. ctrlrun does not.**" in text # The row was cut from thirteen to ten on 2026-09-11: `pypi/pyversions` is metadata rather # than a claim, and `ruff` and `mypy --strict` say how the library is written, which is not # what a stranger is deciding on the first screen. `scripts/check.sh` still runs all three diff --git a/tests/test_receipt_chain.py b/tests/test_receipt_chain.py index 2d564e86..46de74ad 100644 --- a/tests/test_receipt_chain.py +++ b/tests/test_receipt_chain.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Receipt integrity. Build-list item 6; SPEC-v0.6 §6, §8 T164-T170. @@ -293,7 +293,7 @@ def test_T164_an_altered_receipt_is_content_altered_at_its_seq( merely that something was invalid. The tampering is done in SQL, underneath the store, because that is the threat model: an - `UPDATE` by somebody with write access who has no interest in going through CTRLRun. + `UPDATE` by somebody with write access who has no interest in going through ctrlrun. """ import sqlite3 diff --git a/tests/test_reconcile.py b/tests/test_reconcile.py index e71c81ba..d8a75a3b 100644 --- a/tests/test_reconcile.py +++ b/tests/test_reconcile.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The reconciliation hook. Build-list item 1; SPEC-v0.2 §2. @@ -125,7 +125,7 @@ def refund(payment_id: str, amount: int) -> str: assert len(effect_resolved) == 1 assert effect_resolved[0].data["resolved_by"] == "reconcile" - # The order is the argument: CTRLRun asked, learned an answer, and only then moved the + # The order is the argument: ctrlrun asked, learned an answer, and only then moved the # record — and the retry won its reservation after that (SPEC-v0.2 §2.5). reserved = _events(state_store, EventType.EFFECT_RESERVED) assert ( @@ -476,7 +476,7 @@ def refund(payment_id: str, amount: int) -> str: assert state_store.get_effect("refund:txn_1").state is EffectState.COMMITTED assert remote.calls == 1 - # CTRLRun asked and got an answer, so the asking is recorded; applying it is what was + # ctrlrun asked and got an answer, so the asking is recorded; applying it is what was # dropped, so there is no second EFFECT_RESOLVED claiming a different authority. resolved = _events(state_store, EventType.RECONCILIATION_RESOLVED) assert len(resolved) == 1 diff --git a/tests/test_recovery.py b/tests/test_recovery.py index 83cf338e..b99fd902 100644 --- a/tests/test_recovery.py +++ b/tests/test_recovery.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Recovery on restart. Build-list item 5; SPEC-v0.6 §5, §8 T159-T163. @@ -771,7 +771,7 @@ def tables(schema: str) -> list[str]: cli.main, [*command, "--store-url", f"{POSTGRES_URL}?ctrlrun_schema={empty}"] ) assert result.exit_code != 0, ( - f"`ctrlrun {command[0]}` accepted a schema holding no CTRLRun database:\n" + f"`ctrlrun {command[0]}` accepted a schema holding no ctrlrun database:\n" f"{result.output}" ) assert tables(empty) == [], ( diff --git a/tests/test_release_v0_6.py b/tests/test_release_v0_6.py index 1df1a1f2..dcc68b0b 100644 --- a/tests/test_release_v0_6.py +++ b/tests/test_release_v0_6.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The v0.6 release pass, packaging half. Item 9; SPEC-v0.6 §8's T181. diff --git a/tests/test_reporting.py b/tests/test_reporting.py index 306dc1a7..7423a4d9 100644 --- a/tests/test_reporting.py +++ b/tests/test_reporting.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """`ctrlrun.reporting`: the shared producer behind `stats`, `inspect` and the operator server.""" diff --git a/tests/test_repository_signals.py b/tests/test_repository_signals.py index 58ce2967..63b7193d 100644 --- a/tests/test_repository_signals.py +++ b/tests/test_repository_signals.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The repository's trust signals, as assertions rather than intentions. @@ -895,7 +895,7 @@ def test_normalize_sdist_refuses_without_an_epoch_and_refuses_a_wheel(tmp_path): # --- every source file says who holds it and under what licence ------------------------------- _SOURCE_DIRS = ("src", "tests", "fuzz", "scripts", "adapters", "examples") -_COPYRIGHT_LINE = "# SPDX-FileCopyrightText: 2026 The CTRLRun contributors" +_COPYRIGHT_LINE = "# SPDX-FileCopyrightText: 2026 The ctrlrun contributors" _LICENSE_LINE = "# SPDX-License-Identifier: Apache-2.0" diff --git a/tests/test_resume.py b/tests/test_resume.py index ffe76ef1..3d05d393 100644 --- a/tests/test_resume.py +++ b/tests/test_resume.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Suspension and resumption. Build-list item 6d; SPEC-v0.2 §6.9 via the kernel. diff --git a/tests/test_retention.py b/tests/test_retention.py index 6f64eef9..307a0a0f 100644 --- a/tests/test_retention.py +++ b/tests/test_retention.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Retention: the prune, the checkpoint and the hold. SPEC-v0.11 §4; T540-T552. diff --git a/tests/test_revocation_feed.py b/tests/test_revocation_feed.py index 9863ebd9..76b9faf6 100644 --- a/tests/test_revocation_feed.py +++ b/tests/test_revocation_feed.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """T340 to T352: a credential revoked before its `exp` is refused (SPEC-v0.8 §6). diff --git a/tests/test_revoke_selector.py b/tests/test_revoke_selector.py index 97e880b5..96cfaba3 100644 --- a/tests/test_revoke_selector.py +++ b/tests/test_revoke_selector.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """T272 to T280: `ctrlrun revoke --created-by` and `--under` (SPEC-v0.8 §7). @@ -132,7 +132,7 @@ def workspace(request, tmp_path, monkeypatch): def _with_schema(url: str, schema: str) -> str: - """`url` with CTRLRun's schema parameter added, keeping every parameter it already has. + """`url` with ctrlrun's schema parameter added, keeping every parameter it already has. A naive f-string appends a second '?' to a URL carrying `?sslmode=require`, and `_peel_schema` then reads `ctrlrun_schema` as part of the `sslmode` value and falls back to diff --git a/tests/test_sbom.py b/tests/test_sbom.py index 7045b414..80b33a39 100644 --- a/tests/test_sbom.py +++ b/tests/test_sbom.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The SBOM says what the wheel actually carries, and stays wrong-proof. diff --git a/tests/test_scan.py b/tests/test_scan.py index 9c8460f0..00a4bb07 100644 --- a/tests/test_scan.py +++ b/tests/test_scan.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """`ctrlrun scan` — the coverage finder. `docs/SPEC-scan.md`, T194-T206. diff --git a/tests/test_schema_completeness.py b/tests/test_schema_completeness.py index 219e6f8f..2a97fa1e 100644 --- a/tests/test_schema_completeness.py +++ b/tests/test_schema_completeness.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The release pass's D27 rule: a schema is complete when something actually writes every field. diff --git a/tests/test_scope_provider.py b/tests/test_scope_provider.py index 01b2fe24..20f03a08 100644 --- a/tests/test_scope_provider.py +++ b/tests/test_scope_provider.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """T388 to T398: scope providers (SPEC-v0.9 §5). diff --git a/tests/test_sector_configuration.py b/tests/test_sector_configuration.py index 8b053f20..77d454af 100644 --- a/tests/test_sector_configuration.py +++ b/tests/test_sector_configuration.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The throwaway sector configuration. Item 7; SPEC-v0.6 §7.5, §8 T177b. @@ -43,7 +43,7 @@ #: One sector's configuration, written against §7.3 and §7.4 and nothing else. It is not an #: example, not a template and not a recommendation: no clinical or regulatory claim is made by -#: it, and `source:` strings are cited exactly as an operator would write them -- CTRLRun does not +#: it, and `source:` strings are cited exactly as an operator would write them -- ctrlrun does not #: know what any of them mean (§7.3). HOSPITAL = """ schema: ctrlrun.policy/v4 @@ -173,7 +173,7 @@ def test_the_finding_that_redaction_was_not_needed() -> None: That is the primitive a pack cannot be written without, and it is not on probation. - It does **not** need the value hidden from the evidence. A trust that may not have a diagnosis in its receipt store may not have it in the record system either; redaction in - CTRLRun's evidence would be a second, weaker copy of a control that has to live upstream, + ctrlrun's evidence would be a second, weaker copy of a control that has to live upstream, and shipping it invites an operator to believe the weaker one is the control. - The one place a value must be visible is the **approval payload**, which §7.4 already exempts -- and once a human has to see the real diagnosis to approve the change, redacting diff --git a/tests/test_sinks.py b/tests/test_sinks.py index 305f113b..2fd61555 100644 --- a/tests/test_sinks.py +++ b/tests/test_sinks.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """`EventSink` and `JSONLEventSink`. Build-list item 4; SPEC-v0.2 §4, acceptance test T17. diff --git a/tests/test_soak.py b/tests/test_soak.py index ac1bab06..bcbd0f67 100644 --- a/tests/test_soak.py +++ b/tests/test_soak.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The soak harness. Item 8; SPEC-v0.6 §8.1. diff --git a/tests/test_store_conformance.py b/tests/test_store_conformance.py index 48ddf5c6..c229c1f6 100644 --- a/tests/test_store_conformance.py +++ b/tests/test_store_conformance.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The store conformance suite. Build-list item 1; SPEC-v0.6 §2, §8 T140-T146. diff --git a/tests/test_task_binding.py b/tests/test_task_binding.py index 704160e7..0d3578b9 100644 --- a/tests/test_task_binding.py +++ b/tests/test_task_binding.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """T379 to T387: task-bound authority (SPEC-v0.9 §6). diff --git a/tests/test_transport.py b/tests/test_transport.py index 132353d8..fb33f432 100644 --- a/tests/test_transport.py +++ b/tests/test_transport.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """`ctrlrun.transport`, the `NotExecuted` classifier. SPEC-v0.7 §2; T220 to T229b, T231. @@ -2010,7 +2010,7 @@ def test_T231b_a_gateway_continuation_never_records_FAILED(tmp_path, case): # Relayed unchanged, the tool's own error included: only the record changes. assert answer["result"]["isError"] is True, answer else: - # The upstream's own answer is relayed unchanged; only what CTRLRun records changes. + # The upstream's own answer is relayed unchanged; only what ctrlrun records changes. assert answer["error"]["code"] in (-32601, -32000), answer diff --git a/tests/test_unreadable_receipt.py b/tests/test_unreadable_receipt.py index dd067728..a7896946 100644 --- a/tests/test_unreadable_receipt.py +++ b/tests/test_unreadable_receipt.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """A reader that names a bad row and blinds nothing else. SPEC-v0.11 §5; T510-T519. @@ -66,7 +66,7 @@ def a_chain(store, count: int = 4) -> list: #: The tamper, in SQL, underneath the store: an `UPDATE` by somebody with write access who has -#: no interest in going through CTRLRun. A **declared** key set to a value of the wrong type, +#: no interest in going through ctrlrun. A **declared** key set to a value of the wrong type, #: which is the case SPEC-v0.7 §12.5 named and neither of the two it says already worked (an #: unknown schema label, and an added key, are each already reported at their `seq`). def _tamper_one_value(database: Path, at: int = 2) -> None: diff --git a/tests/test_upgrade_0_10_to_0_11.py b/tests/test_upgrade_0_10_to_0_11.py index 8802fa4c..a9301ad6 100644 --- a/tests/test_upgrade_0_10_to_0_11.py +++ b/tests/test_upgrade_0_10_to_0_11.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The upgrade path, and the one irreversible thing a v0.11 release does. @@ -13,7 +13,7 @@ this build: opened it: 4 receipts, chain ok=True, HEAD 0008_anchor_checkpoint_hold anchors readable, checkpoint None, holds empty 0.10.0 again: REFUSED the migrated store -- SchemaMismatch: This database was written by a - newer build of CTRLRun and records a migration this one does not know. + newer build of ctrlrun and records a migration this one does not know. **The refusal is the point.** Migration `0008_anchor_checkpoint_hold` is additive -- three new tables and nothing altered -- so 0.10.0 could in principle read every row it wrote. It refuses diff --git a/tests/test_upgrade_0_9_to_0_10.py b/tests/test_upgrade_0_9_to_0_10.py index 76863e5d..273076cb 100644 --- a/tests/test_upgrade_0_9_to_0_10.py +++ b/tests/test_upgrade_0_9_to_0_10.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The upgrade path, and the one irreversible thing a v0.10 release does. diff --git a/tests/test_upstream_pinning.py b/tests/test_upstream_pinning.py index 09864fb9..45ccd2fc 100644 --- a/tests/test_upstream_pinning.py +++ b/tests/test_upstream_pinning.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """SPEC-v0.10 §4, item 3: upstream identity pinning. diff --git a/tests/test_verify.py b/tests/test_verify.py index bb79108e..0f95799c 100644 --- a/tests/test_verify.py +++ b/tests/test_verify.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """`ctrlrun verify` — the registry, the engine, G1-G6 and G10. SPEC-v0.4 §2, §3; T100-T107. diff --git a/tests/test_verify_action.py b/tests/test_verify_action.py index fa19f582..98b95807 100644 --- a/tests/test_verify_action.py +++ b/tests/test_verify_action.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The composite action, the badge and `https://ctrlrun.dev/docs/verify`. SPEC-v0.4 §5; T118-T120. @@ -41,7 +41,7 @@ V1_PAYMENTS = REPO_ROOT / "examples" / "policies" / "payments.yaml" #: SPEC-v0.4 §5.3 and §6.1 — the vocabulary the badge, the summary and the page may not use as -#: a claim about CTRLRun or about the operator's system. The same list `v0.2 §10` T31 holds the +#: a claim about ctrlrun or about the operator's system. The same list `v0.2 §10` T31 holds the #: sector templates to. FORBIDDEN = ("secure", "safe", "compliant", "certified", "audited") @@ -196,10 +196,10 @@ def test_T119_the_rendered_badge_text_is_exactly_CTRLRun_verified_N_over_M(tmp_p assert badge is not None rendered = f"{badge['label']} {badge['message']}" - assert rendered == f"CTRLRun verified {report.passed}/{report.applicable}" - assert re.fullmatch(r"CTRLRun verified \d+/\d+", rendered) + assert rendered == f"ctrlrun verified {report.passed}/{report.applicable}" + assert re.fullmatch(r"ctrlrun verified \d+/\d+", rendered) assert BADGE_MESSAGE.fullmatch(badge["message"]) - assert badge["label"] == BADGE_LABEL == "CTRLRun" + assert badge["label"] == BADGE_LABEL == "ctrlrun" assert badge["schemaVersion"] == 1 @@ -342,7 +342,7 @@ def test_T120_the_renderer_writes_no_badge_file_where_none_is_allowed(tmp_path): assert main(["--report", str(document), "--badge", str(badge), "--summary", str(summary)]) == 0 assert not badge.exists() - assert summary.read_text(encoding="utf-8").startswith("### CTRLRun verify") + assert summary.read_text(encoding="utf-8").startswith("### ctrlrun verify") def test_T120_the_renderer_writes_the_badge_where_one_is_allowed(tmp_path): @@ -392,7 +392,7 @@ def test_the_job_summary_carries_the_not_applicable_rows_in_full(): # --- the verify page quotes the real output (SPEC-v0.4 §4.1; the CLAIMS.md standard) -------- #: The README carried a copy of this report until 2026-09-09, when the page was cut to what -#: CTRLRun does, how to use it and how it works, and the report went with the rest of the +#: ctrlrun does, how to use it and how it works, and the report went with the rest of the #: verify section. The guard moved rather than went: the verify page is now the single #: home of the verbatim output, so the "two copies can drift" test below has nothing left to #: compare and is gone, and this one reads the page instead of the README. @@ -536,7 +536,7 @@ def publish(message: str) -> subprocess.CompletedProcess[str]: # be failing on its own setup rather than on the script. (work / "badge").mkdir() (work / "badge" / "verify-badge.json").write_text( - json.dumps({"schemaVersion": 1, "label": "CTRLRun", "message": message}), + json.dumps({"schemaVersion": 1, "label": "ctrlrun", "message": message}), encoding="utf-8", ) # The second artifact, from the `check` job. The script refuses without it rather than @@ -623,7 +623,7 @@ def publish(index: int) -> subprocess.CompletedProcess[str]: ) (work / "badge").mkdir(parents=True) (work / "badge" / "verify-badge.json").write_text( - json.dumps({"schemaVersion": 1, "label": "CTRLRun", "message": "verified 9/9"}), + json.dumps({"schemaVersion": 1, "label": "ctrlrun", "message": "verified 9/9"}), encoding="utf-8", ) (work / "badge" / "tests-badge.json").write_text( diff --git a/tests/test_verify_authority.py b/tests/test_verify_authority.py index eee1ebff..5076cf9b 100644 --- a/tests/test_verify_authority.py +++ b/tests/test_verify_authority.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The authority guarantees and config-derived selection. SPEC-v0.4 §2 (G7-G9), §3.4. diff --git a/tests/test_verify_report.py b/tests/test_verify_report.py index 68c1eb70..b9b488bc 100644 --- a/tests/test_verify_report.py +++ b/tests/test_verify_report.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """Reporting: the human report, `--json`, `--junit`, the exit codes. SPEC-v0.4 §4; T113-T117. @@ -402,7 +402,7 @@ def test_T116_exit_1_when_a_guarantee_fails(tmp_path, monkeypatch): (MALFORMED, (), "unknown policy schema"), (EMPTY, (), "nothing was checked"), (ALL_APPLICABLE, ("--only", "G99"), "G99"), - # A backend CTRLRun really does not have. `postgres://` is accepted from v0.6 (§9.6 + # A backend ctrlrun really does not have. `postgres://` is accepted from v0.6 (§9.6 # amendment 2), so using it here tested the driver's absence rather than the flag's # refusal -- which passed wherever psycopg happened to be installed. (ALL_APPLICABLE, ("--store-url", "mysql://x/y"), "postgresql://"), diff --git a/tests/test_webhook.py b/tests/test_webhook.py index 9ac54cbc..c24ccc66 100644 --- a/tests/test_webhook.py +++ b/tests/test_webhook.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2026 The CTRLRun contributors +# SPDX-FileCopyrightText: 2026 The ctrlrun contributors # SPDX-License-Identifier: Apache-2.0 """The webhook approval provider. Build-list item 7; SPEC-v0.2 §7, tests T27 and T28.