From e9b3c87533d902b9926d399954efd5f90d359375 Mon Sep 17 00:00:00 2001 From: PerishCode Date: Tue, 28 Jul 2026 17:07:17 +0800 Subject: [PATCH] guard: migrate to ectropy and plumb Replace the retired checker across local guard, Forgejo guard, and release verification. Move repository policy into the canonical ectropy.toml, adopt Sealkit 0.1.8, and keep .runseal as thin operator adapters. --- .forgejo/workflows/guard.yml | 13 +++--- .forgejo/workflows/release-beta.yml | 18 ++++---- .forgejo/workflows/release-stable.yml | 18 ++++---- .runseal/deno.lock | 6 +-- .runseal/wrappers/guard.ts | 2 + .runseal/wrappers/init.ts | 20 +++------ AGENTS.md | 64 ++++++++++++++++++--------- negentropy.toml => ectropy.toml | 54 +++++++++++----------- runseal.toml | 2 +- vocabulary.toml | 3 -- 10 files changed, 111 insertions(+), 89 deletions(-) rename negentropy.toml => ectropy.toml (88%) delete mode 100644 vocabulary.toml diff --git a/.forgejo/workflows/guard.yml b/.forgejo/workflows/guard.yml index 7f952d4..dbdfd35 100644 --- a/.forgejo/workflows/guard.yml +++ b/.forgejo/workflows/guard.yml @@ -20,17 +20,16 @@ jobs: - name: Checkout uses: actions/checkout@v6 - - name: Install pinned negentropy - run: | - set -eu - curl -fsSL https://releases.negentropy.perish.uk/manage.sh -o "$RUNNER_TEMP/manage-negentropy.sh" - sh "$RUNNER_TEMP/manage-negentropy.sh" install --channel stable --bin-dir "$HOME/.local/bin" - echo "$HOME/.local/bin" >> "$GITHUB_PATH" + - name: Install Ectropy + uses: PerishLab/actions/setup-ectropy@main + + - name: Install Plumb + uses: PerishLab/actions/setup-plumb@main - name: Guard run: | set -eu - deno run --allow-read "--allow-write=." --allow-env --allow-net "--allow-run=git,cargo,negentropy,deno,tea,sh,runseal" --config .runseal/deno.json --lock .runseal/deno.lock --frozen=true .runseal/wrappers/guard.ts --fresh + deno run --allow-read "--allow-write=." --allow-env --allow-net "--allow-run=git,cargo,ectropy,plumb,deno,tea,sh,runseal" --config .runseal/deno.json --lock .runseal/deno.lock --frozen=true .runseal/wrappers/guard.ts --fresh guard-macos: runs-on: macos diff --git a/.forgejo/workflows/release-beta.yml b/.forgejo/workflows/release-beta.yml index 43587c5..e975c9c 100644 --- a/.forgejo/workflows/release-beta.yml +++ b/.forgejo/workflows/release-beta.yml @@ -70,15 +70,17 @@ jobs: - name: CLI smoke run: cargo run --locked -p sidecar -- doctor --config examples/minimal.toml - - name: Install pinned negentropy - run: | - set -eu - curl -fsSL https://releases.negentropy.perish.uk/manage.sh -o "$RUNNER_TEMP/manage-negentropy.sh" - sh "$RUNNER_TEMP/manage-negentropy.sh" install --channel stable --bin-dir "$HOME/.local/bin" - echo "$HOME/.local/bin" >> "$GITHUB_PATH" + - name: Install Ectropy + uses: PerishLab/actions/setup-ectropy@main + + - name: Install Plumb + uses: PerishLab/actions/setup-plumb@main + + - name: Plumb + run: plumb doctor . - - name: Negentropy - run: negentropy --strict . + - name: Ectropy + run: ectropy --strict . build: needs: [metadata, verify] diff --git a/.forgejo/workflows/release-stable.yml b/.forgejo/workflows/release-stable.yml index 6fdf73e..8acc27f 100644 --- a/.forgejo/workflows/release-stable.yml +++ b/.forgejo/workflows/release-stable.yml @@ -69,15 +69,17 @@ jobs: - name: CLI smoke run: cargo run --locked -p sidecar -- doctor --config examples/minimal.toml - - name: Install pinned negentropy - run: | - set -eu - curl -fsSL https://releases.negentropy.perish.uk/manage.sh -o "$RUNNER_TEMP/manage-negentropy.sh" - sh "$RUNNER_TEMP/manage-negentropy.sh" install --channel stable --bin-dir "$HOME/.local/bin" - echo "$HOME/.local/bin" >> "$GITHUB_PATH" + - name: Install Ectropy + uses: PerishLab/actions/setup-ectropy@main + + - name: Install Plumb + uses: PerishLab/actions/setup-plumb@main + + - name: Plumb + run: plumb doctor . - - name: Negentropy - run: negentropy --strict . + - name: Ectropy + run: ectropy --strict . build: needs: [metadata, verify] diff --git a/.runseal/deno.lock b/.runseal/deno.lock index e12282c..e0f8a6c 100644 --- a/.runseal/deno.lock +++ b/.runseal/deno.lock @@ -1,13 +1,13 @@ { "version": "5", "specifiers": { - "jsr:@perish/sealkit@*": "0.1.2", + "jsr:@perish/sealkit@*": "0.1.8", "jsr:@perish/shield@0.1.0": "0.1.0", "jsr:@std/cli@1.0.30": "1.0.30" }, "jsr": { - "@perish/sealkit@0.1.2": { - "integrity": "49dbe51e044756e5c745de322ef785982b6000421ce9ebd51198f18868bafc40", + "@perish/sealkit@0.1.8": { + "integrity": "068e6078b21999452bb447c57e6d3f0dfd970228e1f14f3b419febd36cff4450", "dependencies": [ "jsr:@perish/shield", "jsr:@std/cli" diff --git a/.runseal/wrappers/guard.ts b/.runseal/wrappers/guard.ts index 480cefc..af68610 100644 --- a/.runseal/wrappers/guard.ts +++ b/.runseal/wrappers/guard.ts @@ -29,8 +29,10 @@ await guard( ".runseal/wrappers/guard.ts", ".runseal/wrappers/init.ts", ".runseal/wrappers/land.ts", + ".runseal/wrappers/release.ts", ]]], }, + { label: "plumb doctor", runs: [["plumb", ["doctor", "."]]] }, ], Deno.args, ); diff --git a/.runseal/wrappers/init.ts b/.runseal/wrappers/init.ts index c9a1bab..50c8837 100644 --- a/.runseal/wrappers/init.ts +++ b/.runseal/wrappers/init.ts @@ -1,15 +1,4 @@ -import { cli, flags } from "@perish/sealkit/cli"; import { init } from "@perish/sealkit/init"; -import { io } from "@perish/sealkit/io"; - -const args = cli.parse(Deno.args, { boolean: ["help", "h"] }); -flags(args).positionals("init", { allowHelp: true }); -if (flags(args).help()) { - io.print("Usage: runseal :init"); - io.print(""); - io.print("Validate the repository and install versioned git hooks."); - Deno.exit(0); -} await init({ tools: [ @@ -19,19 +8,24 @@ await init({ "tea", "runseal", "sh", + "ectropy", + "plumb", ], paths: [ "Cargo.toml", "Cargo.lock", - "negentropy.toml", - "vocabulary.toml", + "ectropy.toml", "manage.sh", + "manage.ps1", "runseal.toml", ".runseal/deno.json", ".runseal/deno.lock", ".runseal/wrappers/guard.ts", ".runseal/wrappers/init.ts", ".runseal/wrappers/land.ts", + ".runseal/wrappers/release.ts", ".forgejo/workflows/guard.yml", + ".forgejo/workflows/release-beta.yml", + ".forgejo/workflows/release-stable.yml", ], }); diff --git a/AGENTS.md b/AGENTS.md index b84c067..2897390 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -44,7 +44,7 @@ The TCP broker is local service discovery and runtime registry for host processe ## Build-time Stamps -`crates/cli` reads three optional build-time env vars via `option_env!` and bakes them into the binary; `.github/scripts/release/assets/package.{sh,ps1}` set all three from the release workflow: +`crates/cli` reads three optional build-time env vars via `option_env!` and bakes them into the binary; `.forgejo/scripts/release/assets/package.{sh,ps1}` set all three from the release workflow: - `SIDECAR_BUILD_VERSION` → `cli::version()` (defaults to `v` for dev builds). - `SIDECAR_BUILD_CHANNEL` → `cli::channel()` (`stable` / `beta` / `dev`; defaults to `dev`, which disables the startup check and `update` subcommand). @@ -102,20 +102,33 @@ Root `manage.{sh,ps1}` accept exactly: `install`, `update`, `uninstall`. There i ## Repo-local Support -`runseal.toml` and `.runseal/wrappers/*` are the repo-local operator entrypoints for support tasks that do not belong in the installable `sidecar` product binary. The wrappers are Deno TypeScript run through `runseal`; local development requires `runseal`, `deno`, and a `negentropy` binary matching the pin in `.runseal/negentropy.version`. Current support commands: - -- `runseal :init` — idempotent post-clone validator. It quick-fails on missing required tools (git, deno, cargo, gh, runseal, the pinned negentropy) or repository entrypoints, and exits cleanly only when the checkout is ready for development. -- `runseal :guard` — the full local gate: fmt, clippy, tests, `deno fmt` / `deno check` over `.runseal`, and the pinned `negentropy --strict .`. -- `runseal :land` — lands the current clean topic branch: push, create or reuse the PR, await the checks on the exact pushed head SHA, squash-merge pinned to that SHA, sync `main`, delete the branch. `--dry-run` prints the plan without touching git or GitHub. -- `runseal :cloudflare` — repo-local Cloudflare support for checking credentials and ensuring exact-path `sidecar.perish.uk/manage.sh|ps1` redirects to the release bucket. Use `manage-ensure-redirect --dry-run` before applying changes. +`runseal.toml` and `.runseal/wrappers/*` are thin repo-local operator +entrypoints for support tasks that do not belong in the installable `sidecar` +product binary. Shared operator logic belongs in Sealkit; `.runseal` contains +no dedicated TypeScript tests. Local development requires `runseal`, `deno`, +Ectropy, and Plumb. Current support commands: + +- `runseal :init` — idempotent post-clone validator for tools, repository + entrypoints, and versioned Git hooks. +- `runseal :guard` — the full local gate: fmt, clippy, tests, Deno checks, + `plumb doctor .`, and `ectropy --strict .`. +- `runseal :land` — lands the current clean topic branch through Forgejo, + waits for checks on the exact pushed head SHA, squash-merges that SHA, syncs + `main`, and deletes the branch. `--dry-run` prints the plan without mutation. +- `runseal :release` — dispatches the stable or beta Forgejo release workflow. ## Constitution -`negentropy` is the structure checker for this repository; `negentropy --strict .` must print `clean` before anything lands. Its configuration is repo-owned: +Ectropy owns pure AST syntax execution. Plumb owns repository shape, the +canonical `ectropy.toml` policy, and which paths receive syntax grants. Both +must pass before anything lands: -- `negentropy.toml` — scan roots (`crates/**/*.rs`, `docs/**/*.md`), module roots, block/path depth limits, the comment ban, the single-word identifier rule, and the test-syntax grant for `crates/*/tests`. -- `vocabulary.toml` — registered compound atoms; while it is empty, every identifier must stay a single word. -- `.runseal/negentropy.version` — the pinned checker version (currently `v0.1.0-beta.9`). `runseal :init`, `runseal :guard`, and CI all verify the installed binary against this pin and refuse a mismatch. +- `ectropy.toml` — scan roots (`crates/**/*.rs`, `docs/**/*.md`), module roots, + limits, the comment ban, the single-word rule, and explicit test/environment + grants. +- `plumb doctor .` — repository layout, operator, workflow, and policy + enforcement. +- `ectropy --strict .` — syntax execution against that policy. ## Common Commands @@ -124,7 +137,7 @@ Root `manage.{sh,ps1}` accept exactly: `install`, `update`, `uninstall`. There i - Clippy: `cargo clippy --locked --workspace --all-targets -- -D warnings` - CLI smoke: `cargo run --locked -p cli -- doctor --config examples/minimal.toml` - Plan: `cargo run --locked -p cli -- plan --config examples/minimal.toml --format json` -- Constitution check: `negentropy --strict .` +- Repository check: `plumb doctor . && ectropy --strict .` - Full gate: `runseal :guard` ## Repository Shape @@ -133,9 +146,11 @@ Root `manage.{sh,ps1}` accept exactly: `install`, `update`, `uninstall`. There i - `crates/cli/`: CLI parsing, lifecycle execution (`start`/`stop`/`restart`/`status`/`list`/`reset`), `inspect [payload]`, output formatting, exit behavior. - `manage.sh` and `manage.ps1`: public install/update/uninstall manager entrypoints uploaded as release assets. - `docs/`: durable design notes for planned architecture changes, including the TCP broker runtime direction. -- `.runseal/`: runseal wrapper entrypoints (`guard.ts`, `init.ts`, `land.ts`, `cloudflare.seal`), the shared wrapper `lib/`, and the `negentropy.version` pin. -- `negentropy.toml` and `vocabulary.toml`: the constitution the `negentropy` checker enforces over `crates/` and `docs/`. -- `.github/scripts/`: workflow-only release helpers. +- `.runseal/`: thin runseal wrapper entrypoints for guard, init, land, and + release. +- `ectropy.toml`: the Plumb-managed syntax policy Ectropy executes over + `crates/` and `docs/`. +- `.forgejo/scripts/`: workflow-only release helpers. ## Standard Workflow @@ -147,7 +162,9 @@ After cloning or when the toolchain looks stale, run: runseal :init ``` -It validates the required tools (including a `negentropy` matching `.runseal/negentropy.version`) and the repository entrypoints, then exits. It installs nothing: there are no local git hooks. The gates are `runseal :guard` before landing and the `guard` workflow in CI. +It validates the required tools and repository entrypoints, then installs the +versioned Git hooks. The gates are `runseal :guard` before landing and the +`guard` workflow in CI. ### Branch Names @@ -178,10 +195,13 @@ cargo clippy --locked --workspace --all-targets -- -D warnings cargo test --locked --workspace deno fmt --check .runseal deno check --config .runseal/deno.json --lock .runseal/deno.lock --frozen=true .runseal/wrappers/*.ts -negentropy --strict . +plumb doctor . +ectropy --strict . ``` -CI reruns the same wrapper: `.github/workflows/guard.yml` installs the pinned negentropy release, then executes `.runseal/wrappers/guard.ts` on every PR and every push to `main`. +CI reruns the same wrapper: `.forgejo/workflows/guard.yml` installs Ectropy and +Plumb through the shared actions, then executes `.runseal/wrappers/guard.ts` on +every PR and every push to `main`. ### PR Descriptions @@ -202,7 +222,9 @@ Add `## Compatibility` when a manifest field, CLI flag, protocol field, output s ### Merging -`main` is PR-only and protected by the repository ruleset `main guard`. The required merge gate is the `guard` check from `.github/workflows/guard.yml` (currently `guard (ubuntu-latest)`). Required approvals are intentionally `0`. +`main` is PR-only and protected by the repository ruleset `main guard`. The +required merge gate is the `guard` check from `.forgejo/workflows/guard.yml`. +Required approvals are intentionally `0`. From a clean topic branch, default to landing with: @@ -210,7 +232,9 @@ From a clean topic branch, default to landing with: runseal :land ``` -It pushes the branch, creates or reuses the PR, records the exact pushed head SHA, polls the GitHub check-runs on that SHA until every one succeeds, squash-merges with `--match-head-commit ` so only the audited commit can land, syncs `main`, and deletes the branch. If `:land` is unavailable, wait for green checks and fall back to `gh pr merge --squash --delete-branch`. +It pushes the branch, creates or reuses the PR, records the exact pushed head +SHA, polls the Forgejo checks on that SHA until every one succeeds, +squash-merges only the audited commit, syncs `main`, and deletes the branch. ## Stamp args protocol diff --git a/negentropy.toml b/ectropy.toml similarity index 88% rename from negentropy.toml rename to ectropy.toml index 8681bca..f20c823 100644 --- a/negentropy.toml +++ b/ectropy.toml @@ -1,41 +1,43 @@ -[scan] -include = [ - "crates/**/*.rs", - "docs/**/*.md", -] -exclude = [ - "**/target/**", -] - -[module] -roots = [ - "crates/*/src", - "crates/*/tests", - "docs", -] - -[limit] -block = 4 -path = 4 - [comment] allow = false -[word] -single = true - [[grant]] -syntax = "test" paths = ["crates/*/tests/**/*.rs"] +syntax = "test" [[grant]] -syntax = "environment" paths = [ - "crates/cli/src/main.rs", "crates/cli/src/args.rs", "crates/cli/src/cli.rs", "crates/cli/src/commands/runtime.rs", + "crates/cli/src/main.rs", "crates/cli/src/update.rs", "crates/core/src/paths.rs", "crates/*/tests/**/*.rs", ] +syntax = "environment" + +[limit] +block = 4 +fanout = 10 +file = 300 +markup = 8 +param = 4 +path = 4 + +[module] +roots = [ + "crates/*/src", + "crates/*/tests", + "docs", +] + +[scan] +exclude = ["**/target/**"] +include = [ + "crates/**/*.rs", + "docs/**/*.md", +] + +[word] +single = true diff --git a/runseal.toml b/runseal.toml index 101759d..ac725eb 100644 --- a/runseal.toml +++ b/runseal.toml @@ -9,7 +9,7 @@ permissions = [ "--allow-write=.", "--allow-env", "--allow-net", - "--allow-run=git,cargo,negentropy,deno,tea,sh,runseal", + "--allow-run=git,cargo,ectropy,plumb,deno,tea,sh,runseal", ] [[injections]] diff --git a/vocabulary.toml b/vocabulary.toml deleted file mode 100644 index 870a531..0000000 --- a/vocabulary.toml +++ /dev/null @@ -1,3 +0,0 @@ -# vocabulary.toml — registered compound atoms. - -[compound]