feat(tools)!: say how the log was captured and what the row cap hid - #140
Merged
lcottercertinia merged 4 commits intoAug 14, 2026
Merged
Conversation
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
lcottercertinia
approved these changes
Aug 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three changes to what
apexlog_list_slow_operationsandapexlog_list_limit_riskssay 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,ERRORno 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,dbLevelandworkflowLevel— 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
CaptureLevelsdoc comment so it is not later "fixed".apexlog_get_summaryis unchanged:timeByKind.logCategoryanddebugLevelsalready 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.
matchedCountis the rowskind,namespaceandminSelfMsmatched, 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.
offsetis deferred; it would first need a total order, and the sort isdurationSelfNsalone, so ties are unstable.Drop
lineNumber(#136)lineNumberis 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 readsDecimal 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,workflowandmanagedPackagehave 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:
apexlog_list_slow_operationsgovernor-heavy.logapexlog_list_slow_operationsminimal.logapexlog_list_limit_risksgovernor-heavy.logapexlog_list_limit_risksminimal.logThe
apexlog_list_slow_operationsdefinition is unchanged at ~361 tokens. Every budget raised is raised deliberately, with the reason beside it.Also
DEVELOPING.mdrecords 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.matchedCount24 was reported as restated by a DML row whosedurationTotalMsis 24468.441.Verification
pnpm run build,pnpm run lint,pnpm test(277) andpnpm run eval(6 cases) all pass. Goldens re-recorded and read at every step: only the stated fields moved, and bothapexlog_get_summarygoldens are untouched throughout.Closes #102
Closes #63
Closes #136