Commit Graph

997 Commits

Author SHA1 Message Date
Matthew Stanley a75fcbef87 docs(readme): frame features as framework capabilities, not a fixed roster
Three corrections to framing that made the project read smaller than it is.

- Drop the 'eight titles brought up so far' count from the hero. A number reads
  as a ceiling; the point is that bringing up a new game is configuration and
  reverse engineering, not new engine work.
- Widescreen: 'six of the seven titles support it' described this ecosystem's
  games rather than the capability. The cull-widening, FOV and aspect machinery
  is framework tooling any project can enable from a [widescreen] block,
  third-party forks included.
- Localization: retitle 'Play a Japanese-only game in English' to 'Swap
  language on the fly'. The mechanism has no built-in notion of a default
  language -- it is any source to any target, switched live. Describe what is
  actually hard about it: substituted text is drawn through the game's own
  glyph routine with per-character advances calibrated from real ink width in
  VRAM, plus auto-fit condensing, so no engine change or regeneration is needed
  to add a language.

Tables are TOML (translations/*.toml, one column per language, hot-reloaded),
verified against text_xlate.cpp and Tsumu's shipped table -- docs/
STRING_TRANSLATION.md still calls them translations.json, which is stale.

Also removes the aging Tomba demo-video embed; the screenshot grid carries the
visual pitch now. Anchors, images and file links all re-verified.
2026-07-27 15:02:34 -07:00
Matthew Stanley c8da81e7f6 docs(readme): drop the aging demo-video thumbnail
The Tomba demo embed predates most of the ecosystem and sat directly above the
new screenshot grid, which now carries the visual pitch. The write-up link
above it stays as project history.
2026-07-27 14:58:50 -07:00
Matthew Stanley c9ead22df8 docs(readme): constrain screenshot widths to their cells
The grid images are 800px and the widescreen shots 1280px with no width
attribute, so a three-wide table would try to lay out ~2400px and GitHub would
scale the cells unevenly. Pin every screenshot to 100% of its container so the
grid stays square and the hero shots span the content width.
2026-07-27 14:01:36 -07:00
Matthew Stanley d16beb16b2 docs(readme): lead with maturity, games, and shipped features
The README opened by explaining what a static recompiler is and how to run the
CLI, and buried the fact that eight titles actually run on it. Restructure so
the catalogue and the shipped features come first, and the build instructions
follow the pitch rather than preceding it.

- Hero: reposition as a general-purpose PS1 static recompiler, plus a 3x2
  screenshot grid of the six playable titles.
- Games: add Xenogears (github.com/OpokXeno/xenogears-recomp), marked
  community, per issue #83; note the author also contributed widescreen
  cull-site work upstream. Per-title notes now name widescreen support.
- New sections: Widescreen (16:9 / 21:9 / adaptive, showing both a 2D sprite
  engine and 3D), Mods (versioned .psxmod packages over a verified stock image,
  with the launcher panel), on-the-fly localization (the Tsumu translation
  layer), and Renderers (OpenGL default, Vulkan experimental, software
  reference).
- Promote 'Do I need a PlayStation BIOS?' near the top: bundled MIT OpenBIOS
  means most players need no dump, which is the first question a newcomer has.
- Reorder: Games / What It Is / BIOS / features / how-to-build / the rest.
  No section was dropped; all 18 are still present.

Screenshots are sized to how they render (800px grid cells, 1280px hero shots)
rather than colour-quantised, so there is no visible loss: 11.6MB of captures
land as 4.6MB of assets. Netplay is deliberately not mentioned.

All image paths, internal anchors and relative file links verified to resolve.
2026-07-27 13:58:13 -07:00
Matthew Stanley da093d4374 fix(build): link a BIOS backend only when its generated C provides one
PSXRECOMP_BIOS_STEMS defaults to "OpenBIOS;SCPH1001", so every game link
required a SCPH1001 backend. That backend is compiled from generated C which is
a derivative of a copyrighted Sony BIOS, so it is gitignored and only exists
after a developer regenerates it from their own dump. A checkout that has only
the bundled MIT OpenBIOS -- the documented default, and what a fresh clone gets
-- configured fine and then failed at link with:

    undefined reference to `SCPH1001_psx_bios_backend'

Hit while building Tomba 1, Tomba 2 and MMX6 to validate an unrelated branch;
all three needed -DPSXRECOMP_BIOS_STEMS=OpenBIOS as a workaround.

Filter the stem list to those that actually supply a backend, rather than
flipping the default. Flipping it would silently drop SCPH1001 for developers
who HAVE regenerated it; filtering keeps it fully supported when present and
simply absent when not.

Presence of the files is not a sufficient test. A generated/ tree produced
before the backend-descriptor mechanism existed has both <stem>_full.c and
<stem>_dispatch.c yet defines no descriptor -- exactly the local state that
produced the error above. The check therefore probes for the descriptor symbol
itself, in <stem>_dispatch.c (~1MB, where the emitter puts it) rather than the
multi-megabyte <stem>_full.c, so configure stays fast.

Also report what happened instead of leaving it implicit:
  - STATUS line naming the linked backends
  - STATUS line naming skipped stems with the regen command to fix them
  - FATAL_ERROR, with the OpenBIOS regen command, if no backend resolves at all
    (previously an empty registry would have surfaced as a confusing link error)

Verified: with default flags and a stale local SCPH1001 tree, Tomba 2 now
reports "BIOS backends skipped ... SCPH1001" / "BIOS backends linked: OpenBIOS",
links, and boots.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 12:32:30 -07:00
Matthew Stanley 4c6aee392b test: wire master's new fps_telemetry_opt_in guard
Master added runtime/tests/test_fps_telemetry_opt_in.py in bb5fcce without an
add_test() entry. It is a source-only guard over runtime/src/main.cpp and
passes, so it joins the same list as the other 15. 30 tests, still under four
seconds.

This is the pattern the earlier commit was meant to prevent recurring -- worth
noting it recurred within a day, which is the argument for a CI check rather
than a convention.
2026-07-27 11:50:05 -07:00
Matthew Stanley 49b57f4178 docs(gpu): correct the MMX6 widescreen comments; record what is safe to delete
Comment-only. No compiled code changes -- the sole non-comment diff line moves
a trailing comment off the g_mmx6_hostside declaration into a block above it;
the declaration itself is byte-identical.

A survey of the mmx6-named widescreen symbols found the existing comments
misleading in a way that invites a dangerous cleanup, so the findings are
recorded in the source next to the code they describe.

1. "Compatibility entry points for already-generated MMX6 sources" (gpu.h) was
   wrong. These are not MMX6 compat shims and are not safe to delete. They are
   the widescreen-2D entry points the recompiler emits for ANY title with a
   [widescreen.bg2d] block, and live generated code in several non-MMX6 repos
   links them today:
     cols/startcol/startx/stream_left/stream_right
         -> CrashBash, MegaManX5, Tsumu (+ -release)
     undercap
         -> Ape, CrashBash, MMX4, MMX5, Tsumu, Vigilante8, THPS2
   Each forwards to its psx_ws_bg2d_* twin, which is the real implementation.
   Only the name is historical. Renaming is not runtime-only: the names come
   from code_generator.cpp, which is in codegen_hash_sources.cmake, so it rolls
   the overlay cache tag and forces regen + reshard of every title.

2. Host-side reveal (g_mmx6_hostside) is dead in every shipping build, which
   the previous "kept inert pending removal" note understated. Nothing sets it
   but the mmx6_reveal debug command, PSX_DEBUG_TOOLS is OFF for Release, and
   no generated source anywhere references a host-side symbol. Noted that
   removing it is the last step decoupling the generic psx_ws_bg2d_* API from
   MMX6 -- mmx6_hostside_active() is currently embedded in the ternaries of
   psx_ws_bg2d_cols/startcol/startx -- and listed the symbols that go with it.

3. BG buffer relocation is a parked FEATURE behind a load-bearing SYMBOL, a
   distinction the old comment did not draw. With g_mmx6_reloc == 0 both hooks
   return their argument unchanged, but MegaManX6Recomp/game.toml names the
   bufbase/cap instruction sites, so the emitter writes calls into generated
   code and psx_ws_mmx6_bg_bufbase is genuinely called by MMX6's generated C.
   The supportable cleanup is to gut the bodies to identity and keep the
   symbols, not to delete the functions.

Removal of (2) and (3) is deliberately deferred: it touches the renderer and
wants its own session with MMX6 revalidation.

Verified: gcc -fsyntax-only on gpu.c exits clean (an earlier draft embedded
"*/" inside a block comment and broke the parse -- caught before commit);
ctest 29/29 green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 11:49:31 -07:00
Matthew Stanley 73785490e7 docs(tcp): refresh generated index after master added a command
Master's debug_server.c changes brought the registered command count to 293.
gen_tcp_commands.py --check caught the drift on the first run after rebase,
which is the behaviour it exists for.
2026-07-27 11:49:31 -07:00
Matthew Stanley 5aced6c306 test: register 21 written-but-unwired tests; document how to run them
Correction to an earlier claim of mine: ctest was NOT absent. recompiler/ and
runtime/ already had 21 add_test() calls between them and `ctest` ran 8 tests
green in the recompiler tree. My original survey used a quoted git-grep
pathspec that the shell passed literally, matched nothing, and I read the empty
result as "no test wiring anywhere". The real problem was narrower: partial
registration and zero documentation.

Of 61 first-party test sources (excluding 3 fixtures/harnesses), 37 were
referenced by no CMakeLists at all. Each was run by hand; 21 pass and are now
registered, taking the recompiler tree from 8 to 29 tests -- still under five
seconds, and still requiring no BIOS dump, no disc image, and no generated
code.

The 15 runtime source-invariant guards are registered from
recompiler/CMakeLists.txt rather than runtime/CMakeLists.txt, following the
existing dirty_text_continuation_guards precedent. They only read runtime
source, and the runtime tree cannot configure until a BIOS has been generated,
so registering them there would have made them unreachable from the only tree a
newcomer can build.

Three tests are deliberately NOT registered because they fail today. Wiring a
known-red test would recreate exactly what took CI off pull requests. Detail is
in docs/TESTING.md; briefly:

  test_interpreter_perf_guards.py    asserts psx_devices_mmio_sync invalidates
      the inline cycle limit. It does not. The function delegates either to
      psx_devices_service_to_now() (which clears g_psx_cycle_fast_limit,
      psx_cycles.c:161) or to psx_devices_recompute_deadline() (:153-157),
      and that second branch never clears it. Needs a timing owner: the guard
      may have found a real hole, or the invariant may have moved. The guard is
      itself partly stale -- it still names s_next_service_cycle, since renamed
      to psx_next_service_cycle.
  test_runtime_perf_diag_guards.py   asserts a substring no longer present in
      the runtime source.
  test_overlay_pair_dedup_runtime.py needs its companion harness built; not a
      source-only guard.

New docs/TESTING.md covers running the suite, running one test, what each group
needs, the known-failing three, and why fixtures must not be registered. It
also records two traps that cost time here: parallel builds can crash cc1plus
on config_loader.cpp under memory pressure (retry with -j 1, it is not a code
error), and the codegen tests need an ABSOLUTE recompiler path -- a relative one
passes their os.path.isfile check and then fails inside subprocess.run on
Windows with WinError 2, which reads as a broken test.

Linked from CONTRIBUTING.md (above the existing manual regression checklist,
noting the suite does not tell you whether a game still runs) and from
docs/BUILDING.md.

Verified: clean configure + build, ctest 29/29 pass in 4.08s.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 11:49:31 -07:00
Matthew Stanley 0c4dd2db85 docs(tcp): generate the command index; fix six wrong entries
TCP_COMMANDS.md instructed contributors to "update this file" on every new
handler. It described 47 commands while the two debug servers registered 292
between them (279 native + 61 Beetle, overlapping) -- 15% coverage. A
hand-maintained inventory that size does not stay correct, and a stale
reference is worse than none because people trust it.

New tools/gen_tcp_commands.py parses the s_commands[] (native) and CMDS[]
(Beetle) tables and regenerates a complete index between AUTOGEN markers,
marking which commands the curated prose already covers. The hand-written
"Command inventory" table is untouched -- it carries parameters and context
worth keeping. `--check` exits non-zero when the block drifts, so this can gate
CI; verified it fails on a simulated added handler and passes once regenerated.

Corrections to the curated table, each verified against the code first:

  write_ram       params are `addr`,`val` -- not `addr`,`hex` -- and it writes
                  exactly one byte per call (handle_write_ram)
  screenshot      writes a PNG (psx_screenshot.png), not a 24-bit BMP
                  (debug_server.c:7666,7856)
  wtrace_range    ring holds 262144 entries (WRITE_TRACE_CAP, 1<<18), not 1024
                  -- the documented figure was 256x too small
  sljit_async     row removed; the command is not registered anywhere (the
                  sljit tier was removed 2026-07-15)
  pause/continue/step/run_to_frame
                  documented as working. All four are registered but
                  unconditionally return an error (debug_server.c:7324-7342);
                  they were deliberately removed in favour of ring-buffer
                  queries. Marked REMOVED with the replacement each error
                  message names.

Also corrected the header: it presented DuckStation (port 4371) as one of the
two live servers, though DuckStation was retired as the oracle on 2026-05-05
and has no .gitmodules entry, while the page's own CLI examples already used
Beetle on 4380. The live pair is now Native 4370 / Beetle 4380, with a note
that the inventory's "D" column is historical, and the "add a handler" checklist
now points at beetle_debug_server.c and the generator instead of DuckStation.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 11:49:31 -07:00
Matthew Stanley 9ad18ded0d docs: correct six statements that contradict the code
These are not stale-by-omission; each asserts something the code does not do,
and each was verified against the implementation before editing.

docs/BUILDING.md
  - PSX_ENABLE_VULKAN was documented "off by default" in two places. It
    defaults ON (runtime/runtime.cmake:778). Corrected both, and noted that
    the build option being ON does not make Vulkan the runtime renderer.
  - Removed the PSX_BUILD_COSIM row: no option() for it exists anywhere in
    the tree. Replaced with PSX_NETPLAY, which is real (runtime.cmake:194).
  - Step 2 (regenerate BIOS) was labelled "(Optional)". It is required before
    the first runtime build: step 3 configures psx-runtime from
    generated/OpenBIOS_full.c and fails at configure time without it
    ("Cannot find source file" / "No SOURCES given to target"). Regenerating
    the *retail* backend remains genuinely optional; that distinction is now
    explicit.
  - "This repository has no required git submodules" contradicted .gitmodules
    (recomp-ui, lib/recomp-net) and README's --recurse-submodules clone.
    Replaced with a table of which submodule each build option needs.

docs/ARCHITECTURE.md
  - Same Vulkan default correction.
  - Removed the claim that BIOS and game configs merge with "game wins on
    scalar keys". No such merge exists: load_bios_config is called only from
    the recompiler front-ends (main_bios.cpp, and main_psx.cpp solely to build
    the BiosAddressModel) and never from runtime/src/main.cpp.

docs/config_schema.md
  - Same merge claim, stated in more detail. Replaced with the actual runtime
    precedence: env > CLI > settings.toml > game.toml > compiled default.

docs/STRING_TRANSLATION.md
  - Header said "SPEC (design only - no implementation yet)" above a shipped
    feature: runtime/src/text_xlate.cpp (~970 lines), initialised from
    main.cpp, configured via [localization]. Marked IMPLEMENTED and added the
    public entry points.

docs/FEATURES.md
  - Layer A (dynamic DLL overlay loading) was headed "SHELVED". It is the live
    production dispatch path: overlay_loader.c is ~4,400 lines with 15
    LoadLibrary sites, last modified 2026-07-26, and COMPILING_OVERLAYS.md
    documents shipping the resulting .dll shards in releases. Added a status
    banner and kept the original account as history, since the CRC-keying
    dead ends it records are still worth reading.

docs/BIOS_SELECTION.md
  - Claimed the identity gate checks "size, CRC32, SHA-256". It checks size
    and CRC32 only (bios_backend_for_file, main.cpp:1088-1089); image_sha256
    is recorded metadata and never enters the accept/reject decision.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 11:49:31 -07:00
Matthew Stanley 45408d60f1 fix(build): stop shipping the wrong binary as psxrecomp.exe
recompiler/CMakeLists.txt defined both `psxrecomp` (the user-facing CLI,
src/main_cli.cpp) and `PSXRecomp` (a legacy duplicate of psxrecomp-game, built
from the identical PSXRECOMP_GAME_SOURCES list). CMake treats those as distinct
targets, but Windows and macOS filesystems are case-insensitive, so both wrote
the same file and the last one linked won.

The CLI lost. After a clean build, psxrecomp.exe was the game recompiler,
answering `--config <game.toml>` instead of the documented
`build --disc ... --bios ... --output ...`. tools/build_cli.py packages that
binary into psxrecomp-cli-windows-x86_64.zip, and README's first instruction to
a new user is to download and run it, so the primary onboarding path handed
people a program matching no documentation. Invisible on Linux, which is why it
survived.

Remove the legacy target (psxrecomp-game already covers those sources) and add a
configure-time guard so it cannot recur: the guard compares OUTPUT_NAME
case-insensitively across a directory's targets and fails with an actionable
message. A rename alone would not have prevented recurrence.

The PSXRecomp.exe named in README's Release Package section is a different
artifact, the BIOS-only runtime named via OUTPUT_NAME in runtime/runtime.cmake,
and is unaffected.

Verified: clean build now yields psxrecomp.exe (791KB) printing the documented
`build --disc` interface; no case-colliding pair remains; guard confirmed to
fire by temporarily reintroducing a collision.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 11:49:31 -07:00
Matthew Stanley bb5fccea51 fix: make FPS telemetry opt-in 2026-07-27 11:44:31 -07:00
Matthew Stanley 79baa76d0a fix: stop overlay autocompile during shutdown 2026-07-27 11:01:50 -07:00
Matthew Stanley a6bd9455ca fix: exclude fail-closed stubs from game dispatch 2026-07-27 10:48:41 -07:00
Matthew Stanley 2696c391b8 feat: add aspect-aware world participation 2026-07-27 10:48:41 -07:00
Matthew Stanley d116e0da3b feat(mods): add presentation interpolation rates 2026-07-27 10:40:49 -07:00
Matthew Stanley d34af54141 feat(mods): support native VBlank rate selection 2026-07-27 10:32:33 -07:00
Matthew Stanley 964cdb3870 feat(mods): expose adaptive display activation 2026-07-27 10:19:07 -07:00
Matthew Stanley fa408aae50 Merge trusted mod plugin support 2026-07-26 21:05:03 -07:00
Matthew Stanley 45dd4210cf Remove Tomba legacy hybrid controller path 2026-07-26 20:52:12 -07:00
Matthew Stanley 9683a1cf8c feat(mods): support mod-owned FMV skipping 2026-07-26 20:34:20 -07:00
Matthew Stanley 27a05dc09e fix(launcher): hide native-only aspect control 2026-07-26 20:26:28 -07:00
Matthew Stanley 99abda239f feat(mods): add pre-runtime activation plugins 2026-07-26 20:18:00 -07:00
Matthew Stanley a4afb828ac feat(mods): add trusted static vblank plugins 2026-07-26 18:59:10 -07:00
Matthew Stanley 8b77d8a730 fix: make overlay autocompile cache-safe 2026-07-26 09:39:24 -07:00
Matthew Stanley 6b437e0657 build: link cue parser into psxrecomp CLI 2026-07-25 23:29:19 -07:00
Matthew Stanley 436fe14101 widescreen: support guarded object participation overrides 2026-07-25 23:24:41 -07:00
Matthew Stanley 05e569c121 runtime: add sparse guarded mod fields 2026-07-25 21:42:35 -07:00
Matthew Stanley 62a80eab30 fix(mods): fingerprint CUE data track only 2026-07-25 21:14:28 -07:00
Matthew Stanley 35c1859cf4 Handle feature prerequisites in mod packages 2026-07-25 21:14:28 -07:00
Matthew Stanley 66f2c7be9e runtime: pass active mod context to resolvers 2026-07-25 21:14:28 -07:00
Matthew Stanley ddf3ca8962 feat: add ordered mod values and MIPS pair patches 2026-07-25 21:14:28 -07:00
Matthew Stanley bd460f3ce1 fix: guard complete dynamic patch records 2026-07-25 21:14:28 -07:00
Matthew Stanley eb3b4db599 feat: add bounded integer mod patches 2026-07-25 21:14:28 -07:00
Matthew Stanley 82bb5a21af fix: compose compatible partial mod overlaps 2026-07-25 21:14:28 -07:00
Matthew Stanley 968c9c23ce feat: add feature-oriented mod resolution 2026-07-25 21:14:28 -07:00
Matthew Stanley 50d4f456bf fix: preserve independent mod toggles 2026-07-25 21:14:28 -07:00
Matthew Stanley 9ff287c1fa feat: support multi-option mod conditions 2026-07-25 21:14:28 -07:00
Matthew Stanley 162194dc9b fix: auto-disable conflicting mod packages 2026-07-25 21:14:27 -07:00
Matthew Stanley 6c1bd61a1f feat: derive modded discs from verified stock images 2026-07-25 21:14:27 -07:00
Matthew Stanley 316eaa44a7 feat: add versioned runtime mod packages 2026-07-25 21:14:27 -07:00
Matthew Stanley ef38122068 fix(launcher): honor CLI disc before verification 2026-07-25 21:14:09 -07:00
Rick G 2715b7483a gpu: union native-wide host reject with guest draw area (#79)
Preserve wider guest staging and composition regions while extending framebuffer draw bounds for native-wide margins. Regression-tested in 16:9 on Tomba and Mega Man X6 with bundled OpenBIOS.
2026-07-25 19:42:30 -07:00
OpokXeno 0a8f8ac98c widescreen: stretch backdrop X-stores about screen centre in native-wide mode (#78)
Stretch explicitly configured backdrop X-store sites across the native-wide surface while leaving 4:3, FMV, menu, and unconfigured games unchanged.\n\nValidated by the contributor on Xenogears Battle Mountain and regression-tested on Tomba's Watch Tower flower backdrop with bundled OpenBIOS.
2026-07-25 19:15:30 -07:00
Matthew Stanley 929b2d17f0 build: stage only redistributable BIOS files 2026-07-25 18:19:19 -07:00
Matthew Stanley 4bf868da4f feat(bios): ship OpenBIOS as a runtime dependency 2026-07-25 18:11:57 -07:00
Matthew Stanley a162aa7fb1 fix(interp): scope load-delay values to BIOS-owned code 2026-07-25 17:02:33 -07:00
Matthew Stanley 986d8be94c Merge remote-tracking branch 'origin/master' into integrate/openbios-bundle
# Conflicts:
#	recompiler/src/full_function_emitter.cpp
#	recompiler/src/full_function_emitter.h
#	recompiler/src/main_bios.cpp
#	runtime/runtime.cmake
#	runtime/src/main.cpp
2026-07-25 12:31:05 -07:00
Matthew Stanley 33589d9c43 feat(bios): show the BIOS row on bundled builds and honour a chosen image
Two runtime-side changes the launcher's SYSTEM row needs.

The row was hidden whenever the linked BIOS was bundled, on the reasoning
that there was nothing to select. With OpenBIOS AND a retail image linked
that strands the player: choosing retail — and clearing back — is exactly
what the row is for. Visibility now asks the registry (is any linked
backend NOT the bundled one?) via psx_bios_has_selectable(), which is
also correct before selection, unlike psx_bios_image.

A BIOS named in settings.toml is likewise now honoured on every build.
It was previously ignored on bundled builds to stop a stale path pointing
at a foreign image, but that guard would now silently discard the only
in-app way to opt into retail. The identity gate is the real protection,
and it explains a mismatch rather than ignoring it. An EMPTY value stays
meaningful in the other direction: that is the launcher's Clear, "go back
to the bundled BIOS", so it is not adopted as a path.

Pairs with recomp-ui feat/bios-row-clear.

Not yet compile-verified against the launcher: building with
PSX_RECOMP_UI=ON on this branch fails in the recomp-ui netplay callback
ABI (main.cpp:4495), which traces to 87491cb and is inherited from this
branch's base predating master's matching integration — the same skew
that stops MMX4/MMX5 building on current master with their pinned
recomp-ui. Needs master merged in, which this branch requires before
merge regardless.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 12:23:48 -07:00