Skip to content

fix(engine): accept the { kind: 'existing', repo } IdeaTarget shape in validateIdeaSubmission (#9609) - #9634

Merged
loopover-orb[bot] merged 1 commit into
JSONbored:mainfrom
RealDiligent:fix/critical-issue-idea-intake-existing-target-9609
Jul 29, 2026
Merged

fix(engine): accept the { kind: 'existing', repo } IdeaTarget shape in validateIdeaSubmission (#9609)#9634
loopover-orb[bot] merged 1 commit into
JSONbored:mainfrom
RealDiligent:fix/critical-issue-idea-intake-existing-target-9609

Conversation

@RealDiligent

Copy link
Copy Markdown
Contributor

Problem

Closes #9609.

packages/loopover-engine/src/idea-intake.ts's validateIdeaSubmission returns { ok: true, idea } whose targetRepo is an IdeaTarget ({ kind: "existing"; repo } | { kind: "provision" }). But its own targetRepo branch only recognized a bare "owner/name" string (back-compat wire form) or a { kind: "provision" } object — so the canonical { kind: "existing", repo: "acme/widgets" } shape it itself produces (and that any TS caller writing against the exported IdeaSubmission type constructs) fell through to target_repo_required. The value it returns did not round-trip back through it.

Fix

Accept the { kind: "existing", repo } object shape, resolving its slug through the same split-and-guard as the bare-string form via a shared resolveExistingTarget helper (exactly-two valid segments, so a "."/".." traversal is rejected identically). { kind: "provision" }, the bare string, and every rejection path are unchanged.

Tests

  • test/unit/idea-intake-bridge.test.ts (root vitest): the object shape round-trips; a malformed slug inside it is rejected like the string form; and null/non-object/non-string-repo/missing-repo/unknown-kind all still require a target.
  • packages/loopover-engine/test/idea-intake.test.ts (engine node:test, new): mirrors the same cases so the engine Codecov flag credits the changed lines.

100% line + branch coverage on the changed source (108/108 branches).

@RealDiligent
RealDiligent requested a review from JSONbored as a code owner July 29, 2026 02:16
@loopover-orb

loopover-orb Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Warning

⏸️ LoopOver review result - manual review recommended

Review updated: 2026-07-29 02:34:26 UTC

3 files · 1 AI reviewer · no blockers · CI green · clean

⏸️ Suggested Action - Manual Review

Review summary
This fixes a real self-inconsistency bug: validateIdeaSubmission produced { kind: 'existing', repo } as its own IdeaTarget output type but its input-parsing branch never accepted that object shape back, so any caller round-tripping the type (or reusing a submission) hit target_repo_required. The fix extracts resolveExistingTarget so both the bare-string wire form and the { kind: 'existing', repo } object shape share the same split-and-guard validation, and it correctly falls through to target_repo_required (not malformed) when repo is missing/non-string or kind is unrecognized, matching prior string-path behavior. Tests in both the engine node:test suite and the root vitest bridge suite explicitly exercise the round-trip, malformed-slug-in-object, and all-fallback-to-required cases, and CI is green.

Nits — 4 non-blocking
  • packages/loopover-engine/src/idea-intake.ts: the resolveExistingTarget JSDoc and the inline comment above the object-shape branch both re-explain the same split-and-guard rule and engine(intake): validateIdeaSubmission rejects the IdeaTarget object shape it returns #9609 context — could be trimmed to avoid duplication.
  • The literal issue number 9609 appears in multiple comments across three files with no shared reference/constant, per the external brief — purely cosmetic.
  • Consider a single top-of-function comment documenting the two accepted shapes rather than repeating the rationale in both resolveExistingTarget's docblock and the call site.
  • packages/loopover-engine/test/idea-intake.test.ts imports from ../dist/index.js — worth a one-line comment noting this requires a prior build step, since it's a slightly unusual test-import pattern relative to the vitest suite which imports from src.

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: 266 registered-repo PR(s), 111 merged, 37 issue(s).
Contributor context ✅ Confirmed Gittensor contributor RealDiligent; Gittensor profile; 266 PR(s), 37 issue(s).
Improvement ✅ Minor risk: clean · value: minor · LLM: moderate
Linked issue satisfaction

Partially addressed
The PR correctly adds an arm accepting `{ kind: "existing", repo }` and round-trips valid slugs and malformed slugs to `target_repo_malformed`, but it fails the explicit requirement that an `existing`-kind object with an absent, non-string, or empty `repo` must push `target_repo_malformed` — instead it falls through to `target_repo_required` (see the `isNonEmptyString(target.repo)` guard and the t

Review context
  • Author: RealDiligent
  • 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: Python, Ruby, TypeScript, Svelte, Cuda, JavaScript, Markdown, MDX
  • Official Gittensor activity: 266 PR(s), 37 issue(s).
  • PR-specific overlap: none found.
Contributor next steps
  • Start here: Await review-lane availability.
  • Then work through the remaining 1 step 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 <question> answers contribution-quality Q&A with source citations and freshness.
  • @loopover chat <question> 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.

…n validateIdeaSubmission

validateIdeaSubmission returns an IdeaTarget (a { kind: 'existing', repo } |
{ kind: 'provision' } union), but its own targetRepo validation only accepted a
bare 'owner/name' string or a { kind: 'provision' } object -- so the canonical
{ kind: 'existing', repo } shape it produces (and that any TS caller writing
against the exported IdeaSubmission type constructs) fell through to
target_repo_required. The value it returns did not round-trip through it.

Accept the existing-target object shape, sharing the same owner/name
split-and-guard as the bare-string form via a resolveExistingTarget helper so a
'..'-traversal slug is rejected identically in both forms.
@codecov

codecov Bot commented Jul 29, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.12%. Comparing base (a402b9f) to head (991f3c3).
⚠️ Report is 3 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #9634      +/-   ##
==========================================
- Coverage   90.12%   90.12%   -0.01%     
==========================================
  Files         891      891              
  Lines      112306   112322      +16     
  Branches    26629    26633       +4     
==========================================
+ Hits       101216   101228      +12     
  Misses       9760     9760              
- Partials     1330     1334       +4     
Flag Coverage Δ
backend 95.51% <100.00%> (-0.01%) ⬇️
engine 67.17% <100.00%> (+0.02%) ⬆️

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 85.00% <100.00%> (+0.78%) ⬆️

... 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 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LoopOver approves — the gate is satisfied and CI is green.

@loopover-orb
loopover-orb Bot merged commit c086363 into JSONbored:main Jul 29, 2026
8 checks passed
@github-actions github-actions Bot mentioned this pull request Jul 29, 2026
7 tasks
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