# Physics Interpreter

> Translate paper-parser's extracted equations, claims, and surrounding text into a typed physics sub-tree of the SimSpec — governing equations, boundary conditions, initial conditions, declared constants, regime classification, and named approximations. Run after paper-parser, before simspec-author.

- **Type:** Skill
- **Install:** `agentstack add skill-leventilo-mobius-physics-interpreter`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [leventilo](https://agentstack.voostack.com/s/leventilo)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [leventilo](https://github.com/leventilo)
- **Source:** https://github.com/leventilo/mobius/tree/main/skills/physics-interpreter

## Install

```sh
agentstack add skill-leventilo-mobius-physics-interpreter
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# physics-interpreter

## Purpose and scope

This skill produces exactly one artifact: the `physics` sub-tree of the SimSpec, defined at `$defs.physics` of `simspec.schema.json`. That sub-tree fixes what every downstream skill is allowed to assume about the paper: which equations govern the system, on which domain they live, with which boundary and initial conditions, with which constants, in which regime, and under which named approximations. Mobius's "same paper, same simulator, ten times in a row" property begins here. If the regime is wrong, every solver heuristic in `simspec-author` Rule 3-6 fires the wrong way and the rest of the pipeline silently builds the wrong simulator.

What this skill does NOT do. It does not extract equations from the PDF — that is `paper-parser`'s job, and its `equations[]` array is an input here, not an output. It does not extract numerical claims from figures or text — that is `claim-extractor`. It does not compose the full SimSpec — that is `simspec-author`. It does not generate Python solver code — that is `primitive-generator`. It does not write Three.js or Canvas templates — that is `viz-mapper`. It does not run units, CFL, or conservation checks — those are the five bundled scripts of `science-integrity`. The physics-interpreter is a typed-translate skill: paper-parser artifacts in, validated `physics` sub-tree out, every interpretive choice logged.

## Inputs

The skill expects three artifacts on disk in the current working directory.

`paper.json` from `paper-parser` carries the full paper provenance and a list `equations[]`. Each equation has at minimum a `latex` field (LaTeX source as it appears in the PDF), a tentative `sympy` field (parser's best-effort sympy translation, may be empty), a free-text `surrounding_context` excerpt (~200 chars before and after), and an optional `variables` dict that paper-parser populated from the equation's typesetting context (e.g. greek-symbol heuristics, definition-by-text "where E is the electric field"). The `figures[]` and `numerical_claims[]` arrays are read for cross-references.

`text_body.json` from `paper-parser` carries the segmented paper body keyed by section anchor (`abstract`, `I`, `II.A`, etc.). The interpreter scans this body for boundary-condition phrases, initial-condition declarations, named approximations, and regime hints. The body is read-only.

`hints.json` from the orchestrator (optional) carries any pre-classification cues: `domain_hint` ("optics"|"mechanics"|...), `paper_class_guess`, and references to known canonical models the orchestrator already matched by title regex. When absent, the interpreter starts from zero priors.

## Outputs

The skill writes `physics.json` to the working directory. Its top-level keys mirror `$defs.physics` exactly:

```json
{
  "governing_equations": [...],
  "boundary_conditions":  [...],
  "initial_conditions":   [...],
  "constants":            [...],
  "regime":               "linear" | "weakly-nonlinear" | "strongly-nonlinear" | "stochastic" | "quantum-coherent",
  "approximations":       ["thin-lens", ...]
}
```

In addition the skill emits a sibling `interpretation_log.json` (NOT part of the schema) with the shape:

```json
{
  "entries": [
    {
      "phase": "1-symbol-resolution" | "2-equation-classification" | "3-bc-extraction" | "4-constants" | "5-regime",
      "choice": "string short label of what was decided",
      "alternatives_considered": ["...", "..."],
      "reason": "why this branch was chosen",
      "confidence": 0.0
    }
  ]
}
```

`simspec-author` reads `physics.json` verbatim into the SimSpec's `physics` sub-tree. The orchestrator forwards `interpretation_log.json` into `integrity.checks[]` as one `type: "ambiguity"` check per low-confidence entry (confidence , \hat{H}, or a wavefunction symbol from Phase 1?
    YES → tag = quantum-coherent. continue Q2a (linearity refinement).
    NO  → continue Q3.

  Q2a. Are nonlinear-in-psi terms present (e.g. |psi|^2 psi for Gross-Pitaevskii)?
       YES → tag = quantum-coherent + nonlinear flag. STOP.
       NO  → tag = quantum-coherent. STOP.

Q3. Does E contain a time derivative \partial_t or d/dt?
    YES → continue Q4.
    NO  → continue Q5 (steady-state branch).

Q4. Are nonlinear terms in the dependent variable present
    (products like u u_x, |E|^2 E, sin(theta) where theta is unbounded)?
    NO  → tag = linear-time-dependent. STOP.
    YES → continue Q4a.

  Q4a. Does the paper text or approximations list contain
       "small amplitude", "perturbative", "linearization", "harmonic approximation"?
       YES → tag = weakly-nonlinear. STOP.
       NO  → tag = strongly-nonlinear. STOP.

Q5. Steady-state. Are nonlinear terms present?
    NO  → tag = linear-steady. STOP.
    YES → tag = strongly-nonlinear-steady. STOP.
```

Per-equation tags are written to a private side-table (not part of the schema output) and consumed by Phase 5.

### Phase 3 — Boundary and initial condition extraction

Scan `text_body.json` for the regex set `(boundary condition|BC|fixed at|clamped at|free at|periodic|open boundary|absorbing|PML|radiation condition|no-slip|no-flux|reflecting wall|hard wall|infinite well)`. For every hit, extract a 200-char window around the match and run the BC classifier:

- "fixed", "clamped", "= 0", "u(0)=", "psi(0)=" → `dirichlet`.
- "no-flux", "insulating", "du/dn = 0", "free end" → `neumann`.
- "Robin", "h u + k du/dn" pattern → `robin`.
- "periodic", "wraps", "torus" → `periodic`.
- "open", "outflow" → `open`.
- "absorbing", "PML", "radiation condition", "Sommerfeld" → `absorbing`.

For each extracted BC, write one entry into `boundary_conditions[]` with `region` filled from the text window (e.g. "x=0", "r=R"), `value` as a sympy-parseable string, and `variable` matching a Phase 1 dependent symbol. If the variable cannot be resolved to a Phase 1 symbol, the BC is dropped and a log entry emitted.

Initial conditions use the regex set `(initial state|initially|at t=0|t = 0|prepared in|the system starts)`. For every hit, extract `variable`, `expression`, and the `t0` value (almost always 0; non-zero only when paper explicitly says "we set the clock at t = t_0"). Wavefunctions get special handling: `psi(x, 0) = exp(-x**2 / (2*sigma**2))` and similar Gaussian-wavepacket forms are recognized verbatim.

### Phase 4 — Constants harvesting

Aggregate three sources:
1. `paper.equations[*].variables` entries with `role == "constant"` from Phase 1.
2. `paper.numerical_claims[]` entries whose `source_quote` matches a definition pattern ("we use g = 9.81 m/s^2", "the mass m = 1 kg").
3. Standard CODATA values pulled from the bundled `constants_table.json` (script-supplied; not in this prompt) when the paper uses a symbol like `\hbar`, `c`, `k_B` without redeclaring.

For each entry produce a `{symbol, value, unit, source}` tuple. `source` is `"paper"` if the value came from sources 1 or 2, `"standard"` if from CODATA, `"derived"` if computed from other constants (e.g. `omega_0 = sqrt(k/m)` when both `k` and `m` are paper constants).

Unit conflicts (paper says `\lambda = 632 nm`, claims says `\lambda = 6.328e-7 m`) are reconciled by canonicalizing both to UCUM via pint's parser, comparing magnitudes, and keeping the paper-text value if they agree to 0.1 %. Disagreement emits a log entry and keeps the paper-text value (paper is authoritative for paper-specific constants).

### Phase 5 — Regime + named approximations

The `physics.regime` enum has five values; map per-equation tags from Phase 2 to one whole-spec value via:

- Any per-equation tag is `stochastic`? → spec regime = `stochastic`.
- Else any tag is `quantum-coherent`? → spec regime = `quantum-coherent`.
- Else any tag is `strongly-nonlinear` or `strongly-nonlinear-steady`? → spec regime = `strongly-nonlinear`.
- Else any tag is `weakly-nonlinear`? → spec regime = `weakly-nonlinear`.
- Else → spec regime = `linear`.

Named approximations are extracted by dictionary lookup over `text_body.json`. The dictionary lives in the bundled `approximations_dictionary.json` (script-supplied) and contains entries like `{"thin lens": "thin-lens", "paraxial approximation": "paraxial", "Born-Oppenheimer": "Born-Oppenheimer", "Boussinesq": "Boussinesq", "small-angle": "harmonic-small-amplitude", "Stokes flow": "Stokes", "WKB": "WKB", "rotating-wave approximation": "RWA", "tight-binding": "tight-binding", "incompressible": "incompressible"}`. Multi-word matches win over single-word matches. Each match emits a log entry with the verbatim quote that triggered it (auditable provenance for `approximations[]`).

## Ambiguity handling

A choice is ambiguous when two or more branches in any of the five phases are non-trivially compatible with the inputs. Every such choice commits a value AND writes one entry into `interpretation_log.json` with the shape declared in Outputs. Confidence is a number in [0, 1] computed as follows:

- Single matching branch with clear textual evidence → confidence 0.95+.
- Multiple branches matched, paper text broke the tie → confidence 0.7 to 0.9.
- Tie broken by bundled-examples prior → confidence 0.5 to 0.7.
- Tie broken by an arbitrary default → confidence below 0.5; the orchestrator surfaces this to the user as a "hidden physics default" toggle in the UI (the SINTEF tacit-assumption gap rendered visible).

Three rules govern logging:
1. Never commit silently. Even high-confidence choices that closed a non-trivial branch are logged.
2. The `reason` field cites either the paper section anchor, the symbol, or the dictionary entry that decided the branch. Never "by convention".
3. Conflicts between sources (paper text vs claims vs equations) become a log entry with `phase: "*-conflict"` and confidence ≤ 0.6.

## The bundled examples library

The `examples/` directory holds eight worked examples, one per canonical physics class. Each file is a markdown document with frontmatter tags `domain`, `regime`, `technique`, `paper`, plus four sections: source equations (LaTeX verbatim), parsed equations (sympy), resulting `physics` sub-tree (JSON validated against `simspec.schema.json`), interpretation reasoning, and a list of ambiguities the interpreter would log on this paper.

The orchestrator loads the relevant subset of these examples by frontmatter match before invoking the skill: when the orchestrator's pre-classifier identifies the paper as e.g. "QM, time-dependent, 1D", it injects `infinite-well-1d.md` and `tunneling-1d.md` as few-shot context. When the paper class is unknown, all eight are injected (the per-skill 200K-token budget under Opus 4.7 1M comfortably absorbs them). The examples are not just human reference: Opus 4.7 reads them as in-context priors when filling Phase 1 symbol roles and Phase 5 named approximations.

The eight examples:

| File                       | Domain     | Regime                | Technique           |
|----------------------------|------------|-----------------------|---------------------|
| `young-double-slit.md`     | optics     | linear                | wave-FFT            |
| `simple-pendulum.md`       | mechanics  | linear (small-angle)  | RK4 ODE             |
| `double-pendulum.md`       | mechanics  | strongly-nonlinear    | RK4 ODE             |
| `infinite-well-1d.md`      | quantum    | quantum-coherent      | analytic            |
| `tunneling-1d.md`          | quantum    | quantum-coherent      | FFT-spectral split  |
| `2d-ising.md`              | stat-mech  | stochastic            | Monte-Carlo         |
| `stokes-sphere.md`         | fluid      | linear (low-Re)       | analytic            |
| `damped-oscillator.md`     | mechanics  | linear (dissipative)  | analytic + ODE      |

## 11. archetype hints

physics-interpreter MAY emit an advisory field `viz_hint_archetype` on each governing equation to help `simspec-author` pick the right `viz.scene_archetype`. Possible values match the eight enum members defined in `viz-mapper`'s SKILL.md §4: `ray-optics`, `glass-dispersion`, `field-2d`, `orbital-3d`, `particle-cloud`, `lattice`, `graph-time`, `schematic`.

Mapping table from per-equation tags (Phase 2) and `domain_hints[]` to advisory archetype:

- `domain_hints` includes `em` or `wave` AND regime is linear-time-dependent → advisory `field-2d` (or `glass-dispersion` if the surrounding text mentions "prism", "dispersion", "Abbe", "refraction in 3D").
- `domain_hints` includes `QM` AND any equation involves a 3D Laplacian on a wavefunction → advisory `orbital-3d`.
- `domain_hints` includes `QM` AND only 1D / 2D coordinates appear → advisory `field-2d`.
- `domain_hints` includes `mechanics` AND regime is `linear` or `weakly-nonlinear` AND only one or two dependent variables → advisory `graph-time`.
- `domain_hints` includes `fluid` → advisory `field-2d`.
- `domain_hints` includes `materials` AND regime is `stochastic` → advisory `lattice`.
- Per-equation tag is `stochastic` AND surrounding text contains "Brownian", "particle", "ensemble", "trajectory" → advisory `particle-cloud`.

The advisory is written into the side-table consumed by `simspec-author`, not into `physics.json` directly (the schema does not currently carry it, and emitting it would break the canonical form §). This field is advisory only — `simspec-author` ultimately decides per the routing rules in its SKILL.md §11. Conflicts between this advisory and `simspec-author`'s rules are logged with `phase: "5x-archetype-advisory"` and confidence 0.5.

## Hand-off

`simspec-author` copies `physics.json` verbatim into `simspec.physics`. All six sub-keys must be present even when empty (use `[]` for arrays, never omit). Schema reference: `$defs.physics` in `mobius/skills/simspec-author/schema/simspec.schema.json`.

Downstream consumers:
- `simspec-author` Rule 3 reads `regime` and `governing_equations[*].domain_hints` to choose `solver_hints.scheme`.
- `simspec-author` Rule 8 reads `approximations` to truncate slider ranges.
- `primitive-generator` reads `governing_equations[*].sympy` and the constants table to template Python solver code.
- `science-integrity` (units check) reads `constants[*].unit` and `governing_equations[*].variables[*].unit`.

## Red flags

Refuse to emit `physics.json` when any of the following holds. Write the partial output AND a `phase: "abort"` log entry; the orchestrator decides whether to re-dispatch.

Never invent equations. If `paper.equations[]` is empty, emit `governing_equations: []` and abort with a log entry. Inventing a "probably the wave equation" entry is the single most catastrophic failure mode.

Never tag a regime without paper evidence. If Phase 5 cannot find at least one per-equation tag, emit `regime: "linear"` (the most conservative default) AND a confidence-0.0 log entry. Do not tag `quantum-coherent` from a title keyword alone.

Never tag an approximation that is not literally named in the paper text or implied by a term explicitly dropped in an equation derivation. The bundled dictionary is a whitelist, not a generator. When in doubt, omit the approximation and log it.

Never resolve a Phase-1 symbol to a unit not declared in the paper or derivable from the equation's dimensional structure. The CODATA fallback is allowed only for universally-named constants (`\hbar`, `c`, `k_B`, `e`, `m_e`).

Never silently drop a boundary condition because its variable is unresolved. Drop AND log; the orchestrator will re-dispatch Phase 1 with extra hints.

Never emit `physics.json` if any of the bundled validators fail (sympy round-trip, pint unit parse, JSON schema). Write `physics.partial.json` instead and log the failure.

## Edge cases (sympy + pint)

Two library-specific traps the skill must handle.

`sympy.parsing.latex.parse_latex` has two backends. The default ANTLR backend silently drops trailing partial expressions: `parse_latex(r'x -')` returns `x` without raising. The Lark backend raises `UnexpectedEOF` on the same input. The skill MUST call `parse_latex(..., backend='lark')` an

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [leventilo](https://github.com/leventilo)
- **Source:** [leventilo/mobius](https://github.com/leventilo/mobius)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-leventilo-mobius-physics-interpreter
- Seller: https://agentstack.voostack.com/s/leventilo
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
