Skip to content

skill: observed topology ≠ current traffic, native verification, PagerDuty incident → service → workload - #30

Merged
safwentrabelsi merged 4 commits into
mainfrom
fix/graph-skill-verification
Sep 18, 2026
Merged

safwentrabelsi merged 4 commits into
mainfrom
fix/graph-skill-verification

Conversation

@safwentrabelsi

Copy link
Copy Markdown
Contributor

Three guidance gaps surfaced by the MCP comparison thread (bugs 2–4; bug 1, the stale EXPOSES edge, is a graph fix in commons #346 + injector + backend):

  • Observed topology is not current traffic (SKILL.md): APM edges accumulate, so max_age_hours / observedAt gate any present-tense claim; structural k8s edges are declared state, EXPOSES {ready} is readiness at the last EndpointSlice shipped. Also documents that edge props are a JSON string in Cypher (props_json), which is why r.ready reads as null.
  • Verify current-state claims with a native source (SKILL.md + query-patterns reporting discipline): graph for topology/history, native tool (kubectl, cloud CLI, APM, PagerDuty) for "right now"; cite which statement rests on which.
  • PagerDuty incident → owning service → workload (recipes.md): resolve through PAGERDUTY_SERVICE -[:RESOLVES_TO]-> workload instead of picking an incident by title; both directions, unmapped-service caveat.

get_related step in query-patterns now mentions max_age_hours. Existing package-validation assertions untouched.

🤖 Generated with Claude Code

…uty incident recipe

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@safwentrabelsi

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6392c093c1

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +355 to +357
OPTIONAL MATCH (i:PAGERDUTY_INCIDENT)-[:AFFECTS]->(s)
WHERE i.status IN ['triggered', 'acknowledged']
OPTIONAL MATCH (a:PAGERDUTY_ALERT)-[:TRIGGERED]->(i)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Filter deleted PagerDuty nodes from the live query

When PagerDuty incidents or alerts have been deleted from the current graph, their relationships remain for history, so these matches can resurrect an incident whose stored status is still triggered/acknowledged and count deleted alerts. This can produce false candidates—or let stale rows consume the 50-row limit—for the workload-to-open-incident lookup. Add :ALIVE to both PAGERDUTY_INCIDENT and PAGERDUTY_ALERT, consistent with the recipe's current-state invariant.

Useful? React with 👍 / 👎.

Comment on lines +48 to +51
- An edge is an observation with an age (`observedAt`), not live traffic: report it as
"last observed at T", and confirm any present-tense claim (current backends, current
callers, current on-call, what an incident concerns) with a native source when one is
mounted (`kubectl`, cloud CLI, APM, PagerDuty).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Scope observedAt reporting to APM edges

For structural Kubernetes relationships such as EXPOSES and CONTROLS, the skill explicitly says there is no observedAt; nevertheless, this reporting rule applies the field and “last observed at T” wording to every edge. An agent following this reference therefore cannot report those relationships as instructed and may incorrectly describe declared topology as aged traffic evidence. Scope this rule to observed APM edges and give structural edges separate declared-state wording.

Useful? React with 👍 / 👎.

…APM edges (codex)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@safwentrabelsi

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bb758c20d0

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +358 to +359
RETURN s.name AS service, i.title AS incident, i.status AS status, i.urgency AS urgency,
count(DISTINCT a) AS alerts

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve incident identity in the aggregation

When a PagerDuty service has multiple open incidents with the same title, status, and urgency, this aggregation groups those distinct incident nodes into one row and pools their alerts in count(DISTINCT a). PagerDuty titles are not unique—as the recipe itself notes—so return i.hashedID as a grouping/output field to avoid hiding concurrent or recurring incidents.

Useful? React with 👍 / 👎.

MATCH (i:RESOURCE {hashedID: '<incident hashedID>'})-[:AFFECTS]->(s:PAGERDUTY_SERVICE:ALIVE)
OPTIONAL MATCH (s)-[:RESOLVES_TO]->(w:ALIVE)
RETURN i.title AS incident, i.status AS status, s.name AS service,
collect(DISTINCT coalesce(w.namespace + '/', '') + coalesce(w.name, w.hashedID)) AS workloads

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Retain cluster identity when collecting workloads

When a PagerDuty service resolves to workloads with the same namespace/name in different clusters, collect(DISTINCT ...) collapses them into one string, so the result understates the affected workloads and cannot identify which cluster is involved. Include each workload's clusterID or hashedID in the collected value rather than deduplicating solely on namespace and name.

Useful? React with 👍 / 👎.

…pe (codex)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@safwentrabelsi

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ac8d9785e0

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

MATCH (i:RESOURCE {hashedID: '<incident hashedID>'})-[:AFFECTS]->(s:PAGERDUTY_SERVICE:ALIVE)
OPTIONAL MATCH (s)-[:RESOLVES_TO]->(w:ALIVE)
RETURN i.title AS incident, i.status AS status, s.name AS service,
collect(DISTINCT {cluster: w.clusterID, workload: coalesce(w.namespace + '/', '') + coalesce(w.name, w.hashedID), hashedID: w.hashedID}) AS workloads

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Return an empty list for unmapped services

When the PagerDuty service has no RESOLVES_TO edge, the optional match leaves w null, but the map literal itself is still non-null, so collect() returns a singleton map whose fields are null rather than the empty workloads list described below. An agent following the caveat may therefore fail to recognize an unmapped service; make the collected expression null when w IS NULL so collect() omits it.

Useful? React with 👍 / 👎.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@safwentrabelsi
safwentrabelsi merged commit 5655051 into main Sep 18, 2026
3 checks passed
@safwentrabelsi
safwentrabelsi deleted the fix/graph-skill-verification branch September 18, 2026 08:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants