@startinblox/research-summary
v1.0.1
Published
An internal PoC of application summarizing the contributions and budgets activities linked to our different research projects
Downloads
297
Keywords
Readme
@startinblox/research-summary
An internal PoC summarising the contributions and budget activity linked to our research projects: what a small team worked on, how far each topic got, where it landed, and what was declared against it.
The backend is research-summary. This package is built from solid-boilerplate.
Usage
<solid-research-summary
data-src="https://research.example/api/topics/?period=2025-W15"
period="2025-W15"
></solid-research-summary>Running it
npm ci
npm run demo # http://localhost:5173 — the whole UI, on offline fixtures
npm run storybook # http://localhost:6006 — one component at a time
npm run cy:run # 27 helper tests + 80 component tests
npm run build # the library
npm run build-demo # the SPA, which is what the deployment servesThe demo runs on recorded fixtures by default — no backend, no GitLab token, no
spreadsheet. To point it at a running API, append ?api=http://localhost:8000.
The same built page does both, so a bug report can carry the mode in its link.
Node 22 or later. Cypress 15 does not run on Node 18 — it fails with
tsx must be loaded with --import instead of --loader, and piping the output throughtailhides it, so a broken run looks like a hang.
The components
| Element | What it is |
|---|---|
| <solid-research-summary> | the Orbit entry point — data-src at a topics container |
| <research-topics> | the list of topics for a period, with theme filters |
| <research-topic> | one topic in full: traces, review state, propagation, hours |
| <research-summary-card> | what a model said about a topic, and how much of it to believe |
| <research-period> | week / month / quarter / year, stepping, list and calendar |
| <research-budget> | declared hours and cost, grouped, with the four attribution buckets |
| <research-readiness> | how far a topic got |
| <research-reach> | where its code is known to have travelled |
| <research-attribution> | hours, and the strength of the tie that produced them |
Presentational components take .object (or .objects) and emit events up:
topic-selected, period-selected, summary-requested, group-selected.
None of them fetch anything. <solid-research-summary> and the demo shell are
the only two places that talk to a backend.
Rules the components hold, not just follow
These are why several of them exist as separate elements, and each has a test that fails if it is broken.
No state is communicated by colour alone. Readiness, reach and attribution each ship a glyph, a word and a colour together. A monochrome print-out and a reader with a colour vision deficiency get the same information.
Hours never appear without their attribution. 13.5 h declared and
11 h inferred are different claims — the first came from a timesheet row
naming the issue, the second from "this was the only thing they touched that
day". <research-attribution> exists so the two cannot be separated, and a
topic with no attributed hours renders nothing rather than 0 h, because zero
reads as they spent no time on it when it means no row could be tied to it.
Nothing claims a feature is complete. <research-readiness> reports
GitLab's own state — merged, in review, in progress, no recent trace. There is
no COMPLETE at any granularity, and a test greps for the vocabulary.
An empty period says which kind of empty. "The snapshot holds nothing for this period" is a statement about the snapshot. "No topic matches the current filters" is a statement about the filters. Neither is a statement about anyone's week, and the component distinguishes both from loading.
Nothing orders people. No commit count, no rank, no sortable per-person
column on the topic list. <research-budget> does show cost per person, which
is a deliberate decision recorded in the backend's
constraints.md —
but there is no ratio anywhere between what somebody produced and what they were
paid.
Slow must not look like broken. A local model takes a minute or two per
topic, so <research-summary-card>'s waiting state has motion, an elapsed
counter that appears only once the wait stops being momentary, and a sentence
saying a long wait is expected. It respects prefers-reduced-motion, because an
animation running for two minutes is exactly what someone turning that off was
trying to avoid.
Layout
src/
components/
solid-research-summary.ts the Orbit component
ui/ the presentational elements
helpers/ui/periods.ts period arithmetic, mirroring the backend's
mocks/ the recorded JSON-LD, and an offline backend
styles/research.scss one stylesheet, into every shadow root
demo/ the SPA that composes them all
stories/ one file per component, every state
cypress/
component/ 80 tests
e2e/helpers/ 27 tests, all pure functionssrc/helpers/ui/periods.ts is a deliberate mirror of the backend's
app/services/periods.py, down to the regex that refuses 2025-13. Change one,
change the other — both suites assert the same cases.
What is not proven
<solid-research-summary> depends on the sib store, and that path has not been
run against a live Orbit application. The demo drives the presentational
components through a thin shell instead, so it shows what is actually proven
rather than quietly becoming the test that Orbit integration works.
