An agent harness for spec-driven development.
charter spec data-model.md
10 kB · 256 lines
Markdown
at main


id: data-model kind: architecture title: Data Model status: active #

Data Model #

Charter's database model should make the spec workspace, event stream, consumer agents, packets, and provenance explicit in Jazz.

The code currently implements the first core slice: project root, documents, versions, events, work items, registered agent consumers, durable agent packets, and agent requests. The target product still adds spans, comments, and richer activity records beyond the current request-prompt compatibility layer.

CharterProject #

One root per repository or logical project.

Current fields:

  • projectId: stable project identity.
  • name: display name.
  • documents: map of document id to current SpecDocument.
  • versions: map of version id to immutable SpecDocumentVersion.
  • events: Jazz CoFeed of append-only ProjectEvent entries.
  • workItems: map of mutable WorkItem CoMaps keyed by work item id.
  • consumers: map of consumer id to AgentConsumer.
  • packets: map of packet id to durable WorkPacket / AgentPacket.
  • requests: map of request id to database-backed AgentRequest.
  • updatedAt: last materialization time.

Target fields:

  • spans: map of span id to DocumentSpan.
  • comments: map of comment id to DocumentComment.
  • activity: feed or map of AgentActivity records.

SpecDocument #

Current materialized state for a spec document.

Fields:

  • id: stable document id.
  • path: POSIX path relative to spec/.
  • title: frontmatter title or first H1 fallback.
  • kind: normalized broad category.
  • status: lifecycle state.
  • body: Markdown body without frontmatter.
  • frontmatterJson: normalized JSON string for frontmatter.
  • headingsJson: extracted heading outline.
  • sha256: content hash of the raw file or equivalent Markdown projection.
  • sizeBytes: raw content byte length.
  • mtimeMs: filesystem modification time when the source is a file.
  • importedAt: import timestamp.

Target document rows should support rich editor state without losing Markdown projection. The minimal path can keep Markdown body as text. A richer path can add editor/co-rich-text state once the front end earns it.

SpecDocumentVersion #

Immutable content snapshot keyed by document id and hash.

Version rows let agents compare old and new requirements without pretending the current row is the whole history. The Jazz adapter creates a SpecDocumentVersion CoMap for each new documentId:sha256 it sees and leaves prior versions addressable through CharterProject.versions.

DocumentSpan #

A span points to a region inside a document.

Fields:

  • id: stable span id.
  • documentId: target document.
  • documentVersionId: version or hash at creation time.
  • startOffset / endOffset: text offsets for the first version.
  • anchorJson: optional richer Markdown-aware anchor.
  • quotedText: text snapshot at creation time.
  • status: active, resolved, stale, archived.
  • createdBy / createdAt.

Spans are how a user highlights part of a spec and asks Charter to do something with that exact region.

DocumentComment #

A comment attaches to a document or span.

Fields:

  • id.
  • documentId.
  • spanId when applicable.
  • body.
  • kind: comment, action-request, resolution-note.
  • status: open, resolved, archived.
  • assignedConsumerId when applicable.
  • createdBy / createdAt / updatedAt.

Action comments should create project events and, when actionable, work items.

ProjectEvent #

Append-only semantic event entry in the CharterProject.events CoFeed. Raw field-level provenance lives in Jazz edit history via $jazz.getEdits(); ProjectEvent is the agent/UI-facing classification layer over that history.

Initial event types:

  • spec.document.upserted
  • spec.document.deleted
  • spec.span.created
  • spec.comment.created
  • spec.comment.action_requested
  • consumer.registered
  • consumer.updated
  • agent.packet.created
  • agent.request.created
  • agent.request.executing
  • agent.request.executed
  • agent.request.failed
  • agent.request.blocked
  • agent.request.ignored

Authority values:

  • human.file: direct edit to a Markdown spec file.
  • human.ui: direct edit through the front end.
  • importer.sync: materialization from filesystem into database.
  • agent.proposal: agent-authored suggested change.
  • agent.applied: accepted agent change written back into the source spec.
  • charter.agent: agent request lifecycle activity.
  • charter.system: system classification, packet construction, or queue transition.

A project event should carry source pointers: document id, span id, comment id, work item id, request id, source Jazz row id, and source field/path when available.

WorkItem #

Mutable CoMap representing agent-consumable work derived from a project event.

Fields:

  • id: stable work item id, usually derived from source event id.
  • kind: spec-change, span-action, comment-action, agent-request, or manual.
  • status: queued, claimed, running, done, ignored, failed, or blocked.
  • eventId: source event feed entry.
  • documentId / path / title: affected spec document.
  • spanId / commentId when applicable.
  • matchedConsumerIds: consumers eligible to handle the work.
  • attachedDocumentIds: spec documents supplied as agent context.
  • sourceJazzRowId / sourceField: Jazz provenance pointer when available.
  • claimedBy / claimedAt: worker claim metadata.
  • requestId: linked AgentRequest once running.
  • resultText: optional completion summary.

AgentConsumer #

Database-registered consumer specification.

Fields:

  • id.
  • name.
  • status: active, paused, archived.
  • runnerKind: Letta, Codex, Claude Code, script, CI, custom.
  • runnerCommand: optional default command or adapter command.
  • runnerConfigJson: non-secret runner configuration.
  • eventTypes / eventTypesJson: subscribed event types.
  • documentSelectors / documentSelectorsJson: subscribed document ids, folders, tags, or query selectors.
  • promptDocumentIds / promptDocumentIdsJson: prompt/spec documents composing behavior.
  • defaultContextDocumentIds / defaultContextDocumentIdsJson.
  • cursorJson: last seen event ids or timestamps.
  • createdAt / updatedAt.

Secrets do not belong in the consumer row. Store references to secret names or host-side config, not credential material.

Consumer registration is a write operation with a bounded response shape. Charter should return the created or updated AgentConsumer, the registration/update event id, any matched queued work item ids, and a match event count. Full snapshots belong to explicit inspection commands or read endpoints like charter consumers, GET /api/project, GET /api/consumers, and GET /api/work-items.

When a consumer is registered after work already exists, Charter can rematch existing queued work by re-evaluating each queued work item's source event against that consumer's subscriptions. Rematching updates matchedConsumerIds; it does not recreate the source event or duplicate the work item.

WorkPacket #

The exact packet handed to a consumer run.

Fields:

  • id.
  • consumerId.
  • requestId when created for execution.
  • workItemIds.
  • eventIds.
  • documentVersionIds.
  • affectedDocumentIds.
  • promptDocumentIds.
  • attachedDocumentIds.
  • documentVersionIds: document id to content hash/version id for attached context.
  • prompt: rendered prompt for simple runner compatibility.
  • promptSha256.
  • outputContract.
  • status: planned, executing, executed, failed, blocked, or ignored.
  • raw output fields and result text after execution.
  • createdAt / updatedAt.

Packets make runs inspectable and reproducible. The packet is not the whole world; it is the bounded work context Charter chose to hand over.

AgentRequest #

Database-backed request for an implementation agent or other consumer runner.

Fields:

  • id: stable request identity derived from the packet or prompt hash.
  • status: planned, executing, executed, failed, or blocked.
  • createdAt / updatedAt: lifecycle timestamps.
  • requestedBy: request authority, initially charter.agent.
  • consumerId: registered consumer handling the request.
  • packetId: linked packet.
  • command: optional runner command used for execution.
  • changeIds: spec document ids that changed.
  • workItemIds: durable work items attached to this request.
  • attachedDocumentIds: spec document ids attached as request context.
  • prompt: rendered runner prompt for simple runner compatibility.
  • promptSha256: prompt content hash.
  • resultText: optional runner summary.
  • exitCode: optional runner exit code.

Agent context is document/span/comment attachment plus prompt documents. If an agent needs more durable context, attach or create spec documents rather than writing to a separate memory table.

Agent output capture #

AgentRequest records should store both structured completion and raw execution output.

Structured fields:

  • status: planned, executing, executed, failed, or blocked.
  • reportStatus: done, failed, blocked, ignored, or seen when explicitly reported by the consumer.
  • resultText: human-readable completion report.
  • reportedAt: explicit report timestamp.

Raw audit fields:

  • stdoutText
  • stderrText
  • outputText
  • exitCode

WorkItem records mirror the actionable status and report text so queue consumers do not need to inspect the parent request for common routing decisions.

AgentActivity #

Agent activity is optional at first, but the model should have a place for it.

Examples:

  • consumer saw event and chose no-op.
  • consumer constructed packet.
  • consumer requested more context.
  • consumer failed while resolving a span.
  • consumer produced a proposal.
  • selected reasoning trace or diagnostic was recorded.

Activity records can enter the project event stream when they are meaningful to other consumers. They should not become a dumping ground for every token unless the UI has a reason to expose that much machine weather.