{"schema":"barkday.ai-pack-everything.v1","repo":"CandidQuality/Barkday","commit":"9e38500d723ed1e8e1ca93eb8ae821c831f1414d","updated_utc":"2026-05-05T03:48:27.039Z","count":189,"items":[{"path":".githooks/pre-commit.cmd","size":105,"sha":"45fc3539bfa99753ba69de848077eede39bc9eda","media_type":"application/octet-stream","raw_url":"https://raw.githubusercontent.com/CandidQuality/Barkday/9e38500d723ed1e8e1ca93eb8ae821c831f1414d/.githooks/pre-commit.cmd","html_url":"https://github.com/CandidQuality/Barkday/blob/9e38500d723ed1e8e1ca93eb8ae821c831f1414d/.githooks/pre-commit.cmd","inline_state":"full","max_inline_text_bytes":614400,"max_inline_bin_bytes":204800,"preview_text_bytes":65536,"encoding":"base64","content":"QGVjaG8gb2ZmCnBvd2Vyc2hlbGwgLU5vUHJvZmlsZSAtRXhlY3V0aW9uUG9saWN5IEJ5cGFzcyAtRmlsZSAiJX5kcDBwcmUtY29tbWl0LnBzMSIKZXhpdCAvYiAlZXJyb3JsZXZlbCUK","inline_bytes":140,"content_sha256":"2ef27653e37b05bf66e03c400d84c18d1622c671c2988ed13cb0a1cf821c2fa1"},{"path":".githooks/pre-commit.ps1","size":669,"sha":"878642132238750832ff01ddd63b98e256155b38","media_type":"application/octet-stream","raw_url":"https://raw.githubusercontent.com/CandidQuality/Barkday/9e38500d723ed1e8e1ca93eb8ae821c831f1414d/.githooks/pre-commit.ps1","html_url":"https://github.com/CandidQuality/Barkday/blob/9e38500d723ed1e8e1ca93eb8ae821c831f1414d/.githooks/pre-commit.ps1","inline_state":"full","max_inline_text_bytes":614400,"max_inline_bin_bytes":204800,"preview_text_bytes":65536,"encoding":"base64","content":"JEVycm9yQWN0aW9uUHJlZmVyZW5jZSA9ICJTdG9wIgoKZnVuY3Rpb24gRmFpbCgkbXNnKSB7CiAgV3JpdGUtSG9zdCAiIgogIFdyaXRlLUhvc3QgIlBSRS1DT01NSVQgQkxPQ0tFRDogJG1zZyIKICBXcml0ZS1Ib3N0ICIiCiAgZXhpdCAxCn0KCiMgMSkgVmVyaWZpY2F0aW9uIG11c3QgcGFzcwpwb3dlcnNoZWxsIC1Ob1Byb2ZpbGUgLUV4ZWN1dGlvblBvbGljeSBCeXBhc3MgLUZpbGUgdG9vbHMvdmVyaWZ5LnBzMQppZiAoJExBU1RFWElUQ09ERSAtbmUgMCkgeyBGYWlsICJ0b29scy92ZXJpZnkucHMxIGZhaWxlZC4iIH0KCiMgMikgQSBkZWNpc2lvbiByZWNvcmQgbXVzdCBiZSBzdGFnZWQgKG5vdCBqdXN0IHByZXNlbnQpCiRzdGFnZWQgPSBnaXQgZGlmZiAtLWNhY2hlZCAtLW5hbWUtb25seQokaGFzRGVjaXNpb24gPSAkZmFsc2UKCmZvcmVhY2ggKCRmIGluICRzdGFnZWQpIHsKICBpZiAoJGYgLW1hdGNoICdeZG9jcy9kZWNpc2lvbnMvREVDLVxkezh9LVxkezJ9LS4rXC5tZCQnKSB7ICRoYXNEZWNpc2lvbiA9ICR0cnVlIH0KfQoKaWYgKC1ub3QgJGhhc0RlY2lzaW9uKSB7CiAgRmFpbCAiTm8gc3RhZ2VkIGRlY2lzaW9uIHJlY29yZCBmb3VuZC4gU3RhZ2UgZG9jcy9kZWNpc2lvbnMvREVDLVlZWVlNTURELVhYLSoubWQgYmVmb3JlIGNvbW1pdHRpbmcuIgp9CgpleGl0IDAK","inline_bytes":892,"content_sha256":"c3209476b9cbba3a9aeea55c07c6d687fe605756dc715a53f260016d26a396e7"},{"path":".github/workflows/ai-health.yml","size":5105,"sha":"de4461f4a1835e5042644d931fb59473377a77cc","media_type":"text/yaml","raw_url":"https://raw.githubusercontent.com/CandidQuality/Barkday/9e38500d723ed1e8e1ca93eb8ae821c831f1414d/.github/workflows/ai-health.yml","html_url":"https://github.com/CandidQuality/Barkday/blob/9e38500d723ed1e8e1ca93eb8ae821c831f1414d/.github/workflows/ai-health.yml","inline_state":"full","max_inline_text_bytes":614400,"max_inline_bin_bytes":204800,"preview_text_bytes":65536,"encoding":"utf8","content":"name: AI Health Checks\r\n\r\non:\r\n # keep nightly, plus allow manual runs; add push-on-gifts for fast feedback\n schedule:\r\n - cron: \"17 3 * * *\"\r\n workflow_dispatch:\r\n push:\n paths:\n - 'data/dog-gifts-merged.json'\n - 'docs/**'\n - '.github/workflows/ai-health.yml'\n\r\npermissions:\r\n contents: read\r\n\r\njobs:\r\n nightly:\r\n runs-on: ubuntu-latest\r\n timeout-minutes: 20\r\n steps:\r\n - name: Check gift links via pack endpoint\r\n run: |\r\n node - <<'JS'\r\n const fetch = global.fetch; // Node 20\r\n const PACK_URL = 'https://candidquality.github.io/Barkday/data/dog-gifts-merged.json';\n\r\n // Friendly headers help a lot with CDNs\r\n const UA = 'Barkday-CI/1.0 (+https://candidquality.github.io/Barkday/)';\r\n const HEADERS = { 'User-Agent': UA, 'Accept': '*/*', 'Accept-Language': 'en' };\r\n\r\n // Domains that commonly soft-block bots; warn instead of failing\r\n const SOFT_DOMAINS = [\r\n /(^|\\.)(amazon\\.com|m\\.media-amazon\\.com|images-na\\.ssl-images-amazon\\.com)$/i,\r\n /(^|\\.)(chewy\\.com|img\\.chewy\\.com|images\\.chewy\\.com)$/i,\r\n /(^|\\.)(cloudfront\\.net)$/i\r\n ];\r\n\r\n (async () => {\r\n // Load the live v2 runtime gifts feed published with Pages\n const res = await fetch(PACK_URL, { headers: { ...HEADERS, 'Accept': 'application/json' } });\n if (!res.ok) throw new Error('Fetch gifts failed: ' + res.status);\n const feed = await res.json();\n const gifts = Array.isArray(feed) ? feed : (Array.isArray(feed.items) ? feed.items : []);\n if (!gifts.length) throw new Error('Gift feed did not contain any items');\n\n // Collect unique URLs\n const urls = new Set();\n for (const g of gifts) {\n if (g.url) urls.add(g.url);\n if (g.affiliate && g.affiliate.url) urls.add(g.affiliate.url);\n if (g.image) urls.add(g.image);\n if (Array.isArray(g.images)) for (const u of g.images) urls.add(u);\n }\n const list = Array.from(urls).filter(u => /^https?:\\/\\//i.test(u));\n console.log(`Checking ${list.length} URLs from data/dog-gifts-merged.json…`);\n\r\n const MAX = 8; // concurrency\r\n const REQ_TIMEOUT = 15000; // per request\r\n let i = 0, hard = [], soft = [], okCount = 0;\r\n\r\n const isSoftDomain = (u) => {\r\n try { return SOFT_DOMAINS.some(rx => rx.test(new URL(u).hostname)); }\r\n catch { return false; }\r\n };\r\n\r\n async function probe(u, method) {\r\n const ctrl = new AbortController();\r\n const timer = setTimeout(() => ctrl.abort(), REQ_TIMEOUT);\r\n try {\r\n const res = await fetch(u, { method, redirect: 'follow', signal: ctrl.signal, headers: HEADERS });\r\n clearTimeout(timer);\r\n return res;\r\n } catch (e) {\r\n clearTimeout(timer);\r\n throw e;\r\n }\r\n }\r\n\r\n async function check(u) {\r\n const looksImage = /\\.(png|jpe?g|webp|gif|svg)(\\?|$)/i.test(u) || /images?-/.test(u);\r\n let res;\r\n try {\r\n // Prefer HEAD; but GET for images (some CDNs 405 on HEAD for binaries)\r\n res = await probe(u, looksImage ? 'GET' : 'HEAD');\r\n // Fallback to GET on method/security blocks\r\n if (!res.ok && [401,403,405].includes(res.status)) {\r\n res = await probe(u, 'GET');\r\n }\r\n } catch (e) {\r\n const rec = { url: u, status: 'ERR ' + (e.name || 'error') };\r\n (isSoftDomain(u) ? soft : hard).push(rec);\r\n return;\r\n }\r\n\r\n const okish = res.ok || (res.status >= 200 && res.status < 400);\r\n if (okish) { okCount++; return; }\r\n\r\n const rec = { url: u, status: res.status };\r\n (isSoftDomain(u) || res.status === 429 ? soft : hard).push(rec);\r\n }\r\n\r\n await Promise.all(\r\n Array.from({ length: Math.min(MAX, list.length) }, async () => {\r\n while (i < list.length) {\r\n const u = list[i++]; await check(u);\r\n }\r\n })\r\n );\r\n\r\n console.log(`OK: ${okCount}, soft-warn: ${soft.length}, hard-fail: ${hard.length}`);\r\n if (soft.length) {\r\n console.warn('Soft-blocked URLs (likely CDN anti-bot; not failing build):');\r\n for (const b of soft) console.warn('-', b.status, b.url);\r\n }\r\n if (hard.length) {\r\n console.error('Broken/blocked URLs:');\r\n for (const b of hard) console.error('-', b.status, b.url);\r\n process.exit(1);\r\n }\r\n console.log('All gift and image links look reachable.');\r\n })().catch(e => { console.error(e); process.exit(1); });\r\n JS\r\n","inline_bytes":5103,"content_sha256":"1d211034e2e61acbe441e5fba06bfa9b6734512b1d302fcdd2f3b16148dc95d5"},{"path":".github/workflows/ai-index.yml","size":2284,"sha":"61936536f95990f5a7050eb1cab9a1ef36e6372c","media_type":"text/yaml","raw_url":"https://raw.githubusercontent.com/CandidQuality/Barkday/9e38500d723ed1e8e1ca93eb8ae821c831f1414d/.github/workflows/ai-index.yml","html_url":"https://github.com/CandidQuality/Barkday/blob/9e38500d723ed1e8e1ca93eb8ae821c831f1414d/.github/workflows/ai-index.yml","inline_state":"full","max_inline_text_bytes":614400,"max_inline_bin_bytes":204800,"preview_text_bytes":65536,"encoding":"utf8","content":"name: Build AI Index and Packs\r\n\r\non:\r\n workflow_dispatch:\r\n push:\r\n branches: [ main ]\r\n # Prevent loops when we commit docs/*\r\n paths-ignore:\r\n - 'docs/**'\r\n\r\npermissions:\r\n contents: write\r\n\r\nconcurrency:\r\n group: ai-index-${{ github.ref }}\r\n cancel-in-progress: true\r\n\r\njobs:\r\n build:\r\n runs-on: ubuntu-latest\r\n steps:\r\n - name: Checkout\r\n uses: actions/checkout@v4\r\n with:\r\n fetch-depth: 0\r\n ref: main\r\n\r\n # Always sync to the latest main so our push is fast-forward\r\n - name: Sync to latest main\r\n run: |\r\n git fetch origin main\r\n git checkout main\r\n git pull --rebase origin main\r\n\r\n # Make sure the script sees the current HEAD sha (not the original GITHUB_SHA)\r\n - name: Set GITHUB_SHA to current HEAD\r\n run: echo \"GITHUB_SHA=$(git rev-parse HEAD)\" >> $GITHUB_ENV\r\n\r\n - name: Use Node\r\n uses: actions/setup-node@v4\r\n with:\r\n node-version: '20'\r\n\r\n - name: Build indices and packs\r\n run: node scripts/build_ai_index.mjs\r\n\r\n - name: Verify outputs exist\r\n run: |\r\n ls -la docs | sed -n '1,200p'\r\n test -f docs/ai-index.min.json\r\n test -f docs/ai-docs-list.min.json\r\n test -f docs/ai-pack-all.min.json\r\n test -f docs/ai-pack-everything.manifest.min.json\r\n test -f docs/ai-pack-catalog.min.json\r\n\r\n # Commit and robust push with a rebase retry if main moved again\r\n - name: Commit docs if changed\r\n run: |\r\n if [[ -n \"$(git status --porcelain docs)\" ]]; then\r\n git config user.name \"github-actions[bot]\"\r\n git config user.email \"41898282+github-actions[bot]@users.noreply.github.com\"\r\n git add -A docs\r\n git commit -m \"build(ai): regenerate ai-index and packs\"\r\n\r\n # Try push; if behind, rebase and retry\r\n for i in 1 2 3; do\r\n git push origin HEAD:main && break\r\n echo \"Push failed, rebasing onto origin/main (attempt $i)...\"\r\n git fetch origin main\r\n git rebase origin/main || { git rebase --abort; exit 1; }\r\n done\r\n else\r\n echo \"No changes in docs/\"\r\n fi\r\n\r\n\r\n","inline_bytes":2284,"content_sha256":"975dbc5cce94ccae2ceaaf5de1ab5f7c9b11f457c217dc72febbb40f5cf2720a"},{"path":".github/workflows/ai-pack-validate.yml","size":6675,"sha":"fb165b953073a07d07ebb165cf47808dc86084cb","media_type":"text/yaml","raw_url":"https://raw.githubusercontent.com/CandidQuality/Barkday/9e38500d723ed1e8e1ca93eb8ae821c831f1414d/.github/workflows/ai-pack-validate.yml","html_url":"https://github.com/CandidQuality/Barkday/blob/9e38500d723ed1e8e1ca93eb8ae821c831f1414d/.github/workflows/ai-pack-validate.yml","inline_state":"full","max_inline_text_bytes":614400,"max_inline_bin_bytes":204800,"preview_text_bytes":65536,"encoding":"utf8","content":"name: Validate AI Pack Endpoints\r\n\r\non:\r\n push:\r\n branches: [ main ]\r\n paths:\r\n - '.github/workflows/ai-pack-validate.yml'\r\n - 'docs/**'\r\n - 'scripts/build_ai_index.mjs'\r\n workflow_dispatch:\r\n schedule:\r\n - cron: '0 7 * * *' # daily 07:00 UTC\r\n\r\npermissions:\r\n contents: read\r\n\r\njobs:\r\n validate:\r\n runs-on: ubuntu-latest\r\n timeout-minutes: 10\r\n steps:\r\n - uses: actions/checkout@v4\r\n\r\n - uses: actions/setup-node@v4\r\n with:\r\n node-version: 20\r\n\r\n - name: Validate AI pack endpoints\r\n env:\r\n PAGES_OWNER: ${{ github.repository_owner }}\r\n REPO: ${{ github.repository }}\r\n FALLBACK_ROOT: https://candidquality.github.io/Barkday/\r\n run: |\r\n node - <<'JS'\r\n const fetch = global.fetch;\r\n\r\n const env = process.env;\r\n const owner = (env.PAGES_OWNER || '').toLowerCase();\r\n const repo = (env.REPO || '').split('/')[1] || 'Barkday';\r\n const fallback = env.FALLBACK_ROOT;\r\n const ROOT = `https://${owner}.github.io/${repo}/` || fallback;\r\n\r\n const ALL_URL = ROOT + 'docs/ai-pack-all.min.json';\r\n const MANI_URL = ROOT + 'docs/ai-pack-everything.manifest.min.json';\r\n\r\n const UA = 'Barkday-Validator/1.0 (+'+ROOT+')';\r\n const HEADERS = { 'User-Agent': UA, 'Accept': 'application/json' };\r\n const ok = r => r.ok || (r.status >= 200 && r.status < 400);\r\n\r\n (async () => {\r\n console.log('Root:', ROOT);\r\n\r\n // 1) Combined pack\r\n const allRes = await fetch(ALL_URL, { headers: HEADERS, cache: 'no-store' });\r\n if (!ok(allRes)) throw new Error(`combined pack fetch failed: ${allRes.status} ${ALL_URL}`);\r\n const allJson = await allRes.json();\r\n if (!allJson || typeof allJson !== 'object' || !allJson.sections) {\r\n throw new Error('combined pack: missing top-level \"sections\"');\r\n }\r\n console.log('✓ combined pack OK');\r\n\r\n // 2) Manifest\r\n const maniRes = await fetch(MANI_URL, { headers: HEADERS, cache: 'no-store' });\r\n if (!ok(maniRes)) throw new Error(`manifest fetch failed: ${maniRes.status} ${MANI_URL}`);\r\n const manifest = await maniRes.json();\r\n\r\n // Allow either {shards:[...]} or an array directly\r\n const shards = Array.isArray(manifest) ? manifest : (manifest.shards || []);\r\n if (!Array.isArray(shards) || !shards.length) {\r\n throw new Error('manifest: no shards found');\r\n }\r\n\r\n // Accept your field names AND legacy variants\r\n const getJsonURL = (it) =>\r\n it.url_json || it.json || it.href_json || it.urlJson || it.url;\r\n const getTxtURL = (it) =>\r\n it.url_txt || it.txt || it.href_txt || it.urlTxt;\r\n\r\n // Probe a few shards to keep runtime small\r\n const N = Math.min(5, shards.length);\r\n for (let i = 0; i < N; i++) {\r\n const it = shards[i];\r\n const j = getJsonURL(it);\r\n const t = getTxtURL(it);\r\n if (!j || !t) {\r\n throw new Error(`manifest shard #${i} unexpected shape: ${JSON.stringify(it)}`);\r\n }\r\n const absJ = j.startsWith('http') ? j : (ROOT + j.replace(/^\\//,''));\r\n const res = await fetch(absJ, { headers: HEADERS, cache: 'no-store' });\r\n if (!ok(res)) throw new Error(`shard #${i} fetch failed: ${res.status} ${absJ}`);\r\n const shard = await res.json();\r\n if (!shard || typeof shard !== 'object' || (!('sections' in shard) && !('items' in shard))) {\r\n throw new Error(`shard #${i} unexpected JSON structure`);\r\n }\r\n }\r\n\r\n console.log(`✓ manifest OK (${shards.length} shard entries; sampled ${N})`);\r\n })().catch(e => { console.error(e.stack || e); process.exit(1); });\r\n JS\r\n\r\n - name: Pack shape lint (non-blocking)\r\n continue-on-error: true\r\n env:\r\n PAGES_OWNER: ${{ github.repository_owner }}\r\n REPO: ${{ github.repository }}\r\n run: |\r\n node - <<'JS'\r\n const fetch = global.fetch;\r\n const owner = (process.env.PAGES_OWNER || '').toLowerCase();\r\n const repo = (process.env.REPO || '').split('/')[1] || 'Barkday';\r\n const ROOT = `https://${owner}.github.io/${repo}/`;\r\n const ALL_URL = ROOT + 'docs/ai-pack-all.min.json';\r\n const MANI_URL = ROOT + 'docs/ai-pack-everything.manifest.min.json';\r\n const HEADERS = { 'User-Agent': 'Barkday-Pack-Lint/1.0', 'Accept': 'application/json' };\r\n const ok = r => r.ok || (r.status >= 200 && r.status < 400);\r\n\r\n (async () => {\r\n // Combined pack presence & v2 runtime gift-feed hint\n const allRes = await fetch(ALL_URL, { headers: HEADERS });\n if (!ok(allRes)) { console.warn('⚠️ combined pack fetch:', allRes.status); return; }\n const all = await allRes.json();\n const sections = all.sections || {};\n const hasGifts = Object.values(sections).some(s => (s.items||[]).some(it =>\n (it.path || '').includes('data/dog-gifts-merged.json')));\n console.log(hasGifts ? '✓ combined pack contains data/dog-gifts-merged.json'\n : '⚠️ combined pack: data/dog-gifts-merged.json not found; maybe only in shards');\n\r\n // Approx total shard size (HEAD)\r\n const maniRes = await fetch(MANI_URL, { headers: HEADERS });\r\n if (!ok(maniRes)) { console.warn('⚠️ manifest fetch:', maniRes.status); return; }\r\n const manifest = await maniRes.json();\r\n const shards = Array.isArray(manifest) ? manifest : (manifest.shards || []);\r\n let approxBytes = 0;\r\n for (const it of shards) {\r\n const j = it.url_json || it.json || it.href_json || it.urlJson || it.url;\r\n if (!j) continue;\r\n const absJ = j.startsWith('http') ? j : (ROOT + j.replace(/^\\//,''));\r\n const head = await fetch(absJ, { method: 'HEAD' });\r\n const len = +(head.headers.get('content-length') || 0);\r\n approxBytes += len;\r\n }\r\n console.log(`Manifest shards listed: ${shards.length}`);\r\n console.log(`Approx shard bytes: ${approxBytes.toLocaleString()} (non-blocking hint)`);\r\n })().catch(e => { console.warn('lint error (non-blocking):', e.message); });\r\n JS\r\n","inline_bytes":6657,"content_sha256":"f61947719048fdae9c6fdee1cbc5a2ad6644457db17b87f267a09e0ec7330e05"},{"path":".github/workflows/codeql.yml","size":814,"sha":"2896a7ef9c833fb4f02588808f7958a8a3a60d27","media_type":"text/yaml","raw_url":"https://raw.githubusercontent.com/CandidQuality/Barkday/9e38500d723ed1e8e1ca93eb8ae821c831f1414d/.github/workflows/codeql.yml","html_url":"https://github.com/CandidQuality/Barkday/blob/9e38500d723ed1e8e1ca93eb8ae821c831f1414d/.github/workflows/codeql.yml","inline_state":"full","max_inline_text_bytes":614400,"max_inline_bin_bytes":204800,"preview_text_bytes":65536,"encoding":"utf8","content":"name: CodeQL\r\n\r\non:\r\n push:\r\n branches: [ main ]\r\n pull_request:\r\n schedule:\r\n - cron: \"0 6 * * 1\"\r\n\r\npermissions:\r\n contents: read\r\n security-events: write\r\n\r\njobs:\r\n analyze:\r\n runs-on: ubuntu-latest\r\n permissions:\r\n actions: read\r\n contents: read\r\n security-events: write\r\n strategy:\r\n fail-fast: false\r\n matrix:\r\n language: [ \"javascript\" ]\r\n steps:\r\n - uses: actions/checkout@v4\r\n - name: Initialize CodeQL\r\n uses: github/codeql-action/init@v3\r\n with:\r\n languages: ${{ matrix.language }}\r\n - name: Autobuild\r\n uses: github/codeql-action/autobuild@v3\r\n - name: Perform CodeQL Analysis\r\n uses: github/codeql-action/analyze@v3\r\n with:\r\n category: \"/language:${{ matrix.language }}\"\r\n","inline_bytes":814,"content_sha256":"b43f9107fe5f6ca134c917be373a0377d958fdec99912faec3ec5e9254b32903"},{"path":".github/workflows/data-health.yml","size":1614,"sha":"81e175f81d8198ab01b8ee93d9f573c9afd4472f","media_type":"text/yaml","raw_url":"https://raw.githubusercontent.com/CandidQuality/Barkday/9e38500d723ed1e8e1ca93eb8ae821c831f1414d/.github/workflows/data-health.yml","html_url":"https://github.com/CandidQuality/Barkday/blob/9e38500d723ed1e8e1ca93eb8ae821c831f1414d/.github/workflows/data-health.yml","inline_state":"full","max_inline_text_bytes":614400,"max_inline_bin_bytes":204800,"preview_text_bytes":65536,"encoding":"utf8","content":"name: Data health checks (non-blocking)\r\n\r\non:\r\n schedule:\r\n - cron: \"23 4 * * *\" # daily ~04:23 UTC\r\n pull_request:\r\n paths:\r\n - 'data/**/*.json'\r\n workflow_dispatch: {}\r\n\r\npermissions:\r\n contents: read\r\n\r\njobs:\r\n json-lint:\r\n runs-on: ubuntu-latest\r\n steps:\r\n - uses: actions/checkout@v4\r\n\r\n - name: Lint all data/*.json with jq\r\n run: |\r\n set -e\r\n shopt -s nullglob\r\n files=(data/*.json)\r\n if [ ${#files[@]} -eq 0 ]; then\r\n echo \"No data/*.json files to check\"; exit 0\r\n fi\r\n for f in \"${files[@]}\"; do\r\n echo \"Checking $f\"\r\n jq . \"$f\" >/dev/null\r\n done\r\n\r\n - name: Sanity checks (Node)\r\n run: |\r\n node - <<'JS'\r\n const fs = require('fs');\r\n function read(p){ return JSON.parse(fs.readFileSync(p,'utf8')); }\r\n try {\r\n const curves = read('data/curves.json');\r\n if (!curves || !curves.curves || !Object.keys(curves.curves).length) {\r\n throw new Error('data/curves.json missing \"curves\" map');\r\n }\r\n } catch (e) {\r\n console.error(e.message); process.exit(1);\r\n }\r\n try {\r\n const groups = read('data/breed_groups.json');\r\n if (!Array.isArray(groups) || !groups.length) {\r\n throw new Error('data/breed_groups.json should be a non-empty array');\r\n }\r\n } catch (e) {\r\n console.error(e.message); process.exit(1);\r\n }\r\n console.log('Data sanity OK');\r\n JS\r\n","inline_bytes":1614,"content_sha256":"44a2e4f79898457e7f02a9f565bc3ddae9e797576ffed180523cbcad8a9484dd"},{"path":".github/workflows/doc-lint.yml","size":653,"sha":"4a769296ad5c11c9998e6015edabf5c07e35b345","media_type":"text/yaml","raw_url":"https://raw.githubusercontent.com/CandidQuality/Barkday/9e38500d723ed1e8e1ca93eb8ae821c831f1414d/.github/workflows/doc-lint.yml","html_url":"https://github.com/CandidQuality/Barkday/blob/9e38500d723ed1e8e1ca93eb8ae821c831f1414d/.github/workflows/doc-lint.yml","inline_state":"full","max_inline_text_bytes":614400,"max_inline_bin_bytes":204800,"preview_text_bytes":65536,"encoding":"utf8","content":"name: Lint Docs for Policy Regressions\r\non:\r\n push:\r\n branches: [ main ]\r\n paths: [ 'docs/**/*.md', '.github/workflows/doc-lint.yml' ]\r\n pull_request:\r\n paths: [ 'docs/**/*.md' ]\r\n workflow_dispatch: {}\r\n\r\npermissions: { contents: read }\r\n\r\njobs:\r\n lint:\r\n runs-on: ubuntu-latest\r\n steps:\r\n - uses: actions/checkout@v4\r\n - name: Fail on forbidden phrases\r\n run: |\r\n set -e\r\n if rg -n -i 'gifts?[^\\\\n]{0,80}(gated|paywall)' docs/*.md; then\r\n echo \"::error::Found forbidden phrasing (gifts cannot be gated/paywalled).\"\r\n exit 1\r\n fi\r\n echo \"Docs lint passed.\"\r\n","inline_bytes":653,"content_sha256":"84f1518031dca38b7cd5c64a04d7386c1622912b34f345fc687fb01156781a61"},{"path":".github/workflows/lighthouse.yml","size":951,"sha":"600221c05ea856843caa256f46565cbe7bfa1b43","media_type":"text/yaml","raw_url":"https://raw.githubusercontent.com/CandidQuality/Barkday/9e38500d723ed1e8e1ca93eb8ae821c831f1414d/.github/workflows/lighthouse.yml","html_url":"https://github.com/CandidQuality/Barkday/blob/9e38500d723ed1e8e1ca93eb8ae821c831f1414d/.github/workflows/lighthouse.yml","inline_state":"full","max_inline_text_bytes":614400,"max_inline_bin_bytes":204800,"preview_text_bytes":65536,"encoding":"utf8","content":"name: Lighthouse CI\r\n\r\non:\r\n push:\r\n branches: [ main ]\r\n pull_request:\r\n workflow_dispatch:\r\n\r\npermissions:\r\n contents: read\r\n\r\njobs:\r\n lhci:\r\n runs-on: ubuntu-latest\r\n steps:\r\n - uses: actions/checkout@v4\r\n with:\r\n fetch-depth: 0\r\n\r\n - name: Ensure lighthouserc.json exists\r\n run: |\r\n test -f lighthouserc.json || { echo \"::error::Missing lighthouserc.json at repo root\"; exit 1; }\r\n ls -la lighthouserc.json\r\n\r\n - uses: actions/setup-node@v4\r\n with:\r\n node-version: 20\r\n\r\n - name: Install Lighthouse CI\r\n run: npm i -g @lhci/cli\r\n\r\n - name: Run Lighthouse CI\r\n # Token is optional; enables PR status checks (gets rid of “GitHub token not set” message)\r\n env:\r\n LHCI_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\r\n GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\r\n run: lhci autorun --config=./lighthouserc.json\r\n","inline_bytes":947,"content_sha256":"b38b52b72d716e2886e1f1ba0bb6b84b135739231ceba2c7234a09f9375cdf88"},{"path":".github/workflows/link-check.yml","size":646,"sha":"2b93b719578792d67327045d39695ca4150f7999","media_type":"text/yaml","raw_url":"https://raw.githubusercontent.com/CandidQuality/Barkday/9e38500d723ed1e8e1ca93eb8ae821c831f1414d/.github/workflows/link-check.yml","html_url":"https://github.com/CandidQuality/Barkday/blob/9e38500d723ed1e8e1ca93eb8ae821c831f1414d/.github/workflows/link-check.yml","inline_state":"full","max_inline_text_bytes":614400,"max_inline_bin_bytes":204800,"preview_text_bytes":65536,"encoding":"utf8","content":"name: Link check (site & docs)\r\non:\r\n push: { branches: [ main ] }\r\n pull_request:\r\n schedule: [ { cron: \"23 4 * * 1\" } ] # weekly\r\npermissions: { contents: read }\r\njobs:\r\n lychee:\r\n runs-on: ubuntu-latest\r\n steps:\r\n - uses: actions/checkout@v4\r\n - name: Check links with lychee\r\n uses: lycheeverse/lychee-action@v1\r\n with:\r\n args: >-\r\n --verbose --no-progress\r\n --accept 200,206,301,302,307,308\r\n --exclude-mail\r\n --timeout 20\r\n **/*.md **/*.html\r\n !node_modules/**\r\n env:\r\n GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\r\n","inline_bytes":646,"content_sha256":"7771f010690303e9ba82f4de996582e2330e09f49767eb0a8d17192f2068ba98"},{"path":".github/workflows/markdown-lint.yml","size":854,"sha":"f88996b4cac9fe7d1ca446f57868689166910706","media_type":"text/yaml","raw_url":"https://raw.githubusercontent.com/CandidQuality/Barkday/9e38500d723ed1e8e1ca93eb8ae821c831f1414d/.github/workflows/markdown-lint.yml","html_url":"https://github.com/CandidQuality/Barkday/blob/9e38500d723ed1e8e1ca93eb8ae821c831f1414d/.github/workflows/markdown-lint.yml","inline_state":"full","max_inline_text_bytes":614400,"max_inline_bin_bytes":204800,"preview_text_bytes":65536,"encoding":"utf8","content":"name: Lint Markdown\r\n\r\non:\r\n push:\r\n branches: [main]\r\n paths:\r\n - \"**/*.md\"\r\n - \".markdownlint-cli2.yaml\"\r\n - \".github/workflows/markdown-lint.yml\"\r\n pull_request:\r\n paths:\r\n - \"**/*.md\"\r\n - \".markdownlint-cli2.yaml\"\r\n - \".github/workflows/markdown-lint.yml\"\r\n\r\npermissions:\r\n contents: read\r\n\r\njobs:\r\n markdown:\r\n runs-on: ubuntu-latest\r\n steps:\r\n - uses: actions/checkout@v4\r\n\r\n - name: Markdown lint\r\n uses: DavidAnson/markdownlint-cli2-action@v16\r\n with:\r\n globs: |\r\n **/*.md\r\n !node_modules/**\r\n !dist/**\r\n !build/**\r\n\r\n - name: Spellcheck\r\n uses: codespell-project/actions-codespell@v2\r\n with:\r\n check_filenames: true\r\n path: .\r\n ignore_words_list: Barkday,chewer\r\n ","inline_bytes":854,"content_sha256":"99216c58d511f09af9392fa6b53ec8b06c7a0cebec03de3c96692d9fdc190b3b"},{"path":".github/workflows/pages.yml","size":947,"sha":"838b2898fa913de9925ae7e3d2d16c4db136f4d3","media_type":"text/yaml","raw_url":"https://raw.githubusercontent.com/CandidQuality/Barkday/9e38500d723ed1e8e1ca93eb8ae821c831f1414d/.github/workflows/pages.yml","html_url":"https://github.com/CandidQuality/Barkday/blob/9e38500d723ed1e8e1ca93eb8ae821c831f1414d/.github/workflows/pages.yml","inline_state":"full","max_inline_text_bytes":614400,"max_inline_bin_bytes":204800,"preview_text_bytes":65536,"encoding":"utf8","content":"name: Publish Barkday page\r\n\r\non:\r\n push:\r\n branches: [ main ]\r\n workflow_dispatch:\r\n\r\npermissions:\r\n contents: read\r\n pages: write\r\n id-token: write\r\n\r\nconcurrency:\r\n group: \"pages\"\r\n cancel-in-progress: true\r\n\r\njobs:\r\n build:\r\n runs-on: ubuntu-latest\r\n steps:\r\n - uses: actions/checkout@v4\r\n\r\n # Build the AI indices (master + docs list + data list)\r\n - uses: actions/setup-node@v4\r\n with:\r\n node-version: 20\r\n - name: Build AI indices\r\n run: node scripts/build_ai_index.mjs\r\n\r\n # Upload everything in the repo root (includes /docs/*)\r\n - name: Upload artifact\r\n uses: actions/upload-pages-artifact@v4\r\n with:\r\n path: .\r\n\r\n deploy:\r\n environment:\r\n name: github-pages\r\n url: ${{ steps.deployment.outputs.page_url }}\r\n runs-on: ubuntu-latest\r\n needs: build\r\n steps:\r\n - id: deployment\r\n uses: actions/deploy-pages@v4\r\n","inline_bytes":947,"content_sha256":"f40fd0f176b3621ca695e448abccfc9d8437bb3acc0116f89f9b04d5c098ed1a"},{"path":".github/workflows/validate-json.yml","size":2029,"sha":"686e1a7ce6b9f58fd2892fc9be1b90213fe9da8e","media_type":"text/yaml","raw_url":"https://raw.githubusercontent.com/CandidQuality/Barkday/9e38500d723ed1e8e1ca93eb8ae821c831f1414d/.github/workflows/validate-json.yml","html_url":"https://github.com/CandidQuality/Barkday/blob/9e38500d723ed1e8e1ca93eb8ae821c831f1414d/.github/workflows/validate-json.yml","inline_state":"full","max_inline_text_bytes":614400,"max_inline_bin_bytes":204800,"preview_text_bytes":65536,"encoding":"utf8","content":"name: Validate Barkday gift catalog\n\non:\n push:\n branches: [ main ]\n paths:\n - 'app.js'\n - 'data/dog-gifts-base.json'\n - 'data/dog-gifts-catalog.json'\n - 'data/dog-gifts-merged.json'\n - 'data/dog-gifts-preferred.json'\n - 'data/gift-batches/*.json'\n - 'schema/dog-gifts-*.json'\n - 'scripts/rebuild-gift-catalog.mjs'\n - 'scripts/validate-gifts-v2.mjs'\n - 'scripts/merge-gifts-v2.mjs'\n - 'scripts/smoke-gift-logic.mjs'\n - 'scripts/barkday-doctor.mjs'\n - '.github/workflows/validate-json.yml'\n pull_request:\n paths:\n - 'app.js'\n - 'data/dog-gifts-base.json'\n - 'data/dog-gifts-catalog.json'\n - 'data/dog-gifts-merged.json'\n - 'data/dog-gifts-preferred.json'\n - 'data/gift-batches/*.json'\n - 'schema/dog-gifts-*.json'\n - 'scripts/rebuild-gift-catalog.mjs'\n - 'scripts/validate-gifts-v2.mjs'\n - 'scripts/merge-gifts-v2.mjs'\n - 'scripts/smoke-gift-logic.mjs'\n - 'scripts/barkday-doctor.mjs'\n - '.github/workflows/validate-json.yml'\n workflow_dispatch: {}\n\npermissions:\n contents: read\n\njobs:\n validate:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n\n - name: Setup Node\n uses: actions/setup-node@v4\n with:\n node-version: '22'\n\n - name: Check JavaScript syntax\n run: |\n node --check ./app.js\n node --check ./scripts/barkday-doctor.mjs\n\n - name: Rebuild v2 gift catalog\n run: node ./scripts/rebuild-gift-catalog.mjs\n\n - name: Validate v2 gift catalog\n run: node ./scripts/validate-gifts-v2.mjs ./data/dog-gifts-catalog.json ./data/dog-gifts-preferred.json\n\n - name: Merge v2 runtime gift feed\n run: node ./scripts/merge-gifts-v2.mjs ./data/dog-gifts-catalog.json ./data/dog-gifts-preferred.json ./data/dog-gifts-merged.json\n\n - name: Smoke gift logic\n run: node ./scripts/smoke-gift-logic.mjs\n\n - name: Doctor\n run: node ./scripts/barkday-doctor.mjs\n","inline_bytes":2029,"content_sha256":"f82d1a62b6f33a13998ab4a51bc9399a26433dcdb22c5b9bcf926995856be9f4"},{"path":".gitignore","size":54,"sha":"941f0d42d681ad512527689c0eeefdbdc8018533","media_type":"application/octet-stream","raw_url":"https://raw.githubusercontent.com/CandidQuality/Barkday/9e38500d723ed1e8e1ca93eb8ae821c831f1414d/.gitignore","html_url":"https://github.com/CandidQuality/Barkday/blob/9e38500d723ed1e8e1ca93eb8ae821c831f1414d/.gitignore","inline_state":"full","max_inline_text_bytes":614400,"max_inline_bin_bytes":204800,"preview_text_bytes":65536,"encoding":"base64","content":"IyBBaWRlciBsb2NhbCBhcnRpZmFjdHMKLmFpZGVyKgouYWlkZXIudGFncy5jYWNoZS52NC8K","inline_bytes":72,"content_sha256":"2d0b27ad0efb9f0f4e976221282c876cbb660af353a6ae2ba13e8d3b5741b4d0"},{"path":".markdownlint-cli2.yaml","size":421,"sha":"ba874de500893ad4adcc31558b1d1cc6d0c11aab","media_type":"text/yaml","raw_url":"https://raw.githubusercontent.com/CandidQuality/Barkday/9e38500d723ed1e8e1ca93eb8ae821c831f1414d/.markdownlint-cli2.yaml","html_url":"https://github.com/CandidQuality/Barkday/blob/9e38500d723ed1e8e1ca93eb8ae821c831f1414d/.markdownlint-cli2.yaml","inline_state":"full","max_inline_text_bytes":614400,"max_inline_bin_bytes":204800,"preview_text_bytes":65536,"encoding":"utf8","content":"globs:\n - \"**/*.md\"\n - \"!node_modules/**\"\n - \"!dist/**\"\n - \"!build/**\"\n\nconfig:\n default: true\n\n # Barkday docs are operational documents, not prose manuscripts.\n MD013: false # line-length\n MD024:\n siblings_only: true # allow same heading text in different sections\n MD033: false # inline HTML allowed when needed\n MD036: false # allow emphasis-as-heading style where already used","inline_bytes":421,"content_sha256":"0aa2cf2bde041facd05af3fc93121b6a3499aae117b029228bf71dbc5f5c0493"},{"path":".nojekyll","size":1,"sha":"8b137891791fe96927ad78e64b0aad7bded08bdc","media_type":"application/octet-stream","raw_url":"https://raw.githubusercontent.com/CandidQuality/Barkday/9e38500d723ed1e8e1ca93eb8ae821c831f1414d/.nojekyll","html_url":"https://github.com/CandidQuality/Barkday/blob/9e38500d723ed1e8e1ca93eb8ae821c831f1414d/.nojekyll","inline_state":"full","max_inline_text_bytes":614400,"max_inline_bin_bytes":204800,"preview_text_bytes":65536,"encoding":"base64","content":"Cg==","inline_bytes":4,"content_sha256":"01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b"},{"path":"404.html","size":540,"sha":"fcde30413f551ba2b35bbce1d78cbbc03768fac1","media_type":"text/html","raw_url":"https://raw.githubusercontent.com/CandidQuality/Barkday/9e38500d723ed1e8e1ca93eb8ae821c831f1414d/404.html","html_url":"https://github.com/CandidQuality/Barkday/blob/9e38500d723ed1e8e1ca93eb8ae821c831f1414d/404.html","inline_state":"full","max_inline_text_bytes":614400,"max_inline_bin_bytes":204800,"preview_text_bytes":65536,"encoding":"utf8","content":"\n\n
\n \n \nWe couldn’t find that page.
\n \n\n\n","inline_bytes":532,"content_sha256":"22a2091c642cf3cd50af8c08c881768451f464a436f621d6d41b41ef11b179b9"},{"path":"AGENTS.md","size":15362,"sha":"7e01e30fb119611ddc97dd0a1c07e1a1f07def37","media_type":"text/markdown","raw_url":"https://raw.githubusercontent.com/CandidQuality/Barkday/9e38500d723ed1e8e1ca93eb8ae821c831f1414d/AGENTS.md","html_url":"https://github.com/CandidQuality/Barkday/blob/9e38500d723ed1e8e1ca93eb8ae821c831f1414d/AGENTS.md","inline_state":"full","max_inline_text_bytes":614400,"max_inline_bin_bytes":204800,"preview_text_bytes":65536,"encoding":"utf8","content":"# Barkday repository instructions for local agent work\n\nLast updated: 2026-05-03\n\nThis file is the operating contract for all local agent work in this repository.\n\nRead this file before changing anything.\n\nIf a task touches files under `data/`, also read `data/AGENTS.md` before making changes.\n\n## 1) Project purpose\n\nBarkday is a public-facing dog age and Barkday planning application with:\n\n- a browser-based application shell\n- a mobile store wrapper path using Trusted Web Activity style behavior\n- runtime-fetched data\n- a public recommendation layer\n- a future premium and proprietary overlay layer\n\nThe immediate goal is launch readiness, not experimental churn.\n\nWork should reduce ambiguity, reduce dual-system drift, improve validation, and move the project toward a stable release.\n\n## 2) Non-negotiable operating principles\n\n1. Do not guess. \n If inputs are missing, contradictory, or ambiguous, stop and explain the gap instead of inventing structure or behavior.\n\n2. Do not silently choose the wrong source of truth. \n This repository currently contains both legacy and newer v2 data paths. Verify which one is canonical for the task before editing files.\n\n3. Do not widen task scope without reason. \n Make the smallest change that fully solves the requested problem.\n\n4. Do not clean up unrelated files. \n No drive-by refactors, renames, formatting churn, or dependency changes unless explicitly requested or required for the task.\n\n5. Do not break the release path. \n Any change that touches runtime data loading, gift feeds, caching, service worker behavior, or affiliate links must be validated before commit.\n\n6. Do not violate affiliate or platform rules. \n Amazon Associates compliance and mobile wrapper behavior are part of the product contract, not optional polish.\n\n7. Do not ship proprietary data inside the app shell if it is intended to remain remote-only. \n Public shell and remotely fetched data are intentional architectural boundaries.\n\n8. Re-verify against live-access links after meaningful changes. \n This repository includes generated live access indices and raw file links specifically so changes can be compared against live build state.\n\n## 3) Current repository reality\n\nThe repository currently contains both:\n\n- a legacy root-level gift feed system:\n - `dog-gifts.json`\n - `dog-gifts.schema.json`\n\n- a newer v2 catalog pipeline:\n - `data/dog-gifts-base.json`\n - `data/dog-gifts-catalog.json`\n - `data/dog-gifts-merged.json`\n - `data/dog-gifts-preferred.json`\n - `schema/dog-gifts-catalog.schema.v2.json`\n - `schema/dog-gifts-preferred.schema.v2.json`\n - `scripts/rebuild-gift-catalog.mjs`\n - `scripts/merge-gifts-v2.mjs`\n - `scripts/validate-gifts-v2.mjs`\n\nThe application shell is still centered around:\n\n- `index.html`\n- `app.js`\n- `style.css`\n- `service-worker.js`\n- `js/runtime-fetch.js`\n\nThe application codebase is still largely monolithic in `app.js`.\n\nThis repository also includes:\n\n- many GitHub workflows under `.github/workflows/`\n- local git hooks under `.githooks/`\n- a docs area with notes, decisions, maintainers, and generated AI index files\n- PowerShell tools under `tools/`\n- raw Amazon search and item fetch files under `data/raw-amazon/`\n\nTreat the current state as a migration-in-progress, not a finished architecture.\n\n## 4) Architectural direction that must guide work\n\nThe intended end-state is:\n\n- The installed or wrapped app is the shell only.\n- Changeable public product and recommendation data is fetched remotely at runtime.\n- Proprietary or premium data stays outside the shipped app and is fetched separately.\n- Weekly or frequent gift updates must not require a full app update.\n- The old flat gift feed is legacy and should not be expanded further unless a temporary compatibility export is explicitly required.\n\nThis means:\n\n- the long-term canonical gift model is the v2 structured feed\n- remote feed hosting is acceptable and preferred for public runtime gift data\n- proprietary logic and overlays must remain remote when intended\n- the app shell must normalize remote data safely\n\n## 5) Compliance rules that are binding on engineering work\n\nAmazon-related work must follow these rules:\n\n1. Relationship disclosures must remain visible where required.\n2. All affiliate links must remain proper Special Links with the correct tag and any approved subtags.\n3. No price-tracking history, price-alert features, or similar prohibited behaviors.\n4. If price or availability is shown, freshness, timestamp, and disclaimer requirements must be met.\n5. Do not store Amazon image binaries.\n6. Non-image Amazon Program Content must not be cached longer than 24 hours.\n7. Client apps must not cache Amazon Program Content offline.\n8. Mobile wrapper behavior must open Amazon externally, not inside embedded WebViews.\n9. No scraping reviews or star ratings from Amazon pages.\n10. Do not use Amazon Program Content or Special Link data to train or fine-tune machine learning systems.\n11. Do not introduce pop-up, framed, cloaked, or misleading affiliate placements.\n12. Keep regional host correctness in mind for links when regional logic is added.\n\nWhen in doubt, preserve compliance first and convenience second.\n\nReference documents already present in project context:\n\n- `AMAZON_ASSOCIATES_COMPLIANCE.md`\n- the current live access note for Barkday raw and index links\n\n## 6) Source-of-truth map\n\n### 6.1 Application shell and runtime behavior\n\nPrimary files:\n\n- `index.html`\n- `app.js`\n- `style.css`\n- `service-worker.js`\n- `js/runtime-fetch.js`\n\n### 6.2 Gift data system\n\nCanonical direction:\n\n- v2 data under `data/` and `schema/`\n\nKey files:\n\n- `data/dog-gifts-base.json`\n- `data/dog-gifts-catalog.json`\n- `data/dog-gifts-merged.json`\n- `data/dog-gifts-preferred.json`\n- `schema/dog-gifts-catalog.schema.v2.json`\n- `schema/dog-gifts-preferred.schema.v2.json`\n\nLegacy files:\n\n- `dog-gifts.json`\n- `dog-gifts.schema.json`\n\nLegacy files may still be referenced by old code. Do not assume they are canonical.\n\n### 6.3 Data import and build pipeline\n\nRelevant scripts:\n\n- `scripts/amazon-getitems-to-catalog.mjs`\n- `scripts/build-auto-batch-from-getitems.mjs`\n- `scripts/rebuild-gift-catalog.mjs`\n- `scripts/merge-gifts-v2.mjs`\n- `scripts/validate-gifts-v2.mjs`\n- `scripts/validate-gifts.mjs`\n- `scripts/smoke-gift-logic.mjs`\n- `scripts/barkday-doctor.mjs`\n\nPowerShell helpers:\n\n- `tools/amazon-get-items.ps1`\n- `tools/amazon-search-items.ps1`\n- `tools/run-barkday-checks.ps1`\n- `tools/verify.ps1`\n\n### 6.4 Live access and verification\n\nThis repository has generated live-access support through:\n\n- `docs/ai-index.min.json`\n- `docs/ai-pack-core.txt`\n- `docs/ai-pack-data-config.txt`\n- `docs/ai-pack-everything.manifest.min.json`\n\nWhen a task changes public behavior, verify using local files first, then compare to live-access references before declaring the task complete.\n\n## 7) Current known issues and cautions\n\n1. There are two gift systems in parallel. Do not add new product work to the wrong one.\n2. `data/gift-batches/2026-04-21-batch-07.json` appears suspicious and should be inspected before being trusted.\n3. The old root gift schema and the newer v2 schemas are both present. Do not mix their assumptions.\n4. `app.js` is large and contains many concerns. Avoid broad refactors unless specifically requested.\n5. Raw Amazon import artifacts are numerous. They are useful as evidence and inputs, but they are not the publishable feed.\n6. Do not hand-edit generated output files when the correct change belongs in source inputs or generator scripts.\n\n## 8) Task classification rules\n\nBefore starting work, classify the task.\n\n### Type A - application shell task\n\nExamples:\n\n- loader changes\n- rendering changes\n- filter logic\n- service worker behavior\n- external link behavior\n- wrapper-related logic\n\nRead first:\n\n- this file\n- relevant code files\n- live access references if the task affects deployed behavior\n\n### Type B - gift data model or pipeline task\n\nExamples:\n\n- catalog field changes\n- import logic\n- batch processing\n- v2 schema work\n- canonical feed rules\n- dedupe or merge logic\n\nRead first:\n\n- this file\n- `data/AGENTS.md`\n- relevant scripts and schemas\n\n### Type C - compliance-sensitive task\n\nExamples:\n\n- affiliate links\n- product cards\n- price or availability\n- data refresh behavior\n- mobile wrapper opening behavior\n- disclosures\n\nRead first:\n\n- this file\n- compliance guidance\n- any relevant UI or service-worker files\n\n### Type D - infrastructure or publishing task\n\nExamples:\n\n- GitHub workflows\n- pages publishing\n- AI index generation\n- validation workflow changes\n- deployment and verification improvements\n\nRead first:\n\n- this file\n- relevant workflow files\n- any existing docs in `docs/decisions/` or `docs/`\n\n## 9) Required work habits for Codex or any local agent\n\n1. Read before edit. \n Open the target file and any immediately related file before writing changes.\n\n2. Prefer source fixes over output edits. \n If a generated file is wrong, fix the source file or generator unless a direct output edit is explicitly intended.\n\n3. Preserve file roles. \n Do not convert a generated file into a hand-maintained file without stating that architectural change clearly.\n\n4. Keep diffs reviewable. \n Break work into focused commits where possible.\n\n5. Explain risk before risky edits. \n If a task touches:\n\n - `app.js`\n - `service-worker.js`\n - gift schema contracts\n - affiliate link logic\n - publishing behavior\n\n summarize the risk before making broad edits.\n\n6. Re-run validations after changes. \n No \"looks right\" completions.\n\n7. Re-verify live-access paths after relevant public changes. \n The project owner explicitly wants work grounded in the live file references when needed.\n\n## 10) Validation commands\n\nRun the commands that match the task.\n\n### 10.1 Core gift pipeline validation\n\nFrom repo root:\n\n`node .\\scripts\\rebuild-gift-catalog.mjs`\n\n`node .\\scripts\\validate-gifts-v2.mjs .\\data\\dog-gifts-catalog.json .\\data\\dog-gifts-preferred.json`\n\n`node .\\scripts\\merge-gifts-v2.mjs .\\data\\dog-gifts-catalog.json .\\data\\dog-gifts-preferred.json .\\data\\dog-gifts-merged.json`\n\n`node .\\scripts\\smoke-gift-logic.mjs`\n\n`node .\\scripts\\barkday-doctor.mjs`\n\n### 10.2 Local verification helper\n\nIf maintained and applicable:\n\n`.\\tools\\run-barkday-checks.ps1`\n\n### 10.3 Manual browser validation for application shell tasks\n\nWhen `app.js`, `index.html`, `style.css`, `service-worker.js`, or runtime data loading is changed:\n\n- open the app locally\n- test gift loading\n- test at least one puppy case\n- test at least one senior case\n- test at least one toy-sized case\n- test at least one giant-sized case\n- test each chewer level if gift logic is touched\n- test ignore toggles if filtering is touched\n- verify external opening behavior for Amazon links if that path is touched\n\n### 10.4 Deployment-aware validation\n\nIf public behavior changes:\n\n- compare local changed files to live-access raw references or generated indices\n- verify that deployment-oriented files still point to the intended assets and data paths\n\n## 11) Gift-system migration rules\n\nUntil migration is complete:\n\n1. Do not add new real catalog work to the legacy root `dog-gifts.json` path unless explicitly building a temporary compatibility bridge.\n2. Prefer the v2 catalog pipeline.\n3. Keep public shell behavior and data contract aligned.\n4. If changing the gift feed contract, update:\n\n - loader code\n - validation\n - schema\n - documentation\n - caching assumptions\n\n5. Do not claim migration is complete while both systems still drift independently.\n\n## 12) Remote data and proprietary data rules\n\n1. Public recommendation and gift data can live in a remote runtime-fetched file or endpoint.\n2. Proprietary overlays, premium data, or protected logic must remain remote if intended to stay out of the public app shell.\n3. Do not bundle protected data into the shipped shell as a shortcut.\n4. Any new protected data path must fail gracefully if unavailable.\n5. Public remote data should be designed so it can update without forcing a new app release.\n\n## 13) Service worker and caching rules\n\n1. Be conservative when editing `service-worker.js`.\n2. Do not let cache-first behavior make Amazon-derived or frequently updated runtime data look fresher than it is.\n3. Respect the 24-hour maximum for non-image Amazon Program Content.\n4. Do not enable offline client caching for Amazon Program Content.\n5. If a feed or public data path changes, consider whether the service worker cache name or fetch strategy must also change.\n6. Verify that app shell caching and runtime data freshness are not confused with each other.\n\n## 14) Documentation rules\n\n1. Update docs when the architecture or canonical path changes.\n2. Use `docs/decisions/` for stable architectural decisions.\n3. Do not dump raw temporary notes into permanent docs unless the information is stable and useful.\n4. Keep changelog and maintainers information accurate if ownership or release practices change.\n5. If the meaning of \"canonical gift feed\" changes, document it.\n\n## 15) Git workflow rules\n\n1. Work from the repository root: `C:\\Users\\Master\\Documents\\Barkday`\n2. Prefer a task branch or worktree for non-trivial work.\n3. Before commit:\n\n - inspect changed files\n - run relevant validations\n - check for unintended edits\n\n4. Commit messages should describe the actual change, not vague activity.\n5. Do not push broken validation state.\n6. If the branch is behind remote, fetch and rebase before push unless instructed otherwise.\n\n## 16) What done means\n\nA task is done only when all of the following are true:\n\n1. The requested change is actually implemented.\n2. The correct source-of-truth files were edited.\n3. Relevant validations were run and passed.\n4. No obvious compliance guardrail was violated.\n5. No obvious architecture regression was introduced.\n6. If public runtime behavior changed, the change was checked against live-access expectations.\n7. The resulting diff is understandable and reviewable.\n8. A clear commit can be made.\n\n## 17) What not to do\n\n- Do not invent product data.\n- Do not scrape Amazon pages.\n- Do not add pricing history features.\n- Do not keep both old and new gift systems evolving in parallel without explicit reason.\n- Do not bury critical feed-path changes inside unrelated commits.\n- Do not make temporary shortcuts that ship proprietary data in the public shell.\n- Do not use Amazon content for model training or fine-tuning.\n- Do not add new dependencies casually.\n- Do not change deployment or service-worker behavior without validation.\n\n## 18) First-read checklist for new sessions\n\nAt the beginning of a new local agent session:\n\n1. Read this file.\n2. If touching gifts or runtime data, read `data/AGENTS.md`.\n3. Confirm current branch and git status.\n4. Identify whether the task is shell, data, compliance, or publishing work.\n5. Identify the canonical files for the task.\n6. Identify the validation commands that must be run.\n7. Only then begin editing.\n\n## 19) Preferred next-step behavior for the gift migration\n\nWhen asked to continue gift-system work, default to this sequence:\n\n1. clarify canonical feed path if ambiguous\n2. inspect loader\n3. inspect schema\n4. inspect validation\n5. make minimal aligned change\n6. rebuild and validate\n7. verify runtime behavior\n8. commit cleanly\n","inline_bytes":15362,"content_sha256":"3d1d763c97bf5ed7537ea5e92d007c11ef7da4c15932d1fa05d09da61f1abb53"},{"path":"LICENSE","size":1274,"sha":"f414ccc8b3d2347b2ced0deaf9ad0f73c373e90e","media_type":"application/octet-stream","raw_url":"https://raw.githubusercontent.com/CandidQuality/Barkday/9e38500d723ed1e8e1ca93eb8ae821c831f1414d/LICENSE","html_url":"https://github.com/CandidQuality/Barkday/blob/9e38500d723ed1e8e1ca93eb8ae821c831f1414d/LICENSE","inline_state":"full","max_inline_text_bytes":614400,"max_inline_bin_bytes":204800,"preview_text_bytes":65536,"encoding":"base64","content":"QmFya2RheeKEoiBBcHBsaWNhdGlvbiBMaWNlbnNlCkNvcHlyaWdodCDCqSAyMDI1IENhbmRpZFF1YWxpdHkuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuCgpUaGlzIHNvZnR3YXJlLCBpbmNsdWRpbmcgYWxsIHNvdXJjZSBjb2RlLCBkZXNpZ24sIGltYWdlcywgZGF0YSBmaWxlcywgYW5kCmFzc29jaWF0ZWQgZG9jdW1lbnRhdGlvbiAodGhlICJTb2Z0d2FyZSIpLCBpcyB0aGUgcHJvcHJpZXRhcnkgd29yayBvZiB0aGUKcHJvamVjdCBvd25lciBhbmQgaXMgcHJvdGVjdGVkIGJ5IGNvcHlyaWdodCwgdHJhZGVtYXJrLCBhbmQgb3RoZXIgYXBwbGljYWJsZQpsYXdzLgoKUGVybWlzc2lvbiBpcyBoZXJlYnkgZ3JhbnRlZCB0byBpbmRpdmlkdWFscyB0byB2aWV3IGFuZCB1c2UgdGhlIFNvZnR3YXJlIGZvcgpwZXJzb25hbCwgbm9uLWNvbW1lcmNpYWwgcHVycG9zZXMgb25seSB0aHJvdWdoIHRoZSBvZmZpY2lhbCBCYXJrZGF54oSiIHdlYnNpdGUKb3IgYXV0aG9yaXplZCBkaXN0cmlidXRpb24gY2hhbm5lbHMuCgpSZXN0cmljdGlvbnMKLS0tLS0tLS0tLS0tCldpdGhvdXQgcHJpb3Igd3JpdHRlbiBjb25zZW50IGZyb20gdGhlIGNvcHlyaWdodCBob2xkZXIsIHlvdSBtYXkgTk9UOgotIENvcHksIG1vZGlmeSwgbWVyZ2UsIHB1Ymxpc2gsIGRpc3RyaWJ1dGUsIHN1YmxpY2Vuc2UsIG9yIHNlbGwgY29waWVzIG9mIHRoZSBTb2Z0d2FyZS4KLSBVc2UgdGhlIFNvZnR3YXJlLCBpbiB3aG9sZSBvciBpbiBwYXJ0LCBmb3IgYW55IGNvbW1lcmNpYWwgcHVycG9zZS4KLSBSZW1vdmUgb3IgYWx0ZXIgYW55IGNvcHlyaWdodCwgdHJhZGVtYXJrLCBvciBwcm9wcmlldGFyeSBub3RpY2VzLgoKVHJhZGVtYXJrCi0tLS0tLS0tLQpUaGUgQmFya2RheeKEoiBuYW1lIGFuZCBhc3NvY2lhdGVkIGJyYW5kaW5nIGFyZSBwcm90ZWN0ZWQgYXMgdHJhZGVtYXJrcy4gVXNlIG9mCnRoZSBCYXJrZGF54oSiIG1hcmsgd2l0aG91dCBleHByZXNzIHBlcm1pc3Npb24gaXMgc3RyaWN0bHkgcHJvaGliaXRlZC4KCkRpc2NsYWltZXIKLS0tLS0tLS0tLQpUaGUgU29mdHdhcmUgaXMgcHJvdmlkZWQg4oCcYXMgaXMs4oCdIHdpdGhvdXQgd2FycmFudHkgb2YgYW55IGtpbmQsIGV4cHJlc3Mgb3IKaW1wbGllZC4gSW4gbm8gZXZlbnQgc2hhbGwgdGhlIGNvcHlyaWdodCBob2xkZXIgYmUgbGlhYmxlIGZvciBhbnkgZGFtYWdlcwphcmlzaW5nIGZyb20gdGhlIHVzZSBvZiB0aGUgU29mdHdhcmUuCgpBbGwgcmlnaHRzIG5vdCBleHByZXNzbHkgZ3JhbnRlZCBoZXJlaW4gYXJlIHJlc2VydmVkLgo=","inline_bytes":1700,"content_sha256":"f922e4d3a5af73e337108522235c48218707e31dae2635e22bb85b11239bff59"},{"path":"README.md","size":3390,"sha":"ba89f60cbc62628087a1d312e31fc170b7cbdfe7","media_type":"text/markdown","raw_url":"https://raw.githubusercontent.com/CandidQuality/Barkday/9e38500d723ed1e8e1ca93eb8ae821c831f1414d/README.md","html_url":"https://github.com/CandidQuality/Barkday/blob/9e38500d723ed1e8e1ca93eb8ae821c831f1414d/README.md","inline_state":"full","max_inline_text_bytes":614400,"max_inline_bin_bytes":204800,"preview_text_bytes":65536,"encoding":"utf8","content":"# Barkday(TM)\n\n## Build & Docs Status\n\n[](https://github.com/CandidQuality/Barkday/actions/workflows/pages.yml)\n\n## Live site: