productfoo 0.0.0-stage → 1.0.1
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/LICENSE +201 -0
- package/README.md +338 -2
- package/bin/foo +3 -0
- package/bin/foo.mjs +42 -0
- package/commands/bug.md +31 -0
- package/commands/discovery.md +28 -0
- package/commands/ideate.md +27 -0
- package/commands/init.md +28 -0
- package/commands/pitch.md +27 -0
- package/commands/preview.md +25 -0
- package/commands/research.md +31 -0
- package/commands/roast.md +27 -0
- package/commands/spec.md +30 -0
- package/config/pricing.yaml +198 -0
- package/config/routing-policy.yaml +171 -0
- package/dist-public/src/activeModel.d.ts +83 -0
- package/dist-public/src/activeModel.js +331 -0
- package/dist-public/src/agentAdapters.d.ts +122 -0
- package/dist-public/src/agentAdapters.js +178 -0
- package/dist-public/src/agentState.d.ts +43 -0
- package/dist-public/src/agentState.js +177 -0
- package/dist-public/src/bugCapsule.d.ts +94 -0
- package/dist-public/src/bugCapsule.js +227 -0
- package/dist-public/src/bugCapture.d.ts +26 -0
- package/dist-public/src/bugCapture.js +251 -0
- package/dist-public/src/bugOwnerLog.d.ts +93 -0
- package/dist-public/src/bugOwnerLog.js +304 -0
- package/dist-public/src/bugReportStore.d.ts +48 -0
- package/dist-public/src/bugReportStore.js +860 -0
- package/dist-public/src/bugSpecContext.d.ts +7 -0
- package/dist-public/src/bugSpecContext.js +129 -0
- package/dist-public/src/channelFreshness.d.ts +25 -0
- package/dist-public/src/channelFreshness.js +194 -0
- package/dist-public/src/cli.d.ts +30 -0
- package/dist-public/src/cli.js +1667 -0
- package/dist-public/src/cliArgs.d.ts +7 -0
- package/dist-public/src/cliArgs.js +44 -0
- package/dist-public/src/codeCatalog.d.ts +41 -0
- package/dist-public/src/codeCatalog.js +197 -0
- package/dist-public/src/commandContract.d.ts +84 -0
- package/dist-public/src/commandContract.js +390 -0
- package/dist-public/src/commandUsage.d.ts +3 -0
- package/dist-public/src/commandUsage.js +30 -0
- package/dist-public/src/commands/agent.d.ts +60 -0
- package/dist-public/src/commands/agent.js +1353 -0
- package/dist-public/src/commands/asp.d.ts +8 -0
- package/dist-public/src/commands/asp.js +669 -0
- package/dist-public/src/commands/evalCmd.d.ts +4 -0
- package/dist-public/src/commands/evalCmd.js +405 -0
- package/dist-public/src/commands/login.d.ts +25 -0
- package/dist-public/src/commands/login.js +94 -0
- package/dist-public/src/commands/loop.d.ts +16 -0
- package/dist-public/src/commands/loop.js +619 -0
- package/dist-public/src/commands/reconcile.d.ts +41 -0
- package/dist-public/src/commands/reconcile.js +292 -0
- package/dist-public/src/commands/runtime.d.ts +1 -0
- package/dist-public/src/commands/runtime.js +36 -0
- package/dist-public/src/commands/scanRedact.d.ts +8 -0
- package/dist-public/src/commands/scanRedact.js +266 -0
- package/dist-public/src/commands/setup.d.ts +28 -0
- package/dist-public/src/commands/setup.js +398 -0
- package/dist-public/src/commands/shared.d.ts +17 -0
- package/dist-public/src/commands/shared.js +147 -0
- package/dist-public/src/commands/source.d.ts +1 -0
- package/dist-public/src/commands/source.js +134 -0
- package/dist-public/src/commands/specCmd.d.ts +24 -0
- package/dist-public/src/commands/specCmd.js +794 -0
- package/dist-public/src/commands/update.d.ts +61 -0
- package/dist-public/src/commands/update.js +244 -0
- package/dist-public/src/comprehensionSignal.d.ts +20 -0
- package/dist-public/src/comprehensionSignal.js +166 -0
- package/dist-public/src/conductor/diversity.d.ts +48 -0
- package/dist-public/src/conductor/diversity.js +423 -0
- package/dist-public/src/conductor/engine.d.ts +7 -0
- package/dist-public/src/conductor/engine.js +295 -0
- package/dist-public/src/conductor/families/ideate.d.ts +2 -0
- package/dist-public/src/conductor/families/ideate.js +255 -0
- package/dist-public/src/conductor/families/interview.d.ts +2 -0
- package/dist-public/src/conductor/families/interview.js +39 -0
- package/dist-public/src/conductor/families/specSwarm.d.ts +2 -0
- package/dist-public/src/conductor/families/specSwarm.js +146 -0
- package/dist-public/src/conductor/familyDelivery.d.ts +6 -0
- package/dist-public/src/conductor/familyDelivery.js +37 -0
- package/dist-public/src/conductor/familyPackArtifact.d.ts +4 -0
- package/dist-public/src/conductor/familyPackArtifact.js +260 -0
- package/dist-public/src/conductor/ideateDecision.d.ts +6 -0
- package/dist-public/src/conductor/ideateDecision.js +237 -0
- package/dist-public/src/conductor/pack.d.ts +26 -0
- package/dist-public/src/conductor/pack.js +75 -0
- package/dist-public/src/conductor/types.d.ts +111 -0
- package/dist-public/src/conductor/types.js +7 -0
- package/dist-public/src/configPath.d.ts +2 -0
- package/dist-public/src/configPath.js +7 -0
- package/dist-public/src/discoveryArtifacts.d.ts +54 -0
- package/dist-public/src/discoveryArtifacts.js +467 -0
- package/dist-public/src/discoveryCommand.d.ts +127 -0
- package/dist-public/src/discoveryCommand.js +2148 -0
- package/dist-public/src/discoveryConnections.d.ts +339 -0
- package/dist-public/src/discoveryConnections.js +613 -0
- package/dist-public/src/discoveryDocs.d.ts +129 -0
- package/dist-public/src/discoveryDocs.js +577 -0
- package/dist-public/src/discoveryEvidence.d.ts +59 -0
- package/dist-public/src/discoveryEvidence.js +198 -0
- package/dist-public/src/discoveryFireflies.d.ts +25 -0
- package/dist-public/src/discoveryFireflies.js +551 -0
- package/dist-public/src/discoveryGmail.d.ts +12 -0
- package/dist-public/src/discoveryGmail.js +466 -0
- package/dist-public/src/discoveryGong.d.ts +12 -0
- package/dist-public/src/discoveryGong.js +491 -0
- package/dist-public/src/discoveryLearningRouting.d.ts +90 -0
- package/dist-public/src/discoveryLearningRouting.js +405 -0
- package/dist-public/src/discoveryMatching.d.ts +26 -0
- package/dist-public/src/discoveryMatching.js +73 -0
- package/dist-public/src/discoveryOutlook.d.ts +12 -0
- package/dist-public/src/discoveryOutlook.js +492 -0
- package/dist-public/src/discoveryProcess.d.ts +221 -0
- package/dist-public/src/discoveryProcess.js +703 -0
- package/dist-public/src/discoveryProof.d.ts +143 -0
- package/dist-public/src/discoveryProof.js +529 -0
- package/dist-public/src/discoveryProviders.d.ts +152 -0
- package/dist-public/src/discoveryProviders.js +756 -0
- package/dist-public/src/discoveryReadiness.d.ts +219 -0
- package/dist-public/src/discoveryReadiness.js +672 -0
- package/dist-public/src/discoveryReconcile.d.ts +57 -0
- package/dist-public/src/discoveryReconcile.js +424 -0
- package/dist-public/src/discoverySlack.d.ts +12 -0
- package/dist-public/src/discoverySlack.js +452 -0
- package/dist-public/src/discoverySources.d.ts +56 -0
- package/dist-public/src/discoverySources.js +322 -0
- package/dist-public/src/discoverySync.d.ts +52 -0
- package/dist-public/src/discoverySync.js +151 -0
- package/dist-public/src/discoverySynthesis.d.ts +57 -0
- package/dist-public/src/discoverySynthesis.js +215 -0
- package/dist-public/src/discoveryTeams.d.ts +12 -0
- package/dist-public/src/discoveryTeams.js +516 -0
- package/dist-public/src/discoveryZoom.d.ts +12 -0
- package/dist-public/src/discoveryZoom.js +523 -0
- package/dist-public/src/elicitationSurface.d.ts +13 -0
- package/dist-public/src/elicitationSurface.js +77 -0
- package/dist-public/src/errorReport.d.ts +99 -0
- package/dist-public/src/errorReport.js +443 -0
- package/dist-public/src/errorTaxonomy.d.ts +84 -0
- package/dist-public/src/errorTaxonomy.js +189 -0
- package/dist-public/src/evalCalibration.d.ts +82 -0
- package/dist-public/src/evalCalibration.js +169 -0
- package/dist-public/src/evalChecks.d.ts +21 -0
- package/dist-public/src/evalChecks.js +295 -0
- package/dist-public/src/evalCompile.d.ts +56 -0
- package/dist-public/src/evalCompile.js +58 -0
- package/dist-public/src/evalImprove.d.ts +60 -0
- package/dist-public/src/evalImprove.js +175 -0
- package/dist-public/src/evalJudge.d.ts +47 -0
- package/dist-public/src/evalJudge.js +143 -0
- package/dist-public/src/evalLabels.d.ts +100 -0
- package/dist-public/src/evalLabels.js +287 -0
- package/dist-public/src/evalRecord.d.ts +68 -0
- package/dist-public/src/evalRecord.js +165 -0
- package/dist-public/src/evalRoast.d.ts +88 -0
- package/dist-public/src/evalRoast.js +249 -0
- package/dist-public/src/evidenceIntake.d.ts +66 -0
- package/dist-public/src/evidenceIntake.js +356 -0
- package/dist-public/src/feedback.d.ts +78 -0
- package/dist-public/src/feedback.js +294 -0
- package/dist-public/src/firefliesClient.d.ts +106 -0
- package/dist-public/src/firefliesClient.js +258 -0
- package/dist-public/src/firstMoveTensions.d.ts +10 -0
- package/dist-public/src/firstMoveTensions.js +93 -0
- package/dist-public/src/gitRun.d.ts +21 -0
- package/dist-public/src/gitRun.js +50 -0
- package/dist-public/src/githubReconcile.d.ts +94 -0
- package/dist-public/src/githubReconcile.js +205 -0
- package/dist-public/src/gmailNango.d.ts +131 -0
- package/dist-public/src/gmailNango.js +329 -0
- package/dist-public/src/gongNango.d.ts +118 -0
- package/dist-public/src/gongNango.js +308 -0
- package/dist-public/src/grammarGate.d.ts +13 -0
- package/dist-public/src/grammarGate.js +72 -0
- package/dist-public/src/harnessGates.d.ts +65 -0
- package/dist-public/src/harnessGates.js +168 -0
- package/dist-public/src/humanSurfaceEval.d.ts +19 -0
- package/dist-public/src/humanSurfaceEval.js +156 -0
- package/dist-public/src/ideate.d.ts +43 -0
- package/dist-public/src/ideate.js +255 -0
- package/dist-public/src/ideateContext.d.ts +11 -0
- package/dist-public/src/ideateContext.js +83 -0
- package/dist-public/src/index.d.ts +2 -0
- package/dist-public/src/index.js +4 -0
- package/dist-public/src/initCommand.d.ts +25 -0
- package/dist-public/src/initCommand.js +558 -0
- package/dist-public/src/interviewGate.d.ts +25 -0
- package/dist-public/src/interviewGate.js +126 -0
- package/dist-public/src/jsonFile.d.ts +2 -0
- package/dist-public/src/jsonFile.js +22 -0
- package/dist-public/src/loopBudget.d.ts +182 -0
- package/dist-public/src/loopBudget.js +494 -0
- package/dist-public/src/loopContracts.d.ts +35 -0
- package/dist-public/src/loopContracts.js +364 -0
- package/dist-public/src/machineState.d.ts +1 -0
- package/dist-public/src/machineState.js +9 -0
- package/dist-public/src/notionNango.d.ts +93 -0
- package/dist-public/src/notionNango.js +279 -0
- package/dist-public/src/orchestrationResult.d.ts +11 -0
- package/dist-public/src/orchestrationResult.js +73 -0
- package/dist-public/src/outlookNango.d.ts +124 -0
- package/dist-public/src/outlookNango.js +327 -0
- package/dist-public/src/packetCorrections.d.ts +40 -0
- package/dist-public/src/packetCorrections.js +172 -0
- package/dist-public/src/packetGlossary.d.ts +6 -0
- package/dist-public/src/packetGlossary.js +42 -0
- package/dist-public/src/packetIndex.d.ts +54 -0
- package/dist-public/src/packetIndex.js +744 -0
- package/dist-public/src/packetIndexEntry.d.ts +3 -0
- package/dist-public/src/packetIndexEntry.js +128 -0
- package/dist-public/src/packetIndexServe.d.ts +13 -0
- package/dist-public/src/packetIndexServe.js +113 -0
- package/dist-public/src/packetIndexWorker.d.ts +7 -0
- package/dist-public/src/packetIndexWorker.js +45 -0
- package/dist-public/src/packetLifecycle.d.ts +30 -0
- package/dist-public/src/packetLifecycle.js +127 -0
- package/dist-public/src/packetReconcile.d.ts +73 -0
- package/dist-public/src/packetReconcile.js +386 -0
- package/dist-public/src/packetStatus.d.ts +125 -0
- package/dist-public/src/packetStatus.js +770 -0
- package/dist-public/src/packetValidation.d.ts +17 -0
- package/dist-public/src/packetValidation.js +333 -0
- package/dist-public/src/packetVerify.d.ts +73 -0
- package/dist-public/src/packetVerify.js +303 -0
- package/dist-public/src/packetWrite.d.ts +52 -0
- package/dist-public/src/packetWrite.js +448 -0
- package/dist-public/src/pitch.d.ts +38 -0
- package/dist-public/src/pitch.js +154 -0
- package/dist-public/src/processMemo.d.ts +8 -0
- package/dist-public/src/processMemo.js +23 -0
- package/dist-public/src/productDetect.d.ts +30 -0
- package/dist-public/src/productDetect.js +376 -0
- package/dist-public/src/profile.d.ts +62 -0
- package/dist-public/src/profile.js +334 -0
- package/dist-public/src/questionModel.d.ts +46 -0
- package/dist-public/src/questionModel.js +74 -0
- package/dist-public/src/questionText.d.ts +7 -0
- package/dist-public/src/questionText.js +38 -0
- package/dist-public/src/redactConfig.d.ts +2 -0
- package/dist-public/src/redactConfig.js +11 -0
- package/dist-public/src/redactEngine.d.ts +59 -0
- package/dist-public/src/redactEngine.js +509 -0
- package/dist-public/src/redactGate.d.ts +63 -0
- package/dist-public/src/redactGate.js +209 -0
- package/dist-public/src/repoRead.d.ts +29 -0
- package/dist-public/src/repoRead.js +160 -0
- package/dist-public/src/research.d.ts +242 -0
- package/dist-public/src/research.js +1314 -0
- package/dist-public/src/researchCompletion.d.ts +34 -0
- package/dist-public/src/researchCompletion.js +123 -0
- package/dist-public/src/researchExecution.d.ts +47 -0
- package/dist-public/src/researchExecution.js +309 -0
- package/dist-public/src/researchFormat.d.ts +21 -0
- package/dist-public/src/researchFormat.js +77 -0
- package/dist-public/src/researchGate.d.ts +86 -0
- package/dist-public/src/researchGate.js +1036 -0
- package/dist-public/src/researchReceipt.d.ts +76 -0
- package/dist-public/src/researchReceipt.js +148 -0
- package/dist-public/src/researchRender.d.ts +2 -0
- package/dist-public/src/researchRender.js +267 -0
- package/dist-public/src/researchSigningKey.d.ts +19 -0
- package/dist-public/src/researchSigningKey.js +95 -0
- package/dist-public/src/riskClassify.d.ts +42 -0
- package/dist-public/src/riskClassify.js +149 -0
- package/dist-public/src/routines.d.ts +42 -0
- package/dist-public/src/routines.js +269 -0
- package/dist-public/src/routingPolicy.d.ts +172 -0
- package/dist-public/src/routingPolicy.js +646 -0
- package/dist-public/src/secretInput.d.ts +8 -0
- package/dist-public/src/secretInput.js +89 -0
- package/dist-public/src/selfCheck.d.ts +7 -0
- package/dist-public/src/selfCheck.js +473 -0
- package/dist-public/src/selfCheckCache.d.ts +29 -0
- package/dist-public/src/selfCheckCache.js +147 -0
- package/dist-public/src/severityClass.d.ts +15 -0
- package/dist-public/src/severityClass.js +22 -0
- package/dist-public/src/skillClientPolicy.d.ts +8 -0
- package/dist-public/src/skillClientPolicy.js +16 -0
- package/dist-public/src/skillLocal.d.ts +98 -0
- package/dist-public/src/skillLocal.js +463 -0
- package/dist-public/src/skillTreeDigest.d.ts +9 -0
- package/dist-public/src/skillTreeDigest.js +64 -0
- package/dist-public/src/slackNango.d.ts +62 -0
- package/dist-public/src/slackNango.js +208 -0
- package/dist-public/src/social/arxiv.d.ts +19 -0
- package/dist-public/src/social/arxiv.js +96 -0
- package/dist-public/src/social/github.d.ts +2 -0
- package/dist-public/src/social/github.js +78 -0
- package/dist-public/src/social/hackernews.d.ts +2 -0
- package/dist-public/src/social/hackernews.js +67 -0
- package/dist-public/src/social/index.d.ts +10 -0
- package/dist-public/src/social/index.js +79 -0
- package/dist-public/src/social/reddit.d.ts +2 -0
- package/dist-public/src/social/reddit.js +125 -0
- package/dist-public/src/social/types.d.ts +73 -0
- package/dist-public/src/social/types.js +61 -0
- package/dist-public/src/social/xml.d.ts +10 -0
- package/dist-public/src/social/xml.js +49 -0
- package/dist-public/src/social/youtube.d.ts +7 -0
- package/dist-public/src/social/youtube.js +217 -0
- package/dist-public/src/sourceIngress.d.ts +488 -0
- package/dist-public/src/sourceIngress.js +1651 -0
- package/dist-public/src/specAfter.d.ts +187 -0
- package/dist-public/src/specAfter.js +814 -0
- package/dist-public/src/specBudget.d.ts +124 -0
- package/dist-public/src/specBudget.js +424 -0
- package/dist-public/src/specDecisionSignals.d.ts +38 -0
- package/dist-public/src/specDecisionSignals.js +121 -0
- package/dist-public/src/specLinkedCommits.d.ts +7 -0
- package/dist-public/src/specLinkedCommits.js +107 -0
- package/dist-public/src/specSections.d.ts +105 -0
- package/dist-public/src/specSections.js +718 -0
- package/dist-public/src/statusGlyphs.d.ts +18 -0
- package/dist-public/src/statusGlyphs.js +75 -0
- package/dist-public/src/swarmCommand.d.ts +10 -0
- package/dist-public/src/swarmCommand.js +96 -0
- package/dist-public/src/teamsNango.d.ts +114 -0
- package/dist-public/src/teamsNango.js +301 -0
- package/dist-public/src/tokenStore.d.ts +10 -0
- package/dist-public/src/tokenStore.js +77 -0
- package/dist-public/src/trustEnvelope.d.ts +78 -0
- package/dist-public/src/trustEnvelope.js +392 -0
- package/dist-public/src/untrustedText.d.ts +6 -0
- package/dist-public/src/untrustedText.js +20 -0
- package/dist-public/src/updateNotice.d.ts +41 -0
- package/dist-public/src/updateNotice.js +290 -0
- package/dist-public/src/vendorYaml.d.ts +1 -0
- package/dist-public/src/vendorYaml.js +7374 -0
- package/dist-public/src/workflowRunActuals.d.ts +67 -0
- package/dist-public/src/workflowRunActuals.js +184 -0
- package/dist-public/src/zendeskDiscovery.d.ts +12 -0
- package/dist-public/src/zendeskDiscovery.js +513 -0
- package/dist-public/src/zendeskNango.d.ts +131 -0
- package/dist-public/src/zendeskNango.js +344 -0
- package/dist-public/src/zoomNango.d.ts +106 -0
- package/dist-public/src/zoomNango.js +351 -0
- package/docs/CHANGELOG.md +32 -0
- package/docs/THIRD_PARTY_NOTICES.md +36 -0
- package/package.json +71 -4
- package/schemas/bug-capsule-receipt.schema.json +54 -0
- package/schemas/bug-capsule.schema.json +189 -0
- package/schemas/discovery-evidence-envelope.schema.json +79 -0
- package/schemas/discovery-processing-receipt.schema.json +71 -0
- package/schemas/discovery-service-contract.schema.json +189 -0
- package/skills/AGENTS.md +32 -0
- package/skills/README.md +287 -0
- package/skills/bug/SKILL.md +49 -0
- package/skills/build-preview-spec/SKILL.md +103 -0
- package/skills/capture-external-artifact/SKILL.md +23 -0
- package/skills/capture-external-artifact/agents/openai.yaml +4 -0
- package/skills/capture-external-artifact/references/extractor-libraries.md +27 -0
- package/skills/check-spec-health/SKILL.md +45 -0
- package/skills/check-spec-health/agents/openai.yaml +4 -0
- package/skills/competitive-positioning/SKILL.md +86 -0
- package/skills/competitive-positioning/agents/openai.yaml +4 -0
- package/skills/cpo-lens/SKILL.md +398 -0
- package/skills/cpo-lens/agents/openai.yaml +4 -0
- package/skills/create-prd/SKILL.md +26 -0
- package/skills/create-prd/agents/openai.yaml +4 -0
- package/skills/customer-discovery/SKILL.md +192 -0
- package/skills/design-system/SKILL.md +49 -0
- package/skills/design-system/agents/openai.yaml +4 -0
- package/skills/discussion-guide/SKILL.md +75 -0
- package/skills/discussion-guide/agents/openai.yaml +4 -0
- package/skills/firecrawl-research-papers/SKILL.md +96 -0
- package/skills/firecrawl-research-papers/agents/openai.yaml +4 -0
- package/skills/foo-roast/SKILL.md +325 -0
- package/skills/generate-gtm-brief/SKILL.md +41 -0
- package/skills/generate-gtm-brief/agents/openai.yaml +4 -0
- package/skills/generate-gtm-brief/references/gtm-brief-template.md +56 -0
- package/skills/gong-quotes/SKILL.md +43 -0
- package/skills/gong-quotes/agents/openai.yaml +4 -0
- package/skills/grill-me/SKILL.md +27 -0
- package/skills/grill-me/agents/openai.yaml +4 -0
- package/skills/ideate/SKILL.md +180 -0
- package/skills/ideate/references/family-packs.md +5 -0
- package/skills/initiative-tracker/SKILL.md +23 -0
- package/skills/initiative-tracker/agents/openai.yaml +4 -0
- package/skills/interop-review/SKILL.md +25 -0
- package/skills/interop-review/agents/openai.yaml +4 -0
- package/skills/interview-me/SKILL.md +404 -0
- package/skills/interview-me/agents/openai.yaml +4 -0
- package/skills/loop-library-audit/SKILL.md +123 -0
- package/skills/pitch/SKILL.md +110 -0
- package/skills/pitch/references/opportunity-assessment.md +136 -0
- package/skills/pitch/references/prfaq-template.md +54 -0
- package/skills/pmm-release-briefing/SKILL.md +27 -0
- package/skills/pmm-release-briefing/agents/openai.yaml +4 -0
- package/skills/pmm-release-briefing/references/variant-generalized.md +16 -0
- package/skills/positioning-messaging/SKILL.md +70 -0
- package/skills/positioning-messaging/agents/openai.yaml +4 -0
- package/skills/positioning-messaging/references/guest-insights.md +1400 -0
- package/skills/preview-swarm/SKILL.md +418 -0
- package/skills/preview-swarm/agents/openai.yaml +4 -0
- package/skills/preview-swarm/references/html-effectiveness-build-previews.md +168 -0
- package/skills/product-copywriting/SKILL.md +127 -0
- package/skills/product-copywriting/agents/openai.yaml +4 -0
- package/skills/product-copywriting/references/foo-voice.md +26 -0
- package/skills/product-shape-interview/SKILL.md +219 -0
- package/skills/product-shape-interview/agents/openai.yaml +4 -0
- package/skills/ready-for-release/SKILL.md +39 -0
- package/skills/ready-for-release/agents/openai.yaml +4 -0
- package/skills/redact-transcripts/SKILL.md +23 -0
- package/skills/redact-transcripts/agents/openai.yaml +4 -0
- package/skills/research-brief/SKILL.md +48 -0
- package/skills/research-brief/agents/openai.yaml +4 -0
- package/skills/research-refresh/SKILL.md +63 -0
- package/skills/research-refresh/references/worked-cases.md +44 -0
- package/skills/review-loop/SKILL.md +200 -0
- package/skills/review-spec/SKILL.md +32 -0
- package/skills/review-spec/agents/openai.yaml +4 -0
- package/skills/rfc-from-research/SKILL.md +42 -0
- package/skills/rfc-from-research/agents/openai.yaml +4 -0
- package/skills/rfc-from-research/references/rfc-pattern.md +69 -0
- package/skills/skill-registry.yaml +810 -0
- package/skills/spec-cleanup/SKILL.md +103 -0
- package/skills/spec-cleanup/agents/openai.yaml +4 -0
- package/skills/spec-swarm/SKILL.md +87 -0
- package/skills/spec-swarm/agents/openai.yaml +4 -0
- package/skills/spec-swarm/template/SKILL.template.md +94 -0
- package/skills/spec-swarm/template/personas/accessibility-inclusion.md +40 -0
- package/skills/spec-swarm/template/personas/ai-technical-pm.md +69 -0
- package/skills/spec-swarm/template/personas/analytics-engineering.md +39 -0
- package/skills/spec-swarm/template/personas/applied-ai-eval.md +64 -0
- package/skills/spec-swarm/template/personas/business-gm-pm.md +67 -0
- package/skills/spec-swarm/template/personas/consumer-craft-pm.md +68 -0
- package/skills/spec-swarm/template/personas/content-design.md +40 -0
- package/skills/spec-swarm/template/personas/cpo.md +42 -0
- package/skills/spec-swarm/template/personas/data-science.md +42 -0
- package/skills/spec-swarm/template/personas/developer-experience.md +65 -0
- package/skills/spec-swarm/template/personas/dx-product.md +70 -0
- package/skills/spec-swarm/template/personas/field.md +43 -0
- package/skills/spec-swarm/template/personas/finance-cfo.md +40 -0
- package/skills/spec-swarm/template/personas/growth-pm.md +67 -0
- package/skills/spec-swarm/template/personas/platform-pm.md +67 -0
- package/skills/spec-swarm/template/personas/pmm.md +43 -0
- package/skills/spec-swarm/template/personas/principal-product-designer.md +42 -0
- package/skills/spec-swarm/template/personas/product-ops-program.md +41 -0
- package/skills/spec-swarm/template/personas/qa-release.md +41 -0
- package/skills/spec-swarm/template/personas/responsible-ai-safety.md +64 -0
- package/skills/spec-swarm/template/personas/security-privacy-legal.md +40 -0
- package/skills/spec-swarm/template/personas/strategy-competitive.md +41 -0
- package/skills/spec-swarm/template/personas/technical-architect.md +42 -0
- package/skills/spec-swarm/template/personas/technical-lead.md +43 -0
- package/skills/spec-swarm/template/personas/user-research.md +41 -0
- package/skills/spec-swarm-accessibility-inclusion/SKILL.md +95 -0
- package/skills/spec-swarm-accessibility-inclusion/agents/openai.yaml +4 -0
- package/skills/spec-swarm-ai-technical-pm/SKILL.md +124 -0
- package/skills/spec-swarm-analytics-engineering/SKILL.md +94 -0
- package/skills/spec-swarm-analytics-engineering/agents/openai.yaml +4 -0
- package/skills/spec-swarm-applied-ai-eval/SKILL.md +120 -0
- package/skills/spec-swarm-business-gm-pm/SKILL.md +122 -0
- package/skills/spec-swarm-consumer-craft-pm/SKILL.md +122 -0
- package/skills/spec-swarm-content-design/SKILL.md +95 -0
- package/skills/spec-swarm-content-design/agents/openai.yaml +4 -0
- package/skills/spec-swarm-cpo/SKILL.md +94 -0
- package/skills/spec-swarm-cpo/agents/openai.yaml +4 -0
- package/skills/spec-swarm-data-science/SKILL.md +95 -0
- package/skills/spec-swarm-data-science/agents/openai.yaml +4 -0
- package/skills/spec-swarm-developer-experience/SKILL.md +121 -0
- package/skills/spec-swarm-dx-product/SKILL.md +126 -0
- package/skills/spec-swarm-field/SKILL.md +96 -0
- package/skills/spec-swarm-field/agents/openai.yaml +4 -0
- package/skills/spec-swarm-finance-cfo/SKILL.md +95 -0
- package/skills/spec-swarm-finance-cfo/agents/openai.yaml +4 -0
- package/skills/spec-swarm-growth-pm/SKILL.md +122 -0
- package/skills/spec-swarm-platform-pm/SKILL.md +122 -0
- package/skills/spec-swarm-pmm/SKILL.md +96 -0
- package/skills/spec-swarm-pmm/agents/openai.yaml +4 -0
- package/skills/spec-swarm-principal-product-designer/SKILL.md +95 -0
- package/skills/spec-swarm-principal-product-designer/agents/openai.yaml +4 -0
- package/skills/spec-swarm-product-ops-program/SKILL.md +96 -0
- package/skills/spec-swarm-product-ops-program/agents/openai.yaml +4 -0
- package/skills/spec-swarm-qa-release/SKILL.md +96 -0
- package/skills/spec-swarm-qa-release/agents/openai.yaml +4 -0
- package/skills/spec-swarm-responsible-ai-safety/SKILL.md +120 -0
- package/skills/spec-swarm-security-privacy-legal/SKILL.md +95 -0
- package/skills/spec-swarm-security-privacy-legal/agents/openai.yaml +4 -0
- package/skills/spec-swarm-strategy-competitive/SKILL.md +96 -0
- package/skills/spec-swarm-strategy-competitive/agents/openai.yaml +4 -0
- package/skills/spec-swarm-technical-architect/SKILL.md +95 -0
- package/skills/spec-swarm-technical-architect/agents/openai.yaml +4 -0
- package/skills/spec-swarm-technical-lead/SKILL.md +96 -0
- package/skills/spec-swarm-technical-lead/agents/openai.yaml +4 -0
- package/skills/spec-swarm-user-research/SKILL.md +95 -0
- package/skills/spec-swarm-user-research/agents/openai.yaml +4 -0
- package/skills/strategy-check/SKILL.md +170 -0
- package/skills/strategy-check/agents/openai.yaml +4 -0
- package/skills/study-digest/SKILL.md +41 -0
- package/skills/synthetic-customer/SKILL.md +68 -0
- package/skills/update-cli-spec/SKILL.md +69 -0
- package/skills/update-cli-spec/agents/openai.yaml +4 -0
- package/skills/voc-researcher/SKILL.md +45 -0
- package/skills/voc-researcher/agents/openai.yaml +4 -0
- package/skills-lock.json +35 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
// B3 risky-work classifier. productfoo treats spec-first as a boundary only for work
|
|
2
|
+
// that is irreversible, agentic/delegated, or outbound-facing; everything else
|
|
3
|
+
// is advisory. This makes that judgment concrete, inspectable, and overridable
|
|
4
|
+
// with a recorded reason — never a silent verdict.
|
|
5
|
+
import { withGlyph } from "./statusGlyphs.js";
|
|
6
|
+
const SIGNAL_TERMS = {
|
|
7
|
+
irreversible: [
|
|
8
|
+
"delete", "drop", "destroy", "wipe", "purge", "truncate", "overwrite",
|
|
9
|
+
"migrate", "migration", "schema", "production", "prod", "deploy", "release",
|
|
10
|
+
"payment", "charge", "refund", "billing", "money", "irreversible", "destructive",
|
|
11
|
+
],
|
|
12
|
+
agentic: [
|
|
13
|
+
"agent", "agentic", "autonomous", "autonomy", "unattended", "delegate",
|
|
14
|
+
"delegated", "loop", "background", "automatic", "auto-run", "self-serve",
|
|
15
|
+
"on behalf", "without approval", "cron", "scheduled", "nightly", "automatically",
|
|
16
|
+
],
|
|
17
|
+
outbound: [
|
|
18
|
+
"email", "send", "publish", "post", "notify", "notification", "customer",
|
|
19
|
+
"external", "outbound", "broadcast", "slack", "tweet", "sms", "webhook",
|
|
20
|
+
"public", "message users",
|
|
21
|
+
],
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Terms whose risk a governing frame cannot talk away.
|
|
25
|
+
*
|
|
26
|
+
* These name a *behaviour being created* — something that will act on its own — and
|
|
27
|
+
* "I'm only adding a monitor for it" does not make that safe. Everything else in the
|
|
28
|
+
* lexicon is a noun that risky work and work *about* risky work mention equally often.
|
|
29
|
+
*
|
|
30
|
+
* This is the line that keeps the dampener below from becoming a hole: "add a
|
|
31
|
+
* background loop that watches deploys and auto-rolls-back" still blocks, because
|
|
32
|
+
* `loop` and `automatically` live here, even though `watches` reads like governance.
|
|
33
|
+
*/
|
|
34
|
+
const BEHAVIOUR_TERMS = [
|
|
35
|
+
"autonomous", "autonomy", "unattended", "auto-run", "automatically", "loop",
|
|
36
|
+
"cron", "scheduled", "nightly", "on behalf", "without approval", "delegate", "delegated",
|
|
37
|
+
];
|
|
38
|
+
/** Verbs a *build* statement opens with. "delete the prod table" opens with none of them. */
|
|
39
|
+
const BUILD_VERBS = [
|
|
40
|
+
"add", "create", "write", "wire", "fix", "update", "refactor", "rename",
|
|
41
|
+
"introduce", "implement", "build", "make", "extend", "tighten", "calibrate", "harden",
|
|
42
|
+
];
|
|
43
|
+
/** What the work is doing TO the risky thing: constraining it, or watching it. */
|
|
44
|
+
const GOVERNANCE_TERMS = [
|
|
45
|
+
"hook", "gate", "guard", "block", "blocks", "deny", "denies", "refuse", "refuses",
|
|
46
|
+
"prevent", "prevents", "test", "tests", "lint", "check", "checks", "validate",
|
|
47
|
+
"verify", "audit", "document", "warn", "warns", "log", "logs", "monitor", "monitors",
|
|
48
|
+
"detect", "detects", "review", "classify", "surface", "report", "dry-run", "spec",
|
|
49
|
+
];
|
|
50
|
+
function matchTerms(work, terms) {
|
|
51
|
+
const haystack = work.toLowerCase();
|
|
52
|
+
const matched = [];
|
|
53
|
+
for (const term of terms) {
|
|
54
|
+
// Tolerate the inflections, because a gate that misses is worse than one that asks.
|
|
55
|
+
// `\bdeploy\b` does not match "deploys"; `\bdelete\b` does not match "deleting". The
|
|
56
|
+
// exact-word matcher waved both through — a false negative in a safety gate, and the
|
|
57
|
+
// more dangerous direction to be wrong in.
|
|
58
|
+
const pattern = /^[a-z]+$/.test(term)
|
|
59
|
+
? new RegExp(`\\b${term}(s|d|es|ed|ing)?\\b`)
|
|
60
|
+
: new RegExp(term.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
|
|
61
|
+
if (pattern.test(haystack))
|
|
62
|
+
matched.push(term);
|
|
63
|
+
}
|
|
64
|
+
return matched;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Is the work *governing* the risky thing rather than *doing* it?
|
|
68
|
+
*
|
|
69
|
+
* "Add a hook that denies raw research agents" mentions agents; it does not act
|
|
70
|
+
* agentically — it is the thing that stops agents. "Add a test for the delete path"
|
|
71
|
+
* mentions delete; it deletes nothing. A bag of words cannot tell those from "delete
|
|
72
|
+
* the production table", and this classifier used to call all three risky.
|
|
73
|
+
*
|
|
74
|
+
* The tell is grammatical, and it is cheap to read: the statement OPENS with a build
|
|
75
|
+
* verb (so the risky term is an object, not the action) and names a governing
|
|
76
|
+
* construct (a hook, a gate, a test, an audit). Real risky work does not open with
|
|
77
|
+
* "add a guard that…".
|
|
78
|
+
*
|
|
79
|
+
* This is a heuristic and it will be wrong sometimes. That is why it lowers confidence
|
|
80
|
+
* and prints its reasoning instead of silently flipping the verdict.
|
|
81
|
+
*/
|
|
82
|
+
export function isGovernedFrame(work) {
|
|
83
|
+
const words = work.toLowerCase().split(/[^a-z-]+/).filter(Boolean);
|
|
84
|
+
// The head is either a build verb ("add a hook that blocks deploys") or the governing
|
|
85
|
+
// act itself ("document the refund flow", "audit the payment path"). Risky work opens
|
|
86
|
+
// with neither — "delete the production table" leads with the destruction.
|
|
87
|
+
const head = words.slice(0, 5);
|
|
88
|
+
const opensGoverning = head.some((w) => BUILD_VERBS.includes(w) || GOVERNANCE_TERMS.includes(w));
|
|
89
|
+
return opensGoverning && matchTerms(work, GOVERNANCE_TERMS).length > 0;
|
|
90
|
+
}
|
|
91
|
+
export function classifyWork(work, override, options = {}) {
|
|
92
|
+
const dimensions = {
|
|
93
|
+
irreversible: matchTerms(work, SIGNAL_TERMS.irreversible),
|
|
94
|
+
agentic: matchTerms(work, SIGNAL_TERMS.agentic),
|
|
95
|
+
outbound: matchTerms(work, SIGNAL_TERMS.outbound),
|
|
96
|
+
};
|
|
97
|
+
const hits = Object.keys(dimensions).filter((dim) => dimensions[dim].length > 0);
|
|
98
|
+
const matched = hits.flatMap((dim) => dimensions[dim]);
|
|
99
|
+
const behaviour = matched.filter((term) => BEHAVIOUR_TERMS.includes(term));
|
|
100
|
+
const strict = options.strict === true;
|
|
101
|
+
// A governing frame only earns the downgrade when nothing in the work names a
|
|
102
|
+
// behaviour that acts on its own. "A loop that monitors deploys" is still a loop.
|
|
103
|
+
const governed_frame = hits.length > 0 && behaviour.length === 0 && isGovernedFrame(work);
|
|
104
|
+
const confidence = hits.length === 0 ? "high" : behaviour.length > 0 ? "high" : governed_frame ? "low" : "medium";
|
|
105
|
+
const risky = hits.length > 0 && (strict || !governed_frame);
|
|
106
|
+
const evidence = hits.map((dim) => `${dim} (${dimensions[dim].join(", ")})`).join("; ");
|
|
107
|
+
let rationale;
|
|
108
|
+
if (hits.length === 0) {
|
|
109
|
+
rationale = "No irreversible, agentic, or outbound-facing signals matched; treat spec-first as advisory.";
|
|
110
|
+
}
|
|
111
|
+
else if (governed_frame && !strict) {
|
|
112
|
+
// Never silently downgrade. Show the evidence, the reasoning, and the dissent —
|
|
113
|
+
// the reader has to be able to disagree with this and re-run it.
|
|
114
|
+
rationale =
|
|
115
|
+
`Matched ${evidence} — but this work reads as governing or testing those things, not doing them ` +
|
|
116
|
+
`(it opens with a build verb and names a guard, gate, test, or check). Keyword matches are evidence, ` +
|
|
117
|
+
`not proof, and a classifier that blocks on the mere mention of "agent" or "delete" is one people learn ` +
|
|
118
|
+
`to override on sight. Spec-first is advisory here. If this work really does act — delegates autonomy, ` +
|
|
119
|
+
`touches production, or reaches a user — it is risky and \`foo spec\` is the reflex. Force the block with --strict.`;
|
|
120
|
+
}
|
|
121
|
+
else if (behaviour.length > 0) {
|
|
122
|
+
rationale = `Matched ${evidence}. ${behaviour.join(", ")} names a behaviour that acts on its own, so a governing frame cannot excuse it.`;
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
rationale = `Matched ${evidence}.`;
|
|
126
|
+
}
|
|
127
|
+
// An override is a recorded human decision to waive the boundary, not a silent skip.
|
|
128
|
+
const spec_first_required = risky && !override;
|
|
129
|
+
return { work, risky, dimensions, confidence, governed_frame, rationale, override, spec_first_required };
|
|
130
|
+
}
|
|
131
|
+
export function renderClassification(classification, asJson, glyphsOn = false) {
|
|
132
|
+
if (asJson)
|
|
133
|
+
return `${JSON.stringify({ schema_version: "0.1", ...classification }, null, 2)}\n`;
|
|
134
|
+
const lines = [
|
|
135
|
+
`Risk classification for: ${classification.work}`,
|
|
136
|
+
"",
|
|
137
|
+
withGlyph("classification", `Verdict: ${classification.risky ? "risky" : "routine"} (confidence: ${classification.confidence})`, glyphsOn),
|
|
138
|
+
`Rationale: ${classification.rationale}`,
|
|
139
|
+
];
|
|
140
|
+
if (classification.override) {
|
|
141
|
+
lines.push(`Override recorded: ${classification.override}`);
|
|
142
|
+
}
|
|
143
|
+
lines.push("", classification.spec_first_required
|
|
144
|
+
? withGlyph("needs_input", "Boundary (B3): spec-first is required before build. Run `foo spec` or `foo spec start`.", glyphsOn)
|
|
145
|
+
: classification.override
|
|
146
|
+
? "Boundary (B3) waived by recorded override; proceed with the override on record."
|
|
147
|
+
: "Signal: spec-first is advisory for this work. `foo spec` is still the faster reflex.", "");
|
|
148
|
+
return lines.join("\n");
|
|
149
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { type RuntimeId, type TriggerKind } from "./agentAdapters.js";
|
|
2
|
+
import type { SeverityClass } from "./severityClass.js";
|
|
3
|
+
export interface RoutineTrigger {
|
|
4
|
+
kind: TriggerKind;
|
|
5
|
+
cron: string | null;
|
|
6
|
+
tz: string | null;
|
|
7
|
+
event: string | null;
|
|
8
|
+
}
|
|
9
|
+
export interface Routine {
|
|
10
|
+
id: string;
|
|
11
|
+
title: string;
|
|
12
|
+
goal: string;
|
|
13
|
+
mode: string;
|
|
14
|
+
trigger: RoutineTrigger;
|
|
15
|
+
/** Runtimes this routine is bound to (from the adapters block). */
|
|
16
|
+
runtimes: RuntimeId[];
|
|
17
|
+
/** schedule/event triggers run with no human present at fire time. */
|
|
18
|
+
unattended: boolean;
|
|
19
|
+
verifyCheck: string | null;
|
|
20
|
+
terminalStates: string[];
|
|
21
|
+
approvalBoundaries: string[];
|
|
22
|
+
observeSources: string[];
|
|
23
|
+
allowedActions: string[];
|
|
24
|
+
}
|
|
25
|
+
export interface RoutineFinding {
|
|
26
|
+
kind: "autonomy_incomplete" | "unknown_runtime" | "runtime_not_headless" | "unsupported_runtime" | "trigger_malformed";
|
|
27
|
+
severity: "high" | "medium" | "low";
|
|
28
|
+
class: SeverityClass;
|
|
29
|
+
/** B4 when this is the autonomy boundary; absent for advisory-only findings. */
|
|
30
|
+
boundary: "B4" | null;
|
|
31
|
+
subject: string;
|
|
32
|
+
summary: string;
|
|
33
|
+
suggested_actions: string[];
|
|
34
|
+
requires_human: boolean;
|
|
35
|
+
}
|
|
36
|
+
export declare function parseRoutines(packetDir: string): {
|
|
37
|
+
specId: string;
|
|
38
|
+
routines: Routine[];
|
|
39
|
+
};
|
|
40
|
+
export declare function validateRoutine(routine: Routine): RoutineFinding[];
|
|
41
|
+
export declare function passPrompt(routine: Routine, packetRel: string): string;
|
|
42
|
+
export declare function emitBinding(routine: Routine, runtime: RuntimeId, packetRel: string): string;
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
// Portable routines (portable-routines-v0): a routine is a loop contract whose
|
|
2
|
+
// trigger is a schedule or event (not just manual), bound to one or more coding
|
|
3
|
+
// agents. Fiona Fung's "routines" — async agent management — made runtime-neutral.
|
|
4
|
+
//
|
|
5
|
+
// The split that makes it agnostic:
|
|
6
|
+
// - The CONTRACT (goal, observe, act, verify, stop, ask_user_when, budget) is
|
|
7
|
+
// the existing loop contract — canonical, validated by loopContracts.ts, the
|
|
8
|
+
// same for every runtime.
|
|
9
|
+
// - The TRIGGER (when it fires) is canonical too: a standard cron / event name.
|
|
10
|
+
// - Only the BINDING (how a given agent arms the trigger and runs one pass
|
|
11
|
+
// headless) is per-runtime, and that lives in agentAdapters.ts.
|
|
12
|
+
//
|
|
13
|
+
// This module reads routines out of loops.yaml, enforces the autonomy boundary
|
|
14
|
+
// (B4) for anything that runs unattended, resolves the bound adapters, and emits
|
|
15
|
+
// the per-runtime binding artifact. productfoo never schedules anything itself.
|
|
16
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
17
|
+
import path from "node:path";
|
|
18
|
+
import { parse as parseYaml } from "./vendorYaml.js";
|
|
19
|
+
import { loadLoopContracts } from "./loopContracts.js";
|
|
20
|
+
import { NotFoundError } from "./errorTaxonomy.js";
|
|
21
|
+
import { AGENT_ADAPTERS, getAdapter } from "./agentAdapters.js";
|
|
22
|
+
function asObject(value) {
|
|
23
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
24
|
+
}
|
|
25
|
+
function str(value) {
|
|
26
|
+
return typeof value === "string" && value.trim() !== "" ? value.trim() : null;
|
|
27
|
+
}
|
|
28
|
+
function strArray(value) {
|
|
29
|
+
return Array.isArray(value) ? value.map(str).filter((x) => x !== null) : [];
|
|
30
|
+
}
|
|
31
|
+
function parseTrigger(raw) {
|
|
32
|
+
const t = asObject(raw);
|
|
33
|
+
const kind = (str(t.kind) ?? "manual");
|
|
34
|
+
return { kind, cron: str(t.cron), tz: str(t.tz), event: str(t.event) };
|
|
35
|
+
}
|
|
36
|
+
// Terminal states come from lifecycle.terminal_states when present, else the
|
|
37
|
+
// keys of the stop: block (each key is a terminal state name).
|
|
38
|
+
function parseTerminalStates(loop) {
|
|
39
|
+
const lifecycle = asObject(loop.lifecycle);
|
|
40
|
+
const fromLifecycle = strArray(lifecycle.terminal_states);
|
|
41
|
+
if (fromLifecycle.length > 0)
|
|
42
|
+
return fromLifecycle;
|
|
43
|
+
return Object.keys(asObject(loop.stop));
|
|
44
|
+
}
|
|
45
|
+
// A loop entry is a routine when it declares an adapters block or a non-manual
|
|
46
|
+
// trigger; a plain manual loop stays a loop.
|
|
47
|
+
function isRoutineEntry(loop) {
|
|
48
|
+
if ("adapters" in loop)
|
|
49
|
+
return true;
|
|
50
|
+
const kind = str(asObject(loop.trigger).kind);
|
|
51
|
+
return kind === "schedule" || kind === "event";
|
|
52
|
+
}
|
|
53
|
+
export function parseRoutines(packetDir) {
|
|
54
|
+
const loopsPath = path.join(packetDir, "loops.yaml");
|
|
55
|
+
if (!existsSync(loopsPath)) {
|
|
56
|
+
// Absent is not a defect (#702): NotFoundError classifies as `not_found`, which never pages.
|
|
57
|
+
throw new NotFoundError("loops.yaml missing; run foo loop design <packet-dir> > <packet-dir>/loops.yaml, then add trigger + adapters", "E_PACKET_NOT_FOUND");
|
|
58
|
+
}
|
|
59
|
+
// Reuse the base loop-contract validation so a routine can never skip it.
|
|
60
|
+
const { specId } = loadLoopContracts(packetDir);
|
|
61
|
+
const doc = asObject(parseYaml(readFileSync(loopsPath, "utf8")));
|
|
62
|
+
const loops = Array.isArray(doc.loops) ? doc.loops : [];
|
|
63
|
+
const routines = [];
|
|
64
|
+
for (const raw of loops) {
|
|
65
|
+
const loop = asObject(raw);
|
|
66
|
+
if (!isRoutineEntry(loop))
|
|
67
|
+
continue;
|
|
68
|
+
const trigger = parseTrigger(loop.trigger);
|
|
69
|
+
const adapters = asObject(loop.adapters);
|
|
70
|
+
const runtimes = strArray(adapters.runtimes);
|
|
71
|
+
const verify = asObject(loop.verify);
|
|
72
|
+
routines.push({
|
|
73
|
+
id: str(loop.id) ?? "L-?",
|
|
74
|
+
title: str(loop.title) ?? "",
|
|
75
|
+
goal: str(loop.goal) ?? "",
|
|
76
|
+
mode: str(loop.mode) ?? "human-in-loop",
|
|
77
|
+
trigger,
|
|
78
|
+
runtimes,
|
|
79
|
+
unattended: trigger.kind === "schedule" || trigger.kind === "event",
|
|
80
|
+
verifyCheck: str(verify.check),
|
|
81
|
+
terminalStates: parseTerminalStates(loop),
|
|
82
|
+
approvalBoundaries: strArray(loop.ask_user_when),
|
|
83
|
+
observeSources: strArray(asObject(loop.observe).sources),
|
|
84
|
+
allowedActions: strArray(asObject(loop.act).allowed),
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
return { specId, routines };
|
|
88
|
+
}
|
|
89
|
+
// Validate a routine. The autonomy boundary (B4) findings carry boundary: "B4"
|
|
90
|
+
// and are the only ones a caller should escalate to a blocking exit under
|
|
91
|
+
// --strict; everything else is an advisory signal (signals-over-gates).
|
|
92
|
+
export function validateRoutine(routine) {
|
|
93
|
+
const findings = [];
|
|
94
|
+
// B4: an unattended routine MUST carry a verification check, at least one
|
|
95
|
+
// terminal state, and an approval boundary — otherwise unbounded action.
|
|
96
|
+
if (routine.unattended) {
|
|
97
|
+
const missing = [];
|
|
98
|
+
if (!routine.verifyCheck)
|
|
99
|
+
missing.push("verify.check");
|
|
100
|
+
if (routine.terminalStates.length === 0)
|
|
101
|
+
missing.push("a terminal state");
|
|
102
|
+
if (routine.approvalBoundaries.length === 0)
|
|
103
|
+
missing.push("ask_user_when (approval boundary)");
|
|
104
|
+
if (missing.length > 0) {
|
|
105
|
+
findings.push({
|
|
106
|
+
kind: "autonomy_incomplete",
|
|
107
|
+
severity: "high",
|
|
108
|
+
class: "bad",
|
|
109
|
+
boundary: "B4",
|
|
110
|
+
subject: `routine:${routine.id}`,
|
|
111
|
+
summary: `Unattended routine ${routine.id} is missing ${missing.join(", ")}. An unattended loop without these is unbounded risk (B4).`,
|
|
112
|
+
suggested_actions: [
|
|
113
|
+
"add a verify.check the routine runs every pass",
|
|
114
|
+
"declare terminal states so the loop can stop",
|
|
115
|
+
"list ask_user_when boundaries for actions that need a human",
|
|
116
|
+
],
|
|
117
|
+
requires_human: true,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
if (routine.trigger.kind === "schedule" && !routine.trigger.cron) {
|
|
121
|
+
findings.push({
|
|
122
|
+
kind: "trigger_malformed",
|
|
123
|
+
severity: "high",
|
|
124
|
+
class: "bad",
|
|
125
|
+
boundary: "B4",
|
|
126
|
+
subject: `routine:${routine.id}`,
|
|
127
|
+
summary: `Routine ${routine.id} has a schedule trigger with no cron expression.`,
|
|
128
|
+
suggested_actions: ["set trigger.cron to a standard 5-field cron expression"],
|
|
129
|
+
requires_human: true,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (routine.runtimes.length === 0) {
|
|
134
|
+
findings.push({
|
|
135
|
+
kind: "unknown_runtime",
|
|
136
|
+
severity: "medium",
|
|
137
|
+
class: "sad",
|
|
138
|
+
boundary: null,
|
|
139
|
+
subject: `routine:${routine.id}`,
|
|
140
|
+
summary: `Routine ${routine.id} is bound to no runtime; it has a contract but nothing to run it.`,
|
|
141
|
+
suggested_actions: [`add adapters.runtimes (e.g. [claude, codex])`],
|
|
142
|
+
requires_human: false,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
for (const runtime of routine.runtimes) {
|
|
146
|
+
const adapter = getAdapter(runtime);
|
|
147
|
+
if (!adapter) {
|
|
148
|
+
findings.push({
|
|
149
|
+
kind: "unknown_runtime",
|
|
150
|
+
severity: "medium",
|
|
151
|
+
class: "bad",
|
|
152
|
+
boundary: null,
|
|
153
|
+
subject: `routine:${routine.id}`,
|
|
154
|
+
summary: `Routine ${routine.id} names unknown runtime "${runtime}".`,
|
|
155
|
+
suggested_actions: [`use a known runtime: ${Object.keys(AGENT_ADAPTERS).join(", ")}`],
|
|
156
|
+
requires_human: false,
|
|
157
|
+
});
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
if (routine.unattended && !adapter.can_run_headless) {
|
|
161
|
+
findings.push({
|
|
162
|
+
kind: "runtime_not_headless",
|
|
163
|
+
severity: "high",
|
|
164
|
+
class: "bad",
|
|
165
|
+
boundary: "B4",
|
|
166
|
+
subject: `routine:${routine.id}`,
|
|
167
|
+
summary: `Runtime ${adapter.label} cannot run headless, so it cannot safely back an unattended routine.`,
|
|
168
|
+
suggested_actions: [
|
|
169
|
+
`arm this routine through github-actions running a headless runtime instead`,
|
|
170
|
+
`or bind ${routine.id} to a headless runtime (${AGENT_ADAPTERS.claude.label}, ${AGENT_ADAPTERS.codex.label})`,
|
|
171
|
+
],
|
|
172
|
+
requires_human: true,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
else if (adapter.support === "planned") {
|
|
176
|
+
findings.push({
|
|
177
|
+
kind: "unsupported_runtime",
|
|
178
|
+
severity: "low",
|
|
179
|
+
class: "sad",
|
|
180
|
+
boundary: null,
|
|
181
|
+
subject: `routine:${routine.id}`,
|
|
182
|
+
summary: `Runtime ${adapter.label} is mapped but not yet supported: ${adapter.notes}`,
|
|
183
|
+
suggested_actions: [`prefer a supported runtime for now: ${supportedLabels()}`],
|
|
184
|
+
requires_human: false,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return findings;
|
|
189
|
+
}
|
|
190
|
+
function supportedLabels() {
|
|
191
|
+
return (Object.values(AGENT_ADAPTERS))
|
|
192
|
+
.filter((a) => a.support === "supported")
|
|
193
|
+
.map((a) => a.label)
|
|
194
|
+
.join(", ");
|
|
195
|
+
}
|
|
196
|
+
// The pass instruction handed to whichever runtime fires — identical across
|
|
197
|
+
// runtimes, which is what makes the routine portable. Built from the contract.
|
|
198
|
+
export function passPrompt(routine, packetRel) {
|
|
199
|
+
return [
|
|
200
|
+
`Run one pass of foo routine ${routine.id} on packet ${packetRel}.`,
|
|
201
|
+
`Goal: ${routine.goal}`,
|
|
202
|
+
`Observe only: ${routine.observeSources.join(", ") || "the packet artifacts"}.`,
|
|
203
|
+
`Allowed actions: ${routine.allowedActions.join("; ") || "propose findings only"}.`,
|
|
204
|
+
`Pause and ask a human when: ${routine.approvalBoundaries.join("; ") || "any irreversible or outbound action"}.`,
|
|
205
|
+
`Verify with: ${routine.verifyCheck ?? "foo asp verify " + packetRel}.`,
|
|
206
|
+
`Record the iteration to the packet ledger, then stop at a terminal state (${routine.terminalStates.join(", ") || "succeeded, blocked, exhausted"}).`,
|
|
207
|
+
].join("\n");
|
|
208
|
+
}
|
|
209
|
+
// Emit the per-runtime binding artifact: how THIS runtime arms the trigger and
|
|
210
|
+
// runs one pass. Read-only — prints to stdout for review before it is committed
|
|
211
|
+
// or armed; foo schedules nothing itself.
|
|
212
|
+
export function emitBinding(routine, runtime, packetRel) {
|
|
213
|
+
const adapter = getAdapter(runtime);
|
|
214
|
+
if (!adapter)
|
|
215
|
+
throw new Error(`unknown runtime "${runtime}" — known: ${Object.keys(AGENT_ADAPTERS).join(", ")}`);
|
|
216
|
+
const prompt = passPrompt(routine, packetRel);
|
|
217
|
+
const oneLinePrompt = prompt.replace(/\n/g, " ");
|
|
218
|
+
const invoke = adapter.invoke_template
|
|
219
|
+
? adapter.invoke_template.replace("{prompt}", oneLinePrompt).replace("{packet}", packetRel)
|
|
220
|
+
: null;
|
|
221
|
+
const cron = routine.trigger.cron ?? "<set trigger.cron>";
|
|
222
|
+
const header = [
|
|
223
|
+
`# foo routine binding — ${routine.id} → ${adapter.label} (${adapter.support})`,
|
|
224
|
+
`# Canonical contract lives in the packet; this is only the ${adapter.label} binding.`,
|
|
225
|
+
`# Review before arming. productfoo does not schedule this for you.`,
|
|
226
|
+
];
|
|
227
|
+
if (runtime === "github-actions") {
|
|
228
|
+
// CI is a meta-adapter: it has no invoke of its own (invoke_template is null)
|
|
229
|
+
// but hosts another runtime's headless invocation on a cron, so it is handled
|
|
230
|
+
// before the headless guard below.
|
|
231
|
+
return [
|
|
232
|
+
...header,
|
|
233
|
+
`# Hosts a headless runtime on a cron. Fill in the runtime step below.`,
|
|
234
|
+
`name: foo-routine-${routine.id}`,
|
|
235
|
+
`on:`,
|
|
236
|
+
` schedule:`,
|
|
237
|
+
` - cron: "${cron}"`,
|
|
238
|
+
`jobs:`,
|
|
239
|
+
` ${routine.id.toLowerCase()}:`,
|
|
240
|
+
` runs-on: ubuntu-latest`,
|
|
241
|
+
` steps:`,
|
|
242
|
+
` - uses: actions/checkout@v4`,
|
|
243
|
+
` - name: run routine pass`,
|
|
244
|
+
` run: |`,
|
|
245
|
+
` # e.g. ${AGENT_ADAPTERS.codex.invoke_template?.replace("{prompt}", '"$PROMPT"')}`,
|
|
246
|
+
` env:`,
|
|
247
|
+
` PROMPT: ${JSON.stringify(oneLinePrompt)}`,
|
|
248
|
+
"",
|
|
249
|
+
].join("\n");
|
|
250
|
+
}
|
|
251
|
+
if (!adapter.can_run_headless || !invoke) {
|
|
252
|
+
return [
|
|
253
|
+
...header,
|
|
254
|
+
`#`,
|
|
255
|
+
`# ${adapter.label} cannot be driven headless today.`,
|
|
256
|
+
`# Gap: ${adapter.notes}`,
|
|
257
|
+
`# Fix: arm this routine via github-actions running a headless runtime, or bind to ${supportedLabels()}.`,
|
|
258
|
+
"",
|
|
259
|
+
].join("\n");
|
|
260
|
+
}
|
|
261
|
+
// Schedule bindings render as a reviewable crontab line; manual/event note how
|
|
262
|
+
// they fire instead.
|
|
263
|
+
const trigger = routine.trigger.kind === "schedule"
|
|
264
|
+
? [`# Arm with cron (${routine.trigger.tz ?? "local"} time):`, `${cron} ${invoke}`]
|
|
265
|
+
: routine.trigger.kind === "event"
|
|
266
|
+
? [`# Fires on event: ${routine.trigger.event ?? "<set trigger.event>"}`, `# Invoke per pass:`, invoke]
|
|
267
|
+
: [`# Manual — run per pass:`, invoke];
|
|
268
|
+
return [...header, ...trigger, ""].join("\n");
|
|
269
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import type { ModelRoster } from "./activeModel.js";
|
|
2
|
+
import type { Depth } from "./conductor/types.js";
|
|
3
|
+
import { type PricingTable } from "./loopBudget.js";
|
|
4
|
+
import type { RiskClassification, RiskConfidence } from "./riskClassify.js";
|
|
5
|
+
export type TierDefinition = {
|
|
6
|
+
name: string;
|
|
7
|
+
purpose: string;
|
|
8
|
+
/** Task types this tier serves, from the data file. */
|
|
9
|
+
taskTypes: readonly string[];
|
|
10
|
+
};
|
|
11
|
+
export type DifficultyPolicy = {
|
|
12
|
+
/** How the numbers below were arrived at, in the data file's own words. */
|
|
13
|
+
basis: string | null;
|
|
14
|
+
/** Signal name -> weight. */
|
|
15
|
+
signals: Record<string, number>;
|
|
16
|
+
/** Score at or above which a task moves up the ladder; null when the data file omits it. */
|
|
17
|
+
escalateAt: number | null;
|
|
18
|
+
/** Score at or below which a task moves down the ladder; null when omitted. */
|
|
19
|
+
downgradeAt: number | null;
|
|
20
|
+
maxTierMoves: number | null;
|
|
21
|
+
/** Difficulty band -> thinking budget in tokens (R-003: adaptive, never fixed). */
|
|
22
|
+
thinkingBudgetTokens: Record<string, number>;
|
|
23
|
+
};
|
|
24
|
+
export type EscalationPolicy = {
|
|
25
|
+
basis: string | null;
|
|
26
|
+
quality: {
|
|
27
|
+
retriesSameModel: number | null;
|
|
28
|
+
then: string | null;
|
|
29
|
+
maxTierMoves: number | null;
|
|
30
|
+
};
|
|
31
|
+
resilience: {
|
|
32
|
+
trigger: string | null;
|
|
33
|
+
direction: string | null;
|
|
34
|
+
maxTierMoves: number | null;
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
export type ReviewPolicy = {
|
|
38
|
+
requireDifferentFamily: boolean;
|
|
39
|
+
onSameFamily: string | null;
|
|
40
|
+
};
|
|
41
|
+
export type PanelPolicy = {
|
|
42
|
+
default: string | null;
|
|
43
|
+
requireComparableCandidates: boolean;
|
|
44
|
+
comparabilityMaxRankGap: number | null;
|
|
45
|
+
onIncomparable: string | null;
|
|
46
|
+
adjudication: string | null;
|
|
47
|
+
};
|
|
48
|
+
export type WorkflowLane = "direct" | "shape-fast" | "review-targeted" | "deep-explicit";
|
|
49
|
+
export type WorkflowRollout = "observe_only" | "enabled";
|
|
50
|
+
export type WorkflowPolicy = {
|
|
51
|
+
/** False when workflow policy data cannot safely drive execution. */
|
|
52
|
+
valid: boolean;
|
|
53
|
+
basis: string | null;
|
|
54
|
+
rollout: WorkflowRollout | null;
|
|
55
|
+
defaultLane: WorkflowLane | null;
|
|
56
|
+
conservativeLane: WorkflowLane | null;
|
|
57
|
+
laneOrder: readonly WorkflowLane[];
|
|
58
|
+
executableLanes: readonly WorkflowLane[];
|
|
59
|
+
targetedMinDomains: number | null;
|
|
60
|
+
depthLanes: Partial<Record<Depth, WorkflowLane>>;
|
|
61
|
+
ambiguityTerms: readonly string[];
|
|
62
|
+
domainSignals: Record<string, readonly string[]>;
|
|
63
|
+
limitations: Partial<Record<WorkflowLane, string>>;
|
|
64
|
+
};
|
|
65
|
+
export type RoutingPolicy = {
|
|
66
|
+
/** Basename of the data file, e.g. "routing-policy.yaml". */
|
|
67
|
+
source: string;
|
|
68
|
+
/** False when the data file is missing or unreadable — resolution degrades, never throws. */
|
|
69
|
+
available: boolean;
|
|
70
|
+
date: string | null;
|
|
71
|
+
staleAfterDays: number | null;
|
|
72
|
+
/** Where the tier definitions and task-type map come from. */
|
|
73
|
+
evidence: string | null;
|
|
74
|
+
/** Tier names weakest -> strongest. Difficulty moves along this list and nowhere else. */
|
|
75
|
+
ladder: readonly string[];
|
|
76
|
+
/** Where anything unresolvable lands. Null when the data file is unavailable. */
|
|
77
|
+
conservativeTier: string | null;
|
|
78
|
+
tiers: Record<string, TierDefinition>;
|
|
79
|
+
/** Task type -> tier name. */
|
|
80
|
+
taskTypes: Record<string, string>;
|
|
81
|
+
difficulty: DifficultyPolicy;
|
|
82
|
+
escalation: EscalationPolicy;
|
|
83
|
+
review: ReviewPolicy;
|
|
84
|
+
panel: PanelPolicy;
|
|
85
|
+
/** Request -> orchestration amount. Deliberately separate from model tiers. */
|
|
86
|
+
workflow: WorkflowPolicy;
|
|
87
|
+
/** Data keys that may never become routing inputs (R-011). */
|
|
88
|
+
forbiddenInputs: readonly string[];
|
|
89
|
+
/** Worded findings carried into every decision made against this policy. */
|
|
90
|
+
findings: readonly string[];
|
|
91
|
+
};
|
|
92
|
+
/** One tier decision, with the reason it landed there and any honesty findings. */
|
|
93
|
+
export type TierDecision = {
|
|
94
|
+
/** Resolved tier name, or null when there is no policy to resolve against. */
|
|
95
|
+
tier: string | null;
|
|
96
|
+
/** Plain-text reason, safe to show a reader who did not watch the run. */
|
|
97
|
+
reason: string;
|
|
98
|
+
/** True when the decision fell back to the conservative tier rather than being mapped. */
|
|
99
|
+
degraded: boolean;
|
|
100
|
+
findings: readonly string[];
|
|
101
|
+
};
|
|
102
|
+
export type WorkflowLaneDecision = {
|
|
103
|
+
/** The lane the caller may execute now under the rollout policy. */
|
|
104
|
+
lane: WorkflowLane;
|
|
105
|
+
/** The smallest adequate lane selected before rollout gating. */
|
|
106
|
+
recommended_lane: WorkflowLane;
|
|
107
|
+
reason: string;
|
|
108
|
+
signals: readonly string[];
|
|
109
|
+
confidence: RiskConfidence;
|
|
110
|
+
conservative_escalation: boolean;
|
|
111
|
+
explicit: boolean;
|
|
112
|
+
executable: boolean;
|
|
113
|
+
rollout: WorkflowRollout;
|
|
114
|
+
/** Routing is deterministic and never spends a model call. */
|
|
115
|
+
routing_model_calls: 0;
|
|
116
|
+
boundary?: "B5";
|
|
117
|
+
required_capability?: "research";
|
|
118
|
+
limitation?: string;
|
|
119
|
+
};
|
|
120
|
+
export type WorkflowLaneInput = {
|
|
121
|
+
work: string;
|
|
122
|
+
explicitLane?: WorkflowLane;
|
|
123
|
+
explicitDepth?: Depth;
|
|
124
|
+
risk: RiskClassification;
|
|
125
|
+
researchRequested?: boolean;
|
|
126
|
+
};
|
|
127
|
+
export type RoutingContext = {
|
|
128
|
+
policy: RoutingPolicy;
|
|
129
|
+
roster: ModelRoster;
|
|
130
|
+
/** The price table backing the policy — a stale one degrades routing (R-013). */
|
|
131
|
+
pricing: PricingTable;
|
|
132
|
+
now?: Date;
|
|
133
|
+
};
|
|
134
|
+
/**
|
|
135
|
+
* Every path in `value` whose key is a forbidden routing input (R-011). Exported
|
|
136
|
+
* so any surface that accepts routing data — the policy file, the roster, a
|
|
137
|
+
* future refresh proposal — can ask the same question of it. The node that
|
|
138
|
+
* *declares* the forbidden list is skipped; naming a thing is not using it.
|
|
139
|
+
*/
|
|
140
|
+
export declare function findForbiddenInputs(value: unknown, forbidden: readonly string[]): string[];
|
|
141
|
+
export declare function loadRoutingPolicy(root?: string): RoutingPolicy;
|
|
142
|
+
/** Days between the policy date and `now`; Infinity when the date is unusable. */
|
|
143
|
+
export declare function routingPolicyAgeDays(policy: RoutingPolicy, now?: Date): number;
|
|
144
|
+
/** True when the policy is older than its own declared refresh window (RISK-002). */
|
|
145
|
+
export declare function routingPolicyIsStale(policy: RoutingPolicy, now?: Date): boolean;
|
|
146
|
+
/**
|
|
147
|
+
* The tier a task type maps to, before difficulty adjusts it (R-002 step 1). An
|
|
148
|
+
* unmapped task type lands conservatively and says so — never on the cheap end
|
|
149
|
+
* by default.
|
|
150
|
+
*/
|
|
151
|
+
export declare function tierForTaskType(ctx: RoutingContext, taskType: string): TierDecision;
|
|
152
|
+
/**
|
|
153
|
+
* The tier a specific model sits in. Tier membership is roster data: a model the
|
|
154
|
+
* roster does not place, or does not know at all, resolves conservatively with a
|
|
155
|
+
* worded finding rather than being guessed into a tier (R-013).
|
|
156
|
+
*/
|
|
157
|
+
export declare function tierForModel(ctx: RoutingContext, modelId: string): TierDecision;
|
|
158
|
+
/** Roster models placed in `tier`, in roster order. Empty is a real answer, not an error. */
|
|
159
|
+
export declare function modelsForTier(ctx: RoutingContext, tier: string): string[];
|
|
160
|
+
/**
|
|
161
|
+
* Move `tier` `steps` rungs along the ladder (R-003 allows at most one, and the
|
|
162
|
+
* policy data says how many). A tier that is off the ladder — a different axis
|
|
163
|
+
* rather than a stronger rung — does not move, and says why. Movement stops at
|
|
164
|
+
* the ends rather than wrapping.
|
|
165
|
+
*/
|
|
166
|
+
export declare function adjustTier(policy: RoutingPolicy, tier: string, steps: number): TierDecision;
|
|
167
|
+
/**
|
|
168
|
+
* Choose how much orchestration a request earns. This is a pure, deterministic
|
|
169
|
+
* decision over committed policy data plus the existing B3 classifier: it never
|
|
170
|
+
* calls a model merely to decide that no extra model call is needed.
|
|
171
|
+
*/
|
|
172
|
+
export declare function resolveWorkflowLane(policy: RoutingPolicy, input: WorkflowLaneInput): WorkflowLaneDecision;
|