package site import ( "encoding/json" "fmt" "html/template" "os" "path/filepath" "sort" "strings" "gopkg.in/yaml.v3" ) // PlatformGuide is one `platforms/.yml` file: configuration and public // copy at the same time. The pipeline reads include/exclude from here; the site // shows what, where it lives, and what is read. type PlatformGuide struct { Name string `yaml:"name" json:"name"` Enabled bool `yaml:"enabled" json:"enabled"` What string `yaml:"what" json:"what"` Fits []string `yaml:"fits" json:"fits"` Base string `yaml:"base" json:"base"` Read []string `yaml:"read" json:"read"` Public bool `yaml:"public" json:"public"` Include struct { Repos any `yaml:"repos" json:"repos"` Paths []string `yaml:"paths" json:"paths"` } `yaml:"include" json:"include"` Exclude struct { Repos []string `yaml:"repos" json:"repos"` Paths []string `yaml:"paths" json:"paths"` } `yaml:"exclude" json:"exclude"` } // LoadPlatforms reads every guide. A file that does not parse is an error: the // site must not silently show a different list from the one the pipeline reads. func LoadPlatforms(root string) ([]PlatformGuide, error) { dir := filepath.Join(root, "platforms") entries, err := os.ReadDir(dir) if err != nil { return nil, err } var out []PlatformGuide for _, entry := range entries { if entry.IsDir() || !(strings.HasSuffix(entry.Name(), ".yml") || strings.HasSuffix(entry.Name(), ".yaml")) { continue } raw, err := os.ReadFile(filepath.Join(dir, entry.Name())) if err != nil { return nil, err } var guide PlatformGuide if err := yaml.Unmarshal(raw, &guide); err != nil { return nil, fmt.Errorf("%s: %w", entry.Name(), err) } if guide.Name == "" || guide.What == "" { return nil, fmt.Errorf("%s: name and what are required", entry.Name()) } out = append(out, guide) } sort.Slice(out, func(i, j int) bool { return out[i].Name < out[j].Name }) return out, nil } // RenderPlatforms writes /platforms/index.html and /platforms/index.json. // // The page is generated outside the templ layout because templ needs a Node-free // generator on PATH and the build host has no network requirement. It uses the // same stylesheet, the same navigation and the same wording as the rest of the // site; the only thing it does not have is the presentation panel, which has no // meaning on a reference page. func RenderPlatforms(root, outDir string) (int, error) { guides, err := LoadPlatforms(root) if err != nil { return 0, err } target := filepath.Join(outDir, "platforms") if err := os.MkdirAll(target, 0o755); err != nil { return 0, err } var body strings.Builder body.WriteString(`

The assistant reads four sources. Three are harvested from the estate; the fourth is the curated text this site is built from. Everything below is the same configuration the pipeline runs.

`) for _, guide := range guides { state := "enabled" if !guide.Enabled { state = "disabled" } body.WriteString(`
`) fmt.Fprintf(&body, `

%s %s

`, template.HTMLEscapeString(guide.Name), state) fmt.Fprintf(&body, `

%s

`, template.HTMLEscapeString(strings.TrimSpace(guide.What))) fmt.Fprintf(&body, `
Where it lives
%s
`, template.HTMLEscapeString(guide.Base)) fmt.Fprintf(&body, `
Feeds
%s
`, template.HTMLEscapeString(strings.Join(guide.Fits, ", "))) fmt.Fprintf(&body, `
Reads
%s
`, template.HTMLEscapeString(strings.Join(guide.Read, ", "))) if len(guide.Include.Paths) > 0 { fmt.Fprintf(&body, `
Includes
%s
`, template.HTMLEscapeString(strings.Join(guide.Include.Paths, ", "))) } if len(guide.Exclude.Paths) > 0 { fmt.Fprintf(&body, `
Excludes
%s
`, template.HTMLEscapeString(strings.Join(guide.Exclude.Paths, ", "))) } body.WriteString(`
`) } page, err := template.New("platforms").Parse(platformsPage) if err != nil { return 0, err } file, err := os.Create(filepath.Join(target, "index.html")) if err != nil { return 0, err } defer file.Close() if err := page.Execute(file, map[string]any{"Body": template.HTML(body.String())}); err != nil { return 0, err } raw, err := json.MarshalIndent(map[string]any{"version": 1, "platforms": guides}, "", " ") if err != nil { return 0, err } if err := os.WriteFile(filepath.Join(target, "index.json"), append(raw, '\n'), 0o644); err != nil { return 0, err } return len(guides), nil } const platformsPage = `Sources and platform guides — Sam Rolfe
SR Sam Rolfe AI · Platform · Edge

AI overlay

What the assistant reads

Four sources, one pool. Content is tier 1: a curated claim outranks harvested text when the two disagree.

{{.Body}}

Every artefact · Search the site

`