speclock 5.4.0 → 5.4.1
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/package.json +1 -1
- package/src/cli/index.js +1 -1
- package/src/core/compliance.js +1 -1
- package/src/core/drift-score.js +24 -16
- package/src/dashboard/index.html +2 -2
- package/src/mcp/http-server.js +1 -1
- package/src/mcp/server.js +1 -1
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
"name": "speclock",
|
|
4
4
|
|
|
5
|
-
"version": "5.4.
|
|
5
|
+
"version": "5.4.1",
|
|
6
6
|
|
|
7
7
|
"description": "AI Constraint Engine by Sandeep Roy — Universal Rules Sync (one command syncs constraints to Cursor, Claude Code, Copilot, Windsurf, Gemini, Aider, AGENTS.md). AI Patch Firewall, diff-native review, Patch Gateway (ALLOW/WARN/BLOCK), Spec Compiler (NL→constraints), Code Graph (blast radius), Typed constraints, REST API v2, Python SDK, ROS2 integration. 49 MCP tools, Gemini LLM hybrid, HMAC audit chain, RBAC, encryption, SOC 2/HIPAA compliance. Developed by Sandeep Roy.",
|
|
8
8
|
|
package/src/cli/index.js
CHANGED
|
@@ -122,7 +122,7 @@ function refreshContext(root) {
|
|
|
122
122
|
|
|
123
123
|
function printHelp() {
|
|
124
124
|
console.log(`
|
|
125
|
-
SpecLock v5.4.
|
|
125
|
+
SpecLock v5.4.1 — AI Constraint Engine (Universal Rules Sync + Spec Compiler + Code Graph + Typed Constraints + Python SDK + ROS2 + REST API v2 + Gemini LLM + Policy-as-Code + Auth + RBAC + Encryption)
|
|
126
126
|
Developed by Sandeep Roy (github.com/sgroy10)
|
|
127
127
|
|
|
128
128
|
Usage: speclock <command> [options]
|
package/src/core/compliance.js
CHANGED
package/src/core/drift-score.js
CHANGED
|
@@ -39,17 +39,21 @@ export function computeDriftScore(root, options = {}) {
|
|
|
39
39
|
|
|
40
40
|
// --- Signal 1: Violation Rate (0-30 points) ---
|
|
41
41
|
// How often did the AI hit constraints?
|
|
42
|
-
|
|
42
|
+
// Violations are stored in brain.state.violations by the conflict checker
|
|
43
|
+
const allViolations = brain.state.violations || [];
|
|
44
|
+
const violations = allViolations.filter((v) => v.at >= cutoff);
|
|
45
|
+
// Also count from events for backward compatibility
|
|
46
|
+
const eventViolations = recentEvents.filter(
|
|
43
47
|
(e) => e.type === "conflict_blocked" || e.type === "conflict_warned" ||
|
|
44
48
|
(e.summary && (e.summary.includes("CONFLICT") || e.summary.includes("BLOCK")))
|
|
45
49
|
);
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
const totalViolations = Math.max(violations.length, eventViolations.length);
|
|
51
|
+
// Estimate total checks: violations + safe checks (from events)
|
|
52
|
+
const safeChecks = recentEvents.filter(
|
|
53
|
+
(e) => e.type === "conflict_checked"
|
|
54
|
+
).length;
|
|
55
|
+
const totalChecks = totalViolations + safeChecks || 1;
|
|
56
|
+
const violationRate = (totalViolations / totalChecks) * 100;
|
|
53
57
|
// 0% violations = 0 drift, 50%+ = 30 drift
|
|
54
58
|
const violationScore = Math.min(30, Math.round(violationRate * 0.6));
|
|
55
59
|
|
|
@@ -105,10 +109,16 @@ export function computeDriftScore(root, options = {}) {
|
|
|
105
109
|
const sessionEvents = recentEvents.filter(
|
|
106
110
|
(e) => e.at >= s.startedAt && (s.endedAt ? e.at <= s.endedAt : true)
|
|
107
111
|
);
|
|
108
|
-
|
|
112
|
+
// Check both brain.state.violations and events for this session window
|
|
113
|
+
const brainViolationsInSession = allViolations.filter(
|
|
114
|
+
(v) => v.at >= s.startedAt && (s.endedAt ? v.at <= s.endedAt : true)
|
|
115
|
+
);
|
|
116
|
+
const eventViolationsInSession = sessionEvents.filter(
|
|
109
117
|
(e) => e.type === "conflict_blocked" || e.type === "conflict_warned" ||
|
|
110
118
|
(e.summary && e.summary.includes("CONFLICT"))
|
|
111
119
|
);
|
|
120
|
+
const sessionViolations = brainViolationsInSession.length > eventViolationsInSession.length
|
|
121
|
+
? brainViolationsInSession : eventViolationsInSession;
|
|
112
122
|
const sessionOverrides = sessionEvents.filter(
|
|
113
123
|
(e) => e.type === "override_applied"
|
|
114
124
|
);
|
|
@@ -128,12 +138,10 @@ export function computeDriftScore(root, options = {}) {
|
|
|
128
138
|
const midpoint = new Date(Date.now() - (days / 2) * 86400000).toISOString();
|
|
129
139
|
const firstHalf = recentEvents.filter((e) => e.at < midpoint);
|
|
130
140
|
const secondHalf = recentEvents.filter((e) => e.at >= midpoint);
|
|
131
|
-
const firstViolations =
|
|
132
|
-
(e) => e.type === "conflict_blocked" || e.type === "conflict_warned"
|
|
133
|
-
).length
|
|
134
|
-
|
|
135
|
-
(e) => e.type === "conflict_blocked" || e.type === "conflict_warned"
|
|
136
|
-
).length;
|
|
141
|
+
const firstViolations = allViolations.filter((v) => v.at >= cutoff && v.at < midpoint).length
|
|
142
|
+
+ firstHalf.filter((e) => e.type === "conflict_blocked" || e.type === "conflict_warned").length;
|
|
143
|
+
const secondViolations = allViolations.filter((v) => v.at >= midpoint).length
|
|
144
|
+
+ secondHalf.filter((e) => e.type === "conflict_blocked" || e.type === "conflict_warned").length;
|
|
137
145
|
|
|
138
146
|
let trend;
|
|
139
147
|
if (firstViolations === 0 && secondViolations === 0) trend = "stable";
|
|
@@ -148,7 +156,7 @@ export function computeDriftScore(root, options = {}) {
|
|
|
148
156
|
trend,
|
|
149
157
|
period: `${days} days`,
|
|
150
158
|
signals: {
|
|
151
|
-
violations: { score: violationScore, max: 30, count:
|
|
159
|
+
violations: { score: violationScore, max: 30, count: totalViolations, total: totalChecks },
|
|
152
160
|
overrides: { score: overrideScore, max: 20, count: overrides.length },
|
|
153
161
|
reverts: { score: revertScore, max: 15, count: reverts.length },
|
|
154
162
|
lockChurn: { score: churnScore, max: 15, removed: locksRemoved.length, added: locksAdded.length },
|
package/src/dashboard/index.html
CHANGED
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
<div class="header">
|
|
90
90
|
<div>
|
|
91
91
|
<h1><span>SpecLock</span> Dashboard</h1>
|
|
92
|
-
<div class="meta">v5.4.
|
|
92
|
+
<div class="meta">v5.4.1 — AI Constraint Engine</div>
|
|
93
93
|
</div>
|
|
94
94
|
<div style="display:flex;align-items:center;gap:12px;">
|
|
95
95
|
<span id="health-badge" class="status-badge healthy">Loading...</span>
|
|
@@ -182,7 +182,7 @@
|
|
|
182
182
|
</div>
|
|
183
183
|
|
|
184
184
|
<div style="text-align:center;padding:24px;color:var(--muted);font-size:12px;">
|
|
185
|
-
SpecLock v5.4.
|
|
185
|
+
SpecLock v5.4.1 — Developed by Sandeep Roy — <a href="https://github.com/sgroy10/speclock" style="color:var(--accent)">GitHub</a>
|
|
186
186
|
</div>
|
|
187
187
|
|
|
188
188
|
<script>
|
package/src/mcp/http-server.js
CHANGED
|
@@ -113,7 +113,7 @@ import { fileURLToPath } from "url";
|
|
|
113
113
|
import _path from "path";
|
|
114
114
|
|
|
115
115
|
const PROJECT_ROOT = process.env.SPECLOCK_PROJECT_ROOT || process.cwd();
|
|
116
|
-
const VERSION = "5.4.
|
|
116
|
+
const VERSION = "5.4.1";
|
|
117
117
|
const AUTHOR = "Sandeep Roy";
|
|
118
118
|
const START_TIME = Date.now();
|
|
119
119
|
|
package/src/mcp/server.js
CHANGED
|
@@ -125,7 +125,7 @@ const PROJECT_ROOT =
|
|
|
125
125
|
args.project || process.env.SPECLOCK_PROJECT_ROOT || process.cwd();
|
|
126
126
|
|
|
127
127
|
// --- MCP Server ---
|
|
128
|
-
const VERSION = "5.4.
|
|
128
|
+
const VERSION = "5.4.1";
|
|
129
129
|
const AUTHOR = "Sandeep Roy";
|
|
130
130
|
|
|
131
131
|
const server = new McpServer(
|