# CONTRACT.md — Pencil

Pencil's product guarantees. Every clause has a stable ID and at least one tagged test; `pnpm verify` reports each as satisfied, overridden, or broken once the shared seed-spec harness is installed. Security, privacy, and operational guarantees are in `BASELINE.md`; a clause here may supersede one of them by naming its `BASE-` ID (WS-003 supersedes `BASE-SECRET-002` for optional externals, and is the only one). Capacity and rate limits are in `seed.json → limits`. Times are p95 at those limits. Clauses marked *Policy* have a default that buyers may replace through the named extension point.

## Scope

A feedback-to-action product for one business. Operators build short, adaptive feedback flows, invite a known audience or publish a public link or embed, review completed responses, use AI to surface evidence-backed findings, and turn one recommendation into an owned action that can be measured and closed.

## Landing

- **HOME-001 — Configured public landing.** `/` renders the owner-configured public surface using the same respondent experience and admission state as the configured published flow's canonical direct link. The sample configuration selects the sample published flow. If that flow is closed, archived, or missing, `/` uses the same closed or 404 outcome as its direct link. The owner may configure the displayed business name, one supporting line, theme, and the optional default-on static `Powered by Pencil · runeditrun.com` credit. The landing contains no vendor marketing, pricing, invented metrics, testimonials, or third-party requests.

## Application shell

- **SHELL-001 — Operator shell.** `/app` and every route below it require a valid operator session before the operator document or API data is served. A valid session receives labelled, keyboard-operable navigation to the implemented operator surfaces; the public respondent experience is never an operator navigation destination. `/signin` remains sessionless and is the only entry point to the operator session flow, so an unauthenticated request never receives the operator shell as a fallback.

## Operators

- **OPER-001 — Equal operators.** Every operator can see and act on every flow, audience, response, analysis, and action. There are no roles.
- **OPER-002 — Management.** Operators are invited and removed in settings. Any operator may invite another and may remove any other operator, except that the setup operator can be removed only by themselves and the last operator cannot be removed. There are no roles (OPER-001), so there is no seam that narrows who may do this.
- **OPER-003 — Sign-in depends on mail.** `BASE-ACCESS-001` magic links are delivered through `mail.sender.v1`, so mail is a required external, not an optional one: startup fails under WS-003 naming the missing variable when `MAIL_PROVIDER`, `MAIL_API_KEY`, or `MAIL_FROM` is absent. No deployment starts in a state where no operator can sign in.

## Workspace

- **WS-001 — Workspace time zone.** Settings hold one IANA time zone for the workspace, defaulting to `UTC`. Every deadline, sending window, digest send time, and operator-facing date and time is evaluated and displayed in it. A revision records the zone in force when its deadline was set, so changing the workspace zone later cannot move a deadline on an open revision.
- **WS-002 — Sending hours.** Settings hold a daily sending window in the WS-001 zone, defaulting to 09:00–17:00 every day. Scheduled mail — reminders and digests — that falls due outside the window is held and sent when the window next opens; nothing is dropped for falling outside it. Mail an operator triggers directly, and every magic link, is sent immediately regardless of the window.
- **WS-003 — Optional externals are configuration, not degradation.** *Supersedes `BASE-SECRET-002`.* Pencil starts only when every variable marked required in `seed.json → env` is present, and fails at startup naming the first missing one. `ai.provider.v1` and `task.destination.v1` are declared optional: with either absent the deployment starts and serves, every clause that does not name that adapter holds in full, and each feature that does name it is shown as unavailable alongside the variables that would enable it. No feature silently produces a reduced result instead.

## Dashboard

- **DASH-001 — First flow.** A workspace with no flows shows an empty state that explains Ask → Signal → Act and creates a draft flow.
- **DASH-002 — Flow list.** Operators can list flows by draft, open, closed, or archived state, sorted by last activity, with completed-response count, audience, and analysis status visible. An invited flow also shows completions divided by launched participants not bounced or opted out; zero eligible participants shows `0 / 0` with no percentage. An anonymous public flow shows starts and completions without inventing a target denominator.

## Flows

- **FLOW-001 — Lifecycle.** A new flow is draft; publishing makes it open; an open flow can close; a closed flow can reopen the same published revision only after no deadline or target condition remains satisfied, or it can archive; a draft can archive; and an archived flow cannot reopen. Draft public URLs return 404, closed revisions show the standard closed page, and archived public URLs return 404. Archiving removes the flow from default operator views without deleting its revisions, responses, analyses, or actions.
- **FLOW-002 — Ordered steps.** A draft contains an ordered list of steps: at most one welcome screen, which is optional and can only be first; one or more questions; and one or more named completion screens. Operators can add, edit, duplicate, reorder, and delete them.
- **FLOW-003 — Question types.** A question is short text, long text, email, single choice, multiple choice, rating, or priority; it has a prompt, optional why-we-ask text, required flag, and type-specific options within the limits in `seed.json`.
- **FLOW-004 — Saved edits.** A field-valid builder write includes the draft version it read and may leave the graph incomplete until publication. A current-version edit is persisted before the builder shows it as saved; a stale edit is rejected with the latest version and cannot overwrite another operator's edit. Leaving or reloading after the saved indicator cannot lose the accepted edit.
- **FLOW-005 — Branching.** A single-choice answer can match equality, a rating or priority answer can match equality or ordered comparison, and a multiple-choice answer can match any or all selected stable choice IDs. Preview shows the ordered rule evaluations and chosen next step used by the published runtime. *Policy: `flow.branch-selection.v1`; default: first matching rule in displayed order, then the required default path.*
- **FLOW-006 — Publish validation.** Publishing runs every FLOW-012 through FLOW-014 validation and is rejected with all offending stable step IDs and reasons; rejection creates no revision and leaves the draft editable.
- **FLOW-007 — Preview isolation.** Preview runs the current draft with test answers, identifies itself as preview, sends no invitations or external effects, and never creates a response or changes result counts.
- **FLOW-008 — Published revisions.** Publishing creates an immutable numbered revision and makes it the current revision resolved by the flow's canonical direct and embed URL. Every invitation, first-interaction session permitted by RESP-013, completed response, analysis, and export records the revision it used; merely loading the page creates no record.
- **FLOW-009 — Live edits.** Editing an open flow changes a new draft only. Existing invitations and first-interaction sessions stay pinned to their recorded revision; publishing the draft affects only later direct or embedded sessions and later invitation launches.
- **FLOW-010 — Closure.** An operator may close a flow immediately or configure a revision-specific deadline or completed-response target. A deadline is an instant in the WS-001 zone recorded with that revision. A manual flow close closes every revision's admission; a deadline or target closes only that revision's admission atomically.
- **FLOW-011 — Duplication.** Duplicating a flow creates an unlinked draft containing its latest draft or published structure and settings, but no audience members, invitations, responses, analyses, actions, delivery history, or external IDs.
- **FLOW-012 — Valid references.** Every branch predicate references an existing structured question and stable choice when applicable, and every branch destination references an existing question or completion screen.
- **FLOW-013 — Reachable termination.** The flow's first step is its welcome screen or its first question, every step is reachable from it on at least one path, and every possible path reaches one completion screen without a cycle.
- **FLOW-014 — Valid questions.** Every question has a non-empty prompt within the text limit, every structured question has valid distinct options within its type limits, and the flow contains at least one question.
- **FLOW-015 — Stable structure IDs.** A step and each of its choices receive stable IDs when created; editing, reordering, publishing, exporting, and importing preserve them, while duplication creates new IDs.
- **FLOW-016 — Published policy snapshot.** A published revision stores the evaluated configuration for `flow.branch-selection.v1`, `response.identity.v1`, and `flow.response-admission.v1`; later policy changes affect only a newly published revision.
- **FLOW-017 — Revision admission.** A new direct or embedded visit starts the current revision; an invitation starts its recorded revision; and a returning first-interaction session resumes its recorded revision. A superseded revision cannot start a new anonymous session and remains reachable only through an existing invitation or session until its admission closes.
- **FLOW-018 — Revision counts.** Deadline and target admission checks use completed responses for that revision only. Reaching either condition cannot close another revision, while manual close and archive close all revisions.
- **FLOW-019 — Answer values by type.** A stored answer is exactly one of: for short text and long text, a string within that type's character limit in `seed.json → limits`; for email, a syntactically valid address stored as the respondent typed it and compared case-insensitively; for single choice, one stable choice ID belonging to that question; for multiple choice, a set of distinct stable choice IDs belonging to that question, within its configured minimum and maximum selections; for rating, an integer within the question's configured scale; for priority, a complete ordering of that question's stable choice IDs with no repetition and no omission. Nothing else is a valid answer.
- **FLOW-020 — Required applies to the path taken.** A required question blocks progress only on a path that reaches it. A question that the respondent's answers route around is never asked, never recorded as unanswered or missing, and never blocks final submission; publish validation does not require a required question to appear on every path.
- **FLOW-021 — Deleting a question keeps its answers.** Deleting a step in a draft changes only that draft. Answers already collected keep their stable step ID and the revision that asked them, stay in the ledger, results, exports, and existing analyses, and are never rewritten or removed by a later publication. A question first added in a later revision is never reported as unanswered by responses to an earlier revision that did not contain it.

## Audience

- **AUD-001 — Contacts.** Operators can create, edit, import, and delete contacts with name, email, and custom fields. Email addresses compare case-insensitively and an import reports, rather than silently duplicating, matching contacts.
- **AUD-002 — Segments.** A segment filters contacts by stored fields. Launching invitations records the participant set plus evaluated `flow.audience-eligibility.v1` and `flow.reminder-schedule.v1` configuration, so later contact, segment, or policy changes do not rewrite the launch; AUD-006 still enforces current hard consent and existence checks at send time.
- **AUD-003 — Participants.** Each invited contact becomes one participant with an opaque invitation token, independent queued/sent/bounced/failed delivery status, eligible/opted-out consent status, and not-started/started/completed response status. Delivery attempts are append-only, and names, emails, and other contact values never appear in the public URL.
- **AUD-004 — Eligibility.** Invitations are created only for contacts allowed by the audience policy; that policy may narrow but never admit a missing-email or opted-out contact. *Policy: `flow.audience-eligibility.v1`; default: also exclude contacts already invited to that published revision.*
- **AUD-005 — Launch idempotency.** Repeating the same audience-launch command for one revision returns the original launch and cannot create duplicate participants, invitation jobs, or delivery attempts.
- **AUD-006 — Send-time eligibility.** Immediately before each invitation or reminder, Pencil verifies that the contact still exists and has not opted out. Mail uses the participant email snapshot displayed and recorded at launch; editing the contact later never silently reroutes an active participant.

## Distribution

- **DIST-001 — Public link.** Every open revision has a canonical direct link that renders the respondent experience without an operator session.
- **DIST-002 — Embed.** One asynchronous script or HTTPS iframe embeds the same respondent experience. It does not block the host page, and direct and embedded submissions produce the same response data and validation.
- **DIST-003 — Invitations.** An operator can send an invitation to eligible participants. Each message names the business and flow, contains that participant's opaque link, and records a durable delivery attempt before calling `mail.sender.v1`.
- **DIST-004 — Reminders.** Reminders are sent only to sent participants who have not completed, opted out, bounced, or reached a closed or archived flow. Completion cancels pending reminders. *Policy: `flow.reminder-schedule.v1`; default: one reminder 2 days after the invitation, released within the WS-002 sending hours.*
- **DIST-005 — Opt-out.** Every invitation and reminder includes an opt-out link. Opting out prevents future flow mail to that contact regardless of policy. Renewed consent records its actor, time, and source before mail can resume; opt-out does not delete an existing response.
- **DIST-006 — Mail retry.** A transient `mail.sender.v1` failure is retried at most 5 times over 1 hour using the original delivery idempotency key.
- **DIST-007 — Adapter isolation.** Mail cannot be unconfigured (OPER-003). A failing `mail.sender.v1` disables invitations, reminders, notification mail, and new magic-link sign-ins with a named error visible to operators, while existing operator sessions, direct links, embeds, response collection, results, the ledger, and exports keep working. Recovery is restoring the sender; Pencil offers no password or bypass sign-in.
- **DIST-008 — Terminal delivery.** A permanent mail failure or exhausted retry is marked failed with the provider reason and retry action visible to operators; no invitation is recorded as sent merely because it was queued.
- **DIST-009 — Embed isolation.** The embed rejects a host origin not allowed by BASE-PUBLIC-002 before rendering the flow. It exposes no answer, response ID, participant token, or contact value to the host URL, DOM, or `postMessage`; host communication is limited to non-identifying loaded, height, and completed events.
- **DIST-010 — Ambiguous mail outcome.** `mail.sender.v1` implements send-or-get by delivery idempotency key when its provider supports lookup. An ambiguous timeout without lookup is marked unknown and is not automatically retried; an operator may reconcile or explicitly resend it with the duplicate-delivery risk shown.

## Responding

- **RESP-001 — One question at a time.** A respondent sees one welcome, question, or completion screen at a time, with no builder or operator controls.
- **RESP-002 — Truthful progress.** The page shows the respondent's current position and remaining reachable questions for the answers already given. Branching can change that count, and skipped or unreachable questions are never counted.
- **RESP-003 — Answers.** The respondent can submit only the values FLOW-019 defines for the published question's type and configured limits. Rating, priority, and choice questions begin unselected and require affirmative respondent selection; a required unanswered or invalid question stays on screen with a field-specific error and creates no response.
- **RESP-004 — Back navigation.** A respondent can move back through screens already visited in the current session and edit an answer. Pencil recomputes the forward path and progress and discards answers belonging to steps that become unreachable before final validation, export, or analysis.
- **RESP-005 — Privacy disclosure.** Before the first answer, the flow states why feedback is requested, whether the response is anonymous or identified, who can see individual answers, and each configured external and data category declared in `seed.json → externals` that may receive the answers. The policy decides which identity mode applies; it can never suppress or weaken the disclosure of the mode it chose. *Policy: `response.identity.v1`; default: public-link responses are anonymous and invited responses are identified.*
- **RESP-006 — Anonymous means anonymous.** An anonymous response records no contact link, no participant, no name or email taken from an invitation, contact record, or session, no raw IP address, and no host-page identifier. It receives a random response ID that cannot be reversed to identify the respondent; expiring rate-limit and session state is not linked to that ID. An email or name the respondent knowingly types into a question is stored as that answer and nothing else: it creates no contact and no participant, and RESP-005's disclosure states before the flow starts that answering such a question identifies the respondent to the operator.
- **RESP-007 — Final submission.** A completed response, all its answers, count change, and durable outbox commands for required downstream effects are committed in one transaction before any effect is dispatched.
- **RESP-008 — Admission per invitation.** An invitation admits only the completions its recorded admission rule allows. A visit beyond that shows the existing completion state and creates no further response, and no rule can admit a completion after the flow's admission has closed (RESP-009). *Policy: `flow.response-admission.v1`; default: one completion per invitation; anonymous direct links allow multiple independent completions.*
- **RESP-009 — Concurrent closure.** Final submission rechecks deadline, target, and manual closure in the same admission transaction. A submission that loses the final available place sees the standard closed page naming the business and flow and creates no response.
- **RESP-010 — No silent partials.** Answers from an abandoned or reloaded session are not visible to operators, included in results, exported as responses, or counted as completions. Only final submission creates response data in the deployment.
- **RESP-011 — Completion.** After a successful submission the respondent sees the published completion screen and cannot edit the completed response through the public link.
- **RESP-012 — Submission retry.** A submission idempotency key is scoped to one respondent session and published revision and retained with the completed response. Retrying that key returns the same response ID and completion screen and cannot increment counts or dispatch downstream effects twice.
- **RESP-013 — Session metrics.** The first respondent interaction creates an expiring session record containing only an opaque session ID, revision ID, source, start time, the stable ID of the furthest step reached, and a device class of mobile, tablet, or desktop—never answers, contact identity, raw IP, user-agent string, or host identity. It pins the revision and supplies starts, drop-off, device, and completion-time metrics; an abandoned session expires under BASE-DATA-002 after the retention period in `seed.json → limits`.
- **RESP-014 — Submit failure.** A final-submit request that is not durably committed leaves the respondent off the completion screen, preserves current in-browser answers, shows a retryable error, and retries with the original submission idempotency key.
- **RESP-015 — Abuse defences.** Every public submission is checked by defences the deployment owns entirely: a hidden honeypot field, a minimum elapsed time from session start to final submission taken from `seed.json → limits`, a submission token bound to the RESP-013 session, and the public rate limits enforced under BASE-ACCESS-003. Pencil loads no third-party challenge, script, or reputation service on a public surface, so BASE-PUBLIC-001 continues to hold. A submission whose session token is missing or unrecognised is refused under RESP-014 with a visible, retryable error naming the expired session, never silently discarded. Scoring a respondent's trustworthiness beyond these checks is out of scope.
- **RESP-016 — Bot submissions are dropped.** A submission that fills the honeypot is shown the ordinary completion screen and creates no response, answer, count change, session-metric change, or downstream effect. The drop records an audit entry under BASE-OPS-005 naming the defence that fired, the revision, and the time, and containing no answer text or respondent identity. The honeypot is the only check whose failure discards a submission.
- **RESP-017 — Suspected submissions are quarantined, not lost.** A submission that fails only the minimum-elapsed-time check is committed under RESP-007 as a quarantined response. It is stored complete with its answers, is excluded from completed-response counts, admission checks, results, analysis evidence sets, notifications, and response exports, and is never destroyed automatically.
- **RESP-018 — Quarantine review.** Operators can list quarantined responses for a revision with their answers and the defence that quarantined them, and can release or delete each one. Releasing makes the response an ordinary completed response from that moment, and every affected count, rate, and results figure is recomputed; deleting follows DATA-002. A release is allowed after the revision's admission has closed, is recorded with its operator and time, and never reopens admission or re-triggers a deadline or target check. Nothing leaves quarantine without an operator acting.
- **RESP-019 — Retained flow metrics.** When a session completes or expires, its start, furthest-reached step ID, device class, and completion outcome are folded into per-revision counters that hold counts only, cannot be traced to a session, response, or respondent, and are retained with the revision after the session record has expired. These counters are the source of every historical starts, drop-off, and device figure; no partial answer is retained with them.

## Response ledger

- **LEDG-001 — Ledger.** Operators can list and open every released completed response with stable response ID, flow revision, source, participant when identified, started and submitted times, and each answer keyed by stable step ID. A quarantined response appears only in the RESP-018 review list until it is released.
- **LEDG-002 — Counts.** Completed-response and participant counts are derived from stored participants and completed responses. Starts come only from RESP-013 session records and the RESP-019 counters they fold into; preview sessions, duplicate submissions, failed deliveries, abandoned sessions, dropped submissions, and unreleased quarantined responses never increment completed responses.
- **LEDG-003 — Filters.** Operators can filter the ledger by revision, submission time, source, participant status, and structured answer, while preserving the same response IDs shown in exports and evidence.
- **LEDG-004 — Response export.** Starting an export fixes its filter and evidence-cutoff time. CSV and JSON contain exactly the matching released responses committed by that cutoff, including response and revision IDs, source, timestamps, stable step IDs and labels, and lossless values for every answer including open text. Quarantined responses are excluded until released, and the export names how many were excluded.

## Results

- **RSLT-001 — Response summary.** Every published revision has a results view that needs no `ai.provider.v1`: it shows completed responses, starts, completion rate, and average time to complete for the current filter, each beside the counts it was computed from. It is available from publication, shows zeros rather than an error before the first session, and never blocks on an analysis run.
- **RSLT-002 — Drop-off by question.** Results show, for each step on the published revision, how many sessions reached it and how many of those reached no later step, computed from RESP-019 counters and therefore still available after the underlying sessions have expired. A step no session reached is shown as reached by zero, not omitted.
- **RSLT-003 — Stated denominators.** Every percentage Pencil displays in results or analysis names the numerator and denominator it was computed from. A single-answer distribution divides by the responses that answered that question and its parts sum to that denominator. A multiple-choice distribution divides by the same denominator, may therefore total more than 100%, and is labelled as multiple-select. Completion rate divides completed responses by starts for the same revision and filter, and is never mixed with a per-question answer rate. Changing a filter changes both numerator and denominator together.
- **RSLT-004 — Device split.** Results show the share of sessions in each RESP-013 device class, divided by the sessions counted for that revision and filter and named as RSLT-003 requires. Device class is derived at session start, is never stored on a response, and never appears in an export of responses.
- **RSLT-005 — Results across revisions.** A results view spanning more than one revision reports each question only for the revisions that contained it, using the responses that could have answered it as the denominator. It never treats a question added later as unanswered by earlier responses, and it never merges two questions that have different stable step IDs.

## Analysis

- **ANLY-001 — Analysis run.** An operator can request analysis for any revision with completed responses. A run is queued durably and shows queued, running, completed, insufficient-evidence, or failed status. *Policy: `analysis.autorun.v1`; default: run when a flow closes and has at least 5 completed responses.*
- **ANLY-002 — Fixed evidence set.** An analysis records the exact response IDs and revision it read, and reads only released responses. Responses submitted, or released from quarantine, after it started are excluded and make the completed analysis stale rather than changing it in place.
- **ANLY-003 — Findings.** A run that reaches completed status ranks themes and selects one primary finding from its fixed evidence set; an insufficient-evidence run follows ANLY-013 instead.
- **ANLY-004 — Exact evidence.** Text presented as a respondent quote is an exact excerpt of a stored answer. Generated paraphrases and summaries are labelled as analysis and never presented in quotation marks or attributed to a respondent.
- **ANLY-005 — Disclosure threshold.** In every analysis view, a theme or filtered segment with fewer included responses than the threshold shows no raw excerpt, participant identity, unique field combination, or breakdown; exactly the threshold is permitted. Suppression changes presentation, not the raw ledger or operator export. *Policy: `analysis.disclosure.v1`; default threshold: 5 completed responses in that theme or current filter cohort.*
- **ANLY-006 — Recommendations.** A completed analysis with sufficient evidence may propose one primary action with rationale, linked findings, confidence, expected impact, and effort; an insufficient-evidence run proposes none. *Policy: `recommendation.selection.v1`; default: the action supported by the highest-impact finding with sufficient evidence.*
- **ANLY-007 — Staleness.** A stale analysis remains readable and labelled with its evidence cutoff. Operators can run a new analysis; prior runs remain in history except for the erasure redaction required by DATA-002.
- **ANLY-008 — Provider failure.** If `ai.provider.v1` is unconfigured or fails an analysis or builder-assistance request, the request is marked failed with the error and retry action, no suggestion, finding, or recommendation is fabricated, and flow publishing, response collection, the ledger, and exports continue to work.
- **ANLY-009 — Builder assistance.** An operator may request a sharper, broader, or contextual rewrite of a draft question. Suggestions show their source text and are never applied or published without an operator accepting them.
- **ANLY-010 — Cost.** Every AI call records provider, model, input and output usage, feature, analysis or draft ID, and reported cost. Operators can view totals by day and feature.
- **ANLY-011 — Evidence provenance.** Each displayed theme count, percentage, supporting excerpt, and recommendation records the included response IDs from which it was derived; an operator can navigate from displayed evidence to those retained responses.
- **ANLY-012 — Output validation.** Before a run completes, Pencil validates its schema, verifies that cited response IDs belong to the run's evidence set, verifies quote ranges exactly against stored answers, and recomputes counts and percentages over that evidence set; any failure marks the run failed.
- **ANLY-013 — Insufficient evidence.** A run whose evidence or disclosure-eligible cohorts cannot support a finding completes as `insufficient-evidence`, shows the evidence cutoff and reason, and emits no theme, quote, or recommendation.
- **ANLY-014 — Untrusted evidence.** Respondent text is treated only as data: it cannot change system instructions or configuration, select tools, call adapters, create jobs, or authorize effects; schema-invalid provider output fails under ANLY-012.
- **ANLY-015 — Analysis policy snapshot.** Each run stores the evaluated `analysis.disclosure.v1` and `recommendation.selection.v1` configuration; later policy changes affect only a new run and never retroactively reveal or reinterpret historical output.
- **ANLY-016 — Provider retry.** A transient `ai.provider.v1` transport failure is retried at most 3 times over 10 minutes with the same request, evidence set, and policy snapshot; schema or evidence-validation failures are terminal and never retried as transport failures.

## Actions and measurement

- **ACT-001 — Action record.** An operator can accept a recommendation as an action with title, rationale, owner, target metric, review date, and links to its analysis and evidence. Accepting does not itself contact an external service.
- **ACT-002 — Action lifecycle.** A new action is planned. Legal transitions are planned → in progress or closed, in progress → ready for review or closed, ready for review → effective, ineffective, in progress, or closed, and effective or ineffective → closed; every transition records the operator, time, and optional outcome note.
- **ACT-003 — External task.** `task.destination.v1` must implement create-or-get by Pencil's idempotency key and return the existing remote task ID for a repeated key; repeated activation or delivery of one action therefore cannot create more than one external task.
- **ACT-004 — Task failure.** An unconfigured adapter disables external task creation with setup guidance. A failed call keeps the local action, records the attempt and error, and offers a retry that reuses the same idempotency key.
- **ACT-005 — Follow-up measurement.** An action records a metric name, unit, desired direction, baseline value, source, and observation time. An operator can link a later flow or analysis, record the comparable observed value and time, and mark the outcome effective or ineffective. Pencil never sets or changes that verdict itself, and no extension point can; a review date that has passed is surfaced on the action, never resolved on the operator's behalf.
- **ACT-006 — Confirmed task payload.** Before calling `task.destination.v1`, Pencil shows the action fields and operator-authenticated report link that will leave the deployment and sends only those confirmed values; raw answers and excerpts are never included.
- **ACT-007 — Task delivery state.** External task delivery records pending, confirmed, unknown, or failed status, the idempotency key, every attempt, and the remote task ID when confirmed. An unknown network outcome is retried with the same key rather than assumed failed or sent again with a new key.

## Notifications

- **NOTIFY-001 — Analysis attention.** The in-app notification centre records one notification for the requesting operator when an analysis completes or fails; an automatic close-time analysis records one for every unmuted operator. Duplicate queue delivery cannot create the same notification twice.
- **NOTIFY-002 — New-response mail.** Each operator sets, per flow, whether completed responses are mailed to them immediately, summarised in one daily digest, or not mailed. The operator who published the flow starts on the digest; every other operator starts on nothing. A message names the business and flow and links to the ledger; it never carries answer text, respondent identity, or a link that grants access without signing in.
- **NOTIFY-003 — Digest.** A digest is one message per operator per flow per day, sent at the WS-002 window's opening hour in the WS-001 zone, covering the completed responses committed since the previous digest. A day with no new completed responses sends no message, and a digest that fails to send is retried under DIST-006 rather than merging into the next day's.
- **NOTIFY-004 — No mail loops.** Every notification, digest, invitation, and reminder is sent from the configured no-reply address and marked auto-generated. Pencil ingests no inbound mail, so a reply or vacation auto-responder can never create a response, participant, notification, or further outbound mail. A bounce of operator notification or digest mail is recorded against that operator's mail setting only and never marks a participant bounced; participant bounce status comes only from invitation and reminder delivery (DIST-008).

## Data

- **DATA-001 — Export contents.** The deployment export contains every operator, contact, segment, flow and revision, step and branch, participant and delivery attempt, response and answer including quarantined ones with their state, RESP-019 revision counters, analysis and evidence link, recommendation, action, notification, setting, audit entry, and referenced file as original data. Import preserves Pencil's stable graph, revision, response, evidence, recommendation, and action linkages.
- **DATA-002 — Response erasure.** Deleting a response, or a contact that owns it, revokes and deletes active invitation tokens, removes contact links or anonymises retained delivery facts, and removes its answers, raw excerpts, and evidence links from every analysis within 5 minutes. Affected aggregates are recomputed or marked unavailable, affected analyses are labelled redacted and stale, and only the anonymised audit tombstone required by BASE-DATA-003 remains.

## Out of scope

Declared in `seed.json` as non-goals and not tested: multiple workspaces, roles and permissions, agency management, payments and order forms, appointment scheduling, public template marketplaces, survey-panel recruitment, native mobile apps, video, audio, signature and file-upload questions, SSO, advanced fraud scoring, broad marketing analytics, live collaborative editing, arbitrary automation builders, and storing or reporting the answers of an abandoned session. Pencil counts that a session started and how far it reached (RESP-019); it never keeps or shows what an abandoned respondent typed.
