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
37 changes: 37 additions & 0 deletions .github/workflows/semantic-data-portal-hourly-review-repair.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Semantic Data Portal Hourly Review Repair

on:
schedule:
# Minute 59 is reserved for semantic-data-portal in the organization
# caller ledger and is unique among product heartbeats. GitHub may delay
# scheduled runs, so this is a heartbeat rather than a minute-zero surge
# avoidance guarantee.
- cron: "59 * * * *"
Comment thread
seonghobae marked this conversation as resolved.
Comment thread
seonghobae marked this conversation as resolved.

concurrency:
group: semantic-data-portal-hourly-review-repair
# The queue scan is bounded and the worker has its own exact-head lease. Do not
# discard an in-flight RCA merely because the next hourly heartbeat arrives.
cancel-in-progress: false

permissions:
contents: read

jobs:
dispatch-review-repair:
permissions:
contents: read
id-token: write
uses: ./.github/workflows/pr-review-fix-scheduler.yml
with:
target_repository: ContextualWisdomLab/semantic-data-portal
base_branch: main
max_prs: "50"
max_dispatches: "1"
# Central OpenCode/NVIDIA NIM work can legitimately approach two hours.
# A two-hour same-head floor avoids duplicate writers without freezing the
# next eligible PR or confusing provider latency with a source-code defect.
retry_hours: "2"
secrets:
PR_REVIEW_MERGE_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN }}
OPENCODE_APPROVE_TOKEN: ${{ secrets.OPENCODE_APPROVE_TOKEN }}
133 changes: 133 additions & 0 deletions docs/doctoring/semantic-data-portal-hourly-review-caller.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# Semantic Data Portal hourly review-repair caller

## Decision

ContextualWisdomLab operates one protected hourly caller for
`ContextualWisdomLab/semantic-data-portal`. The caller runs at minute 59,
delegates to the product-neutral central review-fix scheduler, inspects at most
50 open pull requests, and dispatches at most one bounded repair per heartbeat.
The minute is reserved for semantic-data-portal in the organization caller
ledger and is unique among the product caller slots. GitHub may delay scheduled
runs, so the cron is a heartbeat and does not promise avoidance of the
minute-zero runner surge.

The caller does not implement review or mutation logic itself. It keeps the
portal independently operable while centralizing privileged automation in
`ContextualWisdomLab/.github`. The reusable worker performs exact-head
root-cause analysis, tests remediation feasibility, and edits only when one
small reversible action can change the diagnosed cause inside its sealed
writer authority.

## Root-cause analysis and remediation feasibility

The portal queue is dependency-ordered: the shared-base cryptography unlock
merges before the security lock, Keyverse claim aliases, the SQL gate pair,
and then the catalog-plane stack. An hourly redispatch of an unchanged head
does not accelerate that order; it duplicates writer pressure. The worker
therefore enforces these transitions:

1. Refetch the exact live head, base, reviews, checks, changed paths, and
writer state.
2. Establish the causal chain rather than repeat the terminal symptom.
3. Enumerate materially distinct minimal remedies.
4. Reject remedies that lack writer authority, cross sealed paths, require
unavailable credentials or protected-setting changes, violate stack order,
cannot be verified, or do not alter the diagnosed cause.
5. Dispatch at most one feasible repair. Otherwise leave the tree unchanged so
another eligible pull request can be considered by a later heartbeat.

A queued or pending check remains a merge blocker but is not itself a code
finding. The independent non-author approval remains an external authorization
gate and is never synthesized by the repair worker. Product-gap development
that a heartbeat cannot safely automate (for example the Data Management
Evidence Console) stays with the repository's own issue-driven loop; the caller
never fabricates UI evidence to fill it.

## Cadence and concurrency

The caller uses a single concurrency group and `cancel-in-progress: false`.
This preserves an in-flight bounded RCA instead of discarding its evidence when
the next hourly heartbeat arrives. The reusable scheduler cancels only its own
superseded short queue scan; the separately dispatched per-PR repair worker and
this product caller remain non-cancelling. The central scheduler and per-PR
worker also retain exact-head leases and mutation limits.

The caller sets a **two-hour same-head retry floor**. Central OpenCode and
NVIDIA NIM work can legitimately approach two hours, so an hourly redispatch of
the same unchanged head would create duplicate writer pressure rather than
faster remediation. A later hourly scan can still select another eligible pull
request.

GitHub scheduled workflows can be delayed under load and execute only from the
default branch. Consequently, the cron expression is a heartbeat rather than a
real-time service-level promise. Exact-head state, not elapsed wall-clock time,
controls every mutation and merge decision.

## Credential and model boundary

The queue-scanning caller has only `contents: read`. It maps only the
established `PR_REVIEW_MERGE_TOKEN` and `OPENCODE_APPROVE_TOKEN` scheduler
credentials and does not use `secrets: inherit`.

Model execution remains inside the central worker. The model credential is the
GitHub Secret `NVIDIA_NIM_API_KEY`; the caller does not receive or forward it.
`COPILOT_GITHUB_TOKEN` is prohibited. GitHub tokens and GitHub Models are not
model credentials for this write-capable path. The independent review-agent
credential contract is unchanged.

## Security, standalone operation, and modularity

The caller adds no portal runtime dependency, database object, network
endpoint, tenant authority, or product credential. The semantic-data-portal
continues to run as a standalone FastAPI application and as a module beside
naruon, Keyverse, DiskSage, fast-mlsirm, TEPP, LineageWeave, RankWeave, and
contextual-orchestrator without weakening its local validation,
protected-branch, exact-head, approval, or security gates. Authority boundaries
stay fixed: identity belongs to Keyverse, policy truth to GRC, capability
targets to enterprise-architecture-core, document-KG writes to naruon, lineage
reconstruction to LineageWeave, and central governance to this repository.

The reusable workflow source is bound to the called workflow repository, SHA,
ref, and file path before privileged scheduler logic runs. The worker cannot
approve, merge, release, weaken checks, change reviewer identities, or modify
protected settings. Queued, pending, absent, failed, cancelled, skipped-required,
neutral-required, stale-head, or synthetic-merge evidence is not success.

## Verification and rollback

Repository contracts require the exact cron, target repository, one-dispatch
budget, two-hour retry floor, non-cancelling single-flight policy, read-only
workflow token, explicit secret mapping, and absence of both
`NVIDIA_NIM_API_KEY` and `COPILOT_GITHUB_TOKEN` from the caller.

Rollback is a reviewed source change. Do not disable exact-head binding, reduce
the independent approval requirement, increase dispatch volume, use inherited
secrets, or convert provider latency into a fabricated code edit. If the
heartbeat becomes too frequent or too slow, change only the caller cadence and
retry floor after examining observed run duration and queue throughput; preserve
the central RCA, feasibility, lease, and credential contracts.

## APA 7th references

GitHub. (n.d.). *Control the concurrency of workflows and jobs*. Retrieved
August 25, 2026, from
https://docs.github.com/en/actions/how-tos/write-workflows/choose-when-workflows-run/control-workflow-concurrency

GitHub. (n.d.). *Events that trigger workflows: Schedule*. Retrieved August 25,
2026, from
https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows#schedule

GitHub. (n.d.). *Reuse workflows*. Retrieved August 25, 2026, from
https://docs.github.com/en/actions/how-tos/sharing-automations/reusing-workflows

NVIDIA. (n.d.). *NVIDIA NIM for large language models documentation*. Retrieved
August 25, 2026, from
https://docs.nvidia.com/nim/large-language-models/latest/

OpenCode. (n.d.). *OpenCode documentation*. Retrieved August 25, 2026, from
https://opencode.ai/docs/
Comment thread
coderabbitai[bot] marked this conversation as resolved.

Souppaya, M., Scarfone, K., & Dodson, D. (2022). *Secure software development
framework (SSDF) version 1.1: Recommendations for mitigating the risk of
software vulnerabilities* (NIST Special Publication 800-218). National
Institute of Standards and Technology. https://doi.org/10.6028/NIST.SP.800-218
114 changes: 114 additions & 0 deletions tests/test_semantic_data_portal_hourly_review_caller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
"""Contract tests for the semantic-data-portal bounded hourly review-repair caller."""

import re
from pathlib import Path


CALLER = Path(".github/workflows/semantic-data-portal-hourly-review-repair.yml")
DOCTORING = Path("docs/doctoring/semantic-data-portal-hourly-review-caller.md")


def _read(path: Path) -> str:
"""Return one repository contract file as UTF-8 text."""
return path.read_text(encoding="utf-8")


def _permission_map(caller: str, header: str) -> dict[str, str]:
"""Parse one exact YAML permission block without widening test dependencies."""
lines = caller.splitlines()
header_index = lines.index(header)
entry_indent = len(header) - len(header.lstrip()) + 2
permissions: dict[str, str] = {}
for line in lines[header_index + 1 :]:
if not line.strip():
continue
indent = len(line) - len(line.lstrip())
if indent < entry_indent:
break
if indent != entry_indent:
continue
key, separator, value = line.strip().partition(":")
assert separator, f"malformed permission entry: {line!r}"
permissions[key] = value.strip()
return permissions


def test_semantic_data_portal_caller_is_hourly_bounded_and_non_cancelling() -> None:
"""The portal receives one realistic repair opportunity without overlap cancellation."""
caller = _read(CALLER)

assert 'cron: "59 * * * *"' in caller
assert "group: semantic-data-portal-hourly-review-repair" in caller
assert "cancel-in-progress: false" in caller
assert "uses: ./.github/workflows/pr-review-fix-scheduler.yml" in caller
assert "target_repository: ContextualWisdomLab/semantic-data-portal" in caller
assert "base_branch: main" in caller
assert 'max_prs: "50"' in caller
assert 'max_dispatches: "1"' in caller
assert 'retry_hours: "2"' in caller


def test_semantic_data_portal_caller_preserves_credentials_and_read_only_token_scope() -> None:
"""The queue scanner maps established credentials without exposing model secrets."""
caller = _read(CALLER)
workflow_scope, jobs_scope = caller.split("\njobs:\n", maxsplit=1)

assert _permission_map(workflow_scope, "permissions:") == {"contents": "read"}
assert _permission_map(jobs_scope, " permissions:") == {
"contents": "read",
"id-token": "write",
}
assert "PR_REVIEW_MERGE_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN }}" in caller
assert "OPENCODE_APPROVE_TOKEN: ${{ secrets.OPENCODE_APPROVE_TOKEN }}" in caller
assert "secrets: inherit" not in caller
assert "NVIDIA_NIM_API_KEY" not in caller
assert "COPILOT_GITHUB_TOKEN" not in caller
for forbidden in (
"actions: write",
"contents: write",
"issues: write",
"pull-requests: write",
"statuses: write",
):
assert forbidden not in caller


def test_semantic_data_portal_caller_cron_avoids_other_callers() -> None:
"""Minute 59 does not collide with any other product caller heartbeat."""
caller = _read(CALLER)
assert '- cron: "59 * * * *"' in caller
other_minutes = {
minute
for path in Path(".github/workflows").glob("*hourly-review-repair.yml")
if path != CALLER
for minute in re.findall(r'cron:\s*["\'](\d+) \* \* \* \*["\']', _read(path))
}
assert "59" not in other_minutes


def test_semantic_data_portal_caller_doctoring_records_rca_feasibility_and_latency() -> None:
"""Operators retain the exact rationale for the bounded two-hour retry policy."""
doctoring = _read(DOCTORING)

for phrase in (
"root-cause analysis",
"remediation feasibility",
"two-hour same-head retry floor",
"exact-head",
"cancel-in-progress: false",
"NVIDIA_NIM_API_KEY",
"COPILOT_GITHUB_TOKEN",
"PR_REVIEW_MERGE_TOKEN",
"OPENCODE_APPROVE_TOKEN",
"ContextualWisdomLab/semantic-data-portal",
"minute 59",
):
assert phrase in doctoring, phrase

for reference in (
"https://docs.github.com/en/actions/how-tos/write-workflows/choose-when-workflows-run/control-workflow-concurrency",
"https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows#schedule",
"https://docs.github.com/en/actions/how-tos/sharing-automations/reusing-workflows",
"https://doi.org/10.6028/NIST.SP.800-218",
):
assert reference in doctoring, reference
Loading