{
  "catalog": "doc-atoms",
  "version": "0.1.0",
  "built_at": "2026-05-28T20:51:45+00:00",
  "atoms": [
    {
      "schema": "https://doc-atoms.com/schemas/atom-v1.json",
      "type": "explanation",
      "id": "atoms-vs-compositions",
      "version": "0.1.0",
      "name": "Atoms vs Compositions — What Goes Where?",
      "summary": "Explains the difference between atoms (reusable primitives) and compositions (assembled higher-order artifacts), and the decision rule for where content belongs.",
      "audience": [
        "contributor",
        "developer"
      ],
      "tags": [
        "catalog-design",
        "composition-atoms",
        "architecture",
        "atoms-ecosystem"
      ],
      "explanation_profile": {
        "concept": "Atom primitives vs composition assemblies in the *-atoms ecosystem",
        "depth": "overview"
      },
      "body": {
        "format": "markdown",
        "inline": "## The core distinction\n\nIn the *-atoms ecosystem, **atoms** are the smallest reusable units of a domain — they have a single clear purpose, a stable ID, and no dependencies on other atoms except through explicit reference. **Compositions** are assemblies of atoms into higher-order artifacts that serve a specific deployment context.\n\nThink of atoms as vocabulary and compositions as sentences.\n\n## Atoms\n\nAn atom is:\n- Stable: its `id` and `version` never change once published\n- Purposeful: it solves exactly one problem in its domain\n- Reusable: it can appear in multiple compositions without modification\n- Self-contained: everything a consumer needs to use it is in the atom JSON itself\n\nExamples: a persona atom defining an agent's identity, a prompt fragment capturing a reasoning instruction, a key-rotation policy document.\n\n## Compositions\n\nA composition is:\n- Assembled: it references atoms by ID and version, never embeds them\n- Context-specific: it exists to satisfy a deployment requirement, not to be reused broadly\n- Versioned separately from its constituent atoms\n- Owned by composition-atoms, not by the source catalog\n\nA composition might assemble a persona atom, three prompt fragments, a context specification, and a tool manifest to produce a fully-specified agent for a particular product integration.\n\n## Decision rule\n\nAsk: *Is this content reusable independently of its deployment context?*\n\n- Yes → atom in the appropriate catalog\n- No → composition in composition-atoms\n\nIf you find yourself writing an atom that only makes sense when combined with another specific atom, that is a sign the two belong in a composition rather than as atoms.\n\n## Common mistakes\n\n**Embedding instead of referencing.** Compositions MUST reference atoms by ID, not copy their content. Copying defeats the stability guarantee — if the atom changes, embedded copies drift silently.\n\n**Putting deployment config in an atom.** Configuration that changes per-environment (API endpoints, tenant IDs, feature flags) belongs in deployment manifests or runtime config, not in atoms.\n\n**Creating a composition atom in the source catalog.** Compositions belong in composition-atoms. A persona catalog should not contain assembled agents."
      }
    },
    {
      "schema": "https://doc-atoms.com/schemas/atom-v1.json",
      "type": "explanation",
      "id": "persona-vs-prompt-atoms",
      "version": "0.1.0",
      "name": "Persona Atoms vs Prompt Atoms — The Distinction",
      "summary": "Explains that persona-atoms defines identity (who the agent IS) while prompt-atoms defines fragments (what the agent SAYS), and why keeping them separate enables better composition.",
      "audience": [
        "contributor",
        "developer",
        "operator"
      ],
      "tags": [
        "persona-atoms",
        "prompt-atoms",
        "catalog-design",
        "agent-assembly"
      ],
      "explanation_profile": {
        "concept": "Separation of agent identity from prompt content in the atoms ecosystem",
        "depth": "overview"
      },
      "body": {
        "format": "markdown",
        "inline": "## The question\n\nWhen you are creating content for an AI agent, you will repeatedly face the question: does this go in persona-atoms or prompt-atoms? The answer turns on a single distinction: **identity vs utterance**.\n\n## Persona atoms: who the agent IS\n\nA persona atom defines the stable, deployment-independent identity of an agent:\n- Its role and purpose\n- Its communication style and tone\n- Its behavioral constraints and invariants\n- What it will never do, regardless of instruction\n\nA persona does not change based on what task the agent is performing. If you find yourself writing a \"persona\" that is really just a system prompt for a specific workflow, that content belongs in prompt-atoms.\n\n**Persona atom example:** `customer-success-specialist` — a persona for an agent that handles customer queries with a warm, precise communication style and never commits to refunds without escalation approval.\n\n## Prompt atoms: what the agent SAYS\n\nA prompt atom (also called a prompt fragment) is a discrete, reusable piece of instructional text:\n- A reasoning instruction (\"think step by step before answering\")\n- A format constraint (\"always respond in JSON matching the following schema\")\n- A safety instruction (\"never include PII in your response\")\n- A task-specific instruction (\"when the user asks about billing, route to the billing FAQ\")\n\nPrompt fragments are composable: you combine several to build the instructional layer of an agent's system prompt. They are independent of identity — the same reasoning instruction can apply to many different personas.\n\n## Why the separation matters\n\n**Reuse.** A persona can be composed with many different prompt fragment sets across products. A prompt fragment can be composed with many different personas. Collapsing them into a single blob makes both harder to reuse and harder to audit.\n\n**Stability contracts.** Personas carry stronger stability guarantees than prompt fragments. Identity changes infrequently; instructional fragments are tuned more often. Keeping them separate lets each evolve at its own pace without entangling versioning.\n\n**Auditability.** When an agent produces an unexpected behavior, you want to know: is this an identity issue (wrong persona) or an instruction issue (wrong fragment)? The separation makes the diagnostic tractable.\n\n## Decision rule\n\n| Content | Catalog |\n|---|---|\n| Role, voice, constraints, behavioral invariants | persona-atoms |\n| Instructions, format rules, task guidance, safety guardrails | prompt-atoms |\n| Assembled agent with persona + fragments | composition-atoms |"
      }
    },
    {
      "schema": "https://doc-atoms.com/schemas/atom-v1.json",
      "type": "explanation",
      "id": "what-is-a-grammar-atom",
      "version": "0.1.0",
      "name": "What is a Grammar Atom?",
      "summary": "Explains the grammar atom concept: a TOML file in schema-atoms that defines the structural contract for a catalog's primary atom class.",
      "audience": [
        "contributor",
        "developer"
      ],
      "tags": [
        "schema-atoms",
        "grammar",
        "contracts",
        "catalog-design"
      ],
      "explanation_profile": {
        "concept": "Grammar atoms as structural contracts for catalog atom classes",
        "depth": "intermediate"
      },
      "body": {
        "format": "markdown",
        "inline": "## What is a grammar atom?\n\nA *grammar atom* is a special atom type in schema-atoms that defines the structural contract for a catalog's primary atom class. Where a JSON Schema atom specifies the machine-readable validation rules, a grammar atom provides the human-readable narrative: what fields exist, why they exist, what the invariants are, and how the schema is expected to evolve.\n\n## Why they exist\n\nThe *-atoms ecosystem has 25+ catalogs, each with its own primary atom type. Without grammar atoms, the structural contracts for each type would be scattered across READMEs, ADRs, and comments inside JSON Schema files — discoverable only if you know where to look. Grammar atoms make the contracts first-class citizens of the ecosystem: indexable, linkable, and evolvable through the same PR-and-release process as any other atom.\n\n## Anatomy of a grammar atom\n\nA grammar atom in schema-atoms looks like this:\n\n```json\n{\n  \"schema\": \"https://schema-atoms.com/schemas/atom-v1.json\",\n  \"type\": \"grammar\",\n  \"id\": \"doc-atoms-v1\",\n  \"version\": \"0.1.0\",\n  \"name\": \"doc-atoms v1 atom grammar\",\n  \"summary\": \"Structural contract for doc-atoms primary atom class.\",\n  \"body\": {\n    \"format\": \"markdown\",\n    \"inline\": \"## Required fields\\n...\"\n  }\n}\n```\n\nThe `id` follows the convention `<catalog>-v<major>` to make versioning explicit at a glance.\n\n## Grammar atoms vs JSON Schema atoms\n\n| | Grammar atom | JSON Schema atom |\n|---|---|---|\n| Format | JSON (body is Markdown) | JSON (body references a `.json` schema file) |\n| Purpose | Human-readable narrative contract | Machine-readable validation rules |\n| Consumer | Contributors, reviewers, AI tooling | Validators, CI, code generators |\n| Evolution | Prose amendments, new sections | Additive schema changes with version bump |\n\n## Relationship to catalogs\n\nEach catalog's grammar atom lives in schema-atoms, not in the catalog itself. This separation keeps schema-atoms as the single authoritative registry of structural contracts while keeping catalog repos focused on their own atom content. A catalog's CI pipeline references its grammar atom ID when running the validator."
      }
    },
    {
      "schema": "https://doc-atoms.com/schemas/atom-v1.json",
      "type": "how-to",
      "id": "add-a-label-alias",
      "version": "0.1.0",
      "name": "Add a Label Alias to repo-standards",
      "summary": "How to add a new label mapping to label-aliases.yml in convergent-systems-co/repo-standards so the label-cleanup workflow can migrate it across all org repos.",
      "audience": [
        "contributor",
        "operator"
      ],
      "tags": [
        "repo-standards",
        "labels",
        "github-actions",
        "org-maintenance"
      ],
      "how_to_profile": {
        "task": "Add a new label alias mapping to the org-wide label cleanup workflow",
        "applies_to": [
          "convergent-systems-co/repo-standards",
          "all org repos"
        ]
      },
      "body": {
        "format": "markdown",
        "inline": "## When to do this\n\nDo this when you have renamed a label (or want to retire an old label in favor of a new one) and need the change to propagate automatically to all repositories in the `convergent-systems-co` org.\n\n## Steps\n\n### 1. Locate label-aliases.yml\n\n```bash\ngh api repos/convergent-systems-co/repo-standards/contents/label-aliases.yml \\\n  --jq '.download_url' | xargs curl -s\n```\n\nOr clone repo-standards and open `label-aliases.yml` directly.\n\n### 2. Add your mapping\n\nThe file format is a list of `{from, to}` pairs:\n\n```yaml\naliases:\n  - from: \"old-label-name\"\n    to: \"new-label-name\"\n  - from: \"bug\"\n    to: \"type/bug\"\n```\n\nAdd your new mapping at the end of the list. The `from` value is the label to be replaced; `to` is the label it should become.\n\n### 3. Ensure the target label exists in the label definition file\n\nCheck that `to` is listed in `labels.yml` (or equivalent) in repo-standards. If not, add it there too. The cleanup workflow applies aliases only to labels that exist in the canonical definition.\n\n### 4. Commit and open a PR\n\n```bash\ngit add label-aliases.yml\ngit commit -m \"chore: add alias <old> -> <new>\"\ngit push origin feat/label-alias-<new>\ngh pr create --repo convergent-systems-co/repo-standards\n```\n\n### 5. Merge and observe propagation\n\nAfter the PR merges, the label-cleanup workflow runs on a schedule (or you can trigger it manually via `gh workflow run`). Confirm propagation on a sample repo:\n\n```bash\ngh label list --repo convergent-systems-co/<sample-repo> | grep <new-label-name>\n```\n\n## Notes\n\n- Label aliases are additive. Removing a mapping from the file does not undo a completed migration.\n- If a repo has open issues or PRs with the old label, GitHub will update them automatically when the label is renamed via the API."
      }
    },
    {
      "schema": "https://doc-atoms.com/schemas/atom-v1.json",
      "type": "how-to",
      "id": "configure-terraform-backend",
      "version": "0.1.0",
      "name": "Configure the R2 Terraform Backend",
      "summary": "How to fill in backend.tf for a *-atoms repo using the shared cs-tfstate R2 bucket so Terraform state is centralized and not committed to the repo.",
      "audience": [
        "contributor",
        "operator"
      ],
      "tags": [
        "terraform",
        "r2",
        "cloudflare",
        "infrastructure",
        "state"
      ],
      "how_to_profile": {
        "task": "Configure Terraform remote state backend pointing at the shared cs-tfstate R2 bucket",
        "applies_to": [
          "any *-atoms catalog with infra/ Terraform code"
        ]
      },
      "body": {
        "format": "markdown",
        "inline": "## When to do this\n\nDo this when setting up Terraform infrastructure for a new *-atoms catalog. Every catalog with an `infra/` directory uses the shared `cs-tfstate` R2 bucket for state storage rather than local state files.\n\n## Prerequisites\n\n- Terraform 1.6+\n- Access to the Cloudflare R2 credentials for `cs-tfstate` (stored in the \"Convergent Systems LLC\" 1Password vault)\n- The catalog's Cloudflare account ID\n\n## Steps\n\n### 1. Locate or create backend.tf\n\nIn the catalog's `infra/terraform/` directory, open (or create) `backend.tf`.\n\n### 2. Write the backend configuration\n\n```hcl\nterraform {\n  backend \"s3\" {\n    # Cloudflare R2 — S3-compatible\n    bucket = \"cs-tfstate\"\n    key    = \"<catalog-name>/terraform.tfstate\"\n\n    # R2 endpoint — replace <account-id> with the Cloudflare account ID\n    endpoint = \"https://<account-id>.r2.cloudflarestorage.com\"\n\n    # R2 uses access key / secret key from env vars:\n    # AWS_ACCESS_KEY_ID     = R2 token access key\n    # AWS_SECRET_ACCESS_KEY = R2 token secret key\n    region = \"auto\"\n\n    # Disable AWS-specific features not available on R2\n    skip_credentials_validation = true\n    skip_metadata_api_check     = true\n    skip_region_validation      = true\n    force_path_style            = true\n  }\n}\n```\n\nReplace `<catalog-name>` with the catalog's kebab-case name (e.g., `doc-atoms`). This ensures each catalog's state is stored in a separate key within the shared bucket.\n\n### 3. Set R2 credentials as environment variables\n\nNever hardcode credentials in `backend.tf`. Set them in your shell before running Terraform:\n\n```bash\nexport AWS_ACCESS_KEY_ID=\"$(op read 'op://Convergent Systems LLC/cs-tfstate-r2/access-key-id')\"\nexport AWS_SECRET_ACCESS_KEY=\"$(op read 'op://Convergent Systems LLC/cs-tfstate-r2/secret-access-key')\"\n```\n\nFor CI, these are set as GitHub Actions secrets at the org level (see `reference_cloudflare_pages_org_secrets`).\n\n### 4. Initialize Terraform\n\n```bash\ncd infra/terraform\nterraform init\n```\n\nExpected output: `Terraform has been successfully initialized!`\n\n### 5. Verify state storage\n\n```bash\nterraform plan\n```\n\nIf the backend is configured correctly, Terraform will read existing state (or show an empty state for a new catalog) without error.\n\n## Notes\n\n- The `key` value is the only field that varies between catalogs. All other backend settings are identical.\n- Do not commit `terraform.tfstate`, `.terraform/`, or `.terraform.lock.hcl` to the repo. Confirm `.gitignore` excludes them.\n- State for each catalog is isolated by key — a mistake in one catalog's Terraform run cannot affect another catalog's state."
      }
    },
    {
      "schema": "https://doc-atoms.com/schemas/atom-v1.json",
      "type": "how-to",
      "id": "create-a-grammar-atom",
      "version": "0.1.0",
      "name": "Create a Grammar Atom for a New Catalog Class",
      "summary": "How to add a grammar atom to schema-atoms/atoms/ for a new catalog class, registering its structural contract in the ecosystem.",
      "audience": [
        "contributor"
      ],
      "tags": [
        "schema-atoms",
        "grammar",
        "catalog-design",
        "authoring"
      ],
      "prerequisites": [
        "what-is-a-grammar-atom"
      ],
      "how_to_profile": {
        "task": "Register a new catalog's structural contract in schema-atoms",
        "applies_to": [
          "schema-atoms",
          "any new *-atoms catalog"
        ]
      },
      "body": {
        "format": "markdown",
        "inline": "## When to do this\n\nDo this when you are creating a new *-atoms catalog and need to register its primary atom class in schema-atoms so it is discoverable and formally contracted.\n\n## Steps\n\n### 1. Name the grammar atom\n\nGrammar atom IDs follow the convention `<catalog-name>-v<major>`. For example: `doc-atoms-v1`, `persona-atoms-v1`, `prompt-atoms-v1`.\n\n### 2. Create the atom file\n\nIn a worktree on schema-atoms:\n\n```bash\ntouch atoms/grammar/<catalog-name>-v1.json\n```\n\n### 3. Write the grammar atom JSON\n\n```json\n{\n  \"schema\": \"https://schema-atoms.com/schemas/atom-v1.json\",\n  \"type\": \"grammar\",\n  \"id\": \"<catalog-name>-v1\",\n  \"version\": \"0.1.0\",\n  \"name\": \"<Catalog name> v1 atom grammar\",\n  \"summary\": \"Structural contract for the <catalog name> primary atom class.\",\n  \"audience\": [\"contributor\", \"developer\"],\n  \"tags\": [\"<catalog-name>\", \"grammar\", \"schema\"],\n  \"body\": {\n    \"format\": \"markdown\",\n    \"inline\": \"## Required fields\\n\\n| Field | Type | Description |\\n|---|---|---|\\n| `schema` | string | Must be `https://<catalog-domain>/schemas/atom-v1.json` |\\n| `type` | string | The atom type enum value |\\n| `id` | string | Kebab-case identifier, unique within the catalog |\\n| `version` | string | SemVer string |\\n| `name` | string | Human-readable name |\\n\\n## Optional fields\\n\\n<List optional fields with their purpose>\\n\\n## Invariants\\n\\n<List the invariants that all atoms of this class must satisfy>\"\n  }\n}\n```\n\n### 4. Validate and sign\n\n```bash\npython3 scripts/validate.py\nnode scripts/sign-atoms.mjs\n```\n\n### 5. Update schema-atoms catalog.json\n\nAdd your grammar atom to `exports/catalog.json` in schema-atoms.\n\n### 6. Commit and open a PR against schema-atoms\n\n```bash\ngit add atoms/grammar/<catalog-name>-v1.json keys/ exports/catalog.json\ngit commit -m \"feat: add <catalog-name>-v1 grammar atom\"\ngit push origin feat/<catalog-name>-grammar\ngh pr create --repo convergent-systems-co/schema-atoms\n```\n\n## Notes\n\n- The grammar atom PR on schema-atoms and the seed PR on your new catalog can be opened in parallel, but schema-atoms should merge first so the grammar reference is resolvable before the catalog deploys."
      }
    },
    {
      "schema": "https://doc-atoms.com/schemas/atom-v1.json",
      "type": "reference-guide",
      "id": "atoms-ecosystem-overview",
      "version": "0.1.0",
      "name": "Atoms Ecosystem Reference",
      "summary": "Complete reference guide to the *-atoms ecosystem: catalogs, their domains, primary atom types, relationships, and key conventions.",
      "audience": [
        "contributor",
        "developer",
        "operator"
      ],
      "tags": [
        "atoms-ecosystem",
        "reference",
        "catalogs",
        "architecture"
      ],
      "reference_profile": {
        "covers": [
          "catalog inventory",
          "atom types",
          "catalog relationships",
          "shared infrastructure",
          "naming conventions",
          "release lifecycle"
        ]
      },
      "body": {
        "format": "markdown",
        "inline": "## What is the *-atoms ecosystem?\n\nThe *-atoms ecosystem is a collection of versioned, content-addressable catalog repositories hosted under the `convergent-systems-co` GitHub organization. Each catalog stores a distinct class of reusable primitives. Catalogs are deployed as static sites on Cloudflare Pages and are served from their own domain.\n\n## Catalog inventory\n\n| Catalog | Domain | Primary atom type | Description |\n|---|---|---|---|\n| schema-atoms | schema-atoms.com | json-schema, grammar | Normative JSON Schemas and grammar contracts for all catalogs |\n| doc-atoms | doc-atoms.com | tutorial, explanation, runbook, how-to, reference-guide | Explanatory documentation for the ecosystem itself |\n| persona-atoms | persona-atoms.com | persona | Stable AI agent identity definitions |\n| prompt-atoms | prompt-atoms.com | prompt-fragment | Reusable AI prompt fragments |\n| context-atoms | context-atoms.com | context | Context specifications for agent deployments |\n| composition-atoms | composition-atoms.com | composition | Assembled agents from persona + fragments + context + tools |\n| key-atoms | key-atoms.com | key | Cryptographic key references and rotation policies |\n| action-atoms | action-atoms.com | action | Discrete agent action definitions |\n| amendment-atoms | amendment-atoms.com | amendment | Governance amendment records |\n| constitution-atoms | constitution-atoms.com | constitution | Constitutional governance documents |\n| tool-atoms | tool-atoms.com | tool | Agent tool specifications and manifests |\n| channel-atoms | channel-atoms.com | channel | Communication channel definitions for agent integrations |\n| memory-atoms | memory-atoms.com | memory | Agent persistent memory specifications |\n| workflow-atoms | workflow-atoms.com | workflow | Multi-step workflow definitions |\n| event-atoms | event-atoms.com | event | Event schema definitions for async pipelines |\n\n## Shared conventions\n\n### Atom ID format\n\nAll atom IDs must match `^[a-z0-9][a-z0-9-]{1,62}[a-z0-9]$` — lowercase kebab-case, 3–64 characters. No slashes, no dots. IDs must be unique within a catalog.\n\n### Version format\n\nAll atoms use SemVer: `MAJOR.MINOR.PATCH` or `MAJOR.MINOR.PATCH-prerelease`. The version `0.1.0` is the canonical seed version for new atoms.\n\n### Schema field\n\nEvery atom includes a `schema` field set to the catalog's atom schema URL (e.g., `https://doc-atoms.com/schemas/atom-v1.json`). This field is the discriminator used by validators and tooling to identify the expected schema.\n\n### Required fields\n\nEvery atom, regardless of type, requires: `schema`, `type`, `id`, `version`, `name`.\n\n## Shared infrastructure\n\n| Component | Details |\n|---|---|\n| Hosting | Cloudflare Pages — one Pages project per catalog |\n| State | Cloudflare R2 bucket `cs-tfstate` — shared Terraform state, keyed per catalog |\n| Signing | 1Password vault \"Convergent Systems LLC\" — root signing key for all catalogs |\n| CI secrets | `CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_ACCOUNT_ID` at org level |\n| Label standards | `convergent-systems-co/repo-standards` — canonical label definitions and aliases |\n| Infrastructure provisioning | Terraform in each catalog's `infra/` directory |\n\n## Catalog relationships\n\n```\nschema-atoms  ──→  all catalogs (provides JSON Schema validation contracts)\npersona-atoms ─┐\nprompt-atoms  ─┤\ncontext-atoms ─┼──→  composition-atoms (assembled into agent compositions)\ntool-atoms    ─┘\ncomposition-atoms  ──→  deployed agents (consumed by runtime pipelines)\ndoc-atoms     ──→  contributors and operators (ecosystem documentation)\n```\n\n## Release lifecycle\n\n1. Develop on a `feat/*` branch in a worktree\n2. Validate atoms: `python3 scripts/validate.py`\n3. Sign atoms: `node scripts/sign-atoms.mjs`\n4. Open PR, wait for CI green\n5. Merge with merge commit (no squash)\n6. Tag: `git tag vX.Y.Z && git push origin vX.Y.Z`\n7. Release workflow deploys to Cloudflare Pages and publishes GitHub Release\n\n## Naming conventions\n\n| Artifact | Convention | Example |\n|---|---|---|\n| Catalog repo | `<domain>-atoms` | `persona-atoms` |\n| Atom ID | kebab-case | `customer-success-specialist` |\n| Grammar atom ID | `<catalog>-v<major>` | `persona-atoms-v1` |\n| Worktree | `feat/<slug>` | `feat/v0.1-seed` |\n| Release tag | `vMAJOR.MINOR.PATCH` | `v0.1.0` |"
      }
    },
    {
      "schema": "https://doc-atoms.com/schemas/atom-v1.json",
      "type": "runbook",
      "id": "cut-a-release",
      "version": "0.1.0",
      "name": "Cut a Catalog Release",
      "summary": "Runbook for tagging and pushing a vX.Y.Z release to trigger the release workflow, update the Cloudflare Pages deployment, and publish a GitHub release.",
      "audience": [
        "contributor",
        "operator"
      ],
      "tags": [
        "release",
        "git-tags",
        "cloudflare-pages",
        "github-actions"
      ],
      "runbook_profile": {
        "scenario": "The main branch is green and you are ready to cut an official release of a *-atoms catalog.",
        "severity": "info",
        "escalation": "If the release workflow fails after tagging, check the Actions run log. For Cloudflare Pages deployment failures, check the Cloudflare dashboard for the affected Pages project."
      },
      "body": {
        "format": "markdown",
        "inline": "## Prerequisites\n\n- You are on the `main` branch with a clean working tree\n- All atoms are signed (see *Sign Atoms with the Root Key* runbook)\n- `exports/catalog.json` is populated and `built_at` is current\n- CI is green on the latest main commit\n- You know the next version (SemVer: vX.Y.Z)\n\n## Procedure\n\n### Step 1: Confirm you are on a clean main\n\n```bash\ngit checkout main && git pull origin main\ngit status  # must be clean\n```\n\n### Step 2: Update CHANGELOG.md\n\nAdd a new section for the release version. Follow the existing format in the repo. Stage and commit:\n\n```bash\ngit add CHANGELOG.md\ngit commit -m \"chore: update changelog for vX.Y.Z\"\ngit push origin main\n```\n\n### Step 3: Tag the release\n\n```bash\ngit tag vX.Y.Z\ngit push origin vX.Y.Z\n```\n\nPushing the tag triggers the `.github/workflows/release.yml` workflow.\n\n### Step 4: Monitor the release workflow\n\n```bash\ngh run list --repo convergent-systems-co/<catalog> --limit 5\n```\n\nOr watch in the GitHub Actions UI. The workflow will:\n1. Validate all atoms\n2. Verify signatures\n3. Build the static site\n4. Deploy to Cloudflare Pages\n5. Publish a GitHub Release with the catalog export as an asset\n\n### Step 5: Confirm the deployment\n\n```bash\ncurl -s https://<catalog-domain>/exports/catalog.json | jq '.version'\n```\n\nExpected: `\"vX.Y.Z\"` (or the version string you tagged).\n\n### Step 6: Confirm the GitHub Release\n\n```bash\ngh release view vX.Y.Z --repo convergent-systems-co/<catalog>\n```\n\n## Rollback\n\nIf the release is defective after tagging, do NOT delete the tag — this would break any downstream references. Instead:\n\n1. Fix the defect in a new commit on main\n2. Tag a patch release (e.g., `v0.1.1`)\n3. Note the defect in `CHANGELOG.md` under the new version\n\n## Notes\n\n- Release tags are immutable in the *-atoms ecosystem. Once pushed, a tag is never force-updated.\n- The `CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_ACCOUNT_ID` secrets are configured at the `convergent-systems-co` org level and are available to all catalog repos automatically."
      }
    },
    {
      "schema": "https://doc-atoms.com/schemas/atom-v1.json",
      "type": "runbook",
      "id": "run-the-validator",
      "version": "0.1.0",
      "name": "Run the Catalog Validator",
      "summary": "Runbook for running python3 scripts/validate.py (or validate_atoms.py) in any *-atoms catalog to confirm all atoms conform to the catalog schema.",
      "audience": [
        "contributor",
        "operator"
      ],
      "tags": [
        "validator",
        "ci",
        "quality",
        "atoms-ecosystem"
      ],
      "runbook_profile": {
        "scenario": "You need to confirm that all atoms in a catalog are valid before committing, opening a PR, or cutting a release.",
        "severity": "info",
        "escalation": "If the validator script itself errors (not an atom validation failure), open an issue in the affected catalog repo and ping the catalog maintainer."
      },
      "body": {
        "format": "markdown",
        "inline": "## Prerequisites\n\n- Python 3.10+\n- `pip install jsonschema` (or `pip install -r requirements.txt` if the repo has one)\n- Working directory is the catalog repo root\n\n## Procedure\n\n### Step 1: Confirm the validator script exists\n\n```bash\nls scripts/validate.py 2>/dev/null || ls scripts/validate_atoms.py 2>/dev/null\n```\n\nIf neither exists, the catalog may use a different validator. Check the `Makefile` for a `validate` or `lint` target.\n\n### Step 2: Run the validator\n\n```bash\npython3 scripts/validate.py\n```\n\nor, if the script is named differently:\n\n```bash\npython3 scripts/validate_atoms.py\n```\n\n### Step 3: Read the output\n\n**All atoms valid — expected output:**\n\n```\nValidating atoms...\n  ✓ atoms/tutorial/how-to-create-a-json-schema-atom.json\n  ✓ atoms/explanation/what-is-a-grammar-atom.json\n  ...\nAll N atoms valid.\n```\n\n**Validation failure — example output:**\n\n```\nValidating atoms...\n  ✗ atoms/tutorial/how-to-create-a-json-schema-atom.json\n    Additional properties are not allowed ('description' was unexpected)\n```\n\n### Step 4: Fix failures\n\nEach failure message names the file and the validation error. Common causes:\n\n| Error | Fix |\n|---|---|\n| `Additional properties are not allowed` | Remove the unexpected field or update the schema |\n| `'id' does not match pattern` | Ensure id is kebab-case, no slashes |\n| `'version' is a required property` | Add the `version` field |\n| `'type' is not one of [...]` | Check the enum values in atom-v1.json |\n\n### Step 5: Re-run after fixes\n\nRepeat until all atoms pass. The CI pipeline runs the same validator — local passage is a prerequisite for a green build.\n\n## Via Makefile (if available)\n\n```bash\nmake validate\n```\n\nCheck the `Makefile` target definition to understand what it runs if you need to debug.\n\n## Notes\n\n- The validator checks all `.json` files under `atoms/` against `schemas/atom-v1.json`.\n- Files in `exports/`, `keys/`, and `schemas/` are not validated as atoms.\n- The CI workflow in `.github/workflows/` runs this step automatically on every push and PR."
      }
    },
    {
      "schema": "https://doc-atoms.com/schemas/atom-v1.json",
      "type": "runbook",
      "id": "sign-atoms",
      "version": "0.1.0",
      "name": "Sign Atoms with the Root Key",
      "summary": "Runbook for signing atoms using scripts/sign-atoms.mjs. Requires the op CLI authenticated to the 1Password vault 'Convergent Systems LLC'.",
      "audience": [
        "contributor",
        "operator"
      ],
      "tags": [
        "signing",
        "1password",
        "security",
        "release"
      ],
      "runbook_profile": {
        "scenario": "You have new or changed atoms that need to be signed before a release commit or PR merge.",
        "severity": "info",
        "escalation": "If the signing script fails with a vault access error, verify your 1Password session and vault membership with the Convergent Systems LLC account owner."
      },
      "body": {
        "format": "markdown",
        "inline": "## Prerequisites\n\n- Node.js 20+\n- 1Password CLI (`op`) — install via `brew install 1password-cli` on macOS\n- Active 1Password session with access to the \"Convergent Systems LLC\" vault\n- All atoms must pass the validator (see *Run the Catalog Validator* runbook) before signing\n\n## Procedure\n\n### Step 1: Verify 1Password session\n\n```bash\nop whoami\n```\n\nExpected output: your account email. If you see an error, sign in:\n\n```bash\nop signin\n```\n\nFollow the browser prompt. If you use biometric unlock, approve the Touch ID prompt.\n\n### Step 2: Run the signing script\n\n```bash\nnode scripts/sign-atoms.mjs\n```\n\nThe script reads the root key from the 1Password vault, signs each atom file under `atoms/`, and writes signatures to `keys/`.\n\n### Step 3: Verify output\n\n**Success output:**\n\n```\nSigning atoms...\n  Signed: atoms/tutorial/how-to-create-a-json-schema-atom.json → keys/tutorial/how-to-create-a-json-schema-atom.sig\n  ...\nAll N atoms signed.\n```\n\n**Check that signature files exist:**\n\n```bash\nls keys/\n```\n\nYou should see `.sig` files corresponding to every atom.\n\n### Step 4: Stage signature files\n\n```bash\ngit add keys/\n```\n\nSignature files are committed alongside the atom files. They are verified by CI on every push.\n\n## Troubleshooting\n\n| Error | Cause | Fix |\n|---|---|---|\n| `[AUTH_REQUIRED]` | 1Password session expired | Run `op signin` |\n| `item not found: atoms-root-key` | Vault item name mismatch | Confirm item name in 1Password UI |\n| `Cannot find module` | Node deps not installed | Run `npm ci` in the repo root |\n| `Atom X failed validation` | Atom has a schema error | Run the validator and fix before signing |\n\n## Notes\n\n- Signing is required before a release tag. CI enforces signature validity on the release workflow.\n- You do not need to re-sign atoms that have not changed. The script detects changes and only re-signs modified atoms (check `scripts/sign-atoms.mjs` for the exact logic).\n- The root key itself never leaves 1Password — the script uses the `op` CLI to perform the signing operation without writing the key to disk."
      }
    },
    {
      "schema": "https://doc-atoms.com/schemas/atom-v1.json",
      "type": "tutorial",
      "id": "how-to-create-a-json-schema-atom",
      "version": "0.1.0",
      "name": "How to Create a json-schema Atom",
      "summary": "Step-by-step tutorial for publishing a JSON Schema document as a json-schema type atom in schema-atoms.",
      "audience": [
        "contributor"
      ],
      "tags": [
        "schema-atoms",
        "json-schema",
        "authoring",
        "publishing"
      ],
      "prerequisites": [
        "what-is-a-grammar-atom"
      ],
      "tutorial_profile": {
        "estimated_minutes": 20,
        "outcome": "You have a valid json-schema atom file committed to schema-atoms/atoms/ and can reference it by its stable atom ID from any catalog."
      },
      "body": {
        "format": "markdown",
        "inline": "## Overview\n\nA *json-schema atom* is a versioned JSON Schema document stored in schema-atoms and addressable by a stable ID. Once published, any catalog can reference it in its grammar atom or validator configuration.\n\n## Steps\n\n### 1. Identify the schema you want to publish\n\nDecide the logical name for your schema. Use kebab-case. Examples: `persona-v1`, `prompt-fragment-v1`, `key-rotation-policy-v1`. The name becomes the atom ID and must be unique across schema-atoms.\n\n### 2. Create the schema file\n\n```bash\n# Inside a feat/* worktree on schema-atoms\nmkdir -p atoms/json-schema\ntouch atoms/json-schema/<your-name>.json\n```\n\nStart with the canonical header:\n\n```json\n{\n  \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n  \"$id\": \"https://schema-atoms.com/schemas/<your-name>.json\",\n  \"title\": \"<Human readable title>\",\n  \"description\": \"<One sentence: what this schema validates>\",\n  \"type\": \"object\",\n  \"required\": [],\n  \"additionalProperties\": false,\n  \"properties\": {}\n}\n```\n\n### 3. Write the atom wrapper\n\nEvery schema-atoms atom has a JSON wrapper that conforms to the schema-atoms atom-v1 grammar. Create `atoms/json-schema/<your-name>-atom.json`:\n\n```json\n{\n  \"schema\": \"https://schema-atoms.com/schemas/atom-v1.json\",\n  \"type\": \"json-schema\",\n  \"id\": \"<your-name>\",\n  \"version\": \"0.1.0\",\n  \"name\": \"<Human-readable name>\",\n  \"summary\": \"<One sentence summary>\",\n  \"audience\": [\"contributor\"],\n  \"tags\": [],\n  \"body\": {\n    \"format\": \"markdown\",\n    \"inline\": \"Inline description or leave path to the .json schema file.\"\n  }\n}\n```\n\n### 4. Run the validator\n\n```bash\ncd schema-atoms\npython3 scripts/validate.py\n```\n\nFix any validation errors reported against `atom-v1.json` before continuing.\n\n### 5. Sign the atom\n\n```bash\nnode scripts/sign-atoms.mjs\n```\n\nThis requires the 1Password CLI (`op`) authenticated to the \"Convergent Systems LLC\" vault. The script writes signatures to `keys/`.\n\n### 6. Commit and open a PR\n\n```bash\ngit add atoms/json-schema/<your-name>.json atoms/json-schema/<your-name>-atom.json keys/\ngit commit -m \"feat: add <your-name> json-schema atom v0.1.0\"\ngit push origin feat/<your-name>\ngh pr create --repo convergent-systems-co/schema-atoms\n```\n\nThe CI workflow validates all atoms on every PR. Merge only after CI is green.\n\n## Verification\n\nAfter merging, confirm the atom is resolvable:\n\n```bash\ncurl https://schema-atoms.com/atoms/json-schema/<your-name>.json\n```\n\nYou should receive the atom JSON with a 200 status code."
      }
    },
    {
      "schema": "https://doc-atoms.com/schemas/atom-v1.json",
      "type": "tutorial",
      "id": "how-to-publish-a-persona",
      "version": "0.1.0",
      "name": "How to Create and Publish a Persona Atom",
      "summary": "Tutorial for creating a persona atom in persona-atoms, from TOML authorship to signed publication.",
      "audience": [
        "contributor"
      ],
      "tags": [
        "persona-atoms",
        "toml",
        "authoring",
        "publishing"
      ],
      "prerequisites": [
        "what-is-a-grammar-atom"
      ],
      "tutorial_profile": {
        "estimated_minutes": 30,
        "outcome": "You have a valid persona atom signed and published to persona-atoms, referenceable from composition-atoms or any agent assembly pipeline."
      },
      "body": {
        "format": "markdown",
        "inline": "## Overview\n\nA *persona atom* defines the stable identity of an AI agent: its role, voice, constraints, and behavioral contracts. Personas live in persona-atoms and are composed with prompt fragments, context, and tools at assembly time. This tutorial walks you from a blank file to a signed, published persona.\n\n## Steps\n\n### 1. Understand what a persona is\n\nA persona atom answers: *who is this agent?* It is distinct from a prompt fragment (what it says) and a tool (what it can do). A persona should be stable across deployments — it defines identity, not capability.\n\n### 2. Create a TOML source file\n\nPersona atoms are authored in TOML for human readability and converted to JSON at publish time. Create `atoms/persona/<your-persona>.toml`:\n\n```toml\nschema = \"https://persona-atoms.com/schemas/atom-v1.json\"\ntype = \"persona\"\nid = \"<kebab-case-id>\"\nversion = \"0.1.0\"\nname = \"<Human-readable name>\"\nsummary = \"<One sentence describing who this persona is>\"\naudience = [\"contributor\", \"operator\"]\ntags = []\n\n[body]\nformat = \"markdown\"\ninline = \"\"\"\n## Role\n<Describe the role in 1-2 sentences.>\n\n## Voice\n<Describe the tone and communication style.>\n\n## Constraints\n- <Behavioral constraint 1>\n- <Behavioral constraint 2>\n\n## Identity contracts\n<What invariants the persona always upholds.>\n\"\"\"\n```\n\n### 3. Convert TOML to JSON\n\n```bash\ncd persona-atoms\npython3 scripts/toml_to_json.py atoms/persona/<your-persona>.toml > atoms/persona/<your-persona>.json\n```\n\n### 4. Run the validator\n\n```bash\npython3 scripts/validate.py\n```\n\nFix any errors against the persona-atoms `atom-v1.json` schema.\n\n### 5. Sign the atom\n\n```bash\nnode scripts/sign-atoms.mjs\n```\n\nRequires `op` CLI authenticated to the \"Convergent Systems LLC\" vault.\n\n### 6. Update exports/catalog.json\n\nAdd an entry for your new persona to the `atoms` array in `exports/catalog.json`.\n\n### 7. Commit and open a PR\n\n```bash\ngit add atoms/persona/<your-persona>.json keys/ exports/catalog.json\ngit commit -m \"feat: add <your-persona> persona atom v0.1.0\"\ngit push origin feat/<your-persona>\ngh pr create --repo convergent-systems-co/persona-atoms\n```\n\n## Verification\n\nAfter the PR merges and Cloudflare Pages deploys:\n\n```bash\ncurl https://persona-atoms.com/atoms/persona/<your-persona>.json\n```\n\nExpect a 200 response with the full atom JSON."
      }
    },
    {
      "schema": "https://doc-atoms.com/schemas/atom-v1.json",
      "type": "tutorial",
      "id": "how-to-seed-a-catalog",
      "version": "0.1.0",
      "name": "How to Seed a Catalog with Starter Atoms",
      "summary": "Tutorial for creating the first atoms in a new *-atoms catalog: schemas, seed content, validator, and catalog export.",
      "audience": [
        "contributor"
      ],
      "tags": [
        "catalog",
        "authoring",
        "bootstrapping",
        "atoms-ecosystem"
      ],
      "tutorial_profile": {
        "estimated_minutes": 45,
        "outcome": "Your new catalog has at least one valid, signed atom, a passing validator run, and a populated catalog.json ready for v0.1.0 release."
      },
      "body": {
        "format": "markdown",
        "inline": "## Overview\n\nEvery *-atoms catalog follows the same directory layout and lifecycle. This tutorial walks you through the three phases of seeding a catalog from an empty repo to a publishable v0.1.0.\n\n## Phase 1: Define the schema\n\n### 1.1 Copy the schema template\n\nEvery catalog defines its primary atom type in `schemas/atom-v1.json`. Start from the doc-atoms schema as a template:\n\n```bash\ncp /path/to/doc-atoms/schemas/atom-v1.json schemas/atom-v1.json\n```\n\nEdit the `$id`, `title`, `description`, and `properties` to match your catalog's atom type.\n\n### 1.2 Write the grammar atom\n\nA grammar atom in schema-atoms formally registers your schema in the broader ecosystem. See the *How to Create a json-schema Atom* tutorial before proceeding.\n\n## Phase 2: Create seed atoms\n\n### 2.1 Create the atoms directory structure\n\n```bash\nmkdir -p atoms/<primary-type>\n```\n\n### 2.2 Write at least one atom\n\nEach atom is a `.json` file conforming to `schemas/atom-v1.json`. Required fields: `schema`, `type`, `id`, `version`, `name`. Include `summary`, `audience`, and `tags` for discoverability.\n\n```json\n{\n  \"schema\": \"https://<catalog-domain>/schemas/atom-v1.json\",\n  \"type\": \"<your-type>\",\n  \"id\": \"<kebab-case-id>\",\n  \"version\": \"0.1.0\",\n  \"name\": \"<Human name>\",\n  \"summary\": \"<One sentence>\",\n  \"audience\": [\"contributor\"]\n}\n```\n\n## Phase 3: Validate, sign, and export\n\n### 3.1 Run the validator\n\n```bash\npython3 scripts/validate.py\n```\n\nAll atoms must pass before proceeding.\n\n### 3.2 Sign the atoms\n\n```bash\nnode scripts/sign-atoms.mjs\n```\n\nRequires the 1Password CLI (`op`) authenticated to \"Convergent Systems LLC\".\n\n### 3.3 Populate catalog.json\n\nUpdate `exports/catalog.json` to list every atom:\n\n```json\n{\n  \"catalog\": \"<catalog-name>\",\n  \"version\": \"0.1.0\",\n  \"built_at\": \"<ISO-8601 timestamp>\",\n  \"atoms\": [\n    { \"id\": \"<atom-id>\", \"type\": \"<type>\", \"version\": \"0.1.0\", \"path\": \"atoms/<type>/<filename>.json\" }\n  ],\n  \"compositions\": []\n}\n```\n\n### 3.4 Commit and cut the release\n\n```bash\ngit add -A\ngit commit -m \"feat(v0.1): seed <N> atoms\"\ngit push origin feat/<branch>\ngh pr create && # merge after CI green\ngit tag v0.1.0 && git push origin v0.1.0\n```\n\n## Verification\n\nConfirm the catalog is live by requesting the Cloudflare Pages deployment URL and checking that `exports/catalog.json` is served correctly."
      }
    }
  ],
  "compositions": []
}
