mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-15 02:42:45 +08:00
feat: add Terminal Dashboard components and state management
- Implemented DashboardToolbar for managing panel toggles and layout presets. - Created FloatingPanel for a generic sliding panel interface. - Developed TerminalGrid for rendering a recursive layout of terminal panes. - Added TerminalPane to encapsulate individual terminal instances with toolbar actions. - Introduced layout utilities for managing Allotment layout trees. - Established Zustand store for terminal grid state management, supporting pane operations and layout resets.
This commit is contained in:
@@ -169,7 +169,7 @@ TaskCreate({ subject: "DISCUSS-005: 执行计划与MVP范围讨论", description
|
||||
TaskUpdate({ taskId: discuss5Id, owner: "discussant", addBlockedBy: [draft4Id] })
|
||||
|
||||
// QUALITY-001: Readiness Check (blockedBy DISCUSS-005)
|
||||
TaskCreate({ subject: "QUALITY-001: 规格就绪度检查", description: `全文档交叉验证和质量评分\n\nSession: ${specSessionFolder}\n输入: 全部文档\n输出: ${specSessionFolder}/readiness-report.md + spec-summary.md\n\n评分维度: 完整性(25%) + 一致性(25%) + 可追溯性(25%) + 深度(25%)`, activeForm: "质量检查中" })
|
||||
TaskCreate({ subject: "QUALITY-001: 规格就绪度检查", description: `全文档交叉验证和质量评分\n\nSession: ${specSessionFolder}\n输入: 全部文档\n输出: ${specSessionFolder}/readiness-report.md + spec-summary.md\n\n评分维度: 完整性(20%) + 一致性(20%) + 可追溯性(20%) + 深度(20%) + 需求覆盖率(20%)`, activeForm: "质量检查中" })
|
||||
TaskUpdate({ taskId: qualityId, owner: "reviewer", addBlockedBy: [discuss5Id] })
|
||||
|
||||
// DISCUSS-006: 最终签收 (blockedBy QUALITY-001)
|
||||
@@ -217,7 +217,7 @@ Receive teammate messages and make dispatch decisions. **Before each decision: `
|
||||
|
||||
| Received Message | Action |
|
||||
|-----------------|--------|
|
||||
| Analyst: research_ready | Read discovery-context.json → team_msg log → TaskUpdate RESEARCH completed (auto-unblocks DISCUSS-001) |
|
||||
| Analyst: research_ready | Read discovery-context.json → **用户确认检查点** → team_msg log → TaskUpdate RESEARCH completed (auto-unblocks DISCUSS-001) |
|
||||
| Discussant: discussion_ready | Read discussion.md → judge if revision needed → unblock next DRAFT task |
|
||||
| Discussant: discussion_blocked | Intervene → AskUserQuestion for user decision → write decision to discussion record → manually unblock |
|
||||
| Writer: draft_ready | Read document summary → team_msg log → TaskUpdate DRAFT completed (auto-unblocks next DISCUSS) |
|
||||
@@ -242,6 +242,46 @@ Receive teammate messages and make dispatch decisions. **Before each decision: `
|
||||
|
||||
When DISCUSS-006 completes in full-lifecycle mode, PLAN-001 is auto-unblocked via the dependency chain.
|
||||
|
||||
#### Research Confirmation Checkpoint
|
||||
|
||||
When receiving `research_ready` from analyst, confirm extracted requirements with user before unblocking:
|
||||
|
||||
```javascript
|
||||
if (msgType === 'research_ready') {
|
||||
const discoveryContext = JSON.parse(Read(`${specSessionFolder}/discovery-context.json`))
|
||||
const dimensions = discoveryContext.seed_analysis?.exploration_dimensions || []
|
||||
const constraints = discoveryContext.seed_analysis?.constraints || []
|
||||
const problemStatement = discoveryContext.seed_analysis?.problem_statement || ''
|
||||
|
||||
// Present extracted requirements for user confirmation
|
||||
AskUserQuestion({
|
||||
questions: [{
|
||||
question: `研究阶段提取到以下需求,请确认是否完整:\n\n**问题定义**: ${problemStatement}\n**探索维度**: ${dimensions.join('、')}\n**约束条件**: ${constraints.join('、')}\n\n是否有遗漏?`,
|
||||
header: "需求确认",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: "确认完整", description: "提取的需求已覆盖所有关键点,继续推进" },
|
||||
{ label: "需要补充", description: "有遗漏的需求,我来补充" },
|
||||
{ label: "需要重新研究", description: "提取方向有偏差,重新执行研究" }
|
||||
]
|
||||
}]
|
||||
})
|
||||
|
||||
if (userChoice === '需要补充') {
|
||||
// User provides additional requirements via free text
|
||||
// Merge into discovery-context.json, then unblock DISCUSS-001
|
||||
discoveryContext.seed_analysis.user_supplements = userInput
|
||||
Write(`${specSessionFolder}/discovery-context.json`, JSON.stringify(discoveryContext, null, 2))
|
||||
} else if (userChoice === '需要重新研究') {
|
||||
// Reset RESEARCH-001 to pending, notify analyst
|
||||
TaskUpdate({ taskId: researchId, status: 'pending' })
|
||||
team_msg({ type: 'fix_required', summary: 'User requests re-research with revised scope' })
|
||||
return // Do not unblock DISCUSS-001
|
||||
}
|
||||
// '确认完整' → proceed normally: TaskUpdate RESEARCH completed
|
||||
}
|
||||
```
|
||||
|
||||
#### Discussion Blocked Handling
|
||||
|
||||
```javascript
|
||||
|
||||
@@ -51,17 +51,18 @@ Each discussion round analyzes from 4 perspectives:
|
||||
| **Technical** | Feasibility, tech debt, performance, security, maintainability | Tech Lead |
|
||||
| **Quality** | Completeness, testability, consistency, standards compliance | QA Lead |
|
||||
| **Risk** | Risk identification, dependency analysis, assumption validation, failure modes | Risk Analyst |
|
||||
| **Coverage** | Requirement completeness vs original intent, scope drift, gap detection | Requirements Analyst |
|
||||
|
||||
## Discussion Round Configuration
|
||||
|
||||
| Round | Artifact | Key Perspectives | Focus |
|
||||
|-------|----------|-----------------|-------|
|
||||
| DISCUSS-001 | discovery-context | product + risk | Scope confirmation, direction |
|
||||
| DISCUSS-002 | product-brief | product + technical + quality | Positioning, feasibility |
|
||||
| DISCUSS-003 | requirements | quality + product | Completeness, priority |
|
||||
| DISCUSS-001 | discovery-context | product + risk + **coverage** | Scope confirmation, direction, initial coverage check |
|
||||
| DISCUSS-002 | product-brief | product + technical + quality + **coverage** | Positioning, feasibility, requirement coverage |
|
||||
| DISCUSS-003 | requirements | quality + product + **coverage** | Completeness, priority, gap detection |
|
||||
| DISCUSS-004 | architecture | technical + risk | Tech choices, security |
|
||||
| DISCUSS-005 | epics | product + technical + quality | MVP scope, estimation |
|
||||
| DISCUSS-006 | readiness-report | all 4 perspectives | Final sign-off |
|
||||
| DISCUSS-005 | epics | product + technical + quality + **coverage** | MVP scope, estimation, requirement tracing |
|
||||
| DISCUSS-006 | readiness-report | all 5 perspectives | Final sign-off |
|
||||
|
||||
## Execution (5-Phase)
|
||||
|
||||
@@ -91,12 +92,12 @@ const roundMatch = task.subject.match(/DISCUSS-(\d+)/)
|
||||
const roundNumber = roundMatch ? parseInt(roundMatch[1]) : 0
|
||||
|
||||
const roundConfig = {
|
||||
1: { artifact: 'discovery-context.json', type: 'json', outputFile: 'discuss-001-scope.md', perspectives: ['product', 'risk'], label: '范围讨论' },
|
||||
2: { artifact: 'product-brief.md', type: 'md', outputFile: 'discuss-002-brief.md', perspectives: ['product', 'technical', 'quality'], label: 'Brief评审' },
|
||||
3: { artifact: 'requirements/_index.md', type: 'md', outputFile: 'discuss-003-requirements.md', perspectives: ['quality', 'product'], label: '需求讨论' },
|
||||
1: { artifact: 'discovery-context.json', type: 'json', outputFile: 'discuss-001-scope.md', perspectives: ['product', 'risk', 'coverage'], label: '范围讨论' },
|
||||
2: { artifact: 'product-brief.md', type: 'md', outputFile: 'discuss-002-brief.md', perspectives: ['product', 'technical', 'quality', 'coverage'], label: 'Brief评审' },
|
||||
3: { artifact: 'requirements/_index.md', type: 'md', outputFile: 'discuss-003-requirements.md', perspectives: ['quality', 'product', 'coverage'], label: '需求讨论' },
|
||||
4: { artifact: 'architecture/_index.md', type: 'md', outputFile: 'discuss-004-architecture.md', perspectives: ['technical', 'risk'], label: '架构讨论' },
|
||||
5: { artifact: 'epics/_index.md', type: 'md', outputFile: 'discuss-005-epics.md', perspectives: ['product', 'technical', 'quality'], label: 'Epics讨论' },
|
||||
6: { artifact: 'readiness-report.md', type: 'md', outputFile: 'discuss-006-final.md', perspectives: ['product', 'technical', 'quality', 'risk'], label: '最终签收' }
|
||||
5: { artifact: 'epics/_index.md', type: 'md', outputFile: 'discuss-005-epics.md', perspectives: ['product', 'technical', 'quality', 'coverage'], label: 'Epics讨论' },
|
||||
6: { artifact: 'readiness-report.md', type: 'md', outputFile: 'discuss-006-final.md', perspectives: ['product', 'technical', 'quality', 'risk', 'coverage'], label: '最终签收' }
|
||||
}
|
||||
|
||||
const config = roundConfig[roundNumber]
|
||||
@@ -112,8 +113,9 @@ Launch parallel CLI analyses for each required perspective:
|
||||
- **Technical Perspective** (codex): Feasibility, complexity, architecture decisions, tech debt risks. Rate 1-5.
|
||||
- **Quality Perspective** (claude): Completeness, testability, consistency, ambiguity detection. Rate 1-5.
|
||||
- **Risk Perspective** (gemini): Risk identification, dependency analysis, assumption validation, failure modes. Rate risk level.
|
||||
- **Coverage Perspective** (gemini): Compare current artifact against original requirements in discovery-context.json. Identify covered_requirements[], partial_requirements[], missing_requirements[], scope_creep[]. Rate coverage 1-5. **If missing_requirements is non-empty, flag as critical divergence.**
|
||||
|
||||
Each CLI call produces structured critique with: strengths[], weaknesses[], suggestions[], rating.
|
||||
Each CLI call produces structured critique with: strengths[], weaknesses[], suggestions[], rating. Coverage perspective additionally outputs: covered_requirements[], missing_requirements[], scope_creep[].
|
||||
|
||||
### Phase 4: Consensus Synthesis
|
||||
|
||||
@@ -131,6 +133,17 @@ const synthesis = {
|
||||
|
||||
// Extract convergent themes (items mentioned positively by 2+ perspectives)
|
||||
// Extract divergent views (items where perspectives conflict)
|
||||
// Check coverage gaps from coverage perspective (if present)
|
||||
const coverageResult = perspectiveResults.find(p => p.perspective === 'coverage')
|
||||
if (coverageResult?.missing_requirements?.length > 0) {
|
||||
synthesis.coverage_gaps = coverageResult.missing_requirements
|
||||
synthesis.divergent_views.push({
|
||||
topic: 'requirement_coverage_gap',
|
||||
description: `${coverageResult.missing_requirements.length} requirements from discovery-context not covered: ${coverageResult.missing_requirements.join(', ')}`,
|
||||
severity: 'high',
|
||||
source: 'coverage'
|
||||
})
|
||||
}
|
||||
// Check for unresolvable conflicts
|
||||
const criticalDivergences = synthesis.divergent_views.filter(d => d.severity === 'high')
|
||||
if (criticalDivergences.length > 0) synthesis.consensus_reached = false
|
||||
|
||||
@@ -222,7 +222,7 @@ function verifyRequirements(plan, fileContents) {
|
||||
|
||||
```javascript
|
||||
if (reviewMode === 'spec') {
|
||||
const scores = { completeness: 0, consistency: 0, traceability: 0, depth: 0 }
|
||||
const scores = { completeness: 0, consistency: 0, traceability: 0, depth: 0, requirementCoverage: 0 }
|
||||
|
||||
// Completeness (25%): all sections present with content
|
||||
function scoreCompleteness(docs) {
|
||||
@@ -297,19 +297,55 @@ if (reviewMode === 'spec') {
|
||||
return { score: Math.max(0, score), issues }
|
||||
}
|
||||
|
||||
// Requirement Coverage (20%): original requirements → document mapping
|
||||
function scoreRequirementCoverage(docs) {
|
||||
let score = 100
|
||||
const issues = []
|
||||
if (!docs.discoveryContext) {
|
||||
return { score: 0, issues: ['discovery-context.json missing, cannot verify requirement coverage'] }
|
||||
}
|
||||
const context = typeof docs.discoveryContext === 'string' ? JSON.parse(docs.discoveryContext) : docs.discoveryContext
|
||||
const dimensions = context.seed_analysis?.exploration_dimensions || []
|
||||
const constraints = context.seed_analysis?.constraints || []
|
||||
const userSupplements = context.seed_analysis?.user_supplements || ''
|
||||
const allRequirements = [...dimensions, ...constraints]
|
||||
if (userSupplements) allRequirements.push(userSupplements)
|
||||
|
||||
if (allRequirements.length === 0) {
|
||||
return { score: 100, issues: [] } // No requirements to check
|
||||
}
|
||||
|
||||
const allDocContent = [docs.productBrief, docs.requirementsIndex, docs.architectureIndex, docs.epicsIndex,
|
||||
...docs.requirements, ...docs.adrs, ...docs.epics].filter(Boolean).join('\n').toLowerCase()
|
||||
|
||||
let covered = 0
|
||||
for (const req of allRequirements) {
|
||||
const keywords = req.toLowerCase().split(/[\s,;]+/).filter(w => w.length > 2)
|
||||
const isCovered = keywords.some(kw => allDocContent.includes(kw))
|
||||
if (isCovered) { covered++ }
|
||||
else { issues.push(`Requirement not covered in documents: "${req}"`) }
|
||||
}
|
||||
|
||||
score = Math.round((covered / allRequirements.length) * 100)
|
||||
return { score, issues }
|
||||
}
|
||||
|
||||
const completenessResult = scoreCompleteness(documents)
|
||||
const consistencyResult = scoreConsistency(documents)
|
||||
const traceabilityResult = scoreTraceability(documents)
|
||||
const depthResult = scoreDepth(documents)
|
||||
const coverageResult = scoreRequirementCoverage(documents)
|
||||
|
||||
scores.completeness = completenessResult.score
|
||||
scores.consistency = consistencyResult.score
|
||||
scores.traceability = traceabilityResult.score
|
||||
scores.depth = depthResult.score
|
||||
scores.requirementCoverage = coverageResult.score
|
||||
|
||||
const overallScore = (scores.completeness + scores.consistency + scores.traceability + scores.depth) / 4
|
||||
const qualityGate = overallScore >= 80 ? 'PASS' : overallScore >= 60 ? 'REVIEW' : 'FAIL'
|
||||
const allSpecIssues = [...completenessResult.issues, ...consistencyResult.issues, ...traceabilityResult.issues, ...depthResult.issues]
|
||||
const overallScore = (scores.completeness + scores.consistency + scores.traceability + scores.depth + scores.requirementCoverage) / 5
|
||||
const qualityGate = (overallScore >= 80 && scores.requirementCoverage >= 70) ? 'PASS' :
|
||||
(overallScore < 60 || scores.requirementCoverage < 50) ? 'FAIL' : 'REVIEW'
|
||||
const allSpecIssues = [...completenessResult.issues, ...consistencyResult.issues, ...traceabilityResult.issues, ...depthResult.issues, ...coverageResult.issues]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -346,10 +382,11 @@ version: 1
|
||||
## Quality Scores
|
||||
| Dimension | Score | Weight |
|
||||
|-----------|-------|--------|
|
||||
| Completeness | ${scores.completeness}% | 25% |
|
||||
| Consistency | ${scores.consistency}% | 25% |
|
||||
| Traceability | ${scores.traceability}% | 25% |
|
||||
| Depth | ${scores.depth}% | 25% |
|
||||
| Completeness | ${scores.completeness}% | 20% |
|
||||
| Consistency | ${scores.consistency}% | 20% |
|
||||
| Traceability | ${scores.traceability}% | 20% |
|
||||
| Depth | ${scores.depth}% | 20% |
|
||||
| Requirement Coverage | ${scores.requirementCoverage}% | 20% |
|
||||
| **Overall** | **${overallScore.toFixed(1)}%** | **100%** |
|
||||
|
||||
## Quality Gate: ${qualityGate}
|
||||
@@ -448,7 +485,7 @@ if (reviewMode === 'spec') {
|
||||
operation: "log", team: teamName,
|
||||
from: "reviewer", to: "coordinator",
|
||||
type: qualityGate === 'FAIL' ? "fix_required" : "quality_result",
|
||||
summary: `质量检查 ${qualityGate}: ${overallScore.toFixed(1)}分 (完整性${scores.completeness}/一致性${scores.consistency}/追溯${scores.traceability}/深度${scores.depth})`,
|
||||
summary: `质量检查 ${qualityGate}: ${overallScore.toFixed(1)}分 (完整性${scores.completeness}/一致性${scores.consistency}/追溯${scores.traceability}/深度${scores.depth}/覆盖率${scores.requirementCoverage})`,
|
||||
data: { gate: qualityGate, score: overallScore, issues: allSpecIssues }
|
||||
})
|
||||
|
||||
@@ -468,6 +505,7 @@ if (reviewMode === 'spec') {
|
||||
| 一致性 | ${scores.consistency}% |
|
||||
| 可追溯性 | ${scores.traceability}% |
|
||||
| 深度 | ${scores.depth}% |
|
||||
| 需求覆盖率 | ${scores.requirementCoverage}% |
|
||||
|
||||
### 问题列表 (${allSpecIssues.length})
|
||||
${allSpecIssues.map(i => '- ' + i).join('\n') || '无问题'}
|
||||
|
||||
Reference in New Issue
Block a user