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,165 @@
|
|
|
1
|
+
import { readFileSync, readdirSync, existsSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { parse as parseYaml } from "./vendorYaml.js";
|
|
4
|
+
// The eval contract (foo-eval-system-v0 R-001). An eval is a typed foo record
|
|
5
|
+
// that states its criterion in plain language; how a criterion gets scored is
|
|
6
|
+
// the runner's business, not the record's. The lint below keeps runner and
|
|
7
|
+
// template vocabulary out of records, which is what lets the judge transport
|
|
8
|
+
// change without rewriting a corpus.
|
|
9
|
+
export const EVAL_RECORD_SCHEMA_VERSION = "0.1";
|
|
10
|
+
/**
|
|
11
|
+
* Where golden sets, owner labels, and seeded artifacts live (R-008).
|
|
12
|
+
*
|
|
13
|
+
* The harness is open source; the corpus is not, and it never enters git
|
|
14
|
+
* history at all — committed records reference it by pointer and content hash
|
|
15
|
+
* only. This path sits under the already-ignored `.foo/`, and two pinned tests
|
|
16
|
+
* hold the line: nothing under it may be tracked, and the published payload may
|
|
17
|
+
* never contain it. Path exclusion cannot rest on the secret scanner, which
|
|
18
|
+
* waves ordinary prose through by design.
|
|
19
|
+
*/
|
|
20
|
+
export const EVAL_CORPUS_HOME = ".foo/eval-corpus";
|
|
21
|
+
// Runner vocabulary that must never appear in a record (V-006). A record
|
|
22
|
+
// states a criterion; the moment it carries a runner's config format, changing
|
|
23
|
+
// how evals are scored becomes a corpus migration. Token boundaries follow the
|
|
24
|
+
// humanSurfaceEval lesson: a substring is not evidence the phrase is present.
|
|
25
|
+
const ENGINE_VOCABULARY = [
|
|
26
|
+
{ pattern: /\{\{\s*[\w.]+\s*\}\}/, label: "template placeholder syntax" },
|
|
27
|
+
{ pattern: /(?<![\w-])llm-rubric(?![\w-])/i, label: "third-party assertion type" },
|
|
28
|
+
{ pattern: /(?<![\w-])(?:model-graded-closedqa|answer-relevance|context-faithfulness)(?![\w-])/i, label: "third-party assertion type" },
|
|
29
|
+
{ pattern: /(?<![\w-])assert(?:ions)?\s*:/i, label: "runner config key" },
|
|
30
|
+
{ pattern: /(?<![\w-])(?:providers|defaultTest|testCases)\s*:/, label: "runner config key" },
|
|
31
|
+
{ pattern: /(?<![\w-])promptfoo(?![\w-])/i, label: "third-party engine name" },
|
|
32
|
+
];
|
|
33
|
+
const RECORD_ID = /^E-\d{3,4}$/;
|
|
34
|
+
function lintEngineVocabulary(record, findings) {
|
|
35
|
+
const surfaces = [
|
|
36
|
+
["pass", record.pass],
|
|
37
|
+
["target", record.target],
|
|
38
|
+
["judge.prompt", record.judge?.prompt],
|
|
39
|
+
];
|
|
40
|
+
for (const [field, text] of surfaces) {
|
|
41
|
+
if (!text)
|
|
42
|
+
continue;
|
|
43
|
+
for (const { pattern, label } of ENGINE_VOCABULARY) {
|
|
44
|
+
const match = text.match(pattern);
|
|
45
|
+
if (match) {
|
|
46
|
+
findings.push({
|
|
47
|
+
rule: "ER-002",
|
|
48
|
+
record: record.id ?? "(missing id)",
|
|
49
|
+
field,
|
|
50
|
+
phrase: match[0],
|
|
51
|
+
message: `Engine vocabulary (${label}) belongs only in compiler output; state the check in productfoo's contract terms.`,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function validateRecord(raw, findings) {
|
|
58
|
+
if (typeof raw !== "object" || raw === null) {
|
|
59
|
+
findings.push({
|
|
60
|
+
rule: "ER-001",
|
|
61
|
+
record: "(unparsed)",
|
|
62
|
+
field: "(root)",
|
|
63
|
+
phrase: String(raw),
|
|
64
|
+
message: "An eval record must be a mapping.",
|
|
65
|
+
});
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
const record = raw;
|
|
69
|
+
const problems = [];
|
|
70
|
+
if (record.schema_version !== EVAL_RECORD_SCHEMA_VERSION) {
|
|
71
|
+
problems.push(["schema_version", `expected ${EVAL_RECORD_SCHEMA_VERSION}`]);
|
|
72
|
+
}
|
|
73
|
+
if (typeof record.id !== "string" || !RECORD_ID.test(record.id)) {
|
|
74
|
+
problems.push(["id", "expected an E-001 style id"]);
|
|
75
|
+
}
|
|
76
|
+
if (typeof record.suite !== "string" || record.suite.length === 0) {
|
|
77
|
+
problems.push(["suite", "required"]);
|
|
78
|
+
}
|
|
79
|
+
if (typeof record.target !== "string" || record.target.length === 0) {
|
|
80
|
+
problems.push(["target", "required"]);
|
|
81
|
+
}
|
|
82
|
+
if (record.kind !== "deterministic" && record.kind !== "judge") {
|
|
83
|
+
problems.push(["kind", "expected deterministic or judge"]);
|
|
84
|
+
}
|
|
85
|
+
if (typeof record.pass !== "string" || record.pass.length === 0) {
|
|
86
|
+
problems.push(["pass", "a binary pass definition is required"]);
|
|
87
|
+
}
|
|
88
|
+
if (record.kind === "deterministic" && typeof record.check?.id !== "string") {
|
|
89
|
+
problems.push(["check", "a deterministic record names its registered check"]);
|
|
90
|
+
}
|
|
91
|
+
if (record.kind === "judge") {
|
|
92
|
+
if (typeof record.judge?.prompt !== "string" || record.judge.prompt.length === 0) {
|
|
93
|
+
problems.push(["judge.prompt", "required"]);
|
|
94
|
+
}
|
|
95
|
+
if (typeof record.judge?.model !== "string" || record.judge.model.length === 0) {
|
|
96
|
+
problems.push(["judge.model", "a judge pins its model"]);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const provenance = record.provenance;
|
|
100
|
+
const provenanceOk = (provenance?.source === "roast-run" && typeof provenance.run === "string") ||
|
|
101
|
+
(provenance?.source === "observed-failure" && typeof provenance.ref === "string");
|
|
102
|
+
if (!provenanceOk) {
|
|
103
|
+
problems.push(["provenance", "every eval names the roast run or observed failure that produced it"]);
|
|
104
|
+
}
|
|
105
|
+
if (typeof record.approved !== "boolean") {
|
|
106
|
+
problems.push(["approved", "required — unapproved records never run"]);
|
|
107
|
+
}
|
|
108
|
+
for (const [field, message] of problems) {
|
|
109
|
+
findings.push({
|
|
110
|
+
rule: "ER-001",
|
|
111
|
+
record: typeof record.id === "string" ? record.id : "(missing id)",
|
|
112
|
+
field,
|
|
113
|
+
phrase: "",
|
|
114
|
+
message,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
if (problems.length > 0)
|
|
118
|
+
return undefined;
|
|
119
|
+
lintEngineVocabulary(record, findings);
|
|
120
|
+
return record;
|
|
121
|
+
}
|
|
122
|
+
/** Validate a set of parsed records: shape, unique ids, engine-boundary lint. */
|
|
123
|
+
export function validateEvalRecords(rawRecords) {
|
|
124
|
+
const findings = [];
|
|
125
|
+
const records = [];
|
|
126
|
+
const seen = new Set();
|
|
127
|
+
for (const raw of rawRecords) {
|
|
128
|
+
const record = validateRecord(raw, findings);
|
|
129
|
+
if (!record)
|
|
130
|
+
continue;
|
|
131
|
+
if (seen.has(record.id)) {
|
|
132
|
+
findings.push({
|
|
133
|
+
rule: "ER-003",
|
|
134
|
+
record: record.id,
|
|
135
|
+
field: "id",
|
|
136
|
+
phrase: record.id,
|
|
137
|
+
message: "Duplicate eval id — ids are minted once and never reused.",
|
|
138
|
+
});
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
seen.add(record.id);
|
|
142
|
+
records.push(record);
|
|
143
|
+
}
|
|
144
|
+
return { ok: findings.length === 0, records, findings };
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Load a packet's eval suite from `specs/<packet>/evals/*.yaml` (R-001: records
|
|
148
|
+
* live in the target's packet). Missing directory is an empty suite, not an error.
|
|
149
|
+
*/
|
|
150
|
+
export function loadEvalSuite(packetDir) {
|
|
151
|
+
const home = path.join(packetDir, "evals");
|
|
152
|
+
if (!existsSync(home))
|
|
153
|
+
return { ok: true, records: [], findings: [] };
|
|
154
|
+
const rawRecords = [];
|
|
155
|
+
for (const entry of readdirSync(home).sort()) {
|
|
156
|
+
if (!entry.endsWith(".yaml") && !entry.endsWith(".yml"))
|
|
157
|
+
continue;
|
|
158
|
+
const parsed = parseYaml(readFileSync(path.join(home, entry), "utf8"));
|
|
159
|
+
if (Array.isArray(parsed))
|
|
160
|
+
rawRecords.push(...parsed);
|
|
161
|
+
else if (parsed != null)
|
|
162
|
+
rawRecords.push(parsed);
|
|
163
|
+
}
|
|
164
|
+
return validateEvalRecords(rawRecords);
|
|
165
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { type EvalCheckKind, type EvalRecord } from "./evalRecord.js";
|
|
2
|
+
/** A roast run, durable enough that a compiled eval can cite it years later. */
|
|
3
|
+
export type RoastRunRecord = {
|
|
4
|
+
schema_version: string;
|
|
5
|
+
/** `roast-YYYY-MM-DD-<slug>`; minted by `foo roast --deep`, never by hand. */
|
|
6
|
+
run: string;
|
|
7
|
+
/** Artifact path or named behavior the roast examined. */
|
|
8
|
+
target: string;
|
|
9
|
+
at: string;
|
|
10
|
+
candidates: RoastCandidate[];
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* One candidate check as the roast named it. Deliberately three fields: the
|
|
14
|
+
* roast decides WHAT to judge, never how it is scored (R-001).
|
|
15
|
+
*/
|
|
16
|
+
export type RoastCandidate = {
|
|
17
|
+
kind: EvalCheckKind;
|
|
18
|
+
/** Binary pass definition, plain language. This is what the owner approves. */
|
|
19
|
+
pass: string;
|
|
20
|
+
/** Optional narrower target; defaults to the run's target. */
|
|
21
|
+
target?: string;
|
|
22
|
+
/** Deterministic only: which registered check, and its params. */
|
|
23
|
+
check?: {
|
|
24
|
+
id: string;
|
|
25
|
+
params?: Record<string, unknown>;
|
|
26
|
+
};
|
|
27
|
+
/** Judge only: the criterion and the pinned model. */
|
|
28
|
+
judge?: {
|
|
29
|
+
prompt: string;
|
|
30
|
+
model: string;
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
export type RoastCompileFinding = {
|
|
34
|
+
rule: "RC-001" | "RC-002" | "RC-003" | "RC-004";
|
|
35
|
+
candidate: number;
|
|
36
|
+
message: string;
|
|
37
|
+
};
|
|
38
|
+
export type RoastCompileResult = {
|
|
39
|
+
ok: boolean;
|
|
40
|
+
run: string;
|
|
41
|
+
/** Compiled records, always unapproved: approval is a separate human act. */
|
|
42
|
+
records: EvalRecord[];
|
|
43
|
+
findings: RoastCompileFinding[];
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Mint a run id from the target and the day. Content-derived rather than
|
|
47
|
+
* random so the same roast of the same artifact on the same day is the same
|
|
48
|
+
* run — a re-roast that finds the same check should not fork provenance.
|
|
49
|
+
*/
|
|
50
|
+
export declare function mintRoastRunId(target: string, at?: Date): string;
|
|
51
|
+
/**
|
|
52
|
+
* Read a roast run record. The candidate block is data the roast emitted, so it
|
|
53
|
+
* is parsed defensively: a malformed block is a finding, never a throw that
|
|
54
|
+
* loses the rest of the run.
|
|
55
|
+
*/
|
|
56
|
+
export declare function parseRoastRun(raw: unknown): {
|
|
57
|
+
run: RoastRunRecord | null;
|
|
58
|
+
findings: RoastCompileFinding[];
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Compile a roast run's candidates into eval records.
|
|
62
|
+
*
|
|
63
|
+
* Every record comes out `approved: false`. That is the whole point of the
|
|
64
|
+
* separation: compiling is mechanical, approving is judgment, and a suite that
|
|
65
|
+
* grows without anyone saying yes is the sprawl R-004 is guarding against.
|
|
66
|
+
*/
|
|
67
|
+
export declare function compileRoastRun(run: RoastRunRecord, suite: string, options?: {
|
|
68
|
+
startAt?: number;
|
|
69
|
+
}): RoastCompileResult;
|
|
70
|
+
/**
|
|
71
|
+
* The approval surface: each check in the roast's own words, with where it came
|
|
72
|
+
* from. Rendered for a human to read and answer, never for a machine to parse.
|
|
73
|
+
*/
|
|
74
|
+
export declare function renderApprovalRequest(result: RoastCompileResult, glyphsOn: boolean): string;
|
|
75
|
+
/** Where a packet keeps its eval records. Matches loadEvalSuite's home. */
|
|
76
|
+
export declare function evalHome(packetDir: string): string;
|
|
77
|
+
/**
|
|
78
|
+
* Write compiled records into the packet, merging by id so a re-compile of the
|
|
79
|
+
* same roast never silently drops an approval the owner already gave.
|
|
80
|
+
*/
|
|
81
|
+
export declare function writeCompiledSuite(packetDir: string, records: EvalRecord[]): {
|
|
82
|
+
written: number;
|
|
83
|
+
kept: number;
|
|
84
|
+
};
|
|
85
|
+
/** Flip one record's approval. Returns null when the id is not in the suite. */
|
|
86
|
+
export declare function setApproval(packetDir: string, id: string, approved: boolean): {
|
|
87
|
+
record: EvalRecord;
|
|
88
|
+
} | null;
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
// foo-eval-system-v0 T-005 (R-004): roast is the authoring front door.
|
|
2
|
+
//
|
|
3
|
+
// A roast already ends by naming what the next generation should be judged
|
|
4
|
+
// against. That naming was prose, so it evaporated: nothing carried a candidate
|
|
5
|
+
// check from the roast that found it to the suite that would run it, and the
|
|
6
|
+
// only way to get an eval was to hand-author one — which R-001 forbids reaching
|
|
7
|
+
// engine configuration, and which nobody does twice.
|
|
8
|
+
//
|
|
9
|
+
// So `foo roast --deep` mints a run id, the roast emits a machine-readable
|
|
10
|
+
// candidate block beside its prose, and this module compiles that block into
|
|
11
|
+
// eval records whose provenance points back at the run. The prose stays the
|
|
12
|
+
// approval and provenance surface: a candidate arrives unapproved, is shown to
|
|
13
|
+
// the owner in the roast's own words with its origin, and only an explicit
|
|
14
|
+
// per-check approval lets it run.
|
|
15
|
+
import { createHash } from "node:crypto";
|
|
16
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
17
|
+
import path from "node:path";
|
|
18
|
+
import { parse as parseYaml } from "./vendorYaml.js";
|
|
19
|
+
import { EVAL_RECORD_SCHEMA_VERSION, } from "./evalRecord.js";
|
|
20
|
+
import { DETERMINISTIC_CHECK_IDS } from "./evalChecks.js";
|
|
21
|
+
const RUN_ID = /^roast-\d{4}-\d{2}-\d{2}-[a-z0-9][a-z0-9-]*$/;
|
|
22
|
+
/**
|
|
23
|
+
* Mint a run id from the target and the day. Content-derived rather than
|
|
24
|
+
* random so the same roast of the same artifact on the same day is the same
|
|
25
|
+
* run — a re-roast that finds the same check should not fork provenance.
|
|
26
|
+
*/
|
|
27
|
+
export function mintRoastRunId(target, at = new Date()) {
|
|
28
|
+
const day = at.toISOString().slice(0, 10);
|
|
29
|
+
const slugSource = path.basename(target).toLowerCase();
|
|
30
|
+
const slug = slugSource
|
|
31
|
+
.replace(/\.[a-z0-9]+$/, "")
|
|
32
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
33
|
+
.replace(/^-+|-+$/g, "")
|
|
34
|
+
.slice(0, 40);
|
|
35
|
+
const stable = slug || createHash("sha256").update(target).digest("hex").slice(0, 8);
|
|
36
|
+
return `roast-${day}-${stable}`;
|
|
37
|
+
}
|
|
38
|
+
function isRecord(value) {
|
|
39
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Read a roast run record. The candidate block is data the roast emitted, so it
|
|
43
|
+
* is parsed defensively: a malformed block is a finding, never a throw that
|
|
44
|
+
* loses the rest of the run.
|
|
45
|
+
*/
|
|
46
|
+
export function parseRoastRun(raw) {
|
|
47
|
+
const findings = [];
|
|
48
|
+
if (!isRecord(raw)) {
|
|
49
|
+
findings.push({ rule: "RC-001", candidate: -1, message: "roast run record is not an object" });
|
|
50
|
+
return { run: null, findings };
|
|
51
|
+
}
|
|
52
|
+
const run = typeof raw.run === "string" ? raw.run : "";
|
|
53
|
+
if (!RUN_ID.test(run)) {
|
|
54
|
+
findings.push({
|
|
55
|
+
rule: "RC-001",
|
|
56
|
+
candidate: -1,
|
|
57
|
+
message: `roast run id "${run}" is not a minted id (expected roast-YYYY-MM-DD-<slug>; run \`foo roast --deep\` to mint one)`,
|
|
58
|
+
});
|
|
59
|
+
return { run: null, findings };
|
|
60
|
+
}
|
|
61
|
+
if (typeof raw.target !== "string" || raw.target.trim() === "") {
|
|
62
|
+
findings.push({ rule: "RC-001", candidate: -1, message: "roast run record names no target" });
|
|
63
|
+
return { run: null, findings };
|
|
64
|
+
}
|
|
65
|
+
const candidates = Array.isArray(raw.candidates) ? raw.candidates : [];
|
|
66
|
+
return {
|
|
67
|
+
run: {
|
|
68
|
+
schema_version: typeof raw.schema_version === "string" ? raw.schema_version : EVAL_RECORD_SCHEMA_VERSION,
|
|
69
|
+
run,
|
|
70
|
+
target: raw.target,
|
|
71
|
+
at: typeof raw.at === "string" ? raw.at : new Date().toISOString(),
|
|
72
|
+
candidates: candidates,
|
|
73
|
+
},
|
|
74
|
+
findings,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Compile a roast run's candidates into eval records.
|
|
79
|
+
*
|
|
80
|
+
* Every record comes out `approved: false`. That is the whole point of the
|
|
81
|
+
* separation: compiling is mechanical, approving is judgment, and a suite that
|
|
82
|
+
* grows without anyone saying yes is the sprawl R-004 is guarding against.
|
|
83
|
+
*/
|
|
84
|
+
export function compileRoastRun(run, suite, options = {}) {
|
|
85
|
+
const findings = [];
|
|
86
|
+
const records = [];
|
|
87
|
+
let next = options.startAt ?? 1;
|
|
88
|
+
run.candidates.forEach((candidate, index) => {
|
|
89
|
+
if (!isRecord(candidate)) {
|
|
90
|
+
findings.push({ rule: "RC-002", candidate: index, message: "candidate is not an object" });
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (candidate.kind !== "deterministic" && candidate.kind !== "judge") {
|
|
94
|
+
findings.push({ rule: "RC-002", candidate: index, message: `candidate kind must be deterministic or judge, got "${String(candidate.kind)}"` });
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (typeof candidate.pass !== "string" || candidate.pass.trim() === "") {
|
|
98
|
+
findings.push({ rule: "RC-002", candidate: index, message: "candidate has no binary pass definition — the roast must say what passing means" });
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (candidate.kind === "deterministic") {
|
|
102
|
+
const id = candidate.check?.id;
|
|
103
|
+
if (!id) {
|
|
104
|
+
findings.push({ rule: "RC-003", candidate: index, message: "deterministic candidate names no registered check id" });
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (!DETERMINISTIC_CHECK_IDS.includes(id)) {
|
|
108
|
+
findings.push({
|
|
109
|
+
rule: "RC-003",
|
|
110
|
+
candidate: index,
|
|
111
|
+
message: `"${id}" is not a registered deterministic check (registered: ${DETERMINISTIC_CHECK_IDS.join(", ")})`,
|
|
112
|
+
});
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
if (!candidate.judge?.prompt || !candidate.judge.model) {
|
|
118
|
+
findings.push({ rule: "RC-004", candidate: index, message: "judge candidate needs both a criterion and a pinned model" });
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
records.push({
|
|
123
|
+
schema_version: EVAL_RECORD_SCHEMA_VERSION,
|
|
124
|
+
id: `E-${String(next).padStart(3, "0")}`,
|
|
125
|
+
suite,
|
|
126
|
+
target: candidate.target ?? run.target,
|
|
127
|
+
kind: candidate.kind,
|
|
128
|
+
pass: candidate.pass,
|
|
129
|
+
...(candidate.kind === "deterministic" ? { check: candidate.check } : {}),
|
|
130
|
+
// A compiled judge is uncalibrated by construction: no calibration
|
|
131
|
+
// reference is minted here, so R-005 keeps its verdicts advisory-only
|
|
132
|
+
// until a real blind pass certifies it.
|
|
133
|
+
...(candidate.kind === "judge" ? { judge: { prompt: candidate.judge.prompt, model: candidate.judge.model } } : {}),
|
|
134
|
+
provenance: { source: "roast-run", run: run.run },
|
|
135
|
+
approved: false,
|
|
136
|
+
});
|
|
137
|
+
next += 1;
|
|
138
|
+
});
|
|
139
|
+
return { ok: findings.length === 0, run: run.run, records, findings };
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* The approval surface: each check in the roast's own words, with where it came
|
|
143
|
+
* from. Rendered for a human to read and answer, never for a machine to parse.
|
|
144
|
+
*/
|
|
145
|
+
export function renderApprovalRequest(result, glyphsOn) {
|
|
146
|
+
const lines = [];
|
|
147
|
+
const mark = glyphsOn ? "✋ needs your input" : "needs your input";
|
|
148
|
+
lines.push(`${mark} — ${result.records.length} check(s) compiled from ${result.run}. Nothing runs until you approve it.`);
|
|
149
|
+
lines.push("");
|
|
150
|
+
for (const record of result.records) {
|
|
151
|
+
lines.push(` ${record.id} [${record.kind}] ${record.target}`);
|
|
152
|
+
lines.push(` passes when: ${record.pass}`);
|
|
153
|
+
lines.push(` from your roast: ${result.run}`);
|
|
154
|
+
lines.push("");
|
|
155
|
+
}
|
|
156
|
+
if (result.findings.length > 0) {
|
|
157
|
+
lines.push("Not compiled:");
|
|
158
|
+
for (const finding of result.findings) {
|
|
159
|
+
const which = finding.candidate >= 0 ? `candidate ${finding.candidate + 1}` : "the run record";
|
|
160
|
+
lines.push(` ${which}: ${finding.message}`);
|
|
161
|
+
}
|
|
162
|
+
lines.push("");
|
|
163
|
+
}
|
|
164
|
+
lines.push("Approve one: foo eval approve <packet-dir> --check E-001 --by <who>");
|
|
165
|
+
lines.push("Reject one: foo eval reject <packet-dir> --check E-001 --by <who> --reason <why>");
|
|
166
|
+
return lines.join("\n");
|
|
167
|
+
}
|
|
168
|
+
const SUITE_FILE = "compiled.yaml";
|
|
169
|
+
/** Where a packet keeps its eval records. Matches loadEvalSuite's home. */
|
|
170
|
+
export function evalHome(packetDir) {
|
|
171
|
+
return path.join(packetDir, "evals");
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Write compiled records into the packet, merging by id so a re-compile of the
|
|
175
|
+
* same roast never silently drops an approval the owner already gave.
|
|
176
|
+
*/
|
|
177
|
+
export function writeCompiledSuite(packetDir, records) {
|
|
178
|
+
const home = evalHome(packetDir);
|
|
179
|
+
mkdirSync(home, { recursive: true });
|
|
180
|
+
const file = path.join(home, SUITE_FILE);
|
|
181
|
+
const existing = existsSync(file)
|
|
182
|
+
? (parseYaml(readFileSync(file, "utf8")) ?? [])
|
|
183
|
+
: [];
|
|
184
|
+
const byId = new Map(existing.map((record) => [record.id, record]));
|
|
185
|
+
let written = 0;
|
|
186
|
+
let kept = 0;
|
|
187
|
+
for (const record of records) {
|
|
188
|
+
const prior = byId.get(record.id);
|
|
189
|
+
if (prior) {
|
|
190
|
+
// An approval is a human act; re-compiling must never revoke or re-grant it.
|
|
191
|
+
byId.set(record.id, { ...record, approved: prior.approved });
|
|
192
|
+
kept += 1;
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
byId.set(record.id, record);
|
|
196
|
+
written += 1;
|
|
197
|
+
}
|
|
198
|
+
const merged = [...byId.values()].sort((left, right) => left.id.localeCompare(right.id));
|
|
199
|
+
writeFileSync(file, `${merged.map((record) => yamlRecord(record)).join("\n")}\n`);
|
|
200
|
+
return { written, kept };
|
|
201
|
+
}
|
|
202
|
+
/** Minimal, stable YAML emission — no dependency on a writer's formatting. */
|
|
203
|
+
function yamlRecord(record) {
|
|
204
|
+
const lines = [
|
|
205
|
+
`- schema_version: "${record.schema_version}"`,
|
|
206
|
+
` id: "${record.id}"`,
|
|
207
|
+
` suite: "${record.suite}"`,
|
|
208
|
+
` target: ${JSON.stringify(record.target)}`,
|
|
209
|
+
` kind: "${record.kind}"`,
|
|
210
|
+
` pass: ${JSON.stringify(record.pass)}`,
|
|
211
|
+
];
|
|
212
|
+
if (record.check) {
|
|
213
|
+
lines.push(` check:`);
|
|
214
|
+
lines.push(` id: "${record.check.id}"`);
|
|
215
|
+
if (record.check.params)
|
|
216
|
+
lines.push(` params: ${JSON.stringify(record.check.params)}`);
|
|
217
|
+
}
|
|
218
|
+
if (record.judge) {
|
|
219
|
+
lines.push(` judge:`);
|
|
220
|
+
lines.push(` prompt: ${JSON.stringify(record.judge.prompt)}`);
|
|
221
|
+
lines.push(` model: "${record.judge.model}"`);
|
|
222
|
+
if (record.judge.calibration)
|
|
223
|
+
lines.push(` calibration: "${record.judge.calibration}"`);
|
|
224
|
+
}
|
|
225
|
+
lines.push(` provenance:`);
|
|
226
|
+
if (record.provenance.source === "roast-run") {
|
|
227
|
+
lines.push(` source: "roast-run"`);
|
|
228
|
+
lines.push(` run: "${record.provenance.run}"`);
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
lines.push(` source: "observed-failure"`);
|
|
232
|
+
lines.push(` ref: ${JSON.stringify(record.provenance.ref)}`);
|
|
233
|
+
}
|
|
234
|
+
lines.push(` approved: ${record.approved}`);
|
|
235
|
+
return lines.join("\n");
|
|
236
|
+
}
|
|
237
|
+
/** Flip one record's approval. Returns null when the id is not in the suite. */
|
|
238
|
+
export function setApproval(packetDir, id, approved) {
|
|
239
|
+
const file = path.join(evalHome(packetDir), SUITE_FILE);
|
|
240
|
+
if (!existsSync(file))
|
|
241
|
+
return null;
|
|
242
|
+
const records = (parseYaml(readFileSync(file, "utf8")) ?? []);
|
|
243
|
+
const target = records.find((record) => record.id === id);
|
|
244
|
+
if (!target)
|
|
245
|
+
return null;
|
|
246
|
+
target.approved = approved;
|
|
247
|
+
writeFileSync(file, `${records.map((record) => yamlRecord(record)).join("\n")}\n`);
|
|
248
|
+
return { record: target };
|
|
249
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { type SourceId } from "./sourceIngress.js";
|
|
2
|
+
export interface SweepSource {
|
|
3
|
+
source_id: SourceId;
|
|
4
|
+
source_type: "github_pr" | "linear_issue";
|
|
5
|
+
url: string;
|
|
6
|
+
title?: string;
|
|
7
|
+
/** review comments / issue thread bodies, in order */
|
|
8
|
+
comments: string[];
|
|
9
|
+
}
|
|
10
|
+
export type SuggestedRoute = "guidance" | "lint" | "example" | "eval" | "no-change";
|
|
11
|
+
export interface Candidate {
|
|
12
|
+
id: string;
|
|
13
|
+
summary: string;
|
|
14
|
+
source_url: string;
|
|
15
|
+
source_type: SweepSource["source_type"];
|
|
16
|
+
suggested_route: SuggestedRoute;
|
|
17
|
+
rationale: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function suggestRoute(text: string): {
|
|
20
|
+
route: SuggestedRoute;
|
|
21
|
+
rationale: string;
|
|
22
|
+
};
|
|
23
|
+
export declare function sweepCandidates(governingPacketDir: string, sources: SweepSource[]): {
|
|
24
|
+
candidates: Candidate[];
|
|
25
|
+
refused: Array<{
|
|
26
|
+
source_id: SourceId;
|
|
27
|
+
reason: string;
|
|
28
|
+
}>;
|
|
29
|
+
};
|
|
30
|
+
export declare function emitPendingPacket(root: string, name: string, candidates: Candidate[], at: string): string;
|
|
31
|
+
export type TriageBucket = "now" | "next" | "later";
|
|
32
|
+
export declare function suggestTriage(text: string): {
|
|
33
|
+
bucket: TriageBucket;
|
|
34
|
+
rationale: string;
|
|
35
|
+
};
|
|
36
|
+
export interface ReplayReport {
|
|
37
|
+
schema_version: "0.1";
|
|
38
|
+
packet: string;
|
|
39
|
+
since: string;
|
|
40
|
+
events: number;
|
|
41
|
+
by_type: Record<string, number>;
|
|
42
|
+
decisions: Array<{
|
|
43
|
+
at: string;
|
|
44
|
+
by: string;
|
|
45
|
+
summary: string;
|
|
46
|
+
}>;
|
|
47
|
+
proofs: Array<{
|
|
48
|
+
at: string;
|
|
49
|
+
task: string;
|
|
50
|
+
}>;
|
|
51
|
+
}
|
|
52
|
+
export declare function replayWindow(packetDir: string, since: string): ReplayReport;
|
|
53
|
+
export type Disposition = "accept" | "override" | "reject";
|
|
54
|
+
export declare function recordDisposition(packetDir: string, candidateId: string, opts: {
|
|
55
|
+
action: Disposition;
|
|
56
|
+
route?: SuggestedRoute;
|
|
57
|
+
by: string;
|
|
58
|
+
at?: string;
|
|
59
|
+
}): {
|
|
60
|
+
action: Disposition;
|
|
61
|
+
route?: SuggestedRoute;
|
|
62
|
+
};
|
|
63
|
+
export declare function runEvidenceCommand(root: string, args: string[]): {
|
|
64
|
+
output: string;
|
|
65
|
+
exitCode: number;
|
|
66
|
+
};
|