{"schema":"barkday.ai-pack-ci.v1","repo":"CandidQuality/Barkday","commit":"9e38500d723ed1e8e1ca93eb8ae821c831f1414d","updated_utc":"2026-05-05T03:48:27.039Z","count":11,"items":[{"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"}]}