Integrates five community PRs merged since v0.13.1 (#52, #56, #58, #59, #60), closes the gaps found while reviewing them, and adds test coverage at every level: +105 fast-suite tests, +3 Neo4j integration tests, a new bolt combo in the slow generated-suite runner, and 2 new tests inside every generated project (fast suite now 1,454 passing; the full CI suite with the connectors extra runs 1,866 passing / 1,881 collected). The new integration coverage immediately exposed — and this release fixes — a long-standing schema-DDL splitter bug that had been silently dropping five indexes/constraints from every seeded database.
NEO4J_DATABASEsupport end-to-end (#60, @henrardo). New--neo4j-databaseflag /NEO4J_DATABASEenv var for the self-hosted backend, threaded through the CLI, wizard (new prompt on manual credential entry), Aura.envimport (previously read and silently discarded), generated.env, the generated app'sSettings, memory-layerMemorySettings, and the raw-driver session inexecute_cypher(). Blank defers to the driver default (neo4j); set it for Aura instances provisioned via the Aura API/CLI, which commonly name the database after the instance id.- Memory-layer failures now surface in
/health(#60, @henrardo).store_message()failures are recorded into the same classified error state used at startup connect time; the bolt/healthresponse gainsmemory,memory_error, andmemory_error_detailfields, and the startup lifecycle checks the memory client instead of assuming success once the raw driver connects. Previously a wrong database name meant the app reported "ok" while every memory write failed with only a log line. execute_cypher()works on NAMS (#56, @benmyrgorod). New_execute_nams_cypher()routes throughclient.query.cypherwith result-shape coercion (_coerce_nams_records), so domain agent tools andPOST /cypherexecute read queries on the hosted backend instead of dying on the never-connected bolt driver._require_neo4j()now returns 503 when the NAMS client is missing, for every route.--ontology-filewired up (#58, @irene221b). The flag was documented (and tracked as #50) but had no code path. Scaffolds directly from a hand-written domain YAML — no LLM call — using the domain id declared in the file.- Custom domains resolve by id (#52, @ecsricktorzynski, issue #30).
load_domain()now searches~/.create-context-graph/custom-domains/(with a declared-domain.idfallback for renamed files), matching whatlist_available_domains()advertises. Bundled domains shadow same-id custom files. demo_scenariosoptional in practice (#59, @irene221b). The generated Playwright spec indexeddemo_scenarios[0].prompts[0]unconditionally, crashing scaffold generation for any domain that omits scenarios. Falls back to a generic prompt.
--ontology-filescaffolds now includedata/ontology.yaml. The renderer only wrote the ontology copy for--custom-domain(YAML string) or bundled domains (copy by id); hand-written ontologies matched neither branch, silently producing a scaffold without the file the docs promise. The raw YAML is now carried into the scaffold verbatim, and the flag also works when the wizard collects the remaining settings.--ontology-file+--custom-domainis now an explicit error instead of a silent precedence pick. (cli.py)NEO4J_DATABASEreaches scaffold-time ingest and the generated import script. #60 threaded the database through the generated app but not throughingest.py(_ingest_with_memory_client,_ingest_with_driver,reset_neo4j) or the scaffoldedimport_data.pybolt path — so--demo/--ingest/--reset-database/make importtargeted the default database while the app read from the configured one, producing an empty graph on non-default-database instances. All bolt sessions now honor the setting;validate_connection()gains an optionaldatabaseparameter. (ingest.py,neo4j_validator.py,templates/backend/connectors/import_data.py.j2).env.exampledocumentsNEO4J_DATABASE. #60 updateddot_env.j2only; the example file now carries the same commented block. (templates/base/dot_env_example.j2)- Playwright spec survives a scenario with an empty
promptslist. #59's guard handled a missingdemo_scenariosbut still crashed rendering when the first scenario hadprompts: [](legal per the Pydantic model). (templates/frontend/e2e/app.spec.ts.j2) - Schema DDL splitter no longer eats statements or executes comment tails. Every consumer of
generate_cypher_schema()output split on;and skipped fragments starting with//— which (a) silently dropped the 5 real statements that sit behind comment headers (person_name,document_title,document_domain,document_name_uniqueand thelocal_file_fulltextindex were never created bymake seed/ingest), and (b) executed the tail of the "dimensions must match your embed model" comment as Cypher, raisingCypherSyntaxErroron every schema apply. New sharedsplit_cypher_statements()strips comment lines before splitting; used byingest.py(both bolt paths), the generatedgenerate_data.py, and the integration suite. (ontology.py,ingest.py,templates/backend/shared/generate_data.py.j2) - Test suite is hermetic against
~/.create-context-graph/custom-domains/. With #52,load_domain()joinslist_available_domains()in scanning the user-local directory — so a contributor's saved custom domains leaked into every domain-iterating test (the "football-intelligence" failures reported while developing #60). An autouse conftest fixture isolates the path; tests that need custom domains patch it explicitly. (tests/conftest.py)
NAMS auto-binds every workspace to the generic nams-default ontology "until an explicit ontology is activated" — and pre-registers a server-side ontology for every bundled domain (a 1:1 catalog; the server's healthcare document is field-for-field our healthcare.yaml). Nothing ever activated one, so all data was stamped with (and extraction spoke) the default vocabulary. Now every NAMS touchpoint binds the workspace to the app's domain first:
- Generated
memory.py:connect_memory()runs_ensure_nams_ontology()after the client connects — already active → no-op; catalog match by domain id → activate its latest version; unknown domain → create the ontology from the scaffold's newbackend/app/ontology_document.jsonand activate it (the custom-domain path). Best-effort and logged: memory still works onnams-defaultif the ontology API is unavailable. Covers app startup andmake seed(which connects through the same path). - CLI ingest (
run_nams_ingest) and the scaffoldedimport_data.pyrun the same ensure sequence as stage 0, pinned by the parity contract test (get_active → list → get → activate, keyword-called so the recorder can compare shapes). build_nams_ontology_document()(ontology.py) produces the server'sOntologyDocumentshape —{domain, entity_types, relationships}, excluding app-side sections (agent_tools,system_prompt,document_templates, …) — used by both the renderer (writesontology_document.jsoninto every scaffold) and the CLI ingest.
Verified against the production service: a fresh workspace on nams-default flips to healthcare on the generated app's first connect (reconnect is a no-op), and a custom test-domain scaffold creates and activates its ontology server-side with the full merged label set. Every stored entity is stamped with the active ontologyVersionId. Known limitation (server-side): the stored entity type remains {Person, Organization, Location, custom} regardless of the active ontology — activation governs the version stamp, validation mode, and extraction vocabulary, not the storage-type enum.
The full flow — scaffold → ingest → boot → API — was exercised against the production NAMS service (memory.neo4jlabs.com) with a real API key and neo4j-agent-memory 0.5.0 (the version fresh scaffolds install). That surfaced five breaks the mocked suites couldn't see, all fixed and now pinned by tests; 19/19 live API checks pass afterward:
- Conversation memory silently failed on every message. The NAMS service only accepts messages addressed to conversation ids it minted at create time — client-chosen session ids 404 with "conversation not found," and
MemoryIntegration(0.5.x) both posts the client id straight through and swallows the failure into an{"error": ...}return value. The generatedmemory.pynow creates the conversation on first use per session and addresses the server-assigned id (_resolve_nams_conversation(), process-local cache — a restart starts a fresh conversation instead of erroring), andstore_message()treats{"error": ...}returns as failures so they surface in/healthrather than reading as success. - Every document/body ingest write failed (
role must be user, assistant, or system+ the same conversation-404). All three NAMS ingest implementations (run_nams_ingest, the scaffoldedimport_data.py, andmake seed'singest_fixtures_nams) now create their message channels viacreate_conversationand sendrole="user"with ametadata.kindmarker ("document"/"entity-body"). Before: 0/25 documents ingested; after: clean. /api/documentsand/api/schema/visualizationreturned nothing. The live service coerces unknownentity_typevalues (OBJECT,EVENT) tocustom, so the server-side OBJECT filter matched nothing — and it rejects empty search queries (query is required), so the schema view's enumerate-everything search 400'd. Both adapters are now cypher-first (using the scaffold's own_pole_type: OBJECT_description marker for documents, and a type-count aggregation for the schema view), with the search-based flows kept as fallbacks.- Graph expand / entity connections were dead.
long_term.get_entity(id)doesn't exist inneo4j-agent-memory0.5.x;expand_node_namsandget_entity_detail_namsnow resolve id-addressed lookups and neighbor edges through the cypher API (which also surfaces the server-createdSAME_ASresolution edges), keeping the old REST flow as a fallback. - NAMS reset never deleted anything.
long_term.delete_entitydoesn't exist in 0.5.x (the old code swallowed theAttributeErrorand reported "0 entities removed"), the REST API has no delete endpoint, and the cypher API is read-only.--reset-databaseand the scaffold'smake resetnow say so honestly (with the current entity count) and point at the NAMS dashboard, instead of pretending. Docs updated to match.
Verified unchanged live: client.query.cypher read queries (the PR #56 dispatch) work and write queries are rejected; the reasoning trace ingest (start_trace/add_step/complete_trace) succeeds; add_relationship/add_fact/add_preference exist in 0.5.0 but raise NotSupportedError against NAMS, so the ccg-edges encoding remains the correct design; reasoning.list_traces is NotSupported client-side, so /api/traces degrades to an empty list on NAMS. Upstream issues worth filing against neo4j-agent-memory: MemoryIntegration should create conversations (or the service should honor client ids), add_message shouldn't silently target nonexistent conversations, and add_entity throws a client-side validation error when the server responds with a dedup/merge result.
tests/test_generated_client_runtime.py(new, 31 tests) — executes the renderedcontext_graph_client.pyandmemory.pyagainst doubles: NAMS dispatch + result coercion + tool-event collection + not-connected error; bolt sessiondatabase=threading;MemorySettingsdatabase pass-through;store_message()error recording/clearing/NotSupportedErrorhandling; the_classify_memory_errorbuckets/healthreports; the NAMS conversation-id translation (created once per session, server id targeted, bolt untouched, create-failure fallback) and the swallowed-{"error"}failure path.test_routes_integration.py(+6) — mounts the generated FastAPI app:POST /cypheron NAMS dispatches throughexecute_cypher(and maps errors to 400), API routes 503 when the NAMS client is missing while/healthreports degraded, bolt/cypherstill injects the$domainparameter, and a livestore_messagefailure flips/healthto degraded with classified fields — then a successful write clears it Three more pin the cypher-first adapters: documents enumerated by description marker (search fallback for custom-typed entities), schema visualization aggregated via cypher.test_cli.py(+12) —TestNeo4jDatabaseFlag(flag→.env, blank default, Aura import, flag-beats-file precedence, dry-run display) andTestOntologyFileFlag(scaffold,data/ontology.yamlcopy, static demo data, invalid YAML exit 1, missing file exit 2,--custom-domainconflict, auto-slug).test_wizard.py(+5) —_parse_aura_envfour-tuple contract: database read/absent/quoted, missing URI/password aborts.test_generated_project.py(+17) — template pins for the database threading (config/client/memory/import script/.env/.env.example, NAMS.envexclusion), memory-error surfacing inmain.py, the NAMS cypher branch, and scenario-fallback rendering (none / empty-prompts / real prompts).test_ontology.py(+9) —split_cypher_statementsunit tests (semicolon-in-comment, comment-header recovery, commented-out DDL dropped, all-domains executable-statement sweep, proof the old pattern droppedperson_name) and custom-domain isolation meta-tests.test_custom_domain.py(+3) — resolution precedence: bundled shadows same-id custom, corrupt custom YAML skipped during the id scan, underscore files ignored.test_bolt_ingest_parity.py(+1) — the scaffolded bolt import session must targetsettings.neo4j_database.test_integration.py(+3,--integration) — explicit-database ingest lands data (viaProjectConfig.neo4j_database),validate_connectionaccepts a database name and rejects an unknown one.TestSchemaCreationnow applies DDL through the shared splitter and asserts the previously-skipped indexes exist.test_generated_tests.py— the slow generated-suite runner now also scaffolds one bolt project (5 combos), and asserts the new backend-specific health tests actually ran. The generatedtest_routes.pygains two tests per backend: NAMS degraded-client reporting, bolt live-write-failure surfacing.scripts/e2e_smoke_test.py— asserts the/healthcontract shape per backend on startup (bolt:neo4j+memoryfields, logging a warning with the classified error when degraded; NAMS:namsfield).
- The full test suite now runs on every PR. The
full-suitejob (formerlymatrix, main-push-only) runspytest --slow --functionalon all pull requests and main pushes: the 176-combo domain × framework matrix, performance tests, generated-project venv suites, and the local-file vault functional tests (theconnectorsextra is installed, which both enables--functionaland materializes ~165 connector-dependent tests that module-levelimportorskipguards silently excluded from dev-only CI — the job runs 1,866 tests). The secrets-dependent smoke-test job stays main-only — fork PRs can't access repository secrets. - CI installs are lockfile-driven. Every job now uses
uv sync --locked --extra ...+uv run --no-sync. The old recipe (uv pip install -e ".[dev]"followed by bareuv run pytest) letuv run's implicit lockfile sync downgrade locked base dependencies underneath freshly-installed latest extras — a newanyiorelease (requiringtyping_extensions.sentinel, newer than the locked pin) made the skew fatal, failing test collection withImportError: cannot import name 'sentinel' from 'typing_extensions'on every branch.--lockedalso means apyproject.tomldependency change without a matchinguv lockfails fast with a clear message instead of resolving to something untested. Makefile test/lint targets useuv run --extra dev ...so local runs resolve from the lock the same way. - 14 accidentally-tracked
.pycfiles untracked. They were committed in the initial commit (before.gitignoreapplied) and have silently churned in diffs ever since;.gitignorealready covers__pycache__/going forward. - Ruff's rule set and version are pinned. The lint job previously installed unpinned latest ruff with no project config, so ruff 0.16's expanded default rule set broke the build with 294 findings for rules this codebase never adopted.
[tool.ruff.lint]now pinsselect = ["E4", "E7", "E9", "F"](the set the codebase is written against — expand it deliberately, not via upstream default drift), andruff>=0.16,<0.17ships in the dev extra so CI andmake lintrun the same binary. Test-job matrix gainsfail-fast: falseso one Python version's failure no longer cancels the other's signal.
reference/cli-options.md—--neo4j-databaseand--ontology-filerows,NEO4J_DATABASEin the env-var table.how-to/use-neo4j-aura.md— caution block on Aura API/CLI-provisioned database names and how the failure presents in/health.how-to/add-custom-domain.md—--ontology-filesemantics: domain id from the file,data/ontology.yamlcopy, mutual exclusion with--custom-domain.reference/generated-project-structure.md—.envlisting includesNEO4J_DATABASE.
Addresses the May 20, 2026 v0.13.0 feedback report. The report mixed verified issues with claims that don't match the current codebase; each claim was verified before scoping work. This release closes every real issue, makes the generated app.models module load-bearing, and adds regression tests so the v0.12.0/v0.13.0 fixes can't silently come back.
-
create-context-graph --dry-runno longer demands a NAMS API key. The non-interactive path validatedMEMORY_API_KEYbefore reaching the--dry-runbranch, so users couldn't preview a scaffold without first signing up. The credential gate is now scoped to non-dry-run flows;--dry-runskips it entirely. (cli.py:392) -
Dead
template_idparameter removed fromlist_documents_nams. The NAMS branch ofGET /documentsalready raises HTTP 501 whentemplate_idis supplied, so the parameter could never reach the function meaningfully. Signature is nowlist_documents_nams(skip, limit). (templates/backend/shared/memory_adapter.py.j2,templates/backend/shared/routes.py.j2) -
DocumentBrowser entity badges use a stable composite key. The mentioned-entities map was the last
key={\${e.name}-${i}`}(index-tainted) site in the frontend; switched tokey={`${selectedDoc.document.title}-${e.name}`}so badges don't collide when the user navigates back to the same document. (templates/frontend/components/DocumentBrowser.tsx.j2`)
- Generated
models.pyusesField(...)for required fields. Previously,generate_pydantic_modelsemittedname: str = ...(bare Ellipsis literal). Valid Pydantic v2, but unfamiliar to contributors and harder to extend with constraints (Field(..., min_length=1)). Required fields now emitname: str = Field(...). (ontology.py:478) - New
GET /schema/modelsendpoint wiresapp.modelsinto the runtime. Returns the JSON Schema for every Pydantic entity model generated from the domain ontology, useful for frontend codegen and OpenAPI clients. Makes the previously-unusedapp.modelsmodule load-bearing. (templates/backend/shared/routes.py.j2)
TestCompositeKeyRegressions(tests/test_frontend_logic.py) — five new assertions pin the composite-key patterns introduced in v0.13.0 (ChatInterfaceentity/preference/tool-call badges,DecisionTracePanelstep keys) plus the newDocumentBrowserfix.TestV0131ModelsPolishandTestV0131TemplateIdRemoval(tests/test_generated_project.py) — assert that the generatedmodels.pyusesField(...)(and never bare= ...), that the/schema/modelsendpoint compiles, and that thelist_documents_namssignature no longer takestemplate_id.- Three new Playwright tests (
templates/frontend/e2e/app.spec.ts.j2) — watch the browser console for React duplicate-key warnings across a multi-prompt chat sequence, verify the decision trace step list renders without page errors, and verify the document browser entity badges render without page errors.
The v0.13.0 feedback report flagged several issues that don't reflect the current code. Documenting here so contributors don't re-litigate:
- "Backend connectors removed from scaffolded projects" — false.
templates/backend/connectors/still exists andrenderer.py:474-514renders connector modules +backend/scripts/import_data.pywhenever--connectoris supplied. Runtimemake import/make import-dry-run/make import-retrytargets continue to work on every scaffold. - "
pyproject.toml.j2bloats NAMS users withsentence-transformers" — false. The template already branches on backend mode at lines 16-20: NAMS scaffolds pinneo4j-agent-memory[litellm], self-hosted scaffolds pinneo4j-agent-memory[litellm,sentence-transformers,extraction,fuzzy]. NAMS users never pull PyTorch. - "Restore media / insurance / supply-chain domains" — these never existed.
git log --all -- src/create_context_graph/domains/*.yamlshows no history for these labels. v0.13.0's "restored domains" arelegal,education,cybersecurity,government— total domain count remains 27. - "Unused
iinContextGraphViewline 210" — leave as-is. The unused index is in a fallbackextractNodesAndRelsparser, not a render hot path, and removing it would churn a path that hasn't drifted in months.
Addresses the May 2026 v0.12.0 feedback report: one runtime bug on the --self-hosted ingest path, one React state bug in the streaming chat, four key={i} re-render hazards, dead/over-fetching code in the document adapter, four restored domains, and documentation for the ccg-edges encoding strategy.
_ingest_via_bolt()now async (scaffoldedimport_data.py).app.context_graph_client.get_driver()returns anAsyncDriver, but the generated_ingest_via_bolt()wasdefand calledwith driver.session() as session:followed by syncsession.run(...). Every self-hostedmake import/make import-retryfailed at runtime with a coroutine-not-iterable error. Function is nowasync def, usesasync with driver, driver.session() as session:to own the driver lifecycle, andawaits everysession.run(...). Both call sites (main(),_retry_deadletter()) wrap withasyncio.run(...)to mirror the NAMS branch.ChatInterface"done" handler no longer reads stale streaming state. ThesetMessagescallback in thedoneSSE branch referencedstreamingEntities/streamingPreferencesfrom closure — these areuseState-backed and could be one render behind the most recententities_extracted/preferences_detectedevent. Accumulators are nowuseRef-backed (streamingEntitiesRef,streamingPreferencesRef); thedonehandler reads from.currentand the two-stepsetMessageswas collapsed into one.list_documents_nams()pushes theOBJECTfilter to the server. Previously, the over-fetch buffer (skip + limit + 50) could be exhausted by unrelated POLE+O entities, silently dropping valid documents on busy NAMS instances. Now passesentity_type="OBJECT"tosearch_entities. Deadtemplate_idfilter at the same site removed.useEffectforexternalInputnow also depends onloading. An "Ask about this" click that landed mid-stream was silently dropped because the effect didn't re-fire whenloadingflipped back to false.
--framework mafalias removed. Use--framework anthropic-toolsinstead. Click now rejectsmafwith the standard "Invalid value for '--framework'" error. TheFRAMEWORK_ALIASESdict and theresolved_frameworkproperty onProjectConfigare gone — call sites useconfig.frameworkdirectly.
- Restored 4 domains as YAML definitions:
legal,education,cybersecurity,government. Each ships with the full schema (entity_types, relationships, document_templates, decision_traces, demo_scenarios, agent_tools, system_prompt, visualization) plus a deterministic static-fallback fixture. Domain count: 23 → 27.
-
key={i}replaced with stable identifiers in 4 locations.ChatInterface.tsx: suggested-prompt buttons keyed by prompt text; entity/preference badges keyed by${type}-${name}-${i}and${category}-${preference}-${i}.DecisionTracePanel.tsx: trace steps keyed bystep-${i}-${action.slice(0, 32)}.
ccg-edgesencoding strategy documented. New Docusaurus page (docs/docs/explanation/ccg-edges.md) explains the fenced YAML block inside entity descriptions, fragility (what happens if descriptions are edited or truncated), the migration playbook once NAMS adds nativeadd_relationship, and the parity test that pins the format. Scaffolded README gains a short "How NAMS stores relationships" section linking to the page.- Custom domain generation walkthrough. New Docusaurus page (
docs/docs/how-to/custom-domains.md) coverscreate-context-graph --custom-domain "..."end-to-end, including the few-shot prompting strategy, the validate-retry loop, and how to convert a generated domain into a permanent contribution.
- Bolt ingest parity test. New
tests/test_bolt_ingest_parity.pymirrorstest_nams_ingest_parity.py— renders the scaffolded template, exec's it against a mockAsyncDriver, and asserts the Cypher statement + parameter sequence matches a frozen golden file. - AST async-shape check.
tests/test_generated_project.pynow parses generatedimport_data.pyand asserts_ingest_via_boltisAsyncFunctionDef, everysession.runcall is inside anAwait, and both call sites wrap withasyncio.run.backend/scripts/import_data.pywas added toPYTHON_FILESso it getspy_compile-checked on every scaffold. - Streaming ref-accumulation test.
tests/test_frontend_logic.pynow AST-checks that thedonehandler inChatInterface.tsx.j2reads fromstreamingEntitiesRef.current/streamingPreferencesRef.current, not theuseStatevalues. - E2E coverage for new panels. Playwright tests for
DecisionTracePanel(tab-switch + step rendering) andDocumentBrowser(pagination + markdown detail view).
The big shift in v0.12.0 is that connector ingest (make import in a scaffolded project) now writes through NAMS REST on NAMS scaffolds — previously the generated import_data.py only spoke bolt Cypher, so the SaaS connectors didn't actually work on the default backend. The two ingest paths (CLI demo-fixture seeding and connector-driven imports) now share a single write shape pinned by a contract test.
ccg-edgesrelationship encoding for NAMS. NAMS REST has noadd_relationshipendpoint yet, so each entity's outbound edges are now encoded into the source entity'sdescriptionas a fencedccg-edgesYAML block (deterministically sorted bytypethentarget). The frontend graph view recognizes this marker and renders edges from it; the agent reads them out naturally as part of the description. A one-shot migration replays them as native edges once NAMS gainsadd_relationship— the seam is_build_ccg_edges_block()in bothsrc/create_context_graph/ingest.pyandsrc/create_context_graph/templates/backend/connectors/import_data.py.j2.- Dual-tracked documents on NAMS. Documents now land as both
long_term.add_entity(name=title, type=OBJECT, description=...)ANDshort_term.add_message(role="document", content=...). The long-term entity is the queryable source of truth (matches the bolt:Documentshape and powers/documents); the short-term message is extraction fuel for the NAMS server-side extractor. The document browser now reads from long-term entities and strips theccg-edges/_pole_type:markers for a clean preview. - Per-connector
BODY_FIELDSmapping. Each connector declaresBODY_FIELDS: dict[str, str]mappingentity_label → property_namefor the prose body. The ingestor pipes that body throughshort_term.add_messageso the NAMS extractor can mine it for secondary entities. Wired up forComment.body(Linear),Message.content(Claude AI / ChatGPT / Claude Code),DecisionThread.content/Reply.content(Google Workspace), andDocument.description/Section.description(local-file). Pure-metadata entities (Person, Project, Label) opt out by omission. - Idempotent connector imports. Generated
import_data.pynow tracks per-connector watermarks in.context-graph/watermarks.jsonso re-runs only fetch deltas. Failed records (one at a time) are appended to.context-graph/deadletter.jsonland the watermark only advances on a clean run. NAMSadd_entityis trusted to merge bynamefor entity-level idempotency. --dry-runand--retrymodes forimport_data.py.--dry-runfetches connectors and writesdata/fixtures.jsonwithout touching the memory backend (useful for sanity-checking a large pull before paying the write cost).--retrydrains.context-graph/deadletter.jsonlback through the writer.- New Make targets.
make import-dry-run(fetch-only, writes fixtures.json),make import-retry(drain deadletter). The legacymake import-and-seedtarget collapsed intomake import— it now both fetches and ingests in one step on either backend. run_nams_ingest()extracted as a reusable async function iningest.py. Takes an already-openMemoryClientplus the fixture dict, ontology, optionalbody_fields, and optionalon_eventcallback; returns counts and a list of failure records. Used by both the CLI's Rich-progress path and the scaffoldedimport_data.py. Pinned to the same call sequence as the generated importer via the newtests/test_nams_ingest_parity.pycontract test (~526 LOC, 14 assertions).
make import-and-seedremoved from generated Makefiles. Replaced by the now-idempotentmake import, which both fetches from connectors and ingests on either backend. Existing scaffolds with the old target keep working until regenerated; the generatedimport_data.pyis the source of truth./documents?template_id=...returns HTTP 501 on NAMS. Template-based filtering relied on theMENTIONSgraph edges that NAMS doesn't yet have. The bolt path is unchanged; un-filteredGET /documentsworks on both backends.
ingest_data()legacy bolt signature restored. v0.11.0 had changed the signature to take aProjectConfigexclusively, breaking library callers that were on the older(neo4j_uri, neo4j_username, neo4j_password)triple.ingest_data()now accepts either: aProjectConfiginstance OR the legacy three positional bolt args;_coerce_ingest_config()synthesizes a boltProjectConfigfrom the legacy triple. CLI usage is unaffected.- Cypher identifier injection guards on bolt fallback.
_ingest_with_driverand_ingest_with_memory_clientnow validate relationship types, source labels, and target labels against[A-Za-z_][A-Za-z0-9_]*before string-interpolating them into a Cypher template. Unsafe identifiers log a warning and skip the record instead of executing. Closes the CodeQLpy/code-injection-shaped finding flagged on the bolt ingest path. (NAMS path was never affected — it never builds Cypher.) - Labeled MATCH on bolt relationship ingest. The bolt connector path was falling back to a label-less
MATCH (a {name: $name})for relationship endpoints, which would match across labels and silently mis-merge. Now usesMATCH (a:SourceLabel ...)/MATCH (b:TargetLabel ...)when the connector supplies both labels, with the existing sanitization guard. - Deadletter retry on partial-failure imports. Records that failed during a connector run (rate limit, transient 5xx) are appended to
.context-graph/deadletter.jsonlwith their original payload;make import-retry(orpython scripts/import_data.py --retry) replays them through the same write path. Records whose failure category isn't retryable (e.g. a permanently unsupported entity shape) are logged but kept in the deadletter for inspection rather than dropped silently.
docs/docs/explanation/memory-backends.md— replaced the "best-effort B-partial port" framing with the actual hybrid write shape:ccg-edgesencoding, dual-tracked documents,BODY_FIELDSextractor channel. Added a note about thetest_nams_ingest_parity.pycontract test that pins the two ingest paths together.docs/docs/how-to/use-nams.md— rewrote the "Seeding a relationship-rich graph" section to reflect that NAMS scaffolds now do encode relationships (just inside descriptions) rather than dropping them, with accg-edgesexample block. Kept the--self-hosted --demorecommendation for native-edge workflows (expand_node, GDS, arbitrary Cypher).src/create_context_graph/ingest.py— module docstring rewritten end-to-end documenting the new NAMS write shape, theBODY_FIELDSextension point, and why the two ingestors are duplicated by design (rendered template vs. runtime CLI consumer).
- New
tests/test_nams_ingest_parity.pycontract test — drivesrun_nams_ingestand the renderedimport_data.pyagainst a shared fixture and asserts identical call sequences against a mockMemoryClient. Pins entity → ccg-edges → body → document → trace ordering, fenced-block format, body-field routing, and decision-trace shape. - Expanded NAMS test coverage. ~1,500 new test lines across
test_ingest_nams.py,test_memory_adapter.py,test_nams_ingest_parity.py,test_routes_integration.py, andtest_doc_snippets.py— coveringccg-edgesround-trips, dual-tracked document reads,BODY_FIELDSper connector, deadletter retry behavior, watermark persistence, the 501 guard on template-filtered/documents, and the bolt Cypher identifier sanitizers.
Rolls up streaming chat for the last two non-streaming frameworks, a much better NAMS failure-mode UX (classified errors surfaced in /health, memory-backend auto-detection from .env), lighter NAMS dependencies, custom-domain robustness fixes, and silent-failure cleanup across all 8 agent templates.
- Streaming chat for CrewAI and Strands. Both frameworks now implement
handle_message_stream(), so the/chat/streamSSE endpoint streams text deltas as the model produces them — previously these two frameworks emitted tool events live but text only arrived at the end of the run. CrewAI subscribes toLLMStreamChunkEventon the crew event bus; Strands iteratesagent.stream_async(). Both include a 60s timeout, partial-text fallback assembly, and emitentities_extracted/preferences_detectedevents. This closes out the streaming matrix — all 8 frameworks now stream text. --import-previewCLI flag. Parses a chat export file (--import-file …+--import-type …) and prints a sanity-check summary (entity counts, conversation date range, sample titles) without scaffolding or ingesting. Useful before committing to a long import of a 1 GB+ ChatGPT/Claude AI export. Implemented incli.py::_run_import_preview().- NAMS error classification surfaced in
/health. New_classify_memory_error()inmemory.py.j2buckets NAMS init failures intoauth/rate_limit/network/config/unknown, with human-readable messages mapped per category (e.g. "NAMS authentication failed — verify MEMORY_API_KEY at https://memory.neo4jlabs.com"). The/healthendpoint now returnsnams_error,nams_error_message,nams_error_detail, andnams_dashboardso the frontend can show a useful diagnostic instead of a generic "memory unavailable". Exposed viaget_error_category()/get_error_message()/get_error_detail()for the FastAPI startup banner. - Memory-backend auto-detection from
.env. New@model_validatorinconfig.py.j2reconcilesmemory_backendwith the credentials actually present in.env: flipsnams → boltifMEMORY_API_KEYis blank butNEO4J_URIis set (and vice-versa), printing a warning. An explicitMEMORY_BACKENDenv var still wins. Default Neo4j credentials in generated.envare now empty rather than baked-in placeholder passwords. - Lighter NAMS dependency footprint. Generated
pyproject.tomlfor NAMS scaffolds drops thesentence-transformersextra (NAMS does embeddings server-side) — extras shrink from[litellm,sentence-transformers]to[litellm]._resolve_embedding_model()short-circuits toNoneon NAMS so the generated venv no longer pullstorchfor a backend that doesn't use local embeddings.
- Custom-domain renderer crash.
_get_domains_path()was imported inside a narrowtry:scope inrenderer.py, raisingUnboundLocalErrorin the success path after partially writingontology.yaml. Import hoisted to module scope. - Custom-domain generation produced silently-truncated ontologies.
custom_domain.pynow checks the LLM responsestop_reasonfor truncation, validates with Pydantic, and asserts completeness (non-emptysystem_prompt/visualization/agent_tools) before accepting. Provides actionable retry messages and clearer errors when the Anthropic/OpenAI SDK isn't installed. - Agent-template degradations across all 8 frameworks. Jinja conditionals that checked
param.defaulttruthiness were rewritten toparam.default is defined, and a silent fallback that swallowed Jinja syntax errors was removed so render failures now surface instead of degrading to stub code. Affects every framework template (anthropic_tools,claude_agent_sdk,crewai,google_adk,langgraph,openai_agents,pydanticai,strands). - Partial streamed text discarded on agent errors. Strands and CrewAI now accumulate emitted text deltas and return the partial response when a generator raises mid-stream, instead of throwing away accumulated output. Also drops a redundant
RuntimeErrorbranch from the memory error classifier. - NAMS Docker builds crashed on
spacy download. The v0.11.2 fix for the generatedMakefileis now mirrored inDockerfile.backend.j2via{% if not is_nams %}— NAMS images no longer fail at build time on a download command for a package they don't depend on. - Frontend "Ask about" button rendered on non-string entity names.
ContextGraphView.tsx.j2adds atypeof === "string"guard before reading.properties.name. - E2E selector regex out of date.
e2e/app.spec.ts.j2regexes updated from/try a demo scenario/ito/try these/ito match the current welcome card label.
docs/docs/how-to/use-nams.md— new "Seeding a relationship-rich graph" section. Documents NAMS's current lack ofadd_relationshipREST support and shows two working patterns: (Option A) scaffold with--self-hosted --demofor the rich dev experience, flipMEMORY_BACKEND=namsfor production reads; (Option B) seed bolt first then migrate. Includes aTODO(nams-relationships)pointer for the future server-side API.- Generated README (
base/README.md.j2) — minor wording updates for the NAMS sign-up flow and the--import-previewworkflow.
- CodeQL
py/incomplete-url-substring-sanitizationcleanup. Test assertions intest_nams_adapter.pyswitched from URL substring checks ("memory.neo4jlabs.com" in env) to full-URL or content-phrase matches. - Frontend devDeps pinned (
package.json.j2). Added@types/react-dom ^19.0.0;overridessection pinslodash ^4.17.24andpostcss ^8.5.10to dodge known vulnerable transitive versions. - New test coverage: ~600 new test lines spread across
test_nams_adapter.py(NAMS error classification, backend auto-detection, Dockerfile spacy guard),test_renderer.py(template-degradation guards, custom-domain regression),test_custom_domain.py(truncation/completeness validation),test_generated_project.py(CrewAI/Strands streaming surface),test_chat_import.py(--import-preview),test_cli.py,test_doc_snippets.py, andtest_routes_integration.py.
Rolls up three follow-up fixes surfaced by running v0.11.0/v0.11.1 end-to-end on a fresh machine, plus a durable safeguard against the same class of bugs.
-
Full matrix + performance test suites broken on CI.
test_matrix.py(184 combos) andtest_performance.py(23 domains) defined their own localrunner = CliRunner()fixtures that bypassed the auto---self-hostedshim added totest_cli.pyin v0.11.0. With NAMS as the new default, every matrix/perf invocation hit the "NAMS API key required for non-interactive mode" guard and failed. 207 of 1,398 slow-suite tests failed on the v0.11.1 tag. Fix: moved_AutoSelfHostedRunnerand therunner/nams_runnerfixtures totests/conftest.pyso every test file inherits the auto-self-hosted behavior. Removed the now-duplicate fixtures fromtest_cli.py,test_matrix.py, andtest_performance.py. -
make installand Docker builds crashed on NAMS scaffolds withNo module named spacy. Both the generatedMakefile'sinstall-backendtarget and the generatedDockerfile.backendranpython -m spacy download en_core_web_smunconditionally, but spacy is only present in the[extraction]extra which NAMS scaffolds correctly omit (entity extraction happens server-side on NAMS). Fix: wrapped thespacy downloadline with{% if not is_nams %}in bothMakefile.j2andDockerfile.backend.j2. On bolt scaffolds the Makefile path is additionally guarded by animport spacycheck so it stays robust even if the user uninstalls the extraction extras post-scaffold. Four regression tests intest_nams_adapter.py::TestBoltRenderedTemplates:test_makefile_skips_spacy_download_on_nams,test_makefile_guards_spacy_download_on_bolt,test_dockerfile_skips_spacy_download_on_nams,test_dockerfile_includes_spacy_download_on_bolt. -
make testin generated projects crashed withNo module named pytest. The generatedpyproject.tomldidn't declare pytest or httpx anywhere, souv syncnever installed them — the generatedtests/test_routes.pyscaffold couldn't run. Fix: added[project.optional-dependencies] dev = ["pytest>=8.0", "httpx>=0.27"]topyproject.toml.j2, and changed the generated Makefile'sinstall-backendtarget fromuv sync→uv sync --extra dev. Generated projects can now runmake testout of the box.
- Root
make smoke-rendertarget — full scaffold → install → import-check → run-generated-tests sweep for both backends, in<1 min, no Neo4j / NAMS / LLM keys required. Catches the class of breakage the mocked unit suite can't see:- dep-resolution failures (
uv syncconflicts) - install-time crashes (e.g. spacy download on NAMS)
- import-time failures in generated
app.main(e.g. questionary default validation, framework SDKs that validate API keys at module-load time) - generated test-scaffold regressions
- dep-resolution failures (
- Sub-targets
make smoke-render-namsandmake smoke-render-boltfor per-backend runs.make smoke-render-cleanremoves the scratch directory (/tmp/ccg-smoke-renderby default). - Verified passing locally:
- NAMS: render → install (no spacy) → import-check → 2 generated tests pass
- Bolt: render → install (with guarded spacy download) → import-check → 2 generated tests pass
The three issues bundled here all surfaced from running the actual product end-to-end on a fresh machine after the v0.11.0/v0.11.1 tags. Each was a class of issue the mocked unit suite couldn't catch by design (CLI fixture bypass, install-time shell commands, generated-project deps). make smoke-render is the durable answer — run it before tagging future releases.
- Interactive wizard crashed at the framework prompt with
ValueError: Invalid 'default' value passed. The value ('Strands') does not exist in the set of choices.—questionary.select(default=...)validates the default againstChoice.title, notChoice.value. The wizard was passing the display label ("Strands") but choices used the framework key ("strands") as their value. Fixed by removing thedefault=argument entirely and reordering the choices list soDEFAULT_FRAMEWORKis first (questionary highlights the first row on entry). Regression testTestQuestionaryConstructionadded totests/test_wizard.py— exercises the realquestionary.selectconstructor (only.askis stubbed) so any baddefault=argument fails at construction time. Total test count: 1,177 → 1,179.
- Default memory backend flipped from self-hosted Neo4j to NAMS —
create-context-graph my-appnow scaffolds against the hosted Neo4j Agent Memory Service by default. The wizard collects a NAMS API key as its memory step. Use--self-hosted(or any explicit--neo4j-*flag) to opt into the legacy bolt path. Existing scaffolded projects are unaffected; only newly generated projects pick up the new default. - Generated
pyproject.tomlpinsneo4j-agent-memory>=0.4.0,<0.6.0(was>=0.1.0). Extras conditional on backend: NAMS scaffolds get[litellm,sentence-transformers]; self-hosted scaffolds additionally get[extraction,fuzzy]for local entity extraction. ingest_data()library signature changed — now takes aProjectConfiginstead of separate Neo4j credentials. CLI users see no change; programmatic users of thecreate_context_graphpackage must update callers.MEMORY_API_KEYenv var added to generated.envfiles. When set, the library auto-routes to NAMS even ifMEMORY_BACKENDis unspecified.
- NAMS hosted backend support — Generated
app/memory.pynow constructsMemoryClient(MemorySettings(backend="nams", nams=NamsConfig(api_key=...)))on the NAMS path. Sign-up panel printed in the wizard with thehttps://memory.neo4jlabs.comlanding URL. --self-hostedCLI flag — Preserves the legacy bolt-Neo4j path with full demo fixtures, schema DDL, and relationship-rich graph view. Recommended for workshops, screen recordings, demos, and air-gapped use.- LiteLLM provider injection — Generated memory layer reads
MEMORY_LLMandMEMORY_EMBEDDINGenv vars (LiteLLM-style provider strings, e.g.anthropic/claude-haiku-4-5,bedrock/anthropic.claude-3-haiku-20240307-v1:0,vertex_ai/gemini-1.5-flash,ollama/llama3). Native adapters resolve first (Anthropic, OpenAI, Bedrock, Vertex AI, SentenceTransformers); everything else routes through LiteLLM. Default fallback:sentence-transformers/all-MiniLM-L6-v2for embeddings,anthropic/claude-haiku-4-5(oropenai/gpt-4o-mini) for entity extraction. - Streamlined 6-prompt wizard — Collapsed from 11 prompts. Domain picker switched to
questionary.autocomplete. Inline Anthropic-key prompt removed (deferred to post-scaffold.envediting with a prominent reminder panel). Advanced settings (MCP toggle, extraction toggles, extra API keys) gated behind a single Y/N prompt. Median wizard run is now ~6 prompts vs ~11. - Default agent framework: AWS Strands — was previously unselected; the wizard now suggests
strandsas the default. All 8 frameworks remain supported. - Backend-aware route adapters — Generated
app/routes.pydispatches/expand,/documents,/traces,/schema/visualization,/entities/{name},/search,/cypherto the NAMS REST adapter (app/memory_adapter.py) or bolt Cypher path based onMEMORY_BACKEND./gds/*returns 501 on NAMS. - Backend-aware MCP config —
claude_desktop_config.jsonships in NAMS or bolt shape depending on the scaffold. NAMS forcesmcp_profile=corebecause extended-profile tools rely on unsupported endpoints (preferences/facts). - Backend-aware
make reset— On NAMS, enumerates entities via REST and deletes one-by-one (slow but correct, with a printed warning). On bolt, retains today'sMATCH (n) DETACH DELETE n. - NAMS-aware
make seed— Generatedgenerate_data.pybranches onsettings.memory_backend. On NAMS, delegates tomemory_adapter.ingest_fixtures_nams()which does the B-partial port (see below). On bolt, applies schema + ingests via Cypher. - Health endpoint backend-aware —
/healthreturns{"memory_backend": "nams", "nams": <bool>}on NAMS or{"memory_backend": "bolt", "neo4j": <bool>}on self-hosted.
The NAMS REST API exposes a narrower write surface than bolt Cypher. The CLI does best-effort B-partial ingest with these documented gaps:
- Relationships are dropped on NAMS —
add_relationshipis not yet exposed by NAMS REST. The CLI logs a single warning per ingest run. The graph view shows entities but no edges. - Entity properties collapse into
description— NAMS REST accepts only{name, type, description}per entity. All other properties (status, severity, blood_type, etc.) are serialized into a markdown block insidedescription. The frontend property panel renders this markdown so the data remains readable. - Preferences and facts are unsupported —
auto_preferences=Trueis forced off on NAMS viaProjectConfig.effective_auto_preferences.auto_extract=Truestill runs but extracted relationships are silently dropped. - Schema DDL skipped on NAMS — NAMS owns its schema.
CREATE CONSTRAINT/CREATE INDEXstatements fromgenerate_cypher_schema()are no-ops on the NAMS path.
For the full relationship-rich demo experience, scaffold with --self-hosted --demo.
| Flag | Purpose |
|---|---|
--self-hosted |
Use self-hosted Neo4j instead of NAMS (the v0.10 default behavior) |
--nams-api-key |
NAMS API key (also reads MEMORY_API_KEY env) |
--nams-endpoint |
Override NAMS endpoint URL (defaults to https://memory.neo4jlabs.com/v1) |
--memory-llm |
LiteLLM provider string for memory entity extraction |
--memory-embedding |
LiteLLM provider string for memory embeddings |
- Use NAMS — sign-up, API key, switching between NAMS and self-hosted, troubleshooting
- Configure Memory Providers — LiteLLM provider strings, native adapters, default fallback behavior, per-provider auth examples
- Memory Backends — conceptual NAMS vs self-hosted comparison, choosing per-project, frontend dispatch architecture
- 1,177 passing fast tests (was 1,102 in v0.10). 50 new tests across 4 files:
test_ingest_nams.py(15) —_ingest_with_namsdispatch, entity serialization, document/trace ingestion, relationship-skip warning, missing-API-key error path,reset_memory_storefor both backends.test_wizard.py(7) — drives the interactive wizard via patched questionary; covers NAMS happy path, NAMS+advanced, self-hosted Docker, self-hosted existing-Neo4j, and edge cases.test_memory_adapter.py(18) — renders a project, imports the generatedmemory_adapter.pyviaimportlib, exercises every adapter function withAsyncMockMemoryClient.test_routes_integration.py(10, gated bypytest.importorskip("fastapi")) — renders a NAMS or bolt project, mounts the generated FastAPI app viaTestClient, asserts correct dispatch on/health,/documents,/search,/schema/visualization,/expand,/traces,/gds/*.test_nams_adapter.pyextended with 5 runtime dispatch tests.
scripts/e2e_smoke_test.py— added--backend {bolt,nams}flag.boltdefault preserves existing flow;namsexercises the hosted-memory scaffold path (requiresMEMORY_API_KEYenv).[dev]extras — now includefastapi,httpx,pydantic-settingsso route integration tests run in CI.
MemorySettings/MemoryClient/MemoryIntegrationconstruction split inmemory.py.j2— explicitMemoryClient(settings)thenMemoryIntegration(client=client, ...)(instead of havingMemoryIntegrationbuild the client implicitly). Enables NAMS backend + LiteLLM provider injection in one place.- CodeQL false-positive fix —
test_nams_adapter.pyassertion changed from substring host check ("memory.neo4jlabs.com" in env_example) to full URL match ("https://memory.neo4jlabs.com/v1" in env_example) to satisfypy/incomplete-url-substring-sanitization. - Generated test scaffold (
backend/tests/test_routes.py) — fixture patches both bolt and NAMS connect/close paths so the generated test suite works regardless of backend.
- local-file connector — Deterministic ingestion of local Markdown, PDF, HTML, AsciiDoc, and Word documents into
:Document→:Sectionhierarchies, withLINKS_TOedges between sections and documents. No LLMs, no embeddings, no randomness. URI-keyed nodes integrate with the existing MERGE-on-(name, domain)pipeline without changes toingest.py. Section URIs use GitHub/Pandoc slug rules (NFKD-normalize, ASCII-lower,[a-z0-9_]runs collapse to-); duplicate-heading collisions disambiguate per-parent (-1,-2, …); skipped heading levels (e.g. H1 → H3) preserve the original level on the child node rather than synthesizing intermediate H2s. Parser strategy per format: markdown-it-py with GFM extensions (Markdown); three-tier fallback pypdf outline →/StructTreeRoot→ pdfplumber font-size heuristic (PDF); BeautifulSoup + lxml (HTML); pure-Python regex with block-delimiter state tracking (AsciiDoc); python-docx (Word). Adds 8 optional dependencies to theconnectorsextra:markdown-it-py,mdit-py-plugins,pdf-oxide,pypdf,pdfplumber,beautifulsoup4,lxml,python-docx. Implementation inconnectors/local_file_connector.pyandconnectors/_local_file/subpackage (parser, mapper, slug, link-resolver). 1,721-linetest_local_file_connector.pyplustest_local_file_vault.pyfunctional test (make test-functional) round-trips a 14-file fixture vault.
reset_database()connection lifecycle — Generatedcontext_graph_client.pypreviously assumed a driver was already open. It now opens its own driver when_driverisNoneand closes it viatry/finally, preserving any pre-existing connection.TestResetDatabaseregression coverage added intests/test_generated_project.py.
- 1,102 passing fast tests (1,321 collected including slow/integration/functional).
- options-intelligence (23rd domain) — Options market intelligence covering 0DTE analysis, dealer positioning (GEX/DEX/VEX/CHEX), gamma regime classification, key levels, and trading strategies. 8 entity types (Underlying, OptionsContract, ExposureLevel, Regime, KeyLevel, Trade, MarketEvent, Strategy), 17 relationships (HAS_OPTION, EXPOSURE_AT, IN_REGIME, TRIGGERED_BY, FLIPPED_TO, PRECEDED_BY, etc.), 10 agent tools (
get_regime,get_key_levels,get_exposure_by_strike,get_trades_by_strategy, etc.), 5 document templates (market briefs, trade journals, regime analysis), 9 decision traces (trade entry, regime flip, level breach, VIX spike, etc.), 4 demo scenarios. Pre-generated fixture: 65 entities, 125 relationships, 25 documents. 17 new property clamp ranges ingenerator.pyfor options-specific values (delta[-1, 1], gamma[0, 0.15], IV[0.05, 2.0], strike, GEX, etc.). 8 label-specific name pools and ID prefixes added inname_pools.py. Tickers (SPX, SPY, QQQ, IWM, AAPL) added to the global_TICKER_POOL.
- Configurable per-resource limits —
GITHUB_LIMIT=20sets the default cap for issues, PRs, and commits. Override individually withGITHUB_ISSUES_LIMIT,GITHUB_PRS_LIMIT,GITHUB_COMMITS_LIMIT. Pagination now usesitertools.isliceto stream results instead of materializing full pages. - Issue/PR body import toggle —
GITHUB_IMPORT_BODY=true(default) controls whether issue and PR bodies are imported asDocumentnodes. Issue/PR bodies are now labeled "Body" rather than "Document" to better reflect their source. - Cross-link issues, PRs, and commits —
GITHUB_LINK_ISSUES_PRS=true(default) createsCLOSESandREFERENCESedges between commits, issues, and PRs. Regex matches(close|closes|closed|fix|fixes|fixed|resolve|resolves|resolved) #N→CLOSES; bare#N→REFERENCES. GraphQLclosingIssuesReferencesprovides authoritative PR closures.GITHUB_LINK_SOURCE=bothselectsregex/graphql/both(falls back tobothon invalid values).CLOSEStakes precedence overREFERENCESfor the same pair, regex and GraphQL results are deduped, and references to numbers outside the fetched set are silently skipped. GraphQL failures log a warning and return empty rather than hard-failing.
- Connector relationship field names — All 7 connectors (GitHub, Notion, Jira, Slack, Gmail, Google Calendar, Salesforce) now emit
source_name/target_namein relationship dicts (wassource/target), aligning with the ingest schema. - Generated
config.pySettings fields — Added env-var fields for all 7 connectors (GitHub, Notion, Jira, Slack, Salesforce, Linear, etc.) so credentials and toggles are honored. Previously silently dropped byextra: "ignore". - Generated
pyproject.toml— Added connector package dependencies (PyGithub,notion-client,atlassian-python-api,slack-sdk, etc.) so generated projects install required SDKs. memory.py.j2boolean rendering — Switched| tojsonto| capitalizeso Jinja-emitted booleans render as PythonTrue/Falserather than JSONtrue/false.- CLI command/comment alignment — Fixed inconsistent spacing in CLI help output so commands and comments line up.
- Test patch target for
is_connected— Tests now patchapp.main.is_connected(where it's used afterfrom … import is_connected) rather thanapp.context_graph_client.is_connected(where it's defined). Generatedtest_routes.pymock fixture also sets dummy API keys (ANTHROPIC_API_KEY,OPENAI_API_KEY,GOOGLE_API_KEY) before importingapp.main, since PydanticAI'sAgent(...)validates the key at module import time. Added regression guardtest_test_file_mocks_is_connectedintests/test_generated_project.pyso accidental removal is caught in the fast unit suite. - options-intelligence fixture quality — Rewrote fixture with consistent per-underlying scoping. Cross-underlying contamination eliminated (each Underlying now has scoped
OptionsContract,ExposureLevel,KeyLevel). Strike scales realistic per underlying (SPX ~5800, SPY ~580, QQQ ~500, IWM ~212, AAPL ~230). Regime timestamps chronologically consistent withFLIPPED_TO/PRECEDED_BYedges. Document content references title entities. Decision-trace placeholders replaced with concrete values from fixture entities. Greeks normalized (delta-1..1, gamma0..0.15, proper put delta signs).
make schematarget — Added to the generatedMakefile. Comments onmake seedandmake import-and-seedclarified.- Tests — 12 new tests for GitHub linking (regex/GraphQL/dedupe/precedence). 89 new tests for body-import toggle and configurable limits. 54 additional renderer tests covering connector field renames, memory boolean rendering, config Settings fields, and the new Makefile target.
- Upgrade npm cli for trusted publishing - project currently uses npm 20 but we need the latest npm cli in order to perform the trusted publishing OIDC workflow.
- Target npm-wrapper directory for node pkg - previously workflow was looing for package.json in top-level directory, but it lives in npm-wrapper.
- Batch entity seeding with
UNWIND—make seedpreviously executed one MERGE query per entity. With large Claude Code imports (27k+ entities), this caused seeding to appear to hang. Entity, relationship, and document creation now useUNWIND $batchwith batches of 500, reducing ~27,000 round-trips to ~55. - Batch ingestion in
import_data.py— The--ingestpath in generated import scripts also usesUNWINDbatching.
- Fix
make importcrash — The scaffolded Claude Code connector crashed withint(None)whenmax_sessionswas passed asNonefromimport_data.py. All credential reads now use theoridiom for None-safety. - Fix missing Settings fields — Added
claude_code_scope,claude_code_since,claude_code_max_sessions,claude_code_content_mode,claude_code_base_pathto the generatedconfig.pySettings class so.envvariables are honored (previously silently dropped byextra: "ignore"). Also addedgoogle_client_id,google_client_secret,gws_folder_idfor the Google Workspace connector. - Fix dict vs attribute access — All template connectors return
dictfromfetch(), butimport_data.pyused attribute access (data.entities). Changed todata["entities"]across the board. - Don't write empty
fixtures.jsonon crash — The import script now skips writingfixtures.jsonwhen no data was collected, instead of silently overwriting it with empty lists. - Fix
make test-connection— Combined two sequentialasyncio.run()calls into one, fixing the "Event loop is closed" error that made it print both "successful" and "failed".
- Expanded scaffolded connector (268 → 457 lines) — The template connector now extracts 9 entity types (added GitBranch, Error, Decision, Preference, Alternative) with 14 relationship types (added ON_BRANCH, ENCOUNTERED_ERROR, MADE_DECISION, CHOSE, REJECTED, NEXT, PRECEDED_BY, USED_TOOL, EXPRESSES_PREFERENCE). Includes secret redaction, language detection from file extensions, file path validation, and
[rerun: bN]suffix stripping. - Claude-Code-specific demo scenarios — When
--connector claude-codeis active, the "Try these" prompts now show relevant questions ("What files have I modified?", "Show me decisions", "What are my coding preferences?") instead of generic software-engineering prompts about PRs and incidents.
- Fixed dark-mode announcement bar (was white on dark background)
- Added explicit
@easyops-cn/docusaurus-search-localplugin configuration - Marked
ANTHROPIC_API_KEYas required for the chat agent in the Claude Code tutorial - Replaced "copy
.env.exampleto.env" with "edit the generated.env" in the tutorial - Added CLI flag literals next to connector display names in the intro page
- Added "Focus" column with one-line disambiguators to the domain catalog
- Custom 404 page with links to Introduction, Quick Start, and "Report Broken Link"
- "See all 22 domains →" link on the homepage carousel
- Tighter scroll transitions for the memory-type explainer
- Higher-contrast step numbers on the "How it works" section
- 7 new tests (1060 total): Settings fields, dict access, entity types, redaction, scenario override
- Claude Code connector — Reads local session JSONL files from
~/.claude/projects/with no authentication required. Parses user/assistant messages, tool_use/tool_result blocks, and progress entries. Extracts 7 entity types (Project, Session, Message, ToolCall, File, GitBranch, Error) with 10 relationship types. Includes heuristic decision extraction (user corrections, deliberation markers, error-resolution cycles, dependency changes) and preference extraction (explicit statements, package frequency). Secret redaction (API keys, tokens, passwords, connection strings) applied by default. 8 session intelligence agent tools injected via the renderer. 5 CLI flags (--claude-code-scope,--claude-code-project,--claude-code-since,--claude-code-max-sessions,--claude-code-content). Implementation split intoconnectors/claude_code_connector.pyandconnectors/_claude_code/subpackage (parser, redactor, decision_extractor, preference_extractor). - Google Workspace connector — Imports from 6 Google APIs (Drive Files, Comments, Revisions, Activity, Calendar, Gmail) with OAuth2 authentication and dynamic scope building. Extracts decision traces from resolved comment threads in Google Docs (question, deliberation, resolution, participants). 10 decision-focused agent tools (
find_decisions,decision_context,who_decided,document_timeline,open_questions,meeting_decisions,knowledge_contributors,trace_decision_to_source,stale_documents,cross_reference). Cross-connector linking detects Linear issue references in comment bodies, doc names, email subjects, and meeting descriptions. 9 CLI flags for scoping imports. Rate limiting (950 queries/100s with exponential backoff).
- Replaced weak cryptographic hashing — Switched from MD5/SHA1 to SHA-256 for content hashing in connectors (code scanning alerts #8 and #9).
- Session collision fix —
Session.namenow usessession_idas the unique MERGE key to avoid cross-session collisions when importing Claude Code data. - Linear connector hardening — Don't fail on blank Linear team key; improved robustness of Linear import with better error handling.
- Google Workspace connector template improvements and renderer integration
- Updated import_data.py template to handle new connectors
- 10 total SaaS connectors (GitHub, Notion, Jira, Slack, Gmail, Google Calendar, Salesforce, Linear, Google Workspace, Claude Code)
- 955 passing tests (1,165 collected including slow/integration)
- Linear SaaS connector — GraphQL-based connector with cursor-based pagination and rate limiting against
https://api.linear.app/graphql. Maps 12 entity types (Issue, Project, Cycle, Team, Person, Label, WorkflowState, Comment, ProjectUpdate, ProjectMilestone, Initiative, Attachment) to the POLE+O entity model with 26 relationship types. Imports issue relations, threaded comments with resolution tracking, project updates with health status, milestones, initiatives, attachments, and Linear Docs. Issue history entries are transformed into decision traces capturing state transitions, assignment changes, and priority changes with actor attribution. Uses stdlib only (urllib.request). - Linear connector hardening — Named constants, structured logging, URLError/JSONDecodeError/429 handling with retry, pagination safety limits (
MAX_PAGES), null-safe field access, team key validation duringauthenticate(), incremental sync viaupdated_after.
- Fix traces silent failure — Decision trace ingestion no longer silently fails on malformed data.
- New tutorial:
linear-context-graph.md— end-to-end guide for importing Linear project data - Updated CLI options reference and SaaS data import guide
- Click on schema node works again — The
Buttoncomponent was added to the template for the "Ask about [entity]" feature but was never added to the@chakra-ui/reactimport. Clicking a schema node crashed the React component, preventing double-click expand from working. - Python 3.14 boundary — Added
<3.14torequires-pythonfor forward compatibility.
- Improved responsive design for Docusaurus landing page
- neo4j-agent-memory no longer requires OpenAI API key — Removed
[openai]extra from the generatedpyproject.tomldependency. Conversation memory now uses localsentence-transformers(all-MiniLM-L6-v2, 384 dims) by default. IfOPENAI_API_KEYis set in the environment, automatically upgrades to OpenAItext-embedding-3-small(1536 dims). Addedsentence-transformers>=2.0as an explicit dependency so local embeddings work out of the box with zero API keys. - openai-agents framework warns about missing API key — CLI now displays a clear warning when
--framework openai-agentsis selected without--openai-api-key. The interactive wizard prompt text changes to indicate the key is "required" (not optional) for this framework.
-
67 new entity name pools — Added domain-appropriate names for every entity label across all 22 domains.
LABEL_NAMESnow has 118 entries (up from 51), eliminating all "Label 1" / "Label 2" fallback names. Covers agent-memory (Conversation, Memory, Session, ToolCall), digital-twin (Alert, Asset, Sensor, Reading, MaintenanceRecord, System), golf-sports (Round, Handicap, Hole, Course, Tournament), hospitality (Room, Reservation, Guest, Staff), oil-gas (Well, Equipment, Reservoir, Formation, Permit), personal-knowledge (JournalEntry, Note, Bookmark, Contact, Topic, Project), retail-ecommerce (Order, Product, Customer, Campaign, Category), vacation-industry (Booking, Package, Resort, Season), wildlife-management (Sighting, Camera, Habitat, Individual, Threat), conservation (Stakeholder), data-journalism (Correction), GIS (Boundary, Coordinate, Feature, Layer, MapProject, Survey), GenAI/LLM-Ops (Model, Prompt, Evaluation, Experiment), product-management (Epic, Metric, Objective, Release, Feedback, UserPersona), and scientific-research (Paper, Researcher, Grant, Institution). -
Post-generation value clamping — LLM-generated entities are now post-processed by
_validate_and_clamp()ingenerator.py. Clamps 28 property types to domain-reasonable ranges (e.g.,price_per_night: $30–$2,000;duration_hours: 0.25–24;rating: 1–5;latitude: -90–90). Also corrects taxonomy class mismatches (e.g., Bengal Tiger → "mammalia", not "aves"). -
Richer entity descriptions — Added
_LOCATION_LABELS,_EVENT_LABELS, and_OBJECT_LABELSsets (parallel to existing_PERSON_LABELS/_ORGANIZATION_LABELS) for POLE-type-aware descriptions. Added 7 label-specific description overrides for Medication, Permit, Sensor, Equipment, Paper, Model, and Species. Fallback descriptions no longer say "record tracked in the knowledge graph". -
digital-twin fixture fix — Fixed label casing in
digital-twin.json(UPPERCASE → PascalCase) to match the domain YAML schema. -
Domain-scoped entity MERGE keys — Changed entity MERGE from
{name: $name}to{name: $name, domain: $domain}in bothgenerate_data.py.j2andingest.py. Prevents constraint violation warnings when multiple domains share a single Neo4j instance.
- google-adk AttributeError guard — Added
try/except AttributeErroraroundrunner.run_async()in bothhandle_messageandhandle_message_streamto gracefully handle thegoogle-genaiSDK'sBaseApiClientcleanup error when_async_httpx_clientwas never initialized.
- Quick-Start page — New
docs/quick-start.mdwith a 5-step guide: scaffold → Neo4j setup → configure → seed → start. - use-neo4j-local guide — New
docs/how-to/use-neo4j-local.mdcovering@johnymontana/neo4j-local(npx), Neo4j Desktop, and Docker standalone with troubleshooting tips. - Domain catalog — New
docs/reference/domain-catalog.mdlisting all 22 domains with entity types, agent tool counts, sample questions, and scaffold commands. Auto-generated from domain YAML files. - Architecture diagram — Mermaid flowchart added to the Introduction page showing CLI → Template Engine → Backend/Frontend → Neo4j data flow. Added
@docusaurus/theme-mermaidfor rendering. - switch-frameworks 404 fix — Added
slug: switch-frameworksto frontmatter so/docs/how-to/switch-frameworksresolves correctly. - Updated navigation — Sidebar now includes quick-start, use-neo4j-local, and domain-catalog pages.
- Larger status indicator — Backend health dot enlarged from 8px to 12px with a text label ("Connected" / "Degraded" / "Offline").
- Health check retry on initial load — First page load now retries the health check 3 times with exponential backoff (1s, 2s, 4s) before showing "Offline". Prevents the transient "Internal Server Error" on initial Next.js compilation.
- Improved empty graph state — Empty knowledge graph panel now shows a link icon, "Your knowledge graph will appear here" heading, and actionable guidance text instead of a minimal "No graph data to display" message.
- 691 passing tests (89 new), up from 602
- New
tests/test_fixtures.py(88 tests) — Cross-validates all 22 domains:- Schema alignment: fixture entities have all required YAML properties
- Agent tool property references: Cypher queries only reference properties that exist in schema or fixtures
- Label coverage: fixtures include entities for every YAML-defined label
- Data quality: numeric property values fall within reasonable ranges
- Docusaurus landing page redesign — New animated terminal hero section with domain-specific demo commands, improved hero animation timing, and terminal width/height fixes.
- Mobile navigation — Responsive nav bar with mobile layout improvements and design polish.
- CI matrix job — Full test suite (including domain × framework matrix, perf, and generated project tests) now runs in the
matrixCI job on push tomain.
- 4 new docs pages — "Use Neo4j Aura", "Use Docker", "Why Context Graphs?", "Framework Comparison"
- Updated sidebars with all new pages
- Bug fixes and data quality improvements across domains
- Updated docs and test coverage
- CrewAI dependency fix — Changed
crewai>=0.1tocrewai[anthropic]>=0.1in framework dependencies. The crewai agent template usesllm="anthropic/claude-sonnet-4-20250514"which requires the anthropic extra. Without it, the generated project crashes on startup withImportError: Anthropic native provider not available. - CLI non-interactive mode fix — The CLI no longer requires a positional
PROJECT_NAMEargument when all flags (--domain,--framework) are provided. Auto-generates a slug likehealthcare-pydanticai-app. Also added TTY detection with helpful error messages for CI/CD environments.
- Document Markdown rendering — Static document content now uses Markdown headings (
##) instead of RST-style===/---separators. The DocumentBrowser component renders content with ReactMarkdown. - Entity-derived document titles — Document titles now reference primary entities: "Discharge Summary: Maria Elena Gonzalez" instead of generic "Discharge Summary #1".
- Realistic entity descriptions — Replaced generic "Comprehensive patient profile for..." with POLE-type-aware descriptions using domain roles and industries (e.g., "Dr. Sarah Chen, attending physician specializing in healthcare").
- Domain-aware Organization.industry — Added
DOMAIN_INDUSTRY_POOLfor all 22 domains. Healthcare organizations get "Hospital Systems" instead of "Technology". - Realistic decision trace observations — Observations now reference actual entity names: "Verified Dr. Sarah Chen against healthcare standards" instead of generic "Found 7 relevant records".
- Improved thinking text filter — Added continuation patterns to catch multi-sentence agent thinking blocks between tool calls.
list_*tools — Every domain now has a list tool for its primary entity type (e.g.,list_patients,list_players,list_accounts) with sort and limit parameters.get_*_by_idtools — Every domain now has a direct ID lookup tool that returns the entity with all connections (e.g.,get_patient_by_id,get_player_by_id).- Gaming-specific — Added
get_top_playerstool (sort by level) for the gaming domain.
- "Ask about this" button — Clicking a node in the Knowledge Graph shows an "Ask about [entity]" button that sends a query to the chat.
- Node hover tooltips — Graph nodes show full name, labels, and key properties on hover.
- Health polling optimization — Reduced polling frequency from 30s to 60s.
- Responsive hint text — Keyboard shortcut hint hidden on small screens to prevent overlap.
- Suggested question max width — Pill buttons capped at 320px to prevent layout stretching.
- Scrollable label badges — Label filter badges in the graph panel scroll when they overflow.
- Seed constraint fix — Entity seeding now uses
ON CREATE SET / ON MATCH SETto avoid constraint violations on re-seed.
- 4 new docs pages — "Use Neo4j Aura", "Use Docker", "Why Context Graphs?", "Framework Comparison"
- Updated sidebars — All new pages added to Docusaurus navigation
- 602 passing tests (57 new), up from 545
- CrewAI no longer hangs — Added explicit
llm="anthropic/claude-sonnet-4-20250514"to prevent defaulting to OpenAI. Added request-level logging and reduced timeout to 60s. - Strands serialization fix — Added
_extract_text()helper that robustly extracts text from agent results, handlingParsedTextBlockserialization issues from newer Anthropic SDK versions. - Google ADK API key support — Added
--google-api-keyCLI flag (GOOGLE_API_KEYenv), wizard prompt when google-adk is selected, andGOOGLE_API_KEYin generated.env/.env.exampletemplates.
--ingestnow creates proper Document and DecisionTrace nodes — Both ingestion paths now create:Documentand:DecisionTrace/:TraceStepnodes using direct Cypher, matching thegenerate_data.pypattern that the frontend expects. Previously, Documents and Decision Traces panels appeared empty after--ingest.- Entity MERGE fix — Direct driver ingestion now uses
MERGE (n:Label {name: $name}) SET ...instead ofMERGE (n:Label {all_props}), preventing duplicate nodes.
- Domain-aware base entities — Person, Organization, Location, Event, and Object entities now use domain-specific names and roles (doctors for healthcare, traders for finance, game designers for gaming, etc.).
- Fixed templated property values — Properties like "Metformin 500mg - Contraindications" now replaced with realistic values. Added pools for contraindications, dosage_form, allergies, sector, lead_reporter, manufacturer, mechanism_of_action, population_trend, and habitat.
- Redesigned chat input — Bordered container with focus highlight and keyboard shortcut hint (Chakra UI Pro inspired).
- Suggested questions redesign — Pill-shaped buttons with full text (no 60-char truncation), "Try these" label with Sparkles icon.
- Message avatars — User and assistant messages now have Circle avatars with User/Bot icons.
- Tool progress counter — Shows "Running tool N of M..." during tool execution.
--democonvenience flag — Shortcut for--reset-database --demo-data --ingest--google-api-keyflag — New CLI flag withGOOGLE_API_KEYenv variable support
- 545 passing tests (35 new), up from 510
- Prevent agents from returning pre-tool text as the final answer — PydanticAI's
run_streamfires aFinalResultEventthe moment anyTextPartbegins streaming. When Claude emits "I'll search for..." alongside a tool call,run_streamtreats that text as the final output and exits before tool results are incorporated. Replacedagent.run_stream()+stream_text()withagent.run()inhandle_message_stream, which completes the full agent loop before emitting text. - Ruff lint fixes — Resolved lint errors across connectors, tests, and scripts.
- Test assertion fix — Directory conflict test now asserts on "not empty" instead of "already exists" for better cross-platform compatibility.
- Improved Anthropic Tools and Claude Agent SDK agent templates
- Enhanced
context_graph_clientevent handling and error recovery - ChatInterface component improvements
- Better error handling in API routes
generate_data.pyimprovements for data quality- 74 new ontology validation tests
- SSR hydration fix in frontend components
- PydanticAI tool serialization fix — agent tools now return JSON string types correctly
- Google ADK hyphenated domain name sanitization
- HuggingFace warning suppression in agent templates
- Retry button on chat errors
- Agent thinking text collapsible filter — reasoning steps render in a collapsible "Show reasoning" section
- Strands
max_tokensconfiguration support - Cypher query validation tests across all 22 domains
- 22 complete domain ontologies with pre-generated LLM fixture data shipped for all domains
- Domain-specific static name pools — 200+ realistic names across 50+ entity labels (medical diagnoses, financial instruments, software repos, etc.)
- Label-aware ID prefixes (
PAT-for Patient,ACT-for Account, etc.) - 12+ domain-specific property pools (currency codes, ticker symbols, drug classes, medical specialties, severities)
domainproperty on all ingested entities for cross-domain isolation when sharing a Neo4j instance- Structured document templates for static fallback data generation
- Fixed missing SSE event messages in chat streaming
- Float value clamping for confidence/rating/efficiency fields
- 510 passing tests (145 new), up from 365
- Fixed conversation history fetching in
context_graph_clientfor multi-turn sessions
- Fixed
pyproject.tomlbuild configuration bug - Strands framework default changed from Bedrock to AnthropicModel
- Agent template improvements across multiple frameworks
- Domain YAML fixes for gaming, genai-llm-ops, healthcare, personal-knowledge, product-management, retail-ecommerce, software-engineering, and trip-planning
- Changed Strands agent framework default from AWS Bedrock to Anthropic native model (
AnthropicModel)
- Fixed API key handling and validation across agent frameworks
- Added
Dockerfile.backendtemplate for Docker builds - Makefile improvements for containerized deployments
- Bug fixes across agent templates
- Playwright e2e test scaffolding for generated projects (
app.spec.ts,playwright.config.ts) - Improved e2e smoke testing infrastructure
- Server-Sent Events (SSE) streaming for real-time chat responses and tool call visualization
POST /chat/streamendpoint withasyncio.Queue-based event streaming- Token-by-token text streaming for PydanticAI, Anthropic Tools, Claude Agent SDK, OpenAI Agents, LangGraph
- Real-time tool call events with Timeline/Spinner/Collapsible UI components
- Text delta batching (~50ms) to optimize React re-renders
- E2E smoke testing infrastructure (
scripts/e2e_smoke_test.py) - Documentation updates
- Critical: Enum identifier sanitization — special characters (
A+,A-,3d_model) in domain ontology enum values now generate valid Python identifiers with value aliases - Critical: Graceful degradation when Neo4j is unavailable — backend starts in degraded mode,
/healthendpoint reports connectivity status - High: Cypher injection prevention in GDS client — label parameters validated against entity type whitelist
- High: CrewAI async/sync deadlock resolved — replaced bare
asyncio.run()withnest_asyncio-compatible helper, crew execution moved to thread - High: Claude Agent SDK model version now configurable via
ANTHROPIC_MODELenvironment variable - Medium: Silent exception swallowing replaced with structured warning messages in
ingest.pyandvector_client.py - Medium: JSON parsing errors in agent tool calls now return helpful error messages instead of crashing
- Medium: Input validation (
max_length) added to chat and search request models - Low: CLI validates empty project names before entering wizard
- Low: Healthcare YAML blood type enums properly quoted
--dry-runCLI flag — preview what would be generated without creating files--verboseCLI flag — enable debug logging during generation/healthendpoint in generated projects — returns Neo4j connectivity status and app version- CORS origins configurable via
CORS_ORIGINSenvironment variable constants.pymodule in generated projects — centralizes magic strings (index names, graph projections, embedding dimensions)- Document browser pagination (page size 20, prev/next controls)
- Semantic HTML landmarks (
<main>,<section>,<aside>) and ARIA labels in frontend - Actionable error messages in chat interface — distinguishes backend errors, network failures, and Neo4j unavailability
- Query timeouts (30s default) on all Neo4j operations
- Credential warnings in generated
.env.example - CORS production configuration guidance
- 365 passing tests (51 new), up from 314
- New: enum identifier sanitization edge cases
- New: models.py compilation across all 22 domains (prevents enum regression)
- New: v0.4.0 feature validation (health endpoint, constants, graceful degradation, input validation, CORS, pagination)
- New: CLI validation and flag tests
- neo4j-agent-memory integration for multi-turn conversations
- Interactive NVL graph visualization (schema view, double-click expand, drag/zoom, property panel)
- LLM-generated demo data (80-90 entities, 25+ documents, 3-5 decision traces per domain)
- Markdown rendering in chat with tool call visualization
- Document browser and entity detail panel
- Improved graph visualization and frontend styling
- Docusaurus documentation site setup and deployment
- Improved domain fixture data quality
- 314 passing tests
- 7 SaaS data connectors — GitHub, Notion, Jira, Slack, Gmail, Google Calendar, Salesforce
- Each connector implements
BaseConnectorABC withauthenticate(),fetch(), andget_credential_prompts() - Gmail/Google Calendar prefer
gwsCLI with Python OAuth2 fallback - Custom domain generation — generate complete domain ontology YAMLs from natural language descriptions using LLM (Anthropic/OpenAI)
- Custom domains saved to
~/.create-context-graph/custom-domains/for reuse - Neo4j Aura
.envimport andneo4j-localsupport in wizard - Documentation site (Docusaurus) with deployment configuration
- Bug fixes for CLI and template rendering
- Test improvements and expanded coverage
- Added
.gitignoreto generated projects
- Interactive CLI scaffolding tool (
create-context-graph) invoked viauvxornpx - 7-step interactive wizard with Questionary prompts
- 8 agent frameworks: PydanticAI, Claude Agent SDK, OpenAI Agents SDK, LangGraph, CrewAI, Strands, Google ADK, Anthropic Tools
- Domain ontology system with YAML definitions and two-layer inheritance (
_base.yaml) - Jinja2 template engine generating full-stack projects (FastAPI backend, Next.js + Chakra UI v3 frontend)
- Neo4j schema generation (constraints + GDS projections)
- Static and LLM-powered synthetic data generation pipeline
- Neo4j data ingestion via
neo4j-agent-memoryor direct driver fallback - Domain-specific agent tools with Cypher queries
- NVL graph visualization component