Changelog
What changed in each su-ns-harness release, from the first public 0.4.0 to 0.5.1, covering checker, cache, results and write-safety changes.
On this page
Versions follow semver. The full history, with every fix, is in CHANGELOG.md in the repository.
0.5.1 (2026-09-28)
Reports
- Report periods as columns in one call. Pass
range: "month"orrange: "quarter"and the report comes back with one column per period plus a total. NetSuite only accepts the lowercase values and silently ignores others such asMONTHorperiod. The guard now lowercasesMONTHtomonth, dropsrangeon reports that don't support it, and the skill tells Claude to make one call instead of one per month. The note for arangethat had no effect lists the accepted values. - Aging reports showed the Current bucket in every column. The bucket columns share a path, and values were looked up by path, so every bucket repeated the Current amount. Values are now read by column id, then by position.
- Readable column names:
2026-01 … Total,2026-Q1 … Total, and for agingCurrent, 1-30, 31-60, 61-90, Over 90, Total. The raw column ids stay in the result's metadata. A warning appears when two value columns are identical on every row. - Report summaries print sections per column with the short headers, show
range,subsidiaryIdandbookin full on theQuery:line, and note sections such as- No Vendor -.
Data directory
- The CLI uses its own install's data directory,
<plugin>-<marketplace>, even before the first NetSuite call creates it. Other installs' directories are ignored and named once per session, not on every command.nsx doctor --preflightsays which directory it will use. - After
/reload-plugins, which doesn't fire SessionStart, the first NetSuite call's hook note carries the session context and theCache CLI:line.
0.5.0 (2026-09-27)
Setup
- Nothing to configure on install. The plugin no longer asks for 8 settings. Every setting has a default, and
nsx config set key=valuechanges one for the next call with no restart. Settings saved earlier through/pluginare no longer read, so re-set any you need withnsx config set. - No account id needed. The cache is keyed by the connector, and
account_idis optional. Set it to share one cache across connectors that reach the same account. - Init runs itself when the cache is empty, instead of asking you to type it.
- After installing or updating, run
/reload-pluginsor start a new session. A mid-session install can load late or only partly. - Claude can run the refresh and doctor skills itself.
SuiteQL checker
- Row caps use
FETCH FIRST.SELECT * FROM (<GROUP BY … ORDER BY …>) WHERE ROWNUM <= Nreturns partial sums in arbitrary order on NetSuite. The checker used to suggest that pattern. It now denies it (rownum-over-subquery) and suggestsORDER BY … FETCH FIRST N ROWS ONLY, andnsx sql fix-rownumno longer recommends an outerROWNUM. OFFSETis denied (offset-ignored). NetSuite ignoresOFFSET n ROWS … FETCH NEXT m ROWS ONLYand returns the first rows again, so "page 2" was page 1. When the offset is an exact multiple of the page, the fix suggestspageSize: m, pageIndex: n/m.- The rules whose results are always wrong can't be overridden:
ROWNUMwith aggregates or over a sorted subquery, never-matchingROWNUM,OFFSET, non-SELECTstatements, a second statement (multi-statement) and HTML-escaped operators (html-entity). [nolint:<rule>]overrides a single rule. Overridden errors are listed in Claude's context.[nolint]no longer skips the first-use metadata step.- New denials from Oracle's SuiteQL docs:
WITHclauses, unsupported functions,DATEliterals,INlists over 1000 items, mixed join syntax, right outer(+),+on strings and bracketed identifiers. - New
undefined-aliaserror forx.colwhenxisn't inFROMorJOIN. - A table missing from the cached record-type list is denied before the call. Plain
[nolint]overrides it. - New warnings: string date comparisons,
statuswithoutBUILTIN.CF, paging withoutORDER BY,ORDER BYon an aggregate alias withGROUP BY, andsum-across-subsidiariesforSUM(tal.amount)without subsidiary grouping on multi-currency accounts. - Unknown standard columns are a warning, not a deny, because the connector's field metadata misses real columns. The warning suggests the closest match.
- The approval-status rule no longer fires on a mere mention of
approvalstatus. On accounts without approval workflows it used to block an init probe. - Fewer false positives from
left-join-aggregate,status-without-cfand single-currency detection.
Paging and queries
pageIndex: 0is added along withpageSize. Without it the connector returns every page at once.pageSizebelow 5 is raised to 5, the connector's minimum.ns_getRecordfieldswith spaces used to return only the first field. The guard now strips the spaces.- Saved Search and System Note searches get the
typevalue the connector needs to run them.
Results
- Report results keep every line, flattened into
line,depth,is_detail,kindand one column per report column. results aggandpivotrefuse sums over nested report rows, base amounts across several subsidiaries, a header amount repeated on every line, and id and rate columns.- Totals across currencies show "currency unknown" or
n/ainstead of a sum. Subsidiary reports say which currency their amounts are in. - Values like
$1,234.00used to be skipped. They're now parsed, and anything unparsable is counted. results diffcompares every--colscolumn, handles currencies the same way asaggandpivot, and no longer double-counts report rows. A self-diff reports no differences.--wherecompares dates as dates, takes quoted column names, and combines repeated flags with AND.!=and=never match blanks.results head,filterandexporttake--sort col [--asc].results agg --min/--maxworks on date columns.--sumand--avgon a date or text column is an error instead of0.- New
nsx results concatjoins pages of one query.
Cache and account profile
- Catalog results that would empty or halve a cached section, or can't be parsed, don't replace it.
- Catalog payloads never reach Claude's context.
- Init fetches field metadata for the five GL core tables only. Other tables are fetched on first use.
- Trailing twelve months of revenue is per subsidiary, and the consolidated figure comes from the Income Statement (
nsx profile ttm-report, using the last 12 complete accounting periods). - When the role can't see
subsidiary, base currency is taken from transactions at exchange rate 1 and marked for you to confirm. - The CLI no longer switches to an empty sibling data dir. It prefers a dir with a cache, then the one whose hooks fired most recently, and names its choice on stderr.
Errors
- Rate-limit retries wait 5, 10 and 20 seconds plus jitter (was 2, 4 and 8). The counter resets after 5 quiet minutes.
SSS_USAGE_LIMIT_EXCEEDEDis treated as a governance error, not a rate limit.- A bad column fails with a generic "unexpected SuiteScript error". When the checker flagged a column on that call, the error is reported as
bad_field_likelyand names it. - "Permission Violation" and "Unable to determine record type" inside a successful saved-search result are classified as errors.
- Error bodies (HTTP 4xx and 5xx strings,
{status, title}objects) are recognised as errors. bad_record_typeonns_getRecordgives REST record-type advice, not SuiteQL advice.
Security
- Path traversal fixed. A crafted
recordTypecould write cache files outside the plugin's data dir. Section names are now validated, and every cache path is checked to stay inside the account dir. - Write preview compares exact paths. The diff used to match fields by leaf name, which could hide a change from the approval prompt. The pre hook also rebuilds the diff from the input being sent.
- Write detection is an allowlist. Every NetSuite tool not known to only read goes through
read_only, preview and approval. Unknown tools say so in the prompt. - "Write succeeded" needs proof: a record id or
success: true. Without either, the result says the outcome is unknown and Claude reads the record back. read_onlyfails closed on unrecognised values. Turning writes on is refused from anything Claude Code runs, including underscript.
CLI
--versionworks. Unknown commands, subcommands, flags and arguments are errors (exit 2) with a suggestion, such as--were→--where.sql lintandpreviewread stdin only with an explicit-. They used to hang.profile setvalidates keys and values, and an empty value removes an override.- Every hint prints the full CLI command.
0.4.0 (2026-09-27)
First public release.
- Local metadata cache per account and environment: reports, saved searches, field metadata, subsidiaries, periods and an account profile, with per-section TTLs (
ttl_overrides). - Large results saved to local files. Claude gets a short summary and a result id, and works on the file with
nsx results agg|filter|pivot|diff|export. - SuiteQL checker in front of every query:
ROWNUMnext toGROUP BYorORDER BY,SELECT *,typeandrecordtypeswaps, unusedapprovalstatusfilters, unknown columns. - Connector errors classified with a recovery step. Rate-limit retries run one call at a time.
- Writes blocked by default (
read_only). When enabled, each create or update needs a preview and your approval, and every attempt is in the audit log (nsx audit export). - CSV exports neutralize formula-leading cells and warn when the file isn't git-ignored.
Install su-ns-harness
Run both inside Claude Code, then /reload-plugins. Version 0.5.1, free and open source.
/plugin marketplace add suiteutils/suiteutils-netsuite-claude-harness/plugin install su-ns-harness@suiteutils
Page last updated 2026-09-28. NetSuite is a trademark of Oracle. Claude is a trademark of Anthropic. Suite Utils is not affiliated with either.