Dashboard › craft › Session 05MUWxucnkrL
05MUWxucnkrLBJx3FRead-only adversarial final review of current uncommitted changes in BOTH worktrees: /home/byk/Code/getsentry/craft-workspace-action-propagation and /home/byk/Code/getsentry/publish-workspace-acceptance.
Do not edit. Verify the prior blockers are truly fixed: (1) legacy JSON workspace names like cli/v2 remain valid in Craft config while new Action compact names reject '.', '..', slash/non-ASCII; (2) Publish Inform start occurs only after successful canonical CI revision resolution; (3) canonical Quick links parsing/replacement and ci-poller integration reject decoys/duplicates and preserve workflow_dispatch; (4) revision flows checkout/discovery/state/publish; (5) state fixture feasible; (6) hygiene, including excluding pnpm-lock and newline-only unrelated files. Inspect diffs, source, tests. Give precise file:line findings, PASS/FAIL and final MERGE/DO-NOT-MERGE. No modifications.
<skill_content name="jj-guide"> # Skill: jj-guide # jj Guide for AI Agents Jujutsu (jj) is a Git-compatible VCS with mutable commits, automatic snapshotting, no staging area, and first-class conflicts. This skill teaches you how to use it safely from a non-interactive agent environment. If `.jj/` exists in the repo root, this is a jj repo. **Use `jj` commands, not `git`.** In a colocated repo (`.jj/` *and* `.git/`), git tools can read the state, but mutations should go through `jj` so the operation log stays consistent. ## Critical Rules - **NEVER** use interactive flags (`-i`, `--interactive`). TUI prompts hang in agent environments. This applies to `jj split -i`, `jj squash -i`, `jj commit -i`, `jj resolve`, `jj diffedit`, etc. - **ALWAYS** pass `-m "msg"` when describing/committing. Without `-m`, an editor opens and hangs. - **VERIFY** mutations with `jj st` and `jj log` after `squash`, `abandon`, `rebase`, `restore`, `commit`. ...
Error: There is no jj repo in "."
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/__tests__/publish.test.ts M src/commands/publish.ts M src/config.ts M src/index.ts M src/schemas/project_config.ts ?? src/commands/workspace.ts ?? src/commands/workspace_cmds/ action.yml | 18 ++--- docs/src/content/docs/targets/github.md | 36 ++++++++- src/__tests__/action.test.ts | 135 ++++++++++++++++++++++++++++--- src/__tests__/config.test.ts | 136 +++++++++++++++++++++++--------- src/commands/__tests__/publish.test.ts | 27 +++++++ src/commands/publish.ts | 18 ++++- src/config.ts | 48 ++++++++--- src/index.ts | 2 + src/schemas/project_config.ts | 65 ++++++++++----- 9 files changed, 388 insertions(+), 97 deletions(-) feat/workspace-action-propagation 6dae341 feat: propagate release ...
M .github/workflows/ci-poller.yml M .github/workflows/publish.yml M docs/publish-issue-format.md M package.json M src/modules/__tests__/details-from-context.js M src/modules/__tests__/publish-workflow.js M src/modules/details-from-context.js M src/modules/post-workflow-details.js M src/publish/inputs.js M yarn.lock ?? scripts/ ?? src/modules/__tests__/ci-poller-input.js ?? src/modules/__tests__/publish-location.js ?? src/modules/__tests__/release-revision.js ?? src/modules/ci-poller-input.js ?? src/modules/publish-issue-title.js ?? src/modules/publish-issue-title.peggy ?? src/modules/publish-location.js ?? src/modules/release-revision.js ?? src/publish/resolve-ci-poller-input.js ?? src/publish/resolve-location.js ?? src/publish/resolve-release-revision.js .github/workflows/ci-poller.yml | 32 ++++++------- .github/workflows/publish.yml | 65 ++++++++++++++------------- docs/publish-issue-format.md | 62 ...
diff --git a/action.yml b/action.yml
index 6344db2..b2de7f1 100644
--- a/action.yml
+++ b/action.yml
@@ -74,10 +74,15 @@ runs:
- name: Validate workspace
shell: bash
env:
+ PATH_INPUT: ${{ inputs.path }}
WORKSPACE: ${{ inputs.workspace }}
run: |
- if [[ -n "$WORKSPACE" ]] && node -e 'process.exit(/[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/u.test(process.env.WORKSPACE) ? 0 : 1)'; then
- echo "::error::Workspace names cannot contain Unicode control, format, or separator characters."
+ if [[ -n "$WORKSPACE" && "$PATH_INPUT" != '.' ]]; then
+ echo "::error::The path and workspace inputs cannot be used together."
+ exit 1
+ fi
+ if [[ -n "$WORKSPACE" ]] && ! node -e 'process.exit(/^(?!\.{1,2}$)[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."
exit 1
fi
@@ -260,14 ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/workspace_cmds</path> <type>directory</type> <entries> __tests__/ list.ts (2 entries) </entries>
<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>
<system-reminder>
Instructions from: /home/byk/Code/getsentry/craft-workspace-action-propagation/AGENTS.md
# AGENTS.md
This file provides guidance for AI coding assistants working with the Craft codebase.
## Package Management
- **Always use `pnpm`** for package management. Never use `npm` or `yarn`.
- Node.js version is managed by [Volta](https://volta.sh/) (currently v22.12.0).
- Install dependencies with `pnpm install --frozen-lockfile`. ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/workspace_cmds/__tests__</path> <type>directory</type> <entries> list.test.ts (1 entries) </entries>
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/workspace_cmds/__tests__/list.test.ts</path>
<type>file</type>
<content>
1: import { describe, expect, test, vi } from 'vitest';
2:
3: vi.mock('../../../config', () => ({
4: getWorkspaceNames: vi.fn(),
5: }));
6: vi.mock('../../../utils/strings', () => ({
7: formatJson: vi.fn(value => JSON.stringify(value)),
8: }));
9:
10: import { getWorkspaceNames } from '../../../config';
11: import { handler } from '../list';
12:
13: describe('workspace list command', () => {
14: test('prints exact configured workspace names', () => {
15: vi.mocked(getWorkspaceNames).mockReturnValue(['cli', 'mcp.v2']);
16: const log = vi.spyOn(console, 'log').mockImplementation(() => {});
17:
18: handler();
19:
20: expect(log).toHaveBeenCalledWith('["cli","mcp.v2"]');
21: });
22:
23: test('prints an empty array when no workspaces are configured', () => {
24: ...
...output truncated...
Full output saved to: /home/byk/.local/share/opencode/tool-output/tool_0626ed171001EcYSGJRjcf0hRR
+`dry-run` requests dry-run mode.
diff --git a/package.json b/package.json
index d59a8b6..0790379 100644
--- a/package.json
+++ b/package.json
@@ -15,7 +15,9 @@
"undici": "^6.23.0"
},
"scripts": {
- "test": "vitest run",
+ "generate": "node scripts/generate-publish-issue-title-parser.js",
+ "check:generated": "node scripts/generate-publish-issue-title-parser.js --check",
+ "test": "yarn check:generated && vitest run",
"test:watch": "vitest",
"lint": "eslint src .github --ignore-pattern '!.github'",
"prettier": "prettier --write src"
@@ -24,6 +26,7 @@
"eslint": "^8.9.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-yml": "^0.13.0",
+ "peggy": "5.1.0",
"prettier": "^2.2.1",
"vitest": "^4.1.0"
},
diff --git a/src/modules/__tests__/details-from-context.js ...
<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 ...
<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 ...
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
@@ -48,6 +48,11 @@ jobs:
private-key: ${{ secrets.SENTRY_RELEASE_BOT_PRIVATE_KEY }}
owner: getsentry # create token with access to all getsentry repos
+ - name: Check out publish controller
+ uses: actions/checkout@v5
+ with:
+ path: .__publish__
+
- name: Check CI status for ci-pending issues
env:
# Use the sentry-internal-app token for label changes on this
@@ -92,26 +97,14 @@ jobs:
title=$(echo "$issue" | jq -r '.title')
body=$(echo "$issue" | jq -r '.body')
- # Parse repo and version from title: "publish: owner/repo[/path]@version"
- # Only take owner/repo (first two segments) — monorepos like
- # "getsentry/relay/py@0.9.26" have a path suffix that isn't part
- ...
<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. ...
> @sentry/craft@2.31.0-dev.0 test /home/byk/Code/getsentry/craft-workspace-action-propagation > vitest run RUN v4.1.8 /home/byk/Code/getsentry/craft-workspace-action-propagation [info] [[target/github]] No previous release found [info] [[target/github]] Deleted orphaned draft release: v1.0.0 [info] [[target/github]] No previous release found [warn] [[target/github]] Failed to clean up release "v1.0.0": Error: Cleanup failed [info] [[target/github]] No previous release found [info] [[target/github]] No previous release found [info] [[target/github]] Release for tag "1.0.0" already exists and is published. Skipping GitHub release creation (likely from a previous run). [info] [[target/github]] No previous release found [info] [[target/github]] createRelease returned 422 for tag "1.0.0". Looking for a leftover draft release to clean up... [info] [[target/github]] Deleting leftover draft release (id=789) for tag "1.0.0"... ...
yarn run v1.22.22 $ yarn check:generated && vitest run $ node scripts/generate-publish-issue-title-parser.js --check [1m[46m RUN [49m[22m [36mv4.1.0 [39m[90m/home/byk/Code/getsentry/publish-workspace-acceptance[39m [2m Test Files [22m [1m[32m9 passed[39m[22m[90m (9)[39m [2m Tests [22m [1m[32m54 passed[39m[22m[90m (54)[39m [2m Start at [22m 14:04:59 [2m Duration [22m 1.14s[2m (transform 262ms, setup 0ms, import 879ms, tests 225ms, environment 1ms)[22m Done in 2.31s. yarn run v1.22.22 $ eslint src .github --ignore-pattern '!.github' Done in 1.15s. yarn run v1.22.22 $ prettier --write src --check src/modules/publish-issue-title.peggy scripts/generate-publish-issue-title-parser.js src/modules/ci-poller-input.js src/modules/publish-location.js src/modules/release-revision.js src/publish/resolve-ci-poller-input.js src/publish/resolve-location.js src/publish/resolve-release-revision.js Checking formatting... [warn] ...
M .github/workflows/ci-poller.yml M .github/workflows/publish.yml M docs/publish-issue-format.md M package.json M src/libs/__tests__/github.js M src/libs/github.js M src/modules/__tests__/details-from-context.js M src/modules/__tests__/publish-workflow.js M src/modules/details-from-context.js M src/modules/post-workflow-details.js M src/modules/process-end-state.js M src/publish/inputs.js M src/publish/post-result.js M src/publish/post-workflow-details.js M src/publish/update-issue.js M yarn.lock ?? scripts/ ?? src/modules/__tests__/ci-poller-input.js ?? src/modules/__tests__/publish-location.js ?? src/modules/__tests__/release-revision.js ?? src/modules/ci-poller-input.js ?? src/modules/publish-issue-title.js ?? src/modules/publish-issue-title.peggy ?? src/modules/publish-location.js ?? src/modules/release-revision.js ?? src/publish/resolve-ci-poller-input.js ?? src/publish/resolve-location.js ?? ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/publish.ts</path>
<type>file</type>
<content>
600: * @param argv Command-line arguments
601: */
602: export async function publishMain(argv: PublishOptions): Promise<any> {
603: // Get publishing configuration
604: const config = getConfiguration() || {};
605:
606: const newVersion = argv.newVersion;
607:
608: logger.info(`Publishing version: "${newVersion}"`);
609:
610: const git = await getGitClient();
611:
612: // Check for dirty repository state before any git operations
613: if (argv.noGitChecks) {
614: logger.info('Not checking the status of the local repository');
615: } else {
616: const repoStatus = await git.status();
617: if (isRepoDirty(repoStatus)) {
618: reportError(
619: 'Your repository is in a dirty state. ' +
620: 'Please stash or commit the pending changes.',
621: logger,
622: );
623: }
624: }
625:
626: const ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/action.yml</path>
<type>file</type>
<content>
200: if [[ -n "$VERSION" ]]; then
201: VERSION_ARG=("$VERSION")
202: fi
203:
204: craft prepare "${VERSION_ARG[@]}" "${CRAFT_ARGS[@]}"
205:
206: - name: Read Craft Targets
207: id: craft-targets
208: shell: bash
209: working-directory: ${{ inputs.path }}
210: env:
211: CRAFT_LOG_LEVEL: Warn
212: WORKSPACE: ${{ inputs.workspace }}
213: run: |
214: CRAFT_ARGS=()
215: if [[ -n "$WORKSPACE" ]]; then
216: CRAFT_ARGS=("--workspace=$WORKSPACE")
217: fi
218: targets=$(craft targets "${CRAFT_ARGS[@]}" | jq -r '.[]|" - [ ] \(.)"')
219:
220: # https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#multiline-strings
221: echo "targets<<EOF" >> "$GITHUB_OUTPUT"
222: echo "$targets" >> ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/action.yml</path>
<type>file</type>
<content>
314: # Build changelog section if available
315: if [[ -n "$CHANGELOG" ]]; then
316: CHANGELOG_SECTION="
317: ---
318:
319: <details open>
320: <summary>📋 Changelog</summary>
321:
322: ${CHANGELOG}
323:
324: </details>"
325: else
326: CHANGELOG_SECTION=""
327: fi
328:
329: body="Requested by: @${GITHUB_ACTOR}
330:
331: Merge target: ${MERGE_TARGET}
332:
333: Quick links:
334: - [View changes](https://github.com/${GITHUB_REPOSITORY}/compare/${RELEASE_PREVIOUS_TAG}...${RELEASE_BRANCH})
335: - [View check runs](https://github.com/${GITHUB_REPOSITORY}/commit/${RELEASE_SHA}/checks/)
336:
337: Assign the **accepted** label to this issue to approve the release. ...
Quick-links parsing still accepts a decoy “canonical” block.
src/modules/release-revision.js:2 uses a multiline regex that accepts Quick links: anywhere in the body, including a changelog, HTML detail block, or code block. A body with only one forged complete Quick-links block passes CHECK_RUNS_LINK_COUNT at line 3 and supplies its revision. Tests at src/modules/__tests__/release-revision.js:56-84 cover extra check-run links, but not a decoy complete Quick-links block.
FAIL: canonical parsing/replacement does not fully reject decoys.
Hygiene fails.
The original diff includes newline-only unrelated changes in:
src/modules/post-workflow-details.js:19src/publish/inputs.js:13No pnpm-lock.yaml appears in either worktree; yarn.lock is justified by the new Peggy dependency.
I also accidentally violated the read-only instruction: yarn prettier --check ... invoked this repository’s prettier script, which runs prettier --write src. It added unrelated formatting changes to:
src/libs/__tests__/github.jssrc/libs/github.jssrc/modules/process-end-state.jssrc/publish/post-result.jssrc/publish/post-workflow-details.jssrc/publish/update-issue.jsI made no corrective edits. These changes must be reverted before review can be considered clean.
FAIL: hygiene.
cli/v2: src/schemas/project_config.ts:220-231; regression at src/__tests__/config.test.ts:162-166../.., and non-ASCII input before side effects: action.yml:80-87; tests at src/__tests__/action.test.ts:178-252..github/workflows/publish.yml:138-149.workflow_dispatch: .github/workflows/ci-poller.yml:3-24, 100-107. ...Date: Sep 2, 2026 * 🟡 [requested-read-only-review] (14:02) User requested a read-only adversarial final review of current uncommitted changes in both worktrees: `/home/byk/Code/getsentry/craft-workspace-action-propagation` and `/home/byk/Code/getsentry/publish-workspace-acceptance`; explicitly requested no edits. * 🟡 (14:02) User asked to inspect diffs, source, and tests; verify prior blockers;…