DashboardcraftSession 12TZZpomX65u

Session 12TZZpomX65u

Full ID: 12TZZpomX65uZ0sdH
Messages: 56
Distillations: 6
Time range: 2026-08-25 14:30:15 — 2026-08-25 15:40:38

Conversation (56 messages)

Perform a read-only adversarial correctness/security review of the uncommitted changes in /home/byk/Code/getsentry/craft-workspace-action-propagation on feat/workspace-action-propagation, compared with origin/master. Do NOT modify files. Scope: action.yml and .github/workflows/release.yml workspace propagation. Verify exact backward compatibility without a workspace; action input and manual/reusable forwarding; shell quoting/injection; title grammar compatibility with publish controller: legacy publish: getsentry/repo/path@version, workspace publish: getsentry/repo/path [workspace: <JSON string>] @version; targets and prepare selection; workspace newline/unsafe behavior must fail before prepare side effects; dry run; issues/state identity collision. Inspect relevant source/test context too. Report findings first by severity with exact file:line evidence, PASS/FAIL per concern, testing gaps, and a clear MERGE/DO-NOT-MERGE verdict. No rubber stamp.

gateway · 2026-08-25 14:30:15
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
.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

(32 entries)
</entries>
gateway · 2026-08-25 14:31:39
result
 M .github/workflows/release.yml
 M action.yml
feat/workspace-action-propagation
21270a1d12a60b461c51fba93bd076b90d9cf4af
 .github/workflows/release.yml |  9 +++++++++
 action.yml                    | 30 +++++++++++++++++++++++++++---
 2 files changed, 36 insertions(+), 3 deletions(-)
 .github/workflows/release.yml |  9 +++++++++
 action.yml                    | 30 +++++++++++++++++++++++++++---
 2 files changed, 36 insertions(+), 3 deletions(-)
gateway · 2026-08-25 14:32:36
result
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
@@ -1,142 +1,151 @@
 name: Release
 concurrency: ${{ github.workflow }}-${{ github.ref }}
 
 on:
   # For Craft's own releases (dogfooding)
   workflow_dispatch:
     inputs:
       version:
         description: Version to release
         required: true
         default: 'auto'
       force:
         description: Force a release even when there are release-blockers (optional)
         required: false
+      workspace:
+        description: Named Craft release workspace to prepare and publish
+        required: false
 
   # For external repos to call this workflow
   workflow_call:
     inputs:
       version:
         description: Version to release (semver, bump type, or "auto")
         type: string
         required: false
       force:
         description: Force a release even when there are ...
gateway · 2026-08-25 14:33:30
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-25 14:34:20
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-25 14:35:48
result
Found 100 matches (more matches available)
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/publishState.ts:
  Line 82: export function getPublishStateFilename(

  Line 119:     getPublishStateFilename(version, githubConfig, cwd, workspace),


/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/helpers.ts:
  Line 25:  * Extracts the `--workspace` selection from the raw argv (or the

  Line 33:  * Supports `--workspace foo` and `--workspace=foo`. It uses Node's built-in

  Line 35:  * `--workspace --dry-run`) is never mistaken for a workspace name. The CLI

  Line 42: export function extractWorkspaceSelection(

  Line 65:       // `--workspace=-foo` is a valid inline value; `--workspace --foo` is not.


/home/byk/Code/getsentry/craft-workspace-action-propagation/src/schemas/project_config.ts:
  Line 227:    * When present, a release run must select one via `--workspace <name>` ...
gateway · 2026-08-25 14:36:01
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/index.ts</path>
<type>file</type>
<content>
1: #!/usr/bin/env node
2: // Import Sentry instrumentation first, before any other code
3: import './instrument';
4: 
5: import isCI from 'is-ci';
6: import yargs from 'yargs';
7: 
8: import { logger, LogLevel } from './logger';
9: import {
10:   sanitizeDynamicLinkerEnv,
11:   warnIfCraftEnvFileExists,
12: } from './utils/env';
13: import {
14:   envToBool,
15:   setGlobals,
16:   extractWorkspaceSelection,
17: } from './utils/helpers';
18: import { getPackageVersion } from './utils/version';
19: import { withTracing } from './utils/tracing';
20: import { setActiveWorkspace } from './config';
21: 
22: // Commands
23: import * as prepare from './commands/prepare';
24: import * as publish from './commands/publish';
25: import * as targets from './commands/targets';
26: import * as config from './commands/config';
27: import * as artifacts from './commands/artifacts';
28: ...
gateway · 2026-08-25 14:37:37
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-25 14:38:25
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-25 14:39:29
result
<path>/home/byk/Code/getsentry</path>
<type>directory</type>
<entries>
cli-api-1423/
cli-wt/
cli/
codecov-action/
craft-vercel-prebuilt-output/
craft-vercel-project-config/
craft-workspace-action-propagation/
craft-workspaces-schema/
craft/
devinfra-coder-infra/
devinfra-deployment-service/
getsentry/
ops/
package_grant_publish_repo/
publish-workspace-acceptance/
publish/
security-as-code/
self-hosted/
sentry-api-schema/
sentry-docs/
sentry-forge/
sentry-go/
sentry-javascript/
sentry-orbital/
sentry-python/
sentry-release-registry/
sentry-wizard/
sentry/
snuba/
spotlight/
sqlish/
static-sites/
stripe-app/
symbolic/

(34 entries)
</entries>
gateway · 2026-08-25 14:40:24
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
src/
vitest.config.js
yarn.lock

(14 entries)
</entries>
gateway · 2026-08-25 14:41:30
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-25 14:42:33
result
 M .github/workflows/publish.yml
 M src/modules/__tests__/details-from-context.js
 M src/modules/details-from-context.js
feat/workspace-acceptance
83d210b build(deps): bump js-yaml from 4.3.0 to 4.3.1 (#9174)
 .github/workflows/publish.yml                 |  9 ++-
 src/modules/__tests__/details-from-context.js | 97 ++++++++++++++++++++++++++-
 src/modules/details-from-context.js           | 24 +++++--
 3 files changed, 123 insertions(+), 7 deletions(-)
gateway · 2026-08-25 14:44:12
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/details-from-context.js</path>
<type>file</type>
<content>
1: /**
2:  * Matches the entire "Targets" section of a github publish issue body.
3:  */
4: const TARGETS_SECTION_PARSER_REGEX =
5:   /^(?!### Targets$\s)(?: *- \[[ xX]\] \S+\s*$(?:\r?\n)?)+/m;
6: 
7: /**
8:  * Matches all targets of a github publish issue body in a section that was already matched and extracted with `TARGETS_PARSER_REGEX`.
9:  * The "id" of the targets is captured within a capture group.
10:  */
11: const TARGETS_PARSER_REGEX = /^\s*- \[[ x]\] (\S+)/gim;
12: 
13: /**
14:  * Matches checked targets of a github publish issue body in a section that was already matched and extracted with `TARGETS_PARSER_REGEX`.
15:  * The "id" of the targets is captured within a capture group.
16:  */
17: const CHECKED_TARGETS_PARSER_REGEX = /^\s*- \[x\] (\S+)/gim;
18: 
19: async function detailsFromContext({ context }) {
20:   if (!context || ...
gateway · 2026-08-25 14:45:17
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-25 14:46:56
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-25 14:47:59
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: `
13: Requested by: @BYK
14: 
15: Merge target: custom-branch
16: 
17: Quick links:
18: - [View changes](https://github.com/getsentry/sentry/compare/21.3.0...refs/heads/releases/21.3.1)
19: - [View check runs](https://github.com/getsentry/sentry/commit/7e5ca7ed5581552de066e2a8bc295b8306be38ac/checks/)
20: 
21: Assign the **accepted** label to this issue to approve the release. ...
gateway · 2026-08-25 14:49:30
result
Found 100 matches (more matches available)
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/system.ts:
  Line 109:   /** Force the process to run in dry-run mode */

  Line 145:     logger.info('[dry-run] Not spawning process:', `${command} ${argsString}`);


/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/registry.ts:
  Line 100:       // reportError throws in non-dry-run mode, but TypeScript doesn't know that


/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/helpers.ts:
  Line 35:  * `--workspace --dry-run`) is never mistaken for a workspace name. The CLI

  Line 76:   'dry-run'?: boolean;

  Line 83:   'dry-run': boolean;

  Line 89:   'dry-run': false,

  Line 95:   if (argv['dry-run'] !== undefined) {

  Line 96:     GLOBAL_FLAGS['dry-run'] = argv['dry-run'];

  Line 110:   return GLOBAL_FLAGS['dry-run'];


/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/githubApi.ts:
  Line 118:     // Wrap ...
gateway · 2026-08-25 14:50:25
result
No files found
gateway · 2026-08-25 14:51:14
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/schemas/project_config.ts</path>
<type>file</type>
<content>
180: const releaseUnitFields = {
181:   github: GitHubGlobalConfigSchema.optional(),
182:   targets: z.array(TargetConfigSchema).optional(),
183:   preReleaseCommand: z.string().optional(),
184:   postReleaseCommand: z.string().optional(),
185:   releaseBranchPrefix: z.string().optional(),
186:   changelog: ChangelogConfigSchema.optional(),
187:   changelogPolicy: z.enum(['auto', 'simple', 'none']).optional(),
188:   requireNames: z.array(z.string()).optional(),
189:   statusProvider: BaseStatusProviderSchema.optional(),
190:   artifactProvider: BaseArtifactProviderSchema.optional(),
191:   versioning: VersioningConfigSchema.optional(),
192:   /**
193:    * Do not merge the release branch after publishing.
194:    * Defaults to true for compiled GitHub Actions (Node.js actions with dist/ folder).
195:    */
196:   noMerge: z.boolean().optional(),
197: } as ...
gateway · 2026-08-25 14:52:11
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/prepare.ts</path>
<type>file</type>
<content>
800:     logger.warn(
801:       `Loading .craft.yml from remote branch "${argv.configFrom}". ` +
802:         `Its preReleaseCommand will be executed by Craft — ensure the ` +
803:         `branch is trusted.`,
804:     );
805:     try {
806:       await git.fetch([argv.remote, argv.configFrom]);
807:       const configContent = await git.show([
808:         `${argv.remote}/${argv.configFrom}:${CONFIG_FILE_NAME}`,
809:       ]);
810:       loadConfigurationFromString(configContent);
811:     } catch (error: any) {
812:       throw new ConfigurationError(
813:         `Failed to load ${CONFIG_FILE_NAME} from branch "${argv.configFrom}": ${error.message}`,
814:       );
815:     }
816:   }
817: 
818:   // Get repo configuration
819:   const config = getConfiguration();
820:   const githubConfig = await getGlobalGitHubConfig();
821: 
822:   const defaultBranch = ...
gateway · 2026-08-25 14:53:19
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/prepare.ts</path>
<type>file</type>
<content>
1: import { existsSync, promises as fsPromises } from 'fs';
2: import { join, relative } from 'path';
3: 
4: import * as shellQuote from 'shell-quote';
5: import type { SimpleGit, StatusResult } from 'simple-git';
6: import type { Arguments, Argv, CommandBuilder } from 'yargs';
7: import { createDryRunIsolation, safeFs } from '../utils/dryRun';
8: 
9: import {
10:   CONFIG_FILE_NAME,
11:   DEFAULT_RELEASE_BRANCH_NAME,
12:   getConfigFileDir,
13:   getConfiguration,
14:   getGitTagPrefix,
15:   getGlobalGitHubConfig,
16:   getVersioningPolicy,
17:   loadConfigurationFromString,
18:   requiresMinVersion,
19: } from '../config';
20: import { logger } from '../logger';
21: import {
22:   ChangelogPolicy,
23:   type TargetConfig,
24:   VersioningPolicy,
25: } from '../schemas/project_config';
26: import { sleep } from '../utils/async';
27: import {
28:   type ...
gateway · 2026-08-25 14:54:57
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/targets.ts</path>
<type>file</type>
<content>
1: import { getConfiguration, expandWorkspaceTargets } from '../config';
2: import { formatJson } from '../utils/strings';
3: import { getAllTargetNames } from '../targets';
4: import { BaseTarget } from '../targets/base';
5: 
6: export const command = ['targets'];
7: export const description = 'List defined targets as JSON array';
8: 
9: export async function handler(): Promise<any> {
10:   let definedTargets = getConfiguration().targets || [];
11: 
12:   // Expand workspace targets (e.g., npm workspaces)
13:   definedTargets = await expandWorkspaceTargets(definedTargets);
14: 
15:   const possibleTargetNames = new Set(getAllTargetNames());
16:   const allowedTargetNames = definedTargets
17:     .filter(target => target.name && possibleTargetNames.has(target.name))
18:     .map(BaseTarget.getId);
19: 
20:   console.log(formatJson(allowedTargetNames));
21: ...
gateway · 2026-08-25 14:56:47
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/package.json</path>
<type>file</type>
<content>
1: {
2:   "name": "@sentry/craft",
3:   "version": "2.31.0-dev.0",
4:   "description": "The universal sentry workflow CLI",
5:   "main": "dist/craft",
6:   "repository": "https://github.com/getsentry/craft",
7:   "author": "Sentry",
8:   "license": "MIT",
9:   "bin": {
10:     "craft": "dist/craft",
11:     "sentry-craft": "dist/craft"
12:   },
13:   "devDependencies": {
14:     "@aws-sdk/client-lambda": "^3.984.0",
15:     "@eslint/js": "^9.17.0",
16:     "@google-cloud/storage": "^7.14.0",
17:     "@octokit/plugin-retry": "^7.1.2",
18:     "@octokit/request-error": "^7.0.0",
19:     "@octokit/rest": "^21.0.2",
20:     "@sentry/esbuild-plugin": "^2.23.0",
21:     "@sentry/node": "^10.27.0",
22:     "@types/async": "^3.0.1",
23:     "@types/aws4": "^1.5.1",
24:     "@types/cli-table": "^0.3.0",
25:     "@types/git-url-parse": "^9.0.0",
26:     "@types/is-ci": "^2.0.0",
27: ...
gateway · 2026-08-25 14:57:49
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/vitest.config.mts</path>
<type>file</type>
<content>
1: import { defineConfig } from 'vitest/config';
2: 
3: export default defineConfig({
4:   test: {
5:     globals: true,
6:     environment: 'node',
7:     include: ['src/**/*.test.ts'],
8:     exclude: ['**/node_modules/**', '**/dist/**', '**/fixtures/**'],
9:     testTimeout: 30000,
10:     alias: {
11:       '^marked$': 'marked/lib/marked.umd.js',
12:     },
13:   },
14: });

(End of file - total 14 lines)
</content>
gateway · 2026-08-25 14:58:46
result
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/__tests__/config.test.ts
gateway · 2026-08-25 14:59:03
result
Found 83 matches
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/__tests__/versionBump.test.ts:
  Line 218:   describe('workspace:* handling (issue #804)', () => {

  Line 220:      * Set up a workspace monorepo:

  Line 221:      *   /package.json                        (workspaces: ["packages/*"])

  Line 223:      *   /packages/app/package.json           (@scope/app, 0.0.1, deps: core @ workspace:*)

  Line 237:           workspaces: ['packages/*'],

  Line 258:           dependencies: { '@scope/core': 'workspace:*' },

  Line 273:       // Simulate `npm version --workspaces`: write the new version to every

  Line 277:           if (args.includes('--workspaces')) {

  Line 284:                 workspaces: ['packages/*'],

  Line 296:                 dependencies: { '@scope/core': 'workspace:*' },

  Line 300:               'npm error code EUNSUPPORTEDPROTOCOL\nnpm error Unsupported URL Type "workspace:": workspace:*',

  Line 315:       const workspaceCalls = ...
gateway · 2026-08-25 14:59:52
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/__tests__/config.test.ts</path>
<type>file</type>
<content>
220:       { name: 'github', id: 'second' },
221:     ]);
222:     // A mixed defined/undefined prefix is still ambiguous.
223:     expect(getGitTagPrefix()).toBe('cli@');
224:     expect(warnSpy).toHaveBeenCalledTimes(1);
225:   });
226: });
227: 
228: describe('workspaces', () => {
229:   afterEach(() => {
230:     setActiveWorkspace(undefined);
231:     vi.restoreAllMocks();
232:   });
233: 
234:   const WS_CONFIG = [
235:     `minVersion: ${WORKSPACES_MIN_VERSION}`,
236:     'github:',
237:     '  owner: getsentry',
238:     '  repo: toolkit',
239:     'changelog: CHANGELOG.md',
240:     'workspaces:',
241:     '  cli:',
242:     '    releaseBranchPrefix: release/cli',
243:     '    github:',
244:     '      projectPath: cli',
245:     '    targets:',
246:     '      - name: github',
247:     '        tagPrefix: "cli@"',
248:     '  mcp:',
249:     '    ...
gateway · 2026-08-25 15:01:33
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/prepare.ts</path>
<type>file</type>
<content>
740: 
741:     const calverFormat =
742:       config.versioning?.calver?.format ?? DEFAULT_CALVER_CONFIG.format;
743: 
744:     return calculateCalVer(git, {
745:       offset: calverOffset,
746:       format: calverFormat,
747:     });
748:   }
749: 
750:   // Handle automatic version detection or version bump types
751:   if (version === 'auto' || isBumpType(version)) {
752:     if (!requiresMinVersion(AUTO_VERSION_MIN_VERSION)) {
753:       const featureName = isBumpType(version)
754:         ? 'Version bump types'
755:         : 'Auto-versioning';
756:       throw new ConfigurationError(
757:         `${featureName} requires minVersion >= ${AUTO_VERSION_MIN_VERSION} in .craft.yml. ` +
758:           'Please update your configuration or specify the version explicitly.',
759:       );
760:     }
761: 
762:     const latestTag = await getLatestTag(git, ...
gateway · 2026-08-25 15:03:42
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-25 15:04:41
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/publish.ts</path>
<type>file</type>
<content>
650:     }
651:   }
652: 
653:   const revision = await git.revparse('HEAD');
654:   logger.debug('Revision to publish: ', revision);
655: 
656:   const statusProvider = await getStatusProviderFromConfig();
657:   const artifactProvider = await getArtifactProviderFromConfig();
658: 
659:   // Check status of all CI builds linked to the revision
660:   await checkRevisionStatus(statusProvider, revision, argv.noStatusCheck);
661: 
662:   await printRevisionSummary(artifactProvider, revision);
663: 
664:   await checkRequiredArtifacts(artifactProvider, revision, config.requireNames);
665: 
666:   // Find targets
667:   let targetsToPublish: Set<string> = new Set(
668:     (typeof argv.target === 'string' ? [argv.target] : argv.target) || [
669:       SpecialTarget.All,
670:     ],
671:   );
672: 
673:   // Treat "all"/"none" specially
674:   for (const ...
gateway · 2026-08-25 15:05:54
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/__tests__/publishState.test.ts</path>
<type>file</type>
<content>
1: import { describe, test, expect, beforeEach, afterEach } from 'vitest';
2: import { homedir } from 'os';
3: import { join } from 'path';
4: 
5: import {
6:   getCraftStateDir,
7:   getPublishStateFilename,
8:   getPublishStatePath,
9: } from '../publishState';
10: 
11: describe('publishState', () => {
12:   const savedEnv = { ...process.env };
13: 
14:   beforeEach(() => {
15:     delete process.env.XDG_STATE_HOME;
16:   });
17: 
18:   afterEach(() => {
19:     process.env = { ...savedEnv };
20:   });
21: 
22:   describe('getCraftStateDir', () => {
23:     test('defaults to $HOME/.local/state/craft when XDG_STATE_HOME is unset', () => {
24:       expect(getCraftStateDir()).toBe(
25:         join(homedir(), '.local', 'state', 'craft'),
26:       );
27:     });
28: 
29:     test('honours XDG_STATE_HOME when set', () => {
30:       ...
gateway · 2026-08-25 15:07:33
result
 RUN  v4.1.8 /home/byk/Code/getsentry/craft-workspace-action-propagation


 Test Files  3 passed (3)
      Tests  99 passed (99)
   Start at  15:07:32
   Duration  1.72s (transform 706ms, setup 0ms, import 2.36s, tests 105ms, environment 0ms)

gateway · 2026-08-25 15:08:32
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:     "test": "vitest run",
19:     "test:watch": "vitest",
20:     "lint": "eslint src .github --ignore-pattern '!.github'",
21:     "prettier": "prettier --write src"
22:   },
23:   "devDependencies": {
24:     "eslint": "^8.9.0",
25:     "eslint-config-prettier": "^8.3.0",
26:     "eslint-plugin-yml": "^0.13.0",
27:     "prettier": "^2.2.1",
28:     "vitest": "^4.1.0"
29:   },
30:   "dependencies": {
31:     ...
gateway · 2026-08-25 15:10:08
result
yarn run v1.22.22
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
error Command "vitest" not found.
gateway · 2026-08-25 15:10:27
result
yarn run v1.22.22
$ vitest run src/modules/__tests__/details-from-context.js
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
/bin/sh: 1: vitest: not found
error Command failed with exit code 127.
gateway · 2026-08-25 15:12:24
result
{"repo":"toolkit","path":".","version":"1.2.3","dry_run":"","merge_target":"","workspace":"cli [preview] \"next\""}
{"repo":"repo","path":"./path","version":"1.2.3","dry_run":"","merge_target":""}
gateway · 2026-08-25 15:14:39
result
--dry-run [{"kind":"positional","index":0,"value":"prepare"},{"kind":"option","name":"workspace","rawName":"--workspace","index":1,"value":"--dry-run","inlineValue":false}]
--config-from=untrusted [{"kind":"positional","index":0,"value":"prepare"},{"kind":"option","name":"workspace","rawName":"--workspace","index":1,"value":"--config-from=untrusted","inlineValue":false}]
-cli [{"kind":"positional","index":0,"value":"prepare"},{"kind":"option","name":"workspace","rawName":"--workspace","index":1,"value":"-cli","inlineValue":false}]
gateway · 2026-08-25 15:16:20
result
node:internal/modules/cjs/loader:1520
  throw err;
  ^

Error: Cannot find module 'yargs-parser'
Require stack:
- /home/byk/Code/getsentry/craft-workspace-action-propagation/[eval]
    at Module._resolveFilename (node:internal/modules/cjs/loader:1517:15)
    at wrapResolveFilename (node:internal/modules/cjs/loader:1071:27)
    at defaultResolveImplForCJSLoading (node:internal/modules/cjs/loader:1095:10)
    at resolveForCJSWithHooks (node:internal/modules/cjs/loader:1122:12)
    at Module._load (node:internal/modules/cjs/loader:1294:5)
    at wrapModuleLoad (node:internal/modules/cjs/loader:255:19)
    at Module.require (node:internal/modules/cjs/loader:1617:12)
    at require (node:internal/modules/helpers:153:16)
    at [eval]:1:13
    at runScriptInThisContext (node:internal/vm:219:10) {
  code: 'MODULE_NOT_FOUND',
  requireStack: [
    '/home/byk/Code/getsentry/craft-workspace-action-propagation/[eval]'
  ]
}

Node.js v24.18.0
gateway · 2026-08-25 15:17:20
result
--dry-run {"_":["prepare"],"workspace":"","dry-run":true,"dryRun":true,"$0":""}
--config-from=untrusted {"_":["prepare"],"workspace":"","config-from":"untrusted","configFrom":"untrusted","$0":""}
-cli {"_":["prepare"],"workspace":"","c":true,"l":true,"i":true,"$0":""}
gateway · 2026-08-25 15:18:22
result
{"title":"publish: getsentry/toolkit [workspace: \"x\\\"] @999.0.0 # $(printf pwned)\"] @1.2.3","result":{"repo":"toolkit","path":".","version":"1.2.3","dry_run":"","merge_target":"","workspace":"x\"] @999.0.0 # $(printf pwned)"}}
gateway · 2026-08-25 15:19:32
result
YAML parse passed
gateway · 2026-08-25 15:21:35
result
 M .github/workflows/release.yml
 M action.yml
9	0	.github/workflows/release.yml
27	3	action.yml
gateway · 2026-08-25 15:23:54
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/.github/workflows/release.yml</path>
<type>file</type>
<content>
1: name: Release
2: concurrency: ${{ github.workflow }}-${{ github.ref }}
3: 
4: on:
5:   # For Craft's own releases (dogfooding)
6:   workflow_dispatch:
7:     inputs:
8:       version:
9:         description: Version to release
10:         required: true
11:         default: 'auto'
12:       force:
13:         description: Force a release even when there are release-blockers (optional)
14:         required: false
15:       workspace:
16:         description: Named Craft release workspace to prepare and publish
17:         required: false
18: 
19:   # For external repos to call this workflow
20:   workflow_call:
21:     inputs:
22:       version:
23:         description: Version to release (semver, bump type, or "auto")
24:         type: string
25:         required: false
26:       force:
27:         description: Force a release even when there are ...
gateway · 2026-08-25 15:24:30
result
# Reuse workflows

Learn how to avoid duplication when creating a workflow by reusing existing workflows.

## Creating a reusable workflow

Reusable workflows are YAML-formatted files, very similar to any other workflow file. As with other workflow files, you locate reusable workflows in the `.github/workflows` directory of a repository. Subdirectories of the `workflows` directory are not supported.

For a workflow to be reusable, the values for `on` must include `workflow_call`:

```yaml
on:
  workflow_call:
```

## Using inputs and secrets in a reusable workflow

You can define inputs and secrets, which can be passed from the caller workflow and then used within the called workflow. There are three stages to using an input or a secret in a reusable workflow.

1. In the reusable workflow, use the `inputs` and `secrets` keywords to define inputs or secrets that will be passed from a caller workflow. ...
gateway · 2026-08-25 15:25:55
result
# Contexts reference

Find information about contexts available in GitHub Actions workflows, including available properties, access methods, and usage examples.

## Available contexts

| Context name | Type     | Description                                                                                                                                          |
| ------------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `github`     | `object` | Information about the workflow run. For more information, see [`github` context](#github-context).                                                   |
| `env`        | `object` | Contains variables set in a workflow, job, or step. For more information, see [`env` context](#env-context). ...
gateway · 2026-08-25 15:29:08
result
diff --git a/dev/stdin b/src/modules/details-from-context.js
index 0000000..492311c 100644
--- a/dev/stdin
+++ b/src/modules/details-from-context.js
@@ -0,0 +1,80 @@
+/**
+ * Matches the entire "Targets" section of a github publish issue body.
+ */
+const TARGETS_SECTION_PARSER_REGEX =
+  /^(?!### Targets$\s)(?: *- \[[ xX]\] \S+\s*$(?:\r?\n)?)+/m;
+
+/**
+ * Matches all targets of a github publish issue body in a section that was already matched and extracted with `TARGETS_PARSER_REGEX`.
+ * The "id" of the targets is captured within a capture group.
+ */
+const TARGETS_PARSER_REGEX = /^\s*- \[[ x]\] (\S+)/gim;
+
+/**
+ * Matches checked targets of a github publish issue body in a section that was already matched and extracted with `TARGETS_PARSER_REGEX`.
+ * The "id" of the targets is captured within a capture group.
+ */
+const CHECKED_TARGETS_PARSER_REGEX = /^\s*- \[x\] (\S+)/gim;
+
+async function detailsFromContext({ context }) {
+  if (!context || !context.payload || ...
gateway · 2026-08-25 15:29:54
result
  chore/fix-security-advisories
* feat/workspace-acceptance
  fix/add-workflow-permissions
  main
  security/dual-write-publish-state
+ security/remove-legacy-publish-state-write
  remotes/origin/HEAD -> origin/main
  remotes/origin/add-agents-md
  remotes/origin/chore/fix-security-advisories
  remotes/origin/ci/switch-to-sentry-internal-app
  remotes/origin/docs/deprecate-action-prepare-release
  remotes/origin/docs/mermaid-release-flow
  remotes/origin/fix/add-packages-write-permission
  remotes/origin/fix/add-workflow-permissions
  remotes/origin/fix/auto-approve-token
  remotes/origin/fix/undici-security-vulnerability
  remotes/origin/fix/update-node-version-in-workflows
  remotes/origin/main
  remotes/origin/migrate-secrets
  remotes/origin/ref/remove-unused-requester-field
  remotes/origin/refactor/auto-approve-repo-list
  remotes/origin/remove/retract-command
  remotes/origin/security/dual-write-publish-state
  ...
gateway · 2026-08-25 15:31:22
result
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index cff6bc3..37519e9 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -153,136 +153,143 @@ jobs:
           fromJSON(steps.inputs.outputs.result).repo == 'sentry-javascript' && fromJSON(steps.inputs.outputs.result).merge_target == 'v9' ||
           fromJSON(steps.inputs.outputs.result).repo == 'sentry-javascript' && fromJSON(steps.inputs.outputs.result).merge_target == 'v8' ||
           fromJSON(steps.inputs.outputs.result).repo == 'sentry-javascript' && fromJSON(steps.inputs.outputs.result).merge_target == 'v7' ||
           fromJSON(steps.inputs.outputs.result).repo == 'sentry-javascript' && fromJSON(steps.inputs.outputs.result).merge_target == 'master' ||
           fromJSON(steps.inputs.outputs.result).repo == 'sentry-python' && fromJSON(steps.inputs.outputs.result).merge_target == 'alpha' ||
           fromJSON(steps.inputs.outputs.result).repo == 'sentry-wizard' && ...
gateway · 2026-08-25 15:32:35
result
 M .github/workflows/publish.yml
 M src/modules/__tests__/details-from-context.js
 M src/modules/__tests__/update-issue.js
 M src/modules/details-from-context.js
 M src/modules/update-issue.js
gateway · 2026-08-25 15:33:47
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 crypto = require("crypto");
3: const path = require("path");
4: const {
5:   TARGETS_SECTION_PARSER_REGEX,
6:   TARGETS_PARSER_REGEX,
7: } = require("./details-from-context");
8: 
9: const CONTAINER_REPO_PATH = "/github/workspace/__repo__";
10: 
11: function sanitiseForFilename(value) {
12:   return value
13:     .toLowerCase()
14:     .replace(/[^a-z0-9._-]+/g, "_")
15:     .replace(/^_+|_+$/g, "");
16: }
17: 
18: function getCraftStateFilePath({
19:   repo,
20:   path: publishPath,
21:   version,
22:   workspace,
23: }) {
24:   const containerCwd = path.posix.resolve(CONTAINER_REPO_PATH, publishPath);
25:   if (
26:     containerCwd !== CONTAINER_REPO_PATH &&
27:     !containerCwd.startsWith(`${CONTAINER_REPO_PATH}/`)
28:   ) {
29:     throw new Error(`Invalid publish issue path: '${publishPath}'`);
30:   }
31: 
32:   const ...
gateway · 2026-08-25 15:35:47
result
Found 13 matches
/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/__tests__/update-issue.js:
  Line 136: test("isolates state files for workspaces and canonical paths", () => {

  Line 142:       workspace: "cli/v2",

  Line 145:     ".craft-state/craft/publish-state-getsentry-toolkit-21cf7beaeda4-workspace-Y2xpL3Yy-1.2.3.json"


/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/__tests__/details-from-context.js:
  Line 104: test("parses a human-readable workspace from the title", async () => {

  Line 111:           title: 'publish: getsentry/toolkit [workspace: "cli/v2"] @1.2.3',

  Line 122:     workspace: "cli/v2",

  Line 126: test("parses escaped workspace characters from the title", async () => {

  Line 134:             'publish: getsentry/toolkit [workspace: "cli [preview] \\"next\\""] @1.2.3',

  Line 142:   expect(result.workspace).toBe('cli [preview] "next"');

  Line 162: test("rejects an empty or control-character workspace", async () => ...
gateway · 2026-08-25 15:36:23
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/details-from-context.js</path>
<type>file</type>
<content>
19: async function detailsFromContext({ context }) {
20:   if (!context || !context.payload || !context.payload.issue) {
21:     throw new Error("Issue context is not defined");
22:   }
23: 
24:   const titleParser =
25:     /^publish: (?:getsentry\/)?(?<repo>[A-Za-z0-9_.-]+)(?<path>\/[\w./-]+)?(?: \[workspace: (?<workspace>"(?:[^"\\]|\\.)*")\] )?@(?<version>[\w.+-]+)$/;
26:   const titleMatch = context.payload.issue.title.match(titleParser);
27:   if (!titleMatch || !titleMatch.groups) {
28:     throw new Error(
29:       `Invalid publish issue title: '${context.payload.issue.title}'`
30:     );
31:   }
32:   const { workspace: workspaceJson, ...titleDetails } = titleMatch.groups;
33:   let workspace = "";
34:   if (workspaceJson) {
35:     workspace = JSON.parse(workspaceJson);
36:     if (!workspace || /[\p{Cc}\p{Zl}\p{Zp}]/u.test(workspace)) {
37:      ...
gateway · 2026-08-25 15:38:16
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/__tests__/details-from-context.js</path>
<type>file</type>
<content>
95:     dry_run: "",
96:     merge_target: "",
97:     path: ".",
98:     repo: "sentry",
99:     targets: ["github", "docker[latest]"],
100:     version: "21.3.1",
101:   });
102: });
103: 
104: test("parses a human-readable workspace from the title", async () => {
105:   const result = await detailsFromContext({
106:     context: {
107:       repo: { owner: "getsentry", repo: "publish" },
108:       payload: {
109:         issue: {
110:           number: "123",
111:           title: 'publish: getsentry/toolkit [workspace: "cli/v2"] @1.2.3',
112:           body: "Requested by: @example",
113:           labels: [],
114:         },
115:       },
116:     },
117:   });
118: 
119:   expect(result).toMatchObject({
120:     repo: "toolkit",
121:     version: "1.2.3",
122:     workspace: "cli/v2",
123:   });
124: });
125: 
126: test("parses escaped ...
gateway · 2026-08-25 15:40:36

Findings

HIGH — workspace input injects Craft options

action.yml:184-186 and action.yml:204-208 build --workspace and its value as separate argv entries. A workspace value such as --config-from=untrusted becomes a Craft option, not a workspace name.

Craft explicitly rejects separate option-like workspace values in src/utils/helpers.ts:63-68; yargs then parses the injected flag. I reproduced:

prepare --workspace --config-from=untrusted
→ workspace: ""
→ config-from: "untrusted"

This can reach the remote-config fetch at src/commands/prepare.ts:796-810, which can later run that config’s preReleaseCommand at :902-910. It also affects target selection.

Use one inline argument in both steps, e.g. CRAFT_ARGS+=("--workspace=$WORKSPACE"), so Craft receives option-like names as values as its parser intends.

HIGH — unsafe workspace validation disagrees with the publish controller and occurs too late

action.yml:171-174 rejects only CR/LF. It permits tabs, other control characters, and Unicode line separators. The paired controller rejects all control characters and Unicode line/paragraph separators at publish-workspace-acceptance/src/modules/details-from-context.js:34-40.

A configured workspace with a tab or U+2028 can therefore pass the action check, reach git remote set-head (action.yml:176-177) and craft prepare (:194), then create a publish issue that the controller rejects. This fails the requirement to reject unsafe workspace names before prepare side effects.

Validate the same character class as the controller before line 176.

HIGH — controller support is not yet committed or deployed

action.yml:258-265 emits the new workspace title grammar. The compatible parser and workspace state forwarding currently exist only as uncommitted edits in /home/byk/Code/getsentry/publish-workspace-acceptance:

  • Parser: src/modules/details-from-context.js:25-40
  • Workspace-aware state preseed: ...
gpt-5.6-terra · 2026-08-25 15:40:38 · ~1.2K tokens

Distillations (6)

Gen 0 2026-08-25 15:26:59 · 692 tokens

Date: Aug 25, 2026 * 🟡 (15:24) Inspected `/home/byk/Code/getsentry/craft-workspace-action-propagation/.github/workflows/release.yml:1-151`: `Release` workflow has concurrency `${{ github.workflow }}-${{ github.ref }}` and supports both `workflow_dispatch` and `workflow_call`. * 🟡 (15:24) `workflow_dispatch` inputs include required `version` (default `'auto'`), optional `force`, and optional `wo…

Gen 0 2026-08-25 15:26:09 · 3505 tokens

Date: Aug 25, 2026 * 🟡 (14:54) Inspected `/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/prepare.ts:1-130`: command is `prepare [NEW-VERSION]`, aliases are `p`, `prerelease`, `prepublish`, `prepare`, `release`, and description is `🚢 Prepare a new release branch`. * 🟡 (14:54) `prepare` positional `NEW-VERSION` accepts semver (example `"1.2.3"`), bump types (`"major"`, …

Gen 0 2026-08-25 14:56:51 · 1173 tokens

Date: Aug 25, 2026 * 🔴 (14:53) User directed that CalVer changelog content destined for publish issues replace `@`-mentions with bold formatting, preventing contributor notifications; committed `CHANGELOG.md` content must retain original `@`-mentions. * 🟡 (14:53) Inspected `/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/prepare.ts:800-1003`. * 🟡 (14:53) `prepareMain` …

Gen 0 2026-08-25 14:55:55 · 2774 tokens

Date: Aug 25, 2026 * 🟡 (14:39) Inspected `/home/byk/Code/getsentry/craft-workspace-action-propagation/src/config.ts` lines 1-270. `CONFIG_FILE_NAME = '.craft.yml'`; `DEFAULT_RELEASE_BRANCH_NAME = 'release'`; `WORKSPACES_MIN_VERSION = '2.29.0'`. * 🟡 (14:39) `src/config.ts:69-95` stores selected workspace in `_activeWorkspaceName: string | undefined`; `setActiveWorkspace(name)` sets it and invali…

Gen 0 2026-08-25 14:42:25 · 686 tokens

Date: Aug 25, 2026 * 🔴 (14:38) User stated `--workspace --dry-run` must never be mistaken for a workspace name. * 🟡 (14:38) `src/utils/helpers.ts:24-72` documents and implements `extractWorkspaceSelection(argv, env = process.env)`: it resolves `--workspace foo` and `--workspace=foo` from raw argv before yargs builders/config access, using `node:util` `parseArgs()` tokenization with `options: { …

Gen 0 2026-08-25 14:41:23 · 1583 tokens

Date: Aug 25, 2026 * 🟡 [requested-security-review] (14:30) User requested a read-only adversarial correctness/security review of uncommitted changes in `/home/byk/Code/getsentry/craft-workspace-action-propagation` on branch `feat/workspace-action-propagation`, compared with `origin/master`; explicitly directed that no files be modified. * 🟡 [requested-security-review] (14:30) Review scope reque…