Skip to content

feat: add agent-named stories to the right rail - #76

Merged
dimslaev merged 1 commit into
masterfrom
feat/rail-stories
Oct 10, 2026
Merged

dimslaev merged 1 commit into
masterfrom
feat/rail-stories

Conversation

@dimslaev

Copy link
Copy Markdown
Owner

Session 4 of the rails plan. Adds the Stories panel to the right rail. A separate daily routine names and keeps the stories (ADR 16).

What changed

  • Data: new story and story_article tables (additive migration d1e2f3a4b5c6), models in app/catalog/models.py, domain module app/catalog/stories.py. save_story makes sure an article sits in at most one open story.
  • API: GET /api/v1/stories?limit=5 returns open stories with 3+ articles from 2+ publishers, ordered by last_article_at desc. Thin route in catalog/routes.py. Generated client regenerated.
  • MCP (write token only): story_candidates(days=14), save_story(slug, name, blurb, urls), close_story(slug). story_candidates reuses story_groups with a new STORY_DIST = 0.40. Open stories' articles act as anchors, so a cluster that reaches a story names it in near.
  • Skill .claude/skills/stories/SKILL.md. Routine prompt added to deploy/README as a new "Stories" section (CRON_TZ=Europe/Zurich 17 6 * * *).
  • Frontend: components/Rails/panels/Stories.tsx is the only panel in RIGHT. It's an accordion with one row open at a time and the first open by default; the open body renders right under its row. Rows show the name, an orange dot when the story grew today, ScoreBars, and the meta "N · Sep 19 – Oct 7". The open body has the blurb, a timeline of up to 6 articles (date, MiniRail, publisher, title) and "Show all N". Open and close animate height, and the animation is turned off under prefers-reduced-motion. Opening a row fires story_open {slug}; article links use articleClickHandlers.
  • backend/scripts/seed_stories.py seeds stories into a local db, from a JSON file or as demo stories from the newest articles. It refuses to run in production.
  • Docs: glossary "Story" (and how it differs from Coverage), ADR 0016, product.md, deploy/README, CLAUDE.md MCP tools list.

Where I departed from the plan

  • Article cap 30, not 8. The plan says the API returns up to 8 articles, but the panel's "Show all 15" has to expand every article in place. 8 can't do that, so STORY_ARTICLES = 30 is used as a payload cap. article_count is still the true total.
  • Hiding an empty panel. The plan says to hide the panel and the right rail on an empty or failed fetch. Panels were static, and RailPanelDef was to stay untouched, so RailsProvider now drops the stories panel until /stories returns at least one story. The rail then hides through the existing empty-side path. This is a small, stories-specific filter in RailsProvider.tsx, not in panels.tsx.
  • first_at / last_at come from the story's articles (published_at, falling back to created_at).
  • Saving to a closed story is refused. The skill tells the agent to start a new slug instead.
  • STORY_DIST is untuned. The plan says to start at 0.40 and tune on real data. This environment had no db dump and could not download the embedding model, so 0.40 is untuned. ADR 16 says so; the routine's reports will show whether clusters are too broad.
  • Panel header: title "Stories", meta "named by the agent".

Verified, and how

  • Backend tests (pytest) against a local Postgres 16 + pgvector with migrations at head: 564 passed. The 1 failure is test_read_endpoints_are_public[/search?q=agents], which also fails on master here because huggingface.co is blocked in this sandbox (the model download). New tests are in app/catalog/tests/test_stories.py:

    • migration shape
    • list filters (min articles and publishers, closed hidden), ordering, the article cap, grew_today, the route
    • save_story upsert and every validation error
    • all-or-nothing on unknown URLs
    • one open story per article
    • story_candidates clustering and near
    • the tool wrapper
  • The read-token refusal is covered by adding the three tools to WRITE_TOOLS in test_curation_tools.py.

  • Lint and types: ruff check, ruff format --check, mypy backend/app backend/scripts, ty check are all clean. Biome is clean on the files I changed. Biome reports a format issue only in tests/rails.spec.ts, which came from master as-is, so I left it.

  • alembic check shows no drift for the new tables.

  • Build: bun run build passes.

  • Playwright: tests/stories.spec.ts mocks /api/v1/stories and checks five things:

    • the first story is open and its body is the next sibling of its row
    • clicking another row moves the body under that row, above the next row
    • "Show all" expands in place
    • an empty list hides the right rail
    • a 500 response hides the right rail

    It ran with stories.spec.ts, master's rails.spec.ts and articles.spec.ts: 10 passed. These ran with --no-deps because the auth setup needs mailcatcher, and none of these specs need sign-in.

  • Seeded three stories locally with seed_stories.py, checked GET /api/v1/stories/, and screenshotted the rail at 1440px: first row open, then moved to the second row.

  • Rebased on master after feat: show who wrote the wire in the left rail #74 (who-wrote). Resolved conflicts in models.py and panels.tsx by keeping both sides, then regenerated the client.

  • Nothing was called against production. No MCP write tool was used.

After merge

  1. On claude.ai/code, create a routine against this repo with the prompt from the "Stories" section of deploy/README. Use the write token (MCP_WRITE_TOKEN as AGENTIQUE_MCP_TOKEN) and the schedule CRON_TZ=Europe/Zurich 17 6 * * *.
  2. Run it once by hand. Check its report, then check the right rail on agentique.ch: it appears once a story has 3 articles from 2 publishers.
  3. Watch the first few reports to see how STORY_DIST (0.40) clusters, and tune it if clusters are too broad or too thin.

🤖 Generated with Claude Code

https://claude.ai/code/session_018V3zAh2gYWTYRqemGWcvUA


Generated by Claude Code

@dimslaev
dimslaev merged commit 70fb009 into master Oct 10, 2026
3 of 5 checks passed
@dimslaev
dimslaev deleted the feat/rail-stories branch October 10, 2026 10:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants