Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 43 additions & 88 deletions .github/workflows/hourly-commercialization-loop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,28 +30,18 @@ jobs:
enable_auto_merge: true
merge_mode: direct_or_auto
update_branches: true
secrets: inherit
Comment thread
seonghobae marked this conversation as resolved.

repair-review-feedback:
needs: inspect-pr-queue
if: ${{ always() }}
permissions:
actions: write
contents: read
issues: write
pull-requests: read
statuses: read
uses: ContextualWisdomLab/.github/.github/workflows/pr-review-fix-scheduler.yml@21397126d708d2d536ccc1d68b0d333653ce9315
with:
target_repository: ContextualWisdomLab/RankWeave
base_branch: main
max_prs: "50"
max_dispatches: "1"
retry_hours: "1"
secrets: inherit

Comment thread
seonghobae marked this conversation as resolved.
# Review-feedback repair is dispatched by the central, always-current
# rankweave-hourly-review-repair.yml caller in ContextualWisdomLab/.github
# (uses: ./.github/workflows/pr-review-fix-scheduler.yml, a same-repository
# reference). A local repair-review-feedback job here previously called
# that reusable workflow cross-repository at a pinned commit SHA; that
# shape can never satisfy pr-review-fix-scheduler.yml's same-repository
# trusted-source check (github.repository == ContextualWisdomLab/.github),
# so every run failed before any job was scheduled ("workflow file issue",
# zero jobs created) for as long as that hardening has been in place.
revalidate-pr-queue:
needs: repair-review-feedback
needs: inspect-pr-queue
if: ${{ always() }}
permissions:
actions: write
Expand All @@ -69,15 +59,13 @@ jobs:
enable_auto_merge: true
merge_mode: direct_or_auto
update_branches: true
secrets: inherit

develop-next-product-gap:
needs: [inspect-pr-queue, repair-review-feedback, revalidate-pr-queue]
needs: [inspect-pr-queue, revalidate-pr-queue]
if: >-
${{
always() &&
needs.inspect-pr-queue.result == 'success' &&
needs.repair-review-feedback.result == 'success' &&
needs.revalidate-pr-queue.result == 'success'
}}
Comment thread
seonghobae marked this conversation as resolved.
runs-on: ubuntu-latest
Expand Down Expand Up @@ -156,7 +144,6 @@ jobs:
{
echo "/opencode.json"
echo "/.agent-red-output.txt"
echo "/PR_MESSAGE.md"
} >>"$GITHUB_WORKSPACE/.git/info/exclude"
- name: Verify the trusted base and network-isolation primitive
if: steps.gate.outputs.eligible == 'true'
Expand Down Expand Up @@ -206,11 +193,18 @@ jobs:
"lsp": false,
"permission": {
"read": {
"*": "allow",
".git/**": "deny",
"opencode.json": "deny",
".env": "deny",
".env.*": "deny"
"*": "deny",
"AGENTS.md": "allow",
"ARCHITECTURE.md": "allow",
"CHANGELOG.md": "allow",
"CLAUDE.md": "allow",
"LICENSE": "allow",
"README.md": "allow",
"pyproject.toml": "allow",
"uv.lock": "allow",
"docs/**": "allow",
"src/**": "allow",
"tests/**": "allow"
Comment on lines 195 to +207

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ“ Info: PR scope broader than title

Beyond removing the dead job, the diff rewrites both OpenCode read permission blocks to deny-all-with-allowlist and deletes the entire PR_MESSAGE.md title/body pipeline in favor of a static title. Both are captured by the CHANGELOG and new tests, so they read as intentional, but the change is wider than the title suggests.

Open in Devin Review

Was this helpful? React with πŸ‘ or πŸ‘Ž to provide feedback.

},
"edit": {
"*": "deny",
Expand Down Expand Up @@ -247,8 +241,7 @@ jobs:
Write a concise design under docs/superpowers/specs/ and write the failing
pytest regression tests first. During this phase do not modify production
code, package metadata, README.md, AGENTS.md, CHANGELOG.md, workflows, or
any file outside tests/ and docs/superpowers/specs/. Do not write
PR_MESSAGE.md yet.
any file outside tests/ and docs/superpowers/specs/.

The tests must express the buyer-visible contract, preserve RankWeave's
standard-library-only runtime, deterministic and immutable evidence,
Expand Down Expand Up @@ -420,11 +413,19 @@ jobs:
"lsp": false,
"permission": {
"read": {
"*": "allow",
".git/**": "deny",
"opencode.json": "deny",
".env": "deny",
".env.*": "deny"
"*": "deny",
".agent-red-output.txt": "allow",
"AGENTS.md": "allow",
"ARCHITECTURE.md": "allow",
"CHANGELOG.md": "allow",
"CLAUDE.md": "allow",
"LICENSE": "allow",
"README.md": "allow",
"pyproject.toml": "allow",
"uv.lock": "allow",
"docs/**": "allow",
"src/**": "allow",
"tests/**": "allow"
},
"edit": {
"*": "allow",
Expand Down Expand Up @@ -473,11 +474,9 @@ jobs:
statistical or standards claim without a primary source already recorded in
the repository. Figma is not applicable because RankWeave has no UI.

Write PR_MESSAGE.md at the repository root. Put a concise PR title on the
first line and a body after it describing buyer impact, evidence,
compatibility, and the exact validation commands. Do not commit, push,
open, approve, merge, publish, or release anything; the workflow performs
deterministic validation and packages one protected pull request.
Do not commit, push, open, approve, merge, publish, or release anything;
the workflow performs deterministic validation and packages one protected
pull request with maintainer-owned metadata.
PROMPT
)"

Expand Down Expand Up @@ -509,15 +508,8 @@ jobs:
if: steps.gate.outputs.eligible == 'true'
run: |
set -euo pipefail
pr_message_backup="${RUNNER_TEMP}/agent-pr-message.md"
if [ -f PR_MESSAGE.md ]; then
cp PR_MESSAGE.md "$pr_message_backup"
fi
rm -f opencode.json .agent-red-output.txt
git clean -fdX
if [ -f "$pr_message_backup" ]; then
cp "$pr_message_backup" PR_MESSAGE.md
fi
"$AUTOMATION_VENV/bin/python" - <<'PY'
from __future__ import annotations

Expand Down Expand Up @@ -583,7 +575,6 @@ jobs:
"CHANGELOG.md",
"README.md",
"pyproject.toml",
"PR_MESSAGE.md",
}
allowed_prefixes = ("src/rankweave/", "tests/", "docs/")
allowed_suffixes = {
Expand Down Expand Up @@ -613,7 +604,7 @@ jobs:
and not path_text.startswith(allowed_prefixes)
):
raise SystemExit(f"path is outside autonomous scope: {path_text}")
if path_text != "PR_MESSAGE.md" and path.suffix not in allowed_suffixes:
if path.suffix not in allowed_suffixes:
raise SystemExit(f"non-text or unsupported path changed: {path_text}")
if path_text.startswith("src/rankweave/") and path.suffix == ".py":
production_changed = True
Expand Down Expand Up @@ -913,44 +904,8 @@ jobs:

title="RankWeave autonomous commercialization increment"
body_file="${RUNNER_TEMP}/pr-body.md"
if [ -f PR_MESSAGE.md ]; then
/usr/bin/python3 -I -S - <<'PY'
from __future__ import annotations

import os
from pathlib import Path

source = Path("PR_MESSAGE.md").read_text(encoding="utf-8")
lines = source.splitlines()
candidate = lines[0].lstrip("#").strip() if lines else ""
if (
10 <= len(candidate) <= 120
and not candidate.startswith("-")
and all(character.isprintable() for character in candidate)
):
title = candidate
else:
title = "RankWeave autonomous commercialization increment"
body = "\n".join(lines[1:]).strip()
if not body:
body = "Autonomous NVIDIA NIM increment; see the diff and CHANGELOG.md."
if len(body.encode("utf-8")) > 20_000:
raise SystemExit("PR body exceeds 20,000 UTF-8 bytes")
Path(os.environ["RUNNER_TEMP"], "pr-title.txt").write_text(
title,
encoding="utf-8",
)
Path(os.environ["RUNNER_TEMP"], "pr-body.md").write_text(
body + "\n",
encoding="utf-8",
)
PY
title="$(cat "${RUNNER_TEMP}/pr-title.txt")"
rm -f PR_MESSAGE.md
else
echo "Autonomous NVIDIA NIM increment; see the diff and CHANGELOG.md." \
>"$body_file"
fi
echo "Autonomous NVIDIA NIM increment; see the diff and CHANGELOG.md." \
>"$body_file"

git reset --soft "$AUTOMATION_BASE_SHA"
branch="nim-agent/product-dev-${GITHUB_RUN_ID}"
Expand Down
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,21 @@ All notable changes to rankweave are documented here. The format follows [Keep a

## [Unreleased]

### Fixed
- Restricted both autonomous OpenCode phases to explicit repository read paths
and removed agent-authored pull-request metadata, preventing workspace-external
reads or generated text from becoming a pull-request title or body.
- Removed the `repair-review-feedback` job from
`hourly-commercialization-loop.yml`: it called
`ContextualWisdomLab/.github`'s `pr-review-fix-scheduler.yml`
cross-repository at a pinned commit SHA, a shape that reusable workflow's
same-repository trusted-source hardening can never satisfy
(`github.repository == ContextualWisdomLab/.github`), so every hourly run
failed before any job was scheduled for as long as that hardening has been
in place. Review-feedback repair is now dispatched by a central,
always-current `rankweave-hourly-review-repair.yml` caller added to
`ContextualWisdomLab/.github`, matching the pattern already used by every
other product repository in the organization.
### Changed
- Bumped the pinned `uv` version from `0.11.29` to `0.12.1` in `pyproject.toml`
and every `astral-sh/setup-uv` workflow step (`ci.yml`, `create-release.yml`,
Expand Down
52 changes: 24 additions & 28 deletions tests/test_hourly_commercialization_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
PROJECT_ROOT = Path(__file__).resolve().parents[1]
WORKFLOW_PATH = PROJECT_ROOT / ".github/workflows/hourly-commercialization-loop.yml"
MERGE_WORKFLOW_SHA = "5983b41ace75040c1d81818171ca7d0f3653254e"
FIX_WORKFLOW_SHA = "21397126d708d2d536ccc1d68b0d333653ce9315"


def _workflow_text() -> str:
Expand Down Expand Up @@ -31,14 +30,16 @@ def test_commercialization_loop_uses_pinned_central_pr_governance():
"ContextualWisdomLab/.github/.github/workflows/"
f"pr-review-merge-scheduler.yml@{MERGE_WORKFLOW_SHA}"
)
fix_reference = (
"ContextualWisdomLab/.github/.github/workflows/"
f"pr-review-fix-scheduler.yml@{FIX_WORKFLOW_SHA}"
)
assert workflow.count(merge_reference) == 2
assert workflow.count(fix_reference) == 1
assert 'retry_hours: "1"' in workflow
assert "secrets: inherit" in workflow
uses_references = [
line.split("uses:", maxsplit=1)[1].strip()
for line in workflow.splitlines()
if line.strip().startswith("uses:")
]
assert all("pr-review-fix-scheduler.yml" not in ref for ref in uses_references)
# Review-feedback repair is dispatched by the central, always-current
# rankweave-hourly-review-repair.yml caller in ContextualWisdomLab/.github
# instead of a cross-repository pinned-SHA job here.


def test_product_development_uses_nvidia_nim_and_fails_closed():
Expand Down Expand Up @@ -89,11 +90,23 @@ def test_opencode_permissions_block_execution_network_and_protected_edits():
assert workflow.count(denied_permission) == 2
assert '"tests/**": "allow"' in workflow
assert '"docs/superpowers/specs/**": "allow"' in workflow
assert workflow.count('"read": {\n "*": "deny"') == 2
assert '".github/**": "deny"' in workflow
assert '".git/**": "deny"' in workflow
assert "Do not read GitHub issues, pull requests, external web pages" in workflow


def test_agent_output_never_controls_pull_request_metadata():
workflow = _workflow_text()

assert "PR_MESSAGE.md" not in workflow
assert 'title="RankWeave autonomous commercialization increment"' in workflow
assert (
"packages one protected\n pull request with maintainer-owned metadata"
in workflow
)


def test_agent_control_file_is_immutable_to_autonomous_authoring():
workflow = _workflow_text()
implementation = workflow[
Expand Down Expand Up @@ -222,20 +235,15 @@ def test_final_queue_and_base_are_rechecked_before_pr_creation():
def test_product_development_requires_successful_pr_governance():
workflow = _workflow_text()

assert (
"needs: [inspect-pr-queue, repair-review-feedback, revalidate-pr-queue]"
in workflow
)
assert "needs: [inspect-pr-queue, revalidate-pr-queue]" in workflow
assert "needs.inspect-pr-queue.result == 'success'" in workflow
assert "needs.repair-review-feedback.result == 'success'" in workflow
assert "needs.revalidate-pr-queue.result == 'success'" in workflow


def test_governance_permissions_are_scoped_per_calling_job():
workflow = _workflow_text()
workflow_default = workflow.split("concurrency:", maxsplit=1)[0]
inspect = _job_section(workflow, "inspect-pr-queue", "repair-review-feedback")
repair = _job_section(workflow, "repair-review-feedback", "revalidate-pr-queue")
inspect = _job_section(workflow, "inspect-pr-queue", "revalidate-pr-queue")
revalidate = _job_section(
workflow, "revalidate-pr-queue", "develop-next-product-gap"
)
Expand All @@ -261,17 +269,7 @@ def test_governance_permissions_are_scoped_per_calling_job():
assert permission in merge_job
assert "issues: write" not in merge_job
assert "statuses: read" not in merge_job

for permission in (
"actions: write",
"contents: read",
"issues: write",
"pull-requests: read",
"statuses: read",
):
assert permission in repair
assert "contents: write" not in repair
assert "id-token: write" not in repair
assert "secrets: inherit" not in merge_job


def test_opencode_binary_and_models_are_pinned():
Expand Down Expand Up @@ -300,6 +298,4 @@ def test_untrusted_execution_drops_privileges():
assert workflow.count("--no-new-privs") == 3
assert workflow.count("--bounding-set=-all") == 3
assert workflow.count("PYTHONPATH=$GITHUB_WORKSPACE/src") == 2
assert 'pr_message_backup="${RUNNER_TEMP}/agent-pr-message.md"' in workflow
assert "/usr/bin/python3 -I -S - <<'PY'" in workflow
assert "strict UTF-8" in workflow
Loading