Analyses
Analyses
An analysis is a page that answers one question, like a Weights & Biases report: markdown prose, with views and dashboards embedded wherever the argument needs them, a contents list that stays in view as you scroll, and callouts that record what was decided and why. It is one Markdown file an agent can write.
On main now: <data-analysis> and <data-analyses> (dist/analysis.js, CONTRACT §19). The blocks below are the analyses example's own files, live.
Three tiers: Views → Dashboards → Analyses
- ViewsLiveOne dataset, many ways to see it: a table, a chart, a board, a map, all from one
.basefile. - DashboardsLiveMany views laid out on a grid over linked datasets: one rail filters them all, a click in one narrows the rest.
- AnalysesLiveA report page that answers one question: prose with views and dashboards embedded, a sticky contents list and decision callouts.
How it builds on the other two
- It embeds, it does not copy. A view in an analysis is the same
<data-views>on the same.basefile as everywhere else; a dashboard is the samelayout:. Change the file and every analysis that embeds it shows the change. - A sticky contents list down the side, from the page's headings and its decision, so a long analysis stays easy to move around. Its edge drags.
- Decision callouts: a boxed verdict (what was decided, and the evidence it rests on) that reads on its own when someone skims.
- A list of analyses, as a table, a gallery of cards or a board by status, so past answers are easy to find again.
An analysis page
An analysis, live <data-analysis src>
Liverobotparts' shoulder-actuator analysis, one Markdown file. On the left, the contents (sticky as you scroll; drag its edge). Then the header: status, question, the parts considered, the decision, Copy as Markdown and Download. Then the prose: an [!illustrative] load, a ```base fence drawn as a table and a compare view, one embedded chart from another example, and the [!decision] callout.
The live analysis starts when it scrolls into sight.
---
type: analysis
title: Shoulder actuator for a 1-DOF arm
question: Which actuator should drive the shoulder of a 1-DOF arm holding 0.3 kg at 0.25 m, under $150?
status: decided
parts:
- aifitlab-8541438771336
- aifitlab-8542347853960
- aifitlab-8565793194120
- jmc-60asm400-5-1024c
decision: aifitlab-8541438771336
author: robotparts.dev (example)
date: 2026-10-26
visibility: public
tags: [actuator, arm]
---
> [!example] An analysis on real catalog data
> The candidates' values are robotparts.dev's actuator rows (the robotparts-actuators case's `actuators.json`). The arm
> and its load are **illustrative**. This file is one Markdown file; the views below are its ```base fences.
## The requirement
> [!illustrative] The load (assumed, not measured)
> - Payload 0.30 kg at 0.25 m: 0.30 × 9.81 × 0.25 = **0.74 N·m**
> - Link 0.12 kg, centre of mass at 0.125 m: **0.15 N·m**
> - **Holding torque 0.88 N·m**: rated torque at least 0.9 N·m, peak at least 1.8 N·m, under $150
## The candidates
The catalog's own rows, filtered to this note's `parts:` (`this` is the note, as in Obsidian):
```base
data: ../robotparts-actuators/actuators.json
filters: 'this.parts.contains(id)'
views:
- type: table
name: Candidates
order: [name, price, rated_torque, peak_torque, torque_density, mass]
sort:
- { property: price, direction: ASC }
- type: compare
name: Side by side
order: [price, rated_torque, peak_torque, torque_density, mass]
compare:
better: { price: lower, mass: lower, peak_torque: higher, rated_torque: higher, torque_density: higher }
```
| Candidate | Rated ≥ 0.9 N·m | Peak ≥ 1.8 N·m | ≤ $150 | Passes |
|---|---|---|---|---|
| [[aifitlab-8541438771336\|Steadywin GIM4305-10]] | 1.05 (1.2×) | 3.82 | $70 | yes, slim thermal margin |
| [[aifitlab-8542347853960\|Steadywin GIM6010-8]] | 5.0 (5.7×) | 11.0 | $70 | yes |
| [[aifitlab-8565793194120\|Damiao DM-J4310-2EC]] | 3.5 (4.0×) | 12.5 | $125 | yes |
| [[jmc-60asm400-5-1024c\|JMC 60ASM400-5-1024C]] | 1.27 (1.4×) | 3.8 | — | no: no price on record |
## Where it sits in the whole catalog
The actuator-charts case's Ashby chart, embedded from its file: every actuator's peak torque against its mass.
![[../actuator-charts/views.base#Ashby · torque vs mass]]
> [!decision] Chosen: [[aifitlab-8541438771336|Steadywin GIM4305-10]]
> - Passes every gate at $70, the price of the GIM6010-8, at 150 g against 388 g.
> - **The risk is heat:** its rated torque is 1.2× the holding torque. If the arm holds at full extension for minutes,
> take the [[aifitlab-8542347853960|Steadywin GIM6010-8]]: same price, 5.7× margin, 238 g heavier.
<script type="module" src="/vendor/data-ui/data-ui.js"></script>
<script type="module" src="/vendor/data-ui/analysis.js"></script>
<data-analysis src="shoulder-actuator.md"></data-analysis>From the example Analyses · reports that mix text with live views · its own page · analysis.js fetches dashboard.js only for the first dashboard it meets
Embedding views and dashboards
Two ways in, both drawn live where they stand in the prose. A .base with layout: is drawn as <data-dashboard>; any other as a <data-views> (no URL state, the compact toolbar).
- A
```basefence is an inline.base: itsdata:is a path beside the analysis (or from/, a URL). ![[name.base]]on its own line embeds a file, looked for beside the analysis and then in eachembeds=folder;![[name.base#View]]embeds one of its views.this.<key>in any filter is the analysis' frontmatter, as Obsidian bindsthisin an embedded base:this.parts.contains(id)keeps the parts the note names.- Callouts:
> [!decision] Titleis the verdict box,[!illustrative]marks what is assumed,[!ai]sets a judgement apart from the numbers; any other type is a plain callout. data-ui check x.mdchecks the frontmatter, then every fence and embed against the data each names, with each problem at its line.
```base
data: ../robotparts-actuators/actuators.json
filters: 'this.parts.contains(id)'
views:
- type: table
name: Candidates
order: [name, price, rated_torque, peak_torque, torque_density, mass]
sort:
- { property: price, direction: ASC }
- type: compare
name: Side by side
order: [price, rated_torque, peak_torque, torque_density, mass]
compare:
better: { price: lower, mass: lower, peak_torque: higher, rated_torque: higher, torque_density: higher }
```![[../actuator-charts/views.base#Ashby · torque vs mass]]
![[../pid-tuning/views.base]]The second line is from Analytic or tuned gains, robot by robot, which embeds the pid-tuning dashboard whole.
The list of analyses
Every analysis, as a table, a gallery or a board <data-analyses base>
LiveOne row per analysis, from its frontmatter: the question, the status, the decision, the parts, when it was written. The tabs switch to a gallery of cards and a board by status; a click opens the analysis.
The live list starts when it scrolls into sight.
# The analyses list (docs/peps/0007-analyses.md): one row per analysis, from its frontmatter (source/build.py writes
# analyses.json with data_ui_analysis.build_index). <data-analyses> draws these presets; a click opens the analysis.
data: analyses.json
facets:
- { as: current, label: Current filters }
- { property: status, as: values, label: Status }
- { property: tags, as: values, label: Tags }
views:
# WHY Table first: a card with no image puts its selection box over the title's first letters (a core cards
# issue, reported to the integrator); analyses have no picture of their own
- type: table
name: Table
order: [title, status, decision, parts, question, date, author]
rowHeight: short
- type: cards
name: Gallery
order: [title, question, status, decision, parts_count, date]
cardSize: 320
sort:
- { property: date, direction: DESC }
- { property: title, direction: ASC }
- type: kanban
name: Board by status
order: [title, decision, date]
groupBy: { property: status }
groupOrder: [draft, open, decided, superseded]
open: { mode: link, url: url }
From the example Analyses · reports that mix text with live views · the rows are analyses.json, written by source/build.py with data_ui_analysis.build_index