mishkan-harness 0.2.0 → 0.2.3

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 (113) hide show
  1. package/README.md +108 -172
  2. package/bin/mishkan.js +677 -16
  3. package/docs/design/MISHKAN_decisions.md +450 -8
  4. package/docs/design/MISHKAN_harness_design.md +1 -33
  5. package/docs/design/MISHKAN_observability.md +13 -9
  6. package/docs/research/graphify-token-saving-poc.md +108 -0
  7. package/docs/usage/09-workflows.md +74 -4
  8. package/docs/usage/10-observability.md +37 -13
  9. package/docs/usage/11-graphify.md +264 -0
  10. package/docs/usage/12-skill-discovery.md +294 -0
  11. package/docs/usage/README.md +5 -3
  12. package/package.json +1 -1
  13. package/payload/install/settings.hooks.json +24 -0
  14. package/payload/mishkan/AGENT_SPEC.md +25 -7
  15. package/payload/mishkan/agents/ahikam.md +4 -2
  16. package/payload/mishkan/agents/aholiab.md +4 -2
  17. package/payload/mishkan/agents/asaph.md +5 -2
  18. package/payload/mishkan/agents/baruch.md +19 -2
  19. package/payload/mishkan/agents/benaiah.md +4 -2
  20. package/payload/mishkan/agents/bezalel.md +4 -2
  21. package/payload/mishkan/agents/caleb.md +4 -2
  22. package/payload/mishkan/agents/deborah.md +4 -2
  23. package/payload/mishkan/agents/elasah.md +4 -2
  24. package/payload/mishkan/agents/eliashib.md +4 -2
  25. package/payload/mishkan/agents/ezra.md +4 -2
  26. package/payload/mishkan/agents/hanun.md +5 -2
  27. package/payload/mishkan/agents/hiram.md +5 -2
  28. package/payload/mishkan/agents/hizkiah.md +5 -2
  29. package/payload/mishkan/agents/huldah.md +4 -2
  30. package/payload/mishkan/agents/huram.md +4 -2
  31. package/payload/mishkan/agents/hushai.md +5 -2
  32. package/payload/mishkan/agents/igal.md +4 -2
  33. package/payload/mishkan/agents/ira.md +5 -2
  34. package/payload/mishkan/agents/jahaziel.md +5 -2
  35. package/payload/mishkan/agents/jakin.md +4 -2
  36. package/payload/mishkan/agents/jehonathan.md +5 -2
  37. package/payload/mishkan/agents/jehoshaphat.md +4 -2
  38. package/payload/mishkan/agents/joab.md +5 -2
  39. package/payload/mishkan/agents/joah.md +5 -2
  40. package/payload/mishkan/agents/maaseiah.md +4 -2
  41. package/payload/mishkan/agents/meremoth.md +5 -2
  42. package/payload/mishkan/agents/meshullam.md +5 -2
  43. package/payload/mishkan/agents/nathan.md +5 -2
  44. package/payload/mishkan/agents/nehemiah.md +4 -2
  45. package/payload/mishkan/agents/obed.md +4 -2
  46. package/payload/mishkan/agents/oholiab.md +5 -2
  47. package/payload/mishkan/agents/palal.md +5 -2
  48. package/payload/mishkan/agents/phinehas.md +4 -2
  49. package/payload/mishkan/agents/rehum.md +4 -2
  50. package/payload/mishkan/agents/salma.md +5 -2
  51. package/payload/mishkan/agents/seraiah.md +4 -2
  52. package/payload/mishkan/agents/shallum.md +5 -2
  53. package/payload/mishkan/agents/shaphan.md +4 -2
  54. package/payload/mishkan/agents/shemaiah.md +4 -2
  55. package/payload/mishkan/agents/shevna.md +5 -2
  56. package/payload/mishkan/agents/uriah.md +5 -2
  57. package/payload/mishkan/agents/zaccur.md +4 -2
  58. package/payload/mishkan/agents/zadok.md +5 -2
  59. package/payload/mishkan/agents/zerubbabel.md +4 -2
  60. package/payload/mishkan/commands/code-graph.md +35 -0
  61. package/payload/mishkan/commands/eval-baruch.md +57 -0
  62. package/payload/mishkan/commands/mishkan-org-reference.md +58 -0
  63. package/payload/mishkan/commands/mishkan-skills-misses.md +47 -0
  64. package/payload/mishkan/commands/mishkan-skills-reindex.md +33 -0
  65. package/payload/mishkan/commands/skills.md +35 -0
  66. package/payload/mishkan/commands/sprint-close.md +3 -1
  67. package/payload/mishkan/hooks/pre-tool-knowledge-route.sh +231 -0
  68. package/payload/mishkan/hooks/pre-tool-task-skill-route.sh +122 -0
  69. package/payload/mishkan/hooks/session-start-skill-index.sh +35 -0
  70. package/payload/mishkan/observability/schema.json +1 -1
  71. package/payload/mishkan/observability/watch/src/mishkan_watch/__main__.py +84 -2
  72. package/payload/mishkan/observability/watch/src/mishkan_watch/app.py +64 -3
  73. package/payload/mishkan/observability/watch/src/mishkan_watch/org_data.py +79 -0
  74. package/payload/mishkan/observability/watch/src/mishkan_watch/skills_data.py +267 -0
  75. package/payload/mishkan/observability/watch/src/mishkan_watch/tabs/agents.py +33 -1
  76. package/payload/mishkan/observability/watch/src/mishkan_watch/tabs/knowledge.py +118 -9
  77. package/payload/mishkan/observability/watch/src/mishkan_watch/tabs/live.py +102 -2
  78. package/payload/mishkan/observability/watch/src/mishkan_watch/tabs/org.py +139 -0
  79. package/payload/mishkan/observability/watch/src/mishkan_watch/tabs/skills.py +187 -0
  80. package/payload/mishkan/observability/watch/src/mishkan_watch/tabs/usage.py +377 -0
  81. package/payload/mishkan/observability/watch/src/mishkan_watch/tabs/workflows.py +199 -27
  82. package/payload/mishkan/observability/watch/src/mishkan_watch/theme.tcss +78 -4
  83. package/payload/mishkan/observability/watchd/src/mishkan_watchd/__main__.py +36 -6
  84. package/payload/mishkan/observability/watchd/src/mishkan_watchd/sources/bus_tail.py +16 -3
  85. package/payload/mishkan/observability/watchd/src/mishkan_watchd/sources/graphify_tail.py +220 -0
  86. package/payload/mishkan/observability/watchd/src/mishkan_watchd/sources/worktree_poll.py +13 -2
  87. package/payload/mishkan/observability/watchd/src/mishkan_watchd/state.py +157 -3
  88. package/payload/mishkan/observability/watchd/tests/test_state.py +6 -2
  89. package/payload/mishkan/org/org.json +141 -0
  90. package/payload/mishkan/scripts/pre-commit-eval-gate.sh +50 -0
  91. package/payload/mishkan/scripts/skill-discovery-indexer.py +462 -0
  92. package/payload/mishkan/scripts/skill-discovery-misses.py +173 -0
  93. package/payload/mishkan/scripts/skill-discovery-router.py +457 -0
  94. package/payload/mishkan/scripts/skill-discovery-smoke.sh +156 -0
  95. package/payload/mishkan/skills/graphify-query-craft/SKILL.md +162 -0
  96. package/payload/mishkan/skills/mishkan-init/SKILL.md +15 -0
  97. package/payload/mishkan/skills/skill-discovery/SKILL.md +120 -0
  98. package/payload/mishkan/templates/case-node.schema.json +1 -0
  99. package/payload/mishkan/templates/observability-log.schema.json +1 -0
  100. package/payload/mishkan/workflows/README.md +78 -13
  101. package/payload/mishkan/workflows/chosheb-feature-ship.js +75 -0
  102. package/payload/mishkan/workflows/migdal-dr-drill.js +94 -0
  103. package/payload/mishkan/workflows/migdal-infra-change.js +67 -0
  104. package/payload/mishkan/workflows/mishkan-blast-radius.js +144 -0
  105. package/payload/mishkan/workflows/mishkan-init.js +58 -0
  106. package/payload/mishkan/workflows/mishkan-knowledge-gap-discovery.js +121 -0
  107. package/payload/mishkan/workflows/mishkan-standards-rollout.js +153 -0
  108. package/payload/mishkan/workflows/mishmar-security-gate.js +83 -0
  109. package/payload/mishkan/workflows/panim-ds-rollout.js +83 -0
  110. package/payload/mishkan/workflows/sefer-release-notes.js +85 -0
  111. package/payload/mishkan/workflows/yasad-data-migration-wave.js +78 -0
  112. package/payload/mishkan/workflows/yasad-schema-evolution.js +76 -0
  113. package/payload/mishkan/templates/user-CLAUDE.md +0 -62
@@ -0,0 +1,83 @@
1
+ // panim-ds-rollout — propagate a design token change across all consumers.
2
+ //
3
+ // A token change (color, spacing, font, radius) is decided at the DS level.
4
+ // This workflow finds every component that consumes it, applies the change
5
+ // per-component (worktree isolation), verifies a11y contrast post-change,
6
+ // runs visual regression snapshots, and gates merge on a 2-reviewer panel.
7
+ //
8
+ // Pattern: pipeline (discover → transform → verify) + worktree + judge panel.
9
+ // ADR D-010 check:
10
+ // - parallelism: per-component fan-out (≥6 components typical).
11
+ // - panel orthogonality: Oholiab (DS consistency) + Asaph (a11y contrast)
12
+ // are distinct lenses, not 70%-overlapping.
13
+ // - termination predicate: judge accepts/rejects per component.
14
+ // - synthesis: rollout report with per-component verdict.
15
+
16
+ export const meta = {
17
+ name: "panim-ds-rollout",
18
+ description: "Propagate a design token change across consumers (per-component worktree + a11y + visual regression).",
19
+ whenToUse: "When a design token changes and the impact spans multiple components.",
20
+ phases: [{ title: "Discover" }, { title: "Transform" }, { title: "Review" }],
21
+ };
22
+
23
+ const tokenChange = args?.token_change;
24
+ const project = args?.project ?? ".";
25
+ if (!tokenChange) throw new Error("args.token_change is required (e.g. {token: 'color.primary', from: '#1A73E8', to: '#0B5FCC'})");
26
+
27
+ const DISCOVERY_SCHEMA = {
28
+ type: "object", required: ["components"],
29
+ properties: { components: { type: "array", items: { type: "object", required: ["file", "name"], properties: { file: {type:"string"}, name: {type:"string"}, usage: {type:"string"} } } } },
30
+ };
31
+ const VERDICT_SCHEMA = {
32
+ type: "object", required: ["accept", "rationale"],
33
+ properties: { accept: {type:"boolean"}, rationale: {type:"string"}, findings: {type:"array", items:{type:"string"}} },
34
+ };
35
+
36
+ phase("Discover");
37
+ const discovery = await agent(
38
+ `Token change: ${JSON.stringify(tokenChange)}. Project: ${project}. Find every component that consumes this token (CSS/SCSS/JS/TS). Return the schema with file paths + component names.`,
39
+ { schema: DISCOVERY_SCHEMA, label: "discover-consumers", agentType: "oholiab" },
40
+ );
41
+
42
+ const components = discovery?.components ?? [];
43
+ log(`Discovered ${components.length} consumers of ${tokenChange.token}.`);
44
+ if (!components.length) {
45
+ return { token_change: tokenChange, components: [], summary: "No consumers found. Token change is safe to merge at the DS level only." };
46
+ }
47
+
48
+ phase("Transform");
49
+ const transformed = await parallel(components.map(c => () =>
50
+ agent(
51
+ `Token change: ${JSON.stringify(tokenChange)}. Component: ${c.file} (${c.name}). Apply the change. Return a short diff summary + any test updates needed.`,
52
+ { label: `transform:${c.name}`, agentType: "salma", isolation: "worktree", phase: "Transform" },
53
+ ).then(diff => ({ component: c, diff }))
54
+ ));
55
+
56
+ phase("Review");
57
+ const REVIEWERS = [
58
+ { key: "ds-consistency", agent: "oholiab", prompt: "DS consistency: does the post-change component still match the DS pattern? Any inconsistency vs other consumers?" },
59
+ { key: "a11y-contrast", agent: "asaph", prompt: "A11y contrast: post-change WCAG contrast ratio compliance, focus-visible, color-only-meaning checks." },
60
+ ];
61
+ const reviewed = await parallel(transformed.filter(Boolean).map(t => () =>
62
+ parallel(REVIEWERS.map(R => () =>
63
+ agent(
64
+ `Token change: ${JSON.stringify(tokenChange)}. Component: ${t.component.file}. Diff summary: ${t.diff}. Your lens: ${R.prompt} Default accept=false if uncertain.`,
65
+ { schema: VERDICT_SCHEMA, label: `review:${R.key}:${t.component.name}`, agentType: R.agent, phase: "Review" },
66
+ )
67
+ )).then(verdicts => {
68
+ const valid = verdicts.filter(Boolean);
69
+ const accepts = valid.filter(v => v.accept).length;
70
+ return { component: t.component, diff: t.diff, verdicts: valid, accepted: accepts >= 2 };
71
+ })
72
+ ));
73
+
74
+ const accepted = reviewed.filter(r => r.accepted);
75
+ const rejected = reviewed.filter(r => !r.accepted);
76
+
77
+ return {
78
+ token_change: tokenChange,
79
+ total_consumers: components.length,
80
+ accepted: accepted.map(r => r.component.file),
81
+ rejected: rejected.map(r => ({ file: r.component.file, findings: r.verdicts.flatMap(v => v.findings ?? []) })),
82
+ summary: `${accepted.length}/${components.length} components accepted by 2/2 reviewers. ${rejected.length} need rework.`,
83
+ };
@@ -0,0 +1,85 @@
1
+ // sefer-release-notes — assemble release notes from git log per category.
2
+ //
3
+ // Given a tag range (or commit range), categorise commits, summarise per
4
+ // category in parallel by Sefer specialists, then Jehoshaphat (lead) applies
5
+ // the doc style guide and produces RELEASE_NOTES.md.
6
+ //
7
+ // Pattern: pipeline (collect → per-category fan-out → style synthesis).
8
+ // ADR D-010 check:
9
+ // - parallelism: 4 categories in parallel (feat/fix/breaking/security).
10
+ // - panel orthogonality: each specialist handles a distinct content layer
11
+ // (Seraiah org-wide, Joah project-level, Shevna team-level,
12
+ // Jehonathan publication style).
13
+ // - synthesis: final markdown produced by Jehoshaphat.
14
+
15
+ export const meta = {
16
+ name: "sefer-release-notes",
17
+ description: "Assemble release notes from git log: per-category summaries, style-guide application, ready-to-publish markdown.",
18
+ whenToUse: "Every release tag, before publishing to GitHub Releases or npm.",
19
+ phases: [{ title: "Collect" }, { title: "Summarise" }, { title: "Publish" }],
20
+ };
21
+
22
+ const fromRef = args?.from_ref;
23
+ const toRef = args?.to_ref ?? "HEAD";
24
+ const project = args?.project ?? ".";
25
+ const releaseTag = args?.release_tag;
26
+ if (!fromRef) throw new Error("args.from_ref is required (previous tag e.g. 'v0.2.0')");
27
+ if (!releaseTag) throw new Error("args.release_tag is required (e.g. 'v0.2.3')");
28
+
29
+ const COMMITS_SCHEMA = {
30
+ type: "object", required: ["commits"],
31
+ properties: { commits: { type: "array", items: { type: "object", required: ["sha", "type", "subject"], properties: { sha:{type:"string"}, type:{type:"string"}, scope:{type:"string"}, subject:{type:"string"}, breaking:{type:"boolean"} } } } },
32
+ };
33
+ const CATEGORY_SCHEMA = {
34
+ type: "object", required: ["entries"],
35
+ properties: { entries: { type: "array", items: { type: "object", required: ["title", "description"], properties: { title:{type:"string"}, description:{type:"string"}, references:{type:"array", items:{type:"string"}} } } } },
36
+ };
37
+
38
+ phase("Collect");
39
+ const collected = await agent(
40
+ `Project: ${project}. Run \`git log ${fromRef}..${toRef} --pretty=format:'%H|%s'\` and parse commits using conventional-commit format (type(scope) subject). Detect breaking changes (subject contains '!' or body has BREAKING CHANGE). Return the schema.`,
41
+ { schema: COMMITS_SCHEMA, label: "collect-commits", agentType: "joah" },
42
+ );
43
+
44
+ const commits = collected?.commits ?? [];
45
+ log(`${commits.length} commits between ${fromRef} and ${toRef}.`);
46
+
47
+ const byCategory = {
48
+ feat: commits.filter(c => c.type === "feat" && !c.breaking),
49
+ fix: commits.filter(c => c.type === "fix"),
50
+ breaking: commits.filter(c => c.breaking),
51
+ security: commits.filter(c => c.type === "fix" && (c.scope === "security" || /CVE|secur/i.test(c.subject))),
52
+ };
53
+
54
+ phase("Summarise");
55
+ const CATEGORIES = [
56
+ { key: "feat", label: "Features", agent: "seraiah", items: byCategory.feat },
57
+ { key: "fix", label: "Fixes", agent: "joah", items: byCategory.fix },
58
+ { key: "breaking", label: "Breaking changes", agent: "shevna", items: byCategory.breaking },
59
+ { key: "security", label: "Security", agent: "jehonathan", items: byCategory.security },
60
+ ];
61
+
62
+ const summaries = await parallel(CATEGORIES.map(C => () =>
63
+ C.items.length === 0
64
+ ? Promise.resolve({ category: C.key, label: C.label, entries: [] })
65
+ : agent(
66
+ `Release ${releaseTag}. Category: ${C.label}. Commits: ${JSON.stringify(C.items.map(c => ({sha: c.sha.slice(0,7), subject: c.subject})))}. Summarise each into a user-facing entry: title (action verb), description (1-2 sentences, why it matters), commit refs. Return the schema.`,
67
+ { schema: CATEGORY_SCHEMA, label: `summarise:${C.key}`, agentType: C.agent, phase: "Summarise" },
68
+ ).then(s => ({ category: C.key, label: C.label, entries: s.entries ?? [] }))
69
+ ));
70
+
71
+ phase("Publish");
72
+ const releaseNotes = await agent(
73
+ `Release ${releaseTag}. Apply the project's documentation style guide. Summaries by category: ${JSON.stringify(summaries.filter(Boolean))}. Produce RELEASE_NOTES.md with: title line, date, intro paragraph (1-3 sentences), sections per non-empty category (## Features / ## Fixes / ## Breaking changes / ## Security), each entry as a bullet with link to commit. Markdown only — no preamble.`,
74
+ { label: "publish-notes", agentType: "jehoshaphat", phase: "Publish" },
75
+ );
76
+
77
+ return {
78
+ release_tag: releaseTag,
79
+ from_ref: fromRef,
80
+ to_ref: toRef,
81
+ commit_count: commits.length,
82
+ category_counts: Object.fromEntries(CATEGORIES.map(C => [C.key, C.items.length])),
83
+ release_notes: releaseNotes,
84
+ summary: `Release notes for ${releaseTag} assembled: ${commits.length} commits across ${CATEGORIES.filter(C => C.items.length > 0).length} non-empty categories.`,
85
+ };
@@ -0,0 +1,78 @@
1
+ // yasad-data-migration-wave — per-table DB migration with 4-reviewer panel.
2
+ //
3
+ // A migration plan (one or more tables) goes in; each table is independently
4
+ // reviewed by 4 orthogonal lenses (contracts, perf, security, tests), then
5
+ // verified by running the migration in a worktree against a fresh snapshot.
6
+ //
7
+ // Pattern: pipeline (analyze → per-table review → verify) + judge panel.
8
+ // ADR D-010 check:
9
+ // - parallelism: tables × 4 reviewers.
10
+ // - panel orthogonality: Nathan (contracts), Zadok (perf/index),
11
+ // Shallum (data security/PII), Uriah (tests) — distinct domains.
12
+ // - synthesis: per-table go/no-go + rollback plan.
13
+
14
+ export const meta = {
15
+ name: "yasad-data-migration-wave",
16
+ description: "Wave of DB migrations, per-table reviewed by 4 orthogonal lenses (contracts/perf/security/tests).",
17
+ whenToUse: "Before applying a migration that touches multiple tables or has invariant impact.",
18
+ phases: [{ title: "Analyze" }, { title: "Review" }, { title: "Verify" }],
19
+ };
20
+
21
+ const migrationPlan = args?.migration_plan;
22
+ const project = args?.project ?? ".";
23
+ const verifyCmd = args?.verify_command;
24
+ if (!migrationPlan) throw new Error("args.migration_plan is required (array of {table, change, reason})");
25
+
26
+ const REVIEW_SCHEMA = {
27
+ type: "object", required: ["safe", "rationale"],
28
+ properties: { safe: {type:"boolean"}, rationale: {type:"string"}, blockers: {type:"array", items:{type:"string"}}, mitigations: {type:"array", items:{type:"string"}} },
29
+ };
30
+
31
+ phase("Analyze");
32
+ log(`Migration wave: ${migrationPlan.length} tables to review.`);
33
+
34
+ phase("Review");
35
+ const REVIEWERS = [
36
+ { key: "contracts", agent: "nathan", prompt: "API contracts: does this migration break any consumer of this table (endpoints, events, projections)?" },
37
+ { key: "perf", agent: "zadok", prompt: "Performance: index implications, lock duration, table-scan risk, query plan changes for downstream queries." },
38
+ { key: "security", agent: "shallum", prompt: "Data security: PII handling, encryption-at-rest changes, RLS impact, audit trail." },
39
+ { key: "tests", agent: "uriah", prompt: "Tests: what tests exist for this table, which break, which are missing. Test plan for the migration itself." },
40
+ ];
41
+
42
+ const reviewed = await parallel(migrationPlan.map(M => () =>
43
+ parallel(REVIEWERS.map(R => () =>
44
+ agent(
45
+ `Migration: table=${M.table}, change=${M.change}, reason=${M.reason}. Project: ${project}. Your lens: ${R.prompt} Default safe=false if uncertain.`,
46
+ { schema: REVIEW_SCHEMA, label: `review:${R.key}:${M.table}`, agentType: R.agent, phase: "Review" },
47
+ )
48
+ )).then(verdicts => {
49
+ const valid = verdicts.filter(Boolean);
50
+ const safeCount = valid.filter(v => v.safe).length;
51
+ const blockers = valid.flatMap(v => v.blockers ?? []);
52
+ return { migration: M, lens_verdicts: valid, safe_count: safeCount, all_safe: safeCount === REVIEWERS.length, blockers };
53
+ })
54
+ ));
55
+
56
+ const allSafe = reviewed.filter(r => r.all_safe);
57
+ const blocked = reviewed.filter(r => !r.all_safe);
58
+ log(`${allSafe.length}/${migrationPlan.length} migrations cleared 4/4 lenses.`);
59
+
60
+ phase("Verify");
61
+ let verifyResult = null;
62
+ if (verifyCmd && allSafe.length === migrationPlan.length) {
63
+ verifyResult = await agent(
64
+ `Run \`${verifyCmd}\` to verify the migration applies cleanly against a fresh snapshot. Report stdout/stderr/exit code summary.`,
65
+ { label: "verify-migration", agentType: "hizkiah", phase: "Verify" },
66
+ );
67
+ }
68
+
69
+ return {
70
+ migration_plan: migrationPlan,
71
+ reviewed: reviewed.map(r => ({ table: r.migration.table, safe_count: r.safe_count, all_safe: r.all_safe, blockers: r.blockers })),
72
+ blocked_tables: blocked.map(b => b.migration.table),
73
+ verify_result: verifyResult,
74
+ decision: blocked.length === 0 ? "ship" : "block",
75
+ summary: blocked.length === 0
76
+ ? `All ${allSafe.length} migrations cleared 4/4 lenses. Verify: ${verifyResult ? "ran" : "skipped"}.`
77
+ : `${blocked.length} migrations BLOCKED. Address blockers per-table before re-running.`,
78
+ };
@@ -0,0 +1,76 @@
1
+ // yasad-schema-evolution — zero-downtime multi-release schema change orchestration.
2
+ //
3
+ // A schema change that requires phased rollout (dual-write, backfill,
4
+ // switch-read, drop-old) goes in; the workflow plans the phases, validates
5
+ // each phase against zero-downtime invariants, and produces a per-phase
6
+ // runbook with rollback for each step.
7
+ //
8
+ // Pattern: pipeline (plan → per-phase validate) + barrier + judge panel.
9
+ // Differs from yasad-data-migration-wave: this is a SINGLE schema change
10
+ // across MULTIPLE releases, not multiple migrations in one wave.
11
+
12
+ export const meta = {
13
+ name: "yasad-schema-evolution",
14
+ description: "Phase a single schema change across multiple releases with zero-downtime invariants and per-phase rollback.",
15
+ whenToUse: "Schema change that can't ship atomically — needs dual-write / backfill / switch / drop sequence.",
16
+ phases: [{ title: "Plan" }, { title: "Validate" }, { title: "Runbook" }],
17
+ };
18
+
19
+ const change = args?.change;
20
+ const project = args?.project ?? ".";
21
+ if (!change) throw new Error("args.change is required (e.g. {from: 'users.email_text', to: 'users.email_normalized', rationale: '...'})");
22
+
23
+ const PLAN_SCHEMA = {
24
+ type: "object", required: ["phases"],
25
+ properties: { phases: { type: "array", items: { type: "object", required: ["name", "actions", "invariants"], properties: { name: {type:"string"}, actions: {type:"array", items:{type:"string"}}, invariants: {type:"array", items:{type:"string"}}, rollback: {type:"array", items:{type:"string"}}, release: {type:"string"} } } } },
26
+ };
27
+ const VALIDATE_SCHEMA = {
28
+ type: "object", required: ["safe", "rationale"],
29
+ properties: { safe: {type:"boolean"}, rationale: {type:"string"}, risks: {type:"array", items:{type:"string"}} },
30
+ };
31
+
32
+ phase("Plan");
33
+ const plan = await agent(
34
+ `Schema change: ${JSON.stringify(change)}. Project: ${project}. Plan the phased rollout (typically: add-new-column → dual-write → backfill → switch-read → drop-old). Each phase: actions, invariants that MUST hold during the phase, rollback steps, target release. Return the schema.`,
35
+ { schema: PLAN_SCHEMA, label: "plan-phases", agentType: "shallum" },
36
+ );
37
+
38
+ const phases = plan?.phases ?? [];
39
+ log(`Plan: ${phases.length} phases.`);
40
+
41
+ phase("Validate");
42
+ const REVIEWERS = [
43
+ { key: "consumer-safety", agent: "nathan", prompt: "Consumer safety: at every phase boundary, can every existing consumer keep working without code change? Identify breakage windows." },
44
+ { key: "data-integrity", agent: "shallum", prompt: "Data integrity: backfill correctness, dual-write consistency, race conditions, idempotency." },
45
+ { key: "rollback", agent: "zadok", prompt: "Rollback: from each phase, is there a clean rollback that doesn't lose data? Identify points of no return." },
46
+ ];
47
+
48
+ const validations = await parallel(phases.map((ph, i) => () =>
49
+ parallel(REVIEWERS.map(R => () =>
50
+ agent(
51
+ `Schema change overall: ${JSON.stringify(change)}. Phase ${i+1}/${phases.length}: ${JSON.stringify(ph)}. Your lens: ${R.prompt} Default safe=false if uncertain.`,
52
+ { schema: VALIDATE_SCHEMA, label: `validate:${R.key}:${ph.name}`, agentType: R.agent, phase: "Validate" },
53
+ )
54
+ )).then(verdicts => {
55
+ const valid = verdicts.filter(Boolean);
56
+ const safeCount = valid.filter(v => v.safe).length;
57
+ return { phase: ph, verdicts: valid, all_safe: safeCount === REVIEWERS.length, risks: valid.flatMap(v => v.risks ?? []) };
58
+ })
59
+ ));
60
+
61
+ phase("Runbook");
62
+ const runbook = await agent(
63
+ `Schema change: ${JSON.stringify(change)}. Validated phases: ${JSON.stringify(validations.map(v => ({phase: v.phase.name, safe: v.all_safe, risks: v.risks})))}. Produce a per-phase runbook: pre-checks, exact commands, post-checks, rollback. Markdown.`,
64
+ { label: "produce-runbook", agentType: "shallum", phase: "Runbook" },
65
+ );
66
+
67
+ const blocked = validations.filter(v => !v.all_safe);
68
+ return {
69
+ change,
70
+ phases: validations.map(v => ({ name: v.phase.name, release: v.phase.release, all_safe: v.all_safe, risks: v.risks })),
71
+ runbook,
72
+ decision: blocked.length === 0 ? "ship" : "block",
73
+ summary: blocked.length === 0
74
+ ? `Schema evolution validated across ${phases.length} phases by 3/3 reviewers. Runbook produced.`
75
+ : `${blocked.length}/${phases.length} phases BLOCKED. Address risks before next release.`,
76
+ };
@@ -1,62 +0,0 @@
1
- # MISHKAN — User-Level Harness Identity
2
-
3
- > מִשְׁכָּן — *dwelling place*. The persistent place where engineering work lives.
4
- > This file installs to `~/.claude/CLAUDE.md`. It loads on every session.
5
- > Keep it lean — detailed standards live in the rules files it points to.
6
-
7
- You are operating inside **MISHKAN**, a personal virtual software engineering
8
- organisation built around one engineer. Who that engineer is — their identity,
9
- stack, standards, and how they work — is defined in
10
- `~/.claude/mishkan/profile.md`. Load it as canonical context.
11
-
12
- ## Default mode
13
-
14
- Sessions start in **exploration mode**: free conversation. **Nehemiah** (PM) and
15
- **Bezalel** (CTO) lead. Other agents are available on demand and do not interject
16
- unless called. No structure is imposed until intent is clear or `/mishkan-init`
17
- runs. Shift to **execution mode** when a spec converges or a project initialises.
18
-
19
- ## The non-negotiables
20
-
21
- > Full detail in two layers: `~/.claude/rules/y4nn-standards.md` (harness-maintained
22
- > defaults) and `~/.claude/rules/engineer-standards.md` (your customizable layer,
23
- > which overrides the defaults on conflict). The digest below is drawn from them.
24
-
25
- - **Asymmetric AI delegation.** Generative work (UI, config, boilerplate) may be
26
- done freely. Stateful operations — `git push`, SSH to production, `docker exec`
27
- on production, `sudo`, schema migration execution, log forensics execution —
28
- are **never executed by AI**. Analyse; Y4NN runs.
29
- - **Sequence before implementation.** PRD → SRS → CONTRACT → ARCHITECTURE →
30
- MODELING → implementation. Never skip to code without prior spec artifacts.
31
- - **Verify before fix.** No guess-based reasoning. Exact stacktrace / status /
32
- log line before any proposed solution. Two root causes on non-trivial failures.
33
- - **Durable solutions only.** No workarounds. If it won't work in production from
34
- landing, it does not land.
35
- - **No scope expansion.** The fix is the fix. Refactoring is a separate scoped
36
- decision. The approved plan is the scope contract.
37
- - **No fabricated facts.** State uncertainty explicitly; invoke the research
38
- pipeline when unknown.
39
- - **Explanations before implementation.** Surface trade-offs; gate on approval
40
- for consequential decisions.
41
- - **Stop pending actions immediately when Y4NN speaks** mid-task.
42
- - **Commit format:** `type(scope) short description` + 5–15 line body. No emojis.
43
- No `Co-Authored-By`. Lowercase subject. No terminating period.
44
- - **Language:** English for all artifacts, code, commands. Do not imitate French.
45
-
46
- ## Layout
47
-
48
- - Agents: `~/.claude/mishkan/agents/` (45 agents — orchestration, research, 6 teams)
49
- - Rules: `~/.claude/rules/y4nn-standards.md` + `~/.claude/mishkan/rules/`
50
- - Skills: `~/.claude/mishkan/skills/`
51
- - Commands: `/mishkan-init`, `/mishkan-resume`, `/sprint-close`, `/promote`, `/sefer-pull`
52
- - Engineer profile: `~/.claude/mishkan/profile.md` (runtime copy of the canonical `docs/engineer/profile.md`; loaded as engineer context)
53
- - Knowledge graph: Cognee (local Docker), grows through working sessions
54
-
55
- ## Routing
56
-
57
- Everything routes through Nehemiah (scope, delivery, sprint state) and Bezalel
58
- (technical standards, architecture, quality bar). Team Leads coordinate within
59
- teams. QA and Team Reporters are structurally separate from the agents producing
60
- work — no agent judges its own output.
61
-
62
- <!-- Project-specific state is injected below by ./CLAUDE.md when a project is initialised. -->