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,57 @@
|
|
|
1
|
+
import { type DiscoveryWrite } from "./discoveryArtifacts.js";
|
|
2
|
+
import { type DiscoveryEvidenceEnvelope } from "./discoveryEvidence.js";
|
|
3
|
+
import { type DiscoveryReceiptStatusEndpoint } from "./discoveryProcess.js";
|
|
4
|
+
import type { TrustStore } from "./trustEnvelope.js";
|
|
5
|
+
export type DiscoveryIndexEntry = {
|
|
6
|
+
id?: string;
|
|
7
|
+
path: string;
|
|
8
|
+
excerpt: string;
|
|
9
|
+
text: string;
|
|
10
|
+
};
|
|
11
|
+
export type DiscoveryIndex = {
|
|
12
|
+
entries: DiscoveryIndexEntry[];
|
|
13
|
+
};
|
|
14
|
+
export type ReconciliationKind = "exact" | "semantic_candidate" | "drift_candidate" | "draft_proposal";
|
|
15
|
+
export type ReconciliationOutcome = {
|
|
16
|
+
finding_id: string;
|
|
17
|
+
kind: ReconciliationKind;
|
|
18
|
+
canonical: boolean;
|
|
19
|
+
target_id?: string;
|
|
20
|
+
target_path?: string;
|
|
21
|
+
existing_excerpt?: string;
|
|
22
|
+
confidence: "low" | "medium" | "high";
|
|
23
|
+
dissent: string;
|
|
24
|
+
};
|
|
25
|
+
export type ReconciliationResult = {
|
|
26
|
+
outcomes: ReconciliationOutcome[];
|
|
27
|
+
};
|
|
28
|
+
export declare function buildDiscoveryIndex(root: string): DiscoveryIndex;
|
|
29
|
+
export declare function reconcileDiscoveryEvidence(root: string, envelope: DiscoveryEvidenceEnvelope): ReconciliationResult;
|
|
30
|
+
export declare function applyDiscoveryEvidence(root: string, studyDir: string, envelope: DiscoveryEvidenceEnvelope, now: string): {
|
|
31
|
+
status: "needs_review" | "clean_no_op";
|
|
32
|
+
writes: DiscoveryWrite[];
|
|
33
|
+
reconciliation: ReconciliationResult;
|
|
34
|
+
};
|
|
35
|
+
export declare function applyInboxDiscoveryEvidence(root: string, inboxDir: string, envelope: DiscoveryEvidenceEnvelope, now: string): {
|
|
36
|
+
status: "needs_review" | "clean_no_op";
|
|
37
|
+
writes: DiscoveryWrite[];
|
|
38
|
+
reconciliation: ReconciliationResult;
|
|
39
|
+
};
|
|
40
|
+
export type DiscoveryBundleApplyResult = {
|
|
41
|
+
status: "needs_review" | "clean_no_op";
|
|
42
|
+
writes: DiscoveryWrite[];
|
|
43
|
+
reconciliation: ReconciliationResult;
|
|
44
|
+
} | {
|
|
45
|
+
status: "needs_service_verification" | "invalid";
|
|
46
|
+
writes: [];
|
|
47
|
+
reason: string;
|
|
48
|
+
};
|
|
49
|
+
export declare function applyReceiptVerifiedDiscoveryBundle(input: {
|
|
50
|
+
root: string;
|
|
51
|
+
target_id: string;
|
|
52
|
+
studyDir: string;
|
|
53
|
+
bundle: unknown;
|
|
54
|
+
trust_store?: TrustStore<"foo">;
|
|
55
|
+
receipt_status?: DiscoveryReceiptStatusEndpoint;
|
|
56
|
+
now: string;
|
|
57
|
+
}): Promise<DiscoveryBundleApplyResult>;
|
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
import { appendFileSync, existsSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { loadDiscoveryRun, transitionDiscoveryRun } from "./discoveryArtifacts.js";
|
|
4
|
+
import { parseDiscoveryQualifiedRef, validateDiscoveryEvidence } from "./discoveryEvidence.js";
|
|
5
|
+
import { parse, stringify } from "./vendorYaml.js";
|
|
6
|
+
import { classifyProvider } from "./sourceIngress.js";
|
|
7
|
+
import { verifyDiscoveryBundleForApply } from "./discoveryProof.js";
|
|
8
|
+
import { createUnreachableDiscoveryReceiptStatus } from "./discoveryProcess.js";
|
|
9
|
+
const INDEX_DIRS = ["discovery", "research", "specs"];
|
|
10
|
+
const INDEX_EXTENSIONS = new Set([".md", ".yaml", ".yml", ".jsonl"]);
|
|
11
|
+
const ID_PATTERN = /\b([RDTFCPEH]-\d{3})\b/g;
|
|
12
|
+
const STOPWORDS = new Set(["about", "after", "again", "before", "being", "between", "could", "every", "should", "their", "there", "these", "those", "through", "with", "without", "would", "from", "into", "only", "that", "this", "when", "where", "which"]);
|
|
13
|
+
function walkFiles(dir) {
|
|
14
|
+
if (!existsSync(dir))
|
|
15
|
+
return [];
|
|
16
|
+
const files = [];
|
|
17
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
18
|
+
const full = path.join(dir, entry.name);
|
|
19
|
+
if (entry.isDirectory())
|
|
20
|
+
files.push(...walkFiles(full));
|
|
21
|
+
else if (entry.isFile() && INDEX_EXTENSIONS.has(path.extname(entry.name)))
|
|
22
|
+
files.push(full);
|
|
23
|
+
}
|
|
24
|
+
return files;
|
|
25
|
+
}
|
|
26
|
+
function excerpt(line) {
|
|
27
|
+
return line.trim().replace(/\s+/g, " ").slice(0, 240);
|
|
28
|
+
}
|
|
29
|
+
// YAML frontmatter is metadata wearing a document's opening lines. Indexed
|
|
30
|
+
// raw, its `---` delimiter became the "first content line" of every research
|
|
31
|
+
// paper — so review cards showed `existing_excerpt: ---` (#529).
|
|
32
|
+
function stripFrontmatter(text) {
|
|
33
|
+
if (!text.startsWith("---\n"))
|
|
34
|
+
return text;
|
|
35
|
+
const closing = text.indexOf("\n---", 4);
|
|
36
|
+
if (closing === -1)
|
|
37
|
+
return text;
|
|
38
|
+
const afterDelimiter = text.indexOf("\n", closing + 1);
|
|
39
|
+
return afterDelimiter === -1 ? "" : text.slice(afterDelimiter + 1);
|
|
40
|
+
}
|
|
41
|
+
// Paragraph-sized blocks, because the semantic score must mean CONCENTRATED
|
|
42
|
+
// overlap. Indexing each file as one whole-document bag of words meant the
|
|
43
|
+
// longest, most vocabulary-rich document cleared the overlap bar against
|
|
44
|
+
// nearly any paraphrase — 78 findings about unrelated industries all "matched"
|
|
45
|
+
// the same research paper (#529). A block that has no blank lines to split on
|
|
46
|
+
// (events.jsonl) falls back to per-line blocks so it can never re-become a
|
|
47
|
+
// whole-file bag.
|
|
48
|
+
function contentBlocks(text) {
|
|
49
|
+
const blocks = [];
|
|
50
|
+
for (const paragraph of stripFrontmatter(text).split(/\n{2,}/)) {
|
|
51
|
+
const trimmed = paragraph.trim();
|
|
52
|
+
if (!trimmed)
|
|
53
|
+
continue;
|
|
54
|
+
if (trimmed.length > 1500) {
|
|
55
|
+
for (const line of trimmed.split("\n"))
|
|
56
|
+
if (line.trim())
|
|
57
|
+
blocks.push(line.trim());
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
blocks.push(trimmed);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return blocks;
|
|
64
|
+
}
|
|
65
|
+
export function buildDiscoveryIndex(root) {
|
|
66
|
+
const entries = [];
|
|
67
|
+
for (const dirName of INDEX_DIRS) {
|
|
68
|
+
for (const file of walkFiles(path.join(root, dirName)).sort()) {
|
|
69
|
+
const relative = path.relative(root, file).split(path.sep).join("/");
|
|
70
|
+
const text = readFileSync(file, "utf8");
|
|
71
|
+
for (const line of text.split("\n")) {
|
|
72
|
+
const ids = [...line.matchAll(ID_PATTERN)].map((match) => match[1]);
|
|
73
|
+
for (const id of ids)
|
|
74
|
+
entries.push({ id, path: relative, excerpt: excerpt(line), text: line });
|
|
75
|
+
}
|
|
76
|
+
for (const block of contentBlocks(text)) {
|
|
77
|
+
entries.push({ path: relative, excerpt: excerpt(block), text: block });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return { entries };
|
|
82
|
+
}
|
|
83
|
+
function tokens(text) {
|
|
84
|
+
return new Set((text.toLowerCase().match(/[a-z0-9]+/g) ?? []).filter((token) => token.length >= 4 && !STOPWORDS.has(token)));
|
|
85
|
+
}
|
|
86
|
+
function semanticScore(finding, entry) {
|
|
87
|
+
const left = tokens(finding);
|
|
88
|
+
if (left.size === 0)
|
|
89
|
+
return 0;
|
|
90
|
+
const right = tokens(entry.text);
|
|
91
|
+
let overlap = 0;
|
|
92
|
+
for (const token of left)
|
|
93
|
+
if (right.has(token))
|
|
94
|
+
overlap += 1;
|
|
95
|
+
if (overlap < 3)
|
|
96
|
+
return 0;
|
|
97
|
+
return overlap / left.size;
|
|
98
|
+
}
|
|
99
|
+
function exactEntry(index, refs) {
|
|
100
|
+
// D-011 (nango-discovery-connector-v0 T-005): only a packet-qualified
|
|
101
|
+
// reference can create an exact mapping, and only when it resolves to
|
|
102
|
+
// exactly one record inside that packet's directory. A bare `R-001`-style
|
|
103
|
+
// id is review-only — it falls through to semantic matching, and the
|
|
104
|
+
// first-match behavior this function used to have (including a bare
|
|
105
|
+
// substring hit on any indexed line) can never silently win for an
|
|
106
|
+
// ambiguous reference (V-007).
|
|
107
|
+
const resolved = [];
|
|
108
|
+
for (const ref of refs) {
|
|
109
|
+
const qualified = parseDiscoveryQualifiedRef(ref);
|
|
110
|
+
if (!qualified)
|
|
111
|
+
continue; // bare ids and https URLs are review-only, never exact
|
|
112
|
+
const packetPrefix = `specs/${qualified.packet_slug}/`;
|
|
113
|
+
for (const entry of index.entries) {
|
|
114
|
+
if (entry.id === qualified.record_id && entry.path.startsWith(packetPrefix))
|
|
115
|
+
resolved.push(entry);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (resolved.length === 0)
|
|
119
|
+
return undefined;
|
|
120
|
+
// Multiple indexed mentions of the same record within one file are one
|
|
121
|
+
// record (definition plus body prose); mentions across files are ambiguous
|
|
122
|
+
// and stay review-only.
|
|
123
|
+
const paths = new Set(resolved.map((entry) => entry.path));
|
|
124
|
+
return paths.size === 1 ? resolved[0] : undefined;
|
|
125
|
+
}
|
|
126
|
+
function outcomeFor(index, finding) {
|
|
127
|
+
const exact = exactEntry(index, finding.explicit_refs ?? []);
|
|
128
|
+
if (exact) {
|
|
129
|
+
return {
|
|
130
|
+
finding_id: finding.finding_id,
|
|
131
|
+
kind: finding.relationship_hint === "contradicts" ? "drift_candidate" : "exact",
|
|
132
|
+
canonical: finding.relationship_hint !== "contradicts",
|
|
133
|
+
target_id: exact.id,
|
|
134
|
+
target_path: exact.path,
|
|
135
|
+
existing_excerpt: exact.excerpt,
|
|
136
|
+
confidence: "high",
|
|
137
|
+
dissent: finding.dissent,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
let best;
|
|
141
|
+
for (const entry of index.entries) {
|
|
142
|
+
const score = semanticScore(finding.paraphrase, entry);
|
|
143
|
+
if (!best || score > best.score)
|
|
144
|
+
best = { entry, score };
|
|
145
|
+
}
|
|
146
|
+
if (best && best.score >= 0.35) {
|
|
147
|
+
return {
|
|
148
|
+
finding_id: finding.finding_id,
|
|
149
|
+
kind: finding.relationship_hint === "contradicts" ? "drift_candidate" : "semantic_candidate",
|
|
150
|
+
canonical: false,
|
|
151
|
+
target_id: best.entry.id,
|
|
152
|
+
target_path: best.entry.path,
|
|
153
|
+
existing_excerpt: best.entry.excerpt,
|
|
154
|
+
confidence: best.score >= 0.6 ? "high" : "medium",
|
|
155
|
+
dissent: finding.dissent,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
return { finding_id: finding.finding_id, kind: "draft_proposal", canonical: false, confidence: "low", dissent: finding.dissent };
|
|
159
|
+
}
|
|
160
|
+
export function reconcileDiscoveryEvidence(root, envelope) {
|
|
161
|
+
const index = buildDiscoveryIndex(root);
|
|
162
|
+
return { outcomes: envelope.findings.map((finding) => outcomeFor(index, finding)) };
|
|
163
|
+
}
|
|
164
|
+
function relativeWrite(root, file) {
|
|
165
|
+
return { path: path.relative(root, file).split(path.sep).join("/"), action: "updated" };
|
|
166
|
+
}
|
|
167
|
+
function nextArtifactId(records, key, prefix) {
|
|
168
|
+
const highest = records.reduce((max, record) => {
|
|
169
|
+
const match = new RegExp(`^${prefix}-(\\d+)$`).exec(String(record[key] ?? ""));
|
|
170
|
+
return match ? Math.max(max, Number(match[1])) : max;
|
|
171
|
+
}, 0);
|
|
172
|
+
return `${prefix}-${String(highest + 1).padStart(3, "0")}`;
|
|
173
|
+
}
|
|
174
|
+
export function applyDiscoveryEvidence(root, studyDir, envelope, now) {
|
|
175
|
+
const run = loadDiscoveryRun(studyDir);
|
|
176
|
+
validateDiscoveryEvidence(envelope, { expected_study_id: run.study_id, expected_lane: "study_linked" });
|
|
177
|
+
const findingsPath = path.join(studyDir, "findings.yaml");
|
|
178
|
+
const proposalsPath = path.join(studyDir, "proposals.yaml");
|
|
179
|
+
const reportPath = path.join(studyDir, "report.md");
|
|
180
|
+
const findingsDoc = parse(readFileSync(findingsPath, "utf8"));
|
|
181
|
+
if (findingsDoc.findings.some((finding) => finding.source_fingerprint === envelope.source.source_fingerprint)) {
|
|
182
|
+
return { status: "clean_no_op", writes: [], reconciliation: { outcomes: [] } };
|
|
183
|
+
}
|
|
184
|
+
const reconciliation = reconcileDiscoveryEvidence(root, envelope);
|
|
185
|
+
// Every applied record carries its provider's registry classification so
|
|
186
|
+
// reconcile renders each source's evidence by its class (RISK-002), and —
|
|
187
|
+
// only when the summary is INCOMPLETE — the coverage limitation. A complete
|
|
188
|
+
// summary records nothing: findings never claim a raw transcript exists.
|
|
189
|
+
const classification = classifyProvider(envelope.source.provider);
|
|
190
|
+
const summaryCoverage = envelope.coverage.transcript_complete === false
|
|
191
|
+
? { transcript_complete: false, caveats: [...envelope.coverage.caveats] }
|
|
192
|
+
: undefined;
|
|
193
|
+
const canonicalFindingIds = new Map();
|
|
194
|
+
for (const finding of envelope.findings) {
|
|
195
|
+
const outcome = reconciliation.outcomes.find((candidate) => candidate.finding_id === finding.finding_id);
|
|
196
|
+
const canonicalFindingId = nextArtifactId(findingsDoc.findings, "finding_id", "F");
|
|
197
|
+
canonicalFindingIds.set(finding.finding_id, canonicalFindingId);
|
|
198
|
+
findingsDoc.findings.push({
|
|
199
|
+
...finding,
|
|
200
|
+
finding_id: canonicalFindingId,
|
|
201
|
+
source_finding_id: finding.finding_id,
|
|
202
|
+
lane: envelope.lane,
|
|
203
|
+
source_provider: envelope.source.provider,
|
|
204
|
+
source_category: classification.source_category,
|
|
205
|
+
evidence_class: classification.evidence_class,
|
|
206
|
+
...(summaryCoverage ? { summary_coverage: summaryCoverage } : {}),
|
|
207
|
+
source_url: envelope.source.stable_url ?? null,
|
|
208
|
+
source_fingerprint: envelope.source.source_fingerprint,
|
|
209
|
+
completed_at: envelope.source.completed_at,
|
|
210
|
+
reconciliation: { ...outcome, finding_id: canonicalFindingId },
|
|
211
|
+
review_state: "needs_review",
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
writeFileSync(findingsPath, stringify(findingsDoc));
|
|
215
|
+
const proposalsDoc = parse(readFileSync(proposalsPath, "utf8"));
|
|
216
|
+
for (const outcome of reconciliation.outcomes.filter((candidate) => candidate.kind !== "exact")) {
|
|
217
|
+
const finding = envelope.findings.find((candidate) => candidate.finding_id === outcome.finding_id);
|
|
218
|
+
proposalsDoc.proposals.push({
|
|
219
|
+
proposal_id: nextArtifactId(proposalsDoc.proposals, "proposal_id", "P"),
|
|
220
|
+
source_finding_ids: [canonicalFindingIds.get(finding.finding_id)],
|
|
221
|
+
proposed_text: outcome.kind === "draft_proposal" ? finding.paraphrase : null,
|
|
222
|
+
rationale: outcome.kind,
|
|
223
|
+
existing_artifact_candidates: outcome.target_path ? [{ path: outcome.target_path, id: outcome.target_id ?? null, excerpt: outcome.existing_excerpt ?? null }] : [],
|
|
224
|
+
confidence: outcome.confidence,
|
|
225
|
+
dissent: finding.dissent,
|
|
226
|
+
destination: null,
|
|
227
|
+
state: "needs_review",
|
|
228
|
+
disposition: null,
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
writeFileSync(proposalsPath, stringify(proposalsDoc));
|
|
232
|
+
const findings = envelope.findings.map((finding) => `- ${canonicalFindingIds.get(finding.finding_id)}: ${finding.paraphrase} (${finding.confidence})`).join("\n");
|
|
233
|
+
const dissent = envelope.findings.map((finding) => `- ${canonicalFindingIds.get(finding.finding_id)}: ${finding.dissent}`).join("\n");
|
|
234
|
+
writeFileSync(reportPath, [
|
|
235
|
+
"# Discovery report",
|
|
236
|
+
"",
|
|
237
|
+
"## Current fit belief",
|
|
238
|
+
"",
|
|
239
|
+
"Needs human review; automation does not decide fit.",
|
|
240
|
+
"",
|
|
241
|
+
"## Evidence that moved the belief",
|
|
242
|
+
"",
|
|
243
|
+
findings,
|
|
244
|
+
"",
|
|
245
|
+
"## Revised belief and confidence",
|
|
246
|
+
"",
|
|
247
|
+
"Pending human review.",
|
|
248
|
+
"",
|
|
249
|
+
"## Contradictions and dissent",
|
|
250
|
+
"",
|
|
251
|
+
dissent,
|
|
252
|
+
"",
|
|
253
|
+
"## Proposals needing review",
|
|
254
|
+
"",
|
|
255
|
+
...reconciliation.outcomes.filter((candidate) => candidate.kind !== "exact").map((candidate) => `- ${candidate.finding_id}: ${candidate.kind}`),
|
|
256
|
+
"",
|
|
257
|
+
].join("\n"));
|
|
258
|
+
const writes = [relativeWrite(root, findingsPath), relativeWrite(root, proposalsPath), relativeWrite(root, reportPath)];
|
|
259
|
+
// A study already parked in needs_review has no needs_review -> needs_review transition
|
|
260
|
+
// (ALLOWED_TRANSITIONS), so re-applying a distinct-fingerprint envelope must not throw:
|
|
261
|
+
// record the derived artifacts and leave the state where it already is.
|
|
262
|
+
if (loadDiscoveryRun(studyDir).state !== "needs_review") {
|
|
263
|
+
writes.push(...transitionDiscoveryRun(studyDir, "needs_review", { type: "agent", id: "foo-discovery" }, now));
|
|
264
|
+
}
|
|
265
|
+
return { status: "needs_review", writes, reconciliation };
|
|
266
|
+
}
|
|
267
|
+
// A workspace whose discovery inbox has never received evidence has no
|
|
268
|
+
// findings/proposals files yet — nothing created them before the first apply.
|
|
269
|
+
// An absent file is an empty collection, not a crash.
|
|
270
|
+
function readInboxYamlDoc(filePath) {
|
|
271
|
+
if (!existsSync(filePath))
|
|
272
|
+
return { schema_version: "0.1", findings: [], proposals: [] };
|
|
273
|
+
return parse(readFileSync(filePath, "utf8"));
|
|
274
|
+
}
|
|
275
|
+
export function applyInboxDiscoveryEvidence(root, inboxDir, envelope, now) {
|
|
276
|
+
validateDiscoveryEvidence(envelope, { expected_study_id: envelope.study_id, expected_lane: "unassigned_call_evidence" });
|
|
277
|
+
const findingsPath = path.join(inboxDir, "findings.yaml");
|
|
278
|
+
const proposalsPath = path.join(inboxDir, "proposals.yaml");
|
|
279
|
+
const eventsPath = path.join(inboxDir, "events.jsonl");
|
|
280
|
+
const findingsDoc = readInboxYamlDoc(findingsPath);
|
|
281
|
+
const existingSourceRecords = findingsDoc.findings.filter((finding) => finding.source_fingerprint === envelope.source.source_fingerprint);
|
|
282
|
+
if (existingSourceRecords.length > 0) {
|
|
283
|
+
const existingIds = existingSourceRecords.map((finding) => String(finding.source_finding_id ?? "")).sort();
|
|
284
|
+
const replayIds = envelope.findings.map((finding) => finding.finding_id).sort();
|
|
285
|
+
if (existingIds.length !== replayIds.length || existingIds.some((id, index) => id !== replayIds[index])) {
|
|
286
|
+
throw new Error("source replay finding set does not match the existing derived evidence");
|
|
287
|
+
}
|
|
288
|
+
let repaired = false;
|
|
289
|
+
for (const finding of envelope.findings) {
|
|
290
|
+
const recordIndex = findingsDoc.findings.findIndex((candidate) => candidate.source_fingerprint === envelope.source.source_fingerprint
|
|
291
|
+
&& candidate.source_finding_id === finding.finding_id);
|
|
292
|
+
const current = findingsDoc.findings[recordIndex];
|
|
293
|
+
let next = current;
|
|
294
|
+
if (typeof current.headline !== "string" || !current.headline.trim()) {
|
|
295
|
+
const { finding_id: findingId, ...fields } = current;
|
|
296
|
+
delete fields.headline;
|
|
297
|
+
next = { finding_id: findingId, headline: finding.headline, ...fields };
|
|
298
|
+
repaired = true;
|
|
299
|
+
}
|
|
300
|
+
if (envelope.source.stable_url
|
|
301
|
+
&& (typeof next.source_url !== "string" || !next.source_url.trim())) {
|
|
302
|
+
next = { ...next, source_url: envelope.source.stable_url };
|
|
303
|
+
repaired = true;
|
|
304
|
+
}
|
|
305
|
+
findingsDoc.findings[recordIndex] = next;
|
|
306
|
+
}
|
|
307
|
+
if (!repaired)
|
|
308
|
+
return { status: "clean_no_op", writes: [], reconciliation: { outcomes: [] } };
|
|
309
|
+
writeFileSync(findingsPath, stringify(findingsDoc));
|
|
310
|
+
return {
|
|
311
|
+
status: "needs_review",
|
|
312
|
+
writes: [relativeWrite(root, findingsPath)],
|
|
313
|
+
reconciliation: { outcomes: [] },
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
const reconciliation = reconcileDiscoveryEvidence(root, envelope);
|
|
317
|
+
// Every applied record carries its provider's registry classification so
|
|
318
|
+
// reconcile renders each source's evidence by its class (RISK-002), and —
|
|
319
|
+
// only when the summary is INCOMPLETE — the coverage limitation. A complete
|
|
320
|
+
// summary records nothing: findings never claim a raw transcript exists.
|
|
321
|
+
const classification = classifyProvider(envelope.source.provider);
|
|
322
|
+
const summaryCoverage = envelope.coverage.transcript_complete === false
|
|
323
|
+
? { transcript_complete: false, caveats: [...envelope.coverage.caveats] }
|
|
324
|
+
: undefined;
|
|
325
|
+
const canonicalFindingIds = new Map();
|
|
326
|
+
for (const finding of envelope.findings) {
|
|
327
|
+
const mapped = reconciliation.outcomes.find((candidate) => candidate.finding_id === finding.finding_id);
|
|
328
|
+
const canonicalFindingId = nextArtifactId(findingsDoc.findings, "finding_id", "F");
|
|
329
|
+
canonicalFindingIds.set(finding.finding_id, canonicalFindingId);
|
|
330
|
+
findingsDoc.findings.push({
|
|
331
|
+
...finding,
|
|
332
|
+
finding_id: canonicalFindingId,
|
|
333
|
+
source_finding_id: finding.finding_id,
|
|
334
|
+
lane: "unassigned_call_evidence",
|
|
335
|
+
source_provider: envelope.source.provider,
|
|
336
|
+
source_category: classification.source_category,
|
|
337
|
+
evidence_class: classification.evidence_class,
|
|
338
|
+
...(summaryCoverage ? { summary_coverage: summaryCoverage } : {}),
|
|
339
|
+
inbox_scope_id: envelope.study_id.slice("inbox:".length),
|
|
340
|
+
source_url: envelope.source.stable_url ?? null,
|
|
341
|
+
source_fingerprint: envelope.source.source_fingerprint,
|
|
342
|
+
completed_at: envelope.source.completed_at,
|
|
343
|
+
reconciliation: { ...mapped, finding_id: canonicalFindingId },
|
|
344
|
+
review_state: "needs_review",
|
|
345
|
+
attached_to: null,
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
writeFileSync(findingsPath, stringify(findingsDoc));
|
|
349
|
+
const proposalsDoc = readInboxYamlDoc(proposalsPath);
|
|
350
|
+
for (const mapped of reconciliation.outcomes.filter((candidate) => candidate.kind !== "exact")) {
|
|
351
|
+
const finding = envelope.findings.find((candidate) => candidate.finding_id === mapped.finding_id);
|
|
352
|
+
proposalsDoc.proposals.push({
|
|
353
|
+
proposal_id: nextArtifactId(proposalsDoc.proposals, "proposal_id", "P"),
|
|
354
|
+
source_finding_ids: [canonicalFindingIds.get(finding.finding_id)],
|
|
355
|
+
proposed_text: mapped.kind === "draft_proposal" ? finding.paraphrase : null,
|
|
356
|
+
rationale: mapped.kind,
|
|
357
|
+
existing_artifact_candidates: mapped.target_path ? [{ path: mapped.target_path, id: mapped.target_id ?? null, excerpt: mapped.existing_excerpt ?? null }] : [],
|
|
358
|
+
confidence: mapped.confidence,
|
|
359
|
+
dissent: finding.dissent,
|
|
360
|
+
destination: null,
|
|
361
|
+
state: "needs_review",
|
|
362
|
+
disposition: null,
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
writeFileSync(proposalsPath, stringify(proposalsDoc));
|
|
366
|
+
appendFileSync(eventsPath, `${JSON.stringify({
|
|
367
|
+
specversion: "1.0",
|
|
368
|
+
id: `evt_discovery_inbox_evidence_${Date.parse(now)}`,
|
|
369
|
+
source: "agent://foo/discovery",
|
|
370
|
+
type: "com.adaptivespec.discovery.inbox_evidence_applied",
|
|
371
|
+
subject: `scope:${envelope.study_id.slice("inbox:".length)}`,
|
|
372
|
+
time: now,
|
|
373
|
+
actor: { type: "agent", id: "foo-discovery" },
|
|
374
|
+
data: { finding_ids: envelope.findings.map((finding) => canonicalFindingIds.get(finding.finding_id)), review_state: "needs_review" },
|
|
375
|
+
})}\n`);
|
|
376
|
+
return {
|
|
377
|
+
status: "needs_review",
|
|
378
|
+
writes: [relativeWrite(root, findingsPath), relativeWrite(root, proposalsPath), relativeWrite(root, eventsPath)],
|
|
379
|
+
reconciliation,
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
// The hosted portable-bundle apply boundary: verify everything locally
|
|
383
|
+
// checkable (discoveryProof gate), then ask the service `discovery_receipt_status`
|
|
384
|
+
// as the LAST network boundary before any write, fail closed on unreachability,
|
|
385
|
+
// a non-active receipt, or any revocation-epoch movement, and only then run
|
|
386
|
+
// the normal discovery apply — which still re-validates and stops at
|
|
387
|
+
// needs_review. No outcome but the two write states touches disk (C-009).
|
|
388
|
+
export async function applyReceiptVerifiedDiscoveryBundle(input) {
|
|
389
|
+
const verified = verifyDiscoveryBundleForApply({
|
|
390
|
+
bundle: input.bundle,
|
|
391
|
+
target_id: input.target_id,
|
|
392
|
+
root: input.root,
|
|
393
|
+
study_dir: input.studyDir,
|
|
394
|
+
...(input.trust_store ? { trust_store: input.trust_store } : {}),
|
|
395
|
+
now: input.now,
|
|
396
|
+
});
|
|
397
|
+
if (!verified.ok)
|
|
398
|
+
return { status: "invalid", writes: [], reason: verified.message };
|
|
399
|
+
try {
|
|
400
|
+
const status = await (input.receipt_status ?? createUnreachableDiscoveryReceiptStatus()).receiptStatus({
|
|
401
|
+
receipt_hash: verified.receipt_hash,
|
|
402
|
+
target_ref: verified.registry.target_ref,
|
|
403
|
+
});
|
|
404
|
+
if (status.receipt_status !== "active" || status.revocation_epoch !== verified.registry.revocation_epoch) {
|
|
405
|
+
return { status: "needs_service_verification", writes: [], reason: "the discovery service could not confirm the receipt is active at the current revocation epoch; nothing was written" };
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
catch {
|
|
409
|
+
// Bounded by design: error text can carry provider or transport content,
|
|
410
|
+
// so the refusal names the state, never the underlying error body.
|
|
411
|
+
return { status: "needs_service_verification", writes: [], reason: "the discovery service is unreachable; receipt status could not be verified and nothing was written" };
|
|
412
|
+
}
|
|
413
|
+
try {
|
|
414
|
+
const applied = verified.registry.lane === "inbox"
|
|
415
|
+
? applyInboxDiscoveryEvidence(input.root, input.studyDir, verified.bundle.envelope, input.now)
|
|
416
|
+
: applyDiscoveryEvidence(input.root, input.studyDir, verified.bundle.envelope, input.now);
|
|
417
|
+
return { status: applied.status, writes: applied.writes, reconciliation: applied.reconciliation };
|
|
418
|
+
}
|
|
419
|
+
catch {
|
|
420
|
+
// Same boundary: a failed apply reports the bounded refusal, and no
|
|
421
|
+
// partial discovery update was accepted.
|
|
422
|
+
return { status: "invalid", writes: [], reason: "the verified evidence could not be applied; no partial discovery update was accepted" };
|
|
423
|
+
}
|
|
424
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type DiscoveryCommandOutcome } from "./discoveryCommand.js";
|
|
2
|
+
import { type SynthesisTransport } from "./discoverySynthesis.js";
|
|
3
|
+
import { type FetchLike } from "./slackNango.js";
|
|
4
|
+
export type SlackDiscoveryDeps = {
|
|
5
|
+
fetchImpl?: FetchLike;
|
|
6
|
+
env?: NodeJS.ProcessEnv;
|
|
7
|
+
now?: () => string;
|
|
8
|
+
sleep?: (ms: number) => Promise<void>;
|
|
9
|
+
transport?: SynthesisTransport;
|
|
10
|
+
fetchTimeoutMs?: number;
|
|
11
|
+
};
|
|
12
|
+
export declare function runSlackDiscovery(root: string, args: string[], deps?: SlackDiscoveryDeps): Promise<DiscoveryCommandOutcome>;
|