← sirdocalot

The whole vocabulary

Widget library

summary

A title, a lead paragraph, and the points that matter. The default way to replace a wall of text.

PropTypeRequired
titlestringyesHeading
leadstringyesOne-paragraph summary
pointsarraynoKey points as strings

What the agent emits

{
  "widget": "summary",
  "props": {
    "title": "Why the nightly job doubled in cost",
    "lead": "The job did not change. Its input did: a partner started sending us every row each night instead of only the ones that changed, so we reprocess 40M rows to find 12,000 that matter.",
    "points": [
      "Cost went from about $30 a night to $240, starting on the 3rd",
      "No deploy went out that week; the code is unchanged",
      "Their API still exposes a `since` parameter, and we stopped sending it"
    ]
  }
}

What the operator sees

Why the nightly job doubled in cost

The job did not change. Its input did: a partner started sending us every row each night instead of only the ones that changed, so we reprocess 40M rows to find 12,000 that matter.

  • Cost went from about $30 a night to $240, starting on the 3rd
  • No deploy went out that week; the code is unchanged
  • Their API still exposes a `since` parameter, and we stopped sending it

key-facts

Labelled values in a two-column layout. For figures, config, and status at a glance.

PropTypeRequired
titlestringyesHeading
factsarrayyesEntries as {key,value}

What the agent emits

{
  "widget": "key-facts",
  "props": {
    "title": "Nightly job, last 7 days",
    "facts": [
      {
        "key": "Rows in",
        "value": "40,112,884"
      },
      {
        "key": "Rows changed",
        "value": "12,207 (0.03%)"
      },
      {
        "key": "Runtime",
        "value": "4h 12m, was 26m"
      },
      {
        "key": "Cost/night",
        "value": "$241, was $29"
      }
    ]
  }
}

What the operator sees

Nightly job, last 7 days

Rows in
40,112,884
Rows changed
12,207 (0.03%)
Runtime
4h 12m, was 26m
Cost/night
$241, was $29

findings

A list of findings, each in its own callout, coloured by tone. For reviews, audits, and diagnostics.

PropTypeRequired
titlestringyesHeading
findingsarrayyesItems as {tone,title,detail}; tone is info|warn|success|danger

What the agent emits

{
  "widget": "findings",
  "props": {
    "title": "Review of the checkout patch",
    "findings": [
      {
        "tone": "danger",
        "title": "Refund path is not idempotent",
        "detail": "A retried webhook issues a second refund. The provider retries on any 5xx, and we return 500 on a DB timeout."
      },
      {
        "tone": "warn",
        "title": "Currency is assumed to be GBP",
        "detail": "Three call sites multiply by 100 to get pence. Correct today, wrong the day we take euros."
      },
      {
        "tone": "success",
        "title": "Rollback was tested",
        "detail": "Restored from snapshot in staging on Monday and timed at 4 minutes."
      }
    ]
  }
}

What the operator sees

Review of the checkout patch

Refund path is not idempotent
A retried webhook issues a second refund. The provider retries on any 5xx, and we return 500 on a DB timeout.
Currency is assumed to be GBP
Three call sites multiply by 100 to get pence. Correct today, wrong the day we take euros.
Rollback was tested
Restored from snapshot in staging on Monday and timed at 4 minutes.

comparison

A table with a heading and an optional caption. For comparing options across the same dimensions.

PropTypeRequired
titlestringyesHeading
columnsarrayyesColumn headers
rowsarrayyesRows, each an array of cells matching columns
captionstringnoNote shown under the table

What the agent emits

{
  "widget": "comparison",
  "props": {
    "title": "Queue options",
    "columns": [
      "Option",
      "Ops burden",
      "Ordering",
      "Cost/month"
    ],
    "rows": [
      [
        "SQS",
        "None",
        "Best effort",
        "~$12"
      ],
      [
        "Redis Streams",
        "We run it",
        "Per stream",
        "~$40 (existing box)"
      ],
      [
        "Postgres table",
        "None new",
        "Total",
        "$0"
      ]
    ],
    "caption": "All three handle the volume. Nothing here is about throughput."
  }
}

What the operator sees

Queue options

OptionOps burdenOrderingCost/month
SQSNoneBest effort~$12
Redis StreamsWe run itPer stream~$40 (existing box)
Postgres tableNone newTotal$0

All three handle the volume. Nothing here is about throughput.

decision-matrix

A comparison table plus a stated recommendation. Use when the point is the conclusion, not the data.

PropTypeRequired
titlestringyesHeading
columnsarrayyesColumn headers, first one naming the option
rowsarrayyesRows, each an array of cells matching columns
recommendationstringnoWhat the agent recommends and why

What the agent emits

{
  "widget": "decision-matrix",
  "props": {
    "title": "Where the job should run",
    "columns": [
      "Option",
      "Change required",
      "Cost/night",
      "Risk"
    ],
    "rows": [
      [
        "Send `since` again",
        "One line",
        "~$29",
        "Low, reverts a regression"
      ],
      [
        "Keep full loads, bigger box",
        "Config",
        "~$180",
        "Low, buys nothing"
      ],
      [
        "Diff on our side",
        "New component",
        "~$35",
        "We own a cache nobody asked for"
      ]
    ],
    "recommendation": "Send `since` again. It restores the behaviour we had, and the other two treat a regression as a capacity problem."
  }
}

What the operator sees

Where the job should run

OptionChange requiredCost/nightRisk
Send `since` againOne line~$29Low, reverts a regression
Keep full loads, bigger boxConfig~$180Low, buys nothing
Diff on our sideNew component~$35We own a cache nobody asked for
Recommendation
Send `since` again. It restores the behaviour we had, and the other two treat a regression as a capacity problem.

timeline

Ordered events with a time and a description. For incident write-ups and change histories.

PropTypeRequired
titlestringyesHeading
eventsarrayyesEvents as {when,what}

What the agent emits

{
  "widget": "timeline",
  "props": {
    "title": "What happened on the 3rd",
    "events": [
      {
        "when": "02:14",
        "what": "Nightly job starts as usual"
      },
      {
        "when": "02:41",
        "what": "First run to exceed its previous longest, no alert fires"
      },
      {
        "when": "06:26",
        "what": "Job finishes, 4h 12m"
      },
      {
        "when": "09:05",
        "what": "Cost alert fires on the daily rollup, 12 hours after the cause"
      }
    ]
  }
}

What the operator sees

What happened on the 3rd

WhenWhat
02:14Nightly job starts as usual
02:41First run to exceed its previous longest, no alert fires
06:26Job finishes, 4h 12m
09:05Cost alert fires on the daily rollup, 12 hours after the cause

annotated-code

Code blocks each preceded by the note explaining them. For diffs, snippets, and walkthroughs.

PropTypeRequired
titlestringyesHeading
notesarrayyesItems as {note,code,language}

What the agent emits

{
  "widget": "annotated-code",
  "props": {
    "title": "The line that changed the bill",
    "notes": [
      {
        "note": "The parameter was dropped when the client was regenerated. Nothing failed, because the API treats it as optional and returns everything.",
        "code": "- rows = client.list_orders(since=last_run_at)\\n+ rows = client.list_orders()",
        "language": "diff"
      },
      {
        "note": "The retry that turns one refund into two: any 5xx is retried by the provider, and a DB timeout returns 500.",
        "code": "except DatabaseTimeout:\\n    return Response(status=500)",
        "language": "python"
      }
    ]
  }
}

What the operator sees

The line that changed the bill

The parameter was dropped when the client was regenerated. Nothing failed, because the API treats it as optional and returns everything.

- rows = client.list_orders(since=last_run_at)\n+ rows = client.list_orders()

The retry that turns one refund into two: any 5xx is retried by the provider, and a DB timeout returns 500.

except DatabaseTimeout:\n    return Response(status=500)

ask

One question, with optional context above it. The smallest collecting brief.

PropTypeRequired
titlestringyesHeading
contextstringnoBackground shown before the question
fieldobjectyesA field spec: {kind,id,label,required,...}

What the agent emits

{
  "widget": "ask",
  "props": {
    "title": "Before I change the billing code",
    "context": "The refund path is not idempotent: a retried webhook issues a second refund. Fixing it properly means storing an idempotency key per refund, which is a schema change.",
    "field": {
      "kind": "choice",
      "id": "approach",
      "label": "Which way should I go?",
      "required": true,
      "multiple": false,
      "options": [
        {
          "value": "schema",
          "label": "Idempotency key in the schema, do it properly"
        },
        {
          "value": "guard",
          "label": "Guard on the existing refund id for now"
        }
      ]
    }
  }
}

What the operator sees

Before I change the billing code

The refund path is not idempotent: a retried webhook issues a second refund. Fixing it properly means storing an idempotency key per refund, which is a schema change.

survey

Several questions under one heading. Field specs are passed through as given.

PropTypeRequired
titlestringyesHeading
introstringnoText shown before the questions
questionsarrayyesField specs

What the agent emits

{
  "widget": "survey",
  "props": {
    "title": "Before I start the migration",
    "intro": "Three things I cannot answer from the code.",
    "questions": [
      {
        "kind": "choice",
        "id": "window",
        "label": "Which maintenance window?",
        "required": true,
        "multiple": false,
        "options": [
          {
            "value": "sat",
            "label": "Saturday 02:00"
          },
          {
            "value": "sun",
            "label": "Sunday 02:00"
          },
          {
            "value": "none",
            "label": "No window, do it live"
          }
        ]
      },
      {
        "kind": "boolean",
        "id": "backfill",
        "label": "Backfill historical rows in the same pass?",
        "required": false
      },
      {
        "kind": "text",
        "id": "anything",
        "label": "Anything I have not asked about?",
        "required": false,
        "long": true
      }
    ]
  }
}

What the operator sees

Before I start the migration

Three things I cannot answer from the code.

approval

A summary of what is proposed, a three-way decision, and room for reasoning.

PropTypeRequired
titlestringyesHeading
proposalstringyesWhat is being proposed
detailstringnoLonger explanation

What the agent emits

{
  "widget": "approval",
  "props": {
    "title": "Sign-off needed",
    "proposal": "Re-enable the `since` parameter on the partner client and redeploy the nightly job tonight.",
    "detail": "One line, reverting an unintended change. It restores the behaviour we had before the 3rd. Worst case is that a night is missed and the next run picks it up, because the parameter is a lower bound rather than a filter."
  }
}

What the operator sees

Sign-off needed

Proposed
Re-enable the `since` parameter on the partner client and redeploy the nightly job tonight.

One line, reverting an unintended change. It restores the behaviour we had before the 3rd. Worst case is that a night is missed and the next run picks it up, because the parameter is a lower bound rather than a filter.

pick-one

A choice between options, with room to say why. For 'which of these should I do'.

PropTypeRequired
titlestringyesHeading
contextstringnoBackground
optionsarrayyesOptions as {value,label}
questionstringyesThe question shown above the options

What the agent emits

{
  "widget": "pick-one",
  "props": {
    "title": "Two ways to fix the refund bug",
    "context": "Both stop the double refund. They differ in what we owe afterwards.",
    "question": "Which should I build?",
    "options": [
      {
        "value": "schema",
        "label": "Idempotency key column, unique index, provider key stored"
      },
      {
        "value": "guard",
        "label": "Check for an existing refund on the order before issuing"
      },
      {
        "value": "queue",
        "label": "Serialise refunds through a single worker"
      }
    ]
  }
}

What the operator sees

Two ways to fix the refund bug

Both stop the double refund. They differ in what we owe afterwards.

rate-items

The same rating scale applied to several items. For prioritisation and confidence checks.

PropTypeRequired
titlestringyesHeading
itemsarrayyesItems as {id,label}
scalenumbernoHighest rating, default 5

What the agent emits

{
  "widget": "rate-items",
  "props": {
    "title": "How much do these worry you?",
    "items": [
      {
        "id": "double-refund",
        "label": "Refunds can be issued twice on retry"
      },
      {
        "id": "currency",
        "label": "Currency assumed to be GBP in three places"
      },
      {
        "id": "cost",
        "label": "Nightly job costing 8x what it did"
      },
      {
        "id": "alerting",
        "label": "Cost alert fired 12 hours after the cause"
      }
    ],
    "scale": 5
  }
}

What the operator sees

How much do these worry you?