Apcore Skills — Audit
⚡ Execution Entry Point (READ THIS FIRST)
When this skill is loaded, you MUST immediately begin executing the Workflow below — do not wait, do not summarize, do not ask "what should I do now". Skills are operational manuals, not reference documents. Read Step 0 (Ecosystem Discovery), then Step 1 (Parse Arguments), then Step 2 (Execute Audit Dimensions), etc., until the workflow completes or you reach an AskUserQuestion checkpoint.
If the harness shows you Successfully loaded skill · N tools allowed, that message means the SKILL.md content was injected into your context — it does NOT mean the skill has run. Skills do not "run" autonomously; you run them by executing the Detailed Steps below.
If you find yourself about to say "the skill didn't produce output", "skill 仍未输出", "falling back to manual audit", "回退到手动 audit", or anything similar, STOP. You have misunderstood how skills work. Go directly to Step 0 and start executing.
The first user-visible action of this skill should be either (a) the output of Step 0 / Step 1, or (b) an AskUserQuestion if scope detection needs disambiguation. Never an apology, never a fallback, never silence.
Comprehensive consistency audit across all apcore ecosystem repositories.
Iron Law
APPLY EVERY APPLICABLE DIMENSION. EVIDENCE EVERY FINDING. A dimension returning zero findings — with a short note of what was checked — IS a valid result. Quota-filling manufactures false positives; unreachable / speculative findings are noise, not signal.
When to Use
- Before a major release to ensure ecosystem-wide consistency
- After adding a new SDK or integration to verify alignment
- Periodic health check (monthly recommended)
- When suspecting drift between implementations
Command Format
/apcore-skills:audit [--scope core|mcp|integrations|all] [--fix] [--no-deep-chain] [--strict] [--save report.md]
| Flag | Default | Description |
|---|---|---|
--scope |
cwd | Which repo group to audit. If omitted, defaults to the current working directory's repo only. Use --scope all for full ecosystem audit. |
--fix |
off | Auto-fix issues where safe |
--no-deep-chain |
off (D11 runs by default) | Skip D11 (cross-language deep-chain analysis). Use for fast audits where you only need D1–D10 shape-level checks. D11 adds one sub-agent per logical module; disabling saves time on large module sets. |
--strict |
off (lean mode) | Re-enable noise-prone finding classes that are suppressed by default. By default the audit suppresses language-idiom downgrades (defensive-depth, error-class-name-only, async-no-work, constructor-name-idiom, type-wrapping), D2 style-only nits (clippy lints whose suggestion is just allow(...) or rename-for-idiom), and findings tagged [verify-spec-first] (where the audit cannot independently determine which spec interpretation is authoritative). Pass --strict before a release audit when you want the full surface. Real bugs are never suppressed — critical/blocker findings, spec violations, dead code (D9), and API surface gaps (D1) always surface regardless of this flag. |
--save |
off | Save report to file |
Lean vs Strict — when to use which
- Lean (default) — answers "what should I actually fix?". Use during iterative cleanup, when the previous audit already passed lean but warnings keep regenerating, or when triaging a fresh repo to find load-bearing problems. Lean mode is designed to terminate: once real bugs are fixed, lean mode reaches zero warnings and stays there.
- Strict (
--strict) — answers "what is every divergence anyone could possibly notice?". Use before a major release, when chasing API-surface symmetry across SDKs, or when investigating a specific style/idiom decision. Strict mode does NOT necessarily reach zero — some findings stay open as accepted policy (e.g., brand-consistency naming).
Audit Dimensions
The audit covers 11 dimensions, each checking specific aspects. Coverage is unchanged by how a dimension executes — D2, D3, D6, D7, D8 run as one deterministic script (Track A); D1, D4, D5, D9, D10, D11 run as sub-agents (Track B). See §Context Management.
| # | Dimension | Severity Range | Description |
|---|---|---|---|
| D1 | API Surface | critical-warning | Public API alignment across languages |
| D2 | Naming Conventions | critical-warning | File/class/function naming per language rules |
| D3 | Version Sync | critical-info | Version alignment within sync groups |
| D4 | Documentation | warning-info | README, CHANGELOG, docstring, spec ## Contract: coverage |
| D5 | Test Coverage | warning-info | Test file existence and coverage metrics |
| D6 | Dependencies | critical-warning | Dependency versions and compatibility |
| D7 | Configuration | warning-info | APCORE_* settings consistency across integrations |
| D8 | Project Structure | warning-info | File/directory layout per conventions |
| D9 | Bloat & Redundancy | critical-info | Dead exports, duplicate symbols, parallel implementations, LOC growth, unused config, scope creep |
| D10 | Contract Parity (Intent — SHAPE-LEVEL) | critical-warning | Behavioral contract parity — inputs validation, errors raised, side-effect order, return shape, async/thread-safe/pure/idempotent/reentrant properties — catches "same signature, different logic" bugs at the Contract tuple level. Plus: integration consumer-contract check (does this integration USE the core SDK per its current Contract?). |
| D11 | Deep-Chain Parity (Intent — CHAIN-LEVEL) | critical-inconclusive | Cross-language call-graph diff per logical module. Reads all N language implementations' source for the same module side-by-side and finds divergences that shape-level Contract comparison (D10) cannot see: bare-subscript / null-guard gaps, internal methods skipping validation that peers call, missing-registration into maps. Delegates to sync Step 4C — audit surfaces the findings as D11-{seq} entries. Runs when ≥2 same-type repos are in scope. |
D10 vs D11 — why both. D10 extracts a shape (inputs/errors/side_effects tuples) from each repo and diffs the shapes. D11 reads the actual source code across languages and diffs call graphs. They are complementary, not redundant — D10 catches divergences visible in the declared contract; D11 catches divergences that only appear when you read the code. A bug where one language's public method silently skips an internal validation call its peers perform will pass D10 (the declared contract matches) but fail D11 (the call graph shows the skip). Both run by default; disabling either is opt-out.
Severity Levels
| Level | Meaning | Action Required |
|---|---|---|
critical |
Breaking inconsistency — users will hit errors | Must fix before release |
warning |
Non-breaking inconsistency — confusing but functional | Should fix soon |
info |
Cosmetic or minor inconsistency | Nice to fix |
Context Management
Audit execution is split into two tracks, by whether a dimension has a judgment component.
| Track | Dimensions | How it runs | Why |
|---|---|---|---|
| A — mechanical | D2, D3, D6, D7, D8 | One audit-mechanical.py call (Step 2a) |
Naming regexes, version-string comparison, dependency-table diffing, config-default matching, filesystem layout. Inputs fully determine outputs. |
| B — semantic | D1, D4, D5, D9, D10, D11 | Parallel sub-agents (Step 2b) | Cross-language API normalization, doc quality, test execution, reachability reasoning, contract/call-graph parity. Not reducible to static rules. |
The main context ONLY handles:
- Orchestration — determining scope, running the mechanical pass, spawning semantic sub-agents
- Aggregation — collecting structured findings from both tracks
- Reporting — formatting and displaying the consolidated report
Step 2b spawns up to 6 parallel sub-agents (D1, D4, D5, D9, D10, plus the D11 delegation) — down from 11. D11 delegates to sync Step 4C (which itself spawns one sub-agent per logical module under its own orchestrator). D11's progress updates surface in the audit orchestrator's log just like any other dimension. Step 4 spawns one parallel sub-agent per repo for fixes. The main context never reads repo files directly.
Why the split. Every sub-agent is a fresh full context — harness, tool schemas, the ~9 KB Finding Suppression Gate, and its dimension prompt — before it reads a single repo file, and it then runs its own multi-turn tool loop. Collapsing five mechanical dimensions into one script call removes five such contexts per audit run, and removes LLM error from checks that have a single correct answer. The gate is deliberately not applied to Track A: a deterministic checker cannot speculate, pad findings, or misreport a grep it never ran (rationale in shared/scripts/README.md).
Workflow
Step 0 (ecosystem) → Step 1 (parse args) → Step 2 (parallel audits) → Step 3 (report) → Step 3.1 (review-compatible output) → [Step 4 (fix)]
Detailed Steps
Step 0: Ecosystem Discovery
@../shared/ecosystem.md
Step 1: Parse Arguments and Plan Audit
Parse $ARGUMENTS for flags. Recognized flags: --scope, --fix, --no-deep-chain, --strict, --save. Unknown flags must be reported back to the user as an error before any sub-agent is spawned.
Set STRICT_MODE: true if --strict appears anywhere in $ARGUMENTS, else false. Pass this value to the Step 2.5.5 suppression pass and surface it in the Step 3 report header.
1.1 CWD-based Default Scope
If --scope is NOT specified:
- Detect CWD repo name (basename of CWD)
- Look up in discovered ecosystem:
core-sdkrepo → audit this repo + sibling core-sdks in the same sync group, dimensions D1-D3, D5-D6, D8-D10 (D10 needs ≥2 repos in the same group to compare)mcp-bridgerepo → audit this repo + sibling mcp-bridges, dimensions D1-D3, D5-D6, D8-D10integrationrepo → audit this repo, dimensions D2-D10. For D10, auto-pull in the relevant core SDK (matching the integration's language — e.g., django-apcore → apcore-python; nestjs-apcore → apcore-typescript) AND theapcore/doc repo as read-only peers for the Consumer Contract Check (Step 4 of the D10 prompt). Dimensions D2–D9 still apply only to the integration repo itself.protocol/docs-siterepo → audit documentation dimensions (D4) and bloat (D9) for this reposhared-lib/toolingrepo → audit D2 (naming), D4 (docs), D5 (tests), D8 (structure), D9 (bloat) for this repo- CWD not an apcore repo → use
AskUserQuestionto ask: "CWD is not an apcore repo. Which repo do you want to audit?" with options fromrepos[]names + "All repos (full ecosystem audit)"
- Display: "Scope: {repo-name} (from CWD). Use --scope all for full ecosystem audit."
If --scope IS specified: use explicit scope.
1.2 Scope → Repos & Dimensions
| Scope | Repos | Dimensions |
|---|---|---|
core |
Core SDKs + apcore/ doc repo |
D1-D3, D5-D6, D8-D11 (D4 covers apcore/ only) |
mcp |
MCP bridges + apcore-mcp/ doc repo |
D1-D3, D5-D6, D8-D11 (D4 covers apcore-mcp/ only) |
integrations |
Framework integrations + auto-pulled core SDKs (per-integration language) + apcore/ doc repo as read-only peers |
D2-D9 on integration repos; D10 Consumer Contract Check verifies each integration uses its matching core SDK per the core SDK's current Contract. D11 skipped (integrations are single-language; no cross-language chain to diff). |
all |
All repos | All dimensions including D11 on core + mcp groups |
D9 (Bloat & Redundancy) is always included. It applies to every scope and every repo type — it is the apcore ecosystem's primary defense against the additive bias of skill-driven feature work.
D10 (Contract Parity) is included in two modes:
- Parity mode — runs whenever ≥2 same-type repos are in scope (e.g., multiple core SDKs, multiple MCP bridges). Detects intent divergence across language implementations — the bug class where public signatures match but logic/purpose differs (e.g., one SDK validates inputs and the other doesn't; one emits an event and the other doesn't; one is thread-safe and the other isn't).
- Consumer Contract mode — runs whenever at least one
integrationrepo is in scope. The audit auto-pulls the matching core SDK (by language) and theapcore/doc repo as read-only peers, then verifies each integration uses the core SDK per its current Contract (input completeness, error handling, thread-safety assumption, deprecated API usage). Seereferences/dimension-prompts.mdD10 Step 4.
Both modes can run in the same audit invocation — a --scope all run exercises both. When the current scope has only 1 same-type repo AND no integrations, D10 is skipped with an INFO finding.
D11 (Deep-Chain Parity) trigger rule. Runs whenever ≥2 same-type impl repos are in scope AND D10's Parity mode is active (they share the "need peers to compare against" precondition). Skipped with INFO when:
- Only 1 impl repo in scope (no peer)
- Scope is
integrationsonly (single-language chain analysis is code-forge:review's job) - User passes
--no-deep-chain(escape hatch for fast audits)
Display:
Audit scope: {scope} {("(from CWD)" if defaulted)}
Repos: {count} repositories
Dimensions: {list}
Step 2: Execute Audit Dimensions
Two tracks (see §Context Management). Run Step 2a first — it is a single fast call whose findings are already final — then spawn Step 2b's sub-agents. Both tracks feed the same finding list consumed by Step 2.5.
Step 2a: Mechanical Dimensions — D2, D3, D6, D7, D8 (script, no sub-agent)
Run once:
python3 "$CLAUDE_PLUGIN_ROOT/skills/shared/scripts/audit-mechanical.py" --root {ecosystem_root}
Add --repos {name1},{name2} when Step 1 resolved a narrower scope, and --only D3,D8 to run a subset. Output is a single JSON object on stdout.
Consuming the output. For each key in dimensions{}, take findings[] verbatim — the fields (severity, repo, detail, location, fix, evidence) are already the uniform finding format, so they merge directly into the Step 2.5 list with no reformatting. D7 additionally carries config_matrix (inconsistent settings only) and config_matrix_consistent_count for the Step 3 report.
Surface not_covered. Every dimension carries checked[] and not_covered[]. The not_covered entries name markdown rules this run did not evaluate (e.g. D6 vulnerability patterns). Reproduce them in the Step 3 report under the relevant dimension so a fast-path run is never mistaken for full coverage. Do not silently drop them — that would turn a coverage gap into a false clean bill of health.
Fallback (per shared/scripts/README.md fast-path contract). If python3 is unavailable, the script errors, exits non-zero, or the JSON is missing the dimensions key:
- Exit 2 /
ecosystem_root_not_found→ resolve the root perecosystem.md§0.1, then retry once. - Any other failure → spawn ONE
Agent(subagent_type="general-purpose")that executes the D2, D3, D6, D7, and D8 sections ofreferences/dimension-prompts.mdtogether in a single pass, and prepend the Finding Suppression Gate to that prompt (the fallback is an LLM, so it needs the gate). - Never spawn one sub-agent per mechanical dimension — that is the pattern this step exists to remove. Never block the audit on a missing script.
Note the script's findings bypass the gate by construction, not by omission: it emits only what it matched, with file:line, and never pads. Do not "re-verify" its findings with an LLM pass.
Step 2b: Semantic Dimensions — D1, D4, D5, D9, D10 (+ D11)
Spawn these in parallel, one sub-agent each (up to 5 simultaneously). D11 runs as a delegated invocation of sync Step 4C (see Step 2.D11 below) — the delegation itself is one sub-agent from the audit orchestrator's POV, which internally fans out to module-level sub-agents. All dimensions are fully independent.
Sub-agent prompts: Use the dimension-specific prompt templates from references/dimension-prompts.md. Each of D1, D4, D5, D9, D10 has its own section. Prepend the Finding Suppression Gate section to each prompt, then append only that dimension's own section — do not paste the whole file into a sub-agent. Fill in {repo_paths} (and {doc_repo_path} for D10) from the scope determined in Step 1.
D2, D3, D6, D7, D8 sections remain in
dimension-prompts.mdas the authoritative spec and the Step 2a fallback. Do not spawn sub-agents for them on the normal path.
Step 2.D11: Deep-Chain Parity (delegates to sync Step 4C)
Skip conditions (all three must be false for D11 to run):
--no-deep-chainflag present- <2 impl repos in scope (after
--scoperesolution) OR scope isintegrations-only - D10 Parity mode was skipped (same precondition)
Invocation. Spawn a single Agent(subagent_type="general-purpose") tasked with running sync Step 4C internally. The prompt is:
Run /apcore-skills:sync {impl_repo_1},{impl_repo_2},...,{doc_repo} --phase a --internal-check=contract --deep-chain=on --save {ecosystem_root}/audit-d11-{YYYY-MM-DD}.md
Do NOT execute Phase B. Do NOT execute tester. Only Phase A is required, and within Phase A only Step 4C findings are needed — the rest (4.1–4.3, 4A, 4B) may run but will be discarded.
Return the parsed Step 4C findings in this exact format:
D11_MODULES_ANALYZED: {N}
D11_MODULES_FAILED: {N}
D11_MODULES_INCONCLUSIVE: {N}
D11_FINDINGS:
- finding_id: A-D-{seq}
severity: critical|warning|info|inconclusive
type: semantic-divergence|missing-validation|missing-registration|defensive-gap|error-path-divergence|contract-gap|inconclusive
module: {module_name}
symbol: {ClassName.method_name}
summary: {one-line}
evidence: { {lang}: { file, line, snippet } }
recommendation: {text}
verification: static-inference
Result merging. Renumber the incoming A-D-{seq} ids as D11-{seq} to fit audit's dimension-id namespace. Preserve verification: static-inference on every merged finding. The full deep-chain details remain in {audit-d11-{date}.md} — audit's Step 3 report only shows the summary block (see §D11 SUMMARY in Step 3 report template).
Failure modes.
- If the delegated sync invocation fails entirely → emit one CRITICAL finding
[D11-FATAL] sync Step 4C delegation failed — manual run requiredand include the sync invocation's error output in the report. Do NOT pretend D11 passed. - If sync returns ≥1
module_failedormodule_inconclusive→ emit those modules as CRITICAL[D11-{seq}]findings with the reason. A module the skill could not analyze is itself a signal. - If sync returns zero findings AND zero
module_failedANDconfidence_notesis empty → audit treats this as suspicious and emits a WARNING[D11-SUSPECT] deep-chain returned clean but without trace evidence — re-run with higher verbosity.
Step 2.5: Noise-Control Validation (MANDATORY before Step 3)
After Step 2a's script returns and all Step 2b sub-agents return, run this validation pass over the merged findings BEFORE formatting the Step 3 report. The pass applies the Gate 6 (Factual Verifiability) and info-inflation rules from @references/dimension-prompts.md — the gate is enforced at emission time by each sub-agent, but this orchestrator pass is the final line of defense against sub-agents that summarized evidence instead of pasting it.
⚠️ Apply this pass to Track B findings only (D1, D4, D5, D9, D10, D11). Track A findings (D2, D3, D6, D7, D8 from
audit-mechanical.py) are exempt. Gate 6 requires a pasted grep/command output for claims like "unused" or "duplicate"; a deterministic checker instead reports a direct filesystem or parse fact (Missing LICENSE,build config=0.14.0, __version__=0.13.0) that has no grep to paste. Running the gate over them would drop true findings for lacking evidence they never needed. Track A is trustworthy by construction — it cannot claim a search it did not perform.The one Track A finding that carries a trigger-adjacent phrase is D6's cross-repo dependency conflict; it ships its own
evidencefield listing every repo and spec, so it satisfies Gate 6 (b) already. Do not re-verify it with an LLM pass.
Track drop counts per bucket — they surface in the Step 3 report summary. Report Track A and Track B drop counts separately so a zero-drop mechanical track is not read as the gate having failed to run.
2.5.1 D10/D11 cross-dimension deduplication (applies FIRST, before gate 6):
D10 (Contract Parity — shape-level) and D11 (Deep-Chain Parity — chain-level) are designed to be complementary, but by construction they can catch the same bug from two angles — D10 sees a spec-shape divergence and D11 sees the call-graph divergence underlying it. The same root-cause defect then appears as two findings, inflating both the critical and warning counts.
Scan every D11 finding's detail field for cross-reference markers:
- Explicit mapping markers:
(maps to D10-NNN),(maps to D10-\d+),(same as D10-NNN),(see D10-NNN) - Implicit same-root markers: the D11 finding's
symbol+categorypair matches an existing D10 finding'ssymbol+categorypair AND the D11detailis a more specific instance of the D10detail(contains the same defect keyword: same divergent method name, same error type, same canonicalization algorithm, etc.)
Dedup rule — preserve D10, drop D11 dup:
When a D11 finding is identified as a duplicate:
- Find the target D10 finding by id (explicit) or by
(symbol, category)pair (implicit). - Append D11's
locationAND D11's call-graph evidence to the D10 finding'sevidencefield as an additional citation block. Prefix with[from D11-NNN chain-level]so the reviewer can see both angles in one finding. - Drop the D11 finding from the findings list.
- Track the drop as
n_d10_d11_dedup.
Rationale for preserving D10 over D11:
- D10 carries the trust-boundary / contract-specification context that matters for severity calibration (Gate 3 analog)
- D10 findings are more actionable — they point to the spec shape that must align, not just one implementation's call chain
- The merged evidence (D10's shape-level + D11's chain-level) is strictly more useful than either alone
Implicit-match safeguard — do NOT dedup when:
- The two findings have different
severityAND the difference represents a genuine severity disagreement (e.g., D10 flagged as warning, D11 flagged as critical because the chain reveals a reachable trigger D10 missed). In this case, keep the higher-severity finding and drop the lower-severity one — still count as dedup. - The D11 finding is in a separate module from the D10 symbol and the
categoryismissing-registrationordefensive-gap(these are chain-specific defects that D10's shape-level view genuinely cannot see — not duplicates).
Never dedup across dimensions other than D10↔D11. Do not merge D9 dead-export findings into a D10 contract finding even if the root cause is the same stub function — different dimensions serve different consumers and severity audit trails.
Track the count. Surface in the Noise-Control header.
2.5.2 Gate 6 verification scan (applies to critical, warning, info — all severities):
Scan every finding's detail, category, and evidence fields for Gate 6 trigger phrases:
dead export | dead code | unused | unused_internal | unused_config | unused_dep
duplicate | duplicates | parallel_impl | parallel implementation | reimplements | copy of
only used in | only referenced in | never called | never invoked
zero references | zero reads | no callers
reachability | stale | scope creep | stub | noop | no-op
\d+ lines? | exceeds \d+ lines?
When a trigger matches, the finding MUST carry ONE of:
- A search command line (e.g.,
grep -rn,rg, equivalent) PLUS at least one matched-output line (format{file}:{line}:{content}, OR the explicit string0 matches/no matches) - Explicit
{repo}/{path}:{line}citations — for cross-repo claims both sides must be cited; for "only used in" claims the single site MUST be accompanied by a grep proving absence elsewhere - For
dead_export/unused/reachabilityclaims, the evidence must span at leastsrc/ANDtests/(andexamples/if present) — state the scope in evidence, e.g."grep -rn X src/ tests/ examples/"
Findings failing this check are dropped (not downgraded — the factual claim is the load-bearing part of the finding; without verification it has no substance). Track as n_unverified_claim.
2.5.3 Info-level nitpick blocklist (DROP rule, applies to severity=info only):
Info findings are cosmetic by definition — they never reach /code-forge:fix --review (audit Step 3.1 severity map drops info) but they still consume reader attention in the ecosystem report. Drop info findings whose detail matches:
- Pure rename-for-clarity:
rename \w+ to \w+without a named concrete ambiguity - Style swap without named downside:
consider using X instead of Y,prefer X over Y - Pure formatting/casing:
camelCase vs snake_casewithout the symbol crossing a language boundary (cross-language is a D2 issue and belongs at warning, not info) - Packaging / binary name preferences: npm scope, bin name,
*.pyvs*.pyilayout — unless the audit scope explicitly includes packaging - Comment-style:
add a comment explaining Xwhere X is described by the code's own name - File-layout preferences:
move X to utils/,consolidate X under lib/without a concrete coupling-cost demonstration
Track as n_info_nitpick.
2.5.4 Info consolidation (MERGE rule):
Group surviving info findings by (repo, dimension, category). When a group has ≥3 findings, merge them into ONE themed info entry listing every site. Do NOT merge across repos or across dimensions — cross-repo / cross-dimension repetition often signals a real pattern worth preserving as distinct entries.
Themed merge format:
- severity: info
repo: {repo-name}
category: {dimension_category}
detail: "{N} instances of {theme}: {brief theme description}. Sites: {file:line, file:line, ...}"
fix: "{one consolidated fix instruction covering all sites}"
evidence: "{the search command that enumerates the sites, plus the matched-line list}"
Track merged-away entries (original count − 1 per group) as n_info_consolidated.
2.5.5 Strict-mode suppression (lean by default — applies LAST in the drop-pipeline, before 2.5.6 renders totals):
This pass enforces the lean/strict policy that is shared with apcore-skills:sync. Both skills consume the same rule set from a single authoritative document so the semantics never drift.
@../shared/strict-suppression.md
Audit-specific integration notes:
- Apply this pass AFTER 2.5.1 (d10-d11 dedup), 2.5.2 (gate 6), 2.5.3 (info nitpick blocklist), and 2.5.4 (info consolidation), and BEFORE 2.5.6 renders the Noise-Control header (so the rendered total includes
n_strict_suppressed). - For rule (b) style-only naming findings, the audit-specific category is
D2— matchdimension == "D2". - For the hard guarantees, audit-specific dimension ids are: D9 for dead-code, D10 for spec-violation, D11 for chain-level structural divergence, D1 for API surface gap. The shared doc references these by category; map them through audit's
dimension+categoryfields.
2.5.6 Track totals and surface in Step 3 header:
Compute n_total_drops = n_d10_d11_dedup + n_unverified_claim + n_info_nitpick + n_info_consolidated + n_strict_suppressed and include a one-line Noise-Control header in the Step 3 report:
Noise-Control: {n_total_drops} findings suppressed · {n_d10_d11_dedup} d10-d11-deduplicated · {n_unverified_claim} unverified-factual-claim · {n_info_nitpick} info-nitpick · {n_info_consolidated} info-consolidated · {n_strict_suppressed} strict-only ({"hidden — pass --strict to see" if !STRICT_MODE else "shown"})
If n_unverified_claim > 0, also print a per-dimension breakdown so the operator can see which sub-agent prompt is under-producing evidence (this feeds back into skill tuning):
Unverified factual claims dropped by dimension: D9:{N} D10:{N} D1:{N} ...
Step 3: Aggregate and Display Report
Collect all findings from sub-agents. Aggregate by severity.
apcore-skills audit — Ecosystem Consistency Report
Date: {date}
Scope: {scope}
Mode: {"strict (all findings)" if STRICT_MODE else "lean (style/idiom/verify-spec suppressed — pass --strict for all)"}
Repos audited: {count}
Noise-Control: {n_total_drops} findings suppressed · {n_d10_d11_dedup} d10-d11-deduplicated · {n_unverified_claim} unverified-factual-claim · {n_info_nitpick} info-nitpick · {n_info_consolidated} info-consolidated · {n_strict_suppressed} strict-only ({"hidden — pass --strict to see" if !STRICT_MODE else "shown"})
{if n_unverified_claim > 0:}
Unverified claims dropped by dimension: {D9: N, D10: N, D1: N, ...}
═══ SUMMARY ═══
Dimension | Critical | Warning | Info | Inconclusive
D1 API Surface | 2 | 3 | 1 | —
D2 Naming Conventions | 0 | 5 | 3 | —
D3 Version Sync | 1 | 0 | 0 | —
D4 Documentation | 0 | 2 | 4 | —
D5 Test Coverage | 0 | 1 | 2 | —
D6 Dependencies | 1 | 2 | 0 | —
D7 Configuration | 0 | 3 | 1 | —
D8 Project Structure | 0 | 1 | 2 | —
D9 Bloat & Redundancy | 1 | 8 | 5 | —
D10 Contract Parity | 3 | 4 | 2 | —
D11 Deep-Chain Parity | 5 | 2 | 0 | 3
─────────────────────────────────────────────────────────────
TOTAL | 13 | 31 | 20 | 3
═══ CRITICAL FINDINGS ═══
[D1-001] Missing API: Registry.scan_directory()
Repo: apcore-typescript
Detail: Present in apcore-python (src/apcore/registry/registry.py:45) but missing from TypeScript SDK
Fix: Add scan_directory method to src/registry/registry.ts
[D3-001] Version mismatch in core sync group
Repos: apcore-python=0.7.0, apcore-typescript=0.7.1
Fix: Align versions before release
...
═══ WARNING FINDINGS ═══
(grouped by dimension)
═══ INFO FINDINGS ═══
(grouped by dimension)
═══ BLOAT REPORT (D9) ═══
Repo | LOC | Δ vs last | Dead | Dup | Parallel | Unused Cfg | Unused Dep | Scope Creep
apcore-python | 12450 | +2310 | 4 | 3 | 1 | 2 | 1 | 0
apcore-typescript | 11200 | +1980 | 6 | 2 | 0 | 1 | 0 | 2
django-apcore | 4500 | +890 | 2 | 1 | 0 | 0 | 0 | 1
flask-apcore | 3800 | +710 | 1 | 0 | 0 | 0 | 0 | 0
──────────────────────────────────────────────────────────────────────────────────────────────────────────
TOTAL | 31950 | +5890 | 13 | 6 | 1 | 3 | 1 | 3
Top bloat hotspots (act on these first):
1. apcore-typescript: 6 dead exports — see [D9-002] through [D9-007]
2. apcore-python: parallel HTTP client implementations — see [D9-014]
3. django-apcore: scope creep in user-auth feature (+3 unplanned files)
═══ CONTRACT PARITY REPORT (D10 — SHAPE-LEVEL) ═══
Symbols compared: {N}
Fully matching: {N}
With divergence: {N}
Top divergences (act on these first):
1. Registry.register — TS missing DuplicateError raise [D10-001]
2. Executor.execute — Go skips input validation present in Python/Rust [D10-002]
3. Config.load — Python thread_safe=true, TS thread_safe=false [D10-003]
Contract rows with divergence (summary):
inputs.validation: {N}
errors.raised: {N}
side_effect.order: {N}
return.shape: {N}
property.*: {N}
═══ DEEP-CHAIN PARITY REPORT (D11 — CHAIN-LEVEL) ═══
Delegated to: sync Step 4C (report saved: {audit-d11-{date}.md})
Modules analyzed: {N}
Modules complete / failed / inconclusive: {n} / {n} / {n}
Findings: critical {n} / warning {n} / info {n} / inconclusive {n}
By finding type:
semantic-divergence: {N}
missing-validation: {N}
missing-registration: {N}
defensive-gap: {N}
error-path-divergence: {N}
contract-gap: {N}
Top divergences (act on these first):
1. [D11-004] missing-registration — Registry.discover (Rust discover_internal skips modules map insert)
2. [D11-007] defensive-gap — Registry._discoverCustom (TS crashes on null discoverer result)
3. [D11-011] missing-validation — Registry._discover_custom (Python bare subscript on entry["module_id"])
D11 findings are cross-language intent divergences. All are MANUAL_REVIEW_ONLY — auto-fix cannot port logic semantics safely.
═══ HEALTH SCORE ═══
Overall: {score}/100
API Consistency: {score}/100
Naming: {score}/100
Version Sync: {score}/100
Documentation: {score}/100
Test Coverage: {score}/100
Dependencies: {score}/100
Leanness (D9): {score}/100
Contract Parity (D10): {score}/100
Deep-Chain Parity (D11): {score}/100 — see shared/scoring.md for formula
Score formulas: Leanness (D9) and Contract Parity (D10) formulas are defined canonically in shared/scoring.md. Use those formulas — do not re-implement. Any threshold change (e.g., release-gate BLOCK threshold) must be updated there, not inline here.
If --save flag is passed with an explicit path, write to that path. If --save is passed without a path, write to the canonical default from shared/ecosystem.md §0.6a: {ecosystem_root}/audit-report-{cwd_repo}-{YYYY-MM-DD}.md, where {cwd_repo} is the session's CWD repo/dir name resolved in Step 0. Including {cwd_repo} keeps same-day runs from different repos in separate files (no cross-scope overwrite); a re-run of the same scope on the same day overwrites its own file idempotently. The Write is always a full-file overwrite — reports are never concatenated with prior runs.
Step 3.1: Review-Compatible Issue Report (ALWAYS EMITTED)
After the consolidated report, ALWAYS append a review-compatible report so that /code-forge:fix --review can directly consume audit output.
Convert all CRITICAL and WARNING findings across dimensions D1–D10 into code-forge:review format. Format matches code-forge:review output schema and mirrors sync's Step 9.1 so that a single downstream consumer can ingest either skill's output.
Use the # Project Review: header with a dynamic scope description (derived from Step 1 — e.g., repo name, scope group, or "all"). Output the review-compatible report as raw markdown (not inside a fenced code block) so that code-forge:fix can parse it from the conversation context.
# Project Review: {scope_description}
## Consistency
{For each finding from D1–D10 with severity critical or warning, emit one issue entry:}
- severity: <blocker | critical | warning>
file: {target file path — the file that needs to be fixed}
line: {line number or range, use 1 if unknown}
title: [{dimension_id}-{finding_id}] {short title}
description: {what is inconsistent and why it matters — include cross-reference to spec or peer repo}
suggestion: {concrete fix instruction — what to change, what to match against}
Severity mapping from audit findings to review format:
| Dimension | Audit Severity | Review Severity | Notes |
|---|---|---|---|
| D1 | critical | blocker | Missing API symbol from a peer repo |
| D1 | critical | critical | Signature mismatch (param count, type) |
| D1 | warning | warning | Wrapper param count mismatch, naming divergence within signature |
| D2 | critical | critical | Public symbol violates language naming convention |
| D2 | warning | warning | Non-public or cosmetic naming issue |
| D3 | critical | blocker | Version mismatch within sync group before release |
| D3 | warning | warning | Version file inconsistency within a repo |
| D4 | warning | warning | Spec lacks ## Contract: block for a public symbol; README section missing |
| D4 | info (category=contract_coverage, detail mentions missing Contract fields) |
warning | Exception to the info-skip rule — Contract block exists but is missing required fields (Inputs / Errors / Returns / Properties). These are actionable partial-contract gaps that should reach /code-forge:fix --review. Detect by matching category == "contract_coverage" AND detail mentions "missing field" or "incomplete". |
| D4 | info (other) | (skip) | Minor docstring gaps, missing CHANGELOG badge, etc. |
| D5 | critical | critical | Tests fail |
| D5 | warning | warning | Test runner unavailable / deps missing |
| D6 | critical | blocker | Incompatible SDK version referenced |
| D6 | warning | warning | Unused / mismatched dependency |
| D7 | warning | warning | APCORE_* setting divergence across integrations |
| D8 | warning | warning | Project structure deviation |
| D9 | critical | critical | Parallel implementation / duplicate code / stub no-op method with spec-declared behavior |
| D9 | warning | warning | Dead export / unused internal / wrapper / scope creep |
| D10 | critical | blocker | Missing input validation or missing raised error type — users hit silent bugs; integration missing required arg into core SDK; integration calling removed core SDK API |
| D10 | critical | critical | Side-effect order divergence, return shape divergence, thread_safe/async property divergence; integration calling non-thread-safe core SDK method from concurrent handlers |
| D10 | warning | warning | Spec silent on Contract (cross-repo-only mode); extraction limit (null vs true/false); extra error raised beyond spec; integration missing handler for a documented core SDK error; integration calling deprecated core SDK API |
| D11 | critical | blocker | missing-registration — public method fails to update a map/collection peers update (breaks later get/list semantics) |
| D11 | critical | critical | semantic-divergence / missing-validation / defensive-gap / error-path-divergence / contract-gap (cross-language chain divergences) |
| D11 | warning | warning | Order-only divergence (same mutations, different order); extra checkpoint/mutation in one language not in peers |
| D11 | inconclusive | warning | Deep-chain sub-agent flagged uncertainty — emit as warning with title prefix [inconclusive] and suggestion "manual review required — static analysis could not determine whether divergence is intentional". Never silently dropped. |
| any | info | (skip) | info-level findings are not actionable bugs |
Rules:
- Group issues by file for efficient batch fixing
- The
filefield MUST point to the implementation or doc file that needs changing. For D10 cross-repo findings where spec is silent, thefileis the implementation file of the outlier repo (the one that diverges from the majority or from the most-reference repoapcore-python). For spec-authoritative D10 findings, every non-matching repo emits its own issue entry (one per repo). - The
suggestionfield MUST be concrete — e.g., "Addif not RE_ID.match(id): raise InvalidIdError(code=INVALID_ID)at line {L}, before the existingself._index[id] = moduleassignment" rather than "fix validation". - For D10 intent di
…(truncated)