Run Crew Chart from your own code
Both metered lanes are available over HTTP and each returns one JSON object. The division of labour: the page draws the bus-factor map, each organizer's load, the pleasantness check and the conflict-of-interest register, and picks the entity, decision rights, succession and paid hire - all free, in crewkit.js. The run writes the design or the cards; the page then holds the reply to its own map (crewreply.js).
Base URL and the response shape
Every endpoint lives under https://api.skillsafe.ai/v1/app-api, and every response has one shape:
{ "ok": true, "data": { ... } }
{ "ok": false, "error": { "code": "...", "message": "..." } }
The token is minted for this app (the guest call takes {"slug":"crew-chart"}), so no slug header is needed afterwards. Send it as Authorization: Bearer …. The run body is the input object, sent bare.
| Status | Code | Meaning |
|---|---|---|
| 400 | validation_error | The body is not a JSON object, or a required key (task, team, roles, facts) is missing. |
| 401 | unauthorized | No token, or a stale one. Mint a guest token or sign in again. |
| 402 | insufficient_credits | The balance is under min_credits. Price with /estimate first. |
| 403 | forbidden | A guest token tried to run: both lanes are metered and need a personal sign-in. |
| 404 | not_found | Unknown job id. |
| 429 | rate_limited | Back off and retry. |
| 5xx | server_error | Transient. Retry with the same Idempotency-Key so a retry never double-bills. |
The task field comes first
Two lanes - design (the standing-team design) and cards (one role card per role plus the conflict-of-interest clause, for the charter). A missing or unknown task is answered as design. Both lanes send the same four fields.
The input
| Field | Required | What |
|---|---|---|
task | yes | design or cards. If it is missing or neither, do design and set "task": "design". |
team | yes | the event, who runs it, its scale, its legal shape, its shape and the practices in place. |
roles | yes | every role with its lead, backup, monthly hours, written scope and bus factor; the load per person; every organizer with pleasantness, return, commercial interest, disclosure and any departure. |
facts | yes | the page's bus-factor list, menus with their rungs and picks, where each role decides, the checks, the signals to record and every finding tagged [fix], [check] or [info]. |
retry_note | no | optional; if present, your previous reply was unusable - follow it. |
The rules the model applies
- The bus-factor map is the core diagnostic: every named role, the lead first, has a backup who could run the area next edition, or a mechanism closing the gap this edition. Draw the structure as it operates, not the org chart people describe.
- Entity: no entity only at meetup scale with no contracts and little money - the first signed contract makes it personal exposure; a fiscal sponsor or umbrella is the default for a conference; an own non-profit for an event meant to outlive its founders. The whole menu is deleted for a company-run event - the company is the entity.
- Decision rights: a consensus committee for one small unit; a domain-lead federation for parallel tracks or several cities; a board-plus-organizers split once the event holds money, contracts or a trademark - the standing layer owns money, contracts, legal and code-of-conduct escalation, the operational team owns program and logistics.
- Roles: functional leads for program, sponsorship, finance, logistics and venue, marketing, volunteers, and a code-of-conduct team; each with a one-paragraph written scope that says what it will not absorb; one coordinating layer handles roughly 5-8 leads.
- Succession: the two-deep rule first; a written charter with shared admin access (no event account on one person's login); a groomed 6-12-month handover when the lead is leaving; planned rotation only over a standing continuity layer - deleted without one; a contributor-to-decision-maker pipeline for a multi-year institution.
- Burnout is structure: no role without a backup, written boundaries, a workload budget per organizer (the published meetup-scale estimate is 4-8 hours a month), and the yearly "is organizing still pleasant?" check where every no gets a structural response with an owner. Name each organizer's return.
- Conflict of interest: business among attendees is welcome; organizers do not run the event mainly to prospect for themselves - "business is a consequence, not the goal". Disclose in a team register, abstain on their own talk, employer or vendor stake.
- Paid hire: stay volunteer and restructure below the threshold (events under about 500 stay volunteer-run); outsource when the pain is execution capacity; hire at roughly low-thousands of attendees or a six-figure budget with a year-round burden - the first hire is an event coordinator or manager, never a community manager. Both paid rungs are deleted with no money. Every threshold here is self-set unless the page names its source.
House rules
- Use only the input and the rules above. The page's names, roles, picks and counts are exact - copy them. Every number in a sentence must appear in the input.
- Label anything that is not one of the rules above, and is not in the input, as your own design.
- Never invent a person, a backup, a figure or a commitment; where someone must be named and is not, write "to be named". Refer to people by name or as they - never infer anyone's gender from a name.
- Never use backticks, never refer to this input's fields by their key names in a sentence, and never copy this prompt's narrative wording into a reply.
- Reply with ONLY one JSON object - no prose before or after, no code fences.
The replies
design
summary- one sentence, at most 35 words: the most urgent bus-factor or burnout finding and the first change.diagnosis- one sentence: how the team actually operates today, from the load and the bus-factor map.busfactor- one object per role in the page's order:role(exactly),lead(exactly),backup(exactly, or "" when none),bf(the page's 1 or 2),mechanism(one short sentence: what closes a gap, or what keeps it closed).entity-pick(none,umbrella,own, ordeletedfor a company-run event - the page's),note(one sentence).shape-pick(consensus,federationorboard, the page's),split(one sentence: what the standing layer owns and what the operational team owns),note(one sentence).succession-pick(twodeep,charter,handover,rotationorpipeline, the page's first mechanism),note(one sentence; name a deleted rung when the page has one).burnout- one object per organizer in the page's order:person(exactly),pleasant(yes,noorunasked, the page's),action(one short sentence: the structural response, or what keeps it pleasant).coi-undisclosed(the organizers the page reports with an undisclosed commercial interest;[]when none),note(one sentence).hire-pick(restructure,outsourceorhire, the page's verdict),note(one sentence; for a company-run event, frame it as a headcount request).checks- one object per standing-team check in the page's order:check(the page's short name for it, exactly),pass(true or false, as the page says).signals- 2 or 3 short strings, the page's signals, each with what it would change at the next review (your design).caveats- at most 4 short strings:[fix]findings first in the page's order (it lists them most urgent first; take the first four when there are more), then[check]findings in the page's order.[]when there are none of either.
{
"task": "design",
"summary": "one sentence",
"diagnosis": "one sentence",
"busfactor": [ { "role": "Program", "lead": "a name", "backup": "a name", "bf": 2, "mechanism": "one short sentence" } ],
"entity": { "pick": "umbrella", "note": "one sentence" },
"shape": { "pick": "board", "split": "one sentence", "note": "one sentence" },
"succession": { "pick": "twodeep", "note": "one sentence" },
"burnout": [ { "person": "a name", "pleasant": "yes", "action": "one short sentence" } ],
"coi": { "undisclosed": [], "note": "one sentence" },
"hire": { "pick": "restructure", "note": "one sentence" },
"checks": [ { "check": "Bus factor", "pass": false } ],
"signals": [ "what to record and what it changes" ],
"caveats": [ "a caveat" ]
}
The page holds it to its own map: every role's lead, backup and bus factor, the entity, decision-rights, succession and paid-hire picks, every organizer's pleasantness answer, the undisclosed interests and the checks; no figure may appear that the input does not hold.
cards
cards- one object per role in the page's order:role(exactly),lead(exactly),backup(exactly, or "to be named" when the page has none),scope(one or two sentences: what the role owns),not(one sentence: what the role will not absorb),joinpath(one sentence: how a newcomer joins or shadows this role),decides(standing,operationalorteam, as the page places the role).coi_clause- two or three sentences of charter text: business is a consequence, not the goal; disclose in the register; abstain on decisions about one's own talk, employer or vendor stake.note- one sentence, at most 30 words, for the organizers: what to settle before the charter is shared.
{
"task": "cards",
"cards": [ { "role": "Program", "lead": "a name", "backup": "a name", "scope": "one or two sentences", "not": "one sentence", "joinpath": "one sentence", "decides": "operational" } ],
"coi_clause": "two or three sentences",
"note": "one sentence"
}
The page requires one card per role in order with its lead, backup (or to be named) and decision layer, a scope and a boundary on each, and a clause that covers disclosure and abstention.
Worked examples - every lane, every sample
These are the page's three free samples (fictional events and people; every date is illustrative). Each input is exactly what the page sends; each reply is the saved reply the sample replays, shipped as the validating model wrote it; the last line is the page's own check. Every sample has both lanes.
Lakeview JS Conference (community, fourth edition) - design
6 roles, 3 at bus factor 1; decision rights board-plus-organizers split; succession first two-deep rule; paid hire stay volunteer and restructure; 0 of 6 checks.
Input:
{
"task": "design",
"team": "Event: Lakeview JS Conference; community-run; 4 editions so far; attendance 420; budget per edition 85000\nLegal shape today: No entity; contracts signed yes; a parent foundation could umbrella it no; a deadline forces the structure soon no; mandate a multi-year institution\nShape: parallel tracks or several cities no; part of a voluntary network of independent communities no; organizing has become a year-round burden yes; money for vendors or a hire yes\nPractice: written charter no; event accounts on a personal account yes; conflict-of-interest register no; structure review cadence no; paid-hire gate run this edition no",
"roles": "Roles, in order (role | lead | backup | hours a month | written scope | bus factor):\n1. Lead organizer | Elif | NO BACKUP | 30 | no | 1 | the lead role\n2. Program | Elif | Omar | 20 | yes | 2\n3. Sponsorship | Elif | NO BACKUP | 15 | no | 1\n4. Finance | Bruno | Elif | 6 | yes | 2\n5. Logistics and venue | Chen | Bruno | 8 | yes | 2\n6. Volunteers | Dana | NO BACKUP | 6 | no | 1\nLoad per person (roles led, backups held, hours led):\n Elif | leads Lead organizer, Program, Sponsorship | backs up Finance | 65 h/month\n Chen | leads Logistics and venue | backs up none | 8 h/month\n Bruno | leads Finance | backs up Logistics and venue | 6 h/month\n Dana | leads Volunteers | backs up none | 6 h/month\n Omar | leads none | backs up Program | 0 h/month\nOrganizers (pleasant | return | commercial interest | disclosed | departing | successor | handover months):\n Elif | no | network and fun | none | no | no | - | -\n Omar | yes | learning | none | no | no | - | -\n Bruno | yes | NOT NAMED | none | no | no | - | -\n Chen | unasked | freelance leads | freelance web agency | no | no | - | -\n Dana | yes | less isolation | none | no | yes | - | -",
"facts": "Bus factor 1: Lead organizer, Sponsorship, Volunteers; missing standard roles: marketing, code-of-conduct team\nEntity: today none; the page's pick is own; rungs No entity available; Fiscal sponsorship / umbrella default; Own non-profit entity promoted\nDecision rights: the page's pick is board; rungs Consensus committee available; Domain-lead federation available; Board-plus-organizers split default\nWhere each role decides: Lead organizer standing; Program operational; Sponsorship standing; Finance standing; Logistics and venue operational; Volunteers operational\nSuccession: the page's first mechanism is twodeep; rungs Two-deep rule default; Written charter + shared admin access available; Groomed 6-12-month handover available; Planned leadership rotation deleted; Contributor-to-decision-maker pipeline not yet\nPaid-hire gate: the page's verdict is restructure; rungs Stay volunteer and restructure default; Outsource to vendors available; Hire paid staff not yet\nChecks (0 of 6): Bus factor (Every named role, lead included, has a backup or an active mechanism closing it) MISSING; Charter (A written charter names each role's scope, join-path and decision rights) MISSING; Succession (The lead's successor is named, inside a 6-12-month handover window) MISSING; Pleasantness (The pleasantness check ran for every organizer, every no with a structural response) MISSING; Paid-hire gate (The paid-hire gate was run this edition, with the verdict recorded) MISSING; Conflict of interest (Every organizer has a conflict-of-interest entry and a named return) MISSING\nSignals to record: Bus factor per role, each edition (self-set: at least 2); Pleasantness answers per person, each year (self-set); Hours per organizer against the workload budget (self-set)\nFindings (19):\n[fix] lead_bf1 (bus factor): The lead role (Lead organizer, Elif) has no backup - everything routes through one person; give it a mechanism this edition.\n[fix] exposure (entity): No entity with signed contracts - personal exposure in a zero-paperwork costume; contracts and liability sit on whoever signed. Move up at the first signed contract.\n[fix] unpleasant (burnout): Not pleasant any more: Elif - a structural response this edition (shrink scope, add a backup, rotate out for an edition), with an owner.\n[fix] coi (conflict of interest): Commercial interest not in the register: Chen (freelance web agency) - disclose, and abstain on decisions about their own talk, employer or vendor stake. A disclosed lead-generation return is legitimate.\n[fix] no_successor (succession): Dana is leaving with no successor named - pick one among active volunteers and hand over across a named window.\n[fix] accounts (succession): Event accounts (domain, socials, ticketing, bank) sit on a personal account - move them to shared custody with a backup admin this week.\n[fix] stacked (burnout): Elif leads 3 roles (Lead organizer, Program, Sponsorship) - doing the job of several people; split the roles.\n[fix] bf1 (bus factor): Bus factor 1: Sponsorship (Elif); Volunteers (Dana) - name a backup who could run the area next edition.\n[check] no_scope (roles): No written scope: Lead organizer, Sponsorship, Volunteers - one paragraph each, including what the role will not absorb.\n[check] missing_roles (roles): No standing role for: marketing, code-of-conduct team - the standard taxonomy for a conference.\n[check] hours (burnout): Elif 65 h/month - above the 4-8 hours a month budget; a role structurally over its budget is a role to split, not a person to exhort (self-set budget; the published estimate is meetup-scale).\n[check] unasked (burnout): Pleasantness not asked: Chen - ask each person directly, every year.\n[check] no_return (burnout): No named return for: Bruno - record what each organizer gets back; a return that disappears is an early departure signal.\n[check] no_register (conflict of interest): No conflict-of-interest register - every organizer lists employer, own business and sponsor ties; write \"business is a consequence, not the goal\" into the founding text.\n[check] charter (succession): No written charter - write roles, scopes, join-paths and decision rights down; an opaque leadership reads as a clique.\n[check] gate_run (paid hire): Re-run the paid-hire gate every edition and record the verdict - it is a recurring gate, not a one-time decision.\n[check] checks (measurement): Standing-team checks: 0 of 6 (all self-set).\n[info] no_rotation (succession): Planned leadership rotation is deleted: no board, staff or fiscal sponsor holds the institutional knowledge - rotation without that layer is the documented fragile design.\n[info] cadence (review): Put the structure on a review cadence - a design nobody may revisit ossifies into the org chart nobody follows."
}
Reply:
{
"task": "design",
"summary": "The lead organizer role, held by Elif, has no backup and everything routes through one person; give it a backup mechanism this edition.",
"diagnosis": "Elif personally leads 3 roles - lead organizer, program and sponsorship - for 65 hours a month and also backs up finance, so the team's single-point-of-failure roles all run through one person while Chen, Bruno, Dana and Omar each carry one lighter role.",
"busfactor": [
{
"role": "Lead organizer",
"lead": "Elif",
"backup": "",
"bf": 1,
"mechanism": "No backup and no mechanism exist yet, so the role depends entirely on Elif."
},
{
"role": "Program",
"lead": "Elif",
"backup": "Omar",
"bf": 2,
"mechanism": "Omar backs up Program, closing the gap behind Elif."
},
{
"role": "Sponsorship",
"lead": "Elif",
"backup": "",
"bf": 1,
"mechanism": "No backup is named, so sponsorship depends entirely on Elif with no mechanism closing the gap."
},
{
"role": "Finance",
"lead": "Bruno",
"backup": "Elif",
"bf": 2,
"mechanism": "Elif backs up Finance, closing the gap behind Bruno."
},
{
"role": "Logistics and venue",
"lead": "Chen",
"backup": "Bruno",
"bf": 2,
"mechanism": "Bruno backs up Logistics and venue, closing the gap behind Chen."
},
{
"role": "Volunteers",
"lead": "Dana",
"backup": "",
"bf": 1,
"mechanism": "No backup is named, and with Dana departing and no successor yet picked, the gap stays open."
}
],
"entity": {
"pick": "own",
"note": "The page promotes its own non-profit entity, since contracts are already being signed with no entity behind them and the event's mandate is a multi-year institution."
},
"shape": {
"pick": "board",
"split": "The standing layer owns money, contracts, legal and code-of-conduct escalation and seats the lead organizer, sponsorship and finance roles, while the operational team owns program and logistics, seating program, logistics and venue, and volunteers.",
"note": "Board-plus-organizers split is the page's pick now that the event holds signed contracts and an 85000 budget each edition."
},
"succession": {
"pick": "twodeep",
"note": "The two-deep rule is the page's first mechanism, though planned leadership rotation is deleted since no standing continuity layer exists yet."
},
"burnout": [
{
"person": "Elif",
"pleasant": "no",
"action": "Split the stacked roles, add a backup and consider rotating out for an edition, with an owner to be named this edition."
},
{
"person": "Omar",
"pleasant": "yes",
"action": "Omar continues backing up Program for the learning they get from it, which is keeping it pleasant."
},
{
"person": "Bruno",
"pleasant": "yes",
"action": "Bruno's work remains pleasant, but record a named return for them since none is on file yet."
},
{
"person": "Chen",
"pleasant": "unasked",
"action": "Ask Chen directly this year, since pleasantness has not been asked yet."
},
{
"person": "Dana",
"pleasant": "yes",
"action": "Volunteering keeps bringing Dana less isolation, but since they are departing, a successor needs naming among active volunteers with a handover window."
}
],
"coi": {
"undisclosed": [
"Chen"
],
"note": "Chen's freelance web agency interest is not yet in a conflict-of-interest register, so it should be disclosed and Chen should abstain on related decisions."
},
"hire": {
"pick": "restructure",
"note": "The verdict is to restructure and stay volunteer, which the page sets despite this edition's year-round burden and money for vendors or a hire."
},
"checks": [
{
"check": "Bus factor",
"pass": false
},
{
"check": "Charter",
"pass": false
},
{
"check": "Succession",
"pass": false
},
{
"check": "Pleasantness",
"pass": false
},
{
"check": "Paid-hire gate",
"pass": false
},
{
"check": "Conflict of interest",
"pass": false
}
],
"signals": [
"Bus factor per role, tracked each edition against the self-set floor of at least 2 - a role dropping below it triggers naming a new backup before the next event.",
"Pleasantness answers per person, recorded each year - any new no triggers a structural response with a named owner before the next review.",
"Hours per organizer measured against the 4-8 hours a month workload budget - exceeding it triggers a role split before the next review."
],
"caveats": [
"The lead organizer role (Elif) has no backup, so a mechanism is needed this edition.",
"No entity exists despite signed contracts, creating personal exposure that should move up now.",
"Elif no longer finds organizing pleasant and needs a structural response this edition, with an owner.",
"Chen's freelance web agency interest is not in the conflict-of-interest register and needs disclosure, with abstention on related decisions."
]
}
The page's check: kept - nothing to flag
Lakeview JS Conference (community, fourth edition) - cards
6 roles, 3 at bus factor 1; decision rights board-plus-organizers split; succession first two-deep rule; paid hire stay volunteer and restructure; 0 of 6 checks.
Input:
{
"task": "cards",
"team": "Event: Lakeview JS Conference; community-run; 4 editions so far; attendance 420; budget per edition 85000\nLegal shape today: No entity; contracts signed yes; a parent foundation could umbrella it no; a deadline forces the structure soon no; mandate a multi-year institution\nShape: parallel tracks or several cities no; part of a voluntary network of independent communities no; organizing has become a year-round burden yes; money for vendors or a hire yes\nPractice: written charter no; event accounts on a personal account yes; conflict-of-interest register no; structure review cadence no; paid-hire gate run this edition no",
"roles": "Roles, in order (role | lead | backup | hours a month | written scope | bus factor):\n1. Lead organizer | Elif | NO BACKUP | 30 | no | 1 | the lead role\n2. Program | Elif | Omar | 20 | yes | 2\n3. Sponsorship | Elif | NO BACKUP | 15 | no | 1\n4. Finance | Bruno | Elif | 6 | yes | 2\n5. Logistics and venue | Chen | Bruno | 8 | yes | 2\n6. Volunteers | Dana | NO BACKUP | 6 | no | 1\nLoad per person (roles led, backups held, hours led):\n Elif | leads Lead organizer, Program, Sponsorship | backs up Finance | 65 h/month\n Chen | leads Logistics and venue | backs up none | 8 h/month\n Bruno | leads Finance | backs up Logistics and venue | 6 h/month\n Dana | leads Volunteers | backs up none | 6 h/month\n Omar | leads none | backs up Program | 0 h/month\nOrganizers (pleasant | return | commercial interest | disclosed | departing | successor | handover months):\n Elif | no | network and fun | none | no | no | - | -\n Omar | yes | learning | none | no | no | - | -\n Bruno | yes | NOT NAMED | none | no | no | - | -\n Chen | unasked | freelance leads | freelance web agency | no | no | - | -\n Dana | yes | less isolation | none | no | yes | - | -",
"facts": "Bus factor 1: Lead organizer, Sponsorship, Volunteers; missing standard roles: marketing, code-of-conduct team\nEntity: today none; the page's pick is own; rungs No entity available; Fiscal sponsorship / umbrella default; Own non-profit entity promoted\nDecision rights: the page's pick is board; rungs Consensus committee available; Domain-lead federation available; Board-plus-organizers split default\nWhere each role decides: Lead organizer standing; Program operational; Sponsorship standing; Finance standing; Logistics and venue operational; Volunteers operational\nSuccession: the page's first mechanism is twodeep; rungs Two-deep rule default; Written charter + shared admin access available; Groomed 6-12-month handover available; Planned leadership rotation deleted; Contributor-to-decision-maker pipeline not yet\nPaid-hire gate: the page's verdict is restructure; rungs Stay volunteer and restructure default; Outsource to vendors available; Hire paid staff not yet\nChecks (0 of 6): Bus factor (Every named role, lead included, has a backup or an active mechanism closing it) MISSING; Charter (A written charter names each role's scope, join-path and decision rights) MISSING; Succession (The lead's successor is named, inside a 6-12-month handover window) MISSING; Pleasantness (The pleasantness check ran for every organizer, every no with a structural response) MISSING; Paid-hire gate (The paid-hire gate was run this edition, with the verdict recorded) MISSING; Conflict of interest (Every organizer has a conflict-of-interest entry and a named return) MISSING\nSignals to record: Bus factor per role, each edition (self-set: at least 2); Pleasantness answers per person, each year (self-set); Hours per organizer against the workload budget (self-set)\nFindings (19):\n[fix] lead_bf1 (bus factor): The lead role (Lead organizer, Elif) has no backup - everything routes through one person; give it a mechanism this edition.\n[fix] exposure (entity): No entity with signed contracts - personal exposure in a zero-paperwork costume; contracts and liability sit on whoever signed. Move up at the first signed contract.\n[fix] unpleasant (burnout): Not pleasant any more: Elif - a structural response this edition (shrink scope, add a backup, rotate out for an edition), with an owner.\n[fix] coi (conflict of interest): Commercial interest not in the register: Chen (freelance web agency) - disclose, and abstain on decisions about their own talk, employer or vendor stake. A disclosed lead-generation return is legitimate.\n[fix] no_successor (succession): Dana is leaving with no successor named - pick one among active volunteers and hand over across a named window.\n[fix] accounts (succession): Event accounts (domain, socials, ticketing, bank) sit on a personal account - move them to shared custody with a backup admin this week.\n[fix] stacked (burnout): Elif leads 3 roles (Lead organizer, Program, Sponsorship) - doing the job of several people; split the roles.\n[fix] bf1 (bus factor): Bus factor 1: Sponsorship (Elif); Volunteers (Dana) - name a backup who could run the area next edition.\n[check] no_scope (roles): No written scope: Lead organizer, Sponsorship, Volunteers - one paragraph each, including what the role will not absorb.\n[check] missing_roles (roles): No standing role for: marketing, code-of-conduct team - the standard taxonomy for a conference.\n[check] hours (burnout): Elif 65 h/month - above the 4-8 hours a month budget; a role structurally over its budget is a role to split, not a person to exhort (self-set budget; the published estimate is meetup-scale).\n[check] unasked (burnout): Pleasantness not asked: Chen - ask each person directly, every year.\n[check] no_return (burnout): No named return for: Bruno - record what each organizer gets back; a return that disappears is an early departure signal.\n[check] no_register (conflict of interest): No conflict-of-interest register - every organizer lists employer, own business and sponsor ties; write \"business is a consequence, not the goal\" into the founding text.\n[check] charter (succession): No written charter - write roles, scopes, join-paths and decision rights down; an opaque leadership reads as a clique.\n[check] gate_run (paid hire): Re-run the paid-hire gate every edition and record the verdict - it is a recurring gate, not a one-time decision.\n[check] checks (measurement): Standing-team checks: 0 of 6 (all self-set).\n[info] no_rotation (succession): Planned leadership rotation is deleted: no board, staff or fiscal sponsor holds the institutional knowledge - rotation without that layer is the documented fragile design.\n[info] cadence (review): Put the structure on a review cadence - a design nobody may revisit ossifies into the org chart nobody follows."
}
Reply:
{
"task": "cards",
"cards": [
{
"role": "Lead organizer",
"lead": "Elif",
"backup": "to be named",
"scope": "The lead organizer coordinates across the program, sponsorship, finance, logistics and volunteer leads, and is the point of escalation when they cannot agree. They also carry the event's external relationships, including its contracts.",
"not": "The lead organizer will not take over the day-to-day work of program, sponsorship, finance, logistics or volunteers.",
"joinpath": "A newcomer shadows Elif across a full edition before being named the role's backup.",
"decides": "standing"
},
{
"role": "Program",
"lead": "Elif",
"backup": "Omar",
"scope": "Program owns the call for proposals, talk selection and the schedule for Lakeview JS Conference.",
"not": "Program will not handle sponsor contracts or venue logistics.",
"joinpath": "A newcomer shadows Omar, the current backup, through a review cycle before co-leading selections.",
"decides": "operational"
},
{
"role": "Sponsorship",
"lead": "Elif",
"backup": "to be named",
"scope": "Sponsorship owns outreach to sponsors, the sponsorship packages and the relationship with paying sponsors through the event.",
"not": "Sponsorship will not negotiate the event's overall contracts or finances on its own.",
"joinpath": "A newcomer shadows Elif on sponsor calls for one edition before being named the role's backup.",
"decides": "standing"
},
{
"role": "Finance",
"lead": "Bruno",
"backup": "Elif",
"scope": "Finance owns the budget, tracking income and spending against the 85000 per-edition budget and keeping the event's financial records.",
"not": "Finance will not sign sponsorship or vendor contracts on its own.",
"joinpath": "A newcomer shadows Bruno through a full budget cycle before taking on reconciliation tasks.",
"decides": "standing"
},
{
"role": "Logistics and venue",
"lead": "Chen",
"backup": "Bruno",
"scope": "Logistics and venue owns the venue booking, on-site setup and vendor coordination for each edition.",
"not": "Logistics and venue will not manage the program schedule or sponsor relationships.",
"joinpath": "A newcomer shadows Chen on-site for one edition before taking on a vendor relationship directly.",
"decides": "operational"
},
{
"role": "Volunteers",
"lead": "Dana",
"backup": "to be named",
"scope": "Volunteers owns recruiting, scheduling and supporting the volunteer team during the event.",
"not": "Volunteers will not take on paid-staff management or financial decisions.",
"joinpath": "A newcomer shadows Dana across one edition before being named the role's backup, since Dana is departing.",
"decides": "operational"
}
],
"coi_clause": "Business among attendees at Lakeview JS Conference is welcome, but organizers do not run the event mainly to prospect for themselves - business is a consequence, not the goal. Every organizer discloses their employer, own business and vendor ties in a conflict-of-interest register, and abstains from decisions about their own talk, employer or vendor stake.",
"note": "Before sharing the charter, settle the lead organizer, sponsorship and volunteers' missing backups and written scopes, and get Chen's freelance interest into the conflict-of-interest register."
}
The page's check: kept - nothing to flag
Rust Night Porto (a two-person meetup in a network) - design
2 roles, 0 at bus factor 1; decision rights consensus committee; succession first two-deep rule; paid hire stay volunteer and restructure; 6 of 6 checks.
Input:
{
"task": "design",
"team": "Event: Rust Night Porto; community-run; 18 editions so far; attendance 60; budget per edition 1500\nLegal shape today: No entity; contracts signed no; a parent foundation could umbrella it no; a deadline forces the structure soon no; mandate a patch for the next edition\nShape: parallel tracks or several cities no; part of a voluntary network of independent communities yes; organizing has become a year-round burden no; money for vendors or a hire no\nPractice: written charter yes; event accounts on a personal account no; conflict-of-interest register yes; structure review cadence no; paid-hire gate run this edition yes",
"roles": "Roles, in order (role | lead | backup | hours a month | written scope | bus factor):\n1. Organizer | Ana | Rui | 6 | yes | 2 | the lead role\n2. Venue and food | Rui | Ana | 4 | yes | 2\nLoad per person (roles led, backups held, hours led):\n Ana | leads Organizer | backs up Venue and food | 6 h/month\n Rui | leads Venue and food | backs up Organizer | 4 h/month\nOrganizers (pleasant | return | commercial interest | disclosed | departing | successor | handover months):\n Ana | yes | fun | none | yes | no | - | -\n Rui | yes | freelance leads | Rust consulting | yes | no | - | -",
"facts": "Bus factor 1: none; missing standard roles: program, sponsorship, finance, marketing, volunteers, code-of-conduct team\nEntity: today none; the page's pick is none; rungs No entity default; Fiscal sponsorship / umbrella available; Own non-profit entity not yet\nDecision rights: the page's pick is consensus; rungs Consensus committee default; Domain-lead federation available; Board-plus-organizers split not yet\nWhere each role decides: Organizer team; Venue and food team\nSuccession: the page's first mechanism is twodeep; rungs Two-deep rule default; Written charter + shared admin access in place; Groomed 6-12-month handover available; Planned leadership rotation deleted; Contributor-to-decision-maker pipeline not yet\nPaid-hire gate: the page's verdict is restructure; rungs Stay volunteer and restructure default; Outsource to vendors deleted; Hire paid staff deleted\nChecks (6 of 6): Bus factor (Every named role, lead included, has a backup or an active mechanism closing it) passed; Charter (A written charter names each role's scope, join-path and decision rights) passed; Succession (The lead's successor is named, inside a 6-12-month handover window) passed; Pleasantness (The pleasantness check ran for every organizer, every no with a structural response) passed; Paid-hire gate (The paid-hire gate was run this edition, with the verdict recorded) passed; Conflict of interest (Every organizer has a conflict-of-interest entry and a named return) passed\nSignals to record: Bus factor per role, each edition (self-set: at least 2); Pleasantness answers per person, each year (self-set); Hours per organizer against the workload budget (self-set)\nFindings (6):\n[info] missing_roles (roles): No standing role for: program, sponsorship, finance, marketing, volunteers, code-of-conduct team - fine at meetup scale; merge what the event does not need.\n[info] no_rotation (succession): Planned leadership rotation is deleted: no board, staff or fiscal sponsor holds the institutional knowledge - rotation without that layer is the documented fragile design.\n[info] no_money (paid hire): No money for vendors or a hire: both paid rungs are deleted and restructuring is the whole menu.\n[info] network (peer network): A peer network of independent communities: write a two-minute manifesto, a written opt-out path, a short minimum binding set and 2-3 shared assets each with an owner - never fold it into the chapter's own shape.\n[info] cadence (review): Put the structure on a review cadence - a design nobody may revisit ossifies into the org chart nobody follows.\n[info] checks (measurement): Standing-team checks: 6 of 6 (all self-set)."
}
Reply:
{
"task": "design",
"summary": "Both roles already hold bus factor 2 with named backups and both organizers find organizing pleasant, so the first change is simply a review cadence for the structure.",
"diagnosis": "Ana leads Organizer for 6 hours a month while backing up Venue and food, and Rui leads Venue and food for 4 hours a month while backing up Organizer, so the two of them fully cover each other's roles.",
"busfactor": [
{
"role": "Organizer",
"lead": "Ana",
"backup": "Rui",
"bf": 2,
"mechanism": "Rui backs up Organizer, closing the gap behind Ana."
},
{
"role": "Venue and food",
"lead": "Rui",
"backup": "Ana",
"bf": 2,
"mechanism": "Ana backs up Venue and food, closing the gap behind Rui."
}
],
"entity": {
"pick": "none",
"note": "No entity fits Rust Night Porto's meetup scale, since no contracts are signed and there is no money for vendors or a hire."
},
"shape": {
"pick": "consensus",
"split": "As a single consensus unit, Organizer and Venue and food decide together as one team rather than splitting into separate standing and operational layers.",
"note": "Consensus committee fits the two of them deciding together at meetup scale."
},
"succession": {
"pick": "twodeep",
"note": "The two-deep rule sits alongside a written charter already in place, though planned leadership rotation is deleted since there is no standing continuity layer."
},
"burnout": [
{
"person": "Ana",
"pleasant": "yes",
"action": "Ana finds organizing fun, and Rui backing up Organizer keeps that sustainable."
},
{
"person": "Rui",
"pleasant": "yes",
"action": "Rui's organizing brings freelance leads, and Ana backing up Venue and food keeps that sustainable."
}
],
"coi": {
"undisclosed": [],
"note": "Rui's Rust consulting interest is already disclosed in the conflict-of-interest register, and Ana reports no commercial interest."
},
"hire": {
"pick": "restructure",
"note": "Both paid rungs are deleted since there is no money for vendors or a hire, so restructuring is the whole menu, and the gate was run this edition."
},
"checks": [
{
"check": "Bus factor",
"pass": true
},
{
"check": "Charter",
"pass": true
},
{
"check": "Succession",
"pass": true
},
{
"check": "Pleasantness",
"pass": true
},
{
"check": "Paid-hire gate",
"pass": true
},
{
"check": "Conflict of interest",
"pass": true
}
],
"signals": [
"Bus factor per role, tracked each edition against the self-set floor of at least 2 - a role dropping below it would trigger naming a new backup before the next event.",
"Pleasantness answers per person, recorded each year - either organizer answering no would trigger a structural response with a named owner before the next review.",
"Hours per organizer measured against the workload budget each month - exceeding it would trigger redistributing tasks before the next review."
],
"caveats": []
}
The page's check: kept - nothing to flag
Rust Night Porto (a two-person meetup in a network) - cards
2 roles, 0 at bus factor 1; decision rights consensus committee; succession first two-deep rule; paid hire stay volunteer and restructure; 6 of 6 checks.
Input:
{
"task": "cards",
"team": "Event: Rust Night Porto; community-run; 18 editions so far; attendance 60; budget per edition 1500\nLegal shape today: No entity; contracts signed no; a parent foundation could umbrella it no; a deadline forces the structure soon no; mandate a patch for the next edition\nShape: parallel tracks or several cities no; part of a voluntary network of independent communities yes; organizing has become a year-round burden no; money for vendors or a hire no\nPractice: written charter yes; event accounts on a personal account no; conflict-of-interest register yes; structure review cadence no; paid-hire gate run this edition yes",
"roles": "Roles, in order (role | lead | backup | hours a month | written scope | bus factor):\n1. Organizer | Ana | Rui | 6 | yes | 2 | the lead role\n2. Venue and food | Rui | Ana | 4 | yes | 2\nLoad per person (roles led, backups held, hours led):\n Ana | leads Organizer | backs up Venue and food | 6 h/month\n Rui | leads Venue and food | backs up Organizer | 4 h/month\nOrganizers (pleasant | return | commercial interest | disclosed | departing | successor | handover months):\n Ana | yes | fun | none | yes | no | - | -\n Rui | yes | freelance leads | Rust consulting | yes | no | - | -",
"facts": "Bus factor 1: none; missing standard roles: program, sponsorship, finance, marketing, volunteers, code-of-conduct team\nEntity: today none; the page's pick is none; rungs No entity default; Fiscal sponsorship / umbrella available; Own non-profit entity not yet\nDecision rights: the page's pick is consensus; rungs Consensus committee default; Domain-lead federation available; Board-plus-organizers split not yet\nWhere each role decides: Organizer team; Venue and food team\nSuccession: the page's first mechanism is twodeep; rungs Two-deep rule default; Written charter + shared admin access in place; Groomed 6-12-month handover available; Planned leadership rotation deleted; Contributor-to-decision-maker pipeline not yet\nPaid-hire gate: the page's verdict is restructure; rungs Stay volunteer and restructure default; Outsource to vendors deleted; Hire paid staff deleted\nChecks (6 of 6): Bus factor (Every named role, lead included, has a backup or an active mechanism closing it) passed; Charter (A written charter names each role's scope, join-path and decision rights) passed; Succession (The lead's successor is named, inside a 6-12-month handover window) passed; Pleasantness (The pleasantness check ran for every organizer, every no with a structural response) passed; Paid-hire gate (The paid-hire gate was run this edition, with the verdict recorded) passed; Conflict of interest (Every organizer has a conflict-of-interest entry and a named return) passed\nSignals to record: Bus factor per role, each edition (self-set: at least 2); Pleasantness answers per person, each year (self-set); Hours per organizer against the workload budget (self-set)\nFindings (6):\n[info] missing_roles (roles): No standing role for: program, sponsorship, finance, marketing, volunteers, code-of-conduct team - fine at meetup scale; merge what the event does not need.\n[info] no_rotation (succession): Planned leadership rotation is deleted: no board, staff or fiscal sponsor holds the institutional knowledge - rotation without that layer is the documented fragile design.\n[info] no_money (paid hire): No money for vendors or a hire: both paid rungs are deleted and restructuring is the whole menu.\n[info] network (peer network): A peer network of independent communities: write a two-minute manifesto, a written opt-out path, a short minimum binding set and 2-3 shared assets each with an owner - never fold it into the chapter's own shape.\n[info] cadence (review): Put the structure on a review cadence - a design nobody may revisit ossifies into the org chart nobody follows.\n[info] checks (measurement): Standing-team checks: 6 of 6 (all self-set)."
}
Reply:
{
"task": "cards",
"cards": [
{
"role": "Organizer",
"lead": "Ana",
"backup": "Rui",
"scope": "Organizer owns the overall coordination of Rust Night Porto, including scheduling each meetup and being the point of contact for the group.",
"not": "Organizer will not handle venue bookings or food arrangements directly.",
"joinpath": "A newcomer shadows Ana at a meetup before taking on backup duties, as Rui already does.",
"decides": "team"
},
{
"role": "Venue and food",
"lead": "Rui",
"backup": "Ana",
"scope": "Venue and food owns booking the venue and arranging food for each meetup.",
"not": "Venue and food will not manage the overall meetup schedule or group communications.",
"joinpath": "A newcomer shadows Rui on venue and food arrangements before taking on backup duties, as Ana already does.",
"decides": "team"
}
],
"coi_clause": "Business among attendees at Rust Night Porto is welcome, but organizers do not run the meetup mainly to prospect for themselves - business is a consequence, not the goal. Every organizer discloses their employer, own business and vendor ties in the conflict-of-interest register, and abstains from decisions about their own talk, employer or vendor stake.",
"note": "Nothing needs settling before sharing, since Rust Night Porto already has a written charter, a conflict-of-interest register and both backups named."
}
The page's check: kept - nothing to flag
Cloudline Dev Summit (company-run, a director handing over) - design
6 roles, 1 at bus factor 1; decision rights domain-lead federation; succession first groomed 6-12-month handover; paid hire hire paid staff; 3 of 6 checks.
Input:
{
"task": "design",
"team": "Event: Cloudline Dev Summit; company-run; 6 editions so far; attendance 2400; budget per edition 600000\nLegal shape today: Own non-profit entity; contracts signed yes; a parent foundation could umbrella it no; a deadline forces the structure soon no; mandate a multi-year institution\nShape: parallel tracks or several cities yes; part of a voluntary network of independent communities no; organizing has become a year-round burden yes; money for vendors or a hire yes\nPractice: written charter yes; event accounts on a personal account no; conflict-of-interest register yes; structure review cadence yes; paid-hire gate run this edition no",
"roles": "Roles, in order (role | lead | backup | hours a month | written scope | bus factor):\n1. Event director | Noor | Sam | 60 | yes | 2 | the lead role\n2. Program | Dev | Pia | 40 | yes | 2\n3. Sponsorship | Sam | Noor | 40 | yes | 2\n4. Operations | Pia | Dev | 50 | yes | 2\n5. Marketing | Lee | NO BACKUP | 30 | yes | 1\n6. Code of conduct team | Noor | Dev | 10 | yes | 2\nLoad per person (roles led, backups held, hours led):\n Noor | leads Event director, Code of conduct team | backs up Sponsorship | 70 h/month\n Pia | leads Operations | backs up Program | 50 h/month\n Sam | leads Sponsorship | backs up Event director | 40 h/month\n Dev | leads Program | backs up Operations, Code of conduct team | 40 h/month\n Lee | leads Marketing | backs up none | 30 h/month\nOrganizers (pleasant | return | commercial interest | disclosed | departing | successor | handover months):\n Noor | yes | career growth | none | yes | yes | Sam | 4\n Dev | yes | learning | none | yes | no | - | -\n Sam | yes | network | none | yes | no | - | -\n Pia | yes | the craft | none | yes | no | - | -\n Lee | no | visibility | none | yes | no | - | -",
"facts": "Bus factor 1: Marketing; missing standard roles: finance, volunteers\nEntity: deleted - the company is the entity\nDecision rights: the page's pick is federation; rungs Consensus committee available; Domain-lead federation promoted; Board-plus-organizers split default\nWhere each role decides: Event director standing; Program operational; Sponsorship standing; Operations operational; Marketing operational; Code of conduct team standing\nSuccession: the page's first mechanism is handover; rungs Two-deep rule default; Written charter + shared admin access in place; Groomed 6-12-month handover promoted; Planned leadership rotation available; Contributor-to-decision-maker pipeline promoted\nPaid-hire gate: the page's verdict is hire; rungs Stay volunteer and restructure available; Outsource to vendors available; Hire paid staff promoted\nChecks (3 of 6): Bus factor (Every named role, lead included, has a backup or an active mechanism closing it) MISSING; Charter (A written charter names each role's scope, join-path and decision rights) passed; Succession (The lead's successor is named, inside a 6-12-month handover window) MISSING; Pleasantness (The pleasantness check ran for every organizer, every no with a structural response) passed; Paid-hire gate (The paid-hire gate was run this edition, with the verdict recorded) MISSING; Conflict of interest (Every organizer has a conflict-of-interest entry and a named return) passed\nSignals to record: Bus factor per role, each edition (self-set: at least 2); Pleasantness answers per person, each year (self-set); Hours per organizer against the workload budget (self-set)\nFindings (9):\n[fix] unpleasant (burnout): Not pleasant any more: Lee - a structural response this edition (shrink scope, add a backup, rotate out for an edition), with an owner.\n[fix] bf1 (bus factor): Bus factor 1: Marketing (Lee) - name a backup who could run the area next edition.\n[check] missing_roles (roles): No standing role for: finance, volunteers - the standard taxonomy for a conference.\n[check] cross_backup (bus factor): Dev leads Program and backs up Operations, Code of conduct team - one person as everyone's backup is a hidden bus factor.\n[check] window (succession): Noor's handover window is 4 months - the published range is 6-12 months.\n[check] gate_run (paid hire): Re-run the paid-hire gate every edition and record the verdict - it is a recurring gate, not a one-time decision.\n[check] checks (measurement): Standing-team checks: 3 of 6 (all self-set).\n[info] company (entity): Company-run: the company already is the entity - the entity menu is deleted, and the paid-hire gate becomes a headcount request.\n[info] first_hire (paid hire): The gate says hire - as a headcount request: the first hire is an event coordinator or manager, part-time first - never a community manager; program and community leadership stay volunteer."
}
Reply:
{
"task": "design",
"summary": "Lee leads Marketing alone with no backup and organizing is no longer pleasant for them; the first change is giving Marketing a backup and a structural response this edition.",
"diagnosis": "Noor leads Event director and Code of conduct team while backing up Sponsorship at 70 hours a month, Dev backs up both Operations and Code of conduct team on top of leading Program, and Lee leads Marketing alone at 30 hours a month with no backup.",
"busfactor": [
{
"role": "Event director",
"lead": "Noor",
"backup": "Sam",
"bf": 2,
"mechanism": "Sam backs up Event director, closing the gap behind Noor."
},
{
"role": "Program",
"lead": "Dev",
"backup": "Pia",
"bf": 2,
"mechanism": "Pia backs up Program, closing the gap behind Dev."
},
{
"role": "Sponsorship",
"lead": "Sam",
"backup": "Noor",
"bf": 2,
"mechanism": "Noor backs up Sponsorship, closing the gap behind Sam."
},
{
"role": "Operations",
"lead": "Pia",
"backup": "Dev",
"bf": 2,
"mechanism": "Dev backs up Operations, closing the gap behind Pia."
},
{
"role": "Marketing",
"lead": "Lee",
"backup": "",
"bf": 1,
"mechanism": "No backup is named - a backup should be picked to run Marketing next edition."
},
{
"role": "Code of conduct team",
"lead": "Noor",
"backup": "Dev",
"bf": 2,
"mechanism": "Dev backs up Code of conduct team, closing the gap behind Noor."
}
],
"entity": {
"pick": "deleted",
"note": "The company already is the entity for this company-run event, so the entity menu is deleted, even though today's legal shape is recorded as an owned non-profit entity already in place."
},
"shape": {
"pick": "federation",
"split": "The standing layer holds Event director, Sponsorship and Code of conduct team, while the operational team runs Program, Operations and Marketing.",
"note": "A domain-lead federation is promoted over the default board-plus-organizers split since the summit runs parallel tracks."
},
"succession": {
"pick": "handover",
"note": "A groomed handover is already running from Noor to Sam, though the 4-month window falls short of the published 6-12 month range."
},
"burnout": [
{
"person": "Noor",
"pleasant": "yes",
"action": "Noor is handing over Event director to Sam across a 4-month window, and the role stays pleasant for them in the meantime."
},
{
"person": "Dev",
"pleasant": "yes",
"action": "Dev's backing up of both Operations and Code of conduct team on top of leading Program is a hidden bus factor to watch, even though the role is pleasant for them today."
},
{
"person": "Sam",
"pleasant": "yes",
"action": "Sam keeps backing up Event director while leading Sponsorship, which keeps the role pleasant for them and supports the planned handover from Noor."
},
{
"person": "Pia",
"pleasant": "yes",
"action": "Pia keeps backing up Program alongside leading Operations, which keeps the role pleasant for them."
},
{
"person": "Lee",
"pleasant": "no",
"action": "Give Marketing a named backup and a structural response this edition - shrink scope, add a backup, or rotate out - with an owner, since organizing is no longer pleasant for Lee."
}
],
"coi": {
"undisclosed": [],
"note": "No organizer reports a commercial interest, and the conflict-of-interest register already has every organizer's entry recorded as disclosed."
},
"hire": {
"pick": "hire",
"note": "As a headcount request, the first hire should be a part-time event coordinator or manager - never a community manager - though the gate was not run or recorded this edition, so it needs to be re-run and logged now."
},
"checks": [
{
"check": "Bus factor",
"pass": false
},
{
"check": "Charter",
"pass": true
},
{
"check": "Succession",
"pass": false
},
{
"check": "Pleasantness",
"pass": true
},
{
"check": "Paid-hire gate",
"pass": false
},
{
"check": "Conflict of interest",
"pass": true
}
],
"signals": [
"Bus factor per role, each edition (self-set: at least 2) - Marketing staying at bus factor 1 next edition would force a named backup before the gate reopens.",
"Pleasantness answers per person, each year (self-set) - a second no from Lee would escalate from a scope cut to a role rotation at the next review.",
"Hours per organizer against the workload budget (self-set) - Noor's 70 hours a month staying over budget at the next review would force splitting Event director and Code of conduct team."
],
"caveats": [
"Not pleasant any more for Lee - needs a structural response with an owner this edition.",
"Bus factor 1: Marketing (Lee) - name a backup who could run the area next edition.",
"No standing role for finance or volunteers - the standard taxonomy for a conference.",
"Dev backs up both Operations and Code of conduct team on top of leading Program - a hidden bus factor."
]
}
The page's check: kept - nothing to flag
Cloudline Dev Summit (company-run, a director handing over) - cards
6 roles, 1 at bus factor 1; decision rights domain-lead federation; succession first groomed 6-12-month handover; paid hire hire paid staff; 3 of 6 checks.
Input:
{
"task": "cards",
"team": "Event: Cloudline Dev Summit; company-run; 6 editions so far; attendance 2400; budget per edition 600000\nLegal shape today: Own non-profit entity; contracts signed yes; a parent foundation could umbrella it no; a deadline forces the structure soon no; mandate a multi-year institution\nShape: parallel tracks or several cities yes; part of a voluntary network of independent communities no; organizing has become a year-round burden yes; money for vendors or a hire yes\nPractice: written charter yes; event accounts on a personal account no; conflict-of-interest register yes; structure review cadence yes; paid-hire gate run this edition no",
"roles": "Roles, in order (role | lead | backup | hours a month | written scope | bus factor):\n1. Event director | Noor | Sam | 60 | yes | 2 | the lead role\n2. Program | Dev | Pia | 40 | yes | 2\n3. Sponsorship | Sam | Noor | 40 | yes | 2\n4. Operations | Pia | Dev | 50 | yes | 2\n5. Marketing | Lee | NO BACKUP | 30 | yes | 1\n6. Code of conduct team | Noor | Dev | 10 | yes | 2\nLoad per person (roles led, backups held, hours led):\n Noor | leads Event director, Code of conduct team | backs up Sponsorship | 70 h/month\n Pia | leads Operations | backs up Program | 50 h/month\n Sam | leads Sponsorship | backs up Event director | 40 h/month\n Dev | leads Program | backs up Operations, Code of conduct team | 40 h/month\n Lee | leads Marketing | backs up none | 30 h/month\nOrganizers (pleasant | return | commercial interest | disclosed | departing | successor | handover months):\n Noor | yes | career growth | none | yes | yes | Sam | 4\n Dev | yes | learning | none | yes | no | - | -\n Sam | yes | network | none | yes | no | - | -\n Pia | yes | the craft | none | yes | no | - | -\n Lee | no | visibility | none | yes | no | - | -",
"facts": "Bus factor 1: Marketing; missing standard roles: finance, volunteers\nEntity: deleted - the company is the entity\nDecision rights: the page's pick is federation; rungs Consensus committee available; Domain-lead federation promoted; Board-plus-organizers split default\nWhere each role decides: Event director standing; Program operational; Sponsorship standing; Operations operational; Marketing operational; Code of conduct team standing\nSuccession: the page's first mechanism is handover; rungs Two-deep rule default; Written charter + shared admin access in place; Groomed 6-12-month handover promoted; Planned leadership rotation available; Contributor-to-decision-maker pipeline promoted\nPaid-hire gate: the page's verdict is hire; rungs Stay volunteer and restructure available; Outsource to vendors available; Hire paid staff promoted\nChecks (3 of 6): Bus factor (Every named role, lead included, has a backup or an active mechanism closing it) MISSING; Charter (A written charter names each role's scope, join-path and decision rights) passed; Succession (The lead's successor is named, inside a 6-12-month handover window) MISSING; Pleasantness (The pleasantness check ran for every organizer, every no with a structural response) passed; Paid-hire gate (The paid-hire gate was run this edition, with the verdict recorded) MISSING; Conflict of interest (Every organizer has a conflict-of-interest entry and a named return) passed\nSignals to record: Bus factor per role, each edition (self-set: at least 2); Pleasantness answers per person, each year (self-set); Hours per organizer against the workload budget (self-set)\nFindings (9):\n[fix] unpleasant (burnout): Not pleasant any more: Lee - a structural response this edition (shrink scope, add a backup, rotate out for an edition), with an owner.\n[fix] bf1 (bus factor): Bus factor 1: Marketing (Lee) - name a backup who could run the area next edition.\n[check] missing_roles (roles): No standing role for: finance, volunteers - the standard taxonomy for a conference.\n[check] cross_backup (bus factor): Dev leads Program and backs up Operations, Code of conduct team - one person as everyone's backup is a hidden bus factor.\n[check] window (succession): Noor's handover window is 4 months - the published range is 6-12 months.\n[check] gate_run (paid hire): Re-run the paid-hire gate every edition and record the verdict - it is a recurring gate, not a one-time decision.\n[check] checks (measurement): Standing-team checks: 3 of 6 (all self-set).\n[info] company (entity): Company-run: the company already is the entity - the entity menu is deleted, and the paid-hire gate becomes a headcount request.\n[info] first_hire (paid hire): The gate says hire - as a headcount request: the first hire is an event coordinator or manager, part-time first - never a community manager; program and community leadership stay volunteer."
}
Reply:
{
"task": "cards",
"cards": [
{
"role": "Event director",
"lead": "Noor",
"backup": "Sam",
"scope": "Event director sets Cloudline Dev Summit's overall direction across its parallel tracks and represents the event externally, and makes the standing-layer calls on money, contracts, legal exposure and code-of-conduct escalation.",
"not": "It does not run any single track's program content or day-to-day operations, which stay with Program and Operations.",
"joinpath": "Sam, the named backup and successor, continues shadowing Noor through the handover, though the current 4-month window is short of the published 6-12 months.",
"decides": "standing"
},
{
"role": "Program",
"lead": "Dev",
"backup": "Pia",
"scope": "Program owns the call for proposals, speaker selection and the schedule across the summit's parallel tracks.",
"not": "It does not own sponsorship commitments or on-site operations, which sit with Sponsorship and Operations.",
"joinpath": "A newcomer shadows Dev alongside Pia, the current backup, across a track before taking one on alone.",
"decides": "operational"
},
{
"role": "Sponsorship",
"lead": "Sam",
"backup": "Noor",
"scope": "Sponsorship owns sponsor prospecting, pitching and contract terms, feeding the standing layer's funding and contract picture.",
"not": "It does not set program content or run on-site operations.",
"joinpath": "A newcomer shadows Sam alongside Noor, the current backup, through a sponsorship cycle before taking on accounts directly.",
"decides": "standing"
},
{
"role": "Operations",
"lead": "Pia",
"backup": "Dev",
"scope": "Operations owns on-site logistics, venue and the day-of run of show across the summit's tracks.",
"not": "It does not set program content or sponsorship terms, which stay with Program and Sponsorship.",
"joinpath": "A newcomer shadows Pia alongside Dev, the current backup, through a live edition before owning operations alone.",
"decides": "operational"
},
{
"role": "Marketing",
"lead": "Lee",
"backup": "to be named",
"scope": "Marketing owns promotion and audience growth for the summit across its channels.",
"not": "It does not set program content or handle sponsor contracts.",
"joinpath": "A newcomer shadows Lee this edition, aiming to be named backup given marketing currently has none.",
"decides": "operational"
},
{
"role": "Code of conduct team",
"lead": "Noor",
"backup": "Dev",
"scope": "Code of conduct team owns the code of conduct, incident intake and standing-layer escalation of conduct issues.",
"not": "It does not run program content or sponsorship decisions.",
"joinpath": "A newcomer shadows Noor alongside Dev, the current backup, to learn incident handling before joining the rota.",
"decides": "standing"
}
],
"coi_clause": "Organizers run Cloudline Dev Summit because it serves the event, not primarily to prospect for their own business; any business that follows is a consequence, not the goal. Each organizer's employer, own business and sponsor ties are listed in the conflict-of-interest register, and an organizer abstains from decisions about their own talk, employer or vendor stake.",
"note": "Before sharing the charter, name a backup for Marketing, address Lee's no on pleasantness, and resolve Dev backing up both Operations and Code of conduct team."
}
The page's check: kept - nothing to flag
The bus-factor map CSV for the first sample
role,lead,backup,hours_per_month,written_scope,bus_factor
Lead organizer,Elif,,30,no,1
Program,Elif,Omar,20,yes,2
Sponsorship,Elif,,15,no,1
Finance,Bruno,Elif,6,yes,2
Logistics and venue,Chen,Bruno,8,yes,2
Volunteers,Dana,,6,no,1
Build the input yourself
Load the engine in node (global.window = global; require("./crewkit.js"); require("./crewreply.js")), then:
const K = CrewKit, R = CrewReply;
const f = K.form({ name: "My Conf", pole: "community", attendance: "420", budget: "85000", entity: "none", contracts: "yes",
roles: fs.readFileSync("roles.csv", "utf8"), people: fs.readFileSync("people.csv", "utf8") });
// f.roles (lead, backup, bf, hours, scope, layer), f.bf1, f.load, f.people, f.missingStd
// f.recEntity, f.recShape, f.recSucc, f.recHire and their rungs, f.checks, f.findings
K.mapCSV(f) // role, lead, backup, hours_per_month, written_scope, bus_factor
const b = K.buildInput(f, "design"); // or "cards"
const wire = K.toWire(b.input); // POST this to /estimate and /run
// then: const reply = R.normalize(R.parseReply(job.output.output), "design");
// R.check(reply, b) // {status: kept or flagged, notes}
Get a token for the steps below from /tokens.html.
Step 1 — a tiny client helper
One function that sends JSON and unwraps the response. Paste a personal token from /tokens.html, or leave it empty and mint a guest token in step 2.
BASE="https://api.skillsafe.ai/v1/app-api"
TOKEN="paste-your-token-here" # from https://crew-chart.skillsafe.ai/tokens.html
api() { # api METHOD PATH [JSON-BODY]
curl -s -X "$1" "$BASE$2" -H "Content-Type: application/json" \
${TOKEN:+-H "Authorization: Bearer $TOKEN"} ${3:+--data "$3"}
}
import json, urllib.request
BASE = "https://api.skillsafe.ai/v1/app-api"
TOKEN = "paste-your-token-here" # from https://crew-chart.skillsafe.ai/tokens.html
def api(method, path, body=None, headers=None):
req = urllib.request.Request(BASE + path, method=method,
data=None if body is None else json.dumps(body).encode(),
headers={"Content-Type": "application/json", **({"Authorization": "Bearer " + TOKEN} if TOKEN else {}), **(headers or {})})
with urllib.request.urlopen(req) as r:
env = json.load(r)
if env.get("error"):
raise RuntimeError(env["error"])
return env["data"]
// crew-chart.mjs -- Node 18+ (global fetch)
const BASE = "https://api.skillsafe.ai/v1/app-api";
let TOKEN = "paste-your-token-here"; // from https://crew-chart.skillsafe.ai/tokens.html
async function api(method, path, body, headers = {}) {
const r = await fetch(BASE + path, { method, headers: { "Content-Type": "application/json",
...(TOKEN ? { Authorization: "Bearer " + TOKEN } : {}), ...headers },
body: body === undefined ? undefined : JSON.stringify(body) });
const env = await r.json();
if (env.error) throw new Error(env.error.code + ": " + env.error.message);
return env.data;
}
package main
import ("bytes"; "encoding/json"; "fmt"; "net/http")
const base = "https://api.skillsafe.ai/v1/app-api"
var token = "paste-your-token-here" // from https://crew-chart.skillsafe.ai/tokens.html
func api(method, path string, body any, headers map[string]string) (map[string]any, error) {
var buf bytes.Buffer
if body != nil { json.NewEncoder(&buf).Encode(body) }
req, _ := http.NewRequest(method, base+path, &buf)
req.Header.Set("Content-Type", "application/json")
if token != "" { req.Header.Set("Authorization", "Bearer "+token) }
for k, v := range headers { req.Header.Set(k, v) }
res, err := http.DefaultClient.Do(req)
if err != nil { return nil, err }
defer res.Body.Close()
var env struct { Data map[string]any; Error map[string]any }
json.NewDecoder(res.Body).Decode(&env)
if env.Error != nil { return nil, fmt.Errorf("%v", env.Error) }
return env.Data, nil
}
// CrewChart.java -- Java 17+, with com.fasterxml.jackson.core:jackson-databind
import com.fasterxml.jackson.databind.*;
import java.net.URI; import java.net.http.*; import java.util.*;
public class CrewChart {
static final String BASE = "https://api.skillsafe.ai/v1/app-api";
static String token = "paste-your-token-here"; // from https://crew-chart.skillsafe.ai/tokens.html
static final ObjectMapper M = new ObjectMapper();
static final HttpClient C = HttpClient.newHttpClient();
static JsonNode api(String method, String path, Object body, Map<String, String> headers) throws Exception {
var b = HttpRequest.newBuilder(URI.create(BASE + path)).header("Content-Type", "application/json");
if (!token.isEmpty()) b.header("Authorization", "Bearer " + token);
if (headers != null) headers.forEach(b::header);
b.method(method, body == null ? HttpRequest.BodyPublishers.noBody() : HttpRequest.BodyPublishers.ofString(M.writeValueAsString(body)));
JsonNode env = M.readTree(C.send(b.build(), HttpResponse.BodyHandlers.ofString()).body());
if (env.hasNonNull("error")) throw new RuntimeException(env.get("error").toString());
return env.get("data");
}
}
require "json"
require "net/http"
BASE = "https://api.skillsafe.ai/v1/app-api"
TOKEN = "paste-your-token-here" # from https://crew-chart.skillsafe.ai/tokens.html
def api(method, path, body = nil, headers = {})
uri = URI(BASE + path)
req = Net::HTTPGenericRequest.new(method, !body.nil?, true, uri.request_uri,
{ "Content-Type" => "application/json" }.merge(TOKEN.empty? ? {} : { "Authorization" => "Bearer #{TOKEN}" }).merge(headers))
req.body = JSON.generate(body) unless body.nil?
env = JSON.parse(Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }.body)
raise env["error"].to_s if env["error"]
env["data"]
end
<?php
const BASE = "https://api.skillsafe.ai/v1/app-api";
$TOKEN = "paste-your-token-here"; // from https://crew-chart.skillsafe.ai/tokens.html
function api(string $method, string $path, $body = null, array $headers = []) {
global $TOKEN;
$h = ["Content-Type: application/json"];
if ($TOKEN) $h[] = "Authorization: Bearer $TOKEN";
foreach ($headers as $k => $v) $h[] = "$k: $v";
$ch = curl_init(BASE . $path);
curl_setopt_array($ch, [CURLOPT_CUSTOMREQUEST => $method, CURLOPT_HTTPHEADER => $h, CURLOPT_RETURNTRANSFER => true]);
if ($body !== null) curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
$env = json_decode(curl_exec($ch), true);
if (!empty($env["error"])) throw new Exception(json_encode($env["error"]));
return $env["data"];
}
// Program.cs -- .NET 6+ console app
using System.Net.Http.Json; using System.Text.Json; using System.Text.Json.Nodes;
static class Api {
public const string Base = "https://api.skillsafe.ai/v1/app-api";
public static string Token = "paste-your-token-here"; // from https://crew-chart.skillsafe.ai/tokens.html
static readonly HttpClient C = new();
public static async Task<JsonNode?> Call(string method, string path, object? body = null, Dictionary<string, string>? headers = null) {
var req = new HttpRequestMessage(new HttpMethod(method), Base + path);
if (Token != "") req.Headers.Add("Authorization", "Bearer " + Token);
if (headers != null) foreach (var h in headers) req.Headers.Add(h.Key, h.Value);
if (body != null) req.Content = JsonContent.Create(body);
var env = JsonNode.Parse(await (await C.SendAsync(req)).Content.ReadAsStringAsync())!;
if (env["error"] is JsonNode e && e.GetValueKind() != JsonValueKind.Null) throw new Exception(e.ToJsonString());
return env["data"];
}
}
Step 2 — a token
A guest token is enough for /me and /estimate. A run is metered, so it needs a personal token: sign in on /tokens.html and copy it from there.
# a guest token (enough for /me and /estimate); a run needs your personal token from /tokens.html
TOKEN=$(curl -s -X POST "$BASE/guest" -H "Content-Type: application/json" -d '{"slug":"crew-chart"}' | jq -r '.data.token')
# a guest token (enough for /me and /estimate); a run needs your personal token from /tokens.html
TOKEN = ""
TOKEN = api("POST", "/guest", {"slug": "crew-chart"})["token"]
// a guest token (enough for /me and /estimate); a run needs your personal token from /tokens.html
TOKEN = "";
TOKEN = (await api("POST", "/guest", { slug: "crew-chart" })).token;
// inside main: a guest token (enough for /me and /estimate); a run needs your personal token
token = ""
g, err := api("POST", "/guest", map[string]string{"slug": "crew-chart"}, nil)
if err != nil { panic(err) }
token = g["token"].(string)
// inside main: a guest token (enough for /me and /estimate); a run needs your personal token
token = "";
token = api("POST", "/guest", Map.of("slug", "crew-chart"), null).get("token").asText();
# a guest token (enough for /me and /estimate); a run needs your personal token from /tokens.html
TOKEN.replace("")
TOKEN.replace(api("POST", "/guest", { slug: "crew-chart" })["token"])
// a guest token (enough for /me and /estimate); a run needs your personal token from /tokens.html
$TOKEN = "";
$TOKEN = api("POST", "/guest", ["slug" => "crew-chart"])["token"];
// a guest token (enough for /me and /estimate); a run needs your personal token from /tokens.html
Api.Token = "";
Api.Token = (await Api.Call("POST", "/guest", new { slug = "crew-chart" }))!["token"]!.GetValue<string>();
Step 3 — GET /me
Who the token is (subject_type: user or guest) and the credit balance.
api GET /me | jq '.data | {subject_type, credits}'
me = api("GET", "/me")
print(me["subject_type"], me.get("credits"))
const me = await api("GET", "/me");
console.log(me.subject_type, me.credits);
me, _ := api("GET", "/me", nil, nil)
fmt.Println(me["subject_type"], me["credits"])
JsonNode me = api("GET", "/me", null, null);
System.out.println(me.get("subject_type") + " " + me.get("credits"));
me = api("GET", "/me")
puts [me["subject_type"], me["credits"]].inspect
$me = api("GET", "/me");
echo $me["subject_type"], " ", $me["credits"] ?? "", "\n";
var me = await Api.Call("GET", "/me");
Console.WriteLine($"{me!["subject_type"]} {me["credits"]}");
Step 4 — POST /estimate
Free. The body is the run input itself, the bare object (never wrapped in {"input": ...}). hold_credits is a reservation, not the price; min_credits is what the balance must cover; model_alias reads gpt-terra.
# input.json: the run input object - {"task": "design", "team": "Event: ...; community-run; 4 editions so far ...", "roles": "Roles, in order ...", "facts": "Bus factor 1: ..."}
# - see the input table
api POST /estimate "$(cat input.json)" | jq '.data | {hold_credits, min_credits, model, model_alias, markup_bps, warnings}'
inp = json.load(open("input.json")) # {"task": "design", "team": "Event: ...; community-run; 4 editions so far ...", "roles": "Roles, in order ...", "facts": "Bus factor 1: ..."}
# - see the input table
est = api("POST", "/estimate", inp)
print(est["hold_credits"], est["min_credits"], est["model"], est.get("warnings"))
import { readFileSync } from "node:fs";
const input = JSON.parse(readFileSync("input.json", "utf8")); // {task: "design" or "cards", team, roles, facts} (see the input table)
const est = await api("POST", "/estimate", input);
console.log(est.hold_credits, est.min_credits, est.model, est.warnings);
raw, _ := os.ReadFile("input.json") // add "os" to the imports
var input map[string]any
json.Unmarshal(raw, &input)
est, _ := api("POST", "/estimate", input, nil)
fmt.Println(est["hold_credits"], est["min_credits"], est["model"])
Map<String, Object> input = M.readValue(new java.io.File("input.json"), Map.class);
JsonNode est = api("POST", "/estimate", input, null);
System.out.println(est.get("hold_credits") + " " + est.get("min_credits") + " " + est.get("model"));
input = JSON.parse(File.read("input.json"))
est = api("POST", "/estimate", input)
puts [est["hold_credits"], est["min_credits"], est["model"]].inspect
$input = json_decode(file_get_contents("input.json"), true);
$est = api("POST", "/estimate", $input);
echo $est["hold_credits"], " ", $est["min_credits"], " ", $est["model"], "\n";
var input = JsonNode.Parse(File.ReadAllText("input.json"))!;
var est = await Api.Call("POST", "/estimate", input);
Console.WriteLine($"{est!["hold_credits"]} {est["min_credits"]} {est["model"]}");
Step 5 — POST /run, then poll the job
Send an Idempotency-Key built from the lane and the input: a retry with the same key never bills twice. The page uses crew-chart:<task>:<inputHash>:a<attempt> - the lane is part of the key - and its one automatic reformat retry is a new attempt number, named in the page as one extra run. A design carries summary, diagnosis, busfactor, entity, shape, succession, burnout, coi, hire, checks, signals and caveats; a cards reply carries cards, coi_clause and note. The code below prints the headline parts of either (list items are plain strings).
KEY="crew-chart:design:$(shasum -a 256 input.json | cut -c1-16):a1"
JOB=$(curl -s -X POST "$BASE/run" -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" \
-H "Idempotency-Key: $KEY" --data @input.json | jq -r '.data.job_id')
while :; do
J=$(api GET "/jobs/$JOB"); S=$(echo "$J" | jq -r '.data.status')
case "$S" in succeeded|failed|cancelled) break;; esac; sleep 2
done
echo "$J" | jq -r '.data.output.output' > reply.json # one JSON object, the reply contract
echo "$J" | jq '.data | {charged_credits, truncated}'
jq -r '.summary // .format, .note // .diagnosis, ((.signals // .caveats)[] | "\(.signal // .caveat): \(.text // .text)")' reply.json
jq -r '.coi_clause // .note // empty' reply.json # the conflict-of-interest clause (cards)
import hashlib, time
key = "crew-chart:" + inp["task"] + ":" + hashlib.sha256(json.dumps(inp).encode()).hexdigest()[:16] + ":a1"
job = api("POST", "/run", inp, {"Idempotency-Key": key})
while True:
j = api("GET", "/jobs/" + job["job_id"])
if j["status"] in ("succeeded", "failed", "cancelled"):
break
time.sleep(2)
reply = json.loads(j["output"]["output"]) # the reply as one JSON object
print(j["charged_credits"], j.get("truncated"), reply.get("summary"))
print(reply.get("note") or reply.get("diagnosis", ""))
for s in reply.get("signals") or reply.get("caveats") or []:
print(s.get("signal", s.get("caveat")), "-", s.get("text", s.get("text")))
if reply.get("coi_clause"):
print("clause:", reply["coi_clause"])
import { createHash } from "node:crypto";
const key = `crew-chart:${input.task}:${createHash("sha256").update(JSON.stringify(input)).digest("hex").slice(0, 16)}:a1`;
const job = await api("POST", "/run", input, { "Idempotency-Key": key });
let j;
do { await new Promise((r) => setTimeout(r, 2000)); j = await api("GET", "/jobs/" + job.job_id); }
while (!["succeeded", "failed", "cancelled"].includes(j.status));
const reply = JSON.parse(j.output.output);
console.log(j.charged_credits, j.truncated, reply.summary);
console.log(reply.note ?? reply.diagnosis);
for (const s of reply.signals || reply.caveats || []) console.log(s.signal ?? s.caveat, "-", s.text ?? s.text);
if (reply.coi_clause) console.log("clause:", reply.coi_clause);
sum := sha256.Sum256(raw) // add "crypto/sha256", "encoding/hex", "time" to the imports
key := "crew-chart:" + input["task"].(string) + ":" + hex.EncodeToString(sum[:8]) + ":a1"
job, _ := api("POST", "/run", input, map[string]string{"Idempotency-Key": key})
var j map[string]any
for {
time.Sleep(2 * time.Second)
j, _ = api("GET", "/jobs/"+job["job_id"].(string), nil, nil)
if s := j["status"]; s == "succeeded" || s == "failed" || s == "cancelled" { break }
}
var reply map[string]any
json.Unmarshal([]byte(j["output"].(map[string]any)["output"].(string)), &reply)
items, _ := reply["signals"].([]any) // signals and caveats are plain strings
fmt.Println(j["charged_credits"], j["truncated"], reply["summary"])
fmt.Println(reply["note"], reply["diagnosis"])
for _, x := range items {
c := x.(map[string]any)
fmt.Println(c["signal"], "-", c["status"])
}
if fix, ok := reply["coi_clause"].(string); ok && fix != "" {
fmt.Println("clause:", fix)
}
var digest = java.security.MessageDigest.getInstance("SHA-256").digest(M.writeValueAsBytes(input));
String key = "crew-chart:" + input.get("task") + ":" + HexFormat.of().formatHex(digest, 0, 8) + ":a1";
JsonNode job = api("POST", "/run", input, Map.of("Idempotency-Key", key)), j;
do { Thread.sleep(2000); j = api("GET", "/jobs/" + job.get("job_id").asText(), null, null); }
while (!Set.of("succeeded", "failed", "cancelled").contains(j.get("status").asText()));
JsonNode reply = M.readTree(j.get("output").get("output").asText());
System.out.println(j.get("charged_credits") + " " + reply.get("summary"));
System.out.println(reply.path("note").asText(reply.path("diagnosis").asText()));
for (JsonNode s : reply.has("signals") ? reply.path("signals") : reply.path("caveats")) System.out.println(s.path("signal").asText(s.path("caveat").asText()) + " - " + s.path("status").asText(s.path("text").asText()));
if (!reply.path("coi_clause").asText().isEmpty()) System.out.println("clause: " + reply.get("coi_clause").asText());
require "digest"
key = "crew-chart:#{input["task"]}:#{Digest::SHA256.hexdigest(JSON.generate(input))[0, 16]}:a1"
job = api("POST", "/run", input, { "Idempotency-Key" => key })
j = nil
loop do
sleep 2
j = api("GET", "/jobs/#{job["job_id"]}")
break if %w[succeeded failed cancelled].include?(j["status"])
end
reply = JSON.parse(j["output"]["output"])
puts [j["charged_credits"], j["truncated"], reply["summary"]].inspect
puts reply["note"] || reply["diagnosis"]
(reply["signals"] || reply["caveats"] || []).each { |s| puts "#{s["signal"] || s["caveat"]} - #{s["text"] || s["text"]}" }
puts "clause: #{reply["coi_clause"]}" unless reply["coi_clause"].to_s.empty?
$key = "crew-chart:" . $input["task"] . ":" . substr(hash("sha256", json_encode($input)), 0, 16) . ":a1";
$job = api("POST", "/run", $input, ["Idempotency-Key" => $key]);
do { sleep(2); $j = api("GET", "/jobs/" . $job["job_id"]); }
while (!in_array($j["status"], ["succeeded", "failed", "cancelled"]));
$reply = json_decode($j["output"]["output"], true);
echo $j["charged_credits"], " ", ($reply["summary"] ?? ""), "\n";
echo $reply["note"] ?? $reply["diagnosis"] ?? "", "\n";
foreach ($reply["signals"] ?? $reply["caveats"] ?? [] as $s) echo $s["signal"] ?? $s["caveat"], " - ", $s["text"] ?? $s["text"], "\n";
if (!empty($reply["coi_clause"])) echo "clause: ", $reply["coi_clause"], "\n";
var digest = System.Security.Cryptography.SHA256.HashData(System.Text.Encoding.UTF8.GetBytes(input.ToJsonString()));
var key = $"crew-chart:{input["task"]}:{Convert.ToHexString(digest, 0, 8).ToLowerInvariant()}:a1";
var job = await Api.Call("POST", "/run", input, new() { ["Idempotency-Key"] = key });
JsonNode j;
do { await Task.Delay(2000); j = (await Api.Call("GET", "/jobs/" + job!["job_id"]))!; }
while (!new[] { "succeeded", "failed", "cancelled" }.Contains(j["status"]!.GetValue<string>()));
var reply = JsonNode.Parse(j["output"]!["output"]!.GetValue<string>());
Console.WriteLine($"{j["charged_credits"]} {reply!["summary"]}");
Console.WriteLine($"{reply["note"] ?? reply["diagnosis"]}");
foreach (var s in (reply["signals"] ?? reply["caveats"]) as JsonArray ?? new JsonArray()) Console.WriteLine($"{s!["signal"] ?? s["caveat"]} - {s["text"] ?? s["text"]}");
if (reply["coi_clause"] is JsonNode fix && fix.GetValue<string>() != "") Console.WriteLine("clause: " + fix);
Step 6 — POST /run-stream
The same run as Server-Sent Events, with the same input.json. Browsers receive progress ticks rather than text deltas; the final done event carries the reply.
# Server-Sent Events: tick/delta events while the job runs, then a final event with the job
curl -N -s -X POST "$BASE/run-stream" -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" \
-H "Idempotency-Key: crew-chart:design:$(shasum -a 256 input.json | cut -c1-16):a1" --data @input.json
rev = json.load(open("input.json")) # the same run input as input.json: {"task": "design", "team": "Event: ...; community-run; 4 editions so far ...", "roles": "Roles, in order ...", "facts": "Bus factor 1: ..."}
# - see the input table
req = urllib.request.Request(BASE + "/run-stream", method="POST", data=json.dumps(rev).encode(),
headers={"Content-Type": "application/json", "Authorization": "Bearer " + TOKEN,
"Idempotency-Key": "crew-chart:design:" + hashlib.sha256(json.dumps(rev).encode()).hexdigest()[:16] + ":a1"})
with urllib.request.urlopen(req) as r:
for line in r:
line = line.decode().strip()
if line.startswith("data:"):
print(line[5:].strip()[:200])
const rev = JSON.parse(readFileSync("input.json", "utf8"));
const r = await fetch(BASE + "/run-stream", { method: "POST", body: JSON.stringify(rev), headers: {
"Content-Type": "application/json", Authorization: "Bearer " + TOKEN,
"Idempotency-Key": `crew-chart:design:${createHash("sha256").update(JSON.stringify(rev)).digest("hex").slice(0, 16)}:a1` } });
const dec = new TextDecoder();
for await (const chunk of r.body) for (const line of dec.decode(chunk).split("\n"))
if (line.startsWith("data:")) console.log(line.slice(5).trim().slice(0, 200));
rraw, _ := os.ReadFile("input.json") // add "bufio", "strings" to the imports
rs := sha256.Sum256(rraw)
req, _ := http.NewRequest("POST", base+"/run-stream", bytes.NewReader(rraw))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Idempotency-Key", "crew-chart:design:"+hex.EncodeToString(rs[:8])+":a1")
res, _ := http.DefaultClient.Do(req)
sc := bufio.NewScanner(res.Body)
for sc.Scan() { if l := sc.Text(); strings.HasPrefix(l, "data:") { fmt.Println(l[5:]) } }
String rev = java.nio.file.Files.readString(java.nio.file.Path.of("input.json"));
var rd = java.security.MessageDigest.getInstance("SHA-256").digest(rev.getBytes());
var sreq = HttpRequest.newBuilder(URI.create(BASE + "/run-stream")).header("Content-Type", "application/json")
.header("Authorization", "Bearer " + token).header("Idempotency-Key", "crew-chart:design:" + HexFormat.of().formatHex(rd, 0, 8) + ":a1")
.POST(HttpRequest.BodyPublishers.ofString(rev)).build();
C.send(sreq, HttpResponse.BodyHandlers.ofLines()).body().filter(l -> l.startsWith("data:")).forEach(System.out::println);
rev = File.read("input.json")
uri = URI(BASE + "/run-stream")
req = Net::HTTP::Post.new(uri, "Content-Type" => "application/json", "Authorization" => "Bearer #{TOKEN}",
"Idempotency-Key" => "crew-chart:design:#{Digest::SHA256.hexdigest(rev)[0, 16]}:a1")
req.body = rev
Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |h|
h.request(req) { |res| res.read_body { |chunk| chunk.each_line { |l| puts l if l.start_with?("data:") } } }
end
$rev = file_get_contents("input.json");
$ch = curl_init(BASE . "/run-stream");
curl_setopt_array($ch, [CURLOPT_POST => true, CURLOPT_POSTFIELDS => $rev, CURLOPT_HTTPHEADER => [
"Content-Type: application/json", "Authorization: Bearer $TOKEN",
"Idempotency-Key: crew-chart:design:" . substr(hash("sha256", $rev), 0, 16) . ":a1"],
CURLOPT_WRITEFUNCTION => function ($ch, $chunk) { foreach (explode("\n", $chunk) as $l) if (str_starts_with($l, "data:")) echo $l, "\n"; return strlen($chunk); }]);
curl_exec($ch);
var rev = File.ReadAllText("input.json");
var rd = System.Security.Cryptography.SHA256.HashData(System.Text.Encoding.UTF8.GetBytes(rev));
var sreq = new HttpRequestMessage(HttpMethod.Post, Api.Base + "/run-stream") { Content = new StringContent(rev, System.Text.Encoding.UTF8, "application/json") };
sreq.Headers.Add("Authorization", "Bearer " + Api.Token);
sreq.Headers.Add("Idempotency-Key", $"crew-chart:design:{Convert.ToHexString(rd, 0, 8).ToLowerInvariant()}:a1");
using var http = new HttpClient();
using var res = await http.SendAsync(sreq, HttpCompletionOption.ResponseHeadersRead);
using var rdr = new StreamReader(await res.Content.ReadAsStreamAsync());
for (string? l; (l = await rdr.ReadLineAsync()) != null;) if (l.StartsWith("data:")) Console.WriteLine(l);