Developer API
For plugin developers: start CompatNav scans in the background, read the report and its findings, check code and plugin zips, and react when a scan finishes. CompatNav 1.1.0 and newer.
CompatNav 1.1.0 adds a small PHP API for add-ons: plain functions and actions that other plugins call on the same server. There are no HTTP endpoints, and nothing leaves the site. This page is for developers; site owners don’t need it.
CompatNav 1.2.0 brings API 1.1: check a piece of code in memory, scan a plugin or theme zip without unpacking it, list the report’s files, and a few smaller additions. They are marked (1.1) and described in Added in API 1.1.
Quick start
A small add-on that runs a full scan in WP-Cron and keeps the verdict. Schedule the
my_addon_scan event the way you like (for example with wp_schedule_event(), weekly):
my-addon.php
<?php
// Scans the site in WP-Cron and keeps the verdict. Needs CompatNav 1.1.0 or newer.
add_action( 'my_addon_scan', function () {
if ( ! function_exists( 'compatnav_start_scan' ) ) {
return; // CompatNav is missing or older than 1.1.0.
}
$state = compatnav_get_scan_state();
if ( ! $state['running'] ) {
$state = compatnav_start_scan( array( 'initiator' => 'scheduled' ) );
}
$stop = microtime( true ) + 15; // A step takes up to 8 s: stay inside a 30 s limit.
while ( ! is_wp_error( $state ) && $state['running'] && microtime( true ) < $stop ) {
$state = compatnav_run_scan_step(); // One batch of files per call.
}
if ( ! is_wp_error( $state ) && $state['running'] ) {
wp_schedule_single_event( time(), 'my_addon_scan' ); // Go on in the next WP-Cron run.
}
} );
add_action( 'compatnav_scan_completed', function ( $scan_id, $summary ) {
update_option( 'my_addon_verdict', $summary['verdict']['text'], false ); // Store only.
}, 10, 2 );
$ wp cron event run my_addon_scan
Executed the cron event 'my_addon_scan' in 16.561s.
Success: Executed a total of 1 cron event.
$ wp cron event run my_addon_scan
Executed the cron event 'my_addon_scan' in 15.095s.
Success: Executed a total of 1 cron event.
$ wp cron event run my_addon_scan
Executed the cron event 'my_addon_scan' in 16.609s.
Success: Executed a total of 1 cron event.
$ wp cron event run my_addon_scan
Executed the cron event 'my_addon_scan' in 15.323s.
Success: Executed a total of 1 cron event.
$ wp cron event run my_addon_scan
Executed the cron event 'my_addon_scan' in 13.564s.
Success: Executed a total of 1 cron event.
$ wp option get my_addon_verdict
Safe to upgrade to PHP 8.5: nothing active will breakThis is the real output: the example was loaded on our test site and its event was run with WP-CLI until it stopped rescheduling itself. Each run starts steps for up to 15 seconds, continues in the next WP-Cron run, and the listener stores the verdict when the scan completes. In a normal WP-Cron run the same code runs inside the cron request; WP-CLI only makes the runs visible.
Ground rules
- Server-side only. Functions and actions in PHP; no HTTP endpoints, no external requests.
- No scan work while a page is rendered.
compatnav_start_scan()(it reads the headers of the installed plugins and themes) andcompatnav_run_scan_step()(it lists and parses files) work only in WP-Cron, WP-CLI and AJAX requests. Anywhere else they return the errorwrong_context. The same goes forcompatnav_check_code()and package scans (1.1). Every other function only reads or deletes stored data. - One request at a time. Functions that change the scan take the same lock as CompatNav’s own
screen. While another request holds it, they return
busyand change nothing: try again later. The lock expires after 60 seconds if a request dies. Reading functions never take it. - One scan at a time, one report. A new scan replaces the report only when it has finished. Cancelling it leaves the last report untouched.
- Crash-safe. A step works within a budget: up to 200 files, 8 seconds (or a third of PHP’s
time limit when that is shorter) and 70% of the memory limit. If PHP dies while reading a file,
that file is recorded as not checked and the next step continues after it. If an exception is
thrown during a step, you get
unexpected_error; the file is recorded the same way, and the next call, even in the same request, continues. - No permission checks inside. WP-Cron and WP-CLI have no logged-in user. If you call the API
for a visitor (an admin page, an AJAX handler), check
compatnav_current_user_can_manage()and a nonce yourself, as CompatNav does. - No server paths, no code. Files are given as the report shows them (inside
wp-content), and findings carry a fingerprint (a hash), not the code line.
Checking for the API
if ( function_exists( 'compatnav_api_version' ) && version_compare( compatnav_api_version(), '1.0', '>=' ) ) {
// The API is there.
}
For the additions of API 1.1, check for '1.1' and keep a 1.0 path for sites that still run
CompatNav 1.1.0:
$has_1_1 = function_exists( 'compatnav_api_version' ) && version_compare( compatnav_api_version(), '1.1', '>=' );
The action compatnav_loaded fires once per request, on plugins_loaded, with the API version.
Which PHP version a scan compares against
A scan reports what changes between the PHP version the website runs and the target version. From the command line that isn’t always the PHP running the code: WP-CLI and real server cron jobs can use a different PHP binary than the website. So CompatNav remembers the version it sees when its admin screen loads or its screen scans, with the date it was last seen (at most one write a day).
compatnav_start_scan() chooses, in this order:
- The
current_versionargument, if you pass one (sourceargument). - In a web request (WP-Cron over HTTP, AJAX): the PHP running the request (source
running_php). - On the command line: the remembered website version (source
stored), returned with its date so you can decide whether it is recent enough. - On the command line with nothing remembered and no argument: the scan does not start, error
site_php_version_unknown. Open Tools → CompatNav once in wp-admin, or passcurrent_version.
Functions
compatnav_api_version(): string
The API version: '1.0' for CompatNav 1.1.0, '1.1' for CompatNav 1.2.0.
compatnav_current_user_can_manage(): bool
Whether the current user may use CompatNav, with the screen’s rule: manage_options, or on
multisite only super admins.
compatnav_get_site_php_version(): ?array
The remembered website PHP version, or null if it was never seen:
[ 'version' => '8.5', 'full' => '8.5.11', 'seen_at' => 1790000000 ] (Unix time).
compatnav_start_scan( array $args = [] ): array|WP_Error
Creates a full scan of all installed plugins and themes, like the screen’s “Start new scan”. It reads the plugins’ and themes’ headers and saves the scan; the first step lists and parses the files.
| Argument | Type | Default | Notes |
|---|---|---|---|
target | string | the screen’s saved target | 8.0 … 8.5. Doesn’t change the saved setting. |
skip_bundled | bool | the screen’s saved setting | Skip bundled libraries. Doesn’t change the saved setting. |
current_version | string | see above | 7.4, 8.0 … 8.5. |
initiator | string | api | scheduled, wp-cli or api. Shown on the report as “Started by: …”. |
caller (1.1) | string | none | Your plugin’s name, 1–50 characters: “Started by: scheduled scan (My Add-on)”. |
Unknown keys and wrong types are refused; a key set to null counts as not given. Returns the new
scan’s state (below). Errors: invalid_argument, invalid_target, invalid_version,
invalid_initiator, wrong_context (all before the lock), busy, scan_running,
site_php_version_unknown, unexpected_error. Fires compatnav_scan_started.
compatnav_run_scan_step(): array|WP_Error
Does one batch of scan work and returns the scan state. Call it again while running is true and
your request has time left. With nothing to do (no scan, or the scan is paused or finished), it
returns the state without an error. Errors: invalid_argument (it takes no arguments yet),
wrong_context, scan_running, busy, unexpected_error. Fires compatnav_scan_completed
when this step finishes the scan.
compatnav_get_scan_state(): array
The current or last scan, always with the same keys (with no scan yet, status is idle and the
other keys hold empty values). The main ones:
| Key | Meaning |
|---|---|
status | idle, collecting, parsing, paused, completed or cancelled |
running | collecting or parsing |
stalled | running, but no step for more than stalled_after_seconds (60) |
last_step_at, seconds_since_last_step | when a step (or start, pause, resume, cancel) last updated it |
scan_id, initiator, caller | the scan and who started it (screen, scheduled, wp-cli, api); caller (1.1) is the name passed at the start, or '' |
scope | type: full, or partial (a re-scan of one plugin or theme from the screen), and sources |
phase, percent, processed, counters | progress; counters: files, parsed, not_parsable, crashed, skipped, not_checked |
current_version, target_version, current_version_source, site_php, skip_bundled | the versions compared and where the current one came from |
context | how the scan runs: initiator, sapi, memory_limit, max_execution_time, limits_varied, current_version_source, site_php (details below) |
started_at, finished_at, active_seconds | times |
last_report | scan_id, finished_at and versions of the last completed report, or null |
compatnav_cancel_scan(): array|WP_Error
Cancels a running or paused scan and deletes its rows; the last report stays. With nothing to
cancel it returns the state. Errors: scan_running (from a listener), busy, unexpected_error.
Fires compatnav_scan_cancelled.
compatnav_get_report_summary(): ?array
The last completed report, with the same verdict and counts as the screen, or null. Ignored
findings are left out of all counts, and “active” is judged now, as on the screen.
| Key | Meaning |
|---|---|
verdict | code: safe, not_ready or not_fully_checked; text: the sentence the screen shows. safe only when nothing active will break and every active file was checked. |
active, inactive | counts of places that will break (errors) and deprecation notices (warnings) |
files | total, checked, not_checked, not_checked_active |
sources | one entry per plugin or theme with findings or unchecked files: id, type, slug, name, version_scanned, version_now, installed, active, status (update_available, no_update, not_wporg, custom, unknown, removed), new_version, counts |
coverage | the files that could not be checked, with a reason code (at most 100, as on the screen; all of them through compatnav_get_files() (1.1)), and the files skipped on purpose. Each item’s file is the path inside the plugin or theme folder; (1.1) path is the same file inside wp-content, as in findings |
scope, full_scan, rescanned | what changed the report last, the full scan it is based on, and plugins re-scanned since |
ignored, updates_checked_at, context, versions, times, legacy | as named |
compatnav_get_findings( array $args = [] ): array|WP_Error
The findings of the last report, in chunks, never all at once.
| Argument | Default | Notes |
|---|---|---|
after | none | the previous chunk’s next_after (a string like "57.4.12345"); leave it out for the first chunk |
limit | 100 | 1–500 |
source | all | one plugin or theme ID from the summary, e.g. plugin:akismet/akismet.php |
severity | all | error or warning |
active | all | all, active or inactive |
ignored | exclude | exclude (like the report), include or only |
scan_id (1.1) | the report | a completed package scan instead of the report (active and ignored don’t apply) |
It returns scan_id, items and next_after (null after the last chunk). Each item has id,
source_id, source_type, source_active, file, line, rule_id, php_version,
severity, confidence, message, more_count (further places of the same check in the file),
bundled, fingerprint and ignored. The fingerprint identifies a finding across scans, even when
its line number changes.
Paging never mixes two reports. The cursor carries the report and its revision. If a scan
finishes between two chunks (or a re-scan is merged into the report), the next call returns
report_changed: start again from the first chunk. Within one report, you get every finding once,
in ID order, without gaps.
findings-by-rule.php
<?php
// Counts the findings of the active plugins and themes per check, in chunks of 500.
$counts = array();
$after = null; // No cursor for the first chunk.
do {
$chunk = compatnav_get_findings( array( 'active' => 'active', 'limit' => 500, 'after' => $after ) );
if ( is_wp_error( $chunk ) ) {
echo $chunk->get_error_code(), "\n"; // 'report_changed': a scan finished; start again later.
return;
}
foreach ( $chunk['items'] as $finding ) {
$counts[ $finding['rule_id'] ] = ( $counts[ $finding['rule_id'] ] ?? 0 ) + 1 + $finding['more_count'];
}
$after = $chunk['next_after'];
} while ( null !== $after );
arsort( $counts );
foreach ( $counts as $rule_id => $places ) {
echo str_pad( $rule_id, 36 ), $places, "\n";
}
php84.implicitly_nullable 9
php84.trigger_error_user_error 4
php84.csv_escape_default 4
php82.relative_callable 3
php80.libxml_disable_entity_loader 2
php85.reflection_set_accessible 2
php84.deprecated_function 1
php85.deprecated_function 1
php82.dynamic_property 1
php82.utf8_encode_decode 1compatnav_get_rules(): array
Every check, keyed by its rule ID: php_version, severity, explanation (the plain-language
text the report shows), fix_before and fix_after. It includes syntax_incompatible, the
finding for code the target version can’t read at all, so every rule_id a finding can carry is
listed (CompatNav 1.1.0 and 1.2.0: 58 checks plus syntax_incompatible).
Added in API 1.1
CompatNav 1.2.0. Nothing of 1.0 changes: these are new functions, new optional arguments and new keys.
compatnav_get_supported_targets(): array
The target versions a scan accepts, the newest, and the screen’s saved target:
[ 'versions' => [ '8.0', '8.1', '8.2', '8.3', '8.4', '8.5' ], 'newest' => '8.5', 'saved' => '8.5' ].
compatnav_get_report_url(): string
The address of the report screen (Tools → CompatNav) for the current site, for links in emails and on other screens. It checks no permission: the screen does that when it is opened.
compatnav_get_files( array $args = [] ): array|WP_Error
Every file of the last report, in chunks, with the same cursor and the same guarantee as
compatnav_get_findings() (report_changed when the report changed). Unlike the summary’s
coverage list, it has no limit of 100, so you can compare what two scans checked exactly.
| Argument | Default | Notes |
|---|---|---|
after | none | the previous chunk’s next_after |
limit | 100 | 1–500 |
source | all | one plugin or theme ID |
status | all | checked, not_checked (the screen’s “could not be checked”) or skipped (skipped on purpose: test folders, bundled libraries when the setting says so, test files inside bundled libraries) |
active | all | all, active or inactive |
Each item has id, source_id, source_type, source_active, file (inside wp-content, as in
findings), status, reason (a code, '' when checked), reason_text, line, bundled, size
(bytes) and mtime (Unix time).
compatnav_check_code( string $code, array $args = [] ): array|WP_Error
Checks the code of one file in memory, with the same rules as a scan, and stores nothing. Use it
to verify a change: check the old and the new code the same way and compare. Arguments: target and
current_version, as for compatnav_start_scan() (on the command line, the remembered website
version, or site_php_version_unknown).
It returns status (checked, or why the code couldn’t be checked: not_parsable, too_complex,
too_large over 2 MB, crashed), reason_text, line (of a parse error, when known),
current_version, target_version and findings (each with line, rule_id, php_version,
severity, confidence, message, more_count).
It is a one-file check: what other files declare (a polyfill, a parent class, a namespace’s own
functions) isn’t known, so a scan of the whole plugin can report less. Like a scan step, it works
only in WP-Cron, WP-CLI and AJAX requests (it loads the parser): wrong_context elsewhere.
check-code.php
<?php
// Checks a piece of code in memory, before and after a change: nothing is stored, no file is read.
$before = <<<'CODE'
<?php
function acme_list( array $items ) {
while ( list( $key, $value ) = each( $items ) ) {
echo $key . ': ' . $value . "\n";
}
}
CODE;
$after = <<<'CODE'
<?php
function acme_list( array $items ) {
foreach ( $items as $key => $value ) {
echo $key . ': ' . $value . "\n";
}
}
CODE;
foreach ( array( 'before' => $before, 'after' => $after ) as $name => $code ) {
$result = compatnav_check_code( $code, array( 'current_version' => '7.4', 'target' => '8.5' ) );
if ( is_wp_error( $result ) ) {
echo $name, ': ', $result->get_error_code(), "\n";
continue;
}
printf( "%s: %s, PHP %s -> %s, %d finding(s)\n", $name, $result['status'], $result['current_version'], $result['target_version'], count( $result['findings'] ) );
foreach ( $result['findings'] as $finding ) {
printf( " line %d %s %s %s\n", $finding['line'], $finding['rule_id'], $finding['severity'], $finding['message'] );
}
}
before: checked, PHP 7.4 -> 8.5, 1 finding(s)
line 3 php80.removed_function error each() was removed in PHP 8.0. Use foreach instead.
after: checked, PHP 7.4 -> 8.5, 0 finding(s)Package scans
Scans a local zip file of a plugin or theme that isn’t installed, with the same engine, rules and versions as a normal scan, in steps. Nothing is extracted: PHP’s zip extension reads the PHP files one at a time, in memory; no code runs. The results are kept under their own scan ID and never change the report.
| Function | Returns |
|---|---|
compatnav_start_package_scan( string $zip_path, array $args = [] ) | the package scan’s state |
compatnav_run_package_scan_step() | one batch of work; the state |
compatnav_get_package_scan( int $scan_id ) | its state and, once completed, its summary; null if unknown |
compatnav_get_package_scans() | the kept package scans, newest first: scan_id, label, status, finished_at |
compatnav_cancel_package_scan() | stops the running package scan and deletes its rows |
compatnav_delete_package_scan( int $scan_id ) | deletes a kept package scan: true |
compatnav_get_findings( [ 'scan_id' => $id ] ) | its findings, once completed; file is the path inside the zip |
Starting. Arguments: target, current_version, skip_bundled and caller as for
compatnav_start_scan(), plus label (up to 100 characters, e.g. “Acme Booking 2.4.0”). The path
must be a local file: a URL is refused. Before anything is stored, the whole zip is refused when:
- PHP’s zip extension is missing:
zip_unavailable. There is no fallback that unpacks files. - It isn’t a readable zip file:
invalid_package. - An entry is unsafe (a
..folder, an absolute path, a drive letter, a backslash, a control character, a symbolic link), or the zip is too large (more than 20,000 entries, more than 200 MB unpacked, or an entry that would expand more than 100 times to over 1 MB):unsafe_package, with the reason and the entry in the error’s data.
Only .php, .inc and .phtml entries are read, with a normal scan’s rules (tests and
node_modules folders skipped, bundled libraries recognised). An entry over 2 MB is listed as not
checked.
Steps follow the same rules as scan steps (budget, lock, crash safety, WP-Cron, WP-CLI and AJAX
only). One package scan runs at a time (package_scan_running), and the report goes first:
while a normal scan is unfinished, a package step does nothing and returns scan_running; a normal
scan can always start. The zip is checked at every step: if it was changed or removed, the package
scan is cancelled and the step returns package_changed.
Keeping. Completed package scans are kept until you delete them; only the 20 newest are kept. Normal scans never delete them; uninstalling CompatNav does. The zip stays yours: CompatNav never moves or deletes it.
package-scan.php
<?php
// Scans a plugin zip that is not installed: read in memory, never extracted, kept apart from the report.
// $zip is the path of a local zip file (WP-Cron, WP-CLI or AJAX only; here: WP-CLI).
$state = compatnav_start_package_scan(
$zip,
array(
'label' => 'Acme Booking 2.3.0',
'caller' => 'My Add-on',
)
);
while ( ! is_wp_error( $state ) && $state['running'] ) {
$step = compatnav_run_package_scan_step();
if ( is_wp_error( $step ) && 'busy' === $step->get_error_code() ) {
sleep( 2 ); // Another request holds the scan lock for a moment: try again.
continue;
}
$state = $step;
}
if ( is_wp_error( $state ) ) {
echo $state->get_error_code(), ': ', $state->get_error_message(), "\n"; // e.g. unsafe_package
return;
}
$scan = compatnav_get_package_scan( $state['scan_id'] );
printf(
"%s (%s): %s, PHP files: %d, will break: %d, notices: %d, PHP %s -> %s\n",
$scan['label'],
$scan['source']['name'],
$scan['status'],
$scan['files']['total'],
$scan['errors'],
$scan['warnings'],
$scan['current_version'],
$scan['target_version']
);
$findings = compatnav_get_findings( array( 'scan_id' => $scan['scan_id'], 'limit' => 500 ) );
foreach ( $findings['items'] as $finding ) {
printf( " %s:%d %s %s\n", $finding['file'], $finding['line'], $finding['rule_id'], $finding['severity'] );
}
compatnav_delete_package_scan( $scan['scan_id'] ); // Or keep it: the 20 newest are kept.
Acme Booking 2.3.0 (Acme Booking): completed, PHP files: 2, will break: 1, notices: 1, PHP 7.4 -> 8.5
acme-booking/legacy-hooks.php:4 php80.removed_function error
acme-booking/legacy-hooks.php:8 php82.utf8_encode_decode warningTabs on the CompatNav screen
Your plugin can add a tab to the CompatNav screen that links to its own admin page:
add_filter( 'compatnav_screen_tabs', function ( array $tabs ) {
$tabs['my-addon'] = array( 'label' => 'My Add-on', 'url' => admin_url( 'tools.php?page=my-addon' ) );
return $tabs;
} );
The screen shows a tab bar only when a tab was added. It keeps at most 5 added tabs, shortens labels
to 40 characters and accepts only this site’s admin addresses. On your own page, call
compatnav_render_screen_tabs( 'my-addon' ) to show the same bar with your tab selected.
Smaller additions
callerwhen starting a scan (seecompatnav_start_scan()), shown in “Started by”, in the scan state, the report summary and the actions’$context.- While a scan that wasn’t started on the screen runs, the screen’s progress box says who started it, e.g. “Scheduled scan (My Add-on) in progress”.
- Coverage items carry
path, the findings’ path format.
Errors
| Code | Meaning |
|---|---|
invalid_argument | an unknown key, a wrong type, a malformed cursor, a limit out of range, arguments to the step |
invalid_target | not one of 8.0 … 8.5 |
invalid_version | current_version not one of 7.4, 8.0 … 8.5 |
invalid_initiator | not scheduled, wp-cli or api |
wrong_context | start or step outside WP-Cron, WP-CLI and AJAX (also compatnav_check_code() and package scans, 1.1) |
busy | another request holds the scan lock; try again later |
scan_running | a scan is already running or paused, or the call comes from a listener of a scan action; (1.1) a package step or start while a normal scan is unfinished |
site_php_version_unknown | command line, no remembered website version, no current_version |
unexpected_error | an exception during the operation; the file being read is recorded as not checked, and the next call continues |
report_changed | the report changed since your cursor (or the package scan was deleted); start again from the first chunk |
zip_unavailable (1.1) | PHP’s zip extension is missing on this server |
invalid_package (1.1) | not a readable local zip file |
unsafe_package (1.1) | an unsafe entry or a too-large zip; the error data has reason and entry |
package_scan_running (1.1) | another package scan is unfinished, or you tried to delete the running one (cancel it instead) |
no_package_scan (1.1) | a package step with no package scan to continue |
package_changed (1.1) | the zip changed or disappeared during the package scan, which was cancelled |
scan_not_completed (1.1) | findings of a package scan that hasn’t completed yet |
unknown_package_scan (1.1) | deleting a package scan that isn’t kept |
The codes are stable. The messages are for people, may change, and come in the site’s language.
Actions
| Action | Arguments | When |
|---|---|---|
compatnav_loaded | string $api_version | once per request, on plugins_loaded |
compatnav_scan_started | int $scan_id, array $context | every scan start, including the screen’s |
compatnav_scan_completed | int $scan_id, array $summary, array $context | when a scan has finished and its report is stored; $summary is the report summary at that moment |
compatnav_scan_cancelled | int $scan_id, array $context | after a cancel |
compatnav_package_scan_completed (1.1) | int $scan_id, array $summary | when a package scan has finished; $summary as compatnav_get_package_scan() |
compatnav_package_scan_cancelled (1.1) | int $scan_id | after a package scan was cancelled (also after package_changed) |
$context has scope, initiator, caller (1.1), target_version, current_version,
current_version_source, site_php, skip_bundled and context.
Rules for listeners. The actions run inside the scan request, after its work is saved, while the
scan lock is still held. So a listener must be fast, must not start, step or cancel scans (those
calls return scan_running), and must not send emails or call remote services. Store what you need
and do the rest in a separate request, for example
wp_schedule_single_event( time(), 'my_addon_after_scan', array( $scan_id ) ).
A listener that throws can’t stop the scan: CompatNav catches it, writes one line per action and
request to the PHP error log, in the form
CompatNav: a listener of <action> failed and was skipped: <exception class>: <message>, and the
scan continues. The exception ends WordPress’s loop over that action, so later listeners of the same
action don’t run in that request: catch your own errors.
Comparing scans
- Compare full scans with full scans. A re-scan of one plugin or theme (
scope.type=partial) replaces that plugin’s rows in the report; it is not a new baseline. - Coverage is not a problem. Which heavy files can be checked depends on how a scan ran. On our
test site, WP-CLI ran without a memory limit and checked a very dense file that scans with a 128 MB
or 256 MB limit skipped. The
context(SAPI, memory and time limits, the version source) says how each scan ran, so a file that moved between “could not be checked” and “checked” can be told apart from a new problem.
Stability
- Within API 1.x, changes are additive: new functions, new optional arguments, new keys in returned arrays, new actions. Existing ones keep their meaning. Ignore keys you don’t know.
- Something that has to go is marked deprecated first and kept for at least two minor releases of CompatNav; it is removed only in API 2.0.
- Rule IDs are part of the contract: an existing ID keeps its meaning, and a withdrawn one is never reused. The same goes for error codes, verdict codes, statuses and scope types.
- Texts come in the site’s language (Settings → General), not the current user’s, and may be reworded between releases: compare codes and IDs, never texts.
Changelog
- 1.1 (CompatNav 1.2.0): one-file code checks in memory, package scans (a zip read in memory, never extracted), the report’s files in chunks, supported targets, the report screen’s address, tabs on the CompatNav screen, a caller name in “Started by”, and the findings’ path on coverage items. Nothing of 1.0 changes.
- 1.0 (CompatNav 1.1.0): first version.