gro-light
a medium for publishing scientific work as it is made. In a collaborative graph rather than piles of pdfs.
the graph consists of typed nodes and edges between them. nodes include claims, evidence, methods, materials, code, datasets. representing research this way, one thing becomes obvious: methods, code packages and other shared resources are the real generators of progress. making them visible as first-class contributions makes them legible to institutions that can then reorganize their incentive mechanisms to take such contributions more explicitly into account.
claims are represented as relationships between two entities and are thus often nexuses of cross-team collaboration: is rs12979860 (a single-nucleotide polymorphism) associated with more spontaneous clearance of hepatitis c? this also means that multiple groups do, can and should add evidence to the same claim, debating, contesting or replicating its veracity. this makes contested points in the research visible and again, allows incentives to be built around resolving them.
this medium is not meant to replace papers entirely but to supplement the scientific record with a more comprehensive and faster-growing body of knowledge and insight. papers can still be written up once everything is done, and that’s great. but papers will never include all the important contributions to science that this graph captures, like code, specific tools, negative results.
the graph is also incredibly easy for llms to traverse and retrieve, allowing researchers to verifiably fetch all claims about entity x, or all datasets that have been analyzed with method y.
here’s an example graph of four papers on the genetics of hepatitis c treatment response. it looks messy, but it’s incredibly easy for a machine to tell you which contributions were key. and even at a glance, you can see which datasets (green squares), methods (orange diamonds) and software (purple triangles) were key, and which claims (blue circles, or hexagons where several papers state the same one) were central (literally). the grey rectangles are individual pieces of evidence — the single numbers behind each claim, and the most numerous thing in the picture.
click the graph to open it full size
this is the schema for gro. note this is still evolving in order to be maximally helpful to scientists.
Claim
id: gro:2026-03-11-wakita-jfh1#cl1
kind: claim
subject: {id: wd:Q18039731, label: IL28B}
predicate: {label: predicts}
object: {id: wd:Q7111590, label: sustained virological response}
qualifiers:
population: [{label: patients with chronic HCV genotype 1}]
system: [] # experimental system node ids
intervention: [{label: peginterferon alfa-2b plus ribavirin}]
adjusted_for: [{label: baseline viral load}]
contrast: CC vs TT
direction: increase
timepoint: week 72
other: null
negated: false
hedged: false
basis: own_result # own_result | from_other_work | interpretation
framed_as: extension # replication | contradiction | extension
origin:
by: [orcid:0000-0002-1825-0097]
at: "2026-03-11" # stamped on publication, not written by hand
grounding: {artifact: runs/2026-03-09/titration.csv, locator: "row 14"}
also_stated_in:
- by: [person:m-thompson]
in: doi:10.1002/hep.23192 # a paper here; any published id works
quote: "…"
locator: "Results, para 3"
basis: own_result
Evidence
One number per evidence node. Spread and uncertainty are recorded.
id: gro:2026-03-11-wakita-jfh1#ev3
kind: evidence
description: odds of SVR for CC versus TT genotype
measure: odds ratio
value: 7.3
value_range: null # {low, high} when the reported value is itself a range
unit: null
ci: {low: 5.1, high: 10.4, level: 0.95} # level null if you don't report one
error: {type: se, value: 0.18} # sd | se | iqr | range | other
p: "<0.0001"
test: logistic regression
n: 1137
population: {label: patients with chronic HCV genotype 1}
outcome: sustained virological response
reference_level: TT genotype
origin: {by: [orcid:0000-0002-1825-0097], at: "2026-03-11"}
grounding: {artifact: runs/2026-03-09/genotype-svr.csv, locator: "row 22"}
error takes {type, value} for sd and se, {type, low, high} for iqr and range. If you publish a bare ”±” without saying what it is, it records as other — the schema never guesses a standard deviation on your behalf.
outcome and reference_level say what the number is for, so it keeps its meaning away from the write-up that surrounds it: an odds ratio for persistence stays an odds ratio for persistence even when you go on to discuss clearance.
Evidence that only counts together is linked through jointly_with on its edges.
A negative or null result is an evidence node like any other, published the same way, and the claim it bears on carries negated: true. Nothing in the schema makes a result that did not work harder to publish than one that did.
Method
id: gro:2026-03-11-wakita-jfh1#me1
kind: method
label: JFH-1 subgenomic replicon assay
status: new # grounded | new | composite | from_other_work | unattributed
from_other_work: null # {named: "Lohmann 1999", id: doi:…} when status says so
used_in: [gro:2026-03-11-wakita-jfh1, doi:10.1002/hep.23192]
origin: {by: [orcid:0000-0002-1825-0097], at: "2026-03-11"}
grounding: {…}
Experimental system
The material a result is obtained in, as its own node, so that reuse shows up as one shared node rather than a repeated string. This is the node that makes “nine groups built on our construct” answerable.
id: gro:2026-03-11-wakita-jfh1#sy1
kind: system
label: pJFH1
system_kind: construct # cell_line | primary_cells | replicon | construct |
# pseudoparticle | animal_model | other
identifiers:
- {scheme: genbank, value: AB047640}
produced:
by: [orcid:0000-0002-1825-0097]
here: produced_here # produced_here | reused | not_stated
from_other_work: null
new: true
origin: {by: [orcid:0000-0002-1825-0097], at: "2026-03-11"}
used_in: [gro:2026-03-11-wakita-jfh1, doi:10.1038/nm1268]
grounding: {…}
identifiers takes the schemes genbank, rrid, atcc, addgene, doi, url and other. Systems derived from other systems are linked by derived_from, which records the modification.
Data
id: gro:2026-03-11-wakita-jfh1#da1
kind: data
label: IDEAL study
identifiers:
- {scheme: nct, value: NCT00081770}
produced:
context: >-
phase 3 RCT of peginterferon alfa-2b vs alfa-2a with ribavirin,
genotype 1, US sites
by: [person:j-mchutchison]
from_other_work: {named: "McHutchison 2009", id: doi:10.1056/NEJMoa0808010}
here: reused # produced_here | reused | not_stated
status: public_registered
access: not_stated
origin: {by: [orcid:0000-0002-1825-0097], at: "2026-03-11"}
used_in: [gro:2026-03-11-wakita-jfh1]
grounding: {…}
identifiers records every identifier the dataset has, each with a scheme: nct, doi, zenodo, dbgap, geo, sra, genbank, pdb, dbsnp, ega, url, other. An empty list means none was given.
produced.context is the study or setting the data came from — trial, cohort, lab system, collection period, site. produced.by is who made it. origin stays what it is for every node: whoever first brought the data into the graph, which for reused data is rarely whoever produced it.
Code
id: gro:2026-04-02-wakita-svr-model#co1
kind: code
label: hcv-svr-glm
code_kind: project_code # package | project_code
status: released # released | stated_not_released
url: https://github.com/…
version: v1.2.0
produced: {by: [person:…], here: produced_here}
origin: {…}
grounding: {…}
Person
A person is a node so that credit is a link rather than a repeated string, and so that the same person publishing twice is one node. This is what makes the graph answerable in the direction that matters to the people in it: what have I put into the world, and what has been built on it.
id: orcid:0000-0002-1825-0097
kind: person
name: "Takaji Wakita"
also_published_as: ["T. Wakita"]
ids:
- {scheme: orcid, value: "0000-0002-1825-0097"}
- {scheme: gscholar, value: "xxxxxxxxxxxx"}
- {scheme: ror, value: "https://ror.org/…"} # affiliation handle
first_seen_in: gro:2026-03-11-wakita-jfh1
ids is a list because one person is reachable through several handles, and because the handle one record carries is not always the one you want to join on. Schemes in use: orcid, gscholar, ror, isni, wikidata, email, url, other. The list may be empty — a person named with no identifier anywhere is still a person node, with a local person:<slug> id.
Credit lands in two places, which answer different questions:
origin.byon a node — who is credited with introducing it, and when.produced.byon data, systems and code — who made the material, which is often not the person who introduced it to the graph.
An authored_by edge runs from any node to the people credited for it, so the question can be asked of the graph directly rather than by walking back through origins.
Edges
| type | from → to |
|---|---|
supports, contradicts | evidence → claim (optional jointly_with: [evidence ids]) |
produced_by | evidence → method |
uses_data | evidence → data |
uses_code | evidence or method → code |
uses_system | evidence or method → system |
composed_of | method → method (records the modification) |
derived_from | system → system (records the modification) |
authored_by | any node → person |
| matcher relations | claim → claim; matcher runs only |
Every edge carries from_kind and to_kind, so a consumer never has to look its endpoints up, and either stated_in or made_by.
Claim relations derived from the fields
For two claims from different groups, these follow from the fields alone and are not stored:
- exact match — same subject, predicate, object,
negatedand qualifiers. They are one node. - refines — same subject, predicate, object and
negated; one claim’s qualifiers are a strict superset of the other’s. - conflicts — same subject, predicate and object, opposite
negated, and one claim’s qualifiers contain the other’s. - same relation, other scope — same subject, predicate, object and
negated; the qualifiers differ and neither set contains the other. - same relation, inverse outcome — one claim’s object is the vocabulary inverse of the other’s (direction flipped, contrast dropped), with subject, predicate and
negatedagreeing.
Anything else that looks alike is for a matcher, which emits the same relation vocabulary — same_claim, refines, same_relation_other_scope, conflicts, agrees — as a made_by edge. The division is that the field rules fire when subject, predicate and object all match and only the qualifiers are in question; a matcher fires when the entities match but the predicates differ, and has to read them to type the relation. The predicate is a matching input, not a matching key: with an open predicate vocabulary, most predicates are used once, and keying on them would stop the field rules from ever firing across groups.
What this draft leaves out
- Versioning and concurrency, which publishing-as-you-go needs and this draft does not have. A claim published early and revised later, several people adding to one claim at once, what merges and what conflicts, and what a reason-for-change is attached to — all open. The schema above is written as though nodes are published once.
- Reference lists. There are none. Work published here credits the work it builds on inline, at the point of use, through
from_other_work— not in a bibliography at the end that someone has to resolve afterwards. - Vocabulary resolution. How entity labels and predicates settle onto shared terms, and the shared vocabulary itself.
- The tooling, and the publishing endpoint that would mint ids and stamp the dates on
origin.
