diff --git a/content/posts/cold-start-tax-agentic-memory.md b/content/posts/cold-start-tax-agentic-memory.md index c6c42bb..2387976 100644 --- a/content/posts/cold-start-tax-agentic-memory.md +++ b/content/posts/cold-start-tax-agentic-memory.md @@ -1,165 +1,130 @@ +++ -title="Stop Paying the Cold-Start Tax: Agentic Memory for Teams Running 42 Agents Nightly" +title="A project lesson your coding agent can find next time" date=2026-07-01 +updated=2026-10-13 +description="Save an actual project instruction, recover it in a new process, and retrieve it through a small knowledge graph. Includes a narrated demo and reproducible example." [taxonomies] categories = ["Technical"] -tags = ["Terraphim", "ai", "agentic-engineering", "memory", "adf", "knowledge-graph", "developer-tools"] +tags = ["Terraphim", "ai", "agentic-engineering", "memory", "knowledge-graph", "developer-tools"] [extra] toc = true comments = true +++ -We run forty-two AI agents in our Dark Factory every night. Every one of them used to wake up with amnesia, rediscovering facts we already knew, paying the same token bill twice. Here is what we built to stop paying it -- and the eight-stage frame, named by memco.ai, that finally made the story legible. As of July 2026, the whole pipeline ships as a single `terraphim-agent memory` CLI namespace. +You explain the project's validation command. The session ends. Next time, the agent needs the same explanation. Keeping the instruction somewhere is useful only if the next run can find the right instruction again. -> Builds on [Teaching AI Agents to Learn from Their Mistakes](/posts/teaching-ai-agents-to-learn-from-mistakes/) and [Knowledge Graph Hooks](/posts/teaching-ai-agents-with-knowledge-graphs/). Maps both into a single lifecycle. +This walkthrough saves one lesson from a fictional project, opens it in a new process, and retrieves it through a small knowledge graph. You can inspect the exact saved text and repeat the same checks with the downloadable example. -## The cold-start tax is a line item, not a metaphor + -Last Tuesday an ADF agent spent fourteen thousand tokens working out that our project uses `bun`, not `npm`. We had captured that correction three months ago. The correction was sitting in `~/.config/terraphim/`, two directories away from where the agent was looking, in a file the agent did not know to read. +[Download the Harbour example](/demos/agent-memory/harbour-memory-example.zip) · [Read the transcript](/demos/agent-memory/transcript.txt) -memco.ai calls this the **cold-start tax**: every agent run begins from zero, paying full token cost for facts the team already owns. They name it well. "Context is rented. Memory is owned." Read their [field guide](https://www.memco.ai/field-guide); it is short and it is right. +## Start with an instruction worth keeping -The tax compounds. With forty-two agents running through the night against the same repository, the same rediscovery happens forty-two times. Most teams cope by buying bigger context windows. We tried that. Bigger windows do not pay the tax; they just stretch the receipt. +Harbour is a small public project brief. It records a decision, a risk and an owner. Before publication, its validator checks that all three fields are present. The instruction we want the next session to remember is deliberately specific: -## The second run is the signal - -The number that actually matters is the delta between run one and run two of the same task. If memory works, run two costs less. If memory fails, run two costs the same as run one and your "AI strategy" is a slot machine with extra steps. - -Here are five tasks from our last seven days of ADF activity, retried within twenty-four hours after a hook-captured correction landed: - -| Gitea issue | Run 1 input tokens | Run 2 input tokens | Delta | -|---|---|---|---| -| #1873 | 47,200 | 31,800 | -32.6% | -| #1862 | 28,400 | 19,100 | -32.7% | -| #1879 | 81,000 | 63,500 | -21.6% | -| #1858 | 33,900 | 28,200 | -16.8% | -| #1850 | 52,100 | 47,400 | -9.0% | - -Median: 21.6%. Best: 32.7%. Worst: 9%. The 9% case is a fair tell -- that issue's correction sat in a project KG the second-run agent did not consult. We patched the role scope; next attempt should join the others. - -The new `second-run` subcommand reads ADF artefacts and computes this delta automatically: - -```bash -$ terraphim-agent memory second-run --issue 1899 -{ - "gitea_issue": 1899, - "runs_compared": 4, - "delta": { - "tokens_saved": 8400, - "retries_avoided": 1, - "wall_time_delta_seconds": -45.2, - "interpretation": "improved (fewer tokens in later run)" - } -} +```text +Validate the Harbour project brief with python3 validate-brief.py --check. +Keep the source decisions in brief.md intact. ``` -We had this data and we were not looking at it. That is the marketing failure underneath the engineering one. +That gives a future agent an action and a boundary. “Remember to be careful” would be much harder to use. The example also includes the brief and validator, so the instruction has something concrete to refer to. -## Eight stages, all green: the lifecycle in our stack +The first step is to save this actual text. Download and extract the example, then install [terraphim-agent v1.21.20](https://github.com/terraphim/terraphim-clients/releases/tag/v1.21.20). From the example folder, prepare a new, isolated run before entering the manual commands below: -memco names eight stages of an agentic memory lifecycle. We checked our stack against it. All eight are now wired behind a single CLI namespace. +```sh +python3 reproduce.py --agent /path/to/terraphim-agent \ + --output ./harbour-run --prepare-only +cd harbour-run/fixture +export TERRAPHIM_DEFAULT_DATA_PATH="$(cd .. && pwd)/data" +export TERRAPHIM_SETTINGS_PATH="$(cd .. && pwd)/settings" +``` -| Stage | What memco names | CLI command | Status | -|---|---|---|---| -| 1. Capture | Record the lesson | `terraphim-agent memory capture --provenance-tag ` | Shipped | -| 2. Distill | Synthesise into a shape worth keeping | `memory distill` | Shipped | -| 3. Scope | Attribute the right role / project | `memory scope --role --check` | Shipped | -| 4. Provenance | Trace it back to where it came from | `memory provenance` | Shipped | -| 5. Retrieve | Find it again when it matters | `memory retrieve ` | Shipped | -| 6. Apply | Inject it into the next run | `memory apply --prompt ` | Shipped | -| 7. Validate | Check the lesson is still true | `memory validate` | Shipped | -| 8. Retire | Demote the lessons that have aged out | `memory retire --lesson-id ` | Shipped | -| -- | Diagnostic | Full reliability readout | `memory rubric --project ` | Shipped | -| -- | Inspection | Browse the evolution store | `memory list`, `memory show`, `memory export` | Shipped | +Use your actual binary path in the first command and put that same verified binary on your PATH for the remaining commands. Preparation creates the role configuration, settings and `lesson.md`; it has not captured anything yet. To run all checks automatically instead, omit `--prepare-only` and choose a fresh output folder. -The implementation lives in [terraphim-clients PR #61](https://git.terraphim.cloud/terraphim/terraphim-clients/pulls/61). Thirteen subcommands, ~1200 lines of Rust, built on `terraphim_agent_evolution` from the terraphim registry. No new crates. No new schemas. Pure consolidation. +```sh +terraphim-agent --format json memory capture \ + --file lesson.md \ + --provenance-tag harbour-public-brief-v1 +``` -## The Memory Reliability Rubric: a ten-minute readout +Capture returns a memory identifier. We use that identifier to inspect the saved item, rather than treating a successful exit code as the whole result. The saved content is the lesson itself, and the provenance tag identifies which public example supplied it. -The rubric subcommand scores every captured memory on six dimensions and produces a markdown diagnostic in under a second: +`--content` accepts a short instruction directly. `--file` is convenient for a multiline note. Both preserve the supplied text. Missing, blank, unreadable or conflicting inputs fail without creating a substitute placeholder. -1. **Faithfulness** -- does the memory accurately describe what happened? -2. **Scope** -- is the role and project boundary correct? -3. **Provenance** -- can we trace this memory to a session, commit, or hook event? -4. **Actionability** -- does the memory tell the next agent what to do, or only what happened? -5. **Decay** -- is the memory still current, or has the codebase moved past it? -6. **Risk** -- does applying this memory introduce a new failure mode (brittle text substitution, false confidence)? +## End the process, then open the memory -Example output against a project with two captured items: +Each command in the demonstration starts a new process. After capture exits, `memory show` opens the saved identifier. `memory list` finds the item, and `memory export` includes the same instruction in JSON. +```sh +terraphim-agent --format json memory show MEMORY_ID +terraphim-agent --format json memory list +terraphim-agent memory export --format json --output memories.json ``` -$ terraphim-agent memory rubric --project ~/project -# Memory Reliability Rubric Report +Replace `MEMORY_ID` with the identifier returned by capture. The reproduction script does this automatically and checks that the reopened and exported text exactly matches `lesson.md`. + +![A new process displays the exact saved Harbour instruction and its provenance tag](/demos/agent-memory/screenshots/saved-lesson.jpg) -| Dimension | Score | Status | -|-------------|-------|---------------| -| Faithfulness | 0.65 | Adequate | -| Scope | 0.30 | Needs attention | -| Provenance | 0.90 | Good | -| Actionability | 0.40 | Adequate | -| Decay | 1.00 | Good | -| Risk | 0.40 | Needs attention | +The store has an explicit location. The example sets `TERRAPHIM_DEFAULT_DATA_PATH` to a new folder for that run; the evolution file lives beneath `terraphim/evolution/cli-agent.json`. A second empty folder supplies the independent-store check. This keeps the demonstration separate from your existing memory files. -Composite score: 0.59 / 1.00 +Without an explicit data root, the agent retains its usual platform configuration-directory location. Changing the root selects another store; it does not silently migrate or merge your previous one. -## Top 3 Items Needing Attention +## Teach the role the words used by the project -1. **ab12cd** (composite: 0.57) -- Memory item captured via CLI with provenance: test -2. **ef34gh** (composite: 0.60) -- Potential risk pattern detected in content +Knowing an identifier is useful for inspection. Day to day, you are more likely to ask a question such as “validate brief”. The **Brief Reviewer** role supplies the terminology for that question. -## Recommended Retirements +Its small graph has a **Brief validation** concept: -- **ef34gh**: contains high-risk command pattern (risk: 0.70) +```markdown +# Brief validation +synonyms:: validate brief, brief validation, --check ``` -## Public commons vs permissioned memory +A second concept describes the **Project brief**, including `brief.md`. Both definitions are ordinary Markdown in the download. The saved instruction mentions these concepts, connecting an action with the document it applies to. -Not every lesson belongs to the world. We split memory into two buckets, with a policy file at the repo root: +The `synonyms::` property makes the relationship visible: “validate brief” and `--check` are labels for the same concept in this project. You can review or change that relationship. Saving a memory does not invent new terminology or update every other role automatically. -**Public commons memory** lives in the [terraphim-skills](https://github.com/terraphim/terraphim-skills) repository and as KG terms published via Gitea wiki. Apache-2.0, no PII, no secrets. Anyone can clone, anyone can contribute. +This is the same explicit-knowledge principle used in the [editor-to-search example](/how-tos/synonym-search/): record the relationship, then use the configured graph to find relevant work. Here the searched material is an authored memory item. -**Permissioned memory** lives in per-project KGs, per-agent corrections, and session transcripts. It stays in `~/.config/terraphim/evolution/cli-agent.json` or per-repo `.terraphim/`. Never leaves the device without an explicit publication step. A scope check in the capture path warns when a permissioned item is about to land in a public location. +## Retrieve the instruction, then check the empty cases -This split is what makes the open-source story work without burning operational secrets. It is documented in `MEMORY_POLICY.md` at the terraphim-clients repo root. +With the example's role configuration loaded, this query retrieves the stored lesson: -## Try it +```sh +terraphim-agent --config ../config.json memory retrieve 'validate brief' \ + --role 'Brief Reviewer' --format json +``` -Install: +The result includes the original text, its memory identifier, the provenance tag and a matched concept. It is a retrieved instruction, not a generated paraphrase. The viewer can compare it directly with the file that was captured. -```bash -# Build from source (terraphim-clients repo) -git clone https://git.terraphim.cloud/terraphim/terraphim-clients.git -cd terraphim-clients -cargo build --release -p terraphim_agent +![The Brief Reviewer retrieves one saved lesson while unrelated-query, other-role and empty-store controls return zero](/demos/agent-memory/screenshots/retrieval-controls.jpg) -# Capture a memory item -terraphim-agent memory capture --provenance-tag "session-abc123" +The accompanying controls matter just as much. “Volcanic eruption” returns no memory. The **Finance Reviewer** role, which has budget and grant concepts, does not match the validation query. The same useful query against a separate empty store also returns nothing. -# Browse the store -terraphim-agent memory list +Those observations distinguish useful retrieval from a command that always prints the latest note. Roles shape which concepts are recognised. They are not an access-control system: use separate stores and ordinary filesystem permissions where separation is required. -# Run the reliability rubric -terraphim-agent memory rubric --project ./my-project +## What happens inside the CLI -# Check second-run acceleration -terraphim-agent memory second-run --issue 1234 +The agent stores the lesson using Terraphim's existing evolution types. During retrieval, the role's thesaurus matches terms, and the role graph ranks memory items by their concept relationships. There is no lexical fallback when a question matches none of the configured concepts. -# Export as JSON -terraphim-agent memory export --format json --output memory-dump.json -``` +This graph uses co-occurrence edges, so the example intentionally connects validation with the project brief. A single isolated concept is not enough to assume the same retrieval behavior. Keep the graph and the note inspectable, and test the questions people will actually ask. -The full `memory` namespace is shipping in [terraphim-clients PR #61](https://git.terraphim.cloud/terraphim/terraphim-clients/pulls/61). The feature request is [terraphim-ai#1899](https://git.terraphim.cloud/terraphim/terraphim-ai/issues/1899). Thirteen subcommands, one CLI surface, zero new crates. +`memory apply` can preview proposed context for a prompt. That preview does not execute a command or prove a coding host consumed it. The separate hooks walkthrough will follow the instruction into actual execution. Captured memories and reviewed learning corrections also use different stores; neither is an automatic copy of the other. -## What memco got right, and what we are adding +## Try the next run yourself -memco named the category and wrote the field guide. That is real work and we are crediting them for the vocabulary throughout this post. What we are adding is the engineering substrate: a working eight-stage pipeline in Rust with 13 CLI subcommands, a six-dimension rubric you can run on your own code, a second-run acceleration signal measured from production ADF data, and a forty-two-agent fleet that has been paying the tax in production for long enough to measure the relief. +The ZIP contains the public brief, validator, lesson, Markdown concepts, reproduction script and saved outputs. Follow its README to verify the released binary, create an isolated run and compare the positive and negative results. The script checks the saved content, rather than relying on expected screenshots. -The cold-start tax is real. The second-run signal is measurable. The lifecycle exists in a single CLI namespace today. Read the field guide. Then check your own stack against the eight stages. If you are missing more than two, you are paying tax you do not need to pay. +[Download and reproduce the memory example](/demos/agent-memory/harbour-memory-example.zip). You can also inspect the [saved outputs and verification manifest](/demos/agent-memory/manifest.json). ---- +The useful result here is modest and concrete: an instruction survives the process that captured it, and the intended project terminology can find it again. Measuring whether that saves tokens or improves later decisions needs a separate experiment. -*Discuss on [Twitter](https://twitter.com/terraphim_ai) or open an issue at [git.terraphim.cloud/terraphim/terraphim-ai](https://git.terraphim.cloud/terraphim/terraphim-ai). The Gitea feature request for the consolidated CLI is [#1899](https://git.terraphim.cloud/terraphim/terraphim-ai/issues/1899). PR: [terraphim-clients#61](https://git.terraphim.cloud/terraphim/terraphim-clients/pulls/61).* +*Updated 13 October 2026 with a reproducible walkthrough. The broader memory-lifecycle vocabulary was inspired by [memco.ai's field guide](https://www.memco.ai/field-guide). The video uses synthetic British female narration and readable views of actual CLI outputs. The Harbour fixture is public sample data.* diff --git a/publication/agent-memory-hooks/README.md b/publication/agent-memory-hooks/README.md new file mode 100644 index 0000000..dd0be9d --- /dev/null +++ b/publication/agent-memory-hooks/README.md @@ -0,0 +1,9 @@ +# Terraphim memory and hooks publication sources + +Two independent releases of reader-facing content: memory on 13 October 2026 and hooks on 14 October, both at 11:00 BST. LinkedIn copy remains draft-only. The existing editor/grep campaign is separate. + +The memory example uses publicly downloaded agent v1.21.20, verified archive signatures/checksums, and actual saved outputs. The downloadable ZIP contains its complete fixture, isolated runner, release identities, receipts and checksums. The final video is 77 seconds with natural-rate synthetic British female Emma narration. + +The hooks example is qualified separately on agent v1.21.19 and OpenCode2.0.20: real failure, explicit reviewed promotion, standalone hook output, then actual host execution through the included local adapter. It must retain that scope and its public-data-only redaction limitation. + +`campaign.json` is the operator ledger, when present. Preparation or a scheduled wakeup does not mean a page or post is live. Per-channel drafts and evidence stay separate from verified published receipts. diff --git a/publication/agent-memory-hooks/memory/package/README.md b/publication/agent-memory-hooks/memory/package/README.md new file mode 100644 index 0000000..909aeba --- /dev/null +++ b/publication/agent-memory-hooks/memory/package/README.md @@ -0,0 +1,79 @@ +# Harbour: save a lesson and find it next time + +This public example captures an actual instruction, recovers it in new processes, +and retrieves it through an explicit project knowledge graph. It uses +terraphim-agent **1.21.20**. Earlier 1.21.19 cannot capture supplied lesson text. + +## Requirements + +- Python 3.9 or later and `rg` (ripgrep) on PATH; the local Markdown KG loader uses it. +- The released `terraphim-agent` for your platform. Download it and SHA256SUMS from: + https://github.com/terraphim/terraphim-clients/releases/tag/v1.21.20 +- Verify the archive against the release's SHA256SUMS before extracting it. + `released-binaries.json` also pins the extracted binaries accepted by this example. +- No model-provider account is needed for this memory walkthrough. There is no + generated answer and no coding-host execution in this example. + +## Run every check + +From this extracted folder, substituting your real binary path: + +```sh +python3 reproduce.py --agent /path/to/terraphim-agent --output ./harbour-run +``` + +The output folder must not exist. The script checks the binary's exact version +and hash, prepares public input and scoped settings, then starts ten independent +CLI processes. It never changes HOME or installs hooks. It writes only inside +that new output folder. If any command or assertion fails, it exits nonzero. + +Expected result: `verification.json` says PASS, with qualification +`released-binary`. One exact saved lesson is retrieved for `validate brief` by +Brief Reviewer. The unrelated query, Finance Reviewer and separate empty store +return zero. `memory apply` previews proposed context; it executes nothing. + +Inspect `outputs/*.stdout.json` for actual CLI responses, `execution-receipts.json` +for argv/stdout/stderr/exit codes and `data/terraphim/evolution/cli-agent.json` +for the persisted store. `recorded/` contains the publication's observed outputs; +your identifiers and timestamps will naturally differ. + +## Walk through the commands yourself + +Use another fresh folder. Preparation does not capture memory: + +```sh +python3 reproduce.py --agent /path/to/terraphim-agent --output ./manual-run --prepare-only +cd manual-run/fixture +export TERRAPHIM_DEFAULT_DATA_PATH="$(cd .. && pwd)/data" +export TERRAPHIM_SETTINGS_PATH="$(cd .. && pwd)/settings" +``` + +Put the same verified binary on PATH, or replace `terraphim-agent` below with its +full path. Check `terraphim-agent --version` reports 1.21.20, then run: + +```sh +terraphim-agent --format json memory capture --file lesson.md --provenance-tag harbour-public-brief-v1 +terraphim-agent --format json memory list +terraphim-agent --format json memory show MEMORY_ID_FROM_CAPTURE +terraphim-agent memory export --format json --output memories.json +terraphim-agent --config ../config.json memory retrieve 'validate brief' --role 'Brief Reviewer' --format json +terraphim-agent --config ../config.json memory retrieve 'volcanic eruption' --role 'Brief Reviewer' --format json +terraphim-agent --config ../config.json memory retrieve 'validate brief' --role 'Finance Reviewer' --format json +``` + +Every command exits; the next invocation reads the saved file in a new process. +The knowledge graph is in `fixture/kg/`. Its `synonyms::` definitions are explicit, +reviewable terminology. Saving a memory does not automatically create concepts +or copy the item to the separate learning-corrections store. + +## Scope and interpretation + +Roles filter graph retrieval; they do not enforce confidentiality. Separate data +roots select separate stores, and ordinary filesystem permissions still apply. +The lesson is preserved as supplied, so review your own notes before sharing or +exporting them. This sample contains no private data or credentials. Graph +retrieval depends on concept co-occurrence; the note deliberately connects Brief +validation with Project brief. No token savings or host execution are claimed. + +The `--candidate` switch is only for engineering checks and labels results +`candidate-only`; those results do not qualify as released-binary evidence. diff --git a/publication/agent-memory-hooks/memory/package/fixture/brief.md b/publication/agent-memory-hooks/memory/package/fixture/brief.md new file mode 100644 index 0000000..53d17b2 --- /dev/null +++ b/publication/agent-memory-hooks/memory/package/fixture/brief.md @@ -0,0 +1,5 @@ +# Harbour project brief + +Decision: validate the public brief before publication. +Risk: missing the owner of the decision. +Owner: Harbour team. diff --git a/publication/agent-memory-hooks/memory/package/fixture/kg/brief/brief-validation.md b/publication/agent-memory-hooks/memory/package/fixture/kg/brief/brief-validation.md new file mode 100644 index 0000000..3958e8f --- /dev/null +++ b/publication/agent-memory-hooks/memory/package/fixture/kg/brief/brief-validation.md @@ -0,0 +1,4 @@ +# Brief validation +synonyms:: validate brief, brief validation, --check + +Validate the public project brief before publication. diff --git a/publication/agent-memory-hooks/memory/package/fixture/kg/brief/project-brief.md b/publication/agent-memory-hooks/memory/package/fixture/kg/brief/project-brief.md new file mode 100644 index 0000000..f432944 --- /dev/null +++ b/publication/agent-memory-hooks/memory/package/fixture/kg/brief/project-brief.md @@ -0,0 +1,4 @@ +# Project brief +synonyms:: public project brief, brief.md, project brief + +The brief records a decision, risk and owner. diff --git a/publication/agent-memory-hooks/memory/package/fixture/kg/finance/budget-review.md b/publication/agent-memory-hooks/memory/package/fixture/kg/finance/budget-review.md new file mode 100644 index 0000000..15f2a83 --- /dev/null +++ b/publication/agent-memory-hooks/memory/package/fixture/kg/finance/budget-review.md @@ -0,0 +1,4 @@ +# Budget review +synonyms:: budget, spending review + +Review the project budget. diff --git a/publication/agent-memory-hooks/memory/package/fixture/kg/finance/grant.md b/publication/agent-memory-hooks/memory/package/fixture/kg/finance/grant.md new file mode 100644 index 0000000..a5df2fa --- /dev/null +++ b/publication/agent-memory-hooks/memory/package/fixture/kg/finance/grant.md @@ -0,0 +1,4 @@ +# Grant +synonyms:: funding, finance grant + +Review the grant funding. diff --git a/publication/agent-memory-hooks/memory/package/fixture/lesson.md b/publication/agent-memory-hooks/memory/package/fixture/lesson.md new file mode 100644 index 0000000..082bbab --- /dev/null +++ b/publication/agent-memory-hooks/memory/package/fixture/lesson.md @@ -0,0 +1 @@ +Validate the Harbour project brief with python3 validate-brief.py --check. Keep the source decisions in brief.md intact. diff --git a/publication/agent-memory-hooks/memory/package/fixture/validate-brief.py b/publication/agent-memory-hooks/memory/package/fixture/validate-brief.py new file mode 100644 index 0000000..752c0d3 --- /dev/null +++ b/publication/agent-memory-hooks/memory/package/fixture/validate-brief.py @@ -0,0 +1,10 @@ +from pathlib import Path +import argparse, json +parser=argparse.ArgumentParser(description="Validate the fictional Harbour project brief") +parser.add_argument("--check",action="store_true",required=True) +args=parser.parse_args() +text=Path("brief.md").read_text() +assert all(x in text for x in ("Decision:","Risk:","Owner:")), "Brief is missing a required field" +result={"project":"Harbour", "status":"valid", "fields":["Decision","Risk","Owner"]} +Path("validation.json").write_text(json.dumps(result,indent=2)+"\n") +print(json.dumps(result)) diff --git a/publication/agent-memory-hooks/memory/package/released-binaries.json b/publication/agent-memory-hooks/memory/package/released-binaries.json new file mode 100644 index 0000000..d393f6a --- /dev/null +++ b/publication/agent-memory-hooks/memory/package/released-binaries.json @@ -0,0 +1,62 @@ +{ + "version": "1.21.20", + "releaseUrl": "https://github.com/terraphim/terraphim-clients/releases/tag/v1.21.20", + "sourceSha": "d55d373ec2eb100d334712f086dc95a2829856e7", + "qualification": "downloaded from public release URLs; SHA256SUMS and embedded signatures verified", + "binaries": [ + { + "target": "aarch64-apple-darwin", + "archive": "terraphim-agent-1.21.20-aarch64-apple-darwin.tar.gz", + "archiveUrl": "https://github.com/terraphim/terraphim-clients/releases/download/v1.21.20/terraphim-agent-1.21.20-aarch64-apple-darwin.tar.gz", + "archiveSha256": "e7e075d47e79b03f41f646ca388d8c7104065cfb8ea0966761b1f6ae796166bd", + "binarySha256": "bff31dc42d6d1a99bf51291039d7719c6a48c2326e582e5e24eaa1eaf11e17ec", + "bytes": 8973418, + "embeddedSignature": "PASS" + }, + { + "target": "aarch64-unknown-linux-musl", + "archive": "terraphim-agent-1.21.20-aarch64-unknown-linux-musl.tar.gz", + "archiveUrl": "https://github.com/terraphim/terraphim-clients/releases/download/v1.21.20/terraphim-agent-1.21.20-aarch64-unknown-linux-musl.tar.gz", + "archiveSha256": "cf09f161ff42180308509d8e9b43b66c94bedd3801987cd1112b38b3718b5789", + "binarySha256": "5ae520c7034dd125dc6ccb0f6a2f7233794e4bef254381f292566827988f9c52", + "bytes": 9393380, + "embeddedSignature": "PASS" + }, + { + "target": "universal-apple-darwin", + "archive": "terraphim-agent-1.21.20-universal-apple-darwin.tar.gz", + "archiveUrl": "https://github.com/terraphim/terraphim-clients/releases/download/v1.21.20/terraphim-agent-1.21.20-universal-apple-darwin.tar.gz", + "archiveSha256": "3b8766759eb227e0e99fe35cf63bbe7d39eb225dfb5b0b89e6a2b32fc3404fda", + "binarySha256": "ced47028f352f3be126c1ad013abd5ad04bb0d5f21438a6aa55730f101caded6", + "bytes": 18720628, + "embeddedSignature": "PASS" + }, + { + "target": "x86_64-apple-darwin", + "archive": "terraphim-agent-1.21.20-x86_64-apple-darwin.tar.gz", + "archiveUrl": "https://github.com/terraphim/terraphim-clients/releases/download/v1.21.20/terraphim-agent-1.21.20-x86_64-apple-darwin.tar.gz", + "archiveSha256": "6928d1e60fc30a7a9eb4eec3f6429d5a9774370cea967ff2a9d088c380b1615c", + "binarySha256": "61ed0e6737c0011a3a0805ffd9383c189b889f2669458ce4fc7b95260b534e90", + "bytes": 9749512, + "embeddedSignature": "PASS" + }, + { + "target": "x86_64-unknown-linux-gnu", + "archive": "terraphim-agent-1.21.20-x86_64-unknown-linux-gnu.tar.gz", + "archiveUrl": "https://github.com/terraphim/terraphim-clients/releases/download/v1.21.20/terraphim-agent-1.21.20-x86_64-unknown-linux-gnu.tar.gz", + "archiveSha256": "c58f4c384a29e2d5dc43b46c14741b6d083699d03e5ab6b6b15b4f5c1fd9a07f", + "binarySha256": "6c1ce6b3c939888021991c400da6281bafacb6ffe9b63c872a895281d633c2f2", + "bytes": 10335616, + "embeddedSignature": "PASS" + }, + { + "target": "x86_64-unknown-linux-musl", + "archive": "terraphim-agent-1.21.20-x86_64-unknown-linux-musl.tar.gz", + "archiveUrl": "https://github.com/terraphim/terraphim-clients/releases/download/v1.21.20/terraphim-agent-1.21.20-x86_64-unknown-linux-musl.tar.gz", + "archiveSha256": "242c3a72849d3e905c3af6359002fb7473c900df2f4c87ccdebe80c0c538e4ce", + "binarySha256": "22795761e381a5cf3bac973aa2f255a3749acd9677c3dddbe54ff776d2645655", + "bytes": 10189950, + "embeddedSignature": "PASS" + } + ] +} diff --git a/publication/agent-memory-hooks/memory/package/reproduce.py b/publication/agent-memory-hooks/memory/package/reproduce.py new file mode 100644 index 0000000..4c70227 --- /dev/null +++ b/publication/agent-memory-hooks/memory/package/reproduce.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python3 +"""Recover an authored lesson using a verified released Terraphim agent.""" +import argparse +import datetime +import hashlib +import json +import os +from pathlib import Path +import shutil +import subprocess +import time + +BASE = Path(__file__).resolve().parent + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--agent", required=True, type=Path) + parser.add_argument("--output", required=True, type=Path, help="New directory for this run") + parser.add_argument("--candidate", action="store_true", help="Engineering check only; never release evidence") + parser.add_argument("--prepare-only", action="store_true", help="Prepare an isolated run for the manual walkthrough without capturing a lesson") + args = parser.parse_args() + agent = args.agent.resolve(strict=True) + binary_hash = hashlib.sha256(agent.read_bytes()).hexdigest() + version = subprocess.check_output([str(agent), "--version"], text=True).strip() + if version != "terraphim-agent 1.21.20": + raise SystemExit(f"Expected terraphim-agent 1.21.20, got {version}") + if not args.candidate: + identities = json.loads((BASE / "released-binaries.json").read_text()) + if binary_hash not in [item["binarySha256"] for item in identities["binaries"]]: + raise SystemExit("Binary checksum is not in this example's verified release identities") + root = args.output.resolve() + root.mkdir(parents=True, exist_ok=False) + shutil.copytree(BASE / "fixture", root / "fixture") + fixture = root / "fixture" + for name in ("settings", "data", "empty-data", "dashmap", "sqlite", "outputs"): + (root / name).mkdir() + + def role(name, key): + return dict(shortname=key, name=name, relevance_function="terraphim-graph", + terraphim_it=True, theme="lumen", kg=dict(automata_path=None, + knowledge_graph_local=dict(input_type="markdown", path=str(fixture / "kg" / key)), + public=False, publish=False), haystacks=[], llm_provider=None, + llm_auto_summarize=False, extra={}) + + config = dict(id="Server", global_shortcut="Ctrl+Shift+T", + roles={"Brief Reviewer": role("Brief Reviewer", "brief"), + "Finance Reviewer": role("Finance Reviewer", "finance")}, + default_role="Brief Reviewer", selected_role="Brief Reviewer") + (root / "config.json").write_text(json.dumps(config, indent=2) + "\n") + quoted = lambda path: json.dumps(str(path)) + (root / "settings/settings.toml").write_text(f'''server_hostname = "127.0.0.1:8000" +api_endpoint = "http://localhost:8000/api" +initialized = "true" +default_data_path = {quoted(root / 'data')} +role_config = {quoted(root / 'config.json')} +[profiles.dashmap] +type = "dashmap" +root = {quoted(root / 'dashmap')} +[profiles.sqlite] +type = "sqlite" +datadir = {quoted(root / 'sqlite')} +connection_string = {quoted(root / 'sqlite/terraphim.db')} +table = "terraphim_kv" +''') + if args.prepare_only: + print(f"Prepared {root}. No memory captured. See README.md for the scoped manual commands.") + return + records = [] + + def run(label, command, store="data"): + env = os.environ.copy() + for key in ("GH_TOKEN", "GITHUB_TOKEN", "OPENAI_API_KEY", "ANTHROPIC_API_KEY", "OPENROUTER_API_KEY"): + env.pop(key, None) + env["TERRAPHIM_DEFAULT_DATA_PATH"] = str(root / store) + env["TERRAPHIM_SETTINGS_PATH"] = str(root / "settings") + argv = [str(agent), "--format", "json", "--config", str(root / "config.json"), *command] + start = time.monotonic() + process = subprocess.run(argv, cwd=fixture, env=env, text=True, capture_output=True, timeout=90) + record = dict(label=label, argv=argv, store=store, exitCode=process.returncode, + stdout=process.stdout, stderr=process.stderr, + seconds=round(time.monotonic() - start, 3), + utc=datetime.datetime.now(datetime.timezone.utc).isoformat()) + records.append(record) + (root / "outputs" / (label + ".stdout.json")).write_text(process.stdout) + (root / "outputs" / (label + ".stderr.txt")).write_text(process.stderr) + (root / "execution-receipts.json").write_text(json.dumps(records, indent=2) + "\n") + if process.returncode: + raise RuntimeError(f"{label} failed: {process.stderr}") + return json.loads(process.stdout) + + baseline = run("before-capture", ["memory", "list"]) + captured = run("capture", ["memory", "capture", "--file", "lesson.md", "--provenance-tag", "harbour-public-brief-v1"]) + memory_id = captured["memory_id"] + shown = run("reopened", ["memory", "show", memory_id, "--json"]) + listed = run("listed", ["memory", "list"]) + exported = run("exported", ["memory", "export", "--format", "json"]) + positive = run("retrieved", ["memory", "retrieve", "validate brief", "--role", "Brief Reviewer", "--format", "json"]) + negative = run("unrelated-query", ["memory", "retrieve", "volcanic eruption", "--role", "Brief Reviewer", "--format", "json"]) + other_role = run("other-role", ["memory", "retrieve", "validate brief", "--role", "Finance Reviewer", "--format", "json"]) + empty = run("separate-store", ["memory", "retrieve", "validate brief", "--role", "Brief Reviewer", "--format", "json"], "empty-data") + preview = run("context-preview", ["memory", "apply", "--role", "Brief Reviewer", "--prompt", "validate brief"]) + lesson = (fixture / "lesson.md").read_text() + checks = dict(emptyBaseline=baseline["count"] == 0, + exactNewProcessReadback=shown["memory_item"]["content"] == lesson, + listPersistence=listed["count"] == 1, + exactExport=any(item["content"] == lesson for item in exported["memory_items"]), + conceptRetrieval=positive["count"] == 1 and positive["items"][0]["id"] == memory_id and positive["items"][0]["content"] == lesson, + unrelatedQuery=negative["count"] == 0, otherRole=other_role["count"] == 0, + separateStore=empty["count"] == 0, + proposedContext=preview["retrieved_items"] == 1 and preview["injected_bytes"] > 0) + result = dict(status="PASS" if all(checks.values()) else "FAIL", checks=checks, + qualification="candidate-only" if args.candidate else "released-binary", + binaryVersion=version, binarySha256=binary_hash, memoryId=memory_id, + lesson=lesson, processCount=len(records), + note="Every command is a fresh process. Roles filter retrieval; they are not access-control boundaries. Apply is a preview, not execution.") + (root / "verification.json").write_text(json.dumps(result, indent=2) + "\n") + print(json.dumps(result, indent=2)) + if not all(checks.values()): + raise SystemExit(1) + + +if __name__ == "__main__": + main() diff --git a/publication/agent-memory-hooks/memory/render_demo.py b/publication/agent-memory-hooks/memory/render_demo.py new file mode 100644 index 0000000..23f9845 --- /dev/null +++ b/publication/agent-memory-hooks/memory/render_demo.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +"""Render a labelled walkthrough from actual CLI receipts, never invented outputs.""" +import argparse +import html +import json +from pathlib import Path +import subprocess +from playwright.sync_api import sync_playwright + +ROOT = Path(__file__).resolve().parent +parser = argparse.ArgumentParser(description=__doc__) +parser.add_argument('--run', required=True, type=Path) +parser.add_argument('--output', required=True, type=Path) +parser.add_argument('--candidate-preview', action='store_true') +args = parser.parse_args() +run = args.run.resolve() +out = args.output.resolve() +out.mkdir(parents=True, exist_ok=True) +(out / 'screenshots').mkdir(exist_ok=True) +verification = json.loads((run / 'verification.json').read_text()) +assert verification['status'] == 'PASS' and all(verification['checks'].values()) +assert args.candidate_preview or verification['qualification'] == 'released-binary' +plan = json.loads((ROOT / 'storyboard.json').read_text()) +read = lambda name: json.loads((run / 'outputs' / (name + '.stdout.json')).read_text()) +capture, shown, found = read('capture'), read('reopened'), read('retrieved') +lesson = shown['memory_item']['content'] +assert found['items'][0]['content'] == lesson +assert found['items'][0]['id'] == capture['memory_id'] +esc = html.escape + +def box(label, value, kind=''): + return f'

{esc(label)}

{esc(value)}
' + +def terminal(command, result): + return box('Command excerpt · runner supplies JSON format, config and isolated store', '$ ' + command, 'command') + box('Actual saved output', result, 'result') + +brief = (run / 'fixture/brief.md').read_text() +items = [ + box('The public Harbour brief', brief) + '

Keep the instruction.
Find it next time.

', + box('lesson.md · authored instruction', lesson) + terminal('terraphim-agent memory capture --file lesson.md\n --provenance-tag harbour-public-brief-v1', json.dumps(capture, indent=2)), + terminal('terraphim-agent memory show ' + capture['memory_id'], json.dumps({'id': shown['id'], 'content': lesson, 'tags': shown['memory_item']['tags']}, indent=2)) + '

Capture has exited. This is a new process reading the saved item.

', + box('Brief validation · concept note', (run / 'fixture/kg/brief/brief-validation.md').read_text()) + box('Project brief · concept note', (run / 'fixture/kg/brief/project-brief.md').read_text()) + '

Explicit project terminology · two connected concepts in the saved lesson

', + terminal('terraphim-agent memory retrieve "validate brief" --role "Brief Reviewer"', json.dumps({'role': found['role'], 'count': found['count'], 'content': found['items'][0]['content'], 'matched_concepts': found['items'][0]['matched_concepts'], 'tags': found['items'][0]['tags']}, indent=2)), + '
' + ''.join(f'

{esc(label)}

{count}

{esc(detail)}

' for label,count,detail in [('Brief Reviewer', found['count'], 'validate brief'), ('Unrelated question', read('unrelated-query')['count'], 'volcanic eruption'), ('Finance Reviewer', read('other-role')['count'], 'validate brief'), ('Separate empty store', read('separate-store')['count'], 'validate brief')]) + '

Actual retrieved-item counts. Roles filter concepts; separate folders hold separate stores.

', + '

One lesson.
Ready for the next run.

Public brief + lesson + graph
Commands + saved outputs + checksums

terraphim.ai/posts/cold-start-tax-agentic-memory/

' +] +css = '''*{box-sizing:border-box}body{margin:0;background:#0c1422;color:#f5f7fa;font-family:Arial,sans-serif}.frame{width:1440px;height:960px;padding:38px 64px;display:flex;flex-direction:column}.top{font-size:19px;letter-spacing:.06em;color:#a2b8ce;display:flex;justify-content:space-between}.title{font-size:42px;margin:30px 0 24px;line-height:1.12}.content{flex:1;overflow:hidden}.box{background:#142237;border:1px solid #36516a;border-radius:14px;padding:20px 25px;margin-bottom:18px}h2{font-size:19px;color:#aec6dc;margin:0 0 12px;font-weight:normal}pre{font:25px/1.42 Menlo,monospace;white-space:pre-wrap;overflow-wrap:anywhere;margin:0}.command pre{color:#9edac2;font-size:22px}.result pre{font-size:23px}.hero{font-size:55px;line-height:1.3;margin:27px 0}.hero em{font-style:normal;color:#a7e5c9}.note{font-size:24px;line-height:1.5;color:#aec6dc}.controls{display:grid;grid-template-columns:1fr 1fr;gap:24px;margin:25px 0}.control{border:1px solid #36516a;border-radius:14px;padding:28px;background:#142237}.control strong{display:block;font-size:76px;color:#a7e5c9;margin:18px 0}.control p{font-size:23px}.download{font-size:30px;line-height:1.6;color:#c8d5e3}.url{font-size:25px;color:#a7e5c9;margin-top:40px}.footer{border-top:1px solid #36516a;padding-top:17px;font-size:18px;color:#a2b8ce;display:flex;justify-content:space-between}.badge{color:#f5ce84}''' +with sync_playwright() as p: + browser = p.chromium.launch() + page = browser.new_page(viewport={'width':1440,'height':960}, device_scale_factor=1) + for i,(beat,body) in enumerate(zip(plan['beats'], items)): + badge = 'CANDIDATE PREVIEW' if args.candidate_preview else 'RELEASED BINARY · ' + verification['binaryVersion'] + doc = f'
TERRAPHIM · PROJECT MEMORY{esc(badge)}

{esc(beat["title"])}

{body}
' + (out / f'scene-{i}.html').write_text(doc) + page.set_content(doc) + assert page.locator('.content').evaluate('(e)=>e.scrollHeight<=e.clientHeight'), f'overflow in scene {i}' + page.screenshot(path=str(out / f'scene-{i}.png')) + if i in (0,2,5): + name = {0:'poster',2:'saved-lesson',5:'retrieval-controls'}[i] + page.screenshot(path=str(out / 'screenshots' / f'{name}.jpg'), type='jpeg', quality=92) + browser.close() + +concat = [] +for i,beat in enumerate(plan['beats']): + concat += [f"file 'scene-{i}.png'", f"duration {beat['end']-beat['start']}"] +concat.append("file 'scene-6.png'") +(out / 'frames.txt').write_text('\n'.join(concat)+'\n') +subprocess.run(['ffmpeg','-hide_banner','-loglevel','error','-y','-f','concat','-safe','0','-i',str(out/'frames.txt'),'-i',str(ROOT/'narration.wav'),'-t',str(plan['durationSeconds']),'-vf','fps=25,format=yuv420p','-c:v','libx264','-preset','medium','-crf','19','-c:a','aac','-b:a','160k','-movflags','+faststart',str(out/'agent-memory.mp4')],check=True) +(out/'render-receipt.json').write_text(json.dumps({'qualification':verification['qualification'],'sourceRun':str(run),'binarySha256':verification['binarySha256'],'outputPresentation':'Excerpts from actual CLI JSON, preserving source strings and numbers. No simulated terminal execution.','durationSeconds':plan['durationSeconds'],'scenes':7,'candidatePreview':args.candidate_preview},indent=2)+'\n') +print(out/'agent-memory.mp4') diff --git a/publication/agent-memory-hooks/memory/render_voice.py b/publication/agent-memory-hooks/memory/render_voice.py new file mode 100644 index 0000000..26d396e --- /dev/null +++ b/publication/agent-memory-hooks/memory/render_voice.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +"""Use the existing local Emma voice at its natural rate, then verify timings.""" +from pathlib import Path +import hashlib +import json +import wave +import numpy as np +from kokoro_onnx import Kokoro + +ROOT = Path(__file__).resolve().parent +plan = json.loads((ROOT / "storyboard.json").read_text()) +voice_dir = ROOT / "voice" +voice_dir.mkdir(exist_ok=True) +engine = Kokoro('/private/tmp/terraphim-kokoro-v1.0.onnx', '/private/tmp/terraphim-kokoro-voices-v1.0.bin') +engine.sess.disable_telemetry_events() if hasattr(engine.sess, 'disable_telemetry_events') else None +audio = np.zeros(plan['durationSeconds'] * 24000, dtype=np.float64) +for i, beat in enumerate(plan['beats']): + path = voice_dir / (hashlib.sha256(beat['text'].encode()).hexdigest()[:16] + '.wav') + if not path.exists(): + samples, rate = engine.create(beat['text'], voice='bf_emma', speed=1.0, lang='en-gb') + with wave.open(str(path), 'wb') as f: + f.setnchannels(1); f.setsampwidth(2); f.setframerate(rate) + f.writeframes(np.round(np.clip(samples, -1, 1) * 32767).astype(' .01 +audio *= 10**(-2/20) / peak +with wave.open(str(ROOT / 'narration.wav'), 'wb') as f: + f.setnchannels(1); f.setsampwidth(2); f.setframerate(24000) + f.writeframes(np.round(audio * 32767).astype(' {stamp(t1)}', ' '.join(words[start:end]), ''] +(ROOT / 'captions.vtt').write_text('\n'.join(vtt)) +(ROOT / 'transcript.txt').write_text('A project lesson your coding agent can find next time\nSynthetic British female voice: Emma (Kokoro).\n\n' + '\n\n'.join(f"{b['start']}–{b['end']}s | {b['title']}\n{b['text']}" for b in plan['beats']) + '\n') diff --git a/publication/agent-memory-hooks/memory/storyboard.json b/publication/agent-memory-hooks/memory/storyboard.json new file mode 100644 index 0000000..ba34bb9 --- /dev/null +++ b/publication/agent-memory-hooks/memory/storyboard.json @@ -0,0 +1,79 @@ +{ + "status": "script-ready-awaiting-released-binary-recording", + "voice": { + "name": "Emma", + "id": "bf_emma", + "language": "en-gb", + "engine": "Kokoro v1.0 ONNX", + "synthetic": true, + "speed": 1.0, + "processing": "Fixed gain and scheduled silence; no time stretching or pitch change" + }, + "durationSeconds": 77, + "presentation": "Readable walkthrough of real saved CLI outputs; source text and numeric outputs unchanged. Every CLI invocation is a separate process.", + "beats": [ + { + "start": 0, + "end": 9.132, + "title": "Keep the lesson for the next run", + "text": "A new agent session should not mean explaining the same project instruction again. Here is one lesson from our Harbour project brief.", + "audio": "voice/2501289df87f66dc.wav", + "audioDuration": 7.732, + "audioSha256": "46e27c7d54a757aeb0f45fe8cb839253fd9324338c1da507fecc66b2b5d3c736" + }, + { + "start": 9.132, + "end": 21.177, + "title": "Save the actual instruction", + "text": "I save the validation instruction as memory, with a tag identifying its source. The saved item contains the exact lesson, not just a label saying something was captured.", + "audio": "voice/64dc57d2da41f227.wav", + "audioDuration": 10.644625, + "audioSha256": "4c9ff33eb4bb047e40d48f96c25e0f0e8a7827452d74a2690b10d2e430b0299e" + }, + { + "start": 21.177, + "end": 31.653, + "title": "Read it back in another process", + "text": "That command has finished. A new process opens the saved item by its identifier. The same instruction is still there, ready to inspect or export.", + "audio": "voice/5f16aa3be9bd797c.wav", + "audioDuration": 9.076, + "audioSha256": "41999eb9b0e5a53c7e2e5e08afb9bfb377cb709d8cd1e90bdd3154d89d13ba29" + }, + { + "start": 31.653, + "end": 42.681, + "title": "Connect the words to the project", + "text": "The project role has a small knowledge graph. Validate brief, brief validation, and the check option name the same concept. The note also names the project brief.", + "audio": "voice/349f5eb1edd0671a.wav", + "audioDuration": 9.628, + "audioSha256": "17344a48853863ad6c44e30fba94e06003faafca0824163d4299a78038e1d66b" + }, + { + "start": 42.681, + "end": 53.402, + "title": "Find the lesson by its meaning", + "text": "I search for validate brief. The graph retrieves the saved instruction and its source tag. This is the actual stored text, rather than a newly generated answer.", + "audio": "voice/6567e11777a6f4dc.wav", + "audioDuration": 9.320666666666666, + "audioSha256": "1248f078ef6239f923347cc3ada5e281dec0a2725fc5df8d7d6935ac5140a64f" + }, + { + "start": 53.402, + "end": 64.272, + "title": "Check when it should stay quiet", + "text": "An unrelated question returns no memory. So does the finance role, and a separate empty store. Roles shape retrieval; separate folders hold separate stores.", + "audio": "voice/a8fdad6099b18698.wav", + "audioDuration": 9.47, + "audioSha256": "672459043a0387e6000fc4c41615926377a4ca0a002ee747f717580d72d2a977" + }, + { + "start": 64.272, + "end": 77, + "title": "Try it on a clean example", + "text": "Download the public brief, graph, commands and saved results. Next, we will use the same project to show a failed command, a reviewed correction, and what an agent actually executes.", + "audio": "voice/e8cb6e566c74185c.wav", + "audioDuration": 10.796, + "audioSha256": "05908960e6ac4b43ce5df574f94a18ca80f98615d7c78bb460148bf0bc1a5fa1" + } + ] +} diff --git a/scripts/test_agent_memory_publication.py b/scripts/test_agent_memory_publication.py new file mode 100644 index 0000000..c9a5a8a --- /dev/null +++ b/scripts/test_agent_memory_publication.py @@ -0,0 +1,67 @@ +"""Bind the memory walkthrough and download to actual released-binary outputs.""" +import hashlib +import json +from pathlib import Path +import re +import unittest +import zipfile + +ROOT = Path(__file__).resolve().parents[1] +MEDIA = ROOT / 'static/demos/agent-memory' + + +class AgentMemoryPublicationTests(unittest.TestCase): + def test_media_inventory_is_complete_and_unchanged(self): + manifest = json.loads((MEDIA / 'manifest.json').read_text()) + self.assertEqual(manifest['qualification'], 'released-binary') + self.assertGreaterEqual(manifest['durationSeconds'], 60) + self.assertLessEqual(manifest['durationSeconds'], 90) + self.assertTrue(manifest['voice']['synthetic']) + self.assertTrue(manifest['voice']['speechRateUnchanged']) + for entry in manifest['files']: + data = (MEDIA / entry['file']).read_bytes() + self.assertEqual(hashlib.sha256(data).hexdigest(), entry['sha256']) + self.assertEqual(len(data), entry['bytes']) + self.assertFalse(manifest['render']['candidatePreview']) + + def test_download_proves_exact_content_and_negative_controls(self): + with zipfile.ZipFile(MEDIA / 'harbour-memory-example.zip') as bundle: + self.assertIsNone(bundle.testzip()) + self.assertTrue(all(not n.startswith('/') and '..' not in Path(n).parts for n in bundle.namelist())) + load = lambda n: json.loads(bundle.read(n)) + verification = load('recorded/verification.json') + self.assertEqual(verification['qualification'], 'released-binary') + self.assertEqual(verification['status'], 'PASS') + self.assertTrue(all(verification['checks'].values())) + ids = load('released-binaries.json') + self.assertIn(verification['binarySha256'], [b['binarySha256'] for b in ids['binaries']]) + lesson = bundle.read('fixture/lesson.md').decode() + shown = load('recorded/outputs/reopened.stdout.json') + found = load('recorded/outputs/retrieved.stdout.json') + self.assertEqual(shown['memory_item']['content'], lesson) + self.assertEqual(found['items'][0]['content'], lesson) + self.assertEqual(found['items'][0]['id'], verification['memoryId']) + self.assertEqual(found['count'], 1) + for name in ['unrelated-query', 'other-role', 'separate-store']: + self.assertEqual(load(f'recorded/outputs/{name}.stdout.json')['count'], 0) + for row in bundle.read('SHA256SUMS').decode().splitlines(): + digest, name = row.split(' ', 1) + self.assertEqual(hashlib.sha256(bundle.read(name)).hexdigest(), digest) + self.assertIn('--prepare-only', bundle.read('README.md').decode()) + + def test_reader_has_setup_media_and_reproduction_links(self): + page = (ROOT / 'content/posts/cold-start-tax-agentic-memory.md').read_text() + self.assertLess(page.index('export TERRAPHIM_DEFAULT_DATA_PATH'), page.index('memory capture')) + self.assertIn('updated=2026-10-13', page) + for name in ['agent-memory.mp4','captions.vtt','transcript.txt','harbour-memory-example.zip','manifest.json']: + self.assertIn('/demos/agent-memory/' + name, page) + self.assertTrue((MEDIA / name).is_file()) + self.assertIn('synthetic British female narration', page) + for obsolete in ['42 agents', 'all eight stages shipped', '89%']: + self.assertNotIn(obsolete, page) + for target in re.findall(r'(?:src|poster)="(/demos/agent-memory/[^"]+)"', page): + self.assertTrue((ROOT / 'static' / target.lstrip('/')).is_file()) + + +if __name__ == '__main__': + unittest.main() diff --git a/static/demos/agent-memory/agent-memory.mp4 b/static/demos/agent-memory/agent-memory.mp4 new file mode 100644 index 0000000..b8073e6 Binary files /dev/null and b/static/demos/agent-memory/agent-memory.mp4 differ diff --git a/static/demos/agent-memory/captions.vtt b/static/demos/agent-memory/captions.vtt new file mode 100644 index 0000000..8146c61 --- /dev/null +++ b/static/demos/agent-memory/captions.vtt @@ -0,0 +1,46 @@ +WEBVTT + +00:00:00.200 --> 00:00:04.066 +A new agent session should not mean explaining the same project + +00:00:04.066 --> 00:00:07.932 +instruction again. Here is one lesson from our Harbour project brief. + +00:00:09.332 --> 00:00:14.654 +I save the validation instruction as memory, with a tag identifying its source. The + +00:00:14.654 --> 00:00:19.977 +saved item contains the exact lesson, not just a label saying something was captured. + +00:00:21.377 --> 00:00:25.733 +That command has finished. A new process opens the saved item by + +00:00:25.733 --> 00:00:30.453 +its identifier. The same instruction is still there, ready to inspect or export. + +00:00:31.853 --> 00:00:36.489 +The project role has a small knowledge graph. Validate brief, brief validation, and + +00:00:36.489 --> 00:00:41.481 +the check option name the same concept. The note also names the project brief. + +00:00:42.881 --> 00:00:47.369 +I search for validate brief. The graph retrieves the saved instruction and its + +00:00:47.369 --> 00:00:52.202 +source tag. This is the actual stored text, rather than a newly generated answer. + +00:00:53.602 --> 00:00:58.337 +An unrelated question returns no memory. So does the finance role, and + +00:00:58.337 --> 00:01:03.072 +a separate empty store. Roles shape retrieval; separate folders hold separate stores. + +00:01:04.472 --> 00:01:08.071 +Download the public brief, graph, commands and saved results. Next, + +00:01:08.071 --> 00:01:11.669 +we will use the same project to show a failed + +00:01:11.669 --> 00:01:15.268 +command, a reviewed correction, and what an agent actually executes. diff --git a/static/demos/agent-memory/harbour-memory-example.zip b/static/demos/agent-memory/harbour-memory-example.zip new file mode 100644 index 0000000..2f02eff Binary files /dev/null and b/static/demos/agent-memory/harbour-memory-example.zip differ diff --git a/static/demos/agent-memory/manifest.json b/static/demos/agent-memory/manifest.json new file mode 100644 index 0000000..5629358 --- /dev/null +++ b/static/demos/agent-memory/manifest.json @@ -0,0 +1,72 @@ +{ + "edition": "agent-memory-20261013", + "qualification": "released-binary", + "durationSeconds": 77, + "release": "1.21.20", + "voice": { + "name": "Emma", + "engine": "Kokoro ONNX", + "locale": "en-GB", + "synthetic": true, + "speechRateUnchanged": true + }, + "render": { + "qualification": "released-binary", + "sourceRun": "/private/tmp/harbour-memory-released-macos-20261013", + "binarySha256": "bff31dc42d6d1a99bf51291039d7719c6a48c2326e582e5e24eaa1eaf11e17ec", + "outputPresentation": "Excerpts from actual CLI JSON, preserving source strings and numbers. No simulated terminal execution.", + "durationSeconds": 77, + "scenes": 7, + "candidatePreview": false + }, + "probe": { + "streams": [ + { + "type": "video", + "codec": "h264" + }, + { + "type": "audio", + "codec": "aac" + } + ], + "durationSeconds": 77.0 + }, + "files": [ + { + "file": "agent-memory.mp4", + "sha256": "c7565a87355da35f278649b3a041cd9a0ce7b75ce4f2fb0943868c479dff0f69", + "bytes": 1911762 + }, + { + "file": "captions.vtt", + "sha256": "b58c07b01f2a95d7c30e81353e27d51be9faba70f1c0c4a72844beecd237ae9e", + "bytes": 1586 + }, + { + "file": "harbour-memory-example.zip", + "sha256": "df88316ce57c972a4c8f0c458149b94d10e071cc54cc4f25fba82bdb00f43606", + "bytes": 17442 + }, + { + "file": "screenshots/poster.jpg", + "sha256": "c68229aa51121987ee37c1e950abdd9a5ff773d924300745eae803acaba3b510", + "bytes": 98313 + }, + { + "file": "screenshots/retrieval-controls.jpg", + "sha256": "71e1a182e226c7c5df4cd7f4f885d27349f74b8485fbeb80e36b569a1264a389", + "bytes": 93292 + }, + { + "file": "screenshots/saved-lesson.jpg", + "sha256": "45337fd0c1bdfd51f7c4190264c0902beb0883d8cef4ab2c02107a661660fe79", + "bytes": 130036 + }, + { + "file": "transcript.txt", + "sha256": "41f32a267d4c29f78abf24ee4d6b2cea3fb7649c670e89f5f1dcdb8416f3cf2d", + "bytes": 1559 + } + ] +} diff --git a/static/demos/agent-memory/screenshots/poster.jpg b/static/demos/agent-memory/screenshots/poster.jpg new file mode 100644 index 0000000..a556a27 Binary files /dev/null and b/static/demos/agent-memory/screenshots/poster.jpg differ diff --git a/static/demos/agent-memory/screenshots/retrieval-controls.jpg b/static/demos/agent-memory/screenshots/retrieval-controls.jpg new file mode 100644 index 0000000..0448121 Binary files /dev/null and b/static/demos/agent-memory/screenshots/retrieval-controls.jpg differ diff --git a/static/demos/agent-memory/screenshots/saved-lesson.jpg b/static/demos/agent-memory/screenshots/saved-lesson.jpg new file mode 100644 index 0000000..d9ea95c Binary files /dev/null and b/static/demos/agent-memory/screenshots/saved-lesson.jpg differ diff --git a/static/demos/agent-memory/transcript.txt b/static/demos/agent-memory/transcript.txt new file mode 100644 index 0000000..c9765a5 --- /dev/null +++ b/static/demos/agent-memory/transcript.txt @@ -0,0 +1,23 @@ +A project lesson your coding agent can find next time +Synthetic British female voice: Emma (Kokoro). + +0–9.132s | Keep the lesson for the next run +A new agent session should not mean explaining the same project instruction again. Here is one lesson from our Harbour project brief. + +9.132–21.177s | Save the actual instruction +I save the validation instruction as memory, with a tag identifying its source. The saved item contains the exact lesson, not just a label saying something was captured. + +21.177–31.653s | Read it back in another process +That command has finished. A new process opens the saved item by its identifier. The same instruction is still there, ready to inspect or export. + +31.653–42.681s | Connect the words to the project +The project role has a small knowledge graph. Validate brief, brief validation, and the check option name the same concept. The note also names the project brief. + +42.681–53.402s | Find the lesson by its meaning +I search for validate brief. The graph retrieves the saved instruction and its source tag. This is the actual stored text, rather than a newly generated answer. + +53.402–64.272s | Check when it should stay quiet +An unrelated question returns no memory. So does the finance role, and a separate empty store. Roles shape retrieval; separate folders hold separate stores. + +64.272–77s | Try it on a clean example +Download the public brief, graph, commands and saved results. Next, we will use the same project to show a failed command, a reviewed correction, and what an agent actually executes.