Prose is the illusion. You choose to see it — with truth wired through.
A .pgmd notebook interleaves markdown prose with pltg computation blocks. Every claim in the narrative connects back to grounded facts, derived metrics, or validated axioms. The rendered output shows both the story and the proof.
pg-bench render notebook.pgmd -o output.html # render to file
pg-bench render notebook.pgmd # render to stdout
pg-bench render notebook.pgmd -t "Q3 Report" # custom title
pg-bench render notebook.pgmd --user Alice --assistant Claude -o out.html
The renderer executes all pltg blocks, builds a bench (lens + diagnostics), and produces self-contained HTML with a notebook view, structure view, layers, and graph.
Session booking is critical for taint analysis. Pass --user and/or --assistant to record who is working on this notebook. The logbook (.parseltongue-bench/logbook.jsonl) feeds into taint computation — without it, the taint system cannot trace signatures back to session participants. Either or both flags work.
The logbook is CWD-relative. .parseltongue-bench/ is created in the current working directory. Always cd into the project directory before rendering:
cd my-analysis/
pg-bench render notebooks/report.pgmd --user "Alice" --assistant "Claude" -o out.html
Running from a parent directory writes the logbook to the wrong location — the taint system won't find the session history next to the notebook.
A .pgmd file is markdown. Code blocks with scheme language and a ;; pltg comment on the first line are treated as executable pltg blocks. Everything else is prose.
# Title
Prose paragraph with **markdown** formatting.
` ` `scheme
;; pltg Block Name
(fact revenue 5000000 :evidence (evidence "memo" :quotes ("Revenue: $5M") :explanation "TTM"))
(derive growth-rate (/ revenue baseline) :using (revenue baseline))
` ` `
Revenue is $[[fact:revenue]], growing at [[term:growth-rate]]x.
The ;; pltg marker and block name are required. The name appears in the rendered output header.
References connect prose to computed values. They resolve at render time.
| Syntax | Renders as | Use |
|---|---|---|
[[fact:revenue]] |
The fact's value with footnote | Show a ground truth value |
[[term:growth-rate]] |
The derived value with footnote | Show a computed metric |
[[~term:margin-check]] |
Silent superscript footnote only | Validation badge — no inline value |
$[[fact:revenue]] |
$ prefix before value |
Dollar amounts |
[[fact:nrr]]% |
% suffix after value |
Percentages |
[[term:ratio]]x |
x suffix after value |
Multipliers |
Prefix/suffix characters stick to the resolved value. $[[fact:revenue]] renders as $2.4M, not $ 2.4M.
Silent refs (~) are for validation checks — they show a small superscript footnote number without displaying the value inline. Use them after the claim they validate: Gross margin is healthy[[~term:margin-check]].
Important: refs render values, not names. A fact (fact nrr 180) renders as 180, not nrr. This means:
$[[fact:revenue]] → $2.4M, [[fact:nrr]]% → 180%true/false) look wrong inline — true is not meaningful prose. Write the claim in normal text and attach the check silently: Retention is strong[[~term:retention-check]], not Retention is [[term:retention-check]](> ?n 130) — always use silent refs for theseAll facts, computation, and prose in a single .pgmd. Self-contained, shareable.
` ` `scheme
;; pltg Core Facts
(load-document "memo" "source/memo.txt")
(fact arr 2800000 :evidence (evidence "memo" :quotes ("ARR: $2,800,000") :explanation "Current ARR"))
(fact cogs 480000 :evidence (evidence "memo" :quotes ("COGS: $480,000") :explanation "TTM COGS"))
` ` `
` ` `scheme
;; pltg Metrics
(derive gross-margin (/ (- revenue cogs) revenue) :using (revenue cogs))
(axiom margin-ok (> ?m 0.6) :origin "gross margin > 60%")
(derive margin-check margin-ok :bind ((?m gross-margin)) :using (gross-margin margin-ok))
` ` `
Gross margin: **[[term:gross-margin]]%**[[~term:margin-check]].
` ` `scheme
;; pltg Review
(verify-manual (quote margin-ok) "Claude")
` ` `
In standalone, the review block lives at the end of the same file — no separate review.pltg needed. Only verify assumptions (:origin-based axioms), not grounded facts.
Best for: technical deep-dives, prototyping, self-contained reproducible analysis.
Import a facts module, then derive metrics in the notebook's pltg blocks.
` ` `scheme
;; pltg Load Facts
(import (quote ..facts.company_facts company))
` ` `
` ` `scheme
;; pltg Margin Analysis
(derive gross-margin (/ (- company.revenue company.cogs) company.revenue) :using (company.revenue company.cogs))
` ` `
Revenue: $[[fact:company.revenue]], margin: [[term:gross-margin]]%.
The (import (quote ..module alias)) syntax gives a short name to the module. company.revenue resolves to the canonical facts.company_facts.revenue.
Best for: technical analyses where facts are maintained separately. Can connect to much larger pltg systems — the notebook is a view into an existing formal model.
Import both facts and a rules module that already derives everything. The notebook is pure narrative.
` ` `scheme
;; pltg Load Analysis
(import (quote ..facts.company_facts))
(import (quote ..analysis.company_rules))
` ` `
LTV/CAC of [[term:company_rules.ltv-to-cac]]x[[~term:company_rules.ltv-cac-check]] with [[fact:company_facts.nrr]]% NRR.
Best for: sharing with non-technical stakeholders. The notebook is clean, non-threatening prose — no visible computation, just narrative with grounded references. Analysts maintain the logic separately; writers author the story. Connects to the full pltg system — the notebook is a readable surface over arbitrarily complex formal models.
Each pltg block executes in order. Later blocks can reference names defined in earlier blocks. A block that errors shows the error in the rendered output — remaining blocks continue executing.
The last non-directive expression in a block becomes the block's return value, displayed in the "Out:" row:
(print "message") produces stdout, shown in green. Errors show in red.
Axioms define business rules with ?-variable placeholders. Derives bind concrete values and check them:
If gross-margin > 0.6, the derive succeeds (theorem is true). Reference it silently in prose:
Margin is strong[[~term:margin-check]].
The rendered output shows a superscript footnote linking to the validation result. Margin pills in the right gutter show all referenced nodes for each paragraph.
For explicit/implicit patterns, organize as:
project/
source/
memo.txt # ground truth document
facts/
company_facts.pltg # facts extracted from source
analysis/
company_rules.pltg # derives, axioms, validations
notebooks/
report.pgmd # the notebook
review-report.pltg # post-session verification (created after)
Import with relative dots: ..facts.company_facts means "up one directory, into facts/, load company_facts.pltg".
The renderer auto-formats numeric values:
= 1,000,000 →
2.4M
= 1,000 →
18,200
80.00% (percentage)true / falsePrefix/suffix from the ref syntax is prepended/appended: $2.4M, 80.00%%, 4.91x.
verify-manual is not part of the analysis itself. It's a post-hoc review step — used after the main work is done, or when assumptions were made outside of documented evidence.
:origin-based facts)Standalone: inline block at the end of the .pgmd — no separate file needed:
Explicit/Implicit: separate review-{notebook}.pltg next to the .pgmd, imported after everything else:
project/
notebooks/
report.pgmd
review-report.pltg # created AFTER main session
;; review-report.pltg — post-session verification
;; Session: Alice + Claude, 2026-03-26
(import (quote ..analysis.company_rules))
;; Assistant-signed (assumptions made during analysis)
(verify-manual (quote company_rules.market-threshold) "Claude")
(verify-manual (quote company_rules.growth-assumption) "Claude")
;; User-signed (explicitly requested by user)
(verify-manual (quote company_facts.adj-revenue) "Alice")
The review file imports the modules it needs to verify, then references names with (quote ...). It's imported in the .pgmd after the analysis imports.
"system" if anonymouspg screen --what issues:origin-based items — these are ungrounded assumptionsreview.pltg with the agreed signaturesmain.pltg and reloadThe logbook (.parseltongue-bench/logbook.jsonl) records who booked the bench session, so signatures trace back to a real session entry.
Every rendered view (notebook pills, layers, graph, cards, detail panel) shows taint status. Taint propagates through the dependency graph — if a source is untrusted, everything derived from it is tainted.
A node is a taint source (red) if:
verified, derived, or manual)A node is tainted (yellow/dashed) if any of its inputs are tainted. This propagates transitively through the full derivation chain.
The taint predicate receives the full logbook history. This means custom predicates can check:
Without --user/--assistant on render, the logbook is empty and taint falls back to evidence-only checks. Always book a session for full taint analysis.
| Context | Taint source | Tainted (propagated) | Clean |
|---|---|---|---|
| Notebook margin pills | Red border, ✖ icon | Yellow dashed border, ⚠ icon | No border |
| Notebook inline refs | Red wavy underline | Yellow dashed underline | Normal |
| Layers pills | Red solid stroke | Yellow dashed stroke | Kind color stroke |
| Graph nodes | Red stroke | Yellow stroke | Kind color |
| Cards | Red border, "taint source" tag | Yellow dashed border, "tainted" tag | Normal border |
| Detail panel | Red "Taint source" section with reason | Yellow "Tainted" section with reason | No section |
Taint reasons are shown on hover (margin pills) and in the detail panel. They explain why: "no evidence", "unverified", or "depends on tainted: X".
Study the AI2AI demo at parseltongue/core/demos/ai2ai_pgmd/. It contains all three patterns side by side:
parseltongue/core/demos/ai2ai_pgmd/
source/
series_a_memo.txt # ground truth — the investment memo
facts/
ai2ai_facts.pltg # 30+ facts extracted with evidence
analysis/
ai2ai_rules.pltg # derives, axioms, validation checks
notebooks/
standalone.pgmd # standalone pattern — everything in one file
explicit.pgmd # explicit pattern — imports facts, computes inline
implicit.pgmd # implicit pattern — imports pre-computed analysis, pure narrative
render_all.py # renders all three to pgmd_out/
Render them with:
pg-bench render parseltongue/core/demos/ai2ai_pgmd/notebooks/implicit.pgmd -o temp/implicit.html