d257c0fd67d96a073c92b84884eccb39c40ac7e9
1016 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
d257c0fd67 |
feat(scribe): snippet management UI + REST routes; embed snippets on create
CI & Build / Python lint (push) Successful in 3s
CI & Build / integration (push) Successful in 23s
CI & Build / TypeScript typecheck (push) Successful in 33s
CI & Build / Python tests (push) Successful in 48s
CI & Build / Build & push image (push) Successful in 1m4s
Step 6 of the Drafter recall milestone (#227): a human-facing surface for the reusable-code snippets that agents record via MCP, plus the REST API behind it. Backend embeds snippets inline on create/update so they're recallable immediately, not only after a restart. Backend: - routes/snippets.py: GET/POST /api/snippets, GET/PATCH/DELETE /api/snippets/<id>. Share-aware per rule #78 (get_note_for_user + can_write_note), writes performed as the owner; list owner-scoped — mirrors routes/notes.py. Registered in app.py. - services/snippets.py: embed on create/update via a _embed_snippet fire-and-forget helper, covering BOTH the MCP tool and the REST route by construction. A snippet's value is immediate recall, so it can't wait for the startup-only backfill (see issue: MCP create path doesn't embed inline for notes/tasks generally). - tests/test_routes_snippets.py: structural registration + handler/service contract + PATCH-field ↔ update_snippet-kwarg parity (rule #33). Frontend (Vue 3 + TS): - api/snippets.ts: typed client, modeled on api/systems.ts. - views: SnippetListView (search, skeleton/empty/error states), SnippetDetailView (read + copy-to-clipboard, ConfirmDialog delete), SnippetEditorView (create/edit all fields, Ctrl/Cmd+S, Esc, autofocus, validation). v1 quality per rules #24/#27. - router: /snippets, /snippets/new, /snippets/:id, /snippets/:id/edit. - NoteType union widened to include 'snippet'; Snippets nav link added to AppHeader (desktop pill bar + mobile menu). - Design system: Moss --color-action-primary for action buttons, accent --color-primary reserved for tags/brand (Hybrid rule); focus rings; JetBrains Mono for code/name/signature/location. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pa2EsuB54BuWQ8GfJq9c7t |
||
|
|
0ea3bff797 |
feat(scribe): snippet recording nudge — reusing-code skill + MCP/SessionStart guidance
CI & Build / Python lint (push) Successful in 3s
CI & Build / integration (push) Successful in 31s
CI & Build / TypeScript typecheck (push) Successful in 33s
CI & Build / Python tests (push) Successful in 55s
CI & Build / Build & push image (push) Successful in 1m10s
Step 5 of the Drafter recall milestone (#227): teach agents the two snippet reflexes — search recorded snippets before writing a new helper/util/component, and record something reusable the moment it's built — via the app's own instruction surfaces, not a Scribe rule (project rule #119). All instance-agnostic (rule #115). - plugin/skills/reusing-code/SKILL.md: new auto-surfacing process-skill covering both reflexes (recall-before-rebuild + record-when-reusable). - src/scribe/mcp/server.py: a Snippets paragraph in the MCP _INSTRUCTIONS. - plugin/hooks/scribe_static_context.md: a "reuse before rebuilding" bullet in the SessionStart static context. - plugin/.claude-plugin/plugin.json: version 0.1.12 -> 0.1.13 in the same change so the autoUpdate marketplace ships it (the #1040 lesson); description skill list updated. - plugin/README.md: trued the process-skill list to what actually ships. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pa2EsuB54BuWQ8GfJq9c7t |
||
|
|
1942913366 |
feat(scribe): add snippet recall — note_type='snippet' service + MCP tools
CI & Build / Python lint (push) Successful in 2s
CI & Build / integration (push) Successful in 20s
CI & Build / TypeScript typecheck (push) Successful in 33s
CI & Build / Python tests (push) Successful in 50s
CI & Build / Build & push image (push) Successful in 1m7s
Record reusable functions/components once so they surface via the existing semantic search + title-first auto-inject, instead of re-solving as one-offs. A snippet is a Note with note_type='snippet' (no schema change): note_type is free-text, and semantic_search_notes never filters by type, so snippets join the recall/auto-inject pool the moment they're embedded. Structured fields (name/language/signature/location/when_to_use/code) are stored via a body convention — title = "name — when to use" (what auto-inject surfaces), language + "snippet" as tags, templated markdown body — keeping storage swappable later without changing the tool/UI contract. - services/snippets.py: compose/parse helpers + create/get/list/update wrappers over notes_svc (dedup + System association reused). - mcp/tools/snippets.py: list_snippets / create_snippet / get_snippet / update_snippet, registered in tools/__init__.py. - unit tests for the serialize/parse round-trip and the MCP tool surface. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pa2EsuB54BuWQ8GfJq9c7t |
||
|
|
d2f08d6113 |
docs(scribe): rewrite features/api-reference/README to the current product
Resolves issue #1778 — the docs still described the pre-pivot product (in-app LLM chat, journal, weather, web-research, push, model management) plus the just-removed calendar/entities. - features.md: full rewrite to the actual surfaces — notes, tasks & issues, projects/milestones (kanban), systems, rules & rulebooks, stored processes, search + knowledge-injection, graph, MCP + the Claude Code plugin, sharing, export/backup (v4), OIDC. Dropped chat/journal/weather/web-research/calendar/ push/workspace/model-management; fixed the shortcuts + settings tables. - api-reference.md: rewrite to the real endpoint surface (verified from the route decorators) — added Knowledge/Rulebooks/Systems/Plugin/Trash/Dashboard and the milestone/system sub-routes; removed Chat/Journal/Push/Quick-Capture/ Images/assist/models. - README.md: Quick Start no longer tells users to pull an Ollama model or size RAM/GPU "for LLM inference" — points at the API key + plugin instead. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BPtbSzA4JLMAKgFZ8VTg7Q |
||
|
|
ab9b5b647c |
docs(scribe): true up plugin + MCP docs after calendar/entity removal
Milestone #194 plugin/docs slice. - plugin.json: "second brain" framing -> "system-of-record"; bump 0.1.11 -> 0.1.12 (any plugin/ change must bump the manifest, issue #1040) - plugin/README.md + using-scribe SKILL + scribe_static_context: drop events/typed-entities from the surface lists; "second brain" -> "system of record" in the SessionStart context Claude reads each session - docs/api-keys-and-mcp.md: drop the Typed-entities + Events MCP tool rows, add the Systems row - README.md: rewrite the stale front-matter (chat/RAG/calendar/weather/push described a pre-pivot product) to the current Claude-driven work store Deeper pre-pivot doc-rot in docs/features.md + docs/api-reference.md (chat/journal/weather/web-research) is tracked separately as an issue. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BPtbSzA4JLMAKgFZ8VTg7Q |
||
|
|
12f71fabdf |
refactor(scribe): remove calendar + entity surfaces from web UI (frontend)
CI & Build / Python lint (push) Successful in 4s
CI & Build / integration (push) Successful in 28s
CI & Build / TypeScript typecheck (push) Successful in 33s
CI & Build / Python tests (push) Successful in 46s
CI & Build / Build & push image (push) Successful in 44s
Frontend half of the narrowing (milestone #194); matches backend
|
||
|
|
dd60244429 |
test(trash): update model-count assertions after Event removal
CI & Build / Python lint (push) Successful in 3s
CI & Build / integration (push) Successful in 13s
CI & Build / TypeScript typecheck (push) Successful in 32s
CI & Build / Python tests (push) Successful in 53s
CI & Build / Build & push image (push) Successful in 1m12s
restore() / purge_expired() now iterate 6 soft-deletable models (Note, Project, Milestone, Rulebook, RulebookTopic, Rule) — Event was removed with the calendar surface. Adjust the two count-coupled assertions (execute.await_count 7→6; the rowcount list drops a zero so the sum stays 4). Caught by CI run #2580. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BPtbSzA4JLMAKgFZ8VTg7Q |
||
|
|
b49efdcb11 |
refactor(scribe): retire calendar/events + person/place/list entities (backend)
CI & Build / Python lint (push) Successful in 3s
CI & Build / TypeScript typecheck (push) Successful in 11s
CI & Build / integration (push) Successful in 30s
CI & Build / Python tests (push) Failing after 31s
CI & Build / Build & push image (push) Has been skipped
Narrow Scribe to a Claude-Code work system-of-record (milestone #194, decision note #1759). Wholesale removal per rule #22 — backend + schema half. Calendar/events + CalDAV: delete models/event, services/{events,caldav, caldav_sync}, routes/events, mcp/tools/events; strip event branches from backup (bump v3->v4), dashboard (upcoming_events), trash, recent, and the mcp server read-only allowlist + instructions. Typed entities (person/place/list): delete mcp/tools/entities; drop the notes.metadata (entity_meta) column from model/service/routes and the knowledge browse service. note_type STAYS — it also marks 'process' notes. Scheduler: event_scheduler -> recurrence_scheduler, keeping only the recurring-task spawn job (drops event reminders + CalDAV sync). Schema: migration 0069 drops the events table + notes.metadata column + orphan caldav settings rows (faithful downgrade recreates them). KEEP: recurrence.py (task recurrence), notifications task reminders, graph view, and every work surface. Frontend + plugin/docs true-up follow next. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BPtbSzA4JLMAKgFZ8VTg7Q |
||
|
|
f6629d4bcf |
fix(plugin): keep always-on rules alive across compaction (0.1.10 → 0.1.11)
Always-on rules were on-demand, not always-present: Tier-1 static context only
tells the agent to call list_always_on_rules(), and Tier-2 dynamic fetch is dark
(token doesn't reach the hook subprocess). On compaction the fetched rules get
summarized away while the harness's own built-in git instruction ("branch first")
survives in the base prompt — so post-compact the generic git instinct wins and
rule #1 ("dev is home") is missed.
- scribe_static_context.md: new "Operator rules govern consequential actions"
bullet — before any git branch/commit/push or hard-to-reverse action, loaded
rules beat generic harness/default habits; re-pull rules if not loaded or
summarized by a compaction. Tier 1 = always fires, keyless, re-fires on compact.
- scribe_session_context.sh: compaction banner now re-pulls list_always_on_rules(),
not just enter_project().
- plugin.json: 0.1.10 → 0.1.11 so autoUpdate ships the plugin/ change (#1040).
Generic and instance-agnostic per rules #115/#119 — no operator-specific rule
text hardcoded. Refs issue #1197.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E4bNefPFAz7esmMZMZmkzL
|
||
|
|
2bc054d7ef |
fix(plugin): bump version 0.1.9 → 0.1.10 to ship auto-inject hook
Path A's UserPromptSubmit hook (scribe_autoinject.sh) + hooks.json were merged to main in PR #74 but the plugin version was never bumped, so the autoUpdate marketplace (keyed by version string) never re-pulled the snapshot — the hook was stranded, uninstallable, and not running in any session. Bumping the version is what makes installs detect and pull it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xz4j1H7pjYSjKsEpgcNH5E |
||
|
|
eec241d3c0 |
feat(plugin): sharpen the recall-before-acting reflex in static context
Turn the SessionStart static guidance into a concrete recall trigger — search Scribe before answering about the operator projects/people/places/decisions or starting a task, and pass the active project id to scope results — instead of a vague "search for related work". Step 4 (pull-path sharpening); the cross-encoder rerank half is deferred until auto_inject telemetry shows precision is the bottleneck. Scribe: project 2, milestone 93, task 1034. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xz4j1H7pjYSjKsEpgcNH5E |
||
|
|
8126db3203 |
feat(plugin): knowledge auto-inject (Path A) — title-first per-turn awareness
CI & Build / Python lint (push) Successful in 2s
CI & Build / integration (push) Successful in 12s
CI & Build / TypeScript typecheck (push) Successful in 21s
CI & Build / Python tests (push) Successful in 43s
CI & Build / Build & push image (push) Successful in 55s
New UserPromptSubmit hook (scribe_autoinject.sh) + GET /api/plugin/retrieve that surface the TITLES (never bodies) of the few notes clearing four anti-bloat gates: a per-user confidence threshold (stricter than pull search), a margin gate, per-session dedup (exclude_ids), and a top-k ceiling. Each retrieval is logged to retrieval_logs as source=auto_inject so the threshold can be tuned from data. Per-user config (enable / threshold / top-k) is DB-backed via /api/settings with a Settings UI card; defaults enabled, threshold 0.55, top-k 3 (conservative — tune once auto_inject telemetry accrues). Scribe: project 2, milestone 93, task 1033. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xz4j1H7pjYSjKsEpgcNH5E |
||
|
|
807f478cac |
feat(search): retrieval telemetry — log every semantic retrieval
CI & Build / Python lint (push) Successful in 2s
CI & Build / integration (push) Successful in 16s
CI & Build / TypeScript typecheck (push) Successful in 32s
CI & Build / Python tests (push) Successful in 44s
CI & Build / Build & push image (push) Successful in 58s
Add retrieval_logs (migration 0068) + services/retrieval_telemetry with a fire-and-forget record_retrieval(), wired into the MCP search tool (source=mcp_search) and the REST search route (source=rest_search). Captures query, effective params, and the per-result score distribution so KB-injection thresholds can be tuned from data rather than guessed. Scribe: project 2, milestone 93, task 1032. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xz4j1H7pjYSjKsEpgcNH5E |
||
|
|
513019786e |
feat(search): pgvector substrate — vector(384) + HNSW for semantic search
Move semantic_search_notes off the full-table Python cosine scan onto a native pgvector column: indexed ORDER BY embedding <=> :q LIMIT k (HNSW, cosine). Migration 0067 enables the extension, converts the JSONB embedding column to vector(384) (stale-dim rows dropped and regenerated by the startup backfill), and builds the HNSW cosine index. Postgres image moves postgres:16-alpine -> pgvector/pgvector:pg17 across prod, quickstart, and CI. Scribe: project 2, milestone 93, task 1031. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xz4j1H7pjYSjKsEpgcNH5E |
||
|
|
5fbee18a94 |
feat(mcp): S5 — issue-kind guidance across all instruction surfaces
CI & Build / Python lint (push) Successful in 3s
CI & Build / integration (push) Successful in 15s
CI & Build / TypeScript typecheck (push) Successful in 34s
CI & Build / Python tests (push) Successful in 46s
CI & Build / Build & push image (push) Successful in 1m3s
Plan #825 (T2 — Issues task_kind) shipped S1–S4 but its S5 docs slice never landed, so every behavioral surface the plugin pushes to the agent still described the pre-kind convention ("tag `issue`" on a create_note). Result: agents fixed bugs without reaching for kind=issue and dumped the work as logs on unrelated open tasks. - _INSTRUCTIONS: rewrite the "record a problem" bullet to create_task(kind="issue") with symptom→cause→fix + arose_from_id / system_ids, and an explicit "not a work-log on an unrelated task"; add Issue + System to the hierarchy section. - skills/systematic-debugging, verification: drop "tag `issue`" / create_note-issue, point at create_task(kind="issue"). - skills/using-scribe: add issues/systems to the entity list + reflex #6. - hooks/scribe_static_context: fix → its own issue on the keyless floor. Instance-agnostic, prose-only; no schema or tool-behavior change. Pairs with always-on rule #118. Issue: #855. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
4f31890bde |
test(ci): dispose engine between integration tests (per-loop pool)
CI & Build / Python lint (push) Successful in 2s
CI & Build / integration (push) Successful in 14s
CI & Build / TypeScript typecheck (push) Successful in 32s
CI & Build / Python tests (push) Successful in 43s
CI & Build / Build & push image (push) Successful in 13s
First integration run proved the lane works (run_maintenance test passed against real Postgres), but the health test failed with 'Future attached to a different loop': pytest-asyncio uses a fresh loop per test while the app's module-level engine pools a connection from the prior test's loop. Dispose the engine in each test's teardown so the next test starts with an empty pool on its own loop. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
2ad2e943f3 |
test(ci): add Postgres integration lane + real run_maintenance guard
CI & Build / Python lint (push) Successful in 2s
CI & Build / integration (push) Failing after 19s
CI & Build / TypeScript typecheck (push) Successful in 20s
CI & Build / Python tests (push) Successful in 45s
CI & Build / Build & push image (push) Successful in 1m1s
The unit suite can't catch sync/async API mismatches against SQLAlchemy (an un-awaited execution_options passed green CI but failed at runtime: VACUUM 0/6). Add a real-Postgres integration lane modelled on the family pattern (rules 6/79-82): a new CI 'integration' job with a postgres:16 service, bridge-IP discovery, busybox-safe readiness wait, and 'alembic upgrade head', running pytest -m integration. Non-gating, like the unit lane. - tests/test_integration_db_maintenance.py: runs run_maintenance() and get_table_health() against real Postgres; asserts all allowlisted tables vacuum OK (the await regression makes this fail) and health reports real stats. - pyproject: register the 'integration' marker. - conftest: integration-marked tests use the real DATABASE_URL, not the stub. - ci.yml: unit 'test' job now runs -m 'not integration'. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
e6c89f6b88 |
fix(db): await AsyncConnection.execution_options in run_maintenance
execution_options() is a coroutine on AsyncConnection and must be awaited; the un-awaited call returned a coroutine, so exec_driver_sql() blew up with AttributeError and every table's VACUUM was skipped (Run-now reported 0/6). A prior change had wrongly dropped the await. Fix it and make the test mock execution_options async so this call shape is actually exercised. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
96079d5b77 |
feat(db): table-health readout — per-table bloat metrics in admin card
You can't decide what to maintain without seeing what's bloating. Adds a read-only health panel driven by Postgres' own statistics views. - services/db_maintenance.py: get_table_health() queries pg_stat_user_tables + pg_total_relation_size + pg_database_size — per-table size, live/dead tuples, dead-tuple ratio (the bloat signal), and last (auto)vacuum/(auto)analyze. - routes/admin.py: admin-only GET /api/admin/db-maintenance/health. - SettingsView.vue: 'Table health' table in the maintenance card, all tables sorted by dead tuples, rows >=20% dead-ratio flagged; total DB size shown; refreshes after a Run-now so the dead-tuple drop is visible. - Tests: health row/size shaping + null-timestamp passthrough; route + service surface. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
c4553d937c |
feat(db): scheduled DB maintenance — daily targeted VACUUM (ANALYZE)
Adds a daily off-hours VACUUM (ANALYZE) over the high-churn tables the retention/purge sweeps churn (app_logs, notifications, token tables, notes, note_versions), on top of Postgres autovacuum, to reclaim bloat left by the nightly bulk DELETEs and keep planner stats fresh. - services/db_maintenance.py: run_maintenance() over a closed table allowlist via an AUTOCOMMIT connection (VACUUM can't run in a txn); per-table summary persisted as the db_maintenance_last_run admin setting. - services/db_maintenance_scheduler.py: BackgroundScheduler cron (default 04:00 UTC, after the 03:30 trash purge); enabled-gate checked at fire time; live reschedule on hour change. Wired into app.py start/stop. - routes/admin.py: admin-only GET/PUT /api/admin/db-maintenance + POST /run. - settings.py: set_admin_setting() (write-side of get_admin_setting) for out-of-request writes. - SettingsView.vue: admin 'Database maintenance' card — enable toggle, run-hour (UTC), Run-now, last-run summary. - Tests: allowlist is closed, VACUUM issued per table, one failure doesn't abort the rest, summary persisted; route/scheduler/service surface. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
ee02ed37c1 |
feat(plugin): compaction-hygiene guidance — recommend safe compaction at seams
#834. The pre-compaction complement to the shipped post-compaction re-grounding banner. Because Scribe records progress as you go (task status, work-logs, decision notes), a compaction at a clean work-seam is lossless — so guide the model to recommend it proactively rather than letting auto-compact fire mid-task. Placed in the ALWAYS-loaded channels (operator wants it consistently in context, not relevance-gated like a skill): MCP _INSTRUCTIONS (every handshake) + the static SessionStart floor (every session, MCP-independent). Behavior: at the end of a block of work in a long session, ensure in-flight state is logged, then tell the operator it's a safe moment to /compact (naming what was logged); recommend at seams, not every turn; the model can't run /compact itself. plugin.json 0.1.8 → 0.1.9 so clients re-pull the static-context change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
dd1fc2d506 |
feat(mcp): extend dedup gate to create_rule / create_project_rule
Completes the Phase 5 follow-up: rules now get the same update-over-create gate. Title-based only (rules aren't a semantic-retrieval/RAG surface), scoped to the same topic (rulebook rule) or same project (project rule). force=true overrides; fail-open like the note/task gate. Deferred-item decisions (operator): REST/web gating SKIPPED (kept MCP-only — humans rarely double-create and a hard block needs UI affordance); orphan scope kept orphan↔orphan (no change). So this rule gate is the only remaining build. - services/dedup.py: find_duplicate_rule(title, topic_id|project_id). - create_rule + create_project_rule: force param + gate. - tests: rule title match, scope-required guard, tool gate (block + force). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
5102ffb558 |
fix(dedup): fail open when the duplicate check can't run
The Phase 5 gate added a DB query before every create_note/create_task. When that query fails (DB unreachable, etc.) the create must NOT error — a dedup check is advisory infrastructure, not a correctness gate. Wrap the title query so any failure degrades to "no duplicate found" and the create proceeds. Also fixes 7 existing create tests that don't mock the DB: they now exercise the fail-open path (no Postgres in the unit-test job) instead of erroring. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
322cbc3b5e |
feat(mcp): Phase 5 — write-time near-duplicate gate (update-over-create)
#755 Phase 5. create_note / create_task now BLOCK a near-duplicate instead of silently inserting: they return {"duplicate": true, "existing_id", message} pointing at the record to UPDATE. Fights store bloat and stale competing copies that semantic search (RAG) would otherwise resurface for reconciliation. A force=true override creates anyway for genuinely-distinct records. - services/dedup.py: find_duplicate_note — two signals, scoped to owner + same project + same kind: (1) normalized-title exact match (cheap, always); (2) semantic cosine ≥ 0.90 but ONLY when body ≥ 200 chars (short/title-only embeddings false-positive — the pre-pivot lesson). Project-less (orphan) records compare only to other orphans on BOTH signals (orphan_only on the semantic call) — they're not matched across every project. - Gate wired into the MCP create_note/create_task tools (the LLM write path) with force override; _INSTRUCTIONS documents the duplicate response + force. - Opt-in by design: the service helper is only called from the interactive create tools. Internal/programmatic creates (recurrence spawn, imports) go straight through services.create_note and are NOT gated — a recurring task spawning its next same-titled instance must not be blocked. - Scope v1: MCP tools only. REST/web (human CRUD, needs a UI affordance) and create_rule (not a RAG surface; _INSTRUCTIONS already steer it) are follow-ups. - tests: dedup service (title/semantic/body-gate/type-filter) + tool gate (blocks, force bypasses) for notes and tasks. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
33f9a0a4d4 |
feat(plugin): Phase 4 — Scribe Processes auto-surface as local skills
#755 Phase 4. Saved Scribe Processes (DRY pass, Drift Audit, …) now surface as auto-triggered Claude Code skills instead of pull-only get_process calls. Design correction vs the plan: stubs live in the USER's ~/.claude/skills/, NOT plugin/skills/_instance/. The plugin is git-cloned and identical per install, so instance-specific generated files can't ride in it; personal skills are live-detected within the session (verified via claude-code-guide). MCP prompts were the alternative but are pull-only (no relevance auto-surface), so skills are the right primitive. - backend: GET /api/plugin/processes manifest (services/plugin_context. build_process_manifest) — {name, slug, description} per Process; description is the auto-surface trigger (title + preview); slugs deduped, blanks skipped. - plugin: scribe_sync_processes.sh writes ~/.claude/skills/scribe-proc-<slug>/ SKILL.md (body = "call get_process(name), follow verbatim") and PRUNES stale scribe-proc-* stubs. Fail-open + silent; a transient fetch failure never wipes existing stubs. Runs as a 2nd SessionStart hook + via the /scribe:sync command. - plugin.json 0.1.7 → 0.1.8; README updated. - tests: build_process_manifest (render, slug dedupe, blank-title skip, preview truncation). Sync script's write+prune validated in isolation (plugin/** is not CI-covered): correct stubs created, stale pruned, unrelated skills untouched. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
e8d6de287b |
test: fix obsolete create_task kind=plan passthrough test
test_create_task_passes_kind asserted create_task forwards kind=plan; the hard-retire guard now rejects that. Exercise passthrough with kind=issue instead. (Service-level create_note still accepts task_kind=plan by design — the guard lives at the user-facing tool/route layer, not the primitive.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
f7742173aa |
chore(plans): make kind=plan retirement consistent across MCP, REST, UI, skills
Audit of the plugin + MCP surface after milestone-as-plan (T3): every path that could still create a kind=plan task or describe the old plan-task model is now aligned with the hard-retire decision. - create_task (MCP + REST POST /api/tasks): reject kind=plan with a message pointing to start_planning. The 'plan' enum value stays valid so legacy plan-tasks remain readable; update paths never touch kind, so they round-trip. - create_task / get_task docstrings: 'plan' dropped from creatable kinds; get_task's rules-augmentation noted as legacy-only (get_milestone for new plans). - skills/writing-plans: rewritten for milestone-as-plan (body = design, steps = child tasks, get_milestone to read back). - skills/using-scribe: "plans live in milestones via start_planning", not kind=plan. - TaskEditorView Kind selector: offers Work/Issue; "Plan (legacy)" shown only when the loaded task is already kind=plan (display round-trip). - test: create_task rejects kind=plan. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
1f6c592226 |
feat(plans): milestone-as-plan-container; retire kind=plan (T3)
The milestone becomes the plan container: a new nullable milestones.body holds the design/intent (Goal/Approach/Verification) and individual steps live as first-class child tasks (milestone_id) instead of checkboxes crammed into one kind=plan task body. start_planning now creates a MILESTONE seeded with the body template (not a kind=plan task) and returns it with applicable rules; a new get_milestone MCP tool reads the plan back (body + steps + rules). kind=plan is hard-retired going forward — start_planning never creates one. The 'plan' task_kind enum value stays valid so the 11 historical plan-tasks remain readable in place; no body-shredding backfill (corpus review showed auto-splitting their checklists into tasks would be lossy: embedded code blocks, a non-binary [~] state, tables, ID-encoded hierarchy). - migration 0066: add milestones.body - model/service/route/MCP: body passthrough on create+update; get_milestone - server _INSTRUCTIONS: "plan" = milestone w/ body + child step-tasks - UI: ProjectView shows/edits a milestone's plan body; start_planning expands the new milestone and opens its plan editor - tests updated to the milestone contract + new body/get_milestone coverage Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
c972af2690 |
ci: make npm cache step non-fatal (fixes recurring typecheck flake #828)
The TypeScript-typecheck job intermittently failed at 'Cache npm download cache' (transient cache-backend hiccup), which skipped install + type check and marked the run red — 3x during the issues+systems build, all on pushes the cache step had no bearing on. continue-on-error: true degrades a cache failure to 'install without cache' instead of failing the job. Closes the rerun churn from task #828. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
b6d01686d8 |
feat(issues): S4b editor controls — Kind selector + Systems multi-select
In the full task editor (TaskEditorView) sidebar: - Kind selector (Work / Plan / Issue), mirroring the Status/Priority selects. - Systems multi-select (checkboxes of the project's systems, fetched via the systems store), shown when a project is set. Both wired through load (prefill from task.task_kind / task.systems), dirty tracking, and save (kind + system_ids via the store's IssueFields). No new colors — existing sb-field/sb-select tokens. Deferred: the arose-from (provenance) picker — least-critical control and the riskiest (task-search UI); the field is already supported by API/store/route for a later add. NEEDS operator browser verification (CI typechecks only). Refs plan 825 (S4b editor). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
94d32c524a |
feat(issues): S4b frontend — open-issues lists + issue plumbing
- types/note.ts: Note gains systems? + arose_from_id?; TaskKind includes 'issue'. - stores/tasks.ts: create/update accept IssueFields (kind/system_ids/arose_from_id). - api/systems.ts: getProjectIssues + TaskLike. - DashboardView.vue: 'Open issues' rail section from dashboard.open_issues (links to /tasks/<id>, project + status). - SystemsSection.vue (project Systems tab): 'Open issues' list via getProjectIssues, with system chips, links to /tasks/<id>. Both reuse existing CSS tokens. Issue-editor controls (kind selector / system multi-select / arose-from picker in WorkspaceTaskPanel) are the remaining S4b piece. NEEDS operator browser verification (CI typechecks only). Refs plan 825 (S4b frontend — lists). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
79040fe5db |
feat(issues): S4b backend — REST task issue fields + dashboard open-issues
Closes the REST gap S4b's UI needs (S2 only extended MCP tools): - routes/tasks.py: create/update accept system_ids (set-semantics) + arose_from_id; GET/create/update return the task's associated systems. kind=issue already flowed via task_kind. Associations set via services/systems (ACL-checked; can_write_note already gated). - services/dashboard.py: _open_issues section (owner-scoped, ranked like other task lists, capped) added to build_dashboard. Dashboard test updated for the new key. Refs plan 825 (S4b, backend half). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
9293a9b198 |
fix(issues): S4a typecheck — allow null color in updateSystem param
vue-tsc TS2345: System.color is string|null, but updateSystem's data param typed color as string, so the store's Partial<Pick<System,...>> wasn't assignable. Widen the param's color to string|null (clearing a color is valid). Refs plan 825 (S4a). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
4da29562bd |
feat(issues): S4a UI — Systems management section in project view
Frontend foundation for Issues + Systems (spec #825, S4a). - frontend/src/api/systems.ts: typed client (System + list/create/update/delete) over /api/projects/<id>/systems, matching the rulebooks api style. - frontend/src/stores/systems.ts: Pinia store keyed by project (fetch/create/ update/archive/unarchive/delete), toast-on-error. - frontend/src/components/SystemsSection.vue: a Systems management section — cards (color swatch, name, description, 'N open' issue-count badge) with inline create/edit, archive (hidden behind a 'show archived' toggle), and a delete-confirm modal. v1 quality: loading skeleton, empty state, error toasts, keyboard a11y, focus rings; reuses existing CSS tokens (no new colors). - ProjectView.vue: new 'Systems' tab (between Notes and Rules), rendering <SystemsSection :project-id>, wired like the existing rules tab. S4b (next) adds issue-editor controls (kind=issue/system multi-select/arose-from), open-issues lists, and the dashboard surface. NEEDS operator browser verification (CI typechecks but can't render). Refs plan 825 (S4a). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
4f22646c88 |
feat(issues): S3 REST routes — systems CRUD + project open-issues
Third slice of Issues + Systems (spec #825). routes/systems.py (nested /api/projects/<id>/...): GET/POST systems (list adds per-system open_issue_count via one grouped query), GET/PATCH/DELETE a system (GET returns records split into issues/tasks/notes), GET .../systems/<id>/records (kind/open_only filters), GET .../issues (project's open issues for the project view + dashboard roll-up). login_required; project access via get_project_for_user; writes gated by can_write_project (clean 403); system.project_id verified to match the path. Blueprint registered in app.py. services/systems.py: + open_issue_counts_by_system (one grouped query) and list_issues (project issues, open by default). Tests: structural (blueprint registered + in app, handlers callable, service contracts take user_id) — matches the house route-test pattern. Refs plan 825 (S3). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
85e0501705 |
feat(issues): S2 MCP tools — system CRUD + issue/system wiring
Second slice of Issues + Systems (spec #825). New mcp/tools/systems.py: create_system, list_systems, get_system (records split into issues/tasks/notes), update_system (incl. archive via status), list_system_records (kind/open_only filters), delete_system. Registered in register_all; read tools (get_system, list_systems, list_system_records) added to the read-only-key allowlist (write tools default-deny). create_task/update_task: kind now accepts 'issue'; new system_ids (set-semantics associations) and arose_from_id (provenance, 0=unchanged/-1=clear) args. create_note/update_note: new system_ids arg (notes associate with systems too). services/notes.create_note: arose_from_id passthrough (update_note already handles it via setattr). Tests: MCP system tools + create_task issue-wiring (kind/provenance/systems), service layer mocked. Refs plan 825 (S2). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
b91c447b0b |
feat(issues): S1 schema — issue task_kind, System entity, associations
First slice of the Issues + Systems feature (spec #825, plan #819 T2). Schema (migration 0065): - task_kind CHECK expands work|plan -> work|plan|issue (same-change, rule 36) - notes.arose_from_id: optional self-FK for issue->originating-task provenance (distinct from parent_id sub-task hierarchy) - systems: per-project, self-describing (name + description) subsystem/area - record_systems: M2M join linking any note/task/issue to systems (mutable) Models: System + RecordSystem; note.py gains arose_from_id (+ index, to_dict). Service services/systems.py: CRUD, archive, soft-delete, set/list associations, records-for-system, open-issue count — all gated via services/access.py project permissions (rule 78, no bare-owner filters). Unit tests lock the ACL gating; the migration is exercised by CI's integration lane (alembic upgrade head). is_task stays a derived property (status is not None) — unchanged. T1 (typing- axis rationalization) intentionally NOT bundled; this only adds the enum value. Refs plan 825 (S1). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
88106309f4 |
feat(plugin): add 4 Scribe-native process-skills (restore superpowers gap)
Superpowers was uninstalled but its replacements were never built (only using-scribe shipped) — a live functional hole. Author the 4 the operator wants back, each integrated with Scribe's toolset rather than generic copies: - writing-plans -> start_planning / kind=plan task, not local .md - systematic-debugging -> capture issue (symptom->cause->fix, tag issue) on resolve - verification -> log results to the task work-log; honest done - brainstorming -> recall prior thinking first; capture the decision note Skipped TDD + receiving-code-review per operator (well-covered by Claude/them). Manifest + using-scribe list now advertise only the 4 that ship. Remove the stale docs/superpowers/*.md reference in _INSTRUCTIONS (superpowers is gone). Plugin 0.1.6 -> 0.1.7. Refs plan 821 (Phase 3 of 755). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
d99c4e3c15 |
feat(plugin): compaction re-grounding in SessionStart hook (A7)
Deliver 'don't silently lose work at compaction' via the mechanism that actually works. Verified contract: a PreCompact hook CANNOT make the model flush to Scribe (host hooks can't trigger model tool calls, and can't know the in-flight task ids), and its additionalContext only shapes the one-shot summary. The correct tool is SessionStart scoped to source=compact, which fires AFTER compaction and injects context the model reads. Our SessionStart hook is matcher-less, so it already fires on compact — it just said nothing compaction-specific. Now it reads the stdin event and, when source==compact, leads with a banner telling the model to reload the active project + in-flight tasks from Scribe and reconcile half-remembered state. Durable path = record-as-you-go (A4/B8) + this post-compaction reload. Refs plan 812 (A7); supersedes the literal 'PreCompact hook' idea. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
c0b9831b0f |
feat(mcp): issue-capture convention in _INSTRUCTIONS (B8)
Finish the breakfix/issue-logging gap as a lightweight convention: when
recording a solved problem, capture symptom -> root cause -> fix and tag it
'issue' so it's findable instead of re-diagnosed. Pairs with the B9 trigger
('log when a problem is found'). No schema change — a structured note_type/
task_kind=issue is deferred to a joint schema pass with B7.
Refs plan 812 (B8 convention; B7 deferred).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
700cfc664b |
feat(plugin): surface dynamic-tier failures in SessionStart hook (A2)
Fail-open but no longer silent. When the dynamic context fetch yields nothing,
append a short status line to the injected context so a session can tell
'couldn't load live context' apart from 'Scribe had nothing to say':
- endpoint+token present but fetch empty/failed -> 'instance unreachable / request failed'
- endpoint present but token absent -> fingerprints the known Claude Code
userConfig export gap ('API token did not reach this hook')
A fully unconfigured install (no url AND no token) stays quiet — static-only is
the intended mode there. Static Tier 1 still always carries the mandate.
Refs plan 812 item A2.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
f125f86e16 |
ref(mcp): make the dev-ACL instruction self-contained (no instance coupling)
Drop the '(This instance's rules carry the specifics.)' pointer — universal _INSTRUCTIONS must not assume this install has a particular rulebook. State the ACL principle on its own so it holds for any Scribe install/fork. Refs plan 812 (instance-agnostic product principle). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
95e1d47ceb |
ref(mcp): neutralize dev-shaped vocabulary in _INSTRUCTIONS + add write-mandate (B9/A4)
The MCP instructions are domain-neutral except a thin layer of dev vocabulary and one project-specific paragraph (B10 audit, task 812). Make the data store's own instructions serve any domain, and add the missing positive write-mandate. B9 (neutralize): - 'before writing code' -> 'before you dive in' - Note examples 'dev-logs' -> 'logs of what happened' - record trigger 'a merge, a shipped feature, a finished plan' + 'dev-log note' -> 'finishing a task, or hitting/discovering a problem that changes direction' (folds in B8: log pivots, not just wins; mirrors the static-tier wording) - recall examples 'ticket/dev-log' -> 'task/prior note' (server + SKILL.md) - 'Engineering and workflow rules' -> 'Workflow and standards rules' - slim the 'developing Scribe itself' ACL paragraph to a neutral one-liner (project-specific specifics already live in rules #47/#78) A4 (write-mandate): state up front that Scribe is the system of record — record work here, recall before acting, don't keep project work in local files. Refs plan 812 (B9, A4, B8-trigger); B10 audit work-log. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
f2ab02ba2b |
ref, plugin: neutral, concrete triggers in static SessionStart mandate
Plain-language 'related prior work' instead of 'prior art'; replace the dev-shaped 'meaningful landing (a merge, a shipped feature, a finished plan)' with concrete neutral triggers — log on task completion and when a problem is found, so direction pivots are captured, not just successes. Keeps the static mandate domain-neutral (pre-empts B9 drift in plan 812). Refs task 809 / plan 812 item A1. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
d11eb9145b |
feat(plugin): two-tier SessionStart hook — static keyless floor + dynamic enrichment
The SessionStart push channel was single-tier: it curled /api/plugin/context with a Bearer token and, on any failure (missing/unexported token, network error, missing curl), injected nothing and exited 0 — silently. A known upstream Claude Code gap (sensitive userConfig not reliably exported to hook subprocesses) trips this routinely, so a fresh session gets no signal to reach for Scribe and falls back to local file-memory (root cause of unlogged work on remote/rc sessions). Split into two tiers: - Tier 1 (static, keyless, networkless, always fires): inject bundled scribe_static_context.md — the load-bearing behavioral mandate. Cannot be suppressed by the upstream key bug. - Tier 2 (dynamic, best-effort, fails open): existing curl for live rules + active-project context, appended below the static block. Lights up as enrichment once the key reaches the hook. Only jq is now required (JSON envelope); curl/token gate the dynamic tier only. Bump plugin 0.1.5 -> 0.1.6 so clients pick up the change. Refs milestone 55; task 809; decision note 810. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
e631a4e615 | Merge pull request 'feat(plugin): Scribe replaces native memory by instruction; tighten project-scope discipline' (#70) from dev into main | ||
|
|
9eddb8497c |
feat(plugin): Scribe replaces native memory by instruction; tighten project-scope discipline
Direction change (operator, see plan task #755 work-log): the plugin must NOT depend on disabling a native Claude function to work. It earns its place by steering behavior, not by toggling autoMemoryEnabled. Memory doctrine (no dual-write): - using-scribe SKILL.md gains "Scribe holds these functions — don't keep a second copy": route rules/recall/planning to Scribe, don't also write them to native auto-memory, never instruct disabling a native function, and accept a "Scribe-shaped hole" if the plugin is removed (recover over time). - mcp/server.py _INSTRUCTIONS: drop the paragraph that told the model to create/refresh a "rules live in Scribe" pointer in CLAUDE.md / ~/.claude memory. That was an active dual-write instruction; the SessionStart hook is the bridge now. Replaced with the no-dual-write / no-settings-dependency doctrine. Supersedes plan #755 Phase 6 ("set autoMemoryEnabled:false"). Project-scope discipline (stop cross-project bleed): - using-scribe SKILL.md gains "Stay inside the active project's scope": pass project_id to every read, only reference/offer work on the in-scope project, ask before switching. - _INSTRUCTIONS scope bullet extended from reads to referencing/offering, and flags get_recent as cross-project. - get_recent docstring gains a scope note steering to scoped list_* when a project is active. plugin.json 0.1.4 -> 0.1.5 so clients' caches actually refresh (re-shipping under the same version does not bust the cache). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
974fa6a215 | Merge pull request 'feat(ui): declutter dashboard done-recently + MCP-access, add per-project stats' (#69) from dev into main | ||
|
|
da511fcc9f |
feat(ui): declutter dashboard done-recently + MCP-access, add per-project stats
Dashboard: - 'Done recently' chip-cloud -> compact uniform list (Active-now row style), showing 5 with inline expand to the rest (backend already returns up to 8). - New 'Projects' rail card: each active project with 'N open · M done'. Backend already computed done_count (dashboard.py) — now surfaced in the /api/dashboard payload per active project. MCP Access (Connect Claude / Claude Code): - Progressive disclosure: lead with the pre-filled plugin-install snippet; fold server name, scope, marketplace URL, and the MCP-only path into a single 'Customize' expander. Desktop tab keeps its own server-name field. - Marketplace URL now defaults to this instance's own repo via config.PLUGIN_MARKETPLACE_URL (env-overridable); /api/plugin/marketplace-url falls back to it, so the field + install snippet are pre-filled out of the box instead of showing a generic placeholder. Refs #761 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
79aec4f9c1 | Merge pull request 'chore(plugin): bump to 0.1.4 so clients pick up the using-scribe update' (#68) from dev into main |