snapshot of c000b74a5b09c433f96a69fa102c5c8f6583dff0 Annotations about the code that implements koment.

internal/projectlayout/layout.go

1 package projectlayout
2
3 import (
4 "bytes"
5 "crypto/sha256"
6 "errors"
7 "fmt"
8 "os"
9 "os/exec"
10 "path/filepath"
11 "sort"
12 "strings"
13 )
14
15 const ReferencePath = "docs/reference/repository-layout.md"
16
17 const zedLicensePath = "integrations/editors/zed/LICENSE"
18 const zedManifestPath = "integrations/editors/zed/Cargo.toml"
19 const zedGPLv3SHA256 = "3972dc9744f6499f0f9b2dbf76696f2ae7ad8af9b23dde66d6af86c9dfb36986"
20
21 type Area struct {
22 Path string
23 Purpose string
24 }
25
26 var Areas = []Area{
27 {Path: ".claude-plugin/", Purpose: "Claude marketplace discovery metadata"},
28 {Path: ".codex/", Purpose: "generated Codex repository adapter"},
29 {Path: ".cursor/", Purpose: "generated Cursor repository adapter"},
30 {Path: ".github/", Purpose: "GitHub workflows, templates and ownership"},
31 {Path: ".koment/", Purpose: "authoritative annotations and policy"},
32 {Path: ".mise/", Purpose: "pinned toolchain configuration"},
33 {Path: ".opencode/", Purpose: "generated OpenCode repository adapter"},
34 {Path: ".vscode/", Purpose: "VS Code workspace discovery and validation"},
35 {Path: "cmd/", Purpose: "Go binary entry points"},
36 {Path: "distribution/", Purpose: "delivery and deployment assets"},
37 {Path: "docs/", Purpose: "start, guide, reference and explanation documentation"},
38 {Path: "examples/", Purpose: "runnable and inspectable product examples"},
39 {Path: "integrations/", Purpose: "code installed into another product"},
40 {Path: "internal/", Purpose: "private Go product packages"},
41 {Path: "schema/", Purpose: "versioned public schemas"},
42 {Path: "scripts/", Purpose: "repository automation"},
43 {Path: "testdata/", Purpose: "repository-wide fixtures"},
44 }
45
46 var RootFiles = []string{
47 ".gitignore",
48 ".golangci.yml",
49 ".lefthook.toml",
50 ".mcp.json",
51 ".release-please-manifest.json",
52 ".renovaterc.json5",
53 "AGENTS.md",
54 "CHANGELOG.md",
55 "CLA.md",
56 "CLAUDE.md",
57 "CONTRIBUTING.md",
58 "DESIGN.md",
59 "Dockerfile",
60 "LICENSE",
61 "README.md",
62 "SECURITY.md",
63 "TRADEMARK.md",
64 "action.yml",
65 "go.mod",
66 "go.sum",
67 "opencode.json",
68 "release-please-config.json",
69 "server.json",
70 }
71
72 var ClosedChildren = map[string][]string{
73 "distribution": {"helm", "package-managers"},
74 "distribution/helm": {"chart_test.go", "koment"},
75 "distribution/package-managers": {"README.md", "homebrew", "naming_test.go", "registry_test.go", "scoop", "winget"},
76 "docs": {"README.md", "explanation", "guides", "reference", "start"},
77 "examples": {"annotated-workspace"},
78 "integrations": {"agent-plugins", "editors"},
79 "integrations/agent-plugins": {"README.md", "claude", "codex", "hermes", "opencode"},
80 "integrations/editors": {"vscode", "zed"},
81 }
82
83 var LegacyRoots = []string{"charts", "editors", "packaging", "plugins", "workspace"}
84
85 type referenceMigration struct {
86 Retired string
87 Replacement string
88 }
89
90 var referenceMigrations = []referenceMigration{
91 {Retired: "docs/" + "releasing.md", Replacement: "docs/guides/release-koment.md"},
92 }
93
94 func RepositoryRoot(start string) (string, error) {
95 root, err := filepath.Abs(start)
96 if err != nil {
97 return "", err
98 }
99 for {
100 if _, err := os.Stat(filepath.Join(root, "go.mod")); err == nil {
101 return root, nil
102 }
103 parent := filepath.Dir(root)
104 if parent == root {
105 return "", fmt.Errorf("no repository root above %s", start)
106 }
107 root = parent
108 }
109 }
110
111 func Check(root string) error {
112 repositoryRoot, err := os.OpenRoot(root)
113 if err != nil {
114 return err
115 }
116 checkError := checkRepository(root, repositoryRoot)
117 return errors.Join(checkError, repositoryRoot.Close())
118 }
119
120 func checkRepository(root string, repositoryRoot *os.Root) error {
121 paths, err := repositoryPaths(root, repositoryRoot)
122 if err != nil {
123 return err
124 }
125 violations := ValidatePaths(paths)
126 referenceViolations, err := validateRetiredReferences(repositoryRoot, paths)
127 if err != nil {
128 return err
129 }
130 violations = append(violations, referenceViolations...)
131 rootViolations, err := validateLegacyRoots(repositoryRoot)
132 if err != nil {
133 return err
134 }
135 violations = append(violations, rootViolations...)
136 licenseViolations, err := validateZedLicense(repositoryRoot)
137 if err != nil {
138 return err
139 }
140 violations = append(violations, licenseViolations...)
141 current, err := repositoryRoot.ReadFile(ReferencePath)
142 if err != nil {
143 violations = append(violations, fmt.Sprintf("%s: %v", ReferencePath, err))
144 } else if !bytes.Equal(current, Reference()) {
145 violations = append(violations, ReferencePath+": generated reference is stale; run mise run layout-render")
146 }
147 if len(violations) == 0 {
148 return nil
149 }
150 sort.Strings(violations)
151 return errors.New(strings.Join(violations, "\n"))
152 }
153
154 func validateZedLicense(repositoryRoot *os.Root) ([]string, error) {
155 license, err := repositoryRoot.ReadFile(zedLicensePath)
156 if err != nil {
157 return nil, fmt.Errorf("read %s: %w", zedLicensePath, err)
158 }
159 manifest, err := repositoryRoot.ReadFile(zedManifestPath)
160 if err != nil {
161 return nil, fmt.Errorf("read %s: %w", zedManifestPath, err)
162 }
163 violations := []string{}
164 if fmt.Sprintf("%x", sha256.Sum256(license)) != zedGPLv3SHA256 {
165 violations = append(violations, zedLicensePath+": expected the verbatim GPLv3 text required by ADR 0145")
166 }
167 if !bytes.Contains(manifest, []byte("license = \"GPL-3.0-or-later\"")) {
168 violations = append(violations, zedManifestPath+": expected license = \"GPL-3.0-or-later\" required by ADR 0145")
169 }
170 return violations, nil
171 }
172
173 func ValidatePaths(paths []string) []string {
174 allowedRoots := map[string]bool{}
175 for _, area := range Areas {
176 allowedRoots[strings.TrimSuffix(area.Path, "/")] = true
177 }
178 allowedFiles := map[string]bool{}
179 for _, file := range RootFiles {
180 allowedFiles[file] = true
181 }
182 legacy := map[string]bool{}
183 for _, name := range LegacyRoots {
184 legacy[name] = true
185 }
186 closed := map[string]map[string]bool{}
187 for parent, children := range ClosedChildren {
188 closed[parent] = map[string]bool{}
189 for _, child := range children {
190 closed[parent][child] = true
191 }
192 }
193
194 violations := []string{}
195 for _, repositoryPath := range paths {
196 parts := strings.Split(filepath.ToSlash(repositoryPath), "/")
197 if len(parts) == 1 {
198 if !allowedFiles[parts[0]] {
199 violations = append(violations, repositoryPath+": root file is outside the layout contract")
200 }
201 continue
202 }
203 if legacy[parts[0]] {
204 violations = append(violations, repositoryPath+": legacy root must be migrated")
205 continue
206 }
207 if !allowedRoots[parts[0]] {
208 violations = append(violations, repositoryPath+": root directory is outside the layout contract")
209 continue
210 }
211 for depth := 1; depth < len(parts); depth++ {
212 parent := strings.Join(parts[:depth], "/")
213 children, isClosed := closed[parent]
214 if isClosed && !children[parts[depth]] {
215 violations = append(violations, repositoryPath+": "+parts[depth]+" is not allowed under "+parent)
216 break
217 }
218 }
219 }
220 return violations
221 }
222
223 func Reference() []byte {
224 var output strings.Builder
225 output.WriteString("# Repository layout\n\n")
226 output.WriteString("Generated from `internal/projectlayout`; edit the executable specification, not this file.\n\n")
227 output.WriteString("The repository root is a closed contract. A tracked or non-ignored path outside the areas and exact root files below fails `mise run layout-check`.\n\n")
228 output.WriteString("The same check rejects repository-controlled references to paths retired by completed migrations. Historical path provenance under `.koment/` is excluded.\n\n")
229 output.WriteString("## Architectural areas\n\n")
230 output.WriteString("| Path | Owner |\n|---|---|\n")
231 for _, area := range Areas {
232 fmt.Fprintf(&output, "| `%s` | %s |\n", area.Path, area.Purpose)
233 }
234 output.WriteString("\n## Closed categories\n\n")
235 parents := make([]string, 0, len(ClosedChildren))
236 for parent := range ClosedChildren {
237 parents = append(parents, parent)
238 }
239 sort.Strings(parents)
240 for _, parent := range parents {
241 children := append([]string(nil), ClosedChildren[parent]...)
242 sort.Strings(children)
243 fmt.Fprintf(&output, "- `%s/`: `%s`\n", parent, strings.Join(children, "`, `"))
244 }
245 output.WriteString("\n## Exact root files\n\n")
246 files := append([]string(nil), RootFiles...)
247 sort.Strings(files)
248 for _, file := range files {
249 fmt.Fprintf(&output, "- `%s`\n", file)
250 }
251 output.WriteString("\n## Changing the contract\n\n")
252 output.WriteString("A boundary change must supersede ADR 0143, demonstrate why no existing area can own the capability, update `DESIGN.md` and this specification, regenerate this page, and migrate every path and reference in the same change. Convenience, file count, implementation language and symmetry are insufficient reasons.\n")
253 return []byte(output.String())
254 }
255
256 func WriteReference(root string) error {
257 repositoryRoot, err := os.OpenRoot(root)
258 if err != nil {
259 return err
260 }
261 writeError := repositoryRoot.WriteFile(ReferencePath, Reference(), 0o644)
262 return errors.Join(writeError, repositoryRoot.Close())
263 }
264
265 func repositoryPaths(root string, repositoryRoot *os.Root) ([]string, error) {
266 command := exec.Command("git", "ls-files", "--cached", "--others", "--exclude-standard", "-z")
267 command.Dir = root
268 content, err := command.Output()
269 if err != nil {
270 return nil, fmt.Errorf("list repository paths: %w", err)
271 }
272 paths := []string{}
273 for _, item := range bytes.Split(content, []byte{0}) {
274 if len(item) == 0 {
275 continue
276 }
277 path := string(item)
278 if _, err := repositoryRoot.Lstat(filepath.FromSlash(path)); err == nil {
279 paths = append(paths, path)
280 } else if !os.IsNotExist(err) {
281 return nil, fmt.Errorf("inspect %s: %w", path, err)
282 }
283 }
284 return paths, nil
285 }
286
287 func validateRetiredReferences(repositoryRoot *os.Root, paths []string) ([]string, error) {
288 violations := []string{}
289 for _, repositoryPath := range paths {
290 if strings.HasPrefix(filepath.ToSlash(repositoryPath), ".koment/") {
291 continue
292 }
293 content, err := repositoryRoot.ReadFile(filepath.FromSlash(repositoryPath))
294 if err != nil {
295 return nil, fmt.Errorf("inspect references in %s: %w", repositoryPath, err)
296 }
297 for _, migration := range referenceMigrations {
298 if bytes.Contains(content, []byte(migration.Retired)) {
299 violations = append(violations, fmt.Sprintf("%s: retired reference %q must be replaced with %q", repositoryPath, migration.Retired, migration.Replacement))
300 }
301 }
302 }
303 return violations, nil
304 }
305
306 func validateLegacyRoots(repositoryRoot *os.Root) ([]string, error) {
307 violations := []string{}
308 for _, name := range LegacyRoots {
309 _, err := repositoryRoot.Stat(name)
310 if os.IsNotExist(err) {
311 continue
312 }
313 if err != nil {
314 return nil, fmt.Errorf("inspect legacy root %s: %w", name, err)
315 }
316 violations = append(violations, name+"/: legacy root must be migrated")
317 }
318 return violations, nil
319 }

Find an annotation

Search file paths, rationale, kinds, and authors.

moveEnter openEsc close