From rubio-standards
Audits this repo against Rubio-Enterprises standards. Use when the user asks to check standards, run a compliance audit, or sync a repo to org conventions. For repos not yet onboarded (no `.copier-answers.yml`), use `/onboard-repo` first.
How this skill is triggered — by the user, by Claude, or both
Slash command
/rubio-standards:audit-standardsThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
!`bash ${CLAUDE_PLUGIN_ROOT}/skills/audit-standards/scripts/detect-stack.sh`
data/managed-content-drift.ymlscripts/check-managed-content.shscripts/check-md-carrier-missing-keys.shscripts/check-md-carrier-superset.shscripts/check-md-gitignore-claude-denyblock.shscripts/check-pins.shscripts/check-rp-manifest-drift.shscripts/detect-stack.shscripts/fix-md-carrier-superset.shscripts/fix-rp-manifest-drift.sh!bash ${CLAUDE_PLUGIN_ROOT}/skills/audit-standards/scripts/detect-stack.sh
Read these files in order:
If detect-stack.sh returns multiple stacks (e.g., typescript python), read each ${CLAUDE_PLUGIN_ROOT}/spec/<stack>.md in order before reporting. If it returns unknown, read ${CLAUDE_PLUGIN_ROOT}/spec/unknown.md and note in the audit report that no stack-specific rules apply.
detect-stack.sh is archetype-aware: when .copier-answers.yml declares archetype: content or archetype: bare it returns that value (these are toolchain-free, so the manifest probe would otherwise mis-route them to unknown.md), and you read ${CLAUDE_PLUGIN_ROOT}/spec/content.md (resp. spec/bare.md). The content archetype (docs / marketplace, no language toolchain) is CI-audited but exempt from TOOL-VERSIONS-PRESENT and the §9 SessionStart / cloud SCRIPT carriers; check.sh still enforces CONTENT-NO-TOOLCHAIN (no language manifest when archetype=content) and keeps CLAUDE-SETTINGS-PLUGIN-BLOCK at err-tier for it. See spec/content.md (incl. its "Distinct from bare" table).
${CLAUDE_PLUGIN_ROOT}/template/
${CLAUDE_PLUGIN_ROOT}/.claude-plugin/plugin.json and print a single line as the very first line of your reply (before any other prose, headings, or tool-call narration): rubio-standards plugin v<version> where <version> is the version field from that file. This lets the user verify at a glance which cached plugin version is actually running — different from any tag the user thinks is current. Do not skip this step; do not embed it later in the output.${CLAUDE_PLUGIN_ROOT}/scripts/check.sh for the deterministic baseline. Parse output.bash ${CLAUDE_PLUGIN_ROOT}/skills/audit-standards/scripts/check-pins.sh for pin-currency checks (WF-02 reusable_workflow_sha drift, WF-03 template tag drift). This helper is the canonical source for those two rows — emit them verbatim into the report (mapping PASS/FAIL/WARN/SKIP to the report's Status column). Do NOT downgrade a FAIL from this script to INFO; the helper requires network and exits cleanly with WARN when offline, so a FAIL row means the comparison succeeded and the consumer is genuinely behind. Remediation for either FAIL is the manual copier update --trust --vcs-ref=<your-_commit-value> command already embedded in the row text — the skill does NOT run that command automatically in fix mode (see step 5 for why).2c. Run bash ${CLAUDE_PLUGIN_ROOT}/skills/audit-standards/scripts/check-managed-content.sh for managed-content drift checks (the MD-NN and RP-NN rule classes). This helper covers files listed in copier.yml::_skip_if_exists that copier update cannot propagate through (consumer-owned by design), the secondary case where the floating template/v1 tag shares a SHA with the versioned target tag so copier update reports "no changes" even when there are drift-able floor edits, AND release-please manifest invariants (the RP-NN rule class — see RELEASES.md "Release-please bootstrap on a tagged repo" for the canonical onboarding step that RP-01 enforces). The helper reads the YAML rules table at ${CLAUDE_PLUGIN_ROOT}/skills/audit-standards/data/managed-content-drift.yml, runs each rule's check: block against the consumer's working tree, and emits one PASS/FAIL/SKIP/WARN row per rule in the same shape as check-pins.sh. Emit rows verbatim into the audit report. Like check-pins.sh, missing dependencies (yq, jq) or a missing/malformed rules file degrade to a single SKIP row rather than crashing. A WARN row from this helper means the rule's check tool (e.g. jq for check.kind: jq) could not evaluate the target file — typically because the file contains JSONC syntax the line-comment strip does not handle (block comments /* ... */ or other JSON5 features), or an external-script rule's helper exited with an unexpected status. This is a file-format / tool-error issue, not drift; surface the row verbatim and hand-inspect, but do NOT treat it as a remediation-eligible FAIL in step 5. CI parity: the org-required audit.yml runs this SAME script with CHECK_MANAGED_CONTENT_STRICT=1, which promotes any MD-NN/RP-NN FAIL from reporter-mode (always exit 0) to a BLOCKING gate (exit 1) — so a FAIL here hard-fails the consumer's required check even though the local default invocation exits 0. Run it with CHECK_MANAGED_CONTENT_STRICT=1 for a true pass/fail parity check.
Adding a new managed-content rule is a single-row YAML edit — append to ${CLAUDE_PLUGIN_ROOT}/skills/audit-standards/data/managed-content-drift.yml with a new MD-NN or RP-NN ID, no script changes needed (for jq / grep-absent kinds; external-script adds a small helper under scripts/). The schema is documented in the top-of-file comment of that YAML.
Rule shape:
- id: MD-NN | RP-NN
file: <repo-relative path the rule inspects>
rule: <one-sentence floor invariant; reproduced in the FAIL row>
since: template/vX.Y.Z | audit/vX.Y.Z # informational — the tag at which this floor became normative
check:
kind: jq | grep-absent | external-script
# jq: expression must evaluate to true for PASS
expression: '<jq expression>'
# grep-absent: FAIL when pattern matches any line
pattern: '<basic regex>'
# external-script: run helper script; exit 0=PASS, 1=FAIL, 2=SKIP, other=WARN
script: 'skills/audit-standards/scripts/check-<rule-id>.sh'
fix:
kind: json-merge | sed-delete-line | external-script
# json-merge: jq merge-object filter applied via `. * (<patch>)`
patch: '<jq object expression>'
# sed-delete-line: deletes every line matching pattern
pattern: '<basic regex>'
# external-script: run helper script; exit 0=applied, non-zero=failed
script: 'skills/audit-standards/scripts/fix-<rule-id>.sh'
Rule ID classes: MD-NN for managed-content drift (copier.yml::_skip_if_exists files that copier update can't propagate). RP-NN for release-please manifest invariants — these use external-script because they need to query git tags. See RELEASES.md "Release-please bootstrap on a tagged repo" for the operator playbook RP-01 enforces.
To add a new check.kind or fix.kind (e.g., a Python toml-edit shape, a yaml deep-merge shape), extend scripts/check-managed-content.sh and the fix-mode dispatcher in step 5 below — keep those two the only places that know per-kind semantics. external-script is the escape hatch for one-off checks (git-tag comparison, GitHub API queries) that don't fit a generic kind.
2d. JSON Schema validation (check-jsonschema) — CI parity, language-archetype-gated. The org-required audit.yml runs intra-document schema rules that check.sh does NOT. Run them locally so a green local audit matches CI:
if [ -f package.json ]; then
check-jsonschema --schemafile ${CLAUDE_PLUGIN_ROOT}/schemas/package-json.schema.json package.json
fi
if [ -f pyproject.toml ]; then
check-jsonschema --schemafile ${CLAUDE_PLUGIN_ROOT}/schemas/pyproject.schema.json pyproject.toml
fi
Emit a JSON-01 (package.json) / JSON-02 (pyproject.toml) PASS/FAIL row per file present. The package-json schema requires scripts.lint + scripts.test, scripts.e2e iff @playwright/test is a dependency, and bin iff rubio.is_mcp_server is true; the pyproject schema requires the dev dependency-group + pyright. This step is a NO-OP for content / bare repos (no language manifest), so it never produces a row there. A schema FAIL is a CI-BLOCKING gate even when check.sh is green — the single most common "local-green / CI-red" surprise. Surface FAILs but do NOT auto-apply (see step 5).
2e. Conftest policy bundle (conftest) — CI parity, relational/cross-file rules. CI also runs the Rego policy bundle over language config files; mirror it:
inputs=()
for f in biome.json ruff.toml .golangci.yml package.json deno.json deno.jsonc; do
[ -f "$f" ] && inputs+=("$f")
done
if [ ${#inputs[@]} -gt 0 ]; then
conftest test --combine --policy ${CLAUDE_PLUGIN_ROOT}/policy --data ${CLAUDE_PLUGIN_ROOT}/data/base "${inputs[@]}"
fi
Emit a POLICY-01 PASS/FAIL row. These are the TypeScript scripts.lint Biome-contract rule (policy/typescript/overrides.rego) and the Deno Layer-C rules (policy/deno/overrides.rego). NO-OP for content / bare. Like the schema gate, any FAIL is CI-blocking and check.sh does not surface it; surface but do NOT auto-apply (see step 5). CI additionally runs npm ci --dry-run --ignore-scripts per checked-in package-lock.json as a manifest↔lockfile drift gate (distinct from check.sh's presence-only NPM-LOCKFILE-PRESENT) — reproduce it locally for npm repos before pushing.
2b. Fork-without-standards short-circuit (FORK-01). Before walking the spec files, detect whether this consumer repo is a fork that has not yet had Rubio standards rendered onto it. The CI (org-required Rubio-Enterprises/.github/.github/workflows/audit.yml) runs check.sh against the consumer's working tree with no fork-awareness — is_fork: true only changes the audit's behavior after copier has rendered it into .copier-answers.yml. If the audit walks the spec files in this state, it will emit ~10 N/A rows ("intentional: fork carve-out") for missing commitlint.config.cjs, lefthook.yml, .github/workflows/standards.yml, etc. — but CI will HARD-FAIL on those same items. The N/As are misleading; collapse them to one actionable FAIL.
Detection (two signals; require both (A OR B) AND C to fire):
A. gh repo view --json parent --jq '.parent.nameWithOwner' 2>/dev/null returns a non-empty value (the repo is a GitHub-side fork).
B. A git remote named upstream exists AND its URL differs from origin:
up="$(git remote get-url upstream 2>/dev/null || true)"
orig="$(git remote get-url origin 2>/dev/null || true)"
[ -n "$up" ] && [ "$up" != "$orig" ]
C. .copier-answers.yml is ABSENT (the standards template has never been rendered onto this repo). Check with [ ! -f .copier-answers.yml ].
Branching:
.copier-answers.yml EXISTS and contains is_fork: true: continue with the normal audit. The template's _skip_if_exists directives and {% if not is_fork %} gating in copier.yml (line 262) already produce correct PASS/FAIL outcomes for fork repos — no override needed here. Do NOT emit FORK-01 in this case.
.copier-answers.yml is MISSING AND (signal A OR signal B) is positive: emit a SINGLE FAIL row and short-circuit the rest of the audit (skip step 3 entirely; jump directly to step 4 with the table containing only this one row, then step 4b with FORK-01 in ## Remaining action items). The row:
| ID | Source | Rule | Status | Evidence |
|---|---|---|---|---|
| FORK-01 | audit-standards skill | Standards must be applied via copier before audit can produce meaningful results | FAIL | Repo appears to be a fork (gh parent: <value-from-signal-A-or-"upstream-remote">) but .copier-answers.yml is absent. Standards have not been applied. Run: copier copy --trust --vcs-ref=template/v1 --data is_fork=true --data project_name=<name> --data archetype=<archetype> --data default_branch=<branch> --data uses_release_please=false --data tier=experimental gh:Rubio-Enterprises/standards . See also tools-fork-maintenance SKILL.md step 13 for the same command in the broader fork-setup playbook. |
Substitute <name>, <archetype>, <branch> with reasonable defaults inferred from the consumer repo where possible (e.g., <name> from basename "$(git rev-parse --show-toplevel)", <branch> from git symbolic-ref --short refs/remotes/origin/HEAD | sed 's@^origin/@@'); leave <archetype> as a literal placeholder if you cannot infer it — the operator must pick from the values defined in copier.yml.
Neither signal fires (not a fork, OR .copier-answers.yml already present without is_fork: true): continue with the normal audit as today. Do NOT emit FORK-01.
FORK-01 is mutually exclusive with the normal audit table: when it fires, no other rows are emitted (the cascade of dependent N/As is exactly what FORK-01 replaces). When it does not fire, FORK-01 itself is not in the report at all.
Inspect rules from the spec files listed in ## Standards spec above (already read once at session start — do NOT re-read here) for any PASS / FAIL / N/A not covered by check.sh or check-pins.sh. Report each with file:line evidence. Skip this step entirely if FORK-01 fired in step 2b — the consumer needs to render standards first; there is nothing meaningful to audit until then.
Print the audit report directly in the conversation as a markdown table (header row + one row per rule, columns: ID, Source, Rule, Status, Evidence). Do NOT write the report to any file — chat is the sole output channel for the report. The conversation transcript is the durable record; if the user wants to persist it they can copy it themselves.
4b. After the audit table, ALWAYS emit a ## Remaining action items section that lists every FAIL row from the audit (whether or not fix mode is active, whether or not the FAIL was auto-remediated this run). Format as a numbered list. For each FAIL row include:
Distinct from the table, this is a checklist the operator can act on without re-reading the table. Do NOT skip this section when fix mode auto-remediated everything — instead, list the remediated rules with status "auto-applied in this PR (no further action required)" so the operator sees the full picture.
If COPIER-ANSWERS-PRESENT is a FAIL (no .copier-answers.yml), add this as
the FIRST item in ## Remaining action items:
COPIER-ANSWERS-PRESENT — This repo has not been onboarded to Rubio-Enterprises standards. Run
/onboard-repoto scaffold it, then re-run/audit-standards. Most other FAILs in this audit will resolve automatically once onboarding is complete.
Example shape:
## Remaining action items
1. **WF-02 (reusable_workflow_sha drift)** — auto-applied in this PR via
sed-replace on `.copier-answers.yml` + 5 `uses:` lines in
`.github/workflows/standards.yml`. **No further action required.**
2. **WF-03 (template tag drift)** — pinned `template/v1.4.4` is behind
latest `template/v1.6.0`. **Run manually**:
copier update --trust --vcs-ref=template/v1.6.0
...then commit and push the resulting diff. Cannot be auto-remediated
because copier's interactive prompts (PEP-440 downgrade comparisons on
floating tags, new questions added since last update) make
non-interactive invocation unreliable.
3. **ALL-02 (missing commitlint.config.cjs)** — auto-fetched from
`${CLAUDE_PLUGIN_ROOT}/template/commitlint.config.cjs` in this PR.
**No further action required.**
If the audit has zero FAILs, emit ## Remaining action items with a single line: None — no FAILs detected. Do not skip the section entirely; its presence reassures the operator that the audit completed.
This section is always emitted, regardless of whether fix mode is active. Even a plain /audit-standards (no fix) must produce it so the operator sees manual action items even when nothing is being auto-fixed.
FORK-01 special case. When step 2b fired FORK-01, the audit table contains only that single row and ## Remaining action items contains exactly one numbered item: the FORK-01 entry with the copier copy command from the row's Evidence column reproduced verbatim, ready to paste. Do NOT include fix-mode auto-remediation language for FORK-01 — fix mode cannot run copier copy on the operator's behalf (the same non-interactive-copier rationale in step 5 applies; the command needs operator-supplied project_name/archetype answers). Example shape:
## Remaining action items
1. **FORK-01 (standards not applied)** — repo appears to be a fork but
`.copier-answers.yml` is absent. **Run manually** from the repo root:
copier copy --trust --vcs-ref=template/v1 \
--data is_fork=true \
--data project_name=<name> \
--data archetype=<archetype> \
--data default_branch=<branch> \
--data uses_release_please=false \
--data tier=experimental \
gh:Rubio-Enterprises/standards .
See also `tools-fork-maintenance` SKILL.md step 13 for the same
command in the broader fork-setup playbook.
Pick `<archetype>` from `copier.yml` (e.g., `ts-cli`, `py-library`;
`content` for a docs/marketplace repo — CI-audited, no language
toolchain, see `${CLAUDE_PLUGIN_ROOT}/spec/content.md`; or `bare`
for a hands-off registry not enrolled in standards CI — see
`${CLAUDE_PLUGIN_ROOT}/spec/bare.md`). Commit the rendered files,
then re-run `/audit-standards` — the normal rule walk will produce
meaningful PASS/FAIL rows once the standards have landed.
If $ARGUMENTS includes fix AND the audit report contains at least one FAIL row from EITHER check.sh OR check-pins.sh, apply low-risk remediations per the per-FAIL minimal patches below (Pattern E — audit-driven minimal patches, called by Plan 2 Task 14a/b/c migration flow). If no FAILs exist (or only WARN/INFO/N/A rows), skip fix mode entirely — there is nothing to remediate. The skill does NOT run copier update automatically: the non-interactive copier update path was tried in v1.2.x–v1.3.0 and abandoned in v1.3.1 after repeated smoke-test failures (the --overwrite flag is missing from older copier releases like 9.12.0; copier's PEP-440 "downgrade" comparison against the floating template/v1 tag fails non-deterministically; even --defaults does not reliably prevent interactive hangs past 30s on consumer repos that have accumulated new questions since their last update). Greenfield repos (no .copier-answers.yml) are redirected to /onboard-repo — see below.
Greenfield (no .copier-answers.yml): This repo has not been onboarded to
standards. The audit cannot apply meaningful fixes without the template having
been rendered first. Direct the user to run /onboard-repo to scaffold the
repo, then re-run /audit-standards fix for ongoing compliance.
Do NOT attempt copier copy in fix mode — the template rendering is only half
the onboarding process. The per-archetype manifest bootstrap (package.json
scripts, devDeps, pyproject.toml dependency groups, uv lock) lives exclusively
in /onboard-repo step 4 and is required for a working project.
Per-FAIL minimal patches (the canonical fix-mode remediation):
For each FAIL row in the audit report (whether emitted by check.sh or
check-pins.sh), apply the smallest patch that resolves it:
From check-pins.sh:
WF-02 (reusable_workflow_sha drift) — the FAIL row text contains both
the consumer's pinned SHA and the current plugin default SHA. Apply the
bump in two places (both with sed-style targeted replacement, NOT a
full-file rewrite — preserve every other line):
.copier-answers.yml: replace the line
reusable_workflow_sha: <old-sha> with
reusable_workflow_sha: <new-sha>..github/workflows/standards.yml: replace every occurrence of
<old-sha> with <new-sha> (typically 4-5 uses: ...@<sha> lines —
audit / secret-scan ×2 / e2e / lint-hooks; the exact set depends on
which jobs the consumer renders for their archetype).
Both SHAs come directly from the WF-02 FAIL row text emitted by
check-pins.sh (they're embedded in pinned: X, current plugin default: Y
— parse them out, don't re-derive them).WF-03 (template tag drift) — the FAIL row text contains both the
consumer's pinned versioned tag (e.g., template/v1.4.4) and the latest
tag (e.g., template/v1.5.1). Apply in one place:
.copier-answers.yml: replace the line _commit: <old-tag> with
_commit: <new-tag>.
Note: WF-03 only emits FAIL when the consumer pins a versioned tag like
template/v1.4.4 that's behind. Consumers on the floating template/v1
tag get a PASS row from check-pins.sh instead (floating tags always
resolve to the latest on the next copier update — no drift possible).From check-managed-content.sh (the MD-NN and RP-NN rule classes):
FAIL: MD-NN ... OR FAIL: RP-NN ... row's ID — both classes use the same YAML table and the same fix-dispatch logic. Look up the rule in ${CLAUDE_PLUGIN_ROOT}/skills/audit-standards/data/managed-content-drift.yml by id. Apply the rule's fix: block to its file:. Each fix.kind has a fixed remediation shape:
fix.kind: json-merge - apply the row's patch: jq as a merge-object: jq '. * (<patch>)' <file> > <file>.tmp && mv <file>.tmp <file>. ADDS the keys a floor requires while preserving every other consumer-owned key. Apply the row's patch verbatim; do NOT full-file-rewrite or add extra keys.fix.kind: json-del — apply the row's expression: (a jq del(...) filter) as jq '<expression>' <file> > <file>.tmp && mv <file>.tmp <file>. Removes ONLY the named keys, preserves every sibling, and always re-emits valid JSON. The committed-.claude/settings.json REMOVE-LIST rule (MD-07) uses this kind to subtract the banned enabledPlugins keys (security-guidance@claude-plugins-official, superpowers@claude-plugins-official) without disturbing the required rubio-standards@rubio key or the legitimate superpowers@superpowers-dev bundle — a text line-delete would orphan a trailing comma or wipe a co-located key on the canonical single-line enabledPlugins..claude/settings.json SUPERSET rule (MD-05) and silent-drop loud-FAIL rule (MD-08) use fix.kind: external-script (NOT inline jq). MD-05 asserts the carrier is a SUPERSET of the canonical org plugin set (all org marketplaces + plugins, read dynamically from template/.claude/org-plugins.generated.json — the same vendored SSOT the carrier is rendered from, so no hardcoded plugin list drifts); its fix json-merges the ENTIRE canonical block in, org floor winning on collision while consumer permissions/hooks/env + consumer-added extra plugins survive local-wins. MD-08 fires (err-tier) when .claude/settings.json exists but declares NO extraKnownMarketplaces/enabledPlugins at all — the silent _skip_if_exists copier-skip outcome (0 org plugins reach cloud) — and shares MD-05's superset fix script. An inline jq check cannot serve these because the jq kind only reads the ONE consumer file:, never the second canonical file. These rules REPLACE the former rubio-anchor-only MD-05/MD-06 (which PASSed on an incomplete carrier, hiding the full-set gap — SSOT-PLAN.md §12.3/§12.4).fix.kind: sed-delete-line — sed -i.bak -E '/<pattern>/d' <file> && mv <file>.bak /tmp/ (the .bak move is portable across BSD sed on macOS and GNU sed on Linux; both ship -i semantics but BSD requires a backup-suffix argument). Removes every line where the pattern matches.fix.kind: external-script — invoke ${CLAUDE_PLUGIN_ROOT}/<rule.fix.script> from the consumer repo's cwd. Exit 0 means the fix was applied; non-zero means the helper failed (surface stderr and leave the FAIL row in ## Remaining action items for manual handling). Used by RP-NN rules where the fix needs to query git tags (see scripts/fix-rp-manifest-drift.sh) AND by the MD-05/MD-08 carrier-superset rules where the check/fix must read the canonical template/.claude/org-plugins.generated.json (a second file the inline jq kind cannot reach) — see scripts/check-md-carrier-superset.sh / scripts/fix-md-carrier-superset.sh.Re-run bash ${CLAUDE_PLUGIN_ROOT}/skills/audit-standards/scripts/check-managed-content.sh after applying to confirm the FAIL flipped to PASS. The same YAML drives both the check and the fix — there is no separate fix-mode rule table. RP-NN rows route through the same dispatcher as MD-NN; the only difference is the external-script fix kind invokes a helper instead of running inline jq / sed.
From check.sh:
commitlint.config.cjs / lefthook.yml / .editorconfig → re-fetch from ${CLAUDE_PLUGIN_ROOT}/template/ (the template ships .cjs, NOT .js; check.sh errors on .js when package.json declares "type": "module").package.json script → npm pkg set scripts.<name>="...".pyproject.toml dependency-groups.dev.pyright → tomlkit patch (NOT a full file rewrite — preserve the consumer's other deps).cog.toml, .husky/, .pre-commit-config.yaml) → git rm / rm -rf.git mv CLAUDE.md AGENTS.md && ln -s AGENTS.md CLAUDE.md && git add CLAUDE.md. Low-risk — content is preserved verbatim, just renamed; the symlink is the canonical template shape.rm CLAUDE.md && ln -s AGENTS.md CLAUDE.md && git add CLAUDE.md. Low-risk — the wrong-target symlink is dead reference; AGENTS.md is the documented canonical target.## Remaining action items as a manual task: the operator must (a) read both files, (b) refactor AGENTS.md to follow the org's CLAUDE.md best practices and incorporate any unique content from CLAUDE.md, (c) rm CLAUDE.md && ln -s AGENTS.md CLAUDE.md. Auto-merging would either lose content or fabricate it — both worse than a human merge.SessionStart-hook standardization (HOOK-01) - bespoke prose, NOT an MD-NN row (multi-file git mv). The floor is a contract on hook CONTENT, not a path (abort-proof || true/exit 0; GH_TOKEN/GITHUB_TOKEN; PATH via $CLAUDE_ENV_FILE; BASH_SOURCE root). Fix mode accepts ANY conforming flavour (mise/dockerd/deps-install + npx playwright install chromium/composite) and patches a non-conforming hook IN PLACE. NEVER force mise-only or blanket-git mv to scripts/claude-session-start.sh (mislabels dockerd, drops the deps-install playwright-chromium install, clobbers a security block); NEVER touch a PreToolUse-only security hook (todoist-classify guards). Rename only on an explicit MOVE FAIL with opt-in: git mv <old> scripts/claude-session-start.sh + repoint the matching .claude/settings.json SessionStart[].hooks[].command (preserve every other key) + git add -A. ROLLBACK (mandatory): on any failure, git mv scripts/claude-session-start.sh <old> then git restore --staged --worktree .claude/settings.json <old> scripts/claude-session-start.sh; never leave a half-renamed state.
For each schema/Conftest violation, surface in the audit report with the failing rule ID and a suggested patch — do NOT auto-apply (these are higher-risk). User reviews the PR.
Then: branch, commit, push, PR. Read the consumer's default branch from .copier-answers.yml::default_branch (fallback: git symbolic-ref --short refs/remotes/origin/HEAD | sed 's@^origin/@@'). Create branch chore/standards-audit-$(date -u +%Y-%m-%d) based on origin/<default-branch> (NOT current HEAD — the operator may be on a feature branch, and basing on HEAD would inherit unrelated drift that cancels the audit diff). Then git add -A, commit with message chore: standards audit YYYY-MM-DD, and push.
PR body emission — use a tempfile, not a heredoc. Use the Write tool to author the PR body as a markdown file at /tmp/audit-pr-body-$(date -u +%Y-%m-%d).md (or equivalent path OUTSIDE the consumer repo — if the file lands inside the repo it shows up in git status and contaminates the next audit). The Write tool handles markdown formatting natively, so there are no shell-quoting concerns with backticks, dollar signs, or fenced code blocks.
Write the PR body content with LONG LINES preserved — do NOT hard-wrap. SHAs, fully-qualified workflow paths, file paths, and other identifiers must remain on a single line. Markdown collapses single newlines inside paragraphs, but the raw file should not split tokens. If a paragraph is long, let it be long; if you want visual separation, use blank lines (paragraph breaks) instead of mid-line wraps. Backticks for inline code containing identifiers protect them from auto-wrap in some renderers but not from the literal newlines you might insert yourself. The model's default markdown-authoring style wraps at ~80 chars — explicitly override that here.
Then invoke:
gh pr create --base <default-branch> --head <audit-branch> --title "chore: standards audit YYYY-MM-DD" --body-file /tmp/audit-pr-body-<date>.md
The explicit --base flag is required — origin may have multiple fork remotes, leaving the base ambiguous otherwise. After gh pr create succeeds, delete the tempfile.
Do NOT use gh pr create --body-file - <<'EOF' ... EOF. The model habitually indents inside multi-line shell heredocs, and with <<'EOF' (not <<-'EOF') bash treats leading whitespace literally — any indentation leaks into the rendered PR body as ## Heading-style markdown. The tempfile path sidesteps this entirely. The wording-only "write flush to column 0" guidance shipped in v1.3.0 did not stick in practice.
PR body contents. A remediation log (one bullet per fix applied) — the full audit report stays in chat per step 4; the PR body only describes what was changed. After the remediation log, append a ## Remaining action items section that mirrors the chat output from step 4b: one entry per FAIL row (auto-applied or manual), same shape and wording as the chat version, so the PR reviewer sees the same operator-facing summary that landed in the chat. After that, append a section surfacing the manual broader-sync option:
## Optional: broader sync via copier update
This audit applied surgical per-FAIL patches. For a broader sync that picks
up template-side changes the audit did not flag (e.g., gitignore fragment
updates, _tasks-hook rerun), the operator can manually run:
copier update --trust --vcs-ref=<consumer-commit>
...where `<consumer-commit>` is the `_commit` value from this repo's
`.copier-answers.yml` (here: `<actual-value-from-consumer>`). Run this
from the consumer's default branch after this PR merges, in a separate
commit/PR. The skill does NOT attempt this automatically because copier's
interactive prompts (new questions, PEP-440 downgrade resolution on
floating tags, `--overwrite` flag availability varying by copier version)
make non-interactive invocation unreliable.
Read the actual _commit value from .copier-answers.yml and inline it into the <actual-value-from-consumer> placeholder so the operator can copy-paste the command without further lookup. The user reviews the PR rather than the working tree.
NEVER:
_archive/, _old/, _reference/git reset --hard, force-push, or rm -rf on any path NOT explicitly named in the fix-mode banned-files remediation above (.husky/ etc. are authorized; nothing else is)Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub rubio-enterprises/standards --plugin rubio-standards