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.
167| =#+TEMPLATE:= | The layout this page renders through. | 167| =#+TEMPLATE:= | The layout this page renders through. |
168| =#+OPTIONS:= | Per-file export switches. | 168| =#+OPTIONS:= | Per-file export switches. |
169| =#+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:=. | 169| =#+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. |
170 171
171Every other =#+KEYWORD:= is available to templates as 172Every other =#+KEYWORD:= is available to templates as
172={{ page.keywords.that_keyword }}=, so metadata orgo has never heard of still reaches 173={{ page.keywords.that_keyword }}=, so metadata orgo has never heard of still reaches
@@ -180,7 +181,6 @@ a build.
180| Construct | What happens | 181| Construct | What happens |
181|-----------+--------------| 182|-----------+--------------|
182| Babel execution, =:results= | The source block renders as code. A checked-in =#+RESULTS:= block is *dropped*. | 183| 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. |
184| =#+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. | 184| =#+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. |
185| LaTeX, MathJax | Survives as the literal text you typed. | 185| LaTeX, MathJax | Survives as the literal text you typed. |
186| Macros ={{{name}}}=, radio targets | Literal text. | 186| 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:
17* Reading the output 17* Reading the output
18 18
19#+BEGIN_EXAMPLE 19#+BEGIN_EXAMPLE
20corpus: 179 file(s), 29258 line(s) 20corpus: 180 file(s), 29742 line(s)
21 21
22CONSTRUCTS (by frequency) 22CONSTRUCTS (by frequency)
23 construct uses files first seen 23 construct uses files first seen
24IN list item 1282 109 blog/2018-11-28-aes-encryption.org:53 24IN list item 1309 111 blog/2018-11-28-aes-encryption.org:53
25IN heading 1128 174 blog/2018-11-28-aes-encryption.org:7 25IN heading 1148 176 blog/2018-11-28-aes-encryption.org:7
26IN source block 932 121 blog/2018-11-28-cpp-compiler.org:17 26IN verbatim 1048 130 blog/2018-11-28-aes-encryption.org:79
27... 27...
28OUT table formula (#+TBLFM:) 4 1 blog/2024-08-11-org-mode-features.org:191 28IN 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 29OUT unmodelled block type 1 1 blog/2026-03-03-auditing-aws-s3.org:50
30 30
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%)
32 32
33KEYWORDS 33KEYWORDS
34 TITLE 179 179 blog/2018-11-28-aes-encryption.org:2 34 SLUG 180 180 blog/2018-11-28-aes-encryption.org:4
35??? SLUG 178 178 blog/2018-11-28-aes-encryption.org:4 35 TITLE 180 180 blog/2018-11-28-aes-encryption.org:2
36 DESCRIPTION 176 176 blog/2018-11-28-aes-encryption.org:3
37... 36...
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
38#+END_EXAMPLE 42#+END_EXAMPLE
39 43
40- =IN= is supported; =OUT= is excluded by design and degrades as described in 44- =IN= is supported; =OUT= is excluded by design and degrades as described in
41 [[file:05-org-support.org][Org support]]. 45 [[file:05-org-support.org][Org support]].
42- The *coverage* line is the number to look at first. 46- 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 47- =???= marks a name orgo does not recognise at all — here =#+begin_note=, which one
44 a run made before it was supported. 48 post uses once. That is the blind-spot signal: not "known unsupported", but unknown.
45 49
46Four censuses follow the construct table: every distinct =#+KEYWORD:=, block type, 50Four censuses follow the construct table: every distinct =#+KEYWORD:=, block type,
47drawer name and link scheme in the corpus. A =???= in any of them is worth a look. 51drawer 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 {
258 "CAPTION" | "NAME" | "ATTR_HTML" => { 258 "CAPTION" | "NAME" | "ATTR_HTML" => {
259 self.count(Scope::In, "affiliated keyword", at) 259 self.count(Scope::In, "affiliated keyword", at)
260 } 260 }
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),
262 "INCLUDE" => self.count(Scope::Out, "#+INCLUDE:", at), 266 "INCLUDE" => self.count(Scope::Out, "#+INCLUDE:", at),
263 "RESULTS" => self.count(Scope::Out, "babel results block", at), 267 "RESULTS" => self.count(Scope::Out, "babel results block", at),
264 "TODO" => self.count(Scope::Out, "#+TODO: keyword sequence", at), 268 "TODO" => self.count(Scope::Out, "#+TODO: keyword sequence", at),
tests/constructs.rs +16
@@ -387,6 +387,7 @@ fn well_formed_fixtures_produce_no_diagnostics() {
387 "blocks.org", 387 "blocks.org",
388 "timestamps.org", 388 "timestamps.org",
389 "images.org", 389 "images.org",
390 "tblfm.org",
390 ] { 391 ] {
391 let document = parse_fixture(name); 392 let document = parse_fixture(name);
392 assert!( 393 assert!(
@@ -800,3 +801,18 @@ fn audit_counts_real_entities() {
800 "a real entity was not counted:\n{report}" 801 "a real entity was not counted:\n{report}"
801 ); 802 );
802} 803}
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() {
628 "timestamps.org", 628 "timestamps.org",
629 "images.org", 629 "images.org",
630 "elements.org", 630 "elements.org",
631 "tblfm.org",
631 ]; 632 ];
632 let mut offenders = Vec::new(); 633 let mut offenders = Vec::new();
633 for fixture in fixtures { 634 for fixture in fixtures {