Summary
The dashboard file browser silently hides files nested deeper than 3 directory levels. The
GET /api/files endpoint defaults to depth=3 and the dashboard never sends a depth
parameter, so the tree is always truncated at 3.
The symptom is easy to misread. In a repo where the conventional directories are shallow
(codev/specs/*.md is depth 2) but working files are deeper, it looks like "markdown preview
only works under codev/". It isn't a preview restriction — the preview gate is correct and
path-agnostic (tower-routes.js, const isMarkdown = ext === 'md'). It is a depth cap on the
tree, so the deeper files never appear to be clicked in the first place.
Observed on @cluesmith/codev@3.3.4: a reports/weekly/draft/ directory came back with 0
children (8 .md files hidden) while codev/specs returned all 41 entries.
Root cause
dist/agent-farm/servers/tower-routes.js, handleWorkspaceFiles:
const maxDepth = parseInt(url.searchParams.get('depth') || '3', 10);
readTree returns [] once depth <= 0, so a truncated directory is indistinguishable from an
empty one in the response — the client cannot tell that anything was withheld. The depth is also
not exposed in .codev/config.json, so there is no way to raise it short of editing dist/.
Measurements
Against a live Tower, one moderately large repo, timings include JSON transfer:
| depth |
entries |
.md found |
wall time |
| 3 (current default) |
2,027 |
156 |
31 ms |
| 4 |
4,594 |
227 |
76 ms |
| 5 |
— |
— |
2,212 ms |
Depth 4 is close to free and recovers the files that actually matter. Depth 5 is already
seconds, which is not acceptable for an endpoint the dashboard polls.
Why "just extend the ignore set" is not sufficient
The natural reading of the table is that deep walks are slow because they descend into
dependency directories, and that a bigger ignore set would let the default rise. On the repo
measured, that turns out not to be the case. Breaking down the .md files reachable at depth 8:
8431 data/output <- the project's own experiment artifact directory
64 codev/protocols
50 reports/real_end_to_end_production_smoke
41 codev/specs
The overwhelming majority are in data/output — a project-specific artifact directory, not
vendor content. node_modules, dist, __pycache__ and friends are already ignored. No ignore
set shipped in the package can anticipate that a given project dumps tens of thousands of
generated markdown files under data/output, so extending the shipped list does not make a
deeper default safe in general.
This matters because it rules out the cheapest-looking fix.
Suggested fixes
Preferred — lazy-load children on expand. Have the browser request a directory's children
when the user expands it, rather than serving a fixed-depth tree up front. This removes the
depth ceiling entirely and makes cost proportional to what the user actually opens, which is
the only approach that is robust to arbitrary project layouts.
Interim — expose the depth. Two small changes that are useful even alongside the above:
- Make the depth configurable in
.codev/config.json so a project can tune it to its own
layout, and/or have the dashboard send an explicit depth.
- Mark truncation in the response, e.g.
{ type: 'directory', truncated: true, children: [] },
so a capped directory is distinguishable from an empty one. This alone would have made the
original symptom self-explanatory instead of looking like a preview bug.
Raising the shipped default from 3 to 4 is a reasonable stopgap — it is ~45 ms on the repo
measured — but per the breakdown above it should not go past 4 without the lazy-loading change.
Workaround for anyone hitting this
afx open <path>.md renders preview for any markdown file regardless of depth or location,
because the preview gate keys on the file extension only. Only the tree is affected.
Summary
The dashboard file browser silently hides files nested deeper than 3 directory levels. The
GET /api/filesendpoint defaults todepth=3and the dashboard never sends adepthparameter, so the tree is always truncated at 3.
The symptom is easy to misread. In a repo where the conventional directories are shallow
(
codev/specs/*.mdis depth 2) but working files are deeper, it looks like "markdown previewonly works under
codev/". It isn't a preview restriction — the preview gate is correct andpath-agnostic (
tower-routes.js,const isMarkdown = ext === 'md'). It is a depth cap on thetree, so the deeper files never appear to be clicked in the first place.
Observed on
@cluesmith/codev@3.3.4: areports/weekly/draft/directory came back with0children (8
.mdfiles hidden) whilecodev/specsreturned all 41 entries.Root cause
dist/agent-farm/servers/tower-routes.js,handleWorkspaceFiles:readTreereturns[]oncedepth <= 0, so a truncated directory is indistinguishable from anempty one in the response — the client cannot tell that anything was withheld. The depth is also
not exposed in
.codev/config.json, so there is no way to raise it short of editingdist/.Measurements
Against a live Tower, one moderately large repo, timings include JSON transfer:
.mdfoundDepth 4 is close to free and recovers the files that actually matter. Depth 5 is already
seconds, which is not acceptable for an endpoint the dashboard polls.
Why "just extend the ignore set" is not sufficient
The natural reading of the table is that deep walks are slow because they descend into
dependency directories, and that a bigger ignore set would let the default rise. On the repo
measured, that turns out not to be the case. Breaking down the
.mdfiles reachable at depth 8:The overwhelming majority are in
data/output— a project-specific artifact directory, notvendor content.
node_modules,dist,__pycache__and friends are already ignored. No ignoreset shipped in the package can anticipate that a given project dumps tens of thousands of
generated markdown files under
data/output, so extending the shipped list does not make adeeper default safe in general.
This matters because it rules out the cheapest-looking fix.
Suggested fixes
Preferred — lazy-load children on expand. Have the browser request a directory's children
when the user expands it, rather than serving a fixed-depth tree up front. This removes the
depth ceiling entirely and makes cost proportional to what the user actually opens, which is
the only approach that is robust to arbitrary project layouts.
Interim — expose the depth. Two small changes that are useful even alongside the above:
.codev/config.jsonso a project can tune it to its ownlayout, and/or have the dashboard send an explicit
depth.{ type: 'directory', truncated: true, children: [] },so a capped directory is distinguishable from an empty one. This alone would have made the
original symptom self-explanatory instead of looking like a preview bug.
Raising the shipped default from 3 to 4 is a reasonable stopgap — it is ~45 ms on the repo
measured — but per the breakdown above it should not go past 4 without the lazy-loading change.
Workaround for anyone hitting this
afx open <path>.mdrenders preview for any markdown file regardless of depth or location,because the preview gate keys on the file extension only. Only the tree is affected.