If the paths above do not exist, report an error and stop. Do not search the filesystem or guess paths.
For each area below, record: what exists, what's missing, what's under a wrong name, what duplicates a standard artifact.
-
2a. Docs folder. Look for docs/ (standard) or variants doco/, documentation/, doc/, documents/. Check for docs/specs/ and docs/plans/ subdirs. Flag loose markdown files in docs/ for classification.
-
2b. CI workflows. List .github/workflows/*.yml. Identify any existing workflow that does what ci.yml/release.yml/deploy-pages.yml should do, under any filename. Per spec [CI-WORKFLOWS] / [CI-JOBS] these will be renamed in Step 3, not re-created.
Also note whether .github/workflows/dependabot-automerge.yml exists and whether the dependabot-upgrades staging branch exists (git ls-remote --heads origin dependabot-upgrades) — both are required by [GITHUB-DEPENDABOT], created in Step 3g-dep if missing.
Flag these trigger violations per [CI-WORKFLOWS]:
ci.yml triggering on push: branches: [main] (merges to main must trigger nothing — PR-only, unless the repo allows non-PR commits to main, which must be commented).
ci.yml or codeql.yml triggering on branches: [dependabot-upgrades] (breaks the no-CI-on-staging guarantee — staging PRs must run zero CI; [GITHUB-DEPENDABOT]).
release.yml triggering on anything other than a v* tag push (no release on merge/schedule).
- A repo that HAS a website but whose
release.yml has no website-deploy step (release MUST deploy the site).
For release workflows, also flag these as critical blockers: checkout ref: main in tag jobs, any
git commit/git push/tag mutation, source-control version bumps after the tag, ad hoc sed
stamping of structured files, missing tests for build-time version stamping, and — for any repo with
a codeql.yml — a release.yml whose publish jobs do NOT needs: a gated CodeQL job (a release that
cannot be stopped by a security finding is a critical blocker, see [GITHUB-CODE-SCANNING]).
-
2c. Makefile. If present, read in full. Classify every public target per spec [MAKE-TARGETS]:
- (i) A standard target that applies to this repo → note whether present/missing/wrongly-implemented. Do NOT flag an absent standard target as missing if it has nothing to do here (no
build on a docs-only repo) — only the targets that apply are required.
- (ii) Duplicates/shadows a standard target under a synonym (e.g.
test-all, lint-fix, build-release) → candidate for merge+delete.
- (iii) Agent-pmo-stamped but source no longer in standards repo → orphan per [MARKER-CLEANUP].
- (iv) Genuine repo-specific target → belongs in
Repo-Specific Targets; preserve verbatim — never delete, rename, reorder, or regenerate it ([MAKE-REPO-SPECIFIC]).
- (v) Editor extension build (
.vsix/Zed/etc.) with no rebuild-install-<kind> target → note for addition ([MAKE-IDE-EXT]). Also flag any packaging-parity violation ([MAKE-IDE-EXT-PARITY]): a test/e2e target that builds a separate "test bundle" (different profile, target triple, or staging path) instead of running the one packaging recipe the release ships, or a release/test/install path that does not share a single manifest-driven staging helper.
Flag cross-platform violations ([MAKE-CROSS-PLATFORM]): raw rm -rf/mkdir -p instead of $(RM)/$(MKDIR). If a justfile/Taskfile.yml is used instead, ask the user before replacing.
-
2d. Linter configs. Look for legacy/alternate-name configs per spec [LINT]. ESLint v0-v8 files → eslint.config.mjs. Old Prettier variants → .prettierrc.json. Python .flake8/setup.cfg/tox.ini → Basilisk+ruff+pyright ([LINT-PYTHON-BASILISK]). .golangci.yaml → .golangci.yml. Migrating means delete the old file once the new one covers it.
-
2e. Formatter configs. Check CSharpier, Fantomas, Prettier, rustfmt, ruff format per spec [FMT] / [FMT-TOOLS]. [tool.black] in pyproject → migrate to [tool.ruff.format].
-
2f. Coverage. Check for coverage-thresholds.json at repo root (spec [COVERAGE-THRESHOLDS-JSON]). Flag any legacy threshold storage for migration:
vars.COVERAGE_THRESHOLD* in workflows
COVERAGE_THRESHOLD ?= … in Makefile
- Hardcoded numbers in CI YAML (
--lines 90, --fail-under 85)
gh variable list 2>/dev/null | grep -i COVERAGE
- Shell scripts (
scripts/check_coverage.sh, etc.) — replaced by the _coverage_check Make recipe
.coveragerc vs pyproject.toml [tool.coverage] — don't have both
-
2g. Gitignore. Read existing .gitignore in full. Per spec [GITIGNORE] and [GITIGNORE-RULES]:
- NEVER add ignores for:
.vscode/, .idea/, .claude/, .codex/, .agents/, .cline/, .clinerules/, .opencode/, .cursorrules, .windsurfrules, .github/copilot-instructions.md, AGENTS.md, CLAUDE.md, or any AI agent config/skill directories. These MUST be committed.
- If the existing
.gitignore already ignores any of the above, remove those lines — they are bugs.
- Add only clearly-safe patterns: OS junk (
.DS_Store, Thumbs.db), build artifacts (target/, node_modules/, dist/), secrets (.env, *.pem), and tool caches. Do not duplicate or replace patterns already present.
-
2h. LICENSE. Check for LICENSE/LICENSE.md/LICENSE.txt/LICENCE/COPYING/UNLICENSE. If missing, record for the Step 5 big warning. Do NOT create one — license choice has legal consequences and must be the user's decision.
-
2i. GitHub repo settings. Run gh api repos/OWNER/REPO. If gh is unavailable/unauthenticated/returns nulls, STOP and emit the warning below verbatim, then wait for explicit YES/NO:
╔══════════════════════════════════════════════════════════════════╗
║ ⚠️ WARNING: GitHub repo settings COULD NOT BE READ ⚠️ ║
╠══════════════════════════════════════════════════════════════════╣
║ `gh api` failed or returned no data. This matters because: ║
║ ║
║ 1. CI WORKFLOW: squash-only vs merge-commit affects how ║
║ ci.yml must be configured (branch triggers, merge checks). ║
║ ║
║ 2. REPO NORMALIZATION: merge strategy, branch protection, ║
║ PR settings, and issue/wiki toggles CANNOT be applied ║
║ without `gh` access. The repo will be left in whatever ║
║ state GitHub currently has it in. ║
║ ║
║ To fix: run `gh auth login` then re-run this skill. ║
╚══════════════════════════════════════════════════════════════════╝
Do you want to continue WITHOUT applying GitHub repo settings?
Answering YES means CI workflow assumptions may be wrong and repo
settings will NOT be normalized.
→ Type YES to continue anyway, or NO to abort and fix gh access first.
- NO → stop. Tell the user to run
gh auth login and re-invoke.
- YES → note "GitHub settings skipped — gh unavailable" in the Step 5 report and continue.
- If
gh works → compare against {{STANDARDS_REPO}}/agent-pmo-skill/templates/.github/common-repo-settings.md and only apply the diffs.
-
2j. Deslop duplication gate (spec [CI-DESLOP]; docs https://deslop.live/docs/for-ai/). Only if the repo's language is Deslop-supported — Rust, C#, Dart, Python. Otherwise skip and note "no Deslop-supported language". When it applies, record what's missing: (i) committed .deslop.toml at repo root with [threshold] max_duplication_percent; (ii) the Deslop install + deslop . gate step in ci.yml; (iii) the Duplication — Deslop section in the canonical instruction file telling the agent to use the find-similar/rescan/top-offenders MCP loop. Flag any anti-patterns: a hardcoded --fail-over <N> number in CI YAML, a threshold in a GitHub variable/env var instead of .deslop.toml, or a threshold that was raised without justification.
For every item: (1) compliant equivalent exists → leave alone; (2) equivalent exists under wrong name/content → rename/update in place; (3) nothing equivalent → create from template.
-
3a. Docs folder. Rename doco//documentation//doc//documents/ → docs/. Merge if both exist, then delete the non-standard one. Create docs/specs/ and docs/plans/. Classify loose markdown files: specs = behavior/requirements; plans = how-to with TODO checklists. Update internal references that pointed to the old folder name.
-
3a-ii. Spec ID rule. Ensure the rule is present in the canonical instruction file. Validation/renaming of existing spec IDs is the spec-check skill's job, not this one.
-
3b. Makefile. Per spec [MAKE-TARGETS], [MAKE-REPO-SPECIFIC], [MAKE-IDE-EXT], [MAKE-TEMPLATE]. Edit the existing Makefile surgically — never overwrite the whole file to "regenerate" it. The Makefile has two sections:
Standard Targets — canonical names (build, test, lint, fmt, clean, ci, setup), but ONLY the subset that applies to this repo. No synonyms, no extras here, and no hollow no-op target added just to complete the set.
Repo-Specific Targets — a separate section below, owned by the repo, varies per repo, preserved byte-for-byte unless agent-pmo stamped it.
Act on the Step 2c classification:
- (i) applicable standard target missing/wrong → add/fix in
Standard Targets. A standard target that doesn't apply here is NOT missing — leave it out.
- (ii) synonym/duplicate of a standard target → merge useful logic into the standard target, delete the duplicate, update callers.
- (iii) orphan → [MARKER-CLEANUP]: merge useful logic into the correct standard target, delete the orphan.
- (iv) genuine repo-specific target → leave it exactly as-is. If it sits inside
Standard Targets, move it down into Repo-Specific Targets unchanged. Never delete, rename, reorder, or "tidy up".
- (v) editor extension with no
rebuild-install-<kind> target → add one in Repo-Specific Targets ([MAKE-IDE-EXT]): uninstall → clean → rebuild → package → install-if-supported, via underscore-prefixed sub-recipes. Enforce parity ([MAKE-IDE-EXT-PARITY]): factor packaging into ONE recipe shared by rebuild-install-<kind>, the test/e2e target (which runs it BEFORE testing), and the release workflow, so the tested bundle is byte-for-byte the shipped bundle. Fold any separate "test build" into that one recipe.
Other rules from the spec: cross-platform ([MAKE-CROSS-PLATFORM]); _lint and _fmt do not overlap; _test uses the fail-fast flag AND calls _coverage_check last ([TEST-RULES]); uncomment only the language blocks that apply.
-
3c. GitHub Actions workflows. Rename the existing workflow from Step 2b rather than creating a parallel one. Follow spec [CI-WORKFLOWS], [CI-JOBS], [CI-TEMPLATE], [CI-RELEASE], [CI-SHIPWRIGHT], [CI-PAGES]. Default to a single ci job with sequential steps make lint → make test → make build; only split into parallel jobs if individual tasks are 5+ minutes each. Every job MUST have timeout-minutes: 10 — deviate only with a comment above explaining why:
timeout-minutes: 15
For release workflows, ensure every build/publish job uses the tagged SHA and runner-local version
stamping only.
CRITICAL — developer-tool repos: if Step 1 flagged this repo as a developer tool, run the
shipwright-compliance skill and follow it end to end (spec [CI-SHIPWRIGHT]). Record the outcome
in the Step 5 report.
-
3c-ii. Deslop duplication gate (spec [CI-DESLOP]). Only for Deslop-supported languages (Rust, C#, Dart, Python); skip otherwise.
- Create
.deslop.toml at the repo root from templates/.deslop.toml. Set max_duplication_percent to the repo's currently measured duplication (ratchet from measured — never invent a looser number). If you cannot measure it now, leave the template default and note it for the user to tighten.
- Add the Deslop install +
deslop . gate step to ci.yml from templates/.github/workflows/ci.yml (after make lint). Pin DESLOP_VERSION. The gate reads .deslop.toml — never hardcode the threshold in YAML.
- Ensure the canonical instruction file keeps the Duplication — Deslop section (the
find-similar before / rescan + top-offenders after MCP loop). This is preserved/added in 3h.
- Ratchet is downward only: never raise
max_duplication_percent without explicit written PR justification.
-
3d. Coverage. Read spec [TEST] in full before doing anything here. make test = fail-fast + coverage + threshold, one indivisible operation. Thresholds ONLY in coverage-thresholds.json per [COVERAGE-THRESHOLDS-JSON].
This skill must:
- Create
coverage-thresholds.json at the repo root from templates/coverage-calc/coverage-thresholds.json. Set default_threshold per [COVERAGE-THRESHOLDS]. For multi-project repos, list each project with its currently measured threshold (ratchet from measured — never above).
- Migrate every legacy threshold storage found in Step 2f into the JSON. Delete the old storage (env blocks, Makefile defaults, shell scripts, public
coverage* targets). For GitHub repo variables, instruct the user to delete them from Settings → Variables → Actions (this skill can't delete them).
- Wire
_coverage_check into _test. Keep it private — it is never a public target. Reference the language-specific commented blocks in templates/Makefile.
- Verify
ci.yml has NO coverage-check/coverage step and NO COVERAGE_THRESHOLD env vars. The workflow just calls make lint, make test, make build.
- .NET:
coverlet.runsettings per [COVERAGE-COVERLET]. Python: pyproject.toml [tool.coverage] only (no .coveragerc). TypeScript/Jest: [COVERAGE-JEST].
- Thresholds are monotonically increasing. Reject PRs that lower a threshold unless explicitly justified.
-
3e. Linter configs. Apply spec [LINT] and the per-language sections ([LINT-RUST], [LINT-TS-ESLINT], [LINT-TS-PRETTIER], [LINT-TS-STRICT], [LINT-PYTHON-BASILISK], [LINT-DART], [LINT-GO], [LINT-CSHARP], [LINT-FSHARP]). Merge into existing pyproject.toml/Cargo.toml/tsconfig.json — don't clobber non-lint sections. Delete superseded files (.eslintrc.*, .flake8, setup.cfg [flake8], .golangci.yaml, etc.) after migration.
-
3f. Formatting. Apply spec [FMT] / [FMT-TOOLS] / [FMT-PYTHON] / [FMT-MULTI]. make fmt, make lint, make test are three separate, non-overlapping targets.
-
3g-pre. Gitignore remediation. Before applying GitHub settings, fix .gitignore per spec [GITIGNORE-RULES]: scan the file for any line that ignores .vscode/, .idea/, .claude/, .codex/, .agents/, .cline/, .clinerules/, .opencode/, .cursorrules, .windsurfrules, or similar AI/IDE config paths. Delete those lines — they are bugs, not features. Then append patterns from templates/gitignore/universal.gitignore and the relevant language gitignore(s), skipping duplicates.
-
3g. GitHub repo settings. Apply spec [GITHUB-SETTINGS] / [GITHUB-MERGE] / [GITHUB-FEATURES] / [GITHUB-PROTECTION] / [GITHUB-CLI] via the commands in templates/.github/common-repo-settings.md. Applies to both new and existing repos. If the repo is local-only (no remote yet), skip and note for after-first-push. If gh was skipped in Step 2i, skip here too and record in the Step 5 report.
-
3g-dep. Dependabot (spec [GITHUB-DEPENDABOT]). Supply-chain defense without PR spam, and no CI on routine bumps — applies to every repo. Routine version bumps target a long-lived dependabot-upgrades staging branch that runs zero CI; the batch reaches main via one consolidation PR.
- Write
.github/dependabot.yml from templates/.github/dependabot.yml. Keep the github-actions block (any repo with workflows; version-only). Keep only the package-ecosystem blocks whose manifests exist in this repo (detect from Step 1's language scan); delete the rest. Every block must set target-branch: "dependabot-upgrades". Each LANGUAGE ecosystem needs BOTH a version-updates group AND a <eco>-security group (applies-to: security-updates), each patterns:["*"] with no update-types — never leave one ungrouped, never split minor/major.
- Write
.github/workflows/dependabot-automerge.yml from templates/.github/workflows/dependabot-automerge.yml — it sweeps EVERY dependabot[bot] PR into staging with git merge -X theirs (latest bump clobbers previous), triggering on BOTH dependabot-upgrades and main (security PRs are force-opened against main). Then add the Dependabot skip to ci.yml/codeql.yml: keep them pull_request: [main]-only (never add branches: [dependabot-upgrades]) AND gate against the bot — a job-level if: github.actor != 'dependabot[bot]' on the CI/security jobs, and && github.actor != 'dependabot[bot]' appended to the CodeQL visibility gate — so a security PR on main never burns the matrix on a bump that is immediately swept away.
- Cut the
dependabot-upgrades branch from main AFTER dependabot.yml + dependabot-automerge.yml are on main (so the staging branch carries the auto-merge workflow): gh api -X POST repos/$REPO/git/refs -f ref=refs/heads/dependabot-upgrades -f sha=$(gh api repos/$REPO/git/ref/heads/main --jq .object.sha). If gh was skipped or the repo is local-only, record for after-first-push.
- Enable per-repo alerts + security updates:
gh api -X PUT repos/$REPO/vulnerability-alerts and gh api -X PUT repos/$REPO/automated-security-fixes. If gh was skipped, record for after-first-push.
- Grouped security updates and "auto-enable for new repos" are account/org-level toggles — note them in the Step 5 report for the user to set in Settings → Code security (this skill can't toggle org settings).
-
3g-sec. Code scanning + secret scanning (spec [GITHUB-CODE-SCANNING] / [GITHUB-DEP-REVIEW] / [GITHUB-SECRET-SCANNING]). Every PR must undergo security scanning, with no coverage overlap between linting, CodeQL, and dependency review.
- CodeQL — tailor the matrix at runtime, do NOT copy a frozen language list.
- List the languages actually present in this repo (Step 1 language scan).
- Determine which languages CodeQL supports right now, at skill-run time — check live (CodeQL docs, or
codeql resolve languages if the CLI is available). CodeQL's set grows (Rust was added; more will follow); the list in the spec is illustrative only.
- Write
.github/workflows/codeql.yml from templates/.github/workflows/codeql.yml with one matrix include: entry per language in the intersection of 1 and 2, plus actions (always). Use build-mode: none for interpreted langs + rust/csharp; autobuild/manual for compiled langs that need a build (go, java-kotlin, c-cpp). Keep the SHA-pinned action versions and the if: ...visibility == 'public' gate.
- Triggers:
pull_request to main, schedule (weekly), and workflow_call exposing a gate boolean input. NEVER add push: branches:[main]. NEVER a standalone push: tags scan — it runs alongside the release and can only file alerts after the artifact ships, so it cannot gate anything.
- Wire the HARD release gate — CodeQL MUST be able to stop a release. If the repo ships releases (
release.yml exists), add a codeql job that uses: ./.github/workflows/codeql.yml with: { gate: true } (job-level permissions: security-events: write, actions: read, contents: read) and add it to the needs: of the release job — so it transitively gates every publish job. With gate: true the analyze job parses its SARIF and FAILS on any finding whose security-severity is High/Critical (>= 7.0); a dirty scan then blocks publishing. A release MUST NOT be able to ship code CodeQL flagged. The gated call also scans the exact released SHA with the current query set.
- If the intersection is empty (e.g. Dart/Flutter- or F#-only repo), do NOT write codeql.yml and add no gate job — record in the Step 5 report that no CodeQL-supported language was found.
- COMBINE — anti-duplication is mandatory (duplicate analysis wastes GitHub Actions minutes = money). Before adding anything, AUDIT the repo's existing workflows and configs and enforce exactly one owner per concern; delete the loser:
- Style/correctness → linters in
make lint. Strip any security-rule linter plugins (eslint-plugin-security, gosec, bandit, semgrep-in-CI) that re-cover CodeQL.
- Vulnerable code → CodeQL only. Check Settings → Code security: if GitHub default-setup CodeQL is ON, turn it OFF when committing
codeql.yml (advanced setup) — running both scans the same code twice.
- Vulnerable dependencies → exactly ONE of dependency-review OR a native vuln-gate (osv-scanner/cargo-deny/npm audit). If the repo already has a vuln-gate (e.g. Shipwright's
SWR-SEC-VULN-GATE), do NOT also add dependency-review.
- Secrets → GitHub secret scanning + push protection (platform, zero CI minutes). Remove any trufflehog/gitleaks CI jobs the platform feature makes redundant.
- Dependency review: if the repo has NO native vuln-gate, ensure
ci.yml has the security job from templates/.github/workflows/ci.yml (actions/dependency-review-action, fail-on-severity: high). Skip if a vuln-gate already covers deps, or if there are no manifests.
- Secret scanning + push protection: enable via the
gh api -X PATCH ... security_and_analysis command in templates/.github/common-repo-settings.md. If gh was skipped or the repo is private without GHAS, record for the user.
-
3g-pol. Security policy (spec [GITHUB-SECURITY-POLICY]). Every repo needs one.
- Write
SECURITY.md at the repo root (or .github/SECURITY.md) from templates/SECURITY.md. Fill every placeholder ({{OWNER}}, {{REPO}}, {{SECURITY_CONTACT_EMAIL}}) and set the supported-versions table to the repo's real release lines. Keep the GitHub doc URLs.
- Enable private vulnerability reporting:
gh api -X PUT repos/$REPO/private-vulnerability-reporting. If gh was skipped or the repo is private without GHAS, record for the user.
-
3h. Agent instruction files. Per spec [AGENT] / [AGENT-TEMPLATE] / [AGENT-POINTERS].
The canonical file MUST be fully customised. Fill every placeholder, strip every language/tool/framework section that does not apply, fill the project overview and architecture section with real content. The test: ZERO references to languages or tools the repo does not use. Keep the Duplication — Deslop section iff the repo is in a Deslop-supported language (Rust, C#, Dart, Python) per [CI-DESLOP]; delete it otherwise.
- Write the canonical file. Default to the generic
AGENTS.md for every agent, Claude included ([DESIGN] 5a, [AGENT-PLACEMENT]) — unless a pre-existing canonical CLAUDE.md was detected, in which case merge into it. (Copilot's own file is .github/copilot-instructions.md.)
- Disable auto-memory per [AGENT-AUTOMEMORY]: for Claude Code, ensure a committed
.claude/settings.local.json contains "autoMemoryEnabled": false (create from templates/.claude/settings.local.json, or merge the key into an existing file — never overwrite existing keys; force-committed per [GITIGNORE-RULES]); for other agents disable the equivalent per [AGENT-DOCS].
- Every other agent instruction file becomes a trivial pointer to the canonical file. See spec [AGENT-POINTERS]. Strip existing content from non-canonical files — no headings, no preamble, no leftover rules — leave only the marker line and
@<canonical_file> redirect. Use templates/CLAUDE.md as the pointer template.
- Place skills from
templates/skills/ into the target agent's native directory per spec [SKILL-PLACEMENT]. Apply spec [MODES-CUSTOMIZE]:
- Language-customizable skills (
code-dedup, ci-prep, upgrade-packages): strip irrelevant language sections, fill placeholders.
- Content-preserving skills (
website-audit, spec-check, submit-pr, any skill without multi-language examples): copy the step-by-step procedure verbatim. Add repo-specific context if useful, but never drop/merge/summarize/rewrite/gut steps. Diff source vs output — the only differences should be repo-specific additions.
If an existing canonical file has substantial custom content, merge into it instead of overwriting. Result must read as a coherent document for this repo, not a generic template with the name swapped in.
-
3h-web. Website theme + upgrade (only if the repo has a website). Per spec [WEB-TECHDOC] / [WEB-UPGRADE]:
- Dev-tool/docs sites MUST use
eleventy-plugin-techdoc on Eleventy 3.x. If on another theme/SSG, flag for migration.
- Always upgrade
eleventy-plugin-techdoc (and @11ty/eleventy) to latest, update the lockfile, and rebuild.
- Then run the
website-audit skill and record the result in the Step 5 report.
-
3i. VS Code title bar colorization (only if .vscode/ or .vscode/settings.json exists).
- Find the project's primary brand color. Check in order:
designsystem//design-system//design/, CSS custom properties (--color-*, --brand-*, --primary) in website//site//docs/, tailwind.config.* theme, theme.*/colors.* files, README color scheme.
- Extract the dominant accent (not white/black/grey).
- Merge into
.vscode/settings.json:
{
"workbench.colorCustomizations": {
"titleBar.activeBackground": "<primary-brand-color>",
"titleBar.activeForeground": "<contrasting-text-color>",
"titleBar.inactiveBackground": "<darker-shade-of-primary>",
"titleBar.inactiveForeground": "<contrasting-text-color-with-opacity>"
}
}
Inactive background = slightly darker/desaturated primary. Inactive foreground = active foreground with ~80% opacity (append cc to hex).
- If no brand colors can be found anywhere, skip and note in the report. Do NOT invent colors.
-
3j. VS Code recommended extensions. Merge templates/vscode/universal.extensions.json into .vscode/extensions.json. If the repo has a web API (REST/GraphQL/HTTP server), also merge webapi.extensions.json. De-duplicate the recommendations array. Create .vscode/ if absent.
Merging multiple files into one is a valid outcome and overrides the no-delete default.