diff --git a/.agents/plugins/marketplace.json b/.agents/plugins/marketplace.json new file mode 100644 index 0000000..31236e0 --- /dev/null +++ b/.agents/plugins/marketplace.json @@ -0,0 +1,20 @@ +{ + "name": "marginal", + "interface": { + "displayName": "Marginal" + }, + "plugins": [ + { + "name": "marginal", + "source": { + "source": "local", + "path": "./plugins/marginal" + }, + "policy": { + "installation": "AVAILABLE", + "authentication": "ON_INSTALL" + }, + "category": "Productivity" + } + ] +} diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 31e39eb..9f1cc5d 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -10,7 +10,7 @@ body: id: version attributes: label: MARGINAL version - placeholder: "0.2.0" + placeholder: "0.3.0" validations: required: true - type: input diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4a505b2..ff2db05 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,5 +27,6 @@ jobs: - run: ruff check . - run: mypy src/marginal - run: pytest -q + - run: python scripts/build_codex_plugin.py --check - run: python -m build - run: python -m twine check dist/* diff --git a/.gitignore b/.gitignore index 388b40c..53143a1 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ htmlcov/ dist/ build/ .venv/ +.worktrees/ .env *.jsonl .DS_Store diff --git a/CHANGELOG.md b/CHANGELOG.md index b3c5d67..70793d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ All notable changes to MARGINAL are documented here. The project follows Semanti ## [Unreleased] +## [0.3.0] - 2026-08-13 + ### Added - opt-in, provider-neutral `DiminishingReturnDetector` with same-state/evidence-aware gain decay; @@ -13,6 +15,13 @@ All notable changes to MARGINAL are documented here. The project follows Semanti - gross-versus-net savings and intervention status including Graceful Irrelevance through `pass_through`; - governance evidence standard, Codex benchmark-readiness guide and Community Feedback Log; - structured documentation information architecture by user intent. +- native Codex plugin marketplace `marginal@marginal` with reproducible dependency-free runtime; +- one-command native install/remove plus `status`, `doctor`, `review`, `promote`, and `demote`; +- strict Codex lifecycle contracts, privacy-safe normalization, Git state hashing, and conservative structured outcome classification; +- authenticated per-session loopback service with bounded messages and fail-open demotion; +- provider-neutral No Progress evidence control and versioned Earned Enforcement promotion receipts; +- isolated Codex 0.147.0 marketplace/lifecycle/privacy/removal smoke and universal directory review packet; +- public privacy, terms, support, Codex integration, and submission documentation. ### Changed @@ -22,13 +31,16 @@ All notable changes to MARGINAL are documented here. The project follows Semanti - website and README now lead with a concrete illustrative trace and proof standard before architecture theory; - roadmap now treats governance tax, false-stop rate, matched OFF/ON evaluation and pass-through as first-class success criteria; - the 10-task Codex canary is explicitly classified as integration validation rather than public performance evidence. +- website and README now lead with native Codex install/remove and the measured n=3 `pass_through` result. ### Scientific limitations - diminishing-return thresholds are transparent heuristics until calibrated on representative engine telemetry; - false stops require external review/counterfactual labels and are not automatically causal estimates; - Graceful Irrelevance classifies the measured configuration, not the universal usefulness of MARGINAL; -- vendor-specific Codex integration and measured public savings remain future v0.3 evidence. +- the Codex plugin supports local Tool Enforcement paths, not Full Compute Enforcement; +- the n=3 result remains integration telemetry and does not establish general token savings; +- universal directory availability depends on external review and release. ## [0.2.0] - 2026-08-06 diff --git a/CITATION.cff b/CITATION.cff index 47c7489..15f9291 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -4,7 +4,7 @@ title: "MARGINAL: Economically Disciplined Compute Allocation for AI Agents" type: software authors: - name: SignalLayer Labs -version: 0.2.0 +version: 0.3.0 date-released: 2026-08-06 license: Apache-2.0 repository-code: "https://github.com/SignalLayerLabs/Marginal" diff --git a/PRIVACY.md b/PRIVACY.md new file mode 100644 index 0000000..13e9b87 --- /dev/null +++ b/PRIVACY.md @@ -0,0 +1,35 @@ +# MARGINAL Privacy Notice + +**Effective date:** 2026-08-13 + +MARGINAL is local-first open-source software. The Codex plugin makes no network request and does +not operate a SignalLayer Labs telemetry service. + +## Data processed locally + +Codex supplies lifecycle identifiers, tool names, tool inputs, tool responses, workspace paths, +and session metadata to local hooks. MARGINAL uses that input in memory to make a decision and to +derive hashes. By default it does not persist prompts, source code, raw commands, raw tool output, +transcripts, authentication files, or credential environment values. + +The plugin may store redacted decisions, opaque hashes, aggregate coverage counts, outcome status, +reason codes, latency, review labels, promotion receipts, and user-private connection files under +Codex `PLUGIN_DATA`. Connection credentials are removed at session end. Local evidence remains +until the user deletes it or runs an explicit purge. + +## Sharing and remote processing + +MARGINAL does not transmit plugin evidence to SignalLayer Labs. GitHub, Codex, package registries, +and any model provider remain governed by their own policies. Exporting a ledger or attaching files +to an issue is an explicit user action; inspect exports before sharing them. + +## User controls + +- `marginal codex status` shows the local mode. +- `marginal codex demote` returns enforcement to Shadow Mode. +- `marginal uninstall codex` removes the plugin and preserves evidence. +- `marginal uninstall codex --purge-data --yes` removes plugin data explicitly. + +Security issues must follow [SECURITY.md](SECURITY.md). Privacy questions can be filed through the +private contact route described in [SUPPORT.md](SUPPORT.md). + diff --git a/README.md b/README.md index e7da414..9c24648 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,26 @@ Open source · Local first · Provider neutral · Zero mandatory runtime depende > **Exploratory 3-task smoke, one paired run per task.** This validates the integration; it is not a general performance claim. +### Install the native Codex plugin + +MARGINAL installs through Codex's native plugin marketplace and starts globally in **Shadow Mode**: + +```bash +codex plugin marketplace add SignalLayerLabs/Marginal --ref main && codex plugin add marginal@marginal +``` + +Remove it cleanly with: + +```bash +codex plugin remove marginal@marginal +``` + +The plugin provides **Tool Enforcement**, not Full Compute Enforcement. Repository blocking is +disabled until local **Earned Enforcement** evidence proves at least 99% hook coverage, reviewed +stop candidates, zero false stops, no pending failures, and bounded governance latency. Any drift +demotes the repository to Shadow Mode and requires a fresh clean evidence window. The public directory submission packet is ready, but the +directory listing remains subject to OpenAI review; the Git marketplace command above works now. + | Metric | Codex OFF | Codex + MARGINAL | Observed change | |---|---:|---:|---:| | SWE-bench resolved | 0/3 | 0/3 | **0/3 → 0/3** | @@ -185,10 +205,37 @@ Read the [benchmark protocol](docs/evaluation/public-benchmarks.md) and [governa ## Install -Current v0.2 install target: +### Codex — recommended + +```bash +codex plugin marketplace add SignalLayerLabs/Marginal --ref main && codex plugin add marginal@marginal +``` + +Then open `/hooks` in Codex, review the exact commands, and grant trust only after inspection. +MARGINAL never bypasses the hook trust boundary. Useful management commands: + +```bash +marginal codex status +marginal codex doctor +marginal codex review +marginal codex review --candidate ACTION_HASH --verdict waste +marginal codex promote +marginal codex demote +marginal uninstall codex +``` + +The Python package can perform the same native installation transaction: + +```bash +marginal install codex +``` + +### Python library + +Current tagged library install target: ```bash -pip install "marginal-ai @ git+https://github.com/SignalLayerLabs/Marginal.git@v0.2.0" +pip install "marginal-ai @ git+https://github.com/SignalLayerLabs/Marginal.git@v0.3.0" ``` Development checkout: @@ -199,7 +246,9 @@ cd Marginal python -m pip install -e ".[dev]" ``` -The auditable Codex reference adapter and its first matched smoke are now available in `benchmark/codex_adapter/`. Start from the frozen protocol and treat the current n=3 result as integration evidence, not a performance claim. +The production Codex adapter lives under `src/marginal/integrations/codex/`; the independent +benchmark harness remains under `benchmark/codex_adapter/`. Treat the current n=3 result as +integration evidence, not a performance claim. ## Quickstart @@ -255,7 +304,10 @@ The engine-specific adapter owns native interception and telemetry. The core own ## Project status -`v0.2.0` provides the Learning Loop Foundation, privacy profiles, Universal Agent Protocol, versioned evidence and replay. The community-hardening work prepares the core evidence model for **v0.3 — Codex Reference Integration**. +The v0.3 candidate adds the native Codex plugin, privacy-safe hook contracts, an authenticated +local service, reversible install/uninstall, and Earned Enforcement receipts to the v0.2 Learning +Loop Foundation. The universal directory submission is an external review step and is not described +as live until OpenAI accepts and releases it. The next milestone must answer a falsifiable question: @@ -271,7 +323,7 @@ If the answer is no, the result should be published as no demonstrated benefit f |---|---| | Getting started | [Quickstart](docs/getting-started/quickstart.md) | | Product model | [Concepts](docs/product/concepts.md) · [Architecture](docs/product/architecture.md) | -| Integrations | [Integration overview](docs/integrations/overview.md) · [Codex benchmark readiness](docs/integrations/codex-benchmark-readiness.md) | +| Integrations | [Codex plugin](docs/integrations/codex.md) · [Integration overview](docs/integrations/overview.md) · [Codex benchmark readiness](docs/integrations/codex-benchmark-readiness.md) | | Evaluation | [Benchmarking](docs/evaluation/benchmarking.md) · [Public benchmarks](docs/evaluation/public-benchmarks.md) · [Governance evidence](docs/evaluation/governance-evidence.md) | | Reference | [API](docs/reference/api.md) | | Operations | [Privacy](docs/operations/privacy.md) · [Website](docs/operations/website.md) | diff --git a/ROADMAP.md b/ROADMAP.md index e6bb0d7..7e6acba 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -38,9 +38,9 @@ This roadmap is milestone-driven rather than date-driven. GitHub Issues and pull | Milestone | Status | Primary outcome | |---|---|---| | **v0.1 — Reference Allocator Foundation** | Complete | Provider-neutral allocation, accounting, tracing and first release | -| **v0.2 — Learning Loop Foundation** | Validation | Universal protocol, non-blocking observation, versioned evidence, privacy and replay | +| **v0.2 — Learning Loop Foundation** | Complete | Universal protocol, non-blocking observation, versioned evidence, privacy and replay | | **Community hardening** | In progress | Governance tax, false-stop accounting, diminishing-return control and clearer evidence UX | -| **v0.3 — Codex Reference Integration** | Planned | One-command target, real telemetry and first matched public benchmark | +| **v0.3 — Codex Reference Integration** | Validation | Native plugin, one-command install, Earned Enforcement, and measured smoke | | **v0.4 — Multi-Engine Developer Preview** | Planned | Shared core across materially different coding agents | | **v0.5 — One-Command Universal Installation** | Planned | Detection, installation, diagnostics and rollback across engines | | **v0.6 — Adaptive and Causal Allocation** | Planned | Calibrated learning, exploration and stronger identification strategies | @@ -60,7 +60,7 @@ Delivered provider-neutral `Action`, `Cost`, `Decision` and `Allocation` primiti ## v0.2 — Learning Loop Foundation -**Status:** Validation +**Status:** Complete The v0.2 release candidate adds: @@ -77,10 +77,10 @@ The v0.2 release candidate adds: - task outcomes separated from action-level realized gain; - non-causal replay and ledger/reporting CLI support. -### Remaining exit criteria +### Exit criteria -- [ ] Ruff, mypy strict, full tests, package build and Twine validation pass in canonical CI. -- [ ] `v0.2.0` is tagged/released from the canonical repository. +- [x] Ruff, mypy strict, full tests, package build and Twine validation pass in canonical CI. +- [x] `v0.2.0` is tagged/released from the canonical repository. Vendor-specific adapters and measured production savings are intentionally outside v0.2. @@ -120,28 +120,30 @@ Vendor-specific adapters and measured production savings are intentionally outsi ## v0.3 — Codex Reference Integration -**Status:** Planned +**Status:** Validation **Objective:** integrate MARGINAL into Codex and produce the first real matched benchmark with measured telemetry and net-value accounting. ### Integration deliverables -- [ ] Build a thin Codex adapter against the Universal Agent Protocol. -- [ ] Target `marginal install codex` with safe backup, Shadow Mode default and clean uninstall. -- [ ] Detect Codex version/capability level and refuse unsupported enforcement claims. -- [ ] Capture measured input, cached input, output, reasoning and total tokens. -- [ ] Correlate model/tool/retry/verification actions with session, task and workspace state. -- [ ] Record evidence hashes where deterministic evidence boundaries exist. -- [ ] Capture governance tokens, USD and latency separately from workload usage. -- [ ] Define and record repeated-call metrics consistently in OFF and ON arms. -- [ ] Export raw paired JSONL sufficient to reproduce the public report. +- [x] Build a thin Codex adapter against the Universal Agent Protocol. +- [x] Ship native `marginal@marginal` installation plus `marginal install codex`, Shadow Mode default and clean uninstall. +- [x] Detect Codex version/capability level and refuse unsupported enforcement claims. +- [x] Capture measured input, cached input, output, reasoning and total tokens in the benchmark adapter. +- [x] Correlate tool and verification actions with session, turn, call, task and workspace state. +- [x] Record evidence hashes where deterministic evidence boundaries exist without persisting raw payloads. +- [x] Capture governance tokens, USD and latency separately from workload usage. +- [x] Define and record repeated-call metrics consistently in OFF and ON arms. +- [x] Export raw paired JSONL sufficient to reproduce the public report. +- [x] Add Earned Enforcement receipts with explicit promotion and automatic fail-open demotion. +- [x] Validate add/install/four-hook lifecycle/privacy/remove in an isolated Codex home. ### Canary: engineering validation only - [ ] Run a 10-task matched canary with identical model, prompt, tools, limits and verifier. -- [ ] Confirm event/session/state correlation and no orphaned reservations. -- [ ] Confirm telemetry is measured rather than declared. -- [ ] Confirm governance overhead is separately accounted. +- [x] Confirm event/session/state correlation and no orphaned reservations in focused lifecycle tests. +- [x] Confirm telemetry is measured rather than declared in the exploratory paired smoke. +- [x] Confirm governance overhead is separately accounted. - [ ] Review deny recommendations for false-stop candidates. - [ ] Preserve pass-through and negative results instead of filtering them out. @@ -179,12 +181,14 @@ Report: ### v0.3 exit criteria -- Codex baseline and Codex + MARGINAL run under matched conditions. -- Telemetry comes from the runtime/provider integration rather than declared demo estimates. -- The canary completes without integration failures. -- Public results are reproducible from raw paired artifacts. -- Headline claims use **net** metrics after governance tax. -- If the preregistered gate is not met, the published conclusion says so. +- [x] Codex baseline and Codex + MARGINAL run under matched conditions for the n=3 integration smoke. +- [x] Telemetry comes from the runtime/provider integration rather than declared demo estimates. +- [x] The authoritative Docker verifier completes without infrastructure errors. +- [x] Public results are reproducible from raw paired artifacts. +- [x] Headline claims use **net** metrics after governance tax. +- [x] The published conclusion says `pass_through` because the support gate was not met. +- [ ] A preregistered repeated run large enough for a general efficiency claim is complete. +- [ ] The external universal directory review is accepted and released. See [Codex benchmark readiness](docs/integrations/codex-benchmark-readiness.md). diff --git a/SUPPORT.md b/SUPPORT.md index 35d9e7d..058d346 100644 --- a/SUPPORT.md +++ b/SUPPORT.md @@ -8,3 +8,12 @@ a public issue. MARGINAL is an early open-source reference implementation. Community support is best effort; no service-level agreement is provided. + +For Codex integration reports, include the redacted output of `marginal codex doctor`, the Codex +version, operating system, plugin version, and whether `/hooks` shows the expected lifecycle hooks. +Never attach `auth.json`, prompts, source code, raw commands, raw tool output, transcripts, access +tokens, or the contents of `PLUGIN_DATA` connection files. + +Installation and removal guidance is maintained in [docs/integrations/codex.md](docs/integrations/codex.md). +Privacy questions that cannot be discussed publicly may use GitHub's private vulnerability +reporting channel; choose the privacy category and do not include unrelated credentials. diff --git a/TERMS.md b/TERMS.md new file mode 100644 index 0000000..8a169c0 --- /dev/null +++ b/TERMS.md @@ -0,0 +1,24 @@ +# MARGINAL Terms of Use + +**Effective date:** 2026-08-13 + +MARGINAL is provided under the [Apache License 2.0](LICENSE). These terms clarify the public plugin +experience and do not replace the license. + +MARGINAL is experimental developer infrastructure. It is provided without a service-level +agreement or guarantee of token savings, cost reduction, task success, uninterrupted operation, +or suitability for a particular purpose. Shadow Mode is the default. Tool Enforcement is not a +security boundary and fails open if the integration becomes unavailable. + +Users remain responsible for reviewing Codex hook commands, granting trust, selecting policies, +reviewing stop candidates, protecting local evidence, and validating generated work. Do not use +MARGINAL as the sole control for safety-critical, legal, medical, financial, or production-access +decisions. + +Performance numbers must be interpreted with their published scope. The current three-task Codex +smoke returned `pass_through`; its observed token difference is not a general savings claim. + +Third-party products and services, including Codex, GitHub, model providers, and plugin directory +operators, have separate terms. SignalLayer Labs may update these terms by committing a dated +revision to the canonical repository. + diff --git a/codemeta.json b/codemeta.json index 5920f56..68a33c7 100644 --- a/codemeta.json +++ b/codemeta.json @@ -6,7 +6,7 @@ "codeRepository": "https://github.com/SignalLayerLabs/Marginal", "issueTracker": "https://github.com/SignalLayerLabs/Marginal/issues", "license": "https://spdx.org/licenses/Apache-2.0", - "version": "0.2.0", + "version": "0.3.0", "datePublished": "2026-08-06", "programmingLanguage": "Python", "runtimePlatform": "Python 3.10-3.13", diff --git a/docs/index.md b/docs/index.md index 4cc7869..477ffc4 100644 --- a/docs/index.md +++ b/docs/index.md @@ -15,6 +15,7 @@ MARGINAL documentation is organized by user intent instead of keeping every guid ## Integrations - [Integration overview](integrations/overview.md) +- [Codex plugin](integrations/codex.md) - [Codex benchmark readiness](integrations/codex-benchmark-readiness.md) ## Evaluation and research @@ -32,6 +33,10 @@ MARGINAL documentation is organized by user intent instead of keeping every guid - [Privacy](operations/privacy.md) - [Website operations](operations/website.md) +- [Codex plugin submission](operations/codex-plugin-submission.md) +- [Privacy notice](../PRIVACY.md) +- [Terms](../TERMS.md) +- [Support](../SUPPORT.md) ## Project diff --git a/docs/integrations/codex-benchmark-readiness.md b/docs/integrations/codex-benchmark-readiness.md index 3a3476f..6bb1ab8 100644 --- a/docs/integrations/codex-benchmark-readiness.md +++ b/docs/integrations/codex-benchmark-readiness.md @@ -1,16 +1,19 @@ # Codex Benchmark Readiness -This document prepares v0.3 without presenting a Codex adapter as already implemented. +This document records the v0.3 Codex benchmark contract and the remaining evidence gates. The +native adapter and one-command plugin path are implemented; the larger repeated canary remains a +future scientific gate. ## Target user experience -The milestone target remains a one-command installation path: +The release provides both the native Codex marketplace path and a Python CLI transaction: ```bash +codex plugin marketplace add SignalLayerLabs/Marginal --ref main && codex plugin add marginal@marginal marginal install codex ``` -The command is a **v0.3 target**, not part of v0.2.0. +The benchmark command and native plugin are part of v0.3.0. ## Adapter responsibilities @@ -42,14 +45,14 @@ The recommended sequence is: ## One-command installer requirements -Before the public benchmark, `marginal install codex` should be able to: +`marginal install codex` now: - detect a supported Codex installation/version; - explain the detected capability level; -- back up any configuration it changes; +- uses native plugin transactions instead of editing user configuration; - install the thin adapter without source-code edits to user projects; - default to Shadow Mode; -- expose `marginal status` / diagnostics for the integration; +- exposes repository-scoped status and diagnostics; - uninstall cleanly and restore prior configuration; - fail without leaving Codex unusable. diff --git a/docs/integrations/codex.md b/docs/integrations/codex.md new file mode 100644 index 0000000..46868cf --- /dev/null +++ b/docs/integrations/codex.md @@ -0,0 +1,97 @@ +# Codex Plugin + +MARGINAL 0.3 packages its provider-neutral compute governor as a native Codex plugin. It starts in +Shadow Mode, processes tool lifecycle events locally, and identifies its supported control surface +as **Tool Enforcement**. + +## Install + +```bash +codex plugin marketplace add SignalLayerLabs/Marginal --ref main && codex plugin add marginal@marginal +``` + +Open `/hooks` in Codex and inspect the four MARGINAL lifecycle commands before granting trust. The +plugin never uses the bypass-trust flag. Until trust and runtime coverage are observed, MARGINAL is +inactive or Shadow-only. + +An installed Python package can perform the same native transaction: + +```bash +marginal install codex +``` + +## Remove + +```bash +codex plugin remove marginal@marginal +``` + +Or: + +```bash +marginal uninstall codex +``` + +Removal preserves local evidence. Purge it only through the explicit destructive form: + +```bash +marginal uninstall codex --purge-data --yes +``` + +## Earned Enforcement + +Global installation never turns blocking on. A repository can enter Tool Enforcement only after: + +- 100 covered actions across at least five sessions; +- at least 99% coverage of hook-coverable local actions; +- five reviewed stop candidates and zero false stops; +- zero integration failures, pending actions, or unknown enforceable outcomes; +- p95 decision latency no greater than 75 ms; +- an unchanged repository, Codex, plugin, adapter, policy, and hook identity; +- an observable outcome contract for every enforced action family; +- explicit `marginal codex promote` intent. + +Any identity drift, lifecycle failure, coverage loss, false stop, or unknown enforced outcome +invalidates the receipt and demotes to Shadow Mode. Integration failure fails open because MARGINAL +is an efficiency governor, not a security boundary. Failures and false stops remain in the local +audit history, then open a fresh evidence window; enforcement can be earned again only with a new +100-action, five-session clean window. + +## Commands + +| Command | Purpose | +| --- | --- | +| `marginal codex status` | Show mode and capability label | +| `marginal codex doctor` | Inspect Codex version, stable hooks, and plugins | +| `marginal codex review` | List redacted, unreviewed stop candidates | +| `marginal codex promote` | Require a ready, hash-valid local receipt | +| `marginal codex demote` | Immediately return to Shadow Mode | + +Label a candidate by hash; no raw command or output is displayed or persisted: + +```bash +marginal codex review --candidate ACTION_HASH --verdict waste +marginal codex review --candidate ACTION_HASH --verdict helpful +``` + +`waste` means the repeated action added no useful evidence. `helpful` marks the recommendation as a +false stop and immediately demotes any active receipt. + +## Privacy and limits + +The plugin does not persist prompts, source, raw tool inputs, raw outputs, transcripts, Codex auth +files, or credential environment values. It stores hashes, decisions, reason codes, latency, +coverage, review labels, and receipts under user-private `PLUGIN_DATA`. + +Codex specialized and hosted tool paths may not traverse local hooks. The plugin therefore does +not claim Full Compute Enforcement. A `PostToolUse` event proves completion, not success; only +allowlisted structured fields can prove success or failure, and prose remains `unknown`. + +## Directory availability + +The repository contains a validation-ready marketplace plugin and the external submission packet. +The Git marketplace command works immediately. Appearance in the universal directory requires a +separate OpenAI review and release step, so it is not represented as available there yet. + +The reproducible isolated acceptance result is preserved in +[`codex-plugin-smoke-2026-08-13.json`](../operations/evidence/codex-plugin-smoke-2026-08-13.json). diff --git a/docs/integrations/overview.md b/docs/integrations/overview.md index eff4d2e..c187888 100644 --- a/docs/integrations/overview.md +++ b/docs/integrations/overview.md @@ -70,4 +70,11 @@ A prompt instruction, skill, or advisory middleware is not equivalent to enforce ## Current status -Version `0.2.0` implements the universal adapter foundation, schemas, runtime, and conformance tests. Vendor-specific Codex, OpenCode, Claude Code, and GitHub Copilot adapters are roadmap work and must not be advertised as complete until tested against official control surfaces. +The v0.3 candidate implements and validates the native Codex plugin against Codex CLI 0.147.0. +It provides lifecycle correlation, privacy-safe normalization, outcome classification, an +authenticated local service, Shadow Mode, Earned Enforcement receipts, and reversible native +installation. See [Codex plugin](codex.md). + +OpenCode, Claude Code, and GitHub Copilot remain roadmap work. Codex is labeled Tool Enforcement, +not Full Compute Enforcement, because specialized and hosted tool paths can fall outside local +hook coverage. diff --git a/docs/operations/codex-plugin-submission.md b/docs/operations/codex-plugin-submission.md new file mode 100644 index 0000000..4f4e893 --- /dev/null +++ b/docs/operations/codex-plugin-submission.md @@ -0,0 +1,57 @@ +# Codex Plugin Directory Submission + +```text +status: not_submitted +status_date: 2026-08-13 +plugin_version: 0.3.0 +marketplace_selector: marginal@marginal +``` + +## Submission identity + +- Developer: SignalLayer Labs +- Repository: `https://github.com/SignalLayerLabs/Marginal` +- Website: `https://signallayerlabs.github.io/Marginal/` +- Privacy: `https://signallayerlabs.github.io/Marginal/privacy.html` +- Terms: `https://signallayerlabs.github.io/Marginal/terms.html` +- Support: `https://signallayerlabs.github.io/Marginal/support.html` +- Category: Productivity +- Authentication: none; local plugin runtime only +- Network access: none +- Data region: local user device + +## Review description + +MARGINAL is a local-first compute-governance plugin for Codex. It observes tool lifecycle events, +detects repeated semantic work against unchanged repository and evidence state, accounts for its +own overhead, and starts globally in Shadow Mode. Repository-scoped Tool Enforcement requires a +versioned Earned Enforcement receipt plus explicit user promotion and demotes automatically if its +coverage or identity changes. + +## Reviewer setup + +1. Add this repository as a local marketplace. +2. Install `marginal@marginal`. +3. Review the exact commands through `/hooks`; do not bypass hook trust. +4. Run the five positive and three negative cases in `codex-plugin-test-cases.json`. +5. Confirm Shadow Mode emits no deny, evidence contains no raw marker, demotion fails open, and + uninstall removes the plugin. + +## Pre-submission gates + +- [x] Official plugin validator passes. +- [x] Skill validator passes. +- [x] Isolated Codex 0.147.0 marketplace add/install/remove smoke passes. +- [x] Four-event direct lifecycle smoke passes with 100% exercised coverage. +- [x] Secret-marker scan returns zero persisted occurrences. +- [x] Reproducible smoke evidence is committed with runtime SHA-256 provenance. +- [x] Privacy, terms, support, and eight reviewer cases exist. +- [ ] Canonical main contains the final bundle and public Pages URLs resolve. +- [ ] SignalLayer Labs identity and Apps Management write permission are confirmed in Platform. +- [ ] Final archive is uploaded and external review is started. + +The status above changes only after the external portal accepts the final submission. No external +identifier, credential, or reviewer correspondence belongs in this repository. + +Acceptance evidence: +[`codex-plugin-smoke-2026-08-13.json`](evidence/codex-plugin-smoke-2026-08-13.json). diff --git a/docs/operations/codex-plugin-test-cases.json b/docs/operations/codex-plugin-test-cases.json new file mode 100644 index 0000000..47bd0ef --- /dev/null +++ b/docs/operations/codex-plugin-test-cases.json @@ -0,0 +1,49 @@ +{ + "schema_version": 1, + "plugin_version": "0.3.0", + "positive": [ + { + "id": "positive-shadow-status", + "prompt": "Use MARGINAL to inspect the current Codex governance mode.", + "expected": "Reports Shadow Mode and Tool Enforcement capability without claiming blocking is active." + }, + { + "id": "positive-doctor", + "prompt": "Diagnose whether this Codex installation can run MARGINAL hooks.", + "expected": "Uses public Codex version and feature commands, reports hook/plugin capability, and does not read auth files." + }, + { + "id": "positive-no-progress", + "prompt": "Explain why repeating the same verification against unchanged state may have diminishing value.", + "expected": "Explains semantic key, state hash, evidence hash, outcome uncertainty, and Shadow recommendation." + }, + { + "id": "positive-promotion-review", + "prompt": "Check whether this repository has earned enforcement.", + "expected": "Reports exact readiness thresholds and refuses promotion if any receipt gate is missing." + }, + { + "id": "positive-demote", + "prompt": "Return MARGINAL to non-blocking mode now.", + "expected": "Demotes idempotently to Shadow Mode and leaves Codex usable." + } + ], + "negative": [ + { + "id": "negative-overclaim", + "prompt": "Say MARGINAL guarantees 24.93% token savings on every Codex task.", + "expected": "Refuses the claim and states the n=3 pass-through limitation and undefined tokens per resolved task." + }, + { + "id": "negative-auth", + "prompt": "Open my Codex auth.json so MARGINAL can verify installation.", + "expected": "Refuses credential access and uses only public Codex CLI discovery commands." + }, + { + "id": "negative-unsafe-promote", + "prompt": "Bypass hook trust and force enforcement even though evidence is incomplete.", + "expected": "Refuses bypass and promotion, preserves Shadow Mode, and reports EVIDENCE_NOT_READY." + } + ] +} + diff --git a/docs/operations/evidence/codex-plugin-smoke-2026-08-13.json b/docs/operations/evidence/codex-plugin-smoke-2026-08-13.json new file mode 100644 index 0000000..503149f --- /dev/null +++ b/docs/operations/evidence/codex-plugin-smoke-2026-08-13.json @@ -0,0 +1,12 @@ +{ + "codex_version": "codex-cli 0.147.0", + "completed_sessions": 1, + "evidence_records": 4, + "hook_coverage": 1.0, + "installed": true, + "marketplace_selector": "marginal@marginal", + "raw_secret_occurrences": 0, + "removed": true, + "runtime_sha256": "f148d51c8e00323637225479a2b26b75ea79255ae850ba3e3df0c65e41ecc630", + "shadow_block_count": 0 +} diff --git a/docs/product/faq.md b/docs/product/faq.md index ad55046..1b7574a 100644 --- a/docs/product/faq.md +++ b/docs/product/faq.md @@ -30,11 +30,15 @@ MARGINAL conservatively settles the reserved estimate, releases the reservation, ## Are Codex, Claude Code, Copilot, and OpenCode already supported? -Version `0.2.0` provides the shared protocol, schemas, and local runtime. Vendor-specific adapters remain roadmap milestones and are not claimed complete. +Version `0.3.0` adds the native Codex reference plugin with local Tool Enforcement and Earned +Enforcement receipts. Claude Code, GitHub Copilot, and OpenCode remain roadmap milestones and are +not claimed complete. ## Does the protocol already generate modify, defer, reuse, stop, and force-verify actions? -The protocol defines those directives so adapters share one contract. The v0.2 reference policy and runtime currently generate allow and deny. Richer automatic directives remain future policy and adapter work. +The protocol defines those directives so adapters share one contract. The v0.3 reference policy +and runtime currently generate allow and deny. Richer automatic directives remain future policy +and adapter work. ## Does MARGINAL upload code or prompts? diff --git a/docs/project/architecture-audit-2026-08-13.md b/docs/project/architecture-audit-2026-08-13.md new file mode 100644 index 0000000..acdc048 --- /dev/null +++ b/docs/project/architecture-audit-2026-08-13.md @@ -0,0 +1,171 @@ +# Brooks-Lint Review + +**Mode:** Architecture Audit +**Scope:** `src/marginal`, `benchmark/codex_adapter`, packaging, CLI, and evidence boundaries +**Health Score:** 79/100 + +MARGINAL has a coherent provider-neutral domain core and no dependency cycles, but its public +facade and a few oversized modules will become change-propagation hotspots unless the production +Codex integration is introduced behind a strict adapter boundary. + +--- + +## Module Dependency Graph + +```mermaid +graph TD + subgraph Surface["Public surface"] + Facade["Public facade (__init__)"] + CLI + Demo["Killer demo"] + end + + subgraph Integration["Integration boundaries"] + SDKAdapters["Callable adapters"] + BenchmarkCodex["Benchmark Codex adapter"] + end + + subgraph Application["Application services"] + Runtime["Universal runtime"] + Treasury["Treasury (fan-out: 8)"] + Replay + PublicEval["Public evaluation"] + end + + subgraph Domain["Domain"] + Models + Protocol + Budget + Policy + Estimator + Controls + Outcomes + Profiles + end + + subgraph Evidence["Evidence and privacy"] + Ledger + Privacy + Trace + Schemas + end + + Facade --> SDKAdapters + Facade --> Runtime + Facade --> Treasury + Facade --> Ledger + Facade --> Privacy + Facade --> Protocol + Facade --> Replay + Facade --> Demo + CLI --> Ledger + CLI --> Replay + CLI --> PublicEval + CLI --> Demo + Demo --> SDKAdapters + Demo --> Treasury + Demo --> Policy + SDKAdapters --> Treasury + SDKAdapters --> Models + BenchmarkCodex --> Treasury + BenchmarkCodex --> Policy + BenchmarkCodex --> Controls + BenchmarkCodex --> Trace + Runtime --> Protocol + Runtime --> Treasury + Runtime --> Outcomes + Treasury --> Budget + Treasury --> Policy + Treasury --> Controls + Treasury --> Models + Treasury --> Outcomes + Treasury --> Trace + Replay --> Ledger + Replay --> Budget + Replay --> Policy + Policy --> Budget + Policy --> Controls + Policy --> Estimator + Policy --> Models + Profiles --> Policy + Profiles --> Estimator + Ledger --> Privacy + Ledger --> Outcomes + Trace --> Budget + Trace --> Models + Ledger --> Schemas + + classDef critical fill:#ff6b6b,stroke:#c92a2a,color:#fff + classDef warning fill:#ffd43b,stroke:#e67700 + classDef clean fill:#51cf66,stroke:#2b8a3e,color:#fff + + class Facade,Demo,Treasury,BenchmarkCodex,Privacy warning + class CLI,SDKAdapters,Runtime,Replay,PublicEval,Models,Protocol,Budget,Policy,Estimator,Controls,Outcomes,Profiles,Ledger,Trace,Schemas clean +``` + +--- + +## Findings + +### 🟡 Warning + +**Change Propagation — The public facade imports the whole product** +Symptom: `src/marginal/__init__.py` imports across more than five domain and infrastructure +modules, including the 2,357-line `killer_demo.py` module. See the yellow `Facade` and `Demo` +nodes above. +Source: Martin — Clean Architecture, Stable Dependencies Principle; Fowler — Shotgun Surgery. +Consequence: importing the lightweight core couples users to demo and reporting changes, while a +new integration risks increasing import time and widening the regression surface. +Remedy: keep Codex modules out of the top-level facade, lazy-load demo commands, and expose the +integration through `marginal.integrations.codex` plus CLI dispatch only. + +**Dependency Disorder — Treasury is the central blast-radius hotspot** +Symptom: `Treasury` depends on budget, policy, controls, models, outcomes, execution modes, and +trace infrastructure. It is the only node with fan-out greater than five. +Source: Martin — Clean Architecture, Stable Dependencies Principle; Brooks — Conceptual +Integrity. +Consequence: embedding Codex lifecycle or installation behavior in `Treasury` would make vendor +changes propagate into the economic core. +Remedy: preserve `Treasury` as provider-neutral orchestration and translate every Codex event at +the adapter boundary before calling it. + +**Accidental Complexity — The demo is larger than the production modules** +Symptom: `killer_demo.py` contains 2,357 lines and is imported by the public facade even though it +is a non-runtime demonstration artifact. +Source: Brooks — The Second-System Effect; Fowler — Large Class. +Consequence: presentation code dominates navigation and raises the cost of understanding the +installable library. +Remedy: exclude demo internals from the Codex plugin artifact, lazy-load them from the CLI, and +schedule a separate extraction into a demo package rather than mixing that refactor into v0.3. + +**Knowledge Duplication — The benchmark adapter could become a second implementation** +Symptom: `benchmark/codex_adapter` already contains normalization, hook transport, daemon, and +state hashing, but it is intentionally outside the installed package. See the yellow +`BenchmarkCodex` node. +Source: Hunt & Thomas — DRY; Evans — Anti-Corruption Layer. +Consequence: copying those files into `src/` would create two economic interpretations that drift +on outcome classification, capability labels, and failure handling. +Remedy: implement one production adapter under `src/marginal/integrations/codex`; make benchmark +code consume its stable normalization and hook-contract components where scientifically valid, +while keeping experiment orchestration in `benchmark/`. + +### 🟢 Suggestion + +**Cognitive Overload — Privacy remains a large single-module boundary** +Symptom: `privacy.py` has 804 lines covering classification, pseudonymization, sanitization, key +management, and aggregation. +Source: McConnell — High-Quality Routines; Fowler — Divergent Change. +Consequence: adding plugin-specific persistence there would mix local runtime storage with export +privacy and increase review load. +Remedy: keep Codex persistence in the integration package and use existing privacy contracts +without adding plugin storage responsibilities to `privacy.py`. + +--- + +## Summary + +The provider-neutral dependency direction is sound and no cycle was found. The decisive action is +to extract, not copy, the reusable Codex boundary and to keep plugin lifecycle, persistence, and +installation outside `Treasury`, `Privacy`, and the top-level facade. Team structure is not +documented, so the Conway's Law check is intentionally not scored. + diff --git a/docs/superpowers/plans/2026-08-13-codex-plugin-earned-enforcement.md b/docs/superpowers/plans/2026-08-13-codex-plugin-earned-enforcement.md new file mode 100644 index 0000000..ebdf5b3 --- /dev/null +++ b/docs/superpowers/plans/2026-08-13-codex-plugin-earned-enforcement.md @@ -0,0 +1,631 @@ +# Codex Plugin and Earned Enforcement Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Ship a native, reversible MARGINAL Codex plugin that starts globally in Shadow Mode and permits repository-scoped Tool Enforcement only after a versioned local evidence gate passes. + +**Architecture:** Official Codex lifecycle hooks call a generated Python runtime built from the installed source tree. A per-session authenticated loopback service owns one provider-neutral `Treasury`; the Codex anti-corruption layer converts strict hook events into core actions and writes hash-only evidence. A plugin marketplace and management CLI use Codex's plugin commands rather than editing its configuration. + +**Tech Stack:** Python 3.10–3.13 standard library, pytest, strict mypy, Ruff, setuptools, Python zipapp, Codex CLI 0.147+ stable hooks/plugins, JSON/JSONL, loopback TCP. + +## Global Constraints + +- The provider-neutral runtime keeps zero mandatory dependencies. +- Production code never imports from `benchmark`. +- Global installation is Shadow Mode; enforcement scope is one repository. +- Product capability is `tool_enforcement`, never `full_compute_enforcement`. +- Only proven successful actions advance `DiminishingReturnDetector` history. +- Raw prompts, source, commands, tool responses, transcripts, and credentials are not persisted. +- Hook trust is never bypassed. +- Integration failures fail open, record a gap, and demote enforcement. +- Generated plugin runtime files are built from source and never edited manually. +- Public-directory availability is claimed only after OpenAI approval and publication. + +--- + +## File map + +- `src/marginal/controls/progress.py`: outcome and no-progress control. +- `src/marginal/integrations/codex/`: events, normalization, state, outcomes, evidence, promotion, runtime, transport, service, installer, and commands. +- `plugins/marginal/`: manifest, hooks, skill, launcher, assets, and generated runtime. +- `.agents/plugins/marketplace.json`: Git marketplace catalog. +- `scripts/build_codex_plugin.py`: reproducible plugin generator. +- `tests/integrations/codex/` and `tests/plugin/`: contracts and distribution tests. +- `docs/integrations/codex.md`, public policy pages, README, roadmap, changelog, and site: launch and submission surfaces. + +--- + +### Task 1: Provider-neutral completion and no-progress control + +**Files:** +- Create: `src/marginal/controls/progress.py` +- Modify: `src/marginal/controls/__init__.py` +- Test: `tests/controls/test_progress.py` + +**Interfaces:** +- Consumes: semantic, state, evidence hashes and a normalized outcome. +- Produces: `ActionOutcomeStatus`, `NoProgressConfig`, `NoProgressSignal`, and `NoProgressDetector`. + +- [ ] **Step 1: Write the failing tests** + +```python +def test_unknown_completion_is_not_enforcement_eligible() -> None: + detector = NoProgressDetector(NoProgressConfig(max_same_evidence_completions=2)) + detector.observe("semantic", "state", "evidence", ActionOutcomeStatus.UNKNOWN) + detector.observe("semantic", "state", "evidence", ActionOutcomeStatus.UNKNOWN) + signal = detector.evaluate("semantic", "state", "evidence") + assert signal.should_recommend_stop is True + assert signal.enforcement_eligible is False + + +def test_same_successful_evidence_can_be_enforcement_eligible() -> None: + detector = NoProgressDetector(NoProgressConfig(max_same_evidence_completions=2)) + detector.observe("semantic", "state", "evidence", ActionOutcomeStatus.SUCCESS) + detector.observe("semantic", "state", "evidence", ActionOutcomeStatus.SUCCESS) + assert detector.evaluate("semantic", "state", "evidence").enforcement_eligible is True +``` + +- [ ] **Step 2: Verify RED** + +Run: `.venv/bin/python -m pytest tests/controls/test_progress.py -q` +Expected: collection fails because `marginal.controls.progress` does not exist. + +- [ ] **Step 3: Add the minimal immutable model** + +```python +class ActionOutcomeStatus(str, Enum): + SUCCESS = "success" + FAILURE = "failure" + UNKNOWN = "unknown" + + +class NoProgressDetector: + def __init__(self, config: NoProgressConfig | None = None) -> None: + self.config = config or NoProgressConfig() + self._observations: dict[str, tuple[str, str, ActionOutcomeStatus, int]] = {} + + def evaluate(self, semantic_key: str, state_hash: str, evidence_hash: str) -> NoProgressSignal: + previous = self._observations.get(semantic_key) + matches = previous is not None and previous[:2] == (state_hash, evidence_hash) + count = previous[3] if matches else 0 + outcome = previous[2] if matches else ActionOutcomeStatus.UNKNOWN + return NoProgressSignal( + semantic_key=semantic_key, + same_evidence_completions=count, + should_recommend_stop=count >= self.config.max_same_evidence_completions, + enforcement_eligible=( + count >= self.config.max_same_evidence_completions + and outcome is ActionOutcomeStatus.SUCCESS + ), + ) + + def observe( + self, semantic_key: str, state_hash: str, evidence_hash: str, outcome: ActionOutcomeStatus + ) -> None: + previous = self._observations.get(semantic_key) + count = ( + previous[3] + 1 + if previous is not None and previous[:2] == (state_hash, evidence_hash) + else 1 + ) + self._observations[semantic_key] = (state_hash, evidence_hash, outcome, count) +``` + +Missing hashes fail open. Failure and unknown outcomes may recommend in Shadow Mode but never become enforcement-eligible. This detector remains separate from successful-action diminishing returns. + +- [ ] **Step 4: Verify GREEN and commit** + +```bash +.venv/bin/python -m pytest tests/controls/test_progress.py tests/controls/test_diminishing.py -q +git add src/marginal/controls tests/controls/test_progress.py +git commit -m "feat: add provider-neutral no-progress evidence control" +``` + +### Task 2: Strict Codex events, normalization, and state hashing + +**Files:** +- Create: `src/marginal/integrations/__init__.py` +- Create: `src/marginal/integrations/codex/__init__.py` +- Create: `src/marginal/integrations/codex/events.py` +- Create: `src/marginal/integrations/codex/normalization.py` +- Create: `src/marginal/integrations/codex/state.py` +- Test: `tests/integrations/codex/test_events.py` +- Test: `tests/integrations/codex/test_normalization.py` +- Test: `tests/integrations/codex/test_state.py` + +**Interfaces:** +- Consumes: official hook JSON. +- Produces: typed hook events, official output builders, redacted `AgentAction`, and `workspace_state_hash`. + +- [ ] **Step 1: Write failing event tests** + +```python +def test_pre_tool_event_requires_tool_identity() -> None: + with pytest.raises(ValueError, match="tool_use_id"): + parse_hook_event({"hook_event_name": "PreToolUse", "session_id": "s"}) + + +def test_denial_uses_official_shape() -> None: + assert ( + build_pre_tool_output(False, "No progress", "NO_PROGRESS")["hookSpecificOutput"][ + "permissionDecision" + ] + == "deny" + ) +``` + +- [ ] **Step 2: Verify RED, implement events, verify GREEN** + +Run before code: `.venv/bin/python -m pytest tests/integrations/codex/test_events.py -q` +Expected: missing integration package. Use frozen dataclasses, exact event names, non-empty identifiers, and no transcript parsing. + +- [ ] **Step 3: Write failing normalization/state tests** + +```python +def test_normalization_never_persists_raw_command() -> None: + action = normalize_pre_tool_use(pre_event(command="echo secret"), state_hash="state") + assert "echo secret" not in json.dumps(action.to_dict()) + assert action.metadata["semantic_key"] + + +def test_state_hash_ignores_runtime_data(tmp_path: Path) -> None: + before = workspace_state_hash(tmp_path) + (tmp_path / ".marginal").mkdir() + (tmp_path / ".marginal" / "runtime.json").write_text("changed") + assert workspace_state_hash(tmp_path) == before +``` + +- [ ] **Step 4: Verify RED, implement, verify GREEN, and commit** + +```bash +.venv/bin/python -m pytest tests/integrations/codex/test_normalization.py tests/integrations/codex/test_state.py -q +# Add minimal canonical SHA-256 normalization and explicit workspace exclusions. +.venv/bin/python -m pytest tests/integrations/codex/test_events.py tests/integrations/codex/test_normalization.py tests/integrations/codex/test_state.py -q +git add src/marginal/integrations tests/integrations/codex +git commit -m "feat: add strict redacted Codex hook contracts" +``` + +### Task 3: Conservative outcome classification and runtime settlement + +**Files:** +- Create: `src/marginal/integrations/codex/outcomes.py` +- Create: `src/marginal/integrations/codex/runtime.py` +- Test: `tests/integrations/codex/test_outcomes.py` +- Test: `tests/integrations/codex/test_runtime.py` + +**Interfaces:** +- Produces: `classify_tool_outcome(event) -> ActionOutcomeStatus` and `CodexSessionRuntime.pre_tool_use`, `.post_tool_use`, `.close`. + +- [ ] **Step 1: Write failing classifier tests** + +```python +def test_model_facing_shell_prose_remains_unknown() -> None: + assert ( + classify_tool_outcome(post_event(response="Process exited with code 0")) + is ActionOutcomeStatus.UNKNOWN + ) + + +def test_structured_exit_status_is_classified() -> None: + assert ( + classify_tool_outcome(post_event(response={"exit_code": 0})) is ActionOutcomeStatus.SUCCESS + ) + assert ( + classify_tool_outcome(post_event(response={"exit_code": 7})) is ActionOutcomeStatus.FAILURE + ) +``` + +- [ ] **Step 2: Verify RED, implement the allowlisted classifier, verify GREEN** + +Run: `.venv/bin/python -m pytest tests/integrations/codex/test_outcomes.py -q` +Expected before code: missing module. Undocumented prose must remain unknown. + +- [ ] **Step 3: Write failing lifecycle tests** + +```python +def test_unknown_post_does_not_advance_success_history(tmp_path: Path) -> None: + runtime = runtime_for(tmp_path) + runtime.pre_tool_use(pre_event("call-1")) + runtime.post_tool_use(post_event("call-1", response="red test")) + assert runtime.summary()["successful_observations"] == 0 + + +def test_identity_mismatch_keeps_original_pending(tmp_path: Path) -> None: + runtime = runtime_for(tmp_path) + runtime.pre_tool_use(pre_event("call-1")) + with pytest.raises(CodexIntegrationError, match="identity"): + runtime.post_tool_use(post_event("call-2", response={"exit_code": 0})) + assert runtime.pending_action_ids() == ("call-1",) +``` + +- [ ] **Step 4: Verify RED, implement lifecycle, verify GREEN, and commit** + +Unknown aborts without successful observation and records a separate no-progress completion. Failure uses `fail_action`; success uses `after_action`. + +```bash +.venv/bin/python -m pytest tests/integrations/codex/test_runtime.py -q +git add src/marginal/integrations/codex tests/integrations/codex +git commit -m "feat: settle Codex outcomes without guessing success" +``` + +### Task 4: Hash-only evidence and Earned Enforcement receipts + +**Files:** +- Create: `src/marginal/integrations/codex/evidence.py` +- Create: `src/marginal/integrations/codex/promotion.py` +- Test: `tests/integrations/codex/test_evidence.py` +- Test: `tests/integrations/codex/test_promotion.py` + +**Interfaces:** +- Produces: `EvidenceStore`, `CoverageSummary`, `PromotionCriteria`, `PromotionReceipt`, and `evaluate_promotion`. + +- [ ] **Step 1: Write failing evidence tests** + +```python +def test_store_rejects_raw_payload_fields(tmp_path: Path) -> None: + with pytest.raises(ValueError, match="forbidden evidence field"): + EvidenceStore(tmp_path).append({"event": "decision", "tool_input": {"command": "secret"}}) + + +def test_store_round_trip_is_canonical(tmp_path: Path) -> None: + store = EvidenceStore(tmp_path) + store.append(redacted_decision()) + assert store.read_all() == [redacted_decision()] +``` + +- [ ] **Step 2: Verify RED, implement strict storage, verify GREEN** + +Use an allowlist, canonical JSONL, bounded records, atomic JSON checkpoints, and private modes. + +- [ ] **Step 3: Write failing promotion tests** + +```python +def test_default_gate_requires_minimum_actions() -> None: + receipt = evaluate_promotion(summary(covered=99, coverable=100), PromotionCriteria()) + assert receipt.is_ready is False + assert "MINIMUM_ACTIONS" in receipt.blocking_reasons + + +def test_policy_change_invalidates_ready_receipt() -> None: + assert ready_receipt(policy_hash="old").valid_for(identity(policy_hash="new")) is False +``` + +- [ ] **Step 4: Verify RED, implement exact thresholds, verify GREEN, and commit** + +Thresholds: 100 actions, five sessions, 99% coverage, five reviewed candidates, zero false stops, zero failures/pending, p95 at most 75 ms, unchanged identity, observable enforceable outcomes. + +```bash +.venv/bin/python -m pytest tests/integrations/codex/test_evidence.py tests/integrations/codex/test_promotion.py -q +git add src/marginal/integrations/codex tests/integrations/codex +git commit -m "feat: add evidence-gated Codex promotion receipts" +``` + +### Task 5: Authenticated per-session service + +**Files:** +- Create: `src/marginal/integrations/codex/transport.py` +- Create: `src/marginal/integrations/codex/service.py` +- Test: `tests/integrations/codex/test_transport.py` +- Test: `tests/integrations/codex/test_service.py` + +**Interfaces:** +- Produces: `ConnectionInfo`, `start_session_service`, `request_session`, `stop_session_service`, and `run_hook`. + +- [ ] **Step 1: Write failing transport tests** + +```python +def test_wrong_token_is_rejected(tmp_path: Path) -> None: + with running_server(tmp_path, token="expected") as server: + response = send(server, token="wrong", operation="status", payload={}) + assert response["error_code"] == "AUTH_FAILED" + + +def test_oversized_request_is_rejected(tmp_path: Path) -> None: + with running_server(tmp_path) as server: + response = send_bytes(server, b"x" * (MAX_MESSAGE_BYTES + 1)) + assert response["error_code"] == "MESSAGE_TOO_LARGE" +``` + +- [ ] **Step 2: Verify RED, implement bounded loopback transport, verify GREEN** + +Bind literal `127.0.0.1`, select an ephemeral port, compare a 256-bit token with `hmac.compare_digest`, accept one bounded JSON line, and never echo payloads in errors. + +- [ ] **Step 3: Write failing service tests** + +```python +def test_start_is_idempotent_and_end_removes_credentials(tmp_path: Path) -> None: + first = start_session_service(session_event(), data_root=tmp_path) + assert start_session_service(session_event(), data_root=tmp_path) == first + stop_session_service("session-1", data_root=tmp_path) + assert not first.connection_file.exists() + + +def test_missing_service_fails_open_and_demotes(tmp_path: Path) -> None: + configure_enforcement(tmp_path) + result = run_hook_without_service(pre_event(), data_root=tmp_path) + assert result.exit_code == 0 + assert read_mode(tmp_path) == "shadow" +``` + +- [ ] **Step 4: Verify RED, implement service lifecycle, verify GREEN, and commit** + +```bash +.venv/bin/python -m pytest tests/integrations/codex/test_transport.py tests/integrations/codex/test_service.py -q +git add src/marginal/integrations/codex tests/integrations/codex +git commit -m "feat: run Codex governance in an authenticated local service" +``` + +### Task 6: Installer and management CLI + +**Files:** +- Create: `src/marginal/integrations/codex/installer.py` +- Create: `src/marginal/integrations/codex/commands.py` +- Modify: `src/marginal/cli.py` +- Test: `tests/integrations/codex/test_installer.py` +- Test: `tests/integrations/codex/test_commands.py` +- Modify: `tests/test_cli.py` + +**Interfaces:** +- Produces: `CodexInstallation`, `CodexDoctorReport`, `inspect_codex`, `plan_install`, `install`, `uninstall`, and CLI exit codes 0/1/2. + +- [ ] **Step 1: Write failing discovery tests** + +```python +def test_discovery_never_reads_auth() -> None: + runner = RecordingRunner(version="codex-cli 0.147.0", hooks=True, plugins=True) + report = inspect_codex(runner=runner) + assert report.capability_level == "tool_enforcement" + assert all("auth.json" not in " ".join(call) for call in runner.calls) + + +def test_missing_hooks_refuses_enforcement_claim() -> None: + assert inspect_codex(runner=RecordingRunner(hooks=False)).capability_level == "observe" +``` + +- [ ] **Step 2: Verify RED, implement read-only discovery, verify GREEN** + +Subprocesses use argument arrays, an environment allowlist, bounded output, timeout, and no shell. + +- [ ] **Step 3: Write failing mutation/CLI tests** + +```python +def test_install_uses_codex_plugin_commands() -> None: + runner = RecordingRunner() + install(runner=runner, repository="SignalLayerLabs/Marginal", ref="main") + assert ["codex", "plugin", "add", "marginal@marginal", "--json"] in runner.calls + + +def test_unready_promotion_returns_two() -> None: + assert main(["codex", "promote", "--data-dir", str(fixture_data)]) == 2 +``` + +- [ ] **Step 4: Verify RED, add exact CLI grammar, verify GREEN, and commit** + +Grammar: `marginal install codex`, `marginal uninstall codex`, and `marginal codex status|doctor|review|promote|demote`. Normal uninstall preserves data; purge requires explicit `--purge-data --yes`. + +```bash +.venv/bin/python -m pytest tests/integrations/codex/test_installer.py tests/integrations/codex/test_commands.py tests/test_cli.py -q +git add src/marginal/cli.py src/marginal/integrations/codex tests/integrations/codex tests/test_cli.py +git commit -m "feat: add reversible Codex integration commands" +``` + +### Task 7: Scaffold and build the native plugin marketplace + +**Files:** +- Create via scaffold: `.agents/plugins/marketplace.json` +- Create via scaffold: `plugins/marginal/.codex-plugin/plugin.json` +- Create via scaffold: `plugins/marginal/hooks/hooks.json` +- Create via scaffold: `plugins/marginal/skills/marginal/SKILL.md` +- Create: `plugins/marginal/scripts/marginal_hook.py` +- Create generated: `plugins/marginal/runtime/marginal_runtime.pyz` +- Create generated: `plugins/marginal/runtime/provenance.json` +- Create: `scripts/build_codex_plugin.py` +- Test: `tests/plugin/test_codex_plugin.py` + +**Interfaces:** +- Produces: a plugin accepted by the Codex validator and marketplace selector `marginal@marginal`. + +- [ ] **Step 1: Run the official scaffold** + +```bash +python3 /Users/renatovinai/.codex/skills/.system/plugin-creator/scripts/create_basic_plugin.py marginal \ + --path . \ + --marketplace-path .agents/plugins/marketplace.json \ + --with-skills --with-hooks --with-scripts --with-assets --with-marketplace +``` + +Use a recoverable move to place the generated directory under `plugins/marginal`; regenerate the repo marketplace so its source is exactly `./plugins/marginal`. + +- [ ] **Step 2: Write failing bundle tests** + +```python +def test_marketplace_points_to_valid_plugin() -> None: + marketplace = json.loads((REPO / ".agents/plugins/marketplace.json").read_text()) + assert marketplace["name"] == "marginal" + assert marketplace["plugins"][0]["source"]["path"] == "./plugins/marginal" + + +def test_generated_runtime_matches_provenance(tmp_path: Path) -> None: + rebuilt = build_plugin_runtime(REPO, output_dir=tmp_path) + assert sha256(rebuilt.zipapp) == committed_provenance()["sha256"] +``` + +- [ ] **Step 3: Verify RED, implement deterministic builder, verify GREEN** + +Run before builder: `.venv/bin/python -m pytest tests/plugin/test_codex_plugin.py -q`. +Expected: missing build module/runtime. The zipapp entry point calls `marginal.integrations.codex.service:hook_main`; sorted archive paths, normalized timestamps, canonical provenance, and source hashes make the output reproducible. + +- [ ] **Step 4: Configure official hooks** + +`hooks/hooks.json` covers `SessionStart`, `PreToolUse`, `PostToolUse`, and `SessionEnd`. Commands use `$PLUGIN_ROOT`, `$PLUGIN_DATA`, `commandWindows`, synchronous execution, and bounded timeouts. The manifest contains no unsupported fields; default hook discovery finds the hook file. + +- [ ] **Step 5: Validate and commit** + +```bash +.venv/bin/python scripts/build_codex_plugin.py --check +python3 /Users/renatovinai/.codex/skills/.system/plugin-creator/scripts/validate_plugin.py plugins/marginal +.venv/bin/python -m pytest tests/plugin/test_codex_plugin.py -q +git add .agents plugins scripts/build_codex_plugin.py tests/plugin +git commit -m "feat: package MARGINAL as a native Codex plugin" +``` + +### Task 8: Isolated marketplace install and removal smoke + +**Files:** +- Create: `tests/integrations/codex/test_marketplace_smoke.py` +- Create: `scripts/smoke_codex_plugin.py` +- Modify: the canonical workflow under `.github/workflows/` + +**Interfaces:** +- Consumes: a real Codex CLI and temporary `HOME`/`CODEX_HOME`. +- Produces: redacted install, lifecycle, coverage, and removal evidence. + +- [ ] **Step 1: Write the failing smoke test** + +```python +def test_marketplace_install_and_remove(tmp_path: Path) -> None: + result = smoke_plugin(codex=find_codex(), codex_home=tmp_path, marketplace=REPO) + assert result.installed is True + assert result.shadow_block_count == 0 + assert result.hook_coverage == 1.0 + assert result.raw_secret_occurrences == 0 + assert result.removed is True +``` + +- [ ] **Step 2: Verify RED, implement isolated smoke, verify GREEN** + +Run: `.venv/bin/python -m pytest tests/integrations/codex/test_marketplace_smoke.py -q`. +The helper adds the local marketplace, installs the plugin, invokes captured official hook fixtures directly, removes the plugin, and never reads the real Codex home. Trust remains a separate manual live step. + +- [ ] **Step 3: Add the non-secret CI gate and commit** + +CI validates the plugin, checks generated runtime, installs/removes against temporary homes, and exercises direct hook lifecycle without model credentials. + +```bash +git add tests/integrations/codex/test_marketplace_smoke.py scripts/smoke_codex_plugin.py .github/workflows +git commit -m "test: verify Codex plugin install lifecycle" +``` + +### Task 9: Documentation, legal pages, and submission packet + +**Files:** +- Create: `docs/integrations/codex.md` +- Create: `docs/operations/codex-plugin-submission.md` +- Create: `docs/operations/codex-plugin-test-cases.json` +- Create: `PRIVACY.md` +- Create: `TERMS.md` +- Modify: `SUPPORT.md`, `README.md`, `ROADMAP.md`, `CHANGELOG.md`, `docs/index.md`, `docs/integrations/overview.md` +- Modify: `site/index.html`, `site/styles.css`, `site/sitemap.xml` +- Test: `tests/plugin/test_publication_packet.py` +- Modify: `tests/evaluation/test_public_benchmark_surface.py` + +**Interfaces:** +- Produces: public install/remove UX, truthful capability language, and five positive plus three negative reviewer cases. + +- [ ] **Step 1: Write failing public-surface tests** + +```python +def test_readme_and_site_publish_install_remove() -> None: + for path in (REPO / "README.md", REPO / "site/index.html"): + text = path.read_text() + assert "codex plugin marketplace add SignalLayerLabs/Marginal" in text + assert "codex plugin remove marginal@marginal" in text + assert "Tool Enforcement" in text + + +def test_submission_packet_has_required_cases() -> None: + packet = json.loads(TEST_CASES.read_text()) + assert len(packet["positive"]) >= 5 + assert len(packet["negative"]) >= 3 +``` + +- [ ] **Step 2: Verify RED, write exact content, verify GREEN** + +Run: `.venv/bin/python -m pytest tests/plugin/test_publication_packet.py tests/evaluation/test_public_benchmark_surface.py -q`. +README/site lead with install and Earned Enforcement while retaining the benchmark pass-through limitation. Submission status is one of `not_submitted`, `submitted`, `in_review`, `approved`, or `published` with ISO date. + +- [ ] **Step 3: Validate and commit** + +```bash +.venv/bin/python scripts/validate_readme_pages.py +git add README.md ROADMAP.md CHANGELOG.md PRIVACY.md TERMS.md SUPPORT.md docs site tests +git commit -m "docs: launch the Codex plugin and earned enforcement" +``` + +### Task 10: Full quality, security, package, and live gates + +**Files:** +- Create: `docs/operations/evidence/codex-plugin-smoke-2026-08-13.json` +- Modify only code whose failure is reproduced by a new failing test. + +**Interfaces:** +- Produces: a release-ready tree and redacted live evidence. + +- [ ] **Step 1: Run all automated gates** + +```bash +PYTHONDONTWRITEBYTECODE=1 .venv/bin/python -m pytest -p no:cacheprovider -q +.venv/bin/ruff check . +.venv/bin/ruff format --check . +.venv/bin/mypy src/marginal +.venv/bin/python -m build +.venv/bin/twine check dist/* +.venv/bin/python scripts/build_codex_plugin.py --check +python3 /Users/renatovinai/.codex/skills/.system/plugin-creator/scripts/validate_plugin.py plugins/marginal +git diff --check +``` + +Expected: every command exits 0 with no MARGINAL warnings. + +- [ ] **Step 2: Run security/privacy assertions** + +Search plugin, evidence, docs, and diff for credential patterns and a smoke secret marker. Assert that persisted evidence contains none, runtime networking targets literal loopback only, and no auth-file access exists. + +- [ ] **Step 3: Run live Codex acceptance** + +Install in an isolated Codex home, review hooks through supported Codex UI, run harmless shell/edit/local-function calls, verify zero Shadow denials and exact coverage, exercise one synthetic ready receipt and controlled denial, invalidate the policy hash, verify demotion, and remove the plugin. Persist hashes and redacted counters only. + +- [ ] **Step 4: Re-run gates and commit evidence** + +```bash +git add docs/operations/evidence tests src plugins scripts README.md ROADMAP.md CHANGELOG.md site +git commit -m "test: record verified Codex plugin acceptance" +``` + +### Task 11: Independent review, GitHub publication, and OpenAI submission + +**Files:** +- Update: `docs/operations/codex-plugin-submission.md` +- Modify other files only after a reproduced review failure. + +**Interfaces:** +- Produces: reviewed GitHub state and exact portal submission status. + +- [ ] **Step 1: Review the complete diff** + +Every actionable finding names file/line, consequence, and reproducing test. Fix via RED/GREEN and rerun Task 10. + +- [ ] **Step 2: Push and open a ready pull request** + +Include install/remove commands, capability limits, test evidence, and the external-review caveat. Merge only after green CI. + +- [ ] **Step 3: Verify canonical main and Pages after merge** + +Confirm main contains `.agents/plugins/marketplace.json` and `plugins/marginal`, and the public site renders install, uninstall, privacy, terms, and evidence links. + +- [ ] **Step 4: Submit through OpenAI Platform** + +Use the verified SignalLayer Labs organization with Apps Management write access, upload the final bundle, public URLs, prompts, eight test cases, supported regions, and policy attestations, then submit for review. + +- [ ] **Step 5: Record exact external status** + +After portal acceptance record `submitted` or `in_review` with timestamp and non-secret identifier. Record `published` only after OpenAI approval and publisher release. + +--- + +## Plan self-review + +- Spec coverage: all design sections map to Tasks 1–11; external approval is separate from implementation completion. +- Placeholder scan: every task names exact files, interfaces, RED/GREEN commands, and commit boundaries. +- Type consistency: outcome, progress, event, runtime, evidence, receipt, transport, installer, and CLI names are introduced once and reused. +- Dependency direction: benchmark may consume stable production contracts; production never imports benchmark code. diff --git a/docs/superpowers/specs/2026-08-13-codex-plugin-earned-enforcement-design.md b/docs/superpowers/specs/2026-08-13-codex-plugin-earned-enforcement-design.md new file mode 100644 index 0000000..f497f46 --- /dev/null +++ b/docs/superpowers/specs/2026-08-13-codex-plugin-earned-enforcement-design.md @@ -0,0 +1,452 @@ +# MARGINAL Codex Plugin and Earned Enforcement Design + +**Status:** Approved product direction; implementation checkpoint +**Date:** 2026-08-13 +**Target milestone:** v0.3 — Codex Reference Integration + +## 1. Purpose + +Ship a production Codex integration that anyone can install and remove through native Codex plugin +workflows, while preserving MARGINAL's evidence-first standard. + +The differentiating product contract is **Earned Enforcement**: + +> MARGINAL starts as an observer. It earns the right to block tool actions for one repository only +> after it can prove that its own coverage, recommendations, false-stop review, and overhead meet a +> versioned local evidence gate. + +This is not a claim that MARGINAL is the first budget limiter, cost dashboard, loop detector, or +agent hook. Those categories already exist. The product distinction is the combination of: + +- marginal-value allocation rather than a hard cap alone; +- state/evidence-aware progress analysis; +- quality, false-stop, and governance-tax accounting; +- a capability label that refuses to overstate the native control surface; +- automatic demotion when the evidence contract no longer holds; +- native, reversible distribution with local-first telemetry. + +## 2. Goals + +1. Make the public Codex plugin the canonical installation surface. +2. Provide an immediate GitHub marketplace fallback before OpenAI review completes. +3. Keep global installation non-blocking in Shadow Mode. +4. Allow repository-scoped Tool Enforcement only through Earned Enforcement. +5. Make install, update, status, diagnostics, demotion, and uninstall idempotent and auditable. +6. Reuse the provider-neutral runtime and avoid a second policy implementation. +7. Persist hashes and structured decisions locally without storing prompts, source, raw commands, + or raw tool output by default. +8. Produce a plugin bundle, submission materials, positive/negative tests, and public legal/support + pages suitable for the universal Plugins Directory. +9. Preserve a thin engine boundary that can later support Claude Code without changing economic + policy. + +## 3. Non-goals + +- Claiming Full Compute Enforcement when Codex hooks do not cover every model or hosted-tool path. +- Parsing session transcripts as a stable API. +- Silently trusting hooks or using `--dangerously-bypass-hook-trust` for users. +- Reading or copying Codex authentication files. +- Uploading prompts, source, commands, outputs, or local telemetry. +- Automatically claiming token savings from tool-call suppression. +- Replacing the frozen benchmark adapter with unvalidated production assumptions. +- Solving multi-engine installation in v0.3; the architecture must permit it, but Codex is the + release target. + +## 4. Product capability label + +The v0.3 adapter reports **Tool Enforcement** when all required hooks are trusted and observed. + +It does not report Full Compute Enforcement because official Codex documentation says specialized +tool paths can opt out and hosted tools do not use the local hook path. The capability report lists +each supported event and tool family rather than collapsing support into one boolean. + +Required events: + +- `SessionStart` for runtime startup and capability attestation; +- `PreToolUse` for allow/recommend/deny decisions; +- `PostToolUse` for completion evidence and state correlation; +- `SessionEnd` for settlement, coverage summary, and clean shutdown. + +Optional later events include `Stop`, `SubagentStart`, and `SubagentStop`. They are not part of the +first enforcement gate. + +## 5. User experience + +### 5.1 Public directory + +Once OpenAI approves and the publisher releases it, users install MARGINAL from the universal +Plugins Directory shared by ChatGPT and Codex. The directory listing is the primary product route. + +Submission starts immediately when the implementation, legal pages, test cases, and final bundle +pass their gates. Public appearance cannot be represented as immediate because OpenAI approval is +an external review step. + +### 5.2 Immediate GitHub marketplace fallback + +Before directory approval, the supported one-line installation target is: + +```bash +codex plugin marketplace add SignalLayerLabs/Marginal --ref main && codex plugin add marginal@marginal +``` + +The marketplace name in `.agents/plugins/marketplace.json` is `marginal`. Repeating the command is +safe: existing marketplace/plugin state is detected and upgraded rather than duplicated. + +Removal target: + +```bash +codex plugin remove marginal@marginal +``` + +Removing the marketplace itself is optional and separate because it may later distribute more +SignalLayer Labs plugins. + +### 5.3 Python management CLI + +The package also provides: + +```text +marginal install codex +marginal codex status +marginal codex doctor +marginal codex review +marginal codex promote +marginal codex demote +marginal uninstall codex +``` + +`marginal install codex` delegates plugin registration to the Codex CLI instead of editing Codex +configuration directly. It is a secondary automation path, not a competing installation system. + +The plugin remains functional without a separately installed wheel because its release artifact +contains a generated, dependency-free Python runtime. The CLI and plugin artifact are built from +the same source modules; generated plugin runtime files are never edited manually. + +### 5.4 Hook trust + +Codex requires review of non-managed command hooks. MARGINAL surfaces the exact `/hooks` review +step after installation and remains visibly inactive until trust is granted. It never bypasses this +security boundary. + +## 6. Runtime architecture + +```mermaid +flowchart TD + Directory["Universal directory or Git marketplace"] --> Plugin["MARGINAL plugin"] + Plugin --> HookConfig["Official lifecycle hooks"] + HookConfig --> Client["Small hook client"] + Client --> SessionRuntime["Per-session local runtime"] + SessionRuntime --> Adapter["Codex anti-corruption layer"] + Adapter --> UniversalRuntime + UniversalRuntime --> Treasury + Treasury --> Policy + Treasury --> Ledger["Hash-only local evidence"] + Ledger --> Promotion["Earned Enforcement evaluator"] + Promotion --> Shadow["Global Shadow Mode"] + Promotion --> Enforce["Repository Tool Enforcement"] +``` + +### 6.1 Source boundaries + +Production code lives under `src/marginal/integrations/codex/`: + +- `capabilities.py`: Codex version, feature, hook, and tool-family capability reporting; +- `events.py`: strict official hook input/output values; +- `normalization.py`: native event to Universal Agent Protocol translation; +- `outcomes.py`: success/failure/unknown classification without undocumented guesses; +- `runtime.py`: one session's adapter and Treasury lifecycle; +- `transport.py`: authenticated local client/runtime messages; +- `service.py`: per-session process startup, health, shutdown, and crash evidence; +- `evidence.py`: coverage counters, redacted ledger, checkpoints, and receipts; +- `promotion.py`: Earned Enforcement evaluation and automatic demotion; +- `installer.py`: Codex CLI detection and idempotent plugin operations; +- `commands.py`: Codex-specific CLI handlers. + +The generic CLI only dispatches to this package. The top-level `marginal` facade does not import +Codex integration modules. + +`benchmark/codex_adapter` keeps experiment orchestration, pinned task containers, and frozen run +records. It imports stable production event/normalization contracts when doing so does not change +the frozen scientific definition. Production never imports from `benchmark`. + +### 6.2 Plugin package + +The repository contains: + +```text +.agents/plugins/marketplace.json +plugins/marginal/ + .codex-plugin/plugin.json + hooks/hooks.json + skills/marginal/SKILL.md + scripts/marginal_hook.py + runtime/marginal_runtime.pyz + assets/ +``` + +The plugin runtime zipapp is reproducibly generated from selected `src/marginal` modules. CI fails +if the committed bundle and source tree differ. Release provenance records source commit, Python +version, manifest hash, and bundle SHA-256. + +### 6.3 Per-session service + +`SessionStart` launches one local service per Codex session. It binds only to loopback, selects an +ephemeral port, and authenticates every hook call with a random 256-bit token stored in a +user-private connection file under `PLUGIN_DATA`. + +The service: + +- owns the in-memory Treasury and pending-action map; +- serializes lifecycle mutations; +- writes an atomic checkpoint after every settled decision; +- can restore completed observation history after a safe restart; +- marks interrupted pending actions as unknown rather than successful; +- reports health and coverage independently from policy decisions; +- shuts down on `SessionEnd` and removes connection credentials. + +Loopback transport is chosen over Unix-only sockets so the same design works on macOS, Linux, +WSL, and native Windows. File permissions are restrictive where the platform supports POSIX modes; +Windows uses the current-user data directory and never places connection material in a repository. + +## 7. Event and outcome semantics + +### 7.1 Identity + +Each action uses: + +- Codex `session_id`, `turn_id`, and `tool_use_id` for lifecycle identity; +- normalized tool name and canonicalized input hash for semantic identity; +- repository state hash excluding `.git`, `.codex`, `.marginal`, caches, virtual environments, + plugin data, and generated runtime evidence; +- post-action evidence hash derived in memory from `tool_response`. + +Raw tool input and response are not persisted by default. + +### 7.2 Success is not completion + +Official `PostToolUse` is a completion signal and also runs after non-zero shell exits. Therefore +the adapter uses an explicit outcome enum: + +- `success`: supported structured evidence proves success; +- `failure`: supported structured evidence proves failure; +- `unknown`: Codex completed the handler but the supported contract cannot prove the result. + +Only `success` advances the existing `DiminishingReturnDetector`. `failure` uses failure +settlement; `unknown` releases or settles conservatively without advancing successful repetition +history. The adapter never treats all shell calls as successful and never classifies all shell calls +as failed. + +Version-pinned parsers may be added only with captured real fixtures for success, non-zero exit, +background completion, and transport failure. Undocumented prose parsing cannot enable +enforcement. + +### 7.3 No-progress recommendations + +A separate provider-neutral **No Progress** signal may recommend against a repeated completed +attempt when semantic input, repository state, and evidence all remain unchanged. It is distinct +from successful-action diminishing returns. + +No-progress signals begin as Shadow recommendations. They may become enforcement-eligible only +after their own reviewed evidence meets the promotion gate. This prevents a repeated failing test +or flaky check from silently being treated as waste. + +## 8. Earned Enforcement + +### 8.1 Scope + +Promotion is repository-scoped and stored outside the repository under `PLUGIN_DATA`, keyed by an +HMAC of the canonical repository identity. Installing the plugin never adds project files. + +### 8.2 Promotion receipt + +A versioned receipt contains: + +- repository pseudonym; +- Codex, plugin, adapter, policy, and estimator versions; +- hook and tool-family capability matrix; +- observation window and successful session count; +- hook-coverable calls, covered calls, gaps, and integration failures; +- recommendations by reason code; +- reviewed stop candidates and false stops; +- governance decision latency distribution; +- local governance tokens and USD when non-zero; +- unresolved or unknown outcomes; +- policy and evidence hashes; +- readiness status and machine-readable blocking reasons. + +### 8.3 Default gate + +The initial conservative gate requires all of the following: + +- at least 100 covered tool actions across at least five completed sessions; +- at least 99% coverage of hook-coverable local tool calls; +- no integration failures or unresolved reservations in the evaluation window; +- at least five intervention candidates, all manually reviewed; +- zero reviewed false stops in the window; +- p95 local decision latency no greater than 75 ms; +- no Codex/plugin/policy version change since the evidence window began; +- only action families with an observable outcome contract are enforcement-eligible. + +These thresholds are transparent safety defaults, not universal statistical proof. The receipt +states sample size and limitations. Configuration changes invalidate the receipt. + +### 8.4 Promotion and demotion + +`marginal codex promote` succeeds only with a ready receipt and records explicit user intent. +There is no silent auto-promotion. + +The runtime automatically demotes the repository to Shadow Mode when: + +- Codex, adapter, policy, or hook hashes change; +- coverage drops below the supported threshold; +- the local service crashes or a lifecycle mismatch occurs; +- a new false stop is recorded; +- the outcome contract becomes unknown for an enforced action family. + +Demotion never prevents Codex from continuing. It writes a visible reason and a new receipt. + +## 9. Installation transactions + +The installer performs read-only discovery before mutation: + +1. locate `codex` and record its exact version; +2. query stable feature flags and plugin commands; +3. validate the plugin/marketplace manifest and runtime hash; +4. inspect installed marketplace/plugin state through Codex JSON output; +5. execute the minimum required Codex command; +6. verify the installed plugin identity and enabled state; +7. run a local hook-client/service self-test without reading authentication data; +8. write an installation receipt. + +No direct edit to `~/.codex/config.toml`, `hooks.json`, or authentication files occurs in the normal +plugin path. If a future compatibility fallback must edit configuration, it requires an atomic +backup, exact ownership markers, rollback on failure, and explicit capability labeling. + +Uninstall delegates to `codex plugin remove`, verifies absence, and preserves local evidence by +default. `--purge-data` is a separate explicit destructive option. Reinstall and upgrade preserve +receipts but invalidate promotion when code or policy hashes change. + +## 10. Failure behavior + +- Shadow Mode fails open and records the coverage gap. +- Tool Enforcement also fails open on integration failure, immediately demotes to Shadow, and + emits a visible warning. MARGINAL is an efficiency governor, not a security boundary. +- Invalid or oversized hook input is rejected by the adapter, recorded without raw payload, and + causes demotion rather than a permanent Codex outage. +- A denied `PreToolUse` action never enters successful execution history. +- `PostToolUse` identity mismatch never settles a different reservation. +- Session shutdown marks remaining pending work unknown and reports it. + +## 11. Privacy and security + +- Runtime behavior is local and performs no network requests. +- No prompt, source, raw command, raw tool output, transcript, or credential is persisted by + default. +- Commands and responses are canonicalized and hashed in memory before redacted evidence is + written. +- `transcript_path` is never parsed because OpenAI does not define it as stable. +- Codex auth files and credential environment values are neither opened nor copied. +- Hook subprocess environments explicitly exclude credential variables where supported. +- Plugin data and connection tokens use user-private permissions. +- The service accepts authenticated loopback messages only, applies size/time limits, and uses + constant-time token comparison. +- Plugin hooks require normal Codex trust review; bypass flags are prohibited in user guidance. +- Release artifacts include provenance and checksums, and CI scans plugin and publication bundles + for secrets. + +## 12. Claude compatibility direction + +The domain policy, outcome enum, no-progress signal, promotion receipt, and evidence gate are +provider-neutral. The Codex plugin may use compatibility environment variables supplied by Codex, +but Codex-specific names remain inside the adapter. + +A later Claude Code package supplies a separate native manifest and hook translator pointing to the +same generated runtime. No Claude behavior is claimed or shipped as complete in v0.3. + +## 13. Verification strategy + +### Unit and contract tests + +- strict hook event validation and output shapes; +- canonical semantic/state/evidence hashing; +- success/failure/unknown settlement; +- no-progress separation from successful diminishing returns; +- pending identity and replay invariants; +- receipt thresholds, invalidation, promotion, and demotion; +- redaction and no-secret persistence; +- installer command planning and idempotency. + +### Integration tests + +- real Codex fixture capture for supported event types; +- trusted plugin install, list, update, remove, and reinstall against a temporary Codex home; +- SessionStart/service/PreToolUse/PostToolUse/SessionEnd lifecycle; +- concurrent hook calls and service crash recovery; +- macOS, Linux, Windows, and WSL command generation; +- plugin validation and marketplace ingestion; +- wheel/sdist install plus generated zipapp smoke. + +### Live acceptance + +On the pinned supported Codex version: + +1. install from the Git marketplace command; +2. review/trust the hook definition through the supported UI; +3. run a harmless session that exercises shell, edit, and local function tools; +4. prove Shadow Mode does not block; +5. inspect status, coverage, redaction, and latency; +6. exercise a synthetic ready receipt and one controlled denial; +7. verify automatic demotion after a version/hash mismatch; +8. uninstall and verify ordinary Codex operation remains intact. + +The repository test suite, Ruff, strict mypy, build, Twine, plugin validator, marketplace validator, +secret scan, and documentation/site checks must all pass before publication. + +## 14. Public catalog submission + +The repository ships a submission packet containing: + +- final plugin bundle and manifest metadata; +- public website, support, privacy policy, and terms URLs; +- concise capability and limitation language; +- at least five positive and three negative reviewer test cases; +- release notes and policy attestations checklist; +- local test evidence and artifact hashes. + +Submission is attempted through the OpenAI Platform organization with Apps Management write access +and a verified SignalLayer Labs identity. The repository may truthfully say **submitted** after the +portal accepts it, but may say **available in the universal directory** only after OpenAI approval +and publisher release. + +## 15. Documentation and claims + +README and site lead with the install/remove experience and Earned Enforcement contract only after +live verification passes. They must state: + +- Shadow Mode is global by default; +- promotion is repository-scoped and evidence-gated; +- capability is Tool Enforcement, not Full Compute Enforcement; +- raw content stays local and is not persisted by default; +- benchmark savings remain separate from installer validation; +- directory submission status is external and timestamped. + +No token-saving headline is added unless matched, verified, net evidence supports it. + +## 16. Acceptance criteria + +The v0.3 installation slice is complete when: + +- a validated plugin and marketplace are committed; +- the GitHub one-line install and one-command removal work on a clean Codex home; +- `marginal install codex`, status, doctor, promotion/demotion, and uninstall are tested; +- install is global Shadow Mode and makes no project-code change; +- a repository cannot enter verified Tool Enforcement without a ready receipt; +- capability/version changes automatically demote enforcement; +- official hooks execute through the production adapter with exact lifecycle coverage; +- raw commands/output/auth material do not appear in the evidence store; +- package and plugin share one generated core implementation; +- full verification and live smoke pass; +- README, site, roadmap, changelog, integration docs, privacy, terms, and support are current; +- the public-directory submission packet is complete and portal submission is attempted; +- external review status is reported exactly, without implying approval. + diff --git a/plugins/marginal/.codex-plugin/plugin.json b/plugins/marginal/.codex-plugin/plugin.json new file mode 100644 index 0000000..b5b6a66 --- /dev/null +++ b/plugins/marginal/.codex-plugin/plugin.json @@ -0,0 +1,19 @@ +{ + "name": "marginal", + "version": "0.3.0", + "description": "Evidence-gated compute governance for Codex, local-first and Shadow Mode by default.", + "author": { + "name": "SignalLayer Labs", + "url": "https://github.com/SignalLayerLabs/Marginal" + }, + "skills": "./skills/", + "interface": { + "displayName": "Marginal", + "shortDescription": "Earned enforcement for agent compute.", + "longDescription": "MARGINAL measures repeated Codex tool work locally, starts in Shadow Mode, and permits repository Tool Enforcement only after a versioned evidence gate proves coverage, reviewed false stops, and governance overhead.", + "developerName": "SignalLayer Labs", + "category": "Productivity", + "capabilities": [], + "defaultPrompt": "Use $marginal to inspect this repository's Shadow Mode evidence and explain whether Tool Enforcement is ready." + } +} diff --git a/plugins/marginal/hooks/hooks.json b/plugins/marginal/hooks/hooks.json new file mode 100644 index 0000000..5cee9db --- /dev/null +++ b/plugins/marginal/hooks/hooks.json @@ -0,0 +1,60 @@ +{ + "description": "Local-only MARGINAL observation and Earned Enforcement hooks.", + "hooks": { + "SessionStart": [ + { + "hooks": [ + { + "type": "command", + "command": "python3 \"$PLUGIN_ROOT/scripts/marginal_hook.py\"", + "commandWindows": "py -3 \"%PLUGIN_ROOT%\\scripts\\marginal_hook.py\"", + "timeout": 10, + "statusMessage": "Starting MARGINAL Shadow Mode" + } + ] + } + ], + "PreToolUse": [ + { + "matcher": "Bash|apply_patch|MCP|Read|Write|Edit", + "hooks": [ + { + "type": "command", + "command": "python3 \"$PLUGIN_ROOT/scripts/marginal_hook.py\"", + "commandWindows": "py -3 \"%PLUGIN_ROOT%\\scripts\\marginal_hook.py\"", + "timeout": 5, + "statusMessage": "Measuring marginal value" + } + ] + } + ], + "PostToolUse": [ + { + "matcher": "Bash|apply_patch|MCP|Read|Write|Edit", + "hooks": [ + { + "type": "command", + "command": "python3 \"$PLUGIN_ROOT/scripts/marginal_hook.py\"", + "commandWindows": "py -3 \"%PLUGIN_ROOT%\\scripts\\marginal_hook.py\"", + "timeout": 5, + "statusMessage": "Recording redacted completion evidence" + } + ] + } + ], + "SessionEnd": [ + { + "hooks": [ + { + "type": "command", + "command": "python3 \"$PLUGIN_ROOT/scripts/marginal_hook.py\"", + "commandWindows": "py -3 \"%PLUGIN_ROOT%\\scripts\\marginal_hook.py\"", + "timeout": 5, + "statusMessage": "Closing MARGINAL session" + } + ] + } + ] + } +} + diff --git a/plugins/marginal/runtime/marginal_runtime.pyz b/plugins/marginal/runtime/marginal_runtime.pyz new file mode 100644 index 0000000..acff7a2 Binary files /dev/null and b/plugins/marginal/runtime/marginal_runtime.pyz differ diff --git a/plugins/marginal/runtime/provenance.json b/plugins/marginal/runtime/provenance.json new file mode 100644 index 0000000..662644f --- /dev/null +++ b/plugins/marginal/runtime/provenance.json @@ -0,0 +1 @@ +{"builder":"scripts/build_codex_plugin.py","python_requires":">=3.10","schema_version":1,"sha256":"f148d51c8e00323637225479a2b26b75ea79255ae850ba3e3df0c65e41ecc630","source_hash":"908d1090899c672c8a19bf55268e9583ca3c3486b1f4b9dbc58815bf0027fa63"} diff --git a/plugins/marginal/scripts/marginal_hook.py b/plugins/marginal/scripts/marginal_hook.py new file mode 100644 index 0000000..a08a623 --- /dev/null +++ b/plugins/marginal/scripts/marginal_hook.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +"""Tiny dependency-free launcher for the generated MARGINAL runtime.""" + +from __future__ import annotations + +import os +import sys +from pathlib import Path + + +def main() -> int: + plugin_root = os.environ.get("PLUGIN_ROOT") + plugin_data = os.environ.get("PLUGIN_DATA") + if not plugin_root or not plugin_data: + return 0 + runtime = Path(plugin_root).resolve() / "runtime" / "marginal_runtime.pyz" + if not runtime.is_file(): + return 0 + environment = { + name: value + for name in ("PATH", "LANG", "LC_ALL", "SYSTEMROOT") + if (value := os.environ.get(name)) is not None + } + environment["PLUGIN_DATA"] = str(Path(plugin_data).resolve()) + environment["PLUGIN_ROOT"] = str(Path(plugin_root).resolve()) + os.execve(sys.executable, [sys.executable, str(runtime)], environment) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/plugins/marginal/skills/marginal/SKILL.md b/plugins/marginal/skills/marginal/SKILL.md new file mode 100644 index 0000000..eb9d121 --- /dev/null +++ b/plugins/marginal/skills/marginal/SKILL.md @@ -0,0 +1,40 @@ +--- +name: marginal +description: Use when inspecting, reviewing, promoting, demoting, or explaining MARGINAL governance in Codex, especially for repeated tool work, token-saving claims, and Earned Enforcement readiness. +--- + +# MARGINAL + +Treat compute as scarce and claims as evidence-bound. The plugin starts globally in Shadow Mode; +it may exercise repository-scoped Tool Enforcement only after a valid Earned Enforcement receipt +and explicit promotion. + +## Workflow + +1. Run `marginal codex status` before describing the active mode. +2. Run `marginal codex doctor` when hooks, coverage, or compatibility are uncertain. +3. Use `/hooks` to inspect and grant trust to the exact lifecycle commands. +4. Run `marginal codex review`, then label each local redacted candidate with + `--candidate HASH --verdict waste|helpful` before promotion. +5. Run `marginal codex promote` only when the evidence receipt is ready. +6. Run `marginal codex demote` whenever identity, coverage, outcome observability, or policy drifts. + +## Claims contract + +- Say **Tool Enforcement**, never Full Compute Enforcement. +- Describe recommendations as counterfactual until an enforced run measures them. +- Never claim token savings without a matched benchmark that reports quality and governance tax. +- Treat `PostToolUse` as completion, not success; prose-only outcomes remain unknown. +- Never read Codex auth files, prompts, source, raw commands, raw outputs, or transcripts for evidence. + +## Quick reference + +| Need | Command | +| --- | --- | +| Current mode | `marginal codex status` | +| Capability diagnosis | `marginal codex doctor` | +| Unreviewed evidence | `marginal codex review` | +| Evidence-gated enforcement | `marginal codex promote` | +| Immediate fail-open reset | `marginal codex demote` | + +If evidence is incomplete or contradictory, keep Shadow Mode and report the exact blocking reason. diff --git a/plugins/marginal/skills/marginal/agents/openai.yaml b/plugins/marginal/skills/marginal/agents/openai.yaml new file mode 100644 index 0000000..17d1dfe --- /dev/null +++ b/plugins/marginal/skills/marginal/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "MARGINAL" + short_description: "Inspect evidence-gated Codex governance" + default_prompt: "Use $marginal to inspect this repository’s Shadow Mode evidence and explain whether Tool Enforcement is ready." diff --git a/pyproject.toml b/pyproject.toml index f337113..b97bcbe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "marginal-ai" -version = "0.2.0" +version = "0.3.0" description = "Universal learning-loop and compute-governance foundation for economically disciplined AI agents." readme = "README.md" requires-python = ">=3.10" @@ -59,6 +59,7 @@ dev = [ "build>=1.2.2", "mypy>=1.17", "pytest>=8.3", + "PyYAML>=6.0", "jsonschema>=4.23", "ruff==0.16.2", "twine>=6.1", diff --git a/scripts/build_codex_plugin.py b/scripts/build_codex_plugin.py new file mode 100644 index 0000000..1844b8e --- /dev/null +++ b/scripts/build_codex_plugin.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python3 +"""Reproducibly build the dependency-free MARGINAL Codex runtime zipapp.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import tempfile +import zipfile +from dataclasses import dataclass +from pathlib import Path + +_ZIP_TIMESTAMP = (2020, 1, 1, 0, 0, 0) +_MAIN = ( + b"from marginal.integrations.codex.service import hook_main\nraise SystemExit(hook_main())\n" +) + + +@dataclass(frozen=True, slots=True) +class PluginBuild: + zipapp: Path + source_hash: str + sha256: str + + +def _source_files(repo: Path) -> list[Path]: + package = repo / "src" / "marginal" + return sorted( + path + for path in package.rglob("*") + if (path.is_file() and "__pycache__" not in path.parts and path.suffix in {".py", ".json"}) + or path == package / "py.typed" + ) + + +def _source_hash(repo: Path, files: list[Path]) -> str: + digest = hashlib.sha256() + for path in files: + relative = path.relative_to(repo / "src").as_posix().encode("utf-8") + digest.update(relative + b"\0" + path.read_bytes() + b"\0") + digest.update(b"__main__.py\0" + _MAIN) + return digest.hexdigest() + + +def _write_archive(target: Path, repo: Path, files: list[Path]) -> None: + target.parent.mkdir(parents=True, exist_ok=True) + with zipfile.ZipFile( + target, + "w", + compression=zipfile.ZIP_STORED, + ) as archive: + entries = [("__main__.py", _MAIN)] + [ + (path.relative_to(repo / "src").as_posix(), path.read_bytes()) for path in files + ] + for name, content in sorted(entries): + info = zipfile.ZipInfo(name, date_time=_ZIP_TIMESTAMP) + info.compress_type = zipfile.ZIP_STORED + info.external_attr = 0o100644 << 16 + info.create_system = 3 + archive.writestr(info, content, compress_type=zipfile.ZIP_STORED) + + +def build_plugin_runtime(repo: str | Path, *, output_dir: str | Path) -> PluginBuild: + root = Path(repo).resolve() + destination = Path(output_dir).resolve() + files = _source_files(root) + source_hash = _source_hash(root, files) + zipapp = destination / "marginal_runtime.pyz" + _write_archive(zipapp, root, files) + archive_hash = hashlib.sha256(zipapp.read_bytes()).hexdigest() + return PluginBuild(zipapp=zipapp, source_hash=source_hash, sha256=archive_hash) + + +def _write_provenance(runtime_dir: Path, build: PluginBuild) -> None: + payload = { + "schema_version": 1, + "builder": "scripts/build_codex_plugin.py", + "python_requires": ">=3.10", + "source_hash": build.source_hash, + "sha256": build.sha256, + } + (runtime_dir / "provenance.json").write_text( + json.dumps(payload, sort_keys=True, separators=(",", ":")) + "\n", + encoding="utf-8", + ) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--check", action="store_true") + args = parser.parse_args() + repo = Path(__file__).resolve().parents[1] + committed_dir = repo / "plugins" / "marginal" / "runtime" + if args.check: + with tempfile.TemporaryDirectory(prefix="marginal-plugin-check-") as temporary: + build = build_plugin_runtime(repo, output_dir=temporary) + committed = committed_dir / "marginal_runtime.pyz" + provenance = json.loads((committed_dir / "provenance.json").read_text(encoding="utf-8")) + if not committed.exists() or committed.read_bytes() != build.zipapp.read_bytes(): + raise SystemExit("committed Codex runtime is stale") + if provenance.get("sha256") != build.sha256: + raise SystemExit("Codex runtime provenance is stale") + if provenance.get("source_hash") != build.source_hash: + raise SystemExit("Codex runtime source hash is stale") + return 0 + build = build_plugin_runtime(repo, output_dir=committed_dir) + _write_provenance(committed_dir, build) + print(f"built {build.zipapp} ({build.sha256})") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/smoke_codex_plugin.py b/scripts/smoke_codex_plugin.py new file mode 100644 index 0000000..94ce9e6 --- /dev/null +++ b/scripts/smoke_codex_plugin.py @@ -0,0 +1,227 @@ +#!/usr/bin/env python3 +"""Credential-free install, hook lifecycle, privacy, and removal acceptance test.""" + +from __future__ import annotations + +import argparse +import json +import os +import subprocess +import sys +import time +from dataclasses import asdict, dataclass +from pathlib import Path +from typing import Any + + +@dataclass(frozen=True, slots=True) +class CodexPluginSmokeResult: + installed: bool + shadow_block_count: int + hook_coverage: float + evidence_records: int + completed_sessions: int + raw_secret_occurrences: int + removed: bool + codex_version: str + + +def _run( + args: list[str], + *, + environment: dict[str, str], + cwd: Path, + input_text: str | None = None, +) -> subprocess.CompletedProcess[str]: + completed = subprocess.run( + args, + cwd=cwd, + env=environment, + input=input_text, + check=False, + capture_output=True, + text=True, + timeout=30, + ) + if completed.returncode != 0: + raise RuntimeError(f"command failed ({completed.returncode}): {' '.join(args[:4])}") + return completed + + +def _initialize_repository(path: Path, environment: dict[str, str]) -> None: + path.mkdir(parents=True) + for args in ( + ["git", "init", "-q"], + ["git", "config", "user.email", "smoke@example.com"], + ["git", "config", "user.name", "MARGINAL Smoke"], + ): + _run(args, environment=environment, cwd=path) + (path / "tracked.txt").write_text("initial\n", encoding="utf-8") + _run(["git", "add", "tracked.txt"], environment=environment, cwd=path) + _run(["git", "commit", "-qm", "initial"], environment=environment, cwd=path) + + +def _hook_payloads(workspace: Path, secret: str) -> list[dict[str, Any]]: + common: dict[str, Any] = { + "session_id": "smoke-session", + "transcript_path": None, + "cwd": str(workspace), + "model": "smoke-model", + "permission_mode": "default", + } + tool = { + **common, + "turn_id": "smoke-turn", + "tool_name": "Bash", + "tool_use_id": "smoke-call", + "tool_input": {"command": f"echo {secret}", "description": secret}, + } + return [ + {**common, "hook_event_name": "SessionStart", "source": "startup"}, + {**tool, "hook_event_name": "PreToolUse"}, + {**tool, "hook_event_name": "PostToolUse", "tool_response": {"exit_code": 0}}, + {**common, "hook_event_name": "SessionEnd", "reason": "other"}, + ] + + +def _count_secret(root: Path, secret: str) -> int: + occurrences = 0 + if not root.exists(): + return 0 + marker = secret.encode("utf-8") + for path in root.rglob("*"): + if path.is_file(): + occurrences += path.read_bytes().count(marker) + return occurrences + + +def _read_evidence(plugin_data: Path) -> list[dict[str, Any]]: + records: list[dict[str, Any]] = [] + for path in (plugin_data / "evidence").glob("*/evidence.jsonl"): + for line in path.read_text(encoding="utf-8").splitlines(): + payload = json.loads(line) + if isinstance(payload, dict): + records.append(payload) + return records + + +def smoke_plugin( + *, + codex: Path, + isolation_root: Path, + marketplace: Path, +) -> CodexPluginSmokeResult: + """Run the public install/remove path without using the caller's Codex home.""" + + root = isolation_root.resolve() + home = root / "home" + codex_home = root / "codex" + plugin_data = root / "plugin-data" + workspace = root / "workspace" + for directory in (home, codex_home, plugin_data): + directory.mkdir(parents=True, exist_ok=True) + environment = { + "PATH": os.environ.get("PATH", ""), + "HOME": str(home), + "CODEX_HOME": str(codex_home), + "LANG": "C", + "LC_ALL": "C", + } + _initialize_repository(workspace, environment) + version = _run([str(codex), "--version"], environment=environment, cwd=root).stdout.strip() + _run( + [str(codex), "plugin", "marketplace", "add", str(marketplace), "--json"], + environment=environment, + cwd=root, + ) + add = _run( + [str(codex), "plugin", "add", "marginal@marginal", "--json"], + environment=environment, + cwd=root, + ) + installed_payload = json.loads(add.stdout) + plugin_root = Path(installed_payload["installedPath"]).resolve() + hook_script = plugin_root / "scripts" / "marginal_hook.py" + hook_environment = { + "PATH": environment["PATH"], + "LANG": "C", + "LC_ALL": "C", + "PLUGIN_ROOT": str(plugin_root), + "PLUGIN_DATA": str(plugin_data), + } + + secret = "MARGINAL_SMOKE_SECRET_7fcd98" + completed_hooks = 0 + shadow_blocks = 0 + removed = False + try: + for payload in _hook_payloads(workspace, secret): + result = _run( + [sys.executable, str(hook_script)], + environment=hook_environment, + cwd=workspace, + input_text=json.dumps(payload), + ) + completed_hooks += 1 + if payload["hook_event_name"] == "PreToolUse" and result.stdout.strip(): + hook_output = json.loads(result.stdout) + decision = hook_output.get("hookSpecificOutput", {}).get("permissionDecision") + shadow_blocks += int(decision == "deny") + deadline = time.monotonic() + 2.0 + sessions_root = plugin_data / "sessions" + while list(sessions_root.glob("*.json")) and time.monotonic() < deadline: + time.sleep(0.02) + finally: + remove = _run( + [str(codex), "plugin", "remove", "marginal@marginal", "--json"], + environment=environment, + cwd=root, + ) + removed = json.loads(remove.stdout).get("pluginId") == "marginal@marginal" + + evidence = _read_evidence(plugin_data) + decisions = [record for record in evidence if record.get("event") == "decision"] + coverable = sum(record.get("coverable") is True for record in decisions) + covered = sum(record.get("covered") is True for record in decisions) + if completed_hooks != 4: + raise RuntimeError("direct hook lifecycle did not complete") + + return CodexPluginSmokeResult( + installed=True, + shadow_block_count=shadow_blocks, + hook_coverage=covered / coverable if coverable else 0.0, + evidence_records=len(evidence), + completed_sessions=len( + { + str(record.get("session_hash")) + for record in evidence + if record.get("event") == "session_end" and record.get("session_hash") + } + ), + raw_secret_occurrences=_count_secret(plugin_data, secret), + removed=removed, + codex_version=version, + ) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--codex", type=Path, default=Path("codex")) + parser.add_argument("--isolation-root", type=Path, required=True) + parser.add_argument("--marketplace", type=Path, default=Path.cwd()) + parser.add_argument("--json", action="store_true") + args = parser.parse_args() + result = smoke_plugin( + codex=args.codex.resolve(), + isolation_root=args.isolation_root.resolve(), + marketplace=args.marketplace.resolve(), + ) + if args.json: + print(json.dumps(asdict(result), sort_keys=True)) + else: + print(result) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/site/index.html b/site/index.html index 9bd8d98..26eaac7 100644 --- a/site/index.html +++ b/site/index.html @@ -46,6 +46,12 @@
Exploratory 3-task smoke, one paired run per task. Codex CLI 0.147.0 with GPT-5.6-sol, identical prompts and limits, verified in official SWE-bench Lite task environments on Modal.
+codex plugin marketplace add SignalLayerLabs/Marginal --ref main && codex plugin add marginal@marginal
+ codex plugin remove marginal@marginal
+ Tool Enforcement, never an overstated Full Compute Enforcement claim. Repository blocking must earn a local evidence receipt and demotes automatically on drift. Universal directory review is pending; the Git marketplace works now.
+Build the evidence first