# Verification status — 2026-09-25

This records checks of the implementation developed on `codex/stockbit-safe-tools`, based on
upstream `194d429`, and prepared for version 1.4.1. Use this checkout or verified 1.4.1-or-later
release artifacts; the published 1.3.1 package does not contain these changes. The public `v1.4.0`
tag points to a 1.3.1 package manifest, so its release job stopped before publishing. This is a
technical test record, not regulatory approval or a guarantee of complete Stockbit website coverage.

## What the evidence means

A successful MCP call establishes that the tested arguments worked for this account at this time.
It does not establish every optional filter, every account entitlement, or the meaning of an empty
response. Tool metadata distinguishes **observed** mappings, writes verified by **read-back**, and
**projected** mappings. Some previously observed tools still contain unverified optional fields;
their descriptions state that limitation. The generated [tool reference](TOOLS.md) is the current
inventory; historical research in `PENDING-VERIFICATION.md` is not the current status.

## Safety boundary

- Real buy, sell, amend, cancel and e-IPO subscription tools and transport routes are removed.
  Legacy live settings and environment variables cannot restore them.
- Brokerage portfolio, cash, orders and history remain reads. They never switch to paper data.
- `paper_*` operates on the local simulation ledger. `virtual_*` operates only on Stockbit's
  `/virtualtrading/*` API using the ordinary market-data session.
- Credentials stay in the local credential store. No MCP tool accepts a PIN. Verification reports
  retain controlled test parameters, statuses, counts and comparisons, not account balances,
  identity records, tokens, or raw responses.

The offline suite checks the route boundary, legacy-setting refusal, simulation confirmation,
ambiguous write outcomes, account masking, cache separation, chart persistence verification, and
transport isolation. Tests never submit real orders.

## Authenticated checks

| Area | What was exercised | Result / limit |
|---|---|---|
| Ordinary login | Browser login and separate API process reads | Passed. The browser remains usable for charts. |
| Securities login | One-shot PIN exchange, read-only portfolio proof, then separate MCP process | Passed after fixing the grant envelope, storage-backend reporting, and encrypted access-cache handoff. The PIN is never saved by the client. |
| Securities renewal | Public frontend refresh headers/body checked; live renewal attempted | Renewal remains unverified: the refresh endpoint returned 401. Fresh login and cached access work. Re-run local `trading-login` when the securities session expires. Login/check use portfolio reads instead of unnecessarily rotating a new session. |
| Market, analysis and company data | Representative calls across the registered read tools | Reusable sweep covers quotes, books, broker flow, fundamentals, insider data, news, calendars, screeners, local calculations and renderers. See coverage below. |
| Real portfolio | Portfolio, absent position, cash, orders, history, performance, fees, tradability and masked account metadata | Actual nested response shapes were inspected without recording account values. Empty holdings/orders do not prove nonempty position/order mappings; synthetic regressions cover those branches. |
| Stockbit virtual account | Portfolio, held/absent position, orders and configuration | Reads passed against the website simulation account. |
| Stockbit virtual order lifecycle | Controlled simulated sell, amend and cancel through actual MCP calls on 2026-09-25 | Passed by read-back: a one-lot SUPA sell at 610 IDR became `OPEN`, amendment to 605 IDR produced a replacement `OPEN` order, and cancellation produced `WITHDRAWN`; the original order remained `AMENDED`. No open test orders remained. Holding quantities, costs and reservations, and virtual trading balance were unchanged by this scenario. |
| Stockbit virtual fills | User-authorized one-lot SUPA sell and one-lot GOTO buy through actual MCP calls on 2026-09-25 | Both orders reached `MATCH` with `total: 1`, `done: 1`, `open: 0`. Portfolio quantities and cash changed accordingly; unrelated holdings and preexisting orders were unchanged, with no open orders left. Activation and partial fills remain unverified. Raw `price_average` and `amount.matched` were fee-adjusted in these responses; see below. |
| Chart browser | Open, inspect shapes, analyze without drawing, draw one level, screenshot, save, REST read-back and selective cleanup | Passed. The created entity was found in saved drawings. Cleanup removed only that entity; the original two drawings remained and the saved count returned to two. |
| Watchlist edits | Temporary list: create, rename, add/remove BBRI, favorite/unfavorite, delete | Controlled scenario checks each write by reading it back and verifies existing lists remain unchanged. |
| Screener edits | Temporary screen: save, favorite/unfavorite, delete | Controlled scenario verifies the current frontend request shape and removes the test screen afterward. |
| Client protocols | stdio smoke, SDK initialize/list/call, loopback Streamable HTTP | Local protocol checks passed. Real Claude/ChatGPT account installation is a separate setup step; see [CLIENTS.md](CLIENTS.md). |

## Corrections driven by the live checks

- Securities grants now accept the observed `data.token` with target `2`; unsupported targets are
  refused before a PIN request. An unsuccessful PIN exchange is never retried automatically.
- Earnings supplies the required page, sort and order defaults. Trade-book probes supply both
  symbol and `group_by`. One-week chart series preserves hourly Jakarta timestamps instead of
  pretending it is daily data.
- `price_market` explicitly reads current orderbook market data because the old price endpoint
  rejects the documented request. Unsupported historical-date requests fail explicitly.
- Stream post detail uses the website's empty-body POST. Watchlist search includes the required
  watchlist ID and one-based page; cache keys retain those filters.
- Chart drawing reads supply both symbol and chart ID, deriving them only from unambiguous layout
  metadata. Drawing-template reads specify their line-tool type. Save verification bypasses stale
  cache; REST drawing writes check changed values and deleted keys, not just key presence.
- Chart labels preserve literal JavaScript replacement characters. Selective cleanup can name
  specific server-owned shape IDs and refuses IDs the server does not own.
- Watchlist names respect Stockbit's 25-character limit; favorite changes send `is_favorite`
  explicitly and preserve other favorites. Ad-hoc screener runs use the observed encoded filter
  and universe payload and expose pagination.

## Virtual lifecycle follow-up — 2026-09-25

At 11:09:59 WIB, the controlled virtual sell lifecycle ran while Stockbit accepted virtual orders.
The one-lot SUPA limit sell was placed at 610 IDR and amended to 605 IDR, above the observed
484 IDR bid and 486 IDR offer. The sell acknowledgement carried `order_ids: [string]`; amendment
carried `order_id: string` and `command: string`. Fresh reads verified the exact symbol, side,
price, total lots and good-for-day setting, then the replacement order's `WITHDRAWN` status.
The test did not fill. Final account comparisons confirmed unchanged holdings and virtual cash,
with zero open orders. No order IDs or account values are recorded here.

An earlier one-lot BBCA buy at 5,300 IDR was refused with HTTP 400 for insufficient virtual cash,
without creating an order. Together with the previous day's market-closed refusal, this verifies
buy rejection handling but does not establish the successful buy path. Neither refusal was
automatically retried. Virtual activation was not repeated on an already active account.

At 11:22:40 WIB, a separate, explicitly authorized test sold one virtual SUPA lot with a
484 IDR limit, then used part of the proceeds to buy one virtual GOTO lot with a 50 IDR limit.
Both were read back as `MATCH`, with `total: 1`, `done: 1` and `open: 0`. The SUPA
`balance_lot` decreased by one and GOTO `balance_lot` increased by one. Virtual cash increased
by 48,255 IDR after the sell and decreased by 5,010 IDR after the buy, a net test change of
43,245 IDR. Unlike the earlier cancellation scenario, these authorized fills intentionally changed
the two simulated holdings. All unrelated holdings and preexisting orders were unchanged, and
no open orders remained. This verifies successful buy and sell submission and full fills by
order, position and cash read-back. Activation and partial fills were not exercised.

The raw order fields require care: the sell returned `price_average: 482.548`,
`amount.matched: 48254.8` and `amount.fee: 145.2`; the buy returned `price_average: 50.1`,
`amount.matched: 5010` and `amount.fee: 10`. For these orders, `price_average` and
`amount.matched` included the fee adjustment: the sell matched amount was the 48,400 IDR
limit notional minus the fee, and the buy matched amount was the 5,000 IDR limit notional plus
the fee. Do not label raw `price_average` as the exchange execution price. The sell's cash
credit exceeded its raw matched amount by 0.2 IDR; this observation does not establish a general
rounding rule. The buy cash debit matched exactly.

The configuration endpoint's formula strings contained buy/sell rates of 0.0015/0.0025, while
the observed fees for these orders were 0.002/0.003 of their limit notionals. Those strings are
returned as upstream data, never executed, and must not be assumed to predict the actual fee.
The observed rates are evidence for these two orders, not a guarantee for other orders or accounts.

## Coverage and reproducibility

Run these from the checkout:

```bash
npm run typecheck
npm test
npm run build
npm run docs:tools
npm run smoke
npm run check:pack
npm run build:mcpb
```

The generated inventory currently contains **152 tools** with the `all` profile; the default `core`
profile contains **41**. The read-only live sweep inventories every registered tool:

```bash
npm run verify:tools -- --live
npm run verify:tools -- --live --only quote,portfolio --output .stockbit/verification-subset.json
```

Its private, ignored report is `.stockbit/verification.json`. A `passed` row means the MCP call
returned successfully, not that all optional variants were tested. `blocked` means a prerequisite
was unavailable. `not-run` includes mutations, browser effects and local paper tools that can settle
a ledger; those need isolated tests or the separate controlled scenarios above. The sweep does not
place virtual orders or modify watchlists, saved screens or charts.

On 2026-09-25, the full integration suite passed **1,978 tests** with no failures or skips.
Typecheck, generated tool docs, build, stdio smoke and npm package checks passed after the virtual
evidence update. A real-browser fixture timed out during an earlier run on 2026-09-24; it passed
in isolation and in the subsequent complete runs.

The final authenticated read sweep inventoried all **152 tools**: **101 passed**, **43 were
deliberately excluded from the read-only sweep**, and **8 were blocked**. The blocked set is one
real `order_detail` without an available order ID and seven e-IPO tools whose handoff/prerequisite
is unavailable. There were **zero failed calls** in that final sweep. Watchlist/screener mutations
and chart browser checks were verified separately as described above.

## Remaining limitations

- Stockbit's virtual buy, sell, full fills, sell-price amendment and cancellation have live
  read-back evidence. Activation and partial fills remain unverified. Offline tests cover request
  construction, confirmation, rejection, ambiguous outcomes and exact read-back matching.
- Virtual fee formula strings disagreed with the fees observed on the two filled test orders.
  Raw `price_average` and `amount.matched` were fee-adjusted; they must not be presented as
  an execution price and gross consideration without checking their meaning.
- No order detail was called with a fabricated ID when the real account had no orders.
- The authenticated e-IPO handoff endpoint returns 404. Dependent e-IPO reads remain unavailable;
  public `ipo_pipeline` information works. Real-money subscription capability is removed entirely.
- The underwriters directory endpoint returns 404; the explicitly scoped underwriter-code read
  works. These are different capabilities, and the server reports the directory failure plainly.
- Existing chart layouts/studies were not destructively replaced merely to exercise those tools.
  Browser study addition and low-level layout replacement remain covered by isolated fixtures;
  the controlled live check covers additive drawing, persistence and exact cleanup.
- ChatGPT needs its supported Secure MCP Tunnel or a properly secured remote deployment. The
  optional bearer-protected loopback HTTP endpoint is not a public OAuth deployment. Neither a
  ChatGPT tunnel nor a Claude account connection was installed during these tests.
- Private upstream APIs and account permissions can change. This build exposes its supported
  tools; it does not implement every Stockbit website action.
