Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .claude/agent-memory/archgate-developer/MEMORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ Non-enforceable lessons — environment/CI/platform quirks no static rule can re
- **Don't test that well-known tools exist on PATH** — Tests like `expect(resolveCommand("bun")).toBe("bun")` assert CI environment state, not application logic. They fail when the runner installs tools via shims (e.g., proto on macOS ARM64 where `Bun.which` returns null). Delete such tests entirely — the "returns null for non-existent command" tests already cover `resolveCommand`'s actual logic, and WSL-specific tests cover the `.exe` fallback path.
- **`Bun.Glob.scan()` silently fails for brace patterns with path separators** — `new Bun.Glob("svc/{src/env.ts,env.ts}").scan(...)` returns zero results (no error), while `.match()` works correctly for the same pattern. The match engine was rewritten in Bun 1.2.3 (PR #16824) to expand braces recursively, but the scanner (`GlobWalker.zig`) was not updated. Filed upstream as [oven-sh/bun#32596](https://github.com/oven-sh/bun/issues/32596). Workaround: `expandBracePattern()` in `src/engine/runner.ts` pre-expands brace groups containing `/` before scanning. Applied in `ctx.glob()`, `ctx.grepFiles()`, and `resolveScopedFiles()`.
- **ARCH-020 `glob-scan-dot` rule triggers on `.scan()` in comments** — The rule uses regex `/\.scan\(([^)]*)\)/gu` which matches `.scan()` text in JSDoc/inline comments (e.g., `Bun.Glob.scan() silently...`). Workaround: rephrase comments to avoid the exact `.scan()` text — e.g., "Bun.Glob scanning silently..." instead of "Bun.Glob.scan() silently...".
- **GitHub Actions `secrets.*` and `vars.*` are separate namespaces — configuring a value as one does NOT make it readable via the other** — `release.yml`'s "Annotate release in PostHog" step read `POSTHOG_PROJECT_ID` via `${{ vars.POSTHOG_PROJECT_ID }}`, but the value was only ever configured as a repo **secret** (confirmed with `gh secret list` vs `gh variable list` — zero repo variables existed). `vars.POSTHOG_PROJECT_ID` therefore always resolved to an empty string. Combined with `continue-on-error: true`, an internal guard that does `exit 0` (not a failure) when required config is missing, and a low-visibility `::notice::` log line, the step silently no-opped on every release for ~3 weeks (9 releases, v0.45.0–v0.45.7) with zero CI failures to flag it — confirmed via `gh run view <id> --log` showing `POSTHOG_PROJECT_ID: ` blank in the step's masked env dump. Before wiring a new `secrets.X`/`vars.X` reference, verify with `gh secret list`/`gh variable list` which namespace actually holds the value. For any `continue-on-error` step with a skip-on-missing-config guard, use `::warning::` (not `::notice::`) so misconfiguration surfaces in the Actions UI instead of being invisible indefinitely. Also documented in the project's `CLAUDE.md`.

## Claude Code Harness Config

Expand Down
9 changes: 7 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -168,12 +168,17 @@ jobs:
if: success()
continue-on-error: true
env:
# Both values are GitHub repo secrets (Settings > Secrets and variables > Actions
# > Secrets), not repo variables — do NOT switch POSTHOG_PROJECT_ID to `vars.*`.
# It was previously read via `vars.POSTHOG_PROJECT_ID` while only ever being
# configured as a secret, so this step silently no-opped (exit 0) on every
# release for weeks without failing the build. See git history for details.
POSTHOG_PERSONAL_API_KEY: ${{ secrets.POSTHOG_PERSONAL_API_KEY }}
POSTHOG_PROJECT_ID: ${{ vars.POSTHOG_PROJECT_ID }}
POSTHOG_PROJECT_ID: ${{ secrets.POSTHOG_PROJECT_ID }}
run: |
set -euo pipefail
if [ -z "${POSTHOG_PERSONAL_API_KEY:-}" ] || [ -z "${POSTHOG_PROJECT_ID:-}" ]; then
echo "::notice::Skipping PostHog annotation — POSTHOG_PERSONAL_API_KEY or POSTHOG_PROJECT_ID not configured"
echo "::warning::Skipping PostHog annotation — POSTHOG_PERSONAL_API_KEY or POSTHOG_PROJECT_ID not configured"
exit 0
fi
version=$(jq -r .version package.json)
Expand Down
6 changes: 6 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ git config --local include.path ../.githooks

Opt out of a specific hook: `git config --local hook.<name>.enabled false`. Skip all hooks for a single commit: `git commit --no-verify`.

## GitHub Actions: `secrets.*` vs `vars.*`

These are two distinct, non-overlapping namespaces in workflow expressions — configuring a value as a repo **secret** does NOT make it readable via `vars.*`, and vice versa. `.github/workflows/release.yml`'s "Annotate release in PostHog" step read `POSTHOG_PROJECT_ID` via `${{ vars.POSTHOG_PROJECT_ID }}` while the value only ever existed as a **secret** (`gh secret list`), so `vars.POSTHOG_PROJECT_ID` always resolved empty. Combined with a guard clause that does `exit 0` (not a failure) when required config is missing, plus `continue-on-error: true` and a low-visibility `::notice::` log line, the step silently no-opped on every release for weeks — annotations simply stopped appearing in PostHog with no CI failure to flag it. Fixed by reading `secrets.POSTHOG_PROJECT_ID` to match where the value actually lives.

When adding any workflow step that reads repo-level config: confirm the value's actual location with `gh secret list` / `gh variable list` before writing `secrets.X` vs `vars.X`, and if the step is `continue-on-error: true` with an internal "not configured, skipping" guard, use `::warning::` (or higher) rather than `::notice::` so a misconfiguration is visible in the Actions UI instead of silently invisible indefinitely.

## Claude Code Harness Config (`.claude/settings.json`)

The `hooks.WorktreeCreate` entry is **not** a post-creation setup step — once it's configured, the Claude Code harness defers the _entire_ worktree creation to it (it does not also create a git worktree on its own). The hook receives a JSON payload on stdin (`{ "cwd", "name", ... }`, same pattern as the `PostToolUse` hook reading `.tool_input.file_path` via `jq`) and **must** create the worktree itself and echo _only_ the resulting absolute path as its final stdout line — any other stdout (e.g. unsilenced `bun install` or `git worktree add` output) gets misread as the path and breaks `EnterWorktree`/`ExitWorktree` with errors like `path contains control characters` or `ENOENT: ... chdir`. Redirect all setup-command output to stderr (`>&2`) and keep the trailing `printf` as the only real stdout. Do not simplify this hook back down to a bare `bun install` — that regression is exactly what caused the worktree-creation bug fixed here.
Expand Down