Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/PLUGIN-PHILOSOPHY.md
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,7 @@ doc before a second plugin adopts it. Fleet audits check conformance per row.
| Review severity vocabulary | `review` plugin (`context/severity.md`) |
| Seam phrasing (presence-gated fallbacks) | [`docs/conventions/seam-phrasing/`](conventions/seam-phrasing/README.md) |
| Loop-lane topology, escalation, capability tiers, loop invariants | [`docs/conventions/loop-lane/`](conventions/loop-lane/README.md) |
| Shell test-helper duplication and exit-code divergence | [`docs/conventions/shell-test-helpers/`](conventions/shell-test-helpers/README.md) |

## Cross-platform contract

Expand Down
76 changes: 76 additions & 0 deletions docs/conventions/shell-test-helpers/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Shell test helpers — per-plugin duplication and exit-code divergence are deliberate

Owner doc for one fork this marketplace has already decided: a plugin's shell `*.test.sh` assertion
primitives and a plugin script's exit-code taxonomy are **not** consolidated into a shared,
cross-plugin mechanism. Both stay duplicated or divergent per plugin, on purpose. The
[plugin philosophy](../../PLUGIN-PHILOSOPHY.md) owns the portability boundary this rests on: a plugin
never imports files from a sibling plugin, and cooperation crosses that boundary only through a
documented public seam — a shared shell assertion library is neither.

## Why not the existing vendoring mechanism

This repo already has one sanctioned way to share source across plugins: a canonical file under
[`lib/`](../../../lib/), copied (not imported) into each carrying plugin by a dedicated
`scripts/sync-*.sh`, and tracked in
[`scripts/cross-plugin-source-registry.txt`](../../../scripts/cross-plugin-source-registry.txt) so
`check-cross-plugin-source-drift.sh --check` fails if a copy drifts. `lib/hook-utils.sh` is the
worked example.

That mechanism exists for clusters that are meant to stay **byte-identical**. The assert-helper copies
below are not that: they are already three genuinely different shapes, not one library that drifted —

- **Hook-contract shape** (`ok`/`bad`, `PASS`/`FAIL` counters, plus `make_sink`/`wait_for_sink` for
hook telemetry): [`guardrails/hooks/guardrails-test-helpers.sh`](../../../plugins/guardrails/hooks/guardrails-test-helpers.sh),
[`claude-ops/hooks/claude-ops-test-helpers.sh`](../../../plugins/claude-ops/hooks/claude-ops-test-helpers.sh).
- **Skill-script shape** (`pass`/`fail`, `FAILED`/`CASE_NUM` counters, file-existence assertions):
[`source-control/scripts/test-helpers.sh`](../../../plugins/source-control/scripts/test-helpers.sh),
[`repo-hygiene/skills/clean/scripts/lib/test-helpers.sh`](../../../plugins/repo-hygiene/skills/clean/scripts/lib/test-helpers.sh).
- **Vendored-seam shape** (same `pass`/`fail` primitives, but owned by the seam itself so it stays
correct wherever the seam is resolved from — bundled or consumer-vendored — independent of this
repo's tooling): [`work-items/tools/work-item-tracker/tests/lib.sh`](../../../plugins/work-items/tools/work-item-tracker/tests/lib.sh).

Forcing these into one shared, synced library would mean designing a fourth, unified assertion API
and rewriting every existing `*.test.sh` onto it — a bigger, riskier change than the coupling it would
remove, for a mechanism (`check-cross-plugin-source-drift.sh`) that already classifies these files as
outside its scope: they live at different paths per plugin and are not byte-identical, so `discover`
never flags them as an unregistered cluster.

`scripts/check-skill-portability.test.sh` follows the same reasoning at the repo-tooling layer: it is
not a plugin, so no plugin assertion library is available to source, and it carries its own minimal
`PASS`/`FAIL` counters rather than reaching into a plugin's copy.

## Exit-code taxonomies also diverge, deliberately

Plugin scripts document their own `Exit:` codes rather than sharing one enum, because each taxonomy
encodes a different per-script contract, not an arbitrary numbering:

- [`repo-hygiene/skills/clean/scripts/remove-path.sh`](../../../plugins/repo-hygiene/skills/clean/scripts/remove-path.sh) —
`0/1/2/3/4`: usage and existence checks plus two named blocking conditions (`blocked`, `unpushed`),
where `1` carries both meanings — the target was already absent, and an `--apply` that left the
target present (locked, in use, or crossing a mount) — so the caller retries or reports rather than
treating exit 1 as "nothing to do".
- [`repo-hygiene/skills/clean/scripts/git-tree-reset-batch.sh`](../../../plugins/repo-hygiene/skills/clean/scripts/git-tree-reset-batch.sh) —
`0/1/2` for the batch runner itself, forwarding a child's `5`/`7` (from
[`git-tree-reset.sh`](../../../plugins/repo-hygiene/skills/clean/scripts/git-tree-reset.sh)'s own
`0`–`7` taxonomy) into its own `1`.
- [`scripts/check-skill-portability.sh`](../../../scripts/check-skill-portability.sh) — `0/1/2`: gate
pass/fail plus usage error.

A shared usage/exit helper would need to either flatten these distinct contracts into a lowest common
denominator or grow branching per caller — neither is simpler than each script documenting its own
`Exit:` line, which every script here already does at its own usage banner.

## Deferred, not rejected

`guardrails-test-helpers.sh` and `claude-ops-test-helpers.sh` are the one pair above that already
share a shape closely (both hook-contract helpers with near-identical `ok`/`bad` bodies; their
`make_sink` differs in contract — guardrails' takes a stub body, claude-ops' takes a capture file).
If they converge to byte-identical, vendoring just that pair through the existing `lib/`,
`sync-*.sh`, and registry mechanism — the same pattern `hook-utils.sh` already uses — is the smaller,
precedented move, revisited then rather than spread across all five plugins now.

## Conformance

Each copy site above carries a one-line pointer back to this doc. A new plugin adding its own
`*.test.sh` assertion helper is not required to register anything here — duplication of this shape is
the accepted default, not an opt-in.
2 changes: 1 addition & 1 deletion plugins/claude-ops/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
"name": "claude-ops",
"version": "0.19.1",
"version": "0.19.2",
"description": "Claude Code operations toolkit. Seven skills: observability (read locally captured telemetry — OTEL store, collector, hook-event JSONL, ccusage — with trend reports and store pruning), known-issues (search known Claude product GitHub bugs, check service health, maintain a persistent tracked-issue registry), changelog (ingest Claude Code changelog entries and integrate them into the current repo), plugins (bring a machine's plugin fleet current on demand — marketplace refresh, effective-scope updates including in-repo project/local installs, new-plugin install per policy, scope-divergence detection and explicit convergence), morning-brief (read-only gh-based operator morning view — queue-label counts, merge-ready PRs, parked decisions with their RECOMMENDED lines, and loop-lane telemetry freshness), lanes (start/restart/stop/status loop lanes as named background Claude Code sessions seeded from canonical prompt files, with per-lane model/effort and a repo-pull + marketplace-refresh launch step), and a re-runnable setup action that settles where the known-issues registry lives. Plus a family of seven advisory *-audit telemetry-emitter hooks (API errors, config changes, instruction loads, permission denials, pre-compaction, skill usage, tool failures) that emit the shared hook-telemetry envelope, and a reference sink that maps envelopes into the hook-events.jsonl the observability skill reads.",
"author": {
"name": "Melodic Software",
Expand Down
9 changes: 9 additions & 0 deletions plugins/claude-ops/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@
All notable changes to the `claude-ops` plugin are documented here. Format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning.

## [0.19.2]

### Documentation

- `hooks/claude-ops-test-helpers.sh` now points at
`docs/conventions/shell-test-helpers/README.md`, the repo's owner doc recording that per-plugin
shell assert-helper duplication and per-script exit-code taxonomies are deliberate, not drift. No
behavior change.

## [0.19.1]

### Fixed
Expand Down
3 changes: 3 additions & 0 deletions plugins/claude-ops/hooks/claude-ops-test-helpers.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
# Sourced (never *.test.sh-named, so the test runner's glob ignores it) by each
# hook's *.test.sh after that file sets up its own TEST_TMPDIR + trap. No
# dependency on any host-repo assertion library — the plugin is standalone.
#
# Duplicated across plugins by design, not drift — see
# docs/conventions/shell-test-helpers/README.md at the repo root.

: "${PASS:=0}"
: "${FAIL:=0}"
Expand Down
2 changes: 1 addition & 1 deletion plugins/guardrails/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
"name": "guardrails",
"version": "0.14.2",
"version": "0.14.3",
"description": "Nine safety guards that block secret/credential writes, hardcoded machine-specific paths, git hook-bypass attempts, irreversible git operations (force-push, reset --hard, worktree-wide checkout/restore discards), Bash file-write workarounds that circumvent Write/Edit hooks, commit subjects and gh pr create titles that violate the repo's tracked team convention (when one is declared in .claude/source-control.md), (advisory) hallucinated CLI flags, (advisory) un-throttled Workflow fan-out that risks burst 529s, and (advisory) direct git commit/gh pr create calls bypassing this marketplace's own commit/pull-request skills — each independently toggleable.",
"author": {
"name": "Melodic Software",
Expand Down
9 changes: 9 additions & 0 deletions plugins/guardrails/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@
All notable changes to the `guardrails` plugin are documented here. Format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning.

## [0.14.3]

### Documentation

- `hooks/guardrails-test-helpers.sh` now points at
`docs/conventions/shell-test-helpers/README.md`, the repo's owner doc recording that per-plugin
shell assert-helper duplication and per-script exit-code taxonomies are deliberate, not drift. No
behavior change.

## [0.14.2]

### Fixed
Expand Down
3 changes: 3 additions & 0 deletions plugins/guardrails/hooks/guardrails-test-helpers.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
# Sourced (never *.test.sh-named, so a test runner's glob ignores it) by each
# hook's *.test.sh after that file sets up its own TEST_TMPDIR + trap. No
# dependency on any host-repo assertion library — the plugin is standalone.
#
# Duplicated across plugins by design, not drift — see
# docs/conventions/shell-test-helpers/README.md at the repo root.

: "${PASS:=0}"
: "${FAIL:=0}"
Expand Down
2 changes: 1 addition & 1 deletion plugins/repo-hygiene/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
"name": "repo-hygiene",
"version": "0.7.0",
"version": "0.7.1",
"description": "Repo hygiene action-router: /repo-hygiene:clean sweeps reclaimable caches, build artifacts, and stale git metadata, and can realign the working tree to a fresh-pull state — dry-run-first, with destructive tiers gated behind explicit confirmation and a session-scoped destructive-command guard. Ecosystem targets are detected at runtime; secrets, runtime dependencies, and skill data are preserved by default.",
"author": {
"name": "Melodic Software",
Expand Down
9 changes: 9 additions & 0 deletions plugins/repo-hygiene/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@
All notable changes to the `repo-hygiene` plugin are documented here. Format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning.

## [0.7.1]

### Documentation

- `skills/clean/scripts/lib/test-helpers.sh` now points at
`docs/conventions/shell-test-helpers/README.md`, the repo's owner doc recording that per-plugin
shell assert-helper duplication and per-script exit-code taxonomies are deliberate, not drift. No
behavior change.

## [0.7.0]

### Added
Expand Down
3 changes: 3 additions & 0 deletions plugins/repo-hygiene/skills/clean/scripts/lib/test-helpers.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
#
# Each test file owns its own FAILED / CASE_NUM counters and exits non-zero at
# the end: `[[ $FAILED -eq 0 ]] || exit 1`.
#
# Duplicated across plugins by design, not drift — see
# docs/conventions/shell-test-helpers/README.md at the repo root.

[[ -n "${_CLEAN_TEST_HELPERS_LOADED:-}" ]] && return 0
readonly _CLEAN_TEST_HELPERS_LOADED=1
Expand Down
2 changes: 1 addition & 1 deletion plugins/source-control/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
"name": "source-control",
"version": "0.26.0",
"version": "0.26.1",
"description": "Git and GitHub delivery workflow: /commit (Conventional Commits + Co-Authored-By trailer via safe heredoc mechanics), /pull-request (prep, create, CI monitoring, review-comment triage, merge, CI-log fetch), /babysit-prs (self-pacing fleet loop — safe by default; opt-in worker/autopilot tiers add gate-checked merge and thread resolution behind a deterministic Python engine), /babysit-loop (the loop-lane merge lane: a standing or drain loop that invokes babysit-prs per cycle, configured through repo-scoped babysit_loop_* keys on the layered source-control.md seam, with merge authority human-only until the target repo's tracked config adopts the lane, a gate-proven C2-mechanical baseline once adopted, and merge-rung raises binding from the team-tracked layer only), /worktree (create, status, cleanup, audit for parallel-session isolation), /setup (check the effective commit-subject / PR-title convention merged across its config layers and the babysit-prs config, or apply — interview the repo and write the convention config to a chosen layer), and /resolve-conflicts (intent-first merge/rebase conflict resolution with a semantic-conflict sweep — never --abort). The commit-subject / PR-title convention is configurable via a source-control.md config written by a re-runnable setup skill, layered across a ~/.claude user-global file, the tracked team file, and a gitignored .claude/source-control.local.md personal overlay merged per key; Conventional Commits is the default when no convention is declared.",
"author": {
"name": "Melodic Software",
Expand Down
8 changes: 8 additions & 0 deletions plugins/source-control/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@
All notable changes to the `source-control` plugin are documented here. Format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning.

## [0.26.1]

### Documentation

- `scripts/test-helpers.sh` now points at `docs/conventions/shell-test-helpers/README.md`, the
repo's owner doc recording that per-plugin shell assert-helper duplication and per-script exit-code
taxonomies are deliberate, not drift. No behavior change.

## [0.26.0]

### Added
Expand Down
3 changes: 3 additions & 0 deletions plugins/source-control/scripts/test-helpers.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
#
# Param order: subject (haystack/actual) BEFORE expected (needle), except
# assert_eq / assert_exit which take (label, expected, actual).
#
# Duplicated across plugins by design, not drift — see
# docs/conventions/shell-test-helpers/README.md at the repo root.

[[ -n "${_TESTS_LIB_LOADED:-}" ]] && return 0
readonly _TESTS_LIB_LOADED=1
Expand Down
2 changes: 1 addition & 1 deletion plugins/work-items/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
"name": "work-items",
"version": "0.24.0",
"version": "0.24.1",
"description": "Manages development work items through a provider-neutral tracker seam that ships with the plugin (bundled dispatcher plus github and local-markdown adapters; seam plugin-dir canonical, adapters consumer-local-first): dashboard, taxonomy-labeled creation, a race-safe assignee-plus-lease claim protocol, recurring-schedule checks, TODO scanning, stale-lease auditing, plan decomposition into vertical-slice items, raw-intake triage (issues and unsolicited PRs through raw, verified, briefed, autonomous-eligible states), plus the two work-items loop lanes of the loop-lane convention: a self-paced autonomous work-loop drain (work-class admission gate, adaptive item cap, PR-only) and an attended attend-queue escalation lane. The re-runnable setup skill binds the provider (.work-item-tracker.json), seeds the recurring-schedule seam (.github/recurring-schedule.json), and remaps canonical role labels.",
"author": {
"name": "Melodic Software",
Expand Down
9 changes: 9 additions & 0 deletions plugins/work-items/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@
All notable changes to the `work-items` plugin are documented here. Format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning.

## [0.24.1]

### Documentation

- `tools/work-item-tracker/tests/lib.sh` now points at
`docs/conventions/shell-test-helpers/README.md`, the repo's owner doc recording that per-plugin
shell assert-helper duplication and per-script exit-code taxonomies are deliberate, not drift. No
behavior change.

## [0.24.0]

### Changed
Expand Down
3 changes: 3 additions & 0 deletions plugins/work-items/tools/work-item-tracker/tests/lib.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
#
# Param order: subject (actual) BEFORE expected — reads as "in <subject>,
# expect <something>".
#
# Duplicated across plugins by design, not drift — see
# docs/conventions/shell-test-helpers/README.md at the repo root.

[[ -n "${_WIT_TESTS_LIB_LOADED:-}" ]] && return 0
readonly _WIT_TESTS_LIB_LOADED=1
Expand Down
4 changes: 4 additions & 0 deletions scripts/check-skill-portability.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
# and runs the script's --all mode from a fixture root, exactly as the
# silent-skip suite does. Two cases run against the REAL corpus to prove the
# bare-vs-guarded discrimination on live files, not only synthetic ones.
#
# Bespoke PASS/FAIL counters by design, not drift: this is repo tooling, not a
# plugin, so no plugin assertion library applies here — see
# docs/conventions/shell-test-helpers/README.md.
# shellcheck disable=SC2016 # fixture bodies are literal skill content in single quotes; expansion is never wanted
set -uo pipefail

Expand Down