DashboardcraftSession 1WdPDg9kiH9B

Session 1WdPDg9kiH9B

Full ID: 1WdPDg9kiH9B5NvEO
Messages: 39
Distillations: 1
Time range: 2026-09-04 08:26:23 — 2026-09-04 08:34:36

Conversation (39 messages)

Perform a final strict READ-ONLY adversarial audit. Do NOT edit files, run formatters/generators, or change git state. Inspect complete current uncommitted diffs in BOTH worktrees:

  • /home/byk/Code/getsentry/craft-workspace-action-propagation
  • /home/byk/Code/getsentry/publish-workspace-acceptance

Audit the exact final trees against this required contract:

  1. New Action titles are publish: <checkout-repository>/<full-concrete-workspace-path>@<version> and preserve exact case/path spelling; workspace and checkout path are mutually exclusive.
  2. Publish resolves CI revision before target checkout; exact SHA checkout happens before root .craft.yml workspace list; exact unnormalized full suffix match selects a workspace, all nonmatches remain checkout paths; no root config means path behavior and discovery errors fail closed.
  3. Untrusted manual issue paths and discovered workspace names reject empty, ., .., __proto__, leading -, backslash, non-ASCII/control values before checkout/state/publish. No normalizing.
  4. Craft remote --config-from resolves workspace globs from the checked-out repo root. Workspace glob keys never escape base before filesystem access. Regular *, ?, character classes including [!a] and [^a], and globstar remain valid. Literal/configured workspace path safety matches concrete workspace safety.
  5. Publishing only begins on a fresh ci-ready label event for an open accepted issue with neither pending nor failed CI; waiting state removes stale ci-ready first.
  6. State is external to target checkout and isolated by checkout repo/path/workspace/version. Craft publish gets exact CI revision via --rev.
  7. New titles are intentionally path-only (no legacy JSON workspace form required); docs must accurately say so.
  8. Generated parser parity, tests, docs, intended diff scope, and no unrelated formatter churn. Seven historical Craft unused-variable lint warnings are non-findings. ...
gateway · 2026-09-04 08:26:23
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-09-04 08:27:47
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance</path>
<type>directory</type>
<entries>
.eslintrc.js
.git
.github/
.gitignore
AGENTS.md
auto-approve-repos.txt
docs/
LICENSE
node_modules/
package.json
README.md
scripts/
src/
vitest.config.js
yarn.lock

(15 entries)
</entries>
gateway · 2026-09-04 08:27:56
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/prepare.ts
 M src/config.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/prepare.ts
M	src/config.ts
M	src/schemas/project_config.ts
gateway · 2026-09-04 08:28:00
result
 M .github/workflows/publish.yml
 M docs/publish-issue-format.md
 M src/modules/__tests__/ci-poller-input.js
 M src/modules/__tests__/details-from-context.js
 M src/modules/__tests__/publish-location.js
 M src/modules/__tests__/publish-workflow.js
 M src/modules/details-from-context.js
 M src/modules/publish-issue-title.js
 M src/modules/publish-issue-title.peggy
 M src/modules/publish-location.js
 M src/publish/resolve-location.js
M	.github/workflows/publish.yml
M	docs/publish-issue-format.md
M	src/modules/__tests__/ci-poller-input.js
M	src/modules/__tests__/details-from-context.js
M	src/modules/__tests__/publish-location.js
M	src/modules/__tests__/publish-workflow.js
M	src/modules/details-from-context.js
M	src/modules/publish-issue-title.js
M	src/modules/publish-issue-title.peggy
M	src/modules/publish-location.js
M	src/publish/resolve-location.js
gateway · 2026-09-04 08:28:10
result
diff --git a/action.yml b/action.yml
index 94f3158..84377a8 100644
--- a/action.yml
+++ b/action.yml
@@ -4,162 +4,162 @@ description: 'Prepare a new release using Craft'
 inputs:
   version:
     description: >
       Version to release. Can be a semver string (e.g., "1.2.3"),
       a bump type ("major", "minor", "patch"), or "auto" for automatic detection.
     required: false
   merge_target:
     description: Target branch to merge into. Uses the default branch as a fallback.
     required: false
   force:
     description: Force a release even when there are release-blockers
     required: false
     default: 'false'
   blocker_label:
     description: Label that blocks releases
     required: false
     default: 'release-blocker'
   publish_repo:
     description: Repository for publish issues (owner/repo format)
     required: false
   git_user_name:
     description: Git committer name
     required: false
   git_user_email:
     description: Git committer email
     required: ...
gateway · 2026-09-04 08:28:15
result
diff --git a/src/__tests__/action.test.ts b/src/__tests__/action.test.ts
index 8ca535a..0a1d71b 100644
--- a/src/__tests__/action.test.ts
+++ b/src/__tests__/action.test.ts
@@ -113,136 +113,161 @@ function runRequestPublish(
         WORKSPACE: workspace,
       },
     },
   );
 }
 
 function runActionStep(
   stepName: string,
   workspace: string,
   environment: ReturnType<typeof createActionEnvironment>,
   pathInput = '.',
   locale = 'C',
 ) {
   return spawnSync('bash', ['-e', '-c', getActionStep(stepName).run!], {
     cwd: environment.directory,
     env: {
       ...process.env,
       CRAFT_CALLS: environment.craftCalls,
       CRAFT_CONFIG_FROM_MERGE_TARGET: '',
       GITHUB_OUTPUT: environment.output,
       GIT_CALLS: environment.gitCalls,
       LC_ALL: locale,
       MERGE_TARGET: '',
       PATH: `${environment.binDirectory}:${process.env.PATH}`,
       PATH_INPUT: pathInput,
       VERSION: '',
       WORKSPACE: workspace,
     },
   });
 }
 
 afterEach(() => {
   ...
gateway · 2026-09-04 08:28:23
result
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index f6dc96a..c72068a 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -115,126 +115,121 @@ jobs:
     timeout-minutes: 90
     env:
       SENTRY_DSN: "https://303a687befb64dc2b40ce4c96de507c5@o1.ingest.sentry.io/6183838"
     steps:
       - name: Get repo contents
         uses: actions/checkout@v6
         with:
           path: .__publish__
 
       - name: Setup Node
         uses: actions/setup-node@v6
         with:
           node-version: 24
           cache: yarn
           cache-dependency-path: .__publish__/yarn.lock
 
       - name: Install yarn dependencies
         run: yarn install --cwd ".__publish__"
 
       - name: Parse and set inputs
         id: inputs
         run: node .__publish__/src/publish/inputs.js
 
       - name: Resolve CI-approved release revision
         id: release-revision
         env:
           PUBLISH_ARGS: ${{ ...
gateway · 2026-09-04 08:28:29
result
diff --git a/src/modules/__tests__/ci-poller-input.js b/src/modules/__tests__/ci-poller-input.js
index 8c80856..77feb3a 100644
--- a/src/modules/__tests__/ci-poller-input.js
+++ b/src/modules/__tests__/ci-poller-input.js
@@ -1,53 +1,53 @@
 import { readFileSync } from "fs";
 import { join } from "path";
 
 import { expect, test } from "vitest";
 
 const { getCiPollerInput } = require("../ci-poller-input.js");
 
 const REVISION = "7e5ca7ed5581552de066e2a8bc295b8306be38ac";
 const issueBody = `Requested by: @byk
 
 Merge target: (default)
 
 Quick links:
 - [View changes](https://github.com/getsentry/toolkit/compare/1.2.2...release/1.2.3)
 - [View check runs](https://github.com/getsentry/toolkit/commit/${REVISION}/checks/)`;
 
-test("parses compact and legacy workspace publish titles", () => {
+test("parses root and full-path publish titles", () => {
   expect(
     getCiPollerInput({
       issueBody,
       title: "publish: getsentry/toolkit/cli@1.2.3",
     })
   ).toEqual({
     ...
gateway · 2026-09-04 08:28:34
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-09-04 08:28:48
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/.github/workflows/publish.yml</path>
<type>file</type>
<content>
1: name: Publish
2: on:
3:   issues:
4:     types: [labeled]
5: 
6: concurrency:
7:   # Use the issue title (e.g. "publish: getsentry/foo@1.2.3") so duplicate
8:   # issues for the same repo@version share a concurrency group.
9:   group: ${{ github.event.issue.title }}
10:   cancel-in-progress: false
11: 
12: permissions:
13:   contents: read
14:   issues: write
15:   packages: write
16: 
17: jobs:
18:   # When accepted is added to a publish issue:
19:   #   - Add ci-pending (and remove ci-failed if retrying)
20:   #   - Enable the poller via CI_POLLER_HAS_PENDING=true
21:   #   - Comment on the issue
22:   #   - Trigger the poller immediately so we don't wait for the next cron tick
23:   # The publish job below requires ci-ready, so it will not fire until the
24:   # poller flips ci-pending → ci-ready (which also prevents publishing without
25:   # CI ...
gateway · 2026-09-04 08:28:54
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/.github/workflows/ci-poller.yml</path>
<type>file</type>
<content>
1: name: CI Status Poller
2: 
3: on:
4:   schedule:
5:     - cron: "*/5 * * * *"
6:   workflow_dispatch:
7:     inputs:
8:       attempt:
9:         description: Self-dispatch attempt counter (internal use)
10:         required: false
11:         default: "0"
12: 
13: permissions:
14:   contents: read
15:   issues: read
16: 
17: jobs:
18:   check-ci:
19:     runs-on: ubuntu-latest
20:     environment: production
21:     # Skip entirely (no runner provisioned) when there's nothing to check.
22:     # Set to "true" by ci-pending.yml, reset to "false" here when done.
23:     # Always allow workflow_dispatch for manual recovery.
24:     if: vars.CI_POLLER_HAS_PENDING == 'true' || github.event_name == 'workflow_dispatch'
25:     concurrency:
26:       group: ci-status-poller
27:       cancel-in-progress: false
28:     steps:
29:       # sentry-internal-app token ...
gateway · 2026-09-04 08:28:59
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-09-04 08:29:37
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/config.ts</path>
<type>file</type>
<content>
90: }
91: 
92: /**
93:  * Returns the name of the currently-selected workspace, if any.
94:  */
95: export function getActiveWorkspace(): string | undefined {
96:   return _activeWorkspaceName;
97: }
98: 
99: /**
100:  * Merges a workspace's overrides onto the top-level (base) config, producing a
101:  * flat `CraftProjectConfig` that the rest of craft consumes unchanged.
102:  *
103:  * Resolution rules:
104:  * - Every release-relevant field defined on the workspace replaces the
105:  *   top-level value (shallow override; a workspace either declares a field or
106:  *   inherits it wholesale — we do not deep-merge arrays/objects, to keep
107:  *   behavior predictable).
108:  * - `github` is shallow-merged (owner/repo) so a workspace can override either
109:  *   value while inheriting the other. ...
gateway · 2026-09-04 08:29:42
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-09-04 08:29:50
result
Found 10 matches
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/index.ts:
  Line 20: import { setActiveWorkspace } from './config';

  Line 29: import * as workspace from './commands/workspace';

  Line 92:   // Resolve the active workspace BEFORE parsing. yargs runs command `builder`s

  Line 94:   // choices from config.targets) *before* middleware, so setting the workspace

  Line 96:   // config without a selection and fail. We therefore extract --workspace (or

  Line 99:   setActiveWorkspace(extractWorkspaceSelection(argv));

  Line 112:     .command(workspace)

  Line 126:     .option('workspace', {

  Line 129:         'Select a named workspace (release unit) from the configuration. ' +

  Line 130:         'Required when the config defines "workspaces". Env: CRAFT_WORKSPACE',
gateway · 2026-09-04 08:29:55
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-09-04 08:29:59
result
Found 1 matches
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/helpers.ts:
  Line 42: export function extractWorkspaceSelection(
gateway · 2026-09-04 08:30:05
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-09-04 08:30:21
result
Found 17 matches
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/publishState.ts:
  Line 2:  * Helpers for locating Craft's publish-state file in a path that is NOT

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

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

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

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

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

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

  Line 102:  * Full absolute path to the publish-state file for the given version.


/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/__tests__/publishState.test.ts:
  Line 52:         /^publish-state-getsentry-craft-[0-9a-f]{12}-1\.2\.3\.json$/,

  Line 107:       ...
gateway · 2026-09-04 08:30:25
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-09-04 08:30:34
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-09-04 08:30:38
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:   /^Requested by: @[^\r\n]+\r?\n(?:[ \t]*\r?\n)?[ \t]*Merge target: [^\r\n]+\r?\n(?:[ \t]*\r?\n)?[ \t]*Quick links:\r?\n(?:[ \t]*\r?\n)?[ \t]*- \[View changes\]\([^\r\n]+\)\r?\n[ \t]*- \[View check runs\]\(https:\/\/github\.com\/getsentry\/(?<repo>[A-Za-z0-9_.-]+)\/commit\/(?<revision>[0-9a-f]{40})\/checks\/?\)(?=\r?\n|$)/;
3: const CHECK_RUNS_LINK_COUNT = /^[ \t]*- \[View check runs\]\(/gm;
4: 
5: function getReleaseRevision({ issueBody, repo }) {
6:   if ((issueBody.match(CHECK_RUNS_LINK_COUNT) || []).length !== 1) {
7:     throw new Error(
8:       `Expected exactly one View check runs link in Quick links for getsentry/${repo}.`
9:     );
10:   }
11: 
12:   const match = issueBody.match(CHECK_RUNS_LINK);
13:   if (!match?.groups || match.groups.repo !== repo) {
14:     throw new Error(
15:       `Expected a View check runs link ...
gateway · 2026-09-04 08:30:44
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/package.json</path>
<type>file</type>
<content>
1: {
2:   "name": "publish",
3:   "private": true,
4:   "version": "0.0.1",
5:   "description": "Approval-based publishing system for Sentry",
6:   "main": "index.js",
7:   "repository": "git@github.com:getsentry/publish.git",
8:   "author": "Sentry Open Source <oss@sentry.io>",
9:   "license": "Apache-2.0",
10:   "volta": {
11:     "node": "24.0.0",
12:     "yarn": "1.22.22"
13:   },
14:   "resolutions": {
15:     "undici": "^6.23.0"
16:   },
17:   "scripts": {
18:     "generate": "node scripts/generate-publish-issue-title-parser.js",
19:     "check:generated": "node scripts/generate-publish-issue-title-parser.js --check",
20:     "test": "yarn check:generated && vitest run",
21:     "test:watch": "vitest",
22:     "lint": "eslint src .github --ignore-pattern '!.github'",
23:     "prettier": "prettier --write src"
24:   },
25:   "devDependencies": {
26:     "eslint": ...
gateway · 2026-09-04 08:31:01
result
"cli\n" false false
"cli\r" false false
"cli
" false false
"cli
" false false
gateway · 2026-09-04 08:31:12
result
packages/foo] false
packages/foo! false
packages/foo^ false
packages/foo[ false
packages/foo* true
packages/[!a]* true
packages/[^a]* true
gateway · 2026-09-04 08:31:37
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-09-04 08:32:05
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/publish-location.js</path>
<type>file</type>
<content>
1: function resolvePublishLocation({ path, workspaceNames }) {
2:   if (!isPublishPath(path)) {
3:     throw new Error("Invalid publish path.");
4:   }
5: 
6:   if (path === ".") {
7:     return { path };
8:   }
9: 
10:   if (!workspaceNames.every(isWorkspaceName)) {
11:     throw new Error(
12:       "Craft workspace discovery returned an invalid workspace list."
13:     );
14:   }
15: 
16:   const workspace = path.slice(2);
17:   if (workspaceNames.includes(workspace)) {
18:     return { path: ".", workspace };
19:   }
20: 
21:   return { path };
22: }
23: 
24: function isPublishPath(path) {
25:   return (
26:     typeof path === "string" &&
27:     (path === "." ||
28:       (path.startsWith("./") &&
29:         path
30:           .slice(2)
31:           .split("/")
32:           .every(isSafeWorkspaceSegment)))
33:   );
34: }
35: 
36: function ...
gateway · 2026-09-04 08:32:24
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/publish-issue-title.js</path>
<type>file</type>
<content>
400:   function peg$fail(expected) {
401:     if (peg$currPos < peg$maxFailPos) {
402:       return;
403:     }
404: 
405:     if (peg$currPos > peg$maxFailPos) {
406:       peg$maxFailPos = peg$currPos;
407:       peg$maxFailExpected = [];
408:     }
409: 
410:     peg$maxFailExpected.push(expected);
411:   }
412: 
413:   function peg$buildSimpleError(message, location) {
414:     return new peg$SyntaxError(message, null, null, location);
415:   }
416: 
417:   function peg$buildStructuredError(expected, found, location) {
418:     return new peg$SyntaxError(
419:       peg$SyntaxError.buildMessage(expected, found),
420:       expected,
421:       found,
422:       location
423:     );
424:   }
425: 
426:   function peg$parsePublishIssueTitle() {
427:     let s0, s1, s2, s3, s4, s5, s6, s7, s8;
428: 
429:     s0 = peg$currPos;
430:     if ...
gateway · 2026-09-04 08:32:32
result
Found 2 matches
/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/ci-poller-input.js:
  Line 1: const { parse: parsePublishIssueTitle } = require("./publish-issue-title");


/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/details-from-context.js:
  Line 1: const { parse: parsePublishIssueTitle } = require("./publish-issue-title");
gateway · 2026-09-04 08:32:37
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/__tests__/publish-location.js</path>
<type>file</type>
<content>
1: import { expect, test } from "vitest";
2: 
3: const { resolvePublishLocation } = require("../publish-location.js");
4: 
5: test("classifies an exact full workspace path without normalizing it", () => {
6:   expect(
7:     resolvePublishLocation({
8:       path: "./packages/CLI",
9:       workspaceNames: ["packages/cli", "packages/CLI"],
10:     })
11:   ).toStrictEqual({ path: ".", workspace: "packages/CLI" });
12: });
13: 
14: test("keeps a non-workspace suffix as a checkout path", () => {
15:   expect(
16:     resolvePublishLocation({
17:       path: "./packages",
18:       workspaceNames: ["cli"],
19:     })
20:   ).toStrictEqual({ path: "./packages" });
21: });
22: 
23: test("keeps a multi-segment suffix that is not an exact workspace path", () => {
24:   expect(
25:     resolvePublishLocation({
26:       path: "./packages/cli",
27:       ...
gateway · 2026-09-04 08:32:41
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/__tests__/config.test.ts</path>
<type>file</type>
<content>
280:     loadWithTargets([
281:       { name: 'github', tagPrefix: 'cli@' },
282:       { name: 'github', id: 'second' },
283:     ]);
284:     // A mixed defined/undefined prefix is still ambiguous.
285:     expect(getGitTagPrefix()).toBe('cli@');
286:     expect(warnSpy).toHaveBeenCalledTimes(1);
287:   });
288: });
289: 
290: describe('workspaces', () => {
291:   let originalCwd: string;
292:   const temporaryDirectories: string[] = [];
293: 
294:   beforeEach(() => {
295:     originalCwd = process.cwd();
296:   });
297: 
298:   afterEach(() => {
299:     process.chdir(originalCwd);
300:     for (const directory of temporaryDirectories.splice(0)) {
301:       rmSync(directory, { recursive: true, force: true });
302:     }
303:     setActiveWorkspace(undefined);
304:     vi.restoreAllMocks();
305:   });
306: 
307:   const WS_CONFIG = [
308:     ...
gateway · 2026-09-04 08:32: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-09-04 08:33:01
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/docs/src/content/docs/targets/github.md</path>
<type>file</type>
<content>
70:   - name: github
71:     tagPrefix: "mcp@"
72: ```
73: 
74: Releasing `1.2.3` for each product then produces the tags `cli@1.2.3` / `mcp@1.2.3` on release branches `release/cli/1.2.3` / `release/mcp/1.2.3` — no collisions.
75: 
76: ## Release Workspaces
77: 
78: Use top-level `workspaces:` to define independently versioned release units in
79: one repository. Keys may be literal paths or glob patterns, and Craft expands
80: patterns to concrete directories relative to `.craft.yml`. Select one concrete
81: path explicitly with `--workspace <path>` or
82: `CRAFT_WORKSPACE`:
83: 
84: ```yaml
85: minVersion: 2.29.0
86: github:
87:   owner: getsentry
88:   repo: toolkit
89: workspaces:
90:   packages/*:
91:     releaseBranchPrefix: release/cli
92:     targets:
93:       - name: github
94:         tagPrefix: "cli@"
95:   tools/mcp:
96:     ...
gateway · 2026-09-04 08:33:30
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/prepare.ts
 M src/config.ts
 M src/schemas/project_config.ts
ba5c4249b29220e90994caddec27ab4212a64316
gateway · 2026-09-04 08:33:37
result
 M .github/workflows/publish.yml
 M docs/publish-issue-format.md
 M src/modules/__tests__/ci-poller-input.js
 M src/modules/__tests__/details-from-context.js
 M src/modules/__tests__/publish-location.js
 M src/modules/__tests__/publish-workflow.js
 M src/modules/details-from-context.js
 M src/modules/publish-issue-title.js
 M src/modules/publish-issue-title.peggy
 M src/modules/publish-location.js
 M src/publish/resolve-location.js
c5ebdf0d8061dfb6bcf83055468b9ae3f8617b8f
gateway · 2026-09-04 08:33:58
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/__tests__/action.test.ts</path>
<type>file</type>
<content>
125: ) {
126:   return spawnSync('bash', ['-e', '-c', getActionStep(stepName).run!], {
127:     cwd: environment.directory,
128:     env: {
129:       ...process.env,
130:       CRAFT_CALLS: environment.craftCalls,
131:       CRAFT_CONFIG_FROM_MERGE_TARGET: '',
132:       GITHUB_OUTPUT: environment.output,
133:       GIT_CALLS: environment.gitCalls,
134:       LC_ALL: locale,
135:       MERGE_TARGET: '',
136:       PATH: `${environment.binDirectory}:${process.env.PATH}`,
137:       PATH_INPUT: pathInput,
138:       VERSION: '',
139:       WORKSPACE: workspace,
140:     },
141:   });
142: }
143: 
144: afterEach(() => {
145:   for (const directory of tempDirectories.splice(0)) {
146:     rmSync(directory, { recursive: true, force: true });
147:   }
148: });
149: 
150: test('forwards workspace input to every Craft command', () => {
151:   ...
gateway · 2026-09-04 08:34:03
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/__tests__/action.test.ts</path>
<type>file</type>
<content>
258: test('uses the full workspace path in publish request titles', () => {
259:   const rootEnvironment = createActionEnvironment();
260:   const workspaceEnvironment = createActionEnvironment();
261: 
262:   expect(runRequestPublish('', rootEnvironment).status).toBe(0);
263:   expect(runRequestPublish('packages/cli', workspaceEnvironment).status).toBe(
264:     0,
265:   );
266: 
267:   expect(readFileSync(rootEnvironment.ghTitles, 'utf8')).toBe(
268:     'publish: getsentry/toolkit@1.2.3\n',
269:   );
270:   expect(readFileSync(workspaceEnvironment.ghTitles, 'utf8')).toBe(
271:     'publish: getsentry/toolkit/packages/cli@1.2.3\n',
272:   );
273: });

(End of file - total 273 lines)
</content>
gateway · 2026-09-04 08:34:36
  • Contract 2 — MUST-FIX; Contract 3 — MUST-FIX: Root paths return before validating discovered workspace names. An unsafe discovery result therefore reaches state creation and publish. publish-location.js:6-14; state creation follows at .github/workflows/publish.yml:184-230. Validate workspaceNames before the root-path return.

  • Contract 4 — MUST-FIX: Configured literals such as packages/foo], packages/foo!, and packages/foo^ pass the glob-segment schema but are not glob patterns, so they bypass concrete-path validation and can become workspace names. project_config.ts:218-241; config.ts:208-211,264-279. Literal keys must use the same safety predicate as concrete workspace paths.

  • Contract 8 — MUST-FIX: Regression coverage misses both failures above: literal unsafe configured paths are not tested (config.test.ts:495-502), and invalid discovery output is only tested for non-root paths (publish-location.test.js:32-39,53-79). The seven historical Craft unused-variable warnings are non-findings.

  • Contract 1 — PASS: The action rejects simultaneous path/workspace inputs and builds the full workspace-path title without normalizing it. action.yml:80-85,194-195,238-241,268.

  • Contract 5 — PASS: Acceptance removes stale ci-ready, publishes only on a fresh open accepted ci-ready event without pending/failed labels, and the poller adds that label after removing pending. .github/workflows/publish.yml:42-58,98-114; .github/workflows/ci-poller.yml:203-206.

  • Contract 6 — PASS: The target checkout uses the resolved revision; Craft receives that SHA through --rev; state is outside __repo__ and keyed by checkout identity, path hash, workspace, and version. .github/workflows/publish.yml:159-179,212-230,242-256.

  • Contract 7 — PASS: The grammar removes legacy JSON workspace syntax, and the documentation defines path-only titles. src/modules/publish-issue-title.peggy:9-14; docs/publish-issue-format.md:10-39. ...

gpt-5.6-terra · 2026-09-04 08:34:36 · ~477 tokens

Distillations (1)

Gen 0 2026-09-04 08:28:40 · 664 tokens

Date: Sep 4, 2026 * 🔴 (08:26) User directive: workspace glob keys must never escape base before filesystem access. * 🔴 (08:26) User requires a final strict read-only adversarial audit: do not edit files, run formatters/generators, or change git state. * 🟡 [requested-audit] (08:26) User requested inspection of complete current uncommitted diffs in both worktrees: `/home/byk/Code/getsentry/craft…