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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,22 @@ 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:
BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.before }}
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
Expand All @@ -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
Expand Down
29 changes: 28 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,57 @@ name: Release
on:
push:
tags: ['v*']
workflow_dispatch:

permissions:
contents: read

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 }}
RELEASE_TAG: ${{ github.ref_name }}
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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/
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Loading
Loading