DashboardcraftSession 0qxwIy69SGpB

Session 0qxwIy69SGpB

Full ID: 0qxwIy69SGpBZQKbJ
Messages: 53
Distillations: 2
Time range: 2026-09-04 10:34:47 — 2026-09-04 10:46:00

Conversation (53 messages)

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

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

The prior audit found and the implementation claims to fix: A) resolvePublishLocation() now validates workspaceNames BEFORE its root-path return, so invalid discovery must fail for root and non-root releases before state/publish side effects. B) Craft configured literal workspace keys with glob-only invalid characters (], !, ^) must fail. Safe actual glob patterns including *, ?, [cm], [!a], [^a], and ** remain accepted. All literal/concrete workspace names must obey nonempty ASCII [A-Za-z0-9_.-]+ segments excluding ., .., __proto__, and leading -.

Audit all contracts:

  1. Full exact concrete workspace paths in Action titles and mutual exclusion with checkout paths.
  2. CI-approved revision resolves before exact target checkout and workspace discovery; exact unnormalized match behavior, root-config fallback, discovery errors fail closed.
  3. All untrusted title/discovered/path values rejected before checkout/state/publish, including root discovery output.
  4. Craft config glob/literal safety, remote --config-from repository-root expansion, POSIX paths, supported glob semantics and root containment.
  5. Fresh ci-ready gate only.
  6. External state identity/isolation and exact --rev.
  7. New canonical titles are path-only; no legacy JSON support is required.
  8. Test coverage includes both former issues; parser generated parity; docs correctness; no formatter churn; exact diff scope.

Return ONLY severity-ordered findings with exact file:line citations. Include PASS/CONCERN/MUST-FIX for each numbered contract. End exactly MERGE or DO-NOT-MERGE. No generic praise and no empty result.

gateway · 2026-09-04 10:34:47
result
<path>/home/byk/Code/getsentry</path>
<type>directory</type>
<entries>
cli-api-1423/
cli-wt/
cli/
codecov-action/
craft-vercel-prebuilt-output/
craft-vercel-project-config/
craft-workspace-action-propagation/
craft-workspaces-schema/
craft/
devinfra-coder-infra/
devinfra-deployment-service/
getsentry/
ops/
package_grant_publish_repo/
publish-workspace-acceptance/
publish/
security-as-code/
self-hosted/
sentry-api-schema/
sentry-docs/
sentry-forge/
sentry-go/
sentry-javascript/
sentry-orbital/
sentry-python/
sentry-release-registry/
sentry-wizard/
sentry/
snuba/
spotlight/
sqlish/
static-sites/
stripe-app/
symbolic/

(34 entries)
</entries>
gateway · 2026-09-04 10:35:44
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 10:35:51
result
 M action.yml
 M docs/src/content/docs/targets/github.md
 M src/__tests__/action.test.ts
 M src/__tests__/config.test.ts
 M src/commands/prepare.ts
 M src/config.ts
 M src/schemas/project_config.ts
 action.yml                              |   4 +-
 docs/src/content/docs/targets/github.md |  20 ++--
 src/__tests__/action.test.ts            |  59 ++++++++---
 src/__tests__/config.test.ts            | 153 ++++++++++++++++++++++++++-
 src/commands/prepare.ts                 |   3 +-
 src/config.ts                           | 177 +++++++++++++++++++++++++++++---
 src/schemas/project_config.ts           |  20 ++++
 7 files changed, 395 insertions(+), 41 deletions(-)
diff --git a/action.yml b/action.yml
index 94f3158..84377a8 100644
--- a/action.yml
+++ b/action.yml
@@ -81,8 +81,8 @@ runs:
           echo "::error::The path and workspace inputs cannot be used together."
           exit 1
         fi
-        if [[ -n "$WORKSPACE" ]] && ! node -e ...
gateway · 2026-09-04 10:35:58
result
 M .github/workflows/publish.yml
 M docs/publish-issue-format.md
 M src/modules/__tests__/ci-poller-input.js
 M src/modules/__tests__/details-from-context.js
 M src/modules/__tests__/publish-location.js
 M src/modules/__tests__/publish-workflow.js
 M src/modules/details-from-context.js
 M src/modules/publish-issue-title.js
 M src/modules/publish-issue-title.peggy
 M src/modules/publish-location.js
 M src/publish/resolve-location.js
 .github/workflows/publish.yml                 |   7 +-
 docs/publish-issue-format.md                  |  52 +++---
 src/modules/__tests__/ci-poller-input.js      |   4 +-
 src/modules/__tests__/details-from-context.js | 191 ++--------------------
 src/modules/__tests__/publish-location.js     |  94 +++++------
 src/modules/__tests__/publish-workflow.js     |  18 ++-
 src/modules/details-from-context.js           |  27 +---
 src/modules/publish-issue-title.js            | 225 +++-----------------------
 src/modules/publish-issue-title.peggy         |  19 ...
gateway · 2026-09-04 10:36:08
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 10:36:13
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/action.yml</path>
<type>file</type>
<content>
175:     - name: Craft Prepare
176:       id: craft
177:       shell: bash
178:       env:
179:         CRAFT_LOG_LEVEL: Debug
180:         CRAFT_CONFIG_FROM_MERGE_TARGET: ${{ inputs.craft_config_from_merge_target }}
181:         MERGE_TARGET: ${{ inputs.merge_target }}
182:         VERSION: ${{ inputs.version }}
183:         WORKSPACE: ${{ inputs.workspace }}
184:       working-directory: ${{ inputs.path }}
185:       run: |
186:         # Ensure we have origin/HEAD set
187:         git remote set-head origin --auto
188: 
189:         # Build command with optional flags
190:         CRAFT_ARGS=()
191:         if [[ "$CRAFT_CONFIG_FROM_MERGE_TARGET" == 'true' && -n "$MERGE_TARGET" ]]; then
192:           CRAFT_ARGS=(--config-from "$MERGE_TARGET")
193:         fi
194:         if [[ -n "$WORKSPACE" ]]; then
195:           CRAFT_ARGS+=("--workspace=$WORKSPACE")
196:         ...
gateway · 2026-09-04 10:36:17
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/config.ts</path>
<type>file</type>
<content>
1: import { existsSync, lstatSync, readFileSync } from 'fs';
2: import path from 'path';
3: 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 './utils/version';
30: // ...
gateway · 2026-09-04 10:36:20
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/schemas/project_config.ts</path>
<type>file</type>
<content>
180:  */
181: const releaseUnitFields = {
182:   github: GitHubGlobalConfigSchema.optional(),
183:   targets: z.array(TargetConfigSchema).optional(),
184:   preReleaseCommand: z.string().optional(),
185:   postReleaseCommand: z.string().optional(),
186:   releaseBranchPrefix: z.string().optional(),
187:   changelog: ChangelogConfigSchema.optional(),
188:   changelogPolicy: z.enum(['auto', 'simple', 'none']).optional(),
189:   requireNames: z.array(z.string()).optional(),
190:   statusProvider: BaseStatusProviderSchema.optional(),
191:   artifactProvider: BaseArtifactProviderSchema.optional(),
192:   versioning: VersioningConfigSchema.optional(),
193:   /**
194:    * Do not merge the release branch after publishing.
195:    * Defaults to true for compiled GitHub Actions (Node.js actions with dist/ folder).
196:    */
197:   noMerge: ...
gateway · 2026-09-04 10:36:35
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 10:36:41
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 10:36:45
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: 
4: /**
5:  * Matches the entire "Targets" section of a github publish issue body.
6:  */
7: const TARGETS_SECTION_PARSER_REGEX =
8:   /^(?!### Targets$\s)(?: *- \[[ xX]\] \S+\s*$(?:\r?\n)?)+/m;
9: 
10: /**
11:  * Matches all targets of a github publish issue body in a section that was already matched and extracted with `TARGETS_PARSER_REGEX`.
12:  * The "id" of the targets is captured within a capture group.
13:  */
14: const TARGETS_PARSER_REGEX = /^\s*- \[[ x]\] (\S+)/gim;
15: 
16: /**
17:  * Matches checked targets of a github publish issue body in a section that was already matched and extracted with `TARGETS_PARSER_REGEX`.
18:  * The "id" of the targets is captured within a capture group. ...
gateway · 2026-09-04 10:36:53
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 10:36:58
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 10:37:04
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 10:37:10
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 10:37:14
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 10:37:20
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 10:37:55
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/prepare.ts</path>
<type>file</type>
<content>
730:     }
731: 
732:     // Build CalVer config with overrides
733:     const calverOffset =
734:       options.calverOffset ??
735:       (process.env.CRAFT_CALVER_OFFSET
736:         ? Number.parseInt(process.env.CRAFT_CALVER_OFFSET, 10)
737:         : undefined) ??
738:       config.versioning?.calver?.offset ??
739:       DEFAULT_CALVER_CONFIG.offset;
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:         ? ...
gateway · 2026-09-04 10:38:00
result
Found 10 matches
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/index.ts:
  Line 20: import { setActiveWorkspace } from './config';

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

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

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

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

  Line 99:   setActiveWorkspace(extractWorkspaceSelection(argv));

  Line 112:     .command(workspace)

  Line 126:     .option('workspace', {

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

  Line 130:         'Required when the config defines "workspaces". Env: CRAFT_WORKSPACE',
gateway · 2026-09-04 10:38:04
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 10:38:10
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 10:38:16
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 10:38:22
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, 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 = { github: { ...
gateway · 2026-09-04 10:38:27
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 10:38:37
result
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/publish-issue-title.js</path>
<type>file</type>
<content>
180: 
181:     return (
182:       "Expected " +
183:       describeExpected(expected) +
184:       " but " +
185:       describeFound(found) +
186:       " found."
187:     );
188:   }
189: }
190: 
191: function peg$parse(input, options) {
192:   options = options !== undefined ? options : {};
193: 
194:   const peg$FAILED = {};
195:   const peg$source = options.grammarSource;
196: 
197:   const peg$startRuleFunctions = {
198:     PublishIssueTitle: peg$parsePublishIssueTitle,
199:   };
200:   let peg$startRuleFunction = peg$parsePublishIssueTitle;
201: 
202:   const peg$c0 = "publish: ";
203:   const peg$c1 = "getsentry/";
204:   const peg$c2 = "@";
205:   const peg$c3 = "/";
206: 
207:   const peg$r0 = /^[A-Za-z0-9_.\-]/;
208:   const peg$r1 = /^[A-Za-z0-9_.+\-]/;
209: 
210:   const peg$e0 = peg$literalExpectation("publish: ", false);
211:   const peg$e1 ...
gateway · 2026-09-04 10:38:43
result
Found 13 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 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 107:       expect(name).toMatch(/^publish-state-o-r-[0-9a-f]{12}-1\.2\.3\.json$/);

  Line 118:       expect(name).toMatch(/^publish-state-weird_owner-re_po-[0-9a-f]{12}-/);

  Line 123:       ...
gateway · 2026-09-04 10:38:53
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/publish.ts</path>
<type>file</type>
<content>
130:       description: 'Do not check for build status',
131:       type: 'boolean',
132:     })
133:     .option('no-git-checks', {
134:       default: false,
135:       description: 'Ignore local git changes and unsynchronized remotes',
136:       type: 'boolean',
137:     })
138:     .check(checkVersion)
139:     .demandOption('new-version', 'Please specify the version to publish');
140: };
141: 
142: /** Command line options. */
143: export interface PublishOptions {
144:   /** The git remote to use when pushing */
145:   remote: string;
146:   /** Revision to publish (can be commit, tag, etc.) */
147:   rev?: string;
148:   /** Target branch to merge the release into, auto detected when empty */
149:   mergeTarget?: string;
150:   /** One or more targets we want to publish */
151:   target?: string | string[];
152:   /** The new version to publish */
153:   ...
gateway · 2026-09-04 10:38:57
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 10:39:15
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 10:39:20
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 10:39:35
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: 
7: function getCiPollerInput({ title, issueBody, revision }) {
8:   let parsedTitle;
9:   try {
10:     parsedTitle = parsePublishIssueTitle(title);
11:   } catch {
12:     throw new Error(`Invalid publish issue title: '${title}'`);
13:   }
14:   const { repo, version } = parsedTitle;
15:   const currentRevision = getReleaseRevision({ issueBody, repo });
16: 
17:   return {
18:     ...(revision
19:       ? { issueBody: updateReleaseRevision({ issueBody, repo, revision }) }
20:       : {}),
21:     repo: `getsentry/${repo}`,
22:     revision: currentRevision,
23:     version,
24:   };
25: }
26: 
27: module.exports = { getCiPollerInput };

(End of file - total 27 lines)
</content>
gateway · 2026-09-04 10:39:44
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 10:39:50
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/docs/src/content/docs/targets/github.md</path>
<type>file</type>
<content>
55:   owner: getsentry
56:   repo: toolkit
57: releaseBranchPrefix: release/cli
58: targets:
59:   - name: github
60:     tagPrefix: "cli@"
61: ```
62: 
63: ```yaml
64: # .craft.yml for the MCP product
65: github:
66:   owner: getsentry
67:   repo: toolkit
68: releaseBranchPrefix: release/mcp
69: targets:
70:   - name: github
71:     tagPrefix: "mcp@"
72: ```
73: 
74: Releasing `1.2.3` for each product then produces the tags `cli@1.2.3` / `mcp@1.2.3` on release branches `release/cli/1.2.3` / `release/mcp/1.2.3` — no collisions.
75: 
76: ## Release Workspaces
77: 
78: Use top-level `workspaces:` to define independently versioned release units in
79: one repository. Keys may be literal paths or glob patterns, and Craft expands
80: patterns to concrete directories relative to `.craft.yml`. ...
gateway · 2026-09-04 10:40:17
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
gateway · 2026-09-04 10:40:22
result
 M .github/workflows/publish.yml
 M docs/publish-issue-format.md
 M src/modules/__tests__/ci-poller-input.js
 M src/modules/__tests__/details-from-context.js
 M src/modules/__tests__/publish-location.js
 M src/modules/__tests__/publish-workflow.js
 M src/modules/details-from-context.js
 M src/modules/publish-issue-title.js
 M src/modules/publish-issue-title.peggy
 M src/modules/publish-location.js
 M src/publish/resolve-location.js
gateway · 2026-09-04 10:40:30
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 10:41:04
result
Found 5 matches
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/schemas/project_config.ts:
  Line 44: export const GitHubGlobalConfigSchema = z.object({

  Line 46:   repo: z.string(),

  Line 50: export type GitHubGlobalConfig = z.infer<typeof GitHubGlobalConfigSchema>;

  Line 182:   github: GitHubGlobalConfigSchema.optional(),

  Line 210:   github: GitHubGlobalConfigSchema.partial()
gateway · 2026-09-04 10:41:08
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 10:41:15
result
Found 22 matches
/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/__tests__/ci-poller-input.js:
  Line 26:     version: "1.2.3",

  Line 37:     version: "1.2.3",


/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/__tests__/publish-location.js:
  Line 58:     ).toThrow("Invalid publish path.");


/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/__tests__/publish-workflow.js:
  Line 30: function runSetTargets({ path, repo, version, workspace = "" }) {

  Line 42:       CRAFT_PUBLISH_VERSION: version,

  Line 93:     "craft publish ${{ fromJSON(steps.inputs.outputs.result).version }} --rev ${{ steps.release-revision.outputs.revision }}"

  Line 111:     version: "21.3.1",

  Line 128:     version: "1.2.3",


/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/__tests__/update-issue.js:
  Line 9:   inputs: { repo: "sentry", version: "21.3.1" ...
gateway · 2026-09-04 10:41:21
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 10:41:27
result
Found 1 matches
/home/byk/Code/getsentry/craft-workspace-action-propagation/package.json:
  Line 49:     "glob": "^11.0.0",
gateway · 2026-09-04 10:41:33
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/package.json</path>
<type>file</type>
<content>
30:     "@types/node-fetch": "^2.5.10",
31:     "@types/ora": "^1.3.4",
32:     "@types/prompts": "^2.0.11",
33:     "@types/semver": "^7.7.1",
34:     "@types/shell-quote": "^1.6.0",
35:     "@types/tar": "^4.0.0",
36:     "@types/tmp": "^0.0.33",
37:     "@types/yargs": "^17",
38:     "async": "3.2.2",
39:     "aws4": "^1.11.0",
40:     "chalk": "4.1.1",
41:     "cli-table": "0.3.1",
42:     "consola": "2.15.3",
43:     "esbuild": "^0.28.1",
44:     "eslint": "^9.17.0",
45:     "eslint-config-prettier": "^9.1.0",
46:     "eslint-formatter-github-annotations": "^0.1.0",
47:     "fast-xml-parser": "^5.8.0",
48:     "git-url-parse": "^16.1.0",
49:     "glob": "^11.0.0",
50:     "is-ci": "^2.0.0",
51:     "js-yaml": "4.3.1",
52:     "mkdirp": "^1.0.4",
53:     "mustache": "3.0.1",
54:     "nock": "^13.2.4",
55:     "node-fetch": "^2.6.1",
56:     "node-stream-zip": ...
gateway · 2026-09-04 10:41:44
result
packages/*: true
packages/?li: true
packages/[cm]: true
packages/[!a]: true
packages/[^a]: true
packages/**: true
packages/foo]: false
packages/foo!: false
packages/foo^: false
packages/*]: true
packages/*!: true
gateway · 2026-09-04 10:41:55
result
!packages/*: true
packages/!foo: false
packages/!(foo): true
packages/@(foo): true
packages/+(foo): true
packages/{foo,bar}: true
packages/[a-z]: true
gateway · 2026-09-04 10:42:16
result
No files found
gateway · 2026-09-04 10:42:21
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/node_modules/glob</path>
<type>directory</type>
<entries>
dist/
LICENSE.md
package.json
README.md

(4 entries)
</entries>
gateway · 2026-09-04 10:42:30
result
Found 11 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 564:   first item in the pattern, following the same behavior as Bash.

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

  Line 683: The following characters have special magic meaning when used in

  Line 714:   `{follow:true}` is passed in the options object. A pattern

  Line 717:   or 0 if it is the first item, unless `follow:true` is set, in

  Line 718:   which case it follows all symbolic links.

  Line 772: > setting `{follow:true}` in the options.
gateway · 2026-09-04 10:42:35
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 10:42:50
result
Usage: realpath [OPTION]... FILE...
Print the resolved absolute file name;
all but the last component must exist

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

GNU coreutils online help: <https://www.gnu.org/software/coreutils/>
Full documentation <https://www.gnu.org/software/coreutils/realpath>
or available ...
gateway · 2026-09-04 10:44:25
result
Found 18 matches
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/publish.ts:
  Line 54: import { getPublishStatePath } from '../utils/publishState';

  Line 177: export function getPublishStateGitHubConfig(

  Line 716:   let publishStateGithubConfig = null;

  Line 718:     publishStateGithubConfig = await getGlobalGitHubConfig();

  Line 720:     // Fall through with null — getPublishStatePath() handles this by

  Line 724:   const publishStateFile = getPublishStatePath(

  Line 726:     getPublishStateGitHubConfig(publishStateGithubConfig),

  Line 732:   logger.debug(`Publish state file path: ${publishStateFile}`);

  Line 744:         `Craft now stores publish state at "${publishStateFile}". ` +

  Line 749:   const earlierStateExists = existsSync(publishStateFile);

  Line 750:   let publishState: PublishState;

  Line 753:     publishState = JSON.parse(readFileSync(publishStateFile).toString());

  Line 756:     publishState = { published: ...
gateway · 2026-09-04 10:44:30
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/publish.ts</path>
<type>file</type>
<content>
680:   const revision = await git.revparse('HEAD');
681:   logger.debug('Revision to publish: ', revision);
682: 
683:   const statusProvider = await getStatusProviderFromConfig();
684:   const artifactProvider = await getArtifactProviderFromConfig();
685: 
686:   // Check status of all CI builds linked to the revision
687:   await checkRevisionStatus(statusProvider, revision, argv.noStatusCheck);
688: 
689:   await printRevisionSummary(artifactProvider, revision);
690: 
691:   await checkRequiredArtifacts(artifactProvider, revision, config.requireNames);
692: 
693:   // Find targets
694:   let targetsToPublish: Set<string> = new Set(
695:     (typeof argv.target === 'string' ? [argv.target] : argv.target) || [
696:       SpecialTarget.All,
697:     ],
698:   );
699: 
700:   // Treat "all"/"none" specially
701:   for (const specialTarget of [SpecialTarget.All, ...
gateway · 2026-09-04 10:46:00

MUST-FIX — Contract 3: Title parsing accepts unsafe repository identities and option-like/non-semver versions. Only path is validated before checkout/state writes; repo reaches checkout and version reaches state creation and craft publish. src/modules/publish-issue-title.peggy:19-32, src/modules/details-from-context.js:38-40, .github/workflows/publish.yml:159-166, .github/workflows/publish.yml:184-230, .github/workflows/publish.yml:242-244.

MUST-FIX — Contract 4: Glob root containment is lexical, not physical. A supported ** glob can traverse a symlinked directory; path.resolve() and lstatSync() on its descendant accept an outside directory without a realpath() containment check. src/config.ts:241-255.

PASS — Contract 1: Action rejects simultaneous path/workspace inputs and emits the full workspace path in titles; acceptance resolves only exact full-path matches. action.yml:80-86, action.yml:268, src/modules/publish-location.js:16-21.

PASS — Contract 2: The CI-approved revision resolves before target checkout and discovery; root configuration discovery occurs only after checkout and invalid discovery fails before root-path return. .github/workflows/publish.yml:138-182, src/modules/publish-location.js:6-19.

PASS — Contract 5: Publishing requires a fresh ci-ready label event plus accepted/ready and no pending/failed labels. .github/workflows/publish.yml:102-114.

PASS — Contract 6: State identity includes repository, physical cwd hash, encoded workspace, and version; publishing uses the resolved revision via --rev. .github/workflows/publish.yml:212-230, .github/workflows/publish.yml:242-256.

PASS — Contract 7: The canonical grammar has path-only titles and no legacy JSON workspace production or parsing. src/modules/publish-issue-title.peggy:7-32, docs/publish-issue-format.md:12-43.

PASS — Contract 8: Regression coverage includes invalid root discovery and invalid literal ], !, ^ keys; generated parser ...

gpt-5.6-terra · 2026-09-04 10:46:00 · ~533 tokens

Distillations (2)

Gen 0 2026-09-04 10:44:19 · 996 tokens

Date: Sep 4, 2026 * 🔴 (10:35) User stated that Action publish titles should always carry the full concrete workspace path. * 🟡 (10:35) Read-only audit evidence: `/home/byk/Code/getsentry` contains 34 entries, including the two audited worktrees `craft-workspace-action-propagation/` and `publish-workspace-acceptance/`. * 🟡 (10:35) Read-only audit evidence: `/home/byk/Code/getsentry/craft-worksp…

Gen 0 2026-09-04 10:36:23 · 650 tokens

Date: Sep 4, 2026 * 🔴 [requested-review] [enforced-read-only-audit] (10:34) User requested a final strict READ-ONLY adversarial audit of complete current uncommitted diffs in both exact worktrees: `/home/byk/Code/getsentry/craft-workspace-action-propagation` and `/home/byk/Code/getsentry/publish-workspace-acceptance`. User prohibited editing files, running formatters/generators, and changing git…