Changelog¶
All notable changes to Lithos are documented here. The full changelog is maintained in the main repository.
v0.6.0¶
Released: 2026-10-04 · GitHub Release · PyPI · Docker Hub
Install:
Scoped search and retrieve now rank inside the requested scope, task edges can be deleted, and OpenRouter can attribute Lithos spend to its own app. The tool surface grows from 38 to 39 (lithos_task_edge_delete); none removed. Full detail in the repo CHANGELOG.
Behaviour changes — read before upgrading¶
Permitted by the pre-1.0 compatibility policy (SPECIFICATION.md §1.4).
- Scoped searches return different, better results (PRs #441–#443).
lithos_searchinfulltext/semantic/hybridmode now ranks insidepath_prefix/tags/author/entities. So dolithos_retrieve's vector and lexical scouts insidenamespace_filter/tags/path_prefix. These filters used to apply after ranking the whole knowledge base, so a narrow scope, such as an agent's few notes, often came back empty. Callers that over-fetched to work around this (a largelimit,threshold=0) can drop the workaround. - Dependency floors.
sentence-transformers>=5.4.0(was 2.2.0) andnumpy>=1.22.5are now declared. - No migrations. The new scope indexes are built in memory at startup, so rolling back to 0.5.0 is safe.
Added¶
lithos_task_edge_delete(PR #436): removes ablocks,parent_child,waits_on_gateordiscovered_fromedge. It's a hard delete keyed on(from, to, type), and readiness and hierarchy update immediately. It's the way to re-parent a task (theparent_existsremedy) and to release a waiter from an unwanted gate without falsely completing it. New error codeedge_not_found. See Task Graph.- OpenRouter app attribution (PR #440): the LLM synthesis client sends
HTTP-RefererandX-OpenRouter-Title, from the newlcma.llm.app_url/lcma.llm.app_titlesettings. See Configuration.
Fixed¶
- Scoped
lithos_searchandlithos_retrieve(task 5002185d; PRs #441, #442, #443): see the behaviour change above. Scoped semantic search chooses a strategy by the scope's size. A small scope (up to 200 notes and 2,000 chunks) is ranked exactly from its stored embeddings. Larger scopes widen an unfiltered pool first and fall back to a scope-filtered vector query, which keeps scoped latency close to unscoped on a 100k-chunk store. - Embedding health probe (PR #438): the probe no longer logs a
FutureWarningfrom sentence-transformers on every/healthcall.
v0.5.0¶
Released: 2026-09-26 · GitHub Release · PyPI · Docker Hub
Install:
LCMA phase-3 groundwork (salience recalibration, an LLM-synthesis substrate and typed-edge inference), agent-roster hygiene, optimistic concurrency on tasks, and short id prefixes everywhere. The tool surface grows from 37 to 38 (lithos_agent_archive); none removed. Full detail in the repo CHANGELOG.
Behaviour changes — read before upgrading¶
Permitted by the pre-1.0 compatibility policy (SPECIFICATION.md §1.4).
- Error codes tightened for unknown ids (PR #412). A 6–35-char task id matching nothing now returns
task_not_foundon every task tool — claim/renew/release used to sayclaim_failed/claim_not_found, and status/edge_list/children/finding_list used to return silently empty. An id under 6 chars matching nothing exactly isinvalid_input.lithos_writewith an unknownidreturns anote_not_foundenvelope instead of a protocol-level error. - SSE consumers must handle
event: resync(PR #400).GET /eventsnow announces when a reconnecting client'sLast-Event-IDfell off the replay buffer. Clients that ignore it miss events exactly as before — the difference is the gap is now visible. lcma.llm_provideris removed (PR #405), replaced by thelcma.llmblock. The old field was never read; aLITHOS_LCMA__LLM_PROVIDERenv var now has no effect.- Salience decay stops at a floor (PR #402) —
lcma.salience_floor, default 0.3 — instead of decaying to zero, and a usage signal enters reranking. Ranking on existing corpora shifts; runlithos recalibrate-salienceonce (dry-run first) to lift collapsed rows. - Three idempotent startup migrations on an existing
coordination.db/edges.db:agents.archived_at,tasks.updated_at(backfilled fromCOALESCE(resolved_at, created_at)), and thellm_budget/edge_inference_logledger tables. Rolling back to 0.4.0 is safe — the older code ignores the extra columns and tables.
Added¶
- Short ID prefixes accepted everywhere (PR #412): every task/note id parameter takes an unambiguous git-style prefix (≥ 6 chars). Ambiguity fails loudly with the new
ambiguous_id_prefixcode carrying up to 5{id, title}candidates; mutating responses now echo the resolved full id + title. Behaviour tightened along the way: unknown 6–35-char task prefixes returntask_not_foundon every task tool (previouslyclaim_failed/claim_not_found/silently empty), andlithos_writewith an unknownidreturns anote_not_foundenvelope instead of a protocol-level error. See Envelopes, Errors & IDs. lithos_task_updatecompare-and-set + tag set operations (PR #419):expected_updated_atguards a write with the stamp from a prior read; on mismatch nothing is written and the tool returns{status: "error", code: "version_conflict", current_updated_at}. Newadd_tags/remove_tagsedit the tag list as set operations under the write transaction, so incremental tag edits from stale reads compose instead of clobbering. See Tasks.lithos_agent_archive(PR #426): retires an agent from the roster while keeping its history; any later activity by that id resurrects it.lithos_agent_list(include_archived=True)shows archived agents, every agent row carriesarchived_at,lithos inspect agents --include-archivedmatches, andlithos_agent_registernow returns awarningslist naming other active agents with the same display name (registration never blocks). See Agent & Finding Tools.lithos_readreturnspath(PR #428): the note's file path relative toknowledge/, identical to whatlithos_listreports, for reads byidand bypathalike — a client reading by id can finally learn where a note lives.updated_aton task records (PR #416): last-modified stamp bumped by every task-row write (create/update/complete/cancel/reopen — claims never bump it), returned by all task-fetching tools, carried in row-mutating events, and echoed by mutating responses. Compare stamps for equality to detect concurrent edits. Existing databases are migrated in place.lithos_retrievedegradation signal (PR #397): responses always carrydegradedandfailed_scouts, so callers can distinguish partial results (a backend down) from an empty corpus. New alertable metriclithos.lcma.scout.failures.- Salience recalibration (PR #402): decay now bottoms out at
lcma.salience_floor(default 0.3) instead of zero; new non-decayingusage_scoreresult field feeds reranking; newlithos recalibrate-salienceCLI command backfills collapsed databases; decay/reinforcement constants became config fields. - Local/remote LLM synthesis (PRs #405, #406): new
lcma.llmconfig block (any OpenAI-compatible endpoint — Ollama, llama.cpp, vLLM, hosted). The background enrichment worker has the LLM adjudicate typed edges between semantically-close notes, written toedges.dbasprovenance_type: "inferred"with rationale and confidence as evidence. Enabled only whenbase_urlis set — unset is a strict no-op. Token spend bounded by a daily budget. - SSE
resynccontrol event (PR #400):GET /eventsnow tells reconnecting clients when theirLast-Event-IDfell off the replay ring buffer, instead of silently under-delivering. Consumers must handleevent: resync.
Fixed¶
- Bare YAML dates in frontmatter broke indexing (PR #411): an unquoted
created: 2026-07-30could silently drop notes from search — and after an internal refactor, crash the startup rebuild. Both YAML ingestion points now normalize date objects; hand-edited frontmatter can no longer abort the startup scan. - Docker: the whole
LITHOS_LCMA__LLM__*family is now forwarded into the container (PRs #409, #410) — previously LLM synthesis stayed silently disabled in compose-managed deployments — andllm.max_output_tokensdefault raised 1024 → 4096 so reasoning models don't exhaust the cap on hidden reasoning. Blank pass-through values are treated as unset (env_ignore_empty). - Bare YAML dates entry above also fixes a startup crash introduced after 0.4.0 by the corpus-index extraction — hand-edited frontmatter can no longer abort the startup scan.
v0.4.0¶
Released: 2026-07-06 · GitHub Release · PyPI · Docker Hub
Install:
Breaking — canonical error envelope (PRs #370, #371)¶
Every tool failure now returns exactly {"status": "error", "code": "<stable_code>", "message": "..."}:
- Validation failures use
code: "invalid_input"— including paths that previously raised protocol-level errors (unparseable datetime filters onlithos_list,lithos_agent_list,lithos_finding_list). - Error envelopes no longer include a
warningskey. - Deliberately unchanged: actionable write outcomes keep their own top-level
status(version_conflictwithcurrent_version,duplicate,slug_collision,path_collision), all success shapes, and the{"success": ...}claim envelopes. - Migration: replace
status == "invalid_input"branches withstatus == "error" and code == "invalid_input"(except onlithos_write/lithos_note_update, where the code remains the status). This partially reverses 0.3.0's error-status promotion. Full guide: Envelopes, Errors & IDs.
Added — task graph (Phases 1–3)¶
Dependencies became first-class edges (task_edges table + task_type column), adding six tools — lithos_task_edge_upsert, lithos_task_edge_list, lithos_task_ready, lithos_task_blocked, lithos_task_children, lithos_task_spawn (PRs #342, #343, #356):
- Edge types
blocks,parent_child,discovered_from,waits_on_gate; cycle-checked on write. lithos_task_creategainstask_type(task/epic/gate),depends_on,parent_task_id;lithos_task_completereturnsunblocked.- Gates model external waits (
human/timer/ci/pr/external_task) — Lithos never polls; timer gates auto-resolve at query time. - A cancelled predecessor leaves dependents
blocker_unsatisfiable, never spuriously ready. - Breaking:
metadata.depends_on/blocked_onare rejected withinvalid_metadata_key; a one-time migration backfilled existing conventions intoblocksedges.
Added — lifecycle & notes¶
lithos_task_reopen(PR #357): terminal tasks return toopen, with areblockedlist, a durable[Reopened]finding, and atask.reopenedevent.lithos_task_updatenow works on terminal tasks (#303).lithos_note_update(PR #363): patch a note's frontmatter (tags/metadata/title/status) without resending the body.
With these, the tool count reached 37.
Changed¶
- Python 3.12 is now required (PR #365).
v0.3.3¶
Released: 2026-06-10 · GitHub Release
Packaging fix (PR #338): the published wheel no longer carries the spaCy model as a direct-URL dependency (which broke pip install lithos-mcp). The en_core_web_sm model is downloaded on first use instead.
v0.3.2¶
Released: 2026-06-09 · GitHub Release
Breaking¶
- Transport
sserenamed tohttp(PR #304) — no back-compat alias.lithos serve --transport httpnow serves bothPOST /mcp(StreamableHTTP, stateless) andGET /sse(legacy) on one port; any compliant MCP client connects without a bridge. Update CLI invocations, systemd units, and compose overrides.
Added¶
- Entity extraction (PRs #313, #321, #329): wiki-links + spaCy NER + corroborated heuristics populate an
entitiesfrontmatter list, with extractor provenance so agent-curated entities are never clobbered. New CLI commandlithos extract-entities. entitiesfilter onlithos_searchandlithos_list(PRs #316, #319): exact-match AND filtering, inverted-index backed; entities also boost full-text ranking.
Fixed¶
lithos_cache_lookupno longer crashes when a candidate hasconfidence: null; confidence is healed on read and validated on write (PR #314).- The Docker image bakes in the embedding model and loads it offline — no more HuggingFace rate-limit failures at container start.
v0.3.1¶
Released: 2026-06-05 · GitHub Release · PyPI
First PyPI release: pip install lithos-mcp.
Added¶
- Free-form
metadataon knowledge notes (PR #305): arbitrary key/values persisted to frontmatter vialithos_write(metadata=...), returned bylithos_read/lithos_list. Update semantics: omit preserves,{}clears, non-empty merges per key withnulldeleting a key. metadata_matchfilter onlithos_listandlithos_task_list(PR #306): AND across keys; matches scalar equality or list containment; index-backed on notes, SQL-pushed on tasks.
v0.3.0¶
Released: 2026-05-27 · GitHub Release
Breaking¶
tasks.completed_atrenamed toresolved_at(PR #288) — now written on both terminal transitions (complete and cancel), with an in-place schema migration.lithos_task_listgains aresolved_sincefilter; thetask.completedevent field renamed to match.lithos_task_update(metadata=...)became an additive per-key merge (PR #291):{"key": null}deletes a key, unmentioned keys are preserved,{}is a no-op; there is no wholesale clear. Runs in a single transaction so concurrent writers on different keys can't clobber each other.- Write error codes promoted to top-level
status(PR #217):lithos_writefailures becamestatus="slug_collision"etc. (Partially reversed for other tools by 0.4.0's canonical envelope — see above.)
Added¶
lithos_task_get(PR #294): single-task fetch with the full record and an explicittask_not_foundenvelope;lithos_task_statuswidened to the same field set.lithos_task_list(with_claims=true)(PR #221): inline active claims per task in one batched query.metadataon tasks (PR #216) — arbitrary JSON at create time.
Fixed¶
lithos_write(path="a/b.md")treats a.md-terminated path as the complete filename instead of silently creating a directory namedb.md(PR #301).- File-watcher events carry the canonical
{id, title, path}payload, so external Obsidian edits are no longer dropped by id-filtering consumers (PR #298); external renames preserve the document id. task.updatedevents are actually emitted (PR #284).
v0.2.1¶
Released: 2026-04-18 · GitHub Release · PyPI · Docker Hub
Install:
Added¶
-
lithos_related— composite graph tool (PR #188): New MCP tool that merges wiki-links, provenance chains, and LCMA typed edges into a single response. Eliminates the need to fan out across multiple tools and manually join results.Response shape:
{ "id": "<doc>", "included": ["links", "provenance", "edges"], "links": { "outgoing": [...], "incoming": [...] }, "provenance": { "sources": [...], "derived": [...], "unresolved_sources": [...] }, "edges": { "outgoing": [...], "incoming": [...] }, "related_ids": ["<uuid>", ...] }includecontrols which backends are queried — omit a key and it's excluded from the response entirely.related_idsis the deduplicated union of all referenced document IDs (excluding the queried doc itself).depthis supported (1–3) for the links and provenance sections.namespacescopes theedgessection only.
→ See
lithos_related -
--telemetry-consoleflag onlithos serve(PR #187): DX shortcut that enables in-process OTEL console exporters without a collector. Metrics and spans go to stdout — useful for local debugging. -
LCMA MVP2 (PR #170, #177, #176): Second phase of the Layered Cognitive Memory Architecture. Adds OTEL metrics coverage across the full LCMA pipeline and comprehensive structured logging throughout the LCMA codebase.
-
Multiple environment support (PR #180): Lithos now supports separate named environments (e.g.
prod,staging) with independent OTEL tracking. Useful for running multiple Lithos instances on the same host. -
outcomeparameter onlithos_task_complete(fix for PR #182): Tasks can now record a completion outcome string. The value is persisted on the task row and included in thetask.completedevent payload for LCMA consolidation.→ See Task Tools Reference
Removed¶
-
lithos_linksandlithos_provenanceMCP tools (PR #190): Both tools are fully superseded bylithos_related. Their behaviour is available via:lithos_related(id=..., include=["links"])— replaceslithos_linkslithos_related(id=..., include=["provenance"])— replaceslithos_provenance
lithos_edge_listis retained as it supports global edge queries that don't require a single document centre.Breaking change
Remove any calls to
lithos_linksorlithos_provenanceand replace withlithos_related. Theinclude_unresolvedparameter fromlithos_provenanceis dropped — unresolved sources always surface on the composite path.
Fixed¶
lithos_task_completeacceptsoutcomeparam (PR #182): FixesUnexpected keyword argumenterrors when callers passedoutcome=tolithos_task_complete.- Rerank weights rebalanced (PR #183):
vector=0.21, lexical=0.22(was0.25/0.18). Exact-term lexical hits can no longer be structurally dominated by vector-only candidates at equal normalised score. - OTEL gauge caches primed at startup (PR #184): Eliminates stale/missing gauge readings at server start.
setup_logginghardened + handler eviction between tests (PR #185): Prevents log handler accumulation and test-isolation issues.lithos_searchtags filter with colons (PR #192): Tags containing colons (e.g.type:pattern) were silently dropped by the FTS filter. Fixed.lithos_listcontent_query pushes tags/author/path_prefix into FTS (PR #211): Filter fields were not being applied whencontent_querywas used.- LCMA: snippet against
full_content(PR #210): Snippet generation now runs against the full document content, so title-matching passages surface correctly. - LCMA: stored salience emitted in
lithos_retrieveresults (PR #209): Previously salience was not forwarded in result payloads. - LCMA: tag filter uses AND semantics across scouts (PR #208): Tags filter is now applied consistently with AND semantics across all LCMA scouts.
- Server: search mutations wrapped in
asyncio.to_thread(PR #213): Prevents blocking the event loop during write-side search operations. - uv commands corrected in README (PR #212): Dev setup commands updated.
Refactored¶
KnowledgeManager.get_cached_metapublic accessor (PR #186): Exposes the metadata cache via a clean public API for callers that embed lithos as a library.- Build: project now uses
uvexclusively, with Makefile (PR #165):uv sync --extra devfor dev installs;make check,make test, etc. for common actions.
v0.2.0¶
Released: 2026-04-12 · GitHub Release
Added — LCMA MVP1 (Layered Cognitive Memory Architecture)¶
lithos_retrieve— cognitive retrieval tool that orchestrates parallel scouts (vector, lexical, provenance, task-context) with merge-and-normalize, Terrace 1 reranking, and audit receipt logging. Returnsreasons,scouts,salience,temperature,terrace_reached, andreceipt_idper result. Seelithos_retrieve.lithos_edge_upsert— create or update typed edges inedges.db. Upsert key is(from_id, to_id, type, namespace).lithos_edge_list— query edges fromedges.dbby optional filters (from_id,to_id,type,namespace).lithos_writeLCMA fields:note_type,namespace,access_scope,summaries,schema_version— all optional and additive; existing documents are unaffected.- Receipts logging: every
lithos_retrievecall writes an audit receipt (rcpt_*) for full observability.
v0.1.8¶
v0.1.7 skipped
v0.1.7 was skipped due to a tag issue. This release covers all changes between v0.1.6 and v0.1.8.
Added¶
-
lithos_searchnow supportsmode="graph"(PR #146): Traverse the knowledge graph from a starting document and return linked results. Returns documents reachable by wiki-link relationships rather than text/vector similarity. Seelithos_searchfor details. -
lithos_statsextended with health indicators (PR #159): The stats response now includes ahealthblock with pass/warn/fail indicators for each subsystem (index, embedding, coordination). Surfaces the same signal asGET /healthbut in machine-readable per-subsystem form. -
Observability — OTEL tracing (PRs #148, #151): OpenTelemetry tracing added to all untraced code paths. An OTEL log bridge enables trace-log correlation — structured log entries now carry
trace_idandspan_idfields. -
Observability — metrics (PRs #87, #89, #96, #97, #99, #101, #149, #150, #155, #156, #157): Prometheus-compatible metrics exposed across the server:
lithos.knowledge.write_duration_mshistogram — write latency distribution- Resource gauge metrics (documents, chunks, agents, open tasks)
- Startup duration and file watcher event counters
- Event bus subscriber drop and buffer utilisation gauges
- Per-tool call counters and per-tool error counters
- SSE active clients gauge and events-delivered counter
-
Always-on structured JSON logging (PRs #140, #152, #153): All log output is now structured JSON (when
LITHOS_LOG_LEVELis set to any level). DEBUG traces added for link resolution and slug computation. Coordination and knowledge modules emit structured events for observability pipelines (Loki, CloudWatch, etc.). -
Read audit logging (PR #147):
lithos_readcalls are now written to an audit log (append-onlyread_audit.jsonl). Each entry recordstimestamp,document_id,agent, andpath. Useful for compliance and debugging access patterns.
Fixed¶
-
Preserve incoming edges on document update (PR #139): Previously, updating a document that other documents linked to would silently drop those incoming wiki-link edges from the graph. They are now preserved correctly.
-
Docker healthcheck uses HTTP
/health(PR #143, closes #72, #77): The defaultdocker-compose.ymlhealthcheck now callsGET /healthinstead of using a TCP probe. This gives accurate health signals to Compose, Swarm, and Kubernetes. -
Recover from corrupt ChromaDB stores (PR #160): If the embedding store is detected as corrupt on startup, Lithos now logs a clear error and attempts an automatic rebuild rather than crashing. Contributed by @peterbrown05 (first contribution 🎉).
-
ChromaDB metadata types (PR #145, closes #42): Fixed a type error where ChromaDB's
metadataslist was incorrectly typed, causing failures on some document writes.
Refactored¶
-
KnowledgeManagernow requires explicit config (PR #158, closes #35):KnowledgeManagerno longer accepts implicit defaults. All callers must pass aLithosConfiginstance. This only affects users embeddinglithosas a Python library — the CLI and MCP server are unaffected. -
embed_async()dead code removed (PR #144, closes #74): The unused async embedding path was removed to simplify the codebase.
v0.1.6¶
Fixed¶
- PyPI publish fix. No functional changes. Re-publish to resolve a packaging issue with the v0.1.5 release.
v0.1.5¶
Breaking Changes¶
-
lithos_healthMCP tool replaced by HTTP endpoint.GET /healthis now a plain HTTP endpoint (returns200 OKwhen healthy,503when degraded). It is no longer an MCP tool. Update any callers usinglithos_health()to usecurl http://<host>:8765/healthor an HTTP client instead. -
lithos_semanticMCP tool removed. Uselithos_searchwithmode="semantic"for pure semantic search, or the new defaultmode="hybrid"for best results. -
lithos_searchnow defaults to hybrid mode. Existing callers that relied onlithos_searchfor full-text-only results will now receive hybrid (BM25 + semantic RRF) results. Passmode="fulltext"explicitly to restore the previous behaviour. -
similaritykey renamed toscorein search results. Callers migrating fromlithos_semanticthat readresult["similarity"]must update toresult["score"]. All three modes (hybrid,fulltext,semantic) now use a unifiedscorefield. -
agentis now required onlithos_delete. Previously optional. Callers that omit it will receive aTypeErrorfrom the MCP layer. -
sort_by_confidenceremoved fromlithos_cache_lookup. Results are now always sorted by confidence score. Remove thesort_by_confidenceparameter from any calls that use it.
Added¶
-
lithos_task_list— list tasks with optional filters:agent,status("open"|"completed"|"cancelled"),tags(AND), andsince(ISO timestamp). -
lithos_task_cancel— cancel a task, releasing all active claims. Takestask_id,agent, and an optionalreason. -
lithos_task_update— update mutable task metadata (title,description,tags) without closing the task. At least one field must be provided. -
lithos_searchnow accepts amodeparameter:fulltext|semantic|hybrid(default:hybrid). - Hybrid search mode merges Tantivy (BM25) and ChromaDB (cosine similarity) results using Reciprocal Rank Fusion (RRF, k=60) for improved ranking quality.
- Unknown
modevalues now return a structured{ "status": "error", "code": "invalid_mode", ... }dict instead of raising aValueError. lithos_tagsaccepts an optionalprefixparameter to filter tags by prefix.lithos_listaccepts two new optional filters:title_contains(substring match on title) andcontent_query(full-text search within results).
Fixed¶
-
lithos_readreturns structured error on missing document (issue #102): Previously propagated a rawFileNotFoundError. Now returns{ "status": "error", "code": "doc_not_found", "message": "..." }. -
Consistent error envelopes across all tools (issue #85): Coordination tools (
lithos_task_claim,lithos_task_renew,lithos_task_release,lithos_task_complete) andlithos_deletenow all return the standard{ "status": "error", "code": "...", "message": "..." }envelope on failure paths.Tool Error code lithos_delete(not found)doc_not_foundlithos_task_claim(conflict)claim_failedlithos_task_renew(no claim)claim_not_foundlithos_task_release(no claim)claim_not_foundlithos_task_complete(missing/closed)task_not_foundlithos_task_cancel(missing/closed)task_not_foundlithos_task_update(missing)task_not_found
Schema Changes¶
-
versionfield in frontmatter (issue #45, PR #55): All knowledge documents now have aversion: 1integer field in their YAML frontmatter for optimistic locking. Existing documents without this field are treated asversion: 1on first read — no migration needed.The
lithos_writetool now accepts an optionalexpected_versionparameter. If provided and the document's current version doesn't match, the call returns aversion_conflicterror.
Migration Guide¶
From lithos_semantic to lithos_search¶
Before:
results = lithos_semantic(query="how to run async tasks in python")
# results[0]["similarity"] ← old key
After:
results = lithos_search(query="how to run async tasks in python", mode="semantic")
# or use the new default hybrid mode:
results = lithos_search(query="how to run async tasks in python")
# results["results"][0]["score"] ← new key
Fixing lithos_delete calls¶
Before:
After:
Fixing coordination error handling¶
Before:
result = lithos_task_claim(task_id="...", aspect="...", agent="...")
if result.get("success") == False: # old pattern
print("Claim failed")
After: