Skip to content

feat(tools)!: say how the log was captured and what the row cap hid - #140

Merged
lcottercertinia merged 4 commits into
certinia:mainfrom
lukecotter:102-63-136-listSlowOperations
Aug 14, 2026
Merged

feat(tools)!: say how the log was captured and what the row cap hid#140
lcottercertinia merged 4 commits into
certinia:mainfrom
lukecotter:102-63-136-listSlowOperations

Conversation

@lukecotter

@lukecotter lukecotter commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

Three changes to what apexlog_list_slow_operations and apexlog_list_limit_risks say about their own numbers. None changes what is measured.

Report the level the log was captured at (#102)

A capture level decides what reaches the log at all, so it qualifies every figure beside it. On a log taken at APEX_CODE,ERROR no method entry is emitted, and the work of everything the level hid pools at the nearest logged boundary: on a real customer log that puts 8,161 ms on a Visualforce page, which reads as a finding, and any advice built on it is confidently wrong.

Both tools now report apexCodeLevel, systemLevel, dbLevel and workflowLevel — the four categories that gate every ranked kind. They qualify the whole response rather than any one row, so each is a scalar stated once.

A category the header never declared is left out rather than named at a default. A level has no zero, so a default would state a value the log never did. This is the one deliberate exception to "report a fixed-schema field even at zero", and the reason is in the CaptureLevels doc comment so it is not later "fixed".

apexlog_get_summary is unchanged: timeByKind.logCategory and debugLevels already let the caller join.

Say how many operations the row cap dropped (#63)

The tool returns ten rows by default, and a table of ten out of ten reads the same as ten out of four hundred. matchedCount is the rows kind, namespace and minSelfMs matched, after grouping and before the cap — the one point in the pipeline where the figure exists.

Not doing: paging, a verbosity switch, or a prose "N more" line. offset is deferred; it would first need a total order, and the sort is durationSelfNs alone, so ties are unstable.

Drop lineNumber (#136)

lineNumber is the bracketed line on the log event, which Salesforce stamps with where the call was made — never a line in the code that ran. Verified against a sample app: METHOD_ENTRY|[11]|AccountService.getRevenue() is line 11 of the caller, which reads Decimal revenue = getRevenue();. An agent that reads it as the callee's line opens the wrong file.

#136 was first closed by saying so in the description. That clause cost 21 tokens on every turn, and the column ~18 per governor-heavy response, for a pointer that is null on 32.4% of grouped rows — measured over 60 real logs, 583 rows; codeUnit, flow, workflow and managedPackage have no Apex call site at all — and that names only the slowest call where a group folds calls from more than one caller. The column is therefore removed instead, and can be revisited.

Nothing has shipped with the column, so the 2.0.0 changelog entries read as if it never existed rather than gaining a Removed entry.

Rows are now {kind, name, namespace, callCount, durationTotalMs, durationSelfMs, durationSelfMaxMs, selfPercentage, soqlCount, dmlCount, soslCount, rowCount, thrownCount}.

Cost

Response cost, chars/4:

tool fixture before after
apexlog_list_slow_operations governor-heavy.log ~328 ~320
apexlog_list_slow_operations minimal.log ~112 ~111
apexlog_list_limit_risks governor-heavy.log ~39 ~39
apexlog_list_limit_risks minimal.log ~24 ~24

The apexlog_list_slow_operations definition is unchanged at ~361 tokens. Every budget raised is raised deliberately, with the reason beside it.

Also

  • DEVELOPING.md records the rule the levels follow: a fact that qualifies every number in the response is a response-level scalar, stated once; a fact that varies per row is a column. It also records why no magnitude figure is published — a figure measured once against one org, with the harness not committed, cannot be re-derived by CI and will rot.
  • The eval duplication check no longer scans table rows. It looks for a scalar restated in prose, and a cell that happens to read like one is another measurement of another thing: matchedCount 24 was reported as restated by a DML row whose durationTotalMs is 24468.441.

Verification

pnpm run build, pnpm run lint, pnpm test (277) and pnpm run eval (6 cases) all pass. Goldens re-recorded and read at every step: only the stated fields moved, and both apexlog_get_summary goldens are untouched throughout.

Closes #102
Closes #63
Closes #136

A capture level decides what reaches the log at all, so it qualifies
every figure beside it. On a log taken at APEX_CODE,ERROR no method
entry is emitted, and the work of everything the level hid pools at the
nearest logged boundary — 8,161 ms against a Visualforce page on a real
log, which reads as a finding.

apexlog_list_slow_operations and apexlog_list_limit_risks now report
apexCodeLevel, systemLevel, dbLevel and workflowLevel, the four
categories that gate every ranked kind. They qualify the whole response
rather than any one row, so each is a scalar stated once.

A category the header never declared is left out rather than named at a
default: a level has no zero, so a default would state a value the log
never did.

apexlog_get_summary is unchanged — timeByKind.logCategory and
debugLevels already let the caller join.

Closes certinia#102
apexlog_list_slow_operations returns ten rows by default, and a table of
ten out of ten reads the same as ten out of four hundred. matchedCount
is the rows kind, namespace and minSelfMs matched, after grouping and
before the cap — the one point in the pipeline where the figure exists.

The eval duplication check no longer scans table rows. It looks for a
scalar restated in prose, and a cell that happens to read like one is
another measurement of another thing: matchedCount 24 was reported as
restated by a DML row whose durationTotalMs is 24468.441.

Closes certinia#63
lineNumber is the bracketed line on the log event, which Salesforce
stamps with where the call was made — never a line in the code that ran.
An agent that read it as the callee's line opened the wrong file, and
that is the common case: the rows it matters most on name nothing
openable, such as DML Op:Insert Type:Account or List<Account>.add.

Say so in the tool description, so a client acts on it without reading
this repo, and in the SlowOperation.lineNumber doc comment. The
description costs 21 more tokens, and the definition budget is raised
for them.

Closes certinia#136
`lineNumber` is the call site in the caller's file. Naming that in the
description (certinia#136) cost 21 tokens on every turn, and the column itself
~18 per governor-heavy response, for a pointer that is null on a third
of grouped rows — every codeUnit, flow, workflow and managedPackage row
has no Apex call site — and that names only the slowest call where a
group folds calls from more than one caller.

Nothing shipped with the column, so the 2.0.0 entries read as if it never
existed rather than gaining a Removed entry.

The row shape is now {kind, name, namespace, callCount, durationTotalMs,
durationSelfMs, durationSelfMaxMs, selfPercentage, soqlCount, dmlCount,
soslCount, rowCount, thrownCount}.

Closes certinia#136
@lukecotter lukecotter changed the title feat(tools): say how the log was captured, what the cap hid, and what lineNumber points at feat(tools)!: say how the log was captured and what the row cap hid Aug 14, 2026
@lcottercertinia
lcottercertinia merged commit 2956cf1 into certinia:main Aug 14, 2026
14 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

2 participants