Skip to content

Commit 6deb7d4

Browse files
Document complete development graph
1 parent eb416dd commit 6deb7d4

5 files changed

Lines changed: 230 additions & 27 deletions

File tree

AGENTS.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Software Development Graph
2+
3+
This repository implements a durable, local-LLM software-development workflow orchestrated with Mastra. It is not merely a GitHub webhook receiver. Preserve the complete graph described in `docs/architecture.md` whenever changing the implementation.
4+
5+
## Operating model
6+
7+
The production graph starts from a GitHub event and first checks whether the issue contains enough information. It may ask a human for clarification or propose child issues before implementation. One write-capable Codex worker then implements the accepted issue in an isolated worktree using a persistent goal and explicit acceptance criteria.
8+
9+
After deterministic checks, independent read-only reviewers run in parallel. The required reviewer perspectives are security, architecture, technology/framework, performance, tests and acceptance criteria, accessibility, code quality, bug detection, and visual design consistency. A manager node consolidates their evidence, removes duplicates, resolves compatible recommendations, and identifies genuine conflicts. It may send repair work back to implementation. When competing recommendations depend on product priorities or cannot be resolved from the recorded criteria, it suspends the run and presents the arguments to a human.
10+
11+
Successful runs may proceed to a pull request and human approval. All steps emit evidence to the recorder. Production runs never rewrite their own prompts, skills, or graph.
12+
13+
The self-improvement graph is a separate scheduled process. It reads newly recorded runs after `lastAnalysedRunId`, distils recurring lessons, proposes versioned candidate changes, and evaluates each candidate against a frozen baseline using replay cases. A candidate is promoted only when it improves the agreed metrics without violating regression or safety gates and a human approves it.
14+
15+
## Validation boundaries
16+
17+
Keep these concepts separate in code and documentation:
18+
19+
1. Inner self-correction checks whether the agent's latest action worked.
20+
2. Codex goal validation checks whether the full stated outcome and acceptance criteria are satisfied.
21+
3. Deterministic checks execute externally defined tests, builds, linters, type checks, and policy checks.
22+
4. Independent reviewers assess concerns not established by the implementation worker's own tests.
23+
5. Human approval resolves product decisions and accepts high-impact changes.
24+
25+
Agent-written tests are useful implementation artifacts, but they are not independent proof. Acceptance criteria, existing tests, externally defined checks, review evidence, and human evaluation form the independent verification boundary.
26+
27+
## Safety and concurrency
28+
29+
GitHub delivery IDs are idempotency keys. Persist an event before acknowledging it. An event for an active issue joins that run's inbox and is consumed only at a safe boundary; it must not interrupt a model turn. Different issues may have separate durable runs, but only one write-capable worker may use a given worktree at a time. Reviewers are read-only and may run concurrently after the implementation snapshot is fixed.
30+
31+
Do not let the system review or merge its own bot-generated GitHub events. Version prompts, skills, rubrics, graph definitions, models, and acceptance criteria on every recorded run. Never auto-promote a learned candidate merely because it is frequent or recent.
32+
33+
## Current implementation status
34+
35+
The repository currently implements GitHub ingestion, duplicate suppression, same-issue correlation, a local implementation-slot queue, Mastra persistence, and human suspension/resumption. The implementation action is still simulated. The readiness/decomposition node, real Codex goal worker, reviewer fan-out, manager, recorder schema, pull-request integration, and scheduled learning graph remain to be implemented. Do not describe those pieces as complete until code and tests exist.
36+
37+
When adding a node, update `docs/architecture.md`, add durable state for its inputs and outputs, and test success, retry, suspension, and duplicate-delivery behavior where applicable.

README.md

Lines changed: 34 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,26 @@
1-
# Durable GitHub → Mastra loop
1+
# Local-LLM software-development graph
22

3-
This is the small control plane we discussed for the webinar. GitHub events are
4-
recorded in SQLite first and acknowledged quickly. A coordinator then gives
5-
Mastra at most one implementation slot by default. Consequently, an event that
6-
arrives while Mastra is working is never used to interrupt the current model
7-
turn.
3+
The intended system is a complete development graph: issue readiness and
4+
decomposition, Codex goal-based implementation, deterministic checks, parallel
5+
specialist reviewers, manager consolidation and human escalation, recording,
6+
and a separate scheduled self-improvement graph. The durable GitHub → Mastra
7+
loop below is the first implemented slice, not the complete workflow.
88

9-
For an event concerning the same issue, the coordinator attaches it to the
10-
existing run's inbox. For a different issue it creates another run, which waits
11-
for the local implementation slot. GitHub delivery IDs make retries harmless,
12-
and messages produced by the configured bot are ignored to prevent feedback
13-
loops.
9+
Future agent sessions should begin with `AGENTS.md`. The full design, reviewer
10+
contract, conflict handling, visual-design evidence, recorder, and learning
11+
boundary are documented in `docs/architecture.md`. Machine-readable node status
12+
and the required reviewer set live in `src/graph-definition.ts`.
13+
14+
## Implemented control-plane slice
15+
16+
GitHub events are recorded in SQLite first and acknowledged quickly. A
17+
coordinator gives Mastra at most one implementation slot by default. An event
18+
that arrives while Mastra is working never interrupts the current model turn.
19+
20+
For the same issue, the coordinator attaches the event to the existing run's
21+
inbox. For a different issue it creates another run, which waits for the local
22+
implementation slot. GitHub delivery IDs make retries harmless, and messages
23+
produced by the configured bot are ignored to prevent feedback loops.
1424

1525
```mermaid
1626
flowchart LR
@@ -38,32 +48,29 @@ pnpm start
3848

3949
The service listens only on `127.0.0.1:4317`. `GET /health` shows whether an
4050
implementation occupies the slot, and `GET /runs` shows the durable run state.
41-
The event database and Mastra's workflow snapshots live separately under
42-
`.data/`.
51+
The event database and Mastra workflow snapshots live separately under `.data/`.
4352

4453
The included [GitHub Actions workflow](.github/workflows/local-loop.yml) uses a
4554
self-hosted runner labelled `local-llm`. That runner opens an outbound
4655
connection to GitHub, receives the job, and posts the event to the loop service
47-
on the same machine. Therefore the local machine does not need a public inbound
48-
port. For a direct GitHub webhook instead, expose the receiver through a secure
49-
tunnel and set `GITHUB_WEBHOOK_SECRET`; the receiver validates
50-
`x-hub-signature-256` against the raw body.
56+
on the same machine. The local machine therefore needs no public inbound port.
57+
For a direct GitHub webhook, expose the receiver through a secure tunnel and set
58+
`GITHUB_WEBHOOK_SECRET`; the receiver validates `x-hub-signature-256`.
5159

5260
## Human decisions
5361

54-
Add the label `needs-human` to an issue to demonstrate suspension. Mastra stores
55-
the suspended workflow snapshot and the control database marks the run as
56-
`waiting_human`. A subsequent human issue comment is correlated with that issue
57-
and resumes the exact Mastra run. Other issues may proceed while this one waits.
62+
Add the label `needs-human` to demonstrate suspension. Mastra stores the
63+
suspended workflow snapshot and the control database marks the run as
64+
`waiting_human`. A subsequent human issue comment resumes the exact run. Other
65+
issues may proceed while this one waits.
5866

59-
## Where Codex fits
67+
## Where Codex fits today
6068

6169
The current implementation step deliberately waits for
62-
`SIMULATED_IMPLEMENTATION_MS`; this makes concurrency behavior deterministic in
63-
the demo and tests. Replace that delay inside `src/workflow.ts` with the Codex
64-
goal invocation. The durable inbox, one-writer rule, suspension, and event
65-
routing stay unchanged. Review agents can then be added after implementation as
66-
parallel Mastra branches, followed by the manager/consolidation node we designed.
70+
`SIMULATED_IMPLEMENTATION_MS`; this makes concurrency deterministic in the demo
71+
and tests. It still needs to be replaced by the real Codex goal worker. The
72+
readiness node, reviewers, manager, recorder, pull-request integration, and
73+
self-improvement graph are specified but explicitly marked as planned.
6774

6875
## Verify
6976

docs/architecture.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Intended software-development graph
2+
3+
The webhook receiver is the entrance to the system, not the system itself. The target is a durable graph in which Codex performs one bounded implementation, independent specialists review the resulting snapshot, a manager reconciles their findings, and humans decide questions that cannot be derived from acceptance criteria or evidence.
4+
5+
```mermaid
6+
flowchart TD
7+
E["GitHub event"] --> I["Durable inbox"]
8+
I --> R["Readiness and decomposition"]
9+
R -->|missing information| H1["Ask human and suspend"]
10+
H1 --> R
11+
R -->|split needed| S["Propose child issues"]
12+
S --> H1
13+
R -->|ready| C["Codex implementation with goal"]
14+
C --> D["Deterministic checks"]
15+
D -->|failure| C
16+
D -->|pass| F["Parallel specialist reviews"]
17+
F --> M["Review manager"]
18+
M -->|repairable findings| C
19+
M -->|unresolved trade-off| H2["Human decision"]
20+
H2 --> C
21+
M -->|accepted| P["Pull request and human approval"]
22+
P --> O["Recorder / experience store"]
23+
```
24+
25+
Readiness is explicit because implementation must not compensate for an underspecified issue by inventing product decisions. It checks the definition of done, constraints, affected behaviour, required visual references, and available repository context. If the change contains multiple independently deliverable outcomes, it proposes child issues with separate acceptance criteria. Creating them is a human-approved side effect.
26+
27+
The implementation node has exclusive write access to an isolated worktree. Codex uses a persistent goal containing the accepted outcome, constraints, and verification criteria. Its internal corrections and goal completion are useful but do not count as independent review. Repository-owned tests, builds, linting, type checks, and policy checks run before reviewers receive an immutable commit or diff.
28+
29+
## Review fan-out and manager
30+
31+
The required reviewer set lives in `src/graph-definition.ts`: security, architecture, technology/framework, performance, tests and acceptance criteria, accessibility, code quality, bug detection, and visual design. Every reviewer gets the same issue, acceptance criteria, base and candidate revisions, diff, deterministic evidence, and repository guidance. Reviewers are read-only and return structured, evidenced findings rather than modifying code.
32+
33+
The visual-design reviewer must inspect rendered evidence rather than infer appearance from source. Its inputs include baseline and candidate screenshots at agreed viewports, relevant design tokens or component examples, and important interaction states. It judges hierarchy, spacing, typography, colour, responsiveness, visual regressions, and consistency with the surrounding application. Accessibility remains separate because a coherent-looking screen can still be unusable with a keyboard or assistive technology.
34+
35+
The manager does not decide by majority vote. It groups duplicate findings, checks evidence, applies explicit project priorities, and produces one repair brief. If an architecture recommendation conflicts with a performance recommendation, measurements and existing constraints may resolve it. If the choice depends on an unstated product priority, the manager suspends the run and presents both arguments, evidence, costs, and the decision required to a human. That answer becomes durable input to the same run.
36+
37+
Repair loops are bounded. Exceeding the maximum implementation/review iterations escalates to a human instead of creating an infinite loop.
38+
39+
## Events and concurrency
40+
41+
Every delivery is persisted before acknowledgement. Duplicate delivery IDs are no-ops. An event for an active issue joins its inbox and is read at a safe boundary; it never interrupts a model turn. Different issues retain independent durable state, while a lease prevents two writers from using the same worktree. Reviewers may run in parallel because they are read-only. Bot-authored events and run markers prevent self-triggering.
42+
43+
The current code implements this control-plane slice and generic Mastra suspension/resumption. `src/graph-definition.ts` records which nodes are implemented, partial, or planned so future sessions cannot mistake the design for completed behaviour.
44+
45+
## Recorder and separate self-improvement graph
46+
47+
Every production node will emit an append-only record containing its inputs, outputs, timestamps, tool calls, command results, changed files, deterministic results, reviewer findings, manager resolutions, human decisions, merge/revert/regression outcome, and exact model, prompt, skill, rubric, and graph versions. The recorder observes production; it never changes the graph that generated the evidence.
48+
49+
```mermaid
50+
flowchart LR
51+
P["Production runs"] --> X["Append-only experience store"]
52+
X --> N["Select after lastAnalysedRunId"]
53+
N --> L["Distil recurring lessons"]
54+
L --> C["Versioned candidate"]
55+
C --> B["Baseline vs candidate replay"]
56+
B --> G{"Regression and safety gates"}
57+
G -->|fail| Q["Reject with evidence"]
58+
G -->|pass| A["Human promotion approval"]
59+
A --> V["New version for future runs"]
60+
```
61+
62+
The learning graph is scheduled separately from production. `lastAnalysedRunId` makes discovery incremental, while older runs remain as replay and regression cases. Baseline and candidate run the same representative cases. Quality, completion, regressions, safety, cost, latency, iteration count, and escalation rate are compared. Frequency or plausible wording is not proof of improvement.
63+
64+
Promotion is versioned and human-approved, affects only future runs, and retains the previous version for rollback. This boundary makes the system self-improving without allowing it to silently rewrite its own rules during active development.

src/graph-definition.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/** Machine-readable contract for the graph we are building. */
2+
export type NodeStatus = "implemented" | "partial" | "planned";
3+
4+
export interface GraphNodeDefinition {
5+
id: string;
6+
responsibility: string;
7+
kind: "deterministic" | "agent" | "parallel" | "human-gate" | "side-effect";
8+
status: NodeStatus;
9+
}
10+
11+
export interface ReviewerDefinition {
12+
id:
13+
| "security"
14+
| "architecture"
15+
| "technology"
16+
| "performance"
17+
| "tests"
18+
| "accessibility"
19+
| "code-quality"
20+
| "bugs"
21+
| "visual-design";
22+
question: string;
23+
requiresVisualEvidence: boolean;
24+
}
25+
26+
export const reviewers: readonly ReviewerDefinition[] = [
27+
{ id: "security", question: "Does the change introduce exploitable behavior, unsafe trust boundaries, or leaked secrets?", requiresVisualEvidence: false },
28+
{ id: "architecture", question: "Does the change preserve the application's boundaries, ownership, and long-term design?", requiresVisualEvidence: false },
29+
{ id: "technology", question: "Does it follow the current framework, language, and repository-specific practices?", requiresVisualEvidence: false },
30+
{ id: "performance", question: "Does it create unacceptable latency, memory, network, bundle-size, or scaling costs?", requiresVisualEvidence: false },
31+
{ id: "tests", question: "Are the acceptance criteria independently covered, including important failure paths?", requiresVisualEvidence: false },
32+
{ id: "accessibility", question: "Can people using keyboards and assistive technology perceive and operate the result?", requiresVisualEvidence: true },
33+
{ id: "code-quality", question: "Is the change understandable, maintainable, cohesive, and appropriately simple?", requiresVisualEvidence: false },
34+
{ id: "bugs", question: "What incorrect behavior, edge cases, races, or regressions remain?", requiresVisualEvidence: false },
35+
{ id: "visual-design", question: "Is the rendered result visually appealing and consistent with the rest of the application?", requiresVisualEvidence: true },
36+
] as const;
37+
38+
export const productionGraph: readonly GraphNodeDefinition[] = [
39+
{ id: "github-ingest", responsibility: "Persist, deduplicate, correlate, and acknowledge GitHub events.", kind: "deterministic", status: "implemented" },
40+
{ id: "readiness-and-decomposition", responsibility: "Verify sufficient context and decide whether the issue should be split into child issues.", kind: "agent", status: "planned" },
41+
{ id: "human-clarification", responsibility: "Suspend and resume when missing information or a product decision requires a human.", kind: "human-gate", status: "partial" },
42+
{ id: "codex-goal-implementation", responsibility: "Implement one accepted issue in an isolated worktree against explicit acceptance criteria.", kind: "agent", status: "planned" },
43+
{ id: "deterministic-checks", responsibility: "Run repository-owned tests, builds, linting, type checks, and policy checks.", kind: "deterministic", status: "planned" },
44+
{ id: "specialist-reviewers", responsibility: "Run the independent reviewer set in parallel against one fixed implementation snapshot.", kind: "parallel", status: "planned" },
45+
{ id: "review-manager", responsibility: "Consolidate evidence, resolve compatible findings, route repairs, and expose true conflicts.", kind: "agent", status: "planned" },
46+
{ id: "human-conflict-decision", responsibility: "Choose between irreconcilable recommendations using their evidence and trade-offs.", kind: "human-gate", status: "planned" },
47+
{ id: "pull-request", responsibility: "Publish an approved, verified change and its evidence exactly once.", kind: "side-effect", status: "planned" },
48+
{ id: "recorder", responsibility: "Append reconstructable run evidence without modifying the active graph.", kind: "deterministic", status: "planned" },
49+
] as const;
50+
51+
export const selfImprovementGraph: readonly GraphNodeDefinition[] = [
52+
{ id: "select-new-experiences", responsibility: "Read production evidence after lastAnalysedRunId while retaining older replay cases.", kind: "deterministic", status: "planned" },
53+
{ id: "distil-lessons", responsibility: "Identify recurring failures and propose bounded prompt, skill, rubric, or graph changes.", kind: "agent", status: "planned" },
54+
{ id: "baseline-versus-candidate-replay", responsibility: "Run identical representative cases against the frozen baseline and each candidate.", kind: "parallel", status: "planned" },
55+
{ id: "improvement-gates", responsibility: "Reject regressions, safety violations, semantic drift, and unsupported gains.", kind: "deterministic", status: "planned" },
56+
{ id: "human-promotion", responsibility: "Approve a versioned candidate for future runs; never mutate active runs.", kind: "human-gate", status: "planned" },
57+
] as const;

test/graph-definition.test.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import assert from "node:assert/strict";
2+
import test from "node:test";
3+
import {
4+
productionGraph,
5+
reviewers,
6+
selfImprovementGraph,
7+
} from "../src/graph-definition.js";
8+
9+
test("the graph contract retains every agreed independent reviewer", () => {
10+
assert.deepEqual(
11+
reviewers.map(({ id }) => id),
12+
[
13+
"security",
14+
"architecture",
15+
"technology",
16+
"performance",
17+
"tests",
18+
"accessibility",
19+
"code-quality",
20+
"bugs",
21+
"visual-design",
22+
],
23+
);
24+
});
25+
26+
test("production and learning remain separate graphs with explicit status", () => {
27+
assert.ok(productionGraph.some(({ id }) => id === "recorder"));
28+
assert.ok(selfImprovementGraph.some(({ id }) => id === "human-promotion"));
29+
assert.equal(
30+
productionGraph.find(({ id }) => id === "codex-goal-implementation")?.status,
31+
"planned",
32+
);
33+
assert.equal(
34+
selfImprovementGraph.find(({ id }) => id === "baseline-versus-candidate-replay")
35+
?.status,
36+
"planned",
37+
);
38+
});

0 commit comments

Comments
 (0)