Skip to content

fix(engine): accept the canonical { kind: 'existing', repo } IdeaTarget in validateIdeaSubmission - #9620

Closed
tryeverything24 wants to merge 1 commit into
JSONbored:mainfrom
tryeverything24:fix-9609
Closed

fix(engine): accept the canonical { kind: 'existing', repo } IdeaTarget in validateIdeaSubmission#9620
tryeverything24 wants to merge 1 commit into
JSONbored:mainfrom
tryeverything24:fix-9609

Conversation

@tryeverything24

Copy link
Copy Markdown
Contributor

Closes #9609

Bug

validateIdeaSubmission (packages/loopover-engine/src/idea-intake.ts) rejects the canonical { kind: "existing", repo } object — the exact IdeaTarget shape the function itself returns and the shape any TypeScript caller building against the exported IdeaSubmission type constructs. Its targetRepo branch only recognised the back-compat bare "owner/name" string and { kind: "provision" }; everything else fell through to target_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 the provision variant round-tripped fine. Reachable from real callers: src/mcp/server.ts (loopover_intake_idea / loopover_plan_idea_claims) and src/api/routes.ts thread validated.idea.targetRepo into buildClaimPlan, which already accepts both variants — only the validator lagged.

Root cause

When #7635 introduced the IdeaTarget discriminated union, the validator's input handling was only half-updated: the provision object arm was added, but the existing object arm was not — the bare-string wire form remained the only accepted spelling of an existing-repo target.

Fix (one added else if arm, per the required pattern)

  • Added a single else if arm on the existing if / else if / else chain: an object whose kind is "existing" resolves to { kind: "existing", repo } when repo is a string matching the same owner/name slug pattern the bare-string branch uses, and pushes the existing target_repo_malformed code when repo is 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).
  • The inline regex was hoisted to a module-local constant so both existing-repo arms test the identical pattern — no new exported symbol, no new error code.
  • An object whose kind is neither "existing" nor "provision" still pushes target_repo_required, unchanged. Bare-string and { kind: "provision" } behaviour is unchanged.

RED → GREEN

With the fix stashed (current main behavior), the 4 new tests in test/unit/idea-intake-bridge.test.ts fail; all pre-existing tests pass:

Tests  4 failed | 31 passed (35)
  × accepts the canonical { kind: 'existing', repo } object the validator itself returns (#9609)
  × round-trips its own output: re-validating a validated idea succeeds with targetRepo unchanged (#9609)
  × flags a recognised-but-malformed { kind: 'existing' } repo as target_repo_malformed (#9609)
  × flags an { kind: 'existing' } target whose repo is missing, empty, or not a string as target_repo_malformed (#9609)

With the fix applied:

Test Files  1 passed (1)
     Tests  35 passed (35)

Deliverables asserted by name: the canonical-object accept case (idea.targetRepo deep-equal to { kind: "existing", repo: "acme/widgets" }); the round-trip regression test (feeds a validated result's own idea back in for both union variants and asserts ok === true with targetRepo unchanged); { kind: "existing", repo: "not-a-slug" }["target_repo_malformed"]; { kind: "existing" }, repo: "", whitespace-only, and non-string repo["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.tspackages/loopover-engine/src/idea-intake.ts: 100% lines (73/73), 100% branches (107/107), 100% functions (18/18) (lcov LF: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-string repo, plus the unrecognised-kind fall-through).
  • npm run build --workspace @loopover/engine — clean.
  • npm run typecheck:root (tsc --noEmit) — clean.
  • git diff --check — clean.

…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
@loopover-orb

loopover-orb Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Caution

🛑 LoopOver review result - fixes required

Review updated: 2026-07-29 00:56:53 UTC

2 files · 1 AI reviewer · no blockers · CI failing · unstable

🛑 Suggested Action - Fix Blockers

Review summary
This PR adds a missing `else if` arm to `validateIdeaSubmission` so the canonical `{ kind: 'existing', repo }` object form is accepted, matching the bare-string branch's slug validation via a hoisted shared regex constant. The fix is correctly targeted at the root cause (the validator's input handling, not a downstream caller), and the new tests (round-trip, malformed repo, missing/empty/non-string repo) exercise the real branches the diff adds. The PR closes the named issue #9609 and the change is narrowly scoped to the described bug.

Nits — 4 non-blocking
  • The `else if` chain in idea-intake.ts now has near-duplicate type-guard boilerplate (`typeof input.targetRepo === 'object' && input.targetRepo !== null && (...).kind === ...`) for both 'existing' and 'provision' arms — consider a small helper to reduce repetition.
  • codecov/patch failed at 69.23% against a 99% target; given the new tests look substantive, this is likely just a coverage-ratio artifact of the small diff size rather than missing tests, but worth a quick look at which line/branch is uncovered.
  • Consider factoring the shared `typeof x === 'object' && x !== null && (x as Record<string,unknown>).kind === ...` pattern used for both 'existing' and 'provision' into a tiny local helper to shorten the branch and avoid repeated casts (idea-intake.ts:107-114).
  • Check the codecov/patch report to see exactly which line/branch is uncovered, since the added tests appear to cover both the happy path and the malformed/missing-repo path.

CI checks failing

  • codecov/patch — 69.23% of diff hit (target 99.00%)

Decision drivers

  • ✅ Code review — No blockers (1 reviewer)
  • ✅ Gate result — Passing (No configured blocker found.)
Context & advisory signals — never blocks the verdict
Signal Result Evidence
Linked issue ✅ Linked #9609
Related work ✅ No active overlap found No same-issue or scoped active PR overlap found.
Change scope ✅ 20/20 Low review scope from cached public metadata (1 linked issue).
Validation posture ❌ 5/25 Preflight is holding this PR: the review lane is unavailable, so it is not ready for automated review.
Contributor workload ✅ 10/10 Author activity: 72 registered-repo PR(s), 38 merged, 4 issue(s).
Contributor context ✅ Confirmed Gittensor contributor tryeverything24; Gittensor profile; 72 PR(s), 4 issue(s).
Improvement ✅ Minor risk: clean · value: minor · LLM: significant
Linked issue satisfaction

Addressed
The diff adds exactly one new else-if arm mirroring the required pattern, reusing the hoisted slug regex and the two existing error codes to accept {kind:'existing', repo} and reject malformed/missing/non-string repo values with target_repo_malformed, while bogus kinds still yield target_repo_required.

Review context
  • Author: tryeverything24
  • Role context: outside_contributor
  • Public audience mode: oss maintainer
  • Lane context: Repository is registered but has no active allocation in the current snapshot.
  • Public profile languages: not available
  • Official Gittensor activity: 72 PR(s), 4 issue(s).
  • PR-specific overlap: none found.
Contributor next steps
  • Start here: Await review-lane availability.
  • Then work through the remaining 2 steps in the Signals table above.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Change scope = cached public metadata such as size labels, draft state, and review-burden hints.
  • Validation posture = whether the PR provides enough public validation/test evidence for maintainer review.
  • Contributor workload = public contributor activity and cleanup pressure, not a repo-wide quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.
🧪 Chat with LoopOver

Ask 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.

  • @loopover ask &lt;question&gt; answers contribution-quality Q&A with source citations and freshness.
  • @loopover chat &lt;question&gt; answers in natural prose from cached decision-pack facts via local inference (maintainer/collaborator; read-only).
  • A plain-language @loopover mention with a real question is routed to the closest matching read-only command automatically — no exact syntax required.

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.

  • Re-run LoopOver review

@superagent-security

Copy link
Copy Markdown
Contributor

Superagent didn't find any vulnerabilities or security issues in this PR.

@codecov

codecov Bot commented Jul 29, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 69.23077% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.01%. Comparing base (bd139a5) to head (15298e6).

Files with missing lines Patch % Lines
packages/loopover-engine/src/idea-intake.ts 69.23% 4 Missing ⚠️
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     
Flag Coverage Δ
backend 95.56% <100.00%> (-0.01%) ⬇️
engine 66.54% <30.76%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
packages/loopover-engine/src/idea-intake.ts 76.62% <69.23%> (-0.15%) ⬇️

... and 1 file with indirect coverage changes

@loopover-orb loopover-orb Bot added the gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. label Jul 29, 2026
@loopover-orb

loopover-orb Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

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.

@loopover-orb loopover-orb Bot closed this Jul 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

engine(intake): validateIdeaSubmission rejects the IdeaTarget object shape it returns

1 participant