fix(repositories): stop the shared-pointer live edit in MemoryGraphRepository (A-06) - #100
Open
gustavobertoi wants to merge 5 commits into
Open
gustavobertoi wants to merge 5 commits into
gustavobertoi wants to merge 5 commits into
Conversation
…owledge packs
BACKLOG_V2 tasks are derived from a capability report about the code, not from
the code ("The report describes the code; it is not the code"), so an agent that
treats it as ground truth will confidently implement around bugs that do not
exist. This adds a pipeline that separates verification, planning, building and
review into distinct agents with explicit boundaries.
Agents under .agents/agents/ (per ADR-0009, .claude/agents symlinks into it):
premise-verifier, engine-researcher, implementation-planner, protocol-spec-author,
go-implementer, e2e-harness-engineer, code-reviewer.
Knowledge packs under .agents/skills/: backlog-task (the orchestrating pipeline,
with a routing table for every task id), plus durable-execution-internals,
crash-resume-testing, persistence-and-migrations, observability-tracing,
function-package-authoring, remote-node-protocol, capability-registry-and-agents.
Two boundaries are drawn explicitly because dispatch gets them wrong: process-death
tests belong to the harness engineer and involve no production Go, and the spec
author is the first call for B-01 or any wire-surface change.
Recorded as ADR-0034.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ed findings
One document per task at docs/backlog/<ID>.md — filenames are the bare id so the
pipeline can open a task by id, deliberately unlike docs/adr/'s NNNN-slug.md.
Each carries the backlog text verbatim, premise status distinguishing EXECUTED
from READ from UNVERIFIED, the code area, the agent route, "Done when" as
checkboxes, out-of-scope, the ADR/spec call, and a dated log.
45 documents: F x3, A x11, B x10, C x12, D x6, plus V-a/V-b/V-c. Tier B, Tier C
proposed work and all of Tier D are marked unmistakably as net-new — presenting a
proposal as shipped behaviour is the failure mode these files exist to prevent.
BACKLOG_V2.md gains a "Found during verification — unfiled" section with the three
bugs F-01 turned up, recorded rather than fixed per the backlog's own rule:
V-a agent tool calls bypass workflow_func.go, so they emit no node.execute span
and no duration metric — evidence against B-03's node/tool parity requirement
V-b resume appends a second subworkflow:started for one exec id; no DB constraint
catches it (sub_workflow_refs is UNIQUE on child_workflow_id only)
V-c Workflow.Next discards the FindNode error and derefs nil, crashing the actor
Ids and tiers are left unassigned on purpose — that is the maintainer's call.
BACKLOG.md is marked superseded rather than deleted; every id in it was renumbered.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
F-01's deliverable: each finding is a checked-in test that fails on the current
tree. All three REPRODUCED. No production code is modified — the point is to
demonstrate the defects against the tree as it stands.
.1 the node span context is discarded (workflow_func.go:96), so function code
cannot inject traceparent. Scope correction: `_ = nodeCtx` is a symptom, not
the defect — nothing downstream can accept a context (ExecuteFunction,
FunctionTransport.Execute and type Function are all context-free), so A-04
is a public pkg/ API change with design tension against ADR-0027.
.2 a restart with a pending system/subworkflow spawns a second child. All four
links verified. Scope correction: synchronous sub-workflows only — the async
branch journals step:completed at workflow_handler.go:936 and is immune.
.3 the memory graph repo returns the live pointer, so an Upsert rewrites a
running run's topology. Driver parity executed against a real Postgres 17:
the postgres driver is immune. This is also A-06's premise.
Each test asserts the DEFECT, not a fix: verified empirically by applying candidate
fixes and confirming each test flips green under every shape tried, and stays red
under a paper-over. An earlier revision asserted source text, a row count a correct
fix would not change, and clone-on-read specifically; all three were rewritten.
These stay on this branch. They cannot go green until A-04, A-02 and A-06 land, so
each merges as the first commit of the PR that fixes its defect rather than turning
main red for two sprints. See docs/backlog/F-01.md for the mapping.
Verified: build ok, lint 0 issues, go test -race reports no data race, and the only
reds in the tree are these six.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The 45 per-task documents now live as draft items on https://github.com/orgs/open-source-cloud/projects/2 — one per task, each carrying its full dossier as the item body (premise status distinguishing EXECUTED from READ from UNVERIFIED, code area with file:symbol, dependencies, agent route, done-when, ADR notes, dated log). Keeping both would have meant two sources of task state drifting apart on the first edit. BACKLOG_V2.md remains the narrative and the rationale; the board owns status, size, priority and assignment. Board fields were derived from the backlog only: Status Done = F-01 (verified and closed); Ready = Sprint 1 remainder (F-02, F-03, A-06, A-07); Backlog = everything else Size the backlog's declared S/M/L; Tier D left unset (no declared size) Priority P0 = Sprint 1 plus B-01 (gates all of Tier B, "before writing any Go") P1 = Sprint 2/3 remainder and the rest of Tier A; P2 = everything else The documents remain in this branch's history at commit 4225bb4 if a file-based copy is ever wanted again. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ve graph pointer BACKLOG_V2.md A-06 (F-01 finding 3). MemoryGraphRepository.FindByID returned the exact *workflow.Graph stored by Save, which internal/actors/workflow_handler.go hands to a triggered Workflow. A later GraphService.Upsert -> updateVersioned -> Graph.UpdateSchema for the same schema id then rewrote that in-flight run's topology in place: a forking node lost a branch mid-run, and Workflow.Next could nil-deref a node deleted from under it (internal/workflow/workflow.go:312-314). FindByID now returns a fresh Graph built from a cloned Schema, mirroring the existing FindByIDAndVersion idiom on the same file and the Postgres driver, which already rebuilds a Graph from the object store on every call. SetActiveVersion and Rollback already build fresh Graph values and are unaffected (A-05's territory). Turns the checked-in F-01.3 tests from failing to passing: - internal/services/f01_3_verification_test.go (3 unit tests) - tests/functional/f01_3_verification_test.go / _pg_test.go (memory + postgres contract) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
BACKLOG_V2.md A-06 · Stop the shared-pointer live edit (= F-01 finding 3).
MemoryGraphRepository.FindByID(internal/repositories/graph_memory.go) returned theexact
*workflow.Graphpointer stored bySave— the same objectinternal/actors/workflow_handler.gohands to a triggeredWorkflow. A laterGraphService.Upsert→updateVersioned→Graph.UpdateSchema(
internal/services/graph_service.go:244-284,internal/workflow/graph.go:122-133) for thesame schema id then rewrote that in-flight run's topology in place: a forking node could
lose a branch mid-run, and
Workflow.Nextcould nil-deref a node deleted out from under it(
internal/workflow/workflow.go:312-314).The Postgres driver was already immune —
postgres/graph.go:FindByIDrebuilds a freshGraphfrom the object store on every call.Fix
FindByIDnow returns a fresh*workflow.Graphbuilt from a clonedSchema():This mirrors the existing
FindByIDAndVersionidiom in the same file verbatim(
schema := sv.Schema.Clone(); workflow.NewGraph(&schema)), and needs no change toupdateVersioned,SetActiveVersion, orRollback— the latter two already build freshGraphvalues from a cloned schema and are out of scope here (that territory belongs toA-05). A grep of every
GraphRepository.FindByID/GraphService.FindByIDcall site(
cron_scheduler.go,event_trigger.go,workflow_sup.go,webhook_router.go,workflow_handler.go:168,workflow_schema.go,webhook.go, plus tests) confirmed noneassert pointer identity across calls, so the return-semantics change is safe.
GraphService.FindByIDalready handles the resulting!IsNodesMetadataPopulated()case byrepopulating (
internal/services/graph_service.go:69-74) — the same cost the Postgres driveralready pays on every call, not a regression.
Proof — the F-01.3 tests go from failing to passing
These were checked in on
mainas part of F-01 verification and are this task's stage-1evidence; not touched by this PR:
internal/services/f01_3_verification_test.go(3 unit tests)tests/functional/f01_3_verification_test.go(memory contract)tests/functional/f01_3_verification_pg_test.go(Postgres contract,//go:build functional)All 4 confirmed red on this branch before the fix, green after.
Test plan
make swagger && make lint && make build && make test— lint 0 issues, build clean,make test736/738 passing. The 2 remaining failures(
TestWorkflowFunc_HandleMessage_GivesNodeFunctionTheNodeSpanContext,TestWorkflowHandler_ResumeWithPendingSubWorkflow_KeepsExactlyOneDistinctChild) areF-01 findings 1 and 2 (A-04 / A-02 territory) — confirmed pre-existing on this branch
without this fix via
git stash, unrelated tograph_memory.go, out of scope forA-06.
DB_POSTGRES_DSN=... make test-functionalagainst a real Postgres 17 (make infra-up)— 140/140 passing, including
PostgresGraphRepository live schema edit isolation contract— driver parity confirmed both ways.go test -race -count=1 ./internal/repositories/... ./internal/services/... ./tests/functional/...— clean.🤖 Generated with Claude Code