alter-runtime 0.3.1__tar.gz → 0.3.3__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 (102) hide show
  1. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/.gitignore +1 -1
  2. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/PKG-INFO +13 -19
  3. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/README.md +12 -18
  4. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/__init__.py +2 -3
  5. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/adapters/__init__.py +2 -1
  6. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/adapters/claude_jsonl_watcher.py +8 -9
  7. alter_runtime-0.3.3/alter_runtime/adapters/household/__init__.py +26 -0
  8. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/adapters/household/_base.py +13 -15
  9. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/adapters/household/compost/__init__.py +0 -2
  10. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/adapters/household/compost/adapter.py +4 -4
  11. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/adapters/household/compost/storage.py +1 -1
  12. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/adapters/household/compost/tests/test_adapter.py +3 -3
  13. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/adapters/household/compost/tests/test_storage.py +1 -1
  14. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/adapters/household/compost/tests/test_traits.py +1 -1
  15. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/adapters/household/compost/traits.py +3 -4
  16. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/adapters/household/self_hoster/__init__.py +2 -2
  17. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/adapters/household/self_hoster/adapter.py +14 -14
  18. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/adapters/household/self_hoster/storage.py +1 -1
  19. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/adapters/household/self_hoster/tests/test_adapter.py +2 -2
  20. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/adapters/household/self_hoster/tests/test_storage.py +1 -1
  21. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/adapters/household/self_hoster/tests/test_traits.py +1 -1
  22. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/adapters/household/self_hoster/traits.py +5 -6
  23. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/adapters/household/tapo_ecosystem/__init__.py +2 -2
  24. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/adapters/household/tapo_ecosystem/adapter.py +4 -4
  25. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/adapters/household/tapo_ecosystem/storage.py +2 -2
  26. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/adapters/household/tapo_ecosystem/tests/test_adapter.py +2 -2
  27. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/adapters/household/tapo_ecosystem/tests/test_storage.py +1 -1
  28. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/adapters/household/tapo_ecosystem/tests/test_traits.py +1 -1
  29. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/adapters/household/tapo_ecosystem/traits.py +5 -6
  30. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/adapters/household/workshop_tools/__init__.py +2 -3
  31. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/adapters/household/workshop_tools/adapter.py +3 -3
  32. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/adapters/household/workshop_tools/storage.py +3 -3
  33. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/adapters/household/workshop_tools/tests/test_adapter.py +2 -2
  34. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/adapters/household/workshop_tools/tests/test_storage.py +1 -1
  35. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/adapters/household/workshop_tools/tests/test_traits.py +1 -1
  36. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/adapters/household/workshop_tools/traits.py +4 -5
  37. alter_runtime-0.3.3/alter_runtime/adapters/weave_watcher.py +521 -0
  38. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/adapters/worktree_watcher.py +15 -15
  39. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/atlas/__init__.py +1 -4
  40. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/atlas/base.py +1 -1
  41. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/atlas/ledger.py +2 -2
  42. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/atlas/observations.py +2 -2
  43. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/atlas/schema.py +3 -3
  44. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/cap_cache.py +3 -4
  45. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/cli.py +30 -19
  46. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/clients/token_usage_client.py +14 -19
  47. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/config.py +401 -37
  48. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/consent.py +5 -6
  49. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/daemon.py +94 -80
  50. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/floor_loop.py +20 -20
  51. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/floor_preflight.py +57 -74
  52. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/http_auth.py +65 -16
  53. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/notifiers/__init__.py +2 -2
  54. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/notifiers/desktop.py +2 -2
  55. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/sdk/__init__.py +3 -3
  56. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/sdk/client.py +13 -17
  57. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/service_install.py +12 -13
  58. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/services/__init__.py +2 -3
  59. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/services/launchd/com.alter.runtime.plist.in +9 -13
  60. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/services/systemd/alter-runtime.service.in +16 -8
  61. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/services/systemd/cf-access-env.conf.in +5 -10
  62. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/sockets/__init__.py +2 -2
  63. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/sockets/unix.py +8 -8
  64. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/subscribers/__init__.py +8 -4
  65. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/subscribers/active_sessions_cron_emitter.py +7 -7
  66. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/subscribers/active_sessions_do_publisher.py +206 -44
  67. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/subscribers/active_sessions_gc.py +1 -1
  68. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/subscribers/active_sessions_writer.py +1 -1
  69. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/subscribers/adapters_writer.py +1 -1
  70. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/subscribers/agent_frames.py +49 -24
  71. alter_runtime-0.3.3/alter_runtime/subscribers/attunement_refresher.py +356 -0
  72. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/subscribers/bus.py +3 -3
  73. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/subscribers/cache_writer.py +13 -16
  74. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/subscribers/ceremony_echo.py +8 -10
  75. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/subscribers/do_sse.py +21 -13
  76. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/subscribers/ebpf.py +5 -6
  77. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/subscribers/inbox_writer.py +8 -9
  78. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/subscribers/mcp_fallback.py +66 -18
  79. alter_runtime-0.3.3/alter_runtime/subscribers/presence_feed_writer.py +343 -0
  80. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/subscribers/presence_writer.py +1 -1
  81. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/subscribers/session_presence.py +28 -21
  82. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/subscribers/weave_intent_writer.py +22 -22
  83. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/update_loop.py +40 -50
  84. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/weave/__init__.py +3 -3
  85. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/weave/resolver.py +22 -22
  86. alter_runtime-0.3.3/alter_runtime/weave_log.py +437 -0
  87. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/pyproject.toml +15 -8
  88. alter_runtime-0.3.1/alter_runtime/adapters/household/__init__.py +0 -29
  89. alter_runtime-0.3.1/docs/schemas/README.md +0 -46
  90. alter_runtime-0.3.1/examples/awesomewm/README.md +0 -122
  91. alter_runtime-0.3.1/examples/waybar/README.md +0 -66
  92. alter_runtime-0.3.1/pypi-truealter/README.md +0 -46
  93. alter_runtime-0.3.1/pypi-truealter/pyproject.toml +0 -67
  94. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/LICENSE +0 -0
  95. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/adapters/git_watcher.py +0 -0
  96. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/adapters/household/compost/tests/__init__.py +0 -0
  97. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/adapters/household/self_hoster/tests/__init__.py +0 -0
  98. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/adapters/household/tapo_ecosystem/tests/__init__.py +0 -0
  99. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/adapters/household/workshop_tools/tests/__init__.py +0 -0
  100. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/clients/__init__.py +0 -0
  101. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/sockets/dbus.py +0 -0
  102. {alter_runtime-0.3.1 → alter_runtime-0.3.3}/alter_runtime/subscribers/sse.py +0 -0
@@ -22,7 +22,7 @@ htmlcov/
22
22
  .vscode/
23
23
  .idea/
24
24
 
25
- # Wave 2+ generated artefacts (local daemon state never in the repo)
25
+ # Generated daemon artefacts (local daemon state, never in the repo)
26
26
  *.sock
27
27
  keypair.json
28
28
  runtime.yaml
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: alter-runtime
3
- Version: 0.3.1
3
+ Version: 0.3.3
4
4
  Summary: ~Alter Identity Runtime - local sovereign daemon for the continuous identity field. Subscribes to per-~handle Cloudflare Durable Objects, exposes Unix socket + D-Bus + CLI surfaces, falls back gracefully to direct MCP polling.
5
5
  Project-URL: Homepage, https://truealter.com
6
6
  Project-URL: Documentation, https://truealter.com/docs/alter-runtime
@@ -110,16 +110,10 @@ filing a public issue. Non-security bugs and feature requests: email
110
110
 
111
111
  ## Status
112
112
 
113
- | Wave | Stream | Status |
114
- |------|--------|--------|
115
- | 1 | 1c - Daemon skeleton + `AlterClient` SDK | Shipped |
116
- | 2 | 2b - Subscribers, Unix socket, D-Bus, git watcher | Shipped |
117
- | 2 | 2c - systemd + launchd service units | Shipped |
118
- | 2 | 2d - First pixel: CC hook + scripts upgrade | Shipped |
119
- | 2 | 2e - eBPF subscriber (Patent M, reference impl in `alter-ebpf`) | Shipped |
120
- | 3 | 3a - Cross-platform tray surfaces | Planned |
121
- | 3 | 3b - Windows Service + `pam_alter` stub | Planned |
122
- | 3 | 3c - PyPI release CI + signed binaries | Planned |
113
+ Shipped: the daemon and `AlterClient` SDK; subscribers, the Unix socket,
114
+ D-Bus, and the git watcher; systemd and launchd service units; the CC hook
115
+ and scripts integration; and the eBPF subscriber (reference implementation
116
+ in the `alter-ebpf` package).
123
117
 
124
118
  ## Install
125
119
 
@@ -179,11 +173,11 @@ alter-runtime stop
179
173
  - **HTTP/SSE** loopback at `http://127.0.0.1:<port>/events` - used by the CC hook
180
174
  and shell scripts
181
175
 
182
- 4. **Collects ambient signals** via adapters (Wave 2):
176
+ 4. **Collects ambient signals** via adapters:
183
177
  - Git commits, branch switches, pushes (via `watchdog` on `.git/refs/heads/`)
184
- - CC hook events (forwarded from `.claude/hooks/*.sh`)
178
+ - Editor session hook events (forwarded from local shell hooks)
185
179
  - Shell command invocations (opt-in)
186
- - eBPF kernel attestations (shipped; reference impl in `alter-ebpf`, Patent M)
180
+ - eBPF kernel attestations (shipped; reference impl in `alter-ebpf`)
187
181
 
188
182
  5. **Maintains a local cache** of your last-known-good field state so the first-paint
189
183
  tilde warmth renders in <1 second, even offline. Per IFA's Five OS-Native Properties,
@@ -203,16 +197,16 @@ alter-runtime/
203
197
  │ ├── cli.py # argparse entrypoint: init|start|stop|status|query|ingest|daemon
204
198
  │ ├── sdk/
205
199
  │ │ ├── __init__.py
206
- │ │ └── client.py # AlterClient (lifted from backend/openclaw-skill)
207
- │ ├── subscribers/ # (Wave 2)
200
+ │ │ └── client.py # AlterClient (async identity MCP client)
201
+ │ ├── subscribers/
208
202
  │ │ ├── do_sse.py # primary - subscribes to handle-alter DO SSE
209
203
  │ │ └── mcp_fallback.py # fallback - polls api.truealter.com/api/v1/mcp
210
- │ ├── sockets/ # (Wave 2)
204
+ │ ├── sockets/
211
205
  │ │ ├── unix.py # /run/user/$UID/alter.sock
212
206
  │ │ └── dbus.py # org.alter.Identity1
213
- │ ├── adapters/ # (Wave 2)
207
+ │ ├── adapters/
214
208
  │ │ └── git_watcher.py # watchdog on .git/refs/heads/
215
- │ └── services/ # (Wave 2)
209
+ │ └── services/
216
210
  │ ├── systemd/alter-runtime.service
217
211
  │ ├── launchd/com.alter.runtime.plist
218
212
  │ └── windows/AlterRuntimeService.py
@@ -42,16 +42,10 @@ filing a public issue. Non-security bugs and feature requests: email
42
42
 
43
43
  ## Status
44
44
 
45
- | Wave | Stream | Status |
46
- |------|--------|--------|
47
- | 1 | 1c - Daemon skeleton + `AlterClient` SDK | Shipped |
48
- | 2 | 2b - Subscribers, Unix socket, D-Bus, git watcher | Shipped |
49
- | 2 | 2c - systemd + launchd service units | Shipped |
50
- | 2 | 2d - First pixel: CC hook + scripts upgrade | Shipped |
51
- | 2 | 2e - eBPF subscriber (Patent M, reference impl in `alter-ebpf`) | Shipped |
52
- | 3 | 3a - Cross-platform tray surfaces | Planned |
53
- | 3 | 3b - Windows Service + `pam_alter` stub | Planned |
54
- | 3 | 3c - PyPI release CI + signed binaries | Planned |
45
+ Shipped: the daemon and `AlterClient` SDK; subscribers, the Unix socket,
46
+ D-Bus, and the git watcher; systemd and launchd service units; the CC hook
47
+ and scripts integration; and the eBPF subscriber (reference implementation
48
+ in the `alter-ebpf` package).
55
49
 
56
50
  ## Install
57
51
 
@@ -111,11 +105,11 @@ alter-runtime stop
111
105
  - **HTTP/SSE** loopback at `http://127.0.0.1:<port>/events` - used by the CC hook
112
106
  and shell scripts
113
107
 
114
- 4. **Collects ambient signals** via adapters (Wave 2):
108
+ 4. **Collects ambient signals** via adapters:
115
109
  - Git commits, branch switches, pushes (via `watchdog` on `.git/refs/heads/`)
116
- - CC hook events (forwarded from `.claude/hooks/*.sh`)
110
+ - Editor session hook events (forwarded from local shell hooks)
117
111
  - Shell command invocations (opt-in)
118
- - eBPF kernel attestations (shipped; reference impl in `alter-ebpf`, Patent M)
112
+ - eBPF kernel attestations (shipped; reference impl in `alter-ebpf`)
119
113
 
120
114
  5. **Maintains a local cache** of your last-known-good field state so the first-paint
121
115
  tilde warmth renders in <1 second, even offline. Per IFA's Five OS-Native Properties,
@@ -135,16 +129,16 @@ alter-runtime/
135
129
  │ ├── cli.py # argparse entrypoint: init|start|stop|status|query|ingest|daemon
136
130
  │ ├── sdk/
137
131
  │ │ ├── __init__.py
138
- │ │ └── client.py # AlterClient (lifted from backend/openclaw-skill)
139
- │ ├── subscribers/ # (Wave 2)
132
+ │ │ └── client.py # AlterClient (async identity MCP client)
133
+ │ ├── subscribers/
140
134
  │ │ ├── do_sse.py # primary - subscribes to handle-alter DO SSE
141
135
  │ │ └── mcp_fallback.py # fallback - polls api.truealter.com/api/v1/mcp
142
- │ ├── sockets/ # (Wave 2)
136
+ │ ├── sockets/
143
137
  │ │ ├── unix.py # /run/user/$UID/alter.sock
144
138
  │ │ └── dbus.py # org.alter.Identity1
145
- │ ├── adapters/ # (Wave 2)
139
+ │ ├── adapters/
146
140
  │ │ └── git_watcher.py # watchdog on .git/refs/heads/
147
- │ └── services/ # (Wave 2)
141
+ │ └── services/
148
142
  │ ├── systemd/alter-runtime.service
149
143
  │ ├── launchd/com.alter.runtime.plist
150
144
  │ └── windows/AlterRuntimeService.py
@@ -1,11 +1,10 @@
1
1
  """~Alter Identity Runtime.
2
2
 
3
- L3 of the six-layer identity distribution surface. See the package README and
4
- the Strategic Decisions Register entries D-RT1 through D-RT10 (8 Apr 2026) for
3
+ L3 of the six-layer identity distribution surface. See the package README for
5
4
  architectural context.
6
5
  """
7
6
 
8
7
  from alter_runtime.sdk.client import AlterClient
9
8
 
10
- __version__ = "0.3.1"
9
+ __version__ = "0.3.3"
11
10
  __all__ = ["AlterClient", "__version__"]
@@ -14,6 +14,7 @@ runtime component.
14
14
 
15
15
  from alter_runtime.adapters.claude_jsonl_watcher import ClaudeJsonlWatcher
16
16
  from alter_runtime.adapters.git_watcher import GitWatcher
17
+ from alter_runtime.adapters.weave_watcher import WeaveWatcher
17
18
  from alter_runtime.adapters.worktree_watcher import WorktreeWatcher
18
19
 
19
- __all__ = ["ClaudeJsonlWatcher", "GitWatcher", "WorktreeWatcher"]
20
+ __all__ = ["ClaudeJsonlWatcher", "GitWatcher", "WeaveWatcher", "WorktreeWatcher"]
@@ -1,6 +1,7 @@
1
1
  """ClaudeJsonlWatcher - ambient token-usage adapter for Claude Code transcripts.
2
2
 
3
- Watches ``~/.claude/projects/<slug>/*.jsonl`` files with ``watchdog``.
3
+ Watches the per-project ``*.jsonl`` transcript files under Claude Code's
4
+ home projects directory with ``watchdog``.
4
5
  On modification, reads new lines from the persisted byte offset, parses each
5
6
  via the privacy-hard-stop ``parse_assistant_line`` function, and POSTs
6
7
  batches to the ALTER backend token-usage audit endpoint.
@@ -39,8 +40,8 @@ On file rotation (on-disk size < persisted offset), the offset is reset to 0.
39
40
  Backfill
40
41
  --------
41
42
 
42
- On first run, every existing ``*.jsonl`` under ``~/.claude/projects/`` is
43
- walked and assistant events within the last 30 days are emitted in batches of
43
+ On first run, every existing ``*.jsonl`` under the Claude Code projects
44
+ directory is walked and assistant events within the last 30 days are emitted in batches of
44
45
  at most 500. A 5-second sleep is inserted between batches to avoid flooding
45
46
  the backend.
46
47
 
@@ -60,15 +61,13 @@ is ``True`` (default ``False``).
60
61
  Decisions
61
62
  ---------
62
63
 
63
- - D-IaI-1 (Identity-as-Inference): token usage is a passive aggregate with
64
+ - Identity-as-Inference: token usage is a passive aggregate with
64
65
  k=1 (single principal machine). The inferred signal (burn-rate by model)
65
66
  is operational telemetry, NOT psychometric inference - no clause of the
66
67
  IaI 5-point test is triggered. Return: operator visibility.
67
68
  - HTTP auth: JWT sourced from ``load_session()`` at adapter start, then
68
69
  refreshed per-POST from ``ALTER_RUNTIME_SESSION_JWT`` env var as a
69
- fallback. Full auth substrate wiring (ensureFreshSession equivalent) is a
70
- Wave 2 follow-up - see ``TOKEN_USAGE_AUTH_GAP`` docstring on
71
- ``TokenUsageClient``.
70
+ fallback. See ``TokenUsageClient`` for details.
72
71
  """
73
72
 
74
73
  from __future__ import annotations
@@ -227,7 +226,7 @@ class ClaudeJsonlWatcher(Component):
227
226
  Shared :class:`EventBus` (not published to - present for Component
228
227
  symmetry and future local-signal emission).
229
228
  projects_dir:
230
- Override ``~/.claude/projects/`` for testing.
229
+ Override the Claude Code projects directory for testing.
231
230
  """
232
231
 
233
232
  name = "claude_jsonl_watcher"
@@ -502,7 +501,7 @@ def _read_events_from_file(
502
501
  def _slug_from_path(jsonl_path: Path, projects_dir: Path) -> str:
503
502
  """Derive the project slug from the JSONL file path.
504
503
 
505
- ``~/.claude/projects/<slug>/<session>.jsonl`` → ``<slug>``
504
+ ``<projects-dir>/<slug>/<session>.jsonl`` → ``<slug>``
506
505
  """
507
506
  try:
508
507
  relative = jsonl_path.relative_to(projects_dir)
@@ -0,0 +1,26 @@
1
+ """Household substrate adapters.
2
+
3
+ Adapters for physical-substrate data sources on a home LAN:
4
+
5
+ * :mod:`workshop_tools` (Eco-12): per-plug Wh fingerprint via MQTT.
6
+ * :mod:`compost` (Eco-9): pile-temp curve via MQTT, plate-tag filtered.
7
+ * :mod:`tapo_ecosystem` (Eco-14): hub-level multi-parameter composition.
8
+ * :mod:`self_hoster` (Eco-15): own-host systemd + filesystem polling.
9
+
10
+ All four apply the IaI 5-clause overlay; each ships a hard-coded
11
+ Clause-4 banlist refused at trait-emit time. Bands only: raw Wh, temp,
12
+ and event counts never cross the daemon boundary upward.
13
+
14
+ NOT auto-wired into :mod:`alter_runtime.daemon` startup; activation is a
15
+ follow-up once these adapters land their implementation pass.
16
+ """
17
+
18
+ from alter_runtime.adapters.household._base import (
19
+ EventBusSubscriberBase,
20
+ PassiveLanPollerBase,
21
+ )
22
+
23
+ __all__ = [
24
+ "EventBusSubscriberBase",
25
+ "PassiveLanPollerBase",
26
+ ]
@@ -1,25 +1,23 @@
1
1
  """Shared base classes for the household adapter family.
2
2
 
3
- Two adapter shapes per the Phase-2 cross-pattern catalogue
4
- (ALTER internal Phase-2 cross-pattern catalogue):
3
+ Two adapter shapes are shared across the household adapter family:
5
4
 
6
- * :class:`EventBusSubscriberBase` subscribes to the in-process
5
+ * :class:`EventBusSubscriberBase`: subscribes to the in-process
7
6
  :class:`~alter_runtime.subscribers.bus.EventBus` for events the
8
7
  household-bridge subscriber (or a future HA/MQTT shim) publishes from
9
8
  the maker's LAN. Used by ``workshop_tools``, ``compost``, and
10
9
  ``tapo_ecosystem``.
11
- * :class:`PassiveLanPollerBase` polls a local resource (own host,
10
+ * :class:`PassiveLanPollerBase`: polls a local resource (own host,
12
11
  paired LAN device) on a fixed cadence. Used by ``self_hoster``
13
12
  (degenerate-LAN: the daemon polls its own host).
14
13
 
15
14
  Both extend :class:`~alter_runtime.daemon.Component` so the supervisor
16
15
  can run them as long-lived tasks once wired in. They deliberately stop
17
- short of imposing topic taxonomy, payload shape, or storage choice
18
- those are per-adapter concerns.
16
+ short of imposing topic taxonomy, payload shape, or storage choice.
17
+ Those are per-adapter concerns.
19
18
 
20
- D-PROV-1 (RATIFIED 2026-05-13) governs all consumers; IaI 5-clause map
21
- sits in the per-adapter ``traits.py`` modules so the banlist is
22
- co-located with the trait emit site that enforces it.
19
+ The IaI 5-clause map sits in the per-adapter ``traits.py`` modules so
20
+ the banlist is co-located with the trait emit site that enforces it.
23
21
  """
24
22
 
25
23
  from __future__ import annotations
@@ -61,7 +59,7 @@ class EventBusSubscriberBase(Component):
61
59
 
62
60
  async def run(self) -> None:
63
61
  if not self.subscribe_topics:
64
- self._logger.info("%s no subscribe_topics declared idle", self.name)
62
+ self._logger.info("%s no subscribe_topics declared, idle", self.name)
65
63
  await self._stop_event.wait()
66
64
  return
67
65
 
@@ -82,7 +80,7 @@ class EventBusSubscriberBase(Component):
82
80
  async def _dispatch(self, payload: Any) -> None:
83
81
  try:
84
82
  await self.handle_event(payload)
85
- except Exception as exc: # pragma: no cover defensive
83
+ except Exception as exc: # pragma: no cover - defensive
86
84
  self._logger.warning("%s handle_event raised: %s", self.name, exc)
87
85
 
88
86
  @abstractmethod
@@ -97,13 +95,13 @@ class PassiveLanPollerBase(Component):
97
95
  :meth:`poll_once`. The base supervises the poll loop, sleeps
98
96
  between iterations, and exits cleanly on shutdown.
99
97
 
100
- ``self_hoster`` (Eco-15) is the canonical degenerate-LAN consumer
101
- the daemon polls its own host's systemd D-Bus + filesystem; no
98
+ ``self_hoster`` (Eco-15) is the canonical degenerate-LAN consumer.
99
+ The daemon polls its own host's systemd D-Bus + filesystem; no
102
100
  network calls cross the loopback interface.
103
101
  """
104
102
 
105
103
  name: str = "household_passive_lan_poller"
106
- #: Default 6 hours per the self-hoster stub spec.
104
+ #: Default poll interval (6 hours).
107
105
  poll_interval_seconds: float = 6 * 60 * 60
108
106
 
109
107
  def __init__(self, config: DaemonConfig) -> None:
@@ -119,7 +117,7 @@ class PassiveLanPollerBase(Component):
119
117
  while not self._stop_event.is_set():
120
118
  try:
121
119
  await self.poll_once()
122
- except Exception as exc: # pragma: no cover defensive
120
+ except Exception as exc: # pragma: no cover - defensive
123
121
  self._logger.warning("%s poll_once raised: %s", self.name, exc)
124
122
  try:
125
123
  await asyncio.wait_for(
@@ -4,8 +4,6 @@ Subscribes to MQTT topic glob ``tapo/temp_humid/+/state``, filters to
4
4
  devices whose member-attested plate tag is ``"compost"``, and computes
5
5
  a 30-day temperature-curve patience trait (band). Persists to
6
6
  ``~/.local/share/alter-runtime/compost.db`` (mode 600).
7
-
8
- Spec: ALTER internal - compost substrate adapter.
9
7
  """
10
8
 
11
9
  from alter_runtime.adapters.household.compost.adapter import CompostAdapter
@@ -1,9 +1,9 @@
1
- """Compost adapter (Eco-9) EventBusSubscriber over MQTT, plate-tag filtered.
1
+ """Compost adapter (Eco-9) ; EventBusSubscriber over MQTT, plate-tag filtered.
2
2
 
3
3
  Subscribes to ``tapo/temp_humid/+/state`` events on the in-process bus
4
4
  (republished by the household-bridge from Home-Assistant / Mosquitto).
5
5
  A device only participates in compost-trait derivation if its member-
6
- attested plate tag equals ``"compost"`` declared at pairing time and
6
+ attested plate tag equals ``"compost"`` ; declared at pairing time and
7
7
  read from the payload (``plate_tag`` / ``tag`` field).
8
8
 
9
9
  Per spec §5 raw temperature traces and pile location never leave the
@@ -58,7 +58,7 @@ class CompostAdapter(EventBusSubscriberBase):
58
58
  return
59
59
  plate_tag = (payload.get("plate_tag") or payload.get("tag") or "").lower()
60
60
  if plate_tag != COMPOST_PLATE_TAG:
61
- # Spec §5 only compost-tagged sensors feed the trait.
61
+ # Spec §5 ; only compost-tagged sensors feed the trait.
62
62
  return
63
63
  device_id = payload.get("device_id") or payload.get("mac")
64
64
  temp_c = payload.get("temp_c") or payload.get("temperature_c")
@@ -75,7 +75,7 @@ class CompostAdapter(EventBusSubscriberBase):
75
75
  # TODO(eco-9): derive `soil_cycle_patience_band` from rolling 365-day
76
76
  # cycle-completion ledger; gate on ≥0.6 sensor uptime over 90 days.
77
77
  # TODO(eco-9): jitter timestamps to ±15 min before any upward emit
78
- # (spec §3 sub-hour granularity discarded).
78
+ # (spec §3 ; sub-hour granularity discarded).
79
79
  # TODO(eco-9): emit trait band via self._traits once a cycle window
80
80
  # closes; current scaffold persists samples and stops.
81
81
  await self._storage.record_sample(device_id=str(device_id), temp_c=float(temp_c), ts=ts)
@@ -1,4 +1,4 @@
1
- """Compost storage layer local SQLite, mode 600."""
1
+ """Compost storage layer ; local SQLite, mode 600."""
2
2
 
3
3
  from __future__ import annotations
4
4
 
@@ -1,4 +1,4 @@
1
- """Tests for CompostAdapter plate-tag filter + sample persistence."""
1
+ """Tests for CompostAdapter ; plate-tag filter + sample persistence."""
2
2
 
3
3
  from __future__ import annotations
4
4
 
@@ -53,10 +53,10 @@ async def test_adapter_drops_non_compost_tagged_event(
53
53
  )
54
54
  bus = EventBus()
55
55
  adapter = CompostAdapter(config=DaemonConfig(), bus=bus)
56
- # Tagged "bedroom" must NOT persist, must NOT raise.
56
+ # Tagged "bedroom" ; must NOT persist, must NOT raise.
57
57
  await adapter.handle_event(
58
58
  {"device_id": "AA:BB:CC:DD:EE:22", "plate_tag": "bedroom", "temp_c": 20.0}
59
59
  )
60
- # No tag at all also dropped.
60
+ # No tag at all ; also dropped.
61
61
  await adapter.handle_event({"device_id": "X", "temp_c": 20.0})
62
62
  assert await CompostStorage(db_path=db_path).sample_count() == 0
@@ -1,4 +1,4 @@
1
- """Tests for compost SQLite storage mode 600 + sample insert."""
1
+ """Tests for compost SQLite storage ; mode 600 + sample insert."""
2
2
 
3
3
  from __future__ import annotations
4
4
 
@@ -1,4 +1,4 @@
1
- """Tests for compost trait emitter Clause-4 banlist + band shape."""
1
+ """Tests for compost trait emitter ; Clause-4 banlist + band shape."""
2
2
 
3
3
  from __future__ import annotations
4
4
 
@@ -57,10 +57,9 @@ class CompostTraits:
57
57
 
58
58
  def emit(self, kind: str, band: str) -> CompostTraitEmission:
59
59
  self._refuse_if_banned(kind)
60
- # TODO(clause-3-identity-income): return/x402 hook attaches here per
61
- # D-PROV-1 + clause-3 ratification. Each emission upward is the
62
- # point at which Identity-Income return-flow is metered. Hook
63
- # implementation lives outside this scaffold pass.
60
+ # TODO(clause-3-identity-income): return/x402 hook attaches here.
61
+ # Each emission upward is the point at which Identity-Income
62
+ # return-flow is metered. Hook implementation is a follow-up.
64
63
  return CompostTraitEmission(kind=kind, band=band)
65
64
 
66
65
  def emit_soil_cycle_patience_band(self, band: SoilCyclePatienceBand) -> CompostTraitEmission:
@@ -6,9 +6,9 @@ Polls OWN host every 6 hours (degenerate-LAN; reads its own filesystem
6
6
  ``uptime_steward``.
7
7
 
8
8
  Member-authored allowlists at pairing (services / backup-repo paths /
9
- LE cert dirs) those choices stay LOCAL, never surfaced upward.
9
+ LE cert dirs) ; those choices stay LOCAL, never surfaced upward.
10
10
 
11
- Spec: ``phase2-wave2-stub-specs-pack-2026-05-27.md`` §6.
11
+ Stub adapter; implementation pass is a follow-up wave.
12
12
  """
13
13
 
14
14
  from alter_runtime.adapters.household.self_hoster.adapter import (
@@ -1,4 +1,4 @@
1
- """Self-hoster adapter (Eco-15) PassiveLanPoller, own-host only.
1
+ """Self-hoster adapter (Eco-15) ; PassiveLanPoller, own-host only.
2
2
 
3
3
  Polls the LOCAL host every 6 hours. Reads:
4
4
 
@@ -15,10 +15,10 @@ All five inputs are gated on member-authored allowlists declared at
15
15
  pairing time (services / backup repos / cert dirs). The adapter never
16
16
  walks the filesystem outside those allowlists.
17
17
 
18
- V&V hardening pass (Phase-2 substrate-adapter pilot, 2026-05-28): the
19
- observer-execution surface anywhere this module would invoke
18
+ V&V hardening pass (substrate-adapter pilot): the
19
+ observer-execution surface ; anywhere this module would invoke
20
20
  subprocess / systemctl / Path.stat / Path.iterdir against a host target
21
- MUST first pass the target through :meth:`SelfHosterAdapter._invoke_observer`,
21
+ ; MUST first pass the target through :meth:`SelfHosterAdapter._invoke_observer`,
22
22
  which consults :class:`SelfHosterAllowlists` and refuses any
23
23
  out-of-allowlist target with :class:`AllowlistViolationError`. Real
24
24
  read implementations land in follow-up passes; the guard is wired now
@@ -43,7 +43,7 @@ __all__ = [
43
43
  "SelfHosterAllowlists",
44
44
  ]
45
45
 
46
- #: Default 6-hour cadence per stub spec §6.
46
+ #: Default poll cadence (6 hours).
47
47
  DEFAULT_POLL_INTERVAL_SECONDS: float = 6 * 60 * 60
48
48
 
49
49
  #: Names of the buckets on :class:`SelfHosterAllowlists` the
@@ -63,7 +63,7 @@ class AllowlistViolationError(RuntimeError):
63
63
  subprocess / systemctl / filesystem-walk call site that would touch
64
64
  something outside those lists is refused at the
65
65
  :meth:`SelfHosterAdapter._check_allowlist` guard before the
66
- invocation happens the daemon never silently reads outside the
66
+ invocation happens ; the daemon never silently reads outside the
67
67
  member-authored allowlist.
68
68
  """
69
69
 
@@ -130,11 +130,11 @@ class SelfHosterAdapter(PassiveLanPollerBase):
130
130
  The sub-trait kind being observed (``"backup_cadence"`` etc.).
131
131
  Used in the warning log + exception message for traceability.
132
132
  target:
133
- The concrete host resource a systemd unit name, a backup-repo
133
+ The concrete host resource ; a systemd unit name, a backup-repo
134
134
  ``Path``, a letsencrypt cert dir ``Path``.
135
135
  bucket:
136
136
  One of ``"systemd_units"``, ``"backup_repo_paths"``,
137
- ``"letsencrypt_cert_dirs"`` the attribute name on
137
+ ``"letsencrypt_cert_dirs"`` ; the attribute name on
138
138
  :class:`SelfHosterAllowlists` to consult.
139
139
 
140
140
  Raises
@@ -142,7 +142,7 @@ class SelfHosterAdapter(PassiveLanPollerBase):
142
142
  AllowlistViolationError
143
143
  If ``target`` is not in ``self._allowlists.<bucket>``.
144
144
  AttributeError
145
- If ``bucket`` does not name a known attribute surfaces a
145
+ If ``bucket`` does not name a known attribute ; surfaces a
146
146
  typo loudly rather than silently passing.
147
147
  """
148
148
  allowed = getattr(self._allowlists, bucket)
@@ -155,7 +155,7 @@ class SelfHosterAdapter(PassiveLanPollerBase):
155
155
  )
156
156
  raise AllowlistViolationError(
157
157
  f"self_hoster refused observer invocation kind={kind!r} "
158
- f"target={target!r} bucket={bucket!r} target not in "
158
+ f"target={target!r} bucket={bucket!r} ; target not in "
159
159
  f"member-authored allowlist"
160
160
  )
161
161
 
@@ -187,7 +187,7 @@ class SelfHosterAdapter(PassiveLanPollerBase):
187
187
  return await invocation()
188
188
 
189
189
  # ------------------------------------------------------------------
190
- # Per-sub-trait observers scaffold-level, no real reads yet
190
+ # Per-sub-trait observers ; scaffold-level, no real reads yet
191
191
  # ------------------------------------------------------------------
192
192
 
193
193
  async def _observe_os_update_discipline(self) -> None:
@@ -196,7 +196,7 @@ class SelfHosterAdapter(PassiveLanPollerBase):
196
196
  # never expose package list upward, only band.
197
197
  # When wired, route the shell-out through self._invoke_observer with
198
198
  # a dedicated allowlist bucket (package-mgr metadata is not bound to
199
- # the existing three buckets extend SelfHosterAllowlists first).
199
+ # the existing three buckets ; extend SelfHosterAllowlists first).
200
200
  await self._storage.record_observation(
201
201
  kind="os_update_discipline", value_summary="unobserved"
202
202
  )
@@ -206,7 +206,7 @@ class SelfHosterAdapter(PassiveLanPollerBase):
206
206
  # the snapshot directories and record latest mtime; derive 90-day
207
207
  # cadence band downstream.
208
208
  for path in self._allowlists.backup_repo_paths:
209
- # Allowlist guard exercised even at scaffold time the iteration
209
+ # Allowlist guard exercised even at scaffold time ; the iteration
210
210
  # is over the allowlist itself, so the path is trivially valid.
211
211
  # The check is here so the call shape mirrors the eventual real
212
212
  # stat()-bearing invocation.
@@ -243,6 +243,6 @@ class SelfHosterAdapter(PassiveLanPollerBase):
243
243
  # TODO(eco-15): derive 30-day uptime ratio from systemd boot-list +
244
244
  # monotonic clock; gate against allowlisted-service availability.
245
245
  # Per-unit reads MUST go through self._invoke_observer with bucket
246
- # "systemd_units" uptime is computed against allowlisted services
246
+ # "systemd_units" ; uptime is computed against allowlisted services
247
247
  # only, never the full unit list.
248
248
  await self._storage.record_observation(kind="uptime_steward", value_summary="unobserved")
@@ -1,4 +1,4 @@
1
- """Self-hoster storage layer local SQLite, mode 600.
1
+ """Self-hoster storage layer ; local SQLite, mode 600.
2
2
 
3
3
  Persists per-sub-trait observation rows. Schema is deliberately
4
4
  coarse: ``kind`` + a short summary string + timestamp. Detailed
@@ -1,4 +1,4 @@
1
- """Tests for SelfHosterAdapter poll_once invokes all five sub-observers."""
1
+ """Tests for SelfHosterAdapter ; poll_once invokes all five sub-observers."""
2
2
 
3
3
  from __future__ import annotations
4
4
 
@@ -149,7 +149,7 @@ def test_observer_refuses_out_of_allowlist_letsencrypt_dir(
149
149
  def test_observer_proceeds_for_in_allowlist_target(
150
150
  tmp_path: Path, monkeypatch: pytest.MonkeyPatch
151
151
  ) -> None:
152
- """Positive control in-allowlist targets across all three buckets pass."""
152
+ """Positive control ; in-allowlist targets across all three buckets pass."""
153
153
  backup_path = Path("/tmp/restic-fake")
154
154
  le_dir = Path("/tmp/letsencrypt-fake/live/example")
155
155
  adapter = _adapter_with(
@@ -1,4 +1,4 @@
1
- """Tests for self_hoster SQLite storage mode 600 + observation insert."""
1
+ """Tests for self_hoster SQLite storage ; mode 600 + observation insert."""
2
2
 
3
3
  from __future__ import annotations
4
4
 
@@ -1,4 +1,4 @@
1
- """Tests for self_hoster trait emitter Clause-4 banlist + bands."""
1
+ """Tests for self_hoster trait emitter ; Clause-4 banlist + bands."""
2
2
 
3
3
  from __future__ import annotations
4
4
 
@@ -1,6 +1,6 @@
1
1
  """Self-hoster trait band emitters (Eco-15) with Clause-4 banlist.
2
2
 
3
- Five banded sub-traits (stub spec §6):
3
+ Five banded sub-traits:
4
4
 
5
5
  * ``os_update_discipline``
6
6
  * ``backup_cadence``
@@ -8,7 +8,7 @@ Five banded sub-traits (stub spec §6):
8
8
  * ``tls_hygiene``
9
9
  * ``uptime_steward``
10
10
 
11
- Clause-4 banlist (stub spec §6):
11
+ Clause-4 banlist:
12
12
 
13
13
  * ``sysadmin-burnout``
14
14
  * ``self-hoster-anxiety``
@@ -77,10 +77,9 @@ class SelfHosterTraitEmission(BaseModel):
77
77
  class SelfHosterTraits:
78
78
  def emit(self, kind: str, band: str) -> SelfHosterTraitEmission:
79
79
  self._refuse_if_banned(kind)
80
- # TODO(clause-3-identity-income): return/x402 hook attaches here per
81
- # D-PROV-1 + clause-3 ratification. Each emission upward is the
82
- # point at which Identity-Income return-flow is metered. Hook
83
- # implementation lives outside this scaffold pass.
80
+ # TODO(clause-3-identity-income): return/x402 hook attaches here.
81
+ # Each emission upward is the point at which Identity-Income
82
+ # return-flow is metered. Hook implementation is a follow-up.
84
83
  return SelfHosterTraitEmission(kind=kind, band=band)
85
84
 
86
85
  def emit_os_update_discipline(self, band: OsUpdateDisciplineBand) -> SelfHosterTraitEmission:
@@ -1,4 +1,4 @@
1
- """Tapo ecosystem adapter (Eco-14) hub-level multi-parameter composition.
1
+ """Tapo ecosystem adapter (Eco-14) ; hub-level multi-parameter composition.
2
2
 
3
3
  Subscribes at hub level: N parameters at one logical location (the H100
4
4
  hub). ``MultiParameterProvenanceRecord`` is composed across all paired
@@ -8,7 +8,7 @@ LOCAL and is never surfaced upward.
8
8
  Three banded traits: ``household_appliance_stewardship``,
9
9
  ``household_routine_cadence``, ``ambient_stewardship``.
10
10
 
11
- Spec: ``phase2-wave2-stub-specs-pack-2026-05-27.md`` §5.
11
+ Stub adapter; implementation pass is a follow-up wave.
12
12
  """
13
13
 
14
14
  from alter_runtime.adapters.household.tapo_ecosystem.adapter import (