Skip to content

feat(commands): add @gittensory chat <question> — grounded LLM Q&A via local Ollama #4595

Description

@JSONbored

Part of #4594 (Option 1 — recommended v1).

Context

@gittensory ask <question> already exists and already captures free text (commands.ts:271), but
answers entirely from cached deterministic decision-pack data (buildAskPublicAnswerCard,
commands.ts:440-501) — the question string is only used to build an objective for
planNextWork/executeDecisionPackRun, never actually answered by an LLM. The only LLM-generated text
in that path is a private, never-posted summary. This issue adds the first surface where a
contributor's literal question gets a real, grounded, LLM-generated answer.

Requirements

  1. New command, in the Q&A catalog onlyPUBLIC_MENTION_COMMAND_CATALOG (commands.ts:28-39),
    never the action catalog. This placement is what makes "cannot retrigger review or change gate state"
    a structural guarantee (isGittensoryActionCommand() at commands.ts:312-314 is checked before the
    Q&A dispatcher ever runs) rather than something enforced by prompt instruction alone.
  2. Parser: mirror ask's free-text capture (commands.ts:249,271) for the new verb.
  3. Generation function grounded in the existing decision-pack/signal bundle — narrowly rewrite
    already-deterministic facts (PR verdict, which checks/findings are blocking, what a given finding
    means) into natural prose; do not synthesize new claims. Model this on
    summarizeAgentBundleWithAi/rewriteSignalBundleWithAi (src/services/ai-summaries.ts:25-110,289-345)
    for: enable-flag checks, the shared daily neuron budget gate (estimateNeurons/
    sumAiEstimatedNeuronsSince, same counter every AI feature already sums into), provider call via
    env.AI, and a guaranteed-safe fallback text on every non-ok status. If the question isn't derivable
    from the cached bundle, the function must decline and point at the relevant existing command (e.g.
    @gittensory preflight) rather than guess.
  4. New capability flag: advisoryAiRouting.chatQa: boolean (default false) —
    src/types.ts:1235-1240, src/review/advisory-ai-routing-config.ts, the packages/gittensory-engine
    twin, threaded through src/signals/focus-manifest.ts:575-583 exactly like the 4 existing flags.
  5. No-frontier-fallback divergence: the 4 existing advisoryAiRouting capabilities silently fall
    back to frontier env.AI when their flag is off (a cost optimization — correct for them). This
    capability must never fall back to frontier; "Ollama only" is a hard requirement. Skip/decline
    (post nothing, or a canned "chat Q&A isn't enabled on this instance" reply) whenever chatQa !== true
    or env.AI_ADVISORY is unconfigured — do not call withAdvisoryAiEnv(env, false) and let it silently
    spend frontier tokens.
  6. Authorization: register explicitly in DEFAULT_COMMAND_AUTHORIZATION_POLICY.commands
    (src/settings/command-authorization.ts:5-35) — do not let it fall through to default. Start
    ["maintainer","collaborator"] for v1 (matches the "read-only, at least for v1" framing and the
    generate-tests/#4195 precedent of shipping maintainer-scoped first, then widening). Registering it
    in this map also activates the pr_author-widening guard (command-authorization.ts:162-173) so a
    self-hoster can't accidentally yml themselves to "anyone commenting on their own PR" without the
    guard's protection.
  7. Rate limiting: add to AI_COST_BEARING_COMMANDS (commands.ts:321-330) — inherits the tighter
    commandRateLimitAiMaxPerWindow ceiling (default 5/24h) via the existing
    maybeThrottleGittensoryCommand (processors.ts:14238-14333) with zero new rate-limit code. Note
    the known gap
    : commandRateLimitPolicy defaults to "off" fleet-wide (see Epic: scoped @gittensory Q&A reply capability (local-Ollama chat + intent-routing) #4594) — document this
    as a co-requisite setting for any repo enabling chatQa.
  8. Output sanitization: run the model's generated answer through neutralizePublicMarkdownText +
    sanitizePublicComment (commands.ts:1565,1661) before composing the comment body — the one
    genuinely new defensive call needed, since this is the first surface where model output (not just
    user input) is echoed into a trusted bot comment. ask's existing sanitization (#2457) only covers
    the user's question text today.
  9. Fixed disclaimer footer on every answer card (non-LLM, hardcoded string), e.g.: "Read-only
    informational reply — cannot change review outcomes, gate state, or trigger a re-review. To retrigger
    a review, comment @gittensory review."
  10. Import-isolation test: a static architecture test (grep/import-graph assertion, mirroring
    test/unit/advisory-ai-routing-call-sites.test.ts's shape) that fails CI if the new generation-function
    file ever imports any action-command handler (maybeProcessReviewCommand,
    maybeProcessGateOverrideCommand, maybeProcessPauseCommand, etc.) or any DB-mutation helper beyond
    the existing comment-answer upsert.

Deliverables

  • New generation service (e.g. src/services/ai-chat-qa.ts), modeled on ai-summaries.ts
  • chatQa capability flag: type + normalizer + engine-package twin + focus-manifest merge
  • New command in the Q&A catalog + parser wiring + buildMentionCommandBundle dispatch
  • Authorization policy registration (["maintainer","collaborator"] default)
  • AI_COST_BEARING_COMMANDS membership
  • Output sanitization call + fixed disclaimer footer
  • Import-isolation architecture test
  • Unit tests: parser, authorization default, rate-limit-set membership, generation-function behavior
    (mirroring ai-summaries.test.ts), 99% patch coverage per house rules
  • OpenAPI schema entry (src/openapi/schemas.ts) for dashboard/API visibility
  • config/examples/gittensory.full.yml — document the new flag

Expected outcome

A maintainer or collaborator can @gittensory chat <question> on a PR and get a grounded, natural-prose
answer sourced from the same cached decision-pack data ask/preflight/blockers already read —
powered entirely by local Ollama, never touching the frontier model chain, structurally incapable of
retriggering a review or changing gate state, config-driven so any self-hoster can enable/scope it for
their own repos.

Links + resources

  • Epic: Epic: scoped @gittensory Q&A reply capability (local-Ollama chat + intent-routing) #4594
  • ask command (closest existing precedent): src/github/commands.ts:28-39,271,415-501,802-831
  • advisoryAiRouting pattern: src/types.ts:1230-1240, src/review/advisory-ai-routing-config.ts,
    src/selfhost/ai.ts:1300-1311, src/server.ts:511-532
  • Generation-layer template: src/services/ai-summaries.ts
  • Authorization engine: src/settings/command-authorization.ts
  • Rate limiter: src/queue/processors.ts:14238-14333
  • Dispatch entrypoint: src/queue/processors.ts:14335-14735

Metadata

Metadata

Assignees

Labels

maintainer-onlyOwner-only work — yields no Gittensor points.

Projects

Status
In Progress

Relationships

None yet

Development

No branches or pull requests

Issue actions