wip(394): steps 6+7 — backend path and instruction surfaces

This commit is contained in:
2026-09-11 15:15:33 -04:00
parent 690ca0306e
commit c149ef31a3
28 changed files with 260 additions and 738 deletions
-7
View File
@@ -202,11 +202,6 @@ async def write_path_prior_art():
or `canon:<snippet_id>`) already named this
session by the ledger arm (#2900); its own
channel, like the two above.
rules_etag (opt) — the marker the session was given when it loaded
its always-on rules (milestone 323). Sent back
so the server can say whether those rules have
MOVED since. Absent means the hook has nothing
stored, which is silence, not a mismatch.
shapes (opt) — comma-separated `kind:name` definitions the hook
found in (or enclosing) the payload, kind being
css|sym. The shape ledger's write-path feed
@@ -226,7 +221,6 @@ async def write_path_prior_art():
p.strip() for p in (request.args.get("exclude_derive") or "").split(",") if p.strip()
]
exclude_rule_ids = _int_list(request.args.get("exclude_rule_ids"))
rules_etag = (request.args.get("rules_etag") or "").strip()
shapes = _parse_shapes(request.args.get("shapes") or "")
api_key = getattr(g, "api_key", None)
may_stamp = api_key is None or getattr(api_key, "scope", "") == "write"
@@ -238,7 +232,6 @@ async def write_path_prior_art():
repo_key=repo_bindings_svc.normalize_repo_key(repo) if repo else "",
exclude_derive=exclude_derive,
exclude_rule_ids=exclude_rule_ids,
rules_etag=rules_etag,
)
return jsonify(result)
+1 -1
View File
@@ -99,7 +99,7 @@ async def create_project_route():
@login_required
async def decide_inception_route(project_id: int):
"""Record (or re-record) what a project inherits — milestone 297.
Body: the choices object {exclude_always_on_rulebooks, subscribe_rulebooks,
Body: the choices object {subscribe_rulebooks,
design_system_id, seed_systems}; owner-only."""
uid = get_current_user_id()
data = await request.get_json() or {}
+3 -32
View File
@@ -55,7 +55,7 @@ async def get_rulebook(rulebook_id: int):
@login_required
async def update_rulebook(rulebook_id: int):
data = await request.get_json() or {}
fields = {k: v for k, v in data.items() if k in ("title", "description", "always_on")}
fields = {k: v for k, v in data.items() if k in ("title", "description")}
rb = await rulebooks_svc.update_rulebook(rulebook_id, get_current_user_id(), **fields)
if rb is None:
return jsonify({"error": "rulebook not found"}), 404
@@ -177,7 +177,6 @@ async def create_rule(topic_id: int):
how_to_apply=data.get("how_to_apply", ""),
order_index=data.get("order_index", 0),
when_to_apply=data.get("when_to_apply", ""),
tier=data.get("tier", "always_on"),
# The human door carries `kind` too, and without the MCP door's
# required provenance: an operator editing their own preference
# owes nobody an explanation. That requirement is about auditing
@@ -217,7 +216,7 @@ async def update_rule(rule_id: int):
fields = {
k: v for k, v in data.items()
if k in ("title", "statement", "why", "how_to_apply", "order_index",
"when_to_apply", "tier", "kind", "arose_from_id",
"when_to_apply", "kind", "arose_from_id",
"verify_with", "expires_when")
}
# No clear_fields here: a form sends "" for an emptied input, and the
@@ -395,32 +394,6 @@ async def unsuppress_project_topic(project_id: int, topic_id: int):
return "", 204
@rulebooks_bp.post("/projects/<int:project_id>/exclusions/rulebooks/<int:rulebook_id>")
@login_required
async def exclude_project_rulebook(project_id: int, rulebook_id: int):
"""Opt the project out of a whole always-on rulebook (milestone 297)."""
try:
await rulebooks_svc.exclude_always_on_rulebook_for_project(
project_id=project_id, rulebook_id=rulebook_id, user_id=get_current_user_id(),
)
except ValueError as exc:
msg = str(exc)
return jsonify({"error": msg}), (400 if "not always-on" in msg else 404)
return "", 204
@rulebooks_bp.delete("/projects/<int:project_id>/exclusions/rulebooks/<int:rulebook_id>")
@login_required
async def include_project_rulebook(project_id: int, rulebook_id: int):
try:
await rulebooks_svc.include_always_on_rulebook_for_project(
project_id=project_id, rulebook_id=rulebook_id, user_id=get_current_user_id(),
)
except ValueError as exc:
return jsonify({"error": str(exc)}), 404
return "", 204
@rulebooks_bp.post("/projects/<int:project_id>/rules")
@login_required
async def create_project_rule(project_id: int):
@@ -440,7 +413,6 @@ async def create_project_rule(project_id: int):
how_to_apply=data.get("how_to_apply", ""),
order_index=data.get("order_index", 0),
when_to_apply=data.get("when_to_apply", ""),
tier=data.get("tier", "always_on"),
# The human door carries `kind` too, and without the MCP door's
# required provenance: an operator editing their own preference
# owes nobody an explanation. That requirement is about auditing
@@ -464,7 +436,7 @@ async def create_project_rule(project_id: int):
async def rules_due_for_verification():
"""Rules that carry a check, oldest verification first, never-checked top.
Query params: older_than_days, tier, never_only. A rule with no
Query params: older_than_days, never_only. A rule with no
`verify_with` never appears — it is a decision, not a fact.
"""
uid = get_current_user_id()
@@ -477,7 +449,6 @@ async def rules_due_for_verification():
rules = await rulebooks_svc.rules_due_for_verification(
uid,
older_than_days=older,
tier=args.get("tier", ""),
never_only=args.get("never_only", "").lower() in ("1", "true", "yes"),
)
except ValueError as exc: