- Treat
plan.mdas the single product, architecture, and implementation authority. - GraphX is a strict Task Graph Executor. Do not turn it into a Coding Agent, workflow planner, context manager, tool gateway, sandbox, distributed scheduler, or general workflow platform.
- Preserve the control rule: Workflow Config owns control, GraphX validates and advances the graph, and Codex tasks perform semantic work.
- Keep workflow-specific stages, prompts, node IDs, dependencies, conditions, and completion criteria in configuration. Never hard-code a particular engineering workflow into the Executor.
- Implement the initial system in Python 3.12. Use Pyright strict, Ruff, pytest, strict runtime models, standard-library SQLite, a Python MCP server, and a Codex Skill.
- Treat static typing as an implementation aid, not a runtime trust boundary. Validate Workflow JSON, MCP inputs, Codex results, SQLite rows, and recovered state before use.
- Keep
WorkflowConfig, immutableWorkflowIR, andRunStateas separate types. Only the Compiler may construct a valid IR, and an IR must not change during a run. - In production code, do not use unvalidated
Any,dict[str, Any],cast()as validation, unexplained type-ignore comments,eval,exec, monkey patching, dynamic Runner imports, pickle protocols, or dynamic state mutation withsetattr(). - Use enums, tagged frozen dataclasses, immutable containers, strong ID types, and
assert_never()for state unions. Do not use bare strings for node or run states. - Only
application/state_committer.pymay commit NodeState or RunState changes;core/runtime/transitions.pyonly returns pure decisions. Host adapters and Codex tasks return structured requests or results and never write state directly. - Persist authoritative run state through Application-owned Store ports implemented by
adapters/store/sqlite/. Only that SQLite adapter may importsqlite3, open connections, or execute SQL;bootstrap.pymay pass it the private database path solely for construction and must not expose that path elsewhere. Enforce keys, references, uniqueness, mutation leases, and idempotency with database constraints and transactions, not Python checks alone. - The initial scheduler dispatches one node at a time in stable node-ID order. Every
workspaceMutationnode must hold the single workspace-scoped mutation lease. - Never release a mutation lease merely because of timeout, process exit, or restart. Reconcile the execution: an unknown external disposition becomes
ambiguous; a known but unsettled mutation remainsblocked(or becomesambiguousif its recovery operation is unknown). Both block later mutation. - Persist a
DispatchReservationbefore creating an external task. A successful bind atomically creates oneAgentAttemptand one immutable execution handle for exactly one independent, visible Codex task; activate the declared Task Contract only after that bind. Titles are display labels, not identity. A retry creates a new reservation, attempt, and task. - Leave model context windows, compaction, conversation history, native tools, and sandboxing to Codex. An activated Agent task receives only its declared Task Contract; before activation, a bootstrap task may receive only reservation identity and task-binding metadata, never semantic work instructions. Neither may rewrite the Graph.
- Require runtime Schema checks for NodeResult identity, output shape, workspace revision, and evidence before committing success. An Agent saying “done” never satisfies a terminal condition by itself.
- Keep MCP operations short and map them to Application use cases. MCP handlers never own state semantics or transactions, and no tool call may block for an entire long-running workflow.
- Add or update tests for Schema rejection, import boundaries, read-only Query Service capabilities, Graph analysis, deterministic scheduling, atomic
graphx_next, dispatch reservation/bind/activation, Agent and mechanical execution identity, every transition, retry limits, duplicate/stale results, SQLite row rejection, transaction recovery, mutation lease exclusivity and settlement,ambiguous, and terminal gates. - Enforce service-side dependencies as
inbound adapters -> application -> core, with the SQLite adapter implementing Application-owned Store ports.core/performs no I/O and cannot import outer layers;application/depends only on Core and its own narrow ports;protocol/defines dependency-neutral versioned wire DTOs and imports no GraphX layer. The external Host Adapter may depend only onprotocol/and external APIs, never on Core, Application, inbound adapters, or Store.bootstrap.pycomposes the GraphX Service;adapters/host/main.pystarts the Host separately. Keep each module small.