Fixes from the first live session of the new loop
Found live on 2026-10-07 (bring-up on nisse-testing-operator/nisse-bringup,
Opus as driver):
- The driver's prose nudge went into the system prompt, so the retry ended
on the model's own reply. Opus 5.5 refuses that ("does not support
assistant message prefill. The conversation must end with a user
message"). The nudge is now a harness fact in the conversation. The
delegation harness's prose path did the same and is fixed likewise.
- A helper's first request ended with its own delegation_started event,
rendered as an assistant message: every delegation would have failed on
Opus. The helper now sees it as the harness handing over its task.
- model_contract checks that every request ends on the user's side, so
scripted tests catch this from now on (each fix is mutation-checked).
- `status` (and `details`) in an item's thread were taken as replies: they
woke the driver and cancelled a bug's countdown. They now answer at once
and change nothing.
- The driver had no way to answer a remark without changing the item, so it
answered in prose. Every run woken by a message (or a review comment) now
offers `reply`, which answers where asked and returns to the very wait
that was interrupted.
Also: scripts/bringup-repo.sh makes the bring-up's test repository
(make_repo/delete_repo, split out of fresh_repo), and made repositories get
the agent's labels (bug, discuss, design).
Tests: test_loop_live_fixes.py (one per finding); the walk sends `status`
into threads (status_changes_nothing) and its scripted driver sometimes
answers remarks with reply.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
This commit is contained in:
1 parent
572bd9f8eb
commit
659915d309
9 files changed
+341
-42
No files matched your search
@@ -61,16 +61,14 @@ class FreshRepo:
|
||||
push_key: Path # this repository's deploy key, private half
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def fresh_repo(
|
||||
operator: GiteaClient, run: str, keys: Path, *, agent: str, seed: bool = True
|
||||
) -> Iterator[FreshRepo]:
|
||||
"""A repository of its own for one test run, as the operator would make a
|
||||
project's (D4, D8): seeded with a small Python package, the ``agent``
|
||||
account a collaborator with write access, the default branch protected (pushes only by the
|
||||
owner, an approval to merge), and a deploy key of its own, written under
|
||||
``keys``. Deleted afterwards, unless NISSE_E2E_KEEP_REPOS is set."""
|
||||
name = f"nisse-e2e-{run}"
|
||||
def make_repo(
|
||||
operator: GiteaClient, name: str, keys: Path, *, agent: str, seed: bool = True
|
||||
) -> FreshRepo:
|
||||
"""A repository as the operator would make a project's (D4, D8): seeded
|
||||
with a small Python package (or only a README), the ``agent`` account a
|
||||
collaborator with write access, the default branch protected (pushes only
|
||||
by the owner, an approval to merge), and a deploy key of its own, written
|
||||
under ``keys``. A failure part-way deletes what was made."""
|
||||
full = f"{OWNER}/{name}"
|
||||
operator._request(
|
||||
"POST",
|
||||
@@ -147,8 +145,35 @@ def fresh_repo(
|
||||
"required_approvals": 1,
|
||||
},
|
||||
)
|
||||
yield FreshRepo(full, key)
|
||||
# The labels the agent reads (D1), as a project would have them.
|
||||
for label, color in (
|
||||
("bug", "#ee0701"),
|
||||
("discuss", "#0e8a16"),
|
||||
("design", "#5319e7"),
|
||||
):
|
||||
operator._request(
|
||||
"POST", f"/repos/{full}/labels", json={"name": label, "color": color}
|
||||
)
|
||||
except BaseException:
|
||||
delete_repo(operator, full)
|
||||
raise
|
||||
return FreshRepo(full, key)
|
||||
|
||||
|
||||
def delete_repo(operator: GiteaClient, full: str) -> None:
|
||||
with contextlib.suppress(GiteaError):
|
||||
operator._request("DELETE", f"/repos/{full}")
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def fresh_repo(
|
||||
operator: GiteaClient, run: str, keys: Path, *, agent: str, seed: bool = True
|
||||
) -> Iterator[FreshRepo]:
|
||||
"""A repository of its own for one test run (make_repo), deleted
|
||||
afterwards unless NISSE_E2E_KEEP_REPOS is set."""
|
||||
repo = make_repo(operator, f"nisse-e2e-{run}", keys, agent=agent, seed=seed)
|
||||
try:
|
||||
yield repo
|
||||
finally:
|
||||
if not os.environ.get("NISSE_E2E_KEEP_REPOS"):
|
||||
with contextlib.suppress(GiteaError):
|
||||
operator._request("DELETE", f"/repos/{full}")
|
||||
delete_repo(operator, repo.name)
|
||||
Reference in new issue
Block a user