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
5 changes: 5 additions & 0 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@
"name": "thermos",
"source": "./portable/thermos",
"description": "Independent correctness and code-quality branch reviews."
},
{
"name": "pstack-mods",
"source": "./portable/pstack-mods",
"description": "Opt-in Claude Mods bug-fix evidence gates. Validated against Claude Code 2.1.289."
}
]
}
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ README.md -whitespace
portable/pstack/** -whitespace linguist-generated=true
thermos/** -whitespace
portable/thermos/** -whitespace linguist-generated=true
portable/pstack-mods/** -whitespace linguist-generated=true
14 changes: 13 additions & 1 deletion .github/workflows/validate-pstack-portable.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Validate portable pstack and Thermos
name: Validate portable pstack, Thermos, and Mods

on:
push:
Expand All @@ -11,6 +11,7 @@ on:
- 'README.md'
- 'README_PORTABLE.md'
- 'docs/thermos-*.md'
- 'docs/pstack-mods*.md'
- '.gitattributes'
- 'tools/portable/**'
- '.claude-plugin/**'
Expand All @@ -26,6 +27,7 @@ on:
- 'README.md'
- 'README_PORTABLE.md'
- 'docs/thermos-*.md'
- 'docs/pstack-mods*.md'
- '.gitattributes'
- 'tools/portable/**'
- '.claude-plugin/**'
Expand All @@ -51,6 +53,16 @@ jobs:
- run: python -m pip install PyYAML==6.0.3
- run: python3 tools/portable/validate.py
- run: python3 -m unittest discover -s tools/portable -p 'test_*.py'
- name: Validate Mods with pinned native Claude Code
env:
DISABLE_AUTOUPDATER: '1'
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: '1'
run: |
curl -fsSL https://claude.ai/install.sh -o "$RUNNER_TEMP/install-claude.sh"
bash "$RUNNER_TEMP/install-claude.sh" 2.1.289
export PATH="$HOME/.local/bin:$PATH"
test "$(claude --version)" = '2.1.289 (Claude Code)'
bash tools/portable/check-mods.sh
- run: bash -n tools/portable/sync-upstream.sh
- run: sh -n portable/pstack/hooks/session-start.sh
- name: Install the skills for both agents in an isolated project
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,7 @@ build/

# Logs
*.log

# Runtime-generated Claude Mods declarations
/portable/pstack-mods/.claude-plugin/types/
/tools/portable/mods-assets/.claude-plugin/types/
2 changes: 2 additions & 0 deletions README_PORTABLE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ This is a fork of [cursor/plugins](https://github.com/cursor/plugins), maintaine

For Thermos installation and review commands, see [Thermos for Claude Code and Codex](docs/thermos-portable.md).

For an optional Claude-only bug-fix evidence pipeline, see [pstack Mods](docs/pstack-mods.md). It runs only after explicit opt-in and leaves the existing portable workflows unchanged.

## Install

### Skills CLI (Claude Code and Codex)
Expand Down
60 changes: 60 additions & 0 deletions docs/pstack-mods-verification.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Claude Mods bug-fix verification

Verified on 2026-10-06 in an isolated Linux cloud checkout of `yxhta/pstack`, based on `5f83e9274167e4e6fa50f48e9e4ab847190a3ec6`. The unchanged upstream pin is `e43c7ee26e0038c6c1fa8380dd34ce86ff94cb2a`.

## Results

- The full Python suite passed **126 tests**, including 24 real-process/snapshot cases and 14 generated-package cases for the adapter.
- The official Claude Code **2.1.289** test harness passed **85 tests** against the generated adapter.
- Strict native plugin validation, TypeScript **5.9.3** with the runtime-generated 2.1.289 declarations, and a real no-inference `/pstack-bugfix status` load all passed.
- Source integrity, normal and optimized Python validation, generation freshness, shell syntax, existing plugin schema validation, and whitespace checks passed.
- The new Claude native plugin installed and enabled in a disposable home. No normal user configuration changed.
- Skills CLI **1.7.0** copy installation verified every skill file and executable mode for both Claude Code and Codex. Its default linked installation also preserved all **55** coexisting pstack and Thermos skills and their shared references.
- Codex CLI **0.159.2** installed the unchanged native pstack and Thermos packages in a disposable home. Its actual `skills/list` loader found all three project Thermos skills and all three namespaced plugin skills without errors. The Mods package is absent from the Codex marketplace.
- `pstack/`, `thermos/`, root `README.md`, existing `portable/pstack/` and `portable/thermos/`, and the Codex marketplace remained byte-for-byte unchanged from the base, including executable modes.

The implementation was reviewed independently for correctness and code quality before publication. Review found ordinary concurrent-edit and cancellation races, a raw-byte baseline gap, and a generated-bytecode packaging issue. Fixes have executable regression coverage.

## What ran for real

The Python fixtures create disposable Git repositories and execute actual child processes. They verify a failing assertion followed by a passing fix, repeated execution, actual exit codes, missing programs, signals, timeouts, bounded output, and process-group cleanup after a terminated helper. They also exercise tracked, staged, untracked, deleted, mode-changed, and committed state; preserved-mtime edits; mid-scan changes; ignored outputs; unsupported index flags and filters; and raw clean-tree checks independent of Git's status cache.

The native load smoke runs a registered command through the actual Claude binary. It prints the inactive status, generates that build's types, and performs no inference. The test script uses a fresh home and removes the temporary plugin copy afterward.

## Additional review cycle

A second requested self-review and a fresh adversarial review found two scope errors and one packaging issue. Git's cached timestamps could hide a raw source change from the review diff. Replacement refs could reinterpret the recorded baseline. A normal native load also created type declarations that the freshness check treated as package drift.

Review scope now compares raw file bytes and modes with immutable baseline objects. It builds the patch from isolated before-and-after files, independent of the real Git index's stat cache. Fixtures cover preserved timestamps, replacement refs, binary files, deletion, rename, file-to-directory changes, modes, color configuration, and concurrent edits. Only the five known native declaration sidecars are excluded from package freshness. The committed TypeScript config and all unexpected files remain checked. The real native load smoke now verifies this boundary.

The pstack workflow guided the review cycle. A single owner kept the state machine and runtime changes together; packaging and tests had separate owners. Independent reviewers checked complete diffs, the no-comments pass removed redundant narration, and every accepted defect received an executable regression before another full check.

## What was stubbed

The 85 native tests execute the production hooks through Claude's official test harness. Host process results, filesystem reads, agent listings, identities, and completion events are fixtures. They cover opt-in, missing evidence, repeated commands, snapshot invalidation, failures and missing child results, stale or malformed review reports, required whole-file reads, interruption, cancellation during awaited operations, session restart/reload, bounded Stop, and render-tree output on terminal and desktop surfaces.

Claude 2.1.289 has a test-adapter quirk for `agent.spawn`. The fixture supplies both its public result and the internal `result: { agentId, resolvedModel }` envelope expected by that build's adapter. A focused native regression and generated-type check validate this fixture. It is an undocumented, version-pinned testing accommodation. Production code does not fabricate an agent identity or accept this envelope as evidence. Successful native tests establish behavior with simulated host identities and completion events.

## Not established

- A model-driven end-to-end bug fix or actual reviewer spawn inside authenticated Claude Code
- Review quality, actual model effort support, plan capacity, or real-host reviewer latency
- Terminal or desktop pixel layout; the official harness checks render trees, not application painting
- macOS execution; the helper uses Unix APIs, and Linux was the tested platform
- Windows support, hostile-plugin resistance, atomic filesystem isolation, universal write prevention, or guaranteed cancellation of detached descendants or a helper killed with SIGKILL
- Ignored files, changing services, environment variables, dependencies outside the repository, or other external state

No credentials were copied, login grants created, external model APIs called, or real-model fallback purchased. `verified` is a bounded local evidence state, not an absolute correctness or security guarantee. The operational limits and recovery commands are documented in [pstack Mods](pstack-mods.md).

## Reproduce the checks

```sh
python3 tools/portable/prepare.py --check
python3 tools/portable/validate.py
python3 -O tools/portable/validate.py
python3 -m unittest discover -s tools/portable -p 'test_*.py'
CLAUDE_BIN=/path/to/claude-2.1.289 bash tools/portable/check-mods.sh
git diff --check
```

The portable GitHub Actions workflow runs the Python checks, pinned native Mods checks, and separate Skills CLI copy-install checks. Review the workflow results for the PR's exact head commit before treating remote CI as passed.
89 changes: 89 additions & 0 deletions docs/pstack-mods.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Opt-in bug-fix gates for Claude Code

`pstack-mods` is a separate, optional Claude Code plugin. It tracks a bug fix through reproduction, a changed code snapshot, verification, and two independent Thermos reviews. It never marks a pipeline verified from the parent model's claim that tests or reviews passed.

The existing pstack and Thermos packages and Codex behavior are unchanged. This early-access adapter requires Claude Code **2.1.289**, Python **3.11 or newer**, and Git on Linux or macOS. Windows is not supported by this version. Later Claude versions should be checked against their own generated declarations before relying on the adapter.

## Try it without a persistent installation

From a clone of this repository, launch Claude Code in the repository being fixed:

```sh
claude --plugin-dir /absolute/path/to/pstack/portable/pstack-mods
```

For a persistent installation that you choose yourself:

```text
/plugin marketplace add yxhta/pstack
/plugin install pstack-mods@yxhta-pstack
```

Loading the plugin registers its command and tool. Gates remain off until you start a recipe. This adapter does not enable itself through the existing pstack plugin, Codex, or the Skills CLI.

## Start a bug fix

First identify a reproducible failure and the project's verification commands. Enter this command yourself, adapting its argv arrays and failure text:

```text
/pstack-bugfix start {"repro":["python3","-B","tests/repro.py"],"expectedExit":1,"contains":"AssertionError: wrong value","verify":[["python3","-B","-m","unittest","discover"]],"timeoutMs":120000}
```

The command records the recipe. It does not immediately run a process or a model. Starting a recipe authorizes the adapter to run those exact commands when its pipeline tool is called and to start the two reviewer agents. Reviewers use the session's model and plan. No alternate API, authentication, or paid fallback is configured.

Commands run in the Git repository root with no shell and no interactive stdin. They have the host user's filesystem and network access. Choose local tests whose side effects you intend. Shell expansions, pipes, and redirections are not interpreted, although explicitly configuring an interpreter or shell still runs that program. Keep test output and build artifacts ignored by Git so they do not change the checked snapshot. The reproduction must start from a clean committed working tree. Commit a newly added regression test first on your working branch. This keeps the reproduced baseline and review diff aligned; dirty-start baselines are deliberately unsupported.

Tell Claude to use `mcp__pstack-mods__bugfix` for each stage:

1. `reproduce` runs the configured reproduction. Its actual exit code must equal `expectedExit`, and stdout or stderr must contain `contains`. A missing program, timeout, signal, malformed helper result, or code change during the check cannot count. Edit, Write, and NotebookEdit are gated until this evidence exists.
2. Apply the fix. The current code snapshot must differ from the reproduced snapshot. This is a mechanical change check, not proof that a particular edit fixes the cause.
3. `verify` reruns the original reproduction and every verification command. Every process must exit zero, and the snapshot must remain unchanged. A command that claims success in text but exits unsuccessfully cannot pass.
4. `review` starts two fresh read-only agents, one for correctness and one for code quality. They read the bundled upstream Thermos roles and complete rubrics, plus every surviving changed or untracked file in full. The adapter observes successful full-file Read results, checks the host's child identity, and accepts only matching final `turn.complete` reports. Both must report clean local coverage for the exact verified snapshot.
5. The status becomes `verified` only after all of those gates pass. Publication, merge, and deployment are separate actions and are never performed by this adapter.

Repeated successful tool actions reuse current evidence; repeated `start` with the same recipe does not reset it or resume a paused pipeline. Cancel first to replace a recipe.

## Inspect, pause, and recover

```text
/pstack-bugfix status
/pstack-bugfix pause
/pstack-bugfix resume
/pstack-bugfix cancel
```

The line below the prompt and the band above it show the current stage, missing evidence, and failure reason. `status` provides the captured exits, output, snapshot IDs, and reviewer identities. Stages are `off`, `repro`, `fix`, `verification`, `independent review`, `verified`, `paused`, and `blocked`.

A source change invalidates passing verification and both reviews. The original failing reproduction stays as the baseline. The adapter checks before evidence-producing actions and normal completion, after checks, and every five seconds while active and idle. Known edits invalidate immediately, even if an edit fails. Changes to Git HEAD, the index, file names, bytes, or executable modes also invalidate evidence.

An interrupted parent turn pauses the pipeline. Failed checks or incomplete reviews block it with a reason. `resume` keeps only live reproduction evidence and requires fresh verification and reviews. If a process is still finishing, wait before resuming. Pausing or canceling discards its result; it does not promise to terminate every descendant process or already spawned read-only reviewer. A reviewer has five minutes to return a final report.

A reload, session restart, `/clear`, or `/resume` never restores successful evidence from disk. The plugin stores only a session-scoped reminder that a pipeline existed. Any stored value, including a tampered one, can at most produce `paused`; supply a new recipe and reproduce again. A new session does not inherit another session's pipeline.

## What the gates do and do not prove

- The snapshot covers tracked files, index state, HEAD, and non-ignored untracked regular files. Ignored files, environment variables, external services, dependency caches, databases, and other repositories are outside it. Changes there require you to rerun or restart appropriately. Symlinks, submodules, unresolved merges, assume-unchanged/skip-worktree index flags, content filters, and working-tree encoding attributes are deliberately unsupported. Raw baseline bytes and Git modes are compared with the committed tree rather than trusting cached Git status. Review paths and patches also use raw before-and-after file contents and ignore Git replacement refs. A preserved modification time cannot hide a fix from the reviewers.
- Checks run against the working directory, not an atomic filesystem snapshot. Changes observed between checks invalidate evidence. A malicious edit-and-restore race, altered host, or hostile plugin is outside this workflow's trust model.
- Configured exit codes and failure text identify an observed test execution. They do not prove that the selected command is a meaningful test, that a suite covered every case, or that the test itself is honest.
- Fresh child identity and observed reads establish independent execution and reading coverage. A clean model verdict remains a review judgment, not proof of bug absence. The reviews are local and pre-PR; PR discussion and external integration checks require separate work.
- Reviews larger than 100 changed files or a 200,000-byte diff block. A token-truncated or partial Read does not count as a complete file read. Reduce the review scope or use the ordinary portable workflow; do not label the incomplete gate verified.
- Process output is retained up to 64 KiB per stream with explicit truncation flags. A child signal is distinguished from a test's nonzero exit.
- Mods are early-access middleware. Unhandled errors, hook timeouts, other plugins, unloading the mod, or disabled Mods can bypass hooks. Catch handlers improve the normal failure path; they do not turn this into an enforceable security policy.
- `Stop` only observes normal completion. It requests at most one continuation when evidence is missing, then pauses. It allows a wait for pending reviewers without claiming verification. It does not intercept every interruption or prevent a model from writing inaccurate prose.
- The edit guard covers named editor tools. Bash, arbitrary MCP tools, and other channels can change files. No Bash-string parser or universal write/push/deploy guarantee is claimed. Snapshot invalidation is the safeguard for changes the adapter observes.

## Maintain and verify

Edit `tools/portable/mods-assets/`, then regenerate. Never edit `portable/pstack-mods/` directly. Its version hashes content and executable modes. Native-generated declarations under `.claude-plugin/types/` are excluded from that hash and the freshness check. The committed TypeScript config stays checked. Required Thermos source files and their license are copied unchanged from the repository's recorded upstream pin.

```sh
python3 tools/portable/prepare.py
python3 tools/portable/validate.py
python3 -m unittest discover -s tools/portable -p 'test_*.py'
CLAUDE_BIN=/path/to/claude-2.1.289 bash tools/portable/check-mods.sh
git diff --check
```

`check-mods.sh` uses a disposable copy and isolated Claude home. It runs strict validation, the official native test harness, a real no-inference command load, and TypeScript 5.9.3 against declarations generated by that binary. It never runs an authenticated model task or changes your normal Claude settings. The test harness stubs model/host responses; it is not evidence of a completed real-model bug fix.

The implementation follows the official [creation guide](https://code.claude.com/docs/en/plugins/mods/create), [API guide](https://code.claude.com/docs/en/plugins/mods/api), [event guide](https://code.claude.com/docs/en/plugins/mods/events), [reference](https://code.claude.com/docs/en/plugins/mods/reference), and [test guide](https://code.claude.com/docs/en/plugins/mods/test), checked against native 2.1.289 declarations. [Verification results](pstack-mods-verification.md) distinguish executable checks from unverified real-model behavior.
8 changes: 8 additions & 0 deletions portable/pstack-mods/.claude-plugin/plugin.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading