The shared/nav.js stage strip previously hardcoded four stages
(archive/working/staging/reviewing) with their labels and target
URLs baked into the file. Operators couldn't add a fifth stage or
rename "Working" to "In-Progress" without forking shared code.
Now cascade-driven end-to-end:
Server-side:
listing.FileInfo gains a Declared bool field. fs.ListDirectory
stamps Declared=true on every entry whose name matches the
cascade's ChildrenDeclaredAt(parent) — both real on-disk dirs
and virtual canonical injections. Bugfix in the same patch:
virtualCanonicalFolders was passing the relative dirPath to
ChildrenDeclaredAt (which expects absolute); now passes absDir.
Client-side:
shared/nav.js fetches the project root's JSON listing on
DOMContentLoaded, filters to declared+is_dir entries, sorts by
canonical workflow order (archive → working → staging →
reviewing, then any extras alphabetically), and renders the
strip. Labels read e.display_name → falls back to titleCase(name).
Hardcoded FALLBACK_STAGES kicks in only on fetch failure
(offline / file:// / non-zddc-server backend). Rendered
immediately so the strip appears without flicker, then the
cascade-fetched list replaces it once available.
Effect:
Project-3 (which has display: { archive: "Records",
working: "In-Progress", ... } in its .zddc) now shows
"Records · In-Progress · Outbox · Pending Responses" in every
tool's strip. Project-1 still shows "Archive · Working ·
Staging · Reviewing". No code change to render either; the
cascade decides.
Tests:
- tests/nav.spec.js relies on the mock server returning HTML at
every URL, so the fetch fails over to fallback stages — the
test renders the same Archive/Working/Staging/Reviewing labels
it always did, with no test changes needed.
- All 248 Playwright + all Go tests green.
Remaining client-side hardcode: archive/js/source.js +
archive/js/app.js's mode detection. Phase 4d.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>