Repository navigation
v0.6.21: Skill regressions caught + canonical-home boundary
Sixteen skill-content fixes from live runs
This release tightens the report-builder skill against sixteen regressions caught in the wild after v0.6.17 / v0.6.20 shipped. All fixes are skill-content + boundary-doc + version bump only — no Python code changes.
Each fix below is pinned to a verbatim regression marker from a real run, so the same drift can't recur silently.
1. Windows save flow + plain-English filter summary
The portable temp-file path resolves at runtime via python -c "import tempfile, os; print(...)", so save mode no longer fails silently on Windows when the agent had hardcoded /tmp/. Every preview / save reply now also includes a plain-English filter summary translating each FilterSet field to a one-line outcome (e.g. min_demographic_usa_share: 50 → "only channels with strong US audiences will be included"; cross_references[].exclude_proposed_to_brand: ["Webull"] → "channels already pitched to Webull will automatically be excluded"). Tool-call narration is also capped at phase-outcome level — the harness already shows tool-call detail in collapsible UI.
Regression marker: "Could not read --config-file: [Errno 2] No such file or directory"; "the campaign config (held in working memory; not echoed to chat per the skill's rules)"
2. Save tail is mandatory in every preview
A live FRÉ Skincare run wrote <temp>/fre-skincare-shortlist.csv and skipped the "say save" tail; an aviation/non-MSN run closed only with a refinement offer ("If you want me to tighten to fixed-wing-only or drop drones, say the word") — also no save tail. Both regressions: every preview reply now closes with "If you want this saved as a TL report, just say save.". Refinement offers and the save tail co-exist (refinements first; save tail always last).
3. No chat-only analyst replies bypassing Phase 1–4
A "Brands sponsoring Linus Tech Tips in the past 6 months" prompt produced a free-floating markdown table directly in chat — no FilterSet, no columns, no widgets, no save option. Phases 1–4 always run; the skill never short-circuits to a chat-only data answer.
4. No side-channel deliverables; no ad-hoc data-engineering pipelines
The skill produces exactly two output shapes — a saved TL report (save mode) or a Phase-4 preview with sample-rows table + takeaways + save tail (preview mode). It does not write CSVs, custom dedupe scripts, or any other "full list" file to disk. A real aviation run produced /tmp/aviation_by_name.csv, /tmp/aviation_desc.csv, consolidate_aviation.py, and ended with aviation_consolidated.csv — none of that is the skill's job. The right shape is one ES query with the niche keywords + filters, then a sample, then the FilterSet preview.
5. User-facing language cleaned up
- TL report (not "Campaign #N") — "Report saved. … (Campaign #23801)" leaked the Django model name. The user has never heard "Campaign"; the platform calls these reports.
- Subscribers (not "reach") — "By reach: 1M+ → 2 · 100K–1M → 57 · 10K–100K → 128" leaked the SQL column. The canonical mapping (
AMs say subscribers, SQL says reach) lives intl-data/business-glossary.md; user-facing language is always "subscribers". - Names without
(id N)suffixes — "Crypto Journey (id 1178513)" in sample-table rows is implementation noise. The Markdown link ([Crypto Journey](https://app.thoughtleaders.io/youtube/crypto-journey)) is the addressable identifier; no raw ID needed. - TL platform hyperlinks (not YouTube URLs) — channel rows in the sample-rows table link to
https://app.thoughtleaders.io/youtube/<slug>, never toyoutube.com/@x. Falls back to ID-based TL paths ifslugis missing.
6. Topics-table fetch uses canonical SQL — no more schema-guessing
Two regressions on thoughtleaders_topics: an AI/marketing run guessed WHERE is_active = TRUE (column doesn't exist) and burnt three round-trips before consulting information_schema. A travel/digital-nomad run guessed SELECT id, name, type, parent_id … WHERE name ILIKE ANY(...) — type and parent_id don't exist either. The skill now uses one verbatim fetch query (SELECT id, name, description, keywords FROM thoughtleaders_topics ORDER BY id LIMIT 100 OFFSET 0) and never pushes name-pattern WHERE clauses into the SQL. The canonical schema reference lists every hallucinated column with the regression marker so it can't recur.
7. Skill routing fixed at the harness level
"Show me partnerships from last quarter for beauty creators" (golden G07) was being routed to the tl skill instead of tl-report-builder because both descriptions had overlapping triggers. Fixed:
tl-report-builder/SKILL.mddescription claims dominance for any list/report-shaped request, with per-type triggers verbatim.tl/SKILL.mddescription has an explicit DEFER clause: "would the answer be a TL report? if yes, defer totl-cli:tl-report-builder".
Both descriptions now point at each other for the boundary case.
8. Skill-content boundary hardened (data_plane.md anti-pattern)
An earlier mid-branch commit added tl-report-builder/references/data_plane.md to consolidate the topics-table fetch SQL out of inline tool prose. Right shape (don't restate schema in tool text), wrong home — it forked schema content into a parallel references file that would silently drift from the canonical postgres-schema.md. Fix: relocate the topics-table entry + slug column into skills/tl/references/postgres-schema.md (with companion upstream change in thoughtleaders-skills/tl-data/references/postgres-schema.md, thoughtleaders-skills#42), delete the local file, rewire references via Markdown links.
AGENTS.md "Skill content boundaries" gains an explicit "Anti-pattern: skill-local schema references" subsection, pinned to the data_plane.md regression. Includes a rule of thumb ("if you're about to write 'here's the SQL to query this table' or 'these columns don't exist' anywhere outside skills/tl/references/, stop") and a positive list of when skill-local references ARE appropriate (column metadata for a specific report type, tool-specific JSON schemas, disambiguation tables).
postgres-schema.md on both sides (upstream + local plugin copy) gains a "canonical home" preamble.
How to upgrade
tl setup claude --reinstallThen start a fresh Claude Code session so the new skill content loads.
The "fresh session" step is important — Claude Code caches the SKILL.md it loaded at session start. If tl --version says 0.6.21 but the skill still behaves like v0.6.20 (e.g. dumps JSON to chat, narrates "Campaign #N", or links sample rows to YouTube), open a new session.
Companion change
thoughtleaders-skills#42 lands the thoughtleaders_topics table entry, the slug column on thoughtleaders_channel, and the "canonical home" preamble in the upstream tl-data/references/postgres-schema.md. The local plugin copy in this release mirrors that shape so the next sync is a no-op.