sinapse-ai 1.21.0 → 1.22.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/rules/coderabbit-integration.md +6 -0
- package/.claude/rules/documentation-first-reference.md +146 -0
- package/.claude/rules/documentation-first.md +27 -119
- package/.claude/rules/mandatory-delegation-reference.md +93 -0
- package/.claude/rules/mandatory-delegation.md +29 -93
- package/.claude/rules/project-intelligence-reference.md +159 -0
- package/.claude/rules/project-intelligence.md +37 -155
- package/.claude/rules/safe-collaboration-reference.md +163 -0
- package/.claude/rules/safe-collaboration.md +32 -173
- package/.sinapse-ai/core/doctor/checks/index.js +1 -1
- package/.sinapse-ai/core/execution/build-orchestrator.js +4 -3
- package/.sinapse-ai/core/execution/subagent-dispatcher.js +2 -2
- package/.sinapse-ai/core/ideation/ideation-engine.js +2 -1
- package/.sinapse-ai/core/orchestration/cli-commands.js +13 -0
- package/.sinapse-ai/core/orchestration/doc-first-resolver.js +93 -2
- package/.sinapse-ai/core/orchestration/executors/epic-3-executor.js +15 -2
- package/.sinapse-ai/core/orchestration/executors/epic-4-executor.js +3 -1
- package/.sinapse-ai/core/orchestration/index.js +5 -0
- package/.sinapse-ai/core/orchestration/spec-complexity.js +141 -0
- package/.sinapse-ai/core-config.yaml +30 -0
- package/.sinapse-ai/data/entity-registry.yaml +46 -25
- package/.sinapse-ai/development/agents/architect.md +3 -15
- package/.sinapse-ai/development/agents/data-engineer.md +3 -15
- package/.sinapse-ai/development/agents/developer.md +3 -24
- package/.sinapse-ai/development/agents/devops.md +4 -25
- package/.sinapse-ai/development/agents/quality-gate.md +3 -25
- package/.sinapse-ai/development/knowledge-base/token-economy-guide.md +1 -1
- package/.sinapse-ai/development/tasks/resolve-github-issue.md +1 -1
- package/.sinapse-ai/docs/standards/SINAPSE-LIVRO-DE-OURO-V2.1-COMPLETE.md +6 -2
- package/.sinapse-ai/docs/standards/SINAPSE-LIVRO-DE-OURO-V2.2-SUMMARY.md +4 -1
- package/.sinapse-ai/install-manifest.yaml +44 -40
- package/.sinapse-ai/product/templates/story-tmpl.yaml +11 -6
- package/.sinapse-ai/quality/judge-calibration/README.md +75 -0
- package/.sinapse-ai/quality/judge-calibration/calibration-log.md +70 -0
- package/.sinapse-ai/quality/judge-calibration/golden-set.json +105 -0
- package/.sinapse-ai/quality/judge-calibration/scenarios.json +90 -0
- package/CHANGELOG.md +38 -8
- package/README.en.md +1 -1
- package/README.md +6 -6
- package/bin/sinapse-minimal.js +5 -4
- package/docs/community/README-community-snippet-core.md +1 -1
- package/docs/community/README-community-snippet-mcp.md +2 -2
- package/docs/framework/README.md +2 -2
- package/docs/framework/architecture-overview.md +2 -2
- package/docs/framework/core-architecture.md +1 -1
- package/docs/framework/feature-process.md +5 -5
- package/docs/framework/guiding-principles.md +1 -1
- package/docs/framework/roadmap.md +4 -4
- package/docs/framework/source-tree.md +1 -1
- package/docs/framework/versioning-and-releases.md +1 -1
- package/docs/getting-started.md +1 -1
- package/docs/guides/agent-selection-guide.md +1 -1
- package/docs/guides/agents/traces/README.md +1 -1
- package/docs/guides/config-migration-guide.md +1 -1
- package/docs/guides/development-setup.md +1 -1
- package/docs/guides/docker-mcp-setup.md +5 -5
- package/docs/guides/getting-started.md +1 -1
- package/docs/guides/git-workflow-guide.md +3 -3
- package/docs/guides/ide-integration.md +3 -3
- package/docs/guides/ide-sync-guide.md +1 -1
- package/docs/guides/mcp/desktop-commander.md +2 -2
- package/docs/guides/mcp/docker-gateway-tutorial.md +1 -1
- package/docs/guides/mcp-global-setup.md +3 -3
- package/docs/guides/memory-intelligence-system.md +10 -10
- package/docs/guides/meta-agent-commands.md +1 -1
- package/docs/guides/quality-gates.md +1 -1
- package/docs/guides/security-hardening.md +1 -1
- package/docs/guides/service-discovery.md +1 -1
- package/docs/guides/squad-examples/README.md +1 -1
- package/docs/guides/squads-guide.md +1 -1
- package/docs/guides/testing-guide.md +2 -2
- package/docs/guides/user-guide.md +22 -22
- package/docs/guides/workflows/BROWNFIELD-DISCOVERY-WORKFLOW.md +3 -3
- package/docs/guides/workflows/BROWNFIELD-SERVICE-WORKFLOW.md +1 -1
- package/docs/guides/workflows/GREENFIELD-UI-WORKFLOW.md +2 -2
- package/docs/guides/workflows/SPEC-PIPELINE-WORKFLOW.md +3 -3
- package/docs/guides/workflows/STORY-DEVELOPMENT-CYCLE-WORKFLOW.md +3 -3
- package/docs/guides/workflows/pro-developer-workflow.md +2 -2
- package/docs/guides/workflows-guide.md +1 -1
- package/docs/guides/workflows-overview.md +1 -1
- package/docs/installation/npx-install.md +1 -1
- package/docs/installation/uninstallation.md +1 -1
- package/docs/installation/v4-quick-start.md +1 -1
- package/docs/pt/guides/user-guide.md +18 -18
- package/docs/pt/security.md +2 -2
- package/docs/security/overview.md +2 -2
- package/docs/security/security-best-practices.md +1 -1
- package/docs/sinapse-agent-flows/architect-system.md +1 -1
- package/docs/sinapse-agent-flows/data-engineer-system.md +1 -1
- package/docs/sinapse-agent-flows/dev-system.md +1 -1
- package/docs/sinapse-agent-flows/devops-system.md +2 -2
- package/docs/sinapse-agent-flows/qa-system.md +1 -1
- package/docs/sinapse-agent-flows/sm-system.md +2 -2
- package/docs/sinapse-agent-flows/snps-orqx-system.md +4 -4
- package/docs/sinapse-agent-flows/squad-creator-system.md +1 -1
- package/docs/sinapse-workflows/README.md +2 -2
- package/docs/sinapse-workflows/brownfield-discovery-workflow.md +3 -3
- package/docs/sinapse-workflows/brownfield-service-workflow.md +3 -3
- package/docs/sinapse-workflows/greenfield-ui-workflow.md +2 -2
- package/docs/sinapse-workflows/spec-pipeline-workflow.md +3 -3
- package/docs/sinapse-workflows/story-development-cycle-workflow.md +3 -3
- package/docs/troubleshooting.md +1 -1
- package/package.json +5 -1
- package/scripts/calibrate-judge.js +134 -0
- package/scripts/validate-all.js +5 -0
- package/scripts/validate-constitution.js +58 -0
- package/scripts/validate-story-acs.js +174 -0
- package/scripts/validate-tool-descriptions.js +128 -0
- package/scripts/wave-gate.js +1 -1
- package/squads/claude-code-mastery/agents/project-integrator.md +3 -10
- package/squads/claude-code-mastery/knowledge-base/context-window-optimization.md +2 -2
- package/squads/claude-code-mastery/knowledge-base/memory-systems-reference.md +1 -1
- package/squads/squad-copy/knowledge-base/ai-copy-human-loop-canon.md +1 -1
|
@@ -1,185 +1,44 @@
|
|
|
1
1
|
# Safe Collaboration — Git Safety Net (NON-NEGOTIABLE)
|
|
2
2
|
|
|
3
3
|
> **Applies to ALL agents, ALL projects using SINAPSE.**
|
|
4
|
-
> Users are product builders, NOT git experts.
|
|
5
|
-
>
|
|
4
|
+
> Users are product builders, NOT git experts. Agents handle ALL git complexity.
|
|
5
|
+
> This is the always-on CORE (the law). Operational detail (branch naming tables,
|
|
6
|
+
> step-by-step checklists, conflict matrices, communication templates) lives in
|
|
7
|
+
> `safe-collaboration-reference.md` and loads when you work on files.
|
|
6
8
|
|
|
7
9
|
## Golden Rule
|
|
8
10
|
|
|
9
11
|
**Users focus on WHAT to build. Agents handle HOW to save and share it safely.**
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
6. Verify branch protection is active on main
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
**NEVER start work on `main` directly.** Always create a feature branch.
|
|
34
|
-
|
|
35
|
-
### 2. Branch Naming — Automatic
|
|
36
|
-
|
|
37
|
-
The agent creates the branch. The user never needs to name it.
|
|
38
|
-
|
|
39
|
-
| Who | Branch Pattern | Example |
|
|
40
|
-
|-----|---------------|---------|
|
|
41
|
-
| Caio's session | `caio/{type}/{short-desc}` | `caio/feat/installer-ux` |
|
|
42
|
-
| Matheus's session | `soier/{type}/{short-desc}` | `soier/fix/agent-config` |
|
|
43
|
-
| AI agent | `agent/{squad}/{agent-id}/{type}-{desc}` | `agent/core/pixel/feat-dark-mode` |
|
|
44
|
-
| Unknown | `dev/{type}/{short-desc}` | `dev/feat/new-feature` |
|
|
45
|
-
|
|
46
|
-
Types: `feat`, `fix`, `refactor`, `docs`, `chore`, `test`
|
|
47
|
-
|
|
48
|
-
**User Detection (priority order):**
|
|
49
|
-
1. `git config user.name` -> lookup in mapping table (case-insensitive)
|
|
50
|
-
2. `$USERNAME` (Windows) or `$USER` (Unix) -> lookup in mapping table
|
|
51
|
-
3. Fallback: `dev/`
|
|
52
|
-
|
|
53
|
-
**Mapping Table:**
|
|
54
|
-
|
|
55
|
-
| git config / env var contains | Branch prefix |
|
|
56
|
-
|-------------------------------|---------------|
|
|
57
|
-
| caio (case-insensitive) | `caio/` |
|
|
58
|
-
| matheus OR soier | `soier/` |
|
|
59
|
-
| (anything else) | `dev/` |
|
|
60
|
-
|
|
61
|
-
### 3. Before Every Commit — Safety Checks (MANDATORY)
|
|
62
|
-
|
|
63
|
-
```
|
|
64
|
-
1. git status — verify only expected files changed
|
|
65
|
-
2. git diff --stat — show summary to user
|
|
66
|
-
3. SECRET SCAN — reject if ANY of these are staged:
|
|
67
|
-
- .env files (except .env.example with placeholders)
|
|
68
|
-
- Files containing API keys, tokens, passwords in plaintext
|
|
69
|
-
- Private keys (RSA, SSH, PGP)
|
|
70
|
-
- Database connection strings with credentials
|
|
71
|
-
- Webhook URLs with embedded tokens
|
|
72
|
-
4. Commit with conventional message + story reference
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
**If secrets detected → BLOCK commit, warn user, remove file from staging.**
|
|
76
|
-
|
|
77
|
-
### 4. Before Push — Conflict Prevention (MANDATORY)
|
|
78
|
-
|
|
79
|
-
```
|
|
80
|
-
1. git fetch origin main
|
|
81
|
-
2. git merge origin/main --no-edit (into feature branch)
|
|
82
|
-
3. If conflicts → AGENT resolves them (not the user)
|
|
83
|
-
- For simple conflicts (whitespace, imports): auto-resolve
|
|
84
|
-
- For complex conflicts: show both versions, ask user which to keep
|
|
85
|
-
4. Run tests after merge
|
|
86
|
-
5. Only then: git push origin {branch}
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
### 5. PR Creation — Automatic
|
|
90
|
-
|
|
91
|
-
After push, the agent MUST:
|
|
92
|
-
```
|
|
93
|
-
1. gh pr create with clear title and description (uses PR template)
|
|
94
|
-
2. Auto-assign reviewer based on who is pushing:
|
|
95
|
-
- Maintainer's PR → can merge directly (admin bypass)
|
|
96
|
-
- Collaborator's PR → assign maintainer as reviewer (required approval)
|
|
97
|
-
3. Inform the user: "PR criado"
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
### 6. After PR Merge — Cleanup
|
|
101
|
-
|
|
102
|
-
```
|
|
103
|
-
1. git checkout main
|
|
104
|
-
2. git pull origin main
|
|
105
|
-
3. Delete local feature branch
|
|
106
|
-
4. Inform user: "Branch limpa, pronto para proximo trabalho"
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
## Conflict Resolution Rules
|
|
110
|
-
|
|
111
|
-
| Scenario | Agent Action |
|
|
112
|
-
|----------|-------------|
|
|
113
|
-
| Same file, different sections | Auto-merge (git handles) |
|
|
114
|
-
| Same file, same lines | Show diff, ask user which version to keep |
|
|
115
|
-
| Package.json version conflict | Always take higher version |
|
|
116
|
-
| Generated files (lock, build) | Regenerate after merge |
|
|
117
|
-
| Story/doc files | Merge both contents (additive) |
|
|
118
|
-
|
|
119
|
-
**NEVER use `--force` push. Use `--force-with-lease` ONLY as last resort with user confirmation.**
|
|
120
|
-
|
|
121
|
-
## Communication Protocol
|
|
122
|
-
|
|
123
|
-
When working in parallel, agents MUST inform users about:
|
|
124
|
-
|
|
125
|
-
| Event | Message |
|
|
126
|
-
|-------|---------|
|
|
127
|
-
| Session start | "Atualizando seu projeto... X mudancas novas do {outro}." |
|
|
128
|
-
| Branch created | "Criada area segura para trabalhar: `caio/feat/xxx`" |
|
|
129
|
-
| Pre-push conflict found | "{outro} mudou {file}. Resolvendo automaticamente..." |
|
|
130
|
-
| Secret detected | "BLOQUEADO: encontrei {tipo} em {file}. Removendo antes de salvar." |
|
|
131
|
-
| PR created | "Enviei para revisao. {outro} precisa aprovar no GitHub." |
|
|
132
|
-
| PR merged by other | "{outro} aprovou suas mudancas. Atualizando seu projeto..." |
|
|
133
|
-
|
|
134
|
-
## Destructive Operations — BLOCKED BY DEFAULT
|
|
135
|
-
|
|
136
|
-
These operations require EXPLICIT user confirmation before execution:
|
|
137
|
-
|
|
138
|
-
| Operation | Risk | Confirmation Required |
|
|
139
|
-
|-----------|------|----------------------|
|
|
140
|
-
| `git push --force` / `--force-with-lease` | Overwrite remote history | YES + explain risk |
|
|
141
|
-
| `git reset --hard` | Destroy local uncommitted work | YES + explain risk |
|
|
142
|
-
| `git branch -D` | Delete branch with unmerged commits | YES + explain risk |
|
|
143
|
-
| `git clean -f` | Delete untracked files permanently | YES + explain risk |
|
|
144
|
-
| Delete remote branch | Affects other collaborators | YES |
|
|
12
|
+
Users NEVER resolve conflicts, pick branches, remember to pull, or touch rebase/force-push.
|
|
13
|
+
|
|
14
|
+
## The Law (every session, every commit, every push)
|
|
15
|
+
|
|
16
|
+
1. **Session start — auto-sync (MANDATORY):** `git fetch origin` → sync local with
|
|
17
|
+
origin (fast-forward; if diverged → STOP and resolve safely) → create a work
|
|
18
|
+
branch. **NEVER start work on `main` directly.**
|
|
19
|
+
2. **Auto-branch:** the agent creates and names the branch (`{user}/{type}/{desc}`);
|
|
20
|
+
the user never needs to.
|
|
21
|
+
3. **Before every commit:** `git status` first; **SECRET SCAN — if any secret,
|
|
22
|
+
credential, key, or real `.env` is staged → BLOCK the commit**, warn, unstage.
|
|
23
|
+
4. **Before push:** fetch + merge `origin/main` into the feature branch; the AGENT
|
|
24
|
+
resolves conflicts (simple → auto; complex → show both versions, ask which to
|
|
25
|
+
keep); run tests after merging; only then push.
|
|
26
|
+
5. **After push:** create the PR automatically with reviewer assignment; after
|
|
27
|
+
merge, clean up and re-sync.
|
|
28
|
+
6. **Destructive operations are BLOCKED by default** (`--force`, `reset --hard`,
|
|
29
|
+
`branch -D`, `clean -f`, deleting remote branches): they require EXPLICIT user
|
|
30
|
+
confirmation plus a risk explanation. **NEVER `push --force`**;
|
|
31
|
+
`--force-with-lease` only as a last resort with user confirmation.
|
|
145
32
|
|
|
146
33
|
## Anti-Patterns (FORBIDDEN)
|
|
147
34
|
|
|
148
|
-
- Letting user work on `main` directly
|
|
149
|
-
-
|
|
150
|
-
-
|
|
151
|
-
- Letting conflicts accumulate (merge frequently)
|
|
152
|
-
- Using `git push --force` without explicit user confirmation
|
|
153
|
-
- Assuming the other person isn't working on the same area
|
|
154
|
-
- Committing without checking `git status` first
|
|
35
|
+
- Letting the user work on `main` directly, or pushing to `main` without a PR
|
|
36
|
+
- Skipping `git fetch` at session start; letting conflicts accumulate
|
|
37
|
+
- Committing without checking `git status`; committing secrets or credentials
|
|
155
38
|
- Skipping tests after resolving conflicts
|
|
156
|
-
-
|
|
157
|
-
-
|
|
158
|
-
|
|
159
|
-
## For Projects Using SINAPSE (not just sinapse-ai repo)
|
|
160
|
-
|
|
161
|
-
These same rules apply to ANY project where SINAPSE agents operate:
|
|
162
|
-
1. Auto-branch before work
|
|
163
|
-
2. Auto-sync before starting
|
|
164
|
-
3. Secret scan before every commit
|
|
165
|
-
4. Auto-resolve simple conflicts
|
|
166
|
-
5. Auto-PR with reviewer assignment
|
|
167
|
-
6. User never touches git directly
|
|
168
|
-
|
|
169
|
-
## User Cheat Sheet (the ONLY things users do manually)
|
|
170
|
-
|
|
171
|
-
```
|
|
172
|
-
! git push origin main ← when agent can't push (hook block)
|
|
173
|
-
! npm publish ← when publishing to NPM
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
Everything else: **ask the agent to do it.**
|
|
177
|
-
|
|
178
|
-
## PR Quality Standards
|
|
179
|
-
|
|
180
|
-
| Metric | Target | Warning |
|
|
181
|
-
|--------|--------|---------|
|
|
182
|
-
| PR size | < 200 lines | > 400 lines |
|
|
183
|
-
| PR cycle time | < 1 day | > 3 days |
|
|
39
|
+
- Running destructive git operations without explicit user confirmation
|
|
40
|
+
- Assuming the other person isn't working on the same area
|
|
184
41
|
|
|
185
|
-
**
|
|
42
|
+
> **Detail (loads on file work):** branch naming + user-detection tables, per-phase
|
|
43
|
+
> checklists, conflict-resolution matrix, communication protocol messages, PR
|
|
44
|
+
> quality/DORA targets, user cheat sheet — see `safe-collaboration-reference.md`.
|
|
@@ -87,9 +87,10 @@ const DEFAULT_CONFIG = {
|
|
|
87
87
|
globalTimeout: 45 * 60 * 1000, // 45 minutes
|
|
88
88
|
subtaskTimeout: 10 * 60 * 1000, // 10 minutes per subtask
|
|
89
89
|
|
|
90
|
-
// Epic 10
|
|
91
|
-
|
|
92
|
-
|
|
90
|
+
// NOTE: dead `parallelMode`/`maxParallel` flags lived here (Epic 10) — never
|
|
91
|
+
// read by this orchestrator (waves were measured and rejected; see
|
|
92
|
+
// docs/epics/epic-orchestration-consolidation/KNOWN-LIMITATIONS.md). Removed
|
|
93
|
+
// with `useSubagentDispatch` rationale below (AF-20260704 Lote B).
|
|
93
94
|
// NOTE: a dead `useSubagentDispatch` flag lived here — it was never read.
|
|
94
95
|
// Agent routing happens one level up: epic-4 delegates to this BuildOrchestrator,
|
|
95
96
|
// which builds via executeSubtaskWithClaude. Removed to kill the false affordance
|
|
@@ -262,8 +262,8 @@ class SubagentDispatcher extends EventEmitter {
|
|
|
262
262
|
return this.agentMapping[task.type.toLowerCase()];
|
|
263
263
|
}
|
|
264
264
|
|
|
265
|
-
// F2: if the task names a real agent id directly (any
|
|
266
|
-
//
|
|
265
|
+
// F2: if the task names a real agent id directly (any squad/framework
|
|
266
|
+
// persona known to the resolver), honor it instead of inferring a generic one.
|
|
267
267
|
if (this.agentResolver && task.name && this.agentResolver.has(task.name)) {
|
|
268
268
|
return `@${this.agentResolver.resolve(task.name).id}`;
|
|
269
269
|
}
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* Ideation Engine
|
|
3
3
|
* Story 11.1 - Enhanced Capabilities
|
|
4
4
|
*
|
|
5
|
-
*
|
|
5
|
+
* Heuristic static analysis for codebase improvements (pure-Node scanners: grep,
|
|
6
|
+
* line counts, npm audit, madge — no LLM calls).
|
|
6
7
|
* Suggests optimizations for performance, security, code quality, and UX.
|
|
7
8
|
*/
|
|
8
9
|
|
|
@@ -73,6 +73,14 @@ async function orchestrate(storyId, options = {}) {
|
|
|
73
73
|
console.log(chalk.cyan.bold(` 🚀 SINAPSE Orchestrator: ${storyId}`));
|
|
74
74
|
console.log(chalk.cyan('═══════════════════════════════════════════════════════════\n'));
|
|
75
75
|
|
|
76
|
+
// Measured-scope guard (AF-20260704 Lote B): the help text already states the
|
|
77
|
+
// 1-story-per-run limit, but nothing said it at runtime — where it matters.
|
|
78
|
+
console.log(
|
|
79
|
+
chalk.dim(
|
|
80
|
+
'Scope: 1 story per run — multi-story chaining is not supported (see KNOWN-LIMITATIONS.md).\n',
|
|
81
|
+
),
|
|
82
|
+
);
|
|
83
|
+
|
|
76
84
|
if (options.phaseLimit) {
|
|
77
85
|
console.log(
|
|
78
86
|
chalk.yellow(
|
|
@@ -107,6 +115,11 @@ async function orchestrate(storyId, options = {}) {
|
|
|
107
115
|
// Start from specific epic (AC5)
|
|
108
116
|
if (options.epic) {
|
|
109
117
|
console.log(chalk.yellow(`Starting from Epic ${options.epic}...`));
|
|
118
|
+
console.log(
|
|
119
|
+
chalk.yellow(
|
|
120
|
+
'⚠️ --epic resumes THIS story from the given epic — it does not chain multiple stories.',
|
|
121
|
+
),
|
|
122
|
+
);
|
|
110
123
|
result = await orchestrator.resumeFromEpic(options.epic);
|
|
111
124
|
} else {
|
|
112
125
|
console.log(chalk.green('Starting full pipeline...'));
|
|
@@ -198,6 +198,66 @@ function storyStatusIsReady(file) {
|
|
|
198
198
|
return VALID_STORY_STATUSES.includes(status);
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
+
// ═══════════════════════════════════════════════════════════════════════════════════
|
|
202
|
+
// SPEC SUBSTANCE (AF-20260704 M2)
|
|
203
|
+
// ═══════════════════════════════════════════════════════════════════════════════════
|
|
204
|
+
// A story can be `Ready` yet empty. The status gate alone lets a substanceless
|
|
205
|
+
// story unlock implementation, which violates No Invention (Art. IV). These checks
|
|
206
|
+
// verify the story actually carries the required sections + at least one acceptance
|
|
207
|
+
// criterion, and flag missing traceability as a (non-blocking) concern.
|
|
208
|
+
|
|
209
|
+
const STORY_SECTION_PATTERNS = Object.freeze({
|
|
210
|
+
acceptanceCriteria: /^#{1,4}\s*(acceptance criteria|crit[eé]rios? de aceita)/im,
|
|
211
|
+
scope: /^#{1,4}\s*(scope|escopo)/im,
|
|
212
|
+
description: /^#{1,4}\s*(description|descri[cç][aã]o)/im,
|
|
213
|
+
});
|
|
214
|
+
/** A markdown checkbox item with real content. */
|
|
215
|
+
const AC_CHECKBOX = /^\s*[-*]\s*\[[ xX]\]\s+\S/m;
|
|
216
|
+
/** A Given/When/Then triple (executable AC form). */
|
|
217
|
+
const AC_GWT = /given[\s\S]{0,120}when[\s\S]{0,120}then/i;
|
|
218
|
+
/** Signals that the story traces to evidence/requirement (No Invention). */
|
|
219
|
+
const AC_TRACEABILITY = /(evid[eê]ncia|\bFR-\d|\bNFR-\d|\bCON-\d|\bAC\d|\bepic\b|\bstory\b|audit)/i;
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Inspect a story file for substance (required sections + ≥1 acceptance criterion).
|
|
223
|
+
* Never throws — a read error degrades open (warn-and-proceed), so a filesystem
|
|
224
|
+
* hiccup can never block development (circuit-breaker principle).
|
|
225
|
+
* @param {string} file
|
|
226
|
+
* @returns {{ok:boolean, missingSections:string[], hasAcItem:boolean, hasTraceability:boolean, degraded?:boolean}}
|
|
227
|
+
*/
|
|
228
|
+
function storyHasSubstance(file) {
|
|
229
|
+
let content;
|
|
230
|
+
try {
|
|
231
|
+
content = fs.readFileSync(file, 'utf8');
|
|
232
|
+
} catch {
|
|
233
|
+
return { ok: true, missingSections: [], hasAcItem: true, hasTraceability: true, degraded: true };
|
|
234
|
+
}
|
|
235
|
+
const missingSections = [];
|
|
236
|
+
if (!STORY_SECTION_PATTERNS.description.test(content)) missingSections.push('Descrição/Description');
|
|
237
|
+
if (!STORY_SECTION_PATTERNS.acceptanceCriteria.test(content)) missingSections.push('Acceptance Criteria');
|
|
238
|
+
if (!STORY_SECTION_PATTERNS.scope.test(content)) missingSections.push('Escopo/Scope');
|
|
239
|
+
const hasAcItem = AC_CHECKBOX.test(content) || AC_GWT.test(content);
|
|
240
|
+
const hasTraceability = AC_TRACEABILITY.test(content);
|
|
241
|
+
const ok = missingSections.length === 0 && hasAcItem;
|
|
242
|
+
return { ok, missingSections, hasAcItem, hasTraceability };
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Substance verdict across a project's Ready stories. A project satisfies the
|
|
247
|
+
* substance gate when AT LEAST ONE Ready story is substantive (mirrors how
|
|
248
|
+
* `hasReadyStory` treats "some Ready story exists"). Returns the best verdict.
|
|
249
|
+
* @param {string} projectRoot
|
|
250
|
+
*/
|
|
251
|
+
function readyStorySubstance(projectRoot) {
|
|
252
|
+
const dir = path.join(projectRoot, 'docs', 'stories');
|
|
253
|
+
const readyFiles = walkMarkdown(dir).filter((f) => storyStatusIsReady(f));
|
|
254
|
+
if (readyFiles.length === 0) {
|
|
255
|
+
return { ok: false, missingSections: [], hasAcItem: false, hasTraceability: false, noReadyStory: true };
|
|
256
|
+
}
|
|
257
|
+
const verdicts = readyFiles.map(storyHasSubstance);
|
|
258
|
+
return verdicts.find((v) => v.ok) || verdicts[0];
|
|
259
|
+
}
|
|
260
|
+
|
|
201
261
|
// ═══════════════════════════════════════════════════════════════════════════════════
|
|
202
262
|
// MAIN RESOLVER
|
|
203
263
|
// ═══════════════════════════════════════════════════════════════════════════════════
|
|
@@ -237,17 +297,37 @@ function resolveDocFirstState(opts = {}) {
|
|
|
237
297
|
const prd = hasPrd(projectRoot);
|
|
238
298
|
const epic = hasEpic(projectRoot);
|
|
239
299
|
const readyStory = hasReadyStory(projectRoot);
|
|
300
|
+
// M2: a Ready story must also carry substance (sections + ≥1 AC). Evaluated
|
|
301
|
+
// only when a Ready story exists, so this never changes the "no story" message.
|
|
302
|
+
const substance = readyStory
|
|
303
|
+
? readyStorySubstance(projectRoot)
|
|
304
|
+
: { ok: false, missingSections: [], hasAcItem: false, hasTraceability: false };
|
|
240
305
|
|
|
241
306
|
// Light types ride on an existing project: the minimal gate is "a Ready story"
|
|
242
307
|
// (the existing story-gate). Full projects additionally need PRD + epic.
|
|
243
308
|
const missing = [];
|
|
244
|
-
if (!readyStory)
|
|
309
|
+
if (!readyStory) {
|
|
310
|
+
missing.push('story (status >= Ready)');
|
|
311
|
+
} else if (!substance.ok) {
|
|
312
|
+
// Block: a Ready story without required sections / any AC is not implementable.
|
|
313
|
+
const lack = substance.missingSections.length
|
|
314
|
+
? substance.missingSections.join(', ')
|
|
315
|
+
: 'nenhum acceptance criterion';
|
|
316
|
+
missing.push(`story substance (falta: ${lack})`);
|
|
317
|
+
}
|
|
245
318
|
if (!isLightType) {
|
|
246
319
|
if (!prd) missing.push('PRD (docs/prd.md)');
|
|
247
320
|
if (!epic) missing.push('epic (docs/epics/)');
|
|
248
321
|
}
|
|
249
322
|
const satisfied = missing.length === 0;
|
|
250
323
|
|
|
324
|
+
// Non-blocking concerns (Art. IV No Invention): a substantive story that lacks
|
|
325
|
+
// any traceability signal to evidence/requirement is flagged, not blocked.
|
|
326
|
+
const concerns = [];
|
|
327
|
+
if (readyStory && substance.ok && !substance.hasTraceability) {
|
|
328
|
+
concerns.push('story sem rastreabilidade explícita (Art. IV) — cite evidência/requisito/AC');
|
|
329
|
+
}
|
|
330
|
+
|
|
251
331
|
return {
|
|
252
332
|
brief,
|
|
253
333
|
projectType,
|
|
@@ -255,7 +335,15 @@ function resolveDocFirstState(opts = {}) {
|
|
|
255
335
|
isLightType,
|
|
256
336
|
workflow,
|
|
257
337
|
artifacts,
|
|
258
|
-
gate: {
|
|
338
|
+
gate: {
|
|
339
|
+
prd,
|
|
340
|
+
epic,
|
|
341
|
+
readyStory,
|
|
342
|
+
substance: substance.ok,
|
|
343
|
+
satisfied,
|
|
344
|
+
missing,
|
|
345
|
+
concerns,
|
|
346
|
+
},
|
|
259
347
|
maturity: assessProjectMaturity(projectRoot),
|
|
260
348
|
};
|
|
261
349
|
}
|
|
@@ -315,6 +403,9 @@ module.exports = {
|
|
|
315
403
|
classifyProjectType,
|
|
316
404
|
resolveDocFirstState,
|
|
317
405
|
assessProjectMaturity,
|
|
406
|
+
// M2 (AF-20260704): spec substance checks, exported for reuse + testing.
|
|
407
|
+
storyHasSubstance,
|
|
408
|
+
readyStorySubstance,
|
|
318
409
|
REQUIRED_ARTIFACTS_BY_WORKFLOW,
|
|
319
410
|
TYPE_KEYWORDS,
|
|
320
411
|
// re-exported for callers that want the raw map
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
const fs = require('fs-extra');
|
|
15
15
|
const path = require('path');
|
|
16
16
|
const EpicExecutor = require('./epic-executor');
|
|
17
|
+
const { classifyComplexity } = require('../spec-complexity');
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* Spec Pipeline phases
|
|
@@ -131,14 +132,26 @@ class Epic3Executor extends EpicExecutor {
|
|
|
131
132
|
this._addArtifact('spec', specPath);
|
|
132
133
|
|
|
133
134
|
// Collect complexity and requirements from phases
|
|
134
|
-
const
|
|
135
|
+
const assess = phaseResults['assess-complexity'] || {};
|
|
136
|
+
const complexity = assess.complexity || 'STANDARD';
|
|
135
137
|
const requirements = phaseResults['gather-requirements']?.requirements || [];
|
|
136
138
|
|
|
139
|
+
// M4 (AF-20260704 #9b): turn the COMPLEX≥16 rule from prose into a
|
|
140
|
+
// deterministic classification. Prefer the numeric dimension score when the
|
|
141
|
+
// assess phase provides one; otherwise fall back to the level string.
|
|
142
|
+
const classification = classifyComplexity(
|
|
143
|
+
typeof assess.score === 'number' ? assess.score : assess.dimensions || complexity,
|
|
144
|
+
);
|
|
145
|
+
|
|
137
146
|
// Honesty invariant (epic: orchestration-consolidation, F0a):
|
|
138
147
|
// if the spec was auto-stubbed (no real agent ran), report STUB, not success.
|
|
139
148
|
const specResult = {
|
|
140
149
|
specPath,
|
|
141
|
-
complexity,
|
|
150
|
+
complexity: classification.level,
|
|
151
|
+
complexityScore: classification.score,
|
|
152
|
+
requiresRevisionCycle: classification.requiresRevisionCycle,
|
|
153
|
+
requiresFullSpecPipeline: classification.requiresFullSpecPipeline,
|
|
154
|
+
specPipelinePhases: classification.phases,
|
|
142
155
|
requirements,
|
|
143
156
|
phases: Object.keys(phaseResults),
|
|
144
157
|
};
|
|
@@ -193,12 +193,14 @@ class Epic4Executor extends EpicExecutor {
|
|
|
193
193
|
async _executeViaBuildOrchestrator(storyId, context) {
|
|
194
194
|
try {
|
|
195
195
|
const { BuildOrchestrator } = require('../../execution/build-orchestrator');
|
|
196
|
+
// buildOptions is passed once, via build() — which already merges it over
|
|
197
|
+
// the constructor config ({...this.config, ...options}). Passing it in both
|
|
198
|
+
// places (AF-20260704 Lote B) was redundant, not wrong.
|
|
196
199
|
const builder = new BuildOrchestrator({
|
|
197
200
|
rootPath: this.projectRoot,
|
|
198
201
|
useWorktree: false,
|
|
199
202
|
autoMerge: false,
|
|
200
203
|
runQA: false,
|
|
201
|
-
...(context.buildOptions || {}),
|
|
202
204
|
});
|
|
203
205
|
return await builder.build(storyId, context.buildOptions || {});
|
|
204
206
|
} catch (err) {
|
|
@@ -158,6 +158,8 @@ const {
|
|
|
158
158
|
|
|
159
159
|
module.exports = {
|
|
160
160
|
// Main orchestrators
|
|
161
|
+
// @deprecated WorkflowOrchestrator has no internal consumers (AF-20260704);
|
|
162
|
+
// kept exported because the barrel is public package API. Prefer MasterOrchestrator.
|
|
161
163
|
WorkflowOrchestrator,
|
|
162
164
|
MasterOrchestrator, // Epic 0: ADE Master Orchestrator
|
|
163
165
|
|
|
@@ -290,6 +292,9 @@ module.exports = {
|
|
|
290
292
|
STALE_SNAPSHOT_DAYS,
|
|
291
293
|
|
|
292
294
|
// Story 12.4: Epic Context Accumulator (Projeto Bob)
|
|
295
|
+
// @deprecated EpicContextAccumulator has no internal consumers (AF-20260704;
|
|
296
|
+
// multi-story orchestration was measured and rejected — see KNOWN-LIMITATIONS.md).
|
|
297
|
+
// Kept exported because the barrel is public package API.
|
|
293
298
|
EpicContextAccumulator,
|
|
294
299
|
createEpicContextAccumulator,
|
|
295
300
|
CompressionLevel,
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spec-Pipeline Complexity — deterministic threshold + phase plan.
|
|
3
|
+
*
|
|
4
|
+
* The COMPLEX≥16 rule lived only in prose (`workflow-execution.md` §3,
|
|
5
|
+
* `documentation-first.md` complexity gate): "score ≥16 → COMPLEX → run the full
|
|
6
|
+
* Spec Pipeline (6 phases + revision cycle) FIRST". Nothing in code enforced the
|
|
7
|
+
* threshold or derived the phase plan from it, so the assessment was advisory.
|
|
8
|
+
*
|
|
9
|
+
* This module turns the threshold and the per-level phase plan into deterministic,
|
|
10
|
+
* tested code (Story rodada2-m4, AF-20260704 #9b). It does NOT invent HOW the five
|
|
11
|
+
* dimensions are scored — those values come from the `assess-complexity` phase; it
|
|
12
|
+
* only encodes the mapping score → level → required phases, canonically defined in
|
|
13
|
+
* `workflow-execution.md`.
|
|
14
|
+
*
|
|
15
|
+
* @module core/orchestration/spec-complexity
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
'use strict';
|
|
19
|
+
|
|
20
|
+
/** The five complexity dimensions, each scored 1-5 (min 5, max 25). */
|
|
21
|
+
const DIMENSIONS = Object.freeze(['scope', 'integration', 'infrastructure', 'knowledge', 'risk']);
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Canonical thresholds (`workflow-execution.md` §3):
|
|
25
|
+
* score <= 8 → SIMPLE
|
|
26
|
+
* 9..15 → STANDARD
|
|
27
|
+
* score >= 16 → COMPLEX
|
|
28
|
+
*/
|
|
29
|
+
const THRESHOLDS = Object.freeze({ SIMPLE_MAX: 8, COMPLEX_MIN: 16 });
|
|
30
|
+
|
|
31
|
+
const LEVELS = Object.freeze({ SIMPLE: 'SIMPLE', STANDARD: 'STANDARD', COMPLEX: 'COMPLEX' });
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Phase plans per level (`workflow-execution.md` §3 table):
|
|
35
|
+
* SIMPLE → gather → spec → critique (3)
|
|
36
|
+
* STANDARD → gather → assess → research → spec → critique → plan (6)
|
|
37
|
+
* COMPLEX → the 6 phases + a revision cycle (6 + revision)
|
|
38
|
+
*/
|
|
39
|
+
const PHASES_BY_LEVEL = Object.freeze({
|
|
40
|
+
SIMPLE: Object.freeze(['gather-requirements', 'write-spec', 'critique']),
|
|
41
|
+
STANDARD: Object.freeze([
|
|
42
|
+
'gather-requirements',
|
|
43
|
+
'assess-complexity',
|
|
44
|
+
'research-dependencies',
|
|
45
|
+
'write-spec',
|
|
46
|
+
'critique',
|
|
47
|
+
'plan',
|
|
48
|
+
]),
|
|
49
|
+
COMPLEX: Object.freeze([
|
|
50
|
+
'gather-requirements',
|
|
51
|
+
'assess-complexity',
|
|
52
|
+
'research-dependencies',
|
|
53
|
+
'write-spec',
|
|
54
|
+
'critique',
|
|
55
|
+
'plan',
|
|
56
|
+
]),
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Map a numeric complexity score to its level. Deterministic; no side effects.
|
|
61
|
+
* @param {number} score - Sum of the five dimensions (nominally 5..25).
|
|
62
|
+
* @returns {'SIMPLE'|'STANDARD'|'COMPLEX'}
|
|
63
|
+
*/
|
|
64
|
+
function scoreToLevel(score) {
|
|
65
|
+
const n = Number(score);
|
|
66
|
+
if (!Number.isFinite(n)) return LEVELS.STANDARD; // conservative default
|
|
67
|
+
if (n <= THRESHOLDS.SIMPLE_MAX) return LEVELS.SIMPLE;
|
|
68
|
+
if (n >= THRESHOLDS.COMPLEX_MIN) return LEVELS.COMPLEX;
|
|
69
|
+
return LEVELS.STANDARD;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Normalize a level-ish input (a level string, in any case) to a canonical level. */
|
|
73
|
+
function normalizeLevel(level) {
|
|
74
|
+
const u = String(level || '').trim().toUpperCase();
|
|
75
|
+
return LEVELS[u] || null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Sum an object of dimension → score (1-5). Missing dimensions count as 0.
|
|
80
|
+
* @param {Record<string, number>} dimensions
|
|
81
|
+
* @returns {number}
|
|
82
|
+
*/
|
|
83
|
+
function sumDimensions(dimensions) {
|
|
84
|
+
if (!dimensions || typeof dimensions !== 'object') return NaN;
|
|
85
|
+
return DIMENSIONS.reduce((total, d) => {
|
|
86
|
+
const v = Number(dimensions[d]);
|
|
87
|
+
return total + (Number.isFinite(v) ? v : 0);
|
|
88
|
+
}, 0);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Classify complexity from either a numeric score, a dimensions object, or an
|
|
93
|
+
* already-decided level string. Returns the deterministic plan.
|
|
94
|
+
*
|
|
95
|
+
* @param {number|string|Record<string, number>} input
|
|
96
|
+
* @returns {{
|
|
97
|
+
* level: 'SIMPLE'|'STANDARD'|'COMPLEX',
|
|
98
|
+
* score: number|null,
|
|
99
|
+
* phases: string[],
|
|
100
|
+
* requiresRevisionCycle: boolean,
|
|
101
|
+
* requiresFullSpecPipeline: boolean
|
|
102
|
+
* }}
|
|
103
|
+
*/
|
|
104
|
+
function classifyComplexity(input) {
|
|
105
|
+
let level;
|
|
106
|
+
let score = null;
|
|
107
|
+
|
|
108
|
+
if (typeof input === 'number') {
|
|
109
|
+
score = input;
|
|
110
|
+
level = scoreToLevel(input);
|
|
111
|
+
} else if (typeof input === 'string') {
|
|
112
|
+
level = normalizeLevel(input) || LEVELS.STANDARD;
|
|
113
|
+
} else if (input && typeof input === 'object') {
|
|
114
|
+
const s = sumDimensions(input);
|
|
115
|
+
score = Number.isFinite(s) ? s : null;
|
|
116
|
+
level = Number.isFinite(s) ? scoreToLevel(s) : LEVELS.STANDARD;
|
|
117
|
+
} else {
|
|
118
|
+
level = LEVELS.STANDARD; // conservative default when nothing usable is given
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
level,
|
|
123
|
+
score,
|
|
124
|
+
phases: [...PHASES_BY_LEVEL[level]],
|
|
125
|
+
// COMPLEX is the only level that adds the revision cycle.
|
|
126
|
+
requiresRevisionCycle: level === LEVELS.COMPLEX,
|
|
127
|
+
// SIMPLE runs the trimmed 3-phase pipeline; STANDARD/COMPLEX run the full one.
|
|
128
|
+
requiresFullSpecPipeline: level !== LEVELS.SIMPLE,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
module.exports = {
|
|
133
|
+
DIMENSIONS,
|
|
134
|
+
THRESHOLDS,
|
|
135
|
+
LEVELS,
|
|
136
|
+
PHASES_BY_LEVEL,
|
|
137
|
+
scoreToLevel,
|
|
138
|
+
normalizeLevel,
|
|
139
|
+
sumDimensions,
|
|
140
|
+
classifyComplexity,
|
|
141
|
+
};
|