Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
2009bcb
test(scheduler): close current-main RCA coverage gaps
seonghobae Sep 1, 2026
563a766
test(scheduler): close remaining current-main coverage gaps
seonghobae Sep 1, 2026
8b26dab
fix(agent-mention): stack safe OpenCode slash aliases
seonghobae Sep 1, 2026
cd6a986
fix(agent-mention): exclude a trailing query string from the opencode…
claude Sep 1, 2026
de821df
fix(agent-mention): give each mention alternative its own boundary, f…
claude Sep 1, 2026
f26b3bd
fix(agent-mention): exclude a percent-encoded path or URI scheme sepa…
claude Sep 1, 2026
db106d5
test(ci): close main's post-#1546 scheduler coverage regression
claude Sep 1, 2026
c46445b
fix(agent-mention): make % and : boundary exclusions direction-specific
claude Sep 1, 2026
6920f07
fix(agent-mention): reject colon- and percent-delimited path segments
claude Sep 1, 2026
85c2469
docs(gap-baseline): record post-#1546 scheduler coverage regression
claude Sep 1, 2026
4b42ce9
fix(agent-mention): widen the trailing colon exclusion to cover a sla…
claude Sep 1, 2026
93a782c
chore(stack): move current mention router onto #1567
seonghobae Sep 1, 2026
c9713a8
chore(stack): follow #1567 gap-baseline update
seonghobae Sep 1, 2026
6f40a06
test(ci): document nested REST fixture helpers
seonghobae Sep 1, 2026
0a045f5
chore(stack): follow #1567 docstring repair
seonghobae Sep 1, 2026
10410b9
Merge remote-tracking branch 'origin/main' into fix-opencode-agent-oc…
claude Sep 1, 2026
5534edd
Merge branch 'main' into fix/opencode-agent-oc-alias-stacked-1554
opencode-agent[bot] Sep 1, 2026
e851764
Merge branch 'main' into fix/opencode-agent-oc-alias-stacked-1554
claude Sep 1, 2026
6dbf647
Merge branch 'main' into fix/opencode-agent-oc-alias-stacked-1554
claude Sep 1, 2026
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
2 changes: 2 additions & 0 deletions .github/workflows/agent-mention-router.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ jobs:
&& (
contains(github.event.comment.body, '@cwl-noema-review')
|| contains(github.event.comment.body, '@opencode-agent')
|| contains(github.event.comment.body, '/opencode')
|| contains(github.event.comment.body, '/oc')
Comment thread
seonghobae marked this conversation as resolved.
Comment thread
seonghobae marked this conversation as resolved.
)
concurrency:
group: review-agent-mention-router-local-${{ github.repository }}
Expand Down
4 changes: 2 additions & 2 deletions docs/automation/review-agent-comment-invocation.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# Review-agent comment invocation

Updated: 2026-08-22
Updated: 2026-09-01

## Purpose

Trusted ContextualWisdomLab maintainers can invoke the existing review planes from a pull-request conversation:

- `@cwl-noema-review` requests the independent Noema review.
- `@opencode-agent` requests a bounded current-head OpenCode review only; the invocation itself disables branch updates, automatic merge, and direct merge.
- `@opencode-agent` (or upstream OpenCode's own `/opencode`/`/oc` comment triggers, accepted as aliases of the same request) requests a bounded current-head OpenCode review only; the invocation itself disables branch updates, automatic merge, and direct merge.

The router never checks out or executes pull-request-controlled code. It reads live PR metadata, binds the request to the current head SHA and base branch, and dispatches the already deployed central workflows in `ContextualWisdomLab/.github`.

Expand Down
84 changes: 82 additions & 2 deletions scripts/ci/agent_mention_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,93 @@

CENTRAL_AUTOMATION_REPOSITORY = "ContextualWisdomLab/.github"
TRUSTED_ASSOCIATIONS = frozenset({"OWNER", "MEMBER", "COLLABORATOR"})
# "opencode-agent" also accepts /opencode and /oc: upstream OpenCode's own
# GitHub Action documents those as its trigger phrases
# (https://open-code.ai/en/docs/github), and this repo's dispatch pipeline
# accepts them as aliases of the same @opencode-agent request rather than
# forcing commenters to learn a locally-invented mention instead.
#
# Boundary model (each alternative below carries its own leading and
# trailing lookaround, not a lookahead shared across the alternation, so
# each form's exclusions can differ where the false-positive classes
# differ):
#
# - "@opencode-agent" and the combined "@cwl-noema-review/@opencode-agent"
# separator each exclude a preceding/following Unicode word character
# (\w — this also covers accented and other non-ASCII letters, not just
# ASCII), hyphen, or slash. The leading "/" exclusion rejects URL/path
# embedding (https://youtube.com/@opencode-agent, docs/@opencode-agent);
# the trailing "/" exclusion rejects a root-relative path glued directly
# onto the alias (@opencode-agent/config,
# @cwl-noema-review/@opencode-agent/foo). Ordinary sentence punctuation
# (a trailing "?", ".", "!") is deliberately NOT excluded here: a
# maintainer ending a sentence with "@opencode-agent?" is a legitimate
# request, not a URL continuation — rejecting it (an early version of
# this exclusion did, by mistake, when a query-string fix below was
# applied to every alternative instead of only the one it targeted) is a
# worse failure mode than never seeing the rare literal "@opencode-agent"
# immediately followed by junk with no separating space.
# - The "@cwl-noema-review/@opencode-agent" separator's own left boundary
# is on the combined literal as a whole, not just the trailing slash: a
# boundary check on the slash alone would still fire for invalid pasted
# text where "@cwl-noema-review" is itself embedded in a larger token
# (foo@cwl-noema-review/@opencode-agent,
# docs/@cwl-noema-review/@opencode-agent) without checking that the
# Noema mention has a valid left boundary of its own.
# - The bare "/opencode"/"/oc" forms are the most URL/path-context-prone,
# so both sides exclude the characters that continue a URL/path/filename
# token, but NOT the same set on both sides — each excluded character is
# only ever a continuation indicator from the direction it actually
# appears in a URL. Leading exclusion: a Unicode word character, ".",
# "/", "?", "=", "#", ":", or "-". This rejects a query string
# (?next=/opencode), a URL fragment identifier
# (https://example.com/#/oc), and a URI scheme separator (scheme:/oc,
# app:/opencode) — but NOT a preceding "%", since percent-encoding syntax
# is "%" followed by hex digits, never followed by a literal "/", so a
# leading "%" before "/oc" (100%/oc) is not a URL-encoding pattern and
# was, in an earlier version of this exclusion, wrongly rejected as one.
# Trailing exclusion: a Unicode word character, ".", "/", "?", "=", "#",
# "%", or "-". This rejects a root-relative path (/oc/config), a dotted
# filename continuation (/oc.json), a query string glued on with no
# separator (/oc?mode=docs), a percent-encoded path continuation
# (/oc%2Fconfig), and a Unicode word continuation (/océan) that a plain
# ASCII character class would miss — but NOT a trailing ":", since a
# colon is not itself a path/URL continuation character in this
# direction (unlike the scheme-separator case, which is a *preceding*
# colon), so excluding it on the trailing side too, in an earlier
# version, wrongly rejected ordinary usage like "/oc:" (a colon used as
# a label separator after the command, not as part of a URL).
# Two further exclusions cannot be expressed as a single trailing/leading
# character, because the character that makes them suspicious is not the
# one immediately touching the alias: a colon followed by a further word
# character (/oc:config) is a colon-delimited path segment, not the
# "/oc:" label-separator case just above, where the colon is followed by
# a space or nothing; and a percent sign itself preceded by a path
# separator (docs/%/oc, /%/opencode) is a literal "%" path segment, not
# the "100%/oc" percentage case above, where the percent sign is preceded
# by a digit. Both use a fixed-width two-character lookaround instead of
# widening the single-character sets above, which would have reopened
# one of the two cases each pair is meant to distinguish. The trailing
# colon lookaround excludes a following word character OR "/", not just
# a word character: a colon followed by a slash (/oc:/config, /oc://foo)
# is exactly as much a path/URI structure as a colon followed directly
# by a word, and checking only for a word character left this open.
# - "@cwl-noema-review" on its own additionally excludes a preceding "/"
# (closing the same URL/path-embedding class as "@opencode-agent" above)
# but deliberately NOT a trailing "/": that would break recognition of
# its own mention inside the "@cwl-noema-review/@opencode-agent"
# separator, where a "/" legitimately follows it.
MENTION_PATTERNS = {
"cwl-noema-review": re.compile(
r"(?<![A-Za-z0-9_-])@cwl-noema-review(?![A-Za-z0-9_-])",
r"(?<![\w/-])@cwl-noema-review(?![\w-])",
re.IGNORECASE,
),
"opencode-agent": re.compile(
r"(?<![A-Za-z0-9_-])@opencode-agent(?![A-Za-z0-9_-])",
r"(?:"
r"(?<![\w/-])@opencode-agent(?![\w/-])"
r"|(?<![\w/-])@cwl-noema-review/@opencode-agent(?![\w/-])"
r"|(?<![\w./?=#:-])(?<!/%)(?:/opencode|/oc)(?![\w./?=#%-])(?!:[\w/])"
r")",
re.IGNORECASE,
),
}
Expand Down
Loading
Loading