DashboardcraftSession 0zg0KOV0iM0B

Session 0zg0KOV0iM0B

Full ID: 0zg0KOV0iM0BE974a
Messages: 58
Distillations: 2
Time range: 2026-09-04 23:01:57 — 2026-09-04 23:18:40

Conversation (58 messages)

Perform one final strict READ-ONLY adversarial audit of the CURRENT exact diffs and relevant source. Do not edit or modify files/git state.

Worktrees:

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

IMPORTANT: getsentry/craft:latest is intentional and REQUIRED. It must stay floating for the latest released Craft workspace behavior; target checkout SHA and craft publish --rev bind target operations. Do NOT report it.

The only prior MUST-FIX was nested malformed brace handling. Verify the correction precisely:

  • packages/{cli and packages/{cli} reject;
  • nested malformed packages/{cli,{mcp}} and nested empty packages/{cli,{{},mcp}} reject with no valid branch admitted;
  • valid nested packages/{cli,{mcp,api}} expands correctly;
  • helper uses a non-ambiguous invalid sentinel and propagates failures rather than silently flatMap-dropping them;
  • schema/runtime are consistent and validation happens before glob access.

Audit all previous required contracts for regressions: safe brace/character/negated-class/globstar and unsafe path/realpath containment; poller semantic identity/path validation before API calls; lossless identical Craft/Publish version-state names; action validation/ambient workspace/full title; revision checkout order/exact resolution/missing config fallback/fresh CI-ready/external state/--rev; parser-generated-doc test parity and scope hygiene.

Give only severity-ranked current file:line findings. Explicit PASS/CONCERN/MUST-FIX for each contract. End exactly MERGE or DO-NOT-MERGE. If blocked, start BLOCKED with exact reason. Never return empty.

gateway · 2026-09-04 23:01:57
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 23:02:20
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 23:02:44
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 src/utils/__tests__/publishState.test.ts
 M src/utils/publishState.ts
feat/workspace-action-propagation
4862056 feat: support compact release workspaces
6dae341 feat: propagate release workspaces
21270a1 feat(config): add release workspaces
32f5462 fix(git): prevent fatal error for missing 'origin' remote (#871)
5d0ec43 fix(registry): retry registry push with backoff on concurrent updates (#870)
21270a1d12a60b461c51fba93bd076b90d9cf4af
 .github/workflows/release.yml                      |   9 +
 action.yml                                         |  33 ++-
 docs/src/content/docs/targets/github.md            |  36 ++-
 src/__tests__/action.test.ts                       | 248 +++++++++++++++++++++
 src/__tests__/config.test.ts                       | 136 +++++++----
 ...
gateway · 2026-09-04 23:03:09
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
feat/workspace-acceptance
52ef600 feat: resolve compact workspace publish requests
b658ffa fix: validate publish workspace JSON
33de3f1 feat: accept workspace publish requests
83d210b build(deps): bump js-yaml from 4.3.0 to 4.3.1 (#9174)
5268a23 feat: Add Vercel creds
fatal: Not a valid object name origin/master
gateway · 2026-09-04 23:03:16
result
origin	https://github.com/getsentry/craft.git (fetch)
origin	https://github.com/getsentry/craft.git (push)
  byk/chore/prettier-reformat
  byk/fix/actions-node24-deprecations
  byk/fix/changelog-code-blocks
  byk/fix/changelog-commit-without-prerelease
+ byk/fix/commit-on-repo
+ byk/fix/dependabot-alerts-865
  byk/fix/smol-toml-security
+ byk/pr865-fixes
  ci/fix-node20-deprecation-warnings
  dependabot/npm_and_yarn/docs/astro-6.1.6
  dependabot/npm_and_yarn/tar-7.5.3
  docs/improve-registry-target-docs
  docs/improve-registry-target-docs-v2
  feat/actions-publish-repo-self
+ feat/ci-ready-signal
  feat/improve-onboarding
  feat/internal-changes-commit-types
  feat/npm-oidc-trusted-publishing
  feat/prefixed-tags
  feat/security-commit-prefix
* feat/workspace-action-propagation
+ feat/workspaces-schema
  feat/workspaces-threading
  fix/action-artifact-name
  fix/bump-brace-expansion
  fix/calver-disable-publish-issue-mentions
  fix/changelog-preview-fork-prs
  ...
gateway · 2026-09-04 23:03:20
result
origin	https://github.com/getsentry/publish.git (fetch)
origin	https://github.com/getsentry/publish.git (push)
fatal: Needed a single revision
  chore/fix-security-advisories
* feat/workspace-acceptance
  fix/add-workflow-permissions
+ main
+ opencode/tidy-orchid
  security/dual-write-publish-state
  security/remove-legacy-publish-state-write
  remotes/origin/HEAD -> origin/main
  remotes/origin/feat/ci-poller-variable-gate
  remotes/origin/feat/ci-status-poller
  remotes/origin/feat/workspace-acceptance
  remotes/origin/fix/ci-poller-gh-token
  remotes/origin/fix/ci-poller-variable-auth
  remotes/origin/fix/react-native-release-8-14-craft-config
  remotes/origin/main
  remotes/origin/revert-twine-verbose
gateway · 2026-09-04 23:03:24
result
83d210b29553ea6f4d97508821724a834c43856e
 .github/workflows/ci-poller.yml                |  32 +-
 .github/workflows/cocoapods-keepalive.yml      |   2 +-
 .github/workflows/publish.yml                  |  88 +--
 README.md                                      |   5 +
 docs/publish-issue-format.md                   |  86 +++
 package.json                                   |   5 +-
 scripts/generate-publish-issue-title-parser.js |  40 ++
 src/modules/__tests__/ci-poller-input.js       |  97 ++++
 src/modules/__tests__/details-from-context.js  | 131 ++++-
 src/modules/__tests__/publish-location.js      |  91 ++++
 src/modules/__tests__/publish-workflow.js      | 153 ++++++
 src/modules/__tests__/release-revision.js      | 103 ++++
 src/modules/__tests__/update-issue.js          |  15 +-
 src/modules/ci-poller-input.js                 |  42 ++
 src/modules/details-from-context.js            |  31 +-
 src/modules/publish-issue-title.js             | 725 +++++++++++++++++++++++++
 ...
gateway · 2026-09-04 23:03:30
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 23:03:45
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/schemas/project_config.ts</path>
<type>file</type>
<content>
1: import { z } from 'zod';
2: import { hasMagic } from 'glob';
3: 
4: /**
5:  * DEPRECATED: Use changelog.policy instead. Different policies for changelog management
6:  */
7: export const enum ChangelogPolicy {
8:   Auto = 'auto',
9:   Simple = 'simple',
10:   None = 'none',
11: }
12: 
13: /**
14:  * Name of the status provider
15:  */
16: export const enum StatusProviderName {
17:   GitHub = 'github',
18: }
19: 
20: /**
21:  * Name of the artifact provider
22:  */
23: export const enum ArtifactProviderName {
24:   GCS = 'gcs',
25:   GitHub = 'github',
26:   None = 'none',
27: }
28: 
29: /**
30:  * Default versioning policy when no version argument is provided. ...
gateway · 2026-09-04 23:03:52
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/__tests__/config.test.ts</path>
<type>file</type>
<content>
1: import { describe, test, expect, vi, afterEach, beforeEach } from 'vitest';
2: import { mkdtempSync, mkdirSync, rmSync, symlinkSync, writeFileSync } from 'fs';
3: import { tmpdir } from 'os';
4: import { join } from 'path';
5: /**
6:  * Tests of our ability to read craft config files. (This is NOT general test
7:  * configuration).
8:  */
9: 
10: import {
11:   getGitTagPrefix,
12:   loadConfigurationFromString,
13:   validateConfiguration,
14:   setActiveWorkspace,
15:   getActiveWorkspace,
16:   getConfiguration,
17:   getVersioningPolicy,
18:   getWorkspaceNames,
19:   WORKSPACES_MIN_VERSION,
20: } from '../config';
21: import { CraftProjectConfigSchema } from '../schemas/project_config';
22: import { logger } from '../logger';
23: 
24: describe('validateConfiguration', () => {
25:   test('parses minimal configuration', () => {
26:     const data = { ...
gateway · 2026-09-04 23:03:58
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 23:04:01
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/ci-poller-input.js</path>
<type>file</type>
<content>
1: const { parse: parsePublishIssueTitle } = require("./publish-issue-title");
2: const {
3:   getReleaseRevision,
4:   updateReleaseRevision,
5: } = require("./release-revision");
6: const {
7:   isPublishRepository,
8:   isReleaseVersion,
9: } = require("./publish-issue-validation");
10: const { isPublishPath } = require("./publish-location");
11: 
12: function getCiPollerInput({ title, issueBody, revision }) {
13:   let parsedTitle;
14:   try {
15:     parsedTitle = parsePublishIssueTitle(title);
16:   } catch {
17:     throw new Error(`Invalid publish issue title: '${title}'`);
18:   }
19:   const { repo, version } = parsedTitle;
20:   if (!isPublishRepository(repo)) {
21:     throw new Error(`Invalid publish issue repository: '${repo}'`);
22:   }
23:   if (!isReleaseVersion(version)) {
24:     throw new Error(`Invalid publish issue version: ...
gateway · 2026-09-04 23:04:07
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 23:04:10
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/publish-location.js</path>
<type>file</type>
<content>
1: function resolvePublishLocation({ path, workspaceNames }) {
2:   if (!isPublishPath(path)) {
3:     throw new Error("Invalid publish path.");
4:   }
5: 
6:   if (!workspaceNames.every(isWorkspaceName)) {
7:     throw new Error(
8:       "Craft workspace discovery returned an invalid workspace list."
9:     );
10:   }
11: 
12:   if (path === ".") {
13:     return { path };
14:   }
15: 
16:   const workspace = path.slice(2);
17:   if (workspaceNames.includes(workspace)) {
18:     return { path: ".", workspace };
19:   }
20: 
21:   return { path };
22: }
23: 
24: function isPublishPath(path) {
25:   return (
26:     typeof path === "string" &&
27:     (path === "." ||
28:       (path.startsWith("./") &&
29:         path
30:           .slice(2)
31:           .split("/")
32:           .every(isSafeWorkspaceSegment)))
33:   );
34: }
35: 
36: function ...
gateway · 2026-09-04 23:04:13
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/publish/resolve-location.js</path>
<type>file</type>
<content>
1: const core = require("@actions/core");
2: const { resolvePublishLocation } = require("../modules/publish-location");
3: 
4: function resolveLocation() {
5:   const input = JSON.parse(process.env.PUBLISH_ARGS || "");
6:   const workspaceNames = JSON.parse(process.env.CRAFT_WORKSPACE_NAMES || "");
7: 
8:   if (!Array.isArray(workspaceNames)) {
9:     throw new Error(
10:       "Craft workspace discovery returned an invalid workspace list."
11:     );
12:   }
13: 
14:   core.setOutput(
15:     "result",
16:     resolvePublishLocation({
17:       path: input.path,
18:       workspaceNames,
19:     })
20:   );
21: }
22: 
23: resolveLocation();

(End of file - total 23 lines)
</content>
gateway · 2026-09-04 23:04:24
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 23:04:31
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/publish-issue-title.js</path>
<type>file</type>
<content>
1: /* eslint-disable */
2: // @generated by Peggy 5.1.0.
3: //
4: // https://peggyjs.org/
5: 
6: "use strict";
7: 
8: class peg$SyntaxError extends SyntaxError {
9:   constructor(message, expected, found, location) {
10:     super(message);
11:     this.expected = expected;
12:     this.found = found;
13:     this.location = location;
14:     this.name = "SyntaxError";
15:   }
16: 
17:   format(sources) {
18:     let str = "Error: " + this.message;
19:     if (this.location) {
20:       let src = null;
21:       const st = sources.find((s) => s.source === this.location.source);
22:       if (st) {
23:         src = st.text.split(/\r\n|\n|\r/g);
24:       }
25:       const s = this.location.start;
26:       const offset_s =
27:         this.location.source &&
28:         typeof this.location.source.offset === "function"
29:           ? ...
gateway · 2026-09-04 23:04:36
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 23:04: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 23:04:57
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 23:05:23
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 23:05:31
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 23:05:38
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 23:05:48
result
Found 33 matches
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/publishState.ts:
  Line 89: export function getPublishStateFilename(

  Line 118: export function getPublishStatePath(

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


/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/__tests__/publishState.test.ts:
  Line 7:   getPublishStateFilename,

  Line 8:   getPublishStatePath,

  Line 42:   describe('getPublishStateFilename', () => {

  Line 46:       const name = getPublishStateFilename(

  Line 57:       const a = getPublishStateFilename(

  Line 62:       const b = getPublishStateFilename(

  Line 71:       const cli = getPublishStateFilename(

  Line 77:       const mcp = getPublishStateFilename(

  Line 91:         getPublishStateFilename(

  Line 104:         getPublishStateFilename(version, { owner: 'o', repo: 'r' }, cwd),

  Line 117:       const name = getPublishStateFilename(

  Line 126:       ...
gateway · 2026-09-04 23:05:55
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/publish.ts</path>
<type>file</type>
<content>
1: import { Arguments, Argv, CommandBuilder } from 'yargs';
2: import chalk from 'chalk';
3: import { existsSync, mkdirSync, readFileSync } from 'fs';
4: 
5: import { safeFs } from '../utils/dryRun';
6: import { dirname, join } from 'path';
7: import * as shellQuote from 'shell-quote';
8: import stringLength from 'string-length';
9: 
10: import {
11:   getConfiguration,
12:   getStatusProviderFromConfig,
13:   getArtifactProviderFromConfig,
14:   DEFAULT_RELEASE_BRANCH_NAME,
15:   getGlobalGitHubConfig,
16:   expandWorkspaceTargets,
17:   getNoMergeConfig,
18:   getActiveWorkspace,
19: } from '../config';
20: import { formatTable, logger } from '../logger';
21: import {
22:   type GitHubGlobalConfig,
23:   TargetConfig,
24: } from '../schemas/project_config';
25: import { getAllTargetNames, getTargetByName, SpecialTarget } from '../targets';
26: import { ...
gateway · 2026-09-04 23:06:08
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 23:06:13
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 23:06:21
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 23:06:26
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 23:06:50
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 23:07:15
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/docs/publish-issue-format.md</path>
<type>file</type>
<content>
1: # Publish Issue Format
2: 
3: The Publish workflow treats a publish issue as a release request. The title identifies
4: the release; the body supplies the merge target and selected targets. Do not edit the
5: title by hand unless it remains valid under this format.
6: 
7: ## Title
8: 
9: Every title starts with `publish: `. 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 23:07:23
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/__tests__/ci-poller-input.js</path>
<type>file</type>
<content>
1: import { readFileSync } from "fs";
2: import { join } from "path";
3: 
4: import { expect, test } from "vitest";
5: 
6: const { getCiPollerInput } = require("../ci-poller-input.js");
7: 
8: const REVISION = "7e5ca7ed5581552de066e2a8bc295b8306be38ac";
9: const issueBody = `Requested by: @byk
10: 
11: Merge target: (default)
12: 
13: Quick links:
14: - [View changes](https://github.com/getsentry/toolkit/compare/1.2.2...release/1.2.3)
15: - [View check runs](https://github.com/getsentry/toolkit/commit/${REVISION}/checks/)`;
16: 
17: test("parses root and full-path publish titles", () => {
18:   expect(
19:     getCiPollerInput({
20:       issueBody,
21:       title: "publish: getsentry/toolkit/cli@1.2.3",
22:     })
23:   ).toEqual({
24:     repo: "getsentry/toolkit",
25:     revision: REVISION,
26:     version: "1.2.3",
27:   });
28: 
29:   ...
gateway · 2026-09-04 23:07:30
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 23:08:10
result
Found 13 matches
/home/byk/Code/getsentry/publish-workspace-acceptance/scripts/generate-publish-issue-title-parser.js:
  Line 11:   "publish-issue-title.peggy"

  Line 18:   "publish-issue-title.js"

  Line 21: const parser = peggy.generate(grammar, {

  Line 23:   grammarSource: "publish-issue-title.peggy",

  Line 27: const generatedParser = prettier.format(`/* eslint-disable */\n${parser}`, {

  Line 35:       "The generated publish issue title parser is stale. Run `yarn generate`."


/home/byk/Code/getsentry/publish-workspace-acceptance/package.json:
  Line 18:     "generate": "node scripts/generate-publish-issue-title-parser.js",

  Line 19:     "check:generated": "node scripts/generate-publish-issue-title-parser.js --check",

  Line 20:     "test": "yarn check:generated && vitest run",


/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/ci-poller-input.js:
  Line 1: const { parse: parsePublishIssueTitle } = ...
gateway · 2026-09-04 23:08:41
result
Found 2 matches
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/version.ts:
  Line 37: export function isValidVersion(text: string): boolean {

  Line 123:   return isValidVersion(text) && !!text.match(PREVIEW_RELEASE_REGEX);
gateway · 2026-09-04 23:09:16
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 23:09:38
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 23:09:44
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 23:09:50
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-09-04 23:10:18
result
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 @@ runs:
           echo "::error::The path and workspace inputs cannot be used together."
           exit 1
         fi
-        if [[ -n "$WORKSPACE" ]] && ! node -e 'process.exit(/^(?!\.{1,2}$)(?!__proto__$)(?!-)[A-Za-z0-9_.-]+$/.test(process.env.WORKSPACE) ? 0 : 1)'; then
-          echo "::error::Workspace names must use only ASCII letters, digits, periods, underscores, and hyphens."
+        if ! node -e 'const path = process.env.PATH_INPUT; const segment = /^[A-Za-z0-9_.-]+$/; process.exit(path === "." || (typeof path === "string" && path.split("/").every(name => segment.test(name) && ![".", "..", "__proto__"].includes(name) && !name.startsWith("-"))) ? ...
gateway · 2026-09-04 23:10:44
result
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index f6dc96a..23c546e 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -172,12 +172,7 @@ jobs:
           PUBLISH_ARGS: ${{ steps.inputs.outputs.result }}
         run: |
           workspace_names='[]'
-          requires_workspace_discovery="$(node -e '
-            const { needsWorkspaceDiscovery } = require("./.__publish__/src/modules/publish-location");
-            const input = JSON.parse(process.env.PUBLISH_ARGS || "");
-            process.stdout.write(String(needsWorkspaceDiscovery(input)));
-          ')"
-          if [[ "$requires_workspace_discovery" == "true" && -f __repo__/.craft.yml ]]; then
+          if [[ -f __repo__/.craft.yml ]]; then
             workspace_names="$(docker run --rm \
               --volume "$GITHUB_WORKSPACE/__repo__:/github/workspace/__repo__" \
               --workdir /github/workspace/__repo__ \
@@ -224,13 +219,18 @@ jobs:
           ...
gateway · 2026-09-04 23:11:11
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/update-issue.js</path>
<type>file</type>
<content>
1: const fs = require("fs");
2: const {
3:   TARGETS_SECTION_PARSER_REGEX,
4:   TARGETS_PARSER_REGEX,
5: } = require("./details-from-context");
6: 
7: async function updateTargets({
8:   octokit,
9:   stateFilePath,
10:   publishRepo,
11:   issue_number,
12: }) {
13:   if (!stateFilePath || !fs.existsSync(stateFilePath)) {
14:     return;
15:   }
16: 
17:   const issueRequest = octokit.rest.issues.get({
18:     ...publishRepo,
19:     issue_number,
20:   });
21: 
22:   const craftStateRequest = fs.promises
23:     .readFile(stateFilePath, { encoding: "utf-8" })
24:     .then((data) => JSON.parse(data));
25: 
26:   const [{ data: issue }, craftState] = await Promise.all([
27:     issueRequest,
28:     craftStateRequest,
29:   ]);
30: 
31:   const newIssueBody = transformIssueBody(craftState, issue.body);
32: 
33:   await octokit.rest.issues.update({
34:     ...
gateway · 2026-09-04 23:11:37
result
diff --git a/.github/workflows/ci-poller.yml b/.github/workflows/ci-poller.yml
index a3db430..324b63f 100644
--- a/.github/workflows/ci-poller.yml
+++ b/.github/workflows/ci-poller.yml
@@ -1,232 +1,226 @@
 name: CI Status Poller
 
 on:
   schedule:
     - cron: "*/5 * * * *"
   workflow_dispatch:
     inputs:
       attempt:
         description: Self-dispatch attempt counter (internal use)
         required: false
         default: "0"
 
 permissions:
   contents: read
   issues: read
 
 jobs:
   check-ci:
     runs-on: ubuntu-latest
     environment: production
     # Skip entirely (no runner provisioned) when there's nothing to check.
     # Set to "true" by ci-pending.yml, reset to "false" here when done.
     # Always allow workflow_dispatch for manual recovery.
     if: vars.CI_POLLER_HAS_PENDING == 'true' || github.event_name == 'workflow_dispatch'
     concurrency:
       group: ci-status-poller
       cancel-in-progress: false
     steps:
       # sentry-internal-app token ...
gateway · 2026-09-04 23:11:55
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-09-04 23:12:13
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 23:12:34
result
Found 20 matches
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/helpers.ts:
  Line 42: export function extractWorkspaceSelection(


/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/__tests__/helpers.test.ts:
  Line 10:   extractWorkspaceSelection,

  Line 37: describe('extractWorkspaceSelection', () => {

  Line 42:       extractWorkspaceSelection(['publish', '1.0.0'], NO_ENV),

  Line 48:       extractWorkspaceSelection(['publish', '--workspace', 'cli'], NO_ENV),

  Line 53:     expect(extractWorkspaceSelection(['--workspace=mcp'], NO_ENV)).toBe('mcp');

  Line 57:     expect(extractWorkspaceSelection(['--workspace=-cli'], NO_ENV)).toBe(

  Line 61:       extractWorkspaceSelection(['--workspace', '-cli'], NO_ENV),

  Line 67:       extractWorkspaceSelection(['--workspace', 'cli'], {

  Line 75:       extractWorkspaceSelection(

  Line 84:       extractWorkspaceSelection(

  Line 93:       extractWorkspaceSelection(['publish'], {

  Line ...
gateway · 2026-09-04 23:12:42
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 23:12:50
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/workspace_cmds/list.ts</path>
<type>file</type>
<content>
1: import { getWorkspaceNames } from '../../config';
2: import { formatJson } from '../../utils/strings';
3: 
4: export const command = ['list'];
5: export const description = 'List defined release workspaces as a JSON array';
6: 
7: export function handler(): void {
8:   console.log(formatJson(getWorkspaceNames()));
9: }

(End of file - total 9 lines)
</content>
gateway · 2026-09-04 23:13:05
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-09-04 23:13:31
result
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index cff6bc3..23c546e 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -118,151 +118,164 @@ jobs:
     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: ${{ steps.inputs.outputs.result }}
+          PUBLISH_ISSUE_BODY: ${{ github.event.issue.body }}
+        run: node ...
gateway · 2026-09-04 23:13:57
result
Found 1 matches
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/publish.ts:
  Line 933: export async function getRevisionBranchName(
gateway · 2026-09-04 23:14:12
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/publish.ts</path>
<type>file</type>
<content>
900:       }
901:       lines.push(
902:         ``,
903:         `Error: ${mergeError instanceof Error ? mergeError.message : String(mergeError)}`,
904:       );
905:       logger.warn(lines.join('\n'));
906:     }
907: 
908:     // XXX(BYK): intentionally DO NOT await unlinking as we do not want
909:     // to block (both in terms of waiting for IO and the success of the
910:     // operation) finishing the publish flow on the removal of a temporary
911:     // file. If unlinking fails, we honestly don't care, at least to fail
912:     // the final steps. And it doesn't make sense to wait until this op
913:     // finishes then as nothing relies on the removal of this file.
914:     safeFs
915:       .unlink(publishStateFile)
916:       .catch((err: unknown) =>
917:         logger.trace("Couldn't remove publish state file: ", err),
918:       );
919:     ...
gateway · 2026-09-04 23:14:26
result
diff --git a/src/commands/publish.ts b/src/commands/publish.ts
index 30bb834..dad43f3 100644
--- a/src/commands/publish.ts
+++ b/src/commands/publish.ts
@@ -1,71 +1,74 @@
 import { Arguments, Argv, CommandBuilder } from 'yargs';
 import chalk from 'chalk';
 import { existsSync, mkdirSync, readFileSync } from 'fs';
 
 import { safeFs } from '../utils/dryRun';
 import { dirname, join } from 'path';
 import * as shellQuote from 'shell-quote';
 import stringLength from 'string-length';
 
 import {
   getConfiguration,
   getStatusProviderFromConfig,
   getArtifactProviderFromConfig,
   DEFAULT_RELEASE_BRANCH_NAME,
   getGlobalGitHubConfig,
   expandWorkspaceTargets,
   getNoMergeConfig,
   getActiveWorkspace,
 } from '../config';
 import { formatTable, logger } from '../logger';
-import { TargetConfig } from '../schemas/project_config';
+import {
+  type GitHubGlobalConfig,
+  TargetConfig,
+} from '../schemas/project_config';
 import { getAllTargetNames, getTargetByName, SpecialTarget } ...
gateway · 2026-09-04 23:15:48
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/__tests__/publish.test.ts</path>
<type>file</type>
<content>
1: import { vi, describe, test, expect, beforeEach, type Mock } from 'vitest';
2: import { join as pathJoin } from 'path';
3: import { spawnProcess, hasExecutable } from '../../utils/system';
4: import {
5:   getPublishStateGitHubConfig,
6:   getRevisionBranchName,
7:   runPostReleaseCommand,
8:   handleReleaseBranch,
9:   MergeConflictError,
10:   PushError,
11: } from '../publish';
12: import { getPublishStateFilename } from '../../utils/publishState';
13: import type { SimpleGit } from 'simple-git';
14: 
15: vi.mock('../../utils/system');
16: vi.mock('../../utils/git', () => ({
17:   getDefaultBranch: vi.fn().mockResolvedValue('main'),
18:   getGitClient: vi.fn(),
19:   isRepoDirty: vi.fn(),
20:   findReleaseBranches: vi.fn(),
21: }));
22: 
23: describe('runPostReleaseCommand', () => {
24:   const newVersion = '2.3.4';
25:   const ...
gateway · 2026-09-04 23:17:15
result
HEAD 48620564f86d8724982cddb6506c040235fe8954
base 21270a1d12a60b461c51fba93bd076b90d9cf4af
working tree  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 src/utils/__tests__/publishState.test.ts
 M src/utils/publishState.ts
diff sha256 c368e0271a7da6b04aec416162bfbf9dc3d2e8bdedd4716cb237903f71fefd6d  -
whitespace 
gateway · 2026-09-04 23:17:33
result
HEAD 52ef600d6759911d4b76d7f3108c5a41c3e0ec2a
base 83d210b29553ea6f4d97508821724a834c43856e
working tree  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
diff sha256 8d150b5831c747629ff4c0765a4282ebe9e99393c4bc1a95ab0121ab3f143f8c  -
whitespace 
gateway · 2026-09-04 23:18:40

MUST-FIX

  • Scope hygiene: publish/.github/workflows/cocoapods-keepalive.yml:1 has an unrelated quote-only change. Revert it.

PASS

  • Nested braces: Craft rejects all specified malformed forms and propagates failure without dropping branches: src/config.ts:330-390, src/schemas/project_config.ts:249-319; coverage: src/__tests__/config.test.ts:451-459,523-532.
  • Safe globs and containment: validation precedes glob access; character classes, negated classes, globstars, unsafe paths, and realpath escapes are handled: src/config.ts:231-328, src/schemas/project_config.ts:219-334.
  • Poller validation: repository, version, and path validate before target API calls: publish/src/modules/ci-poller-input.js:12-30, .github/workflows/ci-poller.yml:100-114.
  • State identity: Craft and Publish use matching lossless workspace/version filename encoding: craft/src/utils/publishState.ts:51-105; publish/.github/workflows/publish.yml:217-233.
  • Action propagation: inputs validate first, ambient workspace clears, and titles preserve full paths: craft/action.yml:74-92,191-224,275.
  • Revision and CI gate: revision resolves before checkout; missing config falls back safely; publishing requires fresh ci-ready; Craft receives --rev: publish/.github/workflows/publish.yml:138-182,239-261; publish/src/modules/release-revision.js:1-27.
  • Parser/docs parity: generated-parser check runs with tests and grammar/docs agree: publish/package.json:18-20, scripts/generate-publish-issue-title-parser.js:31-37, docs/publish-issue-format.md:9-47.

DO-NOT-MERGE

gpt-5.6-terra · 2026-09-04 23:18:40 · ~397 tokens

Distillations (2)

Gen 0 2026-09-04 23:20:40 · 276 tokens

Date: Sep 4, 2026 * 🟡 (23:03) User provided additional excerpt of `src/schemas/project_config.ts`, showing `StatusProvider` includes `Manual = 'manual'`; target configuration includes required `owner: z.string()` and optional `excludeNames: z.string().optional()`. * 🟡 (23:03) The `src/schemas/project_config.ts` excerpt defines `BaseStatusProviderSchema`, `BaseArtifactProviderSchema`, `GitHubArt…

Gen 0 2026-09-04 23:12:57 · 1325 tokens

Date: Sep 4, 2026 * 🔴 (23:03) User stated package-management preference: always use `pnpm`; never use `npm` or `yarn`. * 🔴 (23:03) User stated Node.js is managed by Volta, currently v22.12.0. * 🔴 (23:03) User stated dependencies must be installed with `pnpm install --frozen-lockfile`. * 🔴 (23:03) User stated tests use Vitest; test files are in `src/__tests__/`, follow the `*.test.ts` naming p…