Skip to content

fix(llm): harden HAIP AI explanation grounding & transport#129

Merged
telivity-otaip merged 1 commit into
mainfrom
claude/peaceful-euler-49084e
Jun 20, 2026
Merged

fix(llm): harden HAIP AI explanation grounding & transport#129
telivity-otaip merged 1 commit into
mainfrom
claude/peaceful-euler-49084e

Conversation

@telivity-otaip

Copy link
Copy Markdown
Collaborator

Hardening pass on the HAIP AI explanation layer (apps/api/src/modules/llm + agent.service.explainDecision).

Changes

  • Fail closed on ungrounded output — an explanation whose rationale asserts a figure the decision's numbers don't support is now suppressed (explanation: null) and never cached, instead of returned flagged-but-visible.
  • Numbers-only prompt — new numericPayload() strips every free-form string from the decision before it reaches the model, so guest/review/email text can't steer the explanation (prompt-injection). Makes "the model only sees numbers" structural, not just a comment.
  • Tighter grounding heuristic — dropped the unsafe arbitrary ÷100 path (a room count no longer "supports" an invented small percentage); significantNumbers now parses sign, thousands separators (1,200), and scientific notation (1e6).
  • Bounded transportLlmService adds an AbortController timeout (HAIP_AI_TIMEOUT_MS, default 10s) and a Content-Length body cap. Still fail-soft to null on any error.

Verification

  • pnpm test (apps/api): 1015 passing, including new regression tests for each item above.
  • pnpm typecheck: clean.

🤖 Generated with Claude Code

- explainDecision now fails closed: an explanation whose rationale asserts a
  figure the decision's numbers don't support is suppressed (returns null) and
  never cached, instead of being returned flagged-but-visible.
- numericPayload(): strip every free-form string from the decision before the
  prompt so ONLY numbers reach the model — prevents prompt injection via
  guest/review/email fields and makes "model sees only numbers" structural.
- grounding heuristic: drop the unsafe arbitrary ÷100 path (a room count can no
  longer "support" an invented small percentage); parse sign, thousands
  separators, and scientific notation in significantNumbers.
- LlmService: AbortController timeout (HAIP_AI_TIMEOUT_MS, default 10s) plus a
  Content-Length body cap; still fail-soft to null on any error.
- Regression tests for each of the above.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@telivity-otaip telivity-otaip merged commit 1c9c5cf into main Jun 20, 2026
4 checks passed
@telivity-otaip telivity-otaip deleted the claude/peaceful-euler-49084e branch June 20, 2026 22:45
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