Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions AGENTS.md

Large diffs are not rendered by default.

71 changes: 71 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,77 @@ supported as exact pairs; on the 0.x line they are independent. See the
the release commit. A prerelease note describes changes since the preceding
prerelease or stable tag; the final stable note consolidates the series.

## 1.5.0 — Unreleased

The planned number of the next release; it stays **Unreleased** until its tag
exists, and the package version is unchanged. This entry records the diagnostic
additions below and nothing else: the engine pairing, the rest of the content
and the date of 1.5.0 are set when the release is prepared. It changes
diagnostics only; no analyzer or code-generation file differs.

### Compatibility and migration

- **`note` is a third diagnostic severity, and it is nonfatal.** A diagnostic
is now an `error`, a `warning` or a `note`: `Level.NOTE` in Python and `"note"`
as the `severity` of a catalog entry and of a JSON diagnostic. A note is
nonfatal like a warning. `transpile_full()["diagnostics"]` and the success
envelope's `diagnostics` list warnings and notes, a script with only notes
still returns `ok: true` and its C++, and a `CompileError` can carry notes
beside its errors. `transpile()` still returns no nonfatal diagnostics.
`CompileError.format()` prints `note[...]` where it printed `warning[...]` for
the codes below. Code written for two severities must handle `note`: read an
unknown severity as a warning, never as an error. A note does not mean that
results are unaffected; PF-W1505 describes a warmup approximation.
- **Exactly eleven codes change from warning to note:** PF-W1505, PF-W1508,
PF-W1509, PF-W1525, PF-W1526, PF-W1527, PF-W1528, PF-W1529, PF-W1530,
PF-W1531 and PF-W1532. They describe calls PineForge skips as visual only
(`plot(...)`, `table.new(...)`), earnings, dividends, splits, financial,
footprint and other-symbol requests whose value reaches only display and alert
sinks and is lowered to `na`, and the `ta.ema` warmup approximation. Every
other code keeps its severity. PF-W1502 (`color(...)` cast), PF-W1506
(`color.from_gradient`), PF-W1507 (drawing setters that drop color, style or
size), PF-W1078 (visual or style constants) and PF-W1552 (a
`request.security_lower_tf` whose value reaches only display and alert sinks)
also speak of visual-only or display-only handling and remain warnings: a
message's wording does not classify it. A consumer that tallied these eleven
codes as warnings, to gate a run, count warnings or color a banner, now
receives notes. PF-W1509 still classifies as PF-W1508 where the two templates
tie, so no new emitted code appears.
- **`user_message` is a raw ICU template over `args`.** Every catalog entry,
every `Diagnostic` (`Diagnostic.user_message`) and every JSON diagnostic gains
`user_message`: a short English ICU template that uses only that diagnostic's
existing `args` names and may be constant (`{name}() does nothing in a backtest.`
with `args.name = "plot"`). It is not pre-rendered English. Render it with
`args` as plain text, never as template syntax or markup, or translate by
`code`; do not display the unrendered template. It does not replace `message`
and `hint`. The templates of the eleven migrated codes are fixed string for
string by a delta fixture. Every template is checked for form (short,
parseable, over that code's own `args` names). Each of the 97 warning twins
of an error (`PF-W1nnn` for `PF-E1nnn`) has a sentence of its own that says
PineForge warns, and the 91 that warn only inside a `switch` arm also say it
keeps the arm's lowering. No warning or note template says that PineForge
refuses or rejects something, though one may say what TradingView refuses or
rejects. Beyond these checks a template's exact wording is not pinned.
- **Legacy fields are stable.** `code`, `args`, `message`, `hint`, `line`,
`col`, `endCol`, `ok`, `error` and the envelope keys keep their meaning and
values: no key is renamed or removed, and no code is renumbered or reused (the
notes keep their `PF-W` codes, and the prefix is not the severity).
`render_diagnostic()` still returns the unchanged English `message` and
`hint`. The catalog schema id stays `pineforge-diagnostics-catalog/v1`, so a
reader that checks `severity` against two values or rejects unknown entry
fields must accept `note` and `user_message`. The receiver examples in
`tests/fixtures/diagnostic_notes/receiver.json` specify the compatibility
rule; they are not evidence of any application's behavior.
- **The warmup sidecar is later work.** No compile-time or runtime sidecar
exists in this entry. PF-W1505 stays the static `ta.ema` note it was, with
only its severity changed: the support checker emits it at each `ta.ema` call
it visits, with no per-call condition or suppression and no call-site
identifier. The startup inventory and extraction that would bind a versioned
sidecar to a diagnostic by `call_site_id` and source range, so that a consumer
can show a startup sentence only for a known finite shortfall, are not part of
this entry, and no diagnostic carries a call-site identifier or a startup
length.

## 1.4.0 — 2026-10-08

A minor release of the exact engine/codegen pair. Its behavior was validated
Expand Down
17 changes: 12 additions & 5 deletions CLAUDE.md

Large diffs are not rendered by default.

57 changes: 40 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ for the changes in each release from 1.0.0 on and the release-note policy.
`transpile_full()` are the supported Python entry points.
- **Located diagnostics** — the support checker rejects unsupported Pine with
a `file:line:col` location before any C++ is emitted, and
`transpile_full()` returns warnings for supported scripts with documented
approximations.
`transpile_full()` returns nonfatal warnings and, since 1.5.0, notes for supported scripts,
including those with documented approximations.

## Releases and this README

Expand All @@ -64,6 +64,10 @@ covers 1.0.0 on and links the notes of earlier releases. A source install
reports the version in `VERSION`, which the release workflow sets when it tags
a release.

Text that says "since 1.5.0" describes the source tree on `main`, not a
published package: 1.5.0 is the planned number of the next release, and the
statements about 1.4.0 keep their historical meaning.

### Upgrading from 1.3.0

For the 1.4.0 pair, regenerate C++ and relink against engine
Expand Down Expand Up @@ -190,8 +194,8 @@ transpile(
Returns the generated C++ source as a string. Raises
`pineforge_codegen.errors.CompileError` on a rejected construct and, since 1.0.0,
on a syntax error or an input limit (0.10.4 recovers from some syntax errors
and has no input limits). It does not return nonfatal warnings; use
`transpile_full()` to inspect them.
and has no input limits). It does not return nonfatal diagnostics (warnings and,
since 1.5.0, notes); use `transpile_full()` to inspect them.

`libraries` is new in 1.0.0. It maps an import path to the library's
source (`{"user/name/version": source_text}`), and each import the script uses
Expand All @@ -218,8 +222,8 @@ It returns `{"cpp": str, "inputs": list[dict], "strategyParams": dict,
"diagnostics": list[Diagnostic], "requests": list[dict]}` on success (0.10.4
returns the first three keys; `requests` is new in 1.1.0). `inputs` is the input
manifest; its `title` is the actual override key, and an `input.symbol` entry
also has `"kind": "symbol"` (since 1.1.0). `diagnostics` contains nonfatal
warnings. Since 1.4.0, each input also has `supported`, the checked-settings receipt's
also has `"kind": "symbol"` (since 1.1.0). `diagnostics` contains the nonfatal
warnings and, since 1.5.0, notes. Since 1.4.0, each input also has `supported`, the checked-settings receipt's
flag. Hide inputs with a false flag, exclude them from sweeps and optimization,
and do not send a value: the compiled default is used, and the paired engine
refuses a supplied value with `setting_unsupported`. A true flag is not a full
Expand Down Expand Up @@ -250,17 +254,29 @@ renamed or removed, and the changelog lists the values that changed.
A rejected script raises `CompileError` with its diagnostics. The Pyodide
package ships `gate/glue.py`'s `transpile_json(source) -> str`, whose JSON
success and error envelopes carry the same manifest and, since 1.0.0, the same
warnings; since 1.1.0 the success envelope also carries `requests`. See the
warnings; since 1.1.0 the success envelope also carries `requests`. Since 1.5.0, notes, the
informational level described under [Diagnostic codes](#diagnostic-codes),
travel in the same `diagnostics` lists with `"severity": "note"`. See the
[1.0 public contract](https://github.com/pineforge-4pass/pineforge-codegen-oss/blob/main/docs/PUBLIC_CONTRACT.md)
for the exact fields, severity values, and input key rules. There is no
installed CLI or exit-code contract.

### Diagnostic codes

Every diagnostic carries a stable `code` and named `args` (since 1.2.0):
`PF-E1203` for an error, `PF-W0412` for a warning, and the data its English
`message` and `hint` were built from — identifiers, types, keywords and
numbers, raw.
Every diagnostic carries a stable `code` (of the form `PF-E1203` or `PF-W0412`)
and named `args` (since 1.2.0): the data its English `message` and `hint` were
built from — identifiers, types, keywords and numbers, raw.

An `E` code is an error. A `W` code can be a warning or, since 1.5.0, a note:
read its declared `level` (`severity` in JSON entries and the catalog).
A note is a nonfatal, informational diagnostic, and it keeps
the `PF-W` code it had as a warning. Eleven codes are notes: PF-W1505 (the
`ta.ema` initial-warmup approximation, reported at every `ta.ema` call as a
general caveat, not a measured shortfall), PF-W1508 and PF-W1509 (a call such as
`plot` or `alert` that has no effect in a backtest) and PF-W1525 to PF-W1532 (a
request for outside data whose value reaches only display and alert sinks,
lowered to `na`). Every other diagnostic keeps its severity, the other
visual-only and display-only ones included; the catalog is authoritative.

```python
from pineforge_codegen import diagnostics_catalog, transpile_full
Expand All @@ -272,15 +288,21 @@ for d in transpile_full(source)["diagnostics"]:
entry = diagnostics_catalog()["codes"]["PF-W1067"]
# {'severity': 'warning', 'area': 'support',
# 'message': '{name} diverges from TradingView semantics in PineForge.',
# 'hint': '...', 'explanation': '...', 'args': {'name': {'kind': 'identifier'}}}
# 'hint': '...', 'explanation': '...', 'args': {'name': {'kind': 'identifier'}},
# 'user_message': '...'}
```

The catalog (`pineforge_codegen/diagnostics_catalog.json`, also attached to
each GitHub release) gives each code its severity, English ICU MessageFormat
templates and a one-line explanation; rendering the templates with `args`
gives the `message` and `hint` byte for byte, so an application can translate
a diagnostic by its code. Codes are never reused. See
[Diagnostic codes](docs/PUBLIC_CONTRACT.md#diagnostic-codes).
`message` and `hint` templates, a one-line explanation and a short
`user_message` template; rendering the `message` and `hint` templates with
`args` gives the diagnostic's `message` and `hint` byte for byte, so an
application can translate a diagnostic by its code. `user_message` is a
separate short English ICU template (since 1.5.0) over the same `args` (it may be constant):
the translation source and the fallback for a code an application does not
know. It is a template, not pre-rendered English, and each `Diagnostic` and
JSON diagnostic entry carries it as `user_message` too. Codes are never reused.
See [Diagnostic codes](docs/PUBLIC_CONTRACT.md#diagnostic-codes).

### List the other symbols a script requests

Expand Down Expand Up @@ -371,7 +393,8 @@ transpile(src, filename="my_strategy.pine")

Not every request for outside data is refused. A
`request.financial()` whose value reaches only plots, alerts, tables or logs
transpiles with a warning and reads `na`; 0.10.4 refuses it. The
transpiles with a note since 1.5.0 (a warning in earlier releases) and reads `na`; 0.10.4
refuses it. The
[changelog](https://github.com/pineforge-4pass/pineforge-codegen-oss/blob/main/CHANGELOG.md)
lists what 1.0.0 reads, warns about or refuses for such requests.

Expand Down
Loading
Loading