Ctrl K
ComunidadGuías…

CHANGELOG.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Changelog

All notable changes to Titen are recorded here.

The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and
this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

Titen is in `0.x`: per SemVer clause 4, the public API is not yet stable. Below
`1.0.0` a **minor** bump carries breaking changes and a **patch** carries
everything else — any breaking entry appears under **Changed** and names the
migration. Released versions live on npm under `latest`; prereleases
(`0.2.0-rc.1`) under `next`. `main` may be ahead of both; see
[versioning and channels](./docs/engineering/release.md#versioning-and-channels).

The word **stable** around Titen — the npm `latest` dist-tag and
`"channel": "stable"` in [`titen.dev/version.json`](https://titen.dev/version.json)
— names the release *channel*, meaning a deliberate release rather than a
prerelease. It never describes API stability; the paragraph above does.

Every release heading below is dated in **UTC**, matching the npm registry
`time` field for that version. A heading date and
`https://registry.npmjs.org/titen-memory` must agree.

The published npm package is [`titen-memory`](https://www.npmjs.com/package/titen-memory).
The **CLI command is `titen`** regardless; see [Package name](#package-name).

## [Unreleased]

## [0.10.15] — 2026-11-11

schema 32

This release changes enrichment readiness: only a failure that an operator can fix reports `terminal_error`. A rejected model output gets one automatic second run. It adds no migration.

### Changed

- `/readyz` now reports `enrichment_jobs: terminal_error` only for a failed job that an operator can fix. These are the classes `provider_unavailable`, `provider_rejected`, and `provider_protocol`. A failed job with the class `unsafe_output`, `invalid_output`, or `source_changed` no longer marks readiness. The job stays `failed`. `titen jobs list`, `retry`, `ack`, and `purge` still select every failed job.
- A job that fails with `unsafe_output` on its first attempt now returns to `pending` one time. The model output varies between runs, so the second run often passes. The first failed run records no output hash. A second `unsafe_output` failure is terminal and records the hash of the second output.

## [0.10.14] — 2026-11-11

schema 32

This release fixes `titen jobs retry`: a retried enrichment job can now finish. It adds migration 32.

### Fixed

- A retried enrichment job can now record a new output. `titen jobs retry` clears the output hash of the job. Before this fix, the next run recorded a different hash and the database rejected it, so the job stayed leased and stopped the pass. Migration 32 replaces the trigger `enrichment_jobs_output_immutable`. The new trigger allows only the retry reset to `NULL`. Migration 32 also clears the output hash of each `pending` or `leased` job that an earlier retry left behind.

## [0.10.13] — 2026-11-11

schema 31

This release closes 1 issue: the dashboard Approvals queue now shows what an approver decides. It adds no migration.

### Fixed

- Dashboard Approvals now shows what an approver is deciding and why a decision is refused. Policies list their claim kind, minimum trust, and independent-approval setting, and disabled policies are de-emphasized. The approval queue shows each submission's reason and policy, and **Review evidence** opens the claim's Evidence trace in Atlas. The decision confirmation shows the submitter and submission reason. Under an independent-approval policy, the submitter no longer sees an **Approve** or **Reject** that the server always refuses; the row says another approver must decide. If the server does refuse a submitter's decision, the message names that rule instead of the generic "not authorized". The adapter maps that refusal to its own fixed wording. ([#407](https://github.com/RamaAditya49/titen/issues/407))

## [0.10.12] — 2026-11-10

schema 31

This release closes 3 issues: the purge cascade no longer redacts a claim that only cites the purged evidence as a contradiction, and the dashboard fixes the Releases approval view and the reference panes. It adds no migration.

### Fixed

- `DELETE /v1/observations/:id` no longer redacts a claim that cites the observation only as `contradicts`. Before, a purge redacted and revoked every claim that cited the observation in any relation. A disputed claim lost its statement when someone purged its contradicting evidence, although its supporting evidence was intact. Now only claims that cite the observation as `supports` or `qualifies` are redacted and revoked. The `dependent_claims_redacted` count follows the same rule. A legal hold on any claim that cites the observation, in any relation, still blocks the purge. The fix works forward only. A claim that an earlier purge already redacted cannot be restored. ([#404](https://github.com/RamaAditya49/titen/issues/404))
- Dashboard Releases now shows what an approver is publishing. The list has a Content column and channel labels, and the approve, activate, and revoke confirmation shows the full released text with its locale, validity window, proposer, and proposal reason. A release's proposer no longer sees an **Approve** that the server always refuses; the row says another approver must approve. If the server does refuse for separation of duties, the message names that rule instead of the generic "not authorized". The adapter maps the release proposer refusal to its own fixed wording. ([#401](https://github.com/RamaAditya49/titen/issues/401))
- Dashboard Subjects and Projects reference panes no longer hang on "Loading authorized references…". The dashboard adapter encoded the ID a second time, so a subject such as `user:alice` was requested as `user%253Aalice` and got a 404. The adapter now decodes the path segment once before re-encoding it, and rejects a malformed escape with 400. A failed request now shows an error in the pane, and a subject or project with no references says so by name. ([#403](https://github.com/RamaAditya49/titen/issues/403))

## [0.10.11] — 2026-11-09

schema 31

This release closes 5 issues: dashboard usability in Access, Models, Work, and Federation, SDK types for the workspace graph, and the event index on stores with statistics. It adds no migration.

### Added

- `GET /v1/keys` accepts `issued_by`, which returns the keys a principal issued, including derived keys clamped by that principal's grants. The SDK's `listKeys` passes it through.

### Fixed

- The SDK's `compileView` types now match the server. `ViewLens` includes `workspace_graph`, and the options accept `workspace_id` and `max_nodes`, so TypeScript callers can request the workspace graph without casting to `any`. The SDK type contract now pins `ViewLens` to the server's lens list, and `expire`, `acquireLease`, `releaseLease` and `compileView` gained round-trip tests against a real server. ([#392](https://github.com/RamaAditya49/titen/issues/392))
- Webhook and event scans now always read `event_order` through the per-organization `event_order_org_seq` index. Once SQLite had statistics (`ANALYZE`), it preferred the global `seq` rowid range. Each maintenance pass, webhook scan, `/v1/events` listing and federation pull for a quiet organization then walked every other organization's events, which #361 had meant to stop. The queries pin the index with `INDEXED BY`, which also fails loudly if the index is renamed. A query-plan test now checks the plans on an analyzed store with a busy neighbouring organization. ([#396](https://github.com/RamaAditya49/titen/issues/396))
- The dashboard's Access key clamp preview no longer lists every key the organization ever issued. It appears once a principal is selected and requests the active keys the selected principal holds or issued, through the `status`, `principal_id`, and `issued_by` filters of `GET /v1/keys`, which the adapter now forwards. It shows a Status column, starts with ten rows, and offers "Include expired and revoked (N)". The grant form and the access simulator stay within reach instead of sitting thousands of pixels down the page. ([#391](https://github.com/RamaAditya49/titen/issues/391))
- Dashboard Models no longer offers probes that cannot succeed or reports the wrong cause when one fails. Probe buttons are disabled for a group that is not enabled and name the variables that enable it. A ten-second countdown follows each probe. A probe that is rate-limited now says to wait instead of "Too many sign-in attempts", and a probe of a provider that is not enabled says so instead of "Titen is not ready". The adapter maps these known server codes to its own fixed wording and still forwards no upstream text. Its generic 429 message no longer mentions sign-in. When no model is configured, the generated restart configuration reads FTS-only instead of a lone response-mode variable. ([#395](https://github.com/RamaAditya49/titen/issues/395))
- Dashboard Work and Federation lookups no longer wipe the area's lists. **Find checkpoint** and **Load peer log** render in their own region, with a heading that echoes the query, so leases, handoffs, and peers stay visible. A miss reads "No current checkpoint for subject …, agent …, kind …" or "No exchange log for peer …" instead of "The authorized resource was not found". Checkpoint kind is now a choice of the four supported kinds. Handoff rows offer **Find checkpoint** and peer rows offer **View log**, and the Agent ID and Peer ID fields suggest IDs already loaded. ([#399](https://github.com/RamaAditya49/titen/issues/399))

## [0.10.10] — 2026-11-08

schema 31

This release closes 29 issues: query cost on large stores, dashboard usability and accessibility, and correctness fixes.

### Upgrade notes

- Migrations 28 to 31 run on start, or run `titen migrate --db <path>`. Take a backup first. Migration 29 backfills `event_order.org_id` for every event, and migration 30 indexes every active channel release, so the first start on a large store takes longer.
- Migration 28 adds `enrichment_jobs.source_observation_id`. Migration 29 adds `event_order.org_id` and `webhooks.scanned_seq`. Migration 30 adds the `channel_release_fts` index. Migration 31 adds the `observations_project` and `claims_project` indexes.
- Webhook scans now move forward only. An event that the webhook owner cannot read when it is first examined is not delivered later.

### Changed

- Context compile no longer scans a subject's entire observation history to report `budget.unconsolidated_observations`. One statement now inspects the 500 newest caller-readable observations at or before the compile's `as_of` and sets `budget.unconsolidated_observations_capped: true` when that window is full. On a subject with 200,000 unconsolidated observations, median compile latency on Bun dropped from 382 ms to 2.9 ms (2.4 ms for a subject with none). Observations ingested after a historical `at` are no longer counted (#339).
- The derivation backfill that runs on every enrichment drain no longer scans every derivation job for each observation. Migration 28 adds a `source_observation_id` column and an index, so a steady-state pass over 20,000 observations takes about 12 ms. ([#376](https://github.com/RamaAditya49/titen/issues/376))
- Event scans now cost the new events of one organization, not every event ever written in the deployment. `event_order` carries `org_id` with an `(org_id, seq)` index, and each webhook keeps a `scanned_seq` mark that moves forward with each delivery pass. `GET /v1/events`, webhook delivery, and federation pull read only the caller's organization above their cursor or mark. An event the webhook owner cannot read when it is first examined is not delivered later. The API and cursor rules are unchanged. ([#361](https://github.com/RamaAditya49/titen/issues/361))
- `GET /v1/projects` and `GET /v1/subjects` choose the page first and count records only for the returned entries. Cost now follows the page size, not the organization size. Counts, `last_write`, and omission of entries with no readable records are unchanged. Migration 31 adds the `observations_project` and `claims_project` indexes, which also serve the project reference checks ([#338](https://github.com/RamaAditya49/titen/issues/338)).
- `POST /v1/channels/:id/context/compile` now ranks eligible releases by relevance to `task`, falls back to newest activation first, applies the 200-release bound after ranking, and reports `omitted_items` and `budget_exhausted`. Migration 30 adds a release search index and backfills active releases ([#381](https://github.com/RamaAditya49/titen/issues/381)).
- `GET /v1/keys` can reach every key in the organization. It was capped at the 500 oldest keys with no cursor, so once dashboard sign-ins had filled that window, newly issued keys never appeared. The response now carries `data.next_cursor`, and the endpoint accepts `cursor`, `limit` (1-500, default 500), `status` (`active|pending|expired|revoked|all`) and `principal_id`. Filters run before the limit, and fences are loaded only for the returned page. The default first page is unchanged. The SDK's `listKeys(options)` passes these through and returns `next_cursor` (#340).
- Dashboard destructive actions confirm in one shared dialog instead of the browser's `confirm()` and `prompt()`. The dialog names the target record, its impact, and whether it can be undone. Cancel is focused by default, and required reasons go in a labelled multi-line field. Release activation now asks for confirmation too. The API keys table shows Created and Last used columns and a key-id suffix, and marks **This session**, so identical "Dashboard session" rows and their Revoke buttons are distinguishable. Revoking your own session warns first and then says you revoked it (#356).
- The dashboard Context form is laid out like the other forms, with labels above shared-style inputs at every width. A compiled pack now says why it is empty or short. It shows unconsolidated observations with the API's `titen_consolidate` hint, items that did not fit the token budget with their reasons, or that no authorized claims matched. The status line reports the item count. The Subject ID field suggests authorized subjects, and Subjects rows offer **Compile context** (#363).
- Dashboard System status no longer shows failing readiness checks in the same plain style as passing ones. Checks render as a status list with failing checks first, each with a text status and a non-colour marker. The service notice names the failing checks. `enrichment_jobs` reads as words instead of raw JSON and points to `titen jobs list --status terminal_error` on a terminal error. Each optional capability is listed once. ([#383](https://github.com/RamaAditya49/titen/issues/383))

### Fixed

- `tz` local-time fields no longer fail on historical Local Mean Time offsets that carry seconds. Such offsets are rounded to the minute with the wall clock shifted, so each `*_local` value keeps the UTC instant, and a value that cannot be presented becomes `null`. Before this, `POST /v1/observations` with an archival `occurred_at` returned 500 after the write committed, and a retry stored duplicate evidence (#337).
- Dashboard collection tables (API & Keys, Access) no longer hide their row actions at desktop widths. A collection narrower than its table now shows each row as a label/value record, as on phones, so "Revoke" stays visible in half-width split columns from 961px to 1440px. Status and trust values no longer break mid-word. Any table that still scrolls sideways is a named, keyboard-focusable region with edge shadows (#343).
- `GET /v1/memories` applies the claim validity window per row: `active` and `disputed` mean currently valid, whichever other statuses are requested. `status=active,superseded` no longer brings back expired or not-yet-valid active claims, and `facets` now use the same predicate, so `facets.status.active` matches what `status=active` lists. Facets and the validity rule are documented (#347).
- On laptop-height screens the dashboard sidebar shows every navigation group. At 1280x720, Administration and Governance used to sit below a 190 px credential footer with no scroll cue. The footer is now one row with identity and Disconnect, and the credential explanation sits in a disclosure. The nav rhythm tightens below 820 px of height, keeping targets at least 26 px. The nav shows scroll shadows when it overflows and scrolls the active area into view (#350).
- The dashboard Memories list shows one empty state that matches the situation. With no search or filter, it says no memory is visible yet and that agents add memory through the REST API or MCP. With filters, it names each active search, status, and kind filter and offers **Clear search and filters**. The live status line now reports counts only, so it no longer contradicts the visible message, and the pagination footer is hidden while the first page is empty (#352).
- Text with Windows line endings is accepted on every write path (REST, `titen_remember`, and the MCP compatibility tools) instead of failing with "unsafe control character". Each CRLF is stored as LF before validation and hashing, so the same note sent with LF or CRLF and the same `source.id` replays to one record. `titen import` applies the same rule, so markdown sources saved with Windows line endings import exactly like their LF copies. A carriage return not followed by a line feed is still rejected, now with a message that names U+000D. All other #108 controls stay rejected (#354).
- `titen import` no longer splits a long entry inside a surrogate pair or grapheme cluster. Entries over 4,000 characters with no nearby whitespace are now cut at the last grapheme boundary, so emoji and ZWJ sequences stay whole. Each chunk is also checked during prepare, so the preview reports a bad chunk instead of `--apply` stopping halfway with "must contain well-formed Unicode" (#358).
- A wrong password on the dashboard sign-in screen now says "Username or password is invalid." instead of "Your dashboard session is no longer valid", and a used or mistyped recovery code says so. The failed field is marked `aria-invalid`, linked to the message, and focused. After three failures in the tab, a line repeats the documented delay rule without revealing the account's state. Switching to the passkey or recovery-code panel clears the old message. The session-expiry message still appears for a 401 on an authenticated request (#360).
- Memory Atlas `workspace_graph` no longer returns 500 on Cloudflare D1 when the view holds 50 or more claims or 100 or more subjects. The subject and link lookups are now chunked under D1's 100-parameter limit, and links are filtered to the selected claims in memory, with the same output and order. Bun was not affected (#364).
- Dashboard Audit & Events now loads when opened and shows the newest audit entries and events first. It follows the API cursor in a bounded walk, pages with Newer and Older, and says when a list is partial instead of presenting the oldest page as the whole log. The filter form submits on Enter or through "Apply filters" and suggests known action names and event kinds. ([#367](https://github.com/RamaAditya49/titen/issues/367))
- `GET /v1/audit/export` now pages on the same `(created_at, id)` key it orders by. Because audit ids are random, the old id-only cursor skipped some entries and repeated others across pages while still reporting `complete: true`. Following `next_cursor` now returns every entry of the organization exactly once, in the same order as `GET /v1/audit`. An `after` value that names no audit entry in the caller's organization now returns `400 VALIDATION_ERROR` instead of a page that could read as a finished export. ([#368](https://github.com/RamaAditya49/titen/issues/368))
- The dashboard no longer offers passkey and recovery-code controls that cannot work. When readiness reports WebAuthn disabled, Profile hides passkey registration, last-passkey removal, and recovery-code regeneration and names the `TITEN_WEBAUTHN_*` settings that enable them, and the sign-in screen hides "Use passkey or recovery code". A passkey or recovery request that fails because WebAuthn is unavailable now says so instead of "The operation conflicts with current canonical state." ([#371](https://github.com/RamaAditya49/titen/issues/371))
- The `titen mcp` stdio bridge now relays JSON-RPC batches the way `POST /mcp` answers them. A batch on stdin produces the server's response array as one line. A notification-only batch produces nothing. An `initialize` inside a batch sets the negotiated `mcp-protocol-version` for later requests, and a failed batch yields one error per request id instead of a single `id: null` error the client cannot match. ([#372](https://github.com/RamaAditya49/titen/issues/372))
- The dashboard's "Search or jump to" dialog now does what it says. Enter no longer closes the dialog and discards the query. The dialog lists the areas the principal can open, filtered as you type, and offers to search Memories only when that area is available. Closing it returns focus to the trigger. The shortcut hint reads ⌘K on Apple platforms and Ctrl K elsewhere and is exposed through `aria-keyshortcuts`. At 620px and below the trigger becomes an icon button and no longer covers the breadcrumb. ([#375](https://github.com/RamaAditya49/titen/issues/375))
- Dashboard row actions (Inspect, Revoke, Release, Approve and similar) meet WCAG 2.2 target size (2.5.8). They were 14px tall and sat 8–10px apart. They are now at least 24px tall (44px with a coarse pointer such as touch), and a destructive action starts at the far end of the cell, away from Inspect. Standalone text buttons such as Disconnect also get a 24px minimum. ([#378](https://github.com/RamaAditya49/titen/issues/378))
- Dashboard Work, Federation, Approvals and Release policy now load their authorized data when opened, like the other areas, instead of showing "No live data loaded for this principal" until Refresh. The Subjects and Projects reference panes keep their "Select a … to inspect its references" prompt instead of the shared reset text. ([#380](https://github.com/RamaAditya49/titen/issues/380))
- Webhook delivery now reads the clock for each attempt instead of reusing the start of the drain pass. `X-Titen-Timestamp` is the attempt time, as documented, so a receiver applying the 300-second rule no longer rejects deliveries sent late in a pass with a slow endpoint. Each lease now covers the whole HTTP attempt, so a concurrent drain cannot send the same in-flight delivery again. ([#384](https://github.com/RamaAditya49/titen/issues/384))
- The dashboard's phone navigation rail can be closed without choosing an area. It has a visible "Close navigation" button and also closes on Escape or a tap outside it, returning focus to the toggle. While it is open, the page behind it is inert and focus moves into the rail. Choosing an area moves focus to the main region instead of a button that has slid off-screen. The toggle's `aria-controls` now points to the rail. ([#385](https://github.com/RamaAditya49/titen/issues/385))
- The Bun store now waits up to its 5-second busy timeout when another process holds a lock while it opens. Before, a second `titen` process that started during WAL recovery or a WAL switch failed at once with `database is locked`.
- Regenerating recovery codes now deletes the previous codes explicitly in the same batch, instead of relying on a foreign-key cascade that a connection without `PRAGMA foreign_keys` would skip. Contract tests on Bun and D1 now cover regeneration (old codes rejected, new codes accepted, a passkey required), the passkey list, `GET /v1/principals`, and project and subject references, including 404 for records the caller cannot read. ([#386](https://github.com/RamaAditya49/titen/issues/386))
- The dashboard topbar wordmark now loads Noto Sans Javanese from the dashboard origin, so it renders correctly shaped without a system Javanese font. Assistive technology skips it as decoration. ([#345](https://github.com/RamaAditya49/titen/issues/345))

### Security

- Development dependencies move to patched versions: `astro` 7.3.3 with its `vite`, `devalue`, `smol-toml`, and `source-map-js`, plus `undici` 7.30.1 and `sharp` 0.35.5 through workspace overrides. The published package does not ship these dependencies.

## [0.10.9] — 2026-11-03

schema 27

Import, request bodies, webhook signatures, and the audit cursor now close the open findings of [GHSA-27j7-xc9x-322c](https://github.com/RamaAditya49/titen/security/advisories/GHSA-27j7-xc9x-322c).

### Security

- Import applies the direct-route authority rules. A membership record needs an organization owner or admin, and only an owner may import the `owner` role. A credential without `*` may import keys only for its own principal.
- Request bodies are bounded while they are read. A chunked body above 1 MiB returns `PAYLOAD_TOO_LARGE` without buffering the rest. The Bun server and the dashboard adapter also set a runtime body limit.
- Webhook attempts add `X-Titen-Timestamp` and `X-Titen-Signature-V2`, an HMAC of the timestamp, the delivery id, and the body. Verify V2 and reject a timestamp older than 300 seconds. `X-Titen-Signature` stays for existing receivers.
- An audit cursor from another organization now returns no entries, the same as an unknown cursor.

### Changed

- ADR 0007 records `mcp:call` as a tool-bundle scope. Its behavior does not change.

## [0.10.8] — 2026-11-03

schema 27

A flood of sign-in attempts with new usernames can no longer lock every operator out of the dashboard ([GHSA-5f6j-4xcw-58px](https://github.com/RamaAditya49/titen/security/advisories/GHSA-5f6j-4xcw-58px)).

### Security

- Dashboard sign-in no longer locks out every operator when the shared throttle table is full. Admission evicts unblocked rows with the fewest recorded failures first and keeps active blocks. Only a table full of active blocks returns `LOGIN_RATE_LIMITED`.
- The Bun client limiter replaces its oldest client when it tracks 4096 clients, instead of refusing every new client.

## [0.10.7] — 2026-10-14

schema 27

Compile includes each result that fits the reported token remainder, and names every omitted authorized candidate.

### Fixed

- Compile charges item estimates against the reported `max_tokens`. A result that fits in `max_tokens - used_tokens` is included. Each omitted authorized candidate is listed in `budget.omissions` with `claim_id`, `tokens`, and `reason` (`result_too_large`, `budget_exhausted`, or `count_bound`).

## [0.10.6] — 2026-10-07

schema 27

### Upgrade notes

- Migration 27 adds nullable `enrichment_jobs.acked_at`. A failed job with that timestamp no longer marks `/readyz` `terminal_error`. Restart so migrate runs, or run `titen migrate --db <path>`.
- `titen version` prints `schema_version` on a second line. `titen --version` stays the bare package version.

### Added

- Write-fence and trust denials name the failed check and the values the key may use. `VALIDATION_ERROR` lists every missing field. `subject` and `query` are deprecated aliases of `subject_id` and `task`.
- A write or compile that omits `project_id` fills it when the write fence lists exactly one project and reports `project_id_source: "key_fence"`. An unscoped compile on a multi-project fence includes a scope hint.
- `titen_whoami` and `GET /v1/principal` report allowed trust and fenced projects. `titen_project_resolve` accepts a subject id and suggests close project references. The response never includes the raw key.
- Compile reports unconsolidated observation ids, a budget hint when nothing fits, `meta.degraded.reason`, and `meta.consistent_as_of`. SQL and FTS include commits visible at that timestamp. Vector matches follow the index.
- `titen jobs list|retry|ack|purge` manages failed enrichment jobs. `titen principal reassign` moves private observation and claim ownership and writes an audit event. `POST /v1/principals/reassign` does the same for an owner or admin with `keys:manage`.
- Optional `tz` or `TITEN_DISPLAY_TIMEZONE` adds parallel `*_local` timestamps. UTC fields stay unchanged.
- `/readyz` reports `version` and `schema_version`. The dashboard unit uses `PartOf=` and `Wants=` so a titen restart brings it back.

## [0.10.5] — 2026-10-06

schema 26

Share one memory service across agents with an optional one-call claim, a per-project visibility default, and optional key target fences.

### Upgrade notes

- Migration 25 adds nullable `projects.default_visibility`. Null keeps the private default, so a v0.10.4 database can migrate in place. Set a shared project with `titen project update --db <path> --org-id <org> --reference <owner/repo> --default-visibility organization`.
- Migration 26 adds `api_key_fences` and does not rewrite existing keys. Restart the v0.10.4 process on this build so migrate runs, or run `titen migrate --db <path>`. Existing keys remain unrestricted. The raw secret cannot be shown again, so revoke a key and create a replacement: `titen key create --db <path> --org-id <org> --principal <agent> --subjects castle:profile:alice,castle:shared`. JSONL export does not carry fences; a SQLite backup does.

### Added

- `titen_remember` accepts optional `consolidate: true`, which records an observation and one supporting claim in the same call. Trust and visibility are copied. MCP `initialize` instructions say to consolidate after remember, or to pass that flag, when no enrichment model is configured.
- Projects accept `default_visibility` at create and through `PATCH /v1/projects/:id` or `titen project update`. A write that omits `visibility` uses that default. Existing projects stay private until an operator sets one. An explicit private write into a wider project returns `visibility_warning`.
- API keys accept optional read and write fences for project ids and subject patterns at `POST /v1/keys` and `titen key create` (`--subjects`, `--projects`, and the explicit `--read-*` / `--write-*` flags). A write outside the fence returns 403, including through MCP as a tool error. `titen key list`, key creation, `GET /v1/principal`, and the `key.create` audit detail show the fences. Keys with no fence rows stay unrestricted.

## [0.10.4] — 2026-10-05

Protect dashboard sign-in and provide account recovery during password blocks.

### Upgrade notes

- No schema migration is required.
- Configure verified client headers only through a trusted loopback proxy. See the VPS sign-in runbook.

### Added

- Host account list, unlock, and password-reset commands support operator recovery.
- Password-independent passkey and recovery-code sign-in remains available during password blocks.
- Sign-in audit records and successful-session failure counts show preceding failed attempts.

### Fixed

- Bun limits client attempts and concurrent sign-in password checks.
- Persistent client and account throttles preserve active blocks and reject full storage before password work.
- The API rejects ambiguous raw paths before route matching.

### Security

- Session transactions reject stale account or revoked-session state after a host password reset.

## [0.10.3] — 2026-09-25

### Changed

- Requests to OpenRouter for extraction, embeddings, and the System One gate now
  carry app attribution: `HTTP-Referer: https://titen.dev` and the app name
  `Titen.dev`. Other providers receive no extra headers.

## [0.10.2] — 2026-09-25

Jev now decides reflection links. On a live store it handled 87% of reflection
jobs without the generative model and found more duplicates.

### Added

- The decision gate can now decide reflection links. Set
  `TITEN_EXTRACT_GATE_LANES=reflection` to ask one choice question per premise
  pair in a single request. Confident relations become a link-only proposal
  that passes the usual validator. Code orders supersession links by date.
  Claim synthesis, uncertain answers, and gate failures continue to the
  generative call. `TITEN_EXTRACT_GATE_LINK_MIN_CONFIDENCE` (default `0.8`) sets
  the threshold, and a conflict needs at least `0.9`.

## [0.10.1] — 2026-09-25

Titen is now Jev-ready. An optional decision gate lets a structured decision
model skip the generative extraction call when an observation holds no durable
fact.

### Added

- An optional decision gate runs before derivation. It asks one yes/no question
  in the System One wire format. It works with TypeSafe Jev
  (`https://api.typesafe.ai/v1/systemone`, `jev-1.13.0`) and with Jev on
  OpenRouter (`https://openrouter.ai/api/v1/systemone`, `typesafe/jev-1.13`).
  Set `TITEN_EXTRACT_GATE_URL`, `TITEN_EXTRACT_GATE_MODEL`, and the secret
  `TITEN_EXTRACT_GATE_API_KEY` on Bun or Cloudflare.
- Only a yes probability below `TITEN_EXTRACT_GATE_ABSTAIN_BELOW` (default
  `0.1`) records an abstain without the generative call. Every other answer,
  and every gate failure, continues to the generative call. The gate never runs
  for reflection and never adds, links, or raises trust.
- Partial gate configuration, a `latest` model alias, a gate without complete
  extraction, or an out-of-range threshold or timeout reports
  `configured_error`. A gate change re-keys the enrichment pipeline
  fingerprint.

### Security

- The gate sends observation content to the gate provider. It stays off until
  an operator sets its URL and model.

## [0.10.0] — 2026-08-30

### Added

- Optional WebAuthn passkeys now protect dashboard accounts on Cloudflare and
  Bun/SQLite with exact RP ID and origin verification.
- First passkey enrollment returns eight recovery codes once. Profile can list,
  revoke, and add passkeys or replace the recovery set.
- The dashboard completes passkey assertions through fixed same-origin routes.
  A real browser WebAuthn test uses a virtual authenticator.
- A public-artifact release gate rejects private deployment identifiers in the
  source tree and generated package assets.

### Changed

- Migration 24 adds persistent login throttles, explicit API-key authentication
  stages, WebAuthn credentials and challenges, and recovery generations.
- Password login now applies progressive SQL-backed delays before verification.
  The fifth failure waits 30 seconds. Later delays reach at most 30 minutes.
- Accounts with passkeys receive a 15-minute staged key until passkey or
  recovery verification creates a full dashboard session.
- Dashboard sessions are no longer part of credential portability exports.
- Account-specific Cloudflare configuration and private runtime evidence are no
  longer part of the public repository or npm release surface.

### Security

- Login throttle rows store only a hashed account bucket and bounded timing
  state. Passwords and submitted usernames do not enter throttle storage.
- Challenges bind to organization, account, session, purpose, and expiry.
  Conditional SQL claims prevent challenge and recovery-code replay.
- First passkey enrollment revokes older dashboard sessions. Removing the last
  passkey requires the current password.
- Staged keys have no product scopes. Central route authorization permits only
  the required password-change or second-factor completion operations.

## [0.9.1] — 2026-08-30

### Added

- REST and MCP errors now include bounded support guidance that classifies an
  error as expected, requiring investigation, or a defect candidate.
- Missing project references keep `404 NOT_FOUND` and now identify the expected
  setup action, normalized caller-supplied reference, and create capability.
- Distributed agent skills now reproduce and sanitize a defect, search existing
  issues, and use a draft when GitHub write access is unavailable.
- Codex, Claude, Cursor, OpenClaw, Hermes, and Pi plugin manifests now publish
  the updated issue-triage instructions under new patch versions.

### Security

- Error guidance uses allowlisted constants and never copies request bodies,
  exception details, provider responses, credentials, prompts, or memory content.
- Titen keeps GitHub authentication in the host agent. Security findings use the
  private disclosure path instead of a public issue.

## [0.9.0] — 2026-08-29

The operator dashboard now presents each data area for its specific task. The
REST, MCP, storage, and authorization contracts do not change.

### Added

- Task-specific tables and fact views cover Projects, Subjects, Work, Audit &
  Events, Federation, Access, API & Keys, Approvals, Releases, Models, System,
  and Context.
- Each collection includes an authorized record count, selected-record
  inspector, empty state, and closed technical payload disclosure.
- Projects and Subjects load normalized references in the selected area.
- Desktop and 320 pixel mobile screenshots document the Projects directory.

### Changed

- Work, grant, key, approval, and release actions now appear in the affected
  row. Successful mutations still reload state from the server.
- Federation peers and exchange records now use separate schemas. Audit entries
  and domain events also keep separate operator labels.
- Mobile tables now become linear record lists without horizontal page
  scrolling. The dashboard bundle remains below its 80 KiB gzip limit.

### Security

- The new renderer uses text nodes for authorized response data. It does not
  render response HTML or save private data in browser storage.
- Disconnect, denial, logout, and capability changes clear the new tables,
  inspectors, payload disclosures, and row actions.

## [0.8.7] — 2026-08-16

The Level 6 sidebar now restores the approved workspace-selection hierarchy
without changing the live authorization or request contract.

### Fixed

- The exposed native workspace select is replaced by the mockup-aligned
  two-line trigger, workspace icon, animated chevron, active checkmark, and
  bounded menu explanation on desktop and mobile.
- Selecting an authorized workspace still clears stale results and scopes
  Memories and Workspace Graph requests with the same live `workspace_id`.

### Security

- Workspace names and selection state are cleared when the principal signs out
  or the adapter disconnects; no workspace or credential state enters browser
  storage.

## [0.8.6] — 2026-08-16

The complete Level 6 operator surface now runs on live, scoped contracts in
both runtimes, with package and Cloudflare regressions closed before release.

### Added

- The Astro operator dashboard now implements the complete fifteen-destination
  product map from the approved Level 6 mockup: Atlas, Memories, Context,
  Subjects, Work, Audit & Events, System, Models, Federation, Access, API &
  Keys, Projects, Approvals, Releases, and Profile.
- Atlas adds a bounded `workspace_graph` lens over authorized claim/subject
  nodes and canonical relationships, with stable layout, selection, legend,
  truncation state, and direct Evidence Trace navigation.
- New principal, project, subject, reference, scoped-grant, access-simulation,
  and masked model-diagnostic routes run through the shared Bun/Cloudflare
  contract. Model probes are bounded, rate-limited, metadata-audited, and make
  no canonical write.
- Work, grant/key management, approval, and release controls now execute their
  existing versioned server operations with confirmation and refreshed live
  state. Context shows its token budget; System shows readiness detail.

### Changed

- Canonical reads, writes, approvals, events, Atlas, context, federation, and
  directories now share a visibility-plus-additive-grant boundary. Derived API
  keys are clamped to their declared target and their issuer's current grants
  on every request; `admin` delegation remains bounded to its grant target.
- Memories and Workspace Graph keep organization-visible records available in
  every selected workspace while limiting team-visible records to the matching
  workspace.
- Dashboard navigation, responsive rail, local typography, loading/empty/error
  states, search, pagination, and action surfaces now match the approved
  warm-paper desktop and 320 px mobile system without new runtime dependencies.

### Fixed

- The npm package includes `dist/index.html`, so an installed `titen dashboard`
  serves the root redirect as well as `/dashboard/` (#303).
- Atlas fallback routing remains in the shared API root, preserving the
  authenticated Cloudflare administrator compile path (#304).

### Security

- Migration 23 backfills compatibility grants without rewriting evidence;
  denied resources remain absent from labels, counts, facets, cursors, graph
  topology, disputes, citations, and directory summaries.
- Model configuration exposes only masked immutable tuples; dashboard secrets,
  provider payloads, prompts, and embeddings never enter browser-visible state.

## [0.8.5] — 2026-08-15

Atlas Evidence Trace now matches the approved relationship-graph direction:
the focus claim is centered, relationship paths are labeled, and only live
authorized relationships are drawn.

### Added

- Evidence Trace may include complete caller-readable context runs through
  `selected-in` edges and current eligible channel releases through
  `released-as` edges.
- The dashboard renders distinct observation, claim, context, and release nodes
  with `supports`, `contradicts`, `qualifies`, `selected-in`, and `released-as`
  path styles.

### Changed

- The generic Atlas card grid is replaced by a deterministic claim-centered
  Evidence Trace canvas with labeled SVG paths and an accessible node inspector.
- Opening a memory in Atlas immediately compiles and renders that claim's live
  Evidence Trace; it does not show mock records when no trace exists.

### Security

- A context node is returned only when the caller owns or receives the context
  and can still read every item in the pack. Active release nodes are rechecked
  against claim authorization, lifecycle, version, channel status, and validity.

## [0.8.4] — 2026-08-15

The dashboard now separates the actionable Memories inventory from the
read-only visual Atlas. Memory records are visible immediately after login,
without a compile step.

### Added

- `GET /v1/memories` lists authorized canonical claims with bounded lexical
  search, lifecycle/visibility filters, and stable keyset pagination.
- The same-origin dashboard adapter exposes the protected Memories list route.
- Memories now has real loading, empty, error, search, and pagination states;
  a selected record opens its evidence graph in Atlas.

### Changed

- Atlas is a separate navigation destination with its existing graph, inspector,
  compile trace, and explicit administrator boundary.
- Global dashboard search now searches Memories instead of filling an unrelated
  Atlas subject field.

### Security

- Memory list authorization and retention checks run in canonical SQL before
  FTS or pagination. Hidden records never contribute to a count or cursor.

## [0.8.3] — 2026-08-15

Memory Atlas now gives an explicitly authorized owner a safe recovery view,
while ordinary operators get clearer principal-scoped results. Normal semantic
index catch-up no longer removes a canonically healthy service from traffic.

### Added

- Memory Atlas now supports an explicit, same-organization
  `organization_admin` mode gated by `views:compile:all`, active root/owner
  authority, a bounded audit reason, and metadata-only audit evidence.
- Every Atlas result identifies its active principal and access mode; the
  dashboard exposes administrator mode only to eligible operators and explains
  empty principal-scoped results without implying global memory is empty.

### Changed

- Normal pending semantic projection work now keeps `/readyz` at HTTP 200 with
  `index_projection_pending` and an enabled vector capability. Observed
  dependency, fingerprint, metadata, migration, and signing-secret failures
  remain fail-closed at 503.

## [0.8.2] — 2026-08-15

The npm package now carries the same production dashboard that is checked into
the repository, so a fresh install cannot silently fall back to an older static
dashboard bundle.

### Added

- `titen dashboard [--port 4322]` serves the packaged Astro dashboard through
  the existing same-origin adapter. Live mode remains opt-in and requires the
  documented private service configuration.
- `prepack` rebuilds the dashboard and SDK before creating the npm tarball.

### Security

- The package includes only built dashboard assets and the adapter entrypoint;
  credentials, mockup source, memory content, and runtime state remain excluded.

## [0.8.1] — 2026-08-15

The live operator dashboard now follows the approved Memory Atlas visual system
while keeping the authenticated adapter and six-area contracts unchanged.

### Changed

- Reworked the Astro dashboard rail, header, lens controls, status metadata,
  topology workspace, compile trace, and inspector with local Titen icons and
  the approved warm paper palette.
- Added a live, bounded SVG topology that is derived only from authorized Atlas
  nodes and edges, with a synchronized accessible record list and inspector.
- Added a native search dialog that fills the authorized Atlas query without
  moving credentials or memory data into browser storage.
- Wired every visible rail item to a real destination: Memories aliases Atlas,
  System shows live health/readiness, Access shows the effective principal
  policy, and Releases loads authorized release data.
- Added a private Profile view with session-backed password rotation, and fixed
  the readiness revision display when the upstream reports it in response
  metadata.

### Fixed

- Dashboard mobile layout now keeps the Atlas graph inside an internal scroll
  region and preserves a single-column reading path.

## [0.8.0] — 2026-08-13

Titen can now bootstrap curated memory from sixteen agent and memory-system
exports through one bounded, deterministic importer. This release also closes
the three usability gaps that could make accepted evidence look missing, split
one service across working directories, or leave an apparently successful
installer without a runnable command.

### Upgrade notes

- **Breaking:** service commands without `--db` now use the absolute per-user
  store `~/.titen/service.db`, not `./titen.db`. Existing deployments should
  keep passing their current absolute `--db` path or move the database once.
  When a legacy cwd `titen.db` exists and the user store does not, Titen refuses
  to mutate either store and prints the explicit compatibility command.
- The website installer now exits non-zero when `titen` is installed but still
  cannot be resolved by name. Automation that intentionally consumes the
  verified absolute binary path should call `install.sh --print-path`; its
  stdout contains that path only.

### Added

- `titen import-source` previews by default and applies only with an explicit
  local database or exact served origin. Versioned profiles cover Mem0,
  OpenClaw, Hermes, Claude Code, Codex, Gemini CLI, Qwen Code, ByteRover,
  Amazon Q, Replit, Honcho, Letta AgentFile, MemoMind, agent rule files, Basic
  Memory, and explicit Markdown. Imports use the existing observation and
  consolidation contracts, deterministic source and idempotency identities,
  private/unverified defaults, and exact replay accounting.
- Context-pack budgets now report `unconsolidated_observations`: the count of
  readable evidence in the requested subject/project scope that has not yet
  produced a claim. Returned context items remain claim-only.

### Fixed

- An accepted observation no longer looks silently lost: both Bun/SQLite and
  Cloudflare/D1 expose the scoped pending-evidence count without leaking hidden
  organizations, projects, workspaces, or private principals (#297).
- `bootstrap` and `serve` no longer choose different databases when launched
  from different directories, and `serve` refuses to create an empty store by
  accident (#298).
- The installer no longer reports success when the verified binary is outside
  `PATH`; it prints a predictable `TITEN_BIN=...` recovery value and exits 1,
  while `--print-path` provides a scripting-safe mode (#299).

### Security

- Source import rejects secrets before target access, symlinks, non-UTF-8 and
  unsafe-control text, unknown or incomplete structured exports, populated
  AgentFile environment values, dangling block references, more than 10,000
  entries, or more than 64 MiB of selected input. Rule links and vendor
  frontmatter remain inert evidence and are never dereferenced or executed.

## [0.7.4] — 2026-08-10

Three fixes found while wiring a live Titen into Claude Code as a memory
server, and every one of them cost hours before it was understood.

### Fixed

- **A bridge started without its environment answered every lookup from the
  wrong store and said so nowhere.** With neither `TITEN_MCP_URL` nor
  `TITEN_API_KEY` set, `titen mcp` falls back to `~/.titen/memory.db`. That is
  correct, and a host configuration can reach it by accident: on the machine
  where this was found, a project-scoped MCP registration with an empty `env`
  shadowed the user-scoped one that carried both variables. The session looked
  healthy — connected, eighteen tools — while `titen_compile` returned zero
  items for a subject whose claim the served instance returned over HTTP in the
  same minute. Local mode now names the store it opened and both unset
  variables twice: once on stderr, and once appended to the `instructions` in
  the `initialize` result, which is the copy the model reading an empty context
  pack can actually see. A served deployment appends nothing.
- **A failed bridged request was one sentence for every cause.** A revoked key,
  a wrong port and a restarted server all produced exit 0, empty stderr, and
  the same `-32000 Titen MCP request failed.`; a notification produced no reply
  and no trace at all. The endpoint and the caught reason now go to stderr for
  requests and notifications alike, with the API key redacted as it already was
  in a response body, and an upstream answer that is not JSON-RPC reports its
  HTTP status. The body is still never printed, because it carries memory.
- **`.gitignore` covered databases and no key material.** `titen.key`,
  `keys/owner.key` and `secrets/x.pem` were all committable in the repository
  whose CLI prints an API key and a dashboard password, and whose default
  `--db` is relative to the working directory. `*.key`, `*.pem`, `*.p12` and
  `secrets/` are now ignored.

## [0.7.3] — 2026-08-08

Prompted by an external audit of `b2d2fba`. 61 of its claims were verified
against HEAD before any of them was acted on: 32 held, 4 were already fixed,
18 held in part, and 7 were wrong. Everything below survived that check.

### Fixed

- **Switching from `@modelcontextprotocol/server-memory` imported nothing, and
  said nothing.** The search covered `MEMORY_FILE_PATH` and the working
  directory. That server writes **beside its own module** when the variable is
  unset, so anyone who ran it the documented way — `npx -y
  @modelcontextprotocol/server-memory` — has their graph inside a hashed
  directory in npm's `_npx` cache, which was never searched; and an MCP server
  launched by a desktop client inherits the client's working directory, so the
  one fallback was weakest exactly where it was relied on. Both populations
  imported zero entities and were told nothing, which reads as Titen losing
  their memories. The search now covers the working directory,
  `node_modules/@modelcontextprotocol/server-memory/dist` beneath it, and every
  such install in the `_npx` cache, under both the current `memory.jsonl` name
  and the older `memory.json`. A `MEMORY_FILE_PATH` that points at nothing now
  says so on stderr.
- **`memory://knowledge-graph` is served.** `resources/list` returned `-32601`,
  so a client that reads the graph as an MCP resource rather than through
  `read_graph` broke on the switch however well the nine tool names matched.
  `resources/list` and `resources/read` now answer with the same JSON body
  `read_graph` returns. `initialize` declares `resources` with
  `subscribe: false`: the graph is readable, and no change notifications are
  sent.
- **Every authenticated request paid a durable write to record that it had
  read.** `authenticate` compared `last_used_at` against `now`, so the `CASE`
  fired on any request whose clock had advanced a millisecond — in production,
  all of them. Measured on WAL with `synchronous = FULL`: fifty updates whose
  `CASE` is a no-op grow the WAL by **0 bytes**, fifty that change the value
  grow it by **206,032** — one page and one `fsync` each, and one billed write
  on D1. 25 of the 27 `GET` routes paid it. Now bounded to one write per key
  per minute. `last_used_at` stays monotonic and loses resolution, which
  `GET /v1/keys` documents.
- **An imported credential colliding with another organization told the
  operator to retry forever.** `api_keys.id` is a global primary key, but the
  credential preflight is scoped `WHERE org_id = ?` and so cannot see a
  collision elsewhere. The constraint surfaced through the concurrent-write
  handler as *"Import collided with a concurrent write; retry after exporting
  current state"* — advice that can never succeed, because the collision is
  with another organization's row. The foreign-id preflight now covers
  `api_key`, so it reports what actually happened, as every other record type
  does.

### Changed

- **`score` is comparable across queries (#227).** The relevance term was
  rescaled against the candidate set, which pinned the best candidate to
  exactly `1` however poor the match; with every other component constant on a
  uniformly ingested corpus, rank 1 returned the same number on every query and
  threshold-based abstention was arithmetically impossible. Relevance is now
  `strength / (strength + 3.7)` where `strength` is the `bm25` magnitude
  divided by the query's term count, and the semantic arm uses raw cosine.
  Measured on the 424,168-claim anchor store, 500 questions: rank-1 `score`
  went from **1 distinct value** to **498**, spanning 0.4875–0.6632.

  Pre-registered before any cell was scored, then measured on both bench
  conditions with the baselines reproduced first: anchor recall@1 **0.8800 →
  0.8800**, pooled **0.2460 → 0.2460**, sign tests W0/L0/T500 at p = 1.0,
  compile p95 +1.21% and +0.67%.

  **That gate could not have failed, and the report says so.** Both stores were
  ingested in one pass, so all five non-relevance components are constant
  across 89,467 packed items; with those constant the order is identical by
  arithmetic. Where they vary the behaviour does change: at `bm25` −60 against
  −45 the relevance gap compresses from 0.250 to 0.018 and the order flips to
  the `verified` claim over the marginally better-matching `asserted` one. That
  is intended, it is pinned by contract, and it is bounded — a genuinely weak
  match still loses to a strong one however trusted.

  Two limits: `3.7` is calibrated on LongMemEval-S and BM25 is not portable
  across corpora, so the absolute band shifts with the corpus while the
  ordering does not; and the vector arm's move off set-relative scaling is
  **unmeasured**, because both benchmark lanes ran with vectors disabled.

### Removed

- Five exports that shipped in the tarball with no caller anywhere in the
  source, tests, scripts or dashboard: `ftsQuery`, `canReadRecord`,
  `recordEvent`, `param`, `FEEDBACK_ENDPOINT`.

### Internal

- `@types/node` and `@types/bun` were never installed, so `tsc` could not
  resolve `process`, `Bun`, or any `node:`/`bun:` import and the tree reported
  791 errors. With them, and with `Prepared` typed at the untrusted-import
  boundary, `src/` is clean under `tsc --noEmit`. A `typecheck` script now
  exists so the number cannot drift unobserved. No `any`, `@ts-ignore`, or new
  cast was added.

## [0.7.2] — 2026-08-08

An urgent fix: 0.7.1 cannot serve a large single-subject store. Upgrade from
0.7.1 without delay; 0.7.0 is unaffected.

### Fixed

- **`titen-memory@0.7.1` cannot serve a large single-subject store: one context
  compile takes a median 74.5 seconds where 0.7.0 takes under half a second.** The 0.7.1
  fix for [#291](https://github.com/RamaAditya49/titen/issues/291) wrote the
  `disputed` predicate as a join inside `EXISTS` while its own comment claimed
  the nested form, and a join inside `EXISTS` is still a join the planner may
  reorder. SQLite 3.53.0 — the version Bun 1.3.14 links — reorders it to
  `SEARCH o USING INDEX observations_workspace_scope (org_id=?)`, scanning every
  observation in the organization once per candidate row: the exact shape
  `src/core/authorization.ts` documents as the historical 79-second failure,
  shipped in the release that believed it had prevented it.

  **Who is affected:** anyone whose store holds many claims under one subject
  and who compiles with a large `max_candidates`. The cost is the product of
  candidates and organization-wide observations, so small and per-subject stores
  are unaffected — which is why no test and no published benchmark caught it.
  Every published pooled *quality* figure was measured on 0.7.0; every published
  pooled or scoped-anchor *latency* figure was measured on 0.7.0 or on this
  release, never on 0.7.1.

  **Measured**, 342,129-claim / 19,829-observation store, one subject, real
  statement, `EXPLAIN` captured from `bun:sqlite` rather than a pasted copy:

  | | candidate query | served compile |
  | --- | ---: | ---: |
  | 0.7.1 as published | 73,439 ms | **74,474 ms** |
  | this release | 232 ms | **417 ms p50 / 864 ms p95** |

  This restores 0.7.0's behaviour; it does **not** make compile fast. **864 ms
  p95 still fails the pre-registered 250 ms gate**, so the 2026-08-07 latency
  falsifier stands — the pre-registration predicted exactly that outcome in
  writing before the run.

  The ranked output is **byte-identical** to the published 0.7.0 pooled run —
  equal sha256 over all 500 instances — so this restores the shipped answer
  rather than changing it. Full method and both plans in
  [the report](./docs/testing/2026-08-08-pooled-compile-latency.md), protocol
  [pre-registered](./docs/testing/2026-08-08-pooled-compile-latency-prereg.md)
  before the A/B.

### Added

- **Plan-shape guards for the retrieval queries**
  (`tests/integration/query-plan.test.ts`). The regression above was invisible
  to every existing test because contract stores hold tens of rows and still
  return the right answer quickly. The load-bearing discovery is that the bad
  plan **reproduces on an empty store** — SQLite picks the join order from the
  schema, not from row counts — so a cheap deterministic test could have caught
  this and the 2026-08-07 occurrence before either shipped. The guards assert
  the plan of the candidate query, the by-id hydration, and authorized-source
  loading; reverting the fix fails two of the three. They assert the plan, never
  a duration: a timing assertion on this hardware would be flaky, and the plan
  is what regressed.


## [0.7.1] — 2026-08-07

> **Superseded by [0.7.2](#072--2026-08-08). Do not run 0.7.1 on a store with
> many claims under one subject: one context compile takes a median 74.5
> seconds.**

The measurement release: the pooled-store condition on LongMemEval-S, with
two pre-registered falsifiers fired against Titen and published, plus the
#291 disputed-signal authorization fix and the evidence-depth tie-break.

### Added

- **The pooled-store measurement: quality, latency, and build cost at
  production store shape.** All 19,829 distinct LongMemEval-S sessions in one
  single-subject store, all 500 questions against it, at four store sizes —
  the condition every published number in this field (ours included) avoids by
  giving each question its own ~50-session haystack. Pre-registered with five
  falsifiers before the first scored run
  ([prereg](./docs/testing/2026-08-07-pooled-store-prereg.md),
  [report](./docs/testing/2026-08-07-pooled-store.md)). The system under test
  is the published npm tarball, not a checkout.
- The [2026-08-07 agent-memory landscape survey](./docs/research/2026-08-07-memory-agent-landscape.md),
  superseding 2026-08-04 on the fame roster and correcting two of its claims,
  and [the performance-axis answer](./docs/research/2026-08-07-performance-axis.md)
  recording which competitive axes were adversarially killed and why.

### Changed

- **A ranking dead heat now breaks on authorized evidence depth before the
  arbitrary statement fallback** ([#288](https://github.com/RamaAditya49/titen/pull/288)).
  Order changes only where weighted score and vector similarity are both
  exactly tied; measured byte-identical on all 500 LongMemEval-S instances,
  and published as capturing 0.0 of the reranking ceiling on that corpus
  ([report](./docs/testing/2026-08-07-evidence-ranking.md)).
- **The `disputed` signal now resolves through the caller's own authorization,
  so a contradicting observation the caller may not read no longer marks the
  claim ([#291](https://github.com/RamaAditya49/titen/issues/291)).** The flag
  was computed from a bare `EXISTS` over `claim_sources` with no join to
  `observations`, while the citations beside it were filtered correctly. A
  principal who could not read the contradicting source still saw the claim
  demoted by the 0.05 conflict term and still received a `conflicts[]` entry
  whose `evidence_ids` omitted the source that caused it — told a contradiction
  existed and told they could not see it. Fixed at all four query sites:
  `POST /v1/context/compile` (lexical and vector candidates), `GET
  /v1/context/:id`, and the Memory Atlas `conflict_freshness` lens. The
  governance review queue was already correct and now shares the same predicate.

  **Visible consequence:** in a store that already holds a cross-scope
  contradiction, claims that were demoted for callers who cannot read the
  contradicting source stop being demoted. Their `score` rises by up to `0.05`,
  their `score_components.conflict` reads `1` instead of `0`, they leave
  `conflicts[]`, and the resulting order can change. Nothing changes for a
  caller who can read the source, and nothing changes for a claim whose own
  `status` is `disputed` — that is the claim's own field, not an inference about
  hidden evidence. There is no migration and no flag: the previous numbers were
  the leak.

  Response shapes, routes, and field names are unchanged, so this is a **patch**
  under the table in
  [release.md](./docs/engineering/release.md#versioning-and-channels) — the
  minor slot signals shape breakage, and only values that were disclosing hidden
  rows move here.

### Fixed

- **The Memory Atlas review queue no longer scans every observation in the
  organization to decide `has_contradiction`.** It expressed the predicate as
  `claim_sources JOIN observations`, and SQLite drove from `observations`,
  evaluating the membership and retention subqueries for all of them once per
  candidate. On a 424,168-claim store that is **79 s per compile against 17.8
  ms**. The shared predicate added above uses a nested `EXISTS` so
  `claim_sources` seeks its own primary key, and the review queue now uses it.

  **Corrected in [0.7.2](#072--2026-08-08):** what shipped here was a join
  *inside* the `EXISTS`, not the nested form this entry describes, and SQLite
  3.53.0 reordered it straight back into the 79-second shape — a median 74.5 s
  per compile on a large single-subject store.
  Found by benchmarking the change above; the dual-runtime contract suite passed
  on both query shapes, because its stores hold tens of rows.

### Evidence

- [`docs/testing/2026-08-07-disputed-authorization.md`](./docs/testing/2026-08-07-disputed-authorization.md)
  — n=500, ranked output byte-identical before and after (0/0/500, p = 1.0),
  compile latency flat within repeat spread. It also states what it cannot show:
  the corpus holds zero contradicting sources, so it cannot measure the fix
  where the fix fires.

## [0.7.0] — 2026-08-07

### Changed

- **Breaking: `source.ref` is now required on every observation write.** A
  caller that omitted it received `201`; it now receives `400
  VALIDATION_ERROR`. The MCP tool spec already stated the obligation, so MCP
  callers are unaffected; direct HTTP callers must add a pointer back to where
  the content came from. **Migration:** add `"ref"` to the `source` object on
  `POST /v1/observations` — any stable identifier for the origin (a commit sha,
  a ticket id, a URL, a tool invocation id). There is no compatibility flag: an
  entry that cannot be traced to an origin cannot be told apart from junk, which
  is the whole point of the audit work below. Part of #280.

### Added

- **Zero-config local mode.** `npx titen-memory mcp` with no environment opens
  or creates `~/.titen/memory.db`, provisions an organization, workspace,
  project and owner as real rows, and serves MCP over stdio in-process — no HTTP
  hop, no key to paste, no outbound call, FTS-only. The served mode and its auth
  path are unchanged; this is an additional entry point, not a relaxation.
  Closes #278.
- **Drop-in compatibility with `@modelcontextprotocol/server-memory`.** The nine
  reference-server tool names are served alongside the native ones, `search_nodes`
  is routed through Titen retrieval rather than a substring scan, and an existing
  `memory.json` is imported on first run. The switching cost is one line of MCP
  config. Closes #279.
- **`recalled` provenance is server-issued.** `POST /v1/context/compile` returns
  a signed context token; an observation written while carrying it is stamped
  `source.type: "recalled"` by the server, and a caller that merely declares
  `recalled` is refused. A primary-key lookup against the `context_runs` row
  the compile already wrote, so no new table and no migration.
  Known ceiling, stated in the code: the stamp proves the write was made while
  holding a Titen-issued pack, not that its content came from that pack, so it
  is a sound lower bound on the recall loop and never an upper one. Closes #280.
- **`titen audit`.** Reports exact-duplicate, near-duplicate, recall-loop,
  secret-pattern and stale rates over a `memory.json`, a Mem0 export, or a Titen
  store. No network, no LLM, no upload, no composite score, no leaderboard. Its
  first published run is against Titen's own store and opens by naming two
  defects in this product. Closes #281.
- **Concurrent-writer durability suite** on both runtimes, with the invariants
  published before the run and each re-run with its primitive removed to prove
  the suite can fail. Closes #282.

### Evidence

- [`docs/testing/2026-08-07-titen-audit-self-report.md`](./docs/testing/2026-08-07-titen-audit-self-report.md)
  — 17.9% byte-identical duplicates six seconds after write, 96.7% never read
  back, and the compatibility surface turning one entity into six. It also names
  the number it cannot report: there is no 32-day Titen store, so nothing in it
  bounds long-run accumulation.
- [`docs/testing/2026-08-07-durability.md`](./docs/testing/2026-08-07-durability.md)
  — invariants held on both runtimes under concurrent writers.

## [0.6.1] — 2026-08-05

### Fixed

- Webhooks no longer silently drop events written in their own registration
  millisecond. Eligibility compared `w.created_at < e.created_at`, a strict
  comparison on a millisecond wall-clock string, so on a fast host the
  registration and the next write shared a millisecond and those events were
  never queued, never delivered, and never retried. Delivery now pages on the
  `event_order.seq` watermark that `/v1/events` and federation already use:
  migration 22 adds `webhooks.created_seq`, backfilled to the current head so an
  upgrade delivers only future events rather than replaying history. Both
  eligibility sites are converted — `processWebhooks` and the background
  `deliverPending` selector — because the second one would otherwise leave an
  organization unwoken until another event arrived. Measured on a 16-core host:
  the contract file went from 8 of 12 runs failing to 12 of 12 passing.
  Closes #265.

- Retrieval ranking is now reproducible in the FTS-only lane. Exactly-tied
  scores previously fell through to `claim_id`, a fresh uuid per ingest, so the
  same corpus ranked differently on every run. Ties now break on the claim
  statement — content-derived, and compared by code unit so Bun and Workers
  agree — before falling back to the id. This makes rank reproducible, not
  better: it picks an arbitrary-but-stable winner among genuine ties. What
  causes the ties is untouched and stays open as #227. Closes #226.
- Upgrading a pre-0.2.0 store now refuses instead of succeeding into silence.
  Migration 10 scopes `team` visibility to a workspace and nothing backfills the
  column, so a 0.1.x store migrated cleanly and then answered 404 on every
  claim. `migrate` now counts legacy team rows first and fails closed, naming
  the count and the 0.2.0 data floor. Rebinding is deliberately not attempted: a
  legacy row's real workspace is unknowable, and inventing one would invent an
  authorization boundary. Closes #257.
- The logical export refuses a pre-workspace `team` row rather than writing a
  backup its own importer rejects. `export_import` is advertised as enabled, so
  an artifact that cannot be restored is worse than an error. Closes #258.

### Measured, not built

- The reranking stage recorded as strategic debt was measured before being
  written, and the measurement says do not write it. The oracle ceiling over
  Titen's own top-10 is +10.2 points (recall@1 0.880 to 0.982), but every cheap
  reranking signal tested captured at most 0.6 of those points at p=0.61, and
  two were significantly *worse* than no reranking at all. MemPalace, which
  ships one, scored lower with it than without it in both embedding
  configurations. The gain is real and lexical signals cannot reach it.
  Closes #269.

### Added

- In-process mode for Bun hosts. `serve()` returns the handler it serves, and a
  `titen-memory/bun` subpath exports it, so a benchmark or embedding host can
  drive `TitenClient` through an injected `fetch` with no loopback hop. The
  transport is removed; auth, scopes, and response envelopes are unchanged
  because it is the same handler. An ephemeral socket is still bound and unused,
  tracked in `PONYTAIL-DEBT.md`. Closes #230.

### Documentation

- Measured the memory-agent field on an externally authored corpus
  (LongMemEval-S, MIT) and recorded where Titen actually stands:
  [landscape note](./docs/research/2026-08-04-memory-agent-landscape.md).
  `docs/testing/EVALS.md` now marks recall@5/@10 as saturated on that corpus so
  only recall@1 and MRR@10 are quoted as discriminating, and `blueprint.md` no
  longer plans a LoCoMo run — LoCoMo is CC BY-NC 4.0 and a launch is commercial
  use, which an SPDX check misses because GitHub reports it as `NOASSERTION`.
  Closes #267, #270, #271.

- The single-core throughput ceiling is published as an operator sizing rule in
  [VPS deployment](./docs/deployment/vps.md) and `deploy/README.md`: at and
  above 10,000 claims one client saturates one process, so shard by subject
  across processes rather than adding clients. Cloudflare records that this is a
  `bun:sqlite` property that does not transfer to per-request isolates. Closes
  #259.
- `docs/testing/EVALS.md` carries the FTS-only degradation curve, so no
  FTS-only quality figure is quoted without the corpus size it was measured at.
  Closes #260.
- The `cross_language:en` zero is explained rather than left open: the stratum
  rotates document language past query language, so it is three language pairs,
  and English queries never retrieve a non-English document (2,000 of 2,000
  top-10 hits). A provider embedding property, not a fixture defect. The fixture
  is unchanged because its hashes pin the locked holdout and pre-hoc threshold.
  Closes #245.
- `docs/engineering/release.md` states why the tarball carries no provenance
  attestation — it requires a supported CI's OIDC token and this repository
  publishes by hand on purpose — and gives consumers the registry checks that do
  work, with what they do not prove. Closes #242.
- One product sentence across the GitHub description, README, and
  `package.json`. Closes #225.

## [0.6.0] — 2026-08-04

### Evidence

- First published head-to-head against a comparator on an **externally authored**
  corpus: [neutral head-to-head](./docs/testing/2026-08-04-neutral-head-to-head.md),
  Mr.TyDi Indonesian, 25 queries over 100 documents, 10 repeats, against
  self-hosted `mem0ai` 2.0.13. The result is **parity, not superiority**. Point
  estimates favour Titen on every metric, but both systems are deterministic on
  this harness, so disjoint across-repeat ranges prove nothing; paired sign tests
  over the same 25 queries give p >= 0.500 on every metric, and the whole margin
  is two queries (20 of 25 rank-1 hits against 18). Titen loses one query to both
  Mem0 configurations. What the run does establish: ~1.9x lower query latency
  with vector search and ~400x without it, ~9.6x faster ingest, and deterministic
  ranking **when vector search is enabled**. The FTS-only lane still produces a
  different ordering on every repeat, exactly as 0.5.7 did.
- Correction to the record: the five-repeat figures published earlier the same
  day for 0.5.7 — recall@1 0.680, MRR@10 0.807, query p50 170 ms — were the
  **top** of their ranges, not medians. Re-scoring the same artifact per repeat
  gives a 0.62 median for recall@1. Do not requote them.
- [Scale and concurrency](./docs/testing/2026-08-04-scale-and-concurrency.md):
  the service uses one core at every concurrency level, so a single client
  saturates it at and above 10,000 claims, and retrieval quality falls with
  corpus size on a synthetic corpus (recall@1 1.00 / 0.81 / 0.49 at 10^3 / 10^4 /
  10^5). Cold start stays at ~53 ms and resident memory grows 2.9% across a
  hundredfold corpus. Tracked as #259 and #260.
- [Operational lifecycle](./docs/testing/2026-08-04-operational-lifecycle.md):
  nine published releases rehearsed through in-place upgrade with zero canonical
  rows lost or mutated, plus a real backup, destroy and restore drill and a JSONL
  export/import round trip. The **data-usable upgrade floor is 0.2.0**: a 0.1.x
  store migrates without error and then answers 404 on every claim (#257), and
  its export is rejected by the importer (#258).

### Added

- `POST /v1/context/compile` accepts an optional `top_k`, a hard ceiling of 1
  through 1,000 on returned items, exposed by the TypeScript SDK, the Python
  client, and the `titen_compile` MCP tool. It bounds the answer where
  `max_candidates` bounds what retrieval considers, and is applied to the ranked
  list before the token budget, so a bounded pack costs fewer tokens instead of
  being truncated client-side. Omitting it changes nothing. Closes #229.
- `TITEN_EMBED_PROFILE=raw-unit-v1-model-mismatch-acknowledged` sends raw
  embedding input on a model whose id claims a prompt convention. The forced
  rule that rejects `raw-unit-v1` for an EmbeddingGemma model is unchanged and
  still fails closed; this is the one deliberate way past it, for a model served
  without the prompts and for a fair head-to-head against a system that embeds
  raw text. It is a distinct profile in the index fingerprint, so switching to
  or from it answers `503` with `index_fingerprint_mismatch` until the index is
  rebuilt rather than mixing prefixed and raw vectors. The retrieval harness
  accepts `TITEN_EVAL_EMBED_PROFILE` to use it. Closes #250.

### Documentation

- The README states up front that Titen's default memory model is
  caller-authored claims: `consolidate()` takes explicit statements with explicit
  source links, and model-assisted derivation remains activation-gated with no
  candidate model past the gate.
- The README states the client surface exactly: a TypeScript SDK, the `titen`
  CLI, and the standard-library-only Python client in `clients/python/` that is
  installed from a checkout and is not published to PyPI. Callers in any other
  language are pointed at the REST reference.
- Project status leads with pre-1.0 and defines *stable* as the release channel
  (npm `latest`, `"channel": "stable"` in `version.json`), never as API
  stability. The changelog header and the release guide carry the same wording.
- The SDK quickstart now works by copy-paste from `npm init -y`. `titen-memory`
  is ESM-only, so the quickstart adds `npm pkg set type=module` and names the
  file `titen-example.js`; previously the documented steps ended in
  `SyntaxError: Cannot use import statement outside a module`.
- `TITEN_EMBED_REVISION`, `TITEN_EMBED_PROFILE`, and `TITEN_EMBED_MIN_COSINE`
  are documented in the README and the VPS guide with their absence behavior,
  the model-forced profile rule, and a worked EmbeddingGemma example.
  `TITEN_EMBED_MIN_COSINE` has no shipped default and semantic retrieval fails
  closed without it.
- Vectorize status is stated identically everywhere: verified live only on the
  maintainer's isolated the maintainer release stack stack, which is test production and not
  general availability.

### Fixed

- Hybrid ranking no longer decides a tied top result with a random identifier.
  Because lexical and vector relevance are each min-max normalized inside the
  candidate set, each signal's own best scores exactly `1`; when those are
  different claims the two tie on the whole weighted score, and the tie-break
  was `claim_id` over UUIDs minted at ingest. Equal scores now break on the
  stronger vector similarity first, then on `claim_id`. Two cosines are only
  ever compared with each other, so no constant and no cross-scale comparison
  enters the ordering, no returned score changes, and lexical-only ranking is
  untouched. On the release retrieval harness, 10 repeats, the vector lane's
  recall@1 median rises from 0.7143 to 0.8571, MRR@10 from 0.8333 to 0.9048 and
  nDCG@3 from 0.8758 to 0.9286, and every range collapses to zero width. The
  ceiling is unchanged: this is a higher median with the variance eliminated,
  not a higher best case. Part of #226; #226 and #227 both remain open.
- Temporal polarity reaches lexical retrieval. "Mulai Juli 2026" and "Sebelum
  Juli 2026" previously scored identically for any query that did not repeat one
  of those words verbatim, because a surviving marker matched only a claim using
  the identical word. A marker now expands inside the MATCH to the other markers
  naming the same window boundary **in the same language**, so "sejak Juli 2026"
  reaches "Mulai Juli 2026". Groups never span languages, and markers that are
  also function words (`dari`, `from`) stay in the stoplist and are not
  expanded. `query_terms_used` still counts only the caller's terms, and no
  ranking term was added. On the release retrieval harness, 10 repeats, the
  FTS-only lane's recall@1 median rises from 0.5714 to 0.7143 and nDCG@3 from
  0.7517 to 0.8044, both with the variance eliminated and the maximum unchanged.
  MRR@10 rises from a median of 0.7381 to 0.8061 but keeps a residual range of
  [0.8061, 0.8095] across four independent 10-repeat runs, so its variance is
  reduced rather than removed. Closes #228.
- `budget.omitted_items` counts the ranked candidates a `top_k` bound discarded,
  not only those the token budget dropped. A count-bounded pack previously
  reported zero omissions with `budget_exhausted: false`, so a caller could not
  tell a truncated answer from a complete one.
- Release headings for 0.5.7, 0.5.6, 0.5.5, and 0.2.0 now carry their UTC
  publish dates and agree with the npm registry `time` field; they had recorded
  the maintainer's local (UTC+7) date.
- The full-fit context contract no longer pins an absolute as-of date, so it
  stops passing or failing on the calendar. Shipped code is unchanged: the
  fixture asked for context as of a date that had moved into the past, and
  `valid_from <= at` correctly excluded the claims it had just written.

## [0.5.7] — 2026-08-01

### Documentation

- The npm README now gives one ordered path from CLI install through bootstrap,
  agent-specific keys, service readiness, and verified MCP setup for Codex,
  Claude Code, OpenClaw, Hermes, and generic stdio clients.
- The host guide separates direct Streamable HTTP from the bundled `titen mcp`
  bridge, keeps credentials in host environments, and includes a nine-tool
  connection check.

### Fixed

- Dashboard integration and browser gates no longer share hard-coded port
  ranges with unrelated development servers.

## [0.5.6] — 2026-08-01

### Added

- `titen mcp` bridges newline-delimited stdio MCP clients to an existing
  authenticated Titen `/mcp` endpoint, so hosts without native remote MCP can
  use the same nine tools without another memory implementation.

### Changed

- MCP initialization now tells compatible hosts to resolve the repository and
  compile authorized memory once at each task or scope boundary while keeping
  durable writes explicit and typed.

### Security

- The stdio bridge accepts its endpoint and revocable key only through the
  inherited environment, rejects credential-bearing or ambiguous URLs, emits no
  notification reply, and sanitizes upstream failures without exposing the key.

## [0.5.5] — 2026-08-01

### Upgrade notes

- The canonical schema advances from 20 to 21. Take a verified backup and run
  `titen migrate --dry-run` before starting 0.5.5. The migration is additive;
  rollback restores the backup with the 0.5.4 binary.
- Dashboard replicas that must preserve sessions across restarts share one
  base64url-encoded 32-byte `TITEN_DASHBOARD_SESSION_KEY`. Without it, each
  process uses an ephemeral key and restart signs everyone out.

### Added

- Context compilation accepts an explicit historical `at` instant and a
  bounded `max_candidates` value through REST, MCP, and the TypeScript SDK.
- Stable source IDs and canonical claim hashes converge exact re-ingestion after
  the request idempotency window while changed evidence remains append-only.
- `POST /v1/index/verify` checks a bounded set of active claims against
  Vectorize or `sqlite-vec` and queues repairs without reading embedding values.
- The publishable OpenClaw bundle now includes remote Streamable HTTP MCP, and
  the Cursor package includes the metadata and documentation expected by its
  public plugin repository.

### Changed

- Dashboard login state is an authenticated AES-GCM HttpOnly cookie instead of
  process-local memory, so a shared key supports replicas without a session
  database.
- Confirmed semantic statement hashes avoid duplicate embedding calls; explicit
  reconciliation still forces a provider repair when an index record is absent.
- Context budget units now use deterministic UTF-8 bytes across runtimes, and
  Vectorize queries retain the native 100-result ceiling.
- The README now leads with Titen's Level 6 collaboration model and distinguishes
  evidence-grounded context from storage-only and similarity-only memory.

### Security

- Public Cloudflare login failures consume the canonical account throttle and a
  native Rate Limiting binding. The key contains no password or client IP.
- Password changes reject common and account-context values before a verifier is
  stored, while retaining the 15-character minimum and forced first change.
- Sealed dashboard sessions reject forged, expired, or undecipherable cookies;
  raw short-lived API credentials remain outside browser storage.

## [0.5.4] — 2026-08-01

### Added

- A checked-in the maintainer release stack Wrangler profile provisions the isolated live
  Worker, D1, Vectorize, Workers AI, and Cron contract without storing an
  account API token in the Worker.

### Changed

- Cloudflare schema readiness verifies the complete migration contract in one
  D1 read instead of issuing one remote read per required object and column.
- New dashboard passwords use six serial PBKDF2-HMAC-SHA-256 stages of 100,000
  iterations each, preserving the 600,000-operation work factor within the
  Workers Web Crypto per-call limit. Bun retains legacy verifier compatibility.
- The live semantic verifier uses an explicit bounded index drain; Cron remains
  a separately observed production reconciler instead of a timing dependency.

### Fixed

- The dashboard login username example is now the canonical bootstrap account
  `owner`, not a maintainer-specific placeholder.
- The Cloudflare runbook provisions the three Vectorize metadata indexes needed
  for scope-before-search filtering before the first vector upsert.

### Security

- Live unauthenticated and cross-organization probes fail with non-disclosing
  `401`/`404`, while native D1, Vectorize, and Workers AI bindings require no
  account credential inside the deployed Worker.

## [0.5.3] — 2026-08-01

### Added

- Bootstrap creates default username `owner` with a random temporary password;
  human operators sign in with username/password, and authorized owners/admins
  can atomically add another account, membership, role, and one-time password.
  API keys remain unchanged for agents, services, SDKs, CLI recovery, and
  existing integrations.

### Changed

- Schema 20 adds canonical operator accounts. Temporary-password login is
  restricted to password replacement; established login issues an eight-hour
  revocable API credential only to the server-side adapter.

### Fixed

- The per-principal sign-in state now uses a focused responsive login surface;
  the private product sidebar and operator topbar appear only after a valid
  session instead of framing the unauthenticated form.

### Security

- Password verifiers use unique salts and PBKDF2-HMAC-SHA-256 with 600,000
  iterations on both D1 and Bun/SQLite. Login failures are non-disclosing and
  locally throttled; password change and logout revoke short-lived dashboard
  credentials.

## [0.5.2] — 2026-08-01

### Added

- The operator dashboard now wires Memories, Context, Work, Audit, Governance,
  and Federation to fixed authenticated API routes, with capability-gated
  navigation and no fixture fallback.
- Per-principal dashboard login exchanges an API key for an opaque HttpOnly
  session, and authorized owners/admins can atomically create one human
  membership plus its one-time API key.
- The deployment guide now covers private Tailscale Serve and Cloudflare Tunnel
  protected by Cloudflare Access while both Titen listeners remain loopback-only.

### Changed

- The public SDK now types all six Memory Atlas lenses and the optional Add User
  fields on key creation.

### Security

- Dashboard routes enforce exact Host/Origin, bounded bodies and list results,
  credential isolation, revocation, expiry, logout/restart invalidation, and
  server-side authorization independent of browser navigation.

## [0.5.1] — 2026-08-01

### Fixed

- The dashboard content security policy now permits its bundled Titen mark data
  URI, so the release logo renders without allowing remote image sources.

## [0.5.0] — 2026-08-01

### Upgrade notes

- **Breaking:** the canonical schema advances from 17 to 19. Take a verified
  backup and run `titen migrate --dry-run` before starting 0.5.0; rollback
  restores that snapshot with the previous binary rather than running a down
  migration.
- Configure `TITEN_SECRET_KEYS` only when encrypted channels or federation are
  used. The lexical memory core remains available without it.
- A dashboard principal using governance lenses needs an active organization
  membership as well as the documented bounded key scopes.

### Added

- Enterprise governance now covers organization roles, typed approval and
  retention policies, claim review, encrypted channel assertions, versioned
  channel releases, legal holds, and external identity mappings.
- Opt-in canonical federation exchanges signed, filtered claim bundles with
  their evidence and provenance. Imports bind the source organization, preserve
  disagreement, reject unsafe trust elevation, and remain idempotent on both
  supported runtimes.
- The read-only dashboard now uses the live same-origin adapter for six
  authorized Memory Atlas lenses, including governance scope and knowledge
  release views. It has explicit loading, empty, denied, and disconnected
  states instead of a synthetic fallback.

### Changed

- Context compilation reports selected, omitted, and deduplicated candidate
  counts plus an explicit budget-exhaustion flag without exposing unauthorized
  records.

### Fixed

- Local key commands fail cleanly for a missing database, schema, organization,
  or key and no longer report a false revocation success.
- `titen backup` reapplies owner-only permissions after atomic replacement, so
  container bind mounts cannot leave the finished backup group- or world-readable.
- `titen migrate --dry-run` no longer changes file permissions or requires a
  writable database directory, so it works on a read-only pre-upgrade mount.

### Security

- Governance and federation mutations now enforce role, resource, retention,
  replay, peer-source, and cross-organization boundaries in the shared
  Bun/SQLite and workerd/D1 contract.

## [0.4.1] — 2026-08-01

### Added

- `titen version --check` explicitly reads the stable CLI/plugin release
  manifest from `titen.dev` and points users to the manual install guide without
  background polling or remote command execution.

### Fixed

- MCP initialization now reports the package SemVer as `serverInfo.version`
  instead of mislabeling a deployment revision such as `dev`, `test`, or a Git
  SHA as the server implementation version.
- Published SDK declarations now stay inside the package and exactly type claim
  inputs, readiness diagnostics/capabilities, key lifecycle fields, and a
  bounded event iterator that terminates on preserved cursors.
- Extraction now supports explicit strict-schema, JSON-object, and custom modes,
  rejects redirects and incomplete provider finishes, and uses the same model
  proposal validator as the locked release gate.
- Cloudflare liveness no longer waits on D1 preparation, Vectorize queries stay
  within the platform's `topK` limit, and the documented Wrangler recovery
  command uses a real temporary SQL file.
- Bun allows the documented extraction timeout and releases every semantic
  lease acquired before or during bounded SIGTERM shutdown, so a restart can
  recover work immediately.
- Context packing preserves rank when every item fits, applies diversity only
  under budget pressure, and no longer awards disputed claims a positive score.
- Live semantic verification carries project scope, while historical comparison
  tooling refuses to present a current deployment as the frozen `0.3.0` target.

### Security

- D1 diagnostic redaction now covers secrets split across streamed byte and
  text chunks, and Bun creates the canonical SQLite database plus sidecars with
  owner-only permissions.
- API keys now enforce immutable not-before and expiry windows, update
  `last_used_at` monotonically, preserve lifecycle metadata across supported
  operator surfaces, reject unknown creation fields, and cannot gain authority
  through credential import.

## [0.4.0] — 2026-07-31

### Fixed

- Global CLI installs now run directly on Bun and report `titen --version`
  instead of requiring a Node shim.
- Successful SDK responses now reject array, `null`, and primitive JSON
  envelopes with an `INVALID_RESPONSE` `TitenError` while preserving the HTTP
  status, request ID, and safe response metadata.
- Bun HTTP, Cloudflare Workers AI, and injected embedding-provider results now
  require exact output cardinality, ordered provider indices when present,
  dense configured dimensions, and finite numeric coordinates before vector
  query or indexing.
- Semantic readiness now distinguishes intentional FTS-only operation from
  partial configuration, unavailable vector initialization, legacy untracked
  vectors, missing requeue work, unsafe storage aliasing, empty restored
  projections, incompatible fingerprints, and locally observed indexing
  dependency failures; configured failures return a fixed local diagnostic
  without probing providers.
- Semantic retrieval now discards sub-threshold cosine hits before canonical
  hydration, so relative ranking cannot turn a best bad neighbor into useful
  context. Bun and Cloudflare share the same validated unit-vector boundary.
- Manual and background semantic-index drains now fence each outbox row before
  provider I/O, so an expired losing attempt cannot add stale failure evidence
  after another attempt completes the row. Lease eligibility and expiry now use
  the database clock at each conditional claim, so caller clock skew and earlier
  work cannot create an expired or stranded owner.
- Semantic-index upserts and removals now persist canonical reconciliation
  before external mutation and recreate it after stale or apply-then-throw
  outcomes, so a losing owner cannot resurrect a purged vector, erase a newer
  projection, or report unowned work as complete.

### Changed

- Capability contract version 1 reports embedding, extraction, and background
  enrichment separately while retaining `model` as a deprecated `0.3.x`
  embedding alias. Migration 13 persists the claim-index provider, model,
  revision, dimensions, metric, preprocessing, and schema fingerprint and
  requires an explicit reindex after incompatibility. Migration 14 retains only
  safe embedder/vector-store failure timestamps in semantic metadata until a
  later complete embed/upsert proves recovery.
- `sqlite-vec@0.1.9` is a pinned optional peer: default installs remain
  dependency-light while the documented vector install is machine-verifiable.
- Context compilation now treats a missing `project_id` as unscoped-only;
  explicit cross-project recall requires `cross_project: true` plus the separate
  `context:compile:all` capability and reports its effective scope and grant
  reason across REST, SDK, and MCP.
- **Breaking:** Semantic configuration now requires an immutable model revision, a named
  role-aware preprocessing profile, and an operator-calibrated cosine floor in
  the existing index fingerprint. EmbeddingGemma uses its official asymmetric
  query/document prompts; Titen ships no universal threshold.
- Migration 16 adds nullable owner and expiry fields to the rebuildable semantic
  index outbox; canonical observations and claims are unchanged.

## [0.3.0] — 2026-07-31

### Added

- Native Claude/ZCode and Cursor marketplace bundles, a ClawHub/OpenClaw skill
  bundle plus native MCP config, a Hermes skill plugin, a Pi skill package, and
  OpenCode/Windsurf/TRAE host kits now package the same nine-tool Titen MCP
  contract without duplicating the server or embedding an endpoint or
  credential. The standalone Titen Memory skill is public on ClawHub; its
  bundle-plugin package is staged while an upstream inspector incident blocks
  live package publication.
- The TypeScript SDK now covers project resolution, evidence-linked claims,
  feedback, checkpoints, leases, and handoffs with typed results, bounded
  timeout/signal handling, structured API errors, and mutation retry keys. The
  same nine operations are available through MCP with truthful schemas.
- Handoff recipients can read the exact delegated checkpoint and currently
  authorized context pack; operators can page organization leases and active
  organization-level owners/admins can force-release a failed agent's lease.
- `titen migrate --dry-run` prints the pending forward-only SQL without creating
  or changing the SQLite database.
- `titen serve --quiet`, bounded cleanup of expired execution state, and
  content-free audits for credential, portability, collaboration, webhook, and
  federation changes use the existing runtime and SQL primitives.
- The README is now a concise international open-source entrypoint with a
  runnable SDK example, explicit maturity boundaries, and a prominent link to
  [titen.dev](https://titen.dev).

### Changed

- **Breaking:** JSONL export format v2 adds workspace and membership streams,
  actor mappings, supersession pointers, deployment-scoped export authority,
  and byte-bounded pages. Import remains backward-compatible with v1, but code
  that parses export headers must accept format version 2.
- **Breaking:** MCP now exposes nine tools and uses normalized JSON-RPC tool
  results with structured content. Clients that hard-code the previous
  seven-tool list or result shape must update for `0.3.0`.
- Migration 11 atomically rebuilds the derived observation and claim FTS tables
  with Porter stemming and encoded scope terms; canonical SQL is unchanged.
- Migration 12 repairs unsafe collaboration pointers, adds database fences for
  handoff resolution, scopes idempotency to principals across key rotation, and
  assigns monotonic event order without changing public event IDs.
- The default npm install no longer downloads `sqlite-vec`. SDK and lexical-only
  users install only `titen-memory`; vector-enabled VPS and container paths add
  `sqlite-vec@0.1.9` explicitly.

### Security

- JSON depth, unsafe controls, malformed Unicode, non-sortable timestamps, and
  inverted validity windows now fail before canonical mutation. Each returned
  memory item is marked untrusted, feedback is limited to the context actor or a
  currently authorized intended delegate, and evidence purge removes FTS and
  vector projections without exposing a general MCP deletion tool.
- Whole-organization export now requires the separate `export:all` scope and
  writes a metadata-only audit record. Portable actor ownership survives only
  through explicit, preflighted source-to-destination mappings; importing on
  behalf of another principal additionally requires `keys:manage`.

### Fixed

- Lexical retrieval now stems multilingual terms, removes stopword noise,
  applies organization and subject scope before ranking and candidate limits,
  preserves useful tail terms, and fills token budgets without duplicate
  statements or an arbitrary three-item-per-kind ceiling.
- Context compilation bounds correlated SQL work before evidence hydration, so
  large authorized corpora do not turn small requested result sets into
  unbounded query work.
- Checkpoint saves and handoff resolutions now have database-enforced single
  winners under D1 latency, with deterministic duplicate repair and safe
  handoff foreign keys during migration. Handoff preflight and migration also
  reject incomplete, foreign, mismatched, or unauthorized context packs.
- Idempotent retries now follow the acting principal across API-key rotation
  while retaining the original credential ID for audit and preserving
  cross-principal isolation.
- Event polling and federation pulls now page by a database-assigned monotonic
  sequence without changing public event-ID cursors, preventing equal-timestamp
  UUID ordering from skipping committed events; exhausted polling preserves the
  caller's cursor.
- An explicitly scoped REST tombstone removes readable observation and
  dependent-claim text while retaining hashes, provenance, and audit history.
- Validation errors distinguish missing values, identify nested field paths,
  and keep raced purge/consolidation and lifecycle writes fail-closed on both
  SQLite and D1.
- Versioned JSONL v2 restores workspaces, active memberships, team-scoped
  records, actor provenance, claim evidence, and supersession pointers. Export
  pages are UTF-8 byte-bounded so every emitted page fits the import boundary;
  v1 imports remain supported, and a purge racing current-claim import rolls the
  whole import back while revoked tombstones remain portable.
- `titen backup` refuses a missing source, verifies a non-empty current schema
  plus integrity and foreign keys, and atomically refreshes a fixed output path
  without exposing an internal stack. `titen schema` output is deterministic
  and safely repeatable.
- The installed CLI now explains when Bun is missing, while SDK-only installs
  no longer download the optional `sqlite-vec` native package.
- No-vector deployments avoid unused index-outbox work. Configured vector
  deployments retain claim upserts and purge deletes; SQLite uses explicit
  `synchronous=FULL`, and CLI startup failures are short and actionable.

## [0.2.1] — 2026-07-31

### Fixed

- Key creation now returns the canonical `principal_id`, so SDK callers can use
  a newly generated agent identity for handoffs without confusing it with the
  credential's `key_id`.
- Contributor setup now documents a temporary writable `HOME` and
  `XDG_CONFIG_HOME` for pnpm/Wrangler checks in restricted containers.

## [0.2.0] — 2026-07-31

### Added

- **An authorized reviewer queue in Memory Atlas.** The read-only lens derives
  deterministic review work, counts, ownership, evidence, and opaque pagination
  from canonical state without introducing a second queue database.
- **Durable, signed Bun webhooks and rootless deployment artifacts.** Delivery
  uses bounded leases, stable delivery IDs, retries, terminal failure, TLS
  address pinning, and externally keyed secret encryption; the checked-in
  Quadlet keeps the public binding on loopback by default.
- **Broader SDK and CLI coverage.** Typed claim lifecycle/reviewer operations,
  generic authenticated JSON/raw requests, mutation idempotency, strict flag
  validation, and installed-tarball verification now cover the shipped surface.
- **Current agent integration guidance.** Claude Code, Codex, OpenClaw, Hermes,
  Pi, and generic clients share one MCP/REST contract; deliberately deferred
  native adapters are recorded in `PONYTAIL-DEBT.md`.

### Security

- **Authorization now precedes every protected projection.** Team records are
  workspace-bound and require active membership across context, evidence,
  exports, events, Atlas, vectors, and webhook delivery; checkpoint and webhook
  state is principal-bound within an organization.
- **Signing secrets are no longer canonical plaintext.** Webhook and federation
  secrets use versioned AES-256-GCM envelopes backed by an external keyring;
  missing or wrong key material keeps readiness closed, while unrecoverable
  legacy integrations without a secret are terminalized for safe replacement.
- **The MCP transport validates its trust boundary.** Cross-origin browser
  requests and unsupported protocol revisions fail before tool execution, while
  tool annotations let hosts apply read/write approval policy correctly.

### Fixed

- **Schema and mutation races now have one winner.** Migrations recover from
  faults and concurrent startup, leases and claim lifecycle transitions are
  fenced atomically, and idempotency binds the credential plus full request
  identity on both SQLite and D1.
- **Imports are fully preflighted and atomic.** Orphan evidence, collisions, and
  invalid late rows roll back canonical, history, FTS, event, audit, and outbox
  effects even beyond one database batch.
- **Retrieval cannot lose authorized tail terms or top-K slots.** Lexical term
  selection is position-independent, and vector organization/subject/project
  filters execute before ranking on both runtimes.
- **Webhook delivery survives retries, timeouts, crashes, and restart.** Queue
  claims are atomic, expired leases recover, pending age is observable, and
  caller-visible queue state excludes other principals. Membership revocation
  terminalizes queued delivery before any further outbound request.
- **Federation peers and cursors are principal-bound.** Same-organization
  credentials cannot discover, mutate, advance, or replay another principal's
  peer or private event stream. Signed push data is stored as an owner-visible
  untrusted wrapper, so remote actor and resource pointers grant no local
  authority.
- **Runtime and package gates reflect real operation.** Maintenance freshness,
  bounded WAL checkpoints, isolated workerd contract execution, rootless restart
  behavior, README links, SDK error parsing, CLI help, and npm install smokes are
  checked through the actual runtime paths.

### Changed

- **Breaking:** incomplete policy, channel, release, and caller-selected customer
  context routes were removed until their authorization contracts are ready.
  Pre-`1.0`, this requires the `0.2.0` minor release.
- MCP tools now delegate to the same validated application operations as REST,
  negotiate through protocol `2025-11-25`, and expose seven wire tools across six
  semantic families.

## [0.1.2] — 2026-07-30

### Added

- **A five-minute small-team golden path.** The documented researcher, writer,
  operator, and reviewer flow uses four scoped keys and the public SDK to prove
  evidence, conflicting claims, context, checkpoint, lease, handoff, feedback,
  citations, and freshness against a real Bun/SQLite service.
- **Optional loopback live dashboard data.** Conflict & Freshness can read one
  subject-scoped Atlas view through a same-origin adapter that keeps the Titen
  key server-side. Static serving rejects traversal, encoded/backslash/NUL, and
  symlink escapes; every other dashboard lens stays visibly synthetic.
- **More SDK coverage.** The published client now exposes the shipped
  checkpoint, lease, handoff, evidence, and Atlas-view operations used by the
  golden path.
- **One canonical live verifier.** `pnpm verify:live` replaces the duplicate
  script name and remains explicit about requiring a provisioned deployment.

### Fixed

- **Semantic ranking now works with real narrow-band cosine scores.** Vector
  similarity is normalized inside the authorized candidate set and confidence
  is an explicit weighted component, so a semantically correct lower-confidence
  claim no longer ranks last behind lexical decoys.
- **Index dependency outages are retryable.** Embedder and vector-store failures
  return bounded `503 UNAVAILABLE` metadata while leaving pending outbox work
  intact.
- **Canonical imports are order-independent within a request.** Imports preflight
  missing parents before mutation, accept child-before-parent lines atomically,
  and return `UNRESOLVED_REFERENCE` instead of mislabeling a missing dependency
  as a conflict.
- **Readiness reports background repair honestly.** Bun reports whether its
  maintenance timer was actually created; Cloudflare reports external scheduler
  ownership without pretending the request isolate can observe Cron state.

### Changed

- The API reference is verified against the router's 58 implemented routes and
  clearly separates proposed endpoints from callable ones.
- Dashboard screenshots, mobile disclosure, and the capability matrix identify
  synthetic, locally verified, and planned regions consistently.

## [0.1.1] — 2026-07-30

### Fixed

- **`./package.json` is reachable again.** An `exports` map hides every subpath
  it does not list, so `require("titen-memory/package.json")` failed with
  `ERR_PACKAGE_PATH_NOT_EXPORTED`. Bundlers and tooling read that file. It is
  now listed explicitly, and `scripts/verify-pack.sh` fails if it stops
  resolving.

## [0.1.0] — 2026-07-30

First published release. Titen was previously reachable only by cloning the
repository.

### Added

- **`titen` CLI on the registry.** `bunx titen-memory serve` starts the memory
  service with no clone and no build step. Also `bootstrap`, `migrate`,
  `key create|list|revoke`, `backup`, and `schema`.
- **Agent SDK for every runtime.** `import { TitenClient } from "titen-memory"`
  is plain `fetch` — Node 22+, Bun, Deno, and edge workers. The `titen-memory/sdk`
  subpath resolves to the same client.
- **`scripts/verify-pack.sh`** — the release gate. It installs the real tarball
  into a throwaway directory and exercises bootstrap, serve, and the SDK, because
  every path resolves in a working tree even when `files` forgot to ship it.
- **`docs/engineering/release.md`** — the manual release procedure.
- **`CLAUDE.md`** — agent guidance that points at `AGENTS.md` instead of
  restating it.

### Changed

- **`astro` moved to `devDependencies`.** It only builds the dashboard, but it
  was declared as a runtime dependency, so installing Titen pulled a full build
  toolchain onto every consumer's disk. A consumer tree is now three packages:
  `titen-memory`, `sqlite-vec`, and its platform binary.

### Notes

- The tarball is 41 files / ~73 kB: the Web-Standards kernel, the Bun runtime,
  and a type-stripped SDK for Node. The Astro dashboard, the Cloudflare adapter,
  tests, and docs are not shipped — deploy the Worker from a clone.
- `sqlite-vec` is an `optionalDependency` loaded lazily. Without it retrieval
  degrades to lexical FTS5 and `/readyz` reports the vector capability as
  disabled rather than failing.
- **The CLI requires Bun** — it uses `bun:sqlite`, and the published `bin`
  carries a `#!/usr/bin/env bun` shebang. `npx titen-memory` works only with Bun
  on `PATH`. The SDK has no such constraint.
- 0.1.0 carries no git tag. It was published from a staging tree assembled
  before the packaging work was committed, and was superseded by 0.1.1 the same
  day. Prefer 0.1.1.

## Package name

npm refuses to register `titen`:

```
403 Forbidden - PUT https://registry.npmjs.org/titen
Package name too similar to existing package vite
```

That is npm's typosquat filter, not a collision — `npm view titen` still returns
404. The package is therefore `titen-memory`. The **command stays `titen`**,
because that comes from the `bin` field rather than from the package name:

```console
$ bunx titen-memory serve
titen — self-hosted memory service
```

## Releasing

Releases are cut by hand from a maintainer's machine. GitHub Actions stays
disabled so the repository has no hosted automation cost; manual publication
also keeps the npm token out of repository secrets. See
[`docs/engineering/release.md`](./docs/engineering/release.md).

[Unreleased]: https://github.com/RamaAditya49/titen/compare/v0.10.15...HEAD
[0.10.15]: https://github.com/RamaAditya49/titen/releases/tag/v0.10.15
[0.10.14]: https://github.com/RamaAditya49/titen/releases/tag/v0.10.14
[0.10.13]: https://github.com/RamaAditya49/titen/releases/tag/v0.10.13
[0.10.12]: https://github.com/RamaAditya49/titen/releases/tag/v0.10.12
[0.10.11]: https://github.com/RamaAditya49/titen/releases/tag/v0.10.11
[0.10.10]: https://github.com/RamaAditya49/titen/releases/tag/v0.10.10
[0.10.9]: https://github.com/RamaAditya49/titen/releases/tag/v0.10.9
[0.10.8]: https://github.com/RamaAditya49/titen/releases/tag/v0.10.8
[0.10.7]: https://github.com/RamaAditya49/titen/releases/tag/v0.10.7
[0.10.6]: https://github.com/RamaAditya49/titen/releases/tag/v0.10.6
[0.10.5]: https://github.com/RamaAditya49/titen/releases/tag/v0.10.5
[0.10.4]: https://github.com/RamaAditya49/titen/releases/tag/v0.10.4
[0.10.3]: https://github.com/RamaAditya49/titen/releases/tag/v0.10.3
[0.10.2]: https://github.com/RamaAditya49/titen/releases/tag/v0.10.2
[0.10.1]: https://github.com/RamaAditya49/titen/releases/tag/v0.10.1
[0.10.0]: https://github.com/RamaAditya49/titen/releases/tag/v0.10.0
[0.9.1]: https://github.com/RamaAditya49/titen/releases/tag/v0.9.1
[0.9.0]: https://github.com/RamaAditya49/titen/releases/tag/v0.9.0
[0.8.7]: https://github.com/RamaAditya49/titen/releases/tag/v0.8.7
[0.8.6]: https://github.com/RamaAditya49/titen/releases/tag/v0.8.6
[0.8.5]: https://github.com/RamaAditya49/titen/releases/tag/v0.8.5
[0.8.4]: https://github.com/RamaAditya49/titen/releases/tag/v0.8.4
[0.8.3]: https://github.com/RamaAditya49/titen/releases/tag/v0.8.3
[0.8.2]: https://github.com/RamaAditya49/titen/releases/tag/v0.8.2
[0.8.1]: https://github.com/RamaAditya49/titen/releases/tag/v0.8.1
[0.8.0]: https://github.com/RamaAditya49/titen/releases/tag/v0.8.0
[0.7.4]: https://github.com/RamaAditya49/titen/releases/tag/v0.7.4
[0.7.3]: https://github.com/RamaAditya49/titen/releases/tag/v0.7.3
[0.7.2]: https://github.com/RamaAditya49/titen/releases/tag/v0.7.2
[0.7.1]: https://github.com/RamaAditya49/titen/releases/tag/v0.7.1
[0.7.0]: https://github.com/RamaAditya49/titen/releases/tag/v0.7.0
[0.6.1]: https://github.com/RamaAditya49/titen/releases/tag/v0.6.1
[0.6.0]: https://github.com/RamaAditya49/titen/releases/tag/v0.6.0
[0.5.7]: https://github.com/RamaAditya49/titen/releases/tag/v0.5.7
[0.5.6]: https://github.com/RamaAditya49/titen/releases/tag/v0.5.6
[0.5.5]: https://github.com/RamaAditya49/titen/releases/tag/v0.5.5
[0.5.4]: https://github.com/RamaAditya49/titen/releases/tag/v0.5.4
[0.5.3]: https://github.com/RamaAditya49/titen/releases/tag/v0.5.3
[0.5.2]: https://github.com/RamaAditya49/titen/releases/tag/v0.5.2
[0.5.1]: https://github.com/RamaAditya49/titen/releases/tag/v0.5.1
[0.5.0]: https://github.com/RamaAditya49/titen/releases/tag/v0.5.0
[0.4.1]: https://github.com/RamaAditya49/titen/releases/tag/v0.4.1
[0.4.0]: https://github.com/RamaAditya49/titen/releases/tag/v0.4.0
[0.3.0]: https://github.com/RamaAditya49/titen/releases/tag/v0.3.0
[0.2.1]: https://github.com/RamaAditya49/titen/releases/tag/v0.2.1
[0.2.0]: https://github.com/RamaAditya49/titen/releases/tag/v0.2.0
[0.1.2]: https://github.com/RamaAditya49/titen/releases/tag/v0.1.2
[0.1.1]: https://github.com/RamaAditya49/titen/releases/tag/v0.1.1
[0.1.0]: https://www.npmjs.com/package/titen-memory/v/0.1.0