WARDEN-WP-0044-T03: original-intent lookup/context for owner reconciliation
Durably record the original run Invocation, pins, adapter source and tenant/realm provenance before dispatch; expose Journal.original_intent and Adapter.reconcile_original(ReconcileContext) for NK-WP-0048-T15. Missing or mismatched intent refuses and leaves the step uncertain. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 903097@bnt-lap001 Assistant-Session: aa320c8f-c3fa-46e7-8541-0b58e1e45d23
This commit is contained in:
parent
d97652a3bb
commit
a45cf475bc
4 changed files with 191 additions and 7 deletions
|
|
@ -629,3 +629,72 @@ def test_secret_sensitivity_parameter_cannot_reach_admission_or_journal(rig):
|
|||
)
|
||||
with pytest.raises(Refused, match="secret-sensitivity"):
|
||||
executor.admit(sources[0], source, sources[2], sources[3], **kwargs)
|
||||
|
||||
|
||||
def _lose_result(executor, args, calls):
|
||||
old = executor.adapters[args[0].adapter]
|
||||
|
||||
def lost(inv):
|
||||
calls.append(inv)
|
||||
raise TimeoutError("lost")
|
||||
|
||||
seen = []
|
||||
|
||||
def by_original(ctx):
|
||||
seen.append(ctx)
|
||||
return old.reconcile(ctx.attempt)
|
||||
|
||||
executor.adapters[old.reference] = replace(
|
||||
old, invoke=lost, reconcile=None, reconcile_original=by_original
|
||||
)
|
||||
assert executor.run(*args).last.outcome is Outcome.UNCERTAIN
|
||||
return seen
|
||||
|
||||
|
||||
def test_original_intent_survives_restart_separate_from_reconcile_attempt(rig):
|
||||
executor, args, calls, _, _ = rig
|
||||
seen = _lose_result(executor, args, calls)
|
||||
executor.journal = Journal(executor.journal.directory) # restart: fresh handle
|
||||
assert executor.reconcile(*args).last.outcome is Outcome.NO_CHANGE
|
||||
(ctx,) = seen
|
||||
original = ctx.original.invocation
|
||||
assert original.attempt_id == calls[0].attempt_id
|
||||
assert original.operation == "run" and ctx.attempt.operation == "reconcile"
|
||||
assert ctx.attempt.attempt_id != original.attempt_id
|
||||
assert original.pins == args[1].pins
|
||||
assert (original.principal, original.tenant, original.realm) == (
|
||||
args[1].principal,
|
||||
args[1].tenant,
|
||||
args[1].realm,
|
||||
)
|
||||
assert ctx.original.source == executor.adapters[args[0].adapter].source
|
||||
|
||||
|
||||
def test_missing_original_intent_refuses_and_stays_uncertain(rig):
|
||||
executor, args, calls, _, _ = rig
|
||||
seen = _lose_result(executor, args, calls)
|
||||
executor.journal.db.execute("DELETE FROM intents")
|
||||
with pytest.raises(Refused, match="original intent record missing"):
|
||||
executor.reconcile(*args)
|
||||
assert seen == []
|
||||
binding = f"{args[1].step}:{args[1].target}"
|
||||
assert executor.journal.load(binding, args[2]).last.outcome is Outcome.UNCERTAIN
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"change", [{"tenant": "other-tenant"}, {"realm": "other-realm"}, {"lane": "x"}]
|
||||
)
|
||||
def test_original_intent_mismatch_refuses(rig, change):
|
||||
executor, args, calls, _, _ = rig
|
||||
seen = _lose_result(executor, args, calls)
|
||||
with pytest.raises(Refused):
|
||||
executor.reconcile(args[0], replace(args[1], **change), args[2])
|
||||
assert seen == []
|
||||
|
||||
|
||||
def test_original_intent_source_mismatch_refuses(rig):
|
||||
executor, args, calls, _, _ = rig
|
||||
_lose_result(executor, args, calls)
|
||||
binding = f"{args[1].step}:{args[1].target}"
|
||||
with pytest.raises(Refused, match="source mismatch"):
|
||||
executor.journal.original_intent(binding, calls[0], "other-source")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue