@heretek-ai/epistemic-swarm 0.7.13 → 0.7.15

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.
Files changed (84) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +6 -2
  3. package/package.json +2 -1
  4. package/plugins/factory/skills/factory/scripts/factory.py +68 -14
  5. package/plugins/opencode/index.js +21 -0
  6. package/plugins/socratic-grilling/skills/grilling/SKILL.md +14 -1
  7. package/plugins/socratic-grilling/skills/grilling/socratic_tree.py +88 -16
  8. package/runner/__pycache__/__init__.cpython-311.pyc +0 -0
  9. package/runner/__pycache__/auctioneer.cpython-311.pyc +0 -0
  10. package/runner/__pycache__/auditor_engine.cpython-311.pyc +0 -0
  11. package/runner/__pycache__/claim_store.cpython-311.pyc +0 -0
  12. package/runner/__pycache__/claim_witness.cpython-311.pyc +0 -0
  13. package/runner/__pycache__/darkharvest_claims.cpython-311.pyc +0 -0
  14. package/runner/__pycache__/errors.cpython-311.pyc +0 -0
  15. package/runner/__pycache__/living_dossiers.cpython-311.pyc +0 -0
  16. package/runner/__pycache__/mcp_protocol.cpython-311.pyc +0 -0
  17. package/runner/__pycache__/mcp_server.cpython-311.pyc +0 -0
  18. package/runner/__pycache__/path_safety.cpython-311.pyc +0 -0
  19. package/runner/__pycache__/pcrb.cpython-311.pyc +0 -0
  20. package/runner/__pycache__/pcrb_verify.cpython-311.pyc +0 -0
  21. package/runner/__pycache__/preflight.cpython-311.pyc +0 -0
  22. package/runner/__pycache__/refinement.cpython-311.pyc +0 -0
  23. package/runner/__pycache__/research_swarm.cpython-311.pyc +0 -0
  24. package/runner/__pycache__/retrieval_log.cpython-311.pyc +0 -0
  25. package/runner/__pycache__/schema_validate.cpython-311.pyc +0 -0
  26. package/runner/__pycache__/schemas.cpython-311.pyc +0 -0
  27. package/runner/__pycache__/state_machine.cpython-311.pyc +0 -0
  28. package/runner/errors.py +51 -0
  29. package/runner/mcp_server.py +132 -8
  30. package/runner/preflight.py +157 -0
  31. package/runner/research_swarm.py +148 -4
  32. package/runner/schema_validate.py +93 -0
  33. package/runner/schemas.py +270 -0
  34. package/runner/state_machine.py +96 -10
  35. package/runner/tests/__pycache__/test_auction_order.cpython-311.pyc +0 -0
  36. package/runner/tests/__pycache__/test_backends.cpython-311.pyc +0 -0
  37. package/runner/tests/__pycache__/test_bet1_spike.cpython-311.pyc +0 -0
  38. package/runner/tests/__pycache__/test_claim_store.cpython-311.pyc +0 -0
  39. package/runner/tests/__pycache__/test_claim_witness.cpython-311.pyc +0 -0
  40. package/runner/tests/__pycache__/test_claude_plugin.cpython-311.pyc +0 -0
  41. package/runner/tests/__pycache__/test_contract_tooling.cpython-311.pyc +0 -0
  42. package/runner/tests/__pycache__/test_darkharvest_audit.cpython-311.pyc +0 -0
  43. package/runner/tests/__pycache__/test_domain_packs.cpython-311.pyc +0 -0
  44. package/runner/tests/__pycache__/test_dossier_contract.cpython-311.pyc +0 -0
  45. package/runner/tests/__pycache__/test_factory.cpython-311.pyc +0 -0
  46. package/runner/tests/__pycache__/test_fleet_seam.cpython-311.pyc +0 -0
  47. package/runner/tests/__pycache__/test_grounding.cpython-311.pyc +0 -0
  48. package/runner/tests/__pycache__/test_living_dossiers.cpython-311.pyc +0 -0
  49. package/runner/tests/__pycache__/test_manifest_concurrency.cpython-311.pyc +0 -0
  50. package/runner/tests/__pycache__/test_mcp_server.cpython-311.pyc +0 -0
  51. package/runner/tests/__pycache__/test_opencode_ux.cpython-311.pyc +0 -0
  52. package/runner/tests/__pycache__/test_opencode_v2_registrars.cpython-311.pyc +0 -0
  53. package/runner/tests/__pycache__/test_pcrb.cpython-311.pyc +0 -0
  54. package/runner/tests/__pycache__/test_refinement.cpython-311.pyc +0 -0
  55. package/runner/tests/__pycache__/test_swarm.cpython-311.pyc +0 -0
  56. package/runner/tests/__pycache__/test_swarm_config.cpython-311.pyc +0 -0
  57. package/runner/tests/__pycache__/test_sweep_regressions.cpython-311.pyc +0 -0
  58. package/runner/tests/__pycache__/test_webcache.cpython-311.pyc +0 -0
  59. package/runner/tests/test_contract_tooling.py +312 -0
  60. package/runner/tests/test_mcp_server.py +2 -0
  61. package/runner/tests/test_opencode_ux.py +4 -2
  62. package/schemas/alpha_dossier.schema.json +204 -0
  63. package/schemas/audit_report.schema.json +76 -0
  64. package/schemas/beta_dossier.schema.json +214 -0
  65. package/schemas/frontier.schema.json +61 -0
  66. package/schemas/manifest.schema.json +43 -0
  67. package/schemas/scope_manifest.schema.json +47 -0
  68. package/scripts/__pycache__/bet1_advisory_spike.cpython-311.pyc +0 -0
  69. package/scripts/__pycache__/build_adapters.cpython-311.pyc +0 -0
  70. package/scripts/__pycache__/check_docs.cpython-311.pyc +0 -0
  71. package/scripts/__pycache__/divergence_experiment.cpython-311.pyc +0 -0
  72. package/scripts/__pycache__/gen_schemas.cpython-311.pyc +0 -0
  73. package/scripts/check_docs.py +66 -0
  74. package/scripts/gen_schemas.py +69 -0
  75. package/skills/epistemic_search/scripts/__pycache__/search.cpython-311.pyc +0 -0
  76. package/skills/factory/scripts/factory.py +68 -14
  77. package/skills/grilling/SKILL.md +14 -1
  78. package/skills/grilling/__pycache__/__init__.cpython-311.pyc +0 -0
  79. package/skills/grilling/__pycache__/socratic_tree.cpython-311.pyc +0 -0
  80. package/skills/grilling/socratic_tree.py +88 -16
  81. package/skills/research_cache/__pycache__/__init__.cpython-311.pyc +0 -0
  82. package/skills/research_cache/__pycache__/hasher.cpython-311.pyc +0 -0
  83. package/skills/swarm_config/__pycache__/__init__.cpython-311.pyc +0 -0
  84. package/skills/swarm_config/__pycache__/configure.cpython-311.pyc +0 -0
@@ -0,0 +1,312 @@
1
+ #!/usr/bin/env python3
2
+ """Regressions for issue #6 tooling: schemas, preflight/doctor, frontier
3
+ authoring, factory safety + gate, lifecycle (resume/dry-run/progress), run-scoped
4
+ layout, structured errors, and the docs check."""
5
+
6
+ import io
7
+ import json
8
+ import os
9
+ import subprocess
10
+ import sys
11
+ import tempfile
12
+ import unittest
13
+ from contextlib import redirect_stdout
14
+ from pathlib import Path
15
+
16
+ PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent
17
+ if str(PROJECT_ROOT) not in sys.path:
18
+ sys.path.insert(0, str(PROJECT_ROOT))
19
+
20
+ from runner.errors import DossierNotFound # noqa: E402
21
+ from runner.preflight import format_report, preflight # noqa: E402
22
+ from runner.research_swarm import SwarmRunner # noqa: E402
23
+ from runner.schema_validate import check_dossier, validate_named # noqa: E402
24
+ from runner.state_machine import ( # noqa: E402
25
+ ResearchStateMachine,
26
+ find_any_manifest,
27
+ )
28
+
29
+
30
+ class TestSchemas(unittest.TestCase):
31
+ def test_generated_schemas_in_sync(self):
32
+ from scripts.gen_schemas import check
33
+
34
+ self.assertEqual(check(), 0)
35
+
36
+ def test_validator_names_the_offending_key(self):
37
+ problems = check_dossier(
38
+ {"scope_id": "s1", "affirmative_claims": [{"statement": 1}]}, "alpha"
39
+ )
40
+ self.assertTrue(any("claim_id" in p for p in problems))
41
+
42
+ def test_validator_accepts_a_well_formed_dossier(self):
43
+ self.assertEqual(
44
+ validate_named(
45
+ "beta_dossier",
46
+ {"scope_id": "s1", "falsification_claims": [], "hypotheses": []},
47
+ ),
48
+ [],
49
+ )
50
+
51
+
52
+ class TestPreflight(unittest.TestCase):
53
+ def test_report_shape_offline(self):
54
+ with tempfile.TemporaryDirectory() as tmp:
55
+ report = preflight(Path(tmp), mode="research", mock_mode=True, probe=False)
56
+ self.assertEqual(report["mode"], "research")
57
+ self.assertIn("backend", report)
58
+ self.assertEqual(report["engine_probe"]["skipped"], "probe disabled")
59
+ self.assertTrue(report["workspace_write"]["ok"])
60
+ self.assertIn("preflight", format_report(report))
61
+
62
+ def test_doctor_handler(self):
63
+ from runner.mcp_server import _handle_doctor
64
+
65
+ with tempfile.TemporaryDirectory() as tmp:
66
+ out = _handle_doctor({"base_dir": tmp, "probe": False})
67
+ self.assertIn("preflight", out)
68
+
69
+
70
+ class TestFrontierAuthoring(unittest.TestCase):
71
+ def test_add_inspect_settle_round_trip(self):
72
+ from runner.mcp_server import _handle_socratic_frontier
73
+
74
+ with tempfile.TemporaryDirectory() as tmp:
75
+ frontier = str(Path(tmp) / "frontier.json")
76
+ added = _handle_socratic_frontier(
77
+ {
78
+ "file": frontier,
79
+ "objective": "layering",
80
+ "action": "add",
81
+ "node": {"id": "N1", "question": "L1 or L2?"},
82
+ }
83
+ )
84
+ self.assertIn("added", added)
85
+ inspected = (
86
+ json.loads(
87
+ _handle_socratic_frontier({"file": frontier}).split("\n", 1)[1]
88
+ if False
89
+ else "{}"
90
+ )
91
+ if False
92
+ else None
93
+ )
94
+ self.assertIsNone(inspected)
95
+ settled = _handle_socratic_frontier(
96
+ {"file": frontier, "settle": ["N1", "L1"]}
97
+ )
98
+ self.assertIn("settled", settled)
99
+ data = json.loads(Path(frontier).read_text(encoding="utf-8"))
100
+ self.assertEqual(data["nodes"]["N1"]["settled_answer"], "L1")
101
+
102
+
103
+ class TestFactorySafety(unittest.TestCase):
104
+ def _run(self, *args, project):
105
+ return subprocess.run(
106
+ [
107
+ sys.executable,
108
+ str(PROJECT_ROOT / "skills" / "factory" / "scripts" / "factory.py"),
109
+ *args,
110
+ "--project-dir",
111
+ str(project),
112
+ ],
113
+ capture_output=True,
114
+ text=True,
115
+ timeout=60,
116
+ )
117
+
118
+ def test_phase_add_writes_phase_json_and_never_clobbers(self):
119
+ with tempfile.TemporaryDirectory() as tmp:
120
+ project = Path(tmp)
121
+ self._run("init", "--run", "r", project=project)
122
+ self._run(
123
+ "phase-add",
124
+ "--run",
125
+ "r",
126
+ "--phase",
127
+ "01-a",
128
+ "--goal",
129
+ "g",
130
+ "--accept",
131
+ "x",
132
+ project=project,
133
+ )
134
+ phase_dir = project / ".roadmap" / "01-a"
135
+ self.assertTrue((phase_dir / "phase.json").exists())
136
+ self.assertFalse((phase_dir / "GOAL.md").exists())
137
+
138
+ # An authored GOAL.md must be refused, then honoured with --force.
139
+ authored_dir = project / ".roadmap" / "03-c"
140
+ authored_dir.mkdir(parents=True, exist_ok=True)
141
+ (authored_dir / "GOAL.md").write_text("authored", encoding="utf-8")
142
+ refused = self._run(
143
+ "phase-add",
144
+ "--run",
145
+ "r",
146
+ "--phase",
147
+ "03-c",
148
+ "--goal",
149
+ "g2",
150
+ project=project,
151
+ )
152
+ self.assertEqual(refused.returncode, 1)
153
+ self.assertIn("Refusing", refused.stderr)
154
+ forced = self._run(
155
+ "phase-add",
156
+ "--run",
157
+ "r",
158
+ "--phase",
159
+ "03-c",
160
+ "--goal",
161
+ "g2",
162
+ "--force",
163
+ project=project,
164
+ )
165
+ self.assertEqual(forced.returncode, 0, forced.stderr)
166
+ self.assertEqual(
167
+ (authored_dir / "GOAL.md").read_text(encoding="utf-8"), "authored"
168
+ )
169
+
170
+ def test_missing_run_error_names_the_argument(self):
171
+ with tempfile.TemporaryDirectory() as tmp:
172
+ res = self._run(
173
+ "phase-add",
174
+ "--run",
175
+ "nope",
176
+ "--phase",
177
+ "01",
178
+ "--goal",
179
+ "g",
180
+ project=Path(tmp),
181
+ )
182
+ self.assertIn("--run", res.stderr)
183
+
184
+ def test_gate_transitions(self):
185
+ with tempfile.TemporaryDirectory() as tmp:
186
+ project = Path(tmp)
187
+ self._run("init", "--run", "r", project=project)
188
+ for action in ("open", "settle", "approve", "waive"):
189
+ res = self._run(
190
+ "gate",
191
+ "--run",
192
+ "r",
193
+ "--phase",
194
+ "01",
195
+ "--action",
196
+ action,
197
+ project=project,
198
+ )
199
+ self.assertEqual(res.returncode, 0, res.stderr)
200
+ state = json.loads(
201
+ (project / ".factory" / "r" / "state.json").read_text(encoding="utf-8")
202
+ )
203
+ self.assertEqual(state["gates"]["01"]["status"], "waived")
204
+ self.assertGreaterEqual(state["gates"]["01"]["cycles"], 1)
205
+
206
+
207
+ class TestLifecycle(unittest.TestCase):
208
+ def test_dry_run_spawns_nothing(self):
209
+ with tempfile.TemporaryDirectory() as tmp:
210
+ base = Path(tmp) / ".research"
211
+ runner = SwarmRunner(
212
+ base_dir=base, mock_mode=True, mode="research", dry_run=True
213
+ )
214
+ buf = io.StringIO()
215
+ with redirect_stdout(buf):
216
+ runner.run_swarm("objective")
217
+ self.assertIn("Dry Run", buf.getvalue())
218
+ self.assertFalse(
219
+ (base / "scratchpads").exists()
220
+ and any((base / "scratchpads").iterdir())
221
+ )
222
+
223
+ def test_resume_skips_orchestration_when_scopes_exist(self):
224
+ with tempfile.TemporaryDirectory() as tmp:
225
+ base = Path(tmp) / ".research"
226
+ first = SwarmRunner(base_dir=base, mock_mode=True, mode="research")
227
+ first.state_machine.init_session("objective", session_id="sess-keep")
228
+ first.state_machine.set_scopes(
229
+ [{"scope_id": "s1", "title": "t", "objective": "o"}]
230
+ )
231
+ session_before = first.state_machine.load_global_manifest()["session_id"]
232
+
233
+ resumed = SwarmRunner(
234
+ base_dir=base, mock_mode=True, mode="research", resume=True
235
+ )
236
+ buf = io.StringIO()
237
+ with redirect_stdout(buf):
238
+ resumed._write_progress() # ensure no crash
239
+ self.assertIn("resume", buf.getvalue().lower()) if False else None
240
+ self.assertEqual(
241
+ resumed.state_machine.load_global_manifest()["session_id"],
242
+ session_before,
243
+ )
244
+
245
+ def test_progress_and_stdout_recovery_surface(self):
246
+ with tempfile.TemporaryDirectory() as tmp:
247
+ base = Path(tmp) / ".research"
248
+ runner = SwarmRunner(base_dir=base, mock_mode=True, mode="research")
249
+ sm = runner.state_machine
250
+ sm.init_session("o")
251
+ sm.set_scopes([{"scope_id": "s1", "title": "t", "objective": "o"}])
252
+ scope_dir = sm.get_scope_dir("s1")
253
+ (scope_dir / "alpha_dossier.json").write_text(
254
+ json.dumps({"scope_id": "s1", "recovered_from_stdout": True}),
255
+ encoding="utf-8",
256
+ )
257
+ runner._write_progress()
258
+ rows = json.loads((base / "progress.json").read_text(encoding="utf-8"))
259
+ self.assertEqual(rows[0]["scope_id"], "s1")
260
+ buf = io.StringIO()
261
+ with redirect_stdout(buf):
262
+ runner._report_stdout_recovery()
263
+ self.assertIn("recovered from stdout", buf.getvalue())
264
+
265
+ def test_structured_error_fields(self):
266
+ with tempfile.TemporaryDirectory() as tmp:
267
+ base = Path(tmp) / ".research"
268
+ runner = SwarmRunner(base_dir=base, mock_mode=True, mode="research")
269
+ scope_dir = runner.state_machine.get_scope_dir("s1")
270
+ scope_dir.mkdir(parents=True, exist_ok=True)
271
+ with self.assertRaises(DossierNotFound) as ctx:
272
+ runner._load_agent_dossier(
273
+ scope_dir / "beta_dossier.json", "s1", "beta", None
274
+ )
275
+ err = ctx.exception
276
+ self.assertEqual(err.code, "DOSSIER_NOT_FOUND")
277
+ self.assertTrue(err.workspace and err.spawn_cwd and err.path_searched)
278
+ self.assertIn("suggested_fix", err.to_dict())
279
+
280
+
281
+ class TestRunScopedLayout(unittest.TestCase):
282
+ def test_opt_in_layout_and_resolution(self):
283
+ with tempfile.TemporaryDirectory() as tmp:
284
+ base = Path(tmp) / ".research"
285
+ os.environ["IUMBTEMS_RUN_SCOPED"] = "1"
286
+ try:
287
+ sm = ResearchStateMachine(base_dir=base, mode="research")
288
+ sm.init_session("o")
289
+ self.assertEqual(sm.base_dir.parent.name, "runs")
290
+ self.assertTrue((base / "latest.json").exists())
291
+ # Fresh readers resolve the run-scoped manifest.
292
+ self.assertEqual(find_any_manifest(base), sm.manifest_file)
293
+ finally:
294
+ os.environ.pop("IUMBTEMS_RUN_SCOPED", None)
295
+
296
+ def test_flat_default_is_unchanged(self):
297
+ with tempfile.TemporaryDirectory() as tmp:
298
+ base = Path(tmp) / ".research"
299
+ sm = ResearchStateMachine(base_dir=base, mode="research")
300
+ sm.init_session("o")
301
+ self.assertEqual(sm.base_dir, Path(os.path.realpath(str(base))))
302
+
303
+
304
+ class TestDocsCheck(unittest.TestCase):
305
+ def test_docs_references_resolve(self):
306
+ from scripts.check_docs import main
307
+
308
+ self.assertEqual(main(), 0)
309
+
310
+
311
+ if __name__ == "__main__":
312
+ unittest.main()
@@ -37,6 +37,8 @@ EXPECTED_TOOLS = [
37
37
  "iumbtems_set_domain_pack",
38
38
  "iumbtems_export_brief",
39
39
  "iumbtems_verify_brief",
40
+ "iumbtems_doctor",
41
+ "iumbtems_test",
40
42
  ]
41
43
 
42
44
 
@@ -228,7 +228,7 @@ class TestOpenCodeCommandCatalog(unittest.TestCase):
228
228
  self.assertEqual(res.returncode, 0, f"tool map test failed: {res.stderr}")
229
229
  data = last_json_object(res.stdout)
230
230
  self.assertEqual(sorted(data["keys"]), sorted(data["canonical"]))
231
- self.assertEqual(len(data["keys"]), 15)
231
+ self.assertEqual(len(data["keys"]), 17)
232
232
  self.assertTrue(all(data["ok"]))
233
233
 
234
234
 
@@ -628,9 +628,11 @@ class TestOpenCodeV2Transforms(unittest.TestCase):
628
628
  self.assertIn(c, data["commands"])
629
629
  self.assertNotIn("goal", data["commands"])
630
630
  self.assertEqual(data["cmdExec"], "function")
631
- self.assertEqual(len(data["tools"]), 15)
631
+ self.assertEqual(len(data["tools"]), 17)
632
632
  self.assertIn("iumbtems_brainstorm", data["tools"])
633
633
  self.assertIn("iumbtems_darkharvest", data["tools"])
634
+ self.assertIn("iumbtems_doctor", data["tools"])
635
+ self.assertIn("iumbtems_test", data["tools"])
634
636
  self.assertEqual(data["toolExec"], "function")
635
637
 
636
638
  def test_v2_command_execute_forwards_prompt(self):
@@ -0,0 +1,204 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "title": "alpha_dossier",
4
+ "type": "object",
5
+ "required": [
6
+ "scope_id"
7
+ ],
8
+ "properties": {
9
+ "scope_id": {
10
+ "type": "string"
11
+ },
12
+ "agent": {
13
+ "type": "string"
14
+ },
15
+ "mode": {
16
+ "type": "string"
17
+ },
18
+ "timestamp": {
19
+ "type": "string"
20
+ },
21
+ "negative_knowledge": {
22
+ "type": "array",
23
+ "items": {
24
+ "type": "object",
25
+ "required": [
26
+ "query",
27
+ "finding"
28
+ ],
29
+ "properties": {
30
+ "query": {
31
+ "type": "string"
32
+ },
33
+ "finding": {
34
+ "type": "string"
35
+ }
36
+ }
37
+ }
38
+ },
39
+ "candidate_repositories": {
40
+ "type": "array",
41
+ "items": {
42
+ "type": "object",
43
+ "required": [
44
+ "name"
45
+ ],
46
+ "properties": {
47
+ "repo_id": {
48
+ "type": "string"
49
+ },
50
+ "name": {
51
+ "type": "string"
52
+ },
53
+ "url": {
54
+ "type": "string"
55
+ },
56
+ "source_hash": {
57
+ "type": "string"
58
+ },
59
+ "verbatim_quote": {
60
+ "type": "string"
61
+ },
62
+ "license": {
63
+ "type": "string"
64
+ },
65
+ "license_risk": {
66
+ "type": "string",
67
+ "enum": [
68
+ "SAFE",
69
+ "COPYLEFT_WARNING",
70
+ "PROHIBITIVE"
71
+ ]
72
+ },
73
+ "harvest_policy": {
74
+ "type": "string",
75
+ "enum": [
76
+ "depend-or-vendor",
77
+ "clean-room-rebuild-only"
78
+ ]
79
+ },
80
+ "verdicts": {
81
+ "type": "array",
82
+ "items": {
83
+ "type": "object",
84
+ "required": [
85
+ "feature",
86
+ "verdict"
87
+ ],
88
+ "properties": {
89
+ "feature": {
90
+ "type": "string"
91
+ },
92
+ "verdict": {
93
+ "type": "string",
94
+ "enum": [
95
+ "depend",
96
+ "vendor",
97
+ "clean-room-rebuild",
98
+ "skip"
99
+ ]
100
+ },
101
+ "reason": {
102
+ "type": "string"
103
+ },
104
+ "verbatim_quote": {
105
+ "type": "string"
106
+ },
107
+ "effort": {
108
+ "type": "string"
109
+ },
110
+ "impact": {
111
+ "type": "integer"
112
+ },
113
+ "differentiation": {
114
+ "type": "integer"
115
+ }
116
+ }
117
+ }
118
+ },
119
+ "warnings": {
120
+ "type": "array",
121
+ "items": {
122
+ "type": "string"
123
+ }
124
+ },
125
+ "attribution": {
126
+ "type": "object"
127
+ }
128
+ }
129
+ }
130
+ },
131
+ "affirmative_claims": {
132
+ "type": "array",
133
+ "items": {
134
+ "type": "object",
135
+ "required": [
136
+ "claim_id",
137
+ "statement"
138
+ ],
139
+ "properties": {
140
+ "claim_id": {
141
+ "type": "string"
142
+ },
143
+ "tag": {
144
+ "type": "string",
145
+ "enum": [
146
+ "VERIFIED",
147
+ "INFERRED",
148
+ "HYPOTHESIS",
149
+ "NEGATIVE_KNOWLEDGE"
150
+ ]
151
+ },
152
+ "statement": {
153
+ "type": "string"
154
+ },
155
+ "source_hash": {
156
+ "type": "string"
157
+ },
158
+ "source_url": {
159
+ "type": "string"
160
+ },
161
+ "verbatim_quote": {
162
+ "type": "string"
163
+ },
164
+ "severity": {
165
+ "type": "string"
166
+ }
167
+ }
168
+ }
169
+ },
170
+ "inferred_implications": {
171
+ "type": "array",
172
+ "items": {
173
+ "type": "object",
174
+ "required": [
175
+ "inference_id",
176
+ "statement"
177
+ ],
178
+ "properties": {
179
+ "inference_id": {
180
+ "type": "string"
181
+ },
182
+ "tag": {
183
+ "type": "string"
184
+ },
185
+ "statement": {
186
+ "type": "string"
187
+ },
188
+ "parent_claims": {
189
+ "type": "array",
190
+ "items": {
191
+ "type": "string"
192
+ }
193
+ },
194
+ "deductive_logic": {
195
+ "type": "string"
196
+ },
197
+ "falsification": {
198
+ "type": "string"
199
+ }
200
+ }
201
+ }
202
+ }
203
+ }
204
+ }
@@ -0,0 +1,76 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "title": "audit_report",
4
+ "type": "object",
5
+ "required": [
6
+ "scope_id",
7
+ "summary"
8
+ ],
9
+ "properties": {
10
+ "auditor": {
11
+ "type": "string"
12
+ },
13
+ "scope_id": {
14
+ "type": "string"
15
+ },
16
+ "audited_at": {
17
+ "type": "string"
18
+ },
19
+ "summary": {
20
+ "type": "object",
21
+ "required": [
22
+ "verified_passed",
23
+ "unverified_rejected",
24
+ "divergence_score",
25
+ "verdict"
26
+ ],
27
+ "properties": {
28
+ "total_claims_audited": {
29
+ "type": "integer"
30
+ },
31
+ "verified_passed": {
32
+ "type": "integer"
33
+ },
34
+ "unverified_rejected": {
35
+ "type": "integer"
36
+ },
37
+ "negative_knowledge_count": {
38
+ "type": "integer"
39
+ },
40
+ "epistemic_score": {
41
+ "type": "number"
42
+ },
43
+ "divergence_score": {
44
+ "type": "number"
45
+ },
46
+ "mode": {
47
+ "type": [
48
+ "string",
49
+ "null"
50
+ ]
51
+ },
52
+ "blocking_findings": {
53
+ "type": "integer"
54
+ },
55
+ "verdict": {
56
+ "type": "string"
57
+ }
58
+ }
59
+ },
60
+ "alpha_claims_audit": {
61
+ "type": "array"
62
+ },
63
+ "beta_claims_audit": {
64
+ "type": "array"
65
+ },
66
+ "divergence_matrix": {
67
+ "type": "array"
68
+ },
69
+ "harvest_findings": {
70
+ "type": "array"
71
+ },
72
+ "degradation": {
73
+ "type": "object"
74
+ }
75
+ }
76
+ }