diff --git a/zddc/cmd/zddc-server/main_test.go b/zddc/cmd/zddc-server/main_test.go
index 9a42a54..9a1c80d 100644
--- a/zddc/cmd/zddc-server/main_test.go
+++ b/zddc/cmd/zddc-server/main_test.go
@@ -138,14 +138,14 @@ func TestDispatchAppsResolution(t *testing.T) {
"archive": upstream.URL + "/archive_stable.html",
"transmittal": upstream.URL + "/transmittal_stable.html",
"classifier": upstream.URL + "/classifier_stable.html",
- "mdedit": upstream.URL + "/mdedit_stable.html",
"landing": upstream.URL + "/landing_stable.html",
+ "browse": upstream.URL + "/browse_stable.html",
},
}
if err := zddc.WriteFile(root, zf); err != nil {
t.Fatalf("WriteFile: %v", err)
}
- // Create folder convention dirs so classifier/mdedit/transmittal
+ // Create folder convention dirs so classifier/browse/transmittal
// availability rules pass for the test paths used below.
mustMkdir(t, filepath.Join(root, "Project-A", "Working"))
@@ -380,10 +380,10 @@ func TestDispatchArchiveRedirect(t *testing.T) {
func TestDispatchSlashRouting(t *testing.T) {
// Convention:
/ → browse (directory view, via DirTool which
// defaults to browse); → the directory's default_tool ("the
- // specialized app": mdedit under working/, transmittal under
- // staging/, archive under archive/, tables under archive//mdl).
- // Without a default_tool, no-slash falls through to the trailing-
- // slash redirect (302).
+ // specialized app": browse under working/+reviewing/, transmittal
+ // under staging/, archive under archive/, tables under
+ // archive//mdl). Without a default_tool, no-slash falls
+ // through to the trailing-slash redirect (302).
//
// The only trailing-slash redirect is for a directory that is the
// rows-dir of a table declared via a REAL on-disk parent .zddc
@@ -429,7 +429,7 @@ func TestDispatchSlashRouting(t *testing.T) {
wantNoRedirect bool
wantLoc string // checked when wantStatus is a redirect
}{
- {"working no-slash → mdedit", "/Project/working", http.StatusOK, true, ""},
+ {"working no-slash → browse", "/Project/working", http.StatusOK, true, ""},
{"working slash → browse", "/Project/working/", http.StatusOK, true, ""},
{"staging no-slash → transmittal", "/Project/staging", http.StatusOK, true, ""},
{"staging slash → browse", "/Project/staging/", http.StatusOK, true, ""},
@@ -525,21 +525,21 @@ func TestDispatchEmptyCanonicalProjectFolders(t *testing.T) {
}
// No-trailing-slash form on a canonical folder → default app
- // (mdedit for working/, transmittal for staging/, archive for
- // archive/). Mirror of the existing "no-slash → default app"
- // behavior at the IsDir branch, extended to cover the case where
- // the folder doesn't exist on disk yet.
+ // (browse for working/+reviewing/, transmittal for staging/,
+ // archive for archive/). Mirror of the existing "no-slash →
+ // default app" behavior at the IsDir branch, extended to cover
+ // the case where the folder doesn't exist on disk yet.
noSlashDefaultApp := []struct {
stage string
expect string // substring that should appear in the response body
}{
- {"working", "ZDDC Markdown"},
+ {"working", "ZDDC Browse"},
{"staging", "ZDDC Transmittal"},
{"archive", "ZDDC Archive"},
- // reviewing/ also routes to mdedit; the polyfill follows the
- // virtual aggregator's listing into canonical archive/+staging
- // paths from there.
- {"reviewing", "ZDDC Markdown"},
+ // reviewing/ also routes to browse (markdown editor lives
+ // inside it now); the polyfill follows the virtual aggregator's
+ // listing into canonical archive/+staging paths from there.
+ {"reviewing", "ZDDC Browse"},
}
for _, tc := range noSlashDefaultApp {
t.Run("no-slash/"+tc.stage+" → default app", func(t *testing.T) {
diff --git a/zddc/internal/apps/availability_test.go b/zddc/internal/apps/availability_test.go
index 19788bb..5f055cd 100644
--- a/zddc/internal/apps/availability_test.go
+++ b/zddc/internal/apps/availability_test.go
@@ -35,11 +35,12 @@ func TestAppAvailableAt(t *testing.T) {
{root + "/Project-A/archive/ACME/mdl", "classifier", false},
{root + "/Project-A/some-other-folder", "classifier", false},
- // mdedit: working/ only (review responses live in working//)
- {root + "/Project-A/working", "mdedit", true},
- {root + "/Project-A/working/sub", "mdedit", true},
- {root + "/Project-A/staging", "mdedit", false},
- {root + "/Project-A/archive/ACME/incoming", "mdedit", false},
+ // browse: universal — every directory has browse available
+ // (it's in the embedded-defaults baseline available_tools).
+ {root + "/Project-A/working", "browse", true},
+ {root + "/Project-A/working/sub", "browse", true},
+ {root + "/Project-A/staging", "browse", true},
+ {root + "/Project-A/archive/ACME/incoming", "browse", true},
// transmittal: staging/ only
{root + "/Project-A/staging", "transmittal", true},
@@ -48,8 +49,6 @@ func TestAppAvailableAt(t *testing.T) {
{root + "/Project-A/archive/ACME/issued", "transmittal", false},
// case-fold: any case of canonical names matches
- {root + "/Project-A/Working", "mdedit", true},
- {root + "/Project-A/WORKING", "mdedit", true},
{root + "/Project-A/Staging", "transmittal", true},
{root + "/Project-A/STAGING", "transmittal", true},
{root + "/Project-A/archive/ACME/Incoming", "classifier", true},
@@ -81,9 +80,9 @@ func TestDefaultAppAt(t *testing.T) {
// no-slash falls through to the redirect.
{root + "/Project-A", ""},
// Canonical project-root folders.
- {root + "/Project-A/working", "mdedit"},
- {root + "/Project-A/working/alice@example.com", "mdedit"},
- {root + "/Project-A/working/2026-06-15_x (DFT) - y", "mdedit"},
+ {root + "/Project-A/working", "browse"},
+ {root + "/Project-A/working/alice@example.com", "browse"},
+ {root + "/Project-A/working/2026-06-15_x (DFT) - y", "browse"},
{root + "/Project-A/staging", "transmittal"},
{root + "/Project-A/staging/2026-06-15_x (DFT) - y", "transmittal"},
// archive: at the archive root, party folders default to archive.
@@ -98,15 +97,16 @@ func TestDefaultAppAt(t *testing.T) {
// mdl wins over the broader archive rule.
{root + "/Project-A/archive/Acme/mdl", "tables"},
{root + "/Project-A/archive/Acme/mdl/anything-deeper", "tables"},
- // reviewing/ is virtual but mdedit is wired as the default
- // tool; the polyfill follows the listing's canonical URLs
- // into archive/ and staging/ for the actual files.
- {root + "/Project-A/reviewing", "mdedit"},
- {root + "/Project-A/reviewing/123-EM-SUB-0001", "mdedit"},
+ // reviewing/ is virtual; browse hosts the markdown editor that
+ // renders responses (the polyfill follows the listing's
+ // canonical URLs into archive/ and staging/ for the actual
+ // files).
+ {root + "/Project-A/reviewing", "browse"},
+ {root + "/Project-A/reviewing/123-EM-SUB-0001", "browse"},
// Random non-canonical folder names → no default.
{root + "/Project-A/scratch", ""},
// Case-fold on canonical names.
- {root + "/Project-A/Working", "mdedit"},
+ {root + "/Project-A/Working", "browse"},
{root + "/Project-A/STAGING", "transmittal"},
{root + "/Project-A/Archive/Acme/MDL", "tables"},
}
diff --git a/zddc/internal/zddc/defaults.zddc.yaml b/zddc/internal/zddc/defaults.zddc.yaml
index 8690aa6..462a8a4 100644
--- a/zddc/internal/zddc/defaults.zddc.yaml
+++ b/zddc/internal/zddc/defaults.zddc.yaml
@@ -53,11 +53,12 @@ roles:
members: []
# Universal tool baseline. archive (record browser), browse (file
-# tree), and landing (project picker) work everywhere. Each canonical
-# folder below adds its own context-specific tools (mdedit in
-# working/, transmittal in staging/, etc.). The cascade unions
-# available_tools across all levels — leaf restrictions don't drop
-# ancestor entries — so this baseline propagates to every descendant.
+# tree, hosts the in-place markdown editor), and landing (project
+# picker) work everywhere. Each canonical folder below adds its own
+# context-specific tools (transmittal in staging/, etc.). The cascade
+# unions available_tools across all levels — leaf restrictions don't
+# drop ancestor entries — so this baseline propagates to every
+# descendant.
available_tools: [archive, browse, landing]
# ── The slash / no-slash routing convention ────────────────────────────────
@@ -71,9 +72,9 @@ available_tools: [archive, browse, landing]
# default; you rarely set it.
# (no slash) → `default_tool` — the "specialized
# app" for this folder (e.g. archive,
-# transmittal, mdedit, tables). If a
-# folder declares no default_tool, the
-# no-slash form just 302s to the slash
+# transmittal, tables). If a folder
+# declares no default_tool, the no-
+# slash form just 302s to the slash
# form, so you land on `dir_tool`.
#
# JSON listing requests are unaffected by either key — they always get
@@ -81,7 +82,7 @@ available_tools: [archive, browse, landing]
# can enumerate entries no matter what dir_tool/default_tool are.
#
# Both keys cascade leaf→root: a parent's default_tool applies to
-# descendants unless a deeper level overrides it (mdedit set on
+# descendants unless a deeper level overrides it (browse set on
# working/ reaches working/alice/notes/ for free). The keys below set
# default_tool on the canonical folders; dir_tool is left unset
# everywhere, so the slash form is always `browse`.
@@ -201,8 +202,8 @@ paths:
default_tool: archive
worm: [document_controller]
working:
- default_tool: mdedit
- available_tools: [mdedit, classifier]
+ default_tool: browse
+ available_tools: [browse, classifier]
# working/ auto-owns the first creator + the per-user homes
# below.
auto_own: true
@@ -215,8 +216,8 @@ paths:
admins: [document_controller]
paths:
"*": # per-user home dir
- default_tool: mdedit
- available_tools: [mdedit, classifier]
+ default_tool: browse
+ available_tools: [browse, classifier]
auto_own: true
# Per-user home is private by default: the generated
# auto-own .zddc carries inherit:false so ancestor ACL
@@ -233,8 +234,8 @@ paths:
# rationale as working/.
admins: [document_controller]
reviewing:
- default_tool: mdedit
- available_tools: [mdedit]
+ default_tool: browse
+ available_tools: [browse]
# reviewing/ is purely virtual — the aggregator handler
# synthesises listings from received/ ↔ staging/ ↔ issued/.
virtual: true
diff --git a/zddc/internal/zddc/lookups_test.go b/zddc/internal/zddc/lookups_test.go
index 71a7d83..c2770e0 100644
--- a/zddc/internal/zddc/lookups_test.go
+++ b/zddc/internal/zddc/lookups_test.go
@@ -21,10 +21,10 @@ func TestDefaultToolAt_FromEmbeddedConvention(t *testing.T) {
{filepath.Join(root, "Project-X", "archive", "Acme", "incoming"), "classifier"},
{filepath.Join(root, "Project-X", "archive", "Acme", "received"), "archive"},
{filepath.Join(root, "Project-X", "archive", "Acme", "issued"), "archive"},
- {filepath.Join(root, "Project-X", "working"), "mdedit"},
- {filepath.Join(root, "Project-X", "working", "alice@example.com"), "mdedit"},
+ {filepath.Join(root, "Project-X", "working"), "browse"},
+ {filepath.Join(root, "Project-X", "working", "alice@example.com"), "browse"},
{filepath.Join(root, "Project-X", "staging"), "transmittal"},
- {filepath.Join(root, "Project-X", "reviewing"), "mdedit"},
+ {filepath.Join(root, "Project-X", "reviewing"), "browse"},
}
for _, tc := range cases {
got := DefaultToolAt(root, tc.path)
@@ -177,7 +177,7 @@ func TestOperatorOverride_DefaultsAreSurfaceable(t *testing.T) {
t.Fatal(err)
}
// Operator declares that Special/working uses classifier
- // instead of the embedded-default mdedit.
+ // instead of the embedded-default browse.
writeZddc(t, filepath.Join(root, "Special", "working"),
"default_tool: classifier\n")
@@ -185,7 +185,7 @@ func TestOperatorOverride_DefaultsAreSurfaceable(t *testing.T) {
t.Errorf("operator override should set default_tool=classifier, got %q", got)
}
// Default still applies at other projects.
- if got := DefaultToolAt(root, filepath.Join(root, "Project-Y", "working")); got != "mdedit" {
+ if got := DefaultToolAt(root, filepath.Join(root, "Project-Y", "working")); got != "browse" {
t.Errorf("default convention should hold at unchanged paths, got %q", got)
}
}
@@ -193,14 +193,14 @@ func TestOperatorOverride_DefaultsAreSurfaceable(t *testing.T) {
// TestDefaultToolAt_PropagatesToDescendants — once an ancestor sets
// default_tool, descendants inherit it unless they override. So a
// path under working/ that isn't explicitly declared in paths: still
-// gets mdedit as its default tool.
+// gets browse as its default tool.
func TestDefaultToolAt_PropagatesToDescendants(t *testing.T) {
resetCache()
root := t.TempDir()
// Deep path under working/ — not explicitly mentioned in paths:.
deep := filepath.Join(root, "Project-X", "working", "alice@example.com", "notes", "sub", "deep")
- if got := DefaultToolAt(root, deep); got != "mdedit" {
- t.Errorf("DefaultToolAt(%q) = %q, want mdedit (cascade propagation)",
+ if got := DefaultToolAt(root, deep); got != "browse" {
+ t.Errorf("DefaultToolAt(%q) = %q, want browse (cascade propagation)",
deep[len(root):], got)
}
}