Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions src/nilscript/dataplane/intent.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,27 @@ class Binding:
value: Any


# op → the universal generic-CRUD verb every adapter supports (no adapter-specific verb map, no
# keywords): a change intent executes through resource.* — the deterministic write spine.
# op → the universal generic verb every adapter supports (no adapter-specific verb map, no keywords):
# a change intent executes through resource.* — the deterministic write spine. `method` covers workflow
# actions that are NOT CRUD (post an invoice, validate a picking) via the adapter's generic op=method.
OP_TO_RESOURCE: dict[str, str] = {
"create": "resource.create",
"update": "resource.update",
"remove": "resource.delete",
"method": "resource.method",
}


@dataclass(frozen=True)
class Change:
"""The write shape of an intent: what to make true. Resolved → propose→commit→tier (governed)."""

op: str # create | update | remove
op: str # create | update | remove | method
set: dict[str, Any] = field(default_factory=dict)
# for op="method": the workflow method to invoke (action_post / button_validate / …) and its kwargs.
# The adapter's allow-list (default-deny) still governs which (model, method) pairs are committable.
method: str | None = None
params: dict[str, Any] = field(default_factory=dict)


@dataclass(frozen=True)
Expand Down
12 changes: 12 additions & 0 deletions tests/test_intent_reads.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,15 @@ def test_seek_summary_groups_via_aggregate() -> None:
def test_seek_summary_without_a_dimension_is_a_refusal() -> None:
out = _resolver().resolve(Intent(about="res.partner", where=(), seek="summary"))
assert out.kind == "refusal" and out.code == "MISSING_DIMENSION"


# ── op=method contract readiness (Odoo full-coverage gap 3) ───────────────────────────────────────
def test_change_contract_supports_method_op() -> None:
"""The intent write contract carries `method`/`params` and maps op=method → the adapter's generic
resource.method spine, so workflow actions (post/validate/confirm) are expressible through the one
nil_intent payload. The execution provider that consumes this is the intent-unification phase."""
from nilscript.dataplane import OP_TO_RESOURCE, Change

assert OP_TO_RESOURCE["method"] == "resource.method"
change = Change(op="method", method="button_validate", params={"foo": "bar"})
assert change.op == "method" and change.method == "button_validate" and change.params == {"foo": "bar"}
Loading