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
6 changes: 3 additions & 3 deletions .github/actions/verify/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ supplied by you and is not collection-attested.
Gate an OFF and an ON evidence bundle (both must pass):

```yaml
- uses: AlexPaiva/PromiseProof/.github/actions/verify@main
- uses: AlexPaiva/PromiseProof/.github/actions/verify@submission-rc-03
with:
mode: gate
off_evidence: artifacts/personalization-off.json
Expand All @@ -23,7 +23,7 @@ Gate an OFF and an ON evidence bundle (both must pass):
Verify a single bundle:

```yaml
- uses: AlexPaiva/PromiseProof/.github/actions/verify@main
- uses: AlexPaiva/PromiseProof/.github/actions/verify@submission-rc-03
with:
mode: verify
evidence: artifacts/personalization-off.json
Expand All @@ -32,7 +32,7 @@ Verify a single bundle:
Reproduce a report from its evidence:

```yaml
- uses: AlexPaiva/PromiseProof/.github/actions/verify@main
- uses: AlexPaiva/PromiseProof/.github/actions/verify@submission-rc-03
with:
mode: check
report: artifacts/promiseproof/report.json
Expand Down
2 changes: 1 addition & 1 deletion ADOPTION.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ A bundled GitHub Action runs the same frozen verifier in a workflow with no `npm
Gate an OFF and an ON bundle:

```yaml
- uses: AlexPaiva/PromiseProof/.github/actions/verify@main
- uses: AlexPaiva/PromiseProof/.github/actions/verify@submission-rc-03
with:
mode: gate
off_evidence: artifacts/personalization-off.json
Expand Down
88 changes: 88 additions & 0 deletions JUDGE_START_HERE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# PromiseProof: Judge Start Here

## 15-second explanation

When AI says "fixed," that is a claim, not a fact. GPT-5.6 investigates. Codex repairs. Neither decides PASS. An unchanged deterministic verifier does.

## Judge in 60 seconds

**1. Watch the recorded repair lifecycle.**
https://promiseproof.alex0paiva0.workers.dev/walkthrough/

Five stages: Observe, Investigate, Replay, Repair, Prove. It replays one authentic run: a user turned personalization off, one identifiable request still reached the recommendation service, GPT-5.6 ranked competing hypotheses and selected one allowlisted factual replay, Codex proposed a two-file repair, a human approved the exact patch, and an unchanged test decided PASS.

**2. Challenge the proof yourself.**
https://promiseproof.alex0paiva0.workers.dev/verify/?judge=1

After the page loads, verification runs entirely in the browser. The verdict path uses no backend API, evidence upload, or model call. Expected states:

- On load: `PASS` and `BOUND_AND_REPRODUCED`.
- After you tamper one OFF observation: `BROKEN_PROMISE`, violation `PP_IDENTIFIABLE_EVENT_LEAK`, and the report you sealed a moment earlier becomes `STALE_OR_MISMATCH`.
- After you seal the failing evidence: `BROKEN_PROMISE` and `BOUND_AND_REPRODUCED`. A report can honestly bind a failing verdict; it cannot manufacture a PASS.
- After reset: the original `PASS` and original evidence bindings return.

**3. Inspect the GitHub Action.**
https://github.com/AlexPaiva/PromiseProof/tree/submission-rc-03/.github/actions/verify

A bundled Action that runs the same verifier in CI with no `npm install`, no browser, and no API key.

## Who has authority

| Actor | May investigate | May propose source changes | May approve the exact patch | May decide PASS |
| --- | --- | --- | --- | --- |
| GPT-5.6 | Yes | No | No | No |
| Codex | Yes | Yes, in isolation | No | No |
| Human | Yes | No | Yes | No |
| Unchanged verifier | No | No | No | Yes |

## Recorded versus fresh

- **Recorded and authentic:** the GPT-5.6 investigation, the Codex source repair, and the five-stage walkthrough that presents them. These happened once and are replayed, not re-run live.
- **Fresh every time you interact:** the browser verification, the semantic tamper, the CLI verification, and the GitHub Action verification. These re-run the deterministic evaluator on the evidence in front of you.

## Developer Tool path

One deterministic authority, three surfaces:

- **Hosted verifier** at `/verify/?judge=1`, no login and no key.
- **Repository-local CLI:** `npm run promiseproof -- gate|verify|check`.
- **Bundled GitHub Action:** `uses: AlexPaiva/PromiseProof/.github/actions/verify@submission-rc-03`.

No API key, no browser, and no model call in the verdict path. Exactly one contract family is supported: `activity-personalization/v1`. External evidence is evaluated but not collection-attested.

## Test locally

Requirements: Node.js 22.12+, npm 10+, and (for the walkthrough tests) Playwright Chromium.

```bash
npm ci
npx playwright install chromium

# No-key recorded repair rehearsal: applies the approved patch in a
# disposable worktree and runs the unchanged verifier.
npm run demo:rehearse

# Deterministic external verification from evidence:
npm run promiseproof -- init --out .promiseproof
npm run promiseproof -- gate \
--off .promiseproof/passing-off.example.json \
--on .promiseproof/passing-on.example.json \
--out .promiseproof/report
npm run promiseproof -- check \
--report .promiseproof/report/report.json \
--off .promiseproof/passing-off.example.json \
--on .promiseproof/passing-on.example.json

# Test suites:
npm run test:external # CLI, bound-report reproduction, strict validation
npm run test:action # the bundled Action, spawned as a real Action
```

## Scope and limitations

- Signal Shelf is a synthetic reference application. It exists to make one broken promise visible and repairable end to end. This is not a legal or regulatory compliance statement.
- Exactly one external contract family is supported: `activity-personalization/v1`.
- Evidence is externally supplied and is not collection-attested. Content binding proves a report matches its evidence and the pinned evaluator source; it does not prove the evidence was collected honestly.
- The `main` branch stays intentionally seeded-broken so the red-to-green repair can be demonstrated. Production main is not automatically repaired.
- Integrity is repository-level and content-addressed. It is not a signature, notarization, certification, or third-party attestation.
- The CLI is verified on Windows. The bundled Action is exercised on Windows, Ubuntu, and macOS runners.
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

### When software breaks a promise, the AI that repairs it doesn't get the final word.

**GPT-5.6 investigates. Codex repairs. Neither decides PASS.**

Carry a broken product promise all the way through bounded diagnosis, constrained repair, and independent verification, where a deterministic test, *never a model*, decides PASS.

[![OpenAI Build Week 2026](https://img.shields.io/badge/OpenAI-Build_Week_2026-10a37f)](https://openai.com)
Expand All @@ -12,7 +14,7 @@ Carry a broken product promise all the way through bounded diagnosis, constraine
[![Playwright](https://img.shields.io/badge/Playwright-verified-2fa968)](https://playwright.dev/)
[![Cloudflare Workers](https://img.shields.io/badge/hosted-Cloudflare_Workers-f38020)](https://workers.cloudflare.com/)

**[▶ Live demo](https://promiseproof.alex0paiva0.workers.dev/)** · **[Judge walkthrough](https://promiseproof.alex0paiva0.workers.dev/walkthrough/)** · [How it works](#how-it-works) · [Quick start](#quick-start-the-judge-path)
**[▶ Live demo](https://promiseproof.alex0paiva0.workers.dev/)** · **[Judge Start Here](JUDGE_START_HERE.md)** · **[Judge walkthrough](https://promiseproof.alex0paiva0.workers.dev/walkthrough/)** · [How it works](#how-it-works) · [Quick start](#quick-start-the-judge-path)

![PromiseProof: find the boundary that broke the promise](public/og-card.png)

Expand Down Expand Up @@ -132,15 +134,15 @@ npm run promiseproof -- check \
**GitHub Action**, drop the verifier into any workflow with no `npm install`, browser, or key:

```yaml
- uses: AlexPaiva/PromiseProof/.github/actions/verify@main
- uses: AlexPaiva/PromiseProof/.github/actions/verify@submission-rc-03
with:
mode: gate
off_evidence: artifacts/personalization-off.json
on_evidence: artifacts/personalization-on.json
output_directory: artifacts/promiseproof
```

The step fails on `BROKEN_PROMISE` and still leaves `report.json` and `report.md` behind. Replace `@main` with an immutable release tag once one contains the Action. Details in the [Action README](.github/actions/verify/README.md).
The step fails on `BROKEN_PROMISE` and still leaves `report.json` and `report.md` behind. `@submission-rc-03` is the immutable release tag that contains the Action; `@main` also works if you prefer to track the branch. Details in the [Action README](.github/actions/verify/README.md).

What it is, stated exactly:

Expand Down
3 changes: 3 additions & 0 deletions landing/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@
<div class="hd-r">
<span class="pill mono">Build Week 2026 / Developer Tools</span>
<a class="btn btn-ghost" href="#how">How it works</a>
<a class="btn btn-ghost ext" href="/verify/?judge=1">Challenge the proof</a>
<a class="btn btn-primary ext" href="/walkthrough/">Open the walkthrough</a>
</div>
</div>
Expand All @@ -269,10 +270,12 @@
<div class="wrap">
<span class="eyebrow"><span class="dot"></span> A user turned personalization off. One identifiable request still crossed.</span>
<h1 class="hero-h">When software breaks a promise, the AI that repairs it <span class="g">doesn't get the final word.</span></h1>
<p class="hero-thesis" style="font-family:var(--mono);font-size:clamp(.95rem,2vw,1.28rem);font-weight:700;color:#e8edf6;margin:16px 0 0;letter-spacing:.005em">GPT-5.6 investigates. Codex repairs. <span style="color:var(--green-bright)">Neither decides PASS.</span></p>
<p class="hero-sub">PromiseProof turns one "off means off" promise into cross-system evidence. GPT-5.6 proposes and ranks candidate explanations, then requests one allowlisted replay; PromiseProof runs it. Codex proposes a constrained two-file repair, a human approves the exact patch, and an unchanged Playwright journey and deterministic evaluator, <b>never a model</b>, decide PASS.</p>
<p class="hero-contrast">Monitoring can tell you a promise broke. PromiseProof carries the failure through bounded diagnosis, constrained repair, and independent verification.</p>
<div class="hero-cta">
<a class="btn btn-primary ext" href="/walkthrough/">Open the walkthrough</a>
<a class="btn btn-primary ext" href="/verify/?judge=1">Challenge the proof</a>
<a class="btn btn-ghost" href="#how">See how it works &darr;</a>
</div>

Expand Down
8 changes: 4 additions & 4 deletions src/judge/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,7 @@ function renderInvestigate(data: JudgeData): HTMLElement {
element(
"p",
"replay-caption",
"A registered replay is one allowlisted factual experiment the model can request; deterministic code runs it and records the facts. This recorded replay confirms the leading explanation it does not decide the verdict.",
"A registered replay is one allowlisted factual experiment the model can request; deterministic code runs it and records the facts. This recorded replay confirms the leading explanation, but it does not decide the verdict.",
),
);
fork.append(down);
Expand Down Expand Up @@ -658,7 +658,7 @@ function renderReplay(data: JudgeData): HTMLElement {
section.append(toolbar);

// The reveal is a fresh CSS animation on mount, so re-rendering these nodes
// replays it on demand — the crossing is the one moment judges should see move.
// replays it on demand. The crossing is the one moment judges should see move.
// `data-replaying` marks the explicit "Play the crossing" click: under
// prefers-reduced-motion the crossing stays still on stage entry (honoring the
// preference) and only animates when the user actually asks for it.
Expand Down Expand Up @@ -1252,7 +1252,7 @@ function start(): void {
return;
}
stageHost.replaceChildren(definition.render(data));
// Marks that the DOM now holds THIS stage — the signal transitions settle on.
// Marks that the DOM now holds THIS stage. The signal transitions settle on.
root.dataset.rendered = stage;
if (focus) {
stageHost.focus();
Expand Down Expand Up @@ -1294,7 +1294,7 @@ function start(): void {
return;
}

// Ease the outgoing content out, swap, then ease the incoming content in
// Ease the outgoing content out, swap, then ease the incoming content in,
// a real crossfade instead of a hard cut.
stageHost.classList.remove("is-entering");
stageHost.classList.add("is-leaving");
Expand Down
24 changes: 12 additions & 12 deletions src/verify-ui/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ function offFacts(ev: WorkEvidence): Fact[] {
const captured = ev.activity.capturedActivities.length;
const receipts = ev.activity.recommendationServiceReceipts.length;
return [
{ k: "Scenario", v: "OFF personalization off" },
{ k: "Scenario", v: "OFF, personalization off" },
{ k: "Subject", v: ev.subjectId },
{
k: "Preference (ui / stored / backend)",
Expand All @@ -142,7 +142,7 @@ function offFacts(ev: WorkEvidence): Fact[] {
}
function onFacts(ev: WorkEvidence): Fact[] {
return [
{ k: "Scenario", v: "ON personalization on" },
{ k: "Scenario", v: "ON, personalization on" },
{ k: "Subject", v: ev.subjectId },
{
k: "Preference (ui / stored / backend)",
Expand Down Expand Up @@ -224,15 +224,15 @@ function renderViolations(list: HTMLElement, violations: readonly ViolationLike[
list.replaceChildren();
if (violations.length === 0) {
list.className = "vf-violations empty";
list.append(tag("li", undefined, "Promise verified no violations."));
list.append(tag("li", undefined, "Promise verified, no violations."));
return;
}
list.className = "vf-violations";
for (const violation of violations) {
const item = tag("li");
item.append(
tag("code", undefined, violation.code),
document.createTextNode(` (${violation.clause}) ${violation.message}`),
document.createTextNode(` (${violation.clause}): ${violation.message}`),
);
list.append(item);
}
Expand All @@ -259,13 +259,13 @@ function noteFor(outcome: string, repro: string, tampered: boolean): string {
return "This evidence does not pass strict validation, so the deterministic evaluator does not run on it.";
}
if (outcome === "PASS" && repro === "BOUND_AND_REPRODUCED") {
return "PASS. The deterministic evaluator verified the promise over the recorded evidence, and the sealed report reproduces exactly from it. No model graded this — an unchanged evaluator did.";
return "PASS. The deterministic evaluator verified the promise over the recorded evidence, and the sealed report reproduces exactly from it. No model graded this. An unchanged evaluator did.";
}
if (outcome === "BROKEN_PROMISE" && repro !== "BOUND_AND_REPRODUCED") {
return "Broken promise. Identifiable activity is present in the OFF evidence, so the same evaluator now fails no_identifiable_activity and raises PP_IDENTIFIABLE_EVENT_LEAK. The report sealed a moment ago no longer reproduces from this evidence — a PASS cannot be silently carried onto changed evidence.";
return "Broken promise. Identifiable activity is present in the OFF evidence, so the same evaluator now fails no_identifiable_activity and raises PP_IDENTIFIABLE_EVENT_LEAK. The report sealed a moment ago no longer reproduces from this evidence. A PASS cannot be silently carried onto changed evidence.";
}
if (outcome === "BROKEN_PROMISE" && repro === "BOUND_AND_REPRODUCED") {
return "Broken promise, sealed honestly. A new report was sealed for the current evidence and it reproduces as BROKEN_PROMISE. Sealing binds whatever is true; it cannot manufacture a PASS.";
return "Broken promise, sealed honestly. A new report was sealed for the current evidence and it reproduces, as BROKEN_PROMISE. Sealing binds whatever is true; it cannot manufacture a PASS.";
}
if (tampered) {
return "The evidence changed. Re-evaluate it, and verify the sealed report against it.";
Expand Down Expand Up @@ -324,8 +324,8 @@ const APP_HTML = `
<div class="vf-result" style="margin-top:20px">
<div id="vf-live" role="status" aria-live="polite">
<div class="vf-verdict">
<span class="vf-pill" id="vf-outcome" data-testid="outcome-pill"><span class="vf-pill-label">Current evidence</span></span>
<span class="vf-pill" id="vf-repro" data-testid="repro-pill"><span class="vf-pill-label">Sealed report</span></span>
<span class="vf-pill" id="vf-outcome" data-testid="outcome-pill"><span class="vf-pill-label">Current evidence</span></span>
<span class="vf-pill" id="vf-repro" data-testid="repro-pill"><span class="vf-pill-label">Sealed report</span></span>
</div>
<p class="vf-result-note" id="vf-note" data-testid="result-note"></p>
</div>
Expand Down Expand Up @@ -386,7 +386,7 @@ const APP_HTML = `
</div>
<div class="vf-result" style="margin-top:16px" id="vf-byo-result" hidden>
<div class="vf-verdict">
<span class="vf-pill" id="vf-byo-outcome" data-testid="byo-outcome"><span class="vf-pill-label">Local evidence</span></span>
<span class="vf-pill" id="vf-byo-outcome" data-testid="byo-outcome"><span class="vf-pill-label">Local evidence</span></span>
</div>
<ul class="vf-issues" id="vf-byo-issues" hidden></ul>
<table class="vf-clauses" id="vf-byo-table" hidden>
Expand All @@ -405,7 +405,7 @@ const APP_HTML = `
<div class="vf-panel-body">
<p><strong>What it proves.</strong> The verdict comes from one deterministic evaluator whose source is fingerprinted above. The AI that diagnoses and repairs the promise never writes PASS; this unchanged evaluator does, and you can re-derive it here or from the CLI and get the same answer.</p>
<p><strong>Tamper-evidence.</strong> A sealed report is bound to the exact evidence it was computed from. Change the evidence and the report no longer reproduces (STALE_OR_MISMATCH); you can only ever seal what is actually true.</p>
<p><strong>What it does not claim.</strong> Evidence is externally supplied and, here, synthetic. PromiseProof does not attest how evidence was collected only that a bundle which passes strict validation is evaluated deterministically. Full loop and source: <a href="https://github.com/AlexPaiva/PromiseProof">github.com/AlexPaiva/PromiseProof</a>.</p>
<p><strong>What it does not claim.</strong> Evidence is externally supplied and, here, synthetic. PromiseProof does not attest how evidence was collected, only that a bundle which passes strict validation is evaluated deterministically. Full loop and source: <a href="https://github.com/AlexPaiva/PromiseProof">github.com/AlexPaiva/PromiseProof</a>.</p>
</div>
</section>
`;
Expand Down Expand Up @@ -613,7 +613,7 @@ function wireFile(
byId("vf-byo-result").hidden = true;
if (file.size > MAX_INPUT_BYTES) {
const issue = `<root>: input exceeds ${MAX_INPUT_BYTES}-byte limit`;
nameEl.textContent = `${file.name} rejected`;
nameEl.textContent = `${file.name} (rejected)`;
input.value = "";
renderByoInvalid(issue);
toast("That file exceeds the verifier input-size limit.");
Expand Down
2 changes: 1 addition & 1 deletion src/verify-ui/styles.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* PromiseProof Verify Judge Mode. Dark control-console styling on the same
/* PromiseProof Verify. Judge Mode. Dark control-console styling on the same
palette and system type as the public landing, so /verify/ reads as one site.
No web fonts (CSP-safe, offline-safe); state is carried by semantic colour. */

Expand Down
Loading