shotgun-sh 0.1.0.dev22__py3-none-any.whl → 0.1.0.dev24__py3-none-any.whl
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.
Potentially problematic release.
This version of shotgun-sh might be problematic. Click here for more details.
- shotgun/agents/agent_manager.py +95 -15
- shotgun/agents/common.py +143 -25
- shotgun/agents/conversation_history.py +56 -0
- shotgun/agents/conversation_manager.py +105 -0
- shotgun/agents/export.py +5 -2
- shotgun/agents/models.py +16 -7
- shotgun/agents/plan.py +2 -1
- shotgun/agents/research.py +2 -1
- shotgun/agents/specify.py +2 -1
- shotgun/agents/tasks.py +5 -2
- shotgun/agents/tools/file_management.py +67 -2
- shotgun/codebase/core/ingestor.py +1 -1
- shotgun/codebase/core/manager.py +106 -4
- shotgun/codebase/models.py +4 -0
- shotgun/codebase/service.py +60 -2
- shotgun/main.py +9 -1
- shotgun/prompts/agents/export.j2 +14 -11
- shotgun/prompts/agents/partials/common_agent_system_prompt.j2 +6 -9
- shotgun/prompts/agents/plan.j2 +9 -13
- shotgun/prompts/agents/research.j2 +11 -14
- shotgun/prompts/agents/specify.j2 +9 -12
- shotgun/prompts/agents/state/system_state.j2 +27 -5
- shotgun/prompts/agents/tasks.j2 +12 -12
- shotgun/sdk/codebase.py +26 -2
- shotgun/sdk/services.py +0 -14
- shotgun/tui/app.py +9 -4
- shotgun/tui/screens/chat.py +80 -19
- shotgun/tui/screens/chat_screen/command_providers.py +1 -1
- shotgun/tui/screens/chat_screen/history.py +6 -0
- shotgun/tui/utils/mode_progress.py +111 -78
- {shotgun_sh-0.1.0.dev22.dist-info → shotgun_sh-0.1.0.dev24.dist-info}/METADATA +8 -9
- {shotgun_sh-0.1.0.dev22.dist-info → shotgun_sh-0.1.0.dev24.dist-info}/RECORD +35 -54
- shotgun/agents/artifact_state.py +0 -58
- shotgun/agents/tools/artifact_management.py +0 -481
- shotgun/artifacts/__init__.py +0 -17
- shotgun/artifacts/exceptions.py +0 -89
- shotgun/artifacts/manager.py +0 -530
- shotgun/artifacts/models.py +0 -334
- shotgun/artifacts/service.py +0 -463
- shotgun/artifacts/templates/__init__.py +0 -10
- shotgun/artifacts/templates/loader.py +0 -252
- shotgun/artifacts/templates/models.py +0 -136
- shotgun/artifacts/templates/plan/delivery_and_release_plan.yaml +0 -66
- shotgun/artifacts/templates/research/market_research.yaml +0 -585
- shotgun/artifacts/templates/research/sdk_comparison.yaml +0 -257
- shotgun/artifacts/templates/specify/prd.yaml +0 -331
- shotgun/artifacts/templates/specify/product_spec.yaml +0 -301
- shotgun/artifacts/utils.py +0 -76
- shotgun/prompts/agents/partials/artifact_system.j2 +0 -32
- shotgun/prompts/agents/state/artifact_templates_available.j2 +0 -20
- shotgun/prompts/agents/state/existing_artifacts_available.j2 +0 -25
- shotgun/sdk/artifact_models.py +0 -186
- shotgun/sdk/artifacts.py +0 -448
- {shotgun_sh-0.1.0.dev22.dist-info → shotgun_sh-0.1.0.dev24.dist-info}/WHEEL +0 -0
- {shotgun_sh-0.1.0.dev22.dist-info → shotgun_sh-0.1.0.dev24.dist-info}/entry_points.txt +0 -0
- {shotgun_sh-0.1.0.dev22.dist-info → shotgun_sh-0.1.0.dev24.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,252 +0,0 @@
|
|
|
1
|
-
"""Template loader for artifact templates."""
|
|
2
|
-
|
|
3
|
-
from pathlib import Path
|
|
4
|
-
|
|
5
|
-
import yaml
|
|
6
|
-
|
|
7
|
-
from shotgun.artifacts.models import AgentMode
|
|
8
|
-
from shotgun.logging_config import setup_logger
|
|
9
|
-
from shotgun.utils.file_system_utils import get_shotgun_home
|
|
10
|
-
|
|
11
|
-
from .models import ArtifactTemplate, TemplateSummary
|
|
12
|
-
|
|
13
|
-
logger = setup_logger(__name__)
|
|
14
|
-
|
|
15
|
-
TEMPLATES_DIR = Path(__file__).parent
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class ArtifactTemplateLoader:
|
|
19
|
-
"""Loads and manages artifact templates from the filesystem."""
|
|
20
|
-
|
|
21
|
-
def __init__(
|
|
22
|
-
self, templates_dir: Path | None = None, include_user_templates: bool = True
|
|
23
|
-
):
|
|
24
|
-
"""Initialize the template loader.
|
|
25
|
-
|
|
26
|
-
Args:
|
|
27
|
-
templates_dir: Directory containing template files. Defaults to package templates.
|
|
28
|
-
include_user_templates: Whether to include user templates from ~/.shotgun-sh/templates/
|
|
29
|
-
"""
|
|
30
|
-
self.templates_dir = templates_dir or TEMPLATES_DIR
|
|
31
|
-
self.include_user_templates = include_user_templates
|
|
32
|
-
self.user_templates_dir = (
|
|
33
|
-
get_shotgun_home() / "templates" if include_user_templates else None
|
|
34
|
-
)
|
|
35
|
-
|
|
36
|
-
def _discover_templates(self) -> dict[str, tuple[Path, AgentMode]]:
|
|
37
|
-
"""Discover all template files from both built-in and user directories."""
|
|
38
|
-
# Start with built-in templates
|
|
39
|
-
templates = self._discover_templates_from_dir(self.templates_dir, "built-in")
|
|
40
|
-
|
|
41
|
-
# Add user templates (they can override built-in ones)
|
|
42
|
-
if (
|
|
43
|
-
self.include_user_templates
|
|
44
|
-
and self.user_templates_dir
|
|
45
|
-
and self.user_templates_dir.exists()
|
|
46
|
-
):
|
|
47
|
-
user_templates = self._discover_templates_from_dir(
|
|
48
|
-
self.user_templates_dir, "user"
|
|
49
|
-
)
|
|
50
|
-
templates.update(
|
|
51
|
-
user_templates
|
|
52
|
-
) # User templates override built-in ones with same ID
|
|
53
|
-
|
|
54
|
-
return templates
|
|
55
|
-
|
|
56
|
-
def _discover_templates_from_dir(
|
|
57
|
-
self, templates_dir: Path, source_type: str
|
|
58
|
-
) -> dict[str, tuple[Path, AgentMode]]:
|
|
59
|
-
"""Discover template files from a specific directory.
|
|
60
|
-
|
|
61
|
-
Args:
|
|
62
|
-
templates_dir: Directory to search for templates
|
|
63
|
-
source_type: Type of templates ("built-in" or "user") for logging
|
|
64
|
-
|
|
65
|
-
Returns:
|
|
66
|
-
Dictionary mapping template IDs to (path, agent_mode) tuples
|
|
67
|
-
"""
|
|
68
|
-
templates: dict[str, tuple[Path, AgentMode]] = {}
|
|
69
|
-
|
|
70
|
-
if not templates_dir.exists():
|
|
71
|
-
if source_type == "built-in":
|
|
72
|
-
logger.warning("Templates directory does not exist: %s", templates_dir)
|
|
73
|
-
else:
|
|
74
|
-
logger.debug(
|
|
75
|
-
"User templates directory does not exist: %s", templates_dir
|
|
76
|
-
)
|
|
77
|
-
return templates
|
|
78
|
-
|
|
79
|
-
logger.debug("Discovering %s templates from: %s", source_type, templates_dir)
|
|
80
|
-
|
|
81
|
-
for mode_dir in templates_dir.iterdir():
|
|
82
|
-
if not mode_dir.is_dir() or mode_dir.name.startswith("."):
|
|
83
|
-
continue
|
|
84
|
-
|
|
85
|
-
# Skip common non-agent directories without warnings
|
|
86
|
-
if mode_dir.name in ("__pycache__", "node_modules", ".git", ".DS_Store"):
|
|
87
|
-
continue
|
|
88
|
-
|
|
89
|
-
try:
|
|
90
|
-
agent_mode = AgentMode(mode_dir.name)
|
|
91
|
-
except ValueError:
|
|
92
|
-
logger.warning(
|
|
93
|
-
"Unknown agent mode directory in %s templates: %s",
|
|
94
|
-
source_type,
|
|
95
|
-
mode_dir.name,
|
|
96
|
-
)
|
|
97
|
-
continue
|
|
98
|
-
|
|
99
|
-
# Find all YAML files in this mode directory
|
|
100
|
-
for template_file in mode_dir.glob("*.yaml"):
|
|
101
|
-
template_id = template_file.stem
|
|
102
|
-
full_template_id = f"{agent_mode.value}/{template_id}"
|
|
103
|
-
templates[full_template_id] = (template_file, agent_mode)
|
|
104
|
-
logger.debug("Found %s template: %s", source_type, full_template_id)
|
|
105
|
-
|
|
106
|
-
return templates
|
|
107
|
-
|
|
108
|
-
def _load_template_file(
|
|
109
|
-
self, template_path: Path, agent_mode: AgentMode, template_id: str
|
|
110
|
-
) -> ArtifactTemplate | None:
|
|
111
|
-
"""Load a single template file."""
|
|
112
|
-
try:
|
|
113
|
-
with open(template_path, encoding="utf-8") as f:
|
|
114
|
-
data = yaml.safe_load(f)
|
|
115
|
-
|
|
116
|
-
# Validate required fields
|
|
117
|
-
required_fields = ["name", "purpose", "prompt"]
|
|
118
|
-
for field in required_fields:
|
|
119
|
-
if field not in data:
|
|
120
|
-
logger.error(
|
|
121
|
-
"Template %s missing required field '%s'", template_path, field
|
|
122
|
-
)
|
|
123
|
-
return None
|
|
124
|
-
|
|
125
|
-
template = ArtifactTemplate.from_yaml_dict(data, agent_mode, template_id)
|
|
126
|
-
|
|
127
|
-
# Validate dependencies
|
|
128
|
-
dependency_errors = template.validate_dependencies()
|
|
129
|
-
if dependency_errors:
|
|
130
|
-
logger.error(
|
|
131
|
-
"Template %s has dependency errors: %s",
|
|
132
|
-
template_path,
|
|
133
|
-
dependency_errors,
|
|
134
|
-
)
|
|
135
|
-
return None
|
|
136
|
-
|
|
137
|
-
return template
|
|
138
|
-
|
|
139
|
-
except yaml.YAMLError as e:
|
|
140
|
-
logger.error("Failed to parse template YAML %s: %s", template_path, e)
|
|
141
|
-
return None
|
|
142
|
-
except Exception as e:
|
|
143
|
-
logger.error("Failed to load template %s: %s", template_path, e)
|
|
144
|
-
return None
|
|
145
|
-
|
|
146
|
-
def get_template(self, template_id: str) -> ArtifactTemplate | None:
|
|
147
|
-
"""Get a template by ID.
|
|
148
|
-
|
|
149
|
-
Args:
|
|
150
|
-
template_id: Full template ID (e.g., 'research/market_research')
|
|
151
|
-
|
|
152
|
-
Returns:
|
|
153
|
-
Template instance or None if not found
|
|
154
|
-
"""
|
|
155
|
-
discovered = self._discover_templates()
|
|
156
|
-
if template_id not in discovered:
|
|
157
|
-
return None
|
|
158
|
-
|
|
159
|
-
template_path, agent_mode = discovered[template_id]
|
|
160
|
-
return self._load_template_file(template_path, agent_mode, template_id)
|
|
161
|
-
|
|
162
|
-
def list_templates(
|
|
163
|
-
self, agent_mode: AgentMode | None = None
|
|
164
|
-
) -> list[TemplateSummary]:
|
|
165
|
-
"""List all available templates, optionally filtered by agent mode.
|
|
166
|
-
|
|
167
|
-
Args:
|
|
168
|
-
agent_mode: Optional agent mode filter
|
|
169
|
-
|
|
170
|
-
Returns:
|
|
171
|
-
List of template summaries
|
|
172
|
-
"""
|
|
173
|
-
discovered = self._discover_templates()
|
|
174
|
-
summaries = []
|
|
175
|
-
|
|
176
|
-
for full_template_id, (
|
|
177
|
-
template_path,
|
|
178
|
-
template_agent_mode,
|
|
179
|
-
) in discovered.items():
|
|
180
|
-
if agent_mode and template_agent_mode != agent_mode:
|
|
181
|
-
continue
|
|
182
|
-
|
|
183
|
-
template = self._load_template_file(
|
|
184
|
-
template_path, template_agent_mode, full_template_id
|
|
185
|
-
)
|
|
186
|
-
if template:
|
|
187
|
-
summaries.append(
|
|
188
|
-
TemplateSummary(
|
|
189
|
-
template_id=full_template_id,
|
|
190
|
-
name=template.name,
|
|
191
|
-
purpose=template.purpose,
|
|
192
|
-
agent_mode=template.agent_mode,
|
|
193
|
-
section_count=len(template.sections),
|
|
194
|
-
)
|
|
195
|
-
)
|
|
196
|
-
|
|
197
|
-
# Sort by agent mode, then by template name
|
|
198
|
-
summaries.sort(key=lambda s: (s.agent_mode.value, s.name))
|
|
199
|
-
return summaries
|
|
200
|
-
|
|
201
|
-
def get_templates_for_mode(self, agent_mode: AgentMode) -> list[ArtifactTemplate]:
|
|
202
|
-
"""Get all templates for a specific agent mode.
|
|
203
|
-
|
|
204
|
-
Args:
|
|
205
|
-
agent_mode: Agent mode to filter by
|
|
206
|
-
|
|
207
|
-
Returns:
|
|
208
|
-
List of templates for the specified mode
|
|
209
|
-
"""
|
|
210
|
-
discovered = self._discover_templates()
|
|
211
|
-
templates = []
|
|
212
|
-
|
|
213
|
-
for full_template_id, (
|
|
214
|
-
template_path,
|
|
215
|
-
template_agent_mode,
|
|
216
|
-
) in discovered.items():
|
|
217
|
-
if template_agent_mode == agent_mode:
|
|
218
|
-
template = self._load_template_file(
|
|
219
|
-
template_path, template_agent_mode, full_template_id
|
|
220
|
-
)
|
|
221
|
-
if template:
|
|
222
|
-
templates.append(template)
|
|
223
|
-
|
|
224
|
-
templates.sort(key=lambda t: t.name)
|
|
225
|
-
return templates
|
|
226
|
-
|
|
227
|
-
def template_exists(self, template_id: str) -> bool:
|
|
228
|
-
"""Check if a template exists.
|
|
229
|
-
|
|
230
|
-
Args:
|
|
231
|
-
template_id: Template ID to check
|
|
232
|
-
|
|
233
|
-
Returns:
|
|
234
|
-
True if template exists, False otherwise
|
|
235
|
-
"""
|
|
236
|
-
discovered = self._discover_templates()
|
|
237
|
-
return template_id in discovered
|
|
238
|
-
|
|
239
|
-
def get_template_by_short_id(
|
|
240
|
-
self, short_id: str, agent_mode: AgentMode
|
|
241
|
-
) -> ArtifactTemplate | None:
|
|
242
|
-
"""Get template by short ID (without agent mode prefix).
|
|
243
|
-
|
|
244
|
-
Args:
|
|
245
|
-
short_id: Short template ID (e.g., 'market_research')
|
|
246
|
-
agent_mode: Agent mode to search within
|
|
247
|
-
|
|
248
|
-
Returns:
|
|
249
|
-
Template instance or None if not found
|
|
250
|
-
"""
|
|
251
|
-
full_id = f"{agent_mode.value}/{short_id}"
|
|
252
|
-
return self.get_template(full_id)
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
"""Models for artifact templates."""
|
|
2
|
-
|
|
3
|
-
from typing import Any
|
|
4
|
-
|
|
5
|
-
from pydantic import BaseModel, Field
|
|
6
|
-
|
|
7
|
-
from shotgun.artifacts.models import AgentMode
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class TemplateSection(BaseModel):
|
|
11
|
-
"""Represents a section in an artifact template."""
|
|
12
|
-
|
|
13
|
-
instructions: str = Field(..., description="Detailed instructions for this section")
|
|
14
|
-
depends_on: list[str] = Field(
|
|
15
|
-
default_factory=list, description="List of section keys this section depends on"
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
@property
|
|
19
|
-
def formatted_instructions(self) -> str:
|
|
20
|
-
"""Return formatted instructions for display."""
|
|
21
|
-
return self.instructions.strip()
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
class ArtifactTemplate(BaseModel):
|
|
25
|
-
"""Represents an artifact template with structured guidance."""
|
|
26
|
-
|
|
27
|
-
name: str = Field(..., description="Display name of the template")
|
|
28
|
-
purpose: str = Field(..., description="Description of what this template is for")
|
|
29
|
-
prompt: str = Field(..., description="System prompt for agents using this template")
|
|
30
|
-
sections: dict[str, TemplateSection] = Field(
|
|
31
|
-
default_factory=dict, description="Template sections with instructions"
|
|
32
|
-
)
|
|
33
|
-
agent_mode: AgentMode = Field(
|
|
34
|
-
..., description="Agent mode this template belongs to"
|
|
35
|
-
)
|
|
36
|
-
template_id: str = Field(..., description="Unique identifier for the template")
|
|
37
|
-
|
|
38
|
-
def get_section(self, section_key: str) -> TemplateSection | None:
|
|
39
|
-
"""Get a template section by key."""
|
|
40
|
-
return self.sections.get(section_key)
|
|
41
|
-
|
|
42
|
-
def get_ordered_sections(self) -> list[tuple[str, TemplateSection]]:
|
|
43
|
-
"""Get sections in dependency order."""
|
|
44
|
-
# Simple topological sort for section dependencies
|
|
45
|
-
ordered = []
|
|
46
|
-
processed = set()
|
|
47
|
-
|
|
48
|
-
def process_section(key: str, section: TemplateSection) -> None:
|
|
49
|
-
if key in processed:
|
|
50
|
-
return
|
|
51
|
-
|
|
52
|
-
# Process dependencies first
|
|
53
|
-
for dep_key in section.depends_on:
|
|
54
|
-
if dep_key in self.sections and dep_key not in processed:
|
|
55
|
-
process_section(dep_key, self.sections[dep_key])
|
|
56
|
-
|
|
57
|
-
ordered.append((key, section))
|
|
58
|
-
processed.add(key)
|
|
59
|
-
|
|
60
|
-
for key, section in self.sections.items():
|
|
61
|
-
process_section(key, section)
|
|
62
|
-
|
|
63
|
-
return ordered
|
|
64
|
-
|
|
65
|
-
def get_section_keys(self) -> list[str]:
|
|
66
|
-
"""Get all section keys."""
|
|
67
|
-
return list(self.sections.keys())
|
|
68
|
-
|
|
69
|
-
def validate_dependencies(self) -> list[str]:
|
|
70
|
-
"""Validate that all section dependencies exist. Returns list of errors."""
|
|
71
|
-
errors = []
|
|
72
|
-
|
|
73
|
-
for section_key, section in self.sections.items():
|
|
74
|
-
for dep_key in section.depends_on:
|
|
75
|
-
if dep_key not in self.sections:
|
|
76
|
-
errors.append(
|
|
77
|
-
f"Section '{section_key}' depends on non-existent section '{dep_key}'"
|
|
78
|
-
)
|
|
79
|
-
|
|
80
|
-
return errors
|
|
81
|
-
|
|
82
|
-
def to_yaml_dict(self) -> dict[str, Any]:
|
|
83
|
-
"""Convert to dictionary suitable for YAML serialization."""
|
|
84
|
-
return {
|
|
85
|
-
"name": self.name,
|
|
86
|
-
"purpose": self.purpose,
|
|
87
|
-
"prompt": self.prompt,
|
|
88
|
-
"template_id": self.template_id,
|
|
89
|
-
"sections": {
|
|
90
|
-
key: {
|
|
91
|
-
"instructions": section.instructions,
|
|
92
|
-
**(
|
|
93
|
-
{"depends_on": section.depends_on} if section.depends_on else {}
|
|
94
|
-
),
|
|
95
|
-
}
|
|
96
|
-
for key, section in self.sections.items()
|
|
97
|
-
},
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
@classmethod
|
|
101
|
-
def from_yaml_dict(
|
|
102
|
-
cls, data: dict[str, Any], agent_mode: AgentMode, template_id: str
|
|
103
|
-
) -> "ArtifactTemplate":
|
|
104
|
-
"""Create template from YAML dictionary."""
|
|
105
|
-
sections = {}
|
|
106
|
-
|
|
107
|
-
for section_key, section_data in data.get("sections", {}).items():
|
|
108
|
-
sections[section_key] = TemplateSection(
|
|
109
|
-
instructions=section_data.get("instructions", ""),
|
|
110
|
-
depends_on=section_data.get("depends_on", []),
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
return cls(
|
|
114
|
-
name=data["name"],
|
|
115
|
-
purpose=data["purpose"],
|
|
116
|
-
prompt=data["prompt"],
|
|
117
|
-
sections=sections,
|
|
118
|
-
agent_mode=agent_mode,
|
|
119
|
-
template_id=template_id,
|
|
120
|
-
)
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
class TemplateSummary(BaseModel):
|
|
124
|
-
"""Summary information about a template for listing purposes."""
|
|
125
|
-
|
|
126
|
-
template_id: str = Field(..., description="Unique identifier for the template")
|
|
127
|
-
name: str = Field(..., description="Display name of the template")
|
|
128
|
-
purpose: str = Field(..., description="Description of what this template is for")
|
|
129
|
-
agent_mode: AgentMode = Field(
|
|
130
|
-
..., description="Agent mode this template belongs to"
|
|
131
|
-
)
|
|
132
|
-
section_count: int = Field(..., description="Number of sections in the template")
|
|
133
|
-
|
|
134
|
-
def __str__(self) -> str:
|
|
135
|
-
"""String representation for CLI display."""
|
|
136
|
-
return f"{self.template_id}: {self.purpose}"
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
name: Delivery & Release Plan
|
|
2
|
-
purpose: |
|
|
3
|
-
Convert scope and roadmap into a staffed, scheduled, dependency-aware execution plan with
|
|
4
|
-
clear ownership, milestones, risk management, and cutover/rollback strategies.
|
|
5
|
-
prompt: |
|
|
6
|
-
Use this template to build a delivery plan we have the specification ready.
|
|
7
|
-
The output should enable both engineering and product stakeholders to track progress,
|
|
8
|
-
manage dependencies, and prepare for a safe release.
|
|
9
|
-
sections:
|
|
10
|
-
delivery.workstreams:
|
|
11
|
-
instructions: |
|
|
12
|
-
Define parallel streams of work (e.g., Backend, Frontend, Data, DevOps, QA, Security).
|
|
13
|
-
For each: owner (DRI), objectives, deliverables, and relation to PRD scope.
|
|
14
|
-
wbs.estimates:
|
|
15
|
-
instructions: |
|
|
16
|
-
Create a Work Breakdown Structure. Break epics → features → stories → tasks.
|
|
17
|
-
Provide estimates (T-shirt sizes + P50/P90 time in days). Highlight critical path items.
|
|
18
|
-
Include buffers for integration and testing.
|
|
19
|
-
capacity_and_staffing:
|
|
20
|
-
instructions: |
|
|
21
|
-
List team members, roles, and availability (holidays, partial allocations).
|
|
22
|
-
Define velocity assumptions and utilization factors.
|
|
23
|
-
Highlight gaps and hiring needs if capacity < required.
|
|
24
|
-
dependency_map:
|
|
25
|
-
instructions: |
|
|
26
|
-
Capture internal and external dependencies (libraries, vendors, APIs).
|
|
27
|
-
Define lead times and SLAs. Show dependency graph (Mermaid or table).
|
|
28
|
-
Suggest decoupling via feature flags, mocks, or phased integration.
|
|
29
|
-
schedule.plan:
|
|
30
|
-
instructions: |
|
|
31
|
-
Translate WBS + capacity into a timeline. Provide both:
|
|
32
|
-
- High-level milestones (alpha, beta, GA)
|
|
33
|
-
- Detailed Gantt/roadmap (Mermaid or markdown tables)
|
|
34
|
-
Show critical path and dependency-driven slips.
|
|
35
|
-
raci_and_ceremonies:
|
|
36
|
-
instructions: |
|
|
37
|
-
Define RACI (Responsible, Accountable, Consulted, Informed) for decisions.
|
|
38
|
-
Define governance cadences: daily standups, weekly demos, fortnightly checkpoints.
|
|
39
|
-
Specify escalation path for blockers.
|
|
40
|
-
quality_and_testing:
|
|
41
|
-
instructions: |
|
|
42
|
-
Define QA strategy: test types (unit, integration, regression, perf).
|
|
43
|
-
Define gates: test coverage %, perf benchmarks, security scans.
|
|
44
|
-
Specify environments: staging, pre-prod, canary.
|
|
45
|
-
cutover_and_rollback:
|
|
46
|
-
instructions: |
|
|
47
|
-
Provide release checklist: build, config, migrations, monitoring, comms.
|
|
48
|
-
Define dark launch/canary steps, health checks, rollback triggers, and procedures.
|
|
49
|
-
Specify post-release monitoring window and owner on-call.
|
|
50
|
-
release_comms:
|
|
51
|
-
instructions: |
|
|
52
|
-
Draft internal and external comms: release notes, stakeholder briefings, customer updates.
|
|
53
|
-
Specify timing relative to rollout phases.
|
|
54
|
-
delivery_risks:
|
|
55
|
-
instructions: |
|
|
56
|
-
Maintain a risk register: description, prob×impact, owner, mitigation, contingency.
|
|
57
|
-
Track status weekly. Mark high-priority risks explicitly.
|
|
58
|
-
metrics_and_tracking:
|
|
59
|
-
instructions: |
|
|
60
|
-
Link to PRD success metrics and Experiment Plan metrics.
|
|
61
|
-
Define delivery metrics: burn-down, velocity, on-time %.
|
|
62
|
-
Provide dashboards or reporting cadence.
|
|
63
|
-
status_reporting:
|
|
64
|
-
instructions: |
|
|
65
|
-
Define status update format (traffic-light summary, burndown chart, risk log).
|
|
66
|
-
Specify frequency (weekly) and audience (exec, product, eng).
|