feat(systems): a System names its files — path patterns stored, validated and matched (milestone 444 step 3, #4756)
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 15s
CI & Build / TypeScript typecheck (push) Successful in 54s
CI & Build / integration (push) Successful in 1m6s
CI & Build / Python tests (push) Successful in 1m50s
CI & Build / Build & push image (push) Successful in 43s
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 15s
CI & Build / TypeScript typecheck (push) Successful in 54s
CI & Build / integration (push) Successful in 1m6s
CI & Build / Python tests (push) Successful in 1m50s
CI & Build / Build & push image (push) Successful in 43s
A System gains path_patterns: globs relative to the repo root (* within one directory, ** across any depth, a plain directory covering everything under it). One service validates them for every door, so the web UI and MCP refuse the same bad pattern with the same message. systems_for_paths resolves paths to every active System that covers them, which step 4 (#4757) uses to deliver an area's rulings when its files are touched. - schema: systems.path_patterns JSONB NOT NULL default [] (migration 0113) - service: normalize_path_patterns, path_matches, systems_for_paths - routes + MCP create_system/update_system accept it; [] clears - web UI: a Files field in the create and edit forms, patterns on the card - backup carries it through export and restore - using-scribe reflex 7: tagging work keeps a System's files current Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,7 @@ tools are thin wrappers.
|
||||
Sentinels (match the milestone/task tool conventions):
|
||||
- name="" / description="" / color="" / status="" → "leave unchanged" on update
|
||||
- order_index=-1 → "leave unchanged" on update (0 is a valid order_index)
|
||||
- path_patterns=None → "leave unchanged" on update ([] clears them)
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -167,6 +168,7 @@ async def create_system(
|
||||
name: str,
|
||||
description: str = "",
|
||||
color: str = "",
|
||||
path_patterns: list[str] | None = None,
|
||||
) -> dict:
|
||||
"""Create a System (a reusable, self-describing subsystem/area) in a project.
|
||||
|
||||
@@ -195,11 +197,20 @@ async def create_system(
|
||||
under the System, so a ruling there reaches each session working in the
|
||||
area; a quote in a work log reaches one only if a search matches it.
|
||||
|
||||
`path_patterns` name the files that ARE the area: globs relative to the
|
||||
repo root, `*` within one directory, `**` across any depth, and a plain
|
||||
directory covering everything under it (`src/billing`,
|
||||
`frontend/src/components/Billing*.vue`). The description says what the
|
||||
area is for; the patterns say where it lives, so work touching those files
|
||||
can be traced to the System without a search. Give them when the area's
|
||||
files are known; a System without them still works for tagging.
|
||||
|
||||
Args:
|
||||
project_id: The project this system belongs to (required).
|
||||
name: Short label (required).
|
||||
description: What the system is and how it's used — a name is rarely enough.
|
||||
color: Optional UI accent (hex), or empty.
|
||||
path_patterns: The area's files as repo-relative globs, or omit.
|
||||
|
||||
Duplicate-gated like the other creates: if a System with the same
|
||||
normalized name already exists in this project (archived included), the
|
||||
@@ -237,7 +248,7 @@ async def create_system(
|
||||
system = await systems_svc.create_system(
|
||||
uid, project_id=project_id, name=name,
|
||||
description=description or None, color=color or None,
|
||||
canonical_id=applied,
|
||||
canonical_id=applied, path_patterns=path_patterns,
|
||||
)
|
||||
if system is None:
|
||||
raise ValueError(f"cannot create system in project {project_id} (no write access)")
|
||||
@@ -307,6 +318,7 @@ async def update_system(
|
||||
color: str = "",
|
||||
status: str = "",
|
||||
order_index: int = -1,
|
||||
path_patterns: list[str] | None = None,
|
||||
) -> dict:
|
||||
"""Update a System. Only explicitly provided fields change.
|
||||
|
||||
@@ -319,10 +331,14 @@ async def update_system(
|
||||
history. If the charter above it contradicts a ruling, fix that sentence
|
||||
in the same edit.
|
||||
|
||||
`path_patterns` also REPLACES the whole list, and `[]` clears it.
|
||||
|
||||
Args:
|
||||
status: 'active' or 'archived'. Archive a system to retire it without
|
||||
losing history; archived systems hide from default lists.
|
||||
order_index: display position (0-based); -1 = leave unchanged.
|
||||
path_patterns: The area's files as repo-relative globs (see
|
||||
create_system). Omit to leave unchanged; [] clears them.
|
||||
"""
|
||||
uid = current_user_id()
|
||||
fields: dict = {}
|
||||
@@ -336,6 +352,8 @@ async def update_system(
|
||||
fields["status"] = status
|
||||
if order_index >= 0:
|
||||
fields["order_index"] = order_index
|
||||
if path_patterns is not None:
|
||||
fields["path_patterns"] = path_patterns
|
||||
system = await systems_svc.update_system(uid, system_id, **fields)
|
||||
if system is None:
|
||||
raise ValueError(f"system {system_id} not found or no write access")
|
||||
|
||||
Reference in New Issue
Block a user