krz/orgo

Lightning fast org-mode static site generator. fast go org-mode static-site-generator

Commit 809ac9d720

809ac9d720951c3f1ded537571e832c7fb349960

parent: 82639e554b

Unregistered key

cmc <hello@cleberg.net> · 2026-08-23 03:52 UTC
committer: <noreply@github.com>

audit: #+TBLFM: is in scope, not a gap (#27)

The audit counted `#+TBLFM:` as out-of-scope, which read as a gap in coverage.
It is not one: org's HTML exporter does not recalculate table formulas on
export either, so both orgo and Emacs emit the cells exactly as written. The
parser has always swallowed the line, and `docs/guide/05-org-support.org`
already recorded that as the decision — only the audit disagreed.

`fixtures/tblfm.org` is a table whose cells deliberately contradict its
formula, wired into the Emacs oracle so the claim is checked rather than
asserted: 0 unexplained divergences.

Moves the doc row from "Not supported" to the supported keyword table with the
reason, and refreshes the sample output in the auditing guide, which still
showed the pre-#26 entity line and a `??? SLUG` that has since been recognised.

cleberg.net's corpus now audits at 100.0% in-scope coverage, 1 out-of-scope
(the `#+begin_note` of #25).

Closes #24

Layout: unified · split

docs/guide/05-org-support.org +1 −1
@@ -167,6 +167,7 @@ broken.
167167| =#+TEMPLATE:= | The layout this page renders through. |
168168| =#+OPTIONS:= | Per-file export switches. |
169169| =#+CAPTION:=, =#+ATTR_HTML:= | Attach to the image *directly* below them — a blank line in between attaches to nothing, as in org. A captioned image is numbered =Figure N:=. |
170| =#+TBLFM:= | Kept inert, and that matches org: the HTML exporter does not recalculate formulas either, so both emit the cells as written. Recalculate in Emacs (=C-c C-c=) to change them. |
170171
171172Every other =#+KEYWORD:= is available to templates as
172173={{ page.keywords.that_keyword }}=, so metadata orgo has never heard of still reaches
@@ -180,7 +181,6 @@ a build.
180181| Construct | What happens |
181182|-----------+--------------|
182183| Babel execution, =:results= | The source block renders as code. A checked-in =#+RESULTS:= block is *dropped*. |
183| =#+TBLFM:= | Inert. The table renders with the values as written. |
184184| =#+INCLUDE:= | Never expanded, and *reported*: the build prints =file:line: `#+INCLUDE: …` is not expanded=, so a page is never quietly missing content. =--strict= makes it a failure. |
185185| LaTeX, MathJax | Survives as the literal text you typed. |
186186| Macros ={{{name}}}=, radio targets | Literal text. |
docs/guide/09-auditing.org +16 −12
@@ -17,31 +17,35 @@ The audit answers two questions about a body of org files:
1717* Reading the output
1818
1919#+BEGIN_EXAMPLE
20corpus: 179 file(s), 29258 line(s)
20corpus: 180 file(s), 29742 line(s)
2121
2222CONSTRUCTS (by frequency)
2323 construct uses files first seen
24IN list item 1282 109 blog/2018-11-28-aes-encryption.org:53
25IN heading 1128 174 blog/2018-11-28-aes-encryption.org:7
26IN source block 932 121 blog/2018-11-28-cpp-compiler.org:17
24IN list item 1309 111 blog/2018-11-28-aes-encryption.org:53
25IN heading 1148 176 blog/2018-11-28-aes-encryption.org:7
26IN verbatim 1048 130 blog/2018-11-28-aes-encryption.org:79
2727...
28OUT table formula (#+TBLFM:) 4 1 blog/2024-08-11-org-mode-features.org:191
29OUT entity (\name) 3 3 blog/2024-04-06-convert-onenote.org:37
28IN table formula (#+TBLFM:) 4 1 blog/2024-08-11-org-mode-features.org:191
29OUT unmodelled block type 1 1 blog/2026-03-03-auditing-aws-s3.org:50
3030
31coverage: 8854 in-scope use(s) (99.9%), 8 out-of-scope (0.1%)
31coverage: 9001 in-scope use(s) (100.0%), 1 out-of-scope (0.0%)
3232
3333KEYWORDS
34 TITLE 179 179 blog/2018-11-28-aes-encryption.org:2
35??? SLUG 178 178 blog/2018-11-28-aes-encryption.org:4
36 DESCRIPTION 176 176 blog/2018-11-28-aes-encryption.org:3
34 SLUG 180 180 blog/2018-11-28-aes-encryption.org:4
35 TITLE 180 180 blog/2018-11-28-aes-encryption.org:2
3736...
37
38BLOCK TYPES
39 SRC 941 123 blog/2018-11-28-cpp-compiler.org:17
40 QUOTE 115 48 blog/2018-11-28-aes-encryption.org:21
41??? NOTE 1 1 blog/2026-03-03-auditing-aws-s3.org:50
3842#+END_EXAMPLE
3943
4044- =IN= is supported; =OUT= is excluded by design and degrades as described in
4145 [[file:05-org-support.org][Org support]].
4246- The *coverage* line is the number to look at first.
43- =???= marks a name orgo does not recognise at all — in this example =#+SLUG:=, from
44 a run made before it was supported.
47- =???= marks a name orgo does not recognise at all — here =#+begin_note=, which one
48 post uses once. That is the blind-spot signal: not "known unsupported", but unknown.
4549
4650Four censuses follow the construct table: every distinct =#+KEYWORD:=, block type,
4751drawer name and link scheme in the corpus. A =???= in any of them is worth a look.
fixtures/tblfm.org added +10
@@ -0,0 +1,10 @@
1#+TITLE: Table Formula
2
3The cells deliberately contradict the formula: org's HTML exporter does not
4recalculate on export, so both exporters must emit the values as written.
5
6| N | N^2 |
7|---+-----|
8| 2 | 999 |
9| 3 | 888 |
10#+TBLFM: $2=$1^2
src/audit.rs +5 −1
@@ -258,7 +258,11 @@ impl Audit {
258258 "CAPTION" | "NAME" | "ATTR_HTML" => {
259259 self.count(Scope::In, "affiliated keyword", at)
260260 }
261 "TBLFM" => self.count(Scope::Out, "table formula (#+TBLFM:)", at),
261 // Not a gap: org's HTML exporter does not recalculate `#+TBLFM:`
262 // either, so the cells as written are what both exporters emit.
263 // `fixtures/tblfm.org` holds the oracle to that (`tests/oracle.rs`).
264 // Unlike `#+INCLUDE:`, nothing is lost by leaving it inert.
265 "TBLFM" => self.count(Scope::In, "table formula (#+TBLFM:)", at),
262266 "INCLUDE" => self.count(Scope::Out, "#+INCLUDE:", at),
263267 "RESULTS" => self.count(Scope::Out, "babel results block", at),
264268 "TODO" => self.count(Scope::Out, "#+TODO: keyword sequence", at),
tests/constructs.rs +16
@@ -387,6 +387,7 @@ fn well_formed_fixtures_produce_no_diagnostics() {
387387 "blocks.org",
388388 "timestamps.org",
389389 "images.org",
390 "tblfm.org",
390391 ] {
391392 let document = parse_fixture(name);
392393 assert!(
@@ -800,3 +801,18 @@ fn audit_counts_real_entities() {
800801 "a real entity was not counted:\n{report}"
801802 );
802803}
804
805/// `#+TBLFM:` is in scope, not a gap: org's exporter does not recalculate it either, so
806/// the page orgo produces is the page Emacs produces. `fixtures/tblfm.org` is the oracle.
807#[test]
808fn audit_counts_table_formulas_as_in_scope() {
809 let report = audit_fixture("tblfm.org");
810 let line = report
811 .lines()
812 .find(|l| l.contains("table formula"))
813 .expect("the formula is counted at all");
814 assert!(
815 line.starts_with("IN "),
816 "a #+TBLFM: table orgo renders exactly as Emacs does must not read as a gap:\n{line}"
817 );
818}
tests/oracle.rs +1
@@ -628,6 +628,7 @@ fn every_divergence_from_emacs_is_deliberate() {
628628 "timestamps.org",
629629 "images.org",
630630 "elements.org",
631 "tblfm.org",
631632 ];
632633 let mut offenders = Vec::new();
633634 for fixture in fixtures {