logplane 0.13.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.
- logplane-0.13.0/COMPATIBILITY.md +193 -0
- logplane-0.13.0/CONTRIBUTORS.md +15 -0
- logplane-0.13.0/LICENSE +21 -0
- logplane-0.13.0/MANIFEST.in +9 -0
- logplane-0.13.0/PKG-INFO +359 -0
- logplane-0.13.0/README.md +332 -0
- logplane-0.13.0/RECOVERY.md +265 -0
- logplane-0.13.0/RELEASE_NOTES.md +184 -0
- logplane-0.13.0/ROADMAP.md +872 -0
- logplane-0.13.0/benchmarks/REPLAY_RESULTS.md +168 -0
- logplane-0.13.0/benchmarks/RESULTS.md +44 -0
- logplane-0.13.0/benchmarks/replay_admission.py +130 -0
- logplane-0.13.0/benchmarks/task_lookup.py +76 -0
- logplane-0.13.0/ci/constraints-min.txt +6 -0
- logplane-0.13.0/docs/architecture.md +270 -0
- logplane-0.13.0/docs/events.md +271 -0
- logplane-0.13.0/docs/integration.md +346 -0
- logplane-0.13.0/docs/operations.md +577 -0
- logplane-0.13.0/docs/reviews/v0.10-operator-controls-review-c340808.md +263 -0
- logplane-0.13.0/docs/reviews/v0.11-baseline-review-c7a47f0.md +86 -0
- logplane-0.13.0/examples/README.md +25 -0
- logplane-0.13.0/examples/__init__.py +1 -0
- logplane-0.13.0/examples/audit_triage/README.md +71 -0
- logplane-0.13.0/examples/cli_agent/README.md +16 -0
- logplane-0.13.0/examples/cli_agent/TRIAL_NOTES.md +76 -0
- logplane-0.13.0/examples/cli_agent/__init__.py +1 -0
- logplane-0.13.0/examples/cli_agent/adapter.json +15 -0
- logplane-0.13.0/examples/cli_agent/agent.py +30 -0
- logplane-0.13.0/examples/hermes/README.md +206 -0
- logplane-0.13.0/examples/hermes/TRIAL_NOTES.md +1055 -0
- logplane-0.13.0/examples/hermes/__init__.py +5 -0
- logplane-0.13.0/examples/hermes/hermes_executor.py +650 -0
- logplane-0.13.0/examples/hermes/live_smoke.py +241 -0
- logplane-0.13.0/examples/hermes/run_worker.py +127 -0
- logplane-0.13.0/examples/http_agent/README.md +52 -0
- logplane-0.13.0/examples/http_agent/TRIAL_NOTES.md +104 -0
- logplane-0.13.0/examples/http_agent/__init__.py +1 -0
- logplane-0.13.0/examples/http_agent/adapter.json +15 -0
- logplane-0.13.0/examples/http_agent/agent.py +248 -0
- logplane-0.13.0/examples/python_agent/README.md +15 -0
- logplane-0.13.0/examples/python_agent/__init__.py +1 -0
- logplane-0.13.0/examples/python_agent/agent.py +19 -0
- logplane-0.13.0/examples/python_agent/run_worker.py +25 -0
- logplane-0.13.0/pyproject.toml +52 -0
- logplane-0.13.0/requirements-dev.txt +3 -0
- logplane-0.13.0/requirements.txt +4 -0
- logplane-0.13.0/scripts/capture_upgrade_fixture.py +163 -0
- logplane-0.13.0/scripts/check_release.py +319 -0
- logplane-0.13.0/scripts/operator_trial_worker.py +26 -0
- logplane-0.13.0/setup.cfg +4 -0
- logplane-0.13.0/src/logplane/__init__.py +113 -0
- logplane-0.13.0/src/logplane/__main__.py +5 -0
- logplane-0.13.0/src/logplane/adapter_check.py +209 -0
- logplane-0.13.0/src/logplane/adapters.py +21 -0
- logplane-0.13.0/src/logplane/adoption.py +231 -0
- logplane-0.13.0/src/logplane/artifacts.py +187 -0
- logplane-0.13.0/src/logplane/bus.py +2065 -0
- logplane-0.13.0/src/logplane/cli.py +1303 -0
- logplane-0.13.0/src/logplane/client.py +566 -0
- logplane-0.13.0/src/logplane/conformance.py +203 -0
- logplane-0.13.0/src/logplane/demo.py +1075 -0
- logplane-0.13.0/src/logplane/executor_protocol.py +120 -0
- logplane-0.13.0/src/logplane/executors.py +779 -0
- logplane-0.13.0/src/logplane/integration.py +574 -0
- logplane-0.13.0/src/logplane/legacy.py +46 -0
- logplane-0.13.0/src/logplane/limits.py +10 -0
- logplane-0.13.0/src/logplane/local_config.py +251 -0
- logplane-0.13.0/src/logplane/observer.py +169 -0
- logplane-0.13.0/src/logplane/operations.py +1022 -0
- logplane-0.13.0/src/logplane/operator_controls.py +101 -0
- logplane-0.13.0/src/logplane/pm_agent.py +917 -0
- logplane-0.13.0/src/logplane/projection.py +1793 -0
- logplane-0.13.0/src/logplane/projection_store.py +282 -0
- logplane-0.13.0/src/logplane/protocol.py +23 -0
- logplane-0.13.0/src/logplane/recovery.py +281 -0
- logplane-0.13.0/src/logplane/runtime.py +772 -0
- logplane-0.13.0/src/logplane/scheduling.py +31 -0
- logplane-0.13.0/src/logplane/telemetry.py +369 -0
- logplane-0.13.0/src/logplane/topics.py +54 -0
- logplane-0.13.0/src/logplane/version.py +3 -0
- logplane-0.13.0/src/logplane/worker.py +124 -0
- logplane-0.13.0/src/logplane.egg-info/PKG-INFO +359 -0
- logplane-0.13.0/src/logplane.egg-info/SOURCES.txt +124 -0
- logplane-0.13.0/src/logplane.egg-info/dependency_links.txt +1 -0
- logplane-0.13.0/src/logplane.egg-info/entry_points.txt +2 -0
- logplane-0.13.0/src/logplane.egg-info/requires.txt +4 -0
- logplane-0.13.0/src/logplane.egg-info/top_level.txt +1 -0
- logplane-0.13.0/tests/__init__.py +1 -0
- logplane-0.13.0/tests/fixtures/compatibility/v0.11.0-upgrade/README.md +30 -0
- logplane-0.13.0/tests/fixtures/compatibility/v0.11.0-upgrade/database.sql +79 -0
- logplane-0.13.0/tests/fixtures/compatibility/v0.11.0-upgrade/expected.json +2732 -0
- logplane-0.13.0/tests/fixtures/compatibility/v0.11.0.json +121 -0
- logplane-0.13.0/tests/test_adapter_check.py +297 -0
- logplane-0.13.0/tests/test_adoption.py +178 -0
- logplane-0.13.0/tests/test_artifacts.py +42 -0
- logplane-0.13.0/tests/test_bus.py +846 -0
- logplane-0.13.0/tests/test_cli.py +159 -0
- logplane-0.13.0/tests/test_client.py +223 -0
- logplane-0.13.0/tests/test_compatibility.py +62 -0
- logplane-0.13.0/tests/test_demo.py +295 -0
- logplane-0.13.0/tests/test_demo_cli.py +96 -0
- logplane-0.13.0/tests/test_executors.py +408 -0
- logplane-0.13.0/tests/test_hermes_example.py +400 -0
- logplane-0.13.0/tests/test_http_example.py +151 -0
- logplane-0.13.0/tests/test_integration.py +346 -0
- logplane-0.13.0/tests/test_legacy_names.py +46 -0
- logplane-0.13.0/tests/test_operations.py +403 -0
- logplane-0.13.0/tests/test_operator_cli.py +242 -0
- logplane-0.13.0/tests/test_pm_agent.py +1365 -0
- logplane-0.13.0/tests/test_release_check.py +53 -0
- logplane-0.13.0/tests/test_released_upgrade.py +210 -0
- logplane-0.13.0/tests/test_runtime.py +1083 -0
- logplane-0.13.0/tests/test_stream_shutdown.py +208 -0
- logplane-0.13.0/tests/test_telemetry.py +273 -0
- logplane-0.13.0/tests/test_v010_admission.py +262 -0
- logplane-0.13.0/tests/test_v010_hardening.py +606 -0
- logplane-0.13.0/tests/test_v010_phase2.py +571 -0
- logplane-0.13.0/tests/test_v010_phase3_accounting.py +552 -0
- logplane-0.13.0/tests/test_v010_phase3_fairness.py +337 -0
- logplane-0.13.0/tests/test_v010_phase3_policy.py +455 -0
- logplane-0.13.0/tests/test_v011_recovery.py +157 -0
- logplane-0.13.0/tests/test_v011_snapshots.py +242 -0
- logplane-0.13.0/tests/test_v011_streaming_admission.py +105 -0
- logplane-0.13.0/tests/test_v011_task_index.py +176 -0
- logplane-0.13.0/tests/test_v09_integration_kit.py +399 -0
- logplane-0.13.0/tests/test_worker.py +76 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# Compatibility and support policy
|
|
2
|
+
|
|
3
|
+
This document defines the pre-v1.0 baseline and the intended v1.x contract.
|
|
4
|
+
It does not declare v1.0 released or retroactively certify every old version.
|
|
5
|
+
Release status and outstanding evidence live in [ROADMAP.md](ROADMAP.md).
|
|
6
|
+
|
|
7
|
+
## Supported deployment target
|
|
8
|
+
|
|
9
|
+
logplane targets one trusted local host running macOS or Linux, using CPython
|
|
10
|
+
3.10 or newer. The CI matrix covers 3.10, 3.13, and 3.14; results for a new
|
|
11
|
+
matrix entry must pass before that entry is treated as verified. Other Python
|
|
12
|
+
versions are not verified merely because package metadata permits installation.
|
|
13
|
+
Native Windows is not supported: process locking uses `fcntl`. WSL, alternative
|
|
14
|
+
interpreters, network filesystems, and distributed deployments are not certified.
|
|
15
|
+
|
|
16
|
+
CI tests the declared direct dependency minimums on Python 3.10 separately from
|
|
17
|
+
latest-compatible resolution. The floor constraint file is a test input, not
|
|
18
|
+
a production lockfile or a claim to test every transitive dependency combination.
|
|
19
|
+
Resolved dependency versions are retained with each run. Supported releases
|
|
20
|
+
require a passing package-install smoke, not just tests in an editable checkout.
|
|
21
|
+
|
|
22
|
+
## Versioned surfaces
|
|
23
|
+
|
|
24
|
+
| Surface | Current baseline | Compatibility boundary |
|
|
25
|
+
| --- | --- | --- |
|
|
26
|
+
| Package | `0.13.0` release | Pre-1.0 changes must be documented; do not infer a 1.x guarantee yet |
|
|
27
|
+
| Python integration | Names exported by `logplane`, `logplane.adapters`, `logplane.protocol` | Documented constructors, methods, typed outcomes, and effect identity |
|
|
28
|
+
| Executor wire format | `agent-bus.executor` v1 | Selected envelope version is explicit; headers are strict |
|
|
29
|
+
| Event envelope | Built-in writes use schema v2 | History is immutable; acceptance and replay semantics are part of the contract |
|
|
30
|
+
| Local/adapter configuration | Each uses schema v1 | Version and supported keys must match that component |
|
|
31
|
+
| CLI | Documented commands, options, `--json` fields, and exit statuses | Human-readable wording/layout is not an API |
|
|
32
|
+
| Database indexes / PM snapshots | Derived local caches | No public table layout or snapshot-file compatibility promise |
|
|
33
|
+
| Recovery bundles | Versioned verified local format | Unsupported formats must fail safely; hashes are integrity checks, not signatures |
|
|
34
|
+
|
|
35
|
+
Implementation modules live under `logplane.*` (for example `logplane.bus`),
|
|
36
|
+
but integrations should import from `logplane`, `logplane.adapters`, and
|
|
37
|
+
`logplane.protocol`. Private helpers, reducer dataclass internals,
|
|
38
|
+
SQLite tables, test fixtures, and benchmark output are not public integration
|
|
39
|
+
APIs. The `demo` module behind `logplane demo` is a CLI implementation detail,
|
|
40
|
+
not an integration API. Importable implementation modules are not a promise
|
|
41
|
+
to freeze their private members. Repository examples are source assets, not installed packages.
|
|
42
|
+
|
|
43
|
+
### Additions and unknown values
|
|
44
|
+
|
|
45
|
+
- Do not remove or change the meaning/type of an existing public field silently.
|
|
46
|
+
A new optional field needs a default and a compatibility test. Adding a
|
|
47
|
+
required field or changing interpretation is a breaking change.
|
|
48
|
+
- Do not assume every JSON object permits arbitrary keys. Executor envelopes
|
|
49
|
+
and protocol headers, local/adapter configs, and some payloads use strict
|
|
50
|
+
shapes. A change rejected by an older supported reader needs a new format
|
|
51
|
+
version or an explicit coordinated upgrade, not the label "additive".
|
|
52
|
+
- Executor v1 `supported_versions` may advertise future versions as long as the
|
|
53
|
+
selected version is included. It is not automatic negotiation; use the same
|
|
54
|
+
selected version at both ends. Legacy unversioned v0 is limited to the existing
|
|
55
|
+
legacy subprocess path; new configured CLI/HTTP integrations use v1.
|
|
56
|
+
- New built-in writes use event schema v2. Legacy stored rows remain readable;
|
|
57
|
+
readable history does not mean old events satisfy every modern reducer rule.
|
|
58
|
+
Existing migrations and release fixtures, not a blanket "all history works"
|
|
59
|
+
statement, define demonstrated coverage.
|
|
60
|
+
- Unknown custom topics are storage extensions, not permission to influence the
|
|
61
|
+
PM. Keep custom readers tolerant of unrelated topics. Raw telemetry topics
|
|
62
|
+
are not replayed by the PM; compact `workflow.usage_recorded` coordination
|
|
63
|
+
events may affect budget admission.
|
|
64
|
+
- CLI JSON consumers should tolerate additional object fields and avoid parsing
|
|
65
|
+
human output. Existing fields must retain their meaning. Unknown status/enum
|
|
66
|
+
values should be displayed as unknown, not treated as completed/successful.
|
|
67
|
+
Newly emitted enum values require release notes and a consumer review.
|
|
68
|
+
|
|
69
|
+
### CLI exit statuses
|
|
70
|
+
|
|
71
|
+
| Code | Meaning |
|
|
72
|
+
| ---: | --- |
|
|
73
|
+
| 0 | Command succeeded; inspect the result for task/diagnostic state |
|
|
74
|
+
| 2 | Invalid usage/configuration, transport failure, or rejected operation |
|
|
75
|
+
| 3 | Lookup did not find the requested task/workflow |
|
|
76
|
+
| 4 | An adapter probe fails/times out, a storage audit fails, or replay rejects a recorded operator intent |
|
|
77
|
+
| 130 | Interrupted by the operator |
|
|
78
|
+
|
|
79
|
+
Zero does not mean a submitted task completed, or that all doctor checks are
|
|
80
|
+
healthy: read `--json` and the check/state fields. Errors currently go to stderr
|
|
81
|
+
and need not be JSON even when `--json` was selected. Unexpected crashes are not
|
|
82
|
+
a stable error protocol. Tail output is a stream, not one JSON document.
|
|
83
|
+
|
|
84
|
+
## Upgrade, downgrade, and deployment versions
|
|
85
|
+
|
|
86
|
+
1. Read release notes. Stop PMs, workers, submitters, and artifact publishers.
|
|
87
|
+
2. Preserve configuration and a verified database/artifact backup using
|
|
88
|
+
[RECOVERY.md](RECOVERY.md). Do not delete history or copy a live SQLite file
|
|
89
|
+
without its supported backup mechanism.
|
|
90
|
+
3. Upgrade bus, PM, workers, and operator CLI together. Mixed coordination
|
|
91
|
+
package versions are not a supported rolling-upgrade strategy. External
|
|
92
|
+
adapters can remain on their explicitly supported wire version.
|
|
93
|
+
4. Start the bus to run transactional migrations, then PM/workers; verify
|
|
94
|
+
doctor, pending work, and representative task/workflow output.
|
|
95
|
+
|
|
96
|
+
Indexes may be rebuilt and incompatible snapshots discarded. These operations
|
|
97
|
+
must not rewrite original events. Downgrading binaries over an upgraded live
|
|
98
|
+
database is unsupported and is not universally rejected by the current code.
|
|
99
|
+
For rollback, stop new processes and restore a pre-upgrade backup into a separate
|
|
100
|
+
directory with its matching version. Later work and external side effects do
|
|
101
|
+
not disappear or roll back automatically; reconcile them before resuming.
|
|
102
|
+
|
|
103
|
+
## Release and deprecation rules
|
|
104
|
+
|
|
105
|
+
Before v1.0, releases may change contracts but must document known breaks and
|
|
106
|
+
provide tests/upgrade guidance. The readiness baseline protects v0.11.0 public
|
|
107
|
+
exports and representative adapter messages; it is not exhaustive coverage of
|
|
108
|
+
all old releases, CLI outputs, or historical database semantics.
|
|
109
|
+
|
|
110
|
+
The released-database baseline now also captures v0.11.0's actual SQLite
|
|
111
|
+
schema/history/snapshot as SQL, plus release-produced CLI and adapter outputs.
|
|
112
|
+
Tests reconstruct disposable databases, run current initialization repeatedly,
|
|
113
|
+
and verify raw event values/identity, idempotency, counters, snapshot fallback,
|
|
114
|
+
index reconstruction, and continued reconciliation. Cases include DAG result
|
|
115
|
+
propagation, human decisions, retry, pause/resume, cancellation, lease/deadline
|
|
116
|
+
expiry, and persisted policy with missing usage. Public JSON comparisons permit
|
|
117
|
+
additional fields but preserve existing field types and values.
|
|
118
|
+
|
|
119
|
+
This demonstrates **v0.11.0 → current development** for the captured cases.
|
|
120
|
+
v0.12.0 adds CLI commands and options only (`demo`, `submit --depends-on`, and
|
|
121
|
+
`demo-worker` outcome flags), corrects `serve`'s startup-failure status to 2,
|
|
122
|
+
and changes no event, executor, configuration, or `--json` field contract.
|
|
123
|
+
It adds no schema migration and does not certify physical WAL recovery, every
|
|
124
|
+
historical release, or downgrades. See the [fixture provenance](tests/fixtures/compatibility/v0.11.0-upgrade/README.md).
|
|
125
|
+
|
|
126
|
+
v0.13.0 renames the project from agent-bus to logplane: the distribution,
|
|
127
|
+
import package, `logplane` command, `LOGPLANE_*` environment variables, and
|
|
128
|
+
`logplane.local.json` config file. The pre-0.13 flat modules and the
|
|
129
|
+
`agent_bus` package are removed, and old `AGENT_BUS_*` variables or an
|
|
130
|
+
`agent-bus.local.json` without a `logplane.local.json` fail with a rename
|
|
131
|
+
message instead of being ignored. Identifiers stored in history or shared with
|
|
132
|
+
external adapters keep their original spelling, so existing databases,
|
|
133
|
+
backups, and adapters work unchanged: the `agent-bus` ownership owner, the
|
|
134
|
+
effect-key salts behind `effect_id()`, the `agent-bus.executor` protocol name,
|
|
135
|
+
the `X-Agent-Bus-*` HTTP headers, the `agent_bus_conformance_probe` context
|
|
136
|
+
key, and the backup bundle kinds. See the
|
|
137
|
+
[migration steps](RELEASE_NOTES.md#migrating-from-agent-bus).
|
|
138
|
+
|
|
139
|
+
The owner-approved v1.0 upgrade baseline is **v0.11.0**: this is the oldest
|
|
140
|
+
starting release for a supported direct upgrade to v1.0. Direct upgrades from
|
|
141
|
+
earlier releases are not promised. Any intervening release included in the
|
|
142
|
+
supported path needs its own release-derived regression coverage; do not infer
|
|
143
|
+
coverage solely from its version number. The final v1.0 build must pass these
|
|
144
|
+
upgrade checks before release. This is a selected support scope, not a claim
|
|
145
|
+
that v1.0 has shipped or that every historical database has been tested.
|
|
146
|
+
|
|
147
|
+
For v1.x, patches are intended for compatible fixes; minor releases may add
|
|
148
|
+
compatible capabilities. Removing supported APIs or changing existing wire/
|
|
149
|
+
event semantics requires a major version, except urgent security corrections
|
|
150
|
+
whose impact and mitigation must be called out explicitly. Deprecations should
|
|
151
|
+
be documented in release notes and remain available through the current major
|
|
152
|
+
series; use runtime warnings where actionable and safe for protocol output.
|
|
153
|
+
Bug fixes can change behavior outside the documented contract.
|
|
154
|
+
|
|
155
|
+
Maintenance targets the latest release; no LTS/backport window or response-time
|
|
156
|
+
SLA is promised. The project uses the [MIT License](LICENSE), with copyright
|
|
157
|
+
held by deki and development acknowledgements in [CONTRIBUTORS.md](CONTRIBUTORS.md).
|
|
158
|
+
Trials continue using GitHub source installations; PyPI publication, native
|
|
159
|
+
Windows, and a distributed service remain separate decisions. No package is
|
|
160
|
+
published by selecting a license. The public 1.x promise requires
|
|
161
|
+
the remaining roadmap gates, including independent onboarding evidence.
|
|
162
|
+
|
|
163
|
+
Building from source requires setuptools 77.0.3 or newer for standard SPDX
|
|
164
|
+
license metadata. Isolated builds install that build dependency automatically;
|
|
165
|
+
the runtime Python and dependency requirements are unchanged.
|
|
166
|
+
|
|
167
|
+
## Running the release checks
|
|
168
|
+
|
|
169
|
+
From a development checkout with a supported Python:
|
|
170
|
+
|
|
171
|
+
```sh
|
|
172
|
+
python -m pip install -r requirements-dev.txt
|
|
173
|
+
python -m pip install -e .
|
|
174
|
+
python -m pytest -q
|
|
175
|
+
python -m build
|
|
176
|
+
python scripts/check_release.py --wheel dist/logplane-0.13.0-py3-none-any.whl
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Use the wheel filename produced by the build if its version differs. The build
|
|
180
|
+
creates an sdist and builds the wheel from it. The check creates a fresh venv,
|
|
181
|
+
installs the wheel/dependencies, verifies import origins, and exercises a
|
|
182
|
+
successful and timed-out CLI adapter check plus a credential-free live task
|
|
183
|
+
outside the checkout. It also checks discovery, human response, retry, and
|
|
184
|
+
cancellation through a small live worker, including duplicate commands, and
|
|
185
|
+
runs released-database upgrade tests against the installed package. It downloads dependencies and
|
|
186
|
+
uses only a disposable loopback server; it makes no model calls. It reports the
|
|
187
|
+
retained evidence directory and stops its own processes on success or failure.
|
|
188
|
+
Pass `--constraints ci/constraints-min.txt` to exercise direct dependency floors.
|
|
189
|
+
GitHub CI uses the same check and uploads its diagnostics; configuring CI is
|
|
190
|
+
not evidence that remote jobs have already passed.
|
|
191
|
+
|
|
192
|
+
The workflow follows GitHub's [Python build/test guidance](https://docs.github.com/en/actions/tutorials/build-and-test-code/python),
|
|
193
|
+
uses read-only repository permissions, and pins official actions to commit IDs.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Credits
|
|
2
|
+
|
|
3
|
+
Project owner and copyright holder: **deki**.
|
|
4
|
+
|
|
5
|
+
## AI-assisted development acknowledgements
|
|
6
|
+
|
|
7
|
+
The following names are credited as requested by the project owner:
|
|
8
|
+
|
|
9
|
+
- GPT Sol
|
|
10
|
+
- Fable 5.1
|
|
11
|
+
- GPT-6 Astra
|
|
12
|
+
|
|
13
|
+
These are development acknowledgements, not additional copyright-holder
|
|
14
|
+
designations or endorsements. The project is distributed under the
|
|
15
|
+
[MIT License](LICENSE).
|
logplane-0.13.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 deki
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
include COMPATIBILITY.md RECOVERY.md RELEASE_NOTES.md ROADMAP.md
|
|
2
|
+
include LICENSE CONTRIBUTORS.md
|
|
3
|
+
include requirements.txt requirements-dev.txt
|
|
4
|
+
recursive-include benchmarks *.py *.md
|
|
5
|
+
recursive-include scripts *.py
|
|
6
|
+
recursive-include ci *.txt
|
|
7
|
+
recursive-include tests *.py *.json *.sql *.md
|
|
8
|
+
recursive-include examples *.py *.json *.md
|
|
9
|
+
recursive-include docs *.md
|
logplane-0.13.0/PKG-INFO
ADDED
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: logplane
|
|
3
|
+
Version: 0.13.0
|
|
4
|
+
Summary: A local control plane for AI agents, built on an append-only event log
|
|
5
|
+
Author: deki
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/dekizk/logplane
|
|
8
|
+
Project-URL: Documentation, https://github.com/dekizk/logplane#documentation
|
|
9
|
+
Project-URL: Changelog, https://github.com/dekizk/logplane/blob/main/RELEASE_NOTES.md
|
|
10
|
+
Project-URL: Issues, https://github.com/dekizk/logplane/issues
|
|
11
|
+
Keywords: ai-agents,orchestration,control-plane,event-sourcing,llm
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: MacOS
|
|
15
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: fastapi<1,>=0.115
|
|
23
|
+
Requires-Dist: httpx<1,>=0.27
|
|
24
|
+
Requires-Dist: pydantic<3,>=2.8
|
|
25
|
+
Requires-Dist: uvicorn<1,>=0.30
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# logplane
|
|
29
|
+
|
|
30
|
+
**A local control plane for AI agents.** logplane assigns work, recovers from
|
|
31
|
+
crashes, holds risky actions for human approval, and enforces cost caps, and
|
|
32
|
+
it drives all of it from an append-only event log instead of a mutable task
|
|
33
|
+
board.
|
|
34
|
+
|
|
35
|
+

|
|
36
|
+
|
|
37
|
+
Agent workflows fail in ordinary ways. A worker crashes mid-task, the
|
|
38
|
+
coordinator restarts, two runners pick up the same job, an agent needs a human
|
|
39
|
+
answer, or a retry repeats an external action. A kanban-style board only shows
|
|
40
|
+
where a card was last moved. logplane records what actually happened, as
|
|
41
|
+
immutable events, and derives current state by replaying them. Ownership,
|
|
42
|
+
retries, dependencies, and human decisions are all events, so a crash loses
|
|
43
|
+
nothing and every state can be explained.
|
|
44
|
+
|
|
45
|
+
### See it in a minute
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
git clone https://github.com/dekizk/logplane.git && cd logplane
|
|
49
|
+
python3 -m venv .venv && . .venv/bin/activate # Python 3.10+
|
|
50
|
+
python -m pip install -e .
|
|
51
|
+
|
|
52
|
+
logplane demo recovery # kill the worker and the coordinator mid-task
|
|
53
|
+
logplane demo approval # an agent asks before an irreversible tool call
|
|
54
|
+
logplane demo budget # a cost cap stops a runaway bill
|
|
55
|
+
logplane demo audit-triage # parallel reviewers, a human severity call,
|
|
56
|
+
# findings submitted once despite a crash
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Each demo starts its own bus, coordinator, and workers as real processes in a
|
|
60
|
+
throwaway directory, narrates what happens, and keeps the history for you to
|
|
61
|
+
inspect. The crashes are real `kill -9`s. None of it touches your other
|
|
62
|
+
logplane data.
|
|
63
|
+
|
|
64
|
+
<details>
|
|
65
|
+
<summary>Watch <code>logplane demo approval</code>: an agent asks before an irreversible tool call</summary>
|
|
66
|
+
|
|
67
|
+

|
|
68
|
+
|
|
69
|
+
</details>
|
|
70
|
+
|
|
71
|
+
<details>
|
|
72
|
+
<summary>Watch <code>logplane demo budget</code>: a cost cap stops a runaway bill</summary>
|
|
73
|
+
|
|
74
|
+

|
|
75
|
+
|
|
76
|
+
</details>
|
|
77
|
+
|
|
78
|
+
### What you get
|
|
79
|
+
|
|
80
|
+
- **Crash recovery by design.** Workers hold leases. A dead worker's attempt is
|
|
81
|
+
expired and reassigned, and a restarted coordinator rebuilds state by replay.
|
|
82
|
+
- **One owner per task.** Only the current attempt can advance a task, and late
|
|
83
|
+
results from a lost worker are rejected.
|
|
84
|
+
- **Dependencies.** Tasks form a DAG, wait for upstream results, and propagate
|
|
85
|
+
failure downstream.
|
|
86
|
+
- **Humans in the loop.** An agent can block with a question, such as approval
|
|
87
|
+
for a risky tool call. `logplane decide` answers it, and the answer is on
|
|
88
|
+
record.
|
|
89
|
+
- **Spend limits.** Model calls report tokens and cost per task and attempt.
|
|
90
|
+
Workflow budgets stop new work at a cap, and raising the cap is an event too.
|
|
91
|
+
- **Retry-safe external effects.** `assignment.effect_id(...)` is stable across
|
|
92
|
+
attempts, so a retry can skip work a crashed attempt already did.
|
|
93
|
+
- **Bring your own agents.** Wrap a Python object, a CLI, or an HTTP service.
|
|
94
|
+
logplane never needs your agent's internals.
|
|
95
|
+
- **Explainable state.** `logplane task`, `explain`, and `workflow --mermaid`
|
|
96
|
+
answer "why is this waiting?" from the log.
|
|
97
|
+
|
|
98
|
+
It is local-first: one machine, SQLite, loopback only. See
|
|
99
|
+
[trust and scope](#trust-and-scope) for what it does not do yet.
|
|
100
|
+
|
|
101
|
+
## How it works
|
|
102
|
+
|
|
103
|
+
The bus never stores a mutable card as truth. Every action (a task created, an
|
|
104
|
+
attempt assigned, a worker's result, a human decision) is an immutable SQLite
|
|
105
|
+
event. The project manager (PM) derives current state by replaying those
|
|
106
|
+
events and reconciles anything missing, so any process can crash and restart
|
|
107
|
+
without losing ownership state.
|
|
108
|
+
|
|
109
|
+
- **You and your tools publish intent:** tasks, dependencies, policies,
|
|
110
|
+
decisions, cancellations.
|
|
111
|
+
- **The PM** replays the log, assigns work to live workers, and records lease
|
|
112
|
+
expiries, retries, decision requests, and terminal states.
|
|
113
|
+
- **Workers** execute only assignments addressed to their current process and
|
|
114
|
+
report one outcome per attempt.
|
|
115
|
+
- **Every view** (`task`, `explain`, `workflow`) is rebuilt from the log.
|
|
116
|
+
|
|
117
|
+
Four identities keep retries and replays unambiguous: `correlation_id` (one
|
|
118
|
+
workflow), `task_id` (one logical outcome), `assignment_id` (one execution
|
|
119
|
+
attempt), and `instance_id` (one worker process). The normal lifecycle is
|
|
120
|
+
`task.created → task.assigned → task.started → task.completed`; recovery,
|
|
121
|
+
human input, cancellation, and pauses are explicit events too.
|
|
122
|
+
|
|
123
|
+
[How logplane works](docs/architecture.md) covers the full lifecycle,
|
|
124
|
+
components, invariants, crash recovery, and trust model.
|
|
125
|
+
|
|
126
|
+
## Install
|
|
127
|
+
|
|
128
|
+
Use macOS or Linux with Python 3.10+; native Windows is not supported (see the
|
|
129
|
+
[tested-platform policy](COMPATIBILITY.md#supported-deployment-target)).
|
|
130
|
+
|
|
131
|
+
```sh
|
|
132
|
+
git clone --branch v0.13.0 https://github.com/dekizk/logplane.git
|
|
133
|
+
cd logplane
|
|
134
|
+
python3 -m venv .venv && . .venv/bin/activate # python3 --version must be 3.10+
|
|
135
|
+
python -m pip install -e .
|
|
136
|
+
logplane init
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Use `--branch main` instead for the latest development version, and follow
|
|
140
|
+
the README for the version you installed. If you already have a checkout, use
|
|
141
|
+
it rather than cloning over it, and record `git rev-parse HEAD` when reporting
|
|
142
|
+
trial results. Create the environment in `.venv`, not in the repository root.
|
|
143
|
+
If your default `python3` is older than 3.10, create `.venv` with a supported
|
|
144
|
+
interpreter such as `python3.11`. The editable install keeps this checkout as
|
|
145
|
+
the source of the `logplane` command, so after pulling changes that add
|
|
146
|
+
modules (for example `demo.py`), rerun `python -m pip install -e .`. logplane is not yet
|
|
147
|
+
published on PyPI.
|
|
148
|
+
|
|
149
|
+
## Run your own bus
|
|
150
|
+
|
|
151
|
+
Run these in four terminals from the checkout, with `.venv` activated in each.
|
|
152
|
+
The generated configuration binds only to loopback and keeps runtime data in
|
|
153
|
+
the ignored `.logplane/` directory.
|
|
154
|
+
|
|
155
|
+
```sh
|
|
156
|
+
# terminal 1
|
|
157
|
+
logplane serve
|
|
158
|
+
|
|
159
|
+
# terminal 2
|
|
160
|
+
logplane pm
|
|
161
|
+
|
|
162
|
+
# terminal 3
|
|
163
|
+
logplane demo-worker alice
|
|
164
|
+
|
|
165
|
+
# terminal 4
|
|
166
|
+
RESPONSE=$(logplane submit "Verify the logplane quick start" --json)
|
|
167
|
+
TASK_ID=$(printf '%s' "$RESPONSE" | python -c \
|
|
168
|
+
'import json,sys; print(json.load(sys.stdin)["payload"]["task_id"])')
|
|
169
|
+
CORRELATION_ID=$(printf '%s' "$RESPONSE" | python -c \
|
|
170
|
+
'import json,sys; print(json.load(sys.stdin)["correlation_id"])')
|
|
171
|
+
printf 'Task: %s\nWorkflow: %s\n' "$TASK_ID" "$CORRELATION_ID"
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
After about one second, verify the bus, task, and derived workflow:
|
|
175
|
+
|
|
176
|
+
```sh
|
|
177
|
+
logplane doctor
|
|
178
|
+
logplane task "$TASK_ID"
|
|
179
|
+
logplane workflow "$CORRELATION_ID"
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
The expected state is a healthy schema-v2 bus with one healthy worker, a
|
|
183
|
+
`completed` task whose last attempt belongs to `alice`, and a one-task
|
|
184
|
+
`completed` workflow. The demo worker emits no model telemetry, so
|
|
185
|
+
`tokens not reported` and `cost not reported` are expected rather than errors.
|
|
186
|
+
|
|
187
|
+
<details>
|
|
188
|
+
<summary>Machine-readable pass/fail checks</summary>
|
|
189
|
+
|
|
190
|
+
```sh
|
|
191
|
+
logplane task "$TASK_ID" --json | python -c '
|
|
192
|
+
import json, sys
|
|
193
|
+
task = json.load(sys.stdin)
|
|
194
|
+
assert task["status"] == "completed", task
|
|
195
|
+
assert task["assignment_active"] is False, task
|
|
196
|
+
print("PASS: task is completed with no active assignment")
|
|
197
|
+
'
|
|
198
|
+
|
|
199
|
+
logplane workflow "$CORRELATION_ID" --json | python -c '
|
|
200
|
+
import json, sys
|
|
201
|
+
workflow = json.load(sys.stdin)
|
|
202
|
+
assert workflow["status"] == "completed", workflow
|
|
203
|
+
assert workflow["task_count"] == 1, workflow
|
|
204
|
+
print("PASS: workflow reports one completed task")
|
|
205
|
+
'
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
</details>
|
|
209
|
+
|
|
210
|
+
If the task is still `assigned` or `started`, wait briefly and check again. If
|
|
211
|
+
`logplane doctor` keeps warning, the PM, worker, or bus is probably using a
|
|
212
|
+
different URL; see [connecting to the bus](docs/operations.md#connecting-to-the-bus).
|
|
213
|
+
If `logplane` is not found, check that the terminal is in the checkout with
|
|
214
|
+
`.venv` activated before reinstalling. `logplane tasks` and
|
|
215
|
+
`logplane workflows` find IDs again if you lose them, and
|
|
216
|
+
`logplane workflow "$CORRELATION_ID" --mermaid` prints the workflow as a
|
|
217
|
+
Mermaid flowchart: another projection over the log, never an editable board.
|
|
218
|
+
|
|
219
|
+
These checks inspect projected state; they do not prove exactly-once execution
|
|
220
|
+
or external effects. To stop, press Ctrl-C in the worker terminal, then the PM,
|
|
221
|
+
then the server. Local history is preserved.
|
|
222
|
+
|
|
223
|
+
### Try dependencies, decisions, and failures
|
|
224
|
+
|
|
225
|
+
The demo worker can deliberately block, fail, or work slowly so you can watch
|
|
226
|
+
the other paths without writing an agent. Use one outcome flag per worker; the
|
|
227
|
+
outcome happens once for that task ID.
|
|
228
|
+
|
|
229
|
+
```sh
|
|
230
|
+
logplane demo-worker alice --block 2 # task 2 asks a human for a decision
|
|
231
|
+
logplane demo-worker alice --fail 2 # one retryable failure
|
|
232
|
+
logplane demo-worker alice --fail-permanently 2
|
|
233
|
+
logplane demo-worker alice --delay 30 # slow enough to kill mid-task
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
`submit --depends-on` builds a DAG from the command line; repeat it for
|
|
237
|
+
fan-in. Dependents inherit the upstream workflow automatically (IDs below
|
|
238
|
+
assume a fresh bus):
|
|
239
|
+
|
|
240
|
+
```sh
|
|
241
|
+
logplane submit "Research options" # task 1
|
|
242
|
+
logplane submit "Design storage" --depends-on 1 # task 2
|
|
243
|
+
logplane submit "Write docs" --depends-on 1 # task 3
|
|
244
|
+
logplane submit "Ship it" --depends-on 2 --depends-on 3 # task 4
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
A lease expiry or retryable failure consumes one retry, and `submit` defaults
|
|
248
|
+
to no retries. If you plan to use `--fail` or kill a worker mid-task, submit
|
|
249
|
+
with `--max-retries 2` (or higher) so the next attempt is allowed; otherwise
|
|
250
|
+
the task correctly ends `failed`.
|
|
251
|
+
|
|
252
|
+
## Everyday commands
|
|
253
|
+
|
|
254
|
+
| Command | What it does |
|
|
255
|
+
|---|---|
|
|
256
|
+
| `logplane submit TITLE` | Create a task (`--depends-on`, `--capability`, `--max-retries`, `--priority`, `--context`) |
|
|
257
|
+
| `logplane tasks` / `workflows` / `decisions` | List work, workflows, and pending questions with next-step commands |
|
|
258
|
+
| `logplane task ID` / `explain ID` | Show one task's state and exactly why it is waiting or finished |
|
|
259
|
+
| `logplane workflow ID` | Show a workflow's tasks, dependencies, policy, and token/cost usage (`--mermaid` for a diagram) |
|
|
260
|
+
| `logplane tail ID` | Follow a workflow's events live |
|
|
261
|
+
| `logplane decide` / `retry` / `cancel` | Answer a decision, grant more retries, or cancel a task |
|
|
262
|
+
| `logplane pause` / `resume` / `supersede` | Pause or resume a task or workflow, or replace a task with new intent |
|
|
263
|
+
| `logplane policy` / `agent-policy` | Set workflow concurrency and budgets, or per-agent limits |
|
|
264
|
+
| `logplane workers` / `doctor` | Check worker leases and overall bus health |
|
|
265
|
+
| `logplane adapter check` / `adapter run` | Verify and run an existing agent as a worker |
|
|
266
|
+
| `logplane backup` / `verify-backup` / `restore` | Verified local backups ([recovery operations](RECOVERY.md)) |
|
|
267
|
+
|
|
268
|
+
Finite commands accept `--json` for scripts. [Operating logplane](docs/operations.md)
|
|
269
|
+
covers every command, scheduling, fairness, budgets, and controls in detail.
|
|
270
|
+
|
|
271
|
+
## Connect your own agent
|
|
272
|
+
|
|
273
|
+
Wrap an existing agent instead of rewriting it. A Python object with `run()`
|
|
274
|
+
becomes a worker:
|
|
275
|
+
|
|
276
|
+
```python
|
|
277
|
+
from logplane import BusClient, Completed, PythonAgentAdapter, WorkerRuntime
|
|
278
|
+
|
|
279
|
+
class ExistingAgent:
|
|
280
|
+
def run(self, assignment):
|
|
281
|
+
result = do_existing_work(
|
|
282
|
+
goal=assignment.goal,
|
|
283
|
+
context=assignment.context,
|
|
284
|
+
idempotency_key=assignment.effect_id("do-existing-work"),
|
|
285
|
+
)
|
|
286
|
+
return Completed("agent finished", {"result_ref": result.ref})
|
|
287
|
+
|
|
288
|
+
bus = BusClient("http://127.0.0.1:8765", actor="existing-agent")
|
|
289
|
+
WorkerRuntime(
|
|
290
|
+
bus,
|
|
291
|
+
name="existing-agent",
|
|
292
|
+
executor=PythonAgentAdapter(ExistingAgent()),
|
|
293
|
+
capacity=2,
|
|
294
|
+
capabilities=["python"],
|
|
295
|
+
).run()
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
Check the contract before connecting it to a live bus:
|
|
299
|
+
|
|
300
|
+
```sh
|
|
301
|
+
logplane adapter check --python-target my_package.agent:ExistingAgent
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
CLI and HTTP agents use the same versioned protocol through a small JSON
|
|
305
|
+
config. Use `assignment.effect_id(...)` for any irreversible external effect so
|
|
306
|
+
a retry cannot repeat it. [Integrating agents](docs/integration.md) covers the
|
|
307
|
+
adapters, the safety contract, telemetry, and safe adoption alongside an
|
|
308
|
+
existing system; [`examples/`](examples/README.md) has copyable Python, CLI,
|
|
309
|
+
HTTP, and real-agent integrations.
|
|
310
|
+
|
|
311
|
+
## Documentation
|
|
312
|
+
|
|
313
|
+
| Page | Contents |
|
|
314
|
+
|---|---|
|
|
315
|
+
| [How logplane works](docs/architecture.md) | Event log, identities, lifecycle, components, invariants, crash recovery, trust model |
|
|
316
|
+
| [Operating logplane](docs/operations.md) | Commands, decisions, retries, cancellation, pauses, DAGs, scheduling, fairness, budgets |
|
|
317
|
+
| [Integrating agents](docs/integration.md) | Python, CLI, and HTTP adapters, safety contract, telemetry, adoption modes |
|
|
318
|
+
| [Event contracts and the HTTP API](docs/events.md) | Envelope, topics, limits, reading and publishing events |
|
|
319
|
+
| [Local recovery operations](RECOVERY.md) | Artifact audits, backup and restore, task-index rebuilds, PM snapshots |
|
|
320
|
+
| [Compatibility and support](COMPATIBILITY.md) | Platforms, versioned surfaces, upgrades, release checks |
|
|
321
|
+
| [Roadmap](ROADMAP.md) and [release notes](RELEASE_NOTES.md) | Direction, remaining v1.0 gates, and what changed in each release |
|
|
322
|
+
|
|
323
|
+
## Trust and scope
|
|
324
|
+
|
|
325
|
+
logplane is for local, single-user orchestration on one trusted host:
|
|
326
|
+
|
|
327
|
+
- **Perimeter auth is opt-in.** Set `LOGPLANE_TOKEN` to require a bearer token
|
|
328
|
+
on every data route; without it, anyone who can reach the port can publish.
|
|
329
|
+
- **Actors are self-reported.** The token authenticates clients, not
|
|
330
|
+
identities, so any authorized client may publish as any actor, including
|
|
331
|
+
`pm`.
|
|
332
|
+
- **It is not distributed.** Run one PM per bus and one uvicorn worker; the PM
|
|
333
|
+
lock is per machine and event wake-ups are per process.
|
|
334
|
+
|
|
335
|
+
See the [trust model and scope](docs/architecture.md#trust-model) for details.
|
|
336
|
+
|
|
337
|
+
## Development
|
|
338
|
+
|
|
339
|
+
```sh
|
|
340
|
+
python -m pip install -r requirements-dev.txt
|
|
341
|
+
python -m pytest -q
|
|
342
|
+
python -m build
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
The suite focuses on orchestration failures (restarts, stale attempts, retry
|
|
346
|
+
exhaustion, races, DAG propagation, adapter contracts, telemetry isolation)
|
|
347
|
+
and includes live runs of every demo. See
|
|
348
|
+
[running the release checks](COMPATIBILITY.md#running-the-release-checks) for
|
|
349
|
+
the full pre-release procedure.
|
|
350
|
+
|
|
351
|
+
## Status and license
|
|
352
|
+
|
|
353
|
+
logplane was called agent-bus before v0.13.0; if you used it under that name,
|
|
354
|
+
follow the [migration steps](RELEASE_NOTES.md#migrating-from-agent-bus).
|
|
355
|
+
|
|
356
|
+
Current release: **v0.13.0**, the rename to logplane and a `src/` layout. See the
|
|
357
|
+
[release notes](RELEASE_NOTES.md) and the [roadmap](ROADMAP.md) for the path to
|
|
358
|
+
v1.0. Licensed under the [MIT License](LICENSE). Copyright (c) 2026 deki. See
|
|
359
|
+
[credits and acknowledgements](CONTRIBUTORS.md).
|