@synap-core/workspace-templates 0.11.0 → 0.23.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/README.md +483 -0
- package/automations/bookmark-enrichment.automation.json +321 -0
- package/automations/github-pr-events.automation.json +505 -0
- package/automations/mail-feed.automation.json +86 -0
- package/automations/ship-a-change-checks-green.automation.json +106 -0
- package/automations/ship-a-change-checks-red.automation.json +106 -0
- package/automations/ship-a-change-deployed.automation.json +56 -0
- package/automations/ship-a-change-pr-merged.automation.json +110 -0
- package/automations/ship-a-change-pr-opened.automation.json +104 -0
- package/automations/url-bookmark-capture.automation.json +132 -0
- package/capabilities/agency-skills.capability.json +157 -0
- package/capabilities/apify.capability.json +265 -0
- package/capabilities/apollo.capability.json +120 -0
- package/capabilities/arch-backend.capability.json +97 -0
- package/capabilities/arch-client-intelligence.capability.json +401 -0
- package/capabilities/backblaze-b2.capability.json +205 -0
- package/capabilities/cal-com.capability.json +266 -0
- package/capabilities/canva.capability.json +274 -0
- package/capabilities/claude-code-routines.capability.json +172 -0
- package/capabilities/claude-managed-agents.capability.json +215 -0
- package/capabilities/client-comms-report.capability.json +246 -0
- package/capabilities/content-idea-to-production.capability.json +29 -0
- package/capabilities/datagouv.capability.json +315 -0
- package/capabilities/discord-bot.capability.json +157 -0
- package/capabilities/dropbox.capability.json +281 -0
- package/capabilities/exa.capability.json +109 -0
- package/capabilities/fal-ai.capability.json +83 -0
- package/capabilities/fal-media-lab.capability.json +158 -0
- package/capabilities/fireflies.capability.json +191 -0
- package/capabilities/freellmapi.capability.json +114 -0
- package/capabilities/generic-apikey.capability.json +92 -0
- package/capabilities/github-merge.capability.json +99 -0
- package/capabilities/github-read.capability.json +325 -0
- package/capabilities/governance-tighten-recommender.capability.json +81 -0
- package/capabilities/jev.capability.json +226 -0
- package/capabilities/lead-conversion-enrollment.capability.json +246 -0
- package/capabilities/mail-feed.capability.json +277 -0
- package/capabilities/mailgun.capability.json +52 -0
- package/capabilities/nango-google.capability.json +639 -0
- package/capabilities/notion.capability.json +417 -0
- package/capabilities/omniroute.capability.json +137 -0
- package/capabilities/posthog-analytics.capability.json +258 -0
- package/capabilities/postiz.capability.json +467 -0
- package/capabilities/proactive-digest.capability.json +190 -0
- package/capabilities/proton.capability.json +288 -0
- package/capabilities/replicate-rvm.capability.json +97 -0
- package/capabilities/research-methods.capability.json +57 -0
- package/capabilities/shopify.capability.json +395 -0
- package/capabilities/slack.capability.json +369 -0
- package/capabilities/stripe.capability.json +420 -0
- package/capabilities/telegram-bridge.capability.json +56 -0
- package/capabilities/treg.capability.json +102 -0
- package/capabilities/unipile-linkedin.capability.json +471 -0
- package/capabilities/vercel.capability.json +424 -0
- package/dist/define-CPS0n8BV.d.ts +1331 -0
- package/dist/index.d.ts +189 -900
- package/dist/index.js +24654 -21639
- package/dist/index.js.map +1 -1
- package/dist/wire.d.ts +222 -0
- package/dist/wire.js +77 -0
- package/dist/wire.js.map +1 -0
- package/package.json +41 -27
- package/packs/bookmark-views-pack.json +33 -0
- package/packs/crm-pack.json +76 -0
- package/packs/customer-profile.json +28 -0
- package/packs/deep-research.playbook.json +30 -0
- package/packs/event-views-pack.json +33 -0
- package/packs/habit-profile.json +27 -0
- package/packs/hr-pack.json +54 -0
- package/packs/invoice-profile.json +28 -0
- package/packs/knowledge-base-pack.json +56 -0
- package/packs/meeting-profile.json +29 -0
- package/packs/product-profile.json +30 -0
- package/packs/project-management-pack.json +49 -0
- package/packs/recipe-profile.json +29 -0
- package/packs/task-views-pack.json +39 -0
- package/retired.yaml +136 -0
- package/skills/app-factory-build-slice/SKILL.md +70 -0
- package/skills/app-factory-build-slice.catalog.json +17 -0
- package/skills/app-factory-design-canvas/SKILL.md +96 -0
- package/skills/app-factory-design-canvas.catalog.json +17 -0
- package/skills/app-factory-design-review/SKILL.md +99 -0
- package/skills/app-factory-design-review.catalog.json +18 -0
- package/skills/app-factory-research/SKILL.md +123 -0
- package/skills/app-factory-research.catalog.json +17 -0
- package/skills/bookmark-enrichment/SKILL.md +40 -0
- package/skills/carousel-format/carousel-format.skill.json +18 -0
- package/skills/collage-broll/collage-broll.skill.json +20 -0
- package/skills/content-taste/content-taste.skill.json +18 -0
- package/skills/creative-director/creative-director.skill.json +18 -0
- package/skills/design-critique/SKILL.md +135 -0
- package/skills/design-critique.catalog.json +12 -0
- package/skills/designing-with-real-components/SKILL.md +140 -0
- package/skills/designing-with-real-components.catalog.json +12 -0
- package/skills/edit-loop/edit-loop.skill.json +25 -0
- package/skills/facecam-edit/facecam-edit.skill.json +24 -0
- package/skills/mapping-the-server/SKILL.md +54 -0
- package/skills/math-explainer-manimgx/math-explainer-manimgx.skill.json +20 -0
- package/skills/motion-quality-gates/motion-quality-gates.skill.json +20 -0
- package/skills/open-design-edit/open-design-edit.skill.json +24 -0
- package/skills/reading-client-progress/SKILL.md +65 -0
- package/skills/routing-to-channels/SKILL.md +54 -0
- package/skills/stellar-scf-grants-advisor/ASSOCIATE-CHEATSHEET.md +133 -0
- package/skills/stellar-scf-grants-advisor/ASSOCIATE-ONBOARDING-GUIDE.md +761 -0
- package/skills/stellar-scf-grants-advisor/CLIENT-FAQ.md +181 -0
- package/skills/stellar-scf-grants-advisor/PARTNER-HANDOVER.md +130 -0
- package/skills/stellar-scf-grants-advisor/SKILL.md +102 -0
- package/skills/stellar-scf-grants-advisor/examples/README.md +43 -0
- package/skills/stellar-scf-grants-advisor/examples/chat-summary-templates.md +158 -0
- package/skills/stellar-scf-grants-advisor/examples/corpus-comparable-rows.md +136 -0
- package/skills/stellar-scf-grants-advisor/examples/q10-team-rewrites.md +109 -0
- package/skills/stellar-scf-grants-advisor/examples/q2-description-rewrites.md +88 -0
- package/skills/stellar-scf-grants-advisor/examples/q6-integration-rewrites.md +107 -0
- package/skills/stellar-scf-grants-advisor/examples/tranche-structures.md +176 -0
- package/skills/stellar-scf-grants-advisor/reference/01-methodology.md +96 -0
- package/skills/stellar-scf-grants-advisor/reference/02-scoring-framework.md +199 -0
- package/skills/stellar-scf-grants-advisor/reference/03-rejection-patterns.md +241 -0
- package/skills/stellar-scf-grants-advisor/reference/04-winner-patterns.md +120 -0
- package/skills/stellar-scf-grants-advisor/reference/05-comeback-patterns.md +92 -0
- package/skills/stellar-scf-grants-advisor/reference/06-budget-benchmarks.md +125 -0
- package/skills/stellar-scf-grants-advisor/reference/07-integration-list.md +147 -0
- package/skills/stellar-scf-grants-advisor/reference/08-abstract-fields.md +258 -0
- package/skills/stellar-scf-grants-advisor/reference/09-build-fields.md +259 -0
- package/skills/stellar-scf-grants-advisor/reference/10-corpus-winners.md +135 -0
- package/skills/stellar-scf-grants-advisor/reference/11-corpus-rejects.md +267 -0
- package/skills/stellar-scf-grants-advisor/reference/12-client-communication.md +131 -0
- package/skills/stellar-scf-grants-advisor/reference/13-excel-output-spec.md +199 -0
- package/skills/stellar-scf-grants-advisor/reference/14-worked-examples.md +160 -0
- package/skills/stellar-scf-grants-advisor/templates/abstract-review-builder.py +253 -0
- package/skills/stellar-scf-grants-advisor/templates/build-review-builder.py +213 -0
- package/skills/video-direction/video-direction.skill.json +20 -0
- package/skills/video-style-vocabulary/video-style-vocabulary.skill.json +20 -0
- package/skills/video-toolbox/video-toolbox.skill.json +20 -0
- package/src/__fixtures__/lifecycle-presets.flat-before.json +205 -0
- package/src/agency-os.yaml +122 -0
- package/src/agent-os-skill-slugs.test.ts +65 -0
- package/src/apify-social-reader.test.ts +317 -0
- package/src/base.template.test.ts +25 -19
- package/src/base.yaml +95 -39
- package/src/blockchain-ecosystem.yaml +1 -0
- package/src/brand-library.yaml +525 -14
- package/src/builder-dev-fold.template.test.ts +113 -0
- package/src/builder.yaml +798 -92
- package/src/business-model-catalog-description.test.ts +47 -0
- package/src/business-model-grp.template.test.ts +214 -0
- package/src/business-model.yaml +598 -0
- package/src/catalog-safety.test.ts +198 -0
- package/src/catalog.template-items.test.ts +75 -0
- package/src/catalog.ts +85 -2
- package/src/civic-france.yaml +102 -0
- package/src/concept-guard.test.ts +28 -8
- package/src/concept-guard.ts +2 -2
- package/src/content-creation.template.test.ts +52 -0
- package/src/content-creation.yaml +192 -0
- package/src/content-os.edit-loop.template.test.ts +210 -0
- package/src/content-os.publish-due-posts.template.test.ts +261 -0
- package/src/content-os.template.test.ts +322 -0
- package/src/content-os.video-takes.template.test.ts +338 -0
- package/src/content-os.work-a-take.template.test.ts +125 -0
- package/src/content-os.yaml +1830 -39
- package/src/content-studio-retired.template.test.ts +82 -0
- package/src/corpus-flow-validity.test.ts +4 -5
- package/src/crm-absorbs-outreach.template.test.ts +117 -0
- package/src/crm-build-plan.template.test.ts +128 -0
- package/src/crm-lists.template.test.ts +96 -0
- package/src/crm-messages-cadence.template.test.ts +164 -0
- package/src/crm-one-send-door.template.test.ts +65 -0
- package/src/crm.yaml +1456 -181
- package/src/decisions-wave.template.test.ts +186 -0
- package/src/define.ts +130 -17
- package/src/design-to-implementation.template.test.ts +94 -0
- package/src/dist-freshness.test.ts +97 -0
- package/src/domain-template-type.test.ts +75 -0
- package/src/ecosystem.yaml +27 -66
- package/src/enterprise-os.yaml +10 -134
- package/src/facecam-edit.audio-studio.test.ts +188 -0
- package/src/fal-generate-image.test.ts +200 -0
- package/src/fal-media-lab-pod-files.test.ts +180 -0
- package/src/finance.yaml +254 -1
- package/src/foundation.yaml +130 -28
- package/src/frame-host-modules.d.mts +6 -0
- package/src/frame-host-modules.mjs +58 -0
- package/src/generate.mjs +9 -3
- package/src/github-merge.test.ts +210 -0
- package/src/grants.yaml +57 -17
- package/src/hr.yaml +39 -6
- package/src/index.ts +36 -1
- package/src/intent.test.ts +161 -0
- package/src/internal-runbook.yaml +19 -1
- package/src/jev.test.ts +356 -0
- package/src/legal.yaml +33 -1
- package/src/life-os.yaml +65 -240
- package/src/lifecycle-coverage.test.ts +124 -0
- package/src/lifecycle-declarations.test.ts +252 -0
- package/src/lifecycle-presets.test.ts +468 -0
- package/src/lifecycle-presets.ts +494 -0
- package/src/lifecycle-stuck.test.ts +46 -0
- package/src/lifecycle-waits.parity.test.ts +319 -0
- package/src/marketing.yaml +108 -4
- package/src/meetings-knowledge.yaml +18 -0
- package/src/networking.yaml +23 -2
- package/src/operations.yaml +304 -4
- package/src/pipeline-views.template.test.ts +88 -0
- package/src/playbook-goal-placeholders.test.ts +57 -0
- package/src/postiz-contract.test.ts +134 -0
- package/src/presentation-declarations.test.ts +174 -0
- package/src/process-contract.validate.test.ts +352 -0
- package/src/project-management.yaml +39 -740
- package/src/publish-body.d.mts +29 -0
- package/src/publish-body.mjs +74 -0
- package/src/publish-body.test.ts +74 -0
- package/src/publish-official-lib.test.ts +176 -0
- package/src/replicate-rvm.test.ts +504 -0
- package/src/research-base.yaml +119 -20
- package/src/research-decision.yaml +60 -3
- package/src/reserved-profile-slugs.test.ts +83 -0
- package/src/retired-goal-kinds.test.ts +58 -0
- package/src/role-reference-and-step-domain.test.ts +192 -0
- package/src/seed-obligation.test.ts +176 -0
- package/src/send-message-maps.test.ts +227 -0
- package/src/shared-role-ssot.test.ts +42 -1
- package/src/ship-a-change.template.test.ts +177 -0
- package/src/skills.test.ts +170 -0
- package/src/social.yaml +85 -27
- package/src/space-packages.freshness.test.ts +97 -0
- package/src/space-packages.generated.ts +40 -0
- package/src/space-packages.test.ts +125 -0
- package/src/space-packages.ts +73 -0
- package/src/start.test.ts +188 -0
- package/src/task-intents.test.ts +293 -0
- package/src/templates.ts +21639 -21522
- package/src/types.ts +443 -60
- package/src/unipile-publish-post.test.ts +162 -0
- package/src/validate-flow.drift.test.ts +2 -3
- package/src/validate-flow.ts +32 -2
- package/src/validate-onboarding.test.ts +59 -0
- package/src/validate.test.ts +156 -3
- package/src/validate.ts +1384 -7
- package/src/wire-core.ts +231 -0
- package/src/wire.test.ts +159 -0
- package/src/wire.ts +107 -0
- package/src/__fixtures__/report-automation-flow.json +0 -393
- package/src/agent-fleet.yaml +0 -1645
- package/src/business-developer.template.test.ts +0 -351
- package/src/business-developer.yaml +0 -860
- package/src/content-studio.yaml +0 -361
- package/src/dev-dashboard.yaml +0 -990
- package/src/outreach-comms.yaml +0 -486
- package/src/the-arch.yaml +0 -201
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
{
|
|
2
|
+
"apiVersion": "synap/v1",
|
|
3
|
+
"kind": "capability",
|
|
4
|
+
"meta": {
|
|
5
|
+
"slug": "github-merge",
|
|
6
|
+
"name": "GitHub — merge a pull request",
|
|
7
|
+
"description": "Merge a pull request from an approval, on a token the pod holds: pinned to the head the person approved, and refused when the head moved, a required check is not green, or the repository is not in the list. One verb, github_merge_pr. Separate from github-read, which never writes.",
|
|
8
|
+
"tags": [
|
|
9
|
+
"capability"
|
|
10
|
+
],
|
|
11
|
+
"isPublic": true,
|
|
12
|
+
"syncByDefault": false
|
|
13
|
+
},
|
|
14
|
+
"spec": {
|
|
15
|
+
"key": "github-merge",
|
|
16
|
+
"name": "GitHub — merge",
|
|
17
|
+
"description": "Merges ONE pull request on GitHub (https://api.github.com) when the person approves it: github_merge_pr {repo, pr, expectedHeadSha, base, method}. Before merging it reads the pull request and its checks and refuses, with the reason, when the repository is not in this install's list, the PR is not open or targets another base, its head is not the approved one, or a required check is missing or not green (any failed check refuses too). The merge itself is pinned to the approved sha, so GitHub refuses it if the head moved meanwhile. Ship steps run on the pod, not on an agent's machine (decision 4d6d7922).",
|
|
18
|
+
"params": [
|
|
19
|
+
{
|
|
20
|
+
"name": "githubToken",
|
|
21
|
+
"label": "GitHub token (merge)",
|
|
22
|
+
"type": "password",
|
|
23
|
+
"required": true,
|
|
24
|
+
"description": "A fine-grained GitHub token. Repository access = ONLY the repositories listed below (never one that lands through a merge queue). Permissions = Pull requests: Read and write, Contents: Read and write, Checks: Read, Commit statuses: Read. Stored encrypted in YOUR vault (not pod-wide); only the person who installed it can approve a merge through it."
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"name": "repos",
|
|
28
|
+
"label": "Repositories it may merge",
|
|
29
|
+
"type": "text",
|
|
30
|
+
"required": true,
|
|
31
|
+
"description": "Comma-separated owner/repo, e.g. Synap-core/app-bases,Synap-core/browser. A repository not listed is refused before anything is read."
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"name": "requiredChecks",
|
|
35
|
+
"label": "Required checks",
|
|
36
|
+
"type": "text",
|
|
37
|
+
"required": true,
|
|
38
|
+
"default": "synap/devbox",
|
|
39
|
+
"description": "Comma-separated check or commit-status names that must be green on the head, e.g. synap/devbox. Empty is refused: a merge with nothing vouching for the head never runs."
|
|
40
|
+
}
|
|
41
|
+
],
|
|
42
|
+
"vault": [
|
|
43
|
+
{
|
|
44
|
+
"ref": "githubMergeTokenSecret",
|
|
45
|
+
"name": "{{name}} GitHub token (merge)",
|
|
46
|
+
"value": "{{githubToken}}",
|
|
47
|
+
"type": "api_key",
|
|
48
|
+
"service": "github",
|
|
49
|
+
"description": "The installer's own GitHub token with write access to the listed repositories, used only by github_merge_pr. Not pod-wide."
|
|
50
|
+
}
|
|
51
|
+
],
|
|
52
|
+
"tools": [
|
|
53
|
+
{
|
|
54
|
+
"name": "github_merge_api",
|
|
55
|
+
"kind": "api",
|
|
56
|
+
"description": "Authenticated access to the GitHub REST API for github_merge_pr: it reads a pull request and its checks, then merges it. `config.baseUrl` pins api.github.com; `config.auth` injects the vaulted token as `Authorization: Bearer <token>`.",
|
|
57
|
+
"credentialRef": "githubMergeTokenSecret",
|
|
58
|
+
"executor": "is-agent",
|
|
59
|
+
"config": {
|
|
60
|
+
"baseUrl": "https://api.github.com",
|
|
61
|
+
"auth": {
|
|
62
|
+
"in": "header",
|
|
63
|
+
"name": "Authorization",
|
|
64
|
+
"prefix": "Bearer "
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
],
|
|
69
|
+
"skills": [
|
|
70
|
+
{
|
|
71
|
+
"name": "github_merge_pr",
|
|
72
|
+
"intent": "run_external_job",
|
|
73
|
+
"kind": "code",
|
|
74
|
+
"scope": "pod",
|
|
75
|
+
"description": "Merge one pull request, pinned to the head the person approves. Returns { merged, mergeCommitSha, url, detail }. Refuses with the reason (nothing merged) when the repository is not in this install's list, the PR is not open, a draft, or targets another base, its head is not expectedHeadSha, or a required check is missing or not green (any failed check refuses too). Args: { repo: string (owner/name), pr: number, expectedHeadSha: string (the full 40-character head sha), base: string (the branch it lands on), method?: 'merge' | 'squash' | 'rebase' (default merge) }. The proposal reads \"Merge <repo>#<pr> into <base> at <sha8>\".",
|
|
76
|
+
"requires": [
|
|
77
|
+
"github_merge_api"
|
|
78
|
+
],
|
|
79
|
+
"executionMode": "sync",
|
|
80
|
+
"timeoutSeconds": 60,
|
|
81
|
+
"parameters": {
|
|
82
|
+
"repo": "string",
|
|
83
|
+
"pr": "number",
|
|
84
|
+
"expectedHeadSha": "string",
|
|
85
|
+
"base": "string",
|
|
86
|
+
"method": "string?"
|
|
87
|
+
},
|
|
88
|
+
"code": "// MERGE ONE PULL REQUEST, pinned to the head the person approved (decision\n// 4d6d7922, D1-D3). Executor wraps this body as (async (args, context) => {\n// <code> }). It refuses, with the reason, before it merges anything:\n// 1. the repo is not in this install's merge list (backend lands through its\n// merge queue, never this verb);\n// 2. the PR is not open, is a draft, or targets another base;\n// 3. its head moved since the approval (expectedHeadSha);\n// 4. a required check is missing or not green, or any check failed.\n// Then it merges with GitHub's own sha pin (409 when the head moved meanwhile).\n// A failed read THROWS: nothing is ever merged on a guess.\nconst ALLOWED = '{{repos}}'\n .split(',')\n .map((s) => s.trim().replace(/^https?:\\/\\/(?:www\\.)?github\\.com\\//i, '').replace(/\\/+$/, '').toLowerCase())\n .filter(Boolean);\nconst REQUIRED = '{{requiredChecks}}'.split(',').map((s) => s.trim()).filter(Boolean);\nconst H = { Accept: 'application/vnd.github+json', 'X-GitHub-Api-Version': '2022-11-28', 'User-Agent': 'synap-pod-github-merge' };\n\nconst m = /^([A-Za-z0-9_.-]+)\\/([A-Za-z0-9_.-]+)$/.exec(String(args?.repo ?? '').trim());\nif (!m) throw new Error('github_merge_pr: repo must be owner/name, e.g. Synap-core/app-bases.');\nconst [, owner, name] = m;\nconst full = `${owner}/${name}`;\nif (!ALLOWED.includes(full.toLowerCase())) {\n throw new Error(`github_merge_pr: ${full} is not in this pod's merge list (${ALLOWED.join(', ') || 'empty'}). Add it to the capability's repositories, or land it another way.`);\n}\nconst pr = Number(args?.pr);\nif (!Number.isInteger(pr) || pr <= 0) throw new Error('github_merge_pr: pr must be the pull request number.');\nconst sha = String(args?.expectedHeadSha ?? '').trim().toLowerCase();\nif (!/^[0-9a-f]{40}$/.test(sha)) throw new Error('github_merge_pr: expectedHeadSha must be the full 40-character head commit the person approves.');\nconst base = String(args?.base ?? '').trim();\nif (!base) throw new Error('github_merge_pr: base must name the branch it lands on (e.g. main).');\nconst method = String(args?.method ?? 'merge').trim() || 'merge';\nif (!['merge', 'squash', 'rebase'].includes(method)) throw new Error('github_merge_pr: method is merge, squash or rebase.');\nif (REQUIRED.length === 0) {\n throw new Error('github_merge_pr: no required checks are configured, so nothing would vouch for this head. Set the capability\\'s required checks.');\n}\n\nconst get = async (path) => {\n const r = await callProvider('github_merge_api', 'GET', path, undefined, { headers: H });\n if (!r || r.status < 200 || r.status >= 300) {\n throw new Error(`github_merge_pr: GitHub answered HTTP ${r?.status ?? '?'} for ${path} (token missing, expired, or without access to ${full}?).`);\n }\n return r.body ?? {};\n};\n\nconst p = await get(`/repos/${owner}/${name}/pulls/${pr}`);\nconst where = `${full}#${pr}`;\nif (p.merged === true) throw new Error(`github_merge_pr: ${where} is already merged.`);\nif (p.state !== 'open') throw new Error(`github_merge_pr: ${where} is ${p.state ?? 'not open'}.`);\nif (p.draft === true) throw new Error(`github_merge_pr: ${where} is a draft.`);\nconst head = String(p.head?.sha ?? '').toLowerCase();\nif (head !== sha) {\n throw new Error(`github_merge_pr: ${where} moved since it was approved: approved ${sha.slice(0, 8)}, head is now ${head.slice(0, 8) || '?'}. Ask again for the new head.`);\n}\nif (p.base?.ref !== base) throw new Error(`github_merge_pr: ${where} targets ${p.base?.ref ?? '?'}, not ${base}.`);\n\n// The head's checks: each check run (latest per name) and each commit status\n// (GitHub's combined status keeps the latest per context).\nconst state = new Map();\nconst runs = await get(`/repos/${owner}/${name}/commits/${sha}/check-runs?per_page=100`);\nconst latest = new Map();\nfor (const r of runs.check_runs ?? []) {\n const prev = latest.get(r.name);\n if (!prev || Number(r.id) > Number(prev.id)) latest.set(r.name, r);\n}\nfor (const [n, r] of latest) {\n state.set(n, r.status !== 'completed' ? 'pending' : ['success', 'neutral', 'skipped'].includes(r.conclusion) ? 'green' : 'failed');\n}\nconst combined = await get(`/repos/${owner}/${name}/commits/${sha}/status`);\nfor (const s of combined.statuses ?? []) {\n state.set(s.context, s.state === 'success' ? 'green' : s.state === 'pending' ? 'pending' : 'failed');\n}\nconst missing = REQUIRED.filter((n) => !state.has(n));\nconst notGreen = REQUIRED.filter((n) => state.has(n) && state.get(n) !== 'green');\nconst failed = [...state].filter(([, v]) => v === 'failed').map(([n]) => n);\nif (missing.length || notGreen.length || failed.length) {\n const parts = [];\n if (missing.length) parts.push(`missing: ${missing.join(', ')}`);\n if (notGreen.length) parts.push(`not green: ${notGreen.map((n) => `${n} (${state.get(n)})`).join(', ')}`);\n if (failed.length) parts.push(`failed: ${failed.join(', ')}`);\n throw new Error(`github_merge_pr: ${where} at ${sha.slice(0, 8)} is not ready to merge (${parts.join('; ')}).`);\n}\n\nconst res = await callProvider('github_merge_api', 'PUT', `/repos/${owner}/${name}/pulls/${pr}/merge`, { sha, merge_method: method }, { headers: H });\nif (!res || res.status !== 200 || res.body?.merged !== true) {\n throw new Error(`github_merge_pr: GitHub refused to merge ${where} (HTTP ${res?.status ?? '?'}: ${res?.body?.message ?? 'no reason given'}).`);\n}\nconst mergeSha = String(res.body.sha ?? '');\nreturn {\n merged: true,\n mergeCommitSha: mergeSha || null,\n url: p.html_url ?? `https://github.com/${full}/pull/${pr}`,\n detail: `Merged ${where} at ${sha.slice(0, 8)} into ${base} as ${mergeSha.slice(0, 8) || '?'}.`,\n};",
|
|
89
|
+
"metadata": {
|
|
90
|
+
"verbType": "write"
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
],
|
|
94
|
+
"provides": [
|
|
95
|
+
"run_external_job"
|
|
96
|
+
]
|
|
97
|
+
},
|
|
98
|
+
"dependencies": []
|
|
99
|
+
}
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
{
|
|
2
|
+
"apiVersion": "synap/v1",
|
|
3
|
+
"kind": "capability",
|
|
4
|
+
"meta": {
|
|
5
|
+
"slug": "github-read",
|
|
6
|
+
"name": "GitHub — pull requests, checks and releases (read)",
|
|
7
|
+
"description": "Read your GitHub pull requests, their checks, the latest release and how a branch compares with main, on your own token. Read-only: five GET verbs, no write. \"Merged\" and \"checks green\" are read from GitHub, not taken from a report.",
|
|
8
|
+
"tags": [
|
|
9
|
+
"capability"
|
|
10
|
+
],
|
|
11
|
+
"isPublic": true,
|
|
12
|
+
"syncByDefault": true
|
|
13
|
+
},
|
|
14
|
+
"spec": {
|
|
15
|
+
"key": "github-read",
|
|
16
|
+
"name": "GitHub — read",
|
|
17
|
+
"description": "Read-only GitHub REST API (https://api.github.com) on the person's OWN token: one pull request (state, merged, merge commit, head sha), the check runs on a commit, the latest release, a compare of a branch or sha against a base, and one criterion CHECK (github_check_pr_merged) a session criterion of kind capability runs to grade \"merged\" itself. Bring your own: the token is yours, stored in your vault (not pod-wide), and nothing here goes through a Synap-run proxy. An agent reads through it only with a vault grant you give it. Every verb is a fixed GET with typed parameters (owner, repo, number, ref, or the PR url of the outcome a check grades), never a passthrough; nothing writes to GitHub. TOKEN: a fine-grained personal access token limited to the repositories you want readable, with read access to Pull requests, Contents and Checks (Metadata is implied), or a classic token (no scope for public repositories, `repo` for private ones). A missing, expired or under-scoped token fails loudly (401/403/404 from GitHub), never reads as 'not merged' or 'no checks'. Rate limit: 5,000 requests an hour per token.",
|
|
18
|
+
"params": [
|
|
19
|
+
{
|
|
20
|
+
"name": "githubToken",
|
|
21
|
+
"label": "GitHub token (read-only)",
|
|
22
|
+
"type": "text",
|
|
23
|
+
"required": true,
|
|
24
|
+
"description": "A GitHub personal access token, read-only. Fine-grained (recommended): Repository access = the repositories you want the pod to read; Permissions = Pull requests: Read, Contents: Read, Checks: Read. Classic: no scope for public repositories, `repo` for private ones. Stored encrypted in YOUR vault (not pod-wide) and sent as `Authorization: Bearer <token>`; never persisted in this template."
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
"vault": [
|
|
28
|
+
{
|
|
29
|
+
"ref": "githubReadTokenSecret",
|
|
30
|
+
"name": "{{name}} GitHub token (read-only)",
|
|
31
|
+
"value": "{{githubToken}}",
|
|
32
|
+
"type": "api_key",
|
|
33
|
+
"service": "github",
|
|
34
|
+
"description": "The person's own read-only GitHub token, used by every github_* verb of this capability. Not pod-wide: an agent needs a vault grant from its owner to read through it."
|
|
35
|
+
}
|
|
36
|
+
],
|
|
37
|
+
"tools": [
|
|
38
|
+
{
|
|
39
|
+
"name": "github_api",
|
|
40
|
+
"kind": "api",
|
|
41
|
+
"description": "Authenticated READ access to the GitHub REST API. `config.baseUrl` pins api.github.com; `config.auth` injects the vaulted token as `Authorization: Bearer <token>`. Only this capability's GET verbs call it.",
|
|
42
|
+
"credentialRef": "githubReadTokenSecret",
|
|
43
|
+
"executor": "is-agent",
|
|
44
|
+
"config": {
|
|
45
|
+
"baseUrl": "https://api.github.com",
|
|
46
|
+
"auth": {
|
|
47
|
+
"in": "header",
|
|
48
|
+
"name": "Authorization",
|
|
49
|
+
"prefix": "Bearer "
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
],
|
|
54
|
+
"skills": [
|
|
55
|
+
{
|
|
56
|
+
"name": "github_pr_get",
|
|
57
|
+
"intent": "fetch_record",
|
|
58
|
+
"kind": "declarative",
|
|
59
|
+
"scope": "pod",
|
|
60
|
+
"description": "One pull request: is it open, closed or merged, and with which commit. Use it to check \"merged\" yourself instead of trusting a report. Returns { number, title, state ('open' | 'closed'), draft, merged (boolean), mergedAt, mergeCommitSha, mergeableState, headSha, headRef, baseRef, url, updatedAt }. headSha is what github_pr_checks reads. Args: { owner: string (e.g. \"Synap-core\"), repo: string (e.g. \"backend\"), number: number }.",
|
|
61
|
+
"requires": [
|
|
62
|
+
"github_api"
|
|
63
|
+
],
|
|
64
|
+
"executionMode": "sync",
|
|
65
|
+
"timeoutSeconds": 30,
|
|
66
|
+
"parameters": {
|
|
67
|
+
"owner": "string",
|
|
68
|
+
"repo": "string",
|
|
69
|
+
"number": "number"
|
|
70
|
+
},
|
|
71
|
+
"providerSpec": {
|
|
72
|
+
"tool": "github_api",
|
|
73
|
+
"method": "GET",
|
|
74
|
+
"pathTemplate": "/repos/{{owner}}/{{repo}}/pulls/{{number}}",
|
|
75
|
+
"paramMapping": {
|
|
76
|
+
"owner": {
|
|
77
|
+
"required": true,
|
|
78
|
+
"encode": "uri"
|
|
79
|
+
},
|
|
80
|
+
"repo": {
|
|
81
|
+
"required": true,
|
|
82
|
+
"encode": "uri"
|
|
83
|
+
},
|
|
84
|
+
"number": {
|
|
85
|
+
"required": true,
|
|
86
|
+
"encode": "uri"
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
"responseShape": {
|
|
90
|
+
"scalar": {
|
|
91
|
+
"number": "number",
|
|
92
|
+
"title": "title",
|
|
93
|
+
"state": "state",
|
|
94
|
+
"draft": "draft",
|
|
95
|
+
"merged": "merged",
|
|
96
|
+
"mergedAt": "merged_at",
|
|
97
|
+
"mergeCommitSha": "merge_commit_sha",
|
|
98
|
+
"mergeableState": "mergeable_state",
|
|
99
|
+
"headSha": "head.sha",
|
|
100
|
+
"headRef": "head.ref",
|
|
101
|
+
"baseRef": "base.ref",
|
|
102
|
+
"url": "html_url",
|
|
103
|
+
"updatedAt": "updated_at"
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
"headers": {
|
|
107
|
+
"Accept": "application/vnd.github+json",
|
|
108
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
109
|
+
"User-Agent": "synap-pod-github-read"
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
"metadata": {
|
|
113
|
+
"readOnly": true
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
"name": "github_pr_checks",
|
|
118
|
+
"intent": "list_records",
|
|
119
|
+
"kind": "declarative",
|
|
120
|
+
"scope": "pod",
|
|
121
|
+
"description": "The check runs on one commit — what CI says about a pull request's head. Pass the pull request's headSha (from github_pr_get) or a branch name as `ref`. Each run has { name, status ('queued' | 'in_progress' | 'completed'), conclusion ('success' | 'failure' | 'neutral' | 'cancelled' | 'skipped' | 'timed_out' | 'action_required' | null while running), url, startedAt, completedAt, app }. The checks are green only when every run is completed with success, neutral or skipped; a run still queued or in progress is not green yet. Returns { total, count, checkRuns }. Args: { owner: string, repo: string, ref: string (a sha or a branch), perPage?: number (default 100, max 100) }.",
|
|
122
|
+
"requires": [
|
|
123
|
+
"github_api"
|
|
124
|
+
],
|
|
125
|
+
"executionMode": "sync",
|
|
126
|
+
"timeoutSeconds": 30,
|
|
127
|
+
"parameters": {
|
|
128
|
+
"owner": "string",
|
|
129
|
+
"repo": "string",
|
|
130
|
+
"ref": "string",
|
|
131
|
+
"perPage": "number?"
|
|
132
|
+
},
|
|
133
|
+
"providerSpec": {
|
|
134
|
+
"tool": "github_api",
|
|
135
|
+
"method": "GET",
|
|
136
|
+
"pathTemplate": "/repos/{{owner}}/{{repo}}/commits/{{ref}}/check-runs",
|
|
137
|
+
"query": {
|
|
138
|
+
"per_page": "{{perPage}}"
|
|
139
|
+
},
|
|
140
|
+
"paramMapping": {
|
|
141
|
+
"owner": {
|
|
142
|
+
"required": true,
|
|
143
|
+
"encode": "uri"
|
|
144
|
+
},
|
|
145
|
+
"repo": {
|
|
146
|
+
"required": true,
|
|
147
|
+
"encode": "uri"
|
|
148
|
+
},
|
|
149
|
+
"ref": {
|
|
150
|
+
"required": true,
|
|
151
|
+
"encode": "uri"
|
|
152
|
+
},
|
|
153
|
+
"perPage": {
|
|
154
|
+
"default": 100,
|
|
155
|
+
"clampMin": 1,
|
|
156
|
+
"clampMax": 100
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
"responseShape": {
|
|
160
|
+
"collectionPath": "check_runs",
|
|
161
|
+
"collectionAs": "checkRuns",
|
|
162
|
+
"item": {
|
|
163
|
+
"name": "name",
|
|
164
|
+
"status": "status",
|
|
165
|
+
"conclusion": "conclusion",
|
|
166
|
+
"url": "html_url",
|
|
167
|
+
"startedAt": "started_at",
|
|
168
|
+
"completedAt": "completed_at",
|
|
169
|
+
"app": "app.slug"
|
|
170
|
+
},
|
|
171
|
+
"scalar": {
|
|
172
|
+
"total": "total_count",
|
|
173
|
+
"count": "@count"
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
"headers": {
|
|
177
|
+
"Accept": "application/vnd.github+json",
|
|
178
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
179
|
+
"User-Agent": "synap-pod-github-read"
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
"metadata": {
|
|
183
|
+
"readOnly": true
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
"name": "github_release_latest",
|
|
188
|
+
"intent": "fetch_record",
|
|
189
|
+
"kind": "declarative",
|
|
190
|
+
"scope": "pod",
|
|
191
|
+
"description": "The latest published release of a repository (drafts and pre-releases are not \"latest\"). Use it to tell which version is out. Returns { tag, name, publishedAt, targetCommitish, url }. A repository with no release answers 404 (an error, never \"no release\" read as an empty result). Args: { owner: string, repo: string }.",
|
|
192
|
+
"requires": [
|
|
193
|
+
"github_api"
|
|
194
|
+
],
|
|
195
|
+
"executionMode": "sync",
|
|
196
|
+
"timeoutSeconds": 30,
|
|
197
|
+
"parameters": {
|
|
198
|
+
"owner": "string",
|
|
199
|
+
"repo": "string"
|
|
200
|
+
},
|
|
201
|
+
"providerSpec": {
|
|
202
|
+
"tool": "github_api",
|
|
203
|
+
"method": "GET",
|
|
204
|
+
"pathTemplate": "/repos/{{owner}}/{{repo}}/releases/latest",
|
|
205
|
+
"paramMapping": {
|
|
206
|
+
"owner": {
|
|
207
|
+
"required": true,
|
|
208
|
+
"encode": "uri"
|
|
209
|
+
},
|
|
210
|
+
"repo": {
|
|
211
|
+
"required": true,
|
|
212
|
+
"encode": "uri"
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
"responseShape": {
|
|
216
|
+
"scalar": {
|
|
217
|
+
"tag": "tag_name",
|
|
218
|
+
"name": "name",
|
|
219
|
+
"publishedAt": "published_at",
|
|
220
|
+
"targetCommitish": "target_commitish",
|
|
221
|
+
"url": "html_url"
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
"headers": {
|
|
225
|
+
"Accept": "application/vnd.github+json",
|
|
226
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
227
|
+
"User-Agent": "synap-pod-github-read"
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
"metadata": {
|
|
231
|
+
"readOnly": true
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
"name": "github_compare",
|
|
236
|
+
"intent": "fetch_record",
|
|
237
|
+
"kind": "declarative",
|
|
238
|
+
"scope": "pod",
|
|
239
|
+
"description": "How a branch or commit stands against a base: is it behind main, and by how much; does a deployed sha contain a merge commit (compare base = the merge commit, head = the deployed sha: 'identical' or 'ahead' means it does). Returns { status ('identical' | 'ahead' | 'behind' | 'diverged'), aheadBy, behindBy, totalCommits, mergeBaseSha, url }. Args: { owner: string, repo: string, head: string (a branch or sha), base?: string (default \"main\") }.",
|
|
240
|
+
"requires": [
|
|
241
|
+
"github_api"
|
|
242
|
+
],
|
|
243
|
+
"executionMode": "sync",
|
|
244
|
+
"timeoutSeconds": 30,
|
|
245
|
+
"parameters": {
|
|
246
|
+
"owner": "string",
|
|
247
|
+
"repo": "string",
|
|
248
|
+
"head": "string",
|
|
249
|
+
"base": "string?"
|
|
250
|
+
},
|
|
251
|
+
"providerSpec": {
|
|
252
|
+
"tool": "github_api",
|
|
253
|
+
"method": "GET",
|
|
254
|
+
"pathTemplate": "/repos/{{owner}}/{{repo}}/compare/{{base}}...{{head}}",
|
|
255
|
+
"query": {
|
|
256
|
+
"per_page": "1"
|
|
257
|
+
},
|
|
258
|
+
"paramMapping": {
|
|
259
|
+
"owner": {
|
|
260
|
+
"required": true,
|
|
261
|
+
"encode": "uri"
|
|
262
|
+
},
|
|
263
|
+
"repo": {
|
|
264
|
+
"required": true,
|
|
265
|
+
"encode": "uri"
|
|
266
|
+
},
|
|
267
|
+
"head": {
|
|
268
|
+
"required": true,
|
|
269
|
+
"encode": "uri"
|
|
270
|
+
},
|
|
271
|
+
"base": {
|
|
272
|
+
"default": "main",
|
|
273
|
+
"encode": "uri"
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
"responseShape": {
|
|
277
|
+
"scalar": {
|
|
278
|
+
"status": "status",
|
|
279
|
+
"aheadBy": "ahead_by",
|
|
280
|
+
"behindBy": "behind_by",
|
|
281
|
+
"totalCommits": "total_commits",
|
|
282
|
+
"mergeBaseSha": "merge_base_commit.sha",
|
|
283
|
+
"url": "html_url"
|
|
284
|
+
}
|
|
285
|
+
},
|
|
286
|
+
"headers": {
|
|
287
|
+
"Accept": "application/vnd.github+json",
|
|
288
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
289
|
+
"User-Agent": "synap-pod-github-read"
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
"metadata": {
|
|
293
|
+
"readOnly": true
|
|
294
|
+
}
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
"name": "github_check_pr_merged",
|
|
298
|
+
"intent": "fetch_record",
|
|
299
|
+
"kind": "code",
|
|
300
|
+
"scope": "pod",
|
|
301
|
+
"description": "A criterion CHECK: is a pull request merged? Returns { passed, detail, merged, state, mergeCommitSha, url } — `passed` is what a session criterion reads. As a capability criterion (check.kind capability, capability github_check_pr_merged), key the criterion like the outcome that holds the PR (pr-<repo>-<n>, ref = the PR url): the evaluator hands that outcome over and the url comes from its ref. Called by hand, pass { url }. A failed read (no token, no access, GitHub down) is an error, so the check reads unmeasured, never 'not merged'. Args: { outcome?: object (from the evaluator), url?: string (a GitHub pull request url) }.",
|
|
302
|
+
"requires": [
|
|
303
|
+
"github_api"
|
|
304
|
+
],
|
|
305
|
+
"executionMode": "sync",
|
|
306
|
+
"timeoutSeconds": 30,
|
|
307
|
+
"parameters": {
|
|
308
|
+
"outcome": "object?",
|
|
309
|
+
"url": "string?",
|
|
310
|
+
"sessionId": "string?",
|
|
311
|
+
"criterionKey": "string?"
|
|
312
|
+
},
|
|
313
|
+
"code": "// A CRITERION CHECK: is this pull request merged? Executor wraps this body as\n// (async (args, context) => { <code> }). The session evaluator calls it with\n// { sessionId, criterionKey, outcome: {key, kind, label, ref?} } — the session's\n// outcome slot with the criterion's key — so key the criterion like the PR's\n// outcome (pr-<repo>-<n>) and the PR url comes from outcome.ref.url. A person\n// or agent may pass `url` directly instead.\n// callProvider('github_api', …) resolves this capability's tool by NAME and\n// injects the vaulted token as `Authorization: Bearer <token>` (config.auth).\n// A failed read THROWS (the check reads \"unmeasured\"), never \"not merged\".\nconst url = String(args?.outcome?.ref?.url ?? args?.url ?? '').trim();\nconst m = /^https?:\\/\\/(?:www\\.)?github\\.com\\/([A-Za-z0-9_.-]+)\\/([A-Za-z0-9_.-]+)\\/pull\\/(\\d+)(?:[\\/?#].*)?$/i.exec(url);\nif (!m) {\n throw new Error('github_check_pr_merged: no GitHub pull request url. Key the criterion like the outcome that holds the PR (pr-<repo>-<n>, ref = its url), or pass { url }.');\n}\nconst [, owner, repo, number] = m;\nconst res = await callProvider('github_api', 'GET', `/repos/${owner}/${repo}/pulls/${number}`, undefined, {\n headers: { Accept: 'application/vnd.github+json', 'X-GitHub-Api-Version': '2022-11-28', 'User-Agent': 'synap-pod-github-read' },\n});\nif (!res || res.status < 200 || res.status >= 300) {\n throw new Error(`github_check_pr_merged: GitHub answered HTTP ${res?.status ?? '?'} for ${owner}/${repo}#${number} (token missing, expired or without read access to this repository?)`);\n}\nconst pr = res.body ?? {};\nconst merged = pr.merged === true;\nconst detail = merged\n ? `${owner}/${repo}#${number} merged ${pr.merged_at ?? ''} as ${pr.merge_commit_sha ?? '?'}: ${pr.html_url ?? url}`\n : `${owner}/${repo}#${number} is not merged (state ${pr.state ?? '?'}${pr.draft ? ', draft' : ''}): ${pr.html_url ?? url}`;\nreturn { passed: merged, detail, merged, state: pr.state ?? null, mergeCommitSha: pr.merge_commit_sha ?? null, url: pr.html_url ?? url };",
|
|
314
|
+
"metadata": {
|
|
315
|
+
"readOnly": true
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
],
|
|
319
|
+
"provides": [
|
|
320
|
+
"fetch_record",
|
|
321
|
+
"list_records"
|
|
322
|
+
]
|
|
323
|
+
},
|
|
324
|
+
"dependencies": []
|
|
325
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{
|
|
2
|
+
"apiVersion": "synap/v1",
|
|
3
|
+
"kind": "capability",
|
|
4
|
+
"meta": {
|
|
5
|
+
"slug": "governance-tighten-recommender",
|
|
6
|
+
"name": "Governance — Tighten Recommender",
|
|
7
|
+
"description": "Reviews the agent proposals you rejected each day. When you keep rejecting the same kind of change, it files a review item suggesting that change should always need approval. Nothing changes until you approve.",
|
|
8
|
+
"tags": [
|
|
9
|
+
"capability"
|
|
10
|
+
],
|
|
11
|
+
"isPublic": true,
|
|
12
|
+
"syncByDefault": true
|
|
13
|
+
},
|
|
14
|
+
"spec": {
|
|
15
|
+
"key": "governance-tighten-recommender",
|
|
16
|
+
"name": "Governance Tighten Recommender",
|
|
17
|
+
"description": "Config-first daily governance calibration: once a day, scan recent REJECTED agent proposals, cluster them by shape, and — for any write-motif the humans reject consistently (a conservative floor: at least 5 rejections of that shape AND a high reject-rate) — file a pending governance.tighten_lane proposal. Approving one inserts a governance_rules row with verdict:'propose', pinning that agent motif back to REVIEW. This is the mirror image of the trusted-lane WIDEN scanner (packages/jobs/src/workers/governance-lane-scanner.ts): widen PROPOSES opening an auto-approve lane once an agent earns trust; tighten PROPOSES closing one when the humans keep saying no. Governed + never silent: the only side effect is filing PENDING review items — nothing tightens without a human approval. Option B packaging: a governed builtin verb (governance.recommend_tighten) invoked by a cron automation, so the whole loop is stored, user-editable config rather than a hardcoded worker.",
|
|
18
|
+
"params": [],
|
|
19
|
+
"vault": [],
|
|
20
|
+
"tools": [],
|
|
21
|
+
"skills": [],
|
|
22
|
+
"provides": [],
|
|
23
|
+
"playbooks": [],
|
|
24
|
+
"automations": [
|
|
25
|
+
{
|
|
26
|
+
"name": "Governance Tighten Recommender — Daily",
|
|
27
|
+
"description": "Every day at 04:30: run governance.recommend_tighten, which scans each agent's recent rejected proposals and files a governance.tighten_lane review item for any consistently-rejected write-motif. Seeded ACTIVE so the cron schedules (a draft never gets a nextRunAt). The verb is read-only w.r.t. graph data (it only files review items), so the capability node auto-runs unattended.",
|
|
28
|
+
"triggerType": "cron",
|
|
29
|
+
"triggerConfig": {
|
|
30
|
+
"expression": "30 4 * * *"
|
|
31
|
+
},
|
|
32
|
+
"status": "active",
|
|
33
|
+
"metadata": {
|
|
34
|
+
"kind": "calibration-recommender",
|
|
35
|
+
"mirrors": "governance-lane-scanner"
|
|
36
|
+
},
|
|
37
|
+
"flowDefinition": {
|
|
38
|
+
"nodes": [
|
|
39
|
+
{
|
|
40
|
+
"id": "trigger",
|
|
41
|
+
"type": "trigger",
|
|
42
|
+
"position": {
|
|
43
|
+
"x": 0,
|
|
44
|
+
"y": 0
|
|
45
|
+
},
|
|
46
|
+
"data": {
|
|
47
|
+
"triggerType": "cron",
|
|
48
|
+
"label": "Every day at 04:30",
|
|
49
|
+
"config": {
|
|
50
|
+
"expression": "30 4 * * *"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"id": "recommend-tighten",
|
|
56
|
+
"type": "capability",
|
|
57
|
+
"position": {
|
|
58
|
+
"x": 0,
|
|
59
|
+
"y": 120
|
|
60
|
+
},
|
|
61
|
+
"data": {
|
|
62
|
+
"label": "Scan rejections → recommend tighten",
|
|
63
|
+
"verbId": "governance.recommend_tighten",
|
|
64
|
+
"inputMapping": {},
|
|
65
|
+
"_note": "governance.recommend_tighten takes NO params — it scans pod-wide agent proposals and files governance.tighten_lane review items itself, exactly like the widen scanner job. Read-only w.r.t. graph data (files review items only), so the node auto-runs without spawning a proposal for the verb itself."
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
],
|
|
69
|
+
"edges": [
|
|
70
|
+
{
|
|
71
|
+
"id": "e-recommend",
|
|
72
|
+
"source": "trigger",
|
|
73
|
+
"target": "recommend-tighten"
|
|
74
|
+
}
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
},
|
|
80
|
+
"dependencies": []
|
|
81
|
+
}
|