@plebeiantech/lat.md 0.12.3-fork.11 → 0.12.3-fork.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -95,12 +95,40 @@ function inlineTextOf(node) {
95
95
  })
96
96
  .join('');
97
97
  }
98
+ /**
99
+ * A GFM table that the parser handed back as a paragraph.
100
+ *
101
+ * `src/parser.ts` runs remark without `remark-gfm`, so a pipe table is never a
102
+ * `table` node — its rows arrive as one paragraph and every reference document
103
+ * containing a table failed with "this is a second paragraph", while the error
104
+ * text told the author to use a table. Adding the plugin would mean editing an
105
+ * upstream file and changing what `remarkStringify` emits for the whole tool,
106
+ * which `tests/roundtrip` pins; recognising the shape here costs nothing
107
+ * outside this check.
108
+ *
109
+ * The signature is a delimiter row — `| --- | :--: |` — under a line that opens
110
+ * a row. Both are required, because a delimiter row is what separates a table
111
+ * from prose that merely contains pipes.
112
+ */
113
+ function isPipeTable(node, lines) {
114
+ const start = node.position?.start.line;
115
+ const end = node.position?.end.line;
116
+ if (!start || !end || end - start < 1)
117
+ return false;
118
+ const rows = lines.slice(start - 1, end);
119
+ return rows.some((row, i) => i > 0 &&
120
+ /^\s*\|?(?:\s*:?-{3,}:?\s*\|)+\s*:?-{3,}:?\s*\|?\s*$/.test(row) &&
121
+ rows[i - 1].trimStart().startsWith('|'));
122
+ }
98
123
  /**
99
124
  * Extra top-level paragraphs found under headings, beyond the one
100
125
  * `checkSections` already permits as the leading summary. A paragraph
101
126
  * before the first heading is the document lead, not narrative prose.
127
+ *
128
+ * A pipe table is not prose and never counts, in either position: it is
129
+ * neither the leading summary a reference is allowed nor an offender.
102
130
  */
103
- function extraParagraphs(tree) {
131
+ function extraParagraphs(tree, lines) {
104
132
  const offenders = [];
105
133
  let currentHeading = null;
106
134
  let seenParagraphUnderHeading = false;
@@ -111,6 +139,8 @@ function extraParagraphs(tree) {
111
139
  continue;
112
140
  }
113
141
  if (node.type === 'paragraph') {
142
+ if (isPipeTable(node, lines))
143
+ continue;
114
144
  if (currentHeading === null) {
115
145
  // Document lead paragraph before any heading — allowed.
116
146
  continue;
@@ -329,7 +359,7 @@ export async function checkMode(latticeDir, projectRoot = dirname(latticeDir), o
329
359
  continue;
330
360
  }
331
361
  if (mode === 'reference') {
332
- for (const offender of extraParagraphs(tree)) {
362
+ for (const offender of extraParagraphs(tree, content.split('\n'))) {
333
363
  errors.push({
334
364
  file: relPath,
335
365
  line: offender.line,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plebeiantech/lat.md",
3
- "version": "0.12.3-fork.11",
3
+ "version": "0.12.3-fork.13",
4
4
  "description": "A knowledge graph for your codebase, written in markdown",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -17,15 +17,41 @@ enforces one shape rule for each:
17
17
  | `lat.md/reference/` | lookup facts: fields, states, limits, names | no narrative prose — no second paragraph under a heading |
18
18
  | `lat.md/explanation/` | why the design is what it is | no imperative commands |
19
19
 
20
- Do not write documents at the top of `lat.md/`. The root index is the only file
21
- that belongs there. When it carries `require-mode: true`, a document with no
22
- declared mode and no mode directory is a `lat check` error.
20
+ A document is classified either by the directory it sits in, or by declaring
21
+ the mode in its own frontmatter:
22
+
23
+ ```markdown
24
+ ---
25
+ lat:
26
+ mode: explanation
27
+ ---
28
+ ```
29
+
30
+ Both are enforced identically — `lat check mode` applies the shape rule to any
31
+ document that declares a mode, whether or not the root index carries
32
+ `require-mode: true`. That flag adds a separate rule: a document declaring *no*
33
+ mode, in no mode directory, becomes an error too.
34
+
35
+ Prefer the directory in a tree you own outright. Prefer the declaration when
36
+ the file cannot move — a document another project maintains, or a tree other
37
+ agents are writing to while you work. Moving costs every reference to the file;
38
+ declaring costs nothing at read time, because frontmatter sits above the first
39
+ heading and outside every section.
23
40
 
24
41
  **A document that fails its mode is usually two documents.** A file that lists
25
42
  the states *and* argues for the design is mixed: the lookup half goes in
26
43
  `reference/`, the reasoning half in `explanation/`, and each links the other.
27
44
  Filing it under one mode and softening the prose until the check passes is the
28
- wrong repair. So is leaving it at the top level where no check can see it.
45
+ wrong repair.
46
+
47
+ Classifying a mixed document does not make it pass. Narrative prose put under
48
+ `reference/` fails on contact, because a reference may not carry a second
49
+ paragraph under a heading — so a move is not a way to adopt the rule cheaply.
50
+ Split first, then place. And `explanation` is not the safe default: it bans
51
+ only imperative commands, so nearly any prose passes it. Declaring it is a
52
+ claim about the document, not somewhere to put whatever is left over. A
53
+ document you cannot honestly classify is better left undeclared than
54
+ mislabelled.
29
55
 
30
56
  Classify before writing, not after. If a `diataxis` skill is available, use it.
31
57