It would be nice to have a beta instance that moves fast and breaks things, for beta testing new functionality, while the majority of instances stay stable. I’ll be hosting it only for as long as I’m interested. DM for an invitation. Feedback is appreciated.
Forking stash-box: 16 new features, 35 upstream PRs ported, 177 issues dispositioned
commits 268 backend Go files 289 (119 of them _test.go)frontend files 132 migrations 20 files (16 new fork-authored migrations) docs 70 test files of any kind 159 ( _test.go+__tests__+.spec.)schema 94 migrations, one per number, no two sharing a statement suite 60 packages green (untagged), 33 green with -tags=integration, 142 Go test filesfrontend tests 54 files / 683 tests
New features (this fork’s own spec)
Sixteen new migrations carry the fork’s features, numbered 79–95 so they sit above
every upstream migration:
# Migration Feature 79 add_identification_boardidentification board: multi-performer scenes and their volunteers 80 add_authored_questsauthored quests, scored independently of votes 81 add_bonus_pointsbonus points alongside the existing reputation 82 add_reviewswritten reviews, separate from ratings 83 add_site_directorydirectory of sites, their metadata and reachability 84 add_webhooksoutbound webhooks for federation and moderation events 85 add_vote_weightVanguard/trust-weighted Elo 86 add_vanguardVanguard roles and permissions 87 content_denylistcontent denylist for whole sites or tags 88 scene_title_texttitles as text, not a bounded string89 identification_federationidentification federated to peers 90 changelog_indexeskeyset-paginated changelog queries, four of them 91 performer_genitalsgenital attributes on performers 93 performer_weightphysical weight on performers 94 image_typescurator-defined image type vocabulary with per-viewer ordering 95 image_cropscropping on upload, pre-crop original retained, recrop from the original
Two full plan documents are written, built and verified:
docs/plan/feature-03b-content-access-and-vanguard-weighting.md— content access and Vanguard weighting (D1–D8; D5 deliberately split — region/device recorded, not enforced, by decision S2)docs/plan/feature-04-identification-federation.md— identification federation, with 93 lines of deviations recordedUpstream PRs: 35 of 45 ported
Twenty-six ported before this stretch; nine here, each needing structural
reconciliation rather than a patch application:
PR What it brought What it cost #1183 four keyset-paginated changelog queries + upstream tests migration 73 → 90; schemaVersionpin dropped#1269 performer genital attributes migration 76 → 91; same refusal #1225 imageListreturns nullable elementsgenerated code regenerated, not patched #1086 vote bar below comments patch would have reverted compactsupport#1123 typed-URLs actually submitted fixes silent data loss #1215 image type labels migration 76 → 94; imageListsignature kept;storeFileearly-return declined#1216 image crops + recrop migration 77 → 95; storeFiledeclined again#1248 dependency bump pgx held at 5.9.2 (5.10.0 breaks six integration packages) #1266 performer physical weight reconciled with 85_add_vote_weight— same word, different unit
Ten deliberately not ported, each with a recorded reason in
docs/plan/upstream-pr-port.md:
- #928 destructive — downgrades
postgres:18→postgres:16and drops the
pg-spgist_hammingbuild that pHash fingerprint matching depends on- #591 #708 #736 #761 #871 declined on written policy
- #1076 #1278 superseded — verified their content already present, not assumed
- #1155 #878 upstream drafts,
mergeable=UNKNOWN, conflicting against a tree
this fork diverged from months agoIssues: 177 dispositioned, 23 solved
Disposition Count fixed 22 partially-fixed 1 declined (with reasons) 16 feature-request (kept as backlog) 138 outstanding ( bug/suspect-bug/spec-collision)0
The 138 feature-requests each cite a written policy in
docs/SPEC.mdsection 0,
so the ledger is uniform application rather than bulk reclassification. The
partially-fixed row is #583: the 64-byte bcrypt cap is deliberate, and the
misleading error message was the actual defect.Verification
docs/goal-check.py— ten clauses, all PASS:C1 PRs decided all 45 open PRs decided C2 reasons present 138 rows cite a written policy, 12 distinct reasons C2 issues dispositioned 177 rows, zero outstanding C3 spec/plan built all 2 plan documents Built C4 narrow merged issue-fixes is an ancestor of master C5 migrations coherent one migration per number, none shared C6 suite 60 packages accounted for (baseline 60) C6b integration suite 33 packages green with -tags=integration C7 build clean build, vet and gofmt all cleanBugs found in upstream code while porting
Not padding — each was caught by running the suite, not by reading the diff, and
each would have shipped broken:
storeFileordering (upstream #1215, #1216). RoutingCreatethrough it
reinstates a write order that predates this fork’s #738 and #948, orphaning files
the reaper cannot find. Declined both times.- A short
Readyields a truncated image (upstream #1216) whose dimensions and
checksum describe bytes that were never stored. Upstream’sReadFullfix taken.- Four upstream test bugs (#1215): a
Countvs page-length equality that only
holds under 100 fixtures; an ASC page-scan for a performer that ASC sorts off page
one; an unguardedtoTypedImages(performer.images)that crashed the whole form on
merge-source edits while line 221 of the same file already guarded it; and a bare
<img alt="Deleted">replacing this fork’s<DeletedImage>.- pgx 5.10.0 (#1248) panics six integration packages at
initPostgres. Upstream
master is still on 5.9.2, so upstream CI green meant nothing about it.The recurring lesson: upstream’s green checks describe upstream’s fixture count,
not the code. Every one of these passes in a smaller suite.Two verification bugs this fork had in its own tooling
Worth recording because both had the same shape — a number produced by one code path
and consumed by another:
goal-check.py’s C6 floor was decorative. The clause countedokAND
? [no test files];--update-baselinecountedokonly, so the command meant to
prevent drift wrote 31 against a measured 60. A suite losing 29 of 60 packages would
still have reported PASS. Both now share onecount_packages(), verified by
control: floor 200 → FAIL, floor 60 → PASS. A check that cannot fail is not a check.testTimeout: 15000was too tight for a suite grown to 54 files / 683 tests.
user.typeanduser.clickare real timers, so a test killed mid-interaction
leaves the submit callback half-called and the next assertion sees it called twice —
“expected 1, got 2” reads as a logic bug and is not one. Raised to 60000; the same
tests pass 55/55 in isolation and 683/683 in the suite.