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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ body:
id: version
attributes:
label: MARGINAL version
placeholder: "0.3.0"
placeholder: "0.3.1"
validations:
required: true
- type: input
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ jobs:
- run: mypy src/marginal
- run: pytest -q
- run: python -m build
- run: python -m twine check dist/*
- run: python -m twine check dist/*.whl dist/*.tar.gz
- run: python scripts/build_codex_plugin_submission.py --output-dir dist
- name: Create release when the version is new
env:
GH_TOKEN: ${{ github.token }}
Expand Down
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,20 @@ All notable changes to MARGINAL are documented here. The project follows Semanti

## [Unreleased]

## [0.3.1] - 2026-08-13

### Added

- production square logo, public listing metadata and three starter prompts for the universal
Plugins Directory;
- deterministic, single-root Plugins Directory ZIP with the native skill, hooks, local runtime and
assets, published alongside every release.

### Changed

- the submission packet now records the exact OpenAI Platform access and identity prerequisites
separately from completed package, release and public-URL gates.

## [0.3.0] - 2026-08-13

### Added
Expand Down
4 changes: 2 additions & 2 deletions CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ title: "MARGINAL: Economically Disciplined Compute Allocation for AI Agents"
type: software
authors:
- name: SignalLayer Labs
version: 0.3.0
date-released: 2026-08-06
version: 0.3.1
date-released: 2026-08-13
license: Apache-2.0
repository-code: "https://github.com/SignalLayerLabs/Marginal"
url: "https://github.com/SignalLayerLabs/Marginal"
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ marginal install codex
Current tagged library install target:

```bash
pip install "marginal-ai @ git+https://github.com/SignalLayerLabs/Marginal.git@v0.3.0"
pip install "marginal-ai @ git+https://github.com/SignalLayerLabs/Marginal.git@v0.3.1"
```

Development checkout:
Expand Down
2 changes: 1 addition & 1 deletion codemeta.json
Original file line number Diff line number Diff line change
Expand Up @@ -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.3.0",
"version": "0.3.1",
"datePublished": "2026-08-06",
"programmingLanguage": "Python",
"runtimePlatform": "Python 3.10-3.13",
Expand Down
51 changes: 43 additions & 8 deletions docs/operations/codex-plugin-submission.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
```text
status: not_submitted
status_date: 2026-08-13
plugin_version: 0.3.0
plugin_version: 0.3.1
marketplace_selector: marginal@marginal
portal_submission_type: skills_only_zip
portal_access: authentication_required
```

## Submission identity
Expand All @@ -19,6 +21,24 @@ marketplace_selector: marginal@marginal
- Authentication: none; local plugin runtime only
- Network access: none
- Data region: local user device
- Availability: every country or region offered by the submission portal

## Directory listing copy

**Short description:** Earned enforcement for agent compute.

**Long description:** 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. It never reads prompts, source, raw commands, raw
outputs, transcripts, or Codex credentials for evidence.

**Release notes:** First universal-directory candidate. Adds MARGINAL's local-first Shadow Mode,
review workflow, Earned Enforcement gates, fail-open demotion, native Codex lifecycle package,
production logo, eight reviewer cases, and reproducible v0.3.1 archive.

Starter prompts are the three `interface.defaultPrompt` entries in
`plugins/marginal/.codex-plugin/plugin.json`. The production logo is
`plugins/marginal/assets/marginal-logo.png`.

## Review description

Expand All @@ -30,8 +50,9 @@ coverage or identity changes.

## Reviewer setup

1. Add this repository as a local marketplace.
2. Install `marginal@marginal`.
1. Add the public repository marketplace with
`codex plugin marketplace add SignalLayerLabs/Marginal --ref main`.
2. Install `marginal@marginal` with `codex plugin add 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
Expand All @@ -46,12 +67,26 @@ coverage or identity changes.
- [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.
- [x] Canonical main contains v0.3.1 after merge and every public Pages URL resolves.
- [x] A deterministic single-root ZIP builder covers the manifest, skill, hooks, runtime, and logo.
- [ ] SignalLayer Labs identity and Apps Management write permission are confirmed in Platform.
- [ ] Final archive is uploaded and external review is started.
- [ ] The v0.3.1 archive is uploaded in Platform and external review is started.

Build the exact upload artifact with:

```bash
python scripts/build_codex_plugin_submission.py --output-dir dist
```

The release workflow attaches that same ZIP to the matching GitHub release. The package contains no
MCP or app configuration, so the portal submission type is **Skills only**. The archive also carries
the native Codex hook runtime tested by the public marketplace path; OpenAI's ingestion and review
remain authoritative for the components exposed by the universal directory.

The status above changes only after the external portal accepts the final submission. No external
identifier, credential, or reviewer correspondence belongs in this repository.
The status above changes only after the external portal accepts the final submission. Creating or
submitting a draft requires an authenticated Platform session, Apps Management write access, and a
verified SignalLayer Labs identity; none can be inferred from repository or GitHub credentials. 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).
[`codex-plugin-smoke-2026-08-13-v0.3.1.json`](evidence/codex-plugin-smoke-2026-08-13-v0.3.1.json).
3 changes: 1 addition & 2 deletions docs/operations/codex-plugin-test-cases.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"schema_version": 1,
"plugin_version": "0.3.0",
"plugin_version": "0.3.1",
"positive": [
{
"id": "positive-shadow-status",
Expand Down Expand Up @@ -46,4 +46,3 @@
}
]
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"codex_version": "codex-cli 0.147.0",
"completed_sessions": 1,
"directory_archive_sha256": "f43231057fab50a35fed14a55f95a2452e0c0c64f3b9b1f0a1a602e24af19582",
"evidence_records": 4,
"hook_coverage": 1.0,
"installed": true,
"marketplace_selector": "marginal@marginal",
"plugin_version": "0.3.1",
"raw_secret_occurrences": 0,
"removed": true,
"runtime_sha256": "98dc5a49e77864fd529c647cc5e81181b9f5ebda234561135a80d4a05823f06d",
"shadow_block_count": 0
}
23 changes: 21 additions & 2 deletions plugins/marginal/.codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
{
"name": "marginal",
"version": "0.3.0",
"version": "0.3.1",
"description": "Evidence-gated compute governance for Codex, local-first and Shadow Mode by default.",
"author": {
"name": "SignalLayer Labs",
"url": "https://github.com/SignalLayerLabs/Marginal"
},
"homepage": "https://signallayerlabs.github.io/Marginal/",
"repository": "https://github.com/SignalLayerLabs/Marginal",
"license": "Apache-2.0",
"keywords": [
"agent-compute",
"compute-governance",
"codex",
"token-efficiency"
],
"skills": "./skills/",
"interface": {
"displayName": "Marginal",
Expand All @@ -14,6 +23,16 @@
"developerName": "SignalLayer Labs",
"category": "Productivity",
"capabilities": [],
"defaultPrompt": "Use $marginal to inspect this repository's Shadow Mode evidence and explain whether Tool Enforcement is ready."
"websiteURL": "https://signallayerlabs.github.io/Marginal/",
"privacyPolicyURL": "https://signallayerlabs.github.io/Marginal/privacy.html",
"termsOfServiceURL": "https://signallayerlabs.github.io/Marginal/terms.html",
"defaultPrompt": [
"Use $marginal to inspect this repository's Shadow Mode evidence and explain whether Tool Enforcement is ready.",
"Use $marginal to diagnose this Codex installation without reading credentials or transcripts.",
"Use $marginal to review repeated-work candidates and explain which evidence gates remain."
],
"brandColor": "#22D3EE",
"composerIcon": "./assets/marginal-logo.png",
"logo": "./assets/marginal-logo.png"
}
}
Binary file added plugins/marginal/assets/marginal-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified plugins/marginal/runtime/marginal_runtime.pyz
Binary file not shown.
2 changes: 1 addition & 1 deletion plugins/marginal/runtime/provenance.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"builder":"scripts/build_codex_plugin.py","python_requires":">=3.10","schema_version":1,"sha256":"f148d51c8e00323637225479a2b26b75ea79255ae850ba3e3df0c65e41ecc630","source_hash":"908d1090899c672c8a19bf55268e9583ca3c3486b1f4b9dbc58815bf0027fa63"}
{"builder":"scripts/build_codex_plugin.py","python_requires":">=3.10","schema_version":1,"sha256":"98dc5a49e77864fd529c647cc5e81181b9f5ebda234561135a80d4a05823f06d","source_hash":"c0fd650bfd08f8e8550cb88fb1d5b1649203bd44367dcd450f78c9928b8ce6dd"}
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "marginal-ai"
version = "0.3.0"
version = "0.3.1"
description = "Universal learning-loop and compute-governance foundation for economically disciplined AI agents."
readme = "README.md"
requires-python = ">=3.10"
Expand Down
75 changes: 75 additions & 0 deletions scripts/build_codex_plugin_submission.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#!/usr/bin/env python3
"""Build the deterministic ZIP uploaded to the OpenAI Plugins Directory."""

from __future__ import annotations

import argparse
import json
import stat
import zipfile
from pathlib import Path

REPRODUCIBLE_TIMESTAMP = (1980, 1, 1, 0, 0, 0)
EXCLUDED_PARTS = {"__pycache__", ".DS_Store"}


def _included_files(plugin_root: Path) -> list[Path]:
files: list[Path] = []
for path in plugin_root.rglob("*"):
relative = path.relative_to(plugin_root)
if any(part in EXCLUDED_PARTS for part in relative.parts):
continue
if path.is_symlink():
raise ValueError(f"submission archive cannot contain symlinks: {relative}")
if path.is_file() and path.suffix != ".pyc":
files.append(path)
return sorted(files, key=lambda path: path.relative_to(plugin_root).as_posix())


def build_submission_archive(repo: Path, *, output_dir: Path) -> Path:
"""Package one plugin root at the archive root using stable metadata."""

repo = repo.resolve()
plugin_root = repo / "plugins" / "marginal"
manifest = json.loads(
(plugin_root / ".codex-plugin" / "plugin.json").read_text(encoding="utf-8")
)
version = manifest["version"]
output_dir.mkdir(parents=True, exist_ok=True)
archive_path = output_dir / f"marginal-plugin-directory-{version}.zip"

with zipfile.ZipFile(
archive_path,
"w",
compression=zipfile.ZIP_DEFLATED,
compresslevel=9,
) as archive:
for path in _included_files(plugin_root):
relative = path.relative_to(plugin_root).as_posix()
info = zipfile.ZipInfo(relative, date_time=REPRODUCIBLE_TIMESTAMP)
info.compress_type = zipfile.ZIP_DEFLATED
info.create_system = 3
mode = 0o755 if path.stat().st_mode & stat.S_IXUSR else 0o644
info.external_attr = (stat.S_IFREG | mode) << 16
archive.writestr(info, path.read_bytes(), compresslevel=9)

return archive_path


def main() -> None:
parser = argparse.ArgumentParser(
description="Build the deterministic MARGINAL Plugins Directory ZIP."
)
parser.add_argument(
"--output-dir",
type=Path,
default=Path("dist"),
help="Archive destination (default: dist)",
)
args = parser.parse_args()
repo = Path(__file__).resolve().parents[1]
print(build_submission_archive(repo, output_dir=args.output_dir))


if __name__ == "__main__":
main()
2 changes: 1 addition & 1 deletion src/marginal/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,4 @@
"validate_safe_telemetry_record",
]

__version__ = "0.3.0"
__version__ = "0.3.1"
2 changes: 1 addition & 1 deletion src/marginal/integrations/codex/identity.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from .installer import CommandRunner, SubprocessRunner, inspect_codex
from .promotion import PromotionIdentity

PLUGIN_VERSION = "0.3.0"
PLUGIN_VERSION = "0.3.1"
ADAPTER_VERSION = "1"
POLICY_HASH = hashlib.sha256(b"marginal:no-progress:v1:max-same-evidence=2").hexdigest()
DEFAULT_HOOK_HASH = "46b7a85a3a542957d055c615ab501f9fee284bb3193ac9ecfbe8951cce5a9942"
Expand Down
50 changes: 50 additions & 0 deletions tests/plugin/test_codex_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
import json
import subprocess
import sys
import zipfile
from pathlib import Path

import pytest
from scripts.build_codex_plugin import build_plugin_runtime
from scripts.build_codex_plugin_submission import build_submission_archive

REPO = Path(__file__).resolve().parents[2]
PLUGIN = REPO / "plugins" / "marginal"
Expand Down Expand Up @@ -45,6 +47,54 @@ def test_manifest_is_validator_clean() -> None:
assert completed.returncode == 0, completed.stdout + completed.stderr


def test_manifest_has_directory_publication_metadata() -> None:
manifest = json.loads((PLUGIN / ".codex-plugin" / "plugin.json").read_text(encoding="utf-8"))

assert manifest["homepage"] == "https://signallayerlabs.github.io/Marginal/"
assert manifest["repository"] == "https://github.com/SignalLayerLabs/Marginal"
assert manifest["license"] == "Apache-2.0"
assert {"compute-governance", "codex", "token-efficiency"} <= set(manifest["keywords"])

interface = manifest["interface"]
assert interface["websiteURL"] == manifest["homepage"]
assert interface["privacyPolicyURL"].endswith("/privacy.html")
assert interface["termsOfServiceURL"].endswith("/terms.html")
assert interface["brandColor"] == "#22D3EE"
assert interface["composerIcon"] == "./assets/marginal-logo.png"
assert interface["logo"] == "./assets/marginal-logo.png"
assert 1 <= len(interface["defaultPrompt"]) <= 3
assert all(len(prompt) <= 128 for prompt in interface["defaultPrompt"])


def test_directory_logo_is_square_png() -> None:
logo = PLUGIN / "assets" / "marginal-logo.png"
data = logo.read_bytes()

assert data[:8] == b"\x89PNG\r\n\x1a\n"
width = int.from_bytes(data[16:20], "big")
height = int.from_bytes(data[20:24], "big")
assert width == height
assert width >= 512


def test_directory_submission_archive_is_reproducible_and_complete(tmp_path: Path) -> None:
first = build_submission_archive(REPO, output_dir=tmp_path / "first")
second = build_submission_archive(REPO, output_dir=tmp_path / "second")

assert _sha256(first) == _sha256(second)
with zipfile.ZipFile(first) as archive:
names = set(archive.namelist())
assert {
".codex-plugin/plugin.json",
"skills/marginal/SKILL.md",
"skills/marginal/agents/openai.yaml",
"hooks/hooks.json",
"runtime/marginal_runtime.pyz",
"assets/marginal-logo.png",
} <= names
assert not any("__pycache__" in name or name.endswith(".pyc") for name in names)


def test_hooks_cover_exact_supported_lifecycle() -> None:
hooks = json.loads((PLUGIN / "hooks" / "hooks.json").read_text(encoding="utf-8"))

Expand Down
2 changes: 1 addition & 1 deletion tests/test_public_api_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def test_v02_public_exports_and_version() -> None:
"ValueEstimate",
}
assert expected.issubset(set(marginal.__all__))
assert marginal.__version__ == "0.3.0"
assert marginal.__version__ == "0.3.1"


def test_json_schemas_exist_and_are_valid() -> None:
Expand Down
Loading