← back to Rentv 2026
pr-intelligence: state-aware quality gate (stateGate(state)) — Arizona now a first-class gate/dashboard number; /api/pr/gate/check?state=AZ; californiaGate() back-compat wrapper; test (30/30). DTD-C punch-list item 2.
0bad92c373b44f1705791aa7dc979089cfe453cc · 2026-07-30 23:01:26 -0700 · Steve Abrams
Files touched
M src/pr/index.jsM src/pr/services/settings.jsM test/pr/integration.test.js
Diff
commit 0bad92c373b44f1705791aa7dc979089cfe453cc
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Thu Jul 30 23:01:26 2026 -0700
pr-intelligence: state-aware quality gate (stateGate(state)) — Arizona now a first-class gate/dashboard number; /api/pr/gate/check?state=AZ; californiaGate() back-compat wrapper; test (30/30). DTD-C punch-list item 2.
---
src/pr/index.js | 5 +++--
src/pr/services/settings.js | 32 +++++++++++++++++++-------------
test/pr/integration.test.js | 16 ++++++++++++++++
3 files changed, 38 insertions(+), 15 deletions(-)
diff --git a/src/pr/index.js b/src/pr/index.js
index e59f1b34..d66547bf 100644
--- a/src/pr/index.js
+++ b/src/pr/index.js
@@ -139,7 +139,8 @@ module.exports = function mountPR(app, { adminOnly, sendPage }) {
cells_covered: caCells.covered, cells_total: totalCells,
gate,
},
- arizona: { unlocked: azUnlocked, organizations: orgTotals.az, status: azUnlocked ? 'active' : 'locked' },
+ arizona: { unlocked: azUnlocked, organizations: orgTotals.az, status: azUnlocked ? 'active' : 'locked',
+ gate: azUnlocked ? await settings.get('az_gate', null) : null },
organizations: orgTotals, people: pplTotals,
by_category: byCategory, by_role: byRole, by_metro: byMetro,
orgs_without_pr_contact: noPr.n,
@@ -398,7 +399,7 @@ module.exports = function mountPR(app, { adminOnly, sendPage }) {
app.get('/api/pr/settings', adminOnly, h(async (_q, res) => res.json(await settings.getAll())));
app.put('/api/pr/settings/:key', adminOnly, h(async (req, res) =>
res.json(await settings.set(req.params.key, (req.body || {}).value, actorOf(req)))));
- app.post('/api/pr/gate/check', adminOnly, h(async (_q, res) => res.json(await settings.californiaGate())));
+ app.post('/api/pr/gate/check', adminOnly, h(async (req, res) => res.json(await settings.stateGate((req.query.state || (req.body || {}).state || 'CA')))));
app.post('/api/pr/gate/unlock-arizona', adminOnly, h(async (req, res) =>
res.json(await settings.unlockArizona({ actor: actorOf(req), override: (req.body || {}).override === true }))));
diff --git a/src/pr/services/settings.js b/src/pr/services/settings.js
index d4b82882..a4c7a2d6 100644
--- a/src/pr/services/settings.js
+++ b/src/pr/services/settings.js
@@ -32,13 +32,14 @@ async function set(key, value, actor) {
* 6. no inferred/generated contact mislabeled verified (structural query)
* 7. dashboard shows remaining coverage gaps (always true once the dashboard is live — reported)
*/
-async function californiaGate() {
+async function stateGate(state = 'CA') {
+ const ST = String(state).toUpperCase();
const checks = {};
const runs = await db.one(
`SELECT count(*) FILTER (WHERE status='completed') AS completed,
count(*) FILTER (WHERE checkpoint <> '{}'::jsonb) AS checkpointed
- FROM pr_research_runs WHERE state='CA'`, []);
+ FROM pr_research_runs WHERE state=$1`, [ST]);
checks.pipeline_resumable = Number(runs.completed) > 0 && Number(runs.checkpointed) > 0;
const dups = await db.one(
@@ -54,14 +55,14 @@ async function californiaGate() {
`WITH reviewed AS (
SELECT o.*, EXISTS (SELECT 1 FROM pr_field_evidence e WHERE e.entity_type='organization' AND e.entity_id=o.id) AS has_evidence
FROM pr_organizations o
- WHERE 'CA' = ANY(o.state_presence)
+ WHERE $1 = ANY(o.state_presence)
AND o.lifecycle_status IN ('verified','ready_for_outreach','contacted','active_relationship'))
SELECT count(*) AS n,
count(*) FILTER (WHERE display_name IS NOT NULL AND website_url IS NOT NULL
AND organization_type IS NOT NULL
AND (cardinality(metros)>0 OR cardinality(counties)>0)
AND has_evidence) AS complete
- FROM reviewed`, []);
+ FROM reviewed`, [ST]);
const nReviewed = Number(orgQ.n), nComplete = Number(orgQ.complete);
checks.org_completeness = { reviewed: nReviewed, complete: nComplete, pct: nReviewed ? Math.round(nComplete / nReviewed * 100) : 0, pass: nReviewed > 0 && nComplete / nReviewed >= 0.8 };
@@ -70,26 +71,26 @@ async function californiaGate() {
// in the catalog, just not counted as gate-relevant outreach targets.
const exclude = await get('coverage_exclude_types', ['bank', 'credit_union']);
const hi = await db.one(
- `WITH hi AS (SELECT id FROM pr_organizations WHERE 'CA'=ANY(state_presence) AND priority_score >= 60
- AND lifecycle_status NOT IN ('duplicate','archived') AND organization_type <> ALL($1::text[]))
+ `WITH hi AS (SELECT id FROM pr_organizations WHERE $1=ANY(state_presence) AND priority_score >= 60
+ AND lifecycle_status NOT IN ('duplicate','archived') AND organization_type <> ALL($2::text[]))
SELECT count(*) AS n,
count(*) FILTER (WHERE EXISTS (
SELECT 1 FROM pr_people p WHERE p.organization_id = hi.id
AND p.lifecycle_status NOT IN ('duplicate','suppressed','wrong_person','left_company')
AND p.department IN ('communications','marketing','leadership','bd','agency'))) AS with_contact
- FROM hi`, [exclude]);
+ FROM hi`, [ST, exclude]);
const nHi = Number(hi.n), nWith = Number(hi.with_contact);
// Strict lens (informational, Cody cycle 2): comms/marketing/agency/bd ONLY — the
// spec's gate criterion explicitly includes leadership, so pass/fail stays on the
// broader set, but this number keeps "we can email a real comms person" honest.
const commsOnly = await db.one(
- `WITH hi AS (SELECT id FROM pr_organizations WHERE 'CA'=ANY(state_presence) AND priority_score >= 60
- AND lifecycle_status NOT IN ('duplicate','archived') AND organization_type <> ALL($1::text[]))
+ `WITH hi AS (SELECT id FROM pr_organizations WHERE $1=ANY(state_presence) AND priority_score >= 60
+ AND lifecycle_status NOT IN ('duplicate','archived') AND organization_type <> ALL($2::text[]))
SELECT count(*) FILTER (WHERE EXISTS (
SELECT 1 FROM pr_people p WHERE p.organization_id = hi.id
AND p.lifecycle_status NOT IN ('duplicate','suppressed','wrong_person','left_company')
AND p.department IN ('communications','marketing','agency','bd'))) AS n
- FROM hi`, [exclude]);
+ FROM hi`, [ST, exclude]);
checks.contact_coverage = {
high_priority: nHi, with_contact: nWith, pct: nHi ? Math.round(nWith / nHi * 100) : 0,
pass: nHi > 0 && nWith / nHi >= 0.7,
@@ -115,11 +116,16 @@ async function californiaGate() {
const passed = checks.pipeline_resumable && checks.dedupe_working &&
checks.org_completeness.pass && checks.contact_coverage.pass &&
checks.workflows_exercised && checks.no_mislabeled_inferred;
- const result = { passed, checked_at: new Date().toISOString(), checks };
- await set('california_gate', result, 'system:gate');
+ const result = { state: ST, passed, checked_at: new Date().toISOString(), checks };
+ // Store per-state; keep california_gate for back-compat (dashboard reads it).
+ await set(ST.toLowerCase() + '_gate', result, 'system:gate');
+ if (ST === 'CA') await set('california_gate', result, 'system:gate');
return result;
}
+/** Back-compat wrapper — the CA gate is the phase-transition gate that unlocks Arizona. */
+async function californiaGate() { return stateGate('CA'); }
+
/** Arizona is unlocked ONLY by a passing CA gate or an explicit, audit-logged admin override. */
async function unlockArizona({ actor, override = false }) {
const gate = await californiaGate();
@@ -133,4 +139,4 @@ async function unlockArizona({ actor, override = false }) {
async function arizonaUnlocked() { return (await get('arizona_unlocked', false)) === true; }
-module.exports = { get, getAll, set, californiaGate, unlockArizona, arizonaUnlocked };
+module.exports = { get, getAll, set, stateGate, californiaGate, unlockArizona, arizonaUnlocked };
diff --git a/test/pr/integration.test.js b/test/pr/integration.test.js
index 2dd03cd8..ecc4c210 100644
--- a/test/pr/integration.test.js
+++ b/test/pr/integration.test.js
@@ -231,3 +231,19 @@ test('audit log recorded every step of the workflow', async () => {
test.after(async () => {
// leave the test DB in place for inspection; it is throwaway by name.
});
+
+test('state-aware quality gate: computes CA and AZ independently, stores per-state', async () => {
+ const caGate = await settings.stateGate('CA');
+ assert.equal(caGate.state, 'CA');
+ assert.ok(typeof caGate.passed === 'boolean');
+ assert.ok(caGate.checks.contact_coverage && typeof caGate.checks.contact_coverage.pct === 'number');
+ const azGate = await settings.stateGate('AZ');
+ assert.equal(azGate.state, 'AZ');
+ // stored per-state and back-compat CA key still written
+ assert.ok(await settings.get('az_gate'));
+ assert.ok(await settings.get('california_gate'));
+ // the two are independent computations (different state filter) — coverage objects distinct
+ assert.notEqual(caGate.checks.contact_coverage, azGate.checks.contact_coverage);
+ // californiaGate() wrapper still works
+ assert.equal((await settings.californiaGate()).state, 'CA');
+});
← cf8d9dc2 yoloforever: cycle 7 ledger — AZ coverage 81%, name gate har
·
back to Rentv 2026
·
yoloforever ledger: autonomous tick — state-aware gate shipp 2413bbdd →