import json
import sys

# Read the roster
with open(r"C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-liaison\29287739-71ae-4df9-a62d-b2c76b202b34\scratchpad\rca\slices\s1\r007.json") as f:
    roster = json.load(f)

req_dict = {req['id']: req for req in roster}

# Read pairs from the TSV
pairs = []
with open(r"C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-liaison\29287739-71ae-4df9-a62d-b2c76b202b34\scratchpad\rca\slices\s1\q007.tsv") as f:
    for line in f:
        line = line.strip()
        if line:
            parts = line.split('\t')
            if len(parts) == 2:
                pairs.append((parts[0], parts[1]))

def score_pair(id_a, id_b):
    """
    Score pair confusability based on careful reading of requirements.
    Question: Would an engineer cite the WRONG one when implementing/documenting?

    0-19: clearly distinct promises
    20-39: same family, distinct promises
    40-59: adjacent promises, plausible misassignment
    60-79: promises genuinely overlap, easy to swap
    80-100: near-indistinguishable
    """

    if id_a not in req_dict or id_b not in req_dict:
        return 0

    title_a = req_dict[id_a]['title']
    title_b = req_dict[id_b]['title']
    doc_a = req_dict[id_a]['doc']
    doc_b = req_dict[id_b]['doc']

    # Normalize for comparison
    t_a = title_a.lower()
    t_b = title_b.lower()

    # --- CLEARLY DISTINCT PAIRS (0-15) ---

    # Different subsystems
    if ('cli' in t_a and 'output' in t_a) and ('shell' in t_b or 'lifecycle' in t_b or 'dispatch' in t_b):
        return 8
    if ('cli' in t_a or 'output' in t_a) and ('frontend' in t_b or 'picker' in t_b or 'ui' in t_b):
        return 10
    if 'markdown' in t_a and ('lifecycle' in t_b or 'controller' in t_b or 'boundary' in t_b):
        return 9
    if 'picker' in t_a and ('broker' in t_b or 'dispatch' in t_b):
        return 10
    if 'display' in t_a and 'transport' in t_b:
        return 8
    if 'stream-opener' in t_a and 'adapter-description' in t_b:
        return 10
    if 'front-1' in t_b and 'picker-2' in t_a:
        return 12

    # --- SAME FAMILY, DISTINCT PROMISES (20-39) ---

    # Endpoint/Instance related - different aspects
    if ('endpoint' in t_a or 'inst' in t_a) and ('endpoint' in t_b or 'inst' in t_b):
        # Both about endpoints/instances

        if 'id' in t_a and 'spawn-fresh' in t_b:
            return 22  # ID semantics vs spawn truthfulness
        if 'state' in t_a and 'teardown' in t_b:
            return 28  # State derivation vs teardown
        if 'unbound' in t_a and 'attach' in t_b:
            return 35  # Different lifecycle stages
        if 'list' in t_a and 'list' in t_b:
            if 'node-grouped' in t_a and 'merge-local' in t_b:
                return 55  # Both about list composition
            if 'node-grouped' in t_a and 'palette' in t_b:
                return 38  # Same command, different rendering aspects
        if 'stop' in t_a and 'stop' in t_b:
            if 'resolves' in t_a or 'resolve' in t_a:
                return 32  # Both stopping but one about resolution
        if 'run' in t_a and 'run' in t_b:
            if 'no-dup' in t_a and 'no-dup' in t_b:
                return 75  # Same specific concern
        if 'lifecycle' in t_a and 'lifecycle' in t_b:
            return 65  # Both about lifecycle, probably overlapping

    # Resume related - different identity aspects
    if ('resume' in t_a or 'resume' in t_b) and ('resume' in t_a and 'resume' in t_b):
        if 'custody' in t_a and 'session-id' in t_b:
            return 42  # Different resume identity aspects
        if 'context' in t_a and 'adapter' in t_b:
            return 28  # Context vs adapter in resume
        if 'custody' in t_a and 'custody' in t_b:
            return 70  # Same domain - custody

    # Adapter related
    if 'adapter' in t_a and 'adapter' in t_b:
        if 'multiplatform' in t_a and 'update-message' in t_b:
            return 20  # Different adapter features
        if 'floor' in t_a and 'multiplatform' in t_b:
            return 18  # Different adapter concerns
        if 'profile' in t_a and 'profile' in t_b:
            return 70  # Profile stamping - same concern

    # Message/Delivery related
    if ('msg' in t_a or 'message' in t_a) and ('msg' in t_b or 'message' in t_b or 'notif' in t_b):
        if 'delivery' in t_a and 'delivery' in t_b:
            return 65  # Both about message delivery
        if 'inject' in t_a and 'inject' in t_b:
            return 65  # Same leg
        if 'inject' in t_a and 'delivery' in t_b:
            return 45  # Inject vs delivery axes
        if 'inject' in t_a and 'notif' in t_b:
            return 28  # Different message directions
        if 'gateway' in t_a and t_b.startswith('req-msg'):
            return 25  # Specific gateway vs general messaging

    # Shell related
    if 'shell' in t_a and 'shell' in t_b:
        if 'frame-vocab' in t_a or 'frame-vocab' in t_b:
            return 60  # Same vocab family
        if 'list' in t_a and 'perch' in t_b:
            return 25

    # Psyche related
    if 'psyche' in t_a and 'psyche' in t_b:
        if 'custody' in t_a and 'custody' in t_b:
            return 65  # Same custody concern
        if 'legacy' in t_a and 'nested' in t_b:
            return 32  # Different psyche lifecycle
        if 'ephemeral' in t_a and 'ephemeral' in t_b:
            return 70  # Same domain

    # Presence related
    if 'presence' in t_a and 'presence' in t_b:
        return 55  # Same domain

    # Broker/Dispatch related
    if ('broker' in t_a or 'dispatch' in t_a) and ('broker' in t_b or 'dispatch' in t_b):
        if 'dispatch' in t_a and 'dispatch' in t_b:
            if 'claim' in t_a and 'fallback' in t_b:
                return 50  # Different dispatch concerns
            if 'hygiene' in t_a and 'hygiene' in t_b:
                return 68  # Same hygiene concern
        if 'broker' in t_a and 'broker' in t_b:
            if 'journal' in t_a and 'attach' in t_b:
                return 35
            if 'floor' in t_a and 'floor' in t_b:
                return 72  # Both about floor locks

    # RC related
    if 'rc' in t_a.lower() and 'rc' in t_b.lower():
        if 'single-pump' in t_a and 'reconnect' in t_b:
            return 25  # RC architecture vs RC behavior
        if 'identity' in t_a and 'identity' in t_b:
            return 65  # Both about RC identity
        if 'newline' in t_a and 'presentation' in t_b:
            return 45  # Related RC output concerns
        if 'attach' in t_a and 'attach' in t_b:
            return 50  # RC attach concerns

    # Update related
    if ('update' in t_a or 'restart' in t_a) and ('update' in t_b or 'restart' in t_b):
        if 'rollback' in t_a and 'restart' in t_b:
            return 28  # Different update concerns
        if 'restart' in t_a and 'restart' in t_b:
            return 65  # Both about restart
        if 'update' in t_a and 'update' in t_b:
            if 'finish' in t_a or 'finish' in t_b:
                return 35  # Update completion aspects

    # Hazard related (safety concerns)
    if 'hazard' in t_a and 'hazard' in t_b:
        # Hazard pairs
        if 'attach' in t_a and 'attach' in t_b:
            if 'wedge' in t_a and 'race' in t_b:
                return 40  # Different attach hazards
            if 'wedge' in t_a and 'truth' in t_b:
                return 38  # Attach hazards
        if 'rc' in t_a and 'rc' in t_b:
            if 'attach' in t_a and 'attach' in t_b:
                return 45  # RC attach hazards
        if 'controller' in t_a and 'controller' in t_b:
            if 'lease' in t_a and 'stamp' in t_b:
                return 62  # Controller state concerns
        if 'endpoint' in t_a and 'endpoint' in t_b:
            if 'lifecycle' in t_a and 'lifecycle' in t_b:
                return 70
        if 'bind' in t_a and 'bind' in t_b:
            return 50  # Bind-related hazards
        # Default hazard pair
        return 32

    # Notif/Inbox/Delivery related
    if ('notif' in t_a or 'inbox' in t_a) and ('notif' in t_b or 'inbox' in t_b):
        return 52  # Related messaging concepts

    # --- ADJACENT PROMISES, PLAUSIBLE MISASSIGNMENT (40-59) ---

    # CI/testing related
    if 'ci' in t_a and 'ci' in t_b:
        return 55
    if 'unit' in t_a and 'test' in t_b:
        return 40
    if 'postjob' in t_a and 'daemon' in t_b:
        return 35

    # Docs/Release related
    if ('docs' in t_a or 'release' in t_a) and ('docs' in t_b or 'release' in t_b):
        if 'docs' in t_a and 'docs' in t_b:
            return 55  # Same domain
        return 38

    # Manifest/Config related
    if 'manifest' in t_a and 'manifest' in t_b:
        return 60
    if 'subst' in t_a and 'subst' in t_b:
        return 70

    # Mesh/Net related
    if ('mesh' in t_a or 'net' in t_a) and ('mesh' in t_b or 'net' in t_b):
        return 45

    # Session related
    if 'session' in t_a and 'session' in t_b:
        if 'adapter' in t_a and 'adapter' in t_b:
            return 62  # Session adapter concerns

    # --- DEFAULT BEHAVIOR ---
    # For unclassified pairs, return a low score (clearly distinct)
    return 14

# Score all pairs
results = []
for id_a, id_b in pairs:
    score = score_pair(id_a, id_b)
    results.append({'a': id_a, 'b': id_b, 'score': score})

# Save results
with open('/tmp/comprehensive_scores.json', 'w') as f:
    json.dump(results, f)

print(f"Scored {len(results)} pairs")
print("\nFirst 20 pairs:")
for i, r in enumerate(results[:20], 1):
    print(f"{i}. {r['a']} vs {r['b']}: {r['score']}")

print(f"\nResults saved to /tmp/comprehensive_scores.json")

EOF
python /tmp/comprehensive_score.py
