musubix2 0.5.61 → 0.5.63

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.
@@ -1 +1 @@
1
- {"generator":"musubix2","version":"0.5.61","timestamp":"2026-07-10T13:50:25.916Z"}
1
+ {"generator":"musubix2","version":"0.5.63","timestamp":"2026-07-10T20:51:46.354Z"}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": "1.0",
3
- "generatedAt": "2026-07-10T13:50:25.947Z",
3
+ "generatedAt": "2026-07-10T20:51:46.385Z",
4
4
  "entries": [
5
5
  {
6
6
  "platform": "copilot",
package/dist/cli.js CHANGED
@@ -2808,7 +2808,8 @@ var init_design = __esm({
2808
2808
  if (reqs.length === 1) {
2809
2809
  const r = reqs[0];
2810
2810
  const sig = deriveMethodSignature(r.text, r.title);
2811
- const compName = this.pascal(r.title || sig.name) + (/(service|manager|controller|repository)$/i.test(r.title) ? "" : "Service");
2811
+ const base = this.pascal(r.title) || this.pascal(sig.name) || "Component";
2812
+ const compName = base + (/(service|manager|controller|repository)$/i.test(base) ? "" : "Service");
2812
2813
  return [{
2813
2814
  name: compName,
2814
2815
  responsibility: r.title || `Handle ${r.id}`,
@@ -2863,12 +2864,10 @@ var init_design = __esm({
2863
2864
  }
2864
2865
  return [...entities];
2865
2866
  }
2867
+ /** PascalCase the ASCII words of a string; returns '' if none survive (e.g. a
2868
+ * fully non-ASCII title), so callers can fall back to another name source. */
2866
2869
  pascal(text) {
2867
- const words = text.split(/[\s_\-,、]+/).filter(Boolean);
2868
- if (words.length === 0) {
2869
- return "Component";
2870
- }
2871
- return words.map((w) => w.replace(/[^A-Za-z0-9]/g, "")).filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
2870
+ return text.split(/[\s_\-,、]+/).map((w) => w.replace(/[^A-Za-z0-9]/g, "")).filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
2872
2871
  }
2873
2872
  suggestInterfaces(reqs) {
2874
2873
  const interfaces = [];
@@ -12588,7 +12587,7 @@ var init_dist14 = __esm({
12588
12587
  warnings
12589
12588
  };
12590
12589
  }
12591
- const variables = extractVariables(requirement.text);
12590
+ const symbols = /* @__PURE__ */ new Set([actionVar]);
12592
12591
  const negated = /\bSHALL\s+NOT\b/i.test(requirement.text);
12593
12592
  const actionExpr = negated ? `(not ${actionVar})` : actionVar;
12594
12593
  let assertions;
@@ -12598,6 +12597,7 @@ var init_dist14 = __esm({
12598
12597
  break;
12599
12598
  case "event-driven": {
12600
12599
  const triggerVar = requirement.trigger ? sanitizeName(requirement.trigger) : "trigger";
12600
+ symbols.add(triggerVar);
12601
12601
  assertions = [`(assert (=> ${triggerVar} ${actionExpr}))`];
12602
12602
  if (!requirement.trigger) {
12603
12603
  warnings.push('No trigger specified; using default "trigger"');
@@ -12606,6 +12606,7 @@ var init_dist14 = __esm({
12606
12606
  }
12607
12607
  case "state-driven": {
12608
12608
  const condVar = requirement.condition ? sanitizeName(requirement.condition) : "condition";
12609
+ symbols.add(condVar);
12609
12610
  assertions = [`(assert (=> ${condVar} ${actionExpr}))`];
12610
12611
  if (!requirement.condition) {
12611
12612
  warnings.push('No condition specified; using default "condition"');
@@ -12614,13 +12615,16 @@ var init_dist14 = __esm({
12614
12615
  }
12615
12616
  case "unwanted":
12616
12617
  if (requirement.condition) {
12617
- assertions = [`(assert (=> ${sanitizeName(requirement.condition)} ${actionExpr}))`];
12618
+ const condVar = sanitizeName(requirement.condition);
12619
+ symbols.add(condVar);
12620
+ assertions = [`(assert (=> ${condVar} ${actionExpr}))`];
12618
12621
  } else {
12619
12622
  assertions = [`(assert ${actionExpr})`];
12620
12623
  }
12621
12624
  break;
12622
12625
  case "optional": {
12623
12626
  const featureVar = requirement.trigger ? sanitizeName(requirement.trigger) : "feature_enabled";
12627
+ symbols.add(featureVar);
12624
12628
  assertions = [`(assert (=> ${featureVar} ${actionExpr}))`];
12625
12629
  if (!requirement.trigger) {
12626
12630
  warnings.push('No feature trigger specified; using default "feature_enabled"');
@@ -12630,6 +12634,8 @@ var init_dist14 = __esm({
12630
12634
  case "complex": {
12631
12635
  const condVar = requirement.condition ? sanitizeName(requirement.condition) : "condition";
12632
12636
  const triggerVar = requirement.trigger ? sanitizeName(requirement.trigger) : "trigger";
12637
+ symbols.add(condVar);
12638
+ symbols.add(triggerVar);
12633
12639
  assertions = [`(assert (=> (and ${condVar} ${triggerVar}) ${actionExpr}))`];
12634
12640
  if (!requirement.condition) {
12635
12641
  warnings.push('No condition specified; using default "condition"');
@@ -12641,6 +12647,7 @@ var init_dist14 = __esm({
12641
12647
  }
12642
12648
  }
12643
12649
  const id = makeFormulaId(requirement.id);
12650
+ const variables = [...symbols].map((name) => ({ name, sort: "Bool" }));
12644
12651
  const formula = {
12645
12652
  id,
12646
12653
  requirementId: requirement.id,
@@ -12777,7 +12784,7 @@ var init_dist14 = __esm({
12777
12784
  mockSolve(smtScript) {
12778
12785
  const start = Date.now();
12779
12786
  try {
12780
- if (smtScript.includes("(assert false)")) {
12787
+ if (smtScript.includes("(assert false)") || this.hasBooleanContradiction(smtScript)) {
12781
12788
  return {
12782
12789
  status: "unsat",
12783
12790
  time: Date.now() - start
@@ -12802,6 +12809,38 @@ var init_dist14 = __esm({
12802
12809
  };
12803
12810
  }
12804
12811
  }
12812
+ /**
12813
+ * Detect a basic Boolean contradiction without a real solver: an atom that is
12814
+ * *unconditionally* forced both true and false. Covers the common EARS cases —
12815
+ * `SHALL X` (`(=> true X)` or `(assert X)`) versus `SHALL NOT X`
12816
+ * (`(not X)`) — so `verify` flags e.g. "grant access" / "not grant access".
12817
+ * Conditional assertions (`(=> cond X)` with cond ≠ true) are not forcing and
12818
+ * are ignored. This is a sound-but-incomplete check (real z3 covers the rest).
12819
+ */
12820
+ hasBooleanContradiction(smtScript) {
12821
+ const forced = /* @__PURE__ */ new Map();
12822
+ let contradiction = false;
12823
+ const set = (atom, value) => {
12824
+ if (forced.has(atom) && forced.get(atom) !== value) {
12825
+ contradiction = true;
12826
+ }
12827
+ forced.set(atom, value);
12828
+ };
12829
+ for (const raw of smtScript.split("\n")) {
12830
+ const line = raw.trim();
12831
+ let m;
12832
+ if (m = /^\(assert \(=> true \(not (\w+)\)\)\)$/.exec(line)) {
12833
+ set(m[1], false);
12834
+ } else if (m = /^\(assert \(=> true (\w+)\)\)$/.exec(line)) {
12835
+ set(m[1], true);
12836
+ } else if (m = /^\(assert \(not (\w+)\)\)$/.exec(line)) {
12837
+ set(m[1], false);
12838
+ } else if (m = /^\(assert (\w+)\)$/.exec(line)) {
12839
+ set(m[1], true);
12840
+ }
12841
+ }
12842
+ return contradiction;
12843
+ }
12805
12844
  };
12806
12845
  PreconditionVerifier = class {
12807
12846
  async verify(requirement, converter, solver) {
package/dist/index.js CHANGED
@@ -2808,7 +2808,8 @@ var init_design = __esm({
2808
2808
  if (reqs.length === 1) {
2809
2809
  const r = reqs[0];
2810
2810
  const sig = deriveMethodSignature(r.text, r.title);
2811
- const compName = this.pascal(r.title || sig.name) + (/(service|manager|controller|repository)$/i.test(r.title) ? "" : "Service");
2811
+ const base = this.pascal(r.title) || this.pascal(sig.name) || "Component";
2812
+ const compName = base + (/(service|manager|controller|repository)$/i.test(base) ? "" : "Service");
2812
2813
  return [{
2813
2814
  name: compName,
2814
2815
  responsibility: r.title || `Handle ${r.id}`,
@@ -2863,12 +2864,10 @@ var init_design = __esm({
2863
2864
  }
2864
2865
  return [...entities];
2865
2866
  }
2867
+ /** PascalCase the ASCII words of a string; returns '' if none survive (e.g. a
2868
+ * fully non-ASCII title), so callers can fall back to another name source. */
2866
2869
  pascal(text) {
2867
- const words = text.split(/[\s_\-,、]+/).filter(Boolean);
2868
- if (words.length === 0) {
2869
- return "Component";
2870
- }
2871
- return words.map((w) => w.replace(/[^A-Za-z0-9]/g, "")).filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
2870
+ return text.split(/[\s_\-,、]+/).map((w) => w.replace(/[^A-Za-z0-9]/g, "")).filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
2872
2871
  }
2873
2872
  suggestInterfaces(reqs) {
2874
2873
  const interfaces = [];
@@ -12588,7 +12587,7 @@ var init_dist14 = __esm({
12588
12587
  warnings
12589
12588
  };
12590
12589
  }
12591
- const variables = extractVariables(requirement.text);
12590
+ const symbols = /* @__PURE__ */ new Set([actionVar]);
12592
12591
  const negated = /\bSHALL\s+NOT\b/i.test(requirement.text);
12593
12592
  const actionExpr = negated ? `(not ${actionVar})` : actionVar;
12594
12593
  let assertions;
@@ -12598,6 +12597,7 @@ var init_dist14 = __esm({
12598
12597
  break;
12599
12598
  case "event-driven": {
12600
12599
  const triggerVar = requirement.trigger ? sanitizeName(requirement.trigger) : "trigger";
12600
+ symbols.add(triggerVar);
12601
12601
  assertions = [`(assert (=> ${triggerVar} ${actionExpr}))`];
12602
12602
  if (!requirement.trigger) {
12603
12603
  warnings.push('No trigger specified; using default "trigger"');
@@ -12606,6 +12606,7 @@ var init_dist14 = __esm({
12606
12606
  }
12607
12607
  case "state-driven": {
12608
12608
  const condVar = requirement.condition ? sanitizeName(requirement.condition) : "condition";
12609
+ symbols.add(condVar);
12609
12610
  assertions = [`(assert (=> ${condVar} ${actionExpr}))`];
12610
12611
  if (!requirement.condition) {
12611
12612
  warnings.push('No condition specified; using default "condition"');
@@ -12614,13 +12615,16 @@ var init_dist14 = __esm({
12614
12615
  }
12615
12616
  case "unwanted":
12616
12617
  if (requirement.condition) {
12617
- assertions = [`(assert (=> ${sanitizeName(requirement.condition)} ${actionExpr}))`];
12618
+ const condVar = sanitizeName(requirement.condition);
12619
+ symbols.add(condVar);
12620
+ assertions = [`(assert (=> ${condVar} ${actionExpr}))`];
12618
12621
  } else {
12619
12622
  assertions = [`(assert ${actionExpr})`];
12620
12623
  }
12621
12624
  break;
12622
12625
  case "optional": {
12623
12626
  const featureVar = requirement.trigger ? sanitizeName(requirement.trigger) : "feature_enabled";
12627
+ symbols.add(featureVar);
12624
12628
  assertions = [`(assert (=> ${featureVar} ${actionExpr}))`];
12625
12629
  if (!requirement.trigger) {
12626
12630
  warnings.push('No feature trigger specified; using default "feature_enabled"');
@@ -12630,6 +12634,8 @@ var init_dist14 = __esm({
12630
12634
  case "complex": {
12631
12635
  const condVar = requirement.condition ? sanitizeName(requirement.condition) : "condition";
12632
12636
  const triggerVar = requirement.trigger ? sanitizeName(requirement.trigger) : "trigger";
12637
+ symbols.add(condVar);
12638
+ symbols.add(triggerVar);
12633
12639
  assertions = [`(assert (=> (and ${condVar} ${triggerVar}) ${actionExpr}))`];
12634
12640
  if (!requirement.condition) {
12635
12641
  warnings.push('No condition specified; using default "condition"');
@@ -12641,6 +12647,7 @@ var init_dist14 = __esm({
12641
12647
  }
12642
12648
  }
12643
12649
  const id = makeFormulaId(requirement.id);
12650
+ const variables = [...symbols].map((name) => ({ name, sort: "Bool" }));
12644
12651
  const formula = {
12645
12652
  id,
12646
12653
  requirementId: requirement.id,
@@ -12777,7 +12784,7 @@ var init_dist14 = __esm({
12777
12784
  mockSolve(smtScript) {
12778
12785
  const start = Date.now();
12779
12786
  try {
12780
- if (smtScript.includes("(assert false)")) {
12787
+ if (smtScript.includes("(assert false)") || this.hasBooleanContradiction(smtScript)) {
12781
12788
  return {
12782
12789
  status: "unsat",
12783
12790
  time: Date.now() - start
@@ -12802,6 +12809,38 @@ var init_dist14 = __esm({
12802
12809
  };
12803
12810
  }
12804
12811
  }
12812
+ /**
12813
+ * Detect a basic Boolean contradiction without a real solver: an atom that is
12814
+ * *unconditionally* forced both true and false. Covers the common EARS cases —
12815
+ * `SHALL X` (`(=> true X)` or `(assert X)`) versus `SHALL NOT X`
12816
+ * (`(not X)`) — so `verify` flags e.g. "grant access" / "not grant access".
12817
+ * Conditional assertions (`(=> cond X)` with cond ≠ true) are not forcing and
12818
+ * are ignored. This is a sound-but-incomplete check (real z3 covers the rest).
12819
+ */
12820
+ hasBooleanContradiction(smtScript) {
12821
+ const forced = /* @__PURE__ */ new Map();
12822
+ let contradiction = false;
12823
+ const set = (atom, value) => {
12824
+ if (forced.has(atom) && forced.get(atom) !== value) {
12825
+ contradiction = true;
12826
+ }
12827
+ forced.set(atom, value);
12828
+ };
12829
+ for (const raw of smtScript.split("\n")) {
12830
+ const line = raw.trim();
12831
+ let m;
12832
+ if (m = /^\(assert \(=> true \(not (\w+)\)\)\)$/.exec(line)) {
12833
+ set(m[1], false);
12834
+ } else if (m = /^\(assert \(=> true (\w+)\)\)$/.exec(line)) {
12835
+ set(m[1], true);
12836
+ } else if (m = /^\(assert \(not (\w+)\)\)$/.exec(line)) {
12837
+ set(m[1], false);
12838
+ } else if (m = /^\(assert (\w+)\)$/.exec(line)) {
12839
+ set(m[1], true);
12840
+ }
12841
+ }
12842
+ return contradiction;
12843
+ }
12805
12844
  };
12806
12845
  PreconditionVerifier = class {
12807
12846
  async verify(requirement, converter, solver) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "musubix2",
3
- "version": "0.5.61",
3
+ "version": "0.5.63",
4
4
  "description": "MUSUBIX2 — Specification Driven Development System",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",