{"id":"ingest-triage","name":"ingest_triage","summary":"バンドル取り込み時に検出された競合(構造重複、定義矛盾、ほぼ重複クラスタ、再取り込み変更、排除)を分類・解決します。","body":"# Ingest Triage - conflict classification and resolution\n\nThis skill is loaded in two contexts:\n- By a Stage 3 WorkUnit agent when `sl_discover`, deterministic projection\n  output, existing project memory, or prior provenance overlaps with what the\n  current WorkUnit is about to write.\n- By the Stage 4 reconciliation agent for cross-WorkUnit sweeps, accepted patch\n  overlap, and eviction decisions.\n\nApply the rules below before every write that could collide with an existing artifact.\n\n## Decision tree\n\n1. **Is this the same artifact I'm producing now, or a different one with the same name?**\n   Read both. If names match and content matches (modulo whitespace): no conflict - skip the write, the prior one stands.\n\n2. **If content differs, is it an expression-only change (e.g. a different `sql:` body for the same measure name, same grain, same columns)?**\n   Re-ingest change (expression-only): silently replace via `sl_edit_source`. No flag.\n\n3. **If the difference is structural - grain, columns, filter, join shape - is the current bundle the re-ingest of a previously-ingested bundle (i.e. `priorProvenance` has a row for this raw file and artifact)?**\n   Re-ingest change (semantic break): replace + flag. Record in the IngestReport's `conflicts_resolved` list with `flagged_for_human: true`.\n\n4. **If reconciliation sees accepted patches from this same job with no\nprior-sync row, check for same-ingest contradictions:**\n\n   | Kind | Detection | Resolution |\n   |---|---|---|\n   | Structural duplicate | Same name, near-identical expression | Elect canonical by: (a) highest inbound-ref count from other sources; tiebreak: (b) lexicographically first unit key; (c) lexicographically first source name. Subsume losers into `<canonical>-variants.md` wiki page. Do NOT flag unless ambiguous. |\n   | Near-duplicate cluster | Different names, overlapping shape (same table, similar formulas) | Same as structural; one canonical, others subsumed. Flag only if no canonical emerges. |\n   | Definitional contradiction | Same name, substantively different formulas (different aggregation, different filters, different columns) | **Rename + capture**: disambiguate ALL variants with suffix derived from the domain (`churn_risk_engagement_based`, `churn_risk_billing_based`) and write a unified wiki page listing every variant with provenance. The contested name does NOT land in the SL. **Always flag.** |\n\n5. **Eviction (Stage 4 only)**: for each entry in `eviction_list()`:\n   - Remove the artifact (`sl_write_source` or `sl_edit_source` with `delete: true` for SL sources, `wiki_remove` for wiki pages).\n   - Record the removal with `emit_eviction_decision` and `action: \"removed\"`.\n\n## Why same-ingest vs re-ingest differs\n\nWithin ONE bundle there's no user signal telling us which duplicate wins - we capture all variants and flag. Across bundles, re-uploading IS the signal that the new state is intended - we replace silently for expression changes and flag for semantic breaks.\n\n## Naming disambiguation hints\n\nWhen you rename to disambiguate, prefer domain suffixes that match the containing view/table/collection name: `customers.churn_risk_score` → `customers.churn_risk_engagement_based` (if the `customer_churn` view computes it from engagement); `billing.churn_risk_score` → `billing.churn_risk_billing_based`. Avoid numeric suffixes (`churn_risk_1`, `churn_risk_2`) - they disclose nothing.\n\n## Applying canonical pins\n\nWhen the Stage 4 system prompt includes a `<canonical_pins>` block, treat each pin as a prior user decision for that `contestedKey`.\n\n- If the pinned `canonicalArtifactKey` is present in the Stage Index or already exists in SL, keep it as the canonical artifact for that contested key.\n- Disambiguate competing artifacts instead of using the contested name for them.\n- Do not flag the pinned contested key solely because the variants disagree; the user has already chosen the canonical artifact.\n- If the pinned artifact cannot be found and no current WU can recreate it, emit `emit_conflict_resolution` with `flaggedForHuman: true` and explain that the pin references a missing canonical artifact.\n\nWhen a pin applies cleanly, call `emit_conflict_resolution` with `kind: \"definitional_contradiction\"`, `artifactKey` set to the pinned `canonicalArtifactKey`, `detail` describing the pinned election, and `flaggedForHuman: false`.\n\n## What to write in the unified wiki page\n\nWhen you perform rename + capture, also write one page named `<canonical-concept>-definitions.md` under the wiki GLOBAL scope. Structure:\n\n- One heading per variant, referencing the disambiguated SL name.\n- One paragraph per variant: what it computes, where it came from (raw file + line range), when to use it.\n- A closing \"Choosing between these\" paragraph if the variants are legitimately domain-specific.\n\nDo not attempt to rank variants or pick a \"best\" - that's user-override territory.\n\n## Silence rules\n\nFlag for human review when:\n- You did rename + capture for a definitional contradiction (kind 3 above).\n- You performed an eviction retention (kind 5, second row).\n- An override constraint (from a Stage 4 re-run) conflicts with current inbound refs.\n\nDo NOT flag:\n- Same-content duplicate skip (trivial).\n- Structural duplicate with clear canonical election.\n- Expression-only re-ingest replace.","author":"@Kaelio","ownerProfile":null,"authorContacts":null,"sourceUrl":"https://github.com/Kaelio/ktx/tree/main/packages/cli/src/skills/ingest_triage","license":"Apache-2.0","category":"writing","lang":"en","tokens":1198,"stars":0,"calls30d":0,"claimed":false,"visibility":"public","origin":"crawler","version":"0.1.0","createdAt":"2026-08-22","updatedAt":"2026-08-22","files":[],"requires":{"mcp":[],"tools":[]},"safety":{"flags":[],"scannedAt":"2026-08-22","hasScripts":false,"networkEndpoints":[]}}