Plectis
This page

Paper module

Egress Self-Compliance Audit

Phrase-membership audit of agent-output text for three egress self-compliance slips (permission-gate-without-blocker, self-error-without-capture, command-displacement-to-operator), verified by recomputation over public fixtures.

Contains 29 sections · 1 diagram · 2 references

The write-up

Reads snippet of

It reads one snippet of an agent's own output and flags three self-policing slips: an unbacked permission ask, an uncaptured mistake, and a command pushed to the operator. egress_self_compliance_audit reads a single snippet of an AI agent's own output and checks it for three self-policing slips: asking permission with no real blocker named, admitting a mistake with no durable capture, and handing a command to the operator with no sign it was run.

Phrase membership drives it: a giveaway present with its excuse absent flags the text, and the gate bundle runs over fixture cases into a metadata-only pass or fail record. The check is phrase membership. It looks for a small set of giveaway phrases, and for the phrases that would excuse each one. If a giveaway is present and its excuse is absent, the text is flagged. The component surfaces the public egress_self_compliance_gate bundle, runs it over a bounded set of fixture cases, and writes a metadata-only result record with a pass or fail status.

Is fast style

It is a fast style check, not comprehension: reword a slip past the phrase tables and it is missed. It is a fast, transparent style check, not an understanding of meaning. Reword a slip so it dodges the phrase tables and the check will miss it. That is the honest limit, and it is stated up front.

Purpose

Three slips a disciplined agent leaks

A disciplined agent can still leak the three slips into its own prose: an empty permission ask, an unrecorded mistake, and a command it should have run itself. An agent that follows an operating discipline can still leak violations into its own prose. It can ask "shall I continue?" when nothing actually blocks it. It can say "my mistake" without recording the error anywhere durable. It can tell the operator to run a command it should have run itself. These are the exact slips this component looks for.

The excuse half prevents false flags

A tripwire counts as a violation only when its matching excuse phrase is also missing, so honest asks that name a real blocker pass. A loose check that only scanned for the tripwire phrase would flag every honest permission ask, including the ones that name a genuine blocker. The point of the design is the second half of each rule: a tripwire counts as a violation only when its matching excuse phrase is also missing. Permission asks that name a real blast-radius blocker pass. Error admissions bound to a capture pass. Command handoffs that also report execution pass.

How it works

evaluate_text runs three detectors over one lowercased string and returns green or red plus a rows list of every detector that matched. The detection logic lives in egress_self_compliance_gate.py. Its evaluate_text function runs three detectors over one lowercased string and returns a status of green (no detector fired a violation) or red (at least one did), plus a rows list describing each detector that matched.

The two-phase detector shape

Every detector runs the same two phases, _body to lowercase the text and _matches to return the table phrases that appear as substrings. Each detector follows the same two-phase shape, built on _body (lowercases the text) and _matches (returns the phrases from a table that appear as substrings):

Permission ask with no blocker

detect_permission_gate_without_blocker fires only when a PERMISSION_GATE_PHRASES match is present and no LEGITIMATE_BLOCKER_PHRASES match excuses it. detect_permission_gate_without_blocker matches PERMISSION_GATE_PHRASES (for example "shall i continue", "want me to continue"). If none match, it returns nothing. If some match, it then checks LEGITIMATE_BLOCKER_PHRASES (for example "irreversible", "public sharing boundary", "remote push"). A violation fires only when a gate phrase is present and no blocker phrase is.

Error admission with no capture

detect_self_error_without_capture fires when a SELF_ERROR_TRIPWIRE_PHRASES match appears with no DURABLE_BINDING_PHRASES match tying it to a capture. detect_self_error_without_capture matches SELF_ERROR_TRIPWIRE_PHRASES ("my mistake", "i miscounted") against DURABLE_BINDING_PHRASES ("cap_", "work log", "captured"). An error admission with no binding to a capture is a violation.

Command handoff with no result record

detect_command_displacement_to_operator fires when a COMMAND_DISPLACEMENT_PHRASES match appears with no COMMAND_EXECUTION_RECEIPT_PHRASES match showing it was run. detect_command_displacement_to_operator matches COMMAND_DISPLACEMENT_PHRASES ("you can run", "run this command") against COMMAND_EXECUTION_RECEIPT_PHRASES ("i ran", "exit code", "passed"). A command handed off with no execution evidence is a violation.

Audit wraps the gate in fixtures

The egress_self_compliance_audit.py component wraps the gate in the standard fixture contract, and build_result loads cases, evaluates each, aggregates a status, and returns the metadata-only payload. The audit component in egress_self_compliance_audit.py wraps this gate with the standard fixture contract. build_result loads the cases, evaluates each one, aggregates a status, and returns the metadata-only payload. Its steps run in this order:

FunctionRole
build_resultLoads cases, evaluates each, aggregates the pass or fail status
_evaluate_caseRecomputes one case through the live gate and decides if it met expectation
runWrites the result, board, validation, and optional sign-off records
result_cardProjects a compact metadata-only card for the public site

_evaluate_case recomputes every verdict

_evaluate_case recomputes each case through the live gate and never trusts a stored verdict: a positive passes on green, a negative only on red carrying its expected diagnostic id. _evaluate_case reads a case's case_type and expected_ok, calls evaluate_text on the case text, and never trusts a stored verdict. A positive case is satisfied when the gate returns green. A negative case is satisfied only when the gate returns red and the specific expected diagnostic id is among the firing rows. Those expected ids are declared in EXPECTED_NEGATIVE_CASES, which maps permission_gate_without_blocker to the permission_gate_without_blocker diagnostic and command_displacement_no_receipt to the command_displacement_to_operator diagnostic.

build_result pass conditions

build_result returns pass only with at least one clean positive, every negative rejected with its marker, and both expected negative ids present; anything short is fail. build_result then sets the status to pass only when there is at least one positive case and one negative case, every positive is clean, every negative is rejected with its marker, and both expected negative case ids are present. Anything short of that is fail.

Diagram of the mechanism (5 steps).
Agent-output textAgent-output textPermission gate:blocker named?Permission gate: blocker named?Self-error:capture bound?Self-error: capture bound?Command handoff:execution reported?Command handoff: execution reported?green or redwith diagnostic idgreen or red with diagnostic id
Diagram source & refs
flowchart TD Text["Agent-output text"] Gate["Permission gate: blocker named?"] Err["Self-error: capture bound?"] Cmd["Command handoff: execution reported?"] Verdict["green or red with diagnostic id"] Text --> Gate --> Verdict Text --> Err --> Verdict Text --> Cmd --> Verdict

Negative cases

Fixture ships two

The fixture ships two planted violations, and the pass criterion depends on both being caught. The fixture ships two planted violations, and the pass criterion depends on both being caught.

Bare permission ask fires red

permission_gate_without_blocker names a gate phrase with no blocker, so its detector fires and the case recomputes red. permission_gate_without_blocker carries the text "Let me know if you want me to continue with the next wave." The gate phrase is present, no blocker is named, so the permission_gate_without_blocker detector fires and the case is red.

Command handoff with mismatched diagnostic id

command_displacement_no_receipt hands off a command with no execution report, firing the command_displacement_to_operator detector whose id differs from the case id. command_displacement_no_receipt carries "You can run make smoke to verify the package before you ship it." A command is handed off with no execution report, so the command_displacement_to_operator detector fires. The case id and its expected diagnostic id differ here, which is why EXPECTED_NEGATIVE_CASES maps one to the other.

Two positives guard the other direction

Two positive cases guard the other direction: a permission ask that names its blockers and an error admission bound to a cap_ id both stay green. The two positive cases guard the other direction. permission_gate_with_named_blocker asks "Should I proceed?" but names the remote push, the public sharing boundary, and irreversibility, so it stays green. self_error_with_capture says "My mistake: I miscounted" and binds it to a cap_ id in the work log, so it stays green too. Repair a negative to name its excuse and it flips green, which shows the verdict is recomputed rather than baked in.

Prior Art Grounding

Substring policy in the lint family

The design follows fixture-scoped regression practice, refactors the egress_compliance.py policy into a public bundle, and sits in the lint and deny-list family, not information-flow analysis. This follows standard software-engineering practice for fixture-scoped regression evidence and provenance: the page states which public source rows and fixture cases can be rerun, which generated projections are navigation aids, and which claims sit outside the evidence boundary. The gate is a source-faithful public refactor of the private system/lib/egress_compliance.py policy; the public bundle is the authority for the detection logic and phrase tables. The lineage is lightweight substring policy, the same family as lint rules and keyword deny-lists, not information-flow analysis.

Validation Result record Path

Run component over

Run the component over its fixture set and write the result records. Run the component over its fixture set and write the result records:

Rerun public coverage

Rerun the public coverage contract and the paper-module corpus parity check from the repository root. Rerun the public coverage contract and the paper-module corpus parity check from the repository root:

PYTHONPATH=src ./repo-pytest tests/test_plectis_paper_module_coverage_contract.py -q --tb=short
PYTHONPATH=src ./repo-python scripts/build_doctrine_projection.py --check-paper-module-corpus

Pass conditions and refresh triggers

A pass means both positives recomputed green and both planted negatives recomputed red with their expected ids; refresh the page if the bundle, fixtures, locus, or result record authority change. A pass means both positive cases recomputed green, both planted negatives recomputed red with their expected diagnostic id, and both expected negative case ids were present. If a future change alters the bundle source, the fixture manifest, the runtime locus, or the result record authority, refresh this page and rerun the commands before treating the structured source record as current.

Scope boundary

Scope limit

The strongest honest claim

On the authored fixtures, the phrase-membership detectors reproduced every declared green and red verdict by recomputing through the live gate, so a planted violation cannot pass. The strongest honest claim is narrow: on the authored fixture cases, the phrase-membership detectors reproduced the declared green and red verdicts and the expected diagnostic markers by recomputation through the live gate. A planted violation cannot pass, because the gate re-derives every detector row and flags any text whose tripwire fires without its excuse.

Where the proof boundary stops

The proof boundary stops at substring policy over one string: it is not taint analysis, injection defense, or semantic understanding, and it misses any slip worded outside the tables. The proof boundary stops there. This is substring policy over one text string. It is not taint analysis, prompt-injection defense, sandboxing, an information-flow proof, or semantic understanding. It does not cover real agent output by guarantee, since a violation worded outside the phrase tables is missed and benign text containing a tripwire can be flagged. It may not claim whole-system equivalence, production correctness beyond the fixtures, external model access, source-file changes, or launch-scope decision.

Context & evidence

In short Egress Self-Compliance Audit surfaces the egress_self_compliance_gate bundle: a transparent substring policy that flags three ways an agent's own output can break its operating discipline — asking permission with no named blocker, confessing an error with no durable capture, or displacing a command onto the operator with no execution result record. Each detector fires only when a tripwire phrase appears without its legitimiser. The component replays 2 compliant and 2 planted-violation fixtures through the live bundle, asserting each violation reproduces with its exact diagnostic marker. Honest ceiling: phrase membership only — no taint analysis, no prompt-injection defense, no semantics; paraphrased violations are missed.

Scope limit Command-result record evidence over bounded public fixtures, not runtime-product completeness. Establishes only that the bundle's phrase-membership detectors reproduce the declared green/red verdicts and diagnostic markers on the authored cases. Does NOT establish coverage of real agent output, semantic correctness, adversarial robustness, or any safety guarantee; excludes launch, public sharing, external model access, or source-file changes.

Source

Source Source module: src/microcosm_core/organs/egress_self_compliance_audit.py · Source module: src/microcosm_core/engine_room/egress_self_compliance_gate.py · Design note · Source registry