package models import "time" // AssistantAudit is one attempt to use an assistant tool. // // A new table rather than a column on anything: these rows are written on a // different schedule, read by different people, and are the only record of what // an assistant did on a merchant's behalf. Nothing else in the schema has that // job. // // ── Refusals are the interesting rows ─────────────────────────────────────── // // Every call is recorded, including the ones the registry said no to. A trail of // successes answers "did anything try to read another tenant?" with silence, // which reads exactly like "no". // // ── What is NOT stored ────────────────────────────────────────────────────── // // Not the question, and not the answer. The question is a shopkeeper's own words // and can carry anything they typed; the answer contains rows about their // business. Neither is needed to review what the assistant DID — the tool, the // arguments and the outcome are the act — and storing them would make this table // a second copy of the data it exists to police. type AssistantAudit struct { ID int64 `json:"id" gorm:"primaryKey;autoIncrement;column:id"` At time.Time `json:"at" gorm:"column:at;index"` Agent string `json:"agent" gorm:"column:agent;size:64"` Tool string `json:"tool" gorm:"column:tool;size:64;index"` Scope string `json:"scope" gorm:"column:scope;size:16"` Userid int `json:"userid" gorm:"column:userid;index"` Tenantid int `json:"tenantid" gorm:"column:tenantid;index"` // The arguments the handler actually received — validated and defaulted, // not as the model sent them. What ran is what is worth being able to read // back; what was asked for is only interesting when it differs, and the // refusal row records that. Args string `json:"args" gorm:"column:args;type:jsonb"` // ok | refused | failed | proposed | approved. // // `refused` is the guard saying no and `failed` is the handler breaking; // collapsing them would hide a broken tool inside a count of things working // as designed. `proposed` and `approved` are the two halves of a write, and // a `proposed` with no matching `approved` is somebody deciding not to. Outcome string `json:"outcome" gorm:"column:outcome;size:16;index"` Detail string `json:"detail" gorm:"column:detail"` Rows int `json:"rows" gorm:"column:rows"` // Milliseconds. Integer rather than an interval type so it can be averaged // and sorted without anybody having to know the database's duration syntax. Tookms int64 `json:"tookms" gorm:"column:tookms"` } func (AssistantAudit) TableName() string { return "assistantaudit" }