The "tickets" release. Karma now understands work as well as activity:
attach Linear / Jira / GitHub Issues to Claude Code sessions, then see
the dashboard pivot around tickets the same way it pivots around projects.
Added
- Session ↔ Ticket linking for Linear, Jira, and GitHub Issues.
Karma stays read-only — it stores the link and caches metadata, never
writes back to your ticket provider. - Three ways to link a session to a ticket:
- Dashboard paste — open a session, paste a ticket URL or key into
the Tickets section, done. - Slash command / skill —
/link-ticket-to-session ABC-123in a
Claude Code session. The agent fetches title + status via your
Linear / Atlassian / GitHub MCP server (if installed) and posts the
link to karma. HonorsKARMA_API_URLfor non-default hosts. - Branch-name auto-detect (opt-in hook) — when you start a session
onfeat/LINEAR-123-foo, the link is created automatically. Silent
on every failure; never blocksSessionStart.
- Dashboard paste — open a session, paste a ticket URL or key into
/ticketsindex page with provider, project, and search filters./tickets/{provider}/{key}detail page listing every linked
session for a ticket, with live-session enrichment for unindexed
active sessions.- Tickets tab on every project page showing every ticket touched by
any session in that project — and across every checkout of the same
repo (worktrees, subdir CWDs, submodules) via the newgit_identity
column. So a session linked fromclaude-karma/frontend/shows the
ticket on the mainclaude-karmaproject too. - GitHub key heuristic — tickets like
owner/repo#42surface under
any project whosegit_identityisowner/repo, even if no local
session has linked them yet. Useful when a teammate has linked the
ticket on a different machine. - Cross-encoded ticket aggregation powered by
projects.git_identity
(canonicalowner/repolowercase, derived fromgit remote get-url originat index time). Lets karma treat every checkout of the same
repo as one logical project for ticket views.
Changed
- Frontend route param renamed
[project_slug]→[project_id].
The route accepts either form (slug orencoded_name), so existing
URLs and bookmarks continue to work. The new name is honest about
what it accepts. - All API endpoints with a project filter now accept either form
uniformly via the newsafely_resolve_project()helper. Applied to
/tickets,/skills,/skills/usage,/commands(5 endpoints),
/agentsfilters, and/live-sessions/project/{id}. Endpoints that
previously matchedencoded_nameexactly and returned an empty list
for slugs now resolve cleanly.
Fixed
/projects → card → Tickets tabshowed empty for sessions linked
via the dashboard. Cause: project cards link via slug, the tickets
API matchedencoded_nameexactly. Fix: unified data flow with a
clean "slug at the boundary, encoded_name inside" architecture across
every project-by-identifier endpoint. Locked in by a regression test.- GitHub PRs were stored with
/issues/NURLs instead of/pull/N.
Cause: the ticket parser hard-coded/issues/in the canonical URL
it built, throwing away which path segment the input URL had used.
Fix: parser now captures(?P<kind>issues|pull)and uses the
captured segment in the canonical URL. The frontend grew a small
PRpill (with GitHub's pull-request glyph) next to the GH provider
badge so issues and PRs are visually distinguishable everywhere.
Old rows self-heal on re-link; users wanting immediate repair can hit
the newPOST /admin/repair-github-urlsendpoint (see Upgrading).
Internal
- Schema v12. Adds
projects.git_identity TEXT+ index. Migration
runs automatically on first start (idempotent against any phantom
column from out-of-band branches) and nudges session mtimes so the
periodic indexer backfillsgit_identitywithin ~5 minutes. To
populate immediately:POST /admin/reindex. - New service module
api/services/git_identity.py—
normalize_git_url()(pure parser, handles https / ssh / scp-style
with or without.git) andread_git_identity()(timeout-guarded
git remote get-url originshellout). - New helper
safely_resolve_project()inapi/routers/projects.py
— filter-friendly variant ofresolve_project_identifierthat returns
the raw input verbatim on unknown identifiers (so downstream queries
yield empty lists, not 404s). - New frontend helper
src/lib/utils/project-url.ts—
projectHref()+projectHrefFromSession()centralize the
slug || encoded_namepolicy in one place. Migrated 5 call sites
(GlobalSessionCard,LiveSessionsTerminal,LiveSessionsSection,
CommandPalette, plans page,ConversationOverview).
Tests
- 1580 passing (was 1474). Added: 40 ticket endpoint tests, 23
normalize_git_urlparser tests, 7safely_resolve_project/
resolve_project_identifierunit tests, plus parser, enrichment,
branch-detector hook (299 LOC), and schema-migration regression tests.
Upgrading from 0.1.x
No manual steps required. On first start:
- Schema migrates v11 → v12 automatically. The migration is idempotent
and safe to re-run. projects.git_identityis backfilled by the periodic indexer (default
interval: 5 minutes). To trigger immediately:
curl -X POST http://localhost:8000/admin/reindex.- The route rename
[project_slug]→[project_id]is internal —
existing URLs continue to work.
Optional: repair stale GitHub PR URLs. If you linked GitHub PRs in
0.1.x, their stored URLs point at /issues/N even though they were
PRs. Links still work (GitHub redirects), but the new PR pill won't
show. Rows self-heal on re-link, or you can repair them in one shot:
curl -X POST http://localhost:8000/admin/repair-github-urls
# {"status":"ok","rewritten":N}The repair is conservative — it only rewrites rows whose
status='MERGED' (a state unique to PRs). Open or closed-unmerged PRs
remain ambiguous from cached data alone and self-heal on next re-link.
If you used the syncthing prototype in an earlier branch, the
sync_* tables and any jayantdevkar-claude-code-karma-style project
rows are left over from that prototype (not part of this branch's
schema). Safe to delete manually if you want a clean dashboard:
DROP TABLE IF EXISTS sync_subscriptions;
DROP TABLE IF EXISTS sync_removed_members;
DROP TABLE IF EXISTS sync_events;
DROP TABLE IF EXISTS sync_projects;
DROP TABLE IF EXISTS sync_members;
DROP TABLE IF EXISTS sync_teams;
DELETE FROM projects WHERE encoded_name NOT LIKE '-%';