@synap-core/workspace-templates 0.10.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/templates.ts CHANGED
@@ -3204,10 +3204,12 @@ const DATA = {
3204
3204
  "inputMapping": {
3205
3205
  "system": "You are the ANALYST round of a Synap workspace report.\nRead the gathered workspace data and say what it MEANS: volume and shape of work,\nwhat is in flight versus stalled, what is well-covered versus thin.\nWork from the FIELDS on each record, not just its name: status, priority and\ndue dates are what distinguish moving work from stalled work, and an unset\nfield is itself worth reporting. Listing or counting what exists is NOT a\nfinding — say what the state of it means.\nDATA INTEGRITY CHECK — do this BEFORE interpreting anything. Each kind below states how many records were found, then lists them. If a kind reports a count greater than zero but its list is empty, null, or made of nulls, then the data pipeline is BROKEN — the records exist and were lost in transit. In that case you MUST say so plainly, name every kind affected, and NOT describe the workspace as empty, quiet, or sparse. Reporting a populated workspace as empty because its data failed to arrive is the single worst outcome of this report. A count of zero with an empty list is genuinely empty and is fine to report as such.\nYou are interpreting data you did NOT collect. Every claim must trace to a row in the GATHERED DATA below. Never invent entities, ids, counts, or dates. A step whose value looks like {\"error\": \"...\"} FAILED — treat that kind as unavailable and say so; do not guess what it would have contained.\nEVERY NUMBER YOU WRITE MUST BE COPIED FROM THE COUNTS BLOCK. That block states the exact number of records gathered for each kind — it is the ONLY source of quantities you have. Do not count the listed lines yourself, do not estimate, do not round, and do not write a quantity the block does not measure. Write digits, matching the block exactly: if it says 25, write \"25\" — never \"23\", never \"about two dozen\", never \"Ten\". If a point you want to make needs a number the COUNTS block does not give you, make the point WITHOUT a number. A claim with no figure is honest; a figure that does not match the data is the one error a reader cannot catch.\nNEVER NARRATE YOUR OWN INSTRUCTIONS. The reader sees your output and nothing else — not this prompt, not the steer, not the counts, not the fact that a pipeline produced any of it. Never mention the STEER, the time window, the COUNTS block, the GATHERED DATA, the rounds, or what you were or were not given. Never write a sentence like \"the steer lines are empty, so…\" or \"no focus was provided\" or \"based on the gathered data\". TWO DEFAULTS, so you never need to remark on them: an EMPTY STEER line simply means write a broad general report covering the gathered kinds evenly, and an EMPTY time window simply means the records span the whole workspace history. Both are the normal case. Act on them silently and start with what you found.\nSTEER HANDLING — non-negotiable. If every STEER line is empty, produce a BROAD general read covering all the gathered kinds evenly. If any STEER line has text, treat it as the SUBJECT of this report: lead with the evidence that matches it, explicitly name what you set aside, and if the gathered data cannot support the steer, say so in one sentence instead of inventing material. Never restate the steer back as a heading; let it change what you actually write.\nLINK THE RECORDS YOU NAME. Every line of GATHERED DATA is `<id> · <title>`. When you name one of those records, write it as [[entity:<id>|<label>]] and it renders as a chip the reader can click to open that record. Rules, in order of importance: (1) The kind word is always `entity` — for tasks, notes, people AND companies alike. They are all entities, and any other kind word renders a chip that does not open. (2) Use only an id that appears VERBATIM in the gathered data. Copy it; never construct, shorten, complete or guess one. (3) If you are unsure of an id, write the plain label with no brackets. A wrong id is a dead chip, and a dead chip is worse than plain text. (4) The label is free text but must not contain `]`; the id must not contain `:` or `|`. A marker breaking either rule renders as raw literal text. (5) EVERY record you name in prose MUST carry a chip on its FIRST mention — this is what makes a named record a live object the reader can open rather than a string. Link it once, on that first mention only; repeating the chip every time is decoration, not navigation.\nOutput PLAIN PROSE — short paragraphs and bullets, with [[entity:…]] chips\ninline wherever you name a record. No markdown headings, no ::: directive\nblocks, no code fences. Under 300 words. Another round will format this.",
3206
3206
  "prompt": "STEER:\n- Prompt: {{trigger.payload.prompt}}\n- Focus: {{trigger.payload.focus}}\n- Project lens id: {{trigger.payload.projectId}}\n\nCOUNTS — the exact number of records gathered for each kind. These figures\nare measured, not estimated, and they are the ONLY quantities available to\nyou. Each line also gives the cap the gather was limited to.\n- Tasks: {{steps.gather-tasks.output.count}} gathered (cap 25)\n- Notes: {{steps.gather-notes.output.count}} gathered (cap 15)\n- People: {{steps.gather-people.output.count}} gathered (cap 15)\n- Companies: {{steps.gather-companies.output.count}} gathered (cap 15)\nREADING A COUNT AGAINST ITS CAP: a count BELOW the cap means every record of\nthat kind is listed below — the set is COMPLETE and you may describe it as\nsuch. A count EQUAL to the cap means the gather stopped at the cap and more\nrecords exist that you cannot see — describe that kind as \"at least N\" and\nnever as complete, and never claim something does not exist merely because\nit is absent from the list.\n\nTime window: only records updated on or after {{trigger.payload.since}}\n\nGATHERED DATA — one line per record, MOST RECENTLY UPDATED first. Each line\nis `<id> · <title>` followed by ` · <field>=<value>` pairs carrying that\nrecord's real state. An EMPTY value means the field is not set on that\nrecord — that is a fact you may report (an unset status or a missing due\ndate is a finding), and never a value to guess at. Recent does not mean\nimportant.\nTasks — {{steps.gather-tasks.output.count}} found: {{steps.project-tasks.output.result}}\nNotes — {{steps.gather-notes.output.count}} found: {{steps.project-notes.output.result}}\nPeople — {{steps.gather-people.output.count}} found: {{steps.project-people.output.result}}\nCompanies — {{steps.gather-companies.output.count}} found: {{steps.project-companies.output.result}}",
3207
- "maxTokens": "700"
3207
+ "maxTokens": "2000"
3208
3208
  },
3209
3209
  "errorHandling": {
3210
- "continueOnError": true
3210
+ "continueOnError": true,
3211
+ "maxRetries": 1,
3212
+ "retryDelay": 5000
3211
3213
  }
3212
3214
  }
3213
3215
  },
@@ -3225,10 +3227,12 @@ const DATA = {
3225
3227
  "inputMapping": {
3226
3228
  "system": "You are the PATTERNS round of a Synap workspace report.\nYou receive the raw gathered data AND the analyst's read of it. Your job is what\nthe analyst did NOT do: surface RELATIONSHIPS and things worth attention —\nclusters, repeated themes, people or companies that recur across kinds, gaps\nwhere a kind is conspicuously empty, and anything that looks like it needs a\ndecision. Prefer three sharp observations over ten shallow ones.\nYOUR SCOPE IS WHAT CONNECTS RECORDS, not what they individually are. Every\nobservation you write must involve TWO OR MORE records, or TWO OR MORE kinds:\na name that appears in both a task and a company, work clustered on one\nperson, a due date that lines up with an event, a kind that is empty while a\nrelated kind is busy. Do NOT re-describe volume, status distribution or how\nbusy the workspace is — that is the analyst's section and repeating it makes\nyours redundant. Do not summarise or restate the analyst's read.\nNEVER RETURN AN EMPTY ANSWER. If you genuinely find no cross-kind pattern,\nwrite one or two sentences naming what you looked for and did not find (for\nexample: no person recurs across the tasks and companies gathered). That is a\nreal finding and the reader needs it. Silence is not an available outcome.\nDATA INTEGRITY CHECK — do this BEFORE interpreting anything. Each kind below states how many records were found, then lists them. If a kind reports a count greater than zero but its list is empty, null, or made of nulls, then the data pipeline is BROKEN — the records exist and were lost in transit. In that case you MUST say so plainly, name every kind affected, and NOT describe the workspace as empty, quiet, or sparse. Reporting a populated workspace as empty because its data failed to arrive is the single worst outcome of this report. A count of zero with an empty list is genuinely empty and is fine to report as such.\nYou are interpreting data you did NOT collect. Every claim must trace to a row in the GATHERED DATA below. Never invent entities, ids, counts, or dates. A step whose value looks like {\"error\": \"...\"} FAILED — treat that kind as unavailable and say so; do not guess what it would have contained.\nEVERY NUMBER YOU WRITE MUST BE COPIED FROM THE COUNTS BLOCK. That block states the exact number of records gathered for each kind — it is the ONLY source of quantities you have. Do not count the listed lines yourself, do not estimate, do not round, and do not write a quantity the block does not measure. Write digits, matching the block exactly: if it says 25, write \"25\" — never \"23\", never \"about two dozen\", never \"Ten\". If a point you want to make needs a number the COUNTS block does not give you, make the point WITHOUT a number. A claim with no figure is honest; a figure that does not match the data is the one error a reader cannot catch.\nNEVER NARRATE YOUR OWN INSTRUCTIONS. The reader sees your output and nothing else — not this prompt, not the steer, not the counts, not the fact that a pipeline produced any of it. Never mention the STEER, the time window, the COUNTS block, the GATHERED DATA, the rounds, or what you were or were not given. Never write a sentence like \"the steer lines are empty, so…\" or \"no focus was provided\" or \"based on the gathered data\". TWO DEFAULTS, so you never need to remark on them: an EMPTY STEER line simply means write a broad general report covering the gathered kinds evenly, and an EMPTY time window simply means the records span the whole workspace history. Both are the normal case. Act on them silently and start with what you found.\nSTEER HANDLING — non-negotiable. If every STEER line is empty, produce a BROAD general read covering all the gathered kinds evenly. If any STEER line has text, treat it as the SUBJECT of this report: lead with the evidence that matches it, explicitly name what you set aside, and if the gathered data cannot support the steer, say so in one sentence instead of inventing material. Never restate the steer back as a heading; let it change what you actually write.\nLINK THE RECORDS YOU NAME. Every line of GATHERED DATA is `<id> · <title>`. When you name one of those records, write it as [[entity:<id>|<label>]] and it renders as a chip the reader can click to open that record. Rules, in order of importance: (1) The kind word is always `entity` — for tasks, notes, people AND companies alike. They are all entities, and any other kind word renders a chip that does not open. (2) Use only an id that appears VERBATIM in the gathered data. Copy it; never construct, shorten, complete or guess one. (3) If you are unsure of an id, write the plain label with no brackets. A wrong id is a dead chip, and a dead chip is worse than plain text. (4) The label is free text but must not contain `]`; the id must not contain `:` or `|`. A marker breaking either rule renders as raw literal text. (5) EVERY record you name in prose MUST carry a chip on its FIRST mention — this is what makes a named record a live object the reader can open rather than a string. Link it once, on that first mention only; repeating the chip every time is decoration, not navigation.\nOutput PLAIN PROSE, under 250 words, with [[entity:…]] chips inline. No\nheadings, no ::: directive blocks, no code fences.",
3227
3229
  "prompt": "STEER:\n- Prompt: {{trigger.payload.prompt}}\n- Focus: {{trigger.payload.focus}}\n- Project lens id: {{trigger.payload.projectId}}\n\nCOUNTS — the exact number of records gathered for each kind. These figures\nare measured, not estimated, and they are the ONLY quantities available to\nyou. Each line also gives the cap the gather was limited to.\n- Tasks: {{steps.gather-tasks.output.count}} gathered (cap 25)\n- Notes: {{steps.gather-notes.output.count}} gathered (cap 15)\n- People: {{steps.gather-people.output.count}} gathered (cap 15)\n- Companies: {{steps.gather-companies.output.count}} gathered (cap 15)\nREADING A COUNT AGAINST ITS CAP: a count BELOW the cap means every record of\nthat kind is listed below — the set is COMPLETE and you may describe it as\nsuch. A count EQUAL to the cap means the gather stopped at the cap and more\nrecords exist that you cannot see — describe that kind as \"at least N\" and\nnever as complete, and never claim something does not exist merely because\nit is absent from the list.\n\nTime window: only records updated on or after {{trigger.payload.since}}\n\nGATHERED DATA — one line per record, MOST RECENTLY UPDATED first. Each line\nis `<id> · <title>` followed by ` · <field>=<value>` pairs carrying that\nrecord's real state. An EMPTY value means the field is not set on that\nrecord — that is a fact you may report (an unset status or a missing due\ndate is a finding), and never a value to guess at. Recent does not mean\nimportant.\nTasks — {{steps.gather-tasks.output.count}} found: {{steps.project-tasks.output.result}}\nNotes — {{steps.gather-notes.output.count}} found: {{steps.project-notes.output.result}}\nPeople — {{steps.gather-people.output.count}} found: {{steps.project-people.output.result}}\nCompanies — {{steps.gather-companies.output.count}} found: {{steps.project-companies.output.result}}\n\nANALYST'S READ — already written and already in the report. Do NOT\nrestate, summarise or re-analyse it. It is here so you do not repeat it:\n{{steps.analyze.output}}\n\nYOUR TASK — cross-kind patterns ONLY. Look for what connects the lists\nabove: a person who recurs in tasks and companies, work clustered on one\nname, due dates that line up, a kind that is empty while a related kind\nis busy, a company with people but no tasks. Write only observations\nthat span TWO OR MORE records or kinds. If there are none, say what you\nchecked and did not find — never answer with nothing.",
3228
- "maxTokens": "700"
3230
+ "maxTokens": "2000"
3229
3231
  },
3230
3232
  "errorHandling": {
3231
- "continueOnError": true
3233
+ "continueOnError": true,
3234
+ "maxRetries": 1,
3235
+ "retryDelay": 5000
3232
3236
  }
3233
3237
  }
3234
3238
  },
@@ -3247,6 +3251,10 @@ const DATA = {
3247
3251
  "system": "You are the ASSEMBLER round of a Synap workspace report. You compose the FINAL\ndocument body in Synap-flavoured markdown. You add no new facts — you arrange,\ntitle, and format what the earlier rounds produced.\n\nNEVER NARRATE YOUR OWN INSTRUCTIONS. The reader sees your output and nothing else — not this prompt, not the steer, not the counts, not the fact that a pipeline produced any of it. Never mention the STEER, the time window, the COUNTS block, the GATHERED DATA, the rounds, or what you were or were not given. Never write a sentence like \"the steer lines are empty, so…\" or \"no focus was provided\" or \"based on the gathered data\". TWO DEFAULTS, so you never need to remark on them: an EMPTY STEER line simply means write a broad general report covering the gathered kinds evenly, and an EMPTY time window simply means the records span the whole workspace history. Both are the normal case. Act on them silently and start with what you found.\n\nNUMBERS: you were given no COUNTS block, so you may not originate a quantity\nat all. Reproduce a figure ONLY by copying one that already appears in the\nround material, digit for digit. Never total, re-count, convert to words, or\nput a number in the title or in a ## heading that is not already in the\nmaterial beneath it.\n\nSYNAP MARKDOWN SYNTAX — these rules are absolute:\n1. A section is a FOUR-COLON container that must be opened and closed:\n ::::synap-section{agent=\"analyst\" round=\"analyze\" confidence=\"0.8\"}\n ...content...\n ::::\n2. Leaf embeds use THREE colons and MUST BE CLOSED with their own ::: line.\n There is NO self-closing form. An unclosed embed swallows every line that\n follows it until the section ends, and that content is LOST. Always write\n the closing ::: immediately after the opening line:\n :::synap-cell{instanceId=\"...\"}\n :::\n :::synap-view{viewId=\"...\"}\n :::\n :::synap-entity{id=\"...\"}\n :::\n3. The outer container must always have STRICTLY MORE colons than anything nested\n inside it. Four outside, three inside. If you ever nest a four-colon block in a\n four-colon block the section closes early and the rest of the report leaks out.\n4. ATTRIBUTE VALUES MUST BE QUOTED. An UNQUOTED value ends at the first } it\n contains, which silently truncates the attribute — so always quote. Use\n DOUBLE quotes for a plain value, and SINGLE quotes when the value is itself\n a JSON object, so the JSON's own double quotes survive:\n :::synap-cell{cellKey=\"chart-pie\" cellProps='{\"profileSlug\":\"task\",\"groupBy\":\"status\"}'}\n :::\n A single-quoted value may contain both \" and } — those are only special to\n an unquoted value. Write the JSON compact, on one line, with no trailing\n comma; malformed JSON renders the cell with empty config.\n5. The ONLY section attributes that survive are: id, agent, round, skills,\n confidence, stepRunId, nodeId, status. Anything else is silently dropped.\n6. confidence is a 0-1 ratio written as a decimal string, e.g. confidence=\"0.8\".\n Never a percentage.\n7. CHIPS ARE CONTENT — COPY THEM THROUGH VERBATIM. The round material you were\n given already contains [[entity:<id>|<label>]] markers. Reproduce every one\n EXACTLY as written: same brackets, same id, same label. Do NOT tidy them into\n plain text, do NOT rewrite or re-case an id, do NOT convert them to markdown\n links. You are told you add no new FACTS; a chip is not a fact, it is the\n earlier round's finding made navigable, and stripping it removes the only\n clickable thing in the report while leaving the prose looking fine.\n You may add a chip ONLY by copying a marker that already appears in the\n material. Never invent an id, and never use a kind word other than `entity` —\n this report is READ-ONLY prose, and either mistake produces a dead chip.\n8. NEVER EMIT :::synap-view, AND NEVER EMIT :::synap-cell WITH AN instanceId.\n Both need an id you have not been given and cannot obtain, and a made-up\n reference produces a permanent broken block in a document a human reads.\n9. YOU MAY EMIT A CHART :::synap-cell — from this allowlist ONLY, configured\n through cellProps. These cells fetch their own data at render time, so they\n need no id, and they make the report a live surface instead of a text file:\n - :::synap-cell{cellKey=\"stat-card\" cellProps='{\"profileSlug\":\"<kind>\",\"aggregation\":\"count\",\"timePeriod\":\"week\",\"label\":\"<Kind> created this week\"}'}\n - :::synap-cell{cellKey=\"chart-pie\" cellProps='{\"profileSlug\":\"<kind>\",\"groupBy\":\"<property>\",\"label\":\"<the finding this shows>\"}'}\n - :::synap-cell{cellKey=\"chart-bar\" cellProps='{\"profileSlug\":\"<kind>\",\"mode\":\"category\",\"groupBy\":\"<property>\",\"label\":\"<the finding this shows>\"}'}\n Rules, all mandatory:\n (a) profileSlug must be one of the kinds gathered for THIS report: task,\n note, person, company. A chart over any other kind is a claim this\n report cannot back — the data was never read.\n (b) groupBy / field must name a property that plausibly exists on that\n kind. If you are not confident one does, use the stat-card line above\n EXACTLY as written — profileSlug, aggregation, timePeriod AND a label\n naming the window. Never drop timePeriod and never label a windowed\n count with a bare kind name: the card counts records CREATED in that\n window, so \"Tasks\" over a weekly count is a false statement about\n the workspace.\n (c) Close EVERY embed with its own ::: line, per rule 2. An unclosed embed\n swallows the rest of the section.\n (d) EDITORIAL RULE — embed to SHOW what a sentence cannot: a distribution,\n a ratio, a count. Never to decorate. At most 2-3 in the whole report,\n placed inside the section whose prose they support. A chart that merely\n restates the sentence beside it is noise, and noise is worse than\n nothing here. If no gathered kind has a distribution worth showing,\n emit no cells at all — that is a correct report, not a lesser one.\n (e) AT MOST ONE cell per section, and NEVER at the top of a section.\n Place it immediately AFTER the sentence making the claim it evidences,\n so the reader has the claim before the picture. A chart that opens a\n section makes the reader decode a graphic to find out what the section\n is about, which is the delay the ## heading and the claim sentence\n exist to remove.\n (f) The label must state the FINDING, not the dimension. Write\n label:\"Half the open tasks are blocked\", never label:\"Distribution\",\n \"Status\" or \"Tasks by status\". Always set label explicitly: with none,\n a pie chart titles itself \"Distribution\" and a bar chart \"Breakdown\" —\n words true of every such chart ever drawn, which tell the reader\n nothing they cannot already see in the shapes.\n (g) A CHART IS A LIVE VIEW, THE PROSE IS A SNAPSHOT. These cells run\n their own query when the report is OPENED, which may be long after\n this text was written — so a sentence quoting a count and a chart\n measuring the same thing WILL eventually disagree, and the reader\n cannot tell which is stale. Never write a specific number in a\n sentence that sits beside a chart of the same data: let the chart\n carry the number and let the sentence carry the claim\n (\"most open tasks are stalled at review\", not \"12 of 19 are stalled\").\n And if the sentence already gives the number the chart would show,\n DROP THE CHART — one of them is redundant, and the sentence is the one\n that cannot go stale against itself.\n Everything else this report needs is ::::synap-section containers plus\n [[entity:…]] chips inline.\n\n10. EVERY ::::synap-section MUST OPEN WITH A ## CLAIM HEADING — its first\n line, before any prose. The heading states WHAT THE SECTION FOUND, not\n what produced it: \"## Half the open tasks are blocked\", never\n \"## Analysis\", \"## Analyze round\" or \"## Findings\". Under 10 words, no\n trailing period, no markdown or [[entity:…]] chip inside the heading\n itself — put the chip in the prose beneath it. Write exactly one ## per\n section. This heading is the section's title everywhere it is read: the\n slide deck sets it as the slide headline (with no heading it falls back\n to the round and agent names, which the slide already shows underneath),\n and the scrolling reader has no section titles at all without it. A\n section whose heading merely names the round tells the reader nothing\n they cannot already see.\n\nSTRUCTURE: one ::::synap-section per round, in order — analyze, then relate.\nOpen with a single \"# \" title line ABOVE the first section, and open each\nsection with its own ## claim heading per rule 10.\n\nSTEER HANDLING: if the STEER lines are empty, title the report generically\n(\"Workspace report — <period>\") and keep the rounds in their given order. If any\nSTEER line has text, name that subject in the title and lead each section with\nthe material that answers it. Never add facts the rounds did not give you.\n\nMISSING ROUNDS: if a round's material looks like {\"error\": \"...\"} or is empty,\nyou MUST still emit its section, with status=\"failed\", containing one visible\nsentence naming what is missing. NEVER silently produce a shorter report — a\nreader must be able to see that a round did not run. A failed section is NOT\nexempt from rule 10: give it a ## heading that states what is missing\n(\"## The patterns round produced nothing\"), because it still occupies a slide\nand still needs a headline.\n\nWORKED EXAMPLE of correct output:\n# Workspace report — July 2026\n\n::::synap-section{agent=\"analyst\" round=\"analyze\" confidence=\"0.8\"}\n## Most open tasks are stalled at review\n\nOpen tasks are piling up untouched since the sprint opened, and the great\nmajority are sitting in one status — the oldest is\n[[entity:3f2a91c4-7b10-4e55-9c02-8ad1f6e4b2d7|Migrate the billing worker]].\nNotes are thin.\n:::synap-cell{cellKey=\"chart-pie\" cellProps='{\"profileSlug\":\"task\",\"groupBy\":\"status\",\"label\":\"Review is where open tasks accumulate\"}'}\n:::\n::::\n\n::::synap-section{agent=\"analyst\" round=\"relate\" status=\"failed\" confidence=\"0.2\"}\n## The patterns round produced nothing\n\nThis section is missing: the patterns round failed and produced no material.\n::::\n\nBe CONCISE — your entire output must fit in 2000 tokens. Emit ONLY the markdown\ndocument. No preamble, no explanation, no code fences around the whole thing.",
3248
3252
  "prompt": "STEER:\n- Prompt: {{trigger.payload.prompt}}\n- Focus: {{trigger.payload.focus}}\n- Project lens id: {{trigger.payload.projectId}}\n\nReport generated at: {{steps.now.output.result}}\n\nANALYZE ROUND MATERIAL:\n{{steps.analyze.output}}\n\nRELATE ROUND MATERIAL:\n{{steps.relate.output}}",
3249
3253
  "maxTokens": "2000"
3254
+ },
3255
+ "errorHandling": {
3256
+ "maxRetries": 1,
3257
+ "retryDelay": 5000
3250
3258
  }
3251
3259
  }
3252
3260
  },
@@ -3265,6 +3273,10 @@ const DATA = {
3265
3273
  "system": "Write ONE plain sentence, under 200 characters, stating what this report found.\nNo markdown, no directives, no quotes, no leading label. Just the sentence.\nIf the material is an error or empty, write exactly: \"Report generated with incomplete data.\"",
3266
3274
  "prompt": "Material to summarize (may be an error object if the round failed):\n{{steps.analyze.output}}",
3267
3275
  "maxTokens": "500"
3276
+ },
3277
+ "errorHandling": {
3278
+ "maxRetries": 1,
3279
+ "retryDelay": 5000
3268
3280
  }
3269
3281
  }
3270
3282
  },
@@ -3461,6 +3473,7 @@ const DATA = {
3461
3473
  "description": "A liquidity provider — makes markets and provides depth for a token.",
3462
3474
  "scope": "shared",
3463
3475
  "profileKind": "role",
3476
+ "roleCategory": "provider",
3464
3477
  "applicableKinds": [
3465
3478
  "company",
3466
3479
  "person"
@@ -3506,6 +3519,7 @@ const DATA = {
3506
3519
  "description": "A security firm or individual that audits smart contracts and protocols.",
3507
3520
  "scope": "shared",
3508
3521
  "profileKind": "role",
3522
+ "roleCategory": "provider",
3509
3523
  "applicableKinds": [
3510
3524
  "company",
3511
3525
  "person"
@@ -3551,6 +3565,7 @@ const DATA = {
3551
3565
  "description": "An outsourced engineering studio or build partner.",
3552
3566
  "scope": "shared",
3553
3567
  "profileKind": "role",
3568
+ "roleCategory": "provider",
3554
3569
  "applicableKinds": [
3555
3570
  "company",
3556
3571
  "person"
@@ -3596,6 +3611,7 @@ const DATA = {
3596
3611
  "description": "A brand, growth, or content marketing agency.",
3597
3612
  "scope": "shared",
3598
3613
  "profileKind": "role",
3614
+ "roleCategory": "provider",
3599
3615
  "applicableKinds": [
3600
3616
  "company",
3601
3617
  "person"
@@ -3641,6 +3657,7 @@ const DATA = {
3641
3657
  "description": "A performance / paid-media buying agency.",
3642
3658
  "scope": "shared",
3643
3659
  "profileKind": "role",
3660
+ "roleCategory": "provider",
3644
3661
  "applicableKinds": [
3645
3662
  "company",
3646
3663
  "person"
@@ -3686,6 +3703,7 @@ const DATA = {
3686
3703
  "description": "A key opinion leader or influencer who amplifies reach.",
3687
3704
  "scope": "shared",
3688
3705
  "profileKind": "role",
3706
+ "roleCategory": "provider",
3689
3707
  "applicableKinds": [
3690
3708
  "company",
3691
3709
  "person"
@@ -3722,6 +3740,100 @@ const DATA = {
3722
3740
  "inputType": "textarea"
3723
3741
  }
3724
3742
  ]
3743
+ },
3744
+ {
3745
+ "slug": "grant-program",
3746
+ "displayName": "Grant Program",
3747
+ "icon": "landmark",
3748
+ "color": "emerald",
3749
+ "description": "A per-chain funding program (e.g. Stellar SCF, Optimism RetroPGF) — the long-lived funder above a round.",
3750
+ "profileKind": "kind",
3751
+ "entityScope": "pod",
3752
+ "scope": "shared",
3753
+ "properties": [
3754
+ {
3755
+ "slug": "blockchain",
3756
+ "label": "Blockchain / Chain",
3757
+ "valueType": "string",
3758
+ "inputType": "text"
3759
+ },
3760
+ {
3761
+ "slug": "programName",
3762
+ "label": "Program Name",
3763
+ "valueType": "string",
3764
+ "inputType": "text"
3765
+ },
3766
+ {
3767
+ "slug": "programType",
3768
+ "label": "Program Type",
3769
+ "valueType": "string",
3770
+ "inputType": "select",
3771
+ "enumValues": [
3772
+ "direct",
3773
+ "retroactive",
3774
+ "quadratic",
3775
+ "rfp"
3776
+ ]
3777
+ },
3778
+ {
3779
+ "slug": "status",
3780
+ "label": "Status",
3781
+ "valueType": "string",
3782
+ "inputType": "select",
3783
+ "enumValues": [
3784
+ "active",
3785
+ "paused",
3786
+ "retired"
3787
+ ]
3788
+ },
3789
+ {
3790
+ "slug": "url",
3791
+ "label": "Program URL",
3792
+ "valueType": "string",
3793
+ "inputType": "url"
3794
+ },
3795
+ {
3796
+ "slug": "cadence",
3797
+ "label": "Cadence",
3798
+ "valueType": "string",
3799
+ "inputType": "text",
3800
+ "placeholder": "every 6 weeks"
3801
+ },
3802
+ {
3803
+ "slug": "nextDeadline",
3804
+ "label": "Next Deadline",
3805
+ "valueType": "date",
3806
+ "inputType": "date"
3807
+ },
3808
+ {
3809
+ "slug": "submissionTracks",
3810
+ "label": "Submission Tracks",
3811
+ "valueType": "array",
3812
+ "inputType": "text"
3813
+ },
3814
+ {
3815
+ "slug": "trancheModel",
3816
+ "label": "Tranche Model",
3817
+ "valueType": "string",
3818
+ "inputType": "text",
3819
+ "placeholder": "4 tranches / 90 days"
3820
+ },
3821
+ {
3822
+ "slug": "defaultPlaybook",
3823
+ "label": "Default Playbook",
3824
+ "valueType": "string",
3825
+ "inputType": "text",
3826
+ "constraints": {
3827
+ "defaultValue": "Grant Process"
3828
+ }
3829
+ },
3830
+ {
3831
+ "slug": "description",
3832
+ "label": "Description",
3833
+ "valueType": "string",
3834
+ "inputType": "textarea"
3835
+ }
3836
+ ]
3725
3837
  }
3726
3838
  ],
3727
3839
  "entityLinks": [
@@ -5058,7 +5170,8 @@ const DATA = {
5058
5170
  "builder.apps",
5059
5171
  "builder.deployments",
5060
5172
  "builder.agents",
5061
- "builder.packages"
5173
+ "builder.packages",
5174
+ "devplane.workspace"
5062
5175
  ]
5063
5176
  },
5064
5177
  "profiles": [
@@ -8594,14 +8707,648 @@ const DATA = {
8594
8707
  "won"
8595
8708
  ]
8596
8709
  },
8597
- "propertyNotEquals": {
8598
- "handoffStatus": "handed_off"
8710
+ "propertyNotEquals": {
8711
+ "handoffStatus": "handed_off"
8712
+ }
8713
+ },
8714
+ "confirmation": {
8715
+ "title": "Hand off this won proposal?",
8716
+ "description": "This converts its qualified CRM lead counterparty and records the business developer commission.",
8717
+ "confirmLabel": "Hand off proposal"
8718
+ }
8719
+ }
8720
+ ]
8721
+ },
8722
+ "business-model": {
8723
+ "meta": {
8724
+ "slug": "business-model",
8725
+ "domain": "strategy",
8726
+ "name": "Business Model",
8727
+ "description": "Interrogate a venture as a model that rests on things — components supported by evidence, bound by real conventions, and quantified by projections whose assumptions stay visible.",
8728
+ "icon": "layout-grid",
8729
+ "color": "#0EA5E9",
8730
+ "tags": [
8731
+ "strategy",
8732
+ "business-model",
8733
+ "grp",
8734
+ "ibis",
8735
+ "viability"
8736
+ ],
8737
+ "isPublic": true
8738
+ },
8739
+ "workspace": {
8740
+ "name": "Foundation",
8741
+ "description": "Adds business-model interrogation to the strategic substrate — what the model rests on, which conventions bind it, and which numbers assume what.",
8742
+ "subtype": "foundation"
8743
+ },
8744
+ "dependencies": [
8745
+ {
8746
+ "slug": "foundation",
8747
+ "relation": "compose",
8748
+ "reason": "The model's components ARE the foundation vocabulary — mission, audience, pain-point, positioning, offer, and the client/partner/porteur/funder roles. This pack interrogates them; it does not restate them."
8749
+ }
8750
+ ],
8751
+ "profiles": [
8752
+ {
8753
+ "slug": "question",
8754
+ "displayName": "Question",
8755
+ "icon": "help-circle",
8756
+ "profileKind": "kind",
8757
+ "properties": [
8758
+ {
8759
+ "slug": "questionStatus",
8760
+ "label": "Status",
8761
+ "valueType": "string",
8762
+ "inputType": "select",
8763
+ "enumValues": [
8764
+ "open",
8765
+ "exploring",
8766
+ "answered",
8767
+ "abandoned"
8768
+ ]
8769
+ }
8770
+ ]
8771
+ },
8772
+ {
8773
+ "slug": "decision",
8774
+ "displayName": "Decision",
8775
+ "icon": "gavel",
8776
+ "profileKind": "kind",
8777
+ "properties": [
8778
+ {
8779
+ "slug": "decisionStatus",
8780
+ "label": "Status",
8781
+ "valueType": "string",
8782
+ "inputType": "select",
8783
+ "enumValues": [
8784
+ "proposed",
8785
+ "accepted",
8786
+ "superseded",
8787
+ "rejected"
8788
+ ]
8789
+ }
8790
+ ]
8791
+ },
8792
+ {
8793
+ "slug": "convention",
8794
+ "displayName": "Convention",
8795
+ "icon": "scale",
8796
+ "color": "rose",
8797
+ "description": "A binding rule the model must satisfy and did not choose — an eligibility threshold, a professional duty, a contractual floor, a platform policy. Distinct from a decision (nobody decided it) and from a finding (it constrains rather than informs).",
8798
+ "profileKind": "kind",
8799
+ "properties": [
8800
+ {
8801
+ "slug": "rule",
8802
+ "label": "The Rule",
8803
+ "valueType": "string",
8804
+ "inputType": "textarea",
8805
+ "description": "State the constraint in the terms the issuer uses, not in a paraphrase. Paraphrase is where eligibility collisions hide."
8806
+ },
8807
+ {
8808
+ "slug": "issuer",
8809
+ "label": "Issued By",
8810
+ "valueType": "string",
8811
+ "inputType": "text",
8812
+ "description": "Who imposes it — a regulator, a funder, an ordre professionnel, a platform, a counterparty."
8813
+ },
8814
+ {
8815
+ "slug": "conventionType",
8816
+ "label": "Type",
8817
+ "valueType": "string",
8818
+ "inputType": "select",
8819
+ "enumValues": [
8820
+ "regulatory",
8821
+ "deontological",
8822
+ "eligibility",
8823
+ "contractual",
8824
+ "platform",
8825
+ "fiscal",
8826
+ "other"
8827
+ ]
8828
+ },
8829
+ {
8830
+ "slug": "bindingStatus",
8831
+ "label": "Binding Status",
8832
+ "valueType": "string",
8833
+ "inputType": "select",
8834
+ "enumValues": [
8835
+ "verified",
8836
+ "assumed",
8837
+ "contested",
8838
+ "expired"
8839
+ ],
8840
+ "description": "`assumed` is the dangerous default — it means nobody has read the source text yet."
8841
+ },
8842
+ {
8843
+ "slug": "sourceReference",
8844
+ "label": "Source Reference",
8845
+ "valueType": "string",
8846
+ "inputType": "text",
8847
+ "description": "The cahier des charges, article, or guide this comes from. A convention with no reference is an assumption wearing a rule's clothes."
8848
+ },
8849
+ {
8850
+ "slug": "expiresAt",
8851
+ "label": "Expires / Review By",
8852
+ "valueType": "date",
8853
+ "inputType": "date"
8854
+ },
8855
+ {
8856
+ "slug": "consequenceIfBreached",
8857
+ "label": "Consequence If Breached",
8858
+ "valueType": "string",
8859
+ "inputType": "textarea"
8860
+ }
8861
+ ]
8862
+ },
8863
+ {
8864
+ "slug": "projection",
8865
+ "displayName": "Projection",
8866
+ "icon": "trending-up",
8867
+ "color": "violet",
8868
+ "description": "A quantified scenario with its assumptions attached. The number is never the artifact — the assumptions are, because that is the only part anyone can argue with.",
8869
+ "profileKind": "kind",
8870
+ "properties": [
8871
+ {
8872
+ "slug": "scenario",
8873
+ "label": "Scenario",
8874
+ "valueType": "string",
8875
+ "inputType": "select",
8876
+ "enumValues": [
8877
+ "pessimistic",
8878
+ "base",
8879
+ "optimistic"
8880
+ ]
8881
+ },
8882
+ {
8883
+ "slug": "horizon",
8884
+ "label": "Horizon",
8885
+ "valueType": "string",
8886
+ "inputType": "select",
8887
+ "enumValues": [
8888
+ "month",
8889
+ "quarter",
8890
+ "year",
8891
+ "three-year"
8892
+ ]
8893
+ },
8894
+ {
8895
+ "slug": "assumptions",
8896
+ "label": "Assumptions",
8897
+ "valueType": "string",
8898
+ "inputType": "textarea",
8899
+ "description": "Every input this number needs to be true. When one changes, whatever rests on this projection goes stale."
8900
+ },
8901
+ {
8902
+ "slug": "revenueEstimate",
8903
+ "label": "Revenue Estimate",
8904
+ "valueType": "number",
8905
+ "inputType": "number"
8906
+ },
8907
+ {
8908
+ "slug": "costEstimate",
8909
+ "label": "Cost Estimate",
8910
+ "valueType": "number",
8911
+ "inputType": "number"
8912
+ },
8913
+ {
8914
+ "slug": "breakEvenUnits",
8915
+ "label": "Break-Even Units",
8916
+ "valueType": "number",
8917
+ "inputType": "number"
8918
+ },
8919
+ {
8920
+ "slug": "confidence",
8921
+ "label": "Confidence",
8922
+ "valueType": "string",
8923
+ "inputType": "select",
8924
+ "enumValues": [
8925
+ "low",
8926
+ "medium",
8927
+ "high"
8928
+ ]
8929
+ }
8930
+ ]
8931
+ }
8932
+ ],
8933
+ "relationDefs": [
8934
+ {
8935
+ "slug": "rests_on",
8936
+ "displayName": "Rests On",
8937
+ "description": "The source depends on the target for its validity — not for its scheduling. Cycles are LEGAL and meaningful here: a business model is mutually supporting by nature, and forcing it into an acyclic dependency graph destroys the information. When the target changes, everything resting on it is stale until re-checked.",
8938
+ "isDirectional": true,
8939
+ "uiHints": {
8940
+ "category": "reference",
8941
+ "inverseLabel": "Supports"
8942
+ }
8943
+ },
8944
+ {
8945
+ "slug": "refutes",
8946
+ "displayName": "Refutes",
8947
+ "description": "The source is evidence that the target is false. Kept as a first-class edge so a refuted component stays in the graph with its refutation attached, instead of being quietly deleted — the reason a road was not taken is worth more than the road.",
8948
+ "isDirectional": true,
8949
+ "uiHints": {
8950
+ "category": "reference",
8951
+ "inverseLabel": "Refuted By"
8952
+ }
8953
+ }
8954
+ ],
8955
+ "entityLinks": [
8956
+ {
8957
+ "sourceProfileSlug": "offer",
8958
+ "targetProfileSlug": "convention",
8959
+ "type": "rests_on",
8960
+ "label": "Rests on"
8961
+ },
8962
+ {
8963
+ "sourceProfileSlug": "offer",
8964
+ "targetProfileSlug": "audience",
8965
+ "type": "rests_on",
8966
+ "label": "Rests on"
8967
+ },
8968
+ {
8969
+ "sourceProfileSlug": "projection",
8970
+ "targetProfileSlug": "offer",
8971
+ "type": "rests_on",
8972
+ "label": "Rests on"
8973
+ },
8974
+ {
8975
+ "sourceProfileSlug": "decision",
8976
+ "targetProfileSlug": "convention",
8977
+ "type": "rests_on",
8978
+ "label": "Rests on"
8979
+ },
8980
+ {
8981
+ "sourceProfileSlug": "decision",
8982
+ "targetProfileSlug": "projection",
8983
+ "type": "rests_on",
8984
+ "label": "Rests on"
8985
+ },
8986
+ {
8987
+ "sourceProfileSlug": "convention",
8988
+ "targetProfileSlug": "offer",
8989
+ "type": "refutes",
8990
+ "label": "Refutes"
8991
+ },
8992
+ {
8993
+ "sourceProfileSlug": "convention",
8994
+ "targetProfileSlug": "audience",
8995
+ "type": "refutes",
8996
+ "label": "Refutes"
8997
+ },
8998
+ {
8999
+ "sourceProfileSlug": "convention",
9000
+ "targetProfileSlug": "question",
9001
+ "type": "rests_on",
9002
+ "label": "Rests on"
9003
+ }
9004
+ ],
9005
+ "views": [
9006
+ {
9007
+ "name": "Conventions",
9008
+ "slug": "business-model-conventions",
9009
+ "type": "table",
9010
+ "scopeProfileSlug": "convention",
9011
+ "description": "Every rule the model must satisfy, who imposes it, and whether anyone has actually verified it.",
9012
+ "defaultView": true,
9013
+ "config": {
9014
+ "columns": [
9015
+ "title",
9016
+ "conventionType",
9017
+ "issuer",
9018
+ "bindingStatus",
9019
+ "expiresAt"
9020
+ ]
9021
+ }
9022
+ },
9023
+ {
9024
+ "name": "Unverified Conventions",
9025
+ "slug": "business-model-assumed-conventions",
9026
+ "type": "list",
9027
+ "scopeProfileSlug": "convention",
9028
+ "description": "Conventions still marked assumed or contested — the model rests on these and nobody has read the source text.",
9029
+ "config": {
9030
+ "filters": [
9031
+ {
9032
+ "field": "bindingStatus",
9033
+ "operator": "in",
9034
+ "value": [
9035
+ "assumed",
9036
+ "contested"
9037
+ ]
9038
+ }
9039
+ ]
9040
+ }
9041
+ },
9042
+ {
9043
+ "name": "Projections",
9044
+ "slug": "business-model-projections",
9045
+ "type": "table",
9046
+ "scopeProfileSlug": "projection",
9047
+ "description": "Scenarios with their assumptions in the open, so a number can be argued with.",
9048
+ "config": {
9049
+ "columns": [
9050
+ "title",
9051
+ "scenario",
9052
+ "horizon",
9053
+ "revenueEstimate",
9054
+ "costEstimate",
9055
+ "confidence"
9056
+ ]
9057
+ }
9058
+ },
9059
+ {
9060
+ "name": "Open Interrogation",
9061
+ "slug": "business-model-open-questions",
9062
+ "type": "kanban",
9063
+ "scopeProfileSlug": "question",
9064
+ "groupBy": "questionStatus",
9065
+ "description": "The nine component questions and everything they spawned, by status."
9066
+ }
9067
+ ],
9068
+ "seedEntities": [
9069
+ {
9070
+ "profileSlug": "question",
9071
+ "title": "Who is the porteur, and what does the model rest on them for?",
9072
+ "properties": {
9073
+ "questionStatus": "open"
9074
+ },
9075
+ "content": "Name the person, then name the specific competence, network, capital, or legitimacy the model cannot survive without. If the answer is generic, the model has no porteur — it has a job posting."
9076
+ },
9077
+ {
9078
+ "profileSlug": "question",
9079
+ "title": "What does the porteur's situation forbid?",
9080
+ "properties": {
9081
+ "questionStatus": "open"
9082
+ },
9083
+ "content": "Runway, hours available, geography, non-compete, dependants, health. Constraints discovered in month four were knowable in week one."
9084
+ },
9085
+ {
9086
+ "profileSlug": "question",
9087
+ "title": "What is the offer, stated as the problem it ends?",
9088
+ "properties": {
9089
+ "questionStatus": "open"
9090
+ },
9091
+ "content": "Not a description of what is built — a statement of the situation a buyer is in before, and after. If the offer can only be described by its features, the buyer has not been found yet."
9092
+ },
9093
+ {
9094
+ "profileSlug": "question",
9095
+ "title": "Who exactly buys it, and who merely benefits from it?",
9096
+ "properties": {
9097
+ "questionStatus": "open"
9098
+ },
9099
+ "content": "Link to `audience` entities. Payer, user, and beneficiary are frequently three different parties, and a model that conflates them forecasts revenue from people who never pay."
9100
+ },
9101
+ {
9102
+ "profileSlug": "question",
9103
+ "title": "Which conventions bind this model, and which are still assumed?",
9104
+ "properties": {
9105
+ "questionStatus": "open"
9106
+ },
9107
+ "content": "Eligibility thresholds, professional duties, platform policies, fiscal regimes. Every convention starts `assumed`; the work is reading the source text and moving it to `verified`. An eligibility collision — where the segment with the most pull sits outside the scheme that would fund it — only becomes visible once the numbers are pulled."
9108
+ },
9109
+ {
9110
+ "profileSlug": "question",
9111
+ "title": "What does one unit of delivery actually cost to produce?",
9112
+ "properties": {
9113
+ "questionStatus": "open"
9114
+ },
9115
+ "content": "Time, tooling, subcontracting, the unbilled parts. Link the answer to a `projection` so the assumptions stay attached to the number."
9116
+ },
9117
+ {
9118
+ "profileSlug": "question",
9119
+ "title": "Where does the money come from before revenue does?",
9120
+ "properties": {
9121
+ "questionStatus": "open"
9122
+ },
9123
+ "content": "Attach `funder` roles to the companies or schemes involved, with their eligibility conditions. Subsidy is a credential and a discount; it is rarely a pipeline."
9124
+ },
9125
+ {
9126
+ "profileSlug": "question",
9127
+ "title": "What means — people, tools, licences, premises — must exist first?",
9128
+ "properties": {
9129
+ "questionStatus": "open"
9130
+ },
9131
+ "content": "The means rest on the money and the money rests on the offer. That circularity is the model, not a planning error; record it with `rests_on` and stop trying to linearise it."
9132
+ },
9133
+ {
9134
+ "profileSlug": "question",
9135
+ "title": "What observation would refute this model, and by when?",
9136
+ "properties": {
9137
+ "questionStatus": "open"
9138
+ },
9139
+ "content": "Set the falsifiable test now, while it can still be set honestly. A model with no refuting observation is a belief with a spreadsheet attached."
9140
+ }
9141
+ ],
9142
+ "suggestedRelations": [
9143
+ {
9144
+ "sourceRef": "What is the offer, stated as the problem it ends?",
9145
+ "targetRef": "Who exactly buys it, and who merely benefits from it?",
9146
+ "type": "rests_on"
9147
+ },
9148
+ {
9149
+ "sourceRef": "What is the offer, stated as the problem it ends?",
9150
+ "targetRef": "What means — people, tools, licences, premises — must exist first?",
9151
+ "type": "rests_on"
9152
+ },
9153
+ {
9154
+ "sourceRef": "What means — people, tools, licences, premises — must exist first?",
9155
+ "targetRef": "Where does the money come from before revenue does?",
9156
+ "type": "rests_on"
9157
+ },
9158
+ {
9159
+ "sourceRef": "Where does the money come from before revenue does?",
9160
+ "targetRef": "What is the offer, stated as the problem it ends?",
9161
+ "type": "rests_on"
9162
+ },
9163
+ {
9164
+ "sourceRef": "Where does the money come from before revenue does?",
9165
+ "targetRef": "Which conventions bind this model, and which are still assumed?",
9166
+ "type": "rests_on"
9167
+ },
9168
+ {
9169
+ "sourceRef": "Who exactly buys it, and who merely benefits from it?",
9170
+ "targetRef": "Which conventions bind this model, and which are still assumed?",
9171
+ "type": "rests_on"
9172
+ },
9173
+ {
9174
+ "sourceRef": "What does one unit of delivery actually cost to produce?",
9175
+ "targetRef": "What does the porteur's situation forbid?",
9176
+ "type": "rests_on"
9177
+ },
9178
+ {
9179
+ "sourceRef": "What is the offer, stated as the problem it ends?",
9180
+ "targetRef": "Who is the porteur, and what does the model rest on them for?",
9181
+ "type": "rests_on"
9182
+ }
9183
+ ],
9184
+ "playbooks": [
9185
+ {
9186
+ "name": "Business Model Interrogation",
9187
+ "description": "Walk the nine components as questions, resolve each into a decision backed by evidence, and record the conventions and projections the model rests on — including the cycles.",
9188
+ "goalTemplate": "Interrogate the business model for {venture} and record what it rests on. Steps: (1) Walk each of the nine seeded `question` entities in this workspace; where one is already answered, mark it and move on. (2) Resolve each into a `decision`, keeping the options that lost attached via `refutes` rather than deleting them. Where the evidence itself needs gathering, do that in the Research workspace and link back — do not restate sources here. (3) Every external rule discovered becomes a `convention` with its `issuer` and `sourceReference`; leave `bindingStatus` at `assumed` until the source text has actually been read, then `verified`. (4) Every number becomes a `projection` with its `assumptions` filled in — never a bare figure. (5) Wire the graph with `rests_on` — offer on audience, projection on offer, decision on convention. Do NOT try to make the graph acyclic: if the offer rests on the means and the means rest on the money and the money rests on the offer, record all three. (6) Attach `porteur` and `funder` roles to the real people and organisations instead of creating new entities for them. (7) Close by listing which conventions are still `assumed` and which projections carry `low` confidence — that list is the actual state of the model.",
9189
+ "icon": "layout-grid"
9190
+ }
9191
+ ],
9192
+ "actionPlacements": [
9193
+ {
9194
+ "profileSlug": "question",
9195
+ "surface": "entity-detail",
9196
+ "kind": "playbook",
9197
+ "ref": "Business Model Interrogation",
9198
+ "label": "Interrogate as model component"
9199
+ },
9200
+ {
9201
+ "profileSlug": "convention",
9202
+ "surface": "entity-detail",
9203
+ "kind": "playbook",
9204
+ "ref": "Business Model Interrogation",
9205
+ "label": "Re-check what rests on this"
9206
+ }
9207
+ ],
9208
+ "integrations": [
9209
+ {
9210
+ "templateKey": "research-methods"
9211
+ },
9212
+ {
9213
+ "templateKey": "exa"
9214
+ }
9215
+ ]
9216
+ },
9217
+ "civic-france": {
9218
+ "meta": {
9219
+ "slug": "civic-france",
9220
+ "domain": "knowledge",
9221
+ "name": "France Civic Research",
9222
+ "description": "Overlay for investigating French public data (open data / Licence Ouverte). Composes onto Research. Not a government product — a private research stub.",
9223
+ "icon": "landmark",
9224
+ "color": "#1D4ED8",
9225
+ "tags": [
9226
+ "research",
9227
+ "civic",
9228
+ "france",
9229
+ "open-data",
9230
+ "licence-ouverte"
9231
+ ],
9232
+ "isPublic": false
9233
+ },
9234
+ "workspace": {
9235
+ "name": "Research",
9236
+ "description": "Adds a French civic open-data lens onto the Research substrate — track public datasets consulted during investigations.",
9237
+ "subtype": "research-base"
9238
+ },
9239
+ "dependencies": [
9240
+ {
9241
+ "slug": "research-base",
9242
+ "relation": "compose",
9243
+ "reason": "France civic research is a research method — it needs the Research substrate (question / research / source) to work over."
9244
+ }
9245
+ ],
9246
+ "profiles": [
9247
+ {
9248
+ "slug": "question",
9249
+ "displayName": "Question",
9250
+ "icon": "help-circle",
9251
+ "color": "blue",
9252
+ "description": "An open question driving a civic-data investigation.",
9253
+ "properties": [
9254
+ {
9255
+ "slug": "questionStatus",
9256
+ "label": "Status",
9257
+ "valueType": "string",
9258
+ "inputType": "select",
9259
+ "enumValues": [
9260
+ "open",
9261
+ "exploring",
9262
+ "answered",
9263
+ "abandoned"
9264
+ ],
9265
+ "constraints": {
9266
+ "defaultValue": "open"
9267
+ }
9268
+ }
9269
+ ]
9270
+ },
9271
+ {
9272
+ "slug": "research",
9273
+ "displayName": "Research",
9274
+ "icon": "microscope",
9275
+ "color": "violet",
9276
+ "description": "An investigation into French public data — sources, findings, conclusion.",
9277
+ "properties": [
9278
+ {
9279
+ "slug": "researchStatus",
9280
+ "label": "Status",
9281
+ "valueType": "string",
9282
+ "inputType": "select",
9283
+ "enumValues": [
9284
+ "ongoing",
9285
+ "concluded",
9286
+ "abandoned"
9287
+ ],
9288
+ "constraints": {
9289
+ "defaultValue": "ongoing"
9290
+ }
9291
+ }
9292
+ ]
9293
+ },
9294
+ {
9295
+ "slug": "source",
9296
+ "displayName": "Source",
9297
+ "icon": "file-text",
9298
+ "color": "cyan",
9299
+ "description": "Anything consulted — API payload, dataset page, decree, report.",
9300
+ "properties": [
9301
+ {
9302
+ "slug": "url",
9303
+ "label": "URL",
9304
+ "valueType": "string",
9305
+ "inputType": "url"
9306
+ }
9307
+ ]
9308
+ },
9309
+ {
9310
+ "slug": "public_dataset",
9311
+ "displayName": "Public dataset",
9312
+ "icon": "database",
9313
+ "color": "blue",
9314
+ "description": "A French public dataset consulted via official open-data APIs (data.gouv.fr / api.gouv.fr / PISTE) — never scraped HTML.",
9315
+ "scope": "WORKSPACE",
9316
+ "properties": [
9317
+ {
9318
+ "slug": "sourceUrl",
9319
+ "label": "Source URL",
9320
+ "valueType": "string",
9321
+ "inputType": "url"
9322
+ },
9323
+ {
9324
+ "slug": "license",
9325
+ "label": "License",
9326
+ "valueType": "string",
9327
+ "inputType": "text"
9328
+ },
9329
+ {
9330
+ "slug": "publisher",
9331
+ "label": "Publisher",
9332
+ "valueType": "string",
9333
+ "inputType": "text"
8599
9334
  }
8600
- },
8601
- "confirmation": {
8602
- "title": "Hand off this won proposal?",
8603
- "description": "This converts its qualified CRM lead counterparty and records the business developer commission.",
8604
- "confirmLabel": "Hand off proposal"
9335
+ ]
9336
+ }
9337
+ ],
9338
+ "views": [
9339
+ {
9340
+ "name": "Public datasets",
9341
+ "slug": "civic-france-public-datasets",
9342
+ "type": "table",
9343
+ "scopeProfileSlug": "public_dataset",
9344
+ "description": "French public datasets tracked in this research workspace.",
9345
+ "config": {
9346
+ "columns": [
9347
+ "title",
9348
+ "publisher",
9349
+ "license",
9350
+ "sourceUrl"
9351
+ ]
8605
9352
  }
8606
9353
  }
8607
9354
  ]
@@ -15756,6 +16503,95 @@ const DATA = {
15756
16503
  "inputType": "textarea"
15757
16504
  }
15758
16505
  ]
16506
+ },
16507
+ {
16508
+ "slug": "porteur",
16509
+ "displayName": "Porteur",
16510
+ "icon": "user-check",
16511
+ "color": "amber",
16512
+ "description": "The person carrying a venture — the one whose commitment, skills, and constraints the model actually rests on. Distinct from `client` (who pays) and `partner` (who collaborates).",
16513
+ "scope": "shared",
16514
+ "profileKind": "role",
16515
+ "applicableKinds": [
16516
+ "person"
16517
+ ],
16518
+ "properties": [
16519
+ {
16520
+ "slug": "brings",
16521
+ "label": "What They Bring",
16522
+ "valueType": "string",
16523
+ "inputType": "textarea",
16524
+ "description": "The specific competence, network, capital, or legitimacy this person contributes that the model depends on."
16525
+ },
16526
+ {
16527
+ "slug": "commitmentLevel",
16528
+ "label": "Commitment",
16529
+ "valueType": "string",
16530
+ "inputType": "select",
16531
+ "enumValues": [
16532
+ "full-time",
16533
+ "part-time",
16534
+ "advisory",
16535
+ "intermittent"
16536
+ ]
16537
+ },
16538
+ {
16539
+ "slug": "constraints",
16540
+ "label": "Constraints",
16541
+ "valueType": "string",
16542
+ "inputType": "textarea",
16543
+ "description": "What limits this porteur — time, cash runway, geography, non-compete, family. A model that ignores these is fiction."
16544
+ }
16545
+ ]
16546
+ },
16547
+ {
16548
+ "slug": "funder",
16549
+ "displayName": "Funder",
16550
+ "icon": "banknote",
16551
+ "color": "green",
16552
+ "description": "A source of non-revenue money or subsidy — a bank, a public scheme, an investor, a grantor. A hat worn by a company or a person, never a kind of its own.",
16553
+ "scope": "shared",
16554
+ "profileKind": "role",
16555
+ "applicableKinds": [
16556
+ "company",
16557
+ "person"
16558
+ ],
16559
+ "properties": [
16560
+ {
16561
+ "slug": "fundingType",
16562
+ "label": "Funding Type",
16563
+ "valueType": "string",
16564
+ "inputType": "select",
16565
+ "enumValues": [
16566
+ "grant",
16567
+ "subsidy",
16568
+ "loan",
16569
+ "equity",
16570
+ "guarantee",
16571
+ "prize",
16572
+ "other"
16573
+ ]
16574
+ },
16575
+ {
16576
+ "slug": "amountCommitted",
16577
+ "label": "Amount Committed",
16578
+ "valueType": "number",
16579
+ "inputType": "number"
16580
+ },
16581
+ {
16582
+ "slug": "eligibilityConditions",
16583
+ "label": "Eligibility Conditions",
16584
+ "valueType": "string",
16585
+ "inputType": "textarea",
16586
+ "description": "The conditions the venture must satisfy to keep this money. These are the constraints most often discovered too late."
16587
+ },
16588
+ {
16589
+ "slug": "decisionDate",
16590
+ "label": "Decision Date",
16591
+ "valueType": "date",
16592
+ "inputType": "date"
16593
+ }
16594
+ ]
15759
16595
  }
15760
16596
  ],
15761
16597
  "views": [
@@ -16043,6 +16879,12 @@ const DATA = {
16043
16879
  "kind": "workspace",
16044
16880
  "relation": "require",
16045
16881
  "reason": "Provides the pod-wide client / partner / sponsor role vocabulary this workspace reuses."
16882
+ },
16883
+ {
16884
+ "slug": "blockchain-ecosystem",
16885
+ "kind": "workspace",
16886
+ "relation": "require",
16887
+ "reason": "Provides the pod-wide grant-program kind (per-chain funding programs) this workspace's rounds belong to."
16046
16888
  }
16047
16889
  ],
16048
16890
  "workspace": {
@@ -17202,12 +18044,21 @@ const DATA = {
17202
18044
  "sortDir": "desc"
17203
18045
  }
17204
18046
  },
18047
+ {
18048
+ "name": "Deadlines",
18049
+ "type": "calendar",
18050
+ "scopeProfileSlug": "round",
18051
+ "description": "Upcoming round submission deadlines, on one time axis.",
18052
+ "config": {
18053
+ "dateField": "properties.submissionDeadline",
18054
+ "colorField": "status"
18055
+ }
18056
+ },
17205
18057
  {
17206
18058
  "name": "Awarded Projects",
17207
18059
  "type": "gallery",
17208
18060
  "scopeProfileSlug": "grant-project",
17209
18061
  "config": {
17210
- "coverField": "summary",
17211
18062
  "showFields": [
17212
18063
  "currentStatus",
17213
18064
  "awardedRoundCount",
@@ -17251,6 +18102,30 @@ const DATA = {
17251
18102
  "type": "submitted_by",
17252
18103
  "label": "Submitted By"
17253
18104
  },
18105
+ {
18106
+ "sourceProfileSlug": "grant_submission",
18107
+ "targetProfileSlug": "contract",
18108
+ "type": "for_contract",
18109
+ "label": "Engagement"
18110
+ },
18111
+ {
18112
+ "sourceProfileSlug": "grant_submission",
18113
+ "targetProfileSlug": "deal",
18114
+ "type": "billed_as",
18115
+ "label": "Billed as"
18116
+ },
18117
+ {
18118
+ "sourceProfileSlug": "round",
18119
+ "targetProfileSlug": "grant-program",
18120
+ "type": "belongs_to",
18121
+ "label": "Program"
18122
+ },
18123
+ {
18124
+ "sourceProfileSlug": "contract",
18125
+ "targetProfileSlug": "grant-program",
18126
+ "type": "pursues",
18127
+ "label": "Pursues"
18128
+ },
17254
18129
  {
17255
18130
  "sourceProfileSlug": "grant-project",
17256
18131
  "targetProfileSlug": "round",
@@ -17276,6 +18151,222 @@ const DATA = {
17276
18151
  "label": "Evidenced By"
17277
18152
  }
17278
18153
  ],
18154
+ "playbooks": [
18155
+ {
18156
+ "name": "Grant Process",
18157
+ "description": "Run an engagement through a grant program end to end — identify fit, draft the application, submit, track the decision, and deliver the tranches. Reads the program's own cadence / tracks / tranche model so one playbook fits every chain.",
18158
+ "goalTemplate": "Advance this engagement ({contract}) through the grant program ({grantProgram}) it is pursuing. FIRST read the program entity's own data — its cadence, submissionTracks, trancheModel, and nextDeadline — plus the client context on the contract, and let THOSE drive the plan: do not assume a chain's mechanics, read them from the program. Then work the stages in order and keep the client's `grant_submission` card as the durable state of record:\n1. Identify — understand the project and which of the program's\n submissionTracks fit; set the submission `stage` to `scope`.\n2. Draft — write the abstract, then the full proposal (products & services,\n success criteria, GTM, traction); move `stage` `abstract` → `build` and\n fill the submission's body fields.\n3. Submit — finalize against the program's nextDeadline and submit; once\n submitted, set `stage` to `awaiting-results`.\n4. Awaiting — track the decision and keep `status` current\n (in-review / waiting-client / needs-document / blocked).\n5. Awarded — on a decision, set `outcome` (awarded / rejected / partial)\n and record the awarded amount.\n6. Delivering — for an award, work the program's trancheModel: fill the\n tranche deliverables and update trancheCompletion as each milestone ships.\n\nNever fabricate — leave a field blank rather than guess. Every write is proposal-gated for the user to review.",
18159
+ "subjectProfile": {
18160
+ "profileSlug": "contract"
18161
+ },
18162
+ "params": [
18163
+ {
18164
+ "name": "contract",
18165
+ "type": "entity",
18166
+ "profileSlug": "contract",
18167
+ "label": "Engagement",
18168
+ "required": true
18169
+ },
18170
+ {
18171
+ "name": "grantProgram",
18172
+ "type": "entity",
18173
+ "profileSlug": "grant-program",
18174
+ "label": "Grant Program",
18175
+ "required": true
18176
+ }
18177
+ ],
18178
+ "scope": "project",
18179
+ "stages": [
18180
+ {
18181
+ "key": "identify",
18182
+ "name": "Identify",
18183
+ "category": "planned",
18184
+ "description": "Understand the project and which of the program's submission tracks fit — before any writing.",
18185
+ "goal": "A clear project understanding and the fitting submission track; submission stage = scope."
18186
+ },
18187
+ {
18188
+ "key": "draft",
18189
+ "name": "Draft",
18190
+ "category": "started",
18191
+ "description": "Write the abstract, then the full proposal, from the client's real material.",
18192
+ "goal": "Abstract then full proposal drafted; submission stage abstract → build with body fields filled."
18193
+ },
18194
+ {
18195
+ "key": "submit",
18196
+ "name": "Submit",
18197
+ "category": "started",
18198
+ "description": "Finalize against the program deadline and submit the application.",
18199
+ "goal": "Application submitted on time; submission stage = awaiting-results."
18200
+ },
18201
+ {
18202
+ "key": "awaiting",
18203
+ "name": "Awaiting",
18204
+ "category": "paused",
18205
+ "description": "Track the decision and keep the client status current.",
18206
+ "goal": "Submission status reflects where the decision stands (in-review / waiting-client / needs-document / blocked)."
18207
+ },
18208
+ {
18209
+ "key": "awarded",
18210
+ "name": "Awarded",
18211
+ "category": "started",
18212
+ "description": "Record the funder's decision and the awarded amount.",
18213
+ "goal": "Submission outcome set (awarded / rejected / partial) with the awarded amount."
18214
+ },
18215
+ {
18216
+ "key": "delivering",
18217
+ "name": "Delivering",
18218
+ "category": "started",
18219
+ "description": "Work the program's tranche model — ship and record each tranche.",
18220
+ "goal": "Tranche deliverables filled and trancheCompletion updated as milestones ship."
18221
+ }
18222
+ ],
18223
+ "executor": "is-agent",
18224
+ "grants": [
18225
+ "entity.create"
18226
+ ],
18227
+ "status": "active"
18228
+ }
18229
+ ],
18230
+ "automations": [
18231
+ {
18232
+ "key": "grant-submission-sync",
18233
+ "name": "Grant Submission Sync",
18234
+ "description": "Once a day, keep each grant application's grant_submission card current — read the submission's recent activity, assess where the pursuit stands, and advance its stage + status so the pipeline stays the durable, kanban-visible state of record.",
18235
+ "triggerType": "cron",
18236
+ "triggerConfig": {
18237
+ "expression": "0 8 * * *"
18238
+ },
18239
+ "status": "draft",
18240
+ "metadata": {
18241
+ "templateKey": "grant-submission-sync"
18242
+ },
18243
+ "flow": {
18244
+ "nodes": [
18245
+ {
18246
+ "id": "trigger",
18247
+ "type": "trigger",
18248
+ "position": {
18249
+ "x": 0,
18250
+ "y": 0
18251
+ },
18252
+ "data": {
18253
+ "label": "Daily at 08:00",
18254
+ "triggerType": "cron",
18255
+ "config": {
18256
+ "expression": "0 8 * * *"
18257
+ }
18258
+ }
18259
+ },
18260
+ {
18261
+ "id": "list-submissions",
18262
+ "type": "capability",
18263
+ "position": {
18264
+ "x": 0,
18265
+ "y": 120
18266
+ },
18267
+ "data": {
18268
+ "label": "List grant submissions to maintain",
18269
+ "verbId": "entity.query",
18270
+ "inputMapping": {
18271
+ "profileSlug": "grant_submission",
18272
+ "limit": "100"
18273
+ }
18274
+ }
18275
+ },
18276
+ {
18277
+ "id": "submissions-loop",
18278
+ "type": "loop",
18279
+ "position": {
18280
+ "x": 0,
18281
+ "y": 240
18282
+ },
18283
+ "data": {
18284
+ "label": "For each grant submission",
18285
+ "iteratorExpression": "steps.list-submissions.output.entities",
18286
+ "itemVariable": "submission"
18287
+ }
18288
+ },
18289
+ {
18290
+ "id": "read-activity",
18291
+ "type": "messages_query",
18292
+ "position": {
18293
+ "x": 0,
18294
+ "y": 360
18295
+ },
18296
+ "data": {
18297
+ "label": "Read the submission's recent activity",
18298
+ "subjectEntityId": "{{loop.item.id}}",
18299
+ "limit": 40
18300
+ }
18301
+ },
18302
+ {
18303
+ "id": "assess",
18304
+ "type": "capability",
18305
+ "position": {
18306
+ "x": 0,
18307
+ "y": 480
18308
+ },
18309
+ "data": {
18310
+ "label": "Assess the submission's stage + status → JSON",
18311
+ "verbId": "ai.generate",
18312
+ "inputMapping": {
18313
+ "json": "true",
18314
+ "maxTokens": "500",
18315
+ "system": "You are a strict data-extraction function for a grants pipeline. Read a grant application's recent activity and report where the application currently stands. Respond with STRICT JSON and NOTHING else. Never fabricate — if the activity does not clearly indicate a value, keep the submission's current one.",
18316
+ "prompt": "The grant submission: {{loop.item.title}} Current stage: {{loop.item.properties.stage}} Current status: {{loop.item.properties.status}}\nRecent activity (oldest first): {{steps.read-activity.output.messages}}\nReport the submission's current state as STRICT JSON, using ONLY these vocabularies and NOTHING else: {\n \"stage\": \"scope|abstract|build|awaiting-results\",\n \"status\": \"answered|waiting-client|needs-document|in-review|blocked\"\n}"
18317
+ }
18318
+ }
18319
+ },
18320
+ {
18321
+ "id": "advance-submission",
18322
+ "type": "output",
18323
+ "position": {
18324
+ "x": 0,
18325
+ "y": 600
18326
+ },
18327
+ "data": {
18328
+ "label": "Advance the submission stage + status",
18329
+ "outputType": "entity_update",
18330
+ "config": {
18331
+ "entityId": "{{loop.item.id}}",
18332
+ "properties": {
18333
+ "stage": "{{steps.assess.output.stage}}",
18334
+ "status": "{{steps.assess.output.status}}"
18335
+ }
18336
+ }
18337
+ }
18338
+ }
18339
+ ],
18340
+ "edges": [
18341
+ {
18342
+ "id": "gs-trigger-list",
18343
+ "source": "trigger",
18344
+ "target": "list-submissions"
18345
+ },
18346
+ {
18347
+ "id": "gs-list-loop",
18348
+ "source": "list-submissions",
18349
+ "target": "submissions-loop"
18350
+ },
18351
+ {
18352
+ "id": "gs-loop-read",
18353
+ "source": "submissions-loop",
18354
+ "target": "read-activity"
18355
+ },
18356
+ {
18357
+ "id": "gs-read-assess",
18358
+ "source": "read-activity",
18359
+ "target": "assess"
18360
+ },
18361
+ {
18362
+ "id": "gs-assess-advance",
18363
+ "source": "assess",
18364
+ "target": "advance-submission"
18365
+ }
18366
+ ]
18367
+ }
18368
+ }
18369
+ ],
17279
18370
  "app": {
17280
18371
  "kind": "stored",
17281
18372
  "cellKey": "entity-detail"
@@ -21143,6 +22234,19 @@ const DATA = {
21143
22234
  }
21144
22235
  ],
21145
22236
  "views": [
22237
+ {
22238
+ "name": "Knowledge",
22239
+ "slug": "knowledge",
22240
+ "type": "table",
22241
+ "scopeProfileSlug": "knowledge",
22242
+ "description": "Validated insights and cautions, with their long-form context and evidence kept in the graph.",
22243
+ "config": {
22244
+ "columns": [
22245
+ "title",
22246
+ "knowledgeForm"
22247
+ ]
22248
+ }
22249
+ },
21146
22250
  {
21147
22251
  "name": "All Notes",
21148
22252
  "type": "table",
@@ -21981,6 +23085,12 @@ const DATA = {
21981
23085
  "label": "Brain Home",
21982
23086
  "icon": "Home"
21983
23087
  },
23088
+ {
23089
+ "kind": "profile",
23090
+ "profileSlug": "knowledge",
23091
+ "label": "Knowledge",
23092
+ "icon": "Brain"
23093
+ },
21984
23094
  {
21985
23095
  "kind": "profile",
21986
23096
  "profileSlug": "note",
@@ -23941,6 +25051,7 @@ const DATA = {
23941
25051
  "description": "A company or person that provides a capability The Arch can broker a client need against.",
23942
25052
  "scope": "shared",
23943
25053
  "profileKind": "role",
25054
+ "roleCategory": "provider",
23944
25055
  "applicableKinds": [
23945
25056
  "company",
23946
25057
  "person"
@@ -24120,10 +25231,10 @@ const DATA = {
24120
25231
  "y": 240
24121
25232
  },
24122
25233
  "data": {
24123
- "label": "List the solution-provider roster",
25234
+ "label": "List the provider roster (any roleCategory=provider)",
24124
25235
  "verbId": "entity.query",
24125
25236
  "inputMapping": {
24126
- "profileSlug": "solution-provider",
25237
+ "roleCategory": "provider",
24127
25238
  "limit": "100"
24128
25239
  }
24129
25240
  }
@@ -29636,6 +30747,12 @@ const DATA = {
29636
30747
  "kind": "workspace",
29637
30748
  "relation": "require",
29638
30749
  "reason": "The engineering / ops dashboard — the build-side view of the company."
30750
+ },
30751
+ {
30752
+ "slug": "networking",
30753
+ "kind": "workspace",
30754
+ "relation": "require",
30755
+ "reason": "The partner-intro layer — need → intro-matcher → intro → billed_as → deal, with the A↔B confidentiality firewall; the Stellar Client-Sync emits needs that depend on it."
29639
30756
  }
29640
30757
  ],
29641
30758
  "workspace": {