Skip to content

fix(sdk): guest pass issuance + idempotent enroll handling - #42

Merged
kyalabs merged 4 commits into
mainfrom
fix/post-batch2-smoke-test-fixes
Mar 31, 2026
Merged

fix(sdk): guest pass issuance + idempotent enroll handling#42
kyalabs merged 4 commits into
mainfrom
fix/post-batch2-smoke-test-fixes

Conversation

@kyalabs

@kyalabs kyalabs commented Mar 31, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • B1 (P0): guest-pass.ts was not sending iss: "sdk" — every SDK guest pass request failed with 400, Badge.init() silently fell back to offline mode
  • B2 (P0): guest-pass.ts read data.token but API returns data.guest_token — cached token was always undefined, Badge.headers() returned { "Kya-Token": "undefined" }
  • B4 (P1): badge-token.ts now handles { existing: true } idempotent enroll responses gracefully instead of logging misleading "missing badge_token" error

Discovered during comprehensive Batch 1+2 smoke test review. See internalops/specs/roadmaps/ProveIt/batch2_smoke_test.md for full test plan.

Test plan

  • Badge.init() successfully issues a guest pass (returns identityType: "guest", not "offline")
  • badge.headers() returns { "Kya-Token": "gp_v1_..." } (not "undefined")
  • Re-enrollment on same day returns cached token from memory (not null)
  • Existing tests pass (npm run test)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added identity management with guest, verified, and offline identity modes; identity can inject a Kya-Token header.
    • Implemented guest-pass issuance and local caching to preserve identity across restarts.
  • Bug Fixes

    • Improved enrollment handling to treat re-enroll responses as idempotent and avoid incorrect fallback when a token is absent.
  • Documentation

    • Added SDK README with usage, env vars, and caching behavior.
  • Tests

    • Added test suites covering badge and guest-pass behavior.

kyalabs and others added 2 commits March 31, 2026 12:56
…t B)

New SDK surface:
- Badge.init() — async factory, issues guest pass on first run
- badge.headers() — { "Kya-Token": "gp_v1_..." }
- badge.shouldNudge() / nudgeMessage() — upgrade nudge (v1: stub)
- badge.destroy() — cleanup (v1: no-op)
- issueGuestPass() — POST /api/badge/guest-pass with cache
- loadCachedGuestPass() — read ~/.kya/guest_token with TTL check
- Offline fallback when API unreachable

TDD: 12 new tests, 104/104 SDK tests pass, 199 total.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two P0 bugs discovered during Batch 1+2 smoke test review:

- B1: guest-pass.ts was not sending iss: "sdk" in the fetch body,
  causing every guest pass request to fail with 400 (API requires iss
  field). Badge.init() silently fell back to offline mode.
- B2: guest-pass.ts read data.token but the API returns data.guest_token,
  so the cached token was always undefined. Badge.headers() returned
  { "Kya-Token": "undefined" }.

Also fixed P1 in badge-token.ts:
- B4: enrollAndCacheBadgeToken() now handles { existing: true } responses
  gracefully — returns cached token if available instead of logging a
  misleading "missing badge_token" error.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Mar 31, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Adds a framework-agnostic Badge identity primitive with guest-pass lifecycle (issue/load/cache), re-exports guest-pass utilities, and updates badge-token enrollment to handle idempotent enroll responses by reusing an in-memory cached token or returning null when unavailable.

Changes

Cohort / File(s) Summary
Badge identity
packages/sdk/src/badge.ts, packages/sdk/src/badge.test.ts
New Badge class and tests. Provides IdentityType (guest/verified/offline), Badge.init(opts) which loads or issues guest passes and falls back to offline identity; methods: headers(), shouldNudge(), nudgeMessage(), destroy(). Tests cover init, caching, headers, and lifecycle methods.
Guest-pass lifecycle
packages/sdk/src/guest-pass.ts, packages/sdk/src/guest-pass.test.ts
New guest-pass APIs and tests: issueGuestPass() (POST with 5s timeout), loadCachedGuestPass() (reads ~/.kya/guest_token and validates expiry), cacheGuestPass() (writes cache, best-effort). Tests verify success/failure flows and cache behavior.
Badge token enrollment
packages/sdk/src/badge-token.ts
enrollAndCacheBadgeToken() updated to treat enroll response as idempotent: if API returns existing: true without badge_token, attempts to reuse in-memory cached token for that merchant; logs to stderr and returns null if none is cached. Other cases unchanged.
Exports & docs
packages/sdk/src/index.ts, packages/sdk/README.md
Re-exports Badge, IdentityType, BadgeInitOptions, issueGuestPass, loadCachedGuestPass, cacheGuestPass, GuestPassResult. Adds README documenting Badge primitive, lifecycle, env vars, cache paths, and public API.

Sequence Diagram(s)

sequenceDiagram
    participant App as Application
    participant Badge as Badge.init()
    participant Storage as SDK Storage
    participant Cache as Guest Pass Cache
    participant API as Guest Pass API

    App->>Badge: init(opts)
    Badge->>Storage: determine installId
    Storage-->>Badge: installId
    Badge->>Cache: loadCachedGuestPass()
    alt valid cached token
        Cache-->>Badge: GuestPassResult
        Badge-->>App: return Badge (identity: guest)
    else no cached token / expired
        Badge->>API: issueGuestPass(installId,...)
        alt API ok (guest_token)
            API-->>Badge: guest_token, expires_at
            Badge->>Cache: cacheGuestPass(token, expiresAt)
            Cache-->>Badge: stored
            Badge-->>App: return Badge (identity: guest)
        else API failure / network error
            Badge-->>App: return Badge (identity: offline, token: offline_${installId})
        end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 I hopped to fetch a tiny key,

guest passes cached beneath a tree,
Tokens found, or made anew—
offline hops when calls won’t do,
Happy badge, from me to you! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'fix(sdk): guest pass issuance + idempotent enroll handling' accurately and concisely describes both main changes: fixing guest pass issuance issues (B1, B2) and adding idempotent enrollment handling (B4).
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/post-batch2-smoke-test-fixes
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch fix/post-batch2-smoke-test-fixes

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

Covers Badge.init(), headers(), enrollAndCacheBadgeToken(), credential
storage, and KYA_API_URL / KYA_API_KEY / KYA_EXTENDED_AUTH env vars.
Documents the idempotent enrollment caveat (token only returned on first
201, must be persisted by caller).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

@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: 2

🧹 Nitpick comments (2)
packages/sdk/src/badge-token.ts (1)

63-70: Variable cached shadows outer scope declaration.

Line 65 declares const cached which shadows the cached variable from line 32. While functionally correct (the outer cached is already falsy when reaching this point), the shadowing reduces clarity.

♻️ Suggested rename for clarity
    if (data.existing && !data.badge_token) {
      // Already enrolled today — use cached token if we have one
-     const cached = badgeTokenCache.get(merchant);
-     if (cached) return cached;
+     const existingToken = badgeTokenCache.get(merchant);
+     if (existingToken) return existingToken;
      // No cached token (process restarted) — can't recover until next day
      process.stderr.write(`[badge] already enrolled at ${merchant} today but no cached token — persist badge_token on first enrollment\n`);
      return null;
    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/sdk/src/badge-token.ts` around lines 63 - 70, The inner declaration
of const cached in the badge enrollment branch shadows an outer cached variable;
rename the inner variable (e.g., to cachedToken or cachedFromCache) and update
its usages and return so the shadowing is removed—look for the
badgeTokenCache.get(merchant) call in the block that checks data.existing &&
!data.badge_token and replace the inner cached identifier consistently.
packages/sdk/src/guest-pass.test.ts (1)

70-75: Consider adding tests for expired and malformed cache scenarios.

The loadCachedGuestPass() tests only cover the "no cache exists" case. Consider adding tests for:

  • Expired cache (returns null)
  • Malformed JSON cache (returns null)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/sdk/src/guest-pass.test.ts` around lines 70 - 75, Add two unit tests
for loadCachedGuestPass(): one that writes a cached guest pass JSON with an
expiration timestamp in the past and asserts loadCachedGuestPass() returns null,
and another that writes a malformed JSON string (e.g., truncated or invalid) to
the same cache key and asserts loadCachedGuestPass() returns null; ensure each
test sets up the local storage key used by loadCachedGuestPass (and clears it
after) so tests are isolated and reference the loadCachedGuestPass() symbol
directly in the new it(...) blocks.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/sdk/src/guest-pass.test.ts`:
- Around line 37-55: The test mock in guest-pass.test.ts uses the wrong response
field ("token") while issueGuestPass (in guest-pass.ts) expects
data.guest_token; update the mocked JSON returned by mockFetch in the "calls
/api/badge/guest-pass and returns token" test to use guest_token (and matching
expires_at and identity_type) so the mock shape matches the real API and
exercise issueGuestPass parsing logic correctly.

In `@packages/sdk/src/guest-pass.ts`:
- Around line 100-106: The cacheGuestPass function currently creates KYA_DIR and
writes CACHE_FILE without explicit permissions; update it to set secure modes
like storage.ts does: when calling mkdirSync(KYA_DIR) pass { recursive: true,
mode: 0o700 } and when writing the cache file ensure the file is created with
mode 0o600 (use writeFileSync(CACHE_FILE, ..., { encoding: "utf-8", mode: 0o600
}) and, for the case the file already exists, follow-up with
fs.chmodSync(CACHE_FILE, 0o600) to enforce permissions); keep the existing
silent catch behavior for failures.

---

Nitpick comments:
In `@packages/sdk/src/badge-token.ts`:
- Around line 63-70: The inner declaration of const cached in the badge
enrollment branch shadows an outer cached variable; rename the inner variable
(e.g., to cachedToken or cachedFromCache) and update its usages and return so
the shadowing is removed—look for the badgeTokenCache.get(merchant) call in the
block that checks data.existing && !data.badge_token and replace the inner
cached identifier consistently.

In `@packages/sdk/src/guest-pass.test.ts`:
- Around line 70-75: Add two unit tests for loadCachedGuestPass(): one that
writes a cached guest pass JSON with an expiration timestamp in the past and
asserts loadCachedGuestPass() returns null, and another that writes a malformed
JSON string (e.g., truncated or invalid) to the same cache key and asserts
loadCachedGuestPass() returns null; ensure each test sets up the local storage
key used by loadCachedGuestPass (and clears it after) so tests are isolated and
reference the loadCachedGuestPass() symbol directly in the new it(...) blocks.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 2abea87a-e39c-457e-b4ef-d68dbb9f5958

📥 Commits

Reviewing files that changed from the base of the PR and between 2dbe890 and d21c1d0.

📒 Files selected for processing (6)
  • packages/sdk/src/badge-token.ts
  • packages/sdk/src/badge.test.ts
  • packages/sdk/src/badge.ts
  • packages/sdk/src/guest-pass.test.ts
  • packages/sdk/src/guest-pass.ts
  • packages/sdk/src/index.ts

Comment on lines +37 to +55
it("calls /api/badge/guest-pass and returns token", async () => {
mockFetch.mockResolvedValueOnce({
ok: true,
json: async () => ({
token: "gp_v1_fresh_token",
expires_at: "2026-04-07T00:00:00Z",
identity_type: "guest",
}),
});

const result = await issueGuestPass("inst-aaaa-bbbb-cccc-dddddddddddd");
expect(result).toBeDefined();
expect(result!.token).toBe("gp_v1_fresh_token");
expect(result!.identityType).toBe("guest");

const [url, opts] = mockFetch.mock.calls[0];
expect(url).toContain("/api/badge/guest-pass");
expect(opts.method).toBe("POST");
});

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 | 🟡 Minor

Test mock uses wrong field name — should be guest_token not token.

The PR objective B2 states the API returns data.guest_token, and guest-pass.ts:58 correctly reads data.guest_token. However, this test mock returns { token: "..." } instead of { guest_token: "..." }.

The test passes because mockFetch returns a fake response that issueGuestPass parses, but the mock shape doesn't match the real API response. This could mask bugs if the test is later used to validate parsing logic.

🐛 Proposed fix to align mock with API response
      mockFetch.mockResolvedValueOnce({
        ok: true,
        json: async () => ({
-         token: "gp_v1_fresh_token",
+         guest_token: "gp_v1_fresh_token",
          expires_at: "2026-04-07T00:00:00Z",
-         identity_type: "guest",
        }),
      });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/sdk/src/guest-pass.test.ts` around lines 37 - 55, The test mock in
guest-pass.test.ts uses the wrong response field ("token") while issueGuestPass
(in guest-pass.ts) expects data.guest_token; update the mocked JSON returned by
mockFetch in the "calls /api/badge/guest-pass and returns token" test to use
guest_token (and matching expires_at and identity_type) so the mock shape
matches the real API and exercise issueGuestPass parsing logic correctly.

Comment on lines +100 to +106
export function cacheGuestPass(token: string, expiresAt: string): void {
try {
mkdirSync(KYA_DIR, { recursive: true });
writeFileSync(CACHE_FILE, JSON.stringify({ token, expiresAt }), "utf-8");
} catch {
// Silent failure — cache is a performance optimization, not required
}

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 | 🟡 Minor

Missing file permissions on cache directory and file.

The storage.ts module creates ~/.kya with mode: 0o700 and files with mode: 0o600 to protect credentials. This function creates the same directory and writes a credential file without explicit permissions, potentially exposing the guest token to other users on shared systems.

🛡️ Proposed fix to add restrictive permissions
 export function cacheGuestPass(token: string, expiresAt: string): void {
   try {
-    mkdirSync(KYA_DIR, { recursive: true });
-    writeFileSync(CACHE_FILE, JSON.stringify({ token, expiresAt }), "utf-8");
+    mkdirSync(KYA_DIR, { recursive: true, mode: 0o700 });
+    writeFileSync(CACHE_FILE, JSON.stringify({ token, expiresAt }), { encoding: "utf-8", mode: 0o600 });
   } catch {
     // Silent failure — cache is a performance optimization, not required
   }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function cacheGuestPass(token: string, expiresAt: string): void {
try {
mkdirSync(KYA_DIR, { recursive: true });
writeFileSync(CACHE_FILE, JSON.stringify({ token, expiresAt }), "utf-8");
} catch {
// Silent failure — cache is a performance optimization, not required
}
export function cacheGuestPass(token: string, expiresAt: string): void {
try {
mkdirSync(KYA_DIR, { recursive: true, mode: 0o700 });
writeFileSync(CACHE_FILE, JSON.stringify({ token, expiresAt }), { encoding: "utf-8", mode: 0o600 });
} catch {
// Silent failure — cache is a performance optimization, not required
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/sdk/src/guest-pass.ts` around lines 100 - 106, The cacheGuestPass
function currently creates KYA_DIR and writes CACHE_FILE without explicit
permissions; update it to set secure modes like storage.ts does: when calling
mkdirSync(KYA_DIR) pass { recursive: true, mode: 0o700 } and when writing the
cache file ensure the file is created with mode 0o600 (use
writeFileSync(CACHE_FILE, ..., { encoding: "utf-8", mode: 0o600 }) and, for the
case the file already exists, follow-up with fs.chmodSync(CACHE_FILE, 0o600) to
enforce permissions); keep the existing silent catch behavior for failures.

@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: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/sdk/README.md`:
- Around line 41-45: The fenced diagram block is missing a language identifier
which trips markdown linting; update the code fence surrounding the lifecycle
diagram (the block containing "Badge.init()  →  guest pass (gp_v1_*)  → enroll
at merchant  → badge token (kya_*)" and the arrows/cached/consent notes) to
include a language tag such as text (i.e., replace ``` with ```text) so the
markdown linter recognizes the block.
- Around line 113-117: The fenced code block showing the directory listing
starting with "~/.kya/" is missing a language identifier; update that block to
include a language (e.g., "text" or "bash") after the opening backticks so
markdown linters accept it — locate the block containing the lines "~/.kya/",
"install_id", and "guest_token" in packages/sdk/README.md and change the opening
"```" to "```text" (or "```bash").
- Around line 88-99: Update the README description for enrollAndCacheBadgeToken
to clarify re-enrollment semantics: state that enrollAndCacheBadgeToken
(implemented in packages/sdk/src/badge-token.ts) will return the cached badge
token if it was previously issued in the same process, and only returns null
when no in-memory cache exists (e.g., after a process restart), rather than
always returning null on same-day re-enrollment; keep the note about requiring
KYA_API_KEY and persisting the token from the first call.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 34e39898-65cd-4aeb-8854-0496a55d2f17

📥 Commits

Reviewing files that changed from the base of the PR and between d21c1d0 and b44237f.

📒 Files selected for processing (1)
  • packages/sdk/README.md

Comment thread packages/sdk/README.md
Comment on lines +41 to +45
```
Badge.init() → guest pass (gp_v1_*) → enroll at merchant → badge token (kya_*)
↑ ↑
cached in ~/.kya/ requires consent key (pk_*)
```

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 | 🟡 Minor

Add language identifier to the fenced code block.

The lifecycle diagram should specify a language (e.g., text or mermaid) to satisfy markdown linting rules.

📝 Proposed fix
-```
+```text
 Badge.init()  →  guest pass (gp_v1_*)  →  enroll at merchant  →  badge token (kya_*)
                      ↑                         ↑
                  cached in ~/.kya/       requires consent key (pk_*)
</details>

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.22.0)</summary>

[warning] 41-41: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @packages/sdk/README.md around lines 41 - 45, The fenced diagram block is
missing a language identifier which trips markdown linting; update the code
fence surrounding the lifecycle diagram (the block containing "Badge.init() →
guest pass (gp_v1_) → enroll at merchant → badge token (kya_)" and the
arrows/cached/consent notes) to include a language tag such as text (i.e.,
replace withtext) so the markdown linter recognizes the block.


</details>

<!-- fingerprinting:phantom:triton:puma:1ff752b7-4f5b-4c44-b064-95767326b113 -->

<!-- This is an auto-generated comment by CodeRabbit -->

Comment thread packages/sdk/README.md
Comment on lines +88 to +99
### `enrollAndCacheBadgeToken(merchant)`

Enroll at a merchant and receive a `kya_*` badge token. Requires `KYA_API_KEY`.

```typescript
import { enrollAndCacheBadgeToken } from '@kyalabs/badge-sdk'

const token = await enrollAndCacheBadgeToken('store.example.com')
// "kya_abc123..."
```

**Important:** The badge token is only returned on the first enrollment per merchant per day. Re-enrollment on the same day returns `null` (the token hash is one-way). Persist the token from the first call.

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

Clarify the re-enrollment behavior.

Line 99 states "Re-enrollment on the same day returns null", but this is incomplete. Per packages/sdk/src/badge-token.ts lines 58-71, re-enrollment returns the cached token if available in memory, and only returns null if the process restarted and no cached token exists.

This distinction is critical for linked repository consumers (e.g., mcp-server) that rely on receiving the token string on re-enrollment within the same process.

📝 Proposed fix
-**Important:** The badge token is only returned on the first enrollment per merchant per day. Re-enrollment on the same day returns `null` (the token hash is one-way). Persist the token from the first call.
+**Important:** The badge token is only returned on the first enrollment per merchant per day. Re-enrollment on the same day returns the cached token if available in memory, or `null` if the process restarted (the API-stored token is hashed and cannot be recovered). Persist the token from the first 201 response to survive process restarts.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/sdk/README.md` around lines 88 - 99, Update the README description
for enrollAndCacheBadgeToken to clarify re-enrollment semantics: state that
enrollAndCacheBadgeToken (implemented in packages/sdk/src/badge-token.ts) will
return the cached badge token if it was previously issued in the same process,
and only returns null when no in-memory cache exists (e.g., after a process
restart), rather than always returning null on same-day re-enrollment; keep the
note about requiring KYA_API_KEY and persisting the token from the first call.

Comment thread packages/sdk/README.md
Comment on lines +113 to +117
```
~/.kya/
install_id # persistent UUID
guest_token # cached guest pass { token, expiresAt }
```

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 | 🟡 Minor

Add language identifier to the fenced code block.

The directory structure should specify a language (e.g., text or bash) to satisfy markdown linting rules.

📝 Proposed fix
-```
+```text
 ~/.kya/
   install_id      # persistent UUID
   guest_token     # cached guest pass { token, expiresAt }
</details>

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.22.0)</summary>

[warning] 113-113: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @packages/sdk/README.md around lines 113 - 117, The fenced code block showing
the directory listing starting with "/.kya/" is missing a language identifier;
update that block to include a language (e.g., "text" or "bash") after the
opening backticks so markdown linters accept it — locate the block containing
the lines "
/.kya/", "install_id", and "guest_token" in packages/sdk/README.md
and change the opening "" to "text" (or "```bash").


</details>

<!-- fingerprinting:phantom:triton:puma:1ff752b7-4f5b-4c44-b064-95767326b113 -->

<!-- This is an auto-generated comment by CodeRabbit -->

…st_token)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@kyalabs
kyalabs merged commit e5fc93f into main Mar 31, 2026
1 check failed
@kyalabs
kyalabs deleted the fix/post-batch2-smoke-test-fixes branch March 31, 2026 19:35
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