D1-A: combat glyphs, boundary-channel blue, reaction primitives, READ-FAB

Additive frontend/visual. Backend untouched (git diff src/ = empty): no grade,
model, classifier or ledger change. The 41->74 registry expansion is HELD for
D1-B. Push scoring untouched.

REVIEW ZERO — classifier coverage bounded the glyph wiring. Three buckets, and
the computation was redone three times before it was right (the frontend keys
GLYPHS by ARCHETYPE NAME while the backend keys `glyph:` by SHAPE NAME, and most
registry keys are unquoted identifiers — the first two passes mis-parsed both):
  (a) classifier-backed, already wired: 38
  (b) classifier-backed, package SVG exists, NOT wired -> WIRED HERE: 6
      striker, grappler, pressure, counter, grinder, finisher — all MMA/combat
      archetypes in archetypeService.js that were rendering EMOJI fallbacks
      ('*', 'x', '>', '<>') where the package ships real 24-grid duotone marks.
  (c) package SVG with no classifier -> HELD for D1-B: 39 (wiring them would
      render nothing)
  (d) classifier-backed but NO package SVG: 2 ('dual threat', 'paint boss') —
      a DESIGN gap, not a build gap; flagged for D1-B.
GLYPHS map 38 -> 44 keys, deliberately far short of the package's 83.

BOUNDARY CHANNEL — the blue tokens already existed (--priced-out set) and were
applied on NoMarketState and the scan void box, but PriceTriplet's NO_MODEL
("line not priced") still rendered in neutral text, so the channel was applied
inconsistently. NO_MODEL now renders in the channel, completing "every boundary
state or none". Token-only (no hex fallback and no hex in prose — PriceTriplet's
own test forbids literal hex, and it caught both).

REACTION PRIMITIVES — new web/src/lib/reactions.js + globals.css keyframes at the
exact HANDOFF timings: flash .75s ease-out, boot stagger 60ms steps, reactions
gated 1.5s, WIRE hold 6s. nudge() REFUSES a no-op (null/absent direction -> no
flash) so the primitive cannot be attached to an idle loop — a flash without a
new datum is the UI lying about the feed. Reduced-motion honoured.

READ-FAB — aligned to the exact package geometry: 50px circle, translateY(-14px),
6px void ring (was 46px, marginTop -16, 3px ring).

CARD TOKEN — audit correction: #0E0E14 was already tokenised as --bg-1/--card;
the audit's "1 file" was counting the raw hex, not the token. No change needed.

Floor: 317 suites / 3946 tests green (16 new), web build exit 0.

NOT DONE THIS ORDER (reported, not silently dropped): row-hover rationale and
IntersectionObserver reveal (Phase 3 item 8) and team-gradient chips (Phase 4
item 10) are not implemented — they need the System artboard's row anatomy,
which is a larger port than the rest of D1-A.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QJs13VsyiSKYQP6rj3NNmc
This commit is contained in:
Kev
2026-07-31 06:04:19 -04:00
parent 49565b5f02
commit 3d1a3c7794
7 changed files with 242 additions and 6 deletions
+7 -4
View File
@@ -188,14 +188,17 @@ export default function BottomTabBar() {
const body = t.primary ? (
// Scan — prominent raised action
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'flex-start', gap: 3, paddingTop: 4 }}>
{/* D1-A READ-FAB — exact package spec: 50px green circle, void-coloured V,
translateY(-14px), 6px void ring. Was 46px / -16px / 3px ring. */}
<span
className="read-fab"
style={{
width: 46,
height: 46,
marginTop: -16,
width: 50,
height: 50,
transform: 'translateY(-14px)',
borderRadius: '50%',
background: 'var(--g-a)',
border: '3px solid var(--bg-0)',
border: '6px solid var(--bg-0)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
+15 -1
View File
@@ -177,6 +177,13 @@ function Leg({
);
}
/** The boundary channel — one meaning: we cannot hand you this number. */
function modelAbsentColor(state: string): string | null {
// Token-only: PriceTriplet forbids literal hex (its own test enforces it), and
// --priced-out is defined in globals.css, so no fallback is needed here.
return state === STATES.NO_MODEL ? 'var(--priced-out)' : null;
}
export default function PriceTriplet({
data,
compact = false,
@@ -233,7 +240,14 @@ export default function PriceTriplet({
// (verdictFor returns null for this state), never QUARANTINE's "suppressed" copy.
const modelAbsent = state === STATES.NO_MODEL;
// GREEN = TAKEABLE EDGE ONLY. The model leg is green on exactly one state.
const modelColor = state === STATES.VALUE ? 'var(--g-a)' : 'var(--text-0)';
// D1-A boundary channel (2026-07-31): the blue channel is the system being HONEST about what it
// cannot hand you — edge priced out / no market / LINE NOT PRICED. NO_MODEL is the third
// such state and was the last one still rendering in neutral text, so the channel was
// applied inconsistently. It is NOT red (that is a model REFUSAL — a decision) and NOT
// amber (that is QUARANTINE — a leg suppressed). Never decorative: this is the only
// reason the colour appears here.
const modelColor = state === STATES.VALUE ? 'var(--g-a)'
: modelAbsentColor(state) || 'var(--text-0)';
const verdict = verdictFor(state, vsFair, Number.isFinite(ev as number) ? (ev as number) : null);
return (