agentm capture - alexherrero/agentm GitHub Wiki
We need a way to get a thought or an article into agentm's memory from your phone, your browser, or a chat. This design creates a single front door for all captured content. Everything lands in its memory class the moment it arrives, marked with how much is known about it, and the machinery on your machine judges it from there. We define the paths in, how each kind of capture is handled, the machinery that does the processing, and the security boundary everything crosses.
We need a way to capture information from any device. When this design was first written, captures were staged in an inbox that an automated triage pass promoted, merged or expired. Filing v2 (September 2026) retired the inbox: a capture files straight into its class directory and carries its own filing state, status: unfiled until something judges it. The body below describes the write path as it is; the history at the bottom records the change. What a capture holds the moment it lands, and who may write which field, is decided in AgentM Vault Β§ Capture.
Two transports already work. The claude.ai Capture project writes candidates into the vault from the phone or any chat through the Google Drive connector. The Obsidian Web Clipper writes candidates from desktop Chrome. Neither transport needs a credential on any device. Our privacy rules shape every choice here. Data moves only through local and first-party tools. All captured content is treated as untrusted until it crosses one audited doorway.
Building this takes little new effort. We reuse many existing components. We already have the tools to write files safely, split text into chunks, evaluate confidence, run scheduled tasks, and provide server capabilities. The new work consists of a capture interface, tracking where data came from, a command to process long articles, and a scheduled job that fully processes forwarded links. We leave automatic cross-linking across the entire vault to the auto-organization design (agentm-auto-organization.md). That design ships in the same arc. Newly ingested notes get connected soon after they land.
Capture works in three stages. This design is organized the same way.
First are the ways in. Several doors deliver a candidate note into its memory class under Agent/memory/. These doors include your phone, your browser, and your desktop.
Second is the handling. Every candidate is absorbed into memory. Links and articles get fully processed into small linked reference notes. Ideas route to the ideas ledger. A capture can carry your instructions for the agent to act on after it absorbs the note.
Third is the machinery. A small set of tools and one scheduled job on your machine do all the work. The phone and browser only drop off a note.
Ingestion produces both forms always. It keeps the full article stored intact as one document note. It also splits the article into small chunk notes for retrieval. Neither replaces the other. This is the only path that produces more than one note per capture. Everything else stays one note per item. Where the finished memories live is the memory-system design's territory. How they surface automatically in later sessions is also covered there. The last Detailed Design section points there.
Everything in this design runs inside our existing memory engine, server, and task runner. The design adds a second tool beside memory_append. We add two commands, a few tracking fields, and one scheduled job.
| Component | What it is |
|---|---|
memory_capture |
MCP tool: files a candidate at its class directory, status: unfiled until something judges it |
/memory capture |
the same door as a CLI verb, for in-process callers and scripts |
/memory ingest |
URL or file β chunked domain-reference notes, provenance-stamped |
| provenance fields |
source_url Β· source_fetched join save.py's optional field order |
| ingest sweep | a runner job: forwarded links and documents among the unfiled captures β the ingestion pipeline |
| the bridge guard | the one audited boundary all captured content crosses |
| Trigger | Path |
|---|---|
| the Capture project (phone / any chat) | Drive connector create_file β the vault |
| Obsidian Web Clipper (desktop Chrome) | clip β the vault |
| MCP call from any connected host |
memory_capture β its class directory |
| CLI / in-session verb |
/memory capture Β· /memory ingest
|
| runner schedule | ingest sweep, cadence-bound (default hourly) |
| session hooks | reflection files only its HIGH candidates, as unfiled; MEDIUM and LOW go to the session trace |
Saves either succeed completely or fail cleanly. The system alerts you immediately if a capture fails. Your thoughts are never lost silently. Every pass that changes a note journals the change, and the vault is a git repository, so you can undo any of them.
| Door | Status today | Notes |
|---|---|---|
| Claude app / claude.ai chat β the Capture project + Google Drive connector | works today | the primary phone-and-chat path; the recipe and standing instructions live in the findings memo; the captureβrecall probe rides this path |
| Obsidian Web Clipper (desktop Chrome) | works today | the one-click desktop article path; template recorded in the findings memo; clips arrive with full content, so the sweep skips the fetch |
| any file into a class directory (desktop) | works today | any app that writes the folder is a door; a note without a type files as the contract's default type, unfiled; zero build |
/memory capture Β· memory_capture (in-session) |
this design builds them | the explicit door for sessions and scripts |
| reflection's HIGH lane | shipped | the agent's own session learnings; MEDIUM and LOW stay in the session trace as candidates |
| Dispatch / Remote Control | live, research preview | secondary in-session phone paths; Dispatch reaches the vault only when its message explicitly asks for a Code session |
From your phone, the flow works today. You open the Capture project in the Claude app. You send capture this: <link>, a bare thought, or idea: <thought>. You can include a trailing instruction like "add to my ideas ledger". The project uses standing instructions. Claude reads the link and builds the candidate. The claude.ai Google Drive connector creates the file in the vault. The file rides Anthropic's servers to Drive. Drive syncs the file to your machine. The phone requires no harness. The process needs no credentials. Your machine can be asleep. Drive delivers the file when the machine wakes. The local machinery processes the file from there. You complete one initial setup step. You approve the connector's Create-file permission with "Always allow". A failed capture appears in the chat reply. You always know the exact outcome.
The Obsidian Web Clipper runs in your desktop browser. It saves a full article into the vault with one click. Clipper paths resolve relative to Obsidian's own vault root. This root sits one level above the engine's vault_path(). You prefix the clipper paths with Agent/. A class directory is an ordinary folder in the vault. Any app that writes a file there is a door. You can set Obsidian's new-note button to default there. You configure this path via Settings, then Files and links, then Default location for new notes. Obsidian on the phone cannot open a Google-Drive-synced vault. The Claude app provides the phone path.
A candidate carries the memory card of AgentM Vault. We record the transport in source:, the time in created:, and the device in surface:. A candidate also declares its kind. We use capture for links, articles, and thoughts. We use idea for entries headed to the ideas ledger. A candidate includes any tags you attached. It also holds an instructions field. The rules for instructions appear in the next section. A candidate written by a model that knows why it was kept also carries summary, why and importance; one written by anything else leaves why empty, which is what marks it status: unfiled for the nightly pass to judge.
The system writes these notes through the filing engine and save_entry, the same door every writer uses, under the vault mutex and the daily write cap. The new notes use our standard file naming scheme. The nightly enrichment batch judges the unfiled ones together.
The system absorbs every candidate first. The content lands, gets processed, and becomes recallable. Processing depends on what arrived.
-
A thought or note stays one candidate. Filing v2's write path files it directly at its class directory,
status: unfiledandfiling_confidence: low, rather than staging it behind a separate promotion step β the inbox triage system that once promoted, merged, or expired it from a staging folder is retired (agentm v9.20.0). An unfiled note is not hidden from your agent's searches; it is demoted in ranking until something re-judges it. -
A link or a document goes through the ingestion pipeline. One outside source is one note (amended 2026-09-28). Ingestion stores the complete text intact as one document note, beside the atomic facts it draws out, so you can review the original whole whenever you need it. The index cuts a long document into several chunk vectors that all point back to that note, and dense search scores the note by its best chunk, which is how a question answered deep in an article still finds it. Capturing the same source again under the same title updates its note in place, found by
source_idorsource_url: the path, links and id stay, the frontmatter merges with yourimportancekept, the body is replaced, and git keeps the old wording. A different title from the same source, such as one of the article's atomic facts, is a note of its own. Everything else is one note per item. A phone capture is one candidate. An explicit save is one note. Reflection extracts one small note per durable fact from a session. A reply you typed to the agent, such as "yes and then please file aβ¦" or "let's do follow-up b", directs the session rather than recording a fact, so reflection drops it and logs the drop (amended 2026-09-28). The session itself is never stored. Conversations are not rebuildable from memory. This is by design. -
An idea (
kind: idea) preserves your words verbatim. The system records the raw text exactly as submitted. An idea that matches a card inpersonal/ideas/by slug, title or body is added to that card under a dated## Added by captureheading with its source, and no second note is written (amended 2026-09-28). A new idea becomes a card of its own. One mechanism serves both operator-typed ideas and the ideas reflection lists in the session trace. -
A capture with instructions triggers an action after the absorb step succeeds. Examples include "add to my ideas ledger" or "research this further, then file". On a chat surface, the acting happens in the same turn. The Capture project already does this. A clipper capture arrives with its instruction unexecuted. The machinery runs the act step on your machine. It stamps the candidate with
instructions_actedwhen done.
The instructions field may only carry text you typed at capture time. The security rule is mechanical. It applies without exception. Text inside a fetched article is data. Phrases like "ignore previous instructions" are inert body content. This holds true no matter how they are phrased. A fetched article never gains instruction authority. This is the trust boundary's authority rule applied field by field.
The front door tools. The memory_capture tool takes your content and optional details. It files them at their class directory, status: unfiled unless the caller named the type and said why, and returns an identifier. It marks any candidate carrying a link for full processing by the ingest sweep. The memory_append tool continues to work exactly as it does today. It handles explicit writes directly to permanent memory. The design adds memory_capture as a second tool beside it. We update both tool descriptions so agents understand this division. Secondary devices default to the capture tool. A prototype of the chat-surface front door already exists. The claude.ai Capture project uses standing instructions to implement this exact contract. The instructions absorb content first and limit writes to one per message. We use the prototype text as the starting specification for the cloud-side memory_capture behavior when the tool gets built.
Article ingestion. You use the /memory ingest <url|file> command to process web pages or files directly. The system reads the content. It stores the full article intact as one document note. The system writes the document note via save_entry(), which queues it for search indexing automatically, and the index splits a long note into chunk vectors for retrieval (amended 2026-09-28). You can omit the --topic flag. When you do, the agent suggests a title-based slug for you to confirm. Ingested notes take the default volatile decay β the right treatment for reference material. The auto-organization design builds vault-wide linking. Ingestion hands each batch to that system once it ships.
The ingest sweep. A scheduled job on the runner gives forwarded content its full processing. The job scans the unfiled captures each cycle. It looks for candidates that are links or whole documents. It fetches each one and runs it through the /memory ingest pipeline. The sweep marks the original candidate as ingested. It points the candidate at the new batch. A fetch can fail on a paywall or a dead link. The sweep leaves a failed candidate in place and records the error. Your digest reports the failure. The sweep runs hourly by default. Four more duties ride the same job. First, a clipped article arrives with its full content. The sweep skips the fetch and goes straight to chunking. This sidesteps paywalls since you clipped what you could see. Second, the sweep runs the act step for any candidate carrying an unexecuted instruction. Third, the sweep folds kind: idea candidates into the ideas ledger. Fourth, the sweep corrects timestamps. Chat-surface captures come from a model with no live clock. The sweep validates the captured: field against the file's creation time. It corrects the field when they disagree. The Drive connector can create files but never update or delete them. An uncertain capture sometimes lands twice as near-duplicate candidates. The sweep checks sibling unfiled candidates for an exact source_url match before fetching β a resend is marked status: ingest_duplicate and pointed at the original, never re-fetched or promoted separately. This exact-match check is the sweep's own. The inbox triage engine that once ran a broader near-duplicate dedup is retired (agentm v9.20.0) β the copies job now collapses content-identical families, and the dedup stage proposes a merge, but neither parks a fuzzy-similar, fingerprint-distinct pair for you. Without this narrower check, the sweep's own batch processing would still defeat itself on a same-cycle resend.
Provenance plumbing. We formally add source_url and source_fetched as optional fields in FRONTMATTER_FIELD_ORDER. We update the save_entry and vault_lint tools to accept these new fields. We apply this small change independently. The ingestion command categorizes notes under the personal group. A few older notes use a different group: name. We treat those as legacy files.
The capture door provides the single security checkpoint between untrusted content and your agent's environment. The system treats all captured content purely as data. Text that reads like an instruction gets no authority at capture time. The system records exactly where every piece of data came from. An unfiled capture is visible to recall from the moment it lands, at reduced weight until something judges it; nothing is hidden. We reuse the MCP server's _validate_path_segment tool to ensure files go to the right place.
Two mechanisms carry candidates across this boundary, both landing on the same trust guarantee through different machinery. A memory_capture candidate β a thought, a link, or an idea typed at capture time β files directly to its class directory today, flagged filing_confidence: low (filing v2's write path). The inbox triage system that once staged it behind an explicit promote, merge, or expire decision is retired (agentm v9.20.0). A forwarded link or document the ingest sweep fetches on its own schedule is patched in place as status: ingest_staged and promoted through ingest.ingest() after one full sweep cycle. That status is walled from the Python recall arm and not from the Go ranker, so the two arms disagree on it; the reconciliation is filed in the follow-ups, and until it lands the sweep's window is a delay, not invisibility. Explicit, human-invoked /memory ingest and memory_append sit outside both: you named the source yourself, so they write directly to permanent memory at the same trust level as any other deliberate save.
We discuss the remaining risk of malicious content entering your active memory in the Risks section.
The multi-user team vault initiative opens after this arc as its own project (ruled 2026-07-16). We design the current capture process so it can adapt to teams when the time comes. We track the device in surface: and the transport in source: for every note today. An author: field can join them later when multiple people share the vault. The personal/ folder structure provides a clear boundary. A future permissions system can enforce this boundary. We rely on the current system's assumption that only one person writes to the vault. When we build support for multiple simultaneous writers, this capture tool inherits those improvements.
A capture is an ordinary memory entry from the moment it lands. This design adds nothing to how the system shapes those entries: the card is AgentM Vault's, and the memory-system design (wiki/designs/agentm-memory-system.md) holds the older entry contract it inherits from. That contract requires one atomic note per fragment, frontmatter keys, kind as the folder, and dense wikilinks. The same design document details the vault layout, the three ownership tiers, and the archive convention. That convention keeps cold notes out of the way. The recognized kind: values live in the Kind-Taxonomy registry (wiki/reference/Kind-Taxonomy-Registry.md). An ingested article lands as one reference note in its class directory, with its topic in the slug and the tags. Ideas land as cards in personal/ideas/, in the shape AgentM Vault gives them, and a captured idea that matches a card is added to it. Read those documents to understand where everything this design captures ends up.
The recall-loop section of the memory-system design describes how these memories come back on their own. The always-load set assembles at session start. A search runs over the index on every prompt. A captured article surfaces automatically in any later session that needs it. We test this exact behavior toward the end of this arc. A capture made from your phone must surface automatically in a fresh session. The arc's acceptance probe covers capture, ingestion, and automatic recall together.
-
Make every write a candidate. We reject this because you still need a way to save a card you have already judged. A capture that names its type and says why lands
active; anything else landsunfiled. The field, not a second tool, carries the difference. - Use the old Telegram and ntfy.sh notification tools. We reject this because it violates our privacy rule against third-party push services. Our local tools provide the same convenience safely.
- Run a continuous launchd background watcher to monitor the unfiled captures. We reject this because we already have a reliable scheduling system. A continuous background daemon wastes resources to save a few minutes. The capture process works well as a delayed background task.
- Expand the approved-source feed tool (retired 2026-10) to handle article ingestion. We reject this because that tool worked best for single feeds without breaking them into chunks. Modifying it for ingestion requires too much work and confuses its purpose.
- Do nothing and keep capture restricted to the terminal. We reject this because capture-from-anywhere is the first goal of this arc.
We reuse our existing tools without changes: save.py, vault_lock, chunking.py, reflection's lanes, dream_confirm, the runner, and the MCP server. The claude.ai Google Drive connector carries phone and chat captures. Its traffic routes through Anthropic's servers. No credential lives on any device or sandbox. The Obsidian Web Clipper is operator-installed. Chrome blocks automated extension installs by design. We use the Dispatch and Remote Control preview tools as the in-session phone paths. We coordinate with the auto-organization design (agentm-auto-organization.md, same arc) to handle cross-linking your notes.
We add new fields, one new tool, and two new commands. Your existing notes retain their current format. The memory_append tool keeps working exactly as it does today. We add a second tool beside it. You can manually fix the group name on the two older domain notes. No systems depend on them.
- The secondary in-session phone paths (Dispatch, Remote Control) are preview surfaces. If a preview surface is pulled, capture keeps working through the connector and the desktop doors.
- Phone-started cloud sessions run in Anthropic's sandbox. They cannot reach the vault today. Two developments would change that. The memory server's remote tier (scheduled for the mobile/web wave) would let a cloud session call
memory_captureover a tunnel. Anthropic's self-hosted runner program, organization-only today, would let a session target this machine directly. Re-audit trigger: self-hosted runners opening to individual plans. - We ship the ingestion tool before the auto-organization design's vault-wide linking finishes. The ingestion tool creates local links immediately. Auto-organization handles the rest. Re-audit trigger: the auto-organization design gets delayed.
- A
memory_capturecandidate is visible to recall from the moment it lands, at reduced weight whileunfiled. There is no review window. The boundary is elsewhere: captured content never gains instruction authority (theinstructionsrule, mechanical), a fetched or mailed transport carriestrust: untrustedthrough every pass, and recalled memory is data to the surface that reads it. Re-audit trigger: any real injection incident, or a surface that acts on recalled text as instructions. - Capturing content easily from your phone might increase the number of items landing in your vault. Your weekly summary tracks these numbers. The auto-expire this risk once relied on (
inbox_triage.py) retired in agentm v9.20.0 β see the Trust Boundary section and the amendment log below. - A forwarded link can fail to fetch due to a paywall, a dead link, or your machine being offline. The candidate stays where it landed with the failure recorded. Your digest surfaces it. Nothing disappears silently.
- The Drive connector can create files but never update or delete them. We design around this limit. We write once per message. Corrections travel inside that one file. Re-audit trigger: the connector gaining update or delete tools.
- Chat-surface timestamps are model-estimated and approximate. The sweep's re-stamp against the file's own creation time fixes this. Until that sweep lands, the
captured:field from chat surfaces is approximate.
We expand the write surface. We secure this by routing everything through one audited doorway. We reuse our tested path validation tools. A casual capture lands unfiled, demoted in recall until the nightly pass or a session judges it. We rely entirely on local and first-party tools to move your data safely.
The system must never silently lose a thought you captured. It reports success or failure clearly back to the device you used. We make it safe for the system to retry failed saves. The ingest sweep marks every candidate it processes. A crashed cycle resumes where it left off. Retries are safe.
We use our existing safe write tools to ensure file completeness. A bad capture lands as one unfiled note and touches nothing else. Every pass that changes a note journals the change, and the vault is a git repository, so you can undo any mistake.
Your data stays entirely on your device and inside your synced vault. We restrict all data movement to first-party and local tools. Your captured notes live securely in your private vault. Our privacy scanner continues to check anything you later move into a public repository.
The capture process runs in the background. The system promises only that your captured items appear in the vault before your next working session. The ingest sweep runs every hour by default. We can make it run more often if you need faster results.
We can run automated tests on every component. This includes the file formats, the capture tool, the text splitter, and the scheduled jobs. We add a test that tracks a captured note all the way through to a successful search result. We add an arc-end check that a captured article surfaces automatically in a fresh session through the standing recall loop. The ingestion tool uses a sample article to verify it stores the full article intact as one document note, and creates the separate small chunk notes for retrieval.
We split the work into three parts. First, we build the core format and the new capture tools (Medium). Second, we build the article ingestion command, including automatic topic naming and linking (Medium). Finally, we build the ingest sweep and write the device guides (Medium). The sweep handles fetching, chunking, the act step, the idea-ledger fold, and the timestamp re-stamp.
We publish wiki/designs/agentm-capture.md when we finish the first part of the work. We write two new how-to guides: Capture-From-Your-Phone and Ingest-An-Article. We update the frontmatter-field reference and the memory SKILL.md verb-table to document the new tracking fields and commands. We record each finished feature in Completed-Features.md.
We release each of the three parts independently as its own named minor update when the code passes tests. The updates are "capture contract + front door", then "article ingestion", then "forwarded links + the phone path". We record this ladder in ROADMAP-MASTER Β§ FRIDAY. We set the specific dates when each release actually cuts.
N/A: Single-operator local memory tool with no service level agreements.
You monitor your capture activity in the same places you monitor all memory activity. The daily corpus scorecard and the nightly digest show the unfiled count, the queue's age, and what the enrichment batch judged. You see live memory actions directly on your console. The captureβrecall probe's result lives in the plan's evidence. The FRIDAY health family on the dashboard lights up at the end of the arc, per its locked acceptance shape.
Every note records the device and source you used to capture it. The enrichment batch journals what it decided for each note. The ingest sweep logs its progress through our standard task runner.
We add new files and one optional tool. You can delete a bad capture by hand, or let the second sub-floor verdict sink it to dormant. You can undo any pass's change through the vault's git history. If you remove the new tool, the system returns exactly to how it works today. There is no schema or state migration to unwind.
| Date | Change | Status |
|---|---|---|
| 2026-09-28 |
Amended on the operator's rulings of 2026-09-24 on the AgentKV layout convergence spec, section 6 (task 178, Plan C; the governing amendment is AgentM Vault's row of the same date). Three passages change. One outside source is one note: ingestion writes the document note and its atomic facts, the index cuts a long note into chunk vectors, and capturing the same source_id or source_url again updates the note in place. This reverses "Ingestion produces both forms of the article", whose chunk notes were the vector arm's only way into deep text. An idea that matches a card in personal/ideas/ is added to that card under ## Added by capture, replacing the ideas-ledger fold that retired with the ledger. A reply typed to the agent is dropped by reflection with a log line. The stale placement under personal/domains/<topic>/ is corrected to what ingest writes. Why not keep the chunk notes beside the index's chunk vectors: one article became about twenty-seven notes, and each chunk note competed with the document in recall. Re-audit trigger: dense recall on a long article falling after the chunk notes retire. |
final |
| 2026-09-06 |
Reconciled to the write path as it is, from session 2 of the vault-perfection series. The staging premise this design was written around β an _inbox/ folder, candidates hidden from recall, an automated triage system promoting, merging and expiring them β was replaced by filing v2 on 2026-09-04 and the retirement on 2026-09-06 left about twenty-six passages still narrating it. Every one is rewritten: a capture files at its class directory, status: unfiled until something judges it, visible to recall at reduced weight from the moment it lands; the review queue is a query, not a folder; the nightly enrichment batch is what judges the unfiled; reflection files HIGH candidates only and keeps MEDIUM and LOW in the session trace. The untrusted-content risk now states the posture that holds β there is no review window; the boundary is that captured content never gains instruction authority, that fetched and mailed transports carry trust: untrusted, and that recalled memory is data to the surface reading it β with a re-audit trigger on any injection incident. Two facts named rather than smoothed: the ingest sweep's ingest_staged status is walled in the Python recall arm and not in the Go ranker (filed in the follow-ups), and filing_confidence is read by no ranker, so nothing here implies it demotes. The candidate shape now points at AgentM Vault for the card and its why. The stale save_entry bypass rationale (kebab-case folder names) is gone with the folder it explained. |
launched |
| 2026-09-06 |
The inbox triage system retires; a memory_capture candidate stages nowhere now. inbox_triage.py walked memory/_inbox/, a directory filing v2 had already emptied β nothing wrote to it any more, so its promote/merge/expire had proposed nothing since. Its three jobs now have owners elsewhere: the heat policy, the dreaming binary's copies job, and the lifecycle axis. The Trust Boundary section above is corrected: a memory_capture candidate files directly to its class directory today, flagged filing_confidence: low (filing v2's write path), rather than staging behind an inbox-triage promote/merge/expire decision, and it is demoted in ranking rather than hidden outright. The Dependencies list drops inbox_triage accordingly, and the two Risks entries touching it are corrected. What is genuinely lost: nothing now detects a fuzzy-similar, fingerprint-distinct pair and parks it for you β the copies job only takes content-identical families, and the dedup stage only proposes a merge. Recorded as a capability, not just an implementation, so rebuilding it stays a decision someone can take (agentm v9.20.0, PR #575). Left deliberately untouched: the Objective, Overview, and Quality Attributes sections still narrate "the automated triage system" as ambient infrastructure in several places not tied to a single correctable claim β that's a wider reconciliation this entry flags rather than guesses at. |
launched |
| 2026-07-18 |
Ingest sweep ships (part 3 of 3): the automated phone path. harness/skills/memory/scripts/ingest_sweep.py is the hourly runner job that gives forwarded links and documents in _inbox/ full processing with no manual step β six duties in one job: fetch (or, for an already-complete Obsidian Web Clipper capture, skip straight to chunking), a closed deterministic act-step grammar (tag:/file-under: only β never free-text instruction execution), the idea-ledger fold, and a captured: timestamp re-stamp against the file's own creation time. Automated fetches land staged in place on the originating candidate β recall-invisible, exactly as before the sweep touched it β and only promote to permanent memory via ingest.ingest() (part 2, unmodified) after surviving one full sweep cycle; this staging/promotion mechanism is the sweep's own, distinct from inbox_triage.py's triage process that already handles memory_capture candidates (the Trust Boundary section above is corrected to name both mechanisms explicitly, replacing an overclaim of one uniform triage path). A pre-merge /review found and fixed five real bugs before the cut, the most consequential in this whole ladder: _vec_search/_vec_search_filtered (semantic recall) and find_drifted_entries never respected the _inbox exclusion the way keyword recall already did β a pre-existing gap in recall.py/vec_index.py, not introduced by this plan, but the first plan to put untrusted external content somewhere the gap mattered; closed by excluding _inbox from the drift walk and adding a _is_inbox_path() backstop on both vec-search result paths. The other four: vault_mutex protected only 1 of 7 read-modify-write call sites (closed β every candidate-patching function now re-reads fresh and writes inside the lock, narrowed to the minimum span); a body/marker collision could corrupt both the promoted document and the surviving candidate (closed with a matched start/end HTML-comment marker pair); permanently-failing promotions never surfaced in the digest (closed β promote_failures added to SweepResult); and an ordering bug left restamp comparing against the file's own just-updated mtime instead of the true original creation time (closed by reordering restamp first). Windows CI then caught a sixth, unrelated bug on the same PR: os.mkdir's concurrent-acquire race raises PermissionError on Windows where POSIX always raises FileExistsError β fixed in both vault_lock.py copies (top-level + vendored) per the DC-9 byte-identity convention. Two new how-to pages (Capture-From-Your-Phone, Ingest-An-Article β the latter closing a gap from part 2). governs: gains ingest_sweep.py. All three capture parts are now shipped β status flips final β launched, per this design's own Documentation Plan criteria. |
launched |
| 2026-07-18 |
Article ingestion ships (part 2 of 3): /memory ingest. harness/skills/memory/scripts/ingest.py reads a URL or a local file and writes it straight into permanent memory β one intact full-document note plus reading-order-linked chunk notes for retrieval via chunking.py's chunk_text(), both through save_entry() from the start (unlike /memory capture's _inbox/-only staging write). Omitting --topic returns a title-based suggestion and writes nothing until you confirm it β a real confirmation step, not an auto-accept. A new SKILL.md section documents invocation, flags, and failure modes; governs: gains ingest.py. A pre-merge /review β run before the PR this time, not retroactively β found and fixed two real bugs before the cut: a mid-sequence slug collision could leave the document note and every chunk written before it permanently orphaned on disk while the call still reported failure (closed with a pre-flight existence check across every target slug, plus a rollback that unlinks anything the call itself wrote on a later failure); and the HTML sniff only recognized a full-document wrapper (<html>/<body>/<title>), so an HTML fragment with no wrapper tag slipped through as plain text and left raw markup in a saved note (closed with a matching-tag-pair fallback scan). A third review finding β that writing straight to permanent memory here sits in tension with the design's own staged/triage trust model β is not a code defect; it is logged to the vault's FOLLOWUPS.md for an operator ruling before part 3 ships, deliberately left open rather than silently resolved. Parts 1 and 2 are now shipped; part 3 (forwarded links + the phone path) remains open β status stays final, not launched, until all three ship. |
final |
| 2026-07-18 |
Lifted from confidential (_harness/designs/friday/agentm-capture.md) to published, on landing capture-front-door (part 1 of 3). All 6 tasks shipped: the candidate contract's source_url/source_fetched provenance fields, the direct-atomic-write candidate writer, the memory_capture MCP tool (the fourth tool on the memory MCP server, alongside memory_search/memory_append/memory_forget β see agentm-memory-system.md's own amendment for the count correction), the /memory capture CLI verb + SKILL.md documentation, the capture-time-instructions security invariant (mechanically enforced and adversarially tested β content never gains instruction authority), and confirmation that this design's single-write-funnel already satisfies the F1-REAUDIT-named "E17 single-audited-bridge injection guard" (one audited bridge β capture() β between the trusted plane, operator-typed instructions, and the untrusted plane, content; both the MCP tool and the CLI verb funnel through the same function, no second write implementation). governs: stamped with the new/touched modules per the AG lift precedent. Parts 2 (article ingestion) and 3 (forwarded links + the phone path) remain open; status stays final, not launched, until all three ship. |
final |
| 2026-07-17 | Folded the cloud-capture exploration findings (cloud-capture-findings.md, tested end to end with the operator): the claude.ai Capture project + Google Drive connector becomes the primary phone/chat transport and the Obsidian Web Clipper the desktop-Chrome path; Dispatch/Remote Control demoted to secondary; Channels parked. The candidate contract gains kind/tags/instructions with the absorb-then-act rule (instructions only ever from operator capture-time text); the sweep gains the act step, the idea-ledger fold, clip-skips-fetch, and the captured: re-stamp; risks gain the connector's create-only ceiling and the model-clock finding. Same day, on operator feedback: Objective/Background/Overview de-staled (dropped transports no longer mentioned) and the Detailed Design reorganized into processing order β ways in β what arrives β how candidates are handled β the machinery β trust boundary β team-vault seam β where the memories live (with pointers to the memory-system design, the Kind-Taxonomy registry, and the idea-ledger design). Second feedback round the same day: the auto-organization design named wherever "a separate project" appeared; test-history narration removed from the body; "Mac" generalized to "your machine"; parked/dropped transport rows removed from the doors table (Alternatives keeps the rejections); ingestion now stores the full article intact as one document note AND chunks it for retrieval; automatic recall in later sessions referenced via the memory-system recall loop, with an arc-end test noted. Contents approved by the operator, then the two-step voice pass ran (Gemini 3.1 Pro via agy, sectioned to dodge a stream-truncation quirk on the literal <thought> token, Claude-verified) β its sentence-level polish adopted, ten guard-sentence leakage insertions and two wording drifts stripped in verification. Status advanced to review, and the operator approved the design as final the same day β unlocking translate and sequence. Later the same day, the FRIDAY-AGENTM build session ran /design translate, splitting the design into 3 parts (front-door, article-ingestion, phone-ingest-sweep; parts/ files at agentm-capture/parts/ alongside this doc) and /design sequence, landing three executable plans at _harness/queued-plans/PLAN-capture-front-door.md, PLAN-capture-article-ingestion.md, PLAN-capture-phone-ingest-sweep.md, each grounded against the live agentm codebase (file:line references, not paraphrased prose). One real design/code discrepancy surfaced during grounding, not silently resolved: the design states candidates are written "through the direct atomic-write path reflection already uses" β live-code inspection found reflect.py::_save_candidate_to_inbox writes via a raw write_bytes(), no vault_lock/mutex; the actually-atomic write-around-save_entry pattern in this codebase lives in inbox_triage.py's promote step instead. PLAN-capture-front-door.md's Constraints section records the call (default to genuine atomic writes via vault_lock.atomic_write, favoring the design's Data Integrity intent) and flags it for an explicit operator/build-time confirmation rather than assuming either reading silently. |
final |
| 2026-07-16 | Initial draft authored (F1 session) against the operator's goal rulings and confirmed adjudications (V9-after; #33β#34 post-arc); sources: the F0 surface audit re-verified at f3fd62a, the article-ingestion assessment memo, FRIDAY-PRE-VERDICT riders P2/P5, the standing privacy ruling. Same day: voice-conformance pass against the design-doc prose overlay (positive framing, volatile IDs replaced with plain names, fragments expanded, bold reserved), operator added the SLAs statement, and a first cross-model readability pass (Gemini via agy) was selectively applied; then, on the operator's direction, a full simplification pass (Gemini 3.1 Pro via agy, the voice pack inlined verbatim) was adopted wholesale with eleven fact-and-voice corrections (the save_entry bypass restored, the injection guarantee un-overclaimed, part sizes, release names, the health-family exit line, and the operator's own SLAs wording reinstated). Later the same day, operator rulings folded in: the Voice Memos/iCloud path cut entirely; forwarded links become the primary phone flow, fully processed by a new ingest sweep; the chunking boundary got one Overview sentence plus its own Detailed Design section; a from-your-phone walkthrough landed under Transports. Quality Attributes walked in full; Scalability, Abuse, Accessibility, i18n, and Compliance consciously omitted as low-relevance per the 2026-06-09 convention. |
draft |