sinapse-ai 1.20.1 → 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/brownfield-progress.js +219 -0
- package/.sinapse-ai/core/orchestration/cli-commands.js +13 -0
- package/.sinapse-ai/core/orchestration/doc-first-resolver.js +96 -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/greenfield-handler.js +97 -0
- package/.sinapse-ai/core/orchestration/index.js +5 -0
- package/.sinapse-ai/core/orchestration/spec-complexity.js +141 -0
- package/.sinapse-ai/core/orchestration/workflow-executor.js +17 -1
- package/.sinapse-ai/core-config.yaml +30 -0
- package/.sinapse-ai/data/entity-registry.yaml +81 -35
- 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/execute-epic-plan.md +3 -0
- package/.sinapse-ai/development/tasks/resolve-github-issue.md +1 -1
- package/.sinapse-ai/development/workflows/brownfield-discovery.yaml +16 -0
- package/.sinapse-ai/development/workflows/epic-orchestration.yaml +11 -0
- 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 +58 -50
- 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 +55 -10
- 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 +7 -1
- package/scripts/calibrate-judge.js +134 -0
- package/scripts/eval-e2e.js +149 -0
- package/scripts/validate-all.js +8 -0
- package/scripts/validate-article-iv.js +289 -0
- package/scripts/validate-constitution.js +58 -0
- package/scripts/validate-evals.js +9 -2
- package/scripts/validate-story-acs.js +174 -0
- package/scripts/validate-tool-descriptions.js +128 -0
- package/scripts/wave-gate.js +207 -0
- 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
|
@@ -146,7 +146,14 @@ function isPlainObject(v) {
|
|
|
146
146
|
return v !== null && typeof v === 'object' && !Array.isArray(v);
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
-
/**
|
|
149
|
+
/**
|
|
150
|
+
* Suites that follow their OWN contract (not the IDS evals.json/triggers.json
|
|
151
|
+
* one) and have their own runner + jest wrapper. Onda3-S4: epic-gates uses
|
|
152
|
+
* cases.json validated by scripts/eval-e2e.js.
|
|
153
|
+
*/
|
|
154
|
+
const NON_IDS_SUITES = new Set(['epic-gates']);
|
|
155
|
+
|
|
156
|
+
/** Discover gate folders under tests/evals/ (every dir except _lib / _* / non-IDS suites). */
|
|
150
157
|
function discoverGateDirs() {
|
|
151
158
|
let entries;
|
|
152
159
|
try {
|
|
@@ -156,7 +163,7 @@ function discoverGateDirs() {
|
|
|
156
163
|
process.exit(2);
|
|
157
164
|
}
|
|
158
165
|
return entries
|
|
159
|
-
.filter((d) => d.isDirectory() && !d.name.startsWith('_'))
|
|
166
|
+
.filter((d) => d.isDirectory() && !d.name.startsWith('_') && !NON_IDS_SUITES.has(d.name))
|
|
160
167
|
.map((d) => path.join(EVALS_ROOT, d.name))
|
|
161
168
|
.sort();
|
|
162
169
|
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Advisory guard: acceptance criteria in executable Given/When/Then form
|
|
4
|
+
* (Story mesa2-acs-gwt-guard, AF-20260704 Mesa #3).
|
|
5
|
+
*
|
|
6
|
+
* GWT is the documented "preferred" AC format (story-lifecycle.md), but stories
|
|
7
|
+
* historically copy free-form numbered lists from the epic. This guard flags ACs
|
|
8
|
+
* that are NOT in GWT so authors can tighten them — it is advisory by design:
|
|
9
|
+
*
|
|
10
|
+
* - Exit 0 always (WARN-only), mirroring validate-article-iv, UNLESS
|
|
11
|
+
* STORY_ACS_STRICT=1 is set and at least one non-GWT AC is found.
|
|
12
|
+
* - `docs/stories/` is gitignored (local, unversioned), so in CI there are
|
|
13
|
+
* typically no story files — the guard degrades gracefully to a PASS with
|
|
14
|
+
* "nothing to lint" instead of failing.
|
|
15
|
+
*
|
|
16
|
+
* The core is a pure function (`lintStoryAcs`) so tests can drive it with
|
|
17
|
+
* fixtures; the CLI wrapper walks the default story dir.
|
|
18
|
+
*
|
|
19
|
+
* @module scripts/validate-story-acs
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
'use strict';
|
|
23
|
+
|
|
24
|
+
const fs = require('fs');
|
|
25
|
+
const path = require('path');
|
|
26
|
+
|
|
27
|
+
const ROOT = process.cwd();
|
|
28
|
+
|
|
29
|
+
/** Default lint target: local story files (gitignored — may be absent in CI). */
|
|
30
|
+
const DEFAULT_STORIES_DIR = 'docs/stories';
|
|
31
|
+
|
|
32
|
+
/** Heading that opens the acceptance-criteria section (case-insensitive). */
|
|
33
|
+
const AC_HEADING = /^#{1,6}\s+acceptance\s+criteria\b/i;
|
|
34
|
+
|
|
35
|
+
/** Any markdown heading — closes the AC section. */
|
|
36
|
+
const ANY_HEADING = /^#{1,6}\s+/;
|
|
37
|
+
|
|
38
|
+
/** A list item (bullet `-`/`*` with optional checkbox, or `N.` numbered). */
|
|
39
|
+
const LIST_ITEM = /^\s*(?:[-*]\s+(?:\[[ xX]\]\s+)?|\d+[.)]\s+)(.+)$/;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* True when a single AC's text is in executable Given/When/Then form — it must
|
|
43
|
+
* mention all three keywords (order-agnostic, case-insensitive).
|
|
44
|
+
*
|
|
45
|
+
* @param {string} text
|
|
46
|
+
* @returns {boolean}
|
|
47
|
+
*/
|
|
48
|
+
function isGwt(text) {
|
|
49
|
+
const t = text.toLowerCase();
|
|
50
|
+
return /\bgiven\b/.test(t) && /\bwhen\b/.test(t) && /\bthen\b/.test(t);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Extract the acceptance-criteria list items from a story's markdown body and
|
|
55
|
+
* classify each as GWT or not.
|
|
56
|
+
*
|
|
57
|
+
* @param {string} content - full markdown of a story file
|
|
58
|
+
* @returns {{ total: number, gwt: number, nonGwt: Array<{ text: string }> }}
|
|
59
|
+
*/
|
|
60
|
+
function lintStoryAcs(content) {
|
|
61
|
+
const lines = String(content).split(/\r?\n/);
|
|
62
|
+
const items = [];
|
|
63
|
+
|
|
64
|
+
let inSection = false;
|
|
65
|
+
let current = null; // accumulates a multi-line AC item
|
|
66
|
+
|
|
67
|
+
const flush = () => {
|
|
68
|
+
if (current !== null) {
|
|
69
|
+
items.push(current.trim());
|
|
70
|
+
current = null;
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
for (const line of lines) {
|
|
75
|
+
if (!inSection) {
|
|
76
|
+
if (AC_HEADING.test(line)) inSection = true;
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
// A new heading ends the AC section.
|
|
80
|
+
if (ANY_HEADING.test(line)) {
|
|
81
|
+
flush();
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
const m = line.match(LIST_ITEM);
|
|
85
|
+
if (m) {
|
|
86
|
+
flush();
|
|
87
|
+
current = m[1];
|
|
88
|
+
} else if (current !== null && line.trim() !== '') {
|
|
89
|
+
// continuation line of the current AC item
|
|
90
|
+
current += ' ' + line.trim();
|
|
91
|
+
} else if (current !== null && line.trim() === '') {
|
|
92
|
+
flush();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
flush();
|
|
96
|
+
|
|
97
|
+
const nonGwt = items.filter((t) => !isGwt(t)).map((text) => ({ text }));
|
|
98
|
+
return { total: items.length, gwt: items.length - nonGwt.length, nonGwt };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Recursively collect `.md` files under a directory. */
|
|
102
|
+
function walkMarkdown(dir, out = []) {
|
|
103
|
+
let entries;
|
|
104
|
+
try {
|
|
105
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
106
|
+
} catch {
|
|
107
|
+
return out;
|
|
108
|
+
}
|
|
109
|
+
for (const e of entries) {
|
|
110
|
+
const p = path.join(dir, e.name);
|
|
111
|
+
if (e.isDirectory()) walkMarkdown(p, out);
|
|
112
|
+
else if (e.name.endsWith('.md')) out.push(p);
|
|
113
|
+
}
|
|
114
|
+
return out;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** CLI entry point. */
|
|
118
|
+
function main() {
|
|
119
|
+
const strict = process.env.STORY_ACS_STRICT === '1';
|
|
120
|
+
const dir = path.join(ROOT, DEFAULT_STORIES_DIR);
|
|
121
|
+
const files = walkMarkdown(dir);
|
|
122
|
+
|
|
123
|
+
if (files.length === 0) {
|
|
124
|
+
console.log(
|
|
125
|
+
`story-acs: no story files under ${DEFAULT_STORIES_DIR}/ (gitignored) — nothing to lint. PASS.`,
|
|
126
|
+
);
|
|
127
|
+
process.exit(0);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
let totalAcs = 0;
|
|
131
|
+
let totalNonGwt = 0;
|
|
132
|
+
const report = [];
|
|
133
|
+
|
|
134
|
+
for (const file of files) {
|
|
135
|
+
let content;
|
|
136
|
+
try {
|
|
137
|
+
content = fs.readFileSync(file, 'utf8');
|
|
138
|
+
} catch {
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
const { total, nonGwt } = lintStoryAcs(content);
|
|
142
|
+
if (total === 0) continue; // not an AC-bearing story
|
|
143
|
+
totalAcs += total;
|
|
144
|
+
if (nonGwt.length > 0) {
|
|
145
|
+
totalNonGwt += nonGwt.length;
|
|
146
|
+
report.push({ file: path.relative(ROOT, file), nonGwt });
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (totalNonGwt === 0) {
|
|
151
|
+
console.log(`story-acs: ${totalAcs} AC(s) across ${files.length} file(s), all GWT-shaped. PASS.`);
|
|
152
|
+
process.exit(0);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const level = strict ? 'FAIL' : 'WARN';
|
|
156
|
+
console.log(`story-acs [${level}]: ${totalNonGwt}/${totalAcs} AC(s) are not in Given/When/Then form:`);
|
|
157
|
+
for (const r of report) {
|
|
158
|
+
console.log(` ${r.file}`);
|
|
159
|
+
for (const { text } of r.nonGwt) {
|
|
160
|
+
const preview = text.length > 90 ? text.slice(0, 87) + '...' : text;
|
|
161
|
+
console.log(` - ${preview}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
console.log(
|
|
165
|
+
strict
|
|
166
|
+
? '\nSTORY_ACS_STRICT=1 → failing. Rewrite the ACs above as "Given <ctx>, When <action>, Then <outcome>."'
|
|
167
|
+
: '\nAdvisory only (exit 0). Prefer "Given <ctx>, When <action>, Then <outcome>." Set STORY_ACS_STRICT=1 to enforce.',
|
|
168
|
+
);
|
|
169
|
+
process.exit(strict ? 1 : 0);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (require.main === module) main();
|
|
173
|
+
|
|
174
|
+
module.exports = { lintStoryAcs, isGwt };
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Lint guard: tool/command descriptions (Story M2-mesa M3, AF-20260704 #9a).
|
|
4
|
+
*
|
|
5
|
+
* A command an agent exposes IS a tool, and its `description:` is the contract
|
|
6
|
+
* that drives selection (KIT-ai-engineering, Tool Use). A vague, empty, or
|
|
7
|
+
* placeholder description silently degrades routing. This guard enforces a
|
|
8
|
+
* minimal clarity contract on every `description:` field in the agent
|
|
9
|
+
* definitions, and is wired into `validate:all`.
|
|
10
|
+
*
|
|
11
|
+
* Exit 0 = all descriptions valid · Exit 1 = one or more violate the contract.
|
|
12
|
+
*
|
|
13
|
+
* The core is a pure function (`lintDescriptions`) so tests can drive it with
|
|
14
|
+
* fixtures; the CLI wrapper globs the default targets.
|
|
15
|
+
*
|
|
16
|
+
* @module scripts/validate-tool-descriptions
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
'use strict';
|
|
20
|
+
|
|
21
|
+
const fs = require('fs');
|
|
22
|
+
const path = require('path');
|
|
23
|
+
|
|
24
|
+
const ROOT = process.cwd();
|
|
25
|
+
|
|
26
|
+
/** Default lint targets: the framework agent definitions (source of truth). */
|
|
27
|
+
const DEFAULT_TARGET_DIRS = ['.sinapse-ai/development/agents'];
|
|
28
|
+
|
|
29
|
+
/** Minimum characters for a meaningful description (smallest real one is 12). */
|
|
30
|
+
const MIN_LEN = 8;
|
|
31
|
+
|
|
32
|
+
/** Values that are placeholders, not real descriptions. */
|
|
33
|
+
const PLACEHOLDER = /^(todo|tbd|fixme|x{2,}|placeholder|custom|n\/?a|desc|description|\.\.\.|-+|\?+)$/i;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* The WHOLE value is nothing but a template token — e.g. `{description}` or
|
|
37
|
+
* `<desc>`. Inline braces documenting command args (e.g. "Build story
|
|
38
|
+
* (*build {story-id})") are legitimate and must NOT be flagged.
|
|
39
|
+
*/
|
|
40
|
+
const TEMPLATE_ONLY = /^[{<][^}>]*[}>]$/;
|
|
41
|
+
|
|
42
|
+
/** A YAML block-scalar indicator — the real text follows on indented lines. */
|
|
43
|
+
const BLOCK_SCALAR = /^[|>][+-]?\d*$/;
|
|
44
|
+
|
|
45
|
+
/** Recursively collect `.md` files under a directory. */
|
|
46
|
+
function walkMarkdown(dir, out = []) {
|
|
47
|
+
let entries;
|
|
48
|
+
try {
|
|
49
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
50
|
+
} catch {
|
|
51
|
+
return out;
|
|
52
|
+
}
|
|
53
|
+
for (const e of entries) {
|
|
54
|
+
const p = path.join(dir, e.name);
|
|
55
|
+
if (e.isDirectory()) walkMarkdown(p, out);
|
|
56
|
+
else if (e.name.toLowerCase().endsWith('.md')) out.push(p);
|
|
57
|
+
}
|
|
58
|
+
return out;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Strip a single layer of matching quotes from a YAML scalar. */
|
|
62
|
+
function unquote(s) {
|
|
63
|
+
const t = s.trim();
|
|
64
|
+
if ((t.startsWith("'") && t.endsWith("'")) || (t.startsWith('"') && t.endsWith('"'))) {
|
|
65
|
+
return t.slice(1, -1).trim();
|
|
66
|
+
}
|
|
67
|
+
return t;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Lint `description:` fields in the given files.
|
|
72
|
+
* @param {string[]} files
|
|
73
|
+
* @returns {Array<{file:string, line:number, value:string, reason:string}>}
|
|
74
|
+
*/
|
|
75
|
+
function lintDescriptions(files) {
|
|
76
|
+
const violations = [];
|
|
77
|
+
for (const file of files) {
|
|
78
|
+
let content;
|
|
79
|
+
try {
|
|
80
|
+
content = fs.readFileSync(file, 'utf8');
|
|
81
|
+
} catch {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const lines = content.split(/\r?\n/);
|
|
85
|
+
lines.forEach((raw, i) => {
|
|
86
|
+
const m = raw.match(/^\s*description:\s*(.*)$/);
|
|
87
|
+
if (!m) return;
|
|
88
|
+
const rawValue = m[1].trim();
|
|
89
|
+
// Block scalars (`description: |`) carry the text on the following indented
|
|
90
|
+
// lines — treat as valid rather than flag the indicator itself.
|
|
91
|
+
if (BLOCK_SCALAR.test(rawValue)) return;
|
|
92
|
+
const value = unquote(rawValue);
|
|
93
|
+
let reason = null;
|
|
94
|
+
if (value === '') reason = 'descrição vazia';
|
|
95
|
+
else if (PLACEHOLDER.test(value)) reason = `placeholder ("${value}")`;
|
|
96
|
+
else if (TEMPLATE_ONLY.test(value)) reason = `template não preenchido ("${value}")`;
|
|
97
|
+
else if (value.length < MIN_LEN) reason = `curta demais (<${MIN_LEN} chars): "${value}"`;
|
|
98
|
+
if (reason) {
|
|
99
|
+
violations.push({ file: path.relative(ROOT, file), line: i + 1, value, reason });
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
return violations;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Resolve the files to lint: explicit args, or the default target dirs. */
|
|
107
|
+
function collectFiles(args) {
|
|
108
|
+
if (args.length) return args;
|
|
109
|
+
return DEFAULT_TARGET_DIRS.flatMap((d) => walkMarkdown(path.join(ROOT, d)));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function main() {
|
|
113
|
+
const files = collectFiles(process.argv.slice(2));
|
|
114
|
+
const violations = lintDescriptions(files);
|
|
115
|
+
if (violations.length === 0) {
|
|
116
|
+
console.log(`OK — tool/command descriptions valid (${files.length} files scanned).`);
|
|
117
|
+
process.exit(0);
|
|
118
|
+
}
|
|
119
|
+
console.error(`FAIL — ${violations.length} description(s) violate the clarity contract:`);
|
|
120
|
+
for (const v of violations) {
|
|
121
|
+
console.error(` ${v.file}:${v.line} — ${v.reason}`);
|
|
122
|
+
}
|
|
123
|
+
process.exit(1);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (require.main === module) main();
|
|
127
|
+
|
|
128
|
+
module.exports = { lintDescriptions, unquote, walkMarkdown, MIN_LEN };
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* wave-gate.js — deterministic wave gate for harness-based epic waves.
|
|
5
|
+
*
|
|
6
|
+
* Story onda3-s5-epic-waves-wrapper-pilot (AF-20260702 item 3.5).
|
|
7
|
+
*
|
|
8
|
+
* The HÍBRIDO verdict (2026-06-30) killed the homegrown multi-story engine.
|
|
9
|
+
* The wave path that remains viable is a THIN WRAPPER over the harness:
|
|
10
|
+
* fan-out per story into isolated workdirs/worktrees, then THIS gate decides
|
|
11
|
+
* whether the wave may advance — by measurement, not by claim:
|
|
12
|
+
*
|
|
13
|
+
* 1. TESTS GREEN: the story's test command exits 0 (default `node --test`).
|
|
14
|
+
* 2. FILES ACTUALLY WRITTEN: the story dir contains real product files
|
|
15
|
+
* (excluding plan/, docs/, node_modules, dotfiles and markdown) — the
|
|
16
|
+
* echo of the empty-build honesty fix: a wave never advances on a story
|
|
17
|
+
* that produced zero code.
|
|
18
|
+
*
|
|
19
|
+
* Verdict per story and for the wave: APPROVED / NEEDS_WORK (exit 0 / 1).
|
|
20
|
+
*
|
|
21
|
+
* Usage:
|
|
22
|
+
* node scripts/wave-gate.js --stories <dir1> <dir2> [...] \
|
|
23
|
+
* [--test-cmd "node --test"] [--json]
|
|
24
|
+
*
|
|
25
|
+
* Adoption status: utility + pilot instrument. Whether the wave wrapper
|
|
26
|
+
* becomes product is decided EXCLUSIVELY by the pre-registered measurement
|
|
27
|
+
* (tests/evals/epic-gates/PROTOCOL.md) — see the checkpoint in
|
|
28
|
+
* docs/epics/epic-onda3-estrutural/.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
'use strict';
|
|
32
|
+
|
|
33
|
+
const fs = require('fs');
|
|
34
|
+
const path = require('path');
|
|
35
|
+
const { execSync } = require('child_process');
|
|
36
|
+
|
|
37
|
+
const DEFAULT_TEST_CMD = 'node --test';
|
|
38
|
+
|
|
39
|
+
/** Directory/file names that never count as written product files. */
|
|
40
|
+
const EXCLUDED_DIRS = new Set(['node_modules', '.git', 'plan', 'docs', '.sinapse']);
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Recursively list product files written in a story dir.
|
|
44
|
+
* Markdown and dotfiles are not product; plan/ and docs/ are artifacts of
|
|
45
|
+
* planning, not implementation (a plan is not an implementation).
|
|
46
|
+
*
|
|
47
|
+
* @param {string} dir
|
|
48
|
+
* @param {number} [depth]
|
|
49
|
+
* @returns {string[]} Relative paths of product files
|
|
50
|
+
*/
|
|
51
|
+
function scanWrittenFiles(dir, depth = 0) {
|
|
52
|
+
if (depth > 6) return [];
|
|
53
|
+
let entries;
|
|
54
|
+
try {
|
|
55
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
56
|
+
} catch {
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
const out = [];
|
|
60
|
+
for (const entry of entries) {
|
|
61
|
+
if (entry.name.startsWith('.')) continue;
|
|
62
|
+
if (entry.isDirectory()) {
|
|
63
|
+
if (EXCLUDED_DIRS.has(entry.name)) continue;
|
|
64
|
+
out.push(
|
|
65
|
+
...scanWrittenFiles(path.join(dir, entry.name), depth + 1).map((f) =>
|
|
66
|
+
path.join(entry.name, f),
|
|
67
|
+
),
|
|
68
|
+
);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (entry.name.endsWith('.md')) continue;
|
|
72
|
+
out.push(entry.name);
|
|
73
|
+
}
|
|
74
|
+
return out;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Parse a `node --test` (TAP-ish) summary out of test output. Best effort —
|
|
79
|
+
* the EXIT CODE is the source of truth; counts are telemetry.
|
|
80
|
+
*
|
|
81
|
+
* @param {string} output
|
|
82
|
+
* @returns {{pass: ?number, fail: ?number}}
|
|
83
|
+
*/
|
|
84
|
+
function parseNodeTestSummary(output) {
|
|
85
|
+
const grab = (re) => {
|
|
86
|
+
const m = String(output).match(re);
|
|
87
|
+
return m ? parseInt(m[1], 10) : null;
|
|
88
|
+
};
|
|
89
|
+
return {
|
|
90
|
+
pass: grab(/^#\s*pass\s+(\d+)/im),
|
|
91
|
+
fail: grab(/^#\s*fail\s+(\d+)/im),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Evaluate one story dir: run its tests and verify real files were written.
|
|
97
|
+
*
|
|
98
|
+
* @param {string} storyDir
|
|
99
|
+
* @param {{testCmd?: string}} [options]
|
|
100
|
+
* @returns {{story: string, verdict: string, filesWritten: number, files: string[], tests: {exitCode: ?number, pass: ?number, fail: ?number, skipped?: boolean}, reasons: string[]}}
|
|
101
|
+
*/
|
|
102
|
+
function evaluateStory(storyDir, options = {}) {
|
|
103
|
+
const testCmd = options.testCmd || DEFAULT_TEST_CMD;
|
|
104
|
+
const reasons = [];
|
|
105
|
+
|
|
106
|
+
const files = scanWrittenFiles(storyDir);
|
|
107
|
+
if (files.length === 0) {
|
|
108
|
+
reasons.push('zero product files written (a plan is not an implementation)');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
let tests = { exitCode: null, pass: null, fail: null };
|
|
112
|
+
if (files.length === 0) {
|
|
113
|
+
// Nothing to test — do not mask the real failure with a confusing test error.
|
|
114
|
+
tests = { exitCode: null, pass: null, fail: null, skipped: true };
|
|
115
|
+
} else {
|
|
116
|
+
try {
|
|
117
|
+
const output = execSync(testCmd, {
|
|
118
|
+
cwd: storyDir,
|
|
119
|
+
encoding: 'utf8',
|
|
120
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
121
|
+
timeout: 120000,
|
|
122
|
+
});
|
|
123
|
+
tests = { exitCode: 0, ...parseNodeTestSummary(output) };
|
|
124
|
+
} catch (error) {
|
|
125
|
+
const output = `${error.stdout || ''}\n${error.stderr || ''}`;
|
|
126
|
+
tests = {
|
|
127
|
+
exitCode: typeof error.status === 'number' ? error.status : 1,
|
|
128
|
+
...parseNodeTestSummary(output),
|
|
129
|
+
};
|
|
130
|
+
reasons.push(`tests failed (exit ${tests.exitCode}${tests.fail != null ? `, ${tests.fail} failing` : ''})`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
story: storyDir,
|
|
136
|
+
verdict: reasons.length === 0 ? 'APPROVED' : 'NEEDS_WORK',
|
|
137
|
+
filesWritten: files.length,
|
|
138
|
+
files,
|
|
139
|
+
tests,
|
|
140
|
+
reasons,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Evaluate a whole wave (list of story dirs).
|
|
146
|
+
*
|
|
147
|
+
* @param {string[]} storyDirs
|
|
148
|
+
* @param {{testCmd?: string}} [options]
|
|
149
|
+
* @returns {{verdict: string, stories: Array}}
|
|
150
|
+
*/
|
|
151
|
+
function evaluateWave(storyDirs, options = {}) {
|
|
152
|
+
const stories = storyDirs.map((dir) => evaluateStory(dir, options));
|
|
153
|
+
return {
|
|
154
|
+
verdict: stories.every((s) => s.verdict === 'APPROVED') ? 'APPROVED' : 'NEEDS_WORK',
|
|
155
|
+
stories,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function main() {
|
|
160
|
+
const argv = process.argv.slice(2);
|
|
161
|
+
const json = argv.includes('--json');
|
|
162
|
+
|
|
163
|
+
const testCmdIdx = argv.indexOf('--test-cmd');
|
|
164
|
+
const testCmd = testCmdIdx !== -1 ? argv[testCmdIdx + 1] : DEFAULT_TEST_CMD;
|
|
165
|
+
|
|
166
|
+
const storiesIdx = argv.indexOf('--stories');
|
|
167
|
+
if (storiesIdx === -1) {
|
|
168
|
+
console.error('Usage: node scripts/wave-gate.js --stories <dir1> [dir2 ...] [--test-cmd "..."] [--json]');
|
|
169
|
+
process.exit(2);
|
|
170
|
+
}
|
|
171
|
+
const storyDirs = [];
|
|
172
|
+
for (let i = storiesIdx + 1; i < argv.length && !argv[i].startsWith('--'); i += 1) {
|
|
173
|
+
storyDirs.push(path.resolve(argv[i]));
|
|
174
|
+
}
|
|
175
|
+
if (storyDirs.length === 0) {
|
|
176
|
+
console.error('wave-gate: --stories requires at least one directory.');
|
|
177
|
+
process.exit(2);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const wave = evaluateWave(storyDirs, { testCmd });
|
|
181
|
+
|
|
182
|
+
if (json) {
|
|
183
|
+
console.log(JSON.stringify(wave, null, 2));
|
|
184
|
+
} else {
|
|
185
|
+
console.log(`=== wave-gate (${storyDirs.length} story(ies)) ===`);
|
|
186
|
+
for (const s of wave.stories) {
|
|
187
|
+
const testInfo = s.tests.skipped
|
|
188
|
+
? 'tests skipped (nothing to test)'
|
|
189
|
+
: `tests exit ${s.tests.exitCode}${s.tests.pass != null ? ` (${s.tests.pass} pass/${s.tests.fail} fail)` : ''}`;
|
|
190
|
+
console.log(` ${s.verdict.padEnd(10)} ${path.basename(s.story)} — ${s.filesWritten} file(s), ${testInfo}`);
|
|
191
|
+
for (const r of s.reasons) console.log(` - ${r}`);
|
|
192
|
+
}
|
|
193
|
+
console.log('');
|
|
194
|
+
console.log(`wave verdict: ${wave.verdict}`);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
process.exit(wave.verdict === 'APPROVED' ? 0 : 1);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
module.exports = {
|
|
201
|
+
scanWrittenFiles,
|
|
202
|
+
parseNodeTestSummary,
|
|
203
|
+
evaluateStory,
|
|
204
|
+
evaluateWave,
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
if (require.main === module) main();
|
|
@@ -651,6 +651,9 @@ dependencies:
|
|
|
651
651
|
|
|
652
652
|
coderabbit_integration:
|
|
653
653
|
enabled: true
|
|
654
|
+
# CodeRabbit mechanics (WSL execution, timeout, commands, report location) are
|
|
655
|
+
# single-sourced in .sinapse-ai/core-config.yaml + .claude/rules/coderabbit-integration.md.
|
|
656
|
+
# Only this agent's review focus/policy lives here — see Story rodada2-m6.
|
|
654
657
|
focus: Integration patterns, configuration consistency, CLAUDE.md quality, hook coverage
|
|
655
658
|
|
|
656
659
|
when_to_use:
|
|
@@ -659,16 +662,6 @@ dependencies:
|
|
|
659
662
|
- Validating CI/CD workflow configurations
|
|
660
663
|
- Checking settings.json deny/allow rules
|
|
661
664
|
|
|
662
|
-
execution_guidelines: |
|
|
663
|
-
CRITICAL: CodeRabbit CLI is installed in WSL, not Windows.
|
|
664
|
-
|
|
665
|
-
**How to Execute:**
|
|
666
|
-
1. Use 'wsl bash -c' wrapper for all commands
|
|
667
|
-
2. Navigate to project directory in WSL path format (/mnt/c/...)
|
|
668
|
-
3. Use full path to coderabbit binary (~/.local/bin/coderabbit)
|
|
669
|
-
|
|
670
|
-
**Timeout:** 15 minutes (900000ms) - CodeRabbit reviews take 7-30 min
|
|
671
|
-
|
|
672
665
|
# =========================================================================
|
|
673
666
|
# INTEGRATION ALGORITHM
|
|
674
667
|
# =========================================================================
|
|
@@ -19,7 +19,7 @@ The context window is the scarcest resource in agentic systems. Every token has
|
|
|
19
19
|
### Claude Code Session Anatomy
|
|
20
20
|
|
|
21
21
|
```
|
|
22
|
-
TOTAL WINDOW: ~200,000 tokens (
|
|
22
|
+
TOTAL WINDOW: ~200,000 tokens (example — current frontier windows reach 1M; see .claude/rules/token-economy.md)
|
|
23
23
|
├── System Prompt (static): ~2,000 tokens (1%) [CACHED]
|
|
24
24
|
├── CLAUDE.md hierarchy: 1-10,000 tokens (0.5-5%) [CACHED]
|
|
25
25
|
├── Rules files (loaded): 500-5,000 tokens (0.25-2.5%) [CACHED]
|
|
@@ -253,7 +253,7 @@ Parent system prompt (cached):
|
|
|
253
253
|
For SINAPSE agents, recommended allocation:
|
|
254
254
|
|
|
255
255
|
```
|
|
256
|
-
200K
|
|
256
|
+
200K-window session (example — current frontier windows reach 1M):
|
|
257
257
|
Agent persona (CLAUDE.md + rules): ~5K (2.5%)
|
|
258
258
|
HOT memory (current task): ~20K (10%)
|
|
259
259
|
WARM memory (retrieved context): ~30K (15%)
|
|
@@ -43,7 +43,7 @@ The context window is the scarcest resource in agentic systems. A well-designed
|
|
|
43
43
|
### Token Budget Management
|
|
44
44
|
|
|
45
45
|
```
|
|
46
|
-
TOTAL WINDOW: 200,000 tokens (example)
|
|
46
|
+
TOTAL WINDOW: 200,000 tokens (example — current frontier windows reach 1M)
|
|
47
47
|
|- System Prompt: ~2,000 tokens (1%)
|
|
48
48
|
|- Agent Persona: ~1,500 tokens (0.75%)
|
|
49
49
|
|- Memory Context: ~50,000 tokens (25%) ← MANAGED
|
|
@@ -195,7 +195,7 @@ Every AI-assisted copy deliverable MUST record:
|
|
|
195
195
|
|
|
196
196
|
1. **Gate used** — which of the 4 gates (Trust / Light / Heavy / Scrap)
|
|
197
197
|
2. **Prompt template(s) used** — from the library in §6
|
|
198
|
-
3. **Model used** — GPT-5, Claude Opus
|
|
198
|
+
3. **Model used** — e.g. GPT-5, Claude (Opus/Fable family), Gemini 2.5 Pro
|
|
199
199
|
4. **Final edit depth** — rough % AI vs human in the shipped version
|
|
200
200
|
5. **Conversion outcome** (if tracked) — linked to the attribution system
|
|
201
201
|
|