@ara-commons/ara-skills 0.3.1 → 0.4.0

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.
@@ -0,0 +1,211 @@
1
+ # Parsing tolerance — normalize the exploration tree across ARA variants
2
+
3
+ The canonical `exploration-tree-spec.md` uses **generic** field names; many real artifacts instead
4
+ use **type-named** fields. Parse against the *model*, tolerate the *variants*. Never
5
+ hardcode to one example; never assume a fixed field set. The goal: **every node yields an `id`,
6
+ `type`, `title`, `body`, and a `parent`** (plus optional metadata), no matter which dialect.
7
+
8
+ ## 1. Root form
9
+
10
+ Accept both:
11
+ - `tree:` → a **list** of root nodes (canonical).
12
+ - `root:` → a **single** root node. Treat it as a one-element root list.
13
+
14
+ Children nest under each node's `children:` list. A node's `parent` is the nearest enclosing node;
15
+ top-level nodes have `parent: null`.
16
+
17
+ ## 2. Node identity & type
18
+
19
+ - `id` — required (any stable identifier the tree uses, e.g. `N01`, `E07`). Keep verbatim.
20
+ - `type` — one of `question | experiment | decision | dead_end | pivot | insight` (or anything else;
21
+ unknown types still render with a neutral glyph). Keep verbatim.
22
+ - `support_level` — `explicit | inferred` if present, else `null`.
23
+ - `source_refs` — list of strings if present, else `[]`. **External pointers; shown, never resolved.**
24
+ - `isolated: true` — carry through (renders in a separated box).
25
+ - `also_depends_on: [ids]` → emit as `depends_on` (DAG cross-edges).
26
+ - `thinking` — verbatim agent deliberation, **passed straight through** (the primary reasoning block).
27
+ Absent ⇒ omit. Never paraphrase or synthesize it.
28
+ - `code_change` — when the compiler wrote one onto the node (`base_artifact` / `variant_artifact` /
29
+ `lang` / `diff_file`), **pass it through**. The `diff_file`→`diff` inlining and the top-level
30
+ `artifacts[]` index are done in the binding/inline step (binding.md); the visualizer never computes a
31
+ diff itself. Absent ⇒ omit.
32
+
33
+ ## 3. Title + body normalization (the dialect bridge)
34
+
35
+ For each node, derive a **display title** and a **body** by probing known fields **in order**, per type.
36
+ Use the first non-empty match; fall back gracefully so nothing is blank.
37
+
38
+ | type | title — try in order | body — try in order |
39
+ |-------------|-----------------------------------------------|-------------------------------------------------------|
40
+ | question | `title` → `question` | `description` → `question` (if used as title, leave body empty) |
41
+ | experiment | `title` → first sentence of `experiment` | `result` → `outcome` → `experiment` |
42
+ | decision | `title` → first sentence of `decision` | `choice` (+ `alternatives`) → `decision` |
43
+ | dead_end | `title` → first sentence of `dead_end` | `failure_mode`/`why_failed` (+ `hypothesis`,`lesson`) → `dead_end` |
44
+ | pivot | `title` → `trigger` | `from` → `to` → `trigger` |
45
+ | insight | `title` → first sentence of `insight` | `description` → `insight` |
46
+ | *(other)* | `title` → `<type>` field → `description` | the `<type>` field → `description` |
47
+
48
+ Rules:
49
+ - "first sentence of X" = X truncated at the first `. ` or ~80 chars — only when there is no separate
50
+ `title`. If a single field is both the only content and long, use a truncated form as title and the
51
+ full text as body.
52
+ - `decision`: append `alternatives` to the body as "alternatives: a; b" when present.
53
+ - `dead_end`: prefer to show *why it failed* in the body — it is the most valuable content.
54
+ - Never emit an empty title. If truly nothing usable, use the `id`.
55
+
56
+ ## 4. `evidence:` routing
57
+
58
+ A node's `evidence:` (and a decision's `evidence:` string) may mix kinds. Split tokens and route:
59
+ - `C\d+` → claim IDs → drive the `why` / `result` binding (see `binding.md`).
60
+ - `Figure N` / `Table N` / a figure/table filename → evidence file refs → `result.figures`/`tables`.
61
+ - `§...` / prose → keep as a `source_refs`-style chip (context only).
62
+
63
+ ## 5. Provenance
64
+
65
+ May appear as:
66
+ - a per-claim `provenance:` / `Provenance:` field, **or**
67
+ - prose in the `claims.md` header ("all claims are `ai-executed`; C06, C08 are `user-revised`").
68
+
69
+ Capture whichever exists and attach the right tag to each claim in `why[]`. If neither exists, omit
70
+ provenance — do not guess.
71
+
72
+ ## 6. Claims, experiments, evidence index (the hub layers)
73
+
74
+ - `logic/claims.md` — split on `## C\d+` headings. Pull `Statement`, `Status`, `Conditions`,
75
+ `Falsification criteria`, `Proof` (→ `E##`), `Sources` (verbatim «quote» + `← file:line`),
76
+ `Dependencies`. Field labels are bold-prefixed (`**Statement.**` or `- **Statement**:`) — match both.
77
+ - `logic/experiments.md` — split on `## E\d+`. Pull `Verifies` (→ `C##`), `Run`, `Setup`, `Metrics`.
78
+ - `evidence/README.md` — parse the Tables/Figures index to build `claim_id → [evidence files]`.
79
+
80
+ ## 7. Degrade, don't fail — the tree is the only hard requirement
81
+
82
+ Any missing/oddly-shaped field → fall back per the tables above and continue. A smaller honest view
83
+ is correct. **Hard-stop on exactly one condition: `trace/exploration_tree.yaml` is absent or parses to
84
+ zero nodes** (nothing to show). Minimal-validity guard = *the tree parses AND yields ≥1 node* — this
85
+ replaces the old "`PAPER.md` missing ⇒ not an ARA" guard, which no longer hard-stops (a missing
86
+ `PAPER.md` just means the visualizer synthesizes a minimal `meta` from a tree-level `title:` / the dir
87
+ name). Everything else — `logic/`, `evidence/`, `src/`, the four enrichment layers — is optional; absent
88
+ ⇒ contributes nothing, never an error.
89
+
90
+ ### 7a. Raw-trajectory input mode (first-class)
91
+
92
+ The minimum a node needs to render a useful step is `id` + (`title` **or** a type-named text field);
93
+ `body` / `thinking` are optional but make the step legible. So a **bare exploration tree with no
94
+ `logic/`, no `evidence/`, and no `PAPER.md`** — i.e. a raw agent run — is a fully supported input, not a
95
+ degraded one. Each node renders from its own normalized `title` / `body` (per §3) plus its `thinking`
96
+ (the agent's deliberation, when the source carries it — a verbatim pass-through field); the
97
+ `why` / `result` / `how-verified` blocks are simply empty and omitted.
98
+
99
+ **Adapter recipe (generic agent run → minimal tree).** A typical agent log is a sequence of steps, each
100
+ a `{thought, action, observation/result}`. Map it onto the tree:
101
+ - one tree node per step (or per meaningful decision/experiment); `id` = the step index/label.
102
+ - `type` from the step kind: a tried approach → `experiment`; a chosen direction → `decision`; an
103
+ abandoned/failed approach → `dead_end`; an opening/guiding question → `question`.
104
+ - `title` = a one-line summary of the step (first sentence of the action, ≤80 chars).
105
+ - `thinking` = the agent's thought/deliberation for the step (**verbatim** — why it did/branched).
106
+ - `body` = what it actually did + what came back (action + observation).
107
+ - `source_refs` = a pointer back to the log line(s) (shown, never resolved).
108
+ - nesting via `children`; convergence via `also_depends_on`; a discarded branch via `isolated`.
109
+
110
+ No `logic/` or `evidence/` is required; enrich the same tree later (via the compiler) to add claims,
111
+ evidence, and per-node `code_change` diffs.
112
+
113
+ # 8. The four `logic/` enrichment layers (all optional)
114
+
115
+ These produce the OPTIONAL `context` / `glossary` / `dependencies` / `recipes` keys (and the per-node
116
+ `built_on` / `rejected_here` / `recipe_refs` / `concepts` fields, derived in `binding.md`). **Each is
117
+ independent and absent ⇒ omit its key entirely** (no empty stubs). The renderer is inert for any key
118
+ it doesn't see, so an ARA with none of these renders exactly as before.
119
+
120
+ **Governing rule — classify by markdown SHAPE + generic cross-ref regexes; NEVER key on field
121
+ vocabulary.** Do not look for "optimizer", "Fig.", "loss", "shortcut", etc. — only headings, bold-lead
122
+ labels, id tokens, and the ref battery below. Verbatim everywhere: `text`/`definition`/`delta`/`value`,
123
+ all numbers, quotes, table cells, relation strings, and headings are reproduced exactly; a missing
124
+ field is `""`/`[]`; positive-absence signals (`present:false`, `unstructured`, `is_footprint`,
125
+ `inferred_id`) are *shown*, never invented.
126
+
127
+ ## 8.0 Shared sub-routines (define once, reuse for every layer)
128
+
129
+ - **`splitEntries(sectionText)`** — try delimiters in order, take the first that yields ≥1 hit:
130
+ (a) `^### ` H3 headings; (b) `^## ` with a leading id token (`^##\s*([A-Za-z]{1,3}\d+|RW\d+|[OGA]\d+)`);
131
+ (c) top-level bold-lead bullets `^\s*-\s*\*\*(.+?)\.?\*\*`; (d) blank-line-separated paragraph blocks.
132
+ (d) always succeeds ⇒ prose-only dialects still yield entries.
133
+ - **`probeFields(entryText)`** — collect every `^\s*-?\s*\*\*([^*]+?)\*\*\s*[:—]` labeled leader into an
134
+ **open** `[{label,value}]` list. **Labels verbatim, no whitelist.** Empty ⇒ `unstructured:true`,
135
+ `fields:[]`.
136
+ - **`harvestRefs(text)` → `typed-ref[]`** — run the FULL union of patterns; never assume one scheme.
137
+ The shared **typed-ref** is `{ "raw":"<verbatim token>", "kind":"…", "target":null }`; `target` is set
138
+ later in the binding pass (it stays `null` for anything that doesn't resolve inside this ARA).
139
+
140
+ | pattern | kind |
141
+ |---|---|
142
+ | `\[?\b(C\d{1,3})\b\]?` (optionally `(claims.md…)`) | `claim` |
143
+ | `\bRW\d{1,3}\b` | `related_work` |
144
+ | `\b([OGA])\d{1,3}\b` | `observation` / `gap` / `assumption` |
145
+ | `\bK\d{1,3}\b` or a minted term-id | `concept` |
146
+ | `PR\s*#?\d+` | `pr` |
147
+ | `arXiv[:\s]\S+` | `arxiv` |
148
+ | `10\.\d{4,}/\S+` | `doi` |
149
+ | `[\w./-]+\.\w+:\d+(?:-\d+)?` · `trace:N\d+` · `logic/\S+#\w+` | `source` / `node` |
150
+ | `§[\w".]+` · `Table\s*\S+` · `Fig\.?\s*\S+` · `Eqn\.?\s*\S+` | `figure` |
151
+ | `https?://\S+` | `url` |
152
+
153
+ ## 8.1 `logic/problem.md` → `context`
154
+ Absent ⇒ omit (do NOT fabricate context from claims). Split on `^## `; classify each heading by a
155
+ **case-insensitive role-synonym map** (NOT exact names): `setting ⇐ {setting,task,constraints,
156
+ background,context,problem statement}`; `observations ⇐ {observation*,findings,evidence}`;
157
+ `gaps ⇐ {gap*,open question*,challenges,limitations of prior work}`; `insight ⇐ {key insight,insight,
158
+ core idea,approach,thesis}`; `assumptions ⇐ {assumption*,scope,caveats}`. Unmatched ⇒ `role:"other"`,
159
+ heading kept verbatim, **never dropped**. **Merge** same-role sections. Any canonical role that never
160
+ appeared ⇒ emit a `{present:false}` stub (signals the compile, greys the chip). Per section run
161
+ `splitEntries`; `ent_id = \b([OGA]\d+)\b` (width-agnostic) else positional + `inferred_id:true`;
162
+ `ent_title` = text to the first `:`/`—`/`.` else first sentence; `probeFields` (empty ⇒
163
+ `unstructured:true`); `text` = verbatim body (mandatory); `harvestRefs`. `summary` = the insight
164
+ entry's first sentence (else `""`).
165
+
166
+ ## 8.2 `logic/concepts.md` → `glossary`
167
+ Absent ⇒ omit (⇒ popovers globally disabled). **Detect grouping:** a `##` is a *group* (not a term)
168
+ iff it has no `—`/`:` id-separator AND the next non-blank line is a `- ` bullet AND no following
169
+ `**Definition`. Split terms via `splitEntries` (`^##\s+(.+)$` → `^- \*\*(.+?)\.?\*\*` → paragraphs).
170
+ Per term: `term_id` = leading `^(K|C|RW|[A-Z]{1,3})\d{2,}` before the separator, else **mint**
171
+ `G01,G02,…` positionally (a stable popover anchor); `name` = heading minus id/separator (mandatory);
172
+ `aliases` = split a trailing `(…)` and `/`; `definition` = `**Definition**:` value else whole body
173
+ (mandatory); `probeFields` (captures resnet-style `Notation`/`Boundary conditions`; nothing for
174
+ codex/ptb — correct); `related` = any `/relat/i`-labeled field split on commas (resolved to `term_id`s
175
+ in binding); `harvestRefs`. **Build `lexicon`** = lowercased `name` + each alias → `term_id`, skipping
176
+ tokens <3 chars and pure-numeric aliases, first-wins on collision (the renderer uses it for popovers).
177
+
178
+ ## 8.3 `logic/related_work.md` → `dependencies`
179
+ Absent ⇒ omit. Strip the preamble before the first `## ` → `preamble`; parse `**word** (gloss)` pairs →
180
+ `legend[]`; capture a `> **Cross-agent attribution.**` blockquote → `attribution`. Split on `^## `,
181
+ match `^##\s*(RW\d+)?\s*[—:-]?\s*(.*)$`. If a heading has no `RW\d+` and matches `/brief|additional
182
+ referenced|citations/i`, parse its bullets into `footprint[]` (`{ref_id,citation}`) and skip structured
183
+ probing (`is_footprint` entries, or just the `footprint` tail). Per entry: `relation_raw` from a
184
+ ` — **type** ` in the heading ELSE a `**Type:**` line — **kept verbatim**, incl. compound
185
+ (`bounds / refutes`) and transition (`extends → quarantined`); `relation_norm` = the first of
186
+ `{baseline,imports,extends,bounds,refutes}` found, else `""` (color only); `name` = heading after the
187
+ separator; `delta` = `**Delta**`/`What changed:`+`Why:` (concatenate) else body; `adopted` =
188
+ `**Adopted elements**`; `grounding`/`claims` = `harvestRefs` partitioned by kind; `cross_agent:true` if
189
+ the entry is named in `attribution` or its relation mentions another agent. Unnumbered ⇒ positional +
190
+ `inferred_id`. **Degrade:** an entry with no source and no claim keeps `grounding:[]`+`claims:[]` (the
191
+ renderer shows a muted "ungrounded").
192
+
193
+ ## 8.4 `logic/solution/*` → `recipes`
194
+ Glob `logic/solution/*.md`; dir absent ⇒ omit. **Role-classify each file by content signal (priority):**
195
+ filename `constraints.md` → `constraints`; filename `method.md` OR title `/method|recipe|procedure|
196
+ process|pipeline/i` → `method`; uniform `^##\s*[A-Z]\d+:` entries → `heuristics`;
197
+ `## Mathematical formulation`/pseudocode fence/`$…$` → `algorithm`; repeated component `###` micro-schema
198
+ → `architecture`; else → `recipe`. (Only the `constraints.md` filename is special — a universal ARA
199
+ convention, not a field term.) Split each file on `^## ` (recurse `###`). **`heading` kept verbatim —
200
+ never normalized.** Per section pick the **dominant `kind`** in order: table `| … |` → `table`
201
+ (`markdown`); typed bullets `- **K**: v` → `kv` (`fields`); numbered `^\d+\.` → `steps`;
202
+ fenced/ASCII-DAG → `code`; `$$…$$`/`$…$` → `math` (put TeX in `code`); else → `prose`. `text` = verbatim
203
+ body (mandatory fallback); `sec_id` = leading id; `warn` = body under a `/confound|cut-off|incomplete|
204
+ not specified|unverified/i` heading; `refs` = `harvestRefs` (harvest **bare** `C05` too, not only
205
+ `[C05]`). **Degrade:** only `constraints.md` present ⇒ render just it (no "method missing" stub);
206
+ `method.md` absent but `algorithm.md`/`architecture.md` present ⇒ those carry the method role.
207
+
208
+ ## 8.5 Universal absence rule
209
+ Each key is independent; any combination is valid. A `minimal-artifact` (only `problem.md`+`claims.md`)
210
+ yields `context` only — `glossary`/`dependencies`/`recipes` omitted, all per-node enrichment fields
211
+ `[]`, nothing errors.