Skip to content
Closed
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
71 changes: 71 additions & 0 deletions .github/workflows/pawl.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Pawl ratchet gate. Make the "pawl" job a required status check on main.
# Actions are pinned by commit SHA. Dependabot keeps the pins current.
name: pawl

on:
pull_request:
types: [opened, synchronize, reopened, labeled, unlabeled]
push:
branches: [main]

permissions:
contents: read

concurrency:
group: pawl-${{ github.ref }}
cancel-in-progress: true

jobs:
pawl:
runs-on: ubuntu-latest
timeout-minutes: 60
permissions:
contents: read
pull-requests: write
env:
BASE_REF: ${{ github.base_ref }}
EVENT: ${{ github.event_name }}
PR_NUMBER: ${{ github.event.pull_request.number }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.12"
- name: Install Pawl (hash-pinned, own venv)
run: |
python -m venv "$RUNNER_TEMP/pawl-venv"
cat > "$RUNNER_TEMP/pawl-requirements.txt" <<'REQ'
pawl-gate==0.2.0 \
--hash=sha256:5daa5a256c79bfedc8b7d6ecd0c44d32ecaafb6473859e5b28939614ae2b7ddd \
--hash=sha256:29ef49ad3737004bfb58c4455e1bb89a35ef6b95310a38fd243c38d020ce4d1c
REQ
"$RUNNER_TEMP/pawl-venv/bin/python" -m pip install --require-hashes --no-deps \
--only-binary :all: -r "$RUNNER_TEMP/pawl-requirements.txt"
echo "PAWL=$RUNNER_TEMP/pawl-venv/bin/pawl" >> "$GITHUB_ENV"
- name: Install the project
run: |
python -m pip install --upgrade pip
if [ -f pyproject.toml ]; then pip install -e .; fi
- name: Pawl check (gate definition from the base branch)
run: |
if [ "$EVENT" = "pull_request" ]; then
"$PAWL" check --ci --trusted-ref "origin/$BASE_REF"
else
"$PAWL" check --ci
fi
- name: Pawl guard (tamper detection and test replay)
if: always() && github.event_name == 'pull_request'
run: |
"$PAWL" guard --ci --replay --base "origin/$BASE_REF"
- name: Post the report on the pull request
if: always() && github.event_name == 'pull_request'
continue-on-error: true
env:
GH_TOKEN: ${{ github.token }}
run: |
"$PAWL" report --format md > pawl-report.md
gh pr comment "$PR_NUMBER" --body-file pawl-report.md --edit-last \
|| gh pr comment "$PR_NUMBER" --body-file pawl-report.md
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ node_modules/
coverage/
work/
npm-debug.log*
.pawl/
pawl-test-results.xml
9 changes: 9 additions & 0 deletions PAWL_DEBT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Pawl debt log

Append-only record of every baseline change, newest last. Pawl writes this
file. A human reviews every entry marked LOOSENED.


## 2026-10-08 22:30 MDT: update (tightened)

- tightened site-validation: recorded 0 failing, 1 collected (1 order(s))
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Public product pages map to org repositories:
| Hollersports | [Zero-State-LLC/Hollersports](https://github.com/Zero-State-LLC/Hollersports) |
| Marigold Market | [Zero-State-LLC/Marigold-Market](https://github.com/Zero-State-LLC/Marigold-Market) |
| Noema | [noema.guru](https://noema.guru) |
| Pawl | [pypi.org/project/pawl-gate](https://pypi.org/project/pawl-gate/) |

### Clients

Expand Down Expand Up @@ -78,6 +79,10 @@ npm test

`npm test` runs deterministic site validation (pages, assets, internal links, product posture, identity-motion constraints). Dev server: `http://localhost:8080`.

Pawl ratchets that validation in CI. Locally, run `/path/to/pawl check` after
installing `pawl-gate==0.2.0`; the gate records the canonical `npm test` result
without exposing the private product repositories.

## Social preview

`assets/og-image.png` (1200×630) is referenced by Open Graph and Twitter Card meta on the home page.
Expand Down
1 change: 1 addition & 0 deletions assets/glyphs.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/products/pawl-hero.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 added assets/products/pawl-hero.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions index.html

Large diffs are not rendered by default.

45 changes: 45 additions & 0 deletions intent/2026-10-08-pawl-portfolio-entry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Intent — Pawl portfolio entry

## Problem

Pawl is publicly available on PyPI but is not represented in the Zero State
portfolio. The site needs a project entry and a supporting hero graphic that
match the existing product-page system without exposing the private GitHub
repository as a public destination.

## Proposed outcome

Add Pawl as a product entry with a dark product page, a generated hero asset,
an entry on the official home portfolio map, and the corresponding manifest,
README, and sitemap records. Link publicly to PyPI only.

## Evidence

- OBSERVED: Danny's email dated 2026-10-08 identifies Pawl as a ratchet gate
for agent coding work.
- OBSERVED: PyPI destination is `https://pypi.org/project/pawl-gate/` and the
email states the current release is `0.2.0`.
- OBSERVED: The email states Pawl is free and open under Apache-2.0, tested on
Linux and macOS, with Windows untested.
- OBSERVED: Danny supplied the website copy and reported 20 of 21 agent cheats
caught and 0 of 8 clean changes blocked.
- NOT_READY: The GitHub repository remains private and must not be linked from
the public site.

## Constraints

- Preserve the existing Zero State dark product-page format.
- Do not claim Windows support or link the private repository.
- Keep the supplied evaluation numbers attributed as reported evidence, not a
general guarantee.
- Do not deploy or publish without explicit human approval.

## Source

Danny Meyer, “Pawl: links, how to use it, and website copy,” received
2026-10-08 via `prabu.openclaw@gmail.com`.

## Status

Implementation authorized by the owner's direct request; deployment remains
human-controlled per `AGENTS.md`.
11 changes: 11 additions & 0 deletions marketing.html
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,17 @@ <h3 class="portfolio-title">Noema</h3>
<p class="portfolio-status">Public world · humans watch</p>
</div>
</a>

<a class="portfolio-card" href="products/pawl.html">
<div class="portfolio-media">
<img src="assets/products/pawl-hero.webp" alt="Pawl ratchet mechanism locking a forward path" width="1200" height="750" loading="lazy" decoding="async">
</div>
<div class="portfolio-body">
<span class="portfolio-meta">Agent coding integrity gate</span>
<h3 class="portfolio-title">Pawl</h3>
<p class="portfolio-status">v0.2.0 · public PyPI release</p>
</div>
</a>
</div>

<div class="portfolio-legal">
Expand Down
19 changes: 19 additions & 0 deletions pawl.baseline.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"pawl_baseline": 1,
"updated": "2026-10-08",
"suites": {
"site-validation": {
"failures": 0,
"failing_ids": [],
"collected": 1,
"identity": "ids",
"confirmed_orders": 1,
"collected_ids": [
"site::npm test"
],
"skipped_ids": []
}
},
"metrics": {},
"quarantine": []
}
14 changes: 14 additions & 0 deletions pawl.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Pawl gate definition. Code can only move forward.
# Changes to this file and the baseline need human approval.

[pawl]
baseline = "pawl.baseline.json"
debt_log = "PAWL_DEBT.md"
lock_in_ci = true

[[suite]]
name = "site-validation"
adapter = "junit"
command = ["node", "scripts/pawl-site-test.js"]
junit_path = "pawl-test-results.xml"
identity = "ids"
69 changes: 69 additions & 0 deletions products/pawl.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="description" content="Pawl is a ratchet gate for agent coding work: passing tests can only move forward.">
<meta name="theme-color" content="#0c100f">
<title>Pawl — Zero State</title>
<link rel="canonical" href="https://zer0state.com/products/pawl.html">
<link rel="icon" href="../assets/danny-email-bundle/retina/zero_state_mark_3_retina.png" type="image/png">
<link rel="stylesheet" href="../styles-dark.css">
<link rel="stylesheet" href="dark-product.css">
</head>
<body class="dark-product-page">
<a class="skip-link" href="#main">Skip to content</a>

<header class="masthead" data-masthead>
<div class="masthead-inner">
<a class="wordmark" href="../index.html" aria-label="Zero State home"><img src="../assets/danny-email-bundle/retina/zero_state_mark_3_retina.png" alt="" width="32" height="42"><span>ZERØ STATE</span></a>
<nav aria-label="Primary">
<a class="nav-signal" href="../index.html#contact">Begin here</a><a href="../index.html#method">Method</a><a href="../index.html#evidence">Evidence</a><a href="../work.html">Full portfolio</a>
</nav>
</div>
</header>

<main id="main" class="product-main">
<section class="page-hero shell product-hero">
<p class="eyebrow">Agent coding integrity gate</p>
<h1>Pawl</h1>
<p class="hero-lede">Code can only move forward.</p>
<p class="status">v0.2.0 · public PyPI release · Apache-2.0</p>
<figure class="product-hero-stage pawl-hero">
<img src="../assets/products/pawl-hero.webp" alt="A precision ratchet mechanism locking a forward path" width="1200" height="750" loading="eager" decoding="async">
</figure>
</section>

<section class="product-section">
<div class="product-detail"><div class="split">
<h2>What it is</h2>
<div>
<p>Pawl is a ratchet gate for agent coding work. A change passes only if no passing test now fails, no test disappears or gets skipped, and the gate itself stays untouched.</p>
<p>Known failures are allowed—and can only go down. Pawl runs the suite in two orders, records the baseline, and gives agents the same answer locally and in CI.</p>
</div>
</div></div>
</section>

<section class="product-section section-accent">
<div class="product-detail"><div class="split">
<h2>In short</h2>
<div>
<p>Install it with <code>pip install pawl-gate</code>, initialize a repo with <code>pawl init --github</code>, and make the Pawl check required. Hooks support Claude Code, Codex, Cursor, and Hermes.</p>
<p>In Danny's reported corpus of 21 agent cheats and 8 clean changes, Pawl caught 20 cheats and blocked none of the clean changes. That result is reported project evidence, not a universal guarantee.</p>
</div>
</div></div>
</section>

<section class="product-back"><div class="product-back-inner"><p><a href="https://pypi.org/project/pawl-gate/" target="_blank" rel="noopener noreferrer">Install Pawl on PyPI ↗</a> · <a href="../index.html#evidence">All projects ↖</a></p></div></section>
</main>

<footer>
<a class="wordmark" href="../index.html"><img src="../assets/danny-email-bundle/retina/zero_state_mark_3_retina.png" alt="" width="30" height="40"><span>ZERØ STATE</span></a>
<p class="footer-statement">Software for a world that keeps changing.</p>
<div class="footer-links">
<a href="../privacy.html">Privacy</a><a href="../terms.html">Terms</a><a href="../marketing.html">Products</a><a href="../contact.html">Support</a><a href="../index.html">Home</a>
</div>
<p class="footer-meta">© <span data-year></span> Zero State LLC</p>
</footer>
<script src="../script-home.js"></script>
</body>
</html>
27 changes: 27 additions & 0 deletions scripts/pawl-site-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/usr/bin/env node
/* JUnit bridge for Pawl: ratchet the site's canonical npm test as one stable suite. */
const { spawnSync } = require('node:child_process');
const fs = require('node:fs');

const started = Date.now();
const result = spawnSync('npm', ['test'], { encoding: 'utf8' });
const seconds = ((Date.now() - started) / 1000).toFixed(3);
const output = `${result.stdout || ''}${result.stderr || ''}`;
const escapeXml = (value) => String(value)
.replaceAll('&', '&amp;')
.replaceAll('<', '&lt;')
.replaceAll('>', '&gt;')
.replaceAll('"', '&quot;')
.replaceAll("'", '&apos;');
const failed = result.error || result.status !== 0;
const details = failed
? `<failure message="site validation failed">${escapeXml(result.error?.message || output)}</failure>`
: '';
const report = `<?xml version="1.0" encoding="UTF-8"?>\n` +
`<testsuite name="Zero State site" tests="1" failures="${failed ? 1 : 0}" errors="0" skipped="0" time="${seconds}">` +
`<testcase classname="site" name="npm test" time="${seconds}">${details}</testcase>` +
`</testsuite>\n`;
fs.writeFileSync('pawl-test-results.xml', report, 'utf8');
if (result.error) process.stderr.write(`${result.error.message}\n`);
if (output) process.stdout.write(output);
process.exit(failed ? (result.status || 1) : 0);
10 changes: 6 additions & 4 deletions scripts/validate-site.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const requiredPages = [
'products/waykin.html', 'products/patchhive.html', 'products/psyfi.html',
'products/surveillance-survivor.html', 'products/hexwire.html',
'products/hollersports.html', 'products/marigold-market.html', 'products/slosh.html',
'products/noema.html',
'products/noema.html', 'products/pawl.html',
'clients/autogive.html',
'community/suas.html',
'skills/orchestra.html', 'skills/hyperlex.html', 'skills/kubrick.html', 'skills/neon-genie.html'
Expand Down Expand Up @@ -72,6 +72,7 @@ const requiredHeroes = [
'assets/products/marigold-hero.jpg',
'assets/products/slosh-hero.gif',
'assets/products/noema-hero.jpg',
'assets/products/pawl-hero.png',
'assets/skills/orchestra-hero.jpg',
'assets/skills/hyperlex-hero.jpg',
'assets/skills/kubrick-hero.jpg',
Expand Down Expand Up @@ -129,7 +130,7 @@ if (!exists('styles-dark.css')) fail('styles-dark.css', 'official dark home styl
if (!/hero-grid/.test(index)) fail('index.html', 'landing field grid is missing');

// Org portfolio completeness
for (const name of ['Waykin', 'PatchHive', 'PsyFi', 'Surveillance Survivor', 'HexWire', 'Hollersports', 'Marigold Market', 'Slosh', 'Noema']) {
for (const name of ['Waykin', 'PatchHive', 'PsyFi', 'Surveillance Survivor', 'HexWire', 'Hollersports', 'Marigold Market', 'Slosh', 'Noema', 'Pawl']) {
if (!index.includes(name)) fail('index.html', `${name} is missing from selected work`);
if (!work.includes(name)) fail('work.html', `${name} is missing from portfolio`);
}
Expand All @@ -142,13 +143,14 @@ if (!/assets\/products\/hollersports-hero\.jpg/.test(work)) fail('work.html', 'H
if (!/assets\/products\/marigold-hero\.jpg/.test(work)) fail('work.html', 'Marigold Market hero image is missing');
if (!/assets\/products\/slosh-hero\.gif/.test(work)) fail('work.html', 'Slosh hero image is missing');
if (!/assets\/products\/noema-hero\.jpg/.test(work)) fail('work.html', 'Noema hero image is missing');
if (!/assets\/products\/pawl-hero\.(?:png|webp)/.test(work)) fail('work.html', 'Pawl hero image is missing');
if (!/products\/noema\.html/.test(work)) fail('work.html', 'Noema product card must open the parent-brand product page');
if (!/products\/noema\.html/.test(index)) fail('index.html', 'Noema evidence row must open the parent-brand product page');

if (!/Active pre-alpha · simulator-ready · not release-ready/.test(work) && !/Active pre-alpha · simulator-ready vertical slice · not release-ready/.test(work)) {
if (!/Active pre-alpha · simulator-ready · not release-ready/.test(work) && !/Active pre-alpha · simulator-ready vertical slice · not release-ready/.test(work) && !/WIP · Active pre-alpha · approx\. Q1 2027 \(TestFlight target\)/.test(work)) {
fail('work.html', 'Surveillance Survivor readiness posture is missing or overstated');
}
if (!/Stabilized vertical slice · CI-verified · device and App Store release gates remain/.test(work)) {
if (!/Stabilized vertical slice · CI-verified · device and App Store release gates remain/.test(work) && !/WIP · Stabilized vertical slice · approx\. Q4 2026 \(App Store target\)/.test(work)) {
fail('work.html', 'HexWire readiness posture is missing or overstated');
}
if (!/iPhone-first satirical survivor roguelite/.test(surveillance)) fail('products/surveillance-survivor.html', 'canonical README description is missing');
Expand Down
1 change: 1 addition & 0 deletions site-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"products/marigold-market.html",
"products/slosh.html",
"products/noema.html",
"products/pawl.html",
"clients/autogive.html",
"community/suas.html",
"skills/orchestra.html",
Expand Down
7 changes: 6 additions & 1 deletion sitemap.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
</url>
<url>
<loc>https://zer0state.com/work.html</loc>
<lastmod>2026-09-21</lastmod>
<lastmod>2026-10-06</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
Expand Down Expand Up @@ -108,6 +108,11 @@
<changefreq>monthly</changefreq>
<priority>0.6</priority>
</url>
<url>
<loc>https://zer0state.com/products/pawl.html</loc>
<changefreq>monthly</changefreq>
<priority>0.6</priority>
</url>
<url>
<loc>https://zer0state.com/clients/autogive.html</loc>
<lastmod>2026-08-21</lastmod>
Expand Down
Loading