ouroboros/tests/test_complete_chosen_inputs.py
Ouroboros 82de26a2fd Resolve only declared file targets as the plan's constitutional fact
`plan_spec.resolve_constitutional` read every non-URL string in the spec's
`affected_resources` list as a file path. On the live install that turned the
prose item «Отдельный проект «TSMC — 10-летний инвестиционный анализ»» into "a
path under the Ouroboros repository": the plan was marked constitutional and
every reviewer of a deck-shaped plan received BIBLE.md plus ARCHITECTURE.md in
full, ~470k tokens per cycle. Across the recorded history not one of the 14
constitutional verdicts came from a real file path in that list, and the model
never once spelled a target as `file://` (0 of 186 locators).

The spec now says the two things separately (owner decisions 8=A, 9=A, 16=A):

* `affected_paths` — REQUIRED — is the files the work will CHANGE, and the only
  list the host resolves. `[]` is a real claim: "this work changes no files". A
  path under the system repository decides whether or not the file exists yet,
  because creating `ouroboros/new_module.py` IS self-modification.
* `affected_resources` is prose: systems, services, projects, people. Nothing in
  it reaches the filesystem.
* An `evidence` locator is something to LOOK AT. Reading a repository file is not
  changing it, so it no longer buys the pack on its own (16=A); when the work
  will also change that file, the file is in `affected_paths` and decides there.
  System-repo evidence reads are named in the disclosure note, so an author who
  expected the constitution can see why it did not come. Existence stopped
  deciding anything, so `_default_exists`/`evidence_exists` are gone with it.

A NEW spec submitted in the old mixed form is refused before any dispatch with
`PLAN_RESOURCE_FORM_REQUIRED`, quoting the field with a two-line example. The
refusal deliberately does NOT carry the `PLAN_SPEC_INVALID` token: that family
records a superseding attempt on the way out and would orphan an open wave, so
this one writes nothing and, when a wave is open, names its fingerprint and the
free `review_disposition` exit. Waves recorded under the old shape keep their
identity: `affected_paths` is never defaulted onto stored input, so their
`spec_hash` and `plan_fingerprint` are byte-identical (pinned against the three
live examples) and they still close at $0. `spec_delta` reports the appearance of
the key itself, because "stored before the field existed" and "changes no files"
are different claims.

No grace mechanism for the 31 open waves — already disclosed to the owner.

Co-authored-by: Ouroboros <311266734+ouroboros-agent@users.noreply.github.com>
2026-09-15 17:08:58 +03:00

91 lines
4.4 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Complete chosen assignments and operative plans at their actual consumer seams."""
from copy import deepcopy
import json
import pytest
from ouroboros.tools import plan_packet, plan_spec
@pytest.mark.parametrize("field", ["goal", "in_scope", "non_goals", "invariants",
"affected_paths", "affected_resources", "evidence",
"acceptance_claims", "decisions", "deferred"])
def test_operative_tail_changes_normalized_identity_and_current_packet(field):
prefix = "яё𐍈🚀\n" * 500
raw = {"goal": "Full plan", field: prefix + "TAIL_A"}
if field != "goal":
# Also cross the former 40-item list cap; the decisive item is last.
raw[field] = [f"first {i}" for i in range(40)] + [prefix + "TAIL_A"]
before, errors = plan_spec.normalize_spec(raw)
assert not errors
changed = deepcopy(raw)
if field == "goal":
changed[field] = prefix + "TAIL_B"
else:
changed[field][-1] = prefix + "TAIL_B"
after, errors = plan_spec.normalize_spec(changed)
assert not errors
assert plan_spec.spec_hash(before) != plan_spec.spec_hash(after)
assert plan_spec.spec_delta(before, after)["changed"]
serialized = json.dumps(after, ensure_ascii=False)
assert "TAIL_B" in serialized and "TAIL_A" not in serialized
packet = plan_packet.build_plan_review_user_content(
objective="Parent goal", goal=after["goal"], plan_prose="Chosen plan",
spec=after, manifest={}, prior_cycles=[], dispositions=[],
spec_delta=None, root_exploration_log=None,
)
assert "TAIL_B" in packet and "OMISSION NOTE" not in packet
@pytest.mark.parametrize("collection,key", [("decisions", "choice"), ("decisions", "why"),
("decisions", "rejected"), ("deferred", "what"),
("deferred", "why_safe_to_defer")])
def test_nested_operative_tails_survive(collection, key):
tail = "строка\n" * 200 + "DECISIVE_NESTED_TAIL"
item = {"choice": "choice"} if collection == "decisions" else {"what": "later"}
item[key] = [f"rejected {i}" for i in range(8)] + [tail] if key == "rejected" else tail
raw = {"goal": "Full plan", collection: [item]}
spec, errors = plan_spec.normalize_spec(raw)
assert not errors
assert spec[collection][0][key] == item[key]
changed = deepcopy(raw)
changed[collection][0][key] = item[key] + ["next"] if key == "rejected" else tail + "next"
assert plan_spec.spec_hash(spec) != plan_spec.spec_hash(plan_spec.normalize_spec(changed)[0])
def test_large_direct_request_keeps_chosen_prompt_and_host_instruction_roles(tmp_path, monkeypatch):
from ouroboros import claudexor_daemon
from ouroboros.gateways import claudexor
from tests.test_nanny_economics import _start_with_contract
# Importing the helper does not activate its module's autouse fixture.
monkeypatch.setattr(claudexor_daemon, "ensure_owned_gateway", lambda: claudexor.ClaudexorGateway())
prompt = " \n" + "яё𐍈🚀\n" * 55_000 + "CHOSEN_ASSIGNMENT_TAIL\n "
objective = "HOST_OBJECTIVE:" + "О" * 250_001
expected = "HOST_EXPECTED:" + "Е" * 250_001
request = _start_with_contract(tmp_path, monkeypatch, {
"objective": objective, "expected_output": expected,
"context": " \nHOST_REFERENCE_CONTEXT\n ",
}, prompt=prompt)
assert request["prompt"] == prompt
instructions = request["instructions"]
assert instructions.count(objective) == 1 and instructions.count(expected) == 1
marker = "HOST TASK CONTRACT AUTHORITY (complete normalized JSON; exact strings are authority):\n"
host = json.loads(instructions.split(marker, 1)[1])
assert host["objective"] == objective and host["expected_output"] == expected
assert host["context"] == " \nHOST_REFERENCE_CONTEXT\n "
assert "CHOSEN_ASSIGNMENT_TAIL" not in instructions
def test_current_packet_keeps_full_parent_objective_and_plan_prose():
objective = " \n" + "О" * 8_001 + "OBJECTIVE_TAIL\n "
prose = " \n" + "П" * 40_001 + "PROSE_TAIL\n "
spec, errors = plan_spec.normalize_spec({"goal": "g"})
assert not errors
packet = plan_packet.build_plan_review_user_content(
objective=objective, goal="g", plan_prose=prose, spec=spec, manifest={},
prior_cycles=[], dispositions=[], spec_delta=None, root_exploration_log=None,
)
assert objective in packet and prose in packet
assert "OMISSION NOTE" not in packet