build(SDK-970): create agents and skills for tsdoc#1987
Conversation
The write-tsdoc skill documents a single exported symbol given its code and a prose description. The stub generator (scripts/tsdoc-stub.ts) extracts type parameters, parameter names, and return type from the TypeScript signature via ts-morph, producing a pre-filled skeleton so Claude fills in prose only — not structure. Workflow: npm run tsdoc:stub -- --file <path> --symbol <name> → grep .reports/embedded-react-sdk.public.api.md for release tag → fill in descriptions from provided prose Also excludes scripts/ from ESLint (same treatment as build/). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…efinements - Script now detects existing comments (JSDoc, block, line) and checks structural alignment (params, typeParam, returns, release tag) before emitting a skeleton — skips if aligned, replaces with pre-filled summary if not - Output includes DECLARATION: block so the skill never needs to read the source file; replace case adds DELETE_THROUGH: and OLD_COMMENT: blocks - --default-release flag controls fallback tag (default: alpha); script resolves @public automatically from the api-extractor report - Skill updated to reflect new output format, write-to-file step, and scope guard matching LIBRARY_BASE_PATHS / LIBRARY_IGNORE_PATHS Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… skill Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…e, events table rules, and api-report step Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…cost Token-profiler analysis showed the documenter was calling tsdoc-stub once per symbol, burning a full cache-read turn (~87K tokens) per symbol. Updated write-tsdoc to lead with --all-exports / --symbols and forbid per-symbol calls when multiple symbols need documenting in the same file. Updated the document-directory Phase 2 prompt to drive a file-first loop so the agent generates all skeletons in one stub call before writing. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Rename write-tsdoc → tsdoc-file and document-directory → tsdoc-directory. Add Phase 0 baseline build step to tsdoc-directory so the API report diff reflects only documentation changes made during the run. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
serikjensen
left a comment
There was a problem hiding this comment.
Looking good overall! i had a few questions here for clarification but i don't think anything blocking
|
|
||
| **ComponentsContext**: Note that UI rendering goes through `useComponentContext()` and visual output depends on the configured component set. | ||
|
|
||
| **Field Components** (`src/components/Common/Fields/`): Must be used inside a `FormProvider` from react-hook-form. |
There was a problem hiding this comment.
These are interesting codebase patterns to pull out. Are these meant to be codebase patterns that have implications for partners? Just wondering if there's a rhyme or reason to what gets included in this section. If it's just patterns generally, there may be more worth adding. But not sure if being exhaustive is important here
There was a problem hiding this comment.
I think I may delete this section, like you said it's not deeply exhaustive and it's just extra context added to every session that uses this agent
|
|
||
| **Field Components** (`src/components/Common/Fields/`): Must be used inside a `FormProvider` from react-hook-form. | ||
|
|
||
| **Partner hooks**: For hooks returning `errorHandling`, document how the result integrates with `composeErrorHandler`. Reference `composeSubmitHandler` for multi-form screens. |
There was a problem hiding this comment.
Yeah same kind of feedback here, there's much we could say about hook patterns so this is an interesting one to pull out
|
|
||
| Stop and ask the human before continuing if any of these occur: | ||
|
|
||
| - **Comment length**: The TSDoc comment you are drafting is more than twice as long as the code it documents. |
There was a problem hiding this comment.
Wonder if this merits a stop? I suppose it could document something really non standard, but in my experience sometimes claude is just having a verbose day and maybe instructing to be more succinct? I assume we already have some guidance for claude on comment length and keeping things to the point?
There was a problem hiding this comment.
I should delete this too, it's not applicable and it's not stopping anyway since the comments for flow components are always massive compared to the function body 😭
| | `event/string/value` | What triggers it | {@link DataType} or — | | ||
| ``` | ||
|
|
||
| Use `{@link TypeName}` (importing the type if needed) for the Data column when a type from `@gusto/embedded-api/models/components/` matches. Use `—` when the event carries no data. |
There was a problem hiding this comment.
Will this pull in the data type for the rendered docs? or does this link to source somewhere?
Also note there are some events that aren't from embedded-api so we may also need to consider a way of presenting those?
There was a problem hiding this comment.
Urgh this was a half-effort on my part that I need to rethink. The best way to do this is:
- make sure we consistently re-export the models from
@gusto/embedded-api-v-2025-11-15that are included in event bodies. It's not sufficient to say "look at the API docs" because those reference thesnake_caseparams but the actual response iscamelCaseproperties - switch to a rolled up
.d.tsfile. there's an option in our vite config that we can switch torollup: truewhich uses api-extractor under the hood, or we can do it ourselves since we already have the api extractor installed for the reporting- this means we don't have to
import { FederalTaxDetails } from '@gusto/embedded-api-v-....'just to reference it in a comment and have it resolve in an IDE - TypeDoc can handle it as long as that model is exported somewhere in the barrel files, it uses the global namespace to resolve links. but IDEs are dependent on what exists in a single d.ts file
- we may still want to add a lint rule to force ourselves to use the imports so we can have nice linking while writing
- this means we don't have to
- and then we can tell the documenters to always use the correct model
Right now I think I just want to remove this specific rule and just say to indicate what the response is or link to... the public API page? Or just write something and we can standardize it later?
There was a problem hiding this comment.
Here's a sample of what kind of markdown is emitted if we re-export the TS client types and link them. If the speakeasy generated types are already documented somewhere, we don't have to re-publish them here; we could just link to the URL of those docs. But the benefit of doing it this way is that the types are right there and we need them anyway if we ever want to add more specific typing to our event payloads 🤷🏻
…ofiling - Remove Codebase-Specific Patterns section from documenter (not exhaustive, adds noise) - Remove comment-length guardrail (not effective for flow components) - Add no-internal-leakage rule: public/beta/alpha comments must not mention internal symbols - Improve ESLint guidance: fix all errors in one pass, confirm with a single re-run - Backfill: use ESLint output directly for violation list, don't re-read source files - Backfill Case B: expand existing override block's files array instead of duplicating the block - Simplify events table Data column: plain text description instead of requiring cross-package links - Directory skill: batch documenter sessions by subdirectory (≤5 files each), parallel across groups Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Summary
Set up claude agents and skills that can orchestrate them to backfill API documentation with intention.
Changes
Generate api-report md as the source of truth for what is exported
Add
@mircosoft/api-extractoras a dev dependency and include a command to build the API report with full warningsendpoints-inventory:derivescripts into a CI build step so they are autogenerated on every branch that would change themCreate
build/tsdoc-stub.tsto build initial comments based on ASTEach export then gets emitted with context for the LLM about what the symbol's name is, where to insert the new comment, and the complete text of the declaration so we can skip expensive file reads.
Create
/tsdoc-fileskill that writes quality prose based on scriptAllow the LLM to autocomplete the partial documentation then edit the files in place.
The skill, without any additional context, emits this:
Create
tsdoc-api-documenteragent that uses the/tsdoc-filefile skill plus contextThe agent is responsible for gathering source material related to a file (whether from existing docs or MCP tool calls) to include in the context, along with running any generated comments based on that context through the eslint rules until they are resolved.
Running the agent with just
FederalTaxes, it looked updocs/hooks/useFederalTaxesForm.md(employee version, disregarded) anddocs/workflows-overview/company-onboarding.md. The comment it ended up generated looked more like this:Create
tsdoc-backfillagent that is responsible for turning on strict lintingThis agent doesn't write docs directly. It updates the eslint config to make sure that a directory starts doing stricter linting on comments, then gathers a list of all violations per file.
Calling the agent on
FederalTaxesmeans it will update the eslint config to include that directory in strict linting:{ + files: ['src/components/Company/FederalTaxes/**/*.{ts,tsx}'], ignores: LIBRARY_IGNORE_PATHS, rules: { 'tsdoc-coverage/require-comment': 'error', 'tsdoc-coverage/require-release-tag': 'error', }, },And the output it emits includes a list of lint violations:
Create
/tsdoc-directoryskill to orchestrate everythingThis skill
tsdoc-backfillagent to upgrade the directory to strict linting and figure out which files need better docstsdoc-api-documenterin the background with that list of files so that it can gather context and start writing documentation; with the strict linting enabled, it knows when each comment finally passesThis skill also knows to rebuild the api report and compare the diff to make sure we aren't introducing new issues (for example,
{@link CommonComponentInterface.onEvent}is a broken link since it references an internal, non-exported interface so the skill is able to replace it withonEvent)Final sample output for
FederalTaxescomponent:Diff in the
embedded-react-sdk.api.mdreport:Related
Testing
I have been running this locally on a variety of different directories, and I think it's finally ready. You can run this yourself locally with the command
/tsdoc-directory <path>but I don't recommend checking in any generated files yet.