lint.sh only ever checked shape (missing id/name, duplicate ids, bad
requirement types) - it never validated transition targets against the
real state machines. question_lint runs portal's actual validation
logic (content::validate_questions) with no portal build required in
this repo's CI, since portal's deploy job now publishes it to a stable
path on the same runner/host.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
applied.yaml and index.yaml still described review in the passive
third person ("someone here will read this", "taken on by someone
who reads") after project.yaml and the rest of applied.yaml's own
features had already been reworked to a direct first-person "we"
voice centered on the visitor's value, not on the fact that a person
reads it.
"A person reads it" reassures about process (a human, not a bot) but
never answers what an applicant actually wants to know: does what I
specifically bring matter here? Rewrote to say that directly, and
named how it gets used once they're in - dialogue-centered
development, not a queue of pre-decided tasks.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Bucket names describe content, not their relation to portal:
portal_applicants -> applicants, portal_subscribers -> subscribers,
portal_project_inquiries -> projects. ResourceSpec's `bucket:` field
becomes `source: {kind: kv, bucket: ...}` to match portal's new
generalized ResourceSource schema.
The project-inquiry alternative now also shows a live "what we've
built" showcase (a gitea_starred resource, jq-shaped) and asks who the
work is for (organization/contact fields) - b2b framing. Review desk's
"Inquiries" -> "Projects", transitions to accepted/declined instead of
a generic "handled".
Front page gets a `responsible` contact for the "report this question"
mechanism.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Per feedback: sending a newsletter is naturally part of "Subscribers",
not a separate review task. Also fixes a real bug - the standalone
Compose newsletter alternative had no `action`, so AlternativeCard
never rendered a submit button/form for it at all (has_action gates
the <form>, unconditionally). action: /review reloads the dashboard
after sending.
- index.yaml: record_as on "I want something built" and "Keep me
posted" - those submissions previously only existed as a NATS
event, nothing durable, despite promising a reply/subscription.
- review.yaml: consolidated into one dashboard question with four
alternatives (Applicants, Inquiries, Subscribers, Compose
newsletter) rather than separate pages per resource.
- Compose newsletter has no record_as/resource of its own - an
automation (n8n) filters portal.answers.submitted directly for
question_id == "/review" && alternative == "Compose newsletter",
matching events.rs's own "gain a second subscriber" design.
- lint.sh: allow type: prosekit (the new rich-text requirement kind).
Validates YAML/required fields/duplicate ids/requirement types with
yq+jq against the exact shape content.rs parses (not a full schema
check - that would mean building portal just to lint content), then
publishes portal.content.reload over NATS so every running portal
instance picks up the change without a restart (portal's own
content::watch_for_reload, added alongside this).
English URLs throughout; review.yaml adds a qualifies-gated question
listing applicants via the generic NATS-KV resource mechanism, with
Invite/Decline transitions. Content pass: concrete/positive phrasing
(no negation constructions), warmer inclusive tone, light puns in the
encouragement lines.