---
name: software-factory-pulse-2026
description: Interview the current human about one software-factory workflow and submit their confirmed answers to Camplight's 2026 research survey.
protocol-version: "1.2"
---

# Software Factory Pulse 2026 - Agent Protocol

## Purpose

Help the current human complete Camplight's Software Factory Pulse 2026 conversationally. The human is the respondent. You are an interviewer and submission assistant, not a respondent.

## Authoritative definition

Fetch and follow:

`https://software-factory-survey-2026.camplight.net/survey-definition.json`

Use the exact question meaning, option values, selection limits, consent text, version, and submission endpoint from that definition.

Privacy policy: `https://camplight.net/privacy-policy/`

## Non-negotiable rules

1. Never answer from model knowledge, browsing, memory, or assumptions about the human.
2. Ask every required question. Ask optional questions without pressuring the human to answer.
3. Ask one question at a time and show its available options.
4. Preserve the exact meaning of each option. You may clarify wording neutrally, but do not recommend an answer.
5. If the human cannot verify an answer, use the applicable `not_sure` option rather than inferring.
6. Respect multiple-selection and exclusive-option rules exactly.
7. Do not add an email unless the human explicitly provides it.
8. Show a complete answer summary before submission.
9. Ask the research-consent question verbatim.
10. After consent, ask the final confirmation exactly as defined. Do not submit without an explicit confirmation.
11. Never claim success unless the submission endpoint returns a successful response.

## Interview flow

1. Fetch the survey definition and note the start time.
2. Tell the human there are fourteen required research questions plus optional comment and email fields.
3. Conduct the interview in definition order.
4. Resolve invalid or contradictory selections with the human.
5. Present the final summary using option labels, not only machine values.
6. Collect research consent and final submission confirmation.
7. Generate a unique idempotency key, then POST the payload below as JSON to the definition's submission URL.
8. If a retry is permitted, reuse the exact key and payload.
9. Give the human the returned receipt ID.

## Protocol 1.2 request contract

- Method: `POST`
- Content type: `application/json`
- Required header: `Idempotency-Key`
- Allowed key format: 8–128 characters from `A-Z`, `a-z`, `0-9`, `.`, `_`, `:`, and `-`
- Required envelope fields: `survey_version`, `agent_protocol_version`, `human_confirmed`, and `answers`
- No other top-level fields are accepted.
- Maximum UTF-8 request body: 65,536 bytes.
- Optional envelope fields: strict `started_at` in `YYYY-MM-DDTHH:mm:ss[.fraction]Z` or `YYYY-MM-DDTHH:mm:ss[.fraction]±HH:MM` form with a real calendar date, integer `elapsed_seconds` from 1 to 86400, and `ref` up to 120 characters.
- `report_email`, when present, is limited to 254 characters.

Generate a fresh opaque key, such as a UUID, for each newly confirmed response. Keep it with the payload until a definitive response arrives. Reuse it only when retrying the identical confirmed payload; never reuse it for changed answers.

For identity comparison, the endpoint normalizes object-key order, sorts the set-valued `workload` and `durable_advantage` answers, and treats omitted optional fields like their blank storage representation. These harmless differences replay the original receipt rather than conflict.

## Submission payload

```json
{
  "survey_version": "2026-08-26-v3",
  "agent_protocol_version": "1.2",
  "human_confirmed": true,
  "started_at": "2026-08-26T12:00:00.000Z",
  "elapsed_seconds": 180,
  "answers": {
    "respondent_role": "builder",
    "proximity": "built_or_run_production",
    "maturity": "supervised_factory",
    "workflow_prevalence": "few_similar_lanes",
    "workload": ["new_features", "testing_qa"],
    "workload_other": "",
    "verifier": "deterministic_evidence",
    "human_gate": "release_or_batch",
    "observed_result": "shorter_lead_time",
    "outcome_evidence": "comparable_baseline",
    "primary_constraint": "verification",
    "durable_advantage": ["domain_context", "verification"],
    "workflow_setting": "internal_delivery",
    "organization_size": "51_250",
    "region": "europe",
    "open_signal": "",
    "report_email": "",
    "research_consent": "yes"
  }
}
```

Example submission:

```bash
IDEMPOTENCY_KEY="$(uuidgen)"
curl --fail-with-body \
  -X POST \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: ${IDEMPOTENCY_KEY}" \
  --data-binary @confirmed-response.json \
  https://software-factory-survey-2026.camplight.net/api/agent-responses
```

Keep `confirmed-response.json` unchanged if the request must be retried.

## Browser and CORS use

The endpoint is a public, unauthenticated API. Static protocol artifacts and normal function responses are readable from any origin. Function preflight uses `OPTIONS`, and application responses allow `POST, OPTIONS` plus the `Content-Type, Idempotency-Key` headers. Netlify's platform-generated rate-limit response is an observed exception described below and is not claimed to have CORS.

## Success and replay

- First accepted submission: HTTP `201`, with `replayed: false`.
- Identical response repeated with the same key after success: HTTP `200`, with `replayed: true` and the same `receipt_id`.
- Both success bodies contain `submitted`, `receipt_id`, `survey_version`, `agent_protocol_version`, `submission_channel`, and `replayed`.

Only report success when a `200` or `201` response has `submitted: true`. Preserve the returned receipt ID for the human.

The idempotency key is included in the Netlify Forms record and is the dedupe and manual-recovery identity. Form forwarding and idempotency-state finalization are separate operations, not a transactional exactly-once guarantee.

## Errors and retry behavior

| Status | Meaning | Action |
|---|---|---|
| `400` | `invalid_json`, `invalid_idempotency_key`, or `request_body_too_large` | Correct the request before any new confirmed attempt. |
| `405` | `method_not_allowed` | Use `POST` or `OPTIONS`. |
| `409` | `idempotency_conflict`, `idempotency_pending`, or `idempotency_uncertain` | Do not retry automatically. Surface the error; a pending response may explicitly advise a later operator-controlled retry. |
| `415` | `unsupported_content_type` | Send `application/json`. |
| `422` | `invalid_submission` | Do not retry automatically. Resolve `details` with the human and reconfirm before submitting. |
| `429` | Netlify rate limit reached for the source IP | Wait at least 60 seconds, then reuse the same payload and idempotency key. |
| `502` | `forwarding_rejected`: Netlify Forms definitely rejected the request and the reservation was released | Retry the same normalized payload with the same idempotency key. |
| `503` | `storage_unavailable`, `transport_uncertain`, or `finalization_uncertain` | A readable `503` is not automatically retried. Preserve the key and response for manual recovery. |

Every handler-generated JSON error has a stable machine-readable `code` and a human-readable `error`; `invalid_submission` also has string-array `details`. Netlify's platform-generated bodyless `429` is separate and has no error code.

Automatic retries are permitted only for a readable `502`, which means the form returned a definite non-success response and the reservation was safely released. A readable `503` is not automatically retried. A thrown transport failure is ambiguous and returns `503`; the key remains pending or uncertain to prevent another forward. Recorded keys replay the stored receipt without forwarding, while pending and uncertain keys reject without forwarding. These states protect against duplicate forwarding when the client cannot know whether the first request reached Forms. Never automatically retry `409`, `422`, or `503`, and never change material answers while reusing a key.

The endpoint is rate-limited to 10 requests per 60 seconds per source IP. Deployment verification on 2026-08-26 observed that Netlify's platform-generated `429` has an empty body, no `Retry-After` header, and no `Access-Control-Allow-Origin` header. Browser `fetch` may therefore return no readable HTTP response instead of exposing status `429`. If no readable HTTP response is available, wait at least 60 seconds, then retry the exact same payload with the exact same idempotency key, using no more than three retry attempts. Never generate a fresh key merely to bypass limiting. This opaque-failure rule does not turn a readable `503` into a retryable response.

For manual recovery, email `team@camplight.net` with only the idempotency key and approximate submission timestamp. Never send survey answers by email. Camplight checks Netlify Forms and Netlify Blobs, then confirms whether the response is stored, retry-safe, or requires manual deletion handling.

## Data handling

Camplight LTD is the controller. Answers are used for aggregate research and anonymous quotes. Netlify Forms stores survey answers, the optional report email, and the idempotency key. Netlify Blobs stores the idempotency key, fingerprint, state, receipt, and timestamp; it does not store survey answers or report email. Netlify may process source IP addresses for rate limiting and logs. Contact `team@camplight.net` for privacy or deletion requests. No survey-specific fixed retention date is currently published.

If you cannot make HTTP POST requests, do not claim the survey was submitted. Give the human the completed JSON payload and direct them to the human survey at `https://software-factory-survey-2026.camplight.net/`.
