From 0ecd32d10343c23cd3546ec24d2bd0a9be130541 Mon Sep 17 00:00:00 2001 From: nmccready Date: Thu, 9 Jul 2026 15:52:26 -0400 Subject: [PATCH 1/3] =?UTF-8?q?docs(spec):=20SPEC-008=20=E2=80=94=20full-c?= =?UTF-8?q?ontext=20integrity=20lock=20(agents.lock=20+=20agents.sum)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Draft spec for a whole-tree provenance + integrity layer over .agents/: agents.lock records where every artifact comes from (local / source / linked / imported), agents.sum is the go.sum-style per-file content ground truth. New lock/verify commands; verify --json as a scheduled drift sentinel closing the loop with SPEC-005 quarantine. Co-Authored-By: Claude Fable 5 --- specs/SPEC-008-context-integrity-lock.md | 446 +++++++++++++++++++++++ 1 file changed, 446 insertions(+) create mode 100644 specs/SPEC-008-context-integrity-lock.md diff --git a/specs/SPEC-008-context-integrity-lock.md b/specs/SPEC-008-context-integrity-lock.md new file mode 100644 index 0000000..01ebf8f --- /dev/null +++ b/specs/SPEC-008-context-integrity-lock.md @@ -0,0 +1,446 @@ +--- +id: SPEC-008 +title: "Full-context integrity lock: agents.lock + agents.sum for the whole .agents/ tree" +status: Draft +owner: nmccready +created: 2026-07-09 +updated: 2026-07-09 +related: SPEC-003, SPEC-005, SPEC-007 +--- + +# [SPEC-008] Feature: Full-context integrity lock (`agents.lock` + `agents.sum`) + +## Overview + +Add a **full-tree integrity lock** for `.agents/`: two machine-written, +git-committed files that together answer, for *every* artifact in every +bucket, the two questions that matter for an AI-context supply chain: + +1. **Where did this come from?** → `agents.lock` — the *provenance* + record. One entry per artifact: name, bucket, origin + (`local` / `source:` / `linked:` / + `imported:`), and the resolved SHA where one exists. +2. **Is it still exactly what we locked?** → `agents.sum` — the + *content* ground truth. go.sum-style: one line per file, + ` sha256:`, sorted, LF-joined, trivially diffable and + trivially verifiable. + +SPEC-003's `sources.lock` already does this — but **only for +remotely-sourced entries** declared in `sources.yaml`. Locally-authored +rules, hand-written skills, `import`ed one-offs, SPEC-007 linked +checkouts, hooks, agents, plans, specs, and ADRs are invisible to it. +SPEC-008 closes that gap: the lock pair covers the **entire AI-context +picture**, so a single `sync-agents verify` proves (or disproves) that +what the harnesses are loading is what was reviewed and locked. + +> Verbatim intent: "there should be a lock file which encapsulates +> local, rules, skills… everything, to identify where everything is +> coming from. Ideally this would have an associated sum file so +> checksums check out — a full integrity lock of the AI context +> picture." + +## Motivation + +- **Provenance blind spots.** `sources.lock` records only + manifest-declared remote entries. A rule dropped in by hand, a skill + vendored two years ago, or a hostile file *added* to `.agents/rules/` + by a compromised tool has no record anywhere. "Where did + `rules/deploy.md` come from?" is currently unanswerable for most of + the tree. +- **Tamper detection stops at install time.** SPEC-005 quarantine + + scan gates *entry* into the live tree; SPEC-003 content hashes detect + drift *of manifest entries* at the next `pull`. Nothing detects a + post-install edit to a local rule, an injected file inside an + approved skill dir, or a new artifact that never went through any + gate. The `agents.sum` file is the missing continuous-integrity + layer: hash everything once at lock time, verify anytime. +- **Agents audit agents.** A daily `sync-agents verify --json` is + PLEX-style drift detection for the whole AI-context picture — a cron + or CI job can diff "what the context is" against "what the context + was approved to be" and page a human only on unexplained drift. + +## Separate lock, or extend `sources.lock`? (decision) + +**Decision: separate files — `agents.lock` + `agents.sum` at the +`.agents/` root — not an extension of `sources.lock`.** + +Considered extending `sources.lock` with synthetic entries for local +artifacts. Rejected: + +- **Different owner, different lifecycle.** `sources.lock` is written + entry-at-a-time by the pull pipeline and is meaningless without + `sources.yaml`; `agents.lock` is a whole-tree snapshot regenerated by + `lock`/`verify`-adjacent operations and exists even in a repo with no + `sources.yaml` at all. Merging them makes every local edit churn the + file the pull pipeline owns, and makes "sources.lock exists" ambiguous + between "we pull from upstream" and "we merely lock local files". +- **Different failure semantics.** A stale `sources.lock` entry means + "re-pull"; a stale `agents.lock` entry means "tamper or unrecorded + edit". Overloading one file overloads the operator's mental model. +- **The go.mod/go.sum analogy** (which motivates the two-file split + within SPEC-008 itself): Go keeps *resolution* (`go.mod` — what you + asked for and what version was chosen) apart from *integrity* + (`go.sum` — flat, append-friendly, machine-checked hashes). We mirror + that: `agents.lock` is provenance (rich, structured, YAML — humans + read it to answer "where from?"), `agents.sum` is content (flat, + line-per-file, text — machines diff and verify it, and a one-line + change in review screams exactly which file moved). Collapsing hashes + into the lock would make integrity review mean reading YAML diffs of + nested structures; a `.sum` line change is unmissable. +- `sources.lock` stays exactly as it is; `agents.lock` **references** + its entries (via `origin: source:`) rather than duplicating + them. Layering: `sources.yaml → sources.lock` answers "what upstream + resolved to what"; `agents.lock/agents.sum` sits above and answers + "what is the whole tree and does it check out". + +## Goals + +- `sync-agents lock` generates/refreshes `.agents/agents.lock` + + `.agents/agents.sum` covering every artifact in every registered + bucket (the `Buckets` registry — rules, skills, workflows, agents, + plans, specs, hooks, adrs — plus root-level context files, see + Coverage). +- `sync-agents verify` re-walks the tree, compares against lock + sum, + exits non-zero on drift, and supports `--json` for CI/agent + consumption and `--explain ` for "where did this come from and + does it match?". +- Every artifact entry carries an **origin**: + `local` (authored here), `source:` (SPEC-003 manifest pull), + `linked:` (SPEC-007 link), `imported:` (manual + `import`), with `resolved_sha` where the origin has one. +- Deterministic output: same tree ⇒ byte-identical lock and sum files, + on every OS. Regeneration produces minimal, reviewable git diffs. +- `pull`, `update`, `approve`, `detach`, `add`, `remove`, and `import` + refresh the affected entries automatically once the lock exists — + the lock never goes stale through normal sync-agents operations. +- Local edits to a *pulled* artifact are an **ERROR** (tamper signal); + drift in a *linked* artifact is **INFO** by default (it's a live + checkout — drift is its job) and escalates only under `--strict`. + +## Non-goals + +- **Signing.** sha256 pinning only, same posture as SPEC-003. A future + sigstore layer composes on top of `agents.sum` naturally. +- **Auto-remediation.** `verify` reports; it never rewrites the tree. + (Re-locking after an intentional edit is the explicit `lock` command + — the analogue of `go mod tidy` after editing go.mod.) +- **Locking harness-side copies.** We lock `.agents/` (the source of + truth); `.claude/`, `.cursor/` etc. are derived by `sync` and already + covered by symlinking back to the locked tree. +- **Replacing `sources.lock`.** It remains the resolution record for + the pull pipeline; SPEC-008 consumes it, never edits it. + +## File formats + +Both files live at the `.agents/` root, next to `sources.yaml` / +`sources.lock`, and are **committed to git** (that's the point — review +the diff, then trust the verify). + +### `agents.lock` (provenance — YAML, structured) + +```yaml +version: 1 +generated_at: "2026-07-09T14:00:00Z" # informational; excluded from determinism guarantees below it +artifacts: + - path: rules/security.md # relative to .agents/, forward slashes — the sort key + bucket: rules + name: security + origin: "source:rule:my-org/agent-norms@main/rules/security.md" + resolved_sha: "abc1234567890abcdef…" # from sources.lock / _origin.json + tree_hash: "sha256:1f3a…" # SPEC-003 HashTree over the artifact + - path: rules/state.md + bucket: rules + name: state + origin: "local" # no _origin.json, no manifest entry + tree_hash: "sha256:9c2b…" + - path: skills/foo-skill + bucket: skills + name: foo-skill + origin: "linked:file:../foo-skill" # SPEC-007 link override + resolved_sha: "9f3a…" # checkout HEAD at lock time — informational + tree_hash: "sha256:77aa…" # hash of resolved target files AT LOCK TIME + - path: skills/one-off + bucket: skills + name: one-off + origin: "imported:https://github.com/foo/bar/blob/main/skills/one-off/SKILL.md" + resolved_sha: "" + tree_hash: "sha256:41de…" +``` + +- **Ordering:** artifacts sorted lexicographically by `path` + (forward-slash, byte order). Deterministic across platforms; a + one-artifact change is a one-hunk diff. +- **Origin resolution precedence** (per artifact): + 1. `sources.lock` has a `link` record whose target is this artifact + → `linked:`. + 2. `sources.lock` / `_origin.json` with `source: "manifest"` → + `source:` (+ `resolved_sha` from the lock). + 3. `_origin.json` with `source: "manual"` → `imported:` + (URL reconstructed from owner/repo/path/ref; `resolved_sha` = its + `sha` when recorded). + 4. No origin metadata → `local`. +- `tree_hash` reuses SPEC-003's `HashTree` (sorted per-file manifest, + origin files excluded) — one artifact = one hash, matching what + `sources.lock`/`_origin.json` already record for pulled entries, so + a pulled artifact's `tree_hash` in `agents.lock` MUST equal its + `content_hash` in `sources.lock` at lock time (cross-check is a + `verify` invariant). + +### `agents.sum` (integrity — plain text, go.sum-style) + +One line per **file**, not per artifact — the finest verification +grain, so `verify` can name the exact file that moved: + +``` +rules/security.md sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +rules/state.md sha256:aa11… +skills/foo-skill/SKILL.md sha256:bb22… +skills/foo-skill/scripts/run.sh sha256:cc33… +sources.lock sha256:dd44… +sources.yaml sha256:ee55… +agents.lock sha256:ff66… +``` + +- Format: ` sha256:\n` — path relative to `.agents/`, + forward slashes, lexicographically sorted, LF-only, no trailing + metadata. Parseable with `cut`/`awk`; diff-reviewable line by line. +- Hash is sha256 of raw file bytes (no normalization — same rule as + SPEC-003 `hash.go`: the recorded hash must verify against the real + tree). +- **`sources.yaml` and `sources.lock` are themselves summed** — tamper + with the source manifest or its lock and `verify` catches it. +- **`agents.lock` is summed too** (written first, then hashed into the + sum) — the sum file is the single root of trust; a hand-edited lock + fails verification. `agents.sum` cannot contain itself + (chicken-and-egg) — it is the trust root, protected by git review + like `go.sum`. +- Symlinked (linked) artifacts: files are hashed **through** the + symlink (resolved target contents at lock time). The symlink itself + contributes a ` link:` line so a re-pointed + link is detected even when target contents match. + +### Coverage & exclusions + +Included: + +- Every artifact in every registered bucket (`Buckets` registry), + **including OS-scoped subdirs** (`macos/`, `linux/`, `unix/`, + `windows/` per SPEC-006). The lock is a *content* record, not a + *routing* record — a Linux teammate must still be able to verify the + `macos/` files they don't sync. `verify` therefore needs no OS gate. +- Root-level managed context files: `sources.yaml`, `sources.lock` + (sum only — they are inputs, not artifacts, so no lock entries). + +Excluded (never locked, never summed, never flagged): + +- `.quarantine/` — pre-approval holding area by definition + (SPEC-005); locking it would launder unapproved content into the + trust record. +- `.sources/` — SPEC-007 managed clones (live git checkouts; their + linked *artifacts* are covered via the symlink rules above). +- `STATE_*.md` snapshots and legacy `STATE.md` **without** + `shared: true` frontmatter — per-engineer scratch, same convention + the AGENTS.md indexer already applies. A snapshot that opts in via + `shared: true` IS locked (origin `local`). +- `_origin.json` / `*.origin.json` — provenance metadata already + excluded from `HashTree`; their content is reflected in + `agents.lock` origins instead. +- `.findings.json`, editor droppings (`.DS_Store`), and anything + matched by existing gitignore-managed patterns for `.agents/`. + +## Commands + +### `sync-agents lock` + +``` +sync-agents lock [--global] [--dry-run] [--json] +``` + +- Walks `.agents/` per Coverage rules, resolves each artifact's origin, + computes tree + per-file hashes, writes `agents.lock` then + `agents.sum` (atomic writes, lock hashed into sum). +- Idempotent: unchanged tree ⇒ byte-identical files except + `generated_at` (which is therefore updated **only when any other + byte changed**, keeping no-op runs diff-free). +- `--dry-run` prints the would-be entry delta (added / removed / + rehashed) without writing. +- Prints a one-line summary: `locked 34 artifacts (212 files) across 6 + buckets; 2 source, 1 linked, 1 imported, 30 local`. + +### `sync-agents verify` + +``` +sync-agents verify [--global] [--strict] [--json] [--explain ] +``` + +- Recomputes the walk and compares three ways: + 1. **sum check** — every `agents.sum` line matches the file on disk; + every on-disk covered file has a line. + 2. **lock check** — every lock artifact exists, its `tree_hash` + matches, and (for `source:` origins) matches `sources.lock`'s + `content_hash`. + 3. **coverage check** — no covered artifact is missing from the lock. +- **Findings and severities:** + + | condition | severity | rationale | + |---|---|---| + | file hash mismatch, origin `source:`/`imported:` | **ERROR** | local edit to pulled content = tamper signal (ClawHub supply-chain posture; the sanctioned paths are `update` or `detach`) | + | file hash mismatch, origin `local` | **ERROR** | unrecorded edit — re-run `lock` after review | + | file/artifact present on disk, absent from lock | **ERROR** | injected content that bypassed every gate | + | artifact in lock, missing on disk | **ERROR** | deleted without `remove`/re-lock | + | drift in a `linked:` artifact (content or `resolved_sha`) | **INFO** (ERROR under `--strict`) | a live checkout drifts by design; hash is a point-in-time record | + | dangling link symlink | **WARN** | mirrors SPEC-007 `pull` behavior | + | `agents.lock` hash ≠ its `agents.sum` line | **ERROR** | hand-edited lock | + +- **Exit codes:** `0` clean (INFO-only allowed), `1` any WARN/ERROR + (`--strict` promotes INFO to ERROR), `2` usage/IO error. No lock + present ⇒ exit `0` with a notice (`not locked — run 'sync-agents + lock'`), so adding `verify` to CI before adopting the lock is safe. +- `--json` emits `{status, findings: [{path, origin, severity, kind, + want, got}], counts}` — stable schema for cron/agent consumers. +- `--explain ` answers Nick's question for one artifact/file: + + ``` + $ sync-agents verify --explain skills/foo-skill + skills/foo-skill + origin: linked:file:../foo-skill (SPEC-007 link, user checkout) + resolved_sha: 9f3a… (checkout HEAD at lock time; now 41bc…) + locked: sha256:77aa… 2026-07-09T14:00:00Z + on disk: sha256:81fe… → DRIFT (INFO: linked sources are live) + files: 3 match, 1 modified (scripts/run.sh) + ``` + +### Automatic refresh (integration) + +Once `agents.lock` exists, mutating commands refresh the affected +entries (and rewrite the sum) as their final step: + +- `pull` / `update` — re-lock entries whose artifacts were (re)fetched; + `resolved_sha`/`tree_hash` updated from the fresh `sources.lock`. +- `approve` — promotion out of quarantine adds/updates the artifact's + entry (this is the moment SPEC-005's gate verdict becomes a locked + hash). +- `detach` — origin flips `source:…`/`linked:…` → `local` (or + `imported:` when origin metadata is kept), hashes recomputed over the + materialized copy. +- `add` (local scaffold), `import`, `source remove` — entry + added/updated/removed respectively. +- No lock file ⇒ all of the above are no-ops (see Rollout). + +`sync` does **not** verify by default (it must stay fast and offline); +a `sync --verify` opt-in and a documented CI/cron recipe cover the +gate-on-drift use case. + +## Security framing (closing the SPEC-005 loop) + +SPEC-005 gates **entry**: quarantine + static scan means nothing +reaches the live tree without a scan verdict and a human approve. +SPEC-008 gates **residence**: from the moment of approval, every byte +in the tree is pinned in `agents.sum`, so post-install tampering — +malicious edit, injected file, swapped symlink, or an artifact that +sidestepped quarantine entirely (hand-copied into `.agents/`) — is +detected by the next `verify`. + +Together: *quarantine proves it was safe when it entered; the sum file +proves it hasn't changed since.* A scheduled `sync-agents verify +--json` (daily cron or CI on every PR) is PLEX-style drift detection +for the full AI-context picture, with git history of +`agents.lock`/`agents.sum` as the audit trail of every approved change. + +Trust boundaries, explicitly: + +- `agents.sum` is the root of trust and is protected by git review — + an attacker who can silently rewrite the sum in a committed PR has + already won the repo; that threat is out of scope (same stance as + go.sum). +- `verify` reads the tree and the two lock files only; it makes no + network calls (offline-safe, cron-safe). +- Severity policy encodes the posture: pulled content drifting locally + is ERROR (supply-chain tamper signal), linked checkouts drifting is + INFO (that's what links are for), unlocked files appearing is ERROR + (the gate was bypassed). + +## Test plan (Go + bats) + +Go unit tests (`internal/agent/source` + a new `integrity.go` or +`internal/agent/lock` package): + +- [ ] `TestLock_Deterministic` — same tree, two runs ⇒ byte-identical + lock + sum (modulo `generated_at` rule). +- [ ] `TestLock_Ordering` — artifacts and sum lines lexicographically + sorted; inserting an artifact produces a single-hunk diff. +- [ ] `TestLock_OriginResolution` — fixtures for all four origins + (manifest `_origin.json`, manual `_origin.json`, SPEC-007 link + record, bare local file) map to the right `origin:` strings. +- [ ] `TestLock_Exclusions` — `.quarantine/`, `.sources/`, unshared + `STATE_*.md`, origin files, `.findings.json` absent from both + files; `shared: true` STATE snapshot present. +- [ ] `TestLock_OSSubdirs` — `rules/macos/brew.md` locked and summed + regardless of host GOOS. +- [ ] `TestSum_IncludesLockAndSources` — `agents.lock`, + `sources.yaml`, `sources.lock` lines present; `agents.sum` line + absent. +- [ ] `TestVerify_Clean` — untouched tree ⇒ exit 0, empty findings. +- [ ] `TestVerify_PulledEdit_Error` — edit a `source:` artifact file ⇒ + ERROR, exit 1, finding names the file. +- [ ] `TestVerify_UnlockedFile_Error` — drop a new file into + `rules/` ⇒ ERROR (coverage check). +- [ ] `TestVerify_MissingArtifact_Error` — delete a locked artifact ⇒ + ERROR. +- [ ] `TestVerify_LinkedDrift_InfoThenStrict` — modify a linked + checkout ⇒ INFO + exit 0; `--strict` ⇒ ERROR + exit 1. +- [ ] `TestVerify_RepointedLink` — same content, different symlink + target ⇒ finding (link line mismatch). +- [ ] `TestVerify_TamperedLock` — hand-edit `agents.lock` ⇒ ERROR via + its sum line. +- [ ] `TestVerify_NoLock_Noop` — no lock file ⇒ exit 0 + notice. +- [ ] `TestVerify_JSONSchema` — `--json` output round-trips; stable + field names. +- [ ] `TestExplain` — origin, both hashes, and per-file breakdown for + each origin class. +- [ ] `TestRefresh_OnMutatingCommands` — pull/update/approve/detach/ + add/import/remove each leave lock+sum consistent (verify exits 0 + immediately after). +- [ ] Cross-check invariant: pulled artifact's lock `tree_hash` == + `sources.lock` `content_hash`. + +Bats integration: + +- [ ] End-to-end: init → add local rule → source add remote skill → + approve → `lock` → `verify` (0) → tamper pulled file → `verify` + (1, ERROR) → `update` → `verify` (0). +- [ ] Linked flow: `source add --link` → `lock` → edit checkout → + `verify` (0, INFO) → `verify --strict` (1). +- [ ] Portability grep gate: no absolute paths in either file (same + gate as SPEC-007). + +## Rollout + +Additive and backwards-compatible; ships in 1.x (standing no-2.0 +decision): + +1. **Inert until adopted.** No `agents.lock` on disk ⇒ `verify` is a + friendly no-op, mutating commands skip refresh, nothing changes for + existing users. The feature activates on the first explicit + `sync-agents lock`. +2. PR 1 — lock/sum writer + `lock` command + determinism tests. +3. PR 2 — `verify` (+ `--json`, `--strict`, `--explain`) + severity + policy. +4. PR 3 — mutating-command refresh hooks + bats end-to-end. +5. README "Integrity lock" section + documented CI/cron recipe + (`sync-agents verify --json` as a scheduled drift sentinel). +6. CHANGELOG: `feat: full-context integrity lock — agents.lock + + agents.sum with lock/verify commands (SPEC-008)`. + +## Open questions + +- **Q1**: Should `verify` gain `--fix` sugar (re-run `lock` for + INFO-only drift, e.g. linked entries) or stay strictly read-only? + Lean read-only in v1; `lock` is one command away. +- **Q2**: Root-level `AGENTS.md`-adjacent files inside `.agents/` + (e.g. `config`) — sum them too? Lean yes for `config` (it toggles + quarantine — security-relevant), defer decision to implementation PR. +- **Q3**: `generated_at` in the lock at all? It aids audit but is the + only nondeterministic byte. Current answer: keep, with the + "only-touch-when-content-changed" rule; drop entirely if it proves + noisy. From c8f5bd7b775c5e67c920ac1cdc323cd8b5bb5924 Mon Sep 17 00:00:00 2001 From: nmccready Date: Thu, 9 Jul 2026 15:52:31 -0400 Subject: [PATCH 2/3] docs(site): add GitHub Pages landing site + deploy workflow Single self-contained static page at site/index.html (inline CSS, no build step) with a dark mission-control aesthetic: hero + install copy-block, 8 feature cards, pure-CSS animated terminal demo of the source add -> quarantine -> approve -> sync flow, 3-step how-it-works strip, and footer links. Deployed via actions/upload-pages-artifact + actions/deploy-pages on pushes to main touching site/**. Co-Authored-By: Claude Fable 5 --- .github/workflows/pages.yml | 37 +++ site/index.html | 520 ++++++++++++++++++++++++++++++++++++ 2 files changed, 557 insertions(+) create mode 100644 .github/workflows/pages.yml create mode 100644 site/index.html diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 0000000..c4f7a58 --- /dev/null +++ b/.github/workflows/pages.yml @@ -0,0 +1,37 @@ +name: Deploy GitHub Pages + +on: + push: + branches: [main] + paths: + - "site/**" + - ".github/workflows/pages.yml" + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: false + +jobs: + deploy: + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Upload site artifact + uses: actions/upload-pages-artifact@v3 + with: + path: site/ + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/site/index.html b/site/index.html new file mode 100644 index 0000000..a8d71cd --- /dev/null +++ b/site/index.html @@ -0,0 +1,520 @@ + + + + + +sync-agents — one set of agent rules to rule them all + + + + + + + + + + + + + + +
+ + +
+
v1.4.0 — linked sources shipped
+

One set of agent rules
to rule them all.

+

+ A package manager and sync engine for AI-agent context. Keep rules, skills, and + workflows in a single .agents/ tree — pull them from upstream with a lockfile, + scan the untrusted ones, and sync everything to Claude, Windsurf, Cursor, and Copilot. +

+
+ $ npm install -g @brickhouse-tech/sync-agents + +
+
+ also: brew install brickhouse-tech/tap/sync-agents · go install github.com/brickhouse-tech/sync-agents@latest +
+ +
+ +
+

// live session

+

Pull it. Scan it. Trust it. Sync it.

+

Remote agent context is a supply chain. sync-agents treats it like one — + every fetch is SHA-verified, statically scanned, and quarantined until you approve it.

+ +
+ +
+

// capabilities

+

Built like a package manager, because it is one.

+

Lockfiles, provenance, integrity hashes, editable installs — the tooling + discipline your dependencies get, applied to your agents' brains.

+
+
+
.yml
+

Declarative sources + lockfile

+

Declare upstream skills, rules, workflows — or whole .agents/ trees — in + sources.yaml. pull installs them reproducibly and records every + resolution in sources.lock. Commit both; clones rebuild byte-for-byte.

+
+
+
#
+

SHA pinning & integrity

+

Every ref resolves to a commit SHA via the GitHub API, and a deterministic sha256 + content hash is verified before anything touches disk. A tampered tarball or corrupted + cache aborts the install — no partial writes, ever.

+
+
+
[!]
+

Quarantine + static scanning

+

Remote content is treated as a hostile supply chain. Fetches land in + .quarantine/ after a scan for curl | bash patterns, credential + access + network calls, obfuscation, and prompt injection. Critical findings block + approve; overrides are recorded in the lock.

+
+
+
->
+

Linked (editable) sources v1.4.0

+

The npm link of agent context. source add --link=../foo-skill + symlinks a live local checkout instead of a snapshot — edits flow both ways, no + commit→push→update loop. Recorded declaratively, always as relative paths.

+
+
+
OS
+

OS-scoped routing

+

Drop artifacts into macos/, linux/, unix/, or + windows/ subdirectories and they sync only where the host matches. Your + brew rules stop polluting the Linux homelab, and vice versa.

+
+
+
idx
+

AGENTS.md auto-index

+

One generated index of everything in .agents/, symlinked to + CLAUDE.md so Claude reads it natively. watch keeps it fresh, + inheritance links chain project → team → org → global.

+
+
+
{ }
+

Multi-bucket layout

+

Rules, skills, workflows, subagents, plans, specs, hooks, and ADRs — each with its own + lifecycle. Optional buckets activate on demand, and ADR status lives in the directory + tree: proposed/, accepted/, denied/.

+
+
+
~>
+

Semantic routing

+

A Claude skill is invocable; a Windsurf workflow is a slash command. sync-agents routes + on behavioral semantics, not directory names — so nothing gets dumped into + another tool's always-on context by accident.

+
+
+
+ Syncs to + .claude/ + .windsurf/ + .cursor/ + .github/copilot/ + ~/.agents/ (global) +
+
+ +
+

// how it works

+

Three moves. Every repo. Every machine.

+

A single Go binary (shipped via npm, Homebrew, or go install) with + no runtime dependencies — it doesn't even need git to pull.

+
+
+ 01 · declare +

Declare your sources

+

Write what your agents should know in sources.yaml — a skill, a rule, or an entire upstream tree.

+
sources:
+  - skill:anthropic/skill-pack@v1.2.0
+  - rule:my-org/norms@main/rules/sec.md
+  - tree:my-org/team-agents@v2.0.0
+
+
+ 02 · pull & scan +

Pull, verify, review

+

Fetch everything, verify hashes against the lockfile, and review whatever the scanner flags before it can reach an agent.

+
$ sync-agents pull
+ 2 installed · 1 quarantined
+$ sync-agents approve code-review
+
+
+ 03 · index & sync +

Index and fan out

+

Regenerate AGENTS.md and symlink every artifact into each tool's directory. Change once, updated everywhere.

+
$ sync-agents sync
+ 4 targets linked
+ AGENTS.md → CLAUDE.md
+
+
+
+
+ + + + + + From 9ff5398bab661775231deb92a6546ee007957986 Mon Sep 17 00:00:00 2001 From: nmccready Date: Thu, 9 Jul 2026 15:53:31 -0400 Subject: [PATCH 3/3] docs(site): self-enable Pages via actions/configure-pages enablement Co-Authored-By: Claude Fable 5 --- .github/workflows/pages.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index c4f7a58..39c887a 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -27,6 +27,11 @@ jobs: - name: Checkout uses: actions/checkout@v4 + - name: Configure Pages (creates the site if not yet enabled) + uses: actions/configure-pages@v5 + with: + enablement: true + - name: Upload site artifact uses: actions/upload-pages-artifact@v3 with: