Automatic folgezettel (computer-compatible Luhmann-style) bidirectional link generation for org-roam. This package uses the folgezettel index to determine parent-child relationships and automatically creates directional links based on those relationships. The index is placed before the note title and is compatible with the org-roam file-naming system. It is also compatible with printing the note for storage in a paper zettelkasten. Even though the zettelkasten grows organically as new notes are inserted anywhere in the knowledge graph, the index determines the linear order of the notes for storage in paper form.
Electronic zettelkastens rely on reciprocal hyperlinks to relate parent and child notes. The manual insertion of these links is error-prone and time-consuming. This package provides true automation for creating these links by leveraging the parent-child relationship defined in the indexing system. You do not need to click a button to add the links; they are added automatically, out of sight and out of mind.
The folgezettel index determines the order in which a new note is to be placed in a series of notes in a paper-based zettelkasten. The ability to print these indices with the notes provides a bridge between electronic and paper-based zettelkastens. This bridge supports hybrid, mirror, and project-based approaches. In the project-based approach, notes on paper are used during the assembly of a manuscript, where they are ordered and rearranged on a physical tabletop or corkboard.
Most electronic zettelkastens rely either on timestamps or a database ID to identify each unique note. This approach is hopeless if one wants to print out their zettels to store them in a paper-based zettelkasten. Fans of the paper-based approach may object to the printed zettels; they claim that you should write these notes by hand to better integrate the information into your memory. This may be true, but more frequent perusal of the paper zettelkasten may be compensatory and possibly more effective in the long term.
Often, there is not enough time available to rewrite all of the notes by hand. Nor is there time to convert handwritten notes into an electronic format. There is no rule against mixing handwritten and printed notes together. Spending time frequently perusing the notes is the main limiting factor to the successful use of a zettelkasten as a tool for thinking, not the format of the notes.
The inclusion of the folgezettel index in the title indicates where to store the note. Paper-based zettelkastens rely on the folgezettel index (or alternatively the Luhmann-style index or the Scott Scheper index, both of which are not computer-compatible) to specify the linear order of note storage. There is a one-to-one mapping between the zettelkasten graph and the order in which the notes are stored.
This approach supports a hybrid zettelkasten, with part electronic and part paper-based. Of course, it also supports a mirrored zettelkasten in both paper and electronic form.
You can print a note on US letter-size paper, fold it in half with the title facing outward, and store it in this zettelkasten. This folded paper correspondsroughly to A5-sized paper. Luhmann used the smaller A6-sized paper.
If the note spans multiple pages, as may be the case with a structure, keyword, or hub note, you can fold the pages in half together. You can also save paper and space by printing double-sided, resulting in a booklet with two pages per side. For example, an eight-page note would span both sides of two sheets of US letter paper. The text will be rotated by 90°, so you will need to write the index across the top of the outside side of the folded paper.
I favor this approach over the use of index cards because it provides more space and because US Letter printer paper is cheaper and more readily available. This more practical approach reduces the friction of adding new notes to your paper-based zettelkasten.
Obsidian offers a fantastic, infinite canvas for displaying and organizing notes in all kinds of configurations. The ability to print out the notes opens up the opportunity to work with paper versions on a large tabletop or a corkboard. Sometimes, changing the context from electronic to physical can stimulate the mind. This alternative physical approach to arranging notes is useful when assembling a manuscript. You can use the canvas to combine all the notes you want to print. This could be useful for one-off tasks, such as assembling a manuscript, where you may discard the paper notes when you are done.
Some hierarchical order is necessary to ease navigation of the zettelkasten because keyword searching does not guarantee that you will retrieve all relevant notes. Luhmann's paper version had order provided by his indexing system. Disorder was provided by cross-links between notes. According to Luhmann, you need both order and disorder. The optimal mix of order and disorder will probably vary with project and user. Luhmann's indexing system started with root nodes numbered with integers, separated by backslashes from the indices of the descendant nodes.
When starting a zetterlkasten, I recommend spending an hour or more identifying a list of areas of knowledge you want to store notes on in the zettelkasten. These areas can serve as your root nodes. You can expand this list as your interests evolve.
- Overview
- Features
- Installation
- Quick Start
- Folgezettel Address Format
- Usage
- Configuration
- Commands
- Testing
- Info Documentation
- Troubleshooting
- Contributing
- License
Autoslip-roam brings the folgezettel numbering system to org-roam. When you create a note with a folgezettel address in its title (e.g., "1.2a My Topic"), the package automatically:
- Identifies the parent note based on the address hierarchy.
- Inserts a backlink to the parent in the new note.
- Inserts a forward link to the child in the parent note.
This creates a navigable hierarchy of interconnected notes.
- Automatic Bidirectional Linking - Parent and child notes are linked automatically.
- Narrowing of Search Results in Minibuffer - The index narrows the search results in the minibuffer as it is extended, thereby facilitating movement down the chain of thought.
- Address Validation - Prevents invalid folgezettel addresses.
- Duplicate Detection - Warns if an address is already in use.
- Smart Suggestions - Suggests the next available child address.
- Cross-Reference Links - Automatic reciprocal links when inserting manual links.
- Database Sync - Immediate visibility in org-roam graph and queries.
- Extended Alphabet - Supports aa, ab, ..., zz, aaa, ... after z.
- Navigation Commands - Jump to parent, list children, or show a tree view of the vault.
- Chain of Thought - Show every ancestor from the current note to its root in a dedicated buffer, and optionally insert that chain into the current note.
- Cross-linked Chains of Thought - For every note linked under Cross References, show its own ancestor chain so the cross-link is understood in context rather than by name alone.
- Reparenting - Move a note (or a whole subtree) to a new folgezettel address, with links and file names kept in sync.
- Quiet Link Storage - Optional property-drawer mode that keeps parent and child references out of the visible body of the note, reducing merge-conflict surface.
- Emacs 27.1 or later
- org-roam 2.0 or later
Once available on MELPA:
M-x package-install RET autoslip-roam RET- Clone the repository:
git clone https://github.com/MooersLab/autoslip-roam.git
cd autoslip-roam- Add to your Emacs configuration:
(add-to-list 'load-path "/path/to/autoslip-roam")
(require 'autoslip-roam)
(autoslip-roam-mode 1)(use-package autoslip-roam
:after org-roam
:load-path "/path/to/autoslip-roam"
:config
(autoslip-roam-mode 1)
:bind
(:map org-mode-map
("C-c n c" . autoslip-roam-insert-next-child)
("C-c n p" . autoslip-roam-add-backlink-to-parent)))(require 'autoslip-roam)
(autoslip-roam-mode 1)M-x org-roam-node-find RET
Title: 1. Introduction to My Topic
Root note titles carry a trailing period after the integer (e.g., 1.), matching the style used in numbered outlines like 1. Crystallography.
Legacy titles without the trailing period (e.g., 1 Introduction) are still recognized and are treated as equivalent to the canonical form.
With the root note open:
M-x autoslip-roam-insert-next-child RET
The package suggests 1.1 as the first child. Enter a title when prompted.
Child note (1.1 First Subtopic):
#+title: 1.1 First Subtopic
** Parent Note
[[id:abc123][Parent note]]
Your content here...
Parent note (1. Introduction to My Topic):
#+title: 1. Introduction to My Topic
Your content here...
** Child Notes
[[id:def456][1.1 First Subtopic]]
| Address | Description |
|---|---|
1. |
Root note |
1.2 |
Second subtopic of note 1 |
1.2a |
First letter branch of 1.2 |
1.2aa |
27th child of 1.2 (after z) |
1.2a3 |
Third numeric child of 1.2a |
1.2a3b |
Second letter child of 1.2a3 |
- Start with a number - All addresses begin with a root number
- Root notes carry a trailing period - Canonical root form is
N.(e.g.,1.). Legacy bare-integer titles (1 Crystallography) are still accepted and treated as equivalent. - Single period only - Only one
.allowed (the one separating the root from the first subtopic, or the one marking a root note itself) - Alternation - Numbers and letters must alternate after the period
- Lowercase only - Use lowercase letters (a-z)
- Extended alphabet - After z comes aa, ab, ..., zz, aaa, ...
| Child | Parent | Rule |
|---|---|---|
1.2 |
1. |
Remove .number, leave the root period |
1.2a |
1.2 |
Remove letters |
1.2aa |
1.2 |
Remove ALL trailing letters |
1.2a3 |
1.2a |
Remove trailing numbers |
The recommended workflow:
M-x autoslip-roam-insert-next-child
This command:
- Extracts the current note's folgezettel address
- Suggests the next available child address
- Creates the note with automatic bidirectional links
For notes created without automatic linking:
M-x autoslip-roam-add-backlink-to-parent
Check if an address is valid:
M-x autoslip-roam-report-validation-errors RET 1.2a RET
Debug parent-finding problems:
M-x autoslip-roam-diagnose-address RET 1.2 RET
All options are in the autoslip-roam customization group:
M-x customize-group RET autoslip-roam RET
;; Heading for parent links in child notes
(setq autoslip-roam-backlink-heading "Parent Note")
;; Heading for child links in parent notes
(setq autoslip-roam-forward-link-heading "Child Notes")
;; Enable automatic cross-reference links
(setq autoslip-roam-auto-crosslink t)
;; Sync database before queries (recommended)
(setq autoslip-roam-sync-db-before-queries t)
;; Where to store parent and child references.
;; 'headings (default) writes visible "Parent Note" and "Child Notes" sections.
;; 'properties writes to a top-level property drawer (quiet mode).
(setq autoslip-roam-link-storage 'headings)
;; Property keys used when link storage is 'properties.
(setq autoslip-roam-parent-property "FZ_PARENT")
(setq autoslip-roam-children-property "FZ_CHILDREN")
;; Rename a note's file on disk when its folgezettel address changes.
(setq autoslip-roam-rename-files-on-reparent t)
;; Heading used above chain-of-thought outlines inserted into notes.
(setq autoslip-roam-chain-heading "Chain of Thought")
;; Heading used above cross-linked chains-of-thought inserted into notes.
(setq autoslip-roam-chain-crosslink-heading "Cross-linked Chains of Thought")The package supports two modes for recording parent and child references,
selectable with autoslip-roam-link-storage:
'headings(default) writes visible** Parent Noteand** Child Notessections in the note body. This is the original behavior and is useful when you want to see the links inline while reading or exporting.'propertieswrites the references to a top-level property drawer (:FZ_PARENT:and:FZ_CHILDREN:) and leaves the body untouched. This reduces merge-conflict surface on shared vaults, keeps exports clean, and still records the relationships for programmatic traversal.
Both modes are compatible with goto-parent, list-children, show-tree,
and the reparent commands.
(use-package autoslip-roam
:after org-roam
:config
(setq autoslip-roam-parent-link-description "↑ Parent"
autoslip-roam-backlink-heading "Parent Note"
autoslip-roam-forward-link-heading "Child Notes"
autoslip-roam-crosslink-heading "Cross References"
autoslip-roam-auto-crosslink t
autoslip-roam-sync-db-before-queries t)
(autoslip-roam-mode 1)
:bind
(:map org-mode-map
("C-c n c" . autoslip-roam-insert-next-child)
("C-c n p" . autoslip-roam-add-backlink-to-parent)
("C-c n v" . autoslip-roam-report-validation-errors)))| Command | Description |
|---|---|
autoslip-roam-mode |
Toggle the minor mode |
autoslip-roam-insert-next-child |
Create a new child note |
autoslip-roam-add-backlink-to-parent |
Add bidirectional links manually |
autoslip-roam-report-validation-errors |
Validate an address |
autoslip-roam-diagnose-address |
Debug address lookup |
autoslip-roam-check-duplicate-index |
Check for duplicates |
autoslip-roam-goto-parent |
Visit the parent of the current note |
autoslip-roam-list-children |
Pick a direct child of the current note and visit it |
autoslip-roam-show-tree |
Display the whole vault as a folgezettel-ordered tree |
autoslip-roam-show-chain-of-thought |
Open a buffer that lists every ancestor of the current note |
autoslip-roam-insert-chain-of-thought |
Insert the ancestor chain as an org outline at point |
autoslip-roam-show-crosslinked-chains |
Open a buffer grouping each cross-linked note's own ancestor chain |
autoslip-roam-reparent |
Move the current note to a new folgezettel address |
autoslip-roam-reparent-subtree |
Move the current note and all descendants to a new address |
autoslip-roam-goto-parent walks from the current note to the node
whose folgezettel address is one step shallower and opens it. This function errors
helpfully when the current note is a root.
autoslip-roam-list-children offers a completion list of every direct
child of the current note, ordered by folgezettel, and visits the choice.
autoslip-roam-show-tree opens a *Autoslip Tree* buffer with one
line per node, indented by depth, sorted in canonical folgezettel order.
Press RET on any line to visit that note, n and p to move between
lines, g to refresh, and q to close the buffer.
autoslip-roam-show-chain-of-thought walks from the current note up
through every ancestor to the root of its folgezettel tree and displays the
chain in a *Autoslip Chain of Thought* buffer. Unlike the tree view,
the chain shows only the connecting notes (the ancestors), not the rest of
the subtree. Press RET on any line to visit the corresponding note, i
to insert the chain into the note that you opened the buffer from, g to
refresh, and q to close.
autoslip-roam-insert-chain-of-thought writes the chain directly at
point in the current note as an indented org bullet list, each item a
link to the corresponding org-roam node. The heading text above the list
comes from autoslip-roam-chain-heading (set to nil for no heading).
autoslip-roam-show-crosslinked-chains is for the moment when you
have notes cross-linked to the current note, and you want to understand the
context each of those cross-linked notes sits in, not just its title. The
command reads every [[id:...]] under the Cross References heading of the
current note, looks up each target note, and renders one ancestor chain
per cross-link in the *Autoslip Cross-linked Chains* buffer. Press
RET to visit a node, i to insert all the chains into the current note,
g to refresh, and q to close.
autoslip-roam-reparent changes the folgezettel address of the
current note:
- It validates the new address and refuses duplicates.
- It rewrites the title keyword to start with the new address.
- It removes the old parent's forward link and the old
Parent Notesection (or the equivalent property entries), then writes the new ones. - When
autoslip-roam-rename-files-on-reparentis non-nil, it also renames the file on disk so the filename prefix matches the new address.
autoslip-roam-reparent-subtree does the same for the current note
and recursively applies the shift to every descendant, so moving 1.2a to
1.4a also moves 1.2a3, 1.2a3b, and so on. The command pre-checks for
collisions with unrelated notes before making any changes.
(with-eval-after-load 'org-roam
(define-key org-mode-map (kbd "C-c n c") #'autoslip-roam-insert-next-child)
(define-key org-mode-map (kbd "C-c n p") #'autoslip-roam-add-backlink-to-parent)
(define-key org-mode-map (kbd "C-c n u") #'autoslip-roam-goto-parent)
(define-key org-mode-map (kbd "C-c n d") #'autoslip-roam-list-children)
(define-key org-mode-map (kbd "C-c n t") #'autoslip-roam-show-tree)
(define-key org-mode-map (kbd "C-c n h") #'autoslip-roam-show-chain-of-thought)
(define-key org-mode-map (kbd "C-c n H") #'autoslip-roam-insert-chain-of-thought)
(define-key org-mode-map (kbd "C-c n x") #'autoslip-roam-show-crosslinked-chains)
(define-key org-mode-map (kbd "C-c n r") #'autoslip-roam-reparent)
(define-key org-mode-map (kbd "C-c n R") #'autoslip-roam-reparent-subtree))The package includes 100 comprehensive tests.
# Run all tests
make test
# Run unit tests only
make test-unit
# Run integration tests
make test-integration
# Run with verbose output
make test-verbose
# Run a specific test
make test-specific TEST=test-parse-address-single-number;; Load and run all tests
(load-file "test-autoslip-roam.el")
M-x ert RET t RET
;; Run specific category
M-x ert RET ^test-parse RET| Category | Tests | Description |
|---|---|---|
| Parsing | 7 | Address parsing |
| Extraction | 5 | Title extraction |
| Letter sequences | 5 | Alphabet incrementing |
| Validation | 13 | Address validation |
| Suggestions | 8 | Child suggestions |
| Parent lookup | 6 | Find-parent and index-exists helpers |
| Link insertion | 6 | File operations |
| Integration | 2 | Full workflows |
| Edge cases | 5 | Boundary conditions |
| Regression | 5 | Fixed bugs |
| Performance | 2 | Speed tests |
| Address helpers | 5 | Depth, tokens, canonical ordering |
| Children lookup | 2 | Direct-children discovery |
| Rename helpers | 4 | Address-driven file renaming |
| Link storage | 3 | Property-drawer storage mode |
| Reparent helpers | 3 | Title, backlink, and forward-link rewrites |
| Tree view | 2 | show-tree buffer contents |
| Navigation | 3 | goto-parent and list-children commands |
| Chain of thought | 9 | Ancestor walk, org-list rendering, show and insert |
| Cross-linked chains | 5 | Cross-link id extraction and show-crosslinked-chains |
The package includes a comprehensive Info manual accessible within Emacs.
make infoSystem-wide (requires sudo):
sudo make install-infoUser-local (no sudo):
make install-info-userThen add to your init.el:
(add-to-list 'Info-additional-directory-list "~/.emacs.d/info")After installation:
C-h i d m Autoslip Roam RET
Or:
M-x info RET m Autoslip Roam RET
- Verify the parent exists with the correct folgezettel in title
- Run
M-x org-roam-db-syncto update the database - Use
M-x autoslip-roam-diagnose-addressto debug
Ensure database sync is enabled:
(setq autoslip-roam-sync-db-before-queries t)Common issues:
- Multiple periods (
1.2.3) - only one allowed - Uppercase letters (
1.2A) - use lowercase - Special characters - only digits, letters, one period
Verify the mode is active:
(autoslip-roam-mode 1)Check the hook is registered:
(member 'autoslip-roam--process-new-node
org-roam-capture-new-node-hook)Contributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Run tests (
make test) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
git clone https://github.com/MooersLab/autoslip-roam.git
cd autoslip-roam
make check-deps # Verify dependencies
make test # Run test suite
make check # Run all quality checksThis project is licensed under the GNU General Public License v3.0 - see the LICENSE file for details.
- Inspiration from the annual Emacsconf and various Emacs Meetup groups, especially the Austin Emacs Meetup and the Berlin Emacs Meetup.
The package is actively maintained and used in the author's daily workflow.
Core features (automatic bidirectional linking, validation, child-address
suggestion, cross-reference links, navigation, tree view, chain of thought,
cross-linked chains of thought, reparenting, and the quiet property-drawer
storage mode) are covered by 100 ERT tests that run under make test.
Feedback and patches are welcome via the issue tracker.
If you already have a set of topic nodes, perhaps at one level below a master node, you can use them as the root nodes. You will need to number these. I maintain a file called 00. Index of Indices that contains the list of numbered root nodes.
If you have a large graph that was developed from the bottom up, you could ask AI agents to identify candidate root nodes. You could then edit and number these and then ask the agents to apply the numbering scheme described above while honoring the existing links.
My approach is to export the note to PDF, then print it. I added a LaTeX preamble drawer to my template for notes to provide a compact format to save paper.
:PREAMBLE:
#+Options: toc:nil \n:nil num:nil
#+STARTUP: noindent overview
#+LaTeX_CLASS: article
#+LaTeX_CLASS_OPTIONS: [11pt,letterpaper]
#+LaTeX_HEADER:\usepackage[letterpaper, total={7in, 9in}]{geometry} % good with line numbers
#+LATEX_HEADER:\usepackage{parskip} % add a blank line between paragraphs upon export to PDF.
#+LATEX_HEADER:\usepackage[all=subtle,title=normal]{savetrees}
:END:
| Version | Changes | Date |
|---|---|---|
| 3.0.0 | Renamed the package from folgezettel-org-roam to autoslip-roam. Every public symbol, file, buffer name, mode name, and customization group was renamed in step. Users upgrading from 2.x will need to update their require form, their customizations, and any key bindings to the new prefix. Root addresses now canonicalize to N. with a trailing period (e.g., 1.) to match numbered-outline conventions like 1. Crystallography. Legacy bare-integer titles (1 Crystallography) are still recognized and treated as equivalent. |
2026-04-23 |
| 2.5.0 | Added show-chain-of-thought, insert-chain-of-thought, and show-crosslinked-chains commands. Expanded the test suite to 100 tests. |
2026-04-23 |
| 2.4.0 | Added goto-parent, list-children, show-tree, reparent, and reparent-subtree commands. Added a quiet property-drawer link-storage mode. Expanded the test suite to 86 tests. |
2026-04-22 |
| 0.1 | Initial commit. Extensive edits of the README.md. | 2026-01-31 |
- NIH: R01 CA242845, R01 AI088011
- NIH: P30 CA225520 (PI: R. Mannel); P30GM145423 (PI: A. West)
Questions? Open an issue on GitHub.