loomctl orient: one table of contents, restating no rule
Generates .loom/orientation.md — what this repository depends on, where each copy came from, and which facets sit beside it — for whoever arrives next, of any make. One file rather than two, decided by loom's own rule. Generate what varies, adopt what does not: the publishing half varies not at all and is already adopted, so publication.md appears in the index like any other adopted document, in exactly the repositories that adopted it. A second command would emit a file whose whole content is a pointer to a file already in the tree, and would revive a noun declined when published check went. Restates no rule. The three moves are phrased as operations — what to run, what to write, where — and every rule stays in the document that owns it. The .usages.md is pointed at rather than summarised, because what depends on a document is free prose and anything extracting a claim from prose eventually extracts it wrong. It says nothing recorded when a document has no usages file, which is a finding rather than an omission and is currently true of six of nine. Byte-deterministic, pinned by a test that inserts records out of order and generates three times. Written to .loom/orientation.md rather than inside .loom/externals/, where check would report it as an unlocked external in every repository using the feature. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018UTxuSizozEA8yDitPuris
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
package orient
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.hypertheory-labs.dev/loom/loom-cli/internal/lock"
|
||||
)
|
||||
|
||||
// A generated file that churns produces diffs nobody reads, and the diff is most
|
||||
// of the value.
|
||||
func TestGenerateIsDeterministic(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
locks, err := lock.LoadFile(lock.Path(root))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Inserted out of order on purpose: the output must not depend on it.
|
||||
for _, r := range []lock.Record{
|
||||
{Path: "h/o/zeta/z.md", URL: "https://h/z", ETag: `"3"`},
|
||||
{Path: "h/o/alpha/a.md", URL: "https://h/a", ETag: `"1"`},
|
||||
{Path: "h/o/mid/m.md", URL: "https://h/m", ETag: `"2"`},
|
||||
} {
|
||||
locks.Put(r)
|
||||
}
|
||||
if err := locks.Save(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var first string
|
||||
for i := 0; i < 3; i++ {
|
||||
dest, err := Generate(root, filepath.Join(root, "out.md"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b, err := os.ReadFile(dest)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if i == 0 {
|
||||
first = string(b)
|
||||
continue
|
||||
}
|
||||
if string(b) != first {
|
||||
t.Fatal("output changed between runs")
|
||||
}
|
||||
}
|
||||
if a, z := strings.Index(first, "alpha"), strings.Index(first, "zeta"); a > z {
|
||||
t.Error("entries are not ordered by path")
|
||||
}
|
||||
if strings.Contains(first, "publishes") {
|
||||
t.Error("claimed the repository publishes with no .loom/published")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSaysWhenNothingRecordsADependency(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
locks, _ := lock.LoadFile(lock.Path(root))
|
||||
locks.Put(lock.Record{Path: "h/o/r/doc.md", URL: "https://h/doc", ETag: `"1"`})
|
||||
if err := locks.Save(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
dest, err := Generate(root, filepath.Join(root, "out.md"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b, _ := os.ReadFile(dest)
|
||||
if !strings.Contains(string(b), "nothing recorded") {
|
||||
t.Error("a document with no .usages.md should say so — it is a finding, not an omission")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user