Documentation ziprack/1SDK 0.3.0

Capabilities

Activity

activity@1capability contract

On this page

Introduction

A statement is an append-only record that an actor did a verb on an activity with a result. Statements are your applet's audit trail. Because every statement has the same shape, they are also the one export that works identically for every applet on every Rack.

Statements are never updated and never deleted. Re-answering is normal: reporting reads the last answered statement per activity per attempt.

db@1 does not replace this. Statements are what happened, while records are what is currently true.

Declaring Activities

Every question you report on must be declared in your manifest's activities array, and at least one activity is required:

JSON
"activities": [
    { "id": "intro.connection", "type": "rating", "label": "How connected do you feel to patients?",
      "scale": { "min": 1, "max": 6 }, "sequence": 1 },
    { "id": "rd.corine.reaction", "type": "text", "label": "What stood out to you about Corine's story?",
      "segment": "track", "sequence": 2 }
]

Each activity accepts the following fields:

Field Rules
id Required, max 150, unique. This is the data contract.
type Required, max 100. Drives how reporting renders and exports the value.
label Required, max 1000. Becomes the export column header and the analysis heading.
scale Optional object, for rating and scale types.
segment Optional. Must name a declared segment key, or be null for shared.
sequence Optional integer ≥ 0. Controls export column order; defaults to array position.

Use stable, dotted, lowercase ids, such as {phase}.{topic}.{question}. An undeclared id still records, but it exports unlabeled and appended after every declared column. That makes it a useful safety net, but never a plan.

Emitting Statements

Typically, you will use the answer method for a committed answer and the progress method for navigation. Of course, you may also emit any statement you like with the statement method:

TypeScript
// The common case: a committed answer.
await Rack.activity.answer('intro.connection', 5);

// Navigation and progress. Fire and forget.
void Rack.activity.progress('rd.chapter_2');

// Anything else.
void Rack.activity.statement({
    verb: 'viewed',
    activity: { id: 'rd.corine.video', type: 'media' },
    result: { duration_seconds: 184 },
    context: { autoplay: false },
});

answer() emits verb: 'answered' with the value in result.response, which is the only field reporting reads, and fills activity.type from your manifest declaration. progress() emits verb: 'progressed' with activity.type: 'step'.

Verbs must match ^[a-z][a-z0-9_.:-]*$. occurred_at defaults to now, in ISO 8601. You should only pass it yourself when replaying something that genuinely happened earlier.

A statement emitted while staff preview the applet is stored with a preview flag, with or without an attempt, so the audit trail can leave it out. Your applet does nothing different.

Reading Your Declarations

You may read your own declarations back from launch data whenever you need them:

TypeScript
Rack.activity.activities();                  // LaunchActivity[]
Rack.activity.activity('intro.connection');  // { id, type, label, scale, … } | null

Rendering a question from its declared label and scale keeps the participant's wording and the export header identical by construction. It is a small discipline that pays for itself the first time someone edits a question.

Activity Types

type is free text, but reporting understands these types:

type result.response Used for
text string Free text. Eligible for response analysis.
rating number within scale Likert or self-rating. Eligible for metrics.
scale number within scale Semantic alias of rating.
choice string key, or array of keys Single or multi-select.
custom any JSON Anything else. Exported JSON-encoded.

Metrics

A metric pairs a pre and a post rating of the same thing, so reporting can show a single shift rather than two columns to eyeball. You may declare metrics in your manifest's reporting block:

JSON
"reporting": {
    "metrics": [
        { "key": "connection", "label": "Connection",
          "pre": "intro.connection", "post": "outro.connection" }
    ]
}

pre and post must be declared activity ids. Metrics require attempt@1, because they are computed over completed attempts. Both activities should use the same scale, or the shift means nothing.

Designing for the Export

Because every statement has the same shape, a Rack reports every applet's answers the same way, with one column per activity, and your declarations decide what that export has to work with. That yields three rules worth writing on the wall:

  1. id is the data contract. Renaming one between versions splits a column: old attempts keep the old column, and new attempts fill a new one.
  2. label is the header. Renaming one is free, and changes only how the column reads.
  3. sequence is the column order. Set it deliberately, so the export reads top to bottom the way the experience does.

Write labels as full sentences, exactly as the participant sees them. Someone reading the spreadsheet six months later has no other copy of the question.