specweave 1.0.488 → 1.0.489
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/README.md +1 -1
- package/dist/plugins/specweave-ado/lib/ado-ac-checkbox-sync.d.ts +13 -5
- package/dist/plugins/specweave-ado/lib/ado-ac-checkbox-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-ado/lib/ado-ac-checkbox-sync.js +28 -26
- package/dist/plugins/specweave-ado/lib/ado-ac-checkbox-sync.js.map +1 -1
- package/dist/plugins/specweave-ado/lib/ado-client.d.ts +10 -0
- package/dist/plugins/specweave-ado/lib/ado-client.d.ts.map +1 -1
- package/dist/plugins/specweave-ado/lib/ado-client.js +14 -0
- package/dist/plugins/specweave-ado/lib/ado-client.js.map +1 -1
- package/dist/plugins/specweave-ado/lib/ado-pull-sync.d.ts +35 -0
- package/dist/plugins/specweave-ado/lib/ado-pull-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-ado/lib/ado-pull-sync.js +53 -0
- package/dist/plugins/specweave-ado/lib/ado-pull-sync.js.map +1 -0
- package/dist/plugins/specweave-ado/lib/ado-rate-limiter.d.ts +46 -0
- package/dist/plugins/specweave-ado/lib/ado-rate-limiter.d.ts.map +1 -0
- package/dist/plugins/specweave-ado/lib/ado-rate-limiter.js +65 -0
- package/dist/plugins/specweave-ado/lib/ado-rate-limiter.js.map +1 -0
- package/dist/plugins/specweave-ado/lib/ado-spec-sync.d.ts +7 -1
- package/dist/plugins/specweave-ado/lib/ado-spec-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-ado/lib/ado-spec-sync.js +25 -1
- package/dist/plugins/specweave-ado/lib/ado-spec-sync.js.map +1 -1
- package/dist/plugins/specweave-ado/lib/ado-status-sync.d.ts +17 -1
- package/dist/plugins/specweave-ado/lib/ado-status-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-ado/lib/ado-status-sync.js +51 -9
- package/dist/plugins/specweave-ado/lib/ado-status-sync.js.map +1 -1
- package/dist/plugins/specweave-github/lib/github-client-v2.js +1 -1
- package/dist/plugins/specweave-github/lib/github-client-v2.js.map +1 -1
- package/dist/plugins/specweave-github/lib/github-push-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-push-sync.js +15 -3
- package/dist/plugins/specweave-github/lib/github-push-sync.js.map +1 -1
- package/dist/plugins/specweave-jira/lib/jira-spec-sync.d.ts +31 -1
- package/dist/plugins/specweave-jira/lib/jira-spec-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-jira/lib/jira-spec-sync.js +170 -97
- package/dist/plugins/specweave-jira/lib/jira-spec-sync.js.map +1 -1
- package/dist/plugins/specweave-jira/lib/jira-status-sync.d.ts +36 -1
- package/dist/plugins/specweave-jira/lib/jira-status-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-jira/lib/jira-status-sync.js +185 -82
- package/dist/plugins/specweave-jira/lib/jira-status-sync.js.map +1 -1
- package/dist/src/adapters/adapter-loader.d.ts.map +1 -1
- package/dist/src/adapters/adapter-loader.js +8 -2
- package/dist/src/adapters/adapter-loader.js.map +1 -1
- package/dist/src/adapters/codex/adapter.d.ts.map +1 -1
- package/dist/src/adapters/codex/adapter.js +1 -0
- package/dist/src/adapters/codex/adapter.js.map +1 -1
- package/dist/src/adapters/cursor/adapter.d.ts.map +1 -1
- package/dist/src/adapters/cursor/adapter.js +1 -0
- package/dist/src/adapters/cursor/adapter.js.map +1 -1
- package/dist/src/adapters/generic/adapter.d.ts +6 -3
- package/dist/src/adapters/generic/adapter.d.ts.map +1 -1
- package/dist/src/adapters/generic/adapter.js +53 -47
- package/dist/src/adapters/generic/adapter.js.map +1 -1
- package/dist/src/adapters/kimi/adapter.d.ts +21 -0
- package/dist/src/adapters/kimi/adapter.d.ts.map +1 -0
- package/dist/src/adapters/kimi/adapter.js +57 -0
- package/dist/src/adapters/kimi/adapter.js.map +1 -0
- package/dist/src/adapters/opencode/adapter.d.ts +24 -0
- package/dist/src/adapters/opencode/adapter.d.ts.map +1 -0
- package/dist/src/adapters/opencode/adapter.js +71 -0
- package/dist/src/adapters/opencode/adapter.js.map +1 -0
- package/dist/src/adapters/registry.yaml +59 -0
- package/dist/src/adapters/trae/adapter.d.ts +21 -0
- package/dist/src/adapters/trae/adapter.d.ts.map +1 -0
- package/dist/src/adapters/trae/adapter.js +64 -0
- package/dist/src/adapters/trae/adapter.js.map +1 -0
- package/dist/src/cli/commands/init.d.ts.map +1 -1
- package/dist/src/cli/commands/init.js +156 -5
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/commands/update-instructions.d.ts.map +1 -1
- package/dist/src/cli/commands/update-instructions.js +10 -0
- package/dist/src/cli/commands/update-instructions.js.map +1 -1
- package/dist/src/cli/helpers/init/index.d.ts +1 -0
- package/dist/src/cli/helpers/init/index.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/index.js +2 -0
- package/dist/src/cli/helpers/init/index.js.map +1 -1
- package/dist/src/cli/helpers/init/next-steps.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/next-steps.js +52 -0
- package/dist/src/cli/helpers/init/next-steps.js.map +1 -1
- package/dist/src/cli/helpers/init/skill-creator-installer.d.ts +24 -0
- package/dist/src/cli/helpers/init/skill-creator-installer.d.ts.map +1 -0
- package/dist/src/cli/helpers/init/skill-creator-installer.js +54 -0
- package/dist/src/cli/helpers/init/skill-creator-installer.js.map +1 -0
- package/dist/src/core/ado-description-updater.d.ts +22 -0
- package/dist/src/core/ado-description-updater.d.ts.map +1 -0
- package/dist/src/core/ado-description-updater.js +46 -0
- package/dist/src/core/ado-description-updater.js.map +1 -0
- package/dist/src/core/closure-dispatcher.d.ts +96 -0
- package/dist/src/core/closure-dispatcher.d.ts.map +1 -0
- package/dist/src/core/closure-dispatcher.js +116 -0
- package/dist/src/core/closure-dispatcher.js.map +1 -0
- package/dist/src/core/config/types.d.ts +2 -0
- package/dist/src/core/config/types.d.ts.map +1 -1
- package/dist/src/core/config/types.js.map +1 -1
- package/dist/src/core/errors/sync-error.d.ts +12 -0
- package/dist/src/core/errors/sync-error.d.ts.map +1 -0
- package/dist/src/core/errors/sync-error.js +19 -0
- package/dist/src/core/errors/sync-error.js.map +1 -0
- package/dist/src/core/skill-gen/rule-collector.d.ts +28 -0
- package/dist/src/core/skill-gen/rule-collector.d.ts.map +1 -0
- package/dist/src/core/skill-gen/rule-collector.js +112 -0
- package/dist/src/core/skill-gen/rule-collector.js.map +1 -0
- package/dist/src/core/skill-gen/signal-collector.d.ts +2 -1
- package/dist/src/core/skill-gen/signal-collector.d.ts.map +1 -1
- package/dist/src/core/skill-gen/signal-collector.js +21 -5
- package/dist/src/core/skill-gen/signal-collector.js.map +1 -1
- package/dist/src/core/sync/persistent-circuit-breaker.d.ts +22 -0
- package/dist/src/core/sync/persistent-circuit-breaker.d.ts.map +1 -0
- package/dist/src/core/sync/persistent-circuit-breaker.js +65 -0
- package/dist/src/core/sync/persistent-circuit-breaker.js.map +1 -0
- package/dist/src/core/sync/retry-wrapper.d.ts +13 -0
- package/dist/src/core/sync/retry-wrapper.d.ts.map +1 -0
- package/dist/src/core/sync/retry-wrapper.js +37 -0
- package/dist/src/core/sync/retry-wrapper.js.map +1 -0
- package/dist/src/importers/ac-parser.d.ts +27 -0
- package/dist/src/importers/ac-parser.d.ts.map +1 -0
- package/dist/src/importers/ac-parser.js +47 -0
- package/dist/src/importers/ac-parser.js.map +1 -0
- package/dist/src/sync/types.d.ts +8 -0
- package/dist/src/sync/types.d.ts.map +1 -1
- package/dist/src/sync/types.js +12 -0
- package/dist/src/sync/types.js.map +1 -1
- package/package.json +1 -1
- package/plugins/specweave/skills/code-reviewer/agents/reviewer-silent-failures.md +65 -0
- package/plugins/specweave/skills/code-reviewer/agents/reviewer-spec-compliance.md +83 -0
- package/plugins/specweave/skills/code-reviewer/agents/reviewer-types.md +68 -0
- package/plugins/specweave/skills/skill-gen/SKILL.md +20 -3
- package/plugins/specweave/skills/team-lead/agents/architect.md +52 -0
- package/plugins/specweave/skills/team-lead/agents/pm.md +50 -0
- package/plugins/specweave/skills/team-lead/agents/researcher.md +64 -0
- package/plugins/specweave-ado/lib/ado-ac-checkbox-sync.js +23 -21
- package/plugins/specweave-ado/lib/ado-ac-checkbox-sync.ts +37 -29
- package/plugins/specweave-ado/lib/ado-client.js +14 -0
- package/plugins/specweave-ado/lib/ado-client.ts +18 -0
- package/plugins/specweave-ado/lib/ado-pull-sync.js +35 -0
- package/plugins/specweave-ado/lib/ado-pull-sync.ts +74 -0
- package/plugins/specweave-ado/lib/ado-rate-limiter.js +56 -0
- package/plugins/specweave-ado/lib/ado-rate-limiter.ts +86 -0
- package/plugins/specweave-ado/lib/ado-spec-sync.js +25 -1
- package/plugins/specweave-ado/lib/ado-spec-sync.ts +32 -2
- package/plugins/specweave-ado/lib/ado-status-sync.js +52 -14
- package/plugins/specweave-ado/lib/ado-status-sync.ts +64 -16
- package/plugins/specweave-github/lib/github-client-v2.ts +1 -1
- package/plugins/specweave-github/lib/github-push-sync.js +11 -3
- package/plugins/specweave-github/lib/github-push-sync.ts +16 -3
- package/plugins/specweave-jira/lib/jira-spec-sync.js +60 -1
- package/plugins/specweave-jira/lib/jira-spec-sync.ts +93 -1
- package/plugins/specweave-jira/lib/jira-status-sync.js +151 -109
- package/plugins/specweave-jira/lib/jira-status-sync.ts +161 -39
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
You are the TYPE DESIGN REVIEWER agent.
|
|
2
|
+
|
|
3
|
+
REVIEW TARGET: [REVIEW_TARGET]
|
|
4
|
+
|
|
5
|
+
MISSION:
|
|
6
|
+
Analyze type system quality — find overly broad types, unsafe assertions, missing
|
|
7
|
+
invariants, and type designs that don't leverage the compiler to prevent bugs.
|
|
8
|
+
Good types make illegal states unrepresentable. Your job is to find where the type
|
|
9
|
+
system could work harder. You are a read-only analyst — FIND issues, not fix them.
|
|
10
|
+
|
|
11
|
+
SCOPE:
|
|
12
|
+
- If reviewing a PR: run `gh pr diff [PR_NUMBER]` to get the diff, then analyze changed files
|
|
13
|
+
- If reviewing a module: read all files in the target path
|
|
14
|
+
- Focus on TypeScript/JavaScript files (.ts, .tsx, .js, .jsx)
|
|
15
|
+
- Skip type-checking config files, test fixtures, and generated code
|
|
16
|
+
|
|
17
|
+
CHECKLIST:
|
|
18
|
+
1. Explicit `any` type usage — should almost always be `unknown` or a proper type
|
|
19
|
+
2. Type assertions (`as Type`, `!` non-null) that bypass the type system unsafely
|
|
20
|
+
3. Overly broad types (string where a union literal is appropriate, e.g., status: string vs status: "active" | "inactive")
|
|
21
|
+
4. Missing discriminated unions for state machines or multi-state objects
|
|
22
|
+
5. Interface vs type alias misuse (interfaces for objects, types for unions/intersections)
|
|
23
|
+
6. Generic types that could be more constrained (T vs T extends SomeBase)
|
|
24
|
+
7. Missing readonly modifiers on data that should be immutable
|
|
25
|
+
8. Index signatures ([key: string]: any) when specific keys are known
|
|
26
|
+
9. Function return types that are too wide (returns string | number | undefined when narrowable)
|
|
27
|
+
10. Missing or incorrect type predicates and type guards
|
|
28
|
+
11. Zod/io-ts/valibot schemas that diverge from their TypeScript type counterparts
|
|
29
|
+
12. Enums used where const objects or union types would be safer and more tree-shakeable
|
|
30
|
+
|
|
31
|
+
OUTPUT FORMAT:
|
|
32
|
+
Produce a structured findings report using this format for each finding:
|
|
33
|
+
|
|
34
|
+
### [SEVERITY]: [Title]
|
|
35
|
+
- **File**: path/to/file.ts:line
|
|
36
|
+
- **Category**: Type issue (e.g., Unsafe assertion, Overly broad type, Missing discriminant)
|
|
37
|
+
- **Description**: What the type issue is and what it allows that shouldn't be possible
|
|
38
|
+
- **Impact**: What bugs this enables (runtime type errors, invalid state, refactoring hazard)
|
|
39
|
+
- **Recommendation**: The better type design with code example
|
|
40
|
+
- **Code snippet**: The current type (keep brief)
|
|
41
|
+
|
|
42
|
+
Severity levels: CRITICAL | HIGH | MEDIUM | LOW | INFO
|
|
43
|
+
|
|
44
|
+
COMMUNICATION:
|
|
45
|
+
When done, signal completion:
|
|
46
|
+
SendMessage({
|
|
47
|
+
type: "message",
|
|
48
|
+
recipient: "team-lead",
|
|
49
|
+
content: "REVIEW_COMPLETE: Type design review finished. Found [N] issues: [X critical, Y high, Z medium]. Key findings: [brief summary of top 3].",
|
|
50
|
+
summary: "Type design review complete"
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
If you need clarification about type conventions:
|
|
54
|
+
SendMessage({
|
|
55
|
+
type: "message",
|
|
56
|
+
recipient: "team-lead",
|
|
57
|
+
content: "REVIEW_QUESTION: [your question]",
|
|
58
|
+
summary: "Type reviewer needs clarification"
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
RULES:
|
|
62
|
+
- READ-ONLY: Do not modify any files
|
|
63
|
+
- Be specific: include file paths and line numbers for every finding
|
|
64
|
+
- Prioritize: CRITICAL and HIGH findings first
|
|
65
|
+
- No speculation: only report issues with concrete reasoning about what goes wrong
|
|
66
|
+
- Consider project style: if the project consistently uses a pattern, note it but don't fight it
|
|
67
|
+
- Skip generated code: don't flag types in auto-generated files (prisma client, graphql codegen)
|
|
68
|
+
- TypeScript/JavaScript only: skip non-TS files entirely
|
|
@@ -52,13 +52,18 @@ Wait for the user to select a pattern by name or number. The user responds in na
|
|
|
52
52
|
|
|
53
53
|
### Step 4: Check Skill-Creator Plugin
|
|
54
54
|
|
|
55
|
-
Verify Anthropic's official skill-creator is available:
|
|
55
|
+
Verify Anthropic's official skill-creator is available (local-first, then global fallback):
|
|
56
56
|
|
|
57
57
|
```bash
|
|
58
|
-
|
|
58
|
+
# Check local project copy first (auto-installed by specweave init)
|
|
59
|
+
SKILL_CREATOR_PATH=".claude/skills/skill-creator/SKILL.md"
|
|
60
|
+
if [ ! -f "$SKILL_CREATOR_PATH" ]; then
|
|
61
|
+
# Fall back to global plugin cache
|
|
62
|
+
SKILL_CREATOR_PATH=$(find ~/.claude/plugins/cache/claude-plugins-official/skill-creator -name "SKILL.md" -maxdepth 3 2>/dev/null | head -1)
|
|
63
|
+
fi
|
|
59
64
|
if [ -z "$SKILL_CREATOR_PATH" ]; then
|
|
60
65
|
echo "ERROR: Anthropic's skill-creator plugin is not installed."
|
|
61
|
-
echo "Install it via: claude
|
|
66
|
+
echo "Install it via: claude install-skill https://github.com/anthropics/claude-code/tree/main/skill-creator"
|
|
62
67
|
echo ""
|
|
63
68
|
echo "The skill-creator is required to build tested, benchmarked skills."
|
|
64
69
|
exit 1
|
|
@@ -67,6 +72,18 @@ fi
|
|
|
67
72
|
|
|
68
73
|
### Step 5: Delegate to Skill-Creator
|
|
69
74
|
|
|
75
|
+
**Slug dedup guard** — before delegating, check if a skill with this slug already exists:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
SKILL_SLUG="$SELECTED_PATTERN_SLUG" # e.g. "error-handling"
|
|
79
|
+
SKILL_DIR=".claude/skills/$SKILL_SLUG"
|
|
80
|
+
if [ -d "$SKILL_DIR" ] && [ -f "$SKILL_DIR/SKILL.md" ]; then
|
|
81
|
+
echo "Skill '$SKILL_SLUG' already exists at $SKILL_DIR/SKILL.md -- skipping generation."
|
|
82
|
+
# Mark signal as generated in skill-signals.json and continue to next pattern
|
|
83
|
+
exit 0
|
|
84
|
+
fi
|
|
85
|
+
```
|
|
86
|
+
|
|
70
87
|
Invoke the skill-creator with the selected pattern context:
|
|
71
88
|
|
|
72
89
|
1. **Provide context** to skill-creator:
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
You are the ARCHITECT PLANNING agent for increment [INCREMENT_ID].
|
|
2
|
+
|
|
3
|
+
MASTER SPEC (SOURCE OF TRUTH):
|
|
4
|
+
The feature is specified in [MASTER_INCREMENT_PATH]/spec.md.
|
|
5
|
+
Read the spec BEFORE designing anything. Your architecture MUST satisfy all ACs.
|
|
6
|
+
|
|
7
|
+
MISSION:
|
|
8
|
+
Produce plan.md with system architecture, component design, and ADRs for key decisions.
|
|
9
|
+
You own the HOW — defining the technical approach. You work in parallel with
|
|
10
|
+
the Security reviewer who validates your design for vulnerabilities.
|
|
11
|
+
|
|
12
|
+
SKILLS TO INVOKE:
|
|
13
|
+
Skill({ skill: "sw:architect" })
|
|
14
|
+
|
|
15
|
+
FILE OWNERSHIP (WRITE access):
|
|
16
|
+
[MASTER_INCREMENT_PATH]/plan.md
|
|
17
|
+
.specweave/docs/internal/architecture/adr/ (new ADRs only)
|
|
18
|
+
|
|
19
|
+
READ ACCESS: Any file in the repository
|
|
20
|
+
|
|
21
|
+
UPSTREAM DEPENDENCY:
|
|
22
|
+
Wait for the PM agent to signal PLAN_READY or COMPLETION before starting.
|
|
23
|
+
You need spec.md to exist with user stories and ACs before you can design.
|
|
24
|
+
|
|
25
|
+
WORKFLOW:
|
|
26
|
+
1. Read spec.md at [MASTER_INCREMENT_PATH]/spec.md
|
|
27
|
+
2. Explore the codebase to understand existing architecture, patterns, and tech stack
|
|
28
|
+
3. Check existing ADRs at .specweave/docs/internal/architecture/adr/
|
|
29
|
+
4. Design system architecture:
|
|
30
|
+
- Component boundaries and responsibilities
|
|
31
|
+
- Data flow and state management
|
|
32
|
+
- API contracts and integration points
|
|
33
|
+
- Error handling strategy
|
|
34
|
+
- Performance considerations
|
|
35
|
+
5. Write ADRs for significant architectural decisions (use ADR template format)
|
|
36
|
+
6. Write plan.md to [MASTER_INCREMENT_PATH]/plan.md
|
|
37
|
+
7. Signal architecture decisions:
|
|
38
|
+
SendMessage({ type: "message", recipient: "team-lead",
|
|
39
|
+
content: "CONTRACT_READY: Architecture defined in plan.md.\nComponents: [list]\nKey patterns: [e.g., CQRS, event-driven]\nADRs created: [list or 'none']\nTech stack: [decisions]",
|
|
40
|
+
summary: "Architect: plan.md ready with architecture" })
|
|
41
|
+
8. Signal COMPLETION:
|
|
42
|
+
SendMessage({ type: "message", recipient: "team-lead",
|
|
43
|
+
content: "COMPLETION: plan.md finalized.\nComponents: [count]\nADRs: [count]\nKey risk: [biggest concern]",
|
|
44
|
+
summary: "Architect agent: plan complete" })
|
|
45
|
+
|
|
46
|
+
RULES:
|
|
47
|
+
- WRITE only plan.md and ADRs — do not modify spec.md or create tasks.md
|
|
48
|
+
- Every architectural decision must be justified (not just "use X because it's popular")
|
|
49
|
+
- Consider scalability, maintainability, testability, and security
|
|
50
|
+
- Reference existing codebase patterns — don't propose patterns alien to the project
|
|
51
|
+
- Flag technical risks and mitigation strategies
|
|
52
|
+
- Keep plan.md actionable — an implementer should be able to code from it
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
You are the PM PLANNING agent for increment [INCREMENT_ID].
|
|
2
|
+
|
|
3
|
+
FEATURE DESCRIPTION: [FEATURE_DESCRIPTION]
|
|
4
|
+
|
|
5
|
+
MASTER INCREMENT PATH: [MASTER_INCREMENT_PATH]
|
|
6
|
+
|
|
7
|
+
MISSION:
|
|
8
|
+
Produce a comprehensive spec.md with user stories, acceptance criteria, and scope
|
|
9
|
+
boundaries. You own the WHAT — defining what the feature does and how success is
|
|
10
|
+
measured. You work in parallel with the Architect agent who owns the HOW.
|
|
11
|
+
|
|
12
|
+
SKILLS TO INVOKE:
|
|
13
|
+
Skill({ skill: "sw:pm" })
|
|
14
|
+
|
|
15
|
+
FILE OWNERSHIP (WRITE access):
|
|
16
|
+
[MASTER_INCREMENT_PATH]/spec.md
|
|
17
|
+
|
|
18
|
+
READ ACCESS: Any file in the repository (for understanding existing patterns and domain)
|
|
19
|
+
|
|
20
|
+
WORKFLOW:
|
|
21
|
+
1. Read the feature description and any existing context
|
|
22
|
+
2. Explore the codebase to understand the domain, existing patterns, and constraints
|
|
23
|
+
3. Identify stakeholders, personas, and key use cases
|
|
24
|
+
4. Write user stories with acceptance criteria following the format:
|
|
25
|
+
### US-NNN: Story Title
|
|
26
|
+
**Project**: [project-name]
|
|
27
|
+
**As a** [role]
|
|
28
|
+
**I want** [capability]
|
|
29
|
+
**So that** [benefit]
|
|
30
|
+
**Acceptance Criteria**:
|
|
31
|
+
- [ ] **AC-USNN-01**: [Criterion]
|
|
32
|
+
5. Define scope boundaries (in-scope vs out-of-scope)
|
|
33
|
+
6. Write spec.md to [MASTER_INCREMENT_PATH]/spec.md
|
|
34
|
+
7. Send PLAN_READY notification (do NOT wait for response):
|
|
35
|
+
SendMessage({ type: "message", recipient: "team-lead",
|
|
36
|
+
content: "PLAN_READY: spec.md written at [MASTER_INCREMENT_PATH]/spec.md\nUser Stories: [count]\nACs: [count]\nKey decisions: [1-2 sentence summary]",
|
|
37
|
+
summary: "PM: spec.md ready — proceeding" })
|
|
38
|
+
8. Proceed immediately. If team-lead sends PLAN_CORRECTION, revise spec.md accordingly.
|
|
39
|
+
9. Signal COMPLETION:
|
|
40
|
+
SendMessage({ type: "message", recipient: "team-lead",
|
|
41
|
+
content: "COMPLETION: spec.md finalized.\nStories: [count]\nACs: [count]\nScope: [brief summary]",
|
|
42
|
+
summary: "PM agent: spec complete" })
|
|
43
|
+
|
|
44
|
+
RULES:
|
|
45
|
+
- WRITE only spec.md — do not create plan.md or tasks.md (Architect and Planner own those)
|
|
46
|
+
- Every user story MUST have a **Project**: field
|
|
47
|
+
- Every AC MUST use the AC-USNN-NN format for bidirectional linking
|
|
48
|
+
- Be specific in ACs — testable, not vague ("user can log in" not "auth works")
|
|
49
|
+
- Consider edge cases, error states, and non-functional requirements
|
|
50
|
+
- Do NOT scope-creep — stick to the feature description
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
You are the RESEARCHER agent.
|
|
2
|
+
|
|
3
|
+
RESEARCH TOPIC: [RESEARCH_TOPIC]
|
|
4
|
+
RESEARCH SCOPE: [RESEARCH_SCOPE]
|
|
5
|
+
|
|
6
|
+
MISSION:
|
|
7
|
+
Investigate the given topic thoroughly — explore the codebase, search the web,
|
|
8
|
+
analyze patterns, and compile actionable findings. You are a read-only analyst.
|
|
9
|
+
Your job is to FIND information, not implement changes.
|
|
10
|
+
|
|
11
|
+
APPROACH:
|
|
12
|
+
1. Parse the research scope to understand what information is needed
|
|
13
|
+
2. Explore the codebase for relevant patterns, implementations, and conventions
|
|
14
|
+
3. Search the web for related technologies, best practices, and alternatives
|
|
15
|
+
4. Cross-reference findings — validate web claims against actual codebase state
|
|
16
|
+
5. Compile a structured research report
|
|
17
|
+
|
|
18
|
+
YOUR REPORT MUST INCLUDE:
|
|
19
|
+
|
|
20
|
+
### Executive Summary
|
|
21
|
+
2-3 sentence overview of key findings and recommendation.
|
|
22
|
+
|
|
23
|
+
### Current State
|
|
24
|
+
What exists today in the codebase. Include file paths and line references.
|
|
25
|
+
|
|
26
|
+
### External Research
|
|
27
|
+
What the broader ecosystem offers. Technologies, libraries, patterns considered.
|
|
28
|
+
Include sources and links where relevant.
|
|
29
|
+
|
|
30
|
+
### Analysis
|
|
31
|
+
Compare options. Use a decision matrix if comparing 3+ alternatives:
|
|
32
|
+
| Option | Pros | Cons | Effort | Fit |
|
|
33
|
+
|
|
34
|
+
### Recommendations
|
|
35
|
+
Concrete, actionable recommendations ranked by priority.
|
|
36
|
+
Each should include: what to do, why, and estimated effort.
|
|
37
|
+
|
|
38
|
+
### Open Questions
|
|
39
|
+
Things that need further investigation or user input.
|
|
40
|
+
|
|
41
|
+
COMMUNICATION:
|
|
42
|
+
When done, signal completion:
|
|
43
|
+
SendMessage({
|
|
44
|
+
type: "message",
|
|
45
|
+
recipient: "team-lead",
|
|
46
|
+
content: "RESEARCH_COMPLETE: [topic] research finished.\nKey finding: [most important insight]\nRecommendation: [primary recommendation]\nOpen questions: [count]",
|
|
47
|
+
summary: "Research complete: [topic]"
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
For significant discoveries during research:
|
|
51
|
+
SendMessage({
|
|
52
|
+
type: "message",
|
|
53
|
+
recipient: "team-lead",
|
|
54
|
+
content: "INSIGHT: [important discovery that may affect scope or approach]",
|
|
55
|
+
summary: "Researcher found insight"
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
RULES:
|
|
59
|
+
- READ-ONLY: Do not modify any files
|
|
60
|
+
- Be thorough: explore multiple angles, not just the first result
|
|
61
|
+
- Be specific: include file paths, line numbers, URLs — not vague references
|
|
62
|
+
- Be honest: flag uncertainty and gaps in knowledge
|
|
63
|
+
- Stay scoped: answer the research question, don't expand into tangential topics
|
|
64
|
+
- Cite sources: for web findings, include the source URL or reference
|
|
@@ -5,6 +5,7 @@ import axios from "axios";
|
|
|
5
5
|
import { consoleLogger } from "../../specweave/lib/vendor/utils/logger.js";
|
|
6
6
|
import { deriveFeatureId } from "../../specweave/lib/vendor/utils/feature-id-derivation.js";
|
|
7
7
|
import { GitHubACCheckboxSync } from "../../specweave-github/lib/github-ac-checkbox-sync.js";
|
|
8
|
+
import { AdoDescriptionUpdater } from "../../../src/core/ado-description-updater.js";
|
|
8
9
|
class AdoACCheckboxSync {
|
|
9
10
|
constructor(options) {
|
|
10
11
|
this.projectRoot = options.projectRoot;
|
|
@@ -88,47 +89,48 @@ class AdoACCheckboxSync {
|
|
|
88
89
|
}
|
|
89
90
|
}
|
|
90
91
|
/**
|
|
91
|
-
*
|
|
92
|
-
*
|
|
92
|
+
* Update a work item's AC section using API-based section manipulation.
|
|
93
|
+
*
|
|
94
|
+
* GET description → formatACCheckboxes → updateAcSection → PATCH with
|
|
95
|
+
* JSON Patch op:"replace". No regex used at any point.
|
|
93
96
|
*/
|
|
94
|
-
async
|
|
97
|
+
async updateWorkItemACSection(client, storyId, acStatus) {
|
|
98
|
+
const updater = new AdoDescriptionUpdater();
|
|
95
99
|
const resp = await client.get(
|
|
96
100
|
`/wit/workitems/${storyId}?fields=System.Description&api-version=7.0`
|
|
97
101
|
);
|
|
98
|
-
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const beforeRegex = new RegExp(`(\u2610|\u2611)\\s+(${escaped}:)`, "g");
|
|
104
|
-
const newCheckbox = completed ? "\u2611" : "\u2610";
|
|
105
|
-
const replaced = description.replace(beforeRegex, `${newCheckbox} $2`);
|
|
106
|
-
if (replaced !== description) {
|
|
107
|
-
updatedCount++;
|
|
108
|
-
description = replaced;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
if (description === original) return 0;
|
|
102
|
+
const currentDescription = resp.data?.fields?.["System.Description"] ?? "";
|
|
103
|
+
const newAcHtml = updater.formatACCheckboxes(acStatus);
|
|
104
|
+
if (!newAcHtml) return 0;
|
|
105
|
+
const updatedDescription = updater.updateAcSection(currentDescription, newAcHtml);
|
|
106
|
+
if (updatedDescription === currentDescription) return 0;
|
|
112
107
|
await client.patch(
|
|
113
108
|
`/wit/workitems/${storyId}?api-version=7.0`,
|
|
114
|
-
[{ op: "replace", path: "/fields/System.Description", value:
|
|
109
|
+
[{ op: "replace", path: "/fields/System.Description", value: updatedDescription }],
|
|
115
110
|
{ headers: { "Content-Type": "application/json-patch+json" } }
|
|
116
111
|
);
|
|
117
112
|
const completedCount = [...acStatus.values()].filter(Boolean).length;
|
|
118
113
|
const totalCount = acStatus.size;
|
|
119
114
|
const percentage = Math.round(completedCount / totalCount * 100);
|
|
120
115
|
const acLines = [...acStatus.entries()].map(([id, done]) => `<li>${done ? "\u2705" : "\u2B1C"} ${id}</li>`).join("\n");
|
|
121
|
-
const commentHtml = `<h3
|
|
116
|
+
const commentHtml = `<h3>AC Progress Update</h3>
|
|
122
117
|
<p><strong>Acceptance Criteria</strong>: ${completedCount}/${totalCount} (${percentage}%)</p>
|
|
123
118
|
<ul>
|
|
124
119
|
${acLines}
|
|
125
120
|
</ul>
|
|
126
|
-
<p
|
|
121
|
+
<p>Auto-updated by SpecWeave</p>`;
|
|
127
122
|
await client.post(
|
|
128
123
|
`/wit/workItems/${storyId}/comments?api-version=7.1-preview.3`,
|
|
129
124
|
{ text: commentHtml }
|
|
130
125
|
);
|
|
131
|
-
return
|
|
126
|
+
return acStatus.size;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Legacy: Fetch current HTML description, regex-flip checkboxes, PATCH back.
|
|
130
|
+
* Kept for backward compatibility. New code should use updateWorkItemACSection.
|
|
131
|
+
*/
|
|
132
|
+
async updateStoryCheckboxes(client, adoConfig, storyId, acStatus) {
|
|
133
|
+
return this.updateWorkItemACSection(client, storyId, acStatus);
|
|
132
134
|
}
|
|
133
135
|
buildClient(adoConfig) {
|
|
134
136
|
const token = Buffer.from(`:${adoConfig.pat}`).toString("base64");
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
* ADO AC Checkbox Sync
|
|
3
3
|
*
|
|
4
4
|
* When ACs are marked complete in spec.md, updates the HTML description
|
|
5
|
-
* of the linked ADO work item
|
|
5
|
+
* of the linked ADO work item using API-based section update (not regex).
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* Uses AdoDescriptionUpdater for clean HTML section manipulation via
|
|
8
|
+
* sentinel comments (<!-- AC_SECTION_START/END -->).
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { promises as fs, existsSync } from 'fs';
|
|
@@ -15,6 +15,7 @@ import axios, { AxiosInstance } from 'axios';
|
|
|
15
15
|
import { Logger, consoleLogger } from '../../specweave/lib/vendor/utils/logger.js';
|
|
16
16
|
import { deriveFeatureId } from '../../specweave/lib/vendor/utils/feature-id-derivation.js';
|
|
17
17
|
import { GitHubACCheckboxSync } from '../../specweave-github/lib/github-ac-checkbox-sync.js';
|
|
18
|
+
import { AdoDescriptionUpdater } from '../../../src/core/ado-description-updater.js';
|
|
18
19
|
import type { SpecWeaveConfig } from '../../../src/core/config/types.js';
|
|
19
20
|
import type { LivingDocsUSFile } from '../../../src/types/living-docs-us-file.js';
|
|
20
21
|
|
|
@@ -131,41 +132,35 @@ export class AdoACCheckboxSync {
|
|
|
131
132
|
}
|
|
132
133
|
|
|
133
134
|
/**
|
|
134
|
-
*
|
|
135
|
-
*
|
|
135
|
+
* Update a work item's AC section using API-based section manipulation.
|
|
136
|
+
*
|
|
137
|
+
* GET description → formatACCheckboxes → updateAcSection → PATCH with
|
|
138
|
+
* JSON Patch op:"replace". No regex used at any point.
|
|
136
139
|
*/
|
|
137
|
-
|
|
140
|
+
async updateWorkItemACSection(
|
|
138
141
|
client: AxiosInstance,
|
|
139
|
-
adoConfig: { organization: string; project: string; pat: string },
|
|
140
142
|
storyId: number,
|
|
141
|
-
acStatus: Map<string, boolean
|
|
143
|
+
acStatus: Map<string, boolean>,
|
|
142
144
|
): Promise<number> {
|
|
145
|
+
const updater = new AdoDescriptionUpdater();
|
|
146
|
+
|
|
143
147
|
const resp = await client.get(
|
|
144
148
|
`/wit/workitems/${storyId}?fields=System.Description&api-version=7.0`
|
|
145
149
|
);
|
|
146
150
|
|
|
147
|
-
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
for (const [acId, completed] of acStatus) {
|
|
152
|
-
const escaped = acId.replace(/-/g, '\\-');
|
|
153
|
-
// Pattern: ☐ AC-US1-01: or ☑ AC-US1-01:
|
|
154
|
-
const beforeRegex = new RegExp(`(☐|☑)\\s+(${escaped}:)`, 'g');
|
|
155
|
-
const newCheckbox = completed ? '☑' : '☐';
|
|
156
|
-
const replaced = description.replace(beforeRegex, `${newCheckbox} $2`);
|
|
157
|
-
if (replaced !== description) {
|
|
158
|
-
updatedCount++;
|
|
159
|
-
description = replaced;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
151
|
+
const currentDescription: string = resp.data?.fields?.['System.Description'] ?? '';
|
|
152
|
+
const newAcHtml = updater.formatACCheckboxes(acStatus);
|
|
153
|
+
|
|
154
|
+
if (!newAcHtml) return 0;
|
|
162
155
|
|
|
163
|
-
|
|
156
|
+
const updatedDescription = updater.updateAcSection(currentDescription, newAcHtml);
|
|
164
157
|
|
|
165
|
-
|
|
158
|
+
if (updatedDescription === currentDescription) return 0;
|
|
159
|
+
|
|
160
|
+
// PATCH description via JSON Patch
|
|
166
161
|
await client.patch(
|
|
167
162
|
`/wit/workitems/${storyId}?api-version=7.0`,
|
|
168
|
-
[{ op: 'replace', path: '/fields/System.Description', value:
|
|
163
|
+
[{ op: 'replace', path: '/fields/System.Description', value: updatedDescription }],
|
|
169
164
|
{ headers: { 'Content-Type': 'application/json-patch+json' } }
|
|
170
165
|
);
|
|
171
166
|
|
|
@@ -178,19 +173,32 @@ export class AdoACCheckboxSync {
|
|
|
178
173
|
.map(([id, done]) => `<li>${done ? '✅' : '⬜'} ${id}</li>`)
|
|
179
174
|
.join('\n');
|
|
180
175
|
|
|
181
|
-
const commentHtml = `<h3
|
|
176
|
+
const commentHtml = `<h3>AC Progress Update</h3>
|
|
182
177
|
<p><strong>Acceptance Criteria</strong>: ${completedCount}/${totalCount} (${percentage}%)</p>
|
|
183
178
|
<ul>
|
|
184
179
|
${acLines}
|
|
185
180
|
</ul>
|
|
186
|
-
<p
|
|
181
|
+
<p>Auto-updated by SpecWeave</p>`;
|
|
187
182
|
|
|
188
183
|
await client.post(
|
|
189
184
|
`/wit/workItems/${storyId}/comments?api-version=7.1-preview.3`,
|
|
190
185
|
{ text: commentHtml }
|
|
191
186
|
);
|
|
192
187
|
|
|
193
|
-
return
|
|
188
|
+
return acStatus.size;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Legacy: Fetch current HTML description, regex-flip checkboxes, PATCH back.
|
|
193
|
+
* Kept for backward compatibility. New code should use updateWorkItemACSection.
|
|
194
|
+
*/
|
|
195
|
+
private async updateStoryCheckboxes(
|
|
196
|
+
client: AxiosInstance,
|
|
197
|
+
adoConfig: { organization: string; project: string; pat: string },
|
|
198
|
+
storyId: number,
|
|
199
|
+
acStatus: Map<string, boolean>
|
|
200
|
+
): Promise<number> {
|
|
201
|
+
return this.updateWorkItemACSection(client, storyId, acStatus);
|
|
194
202
|
}
|
|
195
203
|
|
|
196
204
|
private buildClient(adoConfig: { organization: string; project: string; pat: string }): AxiosInstance {
|
|
@@ -114,6 +114,20 @@ class AdoClient {
|
|
|
114
114
|
const url = `${this.baseUrl}/_apis/wit/workitems/${id}?api-version=7.1`;
|
|
115
115
|
await this.request("DELETE", url);
|
|
116
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* Pull the current state of a work item for bidirectional sync.
|
|
119
|
+
*
|
|
120
|
+
* @param id - ADO work item ID
|
|
121
|
+
* @returns state and last-modified timestamp
|
|
122
|
+
*/
|
|
123
|
+
async pullWorkItemState(id) {
|
|
124
|
+
const url = `${this.baseUrl}/_apis/wit/workitems/${id}?$select=System.State,System.ChangedDate&api-version=7.1`;
|
|
125
|
+
const item = await this.request("GET", url);
|
|
126
|
+
return {
|
|
127
|
+
state: item.fields["System.State"],
|
|
128
|
+
modifiedAt: new Date(item.fields["System.ChangedDate"])
|
|
129
|
+
};
|
|
130
|
+
}
|
|
117
131
|
// ==========================================================================
|
|
118
132
|
// Comment Operations
|
|
119
133
|
// ==========================================================================
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import https from 'https';
|
|
11
|
+
import { AdoRateLimiter } from './ado-rate-limiter.js';
|
|
11
12
|
|
|
12
13
|
// ============================================================================
|
|
13
14
|
// Types
|
|
@@ -20,6 +21,8 @@ export interface AdoConfig {
|
|
|
20
21
|
workItemType?: 'Epic' | 'Feature' | 'User Story';
|
|
21
22
|
areaPath?: string;
|
|
22
23
|
iterationPath?: string;
|
|
24
|
+
/** Optional rate limiter instance (shared across client instances) */
|
|
25
|
+
rateLimiter?: AdoRateLimiter;
|
|
23
26
|
}
|
|
24
27
|
|
|
25
28
|
export interface WorkItem {
|
|
@@ -207,6 +210,21 @@ export class AdoClient {
|
|
|
207
210
|
await this.request('DELETE', url);
|
|
208
211
|
}
|
|
209
212
|
|
|
213
|
+
/**
|
|
214
|
+
* Pull the current state of a work item for bidirectional sync.
|
|
215
|
+
*
|
|
216
|
+
* @param id - ADO work item ID
|
|
217
|
+
* @returns state and last-modified timestamp
|
|
218
|
+
*/
|
|
219
|
+
async pullWorkItemState(id: number): Promise<{ state: string; modifiedAt: Date }> {
|
|
220
|
+
const url = `${this.baseUrl}/_apis/wit/workitems/${id}?$select=System.State,System.ChangedDate&api-version=7.1`;
|
|
221
|
+
const item = await this.request<WorkItem>('GET', url);
|
|
222
|
+
return {
|
|
223
|
+
state: item.fields['System.State'],
|
|
224
|
+
modifiedAt: new Date(item.fields['System.ChangedDate']),
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
210
228
|
// ==========================================================================
|
|
211
229
|
// Comment Operations
|
|
212
230
|
// ==========================================================================
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
function mapAdoStateToSpecweave(adoState) {
|
|
2
|
+
const map = {
|
|
3
|
+
"New": "planned",
|
|
4
|
+
"To Do": "planned",
|
|
5
|
+
"Active": "active",
|
|
6
|
+
"Doing": "active",
|
|
7
|
+
"In Progress": "active",
|
|
8
|
+
"Resolved": "completed",
|
|
9
|
+
"Closed": "completed",
|
|
10
|
+
"Done": "completed",
|
|
11
|
+
"Removed": "abandoned"
|
|
12
|
+
};
|
|
13
|
+
return map[adoState] ?? "active";
|
|
14
|
+
}
|
|
15
|
+
function pullAdoChanges(ctx) {
|
|
16
|
+
if (!ctx.canUpsertInternalItems) {
|
|
17
|
+
return { changed: false, reason: "canUpsertInternalItems is false" };
|
|
18
|
+
}
|
|
19
|
+
const mappedStatus = mapAdoStateToSpecweave(ctx.adoState);
|
|
20
|
+
if (mappedStatus === ctx.localStatus) {
|
|
21
|
+
return { changed: false, reason: "states are equivalent" };
|
|
22
|
+
}
|
|
23
|
+
if (ctx.adoModifiedAt.getTime() <= ctx.localUpdatedAt.getTime()) {
|
|
24
|
+
return { changed: false, reason: "local is newer" };
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
changed: true,
|
|
28
|
+
newStatus: mappedStatus,
|
|
29
|
+
reason: `ADO state "${ctx.adoState}" is newer \u2014 updating to "${mappedStatus}"`
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export {
|
|
33
|
+
mapAdoStateToSpecweave,
|
|
34
|
+
pullAdoChanges
|
|
35
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ADO Pull Sync — Bidirectional state synchronization
|
|
3
|
+
*
|
|
4
|
+
* Implements last-write-wins conflict resolution for pulling
|
|
5
|
+
* ADO work item state changes back into SpecWeave metadata.
|
|
6
|
+
*
|
|
7
|
+
* @module ado-pull-sync
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export interface PullSyncContext {
|
|
11
|
+
localStatus: string;
|
|
12
|
+
localUpdatedAt: Date;
|
|
13
|
+
adoState: string;
|
|
14
|
+
adoModifiedAt: Date;
|
|
15
|
+
workItemId: number;
|
|
16
|
+
canUpsertInternalItems: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface PullSyncResult {
|
|
20
|
+
changed: boolean;
|
|
21
|
+
newStatus?: string;
|
|
22
|
+
reason?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Map ADO work item states to SpecWeave statuses.
|
|
27
|
+
*
|
|
28
|
+
* Covers Agile/Scrum/Basic process templates.
|
|
29
|
+
*/
|
|
30
|
+
export function mapAdoStateToSpecweave(adoState: string): string {
|
|
31
|
+
const map: Record<string, string> = {
|
|
32
|
+
'New': 'planned',
|
|
33
|
+
'To Do': 'planned',
|
|
34
|
+
'Active': 'active',
|
|
35
|
+
'Doing': 'active',
|
|
36
|
+
'In Progress': 'active',
|
|
37
|
+
'Resolved': 'completed',
|
|
38
|
+
'Closed': 'completed',
|
|
39
|
+
'Done': 'completed',
|
|
40
|
+
'Removed': 'abandoned',
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
return map[adoState] ?? 'active';
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Determine if ADO changes should override local state.
|
|
48
|
+
*
|
|
49
|
+
* Uses last-write-wins: if ADO was modified more recently than local,
|
|
50
|
+
* and the mapped state differs, update local metadata.
|
|
51
|
+
*/
|
|
52
|
+
export function pullAdoChanges(ctx: PullSyncContext): PullSyncResult {
|
|
53
|
+
if (!ctx.canUpsertInternalItems) {
|
|
54
|
+
return { changed: false, reason: 'canUpsertInternalItems is false' };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const mappedStatus = mapAdoStateToSpecweave(ctx.adoState);
|
|
58
|
+
|
|
59
|
+
// Same effective status — no change needed
|
|
60
|
+
if (mappedStatus === ctx.localStatus) {
|
|
61
|
+
return { changed: false, reason: 'states are equivalent' };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Last-write-wins: ADO must be newer
|
|
65
|
+
if (ctx.adoModifiedAt.getTime() <= ctx.localUpdatedAt.getTime()) {
|
|
66
|
+
return { changed: false, reason: 'local is newer' };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
changed: true,
|
|
71
|
+
newStatus: mappedStatus,
|
|
72
|
+
reason: `ADO state "${ctx.adoState}" is newer — updating to "${mappedStatus}"`,
|
|
73
|
+
};
|
|
74
|
+
}
|