feat: configurable max_objective_chars option for objective/evidence/blocker limits - #38
Conversation
…blocker limits The 4000-character limit on goal objectives, completion evidence, and blockers was hardcoded in validateObjective/validateEvidence and in the create_goal / set_goal / update_goal_objective / update_goal tool schemas (zod max() and maxLength). The schema-side limit also causes clients to silently truncate long objectives before the tool is even called. - Add a max_objective_chars plugin option (default 100000, up from 4000). - Thread it through runtime validation and both V1 (zod) and V2 (JSON schema) tool registrations so runtime and advertised limits always match. - Update the /goal command template to ask for a complete, faithful objective (no compressing/truncating, no substitution with references to external files) instead of the weaker "use the full arguments" wording. - Update the boundary test to exercise the configured limit and reset the module-global limit between tests. Fixes prevalentWare#37
danyel117
left a comment
There was a problem hiding this comment.
Thanks for the contribution. The issue is valid, and the updated /goal wording is a good direction. Before merging, please address these correctness and coverage issues:
-
Scope
max_objective_charsper plugin instance. The currentobjectiveCharLimitinsrc/state.tsis module-global, and every V1/V2 setup overwrites it (src/server.ts). This makes active instances affect each other and can make an already-registered schema disagree with runtime validation. I reproduced this by creating instance A with a limit of 100, then instance B with a limit of 10: A's schema still accepted an 11-character objective, but A's executor rejected it withgoal objective must be at most 10 characters. Please capture the limit in each server/setup instance and pass it explicitly to runtime validation/state operations and to V2 tool schema construction. -
Use consistent normalization and character-counting semantics in schemas and runtime. Runtime currently trims first and counts Unicode code points with
[...value].length, while the V1 Zod schemas validate the untrimmed string and count UTF-16 code units. For example, with a limit of 1,😀passes runtime validation but fails the V1 schema; surrounding whitespace can create another mismatch. Since matching advertised and runtime limits is central to #37, please define one behavior and apply it consistently in V1, V2, and runtime validation. -
Add regression coverage for the new option. The current test change only configures the old 4000 boundary, so it does not exercise the new behavior. Please cover custom and default limits in both V1 and V2, advertised schema limits, objective/evidence/blocker runtime boundaries, simultaneous instances with different limits, and Unicode/whitespace behavior. An assertion for the new command fidelity wording would also protect that part of the fix.
Please also confirm that changing the default from 4000 to 100000 is an intentional product decision, since the issue suggested preserving 4000 for backward compatibility or using a more moderate increase.
For reference, the existing local gate passes (lint, typecheck, 213 tests, build, and pack), but these cases are currently uncovered. Once the changes are pushed, we can approve and run the GitHub Actions workflow.
Address review on prevalentWare#38: - Capture the limit per V1/V2 setup instead of a process-wide setter so concurrent instances keep matching schemas and runtime validation. - Count trimmed Unicode code points in V1 Zod, V2 JSON Schema, and runtime validation (emoji and surrounding whitespace now agree). - Cover custom/default limits, advertised schema max, evidence/blocker, simultaneous instances, Unicode/whitespace, and command wording. - Keep the 100000 default as an intentional replacement for the 4000 defect; document that large objectives are echoed into later prompts.
|
Thanks for the review — the requested correctness and coverage issues are addressed in 996188a.
Local gate: lint, typecheck, 216 tests, build, pack:dry-run. |
danyel117
left a comment
There was a problem hiding this comment.
Thanks for the update. The per-instance limit and runtime Unicode code-point counting are fixed, and the branch-local gate passes (216 tests, lint, typecheck, build, pack). Two blockers remain:
-
Advertised schemas still disagree with runtime/Zod whitespace semantics.
boundedGoalTextSchemavalidates throughvalidateObjective/validateEvidence, which trim before checking emptiness and length (src/server.ts:788-801,src/state.ts:411-423), but its advertisedminLength/maxLengthand the V2 JSON Schema validate the raw string (src/server.ts:801-805). Withmax_objective_chars: 1," a "passes V1 Zod/runtime and persists as"a", while a client honoring the advertised schema rejects its raw length of 3. Conversely," "satisfies advertisedminLength: 1but runtime rejects it as empty. This is the schema/runtime mismatch the prior review asked to eliminate. Please choose one counting/normalization rule that schemas and runtime can both enforce, and add schema-boundary tests. The current V2 tests callexecutedirectly (test/server-v2.test.ts:349-375), bypassing JSON Schema validation, and the emoji/whitespace examples are not configured at the actual boundary. -
The branch conflicts with current
main. GitHub reportsCONFLICTING;git merge-tree --write-tree main 996188areports conflicts insrc/server.ts,test/server.test.ts,test/server-v2.test.ts, and generateddist/server.js. Please rebase/merge currentmain, retain the newer executable V2 command registration plus pause/resume and state-recovery behavior, rebuilddist/server.js, and rerun the full gate.
Non-blocking style cleanup: the changed command-template bullet at src/server.ts:160 is 864 columns, while CONTRIBUTING.md:41 specifies 130-column lines. Please split it while resolving the conflicts.
danyel117
left a comment
There was a problem hiding this comment.
Maintainer integration and independent Claude Opus review complete.
Resolved current-main conflicts while preserving executable V2 goal/pause/resume commands and state-recovery reporting. The submitted-text rule now aligns runtime and advertised schemas: raw Unicode code-point maxLength, pattern: "\S" for non-whitespace content, and trimming only before persistence. Added boundary coverage and retained per-instance limits.
Opus found no merge-blocking issues and independently verified Zod 4.1.8 metadata conversion, valid V2 JSON Schema, all create/update/close paths, current-main ancestry, generated bundle reproducibility, lint, typecheck, 234 tests, and pack dry-run.
Residual risks are non-blocking and documented in review notes: large objectives increase continuation prompt size, invalid limits fall back to default, and there is no configured upper sanity cap.
Summary
Fixes #37.
The 4000-character limit on goal objectives, completion evidence, and blockers is hardcoded in
validateObjective/validateEvidenceand in thecreate_goal/set_goal/update_goal_objective/update_goaltool schemas (zodmax()andmaxLength). The schema-side limit also causes clients to silently truncate long objectives before the tool is even called.Changes
max_objective_charsplugin option; default raised from 4000 to 100000/goalcommand template updated to ask for a complete, faithful objective — no compressing/truncating and no substitution with references or pointers to external files, while restructuring/rephrasing for clarity stays allowed (addresses the over-summarization failure mode from Long objectives get lost: 4000-char hard limit + model summarizes instead of passing verbatim #37 without forcing raw verbatim dumps)Example
{ "plugin": [ ["@prevalentware/opencode-goal-plugin", { "max_objective_chars": 100000 }] ] }Validation
bun run lint✅bun run typecheck✅bun run test— 213/213 pass ✅bun run build+bun run pack:dry-run✅