An agent harness for spec-driven development.
0

Configure Feed

Select the types of activity you want to include in your feed.

TypeScript 83.2%
JavaScript 16.8%
Other 0.1%
3 2 0

Clone this repository

https://git.vm.fail/cameron.stream/charter https://git.vm.fail/did:plc:3odur5hhtbdbvqonvmoespnr
ssh://git@knot1.tangled.sh:2222/cameron.stream/charter ssh://git@knot1.tangled.sh:2222/did:plc:3odur5hhtbdbvqonvmoespnr

For self-hosted knots, clone URLs may differ based on your setup.


README.md

Charter#

Charter is a Jazz-native workspace for spec-driven development.

The invariant is intentionally blunt: the spec is the product surface, and Jazz is the coordination truth. Humans edit documents. Charter turns meaningful document changes, spans, comments, prompt changes, and agent lifecycle activity into project events, durable work items, consumer packets, and inspectable agent runs.

This repo's spec/ directory is Charter's own local Markdown projection. The database is the canonical coordination surface for sync, cursors, work queues, consumers, packets, provenance, optional APIs, future UI, and runners.

Current contents#

  • spec/ - local filesystem projection of Charter's product spec.
  • src/markdown.ts - Markdown/frontmatter parser.
  • src/spec-files.ts - spec/**/*.md scanner and import-plan builder.
  • src/db/jazz.ts - default Charter database adapter; persistent Jazz 2 tables with optional sync-server participation.
  • src/db/json.ts - local JSON projection adapter for tests/fallback (CHARTER_DB_ADAPTER=json).
  • src/jazz/schema.ts - Jazz 2 schema for projects, sources, documents, work, workers, leases, packets, and runs.
  • src/server.ts - shared runtime/watch helpers plus optional local HTTP inspection API/status page.
  • src/status.ts - truthful project-state summary over projection drift, database durability, events, work, consumers, packets, and requests.
  • src/cli.ts - CLI with watch, status, serve, start, frontend, scan, index, search, changes, events, work, agent, queue, report, requests, and sync commands.

Target product#

The desired first-class product command is:

charter watch

That starts the local runtime:

  • watches spec/**/*.md.
  • ingests filesystem edits into the Jazz-backed database.
  • writes semantic project events and durable work items.
  • matches registered consumer agents.
  • emits structured JSONL terminal logs.
  • runs agents as subordinate processes when invoked.
  • records packets, reports, failures, stdout, stderr, and provenance.

A browser/editor UI is optional future surface. It is not the product center right now. The product center is the runtime, the database, the spec files, and the event stream.

Commands#

npm install
npm run watch
npm run status
npm run scan
npm run index
npm run search -- agent runtime
npm run changes
npm run events
npm run work
npm run agent
npm run report -- --request <request-id> --status done --message "Implemented and checked."
npm run requests
npm run sync -- --dry-run
npm run sync
npm run sync -- --jazz-runtime
npm run check
npm test

After build, the binary is charter:

npm run build
node dist/src/cli.js scan

npm run watch starts the local controller and emits structured JSONL logs while it ingests selected files. npm run serve is an optional local inspection surface, not the distributed runtime boundary. npm run sync writes native Jazz 2 rows. .spec/state/jazz.sqlite is the durable local Jazz replica; .spec/state/jazz-runtime.json is an inspection receipt, not a bootstrap mirror. Set JAZZ_SERVER_URL plus backend identity configuration to join distributed sync. npm run sync -- --jazz-runtime prints the active Jazz row ids and durability mode. npm run index writes a disposable lexical cache. Work, consumer, packet, request, and report commands operate on the configured database rather than a controller-owned HTTP API.

npm run status is the first answer to “what is happening?” It reports the configured database shape and its actual durability limit, filesystem/database drift, event count, queue state, registered consumers, packet/request state, recent activity, and structural warnings. Runtime-process presence is currently reported as unknown rather than inferred from a state file; Charter does not yet persist a watch-process heartbeat.

Local replica, path-mapping, secret-reference, and cache files are ignored by Git. The Jazz project is shared coordination truth; each controller's replica and filesystem mapping remain machine-local.

Project spec#

Start with spec/index.md and spec/product-vision.md. Root README is operational. Product intent lives in database-backed spec documents; Markdown is the local projection in this repo.

Document identity#

A Markdown file may declare a stable id:

---
id: runtime-architecture
title: Runtime Architecture
kind: architecture
---

If id is omitted, the importer derives one from the relative file path without .md. Example: spec/runtime/architecture.md becomes runtime/architecture.

Pin id before renaming a spec file if the rename should preserve identity.

Watch loop#

The current default loop is:

  1. run charter watch or npm run watch.
  2. edit spec documents in Zed, Obsidian, or another filesystem editor.
  3. watch the runtime ingest changes and emit JSONL events.
  4. run charter status to inspect projection drift, database durability, queue state, registered consumers, packets, requests, and recent activity.
  5. create and run a consumer-scoped request packet with charter agent next --exec ... when a subordinate runner should execute.

The manual CLI loop is still available:

  1. edit spec documents through Markdown, a database-native client, or another adapter.
  2. run charter changes to see what changed against the current database state.
  3. run charter sync to accept projection edits into database state, append project events, and queue durable work items.
  4. run charter work or charter queue to inspect queued work.
  5. run charter agent to create a database-backed agent request from queued work items.
  6. run charter agent --exec --agent-command <command> when a runner should actually execute. For Codex, use codex exec - because Codex reads the prompt from stdin.
  7. the agent calls charter report --request "$CHARTER_REQUEST_ID" --status done|failed|blocked|ignored --message <summary> before exit.
  8. run charter requests to inspect the structured report, exit code, stdout, and stderr captured from the run.
  9. run charter events to inspect the project event feed.

Agent context is attached through spec document ids, spans, comments, prompt documents, and packets. There is no separate memory primitive; durable context belongs in one or more spec documents or explicit activity/provenance objects. Completion is written back through Charter, not inferred from prose alone; stdout/stderr capture is the fallback audit trail.

Running agents#

Create work from spec changes:

npm run changes
npm run sync
npm run work

Inspect and run one queued work item at a time:

npm run queue
npm run queue -- next
npm run agent -- next --exec --letta-agent agent-0ddda04a-3d4c-410a-9fb7-d1940281a93f

queue shows only unclaimed queued work by default; use npm run work -- --all for the full ledger. queue next and agent next select the oldest queued item. Creating an agent request without --exec does not claim work; work moves to running only when the runner starts. Old planned claims remain explicit: inspect them with npm run queue -- next --status claimed or run one with npm run agent -- next --status claimed --exec ....

Run queued work with Codex instead:

npm run agent -- next --exec --agent-command 'codex exec -s workspace-write -'

The prompt is sent on stdin. Tools that need a file can use {promptFile} in the command string, but Codex does not support --prompt-file. Codex also needs a writable sandbox for tests/build caches; otherwise Vitest can fail before collecting tests because it cannot write temp/cache files. The Letta convenience runner uses {promptFile} internally as letta --agent <id> --new -p "$(cat {promptFile})".

Agent completion contract:

npm run report -- --request "$CHARTER_REQUEST_ID" --status done --message "Implemented the requested work and checks pass."
npm run report -- --request "$CHARTER_REQUEST_ID" --status failed --message "Failed because ..."
npm run report -- --request "$CHARTER_REQUEST_ID" --status blocked --message "Blocked because ..."
npm run report -- --request "$CHARTER_REQUEST_ID" --status ignored --message "Ignored because ..."

The runner captures stdout, stderr, and exit code automatically. An explicit report is canonical; captured output is the audit trail and fallback report when the agent exits without reporting.

Inspect runs:

npm run requests
npm run requests -- --request <request-id>

Jazz status#

The executable path uses Jazz 2 alpha and jazz-napi by default. Native rows persist across independent CLI processes; tests exercise shutdown, restart, and row-id continuity without .spec/state/spec-db.json. With no sync server, status honestly reports native-jazz-local. With JAZZ_SERVER_URL, app id, and backend identity configured, it uses edge durability and reports native-jazz-synced.

Distributed identity provisioning and production project-role policies remain deployment work. The schema already represents project membership, sources, workers, capabilities, leases, packets, manifests, and runs so that work does not require another data-model reset. See spec/runtime-architecture.md and spec/jazz-git-boundary.md for the contract.