Skip to content

feat: markdown-formatter plugin (pilot migration)#3

Merged
kyle-sexton merged 3 commits into
mainfrom
feat/markdown-formatter-plugin
Jun 23, 2026
Merged

feat: markdown-formatter plugin (pilot migration)#3
kyle-sexton merged 3 commits into
mainfrom
feat/markdown-formatter-plugin

Conversation

@kyle-sexton

Copy link
Copy Markdown
Contributor

First plugin migrated from medley into this marketplace — the pilot proving the end-to-end lift-and-shift pipeline.

What's in this PR

  • plugins/markdown-formatter/** — ported PostToolUse hook (markdownlint-cli2 on Write/Edit), bundled hook-utils.sh subset, black-box contract test (12/12), README (16faf60).
  • .editorconfig + .shellcheckrc — platform drop-in configs (5bb2e77).
  • .claude-plugin/marketplace.jsoncatalog entry making the plugin installable (was empty).
  • README.md — catalog section listing the plugin.
  • docs/MIGRATION-PLAYBOOK.md — step 10 records the ./-prefixed source requirement + catalog-manifest validation.

Verified

  • claude plugin validate --strict ✔ on both the plugin and the marketplace manifest.
  • --plugin-dir load smoke + source-repo parity (fresh-context) PASS.

⚠️ DO NOT MERGE until marketplace-install is proven from this branch

--plugin-dir and strict-validate do not exercise what a real marketplace install does: relative-source resolution (./plugins/markdown-formatter) and hook firing from the cache copy (~/.claude/plugins/cache). Prove both from this branch ref before merging:

/plugin marketplace add https://github.com/melodic-software/claude-code-plugins.git#feat/markdown-formatter-plugin
/plugin install markdown-formatter@melodic-software
/reload-plugins
# edit a .md with a fixable issue → confirm markdownlint-cli2 auto-fixes

This closes the recorded open gap (live in-session PostToolUse fire via an installed plugin). Merge → final user/global install from main.

Note: dropped metadata.pluginRootclaude plugin validate --strict rejects the bare source it is meant to enable, despite the marketplaces-doc example.

kyle-sexton and others added 3 commits June 23, 2026 12:04
Add .editorconfig (LF, 2-space, shfmt shell keys) and .shellcheckrc
(external-sources, source-path=SCRIPTDIR, selective optional checks),
mirroring the standards/medley drop-in configs. Governs authoring and
linting of this repo's own shell sources only; not shipped to plugin
consumers. Long-term these should be consumed from the standards repo.

Co-Authored-By: Claude <noreply@anthropic.com>
PostToolUse hook that auto-formats and lints Markdown on edit via
markdownlint-cli2 --fix, using the consuming repo's own markdownlint
config (ships none). Lifted and genericized from melodic-software/medley's
in-repo markdown-format hook: telemetry/observability coupling removed,
medley-policy wrapper prose dropped, file-anchored repo-root cd preserved
for config discovery, kill switch HOOK_MARKDOWN_FORMAT_ENABLED retained.
No userConfig. Bundles a trimmed hook-utils subset and a black-box
contract test (12 cases).

Verified: claude plugin validate --strict; --plugin-dir load in a clean
non-medley repo; source-repo parity vs the in-repo hook (byte-identical
--fix output, MD024 finding-line identity, zero plugin telemetry).

Co-Authored-By: Claude <noreply@anthropic.com>
Add the markdown-formatter entry to .claude-plugin/marketplace.json so the
already-committed plugin is installable, and surface it in the README catalog.

Drop metadata.pluginRoot: claude plugin validate --strict rejects the bare
plugin source it is meant to enable (plugins.N.source: Invalid input), despite
the marketplaces-doc example. Entries use the explicit ./plugins/<name> form.

Playbook step 10 now records the ./-prefixed-source requirement and the need to
validate the catalog manifest itself (the entry error surfaces only there).

Co-Authored-By: Claude <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b970d0c306

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

local norm_file norm_project
norm_file=$(hook::normalize_path "$file")
norm_project=$(hook::normalize_path "${CLAUDE_PROJECT_DIR}")
if [[ "$norm_file" != "$norm_project"* ]]; then

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Require a path boundary for project membership

When CLAUDE_PROJECT_DIR is /repo/app, this raw prefix check also accepts /repo/app-backup/file.md. The guard is supposed to skip files outside the current project, but with similarly named sibling directories a Write/Edit to that path will still run markdownlint-cli2 --fix outside the project; compare against $norm_project/ or use a relative-path check with a real boundary.

Useful? React with 👍 / 👎.

Comment on lines +28 to +29
elif command -v npx >/dev/null 2>&1; then
MDLINT=(npx markdownlint-cli2)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid treating npx alone as an available formatter

In repos where npx exists but markdownlint-cli2 is not installed locally/globally and the npm registry is unavailable or blocked, this branch still runs npx markdownlint-cli2; npx --help describes it as running from a “local or remote npm package”, so the hook captures npm fetch failures and reports them as markdownlint findings on every Markdown edit instead of being a no-op. Check for an actually local/resolvable package or suppress this fallback when the package cannot be executed without fetching.

Useful? React with 👍 / 👎.

@kyle-sexton
kyle-sexton merged commit abaf87e into main Jun 23, 2026
1 check passed
@kyle-sexton
kyle-sexton deleted the feat/markdown-formatter-plugin branch June 23, 2026 18:56
kyle-sexton added a commit that referenced this pull request Jul 12, 2026
…r self-contained

Address three more Codex P2 findings:

- teach topic case (#1): topic is start-or-resume, so a fresh-workspace
  expectation flakes on a machine with prior data. Assert the
  start-or-resume contract — mission interview for a new workspace,
  resume for an existing one.
- teach primer case (#3): the primer intakes the user's starting point
  with one question before building the ladder, so a single-turn run may
  correctly open with the intake. Make the expectation intake-first.
- diagnose cleanup case (#6): a broad grep -r "[DEBUG-" already matches
  the diagnose docs and this fixture in the checkout. Scope the assertion
  to the specific [DEBUG-a4f2] tag across the changed source.
kyle-sexton added a commit that referenced this pull request Jul 13, 2026
… hardening (#138)

Applies the resolved `planning/setup` hardening to `knowledge/setup`'s
`library_dir` seam (same single-directory `userConfig` shape, same
latent issues).

## Changes

**`plugins/knowledge/skills/setup/SKILL.md`**
- **Precedence** — step 1 now resolves the full documented order
(Managed > `--settings` > Local > Project > User), warns when higher
layers are unreadable instead of declaring a lower value authoritative,
and surfaces layer shadowing before the project write.
- **Portability** — step 5 guards the tracked project write: never
propagate a machine-specific personal-layer value into team settings;
route personal-only overrides to `.claude/settings.local.json`
(portability required only for the shared project write).
- **Declared-convention alignment** — inspect the repo's declared
working-notes/artifacts convention even when a value is set; surface
divergence and offer to reconcile.

**`plugins/codebase-audit/skills/setup/SKILL.md`** (partial match —
writes a tracked `.md`, not settings)
- Applied only the precedence/shadowing subset: step 1 reports the
*effective additively-merged* config across user-global → team → local
layers (and warns on unreadable layers), instead of summarizing the team
file alone. Settings-write portability specifics do not apply.

## Deviations (own-flagged)

1. **Fix #3 premise softened for knowledge.** The issue's fix #3
parenthetical assumed knowledge "honors that convention over the config
at write time" (true for planning). Verified via
`skills/youtube/extraction/lib/work-root.js` and the youtube pipeline
that **knowledge resolves `library_dir` directly at write time — no
skill overrides it with a repo convention at runtime.** Documenting that
behavior would be false, so I softened the imported write-time-override
claim: `library_dir` is runtime-authoritative, and setup's job is to
keep it *aligned* with any declared convention. Fixes #1 and #2 are
unaffected.
2. **`codebase-audit` version bump not in issue scope.** Patch-bumped
`0.1.0 → 0.1.1` because its SKILL behavior changed; the issue only
specified the `knowledge` minor bump.

## Verification
- `knowledge` `plugin.json` minor bump `0.2.0 → 0.3.0`; `codebase-audit`
`0.1.0 → 0.1.1`.
- `claude plugin validate` clean on both plugins.
- markdownlint clean on both edited files.

Refs melodic-software/medley#1463

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Changes are limited to plugin skill documentation and version
metadata; no application runtime or security-sensitive code paths are
modified.
> 
> **Overview**
> **Hardens setup skills** for the `knowledge` and `codebase-audit`
plugins so interactive setup reflects layered config reality instead of
treating a single team file as authoritative.
> 
> For **`knowledge/setup`**, step 1 now follows the full Claude Code
precedence chain (Managed → `--settings` → Local → Project → User),
warns when higher layers are unreadable, and calls out when a project
write would stay shadowed. A new step reconciles `library_dir` with
repo-declared working-notes conventions even when a value is already
set. Persistence (step 5) blocks copying machine-specific personal paths
into tracked project settings, routes personal-only overrides to
`.claude/settings.local.json`, and the output section documents honest
pipeline behavior (`book-distill` ignores `library_dir`; YouTube does
not yet honor it). **`knowledge`** bumps **0.2.0 → 0.3.0**.
> 
> For **`codebase-audit/setup`**, step 1 is expanded to summarize the
**effective merged** config across user-global → team → local layers
(including dimension opt-outs via empty source lists), explain that only
the team file is written, and warn when overlays block re-enabling
dimensions. **`codebase-audit`** patch-bumps **0.1.0 → 0.1.1**.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
f13139a. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
kyle-sexton added a commit that referenced this pull request Jul 16, 2026
… follow-ups) (#205)

## Summary

The three follow-ups deliberately deferred out of #204 (recorded in its
PR body), each decision-gated with the maintainer before editing. Net
**−341 lines**; no plugin manifest, schema, or skill behavior touched.

## #1 — Retire the audit snapshots

Delete `docs/evals-coverage.md` and
`docs/extensibility-contract-grading.md`. They were hand-stamped,
point-in-time records that drift the moment a plugin changes (the reorg
already forced a two-layer temporal re-stamp).

**Retire over generate.** The durable policy already lives in
`docs/MIGRATION-PLAYBOOK.md` — the warrant rule + consumer-verify recipe
("Evals — warrant policy and consumer-verify recipe") and the four-seams
contract ("Extensibility contract v2.1"). A generator could derive only
eval *presence* (a live glob) — the minority of one doc and none of the
other; class, contract grades, and medley program state stay irreducibly
hand-graded or externally-owned. Generating only the presence slice
yields a half-generated hybrid that still drifts on its biggest surface.
Retire is the clean fit.

**Gaps captured first.** Two warranted-but-unowned eval gaps were
recorded *only* in the retired snapshot (verified still live against the
tree): `context7/setup` and `planning/wayfind`. Filed durably before
deletion:
- `melodic-software/medley#1537` — evals(context7/setup)
- `melodic-software/medley#1538` — evals(planning/wayfind)

Inbound pointers updated: README doc-index (2 bullets removed) and the
playbook's evals section (now states on-demand verification instead of
pointing at a deleted snapshot). No dangling references remain
(repo-wide grep clean).

## #2 — Reclassify the depth-variant convention tension

The playbook stated a depth/intensity variant "takes an argument, never
a sibling," yet `discovery`'s `explore-deep` / `research-deep` ship as
sibling skills. Inspection shows they are **structural, not depth**:

- `explore-deep` — `context: fork` + `agent: general-purpose`: a
distinct execution *topology* (isolated forked subagent), declared in
frontmatter, not expressible as a runtime argument.
- `research-deep` — a dispatcher to the heaviest isolated tier (workflow
engine, forked subagent, or inline fallback), with its own body and
auto-invocation description.

The convention needed no exception — it needed a sharper
structural/depth test. Amended to classify **execution tier as a
structural axis** (sibling) and bind the `-deep` suffix to that axis, so
the naming no longer reads as a violation and a future reader does not
re-flag it. A true effort knob on one execution path still takes an
argument. No skills renamed.

## #3 — README revisit-condition dedup

Assessed, closed **no-op**. The coupling flagged in #204 is a
documented, directional SSOT+index: `docs/CATALOG-TAXONOMY.md` (L71–73)
declares plugin-scoped revisit conditions are *owned by each plugin's
README* and the taxonomy "Trigger register" lists them "only as
pointers." The READMEs are the single owners of genuine plugin-specific
rationale; the register summarizes them. Nothing to dedup. Confirmed
none of the three sections cross-names the taxonomy doc.

## Verification

- `scripts/validate-plugins.sh`: pass (all manifests + strict catalog
sync)
- `scripts/run-plugin-tests.sh`: 7 passed, 0 failed
- `markdownlint-cli2`: 0 errors on both touched markdown files
- Repo-wide grep for the retired doc names: no remaining references

## Note (not in this PR)

`docs/hook-migration-audit.md` is the same "point-in-time audit" genus
as the two retired here. Left untouched; flagged for a consistency
decision in a later pass.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_0163dXPBdRYFpx4wTtS648tE

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
kyle-sexton added a commit that referenced this pull request Jul 16, 2026
#218)

## Summary

`code-tidying:tidy`'s Phase H ("Ship") told the agent to create the PR
by calling `gh pr create` directly — a bare bypass of
`source-control:pull-request`'s canonical create flow, which resolves
issue-linkage (with an issue-existence check before shipping a `Closes
#N` keyword), assembles the PR body injection-safely (quoted heredoc +
parameter-expansion concat, a defense against shell injection through
prompt-derived text), and gates PR creation on a valid closing keyword
or explicit opt-out before calling `gh pr create` itself.

Closes decisions **#3** and **#42** of the melodic-software issue/PR
consistency initiative's Decisions Log
(`https://claude.ai/code/artifact/232ecdce-8316-4880-8c0a-dc3c7dcf3a63`):

- **#3 (formatting-tidy-canonical-pr-gate-bypass)** — "YES, fix — but
not via direct skill-to-skill coupling... minimize coupling between
`code-tidying:tidy` and `source-control:pull-request`... Likely shape: a
shared script/check both skills call, or a convention-level requirement
enforced by a common gate." This PR fixes the bypass without a hard
cross-plugin dependency (below).
- **#42 (crosscut-skill-content-sync-mechanism)** — "Single shared
reference doc, all authoring skills import it." Applied here for its
first concrete instance (see Design rationale).

## What changed

Phase H now applies the same optional-plugin graceful-degrade pattern
`tidy` already uses elsewhere in its own SKILL.md (Phase C for
`discovery`, Phase D for `work-items`):

- **If `source-control` is installed:** invoke `/pull-request create`,
supplying tidy's own title/body-section content. Its stage-and-commit
step (§2.3) is a no-op here since Phase E already committed the tidyings
atomically — the call goes straight to rebase-check, issue-linkage
resolution, and gated PR creation.
- **If `source-control` isn't installed:** the same invariants apply
inline — verify an issue exists before writing `Closes #N`, assemble the
body via quoted heredoc + concat (never an unquoted `<<EOF`), and refuse
`gh pr create` until the body carries a valid closing keyword or an
explicit `Refs #N` / `No related issue:` opt-out.

## Design rationale (decision #42, recorded as an assumption — no user
available to confirm interactively this session)

Considered extracting the safety invariants into a new shared reference
doc (mirroring the `docs/PLUGIN-ARTIFACT-PROTOCOL.md` → per-plugin
`reference/artifact-protocol.md` precedent, drift-checked by
`validate-plugin-contracts.mjs` and this session's new
`scripts/check-cross-plugin-source-drift.sh`). Rejected for now: every
invariant worth stating is already authoritative in
`pull-request/reference/create.md`, so a copied doc would be pure
duplication with drift risk — and with exactly one non-`source-control`
consumer today, it wouldn't even register as a cluster for the new drift
checker (which requires 2+ plugin copies). A prose convention pointer is
lighter, carries no duplication to drift, and degrades gracefully if
`source-control` is absent — which a hard-copied doc wouldn't buy any
safety over.

This is decision #42's design choice for its *first application*, not a
retrofit of every authoring skill in the marketplace — no other skill is
touched by this PR. Rolling the same pattern out to other PR-creating
skills (if any surface a similar bypass) is deferred, unscoped follow-up
work, tracked in the Decisions Log rather than guessed at here.

## Test plan

- `bash scripts/validate-plugins.sh` — all plugin manifests + strict
catalog validation pass.
- `node scripts/validate-plugin-contracts.mjs` — 16 setup skills + 1342
plugin files checked, clean.
- `npx markdownlint-cli2 plugins/code-tidying/skills/tidy/SKILL.md` — 0
errors.
- Confirmed no other stale bare-`gh pr create` references remain under
`plugins/code-tidying/`.
- No existing test file exercises `tidy`'s `SKILL.md` prose directly
(only `open-pr-count.test.sh`, unrelated to Phase H) — nothing to update
there.
kyle-sexton added a commit that referenced this pull request Jul 20, 2026
…verb (#709)

## Summary

The work-item-tracker seam reserves `work-map` **containers** as a
first-class use case, but its read side was repo-global only — there was
no way to operate one container within scope (#498). Concretely:
`list-frontier` had no container scoping, its rows drop parent linkage
(GitHub's list surface omits it), an unassigned/unblocked container
surfaced as **its own** frontier item, and no verb enumerated a
container's children (needed for a closed-children invariant check).
Surfaced by the #416 wayfind-routing planning pass.

## Fix

- **`list-sub-items <parent-id> [--state open|closed|all]`** — new seam
+ adapter verb. Returns a container's **direct** children as full
normalized item objects (same `{items:[…]}` envelope as `list-items`),
each re-parented to the container. Raw enumeration — closed and
nested-container children are kept (the closed-children invariant check
and sub-map traversal need them). `--state` defaults to `all`. Both
adapters implement it: GitHub resolves children via the native
`subIssues` link and intersects with `list-items` output (its list
surface omits parent linkage, so the child set can't be derived from
rows); the offline `local-markdown` adapter matches on the stored
`parent` frontmatter.
- **`list-frontier --parent <container-id>`** — container-scoped
frontier. Core reads `list-sub-items` for that container instead of the
repo-global `list-items`, then applies the identical filter. The scoped
verb is chosen **before** the capability gate, so an adapter with
`list-items` but not `list-sub-items` degrades explicitly (exit 6)
rather than passing the gate then failing mid-call.
- **Container exclusion (obs #3).** `list-frontier` now excludes any
item carrying the container label (`work-map`) **unconditionally** —
global and `--parent`-scoped, and under `--autonomous`. Justification:
the CONTRACT already states containers are never claimable by workers,
so this is a correctness fix, not a policy toggle — deliberately not
behind a flag.

Design notes for the reviewer:
- The container label is a named constant (`WIT_CONTAINER_LABEL`,
default `work-map`) matching the CONTRACT term — not a new binding key.
Per-repo remapping is **deferred** to the existing `config.role_labels`
convention, keyed off the first consumer that needs a non-`work-map`
marker.
- Obs #2 (global rows drop parent linkage) is addressed by the
`--parent` scoping path, **not** by enriching global `list-items` rows —
GitHub's bulk list surface genuinely omits `parent`, and `get-item`
remains authoritative for a single item's parentage (unchanged CONTRACT
stance).
- Container exclusion lives only in the core frontier filter;
`list-sub-items` stays a raw enumeration (nested sub-maps and closed
children must survive it).

Version `0.15.0` → `0.16.0` (additive); CHANGELOG entry added.

## Verification

All commands run in the worktree on the rebased branch (tip of
`origin/main` + this commit).

New behavioral test — `local-markdown list-sub-items` end-to-end through
the core CLI (offline, CI-runnable):

```
$ bash plugins/work-items/tools/work-item-tracker/adapters/local-markdown/list-sub-items.test.sh
PASS: [1] list-sub-items holds open child C1
PASS: [2] list-sub-items holds closed child C2
PASS: [3] list-sub-items excludes the container itself
PASS: [4] list-sub-items excludes an unrelated leaf
PASS: [5] list-sub-items excludes a grandchild (direct children only)
PASS: [6] list-sub-items (default all) child count
PASS: [7] children carry parent_id
PASS: [8] list-sub-items --state open keeps only C1
PASS: [9] list-sub-items --state closed keeps only C2
PASS: [10] list-sub-items on a leaf is empty
PASS: [11] scoped frontier holds the open child C1
PASS: [12] scoped frontier drops the closed child C2
PASS: [13] scoped frontier drops the grandchild (not a direct child)
PASS: [14] scoped frontier never holds the container
PASS: [15] global frontier excludes the unassigned/unblocked container
PASS: [16] global frontier still holds the workable leaf
```

Core frontier filter unit test (container exclusion added):

```
$ bash plugins/work-items/tools/work-item-tracker/lib/frontier.test.sh
PASS: [7] default frontier excludes the container (work-map)
PASS: [8] autonomous frontier also excludes the container
PASS: [9] explicit container label excludes the remap, not the stale default
... (9 cases, 0 failed)
```

Abstract conformance suite through the core CLI (exercises
`list-sub-items` + `list-frontier --parent` for the adapter — CI runs
this offline binding):

```
$ bash plugins/work-items/tools/work-item-tracker/conformance/run-conformance.sh --binding local-markdown
PASS: [43] list-sub-items A (exit code)
PASS: [46] A's children hold B
PASS: [47] enumerated child B is re-parented to A
PASS: [50] list-frontier --parent A (exit code)
PASS: [53] scoped frontier drops blocked child B
Conformance (local-markdown): 72 cases, 0 failed
```

Full `work-items` plugin test suite + shellcheck (`0.11.0`, repo
`.shellcheckrc`) over all changed/new shell files — both clean:

```
$ find plugins/work-items -name '*.test.sh' | while read t; do bash "$t"; done   # all pass, 0 failures
$ shellcheck -x <changed .sh + .test.sh>                                          # rc=0, no findings
$ bash scripts/check-changelog-parity.sh --check-bump origin/main                 # Every changed plugin has its CHANGELOG entry.
```

The GitHub `subIssues`+intersect path runs only under the on-demand
`e2e-probe` (needs a live sandbox repo), which was **not** executed
here; its `subIssues` node shape
(`id/number/title/url/state/repository.nameWithOwner` — no
`assignees/labels/blockedBy`, hence the intersect-with-`list-items`
design) was verified against gh's own source (`cli/cli
api/queries_issue.go`, `LinkedIssue` struct). e2e-probe was extended
with container-exclusion and `list-sub-items`/`--parent` assertions for
when it is next run against a sandbox.

Closes #498

## Related

- #498 — the seam's own read-verb coverage for containers (this PR).
- #416 — routes `planning:wayfind` through the seam (the consumer that
surfaced this gap); related but distinct, unblocked by this change.
- #379 — Jira adapter (a different backend); related but distinct.

---------

Co-authored-by: Claude <noreply@anthropic.com>
kyle-sexton added a commit that referenced this pull request Jul 21, 2026
)

## Summary

Closes the three deferred, independently-corroborated (Codex +
claude[bot]) review findings from PR #248's review cycle against
`plugins/claude-ops/skills/plugins/scripts/fleet-state.sh`. Each was
verified VALID but deferred out of #248 to bound an already-long (6+
round) fix/review loop. All fixed here with TDD regression tests
(red-then-green).

Version bumped `0.15.4` → `0.16.0` (minor: adds a new consumable output
field).

## Fix

**1 — user-scope install completeness (additive, no behavior change to
the existing field).**
`fleet-state.sh` now emits a new field `missing_from_user_install` =
catalog minus *user-scope*-installed minus opted-out, alongside the
untouched all-scope `missing_from_install`. A plugin installed only at
`project`/`local` scope was present all-scope (absent from
`missing_from_install`) and so was never offered a `user`-scope install
by `sync` Step 4 — silently breaking the "usable from any directory"
guarantee for it. `sync.md` Step 4 now keys its user-scope install offer
off the new field. The existing `missing_from_install` field and its
all-scope semantics are unchanged.
Chosen approach: the additive option from the issue (new field), not the
redefinition option — no behavior change to any existing consumer.
Beyond the literal instruction: `SKILL.md`'s report template ("Action
needed") reference was also switched to `missing_from_user_install`, so
the report and Step 4 name the same signal (safe —
`missing_from_user_install` ⊇ `missing_from_install` always, since
user-installed ⊆ all-installed). `scope-semantics.md`'s reference was
deliberately left on `missing_from_install` — it documents the preserved
all-scope field's rename-coverage semantics.

**2 — per-plugin shape validation.**
The shape check validated `.plugins` is an object but not that each
entry is an array. A drifted non-array entry passed the check, then
failed inside the installed-flatten `jq` pipeline in a command
substitution; with `set -uo pipefail` but no `set -e` the failure was
swallowed and the script exited **0** with `installed: []` instead of
failing loud per its stated design. The check now also asserts
`(.plugins | to_entries | all(.value | type == "array"))` and exits 2 on
drift.

**3 — `--marketplace` with no name.**
`fleet-state.sh --marketplace` (no following arg) set `TARGET=""`, then
`shift 2` failed silently (only one positional param left, no `set -e`),
leaving `$1` unchanged so the arg loop re-read `--marketplace` forever;
the post-loop `-z "$TARGET"` guard was never reached. The empty check
now runs inside the `--marketplace` branch before `shift`, exiting 2.
The now-dead post-loop guard was removed.

## Verification

TDD: three regression cases were added and run **against the unfixed
script first** to prove each bug, then the fixes were applied.

Before (unfixed script — the three new cases red, proving each bug):

```
FAIL: user-scope: project-only + never-installed both surface, user-scope install excluded
FAIL: user-scope: opt-out excluded from missing_from_user_install too
FAIL: non-array plugin entry: exit 2
  detail: expected exit 2, got 0            # #2: swallowed failure — exited 0 with installed:[]
FAIL: --marketplace no arg: exit 2, not an infinite loop
  detail: expected exit 2, got 124          # #3: 124 = timeout killed the infinite loop
FAIL: --marketplace no arg: actionable error
```

After (fixed script — full suite green):

```
PASS: --marketplace no arg: exit 2, not an infinite loop
PASS: --marketplace no arg: actionable error

23 cases, 0 failed
```

`shellcheck` (both scripts) and `markdownlint-cli2` (all changed
markdown) report clean:

```
SHELLCHECK CLEAN
Summary: 0 error(s)
```

Closes #254

## Related

- Closes #254 — the tracking issue enumerating all three findings.
- Follow-up from PR #248 (`feat/claude-ops-plugins-skill`), the origin
review cycle where these three were verified VALID and deliberately
deferred.
- #797 — follow-up: `FLEET_STATE_HOOK_UTILS`'s test-only override isn't
gated to test contexts at runtime. Pre-existing (unchanged by this PR),
out of scope here — filed post-green per the lane's review-deferral
rule.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
kyle-sexton added a commit that referenced this pull request Jul 21, 2026
Work item #3 body and the stress-test reversal summary still named
--max-turns for the per-run mechanical bound, contradicting this note's
own recorded deviation (--max-turns absent from the installed CLI;
--max-budget-usd is the actual flag). Update both to --max-budget-usd so
the active Phase 1 build path can't wire the nonexistent flag.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fgx6EDAL4cGAFwTNgLpsus
kyle-sexton added a commit that referenced this pull request Jul 21, 2026
… recording tier, routines provenance (#845)

## Summary

Absorbs the vetted ideas from the "4 Levels of Agentic Coding" video
digest (Boris Cherny "Steps of AI Adoption" walkthrough) into the plugin
estate, per the approved plan at
`docs/topics/boris-video-absorption/PLAN.md`.

- **testing 0.3.0 (minor):** `/testing:run-e2e` gains the consumer
config surface `.claude/testing/e2e.md` (`recording: video|gif|off`
default `off`; `browser_mode: headed|headless` default `headless`) with
explicit read path, per-key merge + provenance reporting, and plumbing
to the playwright executor; keys owned by new `context/e2e-config.md`
(policy-vs-mechanics boundary; prompt > config precedence). Prerequisite
hard-fail keeps STOP and adds a structured verification-environment gap
report. Drive loop subagent-isolated; presence-gated `/verify`-first
delegation (CC >=2.1.145). Evidence contract adds optional recording
tier + session artifacts. Eval #1 updated.
- **verification 0.3.2 (patch):** `/verification:confirm` delegation
wording acknowledges the enriched run-e2e; stages/verdict machinery
untouched.
- **autonomy 0.8.0 (minor):** `reference/routines.md` gains the
instruction-provenance clause — routine instruction content lives in a
version-controlled artifact, stored prompt is a thin pointer; surface
mappings are illustrative deployment-owned bindings.
- Implementers-table row added in
`docs/conventions/consumer-config-layering/README.md`.

Coordination: zero file overlap with the ignition/enforcement lanes
(#778 comment posted; ignition Phases 2–3 merged via #810). Defaults
preserve current consumer behavior.

## Related

No linked issue: this PR closes no GitHub issue. Related, not closed:

- #778 — ignition lane coordination (comment posted; zero file overlap)
- #810 — ignition Phases 2-3 (merged; separate lane)

## Verification

- markdownlint: 0 errors on all touched files; typos gate exit 0
- `check-skill.sh`: run-e2e PASS, confirm PASS (pre-existing advisory
warnings only)
- run-e2e `evals/evals.json`: valid against bundled schema (ajv,
draft2020)
- Sanity greps per PLAN phase checks: all pass
- Stage-structure invariant on confirm SKILL.md: 16 before/after

<details>
<summary>PLAN.md</summary>

# PLAN — boris-video-absorption

## Brief

Absorb the vetted ideas from the Ray Amjad "4 Levels of Agentic Coding"
video digest (research base:

`~/.work/youtube-watch/the-4-levels-of-agentic-coding-how-to-sh-XLA-sTSJ-Wc/`)
into the plugin estate. Interview-locked contracts (this session):

- Extend existing seams, no new skill: `testing:run-e2e` (driver) +
  `verification:confirm` (judge) keep their division of labor.
- run-e2e gains: subagent-isolated surface runs; native-/verify-first
delegation (presence-gated, CC ≥2.1.145); structured missing-environment
  gap report on prerequisite failure (hard-fail stays, plus actionable
  report); video/session-artifact evidence tier.
- Recording format (video | gif | off) and browser mode (headed |
  headless) are layered config knobs per
  `docs/conventions/consumer-config-layering/` — NOT hardcodes.
- `plugins/autonomy/reference/routines.md` gains a thin-pointer clause:
  routine/scheduled-task prompts on any surface point to
  version-controlled files; pasted prose non-compliant.
- `~/.claude/scheduled-tasks/` (exists, live task) proposed for dotfiles
  tracking via the dotfiles repo's add-dotfile flow.
- Deferred-with-trigger (recorded in digest slice, NO tracker issues):
  verification self-improvement loop (trigger: accumulated recorded
  runs); bounded build→review→fix loop (trigger: post-Phase-II
  enforcement rail); Claude Tag enablement (trigger: post-8/3 cutover +
  Enterprise admin check).
- Out of scope: presenter-custom harness (Percy), chat-surface wiring,
  Chronicle adoption, lifecycle reordering.
- Boris doc capture re-verified NO-DRIFT 2026-07-21; all adopted items
  trace to the doc's step 1→2/2→3 recipes or native CC features.

Success: PRs merged with skill-quality + markdownlint green; every
adopted behavior traceable to research findings; zero collision with the
primary session's lanes (ignition Phases 2–3 merged via #810; no pending
branch touches target files — verified).

## Standards grounding

- `docs/conventions/consumer-config-layering/README.md` — three layers
  (user-global / team / local overlay), additive-preferred merge; new
  config surface must declare keys in the owning plugin's docs and point
  at the convention for layering.
- `docs/CATALOG-TAXONOMY.md` + `docs/PLUGIN-PHILOSOPHY.md` —
extend-over-
  add, seam cooperation presence-gated, fresh-eyes delegation.
- Repo commit/PR conventions (conventional commits, codex thread
  resolution before merge, `--body-file -` for PR bodies).

## Plan

### Phase 1: Lane setup [DONE]

- Create worktree off `origin/main`, branch
`feat/boris-video-absorption`
  (sibling-worktree convention).
- Materialize `docs/topics/boris-video-absorption/PLAN.md` (this file) +
`design/design-resolution.md` (Tier B early-exit: no new types; one new
  config surface whose shape is resolved here — see Decisions).
- Post coordination comment on #778: secondary session lane, files
  touched, no overlap with ignition/enforcement lanes.
- **Sanity Check:** `git worktree list` shows the new worktree on the
new
  branch; `gh issue view 778` shows the comment; PLAN.md exists in the
  worktree at `docs/topics/boris-video-absorption/PLAN.md`.

### Phase 2: run-e2e enrichment [DONE]

Files: `plugins/testing/skills/run-e2e/SKILL.md`,
`plugins/testing/skills/run-e2e/context/e2e.md`,
`plugins/testing/README.md`, new
`plugins/testing/skills/run-e2e/context/e2e-config.md` (key owner doc),
`plugins/testing/skills/run-e2e/evals/evals.json` (eval #1 asserts the
prerequisite-failure behavior this phase changes — update
unconditionally), `plugins/testing/CHANGELOG.md` (Keep a Changelog
entry;
**minor** bump — new behaviors),
`docs/conventions/consumer-config-layering/README.md`
(Implementers-table
row for the new surface — the convention tracks conformance, not
assumes it).

- Declare config surface `.claude/testing/e2e.md` (folder-form name per
  convention: surface identity = whole path relative to `.claude/`).
Keys: `recording: video | gif | off` (default `off` — evidence contract
screenshots stay the floor), `browser_mode: headed | headless` (default
  headless, operator overrides for visibility). Per-key override
  semantics. Layering resolution cites the convention doc — no
  restatement.
- **Read path (reviewer gap #3 — mandatory):** SKILL.md gains explicit
  resolution instructions — anchor at repo root, read all three layers
(user-global → team → local overlay), merge per-key, report which layer
  supplied each effective value. **Plumbing:** resolved `browser_mode`
  and `recording` are passed to the executor (`/playwright:playwright`
  invocation args / gif_creator choice) — keys without plumbing are the
  documented ai-briefing deviation; not repeating it.
- **Key-ownership framing (reviewer risk #6):** e2e-config.md states the
  boundary — run-e2e owns capture POLICY (what evidence, which format,
  visibility preference); playwright owns MECHANICS (how the browser
  runs). Keys are policy inputs run-e2e resolves and passes through.
- **Precedence (operator-locked):** config keys are DEFAULTS only; an
  explicit session prompt always wins ("run this headed" overrides
  `browser_mode: headless`). e2e-config.md states this: prompt >
  local overlay > team > user-global > bundled default.
- SKILL.md: subagent-isolated run guidance (delegate the drive loop to a
  subagent; orchestrator consumes evidence paths only); /verify-first
  delegation note (presence-gated on bundled /verify, CC ≥2.1.145, with
  existing orchestrator fallback unchanged).
- Prerequisite failure path: keep hard-fail STOP; add a structured
  verification-environment gap report (what's missing, what the operator
  must provide — keys/CLIs/environments; the "what do you need to verify
  this e2e" elicitation) written to the run's evidence output.
- context/e2e.md evidence contract: add optional recording tier (video
  via playwright CLI preferred for long flows; GIF via gif_creator for
  short demos — driven by the `recording` key) + session artifacts row
(recording path, session ID, transcript pointer) in the evidence table.
- **Sanity Check:**
`rg -c "e2e-config|\.claude/testing/e2e\.md"
plugins/testing/skills/run-e2e/SKILL.md`
  ≥ 1; `rg -c "gap report" plugins/testing/skills/run-e2e/SKILL.md` ≥ 1;
  `rg -c "recording" plugins/testing/skills/run-e2e/context/e2e.md` ≥ 2;
  **read-path present:**
  `rg -c "layer" plugins/testing/skills/run-e2e/SKILL.md` ≥ 2 AND
  `rg -c "browser_mode" plugins/testing/skills/run-e2e/SKILL.md` ≥ 2
  (resolution + plumbing sites); Implementers-table row:
`rg -c "testing/e2e"
docs/conventions/consumer-config-layering/README.md`
  ≥ 1; CHANGELOG entry present; eval #1 updated
  (`rg -c "gap" plugins/testing/skills/run-e2e/evals/evals.json` ≥ 1);
  markdownlint exit 0 on changed files; `/skill-quality:check run-e2e`
  PASS.

### Phase 3: confirm delegation touch-up [DONE]

Files: `plugins/verification/skills/confirm/SKILL.md`,
`plugins/verification/README.md` (only if wording drifts),
`plugins/verification/CHANGELOG.md` (**patch** bump — wording touch-up).

- Delegation section: acknowledge enriched run-e2e (subagent isolation +
  recording/session evidence tier + gap report); /verify stays named as
  the supplementary live-run path with unchanged presence gate. No
  structural change to stages or verdict machinery.
- **Sanity Check:**
`rg -c "gap report|recording"
plugins/verification/skills/confirm/SKILL.md`
  ≥ 1; stage structure unchanged
  (`rg -c "Stage" plugins/verification/skills/confirm/SKILL.md` equal
  before/after); markdownlint exit 0.

### Phase 4: routines.md thin-pointer clause [DONE]

Files: `plugins/autonomy/reference/routines.md`,
`plugins/autonomy/CHANGELOG.md` (**minor** bump — new normative clause).

- New subsection, phrased to honor the doc's hosting-agnosticism
  (reviewer risk #7): the NORMATIVE clause is surface-agnostic — "a
routine's instruction content lives in a version-controlled, reviewable
  artifact; the stored prompt is a thin pointer to it; pasted-prose
  prompts are non-compliant (no history, invisible drift)". The
  surface-specific mappings (cloud routines → committed
  `.claude/skills/`, documented transfer path; Desktop scheduled tasks →
`~/.claude/scheduled-tasks/<task>/SKILL.md` under dotfiles) appear only
  as ILLUSTRATIVE binding examples, marked as deployment-owned bindings
  per the doc's own Hosting stance. Cite research
  (`routines-versioning-deep-dive.md`: native prompt versioning
  documented-absent). If the clause reads better in
  `wiring-vs-advisor.md` at build time, the builder flags it back to the
  main thread rather than deciding — placement objection is the known
  review risk.
- **Sanity Check:**
`rg -c "thin pointer|version-controlled"
plugins/autonomy/reference/routines.md`
  ≥ 2; agnosticism preserved:
  `rg -c "deployment-owned" plugins/autonomy/reference/routines.md`
  count increases by ≥1; markdownlint exit 0; CHANGELOG entry present.

### Phase 5: digest-slice deferred items + through-line [DONE]

Files (home slice, not repo):

`~/.work/youtube-watch/the-4-levels-of-agentic-coding-how-to-sh-XLA-sTSJ-Wc/recommendations/menu.md`,
`README.md` (same slice).

- Update menu items to final dispositions (adopted → PR refs; deferred →
  named triggers; no-go unchanged). Add through-line note: Boris doc →
  interview locks → PLAN → PRs.
- **Sanity Check:** `rg -c "deferred" menu.md` ≥ 3; each adopted item
  carries a PR/branch reference.

### Phase 6: dotfiles tracking proposal [TODO — user gate]

- Run the dotfiles repo's `add-dotfile` flow for
  `~/.claude/scheduled-tasks/` (dir exists with live
  `autonomy-demo-hourly-drain/`). Flow owns chezmoi mechanics; never
  `chezmoi apply` from agent context.
- **Sanity Check:** dotfiles repo PR/branch exists containing the
  scheduled-tasks source state, or an explicit operator decline recorded
  in PLAN.
- **User gate:** surfaces before executing (cross-repo write).

### Phase 7: verify + ship [TODO]

- Empirical verification in worktree: markdownlint sweep, typos gate,
  `/skill-quality:check` per touched skill, eval validation.
- Version bumps via Edit on the version line (biome retab gotcha).
- Conventional commits; PR(s) with PLAN.md in `<details>`; codex threads
  resolved before merge; close-out per `/planning:plan close-out`
  (slice prune + pointer).
- **Sanity Check:** CI green on PR; `gh pr view` shows mergeable; all
  phase tags [DONE].

### Phase 8: trial run (post-merge validation) [TODO]

- After plugins update, run the enriched flow end-to-end on a real UI
  change in a consumer repo: config surface resolved + reported,
  recording produced per key, gap report exercised (remove a prereq
  deliberately), evidence table carries session artifacts.
- **Sanity Check:** recording artifact exists on disk; gap report
emitted
  on induced prereq failure; operator reviews the recording.

## Blast radius

LOW-MEDIUM. Markdown skill/contract text + one new additive config
surface; zero runtime code; three plugins touched; consumers unaffected
by default (recording defaults off, browser_mode default preserves
current behavior). Cross-repo edge: dotfiles proposal (user-gated).

## Stress-test summary

Fresh-context plan-reviewer ran against live repo. 3 CONFIRMED-GAPs
folded: (1) phantom config surface — read-path resolution + executor
plumbing now mandated in P2 with sanity greps; (2) Implementers-table
row added to P2 files; (3) CHANGELOG entries added to P2/P3/P4 with
SemVer magnitudes (testing minor, verification patch, autonomy minor).
Risks addressed: key-ownership boundary framed policy-vs-mechanics in
e2e-config.md; routines.md clause re-phrased surface-agnostic with
bindings as illustrative examples; eval #1 update made unconditional.
Collision re-verified clean (no open PR overlap). Formal
/devils-advocate skipped: blast radius LOW-MEDIUM, no
infra/CI/dependency triggers.

## Execution shape

Wave A (parallel, file-disjoint, one worktree, agents never touch git):

| Phase | Surface | Basis |
|---|---|---|
| 2 run-e2e | Opus builder subagent | mechanical doc edits, largest item
|
| 3 confirm | Opus builder subagent | small disjoint edit |
| 4 routines | Opus builder subagent | small disjoint edit |

Wave B (sequential, main thread): 5 (home slice), 6 (user-gated
cross-repo), 7 (verify + ship — main thread only, empirical checks +
git).

Phase 1 precedes all. Scope fences: each builder gets an ALLOWED list =
its phase's files; FORBIDDEN = PLAN.md, other phases' files, any git
command. Cost: 3 builders vs sequential saves modest wall-clock;
fallback
= run 2→3→4 sequentially in main thread on any fence violation.

## Open questions

None blocking — all interview-locked.

## Handoff to implementation

### User-approval gates

- Phase 6 dotfiles add-dotfile run (cross-repo write).
- Any mid-flight scope change to config-surface shape.

### Execution shape ([EXEC-SHAPE] tagged)

- [EXEC-SHAPE] PLAN drafted in `.work/` first; materialized to
  `docs/topics/` inside the worktree at Phase 1 (avoids writing to the
  canonical checkout the primary session owns).
- [EXEC-SHAPE] Wave A 3-builder parallel shape with scope fences as
  above; sequential fallback documented.
- [EXEC-SHAPE] Single PR for phases 2–4 (one review surface, shared
  rationale) unless review load argues for a split at ship time.

### Mechanical work

- Commit boundaries: Phase 1 (PLAN + design-resolution), one commit per
  phase 2–4, Phase 5 lives outside the repo (no commit), Phase 7 ship.
- Verification checkpoints: markdownlint + typos + skill-quality per
  phase before its commit; empirical greps per Sanity Checks.
- Sequential fallback: any fence violation → abandon parallel, re-run
  remaining phases in main thread.

</details>

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01QdCmaHa15bwUTh9sy7Rfdz

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
kyle-sexton added a commit that referenced this pull request Jul 21, 2026
)

## What

Sharpens the `orchestrate` skill's per-worker tiering (imperative 7)
into an explicit **volume-based default**, and broadens "tier" from
model-only to model **and** reasoning effort.

## Why

Carried from the re-anchor round-2 ledger (task #3), observed in
production: an `/orchestrate` run fanned out ~100–200 agents, all on the
top-tier model. PR #340 (0.9.1) fixed the root spawn-inherit bug — SPEC
EVERY SPAWN + CALIBRATE now carry a per-worker tier — but the residual
enhancement (making the tier *default* flip on volume) was never built.
The prior text stated the direction as a gradient ("the wider the
fan-out, the cheaper the default per-worker tier") without wiring the
verify/judge carve-out as the standing exception, and never named
reasoning effort as a tiering lever.

## Change

`plugins/session-flow/skills/orchestrate/SKILL.md` — imperative 7:

- Past a wide fan-out, the cheaper tier is now the **DEFAULT the whole
fleet inherits** (volume multiplies every notch of over-provisioning).
- The **standing exception** is an explicitly hard stage — verify,
judge/adjudicate, judgment-heavy synthesis — which keeps the parent
tier. A premium fan-out outside those stages is a per-stage decision to
justify, never a default to inherit.
- **Tier is model AND effort**: match reasoning depth (effort), not just
the model, to the subtask.

`context/sources.md` — three new verbatim citations grounding the change
in current official docs: the per-worker `effort` frontmatter lever, the
workflow fleet-model inherit mechanism, and the platform's own wide-run
threshold (its `Large workflow` warning + `/config` size guideline). Per
the skill's model-/tool-agnostic construction, these version-pinned
specifics stay in `sources.md`, never the imperatives or export brief.

## Grounding (fresh docs, this session)

- Subagent `model` precedence + `inherit` default, `effort` field,
cost-routing guidance — <https://code.claude.com/docs/en/sub-agents>
- Workflow fleet-model inheritance, per-stage routing, `Large workflow`
threshold + size guideline — <https://code.claude.com/docs/en/workflows>

## Gates

- `check-skill.sh orchestrate`: PASS (7/7 trigger phrases preserved,
125/500 lines, markdownlint clean)
- `check-changelog-parity.sh --check` and `--check-bump origin/main`:
PASS
- `validate-plugin-contracts.mjs`, `generate-catalog.mjs --check`,
`claude plugin validate .`: PASS
- Version bumped 0.12.2 → 0.12.3 with CHANGELOG entry in the same diff.

## Related

- PR #340 — the session-flow 0.9.1 spawn-inherit fix this builds on
(SPEC EVERY SPAWN + CALIBRATE carrying a per-worker tier); not closed by
this PR.
- The re-anchor round-2 ledger task #3 production observation that
motivated the residual enhancement.

Closes #863.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant