Seed catalogue · Forms
Pencil
A self-hosted feedback-to-action loop for one business: ask a short adaptive flow, surface evidence, and own the next action.
- Alternative to
- Typeform, under MIT
- Domain
- form response
- Version
- 0.0.0
- Contract
- 104 clauses across 15 areas
- Maturity
- contract-drafted-native-ui-thin-spine






6 screenshots from the product's own visual and verification artifacts. Its handover records what was exercised.
What you get
Everything below is generated from the seed's own manifest and contract. No rounded-off claims, no numbers we made up.
Operating cost
Runs on your cloud's free tier
The seed declares the free plan as the default deployment. Its own estimate, thresholds and sources are below, unedited.
- Status
- estimate
- Free Plan Default
- yes
- Platform Monthly Usd At Typical Local Or Early Production Use
- Min 0
- Max 0
- Platform Monthly Usd At Declared Estimated Limits
- Min 5
- Max not declared
- Workers Paid Required When
- The first measured boundary is any of: the 100,001st dynamic Worker request in a UTC day; an invocation that cannot stay within the Free plan's 10 ms CPU limit; the 5,000,001st D1 row read or 100,001st D1 row written in a UTC day; one D1 database exceeding 500 MB or the account exceeding 5 GB; or the 10,001st Queue operation in a UTC day. A normally delivered Queue message consumes write, read, and delete operations, so 3,334 such messages cross the Queue boundary. The declared capacity targets are not Free-capable or load-tested and are not a monthly usage forecast.
- Workers Paid Minimum Monthly Usd
- 5
- Excludes
- R2 usage above its Standard free tier, AI provider usage, email delivery, optional task destination charges, domain registration
- Assumptions
- The current thin spine uses one Worker and D1. R2 and Queues are declared but not implemented; R2 Standard includes 10 GB-month storage, 1 million Class A operations, 10 million Class B operations, and free egress each month. No second worker, Durable Object, Workflow, KV, or hosted database is used until a measured need exists.
Externals declared
3 externals, each with a reason
Under BASE-DATA-004, user data leaves only through what is declared here.
- Cloudflare has no owned primitive that delivers transactional email to external recipients, and BASE-ACCESS-001 magic-link sign-in is delivered by mail, so no deployment can run without it.
- ai
- Evidence synthesis and draft-question assistance require a model provider; no model is bundled with the deployment.
- task-destination
- Creating a task in a buyer-selected task system necessarily sends the confirmed action to that system.
Extension points
35 declared integration conveniences
These hooks are optional conveniences. Owners may change any source, including core logic, schema and infrastructure; their agents adapt useful upstream fixes and verify the result.
Declared limits
Capacity, stated up front
Straight from seed.json → limits. Where a value is an estimate the seed says so.
- Status
- estimated
- Must Be Replaced By Load Test
- yes
- Flows
- Total 1,000
- Simultaneously Open 50
- Flow Revisions
- Per Flow 100
- Steps
- Per Revision 50
- Choice Options Per Step 100
- Priority Options Per Step 10
- Branch Rules Per Step 100
- Contacts
- Total 100,000
- Participants Per Revision 25,000
- Responses
- Per Revision 100,000
- Short Text Characters Per Answer 200
- Open Text Characters Per Answer 5,000
- Respondent Sessions
- Retention Days 30
- Abuse
- Minimum Seconds From Session Start To Submit 3
- Public Rate Limits
- Requests Per Ip Per Minute 120
- Final Submissions Per Ip Per Minute 30
- Operator Rate Limits
- Requests Per Operator Per Minute 600
- P95 Milliseconds
- Public Screen 500
- Answer Transition 500
- Final Submission 1,000
- Response Ledger 500
- Notes
- These are design targets, not proven capacities. The backend pass must load-test them, revise the values, and mark status as tested before release.
Non-goals, stated proudly
A product that says what it will never do is a product you can trust to stay small, fast and legible. These are refusals, not gaps.
- Multiple workspaces
- Roles and permissions
- Agency management
- Payments and order forms
- Appointment scheduling
- Public template marketplace
- Survey-panel recruitment
- Native mobile apps
- Video, audio, signature, and file-upload questions
- Single sign-on
- Advanced fraud scoring
- Broad marketing analytics
- Live collaborative editing
- Arbitrary automation builder
- Storing or reporting the answers of an abandoned session
The contract
All 104 clauses of Pencil, from its own CONTRACT.md. Stable IDs identify requirements; the source handover and verification evidence distinguish implemented, tested and uncovered behavior.
HOME1 clause
/ 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.
SHELL1 clause
/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.
OPER3 clauses
Every operator can see and act on every flow, audience, response, analysis, and action. There are no roles.
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.
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.
WS3 clauses
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.
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.
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.
DASH2 clauses
A workspace with no flows shows an empty state that explains Ask → Signal → Act and creates a draft flow.
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.
FLOW21 clauses
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
Every branch predicate references an existing structured question and stable choice when applicable, and every branch destination references an existing question or completion screen.
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.
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.
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.
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.
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.
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.
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.
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.
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.
AUD6 clauses
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.
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.
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.
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.
Repeating the same audience-launch command for one revision returns the original launch and cannot create duplicate participants, invitation jobs, or delivery attempts.
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.
DIST10 clauses
Every open revision has a canonical direct link that renders the respondent experience without an operator session.
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.
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.
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.
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.
A transient mail.sender.v1 failure is retried at most 5 times over 1 hour using the original delivery idempotency key.
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.
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.
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.
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.
RESP19 clauses
A respondent sees one welcome, question, or completion screen at a time, with no builder or operator controls.
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.
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.
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.
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.
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.
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.
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.
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.
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.
After a successful submission the respondent sees the published completion screen and cannot edit the completed response through the public link.
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.
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.
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.
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.
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.
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.
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.
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.
LEDG4 clauses
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.
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.
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.
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.
RSLT5 clauses
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.
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.
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.
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.
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.
ANLY16 clauses
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
ACT7 clauses
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.
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.
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.
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.
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.
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.
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.
NOTIFY4 clauses
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.
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.
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.
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).
DATA2 clauses
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.
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.
Baseline guarantees
22 declared clauses from this product's BASELINE.md. Selected ownership guarantees are shown below; execution evidence is separate.
The deployment makes no request to the marketplace or the seed author. There is no telemetry, phone-home, licence check, or kill switch.
pnpm export produces every record and attachment; pnpm import restores it into an empty deployment; export, import, export yields an archive with equivalent contents.
User data leaves the deployment only through externals declared in seed.json → externals. No other outbound request carries user data.
Public surfaces (widgets, booking pages, forms, status pages) set no cookies, load nothing from third-party domains, and send no visitor data before the visitor's first interaction.
No secret appears in the client bundle, in logs, or in an error response.
Release history
Honest version: there is one release line so far, and we will not invent the rest.
- v0.0.0
- Current — contract-drafted-native-ui-thin-spine
The manifest states this version and maturity. Consult the source verification evidence before deployment.
DIY quickstart
The code is MIT and the deployment is yours. Nothing below talks to us; optional management remains planned.
This edition has not published a source README yet; use its manifest and contract for current scope and status.
View as agent
Everything on this page exists as machine-readable files. If you are an agent evaluating Pencil for someone, read these instead of the prose.
No catalogue install command or source README is declared for this edition.
- seed.json
- The manifest: capabilities mapped to clauses, externals, extension points, limits, operating cost, deploy requirements.
- CONTRACT.md
- The 104 clauses above, in source form.
- BASELINE.md
- The product's declared baseline requirements.
- AGENTS.md
- Instructions written for the coding agent that will maintain this deployment.
- /llms.txt
- The site, for machines.
Catalogue endpoint
/agents/catalogue.json
Every seed with its id, category, version, clause count, install command and links to the files on the left. Generated from the seeds' own artifacts on every build, so it cannot drift from what ships.

Run Pencil on your own cloud.
MIT code, your data, your infrastructure. Leave whenever you like and it keeps running.