docs: split maintainer agent guidance

Move public maintainer policy and automation guidance into maintainers/, shrink root AGENTS.md into an audience router, and ignore internal .agent scratch state.

Delete tracked internal ExecPlans from .agent and scrub personal/private references from public RFC examples.

Checks: git diff --cached --check; scripts/check-flake-lock-owners.sh; node scripts/select-openclaw-release.test.mjs; bash -n scripts/update-pins.sh; ruby -e 'require "yaml"; YAML.load_file(".github/workflows/yolo-update.yml"); YAML.load_file(".github/workflows/ci.yml")'
This commit is contained in:
joshp123 2026-05-06 09:28:13 +02:00
parent d213d242e7
commit 5e186c192f
11 changed files with 187 additions and 634 deletions

View File

@ -1,172 +0,0 @@
# Codex Execution Plans (ExecPlans):
This document describes the requirements for an execution plan ("ExecPlan"), a design document that a coding agent can follow to deliver a working feature or system change. Treat the reader as a complete beginner to this repository: they have only the current working tree and the single ExecPlan file you provide. There is no memory of prior plans and no external context.
## How to use ExecPlans and PLANS.md
When authoring an executable specification (ExecPlan), follow PLANS.md _to the letter_. If it is not in your context, refresh your memory by reading the entire PLANS.md file. Be thorough in reading (and re-reading) source material to produce an accurate specification. When creating a spec, start from the skeleton and flesh it out as you do your research.
When implementing an executable specification (ExecPlan), do not prompt the user for "next steps"; simply proceed to the next milestone. Keep all sections up to date, add or split entries in the list at every stopping point to affirmatively state the progress made and next steps. Resolve ambiguities autonomously, and commit frequently.
When discussing an executable specification (ExecPlan), record decisions in a log in the spec for posterity; it should be unambiguously clear why any change to the specification was made. ExecPlans are living documents, and it should always be possible to restart from _only_ the ExecPlan and no other work.
When researching a design with challenging requirements or significant unknowns, use milestones to implement proof of concepts, "toy implementations", etc., that allow validating whether the user's proposal is feasible. Read the source code of libraries by finding or acquiring them, research deeply, and include prototypes to guide a fuller implementation.
## Requirements
NON-NEGOTIABLE REQUIREMENTS:
* Every ExecPlan must be fully self-contained. Self-contained means that in its current form it contains all knowledge and instructions needed for a novice to succeed.
* Every ExecPlan is a living document. Contributors are required to revise it as progress is made, as discoveries occur, and as design decisions are finalized. Each revision must remain fully self-contained.
* Every ExecPlan must enable a complete novice to implement the feature end-to-end without prior knowledge of this repo.
* Every ExecPlan must produce a demonstrably working behavior, not merely code changes to "meet a definition".
* Every ExecPlan must define every term of art in plain language or do not use it.
Purpose and intent come first. Begin by explaining, in a few sentences, why the work matters from a user's perspective: what someone can do after this change that they could not do before, and how to see it working. Then guide the reader through the exact steps to achieve that outcome, including what to edit, what to run, and what they should observe.
The agent executing your plan can list files, read files, search, run the project, and run tests. It does not know any prior context and cannot infer what you meant from earlier milestones. Repeat any assumption you rely on. Do not point to external blogs or docs; if knowledge is required, embed it in the plan itself in your own words. If an ExecPlan builds upon a prior ExecPlan and that file is checked in, incorporate it by reference. If it is not, you must include all relevant context from that plan.
## Formatting
Format and envelope are simple and strict. Each ExecPlan must be one single fenced code block labeled as `md` that begins and ends with triple backticks. Do not nest additional triple-backtick code fences inside; when you need to show commands, transcripts, diffs, or code, present them as indented blocks within that single fence. Use indentation for clarity rather than code fences inside an ExecPlan to avoid prematurely closing the ExecPlan's code fence. Use two newlines after every heading, use # and ## and so on, and correct syntax for ordered and unordered lists.
When writing an ExecPlan to a Markdown (.md) file where the content of the file *is only* the single ExecPlan, you should omit the triple backticks.
Write in plain prose. Prefer sentences over lists. Avoid checklists, tables, and long enumerations unless brevity would obscure meaning. Checklists are permitted only in the `Progress` section, where they are mandatory. Narrative sections must remain prose-first.
## Guidelines
Self-containment and plain language are paramount. If you introduce a phrase that is not ordinary English ("daemon", "middleware", "RPC gateway", "filter graph"), define it immediately and remind the reader how it manifests in this repository (for example, by naming the files or commands where it appears). Do not say "as defined previously" or "according to the architecture doc." Include the needed explanation here, even if you repeat yourself.
Avoid common failure modes. Do not rely on undefined jargon. Do not describe "the letter of a feature" so narrowly that the resulting code compiles but does nothing meaningful. Do not outsource key decisions to the reader. When ambiguity exists, resolve it in the plan itself and explain why you chose that path. Err on the side of over-explaining user-visible effects and under-specifying incidental implementation details.
Anchor the plan with observable outcomes. State what the user can do after implementation, the commands to run, and the outputs they should see. Acceptance should be phrased as behavior a human can verify ("after starting the server, navigating to [http://localhost:8080/health](http://localhost:8080/health) returns HTTP 200 with body OK") rather than internal attributes ("added a HealthCheck struct"). If a change is internal, explain how its impact can still be demonstrated (for example, by running tests that fail before and pass after, and by showing a scenario that uses the new behavior).
Specify repository context explicitly. Name files with full repository-relative paths, name functions and modules precisely, and describe where new files should be created. If touching multiple areas, include a short orientation paragraph that explains how those parts fit together so a novice can navigate confidently. When running commands, show the working directory and exact command line. When outcomes depend on environment, state the assumptions and provide alternatives when reasonable.
Be idempotent and safe. Write the steps so they can be run multiple times without causing damage or drift. If a step can fail halfway, include how to retry or adapt. If a migration or destructive operation is necessary, spell out backups or safe fallbacks. Prefer additive, testable changes that can be validated as you go.
Validation is not optional. Include instructions to run tests, to start the system if applicable, and to observe it doing something useful. Describe comprehensive testing for any new features or capabilities. Include expected outputs and error messages so a novice can tell success from failure. Where possible, show how to prove that the change is effective beyond compilation (for example, through a small end-to-end scenario, a CLI invocation, or an HTTP request/response transcript). State the exact test commands appropriate to the projects toolchain and how to interpret their results.
Capture evidence. When your steps produce terminal output, short diffs, or logs, include them inside the single fenced block as indented examples. Keep them concise and focused on what proves success. If you need to include a patch, prefer file-scoped diffs or small excerpts that a reader can recreate by following your instructions rather than pasting large blobs.
## Design quality lens
Use John Ousterhout's design philosophy as the default lens for shaping the plan and resolving design ambiguity.
Prefer simple mental models over elegant-looking structure. Prefer deep modules over shallow wrappers. Prefer interfaces that hide sequencing and policy details. Prefer fewer concepts, fewer knobs, and fewer special cases. Prefer moving complexity behind a stable boundary over redistributing it across more files.
Treat these as the main forms of complexity:
* Change amplification: one logical change requires edits in many places.
* Cognitive load: a reader or caller must hold too many facts in mind.
* Unknown unknowns: important behavior is surprising, implicit, or scattered.
Every ExecPlan should explain the design quality of the proposed change, not just the mechanics. Make it clear:
* what complexity exists today and who pays for it
* what boundary, module, or interface becomes simpler after the change
* what knowledge, sequencing, or policy moves out of callers and into the implementation
* what concepts, branches, or special cases disappear
* what the complexity dividend is for future readers and future changes
Do not mistake motion for simplification. A plan is weaker if it mainly adds wrappers, adapters, flags, layers, or configuration without hiding more detail from the rest of the system. If a new abstraction is required, state exactly what it hides and why the system is simpler with it than without it.
## Milestones
Milestones are narrative, not bureaucracy. If you break the work into milestones, introduce each with a brief paragraph that describes the scope, what will exist at the end of the milestone that did not exist before, the commands to run, and the acceptance you expect to observe. Keep it readable as a story: goal, work, result, proof. Progress and milestones are distinct: milestones tell the story, progress tracks granular work. Both must exist. Never abbreviate a milestone merely for the sake of brevity, do not leave out details that could be crucial to a future implementation.
Each milestone must be independently verifiable and incrementally implement the overall goal of the execution plan.
## Living plans and design decisions
* ExecPlans are living documents. As you make key design decisions, update the plan to record both the decision and the thinking behind it. Record all decisions in the `Decision Log` section.
* ExecPlans must contain and maintain a `Progress` section, a `Surprises & Discoveries` section, a `Decision Log`, and an `Outcomes & Retrospective` section. These are not optional.
* When you discover optimizer behavior, performance tradeoffs, unexpected bugs, or inverse/unapply semantics that shaped your approach, capture those observations in the `Surprises & Discoveries` section with short evidence snippets (test output is ideal).
* If you change course mid-implementation, document why in the `Decision Log` and reflect the implications in `Progress`. Plans are guides for the next contributor as much as checklists for you.
* At completion of a major task or the full plan, write an `Outcomes & Retrospective` entry summarizing what was achieved, what remains, and lessons learned.
# Prototyping milestones and parallel implementations
It is acceptable—-and often encouraged—-to include explicit prototyping milestones when they de-risk a larger change. Examples: adding a low-level operator to a dependency to validate feasibility, or exploring two composition orders while measuring optimizer effects. Keep prototypes additive and testable. Clearly label the scope as “prototyping”; describe how to run and observe results; and state the criteria for promoting or discarding the prototype.
Prefer additive code changes followed by subtractions that keep tests passing. Parallel implementations (e.g., keeping an adapter alongside an older path during migration) are fine when they reduce risk or enable tests to continue passing during a large migration. Describe how to validate both paths and how to retire one safely with tests. When working with multiple new libraries or feature areas, consider creating spikes that evaluate the feasibility of these features _independently_ of one another, proving that the external library performs as expected and implements the features we need in isolation.
## Skeleton of a Good ExecPlan
# <Short, action-oriented description>
This ExecPlan is a living document. The sections `Progress`, `Surprises & Discoveries`, `Decision Log`, and `Outcomes & Retrospective` must be kept up to date as work proceeds.
If PLANS.md file is checked into the repo, reference the path to that file here from the repository root and note that this document must be maintained in accordance with PLANS.md.
## Purpose / Big Picture
Explain in a few sentences what someone gains after this change and how they can see it working. State the user-visible behavior you will enable. Also explain what current complexity or interface burden this change removes.
## Progress
Use a list with checkboxes to summarize granular steps. Every stopping point must be documented here, even if it requires splitting a partially completed task into two (“done” vs. “remaining”). This section must always reflect the actual current state of the work.
- [x] (2025-10-01 13:00Z) Example completed step.
- [ ] Example incomplete step.
- [ ] Example partially completed step (completed: X; remaining: Y).
Use timestamps to measure rates of progress.
## Surprises & Discoveries
Document unexpected behaviors, bugs, optimizations, or insights discovered during implementation. Provide concise evidence.
- Observation: …
Evidence: …
## Decision Log
Record every decision made while working on the plan in the format:
- Decision: …
Rationale: …
Date/Author: …
## Outcomes & Retrospective
Summarize outcomes, gaps, and lessons learned at major milestones or at completion. Compare the result against the original purpose.
## Context and Orientation
Describe the current state relevant to this task as if the reader knows nothing. Name the key files and modules by full path. Define any non-obvious term you will use. Do not refer to prior plans. Make clear what callers or maintainers currently need to know that they should not need to know after the change.
## Plan of Work
Describe, in prose, the sequence of edits and additions. For each edit, name the file and location (function, module) and what to insert or change. Keep it concrete and minimal. Explain how each major edit deepens a module, hides sequencing or policy, removes a special case, or otherwise reduces system complexity.
## Concrete Steps
State the exact commands to run and where to run them (working directory). When a command generates output, show a short expected transcript so the reader can compare. This section must be updated as work proceeds.
## Validation and Acceptance
Describe how to start or exercise the system and what to observe. Phrase acceptance as behavior, with specific inputs and outputs. If tests are involved, say "run <projects test command> and expect <N> passed; the new test <name> fails before the change and passes after>".
## Idempotence and Recovery
If steps can be repeated safely, say so. If a step is risky, provide a safe retry or rollback path. Keep the environment clean after completion.
## Artifacts and Notes
Include the most important transcripts, diffs, or snippets as indented examples. Keep them concise and focused on what proves success.
## Interfaces and Dependencies
Be prescriptive. Name the libraries, modules, and services to use and why. Specify the types, traits/interfaces, and function signatures that must exist at the end of the milestone. Prefer stable names and paths such as `crate::module::function` or `package.submodule.Interface`. For each major interface, say what detail it hides from its callers. E.g.:
In crates/foo/planner.rs, define:
pub trait Planner {
fn plan(&self, observed: &Observed) -> Vec<Action>;
}
If you follow the guidance above, a single, stateless agent -- or a human novice -- can read your ExecPlan from top to bottom and produce a working, observable result. That is the bar: SELF-CONTAINED, SELF-SUFFICIENT, NOVICE-GUIDING, OUTCOME-FOCUSED.
When you revise a plan, you must ensure your changes are comprehensively reflected across all sections, including the living document sections, and you must write a note at the bottom of the plan describing the change and the reason why. ExecPlans must describe not just the what but the why for almost everything.

View File

@ -1,350 +0,0 @@
# Stabilize OpenClaw Nix Packaging Around Runnable Capabilities
This ExecPlan is a living document. The sections `Progress`, `Surprises & Discoveries`, `Decision Log`, and `Outcomes & Retrospective` must be kept up to date as work proceeds.
This document follows `.agent/PLANS.md` in this repository.
## Purpose / Big Picture
The goal is to make `openclaw/nix-openclaw` a reliable Nix packaging surface for OpenClaw users on Linux and macOS. A user should be able to install OpenClaw from this flake, build it with Nix, and run the gateway on Linux or macOS. On macOS, the flake should also provide the desktop `.app` when upstream has published a usable desktop artifact for the same OpenClaw release.
The current system is too brittle because it treats "latest stable GitHub release has a file named `OpenClaw-*.zip`" as the release contract. Upstream release flow does not work that way: OpenClaw source tags and GitHub releases can exist before private macOS signing/notarization workflows upload `.zip`, `.dmg`, and `.dSYM.zip` assets. Missing desktop assets are a release-publication state, not proof that the source gateway cannot be packaged.
After this work, the user-facing package boundary should be simple: users install `openclaw`. The `openclaw` output is the batteries-included bundle: gateway plus tools on Linux, and gateway plus tools plus the macOS app on Darwin. The `openclaw-gateway`, `openclaw-tools`, and `openclaw-app` outputs remain as component outputs for Nix modules, checks, and debugging, but they are not separate product tracks. The update automation should pick the newest fully packageable stable release for default promotion, while separately reporting newer source-only releases that are not yet full desktop releases.
## Progress
- [x] (2026-05-04 17:02Z) Assessed current repository state: `openclaw/nix-openclaw` is pinned to OpenClaw `v2026.4.14`; hourly yolo has failed since mid-April; latest upstream stable releases `v2026.5.3` and `v2026.5.3-1` currently have no public app assets; latest stable with public macOS zip asset is `v2026.5.2`.
- [x] (2026-05-04 17:02Z) Read OpenClaw public release workflows and maintainer release docs in `/Users/josh/code/research/openclaw` and `/Users/josh/code/research/maintainers`.
- [x] (2026-05-04 17:02Z) Determined DJTBOT deployment freshness is out of scope for this plan; this plan focuses only on public Nix packaging.
- [x] (2026-05-04 17:42Z) Read the current global agent guidance in `/Users/josh/.codex/AGENTS.md`, `/Users/josh/code/nix/AGENTS.md`, and `/Users/josh/code/nix/ai-stack/AGENTS.md`; refined this plan around one obvious path, no speculative fallbacks, and existing repo seams.
- [x] (2026-05-04 17:42Z) Re-checked the package graph and upstream gateway CLI. Confirmed `openclaw` is already the default package, `openclaw-gateway` is the source-built CLI component, and runtime smoke should use WebSocket gateway health rather than HTTP `/health`.
- [x] (2026-05-04 17:55Z) Captured Josh's README direction: onboarding should be agent-first, where the user tells a coding agent they want OpenClaw using Nix and the agent interviews/configures/verifies.
- [x] (2026-05-04 19:49Z) Updated README package language so `openclaw` is the single canonical install target, the primary setup flow is agent-first, and component outputs are documented as advanced/internal build seams.
- [x] (2026-05-04 19:49Z) Implemented release discovery that understands package capabilities instead of failing on the first assetless stable release.
- [x] (2026-05-04 19:49Z) Fixed app artifact hashing/unpacking so Nix computes the exact `fetchzip` hash from real unpacked contents and aborts clearly if no `.app` is present.
- [x] (2026-05-04 19:49Z) Repaired config validation so it uses packaged public CLI behavior instead of scanning bundled `dist/config-*.js` internals.
- [x] (2026-05-04 19:49Z) Added runtime smoke checks that prove the Nix-built gateway starts and answers a local health/RPC probe on Linux and macOS.
- [x] (2026-05-04 19:49Z) Updated CI/yolo promotion to validate and promote the newest full packageable stable release, with source-only newer releases reported but not promoted into the default bundle.
- [x] (2026-05-04 19:49Z) Updated README packaging docs after behavior changes were implemented and verified.
- [x] (2026-05-04 19:49Z) Fixed the local Linux external-builder failure by moving the OpenClaw build working tree and PNPM store onto the Nix output filesystem during builds, then cleaning that scratch before outputs finish.
- [x] (2026-05-04 19:49Z) Verified final gates: selector fixture, updater syntax, workflow YAML parse, live selector no-op, `nix flake show`, full Linux CI, and full Darwin CI.
- [x] (2026-05-05 06:45Z) Created daily Codex maintainer automation `nix-openclaw-maintainer` for early Amsterdam mornings; documented that it is an agentic repair run, not a competing release pipeline.
- [x] (2026-05-05 06:45Z) Ran a fresh self-review pass and tightened noisy Home Manager VM diagnostics by removing a space-containing `NODE_OPTIONS` systemd assignment.
- [x] (2026-05-05 06:45Z) Fixed a self-review finding in yolo promotion: validation jobs now record the materialized release diff digest, and promotion refuses to push if it re-materializes a different diff.
- [x] (2026-05-05 06:45Z) Removed the dead standalone config-options check file and renamed the combined gateway/config-options derivation to `openclaw-source-checks.nix`.
## Surprises & Discoveries
- Observation: OpenClaw's public `macos-release.yml` workflow is validation-only.
Evidence: `openclaw/openclaw/.github/workflows/macos-release.yml` says it validates the public release handoff and does not sign, notarize, or upload macOS assets.
- Observation: Real macOS release assets are produced by private workflows after the public GitHub release exists.
Evidence: `/Users/josh/code/research/maintainers/release/macos.md` says `openclaw/releases-private/.github/workflows/openclaw-macos-publish.yml` uploads `OpenClaw-<version>.zip`, `OpenClaw-<version>.dmg`, and `OpenClaw-<version>.dSYM.zip` to the existing public GitHub release.
- Observation: Recent stable OpenClaw releases are not all full desktop releases at the time yolo sees them.
Evidence: `gh api '/repos/openclaw/openclaw/releases?per_page=30'` showed `v2026.5.3-1`, `v2026.5.3`, `v2026.4.23`, and `v2026.4.21` as stable releases with no assets, while nearby stable releases such as `v2026.5.2`, `v2026.4.29`, and `v2026.4.27` had `.zip`, `.dmg`, and `.dSYM.zip`.
- Observation: The current app hash helper is wrong for current Nix behavior.
Evidence: yolo logs show `nix-prefetch-url` prints a real `/nix/store/...-OpenClaw-<version>.zip` path, but `scripts/update-pins.sh` treats the final hash line as a path component and synthesizes a different non-existent store path before calling `unzip`.
- Observation: Building the macOS `.app` from source inside this flake is not the right first move.
Evidence: upstream `scripts/package-mac-app.sh` depends on SwiftPM, Xcode 26.1, Sparkle, Developer ID signing, notarization-adjacent packaging, MLX TTS helper builds, Control UI assets, and private release workflows. The Nix flake already has a clean boundary for the desktop app: use the published app bundle artifact on Darwin.
- Observation: At planning time, the checked upstream OpenClaw source did not expose `openclaw config validate --json`.
Evidence: `/Users/josh/code/research/openclaw/src/cli/config-cli.ts` registers `config get`, `config set`, and `config unset`, but no `config validate`. `config get` calls `loadValidConfig()`, so `openclaw config get gateway --json` is the current public CLI path that proves the generated config can be loaded and validated.
- Observation: The selected packageable release `v2026.5.2` does expose `openclaw config validate --json`.
Evidence: `nix/scripts/check-config-validity.mjs` now runs the packaged CLI's `config validate --json` and then checks `config get agents.defaults.workspace --json` against the generated Home Manager config.
- Observation: Gateway liveness must be proved through WebSocket RPC, not HTTP.
Evidence: upstream registers `openclaw gateway health` and `openclaw gateway call health`, both backed by the gateway `health` RPC method. The main HTTP dispatcher has no stable `/health` route, and unmatched HTTP paths return 404.
- Observation: Determinate's local Linux external builder exposes only about 3.9 GiB for `/build`, while the OpenClaw PNPM store tar expands to about 7.8 GiB.
Evidence: a tiny `x86_64-linux` derivation reported `/build` on a 3.9 GiB tmpfs, and `pnpm-store.tar.zst` contains 61,553 tar entries totaling about 7.80 GiB. The first Linux gateway build failed at PNPM-store extraction with `No space left on device`.
- Observation: The Nix-built gateway hit an upstream runtime guard that rejected Nix-store hardlinks in bundled plugin public surface files.
Evidence: a runtime smoke attempt failed opening `anthropic/provider-policy-api.js` because `openBoundaryFileSync` rejected files with link count greater than one. Nix store deduplication can hardlink immutable package files, so the patch now allows hardlinks only under `OPENCLAW_PACKAGE_ROOT`.
## Decision Log
- Decision: Keep one public default package line based on the newest fully packageable stable release, not the newest source tag when desktop assets are missing.
Rationale: The README promises "Gateway + tools everywhere; macOS app on macOS." Promoting a source-only release would either drop the macOS app from the default Darwin bundle or create a source/app version mismatch. Both are worse for users than staying on the latest complete release while reporting newer incomplete releases.
Date/Author: 2026-05-04 / Codex
- Decision: Do not split the repository into separate "desktop" and "server" release tracks.
Rationale: The package outputs should be segmented, not the product mental model. `openclaw-gateway` is the headless/server-capable source build, `openclaw-app` is Darwin-only desktop, and `openclaw` combines the appropriate outputs for the platform. Callers should not need to know upstream release timing details.
Date/Author: 2026-05-04 / Codex
- Decision: Treat the upstream macOS zip as the preferred desktop app artifact, but validate artifact contents instead of trusting only the filename.
Rationale: Maintainer docs define the real publish outputs as `.zip`, `.dmg`, and `.dSYM.zip`. The Sparkle zip is the most Nix-friendly app-bundle source. The robust boundary is to pick a plausible desktop artifact and verify it actually unpacks to an `.app`; filename matching alone is not enough.
Date/Author: 2026-05-04 / Codex
- Decision: Add runtime smoke checks before changing deployment users such as DJTBOT.
Rationale: The user's goal is public package health. Deployment freshness is downstream of packaging correctness and is intentionally out of scope for this plan.
Date/Author: 2026-05-04 / Codex
- Decision: Make `openclaw` the only canonical user-facing package name, while keeping `openclaw-gateway`, `openclaw-tools`, and `openclaw-app` as component outputs.
Rationale: Josh wants one thing to install, and the README already promises "One flake, everything works." Removing component outputs would make CI, Home Manager module wiring, macOS app packaging, and debugging harder without simplifying the user path. The simpler mental model is: users install `openclaw`; maintainers may inspect component outputs when diagnosing packaging.
Date/Author: 2026-05-04 / Codex
- Decision: Make the README agent-first.
Rationale: The intended user is not supposed to become a Nix/OpenClaw packaging expert. The cleanest onboarding is to tell a coding agent "set up OpenClaw with Nix," then let the agent inspect the machine, ask the small number of setup questions, write the local flake, configure secrets, apply Home Manager, and verify the service. Manual commands should remain as reference and recovery material, not the primary story.
Date/Author: 2026-05-04 / Josh/Codex
- Decision: Use output-backed scratch for OpenClaw Node builds.
Rationale: The package must build on the local Linux external builder and in CI without assuming a large `/build` filesystem. Moving the build root and PNPM store under `$out` during the build uses the Nix store filesystem, then the scripts remove `.pnpm-store` and `.openclaw-build` before the output is finalized.
Date/Author: 2026-05-04 / Codex
- Decision: Make the Nix gateway Vitest runner resource-bounded but not fragile.
Rationale: The one-CPU Linux builder timed out and then hit JS heap OOM with upstream's full gateway suite. The Nix check now defaults to one worker, fork pool, a 60s per-test timeout, and a 4 GiB Node heap so constrained builders fail real hangs without failing normal slow tests.
Date/Author: 2026-05-04 / Codex
- Decision: Use the daily Codex automation as an agentic maintainer repair loop, not as another updater.
Rationale: Yolo is already the release updater. The daily run should check whether yolo and CI can still uphold the package contract, then fix nix-openclaw itself when the breakage is in this repo. It may commit and push directly to `main` only after self-review and full gates pass.
Date/Author: 2026-05-05 / Josh/Codex
## Outcomes & Retrospective
Implemented. The repo now promotes the newest stable release that satisfies the public Nix package contract, not blindly the newest stable source tag. As of 2026-05-04, that is `v2026.5.2`; `v2026.5.3-1` and `v2026.5.3` are reported as skipped because they do not publish the required public macOS zip asset.
The work also found and fixed two packaging bugs that were not obvious from release selection alone: Nix-store hardlinks tripped OpenClaw's bundled plugin public-surface guard, and the local Linux external builder could not fit the PNPM store in `/build`. Both are now handled in the Nix-owned packaging layer.
Final local verification passed:
node scripts/select-openclaw-release.test.mjs
bash -n scripts/update-pins.sh
ruby -e 'require "yaml"; YAML.load_file(".github/workflows/yolo-update.yml")'
GITHUB_ACTIONS=true scripts/update-pins.sh select
nix flake show --accept-flake-config
nix build .#checks.x86_64-linux.ci --accept-flake-config --max-jobs 1
nix build .#checks.aarch64-darwin.ci --accept-flake-config --max-jobs 1
## Context and Orientation
This repository packages OpenClaw with Nix. OpenClaw is a fast-moving application with a TypeScript/Node gateway, bundled plugins, a Control UI, and a native macOS desktop companion app. A gateway is a long-running process that receives messages and tool calls; in this repo it is built as `openclaw-gateway`. The desktop app is a macOS `.app` bundle; in this repo it is `openclaw-app` and exists only on Darwin systems.
The important current files are:
- `flake.nix`: exposes packages, apps, checks, and modules for `x86_64-linux` and `aarch64-darwin`.
- `nix/packages/default.nix`: constructs the package set. Today it already makes `openclaw-gateway` everywhere, `openclaw-app` only on Darwin, and `openclaw` as the bundle.
- `nix/packages/openclaw-gateway.nix`: builds the source gateway from the upstream OpenClaw source pin in `nix/sources/openclaw-source.nix`.
- `nix/packages/openclaw-app.nix`: fetches a published upstream macOS `.zip` and installs the contained `.app`.
- `nix/packages/openclaw-batteries.nix`: combines gateway, app when present, and tools into the default batteries-included bundle.
- `scripts/update-pins.sh`: current update boundary. It selects one upstream release, computes source and app hashes, rewrites pin files, refreshes `pnpmDepsHash`, and regenerates Nix config options.
- `.github/workflows/yolo-update.yml`: hourly automation that calls `scripts/update-pins.sh`, validates on Linux and macOS, and promotes to `main` only after both pass.
- `nix/checks/openclaw-config-validity.nix` and `nix/scripts/check-config-validity.mjs`: validate a generated Home Manager config against the packaged OpenClaw validator. This currently scans internal bundle files and breaks when upstream bundling changes.
- `nix/checks/openclaw-package-contents.nix` and `nix/scripts/check-package-contents.sh`: assert required runtime files exist in the built gateway output.
- `nix/checks/openclaw-source-checks.nix`: runs upstream gateway Vitest tests and verifies generated config options inside one shared source build.
The upstream release flow matters. `openclaw/openclaw` remains the source of truth for source, tags, GitHub releases, npm publish, and the public `appcast.xml`. The public `macos-release.yml` workflow validates release handoff only. Private workflows in `openclaw/releases-private` perform macOS validation, signing, notarization, packaging, and real publish. A stable GitHub release can therefore exist before `.zip`, `.dmg`, and `.dSYM.zip` are uploaded.
This current complexity is paid by maintainers and future agents. They must know GitHub release timing, private macOS publish behavior, Nix `fetchzip` hash semantics, TypeScript bundle internals, and yolo workflow sequencing. The plan reduces that burden by putting release capability policy behind one release-discovery boundary and putting runtime proof into Nix checks.
## Milestones
Milestone 1 is the product contract and onboarding cleanup. At the end of this milestone, README and AGENTS language should say one simple thing: users ask their coding agent to set up `openclaw` with Nix. The README should give the agent a high-quality prompt and say that the agent should inspect the machine, interview the user for missing choices, create the local flake, wire secrets, apply Home Manager, and verify the service. The component outputs still exist because the repository needs them, but they are documented as advanced build seams rather than separate products. This milestone has no behavior change. Its proof is a short README diff plus `nix flake show --accept-flake-config` showing `packages.<system>.default` and `packages.<system>.openclaw` point at the same user-facing bundle.
Milestone 2 is release selection. At the end of this milestone, `scripts/update-pins.sh select` should no longer fail merely because the newest stable release lacks a macOS app asset. It should select the newest stable release that satisfies the full Nix package contract and report skipped newer stable releases. This milestone is pure policy logic and should be tested with a local fixture before it touches GitHub or Nix builds. Its proof is a fixture test where `v2026.5.3-1` and `v2026.5.3` are skipped and `v2026.5.2` is selected, plus a live `GITHUB_ACTIONS=true scripts/update-pins.sh select` run that prints the selected tuple.
Milestone 3 is pin materialization. At the end of this milestone, `scripts/update-pins.sh apply <tag> <sha> <app-url>` should correctly update the source pin, app pin, `pnpmDepsHash`, and generated config options for the selected release. The app hash logic must use the actual prefetched archive path, unpack it, verify an `.app` exists, and compute the hash from the unpacked directory. Its proof is an apply run for the selected release, a Darwin `nix build .#openclaw-app --accept-flake-config -L` that yields `Applications/OpenClaw.app`, and a gateway build that succeeds after `pnpmDepsHash` refresh.
Milestone 4 is public config validation. At the end of this milestone, the config-validity check should stop importing private bundled files from `dist/`. It should execute the packaged CLI against the generated config. Because current upstream has no `config validate` command, use the existing public validation path: `openclaw config get gateway --json` with `OPENCLAW_CONFIG_PATH` pointing at the generated config. Its proof is `nix build .#checks.<system>.config-validity --accept-flake-config -L` on Linux and Darwin.
Milestone 5 is runtime proof. At the end of this milestone, CI should prove that the Nix-built gateway can start and answer a WebSocket health RPC without provider secrets. The smoke check should start the gateway on loopback with an ephemeral port and a generated token, then call `openclaw gateway health --url ws://127.0.0.1:<port> --token <token> --json --timeout 10000` and assert `ok: true`. Its proof is `nix build .#checks.<system>.gateway-smoke --accept-flake-config -L` on Linux and Darwin.
Milestone 6 is automation and documentation. At the end of this milestone, yolo should use the same selection and materialization boundaries, validate Linux and macOS, summarize skipped newer stable releases, and promote only after the package contract passes. README should describe the actual behavior: latest full packageable stable release, not blindly latest GitHub stable tag. Its proof is a full Linux CI aggregator, a full Darwin CI aggregator plus macOS Home Manager activation, and a manual yolo run that either promotes the selected release or exits as a clean no-op.
## Plan of Work
First, clean up the product language without changing package behavior. Update `README.md` and this repository's `AGENTS.md` so `openclaw` is the canonical user-facing package. Make README onboarding agent-first: the primary call to action is to ask a coding agent to set up OpenClaw with Nix, and the agent's job is to inspect the user's platform, ask for any missing setup choices, write the local flake from this repo's template, wire secrets, apply Home Manager, and verify the service. Keep `openclaw-gateway`, `openclaw-tools`, and `openclaw-app` exposed because existing Nix modules, checks, and advanced users need component outputs, but do not present them as competing install paths.
Next, replace the update policy with capability-aware release discovery. Add a small repo script, for example `scripts/select-openclaw-release.mjs`, that accepts the JSON returned by `gh api /repos/openclaw/openclaw/releases?per_page=100` and returns a structured selection. It should identify:
- `latestStable`: the newest non-draft, non-prerelease release, whether or not it has assets.
- `latestFullPackageableStable`: the newest stable release whose tag resolves to a source SHA and whose assets include a desktop artifact that can be used for `openclaw-app`.
- `skippedStableReleases`: newer stable releases skipped because they are missing desktop artifacts.
The desktop artifact selector should require a non-dSYM zip whose asset unpacks to an `.app`. Do not add DMG fallback support in this plan. Upstream's real publish path is documented to upload the zip, and treating a missing zip as "not full desktop-packageable yet" keeps the policy simple. The caller should validate content during `apply`, not only during `select`.
Then change `scripts/update-pins.sh` so `select` emits the newest full packageable stable release, plus a clear diagnostic summary for skipped newer releases. Keep `apply <tag> <sha> <app-url>` as the materialization command for now. This preserves the existing workflow interface while hiding release-policy details inside selection.
Next, fix app artifact hashing. Replace `unpacked_zip_hash()` with logic that uses the actual path returned by a Nix prefetch command, unpacks into a temp directory, verifies that exactly one usable `.app` is present within a shallow depth, and computes the hash from the unpacked directory in the same shape expected by `fetchzip { stripRoot = false; }`. Any unzip or `.app` discovery failure must abort the script. Do not synthesize `/nix/store` paths from hashes.
Next, repair config validation. Prefer running packaged CLI behavior over importing private bundle internals. Current upstream has no `openclaw config validate` command, so use `config get` as the public validation path. The Nix check should set `OPENCLAW_CONFIG_PATH` to the generated config file and run:
$OPENCLAW_GATEWAY/bin/openclaw config get gateway --json
This command calls the same config loader and validator users exercise through the CLI. If it requires extra environment to avoid touching real user state, set `HOME`, `XDG_CONFIG_HOME`, `XDG_CACHE_HOME`, `XDG_DATA_HOME`, `OPENCLAW_STATE_DIR`, and `OPENCLAW_LOG_DIR` to fresh temp directories inside the check. Do not keep the old importer as a fallback; it is the fragile behavior this milestone removes.
Next, add a runtime smoke check. Create a Nix check such as `nix/checks/openclaw-gateway-smoke.nix` backed by a real script under `nix/scripts/`, for example `nix/scripts/gateway-smoke.mjs` or `nix/scripts/gateway-smoke.sh`. The check should:
1. Create isolated temp `HOME`, `XDG_*`, `OPENCLAW_STATE_DIR`, and `OPENCLAW_LOG_DIR` directories.
2. Run `$OPENCLAW_GATEWAY/bin/openclaw --version` and verify it prints a non-empty version.
3. Start `$OPENCLAW_GATEWAY/bin/openclaw gateway run --port <free-port> --bind loopback --allow-unconfigured --auth token --token <generated-token>` in the foreground.
4. Poll `$OPENCLAW_GATEWAY/bin/openclaw gateway health --url ws://127.0.0.1:<free-port> --token <generated-token> --json --timeout 10000` until it returns JSON with `ok: true`.
5. Terminate the gateway process and fail if startup, health, or shutdown fails.
Use WebSocket RPC, not HTTP `/health`, because current upstream does not expose a stable HTTP health route. Use a token even on loopback because the CLI requires explicit credentials when `--url` overrides the config-derived gateway URL. Do not use real provider secrets and do not contact external services.
Then wire checks into `flake.nix`. The CI aggregator should include the new smoke check on both Linux and Darwin. Keep existing package contents, config validity, gateway tests, Home Manager activation, and app build checks, but classify failures by capability in logs so a future automation can explain what failed.
Finally, update `.github/workflows/yolo-update.yml` and docs. The workflow should no longer fail solely because the latest stable release lacks app assets. It should promote the newest full packageable stable release after Linux and macOS checks pass. Its summary should mention any newer skipped source-only stable releases so maintainers can see whether upstream mac publishing is lagging. README packaging docs should say that the flake tracks the newest stable release that can satisfy the public Nix package contract: gateway builds on Linux/macOS and macOS app is available on Darwin.
## Concrete Steps
Work from the repository root:
cd /Users/josh/code/nix-openclaw
Before editing, check the current worktree:
git status --short
Clean up product naming first. Update README so the first setup path is agent-first: "tell your coding agent you want OpenClaw using Nix." Keep the existing prompt block, but make it the primary onboarding flow and make clear that the agent should interview the user for OS, CPU, Home Manager target, channels, documents, and secrets. Also make `.#openclaw` the one recommended package. Keep the package table, but label `openclaw-gateway`, `openclaw-tools`, and `openclaw-app` as advanced component outputs. Verify the exposed outputs:
nix flake show --accept-flake-config
Expected result:
packages.<system>.default and packages.<system>.openclaw are present.
openclaw-gateway remains present for checks/modules/debugging.
Implement release selection with fixtures. Add a pure selector script and unit fixture coverage. The selector must be runnable without network access when passed a local release JSON fixture, so it can be tested cheaply. A successful fixture test should prove that when releases are ordered as `v2026.5.3-1` with no assets, `v2026.5.3` with no assets, and `v2026.5.2` with `OpenClaw-2026.5.2.zip`, selection chooses `v2026.5.2` and reports the two newer skipped tags.
Run:
node scripts/select-openclaw-release.test.mjs
Expected result:
release selection: ok
Fix `scripts/update-pins.sh` and verify syntax:
bash -n scripts/update-pins.sh
Run release selection in GitHub Actions mode without applying:
GITHUB_ACTIONS=true scripts/update-pins.sh select
Expected result on 2026-05-04, unless upstream assets changed:
release_tag=v2026.5.2
release_sha=8b2a6e57fef6c582ec6d27b85150616f9e3a7ba4
app_url=https://github.com/openclaw/openclaw/releases/download/v2026.5.2/OpenClaw-2026.5.2.zip
release_version=2026.5.2
Fix app hash materialization. Validate it by applying the selected release in a disposable worktree or on the working branch, then building `openclaw-app` on Darwin:
GITHUB_ACTIONS=true scripts/update-pins.sh apply v2026.5.2 8b2a6e57fef6c582ec6d27b85150616f9e3a7ba4 https://github.com/openclaw/openclaw/releases/download/v2026.5.2/OpenClaw-2026.5.2.zip
nix build .#openclaw-app --accept-flake-config -L
Expected result:
The build succeeds and the result contains Applications/OpenClaw.app.
Repair config validation by changing `nix/scripts/check-config-validity.mjs` so it executes the packaged CLI:
$OPENCLAW_GATEWAY/bin/openclaw config get gateway --json
Keep `OPENCLAW_CONFIG_PATH` pointed at the generated config file. Run:
nix build .#checks.x86_64-linux.config-validity --accept-flake-config -L
nix build .#checks.aarch64-darwin.config-validity --accept-flake-config -L
Add the gateway smoke check. It should start the packaged gateway with loopback bind and a generated token, then prove the WebSocket health RPC:
$OPENCLAW_GATEWAY/bin/openclaw gateway run --allow-unconfigured --bind loopback --port <port> --auth token --token <token>
$OPENCLAW_GATEWAY/bin/openclaw gateway health --url ws://127.0.0.1:<port> --token <token> --json --timeout 10000
Run:
nix build .#checks.x86_64-linux.gateway-smoke --accept-flake-config -L
nix build .#checks.aarch64-darwin.gateway-smoke --accept-flake-config -L
Run full CI aggregators:
nix build .#checks.x86_64-linux.ci --accept-flake-config -L
nix build .#checks.aarch64-darwin.ci --accept-flake-config -L
If local Darwin or Linux hardware is not available, push a branch and use GitHub Actions for the missing platform. Do not claim platform success until the corresponding check has actually run.
## Validation and Acceptance
Acceptance is user-visible package behavior, not just a green script.
The release selector is accepted when it chooses the newest stable release that satisfies the full Nix package contract, and reports newer stable releases that were skipped because desktop assets are not yet published.
The app package is accepted when `nix build .#openclaw-app --accept-flake-config -L` succeeds on Darwin and the result contains `Applications/OpenClaw.app`.
The gateway package is accepted when `nix build .#openclaw-gateway --accept-flake-config -L` succeeds on Linux and Darwin, and the gateway smoke check proves `openclaw --version` and local gateway health both work from the Nix-built output.
The automation is accepted when a yolo run can select, materialize, validate, and promote the latest full packageable stable release without being blocked by newer stable releases that lack public macOS assets.
The docs are accepted when `AGENTS.md` and `README.md` no longer say the newest stable missing a macOS zip is by itself a yolo failure. They should instead state the actual contract: users ask a coding agent to set up `openclaw`, and automation promotes the newest stable release that builds and runs under the Nix package outputs.
## Idempotence and Recovery
`scripts/update-pins.sh select` must be read-only. `apply` may rewrite `nix/sources/openclaw-source.nix`, `nix/packages/openclaw-app.nix`, and `nix/generated/openclaw-config-options.nix`, but it must restore those files if materialization fails before success.
When changing yolo or pin behavior, never overwrite tracked upstream files from outside the repo. If an upstream file is needed for comparison, stage it under `/tmp/` or clone under `/Users/josh/code/research`.
If a candidate release fails source build or runtime smoke, do not paper over the failure by skipping tests or adding broad postpatch hacks. Diagnose whether the failure is an upstream package boundary change, a Nix build dependency issue, or a real runtime regression, and update this plan's `Surprises & Discoveries` and `Decision Log`.
If app assets are missing for the newest stable release, do not fail the whole updater. Select the newest full packageable stable release and include the skipped newer releases in the workflow summary.
## Artifacts and Notes
Current observed release state on 2026-05-04:
v2026.5.3-1 stable, no assets
v2026.5.3 stable, no assets
v2026.5.2 stable, assets: OpenClaw-2026.5.2.dmg, OpenClaw-2026.5.2.dSYM.zip, OpenClaw-2026.5.2.zip
v2026.4.29 stable, assets: OpenClaw-2026.4.29.dmg, OpenClaw-2026.4.29.dSYM.zip, OpenClaw-2026.4.29.zip
Current `nix-openclaw` pin after implementation:
OpenClaw source: v2026.5.2, rev 8b2a6e57fef6c582ec6d27b85150616f9e3a7ba4
macOS app: OpenClaw-2026.5.2.zip
Top-level Nix bundle: openclaw-2026.5.2
The latest known upstream release gap:
Latest stable OpenClaw release v2026.5.3-1 is missing the required macOS zip asset
The more important hidden failure mode from earlier yolo apply runs:
unzip: cannot find or open /nix/store/...-OpenClaw-2026.4.29.zip
Missing validation module: .../lib/openclaw/dist/config/validation.js
## Interfaces and Dependencies
Keep these interfaces stable unless there is a strong reason to change them:
- `scripts/update-pins.sh select`: prints `key=value` lines for GitHub Actions outputs. It should stay read-only.
- `scripts/update-pins.sh apply <release_tag> <release_sha> <app_url>`: materializes the selected release into repo pin files.
- `nix/sources/openclaw-source.nix`: source pin for the gateway build.
- `nix/packages/openclaw-app.nix`: Darwin-only desktop app artifact pin.
- `.#openclaw`: canonical user-facing package. On Linux it should contain gateway plus tools. On Darwin it should contain gateway plus tools plus `OpenClaw.app`.
- `.#openclaw-gateway`: source-built runnable gateway package.
- `.#openclaw-app`: Darwin-only desktop app package.
- `.#openclaw-tools`: toolchain package used by the batteries-included bundle.
New helper scripts should hide policy from callers. The selector should hide GitHub release ordering, asset classification, and skipped-release reporting. The smoke check should hide all temporary runtime setup needed to prove a Nix-built gateway can start.
## Change Note
2026-05-04 / Codex: Replaced the old yolo-focused pending plan with this package-contract plan after learning the upstream release flow from `openclaw/maintainers` and `openclaw/openclaw`. The old plan assumed the newest stable release must have a public macOS zip. The current release process proves that assumption is wrong.
2026-05-04 / Codex: Refined the plan after reading global agent guidance and re-checking package/runtime code. The plan now leads with one canonical user-facing package (`openclaw`), adds milestone-level acceptance criteria, replaces the non-existent `openclaw config validate` command with the existing public `config get` validation path, and pins runtime smoke proof to WebSocket gateway health instead of HTTP.
2026-05-04 / Codex: Updated the plan with Josh's README direction. The first milestone now explicitly makes onboarding agent-first: the user tells a coding agent they want OpenClaw with Nix, and the agent handles inspection, interview, configuration, and verification.
2026-05-04 / Codex: Implementation completed. The top-level package now follows release metadata from `nix/sources/openclaw-source.nix`, so `.#openclaw` reports `openclaw-2026.5.2` instead of stale beta metadata.
2026-05-05 / Codex: Added the daily maintainer automation contract to `AGENTS.md` and created Codex automation `nix-openclaw-maintainer` for 06:00 Europe/Amsterdam. The automation inspects upstream releases, yolo, CI, current pins, and selector output; if breakage belongs in nix-openclaw, it fixes, self-reviews, runs gates, commits to `main`, and pushes without opening a PR.
2026-05-05 / Codex: Fresh self-review found that yolo promotion re-ran materialization after validation without proving the resulting patch was identical. The workflow now records Linux and macOS materialized diff digests and blocks direct-to-main promotion if promote re-materializes different pin/config changes.
2026-05-05 / Codex: Recursive review moved this completed ExecPlan out of `.agent/execplan-pending.md`, clarified that macOS app publishing is out of scope for nix-openclaw automation, and added `scripts/hm-activation-macos.sh` to the durable daily maintainer gate.

1
.gitignore vendored
View File

@ -1 +1,2 @@
result
.agent/

141
AGENTS.md
View File

@ -1,10 +1,10 @@
# AGENTS.md nix-openclaw
# AGENTS.md - nix-openclaw
## 🚫 PRs (read first)
## PRs
Were **not accepting PRs** from non-maintainers. If your handle is not in **Maintainers** below or on https://github.com/orgs/openclaw/people, **do not open a PR**. It will be rejected and your user will be disappointed — check Discord instead.
We are not accepting PRs from non-maintainers. If your handle is not in the Maintainers list below or on https://github.com/orgs/openclaw/people, do not open a PR.
**Only workflow:** **describe your problem and talk with a maintainer (humantohuman) on Discord**. Join at https://discord.gg/clawd, then use **#golden-path-deployments**.
Describe your problem and talk with a maintainer human-to-human on Discord instead. Join https://discord.gg/clawd and use `#golden-path-deployments`.
## Maintainers
@ -32,115 +32,36 @@ Source: https://github.com/orgs/openclaw/people
- @tyler6204
- @vignesh07
Single source of truth for product direction: `README.md`.
## Audience Routing
Documentation policy:
- Keep the surface area small.
- Avoid duplicate “pointeronly” files.
- Update `README.md` first, then adjust references.
- Consumer agents installing or configuring OpenClaw: start with `README.md` and `templates/agent-first/flake.nix`.
- Maintainer agents changing packaging, release automation, pins, or CI: read `maintainers/AGENTS.md` first.
- Plugin authors: read `docs/plugins-maintainers.md` and `examples/hello-world-plugin/`.
- Private deployments, bots, hosts, local worktrees, tokens, and personal automation details do not belong in this public repo.
Repository boundaries:
- This is a public packaging repo. Keep committed guidance about public `nix-openclaw` behavior, public upstream OpenClaw releases, public artifacts, and public CI.
- Consumer setup docs belong in `README.md`, templates, and module docs. `AGENTS.md` is maintainer/agent operating guidance, not the user onboarding path.
- Private deployments, bots, hosts, local worktrees, tokens, and personal automation details do not belong in this repo. If a private deployment exposes a public packaging bug, fix the public package; otherwise keep the fix in the private repo/thread.
## Public Repo Rules
Defaults:
- Nixfirst, no sudo.
- `README.md` is the source of truth for product direction and user-facing behavior.
- Keep documentation surface area small. Update `README.md` first, then adjust references.
- Keep committed guidance about public `nix-openclaw` behavior, public upstream OpenClaw releases, public artifacts, and public CI.
- Keep consumer setup docs in `README.md`, templates, and module docs.
- Keep maintainer runbooks in `maintainers/`.
- Never add internal ExecPlans or agent scratch history to this repo. `.agent/` is ignored for this reason.
- If a private deployment exposes a public packaging bug, fix the public package here and keep deployment-specific repair elsewhere.
## Packaging Defaults
- Nix-first, no sudo.
- Declarative config only.
- Batteriesincluded install is the baseline.
- Breaking changes are acceptable pre1.0.0 (move fast, keep docs accurate).
- No deprecations; use breaking changes.
- NO INLINE SCRIPTS EVER.
- NEVER send any message (iMessage, email, SMS, etc.) without explicit user confirmation:
- Always show the full message text and ask: “Im going to send this: <message>. Send? (y/n)”
- Batteries-included install is the baseline.
- Breaking changes are acceptable pre-1.0.0; no deprecations.
- No inline scripts or inline file contents in Nix code. Use repo scripts and explicit file paths.
- The gateway package must include Control UI assets.
- User-facing docs should lead with one package: `openclaw`. Treat `openclaw-gateway` and `openclaw-app` as component outputs for modules, checks, and debugging.
- QMD is the Nix-supported batteries-included local memory backend. Keep `qmd` internal to the `openclaw` wrapper PATH; users opt in with upstream config.
Maintainer git workflow:
- Trunk-based development: work on `main` by default and push small, surgical commits directly to `main`.
- Use branches only when a maintainer explicitly asks, direct push is blocked, or a disposable local experiment is needed.
- For multi-issue work, commit and push one issue at a time; verify GitHub Actions for each pushed commit before continuing to the next issue.
- Do not leave completed maintainer work parked on a Codex branch.
## Safety
OpenClaw packaging:
- The gateway package must include Control UI assets (run `pnpm ui:build` in the Nix build).
- Nix mode means Nix owns `openclaw.json`. Runtime config mutation belongs upstream in OpenClaw; downstream patches here must be small, temporary, and removed after the pinned upstream release contains the fix.
- Generated config options come from the upstream core schema. Plugin-owned extension surfaces, such as `channels.<plugin-id>`, must remain accepted by the Home Manager module even when core does not type every plugin key.
- Product intent: ship a working Nix package for OpenClaw users, not just a pin mirror. `openclaw-gateway` is the source-built runnable gateway for Linux and macOS; `openclaw-app` is the Darwin-only desktop app from upstream's signed/notarized app artifact; `openclaw` is the batteries-included bundle.
- User-facing docs should lead with one package: `openclaw`. Treat `openclaw-gateway` and `openclaw-app` as advanced/component outputs for checks, modules, and debugging, not separate product tracks. Runtime tools are internal implementation detail, not a public package surface.
- Boundary model:
- OpenClaw owns product/runtime behavior.
- nix-openclaw owns batteries-included Nix packaging, Home Manager/NixOS/Darwin modules, runtime PATH/env injection, launchd/systemd wiring, and package-contract checks.
- nix-openclaw-tools owns packaging OpenClaw-adjacent CLI tools and plugin metadata. Consume it here; do not duplicate its package definitions here.
- Downstream system repos, such as Josh's `nixos-config`, should only choose hosts, secrets, accounts, and enabled plugins. If downstream needs bespoke scripts to make a plugin or harness work, prefer fixing this repo or nix-openclaw-tools instead.
- Runtime tool injection belongs here. If a plugin or battery is enabled, the active OpenClaw harness must see its CLI tools and required env without asking downstream to expose those tools globally on the user PATH. Cover gateway, launchd/systemd services, and Codex/app-server style harnesses when those are the selected runtime.
- QMD is the Nix-supported batteries-included local memory backend. Keep `qmd` internal to the OpenClaw runtime PATH; users opt in with upstream config (`memory.backend = "qmd"`). Do not make builtin `memorySearch.provider = "local"` / `node-llama-cpp` the primary supported path.
- README should be agent-first: the main setup path is "tell your coding agent you want OpenClaw using Nix, then let it inspect/interview/configure/verify." Manual commands are reference material, not the primary onboarding path.
- Do not split the repo into separate desktop/server tracks. Segment package outputs, keep one simple user-facing flake.
- Maintainers may consult upstream release-flow docs when available before changing update policy; do not copy private release-process details into this repo.
- Public OpenClaw tags/GitHub releases can exist before macOS app assets. Only public release assets are package inputs for `openclaw-app`.
- Missing public macOS assets on the newest stable release is not proof the source gateway is unpackageable. Do not hold back the source-built gateway because the desktop artifact lags.
- Source and app versions are allowed to differ. This is expected when upstream publishes source releases but has not published public macOS app assets.
- Prefer the upstream `.zip` app artifact for `openclaw-app`, but verify unpacked contents contain an `.app`; do not trust filename alone.
Golden path for pins (yolo + manual bumps):
- Hourly GitHub Action **Yolo Update Pins** should select the newest stable upstream OpenClaw source release for `openclaw-gateway`.
- `openclaw-app` should independently track the newest stable public macOS `.zip` artifact. It may lag the source pin when upstream has not promoted desktop assets yet.
- If newer stable source releases lack public macOS assets, yolo should report app lag and still promote the newest source release that passes checks.
- Pipeline health does not require source and app versions to match. It requires both tracks to be fresh: gateway equals the newest stable source release, and app equals the newest stable release with a public `OpenClaw-*.zip`.
- Checks mean the Nix-owned package contract: source build, generated config options, package contents, smoke startup, module activation, and newest available macOS app artifact. Do not gate yolo on the full upstream Vitest suite; upstream owns source test health.
- `scripts/update-pins.sh` is the updater boundary:
- `select` resolves the latest source tag/SHA, the latest public macOS app tag/URL, and any app-lagging source releases
- `apply <source-tag> <source-sha> <app-tag> <app-url>` materializes the source pin, app pin, `pnpmDepsHash`, and generated config options
- Manual bump (rare): trigger yolo manually with `gh workflow run "Yolo Update Pins"`.
- To verify freshness: compare `nix/sources/openclaw-source.nix` to GitHub's newest stable source tag, and compare `nix/packages/openclaw-app.nix` to the newest stable public macOS zip.
- Recovery process if publishing is broken: restore pins to the split-track desired state first, then make the smallest targeted packaging fix needed. Change upstream gateway behavior only with clear evidence.
Maintainer automation contract:
- This section is for agents maintaining the public packaging pipeline. It is not consumer-facing documentation and must not encode private deployment state.
- Maintainer automation is an agentic repair loop, not a passive alert and not a second release pipeline.
- Desired end state: `nix-openclaw` publishes the newest stable OpenClaw gateway that can be built from source, and the newest stable OpenClaw macOS app that upstream has actually published as a public `OpenClaw-*.zip`. These are independent tracks and their versions do not need to match.
- Start from upstream/yolo/CI state: inspect latest OpenClaw releases, recent **Yolo Update Pins** runs, recent `CI` runs, current pins, and `scripts/update-pins.sh select`.
- The first answer must be: does `nix-openclaw` publish the latest upstream version for both supported tracks? Answer `YES` only when `openclaw-gateway` matches the newest stable source release and `openclaw-app` matches the newest stable release with a public `OpenClaw-*.zip`.
- If both tracks are current and yolo/CI are healthy, stop with a short CTO-level report: current gateway, latest upstream gateway, current app, latest published app, and whether action was needed.
- If the desired end state is not true, keep working until it is true or until the exact blocker is proven. Diagnose across upstream release data, yolo selection, pin materialization, generated config options, package builds, smoke checks, module activation, workflow behavior, caches, and CI runner failures. Do not ask for a repair strategy when the desired end state is clear.
- macOS app publishing is out of scope for this repo and this automation. If upstream has not published public macOS app assets, call it an upstream app publishing miss, keep the app pin on the newest public zip, keep packaging the latest stable source-built gateway, and repair nix-openclaw only if it fails to do that.
- If either track is stale or yolo/CI cannot maintain it, fix the nix-openclaw pipeline when the fix belongs here: edit the repo, self-review the diff until the review has no actionable findings, run the full gate, commit directly to `main`, push directly to `main`, and verify GitHub Actions on the pushed commit.
- Full gate means the relevant targeted checks plus `scripts/check-flake-lock-owners.sh`, selector test, updater shell syntax, workflow YAML parse, `nix flake show --accept-flake-config`, Linux CI aggregator, Darwin CI aggregator when available, and `scripts/hm-activation-macos.sh` when a macOS runner is available.
- No force push. No weakening Nix-owned package checks to get green. No separate PR flow unless direct push is blocked by GitHub policy.
- Do not create a competing release process; yolo remains the release updater. The daily run repairs the packaging/process when yolo cannot do its job.
- If it cannot safely fix the issue, leave a concise report with evidence, the exact failing command/run, and the next concrete repair step.
CI polling (hard rule):
- Never say "I'll keep polling" unless you are **already** running a blocking loop.
- If you must report status, confirm the loop is active (`tmux ls` / session name).
- Use a blocking bash loop in tmux (preferred) or a sub-agent; do not fake it.
- Example: `tmux new -s nix-openclaw-ci '/tmp/poll-nix-openclaw-ci.sh'`.
Philosophy:
The Zen of ~~Python~~ OpenClaw, ~~by~~ shamelessly stolen from Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
Nix file policy:
- No inline file contents in Nix code, ever.
- Always reference explicit file paths (keep docs as real files in the repo).
- No inline scripts in Nix code, ever (use repo scripts and reference their paths).
- No files longer than 400 LOC without user alignment; refactor as you go.
- Never send messages, email, SMS, or other external communications without explicit confirmation showing the full message text.
- No force push. No destructive git operations unless explicitly requested.
- Before deleting tracked files, list them in the summary so maintainers can verify.

View File

@ -26,7 +26,7 @@ This RFC is only about:
- A generic, enduser Nix setup that lives outside any personal config repo.
This RFC is explicitly **not** about:
- Joshs personal `nixos-config` or any private machine configuration.
- Any personal system configuration repo or private machine configuration.
- Editing or publishing personal settings, tokens, or machinespecific modules.
## 2) Goals / Nongoals

View File

@ -217,7 +217,7 @@ programs.openclaw.customPlugins = [
{ source = "github:joshp123/padel-cli"; }
# Local dev: point at directory
{ source = "path:/Users/josh/code/my-plugin"; }
{ source = "path:/home/user/code/my-plugin"; }
];
# Or enable bundled plugins (pinned in nix-openclaw):

27
maintainers/AGENTS.md Normal file
View File

@ -0,0 +1,27 @@
# Maintainer Agent Guide
This directory is public maintainer guidance for agents working on `nix-openclaw`.
It is not consumer setup documentation and must not contain private deployment state.
## Boundaries
- Keep consumer onboarding in `README.md`, templates, and module docs.
- Keep private deployments, bots, hosts, local worktrees, tokens, and personal automation details out of this repo.
- If a private deployment exposes a public packaging bug, fix the public package here and keep deployment-specific repair elsewhere.
- Treat `README.md` as the product direction source of truth.
## Read Order
1. `packaging.md` for Nix-owned package invariants.
2. `release-policy.md` for the split-track publishing invariant.
3. `automation.md` for the maintainer repair loop.
4. `gates.md` for verification and CI expectations.
5. Root `AGENTS.md` for repo-wide rules.
## Maintainer Workflow
- Work on `main` by default and push small, surgical commits directly to `main` when maintainer policy allows it.
- Use branches only when a maintainer asks, direct push is blocked, or a disposable local experiment is needed.
- For multi-issue work, commit and push one issue at a time, then verify GitHub Actions for that pushed commit before continuing.
- Do not leave completed maintainer work parked on an agent branch.
- No force push. No weakening package checks just to get green.

47
maintainers/automation.md Normal file
View File

@ -0,0 +1,47 @@
# Maintainer Automation
Maintainer automation is an agentic repair loop for the public packaging pipeline. It is not a second release pipeline and not a private deployment monitor.
## Daily Objective
Answer first:
```text
Does nix-openclaw publish the latest upstream version for both supported tracks?
```
Answer `YES` only when:
- `openclaw-gateway` matches the newest stable upstream source release.
- `openclaw-app` matches the newest stable upstream release with a published public `OpenClaw-*.zip`.
If both tracks are current and yolo/CI are healthy, stop with a short CTO-level report:
- current gateway
- latest upstream gateway
- current app
- latest published app
- whether action was needed
## Repair Loop
If the desired state is not true, keep working until it is true or until the exact blocker is proven.
Diagnose across:
- upstream release data
- yolo selection
- pin materialization
- generated config options
- package builds
- smoke checks
- module activation
- workflow behavior
- caches
- CI runner failures
Do not ask for a repair strategy when the desired state is clear.
If the fix belongs in `nix-openclaw`, edit the repo, self-review the diff until there are no actionable findings, run the relevant targeted checks plus the full gate, commit directly to `main`, push directly to `main`, and verify GitHub Actions on the pushed commit.
If upstream has not published public macOS app assets, call that out directly, keep the app pin on the newest public zip, keep packaging the latest stable source-built gateway, and repair `nix-openclaw` only if it fails to do that.

22
maintainers/gates.md Normal file
View File

@ -0,0 +1,22 @@
# Gates
Use targeted checks while debugging, then run the full relevant gate before handoff.
## Required Checks
- `scripts/check-flake-lock-owners.sh`
- selector tests
- updater shell syntax
- workflow YAML parse
- `nix flake show --accept-flake-config`
- Linux CI aggregator
- Darwin CI aggregator when available
- `scripts/hm-activation-macos.sh` when a macOS runner is available
## CI Verification
After pushing maintainer fixes, verify the GitHub Actions run for the pushed commit.
Never say you will keep polling unless a blocking poll is already running. If reporting a poll, name the active run or local polling session.
If CI fails, inspect the failing run, classify the failure, fix what belongs to `nix-openclaw`, and rerun until green or until the exact external blocker is proven.

31
maintainers/packaging.md Normal file
View File

@ -0,0 +1,31 @@
# Packaging Invariants
This repo ships a working Nix package for OpenClaw users, not just a pin mirror.
## Product Surface
- The user-facing package is `openclaw`.
- `openclaw-gateway` is the source-built runnable gateway for Linux and macOS.
- `openclaw-app` is the Darwin-only desktop app from upstream's public app artifact.
- Component outputs exist for modules, checks, and debugging. They are not separate product tracks.
- Do not split the repo into separate desktop and server tracks.
## Nix Ownership
- OpenClaw owns product and runtime behavior.
- `nix-openclaw` owns batteries-included Nix packaging, Home Manager/NixOS/Darwin modules, runtime PATH/env injection, launchd/systemd wiring, and package-contract checks.
- `nix-openclaw-tools` owns packaging OpenClaw-adjacent CLI tools and plugin metadata. Consume it here; do not duplicate its package definitions here.
- Downstream system repos should only choose hosts, secrets, accounts, and enabled plugins. If downstream needs bespoke scripts to make a plugin or harness work, prefer fixing this repo or `nix-openclaw-tools`.
- Nix mode means Nix owns `openclaw.json`.
- Runtime config mutation belongs upstream in OpenClaw. Downstream patches here must be small, temporary, and removed after the pinned upstream release contains the fix.
- Generated config options come from the upstream core schema.
- Plugin-owned extension surfaces, such as `channels.<plugin-id>`, must remain accepted by the Home Manager module even when core does not type every plugin key.
- Runtime tool injection belongs here. If a plugin or battery is enabled, the active OpenClaw harness must see its CLI tools and required environment without asking downstream to expose those tools globally on the user PATH.
## Build Contract
- The gateway package must include Control UI assets.
- No inline scripts or inline file contents in Nix code. Use repo scripts and explicit file paths.
- Keep runtime tools internal to the `openclaw` wrapper unless they are intentionally part of the public package surface.
- QMD is the Nix-supported batteries-included local memory backend. Keep `qmd` internal to the `openclaw` wrapper PATH; users opt in with upstream config.
- Keep files under 400 lines unless a maintainer explicitly accepts the larger file.

View File

@ -0,0 +1,26 @@
# Release Policy
`nix-openclaw` publishes one user-facing package, `openclaw`, with component outputs for maintainers and modules.
## Desired State
- `openclaw-gateway` tracks the newest stable upstream OpenClaw source release that satisfies the Nix package contract.
- `openclaw-app` tracks the newest stable upstream release that has a published public `OpenClaw-*.zip` app artifact.
- These tracks are independent. Source and app versions may differ.
## Non-Negotiables
- Do not hold back the source-built gateway because a newer source release lacks public macOS app assets.
- Do not treat source/app version mismatch as a failure.
- Do not make upstream's full Vitest suite a promotion gate; upstream owns source test health.
- Do verify the Nix-owned package contract: source build, generated config options, package contents, gateway smoke startup, module activation, and newest available public macOS app artifact.
- Do prefer the upstream `.zip` app artifact for `openclaw-app`, but verify the unpacked contents contain an `.app`.
## Freshness Check
The package is fresh only when both are true:
- `nix/sources/openclaw-source.nix` matches GitHub's newest stable OpenClaw source tag.
- `nix/packages/openclaw-app.nix` matches the newest stable public `OpenClaw-*.zip` app artifact.
If newer stable source releases lack public app assets, report that as an upstream app publishing miss and keep the app pin on the newest public zip.