Context
packages/loopover-engine/src/idea-intake.ts:12-14 states the module's own invariant:
Intake bounds — mirror the manifest text-slot handling (focus-manifest.ts): a renter's freeform text is length-capped so one submission can never dominate a public surface.
This is enforced for title (IDEA_TITLE_MAX_CHARS, line 15), body (IDEA_BODY_MAX_CHARS, line 16), and constraints (IDEA_CONSTRAINT_MAX_CHARS, line 18 — enforced at validateIdeaSubmission, line 102: else if (constraints.some((c) => c.length > IDEA_CONSTRAINT_MAX_CHARS)) errors.push("constraint_too_long");).
acceptanceHints (also a renter-supplied string[], same shape as constraints) has no equivalent cap. validateIdeaSubmission's check at lines 104-107 only validates that the field is an array of strings:
const acceptanceHints = input.acceptanceHints;
if (acceptanceHints !== undefined && (!Array.isArray(acceptanceHints) || !acceptanceHints.every((h) => typeof h === "string"))) {
errors.push("acceptance_hints_invalid");
}
No per-entry length check exists — an individual hint of arbitrary length passes validation. acceptanceHints entries are later folded verbatim into AcceptanceCriterion.statement (see the fold at idea-intake.ts:206 and surrounding code), which becomes part of the public constituent-issue body a contributor reads. An unbounded hint can dominate or bloat that public surface exactly the way the module's stated invariant says it shouldn't be able to.
Note: this is distinct from the already-fixed #6766 (blank/whitespace-only hints being wrongly accepted as an objective signal) — that issue was about content quality (blank string), this one is about the missing length bound (arbitrarily long string), a different, still-open gap in the same validation block.
Requirements
- Add a length cap for each
acceptanceHints entry, analogous to IDEA_CONSTRAINT_MAX_CHARS for constraints. Reuse IDEA_CONSTRAINT_MAX_CHARS itself (both are short freeform renter strings of the same shape) rather than introducing a new unrelated constant, unless there's a documented reason acceptanceHints needs a different bound.
validateIdeaSubmission must push a new distinct error code (e.g. acceptance_hint_too_long) when any hint exceeds the cap, following the existing pattern where constraint_too_long is a separate code from constraints_invalid.
- The check must run only when the array-of-strings shape check already passed (mirroring how
constraint_too_long is only checked in the else if branch after the shape check), so a malformed input still surfaces the shape error, not a length error.
Deliverables
Test Coverage Requirements
This repo's Codecov patch gate is 99%+ hard (branch-counted) on every changed line/branch in src/**/packages/**. Cover both the pass and fail boundary conditions listed above, plus the interaction with the array-shape check.
Expected Outcome
A renter can no longer submit an unbounded-length acceptanceHints entry — every renter-supplied freeform text field (title, body, constraints, acceptanceHints) is now length-capped, matching the module's own stated invariant.
Links & Resources
packages/loopover-engine/src/idea-intake.ts:12-18 (the stated invariant + existing constants), :100-107 (the validation block, showing constraints' enforcement vs. acceptanceHints' gap), :206 (where hints are folded into public acceptance-criteria statements). Related, not duplicate: #6766 (blank/whitespace hints, already fixed).
Context
packages/loopover-engine/src/idea-intake.ts:12-14states the module's own invariant:This is enforced for
title(IDEA_TITLE_MAX_CHARS, line 15),body(IDEA_BODY_MAX_CHARS, line 16), andconstraints(IDEA_CONSTRAINT_MAX_CHARS, line 18 — enforced atvalidateIdeaSubmission, line 102:else if (constraints.some((c) => c.length > IDEA_CONSTRAINT_MAX_CHARS)) errors.push("constraint_too_long");).acceptanceHints(also a renter-suppliedstring[], same shape asconstraints) has no equivalent cap.validateIdeaSubmission's check at lines 104-107 only validates that the field is an array of strings:No per-entry length check exists — an individual hint of arbitrary length passes validation.
acceptanceHintsentries are later folded verbatim intoAcceptanceCriterion.statement(see the fold atidea-intake.ts:206and surrounding code), which becomes part of the public constituent-issue body a contributor reads. An unbounded hint can dominate or bloat that public surface exactly the way the module's stated invariant says it shouldn't be able to.Note: this is distinct from the already-fixed #6766 (blank/whitespace-only hints being wrongly accepted as an objective signal) — that issue was about content quality (blank string), this one is about the missing length bound (arbitrarily long string), a different, still-open gap in the same validation block.
Requirements
acceptanceHintsentry, analogous toIDEA_CONSTRAINT_MAX_CHARSforconstraints. ReuseIDEA_CONSTRAINT_MAX_CHARSitself (both are short freeform renter strings of the same shape) rather than introducing a new unrelated constant, unless there's a documented reasonacceptanceHintsneeds a different bound.validateIdeaSubmissionmust push a new distinct error code (e.g.acceptance_hint_too_long) when any hint exceeds the cap, following the existing pattern whereconstraint_too_longis a separate code fromconstraints_invalid.constraint_too_longis only checked in theelse ifbranch after the shape check), so a malformed input still surfaces the shape error, not a length error.Deliverables
acceptanceHintsentry invalidateIdeaSubmission(packages/loopover-engine/src/idea-intake.ts)acceptance_hint_too_longerror code returned inerrorswhen violatedacceptanceHintsentry at exactly the cap passes; one character over the cap returnsacceptance_hint_too_long; the existing "all errors collected in one pass" behavior (not short-circuited) still holds when combined with other invalid fieldsTest Coverage Requirements
This repo's Codecov patch gate is 99%+ hard (branch-counted) on every changed line/branch in
src/**/packages/**. Cover both the pass and fail boundary conditions listed above, plus the interaction with the array-shape check.Expected Outcome
A renter can no longer submit an unbounded-length
acceptanceHintsentry — every renter-supplied freeform text field (title,body,constraints,acceptanceHints) is now length-capped, matching the module's own stated invariant.Links & Resources
packages/loopover-engine/src/idea-intake.ts:12-18(the stated invariant + existing constants),:100-107(the validation block, showingconstraints' enforcement vs.acceptanceHints' gap),:206(where hints are folded into public acceptance-criteria statements). Related, not duplicate: #6766 (blank/whitespace hints, already fixed).