# dependencies
node_modules/

# secrets — NEVER commit (OAuth client secret, refresh token, API keys)
.env
.env.*
**/.env
**/.env.*
# env copies that dodge the .env* patterns (migration backups, *.bak, env.old …)
**/*env*.bak
**/env.*
**/*.env.bak
_token_result.json

# demo recording artifacts (large binaries — keep scripts, not outputs)
*.webm
*.mp4
*.mov
*.mp3
*.png
*.jpg
*.jpeg
test-results/
**/youtube/**/recording.*
**/youtube/**/*.mp4
**/youtube/**/*.mp3

# OS / editor
.DS_Store
*.log

# loose one-off scripts / result dumps (Stripe/deploy/QA — may contain secrets, not part of the pipeline)
# ROOT-ANCHORED ON PURPOSE: these patterns are for scratch dropped at the repo root.
# Unanchored they apply at every level, and subsystems use the _ prefix for real
# modules (social/_record.mjs, framer/_fm.mjs, …) — which silently went untracked.
/_*.mjs
/_*.json
*-results.json
*-qa-results.json

# demo build outputs (regenerated by build-reel.sh / gen-ad-narration.mjs)
brand/reel/
brand/tpreview*/
remotion/public/adv/
*.m4a

# promo build scratch + local recordings (per-app namespaced too, parallel-safe)
.feat-work/
.prob-work/
.short-work/
.feat-work-*/
.prob-work-*/
.short-work-*/
.feat-rec/
.prob-rec/
test-results/
# thumbnail bg candidate dumps (Unsplash previews + manifest)
thumbs/bg/_cand/

# namespaced QA / recon artifacts (Playwright traces + recon dumps)
test-results-*/
recon/**/report*.json
*-badged.jpg

# gads watchdog runtime state/logs
gads/watchdog-state.json
gads/watchdog.log
gads/launchd.out.log
gads/launchd.err.log
gads/policy-audit.json

# full-res thumbnail sources (local backup only, ~300MB)
brand/reel/*/*-thumbnail-src.png

# agent-usage-sync runtime state/logs
telemetry/.agent-usage-sync-state.json
telemetry/*.launchd.out.log
telemetry/*.launchd.err.log

# session cookies — NEVER track logged-in browser profiles
portals/.profile*/
# ⛔ social/ TOO — the post-shot daemon's profile landed in a machine commit on 2026-08-23
# (1,546 files) and the pre-push secret gate refused the whole estate's push over Chrome's own
# Secure Preferences metadata. A browser profile is runtime state and is never repo content.
social/.profile*/
portals/.cookie-backups/
# reddit agent runtime state (regenerated) + local arm flag + logs
social/reddit/reddit-agent-state.json
social/reddit/warmup-queue.json
social/reddit/ARMED
social/reddit/*.log
# daily social self-audit — runtime state + local arm flag (never track the live-delete switch)
social/social-audit-state.json
social/AUDIT_ARMED
social/social-audit.log
social/social-audit-launchd.*.log
portals/out/
social/x-fetchdue/ARMED
social/x-fetchdue/reply-queue.json
social/x-fetchdue/x-agent-state.json
social/x-fetchdue/targets.json
social/x-fetchdue/*.log
social/x-fetchdue/.last-run-*

# ugc is its own git repo (moved inside 2026-07-16); never index it from demos
social/ugc/
portals/.env

# wire-harvest screenshot cache + rejected crops (regenerable, large)
social/wire-harvest/.shots/

social/wire-harvest/candidates*.json
social/wire-harvest/seen-web.json
social/wire-harvest/claims.json
social/wire-harvest/queues/
social/wire-harvest/posted.jsonl

# dev-harvest screenshot cache — same shape as the wire-harvest rule above,
# which was written and this sibling was missed (373M tracked).
social/dev-harvest/.shots/

# social render output (dry-showcase runs, compound-media). Regenerated per run;
# 561M of it was tracked because no rule existed.
social/out/
portals/.page-health-notified.json
# Which rung of the designated ladder Dice takes. Lane state, not a credential: it names a
# variant, never a value. Remembered so a heal costs one attempt instead of walking the ladder
# and burning failed logins against a real account on every lane start.
portals/.dice-rung.json

# Account marks used by tools/cards.py. These are BRAND ASSETS, not generated output —
# the card renderer cannot draw a wordmark without them, so they are exempt from the *.png rule.
!social/tools/logos/*.png

# The plate Context Window's banner is cut from. It is an INPUT, not generated output: without
# it social/ctxwindow/brand/make-banner.mjs has nothing to read and the banner can never be
# re-cut. Same exemption convention as social/tools/logos above.
!social/ctxwindow/brand/banner-source.png

# auto-reply engine single-run lock (see outreach/lib/replies.py _lock)
outreach/.replies.lock

# Derived URL cache — regenerated by tools/surfaces.mjs; never source of truth.
social/tools/.surfaces-cache.json

# ghscrape HTTP response cache — regenerable, multi-GB, never source of truth.
ghscrape/.cache/
portals/*/out/
portals/out/
.gemini-sessions/

# Runtime lock/ledger state for the gemini + portal daemons. NEVER commit these:
# a committed GEMINI_STOP or a stale <profile>.lock blocks every run on checkout.
portals/.locks/

# Python bytecode caches (tools/notices imports notices.py as a module)
__pycache__/
*.pyc

# Queue purge snapshots — local recovery artifacts, tens of MB of raw JD text.
# purge-queue.mjs writes one per run; they are read by a human restoring rows, never by code.
jobapply/jobs/ats-queue-purged-*.jsonl

# ── /ats-apply OPERATIONAL STATE — regenerable, and it CHURNS ───────────────────────────────
# ats-queue.jsonl is 34MB and is rewritten on every harvest, so tracking it committed another
# 34MB blob per pass — 68MB of one file in a single day, and the reason .git reached 1.2GB and
# pushes began timing out. It is rebuilt by `atsapi.mjs --harvest`; git is not its backup.
# ⛔ ats-queue-done.json STAYS TRACKED. It is small and it is the only thing preventing a second
# application to a job already applied to — losing it is unrecoverable under his real name.
jobapply/jobs/ats-queue.jsonl
jobapply/jobs/out/events.jsonl

# Queue backups are working files, never repo content — a .bak of a queue carries the
# same job-board tokens the live queue does and regenerates a fresh gitleaks fingerprint
# every time it is written. Keep them out of the repo entirely.
jobapply/jobs/*.bak-*
jobapply/jobs/*.jsonl.bak-*

# ⛔ TIMESTAMPED BACKUPS, ANYWHERE IN THE REPO. The narrow jobapply/jobs/ rule above was
# written for one incident; the habit is estate-wide. Already tracked before this rule:
# jobapply/EXPERIENCE-BANK.md.bak-*, social/accounts.json.bak-lane-split,
# social/dev-harvest/claims.json.bak-*, social/health-monitor/*.bak-2026-07-25,
# social/news-x/news-state.json.bak-*, portals/upwork-portfolio.mjs.bak — and
# jobapply/refresh-bank.mjs:348 still writes more.
#
# Two costs, both paid in full on 2026-08-12. A 43.4 MB queue backup landed in a commit and
# every push since has paid a ~13-minute gitleaks rescan of that one blob, because a file
# removed in a LATER commit is still inside the commit that added it. And a backup of a data
# file carries the same tokens the live file does, so each one mints fresh gitleaks
# fingerprints that the ignore list has to chase.
#
# A backup is a working file. Write it outside the repo, or to a path already ignored.
# These patterns do not untrack what is already tracked — they stop the next one.
*.bak
*.bak-*
*.backup
*.backup-*
*.orig
*.jsonl.bak-*
*.json.bak-*
!portals/upwork-portfolio.mjs.bak

# W1 list lane — launchd stdout/stderr, written every run
list/*.log
gobig/census/dist/

# Banner render harness scratch — throwaway Chrome profiles and the esbuild bundle.
# 3,145 of these got tracked in one afternoon; none of it is an input to anything.
social/banners/.build/
social/banners/out/

# ⛔ THE WHOLE DIR, NOT ONE FILENAME. The rule above named only events.jsonl, so every DATED
# file (events.2026-08-14.jsonl) slipped through — one of them reached 138MB and went into git
# history, producing a 20-minute pre-push gitleaks scan and 111 false positives (2026-08-15).
jobapply/jobs/out/

# Arm flags are machine state, not source — same as the lane ARMED files above.
social/compound-yt/.promote-ARMED

# One-off measurement probes written during a browser/render session and never meant to persist.
# Untracked they re-dirty the tree on every pass; see .compound-machine-written for lane output.
portals/_tmp-*.mjs
video/probe-*.mjs

.next/
__pycache__/
*.pyc
social/tools/_mmd_*.html

# Resolved credential cache for portals/lib/signin.mjs — real passwords, 0600, never committed.
**/portal-logins.secret.json
# retired 2026-09-16: the portals sign-in cache was a SECOND credential store and it shadowed
# Bitwarden. The pattern above stays so the file can never come back tracked. The proof below
# carries SHA-256 hashes, never values, and still never belongs in a repo.
**/portal-logins.migration-proof.json

# ⛔ DAEMON-WRITTEN HEALTH STATE, NEVER TRACKED. surface-canary.mjs rebuilds this every pass;
# tracking it means a checkout restores a STALE snapshot over live health data and products read
# as dead until the next pass overwrites it. ShipWall was flagged down on 29 surfaces for two
# weeks that way while all four of its routes answered 200 — measured 2026-08-20.
social/tools/.surface-down.json

# compound-ops/tools/gates/html-review.mjs — the screens it tiles a document into for the judges.
# Regenerable from the document in seconds; never worth a commit.
reviews/walkthrough/review-tiles/

# generated by social/tools/roll-table.mjs on every run — the script is the artifact
social/reports/roll-table.html

# invite lane run artifacts (screenshots + payloads) — proof, not source
social/compound-linkedin/invite-shots/
social/compound-linkedin/.li-invite.lock

# film scratch — composition dumps and one-off probes, never artifacts.
# No BCV-*.html is tracked anywhere; the authored references are the PNGs under
# frames-vertical/<slug>/. These are byproducts of a single run.
video/aesthetic-ref/.shoot-tmp.mjs
video/aesthetic-ref/compound/
costs/vercel-repair.lock

# This Much We Know channel art — brand DELIVERABLES, not the "demo recording artifacts" the
# blanket *.png/*.jpg rules above are for. Same exception convention as social/tools/logos.
# Scoped to the folders this identity owns, so nothing else under art/ is un-ignored.
!social/thismuchweknow-yt/art/mark/**/*.png
!social/thismuchweknow-yt/art/wordmark/*.png
!social/thismuchweknow-yt/art/avatar/*.png
!social/thismuchweknow-yt/art/banner/*.png
!social/thismuchweknow-yt/art/thumbnail/**/*.png
!social/thismuchweknow-yt/art/plates/*.png
!social/thismuchweknow-yt/art/sources/*.png
# The process gate's own evidence. `*.png` above is why these vanished once already: the image
# fixtures of tools/gates/process-is-the-artifact.fixtures/ could never be committed, so the
# variation selftest crashed on a missing directory and the gate's selftest had been failing.
# These two trees are the CONTRACT (the approved samples) and the CALIBRATION (the real templated
# set that must fail and the real per-subject set that must pass), so they are tracked on purpose.
!tools/gates/process-is-the-artifact.fixtures/**/*.png
!process/samples/*.png

!social/thismuchweknow-yt/art/sources/*.jpg
!social/thismuchweknow-yt/art/lockup/*.png
!social/thismuchweknow-yt/art/lockup/sheets/*.png
!social/thismuchweknow-yt/art/kit/**/*.png
!social/thismuchweknow-yt/art/chosen/renders/*.png
!social/thismuchweknow-yt/art/pattern/*.png
!social/thismuchweknow-yt/art/chosen/thumb2/*.png
!social/thismuchweknow-yt/art/chosen/category/*.png
!social/thismuchweknow-yt/art/chosen/pattern/*.png
!social/thismuchweknow-yt/art/thumb2/*.png
!social/thismuchweknow-yt/art/category/*.png
!social/thismuchweknow-yt/art/explore/*.png
!social/thismuchweknow-yt/art/gen/*.png
!social/thismuchweknow-yt/art/objects/*.png
!social/thismuchweknow-yt/art/thumbnail-explore/**/*.png
!social/thismuchweknow-yt/art/thumbnail-explore/**/*.jpg
!social/thismuchweknow-yt/art/thumb3/*.png
!social/thismuchweknow-yt/art/catbullets/*.png
!social/thismuchweknow-yt/art/signal/**/*.png
!social/thismuchweknow-yt/art/video/*.png
!social/thismuchweknow-yt/art/videobrand/**/*.png
# the six baked dia-gradient fields — real build inputs, one headless Chrome run ever
!social/thismuchweknow-yt/art/videofx/aurora/*.png
# the black-and-white channel kit (2026-08-27) — same rule as every other art dir above
!social/thismuchweknow-yt/art/bw/photos/*.jpg
!social/thismuchweknow-yt/art/bw/out/**/*.png
!social/thismuchweknow-yt/art/bw/out/**/*.jpg

media/.openverse-token.json

# derived probe cache for tools/gates/vercel-spend-gate.mjs — regenerated on any run
costs/spend-gate-probe-cache.json
costs/.alert.*

# Campaign runtime state (2026-08-27, the two outbound reply campaigns).
# ⛔ campaigns.json IS committed — it is the query sets, the windows and the facts, and it is the
# only thing anyone edits. Everything below is a counter or a cache the lanes write every tick.
social/campaign-state.json
social/campaign-seats.json
social/campaign-x-sweep.json
social/campaign-facts-cache.json

# ── This Much We Know episode scratch ────────────────────────────────────────────────
# ⛔ MACHINE SCRATCH, NOT THE EPISODE. The episode IS episodes/<slug>/episode.json plus its
# narration, archival record, word timings and caption specs — all of which ARE tracked. These
# three are the working files either side of them and every one is regenerable:
#   .research-prompt.txt / .research-raw.txt   the brief handed to the one model call and its
#     raw reply, written beside the episode on purpose so a bad cut's brief can be read back.
#     Rebuilt by `node ops/research.mjs <slug> --print`.
#   renders/*.description.txt   what ops/publish.mjs hands `_upload.cjs --desc-file`. It is a
#     copy of renders/<slug>.publish.json's `description`, which IS tracked; keeping both would
#     be two accounts of the same text, and lib/publish.py's header bans exactly that.
social/thismuchweknow-yt/episodes/*/.research-prompt.txt
social/thismuchweknow-yt/episodes/*/.research-raw.txt
social/thismuchweknow-yt/episodes/*/renders/*.description.txt

# ── This Much We Know: the heavy per-episode artefacts ─────────────────────────────────────────────
# ⛔ THE CUTS AND THE FOOTAGE ARE REPRODUCIBLE AND THEY ARE ENORMOUS. One 12-minute 1080p cut is
# ~350 MB and one episode's b-roll is another ~300 MB; `git add -A` in this repo would have
# committed about a gigabyte of it. Everything needed to rebuild them IS committed — the spec,
# the slate, ops/build.mjs — and `node lib/broll.mjs` re-fetches the footage. The thumbnail is
# the exception: it is what YouTube was handed, so it is kept.
social/thismuchweknow-yt/episodes/*/assets/broll/*.mp4
social/thismuchweknow-yt/episodes/*/renders/*.mp4
social/thismuchweknow-yt/episodes/*/renders/*.mp3
social/thismuchweknow-yt/episodes/*/renders/*.mov
social/thismuchweknow-yt/episodes/*/renders/*.png
social/thismuchweknow-yt/episodes/*/renders/*.words.json
social/thismuchweknow-yt/episodes/*/renders/*.publish.json
social/thismuchweknow-yt/episodes/*/renders/.verify-*/
social/thismuchweknow-yt/episodes/*/renders/*.captions.txt
social/thismuchweknow-yt/episodes/*/assets/caps/
social/thismuchweknow-yt/episodes/*/assets/vo/
# derived caches, both measured off gitignored binaries and both self-healing
social/thismuchweknow-yt/ops/.rate.json
social/thismuchweknow-yt/ops/.bedpeaks.json
!social/thismuchweknow-yt/episodes/*/renders/*-thumb.jpg
!social/thismuchweknow-yt/episodes/*/renders/MANIFEST.json

# THE REJECTION LEDGER — append-only lane state, not source (2026-08-30).
# social/_rejects.cjs writes one .jsonl per account every time the gate refuses a draft, and the
# lanes write to it on every tick. It is regenerable in full from the drip logs
# (`node social/tools/rejects-import-log.cjs`), it is trimmed in place at 4,000 rows, and it can
# carry the opening lines of drafts that were refused for a hard ban — none of which belongs in a
# repo that deploys. The first-pass-*.json files beside it are diagnostic runs of
# social/tools/first-pass-eval.mjs, same story.
social/voice/rejections/

# thismuchweknow-yt: the one-tick-at-a-time PID lock (ops/tick.mjs)
social/thismuchweknow-yt/ops/.tick.lock

# reply-lane lookup cache (social/_reply-research.mjs) — a rate control, never a source of truth
social/.reply-research-cache/

# the UGC publication sync's upload ledger: which picture hashes are already in storage. Rewritten daily.
social/ugc/.publication-uploads.json

# Gate sweep output: a findings.json plus one PNG per finding, rebuilt every morning by
# run-contrast-sweep.sh and run-figure-sweep.sh. Neither is a source of truth and a 2,000 shot
# sheet must never reach the repo through somebody else's `git add -A`.
tools/contrast-sweep/
tools/figure-sweep/

# The halt announce marker (social/_pagehealth.cjs): which halt this lane has already printed
# the full human runbook for, so a hold reports once in full and one line per tick after that.
# Pure runtime pacing state, rewritten whenever a halt is written or cleared.
social/*/.halt-announced

# ⛔ THE UNSHIPPED HALF OF A TICK (social/_resume.cjs, 2026-09-06). Every lane keeps ONE
# unpublished attempt beside itself so a late failure does not throw away the compose, the
# captures and the render that came before it. It is runtime state, it is burned on publish,
# and it must never be committed.
social/**/.made/
social/.made/

# The landing-register card's OBJECT and its APPROVED RENDERS are fixtures, not outputs.
# The spec pointed "object" at a session scratchpad path on 2026-09-06, so the renderer
# would have run only as long as that session lived and then failed on a missing file.
!social/tools/landing-card/objects/*.png
!social/tools/landing-card/approved/*.png
turso/TRIPPED
costs/turso-health.json

# advice-delivery journals are lane state (record keys, list ids); tracked only by 2026-09-09 bulk session commits
social/state/advice-delivery/

# Compound per-product production trees are output (captures, frames, renders); the object prompt is the one input kept
video/compound-youtube-preview/products/*/*
!video/compound-youtube-preview/products/*/assets/
video/compound-youtube-preview/products/*/assets/*
!video/compound-youtube-preview/products/*/assets/*-object.prompt.txt

# Per run video cuts. One directory per cut, written by the run that posts it and read by
# nothing else (HARD RULE #16). Build output: the spec sheet, the evidence, the captures, the
# render and the receipts all live here and none of it is a source.
video/compound-youtube-preview/runs/

# Tailored resume renders, one per application (jobapply/tailor.mjs RESUME_ARCHIVE). Isaiah,
# 2026-09-12: "we dont need to fucking save the pdfs" / "ok just keep on disc for now gitignore".
# 636 PDFs, 53 MB, were in git and every push scanned them for three minutes.
jobapply/jobs/resumes/
# The tailor's per-application working dirs (jobapply/tailor.mjs out/tailored/<stem>): the rendered
# resume and cover PDFs the lane uploads, plus their html and md. Same rule as jobs/resumes.
jobapply/out/tailored/

# The material library's scraped inbox (social/material-library/instagram-inbox.mjs): third-party post text,
# daemon-written, and on 2026-09-12 one scraped post carried a token-shaped string that blocked every push.
social/material-library/inbox/

# 2026-09-13. The weekly reconcile blanket-committed this report directory and two of its
# artifacts are 135MB and 57MB, over GitHub's 100MB hard limit. compound-ops could not push at
# all from that commit onward. They are machine output of a one-off migration scan, regenerable,
# and nothing reads them.
reports/compound-migration-20260908/occurrences.jsonl.gz
reports/compound-migration-20260908/inventory.json

# next-wave cover sheet: the review shots ARE the artifact here, so they are tracked
!docs/next-wave/covers/shots/*.png
!docs/next-wave/covers/shots/*.jpg

# CivicBinder Health lane: HRSA's own 14MB upstream export, re-pulled on demand by
# lanes/civicbinder-health/outreach/refresh-hrsa.sh --refresh. The derived lead files
# beside it (healthcare-sites.csv, grantees.csv) and the resume caches ARE tracked,
# because losing a cache costs spent crawl work.
lanes/civicbinder-health/leads/hrsa-sites-export.csv

# The pre-09-11 copy of the tailored resume archive. Merged into jobapply/jobs/resumes
# on 2026-09-14 and removed. Same rule as that path: never tracked.
jobs/

# breachprobe lane: the smoketest rewrites this every run, so it is state, not source.
lanes/breachprobe/ops/smoketest/heartbeat.txt
social/compound-facebook/.business-probe.json

# newsletter-pitch computes a dataset figure once per cut and caches it here. Derived from
# published files, rebuilt on a miss, never a source of truth.
social/newsletter-pitch/.dataset-cache/
oss-submit/out/
oss-submit/shots/
jobapply/out/wd-accounts.json
