Stop shared Unreleased edits that conflict across PRs, and cut releases via
Prepare release (towncrier + every plugin/marketplace version bump) then
auto-tag on merge, with agent-oriented CONTRIBUTING and PR template guidance.
New architecture-patterns doc: five design conventions for the
checkpointed discovery protocol (identity/TTL binding, lossless-state vs
fenced-digest split, fail-closed empty state, provenance enforcement,
guarded writes + stale-sibling invalidation), with the meta-lesson that
an external LLM API call inside an engine whose invoker IS an LLM is an
architecture smell. 56/58 claims grounded against the tree by the
validation pass; the two exceptions were corrected in place. CONCEPTS.md
gains Handoff checkpoint and refreshes Junk shape (host verdict vs
heuristic flag) and Covered (judge is now the hosting model).
Claude-Session: https://claude.ai/code/session_01BL9KsoytvnR1WfvZa7SEN7
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
- design-patterns/ranked-output-confidence-floor-honest-empty-state: top-N
rankers need an absolute confidence floor and a first-class 'nothing solid'
empty state - relative ranking cannot express 'none of this is good enough'
- logic-errors/non-daemon-executor-threads-defeat-wall-clock-budget: a
wall-clock budget over ThreadPoolExecutor is answer-bounding, not
process-bounding; daemon threads + semaphore + monotonic deadline make it real
- conventions/argparse-optional-value-flag-dispatch-truthiness: nargs='?'
const='' flags are three-state; dispatch on 'is not None', dependent flags
fail loudly
CONCEPTS.md gains a Discovery cluster (Discovery, Nomination, Enrichment pass,
Confidence floor, Nothing-solid) plus a flagged ambiguity on 'enrichment'.
All claims validated against the tree (mechanical + semantic grounding passes).
* feat(discover): U1 nominate stage with keyword-gate toggle for global vs domain
Extract the discovery fetch+normalize sweep into a reusable nominate_candidates()
and add a keyword_gate param to _fetch_discovery_source. Domain discovery keeps the
gate on; global trending (no domain) turns it off so the river feeds' own hot
ranking is the signal. run_discover now delegates its fetch loop to
nominate_candidates(keyword_gate=True), preserving existing behavior.
* feat(discover): U2 nominate_topics - named, seed-ranked candidate topics
Extract the cluster/rank/name block from run_discover into nominate_topics(),
returning Nomination objects (name, seed velocity score, cluster items, leader
summary). This is the contract between the nominate stage and the upcoming
enrichment fan-out: names deduped casefold, ranked by cheap seed velocity,
never padded past the evidence. run_discover consumes nominations with
identical output to before.
* feat(discover): U3 enrichment fan-out - full pipeline pass per nomination
enrich_nominations() runs the real research pipeline (run() with
internal_subrun=True, the comparison-mode lane) on each nominated topic in a
bounded ThreadPoolExecutor against a wall-clock batch budget. A topic whose
sub-run raises is kept as nomination-only with the error recorded; topics
unfinished at budget expiry are likewise dropped to nomination-only. The batch
never raises and preserves nomination order. This is what buys discovery the
whole multi-source corpus (Reddit+comments, X, YouTube, Techmeme, arXiv, HN,
Polymarket, web) with zero bespoke fetch code.
* feat(discover): U4 confidence floor - 'nothing solid' beats ranked noise
THE bug fix for the 2026-07-12 junk-trend regression (five 1-like tweets
ranked as a 'sports' trend list). passes_discovery_floor() gates every topic:
an absolute engagement floor kills junk outright, then a topic clears via
independent cross-source confirmation OR a genuinely strong single-source
spike (a 1,600-point HN thread is a story; a 30-upvote meme is not).
run_discover gains enrich= (full pipeline pass per nomination via U3) and
judges each topic on its enriched corpus when available, seed evidence
otherwise. Zero floor-clearing topics -> DiscoveryReport.outcome
'nothing-solid' with the strongest sub-floor name in weak_signal, exported
through to_discovery_export. Regression test: the junk corpus now returns an
honest empty result.
* feat(discover): U5 global trending mode - bare --discover with no domain
--discover DOMAIN is now nargs-optional: bare --discover sweeps every river
feed's own hot list (r/all, HN front page, Digg) with the keyword gate off -
the feeds' hot ranking is the signal and the confidence floor culls junk. X
sits out of the global nominate stage (its search lane needs a keyword) and
joins per-topic at enrichment. Dispatch keys on 'flag present' so the empty
domain is never mistaken for 'flag absent'.
Discovery now enriches by default (full research pass per nomination);
--discover-shallow is the fast escape hatch that ranks on listing evidence
only, still floored. Global saves slug as 'trending'.
* feat(discover): U6 rich trend cards + honest nothing-solid rendering
DiscoveryTopic gains top_comment (strongest verbatim community comment from
the enriched corpus, per-platform vote-normalized, with attribution) and
corroboration_count; both exported in to_discovery_export. Renderer upgrades:
'# Trending now' header for global runs, a 'confirmed across N sources' badge
on the momentum line, a '**Community voice:**' quote line on enriched cards,
and an explicit nothing-solid empty state naming the closest weak signal
instead of the old generic 'no clusters survived' line.
* feat(discover): U7 Techmeme + arXiv reach discovery through enrichment
Enrichment sub-runs get requested_sources=None by default, so every available
source - Techmeme, arXiv, YouTube, Polymarket, community comments - joins each
nominated topic's research pass despite having no river feed of its own. An
explicit user --search boundary now holds through enrichment too (the CLI
threads the original pre-narrowed list as enrich_requested_sources). The
keyword-source nominate gap (X/Techmeme/arXiv have no front-page lane) is
documented at the planner.
* docs(discover): U8 route global vs domain trending; document two-stage flow
SKILL.md discovery branch now routes bare trending asks (/last30days trending,
'what's hot') to bare --discover and domain asks to --discover DOMAIN,
documents the nominate-enrich timing (10-minute Bash timeout), the
--discover-shallow fast path, and pins 'Nothing solid this window' as a valid
verbatim pass-through outcome (never retry or fabricate around it).
CONFIGURATION.md and the JSON export reference document the new flag surface
and contract fields (outcome, weak_signal, top_comment, corroboration_count).
* fix(discover): strip quote chars from community-voice bodies to avoid doubled quotes
Live global-trending verification surfaced comments whose bodies start with a
quote character rendering as doubled quotes inside the card's wrapping quotes.
* fix(discover): address PR #816 review - real budget enforcement + shallow-flag guard
P1: enrichment workers now run as daemon threads behind a semaphore instead of
a ThreadPoolExecutor. Executor threads are non-daemon and joined at interpreter
shutdown, so one hung sub-run could keep the process alive long after its topic
was downgraded to nomination-only. Daemon workers make the wall-clock budget
real; abandonment is safe because internal_subrun passes write nothing to disk
and every fetch layer carries its own timeout.
P2: --discover-shallow without --discover now errors (exit 2) instead of
silently no-opping into a full research run.
---------
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
* feat(sources): local corpus source - your own files as a ranked signal
* fix: address self-review findings
* fix: address round-2 residual (surgical round)
* fix: defang corpus sentinels, matching-window snippets, exclusion-aware hosted gate, traversal notes, bounded discovery
* fix: keep absolute local paths out of corpus notes and coverage diagnostics
* fix(corpus): keep raw exception text out of coverage notes
OS and subprocess errors embed the failing absolute path in str(exc),
and scan/cache notes flow into source_status detail rendered in
coverage diagnostics outside the private corpus block. Notes now carry
the error's strerror (or class name) instead, so a permission failure
or file race can no longer leak a local path from a private run.
---------
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
* feat(grounding): per-claim freshness verdicts (--verify-freshness)
* fix: address self-review findings
* fix: address round-2 residual (surgical round)
* fix: prefer active markets in mixed-event verification, plan-only cookies for cached verification
* fix: verify event identity on slug-fallback refetch before assigning verdicts
* test: use a real outcome datum key in slug-identity test
* fix(freshness): carry values in stale detail and announce zero-claim passes
A stale verdict's machine-readable detail said only that the value
moved; the rendered table already showed was/now, so agent-JSON
consumers saw less than readers. Compose the detail as
"moved: <original> -> <current>", formatting each value the way the
claim text does (percent for Polymarket/StockTwits, thousands
separators for stars).
A verification pass that extracts zero claims used to complete
silently, which misled a live post-hoc run. _verify_report_set now
prints one stderr note when no verified report produced any verdict,
aggregated across main and entity reports so multi-report passes note
it once; the drill path routes through the same helper.
* fix(freshness): verify star facts attached by candidate enrichment
A GitHub-flavored run produced zero verdicts live: star enrichment
attaches metadata["github_stars"] to candidates after reranking, but
claim extraction read only item-level engagement, and those candidates'
primary items are typically non-GitHub sources.
Extraction now emits one repo-keyed star claim per enriched repo
(skipping repos already claimed at item level). These claims cannot
ride the item-source dispatch - they would land unsupported as
"reddit"/"x" claims - so verify_report routes the repo-slug shape
straight to the GitHub refetcher, bypassing the grounding-item lookup
and the per-source outcome gate (the datum came from enrichment, not
the github search source). refetch_datum accepts an owner/repo slug as
the datum key, and the snapshot cache keys these claims by repo so
multiple candidates citing one repo share one request.
* fix(freshness): review fixes - honest evidence, joinable export, rendered detail
Four review findings on the hardening pass:
- Unsupported verdicts no longer fabricate evidence: evidence_url and
evidence_timestamp stay empty when no fresh evidence was obtained;
provenance remains on source_url/source_timestamp.
- Agent export results now carry candidate_id so consumers can join
freshness_verdicts to the result they annotate (schema_version 1.2,
golden snapshot and docs updated).
- The rendered verdict table shows the verifier's detail - formatted
movement on stale rows, the un-checkable reason on unsupported rows -
instead of reconstructing raw values.
- Item-level star dedup is scoped per candidate: another candidate's
item-level claim no longer suppresses an enriched candidate's own
verdict and inline flag; both share one repo snapshot.
* fix(freshness): require event-id identity on Polymarket slug fallback
The slug-fallback refetch verified the response slug but accepted a
slug-matched event whose id differed from the cached item's event id,
so a re-created event reusing a slug could produce current/stale
verdicts from another market's prices. When the cached item carries a
real numeric event id, the fallback now requires the response id to
match it (synthetic PM<N> parse ids carry no identity and skip the
check); mismatches raise and degrade to an unsupported verdict.
* fix(freshness): fail closed when the slug fallback has no event identity
A cached item with neither metadata.event_id nor a real numeric item id
(the synthetic PM<N> parse fallback) let the slug fallback accept
whatever event currently owns the slug, so a recreated or loosely
matched event could still drive a current/stale verdict. With no
identity to verify against, the refetch now raises before any request
and the claim degrades to unsupported.
---------
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
* feat(pipeline): add --discover mode for topic-less trending discovery
* fix: address self-review findings
* fix: address round-2 residual (surgical round)
* fix: boundary-aware category match, additive-only discovery totals, preserve discovery intent through onboarding
* fix: visible default-search fallback, non-plural domain anchors, clean X backend fallback
* fix(discover): apply domain filter to Digg listing clusters
The Digg source is an AI-focused leaderboard feed, so a discovery sweep
on a non-AI domain surfaced off-domain stories (observed live: a crypto
sweep returning model-release coverage). Filter parsed Digg clusters
with the same title-level _matches_discovery_domain check the adjacent
Hacker News branch already applies; an all-filtered result stays a
clean no-results outcome.
* fix(discover): surface the producing X backend's own error as partial
Failing over past a dead backend is a clean outcome, but when the
backend that actually produced items also returned an error, the sweep
is degraded; returning a clean state hid that from source_status and
strict-exit runs. Keep the producing backend's error; earlier fallback
errors remain stderr observability only.
* fix(discover): hold the configured source boundary instead of widening
When the configured source filter (LAST30DAYS_DEFAULT_SEARCH or
--search) contains no discovery-capable source, the sweep silently
widened to all discovery feeds with only a warning, querying sources
the user had filtered out. Discovery now exits 2 with the unsupported
sources and the discovery-capable set named, for both the config and
explicit-flag paths.
---------
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
* feat(schema): versioned agent JSON export profile with --json-profile raw fallback
* fix: address self-review findings
* fix: exclude author-reach fields from headline engagement (stocktwits followers)
* fix: fail loudly when the evaluator receives the agent profile instead of raw Report
---------
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
* feat(setup): restore the v3.0.0 first-run NUX wizard on the consent-driven foundation
Step 0 now has two branches. Claude Code (and any host with AskUserQuestion)
gets the restored original guided NUX: welcome message, Auto/Manual/Skip setup
modal, cookie-consent modal, ScrapeCreators signup offer, TikTok/Instagram
INCLUDE_SOURCES opt-in, and a first-topic picker. Hosts without modals
(OpenClaw, Codex, Cursor, Gemini CLI) get the equivalent Non-Modal Prose Flow.
Builds on #659 (first-run gate) and #660 (consent-driven prose, key-persist,
Full Disk Access remediation) - all of that is preserved, not reverted. Additive:
the source inventory is current, not the v3.0.0 set.
- Digg threaded into the install messaging everywhere yt-dlp/YouTube appears
(welcome list, Auto-setup option, manual guide). Install already existed; this
is the copy.
- ScrapeCreators credit count restored to "10,000 free calls".
- Hard "ALWAYS execute Step 0 BEFORE Step 1" gate restored to resist re-erosion.
- Manual-setup guide refreshed to the current source matrix (Digg, youtube
comments, SC Reddit/YouTube backups, Perplexity, Bluesky) with append-only
.env safety rules.
- Threads/Pinterest intentionally not surfaced in onboarding (power-user
INCLUDE_SOURCES only).
- tests/test_onboarding_contract.py rewritten for the two-branch structure and
to lock the flow against silent re-erosion.
- Docs synced: CONFIGURATION.md, AGENTS.md, CHANGELOG.md; original wizard
captured at docs/reference/old-nux-wizard-v3.0.0.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q37ombFQdv9uLbKm2y8vBD
* fix(setup): resolve Greptile findings on the restored NUX wizard
- Skip+Skip path now writes SETUP_COMPLETE: picking "Skip for now" at the setup
choice wrote no .env flag, so the first-run gate re-fired on every invocation.
The skip branch now persists SETUP_COMPLETE=true and goes to the topic picker.
- Modal step labels (Step 1/2/3) now match the sequence descriptor; the body
jumped from Step 1 to Step 4.
- Non-Modal Prose Flow now honors an existing BROWSER_CONSENT=true (skip re-asking
when consent was granted in a prior session).
- Contract test: symmetric Full Disk Access assertion on the prose branch + a
guard that the Skip path writes SETUP_COMPLETE.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q37ombFQdv9uLbKm2y8vBD
* fix(setup): restore masked-key retrieval hint on persisted:false path
When setup --github succeeds but the key write fails, both flows told the user to
add SCRAPECREATORS_API_KEY=<key> manually but dropped the hint on how to obtain
the value (the raw key is masked in output). Restored the parenthetical:
re-run setup --github or retrieve it from scrapecreators.com. (Greptile)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q37ombFQdv9uLbKm2y8vBD
---------
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The last-chance X keyword retry collapsed a multi-word subquery to a bare
`max(candidates, key=len)` token — e.g. 'trevin chow ai agents compound'
became 'compound since:...', flooding the X pool with off-topic 'compound
interest/nevus' posts. Keep the entity anchor (first distinctive topic
token) in the retry, optionally plus the strongest distinctive token;
better to return 0 than over-broaden to an unanchored generic term.
Also lands the investigation that grounds the X/funny fix plan, including
the verified correction that Digg's X-enrichment was a red herring — it
renders as Digg-cluster quotes, never enters the X column (so the planned
Digg-filter unit was dropped as a non-occurring path).
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
* feat(setup): auto-install digg-pp-cli during first-run NUX
Install the free, keyless digg-pp-cli via the Printing Press installer in
run_auto_setup, mirroring the yt-dlp/brew auto-install. The Digg source
already activates whenever the binary is on PATH (available_sources), so
this closes the only NUX gap. Degrades to a recommend-only outcome when
npx is unavailable; verifies on PATH and the Go bin dir; never raises.
Reports the outcome in the setup-complete summary.
* docs(nux): describe Digg auto-install in Step 0 + CONFIGURATION
Step 0 wizard narrative now mentions the best-effort digg-pp-cli install;
CONFIGURATION.md documents Digg as a free, keyless, read-only optional
source with its install command. Source display itself needs no change —
ACTIVE_SOURCES_LIST is built from the engine's --diagnose output, which
already maps digg->Digg.
* fix(setup): align Digg NUX with printing-press-library PATH contract
Use pinned @mvanhorn/printing-press-library@0.1.16, probe ~/.local/bin,
distinguish installed_off_path from engine-active installs, and extend
OpenClaw setup. Document the Hermes/OpenClaw agent PATH gap in AGENTS.md
and docs/solutions/.
* fix(setup): off-PATH Digg message names the actual install dir
The installed_off_path status hardcoded '$HOME/.local/bin' in its PATH
instruction even though the binary is probed across ~/.local/bin,
$GOPATH/bin, and ~/go/bin. A user whose digg-pp-cli landed in ~/go/bin
was told to add the wrong dir and couldn't activate Digg. Derive the dir
to add from where the binary was actually found. Resolves Greptile #590.
---------
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Trevin Chow <trevin@trevinchow.com>
CHANGELOG covers the narrative lens (Added) and head-token grounding
fix (Fixed). CONCEPTS.md gains a Research pipeline cluster (Primary
entity, Intent modifier, Entity grounding, Keyless path,
Comment-enrichment slots). New docs/solutions/logic-errors/ entry
captures the false-demotion bug, the rejected alternatives, and the
shared-helper prevention rule.
* chore: remove dev artifacts from installer scan surface
Hermes (and other harnesses that clone raw GitHub instead of honoring
.clawhubignore) scan files that never reach an installed skill, producing
a wall of false-positive security findings. Remove the stale SKILL-original.md
backup, internal docs/plans and docs/test-results, and release-notes.md so
the scanned tree matches what actually ships.
These were already excluded from the ClawHub bundle via .clawhubignore and
from git archives via .gitattributes export-ignore. No runtime files change.
Refs #464
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs: drop dangling SKILL-original.md reference in AGENTS.md
Greptile-flagged: the deletion left a 'kept for reference only' pointer to
the removed file. Refs #465
---------
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Closes#374 (adapted, not 1:1 merged).
@hnshah opened PR #374 proposing a docs/adr/ directory for architecture
decision records. The intent is right -- the "why is search-quality eval
manual?" reasoning drifts out of memory if it isn't written down -- but
the docs/adr/ convention doesn't fit alongside the existing
docs/solutions/ structure (compound-engineering ce-compound pattern with
frontmatter metadata, additive entries, no membership-contract test).
This commit adopts hnshah's ADR 002 content (search-quality eval is
manual by default) as a docs/solutions/architecture/ entry with the
canonical compound-style frontmatter (module, problem_type, applies_when,
related_components, tags). Drops the docs/adr/ directory pattern, the
README index, and the test_adr_docs.py contract test.
ADR 001 (multi-surface packaging) is intentionally not adopted here: it
referenced sync.sh as the deploy mechanism, but sync.sh was removed in
PR #405 in favor of `npx skills add . -g -y`. The multi-surface
packaging story is still real but has moved beyond what the original
ADR captured; a fresh "how we ship to multiple harnesses" entry would
make sense as a separate doc.
Co-authored-by: hnshah <hnshah@users.noreply.github.com>
Documents the cascade pattern surfaced during this session's install-modernization
arc: a `test_sync_cache_path_uses_skill_version` test asserted that a hardcoded
version pin in `sync.sh` matched the version frontmatter in SKILL.md. When a
release bumped SKILL.md, every open PR's CI failed simultaneously on the
unrelated stale-pin assertion. Affected at least 5 PRs across the 2026-05-13
to 2026-05-15 window (#400, #390, #392, and two others) plus required hotfix
PR #397 to unblock the queue.
The permanent fix shipped in PR #405 (deleted sync.sh + the test). This doc
captures the design lesson so the pattern doesn't reappear: don't write
consistency tests that read two files and assert one matches a value derived
from the other. Either derive at runtime from a single source of truth, or
self-skip / merge-base-scope the test so deletion is a non-event.
Created via /ce-compound. Includes:
- docs/solutions/workflow-issues/release-consistency-test-cascade-2026-05-16.md
(the new learning — first entry under docs/solutions/)
- CONCEPTS.md (new — 4 entries: Skill, Engine, Harness, Beta channel,
capturing project-specific vocabulary that surfaced across the session)
- AGENTS.md (added one-line Structure entries surfacing docs/solutions/ and
CONCEPTS.md so fresh agents discover them)
- docs/plans/2026-04-22-{002,003,005,006}-*-plan.md (added deprecation banner
to each, pointing readers at PR #405 and the new docs/solutions entry —
these 4 historical plans still reference the deleted sync.sh inline)
Also: closed PR #379 (j-sperling's workaround for the same cascade,
superseded by PR #405's permanent fix).
* feat: vs mode runs N full passes; --competitors wraps vs with auto-discovery
Unifies vs-mode and --competitors onto one fanout architecture. A topic
containing "vs" / "versus" now runs N full pipeline.run() calls in parallel
(reverting the one-pass latency optimization that removed per-entity
depth); --competitors becomes a SKILL.md-level shortcut where the hosting
reasoning model (Claude Code, Codex, Hermes, Gemini) discovers N peers via
its own WebSearch, runs Step 0.55 per entity, and invokes the engine with
a vs-topic + --competitors-plan JSON.
Changed:
- vs-mode: N full passes in parallel via fanout (was 1 merged pass).
- --competitors: SKILL.md shortcut for vs-mode-with-discovery. Engine flag
kept for headless/cron use. LAW 7-style stderr reframed to lead with the
hosting-model path (use WebSearch + --competitors-plan) instead of
BRAVE_API_KEY. Footer BRAVE/SERPER nudge suppressed when --plan or
--competitors-plan present (hosting model already has WebSearch).
Added:
- --competitors-plan JSON flag: per-entity {x_handle, x_related, subreddits,
github_user, github_repos, context}. Accepts inline JSON or file path.
subrun_kwargs_for helper is the single source of truth for per-entity
kwargs — no closure-default fallthrough from main scope.
- Per-entity save files: each entity's sub-run produces its own
{slug}-raw.md with a single-row Resolved Entities block.
- --polymarket-keywords filter for ambiguous single-token topics.
Fixed:
- test_competitor_subrun_isolation regression suite locks in 3.0.12's
no-leak invariant (main flags do not inherit into peer sub-runs).
- Updates test_regression.py for the new comparison-mode payload shape.
Bumps plugin.json to 3.0.13. 1,219 tests passing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: comparison title attribution — (Last 30 Days) → (/Last30Days)
User feedback on 3.0.13 dogfood runs (Kanye vs Drake, Mercer Island,
Figma): the comparison-mode synthesis title should attribute to the
slash command rather than restate the date range.
Three SKILL.md occurrences updated. Pure documentation change. Bumps to
3.0.14.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Four fixes based on 2026-04-22 test-window feedback on v3.0.11 --competitors:
- Each competitor sub-run now runs Step 0.55 (X handle / subreddits /
GitHub) via resolve.auto_resolve inside the fanout closure. Deep-copied
config per entity prevents _auto_resolve_context leak across sub-runs.
Resolved data stored on report.artifacts["resolved"] for the renderer.
- New internal_subrun keyword on planner.plan_query and pipeline.run
suppresses the LAW 7 "No --plan passed" stderr for engine-internal
fan-out only. Default path unchanged.
- Default --competitors count is now 2 (3-way total). --competitors=N
still customizes; range 1..6.
- SKILL.md STEP 0 canonical-path self-check forces readers who loaded
from marketplaces/ (auto-restored to origin/main, stale) to re-read
from plugins/cache/last30days-skill/last30days/{VERSION}/SKILL.md.
Two of three 2026-04-22 test windows hit this stale-path trap.
- New ## Resolved Entities block in render_comparison_multi shows
per-entity handles/subs/github for debug visibility.
Bumps plugin.json to 3.0.12. 12 new tests; 1,175 total passing.
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pass `--competitors` on a single-entity topic and the engine auto-discovers
2-6 peer entities via web search, runs the full pipeline on each in
parallel, and returns one N-way comparison reusing the existing 9-axis
Head-to-Head scaffold. `last30days OpenAI --competitors` resolves to
Anthropic + xAI + Google Gemini; `last30days Kanye West --competitors`
resolves to Drake + Kendrick Lamar + one more peer.
- New CLI flags: --competitors, --competitors=N, --competitors-list
- New scripts/lib/competitors.py — mirrors resolve.auto_resolve pattern
(web search + deterministic text extraction, no internal LLM)
- New scripts/lib/fanout.py — ThreadPoolExecutor orchestrator; per-entity
failures degrade gracefully as long as >=2 entities survive
- Multi-report render in scripts/lib/render.py reuses the comparison
scaffold for the synthesis table
- LAW 7-style stderr when no backend and no list, pointing the hosting
reasoning model at --competitors-list
- 38 new tests across CLI parsing, discovery, fanout, and rendering
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds docs/releases/v3.0.9.md as the GitHub Release body and appends
the matching CHANGELOG.md entry.
Covers what shipped in v3.0.9 (Class 1 refuse-gate, LAW 1 WebSearch
precedence, END-boundary, stale SKILL.md deletion) plus the community
contributions that landed across 3.0.1-3.0.8 that had never been
announced (TikTok + YouTube top comments, Hermes support, multi-key
rotation, cross-platform fixes, HTTP layer consolidation, eval
fixtures).
Contributors credited: @j-sperling, @stephenmcconnachie, @zaydiscold,
@iliaal, @Chelebii, @Gujiassh, @hnshah, @george231224, @shalomma,
@BryanTegomoh, @uppinote20, @zerone0x, @thinkun, @thomasmktong,
@fanispoulinakisai-boop, @pejmanjohn, @zl190, @Jah-yee, @dannyshmueli,
@Cody-Coyote.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Internal ce:plan output shouldn't ship on the public repo.
Adds docs/plans/ to .gitignore and removes the two already-tracked
plan files from the index. Working copies stay local for reference.
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Both plans describe work that was already shipped:
- feat-add-websearch-source.md - websearch is in the v3 pipeline (scripts/lib/perplexity.py etc)
- fix-strict-date-filtering.md - date filtering is enforced in scripts/lib/dates.py
New planning goes in docs/plans/ following the ce:plan convention.
Isolate eval subprocesses from local yt-dlp config and fix nDCG normalization against the judged pool.
Validation: uv run python -m unittest tests.test_evaluate_search_quality
This workspace uses GOOGLE_API_KEY as the canonical Google credential. Accept it ahead of the Gemini-specific aliases so the local evaluation harness can run without a separate GEMINI_API_KEY export.
Validation: uv run python -m unittest tests.test_env_project tests.test_evaluate_search_quality and a one-shot keychain-backed resolution check.
Add an optional local evaluator that compares a baseline revision against a candidate checkout, computes deterministic stability metrics, and can call Gemini for judged ranking metrics when configured.
The harness isolates child runs with a temporary HOME and a node-free PATH so historical revisions cannot trigger Bird browser-cookie auth during evaluation.
Validation: uv run python -m unittest and local smoke/full deterministic eval runs.
Update README, launch copy, and UI guidance to prefer popup-free AUTH_TOKEN/CT0 configuration, and keep X backend selection on the verified Bird or xAI paths.
Validation: uv run python -m unittest tests.test_env_project
- Remove duplicate detect_query_type from query.py (divergent 5-type version);
canonical 7-type version lives in query_type.py
- Fix reddit.py import to use query_type.detect_query_type
- Clean unused STOPWORDS/SYNONYMS/tokenize imports from youtube_yt, instagram,
tiktok, scrapecreators_x, bird_x after relevance consolidation
- Fix _relevance_filter default from 0.7 to 0.0 (items without relevance
should not silently pass the filter)
- Remove --dateafter from yt-dlp (returns 0 results for evergreen topics)
- Remove restrictSearchableAttributes from HN search (misses Ask/Show HN)
- Lower HN points filter from >5 to >2 (avoids filtering niche posts)
- Add error logging to select_openai_model HTTP failures
- Remove mise.toml and internal planning doc from repo
- Update module docstrings to describe current purpose, not migration history
- Update tests to import from canonical relevance module
Mastodon-compatible API at truthsocial.com/api/v2/search.
Opt-in via TRUTHSOCIAL_TOKEN env var (bearer token from browser).
Silent when unconfigured. Full pipeline: search, parse, normalize,
score, dedupe, render across all 10 pipeline files.
27 new tests, 440 total passing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix plugin.json hooks field from invalid array to empty object
- Add Claude Code plugin install above ClawHub badge
- Version bump to v2.9.5 with new features block (Bluesky, comparative
mode, ScrapeCreators X, per-project env, expanded tests)
- Add Bluesky to all source list references
- Document BSKY_HANDLE/BSKY_APP_PASSWORD env vars in install + optional sections
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
searchPosts endpoint now returns 403 for unauthenticated requests.
Add session auth via createSession, gate on BSKY_HANDLE + BSKY_APP_PASSWORD
env vars. When unconfigured, Bluesky is completely invisible (no error).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update .claude-plugin/marketplace.json and plugin.json to v2.9.5 with
full metadata. Add plugin install instructions to README as the
recommended install method. The repo root serves as both the marketplace
and the plugin - skills/last30days/SKILL.md (symlink) is discovered
automatically.
Users can now install with:
/plugin marketplace add mvanhorn/last30days-skill
/plugin install last30days@last30days-skill
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>