DashboardcraftSession 1OTYS3m4nK6o

Session 1OTYS3m4nK6o

Full ID: 1OTYS3m4nK6oXpULY
Messages: 43
Distillations: 1
Time range: 2026-09-04 13:14:02 — 2026-09-04 13:23:37

Conversation (43 messages)

Run the final strict READ-ONLY adversarial audit. Do NOT edit files, regenerate artifacts, format files, or modify git state. Inspect complete current uncommitted diffs and related source in BOTH exact worktrees:

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

The latest prior must-fix findings claim remediation:

  1. action.yml Validate workspace now validates PATH_INPUT before any side effect (safe concrete relative POSIX segments, . root allowed) AND validates workspace segments. It must reject all traversal/absolute/empty/options/prototype/non-ASCII inputs before git/craft/network effects, preserve mutual exclusion, and titles must have repository plus exact full concrete workspace suffix.
  2. The action must never inherit CRAFT_WORKSPACE: both prepare and targets must explicitly clear it before considering the explicit action workspace input, so a root input always is root through prepare/targets/title/state.
  3. Publish has shared semantic repository/version validation used by BOTH detailsFromContext (publish controller) and getCiPollerInput (CI poller) before API endpoints/check-run parsing/checkout/state/publish. It must fail closed on malformed, dot, dotdot, proto, leading-dash, empty/non-token repository and option-like/incomplete/non-semver version. Preserve valid build metadata (4.2.6+sentry1).
  4. Craft glob results require physical realpath containment under workspace root and regression shows external symlink matches excluded.

Review complete design: exact full path matching/no normalization; action mutual exclusion; remote config root; glob safety + valid standard globs; pre-side-effect validation in all paths; root config fallback; revision before target checkout; fresh ci-ready only; state isolation + exact --rev; canonical path-only grammar; tests/generated parser/docs/diff hygiene. Beware regressions in action shell semantics and CI poller behavior. ...

gateway · 2026-09-04 13:14:02
result
No files found
gateway · 2026-09-04 13:14:14
result
No files found
gateway · 2026-09-04 13:14:19
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 13:14:22
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 13:14:28
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/AGENTS.md</path>
<type>file</type>
<content>
1: # AGENTS.md - Coding Agent Guidelines
2: 
3: This document provides guidelines for AI coding agents working in this repository.
4: 
5: ## Project Overview
6: 
7: This is the **Sentry Publish Repository** - a GitHub Actions-based approval system for publishing releases across multiple Sentry projects using [Craft](https://github.com/getsentry/craft). It's a pure JavaScript (Node.js 24.0.0) project using CommonJS modules.
8: 
9: ## Build/Lint/Test Commands
10: 
11: ### Package Manager
12: 
13: - **Yarn 1.22.22** (classic) - Do NOT use npm
14: - Version management via Volta (pinned in package.json)
15: 
16: ### Commands
17: 
18: ```bash
19: # Install dependencies
20: yarn install
21: 
22: # Run all tests
23: yarn test
24: 
25: # Run tests in watch mode
26: yarn test:watch
27: 
28: # Run a single test file
29: yarn test src/modules/__tests__/update-issue.js
30: 
31: # Run tests ...
gateway · 2026-09-04 13:14:36
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
 action.yml                              |  11 +-
 docs/src/content/docs/targets/github.md |  20 ++--
 src/__tests__/action.test.ts            | 101 ++++++++++++++----
 src/__tests__/config.test.ts            | 175 +++++++++++++++++++++++++++++-
 src/commands/prepare.ts                 |   3 +-
 src/config.ts                           | 183 +++++++++++++++++++++++++++++---
 src/schemas/project_config.ts           |  20 ++++
 7 files changed, 470 insertions(+), 43 deletions(-)
diff --git a/action.yml b/action.yml
index 94f3158..d160509 100644
--- a/action.yml
+++ b/action.yml
@@ -74,6 +74,7 @@ runs:
     - name: Validate workspace
       shell: bash
       env:
+        LC_ALL: C
         PATH_INPUT: ${{ inputs.path }}
         WORKSPACE: ${{ inputs.workspace }}
       run: |
@@ -81,8 +82,12 ...
gateway · 2026-09-04 13:14:41
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/ci-poller-input.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
?? src/modules/publish-issue-validation.js
 .github/workflows/publish.yml                 |   7 +-
 docs/publish-issue-format.md                  |  56 +++----
 src/modules/__tests__/ci-poller-input.js      |  37 ++++-
 src/modules/__tests__/details-from-context.js | 219 ++++++-------------------
 src/modules/__tests__/publish-location.js     |  94 +++++------
 src/modules/__tests__/publish-workflow.js     |  18 ++-
 src/modules/ci-poller-input.js                |  10 ++
 src/modules/details-from-context.js           |  35 ...
gateway · 2026-09-04 13:15:16
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/publish-issue-validation.js</path>
<type>file</type>
<content>
1: function isPublishRepository(name) {
2:   return (
3:     /^[A-Za-z0-9_.-]+$/.test(name) &&
4:     name !== "." &&
5:     name !== ".." &&
6:     name !== "__proto__" &&
7:     !name.startsWith("-")
8:   );
9: }
10: 
11: function isReleaseVersion(version) {
12:   return /^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(?:-?([0-9a-z-]+(?:\.[0-9a-z-]+)*))?(?:\+([0-9a-z-]+(?:\.[0-9a-z-]+)*))?$/i.test(
13:     version
14:   );
15: }
16: 
17: module.exports = { isPublishRepository, isReleaseVersion };

(End of file - total 17 lines)
</content>
gateway · 2026-09-04 13:15:23
result
Found 39 matches
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/version.ts:
  Line 42:  * SemVer is a parsed semantic version.

  Line 44: export interface SemVer {

  Line 63: export function parseVersion(text: string): SemVer | null {

  Line 81: export function versionGreaterOrEqualThan(v1: SemVer, v2: SemVer): boolean {

  Line 164:  * Returns the stringified version of the passed SemVer object.

  Line 166: export function semVerToString(s: SemVer) {


/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/symlink.ts:
  Line 7:   SemVer,

  Line 107: function getExistingSymlinkedVersion(symlinkPath: string): SemVer | null {


/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/packagePath.ts:
  Line 66: export function parseCanonical(canonicalName: string): string[] {


/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/git.ts:
  Line 212: function parseGitBranchOutput(output: string): string[] ...
gateway · 2026-09-04 13:15:28
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/version.ts</path>
<type>file</type>
<content>
1: import { getGitTagPrefix } from '../config';
2: 
3: /**
4:  * Regular expression for matching semver versions.
5:  *
6:  * Modified to match version components
7:  * Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
8:  * @see https://github.com/sindresorhus/semver-regex
9:  */
10: const semverRegex = () =>
11:   /\bv?(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(?:-?([\da-z-]+(?:\.[\da-z-]+)*))?(?:\+([\da-z-]+(?:\.[\da-z-]+)*))?\b/gi;
12: 
13: /**
14:  * Extracts a version number from the given text.
15:  *
16:  * In case the version contains a leading "v", it is stripped from the result.
17:  * All semantic versions are supported. See {@link http://semver.org/} for
18:  * more information. ...
gateway · 2026-09-04 13:15:38
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/details-from-context.js</path>
<type>file</type>
<content>
1: const { parse: parsePublishIssueTitle } = require("./publish-issue-title");
2: const { isPublishPath } = require("./publish-location");
3: const {
4:   isPublishRepository,
5:   isReleaseVersion,
6: } = require("./publish-issue-validation");
7: 
8: /**
9:  * Matches the entire "Targets" section of a github publish issue body.
10:  */
11: const TARGETS_SECTION_PARSER_REGEX =
12:   /^(?!### Targets$\s)(?: *- \[[ xX]\] \S+\s*$(?:\r?\n)?)+/m;
13: 
14: /**
15:  * Matches all targets of a github publish issue body in a section that was already matched and extracted with `TARGETS_PARSER_REGEX`.
16:  * The "id" of the targets is captured within a capture group. ...
gateway · 2026-09-04 13:15:44
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 13:15:50
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/publish/inputs.js</path>
<type>file</type>
<content>
1: const github = require('@actions/github');
2: const core = require('@actions/core');
3: const { detailsFromContext } = require('../modules/details-from-context');
4: 
5: async function inputs() {
6:     const result = await detailsFromContext({
7:       context: github.context,
8:     });
9:     core.setOutput('result', result);
10: }
11: 
12: 
13: inputs();

(End of file - total 13 lines)
</content>
gateway · 2026-09-04 13:16:30
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/ci-poller-input.js</path>
<type>file</type>
<content>
1: const { parse: parsePublishIssueTitle } = require("./publish-issue-title");
2: const {
3:   getReleaseRevision,
4:   updateReleaseRevision,
5: } = require("./release-revision");
6: const {
7:   isPublishRepository,
8:   isReleaseVersion,
9: } = require("./publish-issue-validation");
10: 
11: function getCiPollerInput({ title, issueBody, revision }) {
12:   let parsedTitle;
13:   try {
14:     parsedTitle = parsePublishIssueTitle(title);
15:   } catch {
16:     throw new Error(`Invalid publish issue title: '${title}'`);
17:   }
18:   const { repo, version } = parsedTitle;
19:   if (!isPublishRepository(repo)) {
20:     throw new Error(`Invalid publish issue repository: '${repo}'`);
21:   }
22:   if (!isReleaseVersion(version)) {
23:     throw new Error(`Invalid publish issue version: '${version}'`);
24:   }
25:   const currentRevision = getReleaseRevision({ ...
gateway · 2026-09-04 13:16:34
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 13:16:40
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 13:16:46
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 13:16:53
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 13:17:04
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/publish/resolve-ci-poller-input.js</path>
<type>file</type>
<content>
1: const { getCiPollerInput } = require("../modules/ci-poller-input");
2: 
3: process.stdout.write(
4:   JSON.stringify(
5:     getCiPollerInput({
6:       issueBody: process.env.PUBLISH_ISSUE_BODY || "",
7:       title: process.env.PUBLISH_TITLE || "",
8:       revision: process.env.PUBLISH_REVISION || "",
9:     })
10:   )
11: );

(End of file - total 11 lines)
</content>
gateway · 2026-09-04 13:17:17
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 13:17:41
result
Found 1 matches
/home/byk/Code/getsentry/craft-workspace-action-propagation/package.json:
  Line 49:     "glob": "^11.0.0",
gateway · 2026-09-04 13:18:10
result
Found 6 matches
/home/byk/Code/getsentry/craft-workspace-action-propagation/node_modules/glob/README.md:
  Line 198:   -f --follow            Follow symlinked directories when expanding '**'

  Line 522: > When `follow` and `nodir` are both set, then symbolic

  Line 558: - `follow` Follow symlinked directories when expanding `**`

  Line 562:   By default, a `**` in a pattern will follow 1 symbolic link if

  Line 567: > When `follow` and `nodir` are both set, then symbolic

  Line 718:   which case it follows all symbolic links.
gateway · 2026-09-04 13:18:16
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/node_modules/glob/README.md</path>
<type>file</type>
<content>
530: 
531: - `ignore` string or string[], or an object with `ignored` and
532:   `childrenIgnored` methods.
533: 
534:   If a string or string[] is provided, then this is treated as
535:   a glob pattern or array of glob patterns to exclude from
536:   matches. To ignore all children within a directory, as well
537:   as the entry itself, append `'/**'` to the ignore pattern.
538: 
539:   If an object is provided that has `ignored(path)` and/or
540:   `childrenIgnored(path)` methods, then these methods will be
541:   called to determine whether any Path is a match or if its
542:   children should be traversed, respectively.
543: 
544:   The `path` argument to the methods will be a
545:   [`path-scurry`](https://isaacs.github.io/path-scurry/index.html)
546:   [`Path`](https://isaacs.github.io/path-scurry/classes/PathBase)
547:   object, which extends
548:   ...
gateway · 2026-09-04 13:18:27
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/config.ts</path>
<type>file</type>
<content>
1: import { existsSync, lstatSync, readFileSync, realpathSync } from 'fs';
2: import path from 'path';
3: import { globSync, hasMagic } from 'glob';
4: 
5: import { load } from 'js-yaml';
6: import GitUrlParse from 'git-url-parse';
7: import { createGitClient } from './utils/git';
8: import { ZodError } from 'zod';
9: 
10: import { logger } from './logger';
11: import {
12:   CraftProjectConfig,
13:   CraftProjectConfigSchema,
14:   GitHubGlobalConfig,
15:   ArtifactProviderName,
16:   StatusProviderName,
17:   TargetConfig,
18:   ChangelogPolicy,
19:   VersioningPolicy,
20:   Workspace,
21: } from './schemas/project_config';
22: import { ConfigurationError } from './utils/errors';
23: import { isCompiledGitHubAction } from './utils/detection';
24: import {
25:   getPackageVersion,
26:   parseVersion,
27:   versionGreaterOrEqualThan,
28:   SemVer,
29: } from ...
gateway · 2026-09-04 13:18:46
result
Found 3 matches
/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/__tests__/details-from-context.js:
  Line 49: test("can parse version containing +", async () => {

  Line 56:           title: "publish: getsentry/sentry-forked-django-stubs@4.2.6+sentry1",

  Line 63:   expect(result.version).toEqual("4.2.6+sentry1");
gateway · 2026-09-04 13:18:53
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-09-04 13:19:14
result
Found 18 matches
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/publishState.ts:
  Line 34:  * `scripts/print-publish-state-path.ts` if present) that needs to

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

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

  Line 90:     ? `workspace-${Buffer.from(workspace).toString('base64url')}-`

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

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


/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 84:       expect(cli).toMatch(/-workspace-Y2xp-1\.2\.3\.json$/);

  Line 85:       expect(mcp).toMatch(/-workspace-bWNw-1\.2\.3\.json$/);

  Line 107:       ...
gateway · 2026-09-04 13:19:22
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 13:19:41
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 13:19:48
result
Found 7 matches
/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/ci-poller-input.js:
  Line 1: const { parse: parsePublishIssueTitle } = require("./publish-issue-title");

  Line 14:     parsedTitle = parsePublishIssueTitle(title);


/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/publish-issue-title.js:
  Line 198:     PublishIssueTitle: peg$parsePublishIssueTitle,

  Line 200:   let peg$startRuleFunction = peg$parsePublishIssueTitle;

  Line 426:   function peg$parsePublishIssueTitle() {


/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/details-from-context.js:
  Line 1: const { parse: parsePublishIssueTitle } = require("./publish-issue-title");

  Line 33:     titleDetails = parsePublishIssueTitle(context.payload.issue.title);
gateway · 2026-09-04 13:19:56
result
(no output)
gateway · 2026-09-04 13:20:04
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/__tests__/action.test.ts</path>
<type>file</type>
<content>
1: import {
2:   chmodSync,
3:   mkdtempSync,
4:   mkdirSync,
5:   readFileSync,
6:   rmSync,
7:   writeFileSync,
8: } from 'fs';
9: import { tmpdir } from 'os';
10: import { join } from 'path';
11: import { spawnSync } from 'child_process';
12: 
13: import { load } from 'js-yaml';
14: import { afterEach, expect, test } from 'vitest';
15: 
16: interface ActionStep {
17:   env?: Record<string, string>;
18:   name?: string;
19:   run?: string;
20: }
21: 
22: function getActionSteps(): ActionStep[] {
23:   const action = load(
24:     readFileSync(join(__dirname, '../../action.yml'), 'utf8'),
25:   ) as {
26:     runs?: { steps?: ActionStep[] };
27:   };
28:   return action.runs?.steps || [];
29: }
30: 
31: function getActionStep(name: string): ActionStep {
32:   const step = getActionSteps().find(step => step.name === name);
33:   if (!step?.run) {
34:     ...
gateway · 2026-09-04 13:20:40
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/__tests__/config.test.ts</path>
<type>file</type>
<content>
350:   test('a different workspace resolves independently', () => {
351:     setActiveWorkspace('mcp');
352:     const config = loadConfigurationFromString(WS_CONFIG);
353:     expect(config.releaseBranchPrefix).toBe('release/mcp');
354:     expect(getGitTagPrefix()).toBe('mcp@');
355:     expect(getVersioningPolicy()).toBe('calver');
356:     expect(config.github).toEqual({ owner: 'getsentry', repo: 'toolkit' });
357:   });
358: 
359:   test('errors when workspaces are defined but none is selected', () => {
360:     setActiveWorkspace(undefined);
361:     expect(() => loadConfigurationFromString(WS_CONFIG)).toThrow(
362:       /defines workspaces; select one/,
363:     );
364:   });
365: 
366:   test('lists concrete workspace names without requiring a selection', () => {
367:     setActiveWorkspace(undefined);
368:     const directory = ...
gateway · 2026-09-04 13:20:46
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/schemas/project_config.ts</path>
<type>file</type>
<content>
200: /**
201:  * Configuration for a single named workspace (release unit).
202:  *
203:  * A workspace mirrors the release-relevant subset of the top-level config;
204:  * every field is optional and inherits the top-level value when omitted. The
205:  * `github` block is *partial* (all fields optional) so a workspace can override
206:  * `owner` and/or `repo` while inheriting the rest from the top-level `github`.
207:  */
208: export const WorkspaceSchema = z.object({
209:   ...releaseUnitFields,
210:   github: GitHubGlobalConfigSchema.partial()
211:     .refine(github => github.projectPath === undefined, {
212:       message: 'Workspace github.projectPath is not supported.',
213:     })
214:     .optional(),
215: });
216: 
217: export type Workspace = z.infer<typeof WorkspaceSchema>;
218: 
219: function isSafeWorkspaceGlobSegment(segment: string): ...
gateway · 2026-09-04 13:21:31
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/__tests__/publish-workflow.js</path>
<type>file</type>
<content>
1: import { spawnSync } from "child_process";
2: import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "fs";
3: import { tmpdir } from "os";
4: import { join } from "path";
5: 
6: import { afterEach, expect, test } from "vitest";
7: 
8: const { resolvePublishLocation } = require("../publish-location.js");
9: 
10: const temporaryDirectories = [];
11: 
12: function getWorkflow() {
13:   return readFileSync(
14:     join(__dirname, "../../../.github/workflows/publish.yml"),
15:     "utf8"
16:   );
17: }
18: 
19: function getSetTargetsScript() {
20:   const workflow = getWorkflow();
21:   const section = workflow.match(
22:     / {6}- name: Set targets[\s\S]*? {8}run: \|\n(?<script>[\s\S]*?)\n {6}- uses: docker:\/\/getsentry\/craft:latest/
23:   );
24:   if (!section?.groups?.script) {
25:     throw new Error("Missing Set targets workflow ...
gateway · 2026-09-04 13:21:40
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 the complete suffix as a workspace after checking
9: // out the CI-approved revision.
10: PublishIssueTitle
11:   = "publish: " "getsentry/"? repo:Repository path:Path? "@" version:Version !. {
12:       return {
13:         repo,
14:         path: path || "",
15:         version,
16:       };
17:     }
18: 
19: Repository
20:   = characters:RepositoryCharacter+ { return join(characters); }
21: 
22: RepositoryCharacter
23:   = [A-Za-z0-9_.-]
24: 
25: Path
26:   = segments:("/" segment:PathSegment { return `/${segment}`; })+ { return join(segments); }
27: 
28: PathSegment
29:   = characters:RepositoryCharacter+ { return join(characters); }
30: 
31: ...
gateway · 2026-09-04 13:22:24
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/prepare.ts</path>
<type>file</type>
<content>
790:  *
791:  * @param argv Command-line arguments
792:  */
793: export async function prepareMain(argv: PrepareOptions): Promise<any> {
794:   let git = await getGitClient();
795: 
796:   // Handle --config-from: load config from remote branch. The caller
797:   // is responsible for ensuring the branch is trusted — the remote
798:   // config's preReleaseCommand will be executed by Craft.
799:   if (argv.configFrom) {
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:       const ...
gateway · 2026-09-04 13:22:33
result
Found 15 matches
/home/byk/Code/getsentry/craft-workspace-action-propagation/node_modules/glob/README.md:
  Line 215:   --nobrace              Do not expand {...} patterns

  Line 323: Brace expansion is not considered "magic" unless the

  Line 324: `magicalBraces` option is set, as brace expansion just turns one

  Line 329: `magicalBraces:true` is in the options, brace expansion _is_

  Line 348: If the `windowsPathsNoEscape` option is used, then square-brace

  Line 354: When `windowsPathsNoEscape` is not set, then both brace escapes

  Line 474: - `magicalBraces` Treat brace expansion like `{a,b}` as a "magic"

  Line 475:   pattern. Has no effect if {@link nobrace} is set.

  Line 493: - `nobrace` Do not expand `{a,b}` and `{1..3}` brace sets.

  Line 664: > already be added before its ancestor, if multiple or braced

  Line 677: Before parsing the path part patterns, braced sections are

  Line 678: expanded into a set. ...
gateway · 2026-09-04 13:22:38
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/node_modules/glob/README.md</path>
<type>file</type>
<content>
310: 
311: Syncronous form of `globStream()`. Will read all the matches as
312: fast as you consume them, even all in a single tick if you
313: consume them immediately, but will still respond to backpressure
314: if they're not consumed immediately.
315: 
316: Alias: `glob.stream.sync()`, `glob.sync.stream()`
317: 
318: ## `hasMagic(pattern: string | string[], options?: GlobOptions) => boolean`
319: 
320: Returns `true` if the provided pattern contains any "magic" glob
321: characters, given the options provided.
322: 
323: Brace expansion is not considered "magic" unless the
324: `magicalBraces` option is set, as brace expansion just turns one
325: string into an array of strings. ...
gateway · 2026-09-04 13:22:45
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/publish-issue-title.js</path>
<type>file</type>
<content>
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 (input.substr(peg$currPos, 9) === peg$c0) {
431:       s1 = peg$c0;
432:       peg$currPos += 9;
433:     } else {
434:       s1 = peg$FAILED;
435:       if (peg$silentFails === 0) {
436:         peg$fail(peg$e0);
437:       }
438:     }
439:     if (s1 !== peg$FAILED) {
440: ...
gateway · 2026-09-04 13:22:57
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:                     "@", version ;
14: repository        = token, { token } ;
15: path              = "/", path-segment, { "/", path-segment } ;
16: path-segment      = token, { token } ;
17: version           = version-character, { version-character } ;
18: token             = ? ASCII letter, digit, ".", "_", or "-" ? ...
gateway · 2026-09-04 13:23:37
  • MUST-FIX — publish-workspace-acceptance/src/modules/ci-poller-input.js:18-25: The CI poller validates repository and version but never validates parsedTitle.path. A title such as publish: getsentry/toolkit/../other@1.2.3 reaches check-run parsing and cross-repo API calls at .github/workflows/ci-poller.yml:100-206; only the publish controller rejects it later at details-from-context.js:48-50. Validate '.' + parsedTitle.path with isPublishPath() before getReleaseRevision(), and add CI-poller regressions for traversal, dot, __proto__, and option-like segments.

  • MUST-FIX — state identity collision: craft-workspace-action-propagation/src/utils/publishState.ts:46-56,82-98; publish-workspace-acceptance/.github/workflows/publish.yml:218-228: Both sides lowercase and sanitize the version in the state filename. Valid distinct versions 4.2.6+sentry1 and 4.2.6+Sentry1 pass publish-issue-validation.js:11-14 but resolve to the same 4.2.6_sentry1 state file. A failed release can therefore make another release skip targets. Use lossless collision-safe version encoding on both sides and test distinct prerelease/build-metadata variants.

  • MUST-FIX — craft-workspace-action-propagation/src/schemas/project_config.ts:230-232; src/config.ts:287-302: Safe standard brace globs such as packages/{cli,mcp} are rejected because both allowlists omit {, }, and ,, despite hasMagic(..., {magicalBraces: true}). This breaks required standard-glob support. Validate every brace alternative safely and add a positive brace-expansion regression alongside the existing unsafe-alternative tests at src/__tests__/config.test.ts:498-515.

  • PASS — remediation 1: craft-workspace-action-propagation/action.yml:74-92 is the first action step, rejects unsafe path and workspace segments before the first Git/Craft/network step at line 94, preserves mutual exclusion, and permits only . as the root path. Full workspace titles are emitted at ...

gpt-5.6-terra · 2026-09-04 13:23:37 · ~939 tokens

Distillations (1)

Gen 0 2026-09-04 13:25:44 · 1514 tokens

Date: Sep 4, 2026 * 🔴 [requested-read-only-audit] (13:14) User requested a final strict READ-ONLY adversarial audit of complete current uncommitted diffs and related source in both exact worktrees: `/home/byk/Code/getsentry/craft-workspace-action-propagation` and `/home/byk/Code/getsentry/publish-workspace-acceptance`. * 🔴 (13:14) User directed that the audit must not edit files, regenerate art…