#!/usr/bin/env python3
"""Hand-coded defect dataset for "Green but Wrong" -> data/defects.csv + data/defects.json.

Each row was coded from the primary source named in `source` (a commit message,
read with `git show -s`, or a committed document). Codes are defined in
../README.md and in Section 4 of the paper. Descriptions are short paraphrases;
no source code is reproduced.

Columns (pipe-separated below):
 id | product | title | category | severity | detection | red | green_over |
 author | attr_tier | intro | fix | source | path
"""
import csv
import json
import os

REPO = {"Bupples": "Bupples", "CareerOS": "careerapp", "WayClub": "WayClub",
        "Photoshoot": "photoshoot", "CodeAtlas": "GitSpark", "Adelante": "Adelante",
        "Planket": "planket", "HACKFAM": "hackathontinder"}

ROWS = r"""
B01|Bupples|Session member could rewrite authedUids via the self-leave rule branch (evict host, inject members)|AUTHZ|H|REVIEW|RV|U|claude|A|29796ba7|a6a5d9a7|docs/audits/SECURITY_AUDIT.md V1; a6a5d9a7 msg ("revert-checked")|D
B02|Bupples|DM photo Storage reads open to any signed-in user; the relaxing commit called the change safe|PRIV|H|REVIEW|N|Y|claude|A|4bc44b79|a6a5d9a7 fb6e549f|SECURITY_AUDIT.md V2; 4bc44b79 msg; a6a5d9a7 msg ("code-reviewed", harness Firestore-only)|D
B03|Bupples|Session doc readable by removed and banned members (lastActivity leak)|PRIV|M|REVIEW|T|U|agent|C||a6a5d9a7|SECURITY_AUDIT.md V8; a6a5d9a7 msg|D
B04|Bupples|usernames collection fully listable (directory enumeration)|PRIV|M|REVIEW|T|U|agent|C||a6a5d9a7|SECURITY_AUDIT.md V6|D
B05|Bupples|Non-owner could rewrite an open receipt's currency/merchant before finalise|MONEY|M|REVIEW|T|U|agent|C||a6a5d9a7|SECURITY_AUDIT.md V4|D
B06|Bupples|Turbo participant could forge host-only paidConfirmed on own claim|AUTHZ|M|REVIEW|T|U|agent|C||a6a5d9a7|SECURITY_AUDIT.md V5|D
B07|Bupples|Public displayName written with no length/type validation|ABUSE|M|REVIEW|T|U|agent|C||a6a5d9a7|SECURITY_AUDIT.md V7|D
B08|Bupples|Anonymous uids defeated per-uid AI quotas (denial-of-wallet)|ABUSE|M|REVIEW|U|U|agent|C||fb6e549f|SECURITY_AUDIT.md V12; fb6e549f msg|D
B09|Bupples|CSV export open to spreadsheet formula injection|ABUSE|L|REVIEW|U|U|agent|C||fb6e549f|SECURITY_AUDIT.md V19|D
B10|Bupples|Predictable settlement-proof path let an attacker pre-plant and block a payer's proof|ABUSE|M|REVIEW|U|U|agent|C||fb6e549f|SECURITY_AUDIT.md V10|D
B11|Bupples|Android allowBackup left at default true|PRIV|L|REVIEW|N|U|agent|C||fb6e549f|SECURITY_AUDIT.md V30|D
B12|Bupples|Cross-session balances read the raw wallet hint and could show money for a left/archived hangout|MONEY|H|REVIEW|U|U|agent|C||d8146f05|docs/audits/QUALITY_AUDIT.md B1; d8146f05 msg|D
B13|Bupples|Gifting spent the one irreversible free month on a single unconfirmed tap|LOGIC|H|REVIEW|U|U|agent|C||d8146f05|QUALITY_AUDIT.md B2; d8146f05 msg|D
B14|Bupples|Bio never saved: field missing from the server's public-profile allowlist; suite green at 2,582|CONTRACT|H|OWNER|T|Y|claude|A|fd9a04db|cfbaf5f9|cfbaf5f9 msg (test asserted a method with zero production callers)|D
B15|Bupples|Transfer defaults never refreshed for payers (one-shot cached provider)|STALE|M|OWNER|T|Y|agent|C||cfbaf5f9|cfbaf5f9 msg (how_to_pay_test overrode the provider under test)|D
B16|Bupples|Catch-all switch arms let a new frame compile and render nothing|UI|L|SELF|T|N|agent|C||cfbaf5f9|cfbaf5f9 msg|D
B17|Bupples|Reset hints did nothing while the toast claimed success|ERR|M|SELF|U|N|agent|C||cfbaf5f9|cfbaf5f9 msg|D
B18|Bupples|Widget carried a second money-left formula ignoring income and goals|MONEY|M|SELF|U|N|agent|C||cfbaf5f9|cfbaf5f9 msg|D
B19|Bupples|Dev Pro toggle guards used kDebugMode where the system used !kReleaseMode|BUILD|L|SELF|U|N|agent|C||cfbaf5f9|cfbaf5f9 msg|D
B20|Bupples|NUL bytes hid two source files from grep and misled an auditor into calling live code dead|BUILD|L|SELF|N|N|agent|C||cfbaf5f9|cfbaf5f9 msg|D
B21|Bupples|Bubble wake gate was dead code: any cosmetic id rode the per-vsync rebuild path forever|PERF|M|REVIEW|RV|N|agent|C||396b704|perf-rework-2026-07/COMPLETION_REPORT.md 1.1; 396b704 msg ("verified red on the old code")|D
B22|Bupples|Cached bubble still never re-armed after a radius ease (latched dynamic path)|STALE|M|REVIEW|RV|N|agent|C||396b704|COMPLETION_REPORT.md 1.1|D
B23|Bupples|Photo decode width keyed on the animated radius (~40 decodes per ease)|PERF|M|REVIEW|RV|N|agent|C||396b704|COMPLETION_REPORT.md 1.1|D
B24|Bupples|Seven provider families held route-scoped Firestore listeners for app lifetime|PERF|M|REVIEW|RV|N|agent|C||51d7f0b|COMPLETION_REPORT.md 1.2; 51d7f0b msg|D
B25|Bupples|Chat last-seen state not keyed per account (account switch inherited read state)|STALE|M|REVIEW|RV|N|agent|C||51d7f0b|51d7f0b msg|D
B26|Bupples|Receipt claim list showed a false all-unclaimed frame while claims loaded|ERR|M|REVIEW|RV|N|agent|C||51d7f0b|COMPLETION_REPORT.md 1.5; 51d7f0b msg|D
B27|Bupples|Direct-settlement solver re-ran three times for every hangout on any non-money churn|PERF|M|REVIEW|RV|N|agent|C||da91d91|COMPLETION_REPORT.md 1.3|D
B28|Bupples|requestsProvider missing its auth re-scope|STALE|M|REVIEW|RV|N|agent|C||da91d91|da91d91 msg|D
B29|Bupples|Widget payload stamped DateTime.now, re-rendering PNGs and burning OS reloads|PERF|M|REVIEW|RV|N|agent|C||da91d91|COMPLETION_REPORT.md 1.3|D
B30|Bupples|Activity feed built every money event eagerly with no cap|PERF|L|REVIEW|RV|N|agent|C||da91d91|COMPLETION_REPORT.md 1.3|D
B31|Bupples|FCM token registration ran three times per boot|PERF|L|REVIEW|RV|N|agent|C||524b9a3|COMPLETION_REPORT.md 1.4|D
B32|Bupples|Push taps and deep links stacked duplicate copies of the top screen|UI|L|REVIEW|RV|N|agent|C||524b9a3|COMPLETION_REPORT.md 1.4|D
B33|Bupples|Money taps could fire twice; Confirm-all double-tap ran 2N writes|CONC|M|REVIEW|RV|N|agent|C||a50fc0c|COMPLETION_REPORT.md 1.5; a50fc0c msg|D
B34|Bupples|System back killed a quota-counted receipt scan mid-flight|ERR|M|REVIEW|RV|N|agent|C||a50fc0c|COMPLETION_REPORT.md 1.5|D
B35|Bupples|Avatar warmup decoded 13 full-size photos into cache keys no render path matched|PERF|M|REVIEW|RV|N|agent|C||913fb33|COMPLETION_REPORT.md 1.6|D
B36|Bupples|Cover photos cached by session id served a stale cover|STALE|L|REVIEW|RV|N|agent|C||913fb33|COMPLETION_REPORT.md 1.6|D
B37|Bupples|Edge-swipe back re-inflated the page (lost State) and leaked a settle listener per abort|UI|M|REVIEW|RV|N|agent|C||b7fbdc1e|b7fbdc1e msg; COMPLETION_REPORT.md 2|S
B38|Bupples|Shell pager answered a user drag with animateToPage, cancelling the gesture; tabs lost scroll|UI|M|REVIEW|RV|N|agent|C||8b49a89|8b49a89 msg|D
B39|Bupples|Frame pacer sorted up to ~36k samples per frame in release|PERF|L|REVIEW|RV|N|agent|C||f3b1928|f3b1928 msg|D
B40|Bupples|Friends flashed a zero count and first-run pitch while the stream was silent|ERR|L|REVIEW|RV|N|agent|C||2e38f85|COMPLETION_REPORT.md 1.5; 2e38f85 msg|D
B41|Bupples|Settings showed the email on both name and email lines|UI|L|UNK|N|N|agent|C||a6104acb|a6104acb msg|S
B42|Bupples|currentUidProvider cached the first uid forever, so writes failed with permission-denied after any auth change|STALE|H|UNK|T|N|claude|A|05587592|bd8f0843|bd8f0843 msg|S
B43|Bupples|Pip mascot eyes vanished in light mode|UI|L|OWNER|N|N|agent|C||ce87e3d0|ce87e3d0 msg (device-bug batch)|S
B44|Bupples|Money amounts ellipsised instead of shrinking to fit|UI|M|OWNER|N|N|agent|C||075b4b18|075b4b18 msg|S
B45|Bupples|assetlinks.json shipped a placeholder fingerprint, so Android App Links never verified|BUILD|M|UNK|N|N|agent|C||fd9223e8|fd9223e8 msg|S
B46|Bupples|Paywall showed the RM fallback price to users outside Malaysia|LOGIC|M|UNK|N|N|agent|C||1c677a72|1c677a72 msg|S
B47|Bupples|Android profile build failed silently; device kept reinstalling a stale APK|BUILD|M|OWNER|N|N|agent|C||c8dcd188|c8dcd188 msg|S
B48|Bupples|Turbo split rules readable by any signed-in user exposed payee bank details|PRIV|H|UNK|N|N|agent|C||a9c21fc8|a9c21fc8 msg|S
B49|Bupples|Blurred cover paint escaped its bounds as a coloured bar|UI|L|OWNER|T|N|agent|C||1fc40683|1fc40683 msg ("reported twice")|S
B50|Bupples|Receipt total ignored the bill-level discount|MONEY|H|SELF|T|N|agent|C||53c365a3|53c365a3 msg|S
B51|Bupples|Chooser row carried a hard-coded decorative tick never driven by state|UI|L|OWNER|N|N|agent|C||20b83e40|20b83e40 msg|S
B52|Bupples|A payment on a simplified-plan line vanished: money left the payer but no balance moved|MONEY|H|UNK|U|N|agent|C||09d09516|09d09516 msg|S
B53|Bupples|Pay sheet labelled the instant-transfer scheme as Malaysian for every region|LOGIC|M|UNK|T|N|agent|C||4e1a2499|4e1a2499 msg|S
B54|Bupples|Compact welcome pages not visible in the Android release build|UI|M|UNK|N|N|agent|C||7c002c01|7c002c01 msg|S
C01|CareerOS|Erasure left stored objects (uploads, exports, feed snapshot) behind|PRIV|H|REVIEW|RF|N|codex|B|8dee1a6|4afd29c|4afd29c msg ("each fix has a test that failed before it"); docs/PROGRESS.md session 3|D
C02|CareerOS|API keys (non-humans) could move a claim to verified or user_attested|AUTHZ|H|REVIEW|RF|N|codex|B|5f69c37|4afd29c|4afd29c msg; PROGRESS.md finding 2|D
C03|CareerOS|Verifying an email signed in whoever registered the address first (pre-registration takeover)|AUTHZ|H|REVIEW|RF|N|codex|B|f1dbae5|4afd29c|4afd29c msg; PROGRESS.md finding 3|D
C04|CareerOS|GitHub webhook redelivery attempted only once|ERR|M|REVIEW|RF|N|codex|B|0ecce08|4afd29c|4afd29c msg; PROGRESS.md finding 6|D
C05|CareerOS|Organisation installs could not complete after their webhook arrived (state consumed early)|LOGIC|M|REVIEW|RF|N|codex|B|0ecce08|4afd29c|4afd29c msg; PROGRESS.md finding 7|D
C06|CareerOS|Portable data export omitted owner tables|PRIV|M|REVIEW|RF|N|codex|B|0fc412a|4afd29c|4afd29c msg; PROGRESS.md finding 8|D
C07|CareerOS|Consent withdrawal and deletion did not retract website publications; failed uninstall blocked purge|PRIV|M|REVIEW|RF|N|codex|B|0fc412a|4afd29c|4afd29c msg|D
C08|CareerOS|Rejected-webhook logging unbudgeted; auth flows not limited per email|ABUSE|M|REVIEW|RF|N|codex|B||4afd29c|4afd29c msg|D
C09|CareerOS|Profile edits unpinned every resume entry; deleting an achievement left open suggestions|LOGIC|L|REVIEW|RF|N|codex|B|5f69c37|4afd29c|4afd29c msg; PROGRESS.md finding 15|D
C10|CareerOS|A storage-config test passed over a bug|TEST|L|REVIEW|RF|Y|codex|B|8dee1a6|4afd29c|4afd29c msg|D
C11|CareerOS|Queue: slow job stranded claimed siblings, lost lease did not abort, crash-looping jobs never died|CONC|H|REVIEW|RF|N|codex|B|a63a28a|a2994f9|a2994f9 msg ("each proven red first"); PROGRESS.md session 2 ("authored alongside implementation, rather than observed red first")|D
C12|CareerOS|Numeric gate tokenised ISO dates as YYYY-MM so the date rule never matched|UNITS|M|REVIEW|U|N|codex|B|8e4ebf1|7bcaf65|7bcaf65 msg; PROGRESS.md session 3|D
C13|CareerOS|Deleting a live record left orphaned public items|PRIV|M|UNK|RF|N|claude|A|87fb3d8|9b68d01|9b68d01 msg ("each fix has a test that fails without it")|C
C14|CareerOS|Withdrawing notification permission did not stop queued webhook messages|PRIV|M|UNK|RF|N|claude|A|87fb3d8|9b68d01|9b68d01 msg|C
C15|CareerOS|Two concurrent drafting runs created two achievements|CONC|M|UNK|RF|N|claude|A|7bcaf65|9b68d01|9b68d01 msg; PROGRESS.md session 5 (duplicate demo achievement)|C
C16|CareerOS|Per-email limiter counted correct sign-ins, letting anyone lock an owner out|ABUSE|H|REVIEW|RF|N|agent|C||10e0b55|10e0b55 msg ("all three fail against the original limiter")|C
C17|CareerOS|Passing NextRequest to the Request constructor made every dev-server sign-in a 500|CONTRACT|M|SELF|N|Y|claude|A|d4ce792|adad203|adad203 msg (test "does not reproduce the Next-runtime error")|C
C18|CareerOS|Public API limiter trusted the client-written forwarded hop (unbounded buckets)|ABUSE|M|REVIEW|RF|N|agent|C||3f1c178|3f1c178 msg|C
C19|CareerOS|Publication gates leaked private repository names and links, and minted numbers from titles (gate catch 14/22)|LOGIC|H|REVIEW|RF|Y|codex|B|8e4ebf1|70b84e6|70b84e6 msg (12 of 14 tests fail on old gates; old catch rate 0.64); PROGRESS.md session 5 (gate catch 14/14 before)|C
C20|CareerOS|Regex dotAll flag broke the web typecheck (pre-ES2018 target)|BUILD|L|GATE|N|N|claude|A|70b84e6|44d4884|44d4884 msg|C
C21|CareerOS|A stolen session cookie could mint API keys; password reset left keys working|AUTHZ|H|REVIEW|RF|N|agent|C||bea3642|bea3642 msg ("3 fail")|C
C22|CareerOS|Catch-all logging wrote error messages carrying bound query parameters (PII)|PRIV|M|REVIEW|RF|N|agent|C||ccf3212|ccf3212 msg ("fails against the old http.ts")|C
C23|CareerOS|On organisation installs every merged PR became a verified claim about the person|LOGIC|H|REVIEW|RF|N|claude|A|c749070|d9e1be3|d9e1be3 msg|C
C24|CareerOS|Request bodies read whole before the size check; proxy silently truncated uploads|ABUSE|M|REVIEW|RF|N|agent|C||6e0ab94|6e0ab94 msg ("both failed before")|C
C25|CareerOS|safe-fetch treated IPv4-compatible IPv6 as public (SSRF)|ABUSE|M|REVIEW|T|N|codex|B|8dee1a6|f74504a|f74504a msg; PROGRESS.md session 2 (pinned safe-fetch)|C
C26|CareerOS|Only five common passwords were rejected|ABUSE|L|REVIEW|U|N|codex|B|f1dbae5|84d6bcf|84d6bcf msg|C
C27|CareerOS|Co-author email addresses stored verbatim in evidence text and exports|PRIV|M|SELF|RV|N|claude|A|c749070|83f8442|83f8442 msg ("4 mutations each turned them red"); PROGRESS.md session 8|D
C28|CareerOS|Left-out channel faded to 55% opacity failed contrast|UI|L|GATE|RF|N|claude|C||05cb627|PROGRESS.md session 6 ("seen failing first")|D
C29|CareerOS|Light slate token at 4.31:1 on the page colour|UI|L|GATE|RF|N|claude|C||05cb627|PROGRESS.md session 6; 05cb627 msg ("token test red on old slate")|D
C30|CareerOS|Acceptance suite: delivery sweeper untested, M6 untested, 9 of 15 criteria partly covered|TEST|M|SELF|RV|Y|agent|C||29c677a|PROGRESS.md session 7 table; 29c677a msg|D
C31|CareerOS|db:verify passed while required schema objects were absent|TEST|M|REVIEW|U|Y|codex|B||(completion pass)|PROGRESS.md "Completion-pass verification and review" #1|D
C32|CareerOS|Stalled storage call could hold the profile lock indefinitely|CONC|M|REVIEW|RF|N|codex|B|||PROGRESS.md completion pass #2 ("first failed, then ... passed")|D
C33|CareerOS|Signup responses disclosed whether an account existed|PRIV|M|REVIEW|U|N|agent|C||(completion pass)|docs/DECISIONS.md ADR-025|D
C34|CareerOS|Public cache could serve a removed item for a day (stale-while-revalidate 86400)|STALE|M|REVIEW|U|N|agent|C||(completion pass)|DECISIONS.md ADR-025|D
C35|CareerOS|Profile rename left a static snapshot behind, including under a late concurrent writer|CONC|M|REVIEW|RF|N|agent|C||(completion pass)|DECISIONS.md ADR-025; PROGRESS.md ("deletion/snapshot ordering first failed")|D
W01|WayClub|Approval route not re-selected after a revision cycle (high-risk edit kept the low-risk route)|LOGIC|H|REVIEW|RV|Y|claude|C||60a9e03|60a9e03 msg ("verified to fail when the fix is reverted"; old assertion encoded the defect)|D
W02|WayClub|riskLevel was not an input to approval routing|LOGIC|H|REVIEW|T|N|claude|C||60a9e03|60a9e03 msg|D
W03|WayClub|Budget threshold failed open when unconfigured (NaN)|LOGIC|H|REVIEW|T|N|claude|C||60a9e03|60a9e03 msg|D
W04|WayClub|Web questionnaire and server derivation used disjoint risk keys|CONTRACT|H|REVIEW|T|N|claude|C||60a9e03|60a9e03 msg|D
W05|WayClub|Submit and cancel sent no body; the core action was a guaranteed 400 from the UI|CONTRACT|H|REVIEW|T|N|claude|C||60a9e03|60a9e03 msg|D
W06|WayClub|Off-campus submission required a confirmation the form never collected|CONTRACT|M|REVIEW|T|N|claude|C||60a9e03|60a9e03 msg|D
W07|WayClub|JWT secret not fail-fast; token subject not bound to tenant per request|AUTHZ|H|REVIEW|RF|N|claude|C||85475d7|docs/SECURITY.md adversarial review; 85475d7 msg (exploits re-run)|D
W08|WayClub|Audit hash chain forked under concurrency (seq allocated before the lock)|CONC|H|REVIEW|RF|Y|claude|A|1b3b8d9|85475d7|docs/adr/0009 ("Reproduced before the fix"); docs/TESTING.md (test passed for the wrong reason)|D
W09|WayClub|app.write_audit accepted a forged cross-tenant audit row|AUTHZ|H|REVIEW|RF|N|claude|A|1b3b8d9|85475d7|SECURITY.md; 85475d7 msg|D
W10|WayClub|transition_event allowed approval with undecided stages and ignored the instance|LOGIC|H|REVIEW|RF|Y|claude|C||85475d7|85475d7 msg ("two shipped tests asserted these defects")|D
W11|WayClub|Waitlist promotion authorised on tenancy alone|AUTHZ|H|REVIEW|RF|N|claude|A|1b3b8d9|85475d7|SECURITY.md line on promote_waitlist_head|D
W12|WayClub|Session revocation blind for a second (JWT iat is whole seconds); test hid it with a 1.1 s sleep|UNITS|H|SELF|RV|Y|claude|C||85475d7|SECURITY.md; TESTING.md; 85475d7 msg|D
W13|WayClub|Email verification not gating as specified|AUTHZ|M|REVIEW|U|N|claude|C||85475d7|SECURITY.md list|D
W14|WayClub|guest_policy not enforced across registration paths|LOGIC|M|REVIEW|U|N|claude|C||85475d7|85475d7 msg|D
W15|WayClub|Audit advisory lock held to commit serialised every audited write in a tenant (4.8 s vs 43 ms)|CONC|M|REVIEW|RF|N|claude|A|1b3b8d9|85475d7|docs/adr/0009 (measured before the fix)|D
W16|WayClub|Waitlist positions incorrect|LOGIC|M|REVIEW|U|N|claude|C||85475d7|SECURITY.md list|D
W17|WayClub|Duplicate dialog ids in the web app|UI|L|REVIEW|U|N|claude|C||85475d7|SECURITY.md list|D
W18|WayClub|Autosave dropped edits made during draft creation|CONC|M|REVIEW|U|N|claude|C||85475d7|85475d7 msg|D
W19|WayClub|Dishonest empty states hid errors|ERR|L|REVIEW|U|N|claude|C||85475d7|85475d7 msg|D
W20|WayClub|Incoming custodian refused the second attestation; two-party rule reachable only by accident|LOGIC|M|REVIEW|T|N|claude|C||199ef1d|199ef1d msg ("reported by a teammate agent"); SECURITY.md (no negative control)|D
W21|WayClub|Shipped chain verifier walked seq, passing only because the suite ran serially|TEST|M|SELF|RF|Y|claude|A|1b3b8d9|85475d7|SECURITY.md audit section|D
W22|WayClub|Tenant-scoped coverage set keyed on institution_id silently excluded tables added since migration 017|TEST|H|SELF|U|Y|claude|C|5096bcd||docs/TESTING.md|D
W23|WayClub|Append-only guard compared NULL institution_id with =, blocking chaining for standalone rows|LOGIC|M|SELF|U|N|claude|C|5096bcd|85475d7..5096bcd|docs/adr/0009-per-account-audit-chain.md|D
W24|WayClub|Club colour scope never reached buttons (token alias resolved against the root brand)|UI|M|SELF|RV|N|claude|C||5082ea4|5082ea4 msg ("mutation-checked")|C
W25|WayClub|Seed anchored to Date.now produced events straddling midnight at odd minutes|UNITS|L|SELF|N|N|claude|C||5082ea4|5082ea4 msg|C
W26|WayClub|Test pinned an incidental null poster instead of the response contract|TEST|L|SELF|N|Y|claude|C||5082ea4|5082ea4 msg|C
W27|WayClub|Poster colour computed and stored, then dropped by four hand-written subselects|CONTRACT|M|SELF|U|N|claude|C||0e0fdeb|0e0fdeb msg|C
W28|WayClub|Committee task tables had RLS on and zero policies: no request could reach a row|AUTHZ|M|SELF|U|N|claude|C||0e0fdeb|0e0fdeb msg|C
W29|WayClub|ROADMAP listed shipped features as not started|DOCS|L|SELF|N|N|claude|C||0e0fdeb|0e0fdeb msg|C
W30|WayClub|Static breadcrumbs sent Back to the structural parent, not the origin|UI|L|SELF|U|N|claude|C||0e0fdeb|0e0fdeb msg|C
W31|WayClub|ClubMark nested a div inside a paragraph (production hydration mismatch)|UI|M|SELF|U|N|claude|C||05e18a3|05e18a3 msg|C
W32|WayClub|Accent normalisation not idempotent: each save bleached the stored colour about 8%|UNITS|M|SELF|U|N|claude|C||05e18a3|05e18a3 msg|C
W33|WayClub|Auth pages rendered unstyled after the landing rewrite dropped their rules|UI|M|UNK|N|N|claude|C|a16442d|257c1e4|257c1e4 msg|C
W34|WayClub|Concurrent test-database resets caused phantom failures in green suites|TEST|M|SELF|N|N|claude|C||199ef1d|199ef1d msg|C
W35|WayClub|Sign-up could pre-hijack an address; invite claims lacked the invitee's session|AUTHZ|H|REVIEW|U|N|claude|C||3836ada|3836ada msg|C
W36|WayClub|WORKFLOWS, SECURITY and TESTING docs overstated the code|DOCS|M|REVIEW|N|Y|claude|C||85475d7|85475d7 msg|D
P01|Photoshoot|Windows black viewfinder: the desynchronized WebGL hint presented a black buffer on D3D11 while reporting "Camera live"|PLAT|H|OWNER|RV|Y|claude|A|bb93690|c2a7333|c2a7333 msg (0/256 lit pixels; removing only the line lights 256/256); test/browser/viewfinder.spec.ts|D
P02|Photoshoot|Render loop scheduled only by requestVideoFrameCallback, which can never fire for a hidden video|PLAT|H|OWNER|RM|N|claude|A|bb93690|3ce3da4|3ce3da4 msg (synthetic camera before/after)|C
P03|Photoshoot|Stale saved deviceId made exact-constraint getUserMedia report "No camera found"|STALE|M|OWNER|RM|N|claude|A|bb93690|d219ad6|d219ad6 msg|C
P04|Photoshoot|Theme shadow validator vulnerable to exponential backtracking (ReDoS)|ABUSE|M|SELF|T|N|agent|C||fb81c0b|fb81c0b msg|C
P05|Photoshoot|README claimed a published release and an rVFC render loop that no longer existed|DOCS|L|SELF|N|N|agent|C||6a1a298|6a1a298 msg|C
P06|Photoshoot|Effects grid inherited panel visibility; camera went black after reviewing a photo|UI|H|OWNER|RV|N|claude|C||9f34b37|docs/HANDOFF.md 1.4.0 ("proved red without the fix"); 9f34b37 msg|D
P07|Photoshoot|Video review used an img element and showed black|UI|M|SELF|T|N|claude|C||9f34b37|HANDOFF.md 1.4.0|D
P08|Photoshoot|Face landmarks uploaded y-down and x mirrored twice; every face effect landed below or beside the face|UNITS|M|UNK|T|N|claude|A|70bb1b2|fcdb7bf|HANDOFF.md 1.5.1; fcdb7bf msg; face.spec.ts|D
P09|Photoshoot|Effects-browser renderer started its own loop after a GPU reset|PLAT|M|SELF|U|N|claude|C||9f34b37|HANDOFF.md 1.4.0|D
P10|Photoshoot|Fixed segmentation worker name plus year-long caching could pair a new renderer with a stale worker|STALE|M|REVIEW|T|N|claude|C|18bef78|e935537|e935537 msg; HANDOFF.md (independent review)|C
P11|Photoshoot|Background blur haloed dark hair into the blur|UI|L|OWNER|N|N|claude|C|18bef78|a01c99c|a01c99c msg; HANDOFF.md rework|C
P12|Photoshoot|Quit waited on a virtual camera that was not running|ERR|M|SELF|U|N|claude|C|18bef78|e96ac0d|e96ac0d msg|C
P13|Photoshoot|iOS Safari 100vh put the toolbar under the browser bar|PLAT|M|UNK|U|N|claude|C||3f5104b|3f5104b msg|C
P14|Photoshoot|Landing shader-failure and delayed-initialisation edge cases|ERR|M|REVIEW|T|N|claude|C||(1.2.0 landing)|HANDOFF.md showcase continuation|D
P15|Photoshoot|Page changes requested mid-slide were dropped|UI|L|UNK|U|N|claude|C|67c929d|756f337|756f337 msg|C
P16|Photoshoot|Graphics reset mid-countdown did not cancel flash and shutter sound|CONC|L|REVIEW|U|N|claude|C||702d5e9|702d5e9 msg (deferred review items)|C
P17|Photoshoot|Default Windows IR or busy camera ended the start-up attempt|PLAT|M|OWNER|U|N|claude|C||435e81b|435e81b msg|C
G01|CodeAtlas|Local runner evidence not bound to the execution that produced it|LOGIC|M|SELF|RO|N|codex|B||759c749|test 95ef7c4 precedes fix 759c749|C
G02|CodeAtlas|Runner provenance boundaries not enforced|LOGIC|M|SELF|RO|N|codex|B||042493a|test 4decf52 precedes fix 042493a|C
G03|CodeAtlas|Runtime assertion imports not required (shadowed assertions accepted)|LOGIC|M|SELF|RO|N|codex|B||b842f12|test 45870de precedes fix b842f12|C
G04|CodeAtlas|Generated evidence not bound to the canonical execution|LOGIC|M|SELF|RO|N|codex|B||ecdb57d|test 9530fe9 precedes fix ecdb57d|C
G05|CodeAtlas|Differential evidence not bound to executions|LOGIC|M|SELF|RO|N|codex|B||1dcffc8|test 8919aab precedes fix 1dcffc8|C
G06|CodeAtlas|Serialised digest spoofing accepted in execution results|LOGIC|M|SELF|RO|N|codex|B||bd4bb79|test 1b697f8 precedes fix bd4bb79|C
G07|CodeAtlas|Change Passport publication review findings|LOGIC|M|REVIEW|RO|N|codex|B|1cfa09c|8d59914|test 60ab075 ("cover Change Passport review findings") precedes fix|C
G08|CodeAtlas|Strict runs not validated; temp-file ownership unprotected|LOGIC|M|SELF|RO|N|codex|B||a4be2bb|test e1f63ed precedes fix a4be2bb|C
G09|CodeAtlas|Artefact cleanup could delete a substituted file after inode reuse (CI 242/243)|CONC|M|GATE|RF|N|codex|B||204caba|CI run 37443139235 vs 37453388832; content/projects/codeatlas.json evidence|D
G10|CodeAtlas|AI-inferred test paths admitted into evidence|LOGIC|M|UNK|U|N|codex|B|7ffa763|46635d1|46635d1 subject|C
G11|CodeAtlas|Local test evidence failed open|LOGIC|M|UNK|U|N|codex|B|7aa0d6b|9e5075a|9e5075a subject|C
G12|CodeAtlas|Wrapped exports and composed tests not normalised|LOGIC|L|UNK|U|N|codex|B|5698a92|cec0ec3|cec0ec3 subject|C
G13|CodeAtlas|Static analysis sites incomplete or duplicated|LOGIC|M|UNK|U|N|codex|B|5698a92|ba8fa24|ba8fa24 subject|C
G14|CodeAtlas|Confirmation not bound to cited evidence|LOGIC|M|UNK|U|N|codex|B|303242a|7b72462|7b72462 subject|C
G15|CodeAtlas|Evidence provenance invariants not enforced|LOGIC|M|UNK|U|N|codex|B|303242a|7a727b6|7a727b6 subject|C
G16|CodeAtlas|Replay did not verify signatures or use the current analysis|LOGIC|M|UNK|U|N|codex|B||29ad4d3|29ad4d3 subject|C
A01|Adelante|Widget drifted from the app's current quote (independent rotation and anchor drift)|STALE|M|UNK|T|N|unknown|D|72eb8e8|82c8e51|82c8e51 subject|C
A02|Adelante|Home favourite pill did not light (stale dailyQuote)|STALE|L|UNK|U|N|unknown|D||f4535e9|f4535e9 subject|C
A03|Adelante|Native UIVisualEffect rendered flat inside Flutter|PLAT|L|UNK|N|N|unknown|D|f4535e9|1c3343b|1c3343b subject|C
A04|Adelante|Soft panels lacked a Material, so tile ink was invisible|UI|L|GATE|N|N|unknown|D|1f6bf6c|d448de8|d448de8 msg (debug assertion)|C
A05|Adelante|Overlong quotes overflowed widgets; preview and native disagreed on length; fields never bridged|CONTRACT|M|SELF|T|N|unknown|D|72eb8e8|fd47846|fd47846 msg|C
A06|Adelante|Faith users saw the same 7-12 quotes repeat|LOGIC|M|UNK|T|N|unknown|D||3e0d06d|3e0d06d msg|C
A07|Adelante|Widget editor: sideways drag did nothing on full-width text|UI|M|OWNER|U|N|claude|C||f7d479e|docs/design/2026-10-01-completion-pass.md|D
A08|Adelante|Widget editor: vertical drag scrolled the page instead of moving the text|UI|M|OWNER|U|N|claude|C||f7d479e|completion-pass.md|D
K01|Planket|SceneDelegate used the plain bridge controller, so earlier "native fixes" never ran|PLAT|M|OWNER|N|Y|claude|A|31ac493|6d1ddd1|6d1ddd1 msg|C
K02|Planket|iOS swallowed first clicks on buttons|PLAT|M|OWNER|N|N|claude|C||6d1ddd1|6d1ddd1 msg|C
K03|Planket|Google Calendar re-linking was refused silently|ERR|M|OWNER|N|N|claude|C||5cd11ca|5cd11ca msg|C
K04|Planket|Note-cleanup cron scheduled every minute (avoidable database load and cost)|BUILD|L|SELF|N|N|codex|B|3f670ba|af926de|af926de msg; 3f670ba msg ("Codex completion pass")|C
K05|Planket|Android sign-in race on first run|CONC|M|OWNER|N|N|claude|C||636da46|636da46 subject|C
K06|Planket|Onboarding did not save through the live account|LOGIC|M|OWNER|N|N|claude|C||636da46|636da46 subject|C
K07|Planket|Internal notification helper publicly callable|AUTHZ|M|UNK|U|N|claude|C||3f670ba|docs/HANDOFF.md (migration 0012)|D
K08|Planket|Failed saves lost input and allowed duplicate retries after partial saves|ERR|M|UNK|U|N|claude|C||3f670ba|HANDOFF.md current pass|D
H01|HACKFAM|PUBLIC execute default on private helpers made another person's profile strength readable|AUTHZ|H|GATE|RV|N|codex|B|258d67d|202610180001 (in c2c128a)|docs/verification/db.md Fix 1 (73/74 red, revert red, permanent proof)|D
H02|HACKFAM|Discover deck took 139 s in a realistic pool (timezone catalogue re-read per call)|PERF|H|SELF|RM|N|codex|B|258d67d|202610180009 (in c2c128a)|db.md Fix 2|D
H03|HACKFAM|card_json returned whole rows, leaking recruit codes and GitHub data|PRIV|H|REVIEW|U|N|codex|B|258d67d|(contract v1.1 DTOs)|docs/reviews/2026-10-18-spec-review-contract-architecture.md; spec-rework.md P0-05|D
H04|HACKFAM|Read-receipt preference stored but never enforced|PRIV|M|REVIEW|U|N|codex|B|258d67d|(contract v1.1)|spec-rework.md D2|D
H05|HACKFAM|Raw Postgres constraint errors leaked to clients|ERR|L|REVIEW|U|N|codex|B|258d67d|(api reconcile A raise_input)|spec-review-contract-architecture.md; db-v1.1.md DB-2a|D
H06|HACKFAM|Crew-chat blocked-sender badge test only exercised a closed match, so it could not fail|TEST|M|SELF|RV|Y|claude|C|||docs/verification/db-v1.1.md|D
H07|HACKFAM|Pool-alert proof masked by the dedupe key|TEST|L|SELF|RV|Y|claude|C|||db-v1.1.md|D
H08|HACKFAM|Four mutations survived (32 of 36 red), exposing weak function tests|TEST|M|SELF|RV|Y|claude|C|||docs/verification/functions-v1.1.md v11e|D
H09|HACKFAM|Team admission accepts only forming teams, contradicting the venue-formation decision (open)|LOGIC|M|SELF|X|N|claude|C|||db-v1.1.md open issue|D
"""

FIELDS = ["id", "product", "title", "category", "severity", "detection", "red",
          "green_over", "author", "attr_tier", "intro", "fix", "source", "path"]


def rows():
    out = []
    for line in ROWS.strip().splitlines():
        parts = [p.strip() for p in line.split("|")]
        assert len(parts) == len(FIELDS), (len(parts), line)
        r = dict(zip(FIELDS, parts))
        r["repo"] = REPO[r["product"]]
        out.append(r)
    ids = [r["id"] for r in out]
    assert len(ids) == len(set(ids)), "duplicate id"
    return out


def main():
    here = os.path.dirname(os.path.abspath(__file__))
    data = os.path.join(here, "..", "data")
    os.makedirs(data, exist_ok=True)
    rs = rows()
    cols = ["id", "product", "repo"] + FIELDS[2:]
    with open(os.path.join(data, "defects.csv"), "w", newline="") as f:
        w = csv.DictWriter(f, fieldnames=cols)
        w.writeheader()
        for r in rs:
            w.writerow({k: r[k] for k in cols})
    with open(os.path.join(data, "defects.json"), "w") as f:
        json.dump([{k: r[k] for k in cols} for r in rs], f, indent=1)
    print(len(rs), "defects")


if __name__ == "__main__":
    main()
