Reported issues for ttctl
Pod holds 22 of 219 problems reported by people outside the maintainer team. Issues filed by the project's own owners, members and collaborators are excluded entirely — a maintainer's release checklist is not a warning to a prospective user.
Back to ttctl.
Most discussed
(feat) applications interviews notes update: UpdateInterviewTalentNotes mutation wrapper (CLI + MCP)
[!WARNING] BLOCKED on vendor (2026-06-12) — the talent-notes write surface is broken or unexposed in the Toptal UI in every checked state (scheduled interview 2026-06-03; during the live 2026-06-05 interview), and the live-probed API write model is append-only with undeletable guide-section notes — unsafe to ship from inference. Do NOT test-write against a real account. Implementation preserved on branch
feat/441-interview-notes-update(HEAD d115661; closed draft PR #676). Unbl
Read the thread · 2026-05-20 · open · 6 comments
surveys submit: OPEN_TEXT question with a sentinel option rejects free text
ttctl_surveys_submit (and the surveys submit CLI path) cannot answer free-text questions on ENGAGEMENT_ENDED surveys. Every OPEN_TEXT question on that survey kind is returned by the API carrying a single sentinel option in answers[], and buildSurveyAnswers dispatches question type on answers.length rather than on inputType — so the question takes the multiple-choice path and real prose is validated against a one-item enum.
Environment
| Detail | Value |
|---|---|
Read the thread · 2026-08-11 · closed · 4 comments
(wire-broke) external_update — twitter field rejected, whole call fails
TTCtl version
v0.1.0-rc.7
Exact command that failed
MCP tool call via Claude Code:
{
"tool": "ttctl_profile_external_update",
"args": {
"linkedin": "https://www.linkedin.com/in/<handle>",
"github": "https://github.com/<handle>",
"website": "https://<personal-domain>",
"twitter": "https://x.com/<handle>"
}
}
Error output (stderr + stdout)
Error: ttctl_profile_external_update failed (GRAPHQL_ERROR): External profile update failed: Variable $
[Read the thread](https://github.com/alexey-pelykh/ttctl/issues/526) · 2026-05-23 · closed · 3 comments
### (feat) profile.employment.add: custom (non-catalog) workplace via employerId:null
# Feature: support custom (non-catalog) workplace on `employment.add`
> **Updated 2026-05-19**: original framing ("create a new employer in Toptal's catalog") was based on an incorrect premise — there is no `CreateEmployer` mutation. The real mechanism is the existing `CreateEmployment` with `employerId: null`. Decision: **Option C** (see [decision comment](#) and § Decision below). Original Options A/B and their ACs are superseded.
## Use case
I need to add an employment entry to my Toptal p
[Read the thread](https://github.com/alexey-pelykh/ttctl/issues/401) · 2026-05-19 · closed · 3 comments
### (wire-broke) basic_update / employment_update / employment_add send null for server-required non-null fields
# Bug: write mutations send `null` for server-required non-null fields
## Environment
- **TTCtl version**: `v0.1.0-rc.3` (2026-05-19) — the release that closed #379
- **Invocation**: MCP server (via Claude Code MCP host); not CLI
- **Node.js**: v26.0.0
- **OS**: macOS (Darwin 25.3.0)
- **Toptal surface**: `talent-profile` (per dry-run preview output)
- **`auth status`**: valid (read tools work — `basic_show`, `employment_show`, `industries_list`, `skills_list` all return data)
This is a **wir
[Read the thread](https://github.com/alexey-pelykh/ttctl/issues/392) · 2026-05-19 · closed · 3 comments
### (feat) MCP list tools: add pagination to jobs/applications/viewed surfaces
## Problem / Motivation
The MCP list tools — `ttctl_jobs_list`, `ttctl_applications_list`, and `ttctl_jobs_viewed` — return only the first page of results (≤20 items) with no exposed pagination parameter. The Toptal portal paginates through the same data source, so the wire likely supports pagination — the MCP wrapper just doesn't surface it.
In practice this means callers cannot iterate beyond the first 20 items:
- A user with 37+ eligible jobs sees only ~20 via `ttctl_jobs_list`.
- A user w
[Read the thread](https://github.com/alexey-pelykh/ttctl/issues/369) · 2026-05-18 · closed · 3 comments
### MVP MCP tools: profile_show + profile_update
## Summary
Register the first two MCP tools on the (currently empty) MCP server: `profile_show` (read) and `profile_update` (write). Both call the same code paths as their CLI counterparts.
## Context
Per `CLAUDE.md` § MCP Integration: `ttctl mcp` starts an MCP server on stdio. Currently the server is built but registers no tools. This issue is the MVP MCP surface — two tools that mirror the read/write profile vertical.
The tools must be demonstrable from a Claude Desktop config — once regis
[Read the thread](https://github.com/alexey-pelykh/ttctl/issues/10) · 2026-05-05 · closed · 3 comments
### Expose timesheet approval status, not just submission state
## Problem / Motivation
TTCtl's timesheet tools surface only talent-side submission state. There's no way to answer a routine question: have any of my submitted timesheets been approved, or are they still waiting on someone else?
Across `ttctl_timesheet_list`, `ttctl_timesheet_pending_list`, and `ttctl_timesheet_show`, the only status-bearing fields returned are:
- `timesheetSubmitted` — whether *I* submitted it
- `timesheetOverdue`
No `approved`, `approvalStatus`, `approvedAt`, `approver`,
[Read the thread](https://github.com/alexey-pelykh/ttctl/issues/849) · 2026-07-10 · closed · 2 comments
## Most recent
### (chore) wire-routing manifest gate: enforce docs/wire-validation-routing.md against invoked ops
Follow-up from #883 (PR #883 body, § *Known gap, unowned — flagged for a maintainer call*). The second of the two ungated cross-domain doc surfaces. The maintainer call has been taken: build the mechanical check.
## The gap
CLAUDE.md § Track 1 vs Track 2 disposition states the rule outright: *"The manifest must be updated in the same PR as any new op invocation."* The manifest is [`docs/wire-validation-routing.md`](../blob/main/docs/wire-validation-routing.md), the authoritative per-op `T1` /
[Read the thread](https://github.com/alexey-pelykh/ttctl/issues/887) · 2026-08-12 · closed · 1 comment
### (chore) mcp tool-catalog gate: enforce packages/mcp/README.md against the EXPECTED_TOOLS roster
Follow-up from #883 (PR #883 body, § *Known gap, unowned — flagged for a maintainer call*), which widened `schema-contract-disposition` but deliberately left `doc-surface` narrow, on the grounds that *"an attestation is not detection"* — and named two cross-domain doc surfaces that are genuinely ungated. This is the first of the two. The maintainer call has been taken: build the mechanical check.
## The gap
`packages/mcp/README.md` carries a tool catalog — a stated total plus a per-domain brea
[Read the thread](https://github.com/alexey-pelykh/ttctl/issues/886) · 2026-08-12 · closed · 1 comment
### (feat) MCP-layer sliding-window rate limit for bulk job-signal mutations (anti-automation)
## Problem / Motivation
[ADR-009 § "What We're NOT Solving"](hq/engineering/adr/ADR-009-per-domain-consent-vocabulary.md)
explicitly carved this out as a **separate work item** to be created when #475
is picked up:
> **Anti-automation enforcement for #475** — the ≤20-id batch bound is
> README-discipline; the council's security panelist flagged that an agent can
> loop 50×20 (1000-id mass-view, schema-compliant). This needs an MCP-layer
> sliding-window rate limit, tracked as a **separate work
[Read the thread](https://github.com/alexey-pelykh/ttctl/issues/818) · 2026-06-16 · open · 0 comments
### (chore) disposition transitive npm-audit advisories (hono/ip-address/qs via MCP SDK — unreachable on stdio)
## Problem / Motivation
`pnpm audit --prod` reports **9 advisories** (1 low, 8 moderate) ahead of the v0.1.0 stable cut. A security-aware Toptal engineer runs `npm audit` right after `npm i -g ttctl` — for a credential-handling, Cloudflare-TLS-impersonating tool, "9 vulnerabilities" with no explanation on day one is a first-contact trust ding.
All 9 are **transitive through `@modelcontextprotocol/sdk`** and sit in its optional HTTP/SSE transport stack:
| Severity | Package | Advisory (abbrev.
[Read the thread](https://github.com/alexey-pelykh/ttctl/issues/770) · 2026-06-13 · closed · 1 comment
### (docs) packages/mcp/README.md: recount tool catalog for v0.1.0 stable (88 → actual, add surveys)
## Problem / Motivation
`packages/mcp/README.md:34` states the MCP server "registers **88 tools** (at time of writing)" and gives a per-domain breakdown. Both are stale on the published surface — and `packages/mcp/README.md` ships inside the `@ttctl/mcp` npm tarball, so this is the permanent first-impression catalog a v0.1.0-stable user reads.
Evidence (2026-06-13, against `main`):
- **Count undercount (~47%)**: README says 88; two independent counts of `"ttctl_*"` literals put the real total
[Read the thread](https://github.com/alexey-pelykh/ttctl/issues/769) · 2026-06-13 · closed · 1 comment
### (feat) readme-verbs gate: check ttctl_* MCP tool-name claims against MCP tool registrations
Follow-up to #762 (PR #764), from the post-submit review's frame-level finding.
The README "What It Does" section carries MCP tool-name claims (e.g. the Jobs bullet ships `ttctl_jobs_apply_similar_answers`). `scripts/check-readme-verbs.ts` reports these as unchecked by design — its comparison universe is the CLI command tree only. But MCP tool names are mechanically checkable in principle against the tool registrations under `packages/mcp/src/tools/**`, so the #751 drift class has an MCP-parity
[Read the thread](https://github.com/alexey-pelykh/ttctl/issues/765) · 2026-06-12 · closed · 2 comments
### (wire-broke) surveys submit: INTERVIEW_ENDED occurred checkbox unanswerable
### TTCtl version
`0.1.0-rc.13` (MCP server launched as `npx ttctl@next mcp`)
### Exact command that failed
MCP tool `ttctl_surveys_submit` (host: Claude Code), against a pending `INTERVIEW_ENDED` survey:
```json
{
"surveyId": "<survey-id>",
"answers": [
{ "questionId": "VjEtSW50ZXJ2aWV3RW5kZWRTdXJ2ZXlRdWVzdGlvbi0w", "value": "5" },
{ "questionId": "VjEtSW50ZXJ2aWV3RW5kZWRTdXJ2ZXlRdWVzdGlvbi0z", "value": "unsure" },
{ "questionId": "VjEtSW50ZXJ2aWV3RW5kZWRTdXJ2ZXlRdWVzdGlvbi0
[Read the thread](https://github.com/alexey-pelykh/ttctl/issues/754) · 2026-06-12 · closed · 2 comments
### (fix) README: drop unshipped timesheet update verb until #458 lands
## Problem / Motivation
`README.md:38` reads "**Timesheets** — list, view, submit, and update timesheet billing cycles", but `UpdateTimesheet` has no invocation in core and `packages/cli/src/commands/timesheet/` registers only list / show / submit / pending. The README ships in the npm package — users are invited to invoke a verb that does not exist.
How it happened: docs issue #431 ("append update verb") was closed completed on 2026-05-22 via e8b25dc while its prerequisite #458 (the `timeshee
[Read the thread](https://github.com/alexey-pelykh/ttctl/issues/751) · 2026-06-12 · closed · 1 comment
### (docs) security: classify surveys_* MCP tools in MCP-leakage threat model
Discovered during #704 (surfacing the shipped surveys domain in the storefront READMEs) — out of that issue's scope, deferred here.
## Gap
The `surveys` domain shipped in rc.13 (#671–#674) with three MCP tools:
- `ttctl_surveys_list` — read (pending surveys)
- `ttctl_surveys_submit` — destructive write (IRREVERSIBLE, consent-gated)
- `ttctl_surveys_feedback` — destructive write (IRREVERSIBLE, consent-gated)
`docs/security/mcp-leakage-threat-model.md` has **zero** `survey` mentions (`grep -i
[Read the thread](https://github.com/alexey-pelykh/ttctl/issues/725) · 2026-06-04 · closed · 1 comment
### (fix) MCP upload sandbox: resolve symlinks to close traversal/exfil bypass
Surfaced by the 0.1.0 release-readiness audit (2026-06-03). **Defense-in-depth bypass of a documented control; localized fix.**
## Problem
The MCP file-upload path-prefix sandbox does not resolve symlinks. `validateSandbox` (`packages/mcp/src/tools/file-upload.ts:228`) uses `path.resolve(filePath)` (logical `..` normalization only) and compares against `~/Documents | ~/Downloads | ~/Desktop`; there is no `realpath` / `lstat` / `readlink` anywhere in the upload path. The service then reads the t
[Read the thread](https://github.com/alexey-pelykh/ttctl/issues/707) · 2026-06-03 · closed · 1 comment
### (docs) README: surface shipped surveys domain; drop coming note
Surfaced by the 0.1.0 release-readiness audit (2026-06-03). **Storefront accuracy — fix before tagging.**
## Problem
The `surveys` domain shipped (rc.13; #671-#674: `ttctl surveys list/submit/feedback`, `core` `export * as surveys` at `packages/core/src/index.ts:89`, MCP `ttctl_surveys_*`) but the storefront still hides it:
- `README.md:34-42` "What It Does" — no surveys bullet.
- `README.md:64` — still reads "the `surveys` commands will appear under What It Does as they ship" (future framing).
[Read the thread](https://github.com/alexey-pelykh/ttctl/issues/704) · 2026-06-03 · closed · 1 comment
### (docs) README: align Node minimum to >=22.19.0 across package READMEs
Surfaced by the 0.1.0 release-readiness audit (2026-06-03). **Storefront accuracy — fix before tagging.**
## Problem
Four package READMEs state "Requires Node.js ≥ 24" but the published `engines.node` is `>=22.19.0`:
- `packages/{cli,core,mcp,ttctl}/README.md:22` → "Node.js ≥ 24"
- `packages/{cli,core,mcp,ttctl}/package.json` → `"engines": { "node": ">=22.19.0" }`
- Root `README.md:70` + root `package.json:9` → `>=22.19.0` (correct). CLAUDE.md frames Node 24 only as the release-action default L
[Read the thread](https://github.com/alexey-pelykh/ttctl/issues/703) · 2026-06-03 · closed · 1 comment
### (feat) applications interview show: inline job.title (Approach B of #694)
## Problem
Split from #694 (**Approach B**). `applications interview show` carries `job` only as `{ id, activityItemId }` — no human-readable **title**. A user can't tell *which job* an interview is for without a second command. #694 Approach A added a discovery footer pointing at `applications show <activityItemId>`; Approach B surfaces the title **inline** on the interview detail.
## Scope
Add `job.title` to the `Interview` op selection + projection + renderer:
- `packages/core/src/service
[Read the thread](https://github.com/alexey-pelykh/ttctl/issues/696) · 2026-06-03 · closed · 1 comment
### (feat) extend CLI/MCP output on interview + availability-request ops for better UX
## Problem
The interview + availability-request read ops (`applications interview show`, `applications interview notes show`, `applications interview guide show`, `applications availability-request show`) intentionally trim heavy job-context sub-trees from the wire — now documented as **BY-DESIGN wire trims** (#685). The trimmed context is reachable via a sibling command (`applications show <activityId>`), but:
1. **The end user isn't told.** Only the JSDoc (contributor-facing) names the sibli
[Read the thread](https://github.com/alexey-pelykh/ttctl/issues/694) · 2026-06-03 · closed · 1 comment
The remaining reports are on [the project's issue tracker](https://github.com/alexey-pelykh/ttctl/issues).