SoA2USDM - from the printed table to schemas

A Schedule of Activities table is a 2D presentation of study logic, constrained by paper. Footnotes are the overflow for logic that does not fit the grid. SoA2USDM transcribes the table into five constructs, carries them through three schemas, and lets human judgement in only through sidecars. Stating the result in USDM is a separate step that is still being explored.

1

The constructs of an SoA table

Every SoA table, whatever its layout, is read as the same five constructs. They are the blocks of the extraction schema.

XXXX XXX XXX XXX a b c c a b c 1 2 3 4 5
Schematic. Bars stand for printed text, X for a schedule mark, small letters for annotation markers.
1
Schedule properties the header rows
schedule_properties

Schedule property definitions from table header rows. These define the temporal grid structure.

2
Schedule grid the header cells
schedule_grid

Cell values for schedule properties across columns. The temporal dimension of the table.

3
Activities the body rows
activities

Activity definitions from table body rows. Includes indentation_level for hierarchy reconstruction - parent-child relationships derived in resolved layer.

4
Activity schedule the body cells
activity_schedule

Scheduling data at activity × timepoint intersections. The core content of the SoA table.

5
Annotations markers and the notes they point to
annotations

Footnotes, legends, and other annotations with explicit marker_locations. These encode study logic that doesn't fit the 2D table presentation.

2

The same constructs through three schemas

Each schema answers one question and adds one kind of precision. Nothing is replaced on the way: an id in a later file always points to the entry it came from, down to the position on the printed page.

Layer 1

Extraction

What does this table show?
written by the model, one Claude pass one file per table
Layer 2

Resolution

What precisely is in this table?
derived by program one file per table
Layer 3

Consolidation

What was the protocol expressing?
derived by program one file per protocol
1Schedule properties
schedule_properties[]
row_position property_name property_type hierarchical_level

One entry per header row.

schedule_properties[]
property_id parent_property_id linked_annotation_ids

Each row gets an id and its parent.

property_hierarchy[]
property_id hierarchical_level property_name property_type

One hierarchy across the tables.

2Schedule grid
schedule_grid[]
row_position column_position cell_value merged_cell_range

One entry per header cell.

schedule_columns[]
column_id column_position column_values composite_label

Header cells read downwards become columns.

timeline_segments.<segment>[]
xcol_id segment property_values source_columns

Columns of all tables aligned into segments (main, domain, track, subsidiary).

3Activities
activities[]
row_position source_page activity_name activity_name_source.indentation_level

One entry per body row, with the page it was read from.

activities[]
activity_id parent_activity_id hierarchy_level is_section_header

Each row gets an id; the parent is derived from the indentation level.

unified_activities[]
xact_id source_refs match_status near_matches

Rows of several tables matched into one activity. The source rows stay listed.

4Activity schedule
activity_schedule[]
row_position column_position cell_value method

One entry per non-empty body cell.

activity_schedule[]
activity_id column_id cell_value cell_value_type

Positions become id references.

schedule_matrix[]
xact_id xcol_id consolidated_value source_values

One cell per unified activity and unified column. The value of each source table is kept.

5Annotations
annotations[]
annotation_marker annotation_type annotation_text marker_locations

Marker, text, and every place the marker is printed.

annotations[]
annotation_id referenced_elements annotation_scope document_references

Bound to the elements it applies to.

unified_annotations[]
xannot_id source_occurrences referenced_xacts referenced_xcols

Identical text deduplicated. Every occurrence is kept.

How an element is addressed
Addressed by position: row, column, page
Addressed by per-table ids: prop-NNN, col-NNN, act-NNN, annot-NNN
Addressed by cross-table ids: xact-NNN, xcol-NNN, xannot-NNN, each listing its source ids
Layers 1 to 3 stop at USDM-ready data. What they exclude on purpose are USDM-specific semantics. Those belong to USDM Instantiation, part 4.
3

How corrections work

Review finds things. A reviewer does not edit a file; the reviewer writes an entry in a sidecar, and a program applies it. There are two sidecars, one before Layer 2 and one before Layer 3.

The human is an adjudicator, not an editor. Judgement enters through an auditable sidecar, never by rewriting model output.

Table corrections written by a human

Layer 1.5 · one sidecar per table, only where needed · soa-table-corrections v1.1
Raw extractionmodel output, never overwritten
+
Corrections sidecarnames the raw file in target_extraction
→
Verified extractionsidecar applied by program
→
Resolutionreads verified, else raw
idThe entry's own id.
targetWhich block of the extraction it acts on.
schedule_propertiesschedule_gridactivitiesactivity_scheduleannotationsreview_itemstable_metadata
opWhat it does.
addsetremoveconfirmadd_itemremove_item
matchKey fields that locate the entry, e.g. row and column position.
set field valueWhat to write.
reason by atWhy, who, when.
source_refWhere on the printed page the correction is grounded.
review_itemThe judgement call this entry answers, when it answers one.

A table without a sidecar passes through untouched.

Match decisions written by a human

Layer 3 sidecar · one per protocol, only where needed · soa-consolidation-corrections v1.0
Resolved tables1 ... n of a protocol
+
Match decisions sidecarread before matching
→
Consolidationeach decided source row carries decision: correction_id, op
idThe entry's own id.
opWhat is decided about a pair of rows.
keepsplitmergerefines
source targetThe two source rows, named by table_number + activity_id, with activity_name as a check.
reason by atWhy, who, when.

That two rows are the same activity is a human statement, not a pipeline result. An entry whose premise no longer holds stops consolidation.

The extraction states its open judgement calls as data

review_items[]: id, severity, call_made, alternative

→

A correction answers by naming the item

review_item with op confirm: the call stands. With any other op: the alternative was taken. An item is decided exactly when an entry names it.

4

USDM Instantiation

work in progress

Not a fourth layer. Layers 1 to 3 stop at USDM-ready data. Turning one consolidated SoA into a USDM v4 document is a separate step that has only just started to be explored.

Work in progress. Parts 1 to 3 describe what is in place. This part only indicates the direction. A first minimal build exists, and everything here can change.
Consolidated SoAthe Layer 3 file
+
Study shellthe study structure USDM requires around an SoA
→
USDM v4 documentwith a log of what was decided, derived or left out

The idea of a study shell

  • USDM has no standalone serialization of an SoA. A ScheduleTimeline hangs under a StudyDesign, under a StudyVersion, under a Study.
  • A StudyDesign mandates arms, cells, elements, a population, eligibility criteria and a model. An SoA contains none of these.
  • The shell is that surrounding structure, built from a manifest of a few facts about the protocol: identifier, title, sponsor, epoch list (usdm-manifest v1.0). What is not stated is emitted as an object flagged not stated.
  • The shell and the SoA meet at exactly one attribute, ScheduledInstance.epochId. The epoch list is the one part of the shell the SoA really needs.

The SoA lifting

  • The consolidated table is rendered as USDM schedule objects: activities and scheduled instances in a timeline and, where the table supports it, timings and encounters.
  • Which column property carries the epoch axis is an interpretation. It is stated outside the Layer 1-3 schemas and logged as a decision.
  • Each build is checked against a pinned usdm-rdf release.

What a full-fledged instantiation would need. SoA patterns, to interpret what the timeline and the notes express, and potentially some kind of shared nomenclature. Neither is part of soa2usdm today.