Skip to main content

dct — Deterministic Context Tracker

PyPI version Python 3.11+ CI License: MIT

dct gives AI coding assistants persistent, queryable memory: project state read from a database instead of guessed. One database shared by all your projects tracks issues, todos, changelog entries, plans, sprints, decisions, and session handoffs. The MCP (Model Context Protocol) server and CLI work with any MCP-capable assistant (Cursor, Windsurf, Zed, and friends); Claude Code gets the deepest integration, with 13 slash-command skills and 4 automation hooks on top of the 55 MCP tools.

Part of the dc tools family (dctools.dev) — deterministic context for AI-assisted development.

Why dct?

You have a dozen side projects. Every AI coding session starts cold — the assistant has no memory of what you decided last week, which bug you were mid-fix on, or why you picked SQLite over Postgres. So you re-explain, or it guesses.

The usual trackers don't fill the gap. Your assistant can drive GitHub Issues these days (gh, the official MCP server), but issues live per-repo on someone else's server, with no notion of a cross-project view, a sprint in flight, a session handoff, or a gate that keeps an item open until you've validated it — and Jira is heavier still. TODO.md files rot the moment you stop grooming them. Assistant memory files (CLAUDE.md, auto-memory) help, but they're prose: un-queryable, unstructured, and invisible to your other tools. dct is the structured half — typed items with priorities, state, and gates that any session, script, or MCP client can query.

dct is built for exactly this:

  • One user-scoped database across all your projects — one place to ask "what's open everywhere?" Local-first: SQLite by default, so your tracking data stays on your machine, with first-class PostgreSQL support when you want a server-grade backend.
  • A full tracking model — issues, todos, features, ideas, and improvements, each with priority, notes, and per-item checkpoints; gate checkpoints (dogfood, security, review, approval) keep an item open until they pass.
  • Your markdown plans, indexed — specs, roadmaps, and ADRs stay plain markdown files; dct anchors their sections and checkpoints in the database, so plan state is queryable and promotable to items and sprints.
  • MCP-native — the assistant reads and writes tracking directly: no copy-paste, no context loss.
  • Changelog-first releases — entries accumulate as you work (your assistant writes them); release day is one command.
  • Soft-delete ledger — notes, checkpoints, and handoffs are removed from view at most; nothing is ever hard-deleted.
  • Session handoffs — end a session with a handoff prompt; the next one picks up where you left off.

The day-to-day learning curve is your assistant's, not yours. There is no UI to master and no required syntax: after a one-time dct init you keep talking the way you already do — "track this", "I fixed it", "what's open?" — and the assistant does the bookkeeping. The slash commands below are optional shortcuts for the same moves, and every one of them is equally an MCP tool and a CLI command, so the whole workflow runs on any MCP client.

What a session looks like

Tracking happens in the conversation you're already having — one working session, and the morning after:

> track this: parser crashes on empty frontmatter
Created issue #12 in myproj (P2, open)

> I fixed the frontmatter crash
Changelog entry added (unreleased) — issue #12 resolved

> /handoff
Sprint set (1 item); handoff prompt stored for the next session

# next morning, new session
> /pickup
Handoff: "Frontmatter crash is fixed; release when CI is green."
Proposed first action: /release

No forms, no context switch. /handoff and /pickup are Claude Code slash commands; on any other MCP client the same moves are the handoff_work and pickup_work tools. The three loops behind this session — tracking, session, release — are described below.

Installation

Pick one — whichever matches your Python workflow. The distribution is named dctracker (PyPI); the installed command is dct. Prerequisites: Python 3.11+. Claude Code is needed only for the skills/hooks integration; the CLI and MCP server work with any MCP client.

# Option A: uv tool (recommended — isolated, upgradable)
uv tool install 'dctracker[mcp,pg]'
# Extras: mcp = the MCP server (you want this), pg = the PostgreSQL driver
# (skip it if you stay on the SQLite default), web = the local dashboard.

# From source (dev):
uv tool install --editable /path/to/dct --with 'mcp' --with 'psycopg[binary]'
# Or straight from GitHub, no clone (e.g. to try main before a release):
uv tool install 'dctracker[mcp,pg] @ git+https://github.com/fotodeveloper/dct'

# Option B: pipx (classic isolated install)
pipx install 'dctracker[mcp,pg]'

# Option C: uvx (zero-install, fetches per-invocation) — server-only: pair it
# with Option A/B if you want `dct init`, the CLI, and the hooks (they need
# `dct` on PATH). CLI equivalent: uvx --from 'dctracker[mcp,pg]' dct init
# In ~/.claude.json:  { "command": "uvx", "args": ["--from", "dctracker[mcp,pg]", "dct", "server"] }

# Option D: pip into your venv (not recommended for global CLI)
pip install 'dctracker[mcp,pg]'

After installing, dct must be on your PATH. Verify:

dct --version    # -> dct <version>
dct server --help
dct plan --help  # plan subcommands

First run

dct init

The interactive wizard walks through the one-time global bootstrap:

  1. Database backend — sqlite (default, zero setup at the platform data dir, e.g. ~/.local/share/dctools/dct.db) or postgresql (you supply the URL). It test-connects before saving.
  2. Register projects — for each project it asks slug / path / name, then auto-detects the version from VERSION, pyproject.toml, package.json, Cargo.toml, or src-tauri/tauri.conf.json. If none matches, you get an interactive prompt (0.0.1 / 0.1.0 / 1.0.0 shortcuts); in non-tty contexts it stores current_version=NULL and reminds you to set it later. Register more any time with dct project add.
  3. MCP server — registered at user scope in ~/.claude.json (via claude mcp add --scope user, with a direct-edit fallback), so /track, /clog, /release, and the MCP tools are available in every Claude Code session, from any directory.
  4. Skills + hooks — symlinks the skills into ~/.claude/skills/ and the four hooks into ~/.claude/hooks/. Both ship inside the package; with an editable/dev install the symlinks point at the repo, so a fix goes live without reinstall.
  5. Hook wiring — with your consent, merges the hooks into ~/.claude/settings.json. The merge is idempotent, backs up the file before the first write, and never touches hooks you already have. Re-run any time with dct hooks wire.
  6. CLAUDE.md snippet — with your consent, appends the packaged dct instructions to your personal ~/.claude/CLAUDE.md. This is what makes plain phrases like "track this" reliable. The block is marker-managed and idempotent: re-runs refresh it, your own content is never touched, and the file is backed up before the first write. Decline and paste it later from dct/assets/claude-md-snippet.md.

The wizard closes with a reminder to start a new Claude Code session — skills, hooks, and MCP tools all load at session start. Steps 3–6 are the Claude Code integration; without Claude Code, use the wizard for steps 1–2 and register the server in your own client instead — next section.

dct init writes the config to the platform config dir (~/.config/dctools/dct.toml; existing ~/.claude/dct.toml installs keep working — dct migrate-home moves them), ~/.claude.json (MCP entry), the skill/hook symlinks, and (opt-in) the settings.json wiring — see docs/architecture.md for the full config reference and scope model.

Other MCP clients (Cursor, Windsurf, Zed)

The 13 skills and 4 hooks are Claude Code integrations; everything else — the 55 MCP tools and the CLI — works from any MCP client. Register dct server as a stdio server in your client's MCP config, e.g. for Cursor (~/.cursor/mcp.json):

{ "mcpServers": { "dct": { "command": "dct", "args": ["server"] } } }

Then talk to your assistant as usual ("track this", "what's open?") — it drives the same tools. Slash-command equivalents: /track → create_item, /track-list → list_items, /pickup → pickup_work, /handoff → handoff_work, /clog → add_changelog, /release → release_changelog. To teach your assistant when to reach for them, adapt the CLAUDE.md snippet to your client's rules file.

Troubleshooting

  • dct: command not found after install — the install location isn't on your PATH. For uv tool / pipx, run their ensurepath helper (uv tool update-shell or pipx ensurepath) and restart the shell.
  • PostgreSQL connection fails at dct init — either start your local Postgres and re-run, or just pick the SQLite default (the answer to the backend prompt) — it needs no server and works everywhere. If the error mentions psycopg or a missing module instead, reinstall with the pg extra: uv tool install 'dctracker[mcp,pg]'.
  • MCP tools don't show up in your client — the MCP server is a per-session subprocess, so newly registered tools (or a reinstall) appear only after you restart the session — in Claude Code and any other MCP client alike. Start a fresh session and they'll be there.
  • Plans/decisions tools fail with No module named 'ulid' — your installed tool environment predates the python-ulid dependency. Upgrade it: uv tool upgrade dctracker (or reinstall via your chosen option above).

Updating

dct runs a passive, offline-safe update check — a cached PyPI probe (refreshed at most once a day, never blocking) surfaces a one-line "newer release available" nudge on the CLI and in the MCP server instructions. Opt out with [update] check = false in the dct config file (dct config path shows where it lives). When a newer version exists:

dct upgrade    # detects install method (uv tool / pipx / pip), upgrades, re-links skills + hooks

dct upgrade refreshes the skill/hook symlinks and prunes any that the new package dropped. Editable/dev checkouts print git pull guidance instead (no package to upgrade). Run it at a session boundary — skill descriptions and the MCP server pick up the new code on the next session, migrations on the next engine bootstrap.

The tracking loop: /track → work → /track-resolve

The foundation everything else builds on. An item is one unit of work — an issue, todo, feature, idea, or improvement — with a priority (P1–P3, future), append-only notes, and checkpoints: acceptance criteria your assistant sketches before coding and checks off in real time as it works. Say "track this: parser crashes on empty frontmatter" and the item exists; say "done with #12" and it resolves, with a resolution note in the ledger. Because every project writes to the same database, "what's open everywhere?" is one query, not a tour of a dozen TODO.md files. Illustrative cross-project view:

> /track-list P1
myproj   #12 issue P1 in_progress [2/4]  parser crashes on empty frontmatter
website  #31 todo  P1 open               rotate API keys before launch

Checkpoints can also be gates — dogfood, security, review, approval, or any custom kind you invent — that block an item from resolving until each is done or consciously rejected: a feature is finished when the database says so, not when the conversation has moved on.

The session loop: /handoff → /pickup

AI coding sessions end; your context shouldn't.

Ending a session — /handoff flags the items you were working on as the current sprint and stores a short prompt for whoever comes next (usually you, tomorrow). Starting a session — /pickup reads the newest unconsumed handoff, shows the sprint with checkpoint progress, and proposes the first action, so the session starts in the middle of the work rather than from a cold "what were we doing?" The sprint is the durable set of what's in flight right now: it survives between sessions and cleans itself up — resolving an item drops it from the sprint automatically. Illustrative session start:

> /pickup
Handoff (2026-07-05, scope: sprint-15):
  "Theme tokens are merged; wire the toggle into settings next.
   The Safari session fix (#12) still awaits review."
Sprint: 3 items — #12 in_progress [2/4], #31 open, #7 open
Proposed first action: #12 — wire the theme toggle into settings.

Handoffs live in the database: several can coexist per project (scope tags disambiguate parallel workstreams), consuming one preserves it in the ledger, and an unconsumed one can be amended in place. Available as skills in Claude Code, MCP tools, and the CLI — full catalogue in docs/mcp-tools.md.

The release loop: /clog → /commit → /release

A changelog reconstructed from git log on release day is archaeology — dct makes it a side effect of working. Each time a change lands, your assistant records a one-line entry in the database (the /clog skill, add_changelog over MCP, dct clog add in scripts). A Claude Code hook — wired with your consent at dct init, then on by default for every registered project — can even hold back git commit until the entry exists (dct project changelog <slug> off opts a project out). By release day the changelog is already written:

> /release
15 unreleased entries → stamped as 1.4.0
changelog.md regenerated (Keep a Changelog format)
release: v1.4.0 committed, project version bumped

One command stamps the version, exports Keep a Changelog markdown, and makes the release commit.

Plans: your markdown, indexed

You already write plans — specs, roadmaps, ADRs — in markdown. dct indexes them instead of replacing them: prose stays in the file (still the source of truth), while sections, checkpoints, and inline decisions get stable ULID anchors and live state (pending/done/rejected) in the database. In Claude Code a background hook re-scans the file on every edit; elsewhere, run dct plan rescan <file>. Uncertain section headings can be classified by an optional headless LLM call through your own claude CLI. A plan checkpoint can be promoted to a tracked item, a section to an item or a whole sprint — planning and tracking stay one system.

Full guide — concepts, ULID anchors, ingest pipeline, classification rules, and promotion to items/sprints: docs/plans.md.

Features

  • Centralized tracking — one database for all your projects
  • Session handoffs — end a session with a stored prompt; /pickup resumes the next one mid-work
  • Hybrid changelog — entries in DB, export to Keep a Changelog markdown at release; enforcement hook wired at init with your consent, then on by default per project (opt out via dct project changelog <slug> off) + per-project changelog location (repo root by default, or a subdir like pkg/changelog.md)
  • Plans, specs, roadmaps, ADRs — index markdown plans with stable ULID anchors; state in DB, content stays in markdown
  • First-class sprints — a named, goal-scoped current work set that survives across sessions and self-cleans as items resolve
  • Decision records — ADRs, sprint amendments, inline plan decisions with multi-anchor linkage
  • LLM-assisted classification — uncertain plan sections auto-resolved via claude --print (haiku by default; optional, uses your own claude CLI)
  • Passive autoscan hook — Claude Code PostToolUse hook re-scans plan-like files in background on every edit
  • MCP server — 55 tools over stdio via the official MCP SDK 2.0; works with any MCP client (Claude Code, Cursor, Windsurf, …)
  • CLI — fast dct command for hooks and scripts
  • Skills — Claude Code slash commands (/track, /clog, /dct-import, /plan-status, …)
  • Soft deletes — ledger pattern, nothing is ever hard-deleted
  • DB portable — SQLite (zero setup) or PostgreSQL

Usage

CLI

# Items
dct add myproj todo "Fix parser crash" --priority P1 --tags "parser,crash"
dct list                              # All open items, all projects
dct list myproj --priority P1         # P1 items in myproj
dct list myproj --tag parser          # Items carrying the "parser" tag
dct show 42                           # Full item details with notes
dct update 42 --status in_progress
dct update 42 --tags "perf,arch"      # Full replace of the tag set (--tags "" clears)
dct note 42 "Found root cause in tokenizer"
dct resolve 42 --resolution "Fixed in commit abc123"

# Tags (global dictionary, shared by every project)
dct tags list
dct tags add perf --description "performance work"
dct tags merge test tests             # Fold "test" into "tests"

# Changelog (project auto-detected from CWD; --project overrides)
dct clog add added "New Python parser support" --project myproj
dct clog list --project myproj --unreleased
dct clog count --project myproj       # Used by hooks
dct clog release 1.1 --project myproj # Stamp unreleased → 1.1
dct clog export --project myproj --output changelog.md

# Projects
dct project add myproj /path/to/myproj --name "My Project"    # Version auto-detected
dct project list
dct project detect                                            # Print current project slug (for hooks)
dct project rename myproj --slug myproj2 --name "Rebranded"   # Rename in-place, project_id stable
dct project set-version myproj 1.1                            # Post-factum version fix
dct project changelog myproj off      # Opt out of changelog enforcement (on by default)
dct project changelog-path myproj docs/changelog.md           # Changelog outside repo root; default: changelog.md

# Plans
dct plan rescan /path/to/docs/plans/2026-04-17-feature.md     # Idempotent rescan; hook entrypoint
dct plan rescan $FILE --quiet --resolve-uncertain             # Hook-mode flags

# Checkpoints (item-level)
dct checkpoint add 42 "Write parser"
dct checkpoint list 42
dct checkpoint done 3                 # Mark CP #3 as done
dct checkpoint reject 4               # Scope-change, keep in ledger

# Sprints (first-class, named units of work; /handoff manages the current-sprint set)
dct sprint list --status active
dct sprint create "Payments hardening" --code pay-1 --goal "Close the P1 backlog"
dct sprint show 3                     # Details + linked items
dct sprint activate 3                 # Shortcuts: activate / complete / defer

# Session edges (same composed ops the /pickup and /handoff skills use)
dct pickup                            # Snapshot: sprint, handoffs, gates, decisions
                                      # (read-only — --consume defaults to never)
dct handoff create --prompt-file - --add-items 22,6   # Sprint change + handoff, one transaction
                                      # --remove-items cuts scope, --items replaces;
                                      # omit all three to leave the sprint untouched
dct handoff update 12 --prompt "Wire the toggle into settings next"

# Config
dct config get integrations.claude.task_mirror   # Read a dotted config key
dct config path                       # Show resolved config/data locations

MCP Tools

dct ships 55 MCP tools. Zero-arg calls auto-detect the project from the session's working directory; pass project=<slug> to override. Write ops return a minimal ack (id + a few post-state fields); fetch full context with get_item(id) / get_handoff(id). In Claude Code the tools register at user scope (callable as mcp__dct__<name>), with thirteen daily drivers always loaded and the rest fetched on demand; other MCP clients see the full flat catalogue. Whatever the client, start with create_item, list_items, pickup_work, and handoff_work:

Tool Purpose
create_item Create a tracked item (issue / todo / feature / idea / improvement)
get_item Full item dict — fields, notes, checkpoints, progress
list_items List / search items in compact {cols, rows} form
update_item Update fields on an existing item
resolve_item Close an item (terminal status), with gate-checkpoint guard
add_note Append a note to an item (append-only ledger)
add_checkpoint Add an acceptance criterion to an item
complete_checkpoint Mark a checkpoint done (real-time, never batched)
reject_checkpoint Mark a checkpoint rejected (scope change) — ledger-preserving
add_changelog Add an unreleased changelog entry

Session-edge tools stay deferred: get_handoff (full handoff dict — untruncated prompt + context_summary), create_handoff, set_sprint, release_changelog, and more. Full categorized catalogue: docs/mcp-tools.md.

Web viewer

dct web runs a localhost-only, read-only browser dashboard over all registered projects (items, sprints, plans, changelog, decisions, handoffs, plus Radar / Backlog-Health / Velocity analytics) with live SSE refresh. It binds 127.0.0.1 only — no auth, single-user. Install the optional extra first:

uv tool install 'dctracker[web,mcp,pg]'   # or: pipx install 'dctracker[web]' — adds flask + markdown
dct web start               # start the daemon → prints http://localhost:8787
dct web start --open        # open the browser after starting
dct web status              # show pid + URL
dct web stop                # SIGTERM the daemon

If the preferred port (default 8787) is busy, the daemon auto-selects the next free port (disable with --no-fallback). Configure defaults under [web] in the dct config file.

dct web viewer — read-only dashboard over all registered projects

Integration with Claude Code

Claude Code is where dct runs deepest. dct init offers to attach the ready-made snippet that teaches your assistant when to reach for each skill and MCP tool (step 6 of the wizard); to add it manually, copy dct/assets/claude-md-snippet.md into your personal ~/.claude/CLAUDE.md.

Skills

Skill Purpose
/track Create a tracked item
/track-list List/search items
/track-update Update item fields
/track-resolve Resolve/close items
/clog Add changelog entry
/commit Conventional commit with version suffix + changelog gate
/release Stamp changelog + bump version + generate changelog.md
/pickup Session start — read handoff, show sprint, propose first action
/handoff Session end — flag sprint items, write handoff prompt
/dct-import Discover + multi-select + ingest plan-like markdown files
/dct-init Bootstrap dct + register the current project (interactive)
/plan-status Cross-plan progress dashboard
/plan-resolve-uncertain Batch classify uncertain plan sections via haiku

Global Hooks

dct init symlinks four hooks into ~/.claude/hooks/ (→ the packaged dct/hooks/; an editable/dev install keeps them live-editable from the repo) and — with your consent — wires them into ~/.claude/settings.json automatically. The wiring is an idempotent merge: hooks you already have (dct or your own, in any placement) are never touched, and the file is backed up before the first write. Re-run any time:

dct hooks wire            # idempotent; safe after upgrades or manual edits
Hook Event Purpose
dct-check-changelog.sh PreToolUse (Bash) blocks git commit when app code changed but no unreleased changelog entry exists — once the hook is wired, enforcement is on by default per project; opt out via dct project changelog <slug> off
dct-check-changelog-on-stop.sh Stop session-end reminder about pending unreleased entries (same per-project gate)
dct-plan-autoscan.sh PostToolUse (Edit|Write|MultiEdit) background rescan of plan-like MD files; fire-and-forget
dct-task-mirror.sh PreToolUse (TodoWrite|TaskCreate) mirrors Claude Code tasks into dct as cc-task checkpoints

Manual wiring reference (full JSON) and the five rules every dct hook follows: docs/hooks.md.

Configuration

dct reads its config from the platform config dir — ~/.config/dctools/dct.toml on Linux and macOS, %LOCALAPPDATA%\dctools\dct.toml on Windows — with a permanent fallback to a legacy ~/.claude/dct.toml (run dct migrate-home to move an old install). The minimal config is just the database URL:

[database]
url = "sqlite:////home/you/.local/share/dctools/dct.db"
# or: url = "postgresql+psycopg://localhost:5432/dct"

The DCT_DATABASE_URL environment variable overrides [database].url. For the full reference ([classification], [integrations.claude], [web] sections), the database schema, MCP scope precedence, and project auto-detection mechanics, see docs/architecture.md.

Testing

.venv/bin/pytest -q                    # All tests
.venv/bin/pytest tests/test_plan_parser.py -v          # Parser only
.venv/bin/pytest tests/test_plan_ingest.py -v          # Ingest + sticky-resolved semantics

# MCP server smoke (boot + tool list)
uv run --no-project --with 'mcp' --with sqlalchemy --with 'psycopg[binary]' \
  python -c "import asyncio; from dct.mcp.server import mcp; \
  print(len(asyncio.run(mcp.list_tools())), 'tools')"

Contributing

Bug reports, feature ideas, and PRs are welcome — see CONTRIBUTING.md for dev setup (uv venv + pytest), commit conventions, and how to add MCP tools or skills.

License

MIT — see LICENSE.

Metadata

Release files for dctracker 1.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for dctracker 1.5.0
File Size Uploaded
dctracker-1.5.0.tar.gz 440.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for dctracker 1.5.0
File Interpreter ABI Platform
dctracker-1.5.0-py3-none-any.whl Python 3 none any Details

Total release size: 754.1 kB

Release files / dctracker-1.5.0.tar.gz

Download URL dctracker-1.5.0.tar.gz
Size 440.5 kB
Tags Source
SHA-256 checksum
How to use checksums
93835e71d5792338bf16fc593c8792f1a1f56cc5b54c889f58d36db87ff47599
BLAKE2b-256 checksum
How to use checksums
efc4a5a6e9f9c2a9605122ae2fd88cf68029120f6a94cc1eb0abfb5d395355d0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 1, 2026.

Transparency log

Release files / dctracker-1.5.0-py3-none-any.whl

Download URL dctracker-1.5.0-py3-none-any.whl
Size 313.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8a7c8bf3e6cf0bd5f8115a71a1c1c4d2a5917828e00666ceec07dcfe567ed12f
BLAKE2b-256 checksum
How to use checksums
1418474952ab4b84c3a039c96c2c497959348affd6418d2a0679c377e5abd6df
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 1, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.5.0 This release

2 release files

1.4.0

2 release files

1.3.0

2 release files

1.2.1

2 release files

1.2.0

2 release files

1.1.0

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page