Skip to content

Dev#28

Merged
imbingox merged 3 commits into
mainfrom
dev
May 27, 2026
Merged

Dev#28
imbingox merged 3 commits into
mainfrom
dev

Conversation

@imbingox

@imbingox imbingox commented May 27, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features

    • Juplend: support direct position reads (vaultId+positionId) alongside wallet aggregation; CLI smoke test and adapter now accept rpcUrl and jupApiKey.
  • Documentation

    • Added Trellis spec bootstrapping guides and updated Juplend docs/README/API/architecture to reflect new account/options semantics.
  • Bug Fixes / Behavior

    • Order subscriptions now reject for the unsupported Juplend venue; private streams tolerate missing Juplend credentials.
  • Chores

    • Windows UTF‑8 fixes in hooks, version bumped to 0.5.19, added Juplend SDK deps and test updates.

Review Change Stack

@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@coderabbitai

coderabbitai Bot commented May 27, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 23475f4d-5ebd-4917-ae54-2bb1f4dfd6ef

📥 Commits

Reviewing files that changed from the base of the PR and between e47874a and 99fb840.

📒 Files selected for processing (8)
  • .agents/skills/trellis-spec-bootstarp/SKILL.md
  • .claude/skills/trellis-spec-bootstarp/SKILL.md
  • .trellis/scripts/common/session_context.py
  • .trellis/spec/backend/venue-lending.md
  • docs/api.md
  • docs/architecture.md
  • src/adapters/juplend/private-adapter.ts
  • tests/integration/account.test.ts
✅ Files skipped from review due to trivial changes (2)
  • .claude/skills/trellis-spec-bootstarp/SKILL.md
  • .agents/skills/trellis-spec-bootstarp/SKILL.md
🚧 Files skipped from review as they are similar to previous changes (5)
  • docs/architecture.md
  • src/adapters/juplend/private-adapter.ts
  • docs/api.md
  • .trellis/spec/backend/venue-lending.md
  • tests/integration/account.test.ts

📝 Walkthrough

Walkthrough

This PR adds native Juplend lending account support via the @jup-ag/lend-read SDK, refactors the Juplend private adapter to use position-based reads with optional vault enrichment, updates type contracts to support both wallet aggregation and direct vault+position selection, integrates into client runtime with configurable RPC/API keys, and expands tests and documentation. It also improves Trellis infrastructure with task archive path scoping, Git reporting refactoring, Python UTF-8 encoding fixes for Windows hooks, and adds comprehensive spec bootstrap guidance documentation.

Changes

Juplend lending account native support

Layer / File(s) Summary
Juplend type contracts and spec
src/types/shared.ts, .trellis/spec/backend/venue-lending.md
JuplendAccountOptions becomes a union supporting wallet-based or vault+position-based reads; credentials becomes optional; spec documents dual data-fetch routing and validation rules.
Lend-read SDK adapter
src/adapters/juplend/lend-read.ts, package.json
New adapter wrapping @jup-ag/lend-read for reading Juplend positions, supports configurable RPC URLs with SOL_HELIUS_RPC fallback, provides test SDK injection hooks.
Juplend private adapter with vault enrichment
src/adapters/juplend/private-adapter.ts
Refactored to read positions via lend-read, enrich vaults with Jup API token metadata and pricing, compute normalized balances/risk, marks credentials not required.
Client runtime and credential handling
src/client/context.ts, src/client/runtime.ts, src/client/private-subscription-coordinator.ts
Wires adapter into client initialization with RPC/API key config, updates credential checks to not require Juplend credentials, allows missing credentials in subscription flows.
Order subscription venue restrictions
src/managers/order-manager.ts
Adds VENUE_NOT_SUPPORTED error and rejects order subscriptions for Juplend accounts.
Live smoke test for Juplend
scripts/live-juplend-account-smoke.ts
Supports wallet-based or vault+position reads with optional RPC URL, removes Jupiter API key dependency, demonstrates both modes via CLI.
Juplend test support and fixtures
tests/support/exchanges/juplend.ts
Replaces portfolio mocks with positions/vaults/token-search/pricing fixtures using lend-read SDK mocking, tracks concurrent position requests.
Juplend integration and type tests
tests/integration/account.test.ts, tests/integration/order.test.ts, tests/type/register-account-input.ts
Expands tests for direct position reads, balance normalization, fallback behavior, RPC configuration, order rejection, type validation of wallet/vault options.
Documentation and examples
README.md, docs/api.md, docs/architecture.md
Updates docs with dual read modes, RPC/API key configuration, token metadata fallback rules, and capacity constraints.

Trellis infrastructure and tooling updates

Layer / File(s) Summary
Task archive path scoping
.trellis/scripts/common/safe_commit.py, .trellis/scripts/common/task_store.py
Refactors path staging to support scoped archive operations, prevents duplicate task names, tracks modified child task.json files during archive.
Git context reporting refactoring
.trellis/scripts/common/session_context.py
Restructures Git info collection with root Git detection, worktree support, child repo discovery heuristic, and shared appenders for text output across all context modes.
Python hook UTF-8 encoding fixes
.claude/hooks/inject-workflow-state.py, .claude/hooks/session-start.py, .codex/hooks/inject-workflow-state.py, .codex/hooks/session-start.py
Adds Windows-specific UTF-8 enforcement at startup to prevent Unicode encode/decode failures in hook payloads and JSON output.
Hook configuration and timeouts
.claude/settings.json, .codex/hooks.json, .codex/config.toml
Updates hook commands to use Python UTF-8 mode, increases timeouts, removes multi-agent v2 config from project level per version compatibility note.
Trellis spec bootstrap documentation system
.agents/skills/trellis-spec-bootstarp/*, .claude/skills/trellis-spec-bootstarp/*
Introduces comprehensive guidance for bootstrapping .trellis/spec/ with workflow steps, MCP setup, repository analysis patterns, task planning, and spec writing standards.
Workflow state documentation update
.trellis/workflow.md
Clarifies tools vs skills in in-progress state (trellis-implement/research as sub-agents, trellis-update-spec as skill).
Version and metadata updates
.trellis/.version, .trellis/.template-hashes.json
Bumps Trellis version to 0.5.19, updates template hash mappings for all modified files.

🎯 4 (Complex) | ⏱️ ~50 minutes

🐰 Hops through code with joy,
Juplend reads native positions now,
Specs blooming from roots!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 35.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Dev' is vague and generic, using a non-descriptive term that does not convey meaningful information about the changeset's primary purpose or scope. Replace with a specific, concise title that highlights the main change (e.g., 'Add Juplend lending position support with lend-read integration and Trellis spec bootstrapping').
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/adapters/juplend/private-adapter.ts (1)

262-269: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add request timeouts to Juplend HTTP fetches.

Line 263 uses fetch without a timeout; a hanging upstream call can block bootstrap/poll completion and keep account state stale.

⏱️ Suggested hard timeout wrapper
+const DEFAULT_HTTP_TIMEOUT_MS = 10_000;
+
 async function readJson<T>(url: string, init?: RequestInit): Promise<T> {
-  const response = await fetch(url, init);
+  const controller = new AbortController();
+  const timer = setTimeout(() => controller.abort(), DEFAULT_HTTP_TIMEOUT_MS);
+  const response = await fetch(url, {
+    ...init,
+    signal: init?.signal ?? controller.signal,
+  }).finally(() => clearTimeout(timer));
   if (!response.ok) {
     throw new Error(`Juplend HTTP ${response.status}: ${response.statusText}`);
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/adapters/juplend/private-adapter.ts` around lines 262 - 269, The fetch in
readJson<T> can hang; wrap it with an AbortController and a hard timeout: create
a controller, start a timeout (e.g. configurable default like 10s) that calls
controller.abort(), merge the controller.signal into the fetch by adding it to
the passed init (preserving other init options), and if init.signal exists,
forward that signal by listening to it and calling controller.abort() so either
signal cancels the request; clear the timeout on successful response, and when
abort occurs throw a clear timeout-specific error (e.g., "Juplend fetch
timeout") instead of waiting forever.
tests/integration/account.test.ts (1)

673-679: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fix failing Jup API call assertions by enabling enrichment in these tests.

Line 746 and Line 1162 assert two api.jup.ag calls, but these test clients are created without a Juplend API key, so the requests stay at 0 (matching CI failures). Configure jupApiKey in these tests (or relax the assertions).

Suggested patch
@@
   const client = createClient({
     account: {
       juplend: {
         pollIntervalMs: 60_000,
+        jupApiKey: "test-key",
       },
     },
   });
@@
-  const client = createClient();
+  const client = createClient({
+    account: {
+      juplend: {
+        jupApiKey: "test-key",
+      },
+    },
+  });

Also applies to: 744-746, 1133-1133, 1160-1162

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/integration/account.test.ts` around lines 673 - 679, The tests create
test clients via createClient({ account: { juplend: { pollIntervalMs: 60_000
}}}) but do not set a jupApiKey, so Juplend enrichment is disabled and expected
api.jup.ag calls remain zero; update the test client config used in these
failing specs (tests referencing createClient in account.test.ts / the tests
asserting api.jup.ag calls around the failing assertions) to include a jupApiKey
(a valid or mocked key) so enrichment runs and the two api.jup.ag requests
occur, or alternatively relax the assertions to not require two calls—prefer
adding jupApiKey to the createClient invocation to fix the failures.
🧹 Nitpick comments (1)
.trellis/scripts/common/session_context.py (1)

89-109: 💤 Low value

Rename ambiguous loop variable l to line.

Static analysis flagged the single-letter variable name. Using line improves readability.

✏️ Suggested fix
-    changes = len([l for l in status_out.splitlines() if l.strip()])
+    changes = len([line for line in status_out.splitlines() if line.strip()])
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.trellis/scripts/common/session_context.py around lines 89 - 109, In
_collect_git_repo_info rename the ambiguous loop variable `l` used in the list
comprehension that counts changes to a clearer name like `line`; update the
expression that builds the list from status_out.splitlines() if l.strip() to use
`line.strip()` instead so the semantics remain the same and improve readability.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.agents/skills/trellis-spec-bootstarp/SKILL.md:
- Line 2: Update the skill name/title to correct the typo "bootstarp" →
"bootstrap": change the frontmatter name value `trellis-spec-bootstarp` to
`trellis-spec-bootstrap` and update the H1/title text in SKILL.md to use
"bootstrap" as well; if the identifier must remain stable, at minimum correct
the visible H1/title while leaving the frontmatter name unchanged and add a
brief note or comment explaining the decision.

In @.claude/skills/trellis-spec-bootstarp/SKILL.md:
- Line 2: The skill name contains a typo "trellis-spec-bootstarp"; update the
name value to "trellis-spec-bootstrap" and also correct any other occurrences of
"bootstarp" (notably the other occurrence referenced at line 6) so the `.claude`
and `.agents` skill names remain consistent and searchable.

In @.trellis/spec/backend/venue-lending.md:
- Around line 30-34: The Juplend options example incorrectly marks all fields
optional; update the signature block for the `options` example to reflect the
real union contract by replacing the current `{ walletAddress?: string;
vaultId?: string; positionId?: string }` with a discriminated union like `{
walletAddress: string } | { vaultId: string; positionId: string }` (or
equivalent wording stating "either walletAddress OR both vaultId and positionId
are required") and make the same change to the other Juplend signature
occurrence mentioned so the docs match `src/types/shared.ts`.

In `@docs/api.md`:
- Line 1473: The docs table entry for ACCOUNT_BOOTSTRAP_FAILED is incomplete:
update the row for ACCOUNT_BOOTSTRAP_FAILED to mention both bootstrap failure
due to missing options.walletAddress and the alternative valid path using
vaultId + positionId (i.e., direct mode). Modify the description so it clearly
states that bootstrap can fail when options.walletAddress is missing OR when
using direct mode the caller must supply valid vaultId and positionId, and
ensure the text references the exact keys options.walletAddress, vaultId, and
positionId so readers aren’t misled.

In `@docs/architecture.md`:
- Around line 316-317: Update the options shape so it enforces the Juplend
contract constraint that either walletAddress is provided OR both vaultId and
positionId are provided (not all three optional); change the documented/typed
signature for options (the object containing walletAddress, vaultId, positionId)
into a discriminated union or add explicit runtime validation in the code paths
that consume options to require { walletAddress: string } OR { vaultId: string,
positionId: string } and reject/throw/log when the combination is invalid.

In `@src/adapters/juplend/private-adapter.ts`:
- Around line 448-450: The current freshness gate only returns cached.vaults
when cacheFresh && (cached.enriched || !apiKey), which forces reloads if
enrichment fails while an API key is present; change the early-return to return
cached.vaults whenever cacheFresh is true (i.e., remove the enriched||!apiKey
requirement) so fallback data is honored, and in the enrichment catch block (the
error handler around the enrichment logic that currently retries/reloads) mark
the cached entry as a fallback (e.g., set cached.enriched = false or
cached.fallback = true) and preserve its TTL so subsequent polls use the cached
vaults until the TTL expires.

---

Outside diff comments:
In `@src/adapters/juplend/private-adapter.ts`:
- Around line 262-269: The fetch in readJson<T> can hang; wrap it with an
AbortController and a hard timeout: create a controller, start a timeout (e.g.
configurable default like 10s) that calls controller.abort(), merge the
controller.signal into the fetch by adding it to the passed init (preserving
other init options), and if init.signal exists, forward that signal by listening
to it and calling controller.abort() so either signal cancels the request; clear
the timeout on successful response, and when abort occurs throw a clear
timeout-specific error (e.g., "Juplend fetch timeout") instead of waiting
forever.

In `@tests/integration/account.test.ts`:
- Around line 673-679: The tests create test clients via createClient({ account:
{ juplend: { pollIntervalMs: 60_000 }}}) but do not set a jupApiKey, so Juplend
enrichment is disabled and expected api.jup.ag calls remain zero; update the
test client config used in these failing specs (tests referencing createClient
in account.test.ts / the tests asserting api.jup.ag calls around the failing
assertions) to include a jupApiKey (a valid or mocked key) so enrichment runs
and the two api.jup.ag requests occur, or alternatively relax the assertions to
not require two calls—prefer adding jupApiKey to the createClient invocation to
fix the failures.

---

Nitpick comments:
In @.trellis/scripts/common/session_context.py:
- Around line 89-109: In _collect_git_repo_info rename the ambiguous loop
variable `l` used in the list comprehension that counts changes to a clearer
name like `line`; update the expression that builds the list from
status_out.splitlines() if l.strip() to use `line.strip()` instead so the
semantics remain the same and improve readability.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 4983e953-d9ec-403c-b25b-508b3cec5285

📥 Commits

Reviewing files that changed from the base of the PR and between 21e96a5 and e47874a.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (40)
  • .agents/skills/trellis-spec-bootstarp/SKILL.md
  • .agents/skills/trellis-spec-bootstarp/references/mcp-setup.md
  • .agents/skills/trellis-spec-bootstarp/references/repository-analysis.md
  • .agents/skills/trellis-spec-bootstarp/references/spec-task-planning.md
  • .agents/skills/trellis-spec-bootstarp/references/spec-writing.md
  • .claude/hooks/inject-workflow-state.py
  • .claude/hooks/session-start.py
  • .claude/settings.json
  • .claude/skills/trellis-spec-bootstarp/SKILL.md
  • .claude/skills/trellis-spec-bootstarp/references/mcp-setup.md
  • .claude/skills/trellis-spec-bootstarp/references/repository-analysis.md
  • .claude/skills/trellis-spec-bootstarp/references/spec-task-planning.md
  • .claude/skills/trellis-spec-bootstarp/references/spec-writing.md
  • .codex/config.toml
  • .codex/hooks.json
  • .codex/hooks/inject-workflow-state.py
  • .codex/hooks/session-start.py
  • .trellis/.template-hashes.json
  • .trellis/.version
  • .trellis/scripts/common/safe_commit.py
  • .trellis/scripts/common/session_context.py
  • .trellis/scripts/common/task_store.py
  • .trellis/spec/backend/venue-lending.md
  • .trellis/workflow.md
  • README.md
  • docs/api.md
  • docs/architecture.md
  • package.json
  • scripts/live-juplend-account-smoke.ts
  • src/adapters/juplend/lend-read.ts
  • src/adapters/juplend/private-adapter.ts
  • src/client/context.ts
  • src/client/private-subscription-coordinator.ts
  • src/client/runtime.ts
  • src/managers/order-manager.ts
  • src/types/shared.ts
  • tests/integration/account.test.ts
  • tests/integration/order.test.ts
  • tests/support/exchanges/juplend.ts
  • tests/type/register-account-input.ts

Comment thread .agents/skills/trellis-spec-bootstarp/SKILL.md
Comment thread .claude/skills/trellis-spec-bootstarp/SKILL.md
Comment thread .trellis/spec/backend/venue-lending.md Outdated
Comment thread docs/api.md
Comment thread docs/architecture.md Outdated
Comment on lines +448 to +450
if (cacheFresh && (cached.enriched || !apiKey)) {
return cached.vaults;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Cache fallback vault data when enrichment fails with API key enabled.

With the current freshness gate on Line 448 and the catch path on Line 476–478, repeated enrichment failures can trigger full reloads every poll instead of honoring TTL for fallback data.

🧩 Suggested fallback-cache behavior
-  if (cacheFresh && (cached.enriched || !apiKey)) {
+  if (cacheFresh) {
     return cached.vaults;
   }
@@
       try {
         const enrichedVaults = await enrichVaultsWithJupApi({
           apiKey,
           baseVaults,
         });
         enrichmentCache.set(cacheKey, {
           loadedAt: now,
           vaults: enrichedVaults,
           enriched: true,
         });
         return enrichedVaults;
       } catch {
+        enrichmentCache.set(cacheKey, {
+          loadedAt: now,
+          vaults: baseVaults,
+          enriched: false,
+        });
         return baseVaults;
       }

Also applies to: 465-478

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/adapters/juplend/private-adapter.ts` around lines 448 - 450, The current
freshness gate only returns cached.vaults when cacheFresh && (cached.enriched ||
!apiKey), which forces reloads if enrichment fails while an API key is present;
change the early-return to return cached.vaults whenever cacheFresh is true
(i.e., remove the enriched||!apiKey requirement) so fallback data is honored,
and in the enrichment catch block (the error handler around the enrichment logic
that currently retries/reloads) mark the cached entry as a fallback (e.g., set
cached.enriched = false or cached.fallback = true) and preserve its TTL so
subsequent polls use the cached vaults until the TTL expires.

@imbingox
imbingox merged commit fd9b179 into main May 27, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant