feat(moments): actions map onto moments, with in-session corrections (milestone 458 step 2, #4920)
CI & Build / Plugin hooks (push) Successful in 18s
CI & Build / Python lint (push) Successful in 3s
CI & Build / TypeScript typecheck (push) Successful in 55s
CI & Build / integration (push) Successful in 1m23s
CI & Build / Python tests (push) Successful in 2m0s
CI & Build / Build & push image (push) Successful in 36s
CI & Build / Plugin hooks (push) Successful in 18s
CI & Build / Python lint (push) Successful in 3s
CI & Build / TypeScript typecheck (push) Successful in 55s
CI & Build / integration (push) Successful in 1m23s
CI & Build / Python tests (push) Successful in 2m0s
CI & Build / Build & push image (push) Successful in 36s
moment_actions.resolve(tool, input) names every moment a call reaches and the action that reached it. One call can reach several: kubectl apply is a run, a deliver and a reach outside the workspace. Command tools match by how each segment of the line starts, with a word boundary; other tools by field=value arguments. The MCP server prefix and case are ignored. 56 shipped defaults cover the harness tools, Scribe tools and common command shapes. moment_mappings (migration 0116) holds what an install adds and the defaults it switches off. A removal is a stored row, so an upgrade does not switch the default back on. Per the operator ruling, corrections happen in the session: map_action and unmap_action (write tools) return now_reaches so the fix can be confirmed in the same reply. list_moments now shows each moment's actions on this install. REST mirrors both doors, recorded as human. Backup v21 carries the mappings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -17,6 +17,7 @@ import logging
|
||||
from quart import Blueprint, jsonify, request
|
||||
|
||||
from scribe.auth import get_current_user_id, login_required
|
||||
from scribe.services import moment_actions as moment_actions_svc
|
||||
from scribe.services import moments as moments_svc
|
||||
from scribe.services.retrieval_tuning import set_dial, current_settings, tuning_history
|
||||
|
||||
@@ -110,10 +111,43 @@ async def tuning_history_route():
|
||||
@retrieval_bp.route("/moments", methods=["GET"])
|
||||
@login_required
|
||||
async def moments_route():
|
||||
"""The moments of work that rules mount on (milestone 458).
|
||||
"""The moments of work that rules mount on (milestone 458), with the
|
||||
actions that reach each one on this install.
|
||||
|
||||
The same catalog `list_moments` returns, from the same service: the
|
||||
Settings view of mounts and mappings reads its vocabulary here, so the two
|
||||
doors cannot name different moments.
|
||||
The same payload `list_moments` returns, from the same services, so the
|
||||
session and the Settings view cannot name different moments.
|
||||
"""
|
||||
return jsonify(moments_svc.catalog())
|
||||
out = moments_svc.catalog()
|
||||
out.update(await moment_actions_svc.actions_by_moment(get_current_user_id()))
|
||||
return jsonify(out)
|
||||
|
||||
|
||||
async def _mapping_change(change):
|
||||
"""map/unmap from the browser: the MCP tools' service, recorded as human."""
|
||||
data = await request.get_json()
|
||||
if not isinstance(data, dict):
|
||||
return jsonify({"error": "Expected a JSON object"}), 400
|
||||
if not data.get("tool") or not data.get("moment"):
|
||||
return jsonify({"error": "tool and moment are required"}), 400
|
||||
try:
|
||||
result = await change(
|
||||
get_current_user_id(), str(data["tool"]), str(data.get("match") or ""),
|
||||
str(data["moment"]), reason=str(data.get("reason") or ""), actor="human",
|
||||
)
|
||||
except ValueError as e:
|
||||
# Unknown moment, a match the tool cannot take, nothing to remove —
|
||||
# the service says what would work, so pass it through.
|
||||
return jsonify({"error": str(e)}), 400
|
||||
return jsonify(result)
|
||||
|
||||
|
||||
@retrieval_bp.route("/moments/mappings", methods=["POST"])
|
||||
@login_required
|
||||
async def map_action_route():
|
||||
return await _mapping_change(moment_actions_svc.map_action)
|
||||
|
||||
|
||||
@retrieval_bp.route("/moments/mappings", methods=["DELETE"])
|
||||
@login_required
|
||||
async def unmap_action_route():
|
||||
return await _mapping_change(moment_actions_svc.unmap_action)
|
||||
|
||||
Reference in New Issue
Block a user