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
28 changes: 14 additions & 14 deletions .github/workflows/opencode-review.yml

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
__pycache__/
*.py[cod]
.coverage
.pytest_cache/
3 changes: 3 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## 2024-06-21 - Python JSON Decoding Optimization
**Learning:** In Python, string slicing `text[index:]` inside a loop can cause O(N^2) complexity and severe memory copying overhead. When decoding JSON incrementally from a large text blob, `json.JSONDecoder().raw_decode(text, index)` can parse from a given index without slicing. Combining this with `text.find("{", index)` to skip irrelevant characters is significantly faster than `enumerate(text)`.
**Action:** Always prefer `raw_decode(text, index)` and `string.find()` over string slicing and character-by-character iteration when scanning large files for JSON objects.
## 2024-06-23 - `iter_json_objects` 최적화
**Learning:** Python의 `json.JSONDecoder().raw_decode()`를 사용할 때 문자열을 하나씩 순회하며 슬라이싱(`text[index:]`)을 수행하면, O(N^2)의 메모리 할당 및 복사 작업이 발생하여 매우 큰 병목(Bottleneck)이 될 수 있습니다.
**Action:** `str.find("{", index)`를 사용하여 JSON 객체의 시작 위치를 빠르게 건너뛰고, `raw_decode(text, index)`에서 제공하는 `idx` 인자를 활용해 슬라이싱 없이 직접 파싱을 수행하여 최적화합니다.
23 changes: 23 additions & 0 deletions PR_GOVERNANCE_AUDIT.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ OpenCode decides; GitHub Actions mutates.
- OpenCode app-token merges are deprecated; keep app tokens for review publication, not mechanical branch mutation.
- OpenCode approval publication must be bounded. Peer GitHub Checks can be awaited, but the approval step itself must time out instead of running for hours; the current central limit is a 45 minute approval step with 81 peer-check probes at 30 seconds.
- Tool failures are not source findings. Model failure, API transient, update-branch `422/403`, fork/write-permission failure, conflict, failed checks, and stale review state must be reported as distinct scheduler outcomes.
- Developer experience and user experience are separate review surfaces. Reviews must adopt helpful sibling-repo automation, review, setup, documentation, and product-flow patterns when they reduce friction, and flag noisy automation, false failures, misleading status, repeated waiting, or URL-only diagnostics as experience defects instead of treating them as neutral implementation detail.

## Live Repository Inventory

Expand Down Expand Up @@ -59,6 +60,27 @@ Live generated: 2026-06-23 04:18 KST. PR #28 post-merge refresh: 2026-06-23 16:0
| `codec-carver` | Recent PR #94 was merged by `app/opencode-agent`, and the repo still has legacy `Scheduled PR Review Merge`. | Native auto-merge path for current-head approved PRs. | OpenCode app as merge actor. |
| `VibeSec` | PR #108 had native auto-merge enabled; #106 merged by `app/github-actions`; #109 merged by human. | Keep native auto-merge as preferred waiting path. | Repo-by-repo actor inconsistency. |

## DX/UX Transfer Decisions

Developer experience means the maintainer, reviewer, CI operator, and future
contributor experience. User experience means the product user, documentation
reader, PR reader, and status-check reader experience. PR review must evaluate
both separately; a change can improve one while harming the other.

| Repo | Borrow because it helps DX/UX | Improve because it creates friction | Central action |
|---|---|---|---|
| `.github` | Same-head manual evidence and `--match-head-commit` make self-modifying workflow changes reviewable without pretending stale base-branch checks are current. | Stale `pull_request_target` failures, long polling review runs, and cancelled helper checks can become misleading review noise. | Serialize Strix before OpenCode, bound approval runtime, and require failed-check explanations instead of URL-only comments. |
| `naruon` | Strict required checks, stale review dismissal, and changed-file Mermaid flow DAGs make review evidence easier to audit. | `BEHIND` or outdated approvals can look merge-ready unless the head SHA is treated as the review boundary. | Re-review every updated head and require an exact changed-file evidence path plus a Change Flow DAG before approval. |
| `pg-erd-cloud` | GitHub Actions bot merges with head guards give a clear mechanical actor for merges. | Repo-local autofix workflows are useful there, but centralizing autofix would widen mutation scope too far. | Keep GitHub Actions as the merge actor and leave autofix workflows repo-local. |
| `VibeSec` | Native auto-merge examples show a lower-friction waiting path after current-head approval. | Mixed human, OpenCode, and GitHub Actions merge actors make audit trails harder to interpret. | Prefer native auto-merge or GitHub Actions mutation; do not let OpenCode merge directly. |
| `bandscope` | Broad required checks encode repo-specific release, build, SBOM, and security expectations. | A central script would be noisy if it tried to reinterpret every required check itself. | Let GitHub native auto-merge and rulesets interpret required checks. |
| `newsdom-api` | Required quality gates and security checks give API changes stronger release evidence. | Central review comments that only point at failing check URLs do not help an API maintainer fix the failure. | Require failed-check root cause, source location when available, fix direction, and rerun command. |
| `scopeweave` | Strix self-test and the central scheduler are useful rollout fixtures. | Claiming the rollout complete without a representative current-head trace would be premature. | Keep it on the central path and require a live trace before declaring update/merge behavior proven. |
| `clearfolio` | Direct guarded merge works while auto-merge is intentionally off. | Treating it like an auto-merge repo would create confusing expectations. | Use immediate guarded merge only after same-head evidence, unresolved-thread check, and head guard pass. |
| `codec-carver` | Existing native merge behavior can be retained once current-head evidence is clean. | Legacy OpenCode app-token merge creates a second mechanical actor and weakens audit consistency. | Replace legacy scheduled merge with the central GitHub Actions scheduler. |
| `ContextualWisdomLab.github.io` | Site and documentation changes make reader-facing UX review concrete. | Review comments that say only that a check failed do not help the site reader or maintainer understand the issue. | Treat documentation clarity, homepage behavior, and status-check explanations as UX surfaces. |
| `contextual-orchestrator` | No central pattern is present yet, so it can be onboarded deliberately instead of accidentally. | Silent unmanaged status is easy to miss in organization-level governance. | Either opt it into the central workflows or explicitly mark it unmanaged. |

## Current Scheduler Contract

The checked-in scheduler already does the minimal central path:
Expand Down Expand Up @@ -131,5 +153,6 @@ PR #36: block: merge conflict: DIRTY
- `clearfolio` PR #13 and `codec-carver` PR #98 were opened as thin rollouts. `clearfolio` PR #13 is now merged at `4bc17c6`; `codec-carver` PR #98 remains the thin rollout that deletes the legacy OpenCode app-token merge workflow.
- `clearfolio` PR #13 first failed Strix run `28027843973` because `opencode.jsonc` was missing. Later current-head proof used manual Strix run `28051319530` and manual OpenCode run `28051665082`; the final approval named the changed review-tooling files and head `5fe1791d48ddcf03dbc365cc6fa407e7cbe70a89` before guarded merge.
- `.github` PR #42 exposed that central approval normalization should not accept generic path-looking evidence when exact current-head changed files are available. The OpenCode workflow now writes `git diff --name-only --find-renames "$PR_MERGE_BASE" "$PR_HEAD_SHA"` to `OPENCODE_CHANGED_FILES_FILE`, gives the isolated review workspace `changed-files.txt`, and the normalizer rejects `APPROVE` unless the approval names one of those exact files.
- `.github` PR #42 same-head OpenCode run `28070438305` exposed a second decode gap: model output reading tolerated invalid UTF-8, but approval-summary repair still read `OPENCODE_APPROVAL_REPAIR_EVIDENCE_FILE` as strict UTF-8. DeepSeek produced a repairable control block, then normalization failed on byte `0xea` in bounded evidence. Evidence repair now reads lossy UTF-8 so a damaged transcript byte cannot prevent source-backed normalization.
- `codec-carver` PR #98 already has base `opencode.jsonc`. PR #98 now pins the central scheduler instead of downloading from `main`; same-head Strix run `28030439830` and OpenCode runs `28030438605`/`28030439065` were still in progress at the 2026-06-23 22:48 KST snapshot.
- `.github` PR #38 exposed two central gaps after PR #37 merged: the `review_dispatch` reason lost the `same-head Strix and OpenCode dispatched` contract string, and `failed_status_checks()` treated failed PR-target Strix check runs as blockers even when a later manual `strix` status could supersede them. Commit `7be2d99` restores the reason string, materializes PR-head scheduler policy as non-executed data for Strix self-test, and ignores stale Strix check-run failures when the same head has a successful `strix` status context. Manual Strix run `28030448032` had passed self-test and was still running `Run Strix (quick)` at the 2026-06-23 22:48 KST snapshot.
16 changes: 13 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ The public GitHub organization profile lives in [profile/README.md](profile/READ
Homepage: https://contextualwisdomlab.github.io/

PR governance live audit: [PR_GOVERNANCE_AUDIT.md](PR_GOVERNANCE_AUDIT.md).
The audit includes repository-by-repository DX/UX transfer decisions: what the
central workflow borrows because it reduces friction, and what it rejects
because it adds noise or misleading review experience.

## PR review and merge policy

Expand All @@ -27,9 +30,12 @@ privileged `pull_request_target` OpenCode workflow.
OpenCode approval is evidence-gated. Before approval, the review summary must
name changed files, CodeGraph or structural MCP evidence, a Change Flow DAG,
100% test coverage evidence, 100% docstring coverage evidence, and a concrete
PoC/execution result. The PoC can be a temporary scratch repro, focused test,
lint, security check, performance probe, or UI verification command, but it must
be actually run and cited. Scratch PoC files are not committed.
PoC/execution result. It must also split `Developer experience:` from
`User experience:` so maintainability/review/CI friction is not confused with
product, documentation, review-comment, or status-check reader outcomes. The PoC
can be a temporary scratch repro, focused test, lint, security check,
performance probe, or UI verification command, but it must be actually run and
cited. Scratch PoC files are not committed.

Failed GitHub Checks are not reviewed as URL lists. OpenCode must explain the
failed check name, failing step, source-backed file and line when available,
Expand All @@ -51,3 +57,7 @@ Operational cases folded into the central policy:
- `naruon#745`: new OpenCode review-flow work improves Mermaid output by
replacing generic risk sketches with changed-file flow DAGs. The central
workflow carries that review contract while keeping the self-test drift fix.
- Cross-repo DX/UX: helpful sibling-repo patterns should be adopted when they
reduce maintainer, reviewer, CI-operator, contributor, user, or reader
friction. Noisy automation, repeated waiting, false failures, misleading
statuses, and URL-only diagnostics are treated as review-experience defects.
40 changes: 29 additions & 11 deletions scripts/ci/opencode_review_normalize_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@
"compatibility/convention:",
"breaking-change/backcompat:",
"performance:",
"design/ux:",
"developer experience:",
"user experience:",
"security/privacy:",
)

Expand Down Expand Up @@ -223,6 +224,14 @@ def approval_repair_evidence_file() -> Path | None:
return None


def read_text_lossy(path: Path) -> str | None:
"""Read text while preserving progress across invalid UTF-8 bytes."""
try:
return path.read_text(encoding="utf-8", errors="replace")
except OSError:
return None


def section_between_markers(text: str, marker: str) -> str:
"""Return a markdown section body from a bounded evidence file."""
marker_line = f"## {marker}"
Expand Down Expand Up @@ -296,7 +305,8 @@ def build_approval_repair_summary(summary: str, evidence_text: str) -> str | Non
Compatibility/convention: changed workflow/script conventions and compatibility surfaces were checked in bounded evidence.
Breaking-change/backcompat: deployment evidence and changed-file history were checked for backward-compatibility risk.
Performance: changed surfaces were checked for performance risk in bounded evidence.
Design/UX: changed files did not identify a UI-facing design surface; bounded evidence was reviewed.
Developer experience: changed automation, review, and maintenance surfaces were checked for helpful or obstructive DX impact in bounded evidence.
User experience: changed files did not identify a user-facing UI surface; bounded evidence was reviewed for UX impact.
Security/privacy: workflow-token, review-gate, and repository-automation security/privacy boundaries were checked in bounded evidence.
"""
return f"{summary.rstrip()}\n{repair}"
Expand All @@ -312,9 +322,8 @@ def repair_approval_summary(reason: str, summary: str) -> str:
evidence_file = approval_repair_evidence_file()
if evidence_file is None:
return summary
try:
evidence_text = evidence_file.read_text(encoding="utf-8")
except OSError:
evidence_text = read_text_lossy(evidence_file)
if evidence_text is None:
return summary

repaired_summary = build_approval_repair_summary(summary, evidence_text)
Expand Down Expand Up @@ -452,14 +461,23 @@ def iter_json_objects(text: str) -> list[Any]:
# OpenCode exports may contain prose around the JSON control object.
pass

for index, character in enumerate(text):
if character != "{":
index = 0
while True:
index = text.find("{", index)
if index == -1:
break
next_index = index + 1
while next_index < len(text) and text[next_index] in " \t\r\n":
next_index += 1
if next_index < len(text) and text[next_index] not in {'"', "}"}:
index += 1
continue
try:
value, _ = decoder.raw_decode(text[index:])
value, _ = decoder.raw_decode(text, index)
values.append(value)
except json.JSONDecodeError:
continue
values.append(value)
pass
index += 1

return values

Expand All @@ -481,7 +499,7 @@ def main(argv: list[str]) -> int:
expected_head_sha, expected_run_id, expected_run_attempt, output_file_arg = argv[1:]
output_file = Path(output_file_arg)
try:
output_text = output_file.read_text(encoding="utf-8")
output_text = output_file.read_text(encoding="utf-8", errors="replace")
except OSError as exc:
print(f"cannot read OpenCode output file: {exc}", file=sys.stderr)
return 65
Expand Down
Loading