fix(engine): accept the canonical { kind: 'existing', repo } IdeaTarget in validateIdeaSubmission - #9620
fix(engine): accept the canonical { kind: 'existing', repo } IdeaTarget in validateIdeaSubmission#9620tryeverything24 wants to merge 1 commit into
Conversation
…et in validateIdeaSubmission
validateIdeaSubmission only recognised the back-compat bare-string wire
form and { kind: 'provision' }, so the canonical { kind: 'existing',
repo } object — the exact shape the validator itself returns and the
exported IdeaTarget union declares — was rejected with
target_repo_required, breaking the round-trip over its own output. Add
the one missing else-if arm on the existing chain: a kind === 'existing'
object resolves through the same owner/name slug pattern (hoisted to a
shared module-local constant) and reports the same
target_repo_malformed code when repo is missing, non-string, empty, or
not a valid slug. Unrecognised kinds still report target_repo_required;
bare-string and provision behaviour is unchanged.
Closes JSONbored#9609
|
Caution 🛑 LoopOver review result - fixes requiredReview updated: 2026-07-29 00:56:53 UTC
Review summary Nits — 4 non-blocking
CI checks failing
Decision drivers
Context & advisory signals — never blocks the verdict
Linked issue satisfactionAddressed Review context
Contributor next steps
Signal definitions
🧪 Chat with LoopOverAsk LoopOver a question about this PR directly in a comment — grounded only in the same cached, public-safe facts shown above, never a new claim.
Full command reference: https://loopover.ai/docs/loopover-commands 🧪 Experimental — new and may change. 🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed 💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →. Checked by LoopOver, a quiet PR intelligence layer for OSS maintainers.
|
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #9620 +/- ##
==========================================
- Coverage 90.02% 90.01% -0.01%
==========================================
Files 888 888
Lines 111983 111994 +11
Branches 26570 26572 +2
==========================================
+ Hits 100810 100814 +4
- Misses 9843 9846 +3
- Partials 1330 1334 +4
Flags with carried forward coverage won't be shown. Click here to find out more.
|
|
LoopOver is closing this pull request on the maintainer's behalf (CI is failing (codecov/patch)). This is an automated maintenance action — to pursue this change, please open a new pull request with the issues resolved. Closed PRs may be analyzed later to improve review accuracy, but they are not automatically reopened or re-reviewed. |
Closes #9609
Bug
validateIdeaSubmission(packages/loopover-engine/src/idea-intake.ts) rejects the canonical{ kind: "existing", repo }object — the exactIdeaTargetshape the function itself returns and the shape any TypeScript caller building against the exportedIdeaSubmissiontype constructs. ItstargetRepobranch only recognised the back-compat bare"owner/name"string and{ kind: "provision" }; everything else fell through totarget_repo_required. As a result the validator was not idempotent over its own output:validateIdeaSubmission(validateIdeaSubmission(raw).idea)returned{ ok: false, errors: ["target_repo_required"] }for every valid existing-repo submission, while theprovisionvariant round-tripped fine. Reachable from real callers:src/mcp/server.ts(loopover_intake_idea/loopover_plan_idea_claims) andsrc/api/routes.tsthreadvalidated.idea.targetRepointobuildClaimPlan, which already accepts both variants — only the validator lagged.Root cause
When #7635 introduced the
IdeaTargetdiscriminated union, the validator's input handling was only half-updated: theprovisionobject arm was added, but theexistingobject arm was not — the bare-string wire form remained the only accepted spelling of an existing-repo target.Fix (one added
else ifarm, per the required pattern)else ifarm on the existingif / else if / elsechain: an object whosekindis"existing"resolves to{ kind: "existing", repo }whenrepois a string matching the sameowner/nameslug pattern the bare-string branch uses, and pushes the existingtarget_repo_malformedcode whenrepois missing, not a string, empty/whitespace-only, or not a valid slug (a recognised-but-invalid target, matching how the bare-string branch reports a malformed slug).kindis neither"existing"nor"provision"still pushestarget_repo_required, unchanged. Bare-string and{ kind: "provision" }behaviour is unchanged.RED → GREEN
With the fix stashed (current
mainbehavior), the 4 new tests intest/unit/idea-intake-bridge.test.tsfail; all pre-existing tests pass:With the fix applied:
Deliverables asserted by name: the canonical-object accept case (
idea.targetRepodeep-equal to{ kind: "existing", repo: "acme/widgets" }); the round-trip regression test (feeds a validated result's ownideaback in for both union variants and assertsok === truewithtargetRepounchanged);{ kind: "existing", repo: "not-a-slug" }→["target_repo_malformed"];{ kind: "existing" },repo: "", whitespace-only, and non-stringrepo→["target_repo_malformed"];{ kind: "bogus" }and{}→["target_repo_required"]; existing bare-string and provision cases pass unmodified.Gates
npx vitest run test/unit/idea-intake-bridge.test.ts— 35/35 passed.npx vitest run --coverage test/unit/idea-intake-bridge.test.ts—packages/loopover-engine/src/idea-intake.ts: 100% lines (73/73), 100% branches (107/107), 100% functions (18/18) (lcovLF:73 LH:73 BRF:107 BRH:107 FNF:18 FNH:18), so every changed line and both arms of every new conditional in the patch are covered (valid, malformed, missing, empty, and non-stringrepo, plus the unrecognised-kindfall-through).npm run build --workspace @loopover/engine— clean.npm run typecheck:root(tsc --noEmit) — clean.git diff --check— clean.