---
name: skill-ops-sync
type: skill
description: >-
  Audit parity between the Claude-side saved artefacts (canon) and the claude-ops
  repo (published mirror), and raise publish tickets where Claude is ahead so the
  agents site and the remote routines build from current source. Each run also
  sweeps frontmatter hygiene, routing canon-side defects to the Pulse queue and
  repo-side staleness to a publish ticket so a defect is raised once and stops
  recurring. One-way — Claude to repo, never repo to Claude. Reads the local
  claude-ops clone (no GitHub network). Runs as a scheduled Cowork task on the
  Linear connector; proposes only — never commits, never edits Claude.
license: Proprietary — Aled Pritchard workspace use.
---

# skill-ops-sync

Audits parity between **Claude** (the saved artefacts — **canon**) and the **claude-ops repo** (a **one-way published mirror**, downstream of Claude). The Claude-side copies are the source of truth for skill, convention, agent, task and routine content; claude-ops is published *from* them, so [agents.aledpritchard.com](http://agents.aledpritchard.com) and the remote Cloud Routines build from complete, current source. This skill detects where canon is ahead of the repo and **raises publish tickets** to close the gap. Direction is always **Claude to repo, never repo to Claude.** Follows `convention-linear`. Runs as a scheduled Cowork task. **Proposes only — never commits to the repo, never edits Claude, never deletes.**

## Why this matters

Nothing else enforces parity, and drift is silent. An artefact edited in Claude (canon) but not yet published only surfaces when the site or a remote routine builds from stale or missing source. The dangerous case is an artefact that exists in Claude but not in the repo: the page and the routines build from the repo, so that artefact simply never reaches them. This audit catches that before they do.

## What the audit checks — and what it no longer needs to

Under the **wrapper model** (decision 2026-06-25) every `routine-*` / `task-*` is an ordinary canon artefact in the skills store, and each Cloud Routine / Cowork config is a thin loader (`Read .claude/skills/<artefact-name>/SKILL.md and follow it verbatim`) holding no instructions of its own. Two consequences for this audit:

* **There is no separate task-config body to publish.** The earlier task-layer path — mirroring a live scheduled-task config into the repo — is obsolete and removed; a `task-*` / `routine-*` is audited skill-store ↔ repo exactly like a skill. (That path once published stale wording faithfully, APP-300, 2026-06-25; the wrapper model makes the failure structurally impossible, so the path is gone rather than guarded.)
* **The comparison is a site-freshness check, not an execution-correctness one.** Cowork tasks load their skill from the store **at run time**, so task *execution* already tracks current canon regardless of the repo. What the repo/local-clone comparison protects is the **agents site** (and the cloned Cloud Routines, which read from the repo): it ensures they build from current source. The old skill-canon ↔ scheduled-task-config blind spot is therefore structurally gone, and the hourly local-clone lag is harmless to task execution — it only delays site/clone freshness, which a publish ticket closes.

## Reading the repo — local clone, no GitHub network

The scheduled environment has **no GitHub credentials**: do not use a GitHub connector and do not run any git network operation (clone / fetch / ls-remote / push) — they will fail. Read the repo from the **local connected clone** at `/Users/aledpritchard/claude-ops` (its `.claude/skills` directory), the working copy mounted in this workspace, using workspace file access.

This clone reflects Aled's **last local pull**, not necessarily `origin/main` this instant — a launchd job keeps it current roughly hourly. So **record the clone's current `HEAD` commit in the report** to show the comparison basis, and treat a "Claude ahead" result as "ahead of this clone — pull to confirm against origin" rather than a certainty. If you cannot read the folder at all, stop and report that rather than guessing.

## Placement map (the rules the audit enforces)

On the **Claude (canon)** side, every authored artefact lives in `.claude/skills/`. On the **repo (claude-ops)** side the layout differs: skills, conventions, agents, core and routines live in `.claude/skills/`, while **tasks (`task-*`) live in `.claude/tasks/`** — `build-manifest.ts` classifies by the `type:` frontmatter, defaulting by directory (`.claude/skills/` → skill, `.claude/tasks/` → task). The `type:` field (skill / convention / agent / task / routine / core / workflow / playbook) sets the kind; an artefact must exist on both sides, whichever directory holds it on the repo side.

| Artefact | Claude (canon) | Repo (claude-ops) | Rule |
| -- | -- | -- | -- |
| Skills / conventions / agents | Required — source of truth | Required — published copy | Must exist in both; **Claude wins on content** |
| Tasks (`task-*`) and routines (`routine-*`) | Required — source of truth | Required — published copy + site/clone source (`task-*` under `.claude/tasks/`, `routine-*` under `.claude/skills/`) | Must exist in both; Claude wins on content. **These are legitimate saved artefacts, not orphans.** |
| Repo-native files (`build-manifest.ts`, `package.json`, the site, CI) | n/a | Required — repo is canon for these | Repo-only; no Claude copy; edited in the repo directly |
| Linear reference copies (os.Claude docs) | n/a | n/a | Reference only; never the source |

> Supersedes the earlier rule that a saved `routine-*` / `task-*` was a mis-placed "orphan to delete." Under the wrapper model (decision 2026-06-25) routines and tasks are authored as canon artefacts in the skills store and loaded by a thin scheduled-task / Cloud Routine config; a saved `task-*` / `routine-*` is therefore correct, required in both, and audited exactly like a skill. The only orphan is an artefact that exists on one side after a deletion on the other.

## Frontmatter-hygiene sweep (every run)

Frontmatter defects are not cosmetic noise to flag and move past — flagging without acting is why the same defects resurfaced run after run and were never fixed (APP-302, APP-323, APP-340, across 2026-06-25 to -27). So each run **sweeps frontmatter and routes every defect to the lane that actually closes it**, then it stops appearing. Check each artefact's frontmatter against `convention-artefact-format` (keys `name`, `type`, `description` present and in order; `type` explicit and matching the prefix; `description` the key, YAML-valid — a `": "` in an unquoted scalar is invalid and must be quoted).

Route by where the defect lives:

* **Canon-side defect** (missing `type:`, invalid/unquoted YAML `description:`, a schema gap vs `convention-artefact-format`) → the fix belongs in canon, which this skill never edits. **File it to the Pulse queue** as a `FRICTION:` note (Apps / os.Claude, `agent:PUL-5E` + `type:task` + `work:configuration`, Backlog, unassigned), exactly as APP-340 was filed. First **dedup**: if an open Pulse note already covers the same defect class, add a dated recurrence comment instead of opening a second. The Pulse retro drain then produces the corrected `.skill`, and a later run of this skill publishes it. **Do not raise a publish ticket for a file whose canon frontmatter is still broken** — publishing it would overwrite the repo's valid copy with the broken one; wait until canon is fixed.
* **Repo-side-only staleness** (canon frontmatter is correct, the repo copy lags) → the normal **publish ticket**, like any "Claude ahead" content difference.
* **Genuinely identical frontmatter** → in sync; stays silent.

Net effect: each defect is raised once into a fix lane and then disappears from later reports, rather than being re-flagged indefinitely.

## Audit behaviour

Runs as a scheduled Cowork task on the **Linear connector** plus workspace file access to the local clone.

1. **Enumerate** the saved skills, conventions, agents, tasks and routines on the Claude side and the corresponding directories in the local claude-ops clone (`.claude/skills`). Record the clone's `HEAD` commit.
2. **Compare** content, normalising whitespace before diffing. Run the **frontmatter-hygiene sweep** (above) as part of this pass.
3. **Claude ahead of repo** (changed in Claude, or new in Claude and not yet in the repo) → BEFORE raising anything, **dedup**: search Linear (Apps / os.Claude) for an existing publish ticket for that artefact — open, or resolved/Done within the last ~7 days. If one exists, **skip** and note "appears published or in-flight; local clone may be behind — pull to confirm" (prevents duplicate tickets when the clone lags origin). Otherwise — and provided the canon frontmatter is clean (a broken-frontmatter file is held back per the sweep) — **raise a publish ticket straight to Todo (ready) with `agent:F0-RGE` (Forge)**, not Backlog/Relay. A publish ticket is **born ready**: it is mechanical and fully specified (the action is "commit the attached file verbatim"), so it skips the refinement gate to close the sync gap (the standing "unless he has said otherwise" exception, convention-ticket). Attach the current Claude-side `SKILL.md` as a file (see *The publish handoff*) and **embed the acceptance criteria (Pattern A)**, in order: (a) **verify the artefact is genuinely absent or behind on `origin/main` first** — pull/compare; if origin already carries it, close as already-published and do **not** double-commit; (b) commit the attached file **byte-for-byte**; (c) confirm the agents-site render builds. Forge opens the PR and **never merges** — the operator gate and the Relay post-approval merge are unchanged. Never commit directly. **Keep each publish ticket to ≤10 artefacts.** A larger batch (16 attachments on APP-392, 2026-06-30) crowds a single exec session against its context window and forces a compaction-and-re-fetch round mid-run; the run recovers, but the margin is thin and grows worse with size. When more than ten artefacts are Claude-ahead in one run, split them across several publish tickets of ≤10 each rather than raising one oversized ticket.
   * **Link a sibling repo-lane dependency ticket so it is not left stale-open.** Where the canon being published has a **known repo-native dependency tracked by a separate open repo-lane ticket** — an `OPS:`/`FIX:` on `agent:R3-LAY`/`F0-RGE`, typically filed by the Pulse drain per `skill-ops-retro` 4a (worked case: APP-407 tracked adding the `operator` type to `build-manifest.ts`, a dependency of the operator-layer publish) — **relate the publish ticket to that sibling** and note in the publish ticket that executing it may *incidentally* make the same repo-native change to unblock itself. This is so that when either lands, triage can recognise the sibling as already-satisfied and close it, rather than leaving it live under `R3-LAY` looking like unbuilt work. Worked case: APP-409 (a publish ticket) hit the repo-native blocker and incidentally shipped the `operator`-type change in PR #133 — exactly what the separate APP-407 was raised to make — and APP-407 then sat live in Backlog until the triage freshness check caught it (2026-07-03). Relating the two at publish time closes that coordination gap at source; the freshness check remains the backstop, not the only guard.
4. **In repo, missing from Claude** → either a legitimately repo-native file (ignore — see placement map), or an orphan left after a Claude-side deletion → flag for Aled to confirm removal from the repo. Never delete.
5. **Edited on both sides** → Claude is canon, but do not assume: flag to Aled with a diff and let him confirm before a publish ticket is raised.
6. **Report** — end every run with a short parity report: the clone `HEAD` compared against, in-sync count, publish tickets raised, sibling repo-lane tickets related (IDs), frontmatter defects captured to the Pulse queue (and recurrence comments added), items skipped as already-published/in-flight, items flagged, orphans surfaced (both directions).

## The publish handoff (Claude → repo)

This is how a canon change reaches the repo — it applies whenever Claude is ahead.

- Canon content is **authored Claude-side** (the saved artefacts). This skill never authors content; it only detects drift and proposes the publish.
- For each ahead-of-repo artefact, the run raises the born-ready publish ticket (**Todo + `agent:F0-RGE`**) and **attaches the current Claude-side `SKILL.md` as a file** — the single content-delivery standard: any size, byte-for-byte, never pasted snippets (which risk transcription drift). The ticket instructs Forge to commit the attached file verbatim. The PR goes through the normal Forge → Prism → operator-gate → Relay-merge route; nothing merges without the operator.
- **Attachment mechanism (Linear file upload):** `prepare_attachment_upload` (issue, filename, `contentType: text/markdown`, exact byte size) → `PUT` the raw bytes to the returned signed URL, sending its headers verbatim, within the 60-second window → `create_attachment_from_upload` (issue, assetUrl). One file at a time: prepare → PUT → finalize before preparing the next.
- The reverse direction does not exist. The repo is never pulled back into Claude, and this skill emits no drop-in to install into Claude. If the repo is somehow ahead (an edit made in the repo by mistake), that is drift to flag for Aled — never to apply to Claude.

## Guardrails

- **Claude wins on content** — but never publish a both-sides divergence by stealth; flag conflicts for Aled.
- **Proposes only** — raises tickets and files Pulse `FRICTION:` notes; never commits to claude-ops, never edits or saves a Claude-side artefact, never deletes on either side.
- **A canon fix is fixed in canon first.** Where the audit finds canon that needs correcting — including a frontmatter defect — the correction belongs in Claude (the operator adds it via the save button, drained from the Pulse queue), and that takes priority; any mid-flight publish ticket is then reconciled — refreshed or closed — as part of the next sync. Tickets are downstream of canon, so never publish a file whose canon frontmatter is still broken.
- **Hygiene is fixed, not re-flagged.** Each frontmatter defect is routed once to its fix lane (canon → Pulse queue, repo-only → publish ticket) and then stays silent; a defect that reappears in every report is the failure this sweep removes.
- **A publish ticket links its sibling repo-lane dependency, not just its canon.** When the canon being published has a repo-native dependency tracked by a separate open ticket, relate the two at publish time so an incidental fix made by one does not leave the other stale-open — the coordination gap the triage freshness check would otherwise have to catch alone (worked case: APP-409 incidentally satisfied APP-407, 2026-07-03).
- **Tasks and routines are canon artefacts**, audited like skills — never an orphan to delete merely for being a `task-*` / `routine-*`, and never published from their loader config rather than their canon artefact.
- **The audit is a site-freshness check, not an execution guard** — Cowork tasks already run from current canon at run time; the repo comparison protects the agents site and the cloned routines, so clone lag delays freshness, never correct execution.
- **Repo-native files** (`build-manifest.ts`, `package.json`, the Next.js site, CI) are canon in the repo and have no Claude copy — never flag them as missing-from-Claude.
- **Linear reference copies are reference only** — never the source.
- **No GitHub network** — read the local clone only; never clone/fetch/push.
- **Cap publish tickets at ≤10 artefacts.** Split a larger Claude-ahead set across several tickets so no single exec session is pushed into context compaction mid-run.

## Setup

- Deployed as a **Cowork scheduled task** (cloud) via a loader config — `task-ops-sync` reads this skill.
- **Connectors:** Linear, plus workspace file access to the local `claude-ops` clone. No GitHub.
- **Cadence:** daily or slower.
- Smoke test: the first run's parity report.
