DashboardcraftSession 1IwgpKHTTZkV

Session 1IwgpKHTTZkV

Full ID: 1IwgpKHTTZkVsDo9l
Messages: 62
Distillations: 12
Time range: 2026-08-29 12:48:17 — 2026-08-29 13:21:29

Conversation (62 messages)

Perform a rigorous, read-only final adversarial review of the current uncommitted compact workspace publish-contract changes in BOTH worktrees:

  • /home/byk/Code/getsentry/craft-workspace-action-propagation (branch feat/workspace-action-propagation)
  • /home/byk/Code/getsentry/publish-workspace-acceptance (branch feat/workspace-acceptance)

Do not edit files, stage, commit, reset, or run destructive commands. Inspect the actual current worktrees versus their branch bases.

Critical contract facts:

  • Publish always uses getsentry/craft:latest. This is intentional and must not be reported as a mutable-image concern. Craft must release first, so latest contains workspace list and craft publish --rev.
  • New Craft-created publish requests always include checkout repository identity.
  • Workspace names must preserve exact spelling and never be normalized.
  • New compact workspace releases use one trailing ASCII-safe segment, and Craft rejects a workspace combined with non-root path before side effects.
  • Legacy JSON-qualified workspace titles remain supported but must reject a workspace combined with non-root path.
  • pnpm-lock.yaml in Publish is unrelated and must never be committed.

Assess with exact file:line evidence:

  1. The exact CI-approved revision link is safely extracted and repository-bound.
  2. Checkout, compact classification, state path/CWD, workspace env, and craft publish --rev all use that exact revision and resolved location.
  3. Craft permits detached CI SHA checkout safely, while preserving normal named-ref behavior.
  4. Legacy title behavior is retained; compact workspace-first classification happens only after exact-release checkout; discovery only runs where necessary; absent root .craft.yml remains path behavior; discovery errors fail closed when root config exists.
  5. No workspace/path ambiguity remains, no name normalization or injection/traversal route exists.
  6. ...
gateway · 2026-08-29 12:48:17
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation</path>
<type>directory</type>
<entries>
.craft.yml
.dockerignore
.eslintcache
.git
.github/
.gitignore
.lore.md
.nojekyll
.npmignore
.prettierignore
.prettierrc.yml
.tsbuildinfo
.vscode/
action.yml
AGENTS.md
blog-post-draft.md
build.mjs
CHANGELOG.md
CONTRIBUTING.md
dist/
Dockerfile
docs/
eslint.config.mjs
img/
LICENSE
node_modules/
package.json
pnpm-lock.yaml
README.md
src/
tsconfig.build.json
tsconfig.json
vitest.config.mts

(33 entries)
</entries>
gateway · 2026-08-29 12:51:27
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
pnpm-lock.yaml
README.md
scripts/
src/
vitest.config.js
yarn.lock

(16 entries)
</entries>
gateway · 2026-08-29 12:52:50
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/AGENTS.md</path>
<type>file</type>
<content>
1: # AGENTS.md
2: 
3: This file provides guidance for AI coding assistants working with the Craft codebase.
4: 
5: ## Package Management
6: 
7: - **Always use `pnpm`** for package management. Never use `npm` or `yarn`.
8: - Node.js version is managed by [Volta](https://volta.sh/) (currently v22.12.0).
9: - Install dependencies with `pnpm install --frozen-lockfile`.
10: 
11: ## Development Commands
12: 
13: | Command      | Description                                 |
14: | ------------ | ------------------------------------------- |
15: | `pnpm build` | Build the project (outputs to `dist/craft`) |
16: | `pnpm test`  | Run tests                                   |
17: | `pnpm lint`  | Run ESLint                                  |
18: | `pnpm fix`   | Auto-fix lint issues                        |
19: 
20: To manually test changes:
21: 
22: ```bash
23: pnpm build && ...
gateway · 2026-08-29 12:53:53
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-08-29 12:57:38
result
## feat/workspace-action-propagation...origin/feat/workspace-action-propagation
 M action.yml
 M docs/src/content/docs/targets/github.md
 M src/__tests__/action.test.ts
 M src/__tests__/config.test.ts
 M src/commands/__tests__/publish.test.ts
 M src/commands/publish.ts
 M src/config.ts
 M src/index.ts
 M src/schemas/project_config.ts
?? src/commands/workspace.ts
?? src/commands/workspace_cmds/
feat/workspace-action-propagation
6dae341f9bc1378abde7a9022108caded2d22226
21270a1d12a60b461c51fba93bd076b90d9cf4af
M	.github/workflows/release.yml
M	action.yml
M	docs/src/content/docs/targets/github.md
A	src/__tests__/action.test.ts
M	src/__tests__/config.test.ts
M	src/commands/__tests__/publish.test.ts
M	src/commands/publish.ts
M	src/config.ts
M	src/index.ts
M	src/schemas/project_config.ts
M	action.yml
M	docs/src/content/docs/targets/github.md
M	src/__tests__/action.test.ts
M	src/__tests__/config.test.ts
M	src/commands/__tests__/publish.test.ts
M	src/commands/publish.ts
M	src/config.ts
M	src/in...
gateway · 2026-08-29 12:58:29
result
## feat/workspace-acceptance...origin/feat/workspace-acceptance
 M .github/workflows/publish.yml
 M docs/publish-issue-format.md
 M package.json
 M src/modules/__tests__/details-from-context.js
 M src/modules/__tests__/publish-workflow.js
 M src/modules/details-from-context.js
 M src/modules/post-workflow-details.js
 M src/publish/inputs.js
 M yarn.lock
?? pnpm-lock.yaml
?? scripts/
?? src/modules/__tests__/publish-location.js
?? src/modules/__tests__/release-revision.js
?? src/modules/publish-issue-title.js
?? src/modules/publish-issue-title.peggy
?? src/modules/publish-location.js
?? src/modules/release-revision.js
?? src/publish/resolve-location.js
?? src/publish/resolve-release-revision.js
feat/workspace-acceptance
b658ffa0299fedf78c9dd9a2121cd740691ca157
fatal: Not a valid object name origin/master
gateway · 2026-08-29 12:58:37
result
origin	https://github.com/getsentry/publish.git (fetch)
origin	https://github.com/getsentry/publish.git (push)
* feat/workspace-acceptance
  remotes/origin/feat/workspace-acceptance
  origin/HEAD -> origin/main
  origin/feat/ci-poller-variable-gate
  origin/feat/ci-status-poller
  origin/feat/workspace-acceptance
  origin/fix/ci-poller-gh-token
  origin/fix/ci-poller-variable-auth
  origin/fix/react-native-release-8-14-craft-config
  origin/main
  origin/revert-twine-verbose
refs/remotes/origin/main
error: unknown option `remotes'
usage: git show-ref [--head] [-d | --dereference]
                    [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]
                    [--heads] [--] [<pattern>...]
   or: git show-ref --verify [-q | --quiet] [-d | --dereference]
                    [-s | --hash[=<n>]] [--abbrev[=<n>]]
                    [--] [<ref>...]
   or: git show-ref --exclude-existing[=<pattern>]
   or: git show-ref --exists <ref>

    --[no-]tags           only show tags (can be ...
gateway · 2026-08-29 12:58:44
result
BASE=83d210b29553ea6f4d97508821724a834c43856e
M	.github/workflows/cocoapods-keepalive.yml
M	.github/workflows/publish.yml
M	README.md
A	docs/publish-issue-format.md
M	package.json
M	src/modules/__tests__/details-from-context.js
A	src/modules/__tests__/publish-workflow.js
M	src/modules/__tests__/update-issue.js
M	src/modules/details-from-context.js
M	src/modules/post-workflow-details.js
M	src/modules/update-issue.js
M	src/publish/inputs.js
M	yarn.lock
--- worktree diff ---
M	.github/workflows/publish.yml
M	docs/publish-issue-format.md
M	package.json
M	src/modules/__tests__/details-from-context.js
M	src/modules/__tests__/publish-workflow.js
M	src/modules/details-from-context.js
M	src/modules/post-workflow-details.js
M	src/publish/inputs.js
M	yarn.lock
--- staged diff ---
--- recent history ---
b658ffa (HEAD -> feat/workspace-acceptance, origin/feat/workspace-acceptance) fix: validate publish workspace JSON
33de3f1 feat: accept workspace publish requests
83d210b (origin/main, ...
gateway · 2026-08-29 12:59:01
result
 action.yml                              |  18 ++---
 docs/src/content/docs/targets/github.md |  36 ++++++++-
 src/__tests__/action.test.ts            | 125 +++++++++++++++++++++++++++---
 src/__tests__/config.test.ts            | 130 ++++++++++++++++++++++----------
 src/commands/__tests__/publish.test.ts  |  27 +++++++
 src/commands/publish.ts                 |  18 ++++-
 src/config.ts                           |  48 ++++++++----
 src/index.ts                            |   2 +
 src/schemas/project_config.ts           |  63 +++++++++++-----
 9 files changed, 370 insertions(+), 97 deletions(-)
diff --git a/action.yml b/action.yml
index 6344db2..7239ac1 100644
--- a/action.yml
+++ b/action.yml
@@ -74,10 +74,15 @@ runs:
     - name: Validate workspace
       shell: bash
       env:
+        PATH_INPUT: ${{ inputs.path }}
         WORKSPACE: ${{ inputs.workspace }}
       run: |
-        if [[ -n "$WORKSPACE" ]] && node -e ...
gateway · 2026-08-29 12:59:26
result
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/workspace_cmds/__tests__/list.test.ts
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/workspace_cmds/list.ts
gateway · 2026-08-29 13:00:27
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/workspace.ts</path>
<type>file</type>
<content>
1: import { Argv, CommandBuilder } from 'yargs';
2: 
3: import * as list from './workspace_cmds/list';
4: 
5: export const command = ['workspace <command>'];
6: export const description = 'Manage release workspaces';
7: 
8: export const builder: CommandBuilder = (yargs: Argv) =>
9:   yargs.demandCommand().command(list);
10: 
11: export const handler = (): void => {
12:   /* pass */
13: };

(End of file - total 13 lines)
</content>

<system-reminder>
Instructions from: /home/byk/Code/getsentry/craft-workspace-action-propagation/AGENTS.md
# AGENTS.md

This file provides guidance for AI coding assistants working with the Craft codebase.

## Package Management

- **Always use `pnpm`** for package management. Never use `npm` or `yarn`.
- Node.js version is managed by [Volta](https://volta.sh/) (currently v22.12.0). ...
gateway · 2026-08-29 13:00:54
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/workspace_cmds/list.ts</path>
<type>file</type>
<content>
1: import { getWorkspaceNames } from '../../config';
2: import { formatJson } from '../../utils/strings';
3: 
4: export const command = ['list'];
5: export const description = 'List defined release workspaces as a JSON array';
6: 
7: export function handler(): void {
8:   console.log(formatJson(getWorkspaceNames()));
9: }

(End of file - total 9 lines)
</content>
gateway · 2026-08-29 13:01:23
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/workspace_cmds/__tests__/list.test.ts</path>
<type>file</type>
<content>
1: import { describe, expect, test, vi } from 'vitest';
2: 
3: vi.mock('../../../config', () => ({
4:   getWorkspaceNames: vi.fn(),
5: }));
6: vi.mock('../../../utils/strings', () => ({
7:   formatJson: vi.fn(value => JSON.stringify(value)),
8: }));
9: 
10: import { getWorkspaceNames } from '../../../config';
11: import { handler } from '../list';
12: 
13: describe('workspace list command', () => {
14:   test('prints exact configured workspace names', () => {
15:     vi.mocked(getWorkspaceNames).mockReturnValue(['cli', 'mcp.v2']);
16:     const log = vi.spyOn(console, 'log').mockImplementation(() => {});
17: 
18:     handler();
19: 
20:     expect(log).toHaveBeenCalledWith('["cli","mcp.v2"]');
21:   });
22: 
23:   test('prints an empty array when no workspaces are configured', () => {
24:     ...
gateway · 2026-08-29 13:01:46
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/action.yml</path>
<type>file</type>
<content>
1: name: 'Craft Prepare Release'
2: description: 'Prepare a new release using Craft'
3: 
4: inputs:
5:   version:
6:     description: >
7:       Version to release. Can be a semver string (e.g., "1.2.3"),
8:       a bump type ("major", "minor", "patch"), or "auto" for automatic detection.
9:     required: false
10:   merge_target:
11:     description: Target branch to merge into. Uses the default branch as a fallback. ...
gateway · 2026-08-29 13:02:37
result
 .github/workflows/release.yml           |   9 ++
 action.yml                              |  33 ++++-
 docs/src/content/docs/targets/github.md |  36 ++++-
 src/__tests__/action.test.ts            | 249 ++++++++++++++++++++++++++++++++
 src/__tests__/config.test.ts            | 130 ++++++++++++-----
 src/commands/__tests__/publish.test.ts  |  74 ++++++++++
 src/commands/publish.ts                 |  51 ++++++-
 src/config.ts                           |  48 ++++--
 src/index.ts                            |   2 +
 src/schemas/project_config.ts           |  63 +++++---
 10 files changed, 613 insertions(+), 82 deletions(-)
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 4e6c11e..323e8ba 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -12,6 +12,9 @@ on:
       force:
         description: Force a release even when there are release-blockers (optional)
         required: false
+      workspace:
+        description: Named ...
gateway · 2026-08-29 13:03:48
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/publish.ts</path>
<type>file</type>
<content>
1: import { Arguments, Argv, CommandBuilder } from 'yargs';
2: import chalk from 'chalk';
3: import { existsSync, mkdirSync, readFileSync } from 'fs';
4: 
5: import { safeFs } from '../utils/dryRun';
6: import { dirname, join } from 'path';
7: import * as shellQuote from 'shell-quote';
8: import stringLength from 'string-length';
9: 
10: import {
11:   getConfiguration,
12:   getStatusProviderFromConfig,
13:   getArtifactProviderFromConfig,
14:   DEFAULT_RELEASE_BRANCH_NAME,
15:   getGlobalGitHubConfig,
16:   expandWorkspaceTargets,
17:   getNoMergeConfig,
18:   getActiveWorkspace,
19: } from '../config';
20: import { formatTable, logger } from '../logger';
21: import {
22:   type GitHubGlobalConfig,
23:   TargetConfig,
24: } from '../schemas/project_config';
25: import { getAllTargetNames, getTargetByName, SpecialTarget } from '../targets';
26: import { ...
gateway · 2026-08-29 13:03:52
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/publish.ts</path>
<type>file</type>
<content>
560:  *
561:  * The command is usually for bumping the development version on master or
562:  * cleanup tasks.
563:  *
564:  * @param newVersion Version being released
565:  * @param postReleaseCommand Custom post-release command
566:  */
567: export async function runPostReleaseCommand(
568:   newVersion: string,
569:   postReleaseCommand?: string,
570: ): Promise<boolean> {
571:   let sysCommand: shellQuote.ParseEntry;
572:   let args: shellQuote.ParseEntry[];
573:   if (postReleaseCommand !== undefined && postReleaseCommand.length === 0) {
574:     // Not running post-release command
575:     logger.debug('Not running the post-release command: no command specified');
576:     return false;
577:   } else if (postReleaseCommand) {
578:     [sysCommand, ...args] = shellQuote.parse(postReleaseCommand);
579:   } else if ...
gateway · 2026-08-29 13:03:55
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/config.ts</path>
<type>file</type>
<content>
1: import { existsSync, lstatSync, readFileSync } from 'fs';
2: import path from 'path';
3: 
4: import { load } from 'js-yaml';
5: import GitUrlParse from 'git-url-parse';
6: import { createGitClient } from './utils/git';
7: import { ZodError } from 'zod';
8: 
9: import { logger } from './logger';
10: import {
11:   CraftProjectConfig,
12:   CraftProjectConfigSchema,
13:   GitHubGlobalConfig,
14:   ArtifactProviderName,
15:   StatusProviderName,
16:   TargetConfig,
17:   ChangelogPolicy,
18:   VersioningPolicy,
19:   Workspace,
20: } from './schemas/project_config';
21: import { ConfigurationError } from './utils/errors';
22: import { isCompiledGitHubAction } from './utils/detection';
23: import {
24:   getPackageVersion,
25:   parseVersion,
26:   versionGreaterOrEqualThan,
27:   SemVer,
28: } from './utils/version';
29: // Note: We import getTargetByName lazily in ...
gateway · 2026-08-29 13:04:02
result
 .github/workflows/publish.yml                 | 59 ++++++++++++++-------------
 docs/publish-issue-format.md                  | 55 +++++++++++++++----------
 package.json                                  |  5 ++-
 src/modules/__tests__/details-from-context.js | 20 +++++++++
 src/modules/__tests__/publish-workflow.js     | 36 +++++++++++++++-
 src/modules/details-from-context.js           | 19 +++++----
 src/modules/post-workflow-details.js          |  2 +-
 src/publish/inputs.js                         |  2 +-
 yarn.lock                                     | 31 ++++++++++++++
 9 files changed, 167 insertions(+), 62 deletions(-)
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index c283b79..3158904 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -141,29 +141,12 @@ jobs:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
         run: node .__publish__/src/publish/post-workflow-details.js
 
-      # Setting the target repo ...
gateway · 2026-08-29 13:04:07
result
/home/byk/Code/getsentry/publish-workspace-acceptance/scripts/generate-publish-issue-title-parser.js
gateway · 2026-08-29 13:04:12
result
/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/release-revision.js
/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/publish-location.js
/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/publish-issue-title.js
/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/publish-issue-title.peggy
gateway · 2026-08-29 13:04:33
result
/home/byk/Code/getsentry/publish-workspace-acceptance/src/publish/resolve-release-revision.js
/home/byk/Code/getsentry/publish-workspace-acceptance/src/publish/resolve-location.js
/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/release-revision.js
/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/publish-location.js
/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/__tests__/release-revision.js
/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/__tests__/publish-location.js
gateway · 2026-08-29 13:05:06
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: // Canonical grammar for publish issue titles. A path suffix is syntactic only:
8: // the controller resolves a single segment as a workspace after checking out
9: // the CI-approved revision.
10: PublishIssueTitle
11:   = "publish: " "getsentry/"? repo:Repository path:Path? workspace:LegacyWorkspace? "@" version:Version !. {
12:       return {
13:         repo,
14:         path: path || "",
15:         version,
16:         ...(workspace ? { workspace } : {}),
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:   = ...
gateway · 2026-08-29 13:05:12
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-08-29 13:05:18
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 grammar = fs.readFileSync(grammarPath, "utf8");
21: const parser = peggy.generate(grammar, {
22:   format: "commonjs",
23:   grammarSource: "publish-issue-title.peggy",
24:   output: "source",
25: });
26: 
27: const generatedParser = prettier.format(`/* eslint-disable */\n${parser}`, {
28:   filepath: outputPath,
29: });
30: 
31: if (process.argv.includes("--check")) {
32:   const currentParser = fs.readFileSync(outputPath, "utf8");
33:   ...
gateway · 2026-08-29 13:05:22
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/release-revision.js</path>
<type>file</type>
<content>
1: const CHECK_RUNS_LINK =
2:   /^- \[View check runs\]\(https:\/\/github\.com\/getsentry\/(?<repo>[A-Za-z0-9_.-]+)\/commit\/(?<revision>[0-9a-f]{40})\/checks\/?\)\r?$/m;
3: 
4: function getReleaseRevision({ issueBody, repo }) {
5:   const match = issueBody.match(CHECK_RUNS_LINK);
6:   if (!match?.groups || match.groups.repo !== repo) {
7:     throw new Error(
8:       `Expected a View check runs link for getsentry/${repo} in the publish issue body.`
9:     );
10:   }
11: 
12:   return match.groups.revision;
13: }
14: 
15: module.exports = { getReleaseRevision };

(End of file - total 15 lines)
</content>
gateway · 2026-08-29 13:05:46
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/publish-location.js</path>
<type>file</type>
<content>
1: function needsWorkspaceDiscovery({ path, workspace }) {
2:   return !workspace && /^\.\/[A-Za-z0-9_.-]+$/.test(path);
3: }
4: 
5: function resolvePublishLocation({ path, workspace, workspaceNames }) {
6:   if (workspace) {
7:     if (path !== ".") {
8:       throw new Error("A publish workspace must use the repository root path.");
9:     }
10:     return { path, workspace };
11:   }
12: 
13:   if (!needsWorkspaceDiscovery({ path, workspace })) {
14:     return { path };
15:   }
16: 
17:   if (!workspaceNames.every(isWorkspaceName)) {
18:     throw new Error(
19:       "Craft workspace discovery returned an invalid workspace list."
20:     );
21:   }
22: 
23:   const segments = path.slice(2).split("/");
24:   if (
25:     path.startsWith("./") &&
26:     segments.length === 1 &&
27:     workspaceNames.includes(segments[0])
28:   ) {
29:     return { path: ...
gateway · 2026-08-29 13:06:20
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:   core.setOutput(
7:     "revision",
8:     getReleaseRevision({
9:       issueBody: process.env.PUBLISH_ISSUE_BODY || "",
10:       repo,
11:     })
12:   );
13: }
14: 
15: resolveReleaseRevision();

(End of file - total 15 lines)
</content>
gateway · 2026-08-29 13:06:23
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 {
3:   needsWorkspaceDiscovery,
4:   resolvePublishLocation,
5: } = require("../modules/publish-location");
6: 
7: function resolveLocation() {
8:   const input = JSON.parse(process.env.PUBLISH_ARGS || "");
9:   const workspaceNames = needsWorkspaceDiscovery(input)
10:     ? JSON.parse(process.env.CRAFT_WORKSPACE_NAMES || "")
11:     : [];
12: 
13:   if (!Array.isArray(workspaceNames)) {
14:     throw new Error(
15:       "Craft workspace discovery returned an invalid workspace list."
16:     );
17:   }
18: 
19:   core.setOutput(
20:     "result",
21:     resolvePublishLocation({
22:       path: input.path,
23:       workspace: input.workspace,
24:       workspaceNames,
25:     })
26:   );
27: }
28: 
29: resolveLocation();

(End of file - total 29 lines)
</content>
gateway · 2026-08-29 13:06:49
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 { getReleaseRevision } = require("../release-revision.js");
4: 
5: const REVISION = "7e5ca7ed5581552de066e2a8bc295b8306be38ac";
6: 
7: test("gets the CI-approved revision for the publish repository", () => {
8:   expect(
9:     getReleaseRevision({
10:       repo: "toolkit",
11:       issueBody: `Quick links:
12: - [View check runs](https://github.com/getsentry/toolkit/commit/${REVISION}/checks/)`,
13:     })
14:   ).toBe(REVISION);
15: });
16: 
17: test("gets the CI-approved revision from a CRLF issue body", () => {
18:   expect(
19:     getReleaseRevision({
20:       repo: "toolkit",
21:       issueBody: `Quick links:\r\n- [View check runs](https://github.com/getsentry/toolkit/commit/${REVISION}/checks/)\r\n`,
22:     })
23:   ).toBe(REVISION);
24: });
25: 
26: test("rejects a check-runs link ...
gateway · 2026-08-29 13:07:18
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 {
4:   needsWorkspaceDiscovery,
5:   resolvePublishLocation,
6: } = require("../publish-location.js");
7: 
8: test.each([
9:   [{ path: "./cli" }, true],
10:   [{ path: "./packages/cli" }, false],
11:   [{ path: "." }, false],
12:   [{ path: "./cli", workspace: "cli" }, false],
13: ])("workspace discovery is %s for %j", (input, expected) => {
14:   expect(needsWorkspaceDiscovery(input)).toBe(expected);
15: });
16: 
17: test("classifies an exact one-segment workspace without normalizing it", () => {
18:   expect(
19:     resolvePublishLocation({
20:       path: "./CLI",
21:       workspaceNames: ["cli", "CLI"],
22:     })
23:   ).toStrictEqual({ path: ".", workspace: "CLI" });
24: });
25: 
26: test("keeps a non-workspace suffix as a checkout path", () => {
27:   expect(
28:     ...
gateway · 2026-08-29 13:07:47
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: 
3: /**
4:  * Matches the entire "Targets" section of a github publish issue body.
5:  */
6: const TARGETS_SECTION_PARSER_REGEX =
7:   /^(?!### Targets$\s)(?: *- \[[ xX]\] \S+\s*$(?:\r?\n)?)+/m;
8: 
9: /**
10:  * Matches all targets of a github publish issue body in a section that was already matched and extracted with `TARGETS_PARSER_REGEX`.
11:  * The "id" of the targets is captured within a capture group.
12:  */
13: const TARGETS_PARSER_REGEX = /^\s*- \[[ x]\] (\S+)/gim;
14: 
15: /**
16:  * Matches checked 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-08-29 13:09:24
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-08-29 13:09:58
result
 .github/workflows/cocoapods-keepalive.yml     |   2 +-
 .github/workflows/publish.yml                 |  82 +++++----
 README.md                                     |   5 +
 docs/publish-issue-format.md                  |  92 ++++++++++
 package.json                                  |   5 +-
 src/modules/__tests__/details-from-context.js | 250 +++++++++++++++++++++++++-
 src/modules/__tests__/publish-workflow.js     | 114 ++++++++++++
 src/modules/__tests__/update-issue.js         |  15 +-
 src/modules/details-from-context.js           |  40 ++++-
 src/modules/post-workflow-details.js          |   2 +-
 src/modules/update-issue.js                   |  54 +++---
 src/publish/inputs.js                         |   2 +-
 yarn.lock                                     |  31 ++++
 13 files changed, 623 insertions(+), 71 deletions(-)
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index cff6bc3..3158904 100644
--- a/.github/workflows/publish.yml
+++ ...
gateway · 2026-08-29 13:10:51
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/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-08-29 13:12:47
result
No files found
gateway · 2026-08-29 13:12:54
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/.github</path>
<type>directory</type>
<entries>
CODEOWNERS
workflows/

(2 entries)
</entries>
gateway · 2026-08-29 13:13:00
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/.github/workflows</path>
<type>directory</type>
<entries>
auto-approve.yml
ci-poller.yml
cocoapods-keepalive.yml
publish.yml
test.yml

(5 entries)
</entries>
gateway · 2026-08-29 13:13:10
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/.github/workflows/test.yml</path>
<type>file</type>
<content>
1: name: Test
2: on:
3:   push:
4:     branches:
5:       - main
6:   pull_request:
7: 
8: permissions:
9:   contents: read
10: 
11: defaults:
12:   run:
13:     shell: bash
14: jobs:
15:   unit-test:
16:     runs-on: ubuntu-latest
17:     name: unit tests
18:     steps:
19:       - uses: actions/checkout@v6
20:       - uses: actions/setup-node@v6
21:         with:
22:           node-version: 24
23:       - uses: actions/cache@v4
24:         id: cache
25:         with:
26:           path: node_modules
27:           key: ${{ runner.os }}-node_modules-${{ hashFiles('package.json', 'yarn.lock') }}
28: 
29:       - name: Install Dependencies
30:         if: steps.cache.outputs.cache-hit != 'true'
31:         run: yarn install --frozen-lockfile
32: 
33:       - name: Test
34:         run: yarn test

(End of file - total 34 lines)
</content>
gateway · 2026-08-29 13:13:27
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-08-29 13:13:40
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', result);
10: }
11: 
12: 
13: inputs();

(End of file - total 13 lines)
</content>
gateway · 2026-08-29 13:14:25
result
Usage: realpath [OPTION]... FILE...
Print the resolved absolute file name;
all but the last component must exist

  -e, --canonicalize-existing  all components of the path must exist
  -m, --canonicalize-missing   no path components need exist or be a directory
  -L, --logical                resolve '..' components before symlinks
  -P, --physical               resolve symlinks as encountered (default)
  -q, --quiet                  suppress most error messages
      --relative-to=DIR        print the resolved path relative to DIR
      --relative-base=DIR      print absolute paths unless paths below DIR
  -s, --strip, --no-symlinks   don't expand symlinks
  -z, --zero                   end each output line with NUL, not newline
      --help        display this help and exit
      --version     output version information and exit

GNU coreutils online help: <https://www.gnu.org/software/coreutils/>
Full documentation <https://www.gnu.org/software/coreutils/realpath>
or available ...
gateway · 2026-08-29 13:14:46
result
diff --git a/.github/workflows/cocoapods-keepalive.yml b/.github/workflows/cocoapods-keepalive.yml
index 21c0696..9c11b0e 100644
--- a/.github/workflows/cocoapods-keepalive.yml
+++ b/.github/workflows/cocoapods-keepalive.yml
@@ -1,4 +1,4 @@
-name: "CocoaPods Token Keep-Alive"
+name: CocoaPods Token Keep-Alive
 
 on:
   schedule:
--- committed files per commit ---
33de3f1 feat: accept workspace publish requests
 .github/workflows/publish.yml                 |  27 +++-
 src/modules/__tests__/details-from-context.js | 212 +++++++++++++++++++++++++-
 src/modules/__tests__/publish-workflow.js     |  82 ++++++++++
 src/modules/__tests__/update-issue.js         |  15 +-
 src/modules/details-from-context.js           |  27 +++-
 src/modules/update-issue.js                   |  54 ++++---
 6 files changed, 377 insertions(+), 40 deletions(-)
b658ffa fix: validate publish workspace JSON
 .github/workflows/cocoapods-keepalive.yml     |  2 +-
 README.md                                     |  5 ++
 ...
gateway · 2026-08-29 13:14:59
result
> @sentry/craft@2.31.0-dev.0 test /home/byk/Code/getsentry/craft-workspace-action-propagation
> vitest run


 RUN  v4.1.8 /home/byk/Code/getsentry/craft-workspace-action-propagation

[info] [[target/github]] No previous release found
[info] [[target/github]] Deleted orphaned draft release: v1.0.0
[info] [[target/github]] No previous release found
[warn] [[target/github]] Failed to clean up release "v1.0.0": Error: Cleanup failed
[info] [[target/github]] No previous release found
[info] [[target/github]] No previous release found
[info] [[target/github]] Release for tag "1.0.0" already exists and is published. Skipping GitHub release creation (likely from a previous run).
[info] [[target/github]] No previous release found
[info] [[target/github]] createRelease returned 422 for tag "1.0.0". Looking for a leftover draft release to clean up...
[info] [[target/github]] Deleting leftover draft release (id=789) for tag "1.0.0"... ...
gateway · 2026-08-29 13:15:42
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  8 passed (8)
      Tests  45 passed (45)
   Start at  13:15:43
   Duration  1.33s (transform 246ms, setup 0ms, import 1.02s, tests 300ms, environment 3ms)

Done in 2.84s.
gateway · 2026-08-29 13:15:51
result
 M action.yml
 M docs/src/content/docs/targets/github.md
 M src/__tests__/action.test.ts
 M src/__tests__/config.test.ts
 M src/commands/__tests__/publish.test.ts
 M src/commands/publish.ts
 M src/config.ts
 M src/index.ts
 M src/schemas/project_config.ts
?? src/commands/workspace.ts
?? src/commands/workspace_cmds/
8	10	action.yml
32	4	docs/src/content/docs/targets/github.md
115	10	src/__tests__/action.test.ts
92	38	src/__tests__/config.test.ts
27	0	src/commands/__tests__/publish.test.ts
15	3	src/commands/publish.ts
35	13	src/config.ts
2	0	src/index.ts
44	19	src/schemas/project_config.ts
src/commands/workspace.ts
src/commands/workspace_cmds/__tests__/list.test.ts
src/commands/workspace_cmds/list.ts
gateway · 2026-08-29 13:15:59
result
 M .github/workflows/publish.yml
 M docs/publish-issue-format.md
 M package.json
 M src/modules/__tests__/details-from-context.js
 M src/modules/__tests__/publish-workflow.js
 M src/modules/details-from-context.js
 M src/modules/post-workflow-details.js
 M src/publish/inputs.js
 M yarn.lock
?? pnpm-lock.yaml
?? scripts/
?? src/modules/__tests__/publish-location.js
?? src/modules/__tests__/release-revision.js
?? src/modules/publish-issue-title.js
?? src/modules/publish-issue-title.peggy
?? src/modules/publish-location.js
?? src/modules/release-revision.js
?? src/publish/resolve-location.js
?? ...
gateway · 2026-08-29 13:16:11
result
Found 2 matches
/home/byk/Code/getsentry/publish-workspace-acceptance/node_modules/@actions/core/lib/core.js:
  Line 52: exports.setOutput = setOutput;

  Line 215: function setOutput(name, value) {
gateway · 2026-08-29 13:16:17
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/node_modules/@actions/core/lib/core.js</path>
<type>file</type>
<content>
200:     const val = getInput(name, options);
201:     if (trueValue.includes(val))
202:         return true;
203:     if (falseValue.includes(val))
204:         return false;
205:     throw new TypeError(`Input does not meet YAML 1.2 "Core Schema" specification: ${name}\n` +
206:         `Support boolean input list: \`true | True | TRUE | false | False | FALSE\``);
207: }
208: /**
209:  * Sets the value of an output.
210:  *
211:  * @param     name     name of the output to set
212:  * @param     value    value to store. Non-string values will be converted to a string via JSON.stringify
213:  */
214: // eslint-disable-next-line @typescript-eslint/no-explicit-any
215: function setOutput(name, value) {
216:     const filePath = process.env['GITHUB_OUTPUT'] || '';
217:     if (filePath) {
218:         return (0, file_command_1.issueFileCommand)('OUTPUT', ...
gateway · 2026-08-29 13:16:42
result
Found 100 matches (more matches available)
/home/byk/Code/getsentry/craft-workspace-action-propagation/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 = ...
gateway · 2026-08-29 13:16:53
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/helpers.ts</path>
<type>file</type>
<content>
1: import { appendFileSync, mkdirSync, writeFileSync } from 'fs';
2: import path from 'path';
3: import { parseArgs } from 'node:util';
4: 
5: import prompts from 'prompts';
6: import { logger, LogLevel, setLevel } from '../logger';
7: 
8: /**
9:  * Maximum size (in bytes) for step output values passed through GITHUB_OUTPUT.
10:  * Values exceeding this are truncated to avoid E2BIG errors when GitHub Actions
11:  * expands them into environment variables for subsequent steps.
12:  *
13:  * 64 KB is well under the ~2 MB ARG_MAX kernel limit and also under GitHub's
14:  * ~65 536-character issue body limit, so truncated changelogs still render. ...
gateway · 2026-08-29 13:17:07
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/schemas/project_config.ts</path>
<type>file</type>
<content>
150:   calver: CalVerConfigSchema.optional(),
151: });
152: 
153: export type VersioningConfig = z.infer<typeof VersioningConfigSchema>;
154: 
155: /**
156:  * Changelog configuration
157:  */
158: export const ChangelogConfigSchema = z.union([
159:   z.string(),
160:   z.object({
161:     filePath: z.string().optional(),
162:     policy: z.enum(['auto', 'simple', 'none']).optional(),
163:     scopeGrouping: z.boolean().optional(),
164:   }),
165: ]);
166: 
167: /**
168:  * Fields that describe how a single release unit is built and published.
169:  *
170:  * These are shared between the top-level config (the implicit/default release
171:  * unit) and each entry under the top-level `workspaces` map (an explicit,
172:  * independently-versioned release unit). A workspace inherits the top-level
173:  * values as defaults and overrides the fields it declares. ...
gateway · 2026-08-29 13:17:58
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-08-29 13:18:42
result
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
gateway · 2026-08-29 13:18:59
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/pnpm-lock.yaml</path>
<type>file</type>
<content>
1: lockfileVersion: '9.0'
2: 
3: settings:
4:   autoInstallPeers: true
5:   excludeLinksFromLockfile: false
6: 
7: overrides:
8:   undici: ^6.23.0
9: 
10: importers:
11: 
12:   .:
13:     dependencies:
14:       '@actions/core':
15:         specifier: ^2.0.0
16:         version: 2.0.3
17:       '@actions/github':
18:         specifier: ^7.0.0
19:         version: 7.0.0
20:       '@sentry/node':
21:         specifier: ^10.0.0
22:         version: 10.71.0(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))
23:     devDependencies:
24:       eslint:
25:         specifier: ^8.9.0
26:         version: 8.57.1
27:       eslint-config-prettier:
28:         specifier: ^8.3.0
29:         version: 8.10.2(eslint@8.57.1)
30:       eslint-plugin-yml:
31:         specifier: ^0.13.0
32:         version: 0.13.0(eslint@8.57.1)
33:       peggy:
34:         specifier: 5.1.0
35:         version: ...
gateway · 2026-08-29 13:19:24
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: `. This EBNF is canonical:
10: 
11: ```text
12: title             = "publish: ", [ "getsentry/" ], repository, [ path ],
13:                     [ legacy-workspace ], "@", version ;
14: repository        = token, { token } ;
15: path              = "/", path-segment, { "/", path-segment } ;
16: path-segment      = token, { token } ;
17: legacy-workspace  = " [workspace: ", json-string, "] " ;
18: version           = version-character, { version-character } ;
19: token             = ? ASCII letter, digit, ".", "_", or "-" ? ...
gateway · 2026-08-29 13:19:57
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-08-29 13:20:19
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/__tests__/config.test.ts</path>
<type>file</type>
<content>
120:     const data = { noMerge: false };
121:     expect(validateConfiguration(data)).toEqual(data);
122:   });
123: 
124:   test('noMerge defaults to undefined when not specified', () => {
125:     const data = { github: { owner: 'getsentry', repo: 'craft' } };
126:     const result = validateConfiguration(data);
127:     expect(result.noMerge).toBeUndefined();
128:   });
129: 
130:   test('fails with invalid noMerge type', () => {
131:     expect(() => validateConfiguration({ noMerge: 'yes' })).toThrow(/noMerge/);
132:   });
133: 
134:   test('parses configuration with workspaces', () => {
135:     const data = {
136:       minVersion: '2.27.0',
137:       github: { owner: 'getsentry', repo: 'toolkit' },
138:       workspaces: {
139:         cli: {
140:           releaseBranchPrefix: 'release/cli',
141:           targets: [{ name: 'github', tagPrefix: ...
gateway · 2026-08-29 13:20:25
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/__tests__/action.test.ts</path>
<type>file</type>
<content>
150: test('passes option-looking workspace names inline to Craft', () => {
151:   const environment = createActionEnvironment();
152:   const workspace = '--config-from=untrusted';
153: 
154:   expect(runActionStep('Craft Prepare', workspace, environment).status).toBe(0);
155:   expect(
156:     runActionStep('Read Craft Targets', workspace, environment).status,
157:   ).toBe(0);
158: 
159:   expect(readFileSync(environment.craftCalls, 'utf8')).toBe(
160:     'prepare --workspace=--config-from=untrusted\ntargets --workspace=--config-from=untrusted\n',
161:   );
162: });
163: 
164: test('forwards workspace input to every Craft command', () => {
165:   expect(getActionStep('Validate workspace').env?.PATH_INPUT).toBe(
166:     '${{ inputs.path }}',
167:   );
168:   expect(getActionStep('Craft Prepare').env?.WORKSPACE).toBe(
169:     '${{ inputs.workspace ...
gateway · 2026-08-29 13:20:36
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/.github/workflows/cocoapods-keepalive.yml</path>
<type>file</type>
<content>
1: name: CocoaPods Token Keep-Alive
2: 
3: on:
4:   schedule:
5:     # CocoaPods sessions currently expire after 3 days of inactivity (VALIDITY_LENGTH)
6:     # This runs every day at midnight UTC to keep the current token alive
7:     # See: https://github.com/CocoaPods/trunk.cocoapods.org/blob/a1869790e9ae2229b6985b6af4532da814202558/app/models/session.rb#L9
8:     - cron: "0 0 * * *" # Runs every day at midnight UTC
9:   workflow_dispatch:
10: 
11: permissions:
12:   contents: read
13: 
14: jobs:
15:   keepalive:
16:     name: Refresh CocoaPods Session
17:     runs-on: macos-15
18:     environment: production
19:     env:
20:       COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }}
21: 
22:     steps:
23:       - name: Install CocoaPods
24:         run: gem install cocoapods
25:       - name: Print CocoaPods Version
26:         run: pod ...
gateway · 2026-08-29 13:21:29

Reviewed both full branch ranges and current dirty trees read-only.

CRITICAL

  1. A decoy same-repo check-runs link can select a different revision.
    getReleaseRevision() accepts the first matching link anywhere in the issue body, not the canonical Quick links entry: publish/src/modules/release-revision.js:1-12. That output drives both checkout and craft publish --rev: publish/.github/workflows/publish.yml:164-167,247-249.

    Read-only reproduction: an earlier getsentry/toolkit decoy returned its aaaaaaaa… SHA instead of the later Quick-links SHA. Repository equality is enforced, but a different CI-green commit in the same repository can still be published.

    The CI poller has the same broad extraction flaw: publish/.github/workflows/ci-poller.yml:107-110. Tests cover missing, wrong-repo, and uppercase links, but not decoys or duplicates: publish/src/modules/__tests__/release-revision.js:26-49.

    Fix: Parse exactly one check-runs link from the canonical Quick links block; reject missing, duplicate, or out-of-block links. Apply the same rule in the CI poller. Update docs: docs/publish-issue-format.md:91-92 incorrectly calls this now-security-critical link “informational.”

MAJOR

  1. Craft accepts the workspace name .., but Publish rejects its compact title.
    Craft permits .. through WorkspaceNameSchema: craft/src/schemas/project_config.ts:218-225, and action validation accepts it: craft/action.yml:84. Craft then creates …/..@version: craft/action.yml:268. Publish converts that to ./.. and rejects it as traversal: publish/src/modules/details-from-context.js:53-55.

    This makes a configured workspace unreleasable after craft prepare has already run. Neither config nor action tests cover ./..: craft/src/__tests__/config.test.ts:163-178, craft/src/__tests__/action.test.ts:207-234.

    Fix: Reject . and .. as workspace names consistently in Craft’s schema, action validation, and ...

gpt-5.6-terra · 2026-08-29 13:21:29 · ~1.1K tokens

Distillations (12)

Gen 0 2026-08-29 13:24:13 · 2128 tokens

Date: Aug 29, 2026 * 🟡 (13:20) `publish-workspace-acceptance/package.json` specifies package `publish` version `0.0.1`, private, Apache-2.0, repository `git@github.com:getsentry/publish.git`; Volta pins Node `24.0.0` and Yarn `1.22.22`; `undici` resolution is `^6.23.0`. Scripts: `generate` runs `node scripts/generate-publish-issue-title-parser.js`; `check:generated` adds `--check`; `test` runs `…

Gen 0 2026-08-29 13:22:51 · 756 tokens

Date: Aug 29, 2026 * 🔴 (13:19) User asserted that new Craft publish requests always include the checkout repository identity. * 🔴 (13:19) User asserted that the controller never normalizes names; workspace names preserve exact spelling. * 🟡 (13:19) `docs/publish-issue-format.md` defines canonical publish-title EBNF: `title = "publish: ", [ "getsentry/" ], repository, [ path ], [ legacy-workspa…

Gen 0 2026-08-29 13:22:34 · 2927 tokens

Date: Aug 29, 2026 * 🟡 (13:14) Inspected repository history in `/home/byk/Code/getsentry/publish-workspace-acceptance`: commit `33de3f1` (`feat: accept workspace publish requests`) changed `.github/workflows/publish.yml`, `src/modules/details-from-context.js`, `src/modules/update-issue.js`, and related tests (377 insertions, 40 deletions); commit `b658ffa` (`fix: validate publish workspace JSON`…

Gen 0 2026-08-29 13:17:56 · 1102 tokens

Date: Aug 29, 2026 * 🟡 (13:12) Inspected `/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/publishState.ts`: module moves Craft publish-state files out of the published repository checkout to `$XDG_STATE_HOME/craft/`, falling back to `$HOME/.local/state/craft/`, preventing committed `.craft-publish-<version>.json` files or earlier CI steps from pre-populating completed targe…

Gen 0 2026-08-29 13:17:23 · 2429 tokens

Date: Aug 29, 2026 * 🟡 (13:07) Tool output inspected `/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/__tests__/publish-location.js`: `needsWorkspaceDiscovery()` is tested true only for `{ path: "./cli" }`, and false for `{ path: "./packages/cli" }`, `{ path: "." }`, and `{ path: "./cli", workspace: "cli" }`. * 🟡 (13:07) `publish-location.js` tests require exact, case-preservi…

Gen 0 2026-08-29 13:13:46 · 1388 tokens

Date: Aug 29, 2026 * 🟡 (13:05) Tool output enumerated publish-workspace-acceptance files: `/home/byk/Code/getsentry/publish-workspace-acceptance/src/publish/resolve-release-revision.js`, `/home/byk/Code/getsentry/publish-workspace-acceptance/src/publish/resolve-location.js`, `/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/release-revision.js`, `/home/byk/Code/getsentry/publish…

Gen 0 2026-08-29 13:13:19 · 130 tokens

Date: Aug 29, 2026 * 🟡 (13:04) Tool output identified publish-workspace-acceptance module files: `/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/release-revision.js`, `/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/publish-location.js`, `/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/publish-issue-title.js`, and `/home/byk/Code/getsentry/p…

Gen 0 2026-08-29 13:13:14 · 1526 tokens

Date: Aug 29, 2026 * 🔴 (13:04) User specified that workspace min-version compatibility relaxation applies only to the CURRENT Craft version side, never the configured `minVersion` side. * 🔴 (13:04) User specified that new Craft publish requests always include the checkout repository identity. * 🔴 (13:04) User specified that the publish controller never normalizes workspace names; it preserves …

Gen 0 2026-08-29 13:10:38 · 1648 tokens

Date: Aug 29, 2026 * 🟡 (13:03) Workspace propagation change modifies 10 files with 613 insertions and 82 deletions: `.github/workflows/release.yml`, `action.yml`, `docs/src/content/docs/targets/github.md`, `src/__tests__/action.test.ts`, `src/__tests__/config.test.ts`, `src/commands/__tests__/publish.test.ts`, `src/commands/publish.ts`, `src/config.ts`, `src/index.ts`, and `src/schemas/project_c…

Gen 0 2026-08-29 13:07:14 · 1207 tokens

Date: Aug 29, 2026 * 🟡 (13:02) Craft `action.yml` defines composite GitHub Action `Craft Prepare Release` with inputs `version`, `merge_target`, `force` (default `'false'`), `blocker_label` (default `'release-blocker'`), `publish_repo`, `git_user_name`, `git_user_email`, `path` (default `'.'`), `workspace`, `craft_config_from_merge_target` (default `'false'`), and `craft_version` (default `''`);…

Gen 0 2026-08-29 13:05:48 · 1823 tokens

Date: Aug 29, 2026 * 🔴 (12:57) User stated Publish repository error-handling convention: always throw for unexpected values, including a `default` switch case such as `throw new Error(\`Unknown status: '${status}'\`)`. * 🟡 (12:57) Publish worktree `AGENTS.md` describes the Sentry Publish repository as a GitHub Actions approval system for releases across Sentry projects using Craft; it is a Comm…

Gen 0 2026-08-29 13:03:12 · 1340 tokens

Date: Aug 29, 2026 * 🔴 [requested-review] (12:48) User requested a rigorous, read-only final adversarial review of uncommitted compact workspace publish-contract changes in both worktrees: `/home/byk/Code/getsentry/craft-workspace-action-propagation` on branch `feat/workspace-action-propagation`, and `/home/byk/Code/getsentry/publish-workspace-acceptance` on branch `feat/workspace-acceptance`. U…