Changelog

Every release, documented. Follow development on GitHub.

v0.30.0November 9, 2026Latest

Hardening: safe retries, fair limits, several orgs and a rebuilt Tickets page

  • ✓Tickets, rebuilt. Saved views with counts (Mine, Triage, Needs review, Blocked, Overdue, All), grouping, sorting and search; one-line rows that show review state, blockers, Issue progress and the Jira key, with status and assignee menus right in the row. Opening a ticket shows it in a detail pane (beside the list on wide screens) with a Next step card and Overview, Activity, Review, Child tasks and Jira tabs. Keyboard shortcuts (press ? for the list), bulk Assign, Approve, Dismiss and Follow, a six-column board and a quicker New ticket dialog. See Dashboard.
  • ✓Belong to more than one organization. Accept invites to, auto-join, create or activate several organizations. Anything that names an org uses that org; everything else uses your default org, which you pick with the switcher on the Organization page. sfs org list shows them all. See Organizations.
  • ✓A more grounded session helper. Every sentence that states a fact is tied to the result it came from, and a sentence with no source is marked “not verified”. A conversation stays in one scope and one organization, citations must point at what a tool actually returned, lists say whether they are complete, and the helper always ends with an answer. Docs answers say whether the feature is on your plan and on your kind of install. See Session Helper.
  • ✓Safe retries. Send an Idempotency-Key header on any create and a retry returns the first answer instead of creating a duplicate; the CLI and MCP server send one automatically. See REST API.
  • ✓A published API contract. An OpenAPI snapshot with checks for breaking changes, deprecation headers, and an X-SessionFS-Ignored-Fields header that names request fields the server didn’t read. See API Contract and Versioning.
  • ✓Business metrics and SLOs. Prometheus metrics for tickets, review loops, approvals, sweeps and integrations, four service level objectives with ready-made alert rules (optional Helm PrometheusRule) and Grafana panels. See Metrics Reference and Service Level Objectives.
  • ✓Fair limits per organization. Meeting and chat extraction, helper questions, knowledge compiles and CSV exports are capped per organization (over the cap: 429 org_concurrency_limit with Retry-After), and heavy reads stop after 15 seconds. See Tenancy and Isolation.
  • ✓Bulk operations. Invite up to 100 people, grant up to 100 project group members or resolve up to 100 meeting participants in one all-or-nothing request (sfs org invite --file, sfs project group grant --file, --file on resolve-participant).
  • ✓Lineage and review loop guarantees. A distilled_from lineage edge (teacher to student model) and a page on every edge type; a page listing each review loop guarantee with the test that proves it. See Lineage Edge Types and Review Loop Guarantees.
  • ✓Explained as you go. Product terms across the dashboard explain themselves on hover, focus or tap with a link to the docs; every icon button has a tooltip and a disabled button says why. The project header is one line with a section menu, and project details moved to Settings → About.
  • ✓Changed: merge checks posted to GitHub and GitLab are ordered per commit, so an older result can’t overwrite a newer one; session pushes are scanned under the strictest DLP policy of every organization the session could land in; statuses read in sentence case (“In progress”, “In review”).
  • ✓Fixed: POST /api/v1/sessions now runs the same DLP scan as a sync push; self-hosted signup verification links point at your own server; five review-loop races (retried worker starts, a stale worker finishing after a takeover, a double-posted verdict, loop status showing an older commit); admins of a second organization can manage its service keys, residents, trusted reviewers and teams; invites created without an email provider return the link to share.
  • ●Migrations 001–137 (134 idempotency keys, 135 the distilled_from edge, 136 per-org concurrency slots, 137 merge check ordering; all additive). Self-hosted: run migrations 134–137 and schedule the idempotency-keys sweep hourly (see Self-Hosted). 145 MCP tools (unchanged).
v0.29.14November 8, 2026

Evaluations and lineage

  • ✓Evaluation suites and runs, record-only. Versioned suites with acceptance rules; your own grader posts results and SessionFS computes the verdict. A run is trusted only when a registered grader, independent of whoever produced the work, posted it. SessionFS never runs a model. See Evaluations.
  • ✓Datasets, models and prompts as pointers, with versions, digests and a lineage graph; nothing is fetched or stored.
  • ✓Promotion gated on evals. Staging or production needs a trusted pass of every required suite on that exact version. Compare runs for regressions; regulated cases can be stored as hashes only.
  • ●Migrations 001–133 (131–133 artifacts, suites and runs; additive). Self-hosted: run migrations 131–133. 145 MCP tools (+10). Team+ for writes.
v0.29.13November 8, 2026

External contacts

  • ✓Contacts for the people you work with who aren’t SessionFS users — stakeholders, clients, contractors — in Organization → Contacts, with other names, role, tags and an email only owners, admins and the person who added it can read. See External Contacts.
  • ✓Meeting and chat speakers resolve to contacts. A member first, then exactly one contact by email, name or other name; anything else waits in a To confirm queue for a person. Extraction never creates contacts.
  • ✓Tickets and action items can be owned by a contact, shown as external and never notified. When the contact joins with a verified email, they become the member and their open tickets follow.
  • ✓Merge, export and erase for owners and admins, plus an optional retention sweep. MCP list_external_contacts, get_external_contact, create_external_contact, update_external_contact; CLI sfs contact.
  • ●Migrations 001–130 (130 external contacts; additive). Self-hosted: run migration 130. 135 MCP tools (+4).
v0.29.12November 8, 2026

Who recorded it, and reads that never write

  • ✓Release gates and deployments record who wrote them: a user, a service key or the delivery webhook, taken from the request’s credential only. Public loop share views never show actor ids. See Review Loop.
  • ✓Merge readiness is a pure read. Approval requests are raised only when something changes — a verdict, a decision, a policy write, the merge-check trigger or a pull request or merge request webhook — never by reading readiness. See Approvals.
  • ✓Changed: GitLab commit statuses are sent after the event is saved, with a bounded retry inside the webhook deadline, and GitHub check runs retry too; confirming link_meeting_action_item needs the ticket the preview showed; the Review loops inbox follows the same plan rule as the loop routes.
  • ✓Fixed: unlinking a meeting or chat item locks and re-checks like linking; dismissing a suggestion checks the service key’s ticket scope; the Cursor converter deletes its private database copies.
  • ●Migrations 001–129 (129 actor columns on release gates and deployments; additive). Self-hosted: run migration 129. 131 MCP tools.
v0.29.11November 7, 2026

Action items that find your ticket

  • ✓Action items suggest the ticket you already have. When a meeting or chat thread is extracted, SessionFS shortlists up to 30 open or recently resolved tickets in the same project (or the group’s readable projects) and marks an item that looks like one: “Looks like tk_… · title”. Nothing is linked automatically: press Link, Not this one, or Create new to promote as before. See Meetings.
  • ✓Link any action item to a ticket. Link to ticket… opens a picker with a preview of the comment the link adds; a linked item shows its ticket with Unlink, and the ticket gets a comment and a timeline event. A ticket you can’t read is never named. MCP link_meeting_action_item (previews unless confirm=true), CLI sfs meeting link|unlink and sfs chat link|unlink.
  • ✓Rename helper conversations from the history list, the API (PATCH /api/v1/helper/conversations/{id}) or sfs helper conversations rename, and a Large size for the helper panel (the previous size is now Small). See Session Helper.
  • ✓For You stays readable. Each section shows its first 10 items with Show more / Show less for the rest. See For You.
  • ✓Changed: a project you can’t read now returns the same 404 as one that doesn’t exist (editor-only refusals stay 403); confirming a helper action re-checks the plan and scope, so a proposal made before a downgrade is refused with upgrade_required and stays pending; chart images are pinned by digest, and an opt-in Helm CronJob (api.attentionHides) runs the For You hides sweep.
  • ✓Review loop fixes. Retry-safe start-worker that keeps a taken-over lease holder; only the starting credential or a project admin can finish a worker run; one recorded run per review round; implementers counted from every lease, and a rebound resident’s old key stays in the implementer identity; bounded workflow approval gates with step order enforced on untagged rounds; a bounded needs-you feed with a cached badge; delivery idempotency keys bound to the signed body and pruned; loop share links stop working when their creator loses access, with org DLP applied.
  • ✓Security hardening. Cross-org run and spend reads, reviewer ids and validation inputs are no longer exposed; platform-key denials no longer name another user; person assignment needs read standing; key echoes are scrubbed from audit and narrative errors; Confluence conversion is linear; project merges can’t widen a group’s reach; the Jira account id is read from the connection.
  • ✓Fixed: tidier reminder notifications, and reminders now show on group meetings.
  • ●Migrations 001–128 (125 prior implementer keys on work items, 126 one run per review round, 127 delivery indexes, 128 suggested and linked tickets on action items; all additive). Self-hosted: run migrations 125–128. 131 MCP tools (+1).
v0.29.10November 7, 2026

Agents, tickets and runs on every plan

  • ✓Agents, tickets and agent runs on every plan. Personas, tickets, agent runs and agent feedback are now on Free and every paid plan, with no cap, so a solo developer can run the whole ticket, run and review lifecycle on a personal project. Merge readiness (sfs loop ready) and review-state reads are on every plan too. See Pricing.
  • ✓Review loops stay on Team and Enterprise. Review loops, loop worker runs and review rounds, workflow templates, releases, PRs, gates and deployments, the merge check, loop share links, webhook ingest, the Review loops inbox and the For You findings and proof sections need Team+. Projects grandfathered on tickets before v0.17.2 do not carry that over to review loops.
  • ✓Editors can reassign. Any project editor can assign, reassign or unassign a ticket from the API, MCP, CLI or dashboard, where the reassign panel gains a one-click Unassign. Other fields still need the ticket’s creator or a project admin.
  • ✓The session helper publishes tickets to Jira. Ask it to turn tickets into Jira issues and it proposes one publish per unlinked ticket; on Confirm each is published exactly like Promote to Jira on the ticket page. Linked tickets are refused with an offer to overwrite instead, and after a Jira timeout the helper never claims nothing changed.
  • ✓Meetings and chats as helper sources (GitHub #124). Tickets and comments the helper drafts can cite [meeting:<id> L<a>-<b>] and [chat:<id> M<a>-<b>] ranges, checked like answer citations.
  • ✓Agents tab shows your project’s agents (GitHub #126). An Include N org people switch adds the org’s agents in a separate read-only section.
  • ✓Fixed: meeting extraction on PostgreSQL failed when an action item’s owner matched a participant (GitHub #124); participants are now written first.
  • ●No new migrations (still 001–124). 130 MCP tools (unchanged).
v0.29.9November 7, 2026

Workflow templates

  • ✓Repeatable multi-agent plans (Team+). Store a workflow such as dispatch → implement ×3 → review → verify → document. Each step names a role, an agent, its dependencies, a fan-out of up to 10 tickets, an exit gate and a brief. See Workflow Templates.
  • ✓Record-only. SessionFS stores the template, creates the review loop and computes the plan; your own orchestrator starts every agent and reports back. SessionFS never calls a model or runs a job for a workflow.
  • ✓Project and org templates, versioned. Project editors write project templates, org admins write org templates for every project in the org. An update saves a new version, and a running loop keeps the version it started with.
  • ✓A plan you can follow. Start a workflow on an issue or a new title, then ask for the plan: every step’s lane (blocked, ready, running, needs a person, done), why it is waiting, and the ready tickets with their agent and brief.
  • ✓Gates that mean something. Every reviewer step needs its own independent trusted VERIFIED-CLEAN at each ticket’s latest commit, and an implementer can never pass their own review. approval_policy:<id> gates wait for an approved request at the current commit. Templates are DLP-scanned before storage and ambiguous YAML is refused.
  • ✓Dashboard. Template editor with a step graph under Project → Config → Workflows, a Start workflow dialog, step lanes on the Loop Board, and org templates under Organization → Governance.
  • ✓CLI sfs workflow list|show|create|update|disable|start|plan and --step on loop commands; MCP list_workflow_templates, get_workflow_template, start_workflow and get_workflow_plan; step_id on worker runs and review rounds.
  • ●Migrations 001–124 (124 adds workflow templates, step children and the agent run step id; additive). Self-hosted: run migration 124. 130 MCP tools (+4).
v0.29.8November 7, 2026

For You

  • ✓For You is the dashboard home. One ranked page of what needs you: reminders, approvals, assigned tickets, Review Loop items, handoffs, questions on your tickets, meeting and chat action items and what changed this week. Sessions moved to /sessions (old links redirect), and you can keep Sessions as your home. See For You.
  • ✓Sections that read like a briefing. A Right now ticker, then On your radar, Waiting on you, Your team is asking, From your meetings and chats, Coming up, Worth a second look, What changed this week and Working with others. Ranking is deterministic and explainable; success is “You’re all caught up”.
  • ✓Context on every card. Each card says why it is there (“Assigned to you · due today · priority high”), More context explains its rank, and assigned tickets open Before you start with related knowledge and past sessions.
  • ✓Act without leaving the page. Approve or Reject approvals with a reason, Raise limit on a paused loop, Claim a handoff, acknowledge a meeting or chat action item into a project or group ticket, and Done, Hide or Snooze the rest, each with a real Undo. Keyboard shortcuts (j/k, e, s, h, u, ?) and reduced motion are supported.
  • ✓Working with others. See who your work is waiting on, the reviewer or the owner of a blocking ticket, and send them a reminder with your own message. One open reminder per person and ticket, at most one an hour. Reminders you close can be reopened within an hour.
  • ✓Access checked on every item. Each section uses its source’s own read rules on every request, so losing access removes the card on the next poll. Service keys see their minter’s feed within the key’s org and project allowlist, section by section with the matching scope. A slow source shows “couldn’t load” instead of holding up the page.
  • ✓CLI sfs attention; MCP get_my_attention; the session helper answers “what needs my attention?”; REST GET /api/v1/me/for-you (ETag) with count, hide, context and remind routes; API scope attention:read.
  • ●Migrations 001–123 (123 adds the For You hides table; additive). Self-hosted: run migration 123. 126 MCP tools (+1).
v0.29.7November 7, 2026

Chat Threads

  • ✓Chat threads. Upload a Slack export, a DiscordChatExporter export or a pasted thread from any tool, including Teams, to a project or group, next to its meetings. Every thread carries a channel name such as #infra. See Chat threads.
  • ✓One message per line. Each message is stored as a numbered line (M1, M2, …) with its author and time, so items, citations and ticket quotes point at exact message ranges.
  • ✓Works like meetings. Extract action items and decisions with your own AI key, promote items to tickets through the same preview and confirm, and add reminders to items.
  • ✓Direct messages are refused. Slack and Discord DM exports are rejected; a paste, a Slack export without a channel object and a Discord export without server channel details need an attestation that they came from a channel. Text is DLP-redacted before storage.
  • ✓The session helper cites chat. Ask “what did we decide about the LB cert in #infra?” and the answer cites [chat:<id> M12-15], which opens the highlighted messages. See Session Helper.
  • ✓CLI sfs chat upload|list|show|extract|promote|… and sfs remind add --chat; MCP upload_chat_thread plus a kind filter on the meeting tools. Dashboard Conversations tab with a Meetings / Chat / All filter, a chat view and an upload dialog.
  • ✓Fixed: a crafted transcript or chat thread full of unclosed tags could stall extraction for seconds while its text was cleaned; cleaning is now linear.
  • ●Migrations 001–122 (122 adds the conversation kind, provider and channel name to meetings and a per-message author table; additive). Self-hosted: run migration 122. 125 MCP tools (+1).
v0.29.6November 7, 2026

Approvals · Meeting Extraction Fixes

  • ✓Approval policies (Enterprise). Write rules like “auth changes need one security approver before merge” or “decisions under docs/compliance/ need an admin before they become project truth”. A blocking policy holds the work until enough of the right people approve; an advisory one only warns. Editing a policy creates a new version. See Approvals.
  • ✓Merges wait for approval. Merge readiness, the GitHub check, the GitLab commit status and loop status show “Needs approval” until a matching request is approved. An approval binds to the exact commit; a new commit needs a new approval.
  • ✓Knowledge-base promotion waits for approval. Every promote path checks the policies. A matching entry stays a note marked pending approval and compile leaves it out; once approved it is promoted only if its content is unchanged.
  • ✓Nobody approves their own work. Implementers can’t approve their merge and authors can’t approve their entry, including through a service key they minted or rotated; if that can’t be determined, nobody can approve. Only people decide; agents can only read.
  • ✓Notifications and an approval log. Approvers see requests in the bell, Inbox and digest; requesters hear the decision. Org owners and admins can browse every decision under Org → Governance and export it as CSV.
  • ✓CLI sfs approval policy list|create|update|disable, sfs approval list|show|approve|reject; read-only MCP list_my_approvals, get_approval_request, list_approval_policies. Dashboard Approvals page with a waiting count.
  • ✓Save a pasted meeting from the session helper. Paste a transcript and ask the helper to save it; it proposes the meeting and extracts its action items once you confirm. See Session Helper.
  • ✓Owner unknown. A meeting action item whose owner matches nobody is now kept as “Owner unknown” instead of dropped; you pick the assignee when you turn it into a ticket.
  • ✓Fixed: pasting a transcript into Add meeting left the title empty so Save refused it; a pasted transcript is now titled from its first line or the date. Pasted WebVTT and SRT captions are cleaned like uploaded files.
  • ✓Fixed: extraction on long transcripts could return nothing with reasoning models. It now turns thinking off, allows up to 240 seconds, knows the meeting date so “by Friday” lands on the right day, and accepts a sentence before the JSON.
  • ●Migrations 001–121 (120 adds approval policies, requests and decisions and the approval columns on knowledge entries; 121 keeps the owner text on meeting items; both additive). Self-hosted: run migrations 120–121. 124 MCP tools (+3).
v0.29.5November 7, 2026

Reminders

  • ✓Reminders. A title, a due time with its timezone, the person it is for, an optional link to a meeting, ticket, session or knowledge entry, and a thread of notes. Due reminders appear in the bell and Inbox and in the email digest if your settings allow. Snooze, reschedule, add notes, and close as done or dismissed; closed reminders stay as history on their source.
  • ✓From meetings. Action items and decisions with a due date offer “Add reminder”, pre-filled from the meeting. Promoting an item to a ticket carries its reminders over.
  • ✓From the session helper. “Remind me to book a follow-up with Tolu on Thursday” proposes a reminder with the exact local time; nothing is created until you confirm.
  • ✓Private by default. Only the person it is for and the person who set it can see a reminder. You can set one for a teammate when you can both read the source.
  • ✓CLI sfs remind add|list|show|note|snooze|done|dismiss; MCP list_my_reminders, create_reminder, update_reminder. See Meetings and MCP.
  • ✓Fixed: a crafted meeting transcript could stall the API while its text was cleaned; cleaning is now linear.
  • ✓Fixed: merge readiness, merge checks and the review loop no longer report ready on a review thread too long to evaluate completely, and the review views say when they show only part of it.
  • ✓Fixed: knowledge extraction on sync with Auto Narrative and your saved LLM key failed on every sync. It now uses your saved key for its own provider and endpoint only.
  • ●Migrations 001–119 (119 adds reminders and lets notifications carry reminders as well as tickets; additive). Self-hosted: run migration 119 and enable the api.remindersDue Helm CronJob. 121 MCP tools (+3).
v0.29.4October 31, 2026

Rotated-Key Self-Review · Release Ticket Links · Team Plan for Release Tools

  • ✓Link tickets to a release without a pull request. sfs release record <version> --ticket (repeatable) and sfs release link. The Ship Report and each ticket’s story count tickets linked directly as well as through pull requests. Only tickets in the release’s own project can be linked.
  • ✓A rotated service key counts as everyone who minted or rotated it. When an admin rotates a member’s key, the new key counts as both of them, so neither can review work done with it as if they were independent. A key whose rotation history can’t be followed is never treated as independent. Merge readiness, work queues and review rounds all use the same rule, including when a person reviews work done with a key they minted.
  • ✓Release recording, merge checks, share links and the Review loops inbox need the Team plan, like starting loops. Projects on other plans keep reading their releases, Ship Reports and stories, can still list and revoke share links, and their webhooks are acknowledged without recording anything.
  • ✓Fixed: sfs release deploy printed “None” instead of the version.
  • ●Migrations 001–118 (117 adds direct release–ticket links; 118 records which key a rotated service key replaced; both additive). Self-hosted: run migrations 117–118. 118 MCP tools.
v0.29.3October 31, 2026

Worker Runs · Review Rounds · Merge Check · Ship Reports

  • ✓Worker runs and review rounds in one call each. start_worker_run takes the ticket’s lease and opens a run; finish_worker_run records the commit, how the worker exited and the tokens it used; record_review_round records the verdict for an exact commit, its findings with harm, and the reviewer’s tokens. CLI: sfs loop run start|finish, sfs loop review. Repeating a finish or a round is safe and never counts usage twice. Hitting a token budget or round cap parks the ticket for a person.
  • ✓A merge check on GitHub. With the GitHub App installed, pull requests get a “SessionFS review loop” check that is green only when every ticket linked to the pull request is ready to merge at exactly that commit; branch protection can require it. Installs without the Checks permission get a pull request comment instead. GitLab merge requests get the same answer as a commit status. See Git Integration.
  • ✓Pull requests, releases and deploys. Pull requests link to tickets from ticket ids in their title, body or branch, and from loop branches. Record releases, their gates and deployments by hand (sfs release record|deploy|list) or from the GitHub App and per-org GitHub and GitLab webhooks (sfs org delivery-webhook rotate|status).
  • ✓Ship Report per release or per month: loops, review rounds, findings, harmful findings and whether all were fixed before merge, cost per accepted change, checklist classes learned, gates, deploys and pull requests, plus a markdown snippet for release notes (sfs release ship-report).
  • ✓Model scoreboard in loop insights: harmful findings per accepted change, rounds to clean and cost per accepted change for each worker and reviewer model pairing.
  • ✓Share links for a ticket’s story or a Ship Report that show no dollars, emails or user ids, expire after 7, 30 or 90 days and can be revoked (sfs loop share story|ship-report|list|revoke).
  • ✓Dashboard. A Loop Board with one lane per ticket and a ring per review round sized by harmful findings, a budget forecast, Ship Reports with image and markdown export, the model scoreboard, where each ticket shipped, a Needs you panel that includes proposed checklist items, a Review loops page under Inbox for every project you can see, and the public share page.
  • ✓The worker-review-loop skill 2.0 in the launch catalog, built on these calls. See Worker Review Loop.
  • ✓A review round never closes a ticket for review when the reviewer is one of its implementers or the implementers can’t be determined.
  • ✓Pull request ingest from the GitHub App only writes to project repositories verified through the App.
  • ●Migrations 001–116 (113 adds the review round on agent runs; 114 adds pull requests, releases, gates and deployments; 115 adds share links; 116 adds per-org delivery webhooks; all additive). Self-hosted: run migrations 113–116 (see Self-Hosted). 118 MCP tools (+3).
v0.29.2October 30, 2026

Review Loop API · Budgets · Checklist · Loop Reports

  • ✓Start a review loop in one call. Adopt an existing ticket or create a new one, file a child task per brief and create the reviewer’s queue. CLI: sfs loop start, sfs loop status; MCP: start_review_loop, get_loop_status; API: POST /api/v1/projects/{id}/loops.
  • ✓Token and cost budgets, round caps. A loop can carry a token budget, a dollar budget (org owners and admins only) and a review-round cap; there is no default cap. A limit that is reached hands the item to a person instead of spending more. Raise or clear limits with sfs loop update or update_review_loop. Only people can close an item handed back to them (sfs loop close-item); service keys cannot.
  • ✓Review checklist. A per-project list of review traps that every brief carries: seed it with starters, propose items by hand, accept or retire them. New kinds of finding are proposed automatically. CLI: sfs loop checklist; MCP: get_loop_checklist, propose_checklist_item.
  • ✓Loop reports and insights. Per review round: findings by harm, known versus new classes, design-stage findings and round cost; across a date range: findings caught and cost per accepted change. CLI: sfs loop report, sfs loop insights; MCP: get_loop_report.
  • ✓Review Loop in the dashboard. Loop runs, a story of each ticket from brief to merge, a Checklist panel, and Raise limit and Close item dialogs. Dollar amounts show only to org owners and admins, or the owner of a personal project.
  • ✓New API scopes. loops:read and loops:write for service keys and API keys.
  • ✓Self-review counts a service key as the person who made it. Two keys minted by the same person are one identity, so a worker and a reviewer cannot both be that person’s keys.
  • ●Migrations 001–112 (110 stores loop settings on the parent issue; 111 adds the needs-human item status; 112 adds the checklist; all additive). Self-hosted: run migrations 110–112 (see Self-Hosted). 115 MCP tools (+6).
v0.29.1October 30, 2026

Agent Spend Tracking · Merge Readiness · Commit-Bound Review Verdicts

  • ✓Agent run usage and cost. A run can record its model, provider and role, its input, output and cache tokens, its cost and where that cost came from (reported, estimated, plan or unknown), the head commit and how it exited. Long runs report usage as they go and the figures add up; once a run is complete its totals are fixed. CLI: sfs agent run --model --role, sfs agent usage, sfs agent complete --input-tokens --output-tokens --cost-usd --cost-basis; MCP: report_agent_run_usage; API: POST /api/v1/projects/{id}/agent-runs/{run_id}/usage.
  • ✓Spend rollups. See what a ticket cost across workers, reviewers and review rounds, or a project’s spend by persona, model, ticket, role or day over any date range (the last 30 days by default). Token totals are shown to everyone who can read the project; dollar amounts only to org owners and admins, or the owner of a personal project, and never to service keys. Only reported and estimated costs add dollars; runs on a subscription plan add tokens only. CLI: sfs agent spend; MCP: get_spend_summary; API: GET /api/v1/projects/{id}/spend and /tickets/{tid}/spend.
  • ✓Merge readiness. check_merge_readiness and sfs loop ready <ticket> --sha <head> answer whether a ticket’s work is safe to merge at a given commit: only when a trusted strict VERIFIED-CLEAN verdict is bound to exactly that commit, the reviewer is a different identity from every implementer, and no open finding is harmful. A person still does the merge. See Worker Review Loop.
  • ✓Verdicts bound to the reviewed commit. A reviewer can pass reviewed_commit (a full 40- or 64-character commit id) with its verdict, so a clean verdict on code that has since changed no longer counts as ready. The latest verdict is now the most recently posted one rather than the highest round number.
  • ✓Structured findings. Findings on an agent run can carry round, severity, class, harm and stage keys, validated when present, so mistakes can be counted by kind and by whether they were caught in design or in code. A harm: none tag marks a finding as checked and harmless for merge readiness.
  • ✓Implement queues close only on an independent review. In an implement_until_done work queue, the implementer reporting done no longer finishes an item; it finishes on a trusted strict VERIFIED-CLEAN verdict from a different identity.
  • ✓Service keys can read project context and wiki pages. knowledge:read now covers the project context and its sections, wiki pages, page history and backlinks, and knowledge:write covers wiki page writes, each limited to the key’s project allowlist. Wiki writes and retrieval audit records name the service key as the actor. See Cloud Agents.
  • ✓One active ticket per worker. Parallel workers in different worktrees no longer overwrite each other’s active ticket: set SFS_AGENT_RUN_ID per worker, a copy for the worktree is kept under ~/.sessionfs/workspaces/ (never inside the repository), and captured sessions are tagged with a ticket only when the ticket’s repository matches the session’s.
  • ●Migrations 001–109 (106 adds agent run usage and loop fields; 107 and 109 record service keys on wiki revisions and retrieval audit contexts; 108 adds the reviewed commit to verdicts; all additive). Self-hosted: run migrations 106–109 (see Self-Hosted). 10,373 backend tests + 1859 dashboard UI tests passing. 109 MCP tools (+3).
v0.29.0October 29, 2026

Meeting Transcripts · Action Items to Tickets · Helper That Knows Who Is Asking

  • ✓Meeting transcripts. Add a meeting by pasting text or uploading a .txt, .md, .vtt or .srt file (caption timing is removed) from the dashboard, sfs meeting upload, the API or MCP. Secrets and personal data are redacted under your org’s DLP policy before anything is stored, and a blocking policy refuses the upload. Meetings work on every plan for projects; project-group meetings need Team or Enterprise. See Meetings.
  • ✓Extract action items, decisions and participants. Extraction uses your own AI key or your org’s. Every item cites the transcript lines it came from, and items that can’t be checked against the text are dropped and counted. Participants are matched to org members and personas only when their name appears in the transcript; anyone unmatched can be matched by hand.
  • ✓Create a ticket from an action item. An editable preview quotes the meeting lines, with the assignee and due date filled in and optional Jira publishing. Dismiss an item, or restore it later.
  • ✓Meetings in the dashboard and CLI. A Meetings view sits under a project’s Work section and on project group pages, with search, upload, a numbered transcript, and action items, decisions and participants beside it. sfs meeting covers upload, list, search, show, delete, extract, participants, resolve-participant, action-items, promote, dismiss and restore.
  • ✓Six new MCP tools (106 in total). upload_meeting_transcript, list_meeting_transcripts, get_meeting_transcript, extract_meeting_action_items, promote_meeting_action_item and search_meetings. New API key scopes meetings:read and meetings:write; promoting with a service key also needs tickets:write.
  • ✓The session helper reads meetings. It can search and read meetings in your scope, list your open action items, and cite meeting lines as links that open the transcript at those lines (Enterprise).
  • ✓The session helper knows who is asking. “My tickets” works, a name finds tickets assigned to a persona or to a person, and project groups can be named by name, slug or abbreviation.
  • ✓Retention. Meetings are kept for 180 days by default (1–3650 days, set per org), and the Helm chart has an opt-in CronJob for the cleanup sweep. AI usage is recorded by source, so meeting extraction and helper chats show separately and both count against the org budget.
  • ✓Changed: tabs across the dashboard use a quieter underline style, the project Personas tab is now called Agents, and dialogs keep keyboard focus inside the topmost dialog while it is busy.
  • ●Migrations 001–105 (104 and 105 add meeting transcripts, participants and action items; additive). Self-hosted: run migrations 104–105 (see Self-Hosted). 10,264 backend tests + 1859 dashboard UI tests passing. 106 MCP tools (+6).
v0.28.0October 28, 2026

Helper for Project Groups · Ticket Due Dates & History · Jira Sprints & Parents · Sweep CronJobs

  • ✓Ask the session helper about a project group. Pick a project group in the helper’s scope menu, run sfs helper ask --group, or pass group_id to the ask_session_helper MCP tool. Answers draw only on the group’s member projects you can read, and the group’s compiled overview goes into the prompt, redacted with your DLP policy. See Session Helper.
  • ✓The helper remembers your scope. The drawer keeps the scope you last picked for the rest of the browser tab, per signed-in account, so a new conversation starts where you left off. Ticket and wiki citations become links as soon as an answer finishes, instead of showing “source not available”.
  • ✓Ticket due dates, estimates and history. Tickets show who created and who resolved them, and take a due date and an estimate in Jira duration syntax such as 2d 4h. An Overdue filter lists unfinished tickets past their due date (?overdue=true). Every status change, assignment and edit is recorded with who made it and merged into the ticket’s activity timeline. CLI: sfs ticket history, sfs ticket create|update --due --estimate; MCP: due_date and estimated_effort on create_ticket and update_ticket; API: GET /api/v1/projects/{id}/tickets/{tid}/events.
  • ✓Jira sprints and parents (Enterprise). The publish dialog offers the mapped project’s active and future sprints, and a ticket’s parent becomes the Jira parent or epic. The sprint and epic-link fields are detected automatically, and admins can review or override them under Organization → Integrations → Jira → Connection → Agile fields without re-entering the API token. A linked ticket shows its sprint and parent. See Jira Cloud.
  • ✓Due dates and estimates in Jira. Publishing sends the ticket’s due date and estimate as the issue’s due date and original estimate, Update from Jira brings them back, and with two-way sync on a due date changed in Jira updates the ticket.
  • ✓Clearer, safer Jira publishing. When Jira refuses an optional field such as the sprint or due date, the issue is still published and the dialog explains in plain language what was left out. Publishing never creates a duplicate issue after a timeout or error: the next publish adopts the issue it already created.
  • ✓Helper “create ticket and publish to Jira” with choices. You can pick the Jira project (one mapped to a project you can edit), the story template (pinned to the version you saw) and the sprint.
  • ✓Jira template editor. Tooltips explain each mode and source field, the preview sits in a side pane, Copy from… starts a template from an existing one, and the preview’s project picker is searchable.
  • ✓Org AI key: Fetch models. The org helper key form lists the models the key can use, like the personal form, with manual entry as a fallback. Model lookups are rate-limited per user.
  • ✓Self-hosted: sweep keys and CronJobs. Creating or rotating a platform_sweeps:run key now warns that it works only while the user who minted it stays a platform admin, a key that stopped working gets a 403 naming the cause and the fix, and sfs admin service-keys list shows such keys as inactive. The Helm chart has opt-in CronJobs for every sweep (ticket notifications, org digest, KB compile every 15 minutes, Jira sync every minute, Confluence publish every 15 minutes), with no service account token and, when network policies are on, egress limited to DNS and the API. See Self-Hosted.
  • ●Migrations 001–103 (102 adds ticket due dates, estimates and change history; 103 adds Jira sprint and parent fields; both additive). Self-hosted: run migrations 102–103, then have an org admin run Detect fields once on an existing Jira connection (see Self-Hosted). 10,040+ backend tests + 1801 dashboard UI tests passing. 100 MCP tools (unchanged).
v0.27.0October 27, 2026

Link Existing Jira Issues · Assign Tickets to People · Helper Jira Actions · Reviewer-Agnostic Verdicts

  • ✓Link a ticket to an existing Jira issue (Enterprise). On a ticket, Link existing issue attaches it to an issue that already exists in the mapped Jira project instead of creating a new one. SessionFS checks that the issue exists and isn’t linked to another ticket, and sends nothing to Jira. API: POST /api/v1/projects/{id}/tickets/{tid}/jira/link.
  • ✓Choose which side wins on a linked issue. Until a linked issue has been published from SessionFS, you pick the direction: Overwrite KEY in Jira replaces the issue’s summary and description with the ticket’s (publishing asks you to confirm the overwrite; CLI sfs ticket jira publish --overwrite-existing), and Update from Jira replaces the ticket’s title and description with the issue’s (CLI sfs ticket jira pull, API POST .../jira/pull). An empty Jira field leaves the ticket field unchanged, and pulled text goes through your DLP policy.
  • ✓The session helper works with Jira. It can read a ticket’s linked Jira issue, propose linking a ticket to an existing issue, propose overwriting either side of a linked pair, send a comment to Jira along with the ticket comment, and create a ticket and publish it to Jira in one confirmation. Every Jira condition is checked again when you press Confirm.
  • ✓Assign a ticket to a person. Tickets can now be assigned directly to a teammate, not only to a persona. The person gets a “Your turn” notification and follows the ticket automatically, and the Tickets list has an Assigned to me filter. The assignee picker lists teammates who can read the project and warns when the persona you chose represents nobody, so nobody would be notified. CLI: sfs ticket create|update --user; MCP: assigned_user on create_ticket and update_ticket; API: GET /api/v1/projects/{id}/assignable-users, ?assigned_user=me on the ticket list. The helper can assign to a persona or a person by name.
  • ✓Review verdicts from any reviewer. Verdict headers accept any reviewer name (for example “Claude R1 review”), not just one fixed reviewer. A work-queue completion whose verdict header can’t be parsed is refused with 422 verdict_header_unparseable before anything is written, and sfs ticket review-state prints whether the last verdict is a strict VERIFIED-CLEAN.
  • ✓Worker review loop guide. A new Worker Review Loop page shows how to build a change with brief-driven coding agents in isolated worktrees, gated by an independent trusted reviewer and a re-review before merge. The matching worker-review-loop skill is part of the sessionfs-workflows launch catalog.
  • ✓Changed: projects merged into another project no longer appear in project lists, the helper or Jira health, and can’t be added to a project group (410 project_merged); direct links still answer 410 with the merge target. The Jira sync and Confluence publish sweeps now have documented schedules (Jira sync every minute).
  • ●Migrations 001–101 (101 adds direct-to-person ticket assignment; additive). Self-hosted: run migration 101 (see Self-Hosted). 9830 backend tests + 1659 dashboard UI tests passing. 100 MCP tools (unchanged).
v0.26.0October 27, 2026

Project Groups · Two-Way Jira Sync · Confluence Publishing · Helper Ticket Actions · Sessions by Model

  • ✓Project groups (Team and Enterprise). Org admins can group projects (for example “Payments platform”) and grant people reader, editor or admin access to the whole group. An org can turn on restricted project visibility so members see only the projects they own, created, or reach through a group. Groups offer knowledge search and a list of wiki pages across their projects, and an optional overview document compiled from them, always filtered to the projects each person can read. CLI: sfs project group, sfs project list --group. 15 new MCP tools.
  • ✓Two-way Jira sync (Enterprise). A linked ticket’s status changes move the Jira issue through your status map, and moving the issue to Done in Jira resolves the ticket. Comments can be mirrored in either direction: a Send to Jira checkbox posts a comment to the issue, and trusted VERIFIED-CLEAN review verdicts are forwarded automatically. When a ticket completes, one evidence comment goes to the issue with the verdict, changed files and promoted knowledge. Inbound changes arrive on a webhook signed with a per-org secret, outgoing changes are queued and retried with backoff, and your own changes echoing back are ignored.
  • ✓Custom Jira story templates. Org admins can define story templates with verbatim and rewritten sections and pick one per mapping or per ticket. Every published issue is pinned to the template version it used, with a footer linking back to SessionFS. The mapping dialog now searches your Jira projects instead of loading them all. CLI: sfs org jira templates list|show.
  • ✓Confluence publishing (Enterprise). Publish wiki pages and the compiled project context to a Confluence Cloud space chosen by an org admin, with a preview showing citations and anything dropped. A page someone edited in Confluence is never overwritten: publishing shows the drift as a diff and lets a project admin overwrite or unlink. An optional scheduled publish keeps opted-in pages current and skips unchanged ones. CLI: sfs org confluence, sfs project confluence publish.
  • ✓The session helper reads more of your project. It can look up tickets, wiki pages, agent runs and personas, with the date each answer is as of, and cite them.
  • ✓The helper drafts tickets, assigns them and comments. You review and edit every field before pressing Confirm, and nothing changes until you do. It follows the same permissions as the ticket pages, never posts a trusted review verdict, and records which conversation the change came from.
  • ✓The helper finds sessions by model. Ask “which sessions used DeepSeek?” and it lists the models you’ve used with counts, filters by model or provider family, pages through results, and can propose moving every match to Trash (100 per proposal).
  • ✓Changed: a calmer marketplace. A compact header with most filters behind a Filters button that shows removable chips, install commands in an Install in your tool dialog, simpler cards, and package detail in tabs with scan results and safety alerts always on the first tab.
  • ✓Changed: Organization → Integrations starts with one summary row per integration that names only real problems; each integration’s settings sit in their own sections (overview, connection, mappings, templates, sync and activity).
  • ✓Changed: ticket detail has a clear header and one timeline of comments, review verdicts, Jira activity and edits. Project settings are grouped into General, Knowledge, Repositories, Access & ownership and Danger zone.
  • ✓Changed: the top bar no longer overlaps its icons at tablet widths, and headings, landmarks and scrollable tables are labelled for screen readers.
  • ✓Fixed: merging two projects that both had knowledge entries no longer fails with a server error; the project Transfer panel only appears for people allowed to start a transfer; session search with a date range no longer fails on PostgreSQL; text from consecutive helper steps no longer runs together.
  • ✓Security: two-way Jira sync stops for a project that leaves the linked org; the Confluence Markdown converter can’t be made to stall the server; Confluence pages and new-project knowledge backfill only use sessions that belong to the project or its creator.
  • ●Migrations 001–100 (096–100 add project groups, custom Jira templates, group context, Confluence and Jira sync; all additive). Self-hosted: run migrations 096–100, and schedule POST /api/v1/admin/sweeps/jira-sync and POST /api/v1/admin/sweeps/confluence-publish to keep Jira retries and Confluence auto-publishing running (see Self-Hosted). 9672 backend tests + 1493 dashboard UI tests passing. 100 MCP tools (+15 project group tools).
v0.25.0October 26, 2026

Helper Actions · Helper Docs Answers · Configurable Helper · Sessions by Model · Project Edit Access

  • ✓Helper actions with Confirm. The session helper can propose two changes: moving sessions to Trash (“clean up my scratch sessions from last month”) and turning Auto Narrative on or off for projects. Each proposal shows a card listing every target and what will happen to it; nothing changes until you press Confirm, and access is checked again for each target at that moment. Sessions go to Trash, never deleted, so cleanup can be undone for 30 days. Proposals expire after 15 minutes, and every decision is recorded in the retrieval audit log.
  • ✓Clickable references in helper answers. Ticket ids open a preview over the conversation (status, priority, assignee, description, latest comments, Open ticket) without leaving the page; project and session ids open their pages, and PR numbers and commit SHAs link to GitHub when the project has one verified repository.
  • ✓The helper answers from the SessionFS docs. Questions about how SessionFS works, setup, commands, settings and plans are answered from the official documentation, with links to the exact page and section.
  • ✓Configure the assistant. Org owners and admins, and each user for their own key, can set the maximum answer length, temperature, top-p, tool steps, reasoning effort and answer style; orgs can cap output tokens and tool steps. Custom instructions (up to 4000 characters for the org, 2000 personal) can’t override the helper’s access and safety rules. sfs helper status shows the effective settings.
  • ✓Token usage and a monthly budget. Every helper turn records its token usage. Org admins see usage per member for the current month and can set a monthly token budget for turns that use the org’s key; once it is reached, helper turns return 429 helper_budget_exceeded until the next month. Deleting a conversation doesn’t reset usage.
  • ✓Filter and sort sessions by model. The Sessions page can filter by the LLM model a session used and group sessions by model (?model= on the session list, GET /api/v1/sessions/models).
  • ✓Test connection for LLM keys. Personal and org key settings have a Test connection button that sends a tiny request and shows the endpoint, model and latency, or a readable reason it failed (wrong address, invalid key, model not found, rate limited, out of balance).
  • ✓Help page refresh. The dashboard Help page covers the session helper, ticket notifications, model settings and the current MCP tools.
  • ✓Changed: saving a Base URL checks it first. A base URL that points at a provider’s website or console instead of its API, or a key the provider rejects, is refused with a clear message. Outages and rate limits never block a save; they are shown as warnings. When the helper hits a wrong address at runtime, its error names the API address to use.
  • ✓Changed: only a project’s owner and its organization’s members can change it. Anyone else who synced a session from the same repository can still read the project and add knowledge entries, tickets, comments and agent runs, but changing settings, rules, wiki pages, the context document, personas or work queues, or curating the knowledge base, now returns 403 project_edit_forbidden. Project responses carry can_edit, and the dashboard hides the controls such a collaborator can’t use.
  • ✓Changed: collaborators’ contributions wait for a member. Their knowledge entries stay notes and never reach the context document or compiled rules until an owner or org member promotes them (bulk promote leaves them out unless you pass --include-reader-notes). Their tickets start as suggested and go back to suggested when edited after approval, and work queues don’t pick them up until approved. Their comments don’t reach automated agents.
  • ✓Security: people who only synced a session can no longer change a project. Before this release, anyone with a captured session from a project’s repository could edit its settings, rules and context, which could change the instructions compiled into other members’ tool rules files.
  • ●Migrations 001–095 (094 adds helper actions, 095 helper settings and the usage ledger; both additive). Self-hosted: run migrations 094 and 095, then review tickets and knowledge notes from people outside your org, which now wait for an owner or org member (see Self-Hosted). 8967 backend tests + 1189 dashboard UI tests passing. 85 MCP tools (unchanged).
v0.24.0October 25, 2026

Ticket Following · 14 LLM Providers · Helper Redesign · Crash-Safe Local Store

  • ✓Follow tickets. Follow a ticket to hear when it changes state, changes hands or gets a comment. Creating a ticket, being assigned, commenting or posting a review verdict follows it for you, and you can mute a ticket without unfollowing. A bell in the dashboard opens your inbox, where “your turn” notifications (a ticket assigned to you, a review waiting on you) stand out and open the ticket directly. Bursts of changes are combined into one notification, and notifications never copy comment bodies. CLI: sfs ticket follow|unfollow|inbox.
  • ✓Ticket updates for agents. Agents poll what changed on the tickets they follow with sfs ticket updates, the MCP tools watch_ticket and get_ticket_updates, or GET /api/v1/me/ticket-updates. Each persona keeps its own cursor, so several agents on one account each see every change once; service keys get their own cursors and only see projects they are allowed to.
  • ✓Ticket email digest. Choose off, your_turn (the default) or all under Settings → Preferences. One email lists everything pending instead of one email per change, and access is re-checked before anything is sent.
  • ✓14 LLM providers. The security review, session helper, summaries and Jira drafting accept a key from Anthropic, OpenAI, Google Gemini, OpenRouter, DeepSeek, Kimi (Moonshot), xAI, Mistral, Z.ai (GLM), Qwen (Alibaba Cloud), MiniMax, Groq, Together AI or Fireworks AI, or any OpenAI-compatible endpoint (LiteLLM, vLLM, Ollama). Fetch models lists the models your key can use, and any model id can be typed in. sfs audit --provider reads that provider’s own key variable (DEEPSEEK_API_KEY, MOONSHOT_API_KEY, and so on). The default model is now claude-sonnet-5-5.
  • ✓Helper citations open the exact message. A citation in a helper answer opens the session at the cited message and highlights it. The audit tab’s Jump to message uses the same link.
  • ✓Helper message retention in org settings. Org admins can set how long helper messages are kept from the Organization page.
  • ✓Changed: the session helper drawer is redesigned, with a pinned composer, readable citations, conversation history and New always at hand, and clear states while access is being checked or a scope isn’t available. Retrying a failed answer no longer saves the question twice.
  • ✓Changed: reasoning models work behind any gateway (GitHub #102). When an endpoint rejects temperature, top_p or reasoning settings, SessionFS retries that request once without them and remembers it for that endpoint and model. Reasoning effort is sent in each API’s own shape, and a failed narrative summary now tells you why.
  • ✓Changed: sfs pull makes the local copy match the server. Files the server version no longer has are removed, while local-only reports such as the audit report and checkpoints are kept.
  • ✓Changed: Enterprise features follow the project’s plan. Promote to Jira, the session helper and other project features are shown based on the plan of the project’s organization, not your primary org’s.
  • ✓Fixed: the local session store is crash-safe. Every capture, pull, recapture and delete writes through one locked commit: a deleted session can no longer come back from a capture already in progress, files are written atomically and flushed to disk so a power cut can’t leave a half-written session, symlinked session folders are refused, and a session that changed during an upload uploads again. Captures are no slower.
  • ✓Fixed: a broken Unicode character in a branch name, title or message no longer makes a push fail; it is stored with a replacement character.
  • ✓Fixed: Codex resume no longer produces duplicate tool calls when a message was captured twice.
  • ✓Fixed: the helper status follows the question’s scope, so a project in an Enterprise org shows the helper as available even when your primary org isn’t Enterprise.
  • ✓Security: stored keys use a derived encryption key. LLM keys and integration tokens are encrypted with a key derived by HKDF (existing values still decrypt), and the server refuses to store or read them with an unset or placeholder SFS_VERIFICATION_SECRET outside development.
  • ✓Security: a saved LLM key stays with its provider. Changing a saved key’s provider or endpoint requires entering the key again, and security reviews or narrative summaries that name a different endpoint no longer fall back to your saved key.
  • ✓Security: concurrent upload parts per user are capped and abandoned part data is cleaned up; Jira connection fields are bounded and Jira error bodies are no longer passed through; helper knowledge search escapes % and _.
  • ●Migrations 001–093 (093 adds ticket followers, notifications and cursors; additive). Self-hosted: Helm-generated secrets now persist across upgrades — if stored keys stopped working after an earlier upgrade, re-enter them once; set a real SFS_VERIFICATION_SECRET; schedule POST /api/v1/admin/sweeps/ticket-notifications for ticket emails (see Self-Hosted). 8631 backend tests + 1049 dashboard UI tests passing. 85 MCP tools (+2: watch_ticket, get_ticket_updates).
v0.23.0October 24, 2026

Session Helper · Jira Cloud · Session Changes · Agent Run Replay · Resumable Uploads

  • ✓Session helper (Enterprise). Ask a question about the sessions and project knowledge you can already read (“find the session where we fixed the SSO redirect”, “what did we decide about Redis and why?”) and get an answer that cites the sessions and messages it used. It searches sessions, reads summaries and excerpts, searches project knowledge and finds sessions that touched a file, and never sees more than you can. Answers stream into a dashboard drawer (sidebar, Cmd+K, or a project or session page), into the terminal with sfs helper ask, or come back whole from the MCP tool ask_session_helper. Threads are kept (sfs helper conversations list|show|delete), and sfs helper status shows which key it would use. The model call always uses your own key or your org’s helper key; SessionFS never supplies one. See Session Helper.
  • ✓Org AI key for the session helper. Enterprise org owners and admins can set one provider, model, API key and optional base URL for members without a key of their own (Organization → Policies). The key is encrypted at rest, never shown again after saving (only its last four characters), can be switched off without deleting it, and a custom base URL passes the same address checks as other user-supplied LLM endpoints.
  • ✓Helper conversation retention. Helper messages are deleted after 90 days by default; orgs can set 1–3650 days with helper_retention_days. The deletion runs from a sweep, and the Helm chart can schedule it (api.helperRetention).
  • ✓Jira Cloud integration (Enterprise). Promote a ticket to a Jira issue. Org admins connect a Jira Cloud site from the Organization page’s new Integrations tab or sfs org jira connect, map projects and check health. Promote to Jira shows an editable preview from one of four templates (Agile, Compliance, SRE, Bug report) before anything is sent. Acceptance criteria, completion notes and linked sessions are copied word for word; reworded paragraphs that don’t cite a real part of the ticket are dropped. One way: Jira changes never come back. CLI: sfs ticket jira preview|publish|status|unlink. See Jira Cloud.
  • ✓See what a session changed (GitHub #94). A session’s new Changes tab lists every file the agent created, edited or deleted, with line counts and a diff per file, rebuilt from the captured tool calls of all 10 tools. Diffs come from the stored, already-redacted session, so a redacted secret never reappears.
  • ✓Replay an agent run with a different prompt or context (GitHub #95). sfs agent replay RUN_ID (or Replay in the dashboard, or the MCP tool replay_agent_run) queues a copy of a run with an edited prompt, ticket, persona, context references, fail-on level or trigger ref, and prints the command and a CI snippet to run it. sfs agent compare A B shows which findings were added, removed or unchanged. See CI Integration.
  • ✓Scheduled knowledge-base compile (GitHub #93). Each project can compile its context nightly, weekly or off, optionally once a number of claims are waiting (sfs project compile-schedule or Settings → Scheduled KB compile). The Overview shows when the context was last compiled and how many claims are waiting, with a one-click compile. Scheduled compiles use the deterministic compiler, never an LLM.
  • ✓Org admins can propose moving a project into their org (GitHub #96). The owner of the personal project sees the proposal in their transfer inbox and by email, and nothing moves until they accept. Proposals expire after 7 days.
  • ✓Weekly org activity digest (GitHub #97). Owners and admins of Team and Enterprise orgs get a weekly email with the past week’s sessions, tickets, agent runs, knowledge claims, handoffs and admin actions. Each can turn it off under Organization → Policies → Notifications.
  • ✓Persona activity and history (GitHub #92). Clicking an org persona opens its recent sessions, open tickets, latest knowledge entry and latest agent run, limited to projects you can see; org admins also see who has represented it over time.
  • ✓Operator metrics (GitHub #90). GET /api/v1/admin/metrics serves Prometheus metrics (request latency by route, pushes, pulls, handoffs, DLP findings, auth failures, active users, sessions, DB pool) to a platform admin or an admin_metrics:read service key, with a ready-made Grafana dashboard. See Self-Hosted.
  • ✓Changed: large sessions upload in resumable parts (GitHub #91). Sessions over 8 MiB are pushed in parts that are retried individually, and an interrupted push resends only the missing parts. Older servers get the single-request push automatically.
  • ✓Changed: knowledge confidence decays with time, not with each compile. Claims nobody has referenced for 90 days lose confidence (×0.8) at most once per 30 days.
  • ✓Changed: org settings saves merge. Settings you leave out are kept, and an explicit null returns a setting to its default.
  • ✓Fixed: Codex sessions resume with their tool results attached, and Codex custom tool calls such as apply_patch come back as the same item types they were captured as.
  • ✓Fixed: DLP policies saved before stricter checks stay editable. A stored custom pattern missing a category or severity, or with a regex that no longer compiles, no longer blocks every later save; broken stored patterns are skipped during scans and listed under invalid_custom_patterns.
  • ✓Fixed: a 409 from a push counts as a version conflict only when the server says etag_mismatch; other 409s are retried with backoff instead of dropping the local change.
  • ✓Security: remote images in rendered Markdown aren’t loaded. Project context, wiki pages and helper answers only display images from the dashboard itself or inline image data; any other image appears as a link.
  • ✓Security: Jira credentials and requests are confined. The API token is encrypted at rest and never returned or logged; site URLs must be https://<name>.atlassian.net, redirects aren’t followed, responses are capped at 2 MiB, and ticket text is redacted for secrets before reaching a model.
  • ✓Security: the session helper sees only what you can. Its tools check access on every call, org-bound threads re-check membership every turn, the strictest DLP policy of the orgs involved applies, messages are stored redacted, and logs never contain prompts, excerpts or keys.
  • ✓Security: org keys and Jira connections stay where they were set. Changing the org AI key’s provider or endpoint URL requires entering the key again, the helper re-checks a custom endpoint before every request, and only members of the project’s organization can publish through its Jira connection.
  • ●Migrations 001–092 (084–092 add org helper keys, replay columns, the digest ledger, transfer proposals, resumable uploads, compile schedules, Jira and helper conversations; self-hosted: the KB compile, org digest and helper retention sweeps need a scheduler, see Scheduled Sweeps). 8402 backend tests + 972 dashboard UI tests passing. 83 MCP tools (+2: ask_session_helper, replay_agent_run).
v0.22.0October 24, 2026

One Project per Repository · Operator Merge Tools · Tighter GitLab Integration

  • ✓The same repository no longer becomes two projects (GitHub #88). Remotes written as ssh://, git://, git+ssh://, scp style with a user other than git (or none), with an uppercase scheme or a trailing slash were stored as written, so each spelling made a separate project. All of them now map to the repository path, project creation normalizes the value it’s given, and migration 083 rewrites stored remotes. Where two projects already exist for one repository, the migration leaves the pair alone and prints both ids so an operator can merge them. Repositories in a home directory or at an absolute path on a personal server keep their host, so different machines don’t collide.
  • ✓Merge duplicate accounts. Platform admins can fold a duplicate signup into the account to keep with sfs admin override user-merge SOURCE TARGET --reason "...". Dry run by default; --apply moves sessions, projects, handoffs, tickets, knowledge and the rest in one transaction, deduplicates org memberships (the higher role wins), revokes the duplicate’s API keys, deactivates it and writes one audit entry. It refuses rather than guess when both accounts hold an active plan, or the duplicate has billing or marketplace assets. See Auditing Operator Overrides.
  • ✓Merge duplicate projects as an operator (GitHub #89). sfs admin override project-merge SOURCE TARGET --reason "..." runs the existing project merge for a platform admin who owns neither project, including two users’ personal projects. Dry run by default; the audit entry commits together with the merge.
  • ✓Project pages use the project id in the URL (/projects/proj_...). Old links by remote redirect, and a merged project’s link opens the project it was merged into.
  • ✓Fixed: merging accounts clears a session alias the target already uses and keeps one sync-watchlist row per session.
  • ✓Security: GitLab instance URLs are validated. Saving a GitLab instance URL that points at a private, loopback or cloud-metadata address is refused (422 invalid_gitlab_instance_url), the host is re-checked before every request so a DNS change after saving can’t redirect the token, and redirects aren’t followed. Self-hosted deployments with GitLab on a private network can allow private addresses with SFS_GITLAB_ALLOW_PRIVATE_HOSTS=true (Helm api.gitlab.allowPrivateHosts); metadata addresses stay blocked. See Git Integration.
  • ✓Security: the SSRF guards now judge IPv4-mapped IPv6 addresses (::ffff:169.254.169.254) by their IPv4 part, and also block 0.0.0.0/8 and 100.64.0.0/10.
  • ✓Security: custom LLM endpoints are checked. A custom base_url for the LLM judge or narrative summaries is now checked when it is saved and before every request, redirects are not followed, and on the hosted service it must be a public https endpoint. Self-hosted model gateways on a private network keep working: the Helm chart sets SFS_LLM_ALLOW_PRIVATE_HOSTS=true. Model discovery now takes the API key in the request body (POST /api/v1/settings/judge/models) instead of the query string.
  • ✓Security: dashboard dependencies updated (zod 4.6.7, undici).
  • ●Migrations 001–083 (083 rewrites stored git remotes to one canonical form; 067 and 068 unused). 7740 backend tests + 815 dashboard UI tests passing. 81 MCP tools (unchanged).
v0.21.1October 23, 2026

Project Context You Can Read · GitLab in the Dashboard · Pinned GitLab Namespaces

  • ✓Project context and wiki pages render as documents. Bullets, numbered lists, quotes, tables, task lists and code blocks display properly, each line an agent writes stays on its own line, long ids and URLs wrap, and the text uses the page width at a comfortable reading length. Raw HTML in a document is never rendered, and only web, mail and in-page links are clickable.
  • ✓An “On this page” section list. The project Overview shows a list of the context’s sections beside the text, highlighting the one you’re reading; on phones it’s a collapsible list above it.
  • ✓GitLab in the dashboard. Settings → Integrations → GitLab connects, updates or disconnects your GitLab instance (gitlab.com or self-managed) for merge request context comments. The access token and webhook secret are write-only: the form never shows them back. See Git Integration.
  • ✓GitLab trusted publishing pins the namespace by default. sfs skill publisher trust --gitlab-project ID looks up the project’s current namespace and pins it, so a project moved to another namespace can’t keep publishing. Pass --gitlab-namespace ID to set it yourself or --no-namespace-pin to opt out (with a warning); unpinned GitLab trusts are flagged by the server and marked in the dashboard. See Trusted Publishing from GitLab CI.
  • ✓Session list accessibility. Rows no longer nest buttons inside a clickable row, so screen readers and keyboards handle them properly. In a group of follow-up sessions, the title expands the group and View opens the session.
  • ✓Consistent dashboard styling. Buttons and colours use the shared components and theme colours throughout.
  • ✓Fixed: platform administrator accounts could be treated as Free in plan checks; they now always count as Enterprise.
  • ✓Fixed: checkpoints created within the same filesystem timestamp are listed in a stable order.
  • ✓Security: GET /api/v1/settings/gitlab returned the stored GitLab webhook secret on every read. It now reports only whether one is set (has_webhook_secret); PUT returns the secret once, and only when the server generated it.
  • ●Migrations 001–082 (unchanged; 067 and 068 unused). 7609 backend tests + 806 dashboard UI tests passing. 81 MCP tools (unchanged).
v0.21.0October 22, 2026

A Reorganized Dashboard That Works on Phones · GitLab CI Trusted Publishing · Audited Operator Overrides

  • ✓A reorganized dashboard. The project page’s 12 tabs are now 5 sections — Overview, Knowledge, Work, Rules & Skills and Settings — kept in the address bar so links and the back button work, and old links still open the right place. Project rename, repos, merge, transfer and delete live under Settings. Account settings are split into Account, Preferences, Integrations, Data protection and Sync, each with its own address. The Organization page has tabs (Members, Personas, Policies, Security). The sidebar is grouped into Library, Inbox and Manage, the marketplace pages share one sub-navigation, and the project-level Marketplace tab is now called Skills.
  • ✓The dashboard works on phones. Every page fits a 375 px screen without sideways scrolling, and tab bars scroll instead of spilling out of their card (GitHub #86, #87).
  • ✓Colours and accessibility. Status colours in the light theme meet WCAG AA contrast, the light-theme brand blue is slightly darker, and filled brand-coloured buttons in the dark theme use dark text. Tabs follow the standard keyboard pattern (arrow keys, Home, End), notifications are announced reliably to screen readers, and error messages use one wording.
  • ✓Trusted publishing from GitLab CI (GitHub #82). GitLab CI jobs can exchange a GitLab ID token (id_tokens:) for the same single-use, 15-minute publish token GitHub Actions gets, so no stored skills:admin key is needed in CI. A trust binds to the numeric GitLab project (optionally the namespace), requires a protected ref, matches the ref pattern and environment, and can require a protected GitLab environment. Only push, web and API pipelines publish; merge request, scheduled, trigger and downstream pipelines never do. Set it up with sfs skill publisher trust --gitlab-project ID [--gitlab-host HOST] and publish with sfs skill publish --trusted-publishing --ci-provider gitlab. See Trusted Publishing from GitLab CI.
  • ✓An audit trail for operator overrides (GitHub #84). Admin actions now record a reason, the state before and after, and whether they went through the API or were recorded by hand. A new force-transfer moves a project into an organization without the owner’s consent, under the same safety checks as a normal transfer; the new owner must be an active member of the destination organization, and a retried request can’t move a project twice. Changes made by hand can be recorded, and the audit log filters by target, action, source and admin. User and org tier changes and forced owner transfers accept a reason. CLI: sfs admin audit-log, sfs admin override project-transfer|record. See Auditing Operator Overrides.
  • ✓sfs --version, sfs -V and sfs version (GitHub #85) print the installed version with the Python version and platform.
  • ✓Fixed: /health/ready through the Helm chart’s ingress (GitHub #83). The dashboard’s web server answered /health/ready itself, so readiness probes through the ingress always saw 200 even when the database schema was behind. It is now passed to the API, and other paths under /health/ return 404.
  • ✓Fixed: verifying an SSO domain no longer reports an error when the domain is deleted at the same moment.
  • ✓Upgrading a self-hosted server: run migrations 081 and 082. 081 adds reason, before/after state and source columns (plus indexes) to the admin audit log; 082 lets trusted publishers hold GitLab trusts (downgrading it removes GitLab trusts and their tokens). Self-hosted GitLab users who want trusted publishing: add your instance URL to SFS_GITLAB_OIDC_ISSUERS, exactly as GitLab’s external_url.
  • ●Migrations 001–082 (081–082 new; 067 and 068 unused). 7591 backend tests + 789 dashboard UI tests passing. 81 MCP tools (unchanged).
v0.20.0October 22, 2026

Org Personas That Represent a Member · Project Tools Accept a Folder

  • ✓Org personas that represent a member (GitHub #81). An org owner or admin can link an org persona to the member it stands for. That member can then update the persona’s role, content and specializations without org admin rights; turning the persona on or off, changing or removing the link, creating and deleting stay with admins. Set the link on the dashboard (Organization → Personas → Represents), with sfs persona create --org ORG --represents EMAIL or sfs persona represent NAME --org ORG --user EMAIL|--clear, or through the MCP create_persona / update_persona tools (represented_user). Members see their own persona under Your org persona. Linking a project persona is refused.
  • ✓Project MCP tools accept a folder as git_remote. A value starting with /, ~, ./ or ../ is resolved through that repository’s origin remote (git worktrees included) instead of returning “No project found”. A folder that isn’t a git repository with an origin gets a clear error instead of acting on another project; leaving git_remote out still uses the current workspace. The “No project found” message now says git_remote takes a remote URL such as github.com/org/repo.
  • ✓Security: removing a member from an organization, or deactivating a user, clears any persona link to them, with an entry in the organization’s audit log. A member who rejoins does not get edit rights back until an admin links them again.
  • ✓Upgrading a self-hosted server: run migration 080 (adds a nullable agent_personas.represented_user_id; on PostgreSQL also a foreign key that clears the link when the user is deleted, and a check that only org personas can have one). Existing personas represent nobody.
  • ●Migrations 001–080 (080 new; 067 and 068 unused). 7444 backend tests + 667 dashboard UI tests passing. 81 MCP tools (unchanged).
v0.19.0October 21, 2026

Marketplace Discovery · Native Tool Catalogs · “Works In” Matrix · Org Skills Policy

  • ✓Marketplace discovery. Browse by category and filter by publisher, verified publisher, trusted-publishing provenance, license, worst open scan finding, capability (instruction-only, scripts, MCP servers, hooks, executable extensions, pre-approved tools), the tools a package is verified to work in, and how recently it was updated. The same filters work in the dashboard, sfs skill search, sfs skill categories, the API and the MCP tool search_skill_marketplace. Ranking is fixed and documented — no stars or reviews. Listings show the package README, rendered safely: no raw HTML, no remote images, and images inside the package are checked by content.
  • ✓Install counts and freshness. Listings show 30-day installs per tool (counts under 10 show as <10), last updated and last verified per tool, and stale, deprecated (with an optional successor) and unmaintained flags. Publishers get an analytics view (dashboard Publishers → Analytics, sfs skill publisher stats), and Team organization admins can see what their own members use (sfs skill usage --org). Install counts are sent only after a one-time notice, only while signed in, and stop with SFS_SKILL_USAGE_REPORTING=0 or either telemetry opt-out.
  • ✓Native tool catalogs. Approved packages are published as read-only git catalogs that each tool’s own plugin command can add: Claude Code /plugin, Copilot CLI, the Codex Plugins Directory, Cursor team marketplaces and Gemini CLI extensions. sfs catalog list|add|doctor sets them up, and your key is sent only through the sfs catalog credential helper, never in a URL. There is a public catalog for every signed-in user, a personal catalog (Starter and above) and an organization catalog (Team and above). Blocks, yanks and quarantines remove entries on the next fetch. sfs catalog export --to <remote> copies a catalog to a repository you own, and can run unattended with --ssh-key-file.
  • ✓“Works in” matrix. Every listing shows, per tool, whether the tool’s own installer accepted the package (verified, with tool version and date), whether the generated entry matches the tool’s documented format (format-valid), whether it regressed on the newest tool version, or only what SessionFS expected. sfs skill works-in <id> and MCP get_marketplace_package show it too. Verification runs in an isolated sandbox that never runs a skill; checks on private packages are a Team feature.
  • ✓Org skills policy (Enterprise). One policy, applied on the server (install requests, approvals, assignments and MCP loads are refused when the policy forbids a version) and compiled into each tool’s own managed settings: Claude Code and Copilot managed-settings.json, a VS Code device policy, Gemini CLI system settings, Codex requirements.toml, and a Cursor dashboard checklist. Every field says whether the tool enforces it or only SessionFS checks it. New sfs policy show|compile|status|set|disable, the MCP tool get_skill_policy, and a devices:report scope for compliance reports. Delivery starts in advisory mode; deploy the compiled files through MDM for tool-level enforcement. An organization that leaves Enterprise keeps its last policy enforced, and owners and admins can relax or disable it but not tighten it.
  • ✓Git registries publish themselves (GitHub #69). An organization registry on https:// can be polled by the server: every new tag matching the registry’s pattern becomes a new, immutable version, through exactly the checks of a manual publish (validation, DLP, scanning, disclosure, quotas, blocks). The dashboard shows each registry’s poll status with a Poll now button. Private repositories use a credential reference (env: or k8s:), never a token. The fetch refuses private and metadata addresses, follows no redirects, runs no hooks and checks nothing out.
  • ✓platform_sweeps:run service-key scope so scheduled jobs can run the platform sweeps without a personal admin key. The key stops working if its owner or the admin who created it loses platform admin; report-only and forced rescans still need an admin’s own key.
  • ✓Pushing large sessions is much faster (GitHub #71). The server’s secret scan skips lines and positions a pattern cannot match and redacts large sessions in one pass, and the work no longer blocks other requests. A push whose If-Match is already out of date is refused before any scanning. Pushes report how long each phase took in a Server-Timing header and the server log, and SFS_LOG_LEVEL now takes effect.
  • ✓Helm chart: the API image now includes git and an SSH client for registry polling and catalog export, with a new api.registryPolling values block.
  • ✓Security: email delivery logs no longer include full recipient addresses or, for the logging-only provider, message subjects; marketplace text shown in the terminal has control characters stripped.
  • ✓Upgrading a self-hosted server: run migrations 076–079 (discovery, native catalogs, org skills policy, registry polling) — all additive. Registry polling stays off for existing registries until an owner or admin turns it on, and needs a scheduler calling the poll endpoint with a skills_registry:poll key.
  • ●Migrations 001–079 (076–079 new; 067 and 068 unused). 7408 backend tests + 663 dashboard UI tests passing. 81 MCP tools (1 new, up from 80).
v0.18.0October 20, 2026

A Marketplace You Can Trust · Verified Publishers · Org Personas · Antigravity Capture

  • ✓Install disclosure for every skill package. Before you consent, sfs skill inspect, sfs skill add, the dashboard and the MCP tool get_marketplace_package show what a package will do in each tool: files it writes, scripts and their interpreter, hooks, tool pre-approvals, MCP servers, network endpoints, environment variables, shell commands its instructions ask for, scanner findings, and hidden or bidirectional Unicode characters (with file, line and column).
  • ✓Updates are classified and re-consented. Every update is text (your earlier consent carries forward after one confirmation), code (script, hook or MCP bytes changed) or widening (any new access — always stops until you pass --approve-widening or an org admin approves that exact version). sfs skill pull and the daemon apply text updates and hold the rest. sfs skill diff and the new MCP tool diff_marketplace_versions show what changed between two versions.
  • ✓Held updates in the dashboard. “Updates to review” in an organization’s managed library lists held updates with the diff and the new version’s disclosure; an admin acknowledges the disclosure and advances the assignment.
  • ✓Hosted marketplace installs from the CLI. sfs skill add sessionfs:<namespace>/<name>[@version] checks the content hash three ways before installing and pins the exact version in the lockfile. sfs skill search includes hosted results (--tool filters by compatibility), and sfs skill pull installs your organization’s required assignments and your approved install requests. With cloud sync on, the daemon keeps required assignments current every 15 minutes. Interrupted installs are settled safely on the next run.
  • ✓Publish from the CLI and dashboard. sfs skill validate runs the server’s own publish checks locally with each problem’s file, line and fix; sfs skill publish uploads to your personal library or an organization. The dashboard marketplace has a publish dialog.
  • ✓Reports, takedown and revocations. Anyone can report a listing. Moderators — and an organization’s owners and admins for their own listings — can yank (keep installed copies, refuse new installs) or block (remove from every device) a version or a whole package, with a required reason and an audit record, from a new dashboard moderation queue. Blocked content is refused by content hash, so re-publishing the same bytes doesn’t bring it back. Blocked packages are moved into a local quarantine folder with your edits kept.
  • ✓Instruction scanning. Every published version is scanned for dangerous instructions and scripts: fetch-and-run, encoded execution, credential harvesting, exfiltration endpoints, permission bypass, persistence, concealment, defense evasion and hidden Unicode. Critical findings refuse the publish; live versions are rescanned weekly and whenever the ruleset changes, and a new critical finding quarantines the version until a moderator reviews it.
  • ✓Verified publishers. sfs skill publisher claim|list|show|verify|check claims a globally unique namespace and verifies it with a DNS TXT record or a GitHub organization or account. Listings show a verified: badge, and names that imitate a reserved brand or a verified publisher (lookalike letters, digit swaps, typos, -official suffixes) are refused.
  • ✓Trusted publishing from GitHub Actions. sfs skill publisher trust allows a specific repository and workflow to publish without storing an API key; sfs skill publish --trusted-publishing exchanges the job’s OIDC token for a single-use, 15-minute publish token. Pull-request events never publish, and listings show where each version was published from.
  • ✓Org-level personas (GitHub #70). Define a persona once for an organization and use it in every project in that org. Owners and admins manage them in the dashboard, with sfs persona … --org, or MCP create_persona / update_persona / delete_persona with org_id. A project persona with the same name overrides the org one for that project.
  • ✓Antigravity session capture. A new watcher captures Google Antigravity conversations from the desktop app, the agy CLI and the IDE build, including tool calls and results, with subagent conversations folded into their parent. sfs init, sfs watcher enable antigravity, sfs recapture and sfs mcp install --for antigravity support it. Capture-only, like Cursor. SessionFS now captures from 10 tools.
  • ✓Verified GitHub sign-in when linking the GitHub App. Linking an installation now requires signing in to GitHub, and SessionFS links it only after GitHub confirms your account can access it. Repository verification also needs a recent proof that you own or administer the installation; the dashboard shows Re-verify with GitHub when it lapses.
  • ✓Project features follow the project’s plan. Project context and wiki writes, personas, tickets, agent runs and agent feedback are checked against the plan of the project’s owner — so members of a paid organization get its features in its projects even when their own default organization is on the free plan. Projects that already used a feature keep it.
  • ✓Organization creators are owners. Creating an organization now makes you its owner, and owners have admin standing everywhere admins do.
  • ✓Settings → Account shows your effective plan, including a plan you get through your organization.
  • ✓Billing is more robust. A checkout can’t open a second subscription for the same account, and a paying account is never downgraded by an out-of-order billing update. If billing can’t be confirmed, checkout asks you to try again shortly instead of proceeding unconfirmed.
  • ✓Persona names resolve case-insensitively in both project and org scope, preferring an exact-case match.
  • ✓sfs mcp install and uninstall edit config files atomically, keeping a timestamped backup and leaving files that aren’t valid JSON untouched.
  • ✓Fixed: large sessions couldn’t be pushed (GitHub #68). Uploads and downloads now wait up to 5 minutes by default (configurable with sfs push --timeout, SFS_UPLOAD_TIMEOUT or upload_timeout in config.toml), and a push that runs out of time stops at once with a clear message. sfs auth login now keeps your other sync settings.
  • ✓Fixed: merging projects now moves agent feedback records; sessions with a detected slash command pass format validation; sfs delete and pruning no longer leave a session in the local index; Kilo Code gets the Cline-family context budget.
  • ✓Security: GitHub App integration hardened. Unsigned GitHub deliveries are refused and replays are ignored, installation links require proof of control, and repository verifications from older installation links were reset to owner-attested (links are kept; re-verify by linking the repository again after signing in to GitHub).
  • ✓Security: repo-reclaimed projects can no longer be squatted, and SSO and publisher domains share one canonical spelling so a domain can’t be verified twice.
  • ●Migrations 001–075 (065–075 new; 067 and 068 unused). 6901 backend tests + 636 dashboard UI tests passing. 80 MCP tools (1 new, up from 79) · 10 supported tools (Antigravity added).
v0.17.1October 14, 2026

Sync Backoff · sfs daemon retry-sync · Quieter Server Logs

  • ✓sfs daemon retry-sync [SESSION_ID] asks the running daemon to retry sessions whose sync is backing off or paused. A unique prefix of the session ID works; --config targets a daemon started with a custom config.
  • ✓Sync backs off on repeated failures. When pushes for a session keep failing with server errors, rate limits or network problems, the daemon now retries after 1 minute, 5 minutes, 15 minutes, then hourly, instead of every push interval. After 12 failures over at least six hours it pauses that session until its content changes, the daemon restarts, or you run sfs daemon retry-sync. sfs daemon status and sfs doctor list these sessions with their last error.
  • ✓Local cleanup never removes changes that haven’t been uploaded. A session with newer local changes than the server holds is no longer treated as synced, and sessions the daemon still intends to upload are skipped by automatic pruning.
  • ✓Fixed: one session was retried 15,670 times. Its pushes failed because of a NUL byte (fixed in v0.17.0); the daemon now backs off instead.
  • ✓Fixed: server error logs could include session content. Failed database statements no longer log their values, and integrity errors log only the error type and constraint, never the row data the database quotes.
  • ●Migrations 001–064 unchanged. 4020 backend tests + 492 dashboard UI tests passing. 79 MCP tools · 9 supported tools unchanged.
v0.17.0October 14, 2026

Agent Feedback Records · Domain Auto-Join · Self-Hosted Fixes

  • ✓Agent feedback records (GitHub #54). Record whether an agent’s work was useful once a human has reviewed it — routing, summaries, actionability, duplicates and outcomes — each with a verdict and an optional label from a fixed vocabulary. Records attach to an agent run, a ticket or a ticket comment and are append-only. A summary counts them by type, verdict and label, optionally per persona. When a ticket created by an agent is moved from one persona to another, a wrong_route record is captured automatically. Available on Team and Enterprise through the API, three new MCP tools (record_agent_feedback, list_agent_feedback, get_agent_feedback_summary) and sfs agent feedback add|list|summary. New service-key scopes agent_feedback:read and agent_feedback:write.
  • ✓Auto-join by verified email domain (GitHub #59). Organizations can list domains they have verified; when someone verifies an email address on one of those domains and isn’t in an organization yet, they join as a member. Seat limits apply, people who were removed or already invited are never added this way, and nobody becomes an admin automatically. Requires a paid plan with SSO and real email verification. Manage it in the dashboard or with sfs org auto-join list|add|remove.
  • ✓sfs org set-role changes a member’s role by user ID or email.
  • ✓sfs org remove accepts an email, and --bulk <file> removes many members. Every entry is checked against the member list before anyone is removed.
  • ✓Readiness endpoint /health/ready reports whether the database schema is migrated for the running version. The Helm chart’s readiness probe uses it.
  • ✓DLP redaction works on decoded JSON values. Secrets are now also found in JSON field names, in "field": "value" pairs, inside quoted strings and in malformed lines. Content that DLP can’t fully inspect (for example, nested too deeply or with repeated keys) is refused rather than stored as clean. Redaction keeps every other value exactly as it was, including numbers. The public share check uses the same scanning.
  • ✓Helm under ArgoCD runs in order: database first, then migrations, then (in local license mode) the license seed, then the API and MCP servers. Fresh Helm installs create the migration job alongside the database instead of as a pre-install hook.
  • ✓Self-hosted: domain auto-join requires SFS_VERIFICATION_SECRET to be set to a random value of at least 32 characters (the Helm chart sets one). Email verification links must also match the account’s current address.
  • ✓Fixed: DLP redaction could corrupt session files (GitHub #65). Redacting a quoted value could leave unbalanced escaping, so sfs resume and handoff pickup failed. sfs resume now also skips lines already damaged this way and says how many it skipped.
  • ✓Fixed: a NUL byte in session content caused a 500 on push (GitHub #67) and the daemon retried forever. The push is now accepted and the NUL is stripped from the copies stored in the database; the session archive itself is unchanged.
  • ✓Fixed: the Codex watcher skipped sessions containing "info": null (GitHub #64).
  • ✓Fixed: database migrations didn’t run before the API under ArgoCD (GitHub #66). The API also logs a clear error at startup if the database schema doesn’t match the code.
  • ✓Fixed: fresh installs in local license mode never started, because the license check waited on the API it was guarding. The check now runs in cloud license mode only.
  • ✓Fixed: concurrent membership changes. Accepting an invite, creating an org, activating a license, auto-join, and verifying or deleting a domain now take their locks in one consistent order, so they can’t exceed the seat limit, add a user to two orgs or deadlock.
  • ✓Fixed: saving org settings could undo a concurrent change, and saving only KB settings cleared auto-join domains.
  • ✓Member removals are now recorded in the organization audit log.
  • ●Migrations 001–064 (064 agent feedback records), proven on PostgreSQL. 3996 backend tests + 492 dashboard UI tests passing. 79 MCP tools (3 new, up from 76) · 9 supported tools unchanged.
v0.16.1October 12, 2026

Starter Sync Limits · Marketplace Launch Catalog · Hardened Publishing

  • ✓Launch catalog: sessionfs-workflows. An MIT-licensed package written by SessionFS with three instruction-only skills — session-handoff, ticket-workflow and project-knowledge — and no scripts, hooks or MCP servers. Find it under Marketplace in the dashboard, or copy it from catalog/sessionfs-workflows in the repository.
  • ✓Starter gets paid sync limits. Starter customers were held to Free-tier sync limits. Starter now gets the paid per-session cap (300 MB) and per-file cap (50 MB), like every other paid plan.
  • ✓Pro plan storage is 2 GB, matching the pricing page.
  • ✓sfs sync status reports your plan’s storage — Starter 500 MB, Pro 2 GB, Team 1 GB per user, Enterprise unlimited — instead of the per-session upload cap. Free still reports its 50 MB sync limit. Personal Marketplace publishing is charged against the same plan storage.
  • ✓Clearer oversized-session error. It suggested sfs compact, which doesn’t exist; it now points to upgrading for a larger per-session limit.
  • ✓Hardened hosted publishing. Hosted skill packages are capped at 48 MiB unpacked (about 58 MiB compressed), down from 128 MiB; local sfs skill imports keep the 128 MiB limit. Oversized uploads are refused before they are read, callers are authenticated before a publish slot is taken, busy servers answer 503 publish_busy with Retry-After, and uploads that stall or take longer than two minutes get 408 publish_timeout.
  • ✓Organization storage quota for publishing. Organization packages now count against the organization’s storage, together with members’ synced sessions, and every package is limited to 100 stored versions.
  • ✓Safer Git sources. A malicious Git repository can no longer exhaust memory during sfs skill add; the file listing is streamed and Git is stopped as soon as a file-count, depth, size or time limit is exceeded.
  • ●Migrations 001–063 (unchanged). 3839 backend tests + 486 dashboard UI tests passing. 76 MCP tools (unchanged) · 9 supported tools unchanged.
v0.16.0October 11, 2026

Skills & Plugins Across Every Tool · SessionFS Marketplace

  • ✓sfs skill (alias sfs plugin). Inspect, install, update, remove and roll back Agent Skills and Agent Plugins from a local path, a Git repository pinned to an exact commit, or a registered marketplace (including Claude Code marketplace repositories). One command installs for Claude Code, Codex, Gemini CLI, Cursor, GitHub Copilot, Amp, Cline, Kilo Code and Roo Code (legacy, since Roo’s upstream repository was archived). Standard skills are kept byte-for-byte; anything a target tool can’t express is listed as omitted rather than silently dropped.
  • ✓Safe installs. Every install is planned first, checked for path safety and collisions, and applied as one transaction that rolls back cleanly on failure. Files you edited are never overwritten or deleted. sfs skill doctor reports drift and missing files.
  • ✓Reproducible setups. A committed project lockfile plus sfs skill sync --locked reproduces exactly the same packages on another machine or in CI.
  • ✓Explicit consent for risky content. Packages with scripts, hooks or tool grants need a specific flag (--allow-scripts, --allow-hooks, --allow-executable-extensions), bound to that exact package version; --yes alone is refused. Any change to a package’s contents asks again.
  • ✓Multiple marketplaces. Register several named marketplace sources per user or project with sfs skill marketplace add|list|refresh|remove, and search them with sfs skill search. Packages with the same name in two marketplaces never silently replace each other.
  • ✓SessionFS Marketplace (hosted). Browse packages in the dashboard with their source, exact content hash, license, risk flags and a per-tool compatibility view. Every version is immutable and scanned for secrets on upload. Browsing and downloading are open to every signed-in user, including the Free tier.
  • ✓Publishing. Starter and above can publish private personal packages. Team and Enterprise organizations can publish to their organization, and the public catalog is curated. Organization and public packages must declare a license.
  • ✓Team distribution. Team and Enterprise organizations can manage registries, assign exact package versions to the organization, a member or a device, and run an install-approval queue per project. Enterprise organizations can attach policy to assignments.
  • ✓MCP marketplace tools. Agents can search and preview marketplace packages, load an approved instruction-only skill into the current session (always marked as untrusted third-party text), and request an install. They can’t write files, run scripts, enable hooks or start MCP servers; approval happens only in the dashboard or API, and approving your own request requires an emailed confirmation code.
  • ✓Package provenance in captured sessions. Sessions record which skill packages were active, on every tier.
  • ✓Security. Skill package uploads have a dedicated Cloud Armor rate limit. Skill imports enforce per-file, per-package and file-count limits, refuse unsafe paths and symlinks, and never execute imported code while fetching, scanning or planning.
  • ●Migrations 001–063 (063 skills and marketplace), proven on PostgreSQL 16. 3780 backend tests + 486 dashboard UI tests passing. 76 MCP tools (8 new marketplace tools, up from 68) · 9 supported tools unchanged.
v0.15.6October 9, 2026

Site & Docs — Astro 7, Working Tables

  • ✓Tables in the docs render again. Reference tables — CLI flags, environment variables, API fields — were showing as raw pipe-separated text. They now render properly across the docs.
  • ✓No known vulnerabilities in the site build. The site moves to Astro 7 and Starlight 0.42, clearing every npm advisory reported against the build toolchain.
  • ✓Accurate remote-MCP docs. Remote MCP for web-based clients is not currently supported; the docs now say so and point to the local MCP server, which works with every supported tool.
  • ●Site and docs only. No CLI, server, schema or MCP tool changes — migrations 001–062 (unchanged). 3,198 backend + 459 dashboard UI tests passing. 68 MCP tools (unchanged).
v0.15.5October 8, 2026

MCP & Autosync Fixes — Instant MCP Startup, Continuous Sync

  • ✓The local MCP server connects immediately. sfs mcp serve used to rebuild its search index over every session before answering your AI tool. With a few hundred sessions that took over 30 seconds — longer than tools like Claude Code wait — so SessionFS failed to connect. It now answers in well under a second and indexes in the background.
  • ✓Incremental, continuous search indexing. Unchanged sessions are skipped, so keeping the index current takes a fraction of a second after the first start, and sessions captured while the server runs now show up in search within about a minute. Deleted sessions no longer linger in search results, and one malformed session can no longer stop indexing for the rest.
  • ✓Autosync syncs as you work. With autosync on, sessions were only uploaded when the daemon started — so a day’s work waited for the next restart, which then uploaded the whole backlog at once. Sessions are now synced shortly after they are captured, and re-captured sessions keep their sync state so conflicting copies from another device are still detected.
  • ●Local MCP server and daemon only. No hosted server or MCP tool changes — server migrations 001–062 (unchanged); the local search index upgrades itself in place. 3,198 backend + 459 dashboard UI tests passing. 68 MCP tools (unchanged). The first start after upgrading re-indexes once in the background.
v0.15.4October 8, 2026

Container Security — Hardened MCP Server & Isolated Scanning

  • ✓Zero fixable critical or high findings in the remote MCP server image. The same hardening v0.15.3 applied to the API server: packaging tools upgraded past their published advisories, and pip removed from the image.
  • ✓MCP deploys are gated on vulnerabilities. Every remote MCP server deploy now fails if the image has a critical or high finding with a fix available.
  • ✓Scanning is isolated from deploy credentials. In both server pipelines, images are built and scanned in a job with no cloud credentials and no permission to request them, then handed to a separate push job — what ships is exactly what was scanned. Third-party scanning actions are pinned to verified commits.
  • ●Server image only. No CLI, schema or MCP tool behavior changed — migrations 001–062 (unchanged). 3,145 backend + 459 dashboard UI tests passing. 68 MCP tools (unchanged) · 9 supported tools unchanged.
v0.15.3October 8, 2026

Container Security — Hardened Server Image

  • ✓Zero fixable critical or high findings in the server image. Packaging tools are upgraded past their published advisories, and pip is removed from the runtime image entirely — the server never uses it, and its bundled libraries lag the fixed releases.
  • ✓Deploys are gated on vulnerabilities. Every API deploy now fails if the image has a critical or high finding with a fix available. Base-image findings with no published fix stay visible in the scan report.
  • ●Server image only. No CLI, schema or MCP behavior changed — migrations 001–062 (unchanged). 3,145 backend + 459 dashboard UI tests passing. 68 MCP tools (unchanged) · 9 supported tools unchanged. Self-hosted operators building from the published Dockerfile get the same hardening.
v0.15.2October 6, 2026

CLI Fix — Correct Exit Codes Under typer 0.27

  • ✓sfs commands no longer report deliberate exits as crashes. On installs that pulled in typer 0.27 or newer, any command that exits with a specific code printed “Unexpected error: N” and exited with 1 instead of N, and a few commands that finish successfully reported failure. Declining a confirmation prompt also printed an unexpected-error message instead of a clean cancel. Exit codes are now correct on every typer release.
  • ✓Who is affected: anyone scripting sfs or running it in CI (for example sfs agent complete --enforce) on a v0.15.0 or v0.15.1 install that resolved typer 0.27. Upgrading is recommended so scripts and CI jobs see the exit codes they expect.
  • ●CLI-only. No server, schema or MCP behavior changed — migrations 001–062 (unchanged). 3,145 backend + 459 dashboard UI tests passing. 68 MCP tools (unchanged) · 9 supported tools unchanged.
v0.15.1October 5, 2026

Security Patch — MCP Server Git-Remote Validation

  • ✓Fixed an authenticated request-forgery flaw in the MCP server. Project-scoped MCP tools placed the caller-supplied git_remote argument directly into the API request path, so a crafted remote could redirect a request — carrying your own API key — to a different SessionFS API endpoint. Because MCP tool arguments are chosen by the model, prompt-injected content could use this to reach endpoints the MCP surface deliberately does not expose. Git remotes are now validated and safely encoded before use.
  • ✓Who is affected: anyone using the SessionFS MCP server with any release through v0.15.0. Upgrading is recommended for all MCP users. Every legitimate remote form — HTTPS, SSH and scp-style, GitLab subgroups, IPv6 hosts, scheme-less host/owner/repo — continues to resolve exactly as before.
  • ●No other behavior changed. No schema change — migrations 001–062 (unchanged). 3,084 backend + 459 dashboard UI tests passing. 68 MCP tools (unchanged) · 9 supported tools unchanged.
v0.15.0August 12, 2026

The Adoption Release — Instant First Win · Easier Installs · Share Pages · Handoff Previews

  • ✓Feel it in the first minute. sfs init now ends by capturing your most recent AI session on the spot and telling you exactly what to do next — ask your agent “what did we do last session?” Because MCP is installed in the same wizard step, the question works immediately. Safe by construction: init waits read-only when the daemon owns the store, and respects deletions.
  • ✓Friction-free installs. pipx install sessionfs is now the documented default, joined by a Homebrew formula (brew tap sessionfs/tap) and a checksummed one-line curl installer (curl -fsSL https://sessionfs.dev/install.sh | sh). Platforms: macOS and Linux; Windows support is planned for v0.16.
  • ✓Public share pages. A shared session now renders as a clean read-only page at /s/{token} — owner-revocable, optionally password-protected, never cached or indexed, and hard-gated by a DLP scan of exactly the content served (tool inputs included), re-run whenever the session changes.
  • ✓Handoff recipients see what they were handed before signing up. A pre-signup landing page shows the sender, title, and a bounded, redacted preview; creating an account becomes the claim action. Token-gated with hashed-at-rest tokens and constant 404s for every failure mode.
  • ✓Capture-health self-monitoring. The daemon now detects when an AI tool's format change silently breaks capture (it never flags a tool that is merely idle), and surfaces it in sfs doctor (check #9) and a dashboard Settings card.
  • ✓Anonymous activation telemetry, prominently disclosed. Six funnel events carrying only a random install id, version, OS, and event name — never paths, session content, or personal data. Default-on with two independent opt-outs (SFS_NO_TELEMETRY=1 or [telemetry] enabled=false). Self-hosted deployments send telemetry to their own server, not to SessionFS. Full disclosure: Telemetry.
  • ✓SSO ships in the Helm chart (api.sso values block) with a real-IdP smoke-test runbook for self-hosted deployments.
  • ✓Security fix: SessionFS's own API keys are no longer exempt from DLP scanning. Keys with the sk_sfs_ prefix were excluded from every DLP gate by an allowlist entry, so a user's real SessionFS key could pass session-push scans, handoff previews, and share pages unredacted. The exclusion is removed and a dedicated detection pattern added.
  • ✓One story above the fold. The README, site, and quickstart now lead with the core promise: your AI coding agent never starts from zero again. Platform breadth (handoff, governance, SSO, work queues) moves below the fold.
  • ●Migrations 001–062, all proven on PostgreSQL 16. 3,050 backend + 459 dashboard UI tests passing. 68 MCP tools (unchanged) · 9 supported tools unchanged.
v0.14.0July 6, 2026

Resident Agents · SSO Dashboard · SSO Browser Login

  • ✓Resident runner. An operator-hosted, always-on agent process (sfs resident run / sfs resident health) that drives a work queue by calling your own LLM — SessionFS never holds a server-side LLM key. Two kinds: a reviewer that posts trusted verdicts, and an implementer that writes code in an isolated worktree and can only propose — it never merges, never self-approves (items close only on an independent trusted VERIFIED-CLEAN), and never sends code to the server (only branch/SHA/changed-path references). Includes durable server-side resident memory, living-context hydration with fail-closed cost ceilings, a hardened sandbox profile, and an operator guide. Free for the local runner.
  • ✓SSO admin dashboard. The v0.13.x SSO backend gets its UI: OIDC provider configuration (the client secret is handled strictly as a reference, never a pasteable secret), DNS-TXT domain verification, an org-wide enforcement toggle (the owner is never locked out), and owner-only break-glass grants.
  • ✓“Sign in with SSO” in the browser. Organization slug → identity provider → back into the dashboard, logged in. The browser flow never places a raw API key in a URL or stores one at rest: the callback issues a single-use 120-second hashed one-time code, bound to the authenticating browser to defeat login-CSRF, and a dedicated exchange endpoint mints the key.
  • ✓Migrations are now CI-proven on real PostgreSQL 16 — the gate that catches the two failure classes behind the v0.13.1/v0.13.2 fast-follows before they can reach production.
  • ●Migrations 001–058 (057 resident foundation; 058 SSO browser login), proven on PostgreSQL 16. 2,882 backend + 442 dashboard UI tests passing. 68 MCP tools (unchanged) · 9 supported tools unchanged.
v0.13.2June 29, 2026

Patch — SSO Migration No Longer Recreates An Existing Index

  • ✓Second same-day fast-follow on the SSO migration. Migration 056 tried to create a uniqueness constraint on org memberships that has existed since migration 016, failing on PostgreSQL with a duplicate-object error. The migration no longer touches it. Verified end-to-end against a production-accurate PostgreSQL schema (upgrade and downgrade both clean). The deploy gate worked as designed: production stayed cleanly on v0.12.1 with no partial state until this fix shipped.
  • ●Pure migration mechanics — no application or security surface change from v0.13.0.
v0.13.1June 29, 2026

Patch — SSO Migration Fixed For PostgreSQL

  • ✓Same-day fast-follow. Migration 056 dropped a uniqueness key with DROP INDEX, but PostgreSQL materializes it as a real constraint — the drop (and the downgrade's recreate) are now dialect-aware. Verified end-to-end on PostgreSQL (upgrade → downgrade → re-upgrade). No schema-shape change versus what v0.13.0 intended.
  • ●Pure migration mechanics — no application or security surface change from v0.13.0.
v0.13.0June 29, 2026

Organization SSO (OIDC) — Enterprise Single Sign-On

  • ✓OIDC single sign-on (authorization-code + PKCE) against your identity provider — Okta, Entra ID, Google Workspace, and any spec-compliant OIDC IdP. Login attempts are durable and single-use (replay/CSRF safe), and a successful login mints a normal user API key so the CLI and dashboard work unchanged.
  • ✓Anti-takeover account linking. An SSO identity auto-links to an existing account only when the IdP email is verified, exactly matches an already-verified SessionFS account, and that account is already a member of the IdP's org — every other case is safely denied. Identity is keyed on the provider subject, never the mutable email.
  • ✓JIT provisioning. First SSO login from a verified org domain creates the user (member role only), reconciles any pending invite, and respects the org's seat cap.
  • ✓Provider config + domain verification. Owner/admin endpoints to configure an OIDC provider (the client secret is stored only as a reference, never plaintext) and verify org domains via DNS-TXT, with a consumer-email denylist and one-org-per-domain enforcement.
  • ✓Org-wide enforcement + break-glass. Owners/admins can require SSO for their org; service keys (CI / automation) are categorically exempt, the owner is never locked out, and an owner can issue a time-boxed 1-hour break-glass grant for one admin during an IdP outage. Deprovisioning a member revokes their SSO-minted keys in the same transaction.
  • ✓Hardened by design. Strict id_token validation (asymmetric algorithms only, alg:none/HMAC rejected) and an SSRF-guarded fetch layer for all issuer-derived requests, backed by edge rate-limiting on the SSO endpoints.
  • ●Available on all paid tiers. Migrations 055 + 056. New dependency dnspython (DNS-TXT domain verification). 2,669 backend + 397 dashboard UI tests passing. 68 MCP tools (unchanged) · 9 supported tools unchanged.
v0.12.1June 23, 2026

Multi-Repo Project Fixes · Project Rename · Configurable Work-Queue Reviewers

  • ✓sfs project repos no longer crashes. Listing a project's linked repos errored on every project because the CLI mishandled the endpoint's JSON-array response; it now lists them correctly.
  • ✓Projects can now be renamed. The project name was previously fixed at creation, so a consolidated or umbrella project could be stuck showing a stale name. A new PATCH /api/v1/projects/{id} (project-admin only) and sfs project set --name let an admin rename it.
  • ✓Promoting a new primary repo keeps the old one linked. Setting a new primary now demotes the previous primary to a regular linked repo — it stays resolvable — instead of dropping it. Enforced by a one-primary constraint at the model level.
  • ✓Project merge surfaces repo linking. Merging projects already moved the source repos into the target; the dry-run plan now reports how many repos will be linked, and the path is covered end-to-end.
  • ✓The autonomous review loop's reviewer authorization is now configurable. A work-queue reviewer's verdict now flows through the settle path with the server stamping the reviewer identity and trust from the authenticated caller, and org admins can register, list, and revoke trusted reviewers (/api/v1/orgs/{org_id}/trusted-reviewers + sfs admin trusted-reviewers). This lets a dedicated automated reviewer key be authorized to post the verdicts the work queue trusts. Tightly gated and fully audited.
  • ●Additive only — no schema change. Migrations 001–054 (unchanged). 2,525 backend + 397 dashboard UI tests passing. 68 MCP tools (unchanged) · 9 supported tools unchanged.
v0.12.0June 22, 2026

Agent Work Queues — Autonomous, Supervised Ticket-Closing Loops

  • ✓Point an agent at a queue of tickets and let it run. An agent (Claude, Codex, or Gemini) can be aimed at a work queue and woken repeatedly — via /loop, cron, or CI — to service, review, and close tickets using SessionFS as the source of truth, with no human dispatcher. The server holds all of the loop state, so the agent resumes correctly with zero chat memory. Each wake returns one bounded directive at a time; a crash between steps re-emits the same directive rather than losing or repeating work.
  • ✓Three modes. review_until_clean auto-finishes an item only when the server itself re-derives a trusted, literal VERIFIED-CLEAN with no open findings — it never trusts an agent's self-reported outcome. implement_until_done and triage cover the build-it and sort-it cases. Available on all tiers.
  • ✓Built-in safety envelope. A per-item attempt cap with exponential backoff (2m → 5m → 15m → 60m) parks a stuck item for human reset, plus a per-wake ticket cap, a cadence floor, and a dedicated rate-limit class on the step endpoint — load-bearing, since queues are available on every tier.
  • ✓More trustworthy automated review. Review verdicts now require server-verified provenance: a VERIFIED-CLEAN is recognized only when the server itself stamps it from the authenticated identity, never from a value supplied in the request. Human-facing review display is unchanged; the strict gate backs the autonomous stop oracle.
  • ●Backend + MCP (MCP / API only — no dashboard UI or CLI yet). 6 new MCP tools (62 → 68): create_work_queue, get_work_queue, list_work_queues, set_work_queue_status, run_work_queue_step, complete_work_queue_step; 2 new scopes (work_queues:read / work_queues:write). Migrations 001–054. 2,487 backend + 397 dashboard UI tests passing · 9 supported tools unchanged.
v0.11.2June 20, 2026

Self-Service Licensing In The Dashboard — License Activation · Ownership Transfer

  • ✓License activation, now in the dashboard. v0.11.0 shipped the licensing backend; this release adds the web UI for it. A user who isn't yet in an organization can activate a license key from the dashboard with a guided flow: enter the key, confirm the previewed org and tier, submit, then enter the single-use code emailed for verification — landing as the new organization's owner. Email verification is required, and invalid-key and bad/expired-code states are handled inline. Activation creates a new organization from the license.
  • ✓Ownership transfer, now in the dashboard. An organization owner can transfer ownership to an existing admin directly from the members surface. The target sees an Accept / Decline banner, the initiator sees a pending banner with Cancel, and each shows the transfer's expiry. The owner role is shown in the members list and the owner row is protected from demotion or removal. (Distinct from project transfer.)
  • ✓Hardened the profile and activation paths. A brief duplicate organization-membership race no longer fails the profile endpoint or activation: the caller's membership is resolved deterministically, and an already-member caller is rejected cleanly without creating a partial organization.
  • ●Dashboard UI for the v0.11.0 licensing backend, plus backend hardening. No schema change — migrations 001–052 (unchanged). 2,418 backend + 397 dashboard UI tests passing. 62 MCP tools (unchanged) · 9 supported tools unchanged.
v0.11.1June 20, 2026

Patch — Entitlements Migration Hardening

  • ✓Hardened the v0.11.0 entitlements backfill migration. Fixed a diagnostic statement that could fail the upgrade on databases where the backfill recorded notes (such as a plan-tier coercion or an unmatched license). No schema change, and no behavior change for databases already upgraded to v0.11.0.
  • ●Patch release. Migrations 001–052 (unchanged). 2,415 backend + 388 dashboard UI tests passing. 62 MCP tools (unchanged) · 9 supported tools unchanged.
v0.11.0June 20, 2026

Licensing & Organization Management Redesign — Entitlements As The Single Source Of Truth · Self-Service License Activation · Ownership Transfer

  • ✓Entitlements are now the single source of truth for an organization's plan. Plan tier, seats, and storage for an org are resolved from one authoritative record instead of being scattered across user rows and license tables. This fixes the case where enterprise admins could not fully see or manage their own organizations. Existing orgs are backfilled deterministically from their strongest existing signal, so no plan changes for anyone.
  • ✓Self-service license activation with required email verification. An organization admin can activate a license key against their org directly. Activation completes only after the admin confirms a single-use, time-limited code sent to their email address — verification is required, not optional. The activation lookup never reveals whether an arbitrary key exists, and the flow is rate-limited.
  • ✓Organization ownership transfer. A two-step, owner-initiated transfer (initiate → the new owner accepts, with cancel) moves ownership of an org to another member, backed by an explicit owner role. Both parties' standing is re-validated at accept time, and pending transfers expire if not accepted.
  • ✓Last-owner safety guards. An organization can never be left without an owner: deactivating or transferring away the last owner is blocked or safely re-homed, with a full audit trail. The authenticated profile now returns the caller's effective tier, organization, and org role so the dashboard gates org-management surfaces correctly.
  • ●Backend + dashboard. Migrations 001–052 (050 entitlements foundation; 051 ownership-transfer table; 052 role/status integrity constraints). 2,415 backend + 388 dashboard UI tests passing. 62 MCP tools (unchanged) · 9 supported tools unchanged.
v0.10.32June 18, 2026

Hotfix — A Transient Card Decline No Longer Locks Out Paying Customers

  • ✓Transient billing-health statuses are now non-destructive. A temporary payment hiccup — for example a card decline that the payment processor automatically retries over the following days — previously downgraded a paying customer to the free tier and zeroed their seats and storage immediately. Now the customer keeps their tier, seats, and storage for the duration of the retry window, and a successful retry simply restores normal service.
  • ✓Recovery path restored. Only a terminal subscription cancellation downgrades an account to free, which also keeps the recovery path intact so a customer who resolves a transient decline returns to their plan automatically. Three regression tests cover the keeps-access case for both users and organizations plus the full recovery flow.
  • ●Backend only; no schema change. Migrations 001–049 (unchanged). 2,279 backend + 388 dashboard UI tests passing. 62 MCP tools (unchanged).
v0.10.31June 17, 2026

Multi-Repo Projects — One Project, Many Repos · Verified Repo Linking · Project Merge

  • ✓A project can now own more than one git repo. Products that span several repositories — a backend, a frontend, an infra repo — no longer need a separate SessionFS project per repo. Personas, knowledge, tickets, and rules are shared across every repo in the project instead of duplicated and drifting across single-repo projects. Captured sessions from any linked repo roll up to the same project context. Free for all tiers.
  • ✓Verified-ownership repo linking. Link and unlink repos from the CLI with sfs project link-repo / unlink-repo / repos, with ownership verified before a repo joins a project so you can't attach a repo you don't control. The web dashboard adds a Repos tab to view and manage every repo on a project.
  • ✓sfs project merge to consolidate split projects. If you already had separate single-repo projects for the same product, merge them into one — their repos, knowledge, and tickets fold into a single project. Merge is dry-run by default so you preview exactly what moves before anything changes, with a matching Merge surface in the dashboard.
  • ✓Security — dependency hardening. Upgraded to close freshly-published CVEs in starlette, python-multipart, and cryptography on the backend, plus vite and astro on the site.
  • ●Backend + CLI + dashboard. Migration 049 (first past 048). 2,278 backend + 388 dashboard UI tests passing (was 2,179 + 369 at v0.10.30; +99 backend / +19 dashboard). 62 MCP tools (unchanged) · 9 supported tools unchanged · pricing tiers unchanged.
v0.10.30June 14, 2026

Dashboard Redesign — Dark-First UI · Real Transcript View · New Brand Tagline

  • ✓Complete dashboard visual redesign. The web dashboard is rebuilt on a dark-first, design-token UI — consistent color, spacing, and typography tokens across every surface. A new grouped left sidebar makes navigation across sessions, projects, handoffs, and settings faster, and the session list adds list / grid layout toggles so you can scan many sessions at a glance or focus on detail. The dashboard remains a management interface, not a chat app — you interact with your AI tools natively and use the dashboard to review, resume, and share the resulting sessions.
  • ✓Real captured-session transcript view. Session detail now renders the full captured conversation as a readable transcript with tool-aware formatting — file edits, command runs, and search results display as structured blocks instead of raw JSON. The transcript keeps pagination, the oldest/newest order toggle, and the sidechain / empty-message filters from before.
  • ✓New brand tagline: “Memory Layer For AI Agents.” SessionFS is positioned as the memory and coordination layer for AI agents — capture every session, build organizational knowledge, and give your next AI agent everything the last one learned. Tagline and messaging updated across the site and product.
  • ●Frontend + site only — no backend changes, no new database migrations (still 48), no new MCP tools (still 62), no new CLI commands, no auth changes. 2,179 backend + 369 dashboard UI tests passing (dashboard suite grew from 198 to 369 with the redesign). 9 supported tools unchanged.
v0.10.29June 11, 2026

Named Auth Profiles — Multi-Account On One Device · Cloud Dashboard CORS Fix

  • ✓Named auth profiles. Run more than one SessionFS identity on a single machine — for example a personal account and a work org account — without re-authenticating each time. A single identity switch now governs coordination, sync, and the daemon consistently. Precedence is SESSIONFS_API_KEY > SESSIONFS_PROFILE > the active profile > default config, with zero migration for existing single-account users. Each named profile gets an isolated local store and trash list. New CLI: sfs auth login --profile / use / profiles / whoami. The daemon pins the profile it starts with so a mid-run switch can't flip identity underneath it.
  • ✓Cloud dashboard 428 fix on Context / Knowledge Injection settings. Saving rules from the hosted dashboard on a separate domain returned a 428. Root cause: a CORS gap hid the ETag header from cross-origin JavaScript, so the dashboard replayed an empty If-Match. Fix exposes the ETag header on cross-origin reads, plus drift-guard tests.
  • ✓Security hardening. Raw API keys are now written with atomic 0600 permissions; react-router bumped 7.13.2 → 7.17.0.
v0.10.28June 3, 2026

Coordination + Recovery Surface — update_ticket Verb · Admin Key Recovery · Org Invite Resend · /compile Single-Pass

  • ✓New update_ticket verb across MCP, REST, and CLI. Until now every ticket correction had to land in the comment stream — descriptions drifted out of sync with reality and start_ticket loaded stale framing into the compiled persona context. PUT /api/v1/projects/{pid}/tickets/{tid} ships partial update for mutable fields (title, description, priority, acceptance_criteria, context_refs, file_refs, depends_on); status transitions remain FSM-only. Authz: ticket creator OR project admin. Optional lease_epoch for optimistic concurrency (409 + structured envelope on stale; last-write-wins when omitted; lease_epoch alone is rejected with 400 — it's a fence, not a mutation). Every successful update auto-posts ONE system diff comment (no-op writes don't pollute the audit) and per-field rows land in the new ticket_edits audit table. depends_on updates run same-project + cycle + dedup validation before wipe-and-reinsert. New sfs ticket update <id> CLI with flags --title / --description / --description-file / --priority / --acceptance-criteria-file / --lease-epoch.
  • ✓Admin POST /api/v1/admin/users/{user_id}/api-keys — mint-on-behalf for lost-key recovery. SessionFS has no magic-link / OAuth / password-reset flow, so a user who loses their key has no self-service recovery — the chicken-and-egg blocks POST /auth/me/api-keys. This admin-gated endpoint mints a user-kind ApiKey on behalf of any active user; raw key returned exactly once; audit row via _log_action(action="mint_api_key_on_behalf"). Guards: 404 unknown user, 403 inactive user (no backdoor for disabled accounts), 403 non-admin caller. Service keys remain out of scope — they live at /api/v1/orgs/{org_id}/service-keys per v0.10.10.
  • ✓Org invite UPSERT over stale rows. Migration 016 enforces uq_org_invites_org_email on (org_id, email). v0.10.22 added a route-level predicate filtering accepted/declined/expired rows so the admin surface "looked like" a resend was allowed, but the DB constraint blocked the INSERT with IntegrityError → generic 409 duplicate_resource. After expiry or decline, there was no API path to re-invite a member. Fix: both invite_member endpoints lookup ANY existing row with .with_for_update(), classify by state, and route accordingly — no row INSERTs fresh, active row 409s, live member 409s, stale rows UPDATE in place (regenerate id to invalidate stale email links, refresh expires_at, clear accepted_at / declined_at / decline_reason / last_emailed_at, preserve created_at as the audit signal).
  • ✓/compile single-pass auto_generate_concepts. The route invoked auto_generate_concepts() twice on real-compile requests — once before the noop classification (intended for both branches) and again after the real-compile path (flagged "(Legacy) ... for compatibility" in the prior inline comment). Doubled LLM call latency and increased timeout / OOM risk on large projects. Removed the second call; concept_pages counted once and reused as concept_pages_updated in both response paths.
  • ✓Security — PyJWT 2.12 → 2.13. A dependency scan flagged PYSEC-2026-175/177/178/179 against 2.12.x. Pinned PyJWT>=2.13.0,<3.0. Test suite re-run after upgrade: no regressions. cryptography>=46.0.7 already pinned since v0.10.12 (past all GH #16 reported CVE fix-versions) — GH #16 closed as resolved.
  • ●2,153 backend + 198 dashboard UI tests passing (was 2,119 + 198 at v0.10.27; +34 new) · 48 migrations (047 + new 048 ticket_edits — strictly additive, JSON-encoded old/new as Text for SQLite Helm compatibility) · 62 MCP tools (was 61; +update_ticket). 4 PyJWT CVEs fixed by version bump. Issues closed: GH #50 (MCP update_persona / delete_persona parity — shipped v0.10.24, re-confirmed); GH #51 (sfs org create 500 + IntegrityError envelope — shipped v0.10.24); GH #16 (cryptography 43.0.3 CVEs — pinned >=46.0.7 since v0.10.12).
v0.10.27May 30, 2026

Daemon Liveness Hotfix · Tier-Aware Per-File Cap Discovery via /sync/settings

  • ✓Daemon: skip excluded sessions in _sync_sessions() loop. Without this guard, _sync_sessions() retried sessions already in the local exclusion list (deleted.json) every cycle. A large excluded session on a daemon caused hundreds of retries over several days, starving the async event loop on decompression + DLP scanning, timing out /health, and tripping 13 liveness probe kills. Fix: is_excluded(session_id) short-circuits with continue + _pending_sessions.discard(session_id) so the next cycle does not re-queue.
  • ✓Server: max_member_bytes in GET / PUT /api/v1/sync/settings. SyncSettingsResponse gains a tier-aware max_member_bytes: int field sourced from _member_size_limit_for_tier(await get_effective_tier(user, db)), tracking the same enforcement as _check_member_sizes and honoring SFS_MAX_SYNC_MEMBER_BYTES_{FREE,PAID} operator overrides. Purely additive response shape; no migration. Older CLIs ignore the new field harmlessly.
  • ✓CLI: tier-aware per-file cap discovery via _resolve_max_member_size(). Explicit precedence: SFS_MAX_SYNC_MEMBER_BYTES_PAID env-var override > server-supplied max_member_bytes from GET /sync/settings > 50 MB literal fallback (logged warning). Cached per CLI invocation — one /sync/settings call per sfs push / sfs pull / sfs sync / sfs handoff / sfs pull-handoff run. sfs sync upload path now also runs the resolver-aware oversized-member preflight (parity with sfs push + sfs handoff — no wasted upload of a guaranteed-413 archive).
  • ●2,119 backend + 198 dashboard UI tests passing (was 2,108 + 198 at v0.10.26; +11 net new across the three tickets) · 47 migrations (no new) · 61 MCP tools (unchanged).
v0.10.26May 30, 2026

Hotfix — Catch typer 0.26 BadParameter Class Hierarchy Regression

  • ✓handle_errors now catches both click.exceptions.ClickException AND typer._click.exceptions.ClickException. v0.10.25's click<8.4 pin installed click 8.3.3 in CI but tests still failed: typer 0.26.3 vendors its own click module (typer._click), so typer.BadParameter is typer._click.exceptions.BadParameter, NOT click.exceptions.ClickException. handle_errors builds a tuple of base classes at import time — best-effort tries from typer._click import exceptions and adds typer's ClickException / Exit / Abort classes to the tuples used in except clauses. Falls back to standard click on typer <0.26. Click pin removed.
  • ●2,108 backend + 198 dashboard UI tests passing (unchanged from v0.10.24/25) · 47 migrations (no new) · 61 MCP tools (unchanged). Same root cause as a fix in v0.10.11, resurfaced by typer's 0.26 refactor.
v0.10.25May 30, 2026

Hotfix — Pin click<8.4 So Deploy API Succeeds

  • ✓Pin click<8.4 in pyproject.toml so CI installs the version handle_errors was tested against. v0.10.24 tagged and Release / MCP / Container-Images pipelines all succeeded, but CI + Deploy API failed on 2 CLI tests that assert click BadParameter-derived exit code and output format. Tests passed on local .venv (click 8.3.1) but CI installed click 8.4.1 which changed both the exit-code and the class hierarchy that handle_errors passes through. Affected sfs org create and other CLI flows that reached the generic "Unexpected error" branch.
  • ●2,108 backend + 198 dashboard UI tests passing (unchanged from v0.10.24) · 47 migrations · 61 MCP tools (unchanged). Lifting the pin + making the 2 brittle CLI tests resilient to click BadParameter exit-code / output-format drift tracked as follow-up — superseded by v0.10.26's broader typer 0.26 catch.
v0.10.24May 30, 2026

Issue / Task Rollup · GH #50 + #51 Enterprise Unblocks · Structured IntegrityError Envelope · Dashboard Surface

  • ✓Issue / Task rollup. Migration 047 strictly additive: tickets.kind (issue | task) + parent_ticket_id + composite index (project_id, parent_ticket_id). One user-reported problem can now roll up multiple executor workstreams. Forked FSM: Tasks keep the executor lifecycle (suggested → open → in_progress → blocked → review → done); Issues use a simpler manager FSM (open → in_progress → closed + cancelled escape). New POST /tickets/{tid}/close Issue terminator (rejects Tasks). Actor-based authorization via new user_is_project_admin(db, user_id, project) helper — replaces a user-input-trusting hardcoded-assignee branch. Compiled persona context emits Kind:, Parent Issue:, and child Task list. Dashboard TicketsTab gains kind filters, indigo edge bar, kind badge, in-place Children navigation, Back-to-parent breadcrumb on Tasks, and Close Issue button.
  • ✓MCP update_persona + delete_persona tools (GH #50). MCP tool count 59 → 61. update_persona wraps PUT /personas/{name} (only fields the caller passed are sent — no silent overwrite); delete_persona wraps DELETE /personas/{name}?force=... with strict force-parsing (literal True OR case-insensitive {"true", "1", "yes"} only; closes bool("false") == True Python footgun). 409 surfaces the structured envelope so callers can route on error.code + error.details.open_ticket_count to decide whether to retry with force=true.
  • ✓sfs org create 500 for manual-license enterprise customers (GH #51). One-line await db.flush() between db.add(org) and db.add(member). Latent since v0.10.0 because SQLAlchemy's unit-of-work doesn't reliably topologically sort two pending INSERTs by FK dependency without an intervening flush. For users with billing fields the implicit autoflush triggered by update(User) masked the bug; users with no billing fields (every manual-license enterprise account) hit the FK violation → 500.
  • ✓Generic IntegrityError envelope + structured 5xx hardening (GH #51). New FastAPI exception handler for sqlalchemy.exc.IntegrityError classifies by PostgreSQL SQLSTATE first, falls back to SQLite message string-matching. Unique violation → 409 duplicate_resource; FK violation → 500 foreign_key_violation; NOT NULL → 422 missing_required_field; CHECK → 422 check_constraint_violation. Handler logs raw DBAPI text at ERROR; client envelope strips column names and row values to avoid PII leak. New CLI helper format_api_error(body, status). Dashboard ApiError class extracts code + details + clean message.
  • ✓Session rename. Click-to-edit title + alias inline on session detail view, side-by-side. New MCP tool rename_session(session_id, new_title?, new_alias?) — empty new_alias="" routes through DELETE /alias and captures canonical id from the DELETE response so a caller passing the about-to-be-cleared alias as the identifier doesn't 404 on the follow-up.
  • ✓Rules page max-tokens UX + CORS If-Match preflight unblock. Dashboard DebouncedTokenInput gains max={20000} + n > 20000 debounce gate. Server knowledge_max_tokens + context_max_tokens over-cap branches raise structured envelope code: 'max_tokens_exceeded'. CORSMiddleware.allow_headers extended with If-Match + If-None-Match so dashboard ETag-protected writes (PUT /rules) actually fire.
  • ●2,108 backend + 198 dashboard UI tests passing (was 2,023 + 187 at v0.10.23; +85 backend / +11 dashboard) · 47 migrations (migration 047 strictly additive) · 61 MCP tools (was 58; +rename_session + update_persona + delete_persona). pip-audit 0 vulnerabilities; npm audit (dashboard + site) 0 vulnerabilities.
v0.10.23May 26, 2026

Opt-In entity_ref Upsert on KB Writes · ChatGPT / Claude.ai MCP Tool-Discovery Hotfix · Persona Case-Insensitive Lookup

  • ✓Opt-in entity_ref upsert on /entries/add. New optional upsert: bool = False field on AddEntryRequest. When True AND entity_ref is provided, the route looks up the prior active entry by (project_id, entity_ref, dismissed_at IS NULL, superseded_by_id IS NULL) and supersedes-in-place rather than running the similarity-based dedup that returns 409 on cache rotations. Fixes a silent data-loss path on cache rotations, where a changed entry body could hit a duplicate 409 instead of superseding the prior entry. Existing callers unaffected — feature is fully opt-in. Cloud agent dispatchers (bedrock-action-group.yaml, vertex_tools.py) updated to expose upsert in their tool schemas.
  • ✓ChatGPT / Claude.ai MCP tool-discovery hotfix. New _ConsumedResponse(Response) sentinel in mcp/remote_server.py so handle_mcp returns a no-op ASGI callable after transport.handle_request() consumes the underlying response. Starlette 1.x's Route wrapper rejects None with TypeError: 'NoneType' object is not callable, which truncated the wire response and caused remote MCP clients to see "no tools" after OAuth connect. Auth ordering preserved — Bearer-token validate still runs before transport delegation. 4 new unit tests covering the sentinel shape, ASGI no-op semantics, and auth gate ordering.
  • ✓Persona name case-insensitive lookup + normalize-at-write. New _resolve_persona_name(db, project_id, name) helper in routes/tickets.py using func.lower(AgentPersona.name) == name.lower() with .order_by(id).first() as a deterministic tiebreaker. start_ticket, create_ticket, and update_ticket PUT all normalize assigned_to through it; unknown names pass through as free-text for back-compat. list_tickets filter switched to func.lower(Ticket.assigned_to) == assigned_to.lower() so discovery surfaces legacy rows stored with different casing when an agent queries a lowercase name. create_persona rejects case-insensitive duplicates with 409. Closes the v0.10.18 onboarding pain where start_ticket 400'd because a persona file capitalizes the persona name but agent_personas.name stores it lowercase. 6 regression tests.
  • ●2,023 backend + 187 dashboard UI tests passing (was 2,007 + 187 at v0.10.22; +16 net new across the bundled tickets) · 46 migrations (no new migrations) · 58 MCP tools (unchanged). pip-audit 0 vulnerabilities; npm audit (dashboard + site) 0 vulnerabilities.
v0.10.22May 24, 2026

Org-Member Project Access Fix · Invite Email + Decline / Resend / /invites Page

  • ✓Org-member project access enforced everywhere. The v0.10.0 multi-org design said org members could reach personas / KB / wiki / tickets / agent-runs on org-scoped projects without owning the project row — the model carried the intent, but no route ever enforced it. v0.10.22 ships a shared auth.project_access.user_can_access_project(db, user_id, project) helper and threads it through every project-scoped route group. New org members no longer have to clone the repo locally as a workaround just to read the project's persona doc or KB — their org-membership row is now the authoritative grant. Deny-by-default; helper is the single chokepoint, so a new project-scoped surface that forgets it fails closed.
  • ✓Org invite email + decline + resend + dashboard /invites page. Both invite endpoints (legacy POST /api/v1/org/invite and multi-org POST /api/v1/orgs/{org_id}/members/invite) now dispatch a notification email through the configured provider via services/invite_helpers.dispatch_invite_email. Best-effort — the invite is still created if delivery fails, and the route never returns 500 on an email backend hiccup. New endpoints: POST /api/v1/orgs/{org_id}/invites/{invite_id}/resend (admin only), POST /api/v1/org/invite/{invite_id}/decline (recipient with optional decline_reason), and GET /api/v1/org/invites/me (caller's pending-invite list). Accept and decline both use atomic rowcount-1 guarded FSM transitions so two tabs can't both win. New dashboard /invites page with a top-nav pending-count badge. Migration 046 is strictly-additive: org_invites gains nullable declined_at, decline_reason, and last_emailed_at columns.
  • ✓Reference n8n source adapters. Ready-made normalizers for Hacker News, GitHub Releases, Reddit and RSS feeds (docs/integrations/n8n-source-adapters/) turn each source into one common signal shape (the Reddit adapter also strips user identifiers), so an external agent workflow can feed findings into a project's knowledge base.
  • ●2,007 backend + 187 dashboard UI tests passing (was 1,985 + 187 at v0.10.21; +22 net new across the three tickets) · 46 migrations (migration 046 strictly-additive) · 58 MCP tools (unchanged). pip-audit 0 vulnerabilities; npm audit (dashboard + site) 0 vulnerabilities.
v0.10.21May 22, 2026

KB Attribution + AgentRun Provenance + agent_runs:read Now Live

  • ✓persona_name + author_class on KnowledgeEntry. Migration 045 strictly additive: nullable persona_name VARCHAR(64) validated against agent_personas on write (unknown personas → 422, mirrors v0.10.7 wiki page-write policy); author_class VARCHAR(16) NOT NULL DEFAULT 'human' backfills every existing row to 'human'; new composite index idx_knowledge_persona_recent (project_id, persona_name, created_at DESC) for per-persona retrieval. Anti-spoof invariant: POST /entries/add forces author_class = 'agent' whenever auth.actor_type == 'service_key', regardless of request body. GET /entries gains three AND-composed filters: persona_name, author_class, and source_filter (literal substring via .contains(value, autoescape=True) — % and _ in user input escaped so they match as data, not LIKE wildcards. Both new fields surface on every read response.
  • ✓MCP add_knowledge persona attribution. Input schema gains persona_name (max 64) and author_class (human|agent). When persona_name is omitted AND the active-ticket bundle's project_id matches the resolved project (mirroring update_wiki_page v0.10.7), the handler auto-threads the bundle's persona AND defaults author_class to 'agent' — without this default the bundle path would land rows as human and miss the agent retrieval channel. Explicit args win in both directions. The tool response surfaces the attribution that actually landed so callers can detect server-side overrides (anti-spoof).
  • ✓AgentRunResponse exposes service-key audit triple. actor_type, service_key_id, and service_key_name (v0.10.10 migration 042 columns) now serialize on every AgentRun read path (create, list, get, start, complete, cancel) via the shared _row_to_response helper. No DB migration.
  • ✓agent_runs:read service-key opt-in. GET /agent-runs and GET /agent-runs/{run_id} converted from get_current_user to require_scope("agent_runs:read"), routed through _get_project_for_auth + assert_service_key_can_access_project. Write routes untouched — agent_runs:write remains required for create + complete; /start and /cancel stay user-key only by design. The scope catalog in docs/api-keys.md flips agent_runs:read from "reserved" to "✅ live".
  • ✓Security — starlette>=1.0.1 pin to close GHSA-86qp-5c8j-p5mr (host-header URL reconstruction → potential auth bypass). FastAPI 0.135+ requires only starlette>=0.46.0, so the floor pin prevents the resolver from landing on a vulnerable transitive version on the next deploy.
  • ●1,985 backend + 187 dashboard UI tests passing (was 1,959 + 187 at v0.10.20; +26 net new across the five tickets) · 45 migrations (migration 045 strictly-additive) · 58 MCP tools (unchanged). pip-audit 0 vulnerabilities (after starlette pin); npm audit (dashboard + site) 0 vulnerabilities.
v0.10.20May 22, 2026

Persona Routes Opt Into Service-Key Auth — personas:read + personas:write Now Live

  • ✓Service-key opt-in for persona CRUD routes. Unblocks autonomous agents that need to fetch the actual persona doc as their system prompt at runtime. Mirrors the v0.10.18 + v0.10.19 conversion pattern exactly: require_scope(...) on opted-in routes, deny-by-default on the rest.
  • ✓READ routes converted to require_scope('personas:read'): GET /projects/{pid}/personas (list) and GET /projects/{pid}/personas/{name} (detail).
  • ✓WRITE routes converted to require_scope('personas:write'): POST /projects/{pid}/personas (create), PUT /projects/{pid}/personas/{name} (update), and DELETE /projects/{pid}/personas/{name} (soft-delete via is_active=False).
  • ✓Tier C MCP tools intentionally NOT touched. assume_persona and forget_persona mutate the caller's local provenance bundle (not a project row) and remain user-key only.
  • ✓Migration 044 — AgentPersona audit-row columns. Strictly-additive migration mirroring 042 + 043 exactly: agent_personas gains nullable actor_type / service_key_id / service_key_name columns (no defaults, no constraints, no indexes, no FKs). create / update / delete persona writes stamp the triple from AuthContext so audit rows record the service-key principal.
  • ✓Cross-route helper reuse. Persona routes import _get_project_for_auth from knowledge.py (mirrors wiki._get_project_or_404 cross-route import). Service keys load the project by id only; user keys keep the legacy owner / session gate. assert_service_key_can_access_project enforces the org + allowlist boundary AFTER the helper returns at all 5 sites.
  • ✓docs/api-keys.md updated — personas:read + personas:write moved from "reserved" to "✅ live" with full endpoint lists. Scope-vocabulary count corrected from 14 → 15. Knowledge-intake example extended to include the runtime persona-load step. New explicit note that assume_persona / forget_persona remain user-key only.
  • ●1,959 backend + 187 dashboard UI tests passing (was 1,952 + 187 at v0.10.19; +7 net new in tests/server/integration/test_scoped_service_keys.py) · 44 migrations (migration 044 strictly-additive) · 58 MCP tools (unchanged) · no new endpoints.
v0.10.19May 21, 2026

Ticket CREATE + Knowledge Routes Opt Into Service-Key Auth — knowledge:read + knowledge:write Now Live

  • ✓Service-key opt-in for ticket CREATE + the knowledge surface. Unblocks autonomous discovery / research agents that gather external signals and write them back as KB entries + new tickets. Mirrors the v0.10.18 ticket-route conversion exactly: require_scope(...) on opted-in routes, deny-by-default on the rest.
  • ✓WRITE route converted to require_scope('tickets:write'): POST /projects/{pid}/tickets — the entrypoint an autonomous agent uses to open follow-up tickets from a discovered signal.
  • ✓READ routes converted to require_scope('knowledge:read'): GET /projects/{pid}/entries (list/search) and GET /projects/{pid}/entries/{eid} (detail).
  • ✓WRITE routes converted to require_scope('knowledge:write'): POST /entries/add, PUT /entries/{eid} (dismiss/update), PUT /entries/{eid}/refresh, PUT /entries/{eid}/promote, PUT /entries/{eid}/supersede.
  • ✓knowledge:read is truly read-only for service keys. The GET /entries search side-effect path previously mutated used_in_answer_count / last_relevant_at / retrieved_count whenever the search matched — a service key with only knowledge:read could therefore alter freshness/decay state. v0.10.19 gates the telemetry UPDATE on key_kind == 'service' AND 'knowledge:write' not in scopes. User keys are unaffected.
  • ✓New _get_project_for_auth helper. Service keys minted by an org admin against a project owned by a different org member previously 403'd on the legacy user-owner / captured-session gate before the org/allowlist boundary could evaluate. The helper now branches on auth.key_kind: service keys load by id (404 only); user keys keep the legacy gate. assert_service_key_can_access_project still enforces the org+allowlist boundary AFTER the helper returns.
  • ✓Migration 043 — Ticket audit-row columns. Strictly-additive migration mirroring 042 exactly: tickets gains nullable actor_type / service_key_id / service_key_name columns (no defaults, no constraints, no indexes). create_ticket and every KnowledgeEntry mutator (dismiss/update/refresh/promote/supersede) stamp the triple from AuthContext so audit rows record the service-key principal on every write path.
  • ✓Tier C routes deliberately NOT opted in. /compile, /rebuild, /dismiss-stale, /health, and /compilations remain user-key only. A regression test (test_service_key_still_denied_on_compile_rebuild_dismiss_stale) pins the deny-by-default contract so any future change must opt them in explicitly.
  • ✓docs/api-keys.md updated — knowledge:read + knowledge:write moved from the "reserved" list to "✅ live" with full endpoint lists. New knowledge-intake example added (search_project_knowledge → add_knowledge → create_ticket curl flow). Audit-row narrative updated to include Ticket alongside the other 5 audit tables.
  • ●1,952 backend + 187 dashboard UI tests passing (was 1,942 + 186 at v0.10.18; +10 + 1 net new) · 43 migrations (migration 043 strictly-additive) · 58 MCP tools (unchanged) · no new endpoints.
v0.10.18May 21, 2026

Ticket Routes Opt Into Service-Key Auth — tickets:read + tickets:write Now Live

  • ✓Route opt-in for the ticket surface. Unblocks the n8n triage agent build and every future CI / cloud-agent integration that touches tickets. Mirrors the v0.10.10 agent_runs + handoffs conversion pattern exactly.
  • ✓READ routes converted to require_scope('tickets:read'): GET /projects/{pid}/tickets, GET /projects/{pid}/tickets/{tid}, GET /projects/{pid}/tickets/{tid}/comments, GET /projects/{pid}/tickets/{tid}/review-state.
  • ✓WRITE routes converted to require_scope('tickets:write'): POST /projects/{pid}/tickets/{tid}/comments, POST /projects/{pid}/tickets/{tid}/start, POST /projects/{pid}/tickets/{tid}/complete.
  • ✓Cross-org safety — every converted route runs assert_service_key_can_access_project(db, auth, project) before any DB work. TicketComment audit rows now populate actor_type, service_key_id, and service_key_name from AuthContext so service keys never silently impersonate humans. Lease-epoch fencing on complete / comment preserved.
  • ✓Out of scope this release — resolve / escalate / approve / dismiss / create stay on the legacy get_current_user dependency and continue to reject service keys with service_key_not_allowed. User keys continue to work on all converted routes.
  • ✓docs/api-keys.md updated — tickets:read + tickets:write moved from the "reserved" rows to "✅ live" with the exact endpoint lists. New triage-bot persona example added to the "When to mint one" section.
  • ●1,942 backend + 186 dashboard UI tests passing (was 1,935 + 186 at v0.10.17; +7 net new) · 42 migrations (no new) · 58 MCP tools (unchanged) · no new endpoints · no schema changes. Backend test additions live in tests/server/integration/test_scoped_service_keys.py covering ticket scope acceptance + cross-org denial + insufficient-scope rejection + audit-row provenance.
v0.10.17May 21, 2026

Knowledge Health Fix — pending_entries Now Matches the Compile Pipeline + Two New Counters

  • ✓pending_entries overcount + counterpart undercount fixed. The count used to be a generalized "uncompiled non-dismissed claims" — it counted superseded / stale claims that compile skips, and missed auto-promotable evidence that compile processes. Operators got "Run compile to process N pending entries" advice that lied in both directions: stayed flat after a successful compile, and reported 0 for projects with only eligible evidence. pending_entries now mirrors the compile pipeline's Phase 2b select EXACTLY: claim_class='claim' AND compiled_at IS NULL AND not dismissed AND freshness_class IN ('current', 'aging') AND superseded_by IS NULL.
  • ✓New auto_promotable_evidence field. Surfaces evidence rows the compiler's Phase 2a will auto-promote AND that survive Phase 2b post-promotion: claim_class='evidence' AND confidence >= 0.5 AND length(content) >= 30 AND not dismissed AND compiled_at IS NULL AND current/aging AND no superseder.
  • ✓New uncompiled_notes field. Counts notes that need bulk_promote first — notes do not auto-promote on compile.
  • ✓potentially_stale flag honest again. Now uses the same compile-eligible predicate as pending_entries — notes / superseded claims with novel terms can no longer drive a false-positive "Context may be stale" warning.
  • ✓Smarter "run compile" recommendation. Drives off compile_work_total = pending_entries + auto_promotable_evidence. Structured breakdown below threshold ("3 pending claims + 1 auto-promotable evidence row — run compile"); single-line above threshold ("Run compile to process N entries (P pending claims, Q auto-promotable evidence)"). "No compilations yet" recommendation now gated on compile_work_total > 0; notes-only fresh projects get "No compile-eligible entries yet — add claims (or promote evidence) before running compile". New "N uncompiled notes — call bulk_promote..." recommendation fires whenever notes exist.
  • ✓MCP get_knowledge_health description rewritten. Documents each field with its exact filter clause so MCP agents read the same contract the route enforces. Schema unchanged.
  • ✓5 new regression tests pin each surface — test_health_pending_entries_matches_compile_filter, test_health_counts_auto_promotable_evidence, test_health_potentially_stale_ignores_notes_and_superseded, test_health_auto_promotable_excludes_stale_and_superseded_evidence, test_health_no_compile_advice_when_only_notes_exist. Toggle-tested: temporarily disabling the new filter clauses reproduces the original overcount shape and the regression test fails with the exact pre-fix count.
  • ●1,935 backend + 186 dashboard UI tests passing (was 1,930 + 186 at v0.10.16) · 42 migrations (no new) · 58 MCP tools (description rewritten, schema unchanged). Backward-compatible additive API change — uncompiled_notes and auto_promotable_evidence are optional on the dashboard ProjectHealthResponse interface so older self-hosted servers still parse.
v0.10.16May 20, 2026

Hotfix: Flush DELETEs Before INSERTs in auto_generate_concepts + Route Rollback

  • ✓Second uq_kl_link violation site closed. After v0.10.15 deployed, a second uq_kl_link violation was found at compiler.py:1379 (auto_generate_concepts.db.commit()). The existing-page branch was deleting prefetched (entry → page) links and immediately re-adding them for the same composite key. SQLAlchemy UnitOfWork orders INSERTs ahead of DELETEs for the same table by default, so the INSERT fired while the old row was still present.
  • ✓Explicit await db.flush() after the delete loop at src/sessionfs/server/services/compiler.py:1346. Serialises DELETEs to the open transaction (still rollbackable) so the INSERTs that follow see a clean slate.
  • ✓Belt-and-suspenders await db.rollback() in the /compile route's exception handler at routes/knowledge.py:976-987. Any future commit failure in auto_generate_concepts no longer poisons the session for downstream _count_pages calls.
  • ✓New regression test test_auto_generate_concepts_flushes_delete_before_insert seeds a concept page + entry-link for the same pair, monkeypatches check_concept_candidates + generate_concept_article to force the regenerate branch, asserts exactly one (entry, page) link survives the delete+re-add cycle. Toggle-tested.
  • ●1,930 backend + 186 dashboard UI tests passing (was 1,929 + 186 at v0.10.15) · 42 migrations (no new) · 58 MCP tools.
v0.10.15May 20, 2026

Hotfix: _auto_supersede Idempotency on uq_kl_link

  • ✓Fixes the actual /compile 500. The v0.10.14 fix closed an audited crash class but turned out not to be the root cause. _auto_supersede was creating KnowledgeLink rows with link_type='contradicts' on every compile pass without checking whether the same (source_id, target_id) pair already existed. The supersedes path was self-gating via older.superseded_by; contradicts had no such gate.
  • ✓Prefetch + skip dedup pattern. Prefetch existing entry→entry link (source_id, target_id) pairs ONCE at the top of _auto_supersede; skip db.add(link) when present. In-run adds join the set so two contradictions in the same pass can't double-add. Pair-only dedup (not link_type-aware) matches the DB constraint exactly.
  • ✓ORM model parity with migration 019. Added UniqueConstraint("project_id", "source_type", "source_id", "target_type", "target_id", name="uq_kl_link") to KnowledgeLink.__table_args__ so SQLite test schemas built via Base.metadata.create_all enforce the same uniqueness as production PostgreSQL.
  • ✓2 new regression tests: test_auto_supersede_idempotent_on_existing_contradicts_link (monkeypatches word_overlap to 0.75 to force the contradicts branch, asserts flat link count across 2 passes) and test_auto_supersede_skips_when_other_link_type_already_exists (mixed-link-type edge — existing 'related' link prevents adding 'contradicts' for the same pair).
  • ●1,929 backend + 186 dashboard UI tests passing (was 1,927 + 186 at v0.10.14) · 42 migrations (no new) · 58 MCP tools.
v0.10.14May 20, 2026

Hotfix: Defensive int(lk.source_id) Guard at 4 Sites + _safe_entry_link_ids Helper

  • ✓Fixes /compile 500 on projects with malformed KnowledgeLink source IDs. _prune_dead_concept_pages did int(lk.source_id) without a try/except when source_type=='entry'. KnowledgeLink.source_id is a String(64) column; convention is str(KnowledgeEntry.id) but legacy rows can have non-numeric values (slugs, UUIDs, malformed migration data). A single bad row crashed the request.
  • ✓Extracted _safe_entry_link_ids(links, *, page_slug=None) -> list[int] helper at module scope. All 3 compiler sites now route through it: _prune_dead_concept_pages, dismissed-id prepass, per-existing-page deletion check. Malformed rows skip with a warning log instead of crashing.
  • ✓Fourth unguarded site also fixed. One outside compiler.py at src/sessionfs/server/routes/wiki.py:602 (page regenerate route) had the same crash class. Fixed inline with the matching try/except (TypeError, ValueError) + logger.warning pattern. Final grep confirms only the helper's own (guarded) cast remains.
  • ✓2 new regression tests: test_prune_dead_concept_pages_skips_malformed_links (direct call path) and test_auto_generate_concepts_existing_page_skips_malformed_links (monkeypatches check_concept_candidates to force the existing-page branch).
  • ●1,927 backend + 186 dashboard UI tests passing (was 1,925 + 186 at v0.10.13) · 42 migrations (no new) · 58 MCP tools. v0.10.13's fail-closed contract isolated this to "availability only" — zero DB drift on each failed /compile.
v0.10.13May 20, 2026

Safety Release — Fail-Closed /rebuild + Admin Restore Endpoint

  • ✓Fail-closed /rebuild and /compile. A /rebuild could wipe project.context_document and every active claim's compiled_at when the recompile crashed mid-flight. compile_project_context previously committed twice inside the function and the /rebuild route added two more commits BEFORE calling it for destructive resets — four transaction boundaries through one logical compile, with destructive resets in the first two. Any crash after them was data-destructive.
  • ✓Refactored to single atomic commit — Phase 1 reads only (SELECT FOR UPDATE + pending claims + active claims + sessions for persona lookup), Phase 2 compute in memory during LLM call (no DB writes), Phase 3 single atomic transaction (freshness / decay / retention / auto-promote + destructive reset if force_rebuild=True + projection update + entries marked compiled + ContextCompilation INSERT + section pages + ONE commit). Any exception before the final commit rolls back everything.
  • ✓Related correctness bug fixed — force_rebuild=True was nulling compiled_at but the compile still read context_before = project.context_document and merged new claims INTO that stale text, so dismissed/superseded/aging entries' content could survive a "rebuild" forever. Fix: split previous_context (audit trail) from compile_base_context (becomes "" on force_rebuild so the merge starts from scratch).
  • ✓Admin repair endpoint — POST /api/v1/admin/projects/{project_id}/restore-from-compilation recovers a project's context_document AND compiled_at metadata from a chosen ContextCompilation snapshot. Body {compilation_id: int, dry_run: bool = true}. Single atomic transaction. Admin-gated; audit-logged via AdminAction. Strict isinstance(x, bool) validation avoids Python's bool-is-int trap.
  • ✓Headline regression test test_rebuild_rollback_on_compile_crash_preserves_prior_state. Plus 9 integration tests for the admin restore endpoint covering dry-run/apply, 404 paths, non-admin 401/403, and strict bool validation.
  • ●1,925 backend + 186 dashboard UI tests passing (was 1,912 + 186 at v0.10.12) · 42 migrations (no new) · 58 MCP tools.
v0.10.12May 19, 2026

Bulk-Promote Eligible KB Notes — Practical Repair Surface

  • ✓Bulk-promote eligible KB notes to claims. The v0.10.10 confidence-clamp bug left many production KBs with hundreds of stuck note-class entries; per-entry repair (PUT /entries/{id}/confidence + PUT /entries/{id}/promote × N) doesn't scale past ~5 entries. New POST /api/v1/projects/{pid}/entries/bulk-promote + promote_eligible_entries MCP tool (57 → 58 tools) + sfs project promote-eligible --confirm CLI. Eligibility: class=note, not dismissed, not superseded, ≥ min length, ≥ min confidence (unless caller overrides via set_confidence), no near-duplicate against active claims. Dry-run is the default.
  • ✓Per-ticket review-state endpoint row cap — GET /tickets/{tid}/review-state now caps TicketComment fetch at 500 rows (parity with list_ticket_comments). ORDER BY (created_at, id) ASC ensures the earliest review rounds always survive the cap on long threads.
  • ✓Dry-run/confirm asymmetry fixed on bulk-promote: the original implementation only appended eligible content to the compare set inside if not dry_run, so a dry-run with two near-duplicate notes reported both as promoted while the confirmed run correctly skipped one. Fix: append unconditionally; only the field writes stay gated. The "inspect-then-mutate" safety contract now holds. Also lowered boundary defaults from 0.85 → 0.80 to match the single-entry gate.
  • ●1,912 backend + 186 dashboard UI tests passing (was 1,871 + 186 at v0.10.11) · 42 migrations (no new) · 57 → 58 MCP tools (promote_eligible_entries). Dependency security updates closed 9 HIGH/MEDIUM CVEs: cryptography 46.0.5→48.0.0, idna 3.11→3.15, mako 1.3.10→1.3.12, pygments 2.19.2→2.20.0, urllib3 2.6.3→2.7.0, pip 26.0→26.1.1.
v0.10.11May 19, 2026

Service-Keys CLI + Per-Ticket Review-State Derivation + DNS-Error Catch

  • ✓CLI for v0.10.10 scoped service keys — closes the human-facing surface gap so issuing, listing, rotating, and revoking keys no longer requires curl. sfs admin service-keys list|create|revoke|rotate|scopes (org admin + Team+ tier for mutations) + sfs auth keys list|create|revoke (personal-key surface). --output-key flag on create + rotate emits ONLY the raw key for CI capture. Local scope validation rejects unknown scopes + * wildcard before any network call.
  • ✓docs/api-keys.md — public reference covering both kinds of keys, the 14-scope vocabulary with current opt-in status (only handoffs:write and agent_runs:write live today; 12 remaining scopes reserved for future route opt-in), service-key lifecycle, cloud-agent integration recipes, rotation policy, structured error code reference.
  • ✓Per-ticket review-state derivation — compact summary of open findings, closed findings, last verdict, and severity counts for long review threads. No new schema. New GET /tickets/{tid}/review-state + get_ticket_review_state MCP tool (56 → 57) + sfs ticket review-state CLI. Closure rule: findings raised in round N close when any subsequent round has VERIFIED-CLEAN verdict.
  • ✓DNS-error catch on sfs ticket watch — the shared _api_request helper now catches httpx.RequestError (parent of ConnectError, TimeoutException, NetworkError, SSL errors — but NOT HTTPStatusError) and exits 1 with an actionable Configure cloud auth first: sfs auth login message instead of a raw Python traceback. Every CLI command using _api_request benefits.
  • ✓Shared _api_request bug fixes — DELETE method now forwards json_data via client.request("DELETE", ..., json=...) (httpx's client.delete() doesn't accept json=); 204 No Content responses no longer crash resp.json().
  • ●1,871 backend + 186 dashboard UI tests passing (was 1,819 + 186 at v0.10.10) · 42 migrations (no new) · 54 → 57 MCP tools (update_entry_confidence, promote_entry, get_ticket_review_state).
v0.10.10May 18, 2026

Scoped Service API Keys — Cloud Agents, CI Runners, and Integration Partners Get Capability-Restricted Tokens

  • ✓Scoped service API keys for non-human callers. Cloud agents (Bedrock, Vertex), CI runners (GitHub Actions, GitLab MR), and integration partners now get expirable, scope-restricted keys instead of broad user bearer tokens. Replaces the "use your existing personal bearer token" pattern that was carried over from v0.10.2–v0.10.9.
  • ✓Migration 042 — additive. ApiKey gains key_kind ('user' | 'service'), org_id (FK organizations CASCADE, required for service keys), scopes (JSON list), expires_at, revoked_at, revoke_reason, created_by_user_id, last_used_ip (IPv6-safe), service_key_name, project_ids (optional per-key project allowlist), key_prefix. Existing user keys back-fill to scopes='["*"]' so every legacy token continues to authorize unchanged.
  • ✓Deny-by-default for service keys. get_current_user rejects service keys with 403 service_key_not_allowed. The ONLY way a service key reaches a route handler is via require_scope("handoffs:write") / require_any_scope(...) dependencies. Pre-route enforcement, not post-route middleware — side effects are impossible from unauthorized service keys.
  • ✓14 capability scopes — sessions:read, handoffs:read/write, tickets:read/write, personas:read/write, knowledge:read/write, rules:read/write, agent_runs:read/write, retrieval_audit:read, admin:*. * wildcard reserved for legacy user/admin keys; service keys must enumerate explicitly (422 at create otherwise).
  • ✓Cross-org boundary. assert_service_key_can_access_project enforces that a service key's org_id matches the target project's org_id before any project-scoped state change. Optional per-key project_ids allowlist gives finer control. Handoff routes resolve the source session's project via sessions.project_id (authoritative since migration 036) — a git_remote_normalized fallback that originally "preferred the key's org" on ambiguity (a bypass vector) was fixed.
  • ✓Org-scoped admin surface. POST/GET /api/v1/orgs/{org_id}/service-keys, DELETE /api/v1/orgs/{org_id}/service-keys/{id}, POST /api/v1/orgs/{org_id}/service-keys/{id}/rotate. Org admin role + Team+ tier required for mutations. Cross-org 404 (not 403) for existence hiding. Personal user keys under /api/v1/auth/me/api-keys. Raw key returned exactly once on create + rotate.
  • ✓Structured error codes — api_key_revoked, api_key_expired, service_key_not_allowed, insufficient_scope (with required + current arrays), cross_org_denied (with key/project org IDs), service_key_project_required, service_key_project_not_registered, service_key_project_ambiguous. Agents can distinguish rotate-needed from permission-needed without log-scraping.
  • ✓Audit-row provenance. Five audit-row tables (TicketComment, KnowledgeEntry, AgentRun, RetrievalAuditEvent, HandoffEvent) gain actor_type ('user' | 'service_key') + service_key_id + service_key_name. Service keys never silently impersonate humans in provenance. Route opt-in covers POST /handoffs, claim, revoke, decline, comments POST + agent_runs create/complete.
  • ✓KB confidence honored end-to-end. Manual/MCP add_knowledge calls supplying confidence now reach the database with the caller-supplied value — the silent min(confidence, 0.7) clamp on session_id in ("cli-ask", "manual") (and the matching MCP-side default that triggered it on every call) is removed. AddEntryRequest.confidence is now Optional[float]; when omitted, legacy 0.7-for-manual / 1.0-for-session-derived defaults still apply. New PUT /api/v1/projects/{pid}/entries/{id}/confidence as a repair path for entries clamped before the fix landed.
  • ✓Compile no-op response includes word counts. POST /compile no-op response now derives context_words_before/after from project.context_document (same source as /health.word_count) instead of returning 0. New noop_reason field explains why nothing compiled (e.g., "N note(s) are uncompiled — notes do not auto-promote; update confidence then promote").
  • ✓list_ticket_comments MCP tool (bundled from the v0.10.9 cycle) — wraps GET /api/v1/projects/{pid}/tickets/{id}/comments with since (ISO timestamp) + since_id (cursor tiebreaker for same-millisecond ties) + limit (1-500, default 200). Unblocks autonomous review polling loops over MCP.
  • ●1,819 backend + 186 dashboard UI tests passing (was 1,796 + 186 at v0.10.9) · 42 migrations (additive 042) · 54 MCP tools.
v0.10.9May 17, 2026

Comprehensive Handoff Redesign — Provenance Carry-Through, Team Handoffs, Curated Attachments, Lifecycle Events

  • ✓Handoff as a first-class coordination primitive. Single-feature release that elevates handoffs from "copy a session blob to an email recipient" to the same plane as tickets, personas, and agent runs. Migration 041 is additive (zero existing data loss): 12 new columns on handoffs and 5 new tables (teams, team_members, handoff_attachments, handoff_comments, handoff_events).
  • ✓Provenance carry-through. Sender attaches ticket_id + persona_name; on claim, the response includes an active_ticket_payload the recipient's CLI writes to ~/.sessionfs/active_ticket.json. The next captured session is automatically tagged with the handed-off context. Persona-only handoffs derive project_id from the source session's git_remote.
  • ✓Exactly-one-recipient invariant. Every handoff specifies one of recipient_email, recipient_user_id, or recipient_team_id. Enforced server-side via Pydantic @model_validator with strip_blank validators so whitespace-only IDs collapse to None before the count check.
  • ✓Team handoffs (Team+ tier). Hand off to a team; any team member can claim with atomic race protection. UPDATE Handoff WHERE id=X AND status='pending' runs FIRST; new_session_id is pre-allocated and included in the atomic UPDATE. Race losers never write blobs or insert Session rows — no orphan storage. New team_handoff feature flag on Team + Enterprise tiers.
  • ✓Curated attachments. attachments[] list of {kind: kb_entry|wiki_page|ticket, ref_id} validated against the session's project at create time. At claim, attachments are re-validated against recipient's accessible projects; inaccessible refs are silently dropped and surfaced in dropped_attachments with structured reason (not_accessible | deleted | invalid_id | unknown_kind).
  • ✓Lifecycle endpoints. POST /handoffs/{id}/revoke (sender-only, required reason), POST /handoffs/{id}/decline (recipient-only, optional reason), POST/GET /handoffs/{id}/comments (both parties, paged 200), GET /handoffs/{id}/events (audit log).
  • ✓Existence-hiding (404-not-403). Non-parties get 404 on GET /handoffs/{id}, /claim, /decline, /comments, /events, /summary. Eligibility is checked BEFORE any lazy-expire writes or status-specific responses so non-recipients can't distinguish pending vs claimed vs revoked via response codes.
  • ✓Lazy expiry + viewed_at tracking. Pending handoffs past expires_at flip to expired on read with audit event. Per-tier expires_in_hours clamping: 720h (30d) on Free/Pro/Team, 2160h (90d) on Enterprise. First GET by a non-sender stamps viewed_at + emits a viewed event.
  • ✓4 lifecycle email notifications — send_handoff_claimed (to sender), send_handoff_revoked (to recipient), send_handoff_declined (to sender), send_handoff_comment (to the other party). All templates html.escape() user-supplied fields. All sends are best-effort try/except — never fail the route.
  • ✓8 new handoff MCP tools — create_handoff, claim_handoff, get_handoff, list_inbox_handoffs, list_sent_handoffs, revoke_handoff, decline_handoff, add_handoff_comment. Plus list_ticket_comments follow-up. 46 → 54 tools total.
  • ✓CLI extensions. sfs handoff accepts --to-user-id, --to-team-id, --ticket, --persona, --expires-hours, --attach kind:ref_id (repeatable). New subcommands: sfs handoffs get | revoke | decline | comment | comments | events. sfs pull-handoff now persists active_ticket_payload to ~/.sessionfs/active_ticket.json.
  • ●1,796 backend + 186 dashboard UI tests passing (was 1,745 + 186 at v0.10.8) · 41 migrations (additive 041) · 54 MCP tools.
v0.10.8May 16, 2026

v0.10.7 Hotfix — CI Test Isolation Fix

  • ✓Test isolation fix. test_returns_kb_and_session_sources passed locally but failed on the public CI runner because _fetch_kb_entries_raw re-imports load_config from sessionfs.daemon.config inside the function. The monkeypatch on mcp_server.load_config didn't intercept the re-import; CI without ~/.sessionfs/config.toml on disk got empty defaults → empty sources_cited → assertion fail. Fix patches both namespaces.
  • ●No product code changes from v0.10.7. This release unblocks Deploy API + Deploy MCP Server pipelines that failed on the v0.10.7 push. The deploy picks up v0.10.7's answer provenance fields + sfs ticket watch CLI + migration 040 SQLite-compat fix here. Version skipped 0.10.7.1 because Helm rejects 4-segment versions (strict SemVer). 1,745 backend + 186 dashboard UI tests passing.
v0.10.7May 16, 2026

Answer Provenance — sources_cited, Wiki History, Personas Active, Lease Required Mode

  • ✓sources_cited on ask_project — typed list[{type: "kb"|"session", id}] returned alongside the assembled research markdown. KB IDs come from a structured re-fetch (_fetch_kb_entries_raw) — no regex extraction from prose. Session IDs come from the local search index. Same evidence-trail pattern as v0.10.5 source_entries.
  • ✓Wiki page revision history — new wiki_page_revisions table (migration 040) stores every page edit with revision_number, revised_at, user_id, persona_name, ticket_id, full content snapshot. New GET /api/v1/projects/{id}/pages/{slug}/history endpoint with cursor pagination. New get_wiki_page_history MCP tool (45 → 46 tools). Wiki PUT now accepts optional persona_name + ticket_id; MCP update_wiki_page auto-threads them from the active-ticket bundle when project matches.
  • ✓personas_active on session summaries — new session_summaries.personas_active JSON list collected from manifest + per-message persona annotations. Refreshed on both deterministic and narrative regeneration paths. Documented as session-level overblocking (per-decision authorship requires summarizer prompt rework — separate ticket).
  • ✓Lease required-mode org setting — org admins can flip Organization.settings.require_lease_epoch_on_ticket_writes = true; complete/comment/accept ticket writes then return 422 if lease_epoch is omitted. Existing supplied-lease behavior unchanged. Personal projects (no org) bypass.
  • ✓New CLI sfs ticket watch <id> — polls the GET /comments endpoint and renders new comments live (Panel + Markdown). Flags: --interval N (clamped to [5, 300] seconds, default 30), --from-author NAME filter, --exit-on-new for CI scripting, --notify for macOS terminal-notifier. Pairs with sfs ticket comments (v0.10.5).
  • ✓Migration 040 SQLite-incompat fix — first implementation called op.create_unique_constraint after op.create_table, which fails on SQLite (ALTER TABLE can't add constraints). Fix: sa.UniqueConstraint(...) moved inside op.create_table() as a column-level argument; downgrade simplified to drop the table.
  • ✓Wiki revision provenance ownership check — _validate_revision_provenance allows ticket_id only when the user owns the ticket through one of three roles: creator, current resolver, or active executor (open RetrievalAuditContext for this ticket created by the user via start_ticket, with matching lease_epoch AND ticket still in_progress). Lease+status gate replaces the original closed_at IS NULL check.
  • ●1,745 backend + 186 dashboard UI tests passing (was 1,727 + 186 at v0.10.6) · 40 migrations · 46 MCP tools. +18 backend: ask_project sources_cited, wiki history + revision provenance + executor-association + stale-lease + post-status, lease required-mode + personal-project bypass, sfs ticket watch clamping/filter/exit-on-new/404, migration smoke xfail.
v0.10.6May 15, 2026

Kilo Code Capture — 9-Tool Coverage

  • ✓Kilo Code added as the 9th supported tool (capture-only) — VS Code extension (marketplace ID kilocode.Kilo-Code), fork of Roo Code which is a fork of Cline. New KiloCodeWatcher is a thin subclass of ClineWatcher: same per-task UUID storage format, different globalStorage path (kilocode.kilo-code).
  • ✓Wired end-to-end across the toolchain — daemon registration, sfs init auto-detection, watcher CLI plumbing, MCP install (sfs mcp install --for kilo-code registers SessionFS as an MCP server in Kilo Code's settings), recapture path, and resume rejection (capture-only, like Cursor/Amp/Cline/Roo Code).
  • ✓Bidirectional resume targets unchanged — resume targets remain claude-code, codex, copilot, gemini. Kilo Code sessions can be resumed in any of those four.
  • ●1,727 backend + 186 dashboard UI tests passing (was 1,720 + 186 at v0.10.5) · 39 migrations · 44 MCP tools. +7 backend covering Kilo Code watcher detection, capture, recapture guard, and resume rejection.
v0.10.5May 15, 2026

SoD Cohort Disqualification + Tier-Aware Archive Cap + Ticket Comments CLI

  • ✓Compile source manifest extension — get_context_section.source_entries now carries created_by_persona (resolved from the source session's persona_name, set by the daemon's active-ticket annotation pipeline) and the parent compile_id. Agent Runner SoD callers can disqualify by persona/tool/whole-compile cohort, not just by created_by_user_id. Persona attribution is one batched SELECT per compile (bounded by distinct session_ids, not entry count); compile_id is denormalized at response time so the compile + manifest stay one atomic write.
  • ✓Tier-aware archive unpack cap — sync/archive.py:validate_tar_archive and unpack_session now accept member_limit_bytes. CLI pull / sync / pull_handoff thread MAX_MEMBER_SIZE (reads SFS_MAX_SYNC_MEMBER_BYTES_PAID) into unpack. The old hardcoded 50 MB silently nullified paid-tier overrides above 50 MB — same class of bug DLP carried before v0.9.9.8. Default fallback is 100 MB, matching the server abuse cap in _validate_tar_gz.
  • ✓New CLI sfs ticket comments <id> — read-only client of the existing GET /api/v1/projects/{id}/tickets/{id}/comments endpoint. Closes the gap where cross-agent review threads could only be read through the dashboard. Renders each comment in a titled Panel with author + created_at; Markdown body content so code fences render legibly.
  • ✓Cross-project persona leak on source_entries closed — persona attribution SELECT now constrains Session.project_id == project_id AND Session.is_deleted == False. A project-scoped KB entry pointing at a session from another project (KnowledgeEntry.session_id is plain text, not a project-validated FK) no longer leaks that session's persona_name into this project's source_entries. Deleted-session attribution degrades to created_by_persona=null without error.
  • ✓Deterministic compile_id in same-timestamp bucket — get_context_section latest-compile lookup now orders by (compiled_at DESC, id DESC). Since compile_id is part of the SoD evidence contract, identical-timestamp tiebreaks need to be deterministic.
  • ✓Security: site devalue HIGH (GHSA-77vg-94rm-hx3p, CWE-770 DoS, CVSS 7.5) — site/node_modules/devalue 5.6.4 → 5.8.1 via npm audit fix. Astro stays on 6.3.1. No app code change.
  • ●1,720 backend + 186 dashboard UI tests passing (was 1,711 + 186 at v0.10.4) · 39 migrations · 44 MCP tools. +9 backend: archive tier-aware regressions, cross-project persona leak regressions, same-timestamp compile tiebreak regression.
v0.10.4May 15, 2026

Ticket Lease Fencing + Retrieval Audit + Source Manifest

  • ✓Migration 039 — adds tickets.lease_epoch (NOT NULL DEFAULT 0), context_compilations.source_manifest (TEXT NOT NULL DEFAULT '{}'), sessions.retrieval_audit_id (nullable, indexed), and two new tables: retrieval_audit_contexts (one row per ticket start, links to project + ticket + persona + lease_epoch + created_by_user) and retrieval_audit_events (events per context, with serialized arguments/returned_refs, source flag, caller_user_id).
  • ✓Ticket lease fencing — start_ticket atomically increments lease_epoch via UPDATE ... WHERE status IN (...) SET lease_epoch = lease_epoch + 1. complete_ticket, add_ticket_comment, and accept_ticket accept optional lease_epoch in the request body and use it as an inline WHERE-clause predicate. Stale daemons get 409 with a clear current-vs-supplied message. Coordinated audit, not strict mutex — opt-in semantics documented on the MCP tool descriptions.
  • ✓Compile source manifest — compile_project_context snapshots the active KB claims feeding each section before LLM compilation and stores them on the compilation row. get_context_section returns source_entries so SoD/audit callers can trace rendered prose back to the claims that shaped it.
  • ✓Retrieval audit log (server primary + local fallback) — 4 new routes: POST /api/v1/projects/{id}/retrieval-audit-contexts, POST /api/v1/projects/{id}/retrieval-audit-events, GET /api/v1/retrieval-audit-contexts/{ctx}/events, GET /api/v1/sessions/{id}/retrieval-log. Local fallback: ~/.sessionfs/retrieval_logs/<id>.jsonl for offline MCP clients. MCP records context-shaping retrievals (search_project_knowledge, get_wiki_page, get_persona, get_compiled_rules, get_context_section, find_related_sessions, get_session_context) when an active ticket bundle has retrieval_audit_id; sessions persist the id from active_ticket manifests so the audit chain survives capture.
  • ✓MCP get_session_retrieval_log tool (44 total). Server-success and local-fallback paths return identical top-level keys {session_id, retrieval_audit_id, events, count}; local rows are lifted into the server's RetrievalAuditEventResponse shape with source="local" so consumers parse one schema.
  • ✓New CLI sfs persona pull [<name>|--all] — pulls server-side personas to local .agents/*.md files. Preserves the existing kebab-naming convention on re-pull (<name>-<role-fragment>.md); HTML-comment preamble only (no double H1 against the persona's own identity header). Release-only personas untouched.
  • ✓Site messaging rewrite for v1.0 positioning — landing / features / enterprise / pricing reframed around Memory / Identity / Coordination / Governance pillars. “Easy handoffs from humans to agents.” hybrid-operator section on enterprise. Cloud-agent-ready strip (Bedrock, Vertex, custom API) phrased precisely as “via integration docs” — not a hosted gateway.
  • ✓Retrieval-audit defenses. Path traversal: SAFE_AUDIT_ID_RE regex validates audit/session IDs at every entry point. Session upload validates claimed retrieval_audit_id exists in retrieval_audit_contexts for the same project AND was created by the uploading user — silently drops the field with a warning otherwise. Context create validates supplied ticket_id and persona_name belong to the same project; rejects forged provenance with 422. Event creator must own the context. Payloads capped at 16 KiB with _truncated marker. Cross-project SELECT leak closed via accessible-project subquery filter. Comment lease check is atomic. Local JSONL caps at 10 MiB. sanitize_arguments strips any key containing api_key/token/secret/password/auth/credential. collect_returned_refs walker has 50-level depth limit.
  • ●1,711 backend + 186 dashboard UI tests passing (was 1,626 + 186 at v0.10.3) · 39 migrations · 44 MCP tools. +85 backend: retrieval-audit API regression suite, ticket lease tests, compile source-manifest assertions, security hardening regressions.
v0.10.3May 14, 2026

Dashboard UI: Personas, Tickets, AgentRuns Tabs

  • ✓Three new ProjectDetail tabs — Personas, Tickets, AgentRuns surface the v0.10.1 personas + tickets and v0.10.2 AgentRun backends. Read-focused MVP: the moderation transitions a human reviewer wants ship; the FSM transitions tied to the local active-ticket bundle stay in CLI/MCP.
  • ✓Personas tab — list with role + specializations + last-updated; create modal with name-immutable + ASCII regex pattern + markdown content; edit modal; delete with --force toggle for the server's non-terminal-ticket guard.
  • ✓Tickets tab — list filtered by status (all 7 FSM states + All); expand-in-place detail panel (description, acceptance criteria as ☐ boxes, dependency list, completion notes, comments); approve (suggested→open) + dismiss (suggested/open→cancelled) buttons gated by current status; inline comment composer; new-ticket modal.
  • ✓AgentRuns tab — audit-trail read-only view (CI-driven on the backend); status + persona + trigger filters; 30s refetchInterval; expand to view tool / ticket / trigger ref / fail-on / duration / structured findings JSON. Clickable CI run URL goes through a safeHttpUrl() allowlist (http/https only) so a crafted javascript: or data: URL falls back to plain text.
  • ✓API client + react-query hooks for all three surfaces. 14 new methods on ApiClient; new typed interfaces mirror the server Pydantic responses field-for-field.
  • ✓CLI sfs agent complete --findings-file now rejects non-object list elements locally with an element-index message before the HTTP call (avoids 422 stranding the run in running).
  • ✓handle_errors decorator preserves typer.Exit(N) exit codes — latent bug: typer.Exit is a RuntimeError, not a SystemExit, so generic except Exception was downgrading every typer.Exit(N) to SystemExit(1) with "Unexpected error: N". Affected 7 sites across cmd_agent / cmd_persona / cmd_config / cmd_project. Now catches click.exceptions.Exit explicitly and re-raises as SystemExit(exit_code).
  • ✓Dashboard addToast signature rewritten across 10 sites from addToast({kind, message}) to addToast(type, message). npm run build was failing TS2554 at every site.
  • ✓Dashboard ci_run_url scheme guard via safeHttpUrl() allowlist with rel="noopener noreferrer"; DeleteConfirmModal Esc-close handler aligned with the other modals.
  • ●1,688 backend + 186 dashboard UI tests passing (was 1,686 + 165 at v0.10.2) · 38 migrations · 43 MCP tools. +2 backend (findings-file rejection + Exit-preservation regression); +21 dashboard (three new tab test files covering happy + filter + expand + action-gating + toast + URL safety + Esc close).
v0.10.2May 14, 2026

AgentRun + CI Integration

  • ✓AgentRun + CI Integration — an auditable execution record for one persona run, optionally against one ticket, with CI-friendly policy enforcement (severity × fail_on → exit_code). Tracking + enforcement, not model auto-spawning — the CI script picks the LLM, SessionFS records the result.
  • ✓Migration 038 — new agent_runs table. Project-scoped FK; persona_name / ticket_id / session_id are plain strings (no FK) so audit rows survive persona-renames and session-deletes. Columns: status, severity, trigger_source / ref, ci_provider / run_url, findings JSON-as-text (NOT NULL DEFAULT '[]'), fail_on threshold, policy_result, exit_code, duration_seconds, started_at / completed_at. Five indexes including idx_agent_run_project_status, idx_agent_run_ticket, and three project-scoped composites for persona / trigger / created lookups.
  • ✓AgentRun REST API — POST/GET/POST start/POST complete/POST cancel at /api/v1/projects/{id}/agent-runs. Atomic UPDATE ... WHERE status IN (...) rowcount-1 guards on every transition. Same-project ticket validation (cross-project ticket-id rejected with 422). Errored/failed final statuses force exit_code=1 so --enforce always fails CI on crash. Tier: Team+ (agent_runs).
  • ✓CLI sfs agent — 4 subcommands: run (create + start + emit compiled context to --context-file), complete (record findings + severity + policy result with --enforce for CI exit-code propagation), status (text / json / markdown formats; markdown is GitHub/GitLab step-summary compatible), list. Machine-safe --output-id on sfs agent run and sfs ticket create for CI scripting (stdout is just the id; human "Created ..." confirmation goes to stderr).
  • ✓Cloud Agent Control Plane — Bedrock + Vertex AI integration. docs/integrations/bedrock-action-group.yaml + docs/integrations/bedrock_lambda.py (Bedrock action-group dispatcher with closed OPERATIONS table + parse.quote(safe="") on path params blocking traversal); docs/integrations/vertex_tools.py (function-calling schema + dispatcher). Public docs at Cloud Agents.
  • ✓CI Integration docs page — CI Integration with policy matrix, crash-safety patterns, and PR-injection hardening playbook (token scoping, $[object Object] template-substitution avoidance, persist-credentials: false, separate comment-on-pr job).
  • ✓GitHub Actions + GitLab CI example workflows — docs/integrations/github-actions-agent-run.yml + docs/integrations/gitlab-agent-run.yml. Defense layers: per-step SESSIONFS_API_KEY env scoping (off the PR-controlled review step), persist-credentials: false on actions/checkout, permissions: contents: read only, PR title/body via env: (never $[object Object] template), jq -e 'type == "array" and all(.[]; type == "object")' findings-shape guard routing crash / missing / malformed / non-object-element to the errored complete path so runs never stick in running, workspace-relative .sessionfs/ paths so hashFiles() works AND artifacts upload.
  • ✓MCP tools (7 new) — create_agent_run, complete_agent_run, list_agent_runs, approve_ticket (suggested → open dispatch), checkpoint_session (regex-validated snapshot name blocks path traversal), list_checkpoints, fork_session (records parent_session_id + optional forked_from_checkpoint lineage). Session forking + checkpointing previously CLI-only are now exposed via MCP through shared pure helpers in src/sessionfs/session_ops.py. Total MCP tool count: 36 → 43.
  • ✓Env-var auth for CI — _get_api_config now honors SESSIONFS_API_KEY / SESSIONFS_API_URL env vars before falling back to ~/.sessionfs sync config, so a fresh CI runner with only the documented secret authenticates without sfs auth login first.
  • ✓Manifest schema additions — persona_name, ticket_id, instruction_provenance, _resume_parent_id added to src/sessionfs/spec/schemas/manifest.schema.json. Fixes a bug where active-ticket annotation collided with additionalProperties: false.
  • ✓Tool-aware token budgets — _compile_persona_context now recognises bedrock (16000 tokens) and vertex (8000 tokens) alongside the existing tool aliases.
  • ●1,686 backend + 165 dashboard UI tests passing (was 1,626 + 165 at v0.10.1) · 38 migrations · 43 MCP tools. +60 backend over v0.10.1: 27 AgentRun coverage (lifecycle, policy matrix, atomic concurrent-start race, cross-project guards, tier gate, errored/failed exit-code forcing); 9 sfs agent CLI tests (markdown step-summary, json, output-id stream routing, env-var auth precedence, --enforce defense-in-depth); 14 MCP coverage for the new tools (approve dispatch + 409 path, checkpoint/list/fork happy paths, name regex rejection, duplicate-name rejection, missing-session, missing-checkpoint, prefix resolution); plus Cloud Agent integration smoke tests.
v0.10.1May 13, 2026

Agent Personas + Ticketing System

  • ✓Agent Personas + Ticketing System — first SessionFS release with first-class persona + ticket management. Personas are portable AI roles per project; tickets are self-contained units of work with a server-enforced FSM, dependency graph, comments, and an active-ticket provenance bundle that tags every captured session with persona_name + ticket_id.
  • ✓Migration 037 — 4 new tables: agent_personas (project-scoped, ASCII-name UNIQUE, soft-delete via is_active), tickets (FSM: suggested → open → in_progress → blocked → review → done | cancelled; reporter provenance split into created_by_user_id / session_id / persona), ticket_dependencies (composite-PK edge table with reverse-lookup index), ticket_comments (slack-like, non-idempotent). New columns on sessions: persona_name and ticket_id.
  • ✓Persona CRUD API — GET/POST/PUT/DELETE /api/v1/projects/{id}/personas/{name}. ASCII regex ^[A-Za-z0-9_-]{1,50}$ (no Unicode leak). Pre-check duplicate before insert with narrow constraint-name catch. Persona-delete refuses 409 when non-terminal tickets reference the persona; ?force=true overrides. Tier: Pro+.
  • ✓Ticket CRUD + lifecycle API — 13 routes under /api/v1/projects/{id}/tickets. Atomic UPDATE ... WHERE status='X' rowcount-1 guards on start_ticket and accept. Cross-project dependency validation + JOIN-filtered enrich (belt + suspenders). BFS cycle detection. Agent-created tickets require ≥1 acceptance criterion + ≥20-char description, max 3 per session_id. Tier: Team+.
  • ✓Compiled persona + ticket context — start_ticket returns {ticket, compiled_context}. Markdown assembled from persona content + ticket section (description, criteria as checkboxes, file refs, KB claims, completed-dep notes) + recent comments. Tool-aware token budget via ?tool=: claude-code 16k, codex/gemini/copilot/amp/generic 8k, cursor/windsurf/cline/roo-code 4k tokens.
  • ✓Cross-project leak defense — _compile_persona_context filters every data-exposure SELECT by project_id. KB claims filtered by KnowledgeEntry.project_id == ticket.project_id AND claim_class='claim' AND superseded_by IS NULL AND dismissed=False AND fresh; completion notes filtered by same project_id AND status='done'.
  • ✓MCP tools (14 new across personas + tickets) — list_personas, get_persona, create_persona, assume_persona, forget_persona (5 personas); list_tickets, get_ticket, create_ticket, start_ticket, complete_ticket, resolve_ticket, assign_persona, escalate_ticket, add_ticket_comment (9 tickets). Total MCP tool count: 22 → 36.
  • ✓Shared active-ticket bundle — src/sessionfs/active_ticket.py exposes bundle_path(), read_bundle(), write_bundle() (returns bool, surfaces provenance_warning on OSError), clear_bundle_if_owned() (reads-before-unlink; only removes when both ticket_id AND project_id match the completing ticket).
  • ✓CLI — sfs persona list|show|create|edit|delete|assume|forget (7 commands); sfs ticket list|show|create|start|complete|comment|status|block|unblock|reopen|approve|dismiss|assign|resolve|escalate (15 commands). --tool / --force / --no-print-context flags on start; --as PERSONA on comment; --raw on show.
  • ✓Daemon integration — all 7 watchers (claude_code, codex, copilot, cursor, gemini, amp, cline) call annotate_manifest_with_active_ticket(session_dir) after capture, which reads the local bundle and adds ticket_id + persona_name to the session's manifest.json. Server-side _extract_manifest_metadata sanitizes at column widths.
  • ✓Router precedence — personas.router and tickets.router registered BEFORE projects.router in app.py so their /{project_id}/(personas|tickets)/... paths beat the catch-all /{git_remote_normalized:path} (same trick as rules + project_transfers).
  • ●1,626 backend + 165 dashboard UI tests passing (was 1,502 + 165 at v0.10.0) · 37 migrations · 36 MCP tools. +124 backend over v0.10.0 across schema canaries, persona CRUD, ticket FSM + dependency + atomicity, comments + compiled context + cross-project leak regressions, bundle module, CLI smoke, watcher annotation, session-upload provenance roundtrip, and agent-workflow tools.
v0.10.0May 13, 2026

Org Admin Console

  • ✓Org Admin Console — first SessionFS release with full org-level administration. Org admins manage members, transfer projects between scopes, edit org defaults, and link captured sessions to org-scoped projects from a single Organization page and a parallel CLI surface.
  • ✓Migration 035 — projects.org_id (nullable FK, ON DELETE SET NULL) gives projects an explicit org scope. users.default_org_id stores each user's preferred org for multi-org routing. New project_transfers table provides a durable audit + state machine for cross-scope moves, with a partial-unique pending index as the DB-level backstop for concurrent-initiate races.
  • ✓Migration 036 — sessions.project_id (nullable FK, ON DELETE SET NULL, indexed). Server resolves the linkage on every sync from the workspace git remote so the org-scope of any captured session is recoverable.
  • ✓Project transfer API — POST /projects/{id}/transfer initiates; POST /transfers/{xfer_id}/{accept,reject,cancel} mutates state. Atomic UPDATE ... WHERE state='pending' with rowcount check prevents double-accept. Auto-accept when initiator == target (personal → own org). Standing is re-validated on accept/reject.
  • ✓Multi-org member management API — list memberships, invite, promote/demote, remove. Removal preserves all member-authored data ("data stays, access revoked") — sessions stay user-owned, org-scoped projects auto-transfer with an audit row, KB entries preserve authorship, default-org clears, pending transfers tied to the removed user's standing are cancelled. SELECT ... FOR UPDATE row-locks the admin rows before the last-admin guard.
  • ✓Org settings API — GET/PUT /orgs/{id}/settings for the three KB creation defaults (kb_retention_days, kb_max_context_words, kb_section_page_limit). Admin-only PUT with range validation; DLP block survives a general-settings PUT via structural merge. New org-scoped projects inherit the defaults at create time.
  • ✓Default-org API — GET /auth/me now includes default_org_id. PUT /auth/me/default-org sets it (membership validated); passing null clears.
  • ✓Session project resolution — both upload surfaces (POST /sessions and PUT /sessions/{id}/sync) resolve session.project_id from the workspace git remote inside the write transaction with SELECT ... FOR UPDATE on Project and OrgMember rows. Re-sync re-evaluates; pre-v0.10.0 sessions retroactively pick up project_id when the project is created later.
  • ✓Dashboard surfaces — new MembersTab (org member management), /transfers route inbox, per-project TransferPanel tab, OrgSettingsTab for KB creation defaults. New nav link with a pending-incoming badge.
  • ✓CLI — sfs project init --org <id> / --personal for explicit scope, sfs project transfer --to|--accept|--reject|--cancel, sfs project transfers --direction --state for transfer ops, sfs config default-org [<id>|--clear] for the server-canonical default-org preference.
  • ●1,502 backend + 165 dashboard UI tests passing (was 1,384 + 117 at v0.9.9.12) · 36 migrations · 22 MCP tools. +118 backend (project transfers, org members, default-org routing, org general settings, creation-time inheritance). +48 dashboard (MembersTab, TransferInbox, TransferPanel, OrgSettingsTab).
v0.9.9.12May 12, 2026

Daemon-Reindex Data Loss Hotfix

  • ✓Daemon-reindex data loss when self-heal runs against any malformed manifest — user-reported "7-8 Codex sessions disappeared from sfs list after daemon restart following Index-was-corrupted recovery" traced to manifest.get("source", {}) returning None on "source": null, plus NOT NULL bindings (created_at, source.tool) hitting sqlite3.IntegrityError on the same input class. store/index.py rewritten with _as_dict / _as_list isinstance guards so any non-matching type (falsy or truthy) normalizes to an empty container instead of crashing.
  • ✓Per-session exception broadened in the reindex loop — _rebuild_index_from_disk in store/local.py previously caught only (json.JSONDecodeError, OSError), so any AttributeError / IntegrityError / TypeError aborted the entire loop and every session sorted alphabetically AFTER the bad one was silently dropped. Now except Exception with WARNING-level skip logging and an "N indexed, K skipped" summary.
  • ✓sqlite3.IntegrityError misinterpreted as index corruption — upsert_session_metadata's except sqlite3.DatabaseError branch caught IntegrityError (parent class) and ran the destructive recreate-and-retry recovery path per bad session. IntegrityError-before-DatabaseError split now catches it first and re-raises so the per-session skip handles it cleanly. Same fix applied to upsert_tracked_session.
  • ✓sfs daemon rebuild-index CLI command had the same two bugs duplicated — cli/cmd_daemon.py:rebuild_index carried its own copy of the null-unsafe defaults and the too-narrow except. Fixed in parallel with matching isinstance guards and broadened per-session except. Backfill block now repairs "source": null / "source": "codex" shapes in-place when a tracked_sessions row exists for the ID. Cross-reference comment added; DRY refactor queued for v0.10.x.
  • ✓New regression suite — tests/unit/test_resilience.py covers malformed-manifest survival across both the daemon and CLI reindex paths (+8 backend tests).
  • ✓Helm chart 0.9.14 → 0.9.15, appVersion bumped to 0.9.9.12. Chart evolves independently of app per Helm SemVer.
  • ●1,384 backend + 117 dashboard UI tests passing · 34 migrations · 22 MCP tools
v0.9.9.11May 12, 2026

Dashboard Rules Tab Freeze Fix

  • ✓Dashboard freeze when editing project rules → knowledge / context max tokens (RulesTab.tsx: debounce + flush-on-blur/unmount + boolean-gated Compile).
  • ●1,376 backend + 117 dashboard UI tests passing · 34 migrations · 22 MCP tools
v0.9.9.10May 12, 2026

KB Search Trigram Index, Sync Status Query Collapse, ANSI Test Helper

  • ✓pg_trgm GIN index on knowledge_entries.content (migration 034) — accelerates ILIKE substring search used by GET /projects/{id}/entries?search= and MCP search_project_knowledge. PostgreSQL only; SQLite is a no-op. Route and MCP wrapper now enforce a 3-char minimum on search so every accepted query benefits from the index.
  • ✓/sync/status collapsed 5 queries to 2 — watchlist counts (watched/queued/failed) now come from a single GROUP BY status SELECT against tracked_sessions. Aggregate session counters remain on a single multi-aggregate SELECT. Behavior unchanged.
  • ✓Admin /orgs no longer N+1s — member counts for the paginated org list load via a single WHERE org_id IN (...) GROUP BY org_id SELECT, then merge into the response. Empty-page guard prevents IN ().
  • ✓sfs project ask keyword extractor rebuilt — strips trailing punctuation, lowercases, drops stop words, enforces the 3-char server floor, dedupes, caps at 5 keywords. Previous tokenizer produced 422s on common 2-char tokens (db, ai, ui) once the search gate landed. Canary test fails if CLI floor and server gate drift.
  • ✓Shared ANSI-strip test helper (tests/utils/ansi.py) — replaces four inline regex sites that previously protected tests from the v0.9.9.8 CI-color flake class. 10 dedicated unit tests; case-insensitive matching is opt-out.
  • ✓confirm_or_exit() CLI helper — single source of truth for interactive confirmation prompts with a non-TTY guard so piped EOF returns a structured exit instead of leaking "Unexpected error" through handle_errors. Wired into DLP scan-on-push and push-after-delete prompts.
  • ✓/release skill step 12 hardened — cache-busted probes, strict grep -F version match against the changelog endpoint, vercel inspect <live-alias> on the deployment that actually serves traffic, output captured to a variable so set -o pipefail doesn't mask failures.
  • ✓urllib3 bumped past CVE-2026-44431 / CVE-2026-44432 (fix in 2.7.0). Transitive — no direct pin in pyproject.toml.
  • ✓click.exceptions.Abort backstop in handle_errors — Abort does not inherit from ClickException; without an explicit catch it bubbled up as "Unexpected error: aborted." for any cancelled typer.confirm.
  • ✓Helm chart 0.9.12 → 0.9.13, appVersion bumped to 0.9.9.10. Chart evolves independently of app per Helm SemVer.
  • ●1,376 backend tests + 109 dashboard UI tests passing · 34 migrations · 22 MCP tools
v0.9.9.9May 11, 2026

Deploy API Gate Fix, Stale-Window Tuning, Onboarding Polling Cleanup

  • ✓CI / Deploy API gate fix — tests now ANSI-strip Rich-rendered CLI output before substring assertions. v0.9.9.8's Deploy API was blocked on this regression, leaving api.sessionfs.dev stuck at 0.9.9.7. Pipeline unblocked for this release and going forward.
  • ✓Dashboard polling consolidated — removed dead useRunAudit hook; audit polling now lives only in BackgroundTasksProvider. One source of truth, no duplicate timers.
  • ✓Onboarding stops polling on completion — getting-started page no longer refetches state once the user has at least one session AND one project. Cuts idle background traffic for fully onboarded users.
  • ✓Stale windows extended 30s/60s → 300s — folder list and inbox-handoff queries reuse cached data for five minutes instead of seconds. Mutations still invalidate so no correctness risk; the dashboard just stops hammering the API while you read.
  • ✓Helm chart 0.9.11 → 0.9.12, appVersion bumped to 0.9.9.9. Chart evolves independently of app per Helm SemVer.
  • ●1,344 backend tests + 109 dashboard UI tests passing · 33 migrations · 22 MCP tools
v0.9.9.8May 11, 2026

Tier-Aware DLP Caps, Handoff UX Fix, Doctor PATH Drift Detection

  • ✓DLP per-member size cap is tier-aware — was hardcoded 50 MB and ignored SFS_MAX_SYNC_MEMBER_BYTES_PAID overrides. Now derives from effective tier (10 MB free/starter, 50 MB pro/team/enterprise) with a post-redaction guard so a redact-then-repack flow can't bypass the cap.
  • ✓sfs handoff <handoff_id> redirects to pull-handoff — used to fail with "Missing option --to" when recipients ran the command they were emailed. Now prints clear guidance pointing at sfs pull-handoff <id>.
  • ✓handle_errors preserves Typer validation — invalid CLI options no longer get swallowed as a generic "Unexpected error". UsageError / BadParameter pass through with the original Typer formatting and exit code.
  • ✓Manual sync per-session retry of transient exclusions — hard-delete tombstones still respected via atomic check-and-clear under fcntl.flock. TOCTOU-safe: a delete that races against retry can't be silently undone. Malformed deleted.json entries handled defensively across all helpers.
  • ✓sfs doctor detects PATH drift — when pip installs to user-site but the shell PATH points at an older binary, doctor reports the mismatch via shebang parsing and recommends a fix. python -m sessionfs fallback added for shimless environments.
  • ✓Helm chart 0.9.10 to 0.9.11, appVersion bumped to 0.9.9.8.
  • ●1,344 backend tests + 109 dashboard UI tests passing · 33 migrations · 22 MCP tools
v0.9.9.7May 10, 2026

Tier-Aware Sync Caps, dismiss_knowledge_entry MCP Tool, KB Performance Indexes

  • ✓Tier-aware per-member sync size cap — free / starter capped at 10 MB per archive member, pro / team / enterprise at 50 MB. New env vars SFS_MAX_SYNC_MEMBER_BYTES_FREE and SFS_MAX_SYNC_MEMBER_BYTES_PAID for self-hosted overrides. Oversized members surface SyncTooLargeError with actionable guidance.
  • ✓dismiss_knowledge_entry MCP tool — 22nd MCP tool. Idempotent (calling twice is a safe no-op) with full audit triple: dismissed_at, dismissed_by, dismissed_reason. PUT /entries/{id} now SELECT FOR UPDATE locks the row so concurrent dismissals can't tear the audit record.
  • ✓Concept compiler bulk prefetch — collapsed an N+1 (concept candidates × claims) into 1 + 1 + 1 queries. Compile times on knowledge-rich projects drop from seconds to milliseconds.
  • ✓Handoff list batching + recipient_email_normalized index — migration 032. Inbox query plan no longer N+1 over recipients; large org inboxes load in a single round trip.
  • ✓KnowledgeEntry composite indexes — migration 033 adds covering indexes for the list / pending / cursor pagination paths. Default-sort list_entries with high-volume projects is now bounded-time.
  • ✓Dismissal audit columns on knowledge_entries — migration 031 makes dismissed_at / dismissed_by / dismissed_reason first-class so dashboard and audit reports can show "who dismissed this and why" without recomputing from history.
  • ✓Helm chart 3-segment SemVer fix — chart version goes 0.9.9.6 to 0.9.10 (4-segment chart versions are invalid SemVer in Helm). appVersion bumped to 0.9.9.7.
  • ✓Security — pip-audit advisories cleared and dashboard postcss bumped (transitive npm advisory fix).
  • ●1,300 backend tests + 109 dashboard UI tests passing · 33 migrations · 22 MCP tools
v0.9.9.6May 10, 2026

MCP Tier A Read Surface, Tier-Aware Knowledge Rate Limits, Compile Race Fix

  • ✓MCP Tier A read surface — 7 new tools (get_knowledge_entry, list_knowledge_entries, get_wiki_page, get_knowledge_health, get_context_section, get_session_provenance, compile_knowledge_base). Total MCP tool count goes 14 to 21. All tools enforce existing project membership / session ownership checks.
  • ✓list_knowledge_entries rich filters — claim_class, freshness_class, dismissed, session_id query params plus three sort modes (created_at_desc, last_relevant_at_desc, confidence_desc) with stable id tiebreak so identical sort-key values can't reorder.
  • ✓Keyset cursor pagination — opt-in ?cursor=<id> on list_entries (default sort). Snapshot-stable across concurrent inserts/deletes — no skipped or duplicated rows. Server emits X-Next-Cursor on every default-sort page so callers can bootstrap iteration from page 1.
  • ✓Per-user tier-aware knowledge rate limits — free=20, starter=50, pro=100, team=100, enterprise=200, admin=500 requests/hour on POST /entries/add. Bucket key is user_id (not session_id) so MCP manual callers don't share buckets. Admin tier promoted before effective-tier resolution.
  • ✓Project-row lock on compile + concept generation — compile_project_context and auto_generate_concepts both SELECT FOR UPDATE the project row before reading pending claims / creating concept pages. Eliminates duplicate-context-document and duplicate-concept-page races.
  • ✓413 graceful failure — sfs push / handoff / sync pre-scan archives for oversized members (10MB limit) and surface SyncTooLargeError with actionable guidance instead of an opaque server 413.
  • ✓Compression-safe capture guard — shared should_recapture() helper consulted by all 7 watchers before re-write. Checks deleted-sessions exclusion list FIRST, then compares source JSONL message count against existing .sfs to prevent re-capturing a compressed session as empty.
  • ✓sfs recapture command — manual re-capture flow with CursorComposerPurgedError guard for purged-composer cases.
  • ✓Cross-tool MCP-over-CLI nudge — three-layer fix so agents prefer MCP tools over sfs shell commands: tool descriptions instruct "use MCP instead of sfs", a guidance block is injected into every compiled rules file, and sfs hooks install writes a SessionStart hook for Claude Code that emits the cached compiled output before the first message.
  • ✓Dashboard "Compile now" CTA — workflow hint banner now carries an inline button. Server-side health recommendations fire at any pending count > 0 (was > 20).
  • ✓Pre-upgrade Helm migration Job — runs Alembic upgrade head before the API rolls out, so a self-hosted upgrade that drops a new migration cannot serve traffic against an unmigrated DB.
  • ✓Dashboard manualChunks split — explicit vite.config.ts chunk naming. Main bundle drops from 212 KB to 30 KB (8x smaller); vendors cache separately across deploys.
  • ✓/api/v1/health alias — added alongside /health for self-hosted ingress paths that scope readiness to /api/v1/*.
  • ✓Settings.json fcntl locking — sfs hooks install/uninstall and rules emitter use fcntl.flock on a .sfs-lock file so concurrent invocations can't corrupt the JSON.
  • ✓KB health pending count fix — only counts claims (excludes notes). Notes don't compile, so they shouldn't drive the pending banner.
  • ✓Daemon transient errors no longer exclude sessions — only SyncTooLargeError (413) counts toward the exclusion threshold; transient SyncError failures are retried instead of permanently excluded.
  • ●1,279 backend tests + 109 dashboard UI tests passing · 30 migrations · 21 MCP tools
v0.9.9.5April 17, 2026

First-Run Onboarding, Unified Delete Propagation, Sort & Filter Improvements

  • ✓First-run onboarding — signup auto-authenticates and navigates to /getting-started. Three-step onboarding page (install tool, capture session, create project) with live completion indicators. State-based redirect gate: 0 sessions + 0 projects sends new users to onboarding. API key shown in dismissible banner after signup.
  • ✓Sort direction toggle — ascending/descending on all sort modes in the dashboard. CLI adds --sort messages-asc and --sort tokens-asc for finding small sessions.
  • ✓Tool sort mode — real "Sort: Tool" in the sessions list groups by tool label.
  • ✓Tool filter alias normalization — gemini/gemini-cli and copilot/copilot-cli treated as the same family across list, search, and admin endpoints.
  • ✓Unified 410 delete propagation — structured SyncDeletedError replaces string-based detection. Shared cleanup_deleted_session() helper wired into all three sync paths. Dashboard-deleted sessions are auto-cleaned locally on next sync instead of showing red 410 errors.
  • ✓Full local cleanup on server 410 — removes .sfs directory + SQLite index entry (sessions and tracked_sessions tables) + adds to exclusion list. No more orphaned local copies.
  • ✓Cloud Run min-instances — API deployment now sets --min-instances 1 to eliminate cold-start latency.
  • ✓Migration 030 cross-DB fix — replaced raw PostgreSQL-only INTERVAL syntax with SQLAlchemy Core queries (works on both PostgreSQL and SQLite).
  • ✓sfs delete/restore prefix resolution — now resolves session ID prefixes via local store for all scopes (was only resolving for local/everywhere, not cloud).
  • ✓sfs rules init TTY guard — fails fast with clear message when stdin is not a TTY and --yes is not passed.
  • ●1,205 backend tests + 109 dashboard UI tests passing · 30 migrations
v0.9.9.4April 16, 2026

Session Delete Lifecycle — Three-Scope Delete, Trash/Restore, Admin Purge

  • ✓Three-scope session delete — sfs delete <id> with --cloud (server only, keep local), --local (device only, keep cloud), or --everywhere (both). No default scope — explicit choice required. Confirmation prompt with --force bypass for automation.
  • ✓Sync-aware deletes — autosync respects intentional deletes via ~/.sessionfs/deleted.json exclusion list. Push and pull skip excluded sessions. Un-delete path gated behind X-SessionFS-Undelete header with ETag conflict check — autosync can never reverse a delete.
  • ✓sfs trash — lists soft-deleted sessions in the 30-day retention window with scope badges and purge dates.
  • ✓sfs restore <id> — reverses a soft-delete on the server, clears local tombstone, prints sfs pull guidance when local copy was removed.
  • ✓Dashboard delete dialog — replaces the single confirm() with a two-choice dialog: "Remove from cloud" or "Delete everywhere". One-line explanation per option.
  • ✓Dashboard Trash view — filter toggle on the session list showing soft-deleted sessions with scope badges, purge dates, restore buttons, and scope-aware restore guidance toast.
  • ✓Admin purge endpoint — POST /admin/purge-deleted hard-deletes expired soft-deleted sessions and their blobs. Single-session or bulk. Returns purge count and bytes reclaimed.
  • ✓Restore response guidance — POST /sessions/:id/restore returns restored_from_scope and local_copy_may_be_missing so clients show accurate recovery guidance.
  • ✓DELETE endpoint breaking change — now requires ?scope=cloud|everywhere query parameter (was parameterless). Returns 200 with session record including purge_after (was 204). Old clients without ?scope= get 400.
  • ✓Storage quota fix — soft-deleted sessions excluded from used-bytes calculation.
  • ✓Share links for deleted sessions — return 410 Gone (was 404).
  • ✓Autosync un-delete bug fix — original soft-delete was immediately reversed by autosync pushing the local copy. Now gated by explicit intent header + ETag check.
  • ✓Security — CVE-2026-40347 (python-multipart DoS via crafted multipart preamble/epilogue) + purge endpoint hardening (session_id format validation + atomic audit logging).
  • ●1,203 backend tests + 98 dashboard UI tests passing · 30 migrations
v0.9.9April 14, 2026

Rules Portability — Canonical Project Rules, Five Tool Compilers, Resume-Time Sync

  • ✓Canonical rules per project — new project_rules + rules_versions tables (migration 028) with 4 new instruction-provenance columns on sessions. Managed via GET/PUT /projects/:id/rules and POST /projects/:id/rules/compile with ETag-based optimistic concurrency (SELECT FOR UPDATE row lock).
  • ✓Five tool compilers — deterministic, partial-compile-aware compilers for Claude Code (CLAUDE.md), Codex (codex.md), Cursor (.cursorrules), Copilot (.github/copilot-instructions.md), and Gemini (GEMINI.md). Each embeds a SessionFS managed marker at the top of the file.
  • ✓Knowledge + context injection — compilers pull active claims (default convention + decision types) and project context sections (default overview + architecture) with per-tool token ceilings and progressive condensation.
  • ✓sfs rules CLI — init (auto-detects existing rule files + recent session-history tool usage), edit, show (in-sync state), compile (--tool, --dry-run, --force), push, pull. Default is shared-in-repo; --local-only adds compiled files to .gitignore.
  • ✓Managed-file safety — sfs rules compile refuses to overwrite a user-maintained rule file unless --force is set. Detection reads only the first 512 bytes so markers buried in hand-written content don't trigger false positives.
  • ✓Session instruction provenance — manifests now carry rules_version, rules_hash, rules_source (sessionfs/manual/mixed/none), and instruction_artifacts[]. SFS_CAPTURE_GLOBAL_RULES=off suppresses global hashing for privacy-sensitive environments.
  • ✓Resume-time rules sync — sfs resume preflights the target tool's rule file from current canonical rules before launching the tool (claude-code, codex, copilot, gemini supported). New flags: --no-rules-sync skips preflight; --force-rules overwrites an unmanaged target as a one-time permission (file becomes SessionFS-managed afterward).
  • ✓Source-session provenance display — resume shows the rules version that shaped the original session vs current project rules (e.g. "Source session used rules v3 (sessionfs). Current project rules are v5. Synced codex.md from SessionFS rules v5.").
  • ✓Non-fatal semantics — rules sync failure never fails the resume itself. Warning to stderr, resume continues, exit 0. Partial compiles never bump canonical version history.
  • ✓MCP tools — read-only get_rules and get_compiled_rules for agents. No agent self-modification of rules.
  • ✓Dashboard Rules tab — new RulesTab under ProjectDetail with version badge, static preferences editor, enabled-tools checklist, knowledge/context injection settings, per-tool compiled output viewer, version history, and compile action. ETag optimistic concurrency with 409 toast on stale saves.
  • ✓Helm chart semver fix — 0.9.8.6 was an invalid four-segment chart version. v0.9.9 returns the chart to valid 3-segment semver matching the Python package.
  • ✓Compile hash is marker-independent — content_hash is now the body hash only, so no-op detection doesn't break after a version bump (no more infinite-version-bump loops).
  • ✓Path traversal defense — _safe_target_path() in cli/cmd_rules.py used by both sfs rules compile and resume preflight; validates against canonical TOOL_FILES[tool] and confirms the resolved path stays inside git_root.
  • ✓Concurrent compile + first-create safe — version-number contention retries idempotently; same-body-hash collision short-circuits to no-op; get_or_create_rules catches IntegrityError on first-time creation.
  • ✓Knowledge-injection determinism — claim ordering priority (decision > convention > pattern > dependency) moved into SQL ORDER BY so LIMIT respects it; deterministic id DESC tie-breaker.
  • ✓Security — pytest bumped to >=9.0.3,<10.0 for CVE-2025-71176 (dev-only /tmp DoS).
  • ●1,171 backend tests + 89 dashboard UI tests passing · 28 migrations
v0.9.8.6April 13, 2026

Knowledge Base v2 — Three-Layer Claim Model, Per-Type Freshness, Writeback Gates

  • ✓Three-layer claim model — every knowledge entry is now evidence (raw fact from sync), claim (promoted active truth), or note (rejected/dismissed). Migration 027 adds claim_class, entity_ref, freshness_class, supersession_reason, promoted_at, retrieved_count, used_in_answer_count, compiled_count.
  • ✓Per-type freshness decay — bug 30d, dependency 60d, pattern/discovery 90d, convention 180d, decision 365d. Entries decay current → stale → archived from last_relevant_at.
  • ✓Auto-promotion at compile — evidence with confidence ≥ 0.5 and content ≥ 30 chars is promoted to claim at the start of every compile pass.
  • ✓Writeback gates for agent contributions — add_knowledge defaults to note. Auto-promotes to claim only if specificity gate, semantic dedup (Jaccard-min ≥ 0.85), and rate limit pass. Agents see classification feedback.
  • ✓Supersession + refresh + rebuild endpoints — PUT /entries/:id/supersede retires a claim with a reason and links it to the replacement. PUT /entries/:id/refresh resets last_relevant_at (replaces the old "Still valid" no-op). POST /projects/:id/rebuild + sfs project rebuild force a full re-compile on settled projects.
  • ✓Section pages as true projections — compile iterates ALL slug_map types, not just types in the pending batch. Pages with zero active claims are deleted inline.
  • ✓used_in_answer tracking — MCP ask_project and API search expose a _used_in_answer flag that increments on retrieval, feeding the freshness signal.
  • ✓Dashboard KnowledgeEntriesTab v2 — health banner with stale review queue, claim/freshness badges, filter controls, provenance blocks (entity ref, promoter, retrieved/used counts), promote/supersede/refresh/dismiss actions, rebuild button.
  • ✓Compile default budget lowered to 2,000 words (from 8,000) — active-truth-per-token principle produces sharper context documents.
  • ✓Concept page prune fix — page_type filter was looking for "auto" but concept pages store as "concept", so dead pages were never pruned.
  • ✓Daemon fixes — startup order (settings fetch before full_scan), Codex watcher NoneType crash, sfs daemon stop PID resolution fallback to daemon.json.
  • ●1,091 backend tests + 76 dashboard UI tests passing · 27 migrations
v0.9.8.5April 12, 2026

Security Fixes, Lifecycle Hardening, Dashboard Help, 93 New Tests

  • ✓Security fixes — GitLab webhook user binding (HIGH), GitHub installation claim IDOR (HIGH), effective-tier leak (MEDIUM), pr_comments unique index scoped (MEDIUM)
  • ✓Dashboard Help page — MCP-first guidance, 8-tool installer, agent prompt examples, curated CLI reference, 12-tool MCP reference
  • ✓Admin org endpoints — GET/POST /api/v1/admin/orgs + PUT /api/v1/admin/orgs/:id/tier for internal provisioning
  • ✓Knowledge lifecycle hardening — LLM compile budget enforcement, semantic dedup on all extraction paths, concept page pruning, bulk dismiss-stale endpoint
  • ✓Daemon fixes — Codex watcher null-content crash, startup delay (308 ms vs 96 s), sfs daemon stop fallback with PID resolution
  • ✓Dashboard fixes — signup broken on app.sessionfs.dev, unguarded localStorage hardened via storage helper
  • ✓Helm + Dockerfile hardening — postgres StatefulSet securityContext, helm test hook pod, non-root UID 10001 in both Dockerfiles
  • ✓CVE patches — vite 7.3.2 (3 CVEs), defu 6.1.5 (prototype pollution). Security Scan workflow rewritten with raw trivy binary.
  • ✓Self-hosted Security Posture docs + sfs dlp CLI documentation
  • ●1,091 backend tests + 76 dashboard UI tests passing
v0.9.8.4April 10, 2026

Dashboard Help Page, Signup Fix, Security Hardening

  • ✓New dashboard Help page — 8-tool MCP installer, agent prompt examples, curated CLI reference, full 12-tool MCP reference
  • ✓Signup fix — VITE_API_URL baked into production bundle; api.<domain> derived from app.<domain> in fallback chain
  • ✓Helm chart + Dockerfile hardening — postgres StatefulSet securityContext, non-root UID 10001
  • ✓Security Scan workflow rewritten; 4 CVE patches (vite, defu)
  • ●1,052 backend tests + 22 dashboard UI tests passing
v0.9.8.3April 9, 2026

Connection Pool Optimization, Sync Atomicity

  • ✓Client-side upload concurrency limiter and per-user server semaphore with 429+Retry-After
  • ✓sync_push split into phases — DB connection held ~70ms instead of ~5s
  • ✓Commit-then-promote sync with temp blob preservation; PK constraint for creates, FOR UPDATE for updates
  • ✓New /health/pool endpoint for pool utilization metrics
  • ✓Sync summary shows error count alongside pushed/pulled/conflicts
v0.9.8.2April 9, 2026

Database Pool Configuration

  • ✓Configurable SQLAlchemy pool via SFS_DATABASE_POOL_SIZE (20), SFS_DATABASE_MAX_OVERFLOW (40), SFS_DATABASE_POOL_TIMEOUT (60s), SFS_DATABASE_POOL_RECYCLE (1800s)
  • ✓pool_pre_ping=True — stale connections detected before checkout
v0.9.8.1April 9, 2026

Knowledge Base Lifecycle, Billing Isolation

  • ✓Entry decay (0.8x confidence after 90 days unreferenced), auto-dismiss past retention period
  • ✓Quality gates — 20 char minimum, 20/hr rate limit per session, 85% similarity rejection
  • ✓Context document 8,000 word budget with priority-aware trimming; section page caps (30 items)
  • ✓Concept auto-refresh on 50% cluster growth; auto-delete when all entries dismissed
  • ✓Actionable health endpoint; sfs resume --model for target tool selection
  • ✓Billing webhook isolation — org state only mutates on positive customer_id AND subscription_id match
  • ✓Migration 025: lifecycle fields on knowledge_entries + project settings
v0.9.8April 9, 2026

DLP / Secret Scrubbing

  • ✓Pre-sync content protection — 14 PHI patterns and 19 secret patterns
  • ✓Three enforcement modes: BLOCK, REDACT, WARN — server-side scan of all archive files
  • ✓Org policy via settings JSON; custom patterns and allowlist support
  • ✓CLI: sfs dlp scan, sfs dlp policy; sfs push shows DLP preview
  • ✓Dashboard: Settings > DLP tab for admins; session detail shows findings
  • ✓DLP API feature-gated to Pro+; migration 024 (dlp_scan_results column)
  • ✓43 new DLP tests
v0.9.7.1 – v0.9.7.4April 6–7, 2026

Dashboard UX Overhaul, Docs Refresh, MCP Roots, Billing Isolation

  • ✓MCP workspace detection via roots/list protocol; explicit git_remote parameter on all 6 project-scoped tools
  • ✓0-byte index.db recovery — auto-rebuild on truncated files
  • ✓New docs: knowledge base, dashboard guide, organizations, billing & tiers
  • ✓CLI reference updated with 15 missing commands; all 12 MCP tools documented
  • ✓Dashboard code splitting — main bundle 676KB → 262KB; 12 lazy route chunks
  • ✓Command palette search (Cmd+K), mobile nav drawer, focus trapping, ARIA live regions, Zod form validation
  • ✓Sessions hero redesign, project card health badges, typography polish, product identity sync
  • ✓Billing isolation — Team checkout and subscription management stay scoped to the organization
  • ✓Handoff security — recipient verification on claim, claimed summaries blocked, session ID persisted
  • ✓Knowledge compiler content-level dedup across batches; backfill on project creation
  • ✓Share-link passwords moved to POST body with PBKDF2-HMAC-SHA256 hashing
  • ✓GitHub webhook signature enforcement; GitLab MR comment dedup; per-user webhook secrets
  • ✓Migrations 020–023
v0.9.7April 4, 2026

Living Project Context, Wiki Pages, 12 MCP Tools, Self-Healing Index

  • ✓Living Project Context — auto-summarize on sync, knowledge entries (6 types), structured compilation
  • ✓Wiki pages with multi-document structure, backlinks, and section pages
  • ✓12 MCP tools — 3 write tools + 2 search tools added
  • ✓Auto-narrative toggle per project (LLM narrative on sync)
  • ✓Self-healing SQLite index — auto-rebuild from .sfs files on corruption
  • ✓sfs doctor — 8 health checks with auto-repair
  • ✓handle_errors decorator on all CLI commands (no raw tracebacks)
  • ✓Message pagination with newest-first default and order toggle
  • ✓Dashboard: Knowledge Entries, Pages, History tabs on project detail
  • ✓Project-level access control on all 13 knowledge + wiki routes
  • ✓CLI: sfs project compile, entries, health, dismiss, ask, pages, page, regenerate, set
  • ●1,052 tests passing
v0.9.5March 30, 2026

FSL Licensing, Organizations, Hosted Billing, Security Pipeline

  • ✓FSL licensing — MIT core + FSL enterprise with Helm license validation
  • ✓Server-side tier gating (5 tiers, 30+ feature flags)
  • ✓Organizations + RBAC (admin/member roles, org management)
  • ✓Hosted billing integration with subscription lifecycle
  • ✓LLM Judge revamp — confidence scores, CWE mapping, evidence linking, dismiss/confirm
  • ✓Narrative LLM summaries for session overviews
  • ✓Dashboard analytics + resume preview
  • ✓Handoff UX — status stepper, session context display
  • ✓CLI: sfs init wizard, sfs org, sfs security scan
  • ✓7 MCP tools (added get_session_summary, get_audit_report)
  • ✓Security pipeline — Dependabot, Trivy, Bandit, pip-audit
  • ●921 tests passing
v0.9.3March 28, 2026

Judge V2, Session Summarization, GitLab Integration

  • ✓Severity-classified findings (CRITICAL, HIGH, LOW) with auto-categorization
  • ✓Contradictions-first audit dashboard with metric cards
  • ✓Audit history stored in database with model comparison
  • ✓GitLab MR integration (cloud + self-hosted)
  • ✓Auto-audit trigger (manual, on_sync, on_pr)
  • ✓Session summarization — files, tests, commands, packages extracted automatically
  • ✓Claim extractor fixed for Claude Code sessions (0 claims to 50+)
  • ●848 tests passing
v0.9.0March 28, 2026

Autosync, Handoff Session Copy, Single-Ingress

  • ✓Autosync with three modes: off, all, selective (per-session watchlist)
  • ✓Handoff claim now copies session blob and creates recipient's session
  • ✓Single-ingress via dashboard nginx (removed separate API/MCP ALB targets)
  • ✓Nginx client_max_body_size 100m for session uploads
  • ●835 tests passing
v0.8.2March 27, 2026

Custom Judge Base URL, Project Context, Resume Auto-Launch

  • ✓Custom base URL for LLM Judge (LiteLLM, vLLM, Ollama, Azure OpenAI)
  • ✓Shared project context (sfs project init/edit/show + MCP tool)
  • ✓Resume auto-launches native tool (Claude Code, Codex, Gemini)
  • ✓Codex resume format fixed (source: "cli", missing fields added)
  • ●819 tests passing
v0.8.0March 26, 2026

Storage Management, EKS Fixes, Multi-Provider Email

  • ✓Local storage management: sfs storage, sfs storage prune
  • ✓Multi-provider email: Resend, SMTP, NullProvider
  • ✓EKS deployment fixes: security contexts, nginx non-root, asyncpg SSL
  • ✓Gemini model extraction from logs.json
  • ●763 tests passing