Skip to content

v1.4.4

Choose a tag to compare

@github-actions github-actions released this 06 Oct 20:38
· 19 commits to main since this release

Release Notes v1.4.4

Released: 2026-10-06

This release finishes the team migration story: a project's graph now moves to a team server alongside its raw memories, retries stop at teammate edits instead of writing over them, and the whole migration can be undone. Supporting that are new revision-guarded entity write paths, typed publication staged inside native transactions, a stricter health probe, and fixes for two high-severity dependency advisories.

Highlights

✨ Move a project's whole graph to a team server

sibyl migrate to-team now runs a second pass over the graph (--graph, on by default; --no-graph to skip). Tasks, epics, milestones, decisions, procedures, notes, and their links are recreated on the team server in topological order through POST /entities/bulk and the new links endpoint. Derived rows (topics, passages, mention edges) are left out so the target re-derives them. Everything is written through the authenticated API as the caller, so no cluster or database access is needed. See the new docs/cli/migrate.md.

✨ Undo a team migration without touching anyone's work

sibyl migrate to-team --undo removes what the migration created and nobody has changed since, working from the migration ledger alone. Rows a teammate edited, rows something outside the migration links to, and rows the migration adopted rather than created are all kept. Deletes name the exact revision the migration last wrote, and the server re-checks it under lock. Pair with --dry-run to preview the outcome via GET /entities/{entity_id}/deletable.

🐛 Retries no longer overwrite teammates

When a migration retries after a lost response, writes land through EntityManager.create_direct_authorized(), which calls replace_authorized_entity() in the new sibyl_core.services.graph_write_authority module. If an entity identity already exists under a different owner or project, the write is refused ("Entity identity already exists under another owner or project"). The upsert itself is fenced on a snapshot fingerprint taken before the write: if the row changed in between, the transaction returns 'changed write authority' and the write is rejected rather than applied. Link writes carry expected_revision so topology additions fail closed on concurrent edits.

✨ Typed publication staged in native transactions

stage_native_typed_graph_publication() collects, validates, and stages a typed graph entity inside a caller-owned transaction and never commits it. The caller decides whether to commit or seal the handle with the new NativeTransaction.invalidate(). Source evidence is captured as PublicationSourceEvidence records (namespace, database, row and state hashes) on the returned StagedTypedGraphPublication, and concurrent writes to referenced sources are caught by witness checks before commit instead of after.

🔧 Health reports unhealthy when auth storage is unreadable

A running API could previously report healthy while unable to read auth storage. The readiness probe now performs a bounded auth-store read (SELECT id FROM users LIMIT 1) and returns 503 with dependency detail when it fails. No per-org namespaces are queried and no auth records reach the response body.

Team Migration

  • sibyl migrate to-team gained --graph / --no-graph, --undo, --target-project, --share-private, --allow-personal-org, and --limit, alongside the existing --target-context, --project, --source-org, and --dry-run.
  • Graph creation is ordered by class so containers land before their contents: epics and milestones first, tasks second, everything else after. Links are declared once their targets exist.
  • An epic or milestone a teammate already created under the same name is adopted rather than duplicated, and adopted rows are recorded so --undo never removes them.
  • Each row keeps a stable idempotency key across retries (migration-raw:<sha256>, migration-create:<sha256>, migration-links:<sha256>), so a replayed request returns the original receipt instead of writing twice.
  • Where an earlier outcome is genuinely unknown, callers can pass replay_interrupted=false to refuse blind re-execution; the server answers 409 and the ledger retains the request body for reconciliation.
  • New CLI test coverage in apps/cli/tests/test_migrate_graph.py and expanded test_migrate_to_team.py.

API Changes

  • POST /entities/{entity_id}/links (new, in apps/api/src/sibyl/api/routes/entity_links.py): adds missing links to an existing entity without replacing its content. Requires expected_revision (≥1) and accepts related_to, epic_id, parent_task_id, depends_on. Responds with EntityLinksResponse carrying the new revision, added_relationship_ids, existing_relationship_ids, and replayed. Revision mismatches return 409.
  • GET /entities/{entity_id}/deletable (new): dry-runs the checks a guarded unshared delete would run and reports deletable plus a reason, without deleting anything.
  • DELETE /entities/{entity_id}: new expected_revision and if_unshared query params. With if_unshared=true, the delete is refused with 409 entity_shared when a row outside the entity's own migration links to it. Derived rows and the project itself do not count as sharing.
  • POST /entities: new protect_ownership and replay_interrupted query params. protect_ownership=true requires an authenticated sync=true request and returns 422 otherwise. The response now includes revision. When an epic is supplied on a synchronous create, the epic's existence is now verified before the row is filed (if epic and is_sync in entity_mutations.py).
  • PATCH /tasks/{task_id}: new replay_interrupted query param, an added epic-existence check on the update path, and an epic_started key in the response payload when a status change starts the parent epic.
  • PATCH /entities/{entity_id} now records modified_by from the authenticated user.

Graph Internals

  • New sibyl_core.services.graph_write_authority exports row_author(), row_project(), and replace_authorized_entity().
  • New sibyl_core.services.graph_link_writes and graph_publication_fence modules back the links endpoint and staged publication respectively.
  • EntityManager.delete_many() deletes several entities and their edges in one transaction, using per-id equality predicates so each statement stays index-served rather than scanning the table.
  • source_state_store gained NativeSourceCut, load_native_source_cut(), and check_native_source_cuts() for engine-hashed physical evidence.
  • bulk_identity_parts() was extracted in entity_serialization.py, and bulk-created entities now carry created_by.

Operations and Security

  • The API runtime image now applies Debian security upgrades during build, clearing critical and high advisories in perl and its bundled modules that the pinned debian:12-slim base still shipped.
  • sharp lifted to 0.35.5 (GHSA-wq5f-xc86-pv6w) and source-map-js to 1.2.2 (GHSA-68fv-2mgg-jv7q), both high-severity advisories in packages shipped by the web app. pnpm audit --prod is clean again.
  • Dependency rollup: uvicorn[standard] 0.53.0, pyjwt[crypto] 2.14.0, ty 0.0.84, plus Biome, Playwright, Vitest, Mermaid, and TanStack Query updates. pydantic-ai-slim stays pinned under its <2.52 cap because 2.52 changes structured-output and tool-choice behavior.
  • Docs theme now imports Mermaid through the bundler, so architecture diagrams render.

Reverted

  • Abstained chain retirement (fix(memory): retire abstained chains without changing evidence) was reverted. Routing native reflection ids through the capture-availability projection hid reflections published with reflect_memory(persist=True) from recall. Memory persistence behaves as it did in 1.4.3; the change will return once the default memory loop smoke test passes.

Upgrade Notes

  • Check your health probe expectations. /health now returns 503 when auth storage cannot be read. If a deployment was passing probes while auth storage was degraded, it will now be marked unready. Confirm auth storage connectivity before rolling out.
  • Rebuild or repull the API image to pick up the Debian security upgrades.
  • Reinstall JS dependencies so the sharp and source-map-js overrides take effect; verify with pnpm audit --prod.
  • Existing clients need no changes: replay_interrupted defaults to true and protect_ownership defaults to false, preserving prior behavior. Opt in only where you need the stricter guarantees.
  • Running sibyl migrate to-team --undo requires project maintainer access on the target server, since graph deletes are maintainer-gated. Preview with --dry-run first.

Install

Local server

curl -fsSL https://raw.githubusercontent.com/hyperb1iss/sibyl/main/install.sh | sh -s -- --version 1.4.4

Remote CLI

curl -fsSL https://raw.githubusercontent.com/hyperb1iss/sibyl/main/install.sh | sh -s -- --remote --version 1.4.4
sibyl init --remote https://sibyl.example.com
sibyl auth login

Homebrew

brew install hyperb1iss/tap/sibyl
sibyl up

Arch Linux (AUR)

paru -S sibyl
sibyl up

Headless server

curl -fsSL https://raw.githubusercontent.com/hyperb1iss/sibyl/main/install.sh | sh -s -- --version 1.4.4 --no-open

Kubernetes (Helm)

helm repo add sibyl https://raw.githubusercontent.com/hyperb1iss/sibyl/gh-pages
helm repo update sibyl
helm upgrade --install sibyl sibyl/sibyl --version 1.4.4

Artifacts

This release includes Python wheels and sdists, the generated
Homebrew formula, the generated AUR PKGBUILD, Helm charts, Docker
SBOMs, aggregate dual-registry cosign receipts, and a SHA256 checksum
manifest.