Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
606 commits
Select commit Hold shift + click to select a range
e282c9b
fix(create): contributor weight must be a number (decimals allowed)
holkexyz May 27, 2026
9b30070
fix(leaflet-editor): suppress default ProseMirror node-selection outline
holkexyz May 27, 2026
10b8495
fix(leaflet-editor): restore focus border, harden first-line outline …
holkexyz May 27, 2026
a5383c5
fix(create, leaflet-editor): location modal parity, my-locations drop…
holkexyz May 27, 2026
d773f35
fix(create): unify placeholder color, default rights to Public Display
holkexyz May 27, 2026
372338c
fix(create): location picker — My locations first, map preview, talle…
holkexyz May 27, 2026
7d801db
fix(leaflet-editor, create): stop cursor reset on Enter; match modal …
holkexyz May 27, 2026
1937b1d
fix(create): jump-zoom on existing pick, dedup to existing, smaller t…
holkexyz May 27, 2026
602e03b
fix(create): bump empty-state map zoom from 1 to 2
holkexyz May 27, 2026
be72c6b
fix(create): split location search field from saved name
holkexyz May 27, 2026
19861e9
fix(create): put location search + name side-by-side above the map
holkexyz May 27, 2026
1f07ef2
fix(create): set navbar page title to "New cert"
holkexyz May 27, 2026
e66b99f
fix(create, leaflet-editor, format-date): enable images, fix link/emb…
holkexyz May 27, 2026
fd73b1a
fix(create): clamp location modal width/height to viewport
holkexyz May 27, 2026
8a4b0c1
fix(leaflet-editor): portal link + embed dialogs to body to escape fo…
holkexyz May 27, 2026
a0db52c
fix(create): time-period inputs use literal ISO 8601 YYYY-MM-DD
holkexyz May 27, 2026
a3aeddb
fix(create): one location field that toggles between search and rename
holkexyz May 27, 2026
6762c0f
fix(create): trim location hint, add search-again icon + Enter shortcut
holkexyz May 27, 2026
c8dc975
fix(create): restore native date picker; .value is already ISO 8601
holkexyz May 27, 2026
19ff5fe
feat(navbar): "+" create menu with New cert / project / group shortcuts
holkexyz May 27, 2026
fce69ec
fix(navbar): move "+" create button to the right of the search bar
holkexyz May 27, 2026
9334c45
feat(create, groups): enable groups to publish certs; extract contrib…
holkexyz May 27, 2026
b754ef8
feat(project/new): replace placeholder with full project create form
holkexyz May 27, 2026
20ca7ed
fix(project/new): match project-detail width; hide tabs row
holkexyz May 27, 2026
8b34c80
fix(settings): use page-layout grid for org settings + tighten sticky…
May 27, 2026
6a11321
fix(project/new, project-detail): track collection lexicon; add cert …
holkexyz May 27, 2026
d91f95f
feat(create, project/new): extract LocationPickerDialog; project gets…
holkexyz May 27, 2026
a9e1e7d
fix(location-picker): hide "My locations" tab when the user has none
holkexyz May 27, 2026
95a5afd
feat(project/new): "Your certs" quick-pick checklist
holkexyz May 27, 2026
e8ebc8e
fix(create): narrow /create total width to match /project/new
holkexyz May 27, 2026
7afcc79
fix(project/new): relabel quick-pick, drop search-for-any-cert afford…
holkexyz May 27, 2026
363a2f8
feat(project/new, groups): publish to group repo when active org is set
holkexyz May 27, 2026
5ff7b1b
fix(cert + project detail): suppress personal-edit gate when acting a…
holkexyz May 27, 2026
7dbd639
fix(leaflet-editor dialogs): stop submit bubbling so embed/link don't…
holkexyz May 27, 2026
54da7ae
fix(create): "Add me" tracks active identity; gate start-date <= end-…
holkexyz May 27, 2026
4f9361a
fix(project-detail): match cert's "Read full description" button + "D…
holkexyz May 27, 2026
baac033
fix(add-to-list-menu): anchor popover to the right edge so it flows left
holkexyz May 27, 2026
d742f5f
feat(cert + project detail): type-to-confirm destructive delete
holkexyz May 27, 2026
7d24e53
fix(profile-projects, project-detail, delete): clickable cards, inlin…
holkexyz May 27, 2026
b75ed5f
Merge pull request #104 from hypercerts-org/fix/settings-org-page-layout
holkexyz May 27, 2026
fd872d3
feat(groups/create): redesign to match /create + /project/new shells
holkexyz May 27, 2026
2e1bf1b
fix(chrome): route identity-bound actions to the active account; "+" …
May 28, 2026
9066d96
Merge pull request #105 from hypercerts-org/fix/site-drawer-profile-a…
holkexyz May 28, 2026
e49439c
feat(groups/create): circular avatar + banner on a shared header row;…
May 28, 2026
1d72a0a
Merge pull request #106 from hypercerts-org/feat/groups-create-header…
holkexyz May 28, 2026
af7470c
style(groups/create): match avatar + banner heights, drop the size kn…
May 28, 2026
466c542
style(banners): standardise on 3:1 across project hero + explore card…
May 28, 2026
4a66835
fix(groups/create): keep avatar edit pill fully visible
May 28, 2026
c420e0a
feat(explore): server-side People/Organizations filter (closes #107)
May 28, 2026
a186cd9
style(groups/create): louder dropzone outline + distinct empty icons
May 28, 2026
500cdf9
fix(explore): apply people/orgs sub-toggle to featured + endorsed paths
May 28, 2026
52db8ab
docs: add design audit, divergence sheet, and consolidation plan
holkexyz May 28, 2026
764e39d
style(tokens): tier 1 mechanical fixes
holkexyz May 28, 2026
b405f66
style(radius): push all border-radius values to var(--radius)
holkexyz May 28, 2026
529203a
style(layout): align landing + page-frame breakpoints to 800 px token
holkexyz May 28, 2026
ad95639
style(typography): legal pages → canonical heading scale
holkexyz May 28, 2026
2c22f79
feat(button): add size="icon" variant + migrate domain modal + Plus b…
holkexyz May 28, 2026
b2047e7
style(landing): remove orphan hero text-button CSS
holkexyz May 28, 2026
9b0285d
feat(input): add size + variant API; migrate sign-in + delete-record
holkexyz May 28, 2026
2185715
refactor(profile): source groups from CGS only + show "Endorsed by N"…
May 28, 2026
0976010
feat(badge): expand variant API to absorb ad-hoc pill families
holkexyz May 28, 2026
22af25d
feat(card): extract <Card> with row / elevated / inset variants
holkexyz May 28, 2026
acbe84f
feat(tabs): extract <Tabs> / <TabList> / <Tab> / <TabPanel>
holkexyz May 28, 2026
5c4dcf4
feat(skeleton): extract <Skeleton variant="line|box|circle|text"/>
holkexyz May 28, 2026
148f97d
feat(popover): extract <Popover> primitive (custom, no lib)
holkexyz May 28, 2026
9948999
style(profile-sidebar): unify Endorsed-by link colour across text + c…
May 28, 2026
0b5d0e2
fix(profile): gate Groups tab on active identity, not just session
May 28, 2026
82bed02
style(profile-sidebar): tighten Endorsed-by → followers gap to 6px
May 28, 2026
aa7f0ab
refactor(modals): migrate AddOrg + MembershipSync to <AppDialog>
holkexyz May 28, 2026
727fa86
style(layout): align project-detail wide width to 1280 px
holkexyz May 28, 2026
2cfd007
feat(landing): proper dark-mode treatment + define missing tokens
holkexyz May 28, 2026
1339345
fix(profile-sidebar): wrap stats rows so Endorsed-by sits 6px below f…
May 28, 2026
62cc37c
docs(design): document the consolidation pass + fix react-hooks/refs
holkexyz May 28, 2026
8bc88fc
docs(audit): regenerate screenshots after consolidation pass
holkexyz May 28, 2026
586ba05
feat(cert): dedicated /activity/<did>/<rkey>/edit page mirroring /create
May 28, 2026
b5fe827
fix(top-bar): hide cert tab strip on the edit page
May 28, 2026
9d9f2b1
feat(cert-forms): render picked contributors as avatar+name+handle cards
May 28, 2026
931d299
merge: feat/positioning-redesign into feat/design-consolidation
holkexyz May 28, 2026
6a249f0
Merge pull request #108 from hypercerts-org/feat/design-consolidation
holkexyz May 28, 2026
1a47daa
style(cert-forms): flat contributor card + vertically-center the row …
May 28, 2026
6d484eb
docs: lock in design consolidation rules for future AI agents
holkexyz May 28, 2026
b9a2381
feat(cert-edit): EditBanner replaces bottom Save/Cancel row + canSave…
May 28, 2026
6d3422e
fix(edit-banner): primary Save button no longer goes white-on-white o…
May 28, 2026
3599da7
fix(cert-forms): contributor card only renders after explicit commit
May 28, 2026
fafb659
feat(project): dedicated /project/<did>/<rkey>/edit page mirroring /p…
May 28, 2026
22e2f62
style(profile): shorten "Create new X" / "Create list" CTAs to "New X…
May 28, 2026
e3f922b
feat(project-forms): empty-state note under Certs when the user has n…
May 28, 2026
e2f1cf1
fix(explore): direct keep-set replaces complement, fixes inverted Peo…
May 28, 2026
9e16a75
chore(endorsements): remove dead temp.graph.endorsement legacy path
May 28, 2026
a0c896a
Merge pull request #109 from hypercerts-org/chore/remove-legacy-endor…
holkexyz May 28, 2026
c2d7a32
feat(cert): move Locations + map into the overview main pane
holkexyz May 28, 2026
d9414eb
fix(endorsements): sidebar endorse updates counter and Received tab
May 28, 2026
6ae4c40
feat(cert): show Updates above Locations on overview, cap at one card
holkexyz May 28, 2026
d321069
feat(cert): add Record tab with a schema field/value table
holkexyz May 28, 2026
74e71aa
docs(review): Phase 1 survey findings + CHANGES log
holkexyz May 28, 2026
ead9d67
docs(changes): restructure CHANGES log to append-only with sentinel
holkexyz May 28, 2026
07e945e
fix(context): reject non-http attachment uris in resolveAttachment [b…
holkexyz May 28, 2026
1fc73e3
fix(metadata): point OG image to existing certs-hero asset [bug-002]
holkexyz May 28, 2026
5383a38
fix(groups): preserve InvalidSwap discriminator on BFF writes [bug-003]
holkexyz May 28, 2026
d576305
fix(profile): await in-flight avatar/banner upload before save [bug-004]
holkexyz May 28, 2026
fc61e7d
fix(project): resolve location strongRef so it displays [bug-005]
holkexyz May 28, 2026
4088805
fix(groups): correct audit result pill class allowlist [bug-006]
holkexyz May 28, 2026
18dee15
fix(endorsements): show rejected awards in owner Received inbox [bug-…
holkexyz May 28, 2026
a980cc2
fix(upload): surface blob upload errors and clear preview [bug-008]
holkexyz May 28, 2026
b102c82
fix(profile): self-preview banner pick in BannerUpload [bug-009]
holkexyz May 28, 2026
d129b75
fix(home-feed): render location MapPin in feed card with period [bug-…
holkexyz May 28, 2026
020ef46
fix(use-user-activities): guard loadMore generation + dedup URIs [bug…
holkexyz May 28, 2026
af20bdc
fix(xrpc): redact secrets in echoed 4xx upstream messages [risk-001]
holkexyz May 28, 2026
722bcf5
fix(groups/register): use logSafe on org-limit check failure [risk-002]
holkexyz May 28, 2026
2d6e42f
fix(groups): sanitize handle and validate email at register boundary …
holkexyz May 28, 2026
416554d
fix(profile): reuse minted location rkey on save retry [risk-003]
holkexyz May 28, 2026
fc7b7fb
fix(seo): disallow app-only routes in robots.ts [risk-006]
holkexyz May 28, 2026
e423835
fix(project): route save-conflict re-read through authFetch [risk-008]
holkexyz May 28, 2026
3299ced
refactor(feed): delete dead UserFeed component [quality-001]
holkexyz May 28, 2026
6492574
docs(changes): log quality-002 BLOCKED [quality-002]
holkexyz May 28, 2026
75895ea
refactor(cert-context): paginate fetchAllCertContext via cursor [qual…
holkexyz May 29, 2026
05a13a2
refactor(profile): drop dead EndorseShortcut + inert overlay [quality…
holkexyz May 29, 2026
9dea289
refactor(workspace): drop dead fetchOrganizationDids exports [quality…
holkexyz May 29, 2026
15b8510
refactor(feed): delete dead CertHeadlineByline component [quality-049]
holkexyz May 29, 2026
fafa782
refactor(feed): delete dead LocationCard component [quality-050]
holkexyz May 29, 2026
743d110
docs(feed): correct FeedLayout JSDoc to real consumers [quality-051]
holkexyz May 29, 2026
5b98701
chore(ci): add ci.yml running lint/tsc/test on PRs [quality-009]
holkexyz May 29, 2026
ed186b0
chore(tsconfig): add tsconfig.test.json + typecheck:test [quality-010]
holkexyz May 29, 2026
73850c3
chore(config): pin Node version via engines and .nvmrc [quality-018]
holkexyz May 29, 2026
9fbb21c
chore(scripts): add typecheck npm script [quality-019]
holkexyz May 29, 2026
2ffe2a3
chore(lint): drop no-op --ext flag from lint script [quality-020]
holkexyz May 29, 2026
a8f9c57
chore(lint): expand lint to eslint . with build-output ignores [quali…
holkexyz May 29, 2026
0519f5b
fix(scripts): unify audit dev-server port default to 3000 [quality-022]
holkexyz May 29, 2026
8c6b483
chore(gitignore): anchor core pattern to /core [quality-023]
holkexyz May 29, 2026
d43ed60
refactor(atproto): constrain saveWithSwap TDrafts to Partial<TSnapsho…
holkexyz May 29, 2026
376983e
refactor(swap): share one array-guarded shallowEqual [quality-026]
holkexyz May 29, 2026
dcd6756
fix(badges): sort missing createdAt def to end of canonical pick [qua…
holkexyz May 29, 2026
e762ab9
refactor(atproto): location.ts uses shared strict parseAtUri [quality…
holkexyz May 29, 2026
0954187
refactor(notifications): validate load-bearing edge fields [quality-029]
holkexyz May 29, 2026
c219186
fix(atproto): validate resolveHandle alsoKnownAs is a handle [quality…
holkexyz May 29, 2026
fde4b18
refactor(hooks): add module-level cache to useOrgProfile [quality-006]
holkexyz May 29, 2026
4529435
docs(changes): log quality-037 SKIPPED [quality-037]
holkexyz May 29, 2026
0386675
fix(home-feed): track INVALID_CURSOR recovery controller [quality-031]
holkexyz May 29, 2026
e45219b
fix(hooks): abort focus-revalidate fetches via ref'd controller [qual…
holkexyz May 29, 2026
542fe29
docs(changes): log quality-033 BLOCKED [quality-033]
holkexyz May 29, 2026
1abb7da
refactor(hooks): read live lists in useEndorsementLists mutations [qu…
holkexyz May 29, 2026
f5a19a8
refactor(explore): tie loadMore fetch to generation signal [quality-035]
holkexyz May 29, 2026
d25a3d3
fix(profile): revoke promoted avatar/banner blob URL after save [qual…
holkexyz May 29, 2026
91f29bc
fix(hooks): usePendingAwardsCount returns null when logged out [quali…
holkexyz May 29, 2026
763d9d8
refactor(hooks): reset bottom-sheet viewport styles on cleanup [quali…
holkexyz May 29, 2026
f726a82
fix(hooks): clear bottom-sheet dismiss timeout on unmount [quality-040]
holkexyz May 29, 2026
1a3933c
fix(hooks): clear in-flight dedupe on useOrgMarker refresh [quality-041]
holkexyz May 29, 2026
fe30617
refactor(profile): extract shared PersonCard + name-cache hook [quali…
holkexyz May 29, 2026
87b36e5
fix(profile-lists): show fallback row when a project fails to load [q…
holkexyz May 29, 2026
9d8cdd5
refactor(profile): use useClickOutsideClose for endorsement dropdowns…
holkexyz May 29, 2026
515a1c2
docs(changes): log quality-045 BLOCKED [quality-045]
holkexyz May 29, 2026
6ead1da
fix(profile): stable createdAt sort in endorsement lists [quality-046]
holkexyz May 29, 2026
52c6ee0
refactor(profile-lists): use row index for checkbox aria-label [quali…
holkexyz May 29, 2026
8079ad2
refactor(profile): keep optimistic endorse on list-append failure [qu…
holkexyz May 29, 2026
4edb194
test(sanitize): add unit tests for stripInvisible/sanitizeEmail/sanit…
holkexyz May 29, 2026
f1ec9f8
refactor(search-actors): route catch log through logSafe [quality-014]
holkexyz May 29, 2026
aaa4ef1
refactor(notifications): drop response body from upstream error log […
holkexyz May 29, 2026
18c48fd
fix(xrpc): clamp upstream status to valid HTTP range [quality-016]
holkexyz May 29, 2026
d573291
fix(indexer): enforce 32KB body cap in bytes not UTF-16 units [qualit…
holkexyz May 29, 2026
9266ad8
refactor(popover): use --z-popover token instead of z-[40] [quality-008]
holkexyz May 29, 2026
f66c4f1
refactor(onboarding): revoke step-profile preview object URLs [qualit…
holkexyz May 29, 2026
f1d47db
fix(feed): reset imageFailed when ActivityCard imageUrl changes [qual…
holkexyz May 29, 2026
7e376fc
refactor(feed): memoize ActivityCard to avoid loadMore re-renders [qu…
holkexyz May 29, 2026
38245a2
refactor(right-rail): key news images by thumb URL not index [quality…
holkexyz May 29, 2026
6fbdab9
refactor(feed): make expanded locations map height viewport-relative …
holkexyz May 29, 2026
8a24feb
fix(auth): match safeRedirect http: gate to module's !==production [q…
holkexyz May 29, 2026
ea0a318
docs(auth): note swallowed deleteSession is not session fixation [qua…
holkexyz May 29, 2026
3479605
docs(csrf): document deliberate missing-Origin-AND-Referer reject [qu…
holkexyz May 29, 2026
ea16a22
docs(agents): correct ALLOWED_WRITE_COLLECTIONS count to eleven [qual…
holkexyz May 29, 2026
d4b05fb
refactor(utils): extract countGraphemes helper [quality-056-cert-5]
holkexyz May 29, 2026
657dd36
refactor(project): extract own-certs quick-pick into useOwnCerts [qua…
holkexyz May 29, 2026
1aa685a
docs(project): document activity-only meta reads in project-detail [q…
holkexyz May 29, 2026
c3cb71e
refactor(map): drop dead theme reactivity in base tiles [quality-056-…
holkexyz May 29, 2026
d684e3c
refactor(geocode): delete dead forwardGeocode export [quality-056-map-4]
holkexyz May 29, 2026
b4b151b
refactor(map): tokenize leaflet embed bg; justify JS hex [quality-056…
holkexyz May 29, 2026
d913f1f
refactor(ui): tokenize ProviderRedirectOverlay z-index [quality-056-u…
holkexyz May 29, 2026
bd27181
fix(ui): skeleton text variant aria-hidden + honor width [quality-056…
holkexyz May 29, 2026
576b2b9
fix(ui): hide icon child while loading + default button type [quality…
holkexyz May 29, 2026
ea35ea2
refactor(explore): delete dead ?attrs= filter never set by any UI [qu…
holkexyz May 29, 2026
f685bf8
fix(explore): add aria-haspopup to sort + quality popover triggers [q…
holkexyz May 29, 2026
54321da
refactor(groups): use ConfirmDialog for Leave Group modal [quality-05…
holkexyz May 29, 2026
5c86eda
refactor(groups): delete dead AddOrgModal + MembershipSyncModal [qual…
holkexyz May 29, 2026
672549c
docs(changes): log quality-056-groups-5 SKIPPED [quality-056-groups-5]
holkexyz May 29, 2026
34f6954
fix(test): use renderHook in safe-redirect test to clear lint error […
holkexyz May 29, 2026
bcb4f20
fix(groups): re-stage only failed members on partial add [quality-056…
holkexyz May 29, 2026
561caf9
docs(changes): log quality-056-profile-edit-2 SKIPPED [quality-056-pr…
holkexyz May 29, 2026
e234749
test(profile): link edit-form inputs to error text via aria-described…
holkexyz May 29, 2026
b2c5c45
refactor(profile): derive BannerUpload label from displayed image [qu…
holkexyz May 29, 2026
aaa0869
refactor(profile): tokenize avatar overlay colors [quality-056-profil…
holkexyz May 29, 2026
0d55605
refactor(landing): delete dead orbiting-logos component [quality-056-…
holkexyz May 29, 2026
abb79c6
refactor(legal): theme-aware link/text tokens for dark mode [quality-…
holkexyz May 29, 2026
6adb271
refactor(global-error): radius 6px to 2px + token comment [quality-05…
holkexyz May 29, 2026
0ba95a2
docs(agents): sync §2 tech-stack versions with package.json [quality-…
holkexyz May 29, 2026
9f75429
refactor(groups): derive role allowlists from one ORG_ROLES source [q…
holkexyz May 29, 2026
acd6447
perf(groups/register): early-exit member walk on self-added hit [qual…
holkexyz May 29, 2026
a2d25d9
refactor(notifications): use shared truncateDid + getInitials [qualit…
holkexyz May 29, 2026
b5d5dd3
refactor(labeller): drop dead exports pickKnownLabel/FilterValue/ALL_…
holkexyz May 29, 2026
74cd8ce
refactor(lib): drop dead exports getBlobRefLinkFromBlob/clearRecently…
holkexyz May 29, 2026
ff0a189
test(utils): cover did.ts isValidDid/isDid validators [quality-056-ut…
holkexyz May 29, 2026
317c2f6
test(utils): cover bounded-cache, format-date, ip, recently-viewed [q…
holkexyz May 29, 2026
abac1ee
docs(changes): finalize Phase 2 summary, held list, and follow-ups
holkexyz May 29, 2026
b75955f
Merge pull request #111 from hypercerts-org/overnight-review
holkexyz May 29, 2026
0bce563
Merge feat/positioning-redesign into fix/endorsement-received-overlay
holkexyz May 29, 2026
e70c9fb
Merge pull request #110 from hypercerts-org/fix/endorsement-received-…
holkexyz May 29, 2026
1efefb0
fix(auth): coerce OAuth prompt param to allowlist [judgment-001]
holkexyz May 29, 2026
adb12af
fix(resolve-did): add IP+DID rate limiter to GET /api/resolve-did [ju…
holkexyz May 29, 2026
722a001
fix(xrpc): cap foreign getBlob Content-Length (413) + fix Cache-Contr…
holkexyz May 29, 2026
e99b506
fix(auth): add Cache-Control private, no-store to /api/auth/session […
holkexyz May 29, 2026
2618930
fix(dashboard): gate custom-domain affordance off in group context [j…
holkexyz May 29, 2026
806bea1
fix(seo): canonicalize landing on /welcome; align sitemap/robots/mani…
holkexyz May 29, 2026
aed6b80
docs(postcss): note autoprefixer intentionally omitted [judgment-007]
holkexyz May 29, 2026
a96b1e3
refactor(explore): consolidate menu popovers + z-index token [judgmen…
holkexyz May 29, 2026
1542a34
Merge pull request #113 from hypercerts-org/judgment/clear-fixes
holkexyz May 29, 2026
65aa8ce
feat(resolve-did): optional indexer actorProfile fast-path [resolve-d…
holkexyz May 29, 2026
46e68e4
Merge pull request #114 from hypercerts-org/feat/resolve-did-indexer-…
holkexyz May 29, 2026
cb0d99d
fix(profile): inline-edit preserve avatar/banner; thread swapRecord […
holkexyz Jun 1, 2026
14f7dcd
fix(auth): never seed app.bsky on sign-in + guard app.certified seed …
holkexyz Jun 1, 2026
334eef5
fix(resolve-did): per-field appView fallback so a null indexer avatar…
holkexyz Jun 1, 2026
a07651a
fix(resolve-did): bsky profile is an all-or-nothing fallback [profile…
holkexyz Jun 1, 2026
363e348
Merge pull request #116 from hypercerts-org/fix/profile-avatar-dataloss
holkexyz Jun 1, 2026
9b18363
feat(indexer): send X-RateLimit-Bypass from /api/indexer + /api/notif…
Jun 1, 2026
95a8753
test: fix test-file type errors and wire typecheck:test into CI
Jun 1, 2026
525d0f7
fix(profile): guard follow/endorse while acting as a group
holkexyz Jun 2, 2026
8ff12ea
Merge pull request #117 from hypercerts-org/fix/acting-as-group-write…
holkexyz Jun 2, 2026
3573418
fix(nav): brandmark and /home route signed-out visitors to /welcome
holkexyz Jun 2, 2026
425eff9
Merge pull request #118 from hypercerts-org/fix/brandmark-welcome-rou…
holkexyz Jun 2, 2026
8e5dde4
docs(switcher): mockups + decision record for org identity model
holkexyz Jun 2, 2026
8f261ba
feat(org): act-as delegation — orgs can endorse and follow as first-c…
holkexyz Jun 2, 2026
42152df
feat(org): respond-as-group — orgs accept/reject endorsements they re…
holkexyz Jun 2, 2026
40d523e
Merge pull request #119 from hypercerts-org/feat/org-delegation
holkexyz Jun 2, 2026
ca93798
feat(profile): add Operate-as-group action to the groups tab
holkexyz Jun 2, 2026
b2936fd
fix(endorse): show the operator's real role, not a hard-coded "admin"
holkexyz Jun 2, 2026
e10e9a3
fix(org): no personal endorsement actions while acting as a group
holkexyz Jun 2, 2026
51fade1
feat(lists): accept a bare DID when adding accounts to a list
holkexyz Jun 2, 2026
0839f3c
Merge pull request #120 from hypercerts-org/fix/org-delegation-followups
holkexyz Jun 2, 2026
258df7d
Merge pull request #121 from hypercerts-org/fix/list-add-bare-did
holkexyz Jun 2, 2026
e065422
fix(dark-mode): unreadable white-on-white text on highlighted/active …
holkexyz Jun 2, 2026
de88414
Merge pull request #123 from hypercerts-org/fix/dark-mode-white-on-white
holkexyz Jun 2, 2026
a0b1ebe
chore(cert): remove the Record tab from the cert overview
holkexyz Jun 2, 2026
c6b0c7c
Merge pull request #124 from hypercerts-org/chore/remove-cert-record-tab
holkexyz Jun 2, 2026
1237e6d
fix(welcome): make the footer full-bleed (full screen wide)
holkexyz Jun 2, 2026
38a0e37
refactor(naming): rename cert -> activity in user-facing display text
holkexyz Jun 2, 2026
0a75774
Merge pull request #125 from hypercerts-org/fix/welcome-footer-full-w…
holkexyz Jun 2, 2026
7ac84eb
refactor(naming): rename cert -> activity in URL/query values
holkexyz Jun 2, 2026
c2a52cb
Merge pull request #126 from hypercerts-org/chore/rename-cert-to-acti…
holkexyz Jun 2, 2026
1db4965
fix(resolve-did): batch author/contributor resolution to stop 429s
holkexyz Jun 2, 2026
dee42c5
fix(resolve-did): address impl-review (weighted limit, cooldown, timers)
holkexyz Jun 2, 2026
91cfb03
Merge pull request #127 from hypercerts-org/fix/resolve-did-batch-429
holkexyz Jun 2, 2026
fc065a6
Merge staging into feat/positioning-redesign (ours): supersede stagin…
holkexyz Jun 2, 2026
ec8c6de
ci: bump Node to 20.19.0 — vitest 4 / rolldown needs util.styleText
holkexyz Jun 2, 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
  •  
  •  
  •  
46 changes: 46 additions & 0 deletions .env.local.example
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,52 @@ COOKIE_SECRET=dev-secret-change-in-production
UPSTASH_REDIS_REST_URL=
UPSTASH_REDIS_REST_TOKEN=

# Magic Indexer GraphQL endpoint and DID.
#
# INDEXER_URL is consumed by the server-side /api/indexer and /api/notifications
# proxies. If unset, the code falls back to NEXT_PUBLIC_INDEXER_URL, and then to
# a hardcoded fallback (magic-indexer-prod.up.railway.app). For local dev
# against the dev indexer, set this to magic-indexer-dev.up.railway.app.
#
# INDEXER_DID is required for the notifications JWT `aud` claim. Without it,
# /api/notifications returns 503 and logs a module-load warning.
INDEXER_URL=https://magic-indexer-prod.up.railway.app/graphql
INDEXER_DID=

# Deprecated alias for INDEXER_URL; still read for backwards-compat. Prefer
# INDEXER_URL above and leave this unset on new deploys.
# NEXT_PUBLIC_INDEXER_URL=

# Optional indexer fast-path for /api/resolve-did (default false). When
# "true", resolve-did reads identity (handle + the Bluesky profile block)
# from the indexer's actorProfile(did) query instead of fanning out to
# resolveHandle + app.bsky.actor.getProfile for DIDs the indexer has
# backfilled. Falls back to the legacy path per-field on any indexer miss,
# so it's safe to leave off until the actorProfile query is deployed on the
# targeted indexer. Leave unset (or "false") to keep today's behaviour.
# RESOLVE_DID_USE_INDEXER=false

# Optional rate-limit bypass key for the app's server-side indexer traffic.
# Sent as `X-RateLimit-Bypass` by the /api/indexer, /api/notifications, and
# resolve-did upstream fetches so the app's own proxied requests aren't
# throttled by the indexer's per-IP /graphql rate limiter. Must match the
# indexer's GRAPHQL_RATE_LIMIT_BYPASS_KEY. The header is sent only when this
# is non-empty; only needed once the indexer enables its GraphQL rate limiter.
# INDEXER_RATELIMIT_BYPASS_KEY=

# Optional: Group service URL and DID (CGS). Defaults to the production CGS.
# NEXT_PUBLIC_GROUP_SERVICE_URL=https://groups.certified.app
# NEXT_PUBLIC_GROUP_SERVICE_DID=did:web:groups.certified.app

# Optional: Stadia Maps API key for map tiles. When unset, the map falls back
# to Carto tiles (also free for basic use).
#
# Note: This is a NEXT_PUBLIC_ var, so the key is inlined into the client
# bundle — it is NOT secret. Stadia's intended enforcement is per-domain
# Referer allowlist configured on the Stadia dashboard. Set the allowlist
# there to your production domain(s) before relying on the key in prod.
# NEXT_PUBLIC_STADIA_API_KEY=

# Optional: Set to enable confidential client (private_key_jwt) authentication
# in production. Ignored in loopback dev mode (the spec mandates
# token_endpoint_auth_method: none for loopback clients).
Expand Down
35 changes: 35 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: CI

on:
pull_request:
branches:
- staging
- main

jobs:
verify:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20.19.0'
cache: npm

- name: Install dependencies
run: npm ci

- name: Lint
run: npm run lint

- name: Type check
run: npx tsc --noEmit

- name: Type check (tests)
run: npm run typecheck:test

- name: Test
run: npm test
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,4 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts
core
/core
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
20.19.0
234 changes: 187 additions & 47 deletions AGENTS.md

Large diffs are not rendered by default.

677 changes: 677 additions & 0 deletions CHANGES.md

Large diffs are not rendered by default.

53 changes: 53 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# CLAUDE.md — certified-app

Claude Code reads this file at session start. The longer agent reference is `AGENTS.md`; the design system is `DESIGN.md`. This file holds the **hard rules** that drift most often and the **first checks** to run before any UI change.

## Hard rules (do not violate)

1. **`border-radius` is `var(--radius)` (2 px), `999px` for pills, `50%` for circles.** Never `4`, `6`, `8`, `12`, `16`, `20` px. The sign-in modal is not an exception.
2. **No raw hex / rgb / rgba** outside `src/app/styles/tokens.css` and `src/app/styles/landing.css`. Everywhere else uses tokens (`--bg-canvas`, `--fg-primary`, `--border-default`, …).
3. **Breakpoints are 800 / 1100 / 1300 only.** "Below desktop" → `@media (max-width: 799px)`. Don't introduce 768 / 760 / 640.
4. **Shadows are `var(--shadow-sm | --shadow-md | --shadow-lg)`.** No `box-shadow: 0 X Y rgba(...)`.
5. **Z-index is a `--z-*` token.** No literal numbers.
6. **Headings use the canonical scale + `font-headline`.** `text-h1`/`text-h2`/`text-h3`/`text-h4`. Never `text-xl` / `text-lg` / `text-2xl` for app headings.
7. **Modals use `<AppDialog>`** (or `<ConfirmDialog>` / `<DeleteRecordDialog>` which wrap it). Never hand-roll a backdrop + `useFocusTrap`.
8. **Icon-only buttons are `<Button size="icon" aria-label="…">`.** The TypeScript discriminated union enforces `aria-label`.
9. **Dark mode must work.** Toggle `data-theme="dark"` on `<html>` and verify. Don't pin colors so they break the flip. Landing must also flip (use `--color-navy` / `--color-off-white` — theme-aware — not `--color-primary` / `--color-white` which are invariant).
10. **Reach for a primitive in `src/components/ui/` before writing a new component or BEM class.** The audit that produced this rule found 12 button vocabularies and 8 card families coexisting — don't restart that drift.

## First checks before merging a UI change

```bash
# 1. No off-spec radii
grep -rEn "border-radius:\s+(4|6|8|12|16|20)px" src/app/styles/

# 2. No non-canonical breakpoints
grep -rEn "@media.*?\((max|min)-width:\s*(76[08]|64[80])px\)" src/app/styles/

# 3. No legal-style headings on app pages
grep -rn "font-headline text-\(xl\|lg\|2xl\)" src/app/ src/components/

# 4. No hand-rolled modal backdrops
grep -rn "signin-modal__backdrop" src/components/ | grep -v "ui/sign-in-modal.tsx\|ui/feedback-modal.tsx"

# 5. Type + lint must be clean
npx tsc --noEmit
npm run lint
```

All five should be silent (or, for lint, no change vs. main's baseline of 60 warnings).

## Where to look

- **Design rules:** `DESIGN.md` — read §14 first ("Rules at a glance" and the consolidation pass).
- **Architecture / conventions / pitfalls:** `AGENTS.md`. The "Rules at a glance" callout in §0 mirrors this file's hard rules with more detail. §11 (CSS) and §12 (Component) have the full primitive catalog.
- **Design audit + visual divergence sheet:** `docs/design-audit/component-audit.md`, `docs/design-audit/visual-divergence.md`, `docs/design-audit/divergence-sheet.html` (open in a browser).
- **Implementation plan / decision log:** `docs/design-consolidation/plan.md`.

## Workflow conventions (per the user's global rules)

- For non-trivial work, follow the plan → review → branch → PR flow in the user's global CLAUDE.md (`~/.claude/CLAUDE.md`).
- For certified-app specifically, **substantial work commits directly to `staging`** unless explicitly told otherwise (per `feedback_certified_app_staging.md`).
- Draft PR is `staging → main`. Never auto-open it.
- Never merge — leave the PR Draft + CI green and ask the user.
- No emojis in code, commits, or PR bodies (the `🤖 Generated with` footer is fine).
85 changes: 83 additions & 2 deletions DESIGN.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# DESIGN.md — Certs.social

> **AI agents: read §14 first.** It locks in the post-consolidation rules (2026-05-28) and supersedes earlier sections where they contradict. Read the "Rules at a glance" callout in `AGENTS.md` §0 in parallel — it's the executive summary of what to do and not do.

## 1. Visual Theme & Atmosphere

Certs.social feels like a notary's ledger reimagined as a mobile app — austere, monochrome, and quietly authoritative. The near-absence of color forces attention onto the content: serif headlines anchor each card like a document title, while the surrounding chrome recedes into warm grays. It's deliberately under-decorated — no gradients, no brand accent hue, no playful illustrations. The restraint *is* the brand.
Expand Down Expand Up @@ -211,7 +213,14 @@ All icons from **Lucide React**. Conventions:

### Modals

**Standard modal** (sign-in, domain): Centered on desktop, full-width on mobile. `bg-elevated`, 1px `border-default`, `shadow-lg`. Entry animation: `modalFadeIn` (backdrop 200ms) + `modalSlideUp` (content 300ms, spring easing). Sign-in modal uses 20px radius — the only intentional exception to the 2px system.
**Standard sign-in modal** (`.signin-modal` only): Centered on desktop, full-width on mobile. `bg-elevated`, 1px `border-default`, `shadow-lg`. Entry animation: `modalFadeIn` (backdrop 200ms) + `modalSlideUp` (content 300ms, spring easing). 20px radius + 40px hero padding. This shape is **reserved for the sign-in surface** — it's an intentional exception to the 2px system because sign-in is a once-per-session, branded surface.

**App modals** (every other in-app dialog — endorse-people, create-list, sync-social-graph, future): use `<dialog className="signin-modal app-modal …">`. The `.app-modal` modifier inherits the sign-in chrome (backdrop / animation / close button / focus styling) but overrides:

- `border-radius: var(--radius)` (2px — matches cards, dropdowns, inputs).
- Padding trimmed to `16px 20px 12px` header / `0 20px 20px` body — denser than the sign-in surface, which is right for form-style and list-style modals.

If you're building a new modal that isn't the sign-in flow, **always** add `app-modal` alongside `signin-modal`. Forgetting it makes the dialog read as a chunky sign-in surface and breaks the 2px system everywhere else.

**Bottom sheet** (mobile account switcher, mobile feedback): Fixed to bottom, draggable handle, swipe-down-to-dismiss. `bg-elevated`, top border-radius. Expandable via swipe-up.

Expand Down Expand Up @@ -531,9 +540,81 @@ Things an AI agent is likely to get wrong:
1. **Start with CSS custom properties.** Never hard-code colors — use `var(--bg-canvas)`, `var(--fg-primary)`, etc. This ensures dark mode works automatically.
2. **Default to `var(--radius)` (2px).** Only use 999px for pills (badges, avatars, sign-in submit) or 50% for circles.
3. **Use the `<Button>` component.** Don't create new button styles in CSS — add variants to the component if needed.
4. **Mobile-first CSS.** Write base styles for mobile, then use `@media (min-width: 769px)` for desktop overrides.
4. **Mobile-first CSS.** Write base styles for mobile, then use `@media (min-width: 800px)` for desktop overrides. For "below desktop", use `max-width: 799px`. 768 / 760 / 640 are not canonical — see §14.1.
5. **Check both themes.** Toggle `data-theme="dark"` and verify all text is readable, borders are visible, and the primary button inverts correctly.
6. **Respect the type system.** Headlines → Noto Serif 700. Body → Inter 400. Labels → Inter 500–600 uppercase. Don't mix these roles.
7. **No new shadows on cards.** Cards communicate elevation via background color (`--bg-elevated` vs `--bg-canvas`), not box-shadow. Shadows are reserved for floating elements (modals, tooltips, dropdowns).
8. **Test skeleton states.** Every new data-fetching component needs a skeleton. Match the geometry of the loaded state — rectangles where text will be, circles where avatars will be, same spacing.
9. **Icon sizing follows context.** 14px for inline actions alongside text, 20–22px for navigation chrome, 24px for bottom nav. Don't mix these.

---

## 14. Design consolidation pass (2026-05-28)

This section documents the changes from `feat/design-consolidation` (PR #108, merged into `feat/positioning-redesign`). It supersedes any contradictions earlier in this file.

The audit that drove the work: [`docs/design-audit/component-audit.md`](docs/design-audit/component-audit.md).
The visual divergence sheet: [`docs/design-audit/visual-divergence.md`](docs/design-audit/visual-divergence.md).
The implementation plan + decision log: [`docs/design-consolidation/plan.md`](docs/design-consolidation/plan.md).

### 14.0 Rules at a glance (the agent quick-reference)

The shortlist of rules that drift most often. Hold yourself to these on every UI change.

| Rule | Quick test |
| --- | --- |
| `border-radius` is `var(--radius)` (2 px), pills `999px`, circles `50%`. No 4/6/8/12/16/20. | `grep -rEn "border-radius:\s+(4\|6\|8\|12\|16\|20)px" src/app/styles/` returns 0 |
| No raw hex / rgb outside `tokens.css` (+ `landing.css` for the brand palette). | Search for `#[0-9a-fA-F]{3,8}` in your diff |
| Canonical breakpoints are 800 / 1100 / 1300; "below desktop" is `max-width: 799px`. | `grep -E "@media.*?\((max\|min)-width:\s*(76[08]\|64[80])px\)"` returns 0 |
| Shadows are `var(--shadow-sm\|md\|lg)`. No ad-hoc `box-shadow: 0 X Y rgba(...)`. | Search `box-shadow:` for non-token values |
| Z-index is a `--z-*` token. | Search `z-index:\s+[0-9]+` for literals |
| Headings use `text-h1`..`text-h4` + `font-headline`. Not `text-xl` / `text-lg`. | Search your diff for `font-headline text-(xl\|lg\|2xl)` |
| Modals use `<AppDialog>`. No hand-rolled backdrops. | Search your diff for `signin-modal__backdrop` |
| Icon-only buttons are `<Button size="icon" aria-label="…">`. | TypeScript enforces the label |
| Dark mode must work. | Toggle `data-theme="dark"` and verify everything reads |
| Reach for a `src/components/ui/` primitive before writing a new component or CSS class. | List the directory first |

### 14.1 New rules

1. **All `border-radius` values are `var(--radius)` (2 px).** No exceptions. The previous `--radius: 2px` policy was being eroded by 116+ instances of 4 / 6 / 8 / 12 / 16 / 20 px corners; those are gone. Pills stay at `999px`, circles at `50%`. The sign-in modal is no longer a "hero exception" (was 20 px → 2 px).
2. **Landing has proper dark mode.** The "landing palette kept invariant so /welcome always renders light-themed" policy is retired. The landing tokens (`--color-navy`, `--color-off-white`, `--color-light-gray`, `--color-mid-gray`, `--color-dark-gray`, `--color-surface`, `--color-surface-container-low`) flip in `[data-theme="dark"]`. `--color-primary` and `--color-white` remain invariant for systems that still depend on them (skip-nav, brand SVG).
3. **Breakpoints: 800 / 1100 / 1300 only.** Previously `landing.css` used 768 (9 places) and `home/explore/workspace` used 760 (5 places). All migrated to `max-width: 799px` to match the existing "just below desktop" convention.
4. **Form input padding follows the 4-px grid.** `12 × 14` and `7 × 12` arbitrary values were replaced with `12 × 16` / `8 × 12`.
5. **Cert-detail / project-detail "wide" pages share the 1280 px fullbleed width** with profile / settings / workspace.

### 14.2 New UI components

| Component | File | Purpose |
| --- | --- | --- |
| `<Card variant="row\|elevated\|inset">` | `src/components/ui/card.tsx` | Canonical card with three shapes. Migrate `.feed-card`, `.dash-card`, `.explore-*-card`, `.app-card`, `.endorsements-v2__card` to it. |
| `<Tabs>`, `<TabList>`, `<Tab>`, `<TabPanel>` | `src/components/ui/tabs.tsx` | Proper ARIA tab pattern with keyboard arrow navigation. Migrate `.profile-tabs__tab`, `.feed-tabs__tab`. |
| `<Skeleton variant="line\|box\|circle\|text">` | `src/components/ui/skeleton.tsx` | Single primitive for all loading states. Migrate `ActivityCardSkeleton`, `NotificationRowSkeleton`, `.feed-card__author--skeleton`, etc. |
| `<Popover>`, `<PopoverTrigger>`, `<PopoverContent>`, `<PopoverItem>` | `src/components/ui/popover.tsx` | Floating menus — click-outside, Esc, ARIA wired. Migrate `.feed-filter`, account switcher menu, workspace breadcrumb menu, `.response-menu__menu`. |

### 14.3 Extended component APIs

| Component | New API | What absorbed |
| --- | --- | --- |
| `<Button>` | `size="icon"` (40 × 40 square, requires `aria-label`) | `.desktop-top-bar__icon-btn` and similar icon-only buttons. Variant `accent` rejected; domain modal moved to `primary`. |
| `<Input>` | `size="sm\|md\|lg"` (36 / 44 / 56 px), `variant="default\|underline\|inline-edit"` | `.signin-modal__input` (size=lg), `.delete-record-dialog__input` (variant=inline-edit). |
| `<Badge>` | New variants: `tag`, `role`, `count`, `high-quality`, `standard`, `draft`, `test`. `compact` prop for the tighter 11 px chip. | `.feed-card__label*` (4 quality variants), `.org-list__item-role`. `FeedLabelPill` now composes Badge. |

### 14.4 Modal hygiene

`AddOrgModal` and `MembershipSyncModal` moved from hand-rolled backdrop/Esc/focus-trap implementations to the canonical `<AppDialog>`. `CustomDomainModal` migration is deferred (multi-step indicator needs visual review). `<ResponsiveModal>` extraction from `FeedbackModal` is deferred until a second consumer exists.

### 14.5 Z-index tokens

Added `--z-feedback: 10000` and `--z-feedback-above: 10001` to the token map. Hardcoded z-index values (`49`, `999`, `10000`, `10001`) in `layout.css` / `landing.css` / `components.css` are now token references.

### 14.6 What didn't make it

- Migration of every `.profile-tabs__tab` / `.feed-tabs__tab` to `<Tabs>` — primitive shipped, call sites stay until each is touched.
- Migration of every CSS-based card to `<Card>` — same.
- Migration of every CSS-based popover to `<Popover>` — same.
- Migration of skeleton CSS to `<Skeleton>` — same.
- `CustomDomainModal` → `<AppDialog>`.
- `<ResponsiveModal>` extraction from `FeedbackModal`.
- A stylelint rule that flags `border-radius: 6px` and raw hex outside `tokens.css`.

These are documented as follow-on work in `docs/design-consolidation/plan.md`.
Loading
Loading