# Seneca Starter Kit

Synthetic examples, schemas and starter code for one question: how does
evidence from one learning product (Product A) give another product
(Product B) a useful first session, with the learner's permission, without
the two products ever connecting?

Everything here is synthetic. Every product, learner and item is fictional.
Please don't run the kit on real student data.

Walkthrough: https://vindicaseneca.com/developers/starter-kit
Technical reference: https://vindicaseneca.com/developers/starter-kit/docs

**Sandbox evaluation only.** Use the kit to evaluate an integration with
Seneca, in a test environment, with synthetic data. No production use, no
redistribution. The full terms are in [LICENSE.md](LICENSE.md).

## Run it

Node 18 or later. No install, no dependencies, no network calls.

```bash
node kit/cli.mjs --check
node kit/cli.mjs examples/assessment-to-tutoring/product-a-export.json
node kit/cli.mjs examples/session-to-session/product-a-statements.xapi.json --product product-a --kind "AI tutoring app" --setting tutoring
node kit/cli.mjs examples/app-to-app/product-a-export.csv --product product-a --kind "vocabulary and grammar app"
node kit/cli.mjs examples/more-inputs/caliper-envelope.json
node kit/cli.mjs examples/essay-to-writing-tutor/product-a-export.json
node kit/product-b-start.mjs
python python/product_b_start.py
```

### Your own CSV or JSON rows

If your export uses its own column names, let the kit propose a mapping,
review it, then transform through it:

```bash
node kit/cli.mjs --suggest your-export.csv > mapping.json
node kit/cli.mjs your-export.csv --map mapping.json
```

The mapping (`seneca-starter-mapping@0.1`) says which of your columns is
which kit field, how your value labels translate (for example `Right` to
`correct`), and which timezone to apply to timestamps that have none.
Columns you don't map stay in your product and are listed by name. See
`examples/more-inputs/custom-export.csv` and its mapping.

`kit/transform.mjs` also runs in a browser. The walkthrough page imports
the same file.

## The flow

1. **Product A records events** in its own format: a JSON export, a CSV,
   xAPI statements or a Caliper envelope.
2. **The transform runs on Product A's side.** It reads only the fields it
   knows, replaces learner, session and item IDs with pseudonyms, and
   leaves everything else behind. Every left-behind field is listed by
   name. Question text, answers and names never leave Product A. Payloads
   containing what looks like an email, phone number or national ID number
   are refused.
3. **Seneca keeps a record** that separates what was observed, what
   Product A concluded (attributed to Product A), and Seneca's own readings
   with confidence, plus provenance, scope and limitations.
4. **Product B asks for context** for one purpose, under the learner's
   authorization. It receives a summary with limits: what it may use it
   for and what it should not infer. It never receives Product A's rows,
   item IDs or content, and it never talks to Product A. What Product B
   teaches next is Product B's decision.

## Files

| Path | What it is |
|---|---|
| `LICENSE.md` | Sandbox evaluation terms |
| `kit/transform.mjs` | Reference transform and context preview (browser + Node) |
| `kit/validate.mjs` | Small JSON Schema checker for the kit's schemas |
| `kit/cli.mjs` | Command line: transform a file, check the examples |
| `kit/product-b-start.mjs` | How Product B might plan a first session from the context |
| `python/product_b_start.py` | The same Product B example in Python (standard library only) |
| `schemas/starter-mapping.schema.json` | Column mapping for a CSV with its own column names |
| `schemas/starter-evidence.schema.json` | What leaves Product A after the transform |
| `schemas/starter-record.schema.json` | How the Seneca record reads (illustrative) |
| `schemas/starter-context.schema.json` | What Product B receives (illustrative) |
| `examples/scenarios.json` | The four worked examples and their transform options |
| `examples/*/product-a-*` | Synthetic Product A inputs |
| `examples/*/evidence.json` | Transform output, generated by `kit/cli.mjs --write-examples` |
| `examples/*/seneca-record.json` | Illustrative Seneca record (hand-written) |
| `examples/*/product-b-request.json` | Illustrative Product B request |
| `examples/*/product-b-context.json` | Illustrative context Product B receives (hand-written) |
| `examples/more-inputs/caliper-envelope.json` | A Caliper 1.2 sample for the transform |
| `examples/more-inputs/custom-export.csv` | A CSV with its own column names, plus `custom-export.mapping.json` |

## Input shapes the kit reads

**JSON export** (`native-json`): an object with `export`, `student.id`,
optional `session`, `skill_labels`, `skill_report`, and `responses[]`
with `question_id`, `skill_tag`, `outcome`, `points`, `max_points`,
`seconds`, `attempt`, `hint_used` or `assistance`, `answered_at`.

**JSON export (writing)** (`writing-json`): an object with `export`,
`student.id`, `assignments` (`label`, `genre`, `setting`), `criteria`
(`label`) and `submissions[]` with `submission_id`, `assignment_id`,
`draft`, `revises`, `submitted_at`, `word_count`, `ai_assistance` and
`scores[]` (`criterion`, `score`, `max`, `rater.role`, `scored_at`). The
essay text, prompts, comments and rater identities are left behind. Every score becomes a judgment attributed to its rater
(`teacher`, `ai_grader`, `peer`, `self`); a score without a rater role
is refused.

**JSON rows** (`json-rows`): an array of flat objects, read exactly like
CSV rows. Keys holding nested objects or arrays are left behind.

**CSV**: one row per answered item. Required columns `learner_id`,
`item_id`, `occurred_at`. Optional: `session_id`, `skill_tag`,
`skill_label`, `aligned_to`, `item_type`, `outcome`, `points`,
`max_points`, `seconds`, `attempt`, `assistance`, `setting`, `timed`,
`self_rating`. Other columns are left behind and listed.

**xAPI**: statements with an account actor, the ADL `answered` or
`completed` verb, an activity object, a timestamp, and optional
`result.success`, `result.score` (raw/min/max), `result.duration`,
category activities as skill tags and a `registration` as the session.

**Caliper 1.2**: an envelope of `AssessmentItemEvent` (Completed) and
`GradeEvent` (Graded, with a Score) events.

One learner per payload, up to 2,000 events, up to 1 MiB.

## What is illustrative

- The three formats in `schemas/` are the kit's teaching formats. They are
  not Seneca's production contract, which is shared in a sandbox under a
  pilot agreement. A pilot maps your events to that contract.
- `seneca-record.json` and `product-b-context.json` were written by hand.
  Seneca forms its readings and confidence on its own side. The kit does not
  compute them and does not describe how they are formed.
- Pseudonyms in the kit are an unkeyed demo hash. Production references are
  keyed and differ for every recipient.
- `product-b-start.mjs` reads a local file. There is no public endpoint.

## What Seneca supports today

- **Partner events** in Seneca's own event format: served to approved pilot
  partners.
- **CSV**: mapped to that event format during a pilot.
- **xAPI 2.0.0 and 1.0.3, Caliper 1.2**: SDK adapters exist for a defined
  subset (answered/completed statements; AssessmentItemEvent Completed and
  GradeEvent Graded). Hosted intake for them is not open yet. This is not a
  certification.
- **AI assistants over MCP**: live at `https://vindicaseneca.com/mcp` for
  learner-authorized reads of Seneca's own record.

## Next step

When you can picture this in your product, email
kevinchoi@vindicaseneca.com to review an integration, discuss a paid
pilot, or send a synthetic example for mapping. Synthetic data only, please.

(c) 2026 Vindica Inc. Sandbox evaluation only. See [LICENSE.md](LICENSE.md).
