plainweave 1.0.0__py3-none-any.whl
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.
- plainweave/__init__.py +5 -0
- plainweave/__main__.py +8 -0
- plainweave/_version.py +1 -0
- plainweave/bindings.py +98 -0
- plainweave/cli.py +35 -0
- plainweave/cli_commands.py +1584 -0
- plainweave/envelopes.py +115 -0
- plainweave/errors.py +34 -0
- plainweave/intent_graph.py +184 -0
- plainweave/loomweave_adapter.py +625 -0
- plainweave/mcp_server.py +172 -0
- plainweave/mcp_surface.py +1489 -0
- plainweave/models.py +310 -0
- plainweave/paths.py +24 -0
- plainweave/service.py +3018 -0
- plainweave/store.py +311 -0
- plainweave-1.0.0.dist-info/METADATA +160 -0
- plainweave-1.0.0.dist-info/RECORD +21 -0
- plainweave-1.0.0.dist-info/WHEEL +4 -0
- plainweave-1.0.0.dist-info/entry_points.txt +3 -0
- plainweave-1.0.0.dist-info/licenses/LICENSE +21 -0
plainweave/__init__.py
ADDED
plainweave/__main__.py
ADDED
plainweave/_version.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.0.0"
|
plainweave/bindings.py
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""Plainweave ↔ Loomweave SEI bindings via the ADR-029 entity-association contract.
|
|
2
|
+
|
|
3
|
+
Code leaves are keyed by **Loomweave SEI** (``loomweave:eid:...``), so a binding
|
|
4
|
+
survives rename/move (design §3). Bindings **reuse the ADR-029 entity-association
|
|
5
|
+
contract** — SEI-keyed, with ``content_hash_at_attach`` drift detection, the same
|
|
6
|
+
pattern Filigree uses to bind issues to code — **not** a new link store
|
|
7
|
+
(design §4).
|
|
8
|
+
|
|
9
|
+
The write path is **authoring-time** ("speak SEI at entry," extended to intent,
|
|
10
|
+
design §5): when an agent creates or commits a module / public entity, Plainweave
|
|
11
|
+
offers an inline bind — link this SEI to a requirement (existing or a freshly
|
|
12
|
+
minted shell) and optionally ladder that requirement to a goal. One call,
|
|
13
|
+
attributed (who bound it, when). Code that skips the bind is exactly what
|
|
14
|
+
surfaces as an orphan via :mod:`plainweave.intent_graph`.
|
|
15
|
+
|
|
16
|
+
The ``loomweave:eid:`` SEI scheme is **FROZEN** — Plainweave consumes it, never
|
|
17
|
+
mints or reinterprets it. Persistence lives in :mod:`plainweave.service`; this
|
|
18
|
+
module holds the public value object and small drift helpers used by CLI/MCP
|
|
19
|
+
serializers.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
from dataclasses import dataclass
|
|
25
|
+
from datetime import UTC, datetime
|
|
26
|
+
from typing import Any
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass(frozen=True)
|
|
30
|
+
class SeiBinding:
|
|
31
|
+
"""A code-leaf ↔ requirement binding, recorded via the ADR-029
|
|
32
|
+
entity-association contract.
|
|
33
|
+
|
|
34
|
+
``sei`` is an opaque ``loomweave:eid:...`` identifier (frozen scheme;
|
|
35
|
+
consumed, never minted). ``content_hash_at_attach`` is stored so the consumer
|
|
36
|
+
read path can detect drift between the bound entity's content and its content
|
|
37
|
+
when the binding was made (ADR-029).
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
entity_id: str
|
|
41
|
+
entity_kind: str
|
|
42
|
+
requirement_id: str
|
|
43
|
+
content_hash_at_attach: str | None
|
|
44
|
+
drift_status: str
|
|
45
|
+
freshness: str
|
|
46
|
+
bound_by: str
|
|
47
|
+
bound_at: str
|
|
48
|
+
provenance: dict[str, Any]
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def sei(self) -> str:
|
|
52
|
+
"""Backward-compatible alias for the opaque Loomweave entity ID."""
|
|
53
|
+
return self.entity_id
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def bind_sei_to_requirement(
|
|
57
|
+
sei: str,
|
|
58
|
+
requirement_id: str,
|
|
59
|
+
*,
|
|
60
|
+
bound_by: str,
|
|
61
|
+
content_hash_at_attach: str | None = None,
|
|
62
|
+
entity_kind: str = "loomweave_entity",
|
|
63
|
+
bound_at: str | None = None,
|
|
64
|
+
provenance: dict[str, Any] | None = None,
|
|
65
|
+
) -> SeiBinding:
|
|
66
|
+
"""Construct a SEI binding value object for the ADR-029 contract.
|
|
67
|
+
|
|
68
|
+
The service method with the same semantic name persists this object. This
|
|
69
|
+
module-level helper stays storage-free so callers can validate envelope
|
|
70
|
+
shapes without opening a project database.
|
|
71
|
+
"""
|
|
72
|
+
if not sei:
|
|
73
|
+
raise ValueError("sei is required")
|
|
74
|
+
if not requirement_id:
|
|
75
|
+
raise ValueError("requirement_id is required")
|
|
76
|
+
if not bound_by:
|
|
77
|
+
raise ValueError("bound_by is required")
|
|
78
|
+
return SeiBinding(
|
|
79
|
+
entity_id=sei,
|
|
80
|
+
entity_kind=entity_kind,
|
|
81
|
+
requirement_id=requirement_id,
|
|
82
|
+
content_hash_at_attach=content_hash_at_attach,
|
|
83
|
+
drift_status="unknown" if content_hash_at_attach is None else "attached",
|
|
84
|
+
freshness="unknown" if content_hash_at_attach is None else "current",
|
|
85
|
+
bound_by=bound_by,
|
|
86
|
+
bound_at=bound_at or datetime.now(UTC).isoformat(),
|
|
87
|
+
provenance=dict(provenance or {}),
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def is_drifted(binding: SeiBinding, current_content_hash: str) -> bool:
|
|
92
|
+
"""Whether the bound entity's content has changed since the binding was made
|
|
93
|
+
(``content_hash_at_attach`` vs. ``current_content_hash``; ADR-029 drift).
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
if binding.content_hash_at_attach is None:
|
|
97
|
+
return False
|
|
98
|
+
return binding.content_hash_at_attach != current_content_hash
|
plainweave/cli.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
from collections.abc import Callable, Sequence
|
|
5
|
+
from typing import cast
|
|
6
|
+
|
|
7
|
+
from plainweave import __version__
|
|
8
|
+
from plainweave.cli_commands import register_commands
|
|
9
|
+
|
|
10
|
+
DESCRIPTION = "Plainweave requirements and verification authority."
|
|
11
|
+
EPILOG = "Local-core commands are available for requirements, criteria, trace, init, and diagnostics."
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
15
|
+
parser = argparse.ArgumentParser(prog="plainweave", description=DESCRIPTION, epilog=EPILOG)
|
|
16
|
+
parser.add_argument("--version", action="store_true", help="Print the Plainweave version and exit.")
|
|
17
|
+
subparsers = parser.add_subparsers(dest="command")
|
|
18
|
+
register_commands(subparsers)
|
|
19
|
+
return parser
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def main(argv: Sequence[str] | None = None) -> int:
|
|
23
|
+
parser = build_parser()
|
|
24
|
+
if argv is not None and list(argv) in (["-h"], ["--help"]):
|
|
25
|
+
parser.print_help()
|
|
26
|
+
return 0
|
|
27
|
+
args = parser.parse_args(argv)
|
|
28
|
+
if args.version:
|
|
29
|
+
print(f"plainweave {__version__}")
|
|
30
|
+
return 0
|
|
31
|
+
handler = getattr(args, "handler", None)
|
|
32
|
+
if handler is not None:
|
|
33
|
+
return cast(Callable[[argparse.Namespace], int], handler)(args)
|
|
34
|
+
parser.print_help()
|
|
35
|
+
return 0
|