Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
2819 commits
Select commit Hold shift + click to select a range
78c3693
fix(storage): use path-style URLs for S3 public asset URLs
jthrilly Apr 29, 2026
9290a6d
fix(synthetic-interviews): emit schema-valid stageMetadata
jthrilly Apr 29, 2026
a0abcdc
fix(dashboard): use Trash2 icon for protocol delete action
jthrilly Apr 29, 2026
9bae070
chore: drop unused exports flagged by knip
jthrilly Apr 29, 2026
82366b6
feature(protocols): allow downloading original .netcanvas protocol
buckhalt Apr 29, 2026
b729335
refactor(protocols): use useDownload hook for download action
buckhalt Apr 29, 2026
cbc997b
refactor(protocols): unify asset and original file upload into one call
buckhalt Apr 29, 2026
ade5aba
reorganise UI components ready for migration to package
jthrilly Apr 29, 2026
5528937
Merge pull request #731 from complexdatacollective/fix/confirm-passwo…
jthrilly Apr 29, 2026
56bb573
Merge remote-tracking branch 'origin/next' into feature/download-prot…
buckhalt Apr 29, 2026
a7f8865
refactor(protocols): verify uploaded original file matches input by name
buckhalt Apr 29, 2026
b680504
Merge pull request #732 from complexdatacollective/feature/download-p…
jthrilly Apr 30, 2026
0069316
fix(dashboard): preserve activity filter search and unify login events
claude Apr 30, 2026
eca1805
fix(dashboard): keep Recovery Code Used as a distinct activity type
claude Apr 30, 2026
98b3431
Fix: preserve badge colors for legacy activity types with default fal…
Copilot Apr 30, 2026
4c51ba8
Merge pull request #733 from complexdatacollective/claude/fix-activit…
jthrilly Apr 30, 2026
3ebe646
fix(combobox): keep search query when an item is selected
claude Apr 30, 2026
7e5b22c
docs(combobox): note base-ui workaround and upstream tracking issues
claude Apr 30, 2026
318e880
feat(deps): add @codaco/fresco-ui@next
jthrilly Apr 30, 2026
7b77384
feat(fresco): import @codaco/fresco-ui/styles.css and bump tailwindcss
jthrilly Apr 30, 2026
fe636d3
fix(ci): set Netlify DB env before deploy and force a fresh build
claude Apr 30, 2026
b581091
refactor(fresco): relocate SubmitButton, Link (drop NativeLink export…
jthrilly Apr 30, 2026
2ce02db
refactor(fresco): rewrite imports to @codaco/fresco-ui
jthrilly Apr 30, 2026
54e9282
fix(ci): skip Netlify auto-builds without branch-scoped DB env
claude Apr 30, 2026
582bc8a
chore(deps): bump @codaco/fresco-ui to 0.1.0-next.1
jthrilly Apr 30, 2026
77ee71c
fix(useProtocolForm): rewrite relative imports to @codaco/fresco-ui a…
jthrilly Apr 30, 2026
ef7867e
test(e2e): skip geospatial stage on WebKit too
claude Apr 30, 2026
7632ea4
Merge pull request #734 from complexdatacollective/claude/preserve-co…
jthrilly Apr 30, 2026
6da2d29
chore(fresco): delete code migrated to @codaco/fresco-ui and clean un…
jthrilly Apr 30, 2026
e8ad96e
chore(deps): bump @codaco/fresco-ui to 0.1.0-next.2
jthrilly May 1, 2026
fb6239c
fix(geospatial): re-enable mapbox-gl/dist/mapbox-gl.css import (WebKi…
jthrilly May 1, 2026
4c01781
Merge remote-tracking branch 'origin/next' into reorganise-ui
jthrilly May 1, 2026
e0ff651
Revert "fix(geospatial): re-enable mapbox-gl/dist/mapbox-gl.css impor…
jthrilly May 1, 2026
745aa59
chore(deps): bump @codaco/fresco-ui to ^0.1.0 stable
jthrilly May 1, 2026
d9149ab
chore(deps+test): bump @codaco/fresco-ui to 0.1.1; drop stale webkit …
jthrilly May 1, 2026
5d58a78
test(e2e): remove stale webkit geospatial snapshots
jthrilly May 1, 2026
2091c22
refactor: update fresco-ui form import paths after flatten
jthrilly May 1, 2026
7f8060b
refactor: update fresco-ui imports for renamed components
jthrilly May 1, 2026
23a27a7
chore: drop unused dateOptions export flagged by knip
jthrilly May 1, 2026
d85bee0
chore(deps): bump @codaco/fresco-ui to ^0.2.1
jthrilly May 1, 2026
4b7c0ff
chore(deps): add @codaco/tailwind-config as direct dep
jthrilly May 1, 2026
e35163b
chore(deps): bump @codaco/fresco-ui to ^0.3.0; hoist tailwind-config …
jthrilly May 1, 2026
77103a0
test(e2e): skip geospatial stages 19, 40, 50 on webkit
jthrilly May 1, 2026
48cfce5
chore(deps): bump @codaco/fresco-ui to ^1.0.0
jthrilly May 1, 2026
43be1b4
chore(deps): bump @codaco/fresco-ui to ^2.0.0; pin tailwind-config
jthrilly May 1, 2026
437028e
refactor: import canonical themes from @codaco/tailwind-config
jthrilly May 1, 2026
1120daa
chore(fresco-ui-dep): bump @codaco/fresco-ui to ^2.0.1; drop now-redu…
buckhalt May 1, 2026
a4c38ac
Merge pull request #735 from complexdatacollective/reorganise-ui
buckhalt May 1, 2026
5b4ad70
feat(geospatial): add UA detection for Mapbox stub mode
jthrilly May 4, 2026
b34ecb7
feat(geospatial): add GeospatialStubSearch component for E2E
jthrilly May 4, 2026
17e3f30
feat(geospatial): wire E2E stub render path on webkit/firefox
jthrilly May 4, 2026
10c762e
fix(geospatial): remove type assertion and redundant stub guards
jthrilly May 4, 2026
5526709
test(e2e): stub-mode short-circuits in geospatial fixture waits
jthrilly May 4, 2026
42a9af1
test(e2e): remove webkit/firefox skips on geospatial stages
jthrilly May 4, 2026
2fb4712
test(e2e): regenerate visual snapshot baselines for geospatial stub
jthrilly May 4, 2026
bf3c7ee
use network-query package
jthrilly May 4, 2026
756fc62
perf(geospatial): dynamic-import GeospatialStubSearch to keep it out …
jthrilly May 4, 2026
309fdc2
chore(deps): add @codaco/network-exporters ^1.0.1
jthrilly May 4, 2026
decaa38
feat(export): add describeExportError for new package error tags
jthrilly May 4, 2026
9796a33
chore(deps): bump @codaco/network-exporters to ^1.0.2; drop ambient s…
jthrilly May 4, 2026
9f19558
refactor(export): drop type assertion in classifyCause
jthrilly May 4, 2026
99ded53
feat(export): move ExportSseEvent + formatSSE to lib/export/sseEvents
jthrilly May 4, 2026
11344a6
feat(export): add Prisma-backed InterviewRepository layer
jthrilly May 4, 2026
7646c53
feat(export): add Prisma-backed ProtocolRepository layer
jthrilly May 4, 2026
aa96031
feat(export): add makeS3Sink for streaming zip uploads
jthrilly May 4, 2026
80529d3
feat(export): add makeUploadThingSink for streaming zip uploads
jthrilly May 4, 2026
f1d698a
feat(export): add makeLocalSink for streaming zip uploads
jthrilly May 4, 2026
460c2bd
feat(export): add Output layer factories for s3/uploadthing/local
jthrilly May 4, 2026
b530e8c
fix(export): stabilize Output integration tests
jthrilly May 4, 2026
020ebb5
refactor(export): rewire export route to @codaco/network-exporters
jthrilly May 4, 2026
d684abf
refactor(export): switch consumers to @codaco/network-exporters
jthrilly May 4, 2026
4d1f7ff
refactor(storage): drop network-exporters bundling from storage layers
jthrilly May 4, 2026
849ca1c
refactor(export): remove local network-exporters lib and orphaned layers
jthrilly May 4, 2026
2cb1887
fix(quick-add): lucide icon rendering in node action buttons
buckhalt May 4, 2026
ab98ef8
chore(stories): add lucide icon types to node action button story con…
buckhalt May 4, 2026
659e592
chore(stories): remove non-node-type icons from action button options
buckhalt May 4, 2026
8129878
fix(action-button-variants): restore custom icon sizing in node actio…
buckhalt May 4, 2026
c3d86b9
chore: drop redundant size prop on plus icons
buckhalt May 4, 2026
c4536d5
build(deps-dev): bump eslint-plugin-better-tailwindcss
dependabot[bot] May 4, 2026
eec9b11
build(deps): bump @prisma/adapter-pg from 7.7.0 to 7.8.0
dependabot[bot] May 4, 2026
d55f424
build(deps): bump posthog-js from 1.367.0 to 1.372.8
dependabot[bot] May 4, 2026
8987468
build(deps): bump es-toolkit from 1.45.1 to 1.46.1
dependabot[bot] May 4, 2026
5342cdf
chore(deps): remove unused archiver/xmldom/sanitize-filename
jthrilly May 4, 2026
2c83881
fix(stories): pin interview theme on QuickAddField and NodeForm stories
buckhalt May 4, 2026
4ef7730
refactor: replace lib/interviewer with @codaco/interview package
jthrilly May 5, 2026
44338b9
chore: remove orphaned CopyDebugInfoButton
jthrilly May 5, 2026
d56f37f
chore: remove playwright and all e2e test infrastructure
jthrilly May 5, 2026
756b3a2
chore: remove E2E_TEST runtime branches and orphaned support code
jthrilly May 5, 2026
3ebb812
Merge pull request #742 from complexdatacollective/dependabot/npm_and…
jthrilly May 5, 2026
86cc27c
Merge pull request #741 from complexdatacollective/dependabot/npm_and…
jthrilly May 5, 2026
77a5696
Merge pull request #740 from complexdatacollective/dependabot/npm_and…
jthrilly May 5, 2026
9b1f9d4
Merge pull request #738 from complexdatacollective/dependabot/npm_and…
jthrilly May 5, 2026
13d2f93
Merge pull request #737 from complexdatacollective/fix/quickadd-lucid…
jthrilly May 5, 2026
f2f49f8
build(deps): bump @prisma/client from 7.7.0 to 7.8.0
dependabot[bot] May 5, 2026
3c1ec2f
chore(deps): bump @codaco/interview to 1.0.0-alpha.1
jthrilly May 5, 2026
8ea254e
chore: merge next into interview-package (branch wins on conflict)
jthrilly May 5, 2026
b74107e
fix(deps): bump @base-ui/react to ^1.4.1 with pnpm override to dedupe
jthrilly May 5, 2026
1cf2812
chore(deps): bump @codaco/fresco-ui to ^2.1.0; drop @base-ui/react ov…
jthrilly May 5, 2026
5a277c1
chore(deps): bump @codaco/tailwind-config to ^0.4.1
jthrilly May 5, 2026
2732e99
chore(deps): bump tailwind-config 0.5.0 + fresco-ui 2.2.0; switch to …
jthrilly May 5, 2026
f06818b
chore(deps): bump @codaco/{tailwind-config,fresco-ui,interview} to tr…
jthrilly May 5, 2026
d80821b
remove duplicated utilities
jthrilly May 5, 2026
e9bced1
chore(deps): bump @codaco/tailwind-config to 1.0.0-alpha.1
jthrilly May 5, 2026
607111b
chore(deps): bump @codaco/{tailwind-config,fresco-ui} to track varian…
jthrilly May 5, 2026
07103da
feat: integrate @codaco/interview@1.0.0-alpha.4 analytics
jthrilly May 5, 2026
dc893f5
chore(deps): bump @codaco/tailwind-config to 1.0.0-alpha.4
jthrilly May 5, 2026
82640df
chore(deps): bump @codaco/interview to 1.0.0-alpha.5
jthrilly May 5, 2026
8b56366
chore(deps): bump @codaco/tailwind-config to 1.0.0-alpha.5
jthrilly May 5, 2026
9ac2b87
Merge pull request #739 from complexdatacollective/dependabot/npm_and…
jthrilly May 5, 2026
465b94e
chore(deps): bump @codaco/{interview,tailwind-config} to 1.0.0-alpha.6
jthrilly May 5, 2026
763e8f5
chore(deps): bump @codaco/{interview,tailwind-config} to 1.0.0-alpha.7
jthrilly May 5, 2026
48ecb56
chore(deps): bump @codaco/fresco-ui to 2.5.2
jthrilly May 5, 2026
7816e8d
chore(deps): bump @codaco/{interview,tailwind-config} to 1.0.0-alpha.8
jthrilly May 5, 2026
649b6a5
fix(storybook): drop stale theme imports; rename data-theme attribute
jthrilly May 5, 2026
4105cb6
Merge branch 'next' into interview-package
jthrilly May 5, 2026
15669cb
fix(test): eliminate flaky timeout in Output.test.ts s3 happy path
jthrilly May 5, 2026
7b139dd
chore(deps): bump @codaco/interview to 1.0.0-alpha.9
jthrilly May 5, 2026
cbebeda
chore(deps): bump @codaco/{tailwind-config,fresco-ui,interview}
jthrilly May 5, 2026
c42a682
fix(globals.css): drop duplicate @import "tailwindcss"
jthrilly May 5, 2026
680367a
chore(deps): bump @codaco/{fresco-ui,interview,tailwind-config}
jthrilly May 7, 2026
3076886
bump package
jthrilly May 7, 2026
08028b9
chore(deps): bump @codaco/interview to 1.0.0-alpha.13
jthrilly May 7, 2026
ddd61df
fix(interview): push step changes to browser history
jthrilly May 7, 2026
b0b03aa
feat(protocols-table): default sort by import date, newest first
jthrilly May 7, 2026
f934032
remove --color prefixed variables in preparation for theming change
jthrilly May 8, 2026
9e3a48a
chore(deps): bump @codaco/{interview,fresco-ui} to alpha.17 / 2.9.0
jthrilly May 8, 2026
419afbc
ci: pin setup-pnpm, chromaui, and setup-bun to immutable SHAs
jthrilly May 12, 2026
284074d
ci: bump first-party actions to latest major versions
jthrilly May 12, 2026
477ff2c
ci: SHA-pin deploy-preview actions and migrate to thollander v3
jthrilly May 12, 2026
ab2c0a1
ci(chromatic): harden issue_comment trigger against fork-PR abuse
jthrilly May 12, 2026
19bf35a
ci: bump e2e workflow actions to latest major versions
jthrilly May 12, 2026
357e80d
ci(e2e): scope id-token: write to jobs that consume it
jthrilly May 12, 2026
7c26899
Merge pull request #750 from complexdatacollective/ci-supply-chain-ha…
jthrilly May 12, 2026
2f529fe
remove arbitrary values
jthrilly May 12, 2026
ee8ddec
chore(deps): bump @codaco/{fresco-ui,interview,tailwind-config} to 2.…
jthrilly May 12, 2026
607fe09
Merge remote-tracking branch 'origin/next' into interview-package
jthrilly May 12, 2026
59340a7
fix(data-table): wrap --sea-green tuple in oklch() for color-mix
jthrilly May 12, 2026
d5d290c
same size text in exported inteterview column
jthrilly May 12, 2026
d46a1b9
chore(deps): bump @codaco/{fresco-ui,interview,tailwind-config} to 2.…
jthrilly May 12, 2026
49caee3
feat: remove Preview Mode feature
jthrilly May 13, 2026
1514659
Merge pull request #751 from complexdatacollective/remove-preview-mode
jthrilly May 13, 2026
ffc31a5
Merge pull request #743 from complexdatacollective/interview-package
jthrilly May 13, 2026
e932c2c
chore(deps): bump @codaco/interview to 1.0.0-alpha.20
jthrilly May 14, 2026
d149f11
feat(api): add protocols-meta endpoint
buckhalt May 14, 2026
c975393
refactor(api): address coderabbit review
buckhalt May 14, 2026
6a9dc49
chore(deps): update all dependencies to latest
claude May 14, 2026
77c922b
chore(deps): pin @types/node to v24 to match project config
claude May 14, 2026
b6db856
fix(RecruitmentTestSection): drop as Route casts flagged by lint in CI
claude May 14, 2026
ca4ff60
fix(RecruitmentTestSection): use Route annotation without redundant c…
claude May 14, 2026
6776317
chore: run next typegen on postinstall so Route is narrowed for CI
claude May 14, 2026
5bc8120
Merge pull request #753 from complexdatacollective/claude/update-all-…
jthrilly May 14, 2026
61fd9e1
chore: upgrade pnpm to v11
claude May 15, 2026
1331c44
chore: pin pnpm packageManager hash, document exact version
claude May 15, 2026
434ae20
Merge pull request #755 from complexdatacollective/claude/upgrade-pnp…
jthrilly May 15, 2026
f345263
refactor(posthog): make server telemetry helpers non-throwing
jthrilly May 15, 2026
b1a50f2
ci: install netlify-cli via npm to avoid pnpm 10 global bin issue
jthrilly May 15, 2026
8bc4f12
ci: bump shared setup-pnpm action to v2
jthrilly May 15, 2026
8b36f75
Merge pull request #752 from complexdatacollective/feature/api-protoc…
jthrilly May 15, 2026
706ce2c
ci: use mode: delete for failure-comment cleanup
jthrilly May 15, 2026
3d60131
ci: gate deploy preview on Netlify build outcome, add prod deploy on …
jthrilly May 15, 2026
6e20e42
ci: drop production deploy workflow — Netlify handles pushes to next
jthrilly May 15, 2026
08f3d53
ci: add Production Deploy Status check for pushes to next
jthrilly May 15, 2026
dbabade
chore(deps): update @codaco packages, move DataTable to fresco-ui
jthrilly May 21, 2026
51633fa
fix(activity-feed): let Details cells wrap so the table stops overflo…
jthrilly May 21, 2026
a5f5d6e
build(deps-dev): bump fflate from 0.8.2 to 0.8.3
dependabot[bot] May 21, 2026
302e569
build(deps-dev): bump tsx from 4.22.0 to 4.22.3
dependabot[bot] May 21, 2026
4253a0c
Merge pull request #761 from complexdatacollective/dependabot/npm_and…
jthrilly May 27, 2026
a53223e
Merge pull request #760 from complexdatacollective/dependabot/npm_and…
jthrilly May 27, 2026
5b6a1b8
build(deps): update dependencies to latest versions
jthrilly Jun 1, 2026
49b3fc0
build(deps): update @codaco packages to latest versions
jthrilly Jun 1, 2026
c7b99eb
feat(activity-feed): attribute admin events to user and add CSV export
jthrilly Jun 2, 2026
ae6b2ba
feat(import): add protocol upload size limit helper
jthrilly Jun 3, 2026
1e3aef7
feat(import): add dedicated uploading-protocol progress phase
jthrilly Jun 3, 2026
3ebe8fa
feat(uploadthing): add createUpload export and retry wrapper
jthrilly Jun 3, 2026
ddfecc8
refactor(import): route uploadthing uploads through retry wrapper
jthrilly Jun 3, 2026
ab7e575
feat(import): upload protocol file in its own step before assets
jthrilly Jun 3, 2026
0a514fa
feat(import): guard oversized protocols and show spinner toast icon
jthrilly Jun 3, 2026
937e357
feat(import): show upload protocol phase and numeric percentage
jthrilly Jun 3, 2026
f5694d6
test(import): add uploading-protocol story and update percentages
jthrilly Jun 3, 2026
4a86656
feat(import): clean up orphaned uploads when an import fails
jthrilly Jun 3, 2026
a504d06
fix(import): inclusive size-limit wording and round file size up
jthrilly Jun 3, 2026
057be82
Merge pull request #771 from complexdatacollective/fix/large-protocol…
jthrilly Jun 3, 2026
85cc1d6
chore(deps): update @codaco packages to latest versions
jthrilly Jun 5, 2026
e19e23d
chore(deps): update @codaco packages to latest versions (#781)
jthrilly Jun 11, 2026
2eb46a3
docs(compose): clarify that the S3 endpoint URL must be browser-reach…
jthrilly Jun 11, 2026
b969635
chore(deps): update @codaco packages to latest versions (#782)
jthrilly Jun 11, 2026
a34dd47
docs: add self-hosted architecture diagrams (S3 and UploadThing scena…
jthrilly Jun 11, 2026
3400662
feat(export): add ExportTicket model
jthrilly Jun 11, 2026
d43ed20
feat(export): add single-use export ticket helpers
jthrilly Jun 11, 2026
69b0bf5
feat(export): replace storage sinks with HTTP streaming sink
jthrilly Jun 11, 2026
d2bdb90
feat(export): ticket-based POST and streaming GET download route
jthrilly Jun 11, 2026
bdd5520
fix(export): harden ticket consumption and download stream error paths
jthrilly Jun 11, 2026
247e7d0
refactor(export): trigger native browser download via export ticket
jthrilly Jun 11, 2026
fb2a7d5
refactor(storage): remove FileStorage layer — exports no longer touch…
jthrilly Jun 11, 2026
c0f8e52
feat(storage): add storage configuration environment variables
jthrilly Jun 11, 2026
fb36c7f
feat(storage): add s3PublicUrl setting
jthrilly Jun 11, 2026
cf09ae5
feat(storage): central storage config resolver with env override
jthrilly Jun 11, 2026
1c9aad3
refactor(storage): route all storage credential reads through config …
jthrilly Jun 11, 2026
2d64762
feat(storage): public URL field, env-managed lock, write-only secrets
jthrilly Jun 11, 2026
282e8a3
fix(storage): robust setting editors, named env vars in lock alert, u…
jthrilly Jun 11, 2026
d5a2470
feat(storage): app-relative asset URLs and UUID asset keys
jthrilly Jun 11, 2026
150e254
docs: update architecture diagrams for private-bucket storage topology
jthrilly Jun 11, 2026
6045590
docs: bundle Traefik in deployment plan and diagrams; rename asset pa…
jthrilly Jun 11, 2026
89dfee9
feat(storage): asset redirect route to presigned GET URLs
jthrilly Jun 11, 2026
28f4b13
feat(deploy): private MinIO behind reverse-proxy path route
jthrilly Jun 11, 2026
7234e1c
feat(deploy): bundle Traefik with automatic TLS and asset path routing
jthrilly Jun 11, 2026
ad9d4a1
fix(dev): keep dev MinIO bucket private to mirror production semantics
jthrilly Jun 11, 2026
d309bb9
fix(storage): env-aware provider in dashboard settings; drop dead set…
jthrilly Jun 11, 2026
cb64f7e
fix(export,storage): address PR review — atomic user-bound ticket con…
jthrilly Jun 11, 2026
fc35085
fix(settings): runtime-validate setting writes against the read schema
jthrilly Jun 11, 2026
1ff8e65
feat(deploy): compose variants for external S3 and UploadThing storage
jthrilly Jun 11, 2026
e14892c
fix: address CodeRabbit review — step clamping, env blank handling, k…
jthrilly Jun 11, 2026
83a06c1
fix(storage): use ListObjectsV2 for S3 validation; add dev env defaults
jthrilly Jun 12, 2026
656c6d1
feat(setup): show storage form with disabled fields when env-managed
jthrilly Jun 12, 2026
0f0c1b3
fix(docker): copy pnpm-workspace.yaml into runner stage
jthrilly Jun 12, 2026
9c39fe6
fix(docker): exclude .pnpm-store and test artifacts from build context
jthrilly Jun 12, 2026
9bac412
refactor(deploy): derive PUBLIC_URL from FRESCO_DOMAIN
jthrilly Jun 12, 2026
7817b89
fix(import): show protocol name in completion toast
jthrilly Jun 12, 2026
6ff4114
fix(settings): clamp synthetic-interview count and surface generation…
jthrilly Jun 12, 2026
3213ae5
feat(export): add streamed export event protocol
jthrilly Jun 12, 2026
9c6fac9
feat(export): emit zip as base64 data events through a shared writer
jthrilly Jun 12, 2026
a378005
feat(export): stream stage/progress events alongside the zip
jthrilly Jun 12, 2026
1ad432c
feat(export): stage-aware export progress toast component
jthrilly Jun 12, 2026
007eb2e
feat(export): stage-aware progress, cancel, and nav warning in export UI
jthrilly Jun 12, 2026
a7f0872
fix(export): persist progress toast with timeout 0, not Infinity
jthrilly Jun 12, 2026
3c4dae5
fix(docker): slim runner image from 8.86GB to ~822MB
jthrilly Jun 15, 2026
ba1d265
perf(export): throttle progress events and commit status before compl…
jthrilly Jun 15, 2026
d417bcd
fix(export): refresh interviews list after export (read-your-own-writes)
jthrilly Jun 15, 2026
8d9f091
feat(interviews): add server-pagination searchParams + SQL where builder
jthrilly Jun 15, 2026
f1c96c3
feat(interviews): paginated getInterviews + filter options + bulk id …
jthrilly Jun 15, 2026
c547e1a
feat(interviews): thread search params into interviews page + server …
jthrilly Jun 15, 2026
69f4f65
feat(interviews): add nuqs date/range/boolean toolbar filters
jthrilly Jun 15, 2026
042c12e
feat(interviews): add nuqs network operator toolbar filter
jthrilly Jun 15, 2026
dc45480
feat(interviews): server-paginated table shell, rows, toolbar + crite…
jthrilly Jun 15, 2026
506f2eb
feat(interviews): true cross-page checkbox selection with select-all-…
jthrilly Jun 15, 2026
2a2f556
refactor(interviews): drop unused GetInterviewsResult type
jthrilly Jun 15, 2026
8065abe
refactor(export): rely on server-action auto-revalidation, drop route…
jthrilly Jun 15, 2026
f5c33a4
perf(protocols): select only needed interview fields, not full networ…
jthrilly Jun 15, 2026
5a4eb22
fix(interviews): restore per-column-header filter popovers (server-side)
jthrilly Jun 15, 2026
076dfa7
feat(participants): server-side pagination, sort, search + cross-page…
jthrilly Jun 15, 2026
9f008ac
build(docker): cache npm downloads for runtime deps via BuildKit mount
jthrilly Jun 15, 2026
17b80d3
Merge pull request #783 from complexdatacollective/feat/storage-archi…
jthrilly Jun 15, 2026
6449e7b
chore: upgrade Storybook to 10.4.4
claude Jun 15, 2026
a88b920
chore(deps): update non-major dependencies to latest
claude Jun 15, 2026
0865da2
chore(deps): update sharp to 0.35.1
claude Jun 15, 2026
6cef7f9
Merge pull request #791 from complexdatacollective/claude/magical-dav…
jthrilly Jun 15, 2026
4154c51
fix(export): client-orchestrated batched export for large interview s…
jthrilly Jun 15, 2026
9c36548
security: harden auth, storage, export and infra for 4.0.0 (#793)
jthrilly Jun 17, 2026
7d15730
fix(dashboard): show finished interviews as 100% progress (#801)
jthrilly Jun 22, 2026
69b371c
chore(deps): update @codaco packages and migrate collected categorica…
jthrilly Jun 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
335 changes: 335 additions & 0 deletions .claude/skills/generate-e2e-tests/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,335 @@
---
name: generate-e2e-tests
description: Generate comprehensive Playwright e2e tests from a .netcanvas protocol file and an optional recording. Invoke with /generate-e2e-tests <protocol-path> [recording-path]
user-invocable: true
---

# Generate E2E Tests from Protocol

You are generating comprehensive Playwright e2e tests for a Fresco interview protocol.

## Inputs

- **Protocol path**: `$1` — path to a `.netcanvas` file (ZIP containing `protocol.json`)
- **Recording path** (optional): `$2` — path to a recording directory (contains `actions.jsonl`, `SESSION.md`, `screenshots/`)

If no recording is provided, generate a **synthetic happy path** using the data generation strategies in STAGE_TEST_REFERENCE.md (see "Synthetic Data Generation" section).

## Step 1: Read Reference Materials

Read these files to understand the testing infrastructure and patterns:

1. `tests/e2e/docs/STAGE_TEST_REFERENCE.md` — what to test for each stage type, fixture availability, validation testing patterns
2. `tests/e2e/fixtures/stage-fixture.ts` — available fixture methods and their signatures
3. `tests/e2e/fixtures/interview-fixture.ts` — interview navigation fixture
4. `tests/e2e/fixtures/protocol-fixture.ts` — protocol installation and network state inspection
5. `tests/e2e/CLAUDE.md` — full e2e testing architecture guide
6. `tests/e2e/specs/interview/silos-protocol.spec.ts` — reference test implementation to match style/structure
7. `CLAUDE.md` — project coding conventions (path aliases, TypeScript, etc.)

## Step 2: Extract and Analyze Protocol

Extract the protocol JSON:

```bash
unzip -p "$1" protocol.json
```

From the extracted JSON, build a **stage map** — for each stage (by index), extract:

- `type` — stage type (e.g., `NameGeneratorQuickAdd`, `EgoForm`, `Sociogram`)
- `label` — display label
- `subject` — `{ entity, type }` pointing to codebook entry (null for Information/Anonymisation)
- `introductionPanel` — title and text (if present)
- `form.fields[]` — array of `{ variable }` referencing codebook variables
- `prompts[]` — array of prompt objects (with `createEdge`, `variable`, `highlight`, etc.)
- `panels[]` — side panel configuration
- `behaviours` — `maxNodes`, `minNodes`, `freeDraw`, etc.

For each form field, resolve the variable UUID against the codebook:

- Look up `codebook.[entity].[type].variables.[variableId]`
- Extract: `name`, `type`, `component`, `validation`, `options`

This gives you the field name (UUID), display name, input component type, validation rules, and available options for each form field.

## Step 3: Analyze Recording (if provided)

If `$2` is provided, read `$2/actions.jsonl` (one JSON object per line).

Group actions by stage — track URL changes via the `step=N` query parameter. For each stage visited:

- Extract the sequence of user actions (click, fill, press, select)
- Note filled values and selected options
- Note which nodes were created (names entered in quick-add or name generator forms)
- Note edge-creating interactions (sociogram clicks, dyad census selections)

The recording represents the **happy path** — the exact user journey to replay.

### If no recording

Generate a synthetic happy path from the protocol alone:

1. Walk through all stages in order (index 0 to N)
2. For each stage, use the **Synthetic Data Generation** section of STAGE_TEST_REFERENCE.md to determine what values to fill, how many nodes to create, etc.
3. For conditional/skip logic, choose the path that visits the **most stages**
4. Track synthetic state as you go — node names created on earlier stages are needed for bin/census/sociogram stages later

## Step 4: Generate Test File

Create `tests/e2e/specs/interview/<protocol-name>.spec.ts` where `<protocol-name>` is derived from the protocol name (kebab-case, lowercase).

### File Structure

Follow this exact pattern (from the reference implementation):

```typescript
/**
* <Protocol Name> Tests
*
* Tests interview stage navigation using a real .netcanvas protocol file.
*/

import path from 'node:path';
import { expect, test } from '~/tests/e2e/fixtures/interview-test.js';
import { expectURL } from '~/tests/e2e/helpers/expectations.js';

const PROTOCOL_PATH = path.resolve(
import.meta.dirname,
'../../data/<filename>.netcanvas',
);

let sharedProtocolId: string;

test.describe('<Protocol Name>', () => {
test.beforeAll(async ({ database, protocol }) => {
await database.restoreSnapshot();
const { protocolId } = await protocol.install(PROTOCOL_PATH);
sharedProtocolId = protocolId;
});

test.describe('Happy Path', () => {
test.describe.configure({ mode: 'serial' });

let interviewId: string;

test.beforeAll(async ({ protocol }) => {
interviewId = await protocol.createInterview(sharedProtocolId);
});

test.beforeEach(({ interview }) => {
interview.interviewId = interviewId;
});

test.afterEach(async ({ page, interview }) => {
const stepMatch = /step=(\d+)/.exec(page.url());
if (stepMatch?.[1]) {
const step = stepMatch[1];
// List stage indices with non-deterministic rendering
const highToleranceStages: string[] = [/* sociogram indices */];

await interview.capture(`stage-${step}-final`, {
maxDiffPixelRatio: highToleranceStages.includes(step)
? 0.1
: undefined,
});
}
});

// One test() per stage...
});
});
```

### Per-Stage Test Generation

For each stage in the protocol, generate a `test()` block. Use the STAGE_TEST_REFERENCE.md to determine what to test.

#### Mapping Recording Actions to Fixture Calls

Translate recording actions to fixture method calls using these mappings:

| Recording Pattern | Fixture Call |
|---|---|
| Navigate to URL with `step=N` | `interview.goto(N)` |
| Click element matching next/forward button | `interview.nextButton.click()` |
| Fill input within `[data-field-name="UUID"]` | `stage.form.fillText(UUID, value)` or `fillNumber`/`fillDate` based on codebook component |
| Click radio within `[data-field-name="UUID"]` | `stage.form.selectRadio(UUID, optionLabel)` |
| Click checkbox within `[data-field-name="UUID"]` | `stage.form.selectCheckbox(UUID, optionLabel)` |
| Click toggle button within `[data-field-name="UUID"]` | `stage.form.selectToggleButton(UUID, optionLabel)` |
| Fill quick-add input + press Enter | `stage.quickAdd.addNode(value)` |
| Click "Add a person" button | `stage.nameGenerator.openAddForm()` |
| Click "Finished" button in dialog | `stage.nameGenerator.submitForm()` |
| Drag node from panel | `stage.nodePanel.dragNodeToMainList(label)` |
| Click node on sociogram (connecting) | `stage.sociogram.connectNodes(from, to)` |
| Drag node to ordinal bin | `stage.ordinalBin.dragNodeToBin(node, bin)` |
| Drag node to categorical bin | `stage.categoricalBin.dragNodeToBin(node, bin)` |

#### Determine Form Method from Codebook

Use the codebook variable's `component` (or `type` if no component) to pick the right form fixture method:

| Component | Method |
|---|---|
| `Text`, `TextArea` | `fillText` |
| `Number` | `fillNumber` |
| `DatePicker` | `fillDate` |
| `RadioGroup` | `selectRadio` |
| `LikertScale` | `selectLikert` |
| `CheckboxGroup` | `selectCheckbox` |
| `ToggleButtonGroup` | `selectToggleButton` |
| `Boolean` | `selectRadio` (options are "Yes"/"No" or custom labels from codebook) |

#### Comments

Add a comment above each form field interaction with the field's display name and component type:

```typescript
// 1. Date of birth (DatePicker)
await stage.form.fillDate('596c2ac2-...', '2000-06-15');

// 2. Gender identity (RadioGroup)
await stage.form.selectRadio('a06f06f5-...', 'Cisgender Male');
```

### Validation Tests

For each form stage (EgoForm, AlterForm, AlterEdgeForm), examine the codebook variables for targeted validation rules. Generate validation test assertions **within the happy path test** for that stage:

1. **Before filling fields**: Try to advance, verify validation blocks:
```typescript
// Verify validation blocks advancement
await interview.nextButton.click();
await expectURL(page, /step=N/); // Still on same stage

// Verify required field errors
await expect(
stage.form.getFieldError('field-uuid'),
).toBeVisible();
```

2. **Then fill fields normally** from the recording data.

Only test these validations (skip others):
- `required: true` — always test
- `minValue` / `maxValue` — test if present
- `minLength` / `maxLength` — test if present
- `pattern` — test if present
- `unique` — test if applicable (needs duplicate value scenario)
- `sameAs` / `differentFrom` — test if present

### Network State Verification

The sync middleware uses a 3-second debounce with leading+trailing edges. Each `interview.goto()` destroys the current page, killing any pending trailing-edge syncs. Stages that set data used by downstream skip logic or filtering must explicitly wait for that data to persist.

#### Form stages (EgoForm, AlterForm) must click Next to submit

Form data lives in React Hook Form's local state until the form is submitted. **You must click `interview.nextButton` at the end of every form stage** to flush the data to Redux. Without this, the sync middleware never sees the data.

For **EgoForm** stages, click Next as the last interaction (replaces the `toBeEnabled` assertion):

```typescript
// Submit form to flush data to Redux
await interview.nextButton.click();
```

For **AlterForm** stages with slides, click Next after filling the **last slide** (the earlier slides already submit when you click Next to advance):

```typescript
// Submit last slide to flush form data to Redux
await interview.nextButton.click();
```

Note: clicking Next navigates to the next stage, so the `afterEach` screenshot will capture the next stage's initial state rather than the current stage's final state.

#### Persistence waits for skip logic

After stages that set attributes consumed by downstream skip logic or filtering, add explicit waits using the protocol fixture. Available methods:

- `protocol.waitForNodes(interviewId, expectedCount)` — after node creation stages
- `protocol.waitForNode(interviewId, nodeName)` — when count alone is ambiguous
- `protocol.waitForNodeAttribute(interviewId, nodeName, attributeId)` — after CategoricalBin, OrdinalBin, or AlterForm stages (checks for non-null value)
- `protocol.waitForEgoAttribute(interviewId, attributeId, expectedValue)` — after EgoForm stages

Example for a CategoricalBin stage with downstream skip logic:

```typescript
test('Stage N: CategoricalBin', async ({ interview, stage, protocol }) => {
await interview.goto(N);

await stage.categoricalBin.dragNodeToBin('Dan', 'Yes');
await stage.categoricalBin.dragNodeToBin('Alice', 'No');

await expect(interview.nextButton).toBeEnabled();

// Wait for the LAST categorized node's attribute to persist
await protocol.waitForNodeAttribute(
interview.interviewId,
'Alice',
'variable-uuid',
);
});
```

**Always add `protocol` to the test's destructured fixtures** when using persistence waits.

### Stages With Placeholder Fixtures

Check the Fixture Availability Summary in STAGE_TEST_REFERENCE.md. If a stage type's fixture is marked **Placeholder**, generate a minimal test with a TODO referencing the placeholder:

```typescript
test('Stage N: Stage Label', async ({ page, interview }) => {
await interview.goto(N);

// TODO: stage.dyadCensus is a placeholder fixture — implement its
// interaction methods before writing full test assertions.
// See DyadCensusFixture JSDoc in stage-fixture.ts for the methods needed.
//
// Expected behavior from recording:
// - Dismiss intro panel
// - Select Yes/No for each node pair
// - Auto-advances after 350ms
});
```

Always reference the `stage.<fixtureName>` property (e.g., `stage.dyadCensus`, `stage.narrative`) so the test structure is ready — it just needs the fixture methods implemented. Never use raw Playwright selectors as a fallback.

### Skipped Stages

If the recording skips certain stage indices (e.g., conditional stages), add a comment:

```typescript
// Stages N-M are skipped (conditional on <condition from codebook>)
```

### Browser-Specific Skips

Add `test.skip()` for known browser limitations:

```typescript
// Skip geospatial on Firefox (no WebGL in Playwright's Firefox)
test.skip(browserName === 'firefox', 'Firefox lacks WebGL support in Playwright');
```

## Step 5: Verify Protocol File Location

Check if the `.netcanvas` file is already in `tests/e2e/data/`. If not, suggest copying it there and update the path constant accordingly.

## Step 6: Output Summary

After generating the test file, output:
1. Path to the generated test file
2. Number of stages covered
3. Number of validation tests included
4. List of stages with TODO placeholders (missing fixtures)
5. Suggested next steps (copy protocol to test data, run tests, etc.)

## Important Rules

- **Always use path aliases** (`~/tests/e2e/...`) for imports, never relative paths
- **Use `.js` extensions** in import paths (TypeScript with ESM)
- **Field names are UUIDs** — always use the variable UUID from the codebook, not the display name
- **Serial mode** — interview tests MUST use `test.describe.configure({ mode: 'serial' })`
- **Soft assertions for screenshots** — the `afterEach` capture pattern handles this via `interview.capture()`
- **No `console.log`** — project ESLint rule forbids it
- **Follow existing patterns** — match the style, structure, and conventions of `silos-protocol.spec.ts` exactly
4 changes: 4 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ node_modules
npm-debug.log
README.md
.next
.pnpm-store
.e2e-assets
.worktrees
test-results
.git
.gitignore
.env*
Expand Down
11 changes: 11 additions & 0 deletions .env.development
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Local development S3 storage — points at the MinIO container started by `pnpm dev`.
# These values are intentional test credentials and are safe to commit.
# To test the full setup wizard, comment out STORAGE_PROVIDER so the provider
# is not pinned and the settings UI stays editable.
STORAGE_PROVIDER=s3
S3_ENDPOINT=http://localhost:9000
S3_PUBLIC_URL=http://localhost:9000
S3_BUCKET=fresco-dev
S3_REGION=us-east-1
S3_ACCESS_KEY_ID=minioadmin
S3_SECRET_ACCESS_KEY=minioadmin
Loading
Loading