@aperant/framework 0.8.4 → 0.8.6

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 (108) hide show
  1. package/CHANGELOG.md +156 -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/verify-proof/idl/index.d.mts +1 -1
  20. package/dist/cli/verify-proof/idl/index.mjs +7 -8
  21. package/dist/cli/verify-proof/idl/index.mjs.map +1 -1
  22. package/dist/cli/verify-proof/idl/types.d.ts +5 -4
  23. package/dist/cli/verify-proof/idl/types.d.ts.map +1 -1
  24. package/dist/cli/verify-proof/idl/types.js +4 -3
  25. package/dist/cli/verify-proof/idl/types.js.map +1 -1
  26. package/dist/cli/verify-proof/resolver.d.mts +63 -1
  27. package/dist/cli/verify-proof/resolver.d.mts.map +1 -1
  28. package/dist/cli/verify-proof/resolver.mjs +164 -4
  29. package/dist/cli/verify-proof/resolver.mjs.map +1 -1
  30. package/dist/cli/verify-proof/runtime-detect.d.mts +6 -3
  31. package/dist/cli/verify-proof/runtime-detect.d.mts.map +1 -1
  32. package/dist/cli/verify-proof/runtime-detect.mjs +104 -11
  33. package/dist/cli/verify-proof/runtime-detect.mjs.map +1 -1
  34. package/dist/cli/verify-proof/trust.d.mts +1 -1
  35. package/dist/cli/verify-proof/trust.d.mts.map +1 -1
  36. package/dist/cli/verify-proof/trust.mjs +1 -1
  37. package/dist/driver-sdk/conformance.d.ts +41 -0
  38. package/dist/driver-sdk/conformance.d.ts.map +1 -0
  39. package/dist/driver-sdk/conformance.js +123 -0
  40. package/dist/driver-sdk/conformance.js.map +1 -0
  41. package/dist/driver-sdk/errors.d.ts +75 -0
  42. package/dist/driver-sdk/errors.d.ts.map +1 -0
  43. package/dist/driver-sdk/errors.js +80 -0
  44. package/dist/driver-sdk/errors.js.map +1 -0
  45. package/dist/driver-sdk/idl.d.ts +221 -0
  46. package/dist/driver-sdk/idl.d.ts.map +1 -0
  47. package/dist/driver-sdk/idl.js +140 -0
  48. package/dist/driver-sdk/idl.js.map +1 -0
  49. package/dist/driver-sdk/index.d.ts +28 -0
  50. package/dist/driver-sdk/index.d.ts.map +1 -0
  51. package/dist/driver-sdk/index.js +28 -0
  52. package/dist/driver-sdk/index.js.map +1 -0
  53. package/dist/driver-sdk/manifest.d.ts +93 -0
  54. package/dist/driver-sdk/manifest.d.ts.map +1 -0
  55. package/dist/driver-sdk/manifest.js +12 -0
  56. package/dist/driver-sdk/manifest.js.map +1 -0
  57. package/dist/driver-sdk/retry.d.ts +33 -0
  58. package/dist/driver-sdk/retry.d.ts.map +1 -0
  59. package/dist/driver-sdk/retry.js +50 -0
  60. package/dist/driver-sdk/retry.js.map +1 -0
  61. package/dist/plugin/.claude-plugin/plugin.json +2 -1
  62. package/dist/plugin/agents/apt-planner.md +34 -3
  63. package/dist/plugin/skills/apt-plan/SKILL.md +50 -0
  64. package/dist/plugin/skills/apt-pr-review/SKILL.md +1 -1
  65. package/dist/plugin/skills/apt-research/SKILL.md +526 -0
  66. package/dist/plugin/skills/apt-research/appendices/budget-loop.md +397 -0
  67. package/dist/plugin/skills/apt-research/appendices/claim-graph-schema.md +206 -0
  68. package/dist/plugin/skills/apt-research/appendices/domain-tools.md +236 -0
  69. package/dist/plugin/skills/apt-spar/SKILL.md +19 -7
  70. package/dist/plugin/skills/apt-verify-proof/SKILL.md +41 -2
  71. package/dist/schemas/quick-task.d.ts +17 -17
  72. package/drivers/.gitkeep +0 -0
  73. package/drivers/api/README.md +40 -0
  74. package/drivers/api/driver.mjs +59 -0
  75. package/drivers/api/manifest.json +26 -0
  76. package/drivers/browser/README.md +105 -0
  77. package/drivers/browser/driver.mjs +134 -0
  78. package/drivers/browser/manifest.json +35 -0
  79. package/drivers/cli/README.md +44 -0
  80. package/drivers/cli/driver.mjs +62 -0
  81. package/drivers/cli/manifest.json +28 -0
  82. package/drivers/electron/README.md +64 -0
  83. package/drivers/electron/driver.mjs +87 -0
  84. package/drivers/electron/manifest.json +37 -0
  85. package/package.json +7 -3
  86. package/skills/apt-plan/SKILL.md +50 -0
  87. package/skills/apt-planner.md +16 -0
  88. package/skills/apt-pr-review/SKILL.md +1 -1
  89. package/skills/apt-research/SKILL.md +526 -0
  90. package/skills/apt-research/appendices/budget-loop.md +397 -0
  91. package/skills/apt-research/appendices/claim-graph-schema.md +206 -0
  92. package/skills/apt-research/appendices/domain-tools.md +236 -0
  93. package/skills/apt-spar/SKILL.md +19 -7
  94. package/skills/apt-verify-proof/SKILL.md +41 -2
  95. package/src/cli/commands/coverage-check.mjs +126 -44
  96. package/src/cli/commands/detect-runtime.mjs +23 -14
  97. package/src/cli/coverage-check/user-outcomes.mjs +273 -0
  98. package/src/cli/host/detect.mjs +1 -1
  99. package/src/cli/install/install-from-source.mjs +3 -3
  100. package/src/cli/install/legacy-paths.mjs +2 -0
  101. package/src/cli/verify-proof/idl/index.mjs +7 -8
  102. package/src/cli/verify-proof/idl/types.ts +5 -4
  103. package/src/cli/verify-proof/manifest-schema.json +2 -1
  104. package/src/cli/verify-proof/resolver.mjs +171 -4
  105. package/src/cli/verify-proof/runtime-detect.mjs +99 -10
  106. package/src/cli/verify-proof/trust.mjs +1 -1
  107. package/templates/proof-verification.md +32 -3
  108. package/workflows/verify-proof.md +78 -9
@@ -161,6 +161,14 @@ and is committable as a second-consumer document. Section ordering is
161
161
  **load-bearing** — downstream tooling (`apt-tools coverage-check`,
162
162
  `apt-tools consistency check`) reads sections by header.
163
163
 
164
+ **Three orthogonal user-facing sections — keep them distinct.** User
165
+ Stories carry product motivation, User Outcomes carry the
166
+ verify-proof input contract, and Acceptance Criteria carry code-level
167
+ invariants. The three do NOT subsume each other; a STANDARD/DEEP spec
168
+ carries all three. See the `## User Outcomes` block below for the
169
+ verify-proof unit shape (Epic line + atomic outcomes + surface tags
170
+ + empty-with-note escape).
171
+
164
172
  ```markdown
165
173
  # Spec: {task title}
166
174
 
@@ -184,6 +192,29 @@ ships: new files, modified files, new flags, new config, new skills.}
184
192
  {persona}, when I {trigger}, I want {behavior} so I can {outcome}."
185
193
  Stories map 1:1 onto subtasks in implementation_plan.json.}
186
194
 
195
+ ## User Outcomes
196
+
197
+ {The verify-proof input contract. One bolded `**Epic:**` headline + N
198
+ atomic outcomes tagged by surface. Authors write the LITERAL string
199
+ `**ON**` — IDs are POSITIONAL and synthesized by the parser (the first
200
+ outcome row becomes `O1`, the second `O2`, etc.). Closed surface set:
201
+ `electron | web | cli | api | electron+web`. Optional cross-references
202
+ via `*(requires ON N)*` where `N` is the digit of a prior outcome's
203
+ position. For pure-refactor specs with no user-observable changes, the
204
+ body MAY be exactly `_No user-observable changes — pure refactor._`
205
+ (italic underscores required, exact em dash, period mandatory).
206
+ Coverage-check parses this section into a `user_outcomes` payload —
207
+ see the gate table below.}
208
+
209
+ **Epic:** {one-sentence user-facing capability headline}
210
+
211
+ - **ON** [surface]: {user does X; system shows/does Y}
212
+ - **ON** [surface]: {user does X; system shows/does Y} *(requires ON 1)*
213
+ - **ON** [electron+web]: {cross-surface outcome — runs twice, once per surface}
214
+
215
+ (Refactor-only escape:)
216
+ _No user-observable changes — pure refactor._
217
+
187
218
  ## Implementation Decisions
188
219
 
189
220
  {Load-bearing principles carried forward into every subtask. Format:
@@ -215,6 +246,25 @@ ID. Subtasks reference these IDs via their per-subtask
215
246
  plan-coverage-check input.}
216
247
  ```
217
248
 
249
+ **`## User Outcomes` is the verify-proof input contract (STANDARD/DEEP
250
+ only — QUICK is hard-exempt per ID-05 / Fast Path Guarantee).** The
251
+ section sits between `## User Stories` (product motivation prose) and
252
+ `## Implementation Decisions` (load-bearing engineering principles).
253
+ Coverage-check (`apt-tools coverage-check validate .`) parses the
254
+ section into a `user_outcomes` payload with this gate behavior:
255
+
256
+ | Header present? | Body shape | Gate result (v0.8.x) | v0.9.0 hard cutover |
257
+ |---|---|---|---|
258
+ | no | — | `warn` (migration deadline emitted) | `block` |
259
+ | yes | `outcomes` (≥1 `**ON**` row) | `pass` | `pass` |
260
+ | yes | exactly `_No user-observable changes — pure refactor._` | `pass` | `pass` |
261
+ | yes | empty (header but nothing under it) | `block` | `block` |
262
+
263
+ Closed surface set: `electron | web | cli | api | electron+web`. The
264
+ `electron+web` tag means the outcome runs on both surfaces during
265
+ verify-proof (split screenshot trees, two test entries in
266
+ `verification.json`).
267
+
218
268
  **Constraint — do NOT interview the user.** The PRD is synthesized from
219
269
  the task description + codebase investigation + AGENTS.md, not from a
220
270
  clarifying interview. If a section truly cannot be filled, write
@@ -100,6 +100,17 @@ Content format branches on track.
100
100
  ## User Stories
101
101
  {**US-NN (persona).** stories, 1:1 onto subtasks.}
102
102
 
103
+ ## User Outcomes
104
+ {Verify-proof input contract. **Epic:** headline + N atomic outcomes.
105
+ Authors write the LITERAL `**ON**` string (IDs are POSITIONAL — parser
106
+ synthesizes O1/O2/…). Closed surface set:
107
+ `electron | web | cli | api | electron+web`. Empty-with-note escape:
108
+ `_No user-observable changes — pure refactor._`}
109
+
110
+ **Epic:** {one-sentence user-facing capability}
111
+
112
+ - **ON** [surface]: {user does X; system shows/does Y}
113
+
103
114
  ## Implementation Decisions
104
115
  {### ID-NN load-bearing principles.}
105
116
 
@@ -120,6 +131,11 @@ PRD section ordering is load-bearing — downstream tooling greps by header.
120
131
  Do NOT interview the user; synthesize from task description + codebase +
121
132
  AGENTS.md.
122
133
 
134
+ **`## User Outcomes` synthesis directive.** Outcomes are
135
+ user-observable; ACs are code-level. Do NOT collapse — they parse
136
+ through different gates. STANDARD/DEEP specs carry both. IDs are
137
+ POSITIONAL — never hand-author `**O1**`/`**O2**`.
138
+
123
139
  ## 5. Create Implementation Plan
124
140
 
125
141
  Create `{task_dir}/implementation_plan.json`:
@@ -1081,7 +1081,7 @@ For every finding with `validation_status == "confirmed_valid"` AND `severity
1081
1081
  - `{REVIEWER_EVIDENCE}` — the finding's `evidence` field verbatim.
1082
1082
  3. Invoke codex using the shape configured at `.aperant/config.json:router.llm.providers.codex`:
1083
1083
  ```bash
1084
- codex exec --model gpt-5.3-codex --input-file /tmp/fp-gate-${FINDING_ID}.md
1084
+ codex exec --input-file /tmp/fp-gate-${FINDING_ID}.md
1085
1085
  ```
1086
1086
  Parse the JSON response `{verdict, reasoning, evidence_quote, severity_assessment}`.
1087
1087
  4. If `verdict == "falsify"`, set the finding's `validation_status = "dismissed_cross_model_falsification"` and copy the codex response into `fp_gate_result`. If `verdict == "confirm"`, pass through unchanged.
@@ -0,0 +1,526 @@
1
+ ---
2
+ name: apt:research
3
+ description: "Multi-source research (UNDERSTAND/VERIFY/SOLVE/COMPARE) with --budget background loop. Claim-graph state, 4 agents per iteration (Builder/Skeptic/Retriever/Judge), domain-gated spar + first-principles, citation audit, converged-vs-stalled verdicts."
4
+ apt-skill-version: {{APT_VERSION}}
5
+ stage: plan
6
+ intent: investigate
7
+ when_to_use: "The user wants multi-source research with explicit confidence and contradiction surfacing — understand a topic, verify a belief, decompose a novel problem, or compare options. NOT for codebase-only questions (use /apt:zoom-out) and NOT for refactor discovery (use /apt:improve)."
8
+ user_invocable: true
9
+ internal: false
10
+ spawns_agent: true
11
+ agent_name: "apt-researcher"
12
+ task_context: create-new
13
+ default_track: STANDARD
14
+ default_execution_mode: step
15
+ execution_modes:
16
+ - auto
17
+ - step
18
+ - research
19
+ allowed-tools: "Bash, Read, Grep, Glob, Write, WebSearch, WebFetch, Task"
20
+ argument-hint: "apt:research [--understand|--verify|--solve|--compare] [--budget <duration>|--overnight|--weekend|--background|--no-budget] [--resume <task-id>] [topic]"
21
+ gates: []
22
+ triggers:
23
+ - /apt:research
24
+ - apt:research
25
+ produces:
26
+ - immutable research-spec.md (success-criterion oracle)
27
+ - claims.json + sources.json (claim graph with conditions, dependencies, tiers)
28
+ - RESEARCH.md synthesis with domain-gated output template
29
+ - progress.log (tail-able per-iteration heartbeat)
30
+ - dual-state verdict (converged | stalled) with calibration report
31
+ ---
32
+
33
+ <critical>
34
+ - The research spec is **immutable** after Step 3. It is the success-criterion oracle. You may not edit it to make findings pass.
35
+ - State is a **claim graph** (`claims.json` + `sources.json`), NOT a flat ledger. Every claim carries source tier, conditions, dependencies, evidence-for, evidence-against, and status. Flat-list rationalizations are rejected.
36
+ - Priority for the next attack is `impact × centrality × uncertainty`. NEVER "lowest confidence first" — high-confidence load-bearing claims get attacked early, not late.
37
+ - Per iteration, FOUR agents run with isolated contexts and distinct objectives: **Builder** (synthesize), **Skeptic** (falsify the most load-bearing claim), **Retriever** (new evidence only, no opinion), **Judge** (update statuses with evidence deltas). Single-agent shortcuts are rejected.
38
+ - First-principles derivation is **domain-gated**. Enabled for mechanistic domains (physics, distributed systems, algorithms). Disabled for empirical/social domains (medicine, market research, lore) — output from a first-principles agent in those domains is demoted to `hypothesis` rows that cannot reach `confidence: high`.
39
+ - Spar with a second LLM is **trigger-gated**, not clock-gated. Spar fires on: tier-1 conflict, high-impact claim unresolved for 2+ iterations, or vendor-only evidence. Each spar MUST produce a concrete claim status change with citation diff, NOT a debate summary.
40
+ - Citation-audit pass runs every N iterations (default 4). It re-fetches the source for top load-bearing claims and verifies the quoted text. Failures are quarantined to `suspect-citations.md`, never silently dropped.
41
+ - Stop has two terminal states: **converged** (critical claims above threshold, no unresolved tier-1 conflicts, no untapped evidence frontier) and **stalled** (retrieval frontier exhausted, claims still contested — outputs "I cannot resolve this without X"). The skill MUST distinguish them in the verdict.
42
+ - A budget is a **CAP, not a target**. CONVERGED at hour 3 of an 8h budget exits cleanly; the saved compute is reported, NOT spent on extra iterations "just in case."
43
+ - Domain-aware output gates apply at synthesis time. Medical forces `## Not medical advice` + `## What this CAN'T tell you`. Strategic forces conditional-tree recommendation. Code-SOLVE blocks `converged` if no falsification attempt was run. Physics/empirical forces `## Evidence gaps`.
44
+ </critical>
45
+
46
+ <objective>
47
+ You are the Aperant research facilitator. You take a topic, claim, problem, or option set
48
+ and produce evidence-backed findings as a **claim graph** with explicit confidence,
49
+ source tiers, conditions, dependencies, and surfaced contradictions.
50
+
51
+ You operate in one of four modes, chosen by reading the user's input — never by asking:
52
+
53
+ - **UNDERSTAND** (deep topic) — build a mental model from multiple perspectives.
54
+ - **VERIFY** (fact/belief) — triangulate a claim across independently-derived sources.
55
+ - **SOLVE** (novel problem) — decompose, generate candidates, search for prior failures.
56
+ - **COMPARE** (options/tools) — fixed evaluation dimensions, parallel per-dimension fan-out.
57
+
58
+ Orthogonal to mode, the `--budget <duration>` flag (with aliases `--overnight`,
59
+ `--weekend`, `--background`) enables a time-boxed multi-iteration loop in the background.
60
+ See `appendices/budget-loop.md` for the loop discipline. Without any budget set,
61
+ the skill runs a single session capped at 5 iterations of the
62
+ Builder/Skeptic/Retriever/Judge cycle.
63
+
64
+ Domain detection (mechanistic / empirical / strategic / general) gates first-principles
65
+ reasoning, tool surface (see `appendices/domain-tools.md`), and output template. The
66
+ domain is detected in Step 1d and persisted to `research-spec.md`.
67
+
68
+ The `apt-researcher` agent runs the 4 specialized roles per iteration. The skill body
69
+ owns mode-detection, domain-detection, budget-detection, spec-writing, role orchestration,
70
+ the claim graph, citation audits, stop-state decisioning, and synthesis.
71
+ </objective>
72
+
73
+ <your_environment>
74
+ - **Working directory:** The project root (where you were invoked)
75
+ - **apt-tools path:** `node packages/framework/bin/apt-tools.mjs` or the locally installed `apt-tools` binary
76
+ - **Constitution:** Read `AGENTS.md` in the project root if it exists
77
+ - **Config:** `.aperant/config.json` — autonomy level, source-credibility overrides, citation-audit cadence, default budget cap if any
78
+ - **State:** `.aperant/state.json` — current execution state (active task, track, phase)
79
+ - **Agent:** `apt-researcher` (defined at `packages/framework/skills/apt-researcher.md`) — spawned 4× per iteration with role-specialized prompts
80
+ - **Web tools:** `WebSearch` and `WebFetch` for retrieval; `mcp__plugin_context7_context7__*` for library-doc lookups
81
+ - **Bash tools:** `curl` (Crossref/PubMed citation verify), `gh` (GitHub releases + code search when present), `pdftotext` if installed (PDF source reads)
82
+ - **Wake primitive:** `ScheduleWakeup` for `--budget` loop continuation (same pattern as `/apt:watch-ci`)
83
+ - **Sparring:** `/apt:spar` (invoked via Skill) when trigger conditions fire
84
+ </your_environment>
85
+
86
+ <state_files>
87
+ ## State Files
88
+
89
+ **Reads:**
90
+ - `AGENTS.md` — project constitution
91
+ - `.aperant/state.json` — current execution state
92
+ - `.aperant/config.json` — autonomy, citation-audit cadence, default budget cap
93
+ - `.aperant/context/notes/*.md` — prior locked decisions (may inform UNDERSTAND/SOLVE)
94
+ - `.aperant/research/*/research-spec.md` — prior research artifacts (cross-task reuse)
95
+ - `.aperant/research/{task-id}/research-spec.md` — immutable spec when resuming
96
+
97
+ **Writes:**
98
+ - `.aperant/research/{task-id}/research-spec.md` — **immutable** after Step 3 (success-criterion oracle, including detected `domain` and `budget`)
99
+ - `.aperant/research/{task-id}/claims.json` — claim graph (see `appendices/claim-graph-schema.md` for schema)
100
+ - `.aperant/research/{task-id}/sources.json` — source registry with tiers, fetch timestamps, verification timestamps
101
+ - `.aperant/research/{task-id}/RESEARCH.md` — domain-gated synthesis with status header (`OPEN | INVESTIGATING | SYNTHESIZING | CONVERGED | STALLED`)
102
+ - `.aperant/research/{task-id}/iteration-log.md` — append-only per-iteration record (which claims attacked, what each role produced, what Judge ruled)
103
+ - `.aperant/research/{task-id}/progress.log` — one line per iteration, tail-able heartbeat (timestamp, iter, budget used/cap, next attack, status)
104
+ - `.aperant/research/{task-id}/suspect-citations.md` — quarantined citations that failed verification
105
+ - `.aperant/research/{task-id}/findings/{source-slug}.md` — per-source extracted quotes (citation-audit substrate)
106
+ - `.aperant/research/{task-id}/deferred-angles.md` — parking lot for angles not pursued (resumable)
107
+ - `.aperant/research/{task-id}/spar/{round-slug}.md` — written when a spar round fires
108
+ </state_files>
109
+
110
+ <rationalization>
111
+ Common shortcuts an agent will be tempted to take — and why each is wrong:
112
+
113
+ - "I'll just keep a flat list of findings — the graph is overkill." → Flat ledgers
114
+ hide claim dependencies. When the load-bearing claim turns out wrong at iter 12, a
115
+ flat list can't tell you what else collapses. The graph is the audit trail that
116
+ makes long-running research honest. See `appendices/claim-graph-schema.md`.
117
+ - "Lowest confidence first, that's standard." → Wrong heuristic. A high-confidence
118
+ load-bearing claim ("Milliken's load-transfer model applies to no-diff karts")
119
+ that turns out misapplied destroys everything built on it. Priority is
120
+ `impact × centrality × uncertainty`. Attack what matters, not what's weakest.
121
+ - "One agent per iteration is enough — Builder and Skeptic in one prompt." → A
122
+ single agent self-rationalizes. Specialization (4 isolated agents with distinct
123
+ objectives) forces honest disagreement. Skipping this is the most expensive
124
+ shortcut — it manufactures false consensus over many iterations.
125
+ - "First-principles is always rigorous, run it every iteration." → False. In
126
+ empirical domains (medicine, market behavior), first-principles produces
127
+ plausible-sounding nonsense that empirical evidence then can't dislodge.
128
+ Gated by domain — see Step 1d + `appendices/domain-tools.md`.
129
+ - "Spar every 4 iterations gives free signal." → Spar between LLMs trained on
130
+ overlapping corpora is mostly theater. Trigger-gated spar (tier-1 conflict /
131
+ unresolved high-impact / vendor-only evidence) earns its keep. Clock-gated spar
132
+ wastes tokens and creates fake "resolutions" of fake conflicts.
133
+ - "I have an 8h budget; I should fill the 8h." → No. A budget is a cap. CONVERGED
134
+ at hour 3 stops cleanly. Continuing wastes compute and risks polishing a wrong
135
+ answer into a confident-wrong one. The loop reports remaining budget, never
136
+ spends it.
137
+ - "Three blog posts confirm it — that's triangulation." → All three may cite the
138
+ same primary source. Independence is about derivation chain. Run the citation
139
+ trace; if they all link back to one source, you have **one** source. The
140
+ `sources.json` schema captures this in the `derivation_chain` field.
141
+ - "Three no-mutation iterations means we converged — stop." → No. That signal
142
+ catches stuckness, not convergence. The skill distinguishes `converged`
143
+ (critical claims above threshold + no unresolved tier-1 conflicts + frontier
144
+ exhausted) from `stalled` (frontier exhausted but claims still contested).
145
+ Conflating them ships confident-wrong answers.
146
+ - "Citations look real — no need to verify." → Medical scenario showed that
147
+ in a long-running loop, fabricated DOIs land in the ledger and look real.
148
+ Citation audit re-fetches and quote-checks every N iterations. Failures
149
+ quarantine to `suspect-citations.md`. Without this, long runtime manufactures
150
+ unearned credibility.
151
+ - "I'll just edit research-spec.md to lower coverage thresholds so my findings
152
+ pass." → The spec is immutable because it's the oracle. If you can't meet it,
153
+ emit `stalled` with a `## Cannot resolve without` section. Never edit the
154
+ oracle.
155
+ - "Vendor docs say X works — that's authoritative." → Vendor claims need
156
+ independent confirmation. For COMPARE mode, vendor-only evidence is itself a
157
+ spar trigger (see `appendices/budget-loop.md` § trigger conditions).
158
+ </rationalization>
159
+
160
+ <autonomy_interaction>
161
+ This skill is non-destructive: it only writes to `.aperant/research/{task-id}/` and never
162
+ modifies application code. Safe at every autonomy level.
163
+
164
+ - **Autonomy 0 (plan-only):** Run Steps 1-4 (init, mode/domain/budget detect, spec,
165
+ decompose). Stop before Step 5. Print the spec + decomposition + planned tool surface +
166
+ resolved budget. Ask the user to confirm before spawning agents (token cost matters).
167
+ - **Autonomy 1 (ask-before-destructive):** Run the full single-session pipeline.
168
+ For any `--budget` ≥ 1h (overnight/weekend/background or explicit), ask before
169
+ scheduling the first wake-loop continuation (the budget is a real compute commitment).
170
+ Default 4-role-per-iter runs without asking.
171
+ - **Autonomy 2 (full auto):** Run the full pipeline including any `--budget` if
172
+ requested. Stop on terminal state (converged/stalled) or budget exhaustion.
173
+
174
+ Resume semantics: `--resume <task-id>` restores from `.aperant/research/{task-id}/`.
175
+ Bare `--resume` picks the most recently modified research directory. Resuming re-reads
176
+ `research-spec.md` (still immutable, including the locked `domain` and original `budget`)
177
+ and the claim graph, then continues from the status field in `RESEARCH.md` with the
178
+ remaining budget computed as `original_budget − elapsed`.
179
+
180
+ The wake-loop is the only continuation primitive — same as `/apt:watch-ci`.
181
+ The skill body schedules its own next wake via `ScheduleWakeup` at the end of each
182
+ iteration; the host CLI delivers the wake event. No daemons, no cross-CLI re-exec.
183
+ </autonomy_interaction>
184
+
185
+ <process>
186
+
187
+ ## 1. Initialize or Resume Session
188
+
189
+ 1a. **Canonical task-id.** Derive from `$ARGUMENTS` topic phrase (lowercase, hyphens,
190
+ ≤40 chars) OR from `--resume <task-id>`. If `--resume` with no value, pick the
191
+ most recently modified `.aperant/research/*/` directory.
192
+
193
+ 1b. **Create or load the task directory.** `mkdir -p .aperant/research/{task-id}/`.
194
+ If `research-spec.md` already exists (resume), read it — DO NOT regenerate.
195
+
196
+ 1c. **Status field.** If creating, write `RESEARCH.md` with `## Status: OPEN`.
197
+ If resuming, read the status and skip ahead: `OPEN` → Step 2; `INVESTIGATING` →
198
+ Step 5; `SYNTHESIZING` → Step 8; `CONVERGED` or `STALLED` → Step 10 (report only).
199
+
200
+ 1d. **Detect domain.** Read the question. Classify into ONE of four domains using
201
+ these signals (recorded in `research-spec.md` under `## Domain`, immutable):
202
+
203
+ | Domain | Signals | Tool surface |
204
+ |---|---|---|
205
+ | **mechanistic** | physics, math, distributed systems, algorithms, deterministic engineering systems | first-principles ENABLED; arxiv + PDF read |
206
+ | **empirical** | medicine, biology, nutrition, psychology, public-health, social science | first-principles GATED to `hypothesis` rows only; PubMed/Crossref citation verify REQUIRED |
207
+ | **strategic** | tool comparison, market analysis, vendor selection, license/contract evaluation | first-principles DISABLED; GitHub releases + license fetch; vendor-only is spar trigger |
208
+ | **general** | catch-all when none of the above fit | first-principles soft-enabled with `hypothesis` demotion; standard tool surface |
209
+
210
+ See `appendices/domain-tools.md` for the per-domain tool surface, output gates,
211
+ and demotion rules.
212
+
213
+ ## 2. Detect Mode + Budget
214
+
215
+ ### 2a. Mode
216
+
217
+ Read the user's input. Pick exactly ONE mode using these signals — never ask:
218
+
219
+ | Mode | Signals |
220
+ |------|---------|
221
+ | **UNDERSTAND** | "explain X", "how does X work", "deep dive on", topic-shaped noun phrase |
222
+ | **VERIFY** | "is it true that", "fact-check", "I believe X — confirm", claim-shaped sentence |
223
+ | **SOLVE** | "novel problem", "no known solution", "how can we", task-shaped open question |
224
+ | **COMPARE** | "X vs Y", "which tool for", enumeration of 2+ options |
225
+
226
+ Explicit flag (`--understand|--verify|--solve|--compare`) overrides signal detection.
227
+ Ambiguous input → UNDERSTAND (lowest-commitment posture).
228
+
229
+ ### 2b. Budget
230
+
231
+ Resolve the budget value using this precedence (highest wins):
232
+
233
+ | Source | Value |
234
+ |---|---|
235
+ | `--no-budget` flag | Force single-session mode (no wake-loop) |
236
+ | `--budget <duration>` flag | Use the explicit duration (`30m`, `4h`, `10h`, `2d` — bounds `1m`–`72h`) |
237
+ | `--overnight` flag | `8h` |
238
+ | `--weekend` flag | `48h` |
239
+ | `--background` flag | `4h` |
240
+ | Prose signal in `$ARGUMENTS` | See table below |
241
+ | `.aperant/config.json: research.default_budget` | If set |
242
+ | (nothing) | single-session mode |
243
+
244
+ Prose signal table (read input, never ask):
245
+
246
+ | Signal in user prose | Inferred budget |
247
+ |---|---|
248
+ | "overnight", "while I sleep", "by morning" | 8h |
249
+ | "weekend", "over the weekend", "by Monday" | 48h |
250
+ | "for N hours/days/minutes" (any explicit duration) | N |
251
+ | "while I'm at work", "during the day" | 8h |
252
+ | "in the background" | 4h |
253
+ | "really thoroughly", "deep dive", "question my beliefs", "take your time" | 4h |
254
+ | Decomposition would produce ≥8 sub-questions OR ≥3 contested perspectives (assess during Step 4) | 4h |
255
+
256
+ If a budget is resolved (non-zero), load `appendices/budget-loop.md` into reasoning
257
+ context. This appendix carries the per-iteration loop discipline, priority formula,
258
+ spar triggers, stop-state criteria, progress checkpoints, and wake-loop continuation.
259
+ Do not re-state its rules here.
260
+
261
+ ### 2c. Announce
262
+
263
+ Open with a one-line declaration that surfaces the resolved budget AND the signal
264
+ that produced it (so the user can override knowingly):
265
+
266
+ ```
267
+ [apt:research] {Mode} / {Domain}{ — budget {value} from {signal-source}}.
268
+ {Pass --budget <duration> to override or --no-budget to disable.}
269
+ ```
270
+
271
+ Examples:
272
+ ```
273
+ [apt:research] UNDERSTAND / mechanistic — budget 8h from signal "overnight".
274
+ Pass --budget <duration> to override or --no-budget to disable.
275
+
276
+ [apt:research] COMPARE / strategic — single-session (no budget set).
277
+ Pass --budget 4h or --background to escalate to a background loop.
278
+
279
+ [apt:research] VERIFY / empirical — budget 10h from --budget flag.
280
+ ```
281
+
282
+ ## 3. Write Immutable Research Spec
283
+
284
+ Write `.aperant/research/{task-id}/research-spec.md`. **Once written, this file is locked
285
+ for the rest of the session.** It is the success-criterion oracle.
286
+
287
+ Mandatory sections, ALL modes:
288
+
289
+ - `## Question` — the question in exact, falsifiable form
290
+ - `## Mode` — UNDERSTAND | VERIFY | SOLVE | COMPARE
291
+ - `## Domain` — mechanistic | empirical | strategic | general (from Step 1d)
292
+ - `## Budget` — duration value (e.g. `8h`) AND source (e.g. `signal: "overnight"` /
293
+ `flag: --weekend` / `none — single-session`)
294
+ - `## Sub-questions` — the decomposed questions retrieval must answer (filled in Step 4)
295
+ - `## Source requirements` — required source tiers and minimum count, per domain rules
296
+ - `## Critical claims` — claims that, if wrong, invalidate the synthesis (load-bearing).
297
+ These get explicit `impact: high` weighting in the priority formula.
298
+ - `## Stop rule` — domain-gated; defaults in `appendices/budget-loop.md` §stop-states
299
+ - `## Anti-patterns` — failure modes specific to this question (e.g. "do NOT cite
300
+ single-author benchmarks for tool comparison")
301
+ - `## Output template` — domain-gated synthesis sections (see `appendices/domain-tools.md`
302
+ §output-gates)
303
+
304
+ Print the spec to stdout for the user to see the commitment.
305
+
306
+ ## 4. Decompose Query
307
+
308
+ Mode-specific decomposition. Output is the input to Step 5 fan-out and the basis for the
309
+ initial claim graph nodes.
310
+
311
+ - **UNDERSTAND**: enumerate 5-8 perspectives (skeptic, practitioner, theorist, historian,
312
+ …) and generate one sub-question per perspective.
313
+ - **VERIFY**: generate 3-5 independent probes from different angles (primary docs,
314
+ community reports, attempted refutation, edge cases, independent reproduction).
315
+ - **SOLVE**: state the problem decomposition, then generate 2-3 candidate approaches.
316
+ Per candidate, generate a prior-art query AND a failure-mode query (mandatory).
317
+ - **COMPARE**: declare 4-6 evaluation dimensions UP FRONT (do not invent dimensions
318
+ mid-research). Each dimension × each option is one cell.
319
+
320
+ Initialize the claim graph (`claims.json`): one claim node per sub-question with
321
+ `status: open`, `confidence: low`, `centrality: 0`, computed `impact` from the
322
+ `## Critical claims` spec section.
323
+
324
+ Write the decomposition into `research-spec.md` under `## Sub-questions`. This finalizes
325
+ the spec.
326
+
327
+ **Late budget upgrade.** If Step 2b resolved no budget AND this decomposition produces
328
+ ≥8 sub-questions OR ≥3 contested perspectives, the prose-signal threshold has been met
329
+ mid-process. Print a one-line escalation offer and proceed in single-session mode unless
330
+ the user opted out (autonomy 0 stops here for confirmation anyway):
331
+
332
+ ```
333
+ [apt:research] Decomposition produced N sub-questions — this is a strong --background
334
+ (4h) candidate. Continuing in single-session mode. Pass --background or --budget 4h on
335
+ resume to escalate.
336
+ ```
337
+
338
+ ## 5. Run One Iteration (4 specialized agents)
339
+
340
+ Set `RESEARCH.md` status to `INVESTIGATING`. Append a one-line heartbeat to
341
+ `progress.log` (format defined in `appendices/budget-loop.md` §progress-checkpoints).
342
+
343
+ Pick the next claim to attack using the priority formula from
344
+ `appendices/budget-loop.md` §priority-formula:
345
+
346
+ ```
347
+ priority(claim) = impact(claim) × centrality(claim) × uncertainty(claim)
348
+ ```
349
+
350
+ Spawn FOUR `apt-researcher` agents **in a single message** for parallel execution.
351
+ Each gets a role-specialized prompt and isolated context. Role prompts and the
352
+ domain-aware tool surface are defined in `appendices/budget-loop.md` §agent-roles
353
+ and `appendices/domain-tools.md` respectively.
354
+
355
+ | Role | Objective | Tool surface | Forbidden |
356
+ |---|---|---|---|
357
+ | **Builder** | Advance the synthesis using current claim graph | full read + WebSearch/WebFetch + domain tools | falsifying claims |
358
+ | **Skeptic** | Falsify the chosen high-priority claim; produce explicit counter-evidence | full read + WebSearch/WebFetch + domain tools | confirming or hedging |
359
+ | **Retriever** | Find NEW evidence only (sources not yet in `sources.json`) | WebSearch/WebFetch + domain-specific search | opinion, synthesis |
360
+ | **Judge** | Read what Builder/Skeptic/Retriever produced; update claim statuses with evidence deltas | read-only: claims.json, the three returns | proposing new claims |
361
+
362
+ For the gated cases:
363
+ - Domain ≠ mechanistic AND Builder/Skeptic uses first-principles reasoning → Judge
364
+ demotes that output to `status: hypothesis` (cannot reach `confidence: high`).
365
+ - Domain = strategic AND Builder ranks options → Judge ensures recommendation is
366
+ conditional-tree, not single-winner (see `appendices/domain-tools.md` §output-gates).
367
+ - Domain = empirical AND a citation is added without resolving through Crossref/PubMed
368
+ → Judge moves the source to `sources.json: pending-verify` (not promoted).
369
+
370
+ Append a structured `## Iteration N` block to `iteration-log.md` containing:
371
+ - Attacked claim ID + computed priority
372
+ - Builder output summary (1-3 lines)
373
+ - Skeptic output summary + falsification result
374
+ - Retriever output: new source IDs added
375
+ - Judge ruling: per-claim status delta + reasoning
376
+
377
+ ## 6. Update the Claim Graph
378
+
379
+ Merge the Judge's status deltas into `claims.json`. For each claim touched this
380
+ iteration:
381
+
382
+ - Append to `history[]` with `{iter, status, note, evidence_delta}`
383
+ - Recompute `centrality` (DAG out-degree on `depends_on` / `supports` / `contradicts`)
384
+ - Recompute `uncertainty` from evidence-for / evidence-against ratio + source tiers
385
+ - If Skeptic produced concrete counter-evidence accepted by Judge, downgrade
386
+ `confidence` and surface in synthesis
387
+
388
+ For each NEW source added by Retriever:
389
+ - Append to `sources.json` with `fetched_at`, `tier`, `derivation_chain` (what other
390
+ sources, if any, it derives from)
391
+ - For domain = empirical, mark `verified_at: null` until citation-audit Step 7 runs
392
+
393
+ For Skeptic output that introduces a new claim contradicting an existing one:
394
+ - Create a new claim node
395
+ - Add `contradicts` edge to existing claim
396
+ - If both are `confidence ≥ medium`, mark both `status: contested` and tag as a
397
+ tier-1 conflict (spar trigger candidate)
398
+
399
+ ## 7. Citation Audit (every N iterations, default N=4)
400
+
401
+ Pick the top-K load-bearing claims (highest `impact × centrality`). For each:
402
+
403
+ - Re-fetch the cited source URL via WebFetch (or `curl` + `pdftotext` if PDF)
404
+ - Verify the quoted text still appears in the source (case-insensitive, ≥80% match)
405
+ - For domain = empirical: resolve every cited DOI/PMID via Crossref or NCBI E-utilities
406
+ (`curl https://api.crossref.org/works/{doi}` / `curl https://eutils.ncbi.nlm.nih.gov/...`)
407
+
408
+ For each failure:
409
+ - Move the citation to `suspect-citations.md` with `{claim_id, source_id, failure_mode}`
410
+ - Downgrade the claim's confidence; if no other source supports it, set
411
+ `status: retracted`
412
+
413
+ Citation audit on a single-session run (no budget) fires once at the end of Step 5.
414
+
415
+ ## 8. Decide: Continue, Synthesize, or Spar
416
+
417
+ After Step 7, check four conditions in order. The FIRST terminal-state hit wins —
418
+ remaining budget is not consumed.
419
+
420
+ **8a. Spar trigger check.** Fire `/apt:spar` (via Skill) when ANY of:
421
+ - A tier-1 conflict exists in the graph (two contested claims with `confidence ≥ medium`)
422
+ - A `critical` claim (from spec `## Critical claims`) is unresolved for 2+ iterations
423
+ - A claim has only `tier: vendor-doc` evidence and no independent confirmation
424
+
425
+ Spar prompt MUST include: the current synthesis position, the specific claim under
426
+ challenge, and an explicit instruction to produce a concrete status delta with citation
427
+ diff. Reject spar returns that are debate summaries with no actionable delta.
428
+
429
+ **8b. Stop check (early-termination wins).** Apply the dual-state rule from
430
+ `appendices/budget-loop.md` §stop-states:
431
+
432
+ - **CONVERGED** if: all `critical` claims at `confidence ≥ high`, zero unresolved
433
+ tier-1 conflicts, and Retriever found no new sources for the last 2 iterations
434
+ (frontier exhausted with synthesis stable). Exits immediately even if budget
435
+ remains.
436
+ - **STALLED** if: Retriever frontier exhausted (no new sources for 2 iterations) but
437
+ critical claims remain `contested` or `confidence < high`. The skill outputs an
438
+ explicit `## Cannot resolve without` section listing what new evidence (real-world
439
+ experiments, paywalled paper access, vendor disclosure, etc.) would be required.
440
+ - **CONTINUE** otherwise. Increment iteration counter. If a budget is set, go to
441
+ Step 9 (wake-loop). If not, return to Step 5 — but cap at 5 iterations for
442
+ single-session runs; if cap hit without terminal state, force `STALLED` with
443
+ `## Cannot resolve in single-session mode — re-run with --budget 4h or higher`.
444
+
445
+ **8c. Budget-cap check (budget loops only).** If `elapsed ≥ budget`, force `STALLED`
446
+ with `## Stopped at budget cap` note. Honest, even if mid-attack.
447
+
448
+ **8d. Final progress.log entry.** When any terminal state fires, write the verdict
449
+ line to `progress.log` (see `appendices/budget-loop.md` §progress-checkpoints for the
450
+ format).
451
+
452
+ ## 9. Schedule Next Wake (budget loops only)
453
+
454
+ If a budget is set AND status = CONTINUE:
455
+
456
+ Compute `remaining = original_budget − elapsed_at_start_of_iter`. Call `ScheduleWakeup`:
457
+
458
+ - `delaySeconds`: 1200 (20 minutes — past cache TTL, lets retrieval breathe)
459
+ - `prompt`: `/apt:research --resume {task-id} --budget {remaining}`
460
+ - `reason`: `apt:research budget loop iter {N+1}, next attack: {claim_id}, budget remaining: {remaining}`
461
+
462
+ The `--budget {remaining}` arg keeps the cap monotonically decreasing across wakes
463
+ so a crash + manual restart preserves the original commitment.
464
+
465
+ Persist `## Next attack: {claim_id}, priority: {value}` and `## Budget remaining:
466
+ {remaining}` to `RESEARCH.md` so the wake-loop iteration knows where to resume
467
+ without recomputing.
468
+
469
+ Exit the current session. The wake event will fire Step 5 in a fresh context with
470
+ all state on disk.
471
+
472
+ ## 10. Synthesize (terminal state reached)
473
+
474
+ Flip status to `SYNTHESIZING`. Write `RESEARCH.md` using the domain-gated output
475
+ template from `appendices/domain-tools.md` §output-gates. Every domain produces:
476
+
477
+ - `## Headline` — one-paragraph synthesis
478
+ - `## Claim graph summary` — top 5 critical claims with `{statement, status, confidence,
479
+ source_tier, conditions}`
480
+ - `## Contradictions` — every unresolved tier-1 conflict, with both positions and the
481
+ evidence each rests on (NEVER silently averaged)
482
+ - `## Iteration log summary` — compact per-iteration trace pulled from `iteration-log.md`
483
+ - `## Self-Score` — `{sub_question_coverage: X/Y, critical_claim_coverage: X/Y,
484
+ stop_state: CONVERGED|STALLED, budget_used: X of Y, calibration_note: "..."}`
485
+
486
+ Domain-gated additions:
487
+ - **mechanistic** → `## First-principles derivations` with cross-check vs sources
488
+ - **empirical** → `## Not medical/financial/safety advice` + `## What this CAN'T tell
489
+ you` + `## Quality of evidence` (RCT/n/peer-review-status per cited study)
490
+ - **strategic** → `## Conditional-tree recommendation` (`if X then A, if Y then B`,
491
+ NEVER single-winner when evidence is vendor-only) + `## Vendor-claim conflicts`
492
+ - **general** → `## Outstanding gaps` + `## Recommended next reads`
493
+
494
+ Mode-gated additions:
495
+ - **UNDERSTAND** → mental model with mechanism + analogy
496
+ - **VERIFY** → verdict (`confirmed | partially confirmed | false | contested | unknown`)
497
+ - **SOLVE** → candidate approaches table + recommended path + `## Falsification status`
498
+ (Code-SOLVE blocks `CONVERGED` if no test/simulator/model-checker was run; force
499
+ `STALLED` with `## Cannot certify without falsification`)
500
+ - **COMPARE** → dimensions × options matrix + per-cell evidence pointers
501
+
502
+ ## 11. Report and What's Next
503
+
504
+ Print to stdout:
505
+
506
+ ```
507
+ [apt:research] {Mode} / {Domain} — {CONVERGED|STALLED}.
508
+ Task: .aperant/research/{task-id}/
509
+ RESEARCH.md — synthesis ({lines} lines)
510
+ research-spec.md — immutable spec
511
+ claims.json — {N} claims, {M} sources
512
+ iteration-log.md — {I} iterations
513
+ progress.log — {I} heartbeats (tail anytime)
514
+ suspect-citations.md — {Q} quarantined ({nonzero ? "REVIEW REQUIRED" : "clean"})
515
+ Self-score: critical-claim coverage {X}%, stop-state {CONVERGED|STALLED}
516
+ Budget: {used} of {cap} ({percent}% — {saved-time saved} if CONVERGED early)
517
+ {1-line headline finding OR 1-line "cannot resolve without" gap}
518
+
519
+ Next:
520
+ /apt:discuss to lock decisions from these findings
521
+ /apt:plan to turn SOLVE/COMPARE output into an implementation plan
522
+ /apt:research --resume {task-id} to extend (re-query gaps, add a mode)
523
+ /apt:research --resume {task-id} --budget <duration> to escalate to a longer loop
524
+ ```
525
+
526
+ </process>