Skip to content

Commit 996d44e

Browse files
committed
Merge remote-tracking branch 'origin/main' into release-please--branches--main--groups--engine-and-dependents
# Conflicts: # .release-please-manifest.json
2 parents 2753ad1 + 3fbde45 commit 996d44e

17 files changed

Lines changed: 1475 additions & 21 deletions
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
# Logic/regex-change backtest (#8139, epic #8082). When a PR touches the watched detection-logic paths,
2+
# this job replays linked_issue_scope_mismatch's recorded raw-context history (#8129/#8130 + the #8139
3+
# model-response capture) through BOTH the PR's own head checkout and its base checkout — actually executing
4+
# the two versions of the detection code, which is why this lives in CI and not in ORB's live Worker (the
5+
# Worker holds credentials and must never execute PR-supplied logic; a CI checkout running a PR's own code
6+
# is the exact trust boundary validate-code/validate-tests already use for every PR). Deliberately a
7+
# separate workflow, not a ci.yml job: PRs that don't touch these paths pay nothing, and ones that do aren't
8+
# slowed — this runs fully parallel to (and finishes long before) the ~11-minute test shards.
9+
# Advisory only: never a required check, never blocks merge (#8105). It posts its OWN clearly-labeled PR
10+
# comment, separate from ORB's unified review comment — see #8139's Boundaries for why.
11+
name: backtest-logic
12+
13+
on:
14+
pull_request:
15+
# Explicit list because the default (opened/synchronize/reopened) omits ready_for_review -- once the
16+
# draft guard below skips draft PRs, marking a PR ready must itself trigger a real run (#6670).
17+
# Mirrors selfhost.yml's pull_request.types comment/list exactly.
18+
types: [opened, synchronize, reopened, ready_for_review]
19+
# Exactly the paths whose changes can alter linked_issue_scope_mismatch-adjacent detection logic —
20+
# see #8139's Design section; keep this list in sync with the issue's own spec.
21+
paths:
22+
- "src/rules/**"
23+
- "src/review/content-lane/**"
24+
- "src/settings/agent-actions.ts"
25+
- "src/services/ai-review.ts"
26+
- "src/services/linked-issue-satisfaction.ts"
27+
28+
# Least privilege: the backtest only reads the repo; pull-requests: write is for its own advisory comment.
29+
permissions:
30+
contents: read
31+
pull-requests: write
32+
33+
concurrency:
34+
# pull_request-only workflow, so one ref-scoped group suffices (no push/github.sha split like ci.yml
35+
# needs): a newer push cancels the superseded run — its comment would be overwritten anyway.
36+
group: backtest-logic-${{ github.ref }}
37+
cancel-in-progress: true
38+
39+
jobs:
40+
backtest:
41+
name: logic backtest (advisory)
42+
# Skip draft PRs (#6670, anti-abuse — mirrors selfhost.yml's guard). Fork PRs are excluded at the job
43+
# level rather than per-step: GitHub withholds repo secrets from fork-originated pull_request runs, so
44+
# the D1 corpus read below is impossible there and the whole job (npm ci included) would be waste —
45+
# the fork-notice job below is this workflow's half of ci.yml's paired fork==true/!=true convention.
46+
if: ${{ github.event.pull_request.draft != true && github.event.pull_request.head.repo.fork != true }}
47+
runs-on: ubuntu-latest
48+
timeout-minutes: 15
49+
steps:
50+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
51+
with:
52+
persist-credentials: false
53+
54+
# The PR's base commit, checked out INSIDE the head workspace: the dynamically imported base modules
55+
# resolve bare npm specifiers by walking up from their own directory into the head checkout's
56+
# node_modules, so one `npm ci` serves both sides of the comparison.
57+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
58+
with:
59+
ref: ${{ github.event.pull_request.base.sha }}
60+
path: .backtest-base
61+
persist-credentials: false
62+
63+
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
64+
with:
65+
node-version-file: .nvmrc
66+
cache: "npm"
67+
68+
- name: Install deps
69+
run: npm ci --ignore-scripts
70+
71+
# The scripts import @loopover/engine, which resolves to its dist/ build output.
72+
- name: Build engine package
73+
run: npx turbo run build --filter=@loopover/engine
74+
75+
# Every step from here down FAILS OPEN (notice + green, never a red check): the review engine
76+
# auto-closes a contributor PR on ANY failed check, required or not — so an advisory job that can go
77+
# red on an infra problem (an under-scoped CLOUDFLARE_API_TOKEN, a D1 outage, a comment-post hiccup)
78+
# would let OUR plumbing close an innocent contributor's PR. "Never blocks merge" (#8105) has to hold
79+
# against this job's own failures, not just its verdicts.
80+
#
81+
# Read-only corpus export (#8084's CLI, reused as-is — no new D1 read code). The wrangler secrets are
82+
# available here because the fork guard above already excluded fork-originated runs.
83+
- name: Export corpus from D1
84+
id: corpus
85+
env:
86+
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
87+
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
88+
run: |
89+
if npx tsx scripts/backtest-corpus-export.ts --rule-id linked_issue_scope_mismatch --output backtest-corpus.json --remote; then
90+
echo "available=true" >> "$GITHUB_OUTPUT"
91+
else
92+
echo "available=false" >> "$GITHUB_OUTPUT"
93+
echo "::notice::Corpus export from D1 failed (missing or under-scoped CLOUDFLARE_API_TOKEN — it needs D1 read/write on the loopover database). Logic backtest skipped; advisory only, never fails the PR."
94+
fi
95+
96+
- name: Run logic backtest
97+
id: backtest
98+
if: ${{ steps.corpus.outputs.available == 'true' }}
99+
env:
100+
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
101+
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
102+
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
103+
BASE_SHA: ${{ github.event.pull_request.base.sha }}
104+
PR_NUMBER: ${{ github.event.pull_request.number }}
105+
run: |
106+
if npx tsx scripts/backtest-logic-check.ts \
107+
--rule-id linked_issue_scope_mismatch \
108+
--corpus backtest-corpus.json \
109+
--head-root . \
110+
--base-root .backtest-base \
111+
--output backtest-comment.md \
112+
--head-sha "$HEAD_SHA" \
113+
--base-sha "$BASE_SHA" \
114+
--persist --remote --db loopover \
115+
--repo "$GITHUB_REPOSITORY" \
116+
--pr "$PR_NUMBER"; then
117+
echo "ready=true" >> "$GITHUB_OUTPUT"
118+
else
119+
echo "ready=false" >> "$GITHUB_OUTPUT"
120+
echo "::notice::Logic backtest run failed — no comparison produced. Advisory only, never fails the PR."
121+
fi
122+
123+
# Update-in-place keyed on the comment marker so a re-run edits the existing comment instead of
124+
# stacking a new one per push.
125+
- name: Post or update the PR comment
126+
if: ${{ steps.backtest.outputs.ready == 'true' }}
127+
env:
128+
GH_TOKEN: ${{ github.token }}
129+
PR_NUMBER: ${{ github.event.pull_request.number }}
130+
run: |
131+
post_comment() {
132+
marker="<!-- loopover-logic-backtest -->"
133+
# --paginate runs the --jq filter once per page, so pin to the first emitted id.
134+
comment_id=$(gh api "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" --paginate \
135+
--jq "[.[] | select(.body | contains(\"${marker}\")) | .id] | first // empty" | head -n 1)
136+
if [ -n "$comment_id" ]; then
137+
gh api "repos/${GITHUB_REPOSITORY}/issues/comments/${comment_id}" -X PATCH -F body=@backtest-comment.md
138+
else
139+
gh api "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" -F body=@backtest-comment.md
140+
fi
141+
}
142+
if ! post_comment; then
143+
echo "::notice::PR comment post failed — backtest result computed and persisted but not posted. Advisory only, never fails the PR."
144+
fi
145+
146+
# The fork half of ci.yml's paired fork==true/!=true convention: fork-originated pull_request runs get no
147+
# repo secrets, so the D1-backed backtest cannot run — say so visibly instead of failing or going silent.
148+
# Advisory only either way; a skipped backtest never blocks anything (#8105).
149+
fork-notice:
150+
name: logic backtest (skipped for fork PRs)
151+
if: ${{ github.event.pull_request.draft != true && github.event.pull_request.head.repo.fork == true }}
152+
runs-on: ubuntu-latest
153+
timeout-minutes: 5
154+
steps:
155+
- name: Explain the skip
156+
run: echo "::notice::Fork PR — repo secrets are withheld, so the D1-backed logic backtest is skipped. Advisory only; nothing blocks."

.release-please-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
"packages/loopover-mcp": "3.5.0",
33
"packages/loopover-engine": "3.5.0",
44
"packages/loopover-miner": "3.5.0",
5-
"packages/loopover-ui-kit": "1.1.0"
5+
"packages/loopover-ui-kit": "1.1.1"
66
}

apps/loopover-ui/src/lib/mcp-package.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export const MCP_PACKAGE_REGISTRY_URL = `https://registry.npmjs.org/${MCP_PACKAG
88
export const MCP_PACKAGE_NPM_URL = `https://www.npmjs.com/package/${MCP_PACKAGE_NAME}`;
99
// Tracks the latest PUBLISHED release: ui:version-audit requires this to equal npm dist-tags.latest, so it is
1010
// bumped to a new version only AFTER that version publishes (never ahead of npm).
11-
export const MCP_PACKAGE_KNOWN_LATEST_VERSION = "3.2.3";
11+
export const MCP_PACKAGE_KNOWN_LATEST_VERSION = "3.4.0";
1212
export const MCP_MINIMUM_SUPPORTED_VERSION = "0.5.0";
1313

1414
export type NpmPackageMetadata = {

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/loopover-ui-kit/CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
# Changelog
22

3+
## [1.1.1](https://github.com/JSONbored/loopover/compare/ui-kit-v1.1.0...ui-kit-v1.1.1) (2026-07-23)
4+
5+
6+
### Fixes
7+
8+
* **miner-ui:** keep mobile chat sheet mounted so conversation state survives ([#7792](https://github.com/JSONbored/loopover/issues/7792)) ([#7885](https://github.com/JSONbored/loopover/issues/7885)) ([e7e10e7](https://github.com/JSONbored/loopover/commit/e7e10e7f79f094e988034c9fa3e82e0ebab0203b))
9+
* **miner-ui:** stick-to-bottom auto-scroll for chat rail ([#7229](https://github.com/JSONbored/loopover/issues/7229)) ([#7298](https://github.com/JSONbored/loopover/issues/7298)) ([8cbcb53](https://github.com/JSONbored/loopover/commit/8cbcb53799b7a943a9ce2a668263c5c427c530e6))
10+
* **test:** close the Node-version guard's remaining coverage gap ([#7627](https://github.com/JSONbored/loopover/issues/7627)) ([#7629](https://github.com/JSONbored/loopover/issues/7629)) ([9f356fe](https://github.com/JSONbored/loopover/commit/9f356fea0cb0cd499f9339d09cca0c044ce292c1))
11+
* **test:** pin loopover-ui + ui-kit jsdom localStorage over Node 26's broken global ([#7616](https://github.com/JSONbored/loopover/issues/7616)) ([d6477bf](https://github.com/JSONbored/loopover/commit/d6477bfa91ca51f130c7ebae7aa5da8ae6310d72))
12+
* **ui-kit:** edge-trigger StateBoundary failure notifications ([#7505](https://github.com/JSONbored/loopover/issues/7505)) ([fa67da4](https://github.com/JSONbored/loopover/commit/fa67da462511e298c06323a2842869f1a5ddd2d9))
13+
314
## [1.1.0](https://github.com/JSONbored/loopover/compare/ui-kit-v1.0.0...ui-kit-v1.1.0) (2026-07-17)
415

516

packages/loopover-ui-kit/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@loopover/ui-kit",
3-
"version": "1.1.0",
3+
"version": "1.1.1",
44
"license": "AGPL-3.0-only",
55
"type": "module",
66
"description": "Shared design-system tokens and component primitives for loopover-ui and loopover-miner-ui.",

0 commit comments

Comments
 (0)