Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ jobs:
run: python -m pytest primitives -v
- name: Run root tests
run: python -m pytest test_stop_machine.py -v
- name: Run invariant tests
run: python -m pytest tests/ -v

geometry-analysis:
needs: [test]
Expand Down
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,27 @@ All notable changes to **stop-machine** will be documented in this file.

---

## [meta-hardening] — 2026-02-24

### Added
- **SECURITY.md**: Security policy documenting threat model, invariants, and reporting
- **docs/runtime-trace.md**: Gate decision flow documentation (incl. SILENCE rephrase)
- **docs/architecture-diagram.md**: Component map and data flow diagram
- **tests/__init__.py**: Test package marker
- **tests/test_invariant_enforcement.py**: Cross-cutting invariant enforcement tests
- EXIT_ENUM invariants (exact members, VALID_EXIT_VALUES consistency, _classify_exit)
- StopMachine terminal invariant (GREEN->AMBER->RED, advance/transition_to/reset from RED)
- Gate boundary invariants (valid->ALLOW, invalid->DENY, legacy PASS->HOLD)

### Changed
- `.github/workflows/ci.yml` — added `python -m pytest tests/ -v` step
- `CHANGELOG.md` — this entry

### Notes
- **No runtime semantic change**. Only docs/tests/security policy/CI wiring.
- No edits to `stop_machine.py`, `gate.py`, `rules.py`, `envelope_parser.py`, or `primitives/*`


## [geometry-layer-v0] — 2026-02-23

### Added
Expand Down
69 changes: 69 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Security Policy for stop-machine

## Scope

**stop-machine** is a deterministic three-state stop controller (`GREEN -> AMBER -> RED`).
RED is terminal. No network I/O, no external dependencies in core primitives.

This repo also contains the **envelope-gate** conformance primitive
(`primitives/envelope-gate/`), which evaluates structured envelopes against
frozen protocol rules. It is equally deterministic and side-effect-free.

## Threat Model

This project is a **governance primitive**, not a networked service.
The primary risks are:

| Risk | Category |
|------|----------|
| Misconfiguration of EXIT_ENUM or gate mappings | Governance |
| Drift between docs, EXIT_ENUM, and VALID_EXIT_VALUES | Integrity |
| Unauthorised mutation of terminal state (RED) | Safety |
| Truncated or malformed test files passing CI | Build |

This repo does **not** handle authentication, transport security, rate limiting,
or logging. Integrators must add those layers.

## Invariants That Security Depends On

1. **EXIT_ENUM frozen set**: `{ALLOW, HOLD, DENY, SILENCE}` (per EXIT_ENUM_ERRATA v0.1)
2. **VALID_EXIT_VALUES** must be identical to the set of `Exit` enum values
3. **`_classify_exit`** must never emit a value outside that frozen set
4. **RED is terminal**: `advance()`, `transition_to()`, and `reset()` all raise
`TerminalStateError` when the machine is in RED
5. **18 conformance rules** in `ALL_RULES` are ordered: R0 structural first,
then enum validation, then policy

## Canonical Pin

Per `CANONICAL.md`:
- `stop_machine@3780882`
- `authority_gate@70ed2c9`

Any commit that changes runtime semantics must update the canonical pin.

## Reporting Vulnerabilities

- **Non-sensitive bugs**: Open a GitHub Issue
- **Security-sensitive issues**: Use the GitHub Security tab
(Security > Advisories > New draft advisory) or email the maintainer directly

Please include:
- Description of the issue
- Steps to reproduce
- Expected vs actual behaviour
- Which invariant (if any) is violated

## Supported Versions

| Version | Supported |
|---------|----------|
| main (HEAD) | Yes |
| Tagged releases | Yes |
| Forks | No |

## Non-Goals

- No guarantees for external services, LLM integrations, or downstream repos
- No promise of uptime or availability (this is a library, not a service)
- No security review of third-party code that imports this primitive
76 changes: 76 additions & 0 deletions docs/architecture-diagram.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Architecture Diagram: stop-machine

## Component Map

```
stop-machine/
|
|-- stop_machine.py # Root primitive: 3-state stop controller
| State: GREEN -> AMBER -> RED (terminal)
| Classes: StopMachine, State, TerminalStateError, InvalidTransitionError
|
|-- primitives/
| |-- envelope-gate/ # Conformance gate primitive
| | |-- envelope_parser.py # Parses raw markdown -> Envelope dataclass
| | |-- rules.py # 18 conformance rules + Exit enum + Violation
| | |-- gate.py # evaluate() -> GateResult (ALLOW|HOLD|DENY|SILENCE)
| | |-- cli.py # CLI entry point
| | |-- conftest.py # pytest isolation for sibling imports
| | '-- test_envelope_gate.py
| |
| |-- authority-gate-v0/ # Stub (no runtime code)
| |-- stop-machine-v0/ # Stub (no runtime code)
| |-- ambiguity-detector/ # Stub
| |-- consistency-tester/ # Stub
| |-- invariant-lock/ # Stub
| '-- no-optimisation-wrapper/ # Stub
|
|-- tests/ # Cross-cutting invariant tests
| |-- __init__.py
| '-- test_invariant_enforcement.py
|
|-- docs/
| |-- runtime-trace.md # Gate decision flow documentation
| |-- architecture-diagram.md # This file
| |-- geometry_export_spec_v0.1.md
| '-- GEOMETRY_LAYER_V0_FIT_REPORT_dddc878.md
|
|-- test_stop_machine.py # Root-level StopMachine tests
|-- CANONICAL.md # Canonical commit pins
|-- CHANGELOG.md
|-- SECURITY.md
|-- GATE_LOG_v0.1.md
|-- README.md
'-- LICENSE
```

## Data Flow

```
+-------------------+
raw markdown ---->| envelope_parser |----> Envelope
+-------------------+ |
v
+-------------------+ +------------+
| rules.py |<--| gate.py |
| (18 rules, | | evaluate() |
| Exit enum, | +------------+
| Violation) | |
+-------------------+ v
GateResult
(exit: ALLOW|HOLD|DENY|SILENCE)
|
v
[optional: Geometry Layer
JSONL emission if
GEOMETRY_LOG_PATH set]
```

## Key Invariants

1. **StopMachine**: RED is terminal. No method can transition out of RED.
2. **Exit enum**: Exactly `{ALLOW, HOLD, DENY, SILENCE}`. No other values.
3. **VALID_EXIT_VALUES**: Must equal `{e.value for e in Exit}`.
4. **_classify_exit**: Output is always a member of `VALID_EXIT_VALUES`.
5. **ALL_RULES**: 18 rules, ordered R0 -> enum -> blank-field -> policy.
6. **Determinism**: Same input -> same output. Always.
64 changes: 64 additions & 0 deletions docs/runtime-trace.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Runtime Trace: Envelope Gate Decision Flow

This document describes the deterministic decision flow when an envelope
is evaluated by the conformance gate (`primitives/envelope-gate/gate.py`).

## Overview

```
Envelope (raw text)
-> parse_envelope() [envelope_parser.py]
-> Envelope dataclass
-> evaluate(envelope) [gate.py]
-> for rule in ALL_RULES: [rules.py, 18 rules]
rule(envelope) -> None | Violation
-> _classify_exit(violations)
-> GateResult
```

## Exit Decision Mapping

| Condition | Exit |
|-----------|------|
| No violations | ALLOW |
| Any `R0_*` structural violation | DENY |
| Non-structural violation (enum, policy) | HOLD |
| Envelope not addressed to gate | SILENCE |

**SILENCE** is emitted when the envelope is not relevant to this gate instance.
It is a valid, non-error exit that means the gate has no opinion on the message.
SILENCE does not indicate suppression or censorship; it indicates routing irrelevance.

## Rule Evaluation Order

Rules are evaluated in registry order (`ALL_RULES` list in `rules.py`):

1. **R0 structural pre-checks** (9 rules): header, msg_id, ts_utc, sender,
recipient, mode, scope, goal, RETURN block
2. **Enum validation** (7 rules): valid sender/recipient agents, valid mode,
valid scope, valid exit code, valid output type/format
3. **Blank-field clarification** (1 rule): response envelopes must set exit
4. **Policy rules** (1 rule): no self-approve execution

## Evaluation Policies

- **FIRST_FAIL** (default, frozen at msg-0003): halts on first violation
- **ACCUMULATE_ALL**: collects all violations (diagnostic use only)

## GateResult Fields

| Field | Type | Description |
|-------|------|-------------|
| `msg_id` | str | Envelope message ID |
| `exit` | str | ALLOW, HOLD, DENY, or SILENCE |
| `violations` | list | Violation objects found |
| `rules_checked` | int | How many rules were evaluated |
| `rules_total` | int | Total rules in registry |
| `passed` | bool | True if exit == ALLOW |

## Determinism Guarantee

Given the same envelope text, the gate will always produce the same
GateResult. No randomness, no wall-clock reads, no network calls.
The optional Geometry Layer emission hook is best-effort and cannot
alter the exit decision.
1 change: 1 addition & 0 deletions tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# tests package
Loading
Loading