Skip to content

feat(meetings): add a per-meeting note the user owns - #7194

Open
kaizawa97 wants to merge 1 commit into
kirodotdev:mainfrom
kaizawa97:pr/meetings-note
Open

feat(meetings): add a per-meeting note the user owns#7194
kaizawa97 wants to merge 1 commit into
kirodotdev:mainfrom
kaizawa97:pr/meetings-note

Conversation

@kaizawa97

Copy link
Copy Markdown
Collaborator

Problem / Motivation

Everything written during a meeting belongs to an agent: the minutes, the diagram, the action items. The user's own thoughts have nowhere to go — anything they type into the broadcast bar is dispatched to the agents, not kept for themselves.

What changed

A per-meeting note the user owns, beside the agents' outputs, with screenshot paste.

  • GET/PUT /meetings/{id}/note — the whole note in, the whole note out (no partial update to express, so no PATCH). Markdown, autosaved, capped at MAX_NOTE_CHARS (100k — a human typing for at most the 4 hours MAX_SESSION_DURATION allows).
  • POST /meetings/{id}/note/images — one pasted screenshot per multipart request. The body is validated by hand (size cap 8 MiB per image, 200 images per meeting) and the content is signature-sniffed (PNG/JPEG/GIF/WebP magic bytes), never trusted from the filename or Content-Type.
  • Two filenames in the data layout are security properties, and both are pinned by test. An agent's output path is always a FLAT safe_agent_id(id) + ext filename, and _SAFE_AGENT_ID_RE can produce neither a leading underscore nor a path separator — so _note.md is unreachable by any agent because of the underscore (note.md would be a legal agent id), and images/ is unreachable because it is a directory. test_note_filename_is_unreachable_by_any_agent asserts this through the validator, so loosening the regex fails there rather than silently handing an agent the user's writing.
  • Frontend: a NoteSidebar (edit/preview, autosave state, image paste with the meeting's elapsed time), opened from the meeting's overflow menu. It joins the existing one-side-panel-at-a-time rule (tasks / translation / note close each other), matching the overflow-menu shape feat(meetings): let the user edit an agent's minutes #5740 landed.
  • Spec updated in the same commit (routes, data layout, the security-properties paragraph, test inventory); catalog keys added to all 13 locales.

Tests

  • test/test_meetings_note.py — store containment, unsafe-id refusal, the HTTP contract (cap accepted at the boundary, oversize 413, no redaction on the user's own text).
  • test/test_meetings_note_images.py — the two unreachability properties, the by-hand multipart validation, signature sniffing per format, the per-meeting image budget.
  • website/src/test/MeetingsNote.test.tsx (28 tests) — sidebar, autosave, paste.

Local runs (rebased onto current main): backend meetings suite 317 green, mypy (1209 files), flake8, isort, and the black ratchet green; website tsc -b + vite build green, meetings vitest files green (MeetingsNote, MeetingsApiClient, MeetingsSessionLogic, 122 tests). The full website vitest suite was not re-run locally; CI covers it.

Manual verification

Not yet performed against a live meeting; the paste flow was exercised only through the component tests.

Related Issues

Part of the meetings feature stack split from the feat/meetnote branch. Independent of the other PRs in the stack (#5738, #5741, #2190).

no linked issue: feature work from the meetings stack; no tracked issue exists for it.

@kaizawa97
kaizawa97 requested a review from a team August 31, 2026 03:25
@kaizawa97
kaizawa97 requested a review from a team as a code owner August 31, 2026 03:25
@kaizawa97
kaizawa97 requested a review from patrigao August 31, 2026 03:25
@github-actions github-actions Bot added fork Pull request from a fork (external contributor) readiness: checking Automated validation is still running labels Aug 31, 2026
@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 31, 2026

@bolichen97 bolichen97 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Description / code mismatch

Two surfaces the Description presents as complete land differently in the code: the note sidebar it describes as a finished editor ships without a focus cue, and the 100k-character ceiling it advertises is unreachable in the non-Latin scripts the dashboard is translated into.

1. The note textarea suppresses the global focus outline with no replacement cue

The Description says

Frontend: a NoteSidebar (edit/preview, autosave state, image paste with the meeting's elapsed time), opened from the meeting's overflow menu.

The code does — the textarea that is the editor carries focus:outline-none with no focus-ring, no focus-visible: cue, and no focus-within: cue on the enclosing <aside>, so the element the whole feature exists to type into has no visible focus indicator at all. website/src/apps/meetings/components/NoteSidebar.tsx:211.

Risk — a keyboard-only or low-vision user tabbing into the note editor cannot tell where focus is, which is WCAG 2.4.7 Level AA. The module contract at the top of scripts/check_focus_cue.py states that rule and states that focus:outline-none is a suppressor rather than a cue; the scanner therefore flags this line. That gate is a required check — focus-cue-lint / "Focus Cue Gate", listed in docs/ci/ci-and-reviews.md:126 and wired at .github/workflows/ci.yml:246 — and it is red on the head SHA, so the PR cannot merge in this state.

Required change — append focus-ring to the textarea's className, matching the precedent at website/src/components/AgentPanel.tsx:293, or add a focus-visible: cue on the textarea or a focus-within: cue on the enclosing <aside>. Then re-run FOCUS_CUE_BASE_REF=origin/main python3 scripts/check_focus_cue.py.

2. The note PUT keeps the shared 256 KiB body cap, so the advertised 100k-character ceiling is unreachable in non-Latin scripts

The Description says

Markdown, autosaved, capped at MAX_NOTE_CHARS (100k — a human typing for at most the 4 hours MAX_SESSION_DURATION allows).

The code doeshandle_put_note calls json_body without a max_bytes argument, so the note inherits the shared _common.MAX_BODY_BYTES of 256 KiB, while the sibling whole-document route passes a route-specific MAX_MINUTES_BODY_BYTES sized for its own character cap. src/kiro_crew/apps/builtins/meetings/backend/routes/meeting_lifecycle.py:734. The character cap is enforced, the byte cap is not raised to cover it, and the two disagree for any text above one byte per character.

Risk — the dashboard ships in 12 languages. A Japanese, Chinese, Korean, Hindi, Bengali, or Russian note crosses 256 KiB of UTF-8 JSON at roughly 87,400 characters (about 65,500 for emoji, and about 43,000 for a client that \u-escapes non-ASCII), well under the advertised 100,000. From that point every autosave answers 413 and the frontend surfaces only the generic noteSaveFailed toast ("Could not save your note."), so everything the user types afterwards is silently unsaved — on the one file the code itself calls the thing in this app the user cannot regenerate. No test observes it, because test_put_accepts_a_note_at_the_cap uses "x" * MAX_NOTE_CHARS, which is ASCII.

This is not new arithmetic: test/test_meetings_minutes.py:516, class TestBodyCaps, already pins precisely this invariant on main, its functional case at :538 uses content = "議" * 100_000 — exactly MAX_NOTE_CHARS many CJK characters — asserting len(payload.encode()) > _common.MAX_BODY_BYTES, and its docstring already names the failure mode as the user being able to open the document, edit it, and only then be told it cannot be saved.

Required change — either give the note a route-specific body cap (a MAX_NOTE_BODY_BYTES sized as MAX_MINUTES_BODY_BYTES is, covering 100_000 * 12 plus envelope) and pass it to json_body in handle_put_note, or lower MAX_NOTE_CHARS to a value the shared 256 KiB cap actually covers and say so in the Description. Either way, extend the spec sentence at docs/system-specs/modules/meetings.md:461 on main — the same sentence sits at roughly line 478 in this head, unchanged by the diff — to name the second whole-document route, and mirror TestBodyCaps for the note with a multi-byte case.

@github-actions github-actions Bot added the merge conflict Branch has merge conflicts with its base — author must resolve before merge label Sep 3, 2026
@bolichen97

Copy link
Copy Markdown
Collaborator

Open PR relationship audit

This is a consolidated, point-in-time code-level audit note. It compares complete merge-base diffs and current/merged code; it does not treat a shared topic as duplication or partial coverage as completion.

Relationship findings

  • PR #5738 is OVERLAPPING relative to this PR. The goals differ or the implementations can complement each other; this is not a duplicate claim. Recommended action for PR #5738: CONTINUE_DEVELOPMENT. Independent Meetings features that collide only on shared surfaces (the toolbar, the session return object, the manifest highlight numbering and 13 locale catalogs). Both should land; the second to land renumbers its highlight and resolves the toolbar conflict. Worth noting for the author because the app.json/appManifest.ts collision auto-merges without a conflict marker and would ship a mismatched App Store highlight list. Files: src/kiro_crew/apps/builtins/meetings/app.json, website/src/apps/meetings/MeetingView.tsx, website/src/apps/meetings/hooks/useMeetingSession.ts.
  • PR #7196 is OVERLAPPING relative to this PR. The goals differ or the implementations can complement each other; this is not a duplicate claim. Recommended action for PR #7196: REBASE. Complementary meetings features sharing the manifest, app.json permission list, MeetingView header and useMeetingSession surface. Whichever lands second resolves the highlight renumbering; no code needs to be dropped. Files: website/src/components/appstore/appManifest.ts, src/kiro_crew/apps/builtins/meetings/app.json, website/src/apps/meetings/MeetingView.tsx.
  • PR #8081 is OVERLAPPING relative to this PR. The goals differ or the implementations can complement each other; this is not a duplicate claim. Recommended action for PR #8081: REBASE. Same app and the same shared append points, entirely different user goal. Independent; no coordination needed beyond ordinary locale-file merges. Files: website/src/apps/meetings/api.ts and src/kiro_crew/apps/builtins/meetings/backend/constants.py. The two independent directions used different labels; the matrix conservatively retains OVERLAPPING for coordination.

No PR, Issue, label, branch, or review state was changed by the relationship-note portion of this audit.

@dwu96 dwu96 added the needs-pr-triage PR scanner: awaiting automated triage label Sep 7, 2026
@NicholasRBowers NicholasRBowers added drive-to-green PR claimed by drive-to-green pipeline and removed needs-pr-triage PR scanner: awaiting automated triage labels Sep 7, 2026
@NicholasRBowers

Copy link
Copy Markdown
Contributor

🤖 Kiro Crew [operator: NicholasRBowers#a942f9ca]: 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: Both blocking review findings come with exact file:line fixes — add a focus-ring cue to the NoteSidebar.tsx:211 textarea (clears the Focus Cue Gate) and add a route-specific MAX_NOTE_BODY_BYTES to handle_put_note so a 100k-char CJK/emoji note doesn't 413, with a multi-byte TestBodyCaps mirror and the one-sentence spec update. A rebase clears the inherited backend reds; also fixing the broken source-pattern frontend test and committing the mandatory screenshots.

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

The minutes an agent writes belong to the agent. This adds a note that
belongs to the user: free-form Markdown stored per meeting, saved
explicitly, and kept separate from anything an agent generates.

Images pasted into the note are stored as meeting-scoped attachments
and referenced from the Markdown, so a screenshot taken during a call
lands in the note instead of being lost with the clipboard.
@bolichen97

Copy link
Copy Markdown
Collaborator

Rebased onto main 1192049a by a maintainer as part of the 2026-09-08 open-PR audit (was 1065 commits behind).

Conflicts, resolved additively (no behaviour change to this PR):

  • src/kiro_crew/apps/builtins/meetings/backend/constants.py — kept main's new # importing an existing recording block and appended this PR's # notes block after it.
  • docs/system-specs/modules/meetings.md — 3 hunks: kept both audio.py and images.py file-map rows; kept main's client-supplied-path bullet plus this PR's two note bullets and merged the "no blocking call" list; merged the test inventory so test_meetings_audio_import.py and the two note test files are both listed.

Gates run locally on the rebased head: black, isort, flake8 (changed files), pytest test_meetings_note{,_images}.py test_meetings_minutes.py test_meetings_routes.py 318 passed, tsc --noEmit clean, MeetingsNote.test.tsx green.

One pre-existing failure, unchanged by the rebase: MeetingsTranslation.test.tsx "keeps the two side panels mutually exclusive" — its source regex expects setSidebarOpen(false) adjacent to setTranslationOpen(open => !open), and this PR inserts setNoteOpen(false) between them. It fails identically on your old head b3f19506; the property still holds, the regex needs widening for the third panel. Please fix in your next push.

Please review the resolution. A maintainer push makes the maintainer the last pusher, so a second approver is needed under the repo's last-push rule. Reply here if anything looks wrong.

@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 readiness: action required A blocking check or review needs attention merge conflict Branch has merge conflicts with its base — author must resolve before merge readiness: checking Automated validation is still running labels Sep 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

drive-to-green PR claimed by drive-to-green pipeline fork Pull request from a fork (external contributor) readiness: action required A blocking check or review needs attention

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants