Skip to content

docs(bugs): answering pipeline bug reports — Aug 2026 (Telegram user issues) - #429

Open
Fork-You-Later wants to merge 6 commits into
Natively-AI-assistant:mainfrom
Fork-You-Later:docs/pipeline-bug-reports-aug2026
Open

docs(bugs): answering pipeline bug reports — Aug 2026 (Telegram user issues)#429
Fork-You-Later wants to merge 6 commits into
Natively-AI-assistant:mainfrom
Fork-You-Later:docs/pipeline-bug-reports-aug2026

Conversation

@Fork-You-Later

Copy link
Copy Markdown

Summary

This PR documents three bugs in the answering pipeline surfaced by Telegram user reports in v2.8.5. No source-code changes — docs only, for dev review and targeted fix.

Related to: general-question refusals, screenshot code-generation failure, skill injection being ignored.


Bug 001 — Document-Grounded Refusal on General Questions 🔴

Root cause: documentGroundedCustomModeActive is read directly from the mode object without checking hasReferenceFiles. Modes seeded with reference_files_primary authority fire the doc-grounded routing even when no files are uploaded, causing the WTA gate to fail against an empty docContextBlock and return a refusal message.

Key finding: documentGroundedFromContract() in modeSourceContract.ts already implements the correct if (!hasReferenceFiles) return false guard — it has zero call-sites.

Fix surface: AnswerPlanner.ts line 2335 (guard condition) + contextRoute.ts line 76 (secondary site).


Bug 002 — Screenshot Attached but Code Not Generated 🟠

Root cause: hasScreenContext is omitted from buildV3Prompt() in the gemini-chat-stream IPC handler, and IntelligenceEngine.ts only checks options.screenContext (the OCR object from the periodic capture service) — not imagePaths. Manually-attached screenshots never set options.screenContext, so hasScreenContext is always false in manual chat.

Fix surface: Single-line addition in ipcHandlers.ts + one-line guard expansion in IntelligenceEngine.ts.


Bug 003 — Skill Injection Ignored in V3 Engine 🟠

Root cause: WhatToAnswerLLM.ts composes the system prompt as _v3p?.system ?? finalPromptOverride. When V3 is active, _v3p is always defined (non-nullish), so finalPromptOverride — which carries activeSkill.promptBlock — is silently discarded. Skills appear to have no effect in v2.8.5.

Fix surface: 3-line change in WhatToAnswerLLM.ts — append skill block after V3 system prompt when activeSkill is present.


Files in this PR

File Purpose
docs/bugs/README.md Index + blast-radius summary
docs/bugs/bug-001-document-grounded-refusal.md Full root cause, diffs, checklist
docs/bugs/bug-002-screenshot-code-generation.md Full root cause, diffs, checklist
docs/bugs/bug-003-skill-injection-failure.md Full root cause, diffs, checklist

Blast Radius (jcodemunch)

documentGroundedCustomModeActive fans into 26 dependent files — all with has_test_reach: false. Each bug doc includes a checklist of tests to add/update before merging the actual code fix.

…ative similarity

Add two focused review documents covering findings from an analysis of the
answering pipeline in IntelligenceEngine.ts and the speculative pre-fetch
similarity gate:

- intelligence-engine-architecture-critique.md
  * runWhatShouldISay is ~2,400 lines — extract into WtaPipeline stages
  * Post-stream repair cascade: up to 3 sequential blocking LLM regen calls
    after the answer already streamed to the UI (worst case +14s latency)
  * Disabled answer relevance guard (Phase 8) still runs NLI on every answer
  * AnswerPlanner.ts at 211KB — needs module split
  * Manual/WTA post-processing duplicated between IntelligenceEngine and ipcHandlers
  * Regex-based LLM failure detection growing per provider/version
  * Dynamic require() in hot path with fail-open catch
  * Inconsistent sanitization limits copy-pasted at 3 repair sites

- speculative-similarity-jaccard-critique.md
  * Jaccard token-overlap inflated by stop words on interview questions
  * Antonym pairs (strengths/weaknesses, success/failure) score ~0.75 — false accept
  * Recommended fix: hybrid Jaccard fast-exit + SBERT (all-MiniLM-L6-v2, 22MB)
    in the ambiguous zone, reusing IntentClassifier.ts worker infrastructure
  * Implementation sketch and threshold calibration guidance included

No production code was changed. These are read-only findings for implementing devs.
- Correct Jaccard scores: succeeded/failed scores ~0.77 (false accept under 0.75 threshold), strengths/weaknesses scores ~0.72 (close to threshold)
- Keep raw SBERT cosine scale consistent rather than normalizing it to [0,1] which would let a raw score of 0.31 pass the 0.65 threshold
- Reference LocalEmbeddingProvider instead of IntentClassifier for retrieving SBERT sentence embeddings
Add replace-placeholder-corruption-bug.md detailing a critical issue where
untrusted model code/math containing $ symbols (like bash $1, jQuery $&,
or math $x_1) get corrupted during post-processing token restoration due
to unsafe usage of JS String.prototype.replace() with string replacements.
Update the index README and severity summary table accordingly.
Update speculativeSimilarity to return an explicit accepted boolean, resolving the contradiction where the caller compared the raw SBERT cosine score against the 0.75 Jaccard threshold (which would reject valid SBERT matches in the 0.65-0.75 range).
Three user-reported bugs documented with root cause analysis,
proposed fixes, and jcodemunch blast-radius assessments:

- Bug 001: documentGroundedCustomModeActive fires without hasReferenceFiles
  causing general-question refusals in doc-grounded modes
- Bug 002: hasScreenContext not propagated from imagePaths in manual chat,
  breaking code generation from attached screenshots
- Bug 003: V3 engine nullish-coalesces activeSkill away,
  silently dropping skill injection in v2.8.5+

documentGroundedFromContract() already has the correct guard but
has zero call-sites -- recommend wiring it in for Bug 001.
@greptile-apps

greptile-apps Bot commented Aug 5, 2026

Copy link
Copy Markdown

Greptile Summary

This documentation-only PR records three answering-pipeline bugs and several architecture and correctness concerns, together with proposed fixes and regression-test checklists.

  • Documents document-grounding refusal, screenshot-context propagation, and V3 skill-injection failures.
  • Adds architecture notes covering answer-pipeline complexity, speculative reuse, and placeholder restoration.

Confidence Score: 5/5

The documentation-only PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
docs/bugs/bug-001-document-grounded-refusal.md Documents the document-grounding failure and now safely guards the optional source contract in its preferred patch.
docs/bugs/bug-002-screenshot-code-generation.md Documents missing manual-screenshot context propagation and proposes updates for both answering entry points.
docs/bugs/bug-003-skill-injection-failure.md Documents how V3 prompt selection discards active skill instructions and proposes preserving both prompt components.

Reviews (2): Last reviewed commit: "docs: address greptile bot review commen..." | Re-trigger Greptile

Comment thread docs/bugs/bug-001-document-grounded-refusal.md Outdated
Comment thread docs/tech-debt/replace-placeholder-corruption-bug.md Outdated
Comment thread docs/tech-debt/speculative-similarity-jaccard-critique.md
…t#429

- bug-001: fix TypeScript undefined risk in preferred fix diff.
  input.activeMode?.sourceContract is ModeSourceContract | undefined;
  documentGroundedFromContract requires a concrete non-null contract.
  Added ternary null guard so call is only made when contract exists.

- replace-placeholder-corruption-bug: correct factually wrong dollar-sign
  examples. With a literal string first arg and no capturing groups,
  ECMAScript specifies that dollar-n patterns (\, \) are left literal
  (NOT expanded). Only \$&, \, and \$' are real vulnerabilities.
  Removed incorrect Bash and math examples; replaced with accurate \$&,
  \, and \$' examples. Added explicit clarifying note.

- speculative-similarity-jaccard-critique: label all SBERT threshold values
  (0.65, 0.92, cosine scores) as illustrative/uncalibrated. Added warning
  block after embedding examples, inline code comment, updated section
  header and summary table.
evinjohnn pushed a commit that referenced this pull request Aug 15, 2026
…pute docs

Sweeps up what was left untracked in the working directory, with one deliberate
exclusion.

gitignore — the reason this is not a routine chore:
  KAUSHAL_12_DISPUTE_HANDOVER.md sits at the repo ROOT, a byte-identical stray
  copy of the file already held in the correctly-ignored
  dispute-evidence-kaushal-shivaprakashan/. The existing rules are all
  directory-scoped (/dispute-evidence-*), so the root copy was fully
  committable and the next `git add -A` would have published it. This repo is
  PUBLIC and that document carries a named customer's email address, phone
  number and home address. Adds /*_DISPUTE_HANDOVER.md and /*_DISPUTE_*.md so
  the root copies can never be staged. README.md and other root docs stay
  committable — the rule is scoped, not a blanket.

Tests (new, previously untracked alongside 279 tracked siblings):
  V3SkillInjection2026_08_05 — PR #429 Bug 003, skill injection silently
  dropped when Context Intelligence V3 is active (default ON since 2026-07-30),
  covering both drop sites independently.
  ManualScreenshotScreenContext2026_08_05 — manual screenshot screen context.

natively-api: cecbfdf → bedc55e, which is merged to that repo's main via PR #7
and is the deployed revision (Railway reports Online, /health 200).

transitions/: 27 CSS transition reference docs. Scanned for credentials and
customer data before publishing — the single hit is the word "password" inside
a prose description of form-validation UX. These are tooling reference material
rather than application source; say the word and they come back out.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SiDsWPD5XtBYgXWt7ZPizu
evinjohnn pushed a commit that referenced this pull request Aug 16, 2026
…Bug 003)

WhatToAnswerLLM composed its system prompt as

    const _wtaSystemPrompt = _v3p?.system ?? finalPromptOverride;

finalPromptOverride is the ONLY carrier of the `## ACTIVE SKILL` block (it is
built as `${basePrompt}\n\n## ACTIVE SKILL\n${activeSkill.promptBlock}` at
WhatToAnswerLLM.ts:588). Context Intelligence V3 has been default ON since
2026-07-30, so _v3p is always defined, `??` never fell through, and the skill
block was dropped on every single turn — the user picked a skill, the UI showed
it active, and the model never saw it.

The two inputs are not interchangeable (V3's system prompt is a different
governed composition, not a superset), so composeWtaSystemPrompt APPENDS the
skill to whichever base actually rides the turn instead of picking one:
  - no V3 prompt        -> legacy override verbatim (it already holds the block)
  - V3 prompt, no skill -> exactly the V3 prompt, byte-identical to before
  - V3 prompt + skill   -> V3 prompt + the ACTIVE SKILL block
Non-skill turns are therefore inert, which is what keeps this safe to land.

Kept as its own pure module so it is testable without constructing the whole
LLM graph, and reuses the same `## ACTIVE SKILL` heading as the legacy path.

The manual-chat half of Bug 003 (ipcHandlers: the /skill prefix is parsed ~35k
chars AFTER the V3 branch, so under V3 the prefix leaks to the model as literal
text) is NOT fixed here — separate commit.

Validation: typecheck clean under TS 7.0.2; the WTA half of
V3SkillInjection2026_08_05 now passes (5/8 -> the 3 remaining are the
manual-chat assertions).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UamHk73hUw1Fxevh8P2oDe
evinjohnn pushed a commit that referenced this pull request Aug 16, 2026
Symptom: 'screenshot attached but code not generated'. A manually-attached
screenshot never set hasScreenContext, so the V3 turn classifier never added
SCREEN_SPECIFIC / SCREEN_FACT and the screen was not treated as authoritative
evidence — for exactly the turns where the screen is most obviously the subject.

Two drop sites, both one-line:

  IntelligenceEngine.ts:2592 (WTA) — `Boolean(options?.screenContext)` reads the
  PERIODIC-CAPTURE OCR object. A hand-attached screenshot rides in imagePaths
  with screenContext null, so this was always false. Widened with the same
  predicate the legacy path already uses for _wtaHasVisualContext (~line 1228),
  so OCR turns are unaffected and only attached-image turns change.

  ipcHandlers.ts (manual chat) — hasScreenContext was omitted from the
  buildV3Prompt call entirely, defaulting to false. Manual chat has no periodic
  OCR object at all, so imagePaths is the only screen signal on that surface.

Also documents V3SkillInjection's still-open manual-chat half in its header:
both sites live in the same gemini-chat handler (V3 branch ~50356, skill parse
~85936), the block's dependencies are all available before the branch, and the
known fallout is the legacy identity probe. Not auto-fixed because hoisting
changes what every read of `message` between those offsets sees, and its
`skillParseIdx < v3EntryIdx` assertion pins SOURCE OFFSETS — satisfiable by an
occurrence that fixes nothing, which is the exact failure mode this branch spent
its time repairing.

Validation: typecheck clean under TS 7.0.2; both ManualScreenshotScreenContext
tests pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UamHk73hUw1Fxevh8P2oDe
evinjohnn pushed a commit that referenced this pull request Aug 16, 2026
…refusals

Two of the three remaining groups.

── Manual-chat skill injection (#429 Bug 003, second surface) ──
The /skill-name parse lived ~35k characters BELOW the Context Intelligence V3
short-circuit in the same gemini-chat handler, so under V3 (default ON since
2026-07-30) the prefix reached the model as literal text and the skill
instructions were injected nowhere.

Hoisted the PARSE above the branch — but deliberately NOT the mutation. The
hoisted block assigns to a new `skillStrippedMessage` and `message` is still
mutated at the original boundary, so every reader in between (identity probe at
~80315, source-switch resolution, error logs) sees the user's literal input
exactly as before. That is what makes a 3k-char relocation across a 12k-line hot
path safe: the only consumer whose input changes is the V3 branch itself, which
is the bug. Early skill errors now return before ForegroundGate.begin, which is
null-guarded at release, and after stream registration exactly as before.

The identity probe additionally gates on !skillPromptBlock. That immunity
already existed but only incidentally — its anchored regexes could not match a
"/humanize who are you" prefix — so it is now explicit and survives a regex
relaxation.

── Doc-grounding refusal requires FILES ──
The policy line keyed on enforcement alone, and enforcement is true on the
AUTHORITY ALONE: strictDocumentGroundedFromContract returns true for
`reference_files_only` before anything is uploaded. Such a mode was told to say
the answer was "not in the uploaded material" when no uploaded material existed.
Now requires enforcement AND files.

I first implemented the stronger form this file originally asked for —
suppression requires STRICT — and it broke F9PurposeHonestyUnderR1_2026_08_15.
Investigating rather than reverting showed the two tests assert OPPOSITE output
for an IDENTICAL plan (same answerType, enforcement, strict, files: verified
directly), so no discriminator exists. R1 enforcement is what actually sets
forceDocumentGrounding (LLMHelper.ts:2299, IntelligenceEngine.ts:1179), so
retrieval IS forced for seeded+files and the honesty mandate is honest. The
older assertions encoded a premise PR #466 superseded; they were rewritten with
that reasoning recorded, not deleted quietly.

Validation: typecheck clean under TS 7.0.2. V3SkillInjection 8/8 (was 5/8),
DocGroundedRoutingRequiresFiles 11/11, F9PurposeHonestyUnderR1 3/3. Full-suite
regression run from the hoist: zero new failures.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UamHk73hUw1Fxevh8P2oDe
Abitesh pushed a commit to Abitesh/Sneak-Peek that referenced this pull request Aug 30, 2026
…pute docs

Sweeps up what was left untracked in the working directory, with one deliberate
exclusion.

gitignore — the reason this is not a routine chore:
  KAUSHAL_12_DISPUTE_HANDOVER.md sits at the repo ROOT, a byte-identical stray
  copy of the file already held in the correctly-ignored
  dispute-evidence-kaushal-shivaprakashan/. The existing rules are all
  directory-scoped (/dispute-evidence-*), so the root copy was fully
  committable and the next `git add -A` would have published it. This repo is
  PUBLIC and that document carries a named customer's email address, phone
  number and home address. Adds /*_DISPUTE_HANDOVER.md and /*_DISPUTE_*.md so
  the root copies can never be staged. README.md and other root docs stay
  committable — the rule is scoped, not a blanket.

Tests (new, previously untracked alongside 279 tracked siblings):
  V3SkillInjection2026_08_05 — PR Natively-AI-assistant#429 Bug 003, skill injection silently
  dropped when Context Intelligence V3 is active (default ON since 2026-07-30),
  covering both drop sites independently.
  ManualScreenshotScreenContext2026_08_05 — manual screenshot screen context.

natively-api: cecbfdf → bedc55e, which is merged to that repo's main via PR Natively-AI-assistant#7
and is the deployed revision (Railway reports Online, /health 200).

transitions/: 27 CSS transition reference docs. Scanned for credentials and
customer data before publishing — the single hit is the word "password" inside
a prose description of form-validation UX. These are tooling reference material
rather than application source; say the word and they come back out.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SiDsWPD5XtBYgXWt7ZPizu
Abitesh pushed a commit to Abitesh/Sneak-Peek that referenced this pull request Aug 30, 2026
…ely-AI-assistant#429 Bug 003)

WhatToAnswerLLM composed its system prompt as

    const _wtaSystemPrompt = _v3p?.system ?? finalPromptOverride;

finalPromptOverride is the ONLY carrier of the `## ACTIVE SKILL` block (it is
built as `${basePrompt}\n\n## ACTIVE SKILL\n${activeSkill.promptBlock}` at
WhatToAnswerLLM.ts:588). Context Intelligence V3 has been default ON since
2026-07-30, so _v3p is always defined, `??` never fell through, and the skill
block was dropped on every single turn — the user picked a skill, the UI showed
it active, and the model never saw it.

The two inputs are not interchangeable (V3's system prompt is a different
governed composition, not a superset), so composeWtaSystemPrompt APPENDS the
skill to whichever base actually rides the turn instead of picking one:
  - no V3 prompt        -> legacy override verbatim (it already holds the block)
  - V3 prompt, no skill -> exactly the V3 prompt, byte-identical to before
  - V3 prompt + skill   -> V3 prompt + the ACTIVE SKILL block
Non-skill turns are therefore inert, which is what keeps this safe to land.

Kept as its own pure module so it is testable without constructing the whole
LLM graph, and reuses the same `## ACTIVE SKILL` heading as the legacy path.

The manual-chat half of Bug 003 (ipcHandlers: the /skill prefix is parsed ~35k
chars AFTER the V3 branch, so under V3 the prefix leaks to the model as literal
text) is NOT fixed here — separate commit.

Validation: typecheck clean under TS 7.0.2; the WTA half of
V3SkillInjection2026_08_05 now passes (5/8 -> the 3 remaining are the
manual-chat assertions).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UamHk73hUw1Fxevh8P2oDe
Abitesh pushed a commit to Abitesh/Sneak-Peek that referenced this pull request Aug 30, 2026
…I-assistant#429 Bug 002)

Symptom: 'screenshot attached but code not generated'. A manually-attached
screenshot never set hasScreenContext, so the V3 turn classifier never added
SCREEN_SPECIFIC / SCREEN_FACT and the screen was not treated as authoritative
evidence — for exactly the turns where the screen is most obviously the subject.

Two drop sites, both one-line:

  IntelligenceEngine.ts:2592 (WTA) — `Boolean(options?.screenContext)` reads the
  PERIODIC-CAPTURE OCR object. A hand-attached screenshot rides in imagePaths
  with screenContext null, so this was always false. Widened with the same
  predicate the legacy path already uses for _wtaHasVisualContext (~line 1228),
  so OCR turns are unaffected and only attached-image turns change.

  ipcHandlers.ts (manual chat) — hasScreenContext was omitted from the
  buildV3Prompt call entirely, defaulting to false. Manual chat has no periodic
  OCR object at all, so imagePaths is the only screen signal on that surface.

Also documents V3SkillInjection's still-open manual-chat half in its header:
both sites live in the same gemini-chat handler (V3 branch ~50356, skill parse
~85936), the block's dependencies are all available before the branch, and the
known fallout is the legacy identity probe. Not auto-fixed because hoisting
changes what every read of `message` between those offsets sees, and its
`skillParseIdx < v3EntryIdx` assertion pins SOURCE OFFSETS — satisfiable by an
occurrence that fixes nothing, which is the exact failure mode this branch spent
its time repairing.

Validation: typecheck clean under TS 7.0.2; both ManualScreenshotScreenContext
tests pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UamHk73hUw1Fxevh8P2oDe
Abitesh pushed a commit to Abitesh/Sneak-Peek that referenced this pull request Aug 30, 2026
…refusals

Two of the three remaining groups.

── Manual-chat skill injection (Natively-AI-assistant#429 Bug 003, second surface) ──
The /skill-name parse lived ~35k characters BELOW the Context Intelligence V3
short-circuit in the same gemini-chat handler, so under V3 (default ON since
2026-07-30) the prefix reached the model as literal text and the skill
instructions were injected nowhere.

Hoisted the PARSE above the branch — but deliberately NOT the mutation. The
hoisted block assigns to a new `skillStrippedMessage` and `message` is still
mutated at the original boundary, so every reader in between (identity probe at
~80315, source-switch resolution, error logs) sees the user's literal input
exactly as before. That is what makes a 3k-char relocation across a 12k-line hot
path safe: the only consumer whose input changes is the V3 branch itself, which
is the bug. Early skill errors now return before ForegroundGate.begin, which is
null-guarded at release, and after stream registration exactly as before.

The identity probe additionally gates on !skillPromptBlock. That immunity
already existed but only incidentally — its anchored regexes could not match a
"/humanize who are you" prefix — so it is now explicit and survives a regex
relaxation.

── Doc-grounding refusal requires FILES ──
The policy line keyed on enforcement alone, and enforcement is true on the
AUTHORITY ALONE: strictDocumentGroundedFromContract returns true for
`reference_files_only` before anything is uploaded. Such a mode was told to say
the answer was "not in the uploaded material" when no uploaded material existed.
Now requires enforcement AND files.

I first implemented the stronger form this file originally asked for —
suppression requires STRICT — and it broke F9PurposeHonestyUnderR1_2026_08_15.
Investigating rather than reverting showed the two tests assert OPPOSITE output
for an IDENTICAL plan (same answerType, enforcement, strict, files: verified
directly), so no discriminator exists. R1 enforcement is what actually sets
forceDocumentGrounding (LLMHelper.ts:2299, IntelligenceEngine.ts:1179), so
retrieval IS forced for seeded+files and the honesty mandate is honest. The
older assertions encoded a premise PR Natively-AI-assistant#466 superseded; they were rewritten with
that reasoning recorded, not deleted quietly.

Validation: typecheck clean under TS 7.0.2. V3SkillInjection 8/8 (was 5/8),
DocGroundedRoutingRequiresFiles 11/11, F9PurposeHonestyUnderR1 3/3. Full-suite
regression run from the hoist: zero new failures.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UamHk73hUw1Fxevh8P2oDe
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