Skip to content

Honor active harness config homes in setup-pstack [#120] - #123

Merged
mergify[bot] merged 10 commits into
mainfrom
factory/issue-120
Oct 4, 2026
Merged

mergify[bot] merged 10 commits into
mainfrom
factory/issue-120

Conversation

@mast-factory

@mast-factory mast-factory Bot commented Oct 4, 2026

Copy link
Copy Markdown

Summary

Closes #120.

Make setup-pstack place its model sheet and parent integration in the config home the active harness reads. The existing harness-path mapping now specifies nonempty CLAUDE_CONFIG_DIR / CODEX_HOME, with $HOME/.claude / $HOME/.codex fallbacks for unset or empty values. Setup uses the resolved home consistently for reads, writes, snapshots, restoration, and readback, and renders Claude imports with spaces escaped.

Add a safe redirected-home evidence fixture, a named static regression invariant, and provenance entries in CHANGES.md and UPSTREAM.md so upstream syncs preserve this port divergence. No changes under .github/workflows/.

Verification on f95670e

  • bun install --frozen-lockfile — passed.
  • bun run test in plugins/pstack/skills/poteto-mode/scripts — 159 pass, 0 fail, 653 assertions across 11 files.
  • bun run typecheck in the same directory — all three strict TypeScript projects passed with --noEmit.
  • PSTACK_STATIC_ONLY=1 bash tests/skill-collision-repro.sh — all 14 static invariant checks passed, including unset/empty defaults and space-containing overrides without writes.
  • Literal-default-path mutation — rejected by the named config-home invariant with exit 1; original content restored.
  • Four marketplace/plugin JSON manifests — parsed successfully.
  • claude plugin validate ./plugins/pstack — validation passed.
  • bash -n tests/setup-config-home-repro.sh tests/skill-collision-repro.sh — passed.
  • Fixture help and redirected preparation — passed; verifier correctly rejected missing model-sheet artifacts. All four Claude/Codex unset/empty live-write attempts were refused.
  • git diff --check — passed; working tree clean.

These are local/static and fixture checks, not real-harness setup acceptance evidence. Local verification output is retained in /tmp/pstack-120-*.log in the build sandbox, not committed or uploaded.

Operator live acceptance on the final head

Per the approved revision-5 build conditions, this PR is ready for review before live proof. The operator must run redirected Claude and Codex setup against the exact final head, retain native transcripts/provider receipts, verify artifacts, preservation of unrelated integration text and daily files, byte-identical reruns, and fresh-session loading. Use tests/setup-config-home-repro.sh with optional generic --claude-config <dir> / --codex-home <dir> credential sources; keep real HOME and USER unchanged. Do not write unset/empty cases into daily config homes. Run directories can contain credentials and must never be committed or uploaded.

Codex is unavailable in this sandbox. Neither pre-fix live reproduction nor final-head live setup/loading evidence is claimed. The operator must supply remaining live evidence and post the exact-head live-gate; the builder has not posted it. Any changed PR head requires fresh operator proof.

The prior .mergify.yml:queue_rules finding is excluded as unrelated to config-home writes; queue behavior is unchanged.

Keep real HOME and USER while preparing redirected, credential-only profiles and checking setup artifacts without claiming a live run.

Co-Authored-By: mastra-platform[bot] <284800079+mastra-platform[bot]@users.noreply.github.com>
Resolve nonempty config-home overrides at the existing harness mapping boundary and reuse that path throughout setup and integration rendering.

Co-Authored-By: mastra-platform[bot] <284800079+mastra-platform[bot]@users.noreply.github.com>
Guard the canonical mapping and default/empty/spaced resolution without daily writes, and record the intentional port divergence for the patch ledger.

Co-Authored-By: mastra-platform[bot] <284800079+mastra-platform[bot]@users.noreply.github.com>
@mergify

mergify Bot commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Merge Protections

🟢 All 2 merge protections satisfied — ready to merge.

Show 2 satisfied protections

🟢 Queue ready pull requests

Automatically queue non-draft pull requests targeting main after verify, Unfret, and live-gate pass on the head.

  • -draft
  • check-success=Unfret
  • check-success=live-gate
  • check-success=verify

🟢 🚦 Auto-queue

When all merge protections are satisfied and these conditions match, this pull request will be queued automatically.

  • -files~=^\.github/
  • base = main

@greptile-apps

greptile-apps Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 4/5

[Medium risk] Updates setup instructions for config directory resolution.

The PR should not merge until setup uses a stable absolute path for relative config-home overrides.

Findings

  1. P1 Relative home points elsewhere ▶
  2. P2 Old include goes untested ▶
  3. P2 Braced home escapes guard ▶

Summary

This PR makes /setup-pstack use the active Claude Code or Codex config home for its model sheet and global instructions. It also adds a fixture for preparing and checking redirected setup runs, plus notes and a static check to preserve the config-home rule.

  • Claude uses nonempty CLAUDE_CONFIG_DIR or $HOME/.claude; Codex uses nonempty CODEX_HOME or $HOME/.codex.
  • Claude imports escape spaces in the resolved sheet path; Codex keeps the sheet bytes in a bounded block.
  • The fixture prepares isolated homes and checks their outputs against daily files and reruns.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Active parent] --> B{Claude or Codex?}
  B -->|Claude| C[CLAUDE_CONFIG_DIR or default]
  B -->|Codex| D[CODEX_HOME or default]
  C --> E[Model sheet and CLAUDE.md import]
  D --> F[Model sheet and AGENTS.md block]
Loading

Reviews (1) · Last reviewed commit: "test: prevent setup config-home regressi..."

Comment on lines +7 to +11
Resolve the current parent's config home once: Claude Code uses nonempty `CLAUDE_CONFIG_DIR`, otherwise `$HOME/.claude`; Codex uses nonempty `CODEX_HOME`, otherwise `$HOME/.codex`. The equivalent quoted shell expressions are:

```bash
"${CLAUDE_CONFIG_DIR:-$HOME/.claude}"
"${CODEX_HOME:-$HOME/.codex}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Relative home points elsewhere

If CLAUDE_CONFIG_DIR or CODEX_HOME is relative and the operator changes directories after starting the parent, setup can write to a different home from the one the parent uses. Its readback can still succeed, but the parent will not load the model sheet. Make the chosen home absolute before setup reuses it.

Comment thread tests/setup-config-home-repro.sh Outdated
Comment on lines +109 to +110
printf '%s\n' '# Fixture instructions' 'Preserve this unrelated text exactly.' > "$run/sentinel"
cp "$run/sentinel" "$run/claude config/CLAUDE.md"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Old include goes untested

The fixture starts with a new CLAUDE.md, so it never checks an upgrade from the old @~/.claude/pstack-models.md line to the new resolved-path line. Add an upgrade case that starts with the old line and checks that only one working include remains. Without it, the evidence run can miss a duplicate or stale import.

Comment thread tests/skill-collision-repro.sh Outdated
# Config-home port invariant (#120): guard upstream merges against daily-home writes.
config_mapping="$repo/plugins/pstack/skills/poteto-mode/references/codex-tools.md"
config_home_bad=""
if grep -nE '~/(\.claude|\.codex)|\$HOME/(\.claude|\.codex)' "$setup"; then

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Braced home escapes guard

The default-home guard matches ~/ and $HOME/, but not ${HOME}/.claude or ${HOME}/.codex. A future setup edit could use the braced spelling to write to a daily home while this check stays green. Match that spelling too.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@unfret-eal

unfret-eal Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

✅ Accepted risk

Accepted by ericlitman: GH-120 (authority 48fa77b)

🟡 Medium · Imported-sheet verifier rejects required migrations

🟡 Medium · Verifier accepts extra integration content after sentinel

🟡 Medium · Alias-led panel rows bypass assignment verification

Advisories (6)
  • Copied Codex auth can invalidate the daily login
    Not blocking because: The supplied prior confirmation bounds the consequence to a forced re-login after refresh-token reuse. It fails closed, exposes no credentials, changes no daily files or pstack artifacts, and cannot create false live-gate evidence.
  • Dash-leading config homes break mapped Codex reads
    Not blocking because: The mapped read fails visibly before snapshots or writes, leaving configuration unchanged. Using ./-codex, an absolute home, or option termination avoids this bounded path-handling failure.
  • Codex recipe uses the wrong invocation surface
    Not blocking because: The wrong invocation causes bounded operator friction. Missing setup artifacts fail verification, live invocation requires separate evidence, and the documented Codex by-name request completes the procedure.
  • Credential copies misparse dash-leading source paths
    Not blocking because: Preparation exits before launching a harness or reporting success. Using ./-login or an absolute source path completes the same operation without changing credentials or weakening acceptance checks.
  • Release summary misstates imported-sheet behavior
    Not blocking because: The confirmed consequence is inconsistent summary wording. The specific state-loading and confirmation procedures establish the actual behavior, and no supplied promotion or acceptance mechanism consumes the inaccurate summary.
  • --static self-test fails on any dirty working tree
    Not blocking because: The static self-test fails closed and removes its temporary fixture without launching a harness or modifying configuration. Running it in a clean checkout works; the defect causes local testing friction rather than an incorrect acceptance result.
Details

Run: GET /unfret/run/panel:6ef25f56618c36526383e471c28065a571bbeb4e:TkYcbp7HNpg:CbFtcEv8llI.

@unfret review · @unfret status · @unfret help

@unfret-eal unfret-eal Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛑 1 blocking finding
🟡 Medium · Claude include rerun lacks a rule for existing sheet imports
Check

### 8. Wire it in

Render the parent integration in memory before either write. On Claude, the integration is the single `@~/.claude/pstack-models.md` include in `~/.claude/CLAUDE.md`. On Codex, it is the exact sheet bytes between one `<!-- pstack:models:begin -->` and `<!-- pstack:models:end -->` pair in `~/.codex/AGENTS.md`. Replace that whole bounded block on a rerun. Insert one block at the end on first run. If either marker is missing, duplicated, or reversed, stop and report inconsistent state instead of guessing a boundary.
Render the parent integration in memory before either write. On Claude, the integration is the single `@` include of the actual resolved `<config-home>/pstack-models.md` path (backslash-escape spaces, never leave a variable or placeholder) in `<config-home>/CLAUDE.md`. On Codex, it is the exact sheet bytes between one `<!-- pstack:models:begin -->` and `<!-- pstack:models:end -->` pair in `<config-home>/AGENTS.md`. Replace that whole bounded block on a rerun. Insert one block at the end on first run. If either marker is missing, duplicated, or reversed, stop and report inconsistent state instead of guessing a boundary.

@unfret-eal unfret-eal Bot Oct 4, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium · Claude include rerun lacks a rule for existing sheet imports

The diff deleted the fixed Claude integration text "the single @~/.claude/pstack-models.md include in ~/.claude/CLAUDE.md". Step 8 now renders @<actual resolved path>. The Codex path has a find/replace/stop rule, but this Claude path gives no rule to find, replace, or reject an existing pstack-models.md import. Scenario 1: an upgrading default-home user reruns setup. CLAUDE.md still holds @~/.claude/pstack-models.md, while the render is @/Users/u/.claude/pstack-models.md. The lines no longer match, so the agent can append a second import. The rerun is then not byte-identical, which contradicts CHANGES' "default-home behavior unchanged". Scenario 2: a CLAUDE_CONFIG_DIR profile created by copying ~/.claude keeps an import of the old home's sheet. Setup appends the new home's import, so both sheets load with conflicting role descriptors. Dispatch can then pick the wrong route. The new fixture starts from a sentinel with no prior import, so it never exercises this path.

Withdrawn in the latest review.

Keep default-home upgrades byte-identical and replace copied-profile imports instead of loading two model sheets. Require deterministic zero/one/many import handling in the config-home invariant.

Co-Authored-By: mastra-platform[bot] <284800079+mastra-platform[bot]@users.noreply.github.com>

@unfret-eal unfret-eal Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛑 1 blocking finding
🟡 Medium · Redirected Claude imports break on paths containing `#`
Check

Render the parent integration in memory before either write. On Claude, the integration is the single `@~/.claude/pstack-models.md` include in `~/.claude/CLAUDE.md`. On Codex, it is the exact sheet bytes between one `<!-- pstack:models:begin -->` and `<!-- pstack:models:end -->` pair in `~/.codex/AGENTS.md`. Replace that whole bounded block on a rerun. Insert one block at the end on first run. If either marker is missing, duplicated, or reversed, stop and report inconsistent state instead of guessing a boundary.
Render the parent integration in memory before either write.

On Claude, the integration is the one `@` import line in `<config-home>/CLAUDE.md` whose target's basename is `pstack-models.md`, regardless of its existing directory or path spelling. When `<config-home>` is the default home, render exactly `@~/.claude/pstack-models.md`, preserving the legacy text. Only when `CLAUDE_CONFIG_DIR` redirects the home, render `@` followed by the absolute resolved `<config-home>/pstack-models.md` path with each space backslash-escaped, never a variable or placeholder. On a rerun, replace that one line in place, preserving all unrelated bytes. If zero matching import lines exist, append one. If more than one exists, stop and report inconsistent state before either write; do not append another import or guess which one to replace.

@unfret-eal unfret-eal Bot Oct 4, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium · Redirected Claude imports break on paths containing #

For CLAUDE_CONFIG_DIR=/tmp/claude#work, a rerun replaces a working @./pstack-models.md import with @/tmp/claude#work/pstack-models.md. Claude Code's import parser treats # as a fragment separator and resolves this to /tmp/claude, not the sheet. Fresh sessions therefore stop loading the configured role choices even though setup's write/readback checks pass. Confirmed against the parsers in Claude Code 2.1.197 and 2.1.289. The same rendering rule also appears at setup line 18 and codex-tools.md line 22.

Withdrawn in the latest review.

Use a sibling-relative model-sheet import so directory characters cannot prevent loading, while preserving legacy default-home imports and deterministic reruns.

Co-Authored-By: mastra-platform[bot] <284800079+mastra-platform[bot]@users.noreply.github.com>

@unfret-eal unfret-eal Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No blocking findings
Check

@ericlitman

ericlitman commented Oct 4, 2026 •

Copy link
Copy Markdown
Owner

Live test, Codex half (head 239a73c)

Result: the Codex half passes. The Claude half has not run yet, so I have not set the live-gate status.

  • Installed version: open-pstack plugin 1.5.0, installed from ref 239a73c into a redirected CODEX_HOME whose path contains a space and a # (<tmp>/codex # config). Codex CLI 0.160.0 (codex-cli 0.160.0), parent model GPT-6.1 Sol.
  • Surface: interactive Codex TUI in that CODEX_HOME.
  • Action 1: I asked Codex to run setup-pstack, reassign every Grok default to codex:gpt-5.6-sol@max, and write.
    • Observed: the Sol and Opus probes passed, then the mixed-panel smoke and the cross-judge passed.
    • The skill wrote pstack-models.md and a bounded <!-- pstack:models --> block in AGENTS.md, both under the redirected home. The unrelated fixture text was preserved.
  • Action 2: I ran tests/setup-config-home-repro.sh --verify on the run directory.
    • Observed: ok: codex redirected artifacts, unrelated bytes, daily targets and rerun snapshots. The daily ~/.codex and ~/.claude targets were unchanged.
  • Action 3: I reran setup-pstack with the same choices.
    • Observed: the probes passed again. SHA-256 of AGENTS.md and pstack-models.md matched the first run byte for byte. --verify passed again.
  • Action 4: I started a fresh codex exec session in that CODEX_HOME and asked it to answer from loaded instructions only, without reading files.
    • Observed: it reported swarm workers: codex:gpt-5.6-sol@max and how explainer: claude:opus@max. These values match the redirected sheet and differ from the daily config.

Configuration note: the default Codex sandbox blocked the external Opus runner's network access (ENOTFOUND). So I set sandbox_mode = "danger-full-access" in the test home. #124 tracks that sandbox gap separately. #123 does not change it.

Still pending: the same live test from Claude Code with a redirected CLAUDE_CONFIG_DIR. It needs a Claude login stored for that directory.

Keep the config-home correction alongside the host sandbox prerequisites added by #125 without changing either implementation.

Co-Authored-By: mastra-platform[bot] <284800079+mastra-platform[bot]@users.noreply.github.com>

@unfret-eal unfret-eal Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No blocking findings
Check

@ericlitman

Copy link
Copy Markdown
Owner

Live test, Codex half (head 536319d)

Result: the Codex half passes on this head, after the main merge. The Claude Code half has not run yet, so I have not set the live-gate status.

  • Installed version: Open Pstack 1.5.0 from --ref 536319d46faac9d5a9cc5e6ad485bc4bb1975b66. I installed it into the redirected CODEX_HOME prepared by tests/setup-config-home-repro.sh codex redirected, whose path contains a space and a #. Codex CLI 0.160.0.
    • The redirected home's auth.json is a symlink to the operator's real ~/.codex/auth.json, not a copy. It stayed a symlink throughout, and the daily codex login status still reports logged in afterwards.
    • CLAUDE_CONFIG_DIR was unset for this half.
  • Surface: the interactive Codex TUI (GPT-6.1 Sol) in that CODEX_HOME.
  • Action 1: "Use the pstack setup-pstack skill for this Codex parent with the default first-run assignments …"
    • Observed: all three default pairs probed and passed: Opus max through the external Claude runner, Sol max native, and Grok xhigh through the external Grok runner. The mixed-panel smoke and cross-judge passed.
    • The skill wrote pstack-models.md and the bounded AGENTS.md block under the redirected home, and the unrelated fixture text was unchanged.
  • Action 2: tests/setup-config-home-repro.sh --verify <run>.
    • Observed: "ok: codex redirected artifacts, unrelated bytes, daily targets and rerun snapshots".
  • Action 3: I reran setup unchanged.
    • Observed: the probes passed again. SHA-256 of AGENTS.md and pstack-models.md was identical to the first run, and --verify passed again.
  • Action 4, fresh-session load: I asked a fresh codex exec session in that home, from context only, whether its instructions contain a heading "Fixture instructions".
    • Observed: "Yes. 'Preserve this unrelated text exactly.'"
    • Control: the same question in a session on the daily CODEX_HOME answered "No".

Still pending: the Claude Code half, which needs a parent Claude session authenticated inside a redirected CLAUDE_CONFIG_DIR.

…#120]

Preserve #129 permission guidance and #120 config-home rules in their respective setup steps.

Co-Authored-By: mastra-platform[bot] <284800079+mastra-platform[bot]@users.noreply.github.com>

@unfret-eal unfret-eal Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛑 2 blocking findings
🟡 Medium · Rerun re-points a cross-directory sheet import without loading that sheet
🟡 Medium · Verifier accepts extra conflicting integration content
Check

Render the parent integration in memory before either write. On Claude, the integration is the single `@~/.claude/pstack-models.md` include in `~/.claude/CLAUDE.md`. On Codex, it is the exact sheet bytes between one `<!-- pstack:models:begin -->` and `<!-- pstack:models:end -->` pair in `~/.codex/AGENTS.md`. Replace that whole bounded block on a rerun. Insert one block at the end on first run. If either marker is missing, duplicated, or reversed, stop and report inconsistent state instead of guessing a boundary.
Render the parent integration in memory before either write.

On Claude, the integration is the one `@` import line in `<config-home>/CLAUDE.md` whose target's basename is `pstack-models.md`, regardless of its existing directory or path spelling. When `<config-home>` is the default home, render exactly `@~/.claude/pstack-models.md`, preserving the legacy text. Only when `CLAUDE_CONFIG_DIR` redirects the home, render exactly `@./pstack-models.md`. This relative import resolves from the importing file's directory, where the sheet also lives, so the import line contains no config-directory characters. On a rerun, replace that one line in place, preserving all unrelated bytes. If zero matching import lines exist, append one. If more than one exists, stop and report inconsistent state before either write; do not append another import or guess which one to replace.

@unfret-eal unfret-eal Bot Oct 4, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium · Rerun re-points a cross-directory sheet import without loading that sheet

Step 8 now treats an @ import with basename pstack-models.md as the integration "regardless of its existing directory" and replaces it in place. Step 2, per line 10, loads current state only from <config-home>/pstack-models.md, so the sheet the existing import actually loads is never read. Scenario: CLAUDE_CONFIG_DIR=/work/claude, and /work/claude/CLAUDE.md holds @~/.claude/pstack-models.md. This happens with a copied profile, or with a manual workaround for #120 that base setup kept updating. The customized sheet lives at ~/.claude/pstack-models.md. /work/claude/pstack-models.md is either absent or a stale copy. Setup then presents first-run defaults, or the stale copy, as the current assignments, with keep as the default. After confirmation it rewrites the import to @./pstack-models.md. Fresh sessions stop loading the customized roles and efforts; for example, a bug-fix lane customized to claude:opus reverts to Sol. This breaks step 2's rule against resetting a customized sheet. On base, setup read and rewrote ~/.claude/pstack-models.md, so that import kept loading the user's configuration. The same rule is repeated at codex-tools.md lines 16–22.

Withdrawn in the latest review.

Comment on lines +59 to +68
if (!text.startsWith(sentinel)) fail('unrelated integration bytes changed');
if (meta.harness === 'claude') {
const includes = text.split('\n').filter(line => line.startsWith('@') && line.includes('pstack-models.md'));
if (includes.length !== 1 || includes[0] !== '@./pstack-models.md') fail('Claude include is not the single relative sheet import');
} else {
const begin = '<!-- pstack:models:begin -->';
const end = '<!-- pstack:models:end -->';
if (text.split(begin).length !== 2 || text.split(end).length !== 2 || text.indexOf(end) <= text.indexOf(begin)) fail('Codex block markers missing, duplicated, or reversed');
const block = text.slice(text.indexOf(begin) + begin.length + 1, text.indexOf(end));
if (!Buffer.from(block).equals(bytes)) fail('Codex block differs from model sheet bytes');

@unfret-eal unfret-eal Bot Oct 4, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium · Verifier accepts extra integration content after sentinel

Carried v1:7d4d3bf1 is still present. The verifier checks only the sentinel prefix and then either one @-led pstack import (Claude) or one marker block (Codex). It still prints ok for: (1) a Claude file with the sentinel, @./pstack-models.md, and bug-fix: claude:opus@max; (2) one that also keeps an indented @~/.claude/pstack-models.md, which Claude still imports but startsWith('@') ignores; (3) a Codex AGENTS.md with a stray row outside the block. It therefore certifies integrations that contradict or double-load the sheet, against step 8's single import/block plus preserved bytes. Also reported for this defect: - Verifier accepts conflicting integration suffixes (tests/setup-config-home-repro.sh:115-124): The verifier only requires the integration to start with the sentinel and contain the expected import or bounded block. Because preparation seeded no other suffix, a generated file containing the valid integration plus a stale row such as bug-fix: claude:opus@max still prints ok, even though that global instruction can conflict with the sheet. - Verifier accepts conflicting content outside the integration (tests/setup-config-home-repro.sh:115-124): Verification checks only the sentinel prefix and the presence of one expected import or bounded block; it never constrains the remaining bytes. A Claude file containing the valid relative import followed by a stale bug-fix: claude:opus@max instruction, or a Codex file with that row outside the valid block, passes even though the fixture initially contained only the sentinel and the extra global routing can contradict the generated sheet. - Verifier accepts conflicting integration suffixes (tests/setup-config-home-repro.sh:115, tests/setup-config-home-repro.sh:59-68, tests/setup-config-home-repro.sh:114-123, tests/setup-config-home-repro.sh:115-118, tests/setup-config-home-repro.sh:115-124, tests/setup-config-home-repro.sh:117-124): Checking only that the integration starts with the sentinel leaves all later non-integration content unconstrained. In both harnesses, the required import or bounded block followed by an unbounded row such as bug-fix: claude:opus@max passes verification, even though that global instruction can conflict with the model sheet. Also reported for this defect: - Verifier accepts conflicting content outside the integration (tests/setup-config-home-repro.sh:115-124): Only the sentinel prefix and the required import or bounded block are checked. A Claude integration containing the valid relative import plus a stale model-routing row, or a Codex integration containing the exact block plus such a row outside it, passes verification even though the extra global instruction can contradict the generated sheet. - Verifier accepts extra or stale content outside the integration (tests/setup-config-home-repro.sh:115-124): Still present at this head (carried v1:7d4d3bf1). Lines 115-124 check only three things: the sentinel prefix, one line that starts with @ and equals @./pstack-models.md (Claude), and one matching marker pair (Codex). Everything else after the sentinel is unconstrained. Verify exits 0 with ok for a redirected CLAUDE.md of sentinel + @./pstack-models.md + bug-fix: claude:opus@max. It also passes one that keeps an indented @~/.claude/pstack-models.md, which Claude still imports but startsWith('@') does not count. A Codex AGENTS.md with a stray row outside the block passes the same way. The helper therefore certifies integrations that contradict or double-load the sheet, against step 8's rule of a single import or block plus preserved bytes. - Verifier accepts extra conflicting integration content (tests/setup-config-home-repro.sh:59-68, tests/setup-config-home-repro.sh:114-123, tests/setup-config-home-repro.sh:115-118, tests/setup-config-home-repro.sh:117-124): The verifier only requires the sentinel as a prefix and then checks for one valid import or bounded block. An integration containing the correct import/block plus an unbounded stale model row still prints ok, even though that extra global instruction can conflict with the model sheet and violates step 8's single integration shape. Also reported for this defect: - Verifier accepts stale model instructions outside the integration (tests/setup-config-home-repro.sh:114-123): For either harness, an integration containing the untouched sentinel, the required relative import or exact bounded block, and an extra unbounded row such as bug-fix: claude:opus@max still passes. The prefix and branch checks never reject that suffix, so a sheet routing bug-fix to Sol can conflict with the global instruction while --verify exits 0 and prints ok. - Verifier still accepts extra conflicting integration content (tests/setup-config-home-repro.sh:115-118): Carried demand v1:7d4d3bf1 is still present at this head. Lines 115-118 only check that the integration starts with the sentinel and contains one @./pstack-models.md import (Claude) or one marker pair matching the sheet (Codex). Nothing else after the sentinel is constrained. Scenario: the redirected CLAUDE.md is the sentinel, then @./pstack-models.md, then a stale bug-fix: claude:opus@max line. Verify exits 0 and prints ok. The same holds for a Codex AGENTS.md with a stray row outside the block. Step 8 allows only the single import line or bounded block beyond the preserved bytes, so the acceptance helper certifies an integration that can contradict the sheet. - Verifier accepts conflicting integration rows (tests/setup-config-home-repro.sh:117-124): A Claude file with the required @./pstack-models.md import, or a Codex file with a byte-identical bounded block, still passes when an additional model row such as bug-fix: claude:opus@max appears outside that integration. Those stale global instructions can conflict with the sheet, yet --verify exits 0 and prints ok.

Now at tests/setup-config-home-repro.sh:115-124

Also affects:

@ericlitman

Copy link
Copy Markdown
Owner

Live tests on head d516ad3: both harnesses pass (not gating)

I am not setting live-gate. Unfret raised a verdict-changing finding on this head (v1:3d2b25df, a rerun re-points a cross-directory sheet import), and a repair is in progress. The next head needs its own run. This records that the d516ad3 behaviour works in both harnesses.

Claude Code

  • Installed version: Claude Code 2.1.289, with CLAUDE_CONFIG_DIR set to a redirected home that holds its own Keychain login and the fixture CLAUDE.md. The candidate was loaded via --plugin-dir from a checkout of exactly d516ad3.
  • Surface: an interactive TUI session.
  • Observed:
    • setup-pstack resolved the redirected home, and the default Opus (native agent), Sol (external codex exec) and Grok (external) probes passed.
    • It wrote pstack-models.md and appended exactly @./pstack-models.md to the redirected CLAUDE.md, preserving the fixture text.
    • The smoke run and cross-judge passed.
    • --verify passed, an unchanged rerun (probes re-run) was byte-identical, and --verify passed again.
    • A fresh claude -p in that home quoted the fixture sentence. The daily-home control did not.
    • The daily ~/.claude targets were unchanged, according to --verify.

Codex

  • Installed version: Codex CLI 0.160.0, with the candidate installed from --ref d516ad3… into the redirected CODEX_HOME. Its auth.json is a symlink to the operator's real login.
  • Surface: codex exec.
  • Observed:
    • The Opus, Sol and Grok probes, the smoke run and the cross-judge passed, and the sheet and AGENTS.md block were written.
    • --verify passed, an unchanged rerun was byte-identical, and the fresh-session fixture check passed.
  • Note: with Explain Codex sandbox restrictions before pstack setup probes [#124] #129's permission guidance now merged, a prompt that says only "write only under the config home" makes setup refuse the external probes, because provider CLIs write their own state. Stating that the provider CLIs may update their own state resolves it. That is a wording interaction, not a defect.

Co-Authored-By: mastra-platform[bot] <284800079+mastra-platform[bot]@users.noreply.github.com>

@unfret-eal unfret-eal Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛑 3 blocking findings
🟡 Medium · Valid imported-sheet migrations fail verification
🟡 Medium · Verifier accepts extra conflicting integration content
🟡 Medium · Assignment check skips alias-led multi-lane rows
Check

if (fs.existsSync(imported)) {
const assignments = text => text.split('\n').filter(line => /^[^:#]+:\s*(?:claude:|codex:|grok:|inherit-parent\s*$|auto\s*$)/.test(line)).map(line => line.trim()).sort();
const before = assignments(fs.readFileSync(imported, 'utf8'));
if (!before.length || JSON.stringify(before) !== JSON.stringify(assignments(bytes.toString('utf8')))) fail('imported sheet assignments were not preserved');

@unfret-eal unfret-eal Bot Oct 4, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium · Imported-sheet verifier rejects required migrations

The exact assignment-list comparison rejects valid setup output. For example, an imported claude:claude-opus-4-6@max descriptor must normalize to claude:opus@max, and setup must also materialize missing roles and drop retired roles; each required transformation makes --verify fail with “assignments were not preserved,” blocking valid imported-sheet acceptance. Also reported for this defect: - Imported-sheet verify rejects setup's required migrations (tests/setup-config-home-repro.sh:111): Still present at this head (carried v1:3cd50c5e). Line 111 requires the redirected sheet's sorted assignment lines to equal the daily sheet's exactly. Setup must change some rows on load: it normalizes claude:claude-opus-* and claude:claude-fable-* to the rolling aliases, drops retired rows such as how critics, materializes missing role rows, and replaces out-of-matrix rows such as 1.4.x grok:grok-4.6. Step 7 (SKILL.md:91) explicitly permits the documented normalization. With such a daily sheet, a correct setup run fails --verify with 'imported sheet assignments were not preserved'. The operator cannot complete imported-sheet acceptance without editing daily config. The static 2-row fixture passes only because it is an output setup could never emit unchanged; setup would materialize the other roles. - Imported-sheet verifier rejects required migrations (tests/setup-config-home-repro.sh:111): The verifier requires the pre- and post-setup assignment lists to be exactly equal. A supported imported sheet containing versioned Fable/Opus descriptors, a retired role, or a missing documented role must be normalized, pruned, or completed by setup, so a correct run exits with imported sheet assignments were not preserved instead of passing acceptance. - Valid imported-sheet migrations fail verification (tests/setup-config-home-repro.sh:111, tests/setup-config-home-repro.sh:110): This exact assignment-list equality rejects transformations that setup explicitly requires. If the imported sheet uses a versioned Fable/Opus predecessor, omits a documented role, or contains a retired role, setup correctly normalizes, materializes, or drops rows, but --verify reports that assignments were not preserved. The static fixture avoids this by manually writing the same incomplete two-row sheet rather than setup's valid complete render. Also reported for this defect: - Imported-sheet verifier rejects required migrations (tests/setup-config-home-repro.sh:110): This comparison requires the old and new assignment-line sets to be identical. For an imported sheet with versioned Fable/Opus predecessors, a retired role, or missing current roles, setup is required to normalize, drop, or materialize rows; the correct output therefore fails with imported sheet assignments were not preserved. The static fixture's two-row input likewise could not be emitted unchanged by a real setup, which must materialize the remaining roles. - Imported-sheet verify rejects setup's mandated normalizations (tests/setup-config-home-repro.sh:111): Line 111 requires the redirected sheet's assignment lines to exactly equal the daily snapshot's. Setup step 2 is required to change some rows: it migrates versioned claude:claude-opus-* descriptors, drops retired rows such as how critics, materializes missing role rows, and requires replacing out-of-matrix rows. CHANGES 1.5.0 notes that a 1.4.x grok:grok-4.6 row persists until setup asks, and also tells users to delete role lines. With such a daily sheet, a correct setup run fails with 'imported sheet assignments were not preserved'. The operator cannot complete imported-sheet acceptance without editing daily config. This fails closed. The static case passes only because it treats a 2-row sheet as correct output, which setup never produces. Also reported for this defect: - Imported-sheet verify rejects setup's required migrations (tests/setup-config-home-repro.sh:111): Carried v1:3cd50c5e is still present. Line 111 requires the sorted assignment lines of the daily sheet and the redirected sheet to be exactly equal. Setup is required to change rows on load: it normalizes claude:claude-opus-*, drops retired rows such as how critics, materializes missing roles, and forces replacement of out-of-matrix rows such as 1.4.x grok:grok-4.6. If the daily ~/.claude/pstack-models.md needs any of these, a correct setup run fails --verify with 'imported sheet assignments were not preserved'. Imported-sheet acceptance then cannot complete without editing daily config. The 2-row static fixture passes only because setup could never emit it unchanged. - Verifier rejects required imported-sheet migrations (tests/setup-config-home-repro.sh:111): For an imported sheet containing a versioned Fable/Opus descriptor, missing documented role, or retired role, setup is required to normalize, complete, or prune the map. This exact before/after assignment-list comparison therefore rejects correct output with imported sheet assignments were not preserved, blocking valid imported-sheet acceptance. - Exact comparison rejects valid imported-sheet migrations (tests/setup-config-home-repro.sh:111): The verifier requires the imported and generated assignment lines to be byte-equivalent after sorting. For an imported sheet containing a supported predecessor such as claude:claude-opus-4-6@max, a retired role, or a missing documented role, setup must normalize, drop, or materialize rows. That correct output therefore makes --verify fail with “assignments were not preserved,” blocking imported-sheet acceptance.

Now at tests/setup-config-home-repro.sh:111

Also affects:

const bytes = fs.readFileSync(sheet);
const imported = path.join(run, 'imported-sheet');
if (fs.existsSync(imported)) {
const assignments = text => text.split('\n').filter(line => /^[^:#]+:\s*(?:claude:|codex:|grok:|inherit-parent\s*$|auto\s*$)/.test(line)).map(line => line.trim()).sort();

@unfret-eal unfret-eal Bot Oct 4, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium · Alias-led panel rows bypass assignment verification

The extraction regex recognizes inherit-parent and auto only when they end the line. A supported panel such as arena runners: inherit-parent, codex:gpt-5.6-sol@max, grok:grok-4.7@xhigh is omitted from both snapshots. If setup drops the Codex lane while another ordinary row keeps the list nonempty, --verify still succeeds and falsely certifies changed fan-out. Also reported for this defect: - Alias-led multi-lane rows escape assignment comparison (tests/setup-config-home-repro.sh:109): Carried v1:dfc04d2c is still present. The line-109 regex accepts inherit-parent or auto only at end of line, so a permitted row like arena runners: inherit-parent, codex:gpt-5.6-sol@max, grok:grok-4.7@xhigh is excluded from both lists. Scenario: setup writes arena runners: inherit-parent, grok:grok-4.7@xhigh, dropping the Sol lane. Other rows keep before nonempty and the lists still compare equal, so verify prints ok despite the changed fan-out. - Alias-led panel rows bypass preservation checks (tests/setup-config-home-repro.sh:109): The regex recognizes inherit-parent and auto only when they end the line. A supported panel such as arena runners: inherit-parent, codex:..., grok:... is omitted from both snapshots; if setup drops the Codex lane while another recognized row keeps the list nonempty, verification still exits 0 and falsely certifies changed fan-out. - Alias-led panel rows bypass assignment verification (tests/setup-config-home-repro.sh:109): The extraction regex only accepts inherit-parent or auto when they end the line. A row such as arena runners: inherit-parent, codex:..., grok:... is therefore omitted before and after setup; if setup drops or changes one of its provider lanes, another recognized row satisfies the nonempty check and --verify still prints ok, falsely certifying changed fan-out. Also reported for this defect: - Assignment filter drops alias-led multi-lane rows (tests/setup-config-home-repro.sh:109): Still present at this head (carried v1:dfc04d2c). The line-109 regex keeps a row only when its value starts with claude:, codex: or grok:, or is exactly inherit-parent or auto. A permitted panel row such as arena runners: inherit-parent, codex:gpt-5.6-sol@max, grok:grok-4.7@xhigh is therefore excluded from both the before and after lists. If setup writes arena runners: inherit-parent, grok:grok-4.7@xhigh, the Sol lane is lost and fan-out drops from 3 to 2. Line 111 still compares equal lists and verify prints ok, certifying changed routing as preserved. - Alias-led multi-lane rows are skipped (tests/setup-config-home-repro.sh:109): The regex recognizes inherit-parent and auto only at end of line, so a panel such as arena runners: inherit-parent, codex:..., grok:... is excluded from both assignment lists. Removing the Codex lane therefore still lets --verify print ok, falsely certifying preservation despite changed fan-out and routing. - Assignment check skips alias-led multi-lane rows (tests/setup-config-home-repro.sh:109): The filter regex on line 109 anchors inherit-parent\s*$|auto\s*$. A panel row whose first lane is an alias is therefore dropped from both the before and after lists. Step 7 allows such rows, for example arena runners: inherit-parent, codex:gpt-5.6-sol@max, grok:grok-4.7@xhigh. Scenario: the imported daily sheet has that row and setup writes arena runners: inherit-parent, grok:grok-4.7@xhigh, dropping the Sol lane. The two lists still compare equal, so verify prints ok and certifies 'preserved' assignments that changed the panel's fan-out. The rerun snapshot only compares later runs, so nothing else catches it.

Now at tests/setup-config-home-repro.sh:109

Co-Authored-By: mastra-platform[bot] <284800079+mastra-platform[bot]@users.noreply.github.com>

@unfret-eal unfret-eal Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛑 4 blocking findings
🔴 Critical · Dangling Claude import resets an existing customized sheet
🟡 Medium · Imported-sheet verifier rejects required migrations
🟡 Medium · Verifier accepts conflicting integration suffixes
🟡 Medium · Alias-led panel rows bypass assignment verification
Check

### 2. Load current state

Read the current parent-specific sheet when it exists. Before matrix validation, normalize only the rolling-alias predecessors that earlier pstack releases generated. A provider-qualified Claude model is migratable when its model component starts with `claude-fable-` or `claude-opus-` and the remaining revision contains only digits and hyphens. Replace that component in memory with `fable` or `opus`, preserving the provider, effort, role, and lane order. Record each original and normalized descriptor for the confirmation in step 7. This migration is valid loaded state and does not require a separate operator choice.
On Claude, if the single existing model-sheet import in `<config-home>/CLAUDE.md` points elsewhere, resolve its target relative to the importing file's directory (expanding `~` to the user's home), read current state from that imported sheet, and name the source file; if it is missing, report that and use the normal first-run path. Otherwise, read the current parent-specific sheet when it exists. Before matrix validation, normalize only the rolling-alias predecessors that earlier pstack releases generated. A provider-qualified Claude model is migratable when its model component starts with `claude-fable-` or `claude-opus-` and the remaining revision contains only digits and hyphens. Replace that component in memory with `fable` or `opus`, preserving the provider, effort, role, and lane order. Record each original and normalized descriptor for the confirmation in step 7. This migration is valid loaded state and does not require a separate operator choice.

@unfret-eal unfret-eal Bot Oct 4, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Critical · Dangling Claude import resets an existing customized sheet

New step 2 (SKILL.md:36) covers the case where the single import points elsewhere and its target is missing. It says to 'use the normal first-run path' and never reads the existing <config-home>/pstack-models.md; codex-tools.md:16 repeats this. Scenario: a user moves ~/.claude to ~/.config/claude and sets CLAUDE_CONFIG_DIR. CLAUDE.md still holds the now-dangling @~/.claude/pstack-models.md, while ~/.config/claude/pstack-models.md holds customized rows (e.g. bug-fix: claude:opus@max). Rerunning setup to repair loading presents first-run defaults as current state, with keep as the default. After confirmation, step 8 overwrites the customized destination with defaults; snapshots are restored only on failure. The default home is also affected when a dangling @~/dotfiles/pstack-models.md import exists: base read and preserved ~/.claude/pstack-models.md, but now it is overwritten. This violates step 2's rule never to reset a customized sheet.

Withdrawn in the latest review.

@ericlitman

Copy link
Copy Markdown
Owner

Step-back result: PR #123 after reviewed head 7 (abb325b)

The step-back used the same three messages as before. The advisor was GPT-6 Astra, read-only, in a checkout of abb325b; it wrote the opening, the orchestrator rebutted, and the advisor closed. This result replaces the head-7 repair list.

Why the earlier repairs didn't converge. Each one answered a single case of the same question: which existing sheet is "current" when the config home and the existing integration disagree. Head 5 added "follow an import that points elsewhere". Head 7 then found that a dangling import fell through to first-run defaults and overwrote a surviving local sheet. The root defect is the step-2 shortcut that treats a missing import target as a first run.

The rule for setup step 2 (agreed)

Inspect <config-home>/pstack-models.md and the existing integration: resolve Claude's single model-sheet import, or read Codex's single bounded model block, reporting missing targets and stopping on unreadable, malformed, or ambiguous state. Use the sole surviving configuration, or the editable sheet when both configurations agree; on Codex, the sheet remains authoritative and the block is its mirror, recoverable when the sheet is absent. If surviving configurations differ, show both sources and their differing assignments and require an explicit source choice before normalization or probing, without merging them automatically. Use first-run defaults only when neither source contains recoverable configuration, then apply the existing normalization, role-completion, and validation rules to the selected state.

Changes

  • Delete: the dangling-import-to-first-run sentence (setup:36) and the static assertion that requires it (skill-collision-repro.sh:104).
  • Replace: the duplicated selection prose in codex-tools.md:16, with a one-line pointer to the setup rule.
  • Keep:
    • imported-sheet recovery (head 5);
    • integration boundaries, snapshots and readback;
    • the legacy default-home rendering and its assertions;
    • the test helper unchanged. Its three oracle findings are inert.

Live acceptance for the next head

Installed behavior on the exact candidate, in both harnesses:

  • Dangling import: a surviving local sheet is preserved.
  • Conflicting sheets: setup shows both and asks the user to choose before probing.
  • Codex: with the sheet absent, setup recovers it from the block.
  • Re-checks: the redirected-home write, rerun byte identity, and fresh-session load are confirmed again.

No product decision is outstanding.

Co-Authored-By: mastra-platform[bot] <284800079+mastra-platform[bot]@users.noreply.github.com>

@unfret-eal unfret-eal Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛑 3 blocking findings
🟡 Medium · Imported-sheet verifier rejects required migrations
🟡 Medium · Verifier accepts extra integration content after sentinel
🟡 Medium · Alias-led panel rows bypass assignment verification
Check

@ericlitman

Copy link
Copy Markdown
Owner

Unfret waiver for head 6ef25f5. Ticket GH-120 is GitHub issue #120. All remaining blocking findings are class inert.

Finding Basis
v1:3cd50c5e, the imported-sheet verifier rejects required migrations These are oracle-precision limits of the repo-local test helper tests/setup-config-home-repro.sh, which is not shipped. It explicitly disclaims live proof (lines 95-132), and the exact-candidate live tests judge setup's real output.
v1:7d4d3bf1, the verifier accepts extra integration content after the sentinel Same basis as above.
v1:dfc04d2c, alias-led panel rows bypass assignment verification Same basis as above.

The verdict-changing findings from heads 5 and 7, about which sheet step 2 reads, were resolved by the step-back rule now at this head.

@ericlitman

Copy link
Copy Markdown
Owner

Live tests and live-gate (head 6ef25f5)

This head carries the step-back rule for setup step 2 and is level with main. Unfret shows Accepted risk, with only the three inert test-helper findings left. I ran both harnesses on this exact head. Everything passes, and I set live-gate to success on 6ef25f56618c36526383e471c28065a571bbeb4e.

Installed version.

  • Claude Code 2.1.289 with CLAUDE_CONFIG_DIR=~/.pstack-verify/claude, a redirected home with its own Keychain login. The candidate was loaded via --plugin-dir from a checkout of exactly 6ef25f56618c36526383e471c28065a571bbeb4e.
  • Codex CLI 0.160.0 in a redirected CODEX_HOME whose path contains a space and a #, with the candidate installed from --ref 6ef25f56618c36526383e471c28065a571bbeb4e. Its auth.json is a symlink to the operator's real login.

Surface: claude -p and codex exec sessions. Interactive TUI runs on earlier heads are linked above.

# Scenario Setup before Observed
1 Claude: dangling import, customized local sheet survives CLAUDE.md imports a missing ~/.claude-moved-away/pstack-models.md. The local sheet has bug-fix: claude:opus@max. Setup used the surviving local sheet. All three probes, the smoke run and the cross-judge passed. It wrote bug-fix: claude:opus@max (preserved) and replaced the import with @./pstack-models.md, keeping the fixture text.
2 Claude: the two surviving sheets differ The import points at an existing sheet with bug-fix: grok:grok-4.7@xhigh. The local sheet has claude:opus@max. Before any probe, setup showed both sources and the differing row and asked for an explicit choice. All three files were byte-identical afterwards, so nothing was written. In a follow-up run that chose the imported sheet, it probed and wrote bug-fix: grok:grok-4.7@xhigh and @./pstack-models.md.
3 Codex: the sheet is absent, the AGENTS.md block survives The block has bug-fix: claude:opus@max. There is no pstack-models.md. Setup reported "Current assignments came from the bounded block". The probes, smoke run and cross-judge passed. It wrote pstack-models.md with bug-fix: claude:opus@max, and the AGENTS.md bytes were preserved.
4 Re-checks in both harnesses Unchanged rerun The Claude and Codex outputs were each byte-identical to the previous run. A fresh session in each redirected home quoted the fixture sentence. The Codex auth.json was still a symlink. The daily ~/.claude targets were unchanged (SHA-256 before and after).

@mergify

mergify Bot commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Merge Queue Status

  • ✅ Entered queue — 2026-10-04 14:23 UTC · Rule: default · triggered by merge protections
  • ✅ Checks skipped · PR is already up-to-date
  • ✅ Merged — 2026-10-04 14:23 UTC · at e46bc891b81b356736f3ee7f312f175984d277af · merge

This pull request spent 9 seconds in the queue, including 1 second running CI.

Required conditions to merge
  • github-review-approved [🛡 GitHub repository ruleset rule Protect main]
  • all of [🛡 Merge Protections rule Queue ready pull requests]:
  • any of [🛡 GitHub repository ruleset rule Protect main]:
    • check-success = @github-actions/verify
    • check-neutral = @github-actions/verify
    • check-skipped = @github-actions/verify
  • any of [🛡 GitHub repository ruleset rule Protect main]:
    • check-success = live-gate
    • check-neutral = live-gate
    • check-skipped = live-gate
  • any of [🛡 GitHub repository ruleset rule Review gate]:
    • check-success = @unfret-eal/Unfret
    • check-neutral = @unfret-eal/Unfret
    • check-skipped = @unfret-eal/Unfret

@mergify
mergify Bot merged commit e46bc89 into main Oct 4, 2026
5 checks passed
andrewfree pushed a commit to andrewfree/open-pstack that referenced this pull request Oct 4, 2026
Bring in ericlitman/open-pstack main at e46bc89: setup reads and writes
its sheet in the active config home (ericlitman#120), with the config-home
invariant and repro tests. The local Cursor and Kimi setup paragraph
is kept. The runner is unchanged. Version 1.5.1-local.9.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
andrewfree pushed a commit to andrewfree/open-pstack that referenced this pull request Oct 4, 2026
Review of the ericlitman#123 merge found the runner reading ./config.toml for an
empty CODEX_HOME, which skipped the Codex MCP switch-off and trust
cleanup, and passing an empty KIMI_CODE_HOME to Seatbelt, which refuses
it. Both now fall back to the default home, matching the config-home
rule ericlitman#123 documents. The local.9 changelog entry now lists the
default-home setup changes ericlitman#123 makes instead of calling them none.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

setup-pstack: honor CLAUDE_CONFIG_DIR and CODEX_HOME for its writes

1 participant