Summary
When an installed script is actually changed after a Batch Update check has already produced a cached result, reconcile that script's existing batch-update record immediately from local data.
This should cover changes such as:
- updating the script from a separate install/update page;
- editing or pasting new script code in the editor and saving it;
- importing/replacing the script through another install path;
- any other successful path that changes the installed script code and/or author metadata.
The reconciliation should not perform another network update check. The Batch Update cache already contains the previously fetched target code and metadata, so the affected entry can be refreshed locally.
Non-script-content changes such as:
- reordering scripts;
- changing GM values / script storage;
- other data that does not change the installed script itself;
must not trigger this reconciliation.
Background
The Batch Update service keeps an in-memory record of the most recent update check.
For each pending update, that record contains data such as:
- the local script snapshot at check time;
- the local code at check time;
- the fetched target code;
- target metadata/version;
- code-similarity result;
- new-
@connect information;
- site information.
The current implementation already has two important protections:
- During an update check, before the result is committed, the service re-reads the current installed script and avoids writing a stale pending-update row if the script changed while the network check was running.
- When an update is performed through the Batch Update action itself, a successful item is removed from the batch-update cache.
Those protections are correct, but they leave another lifecycle gap:
the update check has already finished, the batch record is cached, and then the script is changed through some other path.
In that case, the existing batch record can continue describing the old local script state.
Example
- Script A is installed at
1.0.0.
- Batch Update checks the network and finds
2.0.0.
- The cached record now describes:
- local:
1.0.0
- remote:
2.0.0
- The user does not update from the Batch Update page.
- Instead, the user:
- opens the separate update/install page and installs
2.0.0; or
- manually pastes/saves code that is already
2.0.0.
- The Batch Update record still contains the previous
1.0.0 → 2.0.0 snapshot.
The user can therefore return to the Batch Update page and still see an update that is no longer applicable, or see risk/version information calculated against code that is no longer installed.
The page can eventually detect some stale situations when the user tries to act on the record, but that is too late. The record should stay coherent when the underlying script changes.
Why this should be local reconciliation, not another network check
The necessary remote-side data is already available in the batch record.
A pending record already contains the fetched target:
newCode;
newMeta;
- target version;
- other derived update information.
After the local script changes, the system only needs to compare the new local state against that cached target.
No additional fetch is required.
This has several advantages:
- immediate consistency;
- no unnecessary traffic;
- no update-check rate/latency cost;
- no change to the global "last checked" timestamp;
- no surprise background network operation merely because the user edited a script.
Desired behavior
After a successful mutation that changes an installed script, check whether the Batch Update cache contains an entry with the same uuid.
If there is no cached entry, do nothing.
If there is a cached entry, reconcile only that entry against the newly stored local script/code.
Case A — cached target is no longer an update
For example:
- cached target =
2.0.0;
- installed script changed from
1.0.0 to 2.0.0 or newer.
The old pending-update record should no longer be shown.
The implementation can either remove the entry or convert it to the repository's existing non-update representation, whichever better preserves current totalChecked semantics.
The important user-visible result is:
the stale 1.0.0 → 2.0.0 update must disappear without another network check.
Case B — the script changed locally, but the cached target is still newer
Example:
- cached check originally saw
1.0.0 → 3.0.0;
- user manually edits/replaces the local script and it is now
2.0.0;
- cached target remains
3.0.0.
The pending update is still valid, but the cached local side is stale.
Refresh the entry using the new local state:
- current script snapshot;
- current local code;
- old/current version;
- code similarity against the already cached target code;
- new-
@connect difference against current local metadata;
- other locally derived fields that depend on the old/current script.
Do not re-fetch the target.
Case C — local code changes without a version change
A user can manually edit/paste code while leaving @version unchanged.
This is still a real script change.
The cached record must not continue using the previous local code for similarity/risk calculations merely because the version string stayed the same.
Therefore reconciliation must not be based only on:
oldVersion !== newVersion
The trigger should be a successful mutation of the actual installed script/code, and the cached entry should be recomputed from the newly persisted state.
What should trigger reconciliation
The cleanest boundary is the successful path that commits a new/replaced installed script.
Current installation/update flows already converge through the script installation service for many cases, including normal install/update and editor save paths.
Reconciliation should happen only after the new script state has successfully persisted.
Relevant examples include:
- update/install page installs a new version;
- silent update;
- editor save after changing/pasting code;
- import/restore that replaces the active script;
- VS Code or other supported external update path if it ultimately commits through the same install/update service.
Centralizing this at the actual script-write boundary is preferable to adding Batch Update-specific calls in every UI.
What must NOT trigger reconciliation
This issue is specifically about the cached snapshot becoming stale because the installed script itself changed.
The following should not cause a Batch Update record refresh by themselves:
| Change |
Reconcile batch record? |
Reason |
| Script code changed |
Yes |
Cached local code/risk snapshot is stale |
| Author metadata changed through script replacement/save |
Yes |
Cached update comparison may be stale |
| Installed version changed |
Yes |
Cached version relation may be stale |
| Script reordered |
No |
Update content did not change |
| GM value / storage changed |
No |
Values are runtime/user data, not update content |
| UserConfig value changed |
No, unless it rewrites the actual installed script |
Does not change fetched update comparison |
| Last-run/runtime status changed |
No |
Not part of update content |
| Check timestamp changed |
No |
Does not change the script being compared |
| UI-only selection/filter state changed |
No |
No underlying script mutation |
Enable/disable and user-only execution-scope overrides should also not force an expensive code-comparison refresh unless they are intentionally part of the Batch Update record semantics. The core trigger should remain a real persisted script/code replacement.
Suggested implementation shape
Introduce a small, explicit reconciliation operation owned by the update-check cache, for example conceptually:
reconcileCachedScript(uuid, currentScript, currentCode)
or an equivalent method on ScriptUpdateCheck.
The exact API is flexible, but it should have these properties:
- Find only the matching cached entry.
- Return immediately if there is no current cache or no matching entry.
- Use the already cached target code/metadata.
- Re-read/use the newly persisted local script/code.
- Recompute only the affected entry.
- Regenerate the lightweight delivery cache via the existing cache serialization path.
- Notify open Batch Update views with the existing
refreshRecord mechanism when the visible record actually changed.
- Perform no network request.
This keeps cache-coherence logic in one place instead of teaching each caller how TBatchUpdateRecord is derived.
Ordering / transaction consideration
Reconciliation must happen after the script mutation succeeds.
Do not update the batch cache before the metadata/code write has committed.
Otherwise a failed install/save could make the Batch Update cache claim the new local state exists when storage still contains the old script.
Preferred ordering:
persist script metadata/code successfully
↓
perform normal install/update side effects
↓
reconcile matching Batch Update cache entry
↓
publish refreshRecord if the cached representation changed
The reconciliation is cache maintenance. Failure to update an in-memory cache should not corrupt or roll back an otherwise successfully installed script.
Interaction with an update check that is currently running
There are two related but different stale-record problems.
Already handled
When a network check is still running and the script changes before that check commits its result, current code re-reads the installed script before writing the final Batch Update record. This prevents the old check snapshot from resurrecting an update for a script whose version changed during the check.
That behavior should remain.
This issue
This issue covers:
network check completes
↓
batch cache exists
↓
script changes later through another path
↓
existing cache entry must be reconciled
The two mechanisms should complement each other.
If a script changes while an update check is concurrently constructing a new cache, final commit ordering must still ensure that the resulting cache reflects the current installed script rather than an older snapshot.
Do not change the meaning of “Check for updates”
Local reconciliation should not pretend that a new network check occurred.
Therefore it should not:
- advance the global Batch Update
checktime;
- show “checked just now” merely because a local script was saved;
- replace the cached target with a newly fetched target;
- update records for unrelated scripts.
The remote information remains from the previous check; only the cached local side is being brought up to date.
UI behavior
If the Batch Update page is already open, it should update naturally through the existing record-refresh mechanism.
Examples:
Script became up to date elsewhere
The stale row should disappear without requiring the user to press “Check for updates”.
Script changed but still has an update available
The row should remain, but its:
- current version;
- similarity/risk;
- new-
@connect assessment;
- other old-vs-new information
should reflect the newly installed local script.
The page should not flash into a full loading state just because one cached entry was reconciled.
Performance considerations
This should be a bounded local operation.
For one changed script:
- locate one cache entry;
- read/use one local script/code pair;
- compare it with the already cached target;
- recompute derived information for that entry;
- regenerate delivery data only if needed.
It should not:
- iterate/fetch every installed script from the network;
- start
checkScriptUpdate();
- fetch
checkUpdateUrl/downloadUrl;
- recalculate unrelated entries.
The common case where no batch-update cache exists should be essentially a no-op.
Testing
Please add regression tests around the service-level behavior.
Required cases
Concurrency regression
Acceptance criteria
Design intent
A Batch Update record is a cached comparison between:
the installed script at the time of comparison and the already-fetched candidate update.
Once the installed script changes, the old local half of that comparison is no longer trustworthy.
The cache should therefore follow actual script mutations, using the target data it already has, instead of waiting for the user to discover the stale record or forcing another network check.
Summary
When an installed script is actually changed after a Batch Update check has already produced a cached result, reconcile that script's existing batch-update record immediately from local data.
This should cover changes such as:
The reconciliation should not perform another network update check. The Batch Update cache already contains the previously fetched target code and metadata, so the affected entry can be refreshed locally.
Non-script-content changes such as:
must not trigger this reconciliation.
Background
The Batch Update service keeps an in-memory record of the most recent update check.
For each pending update, that record contains data such as:
@connectinformation;The current implementation already has two important protections:
Those protections are correct, but they leave another lifecycle gap:
In that case, the existing batch record can continue describing the old local script state.
Example
1.0.0.2.0.0.1.0.02.0.02.0.0; or2.0.0.1.0.0 → 2.0.0snapshot.The user can therefore return to the Batch Update page and still see an update that is no longer applicable, or see risk/version information calculated against code that is no longer installed.
The page can eventually detect some stale situations when the user tries to act on the record, but that is too late. The record should stay coherent when the underlying script changes.
Why this should be local reconciliation, not another network check
The necessary remote-side data is already available in the batch record.
A pending record already contains the fetched target:
newCode;newMeta;After the local script changes, the system only needs to compare the new local state against that cached target.
No additional fetch is required.
This has several advantages:
Desired behavior
After a successful mutation that changes an installed script, check whether the Batch Update cache contains an entry with the same
uuid.If there is no cached entry, do nothing.
If there is a cached entry, reconcile only that entry against the newly stored local script/code.
Case A — cached target is no longer an update
For example:
2.0.0;1.0.0to2.0.0or newer.The old pending-update record should no longer be shown.
The implementation can either remove the entry or convert it to the repository's existing non-update representation, whichever better preserves current
totalCheckedsemantics.The important user-visible result is:
Case B — the script changed locally, but the cached target is still newer
Example:
1.0.0 → 3.0.0;2.0.0;3.0.0.The pending update is still valid, but the cached local side is stale.
Refresh the entry using the new local state:
@connectdifference against current local metadata;Do not re-fetch the target.
Case C — local code changes without a version change
A user can manually edit/paste code while leaving
@versionunchanged.This is still a real script change.
The cached record must not continue using the previous local code for similarity/risk calculations merely because the version string stayed the same.
Therefore reconciliation must not be based only on:
oldVersion !== newVersionThe trigger should be a successful mutation of the actual installed script/code, and the cached entry should be recomputed from the newly persisted state.
What should trigger reconciliation
The cleanest boundary is the successful path that commits a new/replaced installed script.
Current installation/update flows already converge through the script installation service for many cases, including normal install/update and editor save paths.
Reconciliation should happen only after the new script state has successfully persisted.
Relevant examples include:
Centralizing this at the actual script-write boundary is preferable to adding Batch Update-specific calls in every UI.
What must NOT trigger reconciliation
This issue is specifically about the cached snapshot becoming stale because the installed script itself changed.
The following should not cause a Batch Update record refresh by themselves:
Enable/disable and user-only execution-scope overrides should also not force an expensive code-comparison refresh unless they are intentionally part of the Batch Update record semantics. The core trigger should remain a real persisted script/code replacement.
Suggested implementation shape
Introduce a small, explicit reconciliation operation owned by the update-check cache, for example conceptually:
or an equivalent method on
ScriptUpdateCheck.The exact API is flexible, but it should have these properties:
refreshRecordmechanism when the visible record actually changed.This keeps cache-coherence logic in one place instead of teaching each caller how
TBatchUpdateRecordis derived.Ordering / transaction consideration
Reconciliation must happen after the script mutation succeeds.
Do not update the batch cache before the metadata/code write has committed.
Otherwise a failed install/save could make the Batch Update cache claim the new local state exists when storage still contains the old script.
Preferred ordering:
The reconciliation is cache maintenance. Failure to update an in-memory cache should not corrupt or roll back an otherwise successfully installed script.
Interaction with an update check that is currently running
There are two related but different stale-record problems.
Already handled
When a network check is still running and the script changes before that check commits its result, current code re-reads the installed script before writing the final Batch Update record. This prevents the old check snapshot from resurrecting an update for a script whose version changed during the check.
That behavior should remain.
This issue
This issue covers:
The two mechanisms should complement each other.
If a script changes while an update check is concurrently constructing a new cache, final commit ordering must still ensure that the resulting cache reflects the current installed script rather than an older snapshot.
Do not change the meaning of “Check for updates”
Local reconciliation should not pretend that a new network check occurred.
Therefore it should not:
checktime;The remote information remains from the previous check; only the cached local side is being brought up to date.
UI behavior
If the Batch Update page is already open, it should update naturally through the existing record-refresh mechanism.
Examples:
Script became up to date elsewhere
The stale row should disappear without requiring the user to press “Check for updates”.
Script changed but still has an update available
The row should remain, but its:
@connectassessment;should reflect the newly installed local script.
The page should not flash into a full loading state just because one cached entry was reconciled.
Performance considerations
This should be a bounded local operation.
For one changed script:
It should not:
checkScriptUpdate();checkUpdateUrl/downloadUrl;The common case where no batch-update cache exists should be essentially a no-op.
Testing
Please add regression tests around the service-level behavior.
Required cases
1.0.0 → 2.0.0; install2.0.0through normalinstallScript; stale pending entry disappears.1.0.0 → 3.0.0; local script becomes2.0.0; row remains but local snapshot/version/risk is refreshed against cached3.0.0.@connect;withNewConnect/new-connect data is recalculated.checktimeis unchanged.refreshRecordonly when the cached visible record changes.Concurrency regression
Acceptance criteria
Design intent
A Batch Update record is a cached comparison between:
Once the installed script changes, the old local half of that comparison is no longer trustworthy.
The cache should therefore follow actual script mutations, using the target data it already has, instead of waiting for the user to discover the stale record or forcing another network check.