Public pull requests
Four changes, and what the structure said about them.
Every example below is a real pull request in a public repository. The diagram is the one Striff drew; the boxed text is what it produced, word for word. Under each one is the check we ran by hand against both revisions before putting it here — so you can repeat it rather than take our word for it.
A structural diff: the components a change touched, and the dependencies between them at both revisions. Additions in green, removals struck through, and the relationships that only exist on one side of the change.
Each shows something the file diff cannot: a type's dependents, a loop that closes, a module boundary crossed by two files, a sentence in a design doc checked against the code.
These four were picked from 79 public pull requests across 27 repositories. 45 of them produced no finding at all. Most pull requests do not move the architecture, and Striff is built to say so quietly.
The reviews
Four public pull requests.
Open any diagram and drag it. Open the pull request beside it and read the diff. The two should agree.
Contract change smallrye/smallrye-graphql #1424 Open PR #1423: add GraphQLResult and wrap successful result
Two accessors on a schema model class were renamed. The rename is one line in the diff. The number of components that depend on the class is not in the diff at all.
#1423: add GraphQLResult and wrap successful result
Two accessors on a schema model class were renamed. The rename is one line in the diff. The number of components that depend on the class is not in the diff at all.
What Striff produced
Wrapper removed public methods isNotEmpty() and setNotEmpty(boolean). 12 components depend on this type, and the removal may cause compilation or runtime failures in those dependents. This is a stable contract change that should be coordinated across the codebase. Suggested direction Restore the removed methods isNotEmpty() and setNotEmpty(boolean) in Wrapper and deprecate them instead, or update all 12 known dependents (e.g., AbstractHelper, DefaultMapAdapter, DataFetcherFactory, Bootstrap) to use the new isNonNull()/setNonNull(boolean) equivalents before removal.
What we checked
-
Wrapper.java, in thecommon/schema-modelmodule, declaressetNotEmpty(boolean)andisNotEmpty()at the base revision. At the head revision it declaressetNonNull(boolean)andisNonNull()instead — and neither of the two methods the check names. - Twelve files under
src/mainreferenceWrapperat both revisions — the same count the check reports. - All four dependents the suggested fix names —
AbstractHelper,DefaultMapAdapter,DataFetcherFactory,Bootstrap— referenceWrapperat the head revision. - The rename is the pull request's own work: the commit that performs it is on the branch, and the base revision is the merge base, so nothing here is an artefact of the branch being behind.
- Only two
isNotEmpty()calls survive anywhere in the repository at the head revision, and both are AssertJ string assertions in integration tests that have nothing to do with this class.
A loop closed citrusframework/citrus-simulator #370 Open PR feat: persist only failed scenarios
A listener class gained a constructor parameter so it could read a configuration flag. That one parameter is the last edge of a loop that runs through four packages.
feat: persist only failed scenarios
A listener class gained a constructor parameter so it could read a configuration flag. That one parameter is the last edge of a loop that runs through four packages.
What Striff produced
org.citrusframework.simulator.listener to org.citrusframework.simulator.config, with no prior edge in this direction. Both of these were recorded as evidence behind the diagram. Neither was raised as an item on the check — the loop was already turning, and this change adds an edge to it rather than creating it.
What we checked
- At the base revision, no file in
org.citrusframework.simulator.listenerreferencesorg.citrusframework.simulator.config. At the head revision,SimulatorStatusListenerimportsSimulatorConfigurationProperties. - The way back was already there, in production sources only, at the base revision:
SimulatorConfigurer(config) importsScenarioMapper(scenario.mapper);ScenarioMappersimportsHttpOperationScenario(http);SimulatorRestAutoConfigurationimportsSimulatorMessageListener(listener). - Those three edges plus the one this pull request adds form a closed loop: config → scenario.mapper → http → listener → config.
- Rebuilding the package graph from imports independently puts both
listenerandconfiginside one strongly connected component at the base revision, which is what the second finding says: the cycle predates the change.
Boundary crossing smallrye/smallrye-graphql #2083 Open PR Add custom scalar support to client value formatter
Two files changed. One of them is a formatting helper inside the client, and it started importing types that are declared in the server module.
Add custom scalar support to client value formatter
Two files changed. One of them is a formatting helper inside the client, and it started importing types that are declared in the server module.
What Striff produced
io.smallrye.graphql.client.impl.core.utils to io.smallrye.graphql.api, with no prior edge in this direction. This is the quiet end of the output. One evidence-only finding, nothing raised on the check, and a diagram that puts the new edge in front of you without anybody having to argue about it.
What we checked
- The pull request changes exactly two files:
ValueFormatter.javaand its test. - At the base revision the package
io.smallrye.graphql.client.impl.core.utilsholds one production file and one test file, and neither mentionsio.smallrye.graphql.api. - At the head revision
ValueFormatterimportsCustomFloatScalar,CustomIntScalarandCustomStringScalar, and branches on all three when formatting a value. - Those three types are declared under
server/api/src/main/java/io/smallrye/graphql/api/— a different Maven module from the client implementation that now reaches for them.
Documented rules spring-ai-community/spring-ai-session #33 Open PR Add runtime branch isolation to SessionMemoryAdvisor
Nineteen sentences from this repository's own docs folder became checkable rules on this pull request. All nineteen held. Six of them are below, with the sentence each one came from.
Add runtime branch isolation to SessionMemoryAdvisor
Nineteen sentences from this repository's own docs folder became checkable rules on this pull request. All nineteen held. Six of them are below, with the sentence each one came from.
Rules read out of the docs
- docs/session-management/multi-agent.md HOLDS
To apply branch isolation automatically inside
SessionMemoryAdvisor, configure theeventFilteron the builder:SessionMemoryAdvisor depends on EventFilter
SessionMemoryAdvisor.java imports EventFilter and holds one as a field
- docs/session-management/cross-session-recall.md HOLDS
The term count is capped at 20 per call (
CrossSessionRecallTools.MAX_QUERY_TERMS).CrossSessionRecallTools declares
MAX_QUERY_TERMSas a fieldCrossSessionRecallTools.java line 75, `static final int MAX_QUERY_TERMS = 20`
- docs/session-management/compaction.md HOLDS
All four strategies share a common safety rule enforced by
CompactionUtils.snapToTurnStart:CompactionUtils declares
snapToTurnStartas a methodCompactionUtils.java line 99
- docs/session-management/recall-storage.md HOLDS
Events dropped from the active window are flagged
SessionEvent.isArchived()and kept in the log.SessionEvent declares
isArchivedas a methodSessionEvent.java line 153
- docs/session-management/recall-storage.md HOLDS
Page size defaults to
EventFilter.DEFAULT_PAGE_SIZE(10) and is configurable via the builder (SessionEventTools.builder(sessionService).pageSize(20).build()).EventFilter declares
DEFAULT_PAGE_SIZEas a field, and SessionEventTools declaresbuilderas a methodEventFilter.java line 133 (`= 10`); SessionEventTools.java line 87
- docs/session-management/compaction.md HOLDS
This ensures
tokensEstimatedSavedinCompactionResultaccurately reflects the full cost of removed events, including tool-heavy turns.CompactionResult declares
tokensEstimatedSavedas a fieldCompactionResult.java line 31, a component of the record
Nineteen rules, nineteen held, nothing raised. That is the ordinary result, and a page that only showed you the other kind would be lying about how often this fires.
What we checked
- Every sentence above appears verbatim in the file it is attributed to, at the head revision, once whitespace from the Markdown line wrapping is normalised.
- Every declaration each rule asserts was read by hand in the source at the head revision; the file and line are printed beside it.
- Every quote is prose from the documentation, not a code sample lifted out of a fenced block.
- The base revision is the merge base of the pull request, so the comparison is the branch's own work and nothing else.
These are other people's repositories. Nothing here is a judgement about the code or the people who wrote it — it is a record of what a change did to the shape of a codebase, and every one of these pull requests is a perfectly reasonable change to make.