Repository navigation
Module identity unification: one mechanism, one ring, one grammar - #136
Merged
Merged
Conversation
…r module self-description IntoChat.AppHost moves to src/DigitalBrain/AppHost as DigitalBrain.AppHost; the DigitalBrain.OS library and DigitalBrain.OS.Host merge into one DigitalBrain.OS executable. Spec and plan for the next step: ids/hosting/deployment declared on the module type, OsCatalog as types, enabled-by-default composition in the image. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Module identity attributes (ModuleId, ModuleHosting, ModuleDeployment) and ModuleIdentity move to DigitalBrain.Contracts so the AppHost-side hosting package can read them without referencing the kernel ring. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…moved Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ables them Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
String WithModule composition calls are gone; the silo composes the OS catalog itself. AI sensitive-data telemetry moves to AppHost configuration. The product composition fact now checks the reference composition against OsCatalog. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…figuration Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…le hosting attributes fail loudly; module ids match configuration casing - HostingModuleOptions.Flatten omits leaves equal to the type's defaults, so an AppHost's untouched option values cannot override the silo's own configuration (the shell title regression the review caught). - ModuleHostingResolution distinguishes an absent assembly (null: composition without that infrastructure) from a present assembly missing the named type (loud error); CompositionFacts gates every catalog [ModuleHosting] string. - SelectModules matches module ids case-insensitively, like configuration keys. - BrandingFacts pins the shell title in host configuration instead of Program.cs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…est structure - DigitalBrain.OS references modules by glob (every runtime module in the repo), with OsCatalog as the compiler-checked semantic list; vestigial Orleans.Client, redundant Kernel reference and repo-covered DisableMSBuildAssemblyCopyCheck gone. - DigitalBrain.Aspire.Hosting's catch-all Brain/ folder becomes Composition/, Resources/ and Identity/ plus root hosting entry points. - DigitalBrain.Aspire.Server: DigitalBrainRuntimeHostingExtensions renames to ServerHostingExtensions; the one-file Configuration/ folder flattens. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
IntoChat.ServiceDefaults moves to src/Aspire/DigitalBrain.ServiceDefaults (it was never product-specific) and stops being a host concern: AddDigitalBrainServer and AddDigitalBrainClient apply the process defaults themselves, MapDigitalBrainModules maps /health and /alive, and the two duplicate copies die — the Client's private ClientHostingDefaults (with its five duplicated OpenTelemetry package references) and the Testing ring's ReferenceTelemetry. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
DigitalBrain:ShippedApps:{publisher}:Path points the host at a directory of app
folders (DirectoryShippedAppSource, same shape and line-ending normalization as
the embedded source). IntoChat.Apps is deleted: the AppHost hands the OS the
repo's src/IntoChat/Apps path, the docker image copies the folder and sets the
same key. The OS's own apps remain embedded — they are the OS's.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The product host tests move to DigitalBrain.OS.Tests.{Unit,E2E}: CompositionFacts →
ProductCompositionFacts, ShippedAppFacts → VendorAppFacts, the E2E builder/fixture →
ProductE2E/ProductHostFixture alongside the reference fixture. Trimmed in passing:
IdentityHostOptionsFacts loses the case that tested configuration precedence rather
than our code, and PathTruthFacts' one surviving fact becomes a stronger catalog-wide
closure check in OsCatalogFacts (every catalog module's assembly ships with the
runtime). src/IntoChat is now only the Apps content folder; the intochat CI suite
folds into the os suite with a budget that covers both fixtures.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- The Orleans dashboard is gone end to end (WithDashboard, MapDigitalBrainDashboard, two Dashboard properties, the hosting URL annotation, the package reference): dashboards are not DigitalBrain's responsibility. - AddDigitalBrain(name, options): persistentStorage, dataVolume and serviceId fold into DigitalBrainHostingOptions, deleting the duplicate-serviceId contradiction. - Dead API deleted: DigitalBrainHostingNames.ForModule and GetOrAddModuleNode(Type). - WithModule(Type) refuses a type without [ModuleId] instead of minting an id. - DigitalBrainModuleBuilder.GetOrAddProjection replaces the GetOrAddState/out added/ AddProjection dance in nine module hosting packages. - StandaloneOptions inlined to the one config read; SiloHosts error message no longer names a Testing-ring option. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…yment WithHttpIdentity() reads DigitalBrain:Identity from the AppHost's own configuration and projects it; the code literals, the IntoChatConfiguration constant and the Compile-Include hack that smuggled it into the AppHost are gone. The image's appsettings remains the standalone deployment's statement, and a unit fact pins the two descriptors to agree while both live in this repo (the AppHost's env projection would otherwise shadow a drift). The unread IntoChat__Hosted__Enabled flag leaves the Dockerfile and publish profile. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…o magic keys IdentityHostOptions and AuthOptions (with IdentityPosture) move to the contracts ring so every writer shares the reader's vocabulary: the hosting ring's HttpIdentity twin is deleted (WithHttpIdentity binds and projects the one type), AuthPosture stops re-spelling the posture key, and tests write configuration through IdentityHostOptions.Key/AuthOptions.Key + nameof instead of string paths. The module test host's process side-channel is owned by one ModuleHostChannel type (keys, serialization and resolution in a single place). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ProductSurfaceResources dies: resource names are the two literals they always were, and the UI/MCP ports are AppHost configuration (DigitalBrain:Http:UiPort/McpPort) with their current defaults. EnvironmentKeys.For is the one config-key-to-env-var conversion (six hand-rolled Replace sites fold into it). DigitalBrainHostingOptions owns its external override keys (Orleans:ServiceId, Orleans:ClusterId, DigitalBrain:Hosting:PersistentStorage), leaving DigitalBrainHostingNames with names only. Gmail and Salesforce hosting errors stop naming a deployment port. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Orleans resource suffix and the Kernel module-node name become private constants of the hosting extension that uses them; the master key parameter name moves onto MasterKey. A miscellaneous-constants bucket is where dead code and misfiled keys accumulated twice; nothing replaces it. EnvironmentKeys stays as the one process-boundary adapter: options bind inside a process, and env vars are how the AppHost writes what another process's configuration reads. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The contract that declares an integration's fields now also owns their configuration home: IntegrationDefinition.SectionName and Key(id, field). The seeder, the AI/Gmail/GitHub hosting projections and every test fixture spell the path through the contract instead of seventeen scattered strings. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…d by architecture DigitalBrainConfiguration (Contracts) is the single grammar: EnvironmentName is the one ':'-to-'__' conversion, Flatten the one JSON walk (full fidelity in-process, omit-defaults cross-process), and the module sections (Modules, per-module Options, brain-scoped overlay) are named once. EnvironmentKeys and the Kernel's duplicate walker are gone. Every raw DigitalBrain:* key literal in the tree now lives in the type that declares that vocabulary — 91 files carried literals, 27 declaration owners remain (options and contract types such as AIOptions, GitHubModuleOptions, FlutterHostOptions, KernelCorsOptions now in Platform.Contracts, SalesforceOAuthOptions and PostgresModuleOptions in their Contracts, ScriptEdgeProtocol, ModuleHostChannel) — and ConfigurationKeyFacts pins the registry exactly in both directions: a new literal fails, a cured file must leave the list. The test tiers ratify the new shapes: <Module>.Tests.Hosting is the model-only Aspire composition tier (it must not boot), DigitalBrain.OS.Tests.Unit is product scope, a boot is a StartAsync, and the AppHost nests per the layout law at src/DigitalBrain/AppHost/DigitalBrain.AppHost. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The credential-suffix blacklist is gone: a name heuristic cannot carry the design rule that secrets belong to the integrations registration, and it put product vocabulary into the contracts ring. What remains is the structural contract — a writable property the serializer skips would silently drop configuration. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Its last check guarded a mistake the codebase has never made (a publicly writable property the serializer skips); the two real JsonIgnore uses are the sanctioned pattern — internal-set resolved values that deliberately do not flatten. The secrets rule lives in the module contract and the integrations registration, not in reflection on every compile. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The catalog lists module instances too (Task 4 shares this commit: the OS build and its suites interlock with the instance-based registration). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The client project moved from src/DigitalBrain/Client to src/DigitalBrain.Client, but the sandbox's ClientProject default still named the old path, so every script failed to restore. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The renderer and AppUiEndpoints learned table and chart nodes, but CompositionValidation still refused them, so DataVisualizer's show failed with "Unsupported composition child kind". Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…un loop A TaskCanceledException from an HTTP timeout escaped RunLoop's per-run catch and its outer catch, ending the view's loop for good; both now only stop on the view's own cancellation. A pump also waits a second after its stream ends before resubscribing, not only after a failed resubscribe. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The workflows and .gitignore still named src/Modules after the module folder moved. 0c190ed's subject is wrong: OS apps are VerifiedByBuild; the bump only clears the differs-without-bump error. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… behaviors, no app tests Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A restarted run-on-change script (reaped, or redeployed by binding the db slot) left views stale until the next registry write. The retry delay now follows every stream end, so a stream that completes cleanly cannot spin. Version 1.0.3. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
First-party apps are verified by their build, and app tests left the app model (DataVisualizer now declares no scenario). ByBuild still demanded at least one scenario heading, so os/datavisualizer 1.0.2+ stayed out at startup with an empty failure list and the Marketplace kept serving 1.0.1. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…w is Inspect Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…brain signals live in the ring Every grain a program resolves through the brain is a neuron now, including the platform identity actors, the identity directory and the shell state. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Read returns the word's snapshot; Inspect is the rich view. The bound-app guard covers the members ISynapse now declares. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Shipped app versions bumped so the renamed bundles publish; deployed program keys are computed through AppProgramKey, which the end-to-end fact probes by generation. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Review fix pass: disposing a compiler iterator while a read is in flight threw and leaked the watch from AppInvocations and the workspace events endpoint. Also: shell state access whitelists Read/Save, the program key helper is internal, and every identity neuron contract must be platform-only. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
IBrain is the entrypoint and the first neuron: Create(Owner), Read, Neuron<T>(NeuronId), On<T>(NeuronId). It owns its address space; nothing else builds or parses keys. NeuronId and SignalId replace bare strings. Synapse is the durable connection as a record (Publisher, Signal), not a grain; execution is a neuron of its own kind. States are states, not snapshots. IDigitalBrain, ISynapse and the snapshot records are gone. Every file carries XML documentation. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
PlatformOnly and PlatformAssembly are enforcement, not vocabulary. They move to DigitalBrain.Framework.Enforcement. ICSharpAppBinding drops the attribute: BindApp is already refused at runtime for any caller but the owning Apps grain. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Resolving is addressing, not a call: IDigitalBrain.Get<T>(NeuronId) is synchronous like a grain factory, On<T>(INeuron) streams a neuron's signals, Self is the brain's own neuron. IKernel : INeuron holds Create(Owner) and Read(); programs never create brains. NeuronId converts implicitly from a string. "Kernel" now names the first neuron; the runtime is the runtime. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
KernelNeuron persists the ring State, Create(Owner) publishes Created, runs the ICreationHandlers and publishes Activated; every wake publishes Activated through KernelActivation. Neuron seals OnActivateAsync and runs IActivationHandler<TNeuron> handlers registered by modules; CSharpFileNeuron and AgentNeuron move their activation work into handlers. IDigitalBrains opens a brain, resolves raw grains and streams signals; CurrentBrain is the silo's IDigitalBrain; the Orleans client and the script connection implement the new entrypoint. Brain ids are flat: the kernel's key, no hash. ISynapse's members return to ICSharpFile with CSharpFileChanged as its change signal. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed
The whole module system now runs on one mechanism, ratified in
docs/superpowers/specs/2026-10-05-module-identity-amendment.md(amending the 2026-10-04 module self-description design):ModuleId(Contracts) owns the id grammar;Module/Module<TOptions>abstract classes replaceIModule— the id is constructor-enforced, no attribute, no reflection.ModuleIdentity,[ModuleId],[ModuleHosting],[ModuleDeployment]are deleted.OsCatalog.ModulesisIReadOnlyList<Module>; the instance is the descriptor; everyActivator.CreateInstancefactory is gone.ModuleHosting<TModule>/ModuleDeployment<TModule>replace the string type-name attributes; discovery scans referenced assemblies of a known root (ModuleHostingScan, deploymentDiscover) instead ofAssembly.Load/Type.GetTypeby name.DigitalBrain:AI,:Workspace,:Apps,:ShippedApps,:CSharp,:Microsoft:GitHub,:Microsoft:Aspire,:Salesforce:OAuth,:Flutter:Hosting,:Time. All module keys derive fromDigitalBrainConfiguration.ModuleSection/ModuleOptionsSection.LegacyConfigurationGuardrefuses retired roots at OS startup; AI and Flutter binds defend their own roots where the OS guard cannot see them.Validate()checks only an options object's own values; cross-ring facts (model markers) resolve loudly once at bind in the hosting ring, with errors naming the value and the catalog.AIOptions.CopyFromand the caller-lessDigitalBrainModuleBuilder<AIModuleHosting>model API are deleted.ModuleKeyGrammarFactspins module keys toDigitalBrain:Modules:(closed allowlist with reasons) and scans Dockerfiles/pubxml/launchSettings for retired env roots; theConfigurationKeyOwners.jsonratchet shrank by eight files.Why
The attribute scheme flattened typed links into assembly-qualified strings (
Assembly.Load, four-way error taxonomies, id cross-checks), the module concept was split across Contracts/Kernel by a dependency accident, and AI (plus seven other modules) carried private configuration roots beside the one grammar. All three were the same rule violated: a fact belongs to the ring that owns it, as a typed reference; strings exist only at the configuration boundary.Reviewer notes
DigitalBrain__ShippedApps__…env var — would crash-loop the container against the guard) and Important (legacy Flutter hosting section silently ignored in the AppHost process) findings are fixed with RED→GREEN tests.RepositoryLayoutFacts(.slnx grouping drift) and Kernel E2E boot in environments without Postgres. Theaspire runsmoke still needs one pass on a provisioned dev machine.SetDefaultLlm/SetDefaultEmbeddingplumbing inAIHostingState; no equality pin betweenServiceTelemetryOptions' mirror constant andAIKeys;DigitalBrainBuilder.Registerduplicate check is Ordinal whileModuleIdequality is case-insensitive.🤖 Generated with Claude Code