mirror of
https://github.com/razzant/ouroboros.git
synced 2026-10-03 04:07:04 +00:00
`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>
91 lines
4.4 KiB
Python
91 lines
4.4 KiB
Python
"""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
|