// Builds the FactHub dataset "homeless-system-performance": HUD System Performance Measures // for 67 large CoCs, FY2015-FY2024, with King County's trend and ranks. // Input: ../../data/spm/spm_panel.csv. Outputs (../out/): spm_king.csv, spm_content.json // Usage: node build_spm_dataset.js const fs = require('fs'); const path = require('path'); const crypto = require('crypto'); const ROOT = path.join(__dirname, '..', '..'); const OUT = path.join(__dirname, '..', 'out'); const NOW = '2026-10-10T22:30:00Z'; const KEY = 'homeless-system-performance'; function parseCsv(text) { const rows = []; for (const line of text.split(/\r?\n/)) { if (!line) continue; const cells = []; let cur = '', q = false; for (let i = 0; i < line.length; i++) { const c = line[i]; if (q) { if (c === '"') { if (line[i + 1] === '"') { cur += '"'; i++; } else q = false; } else cur += c; } else if (c === '"') q = true; else if (c === ',') { cells.push(cur); cur = ''; } else cur += c; } cells.push(cur); rows.push(cells); } const [h, ...body] = rows; return body.map(r => Object.fromEntries(h.map((k, i) => [k, r[i]]))); } const num = v => (v === '' || v == null || isNaN(+v) ? null : +v); const r1 = v => (v == null ? null : Math.round(v * 10) / 10); const median = a => { const s = a.filter(v => v != null).sort((x, y) => x - y); const n = s.length; return n ? (n % 2 ? s[(n - 1) / 2] : (s[n / 2 - 1] + s[n / 2]) / 2) : null; }; const spm = parseCsv(fs.readFileSync(path.join(ROOT, 'data/spm/spm_panel.csv'), 'utf8')); const years = [...new Set(spm.map(r => +r.fy))].sort(); const byYear = fy => spm.filter(r => +r.fy === fy); const get = (code, fy) => spm.find(r => r.coc_code === code && +r.fy === fy); // King County series. const kingRows = years.map(fy => { const r = get('WA-500', fy); return { fy, bedCoveragePct: num(r.bed_coverage_pct), daysHomelessAvg: num(r.m1_es_sh_avg), daysHomelessMedian: num(r.m1_es_sh_median), returnsWithin24MonthsPct: r1(num(r.m2_pct24)), firstTimePer10k: r1(num(r.m5_per10k_es_sh_th)), outreachSuccessfulExitPct: r1(num(r.m7a_pct)), exitsToPermanentHousingPct: r1(num(r.m7b1_pct)), pshRetentionPct: r1(num(r.m7b2_pct)) }; }); const cols = Object.keys(kingRows[0]); fs.writeFileSync(path.join(OUT, 'spm_king.csv'), cols.join(',') + '\n' + kingRows.map(r => cols.map(c => r[c] ?? '').join(',')).join('\n') + '\n'); // Median days homeless (ES+SH): King vs large-CoC median vs Houston, by year (chart-ready, long format). const m1Long = []; for (const fy of years) { m1Long.push({ fy, series: 'Seattle/King County', medianDays: num(get('WA-500', fy).m1_es_sh_median) }); m1Long.push({ fy, series: 'Median of 67 large CoCs', medianDays: median(byYear(fy).map(r => num(r.m1_es_sh_median))) }); m1Long.push({ fy, series: 'Houston/Harris County', medianDays: num(get('TX-700', fy).m1_es_sh_median) }); } // Ranks, FY2019 and FY2024, among CoCs reporting each measure. Rank 1 = most favorable. const MEAS = [ ['m1_es_sh_avg', 'Average days homeless in emergency shelter and safe havens (M1)', 'lower'], ['m1_es_sh_median', 'Median days homeless in emergency shelter and safe havens (M1)', 'lower'], ['m2_pct24', 'Returns to homelessness within 24 months of exit to permanent housing, % (M2)', 'lower'], ['m5_per10k_es_sh_th', 'People homeless for the first time per 10,000 residents (M5)', 'lower'], ['m7a_pct', 'Street outreach exits to temporary or permanent destinations, % (M7a)', 'higher'], ['m7b1_pct', 'Exits from shelter, transitional housing and rapid rehousing to permanent housing, % (M7b.1)', 'higher'], ['m7b2_pct', 'Permanent supportive housing retention or exit to permanent housing, % (M7b.2)', 'higher'], ]; const rankRows = []; for (const fy of [2019, 2024]) for (const [f, label, better] of MEAS) { const rows = byYear(fy).filter(r => num(r[f]) != null).sort((a, b) => better === 'lower' ? num(a[f]) - num(b[f]) : num(b[f]) - num(a[f])); const k = rows.find(r => r.coc_code === 'WA-500'); rankRows.push({ fy, measure: label, better, kingValue: r1(num(k[f])), rank: rows.indexOf(k) + 1, of: rows.length, median: r1(median(rows.map(r => num(r[f])))) }); } // FY2024 comparison, selected CoCs. const PEERS = ['WA-500', 'WA-503', 'OR-501', 'CA-501', 'CA-502', 'CA-600', 'CA-601', 'CA-500', 'CO-503', 'AZ-502', 'NY-600', 'MA-500', 'TX-700']; const peerRows = PEERS.map(c => { const r = get(c, 2024); return { coc: c, name: r.short_name, bedCoveragePct: num(r.bed_coverage_pct), daysHomelessMedian: num(r.m1_es_sh_median), daysHomelessAvg: num(r.m1_es_sh_avg), returnsWithin24MonthsPct: r1(num(r.m2_pct24)), firstTimePer10k: r1(num(r.m5_per10k_es_sh_th)), exitsToPermanentHousingPct: r1(num(r.m7b1_pct)) }; }); function art(id, rel, role, mediaType, description) { const buf = fs.readFileSync(path.join(ROOT, rel)); return { id, role, fileName: path.basename(rel), mediaType, byteLength: buf.length, contentDigest: 'sha256:' + crypto.createHash('sha256').update(buf).digest('hex'), description, access: 'public' }; } const artifacts = [ art('spm-panel', 'data/spm/spm_panel.csv', 'curatedTable', 'text/csv', 'HUD System Performance Measures for 67 large CoCs, FY2015-FY2024, with recomputed percentages, population and data-quality flags.'), art('spm-king', 'facthub/out/spm_king.csv', 'derivedTable', 'text/csv', 'Seattle/King County (WA-500) System Performance Measures by fiscal year.'), art('spm-build-script', 'data/spm/scripts/build_spm.js', 'transformationManifest', 'text/javascript', 'Node.js script that builds spm-panel from the HUD workbook sheets (exported to CSV).'), art('spm-rank-script', 'data/spm/scripts/rank.js', 'transformationManifest', 'text/javascript', 'Node.js script that ranks WA-500 among the large CoCs, with and without data-quality exclusions.'), art('spm-csv-lib', 'data/spm/scripts/csvlib.js', 'transformationManifest', 'text/javascript', 'CSV helper used by the SPM scripts.'), art('spm-dataset-script', 'facthub/scripts/build_spm_dataset.js', 'transformationManifest', 'text/javascript', 'Node.js script that builds spm-king and the saved analyses in this dataset.'), art('spm-notes', 'data/spm/findings-spm.md', 'documentation', 'text/markdown', 'Method notes, results, KCRHA cross-check and data-quality caveats for the System Performance Measures.'), ]; const sources = [ { id: 'hud-spm', name: 'HUD Exchange, CoC System Performance Measures Data Since FY 2015', url: 'https://files.hudexchange.info/resources/documents/System-Performance-Measures-Data.xlsx', retrievedAt: '2026-10-10', role: 'CoC-level System Performance Measures, one sheet per fiscal year FY2015-FY2024, as submitted by each CoC.', licenseName: 'U.S. government work (public domain)', licenseUrl: null, redistribution: 'Public domain; not rehosted here.', artifactId: null, archiveUrl: 'https://web.archive.org/web/20261010221233/https://files.hudexchange.info/resources/documents/System-Performance-Measures-Data.xlsx', archivedAt: '2026-10-10T22:12:33Z', contentDigest: 'sha256:82796eb0686e3fc53d64ebd6ca4d9a4b2629f313591b7d1ae8d3d3aab43c61af' }, { id: 'kcrha-spm-2025', name: 'KCRHA, "Understanding System Performance Measures: A HUD Federal Report Overview," CoC Board, April 1, 2026', url: 'https://kcrha.org/wp-content/uploads/2026/04/System.Performance.Measure.2025_04.01.2026.pdf', retrievedAt: '2026-10-10', role: 'KCRHA\'s own FY2024 and FY2025 figures and its explanation that record cleanup drove part of the FY2025 changes.', licenseName: 'Public agency document', licenseUrl: null, redistribution: 'Linked, not rehosted.', artifactId: null, archiveUrl: 'https://web.archive.org/web/20261010221239/https://kcrha.org/wp-content/uploads/2026/04/System.Performance.Measure.2025_04.01.2026.pdf', archivedAt: '2026-10-10T22:12:39Z', contentDigest: 'sha256:03cf1a53a003cc386b13fc1ea7a1ee7642f517c0944b27f3018e48ecaa7f773d' }, ]; const f = (name, type, description, units, nullable, valueType, derivation, sourceIds) => ({ name, type, description, units, nullable, valueType, derivation, sourceIds }); const tables = [ { id: 'spm-panel', title: 'System Performance Measures, 67 large CoCs, FY2015-FY2024', description: 'HUD SPM counts and percentages for each CoC and fiscal year, with population, first-time homelessness per 10,000 and data-quality flags.', artifactId: 'spm-panel', unitOfObservation: 'CoC by fiscal year (Oct 1 to Sep 30)', fields: [ f('coc_code', 'string', 'HUD CoC code', null, false, 'observed', null, ['hud-spm']), f('short_name', 'string', 'Short display name', null, false, 'derived', null, []), f('fy', 'integer', 'Federal fiscal year of the report', null, false, 'observed', null, ['hud-spm']), f('spm_code_used', 'string', 'CoC code as it appears in HUD\'s sheet (MO-604M before FY2023)', null, false, 'observed', null, ['hud-spm']), f('category', 'string', 'HUD CoC category in the SPM file', null, true, 'observed', null, ['hud-spm']), f('hic_nondv_beds_es_th', 'integer', 'Non-DV ES+TH beds in the HIC', 'beds', true, 'observed', null, ['hud-spm']), f('hmis_nondv_beds_es_th', 'integer', 'Non-DV ES+TH beds participating in HMIS', 'beds', true, 'observed', null, ['hud-spm']), f('bed_coverage_pct_reported', 'number', 'HMIS bed coverage as HUD reports it', 'percent', true, 'observed', null, ['hud-spm']), f('m1_es_sh_avg', 'number', 'M1: average days homeless, ES+SH', 'days', true, 'observed', null, ['hud-spm']), f('m1_es_sh_median', 'number', 'M1: median days homeless, ES+SH', 'days', true, 'observed', null, ['hud-spm']), f('m1_es_sh_th_avg', 'number', 'M1: average days homeless, ES+SH+TH', 'days', true, 'observed', null, ['hud-spm']), f('m1_es_sh_th_median', 'number', 'M1: median days homeless, ES+SH+TH', 'days', true, 'observed', null, ['hud-spm']), f('m2_exits_to_ph', 'integer', 'M2 (All): people who exited to permanent housing in the cohort period', 'people', true, 'observed', null, ['hud-spm']), f('m2_ret6', 'integer', 'M2: of those, returned within 6 months', 'people', true, 'observed', null, ['hud-spm']), f('m2_ret12', 'integer', 'M2: returned within 12 months (cumulative)', 'people', true, 'observed', null, ['hud-spm']), f('m2_ret24', 'integer', 'M2: returned within 24 months (cumulative)', 'people', true, 'observed', null, ['hud-spm']), f('m5_first_time_es_sh_th', 'integer', 'M5.1: people entering ES/SH/TH with no HMIS record in the prior 24 months', 'people', true, 'observed', null, ['hud-spm']), f('m5_first_time_es_sh_th_ph', 'integer', 'M5.2: same, including PH entries', 'people', true, 'observed', null, ['hud-spm']), f('m7a_so_exits', 'integer', 'M7a: street outreach exits', 'people', true, 'observed', null, ['hud-spm']), f('m7a_so_temp', 'integer', 'M7a: outreach exits to temporary destinations', 'people', true, 'observed', null, ['hud-spm']), f('m7a_so_perm', 'integer', 'M7a: outreach exits to permanent destinations', 'people', true, 'observed', null, ['hud-spm']), f('m7a_pct_reported', 'number', 'M7a % as HUD reports it', 'percent', true, 'observed', null, ['hud-spm']), f('m7b1_exits', 'integer', 'M7b.1: exits from ES/SH/TH/RRH', 'people', true, 'observed', null, ['hud-spm']), f('m7b1_to_ph', 'integer', 'M7b.1: of those, exits to permanent housing', 'people', true, 'observed', null, ['hud-spm']), f('m7b1_pct_reported', 'number', 'M7b.1 % as reported', 'percent', true, 'observed', null, ['hud-spm']), f('m7b2_universe', 'integer', 'M7b.2: people in PSH/OPH', 'people', true, 'observed', null, ['hud-spm']), f('m7b2_success', 'integer', 'M7b.2: stayed or exited to permanent housing', 'people', true, 'observed', null, ['hud-spm']), f('m7b2_pct_reported', 'number', 'M7b.2 % as reported', 'percent', true, 'observed', null, ['hud-spm']), f('bed_coverage_pct', 'number', 'Recomputed HMIS bed coverage = hmis / hic', 'percent', true, 'derived', 'hmis_nondv_beds_es_th / hic_nondv_beds_es_th * 100', ['hud-spm']), f('m2_pct6', 'number', 'Returns within 6 months, %', 'percent', true, 'derived', 'm2_ret6 / m2_exits_to_ph * 100', ['hud-spm']), f('m2_pct12', 'number', 'Returns within 12 months, %', 'percent', true, 'derived', 'm2_ret12 / m2_exits_to_ph * 100', ['hud-spm']), f('m2_pct24', 'number', 'Returns within 24 months, %', 'percent', true, 'derived', 'm2_ret24 / m2_exits_to_ph * 100', ['hud-spm']), f('m7a_pct', 'number', 'Recomputed M7a %', 'percent', true, 'derived', '(m7a_so_temp + m7a_so_perm) / m7a_so_exits * 100', ['hud-spm']), f('m7b1_pct', 'number', 'Recomputed M7b.1 %', 'percent', true, 'derived', 'm7b1_to_ph / m7b1_exits * 100', ['hud-spm']), f('m7b2_pct', 'number', 'Recomputed M7b.2 %', 'percent', true, 'derived', 'm7b2_success / m7b2_universe * 100', ['hud-spm']), f('population', 'integer', 'ACS population (from dataset homelessness-rate-vs-rent); FY2020 is the mean of 2019 and 2021', 'people', true, 'derived', null, []), f('m5_per10k_es_sh_th', 'number', 'First-time homeless (M5.1) per 10,000 residents', 'per 10,000 residents', true, 'derived', 'm5_first_time_es_sh_th / population * 10,000', ['hud-spm']), f('m5_per10k_es_sh_th_ph', 'number', 'First-time homeless (M5.2) per 10,000 residents', 'per 10,000 residents', true, 'derived', null, ['hud-spm']), f('dq_flags', 'string', 'Data-quality flags (e.g. bed_cov<85%, MO-604 split, non-cumulative TH returns)', null, true, 'derived', 'build_spm.js', ['hud-spm']), f('dq_unknown_dest_pct_SO', 'number', 'Unknown or missing exit destinations, street outreach (FY2019+)', 'percent', true, 'observed', null, ['hud-spm']), f('dq_unknown_dest_pct_ES', 'number', 'Unknown destinations, emergency shelter', 'percent', true, 'observed', null, ['hud-spm']), f('dq_unknown_dest_pct_TH', 'number', 'Unknown destinations, transitional housing', 'percent', true, 'observed', null, ['hud-spm']), f('dq_unknown_dest_pct_RRH', 'number', 'Unknown destinations, rapid rehousing', 'percent', true, 'observed', null, ['hud-spm']), f('dq_unknown_dest_pct_PSH', 'number', 'Unknown destinations, PSH', 'percent', true, 'observed', null, ['hud-spm']), f('pop_note', 'string', 'Notes on the population value', null, true, 'derived', null, []), ] }, { id: 'spm-king', title: 'Seattle/King County System Performance Measures, FY2015-FY2024', description: 'King County rows from spm-panel, with readable column names.', artifactId: 'spm-king', unitOfObservation: 'Fiscal year', fields: [ f('fy', 'integer', 'Federal fiscal year', null, false, 'observed', null, ['hud-spm']), f('bedCoveragePct', 'number', 'HMIS bed coverage', 'percent', false, 'derived', null, ['hud-spm']), f('daysHomelessAvg', 'number', 'M1 average days homeless, ES+SH', 'days', false, 'observed', null, ['hud-spm']), f('daysHomelessMedian', 'number', 'M1 median days homeless, ES+SH', 'days', false, 'observed', null, ['hud-spm']), f('returnsWithin24MonthsPct', 'number', 'M2 returns within 24 months', 'percent', false, 'derived', null, ['hud-spm']), f('firstTimePer10k', 'number', 'M5.1 first-time homeless per 10,000 residents', 'per 10,000 residents', true, 'derived', null, ['hud-spm']), f('outreachSuccessfulExitPct', 'number', 'M7a outreach exits to temporary or permanent destinations', 'percent', false, 'derived', null, ['hud-spm']), f('exitsToPermanentHousingPct', 'number', 'M7b.1 exits to permanent housing', 'percent', false, 'derived', null, ['hud-spm']), f('pshRetentionPct', 'number', 'M7b.2 PSH retention or exit to permanent housing', 'percent', false, 'derived', null, ['hud-spm']), ] }, ]; const commonLim = ['SPMs are self-reported from each CoC\'s HMIS and not audited by HUD; CoCs may revise prior years.', 'HMIS bed coverage differs by CoC and year (King County 77-87%); domestic-violence providers are excluded everywhere.', 'Descriptive comparisons; differences may reflect data practice, coverage or system design as well as performance.']; const analyses = [ { id: 'king-days-homeless-vs-peers', title: 'Median days homeless in shelter: King County vs. the large-CoC median and Houston, FY2015-FY2024', question: 'How long do people stay homeless in King County\'s shelter system, and how has that compared with other large CoCs over time?', operation: 'M1 median days homeless (ES+SH) by fiscal year for WA-500 and TX-700, and the median across the 67 CoCs', parameters: { measure: 'm1_es_sh_median' }, scope: '67 large CoCs, FY2015-FY2024', resultShape: 'timeSeries', structuredResult: { rows: m1Long }, displayResult: `King County's median days homeless in shelter rose from ${m1Long.find(r => r.fy === 2015 && r.series === 'Seattle/King County').medianDays} (FY2015) to ${m1Long.find(r => r.fy === 2024 && r.series === 'Seattle/King County').medianDays} (FY2024); the large-CoC median went from ${m1Long.find(r => r.fy === 2015 && r.series.startsWith('Median')).medianDays} to ${m1Long.find(r => r.fy === 2024 && r.series.startsWith('Median')).medianDays}; Houston from ${m1Long.find(r => r.fy === 2015 && r.series.startsWith('Houston')).medianDays} to ${m1Long.find(r => r.fy === 2024 && r.series.startsWith('Houston')).medianDays}.`, methodologicalNotes: ['M1 counts days each person was enrolled in ES or SH (and before entry, if self-reported) during the report year; it is computed from entry and exit dates, so it is less sensitive to record-closure practice than destination-based measures.', 'The median is across CoCs reporting M1 that year.'], limitations: [...commonLim, 'FY2020-FY2021 cover the COVID-19 period, when shelters reduced capacity and lengthened stays.'], executedAt: NOW }, { id: 'king-spm-series', title: 'Seattle/King County System Performance Measures, FY2015-FY2024', question: 'How did King County\'s homeless system perform on HUD\'s main measures from FY2015 to FY2024?', operation: 'WA-500 rows from spm-panel', parameters: { coc: 'WA-500' }, scope: 'King County', resultShape: 'timeSeries', structuredResult: { rows: kingRows }, displayResult: `FY2015 to FY2024: average days homeless in shelter ${kingRows[0].daysHomelessAvg} to ${kingRows.at(-1).daysHomelessAvg}; exits to permanent housing ${kingRows[0].exitsToPermanentHousingPct}% to ${kingRows.at(-1).exitsToPermanentHousingPct}%; returns within 24 months ${kingRows[0].returnsWithin24MonthsPct}% to ${kingRows.at(-1).returnsWithin24MonthsPct}%; first-time homeless per 10,000 ${kingRows[0].firstTimePer10k} to ${kingRows.at(-1).firstTimePer10k}.`, methodologicalNotes: ['Percentages recomputed from HUD\'s counts; they match HUD\'s reported percentages within 1.5 points.'], limitations: [...commonLim, 'Destination-based measures (M2, M7) can move tens of points on record cleanup alone; KCRHA attributes part of its FY2025 changes to this.'], executedAt: NOW }, { id: 'king-spm-ranks', title: 'King County rank among large CoCs on HUD System Performance Measures, FY2019 and FY2024', question: 'Where does King County rank among large CoCs on each System Performance Measure?', operation: 'Rank WA-500 among CoCs reporting each measure; rank 1 = most favorable in HUD\'s direction', parameters: { fy: [2019, 2024] }, scope: '66-67 large CoCs per measure', resultShape: 'table', structuredResult: { rows: rankRows }, displayResult: rankRows.map(r => `FY${r.fy} ${r.measure}: ${r.kingValue}, rank ${r.rank} of ${r.of} (median ${r.median})`).join('; '), methodologicalNotes: ['The method notes (spm-notes) also give ranks excluding CoCs with data-quality flags; the pattern is the same.'], limitations: [...commonLim, 'Most large CoCs have 30% or more unknown exit destinations for shelter or outreach, which weakens M2 and M7 comparisons.'], executedAt: NOW }, { id: 'spm-selected-fy2024', title: 'System Performance Measures for selected large CoCs, FY2024', question: 'How does King County compare with other West Coast and large CoCs on HUD\'s measures in FY2024?', operation: 'FY2024 values from spm-panel', parameters: { cocs: PEERS }, scope: 'Selected large CoCs', resultShape: 'table', structuredResult: { rows: peerRows }, displayResult: peerRows.map(r => `${r.name}: median days ${r.daysHomelessMedian}, exits to PH ${r.exitsToPermanentHousingPct}%, returns ${r.returnsWithin24MonthsPct}%`).join('; '), limitations: [...commonLim, 'Comparison set includes the regions named in the accompanying article; the full panel is in spm-panel.'], executedAt: NOW }, { id: 'kcrha-reported-fy2025', title: 'KCRHA-reported figures, FY2024 vs. FY2025', question: 'What did KCRHA report for FY2025, the year after HUD\'s latest published file?', operation: 'Transcribed from KCRHA\'s April 1, 2026 CoC Board deck', parameters: { source: 'kcrha-spm-2025' }, scope: 'King County', resultShape: 'table', structuredResult: { rows: [ { item: 'Average time in shelter (days)', fy2024: 248, fy2025: 263 }, { item: 'Returns to homelessness (%)', fy2024: 10, fy2025: 15 }, { item: 'First-time homeless (people)', fy2024: 10332, fy2025: 9592 }, { item: 'Street outreach exits to permanent housing (%)', fy2024: 75, fy2025: 50 }, { item: 'Annual sheltered count (people)', fy2024: 14470, fy2025: 13884 } ] }, displayResult: 'Average time in shelter 248 to 263 days; returns 10% to 15%; first-time homeless 10,332 to 9,592; outreach exits to permanent housing 75% to 50%; annual sheltered 14,470 to 13,884.', methodologicalNotes: ['KCRHA says the outreach and returns changes mainly reflect record cleanup and correct record closure.', 'KCRHA\'s FY2024 figures differ slightly from HUD\'s workbook (KCRHA reports 248 days; HUD\'s ES+SH+TH average is 249); the deck does not say which return window it uses.'], limitations: ['FY2025 is not yet in HUD\'s published file.', 'Self-reported by KCRHA.'], executedAt: NOW }, ]; const content = { key: KEY, status: 'Draft', rulesVersion: '2.14', title: 'Homeless system performance: Seattle/King County vs. 66 large U.S. Continuums of Care, FY2015-FY2024', description: 'HUD System Performance Measures (time homeless, returns, first-time homelessness, exits to housing) for 67 large Continuums of Care, with Seattle/King County\'s trend and rank.', selectionRationale: 'Tests whether King County\'s homeless system moves people into housing as well as other large regions, using the measures HUD requires every CoC to report. Same 67 CoCs as homelessness-rate-vs-rent.', topics: ['homelessness', 'shelter', 'system performance', 'Seattle', 'King County', 'Washington'], geography: ['United States', 'King County, WA', 'Seattle, WA'], temporalCoverage: 'Federal fiscal years 2015-2024 (HUD); FY2025 KCRHA-reported figures', unitOfObservation: 'CoC by federal fiscal year', updateCadence: 'Annually, after HUD updates its SPM data file', curator: 'FactHub', reproducibilityStatus: 'Reproducible from public sources', reproducibilityExplanation: 'Export each sheet of the HUD SPM workbook to CSV, run spm-build-script with the CoC list and panel from dataset homelessness-rate-vs-rent, then rank-script and spm-dataset-script (Node.js).', limitations: [...commonLim, 'HUD\'s FY2024 file has zeroed transitional-housing return counts for most CoCs; only the combined "All" columns are used.', 'HUD revised exit-destination categories in its FY2024 HMIS Data Standards.'], sources, artifacts, tables, derivedFrom: [{ nodeHandle: 'dataset:homelessness-rate-vs-rent:1', contentDigest: 'sha256:ffda4fdcf8d8d3a1a736d70bfdc991598150be8d9f146043b686d6b5d03fe01c' }], analyses, changelog: [{ date: '2026-10-10', summary: 'Initial version.' }], }; fs.writeFileSync(path.join(OUT, 'spm_content.json'), JSON.stringify(content)); console.log('bytes', JSON.stringify(content).length); for (const r of rankRows) console.log(r.fy, r.rank + '/' + r.of, r.kingValue, 'med', r.median, r.measure.slice(0, 60)); console.log(m1Long.filter(r => r.fy === 2015 || r.fy === 2024));