Let CoS tasks cap review rounds per reviewer via slashdo's ~max=<n> suffix#3146
Merged
Conversation
…3123) slashdo v3.25.0 added a per-entry `~max=<n>` iteration cap on `--review-with` tokens, which is the only way to move the otherwise-hardcoded 3-round caps on the local agent reviewers (codex/claude/agy/grok) and ollama. PortOS emitted the flag but had no way to express a cap, so including a slow local model in a Review Loop chain always cost up to three full review -> fix -> re-review cycles. Adds a `reviewerMaxRounds` token-keyed map alongside the existing `optionalReviewers` (`~opt`) plumbing: a normalizer/resolver pair in cosValidation.js, both `createCosTaskSchema` and `codeReviewSettingsSchema`, the task-metadata sanitizer, every `optionalReviewers` call site, and a numeric cap control on each ReviewerPicker chip (so it lands in the task-add form, the schedule config, Code Review Defaults, and the /do:* run drawer at once). Absent and `0` are deliberately distinct: slashdo reads `~max=0` as "loop until clean" while a missing key keeps its built-in default, so an entry with no usable cap is dropped rather than coerced to 0, and clearing the input deletes the key. `markOptional` becomes `markSuffixes`, emitting `~opt` then `~max=<n>` in slashdo's canonical order. The review-loop follow-up prompt drives its loop in prose rather than by handing slashdo a flag string, so it also spells the caps out — the `equiv` flag string alone would document intent without binding the agent. Also persists `optionalReviewers` in cosTaskStore's addTask, which createCosTaskSchema already validated but the store silently dropped, so the task form's `~opt` badges never survived to task metadata.
…d, not coerced into ~max=0
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
slashdo v3.25.0 (the pinned submodule at
lib/slashdo) added a per-entry~max=<n>iteration cap on--review-withtokens — the only way to move the otherwise-hardcoded 3-round caps on the local agent reviewers (codex/claude/agy/grok) andollama. PortOS emitted the flag but had no way to express a cap, so including a slow local model in a Review Loop chain always cost up to three full review → fix → re-review cycles.This adds a
reviewerMaxRoundstoken-keyed map ({ ollama: 1, '@flaky-bot': 0 }) alongside the existingoptionalReviewers(~opt) plumbing it mirrors:server/lib/cosValidation.js—normalizeReviewerMaxRounds/resolveReviewerMaxRoundsnext to theiroptionalReviewerssiblings, plus a sharednormalizeReviewerTokenso the~optset and the cap map can't disagree about a reviewer's identity.markOptionalbecomesmarkSuffixes, emitting~optthen~max=<n>in slashdo's canonical order from bothbuildReviewersCsvandbuildReviewWithArgs. A lone defaultcopilotcarrying a cap now forces the flag on, the same exemption~optalready had.createCosTaskSchema,codeReviewSettingsSchema(.strict()),slashdoTaskSchema, and the task-metadata sanitizer, keeping the "explicitly empty overrides the default" semanticsoptionalReviewershas.optionalReviewerscall site:codeReview.js(pickCodeReviewDefaults+resolveReviewLoopOptions),agentPromptBuilder.js(all three resolution blocks, both completion-section builders, the slashdo-prune path, and the review-loop follow-up),agentWorktreeCleanup.js(cleanupAgentWorktree+spawnReviewLoopFollowUp),cosTaskGenerator.js(both claim paths + the play-button CSV),cosTaskRoutes.js,prWatcher.js,cosTaskStore.js.ReviewerPickerchip (keyed reviewers and@usernamealike) beside the~optbadge. Since the picker is shared, the control lands inTaskAddForm, the scheduleGlobalConfigControls,CodeReviewDefaultsPanel, andSlashDoRunDrawerat once. Removing a reviewer prunes its cap entry, aswithoutTokenalready does foroptionalReviewers.Absent ≠ 0. slashdo reads
~max=0as "loop until clean" while a missing key keeps its built-in default of 3 — the opposite meanings. So an entry with no usable cap is dropped rather than coerced to0, clearing the input deletes the key instead of writing0, and the builders use aMapwhose.get()distinguishes the two.The review-loop follow-up prompt drives its loop in prose (it isn't slashdo parsing a suffix), so it also spells the caps out — the
equivflag string alone would document intent without binding the agent.0renders there as "loop until clean", never as a zero-round budget.Also fixes a pre-existing drop found while threading:
cosTaskStore'saddTasknever persistedoptionalReviewers, thoughcreateCosTaskSchemavalidated it — so the task form's~optbadges silently fell back to the Code Review Defaults on every task. Without this, the new sibling field would have been the only one of the pair that worked.Test plan
cd server && npm test— the six touched suites pass (lib/validation.test.js,services/codeReview.test.js,services/agentPromptBuilder.test.js,services/cosTaskGenerator.test.js,services/cleanupAgentWorktree.test.js,routes/cos.test.js), covering: normalization (unknown tokens / non-integers / negatives / over-ceiling dropped, an explicit0kept,__proto__key rejected), emission ordering (~optbefore~max),~max=0round-tripping distinctly from no cap,@usernamecaps keyed by the@-form token, the lone-default-copilot flag-forcing, task-over-default precedence including an explicitly empty map, follow-up metadata threading, and the follow-up prompt's prose caps.cd client && npm test— 469 files / 5187 tests pass. NewReviewerPickercases cover the blank-vs-0render, setting a cap on both chip kinds, clearing deleting the key rather than writing0, clamping out-of-range input, cap pruning on reviewer/username removal, and~optstaying independent of the cap.mainshows the same set. The only delta islib/dataRoot.test.js+services/installState.test.js, which fail identically atorigin/maininside this worktree (they assert on the checkout path) and touch nothing in this diff.npx eslintclean on all seven touched client files.Closes #3123