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
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
Frontend half of the narrowing (milestone #194); matches backend b49efdc.
- delete CalendarView, EventSlideOver, WeatherCard (orphan)
- drop /calendar route + nav link + the g→l keyboard shortcut
- strip the calendar-events API client + event/metadata bits from note
types and the notes store
- KnowledgeView: remove People/Places/Lists tabs, entity cards, create
buttons and the upcoming-events widget; keep notes/tasks/plans/processes
+ the overdue-task badge
- NoteEditorView: remove person/place/list forms + list-builder + entity
metadata; keep note + process editors (type select = Note/Process)
- DashboardView: drop the "Upcoming · 7 days" events rail card
- SettingsView: remove the CalDAV integration card + save/test (its
endpoints were deleted backend-side)
- prune the now-dead entity/event CSS
RecurrenceEditor + task recurrence rules are kept (task machinery, not
calendar). Verified by a full dangler sweep.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BPtbSzA4JLMAKgFZ8VTg7Q
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
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
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
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
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
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
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
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>
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>
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>
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>
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>
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>
#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>
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>
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>
#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>
#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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>