@wairon/cli 5.1.1-dev.70 → 5.1.1-dev.72
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/cli/index.js +788 -566
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +172 -106
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -65,7 +65,7 @@ var init_defaults = __esm({
|
|
|
65
65
|
copilot: ".github/prompts",
|
|
66
66
|
codex: ".codex/agents"
|
|
67
67
|
};
|
|
68
|
-
WAIRON_VERSION = "5.1.1-dev.
|
|
68
|
+
WAIRON_VERSION = "5.1.1-dev.72";
|
|
69
69
|
GITHUB_REPO = "SYW-Apps/Waffle-AIron";
|
|
70
70
|
SUPPORTED_ALIASES = ["wai"];
|
|
71
71
|
SCAN_EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
@@ -1860,7 +1860,13 @@ var init_specs = __esm({
|
|
|
1860
1860
|
/** The L2 component that realizes this public interface (the subsystem's published surface). */
|
|
1861
1861
|
component: SpecIdSchema.optional(),
|
|
1862
1862
|
/** Optional L3 interface on that component backing this entry. */
|
|
1863
|
-
interface: SpecIdSchema.optional()
|
|
1863
|
+
interface: SpecIdSchema.optional(),
|
|
1864
|
+
/**
|
|
1865
|
+
* The subsystems this surface is published to. Absent, anyone may depend on
|
|
1866
|
+
* it; present, only these subsystems may — when every entry publishing the
|
|
1867
|
+
* component names consumers, the union of those lists is the whole set.
|
|
1868
|
+
*/
|
|
1869
|
+
consumers: import_zod8.z.array(SpecIdSchema).optional()
|
|
1864
1870
|
});
|
|
1865
1871
|
TrustedLinkSchema = import_zod8.z.object({
|
|
1866
1872
|
/** The peer subsystem id this link sanctions tight coupling with. */
|
|
@@ -9477,6 +9483,39 @@ var init_public_surface_binding = __esm({
|
|
|
9477
9483
|
}
|
|
9478
9484
|
});
|
|
9479
9485
|
|
|
9486
|
+
// src/core/rules/integrity/public-surface-consumers.ts
|
|
9487
|
+
var publicSurfaceConsumersRule;
|
|
9488
|
+
var init_public_surface_consumers = __esm({
|
|
9489
|
+
"src/core/rules/integrity/public-surface-consumers.ts"() {
|
|
9490
|
+
"use strict";
|
|
9491
|
+
init_models();
|
|
9492
|
+
publicSurfaceConsumersRule = {
|
|
9493
|
+
name: "public-surface-consumers",
|
|
9494
|
+
description: "Every subsystem a published surface names as its consumer must exist in the tree. A consumers list restricts who may depend on the surface, so an id that names nothing locks the surface against a caller nobody can be \u2014 and a typo there silently locks out the caller that was meant, which the boundary rule would then report against the wrong party.",
|
|
9495
|
+
codes: [
|
|
9496
|
+
{ code: "PUBLIC_INTERFACE_UNKNOWN_CONSUMER", defaultSeverity: "error", summary: "A published surface names as its consumer a subsystem the tree does not have" }
|
|
9497
|
+
],
|
|
9498
|
+
check(ctx) {
|
|
9499
|
+
const known = new Set(ctx.subsystems.map((s) => s.id));
|
|
9500
|
+
for (const sub of ctx.subsystems) {
|
|
9501
|
+
for (const pi of sub.publicInterfaces) {
|
|
9502
|
+
for (const consumer of pi.consumers ?? []) {
|
|
9503
|
+
if (known.has(consumer)) continue;
|
|
9504
|
+
ctx.addIssue(
|
|
9505
|
+
"error",
|
|
9506
|
+
"PUBLIC_INTERFACE_UNKNOWN_CONSUMER",
|
|
9507
|
+
`Subsystem "${sub.id}" publishes "${pi.component ?? `${pi.type} ${pi.details}`}" to consumer "${consumer}", which names no subsystem of this tree. Name an existing subsystem, or drop the id \u2014 a consumers list admits only the subsystems it names.`,
|
|
9508
|
+
sub.id,
|
|
9509
|
+
isDraftSubsystem(sub)
|
|
9510
|
+
);
|
|
9511
|
+
}
|
|
9512
|
+
}
|
|
9513
|
+
}
|
|
9514
|
+
}
|
|
9515
|
+
};
|
|
9516
|
+
}
|
|
9517
|
+
});
|
|
9518
|
+
|
|
9480
9519
|
// src/core/rules/integrity/public-surface-declared-type.ts
|
|
9481
9520
|
var pubTypeMatches, EVENT_VOCAB, expectedFor, publicSurfaceDeclaredTypeRule;
|
|
9482
9521
|
var init_public_surface_declared_type = __esm({
|
|
@@ -11009,6 +11048,11 @@ var init_auth_source_wiring = __esm({
|
|
|
11009
11048
|
});
|
|
11010
11049
|
|
|
11011
11050
|
// src/core/rules/doctrine/subsystem-boundary-dependencies.ts
|
|
11051
|
+
function restrictedConsumers(ctx, subsystemId, componentId) {
|
|
11052
|
+
const entries = (ctx.subsystems.find((s) => s.id === subsystemId)?.publicInterfaces ?? []).filter((pi) => pi.component === componentId);
|
|
11053
|
+
if (entries.length === 0 || entries.some((pi) => pi.consumers === void 0)) return null;
|
|
11054
|
+
return [...new Set(entries.flatMap((pi) => pi.consumers ?? []))];
|
|
11055
|
+
}
|
|
11012
11056
|
var subsystemBoundaryDepsRule;
|
|
11013
11057
|
var init_subsystem_boundary_dependencies = __esm({
|
|
11014
11058
|
"src/core/rules/doctrine/subsystem-boundary-dependencies.ts"() {
|
|
@@ -11016,14 +11060,15 @@ var init_subsystem_boundary_dependencies = __esm({
|
|
|
11016
11060
|
init_models();
|
|
11017
11061
|
subsystemBoundaryDepsRule = {
|
|
11018
11062
|
name: "subsystem-boundary-dependencies",
|
|
11019
|
-
description: "Judges every dependsOn edge that leaves its own subsystem, and every one that resolves nowhere. Across subsystems the shape is client Adapter \u2192 published remote Portal, OR a direct in-process edge licensed by a trustedLink declared on the SOURCE subsystem (the published-Portal target requirement applies either way). A reference that names nothing in this tree is resolved against the stored surface snapshots: a hit is a DECLARED remote portal and the same Adapter requirement applies to it, snapshots of several providers that disagree make the reference ambiguous, and a reference authored to leave this root that no snapshot covers warns instead of erroring. Boundary rules are never relaxed by a pack profile.",
|
|
11063
|
+
description: "Judges every dependsOn edge that leaves its own subsystem, and every one that resolves nowhere. Across subsystems the shape is client Adapter \u2192 published remote Portal, OR a direct in-process edge licensed by a trustedLink declared on the SOURCE subsystem (the published-Portal target requirement applies either way). A published surface may also name the subsystems it serves (a publicInterfaces entry's consumers): a component every one of whose entries names consumers may be depended on only from those subsystems, which is how a provider keeps a surface \u2014 the raw spec writes \u2014 away from a door that must not reach it. A reference that names nothing in this tree is resolved against the stored surface snapshots: a hit is a DECLARED remote portal and the same Adapter requirement applies to it, snapshots of several providers that disagree make the reference ambiguous, and a reference authored to leave this root that no snapshot covers warns instead of erroring. Boundary rules are never relaxed by a pack profile.",
|
|
11020
11064
|
codes: [
|
|
11021
11065
|
{ code: "INVALID_DEPENDENCY_REFERENCE", defaultSeverity: "error", summary: "dependsOn names a non-existent component" },
|
|
11022
11066
|
{ code: "CROSS_TREE_REF_UNRESOLVED", defaultSeverity: "warning", summary: "Cross-tree dependsOn (super::/:: form) with no surface snapshot covering it" },
|
|
11023
11067
|
{ code: "SURFACE_REF_AMBIGUOUS", defaultSeverity: "error", summary: "Cross-tree dependsOn matched by surface snapshots of several providers with different contracts" },
|
|
11024
11068
|
{ code: "CROSS_SUBSYSTEM_NON_ADAPTER", defaultSeverity: "error", summary: "Non-Adapter component crossing a subsystem boundary" },
|
|
11025
11069
|
{ code: "CROSS_SUBSYSTEM_PRIVATE_ACCESS", defaultSeverity: "error", summary: "Cross-subsystem dependency on an unpublished component" },
|
|
11026
|
-
{ code: "CROSS_SUBSYSTEM_TARGET_NON_PORTAL", defaultSeverity: "error", summary: "Cross-subsystem hop entering through a non-Portal" }
|
|
11070
|
+
{ code: "CROSS_SUBSYSTEM_TARGET_NON_PORTAL", defaultSeverity: "error", summary: "Cross-subsystem hop entering through a non-Portal" },
|
|
11071
|
+
{ code: "CROSS_SUBSYSTEM_UNLISTED_CONSUMER", defaultSeverity: "error", summary: "Cross-subsystem dependency on a surface published only to other subsystems" }
|
|
11027
11072
|
],
|
|
11028
11073
|
check(ctx) {
|
|
11029
11074
|
for (const edge of ctx.dependencyEdges().all) {
|
|
@@ -11106,6 +11151,16 @@ var init_subsystem_boundary_dependencies = __esm({
|
|
|
11106
11151
|
edge.draftContext
|
|
11107
11152
|
);
|
|
11108
11153
|
}
|
|
11154
|
+
const publishedTo = restrictedConsumers(ctx, depComp.subsystem, depComp.id);
|
|
11155
|
+
if (publishedTo && !publishedTo.includes(comp.subsystem)) {
|
|
11156
|
+
ctx.addIssue(
|
|
11157
|
+
"error",
|
|
11158
|
+
"CROSS_SUBSYSTEM_UNLISTED_CONSUMER",
|
|
11159
|
+
`Boundary violation: "${comp.id}" (subsystem "${comp.subsystem}") depends on "${depComp.id}", which subsystem "${depComp.subsystem}" publishes only to ${publishedTo.map((s) => `"${s}"`).join(", ")}. Depend on a surface published to "${comp.subsystem}" instead, or \u2014 when "${comp.subsystem}" really is a caller the provider meant to serve \u2014 add it to the entry's consumers.`,
|
|
11160
|
+
comp.id,
|
|
11161
|
+
edge.draftContext
|
|
11162
|
+
);
|
|
11163
|
+
}
|
|
11109
11164
|
}
|
|
11110
11165
|
}
|
|
11111
11166
|
};
|
|
@@ -15983,6 +16038,7 @@ var init_repository = __esm({
|
|
|
15983
16038
|
init_field_type_references();
|
|
15984
16039
|
init_signature_type_references();
|
|
15985
16040
|
init_public_surface_binding();
|
|
16041
|
+
init_public_surface_consumers();
|
|
15986
16042
|
init_public_surface_declared_type();
|
|
15987
16043
|
init_public_surface_bound_contract();
|
|
15988
16044
|
init_lint_allows();
|
|
@@ -16161,10 +16217,11 @@ var init_repository = __esm({
|
|
|
16161
16217
|
assertionForbiddenEdgesRule,
|
|
16162
16218
|
assertionRequiredFieldsRule,
|
|
16163
16219
|
assertionEndpointShapesRule,
|
|
16164
|
-
// The published surface in
|
|
16165
|
-
// that component's stereotype can realize the type it
|
|
16166
|
-
// the contract it binds is that component's own.
|
|
16220
|
+
// The published surface in four questions: what backs the entry, who it is
|
|
16221
|
+
// published to, whether that component's stereotype can realize the type it
|
|
16222
|
+
// declares, and whether the contract it binds is that component's own.
|
|
16167
16223
|
publicSurfaceBindingRule,
|
|
16224
|
+
publicSurfaceConsumersRule,
|
|
16168
16225
|
publicSurfaceDeclaredTypeRule,
|
|
16169
16226
|
publicSurfaceBoundContractRule,
|
|
16170
16227
|
cyclesRule,
|
|
@@ -19633,6 +19690,7 @@ __export(specs_exports, {
|
|
|
19633
19690
|
deleteGroupSpec: () => deleteGroupSpec,
|
|
19634
19691
|
deleteImplementationSpec: () => deleteImplementationSpec,
|
|
19635
19692
|
deleteInterfaceSpec: () => deleteInterfaceSpec,
|
|
19693
|
+
deleteSpec: () => deleteSpec,
|
|
19636
19694
|
deleteSubsystemSpec: () => deleteSubsystemSpec,
|
|
19637
19695
|
deleteTypeSpec: () => deleteTypeSpec,
|
|
19638
19696
|
deregisterPackRef: () => deregisterPackRef,
|
|
@@ -19659,6 +19717,7 @@ __export(specs_exports, {
|
|
|
19659
19717
|
loadImplementationSpecs: () => loadImplementationSpecs,
|
|
19660
19718
|
loadInterfaceSpec: () => loadInterfaceSpec,
|
|
19661
19719
|
loadInterfaceSpecs: () => loadInterfaceSpecs,
|
|
19720
|
+
loadSpec: () => loadSpec,
|
|
19662
19721
|
loadSubsystemSpec: () => loadSubsystemSpec,
|
|
19663
19722
|
loadSubsystemSpecs: () => loadSubsystemSpecs,
|
|
19664
19723
|
loadSystemSpec: () => loadSystemSpec,
|
|
@@ -19680,6 +19739,7 @@ __export(specs_exports, {
|
|
|
19680
19739
|
saveGroupSpec: () => saveGroupSpec,
|
|
19681
19740
|
saveImplementationSpec: () => saveImplementationSpec,
|
|
19682
19741
|
saveInterfaceSpec: () => saveInterfaceSpec,
|
|
19742
|
+
saveSpec: () => saveSpec,
|
|
19683
19743
|
saveSubsystemSpec: () => saveSubsystemSpec,
|
|
19684
19744
|
saveSystemSpec: () => saveSystemSpec,
|
|
19685
19745
|
saveTypeSpec: () => saveTypeSpec,
|
|
@@ -19946,7 +20006,10 @@ function stripNamespaceFromSubsystem(spec, prefix) {
|
|
|
19946
20006
|
publicInterfaces: spec.publicInterfaces.map((pi) => ({
|
|
19947
20007
|
...pi,
|
|
19948
20008
|
component: pi.component ? relMember(pi.component) : void 0,
|
|
19949
|
-
interface: pi.interface ? relMember(pi.interface) : void 0
|
|
20009
|
+
interface: pi.interface ? relMember(pi.interface) : void 0,
|
|
20010
|
+
// consumers name SUBSYSTEMS — peers of this one, so they are relative to
|
|
20011
|
+
// the namespace the subsystem itself is declared in, never its members'.
|
|
20012
|
+
...pi.consumers ? { consumers: pi.consumers.map((c) => c.includes("::") ? relativizeId(c, prefix) : c) } : {}
|
|
19950
20013
|
})),
|
|
19951
20014
|
lifecycle: spec.lifecycle?.map((le) => ({
|
|
19952
20015
|
...le,
|
|
@@ -20169,6 +20232,8 @@ function identityKeyOf(field, item) {
|
|
|
20169
20232
|
const o = item;
|
|
20170
20233
|
const str = (v) => typeof v === "string" && v.length > 0 ? v : null;
|
|
20171
20234
|
switch (field) {
|
|
20235
|
+
case "publicInterfaces":
|
|
20236
|
+
return publicInterfaceKey(o);
|
|
20172
20237
|
case "dispatch":
|
|
20173
20238
|
return str(o.capability);
|
|
20174
20239
|
// A listener mounts each portal once, so the portal IS the mount's identity.
|
|
@@ -20214,6 +20279,13 @@ function identityKeyOf(field, item) {
|
|
|
20214
20279
|
return str(o.name) ?? str(o.id);
|
|
20215
20280
|
}
|
|
20216
20281
|
}
|
|
20282
|
+
function publicInterfaceKey(o) {
|
|
20283
|
+
const str = (v) => typeof v === "string" && v.length > 0 ? v : null;
|
|
20284
|
+
const component = str(o.component);
|
|
20285
|
+
if (component) return str(o.interface) ? `${component}.${String(o.interface)}` : component;
|
|
20286
|
+
if (typeof o.type === "string" && typeof o.details === "string") return `${o.type} ${o.details}`;
|
|
20287
|
+
return null;
|
|
20288
|
+
}
|
|
20217
20289
|
function reportIdentityOf(field, item) {
|
|
20218
20290
|
if (item !== null && typeof item === "object") {
|
|
20219
20291
|
const step = item.stepNumber;
|
|
@@ -20265,6 +20337,9 @@ function identityFieldsOf(field) {
|
|
|
20265
20337
|
switch (field) {
|
|
20266
20338
|
case "dispatch":
|
|
20267
20339
|
return ["capability"];
|
|
20340
|
+
// Bound: component + interface; not yet bound: type + details.
|
|
20341
|
+
case "publicInterfaces":
|
|
20342
|
+
return ["component", "interface", "type", "details"];
|
|
20268
20343
|
case "lifecycle":
|
|
20269
20344
|
return ["phase", "component", "method"];
|
|
20270
20345
|
case "emits":
|
|
@@ -20677,6 +20752,18 @@ function restoreSpecFiles(snapshot) {
|
|
|
20677
20752
|
function findLegacySpecFiles() {
|
|
20678
20753
|
return current().findLegacySpecFiles();
|
|
20679
20754
|
}
|
|
20755
|
+
function loadSpec(kind, id) {
|
|
20756
|
+
const workspace = current();
|
|
20757
|
+
return workspace.load(kind, id);
|
|
20758
|
+
}
|
|
20759
|
+
function saveSpec(kind, spec) {
|
|
20760
|
+
const workspace = current();
|
|
20761
|
+
return workspace.save(kind, spec);
|
|
20762
|
+
}
|
|
20763
|
+
function deleteSpec(kind, id) {
|
|
20764
|
+
const workspace = current();
|
|
20765
|
+
return workspace.delete(kind, id);
|
|
20766
|
+
}
|
|
20680
20767
|
function updateSpec(kind, id, delta, hooks, dryRun) {
|
|
20681
20768
|
return current().updateSpec(kind, id, delta, hooks, dryRun);
|
|
20682
20769
|
}
|
|
@@ -20870,7 +20957,10 @@ var init_specs2 = __esm({
|
|
|
20870
20957
|
publicInterfaces: sub.publicInterfaces.map((p) => ({
|
|
20871
20958
|
...p,
|
|
20872
20959
|
component: p.component ? qualifyId(p.component, componentPrefix, this.rootSubsystems) : void 0,
|
|
20873
|
-
interface: p.interface ? qualifyId(p.interface, componentPrefix, this.rootSubsystems) : void 0
|
|
20960
|
+
interface: p.interface ? qualifyId(p.interface, componentPrefix, this.rootSubsystems) : void 0,
|
|
20961
|
+
// Each consumer is a subsystem reference: qualified into the namespace
|
|
20962
|
+
// this subsystem is declared in, like a component's `subsystem` field.
|
|
20963
|
+
...p.consumers ? { consumers: p.consumers.map((c) => qualifySubsystemRef(c, namespacePrefix, this.rootSubsystems)) } : {}
|
|
20874
20964
|
})),
|
|
20875
20965
|
lifecycle: sub.lifecycle?.map((le) => ({
|
|
20876
20966
|
...le,
|
|
@@ -21770,6 +21860,76 @@ var init_specs2 = __esm({
|
|
|
21770
21860
|
return true;
|
|
21771
21861
|
}
|
|
21772
21862
|
// -------------------------------------------------------------------------
|
|
21863
|
+
// Kind-generic access (spec_index.load, spec_registry.save / delete)
|
|
21864
|
+
//
|
|
21865
|
+
// The one place a kind held as DATA becomes the typed loader, writer or
|
|
21866
|
+
// delete of that kind. Every caller that holds the kind as a value — the
|
|
21867
|
+
// gated whole-spec write, a delete by kind, the delta update, the method
|
|
21868
|
+
// move — used to repeat this switch, and each copy could drift from the
|
|
21869
|
+
// others (one of them forgot that a type's writer takes no status).
|
|
21870
|
+
// -------------------------------------------------------------------------
|
|
21871
|
+
/** One stored spec of the named kind, or null. The L0 is a singleton: its id is informational. */
|
|
21872
|
+
load(kind, id) {
|
|
21873
|
+
switch (kind) {
|
|
21874
|
+
case "system":
|
|
21875
|
+
return this.loadSystemSpec();
|
|
21876
|
+
case "subsystem":
|
|
21877
|
+
return this.loadSubsystemSpec(id);
|
|
21878
|
+
case "component":
|
|
21879
|
+
return this.loadComponentSpec(id);
|
|
21880
|
+
case "interface":
|
|
21881
|
+
return this.loadInterfaceSpec(id);
|
|
21882
|
+
case "implementation":
|
|
21883
|
+
return this.loadImplementationSpec(id);
|
|
21884
|
+
case "type":
|
|
21885
|
+
return this.loadTypeSpec(id);
|
|
21886
|
+
}
|
|
21887
|
+
}
|
|
21888
|
+
/**
|
|
21889
|
+
* Persist one spec through the typed writer of its kind, answering with that
|
|
21890
|
+
* writer's placement notices (none for the L0 and a subsystem). A draft or
|
|
21891
|
+
* status-less save keeps the stored status, exactly as the typed writers do.
|
|
21892
|
+
*/
|
|
21893
|
+
save(kind, spec) {
|
|
21894
|
+
return this.saveOfKind(kind, spec);
|
|
21895
|
+
}
|
|
21896
|
+
/** The typed writer of a kind, with the caller's save options — the delta update and the move state a demotion. */
|
|
21897
|
+
saveOfKind(kind, spec, opts) {
|
|
21898
|
+
switch (kind) {
|
|
21899
|
+
case "system":
|
|
21900
|
+
this.saveSystemSpec(spec);
|
|
21901
|
+
return [];
|
|
21902
|
+
case "subsystem":
|
|
21903
|
+
this.saveSubsystemSpec(spec, opts);
|
|
21904
|
+
return [];
|
|
21905
|
+
case "component":
|
|
21906
|
+
return this.saveComponentSpec(spec, opts);
|
|
21907
|
+
case "interface":
|
|
21908
|
+
return this.saveInterfaceSpec(spec, opts);
|
|
21909
|
+
case "implementation":
|
|
21910
|
+
return this.saveImplementationSpec(spec, opts);
|
|
21911
|
+
case "type":
|
|
21912
|
+
return this.saveTypeSpec(spec, opts);
|
|
21913
|
+
}
|
|
21914
|
+
}
|
|
21915
|
+
/** Delete one spec document of the named kind; false when none was stored. The L0 cannot be deleted. */
|
|
21916
|
+
delete(kind, id) {
|
|
21917
|
+
switch (kind) {
|
|
21918
|
+
case "system":
|
|
21919
|
+
throw new Error("The L0 system spec cannot be deleted: it is the root every other spec hangs from.");
|
|
21920
|
+
case "subsystem":
|
|
21921
|
+
return this.deleteSubsystemSpec(id);
|
|
21922
|
+
case "component":
|
|
21923
|
+
return this.deleteComponentSpec(id);
|
|
21924
|
+
case "interface":
|
|
21925
|
+
return this.deleteInterfaceSpec(id);
|
|
21926
|
+
case "implementation":
|
|
21927
|
+
return this.deleteImplementationSpec(id);
|
|
21928
|
+
case "type":
|
|
21929
|
+
return this.deleteTypeSpec(id);
|
|
21930
|
+
}
|
|
21931
|
+
}
|
|
21932
|
+
// -------------------------------------------------------------------------
|
|
21773
21933
|
// Spec status promotion (draft/design → complete)
|
|
21774
21934
|
// -------------------------------------------------------------------------
|
|
21775
21935
|
/** Every spec whose status is not yet 'complete', with its current status captured. */
|
|
@@ -21990,23 +22150,7 @@ var init_specs2 = __esm({
|
|
|
21990
22150
|
*/
|
|
21991
22151
|
updateSpec(kind, id, delta, hooks, dryRun = false) {
|
|
21992
22152
|
const notices = [];
|
|
21993
|
-
const result = (
|
|
21994
|
-
switch (kind) {
|
|
21995
|
-
case "system":
|
|
21996
|
-
return this.loadSystemSpec();
|
|
21997
|
-
// singleton — id is informational
|
|
21998
|
-
case "subsystem":
|
|
21999
|
-
return this.loadSubsystemSpec(id);
|
|
22000
|
-
case "component":
|
|
22001
|
-
return this.loadComponentSpec(id);
|
|
22002
|
-
case "interface":
|
|
22003
|
-
return this.loadInterfaceSpec(id);
|
|
22004
|
-
case "implementation":
|
|
22005
|
-
return this.loadImplementationSpec(id);
|
|
22006
|
-
case "type":
|
|
22007
|
-
return this.loadTypeSpec(id);
|
|
22008
|
-
}
|
|
22009
|
-
})();
|
|
22153
|
+
const result = this.load(kind, id);
|
|
22010
22154
|
if (!result) {
|
|
22011
22155
|
throw new Error(`Spec of kind "${kind}" with ID "${id}" does not exist. Define it first.`);
|
|
22012
22156
|
}
|
|
@@ -22338,9 +22482,10 @@ var init_specs2 = __esm({
|
|
|
22338
22482
|
const mergePublicInterfaces = (existing, delta2) => {
|
|
22339
22483
|
const merged = [...existing];
|
|
22340
22484
|
for (const deltaItem of delta2) {
|
|
22341
|
-
const piKey = (i) =>
|
|
22485
|
+
const piKey = (i) => String(identityKeyOf("publicInterfaces", i));
|
|
22342
22486
|
assertDeltaMarkers(deltaItem, `publicInterface "${piKey(deltaItem)}"`);
|
|
22343
|
-
const
|
|
22487
|
+
const key = identityKeyOf("publicInterfaces", deltaItem);
|
|
22488
|
+
const idx = key === null ? -1 : merged.findIndex((item) => identityKeyOf("publicInterfaces", item) === key);
|
|
22344
22489
|
if (idx !== -1) {
|
|
22345
22490
|
if (deltaItem.remove === true || deltaItem.action === "delete") {
|
|
22346
22491
|
merged.splice(idx, 1);
|
|
@@ -22429,7 +22574,8 @@ var init_specs2 = __esm({
|
|
|
22429
22574
|
out.publicInterfaces = out.publicInterfaces.map((pi) => ({
|
|
22430
22575
|
...pi,
|
|
22431
22576
|
...typeof pi?.component === "string" ? { component: qm(pi.component) } : {},
|
|
22432
|
-
...typeof pi?.interface === "string" ? { interface: qm(pi.interface) } : {}
|
|
22577
|
+
...typeof pi?.interface === "string" ? { interface: qm(pi.interface) } : {},
|
|
22578
|
+
...Array.isArray(pi?.consumers) ? { consumers: pi.consumers.map((c) => typeof c === "string" ? qualifySubsystemRef(c, prefix, this.rootSubsystems) : c) } : {}
|
|
22433
22579
|
}));
|
|
22434
22580
|
}
|
|
22435
22581
|
if (Array.isArray(out.lifecycle)) {
|
|
@@ -22483,14 +22629,14 @@ var init_specs2 = __esm({
|
|
|
22483
22629
|
- ${labelErrors.join("\n- ")}`);
|
|
22484
22630
|
}
|
|
22485
22631
|
}
|
|
22486
|
-
const
|
|
22632
|
+
const canonical2 = (spec) => {
|
|
22487
22633
|
const parsed = deltaSchema.safeParse(spec);
|
|
22488
22634
|
const value = { ...parsed.success ? parsed.data : spec };
|
|
22489
22635
|
delete value.updatedAt;
|
|
22490
22636
|
return value;
|
|
22491
22637
|
};
|
|
22492
|
-
const canonicalStored =
|
|
22493
|
-
const canonicalMerged =
|
|
22638
|
+
const canonicalStored = canonical2(storedBefore);
|
|
22639
|
+
const canonicalMerged = canonical2(mergedResult);
|
|
22494
22640
|
const changes = specChanges(canonicalStored, canonicalMerged);
|
|
22495
22641
|
const ineffective = ineffectiveDeltaPaths(
|
|
22496
22642
|
{ ...qualifiedDelta, ...unsetFields.length ? { unset: unsetFields } : {} },
|
|
@@ -22529,26 +22675,7 @@ var init_specs2 = __esm({
|
|
|
22529
22675
|
const opts = {
|
|
22530
22676
|
allowStatusDemotion: Object.prototype.hasOwnProperty.call(delta, "status")
|
|
22531
22677
|
};
|
|
22532
|
-
|
|
22533
|
-
case "system":
|
|
22534
|
-
this.saveSystemSpec(mergedResult);
|
|
22535
|
-
break;
|
|
22536
|
-
case "subsystem":
|
|
22537
|
-
this.saveSubsystemSpec(mergedResult, opts);
|
|
22538
|
-
break;
|
|
22539
|
-
case "component":
|
|
22540
|
-
notices.push(...this.saveComponentSpec(mergedResult, opts));
|
|
22541
|
-
break;
|
|
22542
|
-
case "interface":
|
|
22543
|
-
notices.push(...this.saveInterfaceSpec(mergedResult, opts));
|
|
22544
|
-
break;
|
|
22545
|
-
case "implementation":
|
|
22546
|
-
notices.push(...this.saveImplementationSpec(mergedResult, opts));
|
|
22547
|
-
break;
|
|
22548
|
-
case "type":
|
|
22549
|
-
notices.push(...this.saveTypeSpec(mergedResult));
|
|
22550
|
-
break;
|
|
22551
|
-
}
|
|
22678
|
+
notices.push(...this.saveOfKind(kind, mergedResult, opts));
|
|
22552
22679
|
return {
|
|
22553
22680
|
kind,
|
|
22554
22681
|
id,
|
|
@@ -22909,23 +23036,7 @@ var init_specs2 = __esm({
|
|
|
22909
23036
|
}
|
|
22910
23037
|
/** Persist one moved spec through the store's own writer, and answer with its placement notices. */
|
|
22911
23038
|
saveMovedSpec(kind, spec) {
|
|
22912
|
-
|
|
22913
|
-
switch (kind) {
|
|
22914
|
-
case "system":
|
|
22915
|
-
this.saveSystemSpec(spec);
|
|
22916
|
-
return [];
|
|
22917
|
-
case "subsystem":
|
|
22918
|
-
this.saveSubsystemSpec(spec, opts);
|
|
22919
|
-
return [];
|
|
22920
|
-
case "component":
|
|
22921
|
-
return this.saveComponentSpec(spec, opts);
|
|
22922
|
-
case "interface":
|
|
22923
|
-
return this.saveInterfaceSpec(spec, opts);
|
|
22924
|
-
case "implementation":
|
|
22925
|
-
return this.saveImplementationSpec(spec, opts);
|
|
22926
|
-
case "type":
|
|
22927
|
-
return this.saveTypeSpec(spec);
|
|
22928
|
-
}
|
|
23039
|
+
return this.saveOfKind(kind, spec, { allowStatusDemotion: true });
|
|
22929
23040
|
}
|
|
22930
23041
|
};
|
|
22931
23042
|
DEFAULT_DEPENDENCY_LIMIT = 8;
|
|
@@ -29082,6 +29193,361 @@ var init_core = __esm({
|
|
|
29082
29193
|
}
|
|
29083
29194
|
});
|
|
29084
29195
|
|
|
29196
|
+
// src/core/authoring.ts
|
|
29197
|
+
function candidateOptions() {
|
|
29198
|
+
try {
|
|
29199
|
+
const config = loadProjectConfig();
|
|
29200
|
+
return config ? { rules: config.rules, projectType: config.projectType } : {};
|
|
29201
|
+
} catch {
|
|
29202
|
+
return {};
|
|
29203
|
+
}
|
|
29204
|
+
}
|
|
29205
|
+
function noticesFrom(verdict) {
|
|
29206
|
+
return verdict.warnings.map((w) => `${w.code}: ${w.message}`);
|
|
29207
|
+
}
|
|
29208
|
+
function componentCandidateGate(options = candidateOptions()) {
|
|
29209
|
+
return {
|
|
29210
|
+
gate: (kind, merged) => {
|
|
29211
|
+
if (kind !== "component") return;
|
|
29212
|
+
const verdict = validateComponentCandidate(merged, options);
|
|
29213
|
+
if (verdict.errors.length) throw new Error(formatCandidateRefusal(verdict));
|
|
29214
|
+
return noticesFrom(verdict);
|
|
29215
|
+
}
|
|
29216
|
+
};
|
|
29217
|
+
}
|
|
29218
|
+
function restatementParent(restatement) {
|
|
29219
|
+
const spec = restatement.spec;
|
|
29220
|
+
switch (restatement.kind) {
|
|
29221
|
+
case "subsystem":
|
|
29222
|
+
return { kind: "system", id: "system" };
|
|
29223
|
+
case "component":
|
|
29224
|
+
return { kind: "subsystem", id: spec.subsystem };
|
|
29225
|
+
case "interface":
|
|
29226
|
+
return { kind: "component", id: spec.component };
|
|
29227
|
+
case "implementation":
|
|
29228
|
+
return { kind: "interface", id: spec.contract };
|
|
29229
|
+
default:
|
|
29230
|
+
return null;
|
|
29231
|
+
}
|
|
29232
|
+
}
|
|
29233
|
+
function applyRestatement(restatement, existing, parent) {
|
|
29234
|
+
const replacedExisting = existing !== null;
|
|
29235
|
+
const parentRef = restatementParent(restatement);
|
|
29236
|
+
if (parentRef && !parent) return refused(restatement, replacedExisting, MISSING_PARENT[restatement.kind](parentRef.id));
|
|
29237
|
+
const status2 = statusForCreate(restatement, existing);
|
|
29238
|
+
if ("refusal" in status2) return refused(restatement, replacedExisting, status2.refusal);
|
|
29239
|
+
const candidate = JSON.parse(JSON.stringify(restatement.spec));
|
|
29240
|
+
if (restatement.kind === "subsystem") candidate.parentSystem = parent.name;
|
|
29241
|
+
const carried = carryInto(restatement, existing, candidate);
|
|
29242
|
+
const cleared = clearedByOmission(existing, candidate, restatement.fields);
|
|
29243
|
+
stampLifecycle(candidate, existing, status2.status);
|
|
29244
|
+
const labelErrors = resolveLabelsOf(restatement.kind, candidate);
|
|
29245
|
+
if (labelErrors.length) {
|
|
29246
|
+
return refused(restatement, replacedExisting, `Unresolved narrative label references \u2014 nothing was saved:
|
|
29247
|
+
- ${labelErrors.join("\n- ")}`);
|
|
29248
|
+
}
|
|
29249
|
+
const parsed = parseCandidate(restatement.kind, candidate);
|
|
29250
|
+
if ("refusal" in parsed) return refused(restatement, replacedExisting, parsed.refusal);
|
|
29251
|
+
return {
|
|
29252
|
+
spec: parsed.spec,
|
|
29253
|
+
...status2.status ? { status: status2.status } : {},
|
|
29254
|
+
replacedExisting,
|
|
29255
|
+
notices: existing ? rewriteNotices(restatement.kind, existing, candidate, carried, cleared) : [],
|
|
29256
|
+
changedMethods: changedMethodsOf(restatement.kind, existing, candidate)
|
|
29257
|
+
};
|
|
29258
|
+
}
|
|
29259
|
+
function parseCandidate(kind, candidate) {
|
|
29260
|
+
const masked = /* @__PURE__ */ new Map();
|
|
29261
|
+
const mask = (value) => {
|
|
29262
|
+
if (typeof value === "string") {
|
|
29263
|
+
if (!value.includes("::")) return value;
|
|
29264
|
+
const token = `nsref_${masked.size}_masked`;
|
|
29265
|
+
masked.set(token, value);
|
|
29266
|
+
return token;
|
|
29267
|
+
}
|
|
29268
|
+
if (Array.isArray(value)) return value.map(mask);
|
|
29269
|
+
if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, mask(v)]));
|
|
29270
|
+
return value;
|
|
29271
|
+
};
|
|
29272
|
+
const restore = (value) => {
|
|
29273
|
+
if (typeof value === "string") return masked.get(value) ?? value;
|
|
29274
|
+
if (Array.isArray(value)) return value.map(restore);
|
|
29275
|
+
if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, restore(v)]));
|
|
29276
|
+
return value;
|
|
29277
|
+
};
|
|
29278
|
+
const result = SPEC_SCHEMA[kind].safeParse(mask(candidate));
|
|
29279
|
+
if (result.success) return { spec: restore(result.data) };
|
|
29280
|
+
const id = kind === "system" ? "system" : String(candidate.id);
|
|
29281
|
+
const issues = result.error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ");
|
|
29282
|
+
return {
|
|
29283
|
+
refusal: `Refusing to write ${kind} "${id}": the spec it would become does not parse \u2014 ${issues}. Nothing was written. State the missing or malformed field and call again.`
|
|
29284
|
+
};
|
|
29285
|
+
}
|
|
29286
|
+
function refused(restatement, replacedExisting, refusal2) {
|
|
29287
|
+
return { spec: restatement.spec, refusal: refusal2, replacedExisting, notices: [], changedMethods: [] };
|
|
29288
|
+
}
|
|
29289
|
+
function statusForCreate(restatement, existing) {
|
|
29290
|
+
if (!STATUSED_KINDS.has(restatement.kind)) return {};
|
|
29291
|
+
const stated = restatement.status;
|
|
29292
|
+
const stored = existing?.status;
|
|
29293
|
+
const held = stored !== void 0 && STATUS_ORDER.includes(stored) ? stored : void 0;
|
|
29294
|
+
if (stated === void 0) return { status: held ?? "draft" };
|
|
29295
|
+
if (held === void 0 || STATUS_ORDER.indexOf(stated) >= STATUS_ORDER.indexOf(held)) return { status: stated };
|
|
29296
|
+
const label = `${restatement.kind} "${restatement.spec.id}"`;
|
|
29297
|
+
return {
|
|
29298
|
+
refusal: `Refusing to re-author ${label} at status "${stated}": it is stored at "${held}", and a create tool never lowers a spec's status \u2014 a restatement that reopened a frozen spec would undo a lock without saying so. Nothing was written. To reopen it deliberately, use sdd_update_spec with {"status": "${stated}"}; to keep the level it has, leave status out.`
|
|
29299
|
+
};
|
|
29300
|
+
}
|
|
29301
|
+
function carryInto(restatement, existing, candidate) {
|
|
29302
|
+
if (!existing) return [];
|
|
29303
|
+
const carried = ["createdAt"];
|
|
29304
|
+
if (restatement.memberFields && Array.isArray(candidate.methods)) {
|
|
29305
|
+
const stored = existing.methods ?? [];
|
|
29306
|
+
for (const method2 of candidate.methods) {
|
|
29307
|
+
const prev = stored.find((p) => p.name === method2.name);
|
|
29308
|
+
for (const field of carryUnexpressed(prev, method2, restatement.memberFields)) carried.push(`${field} (${String(method2.name)})`);
|
|
29309
|
+
}
|
|
29310
|
+
}
|
|
29311
|
+
carried.push(...carryUnexpressed(existing, candidate, [...restatement.fields, "createdAt"]));
|
|
29312
|
+
return carried;
|
|
29313
|
+
}
|
|
29314
|
+
function carryUnexpressed(existing, next, expressed) {
|
|
29315
|
+
if (!existing) return [];
|
|
29316
|
+
const carried = [];
|
|
29317
|
+
for (const [field, value] of Object.entries(existing)) {
|
|
29318
|
+
if (value === void 0) continue;
|
|
29319
|
+
if (STORE_MANAGED_FIELDS.has(field)) continue;
|
|
29320
|
+
if (expressed.includes(field) && !ALWAYS_CARRIED_FIELDS.has(field)) continue;
|
|
29321
|
+
if (next[field] !== void 0) continue;
|
|
29322
|
+
next[field] = value;
|
|
29323
|
+
carried.push(field);
|
|
29324
|
+
}
|
|
29325
|
+
return carried;
|
|
29326
|
+
}
|
|
29327
|
+
function isEmptyValue(value) {
|
|
29328
|
+
if (value === void 0 || value === null) return true;
|
|
29329
|
+
if (Array.isArray(value)) return value.length === 0;
|
|
29330
|
+
if (typeof value === "string") return value.trim() === "";
|
|
29331
|
+
if (typeof value === "object") return Object.keys(value).length === 0;
|
|
29332
|
+
return false;
|
|
29333
|
+
}
|
|
29334
|
+
function clearedByOmission(existing, next, expressed) {
|
|
29335
|
+
if (!existing) return [];
|
|
29336
|
+
const stored = existing;
|
|
29337
|
+
const cleared = [];
|
|
29338
|
+
for (const field of expressed) {
|
|
29339
|
+
const before = stored[field];
|
|
29340
|
+
if (STORE_MANAGED_FIELDS.has(field) || ALWAYS_CARRIED_FIELDS.has(field)) continue;
|
|
29341
|
+
if (isEmptyValue(before) || !isEmptyValue(next[field])) continue;
|
|
29342
|
+
cleared.push(Array.isArray(before) ? `${field} (had ${before.length})` : field);
|
|
29343
|
+
}
|
|
29344
|
+
return cleared;
|
|
29345
|
+
}
|
|
29346
|
+
function stampLifecycle(candidate, existing, status2) {
|
|
29347
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
29348
|
+
candidate.createdAt = existing?.createdAt ?? now;
|
|
29349
|
+
candidate.updatedAt = now;
|
|
29350
|
+
if (status2) candidate.status = status2;
|
|
29351
|
+
else delete candidate.status;
|
|
29352
|
+
}
|
|
29353
|
+
function resolveLabelsOf(kind, candidate) {
|
|
29354
|
+
if (kind !== "implementation") return [];
|
|
29355
|
+
const methods = candidate.methods ?? [];
|
|
29356
|
+
return methods.flatMap((m) => resolveNarrativeLabels(m.name, m.narrative ?? []));
|
|
29357
|
+
}
|
|
29358
|
+
function removedByName(before, after) {
|
|
29359
|
+
if (!before?.length) return [];
|
|
29360
|
+
const kept = new Set((after ?? []).map((m) => m.name));
|
|
29361
|
+
return before.map((m) => m.name).filter((n) => !kept.has(n));
|
|
29362
|
+
}
|
|
29363
|
+
function rewriteNotices(kind, existing, candidate, carried, cleared) {
|
|
29364
|
+
const named = kind === "system" ? existing.name : String(candidate.id);
|
|
29365
|
+
const notices = [`${REWRITE_LABEL[kind]} "${named}" already existed \u2014 re-authored in place; this input REPLACES what it expresses.`];
|
|
29366
|
+
if (carried.length) notices.push(`Carried forward (not expressible through this tool): ${carried.join(", ")}.`);
|
|
29367
|
+
const removal = removalNotice(kind, existing, candidate);
|
|
29368
|
+
if (removal) notices.push(removal);
|
|
29369
|
+
if (cleared.length) {
|
|
29370
|
+
notices.push(
|
|
29371
|
+
`CLEARED by omission: ${cleared.join(", ")} \u2014 the argument was not repeated, so the previous value is gone. Use sdd_update_spec if you meant to leave it untouched.`
|
|
29372
|
+
);
|
|
29373
|
+
}
|
|
29374
|
+
return notices;
|
|
29375
|
+
}
|
|
29376
|
+
function removalNotice(kind, existing, candidate) {
|
|
29377
|
+
const before = existing;
|
|
29378
|
+
let removed = [];
|
|
29379
|
+
let noun = "method";
|
|
29380
|
+
let suffix = "";
|
|
29381
|
+
if (kind === "interface" || kind === "implementation") {
|
|
29382
|
+
removed = removedByName(before.methods, candidate.methods);
|
|
29383
|
+
suffix = kind === "interface" ? " (endpoint bindings included)" : " (L5 narratives included)";
|
|
29384
|
+
} else if (kind === "type") {
|
|
29385
|
+
removed = [
|
|
29386
|
+
...removedByName(before.fields, candidate.fields).map((n) => `field ${n}`),
|
|
29387
|
+
...removedByName(before.methods, candidate.methods).map((n) => `method ${n}`)
|
|
29388
|
+
];
|
|
29389
|
+
noun = "member";
|
|
29390
|
+
}
|
|
29391
|
+
if (!removed.length) return void 0;
|
|
29392
|
+
return `REMOVED by this restatement: ${removed.length === 1 ? noun : `${noun}s`} ${removed.map((n) => `"${n}"`).join(", ")}${suffix} \u2014 absent from the input, so no longer in the spec. Restate them to keep them, or use sdd_update_spec to edit one member at a time.`;
|
|
29393
|
+
}
|
|
29394
|
+
function changedMethodsOf(kind, existing, candidate) {
|
|
29395
|
+
if (!existing || !(kind in METHOD_SCHEMA)) return [];
|
|
29396
|
+
const schema = METHOD_SCHEMA[kind];
|
|
29397
|
+
const stored = existing.methods ?? [];
|
|
29398
|
+
const next = candidate.methods ?? [];
|
|
29399
|
+
return stored.filter((method2) => {
|
|
29400
|
+
const restated = next.find((m) => m.name === method2.name);
|
|
29401
|
+
if (!restated) return true;
|
|
29402
|
+
const parsed = schema.safeParse(restated);
|
|
29403
|
+
return !parsed.success || canonical(parsed.data) !== canonical(method2);
|
|
29404
|
+
}).map((method2) => method2.name);
|
|
29405
|
+
}
|
|
29406
|
+
function canonical(value) {
|
|
29407
|
+
return JSON.stringify(value, (_key, v) => v && typeof v === "object" && !Array.isArray(v) ? Object.fromEntries(Object.entries(v).filter(([, x]) => x !== void 0).sort(([a], [b]) => a.localeCompare(b))) : v);
|
|
29408
|
+
}
|
|
29409
|
+
function writeSpec(restatement) {
|
|
29410
|
+
const bound2 = candidateOptions();
|
|
29411
|
+
const parentRef = restatementParent(restatement);
|
|
29412
|
+
const parent = parentRef ? loadSpec(parentRef.kind, parentRef.id) : null;
|
|
29413
|
+
const id = restatement.kind === "system" ? "system" : restatement.spec.id;
|
|
29414
|
+
const existing = loadSpec(restatement.kind, id);
|
|
29415
|
+
const application = applyRestatement(restatement, existing, parent);
|
|
29416
|
+
if (application.refusal !== void 0) throw new Error(application.refusal);
|
|
29417
|
+
const gateNotices = restatement.kind === "component" ? judgeComponent(application.spec, bound2) : [];
|
|
29418
|
+
const persisted = persistCandidate(restatement.kind, application.spec);
|
|
29419
|
+
const testsToRevisit = testsInvalidatedBy(application.changedMethods, [existing, application.spec], bound2);
|
|
29420
|
+
return {
|
|
29421
|
+
kind: restatement.kind,
|
|
29422
|
+
id,
|
|
29423
|
+
name: application.spec.name,
|
|
29424
|
+
replacedExisting: application.replacedExisting,
|
|
29425
|
+
...application.status ? { status: application.status } : {},
|
|
29426
|
+
notices: [...gateNotices, ...persisted.notices, ...application.notices],
|
|
29427
|
+
...persisted.scaffoldedProjectPath ? { scaffoldedProjectPath: persisted.scaffoldedProjectPath } : {},
|
|
29428
|
+
testsToRevisit
|
|
29429
|
+
};
|
|
29430
|
+
}
|
|
29431
|
+
function judgeComponent(candidate, options) {
|
|
29432
|
+
const verdict = validateComponentCandidate(candidate, options);
|
|
29433
|
+
if (verdict.errors.length) throw new Error(formatCandidateRefusal(verdict));
|
|
29434
|
+
return noticesFrom(verdict);
|
|
29435
|
+
}
|
|
29436
|
+
function persistCandidate(kind, spec) {
|
|
29437
|
+
const projectPath = kind === "subsystem" ? spec.projectPath : void 0;
|
|
29438
|
+
if (projectPath && projectPath.trim() !== "") {
|
|
29439
|
+
createChainedSubsystem(spec, spec.name);
|
|
29440
|
+
return { notices: [], scaffoldedProjectPath: projectPath };
|
|
29441
|
+
}
|
|
29442
|
+
return { notices: saveSpec(kind, spec) };
|
|
29443
|
+
}
|
|
29444
|
+
function testsInvalidatedBy(methods, specs, options) {
|
|
29445
|
+
const testRoots = options.rules?.conformance?.testRoots ?? [];
|
|
29446
|
+
if (methods.length === 0 || testRoots.length === 0) return [];
|
|
29447
|
+
const symbolOf = (name) => specs.map((s) => (s?.methods ?? []).find((m) => m.name === name)?.symbol).find((symbol) => symbol !== void 0);
|
|
29448
|
+
return findTestsReferencing2(methods.map((name) => ({ name, symbol: symbolOf(name) })), getProjectRoot(), testRoots);
|
|
29449
|
+
}
|
|
29450
|
+
function deleteSpec2(kind, id) {
|
|
29451
|
+
const bound2 = candidateOptions();
|
|
29452
|
+
const stored = loadSpec(kind, id);
|
|
29453
|
+
const deleted = deleteSpec(kind, id);
|
|
29454
|
+
const methods = deleted ? (stored?.methods ?? []).map((m) => m.name) : [];
|
|
29455
|
+
const testsToRevisit = testsInvalidatedBy(methods, [stored], bound2);
|
|
29456
|
+
return { kind, id, deleted, testsToRevisit };
|
|
29457
|
+
}
|
|
29458
|
+
function updateSpecGated(kind, id, delta, dryRun) {
|
|
29459
|
+
const bound2 = candidateOptions();
|
|
29460
|
+
const gate = componentCandidateGate(bound2);
|
|
29461
|
+
const report2 = updateSpec(kind, id, delta, gate, dryRun);
|
|
29462
|
+
const testRoots = bound2.rules?.conformance?.testRoots ?? [];
|
|
29463
|
+
const changed = changedMethods(report2, delta);
|
|
29464
|
+
if (changed.length > 0 && testRoots.length > 0) {
|
|
29465
|
+
report2.testsToRevisit = findTestsReferencing2(changed, getProjectRoot(), testRoots);
|
|
29466
|
+
}
|
|
29467
|
+
return report2;
|
|
29468
|
+
}
|
|
29469
|
+
function changedMethods(report2, delta) {
|
|
29470
|
+
const named = /* @__PURE__ */ new Set();
|
|
29471
|
+
for (const change of report2.changes) {
|
|
29472
|
+
const [field, name] = change.path.split(".");
|
|
29473
|
+
if (field === "methods" && name) named.add(name);
|
|
29474
|
+
}
|
|
29475
|
+
const written = Array.isArray(delta?.methods) ? delta.methods : [];
|
|
29476
|
+
return [...named].map((name) => ({
|
|
29477
|
+
name,
|
|
29478
|
+
symbol: written.find((m) => m?.name === name)?.symbol
|
|
29479
|
+
}));
|
|
29480
|
+
}
|
|
29481
|
+
function methodMoveGate(source) {
|
|
29482
|
+
const bound2 = candidateOptions();
|
|
29483
|
+
return {
|
|
29484
|
+
gate: (kind, merged) => {
|
|
29485
|
+
if (kind !== "component") return;
|
|
29486
|
+
const { codes, verdict, ceiling } = judgeMoveHome(merged, source, bound2);
|
|
29487
|
+
if (codes.length === 0) return noticesFrom(verdict);
|
|
29488
|
+
throw new Error(
|
|
29489
|
+
`${codes.join(", ")} refuses "${merged.id}" as the home for these methods. ` + (verdict.errors.length ? formatCandidateRefusal(verdict) : `It would reach ${merged.dependsOn?.length ?? 0} dependencies against a ceiling of ${ceiling}.`)
|
|
29490
|
+
);
|
|
29491
|
+
},
|
|
29492
|
+
assess: (kind, merged) => kind === "component" ? judgeMoveHome(merged, source, bound2).codes : []
|
|
29493
|
+
};
|
|
29494
|
+
}
|
|
29495
|
+
function judgeMoveHome(component, source, options) {
|
|
29496
|
+
const ceiling = options.rules?.complexity?.maxComponentDependencies;
|
|
29497
|
+
const verdict = validateComponentCandidate(component, options);
|
|
29498
|
+
const codes = verdict.errors.map((e) => e.code);
|
|
29499
|
+
if (component.id !== source && ceiling !== void 0 && (component.dependsOn?.length ?? 0) > ceiling) {
|
|
29500
|
+
codes.push("EXCESSIVE_DEPENDENCIES");
|
|
29501
|
+
}
|
|
29502
|
+
return { codes: [...new Set(codes)], verdict, ceiling };
|
|
29503
|
+
}
|
|
29504
|
+
function moveMethods2(from, to, methods, dryRun) {
|
|
29505
|
+
return moveMethods(from, to, methods, methodMoveGate(from), dryRun);
|
|
29506
|
+
}
|
|
29507
|
+
var STORE_MANAGED_FIELDS, ALWAYS_CARRIED_FIELDS, STATUS_ORDER, STATUSED_KINDS, MISSING_PARENT, REWRITE_LABEL, SPEC_SCHEMA, METHOD_SCHEMA;
|
|
29508
|
+
var init_authoring = __esm({
|
|
29509
|
+
"src/core/authoring.ts"() {
|
|
29510
|
+
"use strict";
|
|
29511
|
+
init_specs();
|
|
29512
|
+
init_core();
|
|
29513
|
+
init_validation();
|
|
29514
|
+
init_candidate();
|
|
29515
|
+
init_narrative_labels();
|
|
29516
|
+
init_fs();
|
|
29517
|
+
STORE_MANAGED_FIELDS = /* @__PURE__ */ new Set(["status", "updatedAt"]);
|
|
29518
|
+
ALWAYS_CARRIED_FIELDS = /* @__PURE__ */ new Set(["ext"]);
|
|
29519
|
+
STATUS_ORDER = ["draft", "design", "complete"];
|
|
29520
|
+
STATUSED_KINDS = /* @__PURE__ */ new Set(["subsystem", "component", "interface", "implementation"]);
|
|
29521
|
+
MISSING_PARENT = {
|
|
29522
|
+
subsystem: () => "System spec must be initialized (sdd_initialize_system) first.",
|
|
29523
|
+
component: (id) => `Parent subsystem "${id}" does not exist.`,
|
|
29524
|
+
interface: (id) => `Component "${id}" does not exist.`,
|
|
29525
|
+
implementation: (id) => `Interface contract "${id}" does not exist.`
|
|
29526
|
+
};
|
|
29527
|
+
REWRITE_LABEL = {
|
|
29528
|
+
system: "System spec",
|
|
29529
|
+
subsystem: "Subsystem",
|
|
29530
|
+
component: "Component",
|
|
29531
|
+
interface: "Interface",
|
|
29532
|
+
implementation: "Implementation",
|
|
29533
|
+
type: "Type"
|
|
29534
|
+
};
|
|
29535
|
+
SPEC_SCHEMA = {
|
|
29536
|
+
system: SystemSpecSchema,
|
|
29537
|
+
subsystem: SubsystemSpecSchema,
|
|
29538
|
+
component: ComponentSpecSchema,
|
|
29539
|
+
interface: InterfaceSpecSchema,
|
|
29540
|
+
implementation: ImplementationSpecSchema,
|
|
29541
|
+
type: TypeSpecSchema
|
|
29542
|
+
};
|
|
29543
|
+
METHOD_SCHEMA = {
|
|
29544
|
+
interface: MethodSignatureSchema,
|
|
29545
|
+
implementation: MethodImplementationSchema,
|
|
29546
|
+
type: TypeMethodSchema
|
|
29547
|
+
};
|
|
29548
|
+
}
|
|
29549
|
+
});
|
|
29550
|
+
|
|
29085
29551
|
// src/core/instructions.ts
|
|
29086
29552
|
function governingProfile() {
|
|
29087
29553
|
try {
|
|
@@ -29405,93 +29871,6 @@ var init_skills = __esm({
|
|
|
29405
29871
|
}
|
|
29406
29872
|
});
|
|
29407
29873
|
|
|
29408
|
-
// src/core/authoring.ts
|
|
29409
|
-
function candidateOptions() {
|
|
29410
|
-
try {
|
|
29411
|
-
const config = loadProjectConfig();
|
|
29412
|
-
return config ? { rules: config.rules, projectType: config.projectType } : {};
|
|
29413
|
-
} catch {
|
|
29414
|
-
return {};
|
|
29415
|
-
}
|
|
29416
|
-
}
|
|
29417
|
-
function noticesFrom(verdict) {
|
|
29418
|
-
return verdict.warnings.map((w) => `${w.code}: ${w.message}`);
|
|
29419
|
-
}
|
|
29420
|
-
function componentCandidateGate(options = candidateOptions()) {
|
|
29421
|
-
return {
|
|
29422
|
-
gate: (kind, merged) => {
|
|
29423
|
-
if (kind !== "component") return;
|
|
29424
|
-
const verdict = validateComponentCandidate(merged, options);
|
|
29425
|
-
if (verdict.errors.length) throw new Error(formatCandidateRefusal(verdict));
|
|
29426
|
-
return noticesFrom(verdict);
|
|
29427
|
-
}
|
|
29428
|
-
};
|
|
29429
|
-
}
|
|
29430
|
-
function addComponent(candidate) {
|
|
29431
|
-
const verdict = validateComponentCandidate(candidate, candidateOptions());
|
|
29432
|
-
if (verdict.errors.length) throw new Error(formatCandidateRefusal(verdict));
|
|
29433
|
-
return [...noticesFrom(verdict), ...saveComponentSpec(candidate)];
|
|
29434
|
-
}
|
|
29435
|
-
function updateSpecGated(kind, id, delta, dryRun) {
|
|
29436
|
-
const bound2 = candidateOptions();
|
|
29437
|
-
const gate = componentCandidateGate(bound2);
|
|
29438
|
-
const report2 = updateSpec(kind, id, delta, gate, dryRun);
|
|
29439
|
-
const testRoots = bound2.rules?.conformance?.testRoots ?? [];
|
|
29440
|
-
const changed = changedMethods(report2, delta);
|
|
29441
|
-
if (changed.length > 0 && testRoots.length > 0) {
|
|
29442
|
-
report2.testsToRevisit = findTestsReferencing2(changed, getProjectRoot(), testRoots);
|
|
29443
|
-
}
|
|
29444
|
-
return report2;
|
|
29445
|
-
}
|
|
29446
|
-
function changedMethods(report2, delta) {
|
|
29447
|
-
const named = /* @__PURE__ */ new Set();
|
|
29448
|
-
for (const change of report2.changes) {
|
|
29449
|
-
const [field, name] = change.path.split(".");
|
|
29450
|
-
if (field === "methods" && name) named.add(name);
|
|
29451
|
-
}
|
|
29452
|
-
const written = Array.isArray(delta?.methods) ? delta.methods : [];
|
|
29453
|
-
return [...named].map((name) => ({
|
|
29454
|
-
name,
|
|
29455
|
-
symbol: written.find((m) => m?.name === name)?.symbol
|
|
29456
|
-
}));
|
|
29457
|
-
}
|
|
29458
|
-
function methodMoveGate(source) {
|
|
29459
|
-
const bound2 = candidateOptions();
|
|
29460
|
-
return {
|
|
29461
|
-
gate: (kind, merged) => {
|
|
29462
|
-
if (kind !== "component") return;
|
|
29463
|
-
const { codes, verdict, ceiling } = judgeMoveHome(merged, source, bound2);
|
|
29464
|
-
if (codes.length === 0) return noticesFrom(verdict);
|
|
29465
|
-
throw new Error(
|
|
29466
|
-
`${codes.join(", ")} refuses "${merged.id}" as the home for these methods. ` + (verdict.errors.length ? formatCandidateRefusal(verdict) : `It would reach ${merged.dependsOn?.length ?? 0} dependencies against a ceiling of ${ceiling}.`)
|
|
29467
|
-
);
|
|
29468
|
-
},
|
|
29469
|
-
assess: (kind, merged) => kind === "component" ? judgeMoveHome(merged, source, bound2).codes : []
|
|
29470
|
-
};
|
|
29471
|
-
}
|
|
29472
|
-
function judgeMoveHome(component, source, options) {
|
|
29473
|
-
const ceiling = options.rules?.complexity?.maxComponentDependencies;
|
|
29474
|
-
const verdict = validateComponentCandidate(component, options);
|
|
29475
|
-
const codes = verdict.errors.map((e) => e.code);
|
|
29476
|
-
if (component.id !== source && ceiling !== void 0 && (component.dependsOn?.length ?? 0) > ceiling) {
|
|
29477
|
-
codes.push("EXCESSIVE_DEPENDENCIES");
|
|
29478
|
-
}
|
|
29479
|
-
return { codes: [...new Set(codes)], verdict, ceiling };
|
|
29480
|
-
}
|
|
29481
|
-
function moveMethods2(from, to, methods, dryRun) {
|
|
29482
|
-
return moveMethods(from, to, methods, methodMoveGate(from), dryRun);
|
|
29483
|
-
}
|
|
29484
|
-
var init_authoring = __esm({
|
|
29485
|
-
"src/core/authoring.ts"() {
|
|
29486
|
-
"use strict";
|
|
29487
|
-
init_specs2();
|
|
29488
|
-
init_validation();
|
|
29489
|
-
init_candidate();
|
|
29490
|
-
init_core();
|
|
29491
|
-
init_fs();
|
|
29492
|
-
}
|
|
29493
|
-
});
|
|
29494
|
-
|
|
29495
29874
|
// src/mcp/server.ts
|
|
29496
29875
|
var server_exports = {};
|
|
29497
29876
|
__export(server_exports, {
|
|
@@ -29567,6 +29946,30 @@ function qualifiedComponentId(id) {
|
|
|
29567
29946
|
const chained = ids.filter((known) => known.endsWith(`::${bare}`));
|
|
29568
29947
|
return chained.length === 1 ? chained[0] : id;
|
|
29569
29948
|
}
|
|
29949
|
+
function publicInterfacesReplacement(subsystem, stored, list2) {
|
|
29950
|
+
const identity = (pi) => pi.component ? `${pi.component}${pi.interface ? `.${pi.interface}` : ""}` : `${pi.type} ${pi.details}`;
|
|
29951
|
+
const restated = /* @__PURE__ */ new Map();
|
|
29952
|
+
for (const pi of list2) restated.set(identity(pi), (restated.get(identity(pi)) ?? 0) + 1);
|
|
29953
|
+
const twice = [...restated].filter(([, n]) => n > 1).map(([key]) => `"${key}"`);
|
|
29954
|
+
if (twice.length) {
|
|
29955
|
+
return `Refusing to set the public interfaces of subsystem "${subsystem}": the list names ${twice.join(", ")} more than once. An entry is identified by the component and interface it is bound to \u2014 or, not yet bound, by its type and details \u2014 so two entries sharing one are the same entry stated twice. Drop the duplicate. Nothing was written.`;
|
|
29956
|
+
}
|
|
29957
|
+
const seen = /* @__PURE__ */ new Map();
|
|
29958
|
+
const deletes = [];
|
|
29959
|
+
for (const pi of stored) {
|
|
29960
|
+
const key = identity(pi);
|
|
29961
|
+
seen.set(key, (seen.get(key) ?? 0) + 1);
|
|
29962
|
+
if (seen.get(key) > (restated.get(key) ?? 0)) {
|
|
29963
|
+
deletes.push(pi.component ? { component: pi.component, interface: pi.interface, action: "delete" } : { type: pi.type, details: pi.details, action: "delete" });
|
|
29964
|
+
}
|
|
29965
|
+
}
|
|
29966
|
+
const restatedEntries = list2.map((pi) => {
|
|
29967
|
+
const prev = stored.find((s) => identity(s) === identity(pi));
|
|
29968
|
+
const dropped = prev ? Object.keys(prev).filter((f) => prev[f] !== void 0 && pi[f] === void 0) : [];
|
|
29969
|
+
return dropped.length ? { ...pi, unset: dropped } : pi;
|
|
29970
|
+
});
|
|
29971
|
+
return { publicInterfaces: [...deletes, ...restatedEntries] };
|
|
29972
|
+
}
|
|
29570
29973
|
function text(content) {
|
|
29571
29974
|
return { content: [{ type: "text", text: content }] };
|
|
29572
29975
|
}
|
|
@@ -29576,6 +29979,9 @@ function json(value) {
|
|
|
29576
29979
|
function errText(message) {
|
|
29577
29980
|
return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
|
|
29578
29981
|
}
|
|
29982
|
+
function errMessage(e) {
|
|
29983
|
+
return errText(e instanceof Error ? e.message : String(e));
|
|
29984
|
+
}
|
|
29579
29985
|
function structured(content, data) {
|
|
29580
29986
|
return { content: [{ type: "text", text: content }], structuredContent: data };
|
|
29581
29987
|
}
|
|
@@ -29593,25 +29999,30 @@ function renderChangeReport(report2) {
|
|
|
29593
29999
|
lines.push("", "NO EFFECT:", ...report2.ineffective.map((i) => `- ${i}`));
|
|
29594
30000
|
}
|
|
29595
30001
|
if (report2.notices.length) lines.push("", "NOTICE:", ...report2.notices.map((n) => `- ${n}`));
|
|
29596
|
-
|
|
29597
|
-
|
|
29598
|
-
|
|
29599
|
-
|
|
29600
|
-
|
|
29601
|
-
|
|
29602
|
-
|
|
29603
|
-
|
|
29604
|
-
|
|
30002
|
+
lines.push(...renderTestsToRevisit(report2.testsToRevisit));
|
|
30003
|
+
return lines.join("\n");
|
|
30004
|
+
}
|
|
30005
|
+
function renderTestsToRevisit(entries) {
|
|
30006
|
+
if (!entries.length) return [];
|
|
30007
|
+
const lines = ["", "TESTS TO REVISIT:"];
|
|
30008
|
+
for (const entry of entries) {
|
|
30009
|
+
lines.push(`- ${entry.method} (searched as "${entry.symbol}")`);
|
|
30010
|
+
if (entry.imported.length) lines.push(` imports it: ${entry.imported.join(", ")}`);
|
|
30011
|
+
if (entry.mentioned.length) lines.push(` names it: ${entry.mentioned.join(", ")}`);
|
|
30012
|
+
if (entry.indiscriminate) {
|
|
30013
|
+
lines.push(" names it: withheld \u2014 the bare name matches more than a tenth of the suite, so the list carries no signal.");
|
|
29605
30014
|
}
|
|
29606
30015
|
}
|
|
29607
|
-
return lines
|
|
30016
|
+
return lines;
|
|
29608
30017
|
}
|
|
29609
30018
|
function writeReceipt(sentence, receipt) {
|
|
29610
30019
|
const noticeBlock = receipt.notices.length ? `
|
|
29611
30020
|
|
|
29612
30021
|
NOTICE:
|
|
29613
30022
|
- ${receipt.notices.join("\n- ")}` : "";
|
|
29614
|
-
|
|
30023
|
+
const testsBlock = renderTestsToRevisit(receipt.testsToRevisit).join("\n");
|
|
30024
|
+
return structured(`${sentence}${noticeBlock}${testsBlock ? `
|
|
30025
|
+
${testsBlock}` : ""}`, receipt);
|
|
29615
30026
|
}
|
|
29616
30027
|
function captureBuildStamp(entryPath) {
|
|
29617
30028
|
try {
|
|
@@ -29653,74 +30064,6 @@ function reg(server, name, config, cb) {
|
|
|
29653
30064
|
const strictConfig = config.inputSchema ? { ...config, inputSchema: import_zod11.z.object(config.inputSchema).strict() } : config;
|
|
29654
30065
|
server.registerTool(name, strictConfig, guarded);
|
|
29655
30066
|
}
|
|
29656
|
-
function statusForCreate(label, stated, existing) {
|
|
29657
|
-
const stored = existing?.status;
|
|
29658
|
-
const held = stored !== void 0 && STATUS_ORDER.includes(stored) ? stored : void 0;
|
|
29659
|
-
if (stated === void 0) return { status: held ?? "draft" };
|
|
29660
|
-
if (held === void 0) return { status: stated };
|
|
29661
|
-
const rank = (s) => STATUS_ORDER.indexOf(s);
|
|
29662
|
-
if (rank(stated) < rank(held)) {
|
|
29663
|
-
return {
|
|
29664
|
-
refusal: `Refusing to re-author ${label} at status "${stated}": it is stored at "${held}", and a create tool never lowers a spec's status \u2014 a restatement that reopened a frozen spec would undo a lock without saying so. Nothing was written. To reopen it deliberately, use sdd_update_spec with {"status": "${stated}"}; to keep the level it has, leave status out.`
|
|
29665
|
-
};
|
|
29666
|
-
}
|
|
29667
|
-
return { status: stated };
|
|
29668
|
-
}
|
|
29669
|
-
function isEmptyValue(value) {
|
|
29670
|
-
if (value === void 0 || value === null) return true;
|
|
29671
|
-
if (Array.isArray(value)) return value.length === 0;
|
|
29672
|
-
if (typeof value === "string") return value.trim() === "";
|
|
29673
|
-
if (typeof value === "object") return Object.keys(value).length === 0;
|
|
29674
|
-
return false;
|
|
29675
|
-
}
|
|
29676
|
-
function carryUnexpressed(existing, next, expressed) {
|
|
29677
|
-
if (!existing) return [];
|
|
29678
|
-
const carried = [];
|
|
29679
|
-
for (const [field, value] of Object.entries(existing)) {
|
|
29680
|
-
if (value === void 0) continue;
|
|
29681
|
-
if (STORE_MANAGED_FIELDS.has(field)) continue;
|
|
29682
|
-
if (expressed.includes(field) && !ALWAYS_CARRIED_FIELDS.has(field)) continue;
|
|
29683
|
-
if (next[field] !== void 0) continue;
|
|
29684
|
-
next[field] = value;
|
|
29685
|
-
carried.push(field);
|
|
29686
|
-
}
|
|
29687
|
-
return carried;
|
|
29688
|
-
}
|
|
29689
|
-
function clearedByOmission(existing, next, expressed) {
|
|
29690
|
-
if (!existing) return [];
|
|
29691
|
-
const cleared = [];
|
|
29692
|
-
for (const field of expressed) {
|
|
29693
|
-
const before = existing[field];
|
|
29694
|
-
if (STORE_MANAGED_FIELDS.has(field) || ALWAYS_CARRIED_FIELDS.has(field)) continue;
|
|
29695
|
-
if (isEmptyValue(before) || !isEmptyValue(next[field])) continue;
|
|
29696
|
-
cleared.push(Array.isArray(before) ? `${field} (had ${before.length})` : field);
|
|
29697
|
-
}
|
|
29698
|
-
return cleared;
|
|
29699
|
-
}
|
|
29700
|
-
function removedByName(before, after) {
|
|
29701
|
-
if (!before?.length) return [];
|
|
29702
|
-
const kept = new Set((after ?? []).map((m) => m.name));
|
|
29703
|
-
return before.map((m) => m.name).filter((n) => !kept.has(n));
|
|
29704
|
-
}
|
|
29705
|
-
function rewriteNotices(opts) {
|
|
29706
|
-
const notices = [`${opts.label} already existed \u2014 re-authored in place; this input REPLACES what it expresses.`];
|
|
29707
|
-
if (opts.carried.length) {
|
|
29708
|
-
notices.push(`Carried forward (not expressible through this tool): ${opts.carried.join(", ")}.`);
|
|
29709
|
-
}
|
|
29710
|
-
if (opts.removed.length) {
|
|
29711
|
-
const noun = opts.removedNoun ?? "method";
|
|
29712
|
-
const plural = opts.removed.length === 1 ? noun : `${noun}s`;
|
|
29713
|
-
notices.push(
|
|
29714
|
-
`REMOVED by this restatement: ${plural} ${opts.removed.map((n) => `"${n}"`).join(", ")}${opts.removedSuffix ?? ""} \u2014 absent from the input, so no longer in the spec. Restate them to keep them, or use sdd_update_spec to edit one member at a time.`
|
|
29715
|
-
);
|
|
29716
|
-
}
|
|
29717
|
-
if (opts.cleared.length) {
|
|
29718
|
-
notices.push(
|
|
29719
|
-
`CLEARED by omission: ${opts.cleared.join(", ")} \u2014 the argument was not repeated, so the previous value is gone. Use sdd_update_spec if you meant to leave it untouched.`
|
|
29720
|
-
);
|
|
29721
|
-
}
|
|
29722
|
-
return notices;
|
|
29723
|
-
}
|
|
29724
30067
|
function listSkillResources2() {
|
|
29725
30068
|
return listResources();
|
|
29726
30069
|
}
|
|
@@ -29975,30 +30318,24 @@ function createMcpServer(options = {}) {
|
|
|
29975
30318
|
},
|
|
29976
30319
|
({ name, vision, boundaries, globalRequirements, targetLanguage }) => {
|
|
29977
30320
|
try {
|
|
29978
|
-
const
|
|
29979
|
-
|
|
29980
|
-
|
|
29981
|
-
|
|
29982
|
-
|
|
29983
|
-
|
|
29984
|
-
|
|
29985
|
-
|
|
29986
|
-
|
|
30321
|
+
const restatement = {
|
|
30322
|
+
kind: "system",
|
|
30323
|
+
spec: {
|
|
30324
|
+
name,
|
|
30325
|
+
vision,
|
|
30326
|
+
boundaries: boundaries ?? [],
|
|
30327
|
+
globalRequirements: globalRequirements ?? [],
|
|
30328
|
+
...targetLanguage ? { targetLanguage } : {}
|
|
30329
|
+
},
|
|
30330
|
+
fields: systemInputFields
|
|
29987
30331
|
};
|
|
29988
|
-
const
|
|
29989
|
-
const cleared = clearedByOmission(existing, next, systemInputFields);
|
|
29990
|
-
next.schemaVersion ??= "1.0.0";
|
|
29991
|
-
next.databases ??= [];
|
|
29992
|
-
next.createdAt ??= now;
|
|
29993
|
-
next.updatedAt = now;
|
|
29994
|
-
saveSystemSpec2(next);
|
|
29995
|
-
const notices = existing ? rewriteNotices({ label: `System spec "${existing.name}"`, carried, cleared, removed: [] }) : [];
|
|
30332
|
+
const receipt = writeSpec(restatement);
|
|
29996
30333
|
return writeReceipt(
|
|
29997
|
-
`Successfully ${
|
|
29998
|
-
|
|
30334
|
+
`Successfully ${receipt.replacedExisting ? "re-authored" : "initialized"} L0 System Spec for "${name}".`,
|
|
30335
|
+
receipt
|
|
29999
30336
|
);
|
|
30000
30337
|
} catch (e) {
|
|
30001
|
-
return
|
|
30338
|
+
return errMessage(e);
|
|
30002
30339
|
}
|
|
30003
30340
|
}
|
|
30004
30341
|
);
|
|
@@ -30010,7 +30347,8 @@ function createMcpServer(options = {}) {
|
|
|
30010
30347
|
type: import_zod11.z.enum(["REST", "GraphQL", "MessageBus", "RPC", "Custom"]),
|
|
30011
30348
|
details: import_zod11.z.string(),
|
|
30012
30349
|
component: import_zod11.z.string().optional().describe("The L2 component id that realizes this interface (this subsystem's published surface)"),
|
|
30013
|
-
interface: import_zod11.z.string().optional().describe("Optional L3 interface id on that component backing this entry")
|
|
30350
|
+
interface: import_zod11.z.string().optional().describe("Optional L3 interface id on that component backing this entry"),
|
|
30351
|
+
consumers: import_zod11.z.array(import_zod11.z.string()).optional().describe("The subsystem ids this surface is published to. Absent, any subsystem may depend on the component; present, only these may \u2014 when every entry publishing the component names consumers, the union of the lists is the whole set, and a dependency from any other subsystem is CROSS_SUBSYSTEM_UNLISTED_CONSUMER. Every id must name a subsystem of the tree (PUBLIC_INTERFACE_UNKNOWN_CONSUMER).")
|
|
30014
30352
|
}).strict()).optional().describe("Public entrypoints exposed by this subsystem, each bound to a realizing component"),
|
|
30015
30353
|
projectPath: import_zod11.z.string().optional().describe("Relative path to external project root for subsystem chaining"),
|
|
30016
30354
|
targetLanguage: import_zod11.z.string().optional().describe("Override of the system-level targetLanguage for this subsystem"),
|
|
@@ -30039,53 +30377,33 @@ function createMcpServer(options = {}) {
|
|
|
30039
30377
|
},
|
|
30040
30378
|
({ id, name, description, publicInterfaces, projectPath, targetLanguage, profile, designDepth, trustedLinks, lifecycle, status: status2 }) => {
|
|
30041
30379
|
try {
|
|
30042
|
-
const
|
|
30043
|
-
const system = loadSystemSpec3();
|
|
30044
|
-
if (!system) return errText("System spec must be initialized (sdd_initialize_system) first.");
|
|
30045
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
30046
|
-
const existing = loadSubsystemSpec2(id);
|
|
30047
|
-
const resolved = statusForCreate(`subsystem "${id}"`, status2, existing);
|
|
30048
|
-
if ("refusal" in resolved) return errText(resolved.refusal);
|
|
30049
|
-
const spec = {
|
|
30050
|
-
id,
|
|
30051
|
-
name,
|
|
30052
|
-
description,
|
|
30053
|
-
parentSystem: system.name,
|
|
30054
|
-
publicInterfaces: publicInterfaces ?? [],
|
|
30055
|
-
projectPath,
|
|
30056
|
-
...targetLanguage ? { targetLanguage } : {},
|
|
30057
|
-
...profile ? { profile } : {},
|
|
30058
|
-
...designDepth ? { designDepth } : {},
|
|
30059
|
-
...lifecycle ? { lifecycle } : {},
|
|
30060
|
-
trustedLinks: trustedLinks ?? [],
|
|
30061
|
-
status: resolved.status,
|
|
30062
|
-
createdAt: now,
|
|
30063
|
-
updatedAt: now
|
|
30064
|
-
};
|
|
30065
|
-
const carried = carryUnexpressed(existing, spec, subsystemInputFields);
|
|
30066
|
-
const cleared = clearedByOmission(existing, spec, subsystemInputFields);
|
|
30067
|
-
if (existing) spec.createdAt = existing.createdAt;
|
|
30068
|
-
const notices = existing ? rewriteNotices({ label: `Subsystem "${id}"`, carried: ["createdAt", ...carried], cleared, removed: [] }) : [];
|
|
30069
|
-
const receipt = {
|
|
30380
|
+
const restatement = {
|
|
30070
30381
|
kind: "subsystem",
|
|
30071
|
-
|
|
30072
|
-
|
|
30073
|
-
|
|
30074
|
-
|
|
30075
|
-
|
|
30382
|
+
spec: {
|
|
30383
|
+
id,
|
|
30384
|
+
name,
|
|
30385
|
+
description,
|
|
30386
|
+
publicInterfaces: publicInterfaces ?? [],
|
|
30387
|
+
projectPath,
|
|
30388
|
+
...targetLanguage ? { targetLanguage } : {},
|
|
30389
|
+
...profile ? { profile } : {},
|
|
30390
|
+
...designDepth ? { designDepth } : {},
|
|
30391
|
+
...lifecycle ? { lifecycle } : {},
|
|
30392
|
+
trustedLinks: trustedLinks ?? []
|
|
30393
|
+
},
|
|
30394
|
+
fields: subsystemInputFields,
|
|
30395
|
+
status: status2
|
|
30076
30396
|
};
|
|
30077
|
-
|
|
30078
|
-
|
|
30079
|
-
createChainedSubsystem3(spec, name);
|
|
30397
|
+
const receipt = writeSpec(restatement);
|
|
30398
|
+
if (receipt.scaffoldedProjectPath) {
|
|
30080
30399
|
return writeReceipt(
|
|
30081
|
-
`Successfully added external subsystem "${name}" (${id}) and scaffolded its child project at ${
|
|
30082
|
-
|
|
30400
|
+
`Successfully added external subsystem "${name}" (${id}) and scaffolded its child project at ${receipt.scaffoldedProjectPath}.`,
|
|
30401
|
+
receipt
|
|
30083
30402
|
);
|
|
30084
30403
|
}
|
|
30085
|
-
saveSubsystemSpec2(spec);
|
|
30086
30404
|
return writeReceipt(`Successfully added L1 Subsystem Spec "${name}" (${id}).`, receipt);
|
|
30087
30405
|
} catch (e) {
|
|
30088
|
-
return
|
|
30406
|
+
return errMessage(e);
|
|
30089
30407
|
}
|
|
30090
30408
|
}
|
|
30091
30409
|
);
|
|
@@ -30093,30 +30411,34 @@ function createMcpServer(options = {}) {
|
|
|
30093
30411
|
server,
|
|
30094
30412
|
"sdd_set_public_interfaces",
|
|
30095
30413
|
{
|
|
30096
|
-
description: "Set (replace) an existing subsystem's publicInterfaces, binding each to the component (and optional interface) that realizes it. Use this to backfill bindings once the subsystem's components exist \u2014 cross-subsystem dependencies may only target a published public component.",
|
|
30414
|
+
description: "Set (replace) an existing subsystem's publicInterfaces, binding each to the component (and optional interface) that realizes it. Use this to backfill bindings once the subsystem's components exist \u2014 cross-subsystem dependencies may only target a published public component. A replacement, not a merge: an entry left out is removed. An entry's identity is its component and interface, so a list naming one identity twice is refused. Written through the authoring seam as one gated delta, and answered with the change report \u2014 every entry added, changed or removed, or that nothing changed \u2014 as structured content beside the sentence.",
|
|
30097
30415
|
inputSchema: {
|
|
30098
30416
|
subsystem: import_zod11.z.string().describe("The L1 subsystem id to update"),
|
|
30099
30417
|
publicInterfaces: import_zod11.z.array(import_zod11.z.object({
|
|
30100
30418
|
type: import_zod11.z.enum(["REST", "GraphQL", "MessageBus", "RPC", "Custom"]),
|
|
30101
30419
|
details: import_zod11.z.string(),
|
|
30102
30420
|
component: import_zod11.z.string().optional().describe("The L2 component id that realizes this interface"),
|
|
30103
|
-
interface: import_zod11.z.string().optional().describe("Optional L3 interface id on that component")
|
|
30421
|
+
interface: import_zod11.z.string().optional().describe("Optional L3 interface id on that component"),
|
|
30422
|
+
consumers: import_zod11.z.array(import_zod11.z.string()).optional().describe("The subsystem ids this surface is published to. Absent, any subsystem may depend on the component; present, only these may \u2014 when every entry publishing the component names consumers, the union of the lists is the whole set, and a dependency from any other subsystem is CROSS_SUBSYSTEM_UNLISTED_CONSUMER. Every id must name a subsystem of the tree (PUBLIC_INTERFACE_UNKNOWN_CONSUMER).")
|
|
30104
30423
|
}).strict()).describe("The full replacement list of public interfaces for this subsystem")
|
|
30105
|
-
}
|
|
30424
|
+
},
|
|
30425
|
+
outputSchema: specChangeReportOutput
|
|
30106
30426
|
},
|
|
30107
30427
|
({ subsystem, publicInterfaces }) => {
|
|
30108
30428
|
try {
|
|
30109
|
-
const { loadSubsystemSpec:
|
|
30110
|
-
const sub =
|
|
30429
|
+
const { loadSubsystemSpec: loadSubsystemSpec3 } = requireSpecs();
|
|
30430
|
+
const sub = loadSubsystemSpec3(subsystem);
|
|
30111
30431
|
if (!sub) return errText(`Subsystem "${subsystem}" does not exist.`);
|
|
30112
|
-
|
|
30113
|
-
|
|
30114
|
-
|
|
30115
|
-
|
|
30116
|
-
|
|
30117
|
-
|
|
30432
|
+
const delta = publicInterfacesReplacement(subsystem, sub.publicInterfaces ?? [], publicInterfaces);
|
|
30433
|
+
if (typeof delta === "string") return errText(delta);
|
|
30434
|
+
const report2 = updateSpecGated("subsystem", subsystem, delta);
|
|
30435
|
+
return structured(
|
|
30436
|
+
`Updated public interfaces for subsystem "${subsystem}" (${publicInterfaces.length} ${publicInterfaces.length === 1 ? "entry" : "entries"}).
|
|
30437
|
+
${renderChangeReport(report2)}`,
|
|
30438
|
+
report2
|
|
30439
|
+
);
|
|
30118
30440
|
} catch (e) {
|
|
30119
|
-
return
|
|
30441
|
+
return errMessage(e);
|
|
30120
30442
|
}
|
|
30121
30443
|
}
|
|
30122
30444
|
);
|
|
@@ -30124,25 +30446,24 @@ function createMcpServer(options = {}) {
|
|
|
30124
30446
|
server,
|
|
30125
30447
|
"sdd_set_subsystem_project_path",
|
|
30126
30448
|
{
|
|
30127
|
-
description: "Set (or clear) the projectPath of an L1 Subsystem for subsystem chaining. projectPath should be a relative path to the external project root directory.",
|
|
30449
|
+
description: "Set (or clear) the projectPath of an L1 Subsystem for subsystem chaining. projectPath should be a relative path to the external project root directory. It rewrites the link only (sdd_move_subsystem_project relocates the directory with it). Written through the authoring seam as a gated delta, and answered with the change report as structured content beside the sentence.",
|
|
30128
30450
|
inputSchema: {
|
|
30129
30451
|
subsystem: import_zod11.z.string().describe("The L1 subsystem id to update"),
|
|
30130
30452
|
projectPath: import_zod11.z.string().optional().describe("Relative path to external project root (or omit to clear)")
|
|
30131
|
-
}
|
|
30453
|
+
},
|
|
30454
|
+
outputSchema: specChangeReportOutput
|
|
30132
30455
|
},
|
|
30133
30456
|
({ subsystem, projectPath }) => {
|
|
30134
30457
|
try {
|
|
30135
|
-
const {
|
|
30136
|
-
const
|
|
30137
|
-
|
|
30138
|
-
|
|
30139
|
-
|
|
30140
|
-
|
|
30141
|
-
|
|
30142
|
-
});
|
|
30143
|
-
return text(`Updated projectPath for subsystem "${subsystem}" to: ${projectPath || "none (cleared)"}`);
|
|
30458
|
+
const delta = projectPath ? { projectPath } : { unset: ["projectPath"] };
|
|
30459
|
+
const report2 = updateSpecGated("subsystem", subsystem, delta);
|
|
30460
|
+
return structured(
|
|
30461
|
+
`Updated projectPath for subsystem "${subsystem}" to: ${projectPath || "none (cleared)"}
|
|
30462
|
+
${renderChangeReport(report2)}`,
|
|
30463
|
+
report2
|
|
30464
|
+
);
|
|
30144
30465
|
} catch (e) {
|
|
30145
|
-
return
|
|
30466
|
+
return errMessage(e);
|
|
30146
30467
|
}
|
|
30147
30468
|
}
|
|
30148
30469
|
);
|
|
@@ -30315,53 +30636,37 @@ function createMcpServer(options = {}) {
|
|
|
30315
30636
|
},
|
|
30316
30637
|
({ id, name, description, subsystem, componentType, owns, dependsOn, portalType, basePath, dispatch, mounts, durability, dependencyClass, emits, subscribesTo, ext, status: status2 }) => {
|
|
30317
30638
|
try {
|
|
30318
|
-
const
|
|
30319
|
-
|
|
30320
|
-
|
|
30321
|
-
|
|
30322
|
-
|
|
30323
|
-
|
|
30324
|
-
|
|
30325
|
-
|
|
30326
|
-
|
|
30327
|
-
|
|
30328
|
-
|
|
30329
|
-
|
|
30330
|
-
|
|
30331
|
-
|
|
30332
|
-
|
|
30333
|
-
|
|
30334
|
-
|
|
30335
|
-
|
|
30336
|
-
|
|
30337
|
-
|
|
30338
|
-
|
|
30339
|
-
|
|
30340
|
-
|
|
30341
|
-
...subscribesTo ? { subscribesTo } : {},
|
|
30342
|
-
...ext ? { ext } : {},
|
|
30343
|
-
status: resolved.status,
|
|
30344
|
-
createdAt: now,
|
|
30345
|
-
updatedAt: now
|
|
30639
|
+
const restatement = {
|
|
30640
|
+
kind: "component",
|
|
30641
|
+
spec: {
|
|
30642
|
+
id,
|
|
30643
|
+
name,
|
|
30644
|
+
description,
|
|
30645
|
+
subsystem,
|
|
30646
|
+
componentType,
|
|
30647
|
+
owns: owns ?? [],
|
|
30648
|
+
dependsOn: dependsOn ?? [],
|
|
30649
|
+
...portalType ? { portalType } : {},
|
|
30650
|
+
...basePath ? { basePath } : {},
|
|
30651
|
+
...dispatch ? { dispatch } : {},
|
|
30652
|
+
// An EMPTY list is kept: declaring the field is what marks a listener.
|
|
30653
|
+
...mounts ? { mounts } : {},
|
|
30654
|
+
...durability ? { durability } : {},
|
|
30655
|
+
...dependencyClass ? { dependencyClass } : {},
|
|
30656
|
+
...emits ? { emits } : {},
|
|
30657
|
+
...subscribesTo ? { subscribesTo } : {},
|
|
30658
|
+
...ext ? { ext } : {}
|
|
30659
|
+
},
|
|
30660
|
+
fields: componentInputFields,
|
|
30661
|
+
status: status2
|
|
30346
30662
|
};
|
|
30347
|
-
const
|
|
30348
|
-
const cleared = clearedByOmission(existing, candidate, componentInputFields);
|
|
30349
|
-
if (existing) candidate.createdAt = existing.createdAt;
|
|
30350
|
-
const notices = addComponent(candidate);
|
|
30351
|
-
if (existing) {
|
|
30352
|
-
notices.push(...rewriteNotices({
|
|
30353
|
-
label: `Component "${id}"`,
|
|
30354
|
-
carried: ["createdAt", ...carried],
|
|
30355
|
-
cleared,
|
|
30356
|
-
removed: []
|
|
30357
|
-
}));
|
|
30358
|
-
}
|
|
30663
|
+
const receipt = writeSpec(restatement);
|
|
30359
30664
|
return writeReceipt(
|
|
30360
|
-
`Successfully ${
|
|
30361
|
-
|
|
30665
|
+
`Successfully ${receipt.replacedExisting ? "re-authored" : "added"} L2 Component Spec "${name}" (${id}, ${componentType}).`,
|
|
30666
|
+
receipt
|
|
30362
30667
|
);
|
|
30363
30668
|
} catch (e) {
|
|
30364
|
-
return
|
|
30669
|
+
return errMessage(e);
|
|
30365
30670
|
}
|
|
30366
30671
|
}
|
|
30367
30672
|
);
|
|
@@ -30411,51 +30716,20 @@ function createMcpServer(options = {}) {
|
|
|
30411
30716
|
},
|
|
30412
30717
|
({ id, name, description, component, methods, status: status2 }) => {
|
|
30413
30718
|
try {
|
|
30414
|
-
const
|
|
30415
|
-
|
|
30416
|
-
|
|
30417
|
-
|
|
30418
|
-
|
|
30419
|
-
|
|
30420
|
-
if ("refusal" in resolved) return errText(resolved.refusal);
|
|
30421
|
-
const carried = [];
|
|
30422
|
-
const specMethods = (methods ?? []).map((m) => {
|
|
30423
|
-
const prev = existing?.methods.find((p) => p.name === m.name);
|
|
30424
|
-
const next = { ...m };
|
|
30425
|
-
for (const field of carryUnexpressed(prev, next, interfaceMethodInputFields)) {
|
|
30426
|
-
carried.push(`${field} (${m.name})`);
|
|
30427
|
-
}
|
|
30428
|
-
return next;
|
|
30429
|
-
});
|
|
30430
|
-
const spec = {
|
|
30431
|
-
id,
|
|
30432
|
-
name,
|
|
30433
|
-
description,
|
|
30434
|
-
component,
|
|
30435
|
-
methods: specMethods,
|
|
30436
|
-
status: resolved.status,
|
|
30437
|
-
createdAt: now,
|
|
30438
|
-
updatedAt: now
|
|
30719
|
+
const restatement = {
|
|
30720
|
+
kind: "interface",
|
|
30721
|
+
spec: { id, name, description, component, methods: methods ?? [] },
|
|
30722
|
+
fields: interfaceInputFields,
|
|
30723
|
+
memberFields: interfaceMethodInputFields,
|
|
30724
|
+
status: status2
|
|
30439
30725
|
};
|
|
30440
|
-
|
|
30441
|
-
const cleared = clearedByOmission(existing, spec, interfaceInputFields);
|
|
30442
|
-
if (existing) spec.createdAt = existing.createdAt;
|
|
30443
|
-
const notices = saveInterfaceSpec2(spec);
|
|
30444
|
-
if (existing) {
|
|
30445
|
-
notices.push(...rewriteNotices({
|
|
30446
|
-
label: `Interface "${id}"`,
|
|
30447
|
-
carried: ["createdAt", ...carried],
|
|
30448
|
-
cleared,
|
|
30449
|
-
removed: removedByName(existing.methods, specMethods),
|
|
30450
|
-
removedSuffix: " (endpoint bindings included)"
|
|
30451
|
-
}));
|
|
30452
|
-
}
|
|
30726
|
+
const receipt = writeSpec(restatement);
|
|
30453
30727
|
return writeReceipt(
|
|
30454
|
-
`Successfully ${
|
|
30455
|
-
|
|
30728
|
+
`Successfully ${receipt.replacedExisting ? "re-authored" : "defined"} L3 Interface Contract "${name}" (${id}).`,
|
|
30729
|
+
receipt
|
|
30456
30730
|
);
|
|
30457
30731
|
} catch (e) {
|
|
30458
|
-
return
|
|
30732
|
+
return errMessage(e);
|
|
30459
30733
|
}
|
|
30460
30734
|
}
|
|
30461
30735
|
);
|
|
@@ -30463,7 +30737,7 @@ function createMcpServer(options = {}) {
|
|
|
30463
30737
|
server,
|
|
30464
30738
|
"sdd_set_endpoints",
|
|
30465
30739
|
{
|
|
30466
|
-
description: "Bind concrete wire endpoints to existing L3 interface methods (required for every Portal). Pick `transport` and fill that transport's address fields. Run after sdd_define_interface.",
|
|
30740
|
+
description: "Bind concrete wire endpoints to existing L3 interface methods (required for every Portal). Pick `transport` and fill that transport's address fields. Run after sdd_define_interface. Written through the authoring seam as one gated delta, and answered with the change report \u2014 every binding set or changed, or that nothing changed \u2014 as structured content beside the sentence.",
|
|
30467
30741
|
inputSchema: {
|
|
30468
30742
|
interface: import_zod11.z.string().describe('The L3 interface ID (e.g. "ibilling-gateway")'),
|
|
30469
30743
|
endpoints: import_zod11.z.array(import_zod11.z.object({
|
|
@@ -30484,11 +30758,12 @@ function createMcpServer(options = {}) {
|
|
|
30484
30758
|
command: import_zod11.z.string().optional().describe("CLI: command/subcommand"),
|
|
30485
30759
|
address: import_zod11.z.string().optional().describe("Custom: free-form address")
|
|
30486
30760
|
}).strict()).describe("One binding per method")
|
|
30487
|
-
}
|
|
30761
|
+
},
|
|
30762
|
+
outputSchema: specChangeReportOutput
|
|
30488
30763
|
},
|
|
30489
30764
|
({ interface: interfaceId, endpoints }) => {
|
|
30490
30765
|
try {
|
|
30491
|
-
const { loadInterfaceSpec: loadInterfaceSpec2
|
|
30766
|
+
const { loadInterfaceSpec: loadInterfaceSpec2 } = requireSpecs();
|
|
30492
30767
|
const intf = loadInterfaceSpec2(interfaceId);
|
|
30493
30768
|
if (!intf) return errText(`Interface "${interfaceId}" does not exist.`);
|
|
30494
30769
|
const buildRaw = (e) => {
|
|
@@ -30512,6 +30787,7 @@ function createMcpServer(options = {}) {
|
|
|
30512
30787
|
}
|
|
30513
30788
|
};
|
|
30514
30789
|
const bound2 = [];
|
|
30790
|
+
const methods = [];
|
|
30515
30791
|
for (const e of endpoints) {
|
|
30516
30792
|
const m = intf.methods.find((x) => x.name === e.method);
|
|
30517
30793
|
if (!m) return errText(`Method "${e.method}" not found on interface "${interfaceId}". Define it via sdd_define_interface first.`);
|
|
@@ -30520,14 +30796,17 @@ function createMcpServer(options = {}) {
|
|
|
30520
30796
|
const detail = parsed.error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ");
|
|
30521
30797
|
return errText(`Invalid ${e.transport} endpoint for method "${e.method}": ${detail}`);
|
|
30522
30798
|
}
|
|
30523
|
-
|
|
30799
|
+
methods.push({ name: e.method, endpoint: parsed.data });
|
|
30524
30800
|
bound2.push(`${e.method}\u2192${e.transport}`);
|
|
30525
30801
|
}
|
|
30526
|
-
|
|
30527
|
-
|
|
30528
|
-
|
|
30802
|
+
const report2 = updateSpecGated("interface", interfaceId, { methods });
|
|
30803
|
+
return structured(
|
|
30804
|
+
`Bound ${bound2.length} endpoint(s) on "${interfaceId}": ${bound2.join(", ")}.
|
|
30805
|
+
${renderChangeReport(report2)}`,
|
|
30806
|
+
report2
|
|
30807
|
+
);
|
|
30529
30808
|
} catch (e) {
|
|
30530
|
-
return
|
|
30809
|
+
return errMessage(e);
|
|
30531
30810
|
}
|
|
30532
30811
|
}
|
|
30533
30812
|
);
|
|
@@ -30607,65 +30886,35 @@ function createMcpServer(options = {}) {
|
|
|
30607
30886
|
},
|
|
30608
30887
|
({ id, name, description, contract, sourcePath, simPath, technologies, injectedParams, detail, conformance, methods, status: status2 }) => {
|
|
30609
30888
|
try {
|
|
30610
|
-
const
|
|
30611
|
-
|
|
30612
|
-
|
|
30613
|
-
|
|
30614
|
-
|
|
30615
|
-
|
|
30616
|
-
|
|
30617
|
-
|
|
30618
|
-
|
|
30619
|
-
|
|
30620
|
-
|
|
30621
|
-
|
|
30622
|
-
|
|
30623
|
-
|
|
30624
|
-
|
|
30625
|
-
|
|
30626
|
-
|
|
30627
|
-
|
|
30628
|
-
|
|
30629
|
-
|
|
30630
|
-
|
|
30631
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
30632
|
-
const spec = {
|
|
30633
|
-
id,
|
|
30634
|
-
name,
|
|
30635
|
-
description,
|
|
30636
|
-
contract,
|
|
30637
|
-
sourcePath,
|
|
30638
|
-
simPath,
|
|
30639
|
-
technologies,
|
|
30640
|
-
injectedParams,
|
|
30641
|
-
detail,
|
|
30642
|
-
conformance,
|
|
30643
|
-
// Post-resolution every cases/catches entry has its numeric step —
|
|
30644
|
-
// the *Label twins exist only in the input type, not the spec's.
|
|
30645
|
-
methods: resolvedMethods,
|
|
30646
|
-
status: resolved.status,
|
|
30647
|
-
createdAt: now,
|
|
30648
|
-
updatedAt: now
|
|
30889
|
+
const restatement = {
|
|
30890
|
+
kind: "implementation",
|
|
30891
|
+
spec: {
|
|
30892
|
+
id,
|
|
30893
|
+
name,
|
|
30894
|
+
description,
|
|
30895
|
+
contract,
|
|
30896
|
+
sourcePath,
|
|
30897
|
+
simPath,
|
|
30898
|
+
technologies,
|
|
30899
|
+
injectedParams,
|
|
30900
|
+
detail,
|
|
30901
|
+
conformance,
|
|
30902
|
+
methods: (methods ?? []).map((m) => ({
|
|
30903
|
+
...m,
|
|
30904
|
+
narrative: (m.narrative ?? []).map((s, i) => ({ ...s, stepNumber: s.stepNumber ?? i + 1 }))
|
|
30905
|
+
}))
|
|
30906
|
+
},
|
|
30907
|
+
fields: implInputFields,
|
|
30908
|
+
memberFields: implMethodInputFields,
|
|
30909
|
+
status: status2
|
|
30649
30910
|
};
|
|
30650
|
-
|
|
30651
|
-
const cleared = clearedByOmission(existing, spec, implInputFields);
|
|
30652
|
-
if (existing) spec.createdAt = existing.createdAt;
|
|
30653
|
-
const notices = saveImplementationSpec2(spec);
|
|
30654
|
-
if (existing) {
|
|
30655
|
-
notices.push(...rewriteNotices({
|
|
30656
|
-
label: `Implementation "${id}"`,
|
|
30657
|
-
carried: ["createdAt", ...carried],
|
|
30658
|
-
cleared,
|
|
30659
|
-
removed: removedByName(existing.methods, resolvedMethods),
|
|
30660
|
-
removedSuffix: " (L5 narratives included)"
|
|
30661
|
-
}));
|
|
30662
|
-
}
|
|
30911
|
+
const receipt = writeSpec(restatement);
|
|
30663
30912
|
return writeReceipt(
|
|
30664
30913
|
`Successfully saved L4 Implementation Spec "${name}" (${id}) with method narratives.`,
|
|
30665
|
-
|
|
30914
|
+
receipt
|
|
30666
30915
|
);
|
|
30667
30916
|
} catch (e) {
|
|
30668
|
-
return
|
|
30917
|
+
return errMessage(e);
|
|
30669
30918
|
}
|
|
30670
30919
|
}
|
|
30671
30920
|
);
|
|
@@ -30714,9 +30963,6 @@ function createMcpServer(options = {}) {
|
|
|
30714
30963
|
},
|
|
30715
30964
|
({ kind, id, name, description, subsystem, group, fields, methods, componentClass, invariants, database, table, linkedEntity, sourcePath, symbol }) => {
|
|
30716
30965
|
try {
|
|
30717
|
-
const { loadTypeSpec: loadTypeSpec2, saveTypeSpec: saveTypeSpec2 } = requireSpecs();
|
|
30718
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
30719
|
-
const existing = loadTypeSpec2(id);
|
|
30720
30966
|
const spec = {
|
|
30721
30967
|
kind,
|
|
30722
30968
|
id,
|
|
@@ -30739,32 +30985,15 @@ function createMcpServer(options = {}) {
|
|
|
30739
30985
|
...table ? { table } : {},
|
|
30740
30986
|
...linkedEntity ? { linkedEntity } : {},
|
|
30741
30987
|
...sourcePath ? { sourcePath } : {},
|
|
30742
|
-
...symbol ? { symbol } : {}
|
|
30743
|
-
createdAt: now,
|
|
30744
|
-
updatedAt: now
|
|
30988
|
+
...symbol ? { symbol } : {}
|
|
30745
30989
|
};
|
|
30746
|
-
const
|
|
30747
|
-
const cleared = clearedByOmission(existing, spec, typeInputFields);
|
|
30748
|
-
if (existing) spec.createdAt = existing.createdAt;
|
|
30749
|
-
const notices = saveTypeSpec2(spec);
|
|
30750
|
-
if (existing) {
|
|
30751
|
-
notices.push(...rewriteNotices({
|
|
30752
|
-
label: `Type "${id}"`,
|
|
30753
|
-
carried: ["createdAt", ...carried],
|
|
30754
|
-
cleared,
|
|
30755
|
-
removed: [
|
|
30756
|
-
...removedByName(existing.fields, spec.fields).map((n) => `field ${n}`),
|
|
30757
|
-
...removedByName(existing.methods, spec.methods).map((n) => `method ${n}`)
|
|
30758
|
-
],
|
|
30759
|
-
removedNoun: "member"
|
|
30760
|
-
}));
|
|
30761
|
-
}
|
|
30990
|
+
const receipt = writeSpec({ kind: "type", spec, fields: typeInputFields });
|
|
30762
30991
|
return writeReceipt(
|
|
30763
|
-
`Successfully ${
|
|
30764
|
-
|
|
30992
|
+
`Successfully ${receipt.replacedExisting ? "re-authored" : "defined"} ${kind} type "${name}" (${id}).`,
|
|
30993
|
+
receipt
|
|
30765
30994
|
);
|
|
30766
30995
|
} catch (e) {
|
|
30767
|
-
return
|
|
30996
|
+
return errMessage(e);
|
|
30768
30997
|
}
|
|
30769
30998
|
}
|
|
30770
30999
|
);
|
|
@@ -30883,35 +31112,20 @@ function createMcpServer(options = {}) {
|
|
|
30883
31112
|
server,
|
|
30884
31113
|
"sdd_delete_spec",
|
|
30885
31114
|
{
|
|
30886
|
-
description: "Delete a specification file from the spec tree and clean up any empty parent directories.",
|
|
31115
|
+
description: "Delete a specification file from the spec tree and clean up any empty parent directories. Answers with a structured deletion beside the sentence: whether a spec was removed, and the tests that encode a method the deletion took away (when the project declares test roots).",
|
|
30887
31116
|
inputSchema: {
|
|
30888
31117
|
kind: import_zod11.z.enum(["subsystem", "component", "interface", "implementation", "type"]).describe("The kind of spec to delete"),
|
|
30889
31118
|
id: import_zod11.z.string().describe("The ID of the spec to delete")
|
|
30890
|
-
}
|
|
31119
|
+
},
|
|
31120
|
+
outputSchema: specDeletionOutput
|
|
30891
31121
|
},
|
|
30892
31122
|
({ kind, id }) => {
|
|
30893
31123
|
try {
|
|
30894
|
-
const
|
|
30895
|
-
|
|
30896
|
-
|
|
30897
|
-
|
|
30898
|
-
|
|
30899
|
-
break;
|
|
30900
|
-
case "component":
|
|
30901
|
-
deleted = specs.deleteComponentSpec(id);
|
|
30902
|
-
break;
|
|
30903
|
-
case "interface":
|
|
30904
|
-
deleted = specs.deleteInterfaceSpec(id);
|
|
30905
|
-
break;
|
|
30906
|
-
case "implementation":
|
|
30907
|
-
deleted = specs.deleteImplementationSpec(id);
|
|
30908
|
-
break;
|
|
30909
|
-
case "type":
|
|
30910
|
-
deleted = specs.deleteTypeSpec(id);
|
|
30911
|
-
break;
|
|
30912
|
-
}
|
|
30913
|
-
if (!deleted) return errText(`Spec of kind "${kind}" with ID "${id}" could not be deleted (file may not exist).`);
|
|
30914
|
-
return text(`Successfully deleted ${kind} spec "${id}".`);
|
|
31124
|
+
const deletion = deleteSpec2(kind, id);
|
|
31125
|
+
if (!deletion.deleted) return errText(`Spec of kind "${kind}" with ID "${id}" could not be deleted (file may not exist).`);
|
|
31126
|
+
const testsBlock = renderTestsToRevisit(deletion.testsToRevisit).join("\n");
|
|
31127
|
+
return structured(`Successfully deleted ${kind} spec "${id}".${testsBlock ? `
|
|
31128
|
+
${testsBlock}` : ""}`, deletion);
|
|
30915
31129
|
} catch (e) {
|
|
30916
31130
|
return errText(String(e));
|
|
30917
31131
|
}
|
|
@@ -30925,7 +31139,7 @@ function createMcpServer(options = {}) {
|
|
|
30925
31139
|
inputSchema: {
|
|
30926
31140
|
kind: import_zod11.z.enum(["system", "subsystem", "component", "interface", "implementation", "type"]).describe("The spec kind to update (system = the singleton L0 \u2014 vision, boundaries, globalRequirements, databases, and publicInterfaces: the project gateway surface, each entry {id, name, subsystem, component, type, details, audience: project|department|instance|partner|external}; id is informational)"),
|
|
30927
31141
|
id: import_zod11.z.string().describe("The ID of the spec to update (namespaced if needed)"),
|
|
30928
|
-
delta: import_zod11.z.record(import_zod11.z.any()).describe(`The partial fields to merge into the spec. ARRAYS UPSERT, they do not replace: an array whose elements carry an identity is merged element-by-element, so a delta naming ONE element leaves the others intact. Identity is "name" or "id" by default, and per field: dispatch by "capability", a listener's mounts by "portal", lifecycle by phase+component+method, emits/subscribesTo by topic+event, trustedLinks by "subsystem", invariants and patterns by "id", lint.allow by "code" AND "at" (an allow covers one occurrence, so several may share a code on one spec), an interface method's findings by "code", boundaries by "name", globalRequirements by "description", switch cases by "value", try catches by "error". Identity merging applies at EVERY depth, including an array INSIDE an element (a method's params, a step's catches). Add "action: 'delete'" (or "remove: true") alongside that identity to REMOVE an element \u2014 including a stale lint allow. Arrays of plain STRINGS (owns, dependsOn, guarantees) carry no per-element identity and are replaced wholesale; pass [] to clear any array outright. To REMOVE an optional field entirely, list it in "unset": e.g. {"unset": ["basePath", "variant"]} \u2014 passing null/undefined means "no change" (they are skipped), and writing "" would leave the field present but empty, which is a different and usually wrong spec. Unsetting a required field is refused by schema validation, which names it. For narrative steps, match by "stepNumber" and use "action: 'insert'" (shifts subsequent steps up) or "action: 'delete'" (shifts subsequent steps down and removes it). Step entries apply in ASCENDING stepNumber order, each against the numbering the earlier entries of the SAME delta left behind \u2014 delete step 3 and step 7 becomes step 6 \u2014 so prefer labels, and restate the step's "label" or "description" on a delete to have it checked against the step actually addressed. Renumbering RELOCATES every flow jump field (onTrueStep/onFalseStep/cases.step/defaultStep/endStep/catches.step/finallyStep/toStep) in the same narrative. A delete is REJECTED when the narrative has no such step, when a jump still targets it (retarget the referrers first), when a restated label/description does not match, or when it is a loop/try/parallel header whose body would be left standing (retype the header first to dissolve the region, then delete it). Changing a step's "type" REBUILDS it for the new type: its description and label are kept and every field the new type cannot carry is dropped (returned as a NOTICE); a delta that retypes AND sets such a field is refused. A step delta is also refused when it carries a marker the merge does not recognise: a non-boolean "remove", an "action" that is neither "insert" nor "delete", a "captureJumps" outside an insert, or no "stepNumber" to address. Inserting AT a jump target relocates those jumps past the inserted step by default (a NOTICE is returned) \u2014 add "captureJumps": true on the inserted step to retarget entry jumps onto it (loop/try endStep region tails always relocate with the body and are never captured). Every jump field has a "*Label" twin (toLabel, onFalseLabel, endLabel, \u2026, and "label" on a cases/catches entry) resolved against step labels AFTER the merge, so a delta may anchor on a label only pre-existing steps carry; a label the delta supplies REPLACES the stored number it twins, while setting the number and its label together in one delta is refused as a contradiction. Reference ids in deltas may use LOCAL names \u2014 they are qualified against the spec's namespace exactly as the loader would. Per-spec lint suppression: set "lint: { allow: [{ code, at, covers, reason }] }" to silence a WARNING code on this spec only (errors always surface; stale allows are flagged). An allow covers EXACTLY the occurrence it names: "at" is the site the finding names (a contract method, an import edge "from -> to", a declared edge "component -> target") and is REQUIRED for a code whose findings report one, while a finding that reports no site is covered only by an allow that names none; "covers" lists the units an aggregating finding reports, and the allow silences it only when every one is listed. This delta is deliberately OPEN below its top level \u2014 the shapes nest further than a schema here should restate \u2014 so a key that is not a field at its depth is not refused, it is NAMED BACK under NO EFFECT in the answer, together with any value the spec already held and any "unset" that removed nothing. Read that list: it is where a nested typo shows up.`),
|
|
31142
|
+
delta: import_zod11.z.record(import_zod11.z.any()).describe(`The partial fields to merge into the spec. ARRAYS UPSERT, they do not replace: an array whose elements carry an identity is merged element-by-element, so a delta naming ONE element leaves the others intact. Identity is "name" or "id" by default, and per field: dispatch by "capability", a listener's mounts by "portal", lifecycle by phase+component+method, emits/subscribesTo by topic+event, trustedLinks by "subsystem", a subsystem's publicInterfaces by component+interface (or type+details for an entry not yet bound to a component), invariants and patterns by "id", lint.allow by "code" AND "at" (an allow covers one occurrence, so several may share a code on one spec), an interface method's findings by "code", boundaries by "name", globalRequirements by "description", switch cases by "value", try catches by "error". Identity merging applies at EVERY depth, including an array INSIDE an element (a method's params, a step's catches). Add "action: 'delete'" (or "remove: true") alongside that identity to REMOVE an element \u2014 including a stale lint allow. Arrays of plain STRINGS (owns, dependsOn, guarantees) carry no per-element identity and are replaced wholesale; pass [] to clear any array outright. To REMOVE an optional field entirely, list it in "unset": e.g. {"unset": ["basePath", "variant"]} \u2014 passing null/undefined means "no change" (they are skipped), and writing "" would leave the field present but empty, which is a different and usually wrong spec. Unsetting a required field is refused by schema validation, which names it. For narrative steps, match by "stepNumber" and use "action: 'insert'" (shifts subsequent steps up) or "action: 'delete'" (shifts subsequent steps down and removes it). Step entries apply in ASCENDING stepNumber order, each against the numbering the earlier entries of the SAME delta left behind \u2014 delete step 3 and step 7 becomes step 6 \u2014 so prefer labels, and restate the step's "label" or "description" on a delete to have it checked against the step actually addressed. Renumbering RELOCATES every flow jump field (onTrueStep/onFalseStep/cases.step/defaultStep/endStep/catches.step/finallyStep/toStep) in the same narrative. A delete is REJECTED when the narrative has no such step, when a jump still targets it (retarget the referrers first), when a restated label/description does not match, or when it is a loop/try/parallel header whose body would be left standing (retype the header first to dissolve the region, then delete it). Changing a step's "type" REBUILDS it for the new type: its description and label are kept and every field the new type cannot carry is dropped (returned as a NOTICE); a delta that retypes AND sets such a field is refused. A step delta is also refused when it carries a marker the merge does not recognise: a non-boolean "remove", an "action" that is neither "insert" nor "delete", a "captureJumps" outside an insert, or no "stepNumber" to address. Inserting AT a jump target relocates those jumps past the inserted step by default (a NOTICE is returned) \u2014 add "captureJumps": true on the inserted step to retarget entry jumps onto it (loop/try endStep region tails always relocate with the body and are never captured). Every jump field has a "*Label" twin (toLabel, onFalseLabel, endLabel, \u2026, and "label" on a cases/catches entry) resolved against step labels AFTER the merge, so a delta may anchor on a label only pre-existing steps carry; a label the delta supplies REPLACES the stored number it twins, while setting the number and its label together in one delta is refused as a contradiction. Reference ids in deltas may use LOCAL names \u2014 they are qualified against the spec's namespace exactly as the loader would. Per-spec lint suppression: set "lint: { allow: [{ code, at, covers, reason }] }" to silence a WARNING code on this spec only (errors always surface; stale allows are flagged). An allow covers EXACTLY the occurrence it names: "at" is the site the finding names (a contract method, an import edge "from -> to", a declared edge "component -> target") and is REQUIRED for a code whose findings report one, while a finding that reports no site is covered only by an allow that names none; "covers" lists the units an aggregating finding reports, and the allow silences it only when every one is listed. This delta is deliberately OPEN below its top level \u2014 the shapes nest further than a schema here should restate \u2014 so a key that is not a field at its depth is not refused, it is NAMED BACK under NO EFFECT in the answer, together with any value the spec already held and any "unset" that removed nothing. Read that list: it is where a nested typo shows up.`),
|
|
30929
31143
|
dryRun: import_zod11.z.boolean().optional().describe("Ask what this delta WOULD do instead of doing it. The whole write runs, the candidate gate included, and the answer is the change report it would have produced \u2014 marked DRY RUN, with nothing stamped and not one byte of the stored file moved. Use it before a delta that renumbers a long narrative.")
|
|
30930
31144
|
},
|
|
30931
31145
|
outputSchema: specChangeReportOutput
|
|
@@ -31142,7 +31356,7 @@ async function startMcpServer() {
|
|
|
31142
31356
|
} catch {
|
|
31143
31357
|
}
|
|
31144
31358
|
}
|
|
31145
|
-
var import_mcp, import_stdio, import_zod11, import_types3, fs21, path32, import_url, TYPE_REF_GRAMMAR, staleServerOutput, SPEC_KINDS,
|
|
31359
|
+
var import_mcp, import_stdio, import_zod11, import_types3, fs21, path32, import_url, TYPE_REF_GRAMMAR, staleServerOutput, SPEC_KINDS, STATUS_VALUES, testsToRevisitOutput, specWriteReceiptOutput, specDeletionOutput, specChangeOutput, specChangeReportOutput, validationIssueOutput, validateTreeOutput, getSpecOutput, SERVER_BUILD_STAMP, STALE_SERVER_WARNING, SPEC_WRITE_TOOLS, listChangedEmitters, statusInput, SKILL_RESOURCE_MIME, AGENT_BRIEF_SCHEME;
|
|
31146
31360
|
var init_server = __esm({
|
|
31147
31361
|
"src/mcp/server.ts"() {
|
|
31148
31362
|
"use strict";
|
|
@@ -31159,7 +31373,6 @@ var init_server = __esm({
|
|
|
31159
31373
|
init_core();
|
|
31160
31374
|
init_core();
|
|
31161
31375
|
init_defaults();
|
|
31162
|
-
init_narrative_labels();
|
|
31163
31376
|
init_specs();
|
|
31164
31377
|
init_skills();
|
|
31165
31378
|
init_specs2();
|
|
@@ -31181,7 +31394,16 @@ var init_server = __esm({
|
|
|
31181
31394
|
)
|
|
31182
31395
|
};
|
|
31183
31396
|
SPEC_KINDS = ["system", "subsystem", "component", "interface", "implementation", "type"];
|
|
31184
|
-
|
|
31397
|
+
STATUS_VALUES = ["draft", "design", "complete"];
|
|
31398
|
+
testsToRevisitOutput = import_zod11.z.array(import_zod11.z.object({
|
|
31399
|
+
method: import_zod11.z.string().describe("The contract method this write changed or deleted."),
|
|
31400
|
+
symbol: import_zod11.z.string().describe("The code-level name searched for: the method's `symbol` when it declares one, else its name."),
|
|
31401
|
+
imported: import_zod11.z.array(import_zod11.z.string()).describe("Test files that IMPORT that symbol \u2014 the high-confidence list, because an import is a binding and not a coincidence."),
|
|
31402
|
+
mentioned: import_zod11.z.array(import_zod11.z.string()).describe("Test files that name the symbol without importing it \u2014 usually a test driving it through a portal. Empty when `indiscriminate` is true."),
|
|
31403
|
+
indiscriminate: import_zod11.z.boolean().describe("True when the bare name matched more than a tenth of the test suite, so the mention list was withheld as noise.")
|
|
31404
|
+
})).describe(
|
|
31405
|
+
"The tests that encode a method this write changed or deleted, one entry per such method. Empty when the write touched no method, when the project declares no `rules.conformance.testRoots`, or when nothing references them. Read it before promising that existing tests still pass."
|
|
31406
|
+
);
|
|
31185
31407
|
specWriteReceiptOutput = {
|
|
31186
31408
|
kind: import_zod11.z.enum(SPEC_KINDS).describe("The spec kind written."),
|
|
31187
31409
|
id: import_zod11.z.string().describe('The id the spec is stored under; "system" for the L0 singleton.'),
|
|
@@ -31189,8 +31411,8 @@ var init_server = __esm({
|
|
|
31189
31411
|
replacedExisting: import_zod11.z.boolean().describe(
|
|
31190
31412
|
"True when a spec already held this id and the call re-authored it in place rather than adding one. The notices then say what was carried forward, removed or cleared by the restatement."
|
|
31191
31413
|
),
|
|
31192
|
-
status: import_zod11.z.enum(
|
|
31193
|
-
"The lifecycle status written. Absent for a type, which
|
|
31414
|
+
status: import_zod11.z.enum(STATUS_VALUES).optional().describe(
|
|
31415
|
+
"The lifecycle status written. Absent for the L0 and a type, which carry none."
|
|
31194
31416
|
),
|
|
31195
31417
|
notices: import_zod11.z.array(import_zod11.z.string()).describe(
|
|
31196
31418
|
"The notice lines the text answer lists, one per entry: what was carried forward, what a restatement removed, what an omission cleared, and every gate warning the write raised. Empty for a clean create."
|
|
@@ -31198,6 +31420,14 @@ var init_server = __esm({
|
|
|
31198
31420
|
scaffoldedProjectPath: import_zod11.z.string().optional().describe(
|
|
31199
31421
|
"The child project directory a chained subsystem's create scaffolded; absent for every other write."
|
|
31200
31422
|
),
|
|
31423
|
+
testsToRevisit: testsToRevisitOutput,
|
|
31424
|
+
...staleServerOutput
|
|
31425
|
+
};
|
|
31426
|
+
specDeletionOutput = {
|
|
31427
|
+
kind: import_zod11.z.enum(SPEC_KINDS).describe("The spec kind deleted."),
|
|
31428
|
+
id: import_zod11.z.string().describe("The id the spec was stored under."),
|
|
31429
|
+
deleted: import_zod11.z.boolean().describe("True when a spec document was removed."),
|
|
31430
|
+
testsToRevisit: testsToRevisitOutput,
|
|
31201
31431
|
...staleServerOutput
|
|
31202
31432
|
};
|
|
31203
31433
|
specChangeOutput = {
|
|
@@ -31221,15 +31451,7 @@ var init_server = __esm({
|
|
|
31221
31451
|
),
|
|
31222
31452
|
notices: import_zod11.z.array(import_zod11.z.string()).describe("Store placement notices, gate warnings and delta notices."),
|
|
31223
31453
|
summary: import_zod11.z.string().describe("One line for people."),
|
|
31224
|
-
testsToRevisit:
|
|
31225
|
-
method: import_zod11.z.string().describe("The contract method this write changed or deleted."),
|
|
31226
|
-
symbol: import_zod11.z.string().describe("The code-level name searched for: the method's `symbol` when it declares one, else its name."),
|
|
31227
|
-
imported: import_zod11.z.array(import_zod11.z.string()).describe("Test files that IMPORT that symbol \u2014 the high-confidence list, because an import is a binding and not a coincidence."),
|
|
31228
|
-
mentioned: import_zod11.z.array(import_zod11.z.string()).describe("Test files that name the symbol without importing it \u2014 usually a test driving it through a portal. Empty when `indiscriminate` is true."),
|
|
31229
|
-
indiscriminate: import_zod11.z.boolean().describe("True when the bare name matched more than a tenth of the test suite, so the mention list was withheld as noise.")
|
|
31230
|
-
})).describe(
|
|
31231
|
-
"The tests that encode a method this write changed or deleted, one entry per such method. Empty when the write touched no method, when the project declares no `rules.conformance.testRoots`, or when nothing references them. Read it before promising that existing tests still pass."
|
|
31232
|
-
),
|
|
31454
|
+
testsToRevisit: testsToRevisitOutput,
|
|
31233
31455
|
...staleServerOutput
|
|
31234
31456
|
};
|
|
31235
31457
|
validationIssueOutput = {
|
|
@@ -31303,11 +31525,9 @@ var init_server = __esm({
|
|
|
31303
31525
|
"sdd_update_spec"
|
|
31304
31526
|
]);
|
|
31305
31527
|
listChangedEmitters = /* @__PURE__ */ new WeakMap();
|
|
31306
|
-
|
|
31307
|
-
statusInput = import_zod11.z.enum(STATUS_ORDER).optional().describe(
|
|
31528
|
+
statusInput = import_zod11.z.enum(STATUS_VALUES).optional().describe(
|
|
31308
31529
|
"The spec's lifecycle status. Omitted means draft for a NEW spec and the status already stored for a re-authoring, so a restatement never reopens a frozen spec. State it to author straight at design or complete instead of promoting afterwards. A status that would LOWER the stored one is refused \u2014 reopening a spec for revision is sdd_update_spec's job, which sets the demotion deliberately."
|
|
31309
31530
|
);
|
|
31310
|
-
ALWAYS_CARRIED_FIELDS = /* @__PURE__ */ new Set(["ext"]);
|
|
31311
31531
|
SKILL_RESOURCE_MIME = "text/markdown";
|
|
31312
31532
|
AGENT_BRIEF_SCHEME = "wairon-agent://";
|
|
31313
31533
|
}
|
|
@@ -31861,6 +32081,7 @@ init_errors();
|
|
|
31861
32081
|
init_fs();
|
|
31862
32082
|
init_core();
|
|
31863
32083
|
init_core();
|
|
32084
|
+
init_authoring();
|
|
31864
32085
|
init_core();
|
|
31865
32086
|
function composeAgentBrief2(agentId) {
|
|
31866
32087
|
return composeAgentBrief(agentId);
|
|
@@ -31886,8 +32107,11 @@ function projectConfigExists2() {
|
|
|
31886
32107
|
function loadSystemSpec2() {
|
|
31887
32108
|
return loadSystemSpec();
|
|
31888
32109
|
}
|
|
31889
|
-
function
|
|
31890
|
-
|
|
32110
|
+
function loadSubsystemSpec2(id) {
|
|
32111
|
+
return loadSubsystemSpec(id);
|
|
32112
|
+
}
|
|
32113
|
+
function writeSpec2(restatement) {
|
|
32114
|
+
return writeSpec(restatement);
|
|
31891
32115
|
}
|
|
31892
32116
|
function resolveAgentTopology2() {
|
|
31893
32117
|
return resolveAgentTopology();
|
|
@@ -31970,6 +32194,19 @@ function getStatusReport2(options, decor) {
|
|
|
31970
32194
|
function approvalVerdict2() {
|
|
31971
32195
|
return approvalVerdict();
|
|
31972
32196
|
}
|
|
32197
|
+
function subsystemAddRestatement(id, projectPath, name, stored) {
|
|
32198
|
+
const spec = { id, projectPath };
|
|
32199
|
+
const fields = ["id", "projectPath"];
|
|
32200
|
+
if (name !== void 0 || !stored) {
|
|
32201
|
+
spec.name = name ?? id;
|
|
32202
|
+
fields.push("name");
|
|
32203
|
+
}
|
|
32204
|
+
if (!stored) {
|
|
32205
|
+
spec.description = `External subsystem ${spec.name}`;
|
|
32206
|
+
fields.push("description");
|
|
32207
|
+
}
|
|
32208
|
+
return { kind: "subsystem", spec, fields };
|
|
32209
|
+
}
|
|
31973
32210
|
async function runSubsystemAdd(id, options = {}) {
|
|
31974
32211
|
logger.header("wairon subsystem add");
|
|
31975
32212
|
if (!projectConfigExists2()) {
|
|
@@ -31984,23 +32221,12 @@ async function runSubsystemAdd(id, options = {}) {
|
|
|
31984
32221
|
if (!system) {
|
|
31985
32222
|
throw new WaironError("System spec is missing. Run `wairon init` first.");
|
|
31986
32223
|
}
|
|
31987
|
-
const
|
|
31988
|
-
const
|
|
31989
|
-
const
|
|
31990
|
-
id,
|
|
31991
|
-
name: displayName,
|
|
31992
|
-
description: `External subsystem ${displayName}`,
|
|
31993
|
-
parentSystem: system.name,
|
|
31994
|
-
publicInterfaces: [],
|
|
31995
|
-
projectPath: options.projectPath,
|
|
31996
|
-
trustedLinks: [],
|
|
31997
|
-
status: "draft",
|
|
31998
|
-
createdAt: now,
|
|
31999
|
-
updatedAt: now
|
|
32000
|
-
};
|
|
32001
|
-
createChainedSubsystem2(subsystem, displayName);
|
|
32224
|
+
const stored = loadSubsystemSpec2(id);
|
|
32225
|
+
const restatement = subsystemAddRestatement(id, options.projectPath, options.name, stored);
|
|
32226
|
+
const receipt = writeSpec2(restatement);
|
|
32002
32227
|
const childDir = path30.resolve(getProjectRoot(), options.projectPath);
|
|
32003
|
-
logger.success(
|
|
32228
|
+
logger.success(`${receipt.replacedExisting ? "Re-authored" : "Added"} external subsystem "${id}" \u2192 ${options.projectPath}`);
|
|
32229
|
+
for (const notice of receipt.notices) logger.info(notice);
|
|
32004
32230
|
logger.info(`Scaffolded child project at ${path30.relative(process.cwd(), childDir) || "."}`);
|
|
32005
32231
|
logger.info(`Design its spec tree from this parent using namespaced ids (e.g. ${id}::<component>).`);
|
|
32006
32232
|
}
|
|
@@ -32136,20 +32362,16 @@ async function runInitAsExternalSubsystem(parentRoot, cwd, options) {
|
|
|
32136
32362
|
if (!system) {
|
|
32137
32363
|
throw new WaironError(`Parent project at ${parentRoot} has no system spec.`);
|
|
32138
32364
|
}
|
|
32139
|
-
const
|
|
32140
|
-
const
|
|
32141
|
-
|
|
32142
|
-
|
|
32143
|
-
description: `External subsystem ${subsystemId}
|
|
32144
|
-
|
|
32145
|
-
|
|
32146
|
-
|
|
32147
|
-
|
|
32148
|
-
|
|
32149
|
-
createdAt: now,
|
|
32150
|
-
updatedAt: now
|
|
32151
|
-
};
|
|
32152
|
-
createChainedSubsystem2(subsystem, subsystemId);
|
|
32365
|
+
const stored = loadSubsystemSpec2(subsystemId);
|
|
32366
|
+
const spec = { id: subsystemId, projectPath: relPath };
|
|
32367
|
+
const fields = ["id", "projectPath"];
|
|
32368
|
+
if (!stored) {
|
|
32369
|
+
Object.assign(spec, { name: subsystemId, description: `External subsystem ${subsystemId}` });
|
|
32370
|
+
fields.push("name", "description");
|
|
32371
|
+
}
|
|
32372
|
+
const restatement = { kind: "subsystem", spec, fields };
|
|
32373
|
+
const receipt = writeSpec2(restatement);
|
|
32374
|
+
for (const notice of receipt.notices) logger.info(notice);
|
|
32153
32375
|
} finally {
|
|
32154
32376
|
setProjectRoot(prevOverride);
|
|
32155
32377
|
}
|
|
@@ -39217,8 +39439,8 @@ function buildEvaluation(input) {
|
|
|
39217
39439
|
let unresolvedPacks;
|
|
39218
39440
|
if (requiredDefaultResolution) {
|
|
39219
39441
|
unresolvedPacks = requiredDefaultResolution.unresolved;
|
|
39220
|
-
const
|
|
39221
|
-
missingPackNames =
|
|
39442
|
+
const canonical2 = [...new Set(requiredDefaultResolution.resolved.map((r) => r.name))];
|
|
39443
|
+
missingPackNames = canonical2.filter((n) => !present.has(n));
|
|
39222
39444
|
} else {
|
|
39223
39445
|
missingPackNames = requiredDefaultNames(policy).filter((n) => !present.has(n));
|
|
39224
39446
|
unresolvedPacks = [];
|