1d65e98ac293a95f17ed8fbb0dac0a2c75ce8725
12
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
02c1e37620 |
feat(rules): the write path can notice a standing rule it was never given (#3031, milestone 307 step 5, hook arm)
CI & Build / Python lint (push) Successful in 4s
CI & Build / Plugin hooks (push) Successful in 10s
CI & Build / integration (push) Successful in 26s
CI & Build / TypeScript typecheck (push) Successful in 33s
CI & Build / Python tests (push) Successful in 1m7s
CI & Build / Build & push image (push) Successful in 24s
A conditional rule is not resident, so a session can be about to violate one it was never handed. This arm notices: when what is being written resembles a rule's trigger, the hint names it and says to read it before deciding it does not apply. A SUGGESTION, and the plan was wrong about why it could be more. It claimed the hook "already resolves a path to an area" — it does not, and nothing in Scribe maps a path to a System or a canonical area (build_write_path_hint resolves paths against snippet LOCATIONS, a different index; the learned-alias idea belongs to another project). Correction logged on the task. Rather than invent path→area inference to make a stale claim true, the arm does what D7 already decided and what this surface already IS: tags bind at enter_project, meaning suggests here. The header of the hook says NEVER BLOCKS; dressing a hint up as binding would have been the actual mistake. CONDITIONAL RULES ONLY. An always-on rule is already in the session, so re-offering it is noise — and noise on a hint that fires on every write is how a hint gets ignored. Telemetry goes to retrieval_logs, NOT note_usage_events, and that is a correctness call rather than a preference: note_usage ids are REMAPPED on a backup restore, so a rule id written there would come back attached to whatever note took that number — silently corrupting the evidence the next true-up is supposed to read. retrieval_logs is never restored and `source` already separates surfaces. record_retrieval's `results` type widened to match what it actually needs (an `.id`), instead of passing a Rule to something annotated Note. The rule dedup gets its OWN state file and query parameter, like the three channels before it — #2708's lesson was that one shared channel lets a hint of one class silence a different class that had never been shown. Plugin version bumped: a hook change clients cannot see did not ship (#1040). The stub is autouse in conftest rather than added to forty-odd call sites: the arm loads an embedding model, and every existing test that stubs the NOTES search would otherwise pull a real model in through the one arm it had no way to know about. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
2324c15418 |
feat(write-path): the derive arm — the hint names a duplicate family (no canon) or a canon elsewhere for the shapes being written; exclude_derive channel (#2900, milestone 299 step 2)
CI & Build / Python lint (push) Successful in 4s
CI & Build / Plugin hooks (push) Failing after 9s
CI & Build / integration (push) Successful in 25s
CI & Build / TypeScript typecheck (push) Successful in 32s
CI & Build / Python tests (push) Successful in 58s
CI & Build / Build & push image (push) Successful in 23s
shape_ledger.write_time_derive asks the ledger what it knows about each named (kind, symbol): a derive-grouped family (identical body / same name in N other files) -> "derive it now, do not add a copy"; a canonical row at another path -> "canon #N at <path>, reuse". Judged rows at the path and the canon own file stay silent. Rendered by _derive_line beside the divergence line; keyed (group id / canon:<id>) on a third per-session dedup channel in the hook (.derive.ids -> exclude_derive=). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
64c641ce80 |
refactor(routes): one supersession seam for REST and MCP; PUT/PATCH notes share a handler; shared mask/not-found/caller helpers (#2829, milestone 296 area 5)
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 9s
CI & Build / integration (push) Successful in 25s
CI & Build / TypeScript typecheck (push) Successful in 32s
CI & Build / Python tests (push) Failing after 37s
CI & Build / Build & push image (push) Skipped
Reading the 28 route modules against each other and against the MCP tools: - routes/notes.py carried a PUT and a PATCH handler that were the same function minus the supersedes contract on one of them — one handler now serves both verbs, so both carry it. - The two _attach_supersession copies (REST + MCP) become supersession_svc.attach_relations(uid, note_id, data, hint=) — the seam the two surfaces must agree through; only the agent surface adds the one-sentence reading hint. - Three local _uid() wrappers over g.user.id → scribe.auth.get_current_user_id like every other module; design_systems' private _not_found → routes.utils. not_found; the four "********" literals → settings_svc.SECRET_MASK with the read/write contract written once. - routes/plugin.py: the project_id/repo resolution block and the comma-separated id parse were copied into three endpoints — _project_scope() and _int_list() now. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
475f0857c9 |
feat(ledger): write-path stamping — a pulled canon the session then instantiates lands as a hook instance row (#2791, milestone 294 step 5)
CI & Build / Plugin hooks (push) Failing after 2s
CI & Build / Python lint (push) Successful in 4s
CI & Build / integration (push) Failing after 28s
CI & Build / TypeScript typecheck (push) Successful in 36s
CI & Build / Python tests (push) Successful in 1m5s
CI & Build / Build & push image (push) Successful in 39s
The prior-art hook now names the shapes being written (shapes=kind:name — every definition in the payload, or the one enclosing an Edit found by walking the file upward) and the server stamps them as instance rows when the session PULLED a snippet inside PULL_WINDOW that the payload references by symbol or that the semantic arm scored for this very payload. classified_by=hook, evidence in reason; never overrides a judgment or a canonical row, overridable by classify_shapes. Offered-but-unopened stamps nothing. Pulled-and-already-seen snippets stay in the semantic query as evidence without re-entering the deduped menu. A brand-new shape gets a provisional row the next sync confirms or vanishes. Read-scoped keys get the hint, never the stamp. Plugin 0.1.34. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
3162332a13 |
feat(prior-art): edit-time record-sync nudge — the sync class (#2708)
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 11s
CI & Build / TypeScript typecheck (push) Successful in 24s
CI & Build / integration (push) Successful in 26s
CI & Build / Python tests (push) Successful in 56s
CI & Build / Build & push image (push) Successful in 43s
A snippet recorded AT the exact file being edited is not a reuse suggestion — it IS the record of the file being changed. The write-path hint now renders those as their own SYNC class: 'snippet #N records this file — updating the record is part of the edit (update_snippet / verify_snippet)'. Nearby and semantic hits stay the reuse menu. The two classes dedup on separate per-session channels (exclude_ids vs exclude_sync_ids, .ids vs .sync.ids in the hook), so a reuse hint shown early in a session can no longer silence the record-sync nudge when the recorded file itself is edited later. Sync surfacing is measured under its own note_usage source (write_path_sync) — its pull-through rate is the scoreboard for whether edit-time sync actually happens, per decision #2707 (no forge connection; records stay current in the session that has the context). Plugin 0.1.31. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
e0328f2b1c |
feat(plugin): write-path trigger — offer prior art before code is rewritten
CI & Build / TypeScript typecheck (push) Successful in 35s
CI & Build / Python lint (push) Successful in 4s
CI & Build / integration (push) Successful in 27s
CI & Build / Python tests (push) Successful in 47s
CI & Build / Build & push image (push) Successful in 1m2s
The milestone headline. Auto-inject fires on the operator's prompt; the moment reuse is actually lost is later, when the agent decides mid-task to write a helper. A PreToolUse hook on Write|Edit now fires there. Channel: `additionalContext` with NO permissionDecision, so the note reaches Claude beside the tool result and the write is never blocked — a recall aid must not be able to stop the operator's work. Plain stdout would have been invisible to the model, and deny/ask would have made a nudge into a gate. Two arms, different in kind: - BY PLACE — a snippet recorded at this path (or its directory) is prior art by definition, not resemblance, so it is neither scored nor thresholded. This is what #2083's reverse lookup was built to answer. - BY MEANING — semantic search restricted to snippets (new `note_type` filter on semantic_search_notes) over the code about to be written. Place ranks first; the top-k cap spans both arms. Gates carried over from milestone 93 verbatim: threshold, margin, session dedup, titles-never-bodies. Own `source='write_path'` in retrieval_logs so precision is tunable separately — the docstring records that the place arm is unlogged and hands that to #2085. Its own on/off in Settings but the SAME threshold/top-k: one "how loud may Scribe be" knob is easier to reason about than two that drift, and splitting them later is then a data-backed change rather than a guess. Details worth keeping: the hook sends a REPO-RELATIVE path because that is how locations are recorded; the git remote resolves to a project and is never used as the location `repo` filter (different namespaces, would silently match nothing); the endpoint stays a GET because a read-scoped API key cannot POST and every other hook depends on that. plugin.json 0.1.17 -> 0.1.18. Refs #2082, milestone #232. |
||
|
|
8126db3203 |
feat(plugin): knowledge auto-inject (Path A) — title-first per-turn awareness
CI & Build / integration (push) Successful in 12s
CI & Build / Python tests (push) Successful in 43s
CI & Build / Python lint (push) Successful in 2s
CI & Build / TypeScript typecheck (push) Successful in 21s
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 |
||
|
|
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> |
||
|
|
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> |
||
|
|
8fe571e175 |
feat(plugin): resolve session project from git remote, not a pinned project_id
The SessionStart hook asked for a project_id via plugin userConfig, which pins one install to a single project — wrong for an operator working across many repos/projects. Resolve the active project server-side from the working repo's git remote instead (a stable identifier, not a dir-name guess). - repo_bindings table (migration 0064) + RepoBinding model: (user, repo_key) -> project, FKs CASCADE. - services/repo_bindings: normalize_repo_key collapses ssh/https/scp/creds/port/ .git to host/owner/repo; resolve/set/list/delete. - GET /api/plugin/context takes ?repo=<remote>; unbound repo -> a "bind this repo" hint with a ready bind_repo() call. project_id kept as manual override. - MCP tools: bind_repo / list_repo_bindings / unbind_repo. - Hook sends ?repo=$(git remote get-url origin) URL-encoded; all project_id handling removed. plugin.json drops the project_id userConfig (0.1.2 -> 0.1.3). - Tests: normalize equivalence classes + unbound-hint rendering. Refs task 755 (Scribe-as-plugin push channel). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
1d82e81527 |
feat(plugin): admin-configurable marketplace URL as the install default
The Settings install command had a <your-scribe-repo> placeholder — not copyable. Add an instance-global 'plugin_marketplace_url' setting (admin sets it to the app's own repo) that every user's MCP Access reads, so the /plugin marketplace add command is copyable out of the box. Keeps it universal (each deployment configures its own repo) rather than hardcoding one. - services/settings.get_admin_setting(key): admin-scoped global read. - routes/plugin: GET /api/plugin/marketplace-url (any user) + PUT (admin). - SettingsView: Admin → 'Plugin marketplace' field to set it; MCP Access marketplace field falls back to the configured value. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
3ab16fcbdb |
feat(plugin): add /api/plugin/context push-channel endpoint + dogfood hook
Phase 1 of plan #755 (Scribe-as-plugin). Gives Scribe its own session-start push channel so always-on rules + active-project context surface without being asked — the gap behind 'I have to prompt for everything'. - services/plugin_context.build_session_context: renders always-on rule titles grouped by topic (under the 10k additionalContext cap; full text stays one list_always_on_rules/get_rule call away) + optional project goal/open-task count + a recall/update-over-create reflex line. Capped at 9000 chars. - routes/plugin GET /api/plugin/context (login_required already accepts Bearer fmcp_ keys; read scope suffices). - tests: titles-not-statements, project scoping, length cap (pure mocks). - scripts/scribe_session_context.sh: dogfood SessionStart hook, fail-open, reads url+token from .mcp.json. Superseded in Phase 2 by the plugin-bundled hook using userConfig. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |