-
Notifications
You must be signed in to change notification settings - Fork 118
Conversation
@samxu01
samxu01
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Big PR, well-scoped feature. The data model for project + task is reasonable and the room/tab split into PodRoomRouter is a clean extension point. Main concerns are one real security bug and a few data-loss / consistency issues worth fixing before merge.
Please fix before merge:
- Stored XSS via
keyLinks[].url(ProjectPodRoom.tsx:750,podController.ts updatePod).javascript:URIs render into<a href>. Needs scheme validation server-side and guard on render, plusnoopener. - Complete clobbers blocker history (
tasksApi.ts:313). Only flipopen/ stampresolvedAt; preservereason/openedAt/openedBy.
Consistency / tech debt:
updatePodignoresprojectMeta.ownerIds(podController.ts:336). Schema advertises multi-owner, auth check only honorscreatedBy. Either wireownerIdsinto the auth check or drop it from the schema.// @ts-nocheckon 1108-lineProjectPodRoom. Minimum: typeroom,task,podAgentsfrompackages/types.projectMetastored on every pod (Pod.ts:75). Schema defaults populate the subdoc on chat/study/games/dm too. Gate ontype === 'project'.
Not posting inline but worth tracking:
- Optimistic-message coalescing in the socket handler matches on
contentonly — two identical messages in quick succession collapse. Pre-existing pattern elsewhere, not new here. PATCH /:podId/:taskIdacceptsblockeras an opaque object in theallowedlist. Mongoose enum validation will reject invalidseverity, but nothing prevents callers from flippingopenedAt/openedBydirectly. Consider a dedicated/blockerendpoint that stamps those server-side.frontend/start-dev.shusesnpm ci --only=production=false— the--onlyflag was deprecated in npm 7+ and this syntax is malformed.npm ciinstalls devDeps by default; just drop the flag.enqueueTaskAssignedIfNeededfallback lookup does afind({podId, status:'active'})+ in-memory slug match. Fine at current pod sizes; worth an index check if pods grow.PodRoomRouter.tsx:14— 14-line router is a good abstraction; future work should keep this stable when adding more pod types.
Marking as COMMENT; the XSS and blocker-clobber are real defects but contained.
Generated by Claude Code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Stored XSS. link.url is rendered into href= with zero scheme validation, and the backend updatePod stores the string as-is (only trims whitespace). Any pod editor can save javascript:alert(1) — every member who clicks gets XSS.
Fix: validate scheme server-side (allow http:/https:/mailto: only) and guard in the render:
const safeUrl = /^(https?|mailto):/i.test(link.url) ? link.url : '#'; <a href={safeUrl} target="_blank" rel="noopener noreferrer">...
Also add noopener — noreferrer alone doesn't cover older browsers for target="_blank".
Generated by Claude Code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Completing a blocked task $sets the entire blocker object to a fresh default — reason: null, waitingOn: null, openedAt: null, openedBy: null. That erases the audit trail of why/when the task was blocked.
Just flip open and stamp resolvedAt:
'blocker.open': false, 'blocker.resolvedAt': new Date(),
Keep reason, waitingOn, openedAt, openedBy for history.
Generated by Claude Code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The schema introduces projectMeta.ownerIds (plural) but the auth check here only honors the original createdBy. A co-owner added via ownerIds gets 403 when they try to edit project metadata — they paid for the field but can't use it.
Either:
const isOwner = (pod.projectMeta?.ownerIds || []).some( (id) => id.toString() === requesterId?.toString() ); if (!isCreator && !isOwner && !isGlobalAdmin) { ... }
or drop ownerIds from the schema until co-ownership is actually wired.
Generated by Claude Code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// @ts-nocheck on a 1108-line component that's the primary surface for a new feature is expensive tech debt from day one. The rest of the codebase is typed; this file will silently accept shape changes to room.projectMeta, task.blocker, task.assigneeType, etc., and the first signal of a break will be runtime.
Minimum ask: drop @ts-nocheck and at least type the props + the three main state shapes (room, task, podAgents) from packages/types/src/pod.ts. The any-tolerant handlers can stay loose for now; the data surface is what actually needs coverage.
Generated by Claude Code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
projectMeta has field-level defaults but no conditional on type === 'project', so every pod (chat/study/games/dm/...) gets a populated projectMeta: { goal:'', scope:'', successCriteria:[], status:'planning', dueDate:null, ownerIds:[], keyLinks:[] } persisted.
Two small wins: save storage on non-project pods, and make the "is this a project?" check a simple field-presence test. Either mark the subdoc default: undefined and set it explicitly in the project branch of createPod, or gate with required: function() { return this.type === 'project'; }.
Generated by Claude Code
samxu01
commented
May 22, 2026
Hi @MubaiHua — pinging to triage this PR. 2026年05月04日 was the last update and the body lists ~8 substantive items still pending (Create Project Pod wizard, milestone model + sidebar UI, task-state cleanup to todo|in_progress|blocked|in_review|done, richer task detail actions, agent runtime task-progress tools, project timeline, DB/index tuning, doc follow-up).
Is this work still in flight? If yes, what would unblock you — design questions, a base-branch rebase, or pair time on a specific section? If no, happy to close it out as descoped under ADR-011's shell-first track. Either way, thanks for the foundation work here.
This PR introduces a first-pass project pod workspace.
What is included:
projectpod type in backend and shared typesChatandTaskstabsprojectMeta)Still pending:
todo,in_progress,blocked,in_review,doneVerification:
Pod.test.tsxafter the dropdown fix