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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Change Log

## Unreleased

### Bugs fixed

* Fix the workspace shelf (`ws-save!`/`ws-save-as!`/`ws-load!`) storing a live reference instead of a snapshot: mutating the workspace after saving (e.g. clearing the log) corrupted the shelved copy, so a later load restored nothing. It now shelves a fully-dereferenced snapshot, like a recording does.
* Fix `rec-save-as!` reporting `id 'null' to slot 'null'` - it read the recording atom instead of its value.

## [0.7.0] - 2026-07-10

* Editor support for the new data functionality: `sayid-tap-trace` (`C-c s d t`) taps the workspace to your data tool, and `sayid-capture-baseline` (`C-c s d b`) / `sayid-diff-traces` (`C-c s d d`) drive the snapshot-then-compare flow. Backed by new `sayid-tap-trace`, `sayid-capture-baseline` and `sayid-diff-traces` nREPL ops.
Expand Down
1 change: 1 addition & 0 deletions src/sayid/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@ user> (-> #'f1 meta :source)
(->> (#'rec/save-as! recording
(:rec-ns @config)
slot)
deref
((juxt :id :rec-slot))
(apply format "Saved recording with id '%s' to slot '%s'.")))
(util/defalias r-sa! rec-save-as!)
Expand Down
16 changes: 9 additions & 7 deletions src/sayid/shelf.clj
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@


(defn save!
[item shelf slot-kw mk-ex-msg-fn]
(let [item' @item
"Shelve ITEM's value under its SLOT-KW slot in namespace SHELF. PREP, when
given, transforms the value before it's stored - the workspace shelf uses this
to store an independent snapshot rather than a live, still-mutating value."
[item shelf slot-kw mk-ex-msg-fn & [prep]]
(let [item' ((or prep identity) @item)
slot (slot-kw item')]
(if (symbol? slot)
(sym/def-ns-var shelf slot item')
Expand All @@ -14,11 +17,10 @@
item'))

(defn save-as!
[item shelf slot-kw slot mk-ex-msg-fn]
(doto item
(swap! assoc slot-kw
(sym/qualify-sym shelf slot))
(save! shelf slot-kw mk-ex-msg-fn)))
[item shelf slot-kw slot mk-ex-msg-fn & [prep]]
(swap! item assoc slot-kw (sym/qualify-sym shelf slot))
(save! item shelf slot-kw mk-ex-msg-fn prep)
item)

(defn safe-to-load?
[item shelf slot-kw & [force]]
Expand Down
14 changes: 11 additions & 3 deletions src/sayid/workspace.clj
Original file line number Diff line number Diff line change
Expand Up @@ -147,20 +147,24 @@
:children kids
:arg-map forced-arg-map))))

;; Shelve a fully-dereferenced snapshot (like a recording), so the stored copy is
;; independent of the live workspace's mutating child atoms.
(defn save!
[ws ws-shelf]
(shelf/save! ws
ws-shelf
:ws-slot
#(format "Workspace must have a symbol value in :ws-slot. Value was `%s`. Try `save-as!` instead." %)))
#(format "Workspace must have a symbol value in :ws-slot. Value was `%s`. Try `save-as!` instead." %)
deep-deref!))

(defn save-as!
[ws ws-shelf slot]
(shelf/save-as! ws
ws-shelf
:ws-slot
slot
#(format "Workspace must have a symbol value in :ws-slot. Value was `%s`. Try `save-as!` instead." %)))
#(format "Workspace must have a symbol value in :ws-slot. Value was `%s`. Try `save-as!` instead." %)
deep-deref!))

;; TODO remove traces before unloading a ws???
(defn load!
Expand All @@ -170,4 +174,8 @@
:ws-slot
slot
"Current workspace is not saved. Use :f as last arg to force, or else `save!` first."
force))
force)
;; The shelved snapshot is fully realized (its children are plain vectors);
;; re-atomize the root so the restored workspace can record new calls again.
(swap! ws update :children #(if (instance? clojure.lang.Atom %) % (atom (vec %))))
@ws)
9 changes: 9 additions & 0 deletions test/sayid/workspace_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,15 @@
:ws-slot '$ws/test1
:arg-map nil}))))

(t/deftest save-is-an-independent-snapshot
(t/testing "mutating the workspace after saving doesn't corrupt the shelved copy"
(ws/save-as! *ws* *shelf* 'test1)
;; append to the live workspace's call log after the save
(swap! (:children @*ws*) conj {:id :later :children (atom [])})
(ws/load! *ws* *shelf* 'test1 :f)
(t/is (= [] (:children (ws/deep-deref! *ws*)))
"the reloaded snapshot still has the empty log it was saved with")))

(t/deftest load-with-value
(t/testing "load with value"
(ws/save-as! *ws* *shelf* 'test1)
Expand Down
Loading