@heretek-ai/epistemic-swarm 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/brainstorming/SKILL.md +13 -0
- package/.agents/skills/code_audit/SKILL.md +13 -0
- package/.agents/skills/epistemic_search/SKILL.md +13 -0
- package/.agents/skills/grilling/SKILL.md +13 -0
- package/.agents/skills/oss_scout/SKILL.md +13 -0
- package/.agents/skills/research_cache/SKILL.md +13 -0
- package/.agents/skills/swarm_config/SKILL.md +13 -0
- package/.claude-plugin/plugin.json +15 -5
- package/.omp/README.md +39 -0
- package/.omp/SYSTEM.md +12 -0
- package/.omp/commands/audit.md +10 -0
- package/.omp/commands/brainstorming.md +12 -0
- package/.omp/commands/grill.md +9 -0
- package/.omp/commands/scout.md +10 -0
- package/.omp/commands/swarm-config.md +10 -0
- package/.omp/commands/swarm.md +9 -0
- package/.omp/hooks/post/epistemic-audit.ts +16 -0
- package/.omp/hooks/pre/epistemic-redirect.ts +21 -0
- package/.omp/prompts/brainstorming.md +8 -0
- package/.omp/prompts/swarm.md +8 -0
- package/MARKETPLACE.md +8 -0
- package/README.md +32 -3
- package/bin/cli.js +27 -3
- package/config/domain_packs/biopharma.json +23 -0
- package/config/domain_packs/legal.json +19 -0
- package/config/domain_packs/quant.json +19 -0
- package/config/mcp-research-servers.json +7 -0
- package/config/mcp_launcher.py +48 -137
- package/config/opencode-snippet.json +20 -1
- package/config/searxng_mcp.py +42 -83
- package/extensions/pi/index.js +138 -73
- package/install.sh +20 -4
- package/package.json +40 -5
- package/plugins/antigravity/README.md +28 -0
- package/plugins/antigravity/agents/alpha-thesis.md +6 -0
- package/plugins/antigravity/agents/beta-antithesis.md +7 -0
- package/plugins/antigravity/agents/brainstormer.md +7 -0
- package/plugins/antigravity/agents/epistemic-auditor.md +5 -0
- package/plugins/antigravity/hooks.json +23 -0
- package/plugins/antigravity/mcp_config.json +33 -0
- package/plugins/antigravity/plugin.json +21 -0
- package/plugins/antigravity/rules/epistemic-integrity.md +6 -0
- package/plugins/antigravity/skills/brainstorming/SKILL.md +13 -0
- package/plugins/antigravity/skills/code_audit/SKILL.md +13 -0
- package/plugins/antigravity/skills/epistemic_search/SKILL.md +13 -0
- package/plugins/antigravity/skills/grilling/SKILL.md +13 -0
- package/plugins/antigravity/skills/oss_scout/SKILL.md +13 -0
- package/plugins/antigravity/skills/research_cache/SKILL.md +13 -0
- package/plugins/antigravity/skills/swarm_config/SKILL.md +13 -0
- package/plugins/codex/AGENTS.md.snippet +10 -0
- package/plugins/codex/README.md +37 -0
- package/plugins/codex/config.toml.snippet +28 -0
- package/plugins/codex/openai.yaml +24 -0
- package/plugins/codex/skills/brainstorming/SKILL.md +13 -0
- package/plugins/codex/skills/code_audit/SKILL.md +13 -0
- package/plugins/codex/skills/epistemic_search/SKILL.md +13 -0
- package/plugins/codex/skills/grilling/SKILL.md +13 -0
- package/plugins/codex/skills/oss_scout/SKILL.md +13 -0
- package/plugins/codex/skills/research_cache/SKILL.md +13 -0
- package/plugins/codex/skills/swarm_config/SKILL.md +13 -0
- package/plugins/gemini/GEMINI.md +15 -0
- package/plugins/gemini/README.md +19 -0
- package/plugins/gemini/commands/audit.toml +6 -0
- package/plugins/gemini/commands/brainstorming.toml +10 -0
- package/plugins/gemini/commands/grill.toml +6 -0
- package/plugins/gemini/commands/scout.toml +7 -0
- package/plugins/gemini/commands/swarm-config.toml +7 -0
- package/plugins/gemini/commands/swarm.toml +8 -0
- package/plugins/gemini/gemini-extension.json +38 -0
- package/plugins/gemini/hooks/hooks.json +11 -0
- package/plugins/gemini/skills/brainstorming/SKILL.md +13 -0
- package/plugins/gemini/skills/code_audit/SKILL.md +13 -0
- package/plugins/gemini/skills/epistemic_search/SKILL.md +13 -0
- package/plugins/gemini/skills/grilling/SKILL.md +13 -0
- package/plugins/gemini/skills/oss_scout/SKILL.md +13 -0
- package/plugins/gemini/skills/research_cache/SKILL.md +13 -0
- package/plugins/gemini/skills/swarm_config/SKILL.md +13 -0
- package/plugins/opencode/index.js +326 -263
- package/prompts/agent_brainstormer.md +97 -0
- package/runner/__pycache__/__init__.cpython-311.pyc +0 -0
- package/runner/__pycache__/auctioneer.cpython-311.pyc +0 -0
- package/runner/__pycache__/auditor_engine.cpython-311.pyc +0 -0
- package/runner/__pycache__/claim_store.cpython-311.pyc +0 -0
- package/runner/__pycache__/claim_witness.cpython-311.pyc +0 -0
- package/runner/__pycache__/living_dossiers.cpython-311.pyc +0 -0
- package/runner/__pycache__/mcp_protocol.cpython-311.pyc +0 -0
- package/runner/__pycache__/mcp_server.cpython-311.pyc +0 -0
- package/runner/__pycache__/pcrb.cpython-311.pyc +0 -0
- package/runner/__pycache__/pcrb_verify.cpython-311.pyc +0 -0
- package/runner/__pycache__/refinement.cpython-311.pyc +0 -0
- package/runner/__pycache__/research_swarm.cpython-311.pyc +0 -0
- package/runner/__pycache__/state_machine.cpython-311.pyc +0 -0
- package/runner/auctioneer.py +169 -0
- package/runner/auditor_engine.py +127 -12
- package/runner/claim_store.py +361 -0
- package/runner/claim_witness.py +183 -0
- package/runner/living_dossiers.py +355 -0
- package/runner/mcp_protocol.py +188 -0
- package/runner/mcp_server.py +567 -0
- package/runner/pcrb.py +212 -0
- package/runner/pcrb_verify.py +237 -0
- package/runner/refinement.py +335 -0
- package/runner/research_swarm.py +378 -94
- package/runner/tests/__pycache__/test_auction_order.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_claim_store.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_claim_witness.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_domain_packs.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_fleet_seam.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_living_dossiers.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_mcp_server.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_pcrb.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_refinement.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_swarm.cpython-311.pyc +0 -0
- package/runner/tests/fixtures/auction_objective.json +35 -0
- package/runner/tests/fixtures/borderline_claims.json +27 -0
- package/runner/tests/fixtures/divergence_objectives.json +31 -0
- package/runner/tests/test_auction_order.py +173 -0
- package/runner/tests/test_claim_store.py +162 -0
- package/runner/tests/test_claim_witness.py +186 -0
- package/runner/tests/test_domain_packs.py +217 -0
- package/runner/tests/test_fleet_seam.py +113 -0
- package/runner/tests/test_living_dossiers.py +204 -0
- package/runner/tests/test_mcp_server.py +212 -0
- package/runner/tests/test_pcrb.py +240 -0
- package/runner/tests/test_refinement.py +255 -0
- package/runner/tests/test_swarm.py +137 -30
- package/scripts/auction_experiment.py +180 -0
- package/scripts/build_adapters.py +183 -0
- package/scripts/divergence_experiment.py +184 -0
- package/skills/brainstorming/SKILL.md +106 -0
- package/skills/brainstorming/__init__.py +1 -0
- package/skills/brainstorming/scripts/brainstorm.py +200 -0
- package/skills/research_cache/__pycache__/__init__.cpython-311.pyc +0 -0
- package/skills/research_cache/__pycache__/hasher.cpython-311.pyc +0 -0
- package/skills/swarm_config/SKILL.md +1 -1
- package/skills/swarm_config/__pycache__/__init__.cpython-311.pyc +0 -0
- package/skills/swarm_config/__pycache__/configure.cpython-311.pyc +0 -0
- package/skills/swarm_config/configure.py +45 -11
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Refinement-type checker for epistemic claims + pure E(D) scoring.
|
|
4
|
+
|
|
5
|
+
Machine-checkable invariants over ClaimWitness records:
|
|
6
|
+
VERIFIED => has source_hash AND verbatim_quote AND passes witness_check
|
|
7
|
+
INFERRED => parent_claims non-empty AND deductive_logic present
|
|
8
|
+
HYPOTHESIS => falsification present (measurable test)
|
|
9
|
+
NEGATIVE_KNOWLEDGE => query AND finding present
|
|
10
|
+
cross-reference => every parent_claims id resolves within the dossier set
|
|
11
|
+
|
|
12
|
+
Also extracts the epistemic score E(D) into a pure function so Domain Packs
|
|
13
|
+
(Stream G) can override constitution without touching the auditor.
|
|
14
|
+
|
|
15
|
+
Constitution defaults reproduce the LEGACY inlined arithmetic exactly
|
|
16
|
+
(flat 1.0 per verified claim), because the published docs' tier-weighted
|
|
17
|
+
V(c_i) has never been what the runtime computes. Tier weighting becomes an
|
|
18
|
+
opt-in constitution — nothing flips without an explicit pack.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from dataclasses import dataclass, field
|
|
22
|
+
from typing import Any, Dict, Iterable, List, Optional, Tuple
|
|
23
|
+
|
|
24
|
+
from runner.claim_witness import (
|
|
25
|
+
KIND_CLAIM,
|
|
26
|
+
KIND_HYPOTHESIS,
|
|
27
|
+
KIND_INFERENCE,
|
|
28
|
+
KIND_NEGATIVE_KNOWLEDGE,
|
|
29
|
+
STATUS_REJECTED,
|
|
30
|
+
TAG_HYPOTHESIS,
|
|
31
|
+
TAG_INFERRED,
|
|
32
|
+
TAG_NEGATIVE_KNOWLEDGE,
|
|
33
|
+
TAG_VERIFIED,
|
|
34
|
+
ClaimWitness,
|
|
35
|
+
witness_check,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass
|
|
40
|
+
class Violation:
|
|
41
|
+
claim_id: str
|
|
42
|
+
rule: str
|
|
43
|
+
message: str
|
|
44
|
+
|
|
45
|
+
def to_dict(self) -> Dict[str, str]:
|
|
46
|
+
return {"claim_id": self.claim_id, "rule": self.rule, "message": self.message}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass
|
|
50
|
+
class Constitution:
|
|
51
|
+
"""Scoring + enforcement policy. Defaults == legacy auditor behavior."""
|
|
52
|
+
|
|
53
|
+
# Per-verified-claim weight. Flat 1.0 reproduces legacy E(D).
|
|
54
|
+
tier_weights: Dict[str, float] = field(
|
|
55
|
+
default_factory=lambda: {"__default__": 1.0}
|
|
56
|
+
)
|
|
57
|
+
neg_bonus: float = 0.5
|
|
58
|
+
reject_penalty: float = 2.5
|
|
59
|
+
accept_threshold: float = 0.65
|
|
60
|
+
|
|
61
|
+
# Domain-pack enforcement (Stream G); empty = no restriction.
|
|
62
|
+
banned_domains: List[str] = field(default_factory=list)
|
|
63
|
+
mandatory_tags: List[str] = field(default_factory=list)
|
|
64
|
+
retraction_policy: str = "standard" # or "zero_tolerance"
|
|
65
|
+
|
|
66
|
+
def weight_for(self, tier: Optional[str]) -> float:
|
|
67
|
+
if tier is not None and tier in self.tier_weights:
|
|
68
|
+
return self.tier_weights[tier]
|
|
69
|
+
return self.tier_weights.get("__default__", 1.0)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
LEGACY_CONSTITUTION = Constitution()
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
# ---------------------------------------------------------------------------
|
|
76
|
+
# Domain Packs (Stream G): pluggable epistemic constitutions
|
|
77
|
+
# ---------------------------------------------------------------------------
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _project_root():
|
|
81
|
+
from pathlib import Path
|
|
82
|
+
|
|
83
|
+
return Path(__file__).resolve().parent.parent
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def load_domain_pack(pack: str) -> Constitution:
|
|
87
|
+
"""Load a Constitution from a pack id (config/domain_packs/<id>.json) or path.
|
|
88
|
+
|
|
89
|
+
Unknown/unloadable packs raise FileNotFoundError — silent fallback to
|
|
90
|
+
legacy would hide a regulatory misconfiguration.
|
|
91
|
+
"""
|
|
92
|
+
import json
|
|
93
|
+
from pathlib import Path
|
|
94
|
+
|
|
95
|
+
candidates = []
|
|
96
|
+
p = Path(pack)
|
|
97
|
+
if p.suffix == ".json" or "/" in pack:
|
|
98
|
+
candidates.append(p)
|
|
99
|
+
else:
|
|
100
|
+
candidates.append(_project_root() / "config" / "domain_packs" / f"{pack}.json")
|
|
101
|
+
|
|
102
|
+
for cand in candidates:
|
|
103
|
+
if cand.exists():
|
|
104
|
+
data = json.loads(cand.read_text(encoding="utf-8"))
|
|
105
|
+
return Constitution(
|
|
106
|
+
tier_weights=data.get("tier_weights") or {"__default__": 1.0},
|
|
107
|
+
neg_bonus=float(data.get("neg_bonus", 0.5)),
|
|
108
|
+
reject_penalty=float(data.get("reject_penalty", 2.5)),
|
|
109
|
+
accept_threshold=float(data.get("accept_threshold", 0.65)),
|
|
110
|
+
banned_domains=list(data.get("banned_domains") or []),
|
|
111
|
+
mandatory_tags=list(data.get("mandatory_tags") or []),
|
|
112
|
+
retraction_policy=str(data.get("retraction_policy") or "standard"),
|
|
113
|
+
)
|
|
114
|
+
raise FileNotFoundError(f"domain pack not found: {pack}")
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def claim_verdict(
|
|
118
|
+
claim: "ClaimWitness",
|
|
119
|
+
hasher: Any = None,
|
|
120
|
+
constitution: Optional[Constitution] = None,
|
|
121
|
+
retracted_hashes: Optional[set] = None,
|
|
122
|
+
) -> Dict[str, Any]:
|
|
123
|
+
"""Per-claim ACCEPTED/REJECTED gate used by Domain Packs.
|
|
124
|
+
|
|
125
|
+
A claim is REJECTED when:
|
|
126
|
+
- any check_invariants rule fires under this constitution
|
|
127
|
+
(incl. banned domains, missing mandatory tags), or
|
|
128
|
+
- constitution.retraction_policy == "zero_tolerance" and its source is
|
|
129
|
+
in `retracted_hashes` (downgrade regardless of quote match or score).
|
|
130
|
+
"""
|
|
131
|
+
from runner.claim_witness import STATUS_STALE, STATUS_SUSPECT
|
|
132
|
+
|
|
133
|
+
constitution = constitution or LEGACY_CONSTITUTION
|
|
134
|
+
violations = check_invariants([claim], hasher=hasher, constitution=constitution)
|
|
135
|
+
|
|
136
|
+
reasons = [f"{v.rule}: {v.message}" for v in violations]
|
|
137
|
+
if (
|
|
138
|
+
constitution.retraction_policy == "zero_tolerance"
|
|
139
|
+
and claim.source_hash
|
|
140
|
+
and retracted_hashes
|
|
141
|
+
and claim.source_hash in retracted_hashes
|
|
142
|
+
):
|
|
143
|
+
reasons.append(
|
|
144
|
+
f"ZERO_TOLERANCE_RETRACTION: source {claim.source_hash} was retracted"
|
|
145
|
+
)
|
|
146
|
+
if claim.status in (STATUS_STALE, STATUS_SUSPECT) and constitution.retraction_policy == "zero_tolerance":
|
|
147
|
+
if not any(r.startswith("ZERO_TOLERANCE_RETRACTION") for r in reasons):
|
|
148
|
+
reasons.append(f"ZERO_TOLERANCE_RETRACTION: claim status {claim.status}")
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
"claim_id": claim.claim_id,
|
|
152
|
+
"verdict": "REJECTED" if reasons else "ACCEPTED",
|
|
153
|
+
"reasons": reasons,
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def check_invariants(
|
|
158
|
+
claims: Iterable[ClaimWitness],
|
|
159
|
+
hasher: Any = None,
|
|
160
|
+
constitution: Optional[Constitution] = None,
|
|
161
|
+
) -> List[Violation]:
|
|
162
|
+
"""Return every invariant violation found across the claim set.
|
|
163
|
+
|
|
164
|
+
Pass `hasher` (a SourceHasher) to enable the live quote check for
|
|
165
|
+
VERIFIED claims; without it, only structural rules are enforced.
|
|
166
|
+
"""
|
|
167
|
+
constitution = constitution or LEGACY_CONSTITUTION
|
|
168
|
+
claims = list(claims)
|
|
169
|
+
known_ids = {c.claim_id for c in claims}
|
|
170
|
+
out: List[Violation] = []
|
|
171
|
+
|
|
172
|
+
for c in claims:
|
|
173
|
+
# --- tag/kind-specific structural rules ---
|
|
174
|
+
if c.tag == TAG_VERIFIED or c.kind == KIND_CLAIM:
|
|
175
|
+
if not c.source_hash:
|
|
176
|
+
out.append(Violation(c.claim_id, "VERIFIED_REQUIRES_HASH", "source_hash missing"))
|
|
177
|
+
if not c.verbatim_quote:
|
|
178
|
+
out.append(Violation(c.claim_id, "VERIFIED_REQUIRES_QUOTE", "verbatim_quote missing"))
|
|
179
|
+
if hasher is not None and c.source_hash and c.verbatim_quote:
|
|
180
|
+
passed, _conf, msg = witness_check(c, hasher)
|
|
181
|
+
if not passed:
|
|
182
|
+
out.append(Violation(c.claim_id, "WITNESS_CHECK_FAILED", msg or "quote not in source"))
|
|
183
|
+
|
|
184
|
+
if c.tag == TAG_INFERRED or c.kind == KIND_INFERENCE:
|
|
185
|
+
if not c.parent_claims:
|
|
186
|
+
out.append(Violation(c.claim_id, "INFERRED_REQUIRES_PARENTS", "parent_claims empty"))
|
|
187
|
+
if not c.deductive_logic:
|
|
188
|
+
out.append(Violation(c.claim_id, "INFERRED_REQUIRES_LOGIC", "deductive_logic missing"))
|
|
189
|
+
|
|
190
|
+
if c.tag == TAG_HYPOTHESIS or c.kind == KIND_HYPOTHESIS:
|
|
191
|
+
if not c.falsification:
|
|
192
|
+
out.append(Violation(c.claim_id, "HYPOTHESIS_REQUIRES_FALSIFICATION", "falsification missing"))
|
|
193
|
+
|
|
194
|
+
if c.tag == TAG_NEGATIVE_KNOWLEDGE or c.kind == KIND_NEGATIVE_KNOWLEDGE:
|
|
195
|
+
if not c.query:
|
|
196
|
+
out.append(Violation(c.claim_id, "NEG_KNOWLEDGE_REQUIRES_QUERY", "query missing"))
|
|
197
|
+
if not c.finding:
|
|
198
|
+
out.append(Violation(c.claim_id, "NEG_KNOWLEDGE_REQUIRES_FINDING", "finding missing"))
|
|
199
|
+
|
|
200
|
+
# --- cross-reference resolution ---
|
|
201
|
+
for parent in c.parent_claims:
|
|
202
|
+
if parent not in known_ids:
|
|
203
|
+
out.append(
|
|
204
|
+
Violation(c.claim_id, "PARENT_UNRESOLVED", f"parent_claims '{parent}' not in dossier set")
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
# --- domain-pack rules ---
|
|
208
|
+
if constitution.mandatory_tags and not c.tag:
|
|
209
|
+
out.append(Violation(c.claim_id, "MISSING_TAG", "tag required by constitution"))
|
|
210
|
+
if constitution.banned_domains and c.source_url:
|
|
211
|
+
for dom in constitution.banned_domains:
|
|
212
|
+
if dom.lower() in c.source_url.lower():
|
|
213
|
+
out.append(
|
|
214
|
+
Violation(c.claim_id, "BANNED_DOMAIN", f"source_url on banned domain '{dom}'")
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
return out
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def compute_epistemic_score(
|
|
221
|
+
counts: Dict[str, int],
|
|
222
|
+
constitution: Optional[Constitution] = None,
|
|
223
|
+
) -> Tuple[float, Dict[str, Any]]:
|
|
224
|
+
"""Pure E(D) — deterministic, bounded to [0, 1], legacy-compatible.
|
|
225
|
+
|
|
226
|
+
Takes the tallies the auditor already computes:
|
|
227
|
+
counts = {"verified": N, "rejected": N, "inferred": N,
|
|
228
|
+
"hypotheses": N, "neg_knowledge": N}
|
|
229
|
+
|
|
230
|
+
Legacy formula (runner/auditor_engine.py, pre-refactor):
|
|
231
|
+
total_assertions = max(1, verified + inferred + hypotheses + rejected)
|
|
232
|
+
raw = (1.0*verified + 0.5*neg_knowledge - 2.5*rejected) / total_assertions
|
|
233
|
+
score = clamp(round(raw, 3), 0.0, 1.0)
|
|
234
|
+
|
|
235
|
+
Returns (score, breakdown). Breakdown carries the same counts back so the
|
|
236
|
+
auditor's summary keys stay byte-identical.
|
|
237
|
+
"""
|
|
238
|
+
constitution = constitution or LEGACY_CONSTITUTION
|
|
239
|
+
|
|
240
|
+
total_verified = int(counts.get("verified", 0))
|
|
241
|
+
total_rejected = int(counts.get("rejected", 0))
|
|
242
|
+
total_inferred = int(counts.get("inferred", 0))
|
|
243
|
+
total_hypotheses = int(counts.get("hypotheses", 0))
|
|
244
|
+
total_neg_knowledge = int(counts.get("neg_knowledge", 0))
|
|
245
|
+
|
|
246
|
+
# Legacy flat weighting: every verified claim counts as 1.0 regardless of
|
|
247
|
+
# tier. A Domain Pack may override tier_weights; then each verified claim
|
|
248
|
+
# would need its tier looked up — that path is exercised by Stream G's
|
|
249
|
+
# `compute_epistemic_score_from_claims`, not here.
|
|
250
|
+
verified_weight = (
|
|
251
|
+
constitution.weight_for(None) * total_verified
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
total_assertions = max(
|
|
255
|
+
1, total_verified + total_inferred + total_hypotheses + total_rejected
|
|
256
|
+
)
|
|
257
|
+
raw_score = (
|
|
258
|
+
verified_weight
|
|
259
|
+
+ constitution.neg_bonus * total_neg_knowledge
|
|
260
|
+
- constitution.reject_penalty * total_rejected
|
|
261
|
+
) / total_assertions
|
|
262
|
+
epistemic_score = max(0.0, min(1.0, round(raw_score, 3)))
|
|
263
|
+
|
|
264
|
+
breakdown = {
|
|
265
|
+
"total_claims_audited": total_verified + total_rejected,
|
|
266
|
+
"verified_passed": total_verified,
|
|
267
|
+
"unverified_rejected": total_rejected,
|
|
268
|
+
"negative_knowledge_count": total_neg_knowledge,
|
|
269
|
+
"inferred_count": total_inferred,
|
|
270
|
+
"hypothesis_count": total_hypotheses,
|
|
271
|
+
"verified_weight": verified_weight,
|
|
272
|
+
"total_assertions": total_assertions,
|
|
273
|
+
"raw_score": raw_score,
|
|
274
|
+
}
|
|
275
|
+
return epistemic_score, breakdown
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def compute_epistemic_score_from_claims(
|
|
279
|
+
claims: Iterable[ClaimWitness],
|
|
280
|
+
constitution: Optional[Constitution] = None,
|
|
281
|
+
) -> Tuple[float, Dict[str, Any]]:
|
|
282
|
+
"""Same E(D), but computed over ClaimWitness records (tier-aware path).
|
|
283
|
+
|
|
284
|
+
Used by Domain Packs (Stream G) where a constitution supplies per-tier
|
|
285
|
+
weights. Falls back to flat 1.0 when the constitution is legacy.
|
|
286
|
+
"""
|
|
287
|
+
constitution = constitution or LEGACY_CONSTITUTION
|
|
288
|
+
counts = {
|
|
289
|
+
"verified": 0,
|
|
290
|
+
"rejected": 0,
|
|
291
|
+
"inferred": 0,
|
|
292
|
+
"hypotheses": 0,
|
|
293
|
+
"neg_knowledge": 0,
|
|
294
|
+
}
|
|
295
|
+
weight_sum = 0.0
|
|
296
|
+
tier_weighted = any(
|
|
297
|
+
w != 1.0 for w in constitution.tier_weights.values()
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
for c in claims:
|
|
301
|
+
if c.status == STATUS_REJECTED or c.tag == "UNVERIFIED_REJECTED":
|
|
302
|
+
counts["rejected"] += 1
|
|
303
|
+
elif c.tag == TAG_VERIFIED:
|
|
304
|
+
counts["verified"] += 1
|
|
305
|
+
weight_sum += constitution.weight_for(c.tier) if tier_weighted else 1.0
|
|
306
|
+
elif c.tag == TAG_INFERRED:
|
|
307
|
+
counts["inferred"] += 1
|
|
308
|
+
elif c.tag == TAG_HYPOTHESIS:
|
|
309
|
+
counts["hypotheses"] += 1
|
|
310
|
+
elif c.tag == TAG_NEGATIVE_KNOWLEDGE:
|
|
311
|
+
counts["neg_knowledge"] += 1
|
|
312
|
+
|
|
313
|
+
total_assertions = max(
|
|
314
|
+
1,
|
|
315
|
+
counts["verified"] + counts["inferred"] + counts["hypotheses"] + counts["rejected"],
|
|
316
|
+
)
|
|
317
|
+
raw_score = (
|
|
318
|
+
weight_sum
|
|
319
|
+
+ constitution.neg_bonus * counts["neg_knowledge"]
|
|
320
|
+
- constitution.reject_penalty * counts["rejected"]
|
|
321
|
+
) / total_assertions
|
|
322
|
+
epistemic_score = max(0.0, min(1.0, round(raw_score, 3)))
|
|
323
|
+
|
|
324
|
+
breakdown = {
|
|
325
|
+
"total_claims_audited": counts["verified"] + counts["rejected"],
|
|
326
|
+
"verified_passed": counts["verified"],
|
|
327
|
+
"unverified_rejected": counts["rejected"],
|
|
328
|
+
"negative_knowledge_count": counts["neg_knowledge"],
|
|
329
|
+
"inferred_count": counts["inferred"],
|
|
330
|
+
"hypothesis_count": counts["hypotheses"],
|
|
331
|
+
"verified_weight": weight_sum,
|
|
332
|
+
"total_assertions": total_assertions,
|
|
333
|
+
"raw_score": raw_score,
|
|
334
|
+
}
|
|
335
|
+
return epistemic_score, breakdown
|