Repository navigation
v0.14.0
0.14.0 — 2026-09-06
13 commits — 5 features, 4 fixes, 1 performance change, 1 documentation change, and
2 merges. Counted at 94602f46, the merge that closed sprint 355 into develop and the
commit release/0.14.0 was cut from; the release-preparation commits that carry this entry
are necessarily not in their own count. One sprint delivered this window — 355,
GoGraph execution reporting — and all 11 of its tasks closed. 158 files changed,
22 858 insertions, 661 deletions (git diff --shortstat v0.13.0..94602f46).
What that insertion count is, because 22 858 lines is not 22 858 lines of engine:
9 841 insertions across 38 _test.go files, 5 797 across 79 raw measurement files under
docs/benchmarks/*-raw/, 4 470 across 33 non-test .go files, 2 708 across 7 documentation
files, and 42 in one example README. Bucketed by path from
git diff --numstat v0.13.0..94602f46.
Three things define the release.
Execution reporting stopped claiming what it had not measured. The DbHits column could
not distinguish "counted, and it is zero" from "never counted at all" — both printed 0. It
is now tri-state: a counted figure, a counted zero, and ? for a figure nobody counted, with
an incomplete total rendering x + ? rather than silently summing across the gaps. Four
operator families that read storage and reported nothing now count it, and each is proved by
an observed arm in which the db-hits figure differs from the operator's own row count, so
the figure cannot have been derived from it. A new Removed column reports how many candidate
rows a predicate threw away.
The planner consumes its own statistics for the first time. The property statistics have
been maintained since #2097 and, until this release, were read by nothing but the EXPLAIN
renderer. They now decide the drive order of the disjoint-component join reorder, under the
pre-existing trustworthiness veto and evaluated over a certified error interval rather than at
the point estimate. Est.Rows renders beside the measured Rows with its provenance
marked, so an estimate and the measurement that tested it can be read from one table over one
plan; and how wrong the estimates turn out to be is itself observable, as a q-error metric.
The sprint's own gate found two defects, and both were fixed rather than deferred. A
declined MVCC label count was read as an empty label, which demoted the estimate, made the
planner benefit non-deterministic, and let EXPLAIN render a plan the engine did not run
(#2771). And a labelled count cloned a roaring bitmap and returned it uncorrected, purely
to have GetCardinality() called on it (#2773).
go.mod and go.sum are byte-identical to v0.13.0. Same pinned toolchain, same
dependency set at the same versions (git diff v0.13.0..94602f46 -- go.mod go.sum is empty).
There is nothing to report under a dependency heading and nothing is invented to fill one.
No change is marked breaking — no commit in the window carries a BREAKING CHANGE footer
or a ! subject — but the rendered plan output changed shape, and any consumer that parses
plan text or reads the Bolt plan map may need updating. The full list is under
Behaviour a caller can observe — the compatibility surface.
The openCypher TCK gate is unchanged at 3 897/3 897. const tckExecutionBaseline = 3897
in cypher/tck/runner_test.go is untouched and no .feature file changed in this window
(git diff --name-only v0.13.0..94602f46 -- 'cypher/tck/features/**' is empty over 220
feature files), so the scenario population is the one v0.13.0 was measured against.
Read release-notes/v0.14.0.md before upgrading. There are no
data-migration steps, but the observable plan output changed and the release's open gaps are
listed there.
Added
Cypher — the estimate reaches the physical plan
cypher/exec.PlanEstimate,exec.EstimateSourcewithEstimateAbsent,EstimateExact,
EstimateStatsandEstimateHeuristic, and the methodsEstimateSource.Known,
EstimateSource.ExactandEstimateSource.String(#2765) — the planner's cardinality
estimate and its provenance, as carried on a physical plan node.cypher/exec.PlanEstimates,exec.PlanTreeWithEstimates(#2765) — the operator-to-
estimate mapping and the plan-tree builder that applies it. Attribution is a single rule at
buildOperator, the one point holding both a logical node and the operator it lowered to:
the deepest logical node whose lowering returns an operator owns the estimate, and it is
never overwritten.cypher/exec.EstRowsCell,exec.EstRowsAnnotationandconst EstRowsUnknown = "-"
(#2765) — theEst.Rowscell renderer, the tree annotation, and the glyph for an operator
with no derivable estimate. Four plan shapes have no logical node at all — the range-seek
leaf, the min-label re-anchored scan, the parallel leaves and the columnar fusion chains —
and render-rather than borrowing a neighbour's number.cypher/exec.PlanNode.Est(#2765), the field the renderers read.
Cypher — db-hits gains an honest unknown
cypher/exec.PlanNode.DbHitsKnown,exec.DbHitsCell,exec.DbHitsTotalCelland
const DbHitsUnknown = "?"(#2760) — the explicit unknown state and its renderers. A
separate boolean was preferred to Neo4j'sOperatorProfile.NO_DATA = -1sentinel so that no
magic number can leak into arithmetic.cypher/explain.ProfileReport.TotalDbHitsUncertain(#2760), which is what makes the
Totalrow renderx + ?instead of a plain number when any contributing cell is unknown.cypher/exec.PlanRows(#2760), which reports a plan's total rows together with whether
that total is complete.
Cypher — rows removed by a filter
cypher/exec.PlanNode.RowsRemovedByFilter,PlanNode.RowsRemovedByFilterKnownand
exec.RowsRemovedCell(#2764) — the rejected-row figure and its renderer.Filter,
ColumnarFilterandExpandreport it, through arowsRemovedCountermarker with a
compile-time census.
Cypher — the estimate error is observable
cypher.Engine.StatsMisestimatedPairs(#2767) — how many(label, property)pairs a
PROFILEd plan has observed to be misestimated past the margin. It returns a count, not
the pairs themselves. It counts only what aPROFILEhas actually looked at, so a zero means
"nothing observed to be wrong", never "everything is right"; and a successful
Engine.RefreshStatisticsclears it, so a non-zero reading after a refresh is the more
interesting fact that the fresh statistics are still predicting badly. Safe for concurrent
use.
graph/lpg
graph/lpg.Graph.LabelCountAsOf(#2773) — a labelled cardinality as of a snapshot that
answers directly, never declines, and allocates nothing when nothing needs correcting.
Observability
- Seven new
cypher.stats.*metric series —cypher.stats.qerrorand
cypher.stats.qerror.high(#2767),cypher.stats.label_count.declined(#2771), and the four
fallback-reason partitionscypher.stats.lookup.fallback.empty_label,.no_count,
.no_statisticand.stale(#2771).cypher.stats.qerrorobserves
max(est, act) / min(est, act)per operator of aPROFILEd plan, and
cypher.stats.qerror.highcounts the samples past the 3× margin. All eleven
cypher.stats.*series are documented in docs/metrics.md; a set
comparison of the series names incypher/stats_metrics.goagainst that file is empty in
both directions.
Changed
Behaviour a caller can observe — the compatibility surface
These are the release's compatibility surface. Nothing here is marked breaking, but each is
observable, and the first four change bytes a consumer may be parsing.
Engine.Explaingained an(est. rows=N exact)annotation on the physical plan tree
(#2765). Where an operator has no derivable estimate, no annotation is added.Engine.Profile,Engine.ProfileTableandResult.Profile()gainedEst.Rowsand
Removedcolumns (#2764, #2765), and an uncounted db-hits cell now renders?rather
than0(#2760), with an incomplete total renderingx + ?. Each column is dropped
entirely when no operator in the plan carries that figure, so a plan with nothing to say in
a column does not grow one.exec.PlanNodegained exported fields —Est,DbHitsKnown,RowsRemovedByFilter
andRowsRemovedByFilterKnown(#2760, #2764, #2765).explain.OperatorStatsgained the
same four, andexplain.ProfileReportgainedTotalDbHitsUncertain. Code that
constructs any of these with an unkeyed composite literal will no longer compile; keyed
literals are unaffected.- Bolt
planandprofilemetadata gainedEstimatedRows,EstimatedRowsSourceand
RowsRemovedByFilter, and now OMITSdbHitsfor an uncounted operator rather than
sending0(#2760, #2764, #2765).EstimatedRowsis where Neo4j puts it, a plan argument;
EstimatedRowsSourcehas no Neo4j counterpart, because Neo4j publishes the estimate
unqualified.RowsRemovedByFilteris written for anEXPLAIN-shaped node too, unlike
dbHits. - Anything parsing plan text or the Bolt plan map may need updating. A reader that assumed
a db-hits cell is always an integer must now handle?andx + ?; a Bolt client that
assumeddbHitsis always present must now handle its absence, which is the wire-level half
of #2760. Expand's db-hits figure changed definition, not merely scale (#2761). It reported the
edges it emitted and now reports the adjacency slots it walked. On a source with 99:LIKES
and 1:KNOWSout-edge,-[:KNOWS]->previously reporteddbhits=1for the same 100-slot
walk that-->reported as100; both now report100while still emitting 1 row and 100
rows respectively. Neo4j charges the hit regardless of the predicate outcome, so the old
figure was an incompatible definition rather than a different unit. The two binary searches
stay outside the figure and now say so.- The three morsel-parallel leaves report a non-zero db-hits figure (#2762).
ParallelScanProject,ParallelAggregateScanandParallelCountScanreported0while
their workers walked the whole label; each now reports the node references its workers
consumed, equal to what the serial control arm reports for the same graph. The charge is one
atomic add per morsel — one per 1 024 node references — not per row: #2649 measured
per-row shared atomics on this same operator at 18.9 % of flat CPU with scaling stopping past
four workers. TheirPlanDetailno longer says db-hits are not counted. ShortestPathandAllShortestPathsreport a non-zero db-hits figure (#2763), the
relationship slots their searches read, where both previously reported0.Filter,ColumnarFilterandExpandreport aRemovedfigure (#2764). An operator
with no rejection mechanism omits the cell entirely; an operator that has one and rejected
nothing printsremoved=0, because a filter that rejected nothing is exactly the finding a
reader of a slow plan wants. This differs from PostgreSQL, which suppresses the zero in text
mode, and the divergence is deliberate.- The plan a query runs can now differ when statistics are present (#2766). The
disjoint-component join reorder consumes the property statistics, so the same query over the
same graph can plan a different shape according to whetherRefreshStatisticshas run.
The reorder remains result-identical — its order-safety gate is untouched, and a
differential suite holds the result multiset — but the plan is not. The gate is a minimax
over two cost rules, becauseApplyre-Inits the inner arm per outer row while a
Selectionmay be rewritten into a seek that collapses drain onto rows; the planner cannot
see which realisation it will get, so a swap must win under both.estStatsis consumed over
its certified error interval under a 3× margin, and a staleness screen was added for the
equality path, which the original design had not anticipated.
Documentation corrected against the code
- Two documents claimed the statistics reach no plan decision, which #2766 made false
(this release).docs/optimisations.mddescribed the estimate providers as "display-only —
no execution or plan-choice change", anddocs/statistics-design.mdsaid "the reordering
peepholes (P3) are unaffected — they require estExact and never consume statistics". Both are
corrected againstcypher/join_reorder_plan.go(reorderFilteredRows) and
cypher/estimate.go(trustworthy, which admitsestStats). The result-identity claim in
docs/optimisations.mdwas checked and kept: only the display-only half was wrong. docs/reordering-design.mdanddocs/count-store-design.mddescribed the reorder as
inert (this release). The first said "with no consumer today the whole thing is inert" and
sequencedEstStatsas a later arrival; the second said the P3 reorder is "provably inert
until real exact counts are online". Each now carries a dated marker recording what changed
and what still holds — there is still no DPccp enumerator, and the single-edge anchor swap is
still exact-count-only.docs/cypher.mdpointed at the superseded audit (this release). Its only pointer to the
figure classification namedexplain-profile-honesty-audit-2026-09-03.md, whose
classifications this release inverted; it now names the 2026-09-05 re-run.README.mdlistedcypher/planas a module package and describedPROFILEas three
per-operator figures (this release).cypher/planwas deleted in80954706(2026-06-22)
and no.gofile imports it;PROFILEnow reports five figures. Both package lists are
corrected.- Four in-code claims that the statistics are inert, and a
PlanNodegodoc that contradicted
a compile-time census in its own package, were corrected atbd9fc194(#2768) rather than
backlogged. The godoc namedVarLengthExpandas the only counting operator and
ShortestPath,AllShortestPathsand the parallel leaves as uncounted, while
cypher/exec/profile.golisted ninestorageAccessCounterimplementations including all
four. It now points at the census instead of restating it.
Fixed
- A declined MVCC label count was read as an empty label (#2771).
ResolveLabelCountis
exact-or-nothing and declines while MVCC history is live;
statsRangeEstimateInnerdiscarded theokflag, and itsn <= 0guard then demoted the
estimate toestFallback, so the histogram path went silently inert and the veto kept the
written order. The visible symptom wasEXPLAINrendering a reorder the run did not take.
The premise the task was opened on was corrected by the evidence: no concurrent writer is
involved. The records are the seed graph's own birth records, reclaimed by an asynchronous
vacuum that lags under whole-module parallel load — measured at 303 unreclaimed on an idle
graph with no writer. On an idle host the vacuum always wins, which is why the defect was
invisible outsidego test -raceover everything.labelPopulation{n, known}now carries the
distinction in one place, resolving the exact count first and the corrected label bitmap on a
decline; the query path pays nothing, takingNfrom the drain it already holds. The
fallback counter is now partitioned by four reasons and the partition is asserted. - A labelled count cloned a roaring bitmap it never corrected (#2773). The count path gated
on a global unreclaimed-record flag while the bitmap path gated per-label, so when the
global gate was up and the per-label gate clear,labelBitmapAsOfFilteredcloned the bitmap
and returned it uncorrected — the clone existed solely to haveGetCardinality()called
on it. That no correction ran was proved by the allocation figure being flat from 1 to
1 000 live records, where a real correction grows with the count. An idle graph reaches the
state alone, becauseAddNodewrites a life record whose churn set is the labels held at
that instant — none — so an unreclaimed birth raises the global flag and no per-label counter
at all.LabelCountAsOfnow answers directly. Two refutations are recorded: the task's own
brief located the cost inlabelCardinalityEstimate, which is never on this path, and #2753
had attributed the same signature to sibling goroutines and external load.
Performance
This release is not measurably faster, and no claim is made that it is. The
head-to-head against v0.13.0 was run in one dimension only — allocations — because
the host carried a load average of 2.86–19.73 throughout, substantially caused by Spotlight
indexing the measurement's own worktrees. A timing comparison remains UNMEASURED. That
is not the same as "no difference".
Method: two clean worktrees, -trimpath, plain build (no -race — allocation counts
are not build-invariant), three arms interleaved with the order rotated each round, and the
noise floor measured first. Full record:
docs/benchmarks/v0130-vs-v0140-2026-09-06.md.
Noise floor. Two independent builds of the same v0.14.0 tree are byte-identical, so
B vs B2 is the same binary against itself: 169 of 171 benchmarks agree within 0.5 %.
The two that do not are plan-cache shapes, where this host manufactures an 11.34 %
allocation difference from nothing. Separately, the store/wal test binary is
byte-identical between the two releases, and all 7 of its benchmarks report identical
allocations — the method returns "no difference" where none is possible.
Result — the added counting work costs nothing. Sprint 355 added slot bracketing to
Expand, a per-morsel atomic to the three parallel leaves, per-run accumulation to the
shortest-path operators, reject counting to the filters, and an estimate on the physical
plan. Across 171 benchmarks, 159 are unchanged; Expand is identical in all 14 arms and
the parallel leaves in all 17. Nothing regressed.
Result — eight benchmarks changed, every one a reduction:
| Benchmark | v0.13.0 |
v0.14.0 |
Δ allocs/op |
|---|---|---|---|
| join reorder, skewed shape, reorder on | 5 925 706 | 59 968 | −98.99 % |
| join reorder, live-history shape (quiet / live) | 564 429 / 564 428 | 25 031 / 25 055 | −95.6 % |
ReadAfterWrite_Session / _Sessionless |
53 | 41 | −22.64 % |
ShortestPath_Layered / _LayeredTyped |
126 | 108 | −14.29 % |
The reorder gain is conditional and must not be quoted as a general speed-up. It needs
RefreshStatistics to have run and a qualifying query shape — a disjoint two-component
match where a label count cannot identify the cheaper driver but a (label, property)
estimate can. Attribution is direct rather than inferred: the physical plan changes (at
v0.13.0 the 100-row :B component drives and re-scans the 20 000-node :A label once per
row; at v0.14.0 the filtered :A component, estimated at 3 rows, drives), the results are
byte-identical across both trees, and the reorder-disabled control is unchanged.
Four benchmarks are unreliable and no claim is made for them — the plan-cache shapes,
whose own round-to-round spread reaches 19 %.
This release ships no release-level performance claim. Sprint 355 adds counting work to
several paths that previously did none: slot bracketing in Expand, one atomic add per morsel
in the three parallel leaves, per-run slot accumulation in the two shortest-path operators, and
rejected-row counting at eight sites across the row and columnar filter paths. Each was
measured at its own task, against its own baseline, with a noise floor measured first, and
showed no statistically significant regression there — allocations identical across all 18
BenchmarkExpand* arms (#2761), zero allocations added across all 7 shortest-path arms
(#2763), allocations identical across all 11 filter arms (#2764). None of those is a
release-level measurement, and the aggregate cost of the added counting has not been
measured. The one figure this release can state structurally is that the work was added; whether
it is visible at release level is an open question until the head-to-head runs.
The single per-task speed-up worth recording, kept explicitly scoped to its own benchmark and
not offered as a release-level gain:
- The join reorder that #2766 wired to the statistics turned
BenchmarkJoinReorderStatsSkewed— 20 000:Aof which 3 match, × 100:B— from
261.638 m to 2.658 m, −98.98 % (p = 0.000, n = 12), with B/op −98.83 % and allocs/op
−98.99 %. Conditions, because the figure is meaningless without them: Apple M4, 10 cores,
go1.27.1 darwin/arm64, non-race build,-benchtime=1s -count=1over 12 interleaved
rounds, compared withbenchstat. The noise floor was measured first, same code against
itself: 2.658 m ± 1 % versus 2.663 m ± 1 %,~ (p = 0.887, n = 12)— no significant
same-versus-same difference. The host was not idle (load average 1.87–2.72 on 10 cores,
with a persistent rootosascriptat ~15 % CPU), so the absolute ns/op figures are not
quiet-host numbers and must not be quoted as such; the noise floor was measured under those
same conditions, which is why the delta is not attributable to host noise. This is one
synthetic skewed shape, chosen to exhibit the effect, and it says nothing about a workload
whose arms a label count can already separate. - #2773's fix removed 12 allocations from a bare labelled
count()on a plan-cache hit,
32 → 20 allocations per operation on the arm where unreclaimed records are live, with the
quiet arm unchanged as the control. Build mode matters for this figure and is stated:
allocation counts here were read in both plain and-racebuilds (19.0 either way in
isolation;-racechanges B/op, 2 064 versus 2 056, not the count). The task also recorded a
wall-clock figure for the same arm, which is not reproduced here because no noise floor or
host load was recorded alongside it.
The measured head-to-head against
v0.13.0is not in this release entry. It is reserved
under Measured gains against v0.13.0 in
release-notes/v0.14.0.md, to be filled from a run on a quiet
host. Until it lands, this release makes no claim to be faster thanv0.13.0.
Compliance
- openCypher TCK, execution level: 3 897/3 897, unchanged.
const tckExecutionBaseline = 3897is untouched, and no.featurefile changed in this window across 220 feature files,
so the population is the onev0.13.0was measured against. The closing audit's verbose run
at83ba8d8breports3897 scenarios, 3897 passed, 0 failed, 0 undefined, 0 inconclusive (baseline=3897); inmake cithe gate runs non-verbose and reports
ok github.com/FlavioCFOliveira/GoGraph/cypher/tck. - ACID: unchanged. No task in this window changed a durability, atomicity, isolation or
consistency path. #2771 and #2773 both touch MVCC read paths; #2773 added a dedicated
seam so thatLabelCountAsOf's ordering claim — that the gate is sampled after the
cardinality — is gated by a test rather than asserted by a godoc. make ciis green on the release tree, on the seventh attempt.MAKE_EXIT=0, read
from inside the log;go test -race -count=1 -timeout=30m ./...with noDATA RACEand no
FAIL;golangci-lint run ./...reporting0 issues.; and
cover_gate: OK (aggregate 88.6% >= 85.0%, all packages >= 75.0%). The six preceding runs
exited 2 — five attest-shortand one atcover-gate— and the two defects they exposed
are #2771 and #2773 above, both of which were in the module rather than in the instruments.- The soak and nightly layers were NOT run for this release, and nothing here claims
otherwise. Under
Acceptance gates the soak layer is a periodic reliability exercise and not a
release gate, but it has now gone unrun for a seventh consecutive cycle. This release also
carries no production certification of its own; the most recent was taken at the
v0.11.0commit.
Notes
- What this release does NOT do. The closing audit
(docs/explain-profile-honesty-audit-2026-09-05.md)
verified eleven open gaps as still holding at the closing tree. The ones a caller may notice:
exec.OptionalExpandis unreachable from any query — 0 of 11OPTIONAL MATCHshapes
plan it, every one lowering toOptionalApply(… Expand …)— so it is unwired rather than
dead (#2769);exec.RenderPlanhas no caller and renders a plan missing the columns this
release added (#2770); an MCV equality estimate is taggedestExactwith no staleness
check at all, demonstrated by a plan in which one estimate is live and one is stale and both
are taggedexact(#2772);Expand's estimate ignores an interveningSelection, biasing
its q-error high; columnar fusion chains carry no estimate, so the most-wrong estimate is
the one q-error cannot see — a stale MCV estimate wrong by 400× produces no q-error
observation at all, because the shape that exposes it fuses into a columnar chain, and that
shape is the commonest read form in the language; aLIMIT 0PROFILEcaptures the parallel
tier with workers still in flight, yielding a known zero that is not a fact about the
workload; and the count-store leaves render?where a known0might be the better
answer, which this sprint did not settle. Three ordering claims ingraph/lpg
(LabelCountExact,LabelCountBound,LabelsCountExact) are real but ungated, and a
same-label bitmap clone still costs 38 allocations. - What the rendering still does not distinguish. The db-hits column now separates counted
from not counted. It does not separate counted from inferred: aNodeByLabelScan
reporting2000and aParallelScanProjectreporting2000render identically, and the
first is its own row count while the second is a real walk. That is the surviving residue of
the audit's divergence D1. - Audit verdicts, for the record. Of the eleven divergences the 2026-09-03 audit raised
against Neo4j, Memgraph and PostgreSQL: 4 closed (D2 rows-removed, D3 estimate beside
actual, D4 uncounted-prints-zero, D9 Bolt estimates), 2 narrowed (D1 derived db-hits,
D6 parallel tier), 5 unchanged (D5 per-invocationloops, D7 memory figure, D8 Bolt
identifiers, D10 eager warning, D11EXPLAINon a writing statement). Three of the five
unchanged were judged not to matter and that judgement survived re-reading; the two that
matter and did not move are D8 and D11, both explicitly out of scope for this sprint. - The closing audit is independent of the code it audits. It was produced by an external Go
module driving GoGraph's public API through areplacedirective — deliberately not the
sprint's own test suite, because a gate written by the task under audit cannot be the evidence
that the task's claim is true. 31 query shapes over 8 graph shapes, 110 operator instances.
It contains no throughput or latency figure at all, because the host was not quiet
(load average 1.42–2.52); every figure in it is a count, a glyph or a structural comparison. - Release packaging was fixed in this release (#2758).
.goreleaser.yamllisted
docs/**/*, a glob matching only paths carrying at least one directory component, so the
v0.13.0tarball shipped 0 of the 112 top-leveldocs/*.mdfiles and instead carried
254 nested files — 116 Markdown documents plus 138 raw measurement files (112.txt,
25.log, 1.sh), every one of them underdocs/benchmarks/. Counted from
git ls-tree -r --name-only v0.13.0 -- docs/. The file list now shipsdocs/*.mdand
docs/**/*.md, and both patterns are load-bearing because**never matches zero
segments. Verified the only way that counts, by building a snapshot artefact and
extracting it: 113 of 113 top-level documents present, 233 of 233 Markdown
documents in total, 0.txt/.log/.meta/.shfiles, identically across all four
OS/arch archives. Reading the config is what let the defect stand for a release, so the
config now records that the verification must be an extraction. Thev0.13.0entry's claim
that the protection-regime text "reached consumers as supply-chain assurance" was false as a
consequence, and is corrected in place above (#2759). - Pre-1.0 stability. This is a
0.y.zrelease. The public Go API may change without a
major-version bump until1.0.0; pin the exact version you depend on. - Module path. The Go module path is
github.com/FlavioCFOliveira/GoGraphwith no/vN
suffix, which is Semantic-Import-Versioning-correct for a0.xline. - Examples are not part of the module.
examples/is an exercise harness; the module
neither imports nor depends on it. One example README changed in this window
(examples/31_metrics_observability/README.md).
What was delivered — from the project roadmap
2 sprint(s) closed in this release window (355, 356), containing 13 tracked tasks. Every task in a closed sprint is COMPLETED: the roadmap tool refuses to close a sprint that still holds active work.
- Sprint 355 — GoGraph execution reporting
- Sprint 356 — GoGraph v0.14.0 release
Source: the project roadmap (rmp), not the commit log. Sprints are attributed to a release when their close date falls after the previous tag and on or before this one. Task counts are the sprints' own membership counts, read back from the tool.
Further reading
- Narrative release notes:
release-notes/v0.14.0.md - Measured benchmark report:
docs/benchmarks/v0.14.0.md - Changelog file at this tag:
CHANGELOG.md
Commits in this release
- d038873 Merge release/0.13.0 into develop: back-merge the v0.13.0 release
- 7c59a02 Merge release/0.14.0 into main: GoGraph v0.14.0
- 94602f4 Merge sprint 355 into develop: GoGraph execution reporting
- bd9fc19 docs(audit): re-establish the EXPLAIN and PROFILE honesty audit by measurement
- 8fb1a8c docs(benchmarks): the v0.14.0 per-release campaign, and its own noise floor
- f8a3ac0 docs(release): prepare v0.14.0
- 8bb4b01 feat(planner): the join reorder consumes the property statistics
- 1eac6b8 feat(profile): PROFILE puts the planner estimate beside the measurement
- 44804e5 feat(profile): PROFILE reports the rows a filter rejected
- cb1304c feat(profile): a db-hits figure nobody counted stops printing as zero
- 83ba8d8 feat(stats): observe how wrong the planner estimates turn out to be
- e6f6384 fix(profile): Expand counts the adjacency slots it read, not the edges it emitted
- af9acf5 fix(profile): shortest path counts the relationship slots its search read
- f500e8b fix(profile): the morsel-parallel leaves count their workers' node walk
- 411aac1 fix(stats): a declined label count is no longer read as an empty label
- 796d884 perf(lpg): the labelled count stops cloning a bitmap it never corrects
Reproducibility: built from commit 7c59a02 with Go 1.26.