diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7e84dd1..5aef9ae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,7 +22,7 @@ jobs: with: go-version: '1.26.x' check-latest: true - - name: Require canonical SMALL progress with development changes + - name: Require canonical SMALL progress and code/docs co-changes if: github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/main') shell: bash env: @@ -30,8 +30,14 @@ jobs: run: | git fetch --no-tags --depth=1 origin "$BASE_SHA" git diff --name-only "$BASE_SHA" HEAD | ./scripts/check-small-sync.sh + git diff --name-only "$BASE_SHA" HEAD | bash ./scripts/check-docs-sync.sh + - name: Verify documentation contract and source-bound bundle + run: | + go test ./cmd/loopexec -run '^TestDocumentationContract' -count=1 + go test ./tools/docs-continuity -count=1 + go run ./tools/docs-continuity bundle "$RUNNER_TEMP/loopexec-documentation" - name: Format - run: test -z "$(gofmt -l cmd internal examples)" + run: test -z "$(gofmt -l cmd internal examples tools)" - name: Vet run: go vet ./... - name: Test @@ -55,8 +61,8 @@ jobs: run: | mkdir -p "$RUNNER_TEMP/small" cd "$RUNNER_TEMP/small" - archive="small-v1.1.0-$(go env GOOS)-$(go env GOARCH).tar.gz" - base="https://github.com/justyn-clark/small-protocol/releases/download/v1.1.0" + archive="small-v1.1.2-$(go env GOOS)-$(go env GOARCH).tar.gz" + base="https://github.com/justyn-clark/small-protocol/releases/download/v1.1.2" curl --fail --location --retry 2 "$base/checksums.txt" -o checksums.txt curl --fail --location --retry 2 "$base/$archive" -o "$archive" awk -v archive="$archive" '$2 == archive {print}' checksums.txt > selected.txt diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f258775..6d77591 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,6 +3,7 @@ name: Release on: push: tags: ['v*'] + workflow_dispatch: permissions: contents: read @@ -10,18 +11,43 @@ permissions: jobs: verify: uses: ./.github/workflows/ci.yml - publish: + documentation: needs: verify runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-go@v5 + with: + go-version: '1.26.x' + - name: Require source on main + run: git merge-base --is-ancestor HEAD origin/main + - name: Read back source-bound deployed docs + run: | + go run ./tools/docs-continuity bundle "$RUNNER_TEMP/documentation" + go run ./tools/docs-continuity verify-site "$RUNNER_TEMP/documentation/manifest.json" + publish: + if: startsWith(github.ref, 'refs/tags/') + needs: documentation + runs-on: ubuntu-latest permissions: contents: write steps: - uses: actions/checkout@v4 + with: + fetch-depth: 0 - uses: actions/setup-go@v5 with: go-version: '1.26.x' + - name: Require documentation continuity since previous release + run: | + base="$(git describe --tags --abbrev=0 --match 'v[0-9]*' HEAD^)" + git diff --name-only "$base" HEAD | bash ./scripts/check-docs-sync.sh - name: Package six targets run: ./scripts/build-release.sh "${{ github.ref_name }}" dist + - name: Require matching deployed documentation + run: go run ./tools/docs-continuity verify-site dist/documentation/manifest.json - name: Publish release env: GH_TOKEN: ${{ github.token }} @@ -29,4 +55,5 @@ jobs: run: | gh release create "$RELEASE_TAG" dist/loopexec-*.tar.gz dist/loopexec-*.zip dist/checksums.txt \ --verify-tag --draft --title "LoopExec $RELEASE_TAG" --notes-file "docs/releases/$RELEASE_TAG.md" + go run ./tools/docs-continuity verify-site dist/documentation/manifest.json gh release edit "$RELEASE_TAG" --draft=false --latest diff --git a/.gitignore b/.gitignore index 9162e32..c1f1cac 100644 --- a/.gitignore +++ b/.gitignore @@ -26,4 +26,6 @@ UPDATES/ .small/logs .small/runs/ logs/ +# Only individually reviewed command proof belongs here, never the private cache. +!.small-command-proofs/logs/ docs/jcn-agent-stack/runs/ diff --git a/.small-command-proofs/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T230645.843552000Z.txt b/.small-command-proofs/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T230645.843552000Z.txt new file mode 100644 index 0000000..0f85a9a --- /dev/null +++ b/.small-command-proofs/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T230645.843552000Z.txt @@ -0,0 +1 @@ +node /Users/justin/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/node_modules/playwright/cli.js screenshot --viewport-size 1440,1000 --wait-for-timeout 800 --timeout 15000 http://127.0.0.1:4345/ build/deployment/docs-desktop.png \ No newline at end of file diff --git a/.small-command-proofs/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T230805.581332000Z.txt b/.small-command-proofs/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T230805.581332000Z.txt new file mode 100644 index 0000000..1570f81 --- /dev/null +++ b/.small-command-proofs/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T230805.581332000Z.txt @@ -0,0 +1 @@ +node /Users/justin/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/node_modules/playwright/cli.js screenshot --viewport-size 390,844 --wait-for-timeout 800 --timeout 15000 http://127.0.0.1:4345/ build/deployment/docs-mobile.png \ No newline at end of file diff --git a/.small-command-proofs/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261008T194655.178548000Z.txt b/.small-command-proofs/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261008T194655.178548000Z.txt new file mode 100644 index 0000000..b3006b5 --- /dev/null +++ b/.small-command-proofs/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261008T194655.178548000Z.txt @@ -0,0 +1,67 @@ +apply_patch <<'PATCH' +*** Begin Patch +*** Update File: .gitignore +@@ + logs/ ++# Only individually reviewed command proof belongs here, never the private cache. ++!.small-command-proofs/logs/ + docs/jcn-agent-stack/runs/ +*** Update File: AGENTS.md +@@ + Target tooling: SMALL CLI 1.1.0 (supports protocol profiles 1.0.0 and 2.0.0). ++This development checkout's historical command summaries require the upstream ++command-summary correction. Use an explicitly verified patched binary until a ++compatible stable release is adopted; do not use the Homebrew 1.0.2 default. ++Preserve existing receipts. Review and retain only required non-secret command ++proof in `.small-command-proofs/`; never export `.small-cache/` wholesale. New ++truncated captures also require exact-byte proof before cache-free validation. ++Local validation does not update the CI pin or prove hosted release gates. + This checkout remains on the v1 profile unless explicitly migrated through SMALL. +*** Update File: docs/small-integration.md +@@ + benchmark. Use separate worktrees/candidate directories and runtime storage for + independent live work. A single workflow runtime has one governor. ++ ++## Development-state command proof ++ ++The released SMALL v1.1.0 can truncate an allowed localhost URL inside a command ++summary, then reject that display during strict validation. This checkout has ++two such historical screenshot captures. Keep their original receipts unchanged ++and use an explicitly verified CLI containing the upstream command-summary fix. ++An old Homebrew `small` is not a substitute for that binary. ++ ++The correction validates exact original command bytes against the saved ref, ++SHA256 and legacy summary. Individually reviewed, non-secret proof can be kept ++under `.small-command-proofs/`, mirroring the supported `.small-cache/` ref. ++The proof store is portable validation input, not replacement audit history. ++Never export the whole private cache. Missing or changed proof must fail closed. ++ ++New truncated captures marked `command_summary_version: 2` also need portable ++exact-byte proof before committing state for a cache-free checkout. Review each ++full command for private material before copying it; do not strip the marker. ++Short captures retain the existing metadata contract. See the upstream ++[command-proof guide](https://github.com/justyn-clark/small-protocol/blob/88261152c8643d121b4ff642881b1f701fc016d5/docs/command-proof.md). ++ ++Validate with the explicitly selected binary: ++ ++```sh ++/path/to/patched-small version ++/path/to/patched-small check --strict ++``` ++ ++Check a disposable snapshot with no cache: selected proof must pass, while ++missing or tampered proof must fail without changing the saved state. CI still ++pins the released v1.1.0 until the correction is explicitly adopted there; ++local success alone does not establish hosted compatibility or release readiness. + + ## Exit codes and acceptance +*** Update File: CHANGELOG.md +@@ + Future recipes and workflow adapters will remain configurable, opt-in integrations. ++ ++- Prepared selective portable SMALL command proof for preserved development ++ receipts and documented explicit patched-CLI validation. Cache-free validation ++ passes with reviewed proof and fails for missing or tampered bytes. Hosted ++ adoption remains separate; the current CI pin has not yet changed. +*** End Patch +PATCH \ No newline at end of file diff --git a/.small-command-proofs/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T003234.483715000Z.txt b/.small-command-proofs/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T003234.483715000Z.txt new file mode 100644 index 0000000..bf56b4d --- /dev/null +++ b/.small-command-proofs/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T003234.483715000Z.txt @@ -0,0 +1,65 @@ +apply_patch <<'PATCH' +*** Begin Patch +*** Update File: /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec/.github/workflows/ci.yml +@@ +- archive="small-v1.1.0-$(go env GOOS)-$(go env GOARCH).tar.gz" +- base="https://github.com/justyn-clark/small-protocol/releases/download/v1.1.0" ++ archive="small-v1.1.1-$(go env GOOS)-$(go env GOARCH).tar.gz" ++ base="https://github.com/justyn-clark/small-protocol/releases/download/v1.1.1" +*** Update File: /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec/AGENTS.md +@@ +-Target tooling: SMALL CLI 1.1.0 (supports protocol profiles 1.0.0 and 2.0.0). +-This development checkout's historical command summaries require the upstream +-command-summary correction. Use an explicitly verified patched binary until a +-compatible stable release is adopted; do not use the Homebrew 1.0.2 default. ++Target tooling: SMALL CLI 1.1.1 (supports protocol profiles 1.0.0 and 2.0.0). ++This stable release includes the command-summary correction required by this ++checkout's historical receipts. Verify the resolved binary; do not use 1.0.2 ++or the uncorrected 1.1.0 release to write or validate this development state. +*** Update File: /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec/docs/small-integration.md +@@ +-CI pins and checksum-verifies v1.1.0. It creates disposable workspaces; your ++CI pins and checksum-verifies v1.1.1. It creates disposable workspaces; your +@@ +-and use an explicitly verified CLI containing the upstream command-summary fix. +-An old Homebrew `small` is not a substitute for that binary. ++and use the released SMALL v1.1.1, which includes the command-summary fix. ++Verify the resolved binary rather than assuming a Homebrew installation updated. +@@ +-[command-proof guide](https://github.com/justyn-clark/small-protocol/blob/88261152c8643d121b4ff642881b1f701fc016d5/docs/command-proof.md). ++[command-proof guide](https://github.com/justyn-clark/small-protocol/blob/v1.1.1/docs/command-proof.md). +@@ +-/path/to/patched-small version +-/path/to/patched-small check --strict ++small version # must resolve to v1.1.1 for this checkout ++small check --strict +@@ +-missing or tampered proof must fail without changing the saved state. CI still +-pins the released v1.1.0 until the correction is explicitly adopted there; +-local success alone does not establish hosted compatibility or release readiness. ++missing or tampered proof must fail without changing the saved state. CI adopts ++the same v1.1.1 release; local success alone does not establish hosted compatibility ++or release readiness. Verify checks for the committed candidate before publication. +*** Update File: /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec/CHANGELOG.md +@@ +-- Prepared selective portable SMALL command proof for preserved development +- receipts and documented explicit patched-CLI validation. Cache-free validation +- passes with reviewed proof and fails for missing or tampered bytes. Hosted +- adoption remains separate; the current CI pin has not yet changed. +- +@@ + on [GitHub Releases](https://github.com/justyn-clark/loopexec/releases). + ++- Adopted stable SMALL v1.1.1 in checksum-verified CI. Preserved historical ++ command receipts with individually reviewed portable proof; missing or tampered ++ proof fails closed. Local validation is not proof of hosted release readiness. +*** Update File: /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec/docs/releases/v0.4.1.md +@@ + ## Validation and limits + ++CI pins checksum-verified SMALL v1.1.1, including its command-summary correction. ++Historical command receipts remain unchanged; reviewed portable command proof ++supports cache-free strict validation without exporting the private cache. ++ +*** End Patch +PATCH \ No newline at end of file diff --git a/.small-command-proofs/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T003414.303312000Z.txt b/.small-command-proofs/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T003414.303312000Z.txt new file mode 100644 index 0000000..d7be7c2 --- /dev/null +++ b/.small-command-proofs/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T003414.303312000Z.txt @@ -0,0 +1,47 @@ +apply_patch <<'PATCH' +*** Begin Patch +*** Update File: /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec/.github/workflows/release.yml +@@ + on: + push: + tags: ['v*'] ++ workflow_dispatch: +@@ + verify: + uses: ./.github/workflows/ci.yml ++ documentation: ++ needs: verify ++ runs-on: ubuntu-latest ++ steps: ++ - uses: actions/checkout@v4 ++ with: ++ fetch-depth: 0 ++ - uses: actions/setup-go@v5 ++ with: ++ go-version: '1.26.x' ++ - name: Require source on main ++ run: git merge-base --is-ancestor HEAD origin/main ++ - name: Read back source-bound deployed docs ++ run: | ++ go run ./tools/docs-continuity bundle "$RUNNER_TEMP/documentation" ++ go run ./tools/docs-continuity verify-site "$RUNNER_TEMP/documentation/manifest.json" + publish: +- needs: verify ++ if: startsWith(github.ref, 'refs/tags/') ++ needs: documentation +*** Update File: /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec/docs/documentation-continuity.md +@@ + creation and again immediately before making the release public. A failure leaves + publication stopped; a created draft remains a draft for investigation. ++ ++The Release workflow can also be dispatched on main for a non-publishing CI and ++deployed-docs rehearsal. Both dispatch and tag runs require source reachable from ++main. Only a tag run may enter the publication job, after CI and docs readback pass. +*** Update File: /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec/CHANGELOG.md +@@ + every platform archive, and fail-closed deployed-docs checks before publication. + The separate docs site must adopt the build handshake before the next release. ++- Added a non-publishing Release workflow rehearsal and a main-line ancestry gate ++ before deployed-docs verification or tag publication. +*** End Patch +PATCH \ No newline at end of file diff --git a/.small-command-proofs/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T003658.229732000Z.txt b/.small-command-proofs/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T003658.229732000Z.txt new file mode 100644 index 0000000..99d7a32 --- /dev/null +++ b/.small-command-proofs/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T003658.229732000Z.txt @@ -0,0 +1,10 @@ +apply_patch <<'PATCH' +*** Begin Patch +*** Add File: /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec/build/deployment/run-docs-build.mjs ++import { fileURLToPath } from 'node:url'; ++process.chdir(fileURLToPath(new URL('./docs-checkout/apps/docs/', import.meta.url))); ++const cli = process.cwd() + '/node_modules/astro/astro.js'; ++process.argv = [process.argv[0], cli, 'build']; ++await import(cli); +*** End Patch +PATCH \ No newline at end of file diff --git a/.small-command-proofs/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T003833.567017000Z.txt b/.small-command-proofs/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T003833.567017000Z.txt new file mode 100644 index 0000000..3a7b641 --- /dev/null +++ b/.small-command-proofs/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T003833.567017000Z.txt @@ -0,0 +1,61 @@ +apply_patch <<'PATCH' +*** Begin Patch +*** Add File: /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec/build/deployment/site-qa.mjs ++import { chromium } from '/Users/justin/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/node_modules/playwright/index.mjs'; ++import { createServer } from 'node:http'; ++import { readFileSync, existsSync, statSync } from 'node:fs'; ++import { join, extname, resolve } from 'node:path'; ++import assert from 'node:assert/strict'; ++const root = resolve('build/deployment/docs-checkout/apps/docs/dist'); ++const mime = { '.html': 'text/html', '.js': 'text/javascript', '.css': 'text/css', '.json': 'application/json', '.svg': 'image/svg+xml', '.png': 'image/png', '.wasm': 'application/wasm' }; ++const server = createServer((req, res) => { ++ let file = join(root, decodeURIComponent(new URL(req.url, 'http://localhost').pathname)); ++ if (!file.startsWith(root + '/')) { res.writeHead(403).end(); return; } ++ if (existsSync(file) && statSync(file).isDirectory()) file = join(file, 'index.html'); ++ if (!existsSync(file)) { res.writeHead(404).end(); return; } ++ res.setHeader('Content-Type', mime[extname(file)] ?? 'application/octet-stream'); ++ res.end(readFileSync(file)); ++}); ++await new Promise(r => server.listen(0, '127.0.0.1', r)); ++const origin = `http://127.0.0.1:${server.address().port}`; ++const browser = await chromium.launch({ headless: true }); ++const results = []; ++try { ++ for (const [label, viewport] of [['desktop', {width:1440,height:1000}], ['mobile', {width:390,height:844}]]) { ++ const page = await browser.newPage({ viewport }); ++ const errors = []; ++ page.on('pageerror', error => errors.push(error.message)); ++ for (const route of ['/', '/getting-started/', '/cli-reference/', '/acceptance-policy/', '/small-integration/', '/release-notes/']) { ++ assert.equal((await page.goto(origin + route)).status(), 200); ++ await page.waitForLoadState('networkidle'); ++ const proof = await page.evaluate(() => ({ ++ title: document.title, ++ width: document.documentElement.scrollWidth, ++ viewport: innerWidth, ++ text: document.body.innerText, ++ links: [...document.querySelectorAll('a[href]')].map(a=>a.href), ++ images: [...document.images].filter(i=>i.clientWidth).map(i=>({src:i.src,ok:i.complete && i.naturalWidth>0})), ++ })); ++ assert.ok(proof.width <= proof.viewport, `${label} overflow on ${route}: ${proof.width}`); ++ assert.match(proof.text, /Documentation source v0\.4\.1/); ++ assert.ok(proof.images.every(i=>i.ok), `broken image on ${route}`); ++ for (const href of new Set(proof.links)) { ++ const url = new URL(href); ++ if (url.origin !== origin) continue; ++ let path = join(root, decodeURIComponent(url.pathname)); ++ if (existsSync(path) && statSync(path).isDirectory()) path = join(path, 'index.html'); ++ assert.ok(existsSync(path), `broken local link: ${route} -> ${url.pathname}`); ++ } ++ if (route === '/') await page.screenshot({ path: `build/deployment/docs-${label}-v0.4.1.png`, fullPage: false }); ++ results.push({label,route,title:proof.title,width:proof.width,images:proof.images.length}); ++ } ++ assert.deepEqual(errors, []); ++ await page.close(); ++ } ++ console.log(JSON.stringify({status:'SITE_QA_OK',results},null,2)); ++} finally { ++ await browser.close(); ++ await new Promise(r=>server.close(r)); ++} +*** End Patch +PATCH \ No newline at end of file diff --git a/.small-command-proofs/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T004202.466623000Z.txt b/.small-command-proofs/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T004202.466623000Z.txt new file mode 100644 index 0000000..82faf8b --- /dev/null +++ b/.small-command-proofs/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T004202.466623000Z.txt @@ -0,0 +1,64 @@ +apply_patch <<'PATCH' +*** Begin Patch +*** Add File: /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec/docs/small-template-command-validation-handoff.md ++# SMALL Command-Literal Validation Reproduction ++ ++Internal correction context, not public LoopExec documentation and not a gated ++SMALL handoff. Prepared October 8, 2026. No release/deployment occurred. ++ ++## Observed ++ ++- Installed and CI-candidate tooling: stable SMALL v1.1.1, upstream commit ++ `6e1270b77f0cf93ab11f7cc45874b16c83e0f39e` (official tag verified). ++- LoopExec strict passed before the new QA-helper patch capture. It now fails ++ with two insecure-link violations in progress; schemas/non-strict doctor pass. ++- I introduced that captured inline script. It contains a template literal ++ assembling an HTTP origin from exact loopback host plus the ephemeral server ++ port. Strict scans source text as a URL; the template port is not a numeric URL. ++ This is observed behavior, not proof that the intended policy is defective. ++- The server actually binds loopback. Playwright completed all twelve desktop/ ++ mobile route checks; no overflow, broken internal links/images or page errors. ++ ++## Immutable Evidence ++ ++- Replay: `f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96`. ++- Capture: `.small-cache/logs//commands/20261009T003833.567017000Z.txt`. ++- Reviewed exact portable copy: ++ `.small-command-proofs/logs//commands/20261009T003833.567017000Z.txt`. ++- SHA256: `5f06e6615a251c4124e5fd85bd580f405c8ba27c7b20e5d576b3aa4f6d9d0996`. ++- Full reproduction command is already preserved there, without an added newline. ++- Do not edit the command bytes, ref, SHA, summary marker, timestamp or old progress. ++ Do not purge history, migrate/reset state to evade lint, or weaken strict policy. ++ ++## Required Decision And Correction ++ ++Determine whether source-code literals in verified command bodies should receive ++context-aware URL handling, or whether SMALL needs an explicit loss-preserving ++remediation for newly captured commands outside its URL grammar. Preserve fail- ++closed treatment of real external HTTP, lookalike hosts, userinfo, malformed ports, ++missing/tampered proof and credentials beyond the summary cap. A blanket HTTP ++allowance or stripping `command_summary_version` is not acceptable. ++ ++Add regression fixtures for the exact capture and negative cases. The installed ++1.1.1 CLI has no supported command to amend/retract this receipt. A documented ++upstream resolution is needed before adopting a compatible binary in LoopExec CI. ++ ++## Resume Gates ++ ++Run strict against the existing root state and a fresh cache-free snapshot with ++reviewed proof. Hash original history before/after validation; never rewrite it. ++Then resume task-36, paired GitHub PR/CI, protected main, matching Vercel docs, ++official-origin readback and v0.4.1 publication. Browser operations also require ++the Mac to be unlocked. General recipes remain task-38, after deployment/release. ++ ++Local completed checks: `go test ./...`, CLI/docs contract tests, four site bundle ++tests, Astro build, twelve Playwright route checks, six platform archives and ++documentation archive with checksums. Source bundle identity: source ++`34f5732e494ebf8391f338fc8599bb194cf6d3b86d5c8ba535c697a819a2e9a6`, docs ++`85f789531ea6e9665a5d07b139683f02731e872ebe087189ba46bd31c989dbee`. ++ ++Canonical site changes are in `build/deployment/docs-checkout`; original companion ++checkout and unrelated work were preserved. Local direct Node verification used ++the existing dependency cache; clean pnpm installation still needs hosted CI. +*** End Patch +PATCH \ No newline at end of file diff --git a/.small-command-proofs/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T132707.643132000Z.txt b/.small-command-proofs/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T132707.643132000Z.txt new file mode 100644 index 0000000..2ab7951 --- /dev/null +++ b/.small-command-proofs/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T132707.643132000Z.txt @@ -0,0 +1,67 @@ +apply_patch <<'PATCH' +*** Begin Patch +*** Update File: /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec/.github/workflows/ci.yml +@@ +- archive="small-v1.1.1-$(go env GOOS)-$(go env GOARCH).tar.gz" +- base="https://github.com/justyn-clark/small-protocol/releases/download/v1.1.1" ++ archive="small-v1.1.2-$(go env GOOS)-$(go env GOARCH).tar.gz" ++ base="https://github.com/justyn-clark/small-protocol/releases/download/v1.1.2" +*** Update File: /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec/AGENTS.md +@@ +-Target tooling: SMALL CLI 1.1.1 (supports protocol profiles 1.0.0 and 2.0.0). +-This stable release includes the command-summary correction required by this +-checkout's historical receipts. Verify the resolved binary; do not use 1.0.2 +-or the uncorrected 1.1.0 release to write or validate this development state. ++Target tooling: SMALL CLI 1.1.2 (supports protocol profiles 1.0.0 and 2.0.0). ++This stable release includes command-summary correction and narrow captured-code ++loopback URL handling required by this checkout's historical receipts. Verify the ++resolved binary; older releases do not validate the retained QA template capture. +*** Update File: /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec/docs/small-integration.md +@@ +-CI pins and checksum-verifies v1.1.1. It creates disposable workspaces; your ++CI pins and checksum-verifies v1.1.2. It creates disposable workspaces; your +@@ +-and use the released SMALL v1.1.1, which includes the command-summary fix. ++and use the released SMALL v1.1.2, which includes the command-summary fix and ++the captured-source correction needed for the retained QA script. +@@ +-[command-proof guide](https://github.com/justyn-clark/small-protocol/blob/v1.1.1/docs/command-proof.md). ++[command-proof guide](https://github.com/justyn-clark/small-protocol/blob/v1.1.2/docs/command-proof.md). ++ ++v1.1.2 recognizes a narrowly defined backtick-quoted local URL with a supported ++runtime-port expression only after exact command SHA256 and summary verification. ++It does not execute source to validate links. Authored fields, external HTTP, ++unsupported expressions, credentials and missing/tampered proof remain strict. ++The existing QA capture can therefore pass without rewriting its original bytes. +@@ +-small version # must resolve to v1.1.1 for this checkout ++small version # must resolve to v1.1.2 for this checkout +@@ +-the same v1.1.1 release; local success alone does not establish hosted compatibility ++the same v1.1.2 release; local success alone does not establish hosted compatibility +*** Update File: /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec/CHANGELOG.md +@@ +-- Adopted stable SMALL v1.1.1 in checksum-verified CI. Preserved historical ++- Adopted stable SMALL v1.1.2 in checksum-verified CI. Its narrowly scoped ++ captured-source validation resolves the retained local QA template capture. ++ Preserved historical +*** Update File: /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec/docs/releases/v0.4.1.md +@@ +-CI pins checksum-verified SMALL v1.1.1, including its command-summary correction. ++CI pins checksum-verified SMALL v1.1.2, including command-summary correction and ++narrow, exact-proof-backed local URL source-literal handling. +*** Update File: /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec/docs/small-template-command-validation-handoff.md +@@ + # SMALL Command-Literal Validation Reproduction ++ ++## Resolution Verified October 9, 2026 ++ ++Stable SMALL v1.1.2 now supports narrowly defined captured-source local URL ++templates after exact command proof verification. The installed binary is ++v1.1.2, revision `1a101564cac8d22e200b965f912df7c9c37ab0e4`, unmodified. ++Root strict now passes; all 14 audit/proof files remained byte-identical across ++read-only validation. The reproduction below is preserved as historical context, ++not a current audit blocker. CI adoption is paired with canonical prose/changelog. ++Hosted CI, docs readback and release publication remain separate gates. +*** End Patch +PATCH \ No newline at end of file diff --git a/.small/handoff.small.yml b/.small/handoff.small.yml index d3fb9fc..b696c37 100644 --- a/.small/handoff.small.yml +++ b/.small/handoff.small.yml @@ -1,8 +1,10 @@ small_version: "1.0.0" owner: agent -summary: Run complete. strict check passed. All plan tasks completed. +summary: 'SMALL audit blocker resolved: installed stable1.1.2, root/site CI configs and prose updated, exact retained QA proof unchanged. Task41 completed; task36 is the deployment continuation. Cache-free root/site strict pass and missing proof remains fail-closed; CLI/docs contract tests, canonical tests/build and refreshed desktop/mobile QA pass. Source-bound bundle sourcee0d04802 docscb4c8adb in build/deployment/small112-documentation and docs-checkout. Rebuild older platform packages before publication. Next commit paired PRs, verify hosted CI/protections, authorized deployment, official-origin docs readback and release; recipes remain after release. No remote publication or production action.' resume: - next_steps: [] + current_task_id: task-36 + next_steps: + - Unblock task-36 links: [] replayId: value: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 diff --git a/.small/plan.small.yml b/.small/plan.small.yml index 22b8476..c90acdb 100644 --- a/.small/plan.small.yml +++ b/.small/plan.small.yml @@ -100,3 +100,30 @@ tasks: - id: task-33 title: Enforce canonical SMALL progress for LoopExec development changes in CI status: completed + - id: task-34 + title: Audit published LoopExec and home verifier; improve receipt reporting and capture usability roadmap + status: completed + - id: task-35 + title: Enforce general-purpose code/docs release continuity and correct project-specific quality interpretation + status: completed + - id: task-36 + title: Adopt canonical docs-site handshake and hosted release protections before publication + status: pending + - id: task-37 + title: Prepare v0.4.1 release and source-bound docs deployment artifacts with verified CI handoff + status: pending + - id: task-38 + title: Add tested configurable software, game-development and audio-engineering recipes after deployment + status: blocked + dependencies: + - task-36 + - task-37 + - id: task-39 + title: Install latest published SMALL locally and recheck preserved strict-gate failure + status: completed + - id: task-40 + title: Prepare audit-preserving SMALL correction brief for upstream fix and authorized push + status: completed + - id: task-41 + title: Adopt stable SMALL1.1.2 for preserved QA command validation in local and CI tooling + status: completed diff --git a/.small/progress.small.yml b/.small/progress.small.yml index b4cc960..74f55ec 100644 --- a/.small/progress.small.yml +++ b/.small/progress.small.yml @@ -2099,3 +2099,2404 @@ entries: status: completed task_id: task-33 timestamp: "2026-09-27T02:23:47.528293000Z" + - evidence: Added task task-34 via small plan --add + notes: Audit published LoopExec and home verifier; improve receipt reporting and capture usability roadmap + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: pending + task_id: task-34 + timestamp: "2026-10-07T20:23:05.625276000Z" + - evidence: 'Starting: verify live v0.4.0 release/main/CI and deployed docs, inspect the read-only home verifier, reproduce report omissions, add bounded reporting fixes and durable integration/status guidance' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-34 + timestamp: "2026-10-07T20:23:05.857369000Z" + - command: GOCACHE=/private/tmp/loopexec-go-cache go build -o /private/tmp/loopexec-product-audit ./cmd/loopexec + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T202616.051738000Z.txt + command_sha256: ff3a273bbecbf4914eef0e7469ff852c39ac078391e1d3a702361d409c97d1ce + command_summary: GOCACHE=/private/tmp/loopexec-go-cache go build -o /private/tmp/loopexec-product-audit ./cmd/loopexec + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-34 + timestamp: "2026-10-07T20:26:16.051738000Z" + - command: /private/tmp/loopexec-product-audit demo --workdir /private/tmp/loopexec-report-before + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T202644.966625000Z.txt + command_sha256: ee999d6db59adb51f34e741e98f0917d2c6099a4d4c2be97d88c0a18b9f63903 + command_summary: /private/tmp/loopexec-product-audit demo --workdir /private/tmp/loopexec-report-before + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-34 + timestamp: "2026-10-07T20:26:44.966625000Z" + - command: 'apply_patch <<''PATCH'' *** Begin Patch *** Update File: /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec/cmd/loopexec/report_test.go @@ import ( "encoding/json" + "os" + "path/filepath" "s...' + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T202825.168365000Z.txt + command_sha256: a60935172a937dac9b2acdaeccebb68d625c98ecfba256802009c6e71d0686a6 + command_summary: 'apply_patch <<''PATCH'' *** Begin Patch *** Update File: /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec/cmd/loopexec/report_test.go @@ import ( "encoding/json" + "os" + "path/filepath" "s...' + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-34 + timestamp: "2026-10-07T20:28:25.168365000Z" + - command: GOCACHE=/private/tmp/loopexec-go-cache go test ./cmd/loopexec -run TestReport -count=1 + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T202839.907962000Z.txt + command_sha256: f8c6b88a1b900bc8a64d2c6e87df33522b856baf7fe2f95596eddb00023f79a2 + command_summary: GOCACHE=/private/tmp/loopexec-go-cache go test ./cmd/loopexec -run TestReport -count=1 + evidence: + exit_code: 1 + kind: cli_execution + outcome: failed + strength: cli_captured + notes: 'apply: command outcome failed; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-34 + timestamp: "2026-10-07T20:28:39.907962000Z" + - command: 'apply_patch <<''PATCH'' *** Begin Patch *** Update File: /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec/cmd/loopexec/report.go @@ import ( "bufio" + "bytes" @@ Receipt string `json:"recei...' + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T202947.725843000Z.txt + command_sha256: e9eea5b1ffdeb8075e2a779a2ff5fc779523b8d0755455037cd2b8cd15047a19 + command_summary: 'apply_patch <<''PATCH'' *** Begin Patch *** Update File: /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec/cmd/loopexec/report.go @@ import ( "bufio" + "bytes" @@ Receipt string `json:"recei...' + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-34 + timestamp: "2026-10-07T20:29:47.725843000Z" + - command: gofmt -w cmd/loopexec/report.go cmd/loopexec/report_test.go + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T202947.914163000Z.txt + command_sha256: 614bd43fd598a2da45eb725b19298f80fa0e2cc018a73640f3a758f02da8e35a + command_summary: gofmt -w cmd/loopexec/report.go cmd/loopexec/report_test.go + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-34 + timestamp: "2026-10-07T20:29:47.914163000Z" + - command: GOCACHE=/private/tmp/loopexec-go-cache go test ./cmd/loopexec -run TestReport -count=1 + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T202958.145561000Z.txt + command_sha256: f8c6b88a1b900bc8a64d2c6e87df33522b856baf7fe2f95596eddb00023f79a2 + command_summary: GOCACHE=/private/tmp/loopexec-go-cache go test ./cmd/loopexec -run TestReport -count=1 + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-34 + timestamp: "2026-10-07T20:29:58.145561000Z" + - command: 'apply_patch <<''PATCH'' *** Begin Patch *** Update File: /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec/cmd/loopexec/report.go @@ -func yesNo(b bool) string { - if b { - return "yes" - } ...' + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T203227.005555000Z.txt + command_sha256: ab6f38ca9f57a238ebd77f4736007321d03fad0ccbe688db7c0f2cfa9c1b4ce9 + command_summary: 'apply_patch <<''PATCH'' *** Begin Patch *** Update File: /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec/cmd/loopexec/report.go @@ -func yesNo(b bool) string { - if b { - return "yes" - } ...' + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-34 + timestamp: "2026-10-07T20:32:27.005555000Z" + - command: gofmt -w cmd/loopexec/report.go cmd/loopexec/report_test.go + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T203227.168905000Z.txt + command_sha256: 614bd43fd598a2da45eb725b19298f80fa0e2cc018a73640f3a758f02da8e35a + command_summary: gofmt -w cmd/loopexec/report.go cmd/loopexec/report_test.go + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-34 + timestamp: "2026-10-07T20:32:27.168905000Z" + - command: GOCACHE=/private/tmp/loopexec-go-cache go test ./cmd/loopexec -run TestReport -count=1 + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T203237.261405000Z.txt + command_sha256: f8c6b88a1b900bc8a64d2c6e87df33522b856baf7fe2f95596eddb00023f79a2 + command_summary: GOCACHE=/private/tmp/loopexec-go-cache go test ./cmd/loopexec -run TestReport -count=1 + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-34 + timestamp: "2026-10-07T20:32:37.261405000Z" + - command: GOCACHE=/private/tmp/loopexec-go-cache go vet ./... + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T203242.449827000Z.txt + command_sha256: 0302737bffd9d7eef237fbc63875221dbf5831230271b8901d66c42f2b32e024 + command_summary: GOCACHE=/private/tmp/loopexec-go-cache go vet ./... + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-34 + timestamp: "2026-10-07T20:32:42.449827000Z" + - command: 'apply_patch <<''PATCH'' *** Begin Patch *** Update File: /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec/docs/creative-verification.md @@ -The shared budget counts calls only. Do not confu...' + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T203342.583251000Z.txt + command_sha256: e7b7310f4bd83420ef31ce7b4403442b14139416374601cddc7b2539445bd8a4 + command_summary: 'apply_patch <<''PATCH'' *** Begin Patch *** Update File: /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec/docs/creative-verification.md @@ -The shared budget counts calls only. Do not confu...' + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-34 + timestamp: "2026-10-07T20:33:42.583251000Z" + - command: GOCACHE=/private/tmp/loopexec-go-cache go build -o build/product-audit/loopexec ./cmd/loopexec + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T203343.559845000Z.txt + command_sha256: 5c7f46ad66cfaaad97a2cb8f9f9f6f4dc78fb0da3fb53a3b9bdf25fb74389bd4 + command_summary: GOCACHE=/private/tmp/loopexec-go-cache go build -o build/product-audit/loopexec ./cmd/loopexec + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-34 + timestamp: "2026-10-07T20:33:43.559845000Z" + - command: GOCACHE=/private/tmp/loopexec-go-cache go test ./... + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T203538.222477000Z.txt + command_sha256: d5d49bef0689e10d3aaeab13abe1e5b78dedd06ebefe15308006f471b93b5e0e + command_summary: GOCACHE=/private/tmp/loopexec-go-cache go test ./... + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-34 + timestamp: "2026-10-07T20:35:38.222477000Z" + - command: ./scripts/dogfood-proof.sh build/product-audit/loopexec build/product-audit/dogfood + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T203635.884800000Z.txt + command_sha256: 2aa794d3618d569a72cf0ea4f491da30e79acb68a4b0035762cf1588cfdbf2ba + command_summary: ./scripts/dogfood-proof.sh build/product-audit/loopexec build/product-audit/dogfood + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-34 + timestamp: "2026-10-07T20:36:35.884800000Z" + - command: 'apply_patch <<''PATCH'' *** Begin Patch *** Add File: build/product-audit/audit.md +# LoopExec product audit + +Verified October 7, 2026. Public status: [status and roadmap](../../docs/status-and-roa...' + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T203818.496414000Z.txt + command_sha256: 3ac5601b365c1e1711a1a75a22dc123d775bea9cbab86970f0d3b8fd4fc7bcbb + command_summary: 'apply_patch <<''PATCH'' *** Begin Patch *** Add File: build/product-audit/audit.md +# LoopExec product audit + +Verified October 7, 2026. Public status: [status and roadmap](../../docs/status-and-roa...' + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-34 + timestamp: "2026-10-07T20:38:18.496414000Z" + - command: 'apply_patch <<''PATCH'' *** Begin Patch *** Update File: build/product-audit/audit.md @@ The current private project correctly blocks further spending. New IDs cannot be treated as permission for ano...' + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T204103.240261000Z.txt + command_sha256: fa359668a00a6159874da4eaf8e3270dcfe9c36a0b0b938d591ae0e3cb79bd20 + command_summary: 'apply_patch <<''PATCH'' *** Begin Patch *** Update File: build/product-audit/audit.md @@ The current private project correctly blocks further spending. New IDs cannot be treated as permission for ano...' + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-34 + timestamp: "2026-10-07T20:41:03.240261000Z" + - evidence: Verified live v0.4.0 release (7 assets), exact main CI and open backlog; captured 29 floor-rack snapshots, current 12 reservations and 4 analog review/readiness summaries in ignored build/product-audit. Fixed cmd/loopexec/report.go receipt warnings/timeline/failure cause/retention and signature status; added regression tests, changelog v0.4.0 and public evidence guide/roadmap. Focused tests, go vet ./..., go test ./..., DOGFOOD_OK, real read-only home report, format/diff and canonical SMALL sync checks pass. No publication, deployment, paid review or consumer mutation. + notes: Unrelated untracked handoff files and the separate dirty Jev worktree preserved. Public docs v0.2.0 and home pin v0.3.0 still need rollout; pre-ledger history and post-review asset edits remain explicit. Updated local report binary is build/product-audit/loopexec; artifact metadata was checked but release archives were not redownloaded. + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: completed + task_id: task-34 + timestamp: "2026-10-07T20:41:03.407764000Z" + - evidence: Added task task-35 via small plan --add + notes: Enforce general-purpose code/docs release continuity and correct project-specific quality interpretation + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: pending + task_id: task-35 + timestamp: "2026-10-07T20:53:05.327929000Z" + - evidence: 'Starting: add CI/packaging/publication docs parity gates, source-bound documentation artifacts and negative tests; preserve owner-adjusted acceptance versus aspirational quality and keep LoopExec domain-neutral' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-35 + timestamp: "2026-10-07T20:53:05.499433000Z" + - command: 'apply_patch <<''PATCH'' *** Begin Patch *** Add File: cmd/loopexec/documentation_test.go +package main + +import ( + "bytes" + "flag" + "fmt" + "os" + "path/filepath" + "sort" + "strings" + "testing"...' + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T205657.284429000Z.txt + command_sha256: 90d8d8cd511fad0c096706374bf92585191dddf1b4914ca141932b329dd522e0 + command_summary: 'apply_patch <<''PATCH'' *** Begin Patch *** Add File: cmd/loopexec/documentation_test.go +package main + +import ( + "bytes" + "flag" + "fmt" + "os" + "path/filepath" + "sort" + "strings" + "testing"...' + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-35 + timestamp: "2026-10-07T20:56:57.284429000Z" + - command: 'apply_patch <<''PATCH'' *** Begin Patch *** Add File: tools/docs-continuity/main.go +// docs-continuity binds public documentation to the exact source being released. +package main + +import ( + "byt...' + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T205827.165486000Z.txt + command_sha256: b1c38b1d4d093c3a7bb27e136e471e2d0cacaf1f2e518555baf25da79204f969 + command_summary: 'apply_patch <<''PATCH'' *** Begin Patch *** Add File: tools/docs-continuity/main.go +// docs-continuity binds public documentation to the exact source being released. +package main + +import ( + "byt...' + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-35 + timestamp: "2026-10-07T20:58:27.165486000Z" + - command: 'apply_patch <<''PATCH'' *** Begin Patch *** Add File: tools/docs-continuity/main_test.go +package main + +import ( + "encoding/json" + "fmt" + "net/http" + "net/http/httptest" + "os" + "os/exec" + "p...' + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T205934.391997000Z.txt + command_sha256: e18310fbd1f1595e9f7b4f9c651f7f3ce6a54596fd1c764ad2ba8a7813077daf + command_summary: 'apply_patch <<''PATCH'' *** Begin Patch *** Add File: tools/docs-continuity/main_test.go +package main + +import ( + "encoding/json" + "fmt" + "net/http" + "net/http/httptest" + "os" + "os/exec" + "p...' + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-35 + timestamp: "2026-10-07T20:59:34.391997000Z" + - command: 'apply_patch <<''PATCH'' *** Begin Patch *** Update File: .github/workflows/ci.yml @@ git diff --name-only "$BASE_SHA" HEAD | ./scripts/check-small-sync.sh + git diff --name-only "$BASE_SHA" HEAD | ba...' + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T210106.236546000Z.txt + command_sha256: b8c608cbacdfb1d546b9adfe15d1dac619e7df0fac125e1db8871d020ef2183a + command_summary: 'apply_patch <<''PATCH'' *** Begin Patch *** Update File: .github/workflows/ci.yml @@ git diff --name-only "$BASE_SHA" HEAD | ./scripts/check-small-sync.sh + git diff --name-only "$BASE_SHA" HEAD | ba...' + evidence: + exit_code: 1 + kind: cli_execution + outcome: failed + strength: cli_captured + notes: 'apply: command outcome failed; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-35 + timestamp: "2026-10-07T21:01:06.236546000Z" + - command: 'apply_patch <<''PATCH'' *** Begin Patch *** Update File: cmd/loopexec/documentation_test.go @@ "path/filepath" + "reflect" @@ visit(root) + fmt.Fprintln(&out, "## JSON field contract\n\nTypes and JSO...' + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T210319.247705000Z.txt + command_sha256: 0014d3dbaf6308251d3e8038b8fd61de15363c51290dfa8e2678b0d7e8e8d20a + command_summary: 'apply_patch <<''PATCH'' *** Begin Patch *** Update File: cmd/loopexec/documentation_test.go @@ "path/filepath" + "reflect" @@ visit(root) + fmt.Fprintln(&out, "## JSON field contract\n\nTypes and JSO...' + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-35 + timestamp: "2026-10-07T21:03:19.247705000Z" + - command: gofmt -w cmd/loopexec/documentation_test.go tools/docs-continuity + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T210328.142415000Z.txt + command_sha256: c1e1f344ce2ea9e2f52043700eb8bbaa1532f2d3e7a4d612f683347cc4a24500 + command_summary: gofmt -w cmd/loopexec/documentation_test.go tools/docs-continuity + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-35 + timestamp: "2026-10-07T21:03:28.142415000Z" + - command: GOCACHE=/private/tmp/loopexec-go-cache go test ./cmd/loopexec -run '^TestDocumentationContract$' -args -update-docs + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T210330.421384000Z.txt + command_sha256: 20ef83012185e4a919daced4d0b59f016f7076177661efec255b7ac9b953ab34 + command_summary: GOCACHE=/private/tmp/loopexec-go-cache go test ./cmd/loopexec -run '^TestDocumentationContract$' -args -update-docs + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-35 + timestamp: "2026-10-07T21:03:30.421384000Z" + - command: GOCACHE=/private/tmp/loopexec-go-cache go test ./tools/docs-continuity -count=1 + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T210343.242162000Z.txt + command_sha256: bcc1987239d0f7f470328034e61082e2ca6e2646599867a93e59763b60f3ea5f + command_summary: GOCACHE=/private/tmp/loopexec-go-cache go test ./tools/docs-continuity -count=1 + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-35 + timestamp: "2026-10-07T21:03:43.242162000Z" + - command: 'apply_patch <<''PATCH'' *** Begin Patch *** Update File: tools/docs-continuity/main.go @@ for _, path := range allowlist.Files { + if !publicPath(path) { + return m, fmt.Errorf("non-public documentat...' + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T210524.983033000Z.txt + command_sha256: dd8db2f89d4a42879831ad9230b78769c1994f7aba991b729e96dba23e5dbb04 + command_summary: 'apply_patch <<''PATCH'' *** Begin Patch *** Update File: tools/docs-continuity/main.go @@ for _, path := range allowlist.Files { + if !publicPath(path) { + return m, fmt.Errorf("non-public documentat...' + evidence: + exit_code: 1 + kind: cli_execution + outcome: failed + strength: cli_captured + notes: 'apply: command outcome failed; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-35 + timestamp: "2026-10-07T21:05:24.983033000Z" + - command: 'apply_patch <<''PATCH'' *** Begin Patch *** Update File: tools/docs-continuity/main.go @@ for _, path := range allowlist.Files { + if !publicPath(path) { + return m, fmt.Errorf("non-public documentat...' + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T210611.331248000Z.txt + command_sha256: 64363a0fc9d18e0fe7b2a33838bfd3bff9c4463c562dd6d205febc9d7f3fb6bc + command_summary: 'apply_patch <<''PATCH'' *** Begin Patch *** Update File: tools/docs-continuity/main.go @@ for _, path := range allowlist.Files { + if !publicPath(path) { + return m, fmt.Errorf("non-public documentat...' + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-35 + timestamp: "2026-10-07T21:06:11.331248000Z" + - command: gofmt -w cmd/loopexec/documentation_test.go cmd/loopexec/report_test.go tools/docs-continuity + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T210641.180230000Z.txt + command_sha256: 120cc6df0f564da7fc210d5f7eec1a9c7d2268548b3bf1a84804c43142182adc + command_summary: gofmt -w cmd/loopexec/documentation_test.go cmd/loopexec/report_test.go tools/docs-continuity + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-35 + timestamp: "2026-10-07T21:06:41.180230000Z" + - command: chmod +x scripts/check-docs-sync.sh + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T210641.344462000Z.txt + command_sha256: 5d5a1969bca3a6329fdcf959ccb587e22e945a3b97194fe9c7c6316c33f52fae + command_summary: chmod +x scripts/check-docs-sync.sh + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-35 + timestamp: "2026-10-07T21:06:41.344462000Z" + - command: GOCACHE=/private/tmp/loopexec-go-cache go test ./cmd/loopexec ./tools/docs-continuity -run 'TestDocumentationContract|TestReport|TestDeployedDocumentationGate|TestDocsCoChangeGate|TestBundleBoundTo... + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T210650.228364000Z.txt + command_sha256: ad93c24536aefc377bbe24009fff8aeb8f85bcd9877dff3889c9c50f1c35bceb + command_summary: GOCACHE=/private/tmp/loopexec-go-cache go test ./cmd/loopexec ./tools/docs-continuity -run 'TestDocumentationContract|TestReport|TestDeployedDocumentationGate|TestDocsCoChangeGate|TestBundleBoundTo... + evidence: + exit_code: 1 + kind: cli_execution + outcome: failed + strength: cli_captured + notes: 'apply: command outcome failed; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-35 + timestamp: "2026-10-07T21:06:50.228364000Z" + - command: 'apply_patch <<''PATCH'' *** Begin Patch *** Update File: tools/docs-continuity/main_test.go @@ "strings" "testing" ) + +type roundTripFunc func(*http.Request) (*http.Response, error) + +func (f round...' + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T210730.097682000Z.txt + command_sha256: 7b3dd0fd1c70c0cafa69f2a302fe3944e6297dd491839f700dc7c2d58e84f4a4 + command_summary: 'apply_patch <<''PATCH'' *** Begin Patch *** Update File: tools/docs-continuity/main_test.go @@ "strings" "testing" ) + +type roundTripFunc func(*http.Request) (*http.Response, error) + +func (f round...' + evidence: + exit_code: 1 + kind: cli_execution + outcome: failed + strength: cli_captured + notes: 'apply: command outcome failed; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-35 + timestamp: "2026-10-07T21:07:30.097682000Z" + - command: 'apply_patch <<''PATCH'' *** Begin Patch *** Update File: docs/cli.md @@ This document defines the command surface and machine contract for loopexec. + +The [generated command, flag and JSON field con...' + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T210823.858950000Z.txt + command_sha256: 0bf1c1224f8cf0095a30114c7923b0b2893496d1d8eef4da1138bc972c59aea0 + command_summary: 'apply_patch <<''PATCH'' *** Begin Patch *** Update File: docs/cli.md @@ This document defines the command surface and machine contract for loopexec. + +The [generated command, flag and JSON field con...' + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-35 + timestamp: "2026-10-07T21:08:23.858950000Z" + - command: gofmt -w tools/docs-continuity + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T210824.019009000Z.txt + command_sha256: 40562218719832f22d8bbe22e42f2f0ab9b02e6bd820890ee38038824f391e57 + command_summary: gofmt -w tools/docs-continuity + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-35 + timestamp: "2026-10-07T21:08:24.019009000Z" + - command: GOCACHE=/private/tmp/loopexec-go-cache go test ./cmd/loopexec ./tools/docs-continuity -run 'TestDocumentationContract|TestReport|TestDeployedDocumentationGate|TestDocsCoChangeGate|TestBundleBoundTo... + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T210833.076607000Z.txt + command_sha256: ad93c24536aefc377bbe24009fff8aeb8f85bcd9877dff3889c9c50f1c35bceb + command_summary: GOCACHE=/private/tmp/loopexec-go-cache go test ./cmd/loopexec ./tools/docs-continuity -run 'TestDocumentationContract|TestReport|TestDeployedDocumentationGate|TestDocsCoChangeGate|TestBundleBoundTo... + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-35 + timestamp: "2026-10-07T21:08:33.076607000Z" + - command: GOCACHE=/private/tmp/loopexec-go-cache go vet ./... + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T210903.882322000Z.txt + command_sha256: 0302737bffd9d7eef237fbc63875221dbf5831230271b8901d66c42f2b32e024 + command_summary: GOCACHE=/private/tmp/loopexec-go-cache go vet ./... + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-35 + timestamp: "2026-10-07T21:09:03.882322000Z" + - command: GOCACHE=/private/tmp/loopexec-go-cache go test ./... -count=1 + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T211011.876191000Z.txt + command_sha256: e9c67368d139c67c55c697d1248ede62de6edfcd5381e250c225f094c5905ba7 + command_summary: GOCACHE=/private/tmp/loopexec-go-cache go test ./... -count=1 + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-35 + timestamp: "2026-10-07T21:10:11.876191000Z" + - command: GOCACHE=/private/tmp/loopexec-go-cache ./scripts/build-release.sh v0.4.0 build/continuity-release-proof + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T211051.019593000Z.txt + command_sha256: f38225195e2d1041ad728b9d5fcfc411c94ae66b4a1b8b257ea4ef73b9be00e0 + command_summary: GOCACHE=/private/tmp/loopexec-go-cache ./scripts/build-release.sh v0.4.0 build/continuity-release-proof + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-35 + timestamp: "2026-10-07T21:10:51.019593000Z" + - command: 'apply_patch <<''PATCH'' *** Begin Patch *** Add File: build/product-audit/verify-packaged-docs.sh +#!/usr/bin/env bash +set -euo pipefail +out="$1" +manifest="$out/documentation/manifest.json" +jq -e...' + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T211115.824969000Z.txt + command_sha256: 0d71293268f649209320a8921acefce6277dafc5a00352cd5244a7169eae7966 + command_summary: 'apply_patch <<''PATCH'' *** Begin Patch *** Add File: build/product-audit/verify-packaged-docs.sh +#!/usr/bin/env bash +set -euo pipefail +out="$1" +manifest="$out/documentation/manifest.json" +jq -e...' + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-35 + timestamp: "2026-10-07T21:11:15.824969000Z" + - command: GOCACHE=/private/tmp/loopexec-go-cache go run ./tools/docs-continuity verify-site build/continuity-release-proof/documentation/manifest.json + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T211135.382179000Z.txt + command_sha256: c52082b7453bb84cb5fff9b686eec6f46826ae63ed09c34842ec568dfbea8e3b + command_summary: GOCACHE=/private/tmp/loopexec-go-cache go run ./tools/docs-continuity verify-site build/continuity-release-proof/documentation/manifest.json + evidence: + exit_code: 1 + kind: cli_execution + outcome: failed + strength: cli_captured + notes: 'apply: command outcome failed; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-35 + timestamp: "2026-10-07T21:11:35.382179000Z" + - command: bash build/product-audit/verify-packaged-docs.sh build/continuity-release-proof + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T211136.789116000Z.txt + command_sha256: 419c75e07012f214e9112bec45277a4c7a7cf6f0742bffd7914120d6c3c95c1a + command_summary: bash build/product-audit/verify-packaged-docs.sh build/continuity-release-proof + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-35 + timestamp: "2026-10-07T21:11:36.789116000Z" + - command: mkdir -p build/continuity-release-proof/install-darwin-arm64 + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T211202.499001000Z.txt + command_sha256: 533848850e20685a8c2ce816e7820855bb006d2869b3cf70a310d15a5be8eb20 + command_summary: mkdir -p build/continuity-release-proof/install-darwin-arm64 + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-35 + timestamp: "2026-10-07T21:12:02.499001000Z" + - command: tar -xzf build/continuity-release-proof/loopexec-v0.4.0-darwin-arm64.tar.gz -C build/continuity-release-proof/install-darwin-arm64 + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T211202.692259000Z.txt + command_sha256: 26c563517912898f9a041ca546ce9464aab0c1c58c8a6df7f3716140302ecc10 + command_summary: tar -xzf build/continuity-release-proof/loopexec-v0.4.0-darwin-arm64.tar.gz -C build/continuity-release-proof/install-darwin-arm64 + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-35 + timestamp: "2026-10-07T21:12:02.692259000Z" + - command: ./scripts/dogfood-proof.sh build/continuity-release-proof/install-darwin-arm64/loopexec build/product-audit/continuity-dogfood + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T211203.979527000Z.txt + command_sha256: 646843fbdea3b090f09fbf0992f26ba4267361e7a19dff1c5159a5dce83812e0 + command_summary: ./scripts/dogfood-proof.sh build/continuity-release-proof/install-darwin-arm64/loopexec build/product-audit/continuity-dogfood + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-35 + timestamp: "2026-10-07T21:12:03.979527000Z" + - command: 'apply_patch <<''PATCH'' *** Begin Patch *** Update File: scripts/build-release.sh @@ - cp LICENSE README.md "$stage/" + cp -R "$OUT/documentation/." "$stage/" @@ - (cd "$stage" && zip -qr "$OUT/$name...' + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T211418.341529000Z.txt + command_sha256: 5992b69a90e9e4a3f89aabe2301de7758ea84a9945500ef934ab9a0f0c258aeb + command_summary: 'apply_patch <<''PATCH'' *** Begin Patch *** Update File: scripts/build-release.sh @@ - cp LICENSE README.md "$stage/" + cp -R "$OUT/documentation/." "$stage/" @@ - (cd "$stage" && zip -qr "$OUT/$name...' + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-35 + timestamp: "2026-10-07T21:14:18.341529000Z" + - command: gofmt -w tools/docs-continuity + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T211418.503233000Z.txt + command_sha256: 40562218719832f22d8bbe22e42f2f0ab9b02e6bd820890ee38038824f391e57 + command_summary: gofmt -w tools/docs-continuity + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-35 + timestamp: "2026-10-07T21:14:18.503233000Z" + - command: GOCACHE=/private/tmp/loopexec-go-cache go test ./tools/docs-continuity -count=1 + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T211419.630111000Z.txt + command_sha256: bcc1987239d0f7f470328034e61082e2ca6e2646599867a93e59763b60f3ea5f + command_summary: GOCACHE=/private/tmp/loopexec-go-cache go test ./tools/docs-continuity -count=1 + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-35 + timestamp: "2026-10-07T21:14:19.630111000Z" + - command: GOCACHE=/private/tmp/loopexec-go-cache go vet ./tools/docs-continuity + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T211435.365423000Z.txt + command_sha256: bf79fdc11f5fd0a308ff22e021e5850e90ca298f7435bf4c0aa68440ee060514 + command_summary: GOCACHE=/private/tmp/loopexec-go-cache go vet ./tools/docs-continuity + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-35 + timestamp: "2026-10-07T21:14:35.365423000Z" + - command: GOCACHE=/private/tmp/loopexec-go-cache ./scripts/build-release.sh v0.4.0 build/continuity-release-proof-final + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T211441.661659000Z.txt + command_sha256: df4a7a5af66b8fdfbf04a6b8578ebe887217e6e35f518206849ac30381bf3d5f + command_summary: GOCACHE=/private/tmp/loopexec-go-cache ./scripts/build-release.sh v0.4.0 build/continuity-release-proof-final + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-35 + timestamp: "2026-10-07T21:14:41.661659000Z" + - command: GOCACHE=/private/tmp/loopexec-go-cache go run ./tools/docs-continuity verify-site build/continuity-release-proof-final/documentation/manifest.json + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T211513.704855000Z.txt + command_sha256: 28d10685487306d919569882b2e858c59e28878927bfa282edd72381c78483f4 + command_summary: GOCACHE=/private/tmp/loopexec-go-cache go run ./tools/docs-continuity verify-site build/continuity-release-proof-final/documentation/manifest.json + evidence: + exit_code: 1 + kind: cli_execution + outcome: failed + strength: cli_captured + notes: 'apply: command outcome failed; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-35 + timestamp: "2026-10-07T21:15:13.704855000Z" + - command: bash build/product-audit/verify-packaged-docs.sh build/continuity-release-proof-final + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T211515.127432000Z.txt + command_sha256: 58a5cd31ae09503049ada654b61c351afb80085fa6a97260f4fe5b00fdedf97e + command_summary: bash build/product-audit/verify-packaged-docs.sh build/continuity-release-proof-final + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-35 + timestamp: "2026-10-07T21:15:15.127432000Z" + - command: 'apply_patch <<''PATCH'' *** Begin Patch *** Add File: build/product-audit/continuity-hardening.md +# Code/docs continuity hardening receipt + +Outcome: local hardening verified; no-ship until the doc...' + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T211656.345834000Z.txt + command_sha256: ddb8cba85bec49ea848b5b1be7d3d3e3c0a119527161c0e03533ea31aa152d80 + command_summary: 'apply_patch <<''PATCH'' *** Begin Patch *** Add File: build/product-audit/continuity-hardening.md +# Code/docs continuity hardening receipt + +Outcome: local hardening verified; no-ship until the doc...' + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-35 + timestamp: "2026-10-07T21:16:56.345834000Z" + - evidence: 'Implemented AGENTS/SPEC continuity, canonical prose+changelog co-change gate, generated Cobra CLI/JSON contract, source-bound allowlisted docs builder/verifier, six-platform paired docs packaging and fail-closed official site checks before draft/publication. General-purpose acceptance guide and corrected owner-adjusted 8.1 versus desired 8.5 interpretation; private quality-gap plan. Full suite/vet, focused final tests, seven archive document hashes/root paths, checksums and identical fresh macOS dogfood binary pass; live site manifest HTTP404 correctly blocks release. Receipt: build/product-audit/continuity-hardening.md.' + notes: Local uncommitted work only; no consumer mutation, model/engine call, spending, commit, push or deployment. Separate docs-site adoption and hosted branch/tag/release protection verification remain required before publication. Unrelated handoff notes preserved. + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: completed + task_id: task-35 + timestamp: "2026-10-07T21:16:56.487264000Z" + - evidence: Added task task-36 via small plan --add + notes: Adopt canonical docs-site handshake and hosted release protections before publication + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: pending + task_id: task-36 + timestamp: "2026-10-07T21:16:56.637017000Z" + - evidence: Live official endpoint https://loopexec.dev/loopexec-documentation.json returns HTTP404 against the final source-bound bundle; release gate must remain closed. Canonical site integration/build/render/deploy requires the separate loopexec.dev checkout, outside this workspace write roots. Hosted CI/main protection/tag/release write rules also need authorized rollout and live verification. No bypass or publication attempted. + notes: Prepared contract and exact commands in docs/documentation-continuity.md; receipt and rollout steps in build/product-audit/continuity-hardening.md. Local task-35 hardening is complete; rollout is not. + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: blocked + task_id: task-36 + timestamp: "2026-10-07T21:17:31.304771000Z" + - evidence: User authorized deployment. Starting exact-source docs-site integration/build/publishing checks, v0.4.1 patch release preparation, hosted CI and live readback. GitHub connector core repo accessible; companion docs repo returns404; local gh auth invalid and git DNS fails. Checking existing Vercel access before declaring deployment blocked. + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-07T22:51:29.864265000Z" + - evidence: Added task task-37 via small plan --add + notes: Prepare v0.4.1 release and source-bound docs deployment artifacts with verified CI handoff + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: pending + task_id: task-37 + timestamp: "2026-10-07T22:52:11.181275000Z" + - evidence: Starting patch release preparation and isolated docs-site integration in writable build artifacts. User authorized deployment and requested recipes afterward. Preserve unrelated files and original home evidence/budgets. No stable publication until matching site and exact CI checks pass. + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-37 + timestamp: "2026-10-07T22:52:11.362383000Z" + - command: mkdir -p build/deployment/docs-site + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T225229.084574000Z.txt + command_sha256: d48ba89ef5b472ec5957aa24c128523e6169528325a23033f2520976b9f47278 + command_summary: mkdir -p build/deployment/docs-site + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-37 + timestamp: "2026-10-07T22:52:29.084574000Z" + - command: git -C /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec.dev archive HEAD | tar -xf - -C build/deployment/docs-site + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T225229.291700000Z.txt + command_sha256: 31c3fa789bdf3f15548be6ddbf1a19fcbbc8d809d15bbd01616101e96571b8c7 + command_summary: git -C /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec.dev archive HEAD | tar -xf - -C build/deployment/docs-site + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-37 + timestamp: "2026-10-07T22:52:29.291700000Z" + - command: 'apply_patch <<''PATCH'' *** Begin Patch *** Update File: cmd/loopexec/main.go @@ - toolVersion = "0.4.0" + toolVersion = "0.4.1" *** Update File: docs/documentation-files.json @@ "docs/releases/v0.4....' + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T225707.456433000Z.txt + command_sha256: c9fcd86ecea0f0f72f8e90fb9f753e145f41f45e84ea4fa95c050730d1878340 + command_summary: 'apply_patch <<''PATCH'' *** Begin Patch *** Update File: cmd/loopexec/main.go @@ - toolVersion = "0.4.0" + toolVersion = "0.4.1" *** Update File: docs/documentation-files.json @@ "docs/releases/v0.4....' + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-37 + timestamp: "2026-10-07T22:57:07.456433000Z" + - command: GOCACHE=/private/tmp/loopexec-go-cache go test ./cmd/loopexec -run '^TestDocumentationContract$' -args -update-docs + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T225709.393136000Z.txt + command_sha256: 20ef83012185e4a919daced4d0b59f016f7076177661efec255b7ac9b953ab34 + command_summary: GOCACHE=/private/tmp/loopexec-go-cache go test ./cmd/loopexec -run '^TestDocumentationContract$' -args -update-docs + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-37 + timestamp: "2026-10-07T22:57:09.393136000Z" + - command: 'apply_patch <<''PATCH'' *** Begin Patch *** Add File: build/deployment/docs-site/apps/docs/scripts/canonical-docs.mjs +import { createHash } from "node:crypto"; +import { lstatSync, mkdirSync, readFi...' + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T225937.219629000Z.txt + command_sha256: 0963ae9646742b573e7ca11fd4c737340e5cbc0c3a2caae7b22eb998f23e007f + command_summary: 'apply_patch <<''PATCH'' *** Begin Patch *** Add File: build/deployment/docs-site/apps/docs/scripts/canonical-docs.mjs +import { createHash } from "node:crypto"; +import { lstatSync, mkdirSync, readFi...' + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-37 + timestamp: "2026-10-07T22:59:37.219629000Z" + - command: 'apply_patch <<''PATCH'' *** Begin Patch *** Update File: build/deployment/docs-site/apps/docs/astro.config.mjs @@ import { ion } from "starlight-ion-theme"; +import { canonicalLinks } from "./scripts...' + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T230039.738593000Z.txt + command_sha256: 8a4e1bb0143fd3533efff22569236e1ced7defdc684aeb0ae9ce0310f42e10dc + command_summary: 'apply_patch <<''PATCH'' *** Begin Patch *** Update File: build/deployment/docs-site/apps/docs/astro.config.mjs @@ import { ion } from "starlight-ion-theme"; +import { canonicalLinks } from "./scripts...' + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-37 + timestamp: "2026-10-07T23:00:39.738593000Z" + - command: mkdir -p build/deployment/docs-site/apps/docs/canonical + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T230039.901237000Z.txt + command_sha256: 8cf996d37415b41fd3eeea717aac81cea5abe87e9b125d65669f481ab9ddfd3d + command_summary: mkdir -p build/deployment/docs-site/apps/docs/canonical + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-37 + timestamp: "2026-10-07T23:00:39.901237000Z" + - command: GOCACHE=/private/tmp/loopexec-go-cache go run ./tools/docs-continuity bundle build/deployment/docs-site/apps/docs/canonical/loopexec + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T230040.406444000Z.txt + command_sha256: bf02d7d3881b822617505ea9681e55b252ff5e5497b3827a66362a3d370d3ecd + command_summary: GOCACHE=/private/tmp/loopexec-go-cache go run ./tools/docs-continuity bundle build/deployment/docs-site/apps/docs/canonical/loopexec + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-37 + timestamp: "2026-10-07T23:00:40.406444000Z" + - command: mkdir -p build/deployment/docs-site/node_modules + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T230111.744326000Z.txt + command_sha256: 0b2a160dff9c696a6a0c01e8b84e517e9d393a1af932bf96301669c516c4d4b4 + command_summary: mkdir -p build/deployment/docs-site/node_modules + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-37 + timestamp: "2026-10-07T23:01:11.744326000Z" + - command: ln -s /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec.dev/node_modules/.pnpm build/deployment/docs-site/node_modules/.pnpm + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T230111.918047000Z.txt + command_sha256: c67ab169cfe13da3f8a5ba86b4159245b418f1ba4105695f7c6d5d8a158fab5a + command_summary: ln -s /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec.dev/node_modules/.pnpm build/deployment/docs-site/node_modules/.pnpm + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-37 + timestamp: "2026-10-07T23:01:11.918047000Z" + - command: cp -R /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec.dev/apps/docs/node_modules build/deployment/docs-site/apps/docs/ + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T230112.112142000Z.txt + command_sha256: d71e433d0d3288f5740015b94588e5e6850000093dd53683269ec9b908271bd5 + command_summary: cp -R /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec.dev/apps/docs/node_modules build/deployment/docs-site/apps/docs/ + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-37 + timestamp: "2026-10-07T23:01:12.112142000Z" + - command: node --test build/deployment/docs-site/apps/docs/scripts/canonical-docs.test.mjs + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T230112.542706000Z.txt + command_sha256: b6a1b594bc84d12a4f38a489d60b36f31f99efc2797265cf9574d011d5dc949a + command_summary: node --test build/deployment/docs-site/apps/docs/scripts/canonical-docs.test.mjs + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-37 + timestamp: "2026-10-07T23:01:12.542706000Z" + - command: pnpm --dir build/deployment/docs-site/apps/docs run build + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T230116.122229000Z.txt + command_sha256: d79f4bbb26dd397693dfa62255055e7183f75fe069650f8abb4918572eabf747 + command_summary: pnpm --dir build/deployment/docs-site/apps/docs run build + evidence: + exit_code: 1 + kind: cli_execution + outcome: failed + strength: cli_captured + notes: 'apply: command outcome failed; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-37 + timestamp: "2026-10-07T23:01:16.122229000Z" + - command: GOCACHE=/private/tmp/loopexec-go-cache go test ./cmd/loopexec ./tools/docs-continuity -run 'TestVersionIdentity|TestDocumentationContract|TestReport|TestDeployedDocumentationGate|TestDocsCoChangeGa... + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T230151.525330000Z.txt + command_sha256: f73778a007a8472409f12008c72e665d69f2dba8057699c00ce9b26f53748c00 + command_summary: GOCACHE=/private/tmp/loopexec-go-cache go test ./cmd/loopexec ./tools/docs-continuity -run 'TestVersionIdentity|TestDocumentationContract|TestReport|TestDeployedDocumentationGate|TestDocsCoChangeGa... + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-37 + timestamp: "2026-10-07T23:01:51.525330000Z" + - command: cp -R /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec.dev/packages/starlight-ion-theme/node_modules build/deployment/docs-site/packages/starlight-ion-theme/ + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T230212.499627000Z.txt + command_sha256: e4c0d2cc3458441d008c9efaa3f5c258a9ab4d2e2dd58629fd1c6400107adc28 + command_summary: cp -R /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec.dev/packages/starlight-ion-theme/node_modules build/deployment/docs-site/packages/starlight-ion-theme/ + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-37 + timestamp: "2026-10-07T23:02:12.499627000Z" + - command: ASTRO_TELEMETRY_DISABLED=1 pnpm --dir build/deployment/docs-site/apps/docs run build + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T230220.387562000Z.txt + command_sha256: f4ed496b615cd4a461f5c0838bb06dea62267fb0ed875eea901635d630644f2d + command_summary: ASTRO_TELEMETRY_DISABLED=1 pnpm --dir build/deployment/docs-site/apps/docs run build + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-37 + timestamp: "2026-10-07T23:02:20.387562000Z" + - evidence: Added task task-38 via small plan --add + notes: Add tested configurable software, game-development and audio-engineering recipes after deployment + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: pending + task_id: task-38 + timestamp: "2026-10-07T23:03:04.020034000Z" + - evidence: Added dependency task-36 to task-38 + notes: small plan --depends + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: pending + task_id: task-38 + timestamp: "2026-10-07T23:03:04.189698000Z" + - evidence: Added dependency task-37 to task-38 + notes: small plan --depends + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: pending + task_id: task-38 + timestamp: "2026-10-07T23:03:04.395297000Z" + - command: GOCACHE=/private/tmp/loopexec-go-cache go vet ./... + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T230305.592333000Z.txt + command_sha256: 0302737bffd9d7eef237fbc63875221dbf5831230271b8901d66c42f2b32e024 + command_summary: GOCACHE=/private/tmp/loopexec-go-cache go vet ./... + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-37 + timestamp: "2026-10-07T23:03:05.592333000Z" + - command: GOCACHE=/private/tmp/loopexec-go-cache go test ./... -count=1 + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T230427.244844000Z.txt + command_sha256: e9c67368d139c67c55c697d1248ede62de6edfcd5381e250c225f094c5905ba7 + command_summary: GOCACHE=/private/tmp/loopexec-go-cache go test ./... -count=1 + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-37 + timestamp: "2026-10-07T23:04:27.244844000Z" + - command: 'apply_patch <<''PATCH'' *** Begin Patch *** Update File: build/deployment/docs-site/apps/docs/scripts/canonical-docs.mjs @@ for (const page of [...pages, release]) { @@ writeFileSync(join(contentRoot...' + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T230437.480725000Z.txt + command_sha256: 7b05a79c65ad94d228f7bd5942dc3d63dca9dcbe66a3845cf1e8c21fecbc7319 + command_summary: 'apply_patch <<''PATCH'' *** Begin Patch *** Update File: build/deployment/docs-site/apps/docs/scripts/canonical-docs.mjs @@ for (const page of [...pages, release]) { @@ writeFileSync(join(contentRoot...' + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-37 + timestamp: "2026-10-07T23:04:37.480725000Z" + - command: node --test build/deployment/docs-site/apps/docs/scripts/canonical-docs.test.mjs + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T230438.521307000Z.txt + command_sha256: b6a1b594bc84d12a4f38a489d60b36f31f99efc2797265cf9574d011d5dc949a + command_summary: node --test build/deployment/docs-site/apps/docs/scripts/canonical-docs.test.mjs + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-37 + timestamp: "2026-10-07T23:04:38.521307000Z" + - command: ASTRO_TELEMETRY_DISABLED=1 pnpm --dir build/deployment/docs-site/apps/docs run build + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T230451.977112000Z.txt + command_sha256: f4ed496b615cd4a461f5c0838bb06dea62267fb0ed875eea901635d630644f2d + command_summary: ASTRO_TELEMETRY_DISABLED=1 pnpm --dir build/deployment/docs-site/apps/docs run build + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-37 + timestamp: "2026-10-07T23:04:51.977112000Z" + - command: 'apply_patch <<''PATCH'' *** Begin Patch *** Update File: build/deployment/docs-site/apps/docs/scripts/canonical-docs.mjs @@ export function canonicalLinks() { + const manifest = verifyCanonical(); @@...' + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T230636.869115000Z.txt + command_sha256: 1c722850b88850fc7e467762304479854a3ea75993c8e61088aebc43e32c6acb + command_summary: 'apply_patch <<''PATCH'' *** Begin Patch *** Update File: build/deployment/docs-site/apps/docs/scripts/canonical-docs.mjs @@ export function canonicalLinks() { + const manifest = verifyCanonical(); @@...' + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-37 + timestamp: "2026-10-07T23:06:36.869115000Z" + - command: node --test build/deployment/docs-site/apps/docs/scripts/canonical-docs.test.mjs + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T230642.196234000Z.txt + command_sha256: b6a1b594bc84d12a4f38a489d60b36f31f99efc2797265cf9574d011d5dc949a + command_summary: node --test build/deployment/docs-site/apps/docs/scripts/canonical-docs.test.mjs + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-37 + timestamp: "2026-10-07T23:06:42.196234000Z" + - command: node /Users/justin/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/node_modules/playwright/cli.js screenshot --viewport-size 1440,1000 --wait-for-timeout 800 --timeout 15000 http://12... + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T230645.843552000Z.txt + command_sha256: 3a9159b8ed0e83cadee0ccc34e8fb70164815a20f165c86402bbfa7edfd6f607 + command_summary: node /Users/justin/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/node_modules/playwright/cli.js screenshot --viewport-size 1440,1000 --wait-for-timeout 800 --timeout 15000 http://12... + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-37 + timestamp: "2026-10-07T23:06:45.843552000Z" + - evidence: v0.4.1 source/reference/allowlist/notes prepared. go vet ./... and full suite pass (CLI80.454s; creative14.048s). Isolated docs-site canonical tests4/4 and Astro static build pass,18 searchable canonical pages plus redirects; source marker is generated from exact imported bundle. Preparing source branch/PR via accessible GitHub connector; stable publication remains gated on companion repo/Vercel access and hosted protections. + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-37 + timestamp: "2026-10-07T23:07:37.121912000Z" + - command: ASTRO_TELEMETRY_DISABLED=1 pnpm --dir build/deployment/docs-site/apps/docs run build + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T230740.173696000Z.txt + command_sha256: f4ed496b615cd4a461f5c0838bb06dea62267fb0ed875eea901635d630644f2d + command_summary: ASTRO_TELEMETRY_DISABLED=1 pnpm --dir build/deployment/docs-site/apps/docs run build + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-37 + timestamp: "2026-10-07T23:07:40.173696000Z" + - command: node /Users/justin/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/node_modules/playwright/cli.js screenshot --viewport-size 390,844 --wait-for-timeout 800 --timeout 15000 http://127.... + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T230805.581332000Z.txt + command_sha256: f967306198f45f0b60b0e9eae2d03399e082d970c7ed26d7da916fc2eaf982c1 + command_summary: node /Users/justin/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/node_modules/playwright/cli.js screenshot --viewport-size 390,844 --wait-for-timeout 800 --timeout 15000 http://127.... + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-37 + timestamp: "2026-10-07T23:08:05.581332000Z" + - command: kill -TERM 48890 + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261007T231303.561109000Z.txt + command_sha256: a36133c4ef1348f24550344b04f1553b4a267b1ae60bda1c294db5552b7d81bc + command_summary: kill -TERM 48890 + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-37 + timestamp: "2026-10-07T23:13:03.561109000Z" + - evidence: 'Deployment authorized but not performed: GitHub connector can read justyn-clark/loopexec; justyn-clark/loopexec.dev repo and main return404. gh default token invalid; git remote DNS unavailable; Vercel CLI fails resolving api.vercel.com. Vercel plugin suggestion was made but no connection confirmed. Official docs manifest readback returned404. Hosted main protections not configured; core main protected=false. Need docs-repo access and Vercel connection, followed by canonical site deployment/readback and exact-head hosted gates.' + notes: No production changes, remote commits, PR, tag, merge or release created. + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: blocked + task_id: task-36 + timestamp: "2026-10-07T23:13:45.930872000Z" + - evidence: 'Prepared v0.4.1 CLI identity, generated reference, docs allowlist and release notes. go vet ./... and fresh go test ./... -count=1 passed. Isolated companion site in build/deployment/docs-site: canonical tests4/4, static Astro build, visually inspected desktop/mobile screenshots pass. Bundle source cdc25eb53140c51956b9ecfc1be343e7f83aa3eda7736755cabb7121807a877d; docs16c95e02dd11b8c5af567f26918faf822d389b4ae055893667bc60e9550d7b89;20files. Final SMALL strict fails4 lint violations: two apply command summaries truncated loopback URL hosts; both command and command_summary duplicates fail. Full command refs and SHA256 intact. Supported CLI has no audit-entry repair; no manual history edits or gate bypass performed.' + notes: Screenshots build/deployment/docs-desktop.png and docs-mobile.png. Preview PID48890 terminated; port4345 no listener. Original preview apply ended143 and failed recording because concurrent cleanup held state.lock; not retried. No current v0.4.1 packages built or hosted CI verified; do not publish. Need supported SMALL repair before resumed work; handoff forbidden while strict fails. + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: blocked + task_id: task-37 + timestamp: "2026-10-07T23:13:46.139515000Z" + - evidence: 'Waiting for deployment prerequisites task-36/task-37, preserving requested order: deploy first, then add more software-engineering, coding/programming, game-development and audio-engineering workflows. No new recipes implemented this turn. Core remains general-purpose with caller-supplied policies;8.5/8.1 are project-specific creative quality criteria, not global acceptance defaults.' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: blocked + task_id: task-38 + timestamp: "2026-10-07T23:13:46.360881000Z" + - evidence: Owner requests mobile-authorized headless login. Attempting official GitHub and Vercel device flows only; deployment remains stopped behind existing SMALL strict failure. Auth config will be isolated in ignored build/deployment/auth; never capture access tokens or change .env. + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-08T03:58:37.499431000Z" + - command: GH_CONFIG_DIR=$PWD/build/deployment/auth/github GH_BROWSER=/usr/bin/true gh auth login --web --hostname github.com --git-protocol https --scopes workflow + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261008T040339.764400000Z.txt + command_sha256: 10c943bc13aff77e744d4e9f1c2506b94cf96b6ca5405917cbbb34462aff4b83 + command_summary: GH_CONFIG_DIR=$PWD/build/deployment/auth/github GH_BROWSER=/usr/bin/true gh auth login --web --hostname github.com --git-protocol https --scopes workflow + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-08T04:03:39.764400000Z" + - command: chmod 700 build/deployment/auth build/deployment/auth/vercel build/deployment/auth/github + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261008T040542.566245000Z.txt + command_sha256: 1088ade535733c249d1f6634ff038c79575bc664bdd59876f2cc231f9dac0231 + command_summary: chmod 700 build/deployment/auth build/deployment/auth/vercel build/deployment/auth/github + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-08T04:05:42.566245000Z" + - command: BROWSER=/usr/bin/true vercel login --future --global-config build/deployment/auth/vercel + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261008T040843.582737000Z.txt + command_sha256: 154b5de203de6f2da0138863dbc4070a359f35eabe13c8e0744ee18b2a5879d7 + command_summary: BROWSER=/usr/bin/true vercel login --future --global-config build/deployment/auth/vercel + evidence: + exit_code: 1 + kind: cli_execution + outcome: failed + strength: cli_captured + notes: 'apply: command outcome failed; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-08T04:08:43.582737000Z" + - evidence: GitHub official device flow completed as justyn-clark; gh API validation is blocked by api.github.com DNS failure (curl exit6), so earlier auth-status invalid-token output is not credential proof. Vercel CLI device request expired; mobile Brave/Android finds no passkey. Owner has signed in from computer with existing passkey; desktop Vercel session visibly confirmed via native Brave UI. Guided owner to add mobile TOTP as additional factor without removing passkey or weakening2FA; new credential creation handed to owner. Owner now reports Vercel sudo-mode timeout; checking vendor-supported reauthentication. No production/account-security changes made by agent. + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-08T04:13:26.406217000Z" + - evidence: 'Mobile authorization attempt: GitHub device flow completed; API verification still fails because api.github.com cannot resolve. Vercel device code expired before approval. Owner Android/Brave could not find Vercel passkey; owner can sign in from computer with existing passkey. Desktop signed-in Vercel project session confirmed through native Brave UI. New mobile authentication credential setup requires owner entry/confirmation and was handed off: add TOTP authenticator or second Android passkey while retaining computer key. Owner reports sudo-mode timeout; advised refreshing Authentication and fresh existing-passkey confirmation, not reuse expired setup QR. Mobile access and Vercel CLI credentials remain unverified. No account methods removed,2FA disabled, secrets read/displayed, production deployment or code changes performed this turn.' + notes: CLI auth config isolated under ignored build/deployment/auth with700directories; existing credential files600. GitHub login and expired Vercel login sessions have ended. Need owner completion/test of mobile factor, then fresh Vercel device approval. Existing SMALL command-summary lint blocker persists; no manual audit edits or handoff performed. + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: blocked + task_id: task-36 + timestamp: "2026-10-08T04:15:27.749187000Z" + - evidence: Added task task-39 via small plan --add + notes: Install latest published SMALL locally and recheck preserved strict-gate failure + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: pending + task_id: task-39 + timestamp: "2026-10-08T04:28:18.612240000Z" + - evidence: Owner requests latest SMALL locally. Live GitHub release metadata resolves v1.1.0, published2026-09-16. Default PATH is Homebrew1.0.2; prior agent binary is unreleased1.1.1 development snapshot. Installing verified official darwin-arm64 archive under ignored build/tooling/small-v1.1.0 and testing unchanged canonical audit. No protocol migration or history edits; global Homebrew path outside writable scope. + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-39 + timestamp: "2026-10-08T04:28:39.060602000Z" + - command: mkdir -p build/tooling/small-v1.1.0 + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261008T042839.266406000Z.txt + command_sha256: e45c763ff902c51d4f04402ee7bc2af07e4a2df4f27c027d748af3a57424abf0 + command_summary: mkdir -p build/tooling/small-v1.1.0 + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-39 + timestamp: "2026-10-08T04:28:39.266406000Z" + - command: curl -fLsS --connect-timeout 10 --max-time 60 https://github.com/justyn-clark/small-protocol/releases/download/v1.1.0/small-v1.1.0-darwin-arm64.tar.gz -o build/tooling/small-v1.1.0/release.tar.gz + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261008T042840.191176000Z.txt + command_sha256: 49a2bac17809bff7044d937b06e9464fe2ba4a43172882ee0654e903cc24c4e3 + command_summary: curl -fLsS --connect-timeout 10 --max-time 60 https://github.com/justyn-clark/small-protocol/releases/download/v1.1.0/small-v1.1.0-darwin-arm64.tar.gz -o build/tooling/small-v1.1.0/release.tar.gz + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-39 + timestamp: "2026-10-08T04:28:40.191176000Z" + - command: curl -fLsS --connect-timeout 10 --max-time 60 https://github.com/justyn-clark/small-protocol/releases/download/v1.1.0/checksums.txt -o build/tooling/small-v1.1.0/checksums.txt + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261008T042852.591355000Z.txt + command_sha256: 4eebb32ddc2fd0cdb3b90e81ddc953ed0906bee6fc6add9bcc0a32150fae4221 + command_summary: curl -fLsS --connect-timeout 10 --max-time 60 https://github.com/justyn-clark/small-protocol/releases/download/v1.1.0/checksums.txt -o build/tooling/small-v1.1.0/checksums.txt + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-39 + timestamp: "2026-10-08T04:28:52.591355000Z" + - command: tar -xzf build/tooling/small-v1.1.0/release.tar.gz -C build/tooling/small-v1.1.0 + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261008T042939.604437000Z.txt + command_sha256: 26bf6f2bd3cd4465524213b9d18c154c0b4d2bdebc84405839da4d77ba199da9 + command_summary: tar -xzf build/tooling/small-v1.1.0/release.tar.gz -C build/tooling/small-v1.1.0 + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-39 + timestamp: "2026-10-08T04:29:39.604437000Z" + - command: build/tooling/small-v1.1.0/small init --no-agents --dir build/tooling/small-summary-repro --intent "Isolated official-release command summary and strict lint reproduction" + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261008T043126.315327000Z.txt + command_sha256: 86cc683cd1d456bca43c4dd207f4b45e04975209649d02967b9925adcc3382b2 + command_summary: build/tooling/small-v1.1.0/small init --no-agents --dir build/tooling/small-summary-repro --intent "Isolated official-release command summary and strict lint reproduction" + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-39 + timestamp: "2026-10-08T04:31:26.315327000Z" + - command: build/tooling/small-v1.1.0/small plan --dir build/tooling/small-summary-repro --add "Test URL summary preservation" + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261008T043126.554846000Z.txt + command_sha256: 2037acd67022a493448ca83ec3712abf00fb6ed2cff46c5729748ecae4bb292d + command_summary: build/tooling/small-v1.1.0/small plan --dir build/tooling/small-summary-repro --add "Test URL summary preservation" + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-39 + timestamp: "2026-10-08T04:31:26.554846000Z" + - command: build/tooling/small-v1.1.0/small apply --dir build/tooling/small-summary-repro --task task-2 --cmd 'printf '\''%s\n'\'' '\''aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa... + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261008T043203.116423000Z.txt + command_sha256: 2219dab42a8f1a882eb89025681b59e44d2d06fcf81dc2746276ac7d2a541677 + command_summary: build/tooling/small-v1.1.0/small apply --dir build/tooling/small-summary-repro --task task-2 --cmd 'printf '\''%s\n'\'' '\''aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa... + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-39 + timestamp: "2026-10-08T04:32:03.116423000Z" + - command: 'apply_patch <<''PATCH'' *** Begin Patch *** Add File: /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec/build/tooling/small-v1.1.0/verification.md +# SMALL local installation and strict-gate...' + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261008T043351.071438000Z.txt + command_sha256: b439fdf05b7e690ad1eddbeda14664fb943d86a9476d8afd9aa5f4b627af53f2 + command_summary: 'apply_patch <<''PATCH'' *** Begin Patch *** Add File: /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec/build/tooling/small-v1.1.0/verification.md +# SMALL local installation and strict-gate...' + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-39 + timestamp: "2026-10-08T04:33:51.071438000Z" + - evidence: 'Installed checksum-verified official latest stable SMALLv1.1.0 under build/tooling/small-v1.1.0; version and explicit PATH resolution pass. Release and checksum digests independently match live GitHub API. Fresh isolated fixture build/tooling/small-summary-repro passes strict before harmless printf capture, then fails2 insecure-link findings after the released CLI truncates its allowed loopback URL. Canonical state remains4 findings. Confirmed defect is in SMALL CLI, not LoopExec runtime. Receipt: build/tooling/small-v1.1.0/verification.md. No audit edits, profile migration, gate suppression or production changes.' + notes: Workspace-local installation is active for agent calls. Global Homebrew small remains1.0.2 outside writable scope; machine-wide update not completed. Supported upstream repair still needed for preserved audit; strict handoff remains forbidden. Earlier development binary no longer used for state mutations. + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: blocked + task_id: task-39 + timestamp: "2026-10-08T04:33:51.253539000Z" + - evidence: Added task task-40 via small plan --add + notes: Prepare audit-preserving SMALL correction brief for upstream fix and authorized push + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: pending + task_id: task-40 + timestamp: "2026-10-08T04:47:18.641785000Z" + - evidence: Owner requests correction brief to run in SMALL project, implement fix and push. Preparing private UPDATES/small-command-summary-correction-handoff.md with released-CLI reproduction, original command hashes, secure legacy validation, portable CI evidence requirements, scoped push authorization and LoopExec unblock checks. This is an implementation brief, not a gated SMALL state handoff. No upstream writes or release authorization inferred. + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-40 + timestamp: "2026-10-08T04:49:27.697863000Z" + - command: 'apply_patch <<''PATCH'' *** Begin Patch *** Add File: /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec/UPDATES/small-command-summary-correction-handoff.md +# SMALL correction handoff: trunc...' + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261008T045400.358984000Z.txt + command_sha256: d853f7d5358d692605cefecf3546ebcac52304f0e7094410701b261bd10a284d + command_summary: 'apply_patch <<''PATCH'' *** Begin Patch *** Add File: /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec/UPDATES/small-command-summary-correction-handoff.md +# SMALL correction handoff: trunc...' + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-40 + timestamp: "2026-10-08T04:54:00.358984000Z" + - command: git diff --check + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261008T045427.260373000Z.txt + command_sha256: 466c2f308b48c7661d646fdd068fbecea974c665fe65dbf8ed508f224180ce0b + command_summary: git diff --check + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-40 + timestamp: "2026-10-08T04:54:27.260373000Z" + - evidence: 'Prepared private UPDATES/small-command-summary-correction-handoff.md: official v1.1.0 reproduction, original command refs and verified hashes, audit-preserving secure compatibility, selective CI proof, regression matrix, scoped push and LoopExec unblock criteria. File readback, whitespace check, ignore verification, original-log hash recheck and git diff --check pass. No upstream code changes or push performed.' + notes: Engineering correction brief only; not a formal SMALL handoff. Canonical strict gate remains blocked by four known upstream truncation/lint findings. Stable publication and global installation require their own authorized scope. + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: completed + task_id: task-40 + timestamp: "2026-10-08T04:54:27.445582000Z" + - evidence: Resuming after owner confirms upstream SMALL correction. Explicit patched binary v1.1.0-dev.command-summary.3069dab8ab25 passes canonical strict; progress SHA256 remains548b49b1aa30761f09f1253e101d7080fc1ac55c7f7421cc1f887521956df253 before new entries. Saved next task36. Rechecking canonical docs-site access, hosted protections, stable SMALL release and CI pin; protocol repair alone does not authorize clearing deployment gates. + notes: Append-only continuation; no reset, migration or historical receipt edits. Plain small remains Homebrew1.0.2 and will not be used. + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-08T19:39:38.571545000Z" + - command: mkdir -p .small-command-proofs/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261008T194420.636308000Z.txt + command_sha256: e86ac867a052edff01216f468b1dd7f5184c4c4acf4c36a0399dec7c64623a91 + command_summary: mkdir -p .small-command-proofs/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-08T19:44:20.636308000Z" + - command: cp .small-cache/logs/*/commands/20261007T230645.843552000Z.txt .small-command-proofs/logs/*/commands/ + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261008T194420.812173000Z.txt + command_sha256: a1499e1e7f59a67d32a880d89f87d2d4c55f2d5514a529eaa157935b7b6bca10 + command_summary: cp .small-cache/logs/*/commands/20261007T230645.843552000Z.txt .small-command-proofs/logs/*/commands/ + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-08T19:44:20.812173000Z" + - command: cp .small-cache/logs/*/commands/20261007T230805.581332000Z.txt .small-command-proofs/logs/*/commands/ + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261008T194420.982794000Z.txt + command_sha256: a1d1cc46e08e8d7ffd80d9ba189f840c1ff41a887c095d2f45c97741bea24be3 + command_summary: cp .small-cache/logs/*/commands/20261007T230805.581332000Z.txt .small-command-proofs/logs/*/commands/ + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-08T19:44:20.982794000Z" + - command: mkdir -p build/deployment/small-cacheless-with-proof + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261008T194421.156622000Z.txt + command_sha256: 21c1e3169280b9fbdb0b2962fe1d0479a8acbb3bcdaf983bf58d4acbfdb75b78 + command_summary: mkdir -p build/deployment/small-cacheless-with-proof + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-08T19:44:21.156622000Z" + - command: cp -R .small build/deployment/small-cacheless-with-proof/ + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261008T194421.336495000Z.txt + command_sha256: e72ebb57d8e25d78ede7f8db22ef6db7e702e0a503c6173e31c0ff3553c82288 + command_summary: cp -R .small build/deployment/small-cacheless-with-proof/ + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-08T19:44:21.336495000Z" + - command: cp -R .small-command-proofs build/deployment/small-cacheless-with-proof/ + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261008T194421.516975000Z.txt + command_sha256: 18f50aaba420276f04025c6ac2b01e10dddf4228dd31d56ebc4dbf65ca66471a + command_summary: cp -R .small-command-proofs build/deployment/small-cacheless-with-proof/ + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-08T19:44:21.516975000Z" + - command: mkdir -p build/deployment/small-cacheless-without-proof + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261008T194421.696088000Z.txt + command_sha256: a56f74f0b6753fa1c0f47a835af4a9c981c0173c1d8af0900c9503378fffce65 + command_summary: mkdir -p build/deployment/small-cacheless-without-proof + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-08T19:44:21.696088000Z" + - command: cp -R .small build/deployment/small-cacheless-without-proof/ + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261008T194421.875361000Z.txt + command_sha256: 9932f1122f5870d7614fa9cbd1864defd40f938fc4d07e84911a318214fbda4c + command_summary: cp -R .small build/deployment/small-cacheless-without-proof/ + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-08T19:44:21.875361000Z" + - command: cp -R build/deployment/small-cacheless-with-proof build/deployment/small-cacheless-tamper + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261008T194523.682788000Z.txt + command_sha256: 6d09d07f68b72e06d4308cc73a0cdf17432126171c343dfea472bc264dd731ec + command_summary: cp -R build/deployment/small-cacheless-with-proof build/deployment/small-cacheless-tamper + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-08T19:45:23.682788000Z" + - command: printf '\n' >> build/deployment/small-cacheless-tamper/.small-command-proofs/logs/*/commands/20261007T230645.843552000Z.txt + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261008T194523.865589000Z.txt + command_sha256: bd999c051b802a746f432600837a6eda182f7d6ba676a63edebdb180da75cce7 + command_summary: printf '\n' >> build/deployment/small-cacheless-tamper/.small-command-proofs/logs/*/commands/20261007T230645.843552000Z.txt + evidence: + exit_code: 1 + kind: cli_execution + outcome: failed + strength: cli_captured + notes: 'apply: command outcome failed; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-08T19:45:23.865589000Z" + - command: git diff --check + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261008T194524.368008000Z.txt + command_sha256: 466c2f308b48c7661d646fdd068fbecea974c665fe65dbf8ed508f224180ce0b + command_summary: git diff --check + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-08T19:45:24.368008000Z" + - command: for f in build/deployment/small-cacheless-tamper/.small-command-proofs/logs/*/commands/20261007T230645.843552000Z.txt; do printf '\n' >> "$f"; done + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261008T194539.619113000Z.txt + command_sha256: 778836dee093ffaf26d81fac2959d97895d5895a746aafe0707b57bfe519dacd + command_summary: for f in build/deployment/small-cacheless-tamper/.small-command-proofs/logs/*/commands/20261007T230645.843552000Z.txt; do printf '\n' >> "$f"; done + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-08T19:45:39.619113000Z" + - command: 'apply_patch <<''PATCH'' *** Begin Patch *** Update File: .gitignore @@ logs/ +# Only individually reviewed command proof belongs here, never the private cache. +!.small-command-proofs/logs/ docs/jcn-...' + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261008T194655.178548000Z.txt + command_sha256: e06c6b4155f25bc89d095369ea0f57493f093f0e97540bfb85ef72d46f697978 + command_summary: 'apply_patch <<''PATCH'' *** Begin Patch *** Update File: .gitignore @@ logs/ +# Only individually reviewed command proof belongs here, never the private cache. +!.small-command-proofs/logs/ docs/jcn-...' + command_summary_version: 2 + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-08T19:46:55.178548000Z" + - command: cp .small-cache/logs/*/commands/20261008T194655.178548000Z.txt .small-command-proofs/logs/*/commands/ + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261008T194750.721631000Z.txt + command_sha256: 12033240b6d50614150612b628dfe3aee5c06c58d68b43c9b3d2f8a8f16d43a7 + command_summary: cp .small-cache/logs/*/commands/20261008T194655.178548000Z.txt .small-command-proofs/logs/*/commands/ + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-08T19:47:50.721631000Z" + - command: cp -R .small build/deployment/small-cacheless-with-proof/ + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261008T194750.916948000Z.txt + command_sha256: e72ebb57d8e25d78ede7f8db22ef6db7e702e0a503c6173e31c0ff3553c82288 + command_summary: cp -R .small build/deployment/small-cacheless-with-proof/ + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-08T19:47:50.916948000Z" + - command: cp -R .small-command-proofs build/deployment/small-cacheless-with-proof/ + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261008T194751.102997000Z.txt + command_sha256: 18f50aaba420276f04025c6ac2b01e10dddf4228dd31d56ebc4dbf65ca66471a + command_summary: cp -R .small-command-proofs build/deployment/small-cacheless-with-proof/ + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-08T19:47:51.102997000Z" + - command: cp -R .small build/deployment/small-cacheless-without-proof/ + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261008T194751.290073000Z.txt + command_sha256: 9932f1122f5870d7614fa9cbd1864defd40f938fc4d07e84911a318214fbda4c + command_summary: cp -R .small build/deployment/small-cacheless-without-proof/ + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-08T19:47:51.290073000Z" + - command: git diff --name-only | bash scripts/check-docs-sync.sh + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261008T194751.500408000Z.txt + command_sha256: 5bc970449335df35ad4f483066da4139bec81d0bb3d5e7be495efde79f9f0e61 + command_summary: git diff --name-only | bash scripts/check-docs-sync.sh + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-08T19:47:51.500408000Z" + - command: git diff --check + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261008T194751.709403000Z.txt + command_sha256: 466c2f308b48c7661d646fdd068fbecea974c665fe65dbf8ed508f224180ce0b + command_summary: git diff --check + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-08T19:47:51.709403000Z" + - command: GOCACHE=/private/tmp/loopexec-go-cache go test ./cmd/loopexec -run TestDocumentationContract -count=1 + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261008T194753.961757000Z.txt + command_sha256: 88698b31027e9affe4ed2bad1e35886024bc0db8e38ee7f0643b4077bed0c961 + command_summary: GOCACHE=/private/tmp/loopexec-go-cache go test ./cmd/loopexec -run TestDocumentationContract -count=1 + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-08T19:47:53.961757000Z" + - command: GOCACHE=/private/tmp/loopexec-go-cache go test ./tools/docs-continuity -count=1 + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261008T194824.646580000Z.txt + command_sha256: bcc1987239d0f7f470328034e61082e2ca6e2646599867a93e59763b60f3ea5f + command_summary: GOCACHE=/private/tmp/loopexec-go-cache go test ./tools/docs-continuity -count=1 + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-08T19:48:24.646580000Z" + - command: GOCACHE=/private/tmp/loopexec-go-cache go run ./tools/docs-continuity bundle build/deployment/task36-resumed-documentation + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261008T194917.870961000Z.txt + command_sha256: 371ce9f29537168be6c7e8c4c56791b4671b45172a71f713ff20f22de0959d2c + command_summary: GOCACHE=/private/tmp/loopexec-go-cache go run ./tools/docs-continuity bundle build/deployment/task36-resumed-documentation + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-08T19:49:17.870961000Z" + - evidence: 'Prepared three individually reviewed non-secret portable proofs: two original screenshot refs plus new public-policy patch capture. Proof SHA256 matches saved refs. Cache-free snapshot passes with proof and preserves its audit files; missing proof fails closed, tampered proof rejects SHA256 mismatch. .gitignore now permits supported proof store only while private cache stays ignored. AGENTS.md, docs/small-integration.md and CHANGELOG.md document explicit patched CLI and hosted-adoption boundary. CLI documentation contract and docs-continuity Go tests pass; docs co-change gate and git diff --check pass. Fresh source-bound bundle: build/deployment/task36-resumed-documentation. Canonical strict passes without mutating saved state during validation.' + notes: One disposable tamper attempt failed because POSIX redirection did not expand a glob; corrected generated-data loop then reproduced the intended hash-mismatch rejection. No original proof/history edits or runtime changes. + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-08T19:50:21.437352000Z" + - evidence: 'Protocol blocker cleared and continuation proof prepared. Remaining deployment gates rechecked live: canonical docs endpoint displays404 in Brave; Vercel production remains ea9e69d from Jun27 and browser session is authenticated. Docs GitHub repo connection returns404; companion local checkout is outside writable roots. Core main protected=false; branch-protection API returns403. Published SMALL remainsv1.1.0; LoopExec CI still pins that incompatible release. Asked owner to choose immutable merged correction pin versus waiting for stable patch, and supply writable companion docs context/repo connection. CLI networking cannot resolve hosts; Vercel CLI isolated config has no credentials. No push, deployment, publication or account-security change.' + notes: Do not reopen the resolved SMALL defect or use plain Homebrew small1.0.2. Task37/38 remain blocked pending docs-site adoption and compatible exact-head hosted gates. Existing original receipts and consumer allowances preserved. + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: blocked + task_id: task-36 + timestamp: "2026-10-08T19:50:21.665840000Z" + - evidence: 'Owner expects access to all owned GitHub repositories, local and remote. Diagnosing access rather than treating ownership as permission denial: connected login justyn-clark; owned GitHub App installation105323366 has repository_selection selected. Docs repo still connector404; CLI git/gh fail DNS before authorization. Companion checkout readable, clean except unrelated .zed, outside current writable roots. Checking signed-in GitHub UI and installation scope before any security-sensitive permission changes.' + notes: No credential values read, no broad grant saved, no sandbox or auth bypass. User ownership intention does not itself change current connector/network/filesystem enforcement. + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-08T20:04:29.816859000Z" + - command: git clone --no-hardlinks /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec.dev build/deployment/docs-checkout + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261008T200834.495069000Z.txt + command_sha256: b1769dd8150810801253866284203ad311ce48c67784ef6a26577fd4589a62d7 + command_summary: git clone --no-hardlinks /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec.dev build/deployment/docs-checkout + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-08T20:08:34.495069000Z" + - command: git -C build/deployment/docs-checkout remote set-url origin git@github.com:justyn-clark/loopexec.dev.git + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261008T200940.315263000Z.txt + command_sha256: b1047924645dccbcf3063f4d4873630ae1bf616abd227c73d17250eebdac1cc1 + command_summary: git -C build/deployment/docs-checkout remote set-url origin git@github.com:justyn-clark/loopexec.dev.git + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-08T20:09:40.315263000Z" + - evidence: 'Confirmed private justyn-clark/loopexec.dev exists and is accessible in owner Brave GitHub session. Connector scope cause verified in GitHub UI: ChatGPT Codex Connector installed105323366, only15 selected repositories, excludes docs and core LoopExec. Owner mobile reauthentication succeeded with fresh85 after99 timed out. At actual All repositories setting, requested explicit confirmation before expanding existing read/write app access to all current/future owner repos; no grant saved yet. Created clean standalone writable clone build/deployment/docs-checkout at ea9e69d77a1c6fa2ae9d31f475a4f30e43353309, set origin to canonical GitHub repo, patched strict passes. Original checkout and unrelated .zed remain untouched.' + notes: 'Local docs implementation need not wait for expanded filesystem roots: work in the bounded writable clone. CLI DNS/network limitation remains distinct from remote connector scope. No account credential creation or security weakening.' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-08T20:09:40.879684000Z" + - evidence: Owner explicitly confirmed ChatGPT Codex Connector All repositories grant for current/future justyn-clark repos with existing code/actions/workflows/issues/PR permissions. GitHub mobile85 reauthentication accepted. Saved installation105323366 All repositories in owner settings; live installation API confirms repository_selection all. Connected private docs repo now succeeds and reports pull/push/admin owner permissions; core repo also accessible with push. Remote repository scope blocker resolved. Writable clean docs clone build/deployment/docs-checkout at canonical ea9e69d passes patched strict, original checkout preserved. No password/passkey/2FA weakening, credentials exposed, other-account installation changes or production deployment. + notes: CLI DNS restriction still exists for direct git/gh; use functioning GitHub connection for remote operations. App scope is not a promise of administration API scopes or unrestricted filesystem writes. Repository work can proceed within the writable clone without those bypasses. Stable publication is not a blocker for local implementation. + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-08T20:11:47.532227000Z" + - evidence: Verified global /opt/homebrew/bin/small v1.1.1, Homebrew Cellar1.1.1, Go module v1.1.1 revision6e1270b with vcs.modified=false; live official v1.1.1 release exists. Stable strict passes in LoopExec and docs clone without modifying existing receipts. CI adoption remains task36. + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: completed + task_id: task-39 + timestamp: "2026-10-09T00:32:34.140610000Z" + - evidence: Resuming paired deployment using verified stable SMALL1.1.1. Root and writable docs clone strict passed; GitHub connector access was granted by owner. Adopt stable CI pin and canonical prose, import source-bound docs, then verify hosted checks and official deployed handshake before release. + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-09T00:32:34.295385000Z" + - command: 'apply_patch <<''PATCH'' *** Begin Patch *** Update File: /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec/.github/workflows/ci.yml @@ - archive="small-v1.1.0-$(go env GOOS)-$(go env GOARCH)...' + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T003234.483715000Z.txt + command_sha256: 4e96767c0e6e193af2afc5d5182bb940e41815e70390c01393376bf49a80d4af + command_summary: 'apply_patch <<''PATCH'' *** Begin Patch *** Update File: /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec/.github/workflows/ci.yml @@ - archive="small-v1.1.0-$(go env GOOS)-$(go env GOARCH)...' + command_summary_version: 2 + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-09T00:32:34.483715000Z" + - command: cp .small-cache/logs/*/commands/20261009T003234.483715000Z.txt .small-command-proofs/logs/*/commands/ + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T003302.519873000Z.txt + command_sha256: 0934633b87cfd2f8830c9b0e4d43a49e70b0b25ec2ef893f608380e93475351b + command_summary: cp .small-cache/logs/*/commands/20261009T003234.483715000Z.txt .small-command-proofs/logs/*/commands/ + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-09T00:33:02.519873000Z" + - command: GOCACHE=/private/tmp/loopexec-go-cache go test ./cmd/loopexec ./tools/docs-continuity -count=1 + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T003352.726936000Z.txt + command_sha256: 5b348a410a79c9ae9caa58a996db46c53962d72073b59859ee4a380e9ca644f6 + command_summary: GOCACHE=/private/tmp/loopexec-go-cache go test ./cmd/loopexec ./tools/docs-continuity -count=1 + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-09T00:33:52.726936000Z" + - command: 'apply_patch <<''PATCH'' *** Begin Patch *** Update File: /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec/.github/workflows/release.yml @@ on: push: tags: [''v*''] + workflow_dispatch: @@ ver...' + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T003414.303312000Z.txt + command_sha256: 60ace4050aa19d6da5d7824c3acd2d1d42f51077f9ac5d0826b2a45ca5e0ff63 + command_summary: 'apply_patch <<''PATCH'' *** Begin Patch *** Update File: /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec/.github/workflows/release.yml @@ on: push: tags: [''v*''] + workflow_dispatch: @@ ver...' + command_summary_version: 2 + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-09T00:34:14.303312000Z" + - command: cp .small-cache/logs/*/commands/20261009T003414.303312000Z.txt .small-command-proofs/logs/*/commands/ + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T003450.645408000Z.txt + command_sha256: 9933e5a507772932d5b5bd922afb87b22e1114efbad2f6d5e7d6f7cd1e3eecf2 + command_summary: cp .small-cache/logs/*/commands/20261009T003414.303312000Z.txt .small-command-proofs/logs/*/commands/ + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-09T00:34:50.645408000Z" + - command: GOCACHE=/private/tmp/loopexec-go-cache go run ./tools/docs-continuity bundle build/deployment/release-documentation-v0.4.1 + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T003450.898358000Z.txt + command_sha256: 479b4bdbe7fd3196db982211c1d451fdba919d7b28a4cf06ab44711691954be6 + command_summary: GOCACHE=/private/tmp/loopexec-go-cache go run ./tools/docs-continuity bundle build/deployment/release-documentation-v0.4.1 + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-09T00:34:50.898358000Z" + - command: GOCACHE=/private/tmp/loopexec-go-cache go test ./... -count=1 + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T003602.130952000Z.txt + command_sha256: e9c67368d139c67c55c697d1248ede62de6edfcd5381e250c225f094c5905ba7 + command_summary: GOCACHE=/private/tmp/loopexec-go-cache go test ./... -count=1 + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-09T00:36:02.130952000Z" + - command: mkdir -p build/deployment/release-cacheless-core build/deployment/release-cacheless-site + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T003624.898090000Z.txt + command_sha256: 23a68ff6cb94752a9c4747acb6cbe42e22cc33fb5ca7c616fe1c69cc79fcf339 + command_summary: mkdir -p build/deployment/release-cacheless-core build/deployment/release-cacheless-site + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-09T00:36:24.898090000Z" + - command: cp -R .small .small-command-proofs build/deployment/release-cacheless-core/ + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T003625.044973000Z.txt + command_sha256: c4c5f6a53e01a462fcab9e955e097c66dd7cd04e20a7fc59fff227c2f0664426 + command_summary: cp -R .small .small-command-proofs build/deployment/release-cacheless-core/ + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-09T00:36:25.044973000Z" + - command: cp -R build/deployment/docs-checkout/.small build/deployment/docs-checkout/.small-command-proofs build/deployment/release-cacheless-site/ + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T003625.189972000Z.txt + command_sha256: 5d27acc9fc8c816da9d46c71d9ba8046b7443cb9b89c2fcd6348d6e826d5aea3 + command_summary: cp -R build/deployment/docs-checkout/.small build/deployment/docs-checkout/.small-command-proofs build/deployment/release-cacheless-site/ + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-09T00:36:25.189972000Z" + - command: 'apply_patch <<''PATCH'' *** Begin Patch *** Add File: /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec/build/deployment/run-docs-build.mjs +import { fileURLToPath } from ''node:url''; +proces...' + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T003658.229732000Z.txt + command_sha256: 6faa3db9ba826362f1bf448d1eea7bc86ebc1ab0f4e8434c95be23b0c40d1b0d + command_summary: 'apply_patch <<''PATCH'' *** Begin Patch *** Add File: /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec/build/deployment/run-docs-build.mjs +import { fileURLToPath } from ''node:url''; +proces...' + command_summary_version: 2 + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-09T00:36:58.229732000Z" + - command: /Users/justin/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/bin/node build/deployment/run-docs-build.mjs + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T003702.171674000Z.txt + command_sha256: 287e1ba943433763c7fae83ff524780bb6d689b13eb3cdc01cc95da99043035e + command_summary: /Users/justin/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/bin/node build/deployment/run-docs-build.mjs + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-09T00:37:02.171674000Z" + - command: cp .small-cache/logs/*/commands/20261009T003658.229732000Z.txt .small-command-proofs/logs/*/commands/ + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T003706.876270000Z.txt + command_sha256: 6892b4ed8d06704333dcce0e12d654994bcc57cf334eb8aea266122e33fb64f7 + command_summary: cp .small-cache/logs/*/commands/20261009T003658.229732000Z.txt .small-command-proofs/logs/*/commands/ + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-09T00:37:06.876270000Z" + - command: 'apply_patch <<''PATCH'' *** Begin Patch *** Add File: /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec/build/deployment/site-qa.mjs +import { chromium } from ''/Users/justin/.cache/codex-run...' + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T003833.567017000Z.txt + command_sha256: 5f06e6615a251c4124e5fd85bd580f405c8ba27c7b20e5d576b3aa4f6d9d0996 + command_summary: 'apply_patch <<''PATCH'' *** Begin Patch *** Add File: /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec/build/deployment/site-qa.mjs +import { chromium } from ''/Users/justin/.cache/codex-run...' + command_summary_version: 2 + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-09T00:38:33.567017000Z" + - command: /Users/justin/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/bin/node build/deployment/site-qa.mjs + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T003841.543203000Z.txt + command_sha256: 8540138e3e7ec90522bf32136dca753305c5a6d38cf32aa38e88452cc55a0dde + command_summary: /Users/justin/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/bin/node build/deployment/site-qa.mjs + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-09T00:38:41.543203000Z" + - command: GOCACHE=/private/tmp/loopexec-go-cache ./scripts/build-release.sh v0.4.1 build/deployment/release-packages-v0.4.1 + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T003842.544315000Z.txt + command_sha256: db1950c1bfe948d01dca050c31552591da5904503a80a75c152f493df9f91934 + command_summary: GOCACHE=/private/tmp/loopexec-go-cache ./scripts/build-release.sh v0.4.1 build/deployment/release-packages-v0.4.1 + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-09T00:38:42.544315000Z" + - command: cp .small-cache/logs/*/commands/20261009T003833.567017000Z.txt .small-command-proofs/logs/*/commands/ + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T003845.248371000Z.txt + command_sha256: 3d3779a4ab558375fa2b7d3e0dc5f54f4e4ccc85a6ed096c7f69e260b5102131 + command_summary: cp .small-cache/logs/*/commands/20261009T003833.567017000Z.txt .small-command-proofs/logs/*/commands/ + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-09T00:38:45.248371000Z" + - evidence: 'Verified stable SMALL1.1.1 tag resolves to installed revision6e1270b. Adopted CI pin and canonical prose/changelog. Go test ./... passes; 4 canonical site tests and Astro build pass. Playwright checked six routes at1440x1000 and390x844: no document overflow, missing internal links, broken visible images or page errors. Built six platform archives plus source-bound docs archive with matching SHA256 checksums. Manifest source34f5732e and docs85f78953. Browser controls unavailable while Mac locked; no merge/tag/deployment/publication yet.' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-09T00:39:17.551010000Z" + - command: 'apply_patch <<''PATCH'' *** Begin Patch *** Add File: /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec/docs/small-template-command-validation-handoff.md +# SMALL Command-Literal Validation ...' + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T004202.466623000Z.txt + command_sha256: c159e3fe4a2fb113341384ed389fdbde8d2701131fac495c89e8bc31d235dadc + command_summary: 'apply_patch <<''PATCH'' *** Begin Patch *** Add File: /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec/docs/small-template-command-validation-handoff.md +# SMALL Command-Literal Validation ...' + command_summary_version: 2 + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-09T00:42:02.466623000Z" + - command: cp .small-cache/logs/*/commands/20261009T004202.466623000Z.txt .small-command-proofs/logs/*/commands/ + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T004232.633247000Z.txt + command_sha256: 187375ce6bb548f5315112eb785e51208868019903c26f7097963fea362316cf + command_summary: cp .small-cache/logs/*/commands/20261009T004202.466623000Z.txt .small-command-proofs/logs/*/commands/ + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-09T00:42:32.633247000Z" + - evidence: Local candidate tests, canonical docs build, twelve desktop/mobile route checks and seven checksummed archives pass. Stable SMALL1.1.1 strict now fails on exact QA-helper patch capture20261009T003833.567017000Z, SHA5f06e661, containing a templated loopback origin. Agent introduced this capture; history is preserved. No supported amend/retract command exists. Internal reproduction and required policy decision saved in docs/small-template-command-validation-handoff.md. Mac locked also prevents owner browser controls. No remote PR/merge/tag/deployment/publication. + notes: No strict bypass, receipt rewrite, reset, paid review or quality-threshold change. Root handoff cannot run while strict fails; task39 global stable install remains completed. Resolve exact-command policy upstream or approve a documented loss-preserving remediation before task36 resumes. + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: blocked + task_id: task-36 + timestamp: "2026-10-09T00:42:32.798024000Z" + - evidence: Added task task-41 via small plan --add + notes: Adopt stable SMALL1.1.2 for preserved QA command validation in local and CI tooling + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: pending + task_id: task-41 + timestamp: "2026-10-09T13:27:07.207464000Z" + - evidence: Official latest release is v1.1.2 with narrowly scoped captured-code local URL support. Installed module v1.1.2 revision1a101564, vcs.modified=false. Strict passes against original root state; all14 authoritative audit and reviewed proof hashes unchanged across read-only validation. Align CI and canonical prose; no protocol migration, receipt rewrite or runtime-quality change. + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-41 + timestamp: "2026-10-09T13:27:07.387077000Z" + - command: 'apply_patch <<''PATCH'' *** Begin Patch *** Update File: /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec/.github/workflows/ci.yml @@ - archive="small-v1.1.1-$(go env GOOS)-$(go env GOARCH)...' + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T132707.643132000Z.txt + command_sha256: 011527af69e8bafa7291f43ccd53727504836502c173ca6a9bd17a78e09c9172 + command_summary: 'apply_patch <<''PATCH'' *** Begin Patch *** Update File: /Users/justin/jcnagent/Agent/Projects/ai-protocol/loopexec/.github/workflows/ci.yml @@ - archive="small-v1.1.1-$(go env GOOS)-$(go env GOARCH)...' + command_summary_version: 2 + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-41 + timestamp: "2026-10-09T13:27:07.643132000Z" + - command: cp .small-cache/logs/*/commands/20261009T132707.643132000Z.txt .small-command-proofs/logs/*/commands/ + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T132748.733619000Z.txt + command_sha256: 17a4a9f5911e82155d382860490090368999882801719bf224d3736544ea4109 + command_summary: cp .small-cache/logs/*/commands/20261009T132707.643132000Z.txt .small-command-proofs/logs/*/commands/ + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-41 + timestamp: "2026-10-09T13:27:48.733619000Z" + - command: GOCACHE=/private/tmp/loopexec-go-cache go test ./tools/docs-continuity -count=1 + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T132751.065840000Z.txt + command_sha256: bcc1987239d0f7f470328034e61082e2ca6e2646599867a93e59763b60f3ea5f + command_summary: GOCACHE=/private/tmp/loopexec-go-cache go test ./tools/docs-continuity -count=1 + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-41 + timestamp: "2026-10-09T13:27:51.065840000Z" + - command: GOCACHE=/private/tmp/loopexec-go-cache go test ./cmd/loopexec -run TestDocumentationContract -count=1 + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T132751.326675000Z.txt + command_sha256: 88698b31027e9affe4ed2bad1e35886024bc0db8e38ee7f0643b4077bed0c961 + command_summary: GOCACHE=/private/tmp/loopexec-go-cache go test ./cmd/loopexec -run TestDocumentationContract -count=1 + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-41 + timestamp: "2026-10-09T13:27:51.326675000Z" + - command: GOCACHE=/private/tmp/loopexec-go-cache go run ./tools/docs-continuity bundle build/deployment/small112-documentation + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T132827.390175000Z.txt + command_sha256: 5f38acd7a045b2e9ab8427f4e724297792c6106438855d61b3b4c21c8de5bf02 + command_summary: GOCACHE=/private/tmp/loopexec-go-cache go run ./tools/docs-continuity bundle build/deployment/small112-documentation + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-41 + timestamp: "2026-10-09T13:28:27.390175000Z" + - command: /Users/justin/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/bin/node build/deployment/run-docs-build.mjs + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T132834.988212000Z.txt + command_sha256: 287e1ba943433763c7fae83ff524780bb6d689b13eb3cdc01cc95da99043035e + command_summary: /Users/justin/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/bin/node build/deployment/run-docs-build.mjs + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-41 + timestamp: "2026-10-09T13:28:34.988212000Z" + - command: mkdir -p build/deployment/small112-cacheless/core build/deployment/small112-cacheless/site build/deployment/small112-cacheless/missing-proof + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T132920.701102000Z.txt + command_sha256: 342a4b4065c8b99e4d859619b2bd7bd8c6b7da5ca98936a2478c7a2c4ad2d3d5 + command_summary: mkdir -p build/deployment/small112-cacheless/core build/deployment/small112-cacheless/site build/deployment/small112-cacheless/missing-proof + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-41 + timestamp: "2026-10-09T13:29:20.701102000Z" + - command: cp -R .small .small-command-proofs build/deployment/small112-cacheless/core/ + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T132920.988627000Z.txt + command_sha256: 6c2f7a4fdc3f693de7fb8a2b50fe3256dbb8ba0437bdcbcbc01424391fd03bc0 + command_summary: cp -R .small .small-command-proofs build/deployment/small112-cacheless/core/ + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-41 + timestamp: "2026-10-09T13:29:20.988627000Z" + - command: cp -R .small build/deployment/small112-cacheless/missing-proof/ + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T132921.255167000Z.txt + command_sha256: 7f8847a77bc871ba9cb21603859460e3353e16688a015577b2a0c2626672cef3 + command_summary: cp -R .small build/deployment/small112-cacheless/missing-proof/ + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-41 + timestamp: "2026-10-09T13:29:21.255167000Z" + - command: cp -R build/deployment/docs-checkout/.small build/deployment/docs-checkout/.small-command-proofs build/deployment/small112-cacheless/site/ + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T132921.503779000Z.txt + command_sha256: 5afc075efc538ad2a42d232b0af5605cc51a4f11ce84d9268b88178fa913a720 + command_summary: cp -R build/deployment/docs-checkout/.small build/deployment/docs-checkout/.small-command-proofs build/deployment/small112-cacheless/site/ + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-41 + timestamp: "2026-10-09T13:29:21.503779000Z" + - command: /Users/justin/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/bin/node build/deployment/site-qa.mjs + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T133001.348682000Z.txt + command_sha256: 8540138e3e7ec90522bf32136dca753305c5a6d38cf32aa38e88452cc55a0dde + command_summary: /Users/justin/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/bin/node build/deployment/site-qa.mjs + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-41 + timestamp: "2026-10-09T13:30:01.348682000Z" + - evidence: Stable SMALL1.1.2 resolves retained QA capture without rewriting history. Updated root and writable docs-site CI pins, agent policy, canonical prose and changelogs; refreshed bundle sourcee0d04802 docs cb4c8adb. Root/site strict and cache-free snapshots pass; missing-proof snapshot fails as expected. CLI contract and docs-continuity tests pass; canonical site tests4/4 and Astro build pass. Exact QA proof SHA5f06e661 unchanged. No remote enforcement, deployment or publication claimed. + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: completed + task_id: task-41 + timestamp: "2026-10-09T13:30:59.128182000Z" + - evidence: Audit blocker cleared by verified installed stable SMALL1.1.2; task41 tooling/docs adoption completed. Continue paired commit/PR hosted CI, approved main protections, matching docs deployment/readback and release. Current bundle sourcee0d04802 docs cb4c8adb in build/deployment/small112-documentation and writable docs clone. Prior packages were built against older source34f5732e; rebuild before publication. No global quality score, receipt rewrite, shared allowance reset, protocol migration or production changes. + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-09T13:30:59.484953000Z" + - evidence: Updated task task-36 to pending + notes: small plan --pending + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: pending + task_id: task-36 + timestamp: "2026-10-09T13:38:09.401327000Z" + - evidence: Resuming after task41 completed stable SMALL1.1.2 CI/prose adoption and the originating agent became idle. Default shell CLI now resolves verified v1.1.2; root and docs strict pass. Prepare isolated, reviewable paired PRs from the existing release changes, run hosted CI, retain original dirty checkouts and all audit/proof bytes. GitHub access includes admin/push for both repos; core main has no protection and private docs protection is rejected by GitHub account-plan HTTP403. No production deployment, repository visibility change, paid plan purchase, merge or publication is part of preparation. + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-09T13:38:22.348241000Z" + - command: python3 /private/tmp/prepare-loopexec-release-candidates.py + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T134432.636938000Z.txt + command_sha256: 4e5399b967b3d9e310726870a3c7d13c635efcda8a38aab7b7ca314a4c8953ba + command_summary: python3 /private/tmp/prepare-loopexec-release-candidates.py + evidence: + exit_code: 1 + kind: cli_execution + outcome: failed + strength: cli_captured + notes: 'apply: command outcome failed; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-09T13:44:32.636938000Z" + - command: python3 /private/tmp/prepare-loopexec-release-candidates.py + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T134619.761503000Z.txt + command_sha256: 4e5399b967b3d9e310726870a3c7d13c635efcda8a38aab7b7ca314a4c8953ba + command_summary: python3 /private/tmp/prepare-loopexec-release-candidates.py + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-09T13:46:19.761503000Z" + - command: python3 /private/tmp/verify-loopexec-release-core.py + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T135148.983325000Z.txt + command_sha256: 8b7368c61a57fa9c4de45a30eff91a9dec825defff2250306a35b78265f41c2b + command_summary: python3 /private/tmp/verify-loopexec-release-core.py + evidence: + exit_code: 1 + kind: cli_execution + outcome: failed + strength: cli_captured + notes: 'apply: command outcome failed; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-09T13:51:48.983325000Z" + - command: python3 /private/tmp/verify-loopexec-release-core.py + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T135501.491614000Z.txt + command_sha256: 8b7368c61a57fa9c4de45a30eff91a9dec825defff2250306a35b78265f41c2b + command_summary: python3 /private/tmp/verify-loopexec-release-core.py + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-09T13:55:01.491614000Z" + - command: python3 /private/tmp/loopexec-playwright-help.py + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T140126.031973000Z.txt + command_sha256: 98f07f284f4fd6ef46c93c3d23e279e196168f8ceb41cea7dfc700a81057bbd4 + command_summary: python3 /private/tmp/loopexec-playwright-help.py + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-09T14:01:26.031973000Z" + - command: python3 /private/tmp/loopexec-playwright-help.py + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T140514.660479000Z.txt + command_sha256: 98f07f284f4fd6ef46c93c3d23e279e196168f8ceb41cea7dfc700a81057bbd4 + command_summary: python3 /private/tmp/loopexec-playwright-help.py + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-09T14:05:14.660479000Z" + - command: python3 /private/tmp/qa-loopexec-docs-candidate.py + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T140938.811806000Z.txt + command_sha256: 61f1081fc7cd69205b21d74c473cb4af09c1be7ff0e82c17e439cc46d3a73f9a + command_summary: python3 /private/tmp/qa-loopexec-docs-candidate.py + evidence: + exit_code: 0 + kind: cli_execution + outcome: succeeded + strength: cli_captured + notes: 'apply: command outcome succeeded; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-09T14:09:38.811806000Z" + - evidence: Updated task task-37 to pending + notes: small plan --pending + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: pending + task_id: task-37 + timestamp: "2026-10-09T14:20:27.303642000Z" + - evidence: Prepared isolated candidate at /private/tmp/loopexec-v0.4.1-continuation-20261009/core. Full local CI, race checks, offline proofs and v1/v2 SMALL compatibility pass. Seven platform/documentation archives verify checksums; source e0d04802b2c1a0a58ed03216c3a76b6e56899fa7d5a045dd835973de2ebb51fd and docs cb4c8adb7f9656839d5210d0c03b9502ffc0c2268be806310564dc1ebd43255f match the canonical site import. Clean pnpm installation, scoped formatting, canonical tests, Astro build, twelve real desktop/mobile route checks and exact raw source verification pass. Next is offline redacted secret scanning and paired hosted PR/CI proof; no release tag or production publication yet. + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-37 + timestamp: "2026-10-09T14:20:28.783805000Z" + - command: python3 /private/tmp/commit-loopexec-release-candidates.py + command_ref: .small-cache/logs/f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96/commands/20261009T142109.461009000Z.txt + command_sha256: f1bcdae30ac504bdf32a7647ce4d19b0f365cba3b3510f3e3bbb5477512ce961 + command_summary: python3 /private/tmp/commit-loopexec-release-candidates.py + evidence: + exit_code: 1 + kind: cli_execution + outcome: failed + strength: cli_captured + notes: 'apply: command outcome failed; task acceptance unchanged' + replayId: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 + status: in_progress + task_id: task-36 + timestamp: "2026-10-09T14:21:09.461009000Z" diff --git a/.small/workspace.small.yml b/.small/workspace.small.yml index 2765fcd..1948ce8 100644 --- a/.small/workspace.small.yml +++ b/.small/workspace.small.yml @@ -2,6 +2,6 @@ small_version: "1.0.0" owner: agent kind: repo-root created_at: "2026-01-20T23:56:04Z" -updated_at: "2026-09-27T02:23:47.536891Z" +updated_at: "2026-10-09T14:21:09.527594Z" run: replay_id: f6bbda15d790df6d023dea340edd1b85ccac89c82db362652a1b656981e82d96 diff --git a/AGENTS.md b/AGENTS.md index a8f864b..c60095a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,13 +1,40 @@ # HARD RULES Never directly edit .env files +## Code and documentation continuity + +- LoopExec is a general-purpose bounded agentic loop governor. Project-specific + quality standards belong to adapters, not the runtime's product definition. +- Runtime, CLI, dependency, tooling, workflow or policy changes MUST include + matching canonical prose and CHANGELOG.md in the same change. Review behavior + against docs; a path-co-change check alone cannot prove semantic correctness. +- Regenerate the CLI contract for command/flag/default/help changes, never hand + edit it. Default tests MUST pass against the checked-in reference. +- Public documentation is allowlisted in docs/documentation-files.json. Add new + public guides/release notes there; never bundle private handoffs or state. +- Releases MUST package the matching source-bound documentation and pass deployed + docs readback before publication. Stale, missing or unreachable docs block + release; do not bypass the gate to ship code alone. +- Follow docs/documentation-continuity.md. Never call local gates enforced on + GitHub until committed workflows and required branch checks are verified live. +- Acceptance adjustments require an explicit owner decision and separate policy + evidence. Never rewrite original receipts, silently lower an ideal target, + reset shared allowances or launch more paid reviews to force convergence. + # SMALL Protocol Agent Guide This is the operating contract for any AI agent or human acting like one. Follow it exactly. If anything here conflicts with `small --help`, the CLI wins. SMALL governs state, not execution. The CLI is the only valid way to mutate `.small/`. -Target tooling: SMALL CLI 1.1.0 (supports protocol profiles 1.0.0 and 2.0.0). +Target tooling: SMALL CLI 1.1.2 (supports protocol profiles 1.0.0 and 2.0.0). +This stable release includes command-summary correction and narrow captured-code +loopback URL handling required by this checkout's historical receipts. Verify the +resolved binary; older releases do not validate the retained QA template capture. +Preserve existing receipts. Review and retain only required non-secret command +proof in `.small-command-proofs/`; never export `.small-cache/` wholesale. New +truncated captures also require exact-byte proof before cache-free validation. +Local validation does not update the CI pin or prove hosted release gates. This checkout remains on the v1 profile unless explicitly migrated through SMALL. Check the resolved binary with `small version`; never use an old v1-only CLI to write migrated v2 state. diff --git a/CHANGELOG.md b/CHANGELOG.md index 3090d1b..78cfe1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,46 @@ # Changelog +## Unreleased + +Future recipes and workflow adapters will remain configurable, opt-in integrations. + +## v0.4.1 + +See [release notes](docs/releases/v0.4.1.md). Publication status is authoritative +on [GitHub Releases](https://github.com/justyn-clark/loopexec/releases). + +- Adopted stable SMALL v1.1.2 in checksum-verified CI. Its narrowly scoped + captured-source validation resolves the retained local QA template capture. + Preserved historical + command receipts with individually reviewed portable proof; missing or tampered + proof fails closed. Local validation is not proof of hosted release readiness. +- Hardened code/docs continuity: canonical prose and changelog co-change CI gate, + generated CLI/JSON contract tests, source-bound allowlisted documentation in + every platform archive, and fail-closed deployed-docs checks before publication. + The separate docs site must adopt the build handshake before the next release. +- Added a non-publishing Release workflow rehearsal and a main-line ancestry gate + before deployed-docs verification or tag publication. +- Clarified general-purpose acceptance policy: original run contracts, candidate + retention, explicit owner adjustments and desired objectives remain separate. + No domain-specific score, automatic budget reset or model rerun was introduced. + +- Read-only reports expose the parsed event timeline, command phase/duration/cause, + failure cause, and missing or damaged receipt warnings. Existing report exit + behavior is unchanged; a readable receipt is not an integrity verification. +- Reports distinguish retained numeric candidates from convergence and signature + presence from verified attestation. The legacy JSON `attested` field still + describes file presence; use `attestation_status` and explicit verification. +- Added an evidence-capture guide for creative verification and a release/status + roadmap. These changes are local until a subsequent release is published. + +## v0.4.0 + +See [release notes](docs/releases/v0.4.0.md). Published September 26, 2026. + +Adds shared model-call budgets across run IDs, pinned total/per-phase allowances, +durable pre-call reservations, and an offline adapter/proof. A failed child still +consumes its slot. This counts calls; it does not replace token/dollar metering. + ## v0.3.0 See [release notes](docs/releases/v0.3.0.md) for capabilities, compatibility diff --git a/README.md b/README.md index 8d0a408..851aadc 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,11 @@ The normative contract is `SPEC.md`. The current source implements bounded execu ## Current status +Source identity: **v0.4.1**. This patch adds evidence-complete reports and enforced +code/docs release continuity. See [v0.4.1 notes](docs/releases/v0.4.1.md); the +[Releases page](https://github.com/justyn-clark/loopexec/releases) establishes +publication status. Workflow thresholds remain supplied by each integration. + Implemented through v0.4.0: - Real bounded `run` loop: iterates `--exec` then `--check` until the check passes (`success_condition_met`), a bound trips (`max_iterations_reached`), or work fails (`execution_failure`); "no check, no loop"; computed halt reasons mapped to a stable exit-code class; typed JSONL receipts (`.loopexec/run-.jsonl`) and atomic durable state. @@ -44,9 +49,12 @@ Not implemented yet (named sub-parts; see `SPEC.md` section 11): go install github.com/justyn-clark/loopexec/cmd/loopexec@latest ``` -Requires Go 1.26 or newer. For a pinned install, use `@v0.4.0`. Prebuilt archives and SHA256 checksums are available in the [v0.4.0 release](https://github.com/justyn-clark/loopexec/releases/tag/v0.4.0). +Requires Go 1.26 or newer. For a pinned v0.4.1 install, use `@v0.4.1` once that +release is published. Until then, v0.4.0 remains the prior published stable release. +Prebuilt archives and SHA256 checksums are linked from +[GitHub Releases](https://github.com/justyn-clark/loopexec/releases). -`loopexec --version` prints the installed version; `loopexec version --json` emits its machine-readable identity. See the [release notes](docs/releases/v0.4.0.md) for compatibility and platform limits. +`loopexec --version` prints the installed version; `loopexec version --json` emits its machine-readable identity. See the [release notes](docs/releases/v0.4.1.md) for compatibility and platform limits. SMALL is optional for LoopExec consumers. This repository uses the tracked `.small/` files for its own development audit; CI requires a canonical progress update with source or documentation changes and validates the tracked state. The [SMALL integration guide](docs/small-integration.md) covers SMALL CLI v1.1.0 with legacy v1 artifacts and opt-in collaborative v2 sessions; LoopExec remains the loop governor. @@ -82,6 +90,12 @@ CI enforces: - `go vet ./...` - `go test ./...` +Code/tooling changes also require canonical documentation and changelog updates. +The generated [CLI and JSON contract](docs/cli-reference.generated.md) is checked +against the runtime. Every release packages source-bound docs and publication +requires matching deployed content. See [continuity policy](docs/documentation-continuity.md) +for enforcement and the pending website rollout boundary. + ## CLI The implemented CLI returns deterministic human or JSON output. @@ -97,6 +111,12 @@ The implemented CLI returns deterministic human or JSON output. See `docs/cli.md` for flags and exit semantics. The opt-in scoped budget reserves a child model call before an adapter launches it; changing the run ID does not reset the allowance. It counts calls, not tokens or dollars. +See [acceptance and evidence](docs/acceptance-policy.md) for general-purpose +objective, retention, convergence and owner-decision boundaries. The +[creative verification guide](docs/creative-verification.md) is an optional example. +The [status and roadmap](docs/status-and-roadmap.md) distinguishes published +capabilities, local improvements, and remaining work. + ### Global flag - `--json` - emit exactly one JSON object to stdout diff --git a/SPEC.md b/SPEC.md index ae6cdb0..f7c0f02 100644 --- a/SPEC.md +++ b/SPEC.md @@ -244,3 +244,22 @@ policy, controller state and critic evidence inaccessible for modification. The exact workflow schemas, comparison boundaries, restart behavior, trust boundary, and offline commands are normative in [docs/workflows.md](docs/workflows.md). + +## 13. Documentation and acceptance continuity + +Implementation changes MUST ship with matching canonical documentation and release +notes. CLI command/flag/default/help and JSON field contracts MUST pass the generated +reference comparison. Release archives MUST include their source-bound public docs +snapshot. Automated publication MUST fail closed until the official docs site serves +the matching manifest, source files and homepage build marker. Semantic review and +account-level release protections remain required; automated provenance is not a +claim that every sentence or rendered page is correct. See the normative +[continuity policy](docs/documentation-continuity.md) for the gates and rollout limits. + +LoopExec is a general-purpose governor. Domain-specific targets and oracle policies +belong to integrations. Candidate retention, convergence under the recorded run +contract, explicit human acceptance and publication MUST remain distinct. Owner +adjustments MUST preserve original receipts and desired objectives, with separate +decision evidence; they MUST NOT silently reset budgets or authorize new work. +See [acceptance policy](docs/acceptance-policy.md). Native cross-run decision history +is not implemented; these records currently belong to the adapter/operator. diff --git a/cmd/loopexec/documentation_test.go b/cmd/loopexec/documentation_test.go new file mode 100644 index 0000000..b360a32 --- /dev/null +++ b/cmd/loopexec/documentation_test.go @@ -0,0 +1,131 @@ +package main + +import ( + "bytes" + "flag" + "fmt" + "os" + "path/filepath" + "reflect" + "sort" + "strings" + "testing" + + "github.com/spf13/cobra" + "github.com/spf13/pflag" +) + +var updateDocumentation = flag.Bool("update-docs", false, "Regenerate the checked-in CLI reference") + +func documentationContract(root *cobra.Command) []byte { + var out bytes.Buffer + fmt.Fprintf(&out, "# Generated CLI reference\n\nGenerated from the Cobra command tree. Do not edit manually.\n\nSource version: `%s`.\n\n", root.Version) + var visit func(*cobra.Command) + visit = func(cmd *cobra.Command) { + if cmd.Hidden { + return + } + cmd.InitDefaultHelpFlag() + cmd.InitDefaultVersionFlag() + fmt.Fprintf(&out, "## `%s`\n\n%s\n\nUsage: `%s`\n\n", cmd.CommandPath(), cmd.Short, cmd.UseLine()) + if cmd.Long != "" { + fmt.Fprintf(&out, "%s\n\n", strings.TrimSpace(cmd.Long)) + } + if len(cmd.Aliases) > 0 { + fmt.Fprintf(&out, "Aliases: %s\n\n", strings.Join(cmd.Aliases, ", ")) + } + flags := pflag.NewFlagSet(cmd.Name(), pflag.ContinueOnError) + flags.AddFlagSet(cmd.LocalNonPersistentFlags()) + flags.AddFlagSet(cmd.PersistentFlags()) + flags.AddFlagSet(cmd.InheritedFlags()) + flags.VisitAll(func(f *pflag.Flag) { + if f.Hidden { + return + } + name := "--" + f.Name + if f.Shorthand != "" { + name += ", -" + f.Shorthand + } + fmt.Fprintf(&out, "- `%s` (`%s`, default `%s`): %s", name, f.Value.Type(), f.DefValue, f.Usage) + if f.Deprecated != "" { + fmt.Fprintf(&out, " Deprecated: %s", f.Deprecated) + } + fmt.Fprintln(&out) + }) + fmt.Fprintln(&out) + children := cmd.Commands() + sort.Slice(children, func(i, j int) bool { return children[i].Name() < children[j].Name() }) + for _, child := range children { + visit(child) + } + } + visit(root) + fmt.Fprintln(&out, "## JSON field contract\n\nTypes and JSON tags are generated from the runtime structs. Value semantics and exit behavior are described in cli.md and SPEC.md.") + queue := []reflect.Type{reflect.TypeOf(response{}), reflect.TypeOf(receiptEvent{})} + seen := map[reflect.Type]bool{} + var enqueue func(reflect.Type) + enqueue = func(typ reflect.Type) { + switch typ.Kind() { + case reflect.Pointer, reflect.Slice, reflect.Array, reflect.Map: + enqueue(typ.Elem()) + case reflect.Struct: + if typ.PkgPath() == reflect.TypeOf(response{}).PkgPath() { + queue = append(queue, typ) + } + } + } + for len(queue) > 0 { + typ := queue[0] + queue = queue[1:] + if seen[typ] { + continue + } + seen[typ] = true + fmt.Fprintf(&out, "\n### `%s`\n\n", typ.Name()) + for i := 0; i < typ.NumField(); i++ { + field := typ.Field(i) + tag := field.Tag.Get("json") + if field.PkgPath != "" || tag == "-" { + continue + } + fmt.Fprintf(&out, "- `%s`: `%s`\n", tag, field.Type) + enqueue(field.Type) + } + } + return out.Bytes() +} + +func TestDocumentationContract(t *testing.T) { + path := filepath.Join("..", "..", "docs", "cli-reference.generated.md") + actual := documentationContract(newRootCmd()) + if *updateDocumentation { + if err := os.WriteFile(path, actual, 0644); err != nil { + t.Fatal(err) + } + } + expected, err := os.ReadFile(path) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(actual, expected) { + t.Fatal("CLI documentation drift: update prose as needed, then run go test ./cmd/loopexec -run '^TestDocumentationContract$' -args -update-docs") + } +} + +func TestDocumentationContractDetectsChanges(t *testing.T) { + baseline := documentationContract(newRootCmd()) + for name, mutate := range map[string]func(*cobra.Command){ + "command": func(c *cobra.Command) { c.AddCommand(&cobra.Command{Use: "undocumented", Short: "New behavior"}) }, + "flag": func(c *cobra.Command) { c.PersistentFlags().Bool("undocumented", false, "New flag") }, + "default": func(c *cobra.Command) { c.PersistentFlags().Lookup("json").DefValue = "true" }, + "description": func(c *cobra.Command) { c.Short = "Changed contract" }, + } { + t.Run(name, func(t *testing.T) { + root := newRootCmd() + mutate(root) + if bytes.Equal(baseline, documentationContract(root)) { + t.Fatal("contract change escaped documentation comparison") + } + }) + } +} diff --git a/cmd/loopexec/main.go b/cmd/loopexec/main.go index dd2a0df..676de8a 100644 --- a/cmd/loopexec/main.go +++ b/cmd/loopexec/main.go @@ -16,7 +16,7 @@ import ( const ( toolName = "loopexec" - toolVersion = "0.4.0" + toolVersion = "0.4.1" ) // Exit-code classes are the coarse CI-branch buckets defined in SPEC.md section 5. diff --git a/cmd/loopexec/report.go b/cmd/loopexec/report.go index 3592ffd..9c6522e 100644 --- a/cmd/loopexec/report.go +++ b/cmd/loopexec/report.go @@ -2,6 +2,7 @@ package main import ( "bufio" + "bytes" "encoding/json" "fmt" "os" @@ -15,49 +16,82 @@ import ( // nothing (unlike replay / reexecute): it reads the durable state plus the // typed JSONL event log and presents them. It is the read-only audit view. type reportSummary struct { - Budget *budgetBook `json:"budget,omitempty"` - Numeric *numericState `json:"numeric,omitempty"` - Candidate *candidateState `json:"candidate,omitempty"` - BestCandidate string `json:"best_candidate,omitempty"` - Phase string `json:"phase"` - ExitCode int `json:"exit_code"` - Iterations int `json:"iterations"` - Check string `json:"check,omitempty"` - Workdir string `json:"workdir,omitempty"` - CostUSD float64 `json:"cost_usd,omitempty"` - Model *modelPin `json:"model,omitempty"` - Sampling *samplingPin `json:"sampling,omitempty"` - ContextFiles int `json:"context_files,omitempty"` - Fingerprint *checkFingerprint `json:"fingerprint,omitempty"` - Events int `json:"events"` - Receipt string `json:"receipt,omitempty"` - Attested bool `json:"attested"` + Budget *budgetBook `json:"budget,omitempty"` + Numeric *numericState `json:"numeric,omitempty"` + Candidate *candidateState `json:"candidate,omitempty"` + BestCandidate string `json:"best_candidate,omitempty"` + Phase string `json:"phase"` + ExitCode int `json:"exit_code"` + Iterations int `json:"iterations"` + Check string `json:"check,omitempty"` + Workdir string `json:"workdir,omitempty"` + CostUSD float64 `json:"cost_usd,omitempty"` + Model *modelPin `json:"model,omitempty"` + Sampling *samplingPin `json:"sampling,omitempty"` + ContextFiles int `json:"context_files,omitempty"` + Fingerprint *checkFingerprint `json:"fingerprint,omitempty"` + Events int `json:"events"` + Receipt string `json:"receipt,omitempty"` + ReceiptStatus string `json:"receipt_status"` + Warnings []string `json:"warnings"` + Timeline []receiptEvent `json:"timeline"` + ScoreTarget *float64 `json:"score_target,omitempty"` + AttestationStatus string `json:"attestation_status"` + // Attested preserves the legacy signature-file-presence flag, not verification. + Attested bool `json:"attested"` } -// readReceiptEvents parses the typed JSONL receipt. A missing receipt is not -// fatal (report still renders the recorded state); an unparseable line is -// skipped rather than failing the whole report. -func readReceiptEvents(path string) ([]receiptEvent, error) { +// Keep readable evidence from a damaged receipt, but never conceal the gaps. +// "readable" describes parsing only; it is not an integrity verification. +func readReceiptEvents(path, runID string) ([]receiptEvent, []string, string) { + evs := []receiptEvent{} + warnings := []string{} f, err := os.Open(path) if err != nil { - return nil, err + status := "unreadable" + if os.IsNotExist(err) { + status = "missing" + } + return evs, []string{fmt.Sprintf("receipt %s; timeline unavailable: %v", status, err)}, status } defer f.Close() - var evs []receiptEvent + info, err := f.Stat() + if err != nil || !info.Mode().IsRegular() { + return evs, []string{"receipt is not a readable regular file; timeline unavailable"}, "unreadable" + } + invalid, lineNumber := 0, 0 sc := bufio.NewScanner(f) sc.Buffer(make([]byte, 0, 64*1024), 4*1024*1024) for sc.Scan() { + lineNumber++ line := sc.Bytes() - if len(line) == 0 { + if len(bytes.TrimSpace(line)) == 0 { continue } var e receiptEvent - if err := json.Unmarshal(line, &e); err != nil { + if err := json.Unmarshal(line, &e); err != nil || e.RunID != runID || strings.TrimSpace(e.Event) == "" { + invalid++ + if invalid <= 16 { + warnings = append(warnings, fmt.Sprintf("receipt line %d is malformed or has an invalid event/run identity; excluded from timeline", lineNumber)) + } continue } evs = append(evs, e) } - return evs, sc.Err() + if invalid > 16 { + warnings = append(warnings, fmt.Sprintf("%d additional invalid receipt lines excluded", invalid-16)) + } + if err := sc.Err(); err != nil { + warnings = append(warnings, fmt.Sprintf("receipt read stopped after line %d: %v", lineNumber, err)) + } + if len(evs) == 0 { + warnings = append(warnings, "receipt contains no readable events") + } + status := "readable" + if len(warnings) > 0 { + status = "incomplete" + } + return evs, warnings, status } // newReportCmd renders a recorded run: its outcome, its receipt pins, and the @@ -83,39 +117,55 @@ func newReportCmd() *cobra.Command { } receiptPath := filepath.Join(workdir, ".loopexec", "run-"+st.RunID+".jsonl") - events, _ := readReceiptEvents(receiptPath) + events, warnings, receiptStatus := readReceiptEvents(receiptPath, st.RunID) displayReceipt := receiptPath if _, statErr := os.Stat(receiptPath); statErr != nil { displayReceipt = "" } - _, attestErr := os.Stat(filepath.Join(workdir, ".loopexec", "attest-"+st.RunID+".sig")) + attestInfo, attestErr := os.Stat(filepath.Join(workdir, ".loopexec", "attest-"+st.RunID+".sig")) + attestationStatus := "absent" + if attestErr == nil && attestInfo.Mode().IsRegular() { + attestationStatus = "present_unverified" + } else if !os.IsNotExist(attestErr) { + attestationStatus = "unreadable" + warnings = append(warnings, "attestation is not a readable signature file") + } sum := &reportSummary{Budget: st.Budget, Numeric: st.Numeric, Candidate: st.Candidate, BestCandidate: st.BestCandidatePath, - Phase: st.Phase, - ExitCode: haltExitCode(st.HaltReason), - Iterations: st.Iteration, - Check: st.Check, - Workdir: st.Workdir, - CostUSD: st.CostUSD, - Model: st.Model, - Sampling: st.Sampling, - ContextFiles: len(st.ContextManifest), - Fingerprint: st.Fingerprint, - Events: len(events), - Receipt: displayReceipt, - Attested: attestErr == nil, + Phase: st.Phase, + ExitCode: haltExitCode(st.HaltReason), + Iterations: st.Iteration, + Check: st.Check, + Workdir: st.Workdir, + CostUSD: st.CostUSD, + Model: st.Model, + Sampling: st.Sampling, + ContextFiles: len(st.ContextManifest), + Fingerprint: st.Fingerprint, + Events: len(events), + Receipt: displayReceipt, + ReceiptStatus: receiptStatus, + Warnings: warnings, + Timeline: events, + AttestationStatus: attestationStatus, + Attested: attestErr == nil, + } + if st.Workflow != nil && st.Workflow.Score != nil { + target := st.Workflow.Score.Target + sum.ScoreTarget = &target } r := response{ - Tool: toolName, - Version: toolVersion, - Status: "ok", - RunID: st.RunID, - Iteration: st.Iteration, - HaltReason: st.HaltReason, - Receipt: displayReceipt, - Report: sum, - Errors: []string{}, + FailureCause: st.FailureCause, + Tool: toolName, + Version: toolVersion, + Status: "ok", + RunID: st.RunID, + Iteration: st.Iteration, + HaltReason: st.HaltReason, + Receipt: displayReceipt, + Report: sum, + Errors: []string{}, } if jsonOutput { return printResponse(cmd, r) @@ -140,6 +190,9 @@ func renderReport(cmd *cobra.Command, st loopState, sum *reportSummary, events [ halt = "(none)" } fmt.Fprintf(w, "phase: %s (%s, exit %d)\n", st.Phase, halt, sum.ExitCode) + if st.FailureCause != "" { + fmt.Fprintf(w, "failure cause: %s\n", st.FailureCause) + } fmt.Fprintf(w, "iterations: %d\n", st.Iteration) if st.Check != "" { fmt.Fprintf(w, "check: %s\n", st.Check) @@ -163,6 +216,21 @@ func renderReport(cmd *cobra.Command, st loopState, sum *reportSummary, events [ if st.BestCandidatePath != "" { fmt.Fprintf(w, "best candidate: %s\n", st.BestCandidatePath) } + if sum.Numeric != nil { + if sum.Numeric.Best != nil { + fmt.Fprintf(w, "best retained score (this run): %.3f\n", *sum.Numeric.Best) + } + if sum.ScoreTarget != nil { + fmt.Fprintf(w, "score target: %.3f\n", *sum.ScoreTarget) + } + fmt.Fprintf(w, "reviews: %d; reviews since improvement: %d\n", sum.Numeric.Reviewed, sum.Numeric.Stale) + } + if sum.Candidate != nil { + fmt.Fprintf(w, "candidate retention: %s\n", sum.Candidate.Status) + if st.HaltReason != "success_condition_met" { + fmt.Fprintln(w, "acceptance: run did not converge; candidate retention does not establish acceptance") + } + } if st.Model != nil { fmt.Fprintf(w, "model: %s/%s %s\n", st.Model.Provider, st.Model.ID, st.Model.Version) @@ -170,7 +238,15 @@ func renderReport(cmd *cobra.Command, st loopState, sum *reportSummary, events [ if sum.ContextFiles > 0 { fmt.Fprintf(w, "context files: %d\n", sum.ContextFiles) } - fmt.Fprintf(w, "attested: %s\n", yesNo(sum.Attested)) + attestation := sum.AttestationStatus + if attestation == "present_unverified" { + attestation = "signature present (not verified)" + } + fmt.Fprintf(w, "attestation: %s\n", attestation) + fmt.Fprintf(w, "receipt status: %s (parsing only; not integrity verification)\n", sum.ReceiptStatus) + for _, warning := range sum.Warnings { + fmt.Fprintf(w, "warning: %s\n", warning) + } if sum.Receipt == "" { fmt.Fprintln(w, "receipt: (none on disk)") @@ -180,10 +256,22 @@ func renderReport(cmd *cobra.Command, st loopState, sum *reportSummary, events [ fmt.Fprintln(w, "\ntimeline:") for _, e := range events { line := fmt.Sprintf(" [iter %d] %-10s", e.Iteration, e.Event) + if e.Phase != "" { + line += " phase=" + e.Phase + } if e.ExitCode != nil { line += fmt.Sprintf(" exit %d", *e.ExitCode) } - if e.Event == "halt" && e.Detail != "" { + if e.Event != "command_start" && strings.HasPrefix(e.Event, "command_") { + line += fmt.Sprintf(" duration=%dms", e.DurationMS) + } + if e.Cause != "" { + line += " cause=" + e.Cause + } + if e.OutputTruncated { + line += " output_truncated=true" + } + if e.Detail != "" { line += " " + e.Detail } fmt.Fprintln(w, strings.TrimRight(line, " ")) @@ -196,10 +284,3 @@ func shortHash(h string) string { } return h } - -func yesNo(b bool) string { - if b { - return "yes" - } - return "no" -} diff --git a/cmd/loopexec/report_test.go b/cmd/loopexec/report_test.go index c4ab39b..a2560a9 100644 --- a/cmd/loopexec/report_test.go +++ b/cmd/loopexec/report_test.go @@ -1,20 +1,30 @@ package main import ( + "bytes" "encoding/json" + "os" + "path/filepath" "strings" "testing" + + "github.com/spf13/cobra" ) type reportJSON struct { - RunID string `json:"run_id"` - Report *struct { - Phase string `json:"phase"` - ExitCode int `json:"exit_code"` - Iterations int `json:"iterations"` - Check string `json:"check"` - Events int `json:"events"` - Fingerprint *struct { + RunID string `json:"run_id"` + FailureCause string `json:"failure_cause"` + Report *struct { + Phase string `json:"phase"` + ExitCode int `json:"exit_code"` + Iterations int `json:"iterations"` + Check string `json:"check"` + Events int `json:"events"` + ReceiptStatus string `json:"receipt_status"` + Warnings []string `json:"warnings"` + Timeline []receiptEvent `json:"timeline"` + AttestationStatus string `json:"attestation_status"` + Fingerprint *struct { ExitCode int `json:"exit_code"` } `json:"fingerprint"` } `json:"report"` @@ -49,6 +59,9 @@ func TestReportRendersRecordedRun(t *testing.T) { if r.Report.Events == 0 { t.Fatal("report should count receipt events") } + if r.Report.ReceiptStatus != "readable" || len(r.Report.Warnings) != 0 || r.Report.Events != len(r.Report.Timeline) { + t.Fatalf("healthy receipt incorrectly summarized: %s", out) + } if r.Report.Fingerprint == nil { t.Fatal("report should surface the check fingerprint") } @@ -93,3 +106,135 @@ func TestReportNoStateErrors(t *testing.T) { t.Fatalf("report with no state exit = %d, want 30", code) } } + +func TestReportWarnsOnDamagedReceipt(t *testing.T) { + bin := buildTestBinary(t) + for _, tc := range []struct { + name, data, status string + missing bool + }{ + {name: "malformed", data: "{broken\n", status: "incomplete"}, + {name: "null", data: "null\n", status: "incomplete"}, + {name: "empty object", data: "{}\n", status: "incomplete"}, + {name: "wrong run", data: `{"run_id":"other","event":"halt"}` + "\n", status: "incomplete"}, + {name: "missing", missing: true, status: "missing"}, + {name: "empty", status: "incomplete"}, + } { + t.Run(tc.name, func(t *testing.T) { + dir := t.TempDir() + runCLI(t, bin, "run", "--json", "--run-id", "r", "--workdir", dir, "--check", "true") + path := filepath.Join(dir, ".loopexec", "run-r.jsonl") + if tc.missing { + if err := os.Remove(path); err != nil { + t.Fatal(err) + } + } else { + data := tc.data + if data != "" { + data = `{"run_id":"r","iteration":1,"event":"check","exit_code":0}` + "\n" + data + } + if err := os.WriteFile(path, []byte(data), 0600); err != nil { + t.Fatal(err) + } + } + code, out, errOut := runCLI(t, bin, "report", "--json", "--workdir", dir) + if code != 0 { + t.Fatalf("report exit %d: %s", code, errOut) + } + r := parseReportJSON(t, out) + if r.Report.ReceiptStatus != tc.status || len(r.Report.Warnings) == 0 { + t.Fatalf("damaged receipt hidden: %s", out) + } + if tc.data != "" && (len(r.Report.Timeline) != 1 || r.Report.Timeline[0].Event != "check") { + t.Fatalf("valid evidence not retained separately: %s", out) + } + _, human, _ := runCLI(t, bin, "report", "--workdir", dir) + if !strings.Contains(human, "warning:") { + t.Fatalf("human report hides warning: %s", human) + } + }) + } +} + +func TestReportIncludesFailureEvidence(t *testing.T) { + bin := buildTestBinary(t) + dir := t.TempDir() + code, _, _ := runCLI(t, bin, "run", "--json", "--run-id", "timed", "--workdir", dir, + "--exec", "sleep 2", "--check", "true", "--command-timeout", "40ms", "--terminate-grace", "10ms") + if code != 40 { + t.Fatalf("timeout exit %d, want 40", code) + } + code, out, errOut := runCLI(t, bin, "report", "--json", "--workdir", dir) + if code != 0 { + t.Fatalf("report exit %d: %s", code, errOut) + } + r := parseReportJSON(t, out) + if r.FailureCause == "" || r.Report.ReceiptStatus != "readable" || r.Report.Events != len(r.Report.Timeline) { + t.Fatalf("failure context missing: %s", out) + } + found := false + for _, event := range r.Report.Timeline { + if event.Event == "command_timeout" && event.Phase == "exec" && event.Cause == "deadline_exceeded" && event.ExitCode != nil { + found = true + } + } + if !found { + t.Fatalf("timeout evidence missing: %s", out) + } + _, human, _ := runCLI(t, bin, "report", "--workdir", dir) + for _, want := range []string{"failure cause:", "exec", "deadline_exceeded", "ms", "attestation: absent"} { + if !strings.Contains(human, want) { + t.Fatalf("human report missing %q: %s", want, human) + } + } +} + +func TestReportDoesNotClaimSignatureVerification(t *testing.T) { + bin := buildTestBinary(t) + dir := t.TempDir() + runCLI(t, bin, "run", "--json", "--run-id", "r", "--workdir", dir, "--check", "true") + if err := os.WriteFile(filepath.Join(dir, ".loopexec", "attest-r.sig"), []byte("not a valid signature"), 0600); err != nil { + t.Fatal(err) + } + _, out, _ := runCLI(t, bin, "report", "--json", "--workdir", dir) + if r := parseReportJSON(t, out); r.Report.AttestationStatus != "present_unverified" { + t.Fatalf("signature status missing: %s", out) + } + _, human, _ := runCLI(t, bin, "report", "--workdir", dir) + if !strings.Contains(human, "signature present (not verified)") || strings.Contains(human, "attested: yes") { + t.Fatalf("report implies verification: %s", human) + } +} + +func TestReportReceiptReadFailurePreservesEarlierEvents(t *testing.T) { + path := filepath.Join(t.TempDir(), "receipt.jsonl") + data := `{"run_id":"r","event":"check"}` + "\n" + strings.Repeat("x", 4*1024*1024) + "\n" + if err := os.WriteFile(path, []byte(data), 0600); err != nil { + t.Fatal(err) + } + events, warnings, status := readReceiptEvents(path, "r") + if status != "incomplete" || len(events) != 1 || len(warnings) != 1 || !strings.Contains(warnings[0], "read stopped") { + t.Fatalf("read failure hidden: events=%d warnings=%v status=%s", len(events), warnings, status) + } + events, warnings, status = readReceiptEvents(t.TempDir(), "r") + if status != "unreadable" || len(events) != 0 || len(warnings) == 0 { + t.Fatalf("nonregular receipt accepted: %s %v", status, warnings) + } +} + +func TestReportHumanShowsRetentionAndCommandDiagnostics(t *testing.T) { + var out bytes.Buffer + cmd := &cobra.Command{} + cmd.SetOut(&out) + score, target := 8.1, 8.5 + exit := 1 + st := loopState{RunID: "r", Phase: "halted", HaltReason: "max_iterations_reached"} + sum := &reportSummary{ExitCode: 12, Receipt: "receipt.jsonl", ReceiptStatus: "readable", AttestationStatus: "absent", + Numeric: &numericState{Best: &score, Reviewed: 1}, ScoreTarget: &target, Candidate: &candidateState{Status: "accepted"}} + renderReport(cmd, st, sum, []receiptEvent{{Event: "command_end", Phase: "check", ExitCode: &exit, DurationMS: 123, Cause: "nonzero_exit", OutputTruncated: true}}) + for _, want := range []string{"best retained score (this run): 8.100", "score target: 8.500", "candidate retention: accepted", "run did not converge", "candidate retention does not establish acceptance", "phase=check", "duration=123ms", "cause=nonzero_exit", "output_truncated=true"} { + if !strings.Contains(out.String(), want) { + t.Fatalf("missing %q: %s", want, out.String()) + } + } +} diff --git a/docs/acceptance-policy.md b/docs/acceptance-policy.md new file mode 100644 index 0000000..51efbac --- /dev/null +++ b/docs/acceptance-policy.md @@ -0,0 +1,61 @@ +# Acceptance, objectives and evidence + +LoopExec governs bounded execution, computed stops, accounting and retention. +The integration defines its objective and protected external checks. Software, +research, asset creation and operational workflows can use different criteria +without changing the governor's meaning. + +## Separate the decisions + +- Desired objective: the quality or behavior the owner ultimately wants. +- Recorded run contract: the exact checks, target, rubric and bounds for that run. +- Candidate retention: keeping a best verified intermediate result. +- Computed convergence: all configured gates passed for the recorded contract. +- Human acceptance: an explicit owner's decision, including temporary exceptions. +- Promotion/publication: a separate action bound to the accepted candidate digest. + +An owner may deliberately accept an intermediate result while retaining a higher +desired objective. Record who decided, the candidate digest, original and revised +policy, reason, evidence and remaining gaps. Never relabel an earlier failed run +as converged or overwrite its immutable receipt. A score from an older candidate +does not apply to later edits, and a human adjustment is not a model verdict. + +## Improve without weakening the desired quality + +Freeze references, rubric and evidence conditions before comparing candidates. +Keep protected functional, safety and integrity constraints as vetoes, not +averages that a high subjective score can hide. Break a composite score into +criterion-level gaps with observable correction and verification requirements. + +For each next intervention, record the strongest failing criterion, its evidence, +one proposed change, expected observable improvement and regression check. Prefer +cheap deterministic measurements before another independent model review. Keep +contradictory feedback and uncertain judgments explicit; calibrate the oracle +against labeled examples before asking it to drive more work. + +Separate artifact defects from measurement defects. If repeated attempts produce +no measurable gain, halt and diagnose capture conditions, reference suitability, +rubric consistency, adapter effectiveness and feasibility. Increasing patience +without changing the intervention does not supply new evidence. Do not silently +lower the objective to make the counter turn green. + +Bind comparisons to objective ID, candidate digest, policy/rubric revision, +reference/capture configuration, checks and model identity. Preserve an earlier +better candidate with its evidence; explicitly decide which revision to promote. +Any new allowance or policy revision requires owner authorization, retains the +original spend ledger and records the change. Changing run IDs is not permission +to reset budgets or relaunch paid work. + +## Current capabilities and remaining gap + +Published LoopExec provides per-run receipts, replay, bounds, numeric patience, +candidate retention/restoration and opt-in shared model-call reservations across +runs. The local report patch makes evidence gaps and retention clearer. The +adapter remains responsible for capturing full evidence and human decisions. + +A native cross-run objective/candidate/policy/decision history is not implemented. +Until it exists, retain a project-owned append-only decision record outside run +workspaces and join it to immutable receipts by IDs/digests. Do not promise that +the current governor automatically reconciles changed rubrics or owner decisions. +The [creative verification guide](creative-verification.md) is one worked domain +example, not a universal score or certification policy. diff --git a/docs/cli-reference.generated.md b/docs/cli-reference.generated.md new file mode 100644 index 0000000..9975523 --- /dev/null +++ b/docs/cli-reference.generated.md @@ -0,0 +1,518 @@ +# Generated CLI reference + +Generated from the Cobra command tree. Do not edit manually. + +Source version: `0.4.1`. + +## `loopexec` + +loopexec - deterministic runtime for loop engineering + +Usage: `loopexec [flags]` + +- `--help, -h` (`bool`, default `false`): help for loopexec +- `--json` (`bool`, default `false`): Emit machine-readable JSON output +- `--version, -v` (`bool`, default `false`): version for loopexec + +## `loopexec ack` + +Clear comprehension debt and any paged escalation (records the reviewer) + +Usage: `loopexec ack [flags]` + +- `--help, -h` (`bool`, default `false`): help for ack +- `--json` (`bool`, default `false`): Emit machine-readable JSON output +- `--reviewer` (`string`, default ``): Reviewer identity recorded in the ack (required) +- `--workdir` (`string`, default ``): Directory containing .loopexec/state.json (default: current directory) + +## `loopexec attest` + +Sign the receipt (HMAC) so provenance is verifiable; --verify to check a signature + +Usage: `loopexec attest [flags]` + +- `--help, -h` (`bool`, default `false`): help for attest +- `--json` (`bool`, default `false`): Emit machine-readable JSON output +- `--key` (`string`, default ``): Attestation key (else $LOOPEXEC_ATTEST_KEY, else a dev default) +- `--run-id` (`string`, default ``): Attest a specific recorded run by id (default: the latest run) +- `--verify` (`bool`, default `false`): Verify the stored signature instead of creating one +- `--workdir` (`string`, default ``): Directory containing .loopexec (default: current directory) + +## `loopexec budget` + +Shared call budgets across independent run IDs + +Usage: `loopexec budget [flags]` + +- `--help, -h` (`bool`, default `false`): help for budget +- `--json` (`bool`, default `false`): Emit machine-readable JSON output +- `--policy` (`string`, default ``): Pinned scoped budget policy JSON +- `--store-dir` (`string`, default ``): Existing absolute directory shared by all runs in this budget scope + +## `loopexec budget reserve` + +Consume one model-call slot before launching a child + +Usage: `loopexec budget reserve [flags]` + +- `--call-id` (`string`, default ``): Unique identity for this logical call; duplicates fail closed +- `--help, -h` (`bool`, default `false`): help for reserve +- `--json` (`bool`, default `false`): Emit machine-readable JSON output +- `--phase` (`string`, default ``): Budget phase, such as builder or critic +- `--policy` (`string`, default ``): Pinned scoped budget policy JSON +- `--store-dir` (`string`, default ``): Existing absolute directory shared by all runs in this budget scope + +## `loopexec budget status` + +Read remaining scoped model-call allowance + +Usage: `loopexec budget status [flags]` + +- `--help, -h` (`bool`, default `false`): help for status +- `--json` (`bool`, default `false`): Emit machine-readable JSON output +- `--policy` (`string`, default ``): Pinned scoped budget policy JSON +- `--store-dir` (`string`, default ``): Existing absolute directory shared by all runs in this budget scope + +## `loopexec build-context` + +Assemble a narrow, budgeted context slice (state + failure + relevant files) + +Usage: `loopexec build-context [flags]` + +- `--budget-tokens` (`int`, default `8000`): Context token ceiling (code-calibrated ~3.3 chars/token) +- `--diff-base` (`string`, default `HEAD`): Git ref for the last-diff relevance signal +- `--failure` (`string`, default ``): Failure / stack-trace source: a file path, '-' for stdin, or inline text +- `--help, -h` (`bool`, default `false`): help for build-context +- `--json` (`bool`, default `false`): Emit machine-readable JSON output +- `--out` (`string`, default ``): Output path within --workdir (default: /.loopexec/context.md) +- `--strategy` (`string`, default `stacktrace_last_diff`): Relevance strategy (import_closure / dep_graph are Planned) +- `--workdir` (`string`, default ``): Directory to resolve files against (default: current directory) + +## `loopexec check` + +Validate loop invariants (state hygiene, not the application oracle) + +Usage: `loopexec check [flags]` + +- `--fail-invariant` (`bool`, default `false`): Force invariant failure +- `--help, -h` (`bool`, default `false`): help for check +- `--json` (`bool`, default `false`): Emit machine-readable JSON output + +## `loopexec demo` + +Prove execute -> external check -> receipt -> replay without credentials + +Usage: `loopexec demo [flags]` + +- `--help, -h` (`bool`, default `false`): help for demo +- `--json` (`bool`, default `false`): Emit machine-readable JSON output +- `--workdir` (`string`, default ``): Fresh directory for durable demo artifacts (default: create and retain a temporary directory) + +## `loopexec doctor` + +Gate loop preconditions: determinism + isolation preflight + adequacy canary (--mutate-cmd) + +Usage: `loopexec doctor [flags]` + +- `--bind-model-home` (`bool`, default `false`): Declare a $HOME/.model-home credential bind-mount (fails the isolation preflight) +- `--check` (`string`, default ``): External check command to validate +- `--exec-network` (`string`, default ``): Declared exec-zone network policy; must be 'none' (SPEC section 7) +- `--help, -h` (`bool`, default `false`): help for doctor +- `--json` (`bool`, default `false`): Emit machine-readable JSON output +- `--max-flake-rate` (`float64`, default `0`): Target max flake rate to certify +- `--mutate-cmd` (`string`, default ``): Operator command that plants a mutation in the changed code; the --check MUST then turn red, else check_inadequate (SPEC O4) +- `--runs` (`int`, default `0`): Determinism probe runs (default: derived, else 10) +- `--workdir` (`string`, default ``): Directory to run the check in (default: current directory) + +## `loopexec escalate` + +Emit a structured escalation packet and mark the run paged (cleared by `ack`) + +Usage: `loopexec escalate [flags]` + +- `--channel` (`string`, default `file`): Escalation channel: file | stdout (github/slack are Planned) +- `--help, -h` (`bool`, default `false`): help for escalate +- `--json` (`bool`, default `false`): Emit machine-readable JSON output +- `--workdir` (`string`, default ``): Directory containing .loopexec/state.json (default: current directory) + +## `loopexec explain-halt` + +Explain why the recorded run halted (raise-the-limit vs do-not-retry) + +Usage: `loopexec explain-halt [flags]` + +- `--help, -h` (`bool`, default `false`): help for explain-halt +- `--json` (`bool`, default `false`): Emit machine-readable JSON output +- `--run-id` (`string`, default ``): Explain a specific recorded run by id (default: the latest run) +- `--workdir` (`string`, default ``): Directory containing .loopexec (default: current directory) + +## `loopexec init` + +Initialize loopexec workspace metadata + +Usage: `loopexec init [flags]` + +- `--help, -h` (`bool`, default `false`): help for init +- `--json` (`bool`, default `false`): Emit machine-readable JSON output + +## `loopexec inspect-cost` + +Analyze a per-iteration cost ledger against a budget cap and a sigma anomaly bound + +Usage: `loopexec inspect-cost [flags]` + +- `--budget-usd` (`float64`, default `0`): Run-total hard cap in USD (0 = no cap); over it halts budget_exceeded (18) +- `--cost` (`stringArray`, default `[]`): Per-iteration USD cost (repeatable); an alternative to --ledger +- `--help, -h` (`bool`, default `false`): help for inspect-cost +- `--json` (`bool`, default `false`): Emit machine-readable JSON output +- `--ledger` (`string`, default ``): File of per-iteration USD costs, one per line (the cost ledger) +- `--sigma` (`float64`, default `3`): Anomaly threshold in standard deviations above the rolling mean (cost_anomaly, 18) + +## `loopexec isolate` + +Two-zone isolation: detached clone + per-run minted key + exec/agent zone launch plan + +Usage: `loopexec isolate [flags]` + +- `--agent-image` (`string`, default `loopexec/agent`): Agent-zone container image +- `--agent-network` (`string`, default `loopexec-agent-net`): Agent-zone network (operator-provisioned internal net; egress via the proxy) +- `--branch` (`string`, default ``): Branch to check out (default: the repo default) +- `--check` (`string`, default ``): Check command to run in the exec zone +- `--confirm` (`bool`, default `false`): Confirm launching containers (required with --execute) +- `--egress-allow` (`stringArray`, default `[]`): Allowed egress host:port (default api.model.example:443); RECORDED in the receipt, enforced by the operator's allowlist proxy +- `--egress-proxy` (`string`, default `http://egress-proxy:8080`): Auditing forward proxy the agent zone routes through +- `--exec` (`string`, default ``): Agent/work command to run in the agent zone +- `--exec-image` (`string`, default `loopexec/exec`): Exec-zone container image +- `--execute` (`bool`, default `false`): Launch the zones (default: render the plan only) +- `--help, -h` (`bool`, default `false`): help for isolate +- `--json` (`bool`, default `false`): Emit machine-readable JSON output +- `--key-env` (`string`, default `MODEL_API_KEY`): Env var the minted credential is injected as +- `--mint-cmd` (`string`, default ``): Operator hook that prints a per-run scoped, spend-capped key to stdout (requires --revoke-cmd) +- `--repo` (`string`, default ``): Repository to clone into the sandbox (default: --workdir) +- `--revoke-cmd` (`string`, default ``): Operator hook that revokes the key (receives it via the key-env env var) +- `--run-id` (`string`, default ``): Run identifier (slug) +- `--runtime` (`string`, default `docker`): Container runtime used to launch the zones +- `--workdir` (`string`, default ``): Directory holding .loopexec (default: current directory) + +## `loopexec probe-check` + +Measure check determinism as a confidence bound (no check, no loop) + +Usage: `loopexec probe-check [flags]` + +- `--check` (`string`, default ``): External check command to probe (required) +- `--help, -h` (`bool`, default `false`): help for probe-check +- `--json` (`bool`, default `false`): Emit machine-readable JSON output +- `--max-flake-rate` (`float64`, default `0`): Target max flake rate to certify (derives runs via rule of three) +- `--runs` (`int`, default `0`): Number of probe runs (default: derived from --max-flake-rate, else 10) +- `--workdir` (`string`, default ``): Directory to run the check in (default: current directory) + +## `loopexec reexecute` + +Live re-run of the recorded loop config N times; reports a statistical match (--confirm) + +Usage: `loopexec reexecute [flags]` + +- `--confirm` (`bool`, default `false`): Confirm the budget-burning live re-run +- `--help, -h` (`bool`, default `false`): help for reexecute +- `--json` (`bool`, default `false`): Emit machine-readable JSON output +- `--samples` (`int`, default `3`): Number of live re-run samples +- `--workdir` (`string`, default ``): Directory containing .loopexec/state.json (default: current directory) + +## `loopexec replay` + +VERIFY a recorded receipt: re-run the check and confirm the fingerprint (agent-free) + +Usage: `loopexec replay [flags]` + +- `--help, -h` (`bool`, default `false`): help for replay +- `--json` (`bool`, default `false`): Emit machine-readable JSON output +- `--run-id` (`string`, default ``): Verify a specific recorded run by id (default: the latest run) +- `--workdir` (`string`, default ``): Directory containing .loopexec (default: current directory) + +## `loopexec report` + +Render a recorded receipt (state + JSONL event log); re-runs nothing + +Usage: `loopexec report [flags]` + +- `--help, -h` (`bool`, default `false`): help for report +- `--json` (`bool`, default `false`): Emit machine-readable JSON output +- `--run-id` (`string`, default ``): Report a specific recorded run by id (default: the latest run) +- `--workdir` (`string`, default ``): Directory containing .loopexec (default: current directory) + +## `loopexec run` + +Run a bounded check_fixpoint loop until the check passes or a bound trips + +Usage: `loopexec run [flags]` + +- `--budget-usd` (`float64`, default `0`): Run-total USD allowance; requires workflow strict or observed metering +- `--check` (`string`, default ``): External check command; exit 0 means converged (required) +- `--command-timeout` (`duration`, default `0s`): Deadline for every command and collector (0 = disabled) +- `--comprehension-every` (`int`, default `0`): Halt comprehension_debt_exceeded after N iterations without a `loopexec ack` (0 = off) +- `--context-file` (`stringArray`, default `[]`): File to include in the receipt context manifest (path + sha256); repeatable +- `--cost-usd` (`float64`, default `0`): Legacy recorded cost metadata; workflow actuals are metered separately +- `--exec` (`string`, default ``): Work command run each iteration before the check (e.g. an agent invocation) +- `--failures-cmd` (`string`, default ``): Command printing current open failures (one identity per line); enables set-based progress and the no-regression ratchet +- `--help, -h` (`bool`, default `false`): help for run +- `--integrity-cmd` (`string`, default ``): Command printing the test-determining surface (one identity per line); its t0 set MUST NOT lose a member (metric-integrity gate) +- `--json` (`bool`, default `false`): Emit machine-readable JSON output +- `--max-iterations` (`int`, default `10`): Maximum iterations (fuse) +- `--max-tokens` (`int`, default `0`): Recorded sampling max_tokens +- `--model-id` (`string`, default ``): Record the model id in the receipt (records only; the call lives in --exec); enables the model/sampling pin +- `--model-provider` (`string`, default ``): Record the model provider in the receipt (records only; does NOT select the model - --exec does) +- `--model-version` (`string`, default ``): Record the model version/build in the receipt +- `--no-progress-k` (`int`, default `3`): Halt no_progress_detected after K iterations with no new best failing-set size +- `--once` (`bool`, default `false`): Run exactly one iteration (debug single-step); overrides --max-iterations +- `--resume` (`bool`, default `false`): Resume a workflow run with identical policy and cumulative bounds +- `--run-id` (`string`, default ``): Run identifier +- `--seed` (`int`, default `0`): Recorded sampling seed +- `--temperature` (`float64`, default `0`): Recorded sampling temperature +- `--terminate-grace` (`duration`, default `250ms`): Grace between process-group TERM and KILL +- `--timeout` (`duration`, default `0s`): Total run deadline (0 = disabled) +- `--workdir` (`string`, default ``): Directory to run commands in (default: current directory) +- `--workflow` (`string`, default ``): Pinned workflow JSON (argv hooks, metering, candidates, numeric policy) + +## `loopexec status` + +Show loop status + +Usage: `loopexec status [flags]` + +- `--halt-reason` (`string`, default ``): Current halt reason +- `--help, -h` (`bool`, default `false`): help for status +- `--iteration` (`int`, default `0`): Current iteration +- `--json` (`bool`, default `false`): Emit machine-readable JSON output +- `--run-id` (`string`, default ``): Run identifier + +## `loopexec step` + +Execute a single loop step + +Usage: `loopexec step [flags]` + +- `--help, -h` (`bool`, default `false`): help for step +- `--json` (`bool`, default `false`): Emit machine-readable JSON output + +## `loopexec version` + +Print the CLI identity + +Usage: `loopexec version [flags]` + +- `--help, -h` (`bool`, default `false`): help for version +- `--json` (`bool`, default `false`): Emit machine-readable JSON output + +## `loopexec watch` + +Read the heartbeat and report alive vs heartbeat_stale + +Usage: `loopexec watch [flags]` + +- `--help, -h` (`bool`, default `false`): help for watch +- `--json` (`bool`, default `false`): Emit machine-readable JSON output +- `--stall-timeout` (`int`, default `600`): Seconds before the heartbeat is considered stale +- `--workdir` (`string`, default ``): Directory containing .loopexec/heartbeat (default: current directory) + +## JSON field contract + +Types and JSON tags are generated from the runtime structs. Value semantics and exit behavior are described in cli.md and SPEC.md. + +### `response` + +- `failure_cause,omitempty`: `string` +- `best_candidate,omitempty`: `string` +- `tool`: `string` +- `version`: `string` +- `status`: `string` +- `run_id,omitempty`: `string` +- `iteration,omitempty`: `int` +- `halt_reason,omitempty`: `string` +- `check_exit,omitempty`: `*int` +- `receipt,omitempty`: `string` +- `probe,omitempty`: `*main.probeReport` +- `doctor,omitempty`: `*main.doctorReport` +- `verdict,omitempty`: `string` +- `why,omitempty`: `string` +- `verified,omitempty`: `*bool` +- `signature,omitempty`: `string` +- `ops,omitempty`: `*main.opsReport` +- `context,omitempty`: `*main.contextReport` +- `isolation,omitempty`: `*main.isolationReport` +- `report,omitempty`: `*main.reportSummary` +- `cost,omitempty`: `*main.costReport` +- `budget_scope,omitempty`: `*main.scopedBudgetReport` +- `errors`: `[]string` + +### `receiptEvent` + +- `output_truncated,omitempty`: `bool` +- `phase,omitempty`: `string` +- `duration_ms`: `int64` +- `cause,omitempty`: `string` +- `ts`: `int64` +- `run_id`: `string` +- `iteration`: `int` +- `event`: `string` +- `detail,omitempty`: `string` +- `exit_code,omitempty`: `*int` + +### `probeReport` + +- `runs`: `int` +- `passes`: `int` +- `fails`: `int` +- `flake_count`: `int` +- `stable`: `bool` +- `flake_upper_bound`: `float64` +- `confidence_pct`: `int` +- `distinct_exit_codes`: `[]int` +- `max_flake_rate,omitempty`: `float64` +- `certified`: `bool` + +### `doctorReport` + +- `checks`: `[]main.doctorCheck` +- `probe,omitempty`: `*main.probeReport` + +### `opsReport` + +- `samples,omitempty`: `int` +- `converged,omitempty`: `int` +- `convergence_rate,omitempty`: `float64` +- `distribution,omitempty`: `map[string]int` +- `packet,omitempty`: `string` +- `heartbeat_age_s,omitempty`: `*int` +- `stale,omitempty`: `*bool` + +### `contextReport` + +- `path`: `string` +- `tokens_estimated`: `int` +- `budget_tokens`: `int` +- `files_included`: `[]string` +- `files_dropped`: `[]string` + +### `isolationReport` + +- `sandbox`: `string` +- `clone`: `string` +- `agent_image`: `string` +- `exec_image`: `string` +- `exec_zone_cmd`: `string` +- `agent_zone_cmd`: `string` +- `egress_allow`: `[]string` +- `key_env,omitempty`: `string` +- `hardened`: `bool` +- `minted`: `bool` +- `revoked`: `bool` +- `executed`: `bool` +- `exec_zone_exit,omitempty`: `*int` +- `agent_zone_exit,omitempty`: `*int` + +### `reportSummary` + +- `budget,omitempty`: `*main.budgetBook` +- `numeric,omitempty`: `*main.numericState` +- `candidate,omitempty`: `*main.candidateState` +- `best_candidate,omitempty`: `string` +- `phase`: `string` +- `exit_code`: `int` +- `iterations`: `int` +- `check,omitempty`: `string` +- `workdir,omitempty`: `string` +- `cost_usd,omitempty`: `float64` +- `model,omitempty`: `*main.modelPin` +- `sampling,omitempty`: `*main.samplingPin` +- `context_files,omitempty`: `int` +- `fingerprint,omitempty`: `*main.checkFingerprint` +- `events`: `int` +- `receipt,omitempty`: `string` +- `receipt_status`: `string` +- `warnings`: `[]string` +- `timeline`: `[]main.receiptEvent` +- `score_target,omitempty`: `*float64` +- `attestation_status`: `string` +- `attested`: `bool` + +### `costReport` + +- `iterations`: `int` +- `total_usd`: `float64` +- `mean_usd`: `float64` +- `stddev_usd`: `float64` +- `max_usd`: `float64` +- `budget_usd,omitempty`: `float64` +- `sigma`: `float64` +- `over_budget`: `bool` +- `anomaly_at,omitempty`: `[]int` + +### `scopedBudgetReport` + +- `scope`: `string` +- `max_calls`: `int` +- `used`: `int` +- `remaining`: `int` +- `phase_remaining`: `map[string]int` +- `reservation,omitempty`: `string` + +### `doctorCheck` + +- `name`: `string` +- `status`: `string` +- `detail,omitempty`: `string` + +### `budgetBook` + +- `mode`: `string` +- `microusd`: `int64` +- `monetary_known`: `bool` +- `calls`: `int64` +- `tokens`: `int64` +- `entries`: `map[string]workflow.Call` +- `pending,omitempty`: `*workflow.Preflight` +- `phase_costs_usd,omitempty`: `[]float64` + +### `numericState` + +- `best_accepted,omitempty`: `*float64` +- `patience_anchor,omitempty`: `*float64` +- `reviewed`: `int` +- `reviews_since_improvement`: `int` + +### `candidateState` + +- `best_iteration,omitempty`: `int` +- `numeric,omitempty`: `*main.numericState` +- `attempted_id,omitempty`: `string` +- `accepted_id,omitempty`: `string` +- `restored_id,omitempty`: `string` +- `best_id,omitempty`: `string` +- `initial_id,omitempty`: `string` +- `status`: `string` +- `best_exposed`: `bool` +- `protected`: `[]main.manifestEntry` +- `best_fingerprint,omitempty`: `*main.checkFingerprint` + +### `modelPin` + +- `provider,omitempty`: `string` +- `id`: `string` +- `version,omitempty`: `string` + +### `samplingPin` + +- `temperature`: `float64` +- `seed`: `int` +- `max_tokens,omitempty`: `int` + +### `checkFingerprint` + +- `exit_code`: `int` +- `output_sha256`: `string` + +### `manifestEntry` + +- `path`: `string` +- `sha256`: `string` diff --git a/docs/cli.md b/docs/cli.md index 5ad957a..3173127 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -2,6 +2,10 @@ This document defines the command surface and machine contract for loopexec. +The [generated command, flag and JSON field contract](cli-reference.generated.md) +is checked against the actual runtime by tests. This guide defines value semantics +and examples; both must be updated together when the behavior changes. + ## Commands - `loopexec init` @@ -35,7 +39,9 @@ This document defines the command surface and machine contract for loopexec. - `loopexec attest` - HMAC-sign the receipt (over the model pin, sampling, context manifest, cost, and fingerprint) so provenance is checkable; `--verify` checks the stored signature. Signs the latest run by default; `--run-id ` targets a specific recorded run. Key from `--key`, else `$LOOPEXEC_ATTEST_KEY`, else a dev default. - `loopexec report` - - Render a recorded receipt as a digest: the run's outcome (phase, halt reason, exit class), its pins (check, fingerprint, model, sampling, cost, context manifest size), whether it has been attested, and the per-iteration timeline parsed from `.loopexec/run-.jsonl`. Re-runs nothing and exits `0` even for a failed run (it reports, it does not re-decide). Reports the latest run by default; `--run-id ` targets a specific recorded run. + - Render a recorded receipt as a digest: the run's outcome (phase, halt reason, exit class), its pins (check, fingerprint, model, sampling, cost, context manifest size), signature-file presence, and the per-iteration timeline parsed from `.loopexec/run-.jsonl`. Re-runs nothing and exits `0` even for a failed run (it reports, it does not re-decide). Reports the latest run by default; `--run-id ` targets a specific recorded run. + - Unreleased reporting additions: `report.timeline` contains readable typed events, including phase, duration, cause and output-truncation flags; the response preserves `failure_cause`. `report.receipt_status` is `readable`, `incomplete`, `missing`, or `unreadable`; `report.warnings` names lost evidence. Invalid JSON/events and mismatched run IDs are excluded with warnings, while valid events remain visible. `readable` means parsing succeeded, not that the trajectory or receipt integrity was verified. Warnings do not change the read-only report exit code. + - `report.attestation_status` is `absent`, `present_unverified`, or `unreadable`. The legacy `report.attested` boolean retains its signature-file-presence meaning. Verify explicitly with `attest --verify` and the signing key. Human reports show the best retained score, configured `score_target`, review counts and candidate retention separately from convergence; an `accepted` candidate from a capped run is not quality approval. `run` writes a per-run state snapshot (`.loopexec/run-.state.json`) next to its receipt, so `replay` / `explain-halt` / `attest` / `report` can address any recorded run by `--run-id` even after later runs advance the default `.loopexec/state.json` pointer. - `loopexec reexecute` diff --git a/docs/creative-verification.md b/docs/creative-verification.md new file mode 100644 index 0000000..8df7062 --- /dev/null +++ b/docs/creative-verification.md @@ -0,0 +1,98 @@ +# Capturing creative verification evidence + +LoopExec governs attempts, accounting, candidate retention, and computed stops. +A project's protected checks and independent critic define its quality standard. +This is one optional domain example; LoopExec is not specific to creative work. +An art score is meaningful only with a fixed rubric, calibrated references, +candidate-bound captures, and technical evidence. No generic score certifies AAA +quality across projects. + +## One attempt, complete evidence + +Keep each adapter invocation to one bounded attempt. LoopExec owns retries. +The adapter should record these phases before returning, including failed or +skipped phases: + +| Phase | Retained evidence | Gate | +| --- | --- | --- | +| Readiness | Hashed source, export, runtime, reference calibration, material transfer, controlled lighting, detail comparison, regression checks and resolved contradictions | Missing/stale/unresolved evidence blocks model spending | +| Builder | Actual model/version, reasoning policy, stable call ID, result and available usage | Reserve immediately before launch; failed launch consumes its slot | +| Technical checks | Protected check results, import/load diagnostics, scale/collision/performance measurements | A red technical check skips the critic | +| Captures | Fixed camera IDs/poses, engine version, render warm-up, PNG hashes, capture receipts and diagnostics | Bind every image to this candidate; no recapture in verification | +| Critic | Read-only independent role, rubric hash, category scores, ranked issues tied to view/region, uncertainty and vetoes | A score cannot override a failed check | +| Verdict | Signed candidate/objective/policy/capture/result bindings, local replayable check and score output | Reject missing, tampered or stale evidence | +| Promotion | Explicit human decision, promoted candidate digest and backup | Convergence does not automatically publish or change the main project | + +Store private model logs, references, signing keys and diagnostics outside the +deliverable. Retain compact summaries and hashed evidence. LoopExec keeps bounded +diagnostic tails, not complete model transcripts or engine logs; the adapter +must deliberately capture those if needed. See [workflow trust boundaries](workflows.md). + +## Read a verdict correctly + +```sh +loopexec report --json --workdir --run-id +loopexec report --workdir --run-id +``` + +Both commands read existing evidence without models or engines. The unreleased +report improvements expose typed `report.timeline`, `report.receipt_status`, +`report.warnings`, `failure_cause`, and signature verification status. Inspect +these alongside `halt_reason`, `report.numeric`, and `report.budget`. + +- `success_condition_met` (exit 10) means configured gates converged. +- `max_iterations_reached` (exit 12) is a stopped attempt, even when + `candidate.status` is `accepted` or a best candidate is exposed. That status + describes retention within this run, not meeting its recorded acceptance target. + A later explicit owner adjustment is a separate decision, not receipt convergence. +- `numeric.best_accepted` and patience apply to this run. They do not remember + another run's higher score or prove the current promoted files still match. +- `budget.monetary_known:false` means unknown cost. `microusd:0` or `tokens:0` + in that state does not establish free work or zero token consumption. +- `report.attestation_status:present_unverified` means a signature file exists. + `attest --verify` checks its HMAC; project critic signatures have their own + verifier. Neither signature establishes artistic truth. +- `report.receipt_status:readable` describes parsing. Use `replay` to re-run + the recorded local check and compare its fingerprint. Inspect the check first: + replay executes it and is safe without spending only when the adapter obeys + the local, read-only hook contract. + +Compare reviews only when the candidate digest, rubric, references, engine, +camera and settled lighting are known. Preserve score regressions and unresolved +issues; post-review edits are unreviewed until independently verified. Repeated +or contradictory criticism should lead to a measured diagnostic intervention, +not another run ID or an automatic model switch. + +## Shared allowance and migration + +See [acceptance policy](acceptance-policy.md) for recording an owner-adjusted +intermediate acceptance while preserving the desired quality objective. In the +home 3D integration, the owner deliberately adjusted acceptance to 8.1 after days +of work; 8.5 remains the desired target. The earlier receipt's 8.5 gate is +historical evidence, not a complete account of that later decision. No asset, +project target or review allowance is changed by this guide. + +Use the published v0.4.0 `budget reserve` immediately before every model +subprocess, with a shared store outside all run workspaces and a stable scope +per asset. Include builder, critic and optional camera-director phase limits. +Unknown charges still consume call reservations. Verification, imports and +rendering should never contain hidden model launches. + +For an existing project-specific ledger, preserve every historic reservation, +including failed launches. Migrate with stable call IDs and verify old/new used +and remaining counts before enabling the adapter. An exhausted allowance stays +exhausted. Do not rename the asset, recreate the store, reset a policy or count +only successful reviews to restore spending. See the [offline migration example](../examples/scoped-budget/README.md). + +The shared budget counts calls only. Do not confuse its `budget_scope.used` with a +workflow budget's count of all reported work phases. Engine/technical phases +can appear as unknown workflow usage without consuming a model-call slot. + +## Next usability increment + +A useful cross-run history should join stable asset scope, run ID, candidate +digest, rubric hash, technical result, score/target, ranked issues, reserved +calls, known/unknown cost, retained/promoted revision, and evidence paths. Keep +missing evidence and unmatched promoted files explicit. That history is not +implemented by the current per-run `report`; collecting it should require no +models, engines or automatic retries. diff --git a/docs/documentation-continuity.md b/docs/documentation-continuity.md new file mode 100644 index 0000000..a1987b2 --- /dev/null +++ b/docs/documentation-continuity.md @@ -0,0 +1,96 @@ +# Code and documentation continuity + +LoopExec is a general-purpose bounded agentic loop governor. Canonical product +contracts live in this repository. The website is a presentation of those +contracts, not an independently maintained implementation specification. + +## Required change gates + +Runtime, CLI, dependencies, examples, tools, CI/release workflows and agent-policy +changes require matching canonical prose and CHANGELOG.md in the same change. +`scripts/check-docs-sync.sh` rejects missing co-changes, generated-reference-only +updates and unrelated audit notes. Human review must still verify semantic +accuracy: editing a documentation path alone cannot prove the behavior is correct. + +`TestDocumentationContract` compares the complete declared CLI tree, flags, +defaults, help and JSON struct field contract with the checked-in reference. +Default tests fail on drift. After reviewing the prose, regenerate with: + +```sh +go test ./cmd/loopexec -run '^TestDocumentationContract$' -args -update-docs +go test ./cmd/loopexec -run '^TestDocumentationContract' -count=1 +``` + +Cobra's implicit help/completion commands are framework behavior, not declared +product commands. The reference includes default help/version flags. Behavioral +tests and manual contract review remain necessary for value and exit semantics. + +Add every public guide and new release note to `docs/documentation-files.json`. +The explicit allowlist excludes private handoffs, state, model logs and secrets. +The bundle builder rejects missing required files, duplicates and symlinks. + +## Coupled release artifacts + +`scripts/build-release.sh` verifies the CLI reference and source version, builds +six platform archives, and includes the full `documentation/` snapshot and +manifest in every archive. A separate documentation archive is checksummed and +published alongside binaries. Its manifest contains the version, a deterministic +source-content digest, a documentation digest and each public file's SHA256. +No timestamp or current git HEAD substitutes for hashing the actual content. + +Platform archives also retain the canonical docs/spec/changelog/example paths at +their root so the root README's relative links work after extraction. The nested +documentation snapshot remains the full manifest-bound release evidence. + +Prepare the documentation from the exact intended release source, not a newer +main branch. Label unreleased capabilities as unreleased. Before publication, +deploy the matching docs snapshot while keeping the last published installation +as the default until the binary release is public. Deployment and GitHub are +separate services; these gates do not claim a distributed atomic transaction. + +## Website build handshake + +The docs-site build must consume the canonical bundle, render its CLI, spec, +guides, capabilities and changelog, and publish these files on the same origin: + +- `/loopexec-documentation.json`: byte-equivalent manifest fields. +- `/loopexec-documentation/`: exact allowlisted public source files. +- `/`: HTML containing the build marker generated from that manifest. + +```sh +go run ./tools/docs-continuity bundle build/documentation +go run ./tools/docs-continuity verify-bundle build/documentation +go run ./tools/docs-continuity marker build/documentation/manifest.json +go run ./tools/docs-continuity verify-site build/documentation/manifest.json +``` + +Use a new output directory. The printed HTML comment must come from the manifest +used by the site build; never hardcode it separately. Do not keep old manually +authored capability/install pages beside new generated content. Preserve content +through the site template, then verify rendered pages, installation links and +mobile layout. The automated handshake verifies source provenance and deployed +raw content, not whether a template's prose or layout is truthful or usable. + +The publication tool has no origin override: it checks `https://loopexec.dev`, +rejects cross-origin/non-HTTPS redirects and fails on missing/stale/unreachable +manifest, homepage marker or document content. Release CI reads back before draft +creation and again immediately before making the release public. A failure leaves +publication stopped; a created draft remains a draft for investigation. + +The Release workflow can also be dispatched on main for a non-publishing CI and +deployed-docs rehearsal. Both dispatch and tag runs require source reachable from +main. Only a tag run may enter the publication job, after CI and docs readback pass. + +## Enforcement and rollout boundary + +Require the CI checks on protected main, restrict release-tag and GitHub release +writes to authorized maintainers/automation, and retain these gates during review. +An administrator or direct manual publication can bypass repository automation; +account rules and human semantic review are still required. Do not claim they are +configured without live verification. + +These gates are local unreleased work until committed and adopted by CI. The +separate website currently needs the handshake integration and deployment; the +new release workflow intentionally blocks until it is ready. Do not weaken the +gate because the current website predates it. No release or site deployment is +authorized merely by preparing or testing a bundle. diff --git a/docs/documentation-files.json b/docs/documentation-files.json new file mode 100644 index 0000000..ab964b0 --- /dev/null +++ b/docs/documentation-files.json @@ -0,0 +1,24 @@ +{ + "files": [ + "LICENSE", + "README.md", + "SPEC.md", + "CHANGELOG.md", + "docs/documentation-files.json", + "docs/cli.md", + "docs/cli-reference.generated.md", + "docs/architecture.md", + "docs/workflows.md", + "docs/integrations.md", + "docs/loop-contract.md", + "docs/small-integration.md", + "docs/acceptance-policy.md", + "docs/documentation-continuity.md", + "docs/creative-verification.md", + "docs/status-and-roadmap.md", + "docs/releases/v0.3.0.md", + "docs/releases/v0.4.0.md", + "docs/releases/v0.4.1.md", + "examples/scoped-budget/README.md" + ] +} diff --git a/docs/integrations.md b/docs/integrations.md index ddfc6d7..d60a359 100644 --- a/docs/integrations.md +++ b/docs/integrations.md @@ -29,11 +29,12 @@ fingerprint. It never runs an agent. `report` reads state/receipts without executing anything. `reexecute --confirm` is a live rerun of legacy configs; workflow receipts require explicit initialization of a new adapter workspace. -For unattended creative work, use the [workflow contract](workflows.md). -Its Go adapter executes one builder/test/conditional-critic sequence. +For unattended agentic work, use the [workflow contract](workflows.md). +Each adapter executes one bounded work/check/optional-review sequence. The governor owns retries, subprocess bounds, exact accounting, candidate -promotion/restoration, and numeric patience. The executable fixture needs no -Python runner, network, model key, Blender, or Godot. +promotion/restoration, and numeric patience. The creative example is one +integration, not the product boundary; its fixture needs no Python runner, +network, model key, Blender, or Godot. A nonzero `--budget-usd` now requires workflow metering. Strict caps require enforced per-call reservations; observed metering only stops subsequent work. diff --git a/docs/releases/v0.4.1.md b/docs/releases/v0.4.1.md new file mode 100644 index 0000000..6ff8e9e --- /dev/null +++ b/docs/releases/v0.4.1.md @@ -0,0 +1,66 @@ +# LoopExec v0.4.1 + +This patch improves evidence reporting and couples implementation publication to +matching canonical documentation. LoopExec remains a general-purpose bounded +agentic loop governor, not a project-specific quality certifier. + +## Included + +- Read-only report timelines, saved failure causes, receipt parsing status and + bounded missing/damaged-evidence warnings. +- Clear candidate-retention versus convergence and owner-acceptance boundaries. +- Signature-file presence labeled `present_unverified`; explicit attestation + verification remains separate. Legacy `attested` JSON behavior is preserved. +- Canonical prose/changelog co-change CI gate and generated CLI/JSON field contract. +- Source-bound, allowlisted documentation in all six platform archives, plus a + standalone documentation archive and SHA256 checksums. +- Official deployed-docs manifest, content and homepage-marker readback before + release draft creation and again before publication. Missing/stale/unreachable + documentation blocks publication; there is no alternate-origin override. +- General-purpose acceptance/evidence guidance and an optional creative example. + +Targets, numeric domains, direction, tolerance, improvement and patience belong to +workflow configuration. Example scores are fixtures, not runtime/service defaults. +No automatic model calls, budget resets, consumer migrations or promotion are added. + +## Compatibility + +Existing report exit behavior and convergence exit 10 are unchanged. JSON report +fields are additive. Parsing is not receipt integrity verification, signature +presence is not cryptographic verification, and a retained candidate is not an +owner's acceptance or publication decision. + +Every archive retains canonical root documentation paths for README links and a +full `documentation/manifest.json` snapshot bound to source/docs digests. Private +state, handoff notes, logs and secrets are excluded. + +## Installation and verification + +After publication, with Go 1.26 or newer: + +```sh +go install github.com/justyn-clark/loopexec/cmd/loopexec@v0.4.1 +loopexec version --json +loopexec demo +``` + +Or select the OS/architecture archive from +[the release](https://github.com/justyn-clark/loopexec/releases/tag/v0.4.1), verify +`checksums.txt`, extract, and inspect `documentation/manifest.json`. +The [Releases page](https://github.com/justyn-clark/loopexec/releases) is the +authority for whether publication has completed; prepared notes are not proof. + +## Validation and limits + +CI pins checksum-verified SMALL v1.1.2, including command-summary correction and +narrow, exact-proof-backed local URL source-literal handling. +Historical command receipts remain unchanged; reviewed portable command proof +supports cache-free strict validation without exporting the private cache. + +Release CI covers formatting, vet, unit/subprocess/CLI tests, targeted race checks, +legacy/creative/shared-budget proofs and canonical SMALL validation on Linux/macOS. +Windows is cross-built; native Windows runtime remains unverified. Full external +model/provider billing, production containment and project-specific quality are +operator integration responsibilities. Native cross-run objective/policy/owner +decision history is not implemented. Account protections and semantic/visual +docs review are still required; provenance checks cannot prove every sentence. diff --git a/docs/small-integration.md b/docs/small-integration.md index 65fdb74..cec367c 100644 --- a/docs/small-integration.md +++ b/docs/small-integration.md @@ -20,7 +20,7 @@ small version ``` The optional proof requires Bash, jq, a POSIX shell, and SMALL v1.1.0 or newer. -CI pins and checksum-verifies v1.1.0. It creates disposable workspaces; your +CI pins and checksum-verifies v1.1.2. It creates disposable workspaces; your project's profile is unchanged. The proof covers: - v1 state with explicit task checkpoint after verified LoopExec convergence; @@ -35,6 +35,45 @@ composition and attribution; it is not a distributed-concurrency or live-model benchmark. Use separate worktrees/candidate directories and runtime storage for independent live work. A single workflow runtime has one governor. +## Development-state command proof + +The released SMALL v1.1.0 can truncate an allowed localhost URL inside a command +summary, then reject that display during strict validation. This checkout has +two such historical screenshot captures. Keep their original receipts unchanged +and use the released SMALL v1.1.2, which includes the command-summary fix and +the captured-source correction needed for the retained QA script. +Verify the resolved binary rather than assuming a Homebrew installation updated. + +The correction validates exact original command bytes against the saved ref, +SHA256 and legacy summary. Individually reviewed, non-secret proof can be kept +under `.small-command-proofs/`, mirroring the supported `.small-cache/` ref. +The proof store is portable validation input, not replacement audit history. +Never export the whole private cache. Missing or changed proof must fail closed. + +New truncated captures marked `command_summary_version: 2` also need portable +exact-byte proof before committing state for a cache-free checkout. Review each +full command for private material before copying it; do not strip the marker. +Short captures retain the existing metadata contract. See the upstream +[command-proof guide](https://github.com/justyn-clark/small-protocol/blob/v1.1.2/docs/command-proof.md). + +v1.1.2 recognizes a narrowly defined backtick-quoted local URL with a supported +runtime-port expression only after exact command SHA256 and summary verification. +It does not execute source to validate links. Authored fields, external HTTP, +unsupported expressions, credentials and missing/tampered proof remain strict. +The existing QA capture can therefore pass without rewriting its original bytes. + +Validate with the explicitly selected binary: + +```sh +small version # must resolve to v1.1.2 for this checkout +small check --strict +``` + +Check a disposable snapshot with no cache: selected proof must pass, while +missing or tampered proof must fail without changing the saved state. CI adopts +the same v1.1.2 release; local success alone does not establish hosted compatibility +or release readiness. Verify checks for the committed candidate before publication. + ## Exit codes and acceptance An external check passes with exit 0. LoopExec returns exit 10 for verified diff --git a/docs/status-and-roadmap.md b/docs/status-and-roadmap.md new file mode 100644 index 0000000..2694170 --- /dev/null +++ b/docs/status-and-roadmap.md @@ -0,0 +1,65 @@ +# LoopExec status and roadmap + +Snapshot verified October 7, 2026. This is a dated audit, not a live release feed. +This audit covers the general-purpose LoopExec governor and published `loopexec` +binary. The home project's quality verifier is one consumer, not its product scope. + +## Published and deployed + +| Surface | Verified status | Evidence | +| --- | --- | --- | +| Latest published release | v0.4.0, September 26, 2026; public, not a draft or prerelease | [Release](https://github.com/justyn-clark/loopexec/releases/tag/v0.4.0) | +| Release artifacts | Six macOS/Linux/Windows amd64/arm64 archives plus SHA256 checksums | Release assets and [successful release workflow](https://github.com/justyn-clark/loopexec/actions/runs/36276149282) | +| Release commit | `c0e4cc2653b428faa2fceb6485f94a98aa3167d5` | Annotated v0.4.0 tag resolves to this commit | +| Published main | `4c96ad49d9d57199824fc44c7eeee8108d292dbd`; local main matched at audit start | [Main](https://github.com/justyn-clark/loopexec/tree/main) | +| Main CI | Passed on the exact published main commit | [CI run](https://github.com/justyn-clark/loopexec/actions/runs/36288545259) | +| Open GitHub backlog | No open issues or PRs at audit time; not evidence that the product has no remaining work | Repository issues/PR API reads | +| Public documentation | https://loopexec.dev is accessible; its homepage/changelog describe v0.2.0 | Public page read and matching source in the separate `loopexec.dev` repository | + +LoopExec ships as a locally installed CLI; the deployed website is documentation, +not a hosted LoopExec runtime. The docs repository is configured for Vercel, but +this audit did not verify the provider deployment ID or control-plane settings. +Linux/macOS CI exercises runtime behavior. Windows is cross-built; native +Windows runtime remains unverified. Asset metadata was checked live; the binary +archives were not downloaded again during this audit. + +## Available capabilities + +v0.3.0 introduced bounded unattended workflow hooks, exact known/unknown +accounting, numeric progress/patience, candidate snapshots/restoration, and +offline creative and SMALL compatibility proofs. v0.4.0 adds shared model-call +reservations across run IDs. Existing deterministic loops, integrity guards, +replay/attestation, halt explanations and subprocess deadlines remain available. +See [SPEC section 11](../SPEC.md#11-capability-status-normative-source-for-the-docs-matrix) +for the capability-level contract. + +The current local patch improves reports, enforces code/docs continuity gates, +packages source-bound docs and adds general-purpose [acceptance guidance](acceptance-policy.md). +Those changes are unreleased and do not alter v0.4.0 packages or existing consumer +installations. No new version, push, tag, deployment or publication was performed. + +An optional Jev semantic judge exists in a separate local `codex/jev-judge` +worktree based on the older v0.3.0 development lineage. It is uncommitted and +unreleased; it is absent from published main/v0.4.0. Previous offline validation +does not establish current integration, live-provider readiness or calibrated +quality thresholds. Reconcile it with current main and evaluate labeled fixtures +before considering it for a release. + +## Prioritized remaining work + +| Priority | Work | Acceptance evidence | +| --- | --- | --- | +| P0 | Adopt canonical docs/build handshake in the site, then sync home/install/CLI/capability/changelog | Source-bound manifest/content readback, rendered-page checks and correct published/unreleased boundaries; publication remains blocked until matching | +| P0 | Commit continuity gates and require CI/account release protections | Exact-commit CI plus live branch/tag/release permission verification; local tests do not establish hosted enforcement | +| P0 | Release these report fixes after review and CI | Exact-commit CI, offline proof receipts, packages/checksums and fresh install verification | +| P1 | Migrate existing adapters from v0.3.0/custom budget guards deliberately | Historic and failed reservations preserved; no restored allowance; parity and concurrency checks before model launch | +| P1 | Add general-purpose read-only objective history across run IDs | Objective/candidate/policy/evidence/decision/budget/promotion history; explicit owner revisions and missing-evidence warnings; no work or model calls | +| P1 | Strengthen oracle feedback | Calibrated references, controlled measurements, criterion-level interventions, resolved contradictions and evidence freshness | +| P1 | Reconcile and calibrate the optional Jev judge | Current-main integration tests, labeled accept/reject/escalate fixtures; explicit provider/usage proof before live claims | +| P2 | Broader runtime and safety coverage | Native Windows checks; documented OS limits; operator-provided containment/egress/provider hooks proven in deployment | +| P2 | Remaining specification sub-parts | Assertion-count/coverage-floor integrity, adversarial determinism/sequential monitor, context dependency tiers, escalation integrations/watchdog and full SMALL task-list topology | + +Acceptance criteria are integration contracts, not a universal quality certificate. +Retention, convergence, human approval and promotion must remain distinguishable. +An exhausted review budget or unresolved visual readiness is a stop condition, +not a reason to launch another critic under a new run ID. diff --git a/scripts/build-release.sh b/scripts/build-release.sh index 5d64e73..883d64c 100755 --- a/scripts/build-release.sh +++ b/scripts/build-release.sh @@ -7,9 +7,12 @@ OUT="$2" command -v jq >/dev/null version="$(go run ./cmd/loopexec version --json | jq -er '.version')" [[ "v$version" == "$TAG" ]] || { echo "source version differs from release tag" >&2; exit 1; } +go test ./cmd/loopexec -run '^TestDocumentationContract$' -count=1 mkdir -p "$OUT" [[ -z "$(find "$OUT" -mindepth 1 -print -quit)" ]] || { echo "output must be empty" >&2; exit 1; } OUT="$(cd "$OUT" && pwd -P)" +go run ./tools/docs-continuity bundle "$OUT/documentation" +tar -czf "$OUT/loopexec-$TAG-documentation.tar.gz" -C "$OUT" documentation for target in darwin/amd64 darwin/arm64 linux/amd64 linux/arm64 windows/amd64 windows/arm64; do os="${target%/*}" arch="${target#*/}" @@ -19,13 +22,15 @@ for target in darwin/amd64 darwin/arm64 linux/amd64 linux/arm64 windows/amd64 wi executable=loopexec [[ "$os" != windows ]] || executable=loopexec.exe CGO_ENABLED=0 GOOS="$os" GOARCH="$arch" go build -trimpath -o "$stage/$executable" ./cmd/loopexec - cp LICENSE README.md "$stage/" + cp -R "$OUT/documentation/." "$stage/" cp "docs/releases/$TAG.md" "$stage/RELEASE_NOTES.md" + cp -R "$OUT/documentation" "$stage/documentation" if [[ "$os" == windows ]]; then - (cd "$stage" && zip -q "$OUT/$name.zip" "$executable" LICENSE README.md RELEASE_NOTES.md) + (cd "$stage" && zip -qr "$OUT/$name.zip" "$executable" LICENSE README.md SPEC.md CHANGELOG.md RELEASE_NOTES.md docs examples documentation) else - tar -czf "$OUT/$name.tar.gz" -C "$stage" "$executable" LICENSE README.md RELEASE_NOTES.md + tar -czf "$OUT/$name.tar.gz" -C "$stage" "$executable" LICENSE README.md SPEC.md CHANGELOG.md RELEASE_NOTES.md docs examples documentation fi done +go run ./tools/docs-continuity verify-bundle "$OUT/documentation" (cd "$OUT" && shasum -a 256 loopexec-*.tar.gz loopexec-*.zip > checksums.txt && shasum -a 256 -c checksums.txt) printf 'RELEASE_PACKAGES_OK: %s\n' "$OUT" diff --git a/scripts/check-docs-sync.sh b/scripts/check-docs-sync.sh new file mode 100755 index 0000000..9cb7fc4 --- /dev/null +++ b/scripts/check-docs-sync.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +set -euo pipefail +# Changed paths arrive on stdin, like check-small-sync.sh. +behavior=0 +prose=0 +changelog=0 +while IFS= read -r path; do + case "$path" in + cmd/*|internal/*|examples/*|tools/*|scripts/*|.github/workflows/*|go.mod|go.sum|SPEC.md|AGENTS.md) + behavior=1 ;; + esac + case "$path" in + README.md|SPEC.md|docs/cli.md|docs/architecture.md|docs/workflows.md|docs/integrations.md|docs/loop-contract.md|docs/small-integration.md|docs/acceptance-policy.md|docs/documentation-continuity.md|docs/creative-verification.md) + prose=1 ;; + esac + [[ "$path" != CHANGELOG.md ]] || changelog=1 +done +if [[ "$behavior" -eq 1 && ( "$prose" -ne 1 || "$changelog" -ne 1 ) ]]; then + echo "Code/docs continuity failed: behavioral, tooling or policy changes require canonical prose AND CHANGELOG.md in the same change." >&2 + exit 1 +fi +echo DOCS_SYNC_OK diff --git a/tools/docs-continuity/main.go b/tools/docs-continuity/main.go new file mode 100644 index 0000000..849d3e3 --- /dev/null +++ b/tools/docs-continuity/main.go @@ -0,0 +1,374 @@ +// docs-continuity binds public documentation to the exact source being released. +package main + +import ( + "bytes" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "go/ast" + "go/parser" + "go/token" + "io" + "io/fs" + "net/http" + "os" + "path/filepath" + "reflect" + "sort" + "strconv" + "strings" + "time" +) + +const officialSite = "https://loopexec.dev" + +type fileDigest struct { + Path string `json:"path"` + SHA256 string `json:"sha256"` +} + +type manifest struct { + SchemaVersion int `json:"schema_version"` + Version string `json:"version"` + SourceSHA256 string `json:"source_sha256"` + DocumentationSHA256 string `json:"documentation_sha256"` + Files []fileDigest `json:"files"` +} + +func decode(data []byte, value any) error { + d := json.NewDecoder(bytes.NewReader(data)) + d.DisallowUnknownFields() + if err := d.Decode(value); err != nil { + return err + } + var extra any + if err := d.Decode(&extra); !errors.Is(err, io.EOF) { + return errors.New("expected exactly one JSON document") + } + return nil +} + +func digest(data []byte) string { + sum := sha256.Sum256(data) + return hex.EncodeToString(sum[:]) +} + +func safeRead(root, path string) ([]byte, error) { + if !fs.ValidPath(path) || strings.Contains(path, "\\") { + return nil, fmt.Errorf("unsafe path %q", path) + } + current := root + for _, part := range strings.Split(path, "/") { + current = filepath.Join(current, part) + info, err := os.Lstat(current) + if err != nil { + return nil, err + } + if info.Mode()&os.ModeSymlink != 0 { + return nil, fmt.Errorf("symlink forbidden: %s", path) + } + } + info, err := os.Stat(current) + if err != nil { + return nil, err + } + if !info.Mode().IsRegular() || info.Size() > 4<<20 { + return nil, fmt.Errorf("not a bounded regular file: %s", path) + } + return os.ReadFile(current) +} + +func sourceVersion(root string) (string, error) { + data, err := safeRead(root, "cmd/loopexec/main.go") + if err != nil { + return "", err + } + f, err := parser.ParseFile(token.NewFileSet(), "main.go", data, 0) + if err != nil { + return "", err + } + for _, decl := range f.Decls { + g, ok := decl.(*ast.GenDecl) + if !ok || g.Tok != token.CONST { + continue + } + for _, spec := range g.Specs { + v := spec.(*ast.ValueSpec) + for i, name := range v.Names { + if name.Name == "toolVersion" && i < len(v.Values) { + lit, ok := v.Values[i].(*ast.BasicLit) + if !ok || lit.Kind != token.STRING { + return "", errors.New("toolVersion must be a string constant") + } + return strconv.Unquote(lit.Value) + } + } + } + } + return "", errors.New("missing toolVersion") +} + +func hashFiles(root string, paths []string) ([]fileDigest, string, error) { + sort.Strings(paths) + files := make([]fileDigest, 0, len(paths)) + var binding bytes.Buffer + for i, path := range paths { + if i > 0 && path == paths[i-1] { + return nil, "", fmt.Errorf("duplicate file: %s", path) + } + data, err := safeRead(root, path) + if err != nil { + return nil, "", err + } + hash := digest(data) + files = append(files, fileDigest{path, hash}) + fmt.Fprintf(&binding, "%s\x00%s\n", path, hash) + } + return files, digest(binding.Bytes()), nil +} + +func sourcePaths(root string) ([]string, error) { + paths := []string{"go.mod", "go.sum", "AGENTS.md"} + for _, dir := range []string{"cmd", "internal", "examples", "scripts", "tools", ".github/workflows"} { + err := filepath.WalkDir(filepath.Join(root, dir), func(path string, entry fs.DirEntry, err error) error { + if err != nil { + return err + } + if entry.IsDir() { + switch entry.Name() { + case "vendor", "node_modules", "build", ".loopexec", ".small", ".small-cache": + return filepath.SkipDir + } + return nil + } + switch filepath.Ext(path) { + case ".go", ".sh", ".json", ".yaml", ".yml", ".toml": + relative, err := filepath.Rel(root, path) + if err != nil { + return err + } + paths = append(paths, filepath.ToSlash(relative)) + } + return nil + }) + if err != nil { + return nil, err + } + } + return paths, nil +} + +func makeManifest(root string) (manifest, error) { + var m manifest + registry, err := safeRead(root, "docs/documentation-files.json") + if err != nil { + return m, err + } + var allowlist struct { + Files []string `json:"files"` + } + if err := decode(registry, &allowlist); err != nil { + return m, err + } + m.SchemaVersion = 1 + m.Version, err = sourceVersion(root) + if err != nil { + return m, err + } + required := map[string]bool{"README.md": false, "SPEC.md": false, "CHANGELOG.md": false, "docs/documentation-files.json": false, "docs/cli.md": false, "docs/cli-reference.generated.md": false, "docs/documentation-continuity.md": false, "docs/releases/v" + m.Version + ".md": false, "examples/scoped-budget/README.md": false} + for _, path := range allowlist.Files { + if !publicPath(path) { + return m, fmt.Errorf("non-public documentation path: %s", path) + } + if _, ok := required[path]; ok { + required[path] = true + } + } + for path, present := range required { + if !present { + return m, fmt.Errorf("required public documentation missing from allowlist: %s", path) + } + } + m.Files, m.DocumentationSHA256, err = hashFiles(root, allowlist.Files) + if err != nil { + return m, err + } + sources, err := sourcePaths(root) + if err != nil { + return m, err + } + _, m.SourceSHA256, err = hashFiles(root, sources) + return m, err +} + +func publicPath(path string) bool { + switch path { + case "LICENSE", "README.md", "SPEC.md", "CHANGELOG.md", "docs/documentation-files.json", "examples/scoped-budget/README.md": + return true + } + return fs.ValidPath(path) && strings.HasPrefix(path, "docs/") && + strings.HasSuffix(path, ".md") && !strings.Contains(path, "handoff") && + !strings.Contains(path, "/.") && !strings.Contains(path, "\\") +} + +func verifyBundle(root, out string) (manifest, error) { + var saved manifest + data, err := safeRead(out, "manifest.json") + if err != nil { + return saved, err + } + if err := decode(data, &saved); err != nil { + return saved, err + } + expected, err := makeManifest(root) + if err != nil { + return saved, err + } + if !reflect.DeepEqual(saved, expected) { + return saved, errors.New("local documentation manifest differs from source") + } + for _, file := range saved.Files { + data, err := safeRead(out, file.Path) + if err != nil { + return saved, err + } + if digest(data) != file.SHA256 { + return saved, fmt.Errorf("bundled documentation differs: %s", file.Path) + } + } + return saved, nil +} + +func writeBundle(root, out string) error { + m, err := makeManifest(root) + if err != nil { + return err + } + if err := os.Mkdir(out, 0755); err != nil { + return fmt.Errorf("bundle output must not exist: %w", err) + } + for _, file := range m.Files { + data, err := safeRead(root, file.Path) + if err != nil { + return err + } + if digest(data) != file.SHA256 { + return fmt.Errorf("documentation changed during bundling: %s", file.Path) + } + path := filepath.Join(out, filepath.FromSlash(file.Path)) + if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { + return err + } + if err := os.WriteFile(path, data, 0644); err != nil { + return err + } + } + data, err := json.MarshalIndent(m, "", " ") + if err != nil { + return err + } + return os.WriteFile(filepath.Join(out, "manifest.json"), append(data, '\n'), 0644) +} + +func fetch(client *http.Client, url string) ([]byte, error) { + resp, err := client.Get(url) + if err != nil { + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("%s: HTTP %d", url, resp.StatusCode) + } + data, err := io.ReadAll(io.LimitReader(resp.Body, (4<<20)+1)) + if err == nil && len(data) > 4<<20 { + err = errors.New("site response exceeds limit") + } + return data, err +} + +func siteMarker(m manifest) string { + return fmt.Sprintf("", m.Version, m.SourceSHA256, m.DocumentationSHA256) +} + +func verifySite(client *http.Client, base string, expected manifest) error { + data, err := fetch(client, base+"/loopexec-documentation.json") + if err != nil { + return err + } + var actual manifest + if err := decode(data, &actual); err != nil { + return err + } + if !reflect.DeepEqual(actual, expected) { + return errors.New("deployed documentation manifest does not match release source and docs") + } + home, err := fetch(client, base+"/") + if err != nil { + return err + } + if !bytes.Contains(home, []byte(siteMarker(expected))) { + return errors.New("deployed homepage lacks the matching documentation build marker") + } + for _, file := range expected.Files { + data, err := fetch(client, base+"/loopexec-documentation/"+file.Path) + if err != nil { + return err + } + if digest(data) != file.SHA256 { + return fmt.Errorf("deployed documentation differs: %s", file.Path) + } + } + return nil +} + +func run(args []string) error { + if len(args) != 2 { + return errors.New("usage: docs-continuity bundle | verify-bundle | verify-site | marker ") + } + if args[0] == "bundle" { + return writeBundle(".", args[1]) + } + if args[0] == "verify-bundle" { + _, err := verifyBundle(".", args[1]) + return err + } + data, err := os.ReadFile(args[1]) + if err != nil { + return err + } + var m manifest + if err := decode(data, &m); err != nil { + return err + } + if args[0] == "marker" { + fmt.Println(siteMarker(m)) + return nil + } + if args[0] != "verify-site" { + return errors.New("unknown documentation command") + } + if filepath.Base(args[1]) != "manifest.json" { + return errors.New("expected bundle manifest.json") + } + if _, err := verifyBundle(".", filepath.Dir(args[1])); err != nil { + return err + } + client := &http.Client{Timeout: 15 * time.Second, CheckRedirect: func(req *http.Request, via []*http.Request) error { + if len(via) > 4 || req.URL.Scheme != "https" || req.URL.Host != "loopexec.dev" { + return errors.New("documentation redirect must remain on the official HTTPS origin") + } + return nil + }} + return verifySite(client, officialSite, m) +} + +func main() { + if err := run(os.Args[1:]); err != nil { + fmt.Fprintln(os.Stderr, "DOCS_CONTINUITY_FAILED:", err) + os.Exit(1) + } + fmt.Fprintln(os.Stderr, "DOCS_CONTINUITY_OK") +} diff --git a/tools/docs-continuity/main_test.go b/tools/docs-continuity/main_test.go new file mode 100644 index 0000000..fb967fb --- /dev/null +++ b/tools/docs-continuity/main_test.go @@ -0,0 +1,238 @@ +package main + +import ( + "encoding/json" + "fmt" + "net/http" + "net/http/httptest" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" +) + +type roundTripFunc func(*http.Request) (*http.Response, error) + +func (f roundTripFunc) RoundTrip(r *http.Request) (*http.Response, error) { return f(r) } + +func TestDeployedDocumentationGate(t *testing.T) { + doc := []byte("# Canonical documentation\n") + m := manifest{1, "0.4.0", "source-digest", "docs-digest", []fileDigest{{"README.md", digest(doc)}}} + for _, mode := range []string{"matching", "stale-manifest", "stale-home", "stale-content", "missing", "invalid", "unavailable"} { + t.Run(mode, func(t *testing.T) { + handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if mode == "unavailable" { + w.WriteHeader(http.StatusServiceUnavailable) + return + } + switch r.URL.Path { + case "/loopexec-documentation.json": + actual := m + if mode == "stale-manifest" { + actual.Version = "0.2.0" + } + if mode == "invalid" { + fmt.Fprint(w, "{broken") + return + } + if err := json.NewEncoder(w).Encode(actual); err != nil { + t.Error(err) + } + case "/": + if mode != "stale-home" { + fmt.Fprint(w, siteMarker(m)) + } + case "/loopexec-documentation/README.md": + if mode == "missing" { + http.NotFound(w, r) + return + } + if mode == "stale-content" { + fmt.Fprint(w, "old documentation") + return + } + if _, err := w.Write(doc); err != nil { + t.Error(err) + } + default: + http.NotFound(w, r) + } + }) + client := &http.Client{Transport: roundTripFunc(func(r *http.Request) (*http.Response, error) { + response := httptest.NewRecorder() + handler.ServeHTTP(response, r) + return response.Result(), nil + })} + err := verifySite(client, "https://example.invalid", m) + if (err == nil) != (mode == "matching") { + t.Fatalf("gate returned %v for %s", err, mode) + } + }) + } +} + +func TestDocsCoChangeGate(t *testing.T) { + for _, tc := range []struct { + name, paths string + pass bool + }{ + {"code-only", "cmd/loopexec/run.go\n", false}, + {"changelog-only", "internal/process/run.go\nCHANGELOG.md\n", false}, + {"generated-only", "cmd/loopexec/main.go\ndocs/cli-reference.generated.md\nCHANGELOG.md\n", false}, + {"unrelated-note", "cmd/loopexec/main.go\ndocs/status-and-roadmap.md\nCHANGELOG.md\n", false}, + {"paired", "cmd/loopexec/run.go\ndocs/workflows.md\nCHANGELOG.md\n", true}, + {"workflow-only", ".github/workflows/release.yml\n", false}, + {"policy-only", "AGENTS.md\n", false}, + {"docs-only", "docs/cli.md\n", true}, + } { + t.Run(tc.name, func(t *testing.T) { + cmd := exec.Command("bash", "../../scripts/check-docs-sync.sh") + cmd.Stdin = strings.NewReader(tc.paths) + out, err := cmd.CombinedOutput() + if (err == nil) != tc.pass { + t.Fatalf("gate: %v: %s", err, out) + } + }) + } +} + +func fixture(t *testing.T) string { + t.Helper() + root := t.TempDir() + for _, dir := range []string{"cmd/loopexec", "internal", "examples", "scripts", "tools", ".github/workflows", "docs/releases"} { + if err := os.MkdirAll(filepath.Join(root, dir), 0755); err != nil { + t.Fatal(err) + } + } + if err := os.MkdirAll(filepath.Join(root, "examples/scoped-budget"), 0755); err != nil { + t.Fatal(err) + } + files := []string{"README.md", "SPEC.md", "CHANGELOG.md", "docs/documentation-files.json", "docs/cli.md", "docs/cli-reference.generated.md", "docs/documentation-continuity.md", "docs/releases/v0.4.0.md", "examples/scoped-budget/README.md"} + for _, path := range append(append([]string{}, files...), "go.mod", "go.sum", "AGENTS.md") { + if err := os.WriteFile(filepath.Join(root, path), []byte("fixture\n"), 0644); err != nil { + t.Fatal(err) + } + } + data, err := json.Marshal(struct { + Files []string `json:"files"` + }{files}) + if err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(root, "docs/documentation-files.json"), data, 0644); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(root, "cmd/loopexec/main.go"), []byte("package main\nconst toolVersion = \"0.4.0\"\n"), 0644); err != nil { + t.Fatal(err) + } + return root +} + +func TestBundleBoundToSourceAndDocs(t *testing.T) { + root := fixture(t) + before, err := makeManifest(root) + if err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(root, "cmd/loopexec/new.go"), []byte("package main\n"), 0644); err != nil { + t.Fatal(err) + } + after, err := makeManifest(root) + if err != nil { + t.Fatal(err) + } + if before.SourceSHA256 == after.SourceSHA256 || before.DocumentationSHA256 != after.DocumentationSHA256 { + t.Fatal("source binding did not isolate source change") + } + if err := os.WriteFile(filepath.Join(root, "docs/cli.md"), []byte("changed docs\n"), 0644); err != nil { + t.Fatal(err) + } + docChanged, err := makeManifest(root) + if err != nil { + t.Fatal(err) + } + if after.DocumentationSHA256 == docChanged.DocumentationSHA256 { + t.Fatal("documentation mutation escaped binding") + } + out := filepath.Join(t.TempDir(), "documentation") + if err := writeBundle(root, out); err != nil { + t.Fatal(err) + } + if err := writeBundle(root, out); err == nil { + t.Fatal("overwrote existing bundle") + } + if _, err := verifyBundle(root, out); err != nil { + t.Fatal(err) + } + data, err := os.ReadFile(filepath.Join(out, "manifest.json")) + if err != nil { + t.Fatal(err) + } + var saved manifest + if err := decode(data, &saved); err != nil { + t.Fatal(err) + } + for _, file := range saved.Files { + data, err := safeRead(out, file.Path) + if err != nil || digest(data) != file.SHA256 { + t.Fatalf("bad bundled document %s: %v", file.Path, err) + } + } + if err := os.WriteFile(filepath.Join(out, "README.md"), []byte("tampered\n"), 0644); err != nil { + t.Fatal(err) + } + if _, err := verifyBundle(root, out); err == nil { + t.Fatal("tampered bundle accepted") + } +} + +func TestUnsafeDocumentationRejected(t *testing.T) { + for _, path := range []string{".env", "docs/private-handoff.md", "docs/.private/note.md", "docs/../../private.md", "build/private.md"} { + if publicPath(path) { + t.Fatalf("non-public path accepted: %s", path) + } + } + root := fixture(t) + if _, err := safeRead(root, "../private.txt"); err == nil { + t.Fatal("traversal accepted") + } + if err := os.Symlink(filepath.Join(root, "README.md"), filepath.Join(root, "docs/link.md")); err != nil { + t.Fatal(err) + } + if _, err := safeRead(root, "docs/link.md"); err == nil { + t.Fatal("symlink accepted") + } + if _, _, err := hashFiles(root, []string{"README.md", "README.md"}); err == nil { + t.Fatal("duplicate accepted") + } + if err := os.WriteFile(filepath.Join(root, "docs/documentation-files.json"), []byte(`{"files":["README.md"]}`), 0644); err != nil { + t.Fatal(err) + } + if _, err := makeManifest(root); err == nil { + t.Fatal("incomplete public allowlist accepted") + } +} + +func TestReleasePublicationGatesRemainWired(t *testing.T) { + data, err := os.ReadFile("../../.github/workflows/release.yml") + if err != nil { + t.Fatal(err) + } + text := string(data) + check := strings.Index(text, "verify-site") + create := strings.Index(text, "gh release create") + publish := strings.Index(text, "--draft=false") + if check < 0 || create < check || publish < create || !strings.Contains(text[create:publish], "verify-site") { + t.Fatal("deployed docs must be verified before draft creation and immediately before publication") + } + data, err = os.ReadFile("../../scripts/build-release.sh") + if err != nil { + t.Fatal(err) + } + for _, token := range []string{"TestDocumentationContract", "docs-continuity bundle", "documentation", "zip -qr"} { + if !strings.Contains(string(data), token) { + t.Fatalf("packaging gate missing: %s", token) + } + } +}