From af433384787cdc3913b5f804b2ae7129ee3d3d0f Mon Sep 17 00:00:00 2001 From: Alisue Date: Sun, 11 Oct 2026 13:55:42 +0900 Subject: [PATCH 1/4] chore: run the quality gate with just and drop flake-utils The only check was `deno fmt --check`, run straight from CI. `just ci` runs it together with a nixfmt check and a parse of the marketplace and plugin manifests, and CI runs the same recipe. The flake lists its systems with nixpkgs.lib.genAttrs instead of flake-utils (and no longer makes the probitas flake follow it), drops pagefind and the unused default package, and is formatted with nixfmt. The dev shell provides just, nixfmt and jq. The repository ignores its own .envrc, .direnv, result and worktrees, and .gitattributes keeps checkouts LF on every OS. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01N3xvJ1TaozjYSE6eoyo9mq --- .claude/rules/formatting.md | 7 +++-- .envrc.sample | 1 + .gitattributes | 3 ++ .gitignore | 4 +++ flake.lock | 5 +-- flake.nix | 63 ++++++++++++++++++++++++++----------- justfile | 24 ++++++++++++++ 7 files changed, 81 insertions(+), 26 deletions(-) create mode 100644 .gitattributes create mode 100644 justfile diff --git a/.claude/rules/formatting.md b/.claude/rules/formatting.md index 5304d33..5df7083 100644 --- a/.claude/rules/formatting.md +++ b/.claude/rules/formatting.md @@ -4,9 +4,10 @@ paths: "**/*.md, **/*.json" # Formatting Rules -After modifying `*.md` or `*.json` files, run `deno fmt` to ensure consistent -formatting. +After modifying `*.md` or `*.json` files, run `just fmt` (which runs +`deno fmt`) to ensure consistent formatting. `just ci` fails on unformatted +files. ```bash -deno fmt +just fmt ``` diff --git a/.envrc.sample b/.envrc.sample index 3550a30..11af9c3 100644 --- a/.envrc.sample +++ b/.envrc.sample @@ -1 +1,2 @@ +# Copy to .envrc and run `direnv allow` to enter the Nix dev shell automatically. use flake diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..ca5e98b --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +# Check text out with LF on every OS, so a Windows checkout builds and tests +# the same bytes as the others. +* text=auto eol=lf diff --git a/.gitignore b/.gitignore index e07aa5b..01bdfb6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,6 @@ .DS_Store .coverage +/.envrc +.direnv/ +result +/.claude/worktrees/ diff --git a/flake.lock b/flake.lock index b2ee991..b308072 100644 --- a/flake.lock +++ b/flake.lock @@ -36,9 +36,7 @@ }, "probitas-flake": { "inputs": { - "flake-utils": [ - "flake-utils" - ], + "flake-utils": "flake-utils", "nixpkgs": [ "nixpkgs" ] @@ -59,7 +57,6 @@ }, "root": { "inputs": { - "flake-utils": "flake-utils", "nixpkgs": "nixpkgs", "probitas-flake": "probitas-flake" } diff --git a/flake.nix b/flake.nix index 7950d9e..1fd3b3b 100644 --- a/flake.nix +++ b/flake.nix @@ -3,32 +3,57 @@ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; - flake-utils.url = "github:numtide/flake-utils"; probitas-flake.url = "github:probitas-test/probitas"; probitas-flake.inputs.nixpkgs.follows = "nixpkgs"; - probitas-flake.inputs.flake-utils.follows = "flake-utils"; }; - outputs = { self, nixpkgs, flake-utils, probitas-flake }: - flake-utils.lib.eachDefaultSystem (system: - let - pkgs = import nixpkgs { + outputs = + { + self, + nixpkgs, + probitas-flake, + }: + let + systems = [ + "x86_64-linux" + "aarch64-linux" + "x86_64-darwin" + "aarch64-darwin" + ]; + forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f system); + pkgsFor = + system: + import nixpkgs { inherit system; overlays = [ probitas-flake.overlays.default ]; }; - in { - packages.default = pkgs.probitas; + in + { + devShells = forAllSystems ( + system: + let + pkgs = pkgsFor system; + in + { + default = pkgs.mkShell { + packages = [ + # Formats the Markdown and JSON of the plugins. + pkgs.deno + # Lets the plugin's commands be tried against real scenarios. + pkgs.probitas + # Checks that the plugin manifests parse. + pkgs.jq + pkgs.just + pkgs.nixfmt + ]; - devShells.default = pkgs.mkShell { - packages = with pkgs; [ - deno - pagefind - probitas - ]; + shellHook = '' + export DENO_NO_UPDATE_CHECK=1 + ''; + }; + } + ); - shellHook = '' - export DENO_NO_UPDATE_CHECK=1 - ''; - }; - }); + formatter = forAllSystems (system: nixpkgs.legacyPackages.${system}.nixfmt); + }; } diff --git a/justfile b/justfile new file mode 100644 index 0000000..a27de12 --- /dev/null +++ b/justfile @@ -0,0 +1,24 @@ +# Run `nix develop` (or use direnv) first so every tool is on PATH. + +set shell := ["bash", "-euo", "pipefail", "-c"] + +# List the recipes. +default: + @just --list + +# Format the Markdown, JSON and Nix files. +fmt: + deno fmt + git ls-files -z '*.nix' | xargs -0 nixfmt + +# Check formatting without changing files. +fmt-check: + deno fmt --check + git ls-files -z '*.nix' | xargs -0 nixfmt --check + +# Check that the marketplace and every plugin manifest parse as JSON. +check-manifests: + jq empty .claude-plugin/marketplace.json plugins/*/.claude-plugin/plugin.json + +# Everything CI runs. +ci: fmt-check check-manifests From f24e337984f8b12f8a3f84d536396ff506da1ec0 Mon Sep 17 00:00:00 2001 From: Alisue Date: Sun, 11 Oct 2026 13:55:42 +0900 Subject: [PATCH 2/4] docs: keep the agent guide in AGENTS.md A CLAUDE.md anywhere in the tree makes Claude Code ignore AGENTS.md, and other agents read only AGENTS.md, so the guide lived where only one tool looked. Its content moves to a root AGENTS.md with the layer table, the quality gate (`just ci`), the layout (without the rules/ directory the old tree listed but the plugin does not have), and the commit convention. The ADR and spec procedures and the rules that make them get read land now, so records written from here on follow them. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01N3xvJ1TaozjYSE6eoyo9mq --- .claude/CLAUDE.md | 36 ------------------- .claude/rules/adr.md | 19 ++++++++++ .claude/rules/spec.md | 21 ++++++++++++ AGENTS.md | 74 +++++++++++++++++++++++++++++++++++++++ docs/adr/README.md | 80 +++++++++++++++++++++++++++++++++++++++++++ docs/spec/README.md | 60 ++++++++++++++++++++++++++++++++ 6 files changed, 254 insertions(+), 36 deletions(-) delete mode 100644 .claude/CLAUDE.md create mode 100644 .claude/rules/adr.md create mode 100644 .claude/rules/spec.md create mode 100644 AGENTS.md create mode 100644 docs/adr/README.md create mode 100644 docs/spec/README.md diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md deleted file mode 100644 index 9e9af3f..0000000 --- a/.claude/CLAUDE.md +++ /dev/null @@ -1,36 +0,0 @@ -# Probitas Claude Plugins Marketplace - -Claude Code plugin marketplace for Probitas ecosystem. - -## Structure - -``` -claude-plugins/ -├── .claude-plugin/ -│ └── marketplace.json # Marketplace definition -├── plugins/ -│ └── probitas/ # Probitas plugin -│ ├── .claude-plugin/ -│ ├── rules/ -│ ├── skills/ -│ ├── commands/ -│ └── agents/ -└── .claude/ # This repo's Claude config -``` - -## Commands - -```bash -# No build step - this is a pure Markdown/JSON repository -``` - -## Documentation Source - -Plugin content is based on Probitas documentation: - -- Site: https://probitas-test.github.io/documents/ -- LLM sitemap: https://probitas-test.github.io/documents/llms.txt - -## Updating Plugin Content - -Use `/doc:update` to sync plugin content with latest Probitas documentation. diff --git a/.claude/rules/adr.md b/.claude/rules/adr.md new file mode 100644 index 0000000..e4a69b4 --- /dev/null +++ b/.claude/rules/adr.md @@ -0,0 +1,19 @@ +--- +paths: "docs/adr/**" +--- + +# Writing an ADR + +Before writing, fixing or reviewing a record in `docs/adr/`, read +`docs/adr/README.md` and follow "How to write one" step by step. It is the +whole rule; this file only makes sure it is read at the moment of writing. + +The three steps people skip: + +- Write the title first, as one sentence. It is the decision; everything else + restates it or follows from it. +- Ask of every line: would changing this take a new record that supersedes + this one? If an ordinary change would do, the line is specification — leave + it out. +- When fixing a record, rewrite it from the title. Do not edit the previous + draft. diff --git a/.claude/rules/spec.md b/.claude/rules/spec.md new file mode 100644 index 0000000..c8c6fc1 --- /dev/null +++ b/.claude/rules/spec.md @@ -0,0 +1,21 @@ +--- +paths: "docs/spec/**" +--- + +# Writing a spec + +Before writing, fixing or reviewing a file in `docs/spec/`, read +`docs/spec/README.md` and follow "How to write one". It is the whole rule; +this file only makes sure it is read at the moment of writing. + +The checks people skip: + +- Ask of every line: can a test pin this down? Then it belongs in a test. + Would a user call a change to it a change in behaviour? If not, it is not + spec. +- Numbered steps, mapping tables, other programs' file locations, exact + strings and ordering rules are candidates to take out — unless they + describe a format this project defines for others to write. +- A review fix that does not change what a user sees does not touch the spec. +- Do not copy the level of detail of neighbouring specs; they may be too + detailed themselves. diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..fc46fdb --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,74 @@ +# probitas-test Claude Plugins + +Official Claude Code plugin marketplace for +[Probitas](https://probitas-test.github.io/) ecosystem. + +There is no build step: the plugins are Markdown and JSON. + +## How it is built + +- **Each layer holds one thing.** Put a fact in the one layer that owns it, + and nowhere else. + + | Layer | Holds | + | -------------- | ---------------------------------------------------- | + | `docs/adr/` | one decision and why it was made | + | `docs/spec/` | the behaviour users and other components can rely on | + | Plugin content | the skills, commands and agents the plugins ship | + | Commits | why a change was made | + + Before writing, fixing or reviewing a record, read `docs/adr/README.md`; + before writing, fixing or reviewing a spec, read `docs/spec/README.md`. + Follow their writing order. +- **Dependencies are current.** Before adding or bumping one, look up its + latest release in the registry. Do not copy a version from memory or from + another repository. + +When editing anything under `plugins/`, follow +[`.claude/rules/plugin-maintenance.md`](.claude/rules/plugin-maintenance.md). +For questions about Probitas itself, follow +[`.claude/rules/probitas-research.md`](.claude/rules/probitas-research.md). + +## Quality gate + +Run `just ci` before reporting a change as done. CI runs the same recipe. It +checks the formatting of Markdown, JSON and Nix files and that every plugin +manifest parses; `just fmt` fixes the formatting. + +## Layout + +| Path | What lives there | +| --------------------------------- | ------------------------------------------------------------------------- | +| `.claude-plugin/marketplace.json` | the marketplace definition | +| `plugins/probitas/` | the Probitas plugin: `.claude-plugin/`, `skills/`, `commands/`, `agents/` | +| `docs/` | ADRs and specs | +| `.claude/` | this repository's own rules and commands | +| `.github/` | CI | + +## Documentation source + +Plugin content is based on the Probitas documentation: + +- Site: https://probitas-test.github.io/ +- LLM sitemap: https://probitas-test.github.io/llms.txt + +Use `/doc:update` to sync the plugin content with the latest documentation. + +## Language + +- README, docs and commit messages: English +- Plugin content and code comments: English + +## Commits + +[Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/), as +described in [`.gitmessage`](.gitmessage) (install it with +`git config commit.template .gitmessage`): + +``` +[optional scope]: +``` + +- Types: `feat`, `fix`, `deps`, `refactor`, `test`, `docs`, `style`, `chore`. +- Scopes name the area touched, such as `probitas` (the plugin) or + `marketplace`. diff --git a/docs/adr/README.md b/docs/adr/README.md new file mode 100644 index 0000000..36c1019 --- /dev/null +++ b/docs/adr/README.md @@ -0,0 +1,80 @@ +# Architecture Decision Records + +The significant decisions behind claude-plugins, one decision per file. + +## Conventions + +- Files are named `YYYYMMDD-.md` after the day the decision was made, so + listing the directory in name order lists the decisions in time order. Numbers + and an index are avoided because parallel changes would collide on them. +- The slug is the title in kebab case, so a file name and its title always lead + to each other. +- Each record has a status and the sections Context, Decision, Alternatives + considered and Consequences. +- An accepted record is not rewritten. When the decision changes, a new record + supersedes it, and both say so in their status. +- Records refer to each other by file name. A record does not link to the + specification, which keeps changing; the decision is complete within the + record. + +## What belongs in a record + +A record holds one decision, not everything decided while doing the work. + +For every line, ask: **if this changed, would it take a new record that +supersedes this one?** + +- Yes — it is part of the decision. Keep it. +- No, an ordinary change to the code or the specification would do — it is + specification or implementation. Leave it out. + +Formats, file locations, encodings, UI details and the order of steps almost +always answer no. + +## How to write one + +Write in this order, and do not judge details line by line along the way: + +1. **Title** — the decision as one sentence. This sentence is the decision. +2. **Decision** — restate the title, with only what the replacement test above + keeps. +3. **Alternatives considered** — only options that would have replaced the + title's decision, each with why it was not taken. +4. **Consequences** — only what follows directly from the decision. +5. **Context** — only what a reader needs to see why the title was the answer. + +When a record under review needs fixing, rewrite it from the title without +looking at the previous draft. Editing the draft keeps the details the new +decision no longer needs. + +Before committing, apply the replacement test to every line once more. + +## Example + +This is the size to aim for. + +```markdown +# Keep the user's words in a dictionary of their own + +Status: Accepted + +## Context + +Words the user registers were appended to the bundled dictionary. Every update +replaces the bundled dictionary, so the user's words were lost. + +## Decision + +The user's words live in a separate dictionary that updates never touch. + +## Alternatives considered + +- Merge the user's words back into the bundled dictionary after each update. + A failed merge still loses them, and the two sources become impossible to + tell apart. + +## Consequences + +- Updates can replace the bundled dictionary wholesale. +- Lookups consult two dictionaries. +``` diff --git a/docs/spec/README.md b/docs/spec/README.md new file mode 100644 index 0000000..63ea3e9 --- /dev/null +++ b/docs/spec/README.md @@ -0,0 +1,60 @@ +# Specification + +What claude-plugins promises to whoever uses it, one topic per file. + +## Who reads it + +Users of claude-plugins, and authors of other programs that work with it. A spec +holds what they can rely on, not everything that is true of the code. + +## What belongs in a spec + +Each layer holds one thing: + +| Layer | Holds | +| ----- | ------------------------------------------------------------------ | +| ADR | one decision and why it was made | +| Spec | the behaviour users and other components can rely on | +| Tests | the details that pin the behaviour down: rules, boundaries, tables | +| Code | how it is done | + +Tests hold the what in detail, so a spec is narrower than the tests. What +stays in a spec is what tests express poorly: the flow a user goes through, a +promise such as "existing data is never rewritten", the scope of what is +handled. + +For every line, ask: + +1. **Can a test pin this down (and should one)?** Then it goes in a test, not + here. +2. **Would a change to this be called a change in behaviour by a user?** If + not, it is not spec. + +These usually fail the questions; when one appears, take it out: + +- numbered steps (an algorithm) +- mapping tables +- where another program keeps its files +- exact strings: headers, file names +- ordering rules, screen layout + +### The exception: formats claude-plugins defines + +A format that claude-plugins defines and that others write — its own data files, +its configuration — is read by outside authors, so the spec describes it in +full. A format someone else defines (another program's files) is not +claude-plugins's to promise; reading it is left to the code and the tests. + +## How to write one + +1. Write first, briefly, what claude-plugins promises. Leave the details out. +2. Write the details as test names. Tests named as sentences make the list of + tests a readable specification. +3. When review leads to a fix, do not touch the spec unless the behaviour a + user sees changes. + +Before committing, ask both questions of every line once more. + +A spec is rewritten in place, in the same change as the behaviour it +describes. It never carries "to be updated" notes, and it does not say why — +the reasons live in `docs/adr/`. From 1808234ddb00a9df0519fce41829901476fc5177 Mon Sep 17 00:00:00 2001 From: Alisue Date: Sun, 11 Oct 2026 13:55:42 +0900 Subject: [PATCH 3/4] ci: run just ci in the dev shell and harden the workflow CI installed Deno on its own and ran the formatter check, apart from the dev shell developers use. It now loads the dev shell with nicknovitski/nix-develop and runs `just ci`. The workflow reads the repository by default, the checkout no longer keeps credentials, pull request runs cancel superseded ones, and the actions are on their latest majors. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01N3xvJ1TaozjYSE6eoyo9mq --- .github/workflows/check.yml | 39 ++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 0418d03..0579432 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -6,16 +6,37 @@ on: pull_request: branches: [main] +# A pull request only needs its latest commit checked. A run on main waits +# for the one before it instead of cancelling it, so each one finishes and +# saves the caches that pull requests start from. +concurrency: + group: check-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +permissions: + contents: read + jobs: - fmt: - name: Format + ci: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - - uses: denoland/setup-deno@v2 + - uses: actions/checkout@v7 with: - deno-version: v2.x - - - name: Check formatting - run: deno fmt --check + persist-credentials: false + - uses: cachix/install-nix-action@v31 + with: + extra_nix_config: | + experimental-features = nix-command flakes + # The store the development shell comes from. Only an exact match is + # restored, and only main saves, so pull requests cannot evict the + # caches each other start from. + - uses: nix-community/cache-nix-action@v7 + with: + primary-key: nix-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('flake.nix', 'flake.lock') }} + save: ${{ github.ref == 'refs/heads/main' }} + # Load the development shell (flake.nix) into the job. + - uses: nicknovitski/nix-develop@v1 + # The same gate as local development (justfile). + - run: just ci + env: + CI: "1" From 03d09250e655dc81b2788863c4d4ee6820c7b4c4 Mon Sep 17 00:00:00 2001 From: Alisue Date: Sun, 11 Oct 2026 13:55:42 +0900 Subject: [PATCH 4/4] fix: point stale links at the current site and complete the plugin metadata The documentation site moved from /documents/ to the root, but the marketplace homepage and the research rule still linked the old path, and the doc update command listed plugin files that no longer exist. The links now go to https://probitas-test.github.io/ and the command lists the skill files. plugin.json also names its homepage, repository and license, which the marketplace shows to users. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01N3xvJ1TaozjYSE6eoyo9mq --- .claude-plugin/marketplace.json | 2 +- .claude/commands/doc/update.md | 13 ++++++------- .claude/rules/probitas-research.md | 2 +- plugins/probitas/.claude-plugin/plugin.json | 5 ++++- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 43f51c1..91a9b2f 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -11,7 +11,7 @@ "source": "./plugins/probitas", "description": "Probitas scenario testing framework integration", "category": "testing", - "homepage": "https://probitas-test.github.io/documents/" + "homepage": "https://probitas-test.github.io/" } ] } diff --git a/.claude/commands/doc/update.md b/.claude/commands/doc/update.md index f3cb08f..a0e7eb8 100644 --- a/.claude/commands/doc/update.md +++ b/.claude/commands/doc/update.md @@ -8,18 +8,17 @@ description: Sync plugin content with latest Probitas documentation Use WebFetch to get the latest docs: - - https://probitas-test.github.io/documents/llms.txt - - https://probitas-test.github.io/documents/docs/scenario/index.md - - https://probitas-test.github.io/documents/docs/client/index.md - - https://probitas-test.github.io/documents/docs/expect/index.md - - https://probitas-test.github.io/documents/docs/configuration/index.md + - https://probitas-test.github.io/llms.txt + - https://probitas-test.github.io/docs/scenario/index.md + - https://probitas-test.github.io/docs/client/index.md + - https://probitas-test.github.io/docs/expect/index.md + - https://probitas-test.github.io/docs/configuration/index.md 2. **Review and update plugin files** Compare fetched docs with current plugin content: - - `plugins/probitas/rules/*.md` - Conventions and guidelines - - `plugins/probitas/skills/probitas/SKILL.md` - Patterns and quick reference + - `plugins/probitas/skills/*/SKILL.md` - Patterns and quick reference - `plugins/probitas/commands/*.md` - Slash command definitions - `plugins/probitas/agents/*.md` - Agent definitions diff --git a/.claude/rules/probitas-research.md b/.claude/rules/probitas-research.md index dfca3ae..8a49901 100644 --- a/.claude/rules/probitas-research.md +++ b/.claude/rules/probitas-research.md @@ -20,7 +20,7 @@ Check `plugins/probitas/` for existing documentation: If plugin resources don't cover the question: -- **LLM Sitemap**: https://probitas-test.github.io/documents/llms.txt +- **LLM Sitemap**: https://probitas-test.github.io/llms.txt - Comprehensive documentation optimized for AI consumption - Use WebFetch to retrieve specific sections diff --git a/plugins/probitas/.claude-plugin/plugin.json b/plugins/probitas/.claude-plugin/plugin.json index f19463d..a45c057 100644 --- a/plugins/probitas/.claude-plugin/plugin.json +++ b/plugins/probitas/.claude-plugin/plugin.json @@ -5,5 +5,8 @@ "author": { "name": "probitas-test", "email": "lambdalisue+probitas@gmail.com" - } + }, + "homepage": "https://github.com/probitas-test/claude-plugins", + "repository": "https://github.com/probitas-test/claude-plugins", + "license": "MIT" }