Pack draft format
The felspar.pack-draft JSON format that check_pack and propose_pack accept - purpose, subjects, questions, outcomes, escalations and test cases - with every field and limit.
A pack draft describes one job: what it is for and when to use it, what each case is about, the questions an agent answers from evidence, the outcomes, the rules that pick an outcome, where undecided cases escalate, and test cases. check_pack and propose_pack take it, together with the source_object_id of the document it quotes. Felspar locates every quote and builds the full proposal from it, so nobody writes fingerprints, passages or classifier settings by hand.
The same guide is available to any assistant through the get_pack_guide tool.
Format versions
schema |
Adds | Status |
|---|---|---|
felspar.pack-draft/v2 |
A required purpose, plus useWhen, notFor, owner, subjects, subject and escalations |
Current |
felspar.pack-draft/v1 |
Number and date questions with quoted thresholds, and scoring | Accepted; numbers, dates and scoring are in preview |
felspar.pack-draft/v0 |
Answer-choice questions, outcomes, rules and test cases | Accepted |
Every v0 draft is also a valid v1 draft, and v2 is v1 with a required purpose. Subjects, escalations and the job fields are optional before v2. A draft that doesn’t use them builds exactly the same release as before.
Shape
A fictional support-desk policy, drafted as v2:
{
"schema": "felspar.pack-draft/v2",
"job": "triage-ticket",
"purpose": "<quote: why the job exists and what it resolves>",
"useWhen": ["<quote>"],
"notFor": ["<quote>"],
"owner": "<quote: the accountable role>",
"subjects": [
{ "id": "ticket", "definition": "<quote>", "identifier": "help desk ticket number", "example": "48213",
"relationships": [
{ "name": "customer", "subject": "customer", "cardinality": "one", "definition": "<quote>" }
] },
{ "id": "customer", "definition": "<quote>", "identifier": "account ID", "example": "cus-9f2" }
],
"subject": "ticket",
"escalations": [
{ "id": "human-review", "quote": "<quote>" },
{ "id": "incident-review", "quote": "<quote>" }
],
"evidenceKinds": ["ticket", "account"],
"maxEvidenceAgeDays": 30,
"questions": [
{
"key": "topic",
"meaning": "<quote: the question>",
"answers": [
{ "value": "charge", "meaning": "<quote>", "inclusions": ["<quote>"], "exclusions": ["<quote>"], "examples": ["<quote>"] },
{ "value": "failure", "meaning": "<quote>", "examples": ["<quote>"] },
{ "value": "other", "meaning": "<quote>", "noneOfThese": true }
],
"whereToLook": ["<quote>"]
},
{
"key": "premium",
"about": "customer",
"meaning": "<quote>",
"answers": [
{ "value": "yes", "meaning": "<quote>" },
{ "value": "no", "meaning": "<quote>" }
]
}
],
"outcomes": [
{ "id": "billing", "nextStep": "<quote: what to do next>" },
{ "id": "on-call", "nextStep": "<quote>", "escalate": "incident-review" },
{ "id": "general", "nextStep": "<quote>" }
],
"whenUnresolved": { "quote": "<quote: what to do when nothing is decided>", "escalate": "human-review" },
"attempts": { "max": 2, "escalate": "human-review" },
"rules": [
{ "id": "charge", "meaning": "<quote>", "when": { "topic": "charge" }, "outcome": "billing" },
{ "id": "failure", "meaning": "<quote>", "when": { "topic": "failure" }, "outcome": "on-call" },
{ "id": "other", "meaning": "<quote>", "when": { "topic": "other" }, "outcome": "general" }
],
"cases": [
{ "id": "invoice", "why": "<where the document says so>", "subject": { "id": "48213" },
"facts": { "topic": "charge", "premium": "no" }, "expect": { "outcome": "billing", "rule": "charge" } },
{ "id": "outage", "why": "<where the document says so>",
"facts": { "topic": "failure", "premium": "yes" },
"expect": { "outcome": "on-call", "rule": "failure", "escalation": "incident-review" } }
]
}
Fields
The job
| Field | Required | Rules |
|---|---|---|
schema |
Yes | "felspar.pack-draft/v2" (or v1, v0) |
job |
Yes | The job ID agents use, such as triage-ticket or review-nda |
purpose |
Yes in v2 | Quote: why the job exists and what it resolves |
useWhen, notFor |
No | 1 to 8 quotes each: when the document says to use the job, and when not |
owner |
No | Quote: the accountable role, only if the document names one |
evidenceKinds |
Yes | 1 to 8 kinds of evidence that count, such as ticket, contract or company-site. These become the source classes agents send. |
maxEvidenceAgeDays |
Yes | 1 to 3,650. How old evidence may be. |
Agents see purpose, useWhen, notFor and the subject first, in list_jobs and get_job_requirements, and pick a job by them.
Subjects
| Field | Required | Rules |
|---|---|---|
subjects |
No | 1 to 16 subject types: each kind of record the document talks about |
subjects[].id |
Yes | The record’s name in the document, such as ticket or contract |
subjects[].definition |
Yes | Quote: what the record is |
subjects[].identifier |
For provided |
How its ID is written, such as “the contract ID in the matter system”. Needed when the job’s cases use the record’s own ID, and for any subject a relationship points to. |
subjects[].example |
With identifier |
One ID, written as Felspar keeps it (lowercase for case-insensitive) |
subjects[].match |
No | exact (default) or case-insensitive |
subjects[].identity |
No | How a case gets its ID: provided, fields, content or issued (below). Without it, a subject with an identifier is provided and one without is issued. |
subjects[].relationships |
No | Up to 16, each { name, subject, cardinality, required?, definition }: subject is another declared subject, cardinality is one or many, required is true when the document says a decision needs that record, and definition is a quote |
subject |
No | The subject type this job decides about |
questions[].about |
No | The relationship whose record a question’s evidence comes from |
cases[].subject |
No | { id }: decide this test case as that record |
See Subjects and escalations for how agents send related records and what results carry.
Questions and answers
| Field | Required | Rules |
|---|---|---|
questions |
Yes | 1 to 64 |
questions[].key |
Yes | The question’s ID |
questions[].meaning |
Yes | Quote: the question |
questions[].answers |
Yes, for answer-choice questions | 2 to 64 answers |
answers[].value |
Yes | The answer’s ID |
answers[].meaning |
Yes | Quote: what the answer means |
answers[].inclusions, exclusions, examples, edgeCases |
No | Up to 8 quotes each |
answers[].noneOfThese |
No | true on at most one answer per question: the answer for text that clearly says something outside the list |
questions[].askOnlyWhen |
No | Conditions under which the question applies |
questions[].whereToLook |
No | Up to 8 quotes: where the policy says to look |
Outcomes, escalations and rules
| Field | Required | Rules |
|---|---|---|
outcomes |
Yes | 1 to 64, each { id, nextStep, escalate? } with nextStep a quote and escalate an escalation ID |
escalations |
No | 1 to 16, each { id, quote }: a hand-off the document names, such as legal-review |
whenUnresolved |
Yes | Quote: what an agent does when nothing is decided. Or { quote, escalate } to name the hand-off too. |
attempts.max |
No | 1 to 10, default 3: how many decisions an agent may ask for on one undecided case |
attempts.escalate |
No | The escalation once a case has used its attempts (otherwise whenUnresolved’s) |
rules |
Yes | 1 to 256, in order: the first that matches decides |
rules[].when |
Yes | Question keys mapped to one answer, or a list of 2 to 64 answers meaning “any of these” |
Test cases
| Field | Required | Rules |
|---|---|---|
cases |
Yes | 1 to 200 test cases |
cases[].facts |
Yes | Question keys mapped to answers |
cases[].expect |
Yes | { outcome, rule, escalation? }: the outcome the policy says, the rule that should decide it, and the escalation the document says it hands off to |
Identifiers use lowercase letters, digits, - and _, starting with a letter or digit. Quotes are at most 4,000 characters. Subject examples and case IDs use letters, digits and . _ : @ -, up to 200 characters.
Rules Felspar enforces
- Meaning comes only from the document. Every
meaning,nextStep,whenUnresolved,purpose,useWhen,notFor,owner, subject and relationshipdefinition, escalationquote, inclusion, exclusion, example and where-to-look is an exact quote fromread_source, copied character for character (keep escapes such as\_). A quote must appear exactly once in the document. An answer’s quote is first looked for, as whole words, inside its question’s quote, so an option such as “yes” can be quoted where the question lists it. - Rules are in order. The first rule that matches decides.
- Conditional questions. A question with
askOnlyWhenis asked only when those answers hold. Every rule that uses it must repeat that exact condition. - Tests come from the document. Take each expectation from the policy, never from what the draft returns.
- Don’t decide what the document leaves open. Report contradictions, missing values and undecided cases to the owner as gaps, with both quotes. If the document doesn’t say how a record is identified, which records a decision needs, or who reviews a held case, ask the owner; never invent one.
- Escalations name a kind of review, such as
human-revieworcompliance-review, never a tool, channel or person. The company’s own code decides what each one does. - Quote budget. One question’s quotes may come from at most 16 separate blocks of the document; quotes in the same list or table count once.
Findings
Beyond errors at a path, check_pack and propose_pack return completeness findings:
- Blocking: an answer without a definition; a multiple-choice question without a “none of these” answer that a rule routes; a threshold without test cases on both sides of its edge.
propose_packrefuses while any remain. - Warnings: answers without inclusions or examples; answers without exclusions; combinations of answers no rule decides; an evidence age the document doesn’t state; answers that describe an absence; “unknown” options; questions, outcomes or rules nothing uses or tests; escalations declared while
whenUnresolvednames none, so an undecided case has nowhere to go.
Some findings carry candidates: lines in the document that mention the answer and might supply the missing quote.
When the draft revises a job that already has a release, check_pack and propose_pack also return breaking: each escalation ID, subject type or relationship name the draft removes, and a changed job subject, with the number of agents that run the job. See Review and backtest.
Numbers, dates and scoring
These need felspar.pack-draft/v1 or v2 and are in preview. get_pack_guide has the full rules.
- Number questions give a
type: "number", aunit(a currency code,percent, or the noun a count is of, withunitWords), and a quotedmeaning. Date questions givetype: "date". Felspar reads both exactly from the agent’s excerpt, with no model, so they need no answers. - Thresholds in a rule name their bound and quote where the document states it:
{ "employees": { "lt": 500, "quote": "fewer than 500 employees" } }. A date bound can be a date, another date question plus days, or the decision date plus days. Every threshold needs test cases on both sides of its edge. - Scoring adds up quoted points per criterion; rules then become score bands (
score: { gte, quote }) and gates (gate: true), and test cases give the expected total.
Output checks
When the document says what a written output (a reply, an email) must or must not say, outputChecks names the output, the outcomes that produce it, and its checks: exact wording it must contain (require) or must not (prohibit), the case’s own answer it must state (states), yes/no questions asked of each paragraph (ask), and the approved context an agent may write from. Each check quotes the document. After release, a decided result lists the outputs its outcome asks for, and agents check what they wrote with check_output.
Identity modes
A subject’s identity says how a case gets its ID when the record has none of its own:
identity |
Means | Checked |
|---|---|---|
{ "mode": "provided" } |
The record’s own ID | The subject needs identifier and example |
{ "mode": "fields", "fields": [{ "name", "identifier", "example", "match"? }] } |
Named values the job collects, such as an email address and a requisition number, normalized by each field’s match (default exact) and hashed in name order |
1 to 8 fields with unique names; each example written as its match keeps it |
{ "mode": "content", "question": "<key>" } |
The text one question judges is the case | question is a question key in the job |
{ "mode": "issued" } |
Felspar issues an ID on the first decision | Nothing else |
Without identity, a subject with an identifier is provided and one without is issued. A v2 job without a subject is issued; a v0 or v1 job without one keeps the caller’s own ID. A subject that a relationship points to is always sent by its own ID, so it must be provided, with an identifier. See Subjects and escalations for what agents send and what results carry.