-
Notifications
You must be signed in to change notification settings - Fork 0
Readiness Audit
Standing structure for offering fork work back to chilli-axe/mpc-autofill
one self-contained chunk at a time, without ever sending an omnibus.
This document is a survey, not an action — nothing here has touched
upstream's repo. Phase 2 (cutting actual branches) happens only after the
owner picks specific chunks off the ladder below.
Upstreaming continues at minimal investment — this audit satisfies
docs/infrastructure.md's check-in requirement, and the frontend-direction
question (whether chilli-axe is actually dropping the Node.js frontend)
goes to the maintainer directly via a P.S. on an unrelated upcoming
message, rather than being speculated on here. Full Phase-2 scope waits
on that answer.
One branch cut now, as pattern-proof: upstream-feat-local-file-source
(Tier 1 #2 below), chosen for best survival odds if the frontend answer
turns out to matter — it's 100% backend, no frontend surface at all.
Pushed to origin (this fork) at commit 93874645, cut from
upstream/master at c3d10253. Pre-commit (ruff/isort/black/mypy) green
in the extracted context; test coverage hand-verified where the sandbox's
missing Docker/Postgres/ES made the real test suite uncollectable. Draft
PR description at
drafts/upstream-feat-local-file-source.md.
Not opened anywhere — sits ready until the owner sends it.
Card DOM API (Tier 1 #1) and the dead-image/error-states pairing (Tier 1 #4+#5) are deferred, not dropped — next up once the frontend question is answered, since both are frontend chunks and their value depends on there still being a Node.js frontend for upstream to accept changes into.
CI added for the ladder, 2026-07-18 (while dormant, per a separate
request): .github/workflows/upstream-branch-verification.yml and
upstream-drift-monitor.yml, detection-only, described in full in
conventions.md's "CI for the ladder" section. Neither fires for real
until this branch merges to master (GitHub only schedules workflows
that exist on the default branch) — docs/upstreaming/drift-log.md is
seeded by hand in the meantime with the same computation the workflow
will run.
git diff upstream/master origin/master (two-dot, tree-level) is the
only diff form that works here — there is no merge-base between the
upstream remote (chilli-axe/mpc-autofill) and this fork's origin
history as normally cloned, because the drives.csv history rewrite (see
docs/infrastructure.md) scrubbed a leaked-secrets range. A three-dot
diff or A..B commit range silently returns nonsense (or nothing) against
that gap.
Two gotchas hit while producing this audit, both worth carrying forward:
-
Shallow-clone false alarm. A shallow clone (the default for this
session) makes the merge-base gap look total —
git cat-file -ton every SHA indocs/upstreaming/vote-system.mdfails,git merge-basereturns nothing, and it reads exactly like the history-rewrite incident happened again. It didn't.git fetch --unshallow origin(fetching either remote unshallows the shared object store) restores the real picture: merge-base is3c717d2a(matching vote-system.md's 2026-07-13 finding exactly), every historical SHA resolves, andupstream/masterhas moved exactly 2 commits past that point as of 2026-07-18. Any future session doing extraction work mustgit fetch --unshallowbefore trusting a "no merge-base" or "SHA not found" result as real — it's much more likely to be this artifact than a second history-rewrite incident. -
upstream/mastergenuinely does move. 2 commits in 5 days (2026-07-13 → 2026-07-18) at the observed rate, mixed dependabot and human PRs. Re-verify merge-base and re-diff the target file set immediately before cutting or updating any extraction branch — don't trust a stale check.
The one PR of ours already merged upstream: #467, "Fix
toSearchable to not strip mid-string 'the'" (completes backend PR
#460), merged 2026-07-18 by maintainer Nicholas de Paola. Full
characterization in §6 / conventions.md — it's the house style every
upstream/* branch should match.
Standing risk, foregrounded per docs/infrastructure.md:
upstreaming is currently deprioritized — chilli-axe has signaled
plans to drop the Node.js frontend, which could waste further
upstreaming effort. This audit is the "checking in first" the doc asks
for; it does not override it. Nothing below should be sent without the
owner re-confirming that's still worth doing, chunk by chunk.
Three independent passes: backend (MPCAutofill/, schemas/), frontend
(frontend/), and everything else (CI, docker/, image-cdn/,
desktop-tool/, root config) + conventions mining. Total diff size:
MPCAutofill/ +20,058/−439 (105 files), schemas/ +584/−2 (44 files,
all net-new), frontend/ +14,906/−2,594 (150 files), everything else
+6,352/−61 (38 files). Migrations 0001–0049 are byte-identical to
upstream; everything from 0050 on (0050–0064, 15 migrations) is
fork-only.
Legend — Value: (a) useful to every instance, (b) useful to forks like us, (c) ours-only. Risk: extraction/entanglement risk if cut into its own branch.
Provenance column added per the owner's license-provenance amendment
(§9) — own-work (net-new fork code, no upstream equivalent) or
upstream-GPL3 (a modification/extension of an existing upstream file,
same license, same repo tree — not a third-party import). See §9 for the
one-time external-origin sweep this ladder itself is exempt from (none of
these 11 chunks involve code from outside the fork/upstream tree).
| Chunk | What | LOC | Value | Risk | Depends on | Provenance |
|---|---|---|---|---|---|---|
| A. Printing-tag vote system (core) | Weighted-consensus engine (AbstractWeightedVote, VoteSource) + CanonicalPrintingMetadata/CardPrintingTag, printing-candidate search, ES re-rank hook |
~700 (migrations 0050–0051) |
(a) | Low | none — foundation | own-work |
| B. Artist + tag weighted voting, unified queue | Extends A to artist/tag attributes, 2/voteQueue/
|
~1,300 (migrations 0052–0055, 0057) |
(a) | Medium | A | own-work |
| C. Tag taxonomy (Stage 3) | Seeded Tag rows + fuzzy filename matching, display_name split |
~500 (migration 0056) |
(b) | Low | none (functional dep on B) | upstream-GPL3 (extends upstream's existing Tag model) |
| D. Unified question feed |
2/questionFeed/, single prioritized review stream replacing the 3-tab switcher |
~600 | (a) | Medium | A, B, F | own-work |
| E. Moderation layer | Discord OAuth, privileged-vote co-sign gate, CardReport, Reports/Drives admin queue |
~1,300 (migrations 0057–0059) |
(b), mechanism only | Medium-high | A, B, C | own-work |
| F. Deductive printing-tag backfill | Logic-only vote casting (D1/D2 tiers) for logically-entailed printings | ~600 | (a) | Low | A, C | own-work |
| G. Local OCR/phash identification pilot | Tesseract + perceptual-hash + fallback engines, run-cohort revocability |
~6,700 (migrations 0060–0064) |
(a) with caveats — pilot-status, fork-tuned constants | High | A, F, I | own-work (uses the real pytesseract/imagehash PyPI deps, not copied algorithm code — see §9) |
H. LOCAL_FILE source type |
Real implementation of upstream's already-anticipated stub; local-disk cataloging + path-traversal-safe image serving | ~320 | (a) | Low | none — most self-contained chunk in the diff | upstream-GPL3 (completes a stub upstream's own schema/enum already defined) |
| I. Image-CDN backend fetch helper |
image_cdn_fetch.py, server-side full-res fetch for OCR/hashing |
~77 | (c) as written | Medium | Worker/R2 infra (out of scope) | own-work |
| J. Decklist printing-aware line formatting |
format_decklist_line() appends (SET) NUM when the import API gave printing data |
~50 (entangled in a 107-line file diff) | (a) | Low, but needs hunk-split | none | upstream-GPL3 (edits an existing upstream file) |
| K. Ours-only operational settings | Sentry removal, CORS branding, Discord/session-cookie env wiring | interleaved in settings.py
|
(c) | N/A — strip, don't port | — | upstream-GPL3 (edits upstream's existing settings.py) |
Full per-chunk detail (files, tests, exact dependency chains,
entanglement notes) is in the backend research pass; the table above is
the actionable summary. One correction to the original brief: "PDF
layout backend support" and "XML 2.0 backend support" do not exist —
both are 100% frontend-only (confirmed: zero xml matches anywhere in
MPCAutofill/'s diff).
Same Provenance convention as §1.1. IV.5 is the one row that's genuinely mixed — the ordering-tab logic is own-work, but its flag icon assets are real external-origin content, already found and fully triaged by §9's sweep (not a new finding this table introduces).
| Chunk | What | LOC | Value | Risk | Depends on | Provenance |
|---|---|---|---|---|---|---|
I.1 PDF layout extraction (computeLayout) |
Pure, side-effect-free page-layout math pulled out of render components | 336, both new files | (a) | Very low | none | own-work |
| I.2 WYSIWYG page preview | Canvas preview using I.1, no real PDF render needed | 541 + pdfjs-dist build step |
(a) | Low | I.1 | own-work |
| I.3 PDF dead-image blocking | Blocks/warns instead of silently shipping blank cards on a failed image fetch | ~739 | (a) | Low | none | upstream-GPL3 (modifies existing image-fetch/render logic) |
| I.4 XML 2.0 export + decklist round-trip | Versioned, backward-compatible XML with optional printing data; stripFoilMarker import fix |
~360 | split (a)/(b) | Low (format/foil-fix) / Medium (suffix payload) | suffix payload needs A/D | upstream-GPL3 (extends upstream's existing XML export) |
| II.1 Grid-selector UX/a11y pass | Keyboard nav, modal autofocus fix, mobile filters default | 285 | (a) | Very low | none | upstream-GPL3 (modifies the existing grid-selector component) |
| II.2 Card image error/loading states | "Still loading…" hint, styled error placeholder replacing a harsh black 404 PNG — the display-side of dead-link handling | 404 | (a) | Very low | none | upstream-GPL3 (modifies the existing Card component) |
| II.3 Card DOM API |
data-card-* attributes + mpc:card-selected event, additive/generic |
~300 | (a) | Very low | none — already flagged "pending upstream" in fork memory | own-work |
| II.4 DeckbuilderConfirmAffordance | In-context printing-confirm badge in the editor | 650 | (b) | High | IV.3, I.4, A | own-work |
| II.5 General UI/mobile/a11y polish | Mobile stacking, scroll-chain fix, a11y sweep | ~424 | (a) | Low (verify Navbar.tsx isolation) |
none | upstream-GPL3 (modifies existing components) |
| III.1–III.8 "What's That Card?" frontend | QuestionFeed, PrintingTagPicker, attribute voting/chips, reporting, moderation UI, /whatsthat page |
~6,300 | (b)/(c) mixed | High | A, B, C, D, E | own-work |
| III.2 questionFeed count-semantics fix | The specifically-requested candidate — confirmed real historically (a0e2aa08), but fully subsumed into III.1's current 970-line file, not separable today
|
n/a now | (b)/(c), inherits III.1 | N/A — would need reconstruction from git history if pursued alone | III.1 | own-work |
| IV.1 Google Drive "Save PDF to Drive" | Write-scoped OAuth flow, independent of the existing read-only picker | 174 | (a) | Very low | none | own-work |
| IV.2 Search filters | Mature-content toggle (generic) + resolved-attribute filters (needs backend) | 511 | split (a)/(b) | Low / Medium | ResolvedAttributeFilter needs A/D | upstream-GPL3 (modifies existing filter UI) |
| IV.3 API error surfacing + toasts | Real backend error messages + friendly 429 handling | 250 | (a) | Low | none | upstream-GPL3 (modifies existing API-error handling) |
| IV.4 Keyrune set-icon rendering | Real MTG expansion-symbol glyphs via icon font | ~115 | (a) | Very low | none | own-work (rendering code only — draws on the real keyrune npm package as a normal, gitignored install-time dependency; not the same thing as §9's finding of a second, separately-vendored copy of keyrune's font+license in the backend OCR pilot) |
| IV.5 Print-export ordering tabs + flags | NotMPC/PringlePrints tabs, vendored flag SVGs | 227 | (c) unambiguously | High (vendor content) | none | own-work (tab logic) + external (flag SVGs: lipis/flag-icons, MIT — §9 finding, already attributed in-repo) |
| IV.6 Telemetry/Sentry removal | Deletes fork's own error-tracking opt-out | −64 | (c) | None to extract | — | upstream-GPL3 (removes upstream's own integration) |
| IV.7 Branding & site identity | About page, logo, footer, nav copy | ~148 | (c) | N/A | — | own-work (original identity content; no external-origin assets found here per §9) |
| IV.8 Support-modal removal | Deletes upstream's donate/support-developer modals | −180 | (c) | None | — | upstream-GPL3 (removes upstream's own modals) |
| IV.9 Build/tooling & test-infra | Mostly ours-only config; one candidate: msw+playwright patch |
~110 + lockfile | mostly (c), one (a) | Low | verify upstream has same dep pair first | upstream-GPL3 (modifies existing config) |
cloudflare-static-site/, github-release-reverse-proxy/, and every
root config file (readme.md, pyproject.toml, mypy.ini,
.prettierignore, .python-version, uv.lock) are byte-identical
to upstream — zero drift, nothing to reconcile there. All branding lives
in frontend/.
| Chunk | What | Value | Risk | Provenance |
|---|---|---|---|---|
restart: unless-stopped on all 5 compose services |
Host-reboot resilience, live-verified with a real reboot test | (a) | Very low | upstream-GPL3 (edits existing compose files) |
Postgres/ES bound to 127.0.0.1 not 0.0.0.0 |
Security hardening, stops exposing DB/search ports to the internet | (a) | Very low | upstream-GPL3 (edits existing compose files) |
nginx Host/X-Forwarded-Proto header fix |
Fixes nginx's proxy_pass defaulting Host to the upstream container's own name, breaking absolute-URL construction behind a reverse proxy |
(a) (the 2-line header fix specifically) | Very low | upstream-GPL3 (edits existing nginx config) |
| image-cdn full-tier rate limiter + CORS fix | The "client-side rate pacer" from the original brief — it's a Cloudflare Worker chunk in image-cdn/, not frontend/. Adds real rate limiting + an intentional (not accidental) CORS header for lh4.googleusercontent.com full-res fetches |
(a) | Low | own-work |
desktop-tool PIL.Image → PILImage TYPE_CHECKING rename |
Avoids an import-shadowing footgun | (a) | Trivial | upstream-GPL3 (edits an existing upstream file) |
.dockerignore |
Stops uploading the whole repo (incl. gigabyte+ node_modules) as Docker build context |
(a), minus one .claude line |
Low | upstream-GPL3 (edits an existing upstream file) |
| CI mypy numpy-pin lesson | "An unpinned transitive dep can resolve differently in CI's mypy sandbox than local dev" — a portable insight, not a standalone diff | (a) as a lesson only | N/A alone | N/A — no diff, nothing to classify |
GIT_SHA build-arg baking + staleness gate |
Detects a deployed image running stale migrations | (b) | Medium — split across Docker + backend | own-work |
entrypoint.sh: always migrate, drop blocking first-boot catalog bootstrap |
Real behavior change — a fresh instance no longer auto-populates on first boot | (b) | Medium-high, must be opt-in not a silent default flip | upstream-GPL3 (edits upstream's existing entrypoint.sh) |
GA/Sentry removal from CI, GH Pages deploy pipeline, docker-compose.prod.yml env branding, test-backend dropped from build-frontend's needs |
Fork hosting/telemetry choices | (c) | N/A | upstream-GPL3 (edits existing upstream CI/deploy files) |
web-ci.yml's build-frontend job is currently dead weight, kept for rebase compatibility. It (and the
publish-frontend-to-github/publish-frontend-to-cloudflare composite actions it could feed) are upstream-native
and have no live consumer in this fork today: deploy-frontend.yml does its own independent next build and
actions/upload-pages-artifact/actions/deploy-pages for real GitHub Pages hosting, never touching
build-frontend's cached frontend/out artifact or either publish action. Two options exist — port our real
deploy (deploy-frontend.yml) onto the upstream-native build-frontend/publish-frontend-to-* job shape, or
leave the divergence as-is — neither has been decided; this row just records the fact so a future rebase doesn't
mistake the dead job for something safe to delete.
graph TD
A["A: Printing-tag vote<br/>system (core)"] --> B["B: Artist/tag voting<br/>+ unified queue"]
C["C: Tag taxonomy"] -.functional.-> B
A --> F["F: Deductive backfill"]
C -.functional.-> F
B --> D["D: Question feed"]
F --> D
A --> E["E: Moderation layer"]
B --> E
C --> E
A --> G["G: Local OCR/phash pilot"]
F --> G
I["I: image_cdn_fetch"] --> G
H["H: LOCAL_FILE source"] -.optional hook.-> I
A --> III["III.1-8: What's That Card?<br/>frontend cluster"]
B --> III
C --> III
D --> III
E --> III
IV3["IV.3: API error surfacing"] --> II4["II.4: DeckbuilderConfirmAffordance"]
I4["I.4: XML 2.0 (suffix payload)"] --> II4
A --> II4
D --> II4
A --> IV2b["IV.2b: ResolvedAttributeFilter"]
D --> IV2b
II3["II.3: Card DOM API"] -.sibling helper, no dep.-> III
I1["I.1: PDF layout<br/>(computeLayout)"] --> I2["I.2: WYSIWYG preview"]
classDef ready fill:#2d6a4f,color:#fff;
classDef risky fill:#9d0208,color:#fff;
class II3,H,I1,I2,II2,II1,IV1,IV4,I3 ready
class G,III risky
Roots with zero dependency on any other fork-only code (green in the
graph): Card DOM API (II.3), LOCAL_FILE source (H), PDF layout
extraction (I.1), PDF dead-image blocking (I.3), grid-selector pass
(II.1), card image error states (II.2), Google Drive Save-to-Drive
(IV.1), Keyrune icons (IV.4), all the CI/tooling one-liners. These are
the ones a maintainer can review with zero context on the rest of the
fork.
Chunk A is the true foundation of everything "What's That Card?"-shaped (B through G, III.1–8 all trace back to it) — proposing it is the highest-leverage single move if the vote system is ever pitched at all, but it's also the biggest single non-trivial-review-size chunk in that family (~700 LOC plus the search/ES hooks it wires into).
Ranked by (upstream value × extraction ease). Tier 1 chunks need no other fork-only code, have no fork branding, and are small enough to review as one PR each — these are what Phase 2 should draw from.
- Card DOM API (frontend II.3) — additive, generic, zero deps, already informally flagged as "pending upstream" in this fork's own working notes before this audit even started.
-
LOCAL_FILEsource type (backend H) — completes a stub upstream's own schema/enum already anticipated; the most self-contained chunk in the whole diff; 17 tests need no network/credentials. -
PDF layout extraction /
computeLayout(frontend I.1) — pure refactor-for-testability, two new files, nothing else touched. - Card image error/loading states (frontend II.2) — the display half of dead-link handling; replaces a harsh black 404 PNG with a real placeholder + slow-load hint.
- PDF dead-image blocking (frontend I.3) — a real silent-data-loss bug fix (blank cards shipped to a print vendor with no warning); strong "why this matters" story for a reviewer.
- Grid-selector UX/a11y pass (frontend II.1) — keyboard nav + autofocus + mobile-default fixes, all correctness bugs.
- Google Drive "Save PDF to Drive" (frontend IV.1) — a generically valuable capability extension to a feature upstream already has.
- Keyrune set-icon rendering (frontend IV.4) — real MTG set-symbol glyphs, zero fork data shape required.
-
stripFoilMarkerdecklist-import fix (embedded in frontend I.4, needs extracting from the larger XML-2.0 commit) — tiny, generic bug fix for foiled-card collector-number parsing. -
Docker/infra one-liners:
restart: unless-stopped, Postgres/ES bound to127.0.0.1, the nginxHost/X-Forwarded-Protofix,desktop-tool'sPIL.Imagerename. All trivial, all universal. -
image-cdn full-tier rate limiter (the actual "rate pacer" — it's
in
image-cdn/, notfrontend/, correcting the original brief) — self-contained Worker module + 6 tests, fixes an accidental (not intentional) CORS pass-through as a side effect.
-
WYSIWYG PDF preview (I.2) — needs its
pdfjs-dist/postinstall build step to travel with it. - API error surfacing + friendly 429 toasts (IV.3).
-
General UI/mobile/a11y polish (II.5) — isolate
Navbar.tsx's branding lines first. -
.dockerignore— strip the one.claudereference. - Decklist printing-aware line formatting (backend J) — clean content, needs hand-splitting out of a file that also carries two unrelated changes.
- Mature-content filter toggle only (IV.2, not the resolved-attribute half) — pure UI surfacing of an existing setting.
-
msw+playwrightpatch (IV.9) — verify upstream'spackage.jsonactually has the same dependency pair before proposing. - XML 2.0 format mechanism, minus the canonical-printing-suffix payload (I.4 partial) — versioned/backward-compatible export format on its own merits.
-
Printing-tag vote system: Chunk A (+ optionally B, D) — the
single most upstream-pitchable big idea in this fork (community
disambiguation of which Scryfall printing a catalog image depicts),
but it's feature-sized, not quick-win-shaped, and its frontend half
(Cluster III) is ~6,300 LOC that doesn't work without it. This is
docs/upstreaming/vote-system.md's territory — see §5 below for that doc's current accuracy. - Moderation layer (E) — the mechanism (privileged-vote co-sign, pluggable moderator grant) generalizes; the Discord-specific wiring doesn't, and would need reframing as provider-agnostic OAuth first.
-
GIT_SHAbuild-arg + staleness gate (CI + backend) — needs the Docker and backend halves coordinated in one pitch. -
entrypoint.shmigrate-always change — a real behavior change (drops first-boot auto-catalog-population); must be pitched as opt-in, not a silent default flip, or it breaks upstream's documented quick-start experience.
Branding (About page, logo, footer, nav copy), Discord OAuth specifics,
PringlePrints/NotMPC ordering tabs (vendor-specific business content),
Sentry/GA telemetry removal, support-modal removal, the GitHub Pages
deploy pipeline, docker-compose.prod.yml env/branding additions,
CORS_ALLOWED_ORIGINS swap, dropping test-backend from CI (fork is
missing upstream's secrets, not a bug).
Local OCR/phash identification pilot (backend G, ~6,700 lines). Real
and reusable in mechanism (never-resolve-alone gate, checkpointing,
run-cohort revocability via PilotRunLedger), but explicitly
pilot-status: ~12.8-day single-process full-catalog runtime, tuning
constants (fetch DPI, crop boxes, phash thresholds) empirically derived
from this fork's own small sample images, a RunPython data migration
splitting production vote data (0060), and the heaviest shared-file
entanglement of any chunk. Would need splitting into several
independently-reviewable PRs (OCR engine / phash engine / fallback
engine / orchestration) and a re-validation pass against a different
catalog's images before it's honestly presentable — not a Phase-2
candidate.
Federation verdict-exchange consumer component (subscriber-side:
verdict table, phash tooling, signature verification, agreement badge —
docs/federation-v1.md). Not large or risky like Tier 5 — the opposite,
actually: deliberately dependency-free, no vote/consensus/moderation
stack required, the single most upstream-shaped idea in the whole
federation program. It sits here instead of Tier 1 for a different
reason than size or entanglement: there's no live peer to subscribe to
yet, and this fork's own v1 posture is publisher-only (see
federation-v1.md's "Participation modes"), so there's nothing real to
demo or extract against. Revisit once federation has an actual second
instance on the other end — not before, and not on a schedule.
-
questionFeed count semantics — real (fixed
a0e2aa08, backendget_remaining_estimate()/QuestionFeedCounts), but not separable in the current tree: fully absorbed intoQuestionFeed.tsx's current 970-line state. Demoted off the ladder; would need reconstruction from git history if pursued as a standalone fix, and only makes sense bundled with a Chunk D pitch anyway (question feed doesn't exist upstream to patch). -
CI/tooling hygiene fixes — confirmed, several genuinely good ones
found beyond what the brief named (restart policy, port binding, nginx
header fix,
PIL.Imagerename,.dockerignore, the mypy-numpy-pin lesson). - Dead-link handling — confirmed real and split cleanly into a frontend display half (II.2) and a PDF-export half (I.3), both Tier 1.
-
PDF layout extraction (
computeLayout) — confirmed, Tier 1. -
Client-side rate pacer — does not exist in
frontend/; the actual feature is the image-cdn Worker's rate limiter (image-cdn/, Cloudflare Workers code, not the Next.js app). Real and Tier 1, just mis-scoped in the original ask. - XML 2.0 (+ import round-trip) — confirmed, real, splits into a generic format mechanism (Tier 2) and a fork-dependent payload (bundled with the vote system, Tier 3).
-
Not previously on the ladder, found here: Card DOM API,
LOCAL_FILEsource type, Google Drive Save-to-Drive, Keyrune set-icon rendering, the Docker/nginx one-liners, andstripFoilMarker— several of these (Card DOM API,LOCAL_FILE) are arguably stronger candidates than anything on the original list.
Verified independently against current origin/master: accurate for
what it says it covers (the ordered commit list, the
PrintingTagQueue.tsx/printingQueue.tsx starburst-entanglement
warning, the fork-content check) but materially incomplete as "the
vote system" now means on current master — it predates:
-
Chunk E (moderation layer) entirely (migrations
0057–0059postdate its last-covered migration,0055). -
Chunk D (question feed) — the doc still describes
2/voteQueue/(Chunk B) as the redesign's endpoint;2/questionFeed/has since superseded it as the primary review surface. - Chunks F and G (~7,300 combined lines: deductive backfill + the local OCR/phash pilot) — not mentioned at all.
- Its own §5 re-verification command (
git merge-base upstream/master origin/mastershould show 0 commits ahead) needs the two-dot-diff method substituted for the range-diff it suggests, and the shallow-clone caveat from §0 above applied before trusting the result. - Its specific cherry-pick recipe for
PrintingTagQueue.tsxnow targets a file that no longer exists — that logic was redistributed intocardPanel.tsx/whatsthat.tsx/QuestionFeed.tsxduring the queue redesign. The doc's warning (don't cherry-pick that file's history commit-by-commit) is still directionally correct; its recipe needs redoing against the new files if ever acted on.
Recommendation: keep the doc as-is for the slice it accurately covers (Chunks A/B), but add a header note pointing here for anything past 2026-07-13, so a future reader doesn't assume it's the complete picture.
Full write-up in docs/upstreaming/conventions.md. Headline findings:
- PR #467 (ours, merged) is one commit, plain imperative title, root-cause explanation + concrete before/after example in the body, references the completed companion PR (#460) by number, updates its test in the same commit. No conventional-commits prefix. This is the template to match.
- Upstream has no squash-merge policy and no enforced commit prefix — PR size is highly variable (2-file fixes to 3,000+-line overhauls), but every sampled PR (13 non-dependabot merges inspected) shipped test-file changes alongside the behavior change. None shipped without tests.
- PR bodies are hand-written, not AI-generated, per the maintainer's
explicit ask across all 5 of this fork's upstream PRs so far — the
Co-Authored-By: Claude Sonnet 5commit trailer is the accepted AI-assistance disclosure, not PR body text. - Full-stack features are commonly split into sibling backend/frontend PRs (e.g. #441/#442, merged ~3 hours apart) rather than one combined PR — worth replicating for any full-stack fork feature.
- Tool/hook versions in
.pre-commit-config.yamlare already identical between fork and upstream — no reconciliation needed there. The only drift is the mypy hook'sadditional_dependenciespin list, which should carry only the pins a given extraction branch's own feature needs, and must not silently dropsentry-sdkunless the same PR is also proposing to remove upstream's own telemetry.
Tracking: keep the existing upstream remote
(https://github.com/chilli-axe/mpc-autofill.git), fetched on demand
(git fetch upstream master) rather than on a fixed schedule — chunks
sit ready-and-waiting until the owner personally sends one, so there's
no value in syncing more often than "right before cutting or updating a
branch." Optionally mirror it as a local upstream-master branch
(git branch -f upstream-master upstream/master after each fetch) purely
for discoverability in git branch/git log --graph output; not load-bearing.
Per-chunk branches: this fork already has a naming convention in
active use — upstream-fix-frontend-searchable-the,
upstream-fix-image-cdn-cors, upstream-fix-pdf-canvas-preview,
upstream-fix-pdf-eager-wasm-load, upstream-fix-pdf-thumbnail-worker-route
all exist as branches on origin today. Extend it rather than replace
it: upstream-fix-<slug> for bug fixes (as now), upstream-feat-<slug>
for net-new capability chunks (e.g. upstream-feat-local-file-source,
upstream-feat-card-dom-api, upstream-feat-save-pdf-to-drive).
Cutting a branch: per docs/infrastructure.md's existing workflow —
a separate git worktree add <path> upstream/master -b <branch>, never
a plain checkout in the main tree (keeps the fork's 40+ fork-specific
commits from ever being reachable in the same working copy as upstream
history). Cherry-pick the specific commit(s) when the original message
ports cleanly; hand-reapply against upstream's current tree when it
doesn't (references "our fork"/"our master", etc. — exactly the #467
precedent).
Before every push, not just the first cut: git fetch upstream master, re-run git merge-base upstream/master origin/master
(unshallow first if needed — §0), git diff upstream/master <branch> to
confirm the branch contains nothing but the intended change, rebase onto
the new tip if it moved, re-run pre-commit run --all-files plus
whichever upstream test suite(s) the change touches.
Maintenance cost: observed drift is low-frequency for human-authored, review-relevant changes — 2 commits in the 5 days between vote-system.md's last check and this one, and upstream's own recent-PR sample skews heavily toward dependabot bumps that rarely touch the same files a fork extraction would. Realistic cadence: check immediately before cutting a new branch, and again immediately before opening/updating a PR for one already cut — not a standing sync job.
Superseded by the owner's actual 2026-07-18 decision, recorded at the top
of this document ("Phase 2 status"): one branch cut
(upstream-feat-local-file-source), Card DOM API and the dead-image/
error-states pairing deferred pending the maintainer's frontend-direction
answer. The original pre-decision reasoning (LOCAL_FILE source's
zero-dependency, backend-only shape gave it the best survival odds
regardless of how that answer lands) is preserved here since it's why
that specific chunk was picked first among Tier 1's near-equally-ready
options.
Every row across §1.1–1.3's three tables now carries a Provenance
column: own-work (net-new fork code) or upstream-GPL3 (a
modification of an existing upstream file — same repo tree, same
license, not a third-party import). 41 rows total (11 backend + 20
frontend + 10 CI/tooling), not the 27 the commission estimated — stated
plainly rather than force-fit to match; every row was individually
classified from its own already-recorded "What" description, not
independently re-diffed line-by-line (that would be its own multi-hour
pass — flag any misclassification for correction).
Uniform, as expected, with exactly one flagged exception: no ladder
chunk involves code copied in from outside the fork/upstream tree — IV.5
(print-export flag icons) is the one row that's genuinely mixed, and
that's a pre-existing, already-attributed finding (the tab logic is
own-work; the flag SVG assets are lipis/flag-icons, MIT), not a new
problem this pass introduced.
A separate, one-time sweep of the WHOLE repo (not just these 41
ladder rows — everything under MPCAutofill/, frontend/, schemas/,
image-cdn/, desktop-tool/, cloudflare-static-site/,
github-release-reverse-proxy/, docker/, .github/) for vendored
files, copied snippets, and dependency-adjacent copies found 4 real
external-origin items — not "expected clean, confirmed clean": two
of the four are missing full license-notice compliance, a real,
previously-unnoticed gap this pass closes. Full findings, the
PROTECTED CORE policy, the absorption protocol, and the disclosure
mechanics this amendment also ships are in the new companion doc,
license-provenance.md — kept separate from
this file since it's forward-looking policy + a point-in-time sweep
result, not chunk-decomposition data, matching how conventions.md
already sits alongside this doc for a related-but-distinct concern.
Understanding the system
- Overview
- Documentation-Process
- Theory
- Identification-Pipeline
- Pipeline-Fidelity-Gate
- Federation-v1
- Vote-System
- Readiness-Audit
- License-Provenance
- Upstreaming-Conventions
- Drift-Log
- Upstream-Wiki-Drift
- Printing-Tags
- Catalog-Completion-Plan
- Moderation
- Card-DOM-API
- PDF-Generator
- Print-Export-Page
- Google-Drive-Connect
- Grid-Selector
- Image-CDN
- Local-File-Source
Using it
Operating it
Folded into other pages