feat: file new sessions into a sidebar folder at creation (#6118) - #6146
Conversation
GPT 5.6 Review — ✅ no blocking findingsGPT 5.6 completed its review of This comment is updated in place on each push. Review detailsNo findings. False positive or not applicable? A repository writer can comment: |
Design Review (Fable 5) — ✅ PASSDesign-level review of Design-Verdict: PASS Root-cause fix at the right seam: filing becomes part of creation under the existing tree-shaping gate, and the instruction-layer workaround is deleted rather than kept alongside. Suggestions
[DESIGN-REVIEWED] af94bb7 |
First Principles Review (Fable 5) — 🟡 CONCERNSPremise-level review of All evidence is verified. The review contract's output follows. First-Principles-Verdict: CONCERNS The feature is a clean cause-level fix; the MCP-side app-scope pre-check is a second copy of the endpoint's rule guarding a residue this PR accepts one branch below. What this change shipsIntent: let a caller create a session already filed in a sidebar folder, closing the create-then-move window — an ADDITION built as the root-cause fix a prior review named.
Watch
Subtractions
[FIRST-PRINCIPLES-REVIEWED] af94bb7 |
Opus 4.8 Review — ✅ no blocking findingsReviewed Review detailsBoth candidates fail falsification. Candidate 1 claims Candidate 2 is self-described as latency, not correctness: No grounded Step-2 additions survived scrutiny; the empty-window merge branch is guarded under No findings. [OPUS-REVIEWED] af94bb7 Verdict parsed from the review's SHA-scoped output markers for commit False positive or not applicable? A repository writer can comment: |
73ca866 to
6e77105
Compare
|
/ai-review override gpt e7cfc1c: The folder-delete-during-birth-persist interleave is the move path's own accepted residual, not a new defect: the delete sweep unfiles the published newborn exactly as it unfiles a just-moved session (deletion is the user's newer intent), the create still returns a live persisted session, and closing it would require holding the folder-store lock across file IO on the event loop; the F2 finding (app-scoped segment creation) IS fixed in this head. |
6e77105 to
e7cfc1c
Compare
Human judgment recorded@CrysisDeu marked the gpt AI finding as false positive, not applicable, or explicitly accepted for
This decision applies only to this commit. A new push requires a new judgment. |
|
Human judgment recorded, but the following reviewer lane(s) could not be re-run automatically: GPT 5.6. Re-run the lane's latest workflow run for |
|
/ai-review override gpt d157416: Same disposition as the prior head (rebase onto post-#6109 main only): the folder-delete-during-birth-persist interleave is the move path's own accepted residual — the delete sweep unfiles the published newborn exactly as it unfiles a just-moved session, deletion being the user's newer intent, and closing it would require holding the folder-store lock across file IO on the event loop. |
|
AI-review override not recorded: |
e7cfc1c to
d157416
Compare
|
🤖 Kiro Crew [operator: NicholasRBowers#a942f9ca] Drive-to-green update — head Root cause of everything red on the prior head: the branch's merge with Changes in this push (single commit, rebased onto current main,
Local gates: isort / flake8 / mypy / black(changed files) all clean; full backend suite: 0 failures attributable to this diff (95 host-env failures reproduce byte-identically on pristine main — userns EPERM / home-dir classification family). Local model-pinned reviewers: Opus Authorship: single commit preserving Zezhen Xu (@CrysisDeu) as author (noreply identity), with a Note on the reviewed birth-write race (folder deleted while the birth persist is in flight): left as the code's documented accepted residual — the dangling |
session_create had no folder argument, so a caller that wanted a session filed in a sidebar folder had to make two calls (session_create then chat_folder_move_session), and a folder deleted between the two left the session created but unfiled -- a window the instruction layer could narrow but not close. Add an optional `folder` to session_create (a folder id or '/'-separated human path, resolved with chat_folder_create's `parent` semantics -- missing segments created behind the existing tree-shaping gate) and file the slot as part of creation: - validation.py: SESSION_CREATE_SCHEMA gains `folder`, bounded like every other folder reference (_ARTIFACT_FOLDER_REF_MAX). - mcp_dashboard.py: session_create resolves the reference through _refuse_tree_shaping_if_unverifiable + _ensure_chat_folder_path (the same gate folder creation already rides, not a second authorization path) and posts the resolved folder_id to the create route. An unresolvable folder refuses the whole create. - dashboard/session_control.py: create_session assigns folder_id inside the same synchronous window that configures the slot (no observable unfiled state), re-confirms existence under the folder-store lock (_unhide_folder, keeping the move path's Model-B un-hide) as the last suspension before the re-gate, and carries the placement in the persist-at-birth metadata so it survives a restart. - goal-conductor SKILL.md: the create-then-move dispatch workaround is deleted; dispatch passes `folder` at session_create. Failure semantics: an unresolvable or deleted folder refuses the whole create (folder_not_found) -- nothing exists yet, so refusal loses nothing, matching the move path's posture. App-scoped callers are refused before any folder handling (app_scoped_caller), which subsumes the move path's app-ownership rule at this entry point. Closes #6118 The empty-window forced-save merge carries every slot-owned field the force/closed save paths persist through it (tags, pin, pinned title, artifact binding, project, mode switch, close), not only folder_id, and reads slot state under the history lock at write time so concurrent force-saves cannot commit a stale aggregate out of order; an unreadable-record merge skip raises instead of reporting a durable write, so close rolls back and best-effort savers re-arm the retry -- a folder-only merge acknowledged a tag assignment, a pin, or a close on a message-less newborn and silently dropped it on restart. Original author: Zezhen Xu (CrysisDeu). Rebased onto current main and the dropped chat_persistence.py forced-save hunk restored by Kiro Crew. Co-authored-by: Kiro Crew <noreply@kirodotdev.github.io>
7994f9e to
af94bb7
Compare
|
🤖 Kiro Crew [operator: NicholasRBowers#a942f9ca]
Verified real: |
|
🤖 Kiro Crew [operator: NicholasRBowers#a942f9ca]
The pre-check is not a pure duplicate: it refuses an app-scoped caller before |
|
🤖 Kiro Crew [operator: NicholasRBowers#a942f9ca]
Acknowledged as the review itself notes: the falsy-vs-absent semantics genuinely differ (a merge cannot delete keys; the reconstructive full save expresses "cleared" as absence), so the two shapes cannot share one literal enumeration without changing the full save's on-disk format — out of scope for a drive that restores a dropped hunk. The drift risk is exactly what |
|
🤖 Kiro Crew [operator: NicholasRBowers#a942f9ca]
The reformat came with the original feature commit (the author black-formatted the file they were substantially extending and pruned it from |
|
🤖 Kiro Crew [operator: NicholasRBowers#a942f9ca] ✅ Review-ready at head
Auto-merge is not armed (drive-to-green never merges) — ready for maintainer review and merge. Closes #6118 on merge. |
Open PR relationship auditThis 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
No PR, Issue, label, branch, or review state was changed by the relationship-note portion of this audit. |
Summary
session_createhad no folder argument, so a caller that wanted a session filed in a sidebar folder had to make two calls (session_createthenchat_folder_move_session). The window between the two was a real defect path: a folder deleted in between left the session created but unfiled — PR #6109 hardened the goal-conductor skill's instructions around it but explicitly could not close that case, and both the Design Review and First Principles lanes there named the missing field as the root cause.This PR adds an optional
foldertosession_create(a folder id or/-separated human path, resolved withchat_folder_create'sparentsemantics — missing segments created behind the existing tree-shaping gate) and files the slot as part of creation, then deletes the instruction-layer workaround.Closes #6118
What changed
validation.py—SESSION_CREATE_SCHEMAgainsfolder, bounded like every other folder reference (_ARTIFACT_FOLDER_REF_MAX).mcp_dashboard.py—session_createresolves the reference through_refuse_tree_shaping_if_unverifiable+_ensure_chat_folder_path(the same gate folder creation already rides, not a second authorization path) and posts the resolvedfolder_idto the create route.dashboard/session_control.py—create_sessionconfirms folder existence read-only under the folder-store lock (read_folders) as the last suspension before the re-gate, assignsfolder_idinside the same synchronous window that configures the slot, holdssuspend_slots_pushacross the whole allocation-to-persist span (the slot's first broadcast frame already shows it filed; a slot whose birth write fails is never broadcast), carries the placement in the persist-at-birth metadata, and applies the move path's Model-B un-hide only after the filing has landed.dashboard/chat_persistence.py— a forced save of a message-less slot now mergesfolder_idinto an existing metadata line. Birth metadata made empty sessions durable, which made the save path's empty-window early return newly consequential: without the merge, moving/unfiling an empty newborn (or deleting its folder) would not persist, and a restart would resurrect the birth placement. No line is ever invented for a plain empty tab.goal-conductor/SKILL.md— thechat_folder_createprecondition step and the separatechat_folder_move_sessionstep are deleted; dispatch passesfolderatsession_create.docs/system-specs/modules/session-control.mdupdated in the same commit.Failure semantics (decision, per the issue)
An unresolvable or mid-create-deleted folder refuses the whole create (
folder_not_found): the caller asked for a session filed in this folder, "created but unfiled" would silently honor half of that, and no session exists yet so refusal loses nothing — matching the move path's posture. Path segments already created by the mkdir-p walk persist on a refused create and are reported in the error (created folder path: …), the same partial-report posturechat_folder_createtakes, since folder deletion is deliberately not a capability this server has.Authorization adds no new path: app-scoped callers are refused before any folder handling (
app_scoped_caller, strictly stronger than the move path's app-ownership rule at this entry point), and unverifiable/delegated callers are refused by the tree-shaping gate plus the session-control strict-identity check.Pre-push review
Two blind model-pinned lanes (mirroring the CI reviewers) ran before the PR: both blocked on real defects, all fixed with pinning tests —
get_or_create_slotbroadcasts on a leading edge, so the new slot could render unfiled for a frame → allocation-to-persist now runs undersuspend_slots_push(the move path's own pattern).read_folders); un-hide runs only after the filing lands.chat_persistence.pyabove.One advisory rebutted with evidence: the success message's folder label resolves correctly for freshly created segments because
_ensure_chat_folder_pathappends created rows into the folder list in place before the path render.Testing
suspend_slots_push.folderis omitted.