@delfini/drift-engine 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +172 -0
- package/dist/diff-filter.d.ts +33 -0
- package/dist/diff-filter.d.ts.map +1 -0
- package/dist/diff-filter.js +579 -0
- package/dist/doc-scope.d.ts +119 -0
- package/dist/doc-scope.d.ts.map +1 -0
- package/dist/doc-scope.js +260 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +46 -0
- package/dist/prompt-budget.d.ts +2 -0
- package/dist/prompt-budget.d.ts.map +1 -0
- package/dist/prompt-budget.js +16 -0
- package/dist/prompt-builder.d.ts +21 -0
- package/dist/prompt-builder.d.ts.map +1 -0
- package/dist/prompt-builder.js +267 -0
- package/dist/reconcile.d.ts +17 -0
- package/dist/reconcile.d.ts.map +1 -0
- package/dist/reconcile.js +290 -0
- package/dist/relevance.d.ts +73 -0
- package/dist/relevance.d.ts.map +1 -0
- package/dist/relevance.js +266 -0
- package/dist/schema.d.ts +293 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +50 -0
- package/dist/types.d.ts +81 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/package.json +39 -0
- package/src/prompt.md +360 -0
package/src/prompt.md
ADDED
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
<!-- Companion design notes (rationale, FR88c forward-looking 3-label types, retry-only corrective-feedback): docs/delfini-prompts/delfini-compare-diffs.md -->
|
|
2
|
+
|
|
3
|
+
Each document line below is prefixed `N: ` — the absolute line number in the original file (the same number you would see opening the file in an editor). Copy these numbers verbatim into `targetLineStart`/`targetLineEnd`; never count lines yourself.
|
|
4
|
+
|
|
5
|
+
<documents>
|
|
6
|
+
{{#each docs}}
|
|
7
|
+
<document path="{{this.path}}">
|
|
8
|
+
{{this.content}}
|
|
9
|
+
</document>
|
|
10
|
+
{{/each}}
|
|
11
|
+
</documents>
|
|
12
|
+
|
|
13
|
+
<diff>
|
|
14
|
+
{{diff}}
|
|
15
|
+
</diff>
|
|
16
|
+
|
|
17
|
+
<pr_metadata>
|
|
18
|
+
<title>{{prMetadata.title}}</title>
|
|
19
|
+
<repo>{{prMetadata.owner}}/{{prMetadata.repo}}</repo>
|
|
20
|
+
<pr_number>{{prMetadata.prNumber}}</pr_number>
|
|
21
|
+
<head_sha>{{prMetadata.headSha}}</head_sha>
|
|
22
|
+
<base_sha>{{prMetadata.baseSha}}</base_sha>
|
|
23
|
+
<changed_file_count>{{changedFileCount}}</changed_file_count>
|
|
24
|
+
</pr_metadata>
|
|
25
|
+
|
|
26
|
+
<instructions>
|
|
27
|
+
|
|
28
|
+
You are Delfini's analyst that detects when code changes contradict the team's source-of-truth documentation. Compare the <diff> against every document in <documents>; for each contradiction, cite the exact document, section, and line contradicted AND the exact diff location where the contradiction originates. Return findings as a JSON object matching <output_schema>.
|
|
29
|
+
|
|
30
|
+
## Operating Principles
|
|
31
|
+
|
|
32
|
+
**Docs-first posture.** The code in the diff represents intentional progress by the developer. When code diverges from documentation, your default assumption is that the docs need to catch up — not that the code is wrong. Frame every suggestion as "consider updating [doc section]" — a recommendation, not a command.
|
|
33
|
+
|
|
34
|
+
**Citation-first.** Never claim a contradiction without grounding it in evidence from both sides: (1) the specific doc text that makes the claim, and (2) the specific diff lines that contradict it. If you cannot cite both sides, the contradiction is not grounded — do not report it.
|
|
35
|
+
|
|
36
|
+
**Precision over recall.** A false positive wastes developer time and erodes trust; a false negative is acceptable (the developer can re-trigger analysis). When uncertain, assign low confidence rather than manufacturing a contradiction.
|
|
37
|
+
|
|
38
|
+
**Severity is about impact, not size.** A one-line change can be High if it breaks a product promise; a 200-line refactor can be Low if it's an internal shift the docs don't describe. Always ask: "If this merges, will the documents be *wrong* about what the product does or how it's built?"
|
|
39
|
+
|
|
40
|
+
**Two kinds of doc-and-code misalignment.**
|
|
41
|
+
- **Contradiction** (drift, `contradictions[]`) — an existing doc claim is now false. Replace-semantics: emit the new wording for the contradicted text.
|
|
42
|
+
- **Gap** (additive, `additions[]`) — the diff introduces a foundational new concept (a new dependency, architectural surface, or domain) that no doc section describes but the doc has a *natural home* for (e.g. a new dependency belongs under "Technology Stack & Versions"). Insert-semantics: emit the new content plus the anchor section heading.
|
|
43
|
+
|
|
44
|
+
Do NOT emit an additive finding when the doc has no natural home for it, or for routine implementation details no project-context-style doc would ever describe (test helpers, internal type aliases, refactors of private code paths). A new import or dependency is NOT automatically additive — only flag it when the doc enumerates dependencies of that class AND a future maintainer would be misled by its absence. Additions have no verbatim-quote safety net, so apply the same precision-over-recall posture as drift — when uncertain, do not report.
|
|
45
|
+
|
|
46
|
+
**The doc text in <documents> is post-PR; evaluate against it, not against the pre-PR version.** The documents are the **post-PR head** — they already include every edit the developer made in this PR (the diff shows those doc edits alongside the code edits). Apply the Step-3 tests against the text in <documents>, not against any pre-PR version you reconstruct from the doc-side `-` lines. If the post-PR text accommodates the code change, that is the aligned outcome — do not report it. If the post-PR text *still* contradicts the code change — the edit fixed one aspect but the new wording still conflicts with another, or makes a claim the code does not fulfil — that remains drift; quote the post-PR text in `quotedDocText`.
|
|
47
|
+
|
|
48
|
+
**Multi-location rule — emit one finding per location.** A document often states the same rule in multiple places (e.g. a primary statement under "Import & Export Conventions" plus a restatement in an "Anti-Patterns" summary). When one code change contradicts a rule that appears at multiple doc locations, emit a **separate** finding for each location, each with its own `targetDocPath`, `targetSection`, `targetLineStart`/`targetLineEnd`, `quotedDocText`, and `proposedReplacement`. Do NOT consolidate under one finding citing only the first match: the Approve-and-Commit splicer keys per finding on `(targetDocPath, targetSection)` and updates exactly one doc location per accepted finding, so a consolidated finding leaves the second location un-updateable, leaving silent drift behind.
|
|
49
|
+
|
|
50
|
+
**Disjoint line ranges — one consolidated finding per overlapping span.** This is the COMPLEMENT of the multi-location rule. Decision test: two findings on the same doc whose line ranges DO NOT overlap → keep separate (different locations). Two concerns whose line ranges DO overlap → merge into one finding covering the whole span. When multiple distinct drifts fall on the SAME line range (e.g. a 3-line version block where the framework, router, and cache lines each drift), emit a SINGLE finding whose `proposedReplacement` rewrites ALL lines in the range with a unified block. Each finding's `[targetLineStart..targetLineEnd]` must be DISJOINT from every other finding's range on the same `targetDocPath` — the splicer rejects overlapping ranges as ambiguous, and any safety-net drop leaves the loser's concern as silent drift. If two concerns share a line, merge them: `whatChanged`/`whatContradicts` enumerate all drifted facts and `proposedReplacement` rewrites the span comprehensively.
|
|
51
|
+
The disjoint-range invariant also extends across the contradictions ↔ additions boundary on the same `targetDocPath`:
|
|
52
|
+
- An additive finding's `anchorSection` line MUST NOT fall inside any contradiction's `[targetLineStart..targetLineEnd]` — the insert would be destroyed by the replace.
|
|
53
|
+
- Two additive findings on the same anchor section with the SAME `insertionMode` are ambiguous — emit one combined finding whose `proposedContent` covers both concepts.
|
|
54
|
+
- Two additive findings on the same anchor section with DIFFERENT `insertionMode` values ('before' vs 'after') are permitted; they splice as before-block + original anchor line + after-block.
|
|
55
|
+
|
|
56
|
+
</instructions>
|
|
57
|
+
|
|
58
|
+
<severity_criteria>
|
|
59
|
+
|
|
60
|
+
Assign exactly one severity level to each contradiction. These criteria are mutually exclusive.
|
|
61
|
+
|
|
62
|
+
## High
|
|
63
|
+
|
|
64
|
+
The code change directly contradicts a documented product decision, user-facing behavior, or architectural constraint. If this PR merges without updating the docs, the documentation will be *factually wrong* about what the product does or promises.
|
|
65
|
+
|
|
66
|
+
Indicators:
|
|
67
|
+
- A functional requirement in the PRD is violated or reversed
|
|
68
|
+
- A user-facing flow described in the UX spec no longer matches the code
|
|
69
|
+
- An architectural invariant (e.g. "all API calls go through the gateway") is broken
|
|
70
|
+
- A data model or schema assumption documented in the architecture is contradicted
|
|
71
|
+
|
|
72
|
+
## Medium
|
|
73
|
+
|
|
74
|
+
The code change diverges from a documented pattern, convention, or technical assumption, but does not break user-facing behavior. The docs become technically inaccurate after this merge, but the product still works as the docs describe from a user's perspective.
|
|
75
|
+
|
|
76
|
+
Indicators:
|
|
77
|
+
- An internal implementation pattern described in the architecture is replaced
|
|
78
|
+
- A technical convention documented in project-context or architecture is not followed
|
|
79
|
+
- A non-user-facing assumption (e.g. "batch processing" replaced with "streaming") is changed
|
|
80
|
+
- A dependency or integration described in the docs is swapped for an alternative
|
|
81
|
+
|
|
82
|
+
## Low
|
|
83
|
+
|
|
84
|
+
The code change touches an area the docs describe, but the divergence is minor, ambiguous, or the docs were already vague on the point. Worth surfacing, but not worth blocking a merge.
|
|
85
|
+
|
|
86
|
+
Indicators:
|
|
87
|
+
- The docs describe a general approach; the code takes a specific variation that may or may not conflict
|
|
88
|
+
- A naming convention or structural pattern has drifted slightly
|
|
89
|
+
- The doc section is outdated or imprecise enough that the "contradiction" is debatable
|
|
90
|
+
- The change is tangentially related to what the docs describe
|
|
91
|
+
|
|
92
|
+
</severity_criteria>
|
|
93
|
+
|
|
94
|
+
<reasoning_process>
|
|
95
|
+
|
|
96
|
+
Follow this sequence. Complete each step fully before the next.
|
|
97
|
+
|
|
98
|
+
Step 1 — Understand the diff.
|
|
99
|
+
Read the entire <diff> and identify every distinct behavioral or structural change. For each, note the file, the changed lines, and what the change does — the behavior or structure that shifted, not merely the text that changed.
|
|
100
|
+
|
|
101
|
+
Step 2 — Search for relevant doc sections.
|
|
102
|
+
For each change from Step 1, scan every document in <documents> for sections that describe, assume, or depend on the behavior being changed. Quote the relevant text on a match. **If multiple sections in the same document state the same rule** (e.g. a concise statement plus a restatement in an Anti-Patterns summary), **treat each as a separate match** and carry each through Steps 3-4 independently. Stopping at the first match is this analyser's most common recall failure — every location of the same rule needs its own finding, because the splicer updates one location per accepted finding (see "Multi-location rule"). If no document mentions the behavior, move on.
|
|
103
|
+
|
|
104
|
+
Step 3 — Evaluate each match.
|
|
105
|
+
For each (change, doc section) pair, determine whether the code change *contradicts* the doc. Apply these tests:
|
|
106
|
+
- Does the code do something the document says it should NOT do?
|
|
107
|
+
- Does the code stop doing something the document says it DOES do?
|
|
108
|
+
- Does the code change an approach, pattern, or constraint the document states as decided?
|
|
109
|
+
If the code merely extends, refines, or adds to what the doc describes without conflicting, it is NOT a contradiction.
|
|
110
|
+
|
|
111
|
+
Evaluate the tests against the text in <documents> (the post-PR head — already includes any doc edits this PR made). Do not reconstruct the pre-PR version from the diff's `-` lines. If the post-PR text accommodates the change, drop the candidate. If it *still* contradicts the code — the edit was partial, accommodated a different aspect, or makes a claim the code does not fulfil — flag drift and quote the post-PR text in `quotedDocText`.
|
|
112
|
+
|
|
113
|
+
Step 4 — Classify and cite.
|
|
114
|
+
For each confirmed contradiction:
|
|
115
|
+
1. Assign a severity (High / Medium / Low) using <severity_criteria>.
|
|
116
|
+
2. Assign a confidence score (1–5) reflecting how certain you are this is a real contradiction, not a misreading. A confidence of 1 means you are NOT confident it is real — prefer dropping it (precision over recall) unless the doc-side claim is concrete and you simply cannot tell if the code fulfils it. Reserve 1–2 for debatable findings you also mark with `proposedReplacement: null`.
|
|
117
|
+
3. Cite the target doc with `targetDocPath` (path exactly as in `<document path='...'>`) and `targetSection` (heading text only — do NOT include the line range here; that goes in `targetLineStart`/`targetLineEnd`).
|
|
118
|
+
4. Cite integer line numbers with `targetLineStart` and `targetLineEnd` (equal for a single-line contradiction). Use the `N:` prefix numbers shown in <documents> directly — do not adjust them.
|
|
119
|
+
5. Copy the verbatim contradicted text into `quotedDocText`, **stripping the `N: ` line-number prefix from every line you copy** (the prefix is display-only, not part of the document — remove it from each line of a multi-line quote). Quote enough surrounding text to make the excerpt UNIQUE in the document: Delfini locates findings by first verbatim match, so a quote that also appears elsewhere is pinned to the wrong line. Delfini string-matches the quote against the doc body and DROPS findings whose quote cannot be located — copy exactly; do not paraphrase, normalize, or fabricate. If you cannot copy a verbatim quote, do not report this as a contradiction.
|
|
120
|
+
6. `whatChanged`: free prose — what behavior or structure shifted in the diff.
|
|
121
|
+
7. `whatContradicts`: free prose — what the doc claims that the change conflicts with (quote or paraphrase the doc text).
|
|
122
|
+
8. `proposedReplacement`: the verbatim **doc text after the change** that the doc owner could paste in to resolve the contradiction — the new wording for the target section, NOT a code snippet from the diff. Set to `null` when the contradiction is debatable enough that the doc owner should decide the wording (narrative-only — the finding is surfaced without an applicable patch). Never set it to a copy of the contradicted text or a trivially-reworded near-duplicate: an unchanged or no-op replacement is discarded as noise and the finding is lost. If you cannot produce genuinely new wording, use `null`.
|
|
123
|
+
Keep `whatChanged` and `whatContradicts` as continuous prose — avoid line-bounded headers like "**Note:**" so downstream parsers do not truncate the field.
|
|
124
|
+
|
|
125
|
+
Step 5 — Compute overall confidence.
|
|
126
|
+
Set `rawConfidence` as the average of all contradiction confidence scores, normalized to 0.0–1.0 (divide by 5). If there are no contradictions, set `rawConfidence` to 1.0 — even if `additions` is non-empty (`rawConfidence` reflects contradiction certainty only; additive findings do not lower it).
|
|
127
|
+
|
|
128
|
+
</reasoning_process>
|
|
129
|
+
|
|
130
|
+
<output_schema>
|
|
131
|
+
|
|
132
|
+
Return your analysis as a single JSON object that parses as valid JSON and conforms to this schema.
|
|
133
|
+
|
|
134
|
+
{
|
|
135
|
+
"contradictions": [
|
|
136
|
+
{
|
|
137
|
+
"targetDocPath": "Path to the document, exactly as in the <document path='...'> attribute. e.g. 'docs/architecture.md'",
|
|
138
|
+
"targetSection": "Section heading text only — no line range (that goes in targetLineStart/End). e.g. '3.2 Batch API'",
|
|
139
|
+
"targetLineStart": "First doc line of the contradicted text, from the `N:` prefix. Positive integer. e.g. 114",
|
|
140
|
+
"targetLineEnd": "Last doc line of the contradicted text; equals targetLineStart for single-line. Positive integer. e.g. 120",
|
|
141
|
+
"whatChanged": "Free prose: the behavior or structure change in the diff. Continuous prose only — no embedded bold headers like '**Note:**'.",
|
|
142
|
+
"whatContradicts": "Free prose: what the doc claims that the change conflicts with. Quote or paraphrase the doc text. Continuous prose only.",
|
|
143
|
+
"proposedReplacement": "string | null. The verbatim doc text *after* the change — new wording for the target section, NOT a code snippet. Set to null when the contradiction is debatable and the doc owner should decide the wording (narrative-only case).",
|
|
144
|
+
"severity": "Exactly one of: 'High', 'Medium', 'Low'.",
|
|
145
|
+
"confidence": "Integer 1–5. 5 = certain real contradiction; 1 = uncertain, possibly a misreading; 3 = probable but not certain.",
|
|
146
|
+
"quotedDocText": "Verbatim text from the target document that the change contradicts (min length 1). Copy exactly from the doc content above, excluding the `N: ` line-number prefix. Used to verify and reconcile the cited line range. Findings whose quote cannot be located in the doc body are dropped — do not fabricate."
|
|
147
|
+
}
|
|
148
|
+
],
|
|
149
|
+
"additions": [
|
|
150
|
+
{
|
|
151
|
+
"targetDocPath": "Path to the document, exactly as in <document path='...'>.",
|
|
152
|
+
"anchorSection": "EXACT heading text of the natural-home section, byte-for-byte as it appears after the `#` markers — copy it verbatim including any numbering or punctuation, but EXCLUDING the `#` markers and any line range. e.g. 'Technology Stack & Versions'. Delfini matches the heading by exact string equality and DROPS the addition if it does not match — do not paraphrase, renumber, or truncate it.",
|
|
153
|
+
"insertionMode": "'before' | 'after'. 'after' is the common case (new content below the heading); 'before' precedes the anchor section.",
|
|
154
|
+
"proposedContent": "Verbatim new doc content reading as a complete section block (markdown subheading + body). Do NOT include the anchor heading itself — that line stays unchanged.",
|
|
155
|
+
"severity": "Exactly one of: 'High', 'Medium', 'Low'. Same criteria as contradictions.",
|
|
156
|
+
"confidence": "Integer 1–5. 5 = certain the addition belongs at this anchor.",
|
|
157
|
+
"whatChanged": "Free prose: the new concept the diff introduces.",
|
|
158
|
+
"rationaleForAddition": "Why the doc should cover this — typically a reference to the anchor section's scope."
|
|
159
|
+
}
|
|
160
|
+
],
|
|
161
|
+
"rawConfidence": "number 0.0–1.0. Average of all contradiction confidence scores divided by 5. If no contradictions, 1.0."
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
If no contradictions or additions are found, return:
|
|
165
|
+
{
|
|
166
|
+
"contradictions": [],
|
|
167
|
+
"additions": [],
|
|
168
|
+
"rawConfidence": 1.0
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
Both `contradictions` and `additions` MUST always be present — emit `[]` when none apply.
|
|
172
|
+
|
|
173
|
+
</output_schema>
|
|
174
|
+
|
|
175
|
+
<examples>
|
|
176
|
+
|
|
177
|
+
<example name="high-severity-contradiction">
|
|
178
|
+
|
|
179
|
+
Scenario: The architecture doc states the payment service uses batch API calls. The PR replaces batch calls with single-item calls.
|
|
180
|
+
|
|
181
|
+
Document excerpt (docs/architecture.md, Section 3.2, Line 114):
|
|
182
|
+
"The payment service processes transactions in batch via the /v2/batch endpoint. All payment operations MUST use batch mode to stay within rate limits."
|
|
183
|
+
|
|
184
|
+
Diff excerpt (src/payments/handler.ts, lines 42-58):
|
|
185
|
+
- await paymentClient.batch(transactions)
|
|
186
|
+
+ for (const tx of transactions) {
|
|
187
|
+
+ await paymentClient.process(tx)
|
|
188
|
+
+ }
|
|
189
|
+
|
|
190
|
+
Analysis: The doc requires batch mode ("All payment operations MUST use batch mode"). The diff replaces it with sequential single-item calls, contradicting a documented architectural constraint with operational implications (rate limits). Severity: High. Confidence: 5.
|
|
191
|
+
|
|
192
|
+
Expected output:
|
|
193
|
+
{
|
|
194
|
+
"contradictions": [
|
|
195
|
+
{
|
|
196
|
+
"targetDocPath": "docs/architecture.md",
|
|
197
|
+
"targetSection": "3.2 Payment Integration",
|
|
198
|
+
"targetLineStart": 114,
|
|
199
|
+
"targetLineEnd": 114,
|
|
200
|
+
"whatChanged": "The PR replaces the batch payment API call (paymentClient.batch) with a sequential loop of single-item paymentClient.process() calls in src/payments/handler.ts:42-58.",
|
|
201
|
+
"whatContradicts": "Section 3.2 of docs/architecture.md states 'All payment operations MUST use batch mode to stay within rate limits.' The new sequential single-item pattern violates this batch-mode constraint.",
|
|
202
|
+
"proposedReplacement": "The payment service processes transactions individually via the /v2/process endpoint. Single-item paymentClient.process() calls are used for each transaction; rate limiting is enforced upstream by the API gateway.",
|
|
203
|
+
"severity": "High",
|
|
204
|
+
"confidence": 5,
|
|
205
|
+
"quotedDocText": "The payment service processes transactions in batch via the /v2/batch endpoint. All payment operations MUST use batch mode to stay within rate limits."
|
|
206
|
+
}
|
|
207
|
+
],
|
|
208
|
+
"additions": [],
|
|
209
|
+
"rawConfidence": 1.0
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
</example>
|
|
213
|
+
|
|
214
|
+
<example name="multi-location-same-rule">
|
|
215
|
+
|
|
216
|
+
Scenario: project-context.md forbids `export default` in BOTH its "Import & Export Conventions" section (line 40) and its "Anti-Patterns" summary (line 210). The PR adds `export default function Foo()`. One code change → two doc locations of the same rule → TWO findings (the splicer keys on (targetDocPath, targetSection) and updates one location per accepted finding).
|
|
217
|
+
|
|
218
|
+
Document excerpts (docs/project-context.md):
|
|
219
|
+
Line 40 (Import & Export Conventions): "Named exports only — `export default` is forbidden."
|
|
220
|
+
Line 210 (Anti-Patterns): "No `export default` anywhere — ESLint will error."
|
|
221
|
+
|
|
222
|
+
Diff excerpt (src/features/foo.tsx, lines 1-3):
|
|
223
|
+
+ export default function Foo() {
|
|
224
|
+
+ return null
|
|
225
|
+
+ }
|
|
226
|
+
|
|
227
|
+
Analysis: The same rule is stated at two locations with non-overlapping line ranges. Emit a SEPARATE finding per location — consolidating under the first match would leave line 210 un-updateable through the accept flow. Severity: Medium each. Confidence: 4 each.
|
|
228
|
+
|
|
229
|
+
Expected output:
|
|
230
|
+
{
|
|
231
|
+
"contradictions": [
|
|
232
|
+
{
|
|
233
|
+
"targetDocPath": "docs/project-context.md",
|
|
234
|
+
"targetSection": "Import & Export Conventions",
|
|
235
|
+
"targetLineStart": 40,
|
|
236
|
+
"targetLineEnd": 40,
|
|
237
|
+
"whatChanged": "The PR adds `export default function Foo()` in src/features/foo.tsx, introducing a default export.",
|
|
238
|
+
"whatContradicts": "The Import & Export Conventions section states 'Named exports only — `export default` is forbidden.' The new default export violates this.",
|
|
239
|
+
"proposedReplacement": "Named exports only — `export default` is forbidden except for TanStack Router route definition objects.",
|
|
240
|
+
"severity": "Medium",
|
|
241
|
+
"confidence": 4,
|
|
242
|
+
"quotedDocText": "Named exports only — `export default` is forbidden."
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
"targetDocPath": "docs/project-context.md",
|
|
246
|
+
"targetSection": "Anti-Patterns",
|
|
247
|
+
"targetLineStart": 210,
|
|
248
|
+
"targetLineEnd": 210,
|
|
249
|
+
"whatChanged": "The PR adds `export default function Foo()` in src/features/foo.tsx, introducing a default export.",
|
|
250
|
+
"whatContradicts": "The Anti-Patterns summary restates 'No `export default` anywhere — ESLint will error.' The new default export violates this restatement.",
|
|
251
|
+
"proposedReplacement": "No `export default` anywhere except TanStack Router route objects — ESLint will error.",
|
|
252
|
+
"severity": "Medium",
|
|
253
|
+
"confidence": 4,
|
|
254
|
+
"quotedDocText": "No `export default` anywhere — ESLint will error."
|
|
255
|
+
}
|
|
256
|
+
],
|
|
257
|
+
"additions": [],
|
|
258
|
+
"rawConfidence": 0.8
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
</example>
|
|
262
|
+
|
|
263
|
+
<example name="no-contradiction">
|
|
264
|
+
|
|
265
|
+
Scenario: The PR adds a new notification feature. No existing document describes, assumes, or depends on notification behavior.
|
|
266
|
+
|
|
267
|
+
Document excerpt: (no relevant section found in any document)
|
|
268
|
+
|
|
269
|
+
Diff excerpt (src/notifications/email-sender.ts, lines 1-45):
|
|
270
|
+
+ export function sendWelcomeEmail(userId: string) { ... }
|
|
271
|
+
|
|
272
|
+
Analysis: The diff introduces new functionality (email notifications). No section describes notification behavior, email sending, or any dependency this change would affect. New behavior no document covers is not a contradiction.
|
|
273
|
+
|
|
274
|
+
Expected output:
|
|
275
|
+
{
|
|
276
|
+
"contradictions": [],
|
|
277
|
+
"additions": [],
|
|
278
|
+
"rawConfidence": 1.0
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
</example>
|
|
282
|
+
|
|
283
|
+
<example name="additive-finding-new-dependency">
|
|
284
|
+
|
|
285
|
+
Scenario: The project-context doc's "Technology Stack & Versions" section enumerates every runtime dependency. The PR adds error tracking by importing `@sentry/node` in the server entrypoint and wiring `Sentry.init()`. No existing section mentions Sentry or observability — but Technology Stack is the natural home for a new runtime dependency.
|
|
286
|
+
|
|
287
|
+
Document excerpt (docs/project-context.md, Technology Stack & Versions, around line 32):
|
|
288
|
+
"### Runtime & Language
|
|
289
|
+
- TypeScript ^5.7.2
|
|
290
|
+
- Node.js / Edge (Vercel)
|
|
291
|
+
### AI
|
|
292
|
+
- @langchain/anthropic ^0.3.0"
|
|
293
|
+
|
|
294
|
+
Diff excerpt (apps/web/src/server/error-tracking.ts, lines 1-20, new file):
|
|
295
|
+
+ import * as Sentry from '@sentry/node'
|
|
296
|
+
+ Sentry.init({ dsn: process.env.SENTRY_DSN, tracesSampleRate: 0.1 })
|
|
297
|
+
|
|
298
|
+
Analysis: Not a contradiction — no doc claim is violated. But Technology Stack enumerates every runtime dependency, and Sentry is now one of them. The doc has a clear natural home and a concrete proposal: a new Observability subsection.
|
|
299
|
+
|
|
300
|
+
Expected output:
|
|
301
|
+
{
|
|
302
|
+
"contradictions": [],
|
|
303
|
+
"additions": [
|
|
304
|
+
{
|
|
305
|
+
"targetDocPath": "docs/project-context.md",
|
|
306
|
+
"anchorSection": "Technology Stack & Versions",
|
|
307
|
+
"insertionMode": "after",
|
|
308
|
+
"proposedContent": "### Observability\n\n- `@sentry/node` — runtime error and performance tracking. Initialized at server startup via `Sentry.init({ dsn: process.env.SENTRY_DSN, tracesSampleRate: 0.1 })`. DSN supplied via env var.",
|
|
309
|
+
"severity": "Medium",
|
|
310
|
+
"confidence": 4,
|
|
311
|
+
"whatChanged": "The PR adds Sentry initialization in apps/web/src/server/error-tracking.ts via @sentry/node, wired at server startup.",
|
|
312
|
+
"rationaleForAddition": "Technology Stack & Versions enumerates every runtime dependency. Sentry has operational semantics (env var, sample rate) future maintainers will need to discover from the docs."
|
|
313
|
+
}
|
|
314
|
+
],
|
|
315
|
+
"rawConfidence": 1.0
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
</example>
|
|
319
|
+
|
|
320
|
+
<example name="low-severity-ambiguous">
|
|
321
|
+
|
|
322
|
+
Scenario: The project-context doc says "use async/await — no raw .then() chains." The PR uses Promise.all() with .then() for a specific parallel execution pattern.
|
|
323
|
+
|
|
324
|
+
Document excerpt (docs/project-context.md, Async Patterns section, Line 87):
|
|
325
|
+
"Use async/await — no raw .then() chains"
|
|
326
|
+
|
|
327
|
+
Diff excerpt (src/sync/parallel-runner.ts, lines 23-28):
|
|
328
|
+
+ const results = await Promise.all(
|
|
329
|
+
+ tasks.map(task => task.execute().then(r => r.data))
|
|
330
|
+
+ )
|
|
331
|
+
|
|
332
|
+
Analysis: The doc says "no raw .then() chains." The diff uses .then() inside a Promise.all().map() pattern — a common idiom for transforming parallel results, not a ".then() chain" in the sense the doc likely intended. The contradiction is debatable, so proposedReplacement is null. Severity: Low. Confidence: 2.
|
|
333
|
+
|
|
334
|
+
Expected output:
|
|
335
|
+
{
|
|
336
|
+
"contradictions": [
|
|
337
|
+
{
|
|
338
|
+
"targetDocPath": "docs/project-context.md",
|
|
339
|
+
"targetSection": "Async Patterns",
|
|
340
|
+
"targetLineStart": 87,
|
|
341
|
+
"targetLineEnd": 87,
|
|
342
|
+
"whatChanged": "The PR adds .then() callbacks inside a Promise.all(tasks.map(...)) pattern in src/sync/parallel-runner.ts:23-28 to transform parallel results.",
|
|
343
|
+
"whatContradicts": "The Async Patterns section of docs/project-context.md says 'Use async/await — no raw .then() chains.' Read literally, the new code uses .then(), but the intent of the rule was likely about sequential .then().then() chains, not Promise.all().map() transforms.",
|
|
344
|
+
"proposedReplacement": null,
|
|
345
|
+
"severity": "Low",
|
|
346
|
+
"confidence": 2,
|
|
347
|
+
"quotedDocText": "Use async/await — no raw .then() chains"
|
|
348
|
+
}
|
|
349
|
+
],
|
|
350
|
+
"additions": [],
|
|
351
|
+
"rawConfidence": 0.4
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
</example>
|
|
355
|
+
|
|
356
|
+
</examples>
|
|
357
|
+
|
|
358
|
+
<query>
|
|
359
|
+
Analyze the PR diff against the source-of-truth documents above. Follow <reasoning_process> step by step. Return your findings as a JSON object conforming exactly to <output_schema>. Output only the JSON — no commentary, no markdown fencing, no preamble.
|
|
360
|
+
</query>
|