Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions api_tests/api_test_force_overwrite_ownership/base/core/alpha.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* @Project: @cldmv/slothlet
* @Filename: /api_tests/api_test_force_overwrite_ownership/base/core/alpha.mjs
* @Date: 2026-09-28 21:58:00 -07:00 (1790657880)
* @Author: Shinrai <CLDMV>
* @Email: <Shinrai@users.noreply.github.com>
* -----
* @Last modified by: Shinrai <CLDMV> (Shinrai@users.noreply.github.com)
* @Last modified time: 2026-09-28 22:41:59 -07:00 (1790660519)
* -----
* @Copyright: Copyright (c) 2013-2026 Catalyzed Motivation Inc. All rights reserved.
*/

/**
* @fileoverview Base-module leaf, untouched by the overwriting add (#524).
* @module api_test_force_overwrite_ownership
*/

/**
* Return a marker identifying which module's implementation ran.
* @returns {string} Marker string.
*/
export function ping() {
return "base-ping";
}
25 changes: 25 additions & 0 deletions api_tests/api_test_force_overwrite_ownership/ext/main.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* @Project: @cldmv/slothlet
* @Filename: /api_tests/api_test_force_overwrite_ownership/ext/main.mjs
* @Date: 2026-09-28 21:58:00 -07:00 (1790657880)
* @Author: Shinrai <CLDMV>
* @Email: <Shinrai@users.noreply.github.com>
* -----
* @Last modified by: Shinrai <CLDMV> (Shinrai@users.noreply.github.com)
* @Last modified time: 2026-09-28 22:41:59 -07:00 (1790660519)
* -----
* @Copyright: Copyright (c) 2013-2026 Catalyzed Motivation Inc. All rights reserved.
*/

/**
* @fileoverview Original module's own leaf outside the overwritten path (#524).
* @module api_test_force_overwrite_ownership
*/

/**
* Return a marker identifying which module's implementation ran.
* @returns {string} Marker string.
*/
export function activate() {
return "ext-activate";
}
33 changes: 33 additions & 0 deletions api_tests/api_test_force_overwrite_ownership/ext/session/store.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* @Project: @cldmv/slothlet
* @Filename: /api_tests/api_test_force_overwrite_ownership/ext/session/store.mjs
* @Date: 2026-09-28 21:58:00 -07:00 (1790657880)
* @Author: Shinrai <CLDMV>
* @Email: <Shinrai@users.noreply.github.com>
* -----
* @Last modified by: Shinrai <CLDMV> (Shinrai@users.noreply.github.com)
* @Last modified time: 2026-09-28 22:41:59 -07:00 (1790660519)
* -----
* @Copyright: Copyright (c) 2013-2026 Catalyzed Motivation Inc. All rights reserved.
*/

/**
* @fileoverview Original module's leaves at the path the shadow module overwrites (#524).
* @module api_test_force_overwrite_ownership
*/

/**
* Return a marker identifying which module's implementation ran.
* @returns {string} Marker string.
*/
export function create() {
return "ext-create";
}

/**
* Return a marker identifying which module's implementation ran.
* @returns {string} Marker string.
*/
export function destroy() {
return "ext-destroy";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* @Project: @cldmv/slothlet
* @Filename: /api_tests/api_test_force_overwrite_ownership/ext/session/vault/lock.mjs
* @Date: 2026-09-28 21:58:00 -07:00 (1790657880)
* @Author: Shinrai <CLDMV>
* @Email: <Shinrai@users.noreply.github.com>
* -----
* @Last modified by: Shinrai <CLDMV> (Shinrai@users.noreply.github.com)
* @Last modified time: 2026-09-28 22:41:59 -07:00 (1790660519)
* -----
* @Copyright: Copyright (c) 2013-2026 Catalyzed Motivation Inc. All rights reserved.
*/

/**
* @fileoverview Original module's nested leaf under the overwritten path (#524).
* @module api_test_force_overwrite_ownership
*/

/**
* Return a marker identifying which module's implementation ran.
* @returns {string} Marker string.
*/
export function open() {
return "ext-open";
}
33 changes: 33 additions & 0 deletions api_tests/api_test_force_overwrite_ownership/shadow/store.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* @Project: @cldmv/slothlet
* @Filename: /api_tests/api_test_force_overwrite_ownership/shadow/store.mjs
* @Date: 2026-09-28 21:58:00 -07:00 (1790657880)
* @Author: Shinrai <CLDMV>
* @Email: <Shinrai@users.noreply.github.com>
* -----
* @Last modified by: Shinrai <CLDMV> (Shinrai@users.noreply.github.com)
* @Last modified time: 2026-09-28 22:41:59 -07:00 (1790660519)
* -----
* @Copyright: Copyright (c) 2013-2026 Catalyzed Motivation Inc. All rights reserved.
*/

/**
* @fileoverview Shadow module's replacement leaves, added with forceOverwrite (#524).
* @module api_test_force_overwrite_ownership
*/

/**
* Return a marker identifying which module's implementation ran.
* @returns {string} Marker string.
*/
export function create() {
return "shadow-create";
}

/**
* Return a marker identifying which module's implementation ran.
* @returns {string} Marker string.
*/
export function destroy() {
return "shadow-destroy";
}
25 changes: 25 additions & 0 deletions api_tests/api_test_force_overwrite_ownership/shadow/vault/lock.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* @Project: @cldmv/slothlet
* @Filename: /api_tests/api_test_force_overwrite_ownership/shadow/vault/lock.mjs
* @Date: 2026-09-28 21:58:00 -07:00 (1790657880)
* @Author: Shinrai <CLDMV>
* @Email: <Shinrai@users.noreply.github.com>
* -----
* @Last modified by: Shinrai <CLDMV> (Shinrai@users.noreply.github.com)
* @Last modified time: 2026-09-28 22:42:00 -07:00 (1790660520)
* -----
* @Copyright: Copyright (c) 2013-2026 Catalyzed Motivation Inc. All rights reserved.
*/

/**
* @fileoverview Shadow module's nested replacement leaf, added with forceOverwrite (#524).
* @module api_test_force_overwrite_ownership
*/

/**
* Return a marker identifying which module's implementation ran.
* @returns {string} Marker string.
*/
export function open() {
return "shadow-open";
}
19 changes: 17 additions & 2 deletions src/lib/handlers/api-manager.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2077,6 +2077,14 @@ export class ApiManager extends ComponentBase {
// routineManager is always registered and snapshotRawEntries always returns a Map, so the `|| new Map()` fallback is unreachable.
/* v8 ignore next */
const priorRawEntriesForModule = this.slothlet.handlers.routineManager?.snapshotRawEntries(moduleID) || new Map();
// Record the collision mode this add places moduleID's content under BEFORE buildAPI runs, so the
// registrations its wrapper construction (and any later lazy materialization) triggers through the
// generic impl:created/impl:changed subscribers use it rather than the instance default β€” under
// "replace"/"merge-replace" (forceOverwrite included) this module's content is what goes live, and
// the default "merge" recorded it as a merge loser wherever another module's function already sat
// (#524). Restored if the add is abandoned.
const priorPlacementMode = this.slothlet.handlers.ownership.setPlacementMode(moduleID, effectivePath, collisionMode);
const restorePlacementMode = () => this.slothlet.handlers.ownership.setPlacementMode(moduleID, effectivePath, priorPlacementMode);
// Revert whatever speculative ownership/raw state this candidate build has created for
// moduleID so far. Unlike the skip/warn cleanup calls elsewhere in this function (which walk
// a concrete apiToMerge value), this is also the recovery path for buildAPI()/setValueAtPath()
Expand All @@ -2085,6 +2093,7 @@ export class ApiManager extends ComponentBase {
// valid api-tree reference left to walk at that point, so both managers' state-driven variant
// is used instead (#372 review).
const revertSpeculativeState = () => {
restorePlacementMode();
this.slothlet.handlers.ownership?.revertSpeculativeState(moduleID, priorEntriesForModule);
this.slothlet.handlers.routineManager?.revertSpeculativeState(moduleID, priorRawEntriesForModule);
};
Expand Down Expand Up @@ -2428,6 +2437,7 @@ export class ApiManager extends ComponentBase {
this.slothlet.handlers.routineManager?.revertSpeculativeSubtree(apiToMerge, moduleID, effectivePath, priorRawEntriesForModule);
this.invalidateSpeculativeWrappers(apiToMerge);
}
restorePlacementMode();
throw err;
}

Expand Down Expand Up @@ -2586,15 +2596,20 @@ export class ApiManager extends ComponentBase {
// one whose own assignment was rejected under skip/warn and already had its
// speculative registration reverted above β€” silently undoing that revert (#366 review).
for (const key of rootSucceededKeys) {
this.slothlet.handlers.ownership.registerSubtree(rootSource[key], moduleID, key);
this.slothlet.handlers.ownership.registerSubtree(rootSource[key], moduleID, key, { collisionMode });
}
} else {
this.slothlet.handlers.ownership.registerSubtree(apiToMerge, moduleID, effectivePath);
// Passing the real collision mode lets a "replace"/"merge-replace" add claim every path it
// placed, over registrations that landed during the collision itself β€” in lazy mode the
// replaced module's wrappers materialize there and register after this module's (#524).
this.slothlet.handlers.ownership.registerSubtree(apiToMerge, moduleID, effectivePath, { collisionMode });
}
// Record the mount endpoint so setOwnedProperty can resolve this
// module's ownership root without consulting apiCacheManager.
this.slothlet.handlers.ownership.setModuleEndpoint(moduleID, effectivePath);
}
// Nothing of this add went live (skip/warn), so the placement it recorded does not apply (#524).
if (!anyAssignmentSucceeded) restorePlacementMode();

// Store API in cache (PRIMARY STORAGE). Gated on anyAssignmentSucceeded β€” the same guard the
// ownership registration above uses β€” and, for a root-level add, filtered to only the keys
Expand Down
Loading
Loading