Metadata-Version: 2.4
Name: opencode-pine2pyne
Version: 0.2.4
Summary: Pine Script v6 to PyneCore Python transpiler
Author: rubycell
License-Expression: Apache-2.0
Project-URL: Homepage, https://github.com/rubycell/pine2pyne
Project-URL: Repository, https://github.com/rubycell/pine2pyne
Keywords: pinescript,tradingview,transpiler,pynecore,backtesting
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Office/Business :: Financial :: Investment
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: runtime
Requires-Dist: opencode-pyneruntime[cli]>=6.9.1; extra == "runtime"
Dynamic: license-file

# pine2pyne - Pine Script to PyneCore transpiler

Snapshot written 2026-10-19 at origin/main `dd8b5d3` (branch `card-118-local-named-like-method`, clean apart from this README.md).
The old README is kept at `/home/mike/workspace/github/pine2pyne/backup/README.before-2026-10-19.md` (git-ignored).
Each fact below names its source file. A fact marked UNVERIFIED was not checked in this snapshot.

Backlog and known bugs: [Projects board #10](https://github.com/users/rubycell/projects/10)
(Backlog = open, P0/P1/P2; Done = shipped). The board page itself was not opened in this snapshot: UNVERIFIED.

## 1. What this repo is

- A transpiler. It reads a TradingView Pine Script file (`.pine`) and writes a PyneCore Python file (`.py`).
  Source: `pine2pyne/__init__.py` (`transpile()`), `pyproject.toml` (description "Pine Script v6 to PyneCore Python transpiler").
- Users: the strategy developers of this workspace. They transpile a strategy, then run or optimize it with the `pyne` CLI
  of the PyneCore runtime (repo `/home/mike/workspace/github/pynecore`).
- Where it runs: a command-line tool and a Python library. It has no service, no port and no host of its own.
  Development happens on mike-PC. Full test suites run on Minh_pc (see section 7).
- Package: `opencode-pine2pyne` on PyPI, version `0.2.4` in `pine2pyne/__init__.py` and `CHANGELOG.md`.
  Live PyPI state is UNVERIFIED: 0.2.4 is prepared (commit and local tag `v0.2.4`), not uploaded. 0.2.3 is on PyPI.
  `CHANGELOG.md` lists #119 and its export case under 0.2.4; the tag `v0.2.3` is the release before them.
- The transpiler imports only the standard library (`pyproject.toml`: `dependencies = []`).
  The generated script needs the runtime `opencode-pyneruntime` (`runtime` extra, `>=6.9.1`).
- License: Apache-2.0 (`pyproject.toml`, `LICENSE`).

## 2. Architecture

The pipeline is in `transpile()` in `/home/mike/workspace/github/pine2pyne/pine2pyne/__init__.py`:

```mermaid
flowchart LR
  A[.pine source] --> B[parse_source<br/>lexer + parser + parse_repair]
  B --> C[qualify_legacy_function_names<br/>v1-v4 names to v5 names]
  C --> D[enforce_pine_semantics<br/>refuse what TradingView refuses]
  D --> E[Transformer<br/>Pine AST to PyneCore AST]
  E --> F[CodeGenerator]
  F --> G[.py script]
  G --> H[pyne run / pyne optimize<br/>PyneCore runtime]
```

- Lowering passes run in the parser and in the transformer. They rewrite a Pine construct into plain
  statements. Examples: `once`, `when=` of a strategy call (v1-v5), grouped history `(a + b)[1]`,
  block values of `if`/`switch`, strict `and`/`or` for Pine v5 and earlier.
  Sources: imports in `pine2pyne/parser.py` and `pine2pyne/transformer.py`.
- Two tables are generated from an installed runtime: `pine2pyne/namespace_members.py` and `pine2pyne/bool_builtins.py`
  (both say "GENERATED ... from opencode-pyneruntime 6.9.4; do not edit by hand").
  Regenerate them after a runtime upgrade with `tools/collect_namespace_members.py` and `tools/collect_bool_builtins.py`.
- The transpiler refuses a script when TradingView refuses it (`SemanticError`, card #28) and warns with
  `SemanticWarning` when it only advises. `pyproject.toml` turns an unexpected `SemanticWarning` into a test failure.
- Runtime code placement rule: group a new runtime member beside its kin. Source: `CLAUDE.md`, "Runtime Code Placement".
- Three separate test systems check it (section 7). A green run of one says nothing about the others.
  Source: `CLAUDE.md`.

## 3. Modules

All in `/home/mike/workspace/github/pine2pyne/pine2pyne/` (33 `.py` files, 13,176 lines in total by `wc -l`).

Core pipeline:
- `lexer.py`, `tokens.py` - tokens, with indentation tracking.
- `parser.py`, `ast_nodes.py`, `ast_walk.py` - recursive descent parser, AST node classes, shared child walk.
- `parse_repair.py` - repairs a rejected known spelling of valid Pine (card #55).
- `legacy_function_names.py` - Pine v1-v4 call names to v5 names (card #116).
- `pine_semantics.py` - checks TradingView's compiler makes that the grammar does not (card #28).
- `transformer.py` - Pine AST to PyneCore AST. The largest file.
- `codegen.py` - emits Python source.
- `cli.py`, `__main__.py` - command line (`pine2pyne` command and `python -m pine2pyne`).
- `errors.py` - error types with source locations.

Support tables and typing:
- `pine_builtins.py` - maps Pine built-ins to PyneCore.
- `namespace_members.py`, `bool_builtins.py` - generated runtime tables.
- `symbol_table.py`, `type_inference.py`, `bool_value_typing.py` - scopes, Series or Persistent typing, bool typing.
- `import_resolver.py`, `emitted_names.py` - import lines; emitted names that must not collide.

Lowering and refusal passes:
- `scope_renamer.py` - gives a shadowing local, or a binding named like a runtime name, a name of its own.
- `library_collision.py` - a user binding named like a `pynecore.lib` name.
- `overload_resolution.py` - same-name functions and methods.
- `once_lowering.py`, `strategy_when_lowering.py`, `block_value_lowering.py`, `grouped_history_lowering.py`.
- `for_loop_end_bound.py`, `strict_boolean.py`, `udt_field_defaults.py`, `conditional_history_calls.py`.

Other top-level folders:
- `tests/` - 86 `test_*.py` files, fixtures in `tests/fixtures/`, old strategy outputs in `tests/ground_truth/`.
- `tools/` - corpus harnesses, ground-truth tools, table generators (section 7).
- `sample/pinescript/` (413 `.pine` files) and `sample_pds/` (331 `.pine` files) - the corpus. Counts by `ls | wc -l`.
- `sandbox/` - Docker image (section 6).
- `docs/` - `test_plan.md`, `instructions/` (optimizer guides), `misc/`, `code_review/`.
- `ground_truth/` - tracked baseline: `digests.json` and `manifest.json` only.
- `other_transpiler/`, `runs/`, `scripts/`, `strategy/`, `upstream-run/`, `agents/`, `.claude/agentandskill/` - strategy
  experiments and agent prompts. Their use today is UNVERIFIED.
- `workdir/` - git-ignored scratch folder for runs (`.gitignore`). `backup/` and `dist/` are git-ignored too.
- `pine2pyne/README.md` is the package's own README (set as package data in `pyproject.toml`).
  It may be stale (it says "transformer.py (1626 lines)"): UNVERIFIED.

## 4. Services

None. The repo starts no long-running process: no systemd unit, no cron job, no listening port.
Source: no unit, crontab installer or server entry point found in the repo; `pyproject.toml` has one console script,
`pine2pyne = "pine2pyne.cli:main"`. Distributed optimizer runs start remote `pyne optimize` processes over SSH
from tools that live in another repo (section 7).

## 5. Dashboard and its features

None. This repo has no web UI. Source: no web framework or HTML page in the repo.

## 6. Docker

- One Dockerfile: `/home/mike/workspace/github/pine2pyne/sandbox/Dockerfile`. Base image `python:3.13-slim`.
  Build arg `SOURCE=pypi` (default) installs `opencode-pine2pyne` and `opencode-pyneruntime[cli]>=6.9.1` from PyPI.
  `SOURCE=local` installs the wheels in `sandbox/wheels/`.
  The user is `pyne`, the work folder is `/work`, and the default command prints `pine2pyne --version`, then opens a shell.
- Suggested image name in `sandbox/README.md`: `pyne-sandbox`. Run with `-v "$PWD/workdir:/work"`.
  `sandbox/smoke.sh <data.ohlcv>` transpiles a small strategy and runs `pyne run` on it.
- `sandbox/wheels/` is stale: it holds `opencode_pine2pyne-0.1.0` and `opencode_pyneruntime-6.6.4` (file names, dated 2026-07-31).
  `git ls-files sandbox/wheels` lists nothing: the wheels are not tracked.
- No compose file, no ports, no volumes other than the `/work` mount in `sandbox/README.md`.
- Live state, read only, 2026-10-19. `ssh -o BatchMode=yes minh@minhpc 'docker ps -a; docker images'` and
  `docker ps -a; docker images` on mike-PC show no `pyne-sandbox` image and no pine2pyne container.
  The containers seen there belong to other projects (`vnstock-openclaw-trial-*`, `botrehearsal-trial-*`, `svb-sandbox:*` images).
  Result: the sandbox image is not built on either host.

## 7. Commands

Run all commands from the repo root with the venv active. Paths and options come from `tools/*.py` and `pine2pyne/cli.py`.

Set up:
```bash
bash /home/mike/.claude/skills/opencode-pine2pyne-setup/setup.sh   # skill script: builds .venv, seeds demo data, smoke test
# or by hand:
python3 -m venv .venv
.venv/bin/pip install -e .
.venv/bin/pip install 'opencode-pyneruntime[cli]>=6.9.1'   # same floor as pyproject.toml [runtime]
.venv/bin/pip install optuna numpy                         # optimizer deps, not pulled in by the runtime
source .venv/bin/activate                                  # Linux and macOS; on Windows: .venv\Scripts\activate
.venv/bin/python -c "import pynecore, pine2pyne, optuna, numpy; print('all ok')"
```
The setup script ends with the lines `smoke test : PASS` and `READY`. A failure prints `READY : NO` and exits 1
(`setup.sh` lines 22, 31 and 111-112). It writes `.sandbox-env.sh` with `PINE_CPU_COUNT` and `PINE_MAX_WORKERS`
(source: `/home/mike/.claude/skills/opencode-pine2pyne-setup/setup.sh`; the file is git-ignored).
The `runtime` extra also works: `pip install "opencode-pine2pyne[runtime]"` (`pyproject.toml`).
Test tools (pytest, hypothesis) are in no manifest: a venv built from this README cannot run the unit suite
(project memory "Test deps undeclared"; not re-checked, UNVERIFIED).

Transpile (`pine2pyne/cli.py`, options `-o`, `--validate`, `-v`, `--version`):
```bash
python -m pine2pyne input.pine                  # to stdout
python -m pine2pyne input.pine -o output.py
python -m pine2pyne "sample/pinescript/*.pine" -o workdir/scripts/   # batch into a folder
python -m pine2pyne strategy.pine --validate    # parse and check only
```

Run and optimize (the `pyne` CLI of the runtime; from `workdir/`):
```bash
pyne run strategy.py data/data.ohlcv
pyne optimize strategy.py data/data.ohlcv optimize.json -n 20
```
Optimizer guides: `docs/instructions/Optimizer.md` (the root `Optimizer.md` was deleted in commit `cde04c9`;
old links to it are dead).

Test. Where each run goes: `/home/mike/.claude/where-tests-run.md`. Every full suite runs on Minh_pc through the
`qa-remote-suite` skill. A batch that checks this tool across many strategies is a test, so it runs on Minh_pc too.
On mike-PC only a targeted run is allowed, through `mike-pc-test-run`.

There are three test systems. A green run of one says nothing about the others.

1. Unit tests, pytest, `tests/` (`pyproject.toml`: `testpaths = ["tests"]`):
   ```bash
   .venv/bin/python -m pytest tests/ -q
   .venv/bin/python -m pytest tests/ --cov=pine2pyne --cov-report=term-missing
   .venv/bin/python -m pytest tests/ -m 'not corpus_runtime' -q       # skip tests that run the runtime
   .venv/bin/python -m pytest tests/ -m 'not release_artifact' -q     # skip the wheel build test (needs uv)
   ```
   - No `--ignore` is needed (`CLAUDE.md`; the screener test was retired in card #106).
   - Last measured numbers: 6,870 tests, 6,848 passed, 1 skipped, 21 xfailed, 93% line and 86% branch coverage,
     at `a16621b` (old README). Not re-run in this snapshot: UNVERIFIED. `docs/test_plan.md` holds older numbers
     (2062 passed, 93.3% line, 86.6% branch) and the mutation results.
   - Strict xfail marks a pinned, unfixed bug. Do not delete the marker to get green. When the bug is fixed the
     test XPASSes, strict turns that into a failure, and you then convert the test to a regression assertion.
     Source: `CLAUDE.md` ("6 sites (21 test cases)" there; not re-counted, UNVERIFIED).
   - Guard for the operator-grouping bug (card #6): `tests/test_codegen_operator_grouping.py`.

2. Sample suites, end-to-end transpile and `pyne run` over the corpus (`tools/test_all_samples.py`,
   `tools/test_all_sample_pds.py`, shared code in `tools/_suite_common.py`):
   ```bash
   python tools/test_all_samples.py                 # sample/pinescript
   python tools/test_all_sample_pds.py              # sample_pds
   python tools/test_all_samples.py "ex_001*"       # filter by pattern
   python tools/test_all_samples.py --timeout 30 --verbose -j 0
   python tools/test_all_samples.py --run-dir /tmp/run1   # private scripts/, output/ and reports (card #70)
   ```
   - Defaults: data `workdir/data/demo.ohlcv`, timeout 15 s, `-j` 24 (`tools/test_all_samples.py`).
     Reports go to `test_results{,_pds}.{json,txt}` at the repo root (git-ignored), or into `--run-dir`.
   - Latest local reports, 2026-10-02 08:45 (`test_results.json`, `test_results_pds.json`; local files, not tracked):
     - `sample/pinescript`: 411 run, 2 skipped, 411 transpiled, 393 passed, 18 failed
       (11 `pynecore_api`, 5 `missing_security_data`, 2 `type_error`).
     - `sample_pds`: 328 run, 3 skipped, 319 transpiled, 322 passed, 6 failed (all `missing_security_data`).
       Of the 322 passes, 304 ran clean and 18 are expected-error samples (`expect_error` in
       `tools/sample_expectations.toml`): 9 `pine_refusal` (transpile refused, so not in the 319),
       5 `index_error` and 4 `value_error`. The report's `runtime_failed` is 15 = 6 failures + 5 + 4 expected-error passes.
       (As of 2026-10-20 `expds_160` is no longer declared: TradingView does not halt on it, measured. The declared
       `index_error` samples are 4 now; the report above was made before that.)
       Counted from the `results` list of `test_results_pds.json` (passed, transpile, runtime, error_type per sample).
   - `CLAUDE.md` holds the same numbers (table "Converting Pine Script to PyneCore Python"). Its old 2026-08-17
     figures (391/411, 304/328) were replaced on 2026-10-19.
   - Per-sample rules are in `tools/sample_expectations.toml` (25 sections). Keys: `expect_error` (must fail with that
     category; running clean is a FAIL), `requires_feeds` (extra feeds, passed as `--security TF=name`), `runs_clean`,
     `card`, `refusal`, `why`. Pytest validates the file (`tests/test_error_demo_claims.py`).
   - A feed for `requires_feeds` is made with `cd workdir && pyne data aggregate data/demo.ohlcv -tf 2D`.

3. Ground truth, numeric regression (`tools/generate_ground_truth.py`, `tools/compare_ground_truth.py`,
   `tools/ground_truth_check.py`, shared code in `tools/_ground_truth_common.py`):
   ```bash
   python tools/generate_ground_truth.py            # (re)build the baseline; only when the new output is confirmed correct
   python tools/compare_ground_truth.py             # compare current output with the baseline
   python tools/compare_ground_truth.py --suite sample_pds -j 12
   python tools/ground_truth_check.py capture       # then: compare. Local check, writes only ground_truth_check/ (git-ignored)
   ```
   - `capture --force` moves the old `ground_truth_check/` to `backup/deleteable/ground_truth_check_<time>/`;
     it deletes nothing (`tools/ground_truth_check.py` lines 27-28, 201-206).
   - Baseline: `ground_truth/digests.json` (sha256 and line count per output file, plus the data sha256) and
     `ground_truth/manifest.json`. The output CSVs (about 920 MB) are git-ignored (`.gitignore`, card #62).
   - Current baseline (`manifest.json`, generated 2026-10-20 on pynecore `ae6f03172d6127e88c6ae4f2ced61f0c93a6a331`):
     562 tested, 551 passed, 9 transpile failures, 2 runtime failures, 0 timeouts, 1,637 output files.
     `digests.json` has 551 samples. The 2026-10-03 baseline (550 passed, 1,634 files) matched no current runtime;
     it was made on a pynecore side branch (`88741ade6092`, checked on `ex_001` only).
   - `pynecore_commit` (in `digests.json` and `manifest.json`): the full sha of the pynecore checkout that the `pyne`
     in use imports, written by `generate_ground_truth.py`. `-dirty` is added when a tracked file under that
     checkout's `src/` differs; the value is `null` when the runtime is not a pynecore checkout (`src/pynecore` of a
     git work tree). `compare_ground_truth.py` prints the baseline and the runtime commit and WARNS when they differ,
     or when either is `-dirty` or unknown; it never refuses for this reason.
   - Data: `workdir/data/VN30F1M_15m.ohlcv`, data sha256 `e9ef984b09eaa6d2...` (`digests.json`).
     Capital 10,000,000,000 and timeout 120 s per sample (`tools/_ground_truth_common.py`).
     Why this capital: at 1,000,000 one VN30F1M contract costs more than the equity, so nearly every strategy
     records zero trades (card #57; `tools/_ground_truth_common.py` lines 14-18).
   - Exit codes of compare: 0 = every recorded sample verified (skipped nondeterministic files are listed, not judged);
     1 = digests missing or empty, a regression, a new failure, an unverified recorded sample or a stale mark;
     2 = `DATA CHANGED` (the data file is not the one of the baseline).
   - A `REGRESSION` line names the sample and the file (`tools/compare_ground_truth.py` line 271). To see what changed,
     diff that sample's CSV in the run root against the baseline copy in `ground_truth/<suite>/<sample>/`.
   - `--suite sample` or `--suite sample_pds` rebuilds one suite and keeps the other suite's digests. It refuses
     (exit 1) unless a baseline made on the same data AND the same known `pynecore_commit` exists
     (`tools/generate_ground_truth.py` lines 250-258): one commit must describe the whole file.
   - Before a new baseline is written, the old CSV folder moves to `backup/deleteable/ground_truth_csv_<time>/`;
     nothing is deleted (`tools/generate_ground_truth.py` lines 260-267).
   - Each sample runs in its own folder under `backup/deleteable/ground_truth_runs/<tool>-<time>/` (about 0.9 GB each).
     No tool deletes them. Move old ones to the backup disk by hand, never `rm` (card #70, old README).
   - Nondeterministic plot files: 11 `sample_pds` samples call an unseeded `math.random()` in a plot, so the digest of
     their plot csv `<name>.csv` changes on every run (expds_022, 037, 039, 043, 045, 081, 091, 092, 104, 114, 115).
     `manifest.json` marks exactly those files with `nondeterministic_files` on the sample entry. The list of the 11
     samples is one tuple, `NONDETERMINISTIC_PLOT_SAMPLES` in `tools/_ground_truth_common.py`, and
     `generate_ground_truth.py` writes the marks (a full run, or `--mark-nondeterministic-only`, which changes
     only the manifest and runs no sample). Do not edit the marks by hand. `compare_ground_truth.py` skips only the
     marked files, prints each one under `SKIPPED NONDETERMINISTIC FILES (N)`, and still checks every other file of
     those samples (`_strat.csv`, `_trade.csv`, `_plot_meta.json`). A mark that no longer holds (the file matches
     its digest, or the baseline records no such file) is listed under `STALE NONDETERMINISTIC MARKS` and fails the
     run: regenerate the marks. A new sample with an unseeded `math.random()` needs its key in the tuple.
     Checked 2026-10-20 on pynecore `ae6f03172`: two compares in a row each exited 0 (551 OK, 0 REGRESSION, 11 files skipped).
   - The old shell wrapper was retired in card #66. `tools/ground_truth_check.py capture` then `compare` replace it.

Other tools in `tools/`: `corpus_transpile_ab.py --base <git ref>` (A/B of the transpiled corpus against another revision),
`_run_layout.py`, `_suite_report_compare.py`, `probes/`.

Test data. `workdir/` is git-ignored, so a fresh clone has no data. Both data files were checked on 2026-10-19:
```bash
mkdir -p workdir/data
git show 227f3d2^:workdir/data/demo.ohlcv > workdir/data/demo.ohlcv            # sha256 starts 67e827cc1a5f71ed
cp tests/fixtures/data/demo.toml workdir/data/demo.toml
(cd workdir && pyne data aggregate data/demo.ohlcv -tf 2D && pyne data aggregate data/demo.ohlcv -tf 5D)
git show 5d63d21:workdir/data/VN30F1M_15m.ohlcv > workdir/data/VN30F1M_15m.ohlcv   # sha256 starts e9ef984b09eaa6d2
cp tests/fixtures/data/VN30F1M_15m.toml workdir/data/VN30F1M_15m.toml
```
Take the `.toml` files from `tests/fixtures/data/`, not from the old commits: the old ones number weekdays 1..7,
and pynecore rejects day 7 (0 = Monday ... 6 = Sunday). Source: old README, project memory "pyne runtime gotchas".
The `/opencode-pine2pyne-setup` skill seeds a small synthetic fixture instead. That is fine for a smoke test, but the
results do not match the recorded numbers.

Lint: no linter config found in `pyproject.toml`. UNVERIFIED for any other file.

Deploy (release to PyPI): use the skill `/home/mike/.claude/skills/pypi-publish/SKILL.md`. The repo has no CI file
and no Makefile. `CHANGELOG.md` is the release log; add the entry for #115 to #118 at the next release.

Distributed optimization. `CLAUDE.md` and the old README name `tools/pyne-dynamic.sh`, `tools/pyne-parallel.sh` and
`tools/machines.txt`. **These files are not in this repo.** They moved out in commit `227f3d2`
("move workdir/ and tools/ out to the pinescript workbench"). They exist at
`/home/mike/workspace/github/pinescript/tools/pyne-dynamic.sh`, `.../pyne-parallel.sh` and `.../machines.txt`.
Use those paths. That they run unchanged against this repo is UNVERIFIED. The tool options are in `CLAUDE.md`
("Distributed Optimization") and `docs/misc/DynamicJobQueue.md`. `CLAUDE.md` also holds the cluster setup, the
machine file format and the troubleshooting table (headings "Shared: Machines file format", "Adding a new node",
"Troubleshooting").

## 8. Data and keys

- Corpus: `sample/pinescript/` and `sample_pds/` (tracked `.pine` files).
- Test data: `workdir/data/` (git-ignored; restore with the commands in section 7).
- Baseline: `ground_truth/digests.json`, `ground_truth/manifest.json` (tracked).
- Run output: `workdir/scripts/`, `workdir/output/`, `test_results*.json|txt` (git-ignored).
- Disposable output: `backup/deleteable/` (git-ignored). Disk rule: `~/.claude/CLAUDE.md`, "Never Delete on the root disk".
- Key names only. `/home/mike/workspace/github/pine2pyne/.env` holds `PYPI_TOKEN`. `.gitignore` excludes it
  (`.env`, `.env.*`, `*.pem`, `*.key`, `.pypirc`, `.netrc`). `git ls-files .env` shows it is not tracked.
- Environment names: `PINE_CPU_COUNT` and `PINE_MAX_WORKERS` (written by the setup skill into `.sandbox-env.sh`).
  `PYNE_OPTIMIZE_MODE=1` is set by the optimizer in
  `/home/mike/workspace/github/pynecore/plugins/pyne_optimize_and_stats_tools/pynecore_optimize_and_stats_tools/optimize.py`
  (lines 312, 832 and 887).
- No other key or secret file was found in the repo files that were read.

## 9. Related repos and skills

Calls this repo:
- `/home/mike/workspace/github/pinescript` (the strategy workbench). Its `CLAUDE.md` says the transpiler and runtime
  moved out to `pine2pyne` and `pynecore`, and lists two ways to get the toolchain (editable installs, or PyPI).
  The workbench holds `workdir/` and the cluster scripts of section 7.
- The skills below call `pine2pyne`. They are listed in the session skill list; their code was not read here.
  `opencode-pyne-run`, `opencode-pine-test-run`, `sandbox-e2e`, `opencode-strategy-optimize`.

This repo calls:
- `/home/mike/workspace/github/pynecore` (runtime `opencode-pyneruntime`, the `pyne` CLI). The `.venv` of this repo
  imports it from `/home/mike/workspace/github/pynecore/src/pynecore/` (checked with `import pynecore`).
  The runtime is a fork of PyneSys/pynecore. Never change original pynecore files (see that repo's rules).
- Skills it uses: `opencode-pine2pyne-setup`, `pypi-publish`, `qa-remote-suite`, `mike-pc-test-run`.
  Skills live in `/home/mike/.claude/skills/`.

Rules for every repo are in `/home/mike/.claude/CLAUDE.md`. This README does not copy them. Repo-specific
instructions (runtime code placement, pinned xfail bugs, pipeline notes) are in
`/home/mike/workspace/github/pine2pyne/CLAUDE.md`.
