From 57207a71c0ee021c5922555e790ef8e1a0195f21 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 26 Aug 2026 02:40:47 +0900 Subject: [PATCH 1/4] ci: add semantic-data-portal hourly review-repair caller Adds the protected hourly caller at minute 31 (free slot across all other product callers), delegating to the central pr-review-fix-scheduler with the standard one-dispatch budget and two-hour same-head retry floor. Ships the doctoring record (RCA/feasibility, cadence, credential boundary, authority boundaries incl. Keyverse/GRC/naruon/LineageWeave) and caller contract tests mirroring the disksage pattern. Closes ContextualWisdomLab/semantic-data-portal#77's hourly-trigger requirement without touching COPILOT_GITHUB_TOKEN or review-agent key contracts. --- ...antic-data-portal-hourly-review-repair.yml | 35 +++++ ...mantic-data-portal-hourly-review-caller.md | 131 ++++++++++++++++++ ...mantic_data_portal_hourly_review_caller.py | 85 ++++++++++++ 3 files changed, 251 insertions(+) create mode 100644 .github/workflows/semantic-data-portal-hourly-review-repair.yml create mode 100644 docs/doctoring/semantic-data-portal-hourly-review-caller.md create mode 100644 tests/test_semantic_data_portal_hourly_review_caller.py diff --git a/.github/workflows/semantic-data-portal-hourly-review-repair.yml b/.github/workflows/semantic-data-portal-hourly-review-repair.yml new file mode 100644 index 000000000..e3b4937a9 --- /dev/null +++ b/.github/workflows/semantic-data-portal-hourly-review-repair.yml @@ -0,0 +1,35 @@ +name: Semantic Data Portal Hourly Review Repair + +on: + schedule: + # Minute 31 avoids the taken caller minutes (2, 10, 14, 16, 21, 23, 27, + # 37, 43, 49, 53, 58) and the minute-zero runner surge. + - cron: "31 * * * *" + +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 }} diff --git a/docs/doctoring/semantic-data-portal-hourly-review-caller.md b/docs/doctoring/semantic-data-portal-hourly-review-caller.md new file mode 100644 index 000000000..7e0604e01 --- /dev/null +++ b/docs/doctoring/semantic-data-portal-hourly-review-caller.md @@ -0,0 +1,131 @@ +# Semantic Data Portal hourly review-repair caller + +## Decision + +ContextualWisdomLab operates one protected hourly caller for +`ContextualWisdomLab/semantic-data-portal`. The caller runs at minute 31, +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 avoids every other caller's cron slot (2, 10, 14, 16, 21, 23, 27, +37, 43, 49, 53, 58) and 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 26, 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 26, +2026, from +https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows#schedule + +GitHub. (n.d.). *Reuse workflows*. Retrieved August 26, 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 26, 2026, from +https://docs.nvidia.com/nim/large-language-models/latest/ + +OpenCode. (n.d.). *OpenCode documentation*. Retrieved August 26, 2026, from +https://opencode.ai/docs/ + +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 diff --git a/tests/test_semantic_data_portal_hourly_review_caller.py b/tests/test_semantic_data_portal_hourly_review_caller.py new file mode 100644 index 000000000..9ef0f09f8 --- /dev/null +++ b/tests/test_semantic_data_portal_hourly_review_caller.py @@ -0,0 +1,85 @@ +"""Contract tests for the semantic-data-portal bounded hourly review-repair caller.""" + +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 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: "31 * * * *"' 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 "\npermissions:\n contents: read\n" in workflow_scope + assert "\n permissions:\n contents: read\n id-token: write\n" in jobs_scope + 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 31 does not collide with any other product caller heartbeat.""" + taken = {str(m) for m in (2, 10, 14, 16, 21, 23, 27, 37, 43, 49, 53, 58)} + assert "31" not in taken + caller = _read(CALLER) + assert '- cron: "31 * * * *"' in caller + + +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 31", + ): + 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 From 4189e6a8edeb8f55715d4dbf5732afe7f1a305a1 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 26 Aug 2026 05:06:33 +0900 Subject: [PATCH 2/4] fix: tighten semantic portal caller contracts --- ...mantic-data-portal-hourly-review-caller.md | 10 ++--- ...mantic_data_portal_hourly_review_caller.py | 37 +++++++++++++++++-- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/docs/doctoring/semantic-data-portal-hourly-review-caller.md b/docs/doctoring/semantic-data-portal-hourly-review-caller.md index 7e0604e01..5d629b25c 100644 --- a/docs/doctoring/semantic-data-portal-hourly-review-caller.md +++ b/docs/doctoring/semantic-data-portal-hourly-review-caller.md @@ -108,21 +108,21 @@ the central RCA, feasibility, lease, and credential contracts. ## APA 7th references GitHub. (n.d.). *Control the concurrency of workflows and jobs*. Retrieved -August 26, 2026, from +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 26, +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 26, 2026, from +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 26, 2026, from +August 25, 2026, from https://docs.nvidia.com/nim/large-language-models/latest/ -OpenCode. (n.d.). *OpenCode documentation*. Retrieved August 26, 2026, from +OpenCode. (n.d.). *OpenCode documentation*. Retrieved August 25, 2026, from https://opencode.ai/docs/ Souppaya, M., Scarfone, K., & Dodson, D. (2022). *Secure software development diff --git a/tests/test_semantic_data_portal_hourly_review_caller.py b/tests/test_semantic_data_portal_hourly_review_caller.py index 9ef0f09f8..b7e695093 100644 --- a/tests/test_semantic_data_portal_hourly_review_caller.py +++ b/tests/test_semantic_data_portal_hourly_review_caller.py @@ -1,5 +1,6 @@ """Contract tests for the semantic-data-portal bounded hourly review-repair caller.""" +import re from pathlib import Path @@ -12,6 +13,26 @@ def _read(path: Path) -> str: 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) @@ -32,8 +53,11 @@ def test_semantic_data_portal_caller_preserves_credentials_and_read_only_token_s caller = _read(CALLER) workflow_scope, jobs_scope = caller.split("\njobs:\n", maxsplit=1) - assert "\npermissions:\n contents: read\n" in workflow_scope - assert "\n permissions:\n contents: read\n id-token: write\n" in jobs_scope + 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 @@ -51,10 +75,15 @@ def test_semantic_data_portal_caller_preserves_credentials_and_read_only_token_s def test_semantic_data_portal_caller_cron_avoids_other_callers() -> None: """Minute 31 does not collide with any other product caller heartbeat.""" - taken = {str(m) for m in (2, 10, 14, 16, 21, 23, 27, 37, 43, 49, 53, 58)} - assert "31" not in taken caller = _read(CALLER) assert '- cron: "31 * * * *"' 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 "31" not in other_minutes def test_semantic_data_portal_caller_doctoring_records_rca_feasibility_and_latency() -> None: From cd3e1c2d11a5e04cacf07f03d2fecd56812df43e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 26 Aug 2026 06:29:33 +0900 Subject: [PATCH 3/4] fix: move semantic portal hourly caller to reserved slot --- .../semantic-data-portal-hourly-review-repair.yml | 7 ++++--- .../semantic-data-portal-hourly-review-caller.md | 7 ++++--- .../test_semantic_data_portal_hourly_review_caller.py | 10 +++++----- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/.github/workflows/semantic-data-portal-hourly-review-repair.yml b/.github/workflows/semantic-data-portal-hourly-review-repair.yml index e3b4937a9..60aca69be 100644 --- a/.github/workflows/semantic-data-portal-hourly-review-repair.yml +++ b/.github/workflows/semantic-data-portal-hourly-review-repair.yml @@ -2,9 +2,10 @@ name: Semantic Data Portal Hourly Review Repair on: schedule: - # Minute 31 avoids the taken caller minutes (2, 10, 14, 16, 21, 23, 27, - # 37, 43, 49, 53, 58) and the minute-zero runner surge. - - cron: "31 * * * *" + # Minute 59 is reserved for semantic-data-portal in the organization + # caller ledger; it avoids every other product heartbeat and the + # minute-zero runner surge. + - cron: "59 * * * *" concurrency: group: semantic-data-portal-hourly-review-repair diff --git a/docs/doctoring/semantic-data-portal-hourly-review-caller.md b/docs/doctoring/semantic-data-portal-hourly-review-caller.md index 5d629b25c..53e0c0728 100644 --- a/docs/doctoring/semantic-data-portal-hourly-review-caller.md +++ b/docs/doctoring/semantic-data-portal-hourly-review-caller.md @@ -3,11 +3,12 @@ ## Decision ContextualWisdomLab operates one protected hourly caller for -`ContextualWisdomLab/semantic-data-portal`. The caller runs at minute 31, +`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 avoids every other caller's cron slot (2, 10, 14, 16, 21, 23, 27, -37, 43, 49, 53, 58) and the minute-zero runner surge. +The minute is reserved for semantic-data-portal in the organization caller +ledger and avoids every other caller's cron slot and 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 diff --git a/tests/test_semantic_data_portal_hourly_review_caller.py b/tests/test_semantic_data_portal_hourly_review_caller.py index b7e695093..18cef1cdd 100644 --- a/tests/test_semantic_data_portal_hourly_review_caller.py +++ b/tests/test_semantic_data_portal_hourly_review_caller.py @@ -37,7 +37,7 @@ def test_semantic_data_portal_caller_is_hourly_bounded_and_non_cancelling() -> N """The portal receives one realistic repair opportunity without overlap cancellation.""" caller = _read(CALLER) - assert 'cron: "31 * * * *"' in 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 @@ -74,16 +74,16 @@ def test_semantic_data_portal_caller_preserves_credentials_and_read_only_token_s def test_semantic_data_portal_caller_cron_avoids_other_callers() -> None: - """Minute 31 does not collide with any other product caller heartbeat.""" + """Minute 59 does not collide with any other product caller heartbeat.""" caller = _read(CALLER) - assert '- cron: "31 * * * *"' in 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 "31" not in other_minutes + assert "59" not in other_minutes def test_semantic_data_portal_caller_doctoring_records_rca_feasibility_and_latency() -> None: @@ -101,7 +101,7 @@ def test_semantic_data_portal_caller_doctoring_records_rca_feasibility_and_laten "PR_REVIEW_MERGE_TOKEN", "OPENCODE_APPROVE_TOKEN", "ContextualWisdomLab/semantic-data-portal", - "minute 31", + "minute 59", ): assert phrase in doctoring, phrase From 419ebef1ada7b2c7d19b693d6f7813812bead154 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 26 Aug 2026 07:54:19 +0900 Subject: [PATCH 4/4] docs(semantic-portal): clarify hourly heartbeat timing --- .../workflows/semantic-data-portal-hourly-review-repair.yml | 5 +++-- docs/doctoring/semantic-data-portal-hourly-review-caller.md | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/semantic-data-portal-hourly-review-repair.yml b/.github/workflows/semantic-data-portal-hourly-review-repair.yml index 60aca69be..c77979382 100644 --- a/.github/workflows/semantic-data-portal-hourly-review-repair.yml +++ b/.github/workflows/semantic-data-portal-hourly-review-repair.yml @@ -3,8 +3,9 @@ name: Semantic Data Portal Hourly Review Repair on: schedule: # Minute 59 is reserved for semantic-data-portal in the organization - # caller ledger; it avoids every other product heartbeat and the - # minute-zero runner surge. + # 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 * * * *" concurrency: diff --git a/docs/doctoring/semantic-data-portal-hourly-review-caller.md b/docs/doctoring/semantic-data-portal-hourly-review-caller.md index 53e0c0728..35b825f23 100644 --- a/docs/doctoring/semantic-data-portal-hourly-review-caller.md +++ b/docs/doctoring/semantic-data-portal-hourly-review-caller.md @@ -7,8 +7,9 @@ ContextualWisdomLab operates one protected hourly caller for 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 avoids every other caller's cron slot and the minute-zero runner -surge. +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