Add newcomer guide, agent guide, and sample app files
DIACHRON.md explains the framework to newcomers joining a diachron-based project. diachron/AGENTS.md helps AI coding agents work with the framework conventions and commands. backend/types.ts and backend/services.ts are sample starting points for application-specific types and services. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
168
DIACHRON.md
Normal file
168
DIACHRON.md
Normal file
@@ -0,0 +1,168 @@
|
||||
# What is diachron?
|
||||
|
||||
diachron is a web framework for TypeScript and Node.js. It uses a Go-based
|
||||
master process that handles file watching, building, process management, and
|
||||
request proxying. The application code is TypeScript running on Express.js.
|
||||
|
||||
If you're joining a project that uses diachron, this document will orient you.
|
||||
|
||||
## Why diachron exists
|
||||
|
||||
diachron was built around a few frustrations with mainstream web frameworks:
|
||||
|
||||
- **No dev/prod split.** Most frameworks behave differently in development and
|
||||
production. diachron doesn't. The master process watches files, rebuilds,
|
||||
and manages workers the same way everywhere. There is no `NODE_ENV`.
|
||||
|
||||
- **Managed tooling.** Node.js, pnpm, and other tools are downloaded and
|
||||
pinned to exact versions inside the project. You don't install them
|
||||
system-wide. Everyone on the team runs the same binaries.
|
||||
|
||||
- **PostgreSQL, directly.** No ORM, no database abstraction layer. You write
|
||||
SQL (via Kysely for type safety) and talk to PostgreSQL. If you need
|
||||
MySQL or SQLite support, this is not the framework for you.
|
||||
|
||||
- **Debuggability over magic.** Everything is explicit and inspectable.
|
||||
Logging and observability are first-class concerns, not afterthoughts.
|
||||
|
||||
diachron is inspired by the
|
||||
[Taking PHP Seriously](https://slack.engineering/taking-php-seriously/) essay
|
||||
from Slack Engineering. It's designed for small to medium systems (what we
|
||||
call "Ring 0 and Ring 1") -- not heavy-compliance or banking-scale
|
||||
applications.
|
||||
|
||||
## How it works
|
||||
|
||||
When you run `./master`, the following happens:
|
||||
|
||||
1. The Go master process starts and watches your TypeScript source files.
|
||||
2. It builds the backend using `@vercel/ncc`, producing a single bundled JS
|
||||
file.
|
||||
3. It starts one or more Node.js worker processes running your Express app.
|
||||
4. It proxies HTTP requests from port 8080 to the workers.
|
||||
5. When you edit a source file, it rebuilds and restarts the workers
|
||||
automatically.
|
||||
6. If a worker crashes, it restarts automatically.
|
||||
|
||||
There is no separate "dev server" or "hot module replacement." The master
|
||||
process is the only way to run the application.
|
||||
|
||||
## Project structure
|
||||
|
||||
A diachron project looks like this:
|
||||
|
||||
```
|
||||
.
|
||||
├── DIACHRON.md # This file (framework overview for newcomers)
|
||||
├── master/ # Go master process (framework-owned)
|
||||
├── logger/ # Go logging service (framework-owned)
|
||||
├── diachron/ # Managed binaries, shims, framework library
|
||||
│ ├── AGENTS.md # Guide for AI coding agents
|
||||
│ ├── binaries/ # Downloaded Node.js, pnpm (gitignored)
|
||||
│ ├── cmd.d/ # Commands available via ./cmd
|
||||
│ ├── shims/ # Wrappers that use managed binaries
|
||||
│ └── ...
|
||||
├── backend/ # Your application code
|
||||
│ ├── app.ts # Entry point
|
||||
│ ├── routes.ts # Route definitions
|
||||
│ ├── handlers.ts # Route handlers
|
||||
│ ├── services.ts # Service layer
|
||||
│ ├── types.ts # Application types
|
||||
│ ├── config.ts # Application configuration
|
||||
│ └── diachron/ # Framework library code (framework-owned)
|
||||
├── cmd # Run managed commands (./cmd pnpm install, etc.)
|
||||
├── develop # Development-only commands (./develop reset-db, etc.)
|
||||
├── mgmt # Management commands safe for production
|
||||
├── sync.sh # Install/update all dependencies
|
||||
├── master # The compiled master binary (after sync)
|
||||
└── docker-compose.yml
|
||||
```
|
||||
|
||||
### File ownership
|
||||
|
||||
There are two owners of files in a diachron project:
|
||||
|
||||
- **You own** everything in `backend/` (except `backend/diachron/`), plus
|
||||
`docker-compose.yml`, `package.json`, and anything else you create.
|
||||
- **The framework owns** `master/`, `logger/`, `diachron/`,
|
||||
`backend/diachron/`, and the top-level scripts (`cmd`, `develop`, `mgmt`,
|
||||
`sync.sh`, `check.sh`).
|
||||
|
||||
Don't modify framework-owned files. This separation keeps framework upgrades
|
||||
clean.
|
||||
|
||||
## Getting started
|
||||
|
||||
```bash
|
||||
# Install dependencies and build the master process
|
||||
./sync.sh
|
||||
|
||||
# Start the application
|
||||
./master
|
||||
```
|
||||
|
||||
The app will be available at `http://localhost:8080`.
|
||||
|
||||
You need Linux or macOS on x86_64. For the full stack (database, Redis,
|
||||
etc.), you also need `docker compose`.
|
||||
|
||||
## The command system
|
||||
|
||||
diachron has three types of commands, separated by intent and safety:
|
||||
|
||||
- **`./cmd <command>`** -- Run managed tools (node, pnpm, tsx, etc.). These
|
||||
use the project's pinned versions, not whatever is installed on your system.
|
||||
|
||||
```bash
|
||||
./cmd pnpm install
|
||||
./cmd pnpm test
|
||||
```
|
||||
|
||||
- **`./mgmt <command>`** -- Management commands that are safe to run in
|
||||
production. Migrations, user management, that sort of thing.
|
||||
|
||||
```bash
|
||||
./mgmt migrate
|
||||
./mgmt add-user
|
||||
```
|
||||
|
||||
- **`./develop <command>`** -- Development commands that may be destructive.
|
||||
Database resets, fixture loading, etc. These are gated in production.
|
||||
|
||||
```bash
|
||||
./develop reset-db
|
||||
./develop db # Open a database shell
|
||||
```
|
||||
|
||||
The rule of thumb: if you'd run it at 3am while tired and worried, it's a
|
||||
`mgmt` command. If it destroys data on purpose, it's a `develop` command.
|
||||
|
||||
## Key concepts
|
||||
|
||||
### Call and Result
|
||||
|
||||
diachron wraps Express's `Request` and `Response` in its own types called
|
||||
`Call` and `Result`. This avoids shadowing and keeps the framework's
|
||||
interface distinct from Express internals. Your handlers receive a `Call`
|
||||
and return a `Result`.
|
||||
|
||||
### Routes
|
||||
|
||||
Routes are defined as data (arrays of `Route` objects in `routes.ts`), not
|
||||
through decorators or method chaining. The framework processes them into
|
||||
Express handlers.
|
||||
|
||||
### No environment variables for behavior
|
||||
|
||||
There is no `NODE_ENV`, no `DEBUG`, no mode switching. Configuration that
|
||||
must vary between deployments (database URLs, secrets) lives in
|
||||
configuration files, but the application's behavior doesn't branch on
|
||||
environment.
|
||||
|
||||
## Further reading
|
||||
|
||||
- `README.md` -- Project introduction and requirements
|
||||
- `diachron/AGENTS.md` -- Guide for AI coding agents
|
||||
- `docs/` -- Design documents and philosophy
|
||||
- `docs/commands.md` -- Detailed explanation of the command system
|
||||
- `docs/concentric-circles.md` -- What diachron is (and isn't) designed for
|
||||
15
backend/services.ts
Normal file
15
backend/services.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
// This is a sample file provided by diachron. You are encouraged to modify it.
|
||||
|
||||
// Application services go here. A service encapsulates a capability that
|
||||
// handlers depend on: database queries, external API calls, business logic
|
||||
// that doesn't belong in a handler.
|
||||
//
|
||||
// The framework provides core services via `core` (from ./diachron/core):
|
||||
// core.database, core.logging, core.misc, etc. This file is for your
|
||||
// application's own services.
|
||||
|
||||
import { core } from "./diachron/core";
|
||||
|
||||
const db = core.database.db;
|
||||
|
||||
export { db };
|
||||
8
backend/types.ts
Normal file
8
backend/types.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
// This is a sample file provided by diachron. You are encouraged to modify it.
|
||||
|
||||
// Application-specific types go here. Framework types (Call, Result, Route,
|
||||
// Handler, etc.) are defined in ./diachron/types and should be imported from
|
||||
// there.
|
||||
//
|
||||
// This file is for your domain types: the nouns and shapes that are specific
|
||||
// to your application.
|
||||
191
diachron/AGENTS.md
Normal file
191
diachron/AGENTS.md
Normal file
@@ -0,0 +1,191 @@
|
||||
# Working with diachron (Agent Guide)
|
||||
|
||||
This document helps AI coding agents work effectively with projects built on
|
||||
the diachron framework. It covers the conventions, commands, and structures
|
||||
you need to know.
|
||||
|
||||
## Quick orientation
|
||||
|
||||
diachron is a TypeScript/Express web framework with a Go master process.
|
||||
Your application code lives in `backend/`. The framework owns `master/`,
|
||||
`logger/`, `diachron/`, and `backend/diachron/`.
|
||||
|
||||
**Do not modify framework-owned files** unless explicitly asked to work on
|
||||
the framework itself.
|
||||
|
||||
## Running the application
|
||||
|
||||
```bash
|
||||
./sync.sh # Install dependencies (run once, or after pulling changes)
|
||||
./master # Start the application (watches files, rebuilds, proxies)
|
||||
```
|
||||
|
||||
By default, the app listens on port 8080 (proxy) and workers run on
|
||||
ports starting at 3000.
|
||||
|
||||
## Commands you'll need
|
||||
|
||||
All tools (node, pnpm, tsx, etc.) are managed by the framework. Do not
|
||||
invoke system-installed versions.
|
||||
|
||||
```bash
|
||||
./cmd pnpm install # Install npm packages
|
||||
./cmd pnpm test # Run tests
|
||||
./cmd pnpm biome check . # Lint (run from backend/)
|
||||
./develop db # Open database shell
|
||||
./develop reset-db # Drop and recreate database
|
||||
./develop migrate # Run migrations (development)
|
||||
./mgmt migrate # Run migrations (production-safe)
|
||||
```
|
||||
|
||||
### Formatting and linting
|
||||
|
||||
```bash
|
||||
cd backend && ../cmd pnpm biome check --write .
|
||||
```
|
||||
|
||||
### Building Go code
|
||||
|
||||
```bash
|
||||
cd master && go build
|
||||
cd logger && go build
|
||||
```
|
||||
|
||||
### Quality checks
|
||||
|
||||
```bash
|
||||
./check.sh # shellcheck + golangci-lint
|
||||
```
|
||||
|
||||
## Application structure
|
||||
|
||||
### Where to put code
|
||||
|
||||
| What | Where |
|
||||
|--------------------------|-----------------------------|
|
||||
| Application entry point | `backend/app.ts` |
|
||||
| Route definitions | `backend/routes.ts` |
|
||||
| Route handlers | `backend/handlers.ts` |
|
||||
| Service layer | `backend/services.ts` |
|
||||
| Application types | `backend/types.ts` |
|
||||
| Application config | `backend/config.ts` |
|
||||
| Database migrations | `backend/migrations/` |
|
||||
| Framework library code | `backend/diachron/` |
|
||||
|
||||
### Types and naming
|
||||
|
||||
- HTTP request wrapper: `Call` (not Request)
|
||||
- HTTP response wrapper: `Result` (not Response)
|
||||
- Route definitions: arrays of `Route` objects
|
||||
- Handlers: functions that take a `Call` and return a `Result`
|
||||
|
||||
These names are intentional. Use them consistently.
|
||||
|
||||
Import framework types from `./diachron/types`:
|
||||
|
||||
```typescript
|
||||
import type { Call, Result, Route, Handler } from "./diachron/types";
|
||||
```
|
||||
|
||||
Application-specific domain types go in `backend/types.ts`.
|
||||
|
||||
### Services
|
||||
|
||||
Application services go in `backend/services.ts`. Framework services are
|
||||
accessed through the `core` object:
|
||||
|
||||
```typescript
|
||||
import { core } from "./diachron/core";
|
||||
|
||||
core.database.db // Kysely database instance
|
||||
core.logging.log // Logging
|
||||
core.misc.sleep // Utilities
|
||||
```
|
||||
|
||||
### Exports
|
||||
|
||||
When a TypeScript file exports symbols, they should be listed in
|
||||
alphabetical order.
|
||||
|
||||
## Database
|
||||
|
||||
diachron uses PostgreSQL exclusively, accessed through Kysely (type-safe
|
||||
query builder). There is no ORM.
|
||||
|
||||
- Write SQL via Kysely, not raw query strings (unless Kysely can't express
|
||||
the query)
|
||||
- Migrations live in `backend/migrations/`
|
||||
- Run `./develop codegen` after schema changes to regenerate Kysely types
|
||||
|
||||
## Key conventions
|
||||
|
||||
### No dev/prod distinction
|
||||
|
||||
There is no `NODE_ENV`. The application behaves identically everywhere.
|
||||
Do not introduce environment-based branching.
|
||||
|
||||
### Managed tooling
|
||||
|
||||
Never reference globally installed `node`, `npm`, `npx`, or `pnpm`.
|
||||
Always use `./cmd node`, `./cmd pnpm`, etc.
|
||||
|
||||
### File ownership boundary
|
||||
|
||||
```
|
||||
You may edit: backend/* (except backend/diachron/)
|
||||
Do not edit: master/*, logger/*, diachron/*, backend/diachron/*
|
||||
```
|
||||
|
||||
If a task requires framework changes, confirm with the user first.
|
||||
|
||||
### Command safety tiers
|
||||
|
||||
- `./cmd` -- Tool wrappers, always safe
|
||||
- `./mgmt` -- Production-safe operations (migrations, user management)
|
||||
- `./develop` -- Destructive operations, development only
|
||||
|
||||
Never use `./develop` commands against production data.
|
||||
|
||||
## Common tasks
|
||||
|
||||
### Add a new route
|
||||
|
||||
1. Define the route in `backend/routes.ts` as a `Route` object
|
||||
2. Implement the handler in `backend/handlers.ts`
|
||||
3. Add any needed types to `backend/types.ts`
|
||||
|
||||
### Add a database migration
|
||||
|
||||
1. Create a migration file in `backend/migrations/`
|
||||
2. Run `./develop migrate` to apply it
|
||||
3. Run `./develop codegen` to regenerate Kysely types
|
||||
|
||||
### Install a package
|
||||
|
||||
```bash
|
||||
cd backend && ../cmd pnpm add <package>
|
||||
```
|
||||
|
||||
### Run a one-off TypeScript script
|
||||
|
||||
```bash
|
||||
./develop tsx backend/path/to/script.ts
|
||||
```
|
||||
|
||||
## file-list
|
||||
|
||||
The root `file-list` file is a manifest of all files that ship with the
|
||||
framework. When you create or delete a file that is part of the project
|
||||
(not a scratch file or generated output), you must update `file-list` to
|
||||
match. Keep it sorted alphabetically.
|
||||
|
||||
## Things to avoid
|
||||
|
||||
- Do not introduce `.env` files or `dotenv` without checking with the
|
||||
team first. The configuration story is still being decided.
|
||||
- Do not introduce webpack, vite, or other bundlers. The master process
|
||||
handles building via `@vercel/ncc`.
|
||||
- Do not add express middleware directly. Use the framework's route
|
||||
processing in `backend/diachron/routing.ts`.
|
||||
- Do not use `npm` or globally installed `pnpm`. Use `./cmd pnpm`.
|
||||
- Do not add `NODE_ENV` checks or development/production branches.
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
.gitignore
|
||||
.go-version
|
||||
DIACHRON.md
|
||||
backend/.gitignore
|
||||
backend/.npmrc
|
||||
backend/app.ts
|
||||
@@ -17,13 +18,16 @@ backend/package.json
|
||||
backend/pnpm-workspace.yaml
|
||||
backend/routes.ts
|
||||
backend/run.sh
|
||||
backend/services.ts
|
||||
backend/show-config.sh
|
||||
backend/tsconfig.json
|
||||
backend/types.ts
|
||||
backend/watch.sh
|
||||
bootstrap.sh
|
||||
cmd
|
||||
develop
|
||||
diachron
|
||||
diachron/AGENTS.md
|
||||
file-list
|
||||
logger
|
||||
master
|
||||
|
||||
Reference in New Issue
Block a user