deadbyte-mcp 0.15.21 → 0.15.23

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 (54) hide show
  1. package/MANIFEST.SHA256 +53 -28
  2. package/PROVENANCE.json +10 -10
  3. package/README.txt +18 -7
  4. package/RELEASE.json +51 -16
  5. package/SBOM.cdx.json +33 -7
  6. package/TEST-MATRIX.json +95 -18
  7. package/bin/deadbyte-hardware-observer-r45.exe +0 -0
  8. package/bin/deadbyte-hardware-observer-r45.obj +0 -0
  9. package/docs/R48-FULL-TOOL-CATALOG.json +124 -0
  10. package/native/deadbyte-hardware-observer.cpp +535 -12
  11. package/package.json +95 -93
  12. package/scripts/activate-skill-creator-1.1.1.mjs +46 -0
  13. package/scripts/build-advanced-pcb-skill.mjs +208 -0
  14. package/scripts/build-hardware-r45.ps1 +19 -0
  15. package/scripts/diagnose-skill-tree.mjs +8 -0
  16. package/scripts/full-regression-r48-batch.mjs +27 -0
  17. package/scripts/npm-release-r48.mjs +252 -0
  18. package/scripts/pcb-skill-src-SKILL.md.txt +67 -0
  19. package/scripts/pcb-skill-src-bringup-evidence.md.txt +22 -0
  20. package/scripts/pcb-skill-src-constraint-model.md.txt +17 -0
  21. package/scripts/pcb-skill-src-emc-mechanical-dfm.md.txt +17 -0
  22. package/scripts/pcb-skill-src-high-speed-layout.md.txt +27 -0
  23. package/scripts/pcb-skill-src-kicad-native.md.txt +30 -0
  24. package/scripts/pcb-skill-src-kicad_pcb_probe.py.txt +159 -0
  25. package/scripts/pcb-skill-src-pcb_constraint_gate.py.txt +76 -0
  26. package/scripts/pcb-skill-src-power-integrity.md.txt +15 -0
  27. package/scripts/prepare-skill-creator-1.1.1.mjs +113 -0
  28. package/scripts/promote-local-c-generation.ps1 +217 -0
  29. package/scripts/promote-local-d-generation.ps1 +217 -0
  30. package/scripts/test-r48-post-bump.ps1 +8 -0
  31. package/scripts/test-r48-windows-observation.ps1 +8 -0
  32. package/scripts/test-skill-creator-semver-matrix.mjs +30 -0
  33. package/scripts/test-skill-creator-semver.mjs +22 -0
  34. package/src/capability-ledger.mjs +7 -0
  35. package/src/capability-resolver.mjs +7 -0
  36. package/src/compact-gateway-mcp-tools.mjs +16 -2
  37. package/src/deadbyte-cli.mjs +70 -15
  38. package/src/machine-fs.mjs +16 -3
  39. package/src/observation-mcp-tools.mjs +13 -1
  40. package/src/observation-output-contracts.mjs +8 -0
  41. package/src/observation-runtime.mjs +54 -2
  42. package/src/remote-live-log.mjs +189 -35
  43. package/src/tool-audit.mjs +1 -1
  44. package/src/version.mjs +1 -1
  45. package/test/full-max-catalog-r47.test.mjs +1 -1
  46. package/test/full-max-catalog-r48.test.mjs +78 -0
  47. package/test/machine-fs-runtime.test.mjs +28 -3
  48. package/test/observation-mcp-tools.test.mjs +4 -3
  49. package/test/r48-release-contract.test.mjs +24 -14
  50. package/test/remote-live-log-r37.test.mjs +106 -7
  51. package/test/remote-session-cli.test.mjs +40 -0
  52. package/test/tool-audit.test.mjs +17 -0
  53. package/test/windows-observation-r48.test.mjs +63 -0
  54. package/test/windows-observation-surface-r48.test.mjs +70 -0
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env python3
2
+ import json, sys
3
+
4
+ def load(path):
5
+ with open(path, "r", encoding="utf-8") as f:
6
+ return json.load(f)
7
+
8
+ def main():
9
+ if len(sys.argv) != 3:
10
+ raise SystemExit("usage: pcb_constraint_gate.py board-probe.json board-contract.json")
11
+ probe = load(sys.argv[1])
12
+ contract = load(sys.argv[2])
13
+ if probe.get("schema") != "deadbyte.kicad-pcb-probe.v1":
14
+ raise SystemExit("unsupported probe schema")
15
+
16
+ checks = []
17
+ def add(rule, ok, observed, required):
18
+ checks.append({"rule": rule, "ok": bool(ok), "observed": observed, "required": required})
19
+
20
+ if "required_layers" in contract:
21
+ observed = set(x.get("name") for x in probe.get("layers", []))
22
+ req = list(contract["required_layers"])
23
+ add("required_layers", all(x in observed for x in req), sorted(observed), req)
24
+
25
+ if "min_copper_layers" in contract:
26
+ observed = probe.get("copper_layer_count")
27
+ req = int(contract["min_copper_layers"])
28
+ add("min_copper_layers", isinstance(observed, int) and observed >= req, observed, req)
29
+
30
+ for key, probe_key in [
31
+ ("min_track_width_mm", "track_width_mm"),
32
+ ("min_via_diameter_mm", "via_diameter_mm"),
33
+ ("min_via_drill_mm", "via_drill_mm"),
34
+ ]:
35
+ if key in contract:
36
+ observed = (probe.get(probe_key) or {}).get("min")
37
+ req = float(contract[key])
38
+ add(key, observed is not None and float(observed) >= req, observed, req)
39
+
40
+ if "required_nets" in contract:
41
+ observed = set(probe.get("nets", []))
42
+ req = list(contract["required_nets"])
43
+ add("required_nets", all(x in observed for x in req), sorted(observed), req)
44
+
45
+ for key, count_key in [
46
+ ("min_footprints", "footprints"),
47
+ ("min_segments", "segments"),
48
+ ("min_vias", "vias"),
49
+ ("min_zones", "zones"),
50
+ ]:
51
+ if key in contract:
52
+ observed = (probe.get("counts") or {}).get(count_key)
53
+ req = int(contract[key])
54
+ add(key, isinstance(observed, int) and observed >= req, observed, req)
55
+
56
+ allowed = {
57
+ "required_layers", "min_copper_layers", "min_track_width_mm",
58
+ "min_via_diameter_mm", "min_via_drill_mm", "required_nets",
59
+ "min_footprints", "min_segments", "min_vias", "min_zones"
60
+ }
61
+ unknown = sorted(set(contract) - allowed)
62
+ if unknown:
63
+ checks.append({"rule": "unknown_contract_keys", "ok": False, "observed": unknown, "required": "no unsupported keys"})
64
+
65
+ passed = all(x["ok"] for x in checks)
66
+ out = {
67
+ "schema": "deadbyte.pcb-constraint-gate.v1",
68
+ "status": "PASS" if passed else "FAIL",
69
+ "probe_sha256": probe.get("sha256"),
70
+ "checks": checks
71
+ }
72
+ print(json.dumps(out, indent=2, sort_keys=True))
73
+ raise SystemExit(0 if passed else 2)
74
+
75
+ if __name__ == "__main__":
76
+ main()
@@ -0,0 +1,15 @@
1
+ # Power Integrity and Thermal
2
+
3
+ Power integrity begins with current loops, not capacitor count.
4
+
5
+ For each regulator trace the input current path, keep the high di/dt input loop compact, bound switch-node copper, place bootstrap/gate-drive parts by topology, protect the feedback sense path, close the output-current loop, and provide a credible thermal path.
6
+
7
+ For decoupling separate bulk-energy storage from local high-frequency charge. Account for capacitor mounting inductance, vias, planes, and package inductance. Printed capacitance alone does not prove useful impedance at frequency.
8
+
9
+ For planes and pours verify neck-downs at pads, vias, connectors, and thermal spokes; identify slots caused by keepouts and antipads; preserve return continuity; and check both DC drop and transient current paths.
10
+
11
+ For sensitive analog control where digital and switching currents return. Do not split ground reflexively. Placement, loop control, filtering, and current-path ownership matter more than labels.
12
+
13
+ Thermal claims require dissipation, package thermal path, copper geometry, ambient, airflow, and enclosure assumptions. Missing inputs mean thermal margin UNKNOWN.
14
+
15
+ Bench evidence should include rail voltage, ripple/noise with correct probing, startup sequencing, relevant load transient behavior, and regulator temperature under representative load.
@@ -0,0 +1,113 @@
1
+ import { readFile, writeFile, mkdir, rm, cp } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import os from "node:os";
4
+ import { promisify } from "node:util";
5
+ import { execFile } from "node:child_process";
6
+ import { createHash, createPrivateKey, createPublicKey, sign } from "node:crypto";
7
+ import { createSkillPlane } from "../src/skill-plane-r44.mjs";
8
+
9
+ const ex = promisify(execFile);
10
+ const home = os.homedir();
11
+ const stateDir = path.join(home, ".deadbyte-mcp");
12
+ const skillsRoot = path.join(stateDir, "skills");
13
+ const active = path.join(skillsRoot, "skill-creator");
14
+ const expectedOldManifest = "f34b3953db176c8ae9a5e08e7702b39f12b60b013edcd2f4d63a6268af9ba432";
15
+ const oldEnv = JSON.parse(await readFile(path.join(active, "skill.json"), "utf8"));
16
+ if (oldEnv.manifest_sha256 !== expectedOldManifest) {
17
+ throw new Error("unexpected active skill-creator manifest: " + oldEnv.manifest_sha256);
18
+ }
19
+ if (oldEnv.body.version !== "1.1.0") throw new Error("expected active skill-creator 1.1.0");
20
+
21
+ const history = path.join(stateDir, "skill-history", "skill-creator", "1.1.0-" + expectedOldManifest);
22
+ await mkdir(path.dirname(history), {recursive:true});
23
+ try {
24
+ const prior = JSON.parse(await readFile(path.join(history, "skill.json"), "utf8"));
25
+ if (prior.manifest_sha256 !== expectedOldManifest) throw new Error("history path collision");
26
+ } catch (e) {
27
+ if (e.code !== "ENOENT") throw e;
28
+ await cp(active, history, {recursive:true, errorOnExist:true});
29
+ }
30
+ const archived = JSON.parse(await readFile(path.join(history, "skill.json"), "utf8"));
31
+ if (archived.manifest_sha256 !== expectedOldManifest) throw new Error("archive manifest mismatch");
32
+
33
+ const stageParent = path.join(stateDir, "skill-staging", "skill-creator-1.1.1");
34
+ const stage = path.join(stageParent, "skill-creator");
35
+ await rm(stageParent, {recursive:true, force:true});
36
+ await mkdir(stageParent, {recursive:true});
37
+ await cp(active, stage, {recursive:true});
38
+ await rm(path.join(stage, "references", "skills_cache.json"), {force:true});
39
+
40
+ const createPath = path.join(stage, "scripts", "create-skill.mjs");
41
+ const oldCreate = await readFile(createPath, "utf8");
42
+ const oldRegex = "const VERSION=/^[0-9]+\\\\.[0-9]+\\\\.[0-9]+(?:-[A-Za-z0-9.-]+)?$/;";
43
+ const newRegex = "const VERSION=/^[0-9]+\\.[0-9]+\\.[0-9]+(?:-[A-Za-z0-9.-]+)?$/;";
44
+ if (!oldCreate.includes(oldRegex)) throw new Error("expected defective VERSION regex not found");
45
+ if (oldCreate.split(oldRegex).length !== 2) throw new Error("VERSION regex occurrence count != 1");
46
+ await writeFile(createPath, oldCreate.replace(oldRegex, newRegex), "utf8");
47
+
48
+ const skillPath = path.join(stage, "SKILL.md");
49
+ const oldSkill = await readFile(skillPath, "utf8");
50
+ const versionLine = "version: 1.1.0";
51
+ if (!oldSkill.includes(versionLine) || oldSkill.split(versionLine).length !== 2) {
52
+ throw new Error("SKILL.md version occurrence count != 1");
53
+ }
54
+ await writeFile(skillPath, oldSkill.replace(versionLine, "version: 1.1.1"), "utf8");
55
+
56
+ const sha = b => createHash("sha256").update(b).digest("hex");
57
+ const canonical = v => Array.isArray(v) ? v.map(canonical) : v && typeof v === "object"
58
+ ? Object.fromEntries(Object.keys(v).sort((a,b)=>a.localeCompare(b,"en")).map(k=>[k,canonical(v[k])]))
59
+ : v;
60
+
61
+ const body = structuredClone(oldEnv.body);
62
+ body.version = "1.1.1";
63
+ body.skill_md_sha256 = sha(await readFile(skillPath));
64
+ body.provenance = {
65
+ ...body.provenance,
66
+ source_revision: "r47.1-semver-regex-repair-2026-09-27"
67
+ };
68
+ for (const resource of body.resources) {
69
+ resource.sha256 = sha(await readFile(path.join(stage, ...resource.path.split("/"))));
70
+ }
71
+ const bodyBytes = Buffer.from(JSON.stringify(canonical(body)), "utf8");
72
+ const body_sha256 = sha(bodyBytes);
73
+ const priv = createPrivateKey(await readFile(path.join(stateDir, "trust", "ed25519-private.pem")));
74
+ const pub = createPublicKey(priv);
75
+ const keyId = sha(pub.export({type:"spki", format:"der"}));
76
+ const attestation = {
77
+ algorithm: "ed25519",
78
+ domain: "DEADBYTE-SKILL-MANIFEST-V1",
79
+ key_id: keyId,
80
+ signature_base64: sign(null, Buffer.concat([Buffer.from("DEADBYTE-SKILL-MANIFEST-V1\0", "utf8"), bodyBytes]), priv).toString("base64")
81
+ };
82
+ const unsigned = {body, body_sha256, attestation};
83
+ const envelope = {
84
+ ...unsigned,
85
+ manifest_sha256: sha(Buffer.from(JSON.stringify(canonical(unsigned)), "utf8"))
86
+ };
87
+ await writeFile(path.join(stage, "skill.json"), JSON.stringify(envelope, null, 2), "utf8");
88
+
89
+ const validator = await ex(process.execPath, [path.join(stage, "scripts", "validate-skill.mjs"), stage], {
90
+ timeout: 30000,
91
+ maxBuffer: 1024 * 1024
92
+ });
93
+
94
+ const plane = await createSkillPlane({
95
+ root: stageParent,
96
+ verifyKeyPath: path.join(stateDir, "trust", "ed25519-public.pem")
97
+ });
98
+ const loaded = await plane.load({name:"skill-creator"});
99
+ if (!loaded.verified || loaded.version !== "1.1.1") throw new Error("staging Skill Plane verification failed");
100
+
101
+ console.log(JSON.stringify({
102
+ status:"STAGED",
103
+ active_manifest:oldEnv.manifest_sha256,
104
+ archived_path:history,
105
+ archived_manifest:archived.manifest_sha256,
106
+ stage_parent:stageParent,
107
+ stage_skill:stage,
108
+ candidate_manifest:envelope.manifest_sha256,
109
+ candidate_body_sha256:body_sha256,
110
+ verify_key_id:keyId,
111
+ validator:validator.stdout.trim(),
112
+ skill_plane_verified:loaded.verified
113
+ }, null, 2));
@@ -0,0 +1,217 @@
1
+ #requires -Version 5.1
2
+ [CmdletBinding()]
3
+ param()
4
+ Set-StrictMode -Version 3.0
5
+ $ErrorActionPreference = 'Stop'
6
+
7
+ function Fail([string]$Message) { throw "LOCAL_C_PROMOTION: $Message" }
8
+ function Sha256-File([string]$Path) {
9
+ return (Get-FileHash -LiteralPath $Path -Algorithm SHA256).Hash.ToLowerInvariant()
10
+ }
11
+ function Sha256-Text([string]$Text) {
12
+ $sha = [System.Security.Cryptography.SHA256]::Create()
13
+ try {
14
+ $bytes = [System.Text.Encoding]::UTF8.GetBytes($Text)
15
+ return -join ($sha.ComputeHash($bytes) | ForEach-Object { $_.ToString('x2') })
16
+ } finally { $sha.Dispose() }
17
+ }
18
+ function Write-Utf8NoBom([string]$Path,[string]$Text) {
19
+ [System.IO.File]::WriteAllText($Path,$Text,(New-Object System.Text.UTF8Encoding($false)))
20
+ }
21
+ function Read-Raw([string]$Path) {
22
+ return [System.IO.File]::ReadAllText($Path,[System.Text.Encoding]::UTF8)
23
+ }
24
+
25
+ $stateRoot = Join-Path $env:USERPROFILE '.deadbyte-mcp'
26
+ $pointerPath = Join-Path $stateRoot 'active-release.json'
27
+ $launcher = Join-Path $stateRoot 'bin\DEADBYTE-MCP.ps1'
28
+ $runtimeRoot = Join-Path $stateRoot 'runtime'
29
+ if (-not (Test-Path -LiteralPath $pointerPath -PathType Leaf)) { Fail 'active pointer missing' }
30
+ if (-not (Test-Path -LiteralPath $launcher -PathType Leaf)) { Fail 'stable launcher missing' }
31
+
32
+ $pointerText = Read-Raw $pointerPath
33
+ $pointer = $pointerText | ConvertFrom-Json
34
+ if ([string]$pointer.schema -ne 'deadbyte.active-release.v1') { Fail 'active pointer schema mismatch' }
35
+ $oldGeneration = [System.IO.Path]::GetFullPath([string]$pointer.generation_root)
36
+ $oldManifest = Join-Path $oldGeneration 'GENERATION.SHA256'
37
+ if (-not (Test-Path -LiteralPath $oldManifest -PathType Leaf)) { Fail 'old generation manifest missing' }
38
+ $oldManifestHash = Sha256-File $oldManifest
39
+ if ($oldManifestHash -ne [string]$pointer.generation_manifest_sha256) { Fail 'old generation manifest hash mismatch' }
40
+
41
+ $manifestLines = @(Get-Content -LiteralPath $oldManifest -Encoding UTF8)
42
+ $entries = @()
43
+ foreach ($line in $manifestLines) {
44
+ if ([string]::IsNullOrWhiteSpace([string]$line)) { continue }
45
+ if ([string]$line -notmatch '^([0-9a-f]{64}) ([A-Za-z0-9._-]+)$') { Fail "malformed old generation manifest line: $line" }
46
+ $expected = [string]$Matches[1]
47
+ $name = [string]$Matches[2]
48
+ $path = Join-Path $oldGeneration $name
49
+ if (-not (Test-Path -LiteralPath $path -PathType Leaf)) { Fail "old generation file missing: $name" }
50
+ $actual = Sha256-File $path
51
+ if ($actual -ne $expected) { Fail "old generation hash mismatch: $name" }
52
+ $entries += [pscustomobject]@{ Name=$name; Expected=$expected }
53
+ }
54
+ $names = @($entries | ForEach-Object { $_.Name })
55
+ foreach ($required in @('coding-policy.json','autonomous-policy.json','controller-config.psd1')) {
56
+ if ($required -notin $names) { Fail "required generation file not manifested: $required" }
57
+ }
58
+
59
+ $oldCodingPath = Join-Path $oldGeneration 'coding-policy.json'
60
+ $oldAutonomousPath = Join-Path $oldGeneration 'autonomous-policy.json'
61
+ $oldCodingHash = Sha256-File $oldCodingPath
62
+ $oldAutonomousHash = Sha256-File $oldAutonomousPath
63
+
64
+ $coding = (Read-Raw $oldCodingPath) | ConvertFrom-Json
65
+ if ([string]$coding.schema -ne 'deadbyte.coding-policy.v1' -or $coding.enabled -ne $true) { Fail 'coding policy schema/enabled mismatch' }
66
+ if ($null -eq $coding.roots) { Fail 'coding roots missing' }
67
+
68
+ $localRoot = [pscustomobject][ordered]@{
69
+ path = 'C:\'
70
+ read = $true
71
+ write = $true
72
+ exec = $true
73
+ deny_paths = @()
74
+ }
75
+ if ($coding.roots.PSObject.Properties.Name -contains 'local_c') {
76
+ $coding.roots.local_c = $localRoot
77
+ } else {
78
+ $coding.roots | Add-Member -NotePropertyName 'local_c' -NotePropertyValue $localRoot
79
+ }
80
+ foreach ($commandProp in @($coding.commands.PSObject.Properties)) {
81
+ $command = $commandProp.Value
82
+ if ($command.PSObject.Properties.Name -contains 'root_ids') {
83
+ $ids = @($command.root_ids)
84
+ if ('local_c' -notin $ids) { $command.root_ids = @($ids + 'local_c') }
85
+ }
86
+ }
87
+ $codingText = ($coding | ConvertTo-Json -Depth 32) + [Environment]::NewLine
88
+ $newCodingHash = Sha256-Text $codingText
89
+
90
+ $autonomous = (Read-Raw $oldAutonomousPath) | ConvertFrom-Json
91
+ if ([string]$autonomous.schema -ne 'deadbyte.autonomous-policy.v1' -or $autonomous.enabled -ne $true) { Fail 'autonomous policy schema/enabled mismatch' }
92
+ $autonomous.coding_policy_sha256 = $newCodingHash
93
+ $autonomousText = ($autonomous | ConvertTo-Json -Depth 32) + [Environment]::NewLine
94
+ $newAutonomousHash = Sha256-Text $autonomousText
95
+
96
+ $seed = $oldManifestHash + [Environment]::NewLine + $newCodingHash + [Environment]::NewLine + $newAutonomousHash + [Environment]::NewLine + 'local_c=C:\'
97
+ $generationSuffix = (Sha256-Text $seed).Substring(0,16)
98
+ $newGenerationId = ([string]$pointer.release_id) + '-g' + $generationSuffix
99
+ $generationStore = Join-Path $stateRoot 'generations'
100
+ $newGeneration = Join-Path $generationStore $newGenerationId
101
+ if (Test-Path -LiteralPath $newGeneration) { Fail "target generation already exists: $newGeneration" }
102
+ New-Item -ItemType Directory -Path $newGeneration | Out-Null
103
+
104
+ $pointerBackup = Join-Path $runtimeRoot 'active-release.before-local-c.json'
105
+ $codingGrantPath = Join-Path $runtimeRoot 'coding-grant.json'
106
+ $autonomousGrantPath = Join-Path $runtimeRoot 'autonomous-grant.json'
107
+ $hadCodingGrant = Test-Path -LiteralPath $codingGrantPath -PathType Leaf
108
+ $hadAutonomousGrant = Test-Path -LiteralPath $autonomousGrantPath -PathType Leaf
109
+ $oldCodingGrantText = if ($hadCodingGrant) { Read-Raw $codingGrantPath } else { $null }
110
+ $oldAutonomousGrantText = if ($hadAutonomousGrant) { Read-Raw $autonomousGrantPath } else { $null }
111
+ $wasAutonomousArmed = $false
112
+ if ($hadCodingGrant -and $hadAutonomousGrant) {
113
+ try {
114
+ $cg = $oldCodingGrantText | ConvertFrom-Json
115
+ $ag = $oldAutonomousGrantText | ConvertFrom-Json
116
+ $wasAutonomousArmed =
117
+ ([string]$cg.policy_sha256 -eq $oldCodingHash) -and
118
+ ([string]$ag.policy_sha256 -eq $oldAutonomousHash) -and
119
+ ([string]$ag.coding_policy_sha256 -eq $oldCodingHash)
120
+ } catch { $wasAutonomousArmed = $false }
121
+ }
122
+
123
+ $switched = $false
124
+ try {
125
+ foreach ($entry in $entries) {
126
+ $src = Join-Path $oldGeneration $entry.Name
127
+ $dst = Join-Path $newGeneration $entry.Name
128
+ Copy-Item -LiteralPath $src -Destination $dst
129
+ }
130
+
131
+ Write-Utf8NoBom (Join-Path $newGeneration 'coding-policy.json') $codingText
132
+ Write-Utf8NoBom (Join-Path $newGeneration 'autonomous-policy.json') $autonomousText
133
+
134
+ $configPath = Join-Path $newGeneration 'controller-config.psd1'
135
+ $configText = Read-Raw $configPath
136
+ if ($configText.IndexOf($oldGeneration,[System.StringComparison]::OrdinalIgnoreCase) -lt 0) { Fail 'controller config is not bound to old generation root' }
137
+ $configText = $configText.Replace($oldGeneration,$newGeneration)
138
+ Write-Utf8NoBom $configPath $configText
139
+ $cfg = Import-PowerShellDataFile -LiteralPath $configPath
140
+ if ([System.IO.Path]::GetFullPath([string]$cfg.CodingPolicyFile) -ne [System.IO.Path]::GetFullPath((Join-Path $newGeneration 'coding-policy.json'))) { Fail 'new controller CodingPolicyFile binding mismatch' }
141
+ if ([System.IO.Path]::GetFullPath([string]$cfg.AutonomousPolicyFile) -ne [System.IO.Path]::GetFullPath((Join-Path $newGeneration 'autonomous-policy.json'))) { Fail 'new controller AutonomousPolicyFile binding mismatch' }
142
+
143
+ foreach ($entry in $entries) {
144
+ $p = Join-Path $newGeneration $entry.Name
145
+ $text = $null
146
+ try { $text = Read-Raw $p } catch { $text = $null }
147
+ if ($null -ne $text -and $entry.Name -ne 'controller-config.psd1' -and $text.IndexOf($oldGeneration,[System.StringComparison]::OrdinalIgnoreCase) -ge 0) {
148
+ Fail "unexpected old generation binding remains in $($entry.Name)"
149
+ }
150
+ }
151
+
152
+ $newManifestLines = @()
153
+ foreach ($entry in $entries) {
154
+ $p = Join-Path $newGeneration $entry.Name
155
+ $newManifestLines += ((Sha256-File $p) + ' ' + $entry.Name)
156
+ }
157
+ $newManifestText = ($newManifestLines -join [Environment]::NewLine) + [Environment]::NewLine
158
+ $newManifestPath = Join-Path $newGeneration 'GENERATION.SHA256'
159
+ Write-Utf8NoBom $newManifestPath $newManifestText
160
+ $newManifestHash = Sha256-File $newManifestPath
161
+
162
+ foreach ($line in $newManifestLines) {
163
+ if ($line -notmatch '^([0-9a-f]{64}) ([A-Za-z0-9._-]+)$') { Fail 'new manifest internal format error' }
164
+ $actual = Sha256-File (Join-Path $newGeneration ([string]$Matches[2]))
165
+ if ($actual -ne [string]$Matches[1]) { Fail "new generation verification failed: $($Matches[2])" }
166
+ }
167
+ if ((Sha256-File (Join-Path $newGeneration 'coding-policy.json')) -ne $newCodingHash) { Fail 'new coding hash self-check failed' }
168
+ if ((Sha256-File (Join-Path $newGeneration 'autonomous-policy.json')) -ne $newAutonomousHash) { Fail 'new autonomous hash self-check failed' }
169
+
170
+ Write-Utf8NoBom $pointerBackup $pointerText
171
+ $pointer.generation_root = $newGeneration
172
+ $pointer.config_path = Join-Path $newGeneration 'controller-config.psd1'
173
+ $pointer.generation_manifest_sha256 = $newManifestHash
174
+ if ($pointer.PSObject.Properties.Name -contains 'generation_id') { $pointer.generation_id = $newGenerationId }
175
+ if ($pointer.PSObject.Properties.Name -contains 'generation_manifest_path') { $pointer.generation_manifest_path = $newManifestPath }
176
+ if ($pointer.PSObject.Properties.Name -contains 'updated_at_utc') { $pointer.updated_at_utc = [DateTime]::UtcNow.ToString('o') }
177
+ $newPointerText = ($pointer | ConvertTo-Json -Depth 32) + [Environment]::NewLine
178
+ $pointerTmp = $pointerPath + '.local-c.tmp'
179
+ Write-Utf8NoBom $pointerTmp $newPointerText
180
+ Move-Item -LiteralPath $pointerTmp -Destination $pointerPath -Force
181
+ $switched = $true
182
+
183
+ $statusText = (& $launcher status 2>&1 | Out-String)
184
+ if ($LASTEXITCODE -ne 0) { Fail "launcher status failed after pointer switch: $statusText" }
185
+
186
+ $armCodingText = (& $launcher armcoding 2>&1 | Out-String)
187
+ if ($LASTEXITCODE -ne 0) { Fail "armcoding failed after pointer switch: $armCodingText" }
188
+
189
+ $armAutonomousText = ''
190
+ if ($wasAutonomousArmed) {
191
+ $armAutonomousText = (& $launcher armautonomous 2>&1 | Out-String)
192
+ if ($LASTEXITCODE -ne 0) { Fail "armautonomous failed after pointer switch: $armAutonomousText" }
193
+ }
194
+
195
+ Write-Output 'LOCAL_C_PROMOTION=PASS'
196
+ Write-Output ('OLD_GENERATION=' + $oldGeneration)
197
+ Write-Output ('NEW_GENERATION=' + $newGeneration)
198
+ Write-Output ('OLD_GENERATION_MANIFEST_SHA256=' + $oldManifestHash)
199
+ Write-Output ('NEW_GENERATION_MANIFEST_SHA256=' + $newManifestHash)
200
+ Write-Output ('OLD_CODING_POLICY_SHA256=' + $oldCodingHash)
201
+ Write-Output ('NEW_CODING_POLICY_SHA256=' + $newCodingHash)
202
+ Write-Output ('NEW_AUTONOMOUS_POLICY_SHA256=' + $newAutonomousHash)
203
+ Write-Output ('AUTONOMOUS_REARMED=' + $wasAutonomousArmed)
204
+ Write-Output ('ROLLBACK_POINTER=' + $pointerBackup)
205
+ Write-Output 'RESTART_REQUIRED=YES'
206
+ } catch {
207
+ $message = $_.Exception.Message
208
+ if ($switched) {
209
+ try { Write-Utf8NoBom $pointerPath $pointerText } catch {}
210
+ if ($hadCodingGrant) { try { Write-Utf8NoBom $codingGrantPath $oldCodingGrantText } catch {} }
211
+ else { try { Remove-Item -LiteralPath $codingGrantPath -Force -ErrorAction SilentlyContinue } catch {} }
212
+ if ($hadAutonomousGrant) { try { Write-Utf8NoBom $autonomousGrantPath $oldAutonomousGrantText } catch {} }
213
+ else { try { Remove-Item -LiteralPath $autonomousGrantPath -Force -ErrorAction SilentlyContinue } catch {} }
214
+ }
215
+ try { if (Test-Path -LiteralPath $newGeneration) { Remove-Item -LiteralPath $newGeneration -Recurse -Force } } catch {}
216
+ Fail $message
217
+ }
@@ -0,0 +1,217 @@
1
+ #requires -Version 5.1
2
+ [CmdletBinding()]
3
+ param()
4
+ Set-StrictMode -Version 3.0
5
+ $ErrorActionPreference = 'Stop'
6
+
7
+ function Fail([string]$Message) { throw "LOCAL_D_PROMOTION: $Message" }
8
+ function Sha256-File([string]$Path) {
9
+ return (Get-FileHash -LiteralPath $Path -Algorithm SHA256).Hash.ToLowerInvariant()
10
+ }
11
+ function Sha256-Text([string]$Text) {
12
+ $sha = [System.Security.Cryptography.SHA256]::Create()
13
+ try {
14
+ $bytes = [System.Text.Encoding]::UTF8.GetBytes($Text)
15
+ return -join ($sha.ComputeHash($bytes) | ForEach-Object { $_.ToString('x2') })
16
+ } finally { $sha.Dispose() }
17
+ }
18
+ function Write-Utf8NoBom([string]$Path,[string]$Text) {
19
+ [System.IO.File]::WriteAllText($Path,$Text,(New-Object System.Text.UTF8Encoding($false)))
20
+ }
21
+ function Read-Raw([string]$Path) {
22
+ return [System.IO.File]::ReadAllText($Path,[System.Text.Encoding]::UTF8)
23
+ }
24
+
25
+ $stateRoot = Join-Path $env:USERPROFILE '.deadbyte-mcp'
26
+ $pointerPath = Join-Path $stateRoot 'active-release.json'
27
+ $launcher = Join-Path $stateRoot 'bin\DEADBYTE-MCP.ps1'
28
+ $runtimeRoot = Join-Path $stateRoot 'runtime'
29
+ if (-not (Test-Path -LiteralPath $pointerPath -PathType Leaf)) { Fail 'active pointer missing' }
30
+ if (-not (Test-Path -LiteralPath $launcher -PathType Leaf)) { Fail 'stable launcher missing' }
31
+
32
+ $pointerText = Read-Raw $pointerPath
33
+ $pointer = $pointerText | ConvertFrom-Json
34
+ if ([string]$pointer.schema -ne 'deadbyte.active-release.v1') { Fail 'active pointer schema mismatch' }
35
+ $oldGeneration = [System.IO.Path]::GetFullPath([string]$pointer.generation_root)
36
+ $oldManifest = Join-Path $oldGeneration 'GENERATION.SHA256'
37
+ if (-not (Test-Path -LiteralPath $oldManifest -PathType Leaf)) { Fail 'old generation manifest missing' }
38
+ $oldManifestHash = Sha256-File $oldManifest
39
+ if ($oldManifestHash -ne [string]$pointer.generation_manifest_sha256) { Fail 'old generation manifest hash mismatch' }
40
+
41
+ $manifestLines = @(Get-Content -LiteralPath $oldManifest -Encoding UTF8)
42
+ $entries = @()
43
+ foreach ($line in $manifestLines) {
44
+ if ([string]::IsNullOrWhiteSpace([string]$line)) { continue }
45
+ if ([string]$line -notmatch '^([0-9a-f]{64}) ([A-Za-z0-9._-]+)$') { Fail "malformed old generation manifest line: $line" }
46
+ $expected = [string]$Matches[1]
47
+ $name = [string]$Matches[2]
48
+ $path = Join-Path $oldGeneration $name
49
+ if (-not (Test-Path -LiteralPath $path -PathType Leaf)) { Fail "old generation file missing: $name" }
50
+ $actual = Sha256-File $path
51
+ if ($actual -ne $expected) { Fail "old generation hash mismatch: $name" }
52
+ $entries += [pscustomobject]@{ Name=$name; Expected=$expected }
53
+ }
54
+ $names = @($entries | ForEach-Object { $_.Name })
55
+ foreach ($required in @('coding-policy.json','autonomous-policy.json','controller-config.psd1')) {
56
+ if ($required -notin $names) { Fail "required generation file not manifested: $required" }
57
+ }
58
+
59
+ $oldCodingPath = Join-Path $oldGeneration 'coding-policy.json'
60
+ $oldAutonomousPath = Join-Path $oldGeneration 'autonomous-policy.json'
61
+ $oldCodingHash = Sha256-File $oldCodingPath
62
+ $oldAutonomousHash = Sha256-File $oldAutonomousPath
63
+
64
+ $coding = (Read-Raw $oldCodingPath) | ConvertFrom-Json
65
+ if ([string]$coding.schema -ne 'deadbyte.coding-policy.v1' -or $coding.enabled -ne $true) { Fail 'coding policy schema/enabled mismatch' }
66
+ if ($null -eq $coding.roots) { Fail 'coding roots missing' }
67
+
68
+ $localRoot = [pscustomobject][ordered]@{
69
+ path = 'D:\'
70
+ read = $true
71
+ write = $true
72
+ exec = $true
73
+ deny_paths = @()
74
+ }
75
+ if ($coding.roots.PSObject.Properties.Name -contains 'local_d') {
76
+ $coding.roots.local_d = $localRoot
77
+ } else {
78
+ $coding.roots | Add-Member -NotePropertyName 'local_d' -NotePropertyValue $localRoot
79
+ }
80
+ foreach ($commandProp in @($coding.commands.PSObject.Properties)) {
81
+ $command = $commandProp.Value
82
+ if ($command.PSObject.Properties.Name -contains 'root_ids') {
83
+ $ids = @($command.root_ids)
84
+ if ('local_d' -notin $ids) { $command.root_ids = @($ids + 'local_d') }
85
+ }
86
+ }
87
+ $codingText = ($coding | ConvertTo-Json -Depth 32) + [Environment]::NewLine
88
+ $newCodingHash = Sha256-Text $codingText
89
+
90
+ $autonomous = (Read-Raw $oldAutonomousPath) | ConvertFrom-Json
91
+ if ([string]$autonomous.schema -ne 'deadbyte.autonomous-policy.v1' -or $autonomous.enabled -ne $true) { Fail 'autonomous policy schema/enabled mismatch' }
92
+ $autonomous.coding_policy_sha256 = $newCodingHash
93
+ $autonomousText = ($autonomous | ConvertTo-Json -Depth 32) + [Environment]::NewLine
94
+ $newAutonomousHash = Sha256-Text $autonomousText
95
+
96
+ $seed = $oldManifestHash + [Environment]::NewLine + $newCodingHash + [Environment]::NewLine + $newAutonomousHash + [Environment]::NewLine + 'local_d=D:\'
97
+ $generationSuffix = (Sha256-Text $seed).Substring(0,16)
98
+ $newGenerationId = ([string]$pointer.release_id) + '-g' + $generationSuffix
99
+ $generationStore = Join-Path $stateRoot 'generations'
100
+ $newGeneration = Join-Path $generationStore $newGenerationId
101
+ if (Test-Path -LiteralPath $newGeneration) { Fail "target generation already exists: $newGeneration" }
102
+ New-Item -ItemType Directory -Path $newGeneration | Out-Null
103
+
104
+ $pointerBackup = Join-Path $runtimeRoot 'active-release.before-local-d.json'
105
+ $codingGrantPath = Join-Path $runtimeRoot 'coding-grant.json'
106
+ $autonomousGrantPath = Join-Path $runtimeRoot 'autonomous-grant.json'
107
+ $hadCodingGrant = Test-Path -LiteralPath $codingGrantPath -PathType Leaf
108
+ $hadAutonomousGrant = Test-Path -LiteralPath $autonomousGrantPath -PathType Leaf
109
+ $oldCodingGrantText = if ($hadCodingGrant) { Read-Raw $codingGrantPath } else { $null }
110
+ $oldAutonomousGrantText = if ($hadAutonomousGrant) { Read-Raw $autonomousGrantPath } else { $null }
111
+ $wasAutonomousArmed = $false
112
+ if ($hadCodingGrant -and $hadAutonomousGrant) {
113
+ try {
114
+ $cg = $oldCodingGrantText | ConvertFrom-Json
115
+ $ag = $oldAutonomousGrantText | ConvertFrom-Json
116
+ $wasAutonomousArmed =
117
+ ([string]$cg.policy_sha256 -eq $oldCodingHash) -and
118
+ ([string]$ag.policy_sha256 -eq $oldAutonomousHash) -and
119
+ ([string]$ag.coding_policy_sha256 -eq $oldCodingHash)
120
+ } catch { $wasAutonomousArmed = $false }
121
+ }
122
+
123
+ $switched = $false
124
+ try {
125
+ foreach ($entry in $entries) {
126
+ $src = Join-Path $oldGeneration $entry.Name
127
+ $dst = Join-Path $newGeneration $entry.Name
128
+ Copy-Item -LiteralPath $src -Destination $dst
129
+ }
130
+
131
+ Write-Utf8NoBom (Join-Path $newGeneration 'coding-policy.json') $codingText
132
+ Write-Utf8NoBom (Join-Path $newGeneration 'autonomous-policy.json') $autonomousText
133
+
134
+ $configPath = Join-Path $newGeneration 'controller-config.psd1'
135
+ $configText = Read-Raw $configPath
136
+ if ($configText.IndexOf($oldGeneration,[System.StringComparison]::OrdinalIgnoreCase) -lt 0) { Fail 'controller config is not bound to old generation root' }
137
+ $configText = $configText.Replace($oldGeneration,$newGeneration)
138
+ Write-Utf8NoBom $configPath $configText
139
+ $cfg = Import-PowerShellDataFile -LiteralPath $configPath
140
+ if ([System.IO.Path]::GetFullPath([string]$cfg.CodingPolicyFile) -ne [System.IO.Path]::GetFullPath((Join-Path $newGeneration 'coding-policy.json'))) { Fail 'new controller CodingPolicyFile binding mismatch' }
141
+ if ([System.IO.Path]::GetFullPath([string]$cfg.AutonomousPolicyFile) -ne [System.IO.Path]::GetFullPath((Join-Path $newGeneration 'autonomous-policy.json'))) { Fail 'new controller AutonomousPolicyFile binding mismatch' }
142
+
143
+ foreach ($entry in $entries) {
144
+ $p = Join-Path $newGeneration $entry.Name
145
+ $text = $null
146
+ try { $text = Read-Raw $p } catch { $text = $null }
147
+ if ($null -ne $text -and $entry.Name -ne 'controller-config.psd1' -and $text.IndexOf($oldGeneration,[System.StringComparison]::OrdinalIgnoreCase) -ge 0) {
148
+ Fail "unexpected old generation binding remains in $($entry.Name)"
149
+ }
150
+ }
151
+
152
+ $newManifestLines = @()
153
+ foreach ($entry in $entries) {
154
+ $p = Join-Path $newGeneration $entry.Name
155
+ $newManifestLines += ((Sha256-File $p) + ' ' + $entry.Name)
156
+ }
157
+ $newManifestText = ($newManifestLines -join [Environment]::NewLine) + [Environment]::NewLine
158
+ $newManifestPath = Join-Path $newGeneration 'GENERATION.SHA256'
159
+ Write-Utf8NoBom $newManifestPath $newManifestText
160
+ $newManifestHash = Sha256-File $newManifestPath
161
+
162
+ foreach ($line in $newManifestLines) {
163
+ if ($line -notmatch '^([0-9a-f]{64}) ([A-Za-z0-9._-]+)$') { Fail 'new manifest internal format error' }
164
+ $actual = Sha256-File (Join-Path $newGeneration ([string]$Matches[2]))
165
+ if ($actual -ne [string]$Matches[1]) { Fail "new generation verification failed: $($Matches[2])" }
166
+ }
167
+ if ((Sha256-File (Join-Path $newGeneration 'coding-policy.json')) -ne $newCodingHash) { Fail 'new coding hash self-check failed' }
168
+ if ((Sha256-File (Join-Path $newGeneration 'autonomous-policy.json')) -ne $newAutonomousHash) { Fail 'new autonomous hash self-check failed' }
169
+
170
+ Write-Utf8NoBom $pointerBackup $pointerText
171
+ $pointer.generation_root = $newGeneration
172
+ $pointer.config_path = Join-Path $newGeneration 'controller-config.psd1'
173
+ $pointer.generation_manifest_sha256 = $newManifestHash
174
+ if ($pointer.PSObject.Properties.Name -contains 'generation_id') { $pointer.generation_id = $newGenerationId }
175
+ if ($pointer.PSObject.Properties.Name -contains 'generation_manifest_path') { $pointer.generation_manifest_path = $newManifestPath }
176
+ if ($pointer.PSObject.Properties.Name -contains 'updated_at_utc') { $pointer.updated_at_utc = [DateTime]::UtcNow.ToString('o') }
177
+ $newPointerText = ($pointer | ConvertTo-Json -Depth 32) + [Environment]::NewLine
178
+ $pointerTmp = $pointerPath + '.local-d.tmp'
179
+ Write-Utf8NoBom $pointerTmp $newPointerText
180
+ Move-Item -LiteralPath $pointerTmp -Destination $pointerPath -Force
181
+ $switched = $true
182
+
183
+ $statusText = (& $launcher status 2>&1 | Out-String)
184
+ if ($LASTEXITCODE -ne 0) { Fail "launcher status failed after pointer switch: $statusText" }
185
+
186
+ $armCodingText = (& $launcher armcoding 2>&1 | Out-String)
187
+ if ($LASTEXITCODE -ne 0) { Fail "armcoding failed after pointer switch: $armCodingText" }
188
+
189
+ $armAutonomousText = ''
190
+ if ($wasAutonomousArmed) {
191
+ $armAutonomousText = (& $launcher armautonomous 2>&1 | Out-String)
192
+ if ($LASTEXITCODE -ne 0) { Fail "armautonomous failed after pointer switch: $armAutonomousText" }
193
+ }
194
+
195
+ Write-Output 'LOCAL_D_PROMOTION=PASS'
196
+ Write-Output ('OLD_GENERATION=' + $oldGeneration)
197
+ Write-Output ('NEW_GENERATION=' + $newGeneration)
198
+ Write-Output ('OLD_GENERATION_MANIFEST_SHA256=' + $oldManifestHash)
199
+ Write-Output ('NEW_GENERATION_MANIFEST_SHA256=' + $newManifestHash)
200
+ Write-Output ('OLD_CODING_POLICY_SHA256=' + $oldCodingHash)
201
+ Write-Output ('NEW_CODING_POLICY_SHA256=' + $newCodingHash)
202
+ Write-Output ('NEW_AUTONOMOUS_POLICY_SHA256=' + $newAutonomousHash)
203
+ Write-Output ('AUTONOMOUS_REARMED=' + $wasAutonomousArmed)
204
+ Write-Output ('ROLLBACK_POINTER=' + $pointerBackup)
205
+ Write-Output 'RESTART_REQUIRED=YES'
206
+ } catch {
207
+ $message = $_.Exception.Message
208
+ if ($switched) {
209
+ try { Write-Utf8NoBom $pointerPath $pointerText } catch {}
210
+ if ($hadCodingGrant) { try { Write-Utf8NoBom $codingGrantPath $oldCodingGrantText } catch {} }
211
+ else { try { Remove-Item -LiteralPath $codingGrantPath -Force -ErrorAction SilentlyContinue } catch {} }
212
+ if ($hadAutonomousGrant) { try { Write-Utf8NoBom $autonomousGrantPath $oldAutonomousGrantText } catch {} }
213
+ else { try { Remove-Item -LiteralPath $autonomousGrantPath -Force -ErrorAction SilentlyContinue } catch {} }
214
+ }
215
+ try { if (Test-Path -LiteralPath $newGeneration) { Remove-Item -LiteralPath $newGeneration -Recurse -Force } } catch {}
216
+ Fail $message
217
+ }
@@ -0,0 +1,8 @@
1
+ $ErrorActionPreference='Stop'
2
+ Set-StrictMode -Version Latest
3
+ $Root=Split-Path -Parent $PSScriptRoot
4
+ Push-Location $Root
5
+ try {
6
+ & node --test --test-concurrency=1 test/r48-release-contract.test.mjs test/release-version.test.mjs test/cli-entrypoint.test.mjs test/package-boundary.test.mjs test/release-smoke-isolation.test.mjs test/full-max-catalog-r48.test.mjs
7
+ if($LASTEXITCODE -ne 0){throw "R48 post-bump release checks failed exit=$LASTEXITCODE"}
8
+ } finally { Pop-Location }
@@ -0,0 +1,8 @@
1
+ $ErrorActionPreference='Stop'
2
+ Set-StrictMode -Version Latest
3
+ $Root=Split-Path -Parent $PSScriptRoot
4
+ Push-Location $Root
5
+ try {
6
+ & node --test test/machine-fs-runtime.test.mjs test/windows-observation-r48.test.mjs test/windows-observation-surface-r48.test.mjs test/hardware-observation-r45.test.mjs test/observation-mcp-tools.test.mjs test/compact-gateway-r36.test.mjs test/capability-resolver.test.mjs test/compact-catalog-r36.test.mjs test/compact-max-catalog-r45.test.mjs test/full-max-catalog-r45.test.mjs test/full-max-catalog-r46.test.mjs test/full-max-catalog-r47.test.mjs test/full-max-catalog-r48.test.mjs
7
+ if($LASTEXITCODE -ne 0){throw "R48 focused regression failed exit=$LASTEXITCODE"}
8
+ } finally { Pop-Location }