A focused truth + packaging pass before the website sprint — the product
moved a lot since the last docs sync (local-first SQLite, quickstart,
doctor, attached-repo no-arg workflow, repo-state drift detection,
deletion safety).
README: refresh the stale "Built with Smriti" metrics to current numbers;
add repo-state drift detection and destructive-action guards to the
coordination/trust surfaces; add an explicit no-arg daily-workflow block
so the new-user path (setup -> doctor -> quickstart -> attach -> no-arg
state/current/metrics) routes cleanly.
ARCHITECTURE: correct the /health capabilities list (add
activation_health); document repo-state drift detection.
DECISIONS: record why drift detection is CLI-side and why destructive
Space deletion is gated, not prevented.
REPO_STRUCTURE / CONTRIBUTING: correct the CLI command list (add attach,
quickstart; 16 commands) and the skill-pack template version (2.4).
cli/README.md was already current and is unchanged. The agent skill pack
still teaches manual reconciliation and is deliberately left for its own
versioned follow-up pass.
The repo-state section added by the previous commit shows working-tree
and git-upstream drift. It does not answer the question Smriti's trust
story actually needs: has the repo moved since the last checkpoint?
Record git provenance on checkpoints and compare against it. The CLI
captures the git HEAD and branch when it creates a checkpoint; the V4
commit endpoint stores that under the commit's context_blob and returns
it on read. `smriti state` then compares the working repo against the
latest checkpoint's recorded HEAD/branch and surfaces:
- repo unchanged since the last checkpoint
- repo is N commit(s) ahead — recorded state may be stale
- repo history has diverged from the last checkpoint
- the last checkpoint was taken on a different branch
All local and read-only: no fetch, no reconciliation. Checkpoints made
before this feature (or by the MCP server) record no git state and are
simply left uncompared.
The backend now refuses DELETE /api/v2/repos/{id} with 409 for a space
that still holds checkpoints unless force=true is passed. The web UI's
delete-space request did not send it, so deleting a non-empty space from
the browser failed (it failed safe — nothing was deleted).
deleteRepo() gains an optional { force } argument, mirroring the sibling
deleteCommit()'s { cascade }. WorkspaceOverviewPage passes force=true from
the ConfirmDeleteModal's onConfirm — the point where the user has already
acknowledged the irreversible "this cannot be undone" confirmation. No
modal or UX change; only the request now carries the flag the backend
requires.
The deletion-safety thread guarded the CLI (`--force`) and the MCP tool
(`confirm_space`), but `DELETE /api/v2/repos/{id}` itself still cascade-
deleted a fully populated space — every checkpoint, session, and turn —
with no server-side check. Any client that bypassed the CLI/MCP guards
(a direct API call, the web UI, a future client) reopened the incident
path.
Gate the route: `delete_repo` now counts the space's checkpoints and
refuses with 409 plus a structured detail (`message`, `checkpoint_count`,
`requires_force`) unless `?force=true` is passed — the same shape as the
`checkpoint delete` dependent-guard. An empty space still deletes with no
force.
Wire the existing clients through the new signal so their behavior is
unchanged: the CLI passes `force=args.force` (its own `--force` gate),
the MCP tool passes `force=true` after its `confirm_space` gate, and
quickstart passes `force=true` to tear down its own demo space.
A destructive-operations incident: `smriti space delete <space> -y`
cascade-deleted a fully populated space. `-y` skipped the only gate (the
confirmation prompt), and `cmd_space_delete` never checked how much the
space held — so one flag irreversibly deleted the space and every
checkpoint, session, and turn under it.
Gate destructive deletes in `cmd_space_delete`: a space that holds
checkpoints, or that the current repo is attached to (.smriti.json), now
requires an explicit `--force` — `-y` alone is refused, with a message
naming the reason and the flag. `--force` and `-y` stay orthogonal, the
same shape as `checkpoint delete --cascade`. Empty, unattached spaces keep
the existing `-y` convenience. CLI-side only; no backend change.
Smriti was repo-local wiring: every command needed an explicit <space>, and
the SessionStart hook hard-coded the space name, so working across projects
and sessions meant re-stating the space constantly.
Add a `.smriti.json` attachment file at the repo root binding the repo to a
space. The CLI resolves `<space>` from it — walking up from the working
directory the way git finds `.git` — so `<space>` is now optional on the
everyday commands (state, current, metrics, claim, checkpoint, branch,
worktree). `smriti attach <space>` is the explicit binding verb (`smriti
attach` with no argument shows the current binding); `smriti init` also
writes the attachment. The generated SessionStart hook is now space-agnostic
— `smriti state --compact` resolves from the attachment, so the hook is
identical for every project and survives re-attaching.
`space delete` still requires an explicit space. No backend changes —
attach/init reuse the existing set_project_root endpoint.
`smriti current` already normalizes a list-valued blocked_by to a clean
comma-separated string via the backend CurrentTask validator, but `smriti
state` rendered it through an f-string and leaked the raw Python list repr
(['a', 'b']) into the state brief — the surface agents read at session start.
Add a `_coerce_blocked_by` helper in the CLI formatters mirroring that
validator, and apply it in `_normalize_task_item` so every `smriti state`
task line renders blocked_by the same way `smriti current` does. Adds
regression tests.
External-machine validation found GET /api/v5/current 500s on a real
project: a structured task can carry `blocked_by` as a list of dependency
labels, but CurrentTask.blocked_by is typed Optional[str], so constructing
the model raised a Pydantic ValidationError.
Add a field validator that normalizes `blocked_by` — string, list, or null —
to a single display string. The API contract is unchanged (blocked_by stays
a string), so the CLI and the Project Current State UI panel, which both
read this endpoint, need no change. Adds a regression test.
The 3-way merge auto-applied both Claude's and Codex's MCP-config intros
without flagging a conflict, leaving two paragraphs that both say "smriti
init prints the config." Collapse them into one — keeping the quickstart-first
onboarding wording and folding in Codex's `which smriti-mcp` manual-config tip.
An external-machine validation pass confirmed setup, local-first, doctor,
quickstart, and init all work — but the README still routed a new user
straight to `smriti init`, which opens an empty space where Smriti's value
is not yet visible.
Restructure the "Getting started" flow: a new step 3 runs `smriti doctor`
then `smriti quickstart` so the first thing a user sees is a populated demo
space. The init step now says to cd into your own project first; the MCP
guidance defers to the resolved config `smriti init` prints rather than a
bare `smriti-mcp`; mock-mode limits and "Try the demo" are clarified to match.
A fresh install opens to an empty space, so Smriti's value — which only
shows once reasoning has accumulated — is invisible on day one. `smriti
quickstart` seeds one small, finished project (a rate-limiting feature
built by two agents, with a branch explored and dropped) and prints a
short guided walkthrough.
The demo space `smriti-demo` carries a marker in its description: --remove
only deletes a marked space, --reset rebuilds it, and seeding rolls back a
half-built space on failure. The fixture is plain structured data with
content-integrity tests guarding counts, intent types, note kinds, the
demo marker, and branch divergence.
Packaging/readiness pass — documentation only, no feature or behavior change.
- REPO_STRUCTURE.md, CONTRIBUTING.md: corrected test counts (156 integration,
133 unit, 151 CLI), local-first setup flow (make setup-local / dev-local),
command count, new routes/types/test files.
- README.md: refreshed dogfood metrics, added Project Current State and
smriti doctor to the surfaces, SQLite to the tech stack.
- cli/README.md: MCP tool count 17 -> 21 (added the four worktree tools),
documented the smriti worktree commands.
- ARCHITECTURE.md: API table now lists the Project Current State and metrics
endpoints; new Database modes section (local SQLite / Postgres).
- DECISIONS.md: recorded the local-first SQLite mode and Project Current
State surface decisions.
- AGENTS.md: regenerated from skill pack template v2.3 (was a stale v1.5
render); the .claude/ claude-code render was likewise refreshed locally.
.env.example, docs/DEMO_SCRIPT.md, and CLAUDE.md were reviewed and left as-is.
Smriti can now run against a file-backed SQLite database with no Docker
and no Postgres, removing the operate-a-backend burden for solo builders.
Postgres remains the stronger shared/team mode, unchanged.
- Portable column types (app/db/types.py): JSON renders as JSONB on
PostgreSQL and generic JSON on SQLite; the pgvector embedding column
renders as JSON on SQLite. Same models and create_all on both backends.
- Mode resolution (config.py): SMRITI_DB_MODE=local|postgres, defaulting
to local when unconfigured. An explicitly-set Postgres DATABASE_URL
preserves Postgres behavior, so existing setups are unaffected. Local
DB defaults to ~/.smriti/smriti.db; SMRITI_LOCAL_DB_PATH overrides.
- SQLite engine setup (database.py): check_same_thread plus foreign_keys
/ WAL / busy_timeout pragmas; a lazy first-run create_all bootstrap on
first DB use, which keeps the integration test suite insulated.
- Removed the integration-test JSONB/VECTOR DDL substitution hack — the
models are genuinely portable now, so conftest needs no type patching.
- Alembic resolves its URL through the same logic (Postgres mode only).
- New persistent file-backed SQLite smoke test, plus mode-resolution and
per-dialect type-rendering tests.
Local mode uses create_all, not Alembic. No schema changes.
Type the commit metadata blob — new Note and CommitMetadata interfaces
in types/index.ts — so commit.metadata.notes resolves to Note[] instead
of unknown. This fixes the three CommitDetailPage errors where .length,
.map, and the JSX child type all failed against an unknown/{}-typed
notes field.
Normalize the structured-task list to strings at the rows() call in
ChatWorkspacePage so the string-list renderer receives string[] rather
than (string | StructuredTask)[].
No behavior change. npm run build (tsc -b + vite build) and tsc --noEmit
are now clean.
Add GET /api/v5/current/spaces/{id} — a packaged, computed-on-demand
snapshot of where a space is right now: current direction, counts,
attention signals (open questions, divergence, active work), active
work claims, open tasks grouped by intent, recent milestones, and
recent activity. Shared payload contract with the smriti current CLI
surface built in parallel.
Render it as the ProjectCurrentState panel at the top of LineagePage,
replacing the hand-rolled current-state summary. Extract a shared
_get_active_claims helper so the state and current endpoints report
active work identically. No schema changes.
CONTRIBUTING: fix stale test counts (177 integration / 97 unit / 70 CLI
became 122 / 125 / 122 across V1+V2 work).
DECISIONS: expand the V2 worktree rationale section. The V2 PR shipped
a single paragraph on "why binding is optional"; this adds the broader
design rationale that motivates the primitive itself — the chaanbeen
catastrophic failure mode, why git worktrees vs separate clones, why
state-brief shells out to git per request rather than persisting drift,
why the probe cache is 60s. Existing "binding is optional" paragraph
stays, now flowing naturally from the expanded context.
Done in worktree ~/.smriti/worktrees/smriti-dev/claude-code-555d3005
on branch smriti/claude-code/555d3005 in parallel with Codex's
worktree-list-probe work (V2 dogfood test).
V1 mentioned that claim binding and state-brief enrichment would land
in V2. V2 is now on main (39003a8), so update the bullet to describe
the actual current behavior: bind a claim to a worktree, see drift
in state brief, skill pack v2.0 teaches the reflex. Tool count
unchanged at 21 (V2 added params to existing tools, not new ones).
- README: 17 → 21 MCP tools, add worktrees bullet to coordination
primitives (notes V1 = CRUD primitive only; binding + state-brief
enrichment deferred to V2)
- CONTRIBUTING: 17 → 21 MCP tools
DECISIONS.md and skill pack template are intentionally not touched —
the V1 prompt explicitly deferred those to V2 so the rationale and
skill pack reflexes can be written together with the actual usage
patterns we'll learn from V2 (claim binding + state brief).
Adds WorkTree schema and migration, /api/v5/worktrees CRUD, CLI and MCP worktree surfaces, targeted regression tests, health capability, and minimal docs. Live Postgres migration/manual localhost verification intentionally remain pending until the full backend provider-config gate is resolved.
Captured three screenshots for the README using playwright against
the live local environment:
- lineage-dashboard.png — real LineagePage showing 56 checkpoints,
summary panel, needs-attention signal, author badges
- checkpoint-detail.png — the autonomy milestone checkpoint with
structured tasks (intent badges), entities, and founder notes
- cli-state-and-metrics.png — terminal-styled render of real CLI
output from smriti state --compact and smriti metrics
Fixed a crash in CommitDetailPage exposed by the screenshot attempt:
diffList was typed list[str] but tasks are now structured objects
after the task IDs feature landed. React threw "Objects are not
valid as a React child" when trying to render the diff result.
Fix normalizes tasks to their text field before diffing.
README now references real images instead of inline code blocks.
TypeScript clean, 331 tests pass.
The 3 screenshot placeholders pointed at files that don't exist yet,
which would render as broken images on GitHub. Replaced with inline
code blocks showing real CLI output (smriti state --compact and
smriti metrics) — these render cleanly now and can be swapped for
actual screenshots later when captured.
Removed stale Loom link and old checkpoint-diff screenshot reference.
README now references 3 new screenshots matching the current product:
LineagePage dashboard, CLI state+metrics, checkpoint detail with
structured tasks and notes. Screenshots are placeholders until
captured from the live environment.
Added docs/DEMO_SCRIPT.md: 3-act, 4-5 minute recording script
covering the state surface, coordination proof (metrics + autonomy
milestone), and developer experience. Includes exact commands,
highlight points, and a screenshot capture checklist.
Restructured from single-user chat-drift framing to multi-agent
coordination as the primary story. Leads with the problem (agents
have no shared state), the solution (structured reasoning-state
backend), and the proof (56 checkpoints, 30 cross-agent handoffs,
autonomous complementary task selection). Single-user value prop
preserved as a secondary section. All claims grounded in current
reality — no future hype, no unqualified comparisons.
Three integration tests covering the recheck pattern:
- Duplicate task_id claims both visible (advisory, not rejected)
- Different task_id claims show no collision
- Full pivot flow: detect collision, abandon, re-claim on different task
Single nullable VARCHAR(100) column addition. Applied and verified
against the real local Postgres: claims with task_id round-trip
correctly, claims without task_id work unchanged.
Tasks gain an optional id field (short slug like "impl-1", "docs-arch").
Claims gain an optional task_id field referencing a specific task. The
state brief shows (id: X) on tasks and (task: X) on claims, making it
precise which task a claim covers.
Skill pack v1.9 teaches the recheck pattern: after creating a claim,
re-read state to detect if another agent claimed the same task_id in
the race window. If collision detected, abandon and pivot.
This solves the near-simultaneous start problem from the autonomy
validation where both agents picked [docs] because claims had no
task-level identity.
Cherry-picked the non-redundant section from Codex's
codex/docs-task-claim-affinity-rationale branch: "Why structured
tasks reuse the claim intent vocabulary." Dropped Codex's
open/done-status section (redundant with existing coverage).
The three structured-task decision sections now flow as:
reuse vocabulary → instead of task manager → open/done only