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
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.1.8] - 2026-07-21

### Fixed

- Bounded the evidenced-partner deny guidance: include the recorded partners, or stop and review the exception with a human; no approval bypass is implied.
- Distinguished the local two-partner fixture walkthrough from Billfold's separate one-partner provider-demo proof path.
- Corrected public wording for the hero, generator producer, network boundary, path coverage limits, and Billfold reproduction links.

## [0.1.5] - 2026-07-17

### Fixed
Expand Down
40 changes: 33 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

<br />

<p align="center"><strong>Portable repository history that changes Codex's plan before an evidenced risky edit lands.</strong></p>
<p align="center"><strong>Repository evidence that helps Codex plan around recorded risky changes.</strong></p>

<p align="center"><code>@workspacejson/codex-mcp</code></p>

Expand All @@ -25,9 +25,8 @@
| | |
| --- | --- |
| Task | Update the checkout route |
| Without `workspace.json` | Codex proposes one file |
| With `workspace.json` | The hook identifies two evidenced partners |
| Enforcement | The incomplete patch is denied |
| Recorded evidence | The route and its webhook partner share a co-change history — an incident and a revert, not an import |
| An incomplete patch | The hook denies it, citing the specific evidence and the omitted partner |
| Outcome | Codex revises the changeset before the edit lands |

## Installation
Expand Down Expand Up @@ -115,7 +114,7 @@ npx agents-audit@0.4.3 generate .

This writes `.agents/workspace.json` with repository topology and hygiene. Today, `generated.fileIndex` is empty and `manual` fragility/co-change evidence is not auto-generated — those remain human-authored (ASSERTED tier at minimum, OBSERVED when backed by evidence records). The generator does not guess risk signals; guessed churn has no evidence records, remains ASSERTED, and cannot block. See [`fixture/`](fixture/) for a worked example with manual evidence.

### Verify in two minutes
### Local proof path — two recorded partners

`generate` (above) writes repository topology only — no fragility or co-change evidence, so a freshly generated `workspace.json` has nothing to deny yet. To see the deny path itself, use this repo's `fixture/`, whose `manual` evidence is hand-authored for exactly this demo:

Expand All @@ -125,12 +124,36 @@ This writes `.agents/workspace.json` with repository topology and hygiene. Today

No configuration beyond step 1 above. On your own repo, the same deny path activates once you've authored `manual.fragileFiles` / `manual.coChangePatterns` yourself — see [`docs/workspace-contract.md`](docs/workspace-contract.md).

### Provider-demo proof path — Billfold's one recorded partner

The judge-facing demo runs against [`workspace-json/billfold`](https://github.com/workspace-json/billfold), a small public payments service. This is a separate proof path from this repository's local `fixture/`: Billfold uses the single recorded pairing shown on camera, `src/routes/checkout.ts` and `src/webhooks/stripe.ts`; the local walkthrough above uses `src/auth/session.ts` and `src/lib/format.ts`.

```bash
git clone https://github.com/workspace-json/billfold.git
cd billfold
git checkout 5e97f1dc9e6a41eb80d2d6eb80d5ef703cbe1cde # main as of 2026-07-20; no tag covers this pairing yet
npm install
npx @workspacejson/codex-mcp install --with-hook
```

1. Open `billfold` in Codex. Ask it to change the idempotency-key format in `src/routes/checkout.ts`.
2. The hook denies the patch, citing the recorded revert/incident and the omitted partner, `src/webhooks/stripe.ts`.
3. Ask Codex to include `src/webhooks/stripe.ts` and retry — the patch proceeds. That clears the recorded-partner check; it is not a correctness verdict on the change (see [Current limitations](#current-limitations)).

This pins to the commit above because `billfold`'s `main` is mutable and the two existing tags (`fixture-v1`, `fixture-v2`) predate this pairing — clone and stay on `main` instead if you want the current state.

</details>

## How it works

MCP supplies context. A deterministic hook enforces evidenced omissions. An optional, direct read-only GPT-5.6 API review challenges a supplied completed diff and preserves its request/response receipt locally. The reviewer never controls the hook, and a `PASS` verdict is not a safety certification.

```bash
git diff | npx @workspacejson/codex-mcp review --diff-stdin
```

Requires `OPENAI_API_KEY` (or `OPENROUTER_API_KEY`) in the environment. Without one, it reports `UNAVAILABLE` and deterministic enforcement is unaffected.

Full derivation rules for evidence tiers (`ASSERTED`/`OBSERVED`/`VERIFIED`), the hook's fail-open behavior, and the GPT-5.6 reviewer's scope live in [`docs/how-it-works.md`](docs/how-it-works.md).

## Operational guarantees
Expand All @@ -147,7 +170,9 @@ Each is checkable, not asserted: run `npm run verify` from a clean clone to repr

## Trust boundary

The MCP and deterministic hook run locally over stdio and do not upload repository contents. Initial package installation may contact npm. The optional `review` command sends only the diff you explicitly supply to a configured API provider: OpenAI (`OPENAI_API_KEY`) or OpenRouter (`OPENROUTER_API_KEY`). When both keys exist, set `WORKSPACEJSON_REVIEWER_PROVIDER` to `openai` or `openrouter`; an explicit `WORKSPACEJSON_REVIEWER_BASE_URL` also selects OpenRouter. It uses `store: false` with OpenAI and preserves a local request/response receipt that identifies the provider and model. Do not supply diffs containing secrets.
**Local, no network:** the MCP server, the deterministic hook, and the VS Code extension run over stdio and the local filesystem only. None of them upload repository contents or make network calls.

**Network, by explicit action only:** `npx` package installation contacts npm. The optional `review` command sends only the diff you explicitly supply to a configured API provider: OpenAI (`OPENAI_API_KEY`) or OpenRouter (`OPENROUTER_API_KEY`). When both keys exist, set `WORKSPACEJSON_REVIEWER_PROVIDER` to `openai` or `openrouter`; an explicit `WORKSPACEJSON_REVIEWER_BASE_URL` also selects OpenRouter. It uses `store: false` with OpenAI and preserves a local request/response receipt that identifies the provider and model. Do not supply diffs containing secrets.

## Current limitations

Expand All @@ -156,6 +181,7 @@ The MCP and deterministic hook run locally over stdio and do not upload reposito
- Missing or malformed `workspace.json` fails open with an explicit unavailable warning.
- Stale evidence is not treated as proof of current risk.
- `fragile:false` means the file has no recorded fragility, not that it is verified safe.
- Including a recorded partner's path clears the omission check; it confirms path coverage, not that the partner's content is correct or sufficient.
- This does not replace tests, review, or repository instructions.

## Learn more
Expand All @@ -168,7 +194,7 @@ The MCP and deterministic hook run locally over stdio and do not upload reposito
- [Verification](docs/verification.md) — what's been verified and how
- [Build Week disclosure](docs/submission/build-week.md) — what was authored in-window
- [Development](docs/development.md) — build, test, and smoke-suite commands
- [Clean-install audit](docs/clean-install-audit.md) · [Fixture verification](docs/fixture-verification.md)
- [Clean-install audit](docs/clean-install-audit.md) · [Fixture verification](docs/fixture-verification.md) · [`billfold`](https://github.com/workspace-json/billfold) — the public repo behind the demo video

## License

Expand Down
76 changes: 51 additions & 25 deletions docs/fixture-verification.md
Original file line number Diff line number Diff line change
@@ -1,42 +1,68 @@
# Checkout fixture verification
# Billfold fixture verification

This public fixture is a small, controlled checkout scenario for reproducing
the documented co-change behavior. It is not a production incident record.
Billfold is the public fixture for the `workspace.json` / Codex demonstration. It is a
small, controlled checkout scenario for reproducing the documented co-change behavior —
not a production incident record.

## Frozen fixture
## Repository

- Repository: `workspace-json/codex-demo-fixture`.
- Annotated tag and commit: [`fixture-v2`](https://github.com/workspace-json/codex-demo-fixture/tree/dc6f4d721affac96d517ca96cad8ccf8d9c15e3c)
→ `dc6f4d721affac96d517ca96cad8ccf8d9c15e3c`.
- Artifact: [`.agents/workspace.json`](https://github.com/workspace-json/codex-demo-fixture/blob/dc6f4d721affac96d517ca96cad8ccf8d9c15e3c/.agents/workspace.json).
- Artifact Git blob: `a6807d3ad39aa3f3a1f3471c2ae1d4288f879149`.
- Repository: [`workspace-json/billfold`](https://github.com/workspace-json/billfold).
- Reference commit: [`5e97f1d`](https://github.com/workspace-json/billfold/tree/5e97f1dc9e6a41eb80d2d6eb80d5ef703cbe1cde)
→ `5e97f1dc9e6a41eb80d2d6eb80d5ef703cbe1cde` (2026-07-20).
- No annotated tag currently covers this pairing. The two existing tags,
`fixture-v1` and `fixture-v2`, predate it and point to a different recorded
pairing (`checkout.ts` co-changing with `src/auth/session.ts` and
`src/lib/format.ts` — the same pairing this repo's own `fixture/` directory
reproduces locally). Do not cite `fixture-v1`/`fixture-v2` for the pairing
described below.
- Artifact: [`.agents/workspace.json`](https://github.com/workspace-json/billfold/blob/5e97f1dc9e6a41eb80d2d6eb80d5ef703cbe1cde/.agents/workspace.json).
- Artifact Git blob: `b86f453b716e1f03dc2cb93734e48ba20fdd9f55`.
- Artifact SHA-256:
`5c97c81c8d6457e795c174d740026862512925cbf2efa9c66c1a18712285593d`.
`be4072f3e1937f970fab290ec96ba9eabe34827623a84176be816d13e64b0484`.

The primary edit path is `src/routes/checkout.ts`; its recorded co-change
partners are `src/auth/session.ts` and `src/lib/format.ts`.
This is the provider-demo proof path. It is separate from this repository's local
`fixture/` walkthrough, which records two checkout partners (`src/auth/session.ts` and
`src/lib/format.ts`); Billfold records the one checkout/Stripe partner below.

The primary edit path is `src/routes/checkout.ts`; its recorded co-change partner is
`src/webhooks/stripe.ts`. The relationship is not visible through a direct import or
shared symbol — checkout builds a retry key that the webhook parses independently.
It is recorded as a co-change pattern plus a fragility reason citing a 2026-02-28
rounding change to `checkout.ts` that was reverted two days later
(`985b0d4`, `05989dc`), both reproducible with the `git log`/`git show` commands
embedded in the artifact's own evidence entries.

## Reproduce

```sh
git clone https://github.com/workspace-json/codex-demo-fixture.git
cd codex-demo-fixture
git switch --detach fixture-v2
npm test
git clone https://github.com/workspace-json/billfold.git
cd billfold
git checkout 5e97f1dc9e6a41eb80d2d6eb80d5ef703cbe1cde
npm install
npx @workspacejson/codex-mcp install --with-hook
```

The route-only regression commit
`ca1f7ec8e124b4050deb5cd6d704bea0fe1dcee7` fails `npm test`. The corrective
co-change commit `a9729be1486dc199adc1f42371847217cba9d883` and `fixture-v2`
pass it.
Ask Codex to change the idempotency-key format in `src/routes/checkout.ts` alone. The
hook denies the patch (exit code 2), citing the recorded evidence and the omitted
partner, `src/webhooks/stripe.ts`. Including that partner in the same patch clears the
deny; this confirms the recorded path is present, not that the included change is
correct — see the README's [Current limitations](../README.md#current-limitations).

`billfold`'s own `scripts/capture-red-evidence.mjs` still hardcodes a regression commit
(`ca1f7ec8e124b4050deb5cd6d704bea0fe1dcee7`) from the prior `session.ts`/`format.ts`
pairing, predating the `stripe.ts` evidence above. It has not been updated for this
pairing and should not be used to reproduce a red/green `npm test` pair for the
checkout/webhook relationship described here.

## Spec compatibility

The plugin reads only `manual.fragileFiles`, `manual.coChangePatterns`,
`generated.fileIndex`, and `generated.frameworkManifest` when present.

The frozen artifact is a documented validate-and-warn case: with
`@workspacejson/spec` v0.4.1, both `validate()` and `validateV4()` return
`false`. The consumer still normalizes the locked paths, returning the checkout
fragility record, both recorded partners, and an indexed primary path. This
does not turn the validation discrepancy into an approval or safety claim.
Checked against the currently published `@workspacejson/spec@0.4.3`: `validate()`
returns `true` for the reference artifact above; `validateV4()` and `validateLegacy()`
both return `false`. The consumer still normalizes the locked paths, returning the
checkout fragility record, the recorded partner, and an indexed primary path. This
does not turn the `validateV4()`/`validateLegacy()` result into an approval or safety
claim, and `validate() === true` is not a correctness claim about the recorded
evidence either — only that the document's shape matches the general schema.
2 changes: 1 addition & 1 deletion docs/submission/build-week.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# For the OpenAI Build Week submission

> This project was built during OpenAI Build Week as a new Codex integration for the open `workspace.json` standard. The `workspace.json` standard and its generator (`@workspacejson/cli`) are pre-existing open-source work, used here as a dependency the same way any participant could depend on it. The Build Week contribution is `@workspacejson/codex-mcp`: the MCP server, its tool surface, deterministic hook, packaging, and optional direct GPT-5.6 read-only API reviewer, all authored or integrated in-window and contained in this repository.
> This project was built during OpenAI Build Week as a new Codex integration for the open `workspace.json` standard. The `workspace.json` standard and its generator (`agents-audit`) are pre-existing open-source work, used here as a dependency the same way any participant could depend on it. The Build Week contribution is `@workspacejson/codex-mcp`: the MCP server, its tool surface, deterministic hook, packaging, and optional direct GPT-5.6 read-only API reviewer, all authored or integrated in-window and contained in this repository.

Codex accelerated implementation, regression-test generation, packaging validation, and adversarial review. Human decisions control the product boundary: deterministic evidence remains the enforcement plane; GPT-5.6 performs visible semantic risk review without write or enforcement authority; Q approves the fixture, claims, evidence tiers, design, narration, and submission.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@workspacejson/codex-mcp",
"version": "0.1.7",
"version": "0.1.8",
"description": "MCP server that surfaces workspace.json fragility and co-change intelligence to OpenAI Codex before it edits code.",
"license": "Apache-2.0",
"type": "module",
Expand Down
15 changes: 15 additions & 0 deletions scripts/smoke.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ check(
]),
names.join(","),
);
const assessTool = tools.find((tool) => tool.name === "workspace_assess_change");
check(
"assessment tool describes the bounded human-review path",
assessTool?.description?.includes(
"Include the recorded co-change partners, or stop and review the exception with a human.",
),
assessTool?.description,
);

// ── Tier derivation through file context ──
const r1 = await client.callTool({ name: "workspace_get_file_context", arguments: { path: "src/routes/checkout.ts" } });
Expand Down Expand Up @@ -102,6 +110,13 @@ check(
"deny message names missing partners",
/src\/auth\/session\.ts/.test(d1.structuredContent?.assessments?.[0]?.message ?? ""),
);
check(
"deny message uses bounded human-review wording, not an approval bypass",
/Include the recorded co-change partners, or stop and review the exception with a human\./.test(
d1.structuredContent?.assessments?.[0]?.message ?? "",
) && !/get explicit human approval/i.test(d1.structuredContent?.assessments?.[0]?.message ?? ""),
d1.structuredContent?.assessments?.[0]?.message,
);

const d2 = await client.callTool({
name: "workspace_assess_change",
Expand Down
2 changes: 1 addition & 1 deletion src/evidence.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ export function decideEnforcement(input: {

if (evidencedFragile && missingPartners.length > 0) {
action = "deny";
message = `BLOCK [tier ${input.tier}]: ${input.path} is fragile${input.reason ? ` (${input.reason})` : ""} and historically co-changes with ${missingPartners.join(", ")}, which this change omits.${cite} Include the co-change partners or get explicit human approval to proceed without them.`;
message = `BLOCK [tier ${input.tier}]: ${input.path} is fragile${input.reason ? ` (${input.reason})` : ""} and historically co-changes with ${missingPartners.join(", ")}, which this change omits.${cite} Include the recorded co-change partners, or stop and review the exception with a human.`;
} else if (evidencedFragile) {
action = "warn";
message = `CAUTION [tier ${input.tier}]: ${input.path} is fragile${input.reason ? ` (${input.reason})` : ""}.${cite} Prefer minimal, well-tested changes.${
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { isVerifyEnabled } from "./config.js";
import { SERVER_INSTRUCTIONS } from "./constants.js";
import { registerWorkspaceTools } from "./tools/workspace.js";

const VERSION = "0.1.7";
const VERSION = "0.1.8";

function buildServer(): McpServer {
const server = new McpServer(
Expand Down
2 changes: 1 addition & 1 deletion src/tools/workspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ Returns JSON:
description: `Evaluate a SET of file paths (a proposed change) against workspace.json fragility and co-change history, and return a mechanical enforcement decision.

Decision semantics (derived, never model-emitted):
- "deny": an evidenced-fragile file is touched while its recorded co-change partners are absent from the changeset. Include the partners or get explicit human approval.
- "deny": an evidenced-fragile file is touched while its recorded co-change partners are absent from the changeset. Include the recorded co-change partners, or stop and review the exception with a human.
- "warn": evidenced-fragile file touched (partners covered), or co-change partners missing on a non-evidenced file.
- "annotate": fragility asserted without evidence. Context only.
- "none": no recorded history. This is NOT a safety approval; this tool never certifies a change as safe.
Expand Down
21 changes: 21 additions & 0 deletions tests/unit/evidence.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,30 @@ describe("decideEnforcement", () => {
expect(result.message).toContain("BLOCK");
expect(result.message).toContain("src/auth/session.ts");
expect(result.message).toContain("revert d4e5f6");
expect(result.message).toContain(
"Include the recorded co-change partners, or stop and review the exception with a human.",
);
expect(result.message).not.toContain("get explicit human approval");
expect(result.message).not.toContain("safe");
});

it("uses path membership and has no override for an evidenced missing partner", () => {
const input = {
path: "src/routes/checkout.ts",
fragile: true,
tier: "OBSERVED" as const,
evidence: [{ claim: "revert d4e5f6" }],
coChangePartners: ["src/auth/session.ts"],
changesetPaths: ["src/routes/checkout.ts", "src/auth/session-helper.ts"],
override: true,
};

const result = decideEnforcement(input);

expect(result.action).toBe("deny");
expect(result.missingPartners).toEqual(["src/auth/session.ts"]);
});

it("warns when evidenced fragility is touched but partners are covered", () => {
const result = decideEnforcement({
path: "src/routes/checkout.ts",
Expand Down
Loading