Skip to content

fix(dashboard): refuse unknown-slot approval-mode requests before any global mutation - #4472

Merged
bolichen97 merged 1 commit into
kirodotdev:mainfrom
c020627:fix/chat-mode-slot-validation
Aug 25, 2026
Merged

bolichen97 merged 1 commit into
kirodotdev:mainfrom
c020627:fix/chat-mode-slot-validation

Conversation

@c020627

@c020627 c020627 commented Aug 19, 2026 •

Copy link
Copy Markdown
Contributor

Problem / Motivation

POST /api/chat/mode (src/kiro_crew/dashboard/chat_handlers.py) carried three defects in the ordering between slot validation and global mutation:

  1. A trust_reads request naming a slot that does not resolve silently fell through to the all-slots path, widening a slot-scoped request across every slot on the gateway. Its trust/normal siblings answer 400 unknown slot.
  2. The unknown-slot 400 sat after safety_override().deactivate(), so a refused request ({"mode": "normal", "slot": " "}) still revoked the process-global grant -- a rejected request mutated global state.
  3. deactivate() -- which emits a SEL event -- ran inline on the gateway loop, while the sibling activate() is offloaded with asyncio.to_thread.

Why it matters

The unknown-slot cases are exactly the malformed or stale requests an automation sends (e.g. a session created against a slot that was since deleted). Today those requests either widen a slot-scoped intent across every slot (defect 1 -- the trust_reads path that other modes explicitly refuse) or withdraw a global grant the operator relies on (defect 2), while the blocked-deactivate inline run puts SEL I/O on the gateway event loop (defect 3). None of the three has a good outcome for the operator; each is a "rejected input should be a no-op" violation.

What changed (motivation -> approach -> change)

  • Root cause: validation was interleaved with mutation -- trust_reads never validated, and trust/normal validated only after the global grant was already revoked. The fix is to make "slot does not resolve" a refusal that happens before any mutation, for every slot-scoped branch, and to stop re-indexing state._slots[slot_key] after an await (no check/use gap for a concurrent slot deletion).
  • Approach: the slot key is resolved once, inline at the top of api_chat_mode, and an unresolvable key returns 400 before touching grant or slots. Branches mutate the captured slot reference instead of re-indexing the dict. yolo is global and keeps ignoring slot (a stale key must not refuse it), preserving upstream behavior.
  • Non-string slots refused: body.get("slot") or None previously collapsed falsy non-strings ([], {}, 0, False) into the documented all-slots request -- so {"mode": "trust", "slot": []} trusted every slot (the same widening defect as api_chat_mode: trust_reads widens on an unknown slot, rejected requests still mutate, deactivate blocks the loop #4454 through a normalization back door). The raw value is now type-checked before that normalization: any present non-string slot answers 400 unknown slot, while yolo still ignores slot entirely.
  • Change: src/kiro_crew/dashboard/chat_handlers.py -- hoist validation ahead of the conditional deactivate(); refuse present non-string slots on the raw value; offload deactivate() with await asyncio.to_thread(...) matching the sibling activate(). A slot-scoped trust/trust_reads leaves the process-global grant alone (a per-slot request cannot be answered by withdrawing authority elsewhere); a grant declared in owner-only config is exempt from the narrowing, since selecting another approval mode is the one documented action that ends it.

This branch is rebased onto the current main. The branch bodies adopt main's effective_session_key() shared-session propagation (the policy is per session, the flag is per slot, so a grant/revoke clears every slot sharing the session) while keeping this PR's up-front validation; main's now-redundant late unknown-slot checks inside the trust/normal branches are dropped in favor of the single up-front refusal. Per the First Principles review: error-code-baseline.json is reverted to base (the diff removes two uncoded unknown slot responses and adds two -- net zero -- so no regeneration is needed), and the single-consumer _resolve_mode_slot() helper is inlined at its call site.

Tests

test/test_chat_mode_security.py -- 16 tests:

Regression -- test_trust_reads.py, test_channel_trust_revoke.py, test_dashboard_chat.py (785 passed; one pre-existing TestIsReadOnlyBash failure reproduces on origin/main and is unrelated to this diff). test/test_error_code_contract.py passes with the baseline at base.

Manual verification

N/A -- unit coverage sufficient: the change is confined to one handler whose every branch is exercised directly by the new contract tests, including the SEL event path.

Screenshots / video

N/A -- no user-visible UI change.

Related Issues

Closes #4454

Checklist

  • Single commit with a Conventional Commits title (fix(dashboard): ...)
  • Existing tests pass and new tests added for new functionality
  • Self-review completed; code follows project style guidelines
  • Documentation updated (if applicable) -- docs/system-specs/modules/security.md TTL table corrected
  • No secrets, credentials, or internal references in the diff

@c020627
c020627 requested a review from a team August 19, 2026 04:48
@github-actions github-actions Bot added fork Pull request from a fork (external contributor) readiness: action required A blocking check or review needs attention labels Aug 19, 2026
@iamwhatever

Copy link
Copy Markdown
Contributor

👋 Hi! This PR's description is missing some required sections from our PR template. Workflow runs won't be auto-approved until the description is updated.

Missing sections:

  • ## Problem / Motivation
  • ## Why it matters
  • ## What changed
  • ## Tests

Please update your PR description to include these sections, then push or re-save the description. The workflows will be approved on the next cycle.

2 similar comments
@iamwhatever

Copy link
Copy Markdown
Contributor

👋 Hi! This PR's description is missing some required sections from our PR template. Workflow runs won't be auto-approved until the description is updated.

Missing sections:

  • ## Problem / Motivation
  • ## Why it matters
  • ## What changed
  • ## Tests

Please update your PR description to include these sections, then push or re-save the description. The workflows will be approved on the next cycle.

@bolichen97

Copy link
Copy Markdown
Contributor

👋 Hi! This PR's description is missing some required sections from our PR template. Workflow runs won't be auto-approved until the description is updated.

Missing sections:

  • ## Problem / Motivation
  • ## Why it matters
  • ## What changed
  • ## Tests

Please update your PR description to include these sections, then push or re-save the description. The workflows will be approved on the next cycle.

@github-actions github-actions Bot added the merge conflict Branch has merge conflicts with its base — author must resolve before merge label Aug 19, 2026
@c020627
c020627 force-pushed the fix/chat-mode-slot-validation branch from c710fc2 to 54b4be6 Compare August 19, 2026 06:41
@bolichen97

Copy link
Copy Markdown
Contributor

👋 Hi! This PR's description is missing some required sections from our PR template. Workflow runs won't be auto-approved until the description is updated.

Missing sections:

  • ## Problem / Motivation
  • ## Why it matters
  • ## What changed
  • ## Tests

Please update your PR description to include these sections, then push or re-save the description. The workflows will be approved on the next cycle.

1 similar comment
@iamwhatever

Copy link
Copy Markdown
Contributor

👋 Hi! This PR's description is missing some required sections from our PR template. Workflow runs won't be auto-approved until the description is updated.

Missing sections:

  • ## Problem / Motivation
  • ## Why it matters
  • ## What changed
  • ## Tests

Please update your PR description to include these sections, then push or re-save the description. The workflows will be approved on the next cycle.

@c020627
c020627 force-pushed the fix/chat-mode-slot-validation branch from 54b4be6 to 58ab1df Compare August 19, 2026 06:45
@github-actions github-actions Bot added readiness: checking Automated validation is still running readiness: action required A blocking check or review needs attention and removed merge conflict Branch has merge conflicts with its base — author must resolve before merge readiness: action required A blocking check or review needs attention readiness: checking Automated validation is still running labels Aug 19, 2026
@c020627
c020627 force-pushed the fix/chat-mode-slot-validation branch from 58ab1df to 65a520e Compare August 19, 2026 07:28
@github-actions github-actions Bot added readiness: checking Automated validation is still running and removed readiness: action required A blocking check or review needs attention labels Aug 19, 2026
@c020627
c020627 force-pushed the fix/chat-mode-slot-validation branch from 65a520e to 58f9657 Compare August 19, 2026 07:40
@github-actions github-actions Bot added readiness: action required A blocking check or review needs attention readiness: checking Automated validation is still running and removed readiness: checking Automated validation is still running readiness: action required A blocking check or review needs attention labels Aug 19, 2026
@c020627

c020627 commented Aug 19, 2026

Copy link
Copy Markdown
Contributor Author

CI note: all checks pass on this head (\58f9657) except Backend Tests (Windows) (1). The single failure is \ est_aidlc_store.py::TestProjectCRUD::test_list_projects\ (\�ssert '43f8024ec4fd' == 'e5a621a0834d') — a pre-existing timing-sensitive sort assertion (two \create_project\ calls land on the same \updated_at\ tick, so the tie-break order is unstable). The test file has not been touched since \79e7268\ and is unrelated to this PR's diff (which touches only \chat_handlers.py, \ est_chat_mode_security.py, \security.md, \error-code-baseline.json). Could this job be re-run? Thanks!

@c020627
c020627 force-pushed the fix/chat-mode-slot-validation branch from 58f9657 to 2a2af99 Compare August 19, 2026 08:19
@github-actions github-actions Bot added readiness: checking Automated validation is still running and removed readiness: action required A blocking check or review needs attention labels Aug 19, 2026
@bolichen97
bolichen97 enabled auto-merge (squash) August 24, 2026 06:58
@chenmingwei23 chenmingwei23 added needs-pr-triage PR scanner: awaiting automated triage drive-to-green PR claimed by drive-to-green pipeline and removed needs-pr-triage PR scanner: awaiting automated triage labels Aug 25, 2026
@chenmingwei23

Copy link
Copy Markdown
Contributor

Kiro Crew [operator: chenmingwei23#de330d0c]: This PR has been inactive for 7+ days with a blocking check and merge conflicts. I have assessed the blockers and they appear resolvable -- I will push fixes directly to this branch as a co-author.

Assessment: Branch is CONFLICTING (needs rebase onto main for a fresh merge ref). All Backend/Build/Design lanes are green; the only blocking check is First Principles, which asks for two bounded, mechanical subtractions on this PR own code, no design change: (1) revert the error-code-baseline.json _compliant 834->865 rider hunk to base (the test compares only _totals/files, never _compliant, and the diff adds no code field so it cannot produce that bump); (2) inline the single-consumer _resolve_mode_slot() helper at its one call site in api_chat_mode. The core fix (refuse unknown-slot / non-string-slot approval-mode requests before mutating the global grant) is kept intact.

If you would prefer I do not touch this PR, add the pr-no-autofix label.

@iamwhatever

Copy link
Copy Markdown
Contributor

🤖 Kiro Crew [operator: iamwhatever]: This PR has been inactive for 7+ days with failing CI. I've assessed the blockers and they appear resolvable — I'll push fixes directly to this branch as a co-author.

Assessment: Two blockers, both mechanical. (1) The branch re-conflicted with main after the last resolution — the conflict is in chat_handlers.py / error-code-baseline.json, and the PR's intent (hoist all slot validation ahead of every mutation) is unambiguous, so main's structure + this PR's validation logic resolves it. (2) First Principles Review BLOCKs on one hunk: error-code-baseline.json bumps _compliant 834 → 865, which the diff cannot produce (no code field is added, and the handler hunks net to 0 uncoded responses) — the fix is to revert that file to base, since test_error_code_contract.py only compares _totals and files. The PR body's claim about missing_code 72 → 73 will be corrected to match. Every other check is green, including GPT 5.6, Opus 4.8 and Design Review. The advisory subtraction (inline _resolve_mode_slot() at its single call site) will be answered with a disposition rather than reshaped, since it is not blocking.

If you'd prefer I don't touch this PR, add the pr-no-autofix label.

@chenmingwei23
chenmingwei23 force-pushed the fix/chat-mode-slot-validation branch from 7855c85 to b7d3482 Compare August 25, 2026 10:03
@chenmingwei23

Copy link
Copy Markdown
Contributor

Rebased onto current main and applied the First Principles review subtractions. Head is now b7d3482 (was 7855c85). Original fix authored by @c020627; changes on top:

Rebase / conflict resolution (src/kiro_crew/dashboard/chat_handlers.py):

  • Main's trust/trust_reads/normal branches evolved to effective_session_key() with shared-session propagation (grant/revoke clears every slot sharing the session). The resolved bodies keep that newer semantics, writing through this PR's captured slot reference instead of re-indexing state._slots[slot_key].
  • Main's late unknown slot 400 checks inside the trust/normal branches are dropped: this PR's single up-front refusal (before deactivate()) subsumes them, which is the point of the fix.

First Principles subtractions:

  • error-code-baseline.json reverted to base (resolved by taking main's side in the rebase). The diff removes two uncoded unknown slot responses and adds two -- net zero coded-response delta -- so no regeneration is needed; test/test_error_code_contract.py passes at base.
  • _resolve_mode_slot() inlined at its single call site in api_chat_mode; the helper is deleted.

Verification (local): isort/flake8/mypy clean on touched files; test_chat_mode_security.py (16) + test_error_code_contract.py (6) all pass; regression run of test_trust_reads.py / test_channel_trust_revoke.py / test_dashboard_chat.py = 785 passed with one pre-existing TestIsReadOnlyBash failure that reproduces identically on origin/main (unrelated to this diff). Black baseline gate passes.

PR body updated to match (the previous body's baseline-regeneration claim is removed).

Co-authored-by trailer credits Kiro Crew; XiaoChen (c020627) remains the commit author.

@github-actions github-actions Bot added readiness: checking Automated validation is still running and removed readiness: action required A blocking check or review needs attention merge conflict Branch has merge conflicts with its base — author must resolve before merge labels Aug 25, 2026
@chenmingwei23

Copy link
Copy Markdown
Contributor

Status: the two red shards on head b7d3482 -- Backend Tests (3.10, 4) and Backend Tests (Windows) (4) -- are main-owned, not this PR's. The failing test test_trust_reads.py::TestIsReadOnlyBash::test_a_word_bash_deletes_cannot_forge_a_read_mode was added by #5541 (d8755472) and fails at its own merge commit; main CI has been red on the same shards since it merged at 08:54 UTC (this branch's base e00c749b8 fails identically on main's own run). Filed #5855 with the bisect.

No fix is folded here (zero lines of this diff touch is_read_only_bash or that test). Once main is fixed I will rebase to pick it up and re-drive. The fork AI review lanes trigger on CI success, so they stay pinned until then.

@github-actions github-actions Bot added readiness: action required A blocking check or review needs attention and removed readiness: checking Automated validation is still running labels Aug 25, 2026
@iamwhatever iamwhatever added the needs-human Triage: requires a human or product decision label Aug 25, 2026
@iamwhatever

Copy link
Copy Markdown
Contributor

Drive-to-green diagnosis (automated)

This PR was adopted by the drive-to-green reconciler. Diagnosis of head b7d34828:

All failing checks are pre-existing main breakage, not this PR's diff.

PR-side state is clean: single commit, 3 files, mergeable, 0 unresolved review threads, all 40+ other checks green. It is only 2 commits behind main; rebasing now would inherit an additional red (shard 2) rather than fix anything.

No code changes made. Escalating with needs-human: this PR is blocked solely on the main repair for #5846. Once main is green, a re-run (or trivial rebase) should bring it to review-ready.

… global mutation

A slot-scoped POST /api/chat/mode request naming a slot that does not
resolve is now a strict no-op refusal: 400 before safety_override()
deactivation, no widening to every slot, no revocation of the live
global grant. Non-string slot values are refused on the raw value
before `or None` normalization can erase them into the all-slots
request. deactivate() is offloaded with asyncio.to_thread matching
the sibling activate(). 16 contract tests.

Original fix authored by XiaoChen (c020627). Rebased onto current
main: branch bodies adopt main's effective_session_key() shared-
session propagation while keeping this PR's up-front validation, and
main's now-redundant late unknown-slot checks are dropped. Review
subtractions applied: error-code-baseline.json reverted to base (the
diff adds no coded responses net, so no regeneration is needed) and
the single-consumer _resolve_mode_slot() helper inlined at its call
site.

Closes kirodotdev#4454

Co-authored-by: Kiro Crew <[email protected]>
@chenmingwei23
chenmingwei23 force-pushed the fix/chat-mode-slot-validation branch from b7d3482 to 66d0dbc Compare August 25, 2026 18:17
@chenmingwei23

Copy link
Copy Markdown
Contributor

Rebased onto current main (head b7d3482 -> 66d0dbc, zero code change -- clean rebase). The two inherited Backend Tests shard-4 reds (test_trust_reads wc -l < f, main red since 08:54 UTC, #5855 / #5846) are fixed on main as of ff15bbe1; verified locally that the previously-failing test now passes at the new base and the PR's own suites stay green (test_chat_mode_security 16 + test_error_code_contract 6 + the trust_reads case = 23 passed). Re-approving the fork workflow runs; expecting a fully green run this time.

@github-actions github-actions Bot added readiness: checking Automated validation is still running and removed readiness: action required A blocking check or review needs attention labels Aug 25, 2026
@iamwhatever iamwhatever removed the needs-human Triage: requires a human or product decision label Aug 25, 2026
@github-actions github-actions Bot added readiness: passed Eligible automated validation passed for the current revision and removed readiness: checking Automated validation is still running labels Aug 25, 2026
@bolichen97
bolichen97 merged commit fd313fe into kirodotdev:main Aug 25, 2026
74 checks passed
@chenmingwei23

Copy link
Copy Markdown
Contributor

Review-ready on head 66d0dbc.

  • All CI checks green (zero red names on the rollup; the three no-run names -- Linux Packaging, Bundle Size Gate, Frontend Coverage Merge -- are conditional gates skipped for a backend/docs-only diff)
  • All 5 AI review lanes clean and pinned to this head: GPT 5.6 no blocking findings, Opus 4.8 no blocking findings, Design PASS, First Principles PASS (flipped from the prior BLOCK after the two subtractions: error-code-baseline.json reverted to base + _resolve_mode_slot() inlined), UX skipped -- no UI surface
  • PR Readiness: success ("Eligible automated validation passed for this revision")
  • Zero unresolved review threads; MERGEABLE / CLEAN

Summary of the drive: rebased twice onto moving main (first to clear the original conflict, then to pick up the main-side fix for the inherited test_trust_reads shard-4 red tracked in #5846/#5855), applied both First Principles subtractions, corrected the PR body's stale baseline claim, and preserved @c020627's authorship with a Kiro Crew co-author trailer. Ready for maintainer review/merge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fork Pull request from a fork (external contributor)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

api_chat_mode: trust_reads widens on an unknown slot, rejected requests still mutate, deactivate blocks the loop

4 participants