Skip to content

docs(skills): agent-facing skills + 12 SDK-native recipes#38

Merged
IgorShevchik merged 26 commits into
mainfrom
claude/extract-sdk-examples-7HE3n
May 28, 2026
Merged

docs(skills): agent-facing skills + 12 SDK-native recipes#38
IgorShevchik merged 26 commits into
mainfrom
claude/extract-sdk-examples-7HE3n

Conversation

@IgorShevchik
Copy link
Copy Markdown
Collaborator

@IgorShevchik IgorShevchik commented May 15, 2026

Summary

Adds a project-scoped skill set under .claude/skills/ and a parallel catalogue of 12 SDK-native recipes under docs/content/docs/99.examples/. Built entirely on the canonical $b24.actions.v{2,3}.*.make() surface — the legacy callMethod / callBatch / callListMethod / fetchListMethod is @deprecated for 2.0.0 and is not generated by any skill or recipe.

The skills exist to make AI agents working in this repo produce idiomatic, version-correct code out of the box.

What's in the PR

7 skills (.claude/skills/<name>/SKILL.md)

Skill Covers
b24jssdk-core Init flavors (B24Hook / B24Frame / B24OAuth), error taxonomy, hardErrorCodes / softErrorCodes / retryOnNetworkError tuning
b24jssdk-rest actions.v{2,3}.{call,batch,callList,fetchList,batchByChunk}.make; AjaxResult new shape; v3 all-or-nothing batch; null-result passthrough; v3 method whitelist
b24jssdk-filtering v2 prefix dialect ('>=createdTime', '!stageId', '%title') + v3 array-of-triples dialect ([['fld','>=',v]]); operator lists; order stripping in callList/fetchList; dates via Text.toB24Format
b24jssdk-frame-ui Slider, dialog (selectUser/Users/CRM/Access), parent, placement (with the new setValue helper), options, auth
b24jssdk-helpers useB24Helper, B24HelperManager, Pull client (usePullClient() is arg-less), currency, app/user options
b24jssdk-recipes Catalogue/SKILL.md for the 12 recipes
b24jssdk-vibecode When to combine the SDK with the VibeCode HTTP API. Spoiler: rarely. AI-add-on pattern only.

12 recipes (.claude/skills/b24jssdk-recipes/examples/NN-*.ts)

# File Purpose
1 01-crm-analytics.ts Stream deals via actions.v2.fetchList.make, print funnel report
2 02-mass-messaging.ts Filter contacts, send im.notify per-contact
3 03-task-automation.ts Poll deal stages, create tasks via actions.v3.call.make('tasks.task.add')
4 04-erp-sync.ts Two-way contact sync (Bitrix24 ↔ mock ERP), node-cron schedule
5 05-disk-files.ts Storages → folders → files; one actions.v2.batch.make round-trip
6 06-telegram-bot.ts Poll new deals, notify a Telegram chat via grammy
7 07-webhook-handler.ts Express server that receives Bitrix24 outbound events; verifies application_token against B24_APPLICATION_TOKEN env to block spoofed events
8 08-ai-assistant.ts Deal + timeline → GPT-4o → follow-up task via actions.v3.call.make
9 09-web-search-llm.ts BYOC RAG → answer posted to a deal's timeline
10 10-error-handling.ts Error-handling cookbook with hardErrorCodes / softErrorCodes / retryOnNetworkError
11 11-event-registration.ts CLI: list / bind / unbind outbound webhook events (pairs with #7)
12 12-oauth-install.ts Express server handling ONAPPINSTALL / ONAPPUPDATE / ONAPPUNINSTALL, building B24OAuth on demand with a refresh callback writing tokens back to storage; verifies application_token on uninstall to prevent spoofed credential deletion

Validation pipeline

Recipes are now part of pnpm run typecheck via a new skills:typecheck script:

  • .claude/skills/b24jssdk-recipes/tsconfig.json — strict TypeScript config that points at the built SDK types (packages/jssdk/dist/esm/index.d.ts).
  • grammy, openai, express, node-cron + @types/express and @types/node-cron added as workspace devDeps so recipes get strict typing for opt-in deps too, not just SDK calls.
  • Lint pass already covers the rest of the repo; .claude/** is intentionally excluded from eslint (these are agent templates).

This pipeline caught real bugs during the PR's own self-review (see commits e17be22, 42c1d85).

Maintenance + audit docs

Documentation site

Mirrors the 12 recipes under docs/content/docs/99.examples/ with title, description, env, run command, and a link back to the source .ts file. Recipe 1 embeds the full code; the rest reference the canonical .ts. Recipes 4 and 6 docs include scaling pointers (batchByChunk.make / batch.make).

Repo plumbing

  • eslint.config.mjs — exclude .claude/** (skill files are agent-facing templates, not workspace source)
  • package.json — adds skills:typecheck script, wired into the root typecheck; adds opt-in dep packages

Security hardening

Both event-receiver recipes verify application_token against a known value before acting:

  • Recipe 7 (webhook handler): if B24_APPLICATION_TOKEN env is set, the handler compares it against payload.auth?.application_token and silently drops mismatched requests. Without this guard anyone who knows the URL could replay arbitrary events.
  • Recipe 12 (OAuth install): the uninstall handler verifies the token against the one persisted at install time before deleting credentials. Without this check anyone who can reach /uninstall could erase the credentials of any portal whose member_id they guess.

Both also reply 200 first and verify after, so a bad payload doesn't make Bitrix24 retry the call for 24 h.

Why this shape

  • Skills, not docs: these are loaded by AI agents in .claude/skills/. The matching docs site pages exist so humans see them too, but the source of truth is the .ts recipes and the SKILL.md prose.
  • actions.v{2,3}.* only: the Deprecation notice in packages/jssdk/README-AI.md was the trigger for migrating this branch off callMethod/callBatch/callListMethod/fetchListMethod. All skills and recipes ship the new surface.
  • Source-grounded: every load-bearing fact in the skill set (v3 whitelist, filter operators, AjaxResult shape, idKey defaults, the order-stripping rule in callList) is cited against packages/jssdk/src/... line numbers in REPORT.md. Future audits can verify against the same anchors.
  • v3 surface is narrow today: CRM stays on v2 (its v3 form isn't on version-manager.ts's whitelist yet); tasks switched to v3 where the whitelist allows. Documented per-recipe.
  • Recipes are CI-validated: unlike the markdown-only skills, recipe .ts files compile under strict TypeScript against the real SDK + real external-package types. Future API drift (e.g. an SDK error field rename) will be caught at typecheck time, not at copy-paste time.

Test plan

  • pnpm run lint passes (root)
  • pnpm run typecheck passes (root — every workspace, including skills:typecheck)
  • Recipes compile under strict TS with real types for grammy / openai / express / node-cron
  • Optional, requires B24_HOOK in .env.test: pnpm vitest run -t "js-docs.actions" --project jsSdk:integration — canonical actions specs still pass
  • Optional, on a live portal: walk one recipe end-to-end (recipe 1 is the lowest-risk smoke; recipe 7 wants verification of the data[FIELDS][ID] payload shape; recipe 9 wants verification of ENTITY_TYPE: 'deal' vs ENTITY_TYPE_ID: 2 for crm.timeline.comment.add)

Known unknowns (also tracked in REPORT.md)

  • Recipe 7 webhook payload shape — relies on express.urlencoded({ extended: true }) flattening data[FIELDS][ID]; not yet confirmed against a live outbound webhook.
  • Recipe 9 timeline comment — uses ENTITY_TYPE: 'deal' (string); some portal versions want ENTITY_TYPE_ID: 2 instead. Recipe notes the fallback.
  • v3 method whitelist will grow. When crm.item.* arrives on v3, several recipes should move; that's also a filter-dialect rewrite (prefix → array-of-triples). Tracked in REPORT.md §"Open questions / unresolved".

Future work (in SUGGESTED-EXAMPLES.md)

Top remaining picks: Vue/Nuxt frame boot template, batchByChunk.make bulk CSV import, production-grade multi-tenant backend (builds on recipe 12), actions.v3.aggregate.make when the SDK exposes it.

Commit log

42c1d85  chore(skills): install recipe opt-in deps for strict typechecking
f9c1cd7  fix(skills): wire recipes into typecheck + harden recipes 7/12
e17be22  fix(skills): correct AjaxError/SdkError field access in recipes
60139fa  feat(skills): three new recipes — error handling, event registration, OAuth install
7b96087  refactor(skills): migrate to actions.v{2,3}.*.make() surface
662b165  docs(skills): extract SDK-native examples from llms-full.txt

claude added 9 commits May 15, 2026 15:50
Adapt the VibeCode reference docs (docs/llms-full.txt) into b24jssdk
idioms and land them in two places:

- .claude/skills/ — agent-facing skills:
  - b24jssdk-core: pick the right entry point, boot, error handling
  - b24jssdk-rest: callMethod / callBatch / callListMethod / fetchListMethod
  - b24jssdk-filtering: Bitrix24 prefix-style filters, dates, multi-funnel
  - b24jssdk-frame-ui: slider, dialog, parent, placement, options
  - b24jssdk-helpers: useB24Helper, Pull client, currency
  - b24jssdk-recipes: 9 end-to-end TS recipes (CRM analytics, mass
    messaging, task automation, ERP sync, disk, Telegram bot, webhook
    handler, AI assistant, web-search RAG)
  - b24jssdk-vibecode: when/how to combine SDK with the VibeCode HTTP API
- docs/content/docs/99.examples/: matching docs pages with the same 9
  recipes, replacing the placeholder index

Each recipe is server-side via B24Hook by default; recipe bodies take
TypeB24 so the same code runs unchanged from B24Frame and B24OAuth.

Translation rules + maintenance playbook for the weekly llms-full.txt
review live in .claude/skills/MAINTENANCE.md and REPORT.md, with
follow-up gaps tracked in SUGGESTED-EXAMPLES.md.

Also exclude .claude/** from the repo eslint pass — these are templates
for AI agents, not project source.
The legacy SDK surface — callMethod / callBatch / callBatchByChunk /
callListMethod / fetchListMethod, plus AjaxResult.isMore()/getNext()/
getTotal() — is @deprecated and scheduled for removal in 2.0.0
(see packages/jssdk/README-AI.md "Deprecation notice"). Rewrite every
skill and recipe to use $b24.actions.v{2,3}.*.make() instead.

Per-skill changes:
- b24jssdk-rest: complete rewrite around call/batch/callList/fetchList/
  batchByChunk for both API versions. Documents the new AjaxResult shape
  (getData() -> SuccessPayload<T> | undefined), v3 all-or-nothing batch
  semantics, null-result passthrough, v3 method whitelist (~9 methods
  today per version-manager.ts).
- b24jssdk-filtering: keep the v2 prefix dialect, add the v3 array-of-
  triples dialect ([['fld', 'op', val]]). 8 operators only in v3
  (no like/%); document order-stripping in callList; use Text.toB24Format
  for dates.
- b24jssdk-core: new section on hardErrorCodes / softErrorCodes /
  retryOnNetworkError tuning via setRestrictionManagerParams.
- b24jssdk-frame-ui: un-deprecate selectCRM (re-implemented to normalize
  response buckets), document placement.setValue convenience helper.
- b24jssdk-helpers: correct usePullClient() to arg-less form, mention
  new isInitB24Helper() getter.
- b24jssdk-recipes + 9 example .ts files: rewrite all to actions API.
  CRM stays on v2 (not on the v3 whitelist yet); tasks.task.* switched
  to v3 where on the whitelist (add/get/update/delete).
- b24jssdk-vibecode: AI-add-on example updated to the new surface.

Top-level docs: README links to bitrix24-rest-v3-reference.md as the
canonical v3 protocol source. MAINTENANCE translation table now uses
actions.v{2,3}.*.make. REPORT logs anchor facts and open questions
(OAuth install handshake is the biggest remaining gap). SUGGESTED-
EXAMPLES re-prioritised with the new info.

docs/content/docs/99.examples: index + recipe 1 fully rewritten to the
new surface; recipes 2-9 markdown pages have their API references
updated (full code lives in .claude/skills/.../examples/).
… OAuth install

Closes the top-three items from SUGGESTED-EXAMPLES.md:

- Recipe 10 (error-handling cookbook): demonstrates the four error layers
  (SdkError / AjaxError / network-level / soft) and the new restriction-
  manager knobs (hardErrorCodes, softErrorCodes, retryOnNetworkError) via
  setRestrictionManagerParams. Shows the non-idempotent-call safety
  pattern (retryOnNetworkError: false + try/finally restore).

- Recipe 11 (event registration): CLI tool to list / bind / unbind
  outbound webhook events via event.get / event.bind / event.unbind.
  Closes the loop on recipe 7 (which receives events but doesn't
  register them). Idempotent — ERROR_EVENT_BINDING_EXISTS treated as
  success.

- Recipe 12 (OAuth install handshake): the largest missing piece for
  Marketplace-listed apps. Express server that handles
  ONAPPINSTALL / ONAPPUPDATE / ONAPPUNINSTALL, persists tokens per
  member_id, builds B24OAuth on demand, wires setCallbackRefreshAuth
  to write the latest token pair back to storage. Includes a demo
  endpoint GET /portal/:memberId/profile.

Also adds matching markdown pages under docs/content/docs/99.examples/
and updates SUGGESTED-EXAMPLES.md (Done section + re-prioritised
recommendations).
Self-review of #38 found that recipes were referencing `e.description` on
AjaxError / SdkError, but neither class exposes a `description` property
— it's a constructor param only. The thrown errors expose `code`, `status`,
`message` (inherited from Error), and AjaxError adds `requestInfo`.

This was hidden because `.claude/**` is excluded from the workspace tsconfig
and eslint config, so `pnpm run lint && pnpm run typecheck` did NOT catch
the typos. Verified by running tsc with a private tsconfig that points
`@bitrix24/b24jssdk` at the SDK source.

Fixes:
- 02-mass-messaging.ts: e.description → e.message
- 05-disk-files.ts: tighten batch result cast through `unknown`
- 07-webhook-handler.ts: annotate /health callback with Request/Response
- 10-error-handling.ts: e.description → e.message (×2);
  e.requestInfo.method → e.requestInfo?.method (optional in shape);
  reference helper fns with `void` so they read as used
- 12-oauth-install.ts: e.description → e.message;
  annotate all express callbacks with Request/Response
- b24jssdk-core SKILL.md: same fix in the canonical error-handling template
- b24jssdk-rest SKILL.md: same fix in the error-handling template

Remaining typecheck warnings against the recipes are only TS2307/TS7006/
TS7016 for opt-in deps (node-cron, grammy, openai, express) — expected,
those install with the recipe per the SKILL.md instructions.
Acts on the four follow-ups from the self-review of PR #38:

1. Recipes are now part of `pnpm run typecheck`. New tsconfig at
   `.claude/skills/b24jssdk-recipes/tsconfig.json` typechecks every
   recipe against the built SDK types (the bug from `e17be22` —
   `e.description` on AjaxError / SdkError — would have been caught here
   automatically). External opt-in packages (grammy / openai / express /
   node-cron) are stubbed as `any` via `types/external.d.ts` so the
   workspace doesn't have to ship them.

   Wired the new `skills:typecheck` script into the root `typecheck`
   pipeline. Documented the contract (build SDK first via `dev:prepare`;
   add a new `declare module` to the stubs when adding a recipe with a
   new external dep) in MAINTENANCE.md.

   Side effect of running real typecheck against the built dist: caught
   one more bug — grammy `ctx` parameters defaulted to `any` (recipe 6).
   Annotated explicitly to make the implicit-any deliberate.

2. Recipe 12 (OAuth install) now verifies `application_token` on
   uninstall — without this guard any caller that knows the URL could
   delete credentials for any portal whose member_id they guess. Also
   replies 200 first, verifies after, so a bad payload doesn't keep
   Bitrix24 retrying for 24h.

3. Recipe 7 (webhook handler) now reads `B24_APPLICATION_TOKEN` from the
   env and compares against `payload.auth?.application_token`. Optional
   (skipped if the env var is empty) so local smoke tests stay simple.
   Doc page references recipe 11 for event registration instead of the
   generic "via crm.event.bind or portal admin" line.

4. Recipe docs 4 and 6: added concrete cross-references for scaling —
   recipe 4 points at `batchByChunk.make` for bulk create, recipe 6 at
   `batch.make` for per-tick contact lookups.
Adds grammy, openai, express, node-cron and the two `@types/*` packages
that don't ship inline types (@types/express, @types/node-cron) as
workspace devDeps. Removes the placeholder stubs at
`.claude/skills/b24jssdk-recipes/types/external.d.ts`.

Effect: `pnpm run skills:typecheck` now validates not just SDK API
correctness, but also misuse of the recipe-side packages. The real
types caught one more bug — recipe 12 was passing `req.params.memberId`
to `clientForMember(memberId: string)`, but Express 5's `ParamsDictionary`
types each param as `string | string[]`. Fixed by adding the
`Request<{ memberId: string }>` generic on the handler.

MAINTENANCE.md updated: the "external stubs" workflow is replaced with
"install the package and its @types/* as workspace devDeps".
…first

Second-pass review of PR #38 found a few small things:

1. `tsconfig.json` `include` referenced `types/external.d.ts` which was
   deleted in 42c1d85. Removed the dangling glob.

2. Enabled `noUnusedLocals` / `noUnusedParameters` in the recipes tsconfig
   so dead helpers don't slip through (the previous review's
   `void safeCreateDeal` trick now has a strict checker behind it).
   Caught one unused helper in recipe 5 (`getFile`) and marked it the
   same way — `void getFile` with a comment explaining the educational
   purpose.

3. Recipe 12: handleInstall now follows the same reply-first / verify-
   after pattern as handleUninstall. If saveCredentials stalls (disk
   contention on the demo file store) Bitrix24 won't time out and retry.

4. Recipe 12: dropped redundant `B24OAuthParams & { applicationToken }`
   intersection — the type already includes the field. Renamed
   StoredCredentials to a plain alias.
Two real-world-behaviour issues called out in the third review:

1. Recipe 10 `safeCreateDeal`: setRestrictionManagerParams is instance-
   global, not per-call. Updated the inline comment with a GOTCHA block
   so readers copying the pattern into a concurrent codepath know that
   the policy applies to every call on the same $b24, and offers two
   alternatives (dedicated $b24 instance for non-idempotent flows, or
   per-method idempotency tokens).

2. Recipe 6 Telegram tick: a Telegram 429 / network error would abort
   the whole loop, leaving the cursor unchanged so the unsent deals
   were retried on the next tick — that part was fine. But isolating
   each send with try/catch (suggested in the review) introduced a NEW
   regression: a mid-batch failure followed by successes would advance
   the cursor past the failed deal, dropping it forever. Fixed by only
   advancing lastSeenDealId across the contiguous prefix of successes
   (sawFailure flag). Failed deals + everything after are retried on
   the next tick; preceding successes are not re-sent.
IgorShevchik pushed a commit that referenced this pull request May 26, 2026
choosing-the-right-method.md
- Add explicit caution block at the top: don't use the legacy
  AbstractB24 shortcuts (callMethod / callBatch / callListMethod /
  fetchListMethod / callBatchByChunk). They're @deprecated, emit a
  runtime warning on every call, and slated for removal in v2.0.0.
  Always go through b24.actions.v{2,3}.<primitive>.make(); link to
  the v0 → v1 migration guide for the one-to-one rewrites.
- Mention canonical API surface in the Overview so the reader sees
  the right shape before the decision matrix.
- Rewrite all five anti-patterns to name the canonical methods
  explicitly (e.g. `actions.v2.batch.make` instead of the short
  "Batch") and to lead with the deprecated-shortcut anti-pattern.
- Bump audited to 2026-05-26.

errors.md (follow-up to the user's #fd489ba ::rest-api-version-only
rewrite)
- Fix copy-paste bug in the v3 fetchList catch example: error.code
  comparison referenced JSSDK_CORE_B24_FETCH_LIST_METHOD_API_V2;
  must be _V3 in the v3 slot.

Verified no other open PR touches these two pages (PR #38 covers
adjacent material in .claude/skills/ but doesn't modify them).

docs:lint-pages → 0/0, docs:lint-links → 0/0.

https://claude.ai/code/session_01SexCZHTfKJDrfEU62hcQ54
# Conflicts:
#	docs/content/docs/99.examples/0.index.md
#	package.json
@IgorShevchik
Copy link
Copy Markdown
Collaborator Author

Ревью от автора PR #34 — рекомендации к интеграции

Контекст: PR #34 (claude/implement-skills-structure-nBN5h) делал ту же задачу — agent skills для SDK + сопутствующая чистка README-AI.md и cross-links в docs. С момента его последнего пуша main ушёл вперёд на 9 коммитов (включая v1.1.2 http-фиксы, рестрактуринг AGENTS.md, новые cookbook-страницы), а структура и зрелость этого PR существенно опережают мой подход. PR #34 буду закрывать — этот PR должен слиться. Ниже — то, что стоит подобрать из работы над #34 + несколько находок при сверке этого PR с исходниками.


Сильные стороны (важно зафиксировать — за ними стоит свой PR при будущих изменениях)

  • Recipes как компилируемые .ts со strict typecheck. Серьёзное преимущество над markdown-примерами: SDK-drift ловится pnpm typecheck, не «после копипасты». Это та проблема, которую я не закрыл — мои примеры в docs(skills): add agent skills for the SDK #34 могли молча устареть.
  • REPORT.md с anchor-facts на packages/jssdk/src/... с номерами строк. Должно быть стандартом для будущих скилов — у меня этого не было, и часть фактов в docs(skills): add agent skills for the SDK #34 пришлось верифицировать постфактум через ревью.
  • Security-hardening в recipes 7 и 12 (verify application_token, 200 до verify) — содержательно важная вещь, рекомендую вынести её в виде отдельной заметки в b24jssdk-core SKILL.md как «security checklist for event recipes», чтобы не потерялось в массе примеров.
  • Покрытие hardErrorCodes/softErrorCodes/retryOnNetworkError из недавних http-фиксов в main. У меня их не было — я отстал от этих коммитов.
  • VibeCode HTTP API skill — отдельная польза, у меня этого не было вообще.

Конкретные предложения

1. Discoverability через AGENTS.md (must-have)

AGENTS.md сейчас не упоминает .claude/skills/. Архитектурно правильно — Anthropic-агент автоматически грузит SKILL.md из .claude/skills/, — но человек или сторонний агент, который читает AGENTS.md (а тот объявлен «single source of truth for AI coding agents and human contributors»), не узнает о существовании скилов.

Предлагаю добавить секцию после ## Architecture, по аналогии с тем как сейчас перечислены .github/contributing/:

## Agent Skills (.claude/skills/)

For AI coding agents, the canonical skills are under `.claude/skills/`.
Load these by topic — do not load everything at once.

| Skill | When to use |
|------|--------|
| `b24jssdk-core` | First skill to load — entry point pick (Hook / Frame / OAuth), boot/teardown, error tuning |
| `b24jssdk-rest` | `actions.v{2,3}.*.make()` — call / batch / callList / fetchList / batchByChunk |
| `b24jssdk-filtering` | v2 prefix-keyed filter vs v3 array-of-triples; operators; `Text.toB24Format` |
| `b24jssdk-frame-ui` | iframe-only managers: slider / dialog / parent / placement / options / auth |
| `b24jssdk-helpers` | `useB24Helper`, Pull client, currency formatting, app/user options |
| `b24jssdk-recipes` | 12 end-to-end mini-apps; recipes are CI-validated TS |
| `b24jssdk-vibecode` | When to combine the SDK with the VibeCode HTTP API |

Maintenance / report docs sit alongside in `.claude/skills/MAINTENANCE.md`,
`REPORT.md`, `SUGGESTED-EXAMPLES.md`.

Это та же правка, которую я делал в AGENTS.md в #34, только под новый layout.

2. Cross-link .github/contributing/ ↔ skills

.github/contributing/transports-and-results.md и b24jssdk-rest/b24jssdk-filtering SKILL.md покрывают одну и ту же область с разных углов (один — для контрибьюторов кода SDK, второй — для агентов, генерирующих usage). Если они разойдутся — кому-то будет хуже.

Предлагаю добавить в начало каждого из 4 contributing-guide'ов одну строку:

Agent-facing mirror: for the same topic seen by AI agents generating usage code, see .claude/skills/b24jssdk-rest/ and .claude/skills/b24jssdk-filtering/. Keep this guide and the skills in sync when changing the underlying API.

Симметрично в .claude/skills/README.md уже есть отсылка на .github/contributing/ неявно (через ссылки на packages/jssdk/...), но явный mirror-link сделает синхронизацию обязанностью обоих PR'ов.

3. Cross-link существующих per-version REST docs

В #34 я добавил cross-links на скилы во все 19 страниц docs/content/docs/2.working-with-the-rest-api/ через frontmatter links:. В этом PR — только новые страницы под 99.examples/. Существующие per-version REST docs (1.call-rest-api-ver{2,3}.md, 2.call-list-rest-api-ver{2,3}.md, …) сейчас не знают про новый скил.

Это тривиальная правка (~20 строк), но она замкнёт треугольник docs ↔ skills ↔ source — а сейчас audited: workflow + docs:lint-links уже на месте и поймают рассинхрон.

Минимальный mapping:

Docs страница Skill ссылка
1.call-rest-api-ver{2,3}.md .claude/skills/b24jssdk-rest/SKILL.md
2.call-list-rest-api-ver{2,3}.md, 2.fetch-list-rest-api-ver{2,3}.md .claude/skills/b24jssdk-rest/SKILL.md
3.batch-rest-api-ver{2,3}.md, 4.batch-by-chunk-rest-api-ver{2,3}.md .claude/skills/b24jssdk-rest/SKILL.md
30.frame*.md, 31.frame-*.md .claude/skills/b24jssdk-frame-ui/SKILL.md
66.logger.md (нет соответствия — skip)
77.limiters.md .claude/skills/b24jssdk-core/SKILL.md (там, где про hardErrorCodes / retryOnNetworkError)

Применять через тот же iconName: GitHubIcon пункт в links:.

4. Audit-stamp на новые examples-страницы

В AGENTS.md зафиксировано требование audited: YYYY-MM-DD для страниц с category: actions / tools, и docs:lint-pages это проверяет. В 99.examples/*.md поле сейчас отсутствует. По AGENTS.md examples — «отдельный, более лёгкий скелет, не lint'ится» — формально проходит. Но если кто-то скрипту даст --strict — будут варнинги. Стоит либо явно опустить (как сейчас и есть, оставить), либо проставить дату для согласованности — на усмотрение.

5. Маленькая косметика в recipes — offClientSideWarning?.()

Recipe 1 (и, кажется, ещё пара): $b24.offClientSideWarning?.() — opional chaining намекает, что метод может отсутствовать. После рефакторинга http-клиента он есть на всех concrete entry points (B24Hook, B24OAuth). ?. тут безвреден, но создаёт ложное впечатление вариативности. Достаточно: $b24.offClientSideWarning().


Сверка фактов с исходниками (всё ОК — оставляю для протокола)

  • usePullClient() arg-lesspackages/jssdk/src/helper/use-b24-helper.ts:52 — да, без аргументов. Skill корректен. (В моём docs(skills): add agent skills for the SDK #34 я унаследовал usePullClient('prefix') из старого README-AI.md — это была ошибка с моей стороны. Хорошо, что у вас по-другому.)
  • v3 supported методыcore/version-manager.ts:21-44. Список в b24jssdk-rest/SKILL.md совпадает. ✓
  • Filter dialectsactions/v2/call-list.ts и actions/v3/call-list.ts. v2 = operator-prefixed object, v3 = array of triples. Совпадает. ✓
  • callList/fetchList игнорируют orderactions/v2/call-list.ts:77-79 и v3 mirror. Skill ссылается на правильный код, варнинг описан. ✓
  • AjaxResult.getData() shapecore/http/ajax-result.ts. Возвращает SuccessPayload<T> | undefined. Skill правильно требует ! или isSuccess check. ✓
  • v3 batch all-or-nothing — подтверждается. Skill это отмечает. ✓
  • idKey: 'id' для crm.item.* (lowercase fields в v2) — фактически верно. Подтверждаю по своему ручному тестированию во время docs(skills): add agent skills for the SDK #34.
  • customKeyForResult: 'items' обязателен для crm.item.list — да, иначе пустой массив на выходе. Anti-pattern в b24jssdk-rest это ловит правильно. ✓

Известное в REPORT.md, что я бы пометил приоритетом

Из «Open questions» в REPORT.md:

  • Recipe 7 webhook payload shape (data[FIELDS][ID] через express.urlencoded) — рекомендую проверить до мержа на живом outbound webhook. Это единственный recipe, где payload-shape не верифицирован против реального портала, и он не падает на typecheck — только в рантайме у потребителя.
  • Recipe 9 timeline comment (ENTITY_TYPE: 'deal' vs ENTITY_TYPE_ID: 2) — у меня тоже встречалась эта неоднозначность в SDK-документации; для нас сработало ENTITY_TYPE_ID. Рекомендую заменить на ENTITY_TYPE_ID: 2 в основном примере + комментарием упомянуть string-fallback.

Резюме

Контент PR качественнее моего по структуре, валидации и охвату. Перечисленные выше предложения не блокирующие — пятёрка (3 → 4 → 5) делается одним маленьким коммитом перед мержем, пункт (1) — отдельным следующим. Главное, что стоит подобрать: discoverability через AGENTS.md (#1) и cross-link существующих REST docs (#3) — это то, что у меня было готово, легко переносится в этот PR и закрывает реальные дыры в связности.

Если решите, что подобрать (1)/(3) в этом PR — могу подготовить патч одним коммитом, скажите.

PR #34 закрою после мержа этого.

— Игорь


Generated by Claude Code

claude added 2 commits May 26, 2026 20:00
…inks + polish

Acts on the review comment from PR #34 author (which is being closed in
favour of this one): #38 (comment)

1. AGENTS.md — new "Agent Skills" section listing the 7 skills with a
   one-line "when to use" table. Without this, a human / 3rd-party agent
   reading AGENTS.md as the "single source of truth" doesn't discover
   `.claude/skills/`.

2. .github/contributing/*.md — added an "Agent-facing mirror" reference
   block at the top of all 4 guides (transports-and-results,
   package-structure, testing, documentation) pointing at the matching
   skill files. Makes skill ↔ contributing sync a shared obligation.

3. docs/content/docs/2.working-with-the-rest-api/*.md — added a
   "Skill — <name>" link entry to frontmatter `links:` of 21 pages
   (call/callList/fetchList/batch/batchByChunk × {v2,v3}, frame-*,
   choosing-the-right-method, errors, limiters). Bumped `audited:` to
   2026-05-26 because adding the cross-link is the audit. `docs-lint`
   passes cleanly.

4. Cosmetic — replaced `offClientSideWarning?.()` with
   `offClientSideWarning()` across all skill files and recipes. The
   method is present on every concrete entry point; optional chaining
   created false impression of variability.

5. Recipe 9 — switched the canonical example to `ENTITY_TYPE_ID: 2`
   (numeric, accepted on every portal version per #34 author's testing)
   from the symbolic `ENTITY_TYPE: 'deal'`. Comment updated to mention
   the string fallback.

6. b24jssdk-core SKILL.md — new "Security checklist for event-receiver
   recipes" section consolidating the application_token + reply-200-
   first + setCallbackRefreshAuth + HTML-escape guidance that currently
   only lives in the per-recipe Notes blocks.

Skipped from the review:
- audited stamps on examples/*.md — reviewer explicitly noted this as
  "на усмотрение" and `docs:lint-pages` accepts it.
- Recipe 7 live-portal payload-shape verification — needs B24_HOOK,
  documented in REPORT.md as open question.
Pre-existing miss from PR #36 (which added new entries to the
docs/nuxt.config.ts route list on 2026-05-26 but did not bump the
audited stamp on docs/content/docs/1.getting-started/7.ai/2.llms-txt.md,
which links to that source file). docs-lint in strict mode fails when a
linked source is newer than the page's audited date.

Verified the nuxt.config.ts diff is route-list additions only — no
semantic change to the content described by 2.llms-txt.md, so the page
remains accurate; bump alone is correct.
…docs)

Five parallel role-reviews (docs, programmer, QA, security, CTO) found 16
fixable items. Three larger findings (unit-test infra, isolate recipe deps,
v3 whitelist CI monitor) are moved out to issues #64 / #65 / #66. The rest
land here.

🔴 Runtime bugs
- Recipe 6: replace contiguous-prefix `sawFailure` continue with `break`.
  Original logic still sent Telegram messages for deals after a mid-batch
  failure; cursor stayed at last-success-before-failure, so the next tick
  re-sent the already-delivered post-failure deals as duplicates. With
  `break`, the failed deal AND everything after wait for the next tick.
- Recipe 5: `batch.make` named-object form needs `returnAjaxResult: true`
  to expose named keys safely. Previous `getData() as { Storages, Children }`
  was a runtime lie — actual shape is the union `T | T[] | BatchPayloadResult<T>`.
  Now reads through AjaxResult per key as the canonical SDK spec shows.
- Recipe 8: `JSON.parse(content!)` had no null/parse guard. GPT can return
  null content (content-filter, rate-limit trim) or invalid JSON despite
  `response_format: 'json_object'`. Now throws with useful raw-snippet.

🔴 Security
- Recipe 7: `B24_APPLICATION_TOKEN` is now REQUIRED (throw on startup if
  missing). Previously the recipe accepted any POST when the env var was
  absent. Compare uses `timingSafeEqual` to defeat timing-recovery attacks.
- Recipe 12: same `timingSafeEqual` for the application_token check on
  /uninstall. Also `fs.writeFile(..., { mode: 0o600 })` so other local
  users can't read persisted tokens. INFO-level log no longer includes
  `domain` / `userId` (`logger.info('[${event}] member=${id}')` only).
  /portal/:memberId/profile demo route now has an explicit
  SECURITY-WARNING comment about missing auth.
- Recipe 7 log redaction: log only the keys of `payload.data.FIELDS`
  (not the values) to prevent future sensitive field leakage.

🔴 Docs facts
- b24jssdk-recipes/SKILL.md: «Nine» → «Twelve», description updated to
  mention recipes 10–12 (error handling, event registration, OAuth install).
- b24jssdk-recipes/SKILL.md: caveat about recipe-7-event-registration
  now correctly points to recipe 11 (it exists) instead of «not covered
  by these scripts».
- MAINTENANCE.md: recipe-section count and list updated 9→12.

🟡 Polish
- SUGGESTED-EXAMPLES.md: drop confusing «#1 / #6 / #8» gap numbers in
  the Done section (recipes have their own numbers 10/11/12).
- docs/.../99.examples/0.index.md: drop `offClientSideWarning?.()`
  optional chaining in the shared boot snippet — consistent with skills
  and recipes (the method is present on all entry points).
- Recipe 4: split `created` counter into `createdInB24` / `createdInErp`
  for actionable metrics.
- Recipe 3: `setInterval` now has an overlap guard so slow ticks don't
  race on `dealStages`.
- Recipe 6: add cold-start comment explaining that `lastSeenDealId = 0`
  floods all NEW-stage deals on first tick.
- tsconfig.recipes: explain `ignoreDeprecations: "6.0"` inline.

🟢 Traceability
- Recipes 7 and 9: add `UNVERIFIED_ON_LIVE_PORTAL` markers to the
  header docblocks with pointers to REPORT.md.

CI: `lint`, `typecheck` (incl. `skills:typecheck`), `docs:lint-pages` —
all green.

Co-authored-by: 5 role-review agents (docs, programmer, QA, security, CTO)
# Conflicts:
#	docs/content/docs/1.getting-started/7.ai/2.llms-txt.md
#	package.json
…ssing

The CI `docs-lint` job is a no-deps fast job — it intentionally does not
run `pnpm install` / `pnpm run dev:prepare` (see the comment on the job
in .github/workflows/ci.yml). The integration tests added in #87 spawn
the real docs-typecheck.mjs, which exits 1 when
`packages/jssdk/dist/esm/index.d.ts` is absent — causing the third step
of `docs-lint` to fail in CI.

Locally, where dev:prepare has been run, the tests execute normally.
In CI without dist, the three integration tests are now skipped with
the explanatory message, preserving the fast-job contract.

Verified: local run = 27 pass / 0 skipped (dist present);
simulated CI (dist removed) = 24 pass / 3 skipped / 0 fail.
claude and others added 11 commits May 28, 2026 12:36
Duplicates landed silently across two merges (PR #36 adding cookbook
deps + my PR #38 adding skill-related ones). Eight icon entries were
listed twice, and the order was append-only — hard to diff-review.

Alphabetical sort + dedupe. No functional change (Vite optimizeDeps
include is a set semantically); only diff hygiene.
On large portals (100k+ deals) the fetchList loop runs silently for
minutes between 'Loading deals…' and the final report — easy to
mistake for a hang and Ctrl-C out. Add a per-500-items progress log
with elapsed seconds; teaching comment explains how to drop it on
small portals.

Mirrored in the embedded code on docs/.../1.crm-analytics.md.
Two ergonomic fixes after running recipe 1 against a live portal:

1. LoggerBrowser is @deprecated — replace with Logger.create() +
   ConsoleV2Handler at INFO. Removes the noisy
   'INFO [channel]: info { params: { 0: "..." } }' wrapping and the
   two startup @deprecate warnings.

2. main().catch was logging the error through the structured logger,
   so a thrown error from inside loadAllDeals didn't show its stack
   visibly — script just exited 1 after 'Loading deals…'. Switched to
   raw console.error with explicit Error/non-Error branching so any
   future failure shows the actual stack on stderr.

Doc page mirrored. Pattern lifted from
playgrounds/cli/src/commands/make/companies.ts which is the in-repo
reference for the new Logger API.

Will roll the same migration into recipes 2-12 once verified on a
live portal — keeping the diff small for review.
3013 deals loaded in ~30s on a real bitrix24.ru portal; funnel printed
correctly (NEW 895, WON 1205, LOSE 912, win rate 56.9%, revenue 169M).
Recorded under the new 'Live-portal verification log' table in
REPORT.md so future audits can see which recipes have passed end-to-end
checks.
…Y_TYPE_ID

- Replace deprecated LoggerBrowser.build() with Logger.create() +
  ConsoleV2Handler(LogLevel.INFO, { useStyles: false }) in all 11 remaining
  recipes (02–12), matching the pattern from recipe 01 (commit 4a5bb55).
- Replace logger.error(e) in main().catch with raw console.error so
  structured-logger formatting cannot hide stack traces.
- Fix Logger API mismatches surfaced by skills:typecheck after migration:
  warn() → warning(); fold string[] second-args into the message;
  add missing second {} arg to error() calls that had only one argument.
- Recipe 09: swap ENTITY_TYPE_ID: 2 literal to EnumCrmEntityTypeId.deal
  enum constant; extract `fields` into a named variable; remove
  UNVERIFIED_ON_LIVE_PORTAL header marker (form confirmed correct).
- REPORT.md: add blocked entries for recipe 5 (disk scope missing) and
  recipe 11 (event.* requires OAuth, not B24Hook); close open question #5.

skills:typecheck passes with 0 errors after these changes.

https://claude.ai/code/session_016QWk3vjhi53bbNRw1PFLBa
Add a prominent "Webhook scope required" note in both the recipe JSDoc
header and the docs page so users know to tick "Диск (disk)" in the
portal's inbound webhook settings before running 05-disk-files.ts.
Confirmed via live-portal run: missing scope returns insufficient_scope (401).

https://claude.ai/code/session_016QWk3vjhi53bbNRw1PFLBa
disk.storage.getlist, disk.folder.addsubfolder, actions.v2.batch.make
all working. 5 storages, subfolder created, batch round-trip confirmed.

https://claude.ai/code/session_016QWk3vjhi53bbNRw1PFLBa
crm.item.get returns error code NOT_FOUND (not ERROR_NOT_FOUND which is
the crm.deal.get legacy code). The switch was falling through to default,
re-throwing, and crashing. Add NOT_FOUND as the primary case; keep
ERROR_NOT_FOUND as a fallthrough for callers still on crm.deal.get.

Caught by live-portal run on 2026-05-28.

https://claude.ai/code/session_016QWk3vjhi53bbNRw1PFLBa
All error paths confirmed: NOT_FOUND caught, SdkError for v3 whitelist
caught, full run without unhandled throws. Bug found and fixed in same
session (NOT_FOUND vs ERROR_NOT_FOUND for crm.item.get).

https://claude.ai/code/session_016QWk3vjhi53bbNRw1PFLBa
Resolve conflicts in package.json (keep skills:typecheck + add
package-jssdk:test:run-unit from main; merge devDependencies),
pnpm-lock.yaml (rebased from main + reinstalled), and
scripts/__tests__/docs-typecheck.test.mjs (take main's cleaner
INTEGRATION_SKIP approach).

https://claude.ai/code/session_016QWk3vjhi53bbNRw1PFLBa
@IgorShevchik IgorShevchik merged commit 077da09 into main May 28, 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.

2 participants