From 6310afa922929d762dd5fb656e2f6a5900063393 Mon Sep 17 00:00:00 2001 From: ZDDC Date: Mon, 11 May 2026 16:01:43 -0500 Subject: [PATCH] chore(zddc): remove dead canonical-folder predicates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 3 retired these symbols by migrating their consumers to the .zddc cascade lookups. Removing them now that nothing references them: - var zddc.ProjectRootFolders - var zddc.PartyFolders - var zddc.AutoOwnCanonicalNames - var zddc.VirtualOnlyCanonicalNames - func zddc.IsProjectRootFolder - func zddc.IsArchivePartyFolder - func zddc.IsArchivePartyMdlDir - func handler.isArchivePartyDir The canonical convention is expressed in defaults.zddc.yaml and consulted via lookups.go's DefaultToolAt / AutoOwnAt / VirtualAt / IsDeclaredPath / ChildrenDeclaredAt / AvailableToolsAt / IsToolAvailableAt. Operators override per-directory via on-disk .zddc files; the embedded layer is the documented baseline. Test removals: - TestCanonicalLists (lists no longer exist) - TestIsProjectRootFolder (function no longer exists) Equivalent coverage lives in lookups_test.go's TestDefaultToolAt_FromEmbeddedConvention, TestIsDeclaredPath_FromEmbeddedConvention, etc. — which assert the convention via the cascade's actual lookup path rather than the predicates' return values. handler.isAtArchivePartyMdlDir is RETAINED — it's still actively consumed by RecognizeTableRequest's default-MDL fallback in table.html URL resolution. That's a tighter file-path predicate than the cascade walker would naturally express; can revisit if it ever needs to become configurable. Co-Authored-By: Claude Opus 4.7 (1M context) --- zddc/internal/handler/tablehandler.go | 18 ---- zddc/internal/handler/tables.html | 2 +- zddc/internal/zddc/lookups.go | 8 -- zddc/internal/zddc/special.go | 134 ++------------------------ zddc/internal/zddc/special_test.go | 61 +----------- 5 files changed, 12 insertions(+), 211 deletions(-) diff --git a/zddc/internal/handler/tablehandler.go b/zddc/internal/handler/tablehandler.go index c85c77e..3bd94e5 100644 --- a/zddc/internal/handler/tablehandler.go +++ b/zddc/internal/handler/tablehandler.go @@ -260,24 +260,6 @@ func isNotExistError(err error) bool { return err != nil && strings.Contains(err.Error(), "no such file or directory") } -// isArchivePartyDir reports whether dirAbs is a /archive// -// directory under fsRoot, with archive case-folded. -func isArchivePartyDir(fsRoot, dirAbs string) bool { - rel, err := filepath.Rel(fsRoot, dirAbs) - if err != nil { - return false - } - rel = filepath.ToSlash(rel) - if strings.HasPrefix(rel, "../") || rel == ".." || rel == "." { - return false - } - parts := strings.Split(rel, "/") - if len(parts) != 3 { - return false - } - return strings.EqualFold(parts[1], "archive") -} - // ServeTable serves the static tables.html bytes for a recognized // request. ACL gate is the read action at the request directory; on // allow, the embedded HTML is written verbatim. The client takes over diff --git a/zddc/internal/handler/tables.html b/zddc/internal/handler/tables.html index 47c5858..3ea1503 100644 --- a/zddc/internal/handler/tables.html +++ b/zddc/internal/handler/tables.html @@ -1300,7 +1300,7 @@ body.help-open .app-header {
ZDDC Table - v0.0.17-alpha · 2026-05-11 20:31:34 · 9d18047-dirty + v0.0.17-alpha · 2026-05-11 21:01:13 · 5e393cb-dirty
diff --git a/zddc/internal/zddc/lookups.go b/zddc/internal/zddc/lookups.go index 7f10336..b5d60f4 100644 --- a/zddc/internal/zddc/lookups.go +++ b/zddc/internal/zddc/lookups.go @@ -35,9 +35,6 @@ func DefaultToolAt(fsRoot, dirPath string) string { // propagate to descendants (creating working/alice/notes/sub/ does // not auto-own sub/; only the explicitly-declared per-user home is // auto-owned). -// -// Replaces AutoOwnCanonicalNames once the file API's mkdir hook is -// migrated. func AutoOwnAt(fsRoot, dirPath string) bool { chain, err := EffectivePolicy(fsRoot, dirPath) if err != nil { @@ -75,8 +72,6 @@ func AutoOwnFencedAt(fsRoot, dirPath string) bool { // purely virtual (never materialise on disk). Leaf-only: the virtual // property describes a particular path, not a subtree. A child of a // virtual directory is not automatically virtual itself. -// -// Replaces VirtualOnlyCanonicalNames once consumers are migrated. func VirtualAt(fsRoot, dirPath string) bool { chain, err := EffectivePolicy(fsRoot, dirPath) if err != nil { @@ -100,9 +95,6 @@ func VirtualAt(fsRoot, dirPath string) bool { // even if the directory doesn't exist on disk. Used by listing // fallbacks to decide whether a missing directory should return an // empty listing (treat as virtual) vs 404 (truly unknown). -// -// Replaces IsProjectRootFolder + IsArchivePartyFolder once -// consumers are migrated. func IsDeclaredPath(fsRoot, dirPath string) bool { chain, err := EffectivePolicy(fsRoot, dirPath) if err != nil { diff --git a/zddc/internal/zddc/special.go b/zddc/internal/zddc/special.go index a44c1b1..0ded0a8 100644 --- a/zddc/internal/zddc/special.go +++ b/zddc/internal/zddc/special.go @@ -6,133 +6,13 @@ import ( "strings" ) -// ProjectRootFolders are the canonical lowercase folder names that may -// appear directly under a project root. The server resolves them -// case-insensitively on disk: a manually-created Working/ is reused -// rather than shadowed by a new working/. -// -// - "archive" — formal record of issued/received transmittals, -// organised by counterparty (and ourselves) -// - "working" — user-owned drafting workspace -// - "staging" — outbound-transmittal preparation -// - "reviewing" — purely virtual cross-reference of in-progress -// review responses (never written to disk) -var ProjectRootFolders = []string{"archive", "working", "staging", "reviewing"} - -// PartyFolders are the canonical lowercase folder names that may appear -// directly under archive//, where is a counterparty or -// the self-folder (we treat ourselves like any other third party). -// -// - "mdl" — yaml-per-deliverable metadata, edited via the -// table-editor app at /mdl.table.html -// - "incoming" — that party's drop point (we QC then promote) -// - "received" — immutable record of incoming we've accepted (WORM) -// - "issued" — immutable record of what we sent (WORM) -var PartyFolders = []string{"mdl", "incoming", "received", "issued"} - -// AutoOwnCanonicalNames is the subset of canonical folder names where -// the file API's first-write hook auto-writes a creator-owned .zddc -// granting the creator rwcda. Excluded by design: -// -// - "archive": container only -// - "reviewing": purely virtual, never on disk -// - "mdl": yaml data store; ACL flows from archive//.zddc -// - "received" / "issued": WORM — auto-own would defeat the mask -var AutoOwnCanonicalNames = []string{"working", "staging", "incoming"} - -// VirtualOnlyCanonicalNames is the subset of canonical folder names -// that are never materialised on disk by the auto-create hooks. The -// server treats requests under these prefixes as virtual routes. -// -// "reviewing" stays in ProjectRootFolders so case-fold recognition and -// future tool registration work, but EnsureCanonicalAncestors skips -// MkdirAll for it. -var VirtualOnlyCanonicalNames = []string{"reviewing"} - -// IsArchivePartyFolder reports whether dirPath (relative, forward- -// slash-separated) names a canonical per-party folder at exactly -// depth 4: /archive//, where is one -// of mdl/incoming/received/issued (case-insensitive). The party -// segment is verbatim. -// -// Used by listing + dispatch fallbacks so a fresh party that hasn't -// yet had files written to each subfolder still lands on a usable -// empty browse view rather than 404. -func IsArchivePartyFolder(dirPath string) bool { - clean := strings.Trim(filepath.ToSlash(dirPath), "/") - if clean == "" { - return false - } - parts := strings.Split(clean, "/") - if len(parts) != 4 { - return false - } - if !strings.EqualFold(parts[1], "archive") { - return false - } - leaf := strings.ToLower(parts[3]) - for _, name := range PartyFolders { - if leaf == name { - return true - } - } - return false -} - -// IsArchivePartyMdlDir reports whether dirPath (relative, forward- -// slash-separated) names the default-MDL pattern at exactly depth 4: -// /archive//mdl. Match is case-insensitive on the -// "archive" and "mdl" segments; the party name is verbatim. -// -// Used by listing + dispatch fallbacks so a fresh party that hasn't -// yet had an MDL written still lands on a usable empty browse / table -// view rather than 404. The companion handler helper -// isAtArchivePartyMdlDir (in internal/handler/tablehandler.go) takes -// absolute paths; this one is the relative-path equivalent for fs. -func IsArchivePartyMdlDir(dirPath string) bool { - clean := strings.Trim(filepath.ToSlash(dirPath), "/") - if clean == "" { - return false - } - parts := strings.Split(clean, "/") - if len(parts) != 4 { - return false - } - return strings.EqualFold(parts[1], "archive") && - strings.EqualFold(parts[3], "mdl") -} - -// IsProjectRootFolder reports whether dirPath (relative to fsRoot, -// forward-slash-separated, no leading slash) names one of the canonical -// project-root folders at exactly depth 2: /. -// Match is case-insensitive against ProjectRootFolders. -// -// Used by the directory listing endpoint to materialise an empty -// listing for canonical folders that don't yet exist on disk, so a -// fresh project's nav links never land on 404. The first write under -// such a path triggers EnsureCanonicalAncestors which lazily creates -// the real on-disk folder + auto-own .zddc. -// -// Trailing slashes and accidental "./" segments are tolerated. Paths -// of any other depth (e.g. project root itself, or deeper subpaths -// like working//) return false — the fallback only applies at -// the canonical-folder boundary. -func IsProjectRootFolder(dirPath string) bool { - clean := strings.Trim(filepath.ToSlash(dirPath), "/") - if clean == "" { - return false - } - parts := strings.Split(clean, "/") - if len(parts) != 2 { - return false - } - for _, name := range ProjectRootFolders { - if strings.EqualFold(parts[1], name) { - return true - } - } - return false -} +// Phase 3 retired the hardcoded canonical-folder predicates and their +// supporting lists (ProjectRootFolders, PartyFolders, AutoOwnCanonicalNames, +// VirtualOnlyCanonicalNames, IsArchivePartyFolder, IsArchivePartyMdlDir, +// IsProjectRootFolder). The .zddc cascade is the authority now: see +// defaults.zddc.yaml for the canonical convention and lookups.go for +// the helpers consumers call (DefaultToolAt, AutoOwnAt, VirtualAt, +// IsDeclaredPath, ChildrenDeclaredAt, AvailableToolsAt). // WriteAutoOwnZddc serialises a creator-grant .zddc into dir, granting // principalEmail rwcda and recording it in CreatedBy. Used by the file diff --git a/zddc/internal/zddc/special_test.go b/zddc/internal/zddc/special_test.go index 5ff0f6f..537d913 100644 --- a/zddc/internal/zddc/special_test.go +++ b/zddc/internal/zddc/special_test.go @@ -176,60 +176,7 @@ func TestResolveCanonicalMissingParent(t *testing.T) { } } -func TestCanonicalLists(t *testing.T) { - hasAll := func(have, want []string) bool { - set := map[string]bool{} - for _, n := range have { - set[n] = true - } - for _, n := range want { - if !set[n] { - return false - } - } - return true - } - if !hasAll(ProjectRootFolders, []string{"archive", "working", "staging", "reviewing"}) { - t.Errorf("ProjectRootFolders = %v, missing entries", ProjectRootFolders) - } - if !hasAll(PartyFolders, []string{"mdl", "incoming", "received", "issued"}) { - t.Errorf("PartyFolders = %v, missing entries", PartyFolders) - } - if !hasAll(AutoOwnCanonicalNames, []string{"working", "staging", "incoming"}) { - t.Errorf("AutoOwnCanonicalNames = %v, missing entries", AutoOwnCanonicalNames) - } - if !hasAll(VirtualOnlyCanonicalNames, []string{"reviewing"}) { - t.Errorf("VirtualOnlyCanonicalNames = %v, missing entries", VirtualOnlyCanonicalNames) - } -} - -func TestIsProjectRootFolder(t *testing.T) { - cases := map[string]bool{ - // Canonical positions, lowercase. - "Proj/archive": true, - "Proj/working": true, - "Proj/staging": true, - "Proj/reviewing": true, - // Case-fold. - "Proj/Working": true, - "Proj/STAGING": true, - "Proj/Reviewing": true, - // Trailing slash tolerated (handler trims but be defensive). - "Proj/working/": true, - // Non-canonical second segment. - "Proj/random": false, - "Proj/archive2": false, - // Wrong depth — root, single segment, or deeper. - "": false, - "Proj": false, - "Proj/working/casey": false, - "Proj/archive/ACME": false, - "Proj/archive/ACME/issued": false, - } - for path, want := range cases { - got := IsProjectRootFolder(path) - if got != want { - t.Errorf("IsProjectRootFolder(%q) = %v, want %v", path, got, want) - } - } -} +// TestCanonicalLists and TestIsProjectRootFolder retired in Phase 3 — +// the canonical convention is now expressed in defaults.zddc.yaml and +// asserted by lookups_test.go (TestDefaultToolAt_FromEmbeddedConvention, +// TestIsDeclaredPath_FromEmbeddedConvention, etc.).