graph

package
v0.30.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 11, 2026 License: Apache-2.0 Imports: 21 Imported by: 0

Documentation

Overview

Package graph implements the experimental judgment-graph surface (ADR-0015): a runtime-owned composition of packs a project's jpack.json already names, prototyping the composition the Judgment Pack Specification's RFC 0002 (Draft) proposes.

Nothing here is specified. No JPS version defines a graph, an edge, or a composite result, so this whole package is a non-normative convention of this runtime, labeled as such in every payload, on the same footing as the project convention (ADR-0012) and as removable as any experimental surface (ADR-0007). What is specified is each node's own evaluation: a node is one ordinary §§7-8 evaluation of one conformant pack, performed by the same engine every other surface uses, and this package never reaches inside it.

The composition rules are deliberately small, and each one leans on a decision the runtime already made rather than inventing a new one:

  • A graph references packs by project decision id and never contains one. Identity stays in the pack document (ADR-0012), resolution goes through the project's own jailed directory handle, and a graph cannot change what any pack means.
  • Edges feed dispositions forward in two declared ways: an outcome id written at a facts pointer, and a tri-state contribution to evidence availability. An upstream that produced no outcome injects no fact — the pointer is simply absent, which §7 already reads as unknown — and contributes the edge's declared onUnresolved tri-state, "unknown" unless the edge says "absent". Unresolved upstreams therefore reach a downstream pack only through that pack's own declared unknown handling.
  • The edges must form a DAG, evaluation order is the deterministic topological order with node ids breaking ties, and every node always evaluates: there is no conditional execution to reason about, and a side judgment's escalation is as visible as the result node's.
  • Conflicts are refused, never merged: two edges feeding the same fact pointer, overlapping fact pointers (one inside the other), or the same evidence requirement of one node are each a validation error, and a feed that collides with a caller-supplied input is an evaluation refusal. A merge rule would be a semantics nobody declared.
  • Assembly is bounded: a fact pointer is at most 64 tokens deep, one node's injected outcome ids are charged against the carrier's byte limit before they are written, an assembled document past what the preflight admits is handed to the engine as oversized rather than carried, and encoding adds no escaping inflation — so composing inputs allocates no more than the inputs document's own limit already admitted.

Index

Constants

View Source
const (
	// FormatVersion is the one formatVersion value this runtime accepts, a
	// single integer as a string on the jpack.json configVersion precedent:
	// this file describes a shape a program reads, and a shape either is the
	// one this program knows or is not. It is deliberately not the document's
	// own version member — a graph has an identity version like a pack does,
	// and the two must never share a name in a payload.
	FormatVersion = "1"
	// SchemaID is the embedded schema's own $id.
	SchemaID = "urn:judgmentpack:runtime:jgraph:1"
	// MaxGraphBytes bounds one graph document. It is an index of nodes and
	// edges, not a pack.
	MaxGraphBytes = int64(1 << 20)
	// MaxInputsBytes bounds one inputs document, which carries facts and
	// evidence documents for every node and is read whole before anything runs,
	// on the instance-matrix precedent.
	MaxInputsBytes = int64(16 << 20)
	// MaxFactPointerDepth bounds one fact pointer's reference-token count. The
	// carrier admits documents 128 levels deep, so a pointer bounded at half
	// that keeps every assembled facts document decodable by the engine's own
	// nesting limit instead of exhausting the assembler first; no honest fact
	// path approaches either number.
	MaxFactPointerDepth = 64
)
View Source
const (
	// GraphMatrixVersion is the newest graphMatrixVersion this runtime
	// accepts. It moved to "2" for the handoff-target assertions (ADR-0032),
	// on matrixVersion's own reasoning: a matrix is a closed input, so adding
	// a member moves the version whatever the addition is, and the version
	// gate is what turns "this member is not known" into "this matrix needs
	// graphMatrixVersion 2".
	GraphMatrixVersion = "2"
	// GraphMatrixVersionDefault is what rows declaring no graphMatrixVersion
	// are read as, on the matrixVersion precedent: silence is the version
	// that existed before any member was version-gated, never the newest.
	GraphMatrixVersionDefault = "1"
	// MaxRowsBytes bounds one graph matrix document, which carries an inputs
	// document per row, on the instance-matrix precedent.
	MaxRowsBytes = int64(16 << 20)
	// MaxRowsCases bounds the rows of one graph matrix.
	MaxRowsCases = 10_000
)
View Source
const CodeInventoryBudget = "JPS-GRAPH-INVENTORY-BUDGET"

CodeInventoryBudget marks an inventory refused at its byte budget, the inventory sibling of the walk's JPS-GRAPH-REPORT-BUDGET: a truncated inventory would under-report silently, so none is produced.

View Source
const CodeReportBudget = "JPS-GRAPH-REPORT-BUDGET"

CodeReportBudget marks the one failure an entry may raise that is NOT that entry's in-band mismatch: the whole run is refused.

View Source
const MaxEntryDetailBytes = 4096

MaxEntryDetailBytes caps a graph entry's human-readable detail, marker included: the value this returns is never longer than the cap. Some details echo matrix-supplied values, and a detail is a message for a person rather than data a caller reads programmatically, so truncating one costs nothing a reader needs.

Variables

This section is empty.

Functions

func EmptyInventory added in v0.19.0

func EmptyInventory(configPath, command string) result.GraphInventory

EmptyInventory is the graph inventory of a project with no configuration at all: an answer with its own explanation, never an error, exactly as the pack inventory's is.

func Evaluate

func Evaluate(loaded *project.Project, engine *evaluation.Engine, doc Document, graphPath string, inputs []byte, inputsSupplied bool, options Options) (result.GraphEvaluation, *evaluation.Failure)

Evaluate runs one graph: every node, in the deterministic topological order, each as one ordinary engine evaluation of its resolved pack against its own facts and evidence documents plus whatever the in-edges fed.

The graph is held to its Structural checks first, completely, before any node runs — and deliberately not to the pack-reading checks of Semantic: when a node runs, the engine reads the pack itself, so a malformed or non-conformant pack is refused with the §8.4 class and phase it deserves instead of an unclassed graph refusal, and an evidence feed naming an undeclared requirement is the engine's own malformed-input error in §8.2's order, with the node named. An invalid graph is refused before anything evaluates, a refused node refuses the whole run with that node named and its class intact, and no disposition of any kind accompanies either: errors are not dispositions, at the composite level as much as at the node level.

inputsSupplied says whether the caller supplied an inputs document at all, so empty bytes can be refused as the malformed input they are rather than read as "no inputs".

func Plan

func Plan(loaded *project.Project, doc Document, graphPath, command string) (result.GraphPlan, *evaluation.Failure)

Plan states one graph's evaluation plan without evaluating anything: the deterministic node order, and per node the pack it resolves to and every feed that will arrive before it runs. The one read it performs per node is the identity echo — the pack document's own id and version — and a document that cannot be read is reported on its step rather than failing the plan: a plan has to be printable while one pack is mid-edit, exactly as the project inventory has to be listable.

The graph itself is held to its Structural checks first, completely: a plan for a graph whose own rules do not check out would narrate an order nothing will ever run in. The pack-reading checks stay with validate, which is what lets this plan keep its promise about unreadable packs.

func ProjectDocument added in v0.19.0

func ProjectDocument(loaded *project.Project, id, command string) (result.GraphDocument, []byte, *project.Failure)

ProjectDocument serves one configured graph document by its configured id: the exact bytes beside their metadata, read through the project's own directory handle under this package's byte limit. Serving is not validating — a mid-edit graph is still served, with Status "undecodable" and Detail saying why, so the one moment a client most needs to see the document is not the moment the tool goes silent.

func ProjectInventory added in v0.19.0

func ProjectInventory(loaded *project.Project, command string) (result.GraphInventory, *project.Failure)

ProjectInventory is the resolved, token-cheap description of every configured graph: the configured id, the document's own id and version, the declared result node, the paths, whether rows are declared, and the configuration's description. It is what a client reads to learn what a project composes without fetching a document — the inventory the configuration schema's own description text has anticipated since ADR-0017.

A graph whose document cannot be read or decoded is listed with its Detail saying why and its identity members empty. Listing is not validating: the document is identity-read leniently, exactly as a pack inventory row is, and experimental graph validate is where a broken graph is an error.

func Schema

func Schema() []byte

Schema returns the exact embedded graph schema bytes.

func SchemaDescription

func SchemaDescription(command string) result.GraphSchema

SchemaDescription composes the metadata payload for the embedded schema, on the shape packs schema reports for the configuration schema.

func SupportedFormatVersions

func SupportedFormatVersions() []string

SupportedFormatVersions names every formatVersion this runtime accepts, so a refusal can say what would have been accepted instead of only what was not.

func SupportedGraphMatrixVersions added in v0.19.0

func SupportedGraphMatrixVersions() []string

SupportedGraphMatrixVersions names every graphMatrixVersion this runtime accepts, newest last, so a refusal can say what it would have taken rather than only what was wrong. The list is cumulative and literal — never derived from the newest constant, so a future bump cannot silently drop an intermediate version and with it the gate on the members it introduced (round-1 review) — and a test holds every version the introduction map names to a place in it.

func Test added in v0.9.0

func Test(loaded *project.Project, engine *evaluation.Engine, doc Document, graphPath, rowsPath string, rows Rows, options Options) (result.GraphTest, *evaluation.Failure)

Test runs one graph's matrix: every row, each as one Evaluate of the graph against the row's inputs document, judged exactly as a pack matrix row is — the RFC 8785 canonical §8.3 disposition compared byte for byte (the composite headline, and every node the row names), or the §8.4 class and phase the row expects. What this reports is what a project's own rows did; it is not the specification's corpus, and no JPS version defines a graph.

Beside the rows it reports coverage (ADR-0016): the probes the graph's declarations derive, and which of them some row's expectation witnesses. The derivation reads each node's pack once per run — not per row — through the same jailed handle the evaluation reads it through, so a pack that cannot be read derives no probes rather than failing the run. Coverage sits after the row loop because it reads expectations, which exist whether or not they held; it touches neither Status nor Summary, and a run with every probe missing still exits by its rows alone.

func TestProject added in v0.10.0

func TestProject(loaded *project.Project, engine *evaluation.Engine, id string, options Options) (result.GraphSuite, *evaluation.Failure)

TestProject runs every configured graph's matrix — or the one graph id selects — each entry exactly one Test of that graph against its declared rows. The walk inherits the packs test posture row for row: an entry whose graph or rows cannot be loaded is a mismatch, not a silent skip; an entry declaring no rows is skipped and says so; and a run in which no row ran at all is "skipped" rather than "passed", which the CLI exits non-zero on — a green gate over nothing tested is the failure mode this posture exists to prevent, and for the graph walk that exit is this record's own determination rather than something the packs walk decided (ADR-0017).

func ValidateProject added in v0.10.0

func ValidateProject(loaded *project.Project, id, command string) (result.GraphValidationSuite, *evaluation.Failure)

ValidateProject checks every configured graph — or the one graph id selects — with the same document, rule, and reference checks the path-named validate applies, plus the one check only the configuration can ask for: that a declared rows path stays inside the project. Zero declared graphs is "skipped": nothing was checked, and unlike the test walk this is not a failure — a project without graphs has nothing here to be wrong about.

Types

type Document

type Document struct {
	FormatVersion string          `json:"formatVersion"`
	ID            string          `json:"id"`
	Version       string          `json:"version"`
	Description   string          `json:"description,omitempty"`
	Nodes         map[string]Node `json:"nodes"`
	Edges         []Edge          `json:"edges"`
	Result        string          `json:"result"`
	// Digest names the exact bytes this document was decoded from. It is not a
	// member of the document and is never decoded from one: Load sets it from
	// what it was handed, which is the only place the bytes and the decoded
	// shape are known to be the same thing. A graph's id and version are as
	// mutable as a pack's, so an audit record names both and the digest too.
	Digest string `json:"-"`
}

Document is one graph document, already past the carrier, the version gate, and the embedded schema. Its references are not yet checked: Structural and Semantic are what hold them to the graph's own rules and to the project.

func Load

func Load(data []byte, name string) (Document, *evaluation.Failure)

Load reads one graph document from bytes: the strict carrier decode every other surface uses (duplicate member names, depth, and size limits all enforced), the formatVersion gate before the schema so a document written for a later shape is told exactly that, then the embedded schema. name appears in messages and is the caller's label for the input — a path, or "-".

The returned failure is operational and carries no §8.4 class: a graph is not an input the specification defines, so no evaluation error class could be honest about it.

func (Document) Order

func (d Document) Order() ([]string, []string)

Order returns the deterministic evaluation order and, when the edges do not form a DAG, the sorted node ids that are actually on a cycle. The order is Kahn's algorithm with the smallest node id chosen first at every step, so one document yields one order in every process on every platform. Cycle membership is decided by strongly connected components rather than by Kahn's leftover set, because the leftover set also holds nodes that are merely blocked behind a cycle, and naming one of those would send an author hunting a cycle it is not in. Edges whose endpoints the document does not declare, and self-edges, are ignored here: Structural reports each of those as its own defect, and this order exists for the nodes the document actually relates.

func (Document) Semantic

func (d Document) Semantic(loaded *project.Project) []result.Diagnostic

Semantic is Structural plus the reference checks that read pack documents through the project's own handle: every evidence feed names a requirement its target pack declares, and a target pack that cannot be read or decoded is reported on its node. These checks belong to the validate report and not to the evaluation gate — when a node runs, the engine reads its pack itself and a defect there gets its §8.4 class instead of an unclassed refusal here.

func (Document) Structural

func (d Document) Structural(loaded *project.Project) []result.Diagnostic

Structural holds one loaded document to the graph's own rules — every check that reads nothing but the document and the configuration's declared ids: every edge endpoint and the result name declared nodes, no edge feeds a node from itself, no two edges feed the same fact pointer or the same evidence requirement of one node, no fact pointer sits inside another edge's fact pointer for the same node, no fact pointer is deeper than the injection depth this runtime admits, every node's pack is a decision id the configuration declares, and the edges form a DAG.

This is the gate evaluation and planning run behind, deliberately without the checks that read pack documents: at evaluation time the engine reads each pack itself and assigns the §8.4 class its defects deserve, and a pre-read here would flatten a malformed pack into an unclassed graph refusal. Semantic adds those reference checks for the validate report.

Every finding is reported rather than the first, in a deterministic order — nodes sorted by id, then edges by position, then the result, then the cycle — so two runs over one document disagree with nobody.

type Edge

type Edge struct {
	From        string        `json:"from"`
	To          string        `json:"to"`
	Fact        string        `json:"fact,omitempty"`
	Evidence    *EvidenceFeed `json:"evidence,omitempty"`
	Description string        `json:"description,omitempty"`
}

Edge feeds one upstream node's disposition to one downstream node, as a fact, as evidence availability, or both. The schema requires at least one of the two devices: an edge that feeds nothing declares a dependency nobody can observe, which is an authoring mistake and not an option.

type EvidenceFeed

type EvidenceFeed struct {
	ID           string `json:"id"`
	OnUnresolved string `json:"onUnresolved,omitempty"`
}

EvidenceFeed is one edge's outcome-as-evidence device: the downstream requirement id, and the tri-state an upstream disposition that is not an outcome contributes.

func (EvidenceFeed) State

func (f EvidenceFeed) State(upstreamKind string) string

State returns the tri-state this feed contributes for one upstream disposition kind: "present" for an outcome, and the declared onUnresolved — "unknown" unless the edge says "absent" — for anything else.

type Node

type Node struct {
	Pack        string `json:"pack"`
	Description string `json:"description,omitempty"`
}

Node is one graph node: a reference to one pack by the decision id the project's jpack.json declares. It carries no identity of its own beyond its key, because identity lives in the pack document (ADR-0012).

type Options

type Options struct {
	Command             string
	SupportedExtensions []string

	// Audit is the trail this run's records are appended to, or nil for a run
	// that records nothing. It is a field rather than something derived from
	// the project, because the project is live on both graph paths and only
	// one of them is a decision: an ordinary experimental graph evaluate sets
	// it, and experimental graph test and a declared graph rehearsal
	// (ADR-0041) do not, so a matrix run or a rehearsal over the same graph,
	// with the same configuration, records nothing.
	Audit *audit.Writer

	// Cites are the receipts the caller said this run relied on (ADR-0033),
	// already held to their shape by the surface; every record the run
	// leaves carries them, node and composite alike, since they belong to
	// the run's inputs and every record of a run shares its id. Nil when
	// none were given.
	Cites []audit.Citation

	// LawCheck is a caller-supplied check on each node's pack, applied to the
	// exact bytes this run is about to evaluate, at the one point they are in
	// hand. It is a function rather than anything this package understands:
	// what it enforces is the reviewed-set lock (ADR-0019), which is the
	// caller's concern, and a check that re-read the file to answer would be
	// answering about a different read than the one that decides. nil is a run
	// that checks nothing — experimental graph test leaves it so, for the same
	// reason it records nothing.
	LawCheck func(decisionID string, document []byte) *evaluation.Failure

	// ReportBudget stops a run whose RETAINED report components pass the budget:
	// every row's report as it is judged, every graph's coverage block as it is
	// derived, and every entry's remaining envelope once that entry is
	// finished. 0 is no bound.
	//
	// It is a MITIGATION, not a guarantee, and its scope is an invariant rather
	// than a list of exceptions -- five review rounds were spent enumerating
	// those and being told the list was short by one:
	//
	//	ReportBudget constrains only the bytes RETAINED IN THE REPORT as it is
	//	composed. Everything a run holds in order to PRODUCE that report is
	//	outside it entirely.
	//
	// The second category is bounded by MaxRowsBytes and the carrier limits, and
	// is not small: the rows document is fully decoded before the first row is
	// judged and stays live, the result-row slice is preallocated for the whole
	// declared matrix, and coverage retains a witness per row. Examples, not an
	// exhaustive list.
	//
	// Within what it does constrain: components are metered after composition,
	// so a run overshoots by whichever one crosses the line, and the suite's
	// outer envelope is never charged.
	//
	// What it does buy, and what is tested: the remaining rows of a large matrix
	// are never EVALUATED once the budget is passed. That is work avoided, not
	// memory reclaimed. A caller's check on the finished report backstops the
	// response size and nothing else; it cannot backstop peak working memory,
	// because by then the memory has been spent.
	ReportBudget int

	// RequireComparableFacts is the project's requirement that no node evaluate
	// a fact no comparison reading it can match (ADR-0046), checked against each
	// node's assembled facts document. experimental graph evaluate sets it from
	// the configuration, declared rehearsal or not; experimental graph test
	// leaves it off, because a matrix row may probe such a fact on purpose.
	RequireComparableFacts bool

	// IncludeTraces attaches each compared node's evaluation trace — the
	// ADR-0027 member the walk already holds — to that node's comparison in
	// the matrix report (ADR-0031). Off is today's payload, byte for byte.
	// The traces ride inside each row's marshaled report, so ReportBudget
	// charges them with no new machinery: a matrix that fits without traces
	// may be refused with them, and that is the budget doing its job.
	IncludeTraces bool
	// contains filtered or unexported fields
}

Options carries one graph evaluation's caller choices, applied identically to every node: the reporting surface's command name, and the consumer's extension capability set. There is no per-node option, deliberately — a graph whose nodes ran under different capability sets would be a composite nobody could reason about.

type RowCase added in v0.9.0

type RowCase struct {
	ID                         string                     `json:"id"`
	Inputs                     json.RawMessage            `json:"inputs"`
	ExpectedDisposition        json.RawMessage            `json:"expectedDisposition,omitempty"`
	ExpectedNodes              map[string]json.RawMessage `json:"expectedNodes,omitempty"`
	ExpectedHandoffTarget      json.RawMessage            `json:"expectedHandoffTarget,omitempty"`
	ExpectedNodeHandoffTargets map[string]json.RawMessage `json:"expectedNodeHandoffTargets,omitempty"`
	ExpectedErrorClass         string                     `json:"expectedErrorClass,omitempty"`
	ExpectedErrorPhase         string                     `json:"expectedErrorPhase,omitempty"`
}

RowCase is one graph matrix row: one inputs document for the whole graph, and exactly one expectation — the composite headline disposition, or the §8.4 class (and optionally phase) of a refused run. ExpectedNodes names the per-node dispositions the row also pins; a node it does not name is unchecked, which is the row author's stated choice rather than a default nobody chose.

A row can assert only the two stable vocabularies: canonical §8.3 dispositions, and §8.4 classes with their phases. A refusal that carries no class — a graph-layer or invocation refusal, such as a feed colliding with the row's own inputs — is deliberately unassertable and mismatches every expectation: this runtime's JPS-* codes are provisional detail (§8.4 fixes the class, not the detail), and a row that pinned one would break on wording this runtime is free to change. ExpectedHandoffTarget asserts the composite's reported handoff target — the result node's own, which is the configured target exactly when the disposition requested a handoff and null otherwise — and ExpectedNodeHandoffTargets asserts named nodes' (ADR-0032, graphMatrixVersion "2"): each value is null or a {kind, name} object, exactly a pack row's expectedHandoffTarget. A node whose target a row asserts must also appear in ExpectedNodes: a target is asserted beside the node's disposition, as a pack row asserts it beside its own.

type Rows added in v0.9.0

type Rows struct {
	GraphMatrixVersion string    `json:"graphMatrixVersion,omitempty"`
	Cases              []RowCase `json:"cases"`
}

Rows is one graph's matrix: the rows a project wrote about its own graph.

func LoadRows added in v0.9.0

func LoadRows(data []byte, name string) (Rows, *evaluation.Failure)

LoadRows reads and checks one graph matrix document. "Well-formed" here is the carrier and the shape, exactly as a pack matrix's loader draws the line: strict JSON with no duplicate member names, a closed member set, a declared version this runtime knows, at least one row, unique row ids, an inputs document per row, and exactly one expectation per row. Whether an expectation holds is what graph test answers.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL