Skip to content

scaffolder | features | documentation | ARCHITECTURE

scaffolder | features | documentation | ARCHITECTURE

Section titled “scaffolder | features | documentation | ARCHITECTURE”
project: scaffolder
feature: documentation
doc-type: ARCHITECTURE
status: accepted
version: 0.1
updated: 2026-03-22
depends-on: scaffolder | features | documentation | SPEC

The documentation system is three things wired together:

  1. A config layer that eliminates hardcoded paths across the entire scaffolder system
  2. A Starlight site in a private GitHub repo that renders project docs and ai-sessions as a browsable website
  3. A Cloudflare deployment that serves the site privately at projects.purlshq.com

The folder structure after this feature is complete:

D:\purlshq-dev\
├── workspace.json ← global config
├── scaffolder-engine\ ← engine repo (purlshq/scaffolder)
│ ├── src/
│ ├── templates/
│ └── projects/
│ └── scaffolder/ ← project files MINUS docs/ai-sessions
│ ├── .claude/
│ ├── CLAUDE.md
│ ├── src/
│ └── ...
├── projects-purlshq-com\ ← doc site repo (purlshq/projects-purlshq-com)
│ ├── astro.config.mjs
│ ├── package.json
│ ├── tsconfig.json
│ ├── src/
│ │ └── content/
│ │ └── docs/
│ │ └── scaffolder/
│ │ ├── project.json ← project config
│ │ ├── docs/ ← all scaffolder docs
│ │ │ ├── SPEC.md
│ │ │ ├── ARCHITECTURE.md
│ │ │ ├── features/
│ │ │ │ └── documentation/
│ │ │ │ ├── DISCOVERY.md
│ │ │ │ ├── SPEC.md
│ │ │ │ ├── ARCHITECTURE.md
│ │ │ │ ├── TASKS.md
│ │ │ │ ├── TESTING.md
│ │ │ │ └── decisions/
│ │ │ └── ...
│ │ └── ai-sessions/ ← all scaffolder sessions
│ │ ├── CURRENT-CHAT.md
│ │ ├── CURRENT-CODE.md
│ │ └── logs/
│ └── public/
│ └── favicon.svg
└── backups\

File: D:\purlshq-dev\workspace.json Read by: Joker (filesystem MCP), Harley (filesystem), sc CLI (future)

{
"workspace_root": "D:\\purlshq-dev",
"engine_root": "D:\\purlshq-dev\\scaffolder-engine",
"docs_site_root": "D:\\purlshq-dev\\projects-purlshq-com",
"backups_root": "D:\\purlshq-dev\\backups",
"github_org": "purlshq",
"domain": "projects.purlshq.com",
"projects": ["scaffolder"]
}

Keys:

  • workspace_root — the parent directory containing all repos
  • engine_root — the scaffolder engine repo root
  • docs_site_root — the doc site repo root
  • backups_root — the backup target directory
  • github_org — the GitHub organization/username
  • domain — the doc site domain
  • projects — array of active project names (used to enumerate project configs)

File: {docs_site_root}/src/content/docs/{project}/project.json Read by: Joker (filesystem MCP), Harley (filesystem), sc CLI (future)

{
"project": "scaffolder",
"github_repo": "purlshq/scaffolder",
"scale_tier": "project-management",
"engine_project_path": "projects\\scaffolder",
"linear_project": null,
"version": "0.11.0"
}

Keys:

  • project — project name (matches folder name and workspace.projects entry)
  • github_repo — full GitHub repo path (org/repo)
  • scale_tier — basic / project-management / team
  • engine_project_path — relative path from engine_root to the project’s engine folder (for projects that have engine-side files)
  • linear_project — Linear project ID (null until Linear feature)
  • version — current project version

To find a file, combine config values. Examples:

WhatResolution
CURRENT-CHAT.md{docs_site_root}/src/content/docs/{project}/ai-sessions/CURRENT-CHAT.md
CURRENT-CODE.md{docs_site_root}/src/content/docs/{project}/ai-sessions/CURRENT-CODE.md
Session logs{docs_site_root}/src/content/docs/{project}/ai-sessions/logs/
Feature SPEC{docs_site_root}/src/content/docs/{project}/docs/features/{feature}/SPEC.md
Project CLAUDE.md{engine_root}/{engine_project_path}/CLAUDE.md
Engine source{engine_root}/src/
Workspace config{workspace_root}/workspace.json
Project config{docs_site_root}/src/content/docs/{project}/project.json

Joker reads workspace.json first at every session start, then reads the relevant project.json, then reads session files. All paths are constructed from config values — never hardcoded.

CHAT-REFERENCE.md, PROJECT-INSTRUCTIONS.md, and CLAUDE.md all stop containing literal paths. Instead they describe paths using config keys:

Session files: {docs_site_root}/src/content/docs/{project}/ai-sessions/
Docs: {docs_site_root}/src/content/docs/{project}/docs/
Engine project: {engine_root}/{engine_project_path}/

Joker and Harley resolve these by reading the actual config files. The reference files tell them which config keys to use, not what the paths are.

The CLI config stays where it is. It handles identity and preferences (operator name, AI names, default scale tier, etc.). Path resolution is the workspace config’s job. The two configs are complementary:

ConfigPurposeLocationRead by
workspace.jsonWhere things areD:\purlshq-dev\Joker, Harley, sc (future)
project.jsonProject-specific settingsDoc site repo per projectJoker, Harley, sc (future)
.scaffolder-config.jsonWho am I, what are my defaults~/.scaffolder-config.jsonsc CLI only

Starlight is an Astro-based documentation framework. It generates a static site from markdown files. The folder structure under src/content/docs/ becomes the sidebar navigation automatically.

astro.config.mjs at the doc site repo root:

import { defineConfig } from 'astro/config';
import starlight from '@astrojs/starlight';
export default defineConfig({
integrations: [
starlight({
title: 'purlshq projects',
sidebar: [
// Auto-generated from folder structure
// No manual entries needed
],
}),
],
});

Starlight auto-generates sidebar entries from the folder structure when no manual sidebar config is provided. This is the key requirement: adding a new doc file or project folder automatically updates navigation.

All content lives under src/content/docs/. Each project gets a top-level folder. Within each project, docs/ and ai-sessions/ mirror the structure they had in the engine’s project folder.

Starlight renders the folder hierarchy as nested sidebar navigation:

scaffolder/
├── docs/
│ ├── SPEC.md → sidebar: scaffolder > docs > SPEC
│ ├── features/
│ │ └── documentation/
│ │ ├── SPEC.md → sidebar: scaffolder > docs > features > documentation > SPEC
│ │ └── decisions/
│ │ └── ... → sidebar: scaffolder > docs > features > documentation > decisions > ...
│ └── ...
└── ai-sessions/
├── CURRENT-CHAT.md → sidebar: scaffolder > ai-sessions > CURRENT-CHAT
└── logs/
└── ... → sidebar: scaffolder > ai-sessions > logs > ...

Every markdown file gets YAML frontmatter with at minimum a title field. The title uses our pipe-separated naming convention:

---
title: "scaffolder | features | documentation | SPEC"
---

This is added above the existing # heading and code-block header. The existing header format is unchanged. Starlight uses the frontmatter title for sidebar labels, browser tabs, and breadcrumbs.

src/content.config.ts (or src/content/config.ts depending on Starlight version):

import { defineCollection } from 'astro:content';
import { docsLoader } from '@astrojs/starlight/loaders';
import { docsSchema } from '@astrojs/starlight/schema';
export const collections = {
docs: defineCollection({
loader: docsLoader(),
schema: docsSchema(),
}),
};

Default schema is sufficient. No custom frontmatter fields needed — our page header lives in the document body, not in frontmatter.

project.json files live alongside markdown content in the docs folder. Starlight ignores non-markdown files — they won’t appear in navigation or cause build errors. They’re just config files that happen to live in the content tree for easy co-location.


The doc site repo connects to Cloudflare Pages:

SettingValue
Repositorypurlshq/projects-purlshq-com
Production branchmain
Build commandnpm run build
Build output directorydist
Framework presetAstro
Node.js version18+ (Cloudflare Pages default)

Every push to main triggers an automatic build and deploy.

On Cloudflare DNS (purlshq.com is already on Cloudflare):

TypeNameTarget
CNAMEprojectsprojects-purlshq-com.pages.dev

The exact Pages URL will be provided by Cloudflare when the project is created. The CNAME target may vary.

Access policy on projects.purlshq.com:

SettingValue
Application typeSelf-hosted
Application domainprojects.purlshq.com
Policy nameOperator only
Policy actionAllow
Include ruleEmails — operator email address
Authentication methodOne-time PIN (email OTP)

This is on Cloudflare’s free tier. No cost. The operator receives an email with a one-time code to access the site.


  1. Joker reads workspace.json → gets docs_site_root
  2. Joker reads project.json → gets project name
  3. Joker writes the file to {docs_site_root}/src/content/docs/{project}/docs/...
  4. Operator (or Harley) commits and pushes to main
  5. Cloudflare Pages builds and deploys automatically
  6. Doc appears on projects.purlshq.com
  1. Joker reads workspace.json → gets docs_site_root
  2. Joker reads project.json → gets project name, version, settings
  3. Joker reads CURRENT-CHAT.md from {docs_site_root}/src/content/docs/{project}/ai-sessions/
  4. Joker reads CURRENT-CODE.md from same location
  5. Session begins with full context
  1. Joker writes CURRENT-CHAT.md to {docs_site_root}/src/content/docs/{project}/ai-sessions/
  2. Joker writes immutable log to {docs_site_root}/src/content/docs/{project}/ai-sessions/logs/
  3. Operator commits and pushes (or Harley handles via hook)
  1. Harley reads workspace.json → gets paths
  2. Harley reads project.json → gets project settings
  3. Harley reads CURRENT-CODE.md from doc site repo
  4. Harley reads relevant docs (TASKS.md, SPEC.md, etc.) from doc site repo
  5. Harley works in the engine project folder for code changes
  6. Harley writes to the doc site repo for session state
  1. Add project name to workspace.json projects array
  2. Create {docs_site_root}/src/content/docs/{project}/project.json
  3. Create {docs_site_root}/src/content/docs/{project}/docs/ folder
  4. Create {docs_site_root}/src/content/docs/{project}/ai-sessions/ folder
  5. Project appears in site navigation on next deploy

The build order matters. Dependencies flow downward:

1. Create D:\purlshq-dev\ and workspace.json
2. Move scaffolder-engine into D:\purlshq-dev\
3. Initialize Starlight site at D:\purlshq-dev\projects-purlshq-com\
4. Create GitHub repo purlshq/projects-purlshq-com
5. Move docs/ and ai-sessions/ from engine to doc site repo
6. Add Starlight frontmatter to all markdown files
7. Create project.json for scaffolder
8. Rewrite reference files to use config keys
9. Connect Cloudflare Pages + configure DNS
10. Set up Cloudflare Access
11. Verify build, deploy, and access

Steps 1-2 are the folder restructure. Steps 3-7 are the doc site setup. Step 8 is the config migration. Steps 9-11 are the deployment.


ARCHITECTURE — accepted.