context-motilis 0.0.1__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 (185) hide show
  1. context_motilis-0.0.1/.gitignore +28 -0
  2. context_motilis-0.0.1/LICENSE +21 -0
  3. context_motilis-0.0.1/PKG-INFO +150 -0
  4. context_motilis-0.0.1/README.md +126 -0
  5. context_motilis-0.0.1/docs/adr/0001-mit-licence.md +50 -0
  6. context_motilis-0.0.1/docs/adr/0002-as-of-is-a-position.md +47 -0
  7. context_motilis-0.0.1/docs/adr/0003-follow-up-lag-follows-its-definition.md +52 -0
  8. context_motilis-0.0.1/docs/adr/0004-scheduling-inputs-are-read-when-the-occurrence-is-created.md +56 -0
  9. context_motilis-0.0.1/docs/adr/0005-urgent-is-a-flag-on-the-two-occurrence-sources.md +74 -0
  10. context_motilis-0.0.1/docs/adr/0006-the-brief-overlays-pending-confirmation-on-effective-state.md +92 -0
  11. context_motilis-0.0.1/docs/adr/0007-authority-takes-two-inputs-and-origin-is-not-one.md +71 -0
  12. context_motilis-0.0.1/docs/adr/0008-the-fallback-algorithm-and-what-late-confirmation-matches.md +81 -0
  13. context_motilis-0.0.1/docs/adr/0009-the-clock-is-a-clamped-wall-clock.md +68 -0
  14. context_motilis-0.0.1/docs/adr/0010-a-model-returns-operations-and-the-runtime-builds-the-envelope.md +91 -0
  15. context_motilis-0.0.1/docs/adr/0011-the-model-input-contract-discovery-disclosure-projection.md +106 -0
  16. context_motilis-0.0.1/docs/adr/0012-serve-waits-through-the-clock.md +136 -0
  17. context_motilis-0.0.1/docs/adr/0013-a-diverged-interpretation-is-recorded-bounded.md +96 -0
  18. context_motilis-0.0.1/docs/adr/0014-say-records-and-the-dispatcher-applies.md +76 -0
  19. context_motilis-0.0.1/docs/adr/0015-section-9s-script-follows-section-4-10.md +63 -0
  20. context_motilis-0.0.1/docs/adr/0016-the-listing-is-a-tool-as-well-as-a-resource.md +74 -0
  21. context_motilis-0.0.1/docs/adr/README.md +50 -0
  22. context_motilis-0.0.1/docs/audits/2026-09-12-baseline.md +148 -0
  23. context_motilis-0.0.1/docs/audits/2026-09-12-documentation-system-audit.md +750 -0
  24. context_motilis-0.0.1/docs/audits/2026-09-12-post-task-3.md +94 -0
  25. context_motilis-0.0.1/docs/audits/2026-09-12-task-2-store.md +82 -0
  26. context_motilis-0.0.1/docs/brief-v7.md +794 -0
  27. context_motilis-0.0.1/docs/hosts.md +66 -0
  28. context_motilis-0.0.1/docs/matching.md +249 -0
  29. context_motilis-0.0.1/docs/notes/README.md +17 -0
  30. context_motilis-0.0.1/docs/notes/lexical-ambiguity-is-not-capture-retry-divergence.md +63 -0
  31. context_motilis-0.0.1/docs/roadmap.md +233 -0
  32. context_motilis-0.0.1/docs/walkthrough.md +288 -0
  33. context_motilis-0.0.1/docs/walkthrough.sh +88 -0
  34. context_motilis-0.0.1/fixtures/README.md +7 -0
  35. context_motilis-0.0.1/pyproject.toml +123 -0
  36. context_motilis-0.0.1/src/context_motilis/__init__.py +10 -0
  37. context_motilis-0.0.1/src/context_motilis/canon.py +187 -0
  38. context_motilis-0.0.1/src/context_motilis/capture/__init__.py +0 -0
  39. context_motilis-0.0.1/src/context_motilis/capture/envelope.py +174 -0
  40. context_motilis-0.0.1/src/context_motilis/capture/fallback.py +152 -0
  41. context_motilis-0.0.1/src/context_motilis/capture/filing.py +162 -0
  42. context_motilis-0.0.1/src/context_motilis/capture/intake.py +320 -0
  43. context_motilis-0.0.1/src/context_motilis/capture/interpret.py +463 -0
  44. context_motilis-0.0.1/src/context_motilis/capture/process.py +439 -0
  45. context_motilis-0.0.1/src/context_motilis/capture/validator.py +305 -0
  46. context_motilis-0.0.1/src/context_motilis/cli.py +859 -0
  47. context_motilis-0.0.1/src/context_motilis/clock.py +236 -0
  48. context_motilis-0.0.1/src/context_motilis/contexts/__init__.py +1 -0
  49. context_motilis-0.0.1/src/context_motilis/contexts/acceptance.py +108 -0
  50. context_motilis-0.0.1/src/context_motilis/contexts/apply.py +780 -0
  51. context_motilis-0.0.1/src/context_motilis/contexts/brief.py +263 -0
  52. context_motilis-0.0.1/src/context_motilis/contexts/confirmations.py +871 -0
  53. context_motilis-0.0.1/src/context_motilis/contexts/cursors.py +257 -0
  54. context_motilis-0.0.1/src/context_motilis/contexts/derivation.py +84 -0
  55. context_motilis-0.0.1/src/context_motilis/contexts/header.py +202 -0
  56. context_motilis-0.0.1/src/context_motilis/contexts/kinds.py +257 -0
  57. context_motilis-0.0.1/src/context_motilis/contexts/operations.py +133 -0
  58. context_motilis-0.0.1/src/context_motilis/contexts/parties.py +208 -0
  59. context_motilis-0.0.1/src/context_motilis/contexts/projection.py +481 -0
  60. context_motilis-0.0.1/src/context_motilis/contexts/reading.py +136 -0
  61. context_motilis-0.0.1/src/context_motilis/contexts/suspension.py +170 -0
  62. context_motilis-0.0.1/src/context_motilis/contexts/templates.py +71 -0
  63. context_motilis-0.0.1/src/context_motilis/contexts/versions.py +216 -0
  64. context_motilis-0.0.1/src/context_motilis/contexts/why.py +200 -0
  65. context_motilis-0.0.1/src/context_motilis/demo.py +380 -0
  66. context_motilis-0.0.1/src/context_motilis/echoes.py +93 -0
  67. context_motilis-0.0.1/src/context_motilis/host.py +771 -0
  68. context_motilis-0.0.1/src/context_motilis/ids.py +31 -0
  69. context_motilis-0.0.1/src/context_motilis/mcp_host.py +893 -0
  70. context_motilis-0.0.1/src/context_motilis/models.py +108 -0
  71. context_motilis-0.0.1/src/context_motilis/ports.py +375 -0
  72. context_motilis-0.0.1/src/context_motilis/py.typed +0 -0
  73. context_motilis-0.0.1/src/context_motilis/render.py +620 -0
  74. context_motilis-0.0.1/src/context_motilis/runtime.py +36 -0
  75. context_motilis-0.0.1/src/context_motilis/scheduler/__init__.py +1 -0
  76. context_motilis-0.0.1/src/context_motilis/scheduler/matching.py +283 -0
  77. context_motilis-0.0.1/src/context_motilis/scheduler/occurrences.py +560 -0
  78. context_motilis-0.0.1/src/context_motilis/scheduler/rules.py +200 -0
  79. context_motilis-0.0.1/src/context_motilis/scheduler/serve.py +256 -0
  80. context_motilis-0.0.1/src/context_motilis/scheduler/temperature.py +204 -0
  81. context_motilis-0.0.1/src/context_motilis/scheduler/tick.py +262 -0
  82. context_motilis-0.0.1/src/context_motilis/sinks.py +142 -0
  83. context_motilis-0.0.1/src/context_motilis/store/__init__.py +1 -0
  84. context_motilis-0.0.1/src/context_motilis/store/artifacts.py +96 -0
  85. context_motilis-0.0.1/src/context_motilis/store/backup.py +472 -0
  86. context_motilis-0.0.1/src/context_motilis/store/database.py +237 -0
  87. context_motilis-0.0.1/src/context_motilis/store/events.py +245 -0
  88. context_motilis-0.0.1/src/context_motilis/store/hashes.py +88 -0
  89. context_motilis-0.0.1/src/context_motilis/store/ingestion.py +243 -0
  90. context_motilis-0.0.1/src/context_motilis/store/lease.py +153 -0
  91. context_motilis-0.0.1/src/context_motilis/store/materialize.py +194 -0
  92. context_motilis-0.0.1/src/context_motilis/store/outbox.py +354 -0
  93. context_motilis-0.0.1/src/context_motilis/store/rebuild.py +105 -0
  94. context_motilis-0.0.1/src/context_motilis/store/schema.py +552 -0
  95. context_motilis-0.0.1/tests/__init__.py +0 -0
  96. context_motilis-0.0.1/tests/acceptance/__init__.py +0 -0
  97. context_motilis-0.0.1/tests/acceptance/naming.py +8 -0
  98. context_motilis-0.0.1/tests/acceptance/registry.py +81 -0
  99. context_motilis-0.0.1/tests/acceptance/restart_segment.py +59 -0
  100. context_motilis-0.0.1/tests/acceptance/test_brief.py +308 -0
  101. context_motilis-0.0.1/tests/acceptance/test_brief_traceability.py +65 -0
  102. context_motilis-0.0.1/tests/acceptance/test_capture.py +454 -0
  103. context_motilis-0.0.1/tests/acceptance/test_cli.py +209 -0
  104. context_motilis-0.0.1/tests/acceptance/test_delivery_crash.py +141 -0
  105. context_motilis-0.0.1/tests/acceptance/test_fencing.py +190 -0
  106. context_motilis-0.0.1/tests/acceptance/test_mcp.py +422 -0
  107. context_motilis-0.0.1/tests/acceptance/test_models.py +170 -0
  108. context_motilis-0.0.1/tests/acceptance/test_pending.py +44 -0
  109. context_motilis-0.0.1/tests/acceptance/test_portability.py +232 -0
  110. context_motilis-0.0.1/tests/acceptance/test_registry.py +135 -0
  111. context_motilis-0.0.1/tests/acceptance/test_scheduler.py +418 -0
  112. context_motilis-0.0.1/tests/acceptance/test_stale_confirmation.py +154 -0
  113. context_motilis-0.0.1/tests/capture/__init__.py +0 -0
  114. context_motilis-0.0.1/tests/capture/conftest.py +95 -0
  115. context_motilis-0.0.1/tests/capture/test_envelope.py +199 -0
  116. context_motilis-0.0.1/tests/capture/test_fallback.py +107 -0
  117. context_motilis-0.0.1/tests/capture/test_filing.py +522 -0
  118. context_motilis-0.0.1/tests/capture/test_intake.py +298 -0
  119. context_motilis-0.0.1/tests/capture/test_interpret.py +475 -0
  120. context_motilis-0.0.1/tests/capture/test_process.py +420 -0
  121. context_motilis-0.0.1/tests/capture/test_validator.py +171 -0
  122. context_motilis-0.0.1/tests/conftest.py +160 -0
  123. context_motilis-0.0.1/tests/contexts/__init__.py +0 -0
  124. context_motilis-0.0.1/tests/contexts/test_brief.py +416 -0
  125. context_motilis-0.0.1/tests/contexts/test_confirmations.py +409 -0
  126. context_motilis-0.0.1/tests/contexts/test_cursors.py +429 -0
  127. context_motilis-0.0.1/tests/contexts/test_header.py +320 -0
  128. context_motilis-0.0.1/tests/contexts/test_kinds.py +96 -0
  129. context_motilis-0.0.1/tests/contexts/test_late_confirmation.py +497 -0
  130. context_motilis-0.0.1/tests/contexts/test_operations.py +397 -0
  131. context_motilis-0.0.1/tests/contexts/test_parties.py +216 -0
  132. context_motilis-0.0.1/tests/contexts/test_reading.py +185 -0
  133. context_motilis-0.0.1/tests/contexts/test_suspension.py +117 -0
  134. context_motilis-0.0.1/tests/contexts/test_templates.py +312 -0
  135. context_motilis-0.0.1/tests/contexts/test_why.py +118 -0
  136. context_motilis-0.0.1/tests/hosts/__init__.py +0 -0
  137. context_motilis-0.0.1/tests/hosts/test_mcp.py +706 -0
  138. context_motilis-0.0.1/tests/invariants/README.md +24 -0
  139. context_motilis-0.0.1/tests/invariants/__init__.py +0 -0
  140. context_motilis-0.0.1/tests/invariants/test_acceptance.py +182 -0
  141. context_motilis-0.0.1/tests/invariants/test_attention_independence.py +98 -0
  142. context_motilis-0.0.1/tests/invariants/test_authority.py +257 -0
  143. context_motilis-0.0.1/tests/invariants/test_brief_completeness.py +166 -0
  144. context_motilis-0.0.1/tests/invariants/test_canonical_record.py +471 -0
  145. context_motilis-0.0.1/tests/invariants/test_capture.py +234 -0
  146. context_motilis-0.0.1/tests/invariants/test_dormancy.py +61 -0
  147. context_motilis-0.0.1/tests/invariants/test_ingestion.py +416 -0
  148. context_motilis-0.0.1/tests/invariants/test_occurrence.py +198 -0
  149. context_motilis-0.0.1/tests/invariants/test_provenance.py +198 -0
  150. context_motilis-0.0.1/tests/invariants/test_stated.py +118 -0
  151. context_motilis-0.0.1/tests/invariants/test_time.py +156 -0
  152. context_motilis-0.0.1/tests/scheduler/__init__.py +0 -0
  153. context_motilis-0.0.1/tests/scheduler/conftest.py +97 -0
  154. context_motilis-0.0.1/tests/scheduler/test_matching.py +173 -0
  155. context_motilis-0.0.1/tests/scheduler/test_occurrences.py +668 -0
  156. context_motilis-0.0.1/tests/scheduler/test_rules.py +239 -0
  157. context_motilis-0.0.1/tests/scheduler/test_serve.py +198 -0
  158. context_motilis-0.0.1/tests/scheduler/test_temperature.py +241 -0
  159. context_motilis-0.0.1/tests/scheduler/test_tick.py +310 -0
  160. context_motilis-0.0.1/tests/store/__init__.py +0 -0
  161. context_motilis-0.0.1/tests/store/test_artifacts.py +64 -0
  162. context_motilis-0.0.1/tests/store/test_backup.py +287 -0
  163. context_motilis-0.0.1/tests/store/test_canon.py +141 -0
  164. context_motilis-0.0.1/tests/store/test_events.py +207 -0
  165. context_motilis-0.0.1/tests/store/test_lease.py +119 -0
  166. context_motilis-0.0.1/tests/store/test_migration.py +184 -0
  167. context_motilis-0.0.1/tests/store/test_outbox.py +183 -0
  168. context_motilis-0.0.1/tests/store/test_rebuild.py +75 -0
  169. context_motilis-0.0.1/tests/test_cli_display.py +152 -0
  170. context_motilis-0.0.1/tests/test_cli_say.py +476 -0
  171. context_motilis-0.0.1/tests/test_clock.py +206 -0
  172. context_motilis-0.0.1/tests/test_demo.py +258 -0
  173. context_motilis-0.0.1/tests/test_host.py +193 -0
  174. context_motilis-0.0.1/tests/test_models.py +120 -0
  175. context_motilis-0.0.1/tests/test_network_guard.py +92 -0
  176. context_motilis-0.0.1/tests/test_package.py +41 -0
  177. context_motilis-0.0.1/tests/test_process_chain.py +203 -0
  178. context_motilis-0.0.1/tests/test_release_tag.py +172 -0
  179. context_motilis-0.0.1/tests/test_render.py +510 -0
  180. context_motilis-0.0.1/tests/test_repo_hygiene.py +149 -0
  181. context_motilis-0.0.1/tests/test_time_to_return.py +169 -0
  182. context_motilis-0.0.1/tools/__init__.py +1 -0
  183. context_motilis-0.0.1/tools/release_tag.py +178 -0
  184. context_motilis-0.0.1/tools/repo_hygiene.py +223 -0
  185. context_motilis-0.0.1/tools/time_to_return.py +403 -0
@@ -0,0 +1,28 @@
1
+ # Never committed (§1.2): household data and store files.
2
+ cultures/
3
+ artifacts/
4
+ *.sqlite*
5
+ .env
6
+ .env.*
7
+
8
+ # Python
9
+ __pycache__/
10
+ *.py[cod]
11
+ .venv/
12
+ venv/
13
+ build/
14
+ dist/
15
+ *.egg-info/
16
+
17
+ # Tooling caches
18
+ .pytest_cache/
19
+ .mypy_cache/
20
+ .ruff_cache/
21
+ .hypothesis/
22
+ .coverage
23
+ htmlcov/
24
+
25
+ # Editors / OS
26
+ .idea/
27
+ .vscode/
28
+ .DS_Store
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 cetatec
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.
@@ -0,0 +1,150 @@
1
+ Metadata-Version: 2.5
2
+ Name: context-motilis
3
+ Version: 0.0.1
4
+ Summary: A local-first runtime for resumable context.
5
+ Project-URL: Homepage, https://github.com/cetatec/context-motilis
6
+ Project-URL: Source, https://github.com/cetatec/context-motilis
7
+ Author: cetatec
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: context,local-first,mcp,provenance,runtime
11
+ Classifier: Development Status :: 2 - Pre-Alpha
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Typing :: Typed
19
+ Requires-Python: >=3.12
20
+ Requires-Dist: mcp==2.2.0
21
+ Requires-Dist: pydantic<3,>=2.12
22
+ Requires-Dist: typer<1,>=0.16
23
+ Description-Content-Type: text/markdown
24
+
25
+ # context-motilis
26
+
27
+ A local-first runtime for resumable context.
28
+
29
+ A **context** is a durable unit — a matter, later an entity or an archive — that
30
+ can be suspended for months and resumed by a person or by any model, with its
31
+ evidence, history, accepted state, commitments in both directions, and dates
32
+ intact. The runtime captures inputs durably before it processes them, keeps time
33
+ and fires reminders without a model, authorizes changes by their consequences,
34
+ records every version with its provenance, and produces a deterministic **brief**
35
+ that answers "where were we?" without rereading history.
36
+
37
+ The model interprets and proposes; the runtime preserves, schedules, and
38
+ authorizes. A dormant context owns no running process; the shared runtime reacts
39
+ to indexed dates and events on its behalf.
40
+
41
+ Tools provide capabilities. context-motilis provides continuity.
42
+
43
+ ## Status
44
+
45
+ Stage 1, tasks 1 through 7 of §10 are merged: the store, contexts, the scheduler,
46
+ capture, the brief, export and import, and the CLI host with `motilis demo`
47
+ replaying the stage-1 script of §9. Twenty-three of the twenty-nine stage-1
48
+ acceptance cases are real tests; the six still pending are all MCP, and land
49
+ with task 8.
50
+
51
+ What is absent: the MCP host (`motilis serve` as a server), and with it
52
+ elicitation over MCP and `context://inbox`. Under the default fake clock `serve`
53
+ runs a bounded number of passes; staying up is [ADR 0012](docs/adr/0012-serve-waits-through-the-clock.md)
54
+ and task 8.
55
+
56
+ Two things a new user should know before typing. The CLI is the human's host:
57
+ `note`, `commit`, `remind`, `open`, `ask`, `why`, `due` and `tick` work on a
58
+ clean checkout with no model anywhere. `say` needs a model, this repository
59
+ holds none (§11.1), and in stage 1 free text is interpreted through MCP, where
60
+ the model is the client; on the CLI `say` replays a recorded reading and
61
+ refuses plainly without one. The demo is the way to see the whole loop:
62
+
63
+ ```
64
+ uvx --from git+https://github.com/cetatec/context-motilis motilis demo
65
+ ```
66
+
67
+ The remaining gate is checkpoint 1 (§1.3), run by the owner.
68
+
69
+ ## The brief
70
+
71
+ [`docs/brief-v7.md`](docs/brief-v7.md) is the controlling specification, committed
72
+ verbatim. Every `§` reference in this repository, in its tests and in its commit
73
+ messages points into it. It is authoritative: where it and any other document in
74
+ this repository disagree, it governs. Revisions supersede one another by number,
75
+ and v7 is the one in force.
76
+
77
+ [`docs/roadmap.md`](docs/roadmap.md) indexes where the work stands against §7 and
78
+ §10; it decides nothing.
79
+
80
+ ## Install
81
+
82
+ Before the first release:
83
+
84
+ ```
85
+ uvx --from git+https://github.com/cetatec/context-motilis motilis version
86
+ ```
87
+
88
+ Once released:
89
+
90
+ ```
91
+ uvx --from context-motilis motilis version
92
+ # or
93
+ uv tool install context-motilis && motilis version
94
+ ```
95
+
96
+ The package name and the executable name differ; `--from` is not optional.
97
+
98
+ ## Develop
99
+
100
+ Python ≥ 3.12 and [uv](https://docs.astral.sh/uv/).
101
+
102
+ ```
103
+ uv sync --group dev
104
+ uv run ruff check . && uv run ruff format --check .
105
+ uv run mypy
106
+ uv run pytest
107
+ ```
108
+
109
+ Enable the hygiene hook once per clone, so household data and store files cannot
110
+ be committed:
111
+
112
+ ```
113
+ git config core.hooksPath .githooks
114
+ ```
115
+
116
+ The same check runs in CI, so a bypassed hook still fails the build.
117
+
118
+ ## What is never in this repository
119
+
120
+ Household data, real names, real documents, `.sqlite` files, `artifacts/` and
121
+ `cultures/`. CI runs with no model, no network and no secrets.
122
+
123
+ ## Layout
124
+
125
+ ```
126
+ src/context_motilis/ the runtime and its hosts
127
+ tests/invariants/ one property test per invariant of §3
128
+ tests/acceptance/ the §8 acceptance suite, by stable name
129
+ docs/brief-v7.md the controlling specification; every § points here
130
+ docs/adr/ decisions the brief does not settle, and why
131
+ docs/hosts.md pinned protocol revision, SDK, and tested client builds
132
+ docs/roadmap.md where the work stands against §7 and §10
133
+ fixtures/ generated fixtures only
134
+ tools/ repository tooling, not part of the package
135
+ ```
136
+
137
+ ## Licence
138
+
139
+ MIT. See [`LICENSE`](LICENSE), and [ADR 0001](docs/adr/0001-mit-licence.md) for
140
+ why. Reuse is meant to be straightforward, commercial reuse included.
141
+
142
+ ## Two sentences
143
+
144
+ *Runtime:* A context is a durable boundary that can be suspended and
145
+ independently resumed. It retains evidence and history, may carry commitments
146
+ and deterministic behavior, and exposes a bounded brief to whichever human or
147
+ model attends to it next.
148
+
149
+ *Development principle:* What exactly must survive this kill? A feature that
150
+ cannot name what survives its kill is not added.
@@ -0,0 +1,126 @@
1
+ # context-motilis
2
+
3
+ A local-first runtime for resumable context.
4
+
5
+ A **context** is a durable unit — a matter, later an entity or an archive — that
6
+ can be suspended for months and resumed by a person or by any model, with its
7
+ evidence, history, accepted state, commitments in both directions, and dates
8
+ intact. The runtime captures inputs durably before it processes them, keeps time
9
+ and fires reminders without a model, authorizes changes by their consequences,
10
+ records every version with its provenance, and produces a deterministic **brief**
11
+ that answers "where were we?" without rereading history.
12
+
13
+ The model interprets and proposes; the runtime preserves, schedules, and
14
+ authorizes. A dormant context owns no running process; the shared runtime reacts
15
+ to indexed dates and events on its behalf.
16
+
17
+ Tools provide capabilities. context-motilis provides continuity.
18
+
19
+ ## Status
20
+
21
+ Stage 1, tasks 1 through 7 of §10 are merged: the store, contexts, the scheduler,
22
+ capture, the brief, export and import, and the CLI host with `motilis demo`
23
+ replaying the stage-1 script of §9. Twenty-three of the twenty-nine stage-1
24
+ acceptance cases are real tests; the six still pending are all MCP, and land
25
+ with task 8.
26
+
27
+ What is absent: the MCP host (`motilis serve` as a server), and with it
28
+ elicitation over MCP and `context://inbox`. Under the default fake clock `serve`
29
+ runs a bounded number of passes; staying up is [ADR 0012](docs/adr/0012-serve-waits-through-the-clock.md)
30
+ and task 8.
31
+
32
+ Two things a new user should know before typing. The CLI is the human's host:
33
+ `note`, `commit`, `remind`, `open`, `ask`, `why`, `due` and `tick` work on a
34
+ clean checkout with no model anywhere. `say` needs a model, this repository
35
+ holds none (§11.1), and in stage 1 free text is interpreted through MCP, where
36
+ the model is the client; on the CLI `say` replays a recorded reading and
37
+ refuses plainly without one. The demo is the way to see the whole loop:
38
+
39
+ ```
40
+ uvx --from git+https://github.com/cetatec/context-motilis motilis demo
41
+ ```
42
+
43
+ The remaining gate is checkpoint 1 (§1.3), run by the owner.
44
+
45
+ ## The brief
46
+
47
+ [`docs/brief-v7.md`](docs/brief-v7.md) is the controlling specification, committed
48
+ verbatim. Every `§` reference in this repository, in its tests and in its commit
49
+ messages points into it. It is authoritative: where it and any other document in
50
+ this repository disagree, it governs. Revisions supersede one another by number,
51
+ and v7 is the one in force.
52
+
53
+ [`docs/roadmap.md`](docs/roadmap.md) indexes where the work stands against §7 and
54
+ §10; it decides nothing.
55
+
56
+ ## Install
57
+
58
+ Before the first release:
59
+
60
+ ```
61
+ uvx --from git+https://github.com/cetatec/context-motilis motilis version
62
+ ```
63
+
64
+ Once released:
65
+
66
+ ```
67
+ uvx --from context-motilis motilis version
68
+ # or
69
+ uv tool install context-motilis && motilis version
70
+ ```
71
+
72
+ The package name and the executable name differ; `--from` is not optional.
73
+
74
+ ## Develop
75
+
76
+ Python ≥ 3.12 and [uv](https://docs.astral.sh/uv/).
77
+
78
+ ```
79
+ uv sync --group dev
80
+ uv run ruff check . && uv run ruff format --check .
81
+ uv run mypy
82
+ uv run pytest
83
+ ```
84
+
85
+ Enable the hygiene hook once per clone, so household data and store files cannot
86
+ be committed:
87
+
88
+ ```
89
+ git config core.hooksPath .githooks
90
+ ```
91
+
92
+ The same check runs in CI, so a bypassed hook still fails the build.
93
+
94
+ ## What is never in this repository
95
+
96
+ Household data, real names, real documents, `.sqlite` files, `artifacts/` and
97
+ `cultures/`. CI runs with no model, no network and no secrets.
98
+
99
+ ## Layout
100
+
101
+ ```
102
+ src/context_motilis/ the runtime and its hosts
103
+ tests/invariants/ one property test per invariant of §3
104
+ tests/acceptance/ the §8 acceptance suite, by stable name
105
+ docs/brief-v7.md the controlling specification; every § points here
106
+ docs/adr/ decisions the brief does not settle, and why
107
+ docs/hosts.md pinned protocol revision, SDK, and tested client builds
108
+ docs/roadmap.md where the work stands against §7 and §10
109
+ fixtures/ generated fixtures only
110
+ tools/ repository tooling, not part of the package
111
+ ```
112
+
113
+ ## Licence
114
+
115
+ MIT. See [`LICENSE`](LICENSE), and [ADR 0001](docs/adr/0001-mit-licence.md) for
116
+ why. Reuse is meant to be straightforward, commercial reuse included.
117
+
118
+ ## Two sentences
119
+
120
+ *Runtime:* A context is a durable boundary that can be suspended and
121
+ independently resumed. It retains evidence and history, may carry commitments
122
+ and deterministic behavior, and exposes a bounded brief to whichever human or
123
+ model attends to it next.
124
+
125
+ *Development principle:* What exactly must survive this kill? A feature that
126
+ cannot name what survives its kill is not added.
@@ -0,0 +1,50 @@
1
+ # 0001. The project is licensed MIT
2
+
3
+ **Status:** accepted
4
+ **Date:** 2026-09-12
5
+ **Question:** [#1](https://github.com/cetatec/context-motilis/issues/1), labelled `question`
6
+ **Decided by:** the project owner, answering that issue
7
+
8
+ ## Context
9
+
10
+ The brief requires publishing `0.0.x` early, to PyPI by trusted publishing
11
+ (§1.1). It does not name a licence. A licence is not a question an agent may
12
+ settle on the owner's behalf, so `pyproject.toml` carried no `license` field
13
+ and the repository had no `LICENSE` file. The first release was blocked on it,
14
+ and nothing else was.
15
+
16
+ ## Decision
17
+
18
+ MIT.
19
+
20
+ ## Rationale
21
+
22
+ In the owner's words:
23
+
24
+ > Motilis uses MIT to favor straightforward reuse and broad adoption, including
25
+ > commercial reuse. We accept that others may build competing products. Our
26
+ > commercial experiments will focus on additional value, not on charging for
27
+ > permission already granted by the open-source license.
28
+
29
+ ## Consequences
30
+
31
+ - Anyone may use, modify, redistribute and sell this software, including inside
32
+ a closed-source product and including in direct competition. This is accepted,
33
+ not a side effect to be mitigated later by relicensing.
34
+ - No patent grant is made or received. Apache-2.0 would have given one. This was
35
+ the trade taken in exchange for the simplest possible terms.
36
+ - Contributions arrive under the same terms, so no contributor licence agreement
37
+ is needed and none should be introduced without a superseding record.
38
+ - Any future commercial offering has to be worth paying for on its own, because
39
+ the licence already grants permission. Relicensing the existing code away from
40
+ MIT would not retract what is already granted.
41
+ - `LICENSE` is included in the built distributions, so the terms travel with the
42
+ package rather than living only in the repository.
43
+
44
+ ## Implementation
45
+
46
+ `LICENSE` at the repository root; `license = "MIT"` and
47
+ `license-files = ["LICENSE"]` in `pyproject.toml`, as an SPDX expression rather
48
+ than a classifier. The copyright line names `cetatec`, matching the GitHub owner
49
+ and the package author. If a legal entity should hold the copyright instead,
50
+ that is an amendment to this record, not a new decision.
@@ -0,0 +1,47 @@
1
+ # 0002. `as_of` is a position in the committed order
2
+
3
+ **Status:** accepted
4
+ **Date:** 2026-09-12
5
+ **Question:** [#8](https://github.com/cetatec/context-motilis/issues/8), labelled `question`
6
+ **Decided by:** the project owner, accepting the answer on that issue
7
+
8
+ ## Context
9
+
10
+ §5.1 makes `(epoch, sequence)` the committed order and says timestamps are data,
11
+ never order. §3 `canonical_record` asks for a `canon/1` state-hash rebuild "at
12
+ `as_of`", and the brief does not say in so many words what `as_of` is made of.
13
+ Two readings were available: a position in that order, or a wall-clock instant.
14
+
15
+ The reading matters because `as_of` is the boundary a replay stops at and the
16
+ thing two state hashes must share before they can be compared at all.
17
+
18
+ ## Decision
19
+
20
+ `as_of` is a position in the committed event order, `(epoch, sequence)`. Never a
21
+ time.
22
+
23
+ ## Rationale
24
+
25
+ A timestamp cutoff does not identify one boundary. Events can share a timestamp,
26
+ and nothing in the brief makes event timestamps monotonic — §5.1 says the
27
+ opposite, that timestamps are data. So "every event at or before time T" can
28
+ name several different sets of events, and a hash taken at one of them is not
29
+ comparable with a hash taken at another. A position names exactly one prefix of
30
+ the record.
31
+
32
+ It also keeps one rule rather than two. If `as_of` were a time, the store would
33
+ order by `(epoch, sequence)` and cut by timestamp, and the two would disagree the
34
+ first time a clock moved.
35
+
36
+ ## Consequences
37
+
38
+ - `store.events.Position` is the value `as_of` names, and `hashes.state_hashes`
39
+ serializes its `epoch` and `sequence` into the overall hash.
40
+ - An export manifest carries two different things: the `as_of` through which the
41
+ materialization was rebuilt, and the position of the event recording that the
42
+ export happened. They are not the same number and neither substitutes.
43
+ - State hashes are comparable only at the same `as_of`, which the brief already
44
+ required and this makes checkable.
45
+
46
+ No code changed: task 2 implemented this reading, and the decision records why
47
+ it was the only one available rather than a choice that happened to be made.
@@ -0,0 +1,52 @@
1
+ # 0003. `follow_up_lag` follows its definition, not the demo figure
2
+
3
+ **Status:** accepted
4
+ **Date:** 2026-09-12
5
+ **Question:** [#15](https://github.com/cetatec/context-motilis/issues/15), labelled `question`
6
+ **Decided by:** the project owner, accepting the answer on that issue
7
+
8
+ ## Context
9
+
10
+ §4.10 defines the first overdue follow-up twice, and the two do not agree.
11
+
12
+ The rule: "for an open commitment owed to us, at `D + follow_up_lag`, shifted to
13
+ `quiet_hours_end` if inside quiet hours".
14
+
15
+ The constant: "`follow_up_lag` = next `quiet_hours_end` after D (demo: Sat
16
+ 09:00)".
17
+
18
+ With the default quiet hours — 21:00–08:00 Europe/Paris — the next
19
+ `quiet_hours_end` after a Friday 23:59:59 deadline is Saturday **08:00**, not
20
+ the 09:00 the parenthesis shows.
21
+
22
+ ## Decision
23
+
24
+ The definition governs. `follow_up_lag(D) = quiet_hours_end(D)`, and the demo's
25
+ "Sat 09:00" is a documentation error that should read 08:00.
26
+
27
+ The trailing "shifted to `quiet_hours_end` if inside quiet hours" is already
28
+ what the lag *is*, so it is not applied a second time. Repeats are different:
29
+ they are `previous + follow_up_cooldown` and only then shifted, because a
30
+ cooldown is not defined in terms of quiet hours.
31
+
32
+ ## Rationale
33
+
34
+ A definition and an example that disagree are not two requirements; one of them
35
+ is wrong. The definition is the one written as a rule, it composes with the
36
+ quiet-hours constant the brief also states, and it is the one the algorithm can
37
+ be derived from. The parenthesis is an illustration, and an illustration that
38
+ contradicts its own defaults illustrates nothing.
39
+
40
+ Applying the shift twice would be worse than redundant. At exactly 08:00 the
41
+ boundary rule says the moment is not inside quiet hours, so a second application
42
+ is a no-op today — but a later change to that boundary would silently start
43
+ advancing every first follow-up by a day.
44
+
45
+ ## Consequences
46
+
47
+ - A Friday 23:59:59 Paris deadline produces its first follow-up at Saturday
48
+ 08:00 Paris, `2026-09-12T06:00:00Z`.
49
+ - `ACC-clock-cases` asserts that, and `scheduler/rules.follow_up_lag_surface` is
50
+ one line because of it.
51
+ - The brief's own parenthesis still says 09:00. Correcting the specification is
52
+ the owner's to make; this record is why the implementation does not match it.
@@ -0,0 +1,56 @@
1
+ # 0004. Scheduling inputs are read when the occurrence is created
2
+
3
+ **Status:** accepted
4
+ **Date:** 2026-09-12
5
+ **Question:** [#16](https://github.com/cetatec/context-motilis/issues/16), labelled `question`
6
+ **Decided by:** the project owner, accepting the answer on that issue
7
+
8
+ ## Context
9
+
10
+ §4.10 computes a pre-deadline reminder from the deadline `D` and the act `A`,
11
+ and `A` comes from the context's `next`. So stating a `next` after a date, or
12
+ changing its act later, changes what §4.10 would compute for that date.
13
+
14
+ §3 `occurrence` says the opposite thing about what may move: at most one logical
15
+ occurrence per effect key, "with its original scheduled time preserved". The key
16
+ carries the node and the deadline, not the act.
17
+
18
+ ## Decision
19
+
20
+ The effective `due`, `next.act` and confirmed `next.remind_at` are read when the
21
+ logical occurrence is **first scheduled**. A later change to any of them does
22
+ not move an occurrence that already exists.
23
+
24
+ ## Rationale
25
+
26
+ §3's sentence is the more specific of the two and the one written as an
27
+ invariant. §4.10 says how a surface time is computed; it does not say the
28
+ computation is repeated against an occurrence that has already been placed.
29
+
30
+ Putting the act in the effect key was the alternative and is worse in a way that
31
+ is easy to miss: an act edited after the reminder has already fired would mint a
32
+ second key, and for a deadline in the past that second occurrence surfaces
33
+ immediately as a catch-up. A metadata correction would ring a bell.
34
+
35
+ ## Consequences
36
+
37
+ - `desired` recomputes from current state and so reports a time the table does
38
+ not hold, for any occurrence whose inputs changed after it was scheduled. The
39
+ disagreement is real and is recorded by
40
+ `test_an_act_stated_after_a_date_does_not_move_its_reminder`.
41
+ - **There is no way to move a scheduled reminder.** The original issue text
42
+ offered a confirmed `remind_at` as the owner's escape hatch. That was wrong:
43
+ the key does not carry `remind_at` either, so a later one is the same spent
44
+ key and reconciliation skips it. Pinned by
45
+ `test_a_later_confirmed_remind_at_does_not_move_it_either`. Moving an
46
+ occurrence that exists would need an explicit reschedule, which the brief does
47
+ not describe and which is not in stage 1.
48
+ - Urgency is the one input that reaches an occurrence after the fact, and it
49
+ does so by minting a different key rather than by re-timing one. See
50
+ [0005](0005-urgent-is-a-flag-on-the-two-occurrence-sources.md).
51
+
52
+ A follow-up worth taking when the model is next touched: `desired` could report
53
+ the stored time for an occurrence that exists and the computed time only for one
54
+ that does not, so it stops claiming a time reconciliation will never apply. That
55
+ is behaviour-neutral and was deliberately left out of the change that answered
56
+ this question, because it presumes the answer.
@@ -0,0 +1,74 @@
1
+ # 0005. `urgent` is a flag on `commitment` and `date`
2
+
3
+ **Status:** accepted
4
+ **Date:** 2026-09-12
5
+ **Question:** [#18](https://github.com/cetatec/context-motilis/issues/18), labelled `question`
6
+ **Decided by:** the project owner, accepting the answer on that issue
7
+
8
+ ## Context
9
+
10
+ §4.10 ends its precedence paragraph with one clause:
11
+
12
+ > `urgent` sets `surface = min(surface, now)`.
13
+
14
+ That is the only occurrence of the word in the brief. No §4.4 kind carries the
15
+ flag, no §6.1 operation sets it, no §5 table stores it, and §4.8's authority
16
+ table says nothing about who may declare something urgent. The rule could not be
17
+ implemented without first deciding where urgency lives, which §11.4 makes an
18
+ approval twice over: a field on a kind §4.4 enumerates, and a change to §4.10's
19
+ precedence.
20
+
21
+ ## Decision
22
+
23
+ A boolean `urgent`, defaulting to `false`, on `commitment` and `date` — the two
24
+ kinds §4.10 names as occurrence sources. Effective only from an **accepted**
25
+ version. It clamps the **pre-deadline reminder** only, at the reconciliation
26
+ instant, and it is part of that occurrence's effect key.
27
+
28
+ ## Rationale
29
+
30
+ **On the node, not the occurrence.** Urgency is a property of the thing, not of
31
+ the reminder about it. On an occurrence it would be unreachable from a mutation
32
+ set and would not survive the node being superseded.
33
+
34
+ **Not on `next`.** A `next` raises no occurrence of its own — §4.10 says "for a
35
+ commitment or date with due D and act A" — so an `urgent` there would have
36
+ nothing to act on.
37
+
38
+ **Accepted only**, the rule `remind_at` already follows (§4.5). A model that
39
+ could mark its own extraction urgent could make the runtime interrupt the owner
40
+ on the strength of an untrusted document, which is the shape §4.8 exists to
41
+ prevent. A fallback-derived node is not schedulable at all, so it never reaches
42
+ the question.
43
+
44
+ **Pre-deadline only.** An overdue follow-up exists *because* the deadline
45
+ passed, so "surface it sooner" has nothing to mean for it, and an urgent overdue
46
+ commitment would otherwise fire twice at once. §4.10 fixes both expiry times to
47
+ the expiry itself, and a thing does not lapse sooner for being urgent.
48
+
49
+ **In the effect key**, which is the part that took implementing to see.
50
+ Acceptance lands *after* the mutation carrying it — `apply_set` reconciles, then
51
+ `confirm` accepts and reconciles again — so by the moment urgency is effective
52
+ the ordinary reminder has already been scheduled and its key is spent. Without
53
+ urgency in the key the rule could never fire on the case it exists for: a thing
54
+ created urgent. With it, the base key drops out of `desired` as the urgent one
55
+ appears, so reconciliation cancels it in the same pass and one occurrence stands.
56
+ §3 is satisfied per key, each keeping its own original time.
57
+
58
+ **Only the time depends on the instant, never the key.** `tick.stale_reason`
59
+ asks whether an occurrence's key is still in `desired`, and asks without a
60
+ reconciliation instant. When urgency lived in both, every urgent occurrence was
61
+ cancelled as "no longer implied" moments before delivery — the tick destroying
62
+ the thing urgency is for. The key is a fact about state; `now` only clamps.
63
+
64
+ ## Consequences
65
+
66
+ - Something created urgent surfaces at the instant its acceptance is
67
+ reconciled, and is delivered by the next tick like anything else.
68
+ - `min`, not "set to now": a reminder already due keeps its earlier time.
69
+ - Marking an existing commitment urgent cancels its ordinary reminder and
70
+ schedules an urgent one; the cancelled row still records when it was going to
71
+ surface.
72
+ - **Urgency cannot be taken back.** Un-marking it finds the base key spent and
73
+ schedules nothing. That is the right way round — the owner has already been
74
+ told — but it is a one-way door and is stated here rather than discovered.