Book ↔ App consistency audit — May 2026¶
A read-only audit comparing every concrete claim the Intelligent Book makes against the running prototype. Each finding is classified as drift (book wrong), drift (app wrong), or OK (matches). Run after commits
21a93c5(sims quality pass),e439b6d(learning graph upgrades),edb7ae1(book↔app framing), and252797c(Part II styling-rule reference).
Scope¶
| Check | Compared | Verdict |
|---|---|---|
| C1 Numeric constants | book S1/S4/S5/S6/Ch04 ↔ history_tool.py, color_tool.py, styling_agent.py |
OK |
| C2 Occasion taxonomy | book S3/Ch04 ↔ OCCASION_TAG_MAP |
OK (5 canonical tags) |
| C3 Required pieces | book S3/S5 ↔ REQUIRED_PIECES |
drift (book wrong) — D1 |
| C4 Seven-step labels | book Ch 03 ↔ styling_agent.run_agent step names |
drift (book wrong) — D2 |
| C5 Forbidden-token list | book S8 ↔ fit_tool.FORBIDDEN_TOKENS |
drift (book wrong) — D3 |
| C6 Reasoning-trail phrases | book S6/S7/Ch08 ↔ live styling_agent.py strings |
OK (verified literals) |
| C7 Reasoning-Graph entity count | book Reasoning-Graph chapter ↔ graph/schema.md ↔ runtime graph_tool._NODE_STYLES |
OK (9 entities) |
| C8 Schema relation count | book chapter + graph/schema.md header ↔ relations table |
drift (file wrong) — D4 |
| C9 Rule-slug coverage | every RULES entry in rule_refs.py ↔ Part II / Part I citations |
OK (already protected by tests/test_rule_refs.py) |
| C10 Live reasoning graph parity | graph_tool.py vs. the book's improved Learning Graph viewer |
Improvement target (not a drift) |
Verified matches (no action)¶
C1 · Numeric constants¶
| Claim in book | Code | Match |
|---|---|---|
| Freshness floor 0.4 | history_tool.get_freshness returns max(0.4, min(1.0, …)) |
✓ |
| Frequency penalty caps at 4 wears | 0.25 × min(worn_count, 4) / 4 |
✓ |
| Recency window 3 days | if (t - d).days < 3 |
✓ |
| Color base score 60 | score = 60 (color_tool.score_outfit_colors line 100) |
✓ |
| Color tiers +8 / +4 / −10 | lines 110–117 | ✓ |
| Color clamp 0–100 | max(0, min(100, score)) line 120 |
✓ |
| Accessory cap 2 | accessories_sorted[:2] line 904 |
✓ |
| Outerwear injection <60°F | if isinstance(temp, …) and temp < 60 line 919 |
✓ |
C2 · Occasion taxonomy¶
The five canonical tags work / gym / dinner / formal / casual are
the only values OCCASION_TAG_MAP resolves to. The book is correct.
C6 · Reasoning-trail phrases¶
The verbatim phrases the book claims the agent emits all exist:
- "would round out your wardrobe" —
styling_agent.py:1280 - "you haven't worn '
' in N days — bringing it back today" —styling_agent.py:545-546 - Rule-slug tags
[<pack>#R<N>]— emitted viarule_refs.cite().
C7 · Reasoning Graph entities¶
9 entity types declared in graph/schema.md match the 9 styled in
graph_tool._NODE_STYLES (User, FitProfile, CalendarEvent,
WeatherSnapshot, WardrobeItem, OutfitRecommendation, WardrobeGap,
ShoppingSuggestion, Feedback). Same nine appear in
docs/sims/knowledge-graph/index.md.
C9 · Rule-slug coverage¶
Every slug in rule_refs.RULES resolves to a real ## R<N> heading
(enforced by tests/test_rule_refs.py). Every active slug is cited
at least once in either Part I, Part II, or a MicroSim Linked-Concept
block.
Drift findings¶
D1 · REQUIRED_PIECES — gym entry (book S3 wrong)¶
The Part-II chapter book/S3-occasion-and-context.md
prints the table as pseudocode:
REQUIRED_PIECES = {
"work": {"top", "bottom"},
"gym": {"top", "bottom"}, # activewear-tagged ← wrong
"dinner": {"top", "bottom"},
"formal": {"dress"},
"casual": {"top", "bottom"},
}
The actual code (styling_agent.py:63–69):
REQUIRED_PIECES = {
"work": ["top", "bottom"],
"dinner": ["top", "bottom"],
"gym": ["activewear", "activewear"], ← code reality
"formal": ["dress"],
"casual": ["top", "bottom"]
}
The code treats gym as needing two activewear pieces, not a generic top+bottom. The book's pseudocode flattens that to top/bottom with a comment, which is wrong.
Action: book wrong; fix S3 pseudocode and the explanatory sentence.
D2 · Step 6 label (book Ch 03 wrong)¶
book/03-agent-workflow.md line 27 prints:
The agent emits "Check for Wardrobe Gaps" at
styling_agent.py:1144 (step6 = {"step": 6, "name": "Check for
Wardrobe Gaps", …}). One missing word.
Action: book wrong; add "for".
D3 · Forbidden-token list (book S8 wrong)¶
Book S8 lists 8 tokens:
flaw, fix, hide, minimize, correct, problem area, slimming, shameful
fit_tool.FORBIDDEN_TOKENS actually contains 17 entries across
more root concepts:
FORBIDDEN_TOKENS = {
"flaw", "flaws",
"fix",
"hide",
"minimize", "minimise",
"correct", "corrective",
"problem area", "problem-area", "problem_area", "problem zone", ← extra concept
"trouble area", "trouble-area", ← extra concept
"slimming", "slimmer",
"shameful",
}
The book's S8 chapter discusses the 8 root concepts but doesn't acknowledge that the code enforces two additional concepts ("trouble area," "problem zone") and several variant spellings (minimise, slimmer, corrective, flaws).
Action: book wrong; update S8 to either (a) reflect the full list, or (b) note that the runtime list extends beyond the 8 root concepts with additional families + spelling variants.
D4 · Schema relation count (file wrong)¶
graph/schema.md line 6 says:
"The graph is intentionally minimal — 9 entity types, 14 relation types."
The actual relations table on lines 30–46 lists 17 relations:
has_fit_profile, owns, has_event, sees_weather,
prefers_color_palette, prefers_silhouette,
has_occasion_type, requires_layer_for,
matches_occasion, is_appropriate_for_season,
recommends, addresses_event, flags_gap,
suggests_to_buy, was_rejected_with, excludes_from_pool,
cites_step
The Reasoning-Graph chapter (docs/sims/knowledge-graph/index.md)
inherits the same wording.
Action: file wrong; update schema.md and the chapter to "17
relation types."
Improvement target (not a drift)¶
C10 · Live Reasoning Graph in the app¶
graph_tool.py (the pyvis renderer that powers the live-run graph
inside Streamlit) is mature: per-entity styles, semantic node-size
encoding (wear count, outfit-piece count), curved edges, body-positive
hover tooltips, an explicit LEGEND_DICT documented in code.
What it lacks, compared to the recently-improved
docs/sims/learning-graph/main.html:
- Force-directed layout by default. McCreary's pattern is that a
provenance graph should open hierarchical (top-down) so the
User → Event → Outfit → Items chain reads at a glance. Wearly's
live graph currently opens in
forceAtlas2Basedand lets physics converge, which makes the first frame look like a blob. - No back-link to the abstract schema. A first-time viewer of the live graph has no path to "this is one instance of a well-defined schema — here's the schema."
These aren't drift; they're upgrades that bring the app's runtime graph to parity with the book's interactive Learning Graph.
Summary¶
| Class | Count | Items |
|---|---|---|
| Verified matches (no action) | 6 | C1, C2, C6, C7, C9, parts of C5 |
| Drift, book wrong | 3 | D1, D2, D3 |
| Drift, file wrong | 1 | D4 |
| Drift, app wrong | 0 | — |
| Improvement target | 1 | C10 |
Healthy state. No runtime behavior drift was found. The four drift items are all on the documentation side and are small enough to fix in one commit. The improvement target is a clean upgrade with no removed behavior.
The runtime contracts — body-positive language, freshness floor, color-score formula, accessory cap, outerwear threshold, occasion taxonomy — all hold. The agent does what the book says it does. The book's published numbers and named phrases are correct.
The drifts are exactly the kind that creep in when documentation is written before code stabilizes (D1, D2 are recent — Part II shipped two days ago) or when the documented list intentionally simplifies the implementation (D3 — book says 8 root concepts because that's the teachable unit; code has 17 entries because variants get caught).
Audit performed by reading: history_tool.py, color_tool.py,
fit_tool.py, styling_agent.py, graph_tool.py, rule_refs.py,
graph/schema.md, all book/*.md chapters, docs/sims/*/index.md,
and the test suite. No code was changed during the audit.