Skip to content

Commit e5e0d36

Browse files
authored
ci(automation): restore LineageWeave hourly repair caller (#1380)
1 parent cdde0d8 commit e5e0d36

7 files changed

Lines changed: 194 additions & 4 deletions

.github/workflows/hourly-nvidia-nim-review-repair.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ on:
1111
- .github/workflows/clearfolio-hourly-review-repair.yml
1212
- .github/workflows/disksage-hourly-review-repair.yml
1313
- .github/workflows/inkspan-hourly-review-repair.yml
14+
- .github/workflows/lineageweave-hourly-review-repair.yml
1415
- .github/workflows/fast-mlsirm-hourly-review-repair.yml
1516
- .github/workflows/github-hourly-review-repair.yml
1617
- .github/workflows/governance-risk-compliance-hourly-review-repair.yml
@@ -34,6 +35,7 @@ on:
3435
- tests/test_bandscope_hourly_review_caller.py
3536
- tests/test_disksage_hourly_review_caller.py
3637
- tests/test_inkspan_hourly_review_caller.py
38+
- tests/test_lineageweave_hourly_review_caller.py
3739
- tests/test_fast_mlsirm_hourly_review_caller.py
3840
- tests/test_github_hourly_conflict_repair.py
3941
- tests/test_governance_risk_compliance_hourly_review_caller.py
@@ -62,6 +64,7 @@ on:
6264
- docs/doctoring/conflict-control-evidence-isolation.md
6365
- docs/doctoring/disksage-hourly-review-caller.md
6466
- docs/doctoring/inkspan-hourly-review-caller.md
67+
- docs/doctoring/lineageweave-hourly-review-caller.md
6568
- docs/doctoring/fast-mlsirm-hourly-review-caller.md
6669
- docs/doctoring/github-hourly-conflict-repair.md
6770
- docs/doctoring/governance-risk-compliance-hourly-review-caller.md
@@ -82,6 +85,7 @@ on:
8285
- .github/workflows/clearfolio-hourly-review-repair.yml
8386
- .github/workflows/disksage-hourly-review-repair.yml
8487
- .github/workflows/inkspan-hourly-review-repair.yml
88+
- .github/workflows/lineageweave-hourly-review-repair.yml
8589
- .github/workflows/fast-mlsirm-hourly-review-repair.yml
8690
- .github/workflows/github-hourly-review-repair.yml
8791
- .github/workflows/governance-risk-compliance-hourly-review-repair.yml
@@ -105,6 +109,7 @@ on:
105109
- tests/test_bandscope_hourly_review_caller.py
106110
- tests/test_disksage_hourly_review_caller.py
107111
- tests/test_inkspan_hourly_review_caller.py
112+
- tests/test_lineageweave_hourly_review_caller.py
108113
- tests/test_fast_mlsirm_hourly_review_caller.py
109114
- tests/test_github_hourly_conflict_repair.py
110115
- tests/test_governance_risk_compliance_hourly_review_caller.py
@@ -133,6 +138,7 @@ on:
133138
- docs/doctoring/conflict-control-evidence-isolation.md
134139
- docs/doctoring/disksage-hourly-review-caller.md
135140
- docs/doctoring/inkspan-hourly-review-caller.md
141+
- docs/doctoring/lineageweave-hourly-review-caller.md
136142
- docs/doctoring/fast-mlsirm-hourly-review-caller.md
137143
- docs/doctoring/github-hourly-conflict-repair.md
138144
- docs/doctoring/governance-risk-compliance-hourly-review-caller.md
@@ -204,6 +210,7 @@ jobs:
204210
tests/test_bandscope_hourly_review_caller.py \
205211
tests/test_disksage_hourly_review_caller.py \
206212
tests/test_inkspan_hourly_review_caller.py \
213+
tests/test_lineageweave_hourly_review_caller.py \
207214
tests/test_fast_mlsirm_hourly_review_caller.py \
208215
tests/test_github_hourly_conflict_repair.py \
209216
tests/test_governance_risk_compliance_hourly_review_caller.py \
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: LineageWeave Hourly Review Repair
2+
3+
on:
4+
schedule:
5+
- cron: "4 * * * *"
6+
7+
concurrency:
8+
group: lineageweave-hourly-review-repair
9+
cancel-in-progress: false
10+
11+
permissions:
12+
contents: read
13+
14+
jobs:
15+
dispatch-review-repair:
16+
uses: ./.github/workflows/pr-review-fix-scheduler.yml
17+
permissions:
18+
contents: read
19+
id-token: write
20+
with:
21+
target_repository: ContextualWisdomLab/LineageWeave
22+
base_branch: "*"
23+
max_prs: "50"
24+
max_dispatches: "1"
25+
retry_hours: "2"
26+
secrets:
27+
PR_REVIEW_MERGE_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN }}
28+
OPENCODE_APPROVE_TOKEN: ${{ secrets.OPENCODE_APPROVE_TOKEN }}

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ this file. The format follows Keep a Changelog, and versioned releases follow
55
Semantic Versioning where the repository publishes a release.
66

77
## [Unreleased]
8+
- Add a bounded hourly LineageWeave stacked-PR review-repair caller while
9+
preserving the existing review-agent, model-routing, and protected-merge
10+
boundaries. Product-gap development remains a separately gated coordinator
11+
capability and is not claimed by this caller. The shared repair scheduler
12+
now treats an explicit `*` base scope as all branch bases so stacked pull
13+
requests are inspected instead of silently filtered out.
814
- Ensure the central Security Scan and SAST Semgrep pull-request workflows
915
trigger for stacked PRs targeting feature branches, preserving the same
1016
diff-scoped dependency and repository-wide filesystem security coverage.
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# LineageWeave hourly review-repair caller
2+
3+
## Decision
4+
5+
The organization control plane invokes its existing review-repair scheduler for
6+
`ContextualWisdomLab/LineageWeave` at minute 4 of every hour. The caller scans
7+
all pull-request bases because LineageWeave uses stacked pull requests, but it
8+
dispatches at most one repair and waits two hours before retrying an unchanged
9+
head. A new heartbeat never cancels an in-flight diagnosis.
10+
11+
The caller is deliberately thin. It does not approve, merge, release, or change
12+
a review-agent identity. It does not create product work. The reusable
13+
scheduler refetches the current head, reviews, threads, checks, and writer lease
14+
before dispatching the existing OpenCode repair worker. Independent current-head approval
15+
and all protected checks remain mandatory.
16+
17+
## Credential and model boundary
18+
19+
The workflow token is read-only. The caller maps only the established
20+
`PR_REVIEW_MERGE_TOKEN` and `OPENCODE_APPROVE_TOKEN` inputs and permits the
21+
reusable scheduler's existing OIDC fallback; it never inherits all secrets.
22+
It does not receive a model credential or provider endpoint and does not use
23+
`COPILOT_GITHUB_TOKEN`.
24+
25+
Model execution remains inside the reviewed central worker and crosses its
26+
`contextual-orchestrator` boundary. This caller neither selects a provider nor
27+
changes contextual-orchestrator discovery, routing, or review-agent secrets.
28+
29+
## Product-gap continuation boundary
30+
31+
This caller closes the missing hourly stacked-PR repair path only. Product-gap
32+
development after the PR queue empties remains owned by the organization
33+
commercial-readiness coordinator and an explicitly opted-in, bounded
34+
repository development entrypoint. LineageWeave does not currently publish
35+
that entrypoint, and recent coordinator runs failed before inventory because
36+
their maintainer credential was unavailable. Therefore this workflow is not
37+
evidence that autonomous product-gap development is operational.
38+
39+
## Verification and rollback
40+
41+
Contract tests bind the cadence, stack scope, dispatch budget, retry floor,
42+
permissions, explicit secret mapping, absent model credentials, reusable
43+
scheduler path, and focused quality-workflow coverage. A scheduled run proves
44+
only accepted dispatch after live state validation; it does not prove a repair,
45+
approval, merge, release, or product increment.
46+
47+
Rollback is removal of this caller after confirming another enabled workflow
48+
owns the same LineageWeave writer lease. Do not run duplicate scheduled writers.
49+
50+
## APA 7th references
51+
52+
GitHub. (n.d.). *Control the concurrency of workflows and jobs*. Retrieved
53+
August 28, 2026, from
54+
https://docs.github.com/en/actions/how-tos/write-workflows/choose-when-workflows-run/control-workflow-concurrency
55+
56+
GitHub. (n.d.). *Events that trigger workflows: Schedule*. Retrieved August
57+
28, 2026, from
58+
https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows#schedule
59+
60+
GitHub. (n.d.). *Reuse workflows*. Retrieved August 28, 2026, from
61+
https://docs.github.com/en/actions/how-tos/sharing-automations/reusing-workflows
62+
63+
National Institute of Standards and Technology. (2022). *Secure software
64+
development framework (SSDF) version 1.1: Recommendations for mitigating the
65+
risk of software vulnerabilities* (NIST Special Publication 800-218).
66+
https://doi.org/10.6028/NIST.SP.800-218

scripts/ci/pr_review_fix_scheduler.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,11 @@ def dispatch_autofix(
282282
run(args, stdin=json.dumps(payload))
283283

284284

285+
def _base_branch_matches(pr: dict[str, Any], expected: str) -> bool:
286+
"""Return whether a PR belongs to the configured base scope."""
287+
return expected == "*" or pr.get("baseRefName") == expected
288+
289+
285290
def inspect_pr(
286291
repo: str,
287292
pr: dict[str, Any],
@@ -293,7 +298,7 @@ def inspect_pr(
293298
number = int(pr["number"])
294299
if pr.get("isDraft"):
295300
return "skip", ("draft PR",)
296-
if pr.get("baseRefName") != args.base_branch:
301+
if not _base_branch_matches(pr, args.base_branch):
297302
return "skip", (
298303
f"base branch is {pr.get('baseRefName')}; expected {args.base_branch}",
299304
)
@@ -363,7 +368,7 @@ def process_queue(args: argparse.Namespace) -> int:
363368
for pr in prs:
364369
if pr.get("isDraft"):
365370
continue
366-
if pr.get("baseRefName") != args.base_branch:
371+
if not _base_branch_matches(pr, args.base_branch):
367372
continue
368373
if not same_repository_head(args.repo, pr):
369374
continue
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
"""Contract tests for LineageWeave's bounded hourly review-repair caller."""
2+
3+
from pathlib import Path
4+
5+
6+
CALLER = Path(".github/workflows/lineageweave-hourly-review-repair.yml")
7+
DOCTORING = Path("docs/doctoring/lineageweave-hourly-review-caller.md")
8+
QUALITY_WORKFLOW = Path(".github/workflows/hourly-nvidia-nim-review-repair.yml")
9+
SCHEDULER = Path(".github/workflows/pr-review-fix-scheduler.yml")
10+
11+
12+
def _read(path: Path) -> str:
13+
"""Return one repository contract file as UTF-8 text."""
14+
return path.read_text(encoding="utf-8")
15+
16+
17+
def test_lineageweave_caller_is_hourly_bounded_and_stack_aware() -> None:
18+
"""The stacked repository receives one non-cancelling repair opportunity."""
19+
caller = _read(CALLER)
20+
21+
for contract in (
22+
'cron: "4 * * * *"',
23+
"group: lineageweave-hourly-review-repair",
24+
"cancel-in-progress: false",
25+
"uses: ./.github/workflows/pr-review-fix-scheduler.yml",
26+
"target_repository: ContextualWisdomLab/LineageWeave",
27+
'base_branch: "*"',
28+
'max_prs: "50"',
29+
'max_dispatches: "1"',
30+
'retry_hours: "2"',
31+
):
32+
assert contract in caller
33+
34+
35+
def test_lineageweave_caller_preserves_the_existing_credential_boundary() -> None:
36+
"""The caller maps only scheduler credentials and exposes no model secret."""
37+
caller = _read(CALLER)
38+
workflow_scope, jobs_scope = caller.split("\njobs:\n", maxsplit=1)
39+
40+
assert "\npermissions:\n contents: read\n" in workflow_scope
41+
assert "\n permissions:\n contents: read\n id-token: write\n" in jobs_scope
42+
assert "PR_REVIEW_MERGE_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN }}" in caller
43+
assert "OPENCODE_APPROVE_TOKEN: ${{ secrets.OPENCODE_APPROVE_TOKEN }}" in caller
44+
assert "secrets: inherit" not in caller
45+
assert "COPILOT_GITHUB_TOKEN" not in caller
46+
assert "NVIDIA_NIM_API_KEY" not in caller
47+
assert "LLM_GATEWAY_API_URL" not in caller
48+
assert "ContextualWisdomLab/LineageWeave" not in _read(SCHEDULER)
49+
50+
51+
def test_lineageweave_caller_is_covered_by_the_focused_quality_gate() -> None:
52+
"""Caller, evidence, and regression test all trigger the focused gate."""
53+
quality = _read(QUALITY_WORKFLOW)
54+
55+
assert quality.count(str(CALLER)) == 2
56+
assert quality.count(str(DOCTORING)) == 2
57+
assert quality.count("tests/test_lineageweave_hourly_review_caller.py") == 3
58+
59+
60+
def test_lineageweave_doctoring_keeps_product_and_review_claims_separate() -> None:
61+
"""The evidence record states what this caller can and cannot prove."""
62+
doctoring = _read(DOCTORING).lower()
63+
64+
for contract in (
65+
"stacked pull requests",
66+
"independent current-head approval",
67+
"does not create product work",
68+
"contextual-orchestrator",
69+
"copilot_github_token",
70+
"apa 7th references",
71+
):
72+
assert contract in doctoring

tests/test_pr_review_fix_scheduler.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -770,7 +770,7 @@ def test_inspect_pr_dispatches_conflict_resolution(monkeypatch):
770770

771771
def test_process_queue_includes_conflict_resolution_candidates(monkeypatch, capsys):
772772
"""The queue pre-filter fetches comments for approved conflicting PRs too."""
773-
pr = _approved_dirty_pr()
773+
pr = _approved_dirty_pr(baseRefName="feature-base")
774774
monkeypatch.setattr(fix, "fetch_open_prs", lambda repo, max_prs: [pr])
775775
monkeypatch.setattr(fix, "issue_comments", lambda repo, number: [])
776776
monkeypatch.setattr(
@@ -779,7 +779,7 @@ def test_process_queue_includes_conflict_resolution_candidates(monkeypatch, caps
779779
lambda repo, pr, workflow, workflow_repository, dry_run, resolve_conflict=False: None,
780780
)
781781
monkeypatch.setattr(fix, "create_fix_marker", lambda repo, pr, dry_run: None)
782-
assert fix.main(["--repo", "owner/repo", "--base-branch", "main", "--dry-run"]) == 0
782+
assert fix.main(["--repo", "owner/repo", "--base-branch", "*", "--dry-run"]) == 0
783783
payload = json.loads(capsys.readouterr().out.strip().splitlines()[-1])
784784
assert payload["autofix_dispatches"] == 1
785785

@@ -789,6 +789,12 @@ def test_fix_inspect_skip_wait_and_error_paths(monkeypatch):
789789
args = fix.parse_args(["--repo", "owner/repo", "--base-branch", "main"])
790790
assert fix.inspect_pr("owner/repo", make_pr(isDraft=True), args) == ("skip", ("draft PR",))
791791
assert fix.inspect_pr("owner/repo", make_pr(baseRefName="develop"), args)[1][0].startswith("base branch")
792+
wildcard_args = fix.parse_args(["--repo", "owner/repo", "--base-branch", "*"])
793+
monkeypatch.setattr(fix, "needs_autofix", lambda pr: (False, ()))
794+
assert fix.inspect_pr("owner/repo", make_pr(baseRefName="develop"), wildcard_args) == (
795+
"skip",
796+
("no current-head autofixable review, failed-check RCA, or approved merge conflict",),
797+
)
792798
assert fix.inspect_pr("owner/repo", make_pr(headRepository={"nameWithOwner": "fork/repo"}), args)[1] == (
793799
"external PR head is not writable by repository workflow credentials",
794800
)

0 commit comments

Comments
 (0)