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
116 changes: 34 additions & 82 deletions scripts/spawn.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@
# Agent-discovery symlinks are install artifacts (recreated by the
# PyAutoBrain installer), not source content — drop them from the template.
(".claude/*", "DROP"), (".codex/*", "DROP"),
# Instance branding:
("logo.png", "DROP"),
]

MEMORY_RULES = [
Expand All @@ -73,15 +75,16 @@
("Makefile", "KEEP"), ("LICENSE", "KEEP"), ("CONTRIBUTING.md", "KEEP"),
("AGENTS.md", "KEEP"), ("CLAUDE.md", "KEEP"), (".gitignore", "KEEP"),
("bibliography/*", "EMPTY"),
("*_wiki/*", "DROP"),
(".github/*", "KEEP_SUB"),
# The shared wiki schema is template content; the sub-wikis are instance
# content (the generator stamps an empty wiki/example/ instead).
("wiki/CLAUDE.md", "KEEP"),
("wiki/*", "DROP"),
# Instance branding:
("logo.png", "DROP"),
("index.md", "SPECIAL:memory_index"),
("reading-queue.md", "EMPTY"),
("README.md", "SPECIAL:memory_readme"),
("*.bib", "DROP"),
# Legacy paper-notes families predating the sub-wikis:
("CTI/*", "DROP"), ("DarkMatterModels/*", "DROP"), ("Medical/*", "DROP"),
("LightProFFits/*", "DROP"), ("euclid.sty", "DROP"), ("cticomments", "DROP"),
("Hubble1926*", "DROP"), ("devaucoleurs1948*", "DROP"),
]

# Instance-content tokens that must NEVER appear in a generated template.
Expand Down Expand Up @@ -155,13 +158,13 @@
# PyAutoMemory — index

Top-level navigation across the sub-wikis. Every sub-wiki is self-contained
and follows the schema defined in `example_wiki/CLAUDE.md`.
and follows the schema defined in `wiki/CLAUDE.md`.

| Wiki | Covers |
|------|--------|
| [`example_wiki/`](example_wiki/index.md) | An empty example — copy it to start your first real sub-wiki. |
| [`wiki/example/`](wiki/example/index.md) | An empty example — copy it to start your first real sub-wiki. |

Add sub-wikis beside `example_wiki/` following the same schema, and give
Add sub-wikis beside `wiki/example/` following the same schema, and give
each a row here.
"""

Expand All @@ -175,90 +178,33 @@

| Piece | What it is |
|-------|------------|
| `example_wiki/` | An empty sub-wiki demonstrating the schema — copy it per domain. |
| `wiki/example/` | An empty sub-wiki demonstrating the schema — copy it per domain. |
| `wiki/CLAUDE.md` | The shared schema every sub-wiki inherits. |
| `bibliography/` | Canonical BibTeX metadata every wiki claim cites against. |
| `reading-queue.md` | What is waiting to be read and filed. |

New knowledge updates the metadata and the claim support together, then
passes `make validate-literature-citations`. The wiki schema is defined in
`example_wiki/CLAUDE.md` and inherited by every sub-wiki. How agents should
passes `make validate`. The wiki schema is defined in
`wiki/CLAUDE.md` and inherited by every sub-wiki. How agents should
read this repo: [AGENTS.md](AGENTS.md).

This repo was generated by `spawn` from the live PyAutoScientist organism —
see <https://pyautoscientist.readthedocs.io>.
"""

GENERIC_WIKI_SCHEMA = """\
# example_wiki — schema + usage rules

This sub-wiki gives an AI assistant broad context for one domain. It follows
Karpathy's "LLM Wiki" pattern: concise, cross-linked pages read at query
time, while canonical citation metadata lives separately in
`../bibliography/`. Copy this folder to start a real sub-wiki; every sibling
wiki inherits this schema.

## Layout

```
PyAutoMemory/
├── example_wiki/ # one domain — the compiled wiki (in git)
│ ├── CLAUDE.md # this file — schema + usage rules
│ ├── index.md # the wiki's own navigation
│ ├── concepts/ # one topic per page — the science
│ ├── entities/ # named things: surveys, instruments, software
│ └── sources/ # compact claim support (one source = one section)
└── bibliography/ # canonical BibTeX, aliases, citation tooling
```

Sources are the ground truth; wiki pages are syntheses. If they disagree,
update the wiki and log the change.

## Page types

| Type | Folder | Scope |
|---------|-------------|--------------------------------------------------|
| Concept | `concepts/` | One idea per page — split pages that cover two |
| Entity | `entities/` | One named thing (survey, instrument, code, team) |
| Sources | `sources/` | Claim support for one topic, one section/source |
| Index | root | Navigation and provenance |

## Conventions

- File names are lowercase kebab-case; one concept per concept page.
- Wiki-internal links use `[[page-slug]]`; a link with no target yet is
fine — it marks a future page.
- External references use verified DOI/arXiv/journal metadata, never a
local path; canonical keys live in `../bibliography/` and are validated
by `make validate-literature-citations`.
- Every page starts with YAML frontmatter: `title`, `type`
(concept | entity | sources | meta), `topics`, optional `sources`, and
`status` (stub | drafted | reviewed).

## Concept page structure

`# Title` → `## TL;DR` (one quotable paragraph) → `## What it is` →
`## Why it matters for your project` → `## Key results from the
literature` (each bullet ends with a `([[source-slug]])` link) →
`## See also`.

## Source-collection page structure

One H2 section per source: the canonical BibTeX key, the reference, the
concepts it supports, a short **Supports:** bullet list, and **Use when /
Do not use for** guidance. Keep entries to 2–5 support bullets; never copy
abstracts or infer claims from filenames — add a TODO when support is
unverified.

## How an assistant should use this wiki

Open `index.md` first; follow the relevant concept/entity page; follow the
source entry for claim scope and its canonical key for metadata; if
support is unclear, read the public source and add a TODO rather than
guessing.
EXAMPLE_WIKI_CLAUDE = """\
# example wiki — scope

An empty sub-wiki demonstrating the layout. Copy `wiki/example/` to
`wiki/<your-domain>/` to start a real sub-wiki. All schema rules — page
types, naming, `[[wiki-links]]`, frontmatter, page structures, status
flags — are defined once in [`../CLAUDE.md`](../CLAUDE.md) and inherited;
a sub-wiki's own `CLAUDE.md` (this file) records only its scope: what the
domain covers, and which adjacent topics link out to sibling wikis.
"""

EXAMPLE_WIKI_INDEX = """\
# example_wiki — index
# example wiki — index

The sub-wiki's own navigation. Sources are summarised under `sources/`
(one page per paper/resource, see the stub); concept pages live beside
Expand Down Expand Up @@ -386,6 +332,10 @@ def empty_body(src):
first = src.read_text(errors="replace").splitlines()[0]
except IndexError:
first = ""
if src.suffix in {".yaml", ".yml"}:
# YAML consumers parse every non-# line — an HTML comment would read
# as content (e.g. a bibkey alias with a missing target).
return first + "\n\n# emptied by spawn; schema: REFERENCE.md\n"
return first + "\n\n<!-- emptied by spawn; schema: REFERENCE.md -->\n"


Expand Down Expand Up @@ -452,15 +402,17 @@ def generate_memory(memory_root, out_dir):
dest.parent.mkdir(parents=True, exist_ok=True)
if action == "KEEP":
shutil.copy2(src, dest)
elif action == "KEEP_SUB":
dest.write_text(substitute_owner(src.read_text(errors="replace")))
elif action == "EMPTY":
dest.write_text(empty_body(src))
elif action == "SPECIAL:memory_index":
dest.write_text(MEMORY_INDEX_TEMPLATE)
elif action == "SPECIAL:memory_readme":
dest.write_text(TEMPLATE_README_BANNER + MEMORY_README_TEMPLATE)
wiki = out_dir / "example_wiki"
wiki = out_dir / "wiki" / "example"
wiki.mkdir(parents=True, exist_ok=True)
(wiki / "CLAUDE.md").write_text(GENERIC_WIKI_SCHEMA)
(wiki / "CLAUDE.md").write_text(EXAMPLE_WIKI_CLAUDE)
(wiki / "index.md").write_text(EXAMPLE_WIKI_INDEX)
(wiki / "sources").mkdir(exist_ok=True)
(wiki / "sources" / "EXAMPLE_stub.md").write_text(EXAMPLE_WIKI_STUB)
Expand Down