Developer guide
This guide covers the repository layout, the engine architecture, and the conventions for tests, benchmarks, and documentation.
Repository layout
Section titled “Repository layout”rules/,session/,dsl/,scenario/: the public packages.rulesowns the public rule-definition values, workspace facade, and compiled ruleset facade;sessionexplicitly constructs engine-backed workspaces and exposes the runtime facade;dslexposes the loader facade; andscenarioowns the portable scenario and run-report artifacts and their shared, lossless JSON value contract. Keep public import paths stable while the engine evolves, and avoid exposing new engine internals directly.scenario/artifact_types.godeclares the portable scenario and run-report data types,artifact_number.goowns precision-safe structural counters,artifact_validation.goowns semantic validation and canonical ordering, andartifact_json.goowns strict decoding, canonical encoding, and artifact digests. Keep all Gess-valued fields on the sharedscenario.Valueencoding instead of inventing another JSON projection.internal/engine/: nearly all implementation code, as one flat package with tests and benchmarks beside the implementation files.internal/gesssexp/: the S-expression lexer, parser, and canonical formatter behind the.gesslanguage andgessfmt.internal/fnvhash/: hash primitives used for order-independent token and identity hashing.cmd/gessc/,cmd/gessfmt/: generation and formatting tools, thin wrappers overdsl.GenerateGoandgesssexp.Format.cmd/gess/owns the REPL;cmd/gess-mcp/owns the official MCP SDK dependency and the serialized, root-confined agent-facing session adapter.examples/,tutorial/: runnable examples and the workshop; seeexamples.md.docs/: this documentation set.
Engine architecture
Section titled “Engine architecture”The compiled Rete graph is the only production matching runtime. Feature
gaps are closed with explicit graph node types and session-owned memories;
rule shapes the graph can’t represent fail with an error wrapping
ErrUnsupportedRuntime rather than falling back to a slower path.
A map of internal/engine by subsystem:
- Definitions and compilation:
template.go(templates, duplicate policies, backchain demand template synthesis),rule.go(rule specs and condition forms, including higher-order conditions),ruleset.go(workspace and compiled ruleset revisions),condition.go(condition plans),branch_planning.go(condition ordering andorbranch planning),query.go(queries and query terminals). - Expressions and predicates:
expression.go(expression trees and alpha/beta-residual placement),predicate.go(field constraints),path.go(nested paths),list_pattern.go(list destructuring),pure_function.go(host pure functions). - Rete graph:
rete_graph.go(graph structure, alpha routing, node interning),rete_graph_beta.go(runtime beta memory and propagation),rete_beta.go(token rows and identity hashing),rete_graph_join_outputs.goandrete_graph_token_bucket_table.go(join output and bucket storage),rete_graph_negative_beta.go(blocker-count negation),rete_graph_aggregate.goandaggregate.go(aggregates),rete_graph_terminal_memory.go(terminal token memory),rete_runtime.go(revision-to-graph bridge),fact_field_index.go(alpha field indexes). - Session runtime:
session.go(lifecycle, mutations, queuing) coordinates named session owners:session_fact_store.go(working memory),session_propagation.go(Rete runtime and terminal-delta lifecycle),session_agenda_driver.go(agenda, strategy, and focus),session_tms_store.goandsession_backchain_store.go(support state), andsession_diagnostics_exporter.go(events and explain history).run.godrives firing;agenda.goimplements module queues and conflict resolution;fact.go,mutation.go,snapshot.go,event.go, andaction.goprovide the runtime values and boundaries. Truth maintenance and backward chaining remain implemented inlogical_support.go,backchain_demand_support.go, andbackchain_query_proof.go.runtime_diagnostics.goandpropagation_counter.goprovide instrumentation. Public values, errors, and identifiers are declared inrules; the corresponding engine files retain compiler and runtime helpers. - The
.gesslanguage:gess_dsl.goandgess_dsl_parse.go(loader and parser),gess_generate.go(Go code generation forgessc).
Session ownership and graph invariants
Section titled “Session ownership and graph invariants”Each mutable session subsystem has one named owner. Fork shares the
immutable compiled ruleset, deep-clones fact, agenda, focus, and TMS state,
rebuilds mutable Rete and backchain ownership, and starts run/proof scratch
fresh. Event listeners and explain history are configured fresh; event
sequence continuity and the documented output-writer reference carry over.
session_fork_ownership_test.go recursively inventories Session and every
named session* owner. Adding or moving a field requires an explicit fork
ownership policy and rationale.
Several internal contracts are load-bearing:
- Every
orbranch exposes identical binding names and templates. - Large Cartesian
orproducts compile to support-counted union stages; the bounded public branch list is inspection data, never an execution path. conditionPlansremain in planned execution order. Public condition order is resolved throughbindingSlot, never by positional indexing withcondition.Order.- Compiled condition-tree paths remain authored paths. Physical planning must not rewrite them; rule and ruleset identities are plan-independent.
- Activation identity is the binding tuple, including its fact identities and versions; display paths are not identity.
- Terminal and agenda deltas have one owner. A delta that does not report owned storage may alias transient Rete arenas and must be applied or cloned before that arena is released.
Keep these contracts explicit when adding graph nodes, changing planning, or extending session lifecycle transitions.
Development workflow
Section titled “Development workflow”Run Go commands from the module root. After each implementation step:
gofmt -w <touched-go-files>go fix ./...go test ./...Style expectations:
- Idiomatic Go; small interfaces defined near consumers; explicit errors
with wrapping and sentinels where callers need
errors.Isorerrors.As;context.Contexton operations that run, block, or call user-provided behavior. - Commits use Conventional Commit messages, such as
feat(memory): add fact identity model.
go test ./... is the normal verification command. Conventions in
internal/engine:
- Contract and table-driven tests cover rule-engine semantics;
semantic_scenario_test.goholds end-to-end scenarios asserting fired action traces. matcher_oracle_test.godefines a brute-force oracle matcher, and parity helpers assert that the Rete graph agrees with it. New matching behavior should extend oracle parity coverage.executable_semantics_fuzz_test.gogenerates bounded template, rule, fact, and lifecycle corpora, checks graph-vs-oracle parity after every mutation, and verifies byte-identical explain JSON for equal histories.- Fixture runners (Miss Manners in
manners_runner_test.go, claims triage, loan underwriting, and the scaling runners) double as correctness tests and as opt-in performance harnesses gated byGESS_*_RUNNERenvironment variables, with knobs for iterations, fact counts, and profiles. reconcile_path_inventory_test.gois a governance test: production has no whole-terminal reconcile path; retained parity helpers must stay in test files and remain explicitly classified.- Initial construction,
Reset, andApplyRulesetbuild graph memory through propagation events and update the agenda from owned terminal lifecycle deltas. A missing lifecycle delta isErrUnsupportedRuntime, never a signal to rematch all terminals.
Behavior changes come with new or updated tests, preferring contract-style and table-driven forms.
Scenario and run-report schema changes require strict-decoder cases, canonical
re-encoding coverage, and checked-in JSON fixtures under
scenario/testdata/<version>/. Keeping each schema version in its own directory
prevents a version bump from silently overwriting the previous wire contract.
Golden files end with one file newline, but digest and canonical-byte assertions
use the compact bytes from MarshalScenario or MarshalRunReport without that
newline.
Benchmarks
Section titled “Benchmarks”Benchmark files are named *_benchmark_test.go. Run them with:
go test -bench=. -benchmem ./internal/engine/Add benchmarks only where performance claims or regressions matter: propagation fanout, bucket probes, row scans, memory growth, modify cost, retract cost, and agenda deltas all have precedents to follow.
Documentation workflow
Section titled “Documentation workflow”Markdown in this repository is checked with Vale, configured by
.vale.ini: the Google style at warning level, with project terms
accepted through the vocabulary in
.vale/styles/config/vocabularies/Gess/accept.txt.
After editing any Markdown file:
vale <changed .md files>Fix findings rather than weakening .vale.ini. Add a term to the
vocabulary only when it’s a genuine project or domain term, such as a
product name or a rule-engine term of art. Keep code identifiers, flags,
and .gess forms in backticks, which Vale ignores as code.
When documentation changes commands, snippets, or generated-code
instructions, run the affected example tests, or go test ./... for
broad changes.