pbuffer 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. pbuffer-0.1.0/LICENSE +21 -0
  2. pbuffer-0.1.0/PKG-INFO +77 -0
  3. pbuffer-0.1.0/README.md +63 -0
  4. pbuffer-0.1.0/pyproject.toml +26 -0
  5. pbuffer-0.1.0/setup.cfg +4 -0
  6. pbuffer-0.1.0/src/patternbuffer/__init__.py +317 -0
  7. pbuffer-0.1.0/src/patternbuffer/buffer.py +312 -0
  8. pbuffer-0.1.0/src/patternbuffer/classify.py +359 -0
  9. pbuffer-0.1.0/src/patternbuffer/codec.py +122 -0
  10. pbuffer-0.1.0/src/patternbuffer/dump.py +113 -0
  11. pbuffer-0.1.0/src/patternbuffer/identity.py +1271 -0
  12. pbuffer-0.1.0/src/patternbuffer/indexes.py +1642 -0
  13. pbuffer-0.1.0/src/patternbuffer/ingest.py +617 -0
  14. pbuffer-0.1.0/src/patternbuffer/mcp.py +303 -0
  15. pbuffer-0.1.0/src/patternbuffer/model.py +100 -0
  16. pbuffer-0.1.0/src/patternbuffer/porcelain.py +943 -0
  17. pbuffer-0.1.0/src/patternbuffer/project.py +436 -0
  18. pbuffer-0.1.0/src/patternbuffer/refer.py +256 -0
  19. pbuffer-0.1.0/src/patternbuffer/roles.py +77 -0
  20. pbuffer-0.1.0/src/patternbuffer/salience.py +170 -0
  21. pbuffer-0.1.0/src/patternbuffer/semantics.py +192 -0
  22. pbuffer-0.1.0/src/patternbuffer/testing.py +92 -0
  23. pbuffer-0.1.0/src/patternbuffer/thunks.py +245 -0
  24. pbuffer-0.1.0/src/patternbuffer/tmaint.py +122 -0
  25. pbuffer-0.1.0/src/pbuffer.egg-info/PKG-INFO +77 -0
  26. pbuffer-0.1.0/src/pbuffer.egg-info/SOURCES.txt +63 -0
  27. pbuffer-0.1.0/src/pbuffer.egg-info/dependency_links.txt +1 -0
  28. pbuffer-0.1.0/src/pbuffer.egg-info/entry_points.txt +2 -0
  29. pbuffer-0.1.0/src/pbuffer.egg-info/requires.txt +6 -0
  30. pbuffer-0.1.0/src/pbuffer.egg-info/top_level.txt +1 -0
  31. pbuffer-0.1.0/tests/test_aka_correlation.py +245 -0
  32. pbuffer-0.1.0/tests/test_awareness_projection.py +132 -0
  33. pbuffer-0.1.0/tests/test_axis_heads.py +65 -0
  34. pbuffer-0.1.0/tests/test_bounded_reads.py +129 -0
  35. pbuffer-0.1.0/tests/test_buffer.py +151 -0
  36. pbuffer-0.1.0/tests/test_build_session.py +113 -0
  37. pbuffer-0.1.0/tests/test_classifier_event_safety.py +110 -0
  38. pbuffer-0.1.0/tests/test_confidence.py +169 -0
  39. pbuffer-0.1.0/tests/test_confidence_multiframe.py +178 -0
  40. pbuffer-0.1.0/tests/test_dump.py +72 -0
  41. pbuffer-0.1.0/tests/test_exact_decimal.py +254 -0
  42. pbuffer-0.1.0/tests/test_fidelity_audit.py +240 -0
  43. pbuffer-0.1.0/tests/test_fold.py +646 -0
  44. pbuffer-0.1.0/tests/test_fold_resolution.py +42 -0
  45. pbuffer-0.1.0/tests/test_grammar.py +316 -0
  46. pbuffer-0.1.0/tests/test_identity_containment_veto.py +69 -0
  47. pbuffer-0.1.0/tests/test_identity_recall.py +214 -0
  48. pbuffer-0.1.0/tests/test_identity_thunks.py +229 -0
  49. pbuffer-0.1.0/tests/test_individuation_v2.py +184 -0
  50. pbuffer-0.1.0/tests/test_ingest_frame_default.py +131 -0
  51. pbuffer-0.1.0/tests/test_ingest_hardening.py +282 -0
  52. pbuffer-0.1.0/tests/test_ingest_v2.py +556 -0
  53. pbuffer-0.1.0/tests/test_mcp_wrapper.py +419 -0
  54. pbuffer-0.1.0/tests/test_merge_verb.py +192 -0
  55. pbuffer-0.1.0/tests/test_path_temporal.py +85 -0
  56. pbuffer-0.1.0/tests/test_place_feature.py +150 -0
  57. pbuffer-0.1.0/tests/test_porcelain.py +475 -0
  58. pbuffer-0.1.0/tests/test_route.py +191 -0
  59. pbuffer-0.1.0/tests/test_scaffold.py +24 -0
  60. pbuffer-0.1.0/tests/test_shape_fix.py +371 -0
  61. pbuffer-0.1.0/tests/test_situation_lens.py +208 -0
  62. pbuffer-0.1.0/tests/test_triage_context.py +180 -0
  63. pbuffer-0.1.0/tests/test_who_knows.py +110 -0
  64. pbuffer-0.1.0/tests/test_world.py +634 -0
  65. pbuffer-0.1.0/tests/test_world_retrieval.py +382 -0
pbuffer-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 5000Stadia
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
pbuffer-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,77 @@
1
+ Metadata-Version: 2.4
2
+ Name: pbuffer
3
+ Version: 0.1.0
4
+ Summary: A world-state database: an append-only log of everything true in a world - places, people, objects, knowledge, time. Deploy it under a game, AI agent, or real-world tracker for permanent, queryable world memory: current state, location, history, and who-knows-what, answered by deterministic queries instead of model calls.
5
+ License: MIT
6
+ Requires-Python: >=3.11
7
+ Description-Content-Type: text/markdown
8
+ License-File: LICENSE
9
+ Provides-Extra: dev
10
+ Requires-Dist: pytest>=8; extra == "dev"
11
+ Provides-Extra: mcp
12
+ Requires-Dist: mcp<2,>=1.28; extra == "mcp"
13
+ Dynamic: license-file
14
+
15
+ # pattern-buffer
16
+
17
+ [![tests](https://github.com/5000Stadia/pattern-buffer/actions/workflows/ci.yml/badge.svg)](https://github.com/5000Stadia/pattern-buffer/actions/workflows/ci.yml)
18
+
19
+ > A data structure that maps whatever world it's shown — real or fiction.
20
+ > The memory under the holodeck.
21
+
22
+ **A pattern buffer that never degrades.**
23
+
24
+ One append-only log of perspective-scoped, time-indexed assertions about entities; every other structure — current state, space, knowledge, history, the rendered world — is a disposable projection over it. Fiction simulation and real-world tracking are the same machine under one policy switch.
25
+
26
+ **A world-encapsulation framework, engine-independent by construction.** The substrate's only outside dependency is an injected model callable `(prompt, schema) -> json` — no host concept (players, turns, scenes, sessions) ever reaches inside it — so any engine sits on top: a game runtime, a household or job-site tracker, an agent's long-term memory. Fiction and tracking aren't two systems; they are two *instances* of one substrate, differing only in resolution policy and provenance discipline.
27
+
28
+ > A player places a pipe in a drawer in session 12 of a D&D campaign. Two hundred sessions pass without a mention. At retirement, the player opens the drawer — and the pipe is there, exactly as placed, with the original moment's full history behind it. No maintenance was ever performed. In this architecture, **silence is persistence**: state is folded from the log, never re-inferred and never kept alive by mention. The world remembers so the model doesn't have to.
29
+
30
+ **Status: pre-1.0; substrate validated, ingestion fidelity is the open
31
+ front.** The engine passes its 419-test invariant suite, and the chapter test
32
+ (ingest a complete noir mystery, delete the prose, interrogate the store
33
+ against a hand-authored answer key) has been graded across three full runs.
34
+ The substrate's invariants held in all of them — sealed containers stayed
35
+ sealed, contradictions were flagged and never merged, knowledge frames never
36
+ leaked, the two-time-axis fold answered late-revealed history correctly. Each
37
+ run also surfaced exactly one engine refinement at the fold (a working
38
+ assumption outholding evidence; a wrong inference outholding authored canon),
39
+ each fixed, tested, and generalized the same day — that progression is now
40
+ the **evidence-rank** rule. A registry-first ingestion pipeline
41
+ (whole-document scaffold → parallel compact-grammar extraction → audited
42
+ single commit) replaced serial prompt-iterated extraction after measurement
43
+ showed prose contracts fragment differently every round; identity and key
44
+ fragmentation died as failure classes, and pass-0 registry quality is where
45
+ the remaining extraction failures concentrate. Run 4, with its checkable
46
+ predictions, is in flight. Not yet installable from PyPI; the shipped example
47
+ world with a zero-API-key query tour lands once a verified run stamps it.
48
+
49
+ ## What this is
50
+
51
+ A storage and retrieval substrate that lets a language model maintain a complete, queryable, durable model of a world — places, objects, people, relationships, histories, and unknowns — such that any state question is answered by a deterministic query, never by re-reading prose; the world accretes and is re-enterable; and the same engine serves authored fiction (a holodeck, a campaign, a mystery) and real-world tracking (a household, a job site, a vehicle), differing only in resolution policy and provenance discipline.
52
+
53
+ The buffer holds the pattern; materialization is re-entry; degradation is the failure we exist to prevent; the arch is how the operator steps outside the world to inspect it; and where nothing has been established, you don't get invented detail — **you see the grid.**
54
+
55
+ ## Documents
56
+
57
+ - **[docs/WHITEPAPER.md](docs/WHITEPAPER.md)** — the canonical, comprehensive design reference. Principles, primitives, durability, frames, provenance, thunks, reference resolution, ingestion discipline, identity, the operation algebra, projection, both modes, embedding hazards, evaluation, and the decision record. Start here.
58
+ - **[docs/LEXICON.md](docs/LEXICON.md)** — the working vocabulary and its naming discipline.
59
+ - **[docs/ADOPTION.md](docs/ADOPTION.md)** — for agents (and humans) integrating the library: exact signatures, typed outcomes, the three-seam recipe, MUST/NEVER rules.
60
+ - **[docs/HOST-DISCIPLINE.md](docs/HOST-DISCIPLINE.md)** — the adopter's discipline brief: the seven categories every fact must be classified along to ingest with fidelity, and the structural retrieval strategies (the four correlation axes + the correlation sweep) to surface every relevant, related detail for any subject.
61
+ - **[docs/INGESTION-PLAYBOOK.md](docs/INGESTION-PLAYBOOK.md)** — evidence-based rules for deserializing narrative into a world: the extractor contract, feeding mechanics, what the gate guards, and measured ingestion baselines.
62
+ - **[docs/reference/assertion-world-model-original.md](docs/reference/assertion-world-model-original.md)** — the framework-agnostic design document this project was founded on, preserved verbatim for lineage.
63
+
64
+ ## The road
65
+
66
+ 1. ~~Recover and commit the *Last Honest Meter* eval seed (the chapter test's ground truth).~~ Done — v1-final, `evals/last_honest_meter/`.
67
+ 2. ~~Engine spike: `PatternBuffer` + derived indexes + classifier + projector + resolver + `refer()` tier 1.~~ Done — `src/patternbuffer/`.
68
+ 3. ~~The chapter test, runs 1–4~~: substrate validated; registry-first ingestion (INGEST-V2) landed; run 4 scored **22/33 (best graded run, all remaining failures extraction-class not shape-class)** — the substrate holds; ingestion fidelity (pass-0 registry quality) is the open score lever. Receipts: `evals/results/`.
69
+ 4. ~~The shipped example world: `examples/anchor/`~~ — stamped (`STAMP.json`, run 4): a bible-verified noir mystery as a queryable database, with a zero-API-key scripted tour and 8 open truth-maintenance conflict flags shipped visibly (honesty is the product). Not yet packaged to PyPI.
70
+ 5. ~~The messy-dialogue micro-eval (the honest bridge from fiction ingestion to tracking-mode ingestion).~~ Done — **10/10** on the reality-divergence battery (`evals/results/2026-06-12-micro-v1-final/`); tracking-mode ingestion validated against sloppy conversational fragments.
71
+ 6. ~~The porcelain API + host integration.~~ Done — and proven in the strongest form. The frozen contract (`porcelain-v0.1`, additive-only): five load-bearing verbs — `ingest` (+`ingest_structured`) / `snapshot` / `ask` / `materialize` / `resolve`+`refer()` — over a fuller read-and-identity family (`state`, `where`, `aggregate`, `neighborhood`, `frame_diff`, `who_knows`, `correlate`, `route`, `entities`, `facts`, build sessions, `axis_heads`, …). The first *live* host, [Construct](https://github.com/5000Stadia/construct) (an interactive-fiction engine — `pattern` → `construct` loads it → `holonovel`), runs **entirely on this contract**: zero engine-internal reaches, its 800+-test suite green on the pure public surface. The engine-independent claim, demonstrated rather than asserted. (Intended primary host: [Kernos](https://github.com/5000Stadia/Kernos), adapter pattern.)
72
+ 7. ~~The interactive-fiction milestone (thunk stability across sessions, frame-scoped NPCs, multi-path mystery, clocks, loop closure).~~ Met in the field — every item is exercised in [Construct](https://github.com/5000Stadia/construct)'s live suite (the first host runs entirely on the frozen porcelain). These are host-layer criteria, proven by the adopter, not a further engine deliverable.
73
+ 8. ~~The MCP wrapper~~ — built: `pip install pbuffer[mcp]` → `patternbuffer-mcp --world w.world --world-id w:id` serves the frozen porcelain's **37 deterministic verbs** over stdio to any MCP client — read, query, and *build* a world with zero Python (the genuinely model-free subset; model-backed verbs are V1.1 via sampling). One server ↔ one world; a connected client is a fully **trusted world principal** — frame entitlement for untrusted consumers stays host-mediated. The framework-agnostic claim, proven by demonstration. Far field: the first host's V2 roadmap publicly assigns branch-worlds simulation and its curiosity loop to this engine — the two roadmaps meet in the middle.
74
+
75
+ ## License
76
+
77
+ MIT
@@ -0,0 +1,63 @@
1
+ # pattern-buffer
2
+
3
+ [![tests](https://github.com/5000Stadia/pattern-buffer/actions/workflows/ci.yml/badge.svg)](https://github.com/5000Stadia/pattern-buffer/actions/workflows/ci.yml)
4
+
5
+ > A data structure that maps whatever world it's shown — real or fiction.
6
+ > The memory under the holodeck.
7
+
8
+ **A pattern buffer that never degrades.**
9
+
10
+ One append-only log of perspective-scoped, time-indexed assertions about entities; every other structure — current state, space, knowledge, history, the rendered world — is a disposable projection over it. Fiction simulation and real-world tracking are the same machine under one policy switch.
11
+
12
+ **A world-encapsulation framework, engine-independent by construction.** The substrate's only outside dependency is an injected model callable `(prompt, schema) -> json` — no host concept (players, turns, scenes, sessions) ever reaches inside it — so any engine sits on top: a game runtime, a household or job-site tracker, an agent's long-term memory. Fiction and tracking aren't two systems; they are two *instances* of one substrate, differing only in resolution policy and provenance discipline.
13
+
14
+ > A player places a pipe in a drawer in session 12 of a D&D campaign. Two hundred sessions pass without a mention. At retirement, the player opens the drawer — and the pipe is there, exactly as placed, with the original moment's full history behind it. No maintenance was ever performed. In this architecture, **silence is persistence**: state is folded from the log, never re-inferred and never kept alive by mention. The world remembers so the model doesn't have to.
15
+
16
+ **Status: pre-1.0; substrate validated, ingestion fidelity is the open
17
+ front.** The engine passes its 419-test invariant suite, and the chapter test
18
+ (ingest a complete noir mystery, delete the prose, interrogate the store
19
+ against a hand-authored answer key) has been graded across three full runs.
20
+ The substrate's invariants held in all of them — sealed containers stayed
21
+ sealed, contradictions were flagged and never merged, knowledge frames never
22
+ leaked, the two-time-axis fold answered late-revealed history correctly. Each
23
+ run also surfaced exactly one engine refinement at the fold (a working
24
+ assumption outholding evidence; a wrong inference outholding authored canon),
25
+ each fixed, tested, and generalized the same day — that progression is now
26
+ the **evidence-rank** rule. A registry-first ingestion pipeline
27
+ (whole-document scaffold → parallel compact-grammar extraction → audited
28
+ single commit) replaced serial prompt-iterated extraction after measurement
29
+ showed prose contracts fragment differently every round; identity and key
30
+ fragmentation died as failure classes, and pass-0 registry quality is where
31
+ the remaining extraction failures concentrate. Run 4, with its checkable
32
+ predictions, is in flight. Not yet installable from PyPI; the shipped example
33
+ world with a zero-API-key query tour lands once a verified run stamps it.
34
+
35
+ ## What this is
36
+
37
+ A storage and retrieval substrate that lets a language model maintain a complete, queryable, durable model of a world — places, objects, people, relationships, histories, and unknowns — such that any state question is answered by a deterministic query, never by re-reading prose; the world accretes and is re-enterable; and the same engine serves authored fiction (a holodeck, a campaign, a mystery) and real-world tracking (a household, a job site, a vehicle), differing only in resolution policy and provenance discipline.
38
+
39
+ The buffer holds the pattern; materialization is re-entry; degradation is the failure we exist to prevent; the arch is how the operator steps outside the world to inspect it; and where nothing has been established, you don't get invented detail — **you see the grid.**
40
+
41
+ ## Documents
42
+
43
+ - **[docs/WHITEPAPER.md](docs/WHITEPAPER.md)** — the canonical, comprehensive design reference. Principles, primitives, durability, frames, provenance, thunks, reference resolution, ingestion discipline, identity, the operation algebra, projection, both modes, embedding hazards, evaluation, and the decision record. Start here.
44
+ - **[docs/LEXICON.md](docs/LEXICON.md)** — the working vocabulary and its naming discipline.
45
+ - **[docs/ADOPTION.md](docs/ADOPTION.md)** — for agents (and humans) integrating the library: exact signatures, typed outcomes, the three-seam recipe, MUST/NEVER rules.
46
+ - **[docs/HOST-DISCIPLINE.md](docs/HOST-DISCIPLINE.md)** — the adopter's discipline brief: the seven categories every fact must be classified along to ingest with fidelity, and the structural retrieval strategies (the four correlation axes + the correlation sweep) to surface every relevant, related detail for any subject.
47
+ - **[docs/INGESTION-PLAYBOOK.md](docs/INGESTION-PLAYBOOK.md)** — evidence-based rules for deserializing narrative into a world: the extractor contract, feeding mechanics, what the gate guards, and measured ingestion baselines.
48
+ - **[docs/reference/assertion-world-model-original.md](docs/reference/assertion-world-model-original.md)** — the framework-agnostic design document this project was founded on, preserved verbatim for lineage.
49
+
50
+ ## The road
51
+
52
+ 1. ~~Recover and commit the *Last Honest Meter* eval seed (the chapter test's ground truth).~~ Done — v1-final, `evals/last_honest_meter/`.
53
+ 2. ~~Engine spike: `PatternBuffer` + derived indexes + classifier + projector + resolver + `refer()` tier 1.~~ Done — `src/patternbuffer/`.
54
+ 3. ~~The chapter test, runs 1–4~~: substrate validated; registry-first ingestion (INGEST-V2) landed; run 4 scored **22/33 (best graded run, all remaining failures extraction-class not shape-class)** — the substrate holds; ingestion fidelity (pass-0 registry quality) is the open score lever. Receipts: `evals/results/`.
55
+ 4. ~~The shipped example world: `examples/anchor/`~~ — stamped (`STAMP.json`, run 4): a bible-verified noir mystery as a queryable database, with a zero-API-key scripted tour and 8 open truth-maintenance conflict flags shipped visibly (honesty is the product). Not yet packaged to PyPI.
56
+ 5. ~~The messy-dialogue micro-eval (the honest bridge from fiction ingestion to tracking-mode ingestion).~~ Done — **10/10** on the reality-divergence battery (`evals/results/2026-06-12-micro-v1-final/`); tracking-mode ingestion validated against sloppy conversational fragments.
57
+ 6. ~~The porcelain API + host integration.~~ Done — and proven in the strongest form. The frozen contract (`porcelain-v0.1`, additive-only): five load-bearing verbs — `ingest` (+`ingest_structured`) / `snapshot` / `ask` / `materialize` / `resolve`+`refer()` — over a fuller read-and-identity family (`state`, `where`, `aggregate`, `neighborhood`, `frame_diff`, `who_knows`, `correlate`, `route`, `entities`, `facts`, build sessions, `axis_heads`, …). The first *live* host, [Construct](https://github.com/5000Stadia/construct) (an interactive-fiction engine — `pattern` → `construct` loads it → `holonovel`), runs **entirely on this contract**: zero engine-internal reaches, its 800+-test suite green on the pure public surface. The engine-independent claim, demonstrated rather than asserted. (Intended primary host: [Kernos](https://github.com/5000Stadia/Kernos), adapter pattern.)
58
+ 7. ~~The interactive-fiction milestone (thunk stability across sessions, frame-scoped NPCs, multi-path mystery, clocks, loop closure).~~ Met in the field — every item is exercised in [Construct](https://github.com/5000Stadia/construct)'s live suite (the first host runs entirely on the frozen porcelain). These are host-layer criteria, proven by the adopter, not a further engine deliverable.
59
+ 8. ~~The MCP wrapper~~ — built: `pip install pbuffer[mcp]` → `patternbuffer-mcp --world w.world --world-id w:id` serves the frozen porcelain's **37 deterministic verbs** over stdio to any MCP client — read, query, and *build* a world with zero Python (the genuinely model-free subset; model-backed verbs are V1.1 via sampling). One server ↔ one world; a connected client is a fully **trusted world principal** — frame entitlement for untrusted consumers stays host-mediated. The framework-agnostic claim, proven by demonstration. Far field: the first host's V2 roadmap publicly assigns branch-worlds simulation and its curiosity loop to this engine — the two roadmaps meet in the middle.
60
+
61
+ ## License
62
+
63
+ MIT
@@ -0,0 +1,26 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "pbuffer"
7
+ version = "0.1.0"
8
+ description = "A world-state database: an append-only log of everything true in a world - places, people, objects, knowledge, time. Deploy it under a game, AI agent, or real-world tracker for permanent, queryable world memory: current state, location, history, and who-knows-what, answered by deterministic queries instead of model calls."
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = { text = "MIT" }
12
+ dependencies = []
13
+
14
+ [project.optional-dependencies]
15
+ dev = ["pytest>=8"]
16
+ mcp = ["mcp>=1.28,<2"]
17
+
18
+ [project.scripts]
19
+ patternbuffer-mcp = "patternbuffer.mcp:main"
20
+
21
+ [tool.setuptools.packages.find]
22
+ where = ["src"]
23
+
24
+ [tool.pytest.ini_options]
25
+ testpaths = ["tests"]
26
+ addopts = "-q"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,317 @@
1
+ """patternbuffer: an append-only world-state substrate.
2
+
3
+ One append-only log of perspective-scoped, time-indexed assertions per
4
+ world; every other structure — current state, space, knowledge, history,
5
+ the rendered world — is a disposable projection over it.
6
+
7
+ The engine is host-blind: its single outside dependency is an injected
8
+ model callable ``(prompt, schema) -> json``. See docs/WHITEPAPER.md.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import time
14
+ from pathlib import Path
15
+ from typing import Any, Callable
16
+
17
+ from patternbuffer.buffer import PatternBuffer
18
+ from patternbuffer.classify import Classifier
19
+ from patternbuffer.identity import IdentityRegistry
20
+ from patternbuffer.indexes import Indexes
21
+ from patternbuffer.ingest import Ingestor
22
+ from patternbuffer.model import ATTR_PREFIX, CANON, META_ATTRIBUTES
23
+ from patternbuffer.project import Materialization, Projector
24
+ from patternbuffer.refer import Refer, Resolution
25
+ from patternbuffer.roles import _make_engine_roles
26
+ from patternbuffer.salience import SalienceIndex
27
+ from patternbuffer.semantics import AttributeSemantics
28
+ from patternbuffer.thunks import (
29
+ INVENT_UNDER_CANON,
30
+ OBSERVE_OR_UNKNOWN,
31
+ POLICIES,
32
+ Resolver,
33
+ )
34
+ from patternbuffer.tmaint import TruthMaintenance
35
+
36
+ __version__ = "0.0.1"
37
+
38
+ STANCES = frozenset({"fiction", "reality", "hypothetical"}) # letter 026; fixed enum
39
+ __all__ = ["World", "Materialization", "Resolution", "__version__"]
40
+
41
+
42
+ class World:
43
+ """The named, individual unit: one PatternBuffer + its physics
44
+ (resolution policy, decay configuration) + derived indexes (§16).
45
+
46
+ ``World("campaign.world", world_id="w:campaign", model=callable)`` is
47
+ the entire harness interface — a parameter, not a framework. Role
48
+ capabilities are minted here and nowhere else.
49
+ """
50
+
51
+ def __init__(
52
+ self,
53
+ path: str | Path,
54
+ world_id: str,
55
+ model: Callable[[str, dict], Any] | None = None,
56
+ policy: str = INVENT_UNDER_CANON,
57
+ clock: Callable[[], float] = time.time,
58
+ stance: str = "fiction",
59
+ title: str = "",
60
+ description: str = "",
61
+ attribute_default: Callable[[str], dict | None] | None = None,
62
+ ) -> None:
63
+ if policy not in POLICIES:
64
+ raise ValueError(f"unknown policy {policy!r}")
65
+ if stance not in STANCES:
66
+ raise ValueError(f"unknown stance {stance!r} (fixed enum; letter 026)")
67
+ self.world_id = world_id
68
+ self.policy = policy
69
+ self.buffer = PatternBuffer(path, world_id)
70
+ self.semantics = AttributeSemantics(self.buffer)
71
+ roles = _make_engine_roles()
72
+ self.classifier = Classifier(self.buffer, model or _no_model, self.semantics)
73
+ self.registry = IdentityRegistry(self.buffer, roles["ingestor"], self.semantics)
74
+ self.indexes = Indexes(
75
+ self.buffer, self.classifier, self.registry.resolve, self.semantics
76
+ )
77
+ self.indexes.set_closure_provider(self.registry.closure)
78
+ self.indexes.set_correlation_provider(self.registry.correlation_set)
79
+ self.registry.set_kind_provider(lambda e: self.indexes.fold_key(e, "kind"))
80
+ self.salience_index = SalienceIndex(self.buffer, self.classifier, self.indexes)
81
+ self.indexes.set_salience_provider(self.salience_index.salience)
82
+ self.truth = TruthMaintenance(
83
+ self.buffer, self.classifier, self.indexes, roles["truth_maintenance"],
84
+ self.semantics,
85
+ )
86
+ # SHAPE-FIX-V1 retype wiring: retraction authority + rules-mode
87
+ # durability for the corrected kind row (the set_kind_provider idiom —
88
+ # truth/classifier post-date the registry).
89
+ self.registry.set_retract_provider(self.truth.retract)
90
+ self.registry.set_classify_provider(
91
+ lambda rows: self.classifier.classify_rows(rows, model=False)
92
+ )
93
+ # Win 4 (durable-contradiction veto, HD 089): the registry reads
94
+ # standing folds + durability verdicts through late-bound lookups.
95
+ self.registry.set_fold_provider(self.indexes.fold_key)
96
+ self.registry.set_durability_provider(self.classifier.durability)
97
+ self.resolver = Resolver(
98
+ self.buffer, self.classifier, self.indexes, roles["resolver"],
99
+ model or _no_model, policy,
100
+ )
101
+ self.projector = Projector(
102
+ self.buffer, self.classifier, self.indexes, self.semantics,
103
+ self.salience_index.salience,
104
+ )
105
+ self.ingestor = Ingestor(
106
+ self.buffer, self.classifier, self.registry, roles["ingestor"],
107
+ model, observe_mode=(policy == OBSERVE_OR_UNKNOWN), clock=clock,
108
+ resolver_role=roles["resolver"],
109
+ semantics=self.semantics,
110
+ attribute_default=attribute_default,
111
+ # Write-time containment-cycle gate (HD 002): the ancestor walk
112
+ # is the derived containment chain — locate folds the family.
113
+ containment_ancestors=lambda parent, frame, vf: set(
114
+ self.indexes.locate(parent, frame=frame, valid_as_of=vf)
115
+ ),
116
+ )
117
+ self.refer = Refer(self.buffer, self.indexes, self.registry, model,
118
+ ingestor=self.ingestor)
119
+ # The World Charter (letter 026): a fresh world's genesis write
120
+ # asserts its self-description — stance is ontological stored
121
+ # truth (does this world claim to describe reality?), distinct
122
+ # from operational policy. Ordinary appends; amendable forever.
123
+ if self.buffer.head() == 0:
124
+ charter = [
125
+ {"entity": "world:self", "attribute": "kind", "value": "world",
126
+ "timeless": True},
127
+ {"entity": "world:self", "attribute": "stance", "value": stance,
128
+ "timeless": True},
129
+ ]
130
+ if title:
131
+ charter.append({"entity": "world:self", "attribute": "title",
132
+ "value": title, "timeless": True})
133
+ if description:
134
+ charter.append({"entity": "world:self", "attribute": "description",
135
+ "value": description, "timeless": True})
136
+ self.ingestor.classify_inline = False
137
+ self.ingest_structured(charter)
138
+ self.ingestor.classify_inline = True
139
+ for row in self.buffer.all_rows():
140
+ self.classifier.set(row.id, "CONSTITUTIVE", 1.0)
141
+
142
+ # Reads (deterministic, no LLM — P7).
143
+
144
+ def locate(self, entity: str, **kw) -> list[str]:
145
+ return self.indexes.locate(entity, **kw)
146
+
147
+ def contents(self, container: str, **kw) -> list[str]:
148
+ return self.indexes.contents(container, **kw)
149
+
150
+ # PLACE-FEATURE-ABSTRACTION-V1 — the compositional axis (part_of).
151
+ def composition(self, entity: str, **kw) -> list[str]:
152
+ return self.indexes.composition(entity, **kw)
153
+
154
+ def features(self, place: str, **kw) -> list[str]:
155
+ return self.indexes.features(place, **kw)
156
+
157
+ # WHO-KNOWS-INVERSE-V1 — the computed "who knows X" read.
158
+ def who_knows(self, entity: str, attribute: str, value: Any = None, **kw) -> list[str]:
159
+ return self.indexes.who_knows(entity, attribute, value, **kw)
160
+
161
+ def state(self, entity: str, attribute: str, frame: str = CANON, **kw):
162
+ return self.indexes.fold_key(entity, attribute, frame, **kw)
163
+
164
+ # AKA-CORRELATION-V1 — the explicit, opt-in correlated identity surface.
165
+ def state_union(self, entity: str, attribute: str, frame: str = CANON, **kw):
166
+ return self.indexes.state_union(entity, attribute, frame, **kw)
167
+
168
+ def correlations(self, entity: str, **kw) -> list[str]:
169
+ return self.registry.correlations(entity, **kw)
170
+
171
+ def correlate(self, a: str, b: str, evidence: str, **kw) -> dict:
172
+ return self.registry.correlate(a, b, evidence, **kw)
173
+
174
+ def correlation_conflicts(self, **kw) -> list[dict]:
175
+ return self.registry.correlation_conflicts(**kw)
176
+
177
+ def path(self, a: str, b: str, **kw) -> list[str] | None:
178
+ return self.indexes.path(a, b, **kw)
179
+
180
+ def route(self, a: str, b: str, **kw) -> dict:
181
+ return self.indexes.route(a, b, **kw)
182
+
183
+ def salience(
184
+ self, entity: str, frame: str = CANON, as_of: float | None = None
185
+ ) -> float:
186
+ return self.salience_index.salience(entity, frame, as_of)
187
+
188
+ def confidence(
189
+ self,
190
+ entity: str,
191
+ attribute: str,
192
+ frame: str | list[str] = CANON,
193
+ as_of: float | None = None,
194
+ asserted_as_of: int | None = None,
195
+ ) -> dict:
196
+ return self.indexes.confidence(
197
+ entity, attribute, frame=frame, as_of=as_of,
198
+ asserted_as_of=asserted_as_of,
199
+ )
200
+
201
+ def neighborhood(self, entity: str, **kw) -> dict:
202
+ return self.indexes.neighborhood(entity, **kw)
203
+
204
+ def aggregate(self, container: str, member_attribute: str, op: str, **kw) -> dict:
205
+ return self.indexes.aggregate(container, member_attribute, op, **kw)
206
+
207
+ def fidelity_audit(self, frame: str = CANON, as_of: float | None = None) -> dict:
208
+ """Structural ingestion-fidelity gaps, derived (INGESTION-FIDELITY-V1).
209
+ Read-only; run after a build's classification + `truth.scan()` (it never
210
+ classifies or scans). The engine surfaces gaps; the host joins arc/cast
211
+ severity and drives targeted re-extraction."""
212
+ from patternbuffer.classify import EVENT, STATE
213
+
214
+ collisions = self.registry.name_collisions(frame=frame, valid_as_of=as_of)
215
+
216
+ unstamped: list[dict] = []
217
+ entities: set[str] = set()
218
+ for row in self.buffer.visible(frame=frame):
219
+ if row.entity.startswith("a:") or row.entity.startswith(ATTR_PREFIX):
220
+ continue
221
+ eid = self.registry.resolve(row.entity)
222
+ if eid.startswith(("obj:", "person:")):
223
+ entities.add(eid)
224
+ # Meta/identity edges (same_as/distinct_from/aka/source…) are
225
+ # classified EVENT and carry no valid_from BY DESIGN — bookkeeping,
226
+ # never a spine fact; they are not an unstamped gap.
227
+ if row.attribute in META_ATTRIBUTES:
228
+ continue
229
+ if row.valid_from is None and self.classifier.get(row.id) is not None \
230
+ and self.classifier.durability(row.id) in (STATE, EVENT):
231
+ unstamped.append({"entity": eid, "attribute": row.attribute,
232
+ "assertion_id": row.id})
233
+
234
+ orphans = sorted(
235
+ e for e in entities
236
+ if not self.indexes.locate(e, frame=frame, valid_as_of=as_of)
237
+ )
238
+
239
+ conflicts = [
240
+ {"entity": c.entity, "attribute": c.attribute, "frame": c.frame,
241
+ "kind": c.kind, "assertion_ids": list(c.assertion_ids)}
242
+ for c in self.truth.open_conflicts()
243
+ ]
244
+
245
+ live = sum(1 for g in collisions if g["live"])
246
+ return {
247
+ "frame": frame,
248
+ "name_collisions": collisions,
249
+ "unstamped_timed": unstamped,
250
+ "orphan_entities": orphans,
251
+ "open_conflicts": conflicts,
252
+ "summary": {
253
+ "name_collisions": live, # live-fragmentation groups only
254
+ "name_collisions_total": len(collisions),
255
+ "unstamped_timed": len(unstamped),
256
+ "orphan_entities": len(orphans),
257
+ "open_conflicts": len(conflicts),
258
+ },
259
+ }
260
+
261
+ def materialize(self, scope, **kw) -> Materialization:
262
+ return self.projector.materialize(scope, **kw)
263
+
264
+ # Writes (each behind its role).
265
+
266
+ def ingest(self, text: str, context: str = "", frame: str | None = None,
267
+ classify: str = "inline", extract: str = "full",
268
+ cursor_authoritative: bool = False, pov: str | None = None) -> list:
269
+ return self.ingestor.ingest(text, context, frame=frame, classify=classify,
270
+ extract=extract,
271
+ cursor_authoritative=cursor_authoritative,
272
+ pov=pov)
273
+
274
+ def extract(self, text: str, context: str = "", extract: str = "full",
275
+ pov: str | None = None) -> list:
276
+ """Read-only extraction (INGEST-LATENCY-V2): the host parallelizes these,
277
+ then ingest_structured()s the results serially. No write. `pov`
278
+ (SHAPE-FIX-V1 4c): the viewpoint entity id for deixis binding."""
279
+ return self.ingestor.extract(text, context, extract=extract, pov=pov)
280
+
281
+ def ingest_structured(self, items: list[dict], frame: str | None = None,
282
+ classify: str = "inline",
283
+ cursor_authoritative: bool = False) -> list:
284
+ return self.ingestor.ingest_structured(
285
+ items, frame=frame, classify=classify,
286
+ cursor_authoritative=cursor_authoritative)
287
+
288
+ def resolve(self, entity: str, aspect: str, frame: str = CANON, access=None):
289
+ return self.resolver.resolve(entity, aspect, frame, access)
290
+
291
+ @property
292
+ def porcelain(self):
293
+ """The frozen host surface (PORCELAIN-V1). Lazy; import-local to
294
+ avoid circularity."""
295
+ if not hasattr(self, "_porcelain"):
296
+ from patternbuffer.porcelain import Porcelain
297
+ self._porcelain = Porcelain(self)
298
+ return self._porcelain
299
+
300
+ def charter(self) -> dict[str, Any]:
301
+ """The world's self-description, read from world:self (letter 026)."""
302
+ out: dict[str, Any] = {}
303
+ for attr, result in self.indexes.current_state("world:self").items():
304
+ if result.winner is not None:
305
+ out[attr] = result.winner.value
306
+ return out
307
+
308
+ def close(self) -> None:
309
+ # An open build session never outlives the buffer: abort (restore the
310
+ # classify toggle, classify nothing) before closing (BUILD-SESSION-V1).
311
+ if hasattr(self, "_porcelain"):
312
+ self._porcelain.abort_build()
313
+ self.buffer.close()
314
+
315
+
316
+ def _no_model(prompt: str, schema: dict) -> Any:
317
+ raise RuntimeError("no model callable injected for this operation")