Reference·Eval and the layerIndexClose
Eval and the layer
The eval tool, its helpers, and how a definition is kept, held, skipped, inspected and undone.
Arguments
eval evaluates Common Lisp forms in the running image.
| Argument | Type | Does |
|---|---|---|
form | string, required | One or more forms, evaluated in order. |
yield_time_ms | integer | Milliseconds to wait before the form continues in the background. Default 10000. Maximum 600000. |
strings | object of strings | Named literal texts. Read one inside the form with ($ "name"). |
A multi-line cell may leave parens open. Its indentation closes them. A one-line cell must close its own.
Use strings for text with backslashes, double quotes or newlines. In a Lisp string, "\n" reads as n.
(edit "src/app.py" ($ "old") ($ "new"))
Helpers
Helpers are functions inside eval. Calling one as its own tool is refused with a repair hint.
| Form | Does |
|---|---|
(sh "cmd") | Runs the command through sh -c in the session's folder. Returns stdout and stderr merged. Adds [exit N] when the status is not 0. |
(sh "cmd" :directory "dir") | Runs it in dir. |
(sh (list "git" "status")) | Runs an argv list with no shell. |
(edit path old new) | Replaces the one occurrence of old in the file. Zero or several occurrences is an error. |
(write-file path text) | Writes the whole file. Creates parent folders. |
(patch name old new) | Changes one subform of a kept definition. Evaluates the result. Keeps it. Write name, old and new unquoted. |
(look path) | Attaches an image file to the turn. The model sees it at the next round. |
($ "name") | Reads one entry of strings, exact. |
(help) | Lists the functions and topics. (help :eval) and (help :layer) have the detail. |
| Limit | Value |
|---|---|
sh stdin | Empty. |
sh output capture | 1,000,000 characters. Past that the command is killed. |
look formats | PNG, JPEG, GIF, WebP |
look file size | 5 MiB |
| Relative paths | Resolved against the session's folder. |
On Windows, sh uses the sh.exe from Git for Windows.
Result
| Rule | Detail |
|---|---|
| Cap | 32,000 characters per result. |
| Values | One => line per form. Only the last value prints in full. |
| Failure | The result names the failing form: ERROR in form i of n. Earlier forms stand. |
| Compiler warnings | A [warning: ...] line leads the result. The form still ran. |
| Note | A cell's leading ;; line is its note. Eviction stubs keep it. |
In the next cell, * is the last value of the previous cell. / is the list of its values.
Linux only: a cell's last form is watched when it only reads. It runs again after each later cell. A change shows as [watched FORM: OLD -> NEW].
Background evals
A form still running after yield_time_ms continues in the background. It is never killed by the timeout.
The call answers:
{"status":"running","eval_id":N}
The harness reads the exit for the model. It does so in the same turn, or in a new turn that wakes the session.
| Form | Does |
|---|---|
(list-evals) | Lists this image's background evals. |
(eval-status N) | Returns the output since the last look. Adds the value and exit code once exited. |
(eval-await N) | Waits for new output or the exit. Waits at most the calling cell's yield_time_ms. |
(eval-await N :until-exit t) | Waits for the exit only. |
(eval-interrupt N) | Aborts the eval. |
(eval-interrupt N :force t) | Terminates its thread. Use when the form traps the abort. |
Background output is live only. The exit result is stored in the log.
Kept definitions
The layer is the set of definitions the agent wrote and kept.
A defining form that evaluates successfully as a top-level form is kept. No confirmation is asked.
| Kind | Kept operators |
|---|---|
| Functions | defun, defmacro, defgeneric, defmethod, defsetf |
| Variables | defvar, defparameter, defconstant |
| Types | defclass, defstruct, deftype, defpackage |
| Other | define, and any define-* form such as define-hook |
A definition inside a top-level progn or eval-when is kept as its own file.
| Fact | Value |
|---|---|
| Folder | ~/.nodecode/cells/<name>/ |
<name> | git config user.name, else $USER, else you. Lowercased. Other characters become -. |
| Files | One .lisp file per definition, plus a generated .asd. |
| Git | One commit per write. The message names the definitions and the session id. |
| Log | One organism.definition_recorded event. |
| Result line | [layer: kept NAME] |
| Load | From source at every boot, in every process, after all other cells. |
| Compile cache | ~/.cache/nodecode/layers/<image-stamp>/ |
Not kept:
| Form | Do this |
|---|---|
setf | Write a defparameter. |
| A defining form built as a string and evaluated inside another form | Evaluate the defining form itself. |
| A definition of a kernel name | See the kernel lock below. |
Hooks
| Form | Does | Kept |
|---|---|---|
(define-hook name "key" (next &rest args) ...) | Runs the body around every call of name. | Yes |
(hook 'name "key" fn) | The same advice, live only. | No |
(unhook 'name "key") | Removes the hook. Deletes its layer file. |
A hook survives an upstream change to the function's body. A redefinition of a shipped function may be skipped at boot after an update.
Kernel lock
The kernel is the package nodecode.kernel, nickname nlk. It is locked at boot. The boot records an organism.floor_engaged event.
| Action | Result |
|---|---|
| Redefine a kernel name while locked | Error. |
| Define a kernel name in a cell | Never written to the layer. The result says [layer: NAME is floor material; ...]. |
Advise a kernel function with define-hook | Allowed. Kept. |
The agent can lower the lock itself:
(lower-floor :session-id "s-..." :requested-by "who asked" :reason "why")
All three arguments are required. An organism.floor_lowered event is recorded before the unlock.
Held definitions
A redefinition is held when its name is running on the stack of the calling cell. The turn loop, agent-loop, always is.
- The new definition goes live at once.
- It is written to the layer after it has been entered and has returned once.
- If that first run signals an error, the old definition is restored.
- The session is woken with the error.
A cancelled turn does not count as a failed first run.
| Form | Does |
|---|---|
(held-definitions) | Lists what waits for a first run. |
(revert 'name) | Puts back the displaced definition, or the one this image booted with. |
revert changes the image only. It leaves the layer file as it is.
Skipped files
At boot a layer file is skipped in two cases:
| Case | Example |
|---|---|
| The shipped text it overrides has changed or is gone | An update rewrote the function. |
| It names a function or variable that nothing defines | An update removed a helper. |
The shipped definition stands. The boot line and (cells) name the file and the reason. Other files still load.
The gateway then submits the skipped files to a session named layer. The agent redefines or forgets each one. Each skipped file is handed once. This turn spends tokens.
Source edits
edit or write-file on a file this image loads from evaluates the changed definitions at once. This covers the shipped source, cell folders and the layer.
| Receipt line | Meaning |
|---|---|
live here: ... | Already running in this process. |
not live until a relaunch: ... | Needs a relaunch: a struct, a class, a package, a constant. |
Inspect and undo
| Form or command | Does |
|---|---|
(cells) or /cells | Reports each folder: definitions, overrides, collisions, skipped files, failures. |
(definition-text 'name) | Reads the live definition. |
(definition-location 'name) | Names where it lives: the layer file and the shipped file. |
(held-definitions) | Lists held definitions. |
(revert 'name) | Restores the previous function in the image. |
Forget a definition with a top-level form. The name leaves the image. Its file is deleted. The commit reads forgot NAME.
(fmakunbound 'name)
(makunbound '*name*)
(unhook 'name "key")
(remove-method ...)
(unintern 'name)
A forgetting form computed inside another form is not seen.
Undo a whole change:
cd ~/.nodecode/cells/<name>
git revert <commit>
Then restart Nodecode.