Ctrl K
ComunidadGuías…

CLAUDE.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# CLAUDE.md

Quick-reference for AI assistants working in the Seamless repository. For coding
conventions see `AGENTS.md`.

## What is Seamless?

Seamless is a local-first agent memory and coordination substrate. Its clients are
AI agents (Claude Code and friends); the human is an observer/editor. It provides
persistent memory with a lifecycle (supersession, provenance, archival, a
gardener), ambient sessions via Claude Code hooks, a dependency-aware task
ready-queue, research trials, hybrid recall, and a read-mostly observability
console. Go backend, no CGO, single binary + CLI.

It is a ground-up rebuild of Seam v1 (a private predecessor, read-only
reference). As of the P6 cutover (2026-07-10) Seamless is the sole agent-memory system, serving on
port 8081 with data dir `~/.seamless` and env prefix `SEAMLESS_*`. v1 is
decommissioned (launchd services disabled, port 8080 freed) with `~/.seam`
preserved read-only as a fallback archive.

## Project structure

```
cmd/seamlessd/     server daemon: serve, doctor, export, import, install-hooks,
                   map-repo, unmap-repo, retire-project, family, console-open
cmd/seam/          headless CLI (agents + owner observability)  [P2/P5]
cmd/docsgen/       docs site generator: docs-src/ -> docs/docs/ (see SITE.md)
cmd/demoseed/      thin CLI over internal/demokit: the console-fleet seed
                   (branding screenshots) and `-scenes` (the shared fixture)
cmd/seambench/     agent-scenario benchmark: `run` (headless scenario x condition
                   matrix) + `report` (grade, uplift, version deltas). Driven by
                   `make seambench`; see cmd/seambench/README.md
docs-src/          docs site markdown + nav.yaml (source; docs/docs/ is output)
internal/core/     domain types: Project, Memory, Session, Task, Trial, Event
internal/config/   single YAML + env config (static key, budgets, briefing tunables, llm,
                   peer_auth)
internal/access/   peer_auth policy: who a request speaks for (the owner on TCP or the
                   Unix socket, a restricted local account named by its kernel uid)
                   and which projects it may reach (View); leases on locked
                   projects, judged per request; Unix-socket peer credentials.
                   Foundation layer
internal/passkey/  WebAuthn passkey ceremonies over go-webauthn (approver passkeys
                   for locked projects); stateless, imports nothing internal
internal/approval/ approver credentials for locked projects: passkey and Touch ID
                   enrollment over store/approvers.go (migration 032)
internal/store/    SQLite: schema, FTS5, embeddings (BLOB + cosine), migrations;
                   index_work.go mirrors the DB-native work record (tasks,
                   trials, session findings) into the same fts table the files
                   layer fills with memories and notes
internal/events/   append-only event log; SSE fan-out; retrieval stats
internal/validate/ path/title/name guards
internal/agentproc/ names the agent process (claude/codex) that launched a seam
                   process: the identity the session-start hook stamps and the
                   MCP bridge sends, which binds a connection to its session
internal/files/    markdown layer: frontmatter, atomic writes, watcher     [P1]
internal/markdown/ agent markdown -> sanitized console HTML (goldmark + bluemonday)
internal/llm/      OpenAI (default), Ollama, Anthropic chat + embeddings   [P1]
internal/retrieve/ briefing assembler, prompt-context matcher, recall (RRF),
                   console search                                          [P2/P4]
internal/lifecycle/ supersession, archival, provenance                     [P3]
internal/screening/ write screening (optional feature, off by default): the
                   screens, the hold policy, the held-writes queue the
                   console releases or discards, the corpus review, drift
                   quarantine, lockdown and access.refused; see AGENTS.md
internal/feed/     write screening's metadata feed: event log -> metadata
                   frames -> on-disk spool -> a configured Unix socket
internal/commitfeed/ commit records' snapshot (the commits: option, off by
                   default): sessions, tasks and the commits they recorded ->
                   one JSON file an integrity monitor matches commits against
# (the dependency-aware ready-queue + lease-based claiming live in
#  internal/store/tasks*.go -- there is no internal/tasks package)
internal/gardener/ the propose-only passes (dedup, staleness, stale-stage,
                   dead-weight, digest, stale-plan, memory-wanted -- the
                   canonical list is RunOnce), plus the request- and
                   split-driven proposals                                  [P4]
internal/plans/    captured CC plan vocabulary: tags, statuses, tracking task
internal/mcp/      MCP tools (streamable HTTP, static bearer key or the peer_auth
                   socket); see ToolCount [P2+]
internal/hooks/    session hooks + CC plan-mode capture (PostToolUse etc.) [P2/P3]
internal/console/  server-rendered observability UI (html/template + SSE)  [P5]
internal/capture/  SSRF-safe URL fetch                                     [P4]
internal/update/   update check + automatic updates: GitHub release list,
                   install-kind detection, Target/Decide policy, attempt-record
                   fold, <data_dir>/update/state.json, the Status every surface
                   renders (briefing notice, console, doctor, /healthz). Never
                   installs in-process: the daemon spawns a detached `seamlessd
                   update --auto` (cmd/seamlessd/update_spawn*.go) to run the
                   release's installer. Imports config + core only
internal/releasesig/ Sigstore check that a release asset came from this repo's
                   release workflow on its exact tag (pinned trusted root,
                   offline): the updater's, and the macOS seam's for the Touch
                   ID approver download (linked there from a darwin-only file)
internal/archive/  instance archives: VACUUM INTO snapshot + corpus + manifest
                   out; guarded tar extract and restore-or-merge back in.
                   Never imports config
internal/importer/ one-shot import of the v1 (~/.seam) snapshot
internal/demokit/  importable fixture-seeding primitives + the demo spec sets;
                   the shared seam under BOTH branding and the benchmark
internal/bench/    agent-scenario benchmark: scenario table, condition arms,
                   graders (repo assertions + event log + optional LLM judge),
                   results/uplift aggregation, the on-disk artifact contract
scripts/fixture/   the shared throwaway fixture: make-myapp.sh (demo repo) +
                   harness.sh --mode record|bench (branding | benchmark arms)
scripts/branding/  GH-Pages surface: record.sh, distill.py (verbatim-audited
                   scene data), console-shots.js -- see its README.md
macos/SeamApprove/ the optional macOS Touch ID approver app (Swift/SwiftPM, not
                   part of the Go build): SeamApproveCore + the AppKit
                   executable; scripts/approver/ builds (make approver) and
                   notarizes it
```

Bracketed tags mark the phase that introduces the package; unbuilt ones do not
exist yet.

## Common commands

```bash
make build      # ./bin/seamlessd + ./bin/seam (touches nothing live)
make install    # build + snapshot binaries/config to ~/.local/bin +
                # ~/.config/seamless, repoint the service and hooks, restart.
                # Refuses on a machine the curl installer set up (its service
                # file carries the installer's marker) unless FORCE=1
make test       # unit tests
make test-race  # unit tests with the race detector
make check      # the full gate: build + vet + cross-check (darwin/windows
                # vet) + fmt-check + docs-check + installer-check +
                # site-check + lint + vulncheck + test-race
make check-fast # the pre-commit subset (no build/vulncheck/test-race);
                # .githooks/pre-commit runs it
make lint       # golangci-lint
make vet        # go vet
make fmt        # gofmt tracked files -- NOT `gofmt -w .`, which rewrites other
                # agents' worktrees under dot-dirs that go's ./... skips
make run        # build + serve on 127.0.0.1:8081
make doctor     # build + config/DB self-checks
make console    # open the console in a browser, pre-authenticated
make clean      # remove bin/ and coverage files
make approver   # macOS: the Touch ID approver app into bin/approver/ (ad-hoc
                # signed unless APPROVER_* is set; see scripts/approver/)
make approver-test # its Swift unit tests (no keychain, no prompt)

make docs       # regenerate the docs site (docs-src/ -> docs/docs/, committed)
make docs-check # fail if the committed docs site is stale (runs inside `check`)
make docs-serve # regenerate + serve the site at 127.0.0.1:8899/docs/
make site-check # fail if the hand-written landing page (docs/index.html) drifts
                # from the installer or the CLI -- docs-check cannot see it
make indexnow   # submit the sitemap's URLs to the IndexNow engines (manual,
                # run after a deploy is live; DRY=1 previews the payload)
make metrics    # GitHub stars + release-asset download counts (needs gh)

# single test
go test ./internal/validate -run TestTitle -v

# Two different things called "benchmark". Do not conflate them:
make bench      # Go hot-path MICRO-benchmarks (ns/op, offline, free)
make seambench  # the AGENT-SCENARIO benchmark: runs a real `claude` over the
                # seeded fixture in each condition arm (vanilla/mechanism/full),
                # grades it, and prints with-vs-without uplift + version deltas.
                # SPENDS REAL API TOKENS; needs a `claude` binary + credentials.
                # MANUAL ONLY -- deliberately not in check, check-fast, the
                # pre-commit hook, CI, the release flow, or any schedule.
                # N=5, BASELINE=<ref>, SCENARIO=, CONDITIONS=, OUT=,
                # RUN_ARGS=, REPORT_ARGS=--no-trials. See cmd/seambench/README.md
```

## Tech stack

| Layer | Choice |
|---|---|
| Language | Go 1.26+ (no CGO) |
| Database | SQLite (`modernc.org/sqlite`, WAL, FTS5) |
| Vectors | float32 BLOBs in SQLite + brute-force cosine (no ChromaDB) |
| LLM/embeddings | OpenAI (default, first-class), Ollama, Anthropic |
| IDs | ULID (`oklog/ulid/v2`), never UUID |
| Auth | single static bearer key, localhost bind (no JWT); optional peer_auth Unix socket that names each caller by its kernel-reported uid |
| Console | `html/template` + vanilla JS + SSE (no node/React) |
| Tests | testify/require, table-driven, fresh/in-memory SQLite |

## Configuration

Single YAML file (`$SEAMLESS_CONFIG`, `~/.config/seamless/seamless.yaml`, or
`./seamless.yaml`; see `seamless.yaml.example` for every key) with `SEAMLESS_*`
env overrides -- env wins over file, file over defaults. The `briefing:` block
tunes what the SessionStart `<seam-briefing>` auto-injects (section counts,
recency filters, family cross-over, hard-cap multiplier, and the
utility-vs-recency index order via utility_mode/utility_weight); those knobs are also
editable live in the console (Settings -> Briefing injection), which stores a
runtime override in the DB that wins over file/env until reset and applies from
the next session start without a daemon restart. `gardener.session_idle_minutes`
is the single live/idle threshold shared by the session reaper, the console and
automatic updates' idle gate. The `update:` block (check, check_interval, auto,
max_defer, min_age) drives the update check and automatic updates; its console
override (Settings -> Updates) merges the other way round: `check: false` or
`auto: false` in the file/env is final and locks its toggle (false check means
no update traffic at all). Durations are `config.Duration` ("6h"; a bare
number other than 0 is refused). The `peer_auth:` block (off by default; see
docs-src/guides/peer-auth.md) adds a Unix-socket listener whose callers are
named by the kernel's peer uid, locks the projects it lists until the owner
approves an agent's access, and lists restricted local accounts; a client of it
sets `server_url: unix:///path.sock` and needs no key.
The `commits:` block (off by default; docs-src/guides/security-monitor.md) offers
session_end and tasks_update a `commits` argument (`record`) and writes the
commit-matching snapshot to `snapshot_file`.

## Storage layout

```
~/.seamless/
  seam.db                          SQLite: indexes, sessions, tasks, trials, events, embeddings, ...
  seamlessd.lock                   serve's one-daemon-per-data-dir lock (holder's PID; never delete it)
  memory/{project|_global}/{name}.md   one memory per file (source of truth)
  notes/{project|_global}/{slug}.md    work artifacts (source of truth)
  backups/pre-update-v*.tar.gz     the updater's pre-update archives (newest 2)
  update/state.json                the update check's record (release daemons only)
  update/update.lock               the updater's lock (never delete it)
  update/attempt.json, attempts.jsonl, logs/   the updater's records (it writes, the daemon reads)
  feed/                            write screening's metadata-feed spool (only with screening.feed_socket set)
```

Files are the source of truth for durable knowledge; the DB is the record for
high-churn state (sessions, tasks, trials, events, telemetry) and a rebuildable
index for the files.

Under `peer_auth` the data dir belongs to the daemon's own OS account instead
(the guide's layout: `/opt/seamless/data`, 0700), and serve refuses to start
when it, the socket's directory, the config file or the binary could be
rewritten by another account.

## Memory frontmatter

```yaml
---
id: 01K...                 # ULID
kind: gotcha               # constraint|convention|runbook|protocol|gotcha|decision|refuted|reference|stage
name: chroma-boot-race
description: one line, <=150 chars -- the ONLY text shown in indexes
project: seam              # empty = global
created: 2026-07-10T18:00:00Z
updated: 2026-07-10T18:00:00Z
valid_from: 2026-07-10T18:00:00Z
invalid_at: null           # set on supersession/archive; invalid memories leave indexes
superseded_by: null        # ULID of the replacement
source_session: 01K...     # the writing session's ULID (not its cc/... name)
model: claude-fable-5      # the model that produced the content, verbatim as the provider names it
tags: [x, y]
---
body markdown
```

## MCP surface

The authoritative count is `internal/mcp.ToolCount` (asserted by doctor and by
`catalog_test`); `mcp.Catalog()` is the same list as data, and the docs site
renders its reference from it. Do not transcribe a number here -- three different
stale counts is exactly how this section drifted before.

sessions (`session_start/update/end`), memory (`memory_write/append/read/delete`,
write carries `supersedes`), discovery (`recall` -- the only search tool,
RRF-fused, spanning durable knowledge AND the work record: memories, notes,
tasks, trials, session findings),
notes (`notes_create/read/update/append/delete`), projects (`project_list/create`),
tasks (`tasks_add/update/ready/list` -- `tasks_list id=<id>` loads a single task
by its globally-unique id -- plus `tasks_claim`/`tasks_release` for
atomic lease-based claiming), research lab (`lab_open`, `trial_record`,
`trial_query`), gardener (`gardener_proposals`, `gardener_apply`, plus the
natural-language `gardener_request` and the project-split planner
`gardener_split` -- both LLM-backed, and both only ever propose), utility
(`capture_url`, `usage_summary`, `favorite_set` -- star/unstar any entity;
starred memories pin into briefings and boost recall). Project and session are inherited from the
session binding; agents in mapped repos rarely pass `project` explicitly. A
Claude Code or Codex agent's connection is bound to its own ambient session
automatically (the agent-process identity, `internal/mcp/agent_binding.go`), so
those agents never need `session_start`.

## Plans as composition

A "plan" is not a primitive -- it is a composition keyed by `plan:<slug>`:

- **Narrative + supporting knowledge**: one primary note, plus any further notes
  tagged `plan:<slug>`, so the next agent inherits the design and its supporting
  context. `notes_create` already accepts `tags`.
- **Steps**: tasks created with `plan:<slug>` (`tasks_add plan=...`). Plan-step
  tasks are excluded from the default `tasks_ready`/`tasks_list` (no queue
  pollution) and surfaced with `plan=<slug>`.
- **Claiming**: `tasks_claim` atomically moves a ready task to `in_progress` with
  a lease (default 900s); re-claiming refreshes the lease (heartbeat); an expired
  lease is reclaimable. `tasks_release` (or closing the task, or `session_end`)
  frees it. The briefing surfaces each active plan as a `PLAN: <slug> -- ...` line.

Claude Code plan mode feeds this automatically (`internal/hooks` capture +
`internal/plans` vocabulary): plan-file saves upsert a `cc-plan-<basename>` note
(`plan-status:draft|presented|approved|abandoned|shipped` tag -- the last two
are the gardener's stale-plan settlements: shipped when git evidence shows the
work landed unapproved, abandoned otherwise), planning subagents cache
as `cc-agent-<id>` notes in the composition, approval creates the tracking task,
and the briefing lists unapproved captures as `PLAN (awaiting approval)` lines.
Owner surfaces: `/console/plans` and `seam plan list|show|check|approve` (check =
git-stamp staleness; approve = escape hatch when CC skips the approval hook).

## One fixture, two workflows

`scripts/fixture/harness.sh` stands up the shared throwaway fixture -- the
`myapp` demo repo plus a seeded Seamless instance on a non-live port, never the
live `~/.seamless`/`~/.claude`/`~/.codex`. Above that seam the two halves are
independent and non-interfering:

- **Branding** (`--mode record`): record interactive with/without takes, distill
  them **verbatim** into `docs/static/scenes.js`, publish. Entry point
  `scripts/branding/record.sh`; recipe in `scripts/branding/README.md`.
- **Benchmark** (`--mode bench`): build the `vanilla`/`mechanism`/`full`
  condition arms, run scenarios **headless**, grade, report uplift. Entry point
  `make seambench`; recipe in `cmd/seambench/README.md`.

The demo repo must contain zero Seamless vocabulary (memory
`scene-demo-repo-must-be-seamless-free`), or the without/vanilla side reads its
way out of character. Grading discipline for benchmark code is in `AGENTS.md`.

## Working here

- Conventions live in `AGENTS.md`. Read it before writing code.