research-questions
Generate and resolve TRM research questions from a curator-approved batch. Runs scan-gaps, does web search + confidence judgment per question, then recomputes focus areas. Trigger: /research-questions
SKILL.mdskills/research-questions/SKILL.md
GitHubResearch Questions
Input
A topic name, e.g. willow-run. Locate the topic's ingest directory:
C:\Users\soren\trm-vault\topics\charlie\<topic>\trm-ingest\.
Requires curator-decisions-final.json to already exist there (output of
curator-decision-processor.mjs).
Steps
Generate draft questions. Run:
node C:\dev\src\harvester\external\scan-gaps.mjs --decisions=<path to curator-decisions-final.json> --topic=<topic>This writesdraft-questions.jsoninto the same directory.Load existing state. If
research-questions.jsonalready exists in that directory, read it. Any question whoseid+question_origin_hashmatches an existingclosedentry is already resolved — carry it forward unchanged, do not re-search it. Any question with a new or changedquestion_origin_hash(or not present before) needs processing as below. This is what makes reruns incremental instead of re-asking settled questions.For each
openquestion needing processing:- Vision-read first, for
low-confidenceandgapquestions. Look for a local photo file matching the question'sfact_idunder<topic>/analyzed/variants/(glob<fact_id-stripped-of-any-.jpg-suffix>*— variants carry letter suffixes like_a,_c,76226_c.jpg). If a match exists,Readthe image file directly and judge from the actual pixels before touching WebSearch:- Content check: does what's visible in the photo match the question's topic label (e.g. factory-floor production vs. an airport/tarmac scene vs. a ceremonial/bond-drive setting)? A clear mismatch or match is a strong, directly-observed signal — weight it above a web search would ever get you, since these are unpublished archive originals with no other corroborating source.
- ID-integrity check: many prints carry a negative number stamped
directly in-frame (bottom-right corner is typical for this archive).
If a legible stamped number is visible and it does not match the
question's
fact_id, this is a metadata bug, not a content question — setid_mismatch_flagged: trueand record the stamped number you actually read inid_mismatch_observed. Cross-check against any other variant file for that stamped number (e.g. if76226_c.jpgreads76266-Cin-frame, check whether a76266_*.jpgfile exists and depicts the same shoot) — if so, note that correspondence in theanswerfield so a human can reconcile the two fact_ids. - If the vision read gives a clear verdict (content matches/doesn't
match, and no unresolved ID mismatch): set
status: 'closed',answer,confidence: 'high',sources: ['local-archive-visual-read']. - If an ID mismatch is flagged and can't be resolved by you alone
(i.e. it needs a curator decision, not just a note): set
status: 'escalated',escalation_reasondescribing the mismatch, keepid_mismatch_flagged/id_mismatch_observedset regardless of status. - If no local photo file matches the
fact_idat all, fall through to the WebSearch step below — there's nothing to look at.
- WebSearch fallback, for
curator-flaggedquestions and anylow-confidence/gapquestion with no matching local photo file: run WebSearch using the question text (andfact_id/topic as context) as the query. Record the exact query string(s) tried insearch_terms_used. - Increment
search_attempt_count(starts at 0, so first attempt = 1) whenever WebSearch is actually invoked; a vision-only resolution does not increment this counter. - Judge the search results yourself:
- If results clearly and specifically answer the question with a
source you'd trust: set
status: 'closed',answer(your synthesized answer),confidence: 'high',sources(the URLs that support it). - If results are thin, contradictory, or off-topic: set
status: 'escalated',escalation_reason(why — e.g. "no sources mention this photo directly" or "two sources disagree on date"), keep any partialansweryou can offer withconfidence: 'low'. - If the search errored or returned nothing at all: leave
status: 'open', just updatesearch_attempt_count,search_terms_used, andlast_attempt. Do not force a closed/escalated verdict on a failed search — that's a rule from the design spec, not a judgment call.
- If results clearly and specifically answer the question with a
source you'd trust: set
- If a search result (or a vision read) surfaces a new unknown not
covered by any existing question (e.g. a name or date mentioned that
raises its own question, or a cross-referenced fact_id from an ID
mismatch), append it to that question's
new_leadsarray as a plain string. Do not silently drop it, and do not create a new top-level question entry for it in this pass —new_leadsis picked up as input the next timescan-gaps.mjs-equivalent triage happens on this topic (future enhancement; for now it's just recorded). - Set
last_attemptto the current ISO timestamp regardless of outcome.
- Vision-read first, for
Write
research-questions.jsonin the topic'strm-ingest/directory: the full question list (open, closed, and escalated), matching the augmented shape from the design spec. Carrydeterministic_idandcreated_atforward unchanged fromdraft-questions.json— they're set once at generation time, not per-run. Also include a top-levelllmprovenance block recording what produced this run's verdicts:{ "model": "claude-sonnet-5", "run_at": "<ISO timestamp>" }— this is an audit trail (which model made the close/escalate calls), not a cache key; overwrite it each run.Recompute focus areas. Run:
node C:\dev\src\harvester\external\update-focus-areas.mjs --questions=<path to research-questions.json>This writesfocus-areas.jsoninto the same directory.Report to the user: how many questions were closed / escalated / still open, and the resulting focus areas ranked by priority.
Error handling
- If
curator-decisions-final.jsonis missing, tell the user and stop — do not fabricate a batch. - If
scan-gaps.mjsorupdate-focus-areas.mjsexit non-zero, surface the exact stderr to the user and stop; do not attempt to hand-write the JSON yourself as a substitute for a script failure. - Per-topic failures in a scheduled sweep (multiple topics processed in one invocation) must not stop the remaining topics — log which topic failed and continue to the next.