Skip to content

Honesty Rules don't cover empty results, so query / path returning nothing reads as "absent from the codebase" rather than "absent from the graph" #2672

Description

@flaviohtavares

Problem

graphify/skill.md:707-713 lists five Honesty Rules. All five constrain what the agent may assert — inventing edges, hiding cohesion scores, skipping warnings. None constrain what the agent may conclude from nothing:

- Never invent an edge. If unsure, use AMBIGUOUS.
- Never skip the corpus check warning.
- Always show token cost in the report.
- Never hide cohesion scores behind symbols - show the raw number.
- Never run HTML viz on a graph with more than 5,000 nodes without warning the user.

So when graphify path "A" "B" finds no route, or query traverses to nothing, the natural report is "there is no connection between A and B" — a claim about the codebase. What was actually established is narrower: this graph holds no such path. Those differ whenever the graph is incomplete, which is often and by design:

  • code is indexed structurally by AST, prose semantically by LLM — a relationship that exists only in prose is absent from a code-only corpus, which is the common /graphify . case
  • detect skips sensitive files (detect(): sensitive-file filter silently drops any path containing 'token', 'secret', 'password'… #2106) and unsupported types
  • a chunk whose subagent failed is warned about, then omitted from the graph (skill.md Step B3)
  • the user's wording may simply not match the graph's vocabulary — the query flow already acknowledges this by expanding the question against the graph's own terms first

Every one of those makes an empty result mean "not extracted", not "not there". The user cannot distinguish them from the report.

There is a second, sharper version of the same problem: an empty result and a broken command are indistinguishable on stdout. If the traversal errors, or a lookup runs against a wrong path, the output is also nothing — and "nothing" is exactly the conclusion being tested, so a broken instrument reads as a confirmed finding.

Proposed change

Add two rules to the ## Honesty Rules list:

- Never turn an empty traversal into an absence claim. "No path found" and "no
  matching node" mean *not in this graph* — which is a claim about the
  extraction, not about the corpus. Report the scope you actually searched
  ("nothing in the graph connects X to Y; the graph covers N of M files, and
  code is indexed structurally while prose is indexed semantically") and name
  the likelier explanation: a wording mismatch against the graph's vocabulary,
  a file type that was skipped, or a genuinely absent relationship.
- Never trust an empty result from a command whose failure also prints nothing.
  When a query, traversal, or lookup returns nothing and that emptiness is the
  answer being sought, prove the instrument works first — check the exit
  status, or run it against a node you already know exists. A broken command
  and a true negative are indistinguishable on stdout, and the broken one
  reads as a confirmed finding.

The first rule is cheap to satisfy: the file counts it asks for are already in .graphify_detect.json and the report, so it is a phrasing requirement rather than new work.

Scope

## Honesty Rules is present in all 16 graphify/skill*.md variants (verified against v8 at 26128ab), and the tools/skillgen/expected/ fixtures track them.

This is a rules/wording change with no code impact, so it may be simpler to land than the companion issue — but it has the same 16-variant propagation question, which is why this is an issue and not a PR.

Verified / not verified

  • Verified: the Honesty Rules block verbatim, and its presence in all 16 variants, against v8 at 26128ab.
  • Verified: the incompleteness sources cited above are each described in skill.md itself (Step 3 AST/semantic split, Step 2 skipped_sensitive, Step B3 chunk-failure warning, the query vocab-expansion note).
  • Not verified: a reproduced case of a user acting on a false absence. The argument is that the rules permit it, not that a specific incident occurred.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions