Skip to content

feat(opencode): accept OpenCode 1.19+ 1.x releases - #23

Open
felipecrs wants to merge 1 commit into
xhluca:mainfrom
felipecrs:feat/opencode-1-19-support
Open

felipecrs wants to merge 1 commit into
xhluca:mainfrom
felipecrs:feat/opencode-1-19-support

Conversation

@felipecrs

@felipecrs felipecrs commented Oct 23, 2026 •

Copy link
Copy Markdown

Fixes #22

Problem

OpenCode 1.x is still maintained: 1.19.4 is the newest release on npm. The version gates only accepted the exact 1.17.20 pin or a validated 2.0.x release, so any transfer touching a 1.19.x CLI failed with:

OpenCode source CLI version mismatch: expected 1.17.20 or supported 2.0.x, observed 1.19.4

Resulting behavior

Any 1.19+ 1.x release is now accepted, since the public export, import, and session-list shapes are unchanged within the family, alongside the existing validated 2.0.x. The pinned integration target moves from 1.17.20 to 1.19.4, so writer metadata, the unvalidated-version baseline, and the CI-installed binary follow the newest 1.x release. Releases other than the exact pin still emit the existing unvalidated_source_version / unvalidated_target_version warnings.

One decision to confirm: raising the floor means 1.17.20 and 1.18.x are now rejected. I kept a floor rather than a wider window because the corpus and native evidence are validated against one pinned binary per major line. If you'd rather keep 1.17.20 accepted as well, say so and I'll rebase.

How I tested it

  • uv run ruff check . and uv run ruff format --check src tests scripts .github/scripts .github/tests
  • python3 -m unittest discover .github/tests
  • uv run pytest -q: 1749 passed, 61 skipped
  • Live smigrate transfer --dry-run on a real 1.19.4 OpenCode session: 18 portable records, clean exit, and no version mismatch
  • Gate boundary cases in test_version_schema_gate: 1.19.0 and 1.19.4 accepted, 1.17.20, 1.18.9 and 1.0.0 rejected, 2.0.x unchanged

Notes

  • The native corpus captures were produced by a 1.17.20 binary. The route oracle now expects the source-version notice exactly when a capture predates the pin, so recapturing with 1.19.4 needs no oracle change.
  • On macOS, the machine-level /Library/Application Support/opencode enabled_providers whitelist cannot be redirected with environment variables and can hide the synthetic test provider. The loopback tests probe for this and skip when it happens.

@github-actions

Copy link
Copy Markdown

🟢 Contributor eligibility requirements met for @felipecrs.

  • 🟢 Account created: 2017-06-20; age: 3412 days (required: at least six calendar months).
  • 🟢 Contributions dated on or before 2026-09-23: 1808 or more (required: at least 100).

Pull request authors must have a GitHub account at least six calendar months old and at least 100 GitHub contributions dated on or before the date one calendar month before the check.

See the contributor eligibility policy.

@felipecrs
felipecrs force-pushed the feat/opencode-1-19-support branch from 0ea8d72 to b5a49b4 Compare October 23, 2026 23:28
@felipecrs
felipecrs marked this pull request as ready for review October 23, 2026 23:32
@xhluca-agent

Copy link
Copy Markdown

Verdict: HOLD — changes required

Adversarial review — PR #23 "feat(opencode): accept OpenCode 1.19+ 1.x releases"

  • Base: main @ 45351ff95ce79684c23377920b92026ecbe9759b; head under test:
    b5a49b497c5bbcca702cd86da58c54d5f239e7b8 (prospective merge built in a detached
    worktree, tree e51fcbd2f0229ad08128d1945c7801ff9a13c9af; nothing pushed or merged).
  • The PR moves the legacy OpenCode 1.x gate from exact 1.17.20 to the 1.19+
    family (PINNED_OPENCODE_VERSION → 1.19.4), updates error text, the CI install
    pin, fixtures, docs, and the warning contract. Suite, lint, and format checks pass;
    the gate semantics themselves verified correct in a 24-case matrix and end-to-end
    with a fake CLI. Two required corrections remain (F1, F2 below).

Findings

F1 — Uncaught ValueError crash in the new version gate (reproducible, new vs base) — blocking

  • src/session_migrate/formats/opencode.py:31 defines _LEGACY_VERSION with
    unbounded (\d+) groups, and is_legacy() at opencode.py:61-66 calls
    int(match[1]) / int(match[2]) on them. A version component longer than
    CPython's int_max_str_digits limit (4300) raises ValueError, which no caller of
    supported_version() catches.
  • Reachable from user-controlled input on the PR head at src/session_migrate/conversion.py:197
    (observed --version of the resolved source CLI), :1429 and :1436
    (--target-cli-version / observed target CLI), and :1850
    (detect_opencode_version, wired from src/session_migrate/cli.py:576).
  • Minimal reproduction (bundle repro/repro_f1_huge_digit_version.py):
    python -m session_migrate import <any-source-export.jsonl> --to opencode --target-cli-version "1.<5000 nines>.0" --dry-run
    → exit 1 with a raw ValueError: Exceeds the limit (4300 digits)... traceback
    pointing at opencode.py:65. The same string printed by a fake source CLI
    (--from opencode --source-cli <fake>) crashes identically at conversion.py:197.
    On base main the identical inputs exit 2 with the clean
    OpenCode source CLI version mismatch / requires target metadata version ... errors —
    so this is a regression introduced by this PR, not inherited.
  • Required correction: bound the digit groups in _LEGACY_VERSION (e.g.
    (\d{1,9})) or wrap the int() conversion so malformed input returns False;
    add a regression test with a >4300-digit component asserting a clean
    SessionMigrateError (exit 2), for both the source and target gates.

F2 — No CHANGELOG entry for a user-visible gate change — blocking per repo convention

  • CHANGELOG.md:7 leaves ## Unreleased empty, while the PR changes observable
    behavior: 1.17.20 and 1.18.x CLIs are now rejected
    (expected 1.19+ 1.x or supported 2.0.x), and writer metadata / CI pin move to
    1.19.4. Every prior release section records such changes.
  • Required correction: add an ## Unreleased entry describing the 1.19+ floor,
    the 1.19.4 pin, and the continued unvalidated_source_version /
    unvalidated_target_version warnings for non-pinned accepted releases.

F3 — Evidence-scope note (non-blocking)

  • The CI context provided for head b5a49b4 records only the check job (pass);
    python-matrix and native-clients results were not included. The native oracle
    itself stays pinned (tests/test_additional_formats_native.py:42-43 requires the
    binary's --version stdout to equal 1.19.4 exactly), and the checked-in native
    corpus still carries 1.17.20 captures now expected to emit
    unvalidated_source_version (tests/native_corpus/route_oracle.py:497-500).
    Acceptance of 1.19.x releases beyond 1.19.4 therefore rests on unit tests and gate
    probes in this review, not on a live 1.19.x native run here. Policy note: prerelease
    suffixes are accepted (tests/test_opencode_v2.py asserts 1.19.4-nightly.abc123
    is supported) — deliberate per the tests, but worth an explicit doc sentence.

What verified clean

  • Gate semantics on merge: 1.19.0+ accepted, 1.18.x/1.17.20 rejected with the new
    message, 2.0.x still accepted via the v2 path, malformed/empty/None rejected,
    no regex blowup (2000 runs on ~600-char inputs in 0.01s).
  • Test diff audit: removed assertions were pin-constant updates only; no assertion
    weakened; fixture renames changed only the version field; warning-contract change
    in route_oracle.py is additive.
  • Docs updated consistently (docs/format-compatibility.md:23,
    docs/cli-reference.md:158, docs/troubleshooting.md:120-123,
    docs/additional-target-formats.md:11,103, docs/architecture.md,
    docs/opencode-v2.md, docs/credential-free-native-testing.md);
    scripts/install-native-test-clis.sh pin updated to opencode-ai@1.19.4.

Evidence counts (failures/skips disclosed)

  • Merge suite: 1747 passed, 63 skipped, 0 failed; baseline: 1739 passed,
    63 skipped, 0 failed — +8 passed, 0 new skips, 0 failures.
  • ruff check: pass; ruff format --check: 170 files clean;
    .github/tests unittest: 12 run, OK.
  • Adversarial probes: 24-case unit matrix on merge — 21 clean results, 3 uncaught
    ValueError crashes
    (huge-digit components); 8 CLI e2e runs on merge — 6 clean
    (rc=2), 2 crashes (rc=1); same 8 e2e inputs on baseline — all clean rc=2.
  • CI context: 1 of the expected checks (check) recorded pass for this head;
    python-matrix / native-clients results absent from the provided context (skipped as
    unavailable, not failed).
  • Full bundle: bundle/ (PLAN/REVISIONS/ENVIRONMENT/RESULTS, adv/ + repro/ scripts,
    logs/).

— pi 1.4.0 coding agent, automated production adversarial reviewer (production-adversarial-review 1.0.1); model mistral/mistral-large-4 (read from this run's pi session record). Reviewed head b5a49b497c5b against main @ 45351ff95ce7. Posted by @xhluca-agent.

@xhluca-agent xhluca-agent 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.

Verdict: HOLD — changes required

Adversarial review — PR #23 "feat(opencode): accept OpenCode 1.19+ 1.x releases" (head b5a49b4)

Base main @ 45351ff95ce79684c23377920b92026ecbe9759b; head under test
b5a49b497c5bbcca702cd86da58c54d5f239e7b8. Prospective merge built in a detached
worktree: clean fast-forward, tree e51fcbd2f0229ad08128d1945c7801ff9a13c9af == PR head
tree (nothing pushed or merged). Re-review of this head with the PR discussion
included.

Maintainer requests

  • None exist for this PR. context/discussion.json contains no maintainer reviews
    and no inline threads — only the contributor-eligibility bot comment and the earlier
    automated review. There is no maintainer preference to weigh here; the verdict rests
    on the code findings below.

Findings

F1 — Uncaught ValueError crash in the new version gate (reproducible, new vs base) — blocking

  • src/session_migrate/formats/opencode.py:31 defines _LEGACY_VERSION with unbounded
    (\d+) groups, and is_legacy() at opencode.py:65 calls int(match[1]) /
    int(match[2]) on them. A component longer than CPython's int_max_str_digits limit
    (4300) raises ValueError, which no caller of supported_version() catches.
  • Reachable from user-controlled input at src/session_migrate/conversion.py:197
    (observed --version of the resolved source CLI), :1429 / :1436
    (--target-cli-version / observed target CLI), and :1850
    (detect_opencode_version, wired from src/session_migrate/cli.py:576).
  • Minimal reproduction (bundle/repro/repro_f1_huge_digit_version.py):
    python -m session_migrate import <any-source-export.jsonl> --to opencode --target-cli-version "1.<5000 nines>.0" --dry-run
    → exit 1, raw ValueError: Exceeds the limit (4300 digits)... traceback at
    opencode.py:65. The same string printed by a fake source CLI crashes identically at
    the source gate (conversion.py:197). On base main the identical inputs exit 2
    with the clean version-mismatch error — a regression introduced by this PR.
  • Required correction: bound the digit groups in _LEGACY_VERSION (e.g.
    (\d{1,9})) or catch the conversion failure and return False; add regression
    tests with a >4300-digit component for both the source and target gates.

F2 — No CHANGELOG entry for a user-visible gate change — blocking per repo convention

  • CHANGELOG.md:7 leaves ## Unreleased empty while the PR changes observable
    behavior: 1.17.20 and 1.18.x CLIs are now rejected
    (expected 1.19+ 1.x or supported 2.0.x), and the writer metadata / CI pin move to
    1.19.4. Prior release sections record such changes.
  • Required correction: add an ## Unreleased entry describing the 1.19+ floor, the
    1.19.4 pin, and the continued unvalidated_source_version /
    unvalidated_target_version warnings for non-pinned accepted releases.

F3 — Evidence-scope note (non-blocking)

  • The CI context provided for head b5a49b4 records only the check job (pass);
    python-matrix and native-clients results were not included. The native oracle itself
    stays pinned (tests/test_additional_formats_native.py:42-43 requires the binary's
    --version stdout to equal 1.19.4 exactly), and the checked-in native corpus still
    carries 1.17.20 captures now expected to emit unvalidated_source_version
    (tests/native_corpus/route_oracle.py:497-500). Acceptance of 1.19.x releases beyond
    1.19.4 therefore rests on unit tests and gate probes in this review, not on a live
    1.19.x native run here. Prerelease suffixes are accepted
    (tests/test_opencode_v2.py asserts 1.19.4-nightly.abc123 is supported) — deliberate
    per the tests, but worth an explicit doc sentence.

What verified clean

  • Gate semantics on merge (24-case matrix + e2e with a fake CLI): 1.19.0+ accepted,
    1.18.x/1.17.20 rejected with the new message, 2.0.x still accepted via the v2 path,
    malformed/empty/None rejected, no regex blowup (2000 runs on ~600-char inputs in
    0.01 s).
  • Test-diff audit: removed assertions were pin-constant updates only; no assertion
    weakened; fixture renames changed only the version field; the route_oracle.py
    warning-contract change is additive; docs updated consistently
    (docs/format-compatibility.md:23, docs/cli-reference.md:158,
    docs/troubleshooting.md:120-123, docs/additional-target-formats.md:11,103,
    docs/architecture.md, docs/opencode-v2.md,
    docs/credential-free-native-testing.md); scripts/install-native-test-clis.sh
    pin updated to opencode-ai@1.19.4.

Evidence counts (failures/skips disclosed)

  • Merge suite: 1747 passed, 63 skipped, 0 failed; baseline: 1739 passed, 63 skipped,
    0 failed — +8 passed, 0 new skips, 0 failures. ruff check pass, ruff format --check 170 files clean, .github/tests unittest 12 OK.
  • Adversarial: 24-case unit matrix on merge — 22 clean results, 2 uncaught
    ValueError crashes
    (huge-digit components); 8 CLI e2e runs on merge — 6 clean
    rc=2, 2 crashes rc=1; the same 8 inputs on baseline all clean rc=2.
  • CI context: 1 check (check) recorded pass for this head; the python-matrix and
    native-client jobs were absent from the provided context (disclosed, not assumed).
  • Full bundle: bundle/ (PLAN/REVISIONS/ENVIRONMENT/RESULTS, adv/, repro/,
    logs/); line-level comments in inline.json.

— pi 1.4.0 coding agent, automated production adversarial reviewer (production-adversarial-review 1.0.1); model mistral/mistral-large-4 (read from this run's pi session record). Reviewed head b5a49b497c5b against main @ 45351ff95ce7. Posted by @xhluca-agent.

# Legacy 1.x releases keep the same public export, import, and session-list
# shape, so the 1.19+ family is accepted.
_LEGACY_MIN_VERSION = (19, 0)
_LEGACY_VERSION = re.compile(r"(?:opencode v)?1\.(\d+)\.(\d+)(?:[-+][0-9A-Za-z.-]+)?")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

F1: _LEGACY_VERSION uses unbounded (\d+) groups. Combined with int() in is_legacy (line 65), a version component longer than CPython's int_max_str_digits limit (4300) raises an uncaught ValueError. Reproduced via CLI: import <src> --to opencode --target-cli-version "1.<5000 nines>.0" --dry-run exits 1 with a raw ValueError traceback (main exits 2 with a clean version-mismatch error); a fake source CLI printing the same string crashes the source gate at conversion.py:197 the same way. Fix: bound the groups (e.g. (\d{1,9})) and add a regression test.

— pi 1.4.0 · mistral/mistral-large-4 · @xhluca-agent

"""Whether a 1.x release keeps the pinned legacy public bundle shape."""

match = None if value is None else _LEGACY_VERSION.fullmatch(value)
return match is not None and (int(match[1]), int(match[2])) >= _LEGACY_MIN_VERSION

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

F1 (crash site): int(match[1])/int(match[2]) on the unbounded regex groups from line 31 raises ValueError for >4300-digit components, which no caller of supported_version() catches (conversion.py:197, :1429, :1436, :1850). Verified: merge rc=1 traceback vs baseline rc=2 clean error for the identical input. Fix: wrap the conversion (return False on ValueError) or bound the digits in the regex at line 31.

— pi 1.4.0 · mistral/mistral-large-4 · @xhluca-agent

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Please support newer OpenCode 1.x versions (1.19.x)

2 participants