DashboardcraftSession 0Xl2Oe2WuFj3

Session 0Xl2Oe2WuFj3

Full ID: 0Xl2Oe2WuFj3XVAoq
Messages: 63
Distillations: 8
Time range: 2026-09-08 02:50:33 — 2026-09-08 03:00:48

Conversation (63 messages)

Perform a strict read-only adversarial audit of the exact current uncommitted changes in /home/byk/Code/getsentry/publish-workspace-acceptance for PR #9339. Do not edit files, run formatters, or mutate git state. Inspect git diff against origin/main and all relevant source/tests/workflows. Check: Peggy title and release-revision parser correctness; exact canonical request-header authority and repo/SHA validation; merge target derives only from canonical header; duplicate link detection; rewrite bounds and replacement-SHA validation; workflow CI body byte preservation and safe temp cleanup; valid JSON fallbacks/Action output encoding; conditional root .craft.yml workspace discovery and exact path resolution; fresh ci-ready gates; exact CI revision checkout; state isolation; intentional getsentry/craft:latest; docs-generation parity; action checkout v7 request; tests and diff hygiene. Run read-only validation commands as useful. Return only severity-ranked findings with file:line evidence, explicit PASS/FAIL each requested check, and a final MERGE or DO-NOT-MERGE verdict. If no issues, say so explicitly and still give the verdict.

gateway · 2026-09-08 02:50:33
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance</path>
<type>directory</type>
<entries>
.eslintrc.js
.git
.github/
.gitignore
AGENTS.md
auto-approve-repos.txt
docs/
LICENSE
node_modules/
package.json
README.md
scripts/
src/
vitest.config.js
yarn.lock

(15 entries)
</entries>
gateway · 2026-09-08 02:51:04
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/AGENTS.md</path>
<type>file</type>
<content>
1: # AGENTS.md - Coding Agent Guidelines
2: 
3: This document provides guidelines for AI coding agents working in this repository.
4: 
5: ## Project Overview
6: 
7: This is the **Sentry Publish Repository** - a GitHub Actions-based approval system for publishing releases across multiple Sentry projects using [Craft](https://github.com/getsentry/craft). It's a pure JavaScript (Node.js 24.0.0) project using CommonJS modules.
8: 
9: ## Build/Lint/Test Commands
10: 
11: ### Package Manager
12: 
13: - **Yarn 1.22.22** (classic) - Do NOT use npm
14: - Version management via Volta (pinned in package.json)
15: 
16: ### Commands
17: 
18: ```bash
19: # Install dependencies
20: yarn install
21: 
22: # Run all tests
23: yarn test
24: 
25: # Run tests in watch mode
26: yarn test:watch
27: 
28: # Run a single test file
29: yarn test src/modules/__tests__/update-issue.js
30: 
31: # Run tests ...
gateway · 2026-09-08 02:51:11
result
 M .github/workflows/ci-poller.yml
 M .github/workflows/publish.yml
 M docs/publish-issue-format.md
 M scripts/generate-publish-issue-title-parser.js
 M src/modules/__tests__/details-from-context.js
 M src/modules/__tests__/publish-workflow.js
 M src/modules/__tests__/release-revision.js
 M src/modules/details-from-context.js
 M src/modules/publish-issue-title.js
 M src/modules/publish-issue-title.peggy
 M src/modules/release-revision.js
 M src/publish/inputs.js
 M src/publish/resolve-ci-poller-input.js
 M src/publish/resolve-location.js
 M src/publish/resolve-release-revision.js
?? src/modules/__tests__/generate-publish-issue-title-parser.js
?? src/publish/__tests__/
?? ...
gateway · 2026-09-08 02:51:21
result
M	.github/workflows/ci-poller.yml
M	.github/workflows/publish.yml
M	docs/publish-issue-format.md
M	scripts/generate-publish-issue-title-parser.js
M	src/modules/__tests__/details-from-context.js
M	src/modules/__tests__/publish-workflow.js
M	src/modules/__tests__/release-revision.js
M	src/modules/details-from-context.js
M	src/modules/publish-issue-title.js
M	src/modules/publish-issue-title.peggy
M	src/modules/release-revision.js
M	src/publish/inputs.js
M	src/publish/resolve-ci-poller-input.js
M	src/publish/resolve-location.js
M	src/publish/resolve-release-revision.js
a81ab03 (HEAD -> feat/workspace-acceptance, origin/feat/workspace-acceptance) feat: resolve workspace publish paths
52ef600 feat: resolve compact workspace publish requests
b658ffa fix: validate publish workspace JSON
33de3f1 feat: accept workspace publish ...
gateway · 2026-09-08 02:51:26
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/publish-issue-title.peggy</path>
<type>file</type>
<content>
1: {
2:   function join(characters) {
3:     return characters.join("");
4:   }
5: }
6: 
7: // BEGIN TITLE GRAMMAR
8: // Canonical grammar for publish issue titles. A path suffix is syntactic only:
9: // the controller resolves the complete suffix as a workspace after checking
10: // out the CI-approved revision.
11: PublishIssueTitle
12:   = "publish: " "getsentry/"? repo:Repository path:Path? "@" version:Version !. {
13:       return {
14:         repo,
15:         path: path || "",
16:         version,
17:       };
18:     }
19: 
20: Repository
21:   = characters:RepositoryCharacter+ { return join(characters); }
22: 
23: RepositoryCharacter
24:   = [A-Za-z0-9_.-]
25: 
26: Path
27:   = segments:("/" segment:PathSegment { return `/${segment}`; })+ { return join(segments); }
28: 
29: PathSegment
30:   = characters:RepositoryCharacter+ { return ...
gateway · 2026-09-08 02:51:29
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/release-revision.js</path>
<type>file</type>
<content>
1: const { parse } = require("./publish-issue-title");
2: 
3: function isRevision(revision) {
4:   return /^[0-9a-f]{40}$/.test(revision);
5: }
6: 
7: function getReleaseRevisionDetails({ issueBody, repo }) {
8:   if (
9:     parse(issueBody, { startRule: "CheckRunsLinkCount" }) !== 1
10:   ) {
11:     throw new Error(
12:       `Expected exactly one View check runs link in Quick links for getsentry/${repo}.`
13:     );
14:   }
15: 
16:   let details;
17:   try {
18:     details = parse(issueBody, { startRule: "ReleaseRevision" });
19:   } catch {
20:     throw new Error(
21:       `Expected a View check runs link for getsentry/${repo} in the publish issue body.`
22:     );
23:   }
24: 
25:   if (details.repo !== repo) {
26:     throw new Error(
27:       `Expected a View check runs link for getsentry/${repo} in the publish issue body.`
28:     );
29:   }
30: ...
gateway · 2026-09-08 02:51:33
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/details-from-context.js</path>
<type>file</type>
<content>
1: const { parse: parsePublishIssueTitle } = require("./publish-issue-title");
2: const { getReleaseRevisionDetails } = require("./release-revision");
3: const { isPublishPath } = require("./publish-location");
4: const {
5:   isPublishRepository,
6:   isReleaseVersion,
7: } = require("./publish-issue-validation");
8: 
9: /**
10:  * Matches the entire "Targets" section of a github publish issue body.
11:  */
12: const TARGETS_SECTION_PARSER_REGEX =
13:   /^(?!### Targets$\s)(?: *- \[[ xX]\] \S+\s*$(?:\r?\n)?)+/m;
14: 
15: /**
16:  * Matches all targets of a github publish issue body in a section that was already matched and extracted with `TARGETS_PARSER_REGEX`.
17:  * The "id" of the targets is captured within a capture group. ...
gateway · 2026-09-08 02:51:37
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/publish-issue-validation.js</path>
<type>file</type>
<content>
1: function isPublishRepository(name) {
2:   return (
3:     /^[A-Za-z0-9_.-]+$/.test(name) &&
4:     name !== "." &&
5:     name !== ".." &&
6:     name !== "__proto__" &&
7:     !name.startsWith("-")
8:   );
9: }
10: 
11: function isReleaseVersion(version) {
12:   return /^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(?:-?([0-9a-z-]+(?:\.[0-9a-z-]+)*))?(?:\+([0-9a-z-]+(?:\.[0-9a-z-]+)*))?$/i.test(
13:     version
14:   );
15: }
16: 
17: module.exports = { isPublishRepository, isReleaseVersion };

(End of file - total 17 lines)
</content>
gateway · 2026-09-08 02:51:41
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/publish-location.js</path>
<type>file</type>
<content>
1: function resolvePublishLocation({ path, workspaceNames }) {
2:   if (!isPublishPath(path)) {
3:     throw new Error("Invalid publish path.");
4:   }
5: 
6:   if (!workspaceNames.every(isWorkspaceName)) {
7:     throw new Error(
8:       "Craft workspace discovery returned an invalid workspace list."
9:     );
10:   }
11: 
12:   if (path === ".") {
13:     return { path };
14:   }
15: 
16:   const workspace = path.slice(2);
17:   if (workspaceNames.includes(workspace)) {
18:     return { path: ".", workspace };
19:   }
20: 
21:   return { path };
22: }
23: 
24: function isPublishPath(path) {
25:   return (
26:     typeof path === "string" &&
27:     (path === "." ||
28:       (path.startsWith("./") &&
29:         path
30:           .slice(2)
31:           .split("/")
32:           .every(isSafeWorkspaceSegment)))
33:   );
34: }
35: 
36: function ...
gateway · 2026-09-08 02:51:46
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/ci-poller-input.js</path>
<type>file</type>
<content>
1: const { parse: parsePublishIssueTitle } = require("./publish-issue-title");
2: const {
3:   getReleaseRevision,
4:   updateReleaseRevision,
5: } = require("./release-revision");
6: const {
7:   isPublishRepository,
8:   isReleaseVersion,
9: } = require("./publish-issue-validation");
10: const { isPublishPath } = require("./publish-location");
11: 
12: function getCiPollerInput({ title, issueBody, revision }) {
13:   let parsedTitle;
14:   try {
15:     parsedTitle = parsePublishIssueTitle(title);
16:   } catch {
17:     throw new Error(`Invalid publish issue title: '${title}'`);
18:   }
19:   const { repo, version } = parsedTitle;
20:   if (!isPublishRepository(repo)) {
21:     throw new Error(`Invalid publish issue repository: '${repo}'`);
22:   }
23:   if (!isReleaseVersion(version)) {
24:     throw new Error(`Invalid publish issue version: ...
gateway · 2026-09-08 02:51:50
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/publish/inputs.js</path>
<type>file</type>
<content>
1: const github = require('@actions/github');
2: const core = require('@actions/core');
3: const { detailsFromContext } = require('../modules/details-from-context');
4: 
5: async function inputs() {
6:     const result = await detailsFromContext({
7:       context: github.context,
8:     });
9:     core.setOutput('result', JSON.stringify(result));
10: }
11: 
12: 
13: inputs();

(End of file - total 13 lines)
</content>
gateway · 2026-09-08 02:51:53
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/publish/resolve-ci-poller-input.js</path>
<type>file</type>
<content>
1: const { getCiPollerInput } = require("../modules/ci-poller-input");
2: const { readFileSync } = require("fs");
3: 
4: const issueBody = process.env.PUBLISH_ISSUE_BODY_FILE
5:   ? readFileSync(process.env.PUBLISH_ISSUE_BODY_FILE, "utf8")
6:   : process.env.PUBLISH_ISSUE_BODY || "";
7: 
8: process.stdout.write(
9:   JSON.stringify(
10:     getCiPollerInput({
11:       issueBody,
12:       title: process.env.PUBLISH_TITLE || "",
13:       revision: process.env.PUBLISH_REVISION || "",
14:     })
15:   )
16: );

(End of file - total 16 lines)
</content>
gateway · 2026-09-08 02:51:57
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/publish/resolve-release-revision.js</path>
<type>file</type>
<content>
1: const core = require("@actions/core");
2: const { getReleaseRevision } = require("../modules/release-revision");
3: 
4: function resolveReleaseRevision() {
5:   const { repo } = JSON.parse(process.env.PUBLISH_ARGS || "{}");
6:   if (!repo) {
7:     throw new Error("Publish input must define a repository.");
8:   }
9: 
10:   core.setOutput(
11:     "revision",
12:     getReleaseRevision({
13:       issueBody: process.env.PUBLISH_ISSUE_BODY || "",
14:       repo,
15:     })
16:   );
17: }
18: 
19: resolveReleaseRevision();

(End of file - total 19 lines)
</content>
gateway · 2026-09-08 02:52:01
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/publish/resolve-location.js</path>
<type>file</type>
<content>
1: const core = require("@actions/core");
2: const { resolvePublishLocation } = require("../modules/publish-location");
3: 
4: function resolveLocation() {
5:   const input = JSON.parse(process.env.PUBLISH_ARGS || "{}");
6:   const workspaceNames = JSON.parse(process.env.CRAFT_WORKSPACE_NAMES || "[]");
7: 
8:   if (!Array.isArray(workspaceNames)) {
9:     throw new Error(
10:       "Craft workspace discovery returned an invalid workspace list."
11:     );
12:   }
13: 
14:   core.setOutput(
15:     "result",
16:     JSON.stringify(
17:       resolvePublishLocation({
18:         path: input.path,
19:         workspaceNames,
20:       })
21:     )
22:   );
23: }
24: 
25: resolveLocation();

(End of file - total 25 lines)
</content>
gateway · 2026-09-08 02:52:05
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/publish/discover-location.js</path>
<type>file</type>
<content>
1: const { execFileSync } = require("child_process");
2: const { existsSync } = require("fs");
3: const path = require("path");
4: 
5: const core = require("@actions/core");
6: 
7: const { resolvePublishLocation } = require("../modules/publish-location");
8: 
9: function getWorkspaceNames({
10:   repositoryDirectory,
11:   exists = existsSync,
12:   execFile = execFileSync,
13: }) {
14:   if (!exists(path.join(repositoryDirectory, ".craft.yml"))) {
15:     return [];
16:   }
17: 
18:   const output = execFile(
19:     "docker",
20:     [
21:       "run",
22:       "--rm",
23:       "--volume",
24:       `${path.resolve(repositoryDirectory)}:/github/workspace/__repo__`,
25:       "--workdir",
26:       "/github/workspace/__repo__",
27:       "getsentry/craft:latest",
28:       "workspace",
29:       "list",
30:     ],
31:     { encoding: "utf8" }
32:   );
33:  ...
gateway · 2026-09-08 02:52:09
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/.github/workflows/publish.yml</path>
<type>file</type>
<content>
1: name: Publish
2: on:
3:   issues:
4:     types: [labeled]
5: 
6: concurrency:
7:   # Use the issue title (e.g. "publish: getsentry/foo@1.2.3") so duplicate
8:   # issues for the same repo@version share a concurrency group.
9:   group: ${{ github.event.issue.title }}
10:   cancel-in-progress: false
11: 
12: permissions:
13:   contents: read
14:   issues: write
15:   packages: write
16: 
17: jobs:
18:   # When accepted is added to a publish issue:
19:   #   - Add ci-pending (and remove ci-failed if retrying)
20:   #   - Enable the poller via CI_POLLER_HAS_PENDING=true
21:   #   - Comment on the issue
22:   #   - Trigger the poller immediately so we don't wait for the next cron tick
23:   # The publish job below requires ci-ready, so it will not fire until the
24:   # poller flips ci-pending → ci-ready (which also prevents publishing without
25:   # CI ...
gateway · 2026-09-08 02:52:14
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/.github/workflows/ci-poller.yml</path>
<type>file</type>
<content>
1: name: CI Status Poller
2: 
3: on:
4:   schedule:
5:     - cron: "*/5 * * * *"
6:   workflow_dispatch:
7:     inputs:
8:       attempt:
9:         description: Self-dispatch attempt counter (internal use)
10:         required: false
11:         default: "0"
12: 
13: permissions:
14:   contents: read
15:   issues: read
16: 
17: jobs:
18:   check-ci:
19:     runs-on: ubuntu-latest
20:     environment: production
21:     # Skip entirely (no runner provisioned) when there's nothing to check.
22:     # Set to "true" by ci-pending.yml, reset to "false" here when done.
23:     # Always allow workflow_dispatch for manual recovery.
24:     if: vars.CI_POLLER_HAS_PENDING == 'true' || github.event_name == 'workflow_dispatch'
25:     concurrency:
26:       group: ci-status-poller
27:       cancel-in-progress: false
28:     steps:
29:       # sentry-internal-app token ...
gateway · 2026-09-08 02:52:40
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/__tests__/release-revision.js</path>
<type>file</type>
<content>
1: import { expect, test } from "vitest";
2: 
3: const {
4:   getReleaseRevision,
5:   updateReleaseRevision,
6: } = require("../release-revision.js");
7: 
8: const REVISION = "7e5ca7ed5581552de066e2a8bc295b8306be38ac";
9: 
10: function requestBody(quickLinks) {
11:   return `Requested by: @byk
12: 
13: Merge target: (default)
14: 
15: Quick links:
16: ${quickLinks}`;
17: }
18: 
19: function canonicalQuickLinks(revision = REVISION) {
20:   return `- [View changes](https://github.com/getsentry/toolkit/compare/1.2.2...release/1.2.3)
21: - [View check runs](https://github.com/getsentry/toolkit/commit/${revision}/checks/)`;
22: }
23: 
24: test("gets the CI-approved revision from the request header", () => {
25:   expect(
26:     getReleaseRevision({
27:       repo: "toolkit",
28:       issueBody: requestBody(canonicalQuickLinks()),
29:     })
30:   ...
gateway · 2026-09-08 02:52:45
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/__tests__/details-from-context.js</path>
<type>file</type>
<content>
1: import { test, expect } from "vitest";
2: 
3: const { detailsFromContext } = require("../details-from-context.js");
4: 
5: const inputsArgs = {
6:   context: {
7:     repo: { owner: "getsentry", repo: "publish" },
8:     payload: {
9:       issue: {
10:         number: "223",
11:         title: "publish: getsentry/sentry@21.3.1",
12:         body: `Requested by: @BYK
13: 
14: Merge target: custom-branch
15: 
16: Quick links:
17: - [View changes](https://github.com/getsentry/sentry/compare/21.3.0...refs/heads/releases/21.3.1)
18: - [View check runs](https://github.com/getsentry/sentry/commit/7e5ca7ed5581552de066e2a8bc295b8306be38ac/checks/)
19: 
20: Assign the **accepted** label to this issue to approve the release. ...
gateway · 2026-09-08 02:52:49
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/publish/__tests__</path>
<type>directory</type>
<entries>
discover-location.js
resolve-location.js
resolve-release-revision.js

(3 entries)
</entries>
gateway · 2026-09-08 02:52:55
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/publish/__tests__/discover-location.js</path>
<type>file</type>
<content>
1: import { afterEach, expect, test, vi } from "vitest";
2: 
3: const { discoverLocation, getWorkspaceNames } = require("../discover-location.js");
4: 
5: afterEach(() => {
6:   vi.restoreAllMocks();
7: });
8: 
9: test("retains checkout-path behavior when the root config is absent", () => {
10:   expect(
11:     discoverLocation({
12:       input: { path: "./packages/cli" },
13:       repositoryDirectory: "__repo__",
14:       exists: () => false,
15:     })
16:   ).toEqual({ path: "./packages/cli" });
17: });
18: 
19: test("discovers exact workspace paths using the released Craft image", () => {
20:   const execFile = vi.fn(() => '["packages/CLI"]');
21: 
22:   expect(
23:     discoverLocation({
24:       input: { path: "./packages/CLI" },
25:       repositoryDirectory: "__repo__",
26:       exists: () => true,
27:       execFile,
28:     })
29:   ...
gateway · 2026-09-08 02:53:01
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/publish/__tests__/resolve-location.js</path>
<type>file</type>
<content>
1: import { spawnSync } from "child_process";
2: import { expect, test } from "vitest";
3: 
4: const script = new URL("../resolve-location.js", import.meta.url).pathname;
5: 
6: test("rejects missing workflow input through location validation", () => {
7:   const result = spawnSync(process.execPath, [script], {
8:     encoding: "utf8",
9:     env: {
10:       ...process.env,
11:       PUBLISH_ARGS: "",
12:       CRAFT_WORKSPACE_NAMES: "[]",
13:     },
14:   });
15: 
16:   expect(result.status).not.toBe(0);
17:   expect(result.stderr).toContain("Invalid publish path.");
18:   expect(result.stderr).not.toContain("Unexpected end of JSON input");
19: });

(End of file - total 19 lines)
</content>
gateway · 2026-09-08 02:53:06
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/publish/__tests__/resolve-release-revision.js</path>
<type>file</type>
<content>
1: import { spawnSync } from "child_process";
2: import { expect, test } from "vitest";
3: 
4: const script = new URL("../resolve-release-revision.js", import.meta.url).pathname;
5: 
6: test("rejects missing workflow input through release revision validation", () => {
7:   const result = spawnSync(process.execPath, [script], {
8:     encoding: "utf8",
9:     env: {
10:       ...process.env,
11:       PUBLISH_ARGS: "",
12:       PUBLISH_ISSUE_BODY: "",
13:     },
14:   });
15: 
16:   expect(result.status).not.toBe(0);
17:   expect(result.stderr).toContain("Publish input must define a repository.");
18:   expect(result.stderr).not.toContain("Unexpected end of JSON input");
19: });

(End of file - total 19 lines)
</content>
gateway · 2026-09-08 02:53:11
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/scripts/generate-publish-issue-title-parser.js</path>
<type>file</type>
<content>
1: const fs = require("fs");
2: const path = require("path");
3: const peggy = require("peggy");
4: const prettier = require("prettier");
5: 
6: const grammarPath = path.join(
7:   __dirname,
8:   "..",
9:   "src",
10:   "modules",
11:   "publish-issue-title.peggy"
12: );
13: const outputPath = path.join(
14:   __dirname,
15:   "..",
16:   "src",
17:   "modules",
18:   "publish-issue-title.js"
19: );
20: const documentationPath = path.join(
21:   __dirname,
22:   "..",
23:   "docs",
24:   "publish-issue-format.md"
25: );
26: function generateDocumentation({ documentation, titleGrammar }) {
27:   const marker = /<!-- BEGIN GENERATED TITLE GRAMMAR -->\n[\s\S]*?<!-- END GENERATED TITLE GRAMMAR -->/.exec(
28:     documentation
29:   );
30: 
31:   if (!marker) {
32:     throw new Error("Could not find the generated title grammar in the ...
gateway · 2026-09-08 02:53:15
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/docs/publish-issue-format.md</path>
<type>file</type>
<content>
1: # Publish Issue Format
2: 
3: The Publish workflow treats a publish issue as a release request. The title identifies
4: the release; the body supplies the merge target and selected targets. Do not edit the
5: title by hand unless it remains valid under this format.
6: 
7: ## Title
8: 
9: Every title starts with `publish: `. The following canonical Peggy grammar is
10: generated from `src/modules/publish-issue-title.peggy`:
11: 
12: <!-- BEGIN GENERATED TITLE GRAMMAR -->
13: ```peggy
14: // Canonical grammar for publish issue titles. A path suffix is syntactic only:
15: // the controller resolves the complete suffix as a workspace after checking
16: // out the CI-approved revision.
17: PublishIssueTitle
18:   = "publish: " "getsentry/"? repo:Repository path:Path? "@" version:Version !. ...
gateway · 2026-09-08 02:53:21
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/package.json</path>
<type>file</type>
<content>
1: {
2:   "name": "publish",
3:   "private": true,
4:   "version": "0.0.1",
5:   "description": "Approval-based publishing system for Sentry",
6:   "main": "index.js",
7:   "repository": "git@github.com:getsentry/publish.git",
8:   "author": "Sentry Open Source <oss@sentry.io>",
9:   "license": "Apache-2.0",
10:   "volta": {
11:     "node": "24.0.0",
12:     "yarn": "1.22.22"
13:   },
14:   "resolutions": {
15:     "undici": "^6.23.0"
16:   },
17:   "scripts": {
18:     "generate": "node scripts/generate-publish-issue-title-parser.js",
19:     "check:generated": "node scripts/generate-publish-issue-title-parser.js --check",
20:     "test": "yarn check:generated && vitest run",
21:     "test:watch": "vitest",
22:     "lint": "eslint src .github --ignore-pattern '!.github'",
23:     "prettier": "prettier --write src"
24:   },
25:   "devDependencies": {
26:     "eslint": ...
gateway · 2026-09-08 02:53:29
result
yarn run v1.22.22
$ node scripts/generate-publish-issue-title-parser.js --check
Done in 0.58s.
gateway · 2026-09-08 02:53:34
result
yarn run v1.22.22
$ yarn check:generated && vitest run --runInBand
$ node scripts/generate-publish-issue-title-parser.js --check
file:///home/byk/Code/getsentry/publish-workspace-acceptance/node_modules/vitest/dist/chunks/cac.CWGDZnXT.js:405
          throw new CACError(`Unknown option \`${name.length > 1 ? `--${name}` : `-${name}`}\``);
                ^

CACError: Unknown option `--runInBand`
    at Command.checkUnknownOptions (file:///home/byk/Code/getsentry/publish-workspace-acceptance/node_modules/vitest/dist/chunks/cac.CWGDZnXT.js:405:17)
    at CAC.runMatchedCommand (file:///home/byk/Code/getsentry/publish-workspace-acceptance/node_modules/vitest/dist/chunks/cac.CWGDZnXT.js:605:13)
    at CAC.parse (file:///home/byk/Code/getsentry/publish-workspace-acceptance/node_modules/vitest/dist/chunks/cac.CWGDZnXT.js:546:12)
    at file:///home/byk/Code/getsentry/publish-workspace-acceptance/node_modules/vitest/dist/cli.js:29:13
    at ModuleJob.run ...
gateway · 2026-09-08 02:53:41
result
yarn run v1.22.22
$ yarn check:generated && vitest run
$ node scripts/generate-publish-issue-title-parser.js --check

 RUN  v4.1.0 /home/byk/Code/getsentry/publish-workspace-acceptance


 Test Files  13 passed (13)
      Tests  86 passed (86)
   Start at  02:53:42
   Duration  2.19s (transform 256ms, setup 0ms, import 1.30s, tests 905ms, environment 2ms)

Done in 3.63s.
gateway · 2026-09-08 02:53:48
result
yarn run v1.22.22
$ eslint src .github --ignore-pattern '!.github'
Done in 1.07s.
gateway · 2026-09-08 02:53:56
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/publish-issue-title.js</path>
<type>file</type>
<content>
1: /* eslint-disable */
2: // @generated by Peggy 5.1.0.
3: //
4: // https://peggyjs.org/
5: 
6: "use strict";
7: 
8: class peg$SyntaxError extends SyntaxError {
9:   constructor(message, expected, found, location) {
10:     super(message);
11:     this.expected = expected;
12:     this.found = found;
13:     this.location = location;
14:     this.name = "SyntaxError";
15:   }
16: 
17:   format(sources) {
18:     let str = "Error: " + this.message;
19:     if (this.location) {
20:       let src = null;
21:       const st = sources.find((s) => s.source === this.location.source);
22:       if (st) {
23:         src = st.text.split(/\r\n|\n|\r/g);
24:       }
25:       const s = this.location.start;
26:       const offset_s =
27:         this.location.source &&
28:         typeof this.location.source.offset === "function"
29:           ? ...
gateway · 2026-09-08 02:54:02
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/update-issue.js</path>
<type>file</type>
<content>
1: const fs = require("fs");
2: const {
3:   TARGETS_SECTION_PARSER_REGEX,
4:   TARGETS_PARSER_REGEX,
5: } = require("./details-from-context");
6: 
7: async function updateTargets({
8:   octokit,
9:   stateFilePath,
10:   publishRepo,
11:   issue_number,
12: }) {
13:   if (!stateFilePath || !fs.existsSync(stateFilePath)) {
14:     return;
15:   }
16: 
17:   const issueRequest = octokit.rest.issues.get({
18:     ...publishRepo,
19:     issue_number,
20:   });
21: 
22:   const craftStateRequest = fs.promises
23:     .readFile(stateFilePath, { encoding: "utf-8" })
24:     .then((data) => JSON.parse(data));
25: 
26:   const [{ data: issue }, craftState] = await Promise.all([
27:     issueRequest,
28:     craftStateRequest,
29:   ]);
30: 
31:   const newIssueBody = transformIssueBody(craftState, issue.body);
32: 
33:   await octokit.rest.issues.update({
34:     ...
gateway · 2026-09-08 02:54:10
result
Found 28 matches
/home/byk/Code/getsentry/craft/src/utils/publishState.ts:
  Line 2:  * Helpers for locating Craft's publish-state file in a path that is NOT

  Line 13:  * The file now lives under `$XDG_STATE_HOME/craft/` (falling back to

  Line 30:  * Resolves `$XDG_STATE_HOME/craft/` with the standard fallback to

  Line 31:  * `$HOME/.local/state/craft/` when `XDG_STATE_HOME` is unset.

  Line 34:  * `scripts/print-publish-state-path.ts` if present) that needs to

  Line 38:   const xdgStateHome = process.env.XDG_STATE_HOME;

  Line 68:  * Builds the filename for the publish-state file.

  Line 71:  *   `publish-state-<owner>-<repo>-<sha1(cwd)[:12]>-<version>.json`

  Line 76:  *   `publish-state-<sha256(cwd)[:16]>-<version>.json`

  Line 87:     return `publish-state-${owner}-${repo}-${shortCwdHash(cwd)}-${safeVersion}.json`;

  Line 90:   return `publish-state-${cwdDigest}-${safeVersion}.json`;

  Line 94:  * Full absolute path to the publish-state file for the given version. ...
gateway · 2026-09-08 02:54:15
result
<path>/home/byk/Code/getsentry/craft/src/utils/publishState.ts</path>
<type>file</type>
<content>
1: /**
2:  * Helpers for locating Craft's publish-state file in a path that is NOT
3:  * writable by the repository being published.
4:  *
5:  * Background: `craft publish` writes a small JSON file listing targets
6:  * that have completed so a resumed run can skip them. Before this module
7:  * existed, the file lived at `.craft-publish-<version>.json` in the
8:  * project's cwd. That path is inside the repository checkout, so any
9:  * committed file at the same path (or any earlier CI step) could
10:  * pre-populate the "published" set and trick Craft into silently
11:  * skipping targets.
12:  *
13:  * The file now lives under `$XDG_STATE_HOME/craft/` (falling back to
14:  * `$HOME/.local/state/craft/`). The filename is keyed on
15:  * owner, repo, a hash of cwd (to disambiguate monorepo subpaths), and
16:  * the version being published. ...
gateway · 2026-09-08 02:54:26
result
 M .lore.md
?? .craft-issue-842-plan.md
?? .opencode/
b06435e4f20ff8cb0470de98ee113051978b3d63
b06435e fix(commit-on-git): Use proper username and pass for
byk/fix/commit-on-repo
gateway · 2026-09-08 02:54:35
result
d48b906 feat: support concrete release workspaces
21270a1 feat(config): add release workspaces
ab63572 fix(publish): isolate workspace release state
98052d9 security(publish): move publish-state file out of repo cwd (#797)
21270a1 feat(config): add release workspaces
ab63572 fix(publish): isolate workspace release state
4e2d8f4 feat(config): add top-level workspaces schema + --workspace selector
* byk/fix/commit-on-repo
gateway · 2026-09-08 02:54:41
result
Found 89 matches
/home/byk/Code/getsentry/craft/src/utils/workspaces.ts:
  Line 15: /** Information about a workspace package */

  Line 25:   /** Dependencies that are also workspace packages */

  Line 26:   workspaceDependencies: string[];

  Line 29: /** Result of workspace discovery */

  Line 31:   /** The type of workspace manager detected */

  Line 37: /** Structure of pnpm-workspace.yaml */

  Line 45:   workspaces?: string[] | { packages?: string[] };

  Line 93:  * Extract workspaces array from package.json workspaces field

  Line 97:   workspaces: string[] | { packages?: string[] } | undefined,

  Line 99:   if (!workspaces) {

  Line 102:   if (Array.isArray(workspaces)) {

  Line 103:     return workspaces;

  Line 105:   return workspaces.packages || [];

  Line 115:   // First: collect all workspace package names and locations

  Line 116:   const workspaceLocations: Array<{

  Line 120:   const workspaceNames = new Set<string>();

  Line 132:         ...
gateway · 2026-09-08 02:54:47
result
/**
 * Helpers for locating Craft's publish-state file in a path that is NOT
 * writable by the repository being published.
 *
 * Background: `craft publish` writes a small JSON file listing targets
 * that have completed so a resumed run can skip them. Before this module
 * existed, the file lived at `.craft-publish-<version>.json` in the
 * project's cwd. That path is inside the repository checkout, so any
 * committed file at the same path (or any earlier CI step) could
 * pre-populate the "published" set and trick Craft into silently
 * skipping targets.
 *
 * The file now lives under `$XDG_STATE_HOME/craft/` (falling back to
 * `$HOME/.local/state/craft/`). The filename is keyed on
 * owner, repo, a hash of cwd (to disambiguate monorepo subpaths), and
 * the version being published. `getsentry/publish` runs inside a Docker
 * image with `HOME=/root`, so the XDG state dir is a clean,
 * workflow-writable location that committed repo contents cannot reach. ...
gateway · 2026-09-08 02:55:06
result
No files found
gateway · 2026-09-08 02:55:12
result
6dae341 feat: propagate release workspaces
48620564f86d8724982cddb6506c040235fe8954:src/commands/publish.ts:180
48620564f86d8724982cddb6506c040235fe8954:src/commands/publish.ts:191
48620564f86d8724982cddb6506c040235fe8954:src/commands/__tests__/publish.test.ts:209
6dae341f9bc1378abde7a9022108caded2d22226:src/commands/publish.ts:180
6dae341f9bc1378abde7a9022108caded2d22226:src/commands/publish.ts:191
6dae341f9bc1378abde7a9022108caded2d22226:src/commands/__tests__/publish.test.ts:208
d48b90604ba5e86b80211effc4d91b5384492dc1:src/commands/publish.ts:180
d48b90604ba5e86b80211effc4d91b5384492dc1:src/commands/publish.ts:191
d48b90604ba5e86b80211effc4d91b5384492dc1:src/commands/__tests__/publish.test.ts:209
gateway · 2026-09-08 02:55:43
result
diff --git a/.github/workflows/ci-poller.yml b/.github/workflows/ci-poller.yml
index a3db430..6c112aa 100644
--- a/.github/workflows/ci-poller.yml
+++ b/.github/workflows/ci-poller.yml
@@ -21,165 +21,166 @@ jobs:
     # Skip entirely (no runner provisioned) when there's nothing to check.
     # Set to "true" by ci-pending.yml, reset to "false" here when done.
     # Always allow workflow_dispatch for manual recovery.
     if: vars.CI_POLLER_HAS_PENDING == 'true' || github.event_name == 'workflow_dispatch'
     concurrency:
       group: ci-status-poller
       cancel-in-progress: false
     steps:
       # sentry-internal-app token for label changes on this repo.
       # This token is what triggers publish.yml downstream — see
       # https://github.com/getsentry/publish for why GITHUB_TOKEN can't.
       - name: Get auth token
         id: token
         uses: actions/create-github-app-token@v3
         with:
           client-id: ${{ vars.SENTRY_INTERNAL_APP_ID }}
           ...
gateway · 2026-09-08 02:56:04
result
Found 47 matches
/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/release-revision.js:
  Line 12:       `Expected exactly one View check runs link in Quick links for getsentry/${repo}.`

  Line 21:       `Expected a View check runs link for getsentry/${repo} in the publish issue body.`

  Line 27:       `Expected a View check runs link for getsentry/${repo} in the publish issue body.`


/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/publish-issue-title.js:
  Line 208:   const peg$c4 = "Requested by: @";

  Line 209:   const peg$c5 = "Merge target: ";

  Line 213:   const peg$c9 = "- [View check runs](https://github.com/getsentry/";

  Line 216:   const peg$c12 = "- [View check runs](";

  Line 244:   const peg$e7 = peg$literalExpectation("Requested by: @", false);

  Line 245:   const peg$e8 = peg$literalExpectation("Merge target: ", false);

  Line 250:     "- [View check runs](https://github.com/getsentry/",

  Line 264:   const peg$e16 = ...
gateway · 2026-09-08 02:56:12
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/__tests__/ci-poller-input.js</path>
<type>file</type>
<content>
1: import { readFileSync } from "fs";
2: import { join } from "path";
3: 
4: import { expect, test } from "vitest";
5: 
6: const { getCiPollerInput } = require("../ci-poller-input.js");
7: 
8: const REVISION = "7e5ca7ed5581552de066e2a8bc295b8306be38ac";
9: const issueBody = `Requested by: @byk
10: 
11: Merge target: (default)
12: 
13: Quick links:
14: - [View changes](https://github.com/getsentry/toolkit/compare/1.2.2...release/1.2.3)
15: - [View check runs](https://github.com/getsentry/toolkit/commit/${REVISION}/checks/)`;
16: 
17: test("parses root and full-path publish titles", () => {
18:   expect(
19:     getCiPollerInput({
20:       issueBody,
21:       title: "publish: getsentry/toolkit/cli@1.2.3",
22:     })
23:   ).toEqual({
24:     repo: "getsentry/toolkit",
25:     revision: REVISION,
26:     version: "1.2.3",
27:   });
28: 
29:   ...
gateway · 2026-09-08 02:56:17
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/__tests__/publish-location.js</path>
<type>file</type>
<content>
1: import { expect, test } from "vitest";
2: 
3: const { resolvePublishLocation } = require("../publish-location.js");
4: 
5: test("classifies an exact full workspace path without normalizing it", () => {
6:   expect(
7:     resolvePublishLocation({
8:       path: "./packages/CLI",
9:       workspaceNames: ["packages/cli", "packages/CLI"],
10:     })
11:   ).toStrictEqual({ path: ".", workspace: "packages/CLI" });
12: });
13: 
14: test("keeps a non-workspace suffix as a checkout path", () => {
15:   expect(
16:     resolvePublishLocation({
17:       path: "./packages",
18:       workspaceNames: ["cli"],
19:     })
20:   ).toStrictEqual({ path: "./packages" });
21: });
22: 
23: test("keeps a multi-segment suffix that is not an exact workspace path", () => {
24:   expect(
25:     resolvePublishLocation({
26:       path: "./packages/cli",
27:       ...
gateway · 2026-09-08 02:56:21
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/__tests__/publish-workflow.js</path>
<type>file</type>
<content>
1: import { spawnSync } from "child_process";
2: import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "fs";
3: import { tmpdir } from "os";
4: import { join } from "path";
5: 
6: import { afterEach, expect, test } from "vitest";
7: 
8: const { resolvePublishLocation } = require("../publish-location.js");
9: 
10: const temporaryDirectories = [];
11: 
12: function getWorkflow() {
13:   return readFileSync(
14:     join(__dirname, "../../../.github/workflows/publish.yml"),
15:     "utf8"
16:   );
17: }
18: 
19: function getSetTargetsScript() {
20:   const workflow = getWorkflow();
21:   const section = workflow.match(
22:     / {6}- name: Set targets[\s\S]*? {8}run: \|\n(?<script>[\s\S]*?)\n {6}- uses: docker:\/\/getsentry\/craft:latest/
23:   );
24:   if (!section?.groups?.script) {
25:     throw new Error("Missing Set targets workflow ...
gateway · 2026-09-08 02:56:28
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/__tests__/generate-publish-issue-title-parser.js</path>
<type>file</type>
<content>
1: import { expect, test } from "vitest";
2: 
3: const {
4:   generateDocumentation,
5: } = require("../../../scripts/generate-publish-issue-title-parser.js");
6: 
7: test("requires generated title grammar markers in the documentation", () => {
8:   expect(() =>
9:     generateDocumentation({
10:       documentation: "# Publish Issue Format\n",
11:       titleGrammar: 'PublishIssueTitle = "publish: "',
12:     })
13:   ).toThrow("Could not find the generated title grammar in the documentation.");
14: });

(End of file - total 14 lines)
</content>
gateway · 2026-09-08 02:56:35
result
diff --git a/src/modules/ci-poller-input.js b/src/modules/ci-poller-input.js
new file mode 100644
index 0000000..ca08a72
--- /dev/null
+++ b/src/modules/ci-poller-input.js
@@ -0,0 +1,42 @@
+const { parse: parsePublishIssueTitle } = require("./publish-issue-title");
+const {
+  getReleaseRevision,
+  updateReleaseRevision,
+} = require("./release-revision");
+const {
+  isPublishRepository,
+  isReleaseVersion,
+} = require("./publish-issue-validation");
+const { isPublishPath } = require("./publish-location");
+
+function getCiPollerInput({ title, issueBody, revision }) {
+  let parsedTitle;
+  try {
+    parsedTitle = parsePublishIssueTitle(title);
+  } catch {
+    throw new Error(`Invalid publish issue title: '${title}'`);
+  }
+  const { repo, version } = parsedTitle;
+  if (!isPublishRepository(repo)) {
+    throw new Error(`Invalid publish issue repository: '${repo}'`);
+  }
+  if (!isReleaseVersion(version)) {
+    throw new Error(`Invalid publish issue version: ...
gateway · 2026-09-08 02:56:54
result
Found 6 matches
/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/publish-issue-validation.js:
  Line 11: function isReleaseVersion(version) {

  Line 17: module.exports = { isPublishRepository, isReleaseVersion };


/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/ci-poller-input.js:
  Line 8:   isReleaseVersion,

  Line 23:   if (!isReleaseVersion(version)) {


/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/details-from-context.js:
  Line 6:   isReleaseVersion,

  Line 43:   if (!isReleaseVersion(titleDetails.version)) {
gateway · 2026-09-08 02:56:59
result
Found 70 matches
/home/byk/Code/getsentry/craft/src/config.ts:
  Line 24:   parseVersion,

  Line 200:   const minVersion = parseVersion(minVersionRaw);

  Line 204:   const currentVersion = parseVersion(currentVersionRaw);

  Line 238:   const configuredMinVersion = parseVersion(minVersionRaw);

  Line 239:   const required = parseVersion(requiredVersion);


/home/byk/Code/getsentry/craft/src/commands/publish.ts:
  Line 37: import { isValidVersion } from '../utils/version';

  Line 158:  * Checks that the passed version is a valid version string

  Line 165:   if (isValidVersion(version)) {

  Line 168:     throw Error(`Invalid version provided: "${version}"`);


/home/byk/Code/getsentry/craft/src/commands/prepare.ts:
  Line 66: import { getVersion, isValidVersion } from '../utils/version';

  Line 168:  * We check that the argument is either a valid version string, 'auto' for

  Line 170:  * bump type (major/minor/patch), or a valid semantic version. ...
gateway · 2026-09-08 02:57:03
result
<path>/home/byk/Code/getsentry/craft/src/utils/version.ts</path>
<type>file</type>
<content>
1: import { getGitTagPrefix } from '../config';
2: 
3: /**
4:  * Regular expression for matching semver versions.
5:  *
6:  * Modified to match version components
7:  * Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
8:  * @see https://github.com/sindresorhus/semver-regex
9:  */
10: const semverRegex = () =>
11:   /\bv?(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(?:-?([\da-z-]+(?:\.[\da-z-]+)*))?(?:\+([\da-z-]+(?:\.[\da-z-]+)*))?\b/gi;
12: 
13: /**
14:  * Extracts a version number from the given text.
15:  *
16:  * In case the version contains a leading "v", it is stripped from the result.
17:  * All semantic versions are supported. See {@link http://semver.org/} for
18:  * more information. ...
gateway · 2026-09-08 02:57:13
result
No files found
gateway · 2026-09-08 02:57:20
result
d48b906:docs/src/content/docs/targets/github.md:110:The `craft workspace list` command prints the concrete workspace paths as a
d48b906:src/commands/workspace.ts:3:import * as list from './workspace_cmds/list';
d48b906:src/commands/workspace_cmds/__tests__/list.test.ts:13:describe('workspace list command', () => {
gateway · 2026-09-08 02:57:28
result
import { getWorkspaceNames } from '../../config';
import { formatJson } from '../../utils/strings';

export const command = ['list'];
export const description = 'List defined release workspaces as a JSON array';

export function handler(): void {
  console.log(formatJson(getWorkspaceNames()));
}
import { Argv, CommandBuilder } from 'yargs';

import * as list from './workspace_cmds/list';

export const command = ['workspace <command>'];
export const description = 'Manage release workspaces';

export const builder: CommandBuilder = (yargs: Argv) =>
  yargs.demandCommand().command(list);

export const handler = (): void => {
  /* pass */
};
172-/**
173- * The Publish controller prepopulates a secure state file using the issue's
174- * checkout repository. That can differ from a workspace's release GitHub
175- * configuration, so this override is deliberately limited to state identity. ...
gateway · 2026-09-08 02:57:56
result
...output truncated...

Full output saved to: /home/byk/.local/share/opencode/tool-output/tool_07ef365dd0013Ij3LidM9dWec8

 
             # Check combined commit status ("pending" means statuses exist but
             # some haven't resolved yet; it does NOT mean "no statuses reported").
             # Skip the issue entirely if the status API fails (e.g., app isn't
             # installed on this repo) — don't try to derive state from partial data.
             if ! status_json=$(gh_api_release "repos/${repo}/commits/${sha}/status"); then
               echo "  Could not fetch commit status for ${repo}@${sha:0:8}, skipping."
               continue
             fi
             commit_status=$(echo "$status_json" | jq -r '.state')
             total_statuses=$(echo "$status_json" | jq -r '.total_count')
 
             # Fetch all check runs (paginate to handle repos with >30 checks).
             # --paginate --jq applies the filter per-page, so we flatten with
             # ...
gateway · 2026-09-08 02:58:18
result
pipeline continued
gateway · 2026-09-08 02:58:36
result
Found 14 matches
/home/byk/Code/getsentry/craft/src/commands/publish.ts:
  Line 134:   mergeTarget?: string;

  Line 418:  * @param [mergeTarget] Branch name to merge the release branch into

  Line 425:   mergeTarget?: string,

  Line 428:   if (!mergeTarget) {

  Line 429:     mergeTarget = await getDefaultBranch(git, remoteName);

  Line 431:   logger.debug(`Checking out merge target branch:`, mergeTarget);

  Line 432:   await git.checkout(mergeTarget);

  Line 434:   logger.debug(`Pulling latest changes from ${remoteName}/${mergeTarget}`);

  Line 436:     await git.pull(remoteName, mergeTarget, ['--rebase']);

  Line 448:   logger.debug(`Merging ${branch} into: ${mergeTarget}`);

  Line 501:     await git.push(remoteName, mergeTarget);

  Line 779:   if (noMergeConfig.noMerge && !argv.noMerge && argv.mergeTarget) {

  Line 807:         argv.mergeTarget,


/home/byk/Code/getsentry/craft/src/commands/__tests__/publish.test.ts:
  Line 392:   test('resolves default branch when ...
gateway · 2026-09-08 02:58:42
result
No files found
gateway · 2026-09-08 02:59:03
result
main; not-a-branch
gateway · 2026-09-08 02:59:45
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/__tests__/update-issue.js</path>
<type>file</type>
<content>
1: import { vi, describe, test, expect, beforeAll, beforeEach, it } from "vitest";
2: import fs from "fs";
3: 
4: const { updateIssue, transformIssueBody } = require("../update-issue.js");
5: 
6: let mockExistsSync;
7: 
8: const updateTargetsArgs = {
9:   inputs: { repo: "sentry", version: "21.3.1" },
10:   context: {
11:     runId: "1234",
12:     repo: { owner: "getsentry", repo: "publish" },
13:     payload: { issue: { number: "211" } },
14:   },
15:   octokit: {
16:     rest: {
17:       actions: {
18:         getWorkflowRun: async () => ({
19:           data: {
20:             html_url: "https://github.com/getsentry/sentry/actions/runs/1234",
21:           },
22:         }),
23:       },
24:       issues: {
25:         get: vi.fn(),
26:         update: vi.fn(),
27:         removeLabel: vi.fn(),
28:       },
29:     },
30:   },
31:   Sentry: {
32:    ...
gateway · 2026-09-08 03:00:02
result
canonical revision only
gateway · 2026-09-08 03:00:09
result
 M .github/workflows/ci-poller.yml
 M .github/workflows/publish.yml
 M docs/publish-issue-format.md
 M scripts/generate-publish-issue-title-parser.js
 M src/modules/__tests__/details-from-context.js
 M src/modules/__tests__/publish-workflow.js
 M src/modules/__tests__/release-revision.js
 M src/modules/details-from-context.js
 M src/modules/publish-issue-title.js
 M src/modules/publish-issue-title.peggy
 M src/modules/release-revision.js
 M src/publish/inputs.js
 M src/publish/resolve-ci-poller-input.js
 M src/publish/resolve-location.js
 M src/publish/resolve-release-revision.js
?? src/modules/__tests__/generate-publish-issue-title-parser.js
?? src/publish/__tests__/
?? ...
gateway · 2026-09-08 03:00:48

MAJOR

  1. Invalid revision rewrites can erase an issue body.
    .github/workflows/ci-poller.yml:147-151 pipes the validator into jq without pipefail. If updateReleaseRevision rejects the replacement SHA, jq still exits successfully and writes an empty temp file; gh issue edit --body-file then replaces the canonical body with it.
    Evidence: bash -e continues after false | jq …. No regression covers this workflow failure path.

  2. The canonical parser accepts merge targets outside the documented contract.
    src/modules/publish-issue-title.peggy:43 captures mergeTarget as unrestricted NonNewline (:78), while docs/publish-issue-format.md:94 permits only letters, digits, _, ., /, and -. src/modules/details-from-context.js:54-75 forwards the raw value, and .github/workflows/publish.yml:247 supplies it to Craft.
    Example accepted input: Merge target: main; not-a-branch. This breaks the strict canonical format and docs/code parity.

MEDIUM

  1. Temporary issue-body files are not cleaned up on failure.
    .github/workflows/ci-poller.yml:98-105 and :147-155 remove files only on successful paths. A failing rewrite, gh issue edit, or later unguarded command leaves body_file and/or updated_body_file behind. Use a scoped cleanup trap.

MINOR

  1. Unrelated formatter churn is included.
    .github/workflows/cocoapods-keepalive.yml:1 only removes quotes from the workflow name and has no relation to workspace acceptance.

Requested checks

| Check | Result | |---|---| | Peggy title parser correctness | PASS — anchored grammar; title validation follows parsing. | | Release-revision parser correctness | PASS — header starts at byte zero; exact repo and lowercase 40-character SHA required. | | Canonical request-header authority | FAIL — authority is anchored, but merge-target syntax is not canonical (finding 2). ...

gpt-5.6-terra · 2026-09-08 03:00:48 · ~881 tokens

Distillations (8)

Gen 0 2026-09-08 03:02:03 · 1337 tokens

Date: Sep 8, 2026 * 🟡 (02:58) Search of `craft/src/commands/publish.ts` found 14 `mergeTarget` matches: optional `mergeTarget?: string` at line 134; `mergeTarget` parameter documentation at line 418 and optional parameter at line 425; absent values resolve through `getDefaultBranch(git, remoteName)` at lines 428–430; Craft checks out, pulls/rebases, merges into, and pushes `mergeTarget` at lines…

Gen 0 2026-09-08 03:00:57 · 2073 tokens

Date: Sep 8, 2026 * 🔴 (02:58) User stated that new Craft publish requests always include the checkout repository identity. * 🔴 (02:58) User stated that a missing root `.craft.yml` always means checkout-path behavior. * 🔴 (02:58) User stated that the controller never normalizes workspace names; Craft preserves exact workspace-path spelling. * 🟡 (02:57) `craft/src/commands/workspace_cmds/list.t…

Gen 0 2026-09-08 03:00:09 · 2002 tokens

Date: Sep 8, 2026 * 🔴 (02:56) User provided the file path `/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/__tests__/publish-location.js`. * 🟡 (02:56) Search found 47 publish-issue “View check runs” references across `src/modules/release-revision.js`, generated `src/modules/publish-issue-title.js`, and tests including `src/modules/__tests__/release-revision.js`, `ci-poller-inp…

Gen 0 2026-09-08 02:57:43 · 1608 tokens

Date: Sep 8, 2026 * 🟡 (02:55) Git history/search output identified commit `6dae341` (`feat: propagate release workspaces`) and matching `src/commands/publish.ts:180`, `src/commands/publish.ts:191`, and `src/commands/__tests__/publish.test.ts:208`; earlier matching revisions were `48620564f86d8724982cddb6506c040235fe8954` (`publish.test.ts:209`) and `d48b90604ba5e86b80211effc4d91b5384492dc1` (`pu…

Gen 0 2026-09-08 02:56:55 · 2670 tokens

Date: Sep 8, 2026 * 🟡 (02:52) `src/modules/__tests__/release-revision.js` tests `getReleaseRevision()` and `updateReleaseRevision()` using canonical request-header Quick links for `getsentry/toolkit` and SHA `7e5ca7ed5581552de066e2a8bc295b8306be38ac`. * 🟡 (02:52) `release-revision.js` test coverage requires `getReleaseRevision()` to accept CRLF bodies and a `View check runs` URL with or without…

Gen 0 2026-09-08 02:55:35 · 1410 tokens

Date: Sep 8, 2026 * 🔴 (02:52) User stated that `ci-poller.yml` must always allow `workflow_dispatch` for manual recovery. * 🟡 (02:52) `.github/workflows/ci-poller.yml` is named `CI Status Poller`; triggers on cron `*/5 * * * *` and `workflow_dispatch` with optional internal `attempt` input defaulting to `"0"`. It has `contents: read` and `issues: read` permissions. * 🟡 (02:52) `.github/workflo…

Gen 0 2026-09-08 02:55:00 · 2880 tokens

Date: Sep 8, 2026 * 🔴 (02:51) User provided repository guidelines for `/home/byk/Code/getsentry/publish-workspace-acceptance`: Sentry Publish Repository is a pure CommonJS JavaScript project on Node.js 24.0.0; use Yarn 1.22.22 via Volta and do not use npm; primary commands are `yarn test`, `yarn test:watch`, `yarn lint`, and `yarn prettier`. * 🔴 (02:51) User stated source files use kebab-case, …

Gen 0 2026-09-08 02:52:48 · 322 tokens

Date: Sep 8, 2026 * 🔴 [requested-audit] (02:50) User requested a strict read-only adversarial audit of the exact current uncommitted changes in `/home/byk/Code/getsentry/publish-workspace-acceptance` for PR #9339, comparing `git diff` against `origin/main`. * 🔴 [enforced-read-only] (02:50) User prohibited editing files, running formatters, and mutating Git state; read-only validation commands a…