You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@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
New command, in the Q&A catalog only — PUBLIC_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.
Parser: mirror ask's free-text capture (commands.ts:249,271) for the new verb.
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.
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.
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.
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.
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.
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.
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."
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
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.
Part of #4594 (Option 1 — recommended v1).
Context
@gittensory ask <question>already exists and already captures free text (commands.ts:271), butanswers entirely from cached deterministic decision-pack data (
buildAskPublicAnswerCard,commands.ts:440-501) — the question string is only used to build an objective forplanNextWork/executeDecisionPackRun, never actually answered by an LLM. The only LLM-generated textin 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
PUBLIC_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()atcommands.ts:312-314is checked before theQ&A dispatcher ever runs) rather than something enforced by prompt instruction alone.
ask's free-text capture (commands.ts:249,271) for the new verb.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 viaenv.AI, and a guaranteed-safe fallback text on every non-ok status. If the question isn't derivablefrom the cached bundle, the function must decline and point at the relevant existing command (e.g.
@gittensory preflight) rather than guess.advisoryAiRouting.chatQa: boolean(defaultfalse) —src/types.ts:1235-1240,src/review/advisory-ai-routing-config.ts, thepackages/gittensory-enginetwin, threaded through
src/signals/focus-manifest.ts:575-583exactly like the 4 existing flags.advisoryAiRoutingcapabilities silently fallback to frontier
env.AIwhen their flag is off (a cost optimization — correct for them). Thiscapability 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 !== trueor
env.AI_ADVISORYis unconfigured — do not callwithAdvisoryAiEnv(env, false)and let it silentlyspend frontier tokens.
DEFAULT_COMMAND_AUTHORIZATION_POLICY.commands(
src/settings/command-authorization.ts:5-35) — do not let it fall through todefault. Start["maintainer","collaborator"]for v1 (matches the "read-only, at least for v1" framing and thegenerate-tests/#4195precedent of shipping maintainer-scoped first, then widening). Registering itin this map also activates the
pr_author-widening guard (command-authorization.ts:162-173) so aself-hoster can't accidentally yml themselves to "anyone commenting on their own PR" without the
guard's protection.
AI_COST_BEARING_COMMANDS(commands.ts:321-330) — inherits the tightercommandRateLimitAiMaxPerWindowceiling (default 5/24h) via the existingmaybeThrottleGittensoryCommand(processors.ts:14238-14333) with zero new rate-limit code. Notethe known gap:
commandRateLimitPolicydefaults to"off"fleet-wide (see Epic: scoped @gittensory Q&A reply capability (local-Ollama chat + intent-routing) #4594) — document thisas a co-requisite setting for any repo enabling
chatQa.neutralizePublicMarkdownText+sanitizePublicComment(commands.ts:1565,1661) before composing the comment body — the onegenuinely 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 coversthe user's question text today.
informational reply — cannot change review outcomes, gate state, or trigger a re-review. To retrigger
a review, comment
@gittensory review."test/unit/advisory-ai-routing-call-sites.test.ts's shape) that fails CI if the new generation-functionfile ever imports any action-command handler (
maybeProcessReviewCommand,maybeProcessGateOverrideCommand,maybeProcessPauseCommand, etc.) or any DB-mutation helper beyondthe existing comment-answer upsert.
Deliverables
src/services/ai-chat-qa.ts), modeled onai-summaries.tschatQacapability flag: type + normalizer + engine-package twin + focus-manifest mergebuildMentionCommandBundledispatch["maintainer","collaborator"]default)AI_COST_BEARING_COMMANDSmembership(mirroring
ai-summaries.test.ts), 99% patch coverage per house rulessrc/openapi/schemas.ts) for dashboard/API visibilityconfig/examples/gittensory.full.yml— document the new flagExpected outcome
A maintainer or collaborator can
@gittensory chat <question>on a PR and get a grounded, natural-proseanswer sourced from the same cached decision-pack data
ask/preflight/blockersalready 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
askcommand (closest existing precedent):src/github/commands.ts:28-39,271,415-501,802-831advisoryAiRoutingpattern:src/types.ts:1230-1240,src/review/advisory-ai-routing-config.ts,src/selfhost/ai.ts:1300-1311,src/server.ts:511-532src/services/ai-summaries.tssrc/settings/command-authorization.tssrc/queue/processors.ts:14238-14333src/queue/processors.ts:14335-14735