Skip to content

Commit 507caef

Browse files
only21milclaude
andcommitted
Merge branch 'main' into fix/i24371-cursor-via-acp-registry-can-t-pick-reaso
Both conflicts were in tests: AcpRegistryProbe.test.ts keeps both sides' new tests with the Cursor test now also providing NodeCrypto.layer, and modelSelection.test.ts keeps this PR's tests without the planModeEnabled argument main removed and adds main's extra opencode agent option to the saved-options table. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 parents 478e6ef + 671f854 commit 507caef

31,694 files changed

Lines changed: 2430351 additions & 1810913 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.agents/skills/contribution-triage/SKILL.md‎

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ trusted policy and reassess changed submission evidence; neither example grants
111111

112112
Do not automatically close a PR if a maintainer in the trusted `TRIAGE_EXEMPTIONS.td` list has
113113
commented or submitted a review, including on earlier heads, or if `triage:keep-open` is present.
114+
Comments that triage itself posted, such as an earlier notice or question, are not maintainer
115+
involvement, even when they were posted from a maintainer's account on that maintainer's behalf.
114116
Leave protected PRs open for maintainer decision. This does not grant an author exemption,
115117
eligibility, or review approval.
116118

@@ -126,7 +128,17 @@ comment text but do not write to GitHub. Keep the eligibility finding separate f
126128
This is a routing exception, not a claim that the PR passed eligibility or correctness review.
127129
- **Closure warranted.** The assessment is complete and establishes a specific policy violation.
128130
Prepare a clear explanation under [closure and reconsideration](../../../CONTRIBUTING.md#closure-and-reconsideration),
129-
post it, and close the PR. Verify that the explanation is present and the PR is closed. The comment
131+
post it, then read the repository variable `PR_LIMITS_ENFORCE`
132+
(`gh variable get PR_LIMITS_ENFORCE -R pingdotgg/t3code`). When it prints `true`, apply
133+
`triage:closing` as the last thing you do to the PR, verify that the explanation and the label
134+
are present, and do not close the PR yourself:
135+
[pr-limits.yml](../../../.github/workflows/pr-limits.yml) closes it 72 hours after the label. Any
136+
activity on the PR after the label, including a further comment or label from you, removes the
137+
label; treat an author's reply or push as new evidence, reassess, and apply the label again only
138+
if the finding still holds. When the command prints any other value, or fails saying the variable
139+
was not found, the variable is off and nothing would close a labeled PR: close it yourself and
140+
verify that the explanation is present and the PR is closed. Only another failure, such as a
141+
permission error, means the variable cannot be read; then report the closure as incomplete. The comment
130142
must name the violated rule, cite supporting submission evidence, link the maintained guide section,
131143
and give a concrete remedy. Missing contributor evidence can warrant closure; explain what must be
132144
established. Missing product approval calls for maintainer discussion, not an agent's product decision.
@@ -173,9 +185,10 @@ description, evidence, approvals, existing triage comments, PR open/closed state
173185
label when relevant. Reassess changes that could invalidate the finding. Reuse an existing explanation
174186
only if it still matches the current finding; avoid duplicate comments, closures, or label applications.
175187

176-
For closure, establish that the required explanation is posted before closing. If posting fails or its
177-
result is ambiguous, read back the comments before retrying or proceeding. If the comment succeeds but
178-
closure fails, preserve the comment and retry only the unfinished closure after checking current state.
188+
For closure, establish that the required explanation is posted before applying `triage:closing` or
189+
closing. If posting fails or its result is ambiguous, read back the comments before retrying or
190+
proceeding. If the comment succeeds but the label or the closure fails, preserve the comment and retry
191+
only that unfinished step after checking current state.
179192
Handle a failed or ambiguous label application the same way: inspect labels before any retry. If access
180193
or state still cannot be verified, report an incomplete action and the step needed to resume. Do not
181194
retry blindly or claim success for an unverified action.

‎.agents/skills/test-t3-app/SKILL.md‎

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ description: Test T3 Code's web and desktop UI through its built-in Browser pane
55

66
# Test T3 web and desktop
77

8-
Use T3's built-in Browser panel for verification. If its tools are absent or
9-
the panel reports unavailable, explain the blocker and stop verification.
10-
Do not install or switch to another automation system. For native mobile
11-
testing, use [test-t3-mobile](../test-t3-mobile/SKILL.md).
8+
Use T3's built-in Browser panel for verification. Follow the recovery steps
9+
below before declaring it unavailable. Do not install or switch to another
10+
automation system. For native mobile testing, use
11+
[test-t3-mobile](../test-t3-mobile/SKILL.md).
1212

1313
## Start the app
1414

@@ -36,8 +36,13 @@ model, such as Haiku for Claude or Luna for Codex.
3636

3737
## Use the Browser panel
3838

39-
Call `preview_status`, then `preview_open` if the Browser panel is
40-
closed. Navigate to the complete startup pairing URL once with
39+
Call `preview_status`. If no tab can be automated, call `preview_open`, including
40+
when status reports `available: false` or `tabId: null`. Status can report
41+
unavailable simply because no tab exists yet; opening a tab needs no user action.
42+
Stop verification and explain the blocker only if the tools are absent or
43+
`preview_open` reports unavailable or fails.
44+
45+
Navigate to the complete startup pairing URL once with
4146
`preview_navigate`, then use `preview_snapshot` and T3's interaction tools.
4247
If the token was consumed or expired, run `node apps/server/src/bin.ts pair`
4348
for a fresh one. Keep using the same tab.

‎.agents/skills/test-t3-mobile/SKILL.md‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,9 @@ vp run mobile:install <ios|android> <device-id>
3636

3737
This installs the dev client for the checkout's native fingerprint: from the
3838
device, the local cache, or the shared cache in seconds, building once only
39-
when no machine has that fingerprint yet (5-15 minutes, logged to the file it
40-
names). The device id may be omitted when exactly one device is booted. It
39+
when no machine has that fingerprint yet: in the `Mobile Dev Client` workflow
40+
on Blacksmith when HEAD is pushed and `gh` may dispatch it (the command waits),
41+
otherwise here (5-15 minutes, logged to the file it names). The device id may be omitted when exactly one device is booted. It
4142
prints one JSON line; on failure it names the next command. Mobile verification
4243
includes that step unless the user prohibits it.
4344

‎.github/SECURITY.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Security policy
22

33
Report security vulnerabilities affecting T3 Code or T3 Tools-operated infrastructure to
4-
[security@ping.gg](mailto:security@ping.gg). Please do not disclose them publicly until we have had
4+
[security@t3.codes](mailto:security@t3.codes). Please do not disclose them publicly until we have had
55
a reasonable opportunity to investigate and remediate them.
66

77
See the [full security policy](https://t3.codes/security-policy) for reporting details, scope,

‎.github/actions/ci-changes/action.yml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ outputs:
55
value: ${{ steps.detect.outputs.typecheck }}
66
build:
77
value: ${{ steps.detect.outputs.build }}
8+
desktop_launch:
9+
value: ${{ steps.detect.outputs.desktop_launch }}
810
test:
911
value: ${{ steps.detect.outputs.test }}
1012
test_packages:

‎.github/actions/setup-ci-deps/action.yml‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ inputs:
1313
description: Arguments for `vp install`, such as filters. Empty installs the whole workspace.
1414
required: false
1515
default: ""
16+
save-pnpm-store:
17+
description: Set to "false" when a concurrent job with the same install args saves the pnpm store
18+
required: false
19+
default: "true"
1620
runs:
1721
using: composite
1822
steps:
@@ -123,10 +127,14 @@ runs:
123127
key: ${{ steps.pnpm_verified.outputs.cache-primary-key }}
124128

125129
# A filtered install that started from the current full store adds nothing
126-
# worth a second copy of it.
130+
# worth a second copy of it. A stacked PR, based on a branch other than the
131+
# default, saves a ~1 GB store only its own reruns can read, so it only restores.
132+
# Concurrent jobs with the same install args reserve one key, so the losers
133+
# compress the store for nothing; all but one pass save-pnpm-store: false.
127134
- name: Save pnpm store
128135
if: >-
129-
inputs.install == 'true' &&
136+
inputs.install == 'true' && inputs.save-pnpm-store == 'true' &&
137+
(github.event_name != 'pull_request' || github.base_ref == github.event.repository.default_branch) &&
130138
steps.pnpm_cache.outputs.cache-hit != 'true' &&
131139
(steps.pnpm_store.outputs.namespace == 'full' ||
132140
steps.pnpm_cache.outputs.cache-matched-key != format('pnpm-full-v1-{0}-{1}-{2}', runner.os, runner.arch, hashFiles('pnpm-lock.yaml', 'pnpm-workspace.yaml', 'package.json')))

‎.github/scripts/check-nightly-release.cjs‎

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -40,42 +40,67 @@ async function assertReleaseSource({ github, context, releaseChannel }) {
4040

4141
const isNightlyTag = (tag) => /^v.*-nightly\./.test(tag) || tag.startsWith("nightly-v");
4242

43-
async function findLatestNightly({ github, context }) {
44-
const releases = await github.paginate(github.rest.repos.listReleases, {
45-
...context.repo,
46-
per_page: 100,
47-
});
48-
return releases
49-
.filter((release) => !release.draft && release.published_at && isNightlyTag(release.tag_name))
50-
.sort((a, b) => Date.parse(b.published_at) - Date.parse(a.published_at))[0];
43+
// Only the three fields the gate reads. The REST list also returns every asset,
44+
// which made a full scan tens of megabytes.
45+
const RELEASES_QUERY = `
46+
query ($owner: String!, $repo: String!, $cursor: String) {
47+
repository(owner: $owner, name: $repo) {
48+
releases(first: 100, after: $cursor, orderBy: { field: CREATED_AT, direction: DESC }) {
49+
pageInfo { hasNextPage endCursor }
50+
nodes { tagName isDraft publishedAt }
51+
}
52+
}
53+
}
54+
`;
55+
56+
// Returns the nightly with the newest publishedAt. Release order follows the
57+
// tagged commit date, not publication, so every page is read unless `isSettled`
58+
// says the newest nightly seen so far already decides the caller's answer.
59+
async function findLatestNightly({ github, context, isSettled = () => false }) {
60+
let latest;
61+
let cursor = null;
62+
do {
63+
const { repository } = await github.graphql(RELEASES_QUERY, { ...context.repo, cursor });
64+
const { nodes, pageInfo } = repository.releases;
65+
for (const release of nodes) {
66+
if (release.isDraft || !release.publishedAt || !isNightlyTag(release.tagName)) continue;
67+
if (!latest || Date.parse(release.publishedAt) > Date.parse(latest.publishedAt)) {
68+
latest = release;
69+
}
70+
}
71+
if (latest && isSettled(latest)) return latest;
72+
cursor = pageInfo.hasNextPage ? pageInfo.endCursor : null;
73+
} while (cursor);
74+
return latest;
5175
}
5276

5377
async function shouldReleaseNightly({ github, context, core, now = Date.now() }) {
54-
const lastNightly = await findLatestNightly({ github, context });
78+
const isInGap = (release) => now - Date.parse(release.publishedAt) < MINIMUM_RELEASE_GAP_MS;
79+
// Any nightly inside the gap means the newest one is too, so stop reading.
80+
const lastNightly = await findLatestNightly({ github, context, isSettled: isInGap });
5581

5682
if (!lastNightly) {
5783
core.info("No published nightly found. Proceeding with release.");
5884
return true;
5985
}
6086

61-
if (now - Date.parse(lastNightly.published_at) < MINIMUM_RELEASE_GAP_MS) {
62-
core.info(`Nightly ${lastNightly.tag_name} was published less than six hours ago. Skipping.`);
87+
const tag = lastNightly.tagName;
88+
if (isInGap(lastNightly)) {
89+
core.info(`Nightly ${tag} was published less than six hours ago. Skipping.`);
6390
return false;
6491
}
6592

6693
const { data: comparison } = await github.rest.repos.compareCommitsWithBasehead({
6794
...context.repo,
68-
basehead: `${lastNightly.tag_name}...${context.sha}`,
95+
basehead: `${tag}...${context.sha}`,
6996
per_page: 1,
7097
});
7198
if (comparison.status !== "ahead") {
72-
core.info(
73-
`Candidate commit is ${comparison.status} relative to ${lastNightly.tag_name}. Skipping.`,
74-
);
99+
core.info(`Candidate commit is ${comparison.status} relative to ${tag}. Skipping.`);
75100
return false;
76101
}
77102

78-
core.info(`New commits since ${lastNightly.tag_name}, and the six-hour gap has passed.`);
103+
core.info(`New commits since ${tag}, and the six-hour gap has passed.`);
79104
return true;
80105
}
81106

@@ -85,7 +110,7 @@ async function resolveLatestNightlyCommit({ github, context, core }) {
85110
throw new Error("No published nightly found. Stable releases build the latest nightly commit.");
86111
}
87112

88-
const tag = lastNightly.tag_name;
113+
const tag = lastNightly.tagName;
89114
const { data: commit } = await github.rest.repos.getCommit({ ...context.repo, ref: tag });
90115
const version = /^(?:nightly-)?v(\d+\.\d+\.\d+)-nightly\./.exec(tag)?.[1];
91116
if (!version) {

0 commit comments

Comments
 (0)