{"id":50873628,"url":"https://github.com/hinanohart/tracelin","last_synced_at":"2026-06-15T07:30:57.247Z","repository":{"id":361254358,"uuid":"1252955414","full_name":"hinanohart/tracelin","owner":"hinanohart","description":"Conformance \u0026 race linter for recorded multi-agent agent traces (linearizability + happens-before + MAST failure-mode IDs under the hood)","archived":false,"fork":false,"pushed_at":"2026-06-10T13:37:37.000Z","size":103,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-10T14:09:15.166Z","etag":null,"topics":["happens-before","linearizability","linter","llm-agents","multi-agent","observability","opentelemetry","race-condition","trace-analysis"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hinanohart.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":"NOTICE","maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-29T03:11:47.000Z","updated_at":"2026-06-10T13:39:02.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/hinanohart/tracelin","commit_stats":null,"previous_names":["hinanohart/tracelin"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/hinanohart/tracelin","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hinanohart%2Ftracelin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hinanohart%2Ftracelin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hinanohart%2Ftracelin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hinanohart%2Ftracelin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hinanohart","download_url":"https://codeload.github.com/hinanohart/tracelin/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hinanohart%2Ftracelin/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34353189,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-15T02:00:07.085Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["happens-before","linearizability","linter","llm-agents","multi-agent","observability","opentelemetry","race-condition","trace-analysis"],"created_at":"2026-06-15T07:30:56.414Z","updated_at":"2026-06-15T07:30:57.241Z","avatar_url":"https://github.com/hinanohart.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# tracelin\n\n\u003e Conformance \u0026 race linter for **recorded** multi-agent agent traces\n\u003e — linearizability + happens-before + MAST failure-mode categories under the hood.\n\n`tracelin` is an **offline falsifier**. You give it a recorded multi-agent\nexecution trace (OpenTelemetry GenAI spans, a LangGraph trace, A2A task events,\nJSONL …), declare a spec, and it tells you whether the trace *can possibly be\ncorrect*. When it cannot, it hands you a **1-minimal counterexample sub-history**\nyou can replay by hand, tagged with the closest [MAST](https://arxiv.org/abs/2503.13657)\nfailure-mode category.\n\nIt reads history; it does **not** change it. It is not a runtime guard — it is\nthe thing you run over the traces you already collect, in CI or after an\nincident, to find the lost updates and lifecycle violations that silent\n\"it mostly works\" multi-agent systems hide.\n\n\u003e **Status: v0.1.0a4 (pre-alpha).** Read the CLAIM / NON-CLAIM contract below\n\u003e before relying on any output. APIs may change.\n\n## Install\n\nNot on PyPI yet (pre-alpha). Install from source:\n\n```bash\npip install \"git+https://github.com/hinanohart/tracelin\"          # core (pure stdlib, zero deps)\npip install \"tracelin[otel] @ git+https://github.com/hinanohart/tracelin\"   # + OTel helpers\n```\n\n## Quickstart\n\n```python\nfrom tracelin import History, check\n\n# A counter that two agents incremented concurrently; the committed value is 1.\nrows = [\n    {\"agent_id\": \"sys\", \"op_type\": \"WRITE\", \"object_key\": \"n\", \"value\": 0,\n     \"span_id\": \"i\", \"object_type\": \"counter\"},\n    {\"agent_id\": \"a\", \"op_type\": \"INC\", \"object_key\": \"n\",\n     \"span_id\": \"a0\", \"parent_span_id\": \"i\", \"object_type\": \"counter\"},\n    {\"agent_id\": \"b\", \"op_type\": \"INC\", \"object_key\": \"n\",\n     \"span_id\": \"b0\", \"parent_span_id\": \"i\", \"object_type\": \"counter\"},\n    {\"agent_id\": \"final\", \"op_type\": \"READ\", \"object_key\": \"n\", \"value\": 1,\n     \"span_id\": \"f\", \"parent_span_id\": \"a0\", \"links\": [\"a0\", \"b0\"],\n     \"object_type\": \"counter\"},\n]\nresult = check(History.from_records(rows), \"linearizable\")\nprint(result)\n# Witness (linearizable): [not_linearizable on 'n'] object 'n' (counter) has no\n# happens-before-respecting linearisation consistent with its sequential spec\n# (lost update / stale read) (events: a0, b0, f; MAST FC2)\n```\n\nCommand line:\n\n```bash\ntracelin check trace.jsonl --spec a2a_lifecycle --ci          # native event JSONL\ntracelin check raw.jsonl   --adapter langgraph --spec linearizable\ntracelin classify trace.jsonl                                  # all specs at once\n```\n\n`--ci` makes the exit code fail-closed: non-zero on any `Witness`, and (because\nthey are not a confirmed pass) also on `INSUFFICIENT_HB` / `UNKNOWN`.\n\nA runnable, deterministic reproduction on a **real** LangGraph run lives in\n[`examples/repro.sh`](examples/repro.sh) (the trace was recorded by\n[`examples/langgraph_race_harness.py`](examples/langgraph_race_harness.py),\nwhich reproduces LangGraph's `InvalidUpdateError` and a last-writer-wins lost\nupdate).\n\n## The four verdicts\n\n| Verdict | Meaning |\n|---|---|\n| `PASS` | No violation, and the happens-before order was trustworthy enough to say so. |\n| `Witness` | A real, reproducible violation, with a 1-minimal counter-example sub-history. |\n| `INSUFFICIENT_HB` | A *refusal*: the trace lacked the cross-agent causal edges needed to soundly claim PASS. Not a pass, not a fail. |\n| `UNKNOWN` | A resource cap (ops / concurrency width / time) was hit. Never a guess. |\n\nThe point of four values instead of a boolean is that **tracelin never emits a\nfalse PASS**. If it cannot trust the order, it says `INSUFFICIENT_HB`; if a\nsearch blows its budget, it says `UNKNOWN`. A *linearizability* `Witness` is sound\nat any trust tier — fewer known edges only add candidate linearisations, so a\nviolation found under a weak order persists under any stronger one. The\n*concurrency* checks (race / double-assignee / act-after-terminal) work the other\nway: fewer edges can fabricate apparent concurrency, so tracelin withholds them on\nan untrusted order (reporting `INSUFFICIENT_HB`) and emits those witnesses only at\nthe `causal` tier. Either way, a `Witness` that *is* emitted is sound.\n\n## How it works\n\n1. **Adapters** turn a recorded trace into a `History` of `Event`s. The core\n   imports no agent framework.\n2. **Happens-before** (`hb.py`) builds a vector-clock partial order from explicit\n   `parent_span_id` / `links` edges plus per-agent program order, and labels its\n   **trust tier** (`causal` if real cross-agent edges exist, else\n   `program_order`). Timestamps are never turned into ordering edges.\n3. **Specs** decide conformance:\n   - `a2a_lifecycle` — always-polynomial structural rules: A2A task-state-machine\n     legality, no action after a terminal state, single-assignee per subtask, and\n     concurrent write-write races to a single-writer object with no declared\n     merge (the *structural race condition* of S-Bus).\n   - `linearizable` — a memoised Wing–Gong/Lowe search per object\n     (`register` / `counter` / `map`), with hard caps that yield `UNKNOWN`\n     rather than guessing. Validated against a brute-force oracle by a\n     differential property test.\n4. **Witness** minimisation (`witness.py`) shrinks a failing history to a\n   1-minimal sub-history; **mast.py** annotates the closest failure-mode category.\n\n## Architecture\n\n\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"docs/architecture.png\" alt=\"tracelin architecture\" width=\"840\"\u003e\n\u003c/div\u003e\n\n## CLAIM / NON-CLAIM\n\n**CLAIM.** tracelin is a *sound falsifier* over recorded traces:\n- Every reported `Witness` corresponds to a real violation and is replayable by\n  hand from the sub-history.\n- Within its resource bounds the search is complete; reported witnesses are\n  1-minimal (removing any single event makes the violation disappear).\n- It never reports `PASS` from an untrusted (program-order-only / timestamp)\n  order, and never fabricates ordering from wall-clock timestamps.\n\n**NON-CLAIM.** tracelin is *not*:\n- a runtime prevention layer (that is [S-Bus](https://arxiv.org/abs/2605.17076)'s\n  job; tracelin is the offline, post-hoc complement);\n- able to see concurrency the trace did not record (untraced races are invisible\n  — so a `PASS` is \"no violation *in this trace*\", not \"the system is correct\");\n- an exhaustive MAST detector — witnesses are mapped to a MAST *category* via a\n  small manual table, and anything without a defensible mapping is reported\n  `UNMAPPED`;\n- a checker of `causal` / `sequential` consistency in v0.1 (full causal-\n  consistency checking is NP-complete in general,\n  [Bouajjani et al., POPL 2017](https://arxiv.org/abs/1611.00580); these tiers\n  are deliberately deferred rather than claimed).\n\n## Prior art \u0026 credits\n\ntracelin re-targets four decades of distributed-systems verification\n(Herlihy–Wing linearizability, Lamport happens-before, Knossos/Jepsen/Porcupine/\nElle, Wing–Gong/Lowe) at agent traces, and builds on MAST, the A2A lifecycle,\nOpenTelemetry GenAI conventions, S-Bus, OmniLink, and ddmin. Full attributions\nare in [`NOTICE`](NOTICE). No code is copied from those projects.\n\n## License\n\nMIT. See [`LICENSE`](LICENSE) and [`NOTICE`](NOTICE).\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhinanohart%2Ftracelin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhinanohart%2Ftracelin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhinanohart%2Ftracelin/lists"}