@aperant/framework 0.8.4 → 0.8.7

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 (127) hide show
  1. package/CHANGELOG.md +185 -0
  2. package/agents/apt-planner.md +34 -3
  3. package/dist/cli/commands/coverage-check.d.mts.map +1 -1
  4. package/dist/cli/commands/coverage-check.mjs +74 -7
  5. package/dist/cli/commands/coverage-check.mjs.map +1 -1
  6. package/dist/cli/commands/detect-runtime.d.mts.map +1 -1
  7. package/dist/cli/commands/detect-runtime.mjs +18 -12
  8. package/dist/cli/commands/detect-runtime.mjs.map +1 -1
  9. package/dist/cli/coverage-check/user-outcomes.d.mts +84 -0
  10. package/dist/cli/coverage-check/user-outcomes.d.mts.map +1 -0
  11. package/dist/cli/coverage-check/user-outcomes.mjs +265 -0
  12. package/dist/cli/coverage-check/user-outcomes.mjs.map +1 -0
  13. package/dist/cli/host/detect.mjs +1 -1
  14. package/dist/cli/host/detect.mjs.map +1 -1
  15. package/dist/cli/install/install-from-source.mjs +3 -3
  16. package/dist/cli/install/legacy-paths.d.mts.map +1 -1
  17. package/dist/cli/install/legacy-paths.mjs +2 -0
  18. package/dist/cli/install/legacy-paths.mjs.map +1 -1
  19. package/dist/cli/install/manifest.d.mts +13 -0
  20. package/dist/cli/install/manifest.d.mts.map +1 -1
  21. package/dist/cli/install/manifest.mjs +5 -0
  22. package/dist/cli/install/manifest.mjs.map +1 -1
  23. package/dist/cli/install/pipeline.d.mts +15 -0
  24. package/dist/cli/install/pipeline.d.mts.map +1 -1
  25. package/dist/cli/install/pipeline.mjs +27 -0
  26. package/dist/cli/install/pipeline.mjs.map +1 -1
  27. package/dist/cli/install/transforms/codex.d.mts +1 -1
  28. package/dist/cli/install/transforms/codex.d.mts.map +1 -1
  29. package/dist/cli/install/transforms/codex.mjs +43 -2
  30. package/dist/cli/install/transforms/codex.mjs.map +1 -1
  31. package/dist/cli/install/version-header.d.mts.map +1 -1
  32. package/dist/cli/install/version-header.mjs +7 -1
  33. package/dist/cli/install/version-header.mjs.map +1 -1
  34. package/dist/cli/verify-proof/idl/index.d.mts +1 -1
  35. package/dist/cli/verify-proof/idl/index.mjs +7 -8
  36. package/dist/cli/verify-proof/idl/index.mjs.map +1 -1
  37. package/dist/cli/verify-proof/idl/types.d.ts +5 -4
  38. package/dist/cli/verify-proof/idl/types.d.ts.map +1 -1
  39. package/dist/cli/verify-proof/idl/types.js +4 -3
  40. package/dist/cli/verify-proof/idl/types.js.map +1 -1
  41. package/dist/cli/verify-proof/resolver.d.mts +63 -1
  42. package/dist/cli/verify-proof/resolver.d.mts.map +1 -1
  43. package/dist/cli/verify-proof/resolver.mjs +164 -4
  44. package/dist/cli/verify-proof/resolver.mjs.map +1 -1
  45. package/dist/cli/verify-proof/runtime-detect.d.mts +6 -3
  46. package/dist/cli/verify-proof/runtime-detect.d.mts.map +1 -1
  47. package/dist/cli/verify-proof/runtime-detect.mjs +104 -11
  48. package/dist/cli/verify-proof/runtime-detect.mjs.map +1 -1
  49. package/dist/cli/verify-proof/trust.d.mts +1 -1
  50. package/dist/cli/verify-proof/trust.d.mts.map +1 -1
  51. package/dist/cli/verify-proof/trust.mjs +1 -1
  52. package/dist/driver-sdk/conformance.d.ts +41 -0
  53. package/dist/driver-sdk/conformance.d.ts.map +1 -0
  54. package/dist/driver-sdk/conformance.js +123 -0
  55. package/dist/driver-sdk/conformance.js.map +1 -0
  56. package/dist/driver-sdk/errors.d.ts +75 -0
  57. package/dist/driver-sdk/errors.d.ts.map +1 -0
  58. package/dist/driver-sdk/errors.js +80 -0
  59. package/dist/driver-sdk/errors.js.map +1 -0
  60. package/dist/driver-sdk/idl.d.ts +221 -0
  61. package/dist/driver-sdk/idl.d.ts.map +1 -0
  62. package/dist/driver-sdk/idl.js +140 -0
  63. package/dist/driver-sdk/idl.js.map +1 -0
  64. package/dist/driver-sdk/index.d.ts +28 -0
  65. package/dist/driver-sdk/index.d.ts.map +1 -0
  66. package/dist/driver-sdk/index.js +28 -0
  67. package/dist/driver-sdk/index.js.map +1 -0
  68. package/dist/driver-sdk/manifest.d.ts +93 -0
  69. package/dist/driver-sdk/manifest.d.ts.map +1 -0
  70. package/dist/driver-sdk/manifest.js +12 -0
  71. package/dist/driver-sdk/manifest.js.map +1 -0
  72. package/dist/driver-sdk/retry.d.ts +33 -0
  73. package/dist/driver-sdk/retry.d.ts.map +1 -0
  74. package/dist/driver-sdk/retry.js +50 -0
  75. package/dist/driver-sdk/retry.js.map +1 -0
  76. package/dist/plugin/.claude-plugin/plugin.json +2 -1
  77. package/dist/plugin/agents/apt-planner.md +34 -3
  78. package/dist/plugin/skills/apt-plan/SKILL.md +50 -0
  79. package/dist/plugin/skills/apt-pr-review/SKILL.md +1 -1
  80. package/dist/plugin/skills/apt-research/SKILL.md +526 -0
  81. package/dist/plugin/skills/apt-research/appendices/budget-loop.md +397 -0
  82. package/dist/plugin/skills/apt-research/appendices/claim-graph-schema.md +206 -0
  83. package/dist/plugin/skills/apt-research/appendices/domain-tools.md +236 -0
  84. package/dist/plugin/skills/apt-spar/SKILL.md +19 -7
  85. package/dist/plugin/skills/apt-verify-proof/SKILL.md +41 -2
  86. package/dist/schemas/quick-task.d.ts +17 -17
  87. package/drivers/.gitkeep +0 -0
  88. package/drivers/api/README.md +40 -0
  89. package/drivers/api/driver.mjs +59 -0
  90. package/drivers/api/manifest.json +26 -0
  91. package/drivers/browser/README.md +105 -0
  92. package/drivers/browser/driver.mjs +134 -0
  93. package/drivers/browser/manifest.json +35 -0
  94. package/drivers/cli/README.md +44 -0
  95. package/drivers/cli/driver.mjs +62 -0
  96. package/drivers/cli/manifest.json +28 -0
  97. package/drivers/electron/README.md +64 -0
  98. package/drivers/electron/driver.mjs +87 -0
  99. package/drivers/electron/manifest.json +37 -0
  100. package/package.json +7 -3
  101. package/skills/apt-plan/SKILL.md +50 -0
  102. package/skills/apt-planner.md +16 -0
  103. package/skills/apt-pr-review/SKILL.md +1 -1
  104. package/skills/apt-research/SKILL.md +526 -0
  105. package/skills/apt-research/appendices/budget-loop.md +397 -0
  106. package/skills/apt-research/appendices/claim-graph-schema.md +206 -0
  107. package/skills/apt-research/appendices/domain-tools.md +236 -0
  108. package/skills/apt-spar/SKILL.md +19 -7
  109. package/skills/apt-verify-proof/SKILL.md +41 -2
  110. package/src/cli/commands/coverage-check.mjs +126 -44
  111. package/src/cli/commands/detect-runtime.mjs +23 -14
  112. package/src/cli/coverage-check/user-outcomes.mjs +273 -0
  113. package/src/cli/host/detect.mjs +1 -1
  114. package/src/cli/install/install-from-source.mjs +3 -3
  115. package/src/cli/install/legacy-paths.mjs +2 -0
  116. package/src/cli/install/manifest.mjs +5 -0
  117. package/src/cli/install/pipeline.mjs +28 -0
  118. package/src/cli/install/transforms/codex.mjs +51 -2
  119. package/src/cli/install/version-header.mjs +7 -1
  120. package/src/cli/verify-proof/idl/index.mjs +7 -8
  121. package/src/cli/verify-proof/idl/types.ts +5 -4
  122. package/src/cli/verify-proof/manifest-schema.json +2 -1
  123. package/src/cli/verify-proof/resolver.mjs +171 -4
  124. package/src/cli/verify-proof/runtime-detect.mjs +99 -10
  125. package/src/cli/verify-proof/trust.mjs +1 -1
  126. package/templates/proof-verification.md +32 -3
  127. package/workflows/verify-proof.md +78 -9
@@ -0,0 +1,397 @@
1
+ # Time-Budgeted Background Loop
2
+
3
+ > Per-iteration loop spec for `/apt:research --budget <duration>`. Load
4
+ > when any budget is set (explicit flag, named alias, or prose-detected)
5
+ > OR when running a single session that needs the 4-role discipline.
6
+
7
+ ## Why this exists
8
+
9
+ A naive "keep researching" loop manufactures false confidence over time —
10
+ the agent finds more weak sources that confirm its initial position, prunes
11
+ counter-evidence as noise, and arrives at the budget cap with a polished
12
+ wrong answer. This appendix encodes the disciplines that prevent that:
13
+
14
+ - **4-role specialization** so a single agent can't self-rationalize
15
+ - **Priority by impact × centrality × uncertainty** so load-bearing claims
16
+ get attacked early, not last
17
+ - **Trigger-gated spar** so cross-LLM challenges fire on real conflict, not
18
+ on a clock
19
+ - **Citation audit** so fabricated DOIs get quarantined, not amplified
20
+ - **Dual stop states (CONVERGED vs STALLED)** so the skill never confuses
21
+ stuckness with done-ness
22
+ - **Early termination** so a budget is a CAP not a target — converging at
23
+ hour 3 of an 8h budget stops the loop, doesn't burn the remaining 5h
24
+
25
+ ## Budget as the primitive
26
+
27
+ The skill accepts one canonical flag plus three named aliases:
28
+
29
+ | Flag | Resolves to | Typical use |
30
+ |---|---|---|
31
+ | `--budget <duration>` | Explicit cap; ISO-style `30m`, `4h`, `10h`, `2d` (1m – 72h range) | Any custom horizon |
32
+ | `--overnight` | `--budget 8h` | While you sleep |
33
+ | `--weekend` | `--budget 48h` | Long horizon, Friday → Monday |
34
+ | `--background` | `--budget 4h` | During work day |
35
+
36
+ Auto-detection from prose (Step 2 of SKILL.md) maps natural-language signals
37
+ into a budget value:
38
+
39
+ | Prose signal | Inferred budget |
40
+ |---|---|
41
+ | "overnight", "while I sleep" | 8h |
42
+ | "weekend", "over the weekend", "by Monday" | 48h |
43
+ | "for X hours/days/minutes" | X |
44
+ | "while I'm at work" | 8h |
45
+ | "in the background" | 4h |
46
+ | "really thoroughly", "deep dive", "question my beliefs" | 4h |
47
+ | Decomposition produces ≥8 sub-questions OR ≥3 contested perspectives | 4h |
48
+ | (no signal AND no flag) | none — single-session pipeline runs |
49
+
50
+ The auto-detection announcement (Step 2 closing line) MUST surface BOTH the
51
+ budget value AND the signal that triggered it:
52
+
53
+ ```
54
+ [apt:research] {Mode} / {Domain} — auto-budget 8h from signal "overnight".
55
+ Pass --budget <duration> to override or --no-budget to disable.
56
+ ```
57
+
58
+ The user can pass `--no-budget` to force the single-session pipeline even
59
+ when prose signals would otherwise trigger budget mode. Explicit user
60
+ intent always wins.
61
+
62
+ ## Single-session pipeline (no budget set)
63
+
64
+ If no budget is set AND no prose signal fires:
65
+ - Run Step 5 (the 4-role iteration) up to 5 times
66
+ - After 5 iterations without terminal state, force `STALLED` with
67
+ `## Cannot resolve in single-session mode — re-run with --budget 4h or higher`
68
+ - This is the cap that prevents the lighter single-session mode from
69
+ silently growing into something that should have been a budgeted run
70
+
71
+ ## Agent roles
72
+
73
+ Per iteration, FOUR `apt-researcher` agents are spawned in a single
74
+ parallel message. Each has an isolated context (no shared scratch state)
75
+ and a distinct objective.
76
+
77
+ ### Builder
78
+
79
+ > System prompt seed: "You are a synthesizer. Read the current claim graph
80
+ > and the spec. Advance the synthesis on the chosen attack claim. Propose
81
+ > what we should now believe, given the evidence, and why. You may NOT
82
+ > falsify — that is the Skeptic's job. You may propose new claims if they
83
+ > follow from existing evidence."
84
+
85
+ Tool surface: full read (Read/Grep/Glob), WebSearch, WebFetch, Context7
86
+ MCP, plus the domain-specific tools from `domain-tools.md`.
87
+
88
+ Output shape: a structured block written to the Task return:
89
+ ```
90
+ ## Builder
91
+ ### Position
92
+ {one paragraph synthesis}
93
+ ### Supporting evidence (existing)
94
+ - {source_id}: {claim_id} — {one-line why it supports}
95
+ ### New claims proposed (if any)
96
+ - {statement} — {evidence_for source_ids} — {depends_on claim_ids}
97
+ ```
98
+
99
+ ### Skeptic
100
+
101
+ > System prompt seed: "You are an adversary. Read the chosen attack claim.
102
+ > Your only job is to find concrete counter-evidence or a logical/empirical
103
+ > falsification. Confirming the claim or hedging is failure. Produce at
104
+ > least one specific challenge with a citation OR explicit reasoning."
105
+
106
+ Tool surface: same as Builder.
107
+
108
+ Output shape:
109
+ ```
110
+ ## Skeptic
111
+ ### Falsification target
112
+ {claim_id}: {statement}
113
+ ### Counter-evidence
114
+ - {source_id or new source}: {quote} — {why it falsifies}
115
+ ### Failure mode of the claim
116
+ {one paragraph — when does this claim break? What context invalidates it?}
117
+ ### Verdict
118
+ {retract | downgrade | contest | no-falsification-found}
119
+ ```
120
+
121
+ If `no-falsification-found` AND the claim was at `confidence: high`,
122
+ the Skeptic must explicitly state: "I attempted falsification via [search
123
+ queries / reasoning paths] and found no counter-evidence. The claim
124
+ survives this attack."
125
+
126
+ ### Retriever
127
+
128
+ > System prompt seed: "You are an evidence finder. Read the spec, the
129
+ > claim graph, and the list of sources already in sources.json. Your
130
+ > ONLY job is to find NEW sources (not already in sources.json) that
131
+ > bear on any open or contested claim. Do NOT synthesize. Do NOT take
132
+ > sides. Return URLs + extracted quotes + source-type classification."
133
+
134
+ Tool surface: WebSearch, WebFetch, Context7, plus domain-specific
135
+ search tools (PubMed for empirical, GitHub Code Search for code, arxiv
136
+ for mechanistic).
137
+
138
+ Output shape:
139
+ ```
140
+ ## Retriever
141
+ ### New sources found
142
+ - {url}: tier={tier}, type={type}, derivation_chain={[urls cited]},
143
+ quote_anchor={"..."}
144
+ ### Search trail
145
+ - query: "{q}" — engine: {WebSearch|PubMed|GitHub|...} — useful_hits: {N}
146
+ ### Frontier status
147
+ {exhausted | active | promising} — {one-line why}
148
+ ```
149
+
150
+ The `frontier status: exhausted` signal is load-bearing for the STALLED
151
+ stop state. Retriever must report it honestly — if every new search
152
+ returns sources already in `sources.json` (modulo near-duplicates),
153
+ the frontier IS exhausted for this iteration's queries. Judge uses this
154
+ to decide stop state.
155
+
156
+ ### Judge
157
+
158
+ > System prompt seed: "You are the arbiter. Read the Builder, Skeptic, and
159
+ > Retriever outputs plus the current claim graph. Update claim statuses
160
+ > with explicit evidence deltas. You may NOT propose new claims of your
161
+ > own; you may accept or reject claims proposed by Builder. Apply domain
162
+ > gates: demote first-principles output to `hypothesis` in non-mechanistic
163
+ > domains; require Crossref/PubMed-verified citations in empirical
164
+ > domains."
165
+
166
+ Tool surface: read-only access to `claims.json`, `sources.json`, the three
167
+ role outputs, and `research-spec.md`. NO web tools (no temptation to
168
+ re-research).
169
+
170
+ Output shape (mandatory JSON, parsed and merged into the graph):
171
+ ```json
172
+ {
173
+ "iteration": N,
174
+ "attacked_claim": "C-007",
175
+ "claim_deltas": [
176
+ {
177
+ "claim_id": "C-007",
178
+ "old_status": "live",
179
+ "new_status": "contested",
180
+ "old_confidence": "medium",
181
+ "new_confidence": "low",
182
+ "reason": "Skeptic surfaced S-019 (peer-reviewed) contradicting; downgrade per source-tier weighting."
183
+ }
184
+ ],
185
+ "new_claims": [
186
+ {
187
+ "id": "C-021",
188
+ "statement": "...",
189
+ "source_tier": "...",
190
+ "evidence_for": ["S-019"],
191
+ "depends_on": [],
192
+ "contradicts": ["C-007"],
193
+ "impact": "medium"
194
+ }
195
+ ],
196
+ "new_sources_accepted": ["S-019", "S-020"],
197
+ "new_sources_quarantined": ["S-021"],
198
+ "frontier_exhausted_this_iter": false,
199
+ "tier1_conflict_introduced": true
200
+ }
201
+ ```
202
+
203
+ ## Priority formula
204
+
205
+ ```
206
+ priority(C) = impact(C) × centrality(C) × uncertainty(C)
207
+ ```
208
+
209
+ - `impact(C)`: `high=1.0, medium=0.6, low=0.3` — sourced from
210
+ `research-spec.md ## Critical claims`, may be bumped by Judge when
211
+ graph topology changes.
212
+ - `centrality(C)`: 0-1, computed by Judge each iteration (see
213
+ `claim-graph-schema.md`).
214
+ - `uncertainty(C)`: 0-1, computed by Judge each iteration.
215
+
216
+ The next claim to attack is `argmax(priority)` over claims with
217
+ `status ∈ {open, contested, live}`. Tie-break by: oldest
218
+ `last_attacked_iter`, then by `impact`, then alphabetical claim ID.
219
+
220
+ **Why this beats "lowest confidence first":**
221
+
222
+ A high-confidence claim that everything else depends on (high `impact`,
223
+ high `centrality`) MUST be attacked early. If it survives, the rest of
224
+ the graph rests on firmer ground. If it falls, the cascade reveals which
225
+ descendants were also wrong before more time is spent building on them.
226
+
227
+ "Lowest confidence first" chases the noisiest claims and leaves the
228
+ load-bearing assumptions untested until late, when their failure is
229
+ maximally expensive.
230
+
231
+ ## Spar triggers (NOT every-N-iterations)
232
+
233
+ `/apt:spar` fires from Step 8a when ANY of these conditions hold after
234
+ Judge has updated the graph:
235
+
236
+ | Trigger | Detection |
237
+ |---|---|
238
+ | **Tier-1 conflict** | Two claims with `confidence ≥ medium` and a `contradicts` edge between them |
239
+ | **Critical-claim stall** | A claim flagged in `## Critical claims` has had `status ∈ {contested, open}` for ≥ 2 consecutive iterations with no confidence change |
240
+ | **Vendor-only evidence** | A claim's `evidence_for[]` contains ONLY sources with `tier: vendor-doc` AND `impact ≥ medium` |
241
+
242
+ Spar prompt MUST include:
243
+ 1. The current synthesis position (1 paragraph)
244
+ 2. The specific claim under challenge (statement + current evidence)
245
+ 3. Explicit instruction: "Produce a concrete claim status delta with citation diff. Reject debate summaries with no actionable delta."
246
+ 4. The current `claims.json` excerpt for the challenged claim + neighbors
247
+
248
+ Spar return is parsed for a Judge-shaped delta. If the return does NOT
249
+ include `claim_deltas[]`, mark the spar `## status: theater` in
250
+ `spar/{round-slug}.md` and DO NOT update the graph. This prevents
251
+ "both LLMs trained on the same corpus rephrased each other" from
252
+ corrupting the synthesis.
253
+
254
+ ## Stop states + early termination
255
+
256
+ Two terminal states. The skill MUST distinguish them in `RESEARCH.md`
257
+ status header and in the final report. **A budget is a CAP, not a target.**
258
+ The loop stops the instant a terminal state fires — remaining budget is
259
+ not consumed.
260
+
261
+ ### CONVERGED
262
+
263
+ All of:
264
+ - Every claim in `## Critical claims` is at `confidence: high`
265
+ - Zero unresolved tier-1 conflicts (every contradicts-edge with both ends
266
+ `confidence ≥ medium` has been resolved by retracting one side)
267
+ - Retriever reported `frontier_exhausted_this_iter: true` for the last 2
268
+ iterations (no new sources to be found)
269
+ - Citation audit ran in one of the last 4 iterations with zero new
270
+ quarantines
271
+
272
+ Output: full synthesis with confidence statements. Budget remaining is
273
+ reported in the final report (e.g. "CONVERGED at iter 12 of estimated 24,
274
+ budget used: 3h 14m of 8h").
275
+
276
+ ### STALLED
277
+
278
+ Retriever frontier exhausted (no new sources for 2 iterations) but
279
+ critical claims remain at `contested` or `confidence < high`. Output
280
+ MUST include a `## Cannot resolve without` section listing what new
281
+ evidence would be required:
282
+
283
+ - Real-world experiment (specify the experiment)
284
+ - Paywalled paper access (specify the citation)
285
+ - Vendor disclosure (specify what disclosure)
286
+ - Domain expert interview (specify the question)
287
+ - Code execution / falsification harness (specify the test)
288
+ - Telemetry / instrumented data (specify the measurement)
289
+
290
+ STALLED is NOT a failure — it is the honest answer when evidence
291
+ genuinely runs out. Confident-sounding synthesis on a stalled question
292
+ is the worst outcome the skill can produce; STALLED prevents it.
293
+
294
+ ### Budget-cap exhaustion
295
+
296
+ When `elapsed ≥ budget`, force `STALLED` with `## Stopped at budget cap`
297
+ regardless of mid-iter state. Honest, even if mid-attack. The reason
298
+ this is STALLED and not its own state: hitting the cap means evidence
299
+ acquisition was not yet complete, which IS the STALLED condition. The
300
+ `## Stopped at budget cap` note distinguishes it from a frontier-exhausted
301
+ STALLED for the reader.
302
+
303
+ ### Early-termination rule
304
+
305
+ Whichever terminal state fires first wins. The skill body MUST check
306
+ terminal-state criteria at the END of every iteration before scheduling
307
+ the next wake. A 3-hour CONVERGED at hour 3 of an 8-hour budget exits
308
+ cleanly; the saved compute is reported in the final summary, NOT spent
309
+ on extra iterations "just in case."
310
+
311
+ ## Progress checkpoints (tail-able)
312
+
313
+ Long-running loops need observable progress without interrupting the
314
+ loop. At the START of every iteration, the skill body appends ONE line
315
+ to `.aperant/research/{task-id}/progress.log`:
316
+
317
+ ```
318
+ {ISO timestamp} iter={N}/{est_max} budget={used}/{cap} next_attack={C-id} priority={value} status={INVESTIGATING|SYNTHESIZING|...}
319
+ ```
320
+
321
+ Example:
322
+ ```
323
+ 2026-05-24T10:14:22Z iter=4/24 budget=42m/8h next_attack=C-007 priority=0.84 status=INVESTIGATING
324
+ 2026-05-24T10:34:15Z iter=5/24 budget=1h12m/8h next_attack=C-012 priority=0.71 status=INVESTIGATING
325
+ 2026-05-24T10:55:08Z iter=6/24 budget=1h33m/8h next_attack=C-007 priority=0.69 status=INVESTIGATING
326
+ ```
327
+
328
+ Users can `tail -f .aperant/research/{task-id}/progress.log` to watch the
329
+ loop without blocking it. The line is intentionally one-per-iteration
330
+ (not one-per-agent-return) to keep the log scannable. The richer
331
+ per-iteration trace lives in `iteration-log.md`.
332
+
333
+ At terminal state, the final line includes the verdict:
334
+
335
+ ```
336
+ 2026-05-24T13:28:51Z iter=12/24 budget=3h14m/8h verdict=CONVERGED critical_claims_high=5/5 unresolved_conflicts=0
337
+ ```
338
+
339
+ `est_max` is recomputed each iteration based on average iteration
340
+ duration so far × remaining budget. It is a hint, not a guarantee.
341
+
342
+ ## Wake-loop continuation
343
+
344
+ End of each iteration (when status = CONTINUE), the skill calls
345
+ `ScheduleWakeup`:
346
+
347
+ ```
348
+ ScheduleWakeup({
349
+ delaySeconds: 1200, // 20 min — past cache TTL
350
+ prompt: "/apt:research --resume {task-id} --budget {remaining}",
351
+ reason: "apt:research budget loop iter {N+1}, next attack: {claim_id}, budget remaining: {remaining}"
352
+ })
353
+ ```
354
+
355
+ The `--budget {remaining}` arg keeps the budget value monotonically
356
+ decreasing across wakes so a crash + manual restart preserves the cap.
357
+
358
+ Then exits cleanly. The host CLI delivers the wake event when ready and
359
+ the skill resumes from `RESEARCH.md` status (which was set to
360
+ `INVESTIGATING` before the spawn).
361
+
362
+ This is the same primitive `/apt:watch-ci` uses. No daemons, no
363
+ cross-CLI re-exec, no external scheduler.
364
+
365
+ If `ScheduleWakeup` is unavailable in the current host CLI environment,
366
+ the skill MUST refuse `--budget` with an explicit error pointing the
367
+ user at the manual `--resume` workflow.
368
+
369
+ ## Resume semantics
370
+
371
+ `--resume <task-id>` re-enters at the iteration boundary:
372
+ 1. Read `research-spec.md` (immutable, locked at Step 3)
373
+ 2. Read `claims.json`, `sources.json`, `RESEARCH.md` status
374
+ 3. Read `## Next attack` from `RESEARCH.md` (written by previous iteration)
375
+ 4. Compute remaining budget = `original_budget - elapsed_at_start_of_iter`
376
+ (Step 9 of SKILL.md records elapsed in the iteration log before
377
+ scheduling the wake)
378
+ 5. Go to Step 5 with that claim as the attack target
379
+
380
+ If the previous run was killed mid-iteration (incomplete Judge output in
381
+ `iteration-log.md`), discard the partial iteration entry and re-run from
382
+ the last fully-recorded iteration. The graph state on disk is canonical;
383
+ the partial log entry is lost work, not corrupted state.
384
+
385
+ ## Budget override precedence
386
+
387
+ | Source of budget value | Precedence |
388
+ |---|---|
389
+ | Explicit `--budget <duration>` flag | wins all |
390
+ | Explicit `--no-budget` flag | wins all (forces single-session mode) |
391
+ | Named alias (`--overnight`, `--weekend`, `--background`) | beats prose detection |
392
+ | Prose detection (from Step 2 of SKILL.md) | beats default |
393
+ | Default | single-session mode (no budget loop) |
394
+
395
+ Bounds: `1m ≤ budget ≤ 72h`. Outside-range values are rejected with an
396
+ explicit error. The 72h ceiling is intentional — longer than that and
397
+ the wall-clock predictability of the artifact starts to suffer.
@@ -0,0 +1,206 @@
1
+ # Claim Graph Schema
2
+
3
+ > JSON schema for `claims.json` and `sources.json`. Replaces the flat
4
+ > `findings.tsv` ledger. Load when reasoning about claim state, source
5
+ > tiers, dependencies, or status transitions.
6
+
7
+ ## Why a graph, not a list
8
+
9
+ A flat ledger answers "what did we find?" but not "what does each finding
10
+ depend on?" or "if we retract this claim, what else collapses?" The graph
11
+ makes the dependency structure explicit so:
12
+
13
+ - Priority can rank by `centrality` (how many other claims depend on this one)
14
+ - Skeptic can target the most load-bearing claim, not the weakest one
15
+ - Retraction propagates (downgrading a root claim downgrades its descendants)
16
+ - Contradictions surface as edges, not as silently-averaged confidence
17
+
18
+ ## `claims.json` — one entry per discovered claim
19
+
20
+ ```json
21
+ {
22
+ "version": 1,
23
+ "claims": {
24
+ "C-001": {
25
+ "statement": "Softer rear axle increases lateral grip on no-diff karts.",
26
+ "source_tier": "community-lore",
27
+ "conditions": {
28
+ "kart_class": "OK1",
29
+ "track_temp": "any",
30
+ "tire_compound": "any"
31
+ },
32
+ "evidence_for": ["S-003", "S-007", "S-012"],
33
+ "evidence_against": ["S-005"],
34
+ "depends_on": ["C-002"],
35
+ "supports": ["C-009"],
36
+ "contradicts": [],
37
+ "impact": "high",
38
+ "centrality": 0.83,
39
+ "uncertainty": 0.62,
40
+ "confidence": "medium",
41
+ "status": "contested",
42
+ "last_attacked_iter": 4,
43
+ "history": [
44
+ {
45
+ "iter": 1,
46
+ "status": "open",
47
+ "confidence": "low",
48
+ "note": "Seeded from spec sub-question Q3."
49
+ },
50
+ {
51
+ "iter": 4,
52
+ "status": "contested",
53
+ "confidence": "medium",
54
+ "note": "Skeptic surfaced S-005 (SAE paper) contradicting forum consensus. Judge moved to contested."
55
+ }
56
+ ]
57
+ }
58
+ }
59
+ }
60
+ ```
61
+
62
+ ### Field rules
63
+
64
+ | Field | Required | Notes |
65
+ |---|---|---|
66
+ | `statement` | yes | One sentence, falsifiable form |
67
+ | `source_tier` | yes | `peer-reviewed \| independent-test \| primary-doc \| vendor-doc \| community-lore \| derived-theory \| hypothesis` |
68
+ | `conditions` | yes | Object of context dimensions; `{}` if unconditional |
69
+ | `evidence_for` / `evidence_against` | yes | Arrays of source IDs (`S-NNN`) |
70
+ | `depends_on` | yes | Claim IDs this claim derives from; empty if root |
71
+ | `supports` / `contradicts` | yes | Claim IDs this claim affects |
72
+ | `impact` | yes | `high \| medium \| low` — from spec `## Critical claims` |
73
+ | `centrality` | computed | 0-1; out-degree on `supports`+`contradicts` normalized by graph size |
74
+ | `uncertainty` | computed | 0-1; function of evidence-for/against ratio + source tiers |
75
+ | `confidence` | yes | `high \| medium \| low \| contested` |
76
+ | `status` | yes | `open \| live \| dead \| contested \| retracted \| hypothesis \| converged` |
77
+ | `last_attacked_iter` | yes | Integer; iter number when last picked by priority |
78
+ | `history[]` | yes | Append-only; every Judge ruling adds an entry |
79
+
80
+ ### Status transitions
81
+
82
+ ```
83
+ open ─────┬──> live ──┬──> converged
84
+ │ │
85
+ ├──> contested
86
+ │ │
87
+ ├──> hypothesis (domain ≠ mechanistic + first-principles output)
88
+
89
+ └──> retracted (citation audit failed, no other support)
90
+
91
+ dead = candidate approach with documented prior failure (SOLVE mode)
92
+ ```
93
+
94
+ ## `sources.json` — one entry per discovered source
95
+
96
+ ```json
97
+ {
98
+ "version": 1,
99
+ "sources": {
100
+ "S-003": {
101
+ "url": "https://www.kartpulse.com/threads/axle-stiffness-explained.1234",
102
+ "tier": "community-lore",
103
+ "type": "forum-post",
104
+ "fetched_at": "2026-05-24T10:14:22Z",
105
+ "verified_at": "2026-05-24T11:42:08Z",
106
+ "derivation_chain": [],
107
+ "quality": {
108
+ "author_credentials": "unknown",
109
+ "replicated": false,
110
+ "peer_reviewed": false
111
+ },
112
+ "quote_anchor": "softer axles flex more under load and..."
113
+ },
114
+ "S-005": {
115
+ "url": "https://saemobilus.sae.org/papers/...",
116
+ "tier": "peer-reviewed",
117
+ "type": "paper",
118
+ "fetched_at": "2026-05-24T10:22:09Z",
119
+ "verified_at": "2026-05-24T10:22:31Z",
120
+ "derivation_chain": [],
121
+ "doi": "10.4271/2019-01-1234",
122
+ "doi_verified_via": "crossref",
123
+ "quality": {
124
+ "n": null,
125
+ "design": "analytic",
126
+ "peer_reviewed": true,
127
+ "replicated": null
128
+ },
129
+ "quote_anchor": "torsional stiffness coupling to inside-rear lift..."
130
+ }
131
+ }
132
+ }
133
+ ```
134
+
135
+ ### Field rules
136
+
137
+ | Field | Required | Notes |
138
+ |---|---|---|
139
+ | `url` | yes | Canonical URL or DOI URL |
140
+ | `tier` | yes | Matches the claim `source_tier` taxonomy |
141
+ | `type` | yes | `paper \| RCT \| meta-analysis \| primary-doc \| vendor-doc \| forum-post \| blog \| book \| video \| code` |
142
+ | `fetched_at` | yes | ISO-8601 timestamp at first fetch |
143
+ | `verified_at` | nullable | ISO-8601 timestamp at last citation-audit pass; `null` until verified |
144
+ | `derivation_chain` | yes | Array of source IDs this source cites; **load-bearing for independence checks** |
145
+ | `quality` | yes | Object; fields depend on `type` — `{n, design, peer_reviewed, replicated}` for empirical |
146
+ | `quote_anchor` | yes | The exact substring the claim relies on; citation audit re-fetches and checks ≥80% match |
147
+
148
+ ### Derivation chain (the independence check)
149
+
150
+ Three sources that all cite the same primary source = **one** source for the
151
+ purpose of triangulation. When computing `centrality` and `uncertainty`, the
152
+ Judge must walk `derivation_chain` to deduplicate effective sources. The
153
+ schema captures this explicitly so future iterations don't double-count.
154
+
155
+ ## Computed fields
156
+
157
+ `centrality` and `uncertainty` are recomputed by the Judge at the end of every
158
+ iteration, NEVER written by Builder/Skeptic/Retriever. Formula reference:
159
+
160
+ ```
161
+ centrality(C) =
162
+ ( |descendants_in_supports(C)| + |descendants_in_contradicts(C)| )
163
+ / max(1, |total_claims|)
164
+
165
+ uncertainty(C) =
166
+ 1 - weighted_evidence_for(C) / (weighted_evidence_for(C) + weighted_evidence_against(C))
167
+
168
+ where weight(S) =
169
+ { peer-reviewed: 1.0,
170
+ independent-test: 0.85,
171
+ primary-doc: 0.7,
172
+ vendor-doc: 0.5,
173
+ community-lore: 0.25,
174
+ derived-theory: domain == mechanistic ? 0.6 : 0.2,
175
+ hypothesis: 0.1 }
176
+ ```
177
+
178
+ `impact` is NOT computed — it comes from the spec's `## Critical claims`
179
+ section, optionally adjusted by the Judge when graph structure changes
180
+ (a claim acquiring many descendants gets impact bumped).
181
+
182
+ `priority(C) = impact(C) × centrality(C) × uncertainty(C)` — the next claim
183
+ to attack is `argmax(priority)` over claims with `status ∈ {open, contested,
184
+ live}`.
185
+
186
+ ## Round-trip render to `RESEARCH.md`
187
+
188
+ The Judge renders a compact human-readable view into `RESEARCH.md`'s
189
+ `## Claim graph summary` section at synthesis time. The graph itself
190
+ (`claims.json` + `sources.json`) is the canonical store; the markdown is a
191
+ projection. Renderer guidance:
192
+
193
+ - Top 5 by `impact × centrality`
194
+ - Format: `**[{status}]** {statement} _(confidence: {confidence}, tier: {source_tier}, conditions: {summary})_`
195
+ - For each contested claim, render both positions and their strongest evidence
196
+ side-by-side. Do NOT collapse them.
197
+
198
+ ## Migration from `findings.tsv` (if encountered on resume)
199
+
200
+ If `--resume` finds a legacy `findings.tsv` (from a pre-v1 session), the skill
201
+ must:
202
+ 1. Stop. Print: "Legacy findings.tsv detected. Migrate with `apt-tools research migrate-graph {task-id}`."
203
+ 2. Refuse to continue until migration runs.
204
+
205
+ This is intentional — there is no automatic mapping from flat ledger to graph
206
+ because the dependency edges aren't recoverable from the old format.