chore: add neo4j cypher, python driver, and query tuning skills#9967
chore: add neo4j cypher, python driver, and query tuning skills#9967dgarros wants to merge 4 commits into
Conversation
Vendor the neo4j-contrib/neo4j-skills cypher, python-driver, and query-tuning skills into .agents/skills/ and record them in skills-lock.json.
The neo4j skills add vendored Python scripts under .agents/skills that trip ruff's ALL ruleset (41 errors). These are third-party skill content installed via skills-lock.json, so exclude them from linting via extend-exclude (which survives the CLI `--exclude` override CI passes). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
21 issues found and verified against the latest diff
Confidence score: 3/5
- In
neo4j-driver-python-skill/references/performance.md, theBookmarks.from_raw_values(*...)and asyncio examples are currently non-runnable (TypeError/ awaiting a non-awaitable tuple), so readers can copy code that fails immediately — fix those snippets to the correct iterable call form and an actual async driver flow before merging. - In
neo4j-cypher-skill/references/cypher-syntax.mdandneo4j-cypher-skill/references/syntax-traps.md,coll.sort()is documented as available for 2025.10 andPROPERTY_EXISTSis shown with invalid quoted syntax, which can lead users to ship queries that error at runtime — correct version tags and syntax examples to match the stated target stack. - In
neo4j-cypher-skill/references/indexes.mdandneo4j-query-tuning-skill/references/plan-operators.md, planner/index guidance is internally inconsistent (RANGE vs TEXT and STARTS WITH operator behavior), which can drive incorrect indexing choices and poorer query plans — align these tables with Neo4j planner behavior in one pass before merge. - In
neo4j-cypher-skill/scripts/define_schema.py, self-relationship handling overwrites one direction, so generated schema JSON can silently drop theoutedge definition for same-label relationships — merge only after preserving bothinandoutentries in that branch.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name=".agents/skills/neo4j-query-tuning-skill/SKILL.md">
<violation number="1" location=".agents/skills/neo4j-query-tuning-skill/SKILL.md:98">
P3: The operator name `UndirectedRelationshipByIdSeekPipe` in the operator reference table doesn't match the name `UndirectedRelationshipByIdSeek` in `references/plan-operators.md`. An agent cross-referencing between the two files won't find a match, and the descriptive notes also differ ("Lookup by relationship ID" vs "Undirected rel scan — matches twice"). Align the name and description in both files so the skill consistently references the same operator.</violation>
</file>
<file name=".agents/skills/neo4j-driver-python-skill/references/transactions.md">
<violation number="1" location=".agents/skills/neo4j-driver-python-skill/references/transactions.md:33">
P3: Setting `original.__suppress_context__ = False` is a no-op: Python's `raise ... from ...` sets `__suppress_context__` on the exception being raised (`rollback_err`), not on the cause object (`original`). The line doesn't achieve the intended 'chain both exceptions' effect and could mislead readers copying this pattern into real code.</violation>
</file>
<file name=".agents/skills/neo4j-driver-python-skill/README.md">
<violation number="1" location=".agents/skills/neo4j-driver-python-skill/README.md:20">
P3: The 'Version / compatibility' note dates driver v6.x as 'Jan 2026+', but v6.0.0 actually shipped 2025-09-30 per PyPI's release history — the date is off by a few months and could mislead readers about when v6 became available.</violation>
</file>
<file name=".agents/skills/neo4j-cypher-skill/references/apoc.md">
<violation number="1" location=".agents/skills/neo4j-cypher-skill/references/apoc.md:367">
P3: This table row is self-contradictory and factually off: it lists `apoc.coll.toSet` as deprecated but then gives "`apoc.coll.toSet` still works" as its own replacement, when the actual replacement per Neo4j's APOC docs is the native `coll.distinct()` function. Since this is agent-facing guidance meant to steer code generation toward modern Cypher 25 syntax, the incorrect replacement could cause agents to keep emitting the deprecated call.</violation>
</file>
<file name=".agents/skills/neo4j-cypher-skill/references/indexes.md">
<violation number="1" location=".agents/skills/neo4j-cypher-skill/references/indexes.md:34">
P2: This decision-table row is factually wrong and self-contradicts the 'trigram internals' section later in the same file: Neo4j's planner prefers RANGE over TEXT for `IN` list-membership checks on strings (TEXT only wins for CONTAINS/ENDS WITH). As written, this will steer agents/contributors toward creating the wrong index type for `IN` filters.</violation>
</file>
<file name=".agents/skills/neo4j-driver-python-skill/references/async.md">
<violation number="1" location=".agents/skills/neo4j-driver-python-skill/references/async.md:42">
P2: The `result.single()` / `single(strict=False)` rows in the Async Result Methods table are factually backwards: with the default `strict=False`, zero records return `None` (no raise) and multiple records emit a warning and return the first record (no raise) — raising only happens with `strict=True`. An agent following this table could wrongly assume `single()` raises on empty results and skip a `None` check, leading to unhandled `TypeError`/`AttributeError` downstream.</violation>
<violation number="2" location=".agents/skills/neo4j-driver-python-skill/references/async.md:96">
P3: The sync-driver anti-pattern example uses `GraphDatabase` but it's never imported anywhere in this reference file, making the snippet non-self-contained if copy-pasted.</violation>
</file>
<file name=".agents/skills/neo4j-cypher-skill/scripts/define_schema.py">
<violation number="1" location=".agents/skills/neo4j-cypher-skill/scripts/define_schema.py:95">
P2: For self-relationships (from_label == to_label), the second assignment to schema["value"][to_label]["relationships"][rel] overwrites the first, so the generated schema JSON loses the 'out' direction entry for that node/relationship pair.</violation>
<violation number="2" location=".agents/skills/neo4j-cypher-skill/scripts/define_schema.py:102">
P3: If the same relationship type is defined more than once (e.g. between different node pairs), the top-level schema["value"][rel] entry is overwritten rather than merged, losing previously captured properties.</violation>
</file>
<file name=".agents/skills/neo4j-query-tuning-skill/references/plan-operators.md">
<violation number="1" location=".agents/skills/neo4j-query-tuning-skill/references/plan-operators.md:19">
P2: This reference table incorrectly states that `NodeIndexContainsScan` handles both CONTAINS and STARTS WITH and that STARTS WITH requires a TEXT index. In practice, STARTS WITH is planned via `NodeIndexSeekByRange` against a RANGE index (Neo4j docs), so this entry will mislead agents/devs debugging plans into thinking they need a TEXT index for prefix queries.</violation>
</file>
<file name=".agents/skills/neo4j-cypher-skill/scripts/generate_schema.py">
<violation number="1" location=".agents/skills/neo4j-cypher-skill/scripts/generate_schema.py:45">
P3: This schema export is read-only, but execute_query defaults to write routing, so in a clustered Neo4j deployment the query would be routed to the leader instead of a follower/read replica. Add `routing_="r"` to route it as a read.</violation>
</file>
<file name=".agents/skills/neo4j-cypher-skill/references/syntax-traps.md">
<violation number="1" location=".agents/skills/neo4j-cypher-skill/references/syntax-traps.md:25">
P2: `coll.sort()` was only introduced in Neo4j 2025.11 (per Neo4j's release notes), but the PR states this skill targets the Neo4j 2025.10 stack Infrahub runs on — following this guidance verbatim would produce a query that fails on Infrahub's actual database version. Worth noting the version requirement (like other rows in this table do, e.g. the `LET`/`INSERT` rows) or keeping the `apoc.coll.sort` fallback for pre-2025.11.</violation>
<violation number="2" location=".agents/skills/neo4j-cypher-skill/references/syntax-traps.md:46">
P2: This row is factually incorrect: `toInteger(null)` returns `null` per Neo4j docs, it doesn't throw (it only errors on non-numeric/string/boolean types). Recommend removing this row or correcting it (e.g. contrast with `toInteger('abc')`/non-convertible types, which does throw).</violation>
</file>
<file name=".agents/skills/neo4j-driver-python-skill/references/performance.md">
<violation number="1" location=".agents/skills/neo4j-driver-python-skill/references/performance.md:90">
P2: This asyncio example reuses the sync `driver` (created via `GraphDatabase.driver(...)` earlier in the file) inside `asyncio.gather`. Since `execute_query` on a sync driver returns a plain tuple rather than an awaitable, `asyncio.gather(*tasks)` will raise `TypeError`, and this also contradicts the skill's own `async.md` guidance to never use the sync driver inside asyncio — the example should build an `AsyncGraphDatabase` driver and `await` each `execute_query` call inside the gather.</violation>
<violation number="2" location=".agents/skills/neo4j-driver-python-skill/references/performance.md:112">
P2: `Bookmarks.from_raw_values` accepts a single iterable of bookmark strings, not variadic args — calling it as `Bookmarks.from_raw_values(*bookmarks_a.raw_values, *bookmarks_b.raw_values)` will raise `TypeError` for any real usage of this snippet. It should combine the two sets into one iterable before passing it in.</violation>
</file>
<file name=".agents/skills/neo4j-cypher-skill/references/cypher-syntax.md">
<violation number="1" location=".agents/skills/neo4j-cypher-skill/references/cypher-syntax.md:253">
P2: The `PROPERTY_EXISTS` example uses a quoted string literal for the property name, but Neo4j's GQL predicate requires an unquoted identifier — `PROPERTY_EXISTS(n, 'prop')` is invalid syntax; it should be `PROPERTY_EXISTS(n, prop)`. Since this doc is meant as an authoritative Cypher reference for contributors, this typo could get copied into real queries and fail.</violation>
<violation number="2" location=".agents/skills/neo4j-cypher-skill/references/cypher-syntax.md:277">
P2: `coll.sort(list)` is tagged `[2025.01]` but was actually introduced in Neo4j 2025.11 (per Neo4j's Cypher 25 list-functions docs), not 2025.01 — since the PR states Infrahub runs Neo4j 2025.10, following this reference would lead contributors to use a function that doesn't exist yet on the deployed version.</violation>
</file>
<file name="skills-lock.json">
<violation number="1" location="skills-lock.json:34">
P2: The new `neo4j-cypher-skill` entry substantially overlaps with the pre-existing (unlocked) `.agents/skills/neo4j-cypher-guide` skill — both cover modern Cypher syntax, QPP, and subqueries. Consider retiring/merging `neo4j-cypher-guide` (or adding it to the lockfile with a clear scope split) so agents don't get two competing sources of Cypher guidance.</violation>
</file>
<file name=".agents/skills/neo4j-cypher-skill/SKILL.md">
<violation number="1" location=".agents/skills/neo4j-cypher-skill/SKILL.md:263">
P3: This syntax-trap entry is factually wrong: `toInteger(null)` returns `null`, it does not throw (confirmed in Neo4j's Cypher Manual for versions 5/25/current). Agents following this table may add unnecessary null guards or misdiagnose real `toInteger()` errors (which actually come from non-convertible types like lists/maps, not null input).</violation>
</file>
<file name=".agents/skills/neo4j-cypher-skill/scripts/import_neo4j_schema.py">
<violation number="1" location=".agents/skills/neo4j-cypher-skill/scripts/import_neo4j_schema.py:59">
P2: Unlike convert_standard(), convert_graphrag() doesn't validate property types through neo4j_type_to_apoc(), so an unrecognized type string in the input JSON produces an invalid/unsupported APOC type in the generated schema (e.g. "TEXT" instead of falling back to "STRING"), breaking the documented valid-types contract for downstream consumers of the schema file.</violation>
</file>
<file name=".agents/skills/neo4j-cypher-skill/README.md">
<violation number="1" location=".agents/skills/neo4j-cypher-skill/README.md:27">
P3: The `## Not covered` heading appears twice (lines ~21 and ~42) with overlapping content. The second occurrence lists GQL clauses under the heading `## Not covered` but the GQL note (`LET`, `FINISH`, `FILTER`, `INSERT` are valid Cypher 25) is actually information about what *is* covered rather than what isn't — it describes a version-gated capability of the skill. The duplicated heading makes the document harder to skim and the GQL clause note under "Not covered" is semantically misplaced.</violation>
</file>
Shadow auto-approve: would not auto-approve because issues were found.
Re-trigger cubic
| | `prop = $val`, `prop > $val`, `prop < $val`, `prop >= $val`, `prop <= $val` | **RANGE** | Numbers, dates, booleans, strings | | ||
| | `prop STARTS WITH $val` | **RANGE** | Also supported by TEXT but RANGE is faster for prefix | | ||
| | `prop CONTAINS $val`, `prop ENDS WITH $val` | **TEXT** | Uses trigram (text-2.0); RANGE does NOT support these efficiently | | ||
| | `prop IN [$a, $b]` (string list) | **TEXT** | Faster than RANGE for string list membership | |
There was a problem hiding this comment.
P2: This decision-table row is factually wrong and self-contradicts the 'trigram internals' section later in the same file: Neo4j's planner prefers RANGE over TEXT for IN list-membership checks on strings (TEXT only wins for CONTAINS/ENDS WITH). As written, this will steer agents/contributors toward creating the wrong index type for IN filters.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .agents/skills/neo4j-cypher-skill/references/indexes.md, line 34:
<comment>This decision-table row is factually wrong and self-contradicts the 'trigram internals' section later in the same file: Neo4j's planner prefers RANGE over TEXT for `IN` list-membership checks on strings (TEXT only wins for CONTAINS/ENDS WITH). As written, this will steer agents/contributors toward creating the wrong index type for `IN` filters.</comment>
<file context>
@@ -0,0 +1,349 @@
+| `prop = $val`, `prop > $val`, `prop < $val`, `prop >= $val`, `prop <= $val` | **RANGE** | Numbers, dates, booleans, strings |
+| `prop STARTS WITH $val` | **RANGE** | Also supported by TEXT but RANGE is faster for prefix |
+| `prop CONTAINS $val`, `prop ENDS WITH $val` | **TEXT** | Uses trigram (text-2.0); RANGE does NOT support these efficiently |
+| `prop IN [$a, $b]` (string list) | **TEXT** | Faster than RANGE for string list membership |
+| `prop IS NOT NULL` | **RANGE** | Existence check with range index |
+| `point.distance(n.loc, $pt) < $r`, `point.withinBBox(...)` | **POINT** | Spatial queries |
</file context>
| | `prop IN [$a, $b]` (string list) | **TEXT** | Faster than RANGE for string list membership | | |
| | `prop IN [$a, $b]` (string list) | **RANGE** | RANGE is preferred by the planner for list membership; TEXT only wins for CONTAINS/ENDS WITH | |
| | `await result.values()` | `list[list]` | One inner list per row | | ||
| | `await result.data()` | `list[dict]` | One dict per record, keyed by column name | | ||
| | `await result.single()` | `Record` | Raises if 0 or 2+ results | | ||
| | `await result.single(strict=False)` | `Record \| None` | None for 0, raises for 2+ | |
There was a problem hiding this comment.
P2: The result.single() / single(strict=False) rows in the Async Result Methods table are factually backwards: with the default strict=False, zero records return None (no raise) and multiple records emit a warning and return the first record (no raise) — raising only happens with strict=True. An agent following this table could wrongly assume single() raises on empty results and skip a None check, leading to unhandled TypeError/AttributeError downstream.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .agents/skills/neo4j-driver-python-skill/references/async.md, line 42:
<comment>The `result.single()` / `single(strict=False)` rows in the Async Result Methods table are factually backwards: with the default `strict=False`, zero records return `None` (no raise) and multiple records emit a warning and return the first record (no raise) — raising only happens with `strict=True`. An agent following this table could wrongly assume `single()` raises on empty results and skip a `None` check, leading to unhandled `TypeError`/`AttributeError` downstream.</comment>
<file context>
@@ -0,0 +1,114 @@
+| `await result.values()` | `list[list]` | One inner list per row |
+| `await result.data()` | `list[dict]` | One dict per record, keyed by column name |
+| `await result.single()` | `Record` | Raises if 0 or 2+ results |
+| `await result.single(strict=False)` | `Record \| None` | None for 0, raises for 2+ |
+| `await result.fetch(n)` | `list[Record]` | Up to n records |
+| `await result.consume()` | `ResultSummary` | Discards remaining |
</file context>
| "properties": {k: {**v, "array": False} for k, v in props.items()}, | ||
| } | ||
|
|
||
| schema["value"][to_label]["relationships"][rel] = { |
There was a problem hiding this comment.
P2: For self-relationships (from_label == to_label), the second assignment to schema["value"][to_label]["relationships"][rel] overwrites the first, so the generated schema JSON loses the 'out' direction entry for that node/relationship pair.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .agents/skills/neo4j-cypher-skill/scripts/define_schema.py, line 95:
<comment>For self-relationships (from_label == to_label), the second assignment to schema["value"][to_label]["relationships"][rel] overwrites the first, so the generated schema JSON loses the 'out' direction entry for that node/relationship pair.</comment>
<file context>
@@ -0,0 +1,123 @@
+ "properties": {k: {**v, "array": False} for k, v in props.items()},
+ }
+
+ schema["value"][to_label]["relationships"][rel] = {
+ "direction": "in",
+ "labels": [from_label],
</file context>
| | `NodeIndexSeek` | ✓ | Equality/range predicate satisfied via RANGE or LOOKUP index. Optimal. | | ||
| | `NodeUniqueIndexSeek` | ✓ | Unique constraint index hit. Optimal. | | ||
| | `NodeIndexScan` | ~ | Full scan of an index (no predicate selectivity). Faster than label scan; still linear. | | ||
| | `NodeIndexContainsScan` | ✓ | TEXT index CONTAINS/STARTS WITH. Requires TEXT index on property. | |
There was a problem hiding this comment.
P2: This reference table incorrectly states that NodeIndexContainsScan handles both CONTAINS and STARTS WITH and that STARTS WITH requires a TEXT index. In practice, STARTS WITH is planned via NodeIndexSeekByRange against a RANGE index (Neo4j docs), so this entry will mislead agents/devs debugging plans into thinking they need a TEXT index for prefix queries.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .agents/skills/neo4j-query-tuning-skill/references/plan-operators.md, line 19:
<comment>This reference table incorrectly states that `NodeIndexContainsScan` handles both CONTAINS and STARTS WITH and that STARTS WITH requires a TEXT index. In practice, STARTS WITH is planned via `NodeIndexSeekByRange` against a RANGE index (Neo4j docs), so this entry will mislead agents/devs debugging plans into thinking they need a TEXT index for prefix queries.</comment>
<file context>
@@ -0,0 +1,167 @@
+| `NodeIndexSeek` | ✓ | Equality/range predicate satisfied via RANGE or LOOKUP index. Optimal. |
+| `NodeUniqueIndexSeek` | ✓ | Unique constraint index hit. Optimal. |
+| `NodeIndexScan` | ~ | Full scan of an index (no predicate selectivity). Faster than label scan; still linear. |
+| `NodeIndexContainsScan` | ✓ | TEXT index CONTAINS/STARTS WITH. Requires TEXT index on property. |
+| `NodeIndexEndsWithScan` | ✓ | TEXT index ENDS WITH. Requires TEXT index on property. |
+| `RelationshipIndexSeek` | ✓ | Relationship property index hit. |
</file context>
| | `RETURN DISTINCT a, b` deduplicates `a` | `RETURN DISTINCT` deduplicates complete rows, not individual columns | | ||
| | `CALL IN TRANSACTIONS` inside an explicit transaction | Requires auto-commit session | | ||
| | `PERIODIC COMMIT` in LOAD CSV | Deprecated — use `LOAD CSV ... CALL (...) { } IN TRANSACTIONS OF N ROWS` | | ||
| | `toInteger(null)` throws | `toIntegerOrNull(null)` returns `null` safely | |
There was a problem hiding this comment.
P2: This row is factually incorrect: toInteger(null) returns null per Neo4j docs, it doesn't throw (it only errors on non-numeric/string/boolean types). Recommend removing this row or correcting it (e.g. contrast with toInteger('abc')/non-convertible types, which does throw).
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .agents/skills/neo4j-cypher-skill/references/syntax-traps.md, line 46:
<comment>This row is factually incorrect: `toInteger(null)` returns `null` per Neo4j docs, it doesn't throw (it only errors on non-numeric/string/boolean types). Recommend removing this row or correcting it (e.g. contrast with `toInteger('abc')`/non-convertible types, which does throw).</comment>
<file context>
@@ -0,0 +1,46 @@
+| `RETURN DISTINCT a, b` deduplicates `a` | `RETURN DISTINCT` deduplicates complete rows, not individual columns |
+| `CALL IN TRANSACTIONS` inside an explicit transaction | Requires auto-commit session |
+| `PERIODIC COMMIT` in LOAD CSV | Deprecated — use `LOAD CSV ... CALL (...) { } IN TRANSACTIONS OF N ROWS` |
+| `toInteger(null)` throws | `toIntegerOrNull(null)` returns `null` safely |
</file context>
| | `toInteger(null)` throws | `toIntegerOrNull(null)` returns `null` safely | | |
| | `toInteger('not-a-number')` throws | `toIntegerOrNull('not-a-number')` returns `null` safely | |
| ```python | ||
| # ❌ Sync driver in asyncio — blocks event loop | ||
| async def bad(): | ||
| with GraphDatabase.driver(URI, auth=AUTH) as driver: |
There was a problem hiding this comment.
P3: The sync-driver anti-pattern example uses GraphDatabase but it's never imported anywhere in this reference file, making the snippet non-self-contained if copy-pasted.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .agents/skills/neo4j-driver-python-skill/references/async.md, line 96:
<comment>The sync-driver anti-pattern example uses `GraphDatabase` but it's never imported anywhere in this reference file, making the snippet non-self-contained if copy-pasted.</comment>
<file context>
@@ -0,0 +1,114 @@
+```python
+# ❌ Sync driver in asyncio — blocks event loop
+async def bad():
+ with GraphDatabase.driver(URI, auth=AUTH) as driver:
+ records, _, _ = driver.execute_query("MATCH (p:Person) RETURN p")
+
</file context>
| "properties": {k: {**v, "array": False} for k, v in props.items()}, | ||
| } | ||
|
|
||
| schema["value"][rel] = { |
There was a problem hiding this comment.
P3: If the same relationship type is defined more than once (e.g. between different node pairs), the top-level schema["value"][rel] entry is overwritten rather than merged, losing previously captured properties.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .agents/skills/neo4j-cypher-skill/scripts/define_schema.py, line 102:
<comment>If the same relationship type is defined more than once (e.g. between different node pairs), the top-level schema["value"][rel] entry is overwritten rather than merged, losing previously captured properties.</comment>
<file context>
@@ -0,0 +1,123 @@
+ "properties": {k: {**v, "array": False} for k, v in props.items()},
+ }
+
+ schema["value"][rel] = {
+ "type": "relationship",
+ "count": 0,
</file context>
|
|
||
| try: | ||
| with GraphDatabase.driver(URI, auth=(USERNAME, PASSWORD)) as driver: | ||
| records, _, _ = driver.execute_query( |
There was a problem hiding this comment.
P3: This schema export is read-only, but execute_query defaults to write routing, so in a clustered Neo4j deployment the query would be routed to the leader instead of a follower/read replica. Add routing_="r" to route it as a read.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .agents/skills/neo4j-cypher-skill/scripts/generate_schema.py, line 45:
<comment>This schema export is read-only, but execute_query defaults to write routing, so in a clustered Neo4j deployment the query would be routed to the leader instead of a follower/read replica. Add `routing_="r"` to route it as a read.</comment>
<file context>
@@ -0,0 +1,69 @@
+
+ try:
+ with GraphDatabase.driver(URI, auth=(USERNAME, PASSWORD)) as driver:
+ records, _, _ = driver.execute_query(
+ "CALL apoc.meta.schema()", database_=name
+ )
</file context>
| | `SET n = {k:v}` partial update | `SET n += {k:v}` | | ||
| | `DELETE n` with relationships | `DETACH DELETE n` | | ||
| | `WHERE n.x = null` | `WHERE n.x IS NULL` | | ||
| | `toInteger(null)` throws | `toIntegerOrNull(null)` | |
There was a problem hiding this comment.
P3: This syntax-trap entry is factually wrong: toInteger(null) returns null, it does not throw (confirmed in Neo4j's Cypher Manual for versions 5/25/current). Agents following this table may add unnecessary null guards or misdiagnose real toInteger() errors (which actually come from non-convertible types like lists/maps, not null input).
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .agents/skills/neo4j-cypher-skill/SKILL.md, line 263:
<comment>This syntax-trap entry is factually wrong: `toInteger(null)` returns `null`, it does not throw (confirmed in Neo4j's Cypher Manual for versions 5/25/current). Agents following this table may add unnecessary null guards or misdiagnose real `toInteger()` errors (which actually come from non-convertible types like lists/maps, not null input).</comment>
<file context>
@@ -0,0 +1,400 @@
+| `SET n = {k:v}` partial update | `SET n += {k:v}` |
+| `DELETE n` with relationships | `DETACH DELETE n` |
+| `WHERE n.x = null` | `WHERE n.x IS NULL` |
+| `toInteger(null)` throws | `toIntegerOrNull(null)` |
+| `n.$key` dynamic property | `n[$key]` |
+| `SET n:$label` | `SET n:$($label)` |
</file context>
| @@ -0,0 +1,60 @@ | |||
| # neo4j-cypher-skill | |||
There was a problem hiding this comment.
P3: The ## Not covered heading appears twice (lines ~21 and ~42) with overlapping content. The second occurrence lists GQL clauses under the heading ## Not covered but the GQL note (LET, FINISH, FILTER, INSERT are valid Cypher 25) is actually information about what is covered rather than what isn't — it describes a version-gated capability of the skill. The duplicated heading makes the document harder to skim and the GQL clause note under "Not covered" is semantically misplaced.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .agents/skills/neo4j-cypher-skill/README.md, line 27:
<comment>The `## Not covered` heading appears twice (lines ~21 and ~42) with overlapping content. The second occurrence lists GQL clauses under the heading `## Not covered` but the GQL note (`LET`, `FINISH`, `FILTER`, `INSERT` are valid Cypher 25) is actually information about what *is* covered rather than what isn't — it describes a version-gated capability of the skill. The duplicated heading makes the document harder to skim and the GQL clause note under "Not covered" is semantically misplaced.</comment>
<file context>
@@ -0,0 +1,60 @@
+
+Defaults to 2025.01-safe features. Items new in 2025.x are annotated `[2025.01]` in the reference files; 2026.x items `[2026.01]`.
+
+## Not covered
+
+- Driver migration → `neo4j-migration-skill`
</file context>
There was a problem hiding this comment.
0 issues found across 1 file (changes from recent commits).
Shadow auto-approve: would not auto-approve. Auto-approval blocked by 21 unresolved issues from previous reviews.
Re-trigger cubic
The ty check step of python-lint also flags the vendored neo4j skill scripts under .agents/skills. Exclude them in [tool.ty.src] alongside python_sdk, since they are third-party skill content installed via skills-lock.json. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
0 issues found across 1 file (changes from recent commits).
Shadow auto-approve: would not auto-approve. Auto-approval blocked by 21 unresolved issues from previous reviews.
Re-trigger cubic
Fixing python-lint unblocked validate-documentation-style, which scans the whole docs tree with vale and surfaced a pre-existing error in docs/docs/development-resources/sbom.mdx (added in #9567 on stable): the possessive 'cosign's' is flagged although 'cosign'/'Cosign' are already accepted. Add the possessive form, consistent with existing entries like Infrahub's and schema's. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
0 issues found across 1 file (changes from recent commits).
Shadow auto-approve: would not auto-approve. Auto-approval blocked by 21 unresolved issues from previous reviews.
Re-trigger cubic
Summary
Vendors three Neo4j skills from
neo4j-contrib/neo4j-skillsinto the repo'sagentic tooling so contributors and agents working on Infrahub's graph layer
have first-class, in-repo guidance for writing and tuning Neo4j code — matching
the Neo4j 2025.10 / driver 6.0 stack Infrahub runs on.
Key Changes
Cypher 25 syntax, indexes, APOC, QPP patterns, schema guardrails).
execute_query,managed/async transactions, and data-type mapping.
skills-lock.jsonrecords all three with their upstream source and contenthashes, so the vendored copies stay reproducible and auditable.
Documentation Updates
None —
skills-lock.jsonis the source of truth for the installed skill set;no developer docs enumerate skills.
Test Plan
.agents/skills/plus lockfile entries.skills-lock.jsonhashes.Summary by cubic
Vendors the
neo4j-contrib/neo4j-skillsCypher, Python driver, and query-tuning skills into.agents/skills/to provide in-repo guidance aligned with Neo4j 2025.x and driver v6. No runtime code changes; CI excludes these from lint/type checks and updates Vale spelling exceptions to keep docs style checks green.New Features
neo4j-cypher-skillcovering Cypher 25 syntax/patterns, indexes/APOC, QPEs, schema guardrails, and helper scripts.neo4j-driver-python-skillfor driver v6 lifecycle,execute_query, managed/async transactions, data types, and batching.neo4j-query-tuning-skillfor reading plans, spotting bad operators, using hints, and monitoring.skills-lock.jsonwith sources and content hashes for reproducible vendoring.Refactors
.agents/skillsfrom ruff viaextend-excludeand fromtytype checks to avoid failures on third-party scripts.cosign'sto Vale spelling exceptions to fix a docs style check false-positive.Written for commit 46714f7. Summary will update on new commits.