mirror of
https://github.com/razzant/ouroboros.git
synced 2026-10-19 02:02:00 +00:00
feat(v6.68.0): contributor fast path with target-bound review evidence
This commit is contained in:
parent
543cc8376c
commit
058d578cb7
20 changed files with 1840 additions and 194 deletions
91
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
91
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
<!--
|
||||
Thank you for contributing to Ouroboros.
|
||||
|
||||
Open this PR against the `ouroboros` branch, not `main` or
|
||||
`ouroboros-stable`. Keep review artifacts out of the git diff: attach or link
|
||||
them in the Review evidence section instead.
|
||||
-->
|
||||
|
||||
## Summary
|
||||
|
||||
<!-- What changes, and why is it needed? Link an issue or discussion when relevant. -->
|
||||
|
||||
## Scope
|
||||
|
||||
In scope:
|
||||
|
||||
-
|
||||
|
||||
Non-goals:
|
||||
|
||||
-
|
||||
|
||||
## Verification
|
||||
|
||||
<!-- List exact commands and outcomes. Do not write only "tests pass". -->
|
||||
|
||||
- [ ] Focused tests for the changed behavior pass.
|
||||
- [ ] The default local test suite passes, or the reason it was not run is below.
|
||||
- [ ] Lint/static checks relevant to this change pass.
|
||||
|
||||
Commands and results:
|
||||
|
||||
```text
|
||||
|
||||
```
|
||||
|
||||
## Visual evidence
|
||||
|
||||
<!-- Check one. Visible UI changes need evidence from a real rendered flow. -->
|
||||
|
||||
- [ ] Not applicable; this PR has no visible UI change.
|
||||
- [ ] Before/after screenshots or other rendered-flow evidence are attached below.
|
||||
|
||||
Evidence:
|
||||
|
||||
## Governance and documentation
|
||||
|
||||
- [ ] I followed `CONTRIBUTING.md` and checked the relevant guidance in
|
||||
`BIBLE.md`, `docs/ARCHITECTURE.md`, `docs/DEVELOPMENT.md`, and
|
||||
`docs/CHECKLISTS.md`.
|
||||
- [ ] I updated tests and documentation where behavior or architecture changed.
|
||||
- [ ] I did not include secrets, local settings, runtime state, logs, caches, or
|
||||
generated build/review artifacts in the commit.
|
||||
- [ ] I did **not** bump `VERSION` or release-only version carriers; maintainers
|
||||
assign the collision-free release version during final integration.
|
||||
|
||||
## Agent assistance (optional)
|
||||
|
||||
<!-- Disclosure is useful context, not a quality signal. Remove this section if unused. -->
|
||||
|
||||
- Agent/tool and model:
|
||||
- Work performed by the agent:
|
||||
- Human verification performed:
|
||||
|
||||
## Review evidence
|
||||
|
||||
<!--
|
||||
Attach or link the final triad/scope review output when available. Evidence must
|
||||
correspond to the current PR diff; rerun it after code changes or a rebase. If it
|
||||
was not run, say why. Missing evidence does not prevent opening a PR, but it can
|
||||
make maintainer integration slower.
|
||||
-->
|
||||
|
||||
- [ ] Triad/scope review evidence is attached or linked below.
|
||||
- [ ] Review was not run; the reason is recorded below.
|
||||
|
||||
- Reviewed base SHA:
|
||||
- Reviewed head/diff SHA:
|
||||
- Review command/profile:
|
||||
- Triad verdict:
|
||||
- Scope verdict:
|
||||
- Run artifacts/link:
|
||||
- Known advisory findings or accepted tradeoffs:
|
||||
- If not run, reason:
|
||||
|
||||
## Final checklist
|
||||
|
||||
- [ ] The PR base branch is `ouroboros`.
|
||||
- [ ] The branch is based on a current `ouroboros` revision.
|
||||
- [ ] The PR is one coherent change and is ready to be squash-integrated.
|
||||
- [ ] The description explains any limitations, follow-up work, or compatibility impact.
|
||||
20
.github/workflows/ci.yml
vendored
20
.github/workflows/ci.yml
vendored
|
|
@ -1,6 +1,6 @@
|
|||
# Ouroboros CI — Five-tier cross-platform testing and release pipeline
|
||||
#
|
||||
# Tier 1 (Quick): Push to ouroboros (code paths) → Ubuntu-only tests (~1 min)
|
||||
# Tier 1 (Quick): Push to ouroboros or PR targeting ouroboros → Ubuntu-only tests (~1 min)
|
||||
# Tier 2 (Full): Push to ouroboros-stable / manual / tag → Full 3-OS matrix (~5 min)
|
||||
# Tier 2.5 (Integration): Push to main / ouroboros / ouroboros-stable / manual / tag → Real-provider tests (~2 min)
|
||||
# Tier 2.6 (Skill smoke): Push to ouroboros-stable / manual / tag → LIVE OuroborosHub official-skill install smoke (3-OS, ~5 min)
|
||||
|
|
@ -36,22 +36,34 @@ on:
|
|||
- 'packaging/**'
|
||||
- 'VERSION'
|
||||
- 'README.md'
|
||||
- 'CONTRIBUTING.md'
|
||||
- 'LICENSE'
|
||||
- '.github/PULL_REQUEST_TEMPLATE.md'
|
||||
- 'launcher.py'
|
||||
tags:
|
||||
- 'v*'
|
||||
# Fork-safe PR validation: no provider secrets and no pull_request_target.
|
||||
# Only the deterministic quick-test job matches pull_request refs below.
|
||||
pull_request:
|
||||
branches: [ouroboros]
|
||||
workflow_dispatch:
|
||||
|
||||
# Note: GitHub Actions evaluates `branches` + `paths` together but `tags`
|
||||
# separately — a tag push matching `v*` will trigger regardless of paths.
|
||||
|
||||
# Read-only is sufficient for tests and fork PRs. The release job overrides
|
||||
# this narrowly with contents: write when publishing a tag.
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
# ──────────────────────────────────────────────────────────────────
|
||||
# Tier 1: Quick tests on Ubuntu (every push to ouroboros)
|
||||
# Tier 1: Quick tests on Ubuntu (push to ouroboros or PR targeting it)
|
||||
# ──────────────────────────────────────────────────────────────────
|
||||
quick-test:
|
||||
if: |
|
||||
github.event_name == 'push'
|
||||
&& github.ref == 'refs/heads/ouroboros'
|
||||
(github.event_name == 'push' && github.ref == 'refs/heads/ouroboros')
|
||||
|| (github.event_name == 'pull_request' && github.base_ref == 'ouroboros')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
|
|
|||
2
.github/workflows/redirect-mirror-prs.yml
vendored
2
.github/workflows/redirect-mirror-prs.yml
vendored
|
|
@ -22,5 +22,5 @@ jobs:
|
|||
PR_URL: ${{ github.event.pull_request.html_url }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
gh pr comment "$PR_URL" --body "Thanks for contributing. This repository is an active mirror. Please reopen this pull request against the official repository: https://github.com/razzant/ouroboros"
|
||||
gh pr comment "$PR_URL" --body "Thanks for contributing. This repository is an active mirror. Please reopen this pull request against the lowercase \`ouroboros\` branch of the official repository: https://github.com/razzant/ouroboros. Follow https://github.com/razzant/ouroboros/blob/ouroboros/CONTRIBUTING.md for the contributor workflow."
|
||||
gh pr close "$PR_URL"
|
||||
|
|
|
|||
3
.github/workflows/sync-joi-lab-fork.yml
vendored
3
.github/workflows/sync-joi-lab-fork.yml
vendored
|
|
@ -46,7 +46,8 @@ jobs:
|
|||
> This repository is an active mirror of the official Ouroboros repository:
|
||||
> **https://github.com/razzant/ouroboros**
|
||||
>
|
||||
> Please open issues, pull requests, discussions, and release questions in `razzant/ouroboros`.
|
||||
> Please open issues, discussions, and release questions in `razzant/ouroboros`.
|
||||
> Open pull requests against its lowercase `ouroboros` branch and follow `CONTRIBUTING.md` there.
|
||||
|
||||
"""
|
||||
if not text.startswith("> [!IMPORTANT]\n> This repository is an active mirror"):
|
||||
|
|
|
|||
311
CONTRIBUTING.md
311
CONTRIBUTING.md
|
|
@ -1,47 +1,292 @@
|
|||
# Contributing to Ouroboros
|
||||
|
||||
External contributions are welcome. This file is only a contributor-facing
|
||||
routing guide; the project rules live in the canonical docs.
|
||||
Thank you for helping Ouroboros evolve. This guide explains how to prepare a
|
||||
pull request that is easy to understand, reproduce, review, and integrate.
|
||||
|
||||
## Canonical Docs
|
||||
The short version:
|
||||
|
||||
- `README.md` - install, source setup, tests, build notes, and user-facing overview.
|
||||
- `BIBLE.md` - constitutional principles and design priority.
|
||||
- `docs/ARCHITECTURE.md` - current structure, runtime/data map, endpoints, and release model.
|
||||
- `docs/DEVELOPMENT.md` - engineering conventions, module boundaries, and testing guidance.
|
||||
- `docs/CHECKLISTS.md` - review checklist single source of truth.
|
||||
1. Base your work on the lowercase `ouroboros` branch and open the pull request
|
||||
against `ouroboros`, not `main` or `ouroboros-stable`.
|
||||
2. Read the project governance and engineering documents before making a
|
||||
substantive change.
|
||||
3. Keep the pull request focused, test the changed behavior, and show rendered
|
||||
evidence for visible UI changes.
|
||||
4. Do not bump the project version. Maintainers assign the final release
|
||||
version when integrating the pull request.
|
||||
5. Optionally attach a current triad + scope review packet to enter the faster
|
||||
review path.
|
||||
|
||||
If this file and a canonical doc disagree, the canonical doc wins. Update the
|
||||
canonical doc instead of copying detailed rules here.
|
||||
The pull request template in
|
||||
[`.github/PULL_REQUEST_TEMPLATE.md`](.github/PULL_REQUEST_TEMPLATE.md) mirrors
|
||||
this flow.
|
||||
|
||||
## Before Opening a PR
|
||||
## Read the Project Before Changing It
|
||||
|
||||
1. Follow the `README.md` source setup and test instructions.
|
||||
2. Read the relevant part of `docs/DEVELOPMENT.md` before changing runtime,
|
||||
tools, platform code, provider calls, web UI, skills, prompts, or governance docs.
|
||||
3. Keep the PR focused on one coherent change.
|
||||
4. Add or update tests for changed behavior.
|
||||
5. In the PR body, explain why the change is needed, link the issue when there
|
||||
is one, and include the exact commands or checks you ran.
|
||||
For substantive changes, read these files **in full** before designing or
|
||||
editing:
|
||||
|
||||
## Review Notes
|
||||
- [`BIBLE.md`](BIBLE.md) — constitutional principles and design priorities.
|
||||
- [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) — the current structural and
|
||||
operational map, including the rationale for non-obvious decisions.
|
||||
- [`docs/DEVELOPMENT.md`](docs/DEVELOPMENT.md) — engineering conventions,
|
||||
module boundaries, testing guidance, and the commit/review protocol.
|
||||
- [`docs/CHECKLISTS.md`](docs/CHECKLISTS.md) — the review checklist single
|
||||
source of truth.
|
||||
|
||||
External PRs are reviewed by maintainers with local checks and any configured
|
||||
automation. Ouroboros also has an internal self-modification commit gate;
|
||||
contributors do not need to run that gate for a normal fork PR, but
|
||||
`docs/CHECKLISTS.md` is the best summary of what reviewers will care about.
|
||||
Use the existing modules, contracts, and single sources of truth described in
|
||||
those documents before introducing a new mechanism. If a coding agent is doing
|
||||
the work, give it the same instruction explicitly. A useful starting prompt is:
|
||||
|
||||
## Sensitive Areas
|
||||
> Before editing, read BIBLE.md, docs/ARCHITECTURE.md,
|
||||
> docs/DEVELOPMENT.md, and docs/CHECKLISTS.md in full. Follow their current
|
||||
> principles and reuse the architecture and existing extension seams instead
|
||||
> of inventing parallel mechanisms. Keep the change focused and verify it
|
||||
> against the repository's tests and review checklist.
|
||||
|
||||
Ask before making broad changes to constitutional docs, runtime prompts, safety
|
||||
policy, frozen contracts, release/build plumbing, managed git behavior, or
|
||||
runtime state under `data/`. These areas have stronger review expectations
|
||||
because they affect identity, safety, compatibility, or release provenance.
|
||||
These documents are authoritative descriptions of the current project, not
|
||||
immutable external rules. A pull request may improve `BIBLE.md`, the
|
||||
architecture, development practices, or review checklists. Explain why the
|
||||
change is needed, what invariant it preserves or intentionally evolves, and
|
||||
keep related code, tests, and documentation consistent. Constitutional changes
|
||||
must also respect the change process and semantic protections stated in
|
||||
`BIBLE.md` itself.
|
||||
|
||||
Do not include secrets, local settings, generated runtime state, logs, caches,
|
||||
or build artifacts in a PR.
|
||||
## Choose a Focused Change
|
||||
|
||||
## License
|
||||
Prefer one coherent purpose per pull request. The description should make the
|
||||
following clear:
|
||||
|
||||
By contributing, you agree that your contribution can be released under the
|
||||
repository license.
|
||||
- the problem or opportunity;
|
||||
- what changed and why this approach fits the existing architecture;
|
||||
- what is deliberately out of scope;
|
||||
- compatibility, migration, safety, or operational risks;
|
||||
- the exact verification performed and its result.
|
||||
|
||||
For a broad, ambiguous, security-sensitive, constitutional, or
|
||||
direction-changing proposal, opening an issue or discussion first can avoid
|
||||
expensive rework. Small, well-understood fixes do not need ceremonial design
|
||||
work.
|
||||
|
||||
Do not commit local settings, API keys, runtime state, logs, caches, benchmark
|
||||
runs, generated review runs, or build artifacts. Runtime data belongs outside
|
||||
the git repository.
|
||||
|
||||
## Branch and Pull Request Flow
|
||||
|
||||
Keep an `upstream` remote pointed at the official repository and start from the
|
||||
latest working branch:
|
||||
|
||||
```bash
|
||||
git remote add upstream https://github.com/razzant/ouroboros.git # once
|
||||
git fetch upstream
|
||||
git switch -c your-focused-branch upstream/ouroboros
|
||||
```
|
||||
|
||||
If `upstream` already exists, verify it instead of adding it again. Before final
|
||||
verification and review evidence, update your branch and resolve any drift:
|
||||
|
||||
```bash
|
||||
git fetch upstream
|
||||
git rebase upstream/ouroboros
|
||||
```
|
||||
|
||||
Open the pull request with the base branch set to lowercase `ouroboros`.
|
||||
`main` is protected, and `ouroboros-stable` is a recovery branch rather than
|
||||
the contribution target.
|
||||
|
||||
Keep the worktree clean when producing final evidence: commit the intended
|
||||
changes on your pull request branch, then confirm that `git status --short` is
|
||||
empty. The contributor review command examines a committed base-to-head range;
|
||||
it does not approve uncommitted edits.
|
||||
|
||||
### Do not bump the version
|
||||
|
||||
External contributors should not edit release-only version carriers for a
|
||||
normal pull request: `VERSION`, the package versions, README badge/history,
|
||||
the architecture version header, or release tags. Ouroboros treats an
|
||||
integrated commit as a release, but maintainers assign the collision-free
|
||||
version during final integration, normally while squash-integrating the pull
|
||||
request. This avoids unrelated version conflicts between concurrent
|
||||
contributions.
|
||||
|
||||
You may still update ordinary README or architecture content when the behavior
|
||||
or structure changes; leave only the release number to the maintainer.
|
||||
|
||||
## Verification
|
||||
|
||||
Install the source environment using
|
||||
[`README.md` → Run from Source](README.md#run-from-source). The default local
|
||||
suite is documented in [`README.md` → Run Tests](README.md#run-tests):
|
||||
|
||||
```bash
|
||||
make test
|
||||
```
|
||||
|
||||
Run the narrowest relevant tests while developing, then the repository's
|
||||
default local test suite when practical. Record the exact commands and
|
||||
outcomes in the pull request; “tests pass” without a command is not
|
||||
reproducible evidence. Follow the marker and environment guidance in
|
||||
[`docs/DEVELOPMENT.md`](docs/DEVELOPMENT.md) for integration, browser,
|
||||
portable, and skill-smoke lanes.
|
||||
|
||||
Add or update tests for changed behavior. If a test cannot be run in your
|
||||
environment, say which one and why rather than marking it as passed.
|
||||
|
||||
For a visible UI change:
|
||||
|
||||
1. Open at least one relevant real user flow in an available browser.
|
||||
2. Inspect the rendered result, including the states and viewport sizes that
|
||||
matter for the change.
|
||||
3. Attach before/after screenshots or equivalent evidence and state what was
|
||||
actually inspected.
|
||||
|
||||
A saved screenshot that nobody inspected is not visual verification. Mobile
|
||||
or WebKit evidence is risk-driven rather than mandatory for every UI change.
|
||||
|
||||
## Optional Fast Path: Triad + Scope Review
|
||||
|
||||
A contributor-supplied triad + scope packet is optional, but it materially
|
||||
reduces the work needed to evaluate a pull request. A current, complete packet
|
||||
places the pull request on the faster review path. A pull request without one
|
||||
may still be considered, but Ouroboros and the maintainers must reproduce the
|
||||
review and may need to revise the change manually; that path is slower and the
|
||||
pull request is more likely to be deferred when maintainer capacity is limited.
|
||||
|
||||
Neither a clean packet nor its absence decides acceptance. A passing review is
|
||||
evidence, not a promise to merge, and maintainers may rerun it or request
|
||||
additional changes.
|
||||
|
||||
If the pull request changes the review substrate itself — for example the
|
||||
review script, reviewer configuration, review checklists, or production review
|
||||
code — its contributor packet is diagnostic only. A maintainer must rerun the
|
||||
review from a trusted target-base implementation before integration.
|
||||
|
||||
### What the contributor review runs
|
||||
|
||||
The checked-in
|
||||
[`scripts/run_external_review.py`](scripts/run_external_review.py) contributor
|
||||
mode runs the production triad and scope review over the committed range from
|
||||
the target base to your branch head. It deliberately:
|
||||
|
||||
- resolves the reviewer models and reasoning efforts from the shipped defaults
|
||||
in the target `ouroboros` revision, not from values modified by the pull
|
||||
request;
|
||||
- routes every triad and scope reviewer through OpenRouter;
|
||||
- applies the clean blocking contract for the contributor packet;
|
||||
- does **not** run the Claude advisory pre-review and does not require an
|
||||
Anthropic key;
|
||||
- records the exact base/head binding and full reviewer evidence without
|
||||
committing or modifying the reviewed branch.
|
||||
|
||||
Triad completeness follows Ouroboros's production quorum contract together
|
||||
with an authoritative scope verdict; it does not require every configured
|
||||
actor to respond. The packet preserves actor statuses and degraded reasons, so
|
||||
any failed, partial, or unparsable actor must still be disclosed even when the
|
||||
remaining actors satisfy quorum.
|
||||
|
||||
Do not override the reviewer models, scope-review model, reasoning efforts, or
|
||||
provider. The value of this packet is that every contributor uses the review
|
||||
configuration selected by the current target version of Ouroboros.
|
||||
|
||||
### Produce a packet
|
||||
|
||||
First fetch and rebase on the current target, commit the final change, and
|
||||
confirm the worktree is clean. Configure your own `OPENROUTER_API_KEY` without
|
||||
placing it in a command argument, tracked file, shell history, or attachment.
|
||||
Also set `TOTAL_BUDGET` to a positive finite USD ceiling that you explicitly
|
||||
authorize for this run; contributor mode fails before model calls when that
|
||||
ceiling is missing or invalid. The review calls may incur OpenRouter charges,
|
||||
so use a key with sufficient remaining provider balance and check current
|
||||
pricing before choosing the ceiling. For example:
|
||||
|
||||
```bash
|
||||
export TOTAL_BUDGET="<authorized USD ceiling>"
|
||||
```
|
||||
|
||||
Then run from the repository root:
|
||||
|
||||
```bash
|
||||
python scripts/run_external_review.py \
|
||||
--contributor \
|
||||
--base-ref upstream/ouroboros \
|
||||
--head-ref HEAD \
|
||||
"<PR title>" \
|
||||
--goal "<goal>" \
|
||||
--scope "<scope>"
|
||||
```
|
||||
|
||||
The script creates:
|
||||
|
||||
- `review-evidence.json` — machine-readable base/head, configuration, actor,
|
||||
verdict, and outcome evidence;
|
||||
- `full-output.txt` — full human-readable triad and scope output;
|
||||
- `review-packet.zip` — the attachment-ready packet.
|
||||
|
||||
Use the fresh output directory printed by the command. Review packets are
|
||||
operator artifacts: attach them to the pull request, but do not add them to the
|
||||
git diff.
|
||||
|
||||
### Attach and summarize the evidence
|
||||
|
||||
In the pull request's **Review evidence** section:
|
||||
|
||||
1. Attach `review-packet.zip` and, when useful for quick inspection,
|
||||
`review-evidence.json` and `full-output.txt`.
|
||||
2. Record the reviewed base SHA and head SHA.
|
||||
3. Record the exact command/profile and the resolved reviewer model IDs.
|
||||
4. Summarize every triad actor verdict, the aggregate triad result, and the
|
||||
scope verdict.
|
||||
5. Disclose known advisory findings, accepted trade-offs, infrastructure
|
||||
failures, skipped coverage, or other non-clean conditions. Do not present a
|
||||
failed or incomplete run as a pass.
|
||||
|
||||
The evidence is valid only for the recorded head and base. Rerun it after any
|
||||
code change, rebase, conflict resolution, or other change to the reviewed
|
||||
range. Do not cherry-pick only favorable actor outputs or substitute a
|
||||
screenshot for the raw records.
|
||||
|
||||
### Protect secrets and private data
|
||||
|
||||
Treat review output as public before uploading it. Attach only the generated
|
||||
packet, not the review drive, local observability store, `settings.json`, or an
|
||||
environment dump. Inspect the packet for API keys, authorization headers,
|
||||
credentials, private paths, or unrelated local data even when automatic
|
||||
redaction reports success.
|
||||
|
||||
If redaction is necessary, preserve all verdicts and finding structure and
|
||||
state exactly what was removed. Never upload a secret and never commit a
|
||||
credential in order to make review reproducible; revoke any credential that
|
||||
was exposed.
|
||||
|
||||
## Pull Request Checklist
|
||||
|
||||
Before marking the pull request ready for review, confirm that:
|
||||
|
||||
- the base branch is `ouroboros` and the branch is current with its intended
|
||||
base revision;
|
||||
- the pull request has one coherent purpose with explicit non-goals;
|
||||
- the description explains the architectural fit and important trade-offs;
|
||||
- relevant tests pass, with exact commands and outcomes recorded;
|
||||
- behavior and architecture documentation are updated where necessary;
|
||||
- visible UI changes include inspected rendered-flow evidence;
|
||||
- no version or release-only carrier was bumped by the contributor;
|
||||
- no secrets, local state, logs, caches, generated runs, or build artifacts are
|
||||
present in the diff;
|
||||
- triad + scope evidence is attached and current, or the reason it was not run
|
||||
is stated honestly;
|
||||
- limitations, unresolved findings, and follow-up work are disclosed.
|
||||
|
||||
Disclosure of coding-agent assistance is optional. If you include it, name the
|
||||
agent/model, what it changed, and what a human or independent process verified.
|
||||
Agent use is context, not a positive or negative quality signal.
|
||||
|
||||
## What Happens After Submission
|
||||
|
||||
Maintainers may reproduce tests, rerun triad + scope review, ask for a smaller
|
||||
diff, request changes, or integrate the contribution with additional fixes and
|
||||
the final release-version update. Review evidence accelerates that process but
|
||||
does not replace maintainer judgment or Ouroboros's own governance gates.
|
||||
|
||||
Contributions are licensed under the repository's
|
||||
[`LICENSE`](LICENSE). By submitting a contribution, you confirm that you have
|
||||
the right to provide it under those terms.
|
||||
|
|
|
|||
21
LICENSE
Normal file
21
LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2026 Anton Razzhigaev
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
11
README.md
11
README.md
|
|
@ -7,7 +7,7 @@
|
|||
[](https://github.com/razzant/ouroboros/releases)
|
||||
[](https://github.com/razzant/ouroboros/releases)
|
||||
[](https://github.com/razzant/OuroborosHub)
|
||||
[](VERSION)
|
||||
[](VERSION)
|
||||
|
||||
A self-modifying AI agent that writes its own code, rewrites its own mind, and evolves autonomously. Born February 16, 2026.
|
||||
|
||||
|
|
@ -517,9 +517,11 @@ not paraphrase it.
|
|||
## Contributing
|
||||
|
||||
External contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md)
|
||||
for the contributor workflow. The project rules remain in `BIBLE.md`,
|
||||
`docs/ARCHITECTURE.md`, `docs/DEVELOPMENT.md`, and `docs/CHECKLISTS.md`;
|
||||
the contribution guide only routes to those sources.
|
||||
for the complete workflow. Open pull requests against the lowercase
|
||||
`ouroboros` branch and leave release-version allocation to maintainers. A
|
||||
current OpenRouter triad + scope packet is the optional fast path; pull
|
||||
requests without one remain welcome but require more maintainer-side review
|
||||
and integration work.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -527,6 +529,7 @@ the contribution guide only routes to those sources.
|
|||
|
||||
| Version | Date | Description |
|
||||
|---------|------|-------------|
|
||||
| 6.68.0 | 2026-07-17 | **feat: contributor fast path with target-bound review evidence.** External pull requests now have one explicit public flow: target lowercase `ouroboros`, read the governance/architecture/development SSOTs in full, keep proposals focused, leave collision-prone release versions to the final maintainer squash landing, and attach reproducible evidence when practical. A GitHub PR template gathers scope, tests, rendered UI evidence, optional agent disclosure, and review provenance; fork-safe read-only `pull_request` CI runs the deterministic quick lane without provider secrets; the repository now carries its explicit MIT license. `scripts/run_external_review.py --contributor` reviews a clean committed target-base..head tree with shipped target-base reviewer defaults forced through OpenRouter at blocking enforcement, excludes Claude advisory while preserving a serial hermetic per-commit suite whose child environment scrubs live behavior overrides and secret-class values (CI alone keeps the parallel/non-serial plus serial split), probes every triad/scope route, forbids contributor `VERSION` bumps, defers only the typed release-carrier checklist items without masking scope failure, refuses self-attested review-substrate changes as fast-path proof, and emits a fully redacted SHA-bound JSON/text/ZIP packet. Contributor readiness accelerates triage but never replaces the final P9 version allocation or production commit review. |
|
||||
| 6.67.0 | 2026-07-16 | **feat: composable declarative widgets and evidence-based UI acceptance.** Declarative schema v1 gains recursive `group`, `metric`, and `callout` composition, stable nested identity, bounded validation, shared safe fields, accessible chart fallbacks, and a native kanban Move-to path while preserving the host-owned trust boundary and all existing declarations. Visible UI completion now requires an available real consumer flow to be opened and its rendered evidence actually inspected with vision; the LLM selects states, viewports, and optional engines by risk, without a blanket mobile/WebKit matrix or a new QA service. The constitution now makes autonomous judgment and smallest-class-fix scope explicit, DEVELOPMENT records pragmatic SOLID and mutable external facts without adding analyzers or runtime lookup migrations, and reviewer prompts reject numeric finding pressure in favor of concrete evidenced defects. The operator review wrapper additionally probes the exact reviewer model through each candidate OpenRouter key before selecting it — `limit_remaining` alone is documented to pass ToS-blocked keys. |
|
||||
| 6.66.0 | 2026-07-16 | **feat: honest mutation attribution, attributed commit staging, environment-correct Python, auditable Skill Review history, and a drift-proof operator review wrapper.** The host now captures a `system_repo` baseline (exact commit/tree + pre-existing dirty fingerprints) in the existing task result when a root task starts and a terminal candidate snapshot at outcome derivation; blockers ride into acceptance/review evidence for the LLM panels — deliberately never a structural outcome veto, and deliberately WITHOUT any lease/holder service (physical exclusion is out of scope; a future separate design). `commit_reviewed` stages only clean-at-baseline task-attributed candidates: explicit paths must be a subset and an empty set is `GIT_NO_ATTRIBUTED_CHANGES`, never whole-tree staging (managed update/release transactions keep their typed `git add -A`; baseline-less contexts keep the legacy contract). The four user launch surfaces resolve unversioned `python`/`python3` once in registry pre-dispatch (reviewed skill env → executor backend → project `.venv` → verified agent interpreter) with traced provenance and a fail-closed unproved-interpreter path. Skill Review gains group-wide `review_round` + per-snapshot `snapshot_attempt` with exact task/chat/source provenance in the existing job/history files and a compact UI projection. Child-result dispositions consolidate into the single task-tree ledger row authority (task-result fields become read-time projections; explicit cancellation wins completion races with bounded scratch cleanup, no recovery surface; in-flight ≤v6.65 trees re-emit their dispositions once after upgrade). `scripts/run_external_review.py` becomes a thin SSOT wrapper over the production gate: real advisory by default, frozen detached-checkout review, OpenRouter key health-check from the named pool, and typed exit codes separating infrastructure failures from genuine review blocks; the hermetic pytest preflight substitute runs parallel non-serial with a bounded worker count. Fixes the chronic red CI light-model routing test. This operator phase intentionally advances `VERSION` ahead of the latest annotated tag; the final Phase 3 release publishes the closing tag. |
|
||||
| 6.65.4 | 2026-07-16 | **fix: a zero-usage provider body-error settles a confirmed \$0, not a phantom reservation.** The v6.64.0 physical-attempt ledger settles an HTTP-200 whose body is a top-level provider error (OpenRouter passes 429/5xx/4xx through the body) with `cost=None` and keeps its conservative reservation upper bound as an unresolved amount. Under a provider storm each reroute/strip/fallback is a fresh attempt, so the held bounds accumulate (7 x ~\$2.4 = ~\$16.7) and exhaust the finite task budget while real spend stays small — SWE-Pro tasks died `budget_exhausted` at ~\$7.5 of a \$25 cap. Such a request billed zero tokens (rejected before generation), so `usage_from_response` now settles it as a confirmed \$0 and RELEASES the bound. The under-count protection is intact: any billed tokens (a partial stream or an unknown-price success, prompt/completion > 0) keep the normal cost path and their bound, and the opaque-SDK path still settles via `mark_unresolved`. |
|
||||
|
|
|
|||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
|||
6.67.0
|
||||
6.68.0
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -468,6 +468,36 @@ findings are advisory-only and never replace the full-cap blocking scope-review
|
|||
floor. `docs/CHECKLISTS.md` remains the single source of truth for review items;
|
||||
do not duplicate or fork checklist policy here.
|
||||
|
||||
### External PR readiness is not commit authorization
|
||||
|
||||
`scripts/run_external_review.py --contributor` reuses the production triad and
|
||||
scope substrate for a non-committing proposal review. It is structurally
|
||||
different from `commit_reviewed`:
|
||||
|
||||
- the exact current target-base commit must be an ancestor of the clean,
|
||||
committed proposal head;
|
||||
- reviewer slots, efforts, and scope floor come from literal shipped defaults
|
||||
in that target-base checkout, local overrides are ignored, every reviewer is
|
||||
routed through OpenRouter, and enforcement is blocking;
|
||||
- Claude advisory is excluded; the hermetic deterministic test preflight still
|
||||
runs before triad + scope. Its child pytest receives only disposable
|
||||
data/settings/repo roots: live `OUROBOROS_*` behavior overrides and
|
||||
secret-class environment values are scrubbed;
|
||||
- external contributors do not allocate `VERSION` or release-only carriers.
|
||||
The typed `version_bump` and `changelog_and_badge` items are recorded as final
|
||||
squash-landing obligations when they are the only critical findings; no other
|
||||
checklist item can be demoted this way;
|
||||
- evidence binds target base, proposal head/tree, diff hash, target config, and
|
||||
resolved actors in a redacted shareable packet. A proposal that changes the
|
||||
review substrate cannot self-attest fast-path readiness and requires a
|
||||
trusted maintainer rerun.
|
||||
|
||||
The result is `READY_FOR_INTEGRATION`, not merge authority. Maintainers apply a
|
||||
coherent author-attributed squash onto current `ouroboros`, choose the actual
|
||||
MAJOR/MINOR/PATCH version, synchronize all P9 carriers, and run the ordinary
|
||||
production review against the exact landing parent/tree/VERSION/tag binding.
|
||||
If the target advances, that final binding is rebuilt and reviewed again.
|
||||
|
||||
The commit gate is intentionally one-pass: one substantive request per triad
|
||||
slot and one per scope slot. Do not reuse task-acceptance retries, generic
|
||||
capability resolvers, chunk fan-out, or degraded Low retries to multiply P3
|
||||
|
|
|
|||
|
|
@ -14,35 +14,12 @@ from typing import Optional, Sequence
|
|||
DEFAULT_PYTEST_ARGS = ["tests/", "-q", "--tb=line", "--no-header"]
|
||||
|
||||
|
||||
def _preflight_worker_count() -> int:
|
||||
"""Bounded xdist parallelism: fast on big hosts, polite on shared ones."""
|
||||
raw = os.environ.get("OUROBOROS_PREFLIGHT_PYTEST_WORKERS", "")
|
||||
try:
|
||||
parsed = int(raw)
|
||||
if parsed > 0:
|
||||
return parsed
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
return max(1, min(16, os.cpu_count() or 1))
|
||||
|
||||
|
||||
def _default_preflight_pytest_args() -> list[str]:
|
||||
"""Parallel hermetic smoke mirroring the CI quick-test lane.
|
||||
|
||||
A command-line ``-m`` REPLACES the pyproject ``addopts`` markexpr, so the
|
||||
full default-exclusion list must be repeated here — otherwise the network/
|
||||
browser lanes (skill_smoke, integration, ui_browser, ...) silently enter
|
||||
every hermetic preflight. The serial lane and the full suite remain the
|
||||
caller's separate responsibility (CI / operator runs).
|
||||
"""
|
||||
return [
|
||||
*DEFAULT_PYTEST_ARGS,
|
||||
"-n", str(_preflight_worker_count()),
|
||||
"--dist", "loadscope",
|
||||
"-m",
|
||||
"not serial and not integration and not browser and not ui_browser"
|
||||
" and not ui_browser_docker and not portable_detail and not skill_smoke",
|
||||
]
|
||||
"""Serial hermetic gate; CI alone owns the parallel/serial split."""
|
||||
# Do not pass ``-m`` here: the repository's pyproject addopts remains the
|
||||
# SSOT for excluding integration/browser/portable/smoke marker lanes, while
|
||||
# serial-marked default-suite tests stay inside the fail-closed commit gate.
|
||||
return list(DEFAULT_PYTEST_ARGS)
|
||||
|
||||
|
||||
def _run_git(repo_dir: pathlib.Path, args: Sequence[str], *, input_text: str = "", timeout: int = 30) -> subprocess.CompletedProcess[str]:
|
||||
|
|
@ -90,15 +67,19 @@ def _copy_untracked(repo_dir: pathlib.Path, worktree: pathlib.Path) -> None:
|
|||
|
||||
def _preflight_env(temp_root: pathlib.Path, repo_worktree: pathlib.Path) -> dict:
|
||||
env = dict(os.environ)
|
||||
env.pop("OUROBOROS_MANAGED_BY_LAUNCHER", None)
|
||||
# Scrub secret-class variables: the hermetic preflight pytest must not
|
||||
# inherit live credentials — a (possibly self-written) test could exfiltrate
|
||||
# or spend with them. OUROBOROS_* wiring stays (suite needs it).
|
||||
# The candidate suite must not inherit live runtime behaviour or credentials.
|
||||
# A disposable data/settings/repo triple is injected below; every other
|
||||
# OUROBOROS_* value is owner/runtime state, not test wiring. Keeping those
|
||||
# values made a supposedly hermetic preflight depend on the operator's live
|
||||
# safety/review/mode settings and could also expose prefixed secrets to a
|
||||
# self-written test.
|
||||
secret_suffixes = ("_API_KEY", "_TOKEN", "_PASSWORD", "_CREDENTIALS", "_SECRET")
|
||||
for key in list(env):
|
||||
if key.startswith("OUROBOROS_"):
|
||||
continue
|
||||
if key.endswith(secret_suffixes) or key.startswith("GH_"):
|
||||
if (
|
||||
key.startswith("OUROBOROS_")
|
||||
or key.endswith(secret_suffixes)
|
||||
or key.startswith("GH_")
|
||||
):
|
||||
env.pop(key, None)
|
||||
temp_root = pathlib.Path(temp_root).resolve(strict=False)
|
||||
repo_worktree = pathlib.Path(repo_worktree).resolve(strict=False)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||
|
||||
[project]
|
||||
name = "ouroboros"
|
||||
version = "6.67.0"
|
||||
version = "6.68.0"
|
||||
description = "Self-creating AI agent with constitution, background consciousness, and persistent identity"
|
||||
readme = "README.md"
|
||||
license = {text = "MIT"}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -67,6 +67,12 @@ _SERIAL_TEST_FILES = frozenset({
|
|||
# trees / sweeps processes referencing a temp root → can collateral-damage sibling xdist
|
||||
# workers under -n (their unrelated tests then fail as a crashed-worker batch).
|
||||
"test_preflight_runner.py",
|
||||
# Imports/mutates the process-global server settings facade; when xdist
|
||||
# reuses a worker after unrelated server tests, cached route/probe state can
|
||||
# escape monkeypatch restoration and turn the mocked capability probe into
|
||||
# a real network attempt. Keep the whole hot-reload contract in the serial
|
||||
# lane, matching its process-global subject.
|
||||
"test_settings_budget_hotreload.py",
|
||||
# spawns real long-lived sleeper subprocesses via the legacy ouroboros.tools.services path
|
||||
# AND mutates the module-global tools.services._SERVICES (NOT covered by the
|
||||
# _isolate_workspace_executor_globals fixture, which isolates a different dict).
|
||||
|
|
|
|||
52
tests/test_contributor_flow.py
Normal file
52
tests/test_contributor_flow.py
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
def test_public_contributor_flow_targets_working_branch_and_real_review_profile():
|
||||
guide = (ROOT / "CONTRIBUTING.md").read_text(encoding="utf-8")
|
||||
|
||||
assert "against `ouroboros`, not `main` or `ouroboros-stable`" in guide
|
||||
assert "--contributor" in guide
|
||||
assert "--base-ref upstream/ouroboros" in guide
|
||||
assert "--head-ref HEAD" in guide
|
||||
assert "does **not** run the Claude advisory pre-review" in guide
|
||||
assert "Do not bump the version" in guide
|
||||
assert "review-packet.zip" in guide
|
||||
assert "evidence, not a promise to merge" in guide
|
||||
|
||||
|
||||
def test_pull_request_template_collects_fast_path_evidence_without_version_bump():
|
||||
template = (ROOT / ".github" / "PULL_REQUEST_TEMPLATE.md").read_text(
|
||||
encoding="utf-8"
|
||||
)
|
||||
|
||||
assert "The PR base branch is `ouroboros`" in template
|
||||
assert "I did **not** bump `VERSION`" in template
|
||||
assert "Reviewed base SHA" in template
|
||||
assert "Triad verdict" in template
|
||||
assert "Scope verdict" in template
|
||||
assert "If not run, reason" in template
|
||||
assert "Agent assistance (optional)" in template
|
||||
|
||||
|
||||
def test_pull_request_ci_is_fork_safe_and_does_not_enable_provider_jobs():
|
||||
workflow = (ROOT / ".github" / "workflows" / "ci.yml").read_text(
|
||||
encoding="utf-8"
|
||||
)
|
||||
|
||||
assert "pull_request:\n branches: [ouroboros]" in workflow
|
||||
assert "\n pull_request_target:" not in workflow
|
||||
assert "permissions:\n contents: read" in workflow
|
||||
assert "github.event_name == 'pull_request' && github.base_ref == 'ouroboros'" in workflow
|
||||
assert "release:\n" in workflow
|
||||
assert " contents: write" in workflow
|
||||
|
||||
|
||||
def test_repository_has_explicit_mit_license_holder():
|
||||
license_text = (ROOT / "LICENSE").read_text(encoding="utf-8")
|
||||
|
||||
assert license_text.startswith("MIT License\n")
|
||||
assert "Copyright (c) 2026 Anton Razzhigaev" in license_text
|
||||
assert "Andrew Kaznacheev" not in license_text
|
||||
|
|
@ -471,6 +471,7 @@ def test_gaia_openai_websearch_pin_drops_base_url(monkeypatch):
|
|||
assert "OPENAI_BASE_URL" not in env # dropped so official web_search stays enabled
|
||||
|
||||
|
||||
@pytest.mark.serial
|
||||
def test_gaia_render_injects_keys_and_free_host_service_port(tmp_path, monkeypatch):
|
||||
# Out-of-the-box coexistence with a running desktop app: the rendered settings must
|
||||
# carry a FREE Host-Service port (not the default 8767) and the REAL provider key for
|
||||
|
|
|
|||
|
|
@ -1,19 +1,56 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
|
||||
from scripts.run_external_review import (
|
||||
_REVIEW_SUBSTRATE_PATHS,
|
||||
_apply_contributor_landing_obligations,
|
||||
_apply_contributor_review_env,
|
||||
_assert_contributor_openrouter_config,
|
||||
_classify_exit,
|
||||
_contributor_snapshot,
|
||||
_create_isolated_checkout,
|
||||
_openrouter_key_health,
|
||||
_openrouter_pool,
|
||||
_remove_isolated_checkout,
|
||||
_require_contributor_budget,
|
||||
_resolved_review_config,
|
||||
_review_evidence_and_cost,
|
||||
_select_healthy_openrouter_key,
|
||||
_settings_defaults_at_ref,
|
||||
_write_contributor_packet,
|
||||
)
|
||||
|
||||
|
||||
def test_contributor_trust_boundary_covers_functional_review_dependencies():
|
||||
from ouroboros.tools.scope_review import _CANONICAL_CONTEXT_DOCS
|
||||
|
||||
assert set(_CANONICAL_CONTEXT_DOCS).issubset(_REVIEW_SUBSTRATE_PATHS)
|
||||
assert {
|
||||
"docs/ARCHITECTURE.md",
|
||||
"ouroboros/capability_evidence.py",
|
||||
"ouroboros/code_intelligence.py",
|
||||
"ouroboros/deadline_utils.py",
|
||||
"ouroboros/outcomes.py",
|
||||
"ouroboros/platform_layer.py",
|
||||
"ouroboros/pricing.py",
|
||||
"ouroboros/review_state.py",
|
||||
"ouroboros/runtime_mode_policy.py",
|
||||
"ouroboros/usage_accounting.py",
|
||||
"ouroboros/utils.py",
|
||||
"ouroboros/tools/claude_advisory_review.py",
|
||||
"ouroboros/tools/registry.py",
|
||||
"ouroboros/tools/release_sync.py",
|
||||
"ouroboros/tools/review_synthesis.py",
|
||||
}.issubset(_REVIEW_SUBSTRATE_PATHS)
|
||||
|
||||
|
||||
def test_external_review_script_delegates_verdict_to_production_gate():
|
||||
source = Path("scripts/run_external_review.py").read_text(encoding="utf-8")
|
||||
assert "v6.10.0" not in source
|
||||
|
|
@ -21,11 +58,12 @@ def test_external_review_script_delegates_verdict_to_production_gate():
|
|||
assert "_run_non_committing_review_cycle" in source
|
||||
assert "adaptive_quorum" not in source
|
||||
assert "aggregate_review_verdict" not in source
|
||||
# The wrapper stays thin: no operator-side re-binding layer, and the REAL
|
||||
# advisory (not a bypass) is the default first stage.
|
||||
# The default operator lane still runs the REAL advisory. Contributor mode
|
||||
# explicitly skips it while reusing the production triad+scope cycle.
|
||||
assert "operator_binding" not in source
|
||||
assert "_handle_advisory_pre_review" in source
|
||||
assert "skip_advisory_review=True" not in source
|
||||
assert "skip_advisory_review=args.contributor" in source
|
||||
assert "_CONTRIBUTOR_PROFILE = \"external_pr_readiness\"" in source
|
||||
|
||||
|
||||
def test_external_review_script_defaults_to_pro_mode():
|
||||
|
|
@ -51,6 +89,8 @@ def test_external_review_script_resolves_models_and_efforts(monkeypatch):
|
|||
monkeypatch.setenv("OUROBOROS_SCOPE_REVIEW_MODELS", "openai/gpt-5.5")
|
||||
monkeypatch.setenv("OUROBOROS_EFFORT_REVIEW", "high")
|
||||
monkeypatch.setenv("OUROBOROS_EFFORT_SCOPE_REVIEW", "high")
|
||||
monkeypatch.setenv("OUROBOROS_REVIEW_ENFORCEMENT", "blocking")
|
||||
monkeypatch.setenv("OUROBOROS_SCOPE_REVIEW_FLOOR", "blocking_1m")
|
||||
|
||||
config = _resolved_review_config()
|
||||
|
||||
|
|
@ -62,6 +102,251 @@ def test_external_review_script_resolves_models_and_efforts(monkeypatch):
|
|||
assert config["triad_effort"] == "high"
|
||||
assert config["scope_models"] == ["openai/gpt-5.5"]
|
||||
assert config["scope_effort"] == "high"
|
||||
assert config["review_enforcement"] == "blocking"
|
||||
assert config["scope_review_floor"] == "blocking_1m"
|
||||
|
||||
|
||||
def _write_target_config(repo: Path) -> None:
|
||||
package = repo / "ouroboros"
|
||||
package.mkdir(exist_ok=True)
|
||||
(package / "config.py").write_text(
|
||||
"SETTINGS_DEFAULTS = {\n"
|
||||
" 'OUROBOROS_REVIEW_MODELS': 'anthropic/fable,openai/sol,google/flash',\n"
|
||||
" 'OUROBOROS_SCOPE_REVIEW_MODELS': 'anthropic/fable',\n"
|
||||
" 'OUROBOROS_EFFORT_REVIEW': 'high',\n"
|
||||
" 'OUROBOROS_EFFORT_SCOPE_REVIEW': 'high',\n"
|
||||
" 'OUROBOROS_SCOPE_REVIEW_FLOOR': 'blocking_1m',\n"
|
||||
"}\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
|
||||
def _init_contributor_repo(tmp_path: Path, monkeypatch) -> Path:
|
||||
repo = tmp_path / "repo"
|
||||
repo.mkdir()
|
||||
_git(repo, "init")
|
||||
_git(repo, "config", "user.email", "test@example.com")
|
||||
_git(repo, "config", "user.name", "Test")
|
||||
_git(repo, "config", "core.autocrlf", "false")
|
||||
(repo / "scripts").mkdir()
|
||||
(repo / "scripts" / "run_external_review.py").write_text("# base script\n", encoding="utf-8")
|
||||
_write_target_config(repo)
|
||||
(repo / "ouroboros" / "review_substrate.py").write_text(
|
||||
"# trusted review substrate\n", encoding="utf-8"
|
||||
)
|
||||
(repo / "ouroboros" / "utils.py").write_text(
|
||||
"# trusted review utilities\n", encoding="utf-8"
|
||||
)
|
||||
(repo / "ouroboros" / "tools").mkdir()
|
||||
(repo / "ouroboros" / "tools" / "registry.py").write_text(
|
||||
"# trusted review context\n", encoding="utf-8"
|
||||
)
|
||||
(repo / "pyproject.toml").write_text(
|
||||
'[project]\nname = "test-project"\nversion = "1.2.3"\n',
|
||||
encoding="utf-8",
|
||||
)
|
||||
(repo / "VERSION").write_text("1.2.3\n", encoding="utf-8")
|
||||
(repo / "a.txt").write_text("base\n", encoding="utf-8")
|
||||
_git(repo, "add", "-A")
|
||||
_git(repo, "commit", "-m", "base")
|
||||
_git(repo, "branch", "base")
|
||||
(repo / "a.txt").write_text("proposal\n", encoding="utf-8")
|
||||
_git(repo, "add", "a.txt")
|
||||
_git(repo, "commit", "-m", "proposal")
|
||||
|
||||
import scripts.run_external_review as module
|
||||
|
||||
monkeypatch.setattr(module, "REPO", repo)
|
||||
return repo
|
||||
|
||||
|
||||
def test_target_base_defaults_override_local_review_settings(tmp_path, monkeypatch):
|
||||
_init_contributor_repo(tmp_path, monkeypatch)
|
||||
defaults = _settings_defaults_at_ref("base")
|
||||
monkeypatch.setenv("OUROBOROS_REVIEW_MODELS", "local/override")
|
||||
monkeypatch.setenv("OUROBOROS_EFFORT_REVIEW", "low")
|
||||
|
||||
_apply_contributor_review_env(defaults)
|
||||
|
||||
assert defaults["OUROBOROS_REVIEW_MODELS"] == (
|
||||
"anthropic/fable,openai/sol,google/flash"
|
||||
)
|
||||
assert os.environ["OUROBOROS_REVIEW_MODELS"] == defaults[
|
||||
"OUROBOROS_REVIEW_MODELS"
|
||||
]
|
||||
assert os.environ["OUROBOROS_EFFORT_REVIEW"] == "high"
|
||||
assert os.environ["OUROBOROS_REVIEW_ENFORCEMENT"] == "blocking"
|
||||
assert os.environ["OUROBOROS_OBSERVABILITY_KEEP_RAW"] == "0"
|
||||
assert os.environ["OUROBOROS_PRE_PUSH_TESTS"] == "1"
|
||||
assert os.environ["OUROBOROS_PREFLIGHT_DIFF_AWARE"] == "false"
|
||||
|
||||
|
||||
def test_contributor_defaults_reject_explicit_direct_provider_route(monkeypatch):
|
||||
defaults = {
|
||||
"OUROBOROS_REVIEW_MODELS": "anthropic::claude-fable-5",
|
||||
"OUROBOROS_SCOPE_REVIEW_MODELS": "anthropic/claude-fable-5",
|
||||
"OUROBOROS_EFFORT_REVIEW": "high",
|
||||
"OUROBOROS_EFFORT_SCOPE_REVIEW": "high",
|
||||
"OUROBOROS_SCOPE_REVIEW_FLOOR": "blocking_1m",
|
||||
}
|
||||
with pytest.raises(RuntimeError, match="non-OpenRouter"):
|
||||
_apply_contributor_review_env(defaults)
|
||||
|
||||
|
||||
def test_contributor_budget_must_be_explicit_positive_and_finite(monkeypatch):
|
||||
monkeypatch.delenv("TOTAL_BUDGET", raising=False)
|
||||
with pytest.raises(RuntimeError, match="TOTAL_BUDGET is required"):
|
||||
_require_contributor_budget()
|
||||
for invalid in ("0", "-1", "inf", "not-a-number"):
|
||||
monkeypatch.setenv("TOTAL_BUDGET", invalid)
|
||||
with pytest.raises(RuntimeError, match="positive finite"):
|
||||
_require_contributor_budget()
|
||||
monkeypatch.setenv("TOTAL_BUDGET", "125.50")
|
||||
assert _require_contributor_budget() == 125.5
|
||||
|
||||
|
||||
def test_contributor_snapshot_binds_clean_base_head_and_tree(tmp_path, monkeypatch):
|
||||
repo = _init_contributor_repo(tmp_path, monkeypatch)
|
||||
|
||||
snapshot = _contributor_snapshot("base", "HEAD")
|
||||
|
||||
assert snapshot["base_sha"] == snapshot["merge_base_sha"]
|
||||
assert snapshot["target_version"] == "1.2.3"
|
||||
assert snapshot["head_tree_sha"] == _git(repo, "rev-parse", "HEAD^{tree}").strip()
|
||||
assert snapshot["changed_paths"] == ["a.txt"]
|
||||
assert snapshot["review_substrate_changed"] == []
|
||||
assert snapshot["diff_sha256"]
|
||||
|
||||
(repo / "dirty.txt").write_text("not committed\n", encoding="utf-8")
|
||||
with pytest.raises(RuntimeError, match="not clean"):
|
||||
_contributor_snapshot("base", "HEAD")
|
||||
|
||||
|
||||
def test_contributor_snapshot_rejects_version_bump(tmp_path, monkeypatch):
|
||||
repo = _init_contributor_repo(tmp_path, monkeypatch)
|
||||
(repo / "VERSION").write_text("1.2.4\n", encoding="utf-8")
|
||||
_git(repo, "add", "VERSION")
|
||||
_git(repo, "commit", "-m", "bad contributor bump")
|
||||
|
||||
with pytest.raises(RuntimeError, match="must not bump VERSION"):
|
||||
_contributor_snapshot("base", "HEAD")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"relative_path",
|
||||
[
|
||||
"ouroboros/review_substrate.py",
|
||||
"ouroboros/utils.py",
|
||||
"ouroboros/tools/registry.py",
|
||||
],
|
||||
)
|
||||
def test_contributor_snapshot_flags_transitive_review_substrate_changes(
|
||||
tmp_path, monkeypatch, relative_path
|
||||
):
|
||||
repo = _init_contributor_repo(tmp_path, monkeypatch)
|
||||
path = repo / relative_path
|
||||
path.write_text("# proposal changes trusted review substrate\n", encoding="utf-8")
|
||||
_git(repo, "add", str(path.relative_to(repo)))
|
||||
_git(repo, "commit", "-m", "change review substrate")
|
||||
|
||||
snapshot = _contributor_snapshot("base", "HEAD")
|
||||
|
||||
assert snapshot["review_substrate_changed"] == [relative_path]
|
||||
assert snapshot["review_substrate_matches_base"] is False
|
||||
|
||||
|
||||
def test_contributor_snapshot_flags_release_carrier_changes_without_version_file(
|
||||
tmp_path, monkeypatch
|
||||
):
|
||||
repo = _init_contributor_repo(tmp_path, monkeypatch)
|
||||
path = repo / "pyproject.toml"
|
||||
path.write_text(
|
||||
'[project]\nname = "test-project"\nversion = "1.2.4"\n',
|
||||
encoding="utf-8",
|
||||
)
|
||||
_git(repo, "add", "pyproject.toml")
|
||||
_git(repo, "commit", "-m", "change package carrier only")
|
||||
|
||||
snapshot = _contributor_snapshot("base", "HEAD")
|
||||
|
||||
assert snapshot["release_metadata_or_machinery_changed"] is True
|
||||
assert snapshot["release_sensitive_changes"]["carrier_fields"] == [
|
||||
"pyproject.project.version"
|
||||
]
|
||||
|
||||
|
||||
def test_contributor_landing_obligations_are_exact_typed_items_only():
|
||||
version_only = {
|
||||
"status": "blocked",
|
||||
"block_reason": "critical_findings",
|
||||
"combined_findings": [
|
||||
{"item": "version_bump", "severity": "critical"},
|
||||
{"item": "changelog_and_badge", "severity": "critical"},
|
||||
],
|
||||
}
|
||||
deferred = _apply_contributor_landing_obligations(version_only)
|
||||
assert deferred["status"] == "passed"
|
||||
assert {item["item"] for item in deferred["landing_obligations"]} == {
|
||||
"version_bump",
|
||||
"changelog_and_badge",
|
||||
}
|
||||
|
||||
real_defect = {
|
||||
**version_only,
|
||||
"combined_findings": [
|
||||
*version_only["combined_findings"],
|
||||
{"item": "self_consistency", "severity": "critical"},
|
||||
],
|
||||
}
|
||||
assert _apply_contributor_landing_obligations(real_defect) == real_defect
|
||||
scope_failure = {
|
||||
**version_only,
|
||||
"block_reason": "scope_blocked",
|
||||
}
|
||||
assert _apply_contributor_landing_obligations(scope_failure) == scope_failure
|
||||
assert _apply_contributor_landing_obligations(
|
||||
version_only,
|
||||
release_sensitive=True,
|
||||
) == version_only
|
||||
|
||||
|
||||
def test_contributor_packet_is_redacted_and_shareable(tmp_path):
|
||||
output = tmp_path / "packet"
|
||||
output.mkdir()
|
||||
local_root = "/Users/example/private/repo"
|
||||
packet = _write_contributor_packet(
|
||||
output_dir=output,
|
||||
snapshot={
|
||||
"base_sha": "a" * 40,
|
||||
"head_sha": "b" * 40,
|
||||
"review_substrate_changed": [],
|
||||
},
|
||||
resolved_config={"triad_models": ["anthropic/fable"]},
|
||||
outcome={"status": "passed", "path": local_root, "api_key": "sk-secret-value"},
|
||||
exit_code=0,
|
||||
evidence_refs=[],
|
||||
cost_report={"reported_actor_cost_usd": 1.0},
|
||||
elapsed_sec=1.5,
|
||||
triad_raw=[{"authorization": "Bearer secret-token-value", "path": local_root}],
|
||||
scope_raw={"status": "responded"},
|
||||
degraded_reasons=["reviewer-3=parse_failure (quorum still met)"],
|
||||
replacements=[(local_root, "$REPO")],
|
||||
)
|
||||
|
||||
evidence_text = (output / "review-evidence.json").read_text(encoding="utf-8")
|
||||
full_text = (output / "full-output.txt").read_text(encoding="utf-8")
|
||||
assert "sk-secret-value" not in evidence_text
|
||||
assert "secret-token-value" not in full_text
|
||||
assert local_root not in evidence_text + full_text
|
||||
assert "$REPO" in evidence_text + full_text
|
||||
assert "production_triad_quorum_plus_authoritative_scope" in evidence_text
|
||||
assert "quorum still met" in evidence_text
|
||||
with zipfile.ZipFile(packet) as archive:
|
||||
assert set(archive.namelist()) == {
|
||||
"review-evidence.json",
|
||||
"outcome.json",
|
||||
"full-output.txt",
|
||||
}
|
||||
|
||||
|
||||
def _complete_ctx():
|
||||
|
|
@ -147,6 +432,84 @@ def test_openrouter_pool_orders_hope_keys_last(monkeypatch, tmp_path):
|
|||
]
|
||||
|
||||
|
||||
def test_contributor_openrouter_preflight_fails_closed(monkeypatch):
|
||||
import scripts.run_external_review as module
|
||||
|
||||
monkeypatch.setattr(module, "_openrouter_pool", lambda: [])
|
||||
with pytest.raises(RuntimeError, match="no OpenRouter key"):
|
||||
_select_healthy_openrouter_key(required=True)
|
||||
|
||||
monkeypatch.setattr(module, "_openrouter_pool", lambda: [("key", "secret")])
|
||||
monkeypatch.setattr(
|
||||
module,
|
||||
"_openrouter_key_health",
|
||||
lambda _token, **_kwargs: (False, "model_probe_http_403"),
|
||||
)
|
||||
with pytest.raises(RuntimeError, match="no healthy OpenRouter key"):
|
||||
_select_healthy_openrouter_key(required=True)
|
||||
|
||||
|
||||
def test_contributor_resolved_config_rejects_direct_provider_actors():
|
||||
defaults = {
|
||||
"OUROBOROS_REVIEW_MODELS": "anthropic/claude-fable-5,openai/gpt-5.6-sol",
|
||||
"OUROBOROS_SCOPE_REVIEW_MODELS": "anthropic/claude-fable-5",
|
||||
"OUROBOROS_EFFORT_REVIEW": "high",
|
||||
"OUROBOROS_EFFORT_SCOPE_REVIEW": "high",
|
||||
"OUROBOROS_SCOPE_REVIEW_FLOOR": "blocking_1m",
|
||||
}
|
||||
_assert_contributor_openrouter_config({
|
||||
"triad_models": ["anthropic/claude-fable-5", "openai/gpt-5.6-sol"],
|
||||
"scope_models": ["anthropic/claude-fable-5"],
|
||||
"triad_effort": "high",
|
||||
"scope_effort": "high",
|
||||
"scope_review_floor": "blocking_1m",
|
||||
}, defaults)
|
||||
with pytest.raises(RuntimeError, match="exclusively through OpenRouter"):
|
||||
_assert_contributor_openrouter_config({
|
||||
"triad_models": ["anthropic::claude-fable-5"],
|
||||
"scope_models": ["anthropic/claude-fable-5"],
|
||||
"triad_effort": "high",
|
||||
"scope_effort": "high",
|
||||
"scope_review_floor": "blocking_1m",
|
||||
}, defaults)
|
||||
with pytest.raises(RuntimeError, match="drifted from target-base defaults"):
|
||||
_assert_contributor_openrouter_config({
|
||||
"triad_models": ["anthropic/claude-fable-5"],
|
||||
"scope_models": ["anthropic/claude-fable-5"],
|
||||
"triad_effort": "high",
|
||||
"scope_effort": "high",
|
||||
"scope_review_floor": "blocking_1m",
|
||||
}, defaults)
|
||||
|
||||
|
||||
def test_normal_key_probe_stays_single_model_but_contributor_probes_all(monkeypatch):
|
||||
import scripts.run_external_review as module
|
||||
|
||||
calls: list[str] = []
|
||||
monkeypatch.setattr(module, "_review_probe_models", lambda: ["one", "two", "three"])
|
||||
monkeypatch.setattr(
|
||||
module,
|
||||
"_probe_model_for_key",
|
||||
lambda _token, model: (calls.append(model) is None, f"ok:{model}"),
|
||||
)
|
||||
class Response:
|
||||
status_code = 200
|
||||
|
||||
@staticmethod
|
||||
def json():
|
||||
return {"data": {"limit": None}}
|
||||
|
||||
import httpx
|
||||
|
||||
monkeypatch.setattr(httpx, "get", lambda *_args, **_kwargs: Response())
|
||||
|
||||
assert _openrouter_key_health("secret")[0] is True
|
||||
assert calls == ["one"]
|
||||
calls.clear()
|
||||
assert _openrouter_key_health("secret", probe_all_models=True)[0] is True
|
||||
assert calls == ["one", "two", "three"]
|
||||
|
||||
|
||||
def _git(repo: Path, *args: str) -> str:
|
||||
proc = subprocess.run(
|
||||
["git", *args], cwd=str(repo), capture_output=True, text=True, check=True,
|
||||
|
|
|
|||
|
|
@ -36,6 +36,9 @@ def test_hermetic_pytest_applies_candidate_diff_and_scrubs_live_env(tmp_path, mo
|
|||
assert value.FLAG is True
|
||||
assert extra_value.FLAG is True
|
||||
assert "OUROBOROS_MANAGED_BY_LAUNCHER" not in os.environ
|
||||
assert "OUROBOROS_SAFETY_MODE" not in os.environ
|
||||
assert "OUROBOROS_TASK_REVIEW_MODE" not in os.environ
|
||||
assert "OUROBOROS_FAKE_API_KEY" not in os.environ
|
||||
assert "ouroboros-preflight-" in os.environ["OUROBOROS_DATA_DIR"]
|
||||
assert os.environ["OUROBOROS_SETTINGS_PATH"].startswith(os.environ["OUROBOROS_DATA_DIR"])
|
||||
assert "ouroboros-preflight-" in os.environ["OUROBOROS_REPO_DIR"]
|
||||
|
|
@ -55,6 +58,9 @@ def test_hermetic_pytest_applies_candidate_diff_and_scrubs_live_env(tmp_path, mo
|
|||
(repo / "extra_value.py").write_text("FLAG = True\n", encoding="utf-8")
|
||||
|
||||
monkeypatch.setenv("OUROBOROS_MANAGED_BY_LAUNCHER", "1")
|
||||
monkeypatch.setenv("OUROBOROS_SAFETY_MODE", "light")
|
||||
monkeypatch.setenv("OUROBOROS_TASK_REVIEW_MODE", "off")
|
||||
monkeypatch.setenv("OUROBOROS_FAKE_API_KEY", "must-not-reach-tests")
|
||||
result = run_hermetic_pytest(repo, timeout=30)
|
||||
|
||||
assert result is None
|
||||
|
|
@ -158,34 +164,11 @@ def test_hermetic_pytest_timeout_reaps_detached_session_child(tmp_path, monkeypa
|
|||
assert not alive, f"detached child {child_pid} survived preflight timeout reaping"
|
||||
|
||||
|
||||
def test_default_preflight_args_are_parallel_and_non_serial(monkeypatch):
|
||||
def test_default_preflight_args_are_serial_and_keep_default_suite():
|
||||
from ouroboros import preflight_runner as pr
|
||||
|
||||
monkeypatch.delenv("OUROBOROS_PREFLIGHT_PYTEST_WORKERS", raising=False)
|
||||
args = pr._default_preflight_pytest_args()
|
||||
assert args[: len(pr.DEFAULT_PYTEST_ARGS)] == pr.DEFAULT_PYTEST_ARGS
|
||||
workers = int(args[args.index("-n") + 1])
|
||||
assert 1 <= workers <= 16
|
||||
assert args[args.index("--dist") + 1] == "loadscope"
|
||||
# A CLI -m REPLACES the pyproject addopts markexpr, so the hermetic
|
||||
# preflight must repeat EVERY default exclusion, not just "not serial".
|
||||
markexpr = args[args.index("-m") + 1]
|
||||
for lane in (
|
||||
"serial",
|
||||
"integration",
|
||||
"browser",
|
||||
"ui_browser",
|
||||
"ui_browser_docker",
|
||||
"portable_detail",
|
||||
"skill_smoke",
|
||||
):
|
||||
assert f"not {lane}" in markexpr
|
||||
|
||||
|
||||
def test_preflight_worker_count_env_override(monkeypatch):
|
||||
from ouroboros import preflight_runner as pr
|
||||
|
||||
monkeypatch.setenv("OUROBOROS_PREFLIGHT_PYTEST_WORKERS", "4")
|
||||
assert pr._preflight_worker_count() == 4
|
||||
monkeypatch.setenv("OUROBOROS_PREFLIGHT_PYTEST_WORKERS", "garbage")
|
||||
assert 1 <= pr._preflight_worker_count() <= 16
|
||||
assert args == pr.DEFAULT_PYTEST_ARGS
|
||||
assert "-n" not in args
|
||||
assert "--dist" not in args
|
||||
assert "-m" not in args
|
||||
|
|
|
|||
|
|
@ -483,4 +483,4 @@
|
|||
* @property {boolean=} ok
|
||||
*/
|
||||
|
||||
export const GATEWAY_CONTRACT_VERSION = '6.67.0';
|
||||
export const GATEWAY_CONTRACT_VERSION = '6.68.0';
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "ouroboros-web",
|
||||
"version": "6.67.0",
|
||||
"version": "6.68.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"description": "Ouroboros browser UI package boundary",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue