Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ Tests should always pass on `main`. Run `make test` before sending a PR.

```
cmd/ cobra commands (root, init, check, fix, inspect, collection, item, schema, rules)
internal/project project domain layer: the .katalyst/ loader (loader.go: schemas + storage instances, which embed their collections), the whole workspace, selectors, item enumeration
internal/storage backend-kind registry: StorageType, Known, Granularity, Reference
internal/project project domain layer: the .katalyst/ loader (loader.go: schemas + bases, which embed their collections), the whole workspace, selectors, item enumeration
internal/storage backend-kind registry: BaseType, Known, Scope, Reference
internal/storage/collection the read stack: CollectionDefinition + the thin Item
internal/storage/collection/listing item list filter/grep/sort/skip/limit pipeline
internal/storage/collection/predicate metadata predicate grammar (item list --filter, collection variants)
Expand Down Expand Up @@ -64,8 +64,8 @@ reconstruction), implemented per backend under `storage/collection/<backend>`
(filesystem today). Don't inline filesystem assumptions (globbing, stem-as-id,
path joins) elsewhere, a second backend (SQLite) attaches by implementing that
interface. The `internal/project` loader (`loader.go`) owns the `.katalyst/`
*vocabulary*: it reads the workspace, resolves schemas, and assembles storage
instances. Each object type owns the parse of its own config — the storage
*vocabulary*: it reads the workspace, resolves schemas, and assembles bases.
Each object type owns the parse of its own config — the storage
registry validates a declared `type` (`storage.Known`), and a collection parses
its own block, including variant predicates, in `storage/collection` (which
imports the sibling `predicate` grammar intra-subtree). The loader depends on
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ As your content evolves, Katalyst gives you tools to navigate change.

- *Add or change checks*
- *Change the structure of your content*
- *Change your storage layer*
- *Change your base*

## Design principles

Expand Down
2 changes: 1 addition & 1 deletion cmd/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func newCheckCmd() *cobra.Command {
Use: "check [selector ...]",
Short: "Run configured checks against the selected items",
Long: `check parses each selected item's frontmatter (YAML, TOML, or JSON)
and runs the checks configured for its collection under .katalyst/storage/.
and runs the checks configured for its collection under .katalyst/bases/.

Selectors (see docs/content/deep-dives/domain-model/_index.md):

Expand Down
16 changes: 8 additions & 8 deletions cmd/check_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ func setupNotesRepo(t *testing.T, notesCollection string) string {
t.Helper()
dir := t.TempDir()
writeProject(t, dir, map[string]string{
"config.yaml": schemaFormatJSON,
"schemas/book.json": bookSchemaFixture,
"storage/local.yaml": storageLocal(map[string]string{"notes": notesCollection}),
"config.yaml": schemaFormatJSON,
"schemas/book.json": bookSchemaFixture,
"bases/local.yaml": baseLocal(map[string]string{"notes": notesCollection}),
})
chdir(t, dir)
return dir
Expand Down Expand Up @@ -167,7 +167,7 @@ func setupVariantRepo(t *testing.T, pagesBody string) string {
"schemas/page.yaml": "type: object\nrequired: [title]\nproperties:\n title: {type: string}\n",
"schemas/section.yaml": "type: object\n",
"schemas/content.yaml": "type: object\nrequired: [weight]\nproperties:\n weight: {type: integer}\n",
"storage/local.yaml": storageLocal(map[string]string{"pages": pagesBody}),
"bases/local.yaml": baseLocal(map[string]string{"pages": pagesBody}),
})
chdir(t, dir)
return dir
Expand Down Expand Up @@ -335,7 +335,7 @@ func TestCheck_inlineSchemaKeyTakesPrecedence(t *testing.T) {
"config.yaml": schemaFormatJSON,
"schemas/book.json": bookSchemaFixture,
"schemas/strict-book.json": strictBookSchemaFixture,
"storage/local.yaml": storageLocal(map[string]string{"notes": "path: notes\nschema: book\n"}),
"bases/local.yaml": baseLocal(map[string]string{"notes": "path: notes\nschema: book\n"}),
})
chdir(t, dir)

Expand All @@ -356,7 +356,7 @@ func TestCheck_inlineSchemaKeyTakesPrecedence(t *testing.T) {
func TestCheck_markdownAndFilesystemChecks(t *testing.T) {
dir := t.TempDir()
writeProject(t, dir, map[string]string{
"storage/local.yaml": storageLocal(map[string]string{"notes": "path: notes\nchecks:\n - kind: markdown_title_matches_h1\n field: title\n"}),
"bases/local.yaml": baseLocal(map[string]string{"notes": "path: notes\nchecks:\n - kind: markdown_title_matches_h1\n field: title\n"}),
})
chdir(t, dir)
mustWrite(t, filepath.Join(dir, "notes/dune.md"), "---\ntitle: Dune\n---\n# Children of Dune\n")
Expand All @@ -376,7 +376,7 @@ func TestCheck_markdownAndFilesystemChecks(t *testing.T) {
func TestCheck_collectionScoped_rescanFullCollectionForSingleItemSelector(t *testing.T) {
dir := t.TempDir()
writeProject(t, dir, map[string]string{
"storage/local.yaml": storageLocal(map[string]string{"notes": "path: notes\nchecks:\n - kind: filesystem_unique_field\n field: slug\n"}),
"bases/local.yaml": baseLocal(map[string]string{"notes": "path: notes\nchecks:\n - kind: filesystem_unique_field\n field: slug\n"}),
})
chdir(t, dir)
mustWrite(t, filepath.Join(dir, "notes/a.md"), "---\nslug: dune\n---\n# A\n")
Expand All @@ -395,7 +395,7 @@ func TestCheck_collectionScoped_rescanFullCollectionForSingleItemSelector(t *tes
func TestCheck_writingTells_warnButPass(t *testing.T) {
dir := t.TempDir()
writeProject(t, dir, map[string]string{
"storage/local.yaml": storageLocal(map[string]string{"notes": "path: notes\nchecks:\n - kind: markdown_writing_tells\n"}),
"bases/local.yaml": baseLocal(map[string]string{"notes": "path: notes\nchecks:\n - kind: markdown_writing_tells\n"}),
})
chdir(t, dir)
mustWrite(t, filepath.Join(dir, "notes/x.md"),
Expand Down
2 changes: 1 addition & 1 deletion cmd/collection.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
func newCollectionCmd() *cobra.Command {
c := &cobra.Command{
Use: "collection",
Short: "Inspect collections declared by storage instances under .katalyst/storage/",
Short: "Inspect collections declared by bases under .katalyst/bases/",
}
c.AddCommand(newCollectionListCmd(), newCollectionGetCmd())
return c
Expand Down
4 changes: 2 additions & 2 deletions cmd/fix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func setupFixRepo(t *testing.T) string {
t.Helper()
dir := t.TempDir()
writeProject(t, dir, map[string]string{
"storage/local.yaml": storageLocal(map[string]string{"notes": fixNotesConfig}),
"bases/local.yaml": baseLocal(map[string]string{"notes": fixNotesConfig}),
})
chdir(t, dir)
return dir
Expand All @@ -27,7 +27,7 @@ func setupFixRepoWith(t *testing.T, notesConfig string) string {
t.Helper()
dir := t.TempDir()
writeProject(t, dir, map[string]string{
"storage/local.yaml": storageLocal(map[string]string{"notes": notesConfig}),
"bases/local.yaml": baseLocal(map[string]string{"notes": notesConfig}),
})
chdir(t, dir)
return dir
Expand Down
14 changes: 7 additions & 7 deletions cmd/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,20 +61,20 @@ func chdir(t *testing.T, dir string) {
const schemaFormatJSON = "schemas:\n format: json\n"

// writeProject scaffolds a .katalyst/ tree. Keys are paths relative to the
// .katalyst/ directory (e.g. "schemas/book.json", "storage/local.yaml",
// .katalyst/ directory (e.g. "schemas/book.json", "bases/local.yaml",
// "config.yaml"); values are file contents.
func writeProject(t *testing.T, dir string, files map[string]string) {
t.Helper()
projecttest.WriteProject(t, dir, files)
}

// storageLocal builds a .katalyst/storage/local.yaml body: a filesystem
// instance rooted at the project, declaring the given collections. Each value
// baseLocal builds a .katalyst/bases/local.yaml body: a filesystem base rooted
// at the project, declaring the given collections. Each value
// is the collection's YAML body, re-indented under its name. Collections now
// live inside their storage instance, so tests scaffold them this way instead
// live inside their base, so tests scaffold them this way instead
// of one file per collection.
func storageLocal(collections map[string]string) string {
return projecttest.LocalStorage(collections)
func baseLocal(collections map[string]string) string {
return projecttest.LocalBase(collections)
}

// writeConfigDir writes the two-schema book-and-person project (book and
Expand All @@ -87,7 +87,7 @@ func writeConfigDir(t *testing.T) string {
"config.yaml": schemaFormatJSON,
"schemas/book.json": bookSchemaFixture,
"schemas/person.json": personSchemaFixture,
"storage/local.yaml": storageLocal(map[string]string{
"bases/local.yaml": baseLocal(map[string]string{
"books": "path: notes/books\nschema: book\n",
"people": "path: notes/people\nschema: person\n",
}),
Expand Down
32 changes: 16 additions & 16 deletions cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,24 @@ import (
// the available knobs.
const scaffoldConfig = `# katalyst project configuration.
#
# Schemas live in .katalyst/schemas/<name>.yaml. Storage instances live in
# .katalyst/storage/<name>.yaml, and each instance declares the collections it
# maps. The settings below are optional and shown at their defaults; uncomment
# to change them.
# Schemas live in .katalyst/schemas/<name>.yaml. Bases live in
# .katalyst/bases/<name>.yaml, and each base declares the collections it maps.
# The settings below are optional and shown at their defaults; uncomment to
# change them.
#
# schemas:
# discovery: convention # convention | explicit
# format: yaml # yaml | json | both
# storage:
# bases:
# discovery: convention
# format: yaml
`

// scaffoldLocalStorage is the default storage instance written by init: the
// local filesystem rooted at the project. There is no implicit instance,
// this file is what makes the default explicit. Collections are declared
// inline here (or split into .katalyst/storage/local/<name>.yaml).
const scaffoldLocalStorage = `# The default storage instance: the local filesystem, rooted at the project.
// scaffoldLocalBase is the default base written by init: the local filesystem
// rooted at the project. There is no implicit base, this file is what makes the
// default explicit. Collections are declared inline here (or split into
// .katalyst/bases/local/<name>.yaml).
const scaffoldLocalBase = `# The default base: the local filesystem, rooted at the project.
# Declare collections under "collections:", e.g.
#
# collections:
Expand Down Expand Up @@ -68,21 +68,21 @@ func newInitCmd() *cobra.Command {
return usageErr(fmt.Sprintf("%s already exists; refusing to overwrite", katalystDir))
}

for _, sub := range []string{"schemas", "storage"} {
for _, sub := range []string{"schemas", "bases"} {
rel := filepath.Join(project.Dir, sub)
if err := os.MkdirAll(filepath.Join(target, rel), 0o755); err != nil {
return err
}
fmt.Fprintf(cmd.OutOrStdout(), "created %s/\n", rel)
}

// Write the default storage instance explicitly; katalyst never
// synthesizes one at runtime.
storageRel := filepath.Join(project.Dir, "storage", "local.yaml")
if err := os.WriteFile(filepath.Join(target, storageRel), []byte(scaffoldLocalStorage), 0o644); err != nil {
// Write the default base explicitly; katalyst never synthesizes one
// at runtime.
baseRel := filepath.Join(project.Dir, "bases", "local.yaml")
if err := os.WriteFile(filepath.Join(target, baseRel), []byte(scaffoldLocalBase), 0o644); err != nil {
return err
}
fmt.Fprintf(cmd.OutOrStdout(), "created %s\n", storageRel)
fmt.Fprintf(cmd.OutOrStdout(), "created %s\n", baseRel)

cfgRel := filepath.Join(project.Dir, "config.yaml")
if err := os.WriteFile(filepath.Join(target, cfgRel), []byte(scaffoldConfig), 0o644); err != nil {
Expand Down
6 changes: 3 additions & 3 deletions cmd/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ func TestInit_preparesKatalystDir(t *testing.T) {
for _, want := range []string{
".katalyst",
".katalyst/schemas",
".katalyst/storage",
".katalyst/storage/local.yaml",
".katalyst/bases",
".katalyst/bases/local.yaml",
".katalyst/config.yaml",
} {
if _, err := os.Stat(filepath.Join(dir, want)); err != nil {
Expand All @@ -39,7 +39,7 @@ func TestInit_writesNoExampleContent(t *testing.T) {
"schemas",
"notes",
".katalyst/schemas/book.yaml",
".katalyst/storage/local/notes.yaml",
".katalyst/bases/local/notes.yaml",
} {
if _, err := os.Stat(filepath.Join(dir, unwanted)); err == nil {
t.Errorf("did not expect %s to exist", unwanted)
Expand Down
2 changes: 1 addition & 1 deletion cmd/inspect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func TestInspect_rawPathRunsSourceLayer(t *testing.T) {

func TestInspect_collectionLayerWhenConfigured(t *testing.T) {
dir := t.TempDir()
writeFile(t, dir, ".katalyst/storage/local.yaml", `type: filesystem
writeFile(t, dir, ".katalyst/bases/local.yaml", `type: filesystem
root: .
collections:
notes:
Expand Down
2 changes: 1 addition & 1 deletion cmd/item_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func setupItemRepo(t *testing.T) string {
"config.yaml": schemaFormatJSON,
"schemas/book.json": bookSchemaFixture,
"schemas/strict-book.json": strictBookSchemaFixture,
"storage/local.yaml": storageLocal(map[string]string{"notes": objectNotesConfig}),
"bases/local.yaml": baseLocal(map[string]string{"notes": objectNotesConfig}),
})
chdir(t, dir)
return dir
Expand Down
2 changes: 1 addition & 1 deletion cmd/testdata/snapshots/help/check.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
check parses each selected item's frontmatter (YAML, TOML, or JSON)
and runs the checks configured for its collection under .katalyst/storage/.
and runs the checks configured for its collection under .katalyst/bases/.

Selectors (see docs/content/deep-dives/domain-model/_index.md):

Expand Down
4 changes: 2 additions & 2 deletions docs/content/contributing/how-we-document.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ The durable home for everything a user needs, organized by
- **`reference/`:** information-oriented lookup: configuration, the
generated check-type reference, the glossary, the command surface.
- **`deep-dives/`:** understanding-oriented "why" (the Diátaxis *explanation*
quadrant): the vision and scope, the domain model, the storage layer,
progressive operations, and **design rationale at the behavioral altitude** -
quadrant): the vision and scope, the domain model, bases, progressive
operations, and **design rationale at the behavioral altitude** -
any *why* a user can observe, whatever subsystem it touches. A short **Why
Katalyst?** orientation page sits at the top level. The narrower *why* that
only matters once you are reading a package's code lives with that code (see
Expand Down
2 changes: 1 addition & 1 deletion docs/content/contributing/how-we-plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ document]({{< relref "how-we-document.md" >}}) for what belongs where:
- **`docs/reference/glossary.md`:** new vocabulary.
- **`README.md`:** pointer/overview updates.

Evergreen deep-dive docs (the storage layer, progressive operations) and the
Evergreen deep-dive docs (bases, progressive operations) and the
per-package `AGENTS.md` files are *not* specs and don't get retired: they're
updated in place.

Expand Down
4 changes: 2 additions & 2 deletions docs/content/deep-dives/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ Understanding-oriented discussion of the *why* behind Katalyst: the
[domain model]({{< relref "domain-model/_index.md" >}}) the tool is built on,
and the deeper design discussions that no single page or package owns: how
[checks work]({{< relref "domain-model/checks.md" >}}) and the libraries that
run them, how the [storage layer]({{< relref "domain-model/storage.md" >}})
maps stores onto the model, and how operations grow richer as a backend's
run them, how [bases]({{< relref "domain-model/storage.md" >}}) map stores onto
the model, and how operations grow richer as a backend's
capabilities increase. For the short version, start with
[Welcome]({{< relref "../welcome.md" >}}).

Expand Down
2 changes: 1 addition & 1 deletion docs/content/deep-dives/domain-model/checks.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ real out-of-process library exists.
lifecycle, the schema resolver, and the validation result.
- The [glossary]({{< relref "../../reference/glossary.md" >}}) for the canonical
terms (check type, check instance, CheckLibrary, schema, violation).
- The [storage layer]({{< relref "storage.md" >}}) for the collection and item
- The [Bases]({{< relref "storage.md" >}}) for the collection and item
identities checks run against, and the inspector that is a check's descriptive
dual.
- `go doc ./internal/checks` for the code-level engine contract.
Loading
Loading