feat(tools)!: report every governor limit and where the time went - #114
Merged
Conversation
Record the rules the v2 rename follows: the apexlog_ prefix, a verb that states the shape of the result, banned verbs, and eight field rules. Add "don't state what the caller can derive" to the shaping conventions. Refs #107
Every failure to open the log reported "Log file not found", so a permission error, a directory in place of a file, or an exhausted descriptor table all sent the caller to look for a file that was there. Report ENOENT as missing, name the code otherwise, and keep the original error as `cause`. Refs #109
All three analysis tools documented an absolute path and accepted any string. A relative one resolved against the server's working directory, which is where the client spawned the server and not where the caller is, so it read a different file or none and reported neither. Declare the path once in apexLogSource, the module the three tools already share for loading a log, and refine it to absolute. Refinements do not reach the JSON schema, so the tool definitions are unchanged at ~1,053 tokens. Resolve execute_anonymous's outputDir to absolute too, so the filePath it returns stays one the analysis tools accept. A relative outputDir now anchors to the project root, the base the default already used. Refs #109
Only SIGINT was trapped, so a supervised restart, a container stop, or a client that ends a stdio server hit Node's default and exited without the clean close. Refs #109
execute_anonymous created directories anywhere the process could write, from an agent-supplied path. The MCP spec expects a server to work inside the roots the client declares, so report rather than refuse: the log is still written, and both the response and stderr name where it went. Silent for the default outputDir, for one inside a root, and for a client that declares no roots. Symlinks are followed on both sides. Refs #109
isMethodNode kept EXECUTION_STARTED and ENTERING_MANAGED_PKG, which own no time, and dropped every query, DML, flow and workflow node, which do. This is the one classification the tools will share, with grouping so a query repeated in a loop is one row carrying its call count. Nothing calls it yet. Refs #108
find_performance_bottlenecks returned four hand-built sections that
covered six of the thirteen limits, repeated the CPU figure as prose,
and ranked methods by namespace — a second, disagreeing answer to the
question analyze_apex_log_performance already answers.
It now returns one table: the limits at or above a threshold, worst
first, as {limit, used, max, usedPercentage} rows. All thirteen are
covered. The table is reported even when empty, beside the threshold
that selected it, so "nothing is at risk" is an answer rather than a
gap.
BREAKING CHANGE: the analysisType parameter is removed and replaced by
threshold. The cpuBottlenecks, databaseBottlenecks, methodBottlenecks
and governorLimitWarnings sections and the note field are gone.
Refs #108
analyze_apex_log_performance ranked methods, so the time a transaction spent in queries, DML, flows and managed packages was invisible. It now ranks every timed operation by self time in one table, with kind, namespace, limit, minSelfMs and groupBy to select and fold the rows. The prose summary and the recommendations are gone: the table says what they said. returnedSelfPercentage carries the one fact the rows cannot — what share of the transaction they account for between them. The definition costs 32% more, for the five selection parameters. The response costs 33% less on the governor-heavy log and 54% less on the minimal one. Closes #108
get_apex_log_summary reported five hand-picked totals and only the limits something was spent against. It now reports where the time went, one row per kind of operation, and what each namespace consumed. timeByKind names the trace category that decides whether a kind reaches the log, so a zero can be read: soql 0 beside DB NONE means the queries were not logged, and beside DB FINEST means none ran. limitsByNamespace shows a managed package spending your CPU time, which no tool could show before. truncated reads the top-level lines. The parser marks the line that lost its exit event, not the log, so the root flag is always false. The response costs 16% more on a log that uses its limits, for the two tables. No fact was dropped to pay for them. Closes #62
lcottercertinia
approved these changes
Aug 12, 2026
lcottercertinia
added this pull request to the merge queue
Aug 12, 2026
lcottercertinia
removed this pull request from the merge queue due to a manual request
Aug 12, 2026
lcottercertinia
added this pull request to the merge queue
Aug 12, 2026
lcottercertinia
removed this pull request from the merge queue due to a manual request
Aug 12, 2026
lcottercertinia
pushed a commit
that referenced
this pull request
Aug 12, 2026
## Why No workflow listened for `merge_group`. The merge queue tests the commit it builds, then waits for the required checks to report on it — and a check that does not run on `merge_group` never reports. So a queued pull request waits forever. #114 sat in the queue with every `pull_request` check green. ## What Add `merge_group` to CI, CodeQL, Workflow Lint and Dependency Review. `publish.yml` is release-only and does not belong in the queue. Dependency Review reads the pull request both for the two commits to compare and for a place to comment, and a queued run has neither. It now names the commits from the `merge_group` event, clones deep enough to hold them, and posts nothing. `cancel-in-progress` is already `github.event_name == 'pull_request'`, so a queued run is never cancelled. No change needed there. ## Testing `actionlint` and `zizmor` run in the Workflow Lint job on this pull request.
lukecotter
added a commit
to lukecotter/debug-log-analyzer-mcp
that referenced
this pull request
Aug 12, 2026
…nia#115) Stacked on certinia#114. `execute_anonymous` carried a sentence telling the caller to write a `.gitignore` entry, on the runs where the output directory was new. That is a rule applied to a fact the caller could not see. It now states the fact — `outputDirCreated` — and lets the agent apply its own rule. Four tokens where the sentence was fifteen. It is reported either way, not only when true: an absent field cannot be told apart from one the server never worked out. `success` is now `succeeded`, the bare adjective every other boolean in the server uses. Closes certinia#109
This was referenced Aug 12, 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.
get_apex_log_summaryreported five hand-picked totals and only the limits something was spent against. It now reports where the time went, and what each namespace consumed.timeByKindOne
{kind, logCategory, operationCount, durationSelfMs, selfPercentage}row per kind of operation. The row names the trace category that decides whether the kind reaches the log, so a zero can be read:soql 0besideDB NONEmeans the queries were not logged, and besideDB FINESTmeans none ran.limitsByNamespaceOne
{namespace, limit, used}row per limit a namespace consumed. A managed package that spends your CPU time is visible, which no tool could show before.Fixed:
truncatedwas always falseThe parser marks the line that lost its exit event, not the log. The root is a pseudo node it never terminates, so
apexLog.isTruncatednever becomes true. Truncation propagates up to a top-level line, so those are what is read now. Checked against a log cut at 60%: the root said false, a child said true.Gone
totalMethods,totalSOQLQueries,totalDMLOperations,totalSOQLRowsandtotalDMLRows.size,totalExecutionTimeandparsingErrorsare nowfileSizeBytes,durationTotalMsandparsingErrorCount. AdebugLevelsrow names itslogCategory, not itscategory, which is whattimeByKindcalls the same fact.Cost
16% more on a log that uses its limits, for the two tables it gained. No fact was dropped to pay for them.
Closes #62