Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
14 commits
Select commit Hold shift + click to select a range
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
4 changes: 2 additions & 2 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
{
"name": "maintainerd-core",
"source": "./plugins/core",
"description": "Foundation: bootstrap (generates a repo's .claude/maintainerd.json config contract) and doctor (validates that setup and reports what's wrong), plus the canonical config schema every other plugin reads. Install this first.",
"version": "0.5.0"
"description": "Foundation: bootstrap (generates a repo's .claude/maintainerd.json config contract), doctor (validates that setup, and reports drift from a fleet's repo standard with --profile) and new-repo (creates or adopts a repo against that standard), plus the canonical config schema every other plugin reads. Install this first.",
"version": "0.6.0"
},
{
"name": "repo-ops",
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ jobs:
- name: The coverage-ratchet shell helpers behave, and fail closed
run: ./scripts/test-coverage.sh

- name: The repo-profile helpers resolve and diff correctly, and fail closed
run: ./scripts/test-profile.sh

- name: Vendored reference docs are in sync with the canonical
run: ./scripts/sync-references.sh --check

Expand Down
24 changes: 22 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ skill generates that contract for any repo.

| Plugin | Skills | Install when |
| --- | --- | --- |
| **[maintainerd-core](plugins/core/README.md)** | `bootstrap`, `doctor` | Always — `bootstrap` generates the config every other plugin needs; `doctor` validates it. |
| **[maintainerd-core](plugins/core/README.md)** | `bootstrap`, `doctor`, `new-repo` | Always — `bootstrap` generates the config every other plugin needs; `doctor` validates it; `new-repo` brings a repo to a fleet's standard. |
| **[repo-ops](plugins/repo-ops/README.md)** | `create-pr`, `address-review`, `release`, `daily-changelog`, `daily-update` | You want the baseline PR + changelog dev flow. |
| **[audits](plugins/audits/README.md)** | `audit-architecture`, `audit-tests`, `audit-security`, `audit-deps`, `audit-design-docs`, `audit-product-docs` | You want scheduled tech-debt / test / security / dependency / doc sweeps. |
| **[research](plugins/research/README.md)** | `research-radar` | You want proactive research surfaced — a periodic arXiv scan for papers relevant to this repo. |
Expand Down Expand Up @@ -139,7 +139,7 @@ maintainerd/
scripts/bump-version.py
scripts/test-coverage.sh
plugins/
core/ .claude-plugin/plugin.json plugin.json skills/{bootstrap,doctor}/ references/{config-schema,model-tiers}.md scripts/coverage-{adapt,check}.sh
core/ .claude-plugin/plugin.json plugin.json skills/{bootstrap,doctor,new-repo}/ references/{config-schema,model-tiers,profile-schema}.md scripts/{coverage-adapt,coverage-check,profile-resolve,settings-diff}.sh
repo-ops/ .claude-plugin/plugin.json plugin.json skills/{create-pr,address-review,release,daily-changelog,daily-update}/
audits/ .claude-plugin/plugin.json plugin.json skills/{audit-architecture,audit-tests,audit-security,audit-deps,audit-design-docs,audit-product-docs}/ references/pattern-promotion.md
research/ .claude-plugin/plugin.json plugin.json skills/{research-radar}/
Expand Down Expand Up @@ -250,6 +250,26 @@ A restart is required for either to take effect. If a skill looks like it's runn
version, compare the version in `claude plugin list` against `marketplace.json` — and check the
version segment of the cache path, since that is what the skill is actually being read from.

## One standard, many repos

A workspace's repos should be configured the same way, and "the same way" should be a file rather
than a habit. A **repo profile** is that file: one versioned JSON holding the standard — private or
public, merge methods, branch protection, required checks by name, labels, the CI shape per language,
the coverage policy — with per-language blocks and a per-repo override valve.

Maintainerd ships the mechanism and no values. The profile is an argument; nothing here names an org.

- **[`new-repo`](plugins/core/skills/new-repo/SKILL.md)** creates or `--adopt`s a repo against a
profile: scaffolds the files, runs `bootstrap`, creates the labels, and applies the GitHub settings
— showing every `gh api` call first, and **refusing the mutating half outside an interactive
session with a human's own token**.
- **[`doctor --profile`](plugins/core/skills/doctor/SKILL.md)** reports drift and never fixes it:
files vs profile, GitHub settings vs profile (each difference with the call that fixes it), and a
producer for every required check. The cadence it is built for is a weekly issue per drifted repo,
updated in place and closed on conformance — a human pastes the calls.

The contract is [`plugins/core/references/profile-schema.md`](plugins/core/references/profile-schema.md).

## Roadmap

Planned and candidate skills — what's shipped, what's ready to extract from an existing repo, and
Expand Down
1 change: 1 addition & 0 deletions docs/roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ dedicated `issues` plugin.
| Skill | Status | Source | What it does |
| --- | --- | --- | --- |
| ~~`doctor`~~ | ✅ **Shipped** | new (in `maintainerd-core`) | The companion to `bootstrap`: validate `.claude/maintainerd.json` (parses + schema-conformant), that paths/commands/guidelines resolve, that the configured labels exist on GitHub, that the daily-update roster only names installed skills, auto-dev + release coherence, and flag stubbed `invariants.md`. Read-only PASS/WARN/FAIL report; offers to create missing labels with `--fix`. The thing you run when a skill misbehaves. |
| ~~`new-repo`~~ | ✅ **Shipped** | new (in `maintainerd-core`) | Create — or `--adopt` — a repo against a **repo profile**, the standard a fleet of repos is held to as one versioned JSON file. Scaffolds every file the profile requires (settings, CI with the coverage-ratchet steps, PR template, CODEOWNERS, dependabot), runs `bootstrap`, creates the labels, and applies the GitHub settings via `gh api` — showing every call first and refusing the mutating half outside an interactive session with a human's own token. `doctor --profile` is its report-only companion (checks 14-16), built to feed a weekly drift issue per repo. |
| `sync-config` | ✨ Net-new | — | Re-run bootstrap's detection when the repo changes (new test command, renamed dirs) and propose a config diff for approval. |
| `skill-author` | ♻️ Extract | `skill-author` in pepper | A *meta* skill that scaffolds a new config-driven Maintainerd skill (frontmatter, config preamble, conventions). On-brand and accelerates everything else in this list. |

Expand Down
6 changes: 6 additions & 0 deletions plugins/audits/references/config-schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,12 @@ repo:
}
```

The full profile contract — its shape, the `defaults` + language + override resolution, and what it
deliberately does not govern — is a document of its own, `profile-schema.md`, shipped beside this one
in **maintainerd-core**. It is named rather than linked here because this file is vendored into every
plugin that reads it, and the profile is core's alone: a link would resolve in core's copy and dangle
in the other six.

`mode: "ratchet"` is the only mode: the effective floor is whatever the repo measured at adoption
and may only rise. `target` records where the coverage should land and is never a gate — a number
that fails a build has to be one the repo has already met. `coverage: null` exempts every repo of
Expand Down
6 changes: 6 additions & 0 deletions plugins/auto-dev/references/config-schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,12 @@ repo:
}
```

The full profile contract — its shape, the `defaults` + language + override resolution, and what it
deliberately does not govern — is a document of its own, `profile-schema.md`, shipped beside this one
in **maintainerd-core**. It is named rather than linked here because this file is vendored into every
plugin that reads it, and the profile is core's alone: a link would resolve in core's copy and dangle
in the other six.

`mode: "ratchet"` is the only mode: the effective floor is whatever the repo measured at adoption
and may only rise. `target` records where the coverage should land and is never a gate — a number
that fails a build has to be one the repo has already met. `coverage: null` exempts every repo of
Expand Down
6 changes: 3 additions & 3 deletions plugins/core/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"name": "maintainerd-core",
"version": "0.5.0",
"description": "Foundation for the maintainerd maintainer toolkit. Ships bootstrap (generates a repo's .claude/maintainerd.json config contract and starter guidelines) and doctor (validates that setup — config, paths, commands, labels, guidelines, rosters — and reports what's wrong), plus the canonical config schema reference that every other maintainerd plugin reads.",
"version": "0.6.0",
"description": "Foundation for the maintainerd maintainer toolkit. Ships bootstrap (generates a repo's .claude/maintainerd.json config contract and starter guidelines), doctor (validates that setup — config, paths, commands, labels, guidelines, rosters, the coverage ratchet — and, with --profile, reports a repo's drift from a fleet's repo standard), and new-repo (creates or adopts a repo against that standard), plus the canonical config and profile schema references that every other maintainerd plugin reads.",
"author": {
"name": "Allen Hutchison",
"url": "https://github.com/allenhutchison"
},
"homepage": "https://github.com/Vycari/maintainerd/tree/main/plugins/core#readme",
"repository": "https://github.com/Vycari/maintainerd",
"license": "MIT",
"keywords": ["config", "bootstrap", "doctor", "diagnostics", "maintainer", "scaffolding"]
"keywords": ["config", "bootstrap", "doctor", "diagnostics", "maintainer", "scaffolding", "profile", "drift"]
}
31 changes: 28 additions & 3 deletions plugins/core/README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
# maintainerd-core

The config layer every other maintainerd plugin depends on. `bootstrap` writes the contract;
`doctor` tells you why a skill isn't behaving. Install this first — the other plugins read the file
it generates and stop with "run `/bootstrap`" if it's missing.
`doctor` tells you why a skill isn't behaving; `new-repo` brings a whole repo to a fleet's standard.
Install this first — the other plugins read the file it generates and stop with "run `/bootstrap`"
if it's missing.

## Skills

| Skill | What it does | Typical trigger |
| --- | --- | --- |
| [`bootstrap`](skills/bootstrap/SKILL.md) | Generate `.claude/maintainerd.json` and scaffold `.claude/guidelines/{coding,testing,invariants}.md`. Inspects the repo for language, slug, branch, paths and commands; asks only about what's genuinely ambiguous. Adopts the coverage ratchet: measures `origin/<defaultBranch>` on a fresh worktree and records `coverage.floor`. Idempotent — a re-run never clobbers hand-edited guideline prose. | "bootstrap this repo", "set up maintainerd" |
| [`doctor`](skills/doctor/SKILL.md) | Validate the contract and everything it points at: the JSON parses and conforms, paths and commands resolve, the configured GitHub labels exist, the daily-update roster names only installed skills, and the coverage ratchet holds. Read-only PASS/WARN/FAIL; offers to create missing labels with `--fix`. With `--workspace`, validates an umbrella repo's `workspace` block and runs the whole check once per repo it lists. | "run doctor", "why isn't <skill> working" |
| [`doctor`](skills/doctor/SKILL.md) | Validate the contract and everything it points at: the JSON parses and conforms, paths and commands resolve, the configured GitHub labels exist, the daily-update roster names only installed skills, and the coverage ratchet holds. Read-only PASS/WARN/FAIL; offers to create missing labels with `--fix`. With `--workspace`, validates an umbrella repo's `workspace` block and runs the whole check once per repo it lists. With `--profile <path>`, holds the repo to a [repo profile](references/profile-schema.md) — files, GitHub settings, required-check producers — and reports every difference with the `gh api` call that fixes it, without ever running one. | "run doctor", "why isn't <skill> working", "check this repo against our standard" |
| [`new-repo`](skills/new-repo/SKILL.md) | Create a repo — or `--adopt` an existing one — against a [repo profile](references/profile-schema.md): scaffold every file the profile requires (settings, CI with the coverage steps, PR template, CODEOWNERS, dependabot), run `bootstrap`, create the labels, and apply the GitHub settings with `gh api`. Idempotent, and it shows every call before running any. **Refuses the GitHub-mutating half in a non-interactive session.** | "create a new repo", "adopt this repo into the standard" |

## Why `invariants.md` is the file that matters

Expand Down Expand Up @@ -49,6 +51,29 @@ This plugin ships the two shell steps that make it work, both bash 3.2 + `jq`, n
plugin installed. The contract, the workflow snippet, and the profile-side `coverage` policy are in
[`references/config-schema.md`](references/config-schema.md).

## One standard, many repos: the repo profile

A fleet with several repos wants them configured the same way. A **repo profile** is that standard as
one versioned JSON file — merge methods, branch protection, required checks, labels, the coverage
policy, the files every repo carries — and it is an **argument**: maintainerd ships no profile and
knows no org.

A repo's effective settings are the profile's `defaults`, then its language block, then its override,
merged key by key. `new-repo` applies them; `doctor --profile` compares against them and is
**report-only, forever** — it prints the `gh api` call that fixes each difference and runs none of
them, because branch protection and merge methods are org configuration with the blast radius of a
production write. The intended cadence is a weekly drift issue per repo, updated in place and closed
on conformance.

| Script | What it does |
| --- | --- |
| [`scripts/profile-resolve.sh`](scripts/profile-resolve.sh) | Validates a profile's shape and resolves one repo's effective settings. Both skills call it rather than re-deriving the merge — an explicit `null` overriding while an absent key inherits, and additive-but-never-subtractive `requiredChecks`, are exactly the rules two prose copies would drift on. |
| [`scripts/settings-diff.sh`](scripts/settings-diff.sh) | Diffs the effective settings against captured `gh api` output and prints the exact call that fixes each difference. It reads files, never the network, so the same diff can be dry-run, tested, and reviewed before anything is applied. |

The full contract, including what a profile deliberately does *not* govern, is in
[`references/profile-schema.md`](references/profile-schema.md), with a working example at
[`references/example-profile.json`](references/example-profile.json).

Skills that read text authored outside the repo follow the shared contract in
[`references/untrusted-input.md`](references/untrusted-input.md); scheduled skills note which model
tier they want in [`references/model-tiers.md`](references/model-tiers.md).
Expand Down
8 changes: 5 additions & 3 deletions plugins/core/plugin.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
"name": "maintainerd-core",
"version": "0.5.0",
"description": "Foundation for the maintainerd maintainer toolkit. Ships bootstrap (generates a repo's .claude/maintainerd.json config contract and starter guidelines) and doctor (validates that setup — config, paths, commands, labels, guidelines, rosters — and reports what's wrong), plus the canonical config schema reference that every other maintainerd plugin reads.",
"version": "0.6.0",
"description": "Foundation for the maintainerd maintainer toolkit. Ships bootstrap (generates a repo's .claude/maintainerd.json config contract and starter guidelines), doctor (validates that setup — config, paths, commands, labels, guidelines, rosters, the coverage ratchet — and, with --profile, reports a repo's drift from a fleet's repo standard), and new-repo (creates or adopts a repo against that standard), plus the canonical config and profile schema references that every other maintainerd plugin reads.",
"author": {
"name": "Allen Hutchison",
"url": "https://github.com/allenhutchison"
Expand All @@ -16,6 +16,8 @@
"doctor",
"diagnostics",
"maintainer",
"scaffolding"
"scaffolding",
"profile",
"drift"
]
}
6 changes: 6 additions & 0 deletions plugins/core/references/config-schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,12 @@ repo:
}
```

The full profile contract — its shape, the `defaults` + language + override resolution, and what it
deliberately does not govern — is a document of its own, `profile-schema.md`, shipped beside this one
in **maintainerd-core**. It is named rather than linked here because this file is vendored into every
plugin that reads it, and the profile is core's alone: a link would resolve in core's copy and dangle
in the other six.

`mode: "ratchet"` is the only mode: the effective floor is whatever the repo measured at adoption
and may only rise. `target` records where the coverage should land and is never a gate — a number
that fails a build has to be one the repo has already met. `coverage: null` exempts every repo of
Expand Down
101 changes: 101 additions & 0 deletions plugins/core/references/example-profile.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
{
"profileVersion": 1,
"org": "my-org",

"defaults": {
"private": true,
"defaultBranch": "main",
"merge": { "squash": true, "mergeCommit": false, "rebase": false, "deleteBranchOnMerge": true },
"mergeQueue": { "enabled": true, "mergeMethod": "squash" },
"protection": {
"requiredLinearHistory": true,
"allowForcePushes": false,
"allowDeletions": false,
"strictRequiredChecks": false,
"requiredReviews": { "count": 1, "countsBotApproval": true, "dismissStale": true },
"enforceAdmins": false
},
"labels": [
"architecture", "test-quality", "security", "dependencies", "automated",
"auto:needs-info", "auto:planned", "auto:ready", "auto:in-progress",
"auto:parked", "auto:skip", "auto:pr", "deps:blocked"
],
"review": {
"bots": ["coderabbitai[bot]"],
"approvalThreshold": "approved",
"responderTier": "capable",
"impasseRounds": 2,
"sameFileRoundCap": 3
},
"requireIssueForDeferredWork": true,
"files": {
"prTemplate": true,
"codeowners": "* @a-maintainer",
"greptileRules": false,
"claudeMd": true
},
"claudeSettings": {
"marketplaces": ["my-org/maintainerd"],
"plugins": ["maintainerd-core@maintainerd", "repo-ops@maintainerd", "audits@maintainerd"]
}
},

"languages": {
"python-service": {
"requiredChecks": ["ci", "docs"],
"coverage": { "mode": "ratchet", "target": 70 },
"commands": {
"format": "uv run ruff format --check",
"lint": "uv run ruff check",
"typecheck": "uv run mypy {source}",
"test": "uv run pytest",
"coverage": "uv run pytest --cov --cov-report=json:coverage-summary.json"
},
"dependabot": ["pip", "github-actions", "docker"]
},

"typescript-web": {
"requiredChecks": ["ci"],
"coverage": { "mode": "ratchet", "target": 50 },
"commands": {
"format": "npm run format:check",
"lint": "npm run lint",
"typecheck": "npm run typecheck",
"test": "npm test",
"coverage": "npm run coverage -- --coverage.reporter=json-summary"
},
"dependabot": ["npm", "github-actions"]
},

"shell": {
"requiredChecks": ["ci"],
"coverage": null,
"commands": {
"format": null,
"lint": "shellcheck -x $(git ls-files '*.sh')",
"typecheck": null,
"test": null,
"coverage": null
},
"dependabot": ["github-actions", "docker"]
},

"none": {
"requiredChecks": [],
"coverage": null,
"commands": {},
"dependabot": ["github-actions"]
}
},

"repoOverrides": {
"app": { "requiredChecks": ["ci", "migration-collision", "docker-smoke"] },

"infra": { "requiredChecks": ["invariants"] },

"site": {
"coverage": null,
"commands": { "test": "npm run build && npm run linkcheck", "coverage": null }
}
}
}
Loading
Loading