alter-runtime 0.3.2__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.
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/.gitignore +1 -1
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/PKG-INFO +11 -17
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/README.md +10 -16
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/__init__.py +2 -3
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/__init__.py +2 -1
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/claude_jsonl_watcher.py +8 -9
- alter_runtime-0.3.3/alter_runtime/adapters/household/__init__.py +26 -0
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/household/_base.py +13 -15
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/household/compost/__init__.py +0 -2
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/household/compost/adapter.py +4 -4
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/household/compost/storage.py +1 -1
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/household/compost/tests/test_adapter.py +3 -3
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/household/compost/tests/test_storage.py +1 -1
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/household/compost/tests/test_traits.py +1 -1
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/household/compost/traits.py +3 -4
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/household/self_hoster/__init__.py +2 -2
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/household/self_hoster/adapter.py +14 -14
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/household/self_hoster/storage.py +1 -1
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/household/self_hoster/tests/test_adapter.py +2 -2
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/household/self_hoster/tests/test_storage.py +1 -1
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/household/self_hoster/tests/test_traits.py +1 -1
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/household/self_hoster/traits.py +5 -6
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/household/tapo_ecosystem/__init__.py +2 -2
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/household/tapo_ecosystem/adapter.py +4 -4
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/household/tapo_ecosystem/storage.py +2 -2
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/household/tapo_ecosystem/tests/test_adapter.py +2 -2
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/household/tapo_ecosystem/tests/test_storage.py +1 -1
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/household/tapo_ecosystem/tests/test_traits.py +1 -1
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/household/tapo_ecosystem/traits.py +5 -6
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/household/workshop_tools/__init__.py +2 -3
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/household/workshop_tools/adapter.py +3 -3
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/household/workshop_tools/storage.py +3 -3
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/household/workshop_tools/tests/test_adapter.py +2 -2
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/household/workshop_tools/tests/test_storage.py +1 -1
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/household/workshop_tools/tests/test_traits.py +1 -1
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/household/workshop_tools/traits.py +4 -5
- alter_runtime-0.3.3/alter_runtime/adapters/weave_watcher.py +521 -0
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/worktree_watcher.py +15 -15
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/atlas/__init__.py +1 -4
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/atlas/base.py +1 -1
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/atlas/ledger.py +1 -1
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/atlas/observations.py +2 -2
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/atlas/schema.py +3 -3
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/cap_cache.py +3 -4
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/cli.py +30 -19
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/clients/token_usage_client.py +14 -19
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/config.py +401 -37
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/consent.py +5 -6
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/daemon.py +94 -80
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/floor_loop.py +20 -20
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/floor_preflight.py +57 -74
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/http_auth.py +65 -16
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/notifiers/__init__.py +2 -2
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/notifiers/desktop.py +2 -2
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/sdk/__init__.py +1 -1
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/sdk/client.py +11 -14
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/service_install.py +12 -13
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/services/__init__.py +2 -3
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/services/launchd/com.alter.runtime.plist.in +6 -6
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/services/systemd/alter-runtime.service.in +16 -8
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/services/systemd/cf-access-env.conf.in +5 -10
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/sockets/__init__.py +2 -2
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/sockets/unix.py +8 -8
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/subscribers/__init__.py +8 -4
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/subscribers/active_sessions_cron_emitter.py +7 -7
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/subscribers/active_sessions_do_publisher.py +206 -44
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/subscribers/active_sessions_gc.py +1 -1
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/subscribers/active_sessions_writer.py +1 -1
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/subscribers/adapters_writer.py +1 -1
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/subscribers/agent_frames.py +49 -24
- alter_runtime-0.3.3/alter_runtime/subscribers/attunement_refresher.py +356 -0
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/subscribers/bus.py +3 -3
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/subscribers/cache_writer.py +13 -16
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/subscribers/ceremony_echo.py +8 -10
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/subscribers/do_sse.py +21 -13
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/subscribers/ebpf.py +4 -4
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/subscribers/inbox_writer.py +8 -9
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/subscribers/mcp_fallback.py +66 -18
- alter_runtime-0.3.3/alter_runtime/subscribers/presence_feed_writer.py +343 -0
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/subscribers/presence_writer.py +1 -1
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/subscribers/session_presence.py +28 -21
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/subscribers/weave_intent_writer.py +22 -22
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/update_loop.py +40 -50
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/weave/__init__.py +3 -3
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/weave/resolver.py +22 -22
- alter_runtime-0.3.3/alter_runtime/weave_log.py +437 -0
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/pyproject.toml +15 -8
- alter_runtime-0.3.2/alter_runtime/adapters/household/__init__.py +0 -29
- alter_runtime-0.3.2/docs/schemas/README.md +0 -46
- alter_runtime-0.3.2/examples/awesomewm/README.md +0 -122
- alter_runtime-0.3.2/examples/waybar/README.md +0 -66
- alter_runtime-0.3.2/pypi-truealter/README.md +0 -46
- alter_runtime-0.3.2/pypi-truealter/pyproject.toml +0 -67
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/LICENSE +0 -0
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/git_watcher.py +0 -0
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/household/compost/tests/__init__.py +0 -0
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/household/self_hoster/tests/__init__.py +0 -0
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/household/tapo_ecosystem/tests/__init__.py +0 -0
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/household/workshop_tools/tests/__init__.py +0 -0
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/clients/__init__.py +0 -0
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/sockets/dbus.py +0 -0
- {alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/subscribers/sse.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: alter-runtime
|
|
3
|
-
Version: 0.3.
|
|
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
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
| 2 | 2c - systemd + launchd service units | Shipped |
|
|
118
|
-
| 2 | 2d - First pixel: CC hook + scripts upgrade | Shipped |
|
|
119
|
-
| 2 | 2e - eBPF subscriber (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,9 +173,9 @@ 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
|
|
176
|
+
4. **Collects ambient signals** via adapters:
|
|
183
177
|
- Git commits, branch switches, pushes (via `watchdog` on `.git/refs/heads/`)
|
|
184
|
-
-
|
|
178
|
+
- Editor session hook events (forwarded from local shell hooks)
|
|
185
179
|
- Shell command invocations (opt-in)
|
|
186
180
|
- eBPF kernel attestations (shipped; reference impl in `alter-ebpf`)
|
|
187
181
|
|
|
@@ -204,15 +198,15 @@ alter-runtime/
|
|
|
204
198
|
│ ├── sdk/
|
|
205
199
|
│ │ ├── __init__.py
|
|
206
200
|
│ │ └── client.py # AlterClient (async identity MCP client)
|
|
207
|
-
│ ├── subscribers/
|
|
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/
|
|
204
|
+
│ ├── sockets/
|
|
211
205
|
│ │ ├── unix.py # /run/user/$UID/alter.sock
|
|
212
206
|
│ │ └── dbus.py # org.alter.Identity1
|
|
213
|
-
│ ├── adapters/
|
|
207
|
+
│ ├── adapters/
|
|
214
208
|
│ │ └── git_watcher.py # watchdog on .git/refs/heads/
|
|
215
|
-
│ └── services/
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
| 2 | 2c - systemd + launchd service units | Shipped |
|
|
50
|
-
| 2 | 2d - First pixel: CC hook + scripts upgrade | Shipped |
|
|
51
|
-
| 2 | 2e - eBPF subscriber (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,9 +105,9 @@ 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
|
|
108
|
+
4. **Collects ambient signals** via adapters:
|
|
115
109
|
- Git commits, branch switches, pushes (via `watchdog` on `.git/refs/heads/`)
|
|
116
|
-
-
|
|
110
|
+
- Editor session hook events (forwarded from local shell hooks)
|
|
117
111
|
- Shell command invocations (opt-in)
|
|
118
112
|
- eBPF kernel attestations (shipped; reference impl in `alter-ebpf`)
|
|
119
113
|
|
|
@@ -136,15 +130,15 @@ alter-runtime/
|
|
|
136
130
|
│ ├── sdk/
|
|
137
131
|
│ │ ├── __init__.py
|
|
138
132
|
│ │ └── client.py # AlterClient (async identity MCP client)
|
|
139
|
-
│ ├── subscribers/
|
|
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/
|
|
136
|
+
│ ├── sockets/
|
|
143
137
|
│ │ ├── unix.py # /run/user/$UID/alter.sock
|
|
144
138
|
│ │ └── dbus.py # org.alter.Identity1
|
|
145
|
-
│ ├── adapters/
|
|
139
|
+
│ ├── adapters/
|
|
146
140
|
│ │ └── git_watcher.py # watchdog on .git/refs/heads/
|
|
147
|
-
│ └── services/
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
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
|
-
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
|
4
|
-
(ALTER internal Phase-2 cross-pattern catalogue):
|
|
3
|
+
Two adapter shapes are shared across the household adapter family:
|
|
5
4
|
|
|
6
|
-
* :class:`EventBusSubscriberBase
|
|
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
|
|
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
|
-
|
|
16
|
+
short of imposing topic taxonomy, payload shape, or storage choice.
|
|
17
|
+
Those are per-adapter concerns.
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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(
|
{alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/household/compost/__init__.py
RENAMED
|
@@ -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
|
{alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/household/compost/adapter.py
RENAMED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
"""Compost adapter (Eco-9)
|
|
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"``
|
|
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
|
|
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
|
|
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
|
-
"""Tests for CompostAdapter
|
|
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"
|
|
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
|
|
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
|
{alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/household/compost/traits.py
RENAMED
|
@@ -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
|
|
61
|
-
#
|
|
62
|
-
#
|
|
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:
|
{alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/household/self_hoster/__init__.py
RENAMED
|
@@ -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)
|
|
9
|
+
LE cert dirs) ; those choices stay LOCAL, never surfaced upward.
|
|
10
10
|
|
|
11
|
-
|
|
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 (
|
{alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/household/self_hoster/adapter.py
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Self-hoster adapter (Eco-15)
|
|
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 (
|
|
19
|
-
observer-execution surface
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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"``
|
|
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
|
|
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}
|
|
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
|
|
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
|
|
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
|
|
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"
|
|
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
|
-
"""Tests for SelfHosterAdapter
|
|
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
|
|
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(
|
{alter_runtime-0.3.2 → alter_runtime-0.3.3}/alter_runtime/adapters/household/self_hoster/traits.py
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Self-hoster trait band emitters (Eco-15) with Clause-4 banlist.
|
|
2
2
|
|
|
3
|
-
Five banded sub-traits
|
|
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
|
|
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
|
|
81
|
-
#
|
|
82
|
-
#
|
|
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)
|
|
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
|
-
|
|
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 (
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Tapo ecosystem adapter (Eco-14)
|
|
1
|
+
"""Tapo ecosystem adapter (Eco-14) ; H100/H200 hub-level composition.
|
|
2
2
|
|
|
3
3
|
Subscribes to ``tapo/+/+/state`` and ``tapo/+/+/energy`` topic globs;
|
|
4
4
|
all paired Tapo devices under one hub compose into a single
|
|
@@ -10,7 +10,7 @@ emits. Room-mapping authored by the member at pairing stays LOCAL.
|
|
|
10
10
|
Vendor-cloud posture (option b): TP-Link cloud REFUSED; python-kasa
|
|
11
11
|
local API accepted at the LAN bridge layer; Tapo Cloud account-link
|
|
12
12
|
REFUSED. This adapter only consumes events the household-bridge
|
|
13
|
-
republishes from the local LAN
|
|
13
|
+
republishes from the local LAN ; it never originates cloud calls.
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
16
|
from __future__ import annotations
|
|
@@ -65,7 +65,7 @@ class TapoEcosystemAdapter(EventBusSubscriberBase):
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
``room`` may be present in the bridged payload but is dropped
|
|
68
|
-
before persistence
|
|
68
|
+
before persistence ; room-mapping stays at the bridge level.
|
|
69
69
|
"""
|
|
70
70
|
if not isinstance(payload, dict):
|
|
71
71
|
self._logger.debug("tapo_ecosystem ignoring non-dict payload")
|
|
@@ -81,7 +81,7 @@ class TapoEcosystemAdapter(EventBusSubscriberBase):
|
|
|
81
81
|
return
|
|
82
82
|
|
|
83
83
|
# TODO(eco-14): MultiParameterProvenanceRecord composition keyed by
|
|
84
|
-
# hub_mac
|
|
84
|
+
# hub_mac ; current scaffold persists per-device events flat.
|
|
85
85
|
# TODO(eco-14): compute household_appliance_stewardship band from
|
|
86
86
|
# per-plug Wh fingerprint diversity × rolling-30-day cadence.
|
|
87
87
|
# TODO(eco-14): compute household_routine_cadence band from
|