Commit Graph

13 Commits

Author SHA1 Message Date
bvandeusen 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
2026-06-30 12:41:45 -04:00
bvandeusen 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
2026-06-22 20:39:54 -04:00
bvandeusen 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
2026-06-22 20:31:07 -04:00
bvandeusen 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>
2026-06-14 23:22:17 -04:00
bvandeusen ee02ed37c1 feat(plugin): compaction-hygiene guidance — recommend safe compaction at seams
CI & Build / Python lint (push) Successful in 2s
CI & Build / TypeScript typecheck (push) Successful in 32s
CI & Build / Python tests (push) Successful in 48s
CI & Build / Build & push image (push) Successful in 59s
#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>
2026-06-14 15:41:24 -04:00
bvandeusen 33f9a0a4d4 feat(plugin): Phase 4 — Scribe Processes auto-surface as local skills
CI & Build / Python lint (push) Successful in 3s
CI & Build / TypeScript typecheck (push) Successful in 34s
CI & Build / Python tests (push) Successful in 52s
CI & Build / Build & push image (push) Successful in 1m3s
#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>
2026-06-14 13:00:32 -04:00
bvandeusen 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>
2026-06-13 16:49:10 -04:00
bvandeusen 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>
2026-06-13 16:14:27 -04:00
bvandeusen 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>
2026-06-13 15:20:18 -04:00
bvandeusen 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>
2026-06-13 15:01:35 -04:00
bvandeusen 8fe571e175 feat(plugin): resolve session project from git remote, not a pinned project_id
CI & Build / Python lint (push) Successful in 3s
CI & Build / TypeScript typecheck (push) Successful in 33s
CI & Build / Python tests (push) Successful in 51s
CI & Build / Build & push image (push) Successful in 1m0s
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>
2026-06-10 01:33:58 -04:00
bvandeusen 651119cfb0 fix(plugin): read API token from CLAUDE_PLUGIN_OPTION env, not user_config placeholder
The SessionStart push-channel hook passed the key via
SCRIBE_TOKEN="${user_config.api_token}" in hooks.json, but api_token is
sensitive:true. Claude Code keeps sensitive userConfig in the keychain and
does not interpolate it into hook command strings (only into mcpServers
headers), so the hook received the literal placeholder, sent it as the Bearer
token, and the context endpoint 401'd -> fail-open -> no context injected.

Read the harness-exported CLAUDE_PLUGIN_OPTION_<key> env vars instead (SCRIBE_*
still override for the settings.json dogfooding path), and treat any unexpanded
${...} literal as unset so the hook fails open cleanly instead of 401-ing.
Bump 0.1.1 -> 0.1.2 so installs refresh the cache.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 00:43:40 -04:00
bvandeusen 9924f873b9 feat(plugin): ship the Scribe Claude Code plugin in-repo (marketplace + plugin)
Per operator: the plugin lives in the app repo so it ships and versions in
lockstep with the app and the /api/plugin/context contract it targets (same
co-location rationale as the former in-repo MCP). A git-cloned marketplace
supports relative plugin sources, so the FabledScribe repo IS the marketplace.

- .claude-plugin/marketplace.json — source ./plugin
- plugin/.claude-plugin/plugin.json — userConfig (base URL, api key, project id)
- plugin/.mcp.json — http scribe server, ${user_config.*} substitution
- plugin/hooks/ — SessionStart push-channel hook (fail-open)
- plugin/skills/using-scribe — bootstrap skill
- plugin/README.md — install via the FabledScribe repo marketplace

Phase 2 of plan #755. Install/userConfig-substitution test pending.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 23:04:09 -04:00