Resources
5Install
npx skillscat add omninode-ai/omniclaude/gap Install via the SkillsCat registry.
Gap
Cross-repo integration health audit. Consolidated from gap-analysis, gap-fix, and gap-cycle.
Overview
Unified cross-repo integration health audit. Three subcommands:
- detect: Audit closed epics for Kafka topic drift, model mismatches, FK drift, API contract drift, and DB boundary violations
- fix: Auto-fix loop for gap-analysis findings with decision gates
- cycle: Full detect->fix->verify loop with artifact chaining
Announce at start: "I'm using the gap skill to perform cross-repo integration health analysis."
Subcommand Routing
Parse the first positional argument as the subcommand:
/gap detect --epic OMN-2500
/gap fix --report multi-epic-2026-02-23/run-001
/gap cycle --epic OMN-XXXXRoute to the corresponding section in prompt.md based on the subcommand value.
Subcommand: detect
Absorbed from: gap-analysis (v1.0.0)
When to Use
Use when:
- An Epic has shipped but you suspect integration drift between repos
- You want to verify no silent breakage happened across service boundaries
- Pre-release health check across all repos touched by an Epic
- After a refactoring that spans multiple repositories
Do NOT use when:
- Debugging a single known failure (use
systematic-debugginginstead) - You need a single-repo code review (use
pr-revieworlocal-review) - The Epic is still In Progress (wait for it to close first, or use
pipeline-audit)
CLI Examples
/gap detect --epic OMN-2500
/gap detect --since-days 7
/gap detect --epic OMN-2500 --dry-run
/gap detect --epic OMN-2500 --output json
/gap detect --severity-threshold CRITICAL
/gap detect --max-best-effort 20
/gap detect --epic OMN-2500 --skip-infra-probes
/gap detect --epic OMN-2500 --include-auth-probes --lag-threshold 5000| Arg | Default | Description |
|---|---|---|
--epic |
none | Single Epic to audit |
--repo |
all | Limit probes to one repo |
--since-days |
30 | Days to look back for closed Epics |
--severity-threshold |
WARNING | Minimum severity to surface |
--max-findings |
200 | Cap on total findings |
--max-best-effort |
50 | Cap on BEST_EFFORT findings |
--dry-run |
false | Skip ticket creation/commenting |
--output |
md | Output format: json or md |
--skip-infra-probes |
false | Skip probes 2.7-2.8, 2.10 (env_activation, projection_lag, migration_parity) that require live infrastructure |
--include-auth-probes |
false | Include probe 2.9 (auth_config); disabled by default as it requires Infisical access |
--lag-threshold |
10000 | Consumer group lag threshold for projection_lag probe (probe 2.8) |
Gating Rules (Hard -- Never Violate)
- BEST_EFFORT severity cap: BEST_EFFORT confidence findings are capped at WARNING.
A BEST_EFFORT finding may NEVER be CRITICAL. - DB boundary is always DETERMINISTIC or SKIP: Never BEST_EFFORT for DB boundary
probes. AST-first; grep fallback is acceptable only for non-DB probes. - SKIP probes emit no findings: Log to
skipped_probesonly. - Empty repos_in_scope: If
repos_in_scopeis empty after canonicalization, emitstatus=blocked, reason=NO_REPO_EVIDENCE. Never silently skip.
Confidence Levels
| Level | Meaning |
|---|---|
DETERMINISTIC |
Registry/AST/schema.json -- unambiguous |
BEST_EFFORT |
Grep fallback -- may have false positives |
SKIP |
Required input absent (no OpenAPI, no schema.json) |
The Three Phases
Full orchestration logic is in prompt.md. Summary:
Phase 1 -- Intake: Fetch Epic(s) from Linear, canonicalize repo names, buildrepos_in_scope. Emit status=blocked if no repo evidence found.
Phase 2 -- Probe: Run the 11 probe categories against each repo in scope:
- Kafka topic drift, 2. Model type mismatch, 3. FK reference drift, 4. API contract drift,
- DB boundary violation, 6. Topic registry drift, 7. Env activation drift,
- Projection lag, 9. Auth config drift, 10. Migration parity, 11. Legacy config patterns.
Apply scan-root filtering (skip tests/docs/generated code). Compute fingerprints and apply
suppressions. Topic registry auto-fix is LOCAL-ONLY (enum member addition only).
Legacy config auto-fix uses deterministic search-replace from the denylist.
Phase 3 -- Report: Dedup against existing Linear tickets, create/comment tickets,
write report artifacts to ~/.claude/gap-analysis/{epic_id}/{run_id}.json and .md.
Fingerprint Spec
SHA-256 of pipe-delimited:
category | boundary_kind | rule_name | sorted(repos) | seam_id_suffix | mismatch_shape | repo_relative_pathseam_id_suffix: suffix only (no env/namespace prefix)repo_relative_path: from repo root (not absolute)- No timestamps, run IDs, or line numbers in the fingerprint
Ticket Marker Block
Every created/commented ticket contains a stable machine-readable block:
<!-- gap-analysis-marker
fingerprint: <sha256>
gap_category: CONTRACT_DRIFT
boundary_kind: kafka_topic
rule_name: topic_name_mismatch
seam: pattern-learned.v1
repos: [omniclaude, omniintelligence]
confidence: DETERMINISTIC
evidence_method: registry
detected_at: <ISO timestamp>
-->Dedup Table
| Existing ticket state | Last closed | Fingerprint match | Action |
|---|---|---|---|
| In Progress / Backlog / Todo | -- | -- | Comment, skip creation |
| Done / Duplicate / Cancelled | <= 7 days | Same fingerprint | Comment, skip creation |
| Done / Duplicate / Cancelled | > 7 days OR diff fingerprint | -- | Create new ticket |
| None | -- | -- | Create new ticket |
Ticket Title Format
[gap:<fingerprint[:8]>] <category>: <seam_id_suffix>
Example: [gap:a3f2c891] CONTRACT_DRIFT: pattern-learned.v1
Report Artifacts
~/.claude/gap-analysis/{epic_id}/{run_id}.json # Full report (ModelGapAnalysisReport)
~/.claude/gap-analysis/{epic_id}/{run_id}.md # Human-readable summaryDB Boundary Rules
Repos that must not access upstream DBs (may have local read-model):
no_upstream_db_repos:
- omnidash
- omnidash2
- omniwebRepos where DB writes are allowed:
db_write_allowed_repos:
- omnibase_infra
- omniintelligence
- omnimemoryScan Roots (Prevent False Positives)
| Repo type | Scan | Skip |
|---|---|---|
Python (src/ layout) |
src/** |
tests/**, fixtures/**, docs/** |
| TypeScript | client/**, server/**, src/** |
__tests__/**, *.test.ts, fixtures/** |
| Generated code | -- | Always skip |
Suppressions
Format: skills/gap/suppressions.yaml
suppressions:
- fingerprint: "abc12345..."
reason: "test harness only"
expires: "2026-06-01"
- path_glob: "tests/**"
rule: "db_boundary"
reason: "test fixtures allowed DB access"Precedence: fingerprint > path_glob. Expired suppressions are NOT applied and
emit a WARNING in expired_suppressions_warned.
Subcommand: fix
Absorbed from: gap-fix (v0.1.0)
Overview
Automates the "detected to fixed" loop that detect opens but leaves manual. Reads a
gap-analysis report, classifies findings by auto-dispatch eligibility, dispatchesticket-pipeline for safe-only findings, then calls pr-queue-pipeline --prs on the created PRs.
Scope (v0 -- narrow): Only auto-dispatch findings with a single deterministic resolution.
Anything multi-option emits a decision gate and waits for human input.
Entry Points
/gap fix --report <run_path> # e.g., multi-epic-2026-02-23/run-001
/gap fix --ticket OMN-XXXX # single finding via marker block
/gap fix --latest # follows ~/.claude/gap-analysis/latest/
/gap fix --dry-run # classify and print plan, no side effects| Arg | Default | Description |
|---|---|---|
--report <run_path> |
none | Gap-analysis run path under ~/.claude/gap-analysis/ |
--ticket <id> |
none | Single finding via Linear ticket containing a marker block |
--latest |
false | Follow ~/.claude/gap-analysis/latest/ symlink |
--dry-run |
false | Zero side effects: no ledger, no Linear writes, no PR mutations |
--mode <mode> |
ticket-pipeline |
ticket-pipeline | ticket-work | implement-only |
--choose <decisions> |
none | GAP-id=A,GAP-id=B -- provide choices for gated findings |
--force-decide |
false | Re-open previously decided findings in decisions.json |
Phases
Phase 0: Parse + normalize report -> ModelGapFinding[]
Phase 1: Classify findings by auto-dispatch eligibility
Phase 2: Decision gate -- emit choices block for non-auto findings; skip undecided; continue with auto
Phase 3: Execute fix -- dispatch ticket-pipeline per auto finding
-> emit gap-fix-output.json with prs_created[]
-> call: pr-queue-pipeline --prs <path> (NOT --repos -- scoped to new PRs only)
Phase 4: Re-probe -- minimal grep/AST per boundary_kind before marking fixed
Phase 5: Report -- append fix section to .md artifact; update decisions ledgerAuto-Dispatch vs Gate Table
boundary_kind |
rule_name |
Auto-dispatch? |
|---|---|---|
kafka_topic |
topic_name_mismatch |
YES |
db_url_drift |
legacy_db_name_in_tests |
YES |
db_url_drift |
legacy_env_var |
YES |
topic_registry |
topic_registry_missing_member |
YES (LOCAL-ONLY: adds enum member to local TopicRegistry only) |
legacy_config |
legacy_denylist_match |
YES (search-replace from denylist) |
kafka_topic |
producer_only_no_consumer |
NO -- gate |
api_contract |
missing_openapi |
NO -- gate |
env_activation |
env_var_not_activated |
NO -- gate |
projection_lag |
consumer_group_lag_exceeded |
NO -- gate |
auth_config |
auth_client_config_drift |
NO -- gate |
migration_parity |
migration_head_mismatch |
NO -- gate |
db_boundary |
upstream_db_access |
NO -- gate |
Any BEST_EFFORT with multiple resolutions |
-- | NO -- gate |
Key Invariants
- Re-probe must pass before marking a finding
fixed pr-queue-pipelinealways called with--prs(not--repos) -- only touches created PRs--dry-runproduces zero side effects: no ledger writes, no Linear writes, no PR mutationsdecisions.jsonis write-once per finding;--force-decideto re-openblocked_externalguard: ifpr-queue-pipelinereturnsblocked_external > 0, log a warning
and do NOT retry -- these are external infra failures (deploy locks, CI secrets), not skill bugs
Modes
| Mode | Description |
|---|---|
ticket-pipeline |
Default -- dispatches ticket-pipeline per auto finding; calls pr-queue-pipeline --prs on created PRs |
ticket-work |
Dispatches ticket-work only (no PR creation or merge) |
implement-only |
No ticket-pipeline or ticket-work; implements fixes directly in worktree |
Artifacts
~/.claude/gap-analysis/<source_run_id>/gap-fix-output.json:
{
"prs_created": [
{ "repo": "org/repo", "number": 123, "url": "...", "finding_id": "GAP-a3f9c1d2" }
],
"tickets": ["OMN-XXXX"]
}~/.claude/gap-analysis/<source_run_id>/decisions.json -- persisted decisions so --choose
selections are not re-prompted on every run. <source_run_id> is the gap-analysis run
identifier (the original analysis run), NOT the fix run ID:
{
"GAP-b7e2d5f8": { "choice": "A", "chosen_at": "...", "by": "jonah" }
}ModelSkillResult
Written to ~/.claude/gap-analysis/<source_run_id>/gap-fix-result.json:
{
"skill": "gap",
"version": "1.0.0",
"status": "complete | partial | nothing_to_fix | gate_pending | error",
"run_id": "<run_id>",
"findings_total": 12,
"findings_auto": 5,
"findings_gated": 4,
"findings_skipped": 3,
"prs_created": 5,
"prs_merged": 4,
"findings_fixed": 4,
"findings_still_open": 1,
"gate_pending": ["GAP-b7e2d5f8", "GAP-c3a1e2d4"],
"output_path": "~/.claude/gap-analysis/<source_run_id>/gap-fix-output.json"
}Status values:
complete-- all auto findings fixed, no gate_pendingpartial-- some findings fixed, some failed or gatednothing_to_fix-- no auto-dispatchable findings foundgate_pending-- gated findings emitted; awaiting human--chooseerror-- unrecoverable error
Sub-skills Used
ticket-pipeline(existing) -- creates PR from finding (default mode)pr-queue-pipeline-- merges the created PRs (called with--prs, not--repos)
Subcommand: cycle
Absorbed from: gap-cycle (v0.1.0)
Overview
Orchestrates the full gap investigation loop: detect -> fix -> verify.
Single entry point replacing manual sub-skill chaining.
CLI Examples
# Minimal: detect + fix (default)
/gap cycle --epic OMN-XXXX
# Detect only (no fix)
/gap cycle --epic OMN-XXXX --no-fix
# Detect + fix + single routing verify
/gap cycle --epic OMN-XXXX --verify
# Skip detect (use existing report)
/gap cycle --report ~/.claude/gap-analysis/OMN-XXXX/<run_id>.json
# Resume interrupted run
/gap cycle --resume ~/.claude/gap-cycle/OMN-XXXX/<run_id>/summary.json
# Dry run (no external writes, local files written)
/gap cycle --epic OMN-XXXX --dry-runArtifact Chaining
The cycle subcommand requires stdout markers from sub-phases. If a marker is absent,
it emits a hard error -- it does NOT reconstruct paths from naming conventions or check
fallback pointer files.
- detect phase must emit:
ARTIFACT: <absolute path> - fix phase must emit:
GAP_FIX_OUTPUT: <absolute path>
Output
Written to ~/.claude/gap-cycle/{epic_id}/{run_id}/summary.json:
{
"epic_id": "OMN-XXXX",
"run_id": "gap-cycle-2026-03-04T12:00:00Z",
"source_report_path": "...",
"phases_executed": {"detect": true, "audit": false, "fix": true, "verify": false},
"phase_results": {"detect": null, "audit": null, "fix": null, "verify": null},
"prs_created": [],
"gated_findings_count": 0,
"nothing_to_fix": false,
"composite_status": "complete",
"dry_run": false
}Status Values
complete: All enabled phases ran AND gated_findings_count == 0 AND (verify not enabled OR verify passed). OR nothing_to_fix=true.partial: Some phases skipped by flags; OR verify not enabled; OR gated_findings_count > 0.gate_pending: --auto-only: all findings were gated, zero PRs created. Phase 4 skipped.blocked: Required prerequisite missing -- report not found, epic_id absent from report metadata.error: Exception or tool failure in any phase.
v0.2 Roadmap
- pipeline-audit chaining (Phase 2 automation)
- sidecar audit annotation files (no schema mutation)
- per-repo-to-pipeline verification mapping
Integration Test
Test suite: tests/integration/skills/gap_fix/test_gap_fix_integration.py
All tests use @pytest.mark.unit -- static analysis of skill files, no external services required.
| Test Case | Class | What it verifies |
|---|---|---|
| 1 | TestDryRunContract |
--dry-run produces zero side effects |
| 2 | TestFixedRequiresProof |
fixed status requires probe block with all 5 fields |
| 3 | TestInfraBoundaryGate |
DB import in UI repo classified as GATE |
| 4 | TestPositiveRouting |
pr-queue-pipeline called with --prs |
| 5 | TestDecisionsJsonAppendOnly |
decisions.json is write-once per fingerprint |
| 6 | TestForcedDecideVersioning |
--force-decide re-opens prior decisions |
| 7 | TestPrQueuePipelineInvocation |
pr-queue-pipeline always invoked with --prs |
uv run pytest tests/integration/skills/gap_fix/test_gap_fix_integration.py -vSee Also
pipeline-auditskill (comprehensive end-to-end pipeline verification)systematic-debuggingskill (debugging a single known failure; see Phase 1 Backward Tracing)create-ticketskill (ticket creation patterns)skills/gap/suppressions.yaml(suppression registry)skills/gap/legacy-denylist.yaml(legacy config patterns for probe 2.11)skills/gap/models/(local Pydantic models)skills/gap/docs/FAILURE_TAXONOMY.md(11 failure class reference)skills/gap/docs/POST_MERGE_STABILIZATION.md(6-step post-merge workflow)~/.claude/gap-analysis/(report output directory)~/.claude/gap-cycle/(cycle summary output directory)