diff --git a/product/specs/skill-distribution-plan.md b/product/specs/skill-distribution-plan.md index 97b7270..6855150 100644 --- a/product/specs/skill-distribution-plan.md +++ b/product/specs/skill-distribution-plan.md @@ -1,14 +1,10 @@ # Plan — skill distribution -> **Status: implementing.** Implements -> [`skill-distribution-spec.md`](skill-distribution-spec.md). Phases 1–5 landed -> (scaffold + all seven shipping skills authored, packaging, bootstrap, release -> wiring, local symlink); Phase 6 folded into Phase 1 (see Deviations); Phase 7 -> (cut a tag, graduate docs) is the remaining owner action. +> **Status: planning.** Implements +> [`skill-distribution-spec.md`](skill-distribution-spec.md). No phases started. -Branch: `claude/charming-clarke-s2nx1d` (rebased onto `main` after the repo -moved to `github.com/abegong/katalyst` and a GoReleaser release pipeline -landed — see [Deviations](#deviations)). +Branch: `claude/practical-mayer-uuo45q` (PR +[#20](https://github.com/katabase-ai/katalyst/pull/20)). ## Strategy @@ -82,59 +78,47 @@ tracks the latest Release. ### Phase 3 — Shared bootstrap (fetch the CLI) 1. Implement the bootstrap so the skill installs/locates the CLI by **fetching - and unpacking the CLI archive from the latest GitHub Release**, falling back - to `go install github.com/abegong/katalyst@latest`. -2. Detect OS/arch and pick the matching release **archive** — GoReleaser ships - `katalyst___.tar.gz` (`.zip` on Windows), so the bootstrap - downloads that archive and extracts the `katalyst` binary, rather than - fetching a bare binary. (`checksums.txt` is published alongside for optional - verification.) + the binary from the latest GitHub Release**, falling back to `go install + github.com/katabase-ai/katalyst@latest`. +2. Detect OS/arch and pick the matching release asset name (must agree with the + naming Phase 4 produces). 3. Make it idempotent: reuse an already-installed binary; only download when missing. No version pin (out of scope) — track latest. 4. Decide and implement how the bootstrap ships inside each `.skill` (single shared source copied in at package time, so there is one bootstrap to maintain). -### Phase 4 — Attach skills to the existing GoReleaser release - -The tag-triggered workflow this phase originally proposed **already exists** on -`main`: `.github/workflows/release.yml` fires on `v*` tags with -`permissions: contents: write` and runs GoReleaser (`.goreleaser.yml`), which -builds the cross-platform CLI matrix and publishes the Release. So this phase -shrinks to **extending** that release to also carry the `.skill` artifacts — -the binary matrix, the tag trigger, and the token permission are all done. - -1. Package the skills during the release. GoReleaser supports a `before.hooks` - entry — add `make skills` there (or as a dedicated hook) so the `.skill` - files exist before the release step runs. -2. Attach every shippable `.skill` to the Release via GoReleaser's - `release.extra_files`, so they land beside the binary archives and - `checksums.txt` in one run. (Placeholders never produce a `.skill`, per - Phase 2, so nothing extra is needed to exclude them.) -3. Optionally add a `workflow_dispatch` trigger to `release.yml` so the job can - be **dry-run** from the Actions tab without cutting a tag. -4. Leave per-PR CI (`ci.yml`) unchanged. +### Phase 4 — Release workflow -> **Already settled on `main` (was Phase 4 work):** the GOOS/GOARCH matrix -> (GoReleaser builds linux/darwin/windows × amd64/arm64), `permissions: -> contents: write` on the release job, and the `v*` tag trigger. The asset -> *names* are now GoReleaser's archive template, not free to choose — Phase 3's -> bootstrap must match them. +1. Add a tag-triggered workflow (`on: push: tags: ['v*']`), separate from the + `test` job in `ci.yml`. Also add a `workflow_dispatch` trigger so the job can + be **dry-run** from the Actions tab without cutting a tag. +2. Grant the job `permissions: contents: write` so the built-in `GITHUB_TOKEN` + can create the Release and upload assets — **no PAT or repo secret required.** + (Caveat: if the org/repo default caps `GITHUB_TOKEN` to read-only and the + explicit block is overridden by policy, the upload step 403s; the fix is a + repo-admin toggle at Settings → Actions → General → Workflow permissions → + "Read and write." Not changeable from here.) +3. Build cross-platform CLI binaries via a GOOS/GOARCH matrix (the current + `go build -o bin/katalyst .` is host-only), naming each asset to match what + the Phase 3 bootstrap fetches (e.g. `katalyst__`). +4. Run `make skills` in the job. +5. Upload the binaries **and** every `.skill` as assets on the Release for that + tag, in one workflow run. +6. Leave per-PR CI unchanged. > **Owner action.** Cutting a real release means pushing a `v*` tag — a > deliberate act outside this branch's scope, so it stays with the repo owner > (or is done with explicit go-ahead). Everything else in this phase is just the -> edited `.goreleaser.yml` / workflow file. +> committed workflow file. ### Phase 5 — Local dev symlink 1. Add a `make` target that symlinks each `skills/{name}/` into `.claude/skills/` so they auto-load in a working copy, modeled on `sync_skill_links_from_cursor` in `scripts/agent-link-utils.sh`. -2. No `.gitignore` change is needed: `main` already ignores **all** of - `.claude/` (and `.codex/`), so the symlinks stay uncommitted. Decide whether - to also mirror into `.codex/skills/` (a `setup-codex-skills.sh` now exists - for the contributor skills) or keep product skills Claude-only for now. +2. Add `.claude/skills/` to `.gitignore` (the specific path, not all of + `.claude/`) so the symlinks stay uncommitted. ### Phase 6 — Author the remaining skills @@ -166,70 +150,20 @@ the binary matrix, the tag trigger, and the token permission are all done. Mirrors the spec's test checklist; these assertions prove the change. -- [x] `make skills` produces one `{name}.skill` per **shippable** directory +- [ ] `make skills` produces one `{name}.skill` per **shippable** directory under `skills/`, each unzipping with `SKILL.md` at the archive root, and - emits no artifact for `status: placeholder` skills. *(Verified: 7 skills - packaged, both `katalyst-migrate-*` placeholders excluded; covered by - `internal/skillpack` tests.)* -- [x] `make skill SKILL=` packages a single skill. *(Verified, including - the missing-`SKILL` guard.)* -- [x] `make clean` removes the `.skill` artifacts. *(They live in `bin/`, which - `clean` already `rm -rf`s.)* -- [x] The local-dev target symlinks each `skills/{name}/` into `.claude/skills/`; - the symlinks are git-ignored. *(`make skills-link`; placeholders skipped.)* + emits no artifact for `status: placeholder` skills. +- [ ] `make skill SKILL=` packages a single skill. +- [ ] `make clean` removes the `.skill` artifacts. +- [ ] The local-dev target symlinks each `skills/{name}/` into `.claude/skills/`; + the symlinks are git-ignored. - [ ] On a tag, the release workflow uploads the cross-platform binaries and - every `.skill` as assets on that Release. *(Wired in `.goreleaser.yaml` + - `release.yml`; needs a real tag or `workflow_dispatch` dry-run to confirm — - owner action, Phase 7.)* + every `.skill` as assets on that Release. - [ ] A `.skill` downloaded from a Release installs via "Save skill" with no repo clone, and its bootstrap fetches the matching CLI binary from the - latest Release (with `go install` fallback). *(Manual, Phase 7.)* -- [x] `make all` (vet + test + build) still green; per-PR CI unchanged. + latest Release (with `go install` fallback). +- [ ] `make all` (vet + test + build) still green; per-PR CI unchanged. ## Deviations -Recorded when the branch was rebased onto `main`, after `main` moved well past -the merge base this spec was first written against: - -- **Repo/module renamed `katabase-ai/katalyst` → `abegong/katalyst`.** All - `go install` references and the bootstrap fallback now use - `github.com/abegong/katalyst@latest`. -- **The release pipeline already exists (GoReleaser).** What was a from-scratch - Phase 4 (tag trigger, GOOS/GOARCH matrix, `contents: write`) is already on - `main` in `.github/workflows/release.yml` + `.goreleaser.yml`. Phase 4 now - only *extends* that release to package and attach the `.skill` files. -- **Release assets are GoReleaser archives, not raw binaries.** The bootstrap - (Phase 3) downloads and unpacks `katalyst___.tar.gz` - (`.zip` on Windows) instead of fetching a bare binary; asset names follow - GoReleaser's template rather than a name this plan chooses. -- **`.gitignore` already ignores all of `.claude/`/`.codex/`.** Phase 5 no - longer needs a per-path ignore entry; only the symlink target is new. -- **Lifecycle framing moved.** The "Why Katalyst" framing now lives in - `docs/content/welcome.md` and names **Catalog / Define / Reshape** as headline - features (no standalone **Enforce** stage). The deploy cluster is kept as-is - and reframed as the setup that installs day-to-day enforcement, not a named - lifecycle stage. Phase 7 graduation should fold rationale into - `welcome.md`/deep-dives accordingly. - -Recorded during implementation (Phases 1–5): - -- **Phase 6 folded into Phase 1.** Rather than leave `katalyst-overview`, - `katalyst-catalog`, `katalyst-identify-collections`, and - `katalyst-define-schemas` as empty front-matter stubs (which `make skills` - would package as empty `.skill`s), all seven shipping skills were authored with - real content up front. The two reshape skills remain true placeholders. -- **Packaging is a Go tool, not shell.** `internal/skillpack` (logic, with an - external-package test scaffolding a temp skills tree) plus a thin - `cmd/skillpack` wrapper, run by `make skills` — mirrors the `cmd/gendocs` - precedent and gives placeholder-skipping robust YAML front-matter parsing and - real test coverage, which a shell `zip` loop would not. -- **`.skill` artifacts output to `bin/`.** Already git-ignored and already removed - by `make clean`, so no new ignore entry or clean rule was needed; GoReleaser's - `release.extra_files` globs `./bin/*.skill`. -- **Bootstrap written now (Phase 3), not deferred.** `skills/bootstrap.sh` is the - single source; `cmd/skillpack` copies it into each `.skill` at the archive root. -- **Local symlink targets `.claude/skills/` only.** `make skills-link` (via - `scripts/link-product-skills.sh`, which skips placeholders). Codex mirroring is - deferred — easy to add later by pointing the script at `.codex/skills/` too. -- **Dry-run trigger added.** `release.yml` gained a `workflow_dispatch` with a - `dry_run` input that runs GoReleaser `--snapshot --skip=publish`. +_None yet._ diff --git a/product/specs/skill-distribution-spec.md b/product/specs/skill-distribution-spec.md index eb4a7a2..d60126a 100644 --- a/product/specs/skill-distribution-spec.md +++ b/product/specs/skill-distribution-spec.md @@ -1,51 +1,57 @@ # Skill distribution -> **Status: planning.** Commit a family of user-facing katalyst skills under -> `skills/`, versioned with the CLI, and ship each as a `.skill` attached to the -> existing GitHub Release. `make skills` packages them; the GoReleaser release -> uploads them; a shared bootstrap fetches the CLI at install. +> **Status: planning.** A family of user-facing katalyst skills across the +> content lifecycle — **katalyst-overview** (orientation/router), **katalyst-catalog**, **define** +> (two cross-referencing skills, `katalyst-identify-collections` and `katalyst-define-schemas`), +> and a **katalyst-deploy** cluster (`katalyst-deploy` plus `katalyst-deploy-precommit-hook` and +> `katalyst-deploy-cli-gating`, setting up automatic enforcement) — committed under +> `skills/` and versioned with the CLI. Two Reshape-stage placeholders +> (`katalyst-migrate-schema`, `katalyst-migrate-storage`) are committed as stubs but excluded from +> release until they have content. In scope: package each shippable skill as a +> `.skill` and attach it to GitHub Releases alongside cross-platform binaries, +> which the skills' shared bootstrap fetches at install. `make skills` packages +> them; a tag-triggered release workflow uploads them. Out of scope: marketplace +> plugins (a later channel) and skill↔CLI version coupling. ## Overview -A *skill* — a `SKILL.md`, its references, and a bootstrap — teaches a -Claude/Cowork agent to drive the katalyst CLI for one job. Katalyst spans a -workflow: catalog the content you have, define its language and structure, -enforce that structure day to day, and reshape it as needs change -(`docs/content/welcome.md` names **Catalog**, **Define**, and **Reshape** as the -headline features; enforcement is the day-to-day use those set up). These skills -need a maintenance home that versions them with the CLI **and** a way for users -who never touch Git to install them. Those are separable: **where a skill is -versioned** and **how users obtain it** need not be the same place. This spec -commits the skills to the repo as the single source of truth and ships each as a -downloadable release artifact, so committing them never implies users need repo -access. +Katalyst is a content-consistency layer that spans a lifecycle: catalog the +content you have, define its language and structure, enforce that structure +day to day, and reshape it as needs change (see +`docs/content/why-katalyst.md`). A +*skill* — a `SKILL.md`, its references, and a bootstrap — teaches a Claude/Cowork +agent to drive the CLI for one of those jobs. These skills need a maintenance +home that versions them with the CLI, and a way for users who never touch Git +to install them. Those are separable: **where a skill is versioned** and **how +users obtain it** do not have to be the same place. This spec commits the skills +to the repo as the single source of truth and ships each as a downloadable +release artifact, so committing them never implies users need repo access. ## Scope -In scope: **katalyst's own release cycle** — how skills are sourced, packaged, -and published through Channel 1 (the `.skill`-on-Releases download). Out of -scope: +This spec covers **katalyst's own release cycle** — how skills and binaries are +sourced, packaged, and published through Channel 1 (the `.skill`-on-Releases +download). Out of scope: - **The user's deployment cycle** — how an adopter wires katalyst into their - environment (a pre-commit hook, a gate on writes to its content). That is skill - *content* — what the **katalyst-deploy** cluster teaches — authored with those - skills. -- **Channel 2, marketplace plugins** — recorded in Design as the future - direction, not built here. + environment (a pre-commit hook, a gate on directory access). That is skill + *content* — what the **katalyst-deploy** cluster teaches — authored with those skills. +- **Channel 2, marketplace plugins** — recorded below as the future direction, + but not built here. - **Skill ↔ CLI version coupling** — the bootstrap tracks the latest Release; pinning a skill to a CLI version is a later concern. ## Value -Committing the skills alongside the CLI keeps them in lockstep — a check rename -and the skill update that documents it land in one PR, reviewed together. But -"committed to the repo" reads as "clone the repo to get it," which is wrong for -the audience: these users run a Claude client, not `git`. Publishing a `.skill` -per skill to GitHub Releases breaks that false coupling. Maintainers get -versioned-with-the-tool authoring; users get a download link and a Settings -panel, and can install only the stage they need. One source feeds both channels, -so the committed-vs-plugin decision only ever changes install UX — never whether -repo access is required. +Committing the skills alongside the CLI is what keeps them in lockstep — a check +rename and the skill update that documents it land in one PR, reviewed together. +But "committed to the repo" reads as "clone the repo to get it," which is wrong +for the audience: these users run a Claude client, not `git`. Publishing a +`.skill` per skill to GitHub Releases breaks that false coupling. Maintainers +get versioned-with-the-tool authoring; users get a download link and a Settings +panel, and can install only the lifecycle stage they need. One source feeds both +channels, so the committed-vs-plugin decision only ever changes install UX — +never whether repo access is required. ## Current State @@ -54,32 +60,23 @@ repo access is required. holds *contributor* agent skills — `write-spec`, `write-docs` — mirrored into `.claude/skills/` and `.codex/skills/` for people working *on* katalyst. The new skills are *product* artifacts for people *using* katalyst.) -- **A release pipeline already exists.** `.github/workflows/release.yml` fires - on `v*` tags with `permissions: contents: write` and runs GoReleaser - (`.goreleaser.yml`), which builds the cross-platform CLI matrix - (linux/darwin/windows × amd64/arm64) and publishes a GitHub Release. It does - **not** know about skills: it uploads only the binary archives and - `checksums.txt`. (The `test`/`docs` jobs in `ci.yml` are a separate - build/lint gate on PRs and `main`; they upload nothing.) -- **Release assets are archives, not raw binaries.** GoReleaser publishes - `katalyst___.tar.gz` (`.zip` on Windows) plus - `checksums.txt`, so anything provisioning the CLI must download and unpack an - archive, not a bare binary. `make build` still runs `go build -o bin/$(BINARY) - .` for a single host-platform binary; `README.md` documents install via `go - install github.com/abegong/katalyst@latest` or `make build` from source. -- **How-to guides are separate, human docs.** `docs/content/how-to/` holds task - recipes for human readers, covering much the same task taxonomy the skills do - (`add-a-schema`, `configure-rules`, `profile-an-existing-wiki-*`, - `validate-in-ci`). Skills are **self-contained at runtime** (see Design): an - agent gets everything it needs from the installed skill plus the CLI, with no - dependency on the docs site being reachable. Relating the two editorially is a - separate question, resolved in Design. -- **Skill symlinks have a precedent.** `scripts/setup-claude-code.sh` and - `scripts/setup-codex-skills.sh` (both via `sync_skill_links_from_cursor` in - `scripts/agent-link-utils.sh`) symlink each `.cursor/skills/*` into - `.claude/skills/` and `.codex/skills/`. `.gitignore` excludes **all** of - `.claude/` and `.codex/`, so those mirrors stay uncommitted with no per-path - entry. This is the model the local-dev symlink reuses. +- **No release pipeline.** `.github/workflows/ci.yml` defines one `test` job on + push to `main` and on PRs: tidy-check, vet, race tests, `make build`, + `docs-gen-check`. There is no tag trigger, no cross-platform build, and + nothing uploads release assets. +- **One local binary.** `make build` runs `go build -o bin/$(BINARY) .` — a + single host-platform binary. `README.md` documents install via `go install + github.com/katabase-ai/katalyst@latest` or `make build` from source. There is + no packaging or multi-platform target. +- **How-to guides are separate, human docs.** `docs/content/how-to/` holds + task recipes for human readers. Skills are independent of them (see Design): + an agent gets everything it needs from the installed skill plus the CLI, with + no dependency on the docs site. +- **Skill symlinks have a precedent.** `scripts/setup-claude-code.sh` (via + `sync_skill_links_from_cursor` in `scripts/agent-link-utils.sh`) symlinks each + `.cursor/skills/*` into `.claude/skills/`. `.gitignore` excludes + `.claude/skills/` and `.codex/skills/`, so those mirrors stay uncommitted. + This is the model the local-dev symlink reuses. ## Design @@ -88,28 +85,25 @@ the same folders. ### The skill family -Skills track the workflow stages in `docs/content/welcome.md` — Catalog, -Define, Reshape — plus enforcement *setup* and an orientation skill that spans -them. Enforcement is not a welcome.md headline stage; it is the day-to-day use -Define establishes and Reshape revises, set up once by the **katalyst-deploy** -cluster. +Skills track the lifecycle stages in "Why Katalyst," plus an orientation skill +that spans them: -| Skill | Stage | What it teaches the agent to do | +| Skill | Lifecycle stage | What it teaches the agent to do | |---|---|---| | **katalyst-overview** | Orientation (all) | What katalyst is, its model and vocabulary (collections, items, schemas, checks), and which skill to reach for. The front door and router; does no task work itself. | -| **katalyst-catalog** | Catalog | Take stock of existing content in a specific knowledge base, surface its candidate collections, get oriented. | -| **katalyst-identify-collections** | Define (1 of 2) | Identify the collections — the recurring kinds of item the knowledge base is full of. Points to **katalyst-define-schemas** as the next step. | -| **katalyst-define-schemas** | Define (2 of 2) | Define each collection's schema — the fields its items must have and the constraints they must satisfy. Points back to **katalyst-identify-collections** as its prerequisite. | -| **katalyst-deploy** | Enforce (setup) | Set up automatic enforcement *once*. Knows **both** mechanisms, helps choose, and routes to the two specific skills below. | -| **katalyst-deploy-precommit-hook** | Enforce (setup) | Install a pre-commit hook that runs `katalyst check`, so violations are caught at commit time. | -| **katalyst-deploy-cli-gating** | Enforce (setup) | Gate writes to the project's content through the CLI, so writes are validated as they happen. | +| **katalyst-catalog** | Catalog | Take stock of existing content in a specific knowledge base, map the main concepts, get oriented. | +| **katalyst-identify-collections** | Define (1 of 2) | Identify the collections — the object types the knowledge base has repeatable instances of. Points to **katalyst-define-schemas** as the next step. | +| **katalyst-define-schemas** | Define (2 of 2) | Define each collection's schema — the properties and invariants of its items. Points back to **katalyst-identify-collections** as its prerequisite. | +| **katalyst-deploy** | Enforce | Set up automatic enforcement *once*. Knows **both** mechanisms, helps choose, and routes to the two specific skills below. | +| **katalyst-deploy-precommit-hook** | Enforce | Install a pre-commit hook that runs `katalyst check`, so violations are caught at commit time. | +| **katalyst-deploy-cli-gating** | Enforce | Gate write access to the content directory through the CLI, so writes are validated as they happen. | | **katalyst-migrate-schema** | Reshape | *Placeholder — no content yet.* Migrate content when a collection's schema changes. | | **katalyst-migrate-storage** | Reshape | *Placeholder — no content yet.* Migrate when the storage layer changes. | Each is a separate job with a different agent posture, so each is a separate skill — independently discoverable and installable. The set is additive: -packaging and the release take whatever shippable skills exist under `skills/`, -so stages can land one at a time without reworking the pipeline. +packaging and the release workflow take whatever shippable skills exist under +`skills/`, so stages can land one at a time without reworking the pipeline. **Naming convention: every skill is `katalyst-`-prefixed.** Channel 1 installs each `.skill` individually into one flat namespace in the client, with no @@ -117,53 +111,52 @@ enclosing folder or plugin to group them — so generic names (`overview`, `catalog`, `deploy`) would collide with unrelated skills and give the agent's selection a weaker signal. A uniform prefix disambiguates, clusters the family in any sorted list, matches the "use **katalyst**" phrasing, and yields clear -artifact names (`katalyst-deploy.skill`). The prefix is the shipped identity: it -is the `name` in each `SKILL.md`, the `.skill` artifact name, **and** the +artifact names (`katalyst-deploy.skill`). The prefix is the shipped identity: +it is the `name` in each `SKILL.md`, the `.skill` artifact name, **and** the directory under `skills/`, kept 1:1 so there is no dir→name mapping to drift. (Chosen over a `-with-katalyst` suffix, which would scatter the skills under their action letter instead of grouping them.) The define stage is **two discrete skills**, not one: `katalyst-identify-collections` -(name the collections) precedes `katalyst-define-schemas` (formalize each -collection's fields and constraints). They **cross-reference** each other — identify points -forward, define-schemas points back — so the two-step flow is explicit without -merging two jobs an agent invokes at different times into one skill. +(name the object types) precedes `katalyst-define-schemas` (formalize each type's +fields and invariants). They **cross-reference** each other — `katalyst-identify-collections` +points forward, `katalyst-define-schemas` points back — so the two-step flow is explicit +without merging two jobs an agent invokes at different times into one skill. ### Orientation: the `katalyst-overview` skill -`katalyst-overview` is the family's front door. It carries katalyst's mental -model and vocabulary — collections, items, schemas, checks, the workflow — and -routes an agent to the right task skill for the goal at hand. It does no task -work itself, which keeps it distinct from `katalyst-catalog`: that one takes -stock of a *specific* knowledge base, while `katalyst-overview` explains -katalyst-the-tool independent of any repo. Broadly triggered and a candidate to -install by default, it is how an agent learns katalyst exists and which skill to -load — the same "don't make the agent guess" concern the deploy cluster -addresses, met at the discovery layer. +`katalyst-overview` is the family's front door. It carries katalyst's mental model and +vocabulary — collections, items, schemas, checks, the lifecycle — and routes an +agent to the right task skill for the goal at hand. It does no task work itself, +which keeps it distinct from `katalyst-catalog`: that one takes stock of a *specific* +knowledge base, while `katalyst-overview` explains katalyst-the-tool independent of any +repo. Broadly triggered and a candidate to install by default, it is how an +agent learns katalyst exists and which skill to load — the same "don't make the +agent guess" concern the deploy cluster addresses, met at the discovery layer. ### Enforcement is deployed, not invoked -Enforcement setup is a **cluster**, not a runbook the agent re-runs on every +The Enforce stage is a **cluster**, not a runbook the agent re-runs on every write. Relying on an agent to *choose* to run `check`/`fix` each time is fragile -— the guardrail only holds when it is structural. `katalyst-deploy` is the -umbrella skill: it knows **both** mechanisms, helps pick between them, and routes -to the specific skill. `katalyst-deploy-precommit-hook` installs a pre-commit -hook that runs `katalyst check`; `katalyst-deploy-cli-gating` gates write access -to the project's content through the CLI. Either way enforcement is set up once -and then runs automatically, no matter which agent — or human — does the writing; +— the guardrail only holds when it is structural. `katalyst-deploy` is the umbrella +skill: it knows **both** enforcement mechanisms, helps pick between them, and +routes to the specific skill. `katalyst-deploy-precommit-hook` installs a pre-commit hook +that runs `katalyst check`; `katalyst-deploy-cli-gating` gates write access to the +content directory through the CLI. Either way enforcement is set up once and +then runs automatically, no matter which agent — or human — does the writing; the day-to-day loop needs no skill of its own. The three cross-reference each -other, the same way the two define skills do. (How each mechanism is wired is the -skills' *content*, out of scope per [Scope](#scope).) +other, the same way the two `define` skills do. (How each mechanism is wired is +the skills' *content*, out of scope per [Scope](#scope).) ### Reshape: placeholders The Reshape stage is two **placeholder** skills with no content yet: -`katalyst-migrate-schema` (content migration when a collection's schema changes) -and `katalyst-migrate-storage` (when the storage layer changes). Committed to -reserve the names and capture intent, they are marked `status: placeholder` in -their front matter and **excluded from packaging/release** until they carry real -content — so the additive pipeline already knows about them without shipping -empty skills. +`katalyst-migrate-schema` (content migration when a collection's schema changes) and +`katalyst-migrate-storage` (when the storage layer changes). They replace the single +`reshape` skill from the earlier draft. Committed to reserve the names and +capture intent, they are marked `status: placeholder` in their front matter and +**excluded from packaging/release** until they carry real content — so the +additive pipeline already knows about them without shipping empty skills. ### Source of truth: `skills/{name}/` @@ -203,52 +196,26 @@ Maintainers edit them here. A change to the CLI surface and the skill text that documents it land in the same PR, so a skill never drifts from the tool. This is a **new top-level `skills/` directory**, deliberately separate from `.cursor/skills/`: that tree is contributor tooling synced to local agents, this -tree is shipped product. Naming them apart keeps the two audiences from colliding -in one folder. - -### Relationship to the how-to guides - -Skills and the how-to guides under `docs/content/how-to/` are **parallel -coverage of one task taxonomy for two audiences** — an agent driving the CLI and -a human reading recipes — not one source generated from the other and not -strangers. The mapping is already close to one-to-one: - -| How-to guide | Matching skill | -|---|---| -| `profile-an-existing-wiki-by-hand` / `…-with-an-agent` | **katalyst-catalog** | -| `add-a-schema` | **katalyst-define-schemas** | -| `configure-rules` | **katalyst-define-schemas** (its checks) | -| `validate-in-ci` | **katalyst-deploy** cluster | - -The relationship has three parts: - -- **Shared taxonomy and vocabulary.** Both organize around the lifecycle stages - and use the glossary's terms, so a task is never named one way for humans and - another for agents. Kept consistent by review — a change to a task's CLI - surface updates both the guide and the skill in the same PR — **not** by - generating one from the other. -- **Bidirectional cross-links at the human-facing edges.** Each how-to guide with - a matching skill points to it ("prefer to have an agent do this? install - `katalyst-`," with the download link); each skill's *human-facing* front - matter points back to the guide for the manual path. Discovery flows both ways. -- **Runtime self-containment is preserved.** The cross-link is editorial metadata, - not a runtime dependency: a skill's executable `SKILL.md` body still carries - everything the agent needs, so it functions with the docs site unreachable. The - back-link is for a human reading the skill, never a fetch the agent performs. - -This is the deliberate middle ground between two extremes the spec rejects: -generating skills from the guides (couples agent behavior to human docs and to a -reachable docs site) and treating them as unrelated (drift, and a worse -experience for a user who meets one and not the other). +tree is shipped product. Naming them apart keeps the two audiences from +colliding in one folder. + +### Independent of the how-to guides + +Skills are **self-contained**; they do not reference the how-to guides under +`docs/content/how-to/`. This diverges from treating the two as one procedural +source, on purpose: an agent must function without reaching the docs site at +runtime, and the two audiences (an agent driving the CLI vs. a human reading +recipes) diverge in what they need. The how-to guides and the skills may cover +the same tasks, but neither is generated from or depends on the other. ### Channel 1 (now): `.skill` on GitHub Releases A `.skill` is a zip of a skill directory with `SKILL.md` at its root. `make -skills` produces one `.skill` per shippable skill under `skills/` (e.g. -`katalyst-deploy.skill`, `katalyst-catalog.skill`); the release attaches them to -the GitHub Release for each tag, beside the CLI archives. Users download the -skill(s) they want from the releases page and install through the client's -**Settings → Capabilities → "Save skill."** No clone, no Git. +skills` produces one `.skill` per skill under `skills/` (e.g. `katalyst-deploy.skill`, +`katalyst-catalog.skill`); the release workflow attaches them to the GitHub Release for +each tag, beside the CLI binaries. Users download the skill(s) they want from +the releases page and install through the client's **Settings → Capabilities → +"Save skill"**. No clone, no Git. ### Channel 2 (out of scope): marketplace plugins @@ -262,28 +229,26 @@ ownership are deferred with it. ### Packaging: `make skills` -A `skills` target in the `Makefile` zips each `skills/{name}/` to `{name}.skill` -with `SKILL.md` at the archive root (not nested under a `{name}/` prefix — the -client expects `SKILL.md` at the top). It **skips skills marked `status: -placeholder`** in their `SKILL.md` front matter, so stubs like -`katalyst-migrate-schema` and `katalyst-migrate-storage` are never shipped. It is -the single packaging entry point, reused by the release so local and CI packaging -are identical. A `make skill SKILL=katalyst-deploy` form packages one. `make -clean` removes the `.skill` artifacts alongside `bin/`. +A `skills` target in the `Makefile` zips each `skills/{name}/` to +`{name}.skill` with `SKILL.md` at the archive root (not nested under a +`{name}/` prefix — the client expects `SKILL.md` at the top). It **skips skills +marked `status: placeholder`** in their `SKILL.md` front matter, so stubs like +`katalyst-migrate-schema` and `katalyst-migrate-storage` are never shipped. It is the single +packaging entry point, reused by the release job so local and CI packaging are +identical. A `make skill SKILL=katalyst-deploy` form packages one. `make clean` removes +the `.skill` artifacts alongside `bin/`. ### Release cycle -The tag-triggered release already exists: `.github/workflows/release.yml` runs -GoReleaser on every `v*` tag and publishes the cross-platform binary archives. -This spec **extends that existing release** rather than adding a parallel -workflow: +A tag-triggered GitHub Actions job (e.g. `on: push: tags: ['v*']`), separate +from the existing `test` job in `ci.yml`: -1. The GoReleaser build matrix already produces the cross-platform CLI archives - — no new GOOS/GOARCH work. -2. Run `make skills` (a GoReleaser `before` hook) to package every shippable - skill under `skills/` before the release publishes. -3. Attach all `.skill` files to the same Release as extra assets (GoReleaser's - `release.extra_files`), alongside the binary archives and `checksums.txt`. +1. Build the cross-platform CLI binaries — the current `go build -o bin/katalyst .` + produces only the host platform, so this introduces the GOOS/GOARCH matrix. +2. Run `make skills` to package every shippable skill under `skills/` + (placeholders excluded). +3. Upload the binaries and all `.skill` files as assets on the Release for that + tag, in one workflow. Per-PR CI is unchanged: it builds and tests the CLI. The skills are plain files versioned in the same repo, so the existing review of a PR is what keeps a skill @@ -292,21 +257,17 @@ current with the CLI it drives — no separate gate. ### Local dev: symlink, uncommitted A `make` target symlinks each `skills/{name}/` into `.claude/skills/` so they -auto-load in a working copy, following the `sync_skill_links_from_cursor` pattern -already in `scripts/`. `.gitignore` already excludes all of `.claude/` (and -`.codex/`), so the symlinks stay uncommitted with no new ignore entry. +auto-load in a working copy, following the `sync_skill_links_from_cursor` +pattern already in `scripts/`. `.gitignore` excludes `.claude/skills/` +specifically (not all of `.claude/`), so the symlinks stay uncommitted. ### Binary provisioning: the shared bootstrap -The skills' shared bootstrap installs or locates the CLI by **fetching the CLI -archive from the latest GitHub Release and unpacking it** (falling back to `go -install github.com/abegong/katalyst@latest`), not by embedding binaries in each -`.skill`. Because the Release ships `katalyst___.tar.gz` -(`.zip` on Windows), the bootstrap detects OS/arch, downloads the matching -archive, and extracts the `katalyst` binary — it does not fetch a bare binary. -Embedding would bloat every download and risk a skill whose bundled binary lags -the Release; fetching keeps each `.skill` small and the binary current. One -bootstrap serves the whole family. +The skills' shared bootstrap installs or locates the CLI by **fetching the +binary from the latest GitHub Release** (falling back to `go install`), not by +embedding binaries in each `.skill`. Embedding would bloat every download and +risk a skill whose bundled binary lags the Release; fetching keeps each `.skill` +small and the binary current. One bootstrap serves the whole family. Tracking "latest" sidesteps version coupling for now (it's out of scope): the bootstrap pulls whatever the newest Release ships. Pinning a skill to a specific @@ -315,56 +276,48 @@ skew to bite. ## Open Questions -_None._ The decisions that were open — fetch-vs-embed, define as two skills, -enforce as a cluster, the orientation skill, the `katalyst-` prefix, reshape as -placeholders, Channel 1 first, and deferred version coupling — are folded into -Design above; the paths not taken are in [Rejected alternatives](#rejected-alternatives). - -## Documentation updates - -Land with the work, not after (see `docs/contributing/how-we-document.md`): - -- **Root `AGENTS.md`** — add `skills/` to the Layout section and a one-line - convention: product skills live there, `katalyst-`-prefixed, 1:1 dir↔name; - contributor skills stay in `.cursor/skills/`. Point at the distribution - deep-dive for the *why*. -- **`docs/deep-dives/`** — graduate the locked rationale (one committed source, - fetch-don't-embed, the lifecycle skill family and `katalyst-` naming, deploy - as setup, Channel 1 before Channel 2) into a distribution page at **done**. - `vision.md` and `core-concepts.md` already frame the skills/tools split; this - page explains how skills are shipped. -- **`docs/content/how-to/`** — add the cross-links: each guide with a matching - skill (`add-a-schema`, `configure-rules`, `profile-an-existing-wiki-*`, - `validate-in-ci`) gains a pointer to its skill and the download. The back-links - live in each skill's front matter, authored with the skill. -- **`docs/reference/glossary.md`** — add *skill*, *`.skill`*, *bootstrap*, and - *channel* as defined here. -- **`README.md`** — point the install section at the skills download alongside - the CLI install. -- **Go doc comments** — none; the change is build machinery (Makefile, - GoReleaser, a bootstrap script), not new Go packages. -- **`.cursor/skills/`** — no change; this section exists only to record that the - product skills are deliberately *not* added there. +_None — resolved or deferred._ For the record: + +- **Fetch, don't embed.** The shared bootstrap fetches the CLI binary from the + latest GitHub Release (falling back to `go install`); binaries are not bundled + in the `.skill`. +- **`define` is two skills.** `katalyst-identify-collections` and `katalyst-define-schemas` are + discrete, cross-referencing skills rather than one merged `define` skill. +- **Enforce is a cluster.** `katalyst-deploy` (umbrella, knows both mechanisms) plus + `katalyst-deploy-precommit-hook` and `katalyst-deploy-cli-gating` (the specific setups), set up + once rather than a loop the agent re-runs each write. +- **`katalyst-overview` orientation skill added.** A broadly-triggered front door that + carries katalyst's model/vocabulary and routes to the task skills. +- **`katalyst-` prefix on every skill.** Uniform prefix (name + artifact + + directory) to disambiguate and group the family in a flat skill namespace; + chosen over a `-with-katalyst` suffix. +- **Reshape is two placeholders.** `katalyst-migrate-schema` and `katalyst-migrate-storage` + replace the single `reshape` skill; committed as stubs (`status: placeholder`) + and excluded from release until they have content. +- **Channel 1 only.** The `.skill`-on-Releases download is in scope; marketplace + plugins (Channel 2) are deferred. +- **Versioning deferred.** Skill↔CLI version coupling is out of scope; the + bootstrap tracks the latest Release. ## Rejected alternatives -- **One mega-skill for the whole lifecycle.** Buries most of the workflow behind - whichever job the `SKILL.md` leads with, and forces users to install - catalog/define machinery just to set up enforcement. A per-stage family matches - the "tools and skills" framing in `welcome.md` and lets users install only what - they need. +- **One mega-skill for the whole lifecycle.** Buries most of the lifecycle + behind whichever job the `SKILL.md` leads with, and forces users to install + katalyst-catalog/define machinery just to set up enforcement. A per-stage family + matches the "tools and skills" framing in Why Katalyst and lets users install + only what they need. - **Keep the skills outside the repo (own repo or gists).** Decouples each skill from the CLI it documents; a check rename and its skill update would land in - separate PRs with no shared review — exactly the drift this design prevents. + separate PRs with no shared review, which is exactly the drift this design + prevents. - **Generate skills from the how-to guides (one procedural source).** Couples agent behavior to human docs and to the docs site being reachable at runtime; - the two audiences diverge. Skills stay self-contained at runtime instead — - related to the guides by a shared taxonomy and cross-links, not generation (see - [Relationship to the how-to guides](#relationship-to-the-how-to-guides)). + the two audiences diverge. Skills stay self-contained instead. - **Ship only marketplace plugins, skip the `.skill` download.** Defers all - distribution behind unsettled plugin infrastructure and ownership. The `.skill` - download works today against plain GitHub Releases and the client's existing - "Save skill" flow, so it is Channel 1 and the marketplace is a later add. + distribution behind unsettled plugin infrastructure and ownership. The + `.skill` download works today against plain GitHub Releases and the client's + existing "Save skill" flow, so it is Channel 1 and the marketplace is a later + add. - **Commit the skills under `.cursor/skills/` with the contributor skills.** Conflates two audiences in one tree and pulls shipped artifacts into the local agent-sync machinery. A separate top-level `skills/` keeps product and @@ -372,15 +325,13 @@ Land with the work, not after (see `docs/contributing/how-we-document.md`): ## Test checklist (what the build contract asserts) -- [ ] `make skills` produces one `{name}.skill` per **shippable** directory - under `skills/`, each unzipping with `SKILL.md` at the archive root, and - emits no artifact for `status: placeholder` skills. +- [ ] `make skills` produces one `{name}.skill` per directory under `skills/`, + each with `SKILL.md` at the archive root. - [ ] `make skill SKILL=` packages a single skill. - [ ] `make clean` removes the `.skill` artifacts. - [ ] The local-dev target symlinks each `skills/{name}/` into `.claude/skills/`, and the symlinks are git-ignored. -- [ ] On a `v*` tag, the release uploads the cross-platform CLI archives **and** - every shippable `.skill` as assets on that Release. +- [ ] On a tag, the release workflow uploads the cross-platform binaries and + every `.skill` as assets on that Release. - [ ] A `.skill` downloaded from a Release installs via the client's "Save skill" - flow with no repo clone, and its bootstrap fetches and unpacks the matching - CLI archive (with `go install` fallback). + flow with no repo clone.