issues-fs.sgit.ai / model / layout

The on-disk layout

The folder hierarchy is not packaging around the data — it is part of the data model. Containment is expressed as directory nesting, which is why find answers structural questions and why a git mv is a legitimate way to restructure a graph.

The reference layout

The flagship live graph — 71 nodes, 141 link entries, 84 files — is fully hierarchical. Containment nests, all the way down.

.issues/
├── config/
│   ├── node-types.json          ← 12 node types
│   └── link-types.json          ← 10 verb/inverse pairs, with domain and range
└── issues/
    └── Project-1/
        └── issues/
            └── Phase-1/
                └── issues/
                    └── Feature-9/
                        └── issues/
                            └── Task-N/
                                └── issue.json

Maximum depth is 8 path segments, and 55 of the 71 nodes sit at that depth — this is not a shallow tree with one deep branch, it is a graph whose containment structure is genuinely five levels of hierarchy.

Two consequences worth noticing:

Three incompatible layouts are in circulation

This is a real interoperability problem and it is stated rather than smoothed over. Three layouts are in live use across the ecosystem, and the documentation describes a fourth.

LayoutWhere it is liveShape
Hierarchical issues/SGraph-AI__App__Send — the 71-node graphNested issues/<Label>/issues/…/issue.json, depth 8
Flat data/Issues-FS (24 nodes), Issues-FS__CLI (3 nodes)One flat directory, no nesting
MixedIssues-FS__Service__UI (49 nodes)44 under data/, 4 under issues/, 1 at the root
A fourthNowhereDescribed in the documentation only

This site publishes the hierarchical layout as the reference, because it is the one the largest and most complete graph uses and the one that expresses containment as containment. But the practical answer for a reader is different, and it is the one that matters: run issues-fs init and use whatever it produces. Do not hand-build a tree to match a document — including this one. Which layout is canonical is open question Q2.

Why _index.json is a cache, not a source

Some graphs carry an _index.json summarising their contents. The evidence says not to trust it.

GraphWhat the index saysWhat is on disk
Issues-FS/.issues/total_issues: 22, 6 bugs24 issues, 8 bugs
SGraph-AI__App__Send/.issues/No _index.json at any level71 nodes — and everything works
The index is stale wherever it exists, and the largest, healthiest graph in the ecosystem does not have one.

That is an argument for the design, not an embarrassment. The CLI walks the tree; it does not read an index to find nodes. So the index is a derived artefact — useful for a fast summary, never authoritative — and a stale one costs nothing because nothing depends on it. Contrast the alternative: had the index been load-bearing, the 22-versus-24 divergence would have been a bug that hid two issues from every reader. Instead it is a cache that is out of date.

The rule that follows is short: walk the tree. If you are writing a tool, do not read _index.json to enumerate nodes; glob for issue.json. If you are an agent, the same. Whether the file should exist at all is open question Q3.

Reading a graph with nothing installed

Because the layout is the model, the standard tools are a complete read-only client.

# every node in the graph
find .issues -name issue.json

# how many, by type
find .issues -name issue.json | xargs grep -h '"node_type"' | sort | uniq -c | sort -rn

# find a node by its label
grep -rl '"label": "Bug-27"' .issues

# what does Task-1 relate to
grep -A2 '"verb"' $(grep -rl '"label": "Task-1"' .issues)

# what contains this node — the path is the answer
grep -rl '"label": "Task-42"' .issues

None of that is a fallback for when the CLI is unavailable; it is the same data through a different door. It is also why the tracker survives its own tooling: a graph written by issues-fs 0.7.0 is readable by cat in ten years.

What git sees

Two properties fall out of storing one node per file, and both are reasons to prefer it to a single database file.

The cost is that a restructure is a lot of renames, and that a graph with thousands of nodes is a lot of small files. Neither has bitten yet at the sizes in use — the largest live graph is 84 files.

For an agent

Enumerate nodes by walking the tree for issue.jsonnever by reading _index.json, which is a cache and is stale wherever it exists (one live graph's index reports 22 issues where the disk has 24; the largest graph has no index at all). Containment is the directory structure, not an edge: a node's ancestors are the labelled directories above it. Type configuration lives in .issues/config/node-types.json and .issues/config/link-types.json. Three layouts are in circulation — hierarchical issues/ nesting, flat data/, and mixed — so detect the shape rather than assuming it, and if you are creating a graph, let issues-fs init decide the layout.