Annotation Form Performance Baseline¶
This benchmark records the v1 annotation-form baseline before AF2 and its Angular 22 comparison point after Track 0 and Phase 1. Phase 3 uses the Angular 22 measurement as its before/after reference.
Results¶
| Date | Commit | Machine | Initial render | Study navigation | Keystroke-to-paint p95 | Tab switch |
|---|---|---|---|---|---|---|
| 2026-08-04 | 72f38768c |
local dev | 4461 ms | — | 93 ms | 388 ms |
| 2026-08-06 | 4c1af0a20 |
GitHub Actions ubuntu-latest / Angular 22 |
8210 ms | 2665 ms | 111.3 ms | 3679 ms |
Seeded shape¶
The Playwright scenario creates one study and 100 textbox annotation questions: 60 Study questions and 40 Cohort questions. The cohort set has 20 root questions and 20 child questions whose Target.ParentId values each point to a root. It then creates 15 cohort units through the annotation UI before sampling keystroke-to-paint and tab-switch latency.
Invocation¶
From e2e/, with the local E2E infrastructure and .NET services running:
The test attaches the four metrics as perf JSON to the Playwright result and also prints them with console.table.
Angular 22 re-measure (2026-08-06)¶
The repository's isolated full-E2E workflow measured the Angular 22 platform on PR #2702, run 31059742925. The scenario passed on its second retry after two study-navigation timeouts; the successful attempt emitted the values above. Runner and retry variability mean these are a comparison baseline rather than a product service-level objective. Phase 3 must run the same isolated scenario and report its delta against this row.
AF2 Phase 3.5 performance gate¶
The AF2 benchmark is a separate Playwright scenario so the v1 history above
remains unchanged. It overrides only the browser response for
appConfig.env.json, setting annotationFormV2 to "true" for that test; no
committed runtime configuration or default feature flag is changed.
The scenario creates one ordinary, non-extraction review stage with 1,000
supported textbox questions split evenly between Study and Cohort. AF2 has
one synthetic Study unit in this shape; Cohort deliberately has no extraction
units, which makes switching to its empty unit workspace a visible transition
without broadening the Phase 3 non-experiment seed model.
| Date | Commit | Machine | AF2 host-to-first interactive | Route-to-first interactive (diagnostic) | Edit-to-microtask-settle p95 | Edit-to-paint p95 (diagnostic) | Category switch | Mounted controls / units | Forced-GC heap plateau spread | Route-to-first-interactive delta vs Angular 22 v1 |
|---|---|---|---|---|---|---|---|---|---|---|
| 2026-08-10 | 17cc57e28 |
GitHub Actions ubuntu-24.04 / Angular 22 |
662.7 ms | 8135 ms | 14 ms | 16.3 ms | 66.5 ms | 6 / 1 | 229,016 B | −75 ms (0.9% faster) |
A second ordinary, non-extraction scenario persists 200 Cohort label annotations through the authenticated review-session API. The normal AF2 load path then projects those labels into units and reconciles four supported child questions per unit, giving a representative repeated question graph while exercising the twenty-page bounded workspace without timing setup dialogs.
| Date | Commit | Machine | Session seed (diagnostic) | Page-turn p95 | Mounted controls / units / cards | Forced-GC heap plateau spread |
|---|---|---|---|---|---|---|
| 2026-08-10 | 17cc57e28 |
GitHub Actions ubuntu-24.04 / Angular 22 |
62.2 ms | 173.4 ms | 40 / 10 / 10 | 1,626,948 B (1.55 MiB) |
The first-interactive budget combines host mount to the first painted, enabled
Open all action with that action's click to the first painted, usable control.
Both segments are recorded inside the browser so Playwright protocol and
actionability waits between them are excluded, while blocked main-thread and
rendering work remains counted. The separate route diagnostics include API
latency and application startup outside the form so the cold-navigation result
remains comparable with the v1 baseline.
The hard budgets are: AF2 host-to-first interactive under 1,500 ms, application edit-to-microtask-settle p95 under 16 ms, visible category switching under 250 ms, no more than 20 mounted controls or 10 mounted units, and no more than 8 MiB spread across four forced-GC samples after warm-up. The one-frame paint sample is diagnostic because frame scheduling is not the application-processing time. The 200-unit workload additionally requires page-turn p95 under 250 ms, at most 40 mounted controls and 10 mounted units/cards, and the same 8 MiB heap plateau.
These measurements were attached by the normal run:e2e-full execution on
PR #2710, run 31387747461.
All 18 Playwright scenarios passed (one unrelated bulk-update scenario passed
on its normal retry). The run was marked unsuccessful only after the tests,
when the workflow invoked teardown a second time while the Playwright web
server still occupied its configured port. That shared teardown defect was
fixed and merged in PR #2722; the next exact-head run supplies the final clean
workflow gate. It does not change these browser measurements from commit
17cc57e28.
AF2 Phase 4 PR 4 re-run (2026-08-11)¶
The Phase-4 plan re-runs this gate on any PR that changes mounted-row
rendering. PR 4 adds the dormant outcome UI and mounts the outcome summary
table inside an Experiment unit's form, so the whole benchmark was re-run at
that branch head. All three scenarios passed both Playwright projects
(9/9 tests) with --workers=1.
| Date | Commit | Machine | AF2 host-to-first interactive | Route-to-first interactive (diagnostic) | Edit-to-microtask-settle p95 | Edit-to-paint p95 (diagnostic) | Category switch | Mounted controls / units | Forced-GC heap plateau spread |
|---|---|---|---|---|---|---|---|---|---|
| 2026-08-11 | a13310901 |
local dev | 397.9 ms | 4776 ms | 8.3 ms | 16.1 ms | 70.3 ms | 6 / 1 | 259,816 B |
| Date | Commit | Machine | Session seed (diagnostic) | Page-turn p95 | Mounted controls / units / cards | Forced-GC heap plateau spread |
|---|---|---|---|---|---|---|
| 2026-08-11 | a13310901 |
local dev | 126.0 ms | 131.5 ms | 40 / 10 / 10 | 1,532,624 B (1.46 MiB) |
Every hard budget holds with margin: host-to-first-interactive 397.9 ms against 1,500 ms, edit-to-microtask-settle p95 8.3 ms against 16 ms, category switch 70.3 ms against 250 ms, page-turn p95 131.5 ms against 250 ms, and both heap plateaus far inside 8 MiB. Mounted controls and units are unchanged from the Phase-3.5 rows (6 / 1 and 40 / 10 / 10), which is the expected result: the outcome UI is dormant and off-route, so no scenario in this benchmark reaches an Experiment unit and none of its controls can enter the mounted budget.
These rows are local dev, not GitHub Actions, so they are not directly
comparable with the ubuntu-24.04 rows above — they establish that the branch
holds every budget, and the run:e2e-full label supplies the CI-measured
comparison on the same runner class as the Phase-3.5 row.
AF2 Phase 4 PR 5 re-run (2026-08-11)¶
PR 5 is the activation PR: eligibility now admits extraction stages and the
Experiment category on stage-review, so the Experiment tab and the outcome
summary table it mounts are renderable for the first time. That changes what
the form can mount, so the Phase-3 gate was re-run at this branch head. All
three scenarios passed both Playwright projects (6/6 tests, --workers=1).
| Date | Commit | Machine | AF2 host-to-first interactive | Route-to-first interactive (diagnostic) | Edit-to-microtask-settle p95 | Edit-to-paint p95 (diagnostic) | Category switch | Mounted controls / units | Forced-GC heap plateau spread |
|---|---|---|---|---|---|---|---|---|---|
| 2026-08-11 | d056e8678 |
local dev | 715.9 ms | 5015 ms | 6.4 ms | 16.3 ms | 38.7 ms | 6 / 1 | 222,368 B |
| Date | Commit | Machine | Session seed (diagnostic) | Page-turn p95 | Mounted controls / units / cards | Forced-GC heap plateau spread |
|---|---|---|---|---|---|---|
| 2026-08-11 | d056e8678 |
local dev | 117.6 ms | 127.4 ms | 40 / 10 / 10 | 193,940 B (0.18 MiB) |
Every hard budget holds with margin: host-to-first-interactive 715.9 ms against 1,500 ms, edit-to-microtask-settle p95 6.4 ms against 16 ms, category switch 38.7 ms against 250 ms, page-turn p95 127.4 ms against 250 ms, and both heap plateaus far inside 8 MiB. Mounted controls and units are unchanged from the Phase-3.5 and PR-4 rows (6 / 1 and 40 / 10 / 10).
That last number is the one this PR had to prove. The Experiment category is
now a real, selectable tab on every AF2 stage-review form, but the mounted-row
budgets are per-category and per-page: an empty Experiment workspace mounts
nothing, and the benchmark's two AF2 scenarios are non-extraction stages with
no experiments, so the extra tab costs one more mat-tab-link and no mounted
form rows. The outcome matrix itself only mounts inside an open Experiment
unit's form, which is bounded by the same ten-unit page as every other
category.
The v1 baseline scenario in the same run measured 4,816 ms initial render, 1,600 ms study navigation, 94.1 ms keystroke p95 and 1,739 ms tab switch on this machine — the local-dev comparison point for the AF2 route diagnostic above.
These rows are local dev, not GitHub Actions, so they are not directly
comparable with the ubuntu-24.04 rows above — they establish that the branch
holds every budget. Applying the run:e2e-full label to the PR supplies the
CI-measured comparison on the same runner class as the Phase-3.5 row.
AF2 Phase 4 PR 5 fix round: the outcome matrix enters the gate (2026-08-11)¶
The PR-5 row above measures the two AF2 scenarios that existed at the time, and both are non-extraction stages with no experiments — so neither ever mounts an outcome matrix, and the activation claim did not cover the DOM PR 5 makes reachable. A third AF2 scenario now does.
AF2 extraction outcome-matrix performance gate. One extraction stage with
20 selected Study questions; its question set therefore also carries the whole
system extraction set (Stage.AllStageAnnotationQuestions unions
AnnotationQuestion.SystemQuestionIds, and
selectAnnotationQuestionsForCurrentStage mirrors that union). A topology of
4 experiments x 6 cohorts x 8 outcomes is persisted through the
authenticated session API, so the ordinary AF2 load path builds the matrices:
48 cells per experiment, 192 mounted at once with all four experiments open
on one workspace page — the worst case the ten-unit page can produce for this
shape.
Sampled: switching away from the Experiment workspace (all four matrices unmount), switching back to it (all 192 cells mount), opening a cell editor, the mounted matrix-cell and unit counts, and the forced-GC heap plateau across four category cycles.
| Date | Commit | Machine | Topology seed (diagnostic) | Route-to-host (diagnostic) | Category switch away | Category switch to matrix | Cell editor open | Mounted matrix cells / units | Forced-GC heap plateau spread |
|---|---|---|---|---|---|---|---|---|---|
| 2026-08-11 | b728e1521 |
local dev | 48.9 ms | 3888 ms | 25.2 ms | 119.3 ms | 43.9 ms | 192 / 4 | 156,612 B |
Budgets hold, with margin. Category switching 119.3 ms against the 250 ms
budget; the cell editor opens in 43.9 ms against the same 250 ms interaction
budget; mounted units 4, inside the 10-unit page budget; heap plateau 156,612 B
(0.15 MiB) against 8 MiB. The 192 mounted matrix cells are not covered by
the 20-mounted-control budget and must not be: that budget counts mounted
question controls (syrf-answer-row inputs), and a matrix cell is a summary
button that opens its editor in a dialog, so the cells add DOM nodes but no form
controls. It is recorded as its own number rather than folded into an existing
one.
The other three scenarios were re-run unchanged at the same head, all passing
(7/7 tests, --workers=1):
| Date | Commit | Machine | AF2 host-to-first interactive | Route-to-first interactive (diagnostic) | Edit-to-microtask-settle p95 | Edit-to-paint p95 (diagnostic) | Category switch | Mounted controls / units | Forced-GC heap plateau spread |
|---|---|---|---|---|---|---|---|---|---|
| 2026-08-11 | b728e1521 |
local dev | 316.8 ms | 4195 ms | 5.3 ms | 16.3 ms | 37.8 ms | 6 / 1 | 213,084 B |
| Date | Commit | Machine | Session seed (diagnostic) | Page-turn p95 | Mounted controls / units / cards | Forced-GC heap plateau spread |
|---|---|---|---|---|---|---|
| 2026-08-11 | b728e1521 |
local dev | 117.7 ms | 122.7 ms | 40 / 10 / 10 | 1,535,288 B (1.46 MiB) |
The v1 baseline scenario in the same run measured 4,334 ms initial render, 1,333 ms study navigation, 65.9 ms keystroke p95 and 1,509 ms tab switch.
These rows are local dev, not GitHub Actions, so they are not directly
comparable with the ubuntu-24.04 rows above — they establish that the branch
holds every budget on a matrix-bearing extraction stage. Applying the
run:e2e-full label supplies the CI-measured comparison on the same runner
class as the Phase-3.5 row.
Measurement notes for the matrix scenario¶
- The category tab clicks and the
Open allclick are dispatched rather than driven through the mouse. A fully open Experiment workspace makes the page tall enough that the fixed project navigation overlaps the form's tab strip and Playwright refuses the real click; the v1 baseline scenario already dispatches for the same reason. The sample still measures the browser's own work. - The warm-up switch to the Experiment category is retried. The store
re-selects a populated category on every hydrate (
applySnapshot'spopulatedCategoriesfallback), and an extraction stage's Experiment units only exist once the annotations and the extraction projection have resolved, so a switch issued before that is bounced back to Study. Every sampled switch runs against a settled form.