@heretek-ai/epistemic-swarm 0.7.12 → 0.7.14
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/.claude-plugin/plugin.json +1 -1
- package/README.md +5 -1
- package/package.json +2 -1
- package/plugins/factory/skills/factory/scripts/factory.py +68 -14
- package/plugins/socratic-grilling/skills/grilling/SKILL.md +14 -1
- package/plugins/socratic-grilling/skills/grilling/socratic_tree.py +88 -16
- package/prompts/agent_darkharvest.md +20 -1
- 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__/darkharvest_claims.cpython-311.pyc +0 -0
- package/runner/__pycache__/errors.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__/path_safety.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__/preflight.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__/retrieval_log.cpython-311.pyc +0 -0
- package/runner/__pycache__/schema_validate.cpython-311.pyc +0 -0
- package/runner/__pycache__/schemas.cpython-311.pyc +0 -0
- package/runner/__pycache__/state_machine.cpython-311.pyc +0 -0
- package/runner/auditor_engine.py +101 -10
- package/runner/darkharvest_claims.py +326 -0
- package/runner/errors.py +51 -0
- package/runner/mcp_server.py +132 -8
- package/runner/preflight.py +157 -0
- package/runner/research_swarm.py +178 -5
- package/runner/schema_validate.py +93 -0
- package/runner/schemas.py +270 -0
- package/runner/state_machine.py +131 -10
- package/runner/tests/__pycache__/test_auction_order.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_backends.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_bet1_spike.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_claude_plugin.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_contract_tooling.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_darkharvest_audit.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_domain_packs.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_dossier_contract.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_factory.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_fleet_seam.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_grounding.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_living_dossiers.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_manifest_concurrency.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_mcp_server.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_opencode_ux.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_opencode_v2_registrars.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/__pycache__/test_swarm_config.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_sweep_regressions.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_webcache.cpython-311.pyc +0 -0
- package/runner/tests/test_contract_tooling.py +312 -0
- package/runner/tests/test_darkharvest_audit.py +290 -0
- package/runner/tests/test_mcp_server.py +2 -0
- package/schemas/alpha_dossier.schema.json +204 -0
- package/schemas/audit_report.schema.json +76 -0
- package/schemas/beta_dossier.schema.json +214 -0
- package/schemas/frontier.schema.json +61 -0
- package/schemas/manifest.schema.json +43 -0
- package/schemas/scope_manifest.schema.json +47 -0
- package/scripts/__pycache__/bet1_advisory_spike.cpython-311.pyc +0 -0
- package/scripts/__pycache__/build_adapters.cpython-311.pyc +0 -0
- package/scripts/__pycache__/check_docs.cpython-311.pyc +0 -0
- package/scripts/__pycache__/divergence_experiment.cpython-311.pyc +0 -0
- package/scripts/__pycache__/gen_schemas.cpython-311.pyc +0 -0
- package/scripts/check_docs.py +66 -0
- package/scripts/gen_schemas.py +69 -0
- package/skills/epistemic_search/scripts/__pycache__/search.cpython-311.pyc +0 -0
- package/skills/factory/scripts/factory.py +68 -14
- package/skills/grilling/SKILL.md +14 -1
- package/skills/grilling/__pycache__/__init__.cpython-311.pyc +0 -0
- package/skills/grilling/__pycache__/socratic_tree.cpython-311.pyc +0 -0
- package/skills/grilling/socratic_tree.py +88 -16
- 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/__pycache__/__init__.cpython-311.pyc +0 -0
- package/skills/swarm_config/__pycache__/configure.cpython-311.pyc +0 -0
package/README.md
CHANGED
|
@@ -199,11 +199,13 @@ iumbtems adapters
|
|
|
199
199
|
# Other utility commands
|
|
200
200
|
iumbtems install # install skills & MCP servers into ~/.claude/
|
|
201
201
|
iumbtems marketplace # print the Claude Code marketplace catalog
|
|
202
|
-
iumbtems doctor #
|
|
202
|
+
iumbtems doctor # environment + preflight health check
|
|
203
203
|
iumbtems test # run the automated test suite
|
|
204
204
|
iumbtems help # full usage text
|
|
205
205
|
```
|
|
206
206
|
|
|
207
|
+
Run-shaping flags: `--resume` (continue a session instead of re-orchestrating) and `--dry-run` (validate config/backends/engine without spawning agents). Frontier authoring is fully tool-driven: `socratic_tree.py --add-node/--settle/--export`.
|
|
208
|
+
|
|
207
209
|
Common options: `--mock-claude` (synthetic responses, zero API cost), `--frontier <file>` (settled `frontier.json`), `--mode <research|audit|scout|hybrid|brainstorm|darkharvest>`, `--engine <duckduckgo|brave|firecrawl|searxng>`, `--depth <1-4>`, `--backend <auto|claude|opencode>`, `--dir <path>`.
|
|
208
210
|
|
|
209
211
|
The `epistemic-swarm` binary is an alias of `iumbtems`.
|
|
@@ -231,6 +233,8 @@ The canonical MCP server (`python3 runner/mcp_server.py`, stdio) exposes these t
|
|
|
231
233
|
| `iumbtems_report_retraction` | Record a RETRACTED/REVISED event for a cached source; dependent `VERIFIED` claims degrade to STALE/SUSPECT on the next audit. |
|
|
232
234
|
| `iumbtems_check_staleness` | One claim-degradation pass: join claims against retraction events, write the status ledger, queue re-runs for degraded scopes. Never mutates dossiers. |
|
|
233
235
|
| `iumbtems_set_domain_pack` | Activate a Regulated Domain Pack (`biopharma` / `quant` / `legal` epistemic constitution) for subsequent audits. |
|
|
236
|
+
| `iumbtems_doctor` | Preflight health check: plugin version, backend + resolved binary, search-engine live probe, workspace write test. Run first when something looks wrong. |
|
|
237
|
+
| `iumbtems_test` | Run the packaged test suite in a subprocess and report pass/fail with the tail. |
|
|
234
238
|
|
|
235
239
|
---
|
|
236
240
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heretek-ai/epistemic-swarm",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.14",
|
|
4
4
|
"description": "IUMBTEMS (Epistemic Swarm) — High-Integrity Dialectic Research Agent Harness for Claude Code, OpenCode V2, Pi (pi.dev), OMP (oh-my-pi), Gemini CLI, Codex CLI, and AntiGravity",
|
|
5
5
|
"main": "bin/cli.js",
|
|
6
6
|
"bin": {
|
|
@@ -118,6 +118,7 @@
|
|
|
118
118
|
"bin",
|
|
119
119
|
"prompts",
|
|
120
120
|
"skills",
|
|
121
|
+
"schemas",
|
|
121
122
|
"hooks",
|
|
122
123
|
"config",
|
|
123
124
|
"runner",
|
|
@@ -55,7 +55,10 @@ def run_dir(run):
|
|
|
55
55
|
def load_state(run):
|
|
56
56
|
p = run_dir(run) / "state.json"
|
|
57
57
|
if not p.exists():
|
|
58
|
-
raise SystemExit(
|
|
58
|
+
raise SystemExit(
|
|
59
|
+
f"No factory run '{run}'. Pass --run <name> (or run "
|
|
60
|
+
f"`factory.py init --run <name>` first)."
|
|
61
|
+
)
|
|
59
62
|
return json.loads(p.read_text(encoding="utf-8"))
|
|
60
63
|
|
|
61
64
|
|
|
@@ -82,6 +85,19 @@ def cmd_phase_add(args):
|
|
|
82
85
|
state = load_state(args.run)
|
|
83
86
|
if args.phase in state["phases"]:
|
|
84
87
|
raise SystemExit(f"Phase '{args.phase}' already exists.")
|
|
88
|
+
# Phase output goes to phase.json — a run-state helper must never clobber the
|
|
89
|
+
# author-written GOAL.md / dossier.json it is meant to track (#6 item 2.3).
|
|
90
|
+
# This check runs BEFORE any state mutation so a refusal leaves no trace.
|
|
91
|
+
phase_dir = ROADMAP_DIR / args.phase
|
|
92
|
+
authored = [
|
|
93
|
+
name for name in ("GOAL.md", "dossier.json") if (phase_dir / name).exists()
|
|
94
|
+
]
|
|
95
|
+
if authored and not getattr(args, "force", False):
|
|
96
|
+
raise SystemExit(
|
|
97
|
+
f"Refusing to touch author-written file(s) in {phase_dir}: "
|
|
98
|
+
f"{', '.join(authored)}. phase-add now writes phase.json only; "
|
|
99
|
+
f"re-run with --force to overwrite."
|
|
100
|
+
)
|
|
85
101
|
state["phases"][args.phase] = {
|
|
86
102
|
"goal": args.goal,
|
|
87
103
|
"acceptance": [a.strip() for a in args.accept.split(";") if a.strip()],
|
|
@@ -90,30 +106,52 @@ def cmd_phase_add(args):
|
|
|
90
106
|
"qa_reports": [],
|
|
91
107
|
}
|
|
92
108
|
save_state(args.run, state)
|
|
93
|
-
# Phase output: GOAL.md + dossier.json skeleton (evidence filled by manager).
|
|
94
|
-
phase_dir = ROADMAP_DIR / args.phase
|
|
95
109
|
phase_dir.mkdir(parents=True, exist_ok=True)
|
|
96
|
-
(phase_dir / "
|
|
97
|
-
f"# {args.phase}: {args.goal}\n\n## Acceptance\n"
|
|
98
|
-
+ "".join(f"- [ ] {a}\n" for a in state["phases"][args.phase]["acceptance"]),
|
|
99
|
-
encoding="utf-8",
|
|
100
|
-
)
|
|
101
|
-
(phase_dir / "dossier.json").write_text(
|
|
110
|
+
(phase_dir / "phase.json").write_text(
|
|
102
111
|
json.dumps(
|
|
103
112
|
{
|
|
104
113
|
"phase": args.phase,
|
|
105
114
|
"goal": args.goal,
|
|
106
|
-
"evidence": [],
|
|
107
115
|
"acceptance": state["phases"][args.phase]["acceptance"],
|
|
108
|
-
"
|
|
109
|
-
"verdict": "briefed",
|
|
110
|
-
"hashes": [],
|
|
116
|
+
"status": "briefed",
|
|
111
117
|
},
|
|
112
118
|
indent=2,
|
|
113
119
|
),
|
|
114
120
|
encoding="utf-8",
|
|
115
121
|
)
|
|
116
|
-
print(f"✅ Phase '{args.phase}' briefed → {phase_dir}")
|
|
122
|
+
print(f"✅ Phase '{args.phase}' briefed → {phase_dir / 'phase.json'}")
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def cmd_gate(args):
|
|
126
|
+
"""First-class gate bookkeeping (open/settle/approve/waive/escalate/count)."""
|
|
127
|
+
state = load_state(args.run)
|
|
128
|
+
gates = state.setdefault("gates", {})
|
|
129
|
+
gate = gates.setdefault(
|
|
130
|
+
args.phase, {"status": "closed", "cycles": 0, "waivers": [], "history": []}
|
|
131
|
+
)
|
|
132
|
+
action = (args.action or "").lower()
|
|
133
|
+
if action == "open":
|
|
134
|
+
gate["status"] = "open"
|
|
135
|
+
gate["cycles"] = gate.get("cycles", 0) + 1
|
|
136
|
+
state["gate_cycles"] = state.get("gate_cycles", 0) + 1
|
|
137
|
+
elif action == "settle":
|
|
138
|
+
gate["status"] = "settled"
|
|
139
|
+
elif action == "approve":
|
|
140
|
+
gate["status"] = "approved"
|
|
141
|
+
elif action == "waive":
|
|
142
|
+
gate["status"] = "waived"
|
|
143
|
+
gate["waivers"].append(args.reason or "")
|
|
144
|
+
elif action == "escalate":
|
|
145
|
+
gate["status"] = "escalated"
|
|
146
|
+
gate["cycles"] = gate.get("cycles", 0) + 1
|
|
147
|
+
state["gate_cycles"] = state.get("gate_cycles", 0) + 1
|
|
148
|
+
elif action != "count":
|
|
149
|
+
raise SystemExit(
|
|
150
|
+
"gate --action must be one of: open, settle, approve, waive, escalate, count"
|
|
151
|
+
)
|
|
152
|
+
gate["history"].append({"action": action, "at": now(), "reason": args.reason or ""})
|
|
153
|
+
save_state(args.run, state)
|
|
154
|
+
print(json.dumps({"phase": args.phase, "gate": gate}, indent=2))
|
|
117
155
|
|
|
118
156
|
|
|
119
157
|
def cmd_qa_record(args):
|
|
@@ -211,6 +249,11 @@ def main():
|
|
|
211
249
|
p.add_argument("--phase", required=True)
|
|
212
250
|
p.add_argument("--goal", required=True)
|
|
213
251
|
p.add_argument("--accept", default="")
|
|
252
|
+
p.add_argument(
|
|
253
|
+
"--force",
|
|
254
|
+
action="store_true",
|
|
255
|
+
help="Overwrite author-written GOAL.md/dossier.json if present",
|
|
256
|
+
)
|
|
214
257
|
_add_common(p)
|
|
215
258
|
p = sub.add_parser("qa-record")
|
|
216
259
|
p.add_argument("--run", required=True)
|
|
@@ -227,6 +270,16 @@ def main():
|
|
|
227
270
|
p = sub.add_parser("stop")
|
|
228
271
|
p.add_argument("--run", required=True)
|
|
229
272
|
_add_common(p)
|
|
273
|
+
p = sub.add_parser("gate")
|
|
274
|
+
p.add_argument("--run", required=True)
|
|
275
|
+
p.add_argument("--phase", required=True)
|
|
276
|
+
p.add_argument(
|
|
277
|
+
"--action",
|
|
278
|
+
required=True,
|
|
279
|
+
choices=["open", "settle", "approve", "waive", "escalate", "count"],
|
|
280
|
+
)
|
|
281
|
+
p.add_argument("--reason", default="")
|
|
282
|
+
_add_common(p)
|
|
230
283
|
|
|
231
284
|
args = ap.parse_args()
|
|
232
285
|
root = resolve_project_root(args.project_dir)
|
|
@@ -238,6 +291,7 @@ def main():
|
|
|
238
291
|
"qa-record": cmd_qa_record,
|
|
239
292
|
"expansion": cmd_expansion,
|
|
240
293
|
"stop": cmd_stop,
|
|
294
|
+
"gate": cmd_gate,
|
|
241
295
|
}[args.command](args)
|
|
242
296
|
sys.exit(code if isinstance(code, int) else 0)
|
|
243
297
|
|
|
@@ -42,7 +42,20 @@ Always challenge default premises in Round 1:
|
|
|
42
42
|
|
|
43
43
|
## 5. FRONTIER RESOLUTION & FREEZING
|
|
44
44
|
|
|
45
|
+
The frontier is fully tool-drivable (no hand-written JSON):
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# author a node (prerequisites gate when it appears on the frontier)
|
|
49
|
+
python3 skills/grilling/socratic_tree.py --add-node --id N1 \
|
|
50
|
+
--question "L1 or L2 for state verification?" --depends-on ""
|
|
51
|
+
# settle a node
|
|
52
|
+
python3 skills/grilling/socratic_tree.py --settle N1 "L1 for finality"
|
|
53
|
+
# inspect / freeze
|
|
54
|
+
python3 skills/grilling/socratic_tree.py --show-frontier
|
|
55
|
+
python3 skills/grilling/socratic_tree.py --export
|
|
56
|
+
```
|
|
57
|
+
|
|
45
58
|
When every branch of the design tree has been visited and the frontier is empty:
|
|
46
59
|
1. Summarize the settled constraints.
|
|
47
|
-
2.
|
|
60
|
+
2. Freeze the settled state to `.research/frontier.json` (`--export`, default path).
|
|
48
61
|
3. Hand off the settled frontier to the **Swarm Orchestrator** to begin empirical dialectic execution.
|
|
@@ -11,10 +11,17 @@ import argparse
|
|
|
11
11
|
from pathlib import Path
|
|
12
12
|
from typing import Dict, List, Optional, Any
|
|
13
13
|
|
|
14
|
+
|
|
14
15
|
class DecisionNode:
|
|
15
|
-
def __init__(
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
node_id: str,
|
|
19
|
+
title: str,
|
|
20
|
+
question: str,
|
|
21
|
+
recommended: str,
|
|
22
|
+
options: Optional[List[str]] = None,
|
|
23
|
+
prerequisites: Optional[List[str]] = None,
|
|
24
|
+
):
|
|
18
25
|
self.node_id = node_id
|
|
19
26
|
self.title = title
|
|
20
27
|
self.question = question
|
|
@@ -34,18 +41,18 @@ class DecisionNode:
|
|
|
34
41
|
"recommended": self.recommended,
|
|
35
42
|
"options": self.options,
|
|
36
43
|
"prerequisites": self.prerequisites,
|
|
37
|
-
"settled_answer": self.settled_answer
|
|
44
|
+
"settled_answer": self.settled_answer,
|
|
38
45
|
}
|
|
39
46
|
|
|
40
47
|
@classmethod
|
|
41
|
-
def from_dict(cls, data: Dict[str, Any]) ->
|
|
48
|
+
def from_dict(cls, data: Dict[str, Any]) -> "DecisionNode":
|
|
42
49
|
node = cls(
|
|
43
50
|
node_id=data["node_id"],
|
|
44
51
|
title=data["title"],
|
|
45
52
|
question=data["question"],
|
|
46
53
|
recommended=data["recommended"],
|
|
47
54
|
options=data.get("options", []),
|
|
48
|
-
prerequisites=data.get("prerequisites", [])
|
|
55
|
+
prerequisites=data.get("prerequisites", []),
|
|
49
56
|
)
|
|
50
57
|
node.settled_answer = data.get("settled_answer")
|
|
51
58
|
return node
|
|
@@ -82,7 +89,9 @@ class DesignTree:
|
|
|
82
89
|
self.nodes[node_id].settled_answer = answer
|
|
83
90
|
|
|
84
91
|
def is_complete(self) -> bool:
|
|
85
|
-
return len(self.compute_frontier()) == 0 and all(
|
|
92
|
+
return len(self.compute_frontier()) == 0 and all(
|
|
93
|
+
n.is_settled() for n in self.nodes.values()
|
|
94
|
+
)
|
|
86
95
|
|
|
87
96
|
def export_frontier_json(self, output_path: Path):
|
|
88
97
|
safe_path = Path(os.path.realpath(str(output_path)))
|
|
@@ -93,13 +102,13 @@ class DesignTree:
|
|
|
93
102
|
"nodes": {k: v.to_dict() for k, v in self.nodes.items()},
|
|
94
103
|
"settled_constraints": {
|
|
95
104
|
k: v.settled_answer for k, v in self.nodes.items() if v.is_settled()
|
|
96
|
-
}
|
|
105
|
+
},
|
|
97
106
|
}
|
|
98
107
|
with open(safe_path, "w", encoding="utf-8") as f:
|
|
99
108
|
json.dump(data, f, indent=2)
|
|
100
109
|
|
|
101
110
|
@classmethod
|
|
102
|
-
def load_from_json(cls, file_path: Path) ->
|
|
111
|
+
def load_from_json(cls, file_path: Path) -> "DesignTree":
|
|
103
112
|
safe_path = Path(os.path.realpath(str(file_path)))
|
|
104
113
|
with open(safe_path, "r", encoding="utf-8") as f:
|
|
105
114
|
data = json.load(f)
|
|
@@ -111,12 +120,16 @@ class DesignTree:
|
|
|
111
120
|
|
|
112
121
|
def _display_frontier(tree: DesignTree, frontier: list) -> None:
|
|
113
122
|
print(f"\n🎯 Objective: {tree.objective}")
|
|
114
|
-
print(
|
|
123
|
+
print(
|
|
124
|
+
f"📊 Total Nodes: {len(tree.nodes)} | Settled: {sum(1 for n in tree.nodes.values() if n.is_settled())}"
|
|
125
|
+
)
|
|
115
126
|
if not frontier:
|
|
116
127
|
if tree.nodes and tree.is_complete():
|
|
117
128
|
print("✅ Frontier is EMPTY. All prerequisite branches are fully settled!")
|
|
118
129
|
else:
|
|
119
|
-
print(
|
|
130
|
+
print(
|
|
131
|
+
"ℹ️ No active frontier nodes. Define new decision nodes to begin grilling."
|
|
132
|
+
)
|
|
120
133
|
else:
|
|
121
134
|
print(f"\n⚡ Current Active Frontier ({len(frontier)} questions ready):")
|
|
122
135
|
for idx, node in enumerate(frontier, 1):
|
|
@@ -126,11 +139,49 @@ def _display_frontier(tree: DesignTree, frontier: list) -> None:
|
|
|
126
139
|
|
|
127
140
|
|
|
128
141
|
def main():
|
|
129
|
-
parser = argparse.ArgumentParser(
|
|
142
|
+
parser = argparse.ArgumentParser(
|
|
143
|
+
description="Epistemic Swarm Socratic Decision Tree"
|
|
144
|
+
)
|
|
130
145
|
parser.add_argument("--objective", type=str, help="Research objective")
|
|
131
|
-
parser.add_argument(
|
|
132
|
-
|
|
133
|
-
|
|
146
|
+
parser.add_argument(
|
|
147
|
+
"--file",
|
|
148
|
+
type=str,
|
|
149
|
+
default=".research/frontier.json",
|
|
150
|
+
help="Path to frontier.json",
|
|
151
|
+
)
|
|
152
|
+
parser.add_argument(
|
|
153
|
+
"--show-frontier",
|
|
154
|
+
action="store_true",
|
|
155
|
+
help="Print the current decision frontier",
|
|
156
|
+
)
|
|
157
|
+
parser.add_argument(
|
|
158
|
+
"--settle",
|
|
159
|
+
nargs=2,
|
|
160
|
+
metavar=("NODE_ID", "ANSWER"),
|
|
161
|
+
help="Settle a decision node",
|
|
162
|
+
)
|
|
163
|
+
parser.add_argument(
|
|
164
|
+
"--add-node",
|
|
165
|
+
action="store_true",
|
|
166
|
+
help="Add a decision node (with --id/--question)",
|
|
167
|
+
)
|
|
168
|
+
parser.add_argument("--id", type=str, help="Node id (with --add-node)")
|
|
169
|
+
parser.add_argument("--question", type=str, help="Node question (with --add-node)")
|
|
170
|
+
parser.add_argument(
|
|
171
|
+
"--title", type=str, default="", help="Node title (defaults to --id)"
|
|
172
|
+
)
|
|
173
|
+
parser.add_argument(
|
|
174
|
+
"--recommended", type=str, default="", help="Recommended option"
|
|
175
|
+
)
|
|
176
|
+
parser.add_argument(
|
|
177
|
+
"--depends-on",
|
|
178
|
+
type=str,
|
|
179
|
+
default="",
|
|
180
|
+
help="Comma-separated prerequisite node ids",
|
|
181
|
+
)
|
|
182
|
+
parser.add_argument(
|
|
183
|
+
"--export", action="store_true", help="Write the frontier JSON and exit"
|
|
184
|
+
)
|
|
134
185
|
|
|
135
186
|
args = parser.parse_args()
|
|
136
187
|
frontier_path = Path(os.path.realpath(str(args.file)))
|
|
@@ -141,14 +192,35 @@ def main():
|
|
|
141
192
|
objective = args.objective or "Epistemic Swarm Research Objective"
|
|
142
193
|
tree = DesignTree(objective=objective)
|
|
143
194
|
|
|
195
|
+
if args.add_node:
|
|
196
|
+
if not args.id or not args.question:
|
|
197
|
+
parser.error("--add-node requires --id and --question")
|
|
198
|
+
prereqs = [p.strip() for p in (args.depends_on or "").split(",") if p.strip()]
|
|
199
|
+
tree.add_node(
|
|
200
|
+
DecisionNode(
|
|
201
|
+
node_id=args.id,
|
|
202
|
+
title=args.title or args.id,
|
|
203
|
+
question=args.question,
|
|
204
|
+
recommended=args.recommended or "",
|
|
205
|
+
options=[],
|
|
206
|
+
prerequisites=prereqs,
|
|
207
|
+
)
|
|
208
|
+
)
|
|
209
|
+
tree.export_frontier_json(frontier_path)
|
|
210
|
+
print(f"Added node {args.id} -> {frontier_path}")
|
|
211
|
+
|
|
144
212
|
if args.settle:
|
|
145
213
|
node_id, answer = args.settle
|
|
146
214
|
tree.settle_node(node_id, answer)
|
|
147
215
|
tree.export_frontier_json(frontier_path)
|
|
148
216
|
print(f"Settled {node_id} -> {answer}")
|
|
149
217
|
|
|
218
|
+
if args.export:
|
|
219
|
+
tree.export_frontier_json(frontier_path)
|
|
220
|
+
print(f"Exported frontier -> {frontier_path}")
|
|
221
|
+
|
|
150
222
|
frontier = tree.compute_frontier()
|
|
151
|
-
if args.show_frontier or not args.settle:
|
|
223
|
+
if args.show_frontier or not (args.settle or args.add_node or args.export):
|
|
152
224
|
_display_frontier(tree, frontier)
|
|
153
225
|
|
|
154
226
|
|
|
@@ -33,6 +33,17 @@ partial per capability), benchmark/API quotes, and per-feature harvest
|
|
|
33
33
|
proposals. Every cell needs `[VERIFIED: <hash>]` from
|
|
34
34
|
`.research/sources/<sha256>.md` or `file://<path>#L<start>-L<end>`.
|
|
35
35
|
|
|
36
|
+
Discover and cache every cited source before you cite it (zero-key, no API keys):
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
python3 skills/epistemic_search/scripts/search.py "<query>"
|
|
40
|
+
python3 skills/research_cache/hasher.py cache --url "<URL>" --content "$(cat fetched.md)" --title "<TITLE>"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Confirm a repository actually exists (`owner/repo`) before citing it. A run that
|
|
44
|
+
never searches caches zero sources and is flagged `WARNING_LOW_GROUNDING` — the
|
|
45
|
+
runner prints `retrieval: N queries, M results, K cached` as evidence either way.
|
|
46
|
+
|
|
36
47
|
### Beta — Red Team
|
|
37
48
|
License gate (LICENSE file + package metadata → SPDX), transitive weight,
|
|
38
49
|
CVE/takeover notes, staleness (`⚠️ STALE` when >12mo or solo-maintainer),
|
|
@@ -51,7 +62,9 @@ AND what we lack). Gates are warn-only: badge inline, never auto-skip.
|
|
|
51
62
|
## 4. OUTPUT SPECIFICATION
|
|
52
63
|
|
|
53
64
|
Per scope write `alpha_dossier.json` / `beta_dossier.json` with
|
|
54
|
-
`candidate_repositories[]` entries
|
|
65
|
+
`candidate_repositories[]` entries. Every entry MUST carry `source_hash` **and**
|
|
66
|
+
`verbatim_quote` (an exact substring of the cached `.research/sources/<hash>.md`);
|
|
67
|
+
entries without both are rejected as unverified by the audit.
|
|
55
68
|
|
|
56
69
|
```json
|
|
57
70
|
{
|
|
@@ -59,6 +72,7 @@ Per scope write `alpha_dossier.json` / `beta_dossier.json` with
|
|
|
59
72
|
"name": "owner/repo",
|
|
60
73
|
"url": "https://github.com/owner/repo",
|
|
61
74
|
"source_hash": "<sha256>",
|
|
75
|
+
"verbatim_quote": "<exact substring of the cached source>",
|
|
62
76
|
"license": "Apache-2.0",
|
|
63
77
|
"license_risk": "SAFE | COPYLEFT_WARNING | PROHIBITIVE",
|
|
64
78
|
"harvest_policy": "depend-or-vendor | clean-room-rebuild-only",
|
|
@@ -89,3 +103,8 @@ candidates.
|
|
|
89
103
|
3. No unverified harvest verdicts. Metadata ranks; only VERIFIED harvests.
|
|
90
104
|
4. No unbounded expansion past `--max-repos` or per-repo caps without user
|
|
91
105
|
confirmation. Firecrawl missing → gap note, never abort.
|
|
106
|
+
5. **Do NOT self-report audit results.** Never emit `epistemic_audit`,
|
|
107
|
+
`confidence`, `verified_*`, `unverified_*`, `mean_epistemic_score`, or any
|
|
108
|
+
`*.verified` / `*.total_sources` field. The runner computes verification from
|
|
109
|
+
cached witnesses; agent-declared counts are ignored (renamed to
|
|
110
|
+
`self_reported_*`) and only mislead a human reader.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|