refactor: extract shared sidecar directory-scan skeleton into internal/sidecardir #121
No reviewers
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Cloonar/deckle!121
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "afk/114"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #114
The directory-scan contract shared by
element.LoadDir,pagetype.LoadDir, andbackendlayout.LoadDir— one<name>.yaml|.ymlper file,.yamlbeating.ymlin sorted filename order, the shared^[A-Za-z0-9_][A-Za-z0-9_-]*$base-name rule, per-fileLoadErrorisolation, missing dir = empty + no error, subdirs/other extensions ignored, results sorted by Name/File, never-nil slices — was re-implemented in all three packages. It is now defined once in the newinternal/sidecardirpackage (sidecarwas taken by the shortcode-scaffold generator).Design
sidecardir.Load[S](dir, Config[S])owns the scan skeleton. Each family supplies viaConfig: the error-message noun ("element", "page type", "backend layout"), itsDecode(the existingloadOne, minus the now-shared name-regex check), and optionally aRejecthook.Loadreturns[]Loaded[S]pairing each schema with its source filename — element's cross-schema pass (rteallowedInline/items.elementresolution) uses the pairing to report errors against the right file, unchanged.LoadErroris now a type alias ofsidecardir.LoadError: JSON keysfile/error, pointer-receiverError()="<file>: <msg>", so the/api/...error payload shape is untouched.pagetype's retired_rootpre-check became theRejecthook, which runs before duplicate tracking and does not claim the name — so both_root.yamland_root.ymlstill get the obsolete-message error, never "duplicate".Verification
*_test.gooutsideinternal/sidecardir/touched).internal/sidecardirhas its own 12-test suite: missing dir, dir-read error (via a regular file as dir — root-proof, no chmod), subdir/extension skipping,.yaml-beats-.yml, duplicate-after-failed-decode, invalid-name claiming, reject-hook non-claiming, per-file isolation, name-vs-filename sort order (a/a-b), errs sorted by File,Loaded.Filepairing,Error()format.scaffold/site/AGENTS.mdneeds no edit;agentsdoc_test.gopasses.go test ./...green (withweb/distbuilt and hugo on PATH so the hugo-gated server/theme/supervisor tests actually ran);go vet ./...clean; gofmt clean on all touched files.Out of scope, per the issue: deduplicating
parseNameList/requireVersion1between pagetype and backendlayout (kept as-is to avoid widening the diff).🤖 Generated with Claude Code
Landing validation — PASS
Verification signal relied on: Forgejo Actions
ci / build-and-test (pull_request)— success (8m22s, run 109). Not re-run locally; it coversgo test ./...,go vet, gofmt and theagentsdoc_test.godrift test.Checked:
refactor:); body carries a workingCloses #114; head isafk/114, matching the AFK contract.git merge-treeagainstorigin/mainis conflict-free.*_test.gooutsideinternal/sidecardir/touched, so the three families' existing suites pin the contract unmodified, exactly as the issue required._root→ duplicate → claim →loadOne(name regex → read). New:Reject→ duplicate → claim →nameRe→Decode. Old element/backendlayout: duplicate → claim →loadOne(name regex → read). New: same, minus the hook.TestLoadInvalidNameStillClaimsIt,TestLoadDuplicateReportedEvenWhenFirstFileFailsToDecode), while aRejected_rootdoes not (TestLoadRejectHookDoesNotClaimName).Config.Familyand the regex string comes from the same shared pattern.LoadErroris now a type alias, so the/api/...JSON keys (file/error) and the pointer-receiverError()="<file>: <msg>"are literally the same type.schemas[i].NametobaseName(items[i].File). These agree only ifSchema.Nameis always the derived base name — confirmed in all threeloadOnes (elementL167,pagetypeL124,backendlayoutL97 all build&Schema{Name: name}; none reads aname:key from YAML). Element's dropped post-passsort.Sliceis likewise safe:itemsarrives name-sorted and the cross-schema pass only filters.make([]Schema, 0, len(items))).os.ReadDir/filter/sort/dup loop; the new package ships a focused 12-test suite covering every case the issue listed;AGENTS.mdcorrectly needs no edit.Findings: none blocking. No nits worth holding the merge for.
Verdict: PASS — awaiting the human's free-text merge go-ahead.