@gcunharodrigues/wrxn 0.8.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.
Files changed (30) hide show
  1. package/bin/wrxn.cjs +145 -0
  2. package/lib/agent-conformance.cjs +92 -0
  3. package/lib/compass-coverage.cjs +53 -0
  4. package/lib/flow-status.cjs +87 -0
  5. package/manifest.json +55 -0
  6. package/package.json +1 -1
  7. package/payload/.claude/agents/builder.md +58 -0
  8. package/payload/.claude/agents/devops.md +60 -0
  9. package/payload/.claude/agents/qa-walker.md +57 -0
  10. package/payload/.claude/agents/researcher.md +55 -0
  11. package/payload/.claude/agents/reviewer.md +59 -0
  12. package/payload/.claude/agents/security.md +58 -0
  13. package/payload/.claude/skills/compass/SKILL.md +96 -0
  14. package/payload/.claude/skills/diagnose/SKILL.md +20 -3
  15. package/payload/.claude/skills/grill-me/SKILL.md +1 -1
  16. package/payload/.claude/skills/handoff/SKILL.md +1 -0
  17. package/payload/.claude/skills/improve-codebase-architecture/LANGUAGE.md +60 -0
  18. package/payload/.claude/skills/qa-walk/SKILL.md +33 -0
  19. package/payload/.claude/skills/resolving-merge-conflicts/SKILL.md +14 -0
  20. package/payload/.claude/skills/tdd/SKILL.md +2 -1
  21. package/payload/.claude/skills/tdd/mocking.md +59 -0
  22. package/payload/.claude/skills/tdd/tests.md +61 -0
  23. package/payload/.claude/skills/to-issues/SKILL.md +2 -0
  24. package/payload/.claude/skills/to-prd/SKILL.md +1 -1
  25. package/payload/.claude/skills/triage/OUT-OF-SCOPE.md +5 -1
  26. package/payload/.claude/skills/triage/SKILL.md +2 -1
  27. package/payload/.claude/skills/write-a-skill/GLOSSARY.md +181 -0
  28. package/payload/.claude/skills/write-a-skill/SKILL.md +91 -85
  29. package/payload/.synapse/manifest +3 -1
  30. 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",
@@ -298,11 +333,26 @@
298
333
  "class": "managed",
299
334
  "profile": "project"
300
335
  },
336
+ {
337
+ "path": ".claude/skills/resolving-merge-conflicts/SKILL.md",
338
+ "class": "managed",
339
+ "profile": "project"
340
+ },
301
341
  {
302
342
  "path": ".claude/skills/tdd/SKILL.md",
303
343
  "class": "managed",
304
344
  "profile": "project"
305
345
  },
346
+ {
347
+ "path": ".claude/skills/tdd/tests.md",
348
+ "class": "managed",
349
+ "profile": "project"
350
+ },
351
+ {
352
+ "path": ".claude/skills/tdd/mocking.md",
353
+ "class": "managed",
354
+ "profile": "project"
355
+ },
306
356
  {
307
357
  "path": ".claude/skills/tech-search/SKILL.md",
308
358
  "class": "managed",
@@ -343,6 +393,11 @@
343
393
  "class": "managed",
344
394
  "profile": "project"
345
395
  },
396
+ {
397
+ "path": ".claude/skills/write-a-skill/GLOSSARY.md",
398
+ "class": "managed",
399
+ "profile": "project"
400
+ },
346
401
  {
347
402
  "path": ".claude/skills/write-an-agent/SKILL.md",
348
403
  "class": "managed",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gcunharodrigues/wrxn",
3
- "version": "0.8.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.