DashboardcraftSession 0DURWGP3WfIN

Session 0DURWGP3WfIN

Full ID: 0DURWGP3WfINmnFMa
Messages: 14
Distillations: 1
Time range: 2026-09-08 17:14:04 — 2026-09-08 17:17:35

Conversation (14 messages)

Strict read-only adversarial review in /home/byk/Code/getsentry/craft-workspace-action-propagation of the current uncommitted fix for Craft PR #872 against current HEAD f174ceafa57355e71617f32ba1b0a030953be0a2. Do not edit or mutate files. Review src/config.ts and src/tests/config.test.ts plus relevant callers. Verify the realpath error handling skips only broken/racy glob candidates, cannot hide unsafe entries or bypass lexical/physical containment, maintains deterministic behavior, and regression genuinely fails before the fix. Check diff hygiene. Return severity-ranked file:line findings, explicit PASS/FAIL, test gaps, and end exactly MERGE or DO-NOT-MERGE.

gateway · 2026-09-08 17:14:04
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/AGENTS.md</path>
<type>file</type>
<content>
1: # AGENTS.md
2: 
3: This file provides guidance for AI coding assistants working with the Craft codebase.
4: 
5: ## Package Management
6: 
7: - **Always use `pnpm`** for package management. Never use `npm` or `yarn`.
8: - Node.js version is managed by [Volta](https://volta.sh/) (currently v22.12.0).
9: - Install dependencies with `pnpm install --frozen-lockfile`.
10: 
11: ## Development Commands
12: 
13: | Command      | Description                                 |
14: | ------------ | ------------------------------------------- |
15: | `pnpm build` | Build the project (outputs to `dist/craft`) |
16: | `pnpm test`  | Run tests                                   |
17: | `pnpm lint`  | Run ESLint                                  |
18: | `pnpm fix`   | Auto-fix lint issues                        |
19: 
20: To manually test changes:
21: 
22: ```bash
23: pnpm build && ...
gateway · 2026-09-08 17:14:48
result
 M src/__tests__/config.test.ts
 M src/config.ts
f174ceafa57355e71617f32ba1b0a030953be0a2
M	src/__tests__/config.test.ts
M	src/config.ts
diff --git a/src/__tests__/config.test.ts b/src/__tests__/config.test.ts
index 619c940..80968d1 100644
--- a/src/__tests__/config.test.ts
+++ b/src/__tests__/config.test.ts
@@ -501,6 +501,27 @@ describe('workspaces', () => {
     expect(getWorkspaceNames()).toEqual(['packages/internal/release']);
   });
 
+  test('ignores broken symlinks matched by workspace globs', () => {
+    const directory = mkdtempSync(join(tmpdir(), 'craft-workspaces-'));
+    temporaryDirectories.push(directory);
+    mkdirSync(join(directory, 'packages', 'cli'), { recursive: true });
+    symlinkSync(
+      join(directory, 'missing-workspace'),
+      join(directory, 'packages', 'broken'),
+    );
+    writeFileSync(
+      join(directory, '.craft.yml'),
+      [
+        `minVersion: ${WORKSPACES_MIN_VERSION}`,
+        'workspaces:',
+        '  packages/*: {}',
+      ...
gateway · 2026-09-08 17:15:06
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-08 17:15:13
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/__tests__/config.test.ts</path>
<type>file</type>
<content>
380:     expect(() => getWorkspaceNames()).toThrow(
381:       `requires minVersion >= ${WORKSPACES_MIN_VERSION}`,
382:     );
383:   });
384: 
385:   test('expands a workspace glob into concrete directory paths', () => {
386:     const directory = mkdtempSync(join(tmpdir(), 'craft-workspaces-'));
387:     temporaryDirectories.push(directory);
388:     mkdirSync(join(directory, 'packages', 'cli'), { recursive: true });
389:     mkdirSync(join(directory, 'packages', 'mcp'), { recursive: true });
390:     writeFileSync(join(directory, 'packages', 'README.md'), 'not a workspace');
391:     writeFileSync(
392:       join(directory, '.craft.yml'),
393:       [
394:         `minVersion: ${WORKSPACES_MIN_VERSION}`,
395:         'workspaces:',
396:         '  packages/*:',
397:         '    releaseBranchPrefix: release/package',
398:       ].join('\n'),
399:     ...
gateway · 2026-09-08 17:15:35
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-08 17:15:40
result
Found 68 matches
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/index.ts:
  Line 20: import { setActiveWorkspace } from './config';

  Line 99:   setActiveWorkspace(extractWorkspaceSelection(argv));


/home/byk/Code/getsentry/craft-workspace-action-propagation/src/config.ts:
  Line 72:  * (single implicit release unit). Set once via `setActiveWorkspace` from the

  Line 84: export function setActiveWorkspace(name: string | undefined): void {

  Line 124:     const available = getWorkspaceNamesFromConfig(base, workspaceDirectory);

  Line 197:     ? getWorkspaceGlobMatches(key, workspaceDirectory).includes(workspaceName)

  Line 201: function getWorkspaceNamesFromConfig(

  Line 210:       ? ...
gateway · 2026-09-08 17:15:43
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/config.ts</path>
<type>file</type>
<content>
420: }
421: 
422: function checkWorkspacesMinVersion(config: CraftProjectConfig): void {
423:   if (!isVersionGteMinVersion(config.minVersion, WORKSPACES_MIN_VERSION)) {
424:     throw new ConfigurationError(
425:       `Using "workspaces" requires minVersion >= ${WORKSPACES_MIN_VERSION} ` +
426:         'in the configuration file.',
427:     );
428:   }
429: }
430: 
431: /**
432:  * SemVer build metadata does not affect precedence, but the comparison helper
433:  * intentionally rejects versions carrying it. Strip it before compatibility
434:  * checks so config values such as `2.29.0+linux` remain valid.
435:  */
436: function withoutBuildMetadata(version: SemVer): SemVer {
437:   return version.build ? { ...version, build: undefined } : version;
438: }
439: 
440: /**
441:  * Applies workspace selection + validation to a freshly-parsed config. ...
gateway · 2026-09-08 17:16:06
result
   246	    dot: true,
   247	    ignore: ['**/node_modules/**'],
   248	    posix: true,
   249	  })
   250	    .filter(match => {
   251	      const resolvedMatch = path.resolve(root, match);
   252	      const realMatch = realpathSync(resolvedMatch);
   253	      return (
   254	        isSafeWorkspacePath(match) &&
   255	        (resolvedMatch === root ||
   256	          resolvedMatch.startsWith(`${root}${path.sep}`)) &&
   257	        (realMatch === realRoot ||
   258	          realMatch.startsWith(`${realRoot}${path.sep}`)) &&

 RUN  v4.1.8 /home/byk/Code/getsentry/craft-workspace-action-propagation


 Test Files  1 passed (1)
      Tests  68 passed (68)
   Start at  17:16:07
   Duration  1.48s (transform 383ms, setup 0ms, import 1.16s, tests 121ms, environment 0ms)

gateway · 2026-09-08 17:16:35
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/index.ts</path>
<type>file</type>
<content>
70:     result.push(arg);
71:     if (arg.slice(2) in GLOBAL_BOOLEAN_FLAGS) {
72:       result.push('1');
73:     }
74:   }
75:   return result;
76: }
77: 
78: /**
79:  * Main entrypoint
80:  */
81: async function main(): Promise<void> {
82:   // Strip dynamic-linker env vars (LD_PRELOAD, DYLD_*, ...) before anything
83:   // else so they cannot leak into subprocesses Craft spawns.
84:   sanitizeDynamicLinkerEnv();
85: 
86:   printVersion();
87: 
88:   warnIfCraftEnvFileExists();
89: 
90:   const argv = fixGlobalBooleanFlags(process.argv.slice(2));
91: 
92:   // Resolve the active workspace BEFORE parsing. yargs runs command `builder`s
93:   // (which may read the configuration, e.g. `publish` derives its --target
94:   // choices from config.targets) *before* middleware, so setting the workspace
95:   // via middleware would be too late — the builder would ...
gateway · 2026-09-08 17:16:41
result
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/prepare.ts</path>
<type>file</type>
<content>
775:     const currentVersion = (latestTag && getVersion(latestTag)) || '0.0.0';
776: 
777:     const newVersion = calculateNextVersion(currentVersion, bumpType);
778:     logger.info(
779:       `Version bump: ${currentVersion} -> ${newVersion} (${bumpType} bump)`,
780:     );
781:     return newVersion;
782:   }
783: 
784:   // Explicit semver version - return as-is
785:   return version;
786: }
787: 
788: /**
789:  * Body of 'prepare' command
790:  *
791:  * @param argv Command-line arguments
792:  */
793: export async function prepareMain(argv: PrepareOptions): Promise<any> {
794:   let git = await getGitClient();
795: 
796:   // Handle --config-from: load config from remote branch. The caller
797:   // is responsible for ensuring the branch is trusted — the remote
798:   // config's preReleaseCommand will be executed by Craft.
799:   if (argv.configFrom) ...
gateway · 2026-09-08 17:16:51
result
No files found
gateway · 2026-09-08 17:17:06
result
No files found
gateway · 2026-09-08 17:17:35

MUST-FIX — High

  • src/config.ts:253-258: The unconditional catch suppresses every realpathSync failure, not only broken or raced-away candidates. It silently drops permission failures (EACCES/EPERM), symlink loops (ELOOP), and other unexpected I/O errors. Because lexical and physical containment checks occur only at :260-265, these entries never receive validation or diagnostics. This can hide unsafe or ambiguous filesystem state, though it cannot admit a candidate past containment checks. Rethrow non-disappearance errors.

MUST-FIX — Medium

  • src/__tests__/config.test.ts:504-523: The regression does not prove the broken symlink reaches globSync. It passes both when globSync emits the link and the new filter skips it, and when globSync omits it before the filter. Therefore it does not demonstrate that the base implementation at f174ce…:src/config.ts:252 fails. Assert the fixture is returned by globSync, or add a deterministic mock/spying seam that makes realpathSync throw ENOENT for a glob candidate.

PASS

  • Lexical containment remains enforced by validated workspace glob syntax and isSafeWorkspacePath() (src/config.ts:235-239, :260).
  • Physical containment remains enforced for successfully resolved candidates (src/config.ts:263-265).
  • Ordering remains deterministic through .sort() (src/config.ts:268).
  • Relevant callers consistently route glob expansion through this function: selection (:197), listing (:210, :628-635), and remote config loading (src/commands/prepare.ts:805-815).
  • Diff hygiene: only the two requested files changed; git diff --check passed.
  • Focused suite passed: 68 tests in src/__tests__/config.test.ts.

Test gaps

  • Unexpected realpathSync errors must fail rather than disappear.
  • A candidate removed between globbing and realpathSync should still be skipped.
  • The test must prove baseline failure, not only fixed behavior.

DO-NOT-MERGE

gpt-5.6-terra · 2026-09-08 17:17:35 · ~452 tokens

Distillations (1)

Gen 0 2026-09-08 17:20:48 · 1599 tokens

Date: Sep 8, 2026 * 🔴 (17:14) User requested a strict read-only adversarial review in `/home/byk/Code/getsentry/craft-workspace-action-propagation` of the current uncommitted fix for Craft PR #872 against current HEAD `f174ceafa57355e71617f32ba1b0a030953be0a2`; instructed not to edit or mutate files. Requested review of `src/config.ts`, `src/__tests__/config.test.ts`, and relevant callers; verif…