Skip to main content
pm-organization-task-selection Priority algorithm for selecting next PRD task based on category, dependencies, and risk
跳到安装 Skills Marketplace 发现并探索由社区构建的 Agent Skills
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/feliperyba/ralph-orchestra --skill pm-organization-task-selection命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
下载 Zip 下载中... 同仓库更多 Skills Complete Developer workflow orchestration - task research sequence, implementation flow, validation gates, PRD synchronization, exit conditions.
Complete Game Designer workflow - skill invocation protocol, GDD creation, playtest flow with GDD review, design sessions. MUST load before starting assignments.
Complete PM Coordinator workflow - task assignment, project orchestration, PRD management, worker coordination. Use proactively when starting PM agent work.
name pm-organization-task-selection description Priority algorithm for selecting next PRD task based on category, dependencies, and risk category organization
Task Selection Skill
"Fail fast on risky work – tackle hard problems before easy wins."
Parallel Task Opportunity Check (MANDATORY FIRST STEP)
⚠️ CRITICAL: BEFORE selecting any single task, ALWAYS check if parallel assignment is possible.
When both Developer AND Tech Artist are idle, the PM MUST check for parallel work opportunities. This happens BEFORE the standard single-task selection flow.
Check Steps
const prd = readJson ('prd.json' );
const backlog = readJson (prd.backlogFile || 'prd_backlog.json' );
const allItems = [...prd.items , ...backlog.backlogItems ];
const developerIdle =
prd.agents .developer ?.status === 'idle' || prd.agents .developer ?. === ;
techartistIdle =
prd. . ?. === || prd. . ?. === ;
(developerIdle && techartistIdle) {
devCategories = [ , , ];
devTasks = allItems
. (
!item. &&
item. === &&
devCategories. (item. ) &&
item. . ( allItems. ( i. === depId)?. === )
)
. (priorityComparator);
artistCategories = [ , , ];
artistTasks = allItems
. (
!item. &&
item. === &&
artistCategories. (item. ) &&
item. . ( allItems. ( i. === depId)?. === )
)
. (priorityComparator);
(devTasks. > && artistTasks. > ) {
devTask = devTasks[ ];
artistTask = artistTasks[ ];
(! (devTask, artistTask)) {
(devTask, );
(artistTask, );
;
}
}
}
status
'awaiting_pm'
const
agents
techartist
status
'idle'
agents
techartist
status
'awaiting_pm'
if
const
'architectural'
'functional'
'integration'
const
filter
(item ) =>
passes
status
'pending'
includes
category
dependencies
every
(depId ) =>
find
(i ) =>
id
passes
true
sort
const
'visual'
'shader'
'polish'
const
filter
(item ) =>
passes
status
'pending'
includes
category
dependencies
every
(depId ) =>
find
(i ) =>
id
passes
true
sort
if
length
0
length
0
const
0
const
0
if
areTasksConflicting
assignTask
'developer'
assignTask
'techartist'
return
'parallel_assigned'
Conflict Detection Function function areTasksConflicting (task1, task2 ) {
const paths1 = task1.files || [];
const paths2 = task2.files || [];
for (const p1 of paths1) {
for (const p2 of paths2) {
const dir1 = p1.substring (0 , p1.lastIndexOf ('/' ));
const dir2 = p2.substring (0 , p2.lastIndexOf ('/' ));
if (dir1 === dir2) return true ;
}
}
const deps1 = new Set (task1.dependencies || []);
const deps2 = new Set (task2.dependencies || []);
for (const dep of deps1) {
if (deps2.has (dep)) return true ;
}
return false ;
}
When to Use This Skill
Assigning a new task from the PRD
prd.json.session.currentTask is null or task status is "passed"
After retrospective completes
⚠️ FIRST check for parallel assignment, then proceed to single-task selection if parallel is not possible.
Quick Start
const prd = readJson ('prd.json' );
const backlog = readJson ('prd.backlogFile' || 'prd_backlog.json' );
const allItems = [...prd.items , ...backlog.backlogItems ];
const incomplete = allItems.filter ((item ) => !item.passes );
const unblocked = incomplete.filter ((item ) =>
item.dependencies .every ((depId ) => allItems.find ((i ) => i.id === depId)?.passes === true )
);
const next = unblocked.sort (priorityComparator)[0 ];
PRD Backlog Architecture Tasks are split between two files:
File Contains Size prd.jsonTop 5 active queue ~5 tasks prd_backlog.jsonRemaining backlog ~70 tasks
Only PM and Game Designer need full backlog access. Workers only read their assigned task.
Reading Tasks from Both Files When selecting tasks, PM must:
Read prd.json for active queue
Read prd.backlogFile (defaults to prd_backlog.json)
Combine arrays: allItems = [...prd.items, ...backlog.backlogItems]
Apply filtering/sorting on combined array
If selected task is in backlog, move it to prd.json.items
Automatic Backlog Refill After a task completes (status → "completed") and is removed from active queue:
if (prd.items .length < 5 ) {
const backlog = readJson (prd.backlogFile );
const allTasks = [...prd.items , ...backlog.backlogItems ];
const candidates = backlog.backlogItems .filter ((item ) => {
if (item.passes ) return false ;
const depsMet = item.dependencies .every (
(depId ) => allTasks.find ((t ) => t.id === depId)?.passes === true
);
return depsMet;
});
const tierOrder = {
TIER_0_BLOCKER : 1 ,
TIER_1_FOUNDATION : 2 ,
TIER_2_ECONOMY : 3 ,
TIER_3_SUPPORT : 4 ,
};
candidates.sort ((a, b ) => {
const tierDiff = (tierOrder[a.tier ] || 99 ) - (tierOrder[b.tier ] || 99 );
if (tierDiff !== 0 ) return tierDiff;
return (
(a.priority === 'critical' ? 1 : a.priority === 'high' ? 2 : 3 ) -
(b.priority === 'critical' ? 1 : b.priority === 'high' ? 2 : 3 )
);
});
if (candidates.length > 0 ) {
const toMove = candidates[0 ];
backlog.backlogItems = backlog.backlogItems .filter ((i ) => i.id !== toMove.id );
prd.items .push (toMove);
prd.session .stats .backlogSize = backlog.backlogItems .length ;
prd.session .stats .activeQueueSize = prd.items .length ;
writeJson ('prd_backlog.json' , backlog);
writeJson ('prd.json' , prd);
}
}
Decision Framework Category Priority When to Use architectural1 (Highest) Affects entire codebase structure integration2 Reveals incompatibilities early spike / unknown3 Exploratory work, reduces uncertainty functional4 Standard feature implementation polish5 (Lowest) UI, optimization, documentation
Progressive Guide
Level 1: Basic Selection Select first incomplete, unblocked task (reads from both files):
const prd = readJson ('prd.json' );
const backlog = readJson (prd.backlogFile || 'prd_backlog.json' );
const allItems = [...prd.items , ...backlog.backlogItems ];
const next = allItems.find (
(item ) => !item.passes && item.dependencies .every ((d ) => allItems.find ((i ) => i.id === d)?.passes )
);
Level 2: Priority-Weighted Selection Apply category priority ordering (reads from both files):
const prd = readJson ('prd.json' );
const backlog = readJson (prd.backlogFile || 'prd_backlog.json' );
const allItems = [...prd.items , ...backlog.backlogItems ];
const priorityOrder = {
architectural : 1 ,
integration : 2 ,
unknown : 3 ,
spike : 3 ,
functional : 4 ,
polish : 5 ,
};
const priorityValue = { high : 1 , medium : 2 , low : 3 };
const incomplete = allItems.filter ((item ) => !item.passes );
const unblocked = incomplete.filter ((item ) =>
item.dependencies .every ((d ) => allItems.find ((i ) => i.id === d)?.passes )
);
unblocked.sort ((a, b ) => {
const catDiff = priorityOrder[a.category ] - priorityOrder[b.category ];
if (catDiff !== 0 ) return catDiff;
return priorityValue[a.priority ] - priorityValue[b.priority ];
});
Level 3: Risk-Adjusted Selection Consider retry count and complexity (reads from both files):
const prd = readJson ('prd.json' );
const backlog = readJson (prd.backlogFile || 'prd_backlog.json' );
const allItems = [...prd.items , ...backlog.backlogItems ];
if (task.retryCount >= 3 ) {
logWarning (`Task ${task.id} failed ${task.retryCount} times` );
}
const unblockScore = allItems.filter ((i ) => i.dependencies .includes (task.id )).length ;
Anti-Patterns
Select tasks with unmet dependencies
Assign new task while prd.json.session.currentTask is not null
Skip retrospective to assign faster
Assign multiple tasks to the SAME agent simultaneously
Assign parallel tasks that modify the same files/directories
Skip backlog refill when prd.json.items.length < 5
Only read prd.json without checking prd_backlog.json
Leave completed tasks in prd.json instead of moving to completed file
Verify all dependencies have passes: true (check across both files)
Wait for QA validation before assigning next
Complete retrospective before clearing prd.json.session.currentTask
Log selection rationale in progress file
Assign parallel tasks to Developer AND Tech Artist when safe (see Parallel Assignment below)
Read both prd.json and prd_backlog.json when selecting tasks
Automatically refill when active queue drops below 5 tasks
Move completed tasks to prd_completed.json and refill queue
Parallel Task Assignment (Git Worktree Support) With git worktrees, Developer and Tech Artist can work simultaneously on non-conflicting tasks.
When to Assign Parallel Tasks Check these conditions before assigning parallel tasks:
Both agents are idle:
prd.json.agents.developer.status === "idle" (or "awaiting_pm")
prd.json.agents.techartist.status === "idle" (or "awaiting_pm")
Tasks are non-conflicting:
Different file paths (e.g., src/hooks/ vs src/assets/)
No shared dependencies
Can be tested independently
Each agent has their own worktree:
Developer works in ../developer-worktree on developer-worktree branch
Tech Artist works in ../techartist-worktree on techartist-worktree branch
Conflict Detection Matrix Developer Category Tech Artist Category File Path Overlap? Safe for Parallel? architectural (src/hooks, src/stores, src/server)visual (src/assets)❌ No ✅ Yes functional (src/components/* logic)shader (src/vfx)❌ No ✅ Yes integration (src/utils)polish (src/styles)❌ No ✅ Yes architectural (src/components)visual (src/components)⚠️ Yes ❌ No - sequential only functional (public/)visual (public/)⚠️ Yes ❌ No - sequential only
Parallel Assignment Algorithm
const developerIdle = prd.agents .developer ?.status === 'idle' ;
const techartistIdle = prd.agents .techartist ?.status === 'idle' ;
if (developerIdle && techartistIdle) {
const devTask = findNextTaskForAgent ('developer' , allItems);
const artistTask = findNextTaskForAgent ('techartist' , allItems);
if (areTasksNonConflicting (devTask, artistTask)) {
assignTask (devTask, 'developer' );
assignTask (artistTask, 'techartist' );
return ;
}
}
File Path-Based Conflict Detection function areTasksNonConflicting (task1, task2 ) {
const devPaths = [
'src/hooks/' ,
'src/stores/' ,
'src/server/' ,
'src/utils/' ,
'src/types/' ,
'test/' ,
];
const artistPaths = [
'src/assets/' ,
'src/vfx/' ,
'src/styles/' ,
'public/textures/' ,
'public/models/' ,
];
const task1Path = guessFilePathFromTask (task1);
const task2Path = guessFilePathFromTask (task2);
const task1InDev = devPaths.some ((p ) => task1Path?.startsWith (p));
const task2InArtist = artistPaths.some ((p ) => task2Path?.startsWith (p));
return task1InDev && task2InArtist;
}
Parallel Assignment Steps
Check both Developer and Tech Artist are idle
Find highest-priority incomplete task for each agent
Verify tasks are non-conflicting (different file paths, no shared deps)
Assign to Developer (5-step atomic process)
Assign to Tech Artist (5-step atomic process)
Exit - both agents work in parallel in their worktrees
Checklist ⚠️ CRITICAL: New Phased Workflow Must Complete Before Assignment
When a task status is "passed", the following phases MUST complete in order:
Retrospective (worker contributions only) → retrospective_synthesized
Playtest Session (Game Designer validates) → playtest_complete
PRD Refinement (optional, if needed) → prd_refinement or skip
Acceptance Criteria (MANDATORY - Game Designer provides) → task_ready
Skill Research (PM improves ALL skills) → completed
ONLY when status is "completed" may you set prd.json.session.currentTask = null and select the next task.
⚠️ NEVER assign a task without acceptance criteria from Game Designer.
After Selecting a Task Once a task is selected, you MUST update it in the PRD before assigning:
Read prd.json
Find the selected task by ID
Update the task with:
{
"status" : "assigned" ,
"assignedAt" : "{{ISO_TIMESTAMP}}"
}
Write back to prd.json
Then update session state in prd.json.session:
{
"session" : {
"currentTask" : "{{taskId}}" ,
"currentTaskStatus" : "assigned" ,
"lastAssignmentTime" : "{{ISO_TIMESTAMP}}"
}
}
⚠️ This is Step 1 of the 5-step atomic assignment process. See PM AGENT.md for complete flow.
Completed Task Cleanup (MANDATORY After Retrospective) ⚠️ CRITICAL: After a task completes retrospective (status: "completed"), you MUST:
Move completed task to prd_completed.json
Remove it from prd.json.items array
Refill from backlog if prd.json.items.length < 5
Step-by-Step Cleanup Procedure
const prd = readJson ('prd.json' );
const completedTasks = prd.items .filter (
(item ) => item.status === 'completed' && item.passes === true
);
for (const task of completedTasks) {
const entry = {
id : task.id ,
title : task.title ,
category : task.category ,
tier : task.tier ,
completedAt : task.completedAt || task.qaValidatedAt ,
notes : task.notes || '' ,
validationSummary : task.validationResults || 'PASSED' ,
};
appendFileSync ('prd_completed.json' , JSON .stringify (entry, null , 2 ) + '\n\n' );
}
prd.items = prd.items .filter ((item ) => !(item.status === 'completed' && item.passes === true ));
prd.completedTasks =
parseInt (readFileSync ('prd_completed.json' , 'utf8' ).match (/"id":/g )?.length || 0 ) +
completedTasks.length ;
if (prd.items .length < 5 ) {
const backlog = readJson (prd.backlogFile || 'prd_backlog.json' );
const allTasks = [...prd.items , ...backlog.backlogItems ];
const candidates = backlog.backlogItems .filter ((item ) => {
if (item.passes || item.status === 'deferred' ) return false ;
const depsMet =
item.dependencies ?.every ((depId ) => allTasks.find ((t ) => t.id === depId)?.passes === true ) ??
true ;
return depsMet;
});
const tierOrder = {
TIER_0_BLOCKER : 1 ,
TIER_1_FOUNDATION : 2 ,
TIER_2_ECONOMY : 3 ,
TIER_3_SUPPORT : 4 ,
};
candidates.sort ((a, b ) => {
const tierDiff = (tierOrder[a.tier ] || 99 ) - (tierOrder[b.tier ] || 99 );
if (tierDiff !== 0 ) return tierDiff;
return (
(a.priority === 'critical' ? 1 : a.priority === 'high' ? 2 : 3 ) -
(b.priority === 'critical' ? 1 : b.priority === 'high' ? 2 : 3 )
);
});
while (prd.items .length < 5 && candidates.length > 0 ) {
const toMove = candidates.shift ();
backlog.backlogItems = backlog.backlogItems .filter ((i ) => i.id !== toMove.id );
prd.items .push (toMove);
}
writeJson (prd.backlogFile || 'prd_backlog.json' , backlog);
}
prd.session .stats .activeQueueSize = prd.items .length ;
prd.session .stats .completed = prd.completedTasks ;
prd.session .stats .backlogSize = backlog.backlogItems .length ;
writeJson ('prd.json' , prd);
Cleanup Timing
✅ AFTER retrospective completes (task status = "completed", passes = true)
✅ BEFORE selecting next task (keeps prd.json clean)
❌ NOT during task assignment (too many file operations)
Cleanup Checklist
Reference
Data-Driven Task Selection
JSON-Based Level Design Tasks When assigning tasks related to JSON level data:
Verification Requirements:
Level JSON files must have valid schema
Coordinates must be within world bounds
Material/pig/bird types must be valid enums
Star thresholds should follow progressive formula
Multiple solution paths must exist
Test Plan Considerations:
Unit tests: Schema validation with Ajv
E2E tests: Load and verify level data
Manual tests: Play through each level
Priority Adjustment for Data Tasks Task Type Base Priority Adjustment Level JSON creation functional+1 if blocks progression Schema validation architectural+2 (data integrity) Asset loading architectural+1 (blocks gameplay)