@apnex/network-adapter 0.1.11 → 0.1.12
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/dist/bin/seed-skills.d.ts +42 -0
- package/dist/bin/seed-skills.js +177 -0
- package/dist/bin/seed-skills.js.map +1 -0
- package/dist/build-info.json +3 -3
- package/dist/control-plane/contracts.d.ts +99 -0
- package/dist/control-plane/contracts.js +22 -0
- package/dist/control-plane/contracts.js.map +1 -0
- package/dist/control-plane/index.d.ts +14 -0
- package/dist/control-plane/index.js +3 -0
- package/dist/control-plane/index.js.map +1 -0
- package/dist/control-plane/reconcile-loop.d.ts +50 -0
- package/dist/control-plane/reconcile-loop.js +83 -0
- package/dist/control-plane/reconcile-loop.js.map +1 -0
- package/dist/control-plane/spec-store.d.ts +29 -0
- package/dist/control-plane/spec-store.js +41 -0
- package/dist/control-plane/spec-store.js.map +1 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/skills/claude-skill-actuator.d.ts +28 -0
- package/dist/skills/claude-skill-actuator.js +147 -0
- package/dist/skills/claude-skill-actuator.js.map +1 -0
- package/dist/skills/contracts.d.ts +33 -0
- package/dist/skills/contracts.js +15 -0
- package/dist/skills/contracts.js.map +1 -0
- package/dist/skills/file-skill-ledger.d.ts +7 -0
- package/dist/skills/file-skill-ledger.js +38 -0
- package/dist/skills/file-skill-ledger.js.map +1 -0
- package/dist/skills/index.d.ts +15 -0
- package/dist/skills/index.js +6 -0
- package/dist/skills/index.js.map +1 -0
- package/dist/skills/manifest.d.ts +30 -0
- package/dist/skills/manifest.js +110 -0
- package/dist/skills/manifest.js.map +1 -0
- package/dist/skills/role-skill-source.d.ts +29 -0
- package/dist/skills/role-skill-source.js +20 -0
- package/dist/skills/role-skill-source.js.map +1 -0
- package/dist/skills/set-diff-gate.d.ts +20 -0
- package/dist/skills/set-diff-gate.js +24 -0
- package/dist/skills/set-diff-gate.js.map +1 -0
- package/dist/tool-manager/dispatch/dispatch.d.ts +26 -0
- package/dist/tool-manager/dispatch/dispatch.js +42 -1
- package/dist/tool-manager/dispatch/dispatch.js.map +1 -1
- package/dist/tool-manager/orchestrator/dispatcher.d.ts +10 -0
- package/dist/tool-manager/orchestrator/dispatcher.js +5 -1
- package/dist/tool-manager/orchestrator/dispatcher.js.map +1 -1
- package/package.json +4 -1
package/dist/index.js
CHANGED
|
@@ -37,7 +37,7 @@ export { parseHarnessManifest, loadHarnessManifest, serverCapabilitiesFromManife
|
|
|
37
37
|
// tool-bridge) consumes the SAME per-call behavior wrapper the MCP CallTool
|
|
38
38
|
// handler uses — without importing an MCP server. This is the concrete payoff of
|
|
39
39
|
// the Slice-B extraction: one dispatch authority, many last-mile bindings.
|
|
40
|
-
export { runToolDispatch } from "./tool-manager/dispatch/dispatch.js";
|
|
40
|
+
export { runToolDispatch, DEFAULT_TRANSIENT_DROP_RETRY, } from "./tool-manager/dispatch/dispatch.js";
|
|
41
41
|
// Slice C: dispatcher is the orchestrator (binding assembly); dispatch authority
|
|
42
42
|
// + OIS policy live under dispatch/; cache/reconcile/health under catalog/.
|
|
43
43
|
export { createSharedDispatcher, pendingKey, injectQueueItemId, assertHostWiringComplete, } from "./tool-manager/orchestrator/dispatcher.js";
|
|
@@ -60,4 +60,6 @@ export { appendNotification, buildPendingTaskNotification } from "./notification
|
|
|
60
60
|
// essentials keeps downstream consumers from needing a separate
|
|
61
61
|
// dependency declaration for the standard-pipeline pattern.
|
|
62
62
|
export { CognitivePipeline, CognitiveTelemetry, CircuitBreaker, HubUnavailableError, WriteCallDedup, DedupTimeoutError, ToolResultCache, FlushAllOnWriteStrategy, ToolDescriptionEnricher, ErrorNormalizer, NormalizedError, ResponseSummarizer, summarizeResult, buildPaginationHint, } from "@apnex/cognitive-layer";
|
|
63
|
+
// hcapskills0 build_core — the harness-neutral, resource-generic HCAP consumer core.
|
|
64
|
+
export * from "./control-plane/index.js";
|
|
63
65
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAYtE,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAEvD,sEAAsE;AAEtE,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAW9D,4EAA4E;AAC5E,uEAAuE;AACvE,6EAA6E;AAC7E,uEAAuE;AACvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAgB9D,OAAO,EACL,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,YAAY,EACZ,WAAW,EACX,wBAAwB,GACzB,MAAM,0BAA0B,CAAC;AAElC,yEAAyE;AACzE,oEAAoE;AACpE,qEAAqE;AAErE,OAAO,EACL,WAAW,EACX,mBAAmB,EACnB,sBAAsB,EACtB,qBAAqB,EACrB,gBAAgB,EAChB,kBAAkB,EAClB,qBAAqB,GACtB,MAAM,uBAAuB,CAAC;AAY/B,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAGrE,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,kBAAkB,GACnB,MAAM,4BAA4B,CAAC;AAGpC,OAAO,EACL,WAAW,EACX,YAAY,EACZ,UAAU,EACV,aAAa,EACb,eAAe,GAChB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAGpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAG1D,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,WAAW,EACX,UAAU,EACV,WAAW,EACX,MAAM,EACN,eAAe,GAChB,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EACL,oBAAoB,EACpB,yBAAyB,EACzB,2BAA2B,GAC5B,MAAM,2BAA2B,CAAC;AAGnC,2EAA2E;AAC3E,wEAAwE;AACxE,8EAA8E;AAC9E,2EAA2E;AAC3E,sEAAsE;AACtE,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAEjE,OAAO,EACL,sBAAsB,EACtB,mBAAmB,EACnB,wBAAwB,EACxB,yBAAyB,GAC1B,MAAM,6BAA6B,CAAC;AAGrC,+EAA+E;AAC/E,8EAA8E;AAC9E,8EAA8E;AAC9E,4EAA4E;AAC5E,4EAA4E;AAC5E,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,8BAA8B,GAC/B,MAAM,8BAA8B,CAAC;AAuBtC,0EAA0E;AAC1E,sEAAsE;AACtE,4EAA4E;AAC5E,iFAAiF;AACjF,2EAA2E;AAC3E,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ResourceActuatorPort, ResourceSpec, ConvergeResult, ManagedObservation } from "../control-plane/contracts.js";
|
|
2
|
+
import type { SkillLedgerPort } from "./contracts.js";
|
|
3
|
+
export interface ClaudeSkillActuatorDeps {
|
|
4
|
+
/** the seat's skills root — $CLAUDE_CONFIG_DIR/skills (INJECTED by the bin; the
|
|
5
|
+
* shared package never reads env). Must exist (the bin creates it cold). */
|
|
6
|
+
skillsDir: string;
|
|
7
|
+
/** durable managed-set persistence (invariant 1). */
|
|
8
|
+
ledger: SkillLedgerPort;
|
|
9
|
+
log?: (msg: string) => void;
|
|
10
|
+
}
|
|
11
|
+
export declare class ClaudeSkillActuator implements ResourceActuatorPort {
|
|
12
|
+
private readonly skillsDir;
|
|
13
|
+
private readonly ledger;
|
|
14
|
+
private readonly log;
|
|
15
|
+
/** the DURABLE managed ledger, in memory for this pass — seeded from persistence at
|
|
16
|
+
* construct, written back after every converge. The ONLY set removal may touch. */
|
|
17
|
+
private readonly managed;
|
|
18
|
+
constructor(deps: ClaudeSkillActuatorDeps);
|
|
19
|
+
converge(desired: readonly ResourceSpec[]): ConvergeResult;
|
|
20
|
+
observeManaged(): ManagedObservation;
|
|
21
|
+
/** merge-copy sourceDir → skillsDir/<name>/ (mkdir -p + recursive force copy). */
|
|
22
|
+
private materialize;
|
|
23
|
+
/** unlink skillsDir/<name>/ — only ever called for a name IN the managed ledger. */
|
|
24
|
+
private unlink;
|
|
25
|
+
/** the managed-scoped on-disk observation: dirs under skillsDir that hold a SKILL.md
|
|
26
|
+
* AND are in our managed ledger. Never reports foreign/bootstrap/legacy skills. */
|
|
27
|
+
private readManagedOnDisk;
|
|
28
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* claude-skill-actuator.ts — ClaudeSkillActuator (hcapskills0 build_claude): the
|
|
3
|
+
* Claude SKILLS concrete `ResourceActuatorPort` impl. The mirror of `PiToolActuator`
|
|
4
|
+
* for a different (harness, kind): pi actuates tools via an in-process ExtensionAPI;
|
|
5
|
+
* this actuates skills via the FILESYSTEM claude watches ($CLAUDE_CONFIG_DIR/skills/).
|
|
6
|
+
*
|
|
7
|
+
* RUNTIME-FREE by construction — node:fs only, NO claude SDK — which is what lets the
|
|
8
|
+
* headless seed bin materialize skills before/without the claude runtime (design §5).
|
|
9
|
+
* The SOLE coupling to the claude skill surface (the fs layout + SKILL.md tree shape);
|
|
10
|
+
* skill-isms live here, NEVER in control-plane/ (the import-boundary test guards it).
|
|
11
|
+
*
|
|
12
|
+
* COEXISTENCE UNLINK SAFETY (design §9 firebreak; the load-bearing fleet-safety
|
|
13
|
+
* property during coexist with legacy `mission_kit_sync`, which writes the SAME dir):
|
|
14
|
+
* 1. The managed ledger is DURABLE (injected `SkillLedgerPort`; the bin file-backs
|
|
15
|
+
* it) — a fresh headless process each launch reconstructs its managed set.
|
|
16
|
+
* 2. Removal is STRICTLY ledger-scoped: the unlink set is `managed \ managedEnabled`,
|
|
17
|
+
* iterated over the persisted ledger — NEVER `readdir`. The actuator is
|
|
18
|
+
* structurally incapable of unlinking a path it did not itself materialize
|
|
19
|
+
* (bootstrap / user / mission_kit_sync skills are not in the ledger → untouchable).
|
|
20
|
+
* 3. Materialize is merge-copy (mkdir -p + recursive force copy), mirroring
|
|
21
|
+
* `mission_kit_sync`'s `cp` — a shared-name collision writes identical pinned
|
|
22
|
+
* content, never a split-brain.
|
|
23
|
+
*
|
|
24
|
+
* Seed-time materialize is race-free (the bin completes before `exec claude`, so the
|
|
25
|
+
* watcher is not yet running). Atomic temp-dir+rename is a DEFERRED refinement only
|
|
26
|
+
* the future mid-session dynamic path needs.
|
|
27
|
+
*/
|
|
28
|
+
import { cpSync, existsSync, mkdirSync, readdirSync, rmSync, statSync, } from "node:fs";
|
|
29
|
+
import { join } from "node:path";
|
|
30
|
+
export class ClaudeSkillActuator {
|
|
31
|
+
skillsDir;
|
|
32
|
+
ledger;
|
|
33
|
+
log;
|
|
34
|
+
/** the DURABLE managed ledger, in memory for this pass — seeded from persistence at
|
|
35
|
+
* construct, written back after every converge. The ONLY set removal may touch. */
|
|
36
|
+
managed;
|
|
37
|
+
constructor(deps) {
|
|
38
|
+
this.skillsDir = deps.skillsDir;
|
|
39
|
+
this.ledger = deps.ledger;
|
|
40
|
+
this.log = deps.log ?? (() => { });
|
|
41
|
+
this.managed = new Set(this.ledger.read());
|
|
42
|
+
}
|
|
43
|
+
converge(desired) {
|
|
44
|
+
const managedEnabled = desired.filter((s) => s.enabled).map((s) => s.name);
|
|
45
|
+
const managedEnabledSet = new Set(managedEnabled);
|
|
46
|
+
try {
|
|
47
|
+
// Level 1 — MATERIALIZE every enabled declared skill (merge-copy, idempotent).
|
|
48
|
+
// A materialize fault (missing source tree, unwritable target) is an actuation
|
|
49
|
+
// fault → status:"failed" so the seed bin can fail-closed (abort launch).
|
|
50
|
+
for (const spec of desired) {
|
|
51
|
+
if (!spec.enabled)
|
|
52
|
+
continue;
|
|
53
|
+
this.materialize(spec.name, spec.definition);
|
|
54
|
+
this.managed.add(spec.name); // ledger records only what we actually wrote
|
|
55
|
+
}
|
|
56
|
+
// Level 2 — REMOVE the set-subtraction, STRICTLY ledger-scoped (invariant 2):
|
|
57
|
+
// `managed \ managedEnabled` — a skill THIS actuator materialized in a prior
|
|
58
|
+
// pass that is no longer desired-enabled. We iterate the persisted ledger, NEVER
|
|
59
|
+
// readdir, so a foreign/bootstrap/mission_kit_sync skill (absent from the ledger)
|
|
60
|
+
// can never be unlinked. The set-diff gate (role_map ⊇ mks-delivered) is the
|
|
61
|
+
// second guarantee that an mks-delivered skill is never in this set.
|
|
62
|
+
for (const name of [...this.managed]) {
|
|
63
|
+
if (managedEnabledSet.has(name))
|
|
64
|
+
continue;
|
|
65
|
+
this.unlink(name);
|
|
66
|
+
this.managed.delete(name);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
// persist whatever we durably achieved before the fault (no ledger/disk skew).
|
|
71
|
+
this.ledger.write([...this.managed]);
|
|
72
|
+
return {
|
|
73
|
+
status: "failed",
|
|
74
|
+
klass: "actuate-failed",
|
|
75
|
+
detail: err?.message ?? String(err),
|
|
76
|
+
desiredManaged: managedEnabled,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
this.ledger.write([...this.managed]);
|
|
80
|
+
// Observe the MANAGED subset fresh off DISK (readdir ∩ managed). The filesystem is
|
|
81
|
+
// synchronous + authoritative, so a successful materialize is observable this pass
|
|
82
|
+
// → converged (no next-turn latency like pi; the claude WATCHER's async pickup is
|
|
83
|
+
// the harness's concern, not the actuator's convergence signal).
|
|
84
|
+
const observedManaged = this.readManagedOnDisk();
|
|
85
|
+
const status = sameSet(observedManaged, managedEnabled) ? "converged" : "pending";
|
|
86
|
+
return { status, desiredManaged: managedEnabled };
|
|
87
|
+
}
|
|
88
|
+
observeManaged() {
|
|
89
|
+
return {
|
|
90
|
+
observedManaged: this.readManagedOnDisk(),
|
|
91
|
+
managedNames: [...this.managed],
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/** merge-copy sourceDir → skillsDir/<name>/ (mkdir -p + recursive force copy). */
|
|
95
|
+
materialize(name, def) {
|
|
96
|
+
const sourceDir = def?.sourceDir;
|
|
97
|
+
if (typeof sourceDir !== "string" || sourceDir.length === 0) {
|
|
98
|
+
throw new Error(`skill '${name}': definition.sourceDir missing`);
|
|
99
|
+
}
|
|
100
|
+
if (!existsSync(join(sourceDir, "SKILL.md"))) {
|
|
101
|
+
throw new Error(`skill '${name}': source tree has no SKILL.md (${sourceDir}) — refusing to materialize an empty skill`);
|
|
102
|
+
}
|
|
103
|
+
const dest = join(this.skillsDir, name);
|
|
104
|
+
mkdirSync(dest, { recursive: true });
|
|
105
|
+
cpSync(sourceDir, dest, { recursive: true, force: true });
|
|
106
|
+
this.log(`[hcap-skills] materialized '${name}' → ${dest}`);
|
|
107
|
+
}
|
|
108
|
+
/** unlink skillsDir/<name>/ — only ever called for a name IN the managed ledger. */
|
|
109
|
+
unlink(name) {
|
|
110
|
+
const dest = join(this.skillsDir, name);
|
|
111
|
+
rmSync(dest, { recursive: true, force: true });
|
|
112
|
+
this.log(`[hcap-skills] unlinked managed skill '${name}' (no longer desired)`);
|
|
113
|
+
}
|
|
114
|
+
/** the managed-scoped on-disk observation: dirs under skillsDir that hold a SKILL.md
|
|
115
|
+
* AND are in our managed ledger. Never reports foreign/bootstrap/legacy skills. */
|
|
116
|
+
readManagedOnDisk() {
|
|
117
|
+
let entries;
|
|
118
|
+
try {
|
|
119
|
+
entries = readdirSync(this.skillsDir);
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
return []; // skillsDir absent (cold seat pre-mkdir) → nothing observed yet.
|
|
123
|
+
}
|
|
124
|
+
return entries.filter((name) => this.managed.has(name) &&
|
|
125
|
+
isDir(join(this.skillsDir, name)) &&
|
|
126
|
+
existsSync(join(this.skillsDir, name, "SKILL.md")));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function isDir(p) {
|
|
130
|
+
try {
|
|
131
|
+
return statSync(p).isDirectory();
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/** Order-independent set equality over string name lists. */
|
|
138
|
+
function sameSet(a, b) {
|
|
139
|
+
if (a.length !== b.length)
|
|
140
|
+
return false;
|
|
141
|
+
const sa = new Set(a);
|
|
142
|
+
for (const x of b)
|
|
143
|
+
if (!sa.has(x))
|
|
144
|
+
return false;
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=claude-skill-actuator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-skill-actuator.js","sourceRoot":"","sources":["../../src/skills/claude-skill-actuator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* skills/contracts.ts — skills-kind-LOCAL types (hcapskills0 build_claude). These are
|
|
3
|
+
* NOT part of the neutral control-plane/ Controller; they carry the skill-ism (a skill is a
|
|
4
|
+
* directory TREE materialized on a filesystem claude watches). The neutral Controller sees
|
|
5
|
+
* only `ResourceSpec` with an OPAQUE `definition` — here we pin what that opaque
|
|
6
|
+
* payload IS for the skills kind, and the ledger the actuator scopes itself with.
|
|
7
|
+
*
|
|
8
|
+
* Home rule (ratified 2026-07-13): an actuator lives at the LOWEST layer that can
|
|
9
|
+
* express its coupling. `ClaudeSkillActuator` is contract-coupled but RUNTIME-FREE
|
|
10
|
+
* (node:fs to a path claude watches; no claude SDK) → it lives in the shared package
|
|
11
|
+
* as a control-plane/ SIBLING, reachable by the headless seed bin — which is exactly
|
|
12
|
+
* what lets §5's entrypoint materialize skills before/without the claude runtime.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* The opaque `ResourceSpec.definition` payload for a skill: WHERE its SKILL.md tree is
|
|
16
|
+
* read from. The seed bin resolves this (a path inside the pinned-source clone); the
|
|
17
|
+
* actuator only materializes it (fs copy) — git/clone never enters the actuator.
|
|
18
|
+
*/
|
|
19
|
+
export interface SkillDefinition {
|
|
20
|
+
/** absolute path to the source `skills/<name>/` tree (contains SKILL.md; depth:2). */
|
|
21
|
+
sourceDir: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Persistence for the managed-skill LEDGER — the skill-ids THIS actuator has
|
|
25
|
+
* materialized. It scopes observe + removal to our OWN set, so a foreign/bootstrap
|
|
26
|
+
* skill or a legacy `mission_kit_sync`-copied skill is never mistaken for drift nor
|
|
27
|
+
* unlinked (the coexist firebreak, design §9). Cold seat ⇒ `read()` returns `[]`
|
|
28
|
+
* (the ledger is created empty on the first `write`).
|
|
29
|
+
*/
|
|
30
|
+
export interface SkillLedgerPort {
|
|
31
|
+
read(): string[];
|
|
32
|
+
write(names: readonly string[]): void;
|
|
33
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* skills/contracts.ts — skills-kind-LOCAL types (hcapskills0 build_claude). These are
|
|
3
|
+
* NOT part of the neutral control-plane/ Controller; they carry the skill-ism (a skill is a
|
|
4
|
+
* directory TREE materialized on a filesystem claude watches). The neutral Controller sees
|
|
5
|
+
* only `ResourceSpec` with an OPAQUE `definition` — here we pin what that opaque
|
|
6
|
+
* payload IS for the skills kind, and the ledger the actuator scopes itself with.
|
|
7
|
+
*
|
|
8
|
+
* Home rule (ratified 2026-07-13): an actuator lives at the LOWEST layer that can
|
|
9
|
+
* express its coupling. `ClaudeSkillActuator` is contract-coupled but RUNTIME-FREE
|
|
10
|
+
* (node:fs to a path claude watches; no claude SDK) → it lives in the shared package
|
|
11
|
+
* as a control-plane/ SIBLING, reachable by the headless seed bin — which is exactly
|
|
12
|
+
* what lets §5's entrypoint materialize skills before/without the claude runtime.
|
|
13
|
+
*/
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=contracts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contracts.js","sourceRoot":"","sources":["../../src/skills/contracts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* file-skill-ledger.ts — FileSkillLedger (hcapskills0 build_claude, invariant 1): the
|
|
3
|
+
* DURABLE backing for the managed-skill ledger. A JSON sidecar the seed bin points at
|
|
4
|
+
* (kept OUTSIDE skillsDir so it never confuses claude's `<name>/SKILL.md` scanner).
|
|
5
|
+
* A fresh headless process each launch reconstructs its managed set from this file.
|
|
6
|
+
*
|
|
7
|
+
* Cold seat: the file is absent → `read()` returns `[]` (created empty on first write).
|
|
8
|
+
* Corrupt ledger: reads as EMPTY (fail-safe) — the actuator re-materializes its desired
|
|
9
|
+
* set + rewrites a clean ledger, and unlinks NOTHING (removal is managed-scoped and the
|
|
10
|
+
* managed set starts empty on an unreadable ledger; orphans are safe, missing skills are
|
|
11
|
+
* not). This is the deliberately-safe failure direction for the coexist firebreak.
|
|
12
|
+
*/
|
|
13
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
14
|
+
import { dirname } from "node:path";
|
|
15
|
+
export class FileSkillLedger {
|
|
16
|
+
path;
|
|
17
|
+
constructor(path) {
|
|
18
|
+
this.path = path;
|
|
19
|
+
}
|
|
20
|
+
read() {
|
|
21
|
+
if (!existsSync(this.path))
|
|
22
|
+
return [];
|
|
23
|
+
try {
|
|
24
|
+
const raw = JSON.parse(readFileSync(this.path, "utf8"));
|
|
25
|
+
if (!Array.isArray(raw?.managed))
|
|
26
|
+
return [];
|
|
27
|
+
return raw.managed.filter((n) => typeof n === "string");
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
write(names) {
|
|
34
|
+
mkdirSync(dirname(this.path), { recursive: true });
|
|
35
|
+
writeFileSync(this.path, `${JSON.stringify({ managed: [...names].sort() }, null, 2)}\n`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=file-skill-ledger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-skill-ledger.js","sourceRoot":"","sources":["../../src/skills/file-skill-ledger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,MAAM,OAAO,eAAe;IACG;IAA7B,YAA6B,IAAY;QAAZ,SAAI,GAAJ,IAAI,CAAQ;IAAG,CAAC;IAE7C,IAAI;QACF,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,OAAO,EAAE,CAAC;QACtC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAA0B,CAAC;YACjF,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC;gBAAE,OAAO,EAAE,CAAC;YAC5C,OAAO,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC;QACvE,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,KAAwB;QAC5B,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACnD,aAAa,CACX,IAAI,CAAC,IAAI,EACT,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAC/D,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* skills/ — the Claude SKILLS kind-module (hcapskills0 build_claude): the SECOND kind
|
|
3
|
+
* composed onto the neutral control-plane/ Controller (the FIRST being pi-tools, in
|
|
4
|
+
* adapters/pi-plugin). Contains the ClaudeSkillActuator (outbound edge-seam) +
|
|
5
|
+
* RoleSkillSource (inbound) + the wanted-bundles manifest expansion + the durable
|
|
6
|
+
* ledger. Skill-isms (node:fs, SKILL.md layout, the managed ledger) live HERE — NEVER
|
|
7
|
+
* in control-plane/ (the import-boundary test guards that). The Controller still depends
|
|
8
|
+
* only on ResourceActuatorPort; the seed bin constructs + injects this actuator.
|
|
9
|
+
*/
|
|
10
|
+
export type { SkillDefinition, SkillLedgerPort } from "./contracts.js";
|
|
11
|
+
export { ClaudeSkillActuator, type ClaudeSkillActuatorDeps, } from "./claude-skill-actuator.js";
|
|
12
|
+
export { FileSkillLedger } from "./file-skill-ledger.js";
|
|
13
|
+
export { RoleSkillSource, type RoleSkillSourceDeps } from "./role-skill-source.js";
|
|
14
|
+
export { parseWantedBundles, parseBundleSkills, expandWantedBundles, type WantedBundles, } from "./manifest.js";
|
|
15
|
+
export { missingFromRoleMap } from "./set-diff-gate.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { ClaudeSkillActuator, } from "./claude-skill-actuator.js";
|
|
2
|
+
export { FileSkillLedger } from "./file-skill-ledger.js";
|
|
3
|
+
export { RoleSkillSource } from "./role-skill-source.js";
|
|
4
|
+
export { parseWantedBundles, parseBundleSkills, expandWantedBundles, } from "./manifest.js";
|
|
5
|
+
export { missingFromRoleMap } from "./set-diff-gate.js";
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/skills/index.ts"],"names":[],"mappings":"AAUA,OAAO,EACL,mBAAmB,GAEpB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,eAAe,EAA4B,MAAM,wBAAwB,CAAC;AACnF,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,mBAAmB,GAEpB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* manifest.ts — wanted-bundles.yaml parsing + MECHANICAL bundle-expand (hcapskills0
|
|
3
|
+
* build_claude, design §5). Pure functions, NO fs/env/git (the seed bin reads files
|
|
4
|
+
* and injects a bundle-reader). No yaml dependency — a focused subset parser for the
|
|
5
|
+
* manifest shape we own (mirrors ois `_mk_yaml_scalar` / `_mk_yaml_list`), matching the
|
|
6
|
+
* pinned-source contract so the derived role-map is exactly the set legacy delivers.
|
|
7
|
+
*
|
|
8
|
+
* Format (ois/manifests/skill-sync/wanted-bundles.yaml):
|
|
9
|
+
* source_repo: <git url>
|
|
10
|
+
* source_ref: <sha> # PINNED for reproducibility
|
|
11
|
+
* bundles: [] | - <name> # resolved via <clone>/bundles/<name>.yaml `skills:`
|
|
12
|
+
* extra_skills: - <name> # individual skill via <clone>/skills/<name>/SKILL.md
|
|
13
|
+
*/
|
|
14
|
+
export interface WantedBundles {
|
|
15
|
+
sourceRepo: string;
|
|
16
|
+
sourceRef: string;
|
|
17
|
+
bundles: string[];
|
|
18
|
+
extraSkills: string[];
|
|
19
|
+
}
|
|
20
|
+
/** Parse the top-level manifest (pure). */
|
|
21
|
+
export declare function parseWantedBundles(text: string): WantedBundles;
|
|
22
|
+
/** Parse a bundle file's `skills:` list (pure). */
|
|
23
|
+
export declare function parseBundleSkills(text: string): string[];
|
|
24
|
+
/**
|
|
25
|
+
* The MECHANICAL expansion: bundle skills (resolved via the injected reader) ∪
|
|
26
|
+
* extra_skills, deduped + ordered. `readBundleSkills` reads `<clone>/bundles/<b>.yaml`
|
|
27
|
+
* — injected by the bin so this stays fs-free. Slice-1 manifest has `bundles: []`, so
|
|
28
|
+
* only extra_skills contribute today; the bundle path is implemented for the general case.
|
|
29
|
+
*/
|
|
30
|
+
export declare function expandWantedBundles(manifest: WantedBundles, readBundleSkills: (bundleName: string) => string[]): string[];
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* manifest.ts — wanted-bundles.yaml parsing + MECHANICAL bundle-expand (hcapskills0
|
|
3
|
+
* build_claude, design §5). Pure functions, NO fs/env/git (the seed bin reads files
|
|
4
|
+
* and injects a bundle-reader). No yaml dependency — a focused subset parser for the
|
|
5
|
+
* manifest shape we own (mirrors ois `_mk_yaml_scalar` / `_mk_yaml_list`), matching the
|
|
6
|
+
* pinned-source contract so the derived role-map is exactly the set legacy delivers.
|
|
7
|
+
*
|
|
8
|
+
* Format (ois/manifests/skill-sync/wanted-bundles.yaml):
|
|
9
|
+
* source_repo: <git url>
|
|
10
|
+
* source_ref: <sha> # PINNED for reproducibility
|
|
11
|
+
* bundles: [] | - <name> # resolved via <clone>/bundles/<name>.yaml `skills:`
|
|
12
|
+
* extra_skills: - <name> # individual skill via <clone>/skills/<name>/SKILL.md
|
|
13
|
+
*/
|
|
14
|
+
/** Parse the top-level manifest (pure). */
|
|
15
|
+
export function parseWantedBundles(text) {
|
|
16
|
+
return {
|
|
17
|
+
sourceRepo: scalar(text, "source_repo") ?? "",
|
|
18
|
+
sourceRef: scalar(text, "source_ref") ?? "",
|
|
19
|
+
bundles: list(text, "bundles"),
|
|
20
|
+
extraSkills: list(text, "extra_skills"),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/** Parse a bundle file's `skills:` list (pure). */
|
|
24
|
+
export function parseBundleSkills(text) {
|
|
25
|
+
return list(text, "skills");
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The MECHANICAL expansion: bundle skills (resolved via the injected reader) ∪
|
|
29
|
+
* extra_skills, deduped + ordered. `readBundleSkills` reads `<clone>/bundles/<b>.yaml`
|
|
30
|
+
* — injected by the bin so this stays fs-free. Slice-1 manifest has `bundles: []`, so
|
|
31
|
+
* only extra_skills contribute today; the bundle path is implemented for the general case.
|
|
32
|
+
*/
|
|
33
|
+
export function expandWantedBundles(manifest, readBundleSkills) {
|
|
34
|
+
const set = new Set();
|
|
35
|
+
for (const b of manifest.bundles) {
|
|
36
|
+
for (const s of readBundleSkills(b))
|
|
37
|
+
set.add(s);
|
|
38
|
+
}
|
|
39
|
+
for (const s of manifest.extraSkills)
|
|
40
|
+
set.add(s);
|
|
41
|
+
return [...set].sort();
|
|
42
|
+
}
|
|
43
|
+
// ── subset YAML helpers ──────────────────────────────────────────────────────
|
|
44
|
+
function scalar(text, key) {
|
|
45
|
+
for (const rawLine of text.split("\n")) {
|
|
46
|
+
const line = stripComment(rawLine);
|
|
47
|
+
const m = line.match(new RegExp(`^${escapeKey(key)}:\\s*(.*)$`));
|
|
48
|
+
if (m)
|
|
49
|
+
return unquote(m[1].trim()) || undefined;
|
|
50
|
+
}
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
function list(text, key) {
|
|
54
|
+
const lines = text.split("\n");
|
|
55
|
+
const out = [];
|
|
56
|
+
let inList = false;
|
|
57
|
+
for (const rawLine of lines) {
|
|
58
|
+
const line = stripComment(rawLine);
|
|
59
|
+
if (!inList) {
|
|
60
|
+
const m = line.match(new RegExp(`^${escapeKey(key)}:\\s*(.*)$`));
|
|
61
|
+
if (!m)
|
|
62
|
+
continue;
|
|
63
|
+
const inline = m[1].trim();
|
|
64
|
+
if (inline === "[]" || inline === "") {
|
|
65
|
+
if (inline === "[]")
|
|
66
|
+
return []; // explicit empty inline list
|
|
67
|
+
inList = true; // block list follows on subsequent indented `- ` lines
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
// inline non-empty list `[a, b]`
|
|
71
|
+
if (inline.startsWith("[") && inline.endsWith("]")) {
|
|
72
|
+
return inline
|
|
73
|
+
.slice(1, -1)
|
|
74
|
+
.split(",")
|
|
75
|
+
.map((s) => unquote(s.trim()))
|
|
76
|
+
.filter((s) => s.length > 0);
|
|
77
|
+
}
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
if (line.trim() === "")
|
|
81
|
+
continue; // blank inside/after block — tolerate
|
|
82
|
+
const item = line.match(/^\s+-\s*(.+)$/);
|
|
83
|
+
if (item) {
|
|
84
|
+
const v = unquote(item[1].trim());
|
|
85
|
+
if (v.length > 0)
|
|
86
|
+
out.push(v);
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
break; // dedent / next top-level key → list ended
|
|
90
|
+
}
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
93
|
+
function stripComment(line) {
|
|
94
|
+
// drop a whole-line comment; leave inline `#` inside values alone (our values —
|
|
95
|
+
// urls, shas, skill-ids — never contain `#`, and quoted values are unquoted below).
|
|
96
|
+
if (/^\s*#/.test(line))
|
|
97
|
+
return "";
|
|
98
|
+
return line.replace(/\s+#.*$/, "");
|
|
99
|
+
}
|
|
100
|
+
function unquote(s) {
|
|
101
|
+
if ((s.startsWith('"') && s.endsWith('"')) ||
|
|
102
|
+
(s.startsWith("'") && s.endsWith("'"))) {
|
|
103
|
+
return s.slice(1, -1);
|
|
104
|
+
}
|
|
105
|
+
return s;
|
|
106
|
+
}
|
|
107
|
+
function escapeKey(key) {
|
|
108
|
+
return key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../src/skills/manifest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* role-skill-source.ts — RoleSkillSource (hcapskills0 build_claude): the skills-kind
|
|
3
|
+
* SOURCE (inbound edge-seam = the desired-set). A STATIC role→skills declaration that
|
|
4
|
+
* emits `ResourceSpec[]` for the neutral SpecStore — the mirror of pi's HubSpecSource,
|
|
5
|
+
* but static (no Hub skills-catalog / skillsRevision in slice-1; the source-poll half
|
|
6
|
+
* is deferred, design §4). The source-poll half is NOT exercised here.
|
|
7
|
+
*
|
|
8
|
+
* Slice-1 is role-AGNOSTIC (every fleet role → the same manifest-expanded set, which is
|
|
9
|
+
* exactly what mission_kit_sync delivers today), but the signature is per-role so
|
|
10
|
+
* per-role bundles generalize without a reshape. The skill CONTENT source (`sourceDirFor`)
|
|
11
|
+
* is injected by the bin (the pinned clone), so the source itself stays fs/env-free.
|
|
12
|
+
*/
|
|
13
|
+
import type { ResourceSpec } from "../control-plane/contracts.js";
|
|
14
|
+
export interface RoleSkillSourceDeps {
|
|
15
|
+
/** static role→skill-ids overrides; a role absent here falls back to `defaultSkills`. */
|
|
16
|
+
roleSkills?: ReadonlyMap<string, readonly string[]>;
|
|
17
|
+
/** the set every role receives when not overridden (slice-1: the whole expanded set). */
|
|
18
|
+
defaultSkills: readonly string[];
|
|
19
|
+
/** resolve a skill-id → the source tree dir the actuator copies FROM (bin-injected). */
|
|
20
|
+
sourceDirFor: (skillId: string) => string;
|
|
21
|
+
}
|
|
22
|
+
export declare class RoleSkillSource {
|
|
23
|
+
private readonly deps;
|
|
24
|
+
constructor(deps: RoleSkillSourceDeps);
|
|
25
|
+
/** the declared skill-ids for a role — the load-bearing input to the set-diff gate. */
|
|
26
|
+
skillIdsForRole(role: string): string[];
|
|
27
|
+
/** the desired ResourceSpec set for a role: enabled, opaque `{sourceDir}` definition. */
|
|
28
|
+
specsForRole(role: string): ResourceSpec[];
|
|
29
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export class RoleSkillSource {
|
|
2
|
+
deps;
|
|
3
|
+
constructor(deps) {
|
|
4
|
+
this.deps = deps;
|
|
5
|
+
}
|
|
6
|
+
/** the declared skill-ids for a role — the load-bearing input to the set-diff gate. */
|
|
7
|
+
skillIdsForRole(role) {
|
|
8
|
+
const ids = this.deps.roleSkills?.get(role) ?? this.deps.defaultSkills;
|
|
9
|
+
return [...ids];
|
|
10
|
+
}
|
|
11
|
+
/** the desired ResourceSpec set for a role: enabled, opaque `{sourceDir}` definition. */
|
|
12
|
+
specsForRole(role) {
|
|
13
|
+
return this.skillIdsForRole(role).map((id) => ({
|
|
14
|
+
name: id,
|
|
15
|
+
definition: { sourceDir: this.deps.sourceDirFor(id) },
|
|
16
|
+
enabled: true,
|
|
17
|
+
}));
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=role-skill-source.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"role-skill-source.js","sourceRoot":"","sources":["../../src/skills/role-skill-source.ts"],"names":[],"mappings":"AAwBA,MAAM,OAAO,eAAe;IACG;IAA7B,YAA6B,IAAyB;QAAzB,SAAI,GAAJ,IAAI,CAAqB;IAAG,CAAC;IAE1D,uFAAuF;IACvF,eAAe,CAAC,IAAY;QAC1B,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC;QACvE,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC;IAClB,CAAC;IAED,yFAAyF;IACzF,YAAY,CAAC,IAAY;QACvB,OAAO,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;YAC7C,IAAI,EAAE,EAAE;YACR,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,EAA4B;YAC/E,OAAO,EAAE,IAAI;SACd,CAAC,CAAC,CAAC;IACN,CAAC;CACF"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* set-diff-gate.ts — the coexist acceptance gate (hcapskills0 build_claude; design
|
|
3
|
+
* §8.2 + unlink-safety invariant 3b). During coexist, `role_map(role)` MUST be a
|
|
4
|
+
* SUPERSET of what `mission_kit_sync` delivers for that role. Why it's LOAD-BEARING,
|
|
5
|
+
* not cosmetic: a skill X that this actuator materialized (∈ its ledger) but which is
|
|
6
|
+
* later dropped from role_map while mks still delivers X lands in `managed \
|
|
7
|
+
* managedEnabled` → the removal loop would unlink X → an mks-delivered skill is
|
|
8
|
+
* clobbered. Ledger-scoping does NOT prevent this (X IS in the ledger). ONLY
|
|
9
|
+
* `role_map ⊇ mks-delivered` holding AT RUNTIME does (X∈mks ⟹ X∈role_map ⟹
|
|
10
|
+
* X∈managedEnabled ⟹ X never in the removal set).
|
|
11
|
+
*
|
|
12
|
+
* So this is evaluated as a LIVE, FAIL-CLOSED check in the seed bin (abort launch on
|
|
13
|
+
* violation) — the CI unit test is the fast signal, the seed-time gate is the
|
|
14
|
+
* runtime guarantee under manifest drift. Pure: the bin injects both sets.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* The mks-delivered skills MISSING from a role's role_map (empty ⇒ the gate passes).
|
|
18
|
+
* A non-empty result is a HARD fail-closed condition at seed time.
|
|
19
|
+
*/
|
|
20
|
+
export declare function missingFromRoleMap(roleSkills: readonly string[], mksDelivered: readonly string[]): string[];
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* set-diff-gate.ts — the coexist acceptance gate (hcapskills0 build_claude; design
|
|
3
|
+
* §8.2 + unlink-safety invariant 3b). During coexist, `role_map(role)` MUST be a
|
|
4
|
+
* SUPERSET of what `mission_kit_sync` delivers for that role. Why it's LOAD-BEARING,
|
|
5
|
+
* not cosmetic: a skill X that this actuator materialized (∈ its ledger) but which is
|
|
6
|
+
* later dropped from role_map while mks still delivers X lands in `managed \
|
|
7
|
+
* managedEnabled` → the removal loop would unlink X → an mks-delivered skill is
|
|
8
|
+
* clobbered. Ledger-scoping does NOT prevent this (X IS in the ledger). ONLY
|
|
9
|
+
* `role_map ⊇ mks-delivered` holding AT RUNTIME does (X∈mks ⟹ X∈role_map ⟹
|
|
10
|
+
* X∈managedEnabled ⟹ X never in the removal set).
|
|
11
|
+
*
|
|
12
|
+
* So this is evaluated as a LIVE, FAIL-CLOSED check in the seed bin (abort launch on
|
|
13
|
+
* violation) — the CI unit test is the fast signal, the seed-time gate is the
|
|
14
|
+
* runtime guarantee under manifest drift. Pure: the bin injects both sets.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* The mks-delivered skills MISSING from a role's role_map (empty ⇒ the gate passes).
|
|
18
|
+
* A non-empty result is a HARD fail-closed condition at seed time.
|
|
19
|
+
*/
|
|
20
|
+
export function missingFromRoleMap(roleSkills, mksDelivered) {
|
|
21
|
+
const have = new Set(roleSkills);
|
|
22
|
+
return [...new Set(mksDelivered)].filter((s) => !have.has(s)).sort();
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=set-diff-gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"set-diff-gate.js","sourceRoot":"","sources":["../../src/skills/set-diff-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAChC,UAA6B,EAC7B,YAA+B;IAE/B,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC;IACjC,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AACvE,CAAC"}
|