diff --git a/docs/Development-Plan.md b/docs/Development-Plan.md index a63f20b..95b8adc 100644 --- a/docs/Development-Plan.md +++ b/docs/Development-Plan.md @@ -782,8 +782,11 @@ if the implementation differs. with code's list of the calls they replace, become a `compaction` event that renders in place of its range; a later one folds in the earlier. Failed notes are logged (`context.compaction_failed`) and fitting takes - over; `context.compacted` logs each one. Not yet in nisse-dev's helper - loop. + over; `context.compacted` logs each one. Not in nisse-dev's helper loop, + on purpose (operator, 2026-10-18): its token cap (`delegation.max_tokens`, + 200k, every call's whole prompt counted) ends a delegation at a prompt of + about 36k tokens, well short of `compact_at` on any window in use. Wire + `Compactor` in if that cap is raised several-fold. - **L6 fitting order:** first shorten old tool results to their first and last lines, then drop the oldest events with a note saying so; never the system prompt, the current plan or the most recent turns. View labels: diff --git a/tests/integration/test_trust_live.py b/tests/integration/test_trust_live.py index 99b6bcd..32208ab 100644 --- a/tests/integration/test_trust_live.py +++ b/tests/integration/test_trust_live.py @@ -1,9 +1,11 @@ """The trust dial's hard wall, on a real Gitea (D4, D5; M3.3): with the protection a `merge: approved` project has, the agent's own token can't merge its PR until an operator approves the PR as it is now; it can then; -and a commit pushed after the approval needs approving again.""" +and a commit pushed after the approval needs approving again: at once by the +agent's own check, and by Gitea once it has dismissed the approval.""" import base64 +import time import uuid from pathlib import Path @@ -15,6 +17,9 @@ from nisse_dev.config import AgentConfig pytestmark = pytest.mark.integration +# How long Gitea gets to dismiss an approval after a push (#162). +DISMISS_WAIT_S = 30.0 + def add_file(gitea: GiteaClient, repo: str, branch: str, path: str) -> None: gitea._request( @@ -34,6 +39,18 @@ def approve(operator: GiteaClient, repo: str, pr: int) -> None: ) +def wait_for_dismissal(gitea: GiteaClient, repo: str, pr: int) -> None: + """Gitea dismisses a stale approval in a job it queues on the push, not + in the push itself, and its merge check counts what isn't dismissed yet. + A merge tried before that job has run can still go through (#162).""" + deadline = time.monotonic() + DISMISS_WAIT_S + while gitea.approvals(repo, pr): # dismissed ones aren't listed + assert time.monotonic() < deadline, ( + f"Gitea didn't dismiss the approval within {DISMISS_WAIT_S}s" + ) + time.sleep(0.5) + + def test_merging_needs_the_operators_approval_of_the_pr_as_it_is( tmp_path: Path, live_config: AgentConfig, gitea: GiteaClient ) -> None: @@ -64,12 +81,19 @@ def test_merging_needs_the_operators_approval_of_the_pr_as_it_is( name, head="m-2", base="main", title="b", body="" ) approve(operator, name, second.number) + approved = gitea.pull_request(name, second.number).head_sha add_file(gitea, name, "m-2", "c.txt") # a fix pushed after the approval + # At once, with no waiting: the PR's head is read from the branch, + # so the agent sees the new commit, and the approval is for the old + # one. Its own check (an approval at the head, not stale) holds off. after = gitea.pull_request(name, second.number).head_sha + assert after != approved assert not [ a for a in gitea.approvals(name, second.number) if a.commit == after and not a.stale ] + # Gitea's own refusal comes once it has dismissed the approval. + wait_for_dismissal(gitea, name, second.number) with pytest.raises(GiteaError): gitea.merge_pull_request(name, second.number)