Lab, gardener & usage
Research trials, the propose-only gardener, the usage summary, and favorites - the nine tools for keeping the store honest.
The research lab
A lab is a shared workspace for a systematic investigation: a hypothesis, a
series of trials, and what each one actually did. lab_open binds a lab to the
connection, so trial_record inherits it the way memory inherits a session's
project. trial_query reads the trials back.
The point is parallel agents collaborating on one investigation. Several agents chasing the same bug can open the same lab, record what they tried, and see each other's dead ends instead of re-walking them. When the investigation resolves, distill the finding into a memory - the lab is the working record, not the conclusion.
The gardener proposes; it never acts
The gardener runs periodically over the store looking for drift. Every pass it
makes ends in a proposal row for you to review - it never rewrites your
knowledge behind your back. That is the whole contract, and it is why the tool
surface has both gardener_proposals (read them) and gardener_apply (act on
one) instead of a single "tidy up" button.
Its passes:
| Pass | What it looks for |
|---|---|
| dedup | Two memories that say the same thing, above gardener.dedup_threshold similarity. Proposes a merge: one is kept, the other superseded and pointed at it. |
| staleness | Memories untouched for gardener.staleness_days. Proposes an archive: marked invalid, still readable. |
| stale-stage | A stage memory whose gate is done, missing, or unparseable, unchanged for gardener.stale_stage_days. Proposes an archive. |
| dead-weight | A memory briefings keep injecting with zero recall hits, prompt matches, or reads. Proposes an archive. |
| digest | Enough recent activity to be worth a summary over gardener.digest_days. Proposes a digest note. |
| stale-plan | Plans idle for gardener.stale_plan_days with steps still open. |
| memory-wanted | The same recall query missing across sessions. Proposes a memory_wanted: applying opens a task to write the knowledge agents keep searching for. |
| tool-error | The same normalized tool or hook error recurring (tool errors across 2+ sessions). Proposes a tool_error: applying opens an investigation task with the observed evidence. |
Constraints and pinned stages are never age-filtered or staleness-archived. A constraint does not become less true by sitting still.
gardener_request takes a request in natural language ("fold the two console
theme memories together") and turns it into the same reviewable proposals. It can
also reproject a memory filed under the wrong project - moving it to a
different project that already exists.
Its project argument scopes the memories it may reference: a project slug (that
project plus globals), global for globals only, or all for every project on
the machine. Omit it and the scope resolves the way every other read does - from
the session - rather than quietly widening to everything. A slug that is not a
project is an error, not an empty result.
Splits are a different thing
Moving a memory into a project that does not exist yet is not a reproject - it is
a split, and gardener_split plans it. Splitting one project into new children
creates those projects and a shared parent, so it is planned as a unit rather
than as a pile of individual moves.
usage_summary
usage_summary reports what the store actually contains and what retrieval has
been doing: memory counts, retrieval statistics including the highest-utility
memories by decayed demand, events by kind. It answers "is
this thing working, and on how much?" - the same question the console's Overview
answers, for an agent that cannot open a browser.
favorite_set
favorite_set stars or unstars an item - a memory, note, project, plan, task,
session, or trial. A starred memory is pinned into every session briefing as a
FAVORITE: line and gets a mild recall rank boost; in the console, favorites
carry a star, sort first, and can be filtered. For memories and notes the flag
lives in the file's frontmatter (favorite: true), so it survives an index
rebuild and can be hand-edited. A plan's favorite lives on its primary note, so
a task-only plan (no note) cannot be starred. Starring never bumps an item's
updated time - it is metadata, not authorship.
Applying is an explicit boundary
gardener_apply accepts only apply or dismiss. Dismissal closes the proposal
without changing its target. Apply performs the proposal's typed action and
returns that real outcome; it never substitutes a plausible dummy when the
target disappeared or a file/store mutation failed. Memory-changing actions
route through the lifecycle/files services, so supersession, atomic Markdown
writes, and occupied-path protections remain the same as direct tools.
lab_open
Open a research lab and get its recent trial history for context. Binds the lab to this connection so later trial_record calls inherit it. A lab is just a label; opening a new one creates it implicitly on first trial_record.
| Parameter | Type | Required | Description |
|---|---|---|---|
lab |
string | yes | lab name (a stable label for a line of investigation) |
goal |
string | no | optional note on what this lab is investigating |
trial_record
Record one experiment in a research lab: what changed, expected vs actual, outcome, and optional structured metrics for later querying. Inherits the lab from lab_open unless you pass one.
| Parameter | Type | Required | Description |
|---|---|---|---|
title |
string | yes | short trial title |
actual |
string | no | observed result |
changes |
string | no | what was changed for this trial |
expected |
string | no | expected result |
lab |
string | no | lab name; defaults to the lab opened on this connection |
metrics |
object | no | optional object of structured metrics, e.g. {"hz":497,"err_pct":0.2} (a JSON-object string is also accepted) |
outcome |
string | no | pass|fail|partial|inconclusive (free-form) |
project |
string | no | project slug; defaults to the bound/ambient session's project. An unknown slug CREATES that project -- naming a new one is normal and never an error. Pass project=global ONLY for knowledge that belongs in EVERY project's briefing; it is not a neutral default. With no session and no explicit project the call is rejected as ambiguous. |
trial_query
Query recorded trials, filtered by lab, outcome, and/or an exact-match metrics filter (native structured query over the metrics recorded by trial_record).
| Parameter | Type | Required | Description |
|---|---|---|---|
lab |
string | no | lab name; defaults to the lab opened on this connection |
limit |
number | no | max results (default 20) |
metrics_filter |
object | no | optional object; trials whose metrics equal every given key match, e.g. {"hz":497} (a JSON-object string is also accepted) |
outcome |
string | no | filter by outcome (e.g. fail) |
gardener_proposals
List pending gardener proposals (merge/consolidate duplicate memories, archive stale memories, write a monthly session digest, reproject a memory to another project, set up a project split, abandon a never-approved captured plan, write a memory agents keep searching for in vain, or fix an error agents keep hitting). Review, then apply or dismiss each with gardener_apply. Read-only.
| Parameter | Type | Required | Description |
|---|---|---|---|
kind |
string | no | filter by proposal kind (default: all pending). One of: merge, archive, digest, consolidate, reproject, split, abandon_plan, memory_wanted, tool_error. |
gardener_request
The natural-language entry point for REORGANIZING memory. Describe the change in plain language and it returns reviewable pending proposals -- fold duplicates together ("these two memories are duplicates -- keep the newer"), retire stale memories ("archive anything about the old port 8080"), synthesize several into one ("combine the three auth-flow notes"), or move a mis-filed memory to another EXISTING project ("the iOS DFU memory belongs in arctop-ios"). Use this whenever the user describes how they want their knowledge organized; if the intended change is ambiguous, ask them a clarifying question first. It NEVER mutates memories: it only creates pending proposals -- review with gardener_proposals, resolve with gardener_apply. If the request is to split one project into NEW child projects, it recognizes that and returns guidance (splitSource) pointing you at gardener_split instead. Needs an LLM chat client.
| Parameter | Type | Required | Description |
|---|---|---|---|
request |
string | yes | the reorganization request in plain language |
project |
string | no | scope candidate memories: a project slug (its memories + globals), "global" for globals only, or "all" for every project on the machine. Omit to use the session's project. |
gardener_split
Plan a project SPLIT: divide one existing project into two or more NEW child projects, keeping cross-platform memories in a shared parent (e.g. split arctop-app into arctop-ios + arctop-android with shared arctop-mobile-apps). Use this when the user wants to break one project into several -- gardener_request points you here (via splitSource) when it detects that intent. It NEVER creates a project or moves a memory: it only creates reviewable pending proposals -- one 'split' setup proposal plus one 'reproject' per memory, all under plan 'split-<source>'. Review with gardener_proposals, then apply each with gardener_apply (or retarget a memory first in the console). Needs an LLM chat client and a known source project slug (see project_list).
| Parameter | Type | Required | Description |
|---|---|---|---|
source |
string | yes | the project slug to split (its own memories are classified into the children/shared parent) |
instruction |
string | no | optional guidance: which children, what stays shared |
gardener_apply
Resolve a gardener proposal. action=apply carries out the effect (archive -> retire the memory; merge -> supersede the older by the newer; consolidate -> write a unified memory superseding its sources; digest -> save the summary as a note; reproject -> move the memory to another project; split -> create the child/shared projects, link the family, parent the children, retire the source; memory_wanted -> open a task to write the missing memory; tool_error -> open a task to fix the recurring error); action=dismiss discards it. A dismissed proposal is never re-raised.
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
string | yes | proposal id (ULID) |
action |
string | no | apply (default) or dismiss. One of: apply, dismiss. |
usage_summary
Report a roll-up of activity: memory/note/session/task counts, retrieval totals with the most-injected memories, pending gardener proposals, and events by kind. Read-only.
Takes no parameters.
favorite_set
Star or unstar an item. Favorites sort first in the console, are pinned into session briefings (memories), and get a mild recall rank boost. For memories and notes the flag is stored in the file's frontmatter; starring never bumps an item's updated time.
| Parameter | Type | Required | Description |
|---|---|---|---|
kind |
string | yes | what kind of item to star. One of: memory, note, project, plan, task, session, trial. |
id |
string | yes | the item's identifier: memory name, note id (or slug), project slug, plan slug, task id, session id (or name), trial id |
favorite |
boolean | yes | true to star, false to unstar |
project |
string | no | project scope for memory-name/note-slug resolution; defaults to the bound session's project |