openvibers/openvibe.reviews Commits
Official records count toward the index gate (their own lines and their public body as a source): 1,155 NYC restaurant pages with their inspections were in no sitemap and no network search; bare counts (a CFPB tally, an HPD building) stay under the word floor (#86)
Changes
@@@ -624,12 +624,14 @@ function createReviewsService({ db, stores, outbox, config, now = () => Date.nowconst rec = rev && rev.meta && rev.meta.authorship;// Members' reviews count as substance: their words (from three reviews up) and each review as a citation.const mem = await memberStats(members);++ // So do official records: their own lines and their public body as the source (recordSubstance).++ const recs = recordSubstance(await recordsView(e.id));return {state: e.state === 'deleted' ? 'deleted' : 'published',visibility: 'public',canonicalUrl: entityUrl(e),-- wordCount: (rev ? ssr.wordCount(summaryText(rev)) : 0) + (mem.count >= 3 ? mem.words : 0),-- citationCount: active.length + mem.count,++ wordCount: (rev ? ssr.wordCount(summaryText(rev)) : 0) + (mem.count >= 3 ? mem.words : 0) + recs.words,++ citationCount: active.length + mem.count + recs.sources,unsupportedClaims: rev ? await unsupportedCount(e.id, rev) : 0,noindex: !!e.noindex,...(rec ? authorship.gateFacts(rec, await reviews.latest(summary.id, rev.number)) : {}),@@@ -1045,6 +1047,21 @@ function createReviewsService({ db, stores, outbox, config, now = () => Date.now}/** An entity's official records, for its page: the CFPB count, HPD's open violations, NYC inspections by day (newest first). */++ /**++ * What an entity's official records add to the index gate: the words of their specific lines (each inspection's++ * date, grade, score and violations; the complaint count; the housing counts by class), never the page's fixed++ * captions and caveats, and one source per public body. Only summaries and members' reviews counted before, so++ * 1,155 NYC restaurant pages with their inspections were in no sitemap and no network search (2026-10-18). A page++ * whose records are a bare count (a CFPB tally, an HPD building) stays under the gate's word floor.++ */++ function recordSubstance(rec) {++ const lines = [];++ if (rec.complaints) lines.push(`CFPB complaints ${rec.complaints.count}`);++ if (rec.housing) lines.push(`HPD open violations ${rec.housing.open}: class C ${rec.housing.c}, class B ${rec.housing.b}, class A ${rec.housing.a}${rec.housing.last ? `; inspected ${rec.housing.last}` : ''}`);++ for (const i of rec.inspections) lines.push([i.date, i.grade && `grade ${i.grade}`, i.score != null && `score ${i.score}`, ...i.violations.map((v) => v.text)].filter(Boolean).join(' '));++ return { words: lines.length ? ssr.wordCount(lines.join('\n')) : 0, sources: (rec.complaints ? 1 : 0) + (rec.housing ? 1 : 0) + (rec.inspections.length ? 1 : 0) };++ }++async function recordsView(entityId) {const rows = await q.recordsOf.all([...await closure(entityId)]);const out = { complaints: null, housing: null, inspections: [], restaurant: null };@@@ -51,6 +51,27 @@ t('NYC HPD: a building with open hazardous violations is a landlord page with th} finally { await h.stop(); }});++t('a restaurant page with its inspections is indexable on its own; a bare count is not', async () => {++ const h = await boot();++ try {++ // NYC Health's violation texts are the page's own substance (60 words is the gate's floor).++ const cited = [++ ['06D', 'Food contact surface not properly washed, rinsed and sanitized after each use and following any activity when contamination may have occurred.'],++ ['08A', 'Establishment is not free of harborage or conditions conducive to rodents, insects or other pests, including holes, gaps and standing water.'],++ ['02G', 'Cold food item held above 41 degrees Fahrenheit (smoked fish and reduced oxygen packaged foods above 38 degrees) except during necessary preparation.'],++ ];++ await cited.reduce((p, [code, violation]) => p.then(() => importItem(h, nyc({ inspection_date: '2026-10-01T00:00:00.000', violation_code: code, violation, critical_flag: 'Critical' }))), Promise.resolve());++ await importItem(h, cfpb('CAPITAL ONE FINANCIAL CORPORATION', 34100));++ await importItem(h, hpd({ buildingid: '288857', housenumber: '760', streetname: 'ELDERT LANE', open: '1794', c: '423', b: '1118', a: '252' }));++ const index = (await req(h, 'GET', '/sitemap.xml')).text;++ assert.match(index, /\/sitemaps\/entities-1\.xml/);++ const urls = (await req(h, 'GET', '/sitemaps/entities-1.xml')).text;++ assert.match(urls, /\/e\/the-coffee-club-9526-4-avenue-brooklyn-ny</, 'the restaurant, by its inspection');++ assert.ok(!/capital-one/.test(urls) && !/eldert/.test(urls), 'a complaint count or a housing tally alone stays out (thin)');++ assert.ok(!/noindex/.test((await req(h, 'GET', '/e/the-coffee-club-9526-4-avenue-brooklyn-ny')).text), 'and its page says so');++ } finally { await h.stop(); }++});++t('company names read as the companies write them', () => {const { recordName } = require('../server/reviews/service');assert.strictEqual(recordName('JPMORGAN CHASE & CO.'), 'JPMorgan Chase & Co.');
@@ -624,12 +624,14 @@ function createReviewsService({ db, stores, outbox, config, now = () => Date.now
@@ -624,12 +624,14 @@ function createReviewsService({ db, stores, outbox, config, now = () => Date.now
const rec = rev && rev.meta && rev.meta.authorship;
const rec = rev && rev.meta && rev.meta.authorship;
// Members' reviews count as substance: their words (from three reviews up) and each review as a citation.
// Members' reviews count as substance: their words (from three reviews up) and each review as a citation.
const mem = await memberStats(members);
const mem = await memberStats(members);
+ // So do official records: their own lines and their public body as the source (recordSubstance).
+ const recs = recordSubstance(await recordsView(e.id));
return {
return {
state: e.state === 'deleted' ? 'deleted' : 'published',
state: e.state === 'deleted' ? 'deleted' : 'published',
visibility: 'public',
visibility: 'public',
canonicalUrl: entityUrl(e),
canonicalUrl: entityUrl(e),
- wordCount: (rev ? ssr.wordCount(summaryText(rev)) : 0) + (mem.count >= 3 ? mem.words : 0),
- citationCount: active.length + mem.count,
+ wordCount: (rev ? ssr.wordCount(summaryText(rev)) : 0) + (mem.count >= 3 ? mem.words : 0) + recs.words,
+ citationCount: active.length + mem.count + recs.sources,
unsupportedClaims: rev ? await unsupportedCount(e.id, rev) : 0,
unsupportedClaims: rev ? await unsupportedCount(e.id, rev) : 0,
noindex: !!e.noindex,
noindex: !!e.noindex,
...(rec ? authorship.gateFacts(rec, await reviews.latest(summary.id, rev.number)) : {}),
...(rec ? authorship.gateFacts(rec, await reviews.latest(summary.id, rev.number)) : {}),
@@ -1045,6 +1047,21 @@ function createReviewsService({ db, stores, outbox, config, now = () => Date.now
@@ -1045,6 +1047,21 @@ function createReviewsService({ db, stores, outbox, config, now = () => Date.now
}
}
/** An entity's official records, for its page: the CFPB count, HPD's open violations, NYC inspections by day (newest first). */
/** An entity's official records, for its page: the CFPB count, HPD's open violations, NYC inspections by day (newest first). */
+ /**
+ * What an entity's official records add to the index gate: the words of their specific lines (each inspection's
+ * date, grade, score and violations; the complaint count; the housing counts by class), never the page's fixed
+ * captions and caveats, and one source per public body. Only summaries and members' reviews counted before, so
+ * 1,155 NYC restaurant pages with their inspections were in no sitemap and no network search (2026-10-18). A page
+ * whose records are a bare count (a CFPB tally, an HPD building) stays under the gate's word floor.
+ */
+ function recordSubstance(rec) {
+ const lines = [];
+ if (rec.complaints) lines.push(`CFPB complaints ${rec.complaints.count}`);
+ if (rec.housing) lines.push(`HPD open violations ${rec.housing.open}: class C ${rec.housing.c}, class B ${rec.housing.b}, class A ${rec.housing.a}${rec.housing.last ? `; inspected ${rec.housing.last}` : ''}`);
+ for (const i of rec.inspections) lines.push([i.date, i.grade && `grade ${i.grade}`, i.score != null && `score ${i.score}`, ...i.violations.map((v) => v.text)].filter(Boolean).join(' '));
+ return { words: lines.length ? ssr.wordCount(lines.join('\n')) : 0, sources: (rec.complaints ? 1 : 0) + (rec.housing ? 1 : 0) + (rec.inspections.length ? 1 : 0) };
+ }
+
async function recordsView(entityId) {
async function recordsView(entityId) {
const rows = await q.recordsOf.all([...await closure(entityId)]);
const rows = await q.recordsOf.all([...await closure(entityId)]);
const out = { complaints: null, housing: null, inspections: [], restaurant: null };
const out = { complaints: null, housing: null, inspections: [], restaurant: null };
@@ -51,6 +51,27 @@ t('NYC HPD: a building with open hazardous violations is a landlord page with th
@@ -51,6 +51,27 @@ t('NYC HPD: a building with open hazardous violations is a landlord page with th
} finally { await h.stop(); }
} finally { await h.stop(); }
});
});
+t('a restaurant page with its inspections is indexable on its own; a bare count is not', async () => {
+ const h = await boot();
+ try {
+ // NYC Health's violation texts are the page's own substance (60 words is the gate's floor).
+ const cited = [
+ ['06D', 'Food contact surface not properly washed, rinsed and sanitized after each use and following any activity when contamination may have occurred.'],
+ ['08A', 'Establishment is not free of harborage or conditions conducive to rodents, insects or other pests, including holes, gaps and standing water.'],
+ ['02G', 'Cold food item held above 41 degrees Fahrenheit (smoked fish and reduced oxygen packaged foods above 38 degrees) except during necessary preparation.'],
+ ];
+ await cited.reduce((p, [code, violation]) => p.then(() => importItem(h, nyc({ inspection_date: '2026-10-01T00:00:00.000', violation_code: code, violation, critical_flag: 'Critical' }))), Promise.resolve());
+ await importItem(h, cfpb('CAPITAL ONE FINANCIAL CORPORATION', 34100));
+ await importItem(h, hpd({ buildingid: '288857', housenumber: '760', streetname: 'ELDERT LANE', open: '1794', c: '423', b: '1118', a: '252' }));
+ const index = (await req(h, 'GET', '/sitemap.xml')).text;
+ assert.match(index, /\/sitemaps\/entities-1\.xml/);
+ const urls = (await req(h, 'GET', '/sitemaps/entities-1.xml')).text;
+ assert.match(urls, /\/e\/the-coffee-club-9526-4-avenue-brooklyn-ny</, 'the restaurant, by its inspection');
+ assert.ok(!/capital-one/.test(urls) && !/eldert/.test(urls), 'a complaint count or a housing tally alone stays out (thin)');
+ assert.ok(!/noindex/.test((await req(h, 'GET', '/e/the-coffee-club-9526-4-avenue-brooklyn-ny')).text), 'and its page says so');
+ } finally { await h.stop(); }
+});
+
t('company names read as the companies write them', () => {
t('company names read as the companies write them', () => {
const { recordName } = require('../server/reviews/service');
const { recordName } = require('../server/reviews/service');
assert.strictEqual(recordName('JPMORGAN CHASE & CO.'), 'JPMorgan Chase & Co.');
assert.strictEqual(recordName('JPMORGAN CHASE & CO.'), 'JPMorgan Chase & Co.');