AI survey generation skill for Codex & Claude Code: generate validated, independently deployable, fully customizable survey HTML.
SaferSkills independently audited survey-creator-skill (Agent Skill) and scored it 45/100 (orange). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 6 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 6 flagged
A base64 string of 128+ characters appears in a documentation file. Encoded prompt injection hides the hostile instruction in base64 — invisible to keyword filters — and relies on the agent's ability to decode it at runtime. There is no normal authoring reason to embed a multi-hundred-byte base64 blob in skill docs.
*.sig, SIGNATURES) outside the documentation.Every scanned point with the score it earned and what moved between them.
Score fell 55 points between these scans.
The primary manifest — the file an agent reads to learn what this artifact does.
Generate HTML survey pages only.
Treat skills/survey-creator/ as the canonical generator root for this skill. All generator-private references, templates, validators, tests, examples, and docs live under this directory.
The workflow is:
references/Do not return raw JSON schema by default. Use the schema as an internal generation artifact unless the user explicitly asks to inspect it.
Before generating output, read only the files you need from:
references/schema-notes.mdreferences/field-guide-overview.mdreferences/id-rules.mdreferences/survey-welcom.jsonreferences/survey-fields.mdreferences/question-radio.jsonreferences/radio-fields.mdreferences/question-checkbox.jsonreferences/checkbox-fields.mdreferences/question-input.jsonreferences/input-fields.mdreferences/question-score.jsonreferences/score-fields.mdreferences/question-nps.jsonreferences/nps-fields.mdreferences/question-finish.jsonreferences/finish-fields.mdreferences/rich-text-rules.mdreferences/pagination-rules.mdreferences/submission-contract.mdreferences/child-input-rules.mdreferences/local-cache-rules.mdreferences/logic-rules.mdreferences/logic-specification.mdreferences/logic-example-library.mdreferences/toc-survey-ui-rules.mdreferences/toc-style-system.mdvalidators/README.mdvalidators/validate_reference_consistency.pyvalidators/validate_survey_schema.pyvalidators/validate_survey_payload.pyvalidators/validate_payload_against_schema.pyvalidators/validate_survey_html_runtime.pyvalidators/validate_survey_html_interaction_e2e.pyvalidators/validate_survey_html_accessibility.pyvalidators/render_survey_html.pyvalidators/build_validated_survey.pyvalidators/generate_sample_payload.pydocs/LEGALITY_GUARANTEE.mddocs/LEGALITY_MATRIX.mdtests/contract/README.mdrun_all_legality_checks.shAlways read references/schema-notes.md first, then references/field-guide-overview.md, then references/id-rules.md, references/rich-text-rules.md, references/pagination-rules.md, references/submission-contract.md, references/child-input-rules.md, references/local-cache-rules.md, references/logic-rules.md, references/logic-specification.md, and references/logic-example-library.md, then the specific *-fields.md files for every node type you plan to generate or validate. If the user requests a stronger toC visual direction, also read references/toc-style-system.md and references/toc-survey-ui-rules.md. When schema safety matters, also use validators/validate_survey_schema.py as the executable guardrail before rendering HTML.
Treat this skill as a schema legality engine plus fixed HTML renderer. The user and AI may iterate on business semantics, but the skill must enforce protocol legality before delivery.
Read docs/LEGALITY_GUARANTEE.md when changing the skill, adding fields, adding question types, or assessing whether generated artifacts are safe to deliver.
When modifying references, validators, payload format, or templates, run:
python3 <survey-creator-root>/tests/contract/run_contract_tests.py
<survey-creator-root>/validators/run-validator-smoke-tests.shOnly consider the skill healthy when both pass.
Turn a user's high-level request into a self-contained HTML survey page that:
This skill should think in two layers:
The schema layer is not optional. Build it first, validate it, then render.
Read the prompt carefully and identify:
stylePackIf the prompt is underspecified, make sensible product decisions instead of stopping.
stylePackWhen the user gives visual direction, infer a renderer stylePack before generating HTML.
Use only the generic toC packs:
consumer-minimalconsumer-polishedconsumer-trustconsumer-editorialconsumer-utilityconsumer-campaign#### Explicit mapping
→ consumer-minimal
→ consumer-polished
→ consumer-trust
→ consumer-editorial
→ consumer-utility
→ consumer-campaign
#### Implicit mapping
→ default to consumer-minimal
→ default to consumer-trust
→ default to consumer-polished
→ default to consumer-editorial
→ default to consumer-utility
Always record this decision mentally and pass it into the renderer or pipeline command via --style-pack.
Create an internal questionnaire object using both the reference JSON examples and the field-guide markdown files.
Do not rely on JSON shape alone. Use the field guides to understand what each field means, when it is optional, and how it should be rendered.
For a full survey, the internal shape should be:
{ "survey": { ... }, "questions": [ ... ], "finish": [ { ... } ] }
This internal schema is the planning source for the HTML. Do not expose it unless the user asks.
Before rendering HTML, verify all of the following:
#### Structure validation
survey, questions, finishquestions is an arrayfinish is an array of one or more finish objectssurvey.type === "survey"finish[i].type === "finish"radio, checkbox, input, score, npsattribute.media and option[].attribute.mediaattribute.media and option[].attribute.media, and use range-based scoreDesc keys such as 0-6#### Field validation
survey, radio, checkbox, and input use attributetitle and description are treated as rich-text-capable string fieldsradio, checkbox, and input include id, title, description when appropriateradio and checkbox use option arraysoption[].attribute.random === false overrides before shufflinginput uses an option array with input metadata in option[].attributeinput may contain one or multiple option-defined input fields, so submission must preserve answers per option[].id rather than collapsing everything into one stringchild items of type: "input"attribute configuration and should be fully respected when present, using the same input-style semantics you already received for child field behaviordataType when presentemail, tel, number, text, date, time, dateTime, dateRange, timeRange, dateTimeRange; unknown values must fall back to text#### Reference conformance validation
attribute.random === true, randomized option order should visibly change across reloads or renders, while preserving option ids and submitted valuesoption.attribute.random === false, that option must stay fixed even when the question is randomizedexclusive and mutual-exclusion have different semantics: exclusive clears all other options, while mutual-exclusion only clears other mutual-exclusion optionsIf the inferred schema fails validation, fix it before rendering. Never render HTML from an invalid schema.
After the manual semantic review above, run the executable validator whenever you have local file access or can materialize the schema temporarily:
python3 <survey-creator-root>/validators/validate_survey_schema.py /absolute/path/to/schema.jsonRules:
dataType values as blocking errorshigh warnings should normally be resolved before rendering final HTMLcode, suggestion, and fixHint to repair the schema automatically before proceeding--json when you need a machine-readable validation reportIf you cannot execute the validator in the current environment, you should still follow the same rule set manually and explicitly say that executable validation was not run.
When local scripting is available, prefer using the automated renderer:
python3 <survey-creator-root>/validators/render_survey_html.py --schema /absolute/path/to/schema.json --out /absolute/path/to/output.htmlIf --out points to a directory, the renderer writes:
<survey.id>.htmlOr use the full automatic pipeline in one command:
python3 <survey-creator-root>/validators/build_validated_survey.py --schema /absolute/path/to/schema.json --out-html /absolute/path/to/output.html --out-payload /absolute/path/to/output-payload.jsonIf --out-html, --out-payload, or --out-schema point to directories, the builder writes:
<survey.id>.html<survey.id>.payload.json<survey.id>.repaired.schema.jsonWhen semantic warnings are expected, prefer the repair-enabled pipeline:
python3 <survey-creator-root>/validators/build_validated_survey.py --schema /absolute/path/to/schema.json --out-schema /absolute/path/to/repaired-schema.json --out-html /absolute/path/to/output.html --out-payload /absolute/path/to/output-payload.json --auto-repair --fail-on-high-warningFor skill execution, prefer the single unified entry:
python3 <survey-creator-root>/validators/run_survey_creator_pipeline.py --schema /absolute/path/to/schema.json --output-dir /absolute/path/to/output-dir --style-pack consumer-trust --auto-repair --fail-on-high-warningDefault output naming for the unified pipeline:
--prefix is provided, it is used as the output filename stemsurvey.id as the filename stemsurvey.id === "survey-310991633843965952", the generated HTML file must be survey-310991633843965952.htmlSupported --style-pack values currently include:
consumer-minimalconsumer-polishedconsumer-trustconsumer-editorialconsumer-utilityconsumer-campaignOnly return the final HTML to the user after this automated chain succeeds, desktop/mobile E2E viewports pass, htmlAccessibility.valid === true, payloadAgainstSchema.valid === true, and the pipeline report says releaseDecision.shipReady === true. If the generated HTML fails runtime or E2E checks, run the HTML auto-repair pass before giving up.
Map the validated schema to page structure:
survey → welcome / intro / hero sectiontitle and description using rich-text-capable outputradio → radio groupcheckbox → checkbox groupinput → text input, textarea, or structured range input blockscore → score / rating gridnps → recommendation score scalefinish → one or more submit / thank-you / final action sectionsWhen multiple finish nodes exist:
finish[0] as the default finishlogic.action.type === "end_survey" to route to a specific finish[].id via action.targetQuestionIdfinish[].postSubmitfinish[].postSubmit.redirect must run only after successful submit, never when the finish screen first appearsUse references/submission-contract.md as the default submit serialization protocol.
At minimum, the generated HTML should make it possible to assemble:
surveyIdsubmittedAt as a millisecond timestampanswers[] with questionId, questionType, and type-specific valueFor radio / checkbox / input / score / nps questions, preserve schema ids in DOM structure so submission assembly is reliable. Unanswered questions must be omitted from answers. If a child input carries its own attribute configuration, use that configuration in child rendering and validation. Treat child attribute semantics as the same input-style configuration family already defined by the provided materials. If one option has multiple child fields, submission assembly must preserve them as a list of child answer objects rather than collapsing them into one value. For input questions, use option[].attribute.dataType to choose control type and validation behavior. Apply the same rule to child inputs. If the dataType is missing or unsupported, fall back to text. For dateRange, timeRange, and dateTimeRange, serialize the submitted value as { start, end }. For input questions, serialize value as an array of answered option-field objects, each preserving optionId, dataType, and value.
After assembling the default payload shape, validate it with:
python3 <survey-creator-root>/validators/validate_survey_payload.py /absolute/path/to/payload.jsonRules:
input.value collapses into an object instead of an array, that is a blocking error{ start, end }, that is a blocking errorquestionId repeats in answers, that is a blocking errorchildId, dataType, or value, that is a blocking errorIf you cannot execute the validator in the current environment, follow the same contract rules manually and explicitly say that runtime payload validation was not run.
After rendering the final HTML file, run:
python3 <survey-creator-root>/validators/validate_survey_html_runtime.py /absolute/path/to/file.htmlUse it to confirm the generated HTML still contains the critical behavior hooks for:
exclusivemutual-exclusionlocalStorage persistence and cleanupIf the checker flags missing runtime hooks, treat the HTML as unsafe for delivery. If the checker emits warnings about rich text sanitization or runtime-generated ids, call those out explicitly and fix them before production use.
Always output a single self-contained HTML document with:
<style> blockDo not wrap the answer in markdown fences unless the user asks for fenced code.
The generated HTML must be submittable.
Until the user provides the exact submit contract, use this default approach:
<form>name attributesPOST /api/survey/submit as the default submission endpointcredentials: same-originsurvey.attribute.onePageOneQuestion === true, render the intro, each question, and the finish section as separate steps and show only one at a timePagination with survey.attribute.onePageOneQuestion === true; validation must reject this conflictPagination grouping is needed, set survey.attribute.onePageOneQuestion to false and use Pagination nodes only as page separatorslocalStorage between screens using the key rules from references/local-cache-rules.mdsurvey.attribute.allowBack === true, render a previous-step action; otherwise do not expose previous-step navigationlocalStoragefinish[].postSubmit.redirect only after successful endpoint responseFollow references/submission-contract.md as the default payload shape. Do not invent a different backend API contract unless the user later specifies one.
All ids must be:
This applies to:
Never use placeholder or predictable ids such as:
survey-idquestion-idoption-idq-1opt-1Use canonical type-prefixed ids such as:
survey-190238471928radio-234567checkbox-345678input-456789score-567890nps-678901finish-789012pagination-890123For option ids, reuse the parent question type prefix:
radio-xxxxxxcheckbox-xxxxxxinput-xxxxxxscore-xxxxxxnps-xxxxxxFor child follow-up inputs, use:
input-xxxxxxImportant:
Math.random() or similar runtime generation for production survey ids inside the browsersurvey.id must use a global long snowflake-style suffixsurvey.idUnless the user explicitly asks to extend the schema, only use:
surveyradiocheckboxinputscorenpsfinishEven though the final output is HTML, keep the schema meaning intact:
title and description are string fields with rich-text supportsurvey is an introduction blockradio is single choicecheckbox is multi choiceinput is free text inputfinish is the last section and submit contextOnly render child inputs when the prompt logically implies:
Do not sprinkle child inputs everywhere.
Only include media areas if the prompt explicitly asks for them or the scenario strongly implies them. When used, media resources may be links or base64 values, and may represent image, audio, or video.
The user may freely describe the UI style. Examples:
Apply the requested style to:
If the user gives no style direction, choose a clean, modern, readable default.
When the prompt is vague, make sensible product decisions:
survey.attribute.onePageOneQuestion is enabled or implied, plan a step-based one-question-per-screen flow where intro, each question, and finish are independently displayedPagination separators and keep survey.attribute.onePageOneQuestion falsesurvey.attribute.allowBack is true, include previous-step navigation in step modeFor question count:
Return in this order:
User asks: “做一个新品饮料试喝反馈问卷,5题左右,AI Native 风格。”
What to do:
User asks: “做一个活动报名问卷页面,偏 toC 一点,轻一点,不要后台感。”
What to do:
User asks: “根据这个问卷需求做一个黑色高级感 HTML 页面。”
What to do:
Before finalizing, quickly check:
If any answer is no, fix it before responding.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.