Use cases
End-to-end examples showing how scopes, tags, and org sharing come together for real development workflows.
Autonomous workflow self-improvement
The autonomous-workflow skill (part of mthines/agent-skills) defines a multi-phase agent dispatcher. The aw executor reads memories at Phase 1 (planning) and writes new ones at Phase 4 (stuck-loop) and Phase 7 (end-of-run). Universal memories go to global; repo-bound memories go to repo::{owner}/{repo}.
Read memories before planning
Narrow-to-broad fan-out — more specific wins.
// Phase 1 — read narrow first, then global
memory.list {
scope: "repo::mthines/lorekit",
tags: ["loop::aw-lessons"],
limit: 50
}
memory.list {
scope: "global",
tags: ["loop::aw-lessons"],
limit: 50
}
Record a stuck-loop memory
Write when the agent is stuck for the third iteration on the same area.
memory.write {
scope: "repo::mthines/lorekit",
key: "aw-lessons::supabase-rls-debugging",
value: "RLS failures return 200 with an empty array, not a 4xx. Always\ncheck .data.length before concluding the query returned no rows.",
tags: ["loop::aw-lessons", "skill::aw", "source::stuck-loop"],
source_agent: "aw-executor",
trigger: "stuck-loop"
}
The memory is repo-scoped so it only surfaces for this codebase, not globally.
CI / GitHub Actions context injection
Inject global and repo-scoped memories into any CI step so AI-assisted jobs have the same context as local agents. Generate a read-only token in Settings → API keys and store it as LOREKIT_TOKEN in your repo secrets.
Inject memories before an AI step
Use a read-only token (lk_ro_…) stored as LOREKIT_TOKEN.
- name: Inject LoreKit context
run: |
curl -s -X POST "$LOREKIT_MCP_URL" \
-H "Authorization: Bearer $LOREKIT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc":"2.0","id":1,"method":"tools/call",
"params":{
"name":"memory.list",
"arguments":{
"scope":"repo::${{ github.repository }}",
"tags":["loop::aw-lessons"],
"limit":20
}
}
}'
env:
LOREKIT_MCP_URL: https://…/functions/v1/mcp
LOREKIT_TOKEN: ${{ secrets.LOREKIT_TOKEN }}
CI state records (programmatic, non-LLM memories)
A CI job can use LoreKit as memory of its own, without an agent involved: it writes a small JSON record of what was true at the end of a run, and reads it back on the next one. Flaky tests, a benchmark baseline, the last deployed SHA, what a bot already commented on.
These are a different shape from lessons — machine-written JSON that a script parses, not prose an agent weighs. Keep them in their own bucket: tag ci::<job>-state, key ci-state::<slug>, and kind: "bus" / host: "ci" so lorekit list --kind bus --host ci shows every state record and nothing else.
One key per fact, overwritten in place
Same scope and key is an update, so a job that runs a thousand times still holds one row. Never key on the run id — that grows the store without bound and crowds out the lessons an agent reads at session start.
# Read the known-flaky set. `show` exits 1 on a miss, which is just the first run.
if npx @lorekit/cli show \
--scope "repo::${GITHUB_REPOSITORY}" --key 'ci-state::flaky-tests' \
--remote --json > state.json 2>&1; then
cat state.json
KNOWN=$(jq -c '(.remote.record.value // "{}") | fromjson
| if .v == 1 then .data.flaky else [] end' state.json)
else
cat state.json
echo "No prior state — treating every failure as new."
KNOWN='[]'
fi
Address the record with --scope and --key flags, not the single-token scope::key form — the key itself contains ::.
Write it back, versioned
jq -nc --argjson flaky "$FLAKY" \
--arg run "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" \
'{v: 1, updated_by_run: $run, data: {flaky: $flaky}}' \
| npx @lorekit/cli write \
--scope "repo::${GITHUB_REPOSITORY}" --key 'ci-state::flaky-tests' \
--tags 'ci::test-state' --kind bus --host ci \
--ttl-days 7 --remote --json \
| tee lorekit-write.log \
|| echo "LoreKit write failed (exit $?) — not failing the build."
Four things worth copying from this snippet:
- Keep the TTL short — about a week — and always pass it.
expires_atis recomputed asnow() + ttl_dayson every write, so a record the job rewrites each run never expires while the job keeps running. The TTL measures how long the job has been silent, which is exactly when its contents stopped being true: 7 days for anything running at least daily, 14 for nightly, 30 for weekly. It also caps the damage if someone keys on the run id by mistake — the store drains instead of climbing to the memory cap. Reserveclear_ttlfor a record whose absence is worse than its staleness. Never omit both, or the write silently inherits whatever default the repo configured for lessons. - A write-only
lk_wo_…token inLOREKIT_TOKENis enough for a job that only records. It cannot read anything back, so a leaked CI token cannot exfiltrate the team's lore. Uselk_ro_…for a read-only job. vis a required version stamp. A reader that sees an unrecognised version falls back to its first-run path instead of mis-parsing.- Never on the critical path. A LoreKit outage falls back to the first-run path and logs it; it never turns a green build red.
Never build the payload from an environment dump or a raw error body — a CI environment is full of tokens. Assemble it from an explicit list of fields. The value cap is 64 KiB, so reports, logs, and traces belong in artifacts, not here.
Why not just actions/cache? For pure job-to-job caching, use actions/cache — it is free and built for it. LoreKit earns its place when the agent reads the same record: CI records the flaky set on main, and an agent asked to fix those tests picks it up at session start from the same repo scope, with no extra wiring. The record links back to the workflow run that wrote it, because the CLI derives repo, branch, commit, and PR from the GitHub Actions environment automatically.
Team playbook with org sharing
Store deployment checklists, incident runbooks, and coding standards in an org so every team member's agent gets the same context automatically. Set up your org in Settings → Organization.
Write a shared team runbook
An admin writes; every member's agent reads it during planning.
// Write once by a member/admin
memory.write {
scope: "global",
key: "team::incident-runbook",
value: "On-call: 1. Check Dash0. 2. Ping #incidents. 3. Write a post-mortem within 48h.",
org: "my-team",
tags: ["team::runbook", "source::manual"]
}
// Every agent in the org reads it
memory.list {
scope: "global",
tags: ["team::runbook"]
}
// → includes the org-owned entry for every member
Bind a repo scope to the org
After binding, member agents auto-route writes without passing org every time.
// Admin binds the scope from Settings → Organization → Shared scopes
// scope: "repo::myteam/api" → org: "my-team"
// Now any member write under this scope auto-routes to the org
memory.write {
scope: "repo::myteam/api",
key: "deploy-checklist",
value: "Always smoke-test staging before promoting."
// no "org" needed — binding routes it automatically
}
Branch-scoped experimentation
Keep experimental memories on a feature branch so they don't pollute the repo set. Browse them in the Explorer with a branch scope filter, then promote to repo scope after the branch merges.
Write to a branch scope
Memory only surfaces when the agent is on this branch.
memory.write {
scope: "branch::mthines/lorekit::feat/new-cache",
key: "cache-invalidation-strategy",
value: "Use write-through for the session store; write-behind for lesson aggregates.",
tags: ["wip"]
}
Promote to repo scope after merging
Once proven, promote the memory so it persists beyond the branch.
// After merge: write the same key at repo scope, delete the branch copy
memory.write {
scope: "repo::mthines/lorekit",
key: "cache-invalidation-strategy",
value: "Use write-through for session store; write-behind for lesson aggregates."
}
memory.delete {
scope: "branch::mthines/lorekit::feat/new-cache",
key: "cache-invalidation-strategy"
}
Transient memories with auto-expiry
Not every memory should live forever. Pass ttl_days to memory.write and the entry automatically becomes invisible once the TTL elapses — no manual cleanup required. This is ideal for session-scoped signals: issues already triaged, PR reviews in progress, or any fact that is only relevant for a few days.
Flag a triaged issue (expires in 7 days)
The entry disappears automatically, so the agent won't revisit it next session.
memory.write {
scope: "repo::mthines/lorekit",
key: "triage::ENG-123",
value: "Already triaged — assigned to backend team, no action needed.",
ttl_days: 7
}
// → response includes expires_at so you can confirm the deadline
// { id: "…", created_at: "…", expires_at: "2026-08-04T…" }
On an update, omitting ttl_days leaves the existing expiry unchanged. Pass a new ttl_days to refresh the countdown.
Renew a TTL on the next encounter
Update the value without resetting the expiry, or refresh both at once.
// Update only the value — expiry stays where it was
memory.write {
scope: "repo::mthines/lorekit",
key: "triage::ENG-123",
value: "Triaged — backend confirmed fix ships Friday."
}
// Extend the countdown by supplying a new ttl_days
memory.write {
scope: "repo::mthines/lorekit",
key: "triage::ENG-123",
value: "Triaged — backend confirmed fix ships Friday.",
ttl_days: 3
}
See what is about to expire, before it does
An expiry you never look at is a deletion you did not decide on. GET /memories takes
expiring_within_days (1–365) to list exactly the memories whose TTL runs out inside that horizon —
still live, but not for long — so you can review them and either let them lapse or extend them.
# What lapses in the next 7 days?
curl -H "Authorization: Bearer lk_ro_…" \
"https://pqokxlhvnosogizsjztg.supabase.co/functions/v1/memories?expiring_within_days=7"
Memories with no TTL never appear here, and neither do ones that already expired — this is the
"act now" list, not an audit of everything with a deadline. Refresh anything worth keeping with a
new ttl_days, or clear_ttl: true to make it permanent.
Clean up expired entries explicitly
Expired rows are invisible to reads immediately. Call memory.purge_expired to reclaim storage.
// Expired rows are hidden from all reads once expires_at passes.
// Call memory.purge_expired to physically remove them and reclaim storage:
memory.purge_expired {}
// → { purged: 4 }
The CLI npx @lorekit/cli list always skips expired entries — you'll never see stale data in read results.
The CLI npx @lorekit/cli tree command shows the full scope precedence hierarchy — which memory wins per key, and which are shadowed — so you can audit exactly what an agent will see before a task. You can also browse memories visually in the Explorer.