On this page
The directories selfdoc owns under the hidden tool-state root, the manifest that grants each one, and the authority for every path inside them.
#internal/layout
#internal/layout
Package layout is selfdoc's declaration of the per-repository directories it owns, and the single authority for every path it reads or writes inside them.
A repository keeps one hidden directory at its root, [Root], holding function-named directories rather than tool-named ones: docs/ is the pages a person writes, docs-state/ is what selfdoc generates and the repository commits, docs-cache/ is what selfdoc generates and the repository does not commit, posts/ is the blog, vocabulary/ is the project's word lists. Another tool's state lives beside them under its own function name.
#Ownership
Every directory under [Root] has exactly one owner tool, declared in the [ManifestFileName] the directory itself carries. The manifest is the permission to write: [EnsureDir] refuses a directory whose manifest does not name selfdoc, printing the file and the line to put in it, and selfdoc never writes one itself. A manifest is also what makes a directory with no content yet exist in git. Reading and writing are open to anyone; the owner decides whether what was written is acceptable, which is what [Validate] answers.
#Sides
A directory is entirely handwritten or entirely generated -- never both. The side is a property of the function, so it is declared here once rather than per repository, and [Validate] holds the tree to it: a generated page in a handwritten directory, or a handwritten page in a generated one, is a defect with a named remedy.
#The derived ignore file
git needs one file inside [Root] to keep the uncommitted directories out of the repository. It is derived, at [Root]/[IgnoreFileName], from the commitment each tool declares: an uncommitted directory's contents are ignored and its manifest is not, so the permission travels with the repository while the contents do not. selfdoc owns only the block between its two marker comments and leaves every other line of that file alone, so several tools can write their own blocks into one file.
#Root
const Root = ".stricttools"Root is the one hidden directory a repository's tool-owned state lives in, relative to the repository root.
#Owner
const Owner = "selfdoc"Owner is the name selfdoc is declared under in a directory's manifest.
#ManifestFileName
const ManifestFileName = "manifest.toml"ManifestFileName is the ownership declaration a directory under [Root] carries, inside the directory it speaks for.
#OwnerKey
const OwnerKey = "owner"OwnerKey is the one key a manifest declares. The generated validator refuses a manifest carrying any other key.
#IgnoreFileName
const IgnoreFileName = ".gitignore"IgnoreFileName is the derived ignore file inside [Root], and the one entry there allowed to start with a dot.
#MoveScript
const MoveScript = "scripts/move-to-stricttools-layout.py"MoveScript is the script that moves a repository off the layout selfdoc used before this one. Every refusal of the old layout names it.
#GeneratedMarkerPrefix
const GeneratedMarkerPrefix = "<!-- generated by selfdoc gen"GeneratedMarkerPrefix opens the HTML comment every page selfdoc generates carries under its frontmatter. It is what tells a generated page from a handwritten one on disk, which is how [Validate] checks a directory's side.
#Handwritten
const Handwritten Side = "handwritten"The two sides. A directory is entirely one or entirely the other.
#Generated
const Generated Side = "generated"#Committed
const Committed Commitment = "committed"The two commitments. An uncommitted directory contributes one line to the derived ignore file.
#Uncommitted
const Uncommitted Commitment = "uncommitted"#DocsName
const DocsName = "docs"The names of the function directories selfdoc claims, relative to [Root].
#DocsStateName
const DocsStateName = "docs-state"#DocsCacheName
const DocsCacheName = "docs-cache"#PostsName
const PostsName = "posts"#VocabularyName
const VocabularyName = "vocabulary"#DocsRel
const DocsRel = Root + "/" + DocsNameThe function directories, as paths relative to the repository root, in slash form.
#DocsStateRel
const DocsStateRel = Root + "/" + DocsStateName#DocsCacheRel
const DocsCacheRel = Root + "/" + DocsCacheName#PostsRel
const PostsRel = Root + "/" + PostsName#VocabularyRel
const VocabularyRel = Root + "/" + VocabularyName#GeneratedPagesRel
const GeneratedPagesRel = DocsStateRel + "/pages"GeneratedPagesRel is the second docs root: the pages selfdoc generates, merged into one URL namespace with the handwritten root at build time.
#ManifestRel
const ManifestRel = DocsStateRel + "/manifest.json"ManifestRel is the record of what a build published.
#PostManifestRel
const PostManifestRel = DocsStateRel + "/post-manifest.json"PostManifestRel is the posts-only manifest the assembly's post publisher reads.
#RevisionsRel
const RevisionsRel = DocsStateRel + "/revisions.json"RevisionsRel is the post revision sidecar.
#HashesDirRel
const HashesDirRel = DocsStateRel + "/hashes"HashesDirRel holds the content and description hash baselines.
#HashesRel
const HashesRel = HashesDirRel + "/hashes.json"HashesRel is the hash store itself.
#DataRel
const DataRel = DocsStateRel + "/data"DataRel is where a sandboxed gen-data script writes its output.
#OutputRel
const OutputRel = DocsCacheRel + "/build"OutputRel is the built site.
#VersionsRel
const VersionsRel = DocsCacheRel + "/versions"VersionsRel holds one extracted checkout per archived version.
#DocsDefault
const DocsDefault = DocsRel + "/"The declared values of the config keys that name a layout directory. A project that declares nothing gets these, and a project that declares something else has to keep it under [Root].
#OutputDefault
const OutputDefault = OutputRel + "/"#PostsDefault
const PostsDefault = PostsRel + "/"#DeprecatedRoot
const DeprecatedRoot = ".selfdoc"DeprecatedRoot is the directory selfdoc kept a repository's state in before this layout. Its presence is what a refusal recognizes: nothing reads it.
#GeneratedBy
const GeneratedBy = "0.2.5"GeneratedBy is the strictspec release that produced this file. The runtime pairing guard hard-errors unless it matches the linked runtime exactly.
#SchemaFormatVersion
const SchemaFormatVersion = 1SchemaFormatVersion is the document format_version this validator accepts.
#CheckOwnership
const CheckOwnership = "ownership"The names of the rules [Validate] holds a repository to.
#CheckSide
const CheckSide = "side"#CheckHidden
const CheckHidden = "hidden"#CheckIgnore
const CheckIgnore = "ignore-file"#Side
type Side stringSide is whether a directory's contents are written by a person or by a tool.
#Commitment
type Commitment stringCommitment is whether a directory's contents belong in the repository.
#Directory
type Directory structDirectory is one function directory selfdoc claims: what it is for, which side of the authorship line it sits on, whether the repository commits it, and the paths it replaced.
#OldLayoutError
type OldLayoutError structOldLayoutError is a repository refused for still being laid out the way selfdoc used to lay one out.
There is no migrator and no dual reading: a repository is moved once, by hand, with [MoveScript], and until it is, every command that reads project state refuses it.
#DirectoryManifest
type DirectoryManifest structDirectoryManifest is the frozen typed binding of the "DirectoryManifest" record. Fields are immutable by convention (shallow-plus-generated-immutability); use With* for copy-on-write.
#Problem
type Problem structProblem is one thing wrong with a repository's layout: what is wrong, and what to do about it.
#Declared
func Declared() []DirectoryDeclared returns the directories selfdoc claims, in declaration order.
#Lookup
func Lookup(name string) (Directory, bool)Lookup returns the declaration of one claimed directory.
#Path
func Path(baseDir, rel string) stringPath joins a repository root with one of this package's slash-form relative paths, in the operating system's own spelling.
#UnderRoot
func UnderRoot(declaredPath string) boolUnderRoot reports whether a declared path names something inside [Root]. A project may spell its docs and output paths as it likes as long as they live in the layout.
#FunctionOf
func FunctionOf(rel string) (string, bool)FunctionOf returns the function directory a relative path sits in.
#DirectoryManifestRel
func DirectoryManifestRel(name string) stringDirectoryManifestRel is a directory's ownership manifest, as a path relative to the repository root, in slash form.
#DirectoryManifestPath
func DirectoryManifestPath(baseDir, name string) stringDirectoryManifestPath is where a directory's ownership manifest sits, in the operating system's own spelling.
#DirectoryManifestContent
func DirectoryManifestContent(owner string) stringDirectoryManifestContent renders the manifest that declares one tool the owner of a directory. Every refusal that asks for a manifest prints from here, so the text a person is told to write is generated from the declaration rather than typed beside it.
#ReadDirectoryManifest
func ReadDirectoryManifest(baseDir, name string) (*DirectoryManifest, error)ReadDirectoryManifest reads and validates one directory's ownership manifest.
strictspec is the boundary validator: the file is checked against .strictspec/directory-manifest.schema.toml by the generated validator in this package, so a manifest that reaches a caller declares an owner and nothing else. A missing manifest is returned as the underlying [os.ErrNotExist], which callers answer with the file to create.
The schema's format-version gate is supplied here rather than written into the file, the way a frontmatter block's is: a manifest on disk is the one [OwnerKey] line, and a file that declares the gate itself is refused, so the key has exactly one author.
#KnownOwner
func KnownOwner(owner string) boolKnownOwner reports whether a manifest names a tool this machine has: selfdoc itself, or any name PATH answers with an executable.
#EnsureOwned
func EnsureOwned(baseDir, name string) errorEnsureOwned answers whether selfdoc may write into one of the directories under [Root] in this repository.
The manifest is the permission. A directory carrying none, or one naming another tool, is refused -- the first with the file and the line to write, which selfdoc never writes itself.
#EnsureDir
func EnsureDir(h *effects.Handle, baseDir, rel string) errorEnsureDir creates a directory inside one of selfdoc's function directories, after checking the manifest that permits it, and refreshes the derived ignore file.
rel is one of this package's slash-form relative paths. Creating anything under [Root] goes through here, so no path can reach the filesystem without its owning manifest having been read.
#IgnoreBlock
func IgnoreBlock() []stringIgnoreBlock is selfdoc's block of the derived ignore file: the marker comments around the lines that keep each uncommitted directory it owns out of the repository.
An uncommitted directory contributes two lines rather than one. Its contents are ignored, and its [ManifestFileName] is not: the manifest is the permission to write into the directory, and a permission that git did not carry would have to be written again in every fresh checkout -- including the ones a multi-version build extracts out of git tags.
#RenderIgnore
func RenderIgnore(existing string) stringRenderIgnore returns what the derived ignore file should hold, given what it holds now: selfdoc's block replaced in place, or appended when the file carries none, with every other line untouched.
#IgnorePath
func IgnorePath(baseDir string) stringIgnorePath is where the derived ignore file sits.
#WriteIgnore
func WriteIgnore(h *effects.Handle, baseDir string) errorWriteIgnore brings the derived ignore file up to date, writing nothing when it already holds what it should.
#IgnoreIsCurrent
func IgnoreIsCurrent(baseDir string) (bool, string)IgnoreIsCurrent reports whether the derived ignore file holds selfdoc's block as declared, and returns the content it should hold.
#RefuseOldLayout
func RefuseOldLayout(baseDir, docsDeclared, outputDeclared, postsDeclared string) errorRefuseOldLayout returns an [OldLayoutError] when a repository has not been moved to this layout yet.
It is called from the config loader, so every command that reads project state refuses before it reads anything. The three declared paths are the config keys that name a layout directory; an empty one is taken as undeclared and therefore already the default, which is inside [Root].
#ValidateBytes
func ValidateBytes(input []byte, syntax string) (*DirectoryManifest, []strictspec.Diagnostic)ValidateBytes is the raw-bytes entry point: lossless parse of input in the given syntax ("json" | "toml" | "jsonl"), then validate. It returns the typed root value (nil when any diagnostic fired) and the ordered diagnostics.
#ValidateValue
func ValidateValue(v strictspec.Value) (*DirectoryManifest, []strictspec.Diagnostic)ValidateValue is the tagged-value entry point: validate an already-parsed tagged document value (from strictspec.LoadValue or a typed constructor).
#ValidateBytesWithEvidence
func ValidateBytesWithEvidence(input []byte, syntax string, evidence map[string][]map[string]any) (*DirectoryManifest, []strictspec.Diagnostic)ValidateBytesWithEvidence is ValidateBytes plus cross-document resolver evidence for the phase-2 constraint vocabulary.
#Validate
func Validate(baseDir string) ([]Problem, error)Validate checks one repository's layout and returns every problem it finds, in check order.
The rules: every directory under [Root] carries a [ManifestFileName] naming a tool this machine has, and every directory selfdoc claims that exists names selfdoc; every directory selfdoc owns holds only what its side allows; nothing under [Root] starts with a dot except the derived ignore file; and that file's selfdoc block is what the declaration says it should be.
A missing [Root] is returned as an error rather than a problem: the rest of the rules are unanswerable without it.
#OldLayoutError.Error
func (e *OldLayoutError) Error() string#DirectoryManifest.WithFormatVersion
func (x *DirectoryManifest) WithFormatVersion(v int64) *DirectoryManifestWithFormatVersion returns a copy of DirectoryManifest with FormatVersion set to the given value.
#DirectoryManifest.WithOwner
func (x *DirectoryManifest) WithOwner(v string) *DirectoryManifestWithOwner returns a copy of DirectoryManifest with Owner set to the given value.
#Problem.Error
func (p Problem) Error() string { return "[" + p.Check + "] " + p.Message }Error renders a problem the way the command prints it.