← back to Lifestyle Asset Intel
yolo tick #16: exotic / special-edition badges + audit-test race fix
447537f7bb7ff8a6bde38c1e62b1ec25b90cbf40 · 2026-05-10 07:55:57 -0700 · Steve Abrams
Surface canonical_assets.attrs.exotic and attrs.special_edition on the
console grid, family pages, and asset detail h1 — quick visual signal
for the 14 Sotheby's-sourced exotic configs added in tick #13. Pure
EJS conditional + CSS, no schema changes.
.exotic-badge — accent-tinted pill (e.g., "EXOTIC" on Niloticus
Himalaya, Crocodile, Alligator, Lizard configs)
.edition-badge — warn-tinted pill (e.g., "FAUBOURG", "RAINY DAYS",
"MIDAS" on the Sotheby's special-edition lots)
lib/valuation.listAssets now returns ca.attrs in the SELECT so the
templates can read attrs.exotic and attrs.special_edition without a
second query. valueAsset() already returned attrs as part of the
asset block.
Audit test fix: tests/audit.test.js "GET /api/health is NOT logged"
was racing against parallel test files (node --test runs files
concurrently) — other tests' audit rows landed during the 200ms
sleep, inflating the unfiltered COUNT(*). Tightened the assertion
to filter `WHERE route = '/api/health'` so the test only counts
its own writes. Caught a real flake, not a regression.
44/44 tests green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Files touched
M lib/valuation.jsM public/css/app.cssM tests/audit.test.jsM views/asset.ejsM views/family.ejsM views/index.ejs
Diff
commit 447537f7bb7ff8a6bde38c1e62b1ec25b90cbf40
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Sun May 10 07:55:57 2026 -0700
yolo tick #16: exotic / special-edition badges + audit-test race fix
Surface canonical_assets.attrs.exotic and attrs.special_edition on the
console grid, family pages, and asset detail h1 — quick visual signal
for the 14 Sotheby's-sourced exotic configs added in tick #13. Pure
EJS conditional + CSS, no schema changes.
.exotic-badge — accent-tinted pill (e.g., "EXOTIC" on Niloticus
Himalaya, Crocodile, Alligator, Lizard configs)
.edition-badge — warn-tinted pill (e.g., "FAUBOURG", "RAINY DAYS",
"MIDAS" on the Sotheby's special-edition lots)
lib/valuation.listAssets now returns ca.attrs in the SELECT so the
templates can read attrs.exotic and attrs.special_edition without a
second query. valueAsset() already returned attrs as part of the
asset block.
Audit test fix: tests/audit.test.js "GET /api/health is NOT logged"
was racing against parallel test files (node --test runs files
concurrently) — other tests' audit rows landed during the 200ms
sleep, inflating the unfiltered COUNT(*). Tightened the assertion
to filter `WHERE route = '/api/health'` so the test only counts
its own writes. Caught a real flake, not a regression.
44/44 tests green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---
lib/valuation.js | 2 +-
public/css/app.css | 15 +++++++++++++++
tests/audit.test.js | 11 +++++++++--
views/asset.ejs | 2 ++
views/family.ejs | 2 +-
views/index.ejs | 2 +-
6 files changed, 29 insertions(+), 5 deletions(-)
diff --git a/lib/valuation.js b/lib/valuation.js
index d3f607a..bee13f8 100644
--- a/lib/valuation.js
+++ b/lib/valuation.js
@@ -320,7 +320,7 @@ async function listAssets(opts) {
`SELECT ca.id, ca.slug,
mf.name AS family_name, mf.slug AS family_slug,
b.name AS brand_name, b.slug AS brand_slug,
- ca.size, ca.material, ca.color, ca.hardware, ca.region,
+ ca.size, ca.material, ca.color, ca.hardware, ca.region, ca.attrs,
vs.q10, vs.q50, vs.q90, vs.liquidity_score AS seed_liquidity_score,
vs.confidence, vs.expected_dts AS seed_expected_dts,
vs.comp_count, vs.as_of,
diff --git a/public/css/app.css b/public/css/app.css
index 8396e1e..80b12b6 100644
--- a/public/css/app.css
+++ b/public/css/app.css
@@ -178,6 +178,21 @@ tfoot tr.totals th, tfoot tr.totals td {
font-weight: 600; color: var(--fg);
}
+/* exotic + special-edition badges (v0.2) */
+.exotic-badge, .edition-badge {
+ display: inline-block;
+ font-size: 0.62em;
+ font-weight: 700;
+ letter-spacing: 0.06em;
+ padding: 0.12rem 0.45rem;
+ border-radius: 999px;
+ vertical-align: 0.15em;
+ margin-left: 0.35rem;
+ font-family: var(--mono);
+}
+.exotic-badge { background: color-mix(in srgb, var(--accent) 22%, var(--card)); color: var(--accent); border: 1px solid color-mix(in srgb, var(--accent) 50%, var(--line)); }
+.edition-badge { background: color-mix(in srgb, var(--warn) 22%, var(--card)); color: var(--warn); border: 1px solid color-mix(in srgb, var(--warn) 50%, var(--line)); }
+
/* MSRP / premium-to-retail block (v0.2) */
.msrp-block .premium-cell { background: color-mix(in srgb, var(--accent) 14%, var(--card)); border-color: color-mix(in srgb, var(--accent) 35%, var(--line)); }
.msrp-block .premium-val { color: var(--accent); font-size: 1.05em; }
diff --git a/tests/audit.test.js b/tests/audit.test.js
index 049706c..7fb4d88 100644
--- a/tests/audit.test.js
+++ b/tests/audit.test.js
@@ -90,11 +90,18 @@ test('GET /api/valuation/:slug records a row in valuation_calls', async () => {
});
test('GET /api/health is NOT logged (skipped per SKIP_PATHS)', async () => {
- const before = await pool.query(`SELECT COUNT(*)::int AS n FROM valuation_calls`);
+ // Filter on route='/api/health' so we don't race against other parallel
+ // test files writing audit rows for OTHER routes during the await.
+ const HEALTH_FILTER = `route = '/api/health'`;
+ const before = await pool.query(
+ `SELECT COUNT(*)::int AS n FROM valuation_calls WHERE ${HEALTH_FILTER}`
+ );
const r = await getJson('/api/health');
assert.equal(r.status, 200);
await new Promise((res) => setTimeout(res, 200));
- const after = await pool.query(`SELECT COUNT(*)::int AS n FROM valuation_calls`);
+ const after = await pool.query(
+ `SELECT COUNT(*)::int AS n FROM valuation_calls WHERE ${HEALTH_FILTER}`
+ );
assert.equal(after.rows[0].n, before.rows[0].n, 'health probe should not be audited');
});
diff --git a/views/asset.ejs b/views/asset.ejs
index dc56fbd..5e5bfb6 100644
--- a/views/asset.ejs
+++ b/views/asset.ejs
@@ -5,6 +5,8 @@
<%= data.asset.brand %>
<a href="/family/<%= (data.asset.family || '').toLowerCase() %>"><%= data.asset.family %></a>
<%= data.asset.size || '' %>
+ <% if (data.asset.attrs && data.asset.attrs.exotic) { %><span class="exotic-badge" title="Exotic skin or special edition">EXOTIC</span><% } %>
+ <% if (data.asset.attrs && data.asset.attrs.special_edition) { %><span class="edition-badge" title="Special edition: <%= data.asset.attrs.special_edition %>"><%= String(data.asset.attrs.special_edition).toUpperCase() %></span><% } %>
</h1>
<p class="muted">
<%= data.asset.material || '—' %> · <%= data.asset.color || '—' %> · <%= data.asset.hardware || '—' %>
diff --git a/views/family.ejs b/views/family.ejs
index c779fad..1178a23 100644
--- a/views/family.ejs
+++ b/views/family.ejs
@@ -39,7 +39,7 @@
<a class="card" href="/asset/<%= a.slug %>">
<div class="card-head">
<span class="brand-tag"><%= a.brand_name %></span>
- <span class="family-tag"><%= a.family_name %> <%= a.size || '' %></span>
+ <span class="family-tag"><%= a.family_name %> <%= a.size || '' %><% if (a.attrs && a.attrs.exotic) { %> <span class="exotic-badge" title="Exotic skin or special edition">EXOTIC</span><% } %></span>
</div>
<div class="card-body">
<div class="material"><%= a.material || '—' %><% if (a.color) { %> · <%= a.color %><% } %><% if (a.hardware) { %> · <%= a.hardware %><% } %></div>
diff --git a/views/index.ejs b/views/index.ejs
index 05e9947..e709cda 100644
--- a/views/index.ejs
+++ b/views/index.ejs
@@ -42,7 +42,7 @@
data-asof="<%= a.as_of || '' %>">
<div class="card-head">
<span class="brand-tag"><%= a.brand_name %></span>
- <span class="family-tag"><%= a.family_name %> <%= a.size || '' %></span>
+ <span class="family-tag"><%= a.family_name %> <%= a.size || '' %><% if (a.attrs && a.attrs.exotic) { %> <span class="exotic-badge" title="Exotic skin or special edition">EXOTIC</span><% } %></span>
</div>
<div class="card-body">
<div class="material"><%= a.material || '—' %><% if (a.color) { %> · <%= a.color %><% } %><% if (a.hardware) { %> · <%= a.hardware %><% } %></div>
← db84b94 yolo tick #15: launchd-driven overnight autonomous loop
·
back to Lifestyle Asset Intel
·
snapshot: 6 file(s) changed, ~6 modified 06d377f →