# Changelog

All notable changes to `pfc-mcp` are documented here.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

<!--
Convention (mirrors the yade-dev/trunk ChangeLog discipline):

- `## [Unreleased]` is curated by the maintainer from commit history at
  release time. Contributors do NOT edit this file -- a clear conventional
  commit message is enough. This keeps the changelog complete with zero
  changelog friction on any single change or PR.
- Documentation is first-class here: agents read the docs and the agentic
  install guides to understand and operate pfc-mcp, so notable documentation
  / install-flow changes are logged like behaviour changes.
- "Notable" = anything that changes how a user or agent understands or uses
  pfc-mcp: behaviour changes, added/removed tools, bug fixes, and notable
  documentation / install-flow changes. Pure internal refactors, tests, CI,
  and trivial doc fixes (typos, formatting) are omitted.
- Bridge changes that users perceive (deadlock fixes, new tool surfaces,
  protocol shifts requiring a minimum `itasca-mcp-bridge` version) are
  re-stated here even though they ship from a separate repo — readers of
  pfc-mcp's release notes shouldn't have to follow a second changelog to
  understand what changed for them. The notability test is the same: ask
  "if a user upgrades pfc-mcp without thinking about bridge versions, do
  they perceive this change?" Bridge-internal refactors and silent
  improvements stay only in `itasca-mcp-bridge`'s own changelog.
- Group entries under these categories (Keep a Changelog + Documentation):
  Added / Changed / Deprecated / Removed / Fixed / Security / Documentation.
- Referencing a commit/PR is optional; the entry must stand on its own.

At release the maintainer curates `## [Unreleased]` from `git log` since the
previous release, renames it to `## [x.y.z] - YYYY-MM-DD`, then starts a fresh
empty `## [Unreleased]`. The publish workflow extracts the section whose
header matches the tag version exactly (`## [x.y.z]`); `## [Unreleased]` is
never published until promoted, and a release fails if no matching version
section exists.

## [x.y.z] - YYYY-MM-DD

### Changed
- ...
-->

## [Unreleased]

## [0.4.3] - 2026-05-26

Contact API documentation now distinguishes mechanical and thermal
contact interfaces, eliminating fictitious `force_normal` /
`force_shear` advertisements on thermal contacts and adding version
gating for the PFC 9.0-only `VertexFacetContact`. Rblock contacts
and thermal-specific power methods join their respective interfaces
as first-class entries.

### Fixed
- `pfc_browse_python_api` / `pfc_query_python_api` no longer falsely
  advertise mechanical force methods on thermal contacts. The five
  `*ThermalContact` types (`BallBallThermalContact`,
  `BallFacetThermalContact`, `BallPebbleThermalContact`,
  `PebblePebbleThermalContact`, `PebbleFacetThermalContact`) are
  now documented through a dedicated `ThermalContact` interface that
  exposes contact state, geometry, grouping, model/property, and
  orientation methods — without `force_normal` / `force_shear`.
  Verified against the live PFC 9 bridge: 0 cross-contamination
  across all 15 contact classes. Closes #10.
- `VertexFacetContact` is now correctly tagged as PFC 9.0-only via
  per-type `availability.versions`, so 6.0 / 7.0 queries no longer
  suggest it exists on those versions.

### Added
- Mechanical `Contact` interface now expands to all four rblock
  contact types (`BallRBlockContact`, `PebbleRBlockContact`,
  `RBlockFacetContact`, `RBlockRBlockContact`) and the 9.0-only
  `VertexFacetContact`, matching the official PFC class hierarchy.
- Thermal-specific `power()` / `set_power(value)` methods are now
  documented for all five thermal contact types, sourced from the
  official PFC 9 Python HTML reference.

## [0.4.2] - 2026-05-25

Expanded Python SDK array API coverage (9 new modules) and bumped the
fastmcp dependency to the 3.x line so tests against its public API
surface work on a fresh install.

### Added
- `pfc_browse_python_api` / `pfc_query_python_api` now cover the
  vectorized array API: `ballarray`, `clumparray`, `wallarray`,
  `rblockarray`, and the four contact-pair arrays (`ballballarray`,
  `ballfacetarray`, `ballpebblearray`, `pebblepebblearray`,
  `pebblefacetarray`). Availability is encoded per function with
  `availability.versions` covering PFC 6.0 / 7.0 / 9.0.

### Changed
- Bump minimum `fastmcp` to `>=3.0.0`. Tests now use FastMCP's public
  `mcp.call_tool(...)` / `mcp.list_tools()` API.
- `pfc_browse_commands` lower-cases command input so mixed-case paths
  (e.g. `"Ball Create"`) resolve correctly, and the "command not found"
  suggestion list is now version-filtered (no more 9.0-only commands
  surfaced when querying 6.0).
- `pfc_browse_python_api` no longer echoes the full `functions` block
  inside `summary` when browsing a module, avoiding response truncation
  on large modules.

### Fixed
- Corrected `availability.versions` on the new array docs:
  `rotation` / `set_rotation` / `fill_rotation` entries were removed
  (they don't exist in any PFC version), and `euler` / `moi_prin`
  families are now correctly marked as available on PFC 6.0 as well,
  not 7.0/9.0 only. Verified against PFC 6.0 and 9.0 GUI bridges.

## [0.4.1] - 2026-05-24

Two reliability fixes for the bootstrap path users hit on day one — pip
entry-point detection, network fallback — plus a sharp agent-guidance
warning to stop `program call '*.p?dat'` from wedging the bridge, which
many existing PFC workflows would otherwise trigger.

### Changed
- Bridge startup output is quieter: callback registration, `TaskManager`
  init, and historical-task counts now only go to `bridge.log`, not the
  PFC IPython console. The status banner shows URL + log path and stops.
  Ships when pfc-mcp's pin moves past `itasca-mcp-bridge` `bc68380`.
- `addon.py` drops the interactive upgrade prompt. PFC's `pythonfile`
  host has no usable stdin, so the `input()` prompt was effectively
  dead — it either hit the EOFError fallback or silently returned an
  empty string and skipped the upgrade. A top-of-file
  `AUTO_UPGRADE = True` constant replaces it: the bootstrap now installs
  or upgrades to the latest `itasca-mcp-bridge` on every start with zero
  interaction. Flip to `False` to pin the locally installed version.

### Fixed
- `addon.py` no longer hard-codes pip's entry point to `pip.main`. That
  symbol exists in pip <= 9 (what PFC 6.0 ships) and was later restored
  as an internal shim, but it was absent in pip 10.0 through ~19.x -- a
  PFC interpreter carrying a pip from that range crashed with
  `AttributeError` instead of installing the bridge. The bootstrap now
  probes `pip._internal.cli.main`, `pip._internal`, and `pip.main` in
  turn, so it works regardless of which pip the embedded Python carries.
- A failed bridge install now points at pip's real error output and
  offers a manual `pip install` fallback, instead of surfacing only a
  bare `exit code 2` that hid the underlying cause.

### Documentation
- `pfc_execute_code` and `pfc_execute_task` docstrings now warn agents
  off `itasca.command("program call '<file>.p?dat'")`. PFC's
  command-script interpreter blocks the entire bridge for the script's
  duration — even bridge-internal calls like `pfc_list_tasks` time out,
  and recovery requires the user to manually stop PFC. If a user asks
  to run a `.dat` / `.p3dat` / `.p2dat` file, agents are instructed to
  read the file and translate each command into a `itasca.command(...)`
  call in Python instead, which preserves cycle-gap interleaving and
  keeps every command inspectable. Lots of existing PFC workflows are
  `.p?dat` scripts, so pointing an agent at one was previously a usage
  trap that made the toolchain look broken.
- The agentic bootstrap guide's Step 3 (agent-driven bridge install)
  now documents the PyPI -> Tsinghua mirror fallback, matching the
  fallback `addon.py` already performs. An agent installing the bridge
  for a user on a network that blocks PyPI previously had no documented
  retry path and would stall there.

## [0.4.0] - 2026-05-22

Bridge installation path migrates from `pfc-mcp-bridge` to `itasca-mcp-bridge`.
Tool surface (MCP tools and their wire contracts) is unchanged; the minor bump
reflects the user-visible install/dependency move, not a tool API change.

### Changed
- `addon.py` now installs `itasca-mcp-bridge` (PyPI) instead of
  `pfc-mcp-bridge`. End users get the multi-line `it.command()` deadlock fix
  (recognized aliases like `import itasca as it`) automatically — the
  splitter in `pfc-mcp-bridge` only matched the literal `itasca.command(...)`
  form and silently skipped any aliased call, letting one C batch hold the
  GIL while IPython's ZMQIOStream backed up. Re-download `addon.py` from the
  pfc-mcp repo to pick up the new install path.
- Agentic install now defaults the MCP client config to user/global scope
  instead of workspace, with workspace as an explicit last-resort fallback.
  Per-client profiles are CLI-first where a verified user-scope command
  exists (Claude/Codex/Gemini/Copilot) and file-edit for OpenCode. Affects
  nearly all users of the agentic install flow.

### Deprecated
- `pfc-mcp-bridge` PyPI package is frozen at `0.3.3` (final release). It
  emits a `DeprecationWarning` at import and prints a migration banner from
  `start()`. Users still on the old `addon.py` that upgrade will see the
  signal; those who decline the upgrade prompt stay on the affected version
  until they upgrade once. No further fixes ship to this package.

### Removed
- Embedded `pfc-mcp-bridge/` directory. Bridge sources now live in the
  separate [`itasca-mcp-bridge`](https://github.com/yusong652/itasca-mcp-bridge)
  repo and are referenced from this repo as a git submodule
  (`itasca-mcp-bridge/`). Fresh clones intended for bridge development need
  `git clone --recurse-submodules` (or `git submodule update --init
  --recursive`); end users running `addon.py` do not need the submodule.
- `.github/workflows/publish-bridge.yml` — bridge releases now happen from
  the bridge's own repo.
- `tests/test_command_splitter.py` and `tests/test_command_log.py` — they
  reached into the embedded bridge source via `sys.path` and now live with
  the bridge in its repo.

### Documentation
- `docs/development/source-install.{md,zh-CN.md}` document the submodule
  workflow: clone with `--recurse-submodules`, re-sync after pull, pin
  bump recipe, push order (bridge first), and how to read submodule
  status noise.
- Per-AI bootstrap files (CLAUDE/AGENTS/GEMINI/WARP.md) and the agentic
  install guide describe the new layout: `src/pfc_mcp/` for the MCP server,
  `itasca-mcp-bridge/` (submodule) for the bridge runtime.

## [0.3.15] - 2026-05-16

Version-aware reference documentation. The `contact-models` reference grows
from 5 to 22 models (all PFC mechanical + thermal contact models) and
`pfc_browse_reference` becomes version-aware for PFC 6.0/7.0/9.0.

- `pfc_browse_reference` gains a `version` parameter (6.0/7.0/9.0, default
  7.0) mirroring `pfc_browse_commands`: the model list is filtered by
  version availability, requesting a model absent in the target version
  returns a friendly `item_unavailable_for_version` error (with
  `available_versions`), and not-found suggestions are version-filtered.
- New models: arrlinear, bilinear, burger, eepa, fish, flatjoint,
  hysteretic, jkr, lineardipole, null, smoothjoint, softbond,
  springnetwork, thermalnull, thermalpipe, plus 9.0-only `mohr` and
  `subspringnetwork`. The 5 legacy curated models keep their content.
- Content is shared across versions (measured: 7.0 == 9.0, and 6.0 == 7.0
  for shared properties), so no per-version data duplication. Differences
  are encoded as a per-model `availability` map and per-property `since`
  markers (flatjoint `fj_cohres`/`fj_resmode` since 7.0; softbond
  `rgap`/`sb_coh`/`sb_fa`/`sb_mcf` since 9.0). `range-elements` and
  `plot-items` have no `availability` and remain version-agnostic
  (backward compatible).
- Property extraction reworked to split on `<dt id="kwd:...">` markers
  instead of pairing `<dt>/<dd>`, so pages that nest base properties in a
  child `<dl class="keyword">` (e.g. eepa) no longer bleed text or drop
  the nested keywords; model-specific properties collapse into a single
  clean group.

## [0.3.14] - 2026-05-15

Second wave of command documentation expansion: 123 new commands across
8 new scopes, bringing totals to 21 categories / 362 commands (was 13 / 239).

- `geometry` (34): nodes, edges, polygons, import/export, refinement, tessellation
- `fracture` (32): DFN — create, generate, intersections, templates, joint-set
- `table`    (11): numerical x/y tables for boundary conditions and history data
- `group`     (4): named-group lifecycle (create / list / rename / slot)
- `trace`     (7): per-object trace recording (lifecycle / export / interval)
- `project`   (6): GUI project containers (new / save / restore / execute)
- `data`     (26): user data containers — label/scalar/vector/tensor sub-namespaces
- `domain`    (3): domain extent, boundary conditions, periodic-cell strain-rate

All entries populated for PFC 6.0/7.0/9.0 (7 commands marked unavailable in
6.0 as new in later versions; e.g. `fracture intersections` machinery).

Note: the "dfn" doc module is exposed under the scope name `fracture` to
match the actual command verb users type. Dotted HTML stems (e.g.
`cmd_geometry.edge.create.html`, `cmd_data.scalar.create.html`) are mapped
to dash-separated JSON keys (`edge-create`, `scalar-create`) following the
rblock convention. `parse_pfc{600,700,900}.py` `build_html_map` learned the
same dot→dash rule.

`range` was considered but not added — the HTML files under
`range_manual/range_commands/` are syntax reference (rangelogical,
rangenaming, rangephrase), not commands.

Fixes `pfc_browse_commands` to accept the natural space-separated form
of compound sub-commands (e.g. `geometry edge create`, `data scalar
create`, `fracture intersections compute`, `contact cmat add`).
Previously the lookup only matched the stored dash form (`edge-create`,
`cmat-add`), forcing users to type a non-PFC-syntax variant.

## [0.3.13] - 2026-05-15

Expands PFC command documentation coverage from 177 to 239 entries
across 13 categories (was 10). Three new scopes — `program` (28
commands: license, threads, log, system, lifecycle), `history` (8:
interval, export, label, results), and `fish` (11: callback, define,
list, debug, trace) — join filled gaps in `model` (+9: creep,
dynamic, energy, factor-of-safety, fluid, list, precision, step,
title), `contact` (+3: extra, history, list), and `fragment` (+3:
groupisolated, groupslot, map). All entries are populated for PFC
6.0/7.0/9.0. These commands were previously skipped because their
Python SDK equivalents return nothing; now that `itasca.command()`
stdout is captured via the bridge log, exposing them lets agents
read program/model/history/contact list output through normal
documentation lookup. A new `bootstrap_missing.py` script under
`command_docs/` walks the installed PFC HTML manuals and writes
complete versioned JSON.

Trims trailing float-precision noise from BM25 result scores in
`pfc_query_command` and `pfc_query_python_api` (e.g.
`2.4743000000000004` → `2.47`).

## [0.3.12] - 2026-05-14

Surfaces the structured cancellation outcomes from pfc-mcp-bridge >=
0.3.2 through `pfc_execute_code`: `terminated` (bridge aborted at the
deadline, partial output returned, PFC state may be partially
modified), `timeout` with `details.method="stuck_in_c"` (snippet stuck
in a C extension; bridge may recover when the C call returns),
`interrupted`, and the default `timeout` path. Local wait widens to
`timeout_ms / 1000 + 5.0s` so the bridge has room to deliver the
`terminated` response after its own grace window.

Drops `pfc_mcp.formatting.normalize_status()` (and its re-export from
`pfc_mcp.tools.task_formatting`); the shim only mapped legacy
`success`/`error` status from pre-0.3.0 bridges. Requires
pfc-mcp-bridge >= 0.3.0; pin `pfc-mcp<0.3.12` if you can't upgrade the
bridge.
