memdate 1.0.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.
memdate-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ryan Wesley Hitchcock
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.
memdate-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,210 @@
1
+ Metadata-Version: 2.4
2
+ Name: memdate
3
+ Version: 1.0.0
4
+ Summary: File-based memory system with a hard CAPTURE/DISTILL split: raw records are append-only evidence, distilled files are derived synthesis, and a cross-domain index keeps the connective tissue current
5
+ Author: Ryan Wesley Hitchcock
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/ohpersonperson/frontface
8
+ Keywords: memory,knowledge-management,journaling,capture,distillation,markdown
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.10
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Dynamic: license-file
16
+
17
+ # memdate — file-based memory with a hard CAPTURE/DISTILL split
18
+
19
+ Plain Markdown memory for people or agents. One load-bearing invariant:
20
+
21
+ **CAPTURE preserves. DISTILL interprets. Never mix them.**
22
+
23
+ - **CAPTURE** appends timestamped raw records. No summarizing, no
24
+ synthesis, no editing yesterday's entries. Raw is the permanent record.
25
+ - **DISTILL** consolidates raw into `distilled.md` — and the library
26
+ checksums raw before and after, voiding the run if anything touched it.
27
+ - **INDEX** regenerates the cross-domain connective tissue
28
+ (entities→domains, dependencies, open questions, hot zones) after any
29
+ multi-domain distill. Derived, never authoritative over raw.
30
+
31
+ No prerequisites. You don't need to know anything about the project's
32
+ history. The mechanism is the whole product.
33
+
34
+ *(Lineage: front-facing conversion of the `memdate-v2` skill from Ryan's
35
+ skill-suite, MIT. What changed is documented in CHANGELOG.md — the
36
+ decoupling record.)*
37
+
38
+ ## What it does
39
+
40
+ A memory store is a directory:
41
+
42
+ ```
43
+ <root>/
44
+ ├── INDEX-cross-domain.md ← regenerated after multi-domain distills
45
+ ├── DROP/ ← ingestion: NEW / PROCESSED / PENDING / QUARANTINE
46
+ ├── personal/
47
+ │ ├── raw.md ← append-only evidence log
48
+ │ └── distilled.md ← derived synthesis + provenance frontmatter
49
+ └── work/
50
+ ├── raw.md
51
+ └── distilled.md
52
+ ```
53
+
54
+ The domain list is configuration, not identity. This package ships with
55
+ neutral defaults (`personal`, `work`, `projects`, `reference`, `misc`) —
56
+ define your own via constructor, JSON, or YAML. Storage is a
57
+ `StorageAdapter`: the local filesystem by default, Google Drive as an
58
+ optional seam you can implement (it fails loudly until you do, instead of
59
+ pretending to work).
60
+
61
+ ## Why it works
62
+
63
+ Most memory systems rot because recording and interpreting happen in the
64
+ same motion: you write down what happened *and* what it means, and a
65
+ month later you can't tell which was which. The split is enforced in
66
+ code, not just in prose:
67
+
68
+ - **Capture isolation** — `capture()` only appends; it has no code path
69
+ that edits prior entries (tested: old content is always a strict prefix
70
+ of new content). Entry text is scanned for interpretation markers
71
+ ("in summary", "overall,", … — configurable); a hit raises unless you
72
+ pass `force=True`, and then the bypass is stamped into the entry
73
+ header. The exception is visible, never silent.
74
+ - **Distill isolation** — `distill()` checksums `raw.md` before writing
75
+ `distilled.md` and re-checks after. If raw changed mid-run, the run is
76
+ void. A test adapter that sabotages raw mid-distill proves the check
77
+ fires.
78
+ - **Index discipline** — the four required sections and the 7-step
79
+ regeneration procedure are code. Single-domain distills skip the index
80
+ with a recorded reason. The verify pass asserts no orphan entities and
81
+ no sourceless questions.
82
+ - **Raw wins conflicts** — stated in the docs, enforced by the
83
+ architecture: nothing in the distill path can write to raw.
84
+
85
+ ## Quickstart
86
+
87
+ Zero dependencies beyond Python 3.10+.
88
+
89
+ ```bash
90
+ cd memdate
91
+ PYTHONPATH=src python3 -m unittest discover -s tests # 72 offline tests
92
+ ```
93
+
94
+ ```bash
95
+ export PYTHONPATH=src
96
+ python3 examples/run_local.py --root /tmp/memo init
97
+ python3 examples/run_local.py --root /tmp/memo capture \
98
+ --domain work --text "Acme Corp emailed: contract renews March 1." --source email
99
+ python3 examples/run_local.py --root /tmp/memo capture \
100
+ --domain work --text "In summary, the deal is safe."
101
+ # InterpretationError: Entry looks like interpretation, not capture —
102
+ # markers found: in summary. Rewrite as raw record, or pass force=True
103
+ # to record the bypass explicitly.
104
+ ```
105
+
106
+ Distill from a file holding your consolidated synthesis:
107
+
108
+ ```bash
109
+ python3 examples/run_local.py --root /tmp/memo distill --domain work \
110
+ --file /tmp/work-distilled.md \
111
+ --entities "Acme Corp,Priya" --informs personal \
112
+ --questions "Does the freeze delay the paperwork?"
113
+ python3 examples/run_local.py --root /tmp/memo index --touched work,personal
114
+ ```
115
+
116
+ Or use it as a library:
117
+
118
+ ```python
119
+ from memdate import (
120
+ MemoryConfig, default_adapter, ensure_layout,
121
+ capture, distill, regenerate_index,
122
+ )
123
+
124
+ cfg = MemoryConfig(root="/tmp/memo", domains=("garden", "car"))
125
+ store = default_adapter(cfg.root)
126
+ ensure_layout(cfg, adapter=store)
127
+
128
+ capture(cfg, "garden", "Planted tomatoes, north bed.", adapter=store)
129
+ distill(cfg, "garden", "North bed: tomatoes planted.",
130
+ entities=["north bed"], lifecycle="ITERATIVE", adapter=store)
131
+ ```
132
+
133
+ Drop a file in `DROP/NEW/` with a leading `domain: <name>` line and
134
+ `sweep` routes it — captured to the right domain's raw, or quarantined
135
+ with a reason file if it's unroutable or reads like synthesis:
136
+
137
+ ```bash
138
+ printf 'domain: work\n\nFixed the flaky test.\n' > /tmp/memo/DROP/NEW/note.md
139
+ python3 examples/run_local.py --root /tmp/memo sweep
140
+ ```
141
+
142
+ ## Inputs, outputs
143
+
144
+ - **Input (capture):** domain, text, optional source. Output: timestamped
145
+ entry appended to `<domain>/raw.md`.
146
+ - **Input (distill):** domain, consolidated body, optional entities /
147
+ entity notes / informs / open questions / lifecycle. Output:
148
+ `<domain>/distilled.md` with provenance frontmatter.
149
+ - **Input (index):** list of domains the distill touched. Output:
150
+ `INDEX-cross-domain.md`, or a recorded skip for single-domain distills.
151
+
152
+ ## Distilled body conventions
153
+
154
+ Two line-prefixes the index understands:
155
+
156
+ - `? ` — uncertainty flag → collected into Hot Zones.
157
+ - `! ` — explicit raw↔distilled divergence note → collected into Hot Zones.
158
+
159
+ Entity note contradictions across domains (work says "reliable", personal
160
+ says "misses deadlines") also land in Hot Zones as `[tension]` entries.
161
+
162
+ ## Evidence-taxonomy mapping
163
+
164
+ This package shares a vocabulary with the `adversarial-ifs` evidence
165
+ taxonomy (ten tiers). The mapping is explicit — memdate doesn't tag
166
+ evidence itself, but its artifacts sit at defined tiers:
167
+
168
+ | memdate artifact | IFS tier | Why |
169
+ |---|---|---|
170
+ | `raw.md` entry (logged event) | `OBSERVATION` | Reported/logged, not independently verified |
171
+ | `raw.md` entry (someone's assertion) | `CLAIM` | Explicit assertion by a source |
172
+ | `distilled.md` synthesis | `INFERENCE` | Conclusion drawn from raw evidence |
173
+ | `distilled.md` `? ` flags, index open questions | `UNKNOWN` | Flagged as not presently determinable |
174
+ | distiller-verified fact | `FACT` | Directly established ground truth |
175
+ | provisional model in a distillation | `HYPOTHESIS` | Needs stress-testing before use |
176
+
177
+ No code dependency between the packages — the table is the contract, so
178
+ neither taxonomy can silently fork.
179
+
180
+ ## Worked example
181
+
182
+ `examples/example-1-two-domains.md` — a full CAPTURE → DISTILL → INDEX
183
+ cycle on two neutral domains, with the actual file contents shown at
184
+ each step.
185
+
186
+ *The field is constructed for teaching — it shows the mechanism's shape,
187
+ not real findings.*
188
+
189
+ ## FAQ
190
+
191
+ **Where's Google Drive?** The original skill lived in a Drive folder.
192
+ This package defaults to the local filesystem; `GoogleDriveAdapter`
193
+ documents the seam and raises `DriveNotConfigured` with setup pointers
194
+ until you implement it. Drive is an adapter, not the root.
195
+
196
+ **Can a model do the distilling?** Yes — that's the intended shape. The
197
+ model supplies the consolidated body; the library stamps provenance and
198
+ enforces that raw wasn't touched. The library never calls a model itself.
199
+
200
+ **Does capture need the index?** No. Capture works from the first entry;
201
+ distill works per-domain; the index appears once two domains are
202
+ distilled.
203
+
204
+ **What about the memory interviewer?** The guided-capture interviewer
205
+ (`meminqu`) builds on this library's capture contract — it converts next
206
+ and shares this package's domain config.
207
+
208
+ ## License
209
+
210
+ MIT. See LICENSE.
@@ -0,0 +1,194 @@
1
+ # memdate — file-based memory with a hard CAPTURE/DISTILL split
2
+
3
+ Plain Markdown memory for people or agents. One load-bearing invariant:
4
+
5
+ **CAPTURE preserves. DISTILL interprets. Never mix them.**
6
+
7
+ - **CAPTURE** appends timestamped raw records. No summarizing, no
8
+ synthesis, no editing yesterday's entries. Raw is the permanent record.
9
+ - **DISTILL** consolidates raw into `distilled.md` — and the library
10
+ checksums raw before and after, voiding the run if anything touched it.
11
+ - **INDEX** regenerates the cross-domain connective tissue
12
+ (entities→domains, dependencies, open questions, hot zones) after any
13
+ multi-domain distill. Derived, never authoritative over raw.
14
+
15
+ No prerequisites. You don't need to know anything about the project's
16
+ history. The mechanism is the whole product.
17
+
18
+ *(Lineage: front-facing conversion of the `memdate-v2` skill from Ryan's
19
+ skill-suite, MIT. What changed is documented in CHANGELOG.md — the
20
+ decoupling record.)*
21
+
22
+ ## What it does
23
+
24
+ A memory store is a directory:
25
+
26
+ ```
27
+ <root>/
28
+ ├── INDEX-cross-domain.md ← regenerated after multi-domain distills
29
+ ├── DROP/ ← ingestion: NEW / PROCESSED / PENDING / QUARANTINE
30
+ ├── personal/
31
+ │ ├── raw.md ← append-only evidence log
32
+ │ └── distilled.md ← derived synthesis + provenance frontmatter
33
+ └── work/
34
+ ├── raw.md
35
+ └── distilled.md
36
+ ```
37
+
38
+ The domain list is configuration, not identity. This package ships with
39
+ neutral defaults (`personal`, `work`, `projects`, `reference`, `misc`) —
40
+ define your own via constructor, JSON, or YAML. Storage is a
41
+ `StorageAdapter`: the local filesystem by default, Google Drive as an
42
+ optional seam you can implement (it fails loudly until you do, instead of
43
+ pretending to work).
44
+
45
+ ## Why it works
46
+
47
+ Most memory systems rot because recording and interpreting happen in the
48
+ same motion: you write down what happened *and* what it means, and a
49
+ month later you can't tell which was which. The split is enforced in
50
+ code, not just in prose:
51
+
52
+ - **Capture isolation** — `capture()` only appends; it has no code path
53
+ that edits prior entries (tested: old content is always a strict prefix
54
+ of new content). Entry text is scanned for interpretation markers
55
+ ("in summary", "overall,", … — configurable); a hit raises unless you
56
+ pass `force=True`, and then the bypass is stamped into the entry
57
+ header. The exception is visible, never silent.
58
+ - **Distill isolation** — `distill()` checksums `raw.md` before writing
59
+ `distilled.md` and re-checks after. If raw changed mid-run, the run is
60
+ void. A test adapter that sabotages raw mid-distill proves the check
61
+ fires.
62
+ - **Index discipline** — the four required sections and the 7-step
63
+ regeneration procedure are code. Single-domain distills skip the index
64
+ with a recorded reason. The verify pass asserts no orphan entities and
65
+ no sourceless questions.
66
+ - **Raw wins conflicts** — stated in the docs, enforced by the
67
+ architecture: nothing in the distill path can write to raw.
68
+
69
+ ## Quickstart
70
+
71
+ Zero dependencies beyond Python 3.10+.
72
+
73
+ ```bash
74
+ cd memdate
75
+ PYTHONPATH=src python3 -m unittest discover -s tests # 72 offline tests
76
+ ```
77
+
78
+ ```bash
79
+ export PYTHONPATH=src
80
+ python3 examples/run_local.py --root /tmp/memo init
81
+ python3 examples/run_local.py --root /tmp/memo capture \
82
+ --domain work --text "Acme Corp emailed: contract renews March 1." --source email
83
+ python3 examples/run_local.py --root /tmp/memo capture \
84
+ --domain work --text "In summary, the deal is safe."
85
+ # InterpretationError: Entry looks like interpretation, not capture —
86
+ # markers found: in summary. Rewrite as raw record, or pass force=True
87
+ # to record the bypass explicitly.
88
+ ```
89
+
90
+ Distill from a file holding your consolidated synthesis:
91
+
92
+ ```bash
93
+ python3 examples/run_local.py --root /tmp/memo distill --domain work \
94
+ --file /tmp/work-distilled.md \
95
+ --entities "Acme Corp,Priya" --informs personal \
96
+ --questions "Does the freeze delay the paperwork?"
97
+ python3 examples/run_local.py --root /tmp/memo index --touched work,personal
98
+ ```
99
+
100
+ Or use it as a library:
101
+
102
+ ```python
103
+ from memdate import (
104
+ MemoryConfig, default_adapter, ensure_layout,
105
+ capture, distill, regenerate_index,
106
+ )
107
+
108
+ cfg = MemoryConfig(root="/tmp/memo", domains=("garden", "car"))
109
+ store = default_adapter(cfg.root)
110
+ ensure_layout(cfg, adapter=store)
111
+
112
+ capture(cfg, "garden", "Planted tomatoes, north bed.", adapter=store)
113
+ distill(cfg, "garden", "North bed: tomatoes planted.",
114
+ entities=["north bed"], lifecycle="ITERATIVE", adapter=store)
115
+ ```
116
+
117
+ Drop a file in `DROP/NEW/` with a leading `domain: <name>` line and
118
+ `sweep` routes it — captured to the right domain's raw, or quarantined
119
+ with a reason file if it's unroutable or reads like synthesis:
120
+
121
+ ```bash
122
+ printf 'domain: work\n\nFixed the flaky test.\n' > /tmp/memo/DROP/NEW/note.md
123
+ python3 examples/run_local.py --root /tmp/memo sweep
124
+ ```
125
+
126
+ ## Inputs, outputs
127
+
128
+ - **Input (capture):** domain, text, optional source. Output: timestamped
129
+ entry appended to `<domain>/raw.md`.
130
+ - **Input (distill):** domain, consolidated body, optional entities /
131
+ entity notes / informs / open questions / lifecycle. Output:
132
+ `<domain>/distilled.md` with provenance frontmatter.
133
+ - **Input (index):** list of domains the distill touched. Output:
134
+ `INDEX-cross-domain.md`, or a recorded skip for single-domain distills.
135
+
136
+ ## Distilled body conventions
137
+
138
+ Two line-prefixes the index understands:
139
+
140
+ - `? ` — uncertainty flag → collected into Hot Zones.
141
+ - `! ` — explicit raw↔distilled divergence note → collected into Hot Zones.
142
+
143
+ Entity note contradictions across domains (work says "reliable", personal
144
+ says "misses deadlines") also land in Hot Zones as `[tension]` entries.
145
+
146
+ ## Evidence-taxonomy mapping
147
+
148
+ This package shares a vocabulary with the `adversarial-ifs` evidence
149
+ taxonomy (ten tiers). The mapping is explicit — memdate doesn't tag
150
+ evidence itself, but its artifacts sit at defined tiers:
151
+
152
+ | memdate artifact | IFS tier | Why |
153
+ |---|---|---|
154
+ | `raw.md` entry (logged event) | `OBSERVATION` | Reported/logged, not independently verified |
155
+ | `raw.md` entry (someone's assertion) | `CLAIM` | Explicit assertion by a source |
156
+ | `distilled.md` synthesis | `INFERENCE` | Conclusion drawn from raw evidence |
157
+ | `distilled.md` `? ` flags, index open questions | `UNKNOWN` | Flagged as not presently determinable |
158
+ | distiller-verified fact | `FACT` | Directly established ground truth |
159
+ | provisional model in a distillation | `HYPOTHESIS` | Needs stress-testing before use |
160
+
161
+ No code dependency between the packages — the table is the contract, so
162
+ neither taxonomy can silently fork.
163
+
164
+ ## Worked example
165
+
166
+ `examples/example-1-two-domains.md` — a full CAPTURE → DISTILL → INDEX
167
+ cycle on two neutral domains, with the actual file contents shown at
168
+ each step.
169
+
170
+ *The field is constructed for teaching — it shows the mechanism's shape,
171
+ not real findings.*
172
+
173
+ ## FAQ
174
+
175
+ **Where's Google Drive?** The original skill lived in a Drive folder.
176
+ This package defaults to the local filesystem; `GoogleDriveAdapter`
177
+ documents the seam and raises `DriveNotConfigured` with setup pointers
178
+ until you implement it. Drive is an adapter, not the root.
179
+
180
+ **Can a model do the distilling?** Yes — that's the intended shape. The
181
+ model supplies the consolidated body; the library stamps provenance and
182
+ enforces that raw wasn't touched. The library never calls a model itself.
183
+
184
+ **Does capture need the index?** No. Capture works from the first entry;
185
+ distill works per-domain; the index appears once two domains are
186
+ distilled.
187
+
188
+ **What about the memory interviewer?** The guided-capture interviewer
189
+ (`meminqu`) builds on this library's capture contract — it converts next
190
+ and shares this package's domain config.
191
+
192
+ ## License
193
+
194
+ MIT. See LICENSE.
@@ -0,0 +1,27 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "memdate"
7
+ version = "1.0.0"
8
+ description = "File-based memory system with a hard CAPTURE/DISTILL split: raw records are append-only evidence, distilled files are derived synthesis, and a cross-domain index keeps the connective tissue current"
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ requires-python = ">=3.10"
12
+ authors = [{ name = "Ryan Wesley Hitchcock" }]
13
+ keywords = ["memory", "knowledge-management", "journaling", "capture", "distillation", "markdown"]
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Operating System :: OS Independent",
18
+ ]
19
+
20
+ [project.urls]
21
+ Homepage = "https://github.com/ohpersonperson/frontface"
22
+
23
+ [tool.setuptools.packages.find]
24
+ where = ["src"]
25
+
26
+ [tool.pytest.ini_options]
27
+ testpaths = ["tests"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,78 @@
1
+ """memdate — file-based memory with a hard CAPTURE/DISTILL split.
2
+
3
+ CAPTURE preserves raw records (append-only, timestamped, never
4
+ interpreted). DISTILL consolidates raw into derived distilled.md files
5
+ (never touching raw). INDEX regenerates the cross-domain connective
6
+ tissue after multi-domain distills.
7
+
8
+ Front-facing conversion of the ``memdate-v2`` skill (Ryan's
9
+ skill-suite, MIT). The original skill's Ryan-specific domains and Google
10
+ Drive root are configuration here, not defaults: domains come from
11
+ ``MemoryConfig`` (neutral defaults shipped), storage is a
12
+ ``StorageAdapter`` (local filesystem default, Drive an optional seam).
13
+ """
14
+
15
+ from .adapters import (
16
+ DriveNotConfigured,
17
+ GoogleDriveAdapter,
18
+ LocalFilesystemAdapter,
19
+ StorageAdapter,
20
+ StorageError,
21
+ default_adapter,
22
+ )
23
+ from .capture import (
24
+ CaptureError,
25
+ CaptureResult,
26
+ InterpretationError,
27
+ capture,
28
+ read_raw,
29
+ route,
30
+ scan_interpretation,
31
+ )
32
+ from .config import (
33
+ DEFAULT_DOMAINS,
34
+ ConfigError,
35
+ MemoryConfig,
36
+ )
37
+ from .distill import (
38
+ DistillError,
39
+ DistillResult,
40
+ distill,
41
+ read_distilled,
42
+ )
43
+ from .drop import SweepItem, ensure_layout, sweep_new
44
+ from .frontmatter import FrontmatterError
45
+ from .index import IndexError, IndexResult, read_index, regenerate_index
46
+
47
+ __all__ = [
48
+ "MemoryConfig",
49
+ "ConfigError",
50
+ "DEFAULT_DOMAINS",
51
+ "StorageAdapter",
52
+ "StorageError",
53
+ "LocalFilesystemAdapter",
54
+ "GoogleDriveAdapter",
55
+ "DriveNotConfigured",
56
+ "default_adapter",
57
+ "CaptureError",
58
+ "CaptureResult",
59
+ "InterpretationError",
60
+ "capture",
61
+ "read_raw",
62
+ "route",
63
+ "scan_interpretation",
64
+ "DistillError",
65
+ "DistillResult",
66
+ "distill",
67
+ "read_distilled",
68
+ "SweepItem",
69
+ "ensure_layout",
70
+ "sweep_new",
71
+ "FrontmatterError",
72
+ "IndexError",
73
+ "IndexResult",
74
+ "read_index",
75
+ "regenerate_index",
76
+ ]
77
+
78
+ __version__ = "1.0.0"