mirror of
https://github.com/razzant/ouroboros.git
synced 2026-10-02 19:58:46 +00:00
The documented local command (`make test`, CONTRIBUTING §4, README) was `pytest tests/ -q --tb=short`: the whole default suite (23k tests) in a single process, behind a stale "smoke tests" comment. An Ouroboros task followed it, hit its 1200 s timeout and withheld its PR. The correct recipe — a parallel `not serial` xdist pass, then the serial pass — lived only inside the hermetic commit gate. scripts/run_tests.py is the single entry point for every actor. A bare call always runs the FULL battery: the node lane (a missing or too-old node is a red NOT_RUN, never a silent skip), then every default-lane test in ONE xdist run under `--dist loadgroup --serial-shards=N`. tests/conftest.py gains that option: each serial FILE is pinned to one of N file-sharded groups (one per four workers, at most four; four cores keep a single group), so serial tests overlap the parallel ones while a serial file still never splits across workers or runs beside another file of its shard; other tests keep the scope loadscope uses. The option defaults to 0 and is inert for CI and the commit gate, which keep the two-pass split and stay the authority. `--sequential` keeps the gate's marker split and xdist flags in two passes; extra arguments forward a focused run. Marker exclusions and xdist flags are imported from ouroboros.preflight_runner, never restated. Measured on an 18-core Mac shared with other agents: the two-pass battery took 649 s (parallel 203 s + serial 446 s); the one-run battery took 217–287 s with identical verdicts (22 950 passed). Also: `make test` now calls the script; CONTRIBUTING, README, the Build & CI chapter, the architecture map and one sentence of prompts/SYSTEM.md point at it and say which opt-in lanes it leaves out and what to record when a full run could not finish; tests/test_plan_author_presentation.py resolves node through ouroboros.preflight_node like the rest of the suite instead of a bare PATH `node`; tests/test_run_tests_script.py pins the recipe parity, the bounded shard count, bare call = full battery, node NOT_RUN = red, and the grouping semantics. Not changed: the commit gate and .github/workflows/ci.yml; the three gate self-tests that wait out real 30/20 s timeouts (their slack exists for nested-xdist startup on slow runners); no version carrier. Co-authored-by: Ouroboros <311266734+ouroboros-agent@users.noreply.github.com>
78 lines
4.1 KiB
Python
78 lines
4.1 KiB
Python
"""A real current author plan reaches the existing review UI without a fake wave."""
|
|
|
|
import json
|
|
from pathlib import Path
|
|
import subprocess
|
|
|
|
import pytest
|
|
|
|
from ouroboros.outcomes import public_task_result
|
|
from ouroboros.task_results import load_plan_review_state, load_task_result
|
|
from ouroboros.preflight_node import resolve_node
|
|
from tests.test_plan_review_engine import DECK_SPEC, _call, _finding, harness as _harness
|
|
from tests.test_preflight_node import requires_node
|
|
|
|
harness = _harness
|
|
pytestmark = pytest.mark.serial
|
|
|
|
|
|
@requires_node
|
|
@pytest.mark.parametrize("action", ["finish", "stop"])
|
|
@pytest.mark.parametrize("cap", ["1", "2"])
|
|
def test_current_plan_author_is_not_an_extra_reviewer(harness, monkeypatch, action, cap):
|
|
h = harness
|
|
h.state["enforcement"] = "advisory"
|
|
monkeypatch.setenv("OUROBOROS_REVIEW_ENFORCEMENT", "advisory")
|
|
monkeypatch.setenv("OUROBOROS_REVIEW_MAX_CYCLES", cap)
|
|
ctx = h.make_ctx()
|
|
feedback = json.dumps([_finding("first", "blocking", breaks="claim_1")])
|
|
transport = h.install({slot: feedback for slot in ("s1", "s2", "s3")})
|
|
_call(ctx)
|
|
old = load_plan_review_state(h.drive, ctx.task_id)
|
|
result = _call(ctx, {**DECK_SPEC, "acceptance_claims": ["Corrected current claim"]},
|
|
plan="Corrected plan without another critic.", review_disposition={
|
|
"review_fingerprint": old["current_attempt"]["fingerprint"], "items": [],
|
|
"author_action": action,
|
|
"author_disposition": {"disposition": "partial", "rationale": "Considered the actual feedback."},
|
|
})
|
|
assert "Current author plan saved" in result
|
|
assert len(transport.calls) == 1
|
|
detail = public_task_result(load_task_result(h.drive, ctx.task_id))
|
|
module = Path(__file__).resolve().parents[1] / "web/modules/review_presentation.js"
|
|
script = "import {planReviewGroupFromTaskDetail, renderReviewsSection, mergeReviewGroup} from " + json.dumps(module.as_uri()) + ";" + r"""
|
|
import assert from 'node:assert/strict';
|
|
import { readFileSync } from 'node:fs';
|
|
const detail = JSON.parse(readFileSync(0, 'utf8'));
|
|
const subject = detail.plan_review_state.current_attempt.author_subject;
|
|
const action = subject.author_disposition.action;
|
|
const group = planReviewGroupFromTaskDetail(detail);
|
|
assert.equal(group.activeCount, 0);
|
|
assert.equal(group.attemptCount, detail.plan_review_state.waves.length);
|
|
assert.equal(group.attempts.length, 1);
|
|
assert.equal(group.attempts[0].superseded, false);
|
|
assert.notEqual(group.attempts[0].verdict, 'PASS');
|
|
assert.match(group.authorDecisionText, new RegExp(`Author ${action}: partial`));
|
|
assert.ok(group.authorDecisionText.includes(subject.source_ref.path));
|
|
assert.ok(group.authorDecisionText.includes(subject.author_disposition.subject_hash));
|
|
assert.ok(group.authorDecisionText.includes(subject.review_fingerprint));
|
|
const disclosure = { sectionExpanded: true, expandedGroups: new Set([group.id]) };
|
|
const html = renderReviewsSection([group], disclosure);
|
|
assert.ok(html.includes('Plan author decision'));
|
|
assert.ok(!html.includes(' active'));
|
|
assert.ok(!html.includes('Review result unavailable'));
|
|
assert.equal(disclosure.expandedGroups.size, 1);
|
|
const store = new Map();
|
|
mergeReviewGroup(store, group);
|
|
assert.equal(mergeReviewGroup(store, group).attempts.length, 1);
|
|
// The same source shape with real remaining custody is still active;
|
|
// author finality must not hide an unfinished physical reviewer.
|
|
detail.plan_review_state.waves[0].custody_pending = true;
|
|
const pending = planReviewGroupFromTaskDetail(detail);
|
|
assert.equal(pending.activeCount, 1);
|
|
assert.equal(pending.attempts.length, 1);
|
|
assert.equal(pending.state, 'running');
|
|
assert.equal(pending.authorDecisionText, group.authorDecisionText);
|
|
"""
|
|
completed = subprocess.run([resolve_node(), "--input-type=module", "-e", script],
|
|
input=json.dumps(detail), capture_output=True, text=True, timeout=30)
|
|
assert completed.returncode == 0, completed.stdout + completed.stderr
|