Skip to content

feat(safegres): read PostGraphile behaviors as a declared access-path signal - #1596

Closed
pyramation wants to merge 3 commits into
mainfrom
feat/safegres-behavior-signal
Closed

feat(safegres): read PostGraphile behaviors as a declared access-path signal#1596
pyramation wants to merge 3 commits into
mainfrom
feat/safegres-behavior-signal

Conversation

@pyramation

Copy link
Copy Markdown
Contributor

Summary

paths.ts said, in its own header, that the signal it needed was "whether the generated GraphQL surface still contains the field". PostGraphile v5 behaviors are that signal, and unlike everything else X1 could reach for they are a declaration rather than a measurement — reltuples and idx_scan both read zero on the ephemeral CI database safegres grades, at exactly the moment they would have to mean something.

Read from pg_description, not from a running Graphile instance or a project's metadata tables: @behavior in a comment is the v5 convention, so this works on any Graphile database, the comment already holds the composed value, and no API needs to be up.

SignalDirection = 'read' | 'shape' | 'declared'
SignalName     += 'behavior-hidden'
PathAssessment += 'declared-hidden'      // read > declared > shape

Only an explicit denial counts. Presets grant most behaviors by default, so absence of +list is not evidence — a scanner reading silence as denial recommends dropping an index a live API is using. Concretely, deniesAll(behavior, ['list', 'connection', 'single']): every one of the three, last fragment wins (-* +list grants list), scope paths matched on their final segment (-resource:connection denies connection). A table-level -select hides every key of the table.

This is signal-only — no finding, severity or score moves. Two places had to be held still deliberately:

  • checkUnindexedForeignKeys now tests onWriteOncePointer against the config-record signal rather than the assessment, because a path that is both write-once shaped and declared hidden now reports the stronger assessment, and demote/suppress must keep meaning what they meant.
  • report.perf.paths.declaredHidden is reported in markdown/pretty and counted nowhere in the score.

behavior-hidden is also not a claim that the index is unwanted: the referential-integrity scan on a parent DELETE runs whatever the API exposes. That caveat is why the gating of X1/X7/X8 is a separate change, made after measuring this signal against constructive-db.

Design and rollout: https://github.com/constructive-io/constructive-planning/issues/1356

Tests

__tests__/behaviors.test.ts covers tag extraction (including a tag followed by prose, and prose followed by a would-be tag), fragment parsing, and the denied/granted/undeclared three-way distinction. paths-behaviors.sql + the declared surface block in perf.test.ts cover the catalog path: an explicit denial, a partial denial, a retracted denial, silence, a table-level -select, and a denial that loses to a policy-read. 198 passed.

Link to Devin session: https://app.devin.ai/sessions/ec06ef6eabae4872ae5ec3926f037c85
Requested by: @pyramation

@pyramation pyramation self-assigned this Aug 1, 2026
@devin-ai-integration

Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@devin-ai-integration

Copy link
Copy Markdown
Contributor

Superseded: the exposure-planes work in #1598/main makes this the wrong shape. Behaviors should arrive through the ExposureAdapter layer and refine relation-level reach on an api plane, rather than being hardwired PostGraphile introspection producing a single perf path-signal. Re-implementing on top of planes.

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.

1 participant