@gcunharodrigues/wrxn 0.9.0 → 0.10.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/bin/wrxn.cjs +145 -0
- package/lib/agent-conformance.cjs +92 -0
- package/lib/compass-coverage.cjs +53 -0
- package/lib/flow-status.cjs +87 -0
- package/manifest.json +35 -0
- package/package.json +1 -1
- package/payload/.claude/agents/builder.md +58 -0
- package/payload/.claude/agents/devops.md +60 -0
- package/payload/.claude/agents/qa-walker.md +57 -0
- package/payload/.claude/agents/researcher.md +55 -0
- package/payload/.claude/agents/reviewer.md +59 -0
- package/payload/.claude/agents/security.md +58 -0
- package/payload/.claude/skills/compass/SKILL.md +96 -0
- package/payload/.claude/skills/qa-walk/SKILL.md +33 -0
- package/payload/.synapse/manifest +3 -1
- package/payload/.synapse/pipeline +6 -6
package/bin/wrxn.cjs
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
const fs = require('fs');
|
|
5
5
|
const path = require('path');
|
|
6
6
|
const os = require('os');
|
|
7
|
+
const { execFileSync } = require('child_process');
|
|
7
8
|
|
|
8
9
|
const { init } = require('../lib/install.cjs');
|
|
9
10
|
const { update } = require('../lib/update.cjs');
|
|
@@ -15,6 +16,7 @@ const brain = require('../lib/brain.cjs');
|
|
|
15
16
|
const statusline = require('../lib/statusline.cjs');
|
|
16
17
|
const { convert } = require('../lib/convert.cjs');
|
|
17
18
|
const { ingest } = require('../lib/ingest.cjs');
|
|
19
|
+
const { flowStatus } = require('../lib/flow-status.cjs');
|
|
18
20
|
|
|
19
21
|
const PKG_ROOT = path.join(__dirname, '..');
|
|
20
22
|
|
|
@@ -23,6 +25,12 @@ function version() {
|
|
|
23
25
|
return pkg.version;
|
|
24
26
|
}
|
|
25
27
|
|
|
28
|
+
// Escape a dynamic fragment before embedding it in a RegExp. Slice ids derive from the prd slug and
|
|
29
|
+
// issue filenames, so an unescaped metachar would otherwise build an invalid pattern (crash / ReDoS).
|
|
30
|
+
function escapeRegExp(s) {
|
|
31
|
+
return String(s).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
32
|
+
}
|
|
33
|
+
|
|
26
34
|
function parseArgs(argv) {
|
|
27
35
|
const args = { _: [], flags: {} };
|
|
28
36
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -150,6 +158,14 @@ Usage:
|
|
|
150
158
|
aios-intake.md (the deterministic half of the onboard skill;
|
|
151
159
|
workspace installs only). Idempotent.
|
|
152
160
|
|
|
161
|
+
wrxn flow status <prd> [--root <dir>]
|
|
162
|
+
print the flow-status board for a PRD's issue set. Reads issues
|
|
163
|
+
from .scratch/<prd>/issues/, detects gate artifacts (green commit
|
|
164
|
+
via git log, review-<id>.md, security-<id>.md, walk-findings-<id>.md),
|
|
165
|
+
and prints per-slice gate progress (build/review/sec/qa) plus state
|
|
166
|
+
(done | in-progress | stalled | queued). No separate state store —
|
|
167
|
+
derives from the durable artifacts only.
|
|
168
|
+
|
|
153
169
|
Profiles: --project (default, the dev pipeline + intelligence + enforcement) |
|
|
154
170
|
--workspace (adds the operator layer: onboard/audit/level-up + intake + decisions log +
|
|
155
171
|
connections registry).`;
|
|
@@ -490,6 +506,135 @@ async function main(argv) {
|
|
|
490
506
|
return 0;
|
|
491
507
|
}
|
|
492
508
|
|
|
509
|
+
if (cmd === 'flow') {
|
|
510
|
+
const sub = args._[1];
|
|
511
|
+
if (sub === 'status') {
|
|
512
|
+
const prd = args._[2];
|
|
513
|
+
if (!prd) {
|
|
514
|
+
process.stderr.write('wrxn: flow status requires <prd>\n');
|
|
515
|
+
return 2;
|
|
516
|
+
}
|
|
517
|
+
const root = path.resolve(args.flags.root || process.cwd());
|
|
518
|
+
// S2: prd flows into a filesystem path — keep it a plain dir name contained within
|
|
519
|
+
// <root>/.scratch/. Reject "..", absolute paths, and anything that resolves outside .scratch/.
|
|
520
|
+
const scratchRoot = path.join(root, '.scratch');
|
|
521
|
+
const issuesDir = path.join(scratchRoot, prd, 'issues');
|
|
522
|
+
if (prd.includes('..') || path.isAbsolute(prd)
|
|
523
|
+
|| !path.resolve(issuesDir).startsWith(path.resolve(scratchRoot) + path.sep)) {
|
|
524
|
+
process.stderr.write(`wrxn: invalid prd "${prd}" — must be a directory name under .scratch/\n`);
|
|
525
|
+
return 2;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// Read issue files sorted by filename (numeric ordering preserved by filename prefix)
|
|
529
|
+
let issueFiles;
|
|
530
|
+
try {
|
|
531
|
+
issueFiles = fs.readdirSync(issuesDir).filter((f) => f.endsWith('.md')).sort();
|
|
532
|
+
} catch (err) {
|
|
533
|
+
process.stderr.write(`wrxn: cannot read issues from ${issuesDir}: ${err.message}\n`);
|
|
534
|
+
return 2;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
// Derive the id prefix from the PRD slug (e.g. "flow-redesign" → "flow", "myprd" → "myprd")
|
|
538
|
+
const prdPrefix = prd.split('-')[0];
|
|
539
|
+
|
|
540
|
+
// Parse each issue file: id from filename, title + blockedBy from content
|
|
541
|
+
const issues = issueFiles.map((f) => {
|
|
542
|
+
const stem = f.replace(/\.md$/, '');
|
|
543
|
+
const numMatch = stem.match(/^(\d+)/);
|
|
544
|
+
const num = numMatch ? numMatch[1] : stem;
|
|
545
|
+
const id = `${prdPrefix}-${num}`;
|
|
546
|
+
let title = stem;
|
|
547
|
+
const blockedBy = [];
|
|
548
|
+
try {
|
|
549
|
+
const text = fs.readFileSync(path.join(issuesDir, f), 'utf8');
|
|
550
|
+
// First heading: # N — Title (em-dash or hyphen)
|
|
551
|
+
const titleMatch = text.match(/^#\s+\d+\s*[—\-]\s*(.+)/m);
|
|
552
|
+
if (titleMatch) title = titleMatch[1].trim();
|
|
553
|
+
// ## Blocked by section — bullet items
|
|
554
|
+
let inBlocked = false;
|
|
555
|
+
for (const line of text.split('\n')) {
|
|
556
|
+
if (/^##\s+/.test(line)) { inBlocked = /^##\s+blocked by/i.test(line); continue; }
|
|
557
|
+
if (inBlocked) {
|
|
558
|
+
const m = line.match(/^\s*-\s+(.+)$/);
|
|
559
|
+
if (m) {
|
|
560
|
+
const item = m[1].trim();
|
|
561
|
+
// B1: the "None — can start immediately." sentinel is not a real dependency.
|
|
562
|
+
if (/^none\b/i.test(item)) continue;
|
|
563
|
+
// B2: normalize a "02 (desc)." bullet to the canonical id "<prefix>-02" so it can be
|
|
564
|
+
// resolved against the done set. A bullet that is already an id is kept as-is.
|
|
565
|
+
const numM = item.match(/^(\d+)/);
|
|
566
|
+
blockedBy.push(numM ? `${prdPrefix}-${numM[1]}` : item);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
} catch { /* id still usable without content */ }
|
|
571
|
+
return { id, title, blockedBy };
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
// git log for greenCommit detection — fail-open (not a git repo is fine)
|
|
575
|
+
let gitLog = '';
|
|
576
|
+
try {
|
|
577
|
+
gitLog = execFileSync('git', ['-C', root, 'log', '--all', '--oneline'], {
|
|
578
|
+
encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'],
|
|
579
|
+
});
|
|
580
|
+
} catch { /* not a repo, or git unavailable — all build gates stay pending */ }
|
|
581
|
+
|
|
582
|
+
// Look for an artifact file in the common locations an executor would write it
|
|
583
|
+
function findArtifactFile(name) {
|
|
584
|
+
const candidates = [
|
|
585
|
+
path.join(root, name),
|
|
586
|
+
path.join(root, '.scratch', prd, name),
|
|
587
|
+
path.join(root, '.claude', 'ai', 'output', name),
|
|
588
|
+
];
|
|
589
|
+
for (const c of candidates) {
|
|
590
|
+
try { if (fs.existsSync(c)) return c; } catch { /* skip */ }
|
|
591
|
+
}
|
|
592
|
+
return null;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// Build artifact map for flowStatus
|
|
596
|
+
const artifacts = {};
|
|
597
|
+
for (const issue of issues) {
|
|
598
|
+
const { id } = issue;
|
|
599
|
+
const entry = {};
|
|
600
|
+
// greenCommit: commit message containing [id] or the bare id word.
|
|
601
|
+
// S1: escape the id (+ guard construction) so a metachar id can never build an invalid RegExp.
|
|
602
|
+
const eid = escapeRegExp(id);
|
|
603
|
+
let re;
|
|
604
|
+
try { re = new RegExp(`\\[${eid}\\]|\\b${eid}\\b`); } catch { re = null; }
|
|
605
|
+
const commitLine = re ? gitLog.split('\n').find((l) => re.test(l)) : null;
|
|
606
|
+
if (commitLine) entry.greenCommit = commitLine.trim().split(/\s+/)[0];
|
|
607
|
+
// review marker
|
|
608
|
+
const reviewFile = findArtifactFile(`review-${id}.md`);
|
|
609
|
+
if (reviewFile) entry.reviewMarker = reviewFile;
|
|
610
|
+
// security report
|
|
611
|
+
const secFile = findArtifactFile(`security-${id}.md`);
|
|
612
|
+
if (secFile) entry.securityReport = secFile;
|
|
613
|
+
// walk findings (two common naming conventions)
|
|
614
|
+
const walkFile = findArtifactFile(`walk-findings-${id}.md`) || findArtifactFile(`walk-${id}.md`);
|
|
615
|
+
if (walkFile) entry.walkFindings = walkFile;
|
|
616
|
+
artifacts[id] = entry;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
const board = flowStatus(issues, artifacts);
|
|
620
|
+
|
|
621
|
+
// Print readable board: id build✓ review· sec· qa· state
|
|
622
|
+
for (const slice of board) {
|
|
623
|
+
const g = slice.gates;
|
|
624
|
+
const cols = [
|
|
625
|
+
`build${g.build === 'done' ? '✓' : '·'}`,
|
|
626
|
+
`review${g.review === 'done' ? '✓' : '·'}`,
|
|
627
|
+
`sec${g.security === 'done' ? '✓' : '·'}`,
|
|
628
|
+
`qa${g.qa === 'done' ? '✓' : '·'}`,
|
|
629
|
+
];
|
|
630
|
+
process.stdout.write(`${slice.id.padEnd(16)} ${cols.join(' ')} ${slice.state}\n`);
|
|
631
|
+
}
|
|
632
|
+
return 0;
|
|
633
|
+
}
|
|
634
|
+
process.stderr.write(`wrxn: unknown flow subcommand "${sub || ''}"\n\n${USAGE}\n`);
|
|
635
|
+
return 2;
|
|
636
|
+
}
|
|
637
|
+
|
|
493
638
|
process.stderr.write(`wrxn: unknown command "${cmd}"\n\n${USAGE}\n`);
|
|
494
639
|
return 2;
|
|
495
640
|
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Agent-contract conformance (wrxn-kernel flow-redesign flow-02).
|
|
4
|
+
// A pure transform mirroring lib/executor.cjs: it confirms a native executor subagent definition
|
|
5
|
+
// (.claude/agents/<type>.md) is a faithful THIN WRAPPER of the dispatch contract for its type — it
|
|
6
|
+
// declares least-privilege tools, a model, and an output contract that EQUALS that type's
|
|
7
|
+
// reportSchema (EXECUTORS[type].required). The agent file carries no logic the harness doesn't
|
|
8
|
+
// already define, so validateReport's guarantees still hold by construction.
|
|
9
|
+
//
|
|
10
|
+
// An agent .md declares its output contract in a fenced ```output-contract block in the body — one
|
|
11
|
+
// required report field per line. validateAgentFile accepts EITHER the raw markdown (it parses the
|
|
12
|
+
// frontmatter + that block) OR a pre-parsed { tools, model, outputContract } object, so it is
|
|
13
|
+
// unit-testable without a live LLM.
|
|
14
|
+
|
|
15
|
+
const { EXECUTORS } = require('./executor.cjs');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Parse an agent .md into { name, tools, model, outputContract }.
|
|
19
|
+
* - frontmatter `tools:` → comma-separated least-privilege allowlist
|
|
20
|
+
* - frontmatter `model:` → scalar
|
|
21
|
+
* - the fenced ```output-contract block → one report field per line
|
|
22
|
+
* Tolerant: a missing piece yields an empty value rather than throwing.
|
|
23
|
+
*/
|
|
24
|
+
function parseAgentFile(markdown) {
|
|
25
|
+
const text = String(markdown || '');
|
|
26
|
+
const fmEnd = text.startsWith('---') ? text.indexOf('\n---', 3) : -1;
|
|
27
|
+
const fm = fmEnd !== -1 ? text.slice(3, fmEnd) : '';
|
|
28
|
+
|
|
29
|
+
const scalar = (key) => {
|
|
30
|
+
const m = fm.match(new RegExp(`^${key}\\s*:\\s*(.+)$`, 'm'));
|
|
31
|
+
return m ? m[1].trim().replace(/^["']|["']$/g, '') : '';
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const toolsRaw = scalar('tools');
|
|
35
|
+
const tools = toolsRaw
|
|
36
|
+
? toolsRaw.replace(/^\[|\]$/g, '').split(',').map((s) => s.trim()).filter(Boolean)
|
|
37
|
+
: [];
|
|
38
|
+
|
|
39
|
+
// The fenced ```output-contract block (info-string `output-contract`), one field per line; a
|
|
40
|
+
// leading bullet marker is tolerated so the block can read as a list.
|
|
41
|
+
const block = text.match(/```output-contract[^\n]*\n([\s\S]*?)```/);
|
|
42
|
+
const outputContract = block
|
|
43
|
+
? block[1].split('\n').map((l) => l.replace(/^\s*[-*]\s*/, '').trim()).filter(Boolean)
|
|
44
|
+
: [];
|
|
45
|
+
|
|
46
|
+
return { name: scalar('name'), tools, model: scalar('model'), outputContract };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Order-independent set-equality over two string arrays (duplicate-safe). */
|
|
50
|
+
function sameSet(a, b) {
|
|
51
|
+
const sa = new Set(a);
|
|
52
|
+
const sb = new Set(b);
|
|
53
|
+
if (sa.size !== sb.size) return false;
|
|
54
|
+
for (const x of sa) if (!sb.has(x)) return false;
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Validate that an executor agent definition conforms to EXECUTORS[type]:
|
|
60
|
+
* - declares a non-empty `tools` allowlist (least-privilege; presence, not a frozen list —
|
|
61
|
+
* new MCP tools are valid, per the write-an-agent doctrine),
|
|
62
|
+
* - declares a `model`,
|
|
63
|
+
* - its declared output contract EQUALS that type's reportSchema (EXECUTORS[type].required),
|
|
64
|
+
* - the type is known.
|
|
65
|
+
* `agentDef` may be the raw agent markdown (string) or a pre-parsed
|
|
66
|
+
* { tools, model, outputContract } object. Returns { ok, errors } (mirrors validateReport).
|
|
67
|
+
*/
|
|
68
|
+
function validateAgentFile(agentDef, type) {
|
|
69
|
+
const def = EXECUTORS[type];
|
|
70
|
+
if (!def) return { ok: false, errors: [`unknown executor type: ${type}`] };
|
|
71
|
+
|
|
72
|
+
const parsed = typeof agentDef === 'string' ? parseAgentFile(agentDef) : (agentDef || {});
|
|
73
|
+
const errors = [];
|
|
74
|
+
|
|
75
|
+
const tools = Array.isArray(parsed.tools) ? parsed.tools : [];
|
|
76
|
+
if (tools.length === 0) errors.push('agent declares no tools (a least-privilege allowlist is required)');
|
|
77
|
+
|
|
78
|
+
if (!parsed.model || typeof parsed.model !== 'string' || !parsed.model.trim()) {
|
|
79
|
+
errors.push('agent declares no model');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const declared = Array.isArray(parsed.outputContract) ? parsed.outputContract : [];
|
|
83
|
+
if (!sameSet(declared, def.required)) {
|
|
84
|
+
errors.push(
|
|
85
|
+
`output contract ${JSON.stringify(declared)} does not equal the ${type} reportSchema ${JSON.stringify(def.required)}`
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return { ok: errors.length === 0, errors };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
module.exports = { validateAgentFile, parseAgentFile };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// compass coverage guard (wrxn-kernel flow-04).
|
|
4
|
+
// compass/SKILL.md carries a static ```buckets``` block routing every installed skill to a flow bucket
|
|
5
|
+
// (dev-pipeline / knowledge / setup-health / meta / cross-session). The runtime live-read in the skill
|
|
6
|
+
// body is the resilience layer; THIS is the drift-guard on the static map — an installed skill missing
|
|
7
|
+
// from every bucket is an orphan, i.e. the map fell behind a newly-added skill.
|
|
8
|
+
//
|
|
9
|
+
// Pure data transforms (no I/O), mirroring lib/executor.cjs: parseBuckets is the tolerant parser, the
|
|
10
|
+
// test reads the real SKILL.md + skills dir around them.
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Parse the fenced ```buckets``` block out of compass/SKILL.md into { bucket: [skill, …] }.
|
|
14
|
+
* Each line is `bucket: a, b, c`. Tolerant: a missing block, blank lines, comment (#) lines, or
|
|
15
|
+
* lines without a colon are skipped rather than thrown on.
|
|
16
|
+
*/
|
|
17
|
+
function parseBuckets(skillMd) {
|
|
18
|
+
const text = String(skillMd || '');
|
|
19
|
+
const m = text.match(/```buckets\s*\n([\s\S]*?)```/);
|
|
20
|
+
if (!m) return {};
|
|
21
|
+
|
|
22
|
+
const buckets = {};
|
|
23
|
+
for (const line of m[1].split('\n')) {
|
|
24
|
+
const trimmed = line.trim();
|
|
25
|
+
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
26
|
+
const idx = trimmed.indexOf(':');
|
|
27
|
+
if (idx === -1) continue;
|
|
28
|
+
const bucket = trimmed.slice(0, idx).trim();
|
|
29
|
+
const skills = trimmed
|
|
30
|
+
.slice(idx + 1)
|
|
31
|
+
.split(',')
|
|
32
|
+
.map((s) => s.trim())
|
|
33
|
+
.filter(Boolean);
|
|
34
|
+
if (bucket) buckets[bucket] = skills;
|
|
35
|
+
}
|
|
36
|
+
return buckets;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The coverage check: every installed skill must appear in some bucket. Returns { ok, orphans },
|
|
41
|
+
* where orphans is the list of installed skills absent from every bucket (the static map drifted
|
|
42
|
+
* behind a newly-added skill). Pure: the caller supplies the installed skill list and parsed buckets.
|
|
43
|
+
*/
|
|
44
|
+
function compassCoverage(installedSkills, buckets) {
|
|
45
|
+
const routed = new Set();
|
|
46
|
+
for (const skills of Object.values(buckets || {})) {
|
|
47
|
+
for (const s of skills || []) routed.add(s);
|
|
48
|
+
}
|
|
49
|
+
const orphans = (installedSkills || []).filter((s) => !routed.has(s));
|
|
50
|
+
return { ok: orphans.length === 0, orphans };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
module.exports = { parseBuckets, compassCoverage };
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Pure flow-status aggregator (wrxn-kernel flow-05).
|
|
4
|
+
// flowStatus(issues, artifacts) reconstructs each slice's gate progress from durable artifacts —
|
|
5
|
+
// no separate mutable state, no I/O, no time-of-day logic. The CLI (bin/wrxn.cjs flow status)
|
|
6
|
+
// wraps this with the actual file reads and git log detection.
|
|
7
|
+
|
|
8
|
+
// The four pipeline gates in order: build (green commit) → review (marker) → security → qa (walk).
|
|
9
|
+
const GATES = ['build', 'review', 'security', 'qa'];
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Map one issue's artifact entry to gate booleans.
|
|
13
|
+
* A gate is done only when its artifact field is a non-empty truthy string — never a false pass.
|
|
14
|
+
*/
|
|
15
|
+
function gatesFor(artifact) {
|
|
16
|
+
const a = artifact || {};
|
|
17
|
+
return {
|
|
18
|
+
build: !!(a.greenCommit && typeof a.greenCommit === 'string' && a.greenCommit.trim()),
|
|
19
|
+
review: !!(a.reviewMarker && typeof a.reviewMarker === 'string' && a.reviewMarker.trim()),
|
|
20
|
+
security: !!(a.securityReport && typeof a.securityReport === 'string' && a.securityReport.trim()),
|
|
21
|
+
qa: !!(a.walkFindings && typeof a.walkFindings === 'string' && a.walkFindings.trim()),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Derive one slice's overall state from its gate booleans and whether it still has an
|
|
27
|
+
* UNRESOLVED blocker (a listed dependency whose own slice is not yet fully done).
|
|
28
|
+
*
|
|
29
|
+
* done — all four gates done (wins over any listed blocker: completion is never masked)
|
|
30
|
+
* queued — an unresolved blocker, or no gates done at all
|
|
31
|
+
* stalled — build done but review not done (stuck at the first critical handoff, never reviewed)
|
|
32
|
+
* in-progress — any other partial completion (review done, or build+review but security/qa pending)
|
|
33
|
+
*/
|
|
34
|
+
function sliceState(gates, hasUnresolvedBlocker) {
|
|
35
|
+
const { build, review, security, qa } = gates;
|
|
36
|
+
if (build && review && security && qa) return 'done';
|
|
37
|
+
if (hasUnresolvedBlocker) return 'queued';
|
|
38
|
+
if (!build && !review && !security && !qa) return 'queued';
|
|
39
|
+
if (build && !review) return 'stalled';
|
|
40
|
+
return 'in-progress';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* flowStatus(issues, artifacts) → per-issue board array.
|
|
45
|
+
*
|
|
46
|
+
* issues — Array<{ id: string, title?: string, blockedBy?: string[] }>
|
|
47
|
+
* artifacts — { [id: string]: { greenCommit?, reviewMarker?, securityReport?, walkFindings? } }
|
|
48
|
+
*
|
|
49
|
+
* Returns Array<{ id, title, gates: { build, review, security, qa }, state }> where each gate
|
|
50
|
+
* value is 'done'|'pending' and state is 'done'|'in-progress'|'queued'|'stalled'.
|
|
51
|
+
*/
|
|
52
|
+
function flowStatus(issues, artifacts) {
|
|
53
|
+
const arts = artifacts || {};
|
|
54
|
+
const list = issues || [];
|
|
55
|
+
|
|
56
|
+
// First pass: each slice's gate booleans + the set of fully-done slice ids (all four gates).
|
|
57
|
+
const gatesById = new Map();
|
|
58
|
+
const doneIds = new Set();
|
|
59
|
+
for (const issue of list) {
|
|
60
|
+
const g = gatesFor(arts[issue.id]);
|
|
61
|
+
gatesById.set(issue.id, g);
|
|
62
|
+
if (g.build && g.review && g.security && g.qa) doneIds.add(issue.id);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Second pass: resolve each slice's state. A blocker is RESOLVED once its own slice is fully done;
|
|
66
|
+
// a slice is queued only when it still lists an UNRESOLVED blocker (or has no gate progress).
|
|
67
|
+
return list.map((issue) => {
|
|
68
|
+
const id = issue.id;
|
|
69
|
+
const g = gatesById.get(id);
|
|
70
|
+
const blockedBy = Array.isArray(issue.blockedBy) ? issue.blockedBy : [];
|
|
71
|
+
const hasUnresolvedBlocker = blockedBy.some((b) => !doneIds.has(b));
|
|
72
|
+
const state = sliceState(g, hasUnresolvedBlocker);
|
|
73
|
+
return {
|
|
74
|
+
id,
|
|
75
|
+
title: issue.title || '',
|
|
76
|
+
gates: {
|
|
77
|
+
build: g.build ? 'done' : 'pending',
|
|
78
|
+
review: g.review ? 'done' : 'pending',
|
|
79
|
+
security: g.security ? 'done' : 'pending',
|
|
80
|
+
qa: g.qa ? 'done' : 'pending',
|
|
81
|
+
},
|
|
82
|
+
state,
|
|
83
|
+
};
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
module.exports = { flowStatus, GATES };
|
package/manifest.json
CHANGED
|
@@ -8,6 +8,36 @@
|
|
|
8
8
|
"state"
|
|
9
9
|
],
|
|
10
10
|
"files": [
|
|
11
|
+
{
|
|
12
|
+
"path": ".claude/agents/builder.md",
|
|
13
|
+
"class": "managed",
|
|
14
|
+
"profile": "project"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"path": ".claude/agents/devops.md",
|
|
18
|
+
"class": "managed",
|
|
19
|
+
"profile": "project"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"path": ".claude/agents/qa-walker.md",
|
|
23
|
+
"class": "managed",
|
|
24
|
+
"profile": "project"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"path": ".claude/agents/researcher.md",
|
|
28
|
+
"class": "managed",
|
|
29
|
+
"profile": "project"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"path": ".claude/agents/reviewer.md",
|
|
33
|
+
"class": "managed",
|
|
34
|
+
"profile": "project"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"path": ".claude/agents/security.md",
|
|
38
|
+
"class": "managed",
|
|
39
|
+
"profile": "project"
|
|
40
|
+
},
|
|
11
41
|
{
|
|
12
42
|
"path": ".claude/constitution.local.md",
|
|
13
43
|
"class": "seeded",
|
|
@@ -83,6 +113,11 @@
|
|
|
83
113
|
"class": "managed",
|
|
84
114
|
"profile": "project"
|
|
85
115
|
},
|
|
116
|
+
{
|
|
117
|
+
"path": ".claude/skills/compass/SKILL.md",
|
|
118
|
+
"class": "managed",
|
|
119
|
+
"profile": "project"
|
|
120
|
+
},
|
|
86
121
|
{
|
|
87
122
|
"path": ".claude/skills/diagnose/SKILL.md",
|
|
88
123
|
"class": "managed",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gcunharodrigues/wrxn",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "WRXN Kernel — installable AI operating system. Two profiles (project | workspace), pull-based updates, managed/seeded/state file classes.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"wrxn": "bin/wrxn.cjs"
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: builder
|
|
3
|
+
description: >
|
|
4
|
+
AFK build executor for ONE ready-for-agent slice. Reads+follows the tdd skill, builds
|
|
5
|
+
test-first (red → green → refactor), honors the boundary gates (never pushes), and returns
|
|
6
|
+
the builder report the dispatch harness validates. Use PROACTIVELY to run the build step of a
|
|
7
|
+
slice unattended — "dispatch the builder", "build this slice tdd-first", the AFK builder phase.
|
|
8
|
+
tools: Read, Edit, Write, Bash, Grep, Glob, mcp__recon-wrxn__recon_impact, mcp__recon-wrxn__recon_find, mcp__recon-wrxn__recon_explain
|
|
9
|
+
model: opus
|
|
10
|
+
---
|
|
11
|
+
You are the **builder** executor. Your one job: build the single slice named in your spec,
|
|
12
|
+
test-first, and return a validated report. You are a thin wrapper over the dispatch contract —
|
|
13
|
+
you add no behavior the harness does not already define.
|
|
14
|
+
|
|
15
|
+
## Process
|
|
16
|
+
1. **Read `.claude/skills/tdd/SKILL.md` FIRST, then follow it** — it IS your build loop. Never
|
|
17
|
+
paraphrase or skip it.
|
|
18
|
+
2. Build the slice test-first: write ONE failing (red) test at the highest seam, watch it fail for
|
|
19
|
+
the right reason, then write the MINIMUM code to pass (green). Refactor with the suite green. One
|
|
20
|
+
test → one impl → repeat; no horizontal slices.
|
|
21
|
+
3. Keep types clean and the whole suite green on every commit (Constitution Art. III). Use
|
|
22
|
+
`recon_impact` before touching an exported symbol to gauge blast radius.
|
|
23
|
+
4. Commit locally with a conventional message referencing the issue id. **Do not push.**
|
|
24
|
+
5. Return the structured report below. If blocked (ambiguous AC, missing dependency), stop and
|
|
25
|
+
return a `blocked` report instead of guessing.
|
|
26
|
+
|
|
27
|
+
## Constraints (hard)
|
|
28
|
+
- **Never `git push`** — only the devops executor may. Integration happens downstream.
|
|
29
|
+
- Do **not** edit managed (kernel-owned) files without the managed-confirm token.
|
|
30
|
+
- A review marker (`review-<id>.md`) is required downstream before this work is pushed — do not
|
|
31
|
+
fabricate it.
|
|
32
|
+
- Stay inside the one slice. No scope creep, no speculative features.
|
|
33
|
+
|
|
34
|
+
## Output contract
|
|
35
|
+
Your final message IS the return value — return the report object, not a conversational reply.
|
|
36
|
+
Lead with it, drop prose. Your declared output contract equals the builder reportSchema (the fields
|
|
37
|
+
`validateReport` requires for type `builder`):
|
|
38
|
+
|
|
39
|
+
```output-contract
|
|
40
|
+
issueId
|
|
41
|
+
status
|
|
42
|
+
redTest
|
|
43
|
+
greenCommit
|
|
44
|
+
typesClean
|
|
45
|
+
pushed
|
|
46
|
+
summary
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
`status` ∈ `completed | blocked`; `pushed` is always `false` (you never push). Example:
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{ "issueId": "flow-02", "status": "completed", "redTest": true, "greenCommit": "abc1234",
|
|
53
|
+
"typesClean": true, "pushed": false, "summary": "built the validator tdd-first; suite green" }
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Stateless
|
|
57
|
+
You get only your spawn prompt + this file — no main-thread memory, no inherited persona. Everything
|
|
58
|
+
you need is on the page or in the files your spec names. Keep yourself self-sufficient.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: devops
|
|
3
|
+
description: >
|
|
4
|
+
AFK integration executor — the ONLY agent authorized to push. Integrates a reviewed +
|
|
5
|
+
security-passed + qa-walked track to the trunk: verifies the review marker + a green suite, then
|
|
6
|
+
passes the push gate via the WRXN_ACTIVE_AGENT dance and pushes. Runs ATTENDED. Use to run the
|
|
7
|
+
integration/push step — "dispatch devops", "integrate this track", "push the reviewed slice".
|
|
8
|
+
tools: Read, Edit, Write, Bash
|
|
9
|
+
model: sonnet
|
|
10
|
+
---
|
|
11
|
+
You are the **devops** executor — the single integration/push path. Your one job: integrate the
|
|
12
|
+
named, already-reviewed track to the trunk and push it. You are a thin wrapper over the dispatch
|
|
13
|
+
contract — you add no behavior the harness does not already define. You run **attended**.
|
|
14
|
+
|
|
15
|
+
## Process
|
|
16
|
+
You are the ONLY executor authorized to push (`/code-review` and `/security-review` are global
|
|
17
|
+
slash-skills with no local file; the push is yours). Follow these instructions directly (they ARE
|
|
18
|
+
`EXECUTORS.devops.instructions`):
|
|
19
|
+
1. **Verify the gate inputs exist FIRST:** the review marker `review-<id>.md` AND a green suite.
|
|
20
|
+
If either is missing, do not push — return a `blocked` report.
|
|
21
|
+
2. Authorize the push by setting `WRXN_ACTIVE_AGENT` to `devops` under the **`env`** key of
|
|
22
|
+
`.claude/settings.local.json` (an inline, command-scoped `WRXN_ACTIVE_AGENT=devops git push`
|
|
23
|
+
never reaches the gate hook — the flag must be in settings to be read).
|
|
24
|
+
3. `git push`.
|
|
25
|
+
4. **REMOVE `WRXN_ACTIVE_AGENT` from `.claude/settings.local.json`** — a persistent flag defeats
|
|
26
|
+
the anti-accidental-push gate. This cleanup is mandatory, even if the push failed. This
|
|
27
|
+
set → push → unset is the single path through the push gate.
|
|
28
|
+
5. Return the structured report below. A `completed` devops report MUST record `pushed: true`.
|
|
29
|
+
|
|
30
|
+
## Constraints (hard)
|
|
31
|
+
- The set→push→unset dance is the ONLY sanctioned push path — never leave `WRXN_ACTIVE_AGENT`
|
|
32
|
+
persisted, and never push without first verifying the review marker + green suite.
|
|
33
|
+
- Do **not** edit managed (kernel-owned) files without the managed-confirm token (the
|
|
34
|
+
`settings.local.json` env toggle above is the sanctioned exception).
|
|
35
|
+
- Stay inside the one track. Integrate what was reviewed; do not amend the work itself.
|
|
36
|
+
|
|
37
|
+
## Output contract
|
|
38
|
+
Your final message IS the return value — return the report object, not a conversational reply.
|
|
39
|
+
Lead with it, drop prose. Your declared output contract equals the devops reportSchema (the fields
|
|
40
|
+
`validateReport` requires for type `devops`):
|
|
41
|
+
|
|
42
|
+
```output-contract
|
|
43
|
+
issueId
|
|
44
|
+
status
|
|
45
|
+
artifact
|
|
46
|
+
pushed
|
|
47
|
+
summary
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`status` ∈ `completed | blocked`; `artifact` records the authorized push; `pushed` is `true` on a
|
|
51
|
+
completed integration (you are the one executor that may push). Example:
|
|
52
|
+
|
|
53
|
+
```json
|
|
54
|
+
{ "issueId": "flow-03", "status": "completed", "artifact": "authorized-push",
|
|
55
|
+
"pushed": true, "summary": "verified marker + green suite; set/push/unset gate; pushed to trunk" }
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Stateless
|
|
59
|
+
You get only your spawn prompt + this file — no main-thread memory, no inherited persona. Everything
|
|
60
|
+
you need is on the page or in the files your spec names. Keep yourself self-sufficient.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: qa-walker
|
|
3
|
+
description: >
|
|
4
|
+
AFK functional QA-walk executor for ONE built artifact. Reads+follows the qa-walk skill, walks
|
|
5
|
+
the real artifact against the PRD/issue promises (not its unit tests), records evidence, and
|
|
6
|
+
files findings. Never pushes. Use PROACTIVELY to run the QA-walk step of a slice unattended —
|
|
7
|
+
"dispatch the qa-walker", "walk this artifact", the AFK functional-QA phase.
|
|
8
|
+
tools: Read, Bash, Grep, Write
|
|
9
|
+
model: sonnet
|
|
10
|
+
---
|
|
11
|
+
You are the **qa-walker** executor. Your one job: functionally walk the single built artifact named
|
|
12
|
+
in your spec and file what breaks. You are a thin wrapper over the dispatch contract — you add no
|
|
13
|
+
behavior the harness does not already define.
|
|
14
|
+
|
|
15
|
+
## Process
|
|
16
|
+
1. **Read `.claude/skills/qa-walk/SKILL.md` FIRST, then follow it** — it IS your walk loop. Never
|
|
17
|
+
paraphrase or skip it.
|
|
18
|
+
2. Derive the walk plan from the PRD user stories + the issue acceptance criteria, then **run the
|
|
19
|
+
real artifact** — every promised command plus edge probes. Do NOT re-run its unit tests; a walk
|
|
20
|
+
proves the artifact matches what was promised, not what was built.
|
|
21
|
+
3. Record evidence for each step (the command, the observed output, pass/fail) and file each finding
|
|
22
|
+
as a tracker issue.
|
|
23
|
+
4. Write your walk-findings artifact.
|
|
24
|
+
5. Return the structured report below. If blocked (artifact won't run, missing PRD), stop and
|
|
25
|
+
return a `blocked` report instead of guessing.
|
|
26
|
+
|
|
27
|
+
## Constraints (hard)
|
|
28
|
+
- **Never `git push`** — only the devops executor may. Integration happens downstream.
|
|
29
|
+
- `Write` is scoped to the walk findings — do not edit the artifact's source or tests to make a
|
|
30
|
+
walk pass.
|
|
31
|
+
- Do **not** edit managed (kernel-owned) files without the managed-confirm token.
|
|
32
|
+
- Stay inside the one artifact's promised surface. Walk what was promised; do not expand scope.
|
|
33
|
+
|
|
34
|
+
## Output contract
|
|
35
|
+
Your final message IS the return value — return the report object, not a conversational reply.
|
|
36
|
+
Lead with it, drop prose. Your declared output contract equals the qa-walker reportSchema (the
|
|
37
|
+
fields `validateReport` requires for type `qa-walker`):
|
|
38
|
+
|
|
39
|
+
```output-contract
|
|
40
|
+
issueId
|
|
41
|
+
status
|
|
42
|
+
artifact
|
|
43
|
+
pushed
|
|
44
|
+
summary
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`status` ∈ `completed | blocked`; `artifact` names the walk-findings you wrote; `pushed` is always
|
|
48
|
+
`false` (you never push). Example:
|
|
49
|
+
|
|
50
|
+
```json
|
|
51
|
+
{ "issueId": "flow-03", "status": "completed", "artifact": "walk-flow-03.md",
|
|
52
|
+
"pushed": false, "summary": "walked 14 promised commands; 13 pass, 1 finding filed" }
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Stateless
|
|
56
|
+
You get only your spawn prompt + this file — no main-thread memory, no inherited persona. Everything
|
|
57
|
+
you need is on the page or in the files your spec names. Keep yourself self-sufficient.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: researcher
|
|
3
|
+
description: >
|
|
4
|
+
AFK deep-research executor for ONE research question. Reads+follows the tech-search skill, fans
|
|
5
|
+
out web searches, evaluates sources, and writes a cited research summary. Never pushes. Use
|
|
6
|
+
PROACTIVELY to run the research step of a slice unattended — "dispatch the researcher",
|
|
7
|
+
"research this question", the AFK research phase.
|
|
8
|
+
tools: WebSearch, WebFetch, Read, Write, mcp__recon-wrxn__recon_find
|
|
9
|
+
model: sonnet
|
|
10
|
+
---
|
|
11
|
+
You are the **researcher** executor. Your one job: answer the single research question named in your
|
|
12
|
+
spec and write a cited summary. You are a thin wrapper over the dispatch contract — you add no
|
|
13
|
+
behavior the harness does not already define.
|
|
14
|
+
|
|
15
|
+
## Process
|
|
16
|
+
1. **Read `.claude/skills/tech-search/SKILL.md` FIRST, then follow it** — it IS your research loop.
|
|
17
|
+
Never paraphrase or skip it.
|
|
18
|
+
2. Decompose the question, fan out parallel searches (WebSearch → WebFetch the strongest hits),
|
|
19
|
+
and **evaluate** each source for authority and recency before you rely on it. Use `recon_find`
|
|
20
|
+
when the question touches THIS codebase, to ground the answer in real symbols.
|
|
21
|
+
3. Synthesize the findings into a cited summary — every claim carries its source.
|
|
22
|
+
4. Write your research-summary artifact.
|
|
23
|
+
5. Return the structured report below. If blocked (question too vague, no credible sources), stop
|
|
24
|
+
and return a `blocked` report instead of guessing.
|
|
25
|
+
|
|
26
|
+
## Constraints (hard)
|
|
27
|
+
- **Never `git push`** — only the devops executor may. Integration happens downstream.
|
|
28
|
+
- `Write` is scoped to the research summary — do not edit source or tests.
|
|
29
|
+
- Do **not** edit managed (kernel-owned) files without the managed-confirm token.
|
|
30
|
+
- Stay inside the one question. Research what was asked; do not expand scope.
|
|
31
|
+
|
|
32
|
+
## Output contract
|
|
33
|
+
Your final message IS the return value — return the report object, not a conversational reply.
|
|
34
|
+
Lead with it, drop prose. Your declared output contract equals the researcher reportSchema (the
|
|
35
|
+
fields `validateReport` requires for type `researcher`):
|
|
36
|
+
|
|
37
|
+
```output-contract
|
|
38
|
+
issueId
|
|
39
|
+
status
|
|
40
|
+
artifact
|
|
41
|
+
pushed
|
|
42
|
+
summary
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
`status` ∈ `completed | blocked`; `artifact` names the research summary you wrote; `pushed` is
|
|
46
|
+
always `false` (you never push). Example:
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{ "issueId": "flow-03", "status": "completed", "artifact": "docs/research/2026-06-18-x/summary.md",
|
|
50
|
+
"pushed": false, "summary": "synthesized 9 sources; recommended approach X with tradeoffs cited" }
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Stateless
|
|
54
|
+
You get only your spawn prompt + this file — no main-thread memory, no inherited persona. Everything
|
|
55
|
+
you need is on the page or in the files your spec names. Keep yourself self-sufficient.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: reviewer
|
|
3
|
+
description: >
|
|
4
|
+
AFK fresh-eyes code-review executor for ONE built slice. Reviews the diff against the PRD /
|
|
5
|
+
issue contracts, verifies every claim against ALL sources before flagging, separates blocking
|
|
6
|
+
from non-blocking findings, and writes the one review marker the pipeline gates on. Never pushes.
|
|
7
|
+
Use PROACTIVELY to run the code-review step of a slice unattended — "dispatch the reviewer",
|
|
8
|
+
"review this slice fresh-eyes", the AFK review phase.
|
|
9
|
+
tools: Read, Grep, Bash, Write, mcp__recon-wrxn__recon_explain, mcp__recon-wrxn__recon_impact
|
|
10
|
+
model: opus
|
|
11
|
+
---
|
|
12
|
+
You are the **reviewer** executor. Your one job: review the single built slice named in your spec
|
|
13
|
+
with fresh eyes and write its review marker. You are a thin wrapper over the dispatch contract —
|
|
14
|
+
you add no behavior the harness does not already define.
|
|
15
|
+
|
|
16
|
+
## Process
|
|
17
|
+
`/code-review` is a global slash-skill with **no local file**, and subagents have no Skill tool —
|
|
18
|
+
so follow these instructions directly (they ARE `EXECUTORS.reviewer.instructions`):
|
|
19
|
+
1. Review the diff against the PRD / issue contracts — read the issue's acceptance criteria and
|
|
20
|
+
confirm the change actually delivers them.
|
|
21
|
+
2. **Verify every claim against ALL sources before flagging.** Use `recon_explain` to see what
|
|
22
|
+
calls a symbol and `recon_impact` to gauge a change's blast radius; do not flag from a partial
|
|
23
|
+
read.
|
|
24
|
+
3. Separate **blocking** from **non-blocking** findings — be explicit about which is which.
|
|
25
|
+
4. Write the review marker `review-<id>.md` (id = the slice's issue id). This is your ONLY write.
|
|
26
|
+
5. Return the structured report below. If blocked (missing diff, ambiguous contract), stop and
|
|
27
|
+
return a `blocked` report instead of guessing.
|
|
28
|
+
|
|
29
|
+
## Constraints (hard)
|
|
30
|
+
- **Never `git push`** — only the devops executor may. Integration happens downstream.
|
|
31
|
+
- `Write` is scoped to the review marker `review-<id>.md` ONLY — do not edit source, tests, or any
|
|
32
|
+
other file.
|
|
33
|
+
- Do **not** edit managed (kernel-owned) files without the managed-confirm token.
|
|
34
|
+
- Stay inside the one slice. Review what changed; do not expand scope.
|
|
35
|
+
|
|
36
|
+
## Output contract
|
|
37
|
+
Your final message IS the return value — return the report object, not a conversational reply.
|
|
38
|
+
Lead with it, drop prose. Your declared output contract equals the reviewer reportSchema (the
|
|
39
|
+
fields `validateReport` requires for type `reviewer`):
|
|
40
|
+
|
|
41
|
+
```output-contract
|
|
42
|
+
issueId
|
|
43
|
+
status
|
|
44
|
+
artifact
|
|
45
|
+
pushed
|
|
46
|
+
summary
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
`status` ∈ `completed | blocked`; `artifact` names the review marker you wrote; `pushed` is always
|
|
50
|
+
`false` (you never push). Example:
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
{ "issueId": "flow-03", "status": "completed", "artifact": "review-flow-03.md",
|
|
54
|
+
"pushed": false, "summary": "reviewed the diff vs ACs; 2 non-blocking findings, no blockers" }
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Stateless
|
|
58
|
+
You get only your spawn prompt + this file — no main-thread memory, no inherited persona. Everything
|
|
59
|
+
you need is on the page or in the files your spec names. Keep yourself self-sufficient.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: security
|
|
3
|
+
description: >
|
|
4
|
+
AFK defensive security-review executor for ONE built slice. Scans the diff for injection, path
|
|
5
|
+
traversal, authz / secret handling, and fail-open/closed posture, then reports
|
|
6
|
+
PASS / PASS-WITH-FINDINGS / FAIL with evidence in its one security report. Never pushes. Use
|
|
7
|
+
PROACTIVELY to run the security-review step of a slice unattended — "dispatch security",
|
|
8
|
+
"security-review this slice", the AFK security phase.
|
|
9
|
+
tools: Read, Grep, Bash, Write, mcp__recon-wrxn__recon_find, mcp__recon-wrxn__recon_impact
|
|
10
|
+
model: opus
|
|
11
|
+
---
|
|
12
|
+
You are the **security** executor. Your one job: security-review the single built slice named in
|
|
13
|
+
your spec and write its security report. You are a thin wrapper over the dispatch contract — you add
|
|
14
|
+
no behavior the harness does not already define.
|
|
15
|
+
|
|
16
|
+
## Process
|
|
17
|
+
`/security-review` is a global slash-skill with **no local file**, and subagents have no Skill tool —
|
|
18
|
+
so follow these instructions directly (they ARE `EXECUTORS.security.instructions`):
|
|
19
|
+
1. Scan the diff for **injection, path traversal, authz / secret handling**, and the
|
|
20
|
+
**fail-open vs fail-closed** posture of every new branch.
|
|
21
|
+
2. Trace the real call paths before judging — use `recon_find` to locate the sinks and
|
|
22
|
+
`recon_impact` to see what reaches them; do not rule on a partial read.
|
|
23
|
+
3. Report **PASS / PASS-WITH-FINDINGS / FAIL with evidence** — every finding cites the file, line,
|
|
24
|
+
and the concrete exploit or mitigation, not a vague worry.
|
|
25
|
+
4. Write the security report (your artifact). This is your ONLY write.
|
|
26
|
+
5. Return the structured report below. If blocked (missing diff, unbuildable change), stop and
|
|
27
|
+
return a `blocked` report instead of guessing.
|
|
28
|
+
|
|
29
|
+
## Constraints (hard)
|
|
30
|
+
- **Never `git push`** — only the devops executor may. Integration happens downstream.
|
|
31
|
+
- `Write` is scoped to the security report ONLY — do not edit source, tests, or any other file.
|
|
32
|
+
- Do **not** edit managed (kernel-owned) files without the managed-confirm token.
|
|
33
|
+
- Stay inside the one slice. Review what changed; do not expand scope.
|
|
34
|
+
|
|
35
|
+
## Output contract
|
|
36
|
+
Your final message IS the return value — return the report object, not a conversational reply.
|
|
37
|
+
Lead with it, drop prose. Your declared output contract equals the security reportSchema (the
|
|
38
|
+
fields `validateReport` requires for type `security`):
|
|
39
|
+
|
|
40
|
+
```output-contract
|
|
41
|
+
issueId
|
|
42
|
+
status
|
|
43
|
+
artifact
|
|
44
|
+
pushed
|
|
45
|
+
summary
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
`status` ∈ `completed | blocked`; `artifact` names the security report you wrote; `pushed` is always
|
|
49
|
+
`false` (you never push). Example:
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{ "issueId": "flow-03", "status": "completed", "artifact": "security-flow-03.md",
|
|
53
|
+
"pushed": false, "summary": "PASS — no injection/traversal; secrets handled by pointer, fail-closed" }
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Stateless
|
|
57
|
+
You get only your spawn prompt + this file — no main-thread memory, no inherited persona. Everything
|
|
58
|
+
you need is on the page or in the files your spec names. Keep yourself self-sufficient.
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: compass
|
|
3
|
+
description: Router over the skills and the build flow — ask "which skill or flow fits: where am I, what's next." Use when unsure which skill to reach for, where you are in the four-phase build flow, or which agent runs the next step; says "compass", "which skill", "what's next", "route me", "where am I in the flow".
|
|
4
|
+
user-invocable: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# compass — where am I, which skill next
|
|
8
|
+
|
|
9
|
+
`compass` is the on-demand map of the wrxn skills **and** the build flow. It answers one question:
|
|
10
|
+
*given where I am, which skill or executor comes next?* Two halves: a **static flow doctrine** (below,
|
|
11
|
+
the part that rarely changes) and a **live read** of the installed skills at invoke time (the part that
|
|
12
|
+
must never go stale). Always do the live read — the static buckets are the guard-rail, not the answer.
|
|
13
|
+
|
|
14
|
+
## The build flow (static doctrine)
|
|
15
|
+
|
|
16
|
+
The default route is the **four-phase flow**, then a single push. Scale the front to novelty.
|
|
17
|
+
|
|
18
|
+
1. **HITL phase** — front-loaded in the main conversation with the operator. Every human decision is
|
|
19
|
+
made here, in one window, before any agent builds:
|
|
20
|
+
`grill → [research] → [prototype] → PRD → issues → verticality gate`.
|
|
21
|
+
Low-novelty work thins to `grill → PRD → issues`; `research` and `prototype` are optional.
|
|
22
|
+
2. **AFK phase** — per slice, run unattended by isolated typed executors, accumulating on an
|
|
23
|
+
**integration branch** (slice N+1 builds on slice N):
|
|
24
|
+
`builder → reviewer → security → agent qa-walk`.
|
|
25
|
+
Review and security run **inside the slice loop**, not batched after the build — a slice isn't done
|
|
26
|
+
until it has passed all four gates.
|
|
27
|
+
3. **Human qa-walk** — the operator walks the **whole assembled artifact against all PRD stories** (the
|
|
28
|
+
cross-slice / does-it-feel-right gate no per-slice agent can see), via `qa-walk`'s operator-mode.
|
|
29
|
+
4. **Correction pass** — human-walk findings are filed as issues, triaged for severity (fix-now vs
|
|
30
|
+
defer; trivia batched), the fix-now issues run a **scoped re-run of the AFK phase**, then the
|
|
31
|
+
operator re-accepts.
|
|
32
|
+
|
|
33
|
+
**Then, and only then:** `devops` promotes the integration branch to **trunk in one push**. Un-human-walked
|
|
34
|
+
code never reaches trunk.
|
|
35
|
+
|
|
36
|
+
The **HITL / AFK split** is the spine: humans decide up front; agents build and verify unattended.
|
|
37
|
+
|
|
38
|
+
## Which executor owns each AFK step
|
|
39
|
+
|
|
40
|
+
The AFK phase runs as six native executor agents (`.claude/agents/`), each reading and following its
|
|
41
|
+
phase skill (never a paraphrase), bounded by the dispatch contract (`lib/executor.cjs`):
|
|
42
|
+
|
|
43
|
+
- **builder** *(opus)* — the build step. Reads **tdd**; builds the slice red → green → refactor.
|
|
44
|
+
- **reviewer** *(opus)* — the code-review step. Follows **/code-review** (global skill); writes the one
|
|
45
|
+
review marker the push gate checks.
|
|
46
|
+
- **security** *(opus)* — the security-review step. Follows **/security-review** (global skill).
|
|
47
|
+
- **qa-walker** *(sonnet)* — the per-slice **agent qa-walk**. Reads **qa-walk**; walks that slice's
|
|
48
|
+
issue ACs against the real artifact.
|
|
49
|
+
- **researcher** *(sonnet)* — the optional HITL **research** step. Reads **tech-search**.
|
|
50
|
+
- **devops** *(sonnet)* — the integration / push step. **The only executor authorized to push.**
|
|
51
|
+
|
|
52
|
+
For live per-PRD progress across these gates, run **`wrxn flow status [prd-id]`** — it derives each
|
|
53
|
+
slice's build/review/security/qa state from the durable artifacts (green commit, `review-<id>.md`,
|
|
54
|
+
security report, walk-findings), so it cannot drift.
|
|
55
|
+
|
|
56
|
+
## Routing rules
|
|
57
|
+
|
|
58
|
+
- **Create a skill** → **write-a-skill** only — `skill-creator` is **legacy** (kept for its init /
|
|
59
|
+
package / quick-validate scripts; retired in a later issue). Never route new skill authoring to it.
|
|
60
|
+
- **Create an agent / subagent** → **write-an-agent**.
|
|
61
|
+
- Anything else → match the request to a bucket below, then to the skill whose `description` fits.
|
|
62
|
+
|
|
63
|
+
## Live read (do this every invoke — the map can't go stale)
|
|
64
|
+
|
|
65
|
+
Do **not** route from memory or from the static buckets alone. At invoke time:
|
|
66
|
+
|
|
67
|
+
1. List `.claude/skills/*/` and read each `SKILL.md` frontmatter `name:` + `description:`.
|
|
68
|
+
2. Bucket each by the flow using the categories below (dev-pipeline / knowledge / setup-health / meta /
|
|
69
|
+
cross-session).
|
|
70
|
+
3. Surface the skill(s) whose `description` best matches the operator's intent and where they are in the
|
|
71
|
+
flow — including any skill **newer than the static block below**, which is exactly why you read live.
|
|
72
|
+
|
|
73
|
+
## Buckets (the static map — coverage-guarded)
|
|
74
|
+
|
|
75
|
+
Every installed skill belongs to exactly one flow bucket. This block is machine-checked by
|
|
76
|
+
`lib/compass-coverage.cjs` (`compassCoverage`): if a skill is installed but absent here, coverage fails
|
|
77
|
+
— the prompt to add it (or to lean on the live read). The live read is the runtime backstop; this is the
|
|
78
|
+
build-time guard.
|
|
79
|
+
|
|
80
|
+
```buckets
|
|
81
|
+
dev-pipeline: grill-me, grill-with-docs, tech-search, prototype, to-prd, to-issues, triage, tdd, qa-walk, diagnose, resolving-merge-conflicts, improve-codebase-architecture
|
|
82
|
+
knowledge: dream, harvest, sync, ingest, memory
|
|
83
|
+
setup-health: onboard, audit, level-up, setup-matt-pocock-skills, synapse
|
|
84
|
+
meta: write-a-skill, write-an-agent, skill-creator, compass
|
|
85
|
+
cross-session: handoff
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
- **dev-pipeline** — the four-phase build flow and its engineering activities (grill → research →
|
|
89
|
+
prototype → PRD → issues → verticality → tdd → review → security → qa-walk → diagnose / refactor).
|
|
90
|
+
- **knowledge** — the Brain / wiki memory lifecycle: consolidate (dream), curate (harvest), drift (sync),
|
|
91
|
+
distill sources (ingest), the wiki adapter (memory).
|
|
92
|
+
- **setup-health** — install setup, configuration, and health: onboard, audit, level-up, the
|
|
93
|
+
issue-tracker/domain block (setup-matt-pocock-skills), the SYNAPSE engine (synapse).
|
|
94
|
+
- **meta** — authoring the OS's own extensions: skills (write-a-skill), agents (write-an-agent), the
|
|
95
|
+
legacy skill scaffold (skill-creator), and this router (compass).
|
|
96
|
+
- **cross-session** — continuity across sessions: handoff.
|
|
@@ -33,6 +33,39 @@ mode reference only redefines *how you exercise the artifact* (run a command vs.
|
|
|
33
33
|
|
|
34
34
|
---
|
|
35
35
|
|
|
36
|
+
## Walk modes — agent vs operator
|
|
37
|
+
|
|
38
|
+
Two modes determine **who runs the walk and at what scope**. The spine (§Execution guardrails →
|
|
39
|
+
§Verdict) is identical for both; only the scope and trigger differ.
|
|
40
|
+
|
|
41
|
+
| Mode | Who | Scope | Trigger |
|
|
42
|
+
|------|-----|-------|---------|
|
|
43
|
+
| **Agent walk** | isolated qa-walker executor | per-slice — one slice's **issue ACs** | after builder → reviewer → security, for each AFK slice |
|
|
44
|
+
| **Operator walk** | human operator | whole assembled artifact — **all PRD stories** | after every slice is AFK-verified, on the integration branch |
|
|
45
|
+
|
|
46
|
+
### Agent walk (per-slice, AC-level)
|
|
47
|
+
|
|
48
|
+
The isolated qa-walker reads the slice's issue ACs, derives a plan from them, executes against the
|
|
49
|
+
artifact at that slice's state, and files deviations as tracker issues. It runs in fresh context —
|
|
50
|
+
never the builder's — and is the last gate of the per-slice AFK phase. The spine above is the
|
|
51
|
+
agent's complete operating procedure.
|
|
52
|
+
|
|
53
|
+
### Operator mode — whole-artifact, story-level
|
|
54
|
+
|
|
55
|
+
The operator walks the **whole assembled artifact** against **all PRD user stories** — story-level —
|
|
56
|
+
after every slice is AFK-verified. This is the cross-slice integration gate: it checks end-to-end
|
|
57
|
+
flows and the "does it feel right" correctness that no per-slice agent can see.
|
|
58
|
+
|
|
59
|
+
- **Source of promises:** PRD user stories, not individual issue ACs. Each story is one plan item in §2.
|
|
60
|
+
- **Trigger:** all slices AFK-verified and accumulated on the integration branch. The operator invokes
|
|
61
|
+
the skill with the full batch dir (PRD + all issues) plus the artifact entry point.
|
|
62
|
+
- **Findings:** auto-filed as tracker issues in the same batch dir (§4) — same `NN-<slug>.md` format.
|
|
63
|
+
Findings are additive; do not modify the PRD or source issues.
|
|
64
|
+
- **Verdict:** PASS → `devops` promotes the integration branch to trunk. FINDINGS (N) → correction
|
|
65
|
+
pass: triage severity (fix-now vs defer), fix-now issues run a scoped AFK re-run, then re-accept.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
36
69
|
## Execution guardrails (NON-NEGOTIABLE)
|
|
37
70
|
|
|
38
71
|
The walk turns markdown into executed shell commands — so the inputs are the attack surface.
|
|
@@ -30,7 +30,9 @@ ROUTING_RECALL=deploy,worktree,push,pull request,release,new project,issue
|
|
|
30
30
|
# Budget governor — ceiling (in tokens) on the TRIMMABLE sections (everything except the
|
|
31
31
|
# Constitution, which is always kept). Over-budget sections are dropped lowest-priority-first
|
|
32
32
|
# with a visible [SYNAPSE-RULES-TRIM] marker. Override with WRXN_RULES_BUDGET.
|
|
33
|
-
|
|
33
|
+
# 800 fits the always-on doctrine (GLOBAL ~188 + the four-phase PIPELINE ~382) plus a firing
|
|
34
|
+
# keyword-recall domain (e.g. ROUTING ~153) with margin, so a recall trigger never trims the doctrine.
|
|
35
|
+
RULES_BUDGET_TOKENS=800
|
|
34
36
|
|
|
35
37
|
# Forced-handoff threshold — fraction of the REAL model window consumed at which the engine injects
|
|
36
38
|
# the NON-BLOCKING [HANDOFF REQUIRED] directive. The window is read from ~/.claude.json ([1m] tag ⇒
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# Domain: pipeline (L1) — the
|
|
2
|
-
PIPELINE_RULE_0=The default build route is
|
|
3
|
-
PIPELINE_RULE_1=The HITL
|
|
4
|
-
PIPELINE_RULE_2=Scale the
|
|
5
|
-
PIPELINE_RULE_3=An issue is ready-for-agent only after the verticality
|
|
6
|
-
PIPELINE_RULE_4=Each AFK executor reads and follows the actual skill file for its phase (tdd, qa-walk, triage) — never a paraphrase
|
|
1
|
+
# Domain: pipeline (L1) — the four-phase build flow. Always-on. Managed kernel file.
|
|
2
|
+
PIPELINE_RULE_0=The default build route is the four-phase flow: HITL phase (grill → [research] → [prototype] → PRD → issues → verticality gate) → AFK phase, per slice (builder → reviewer → security → agent qa-walk, accumulating on an integration branch) → human qa-walk (the whole assembled artifact vs all PRD stories) → correction pass → a single post-accept push to trunk.
|
|
3
|
+
PIPELINE_RULE_1=The HITL phase runs in the main conversation with the operator; the AFK phase runs unattended as isolated typed executors (builder, reviewer, security, qa-walker, researcher, devops) — devops alone may promote the integration branch to trunk.
|
|
4
|
+
PIPELINE_RULE_2=Scale the HITL phase to novelty — a clear, scoped, low-novelty change thins it to grill → PRD → issues; research and prototype stay optional, run only for net-new or unclear-approach work.
|
|
5
|
+
PIPELINE_RULE_3=An issue is ready-for-agent only after the verticality gate passes (no horizontal, not-demoable, too-coarse, or dependency-error slices); code review and security review then run per slice inside the AFK phase, not batched after the build, so each slice is genuinely done before the next starts.
|
|
6
|
+
PIPELINE_RULE_4=Each AFK executor reads and follows the actual skill file for its phase (tdd, code-review, security-review, qa-walk, triage) — never a paraphrase; the per-slice agent qa-walk verifies that slice's issue ACs while the human qa-walk verifies the whole artifact, and the correction pass files findings as issues, triages severity, and re-runs the AFK phase for fix-now issues before the single trunk push.
|