Step 4 of #278, product half. The audit that motivated it: one System in
project 2, thirty records tagged, nothing since July 28 — three days after the
feature landed. Not a discipline failure; retrieval was completely blind to
the association (zero references in embeddings, knowledge, search, auto-inject,
or enter_project), so tagging was a write-side label with no read-side payoff,
and labels nobody reads don't get maintained.
Three changes, ordered by what makes the others workable:
1. enter_project returns the project's Systems (id, name, first line of the
charter). Load-bearing for the tagging instruction: you cannot ask an agent
to check a record against a vocabulary it never sees. Trimmed because it
rides on every session start; the full charter stays get_system's job.
Present-and-empty rather than absent when a project has none — "no named
areas yet" is information the create-the-System instruction acts on.
2. search accepts system_id, MCP and REST (#33). Implemented once in
semantic_search_notes as an EXISTS against record_systems — an association
filter deciding candidate-set membership before scoring, like project_id,
not a ranking signal. The REST route's missing project filter stays #2463's:
it carries a default-scope UI decision this change must not preempt.
3. The instructions (#119, _INSTRUCTIONS + using-scribe skill; plugin 0.1.25
for the cache):
- Tag as you write, with an executable test — "would someone investigating
that subsystem want this in the pile list_system_records returns?" —
rather than "tag appropriately", which is what died.
- Create the System when the area has no record: the two-or-more test
snippets use, plus "don't wait to be asked to name an area that plainly
exists", because the agent's default was leaving un-modelled areas
un-modelled forever.
- State vs chronicle: dev-logs are written once and never rewritten; durable
findings live in the System's reference note, updated in place — safe
because note versions are the changelog, which has existed since the
feature shipped and was never named as one.
list_system_records' docstring now sells it as the way to READ a subsystem,
reference note first. No auto-inject boost by System — vocabulary and filter
first, measure before adding ranking behaviour (the #2486 lesson).
Refs #278, #2546
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>