Files
FabledScribe/alembic/versions/0116_moment_mappings.py
T
bvandeusenandClaude Opus 5.5 cf3de5bae1
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
feat(moments): actions map onto moments, with in-session corrections (milestone 458 step 2, #4920)
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>
2026-10-05 10:58:00 -04:00

48 lines
1.6 KiB
Python

"""moment_mappings — an install's own corrections to which actions reach which
moment (milestone 458 step 2)
Revision ID: 0116
Revises: 0115
Create Date: 2026-10-05
The moments and their default actions are code; this table holds what an
install adds ("make ship" is a deliver here) and the defaults it switches off.
User-owned configuration, so it cascades with the user. No CHECK on `effect`,
which the service validates (rule 36).
"""
import sqlalchemy as sa
from alembic import op
revision = "0116"
down_revision = "0115"
branch_labels = None
depends_on = None
def upgrade() -> None:
op.create_table(
"moment_mappings",
sa.Column("id", sa.BigInteger(), primary_key=True),
sa.Column(
"created_at", sa.DateTime(timezone=True), nullable=False,
server_default=sa.text("now()"),
),
sa.Column(
"user_id", sa.Integer(),
sa.ForeignKey("users.id", ondelete="CASCADE"), nullable=False,
),
sa.Column("tool", sa.Text(), nullable=False),
sa.Column("match", sa.Text(), nullable=False, server_default=""),
sa.Column("moment", sa.Text(), nullable=False),
sa.Column("effect", sa.Text(), nullable=False, server_default="add"),
sa.Column("reason", sa.Text(), nullable=False, server_default=""),
sa.Column("actor", sa.Text(), nullable=False, server_default="model"),
sa.UniqueConstraint(
"user_id", "tool", "match", "moment", name="uq_moment_mapping",
),
)
def downgrade() -> None:
op.drop_table("moment_mappings")