specweave 0.1.8 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +600 -0
- package/README.md +263 -88
- package/bin/install-all.sh +1 -1
- package/bin/install-commands.sh +3 -3
- package/bin/specweave.js +39 -9
- package/dist/adapters/adapter-base.d.ts +1 -1
- package/dist/adapters/adapter-base.d.ts.map +1 -1
- package/dist/adapters/adapter-base.js +6 -41
- package/dist/adapters/adapter-base.js.map +1 -1
- package/dist/adapters/adapter-interface.js +1 -2
- package/dist/adapters/adapter-interface.js.map +1 -1
- package/dist/adapters/adapter-loader.d.ts +86 -0
- package/dist/adapters/adapter-loader.d.ts.map +1 -0
- package/dist/adapters/adapter-loader.js +216 -0
- package/dist/adapters/adapter-loader.js.map +1 -0
- package/dist/adapters/agents-md-generator.d.ts +48 -0
- package/dist/adapters/agents-md-generator.d.ts.map +1 -0
- package/dist/adapters/agents-md-generator.js +132 -0
- package/dist/adapters/agents-md-generator.js.map +1 -0
- package/dist/adapters/claude/adapter.d.ts +2 -2
- package/dist/adapters/claude/adapter.d.ts.map +1 -1
- package/dist/adapters/claude/adapter.js +5 -42
- package/dist/adapters/claude/adapter.js.map +1 -1
- package/dist/adapters/claude-md-generator.d.ts +78 -0
- package/dist/adapters/claude-md-generator.d.ts.map +1 -0
- package/dist/adapters/claude-md-generator.js +246 -0
- package/dist/adapters/claude-md-generator.js.map +1 -0
- package/dist/adapters/codex/adapter.d.ts +50 -0
- package/dist/adapters/codex/adapter.d.ts.map +1 -0
- package/dist/adapters/codex/adapter.js +316 -0
- package/dist/adapters/codex/adapter.js.map +1 -0
- package/dist/adapters/copilot/adapter.d.ts +10 -9
- package/dist/adapters/copilot/adapter.d.ts.map +1 -1
- package/dist/adapters/copilot/adapter.js +35 -100
- package/dist/adapters/copilot/adapter.js.map +1 -1
- package/dist/adapters/cursor/adapter.d.ts +8 -6
- package/dist/adapters/cursor/adapter.d.ts.map +1 -1
- package/dist/adapters/cursor/adapter.js +47 -130
- package/dist/adapters/cursor/adapter.js.map +1 -1
- package/dist/adapters/doc-generator.d.ts +69 -0
- package/dist/adapters/doc-generator.d.ts.map +1 -0
- package/dist/adapters/doc-generator.js +247 -0
- package/dist/adapters/doc-generator.js.map +1 -0
- package/dist/adapters/gemini/adapter.d.ts +50 -0
- package/dist/adapters/gemini/adapter.d.ts.map +1 -0
- package/dist/adapters/gemini/adapter.js +281 -0
- package/dist/adapters/gemini/adapter.js.map +1 -0
- package/dist/adapters/generic/adapter.d.ts +7 -4
- package/dist/adapters/generic/adapter.d.ts.map +1 -1
- package/dist/adapters/generic/adapter.js +60 -59
- package/dist/adapters/generic/adapter.js.map +1 -1
- package/dist/cli/commands/init.d.ts +3 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +272 -170
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/install.d.ts.map +1 -1
- package/dist/cli/commands/install.js +22 -58
- package/dist/cli/commands/install.js.map +1 -1
- package/dist/cli/commands/list.d.ts.map +1 -1
- package/dist/cli/commands/list.js +27 -64
- package/dist/cli/commands/list.js.map +1 -1
- package/dist/core/credentials-manager.d.ts +90 -0
- package/dist/core/credentials-manager.d.ts.map +1 -0
- package/dist/core/credentials-manager.js +271 -0
- package/dist/core/credentials-manager.js.map +1 -0
- package/dist/core/project-structure-detector.d.ts +92 -0
- package/dist/core/project-structure-detector.d.ts.map +1 -0
- package/dist/core/project-structure-detector.js +289 -0
- package/dist/core/project-structure-detector.js.map +1 -0
- package/dist/core/rfc-generator-v2.d.ts +149 -0
- package/dist/core/rfc-generator-v2.d.ts.map +1 -0
- package/dist/core/rfc-generator-v2.js +399 -0
- package/dist/core/rfc-generator-v2.js.map +1 -0
- package/dist/core/rfc-generator.d.ts +147 -0
- package/dist/core/rfc-generator.d.ts.map +1 -0
- package/dist/core/rfc-generator.js +434 -0
- package/dist/core/rfc-generator.js.map +1 -0
- package/dist/integrations/ado/ado-client.d.ts +123 -0
- package/dist/integrations/ado/ado-client.d.ts.map +1 -0
- package/dist/integrations/ado/ado-client.js +398 -0
- package/dist/integrations/ado/ado-client.js.map +1 -0
- package/dist/integrations/jira/jira-client.d.ts +139 -0
- package/dist/integrations/jira/jira-client.d.ts.map +1 -0
- package/dist/integrations/jira/jira-client.js +386 -0
- package/dist/integrations/jira/jira-client.js.map +1 -0
- package/dist/integrations/jira/jira-incremental-mapper.d.ts +75 -0
- package/dist/integrations/jira/jira-incremental-mapper.d.ts.map +1 -0
- package/dist/integrations/jira/jira-incremental-mapper.js +474 -0
- package/dist/integrations/jira/jira-incremental-mapper.js.map +1 -0
- package/dist/integrations/jira/jira-mapper.d.ts +105 -0
- package/dist/integrations/jira/jira-mapper.d.ts.map +1 -0
- package/dist/integrations/jira/jira-mapper.js +494 -0
- package/dist/integrations/jira/jira-mapper.js.map +1 -0
- package/dist/testing/test-generator.d.ts +117 -0
- package/dist/testing/test-generator.d.ts.map +1 -0
- package/dist/testing/test-generator.js +370 -0
- package/dist/testing/test-generator.js.map +1 -0
- package/dist/utils/auto-install.d.ts +3 -0
- package/dist/utils/auto-install.d.ts.map +1 -1
- package/dist/utils/auto-install.js +16 -82
- package/dist/utils/auto-install.js.map +1 -1
- package/dist/utils/esm-helpers.d.ts +50 -0
- package/dist/utils/esm-helpers.d.ts.map +1 -0
- package/dist/utils/esm-helpers.js +57 -0
- package/dist/utils/esm-helpers.js.map +1 -0
- package/package.json +16 -7
- package/src/adapters/README.md +1 -2
- package/src/adapters/adapter-base.ts +6 -3
- package/src/adapters/adapter-loader.ts +261 -0
- package/src/adapters/agents-md-generator.ts +162 -0
- package/src/adapters/claude/README.md +6 -14
- package/src/adapters/claude/adapter.ts +4 -4
- package/src/adapters/claude-md-generator.ts +311 -0
- package/src/adapters/codex/README.md +105 -0
- package/src/adapters/codex/adapter.ts +333 -0
- package/src/adapters/copilot/adapter.ts +36 -65
- package/src/adapters/cursor/README.md +0 -2
- package/src/adapters/cursor/adapter.ts +46 -92
- package/src/adapters/doc-generator.ts +331 -0
- package/src/adapters/gemini/README.md +97 -0
- package/src/adapters/gemini/adapter.ts +298 -0
- package/src/adapters/generic/adapter.ts +61 -57
- package/src/adapters/registry.yaml +86 -25
- package/src/agents/devops/AGENT.md +16 -18
- package/src/agents/docs-writer/AGENT.md +2 -2
- package/src/agents/pm/AGENT.md +1 -50
- package/src/commands/README.md +134 -111
- package/src/commands/{build.md → specweave.do.md} +185 -72
- package/src/commands/{done.md → specweave.done.md} +3 -3
- package/src/commands/{inc.md → specweave.inc.md} +4 -4
- package/src/commands/specweave.increment.md +383 -0
- package/src/commands/specweave.md +430 -0
- package/src/commands/specweave.next.md +495 -0
- package/src/commands/specweave.progress.md +258 -0
- package/src/commands/specweave.sync-docs.md +665 -0
- package/src/commands/specweave.sync-github.md +269 -0
- package/src/commands/specweave.sync-jira.md +197 -0
- package/src/commands/{validate.md → specweave.validate.md} +4 -4
- package/src/hooks/README.md +19 -29
- package/src/hooks/post-task-completion.sh +25 -30
- package/src/skills/ado-sync/README.md +1 -36
- package/src/skills/bmad-method-expert/SKILL.md +1 -3
- package/src/skills/brownfield-analyzer/SKILL.md +429 -23
- package/src/skills/brownfield-onboarder/SKILL.md +221 -8
- package/src/skills/context-loader/SKILL.md +239 -617
- package/src/skills/context-optimizer/SKILL.md +0 -30
- package/src/skills/github-sync/SKILL.md +1 -19
- package/src/skills/increment-planner/SKILL.md +64 -18
- package/src/skills/increment-quality-judge/SKILL.md +1 -36
- package/src/skills/jira-sync/README.md +1 -38
- package/src/skills/role-orchestrator/README.md +1 -22
- package/src/skills/role-orchestrator/SKILL.md +1 -59
- package/src/skills/skill-router/SKILL.md +0 -18
- package/src/skills/spec-kit-expert/SKILL.md +1 -3
- package/src/skills/specweave-detector/SKILL.md +225 -275
- package/src/skills/task-builder/README.md +1 -7
- package/src/templates/AGENTS.md.template +334 -0
- package/src/templates/CLAUDE.md.template +131 -297
- package/src/templates/README.md.template +115 -23
- package/src/templates/environments/minimal/README.md +0 -1
- package/INSTALL.md +0 -848
- package/SPECWEAVE.md +0 -711
- package/src/adapters/copilot/.github/copilot/instructions.md +0 -376
- package/src/adapters/cursor/.cursorrules +0 -325
- package/src/adapters/generic/SPECWEAVE-MANUAL.md +0 -676
- package/src/commands/create-project.md +0 -528
- package/src/commands/generate-docs.md +0 -623
- package/src/commands/increment.md +0 -223
- package/src/commands/review-docs.md +0 -331
- package/src/commands/sync-github.md +0 -115
- package/src/skills/ado-sync/test-cases/test-1.yaml +0 -9
- package/src/skills/ado-sync/test-cases/test-2.yaml +0 -8
- package/src/skills/ado-sync/test-cases/test-3.yaml +0 -9
- package/src/skills/bmad-method-expert/test-cases/test-1-placeholder.yaml +0 -12
- package/src/skills/bmad-method-expert/test-cases/test-2-placeholder.yaml +0 -12
- package/src/skills/bmad-method-expert/test-cases/test-3-placeholder.yaml +0 -12
- package/src/skills/brownfield-analyzer/test-cases/test-1-basic-analysis.yaml +0 -48
- package/src/skills/brownfield-analyzer/test-cases/test-2-placeholder.yaml +0 -12
- package/src/skills/brownfield-analyzer/test-cases/test-3-placeholder.yaml +0 -12
- package/src/skills/brownfield-onboarder/test-cases/test-1-placeholder.yaml +0 -12
- package/src/skills/brownfield-onboarder/test-cases/test-2-placeholder.yaml +0 -12
- package/src/skills/brownfield-onboarder/test-cases/test-3-placeholder.yaml +0 -12
- package/src/skills/calendar-system/test-cases/test-1-placeholder.yaml +0 -12
- package/src/skills/calendar-system/test-cases/test-2-placeholder.yaml +0 -12
- package/src/skills/calendar-system/test-cases/test-3-placeholder.yaml +0 -12
- package/src/skills/context-loader/test-cases/test-1-basic-loading.yaml +0 -39
- package/src/skills/context-loader/test-cases/test-2-token-budget-exceeded.yaml +0 -44
- package/src/skills/context-loader/test-cases/test-3-section-anchors.yaml +0 -45
- package/src/skills/context-optimizer/test-cases/test-1-bug-fix-narrow.yaml +0 -97
- package/src/skills/context-optimizer/test-cases/test-2-feature-focused.yaml +0 -109
- package/src/skills/context-optimizer/test-cases/test-3-architecture-broad.yaml +0 -98
- package/src/skills/cost-optimizer/test-cases/test-1-basic-comparison.yaml +0 -75
- package/src/skills/cost-optimizer/test-cases/test-2-budget-constraint.yaml +0 -52
- package/src/skills/cost-optimizer/test-cases/test-3-scale-requirement.yaml +0 -63
- package/src/skills/cost-optimizer/test-results/README.md +0 -46
- package/src/skills/design-system-architect/test-cases/test-1-token-structure.yaml +0 -23
- package/src/skills/design-system-architect/test-cases/test-2-component-hierarchy.yaml +0 -24
- package/src/skills/design-system-architect/test-cases/test-3-accessibility-checklist.yaml +0 -23
- package/src/skills/diagrams-architect/test-cases/test-1-c4-context.yaml +0 -13
- package/src/skills/diagrams-architect/test-cases/test-2-sequence-diagram.yaml +0 -13
- package/src/skills/diagrams-architect/test-cases/test-3-er-diagram.yaml +0 -13
- package/src/skills/diagrams-generator/test-cases/test-1.yaml +0 -9
- package/src/skills/diagrams-generator/test-cases/test-2.yaml +0 -9
- package/src/skills/diagrams-generator/test-cases/test-3.yaml +0 -8
- package/src/skills/docs-updater/test-cases/test-1-placeholder.yaml +0 -12
- package/src/skills/docs-updater/test-cases/test-2-placeholder.yaml +0 -12
- package/src/skills/docs-updater/test-cases/test-3-placeholder.yaml +0 -12
- package/src/skills/dotnet-backend/test-cases/test-1-rest-api.yaml +0 -14
- package/src/skills/dotnet-backend/test-cases/test-2-authentication.yaml +0 -13
- package/src/skills/dotnet-backend/test-cases/test-3-minimal-api.yaml +0 -13
- package/src/skills/e2e-playwright/test-cases/TC-001-basic-navigation.yaml +0 -54
- package/src/skills/e2e-playwright/test-cases/TC-002-form-interaction.yaml +0 -64
- package/src/skills/e2e-playwright/test-cases/TC-003-specweave-integration.yaml +0 -74
- package/src/skills/e2e-playwright/test-cases/TC-004-accessibility-check.yaml +0 -98
- package/src/skills/figma-designer/test-cases/test-1-design-system.yaml +0 -13
- package/src/skills/figma-designer/test-cases/test-2-component-library.yaml +0 -13
- package/src/skills/figma-designer/test-cases/test-3-responsive-layout.yaml +0 -13
- package/src/skills/figma-implementer/test-cases/test-1-design-to-react.yaml +0 -13
- package/src/skills/figma-implementer/test-cases/test-2-storybook.yaml +0 -13
- package/src/skills/figma-implementer/test-cases/test-3-design-tokens.yaml +0 -13
- package/src/skills/figma-mcp-connector/test-cases/test-1-read-file-desktop.yaml +0 -22
- package/src/skills/figma-mcp-connector/test-cases/test-2-read-file-framelink.yaml +0 -21
- package/src/skills/figma-mcp-connector/test-cases/test-3-error-handling.yaml +0 -18
- package/src/skills/figma-to-code/test-cases/test-1-token-generation.yaml +0 -29
- package/src/skills/figma-to-code/test-cases/test-2-component-generation.yaml +0 -27
- package/src/skills/figma-to-code/test-cases/test-3-typescript-generation.yaml +0 -28
- package/src/skills/frontend/test-cases/test-1-react-component.yaml +0 -13
- package/src/skills/frontend/test-cases/test-2-form-validation.yaml +0 -13
- package/src/skills/frontend/test-cases/test-3-state-management.yaml +0 -13
- package/src/skills/github-sync/test-cases/test-1-placeholder.yaml +0 -12
- package/src/skills/github-sync/test-cases/test-2-placeholder.yaml +0 -12
- package/src/skills/github-sync/test-cases/test-3-placeholder.yaml +0 -12
- package/src/skills/hetzner-provisioner/test-cases/test-1-basic-provision.yaml +0 -71
- package/src/skills/hetzner-provisioner/test-cases/test-2-postgres-provision.yaml +0 -85
- package/src/skills/hetzner-provisioner/test-cases/test-3-ssl-config.yaml +0 -126
- package/src/skills/hetzner-provisioner/test-results/README.md +0 -259
- package/src/skills/increment-planner/test-cases/test-1-basic-feature.yaml +0 -27
- package/src/skills/increment-planner/test-cases/test-2-complex-feature.yaml +0 -30
- package/src/skills/increment-planner/test-cases/test-3-auto-numbering.yaml +0 -24
- package/src/skills/increment-quality-judge/test-cases/test-1-good-spec.yaml +0 -95
- package/src/skills/increment-quality-judge/test-cases/test-2-poor-spec.yaml +0 -108
- package/src/skills/increment-quality-judge/test-cases/test-3-export-suggestions.yaml +0 -87
- package/src/skills/jira-sync/test-cases/test-1.yaml +0 -9
- package/src/skills/jira-sync/test-cases/test-2.yaml +0 -9
- package/src/skills/jira-sync/test-cases/test-3.yaml +0 -10
- package/src/skills/nextjs/test-cases/test-1-app-router.yaml +0 -13
- package/src/skills/nextjs/test-cases/test-2-server-actions.yaml +0 -13
- package/src/skills/nextjs/test-cases/test-3-api-routes.yaml +0 -13
- package/src/skills/nodejs-backend/test-cases/test-1-express-api.yaml +0 -13
- package/src/skills/nodejs-backend/test-cases/test-2-prisma-orm.yaml +0 -13
- package/src/skills/nodejs-backend/test-cases/test-3-authentication.yaml +0 -13
- package/src/skills/notification-system/test-cases/test-1-placeholder.yaml +0 -12
- package/src/skills/notification-system/test-cases/test-2-placeholder.yaml +0 -12
- package/src/skills/notification-system/test-cases/test-3-placeholder.yaml +0 -12
- package/src/skills/python-backend/test-cases/test-1-fastapi-crud.yaml +0 -13
- package/src/skills/python-backend/test-cases/test-2-sqlalchemy.yaml +0 -13
- package/src/skills/python-backend/test-cases/test-3-authentication.yaml +0 -13
- package/src/skills/role-orchestrator/test-cases/test-1-simple-product.yaml +0 -98
- package/src/skills/role-orchestrator/test-cases/test-2-quality-gate-failure.yaml +0 -73
- package/src/skills/role-orchestrator/test-cases/test-3-security-workflow.yaml +0 -121
- package/src/skills/role-orchestrator/test-cases/test-4-parallel-execution.yaml +0 -145
- package/src/skills/role-orchestrator/test-cases/test-5-feedback-loops.yaml +0 -149
- package/src/skills/skill-creator/test-cases/test-1-placeholder.yaml +0 -12
- package/src/skills/skill-creator/test-cases/test-2-placeholder.yaml +0 -12
- package/src/skills/skill-creator/test-cases/test-3-placeholder.yaml +0 -12
- package/src/skills/skill-router/test-cases/test-1-basic-routing.yaml +0 -33
- package/src/skills/skill-router/test-cases/test-2-ambiguous-request.yaml +0 -42
- package/src/skills/skill-router/test-cases/test-3-nested-orchestration.yaml +0 -50
- package/src/skills/spec-driven-brainstorming/test-cases/TC-001-simple-idea-to-design.yaml +0 -148
- package/src/skills/spec-driven-brainstorming/test-cases/TC-002-complex-ultrathink-design.yaml +0 -190
- package/src/skills/spec-driven-brainstorming/test-cases/TC-003-unclear-requirements-socratic.yaml +0 -233
- package/src/skills/spec-driven-debugging/test-cases/TC-001-simple-auth-bug.yaml +0 -212
- package/src/skills/spec-driven-debugging/test-cases/TC-002-race-condition-ultrathink.yaml +0 -461
- package/src/skills/spec-driven-debugging/test-cases/TC-003-brownfield-missing-spec.yaml +0 -366
- package/src/skills/spec-kit-expert/test-cases/test-1-placeholder.yaml +0 -12
- package/src/skills/spec-kit-expert/test-cases/test-2-placeholder.yaml +0 -12
- package/src/skills/spec-kit-expert/test-cases/test-3-placeholder.yaml +0 -12
- package/src/skills/specweave-ado-mapper/test-cases/test-1-export-to-ado.yaml +0 -13
- package/src/skills/specweave-ado-mapper/test-cases/test-2-import-from-ado.yaml +0 -13
- package/src/skills/specweave-ado-mapper/test-cases/test-3-bidirectional-sync.yaml +0 -13
- package/src/skills/specweave-detector/test-cases/test-1-basic-detection.yaml +0 -37
- package/src/skills/specweave-detector/test-cases/test-2-missing-config.yaml +0 -37
- package/src/skills/specweave-detector/test-cases/test-3-non-specweave-project.yaml +0 -34
- package/src/skills/specweave-jira-mapper/test-cases/test-1-export-to-jira.yaml +0 -13
- package/src/skills/specweave-jira-mapper/test-cases/test-2-import-from-jira.yaml +0 -13
- package/src/skills/specweave-jira-mapper/test-cases/test-3-sync-status.yaml +0 -13
- package/src/skills/stripe-integrator/test-cases/test-1-placeholder.yaml +0 -12
- package/src/skills/stripe-integrator/test-cases/test-2-placeholder.yaml +0 -12
- package/src/skills/stripe-integrator/test-cases/test-3-placeholder.yaml +0 -12
- package/src/skills/task-builder/test-cases/test-1-placeholder.yaml +0 -12
- package/src/skills/task-builder/test-cases/test-2-placeholder.yaml +0 -12
- package/src/skills/task-builder/test-cases/test-3-placeholder.yaml +0 -12
- package/src/templates/config.yaml +0 -351
- /package/src/commands/{list-increments.md → specweave.list-increments.md} +0 -0
|
@@ -1,461 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: "Race Condition Bug Requiring Ultrathink"
|
|
3
|
-
description: "Debug intermittent race condition in distributed task queue with 3+ failed fixes requiring deep analysis"
|
|
4
|
-
skill: spec-driven-debugging
|
|
5
|
-
priority: P1
|
|
6
|
-
estimated_time: "45-60 minutes (includes ultrathink)"
|
|
7
|
-
|
|
8
|
-
input:
|
|
9
|
-
prompt: "Task queue occasionally processes the same task twice, violating exactly-once delivery guarantee"
|
|
10
|
-
context:
|
|
11
|
-
project_type: "Node.js + TypeScript + Redis + PostgreSQL"
|
|
12
|
-
increment: ".specweave/increments/0005-task-queue/"
|
|
13
|
-
complexity: "High (distributed systems, concurrency)"
|
|
14
|
-
files_exist:
|
|
15
|
-
- "spec.md"
|
|
16
|
-
- "plan.md"
|
|
17
|
-
- "tests.md"
|
|
18
|
-
- "src/queue/task-processor.ts"
|
|
19
|
-
- "src/queue/outbox-poller.ts"
|
|
20
|
-
- "src/db/task-repository.ts"
|
|
21
|
-
error_pattern: "Intermittent - 1-2% of tasks processed twice"
|
|
22
|
-
reproduction: "Inconsistent - happens under load, not in dev"
|
|
23
|
-
|
|
24
|
-
expected_output:
|
|
25
|
-
phase_0_context_loading:
|
|
26
|
-
announcement: "Loading SpecWeave context for distributed systems bug."
|
|
27
|
-
|
|
28
|
-
files_loaded:
|
|
29
|
-
- path: ".specweave/increments/0005-task-queue/spec.md"
|
|
30
|
-
key_findings:
|
|
31
|
-
- "NFR-001: Exactly-once delivery guarantee for all tasks"
|
|
32
|
-
- "NFR-002: Handle 10,000 tasks/sec at peak"
|
|
33
|
-
- "FR-005: Tasks must be idempotent"
|
|
34
|
-
- "Acceptance: TC-003: No task processed more than once"
|
|
35
|
-
|
|
36
|
-
- path: ".specweave/increments/0005-task-queue/plan.md"
|
|
37
|
-
key_findings:
|
|
38
|
-
- "Event sourcing with outbox pattern"
|
|
39
|
-
- "PostgreSQL for transactional outbox"
|
|
40
|
-
- "Redis Streams for queue"
|
|
41
|
-
- "Idempotency keys (UUID v4)"
|
|
42
|
-
- "Distributed locks via Redis"
|
|
43
|
-
|
|
44
|
-
- path: ".specweave/docs/internal/architecture/adr/0007-exactly-once-semantics.md"
|
|
45
|
-
key_findings:
|
|
46
|
-
- "Decision: Use transactional outbox + idempotency keys"
|
|
47
|
-
- "At-least-once delivery + idempotent consumers = exactly-once effect"
|
|
48
|
-
- "Lock timeout: 30 seconds"
|
|
49
|
-
- "Idempotency key stored in tasks table (unique constraint)"
|
|
50
|
-
|
|
51
|
-
bug_classification: "Likely architectural issue - race condition in distributed system"
|
|
52
|
-
ultrathink_suggestion: "This is a distributed systems bug with race conditions. Should I **ultrathink** this to analyze all concurrency scenarios?"
|
|
53
|
-
|
|
54
|
-
phase_1_root_cause:
|
|
55
|
-
error_analysis:
|
|
56
|
-
symptom: "Task processed twice (duplicate charge, duplicate email)"
|
|
57
|
-
frequency: "1-2% under load (>5000 tasks/sec)"
|
|
58
|
-
environment: "Production only, not reproducible in dev"
|
|
59
|
-
logs:
|
|
60
|
-
- "[Worker-1] Processing task abc123 with lock"
|
|
61
|
-
- "[Worker-2] Processing task abc123 with lock" # BOTH got lock!
|
|
62
|
-
|
|
63
|
-
reproduction_attempts:
|
|
64
|
-
attempt_1:
|
|
65
|
-
approach: "Manual testing in dev"
|
|
66
|
-
result: "Cannot reproduce (too slow)"
|
|
67
|
-
|
|
68
|
-
attempt_2:
|
|
69
|
-
approach: "Load testing with 100 concurrent workers"
|
|
70
|
-
result: "Reproduced! 15 duplicates out of 1000 tasks"
|
|
71
|
-
|
|
72
|
-
attempt_3:
|
|
73
|
-
approach: "Added detailed logging at every step"
|
|
74
|
-
result: "Both workers believe they have exclusive lock"
|
|
75
|
-
|
|
76
|
-
spec_comparison:
|
|
77
|
-
spec_says: "NFR-001: Exactly-once delivery (no duplicates)"
|
|
78
|
-
actual_behavior: "1-2% duplicate processing under load"
|
|
79
|
-
discrepancy: "Race condition in lock acquisition or idempotency check"
|
|
80
|
-
|
|
81
|
-
multi_component_instrumentation:
|
|
82
|
-
component_1_outbox_poller:
|
|
83
|
-
log: "console.log('[Outbox] Task ${id} marked as processing by ${workerId}')"
|
|
84
|
-
finding: "✓ Correctly marks task in DB"
|
|
85
|
-
|
|
86
|
-
component_2_redis_lock:
|
|
87
|
-
log: "console.log('[Lock] Acquired lock ${taskId} for ${workerId}: ${acquired}')"
|
|
88
|
-
finding: "❌ Both workers report acquiring lock successfully!"
|
|
89
|
-
|
|
90
|
-
component_3_task_processing:
|
|
91
|
-
log: "console.log('[Processor] Starting task ${id} by ${workerId}')"
|
|
92
|
-
finding: "❌ Two workers process same task"
|
|
93
|
-
|
|
94
|
-
component_4_idempotency_check:
|
|
95
|
-
log: "console.log('[Idempotency] Key ${key} exists: ${exists}')"
|
|
96
|
-
finding: "✓ Idempotency key checked, but AFTER lock acquired"
|
|
97
|
-
|
|
98
|
-
data_flow_trace:
|
|
99
|
-
failing_scenario:
|
|
100
|
-
- step: "Worker-1 reads task from outbox"
|
|
101
|
-
timestamp: "t=100ms"
|
|
102
|
-
- step: "Worker-2 reads task from outbox"
|
|
103
|
-
timestamp: "t=102ms" # race window!
|
|
104
|
-
- step: "Worker-1 tries to acquire Redis lock"
|
|
105
|
-
timestamp: "t=110ms"
|
|
106
|
-
result: "SUCCESS"
|
|
107
|
-
- step: "Worker-2 tries to acquire Redis lock"
|
|
108
|
-
timestamp: "t=111ms"
|
|
109
|
-
result: "SUCCESS" # BOTH succeeded!
|
|
110
|
-
- step: "Worker-1 checks idempotency key (doesn't exist)"
|
|
111
|
-
timestamp: "t=115ms"
|
|
112
|
-
- step: "Worker-2 checks idempotency key (doesn't exist)"
|
|
113
|
-
timestamp: "t=115ms" # same time!
|
|
114
|
-
- step: "Worker-1 inserts idempotency key"
|
|
115
|
-
timestamp: "t=120ms"
|
|
116
|
-
- step: "Worker-2 inserts idempotency key"
|
|
117
|
-
timestamp: "t=121ms"
|
|
118
|
-
result: "Unique constraint violation (ignored)"
|
|
119
|
-
- step: "Both workers process task"
|
|
120
|
-
timestamp: "t=130ms"
|
|
121
|
-
|
|
122
|
-
root_cause_hypothesis_1: "Redis lock implementation has race condition"
|
|
123
|
-
|
|
124
|
-
phase_2_pattern_analysis:
|
|
125
|
-
working_examples: "No working examples - this is the only distributed lock usage"
|
|
126
|
-
|
|
127
|
-
redis_lock_code:
|
|
128
|
-
file: "src/queue/redis-lock.ts"
|
|
129
|
-
implementation: |
|
|
130
|
-
async acquireLock(taskId: string): Promise<boolean> {
|
|
131
|
-
const lockKey = `lock:${taskId}`;
|
|
132
|
-
const result = await redis.set(lockKey, workerId, {
|
|
133
|
-
NX: true, // Only set if not exists
|
|
134
|
-
EX: 30 // Expire in 30 seconds
|
|
135
|
-
});
|
|
136
|
-
return result === 'OK';
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
idempotency_check_code:
|
|
140
|
-
file: "src/queue/task-processor.ts"
|
|
141
|
-
implementation: |
|
|
142
|
-
async processTask(task: Task) {
|
|
143
|
-
const locked = await acquireLock(task.id);
|
|
144
|
-
if (!locked) return; // already processing
|
|
145
|
-
|
|
146
|
-
// Check idempotency (AFTER lock)
|
|
147
|
-
const exists = await db.idempotencyKeys.findUnique({
|
|
148
|
-
where: { key: task.idempotencyKey }
|
|
149
|
-
});
|
|
150
|
-
if (exists) return; // already processed
|
|
151
|
-
|
|
152
|
-
// Process task
|
|
153
|
-
await doWork(task);
|
|
154
|
-
|
|
155
|
-
// Insert idempotency key
|
|
156
|
-
await db.idempotencyKeys.create({
|
|
157
|
-
data: { key: task.idempotencyKey, taskId: task.id }
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
problem_identified:
|
|
162
|
-
issue: "Lock and idempotency check are separate, creating race window"
|
|
163
|
-
sequence_problem:
|
|
164
|
-
- "Worker-1 acquires lock"
|
|
165
|
-
- "Worker-2 acquires lock (shouldn't be possible but happens)"
|
|
166
|
-
- "Both check idempotency (both see 'not exists')"
|
|
167
|
-
- "Both process task"
|
|
168
|
-
|
|
169
|
-
phase_3_hypothesis:
|
|
170
|
-
hypothesis_1:
|
|
171
|
-
statement: "Redis SET NX command has race condition under load"
|
|
172
|
-
test: "Add logging before and after SET NX call"
|
|
173
|
-
result: "FAILED - SET NX is atomic, not the issue"
|
|
174
|
-
attempts: 1
|
|
175
|
-
|
|
176
|
-
hypothesis_2:
|
|
177
|
-
statement: "Lock timeout (30s) expires before task completes, allowing second worker to acquire lock"
|
|
178
|
-
test: "Reduce task processing time to 5 seconds, check if duplicates persist"
|
|
179
|
-
result: "FAILED - Duplicates still occur even with 5s tasks"
|
|
180
|
-
attempts: 2
|
|
181
|
-
|
|
182
|
-
hypothesis_3:
|
|
183
|
-
statement: "Idempotency check should be BEFORE lock, not after"
|
|
184
|
-
test: "Move idempotency check before lock acquisition"
|
|
185
|
-
result: "FAILED - Still get duplicates, now with unique constraint violations"
|
|
186
|
-
attempts: 3
|
|
187
|
-
|
|
188
|
-
ultrathink_triggered: true
|
|
189
|
-
ultrathink_reason: "3 failed hypotheses - this is an architectural issue, not a simple code bug"
|
|
190
|
-
|
|
191
|
-
ultrathink_mode_activated:
|
|
192
|
-
thinking_tokens: 31999
|
|
193
|
-
analysis_depth: "Deep concurrency analysis across all components"
|
|
194
|
-
|
|
195
|
-
ultrathink_analysis:
|
|
196
|
-
problem_space:
|
|
197
|
-
- "Distributed system with multiple concurrent workers"
|
|
198
|
-
- "Two independent systems for consistency: Redis locks + DB idempotency keys"
|
|
199
|
-
- "Race window exists between these two systems"
|
|
200
|
-
|
|
201
|
-
edge_cases_explored:
|
|
202
|
-
- "Network partition between Redis and PostgreSQL"
|
|
203
|
-
- "Redis failover during lock acquisition"
|
|
204
|
-
- "Clock skew between workers"
|
|
205
|
-
- "Transaction isolation levels in PostgreSQL"
|
|
206
|
-
- "Redis persistence settings (AOF vs RDB)"
|
|
207
|
-
|
|
208
|
-
architectural_flaw_identified:
|
|
209
|
-
root_cause: "Fundamental design issue: Two separate consistency mechanisms (Redis lock + DB idempotency) are not atomic"
|
|
210
|
-
why_it_fails: |
|
|
211
|
-
Under high concurrency:
|
|
212
|
-
1. Redis lock prevents MOST concurrent processing
|
|
213
|
-
2. But Redis and PostgreSQL are separate systems
|
|
214
|
-
3. Tiny race window exists between lock acquisition and idempotency insert
|
|
215
|
-
4. Under load (5000+ tasks/sec), workers hit this window
|
|
216
|
-
|
|
217
|
-
alternative_architectures_evaluated:
|
|
218
|
-
approach_1:
|
|
219
|
-
name: "Single Source of Truth (DB-only locking)"
|
|
220
|
-
implementation: "PostgreSQL advisory locks + idempotency key in same transaction"
|
|
221
|
-
pros:
|
|
222
|
-
- "Single atomic operation (no race window)"
|
|
223
|
-
- "Simpler (one system, not two)"
|
|
224
|
-
cons:
|
|
225
|
-
- "PostgreSQL becomes bottleneck (can't scale horizontally)"
|
|
226
|
-
- "Might not support 10K tasks/sec requirement"
|
|
227
|
-
verdict: "Correct for consistency, but may not meet performance NFR"
|
|
228
|
-
|
|
229
|
-
approach_2:
|
|
230
|
-
name: "Idempotency Key as Primary Lock"
|
|
231
|
-
implementation: "Skip Redis lock entirely, use DB unique constraint as lock"
|
|
232
|
-
pros:
|
|
233
|
-
- "Single atomic operation (INSERT with UNIQUE constraint)"
|
|
234
|
-
- "Exactly-once guaranteed by DB"
|
|
235
|
-
cons:
|
|
236
|
-
- "Every worker attempts INSERT (thundering herd on DB)"
|
|
237
|
-
- "Failed inserts waste DB connections"
|
|
238
|
-
verdict: "Correct but inefficient"
|
|
239
|
-
|
|
240
|
-
approach_3:
|
|
241
|
-
name: "Hybrid: Redis pre-filter + DB transaction"
|
|
242
|
-
implementation: |
|
|
243
|
-
1. Redis lock as fast pre-filter (optimistic)
|
|
244
|
-
2. DB transaction with idempotency INSERT (pessimistic, source of truth)
|
|
245
|
-
3. If DB INSERT fails (unique violation) → already processed by another worker
|
|
246
|
-
pros:
|
|
247
|
-
- "Best of both: Redis speed + DB consistency"
|
|
248
|
-
- "Redis filters out MOST duplicate attempts"
|
|
249
|
-
- "DB guarantees exactly-once"
|
|
250
|
-
cons:
|
|
251
|
-
- "Still two systems, but DB is source of truth"
|
|
252
|
-
- "Graceful handling of unique violations needed"
|
|
253
|
-
verdict: "Best approach - maintains performance AND correctness"
|
|
254
|
-
|
|
255
|
-
recommended_solution:
|
|
256
|
-
approach: "Approach 3: Hybrid with DB as source of truth"
|
|
257
|
-
changes_needed:
|
|
258
|
-
- "Keep Redis lock (performance optimization)"
|
|
259
|
-
- "Move idempotency INSERT to BEGINNING of transaction"
|
|
260
|
-
- "Wrap task processing in transaction with idempotency INSERT"
|
|
261
|
-
- "Gracefully handle unique constraint violations (not errors, expected)"
|
|
262
|
-
|
|
263
|
-
phase_4_implementation:
|
|
264
|
-
fix_level: "Architecture-level fix"
|
|
265
|
-
reason: "Fundamental design flaw: two separate consistency mechanisms not atomic"
|
|
266
|
-
|
|
267
|
-
adr_creation:
|
|
268
|
-
needed: true
|
|
269
|
-
file: ".specweave/docs/internal/architecture/adr/0012-fix-exactly-once-with-hybrid-lock.md"
|
|
270
|
-
content: |
|
|
271
|
-
# ADR-0012: Fix Exactly-Once Delivery with Hybrid Lock
|
|
272
|
-
|
|
273
|
-
## Context
|
|
274
|
-
Current implementation uses Redis locks + DB idempotency keys as two
|
|
275
|
-
separate systems. Race window exists between lock acquisition and
|
|
276
|
-
idempotency check, causing 1-2% duplicate processing under load.
|
|
277
|
-
|
|
278
|
-
## Decision
|
|
279
|
-
Use hybrid approach: Redis lock as optimistic pre-filter, DB
|
|
280
|
-
idempotency key INSERT as pessimistic source of truth within
|
|
281
|
-
transaction.
|
|
282
|
-
|
|
283
|
-
## Consequences
|
|
284
|
-
- Exactly-once guarantee restored
|
|
285
|
-
- Performance maintained (Redis filters most duplicates)
|
|
286
|
-
- DB is source of truth (no race window)
|
|
287
|
-
- Graceful handling of unique violations (expected, not errors)
|
|
288
|
-
|
|
289
|
-
test_creation:
|
|
290
|
-
level: "Level 2: Feature Tests + Level 3: Code Tests"
|
|
291
|
-
files:
|
|
292
|
-
- path: ".specweave/increments/0005-task-queue/tests.md"
|
|
293
|
-
test_case: "TC-008: Concurrency test with 100 workers processing same task → exactly one processes"
|
|
294
|
-
|
|
295
|
-
- path: "tests/integration/task-queue.test.ts"
|
|
296
|
-
test_case: |
|
|
297
|
-
describe('Task Queue - Exactly-Once', () => {
|
|
298
|
-
it('should process task exactly once with 100 concurrent workers', async () => {
|
|
299
|
-
const task = await createTask();
|
|
300
|
-
const workers = Array.from({ length: 100 }, (_, i) =>
|
|
301
|
-
processTask(task.id, `worker-${i}`)
|
|
302
|
-
);
|
|
303
|
-
|
|
304
|
-
const results = await Promise.all(workers);
|
|
305
|
-
const processed = results.filter(r => r.success).length;
|
|
306
|
-
|
|
307
|
-
expect(processed).toBe(1); // exactly one succeeded
|
|
308
|
-
expect(results.filter(r => r.alreadyProcessed).length).toBe(99);
|
|
309
|
-
});
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
test_fails_before_fix: true
|
|
313
|
-
|
|
314
|
-
spec_update_needed: false # spec was correct (NFR-001)
|
|
315
|
-
|
|
316
|
-
code_fix:
|
|
317
|
-
file: "src/queue/task-processor.ts"
|
|
318
|
-
before: |
|
|
319
|
-
async processTask(task: Task) {
|
|
320
|
-
const locked = await acquireLock(task.id);
|
|
321
|
-
if (!locked) return;
|
|
322
|
-
|
|
323
|
-
const exists = await db.idempotencyKeys.findUnique({
|
|
324
|
-
where: { key: task.idempotencyKey }
|
|
325
|
-
});
|
|
326
|
-
if (exists) return;
|
|
327
|
-
|
|
328
|
-
await doWork(task);
|
|
329
|
-
await db.idempotencyKeys.create({
|
|
330
|
-
data: { key: task.idempotencyKey }
|
|
331
|
-
});
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
after: |
|
|
335
|
-
async processTask(task: Task) {
|
|
336
|
-
// Redis lock as optimistic pre-filter (performance)
|
|
337
|
-
const locked = await acquireLock(task.id);
|
|
338
|
-
if (!locked) return { success: false, reason: 'lock-failed' };
|
|
339
|
-
|
|
340
|
-
try {
|
|
341
|
-
// DB transaction with idempotency INSERT (source of truth)
|
|
342
|
-
await db.$transaction(async (tx) => {
|
|
343
|
-
// Insert idempotency key FIRST (atomic lock)
|
|
344
|
-
await tx.idempotencyKeys.create({
|
|
345
|
-
data: {
|
|
346
|
-
key: task.idempotencyKey,
|
|
347
|
-
taskId: task.id,
|
|
348
|
-
processedAt: new Date()
|
|
349
|
-
}
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
// Process task (only if INSERT succeeded)
|
|
353
|
-
await doWork(task);
|
|
354
|
-
});
|
|
355
|
-
|
|
356
|
-
return { success: true };
|
|
357
|
-
} catch (error) {
|
|
358
|
-
// Unique constraint violation = already processed (expected!)
|
|
359
|
-
if (error.code === 'P2002') { // Prisma unique violation
|
|
360
|
-
return { success: false, reason: 'already-processed' };
|
|
361
|
-
}
|
|
362
|
-
throw error; // unexpected error
|
|
363
|
-
} finally {
|
|
364
|
-
await releaseLock(task.id);
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
verification:
|
|
369
|
-
test_passes: true
|
|
370
|
-
load_test_results:
|
|
371
|
-
tasks: 10000
|
|
372
|
-
concurrent_workers: 100
|
|
373
|
-
duplicates: 0 # ✓ Fixed!
|
|
374
|
-
duration: "45 seconds"
|
|
375
|
-
spec_aligned: true
|
|
376
|
-
|
|
377
|
-
attempts_count: 4 # 3 failed + 1 ultrathink success
|
|
378
|
-
ultrathink_used: true
|
|
379
|
-
|
|
380
|
-
phase_5_documentation:
|
|
381
|
-
increment_updates:
|
|
382
|
-
- file: ".specweave/increments/0005-task-queue/tests.md"
|
|
383
|
-
change: "Added TC-008: Concurrency test for exactly-once guarantee"
|
|
384
|
-
|
|
385
|
-
- file: ".specweave/increments/0005-task-queue/plan.md"
|
|
386
|
-
change: "Updated architecture section to reflect hybrid lock approach"
|
|
387
|
-
|
|
388
|
-
architecture_updates:
|
|
389
|
-
- file: ".specweave/docs/internal/architecture/adr/0012-fix-exactly-once-with-hybrid-lock.md"
|
|
390
|
-
change: "Created ADR documenting architectural decision"
|
|
391
|
-
|
|
392
|
-
- file: ".specweave/docs/internal/architecture/system-design.md"
|
|
393
|
-
change: "Updated task queue component diagram"
|
|
394
|
-
|
|
395
|
-
strategy_updates: [] # requirements unchanged
|
|
396
|
-
|
|
397
|
-
new_increment_needed: false # fixed within existing increment
|
|
398
|
-
|
|
399
|
-
commit_message: |
|
|
400
|
-
fix(queue): guarantee exactly-once delivery with hybrid lock
|
|
401
|
-
|
|
402
|
-
Root cause: Race condition between Redis lock and DB idempotency
|
|
403
|
-
check. Two separate systems (Redis + PostgreSQL) created race
|
|
404
|
-
window under high concurrency (>5000 tasks/sec).
|
|
405
|
-
|
|
406
|
-
After 3 failed hypotheses, ultrathinked the architecture:
|
|
407
|
-
- Analyzed edge cases: network partitions, clock skew, failover
|
|
408
|
-
- Evaluated 3 alternative architectures
|
|
409
|
-
- Chose hybrid approach: Redis pre-filter + DB source of truth
|
|
410
|
-
|
|
411
|
-
Solution: Moved idempotency INSERT to beginning of DB transaction.
|
|
412
|
-
DB unique constraint is now atomic lock (source of truth).
|
|
413
|
-
Redis lock remains as performance optimization (filters most duplicates).
|
|
414
|
-
|
|
415
|
-
Spec alignment: Restores NFR-001 (exactly-once delivery)
|
|
416
|
-
|
|
417
|
-
Test coverage:
|
|
418
|
-
- Added: TC-008 concurrency test (100 workers, 0 duplicates)
|
|
419
|
-
- Load tested: 10K tasks, 100 workers, 0 duplicates
|
|
420
|
-
- Verified: All existing tests pass
|
|
421
|
-
|
|
422
|
-
Documentation updated:
|
|
423
|
-
- ADR-0012: Documents architectural decision and trade-offs
|
|
424
|
-
- plan.md: Updated with hybrid lock architecture
|
|
425
|
-
- tests.md: Added TC-008 for regression prevention
|
|
426
|
-
- system-design.md: Updated task queue component diagram
|
|
427
|
-
|
|
428
|
-
Ultrathink analysis (31,999 tokens) identified fundamental
|
|
429
|
-
architectural issue after 3 failed fix attempts.
|
|
430
|
-
|
|
431
|
-
Fixes: #89
|
|
432
|
-
|
|
433
|
-
🤖 Generated with Claude Code
|
|
434
|
-
Co-Authored-By: Claude <noreply@anthropic.com>
|
|
435
|
-
|
|
436
|
-
validation:
|
|
437
|
-
- "Phase 0: Loaded spec, plan, ADR for distributed system context"
|
|
438
|
-
- "Phase 0: Suggested ultrathink for complex distributed bug"
|
|
439
|
-
- "Phase 1: Reproduced under load (load testing)"
|
|
440
|
-
- "Phase 1: Added instrumentation across all components"
|
|
441
|
-
- "Phase 1: Traced data flow to identify race window"
|
|
442
|
-
- "Phase 2: Analyzed lock implementation and identified architectural flaw"
|
|
443
|
-
- "Phase 3: Tested 3 hypotheses (all failed)"
|
|
444
|
-
- "Phase 3: Triggered ultrathink after 3 failures (MANDATORY)"
|
|
445
|
-
- "Ultrathink: Analyzed edge cases, evaluated 3 architectures"
|
|
446
|
-
- "Ultrathink: Recommended hybrid approach with DB as source of truth"
|
|
447
|
-
- "Phase 4: Created ADR documenting architectural decision"
|
|
448
|
-
- "Phase 4: Fixed with architectural refactor (not symptom patch)"
|
|
449
|
-
- "Phase 4: Load tested to verify (0 duplicates out of 10K tasks)"
|
|
450
|
-
- "Phase 5: Updated plan.md, tests.md, ADR, system-design.md"
|
|
451
|
-
|
|
452
|
-
expected_errors: []
|
|
453
|
-
|
|
454
|
-
success_criteria:
|
|
455
|
-
- "Ultrathink activated after 3 failed fixes"
|
|
456
|
-
- "Architectural flaw identified (not just code bug)"
|
|
457
|
-
- "ADR created documenting decision"
|
|
458
|
-
- "Exactly-once guarantee restored (0 duplicates in load test)"
|
|
459
|
-
- "Performance maintained (Redis pre-filter)"
|
|
460
|
-
- "Living documentation comprehensive (ADR, plan, tests, design)"
|
|
461
|
-
---
|