Symptom
This is the same class of bug as #1070 (RESOLVED v0.67.147 for domain_services) but generalised — a systematic audit of build_appspec against AppSpec's field set found 16 fields that exist on both ModuleFragment AND AppSpec but are NOT mapped from merged_fragment in build_appspec's final AppSpec(...) construction. Of these 16, 11 are confirmed-dropping in real DSL authored by example apps + fixtures:
| Field |
Apps/fixtures with parsed content silently dropped |
archetypes |
support_tickets (2), pra (3) |
assets |
pra (4) |
channels |
simple_task (3), pra (11) |
documents |
pra (4) |
event_model |
simple_task (1), pra (1) |
hless_pragma |
pra (1) |
messages |
simple_task (5), pra (7) |
projections |
pra (7) |
streams |
pra (9) |
subscriptions |
simple_task (2), pra (7) |
templates |
pra (5) |
The remaining 5 fields (data_products, e2e_flows, fixtures, interfaces, policies) are unmapped in build_appspec too — they just aren't exercised by current example apps / fixtures, so we can't observe the drop. Same bug shape; same fix shape.
Reproduction
from pathlib import Path
from dazzle.core.parser import parse_modules
from dazzle.core.linker import build_appspec
base = Path("examples/simple_task")
modules = parse_modules(sorted((base / "dsl").glob("*.dsl")))
appspec = build_appspec(modules, "simple_task.core")
# Per-module: simple_task.messaging has 3 channels, 5 messages, 2 subscriptions
# simple_task.events has 1 event_model
# Merged appspec: ALL zero / None
print(f"channels: {len(appspec.channels)} (parsed: 3)") # 0
print(f"messages: {len(appspec.messages)} (parsed: 5)") # 0
print(f"subscriptions: {len(appspec.subscriptions)} (parsed: 2)") # 0
print(f"event_model: {appspec.event_model}") # None (parsed: 1)
Same shape on pra — 7 constructs drop with non-trivial counts.
Root cause (same as #1070, 11× over)
build_appspec in src/dazzle/core/linker.py:194 constructs the final AppSpec(...) by explicitly mapping each field from merged_fragment. The explicit mapping list is missing 16 entries. The fields exist on ModuleFragment (so the parser writes them); they exist on AppSpec (so consumers can read them); but the bridge is incomplete.
Possible secondary cause for some: merge_fragments in linker_impl.py:1444 also doesn't always thread these through (some, like domain_services before v0.67.147, are missing from the SymbolTable + merge return). The full pipeline has 3 stages:
- Parse —
dsl_parser_impl/*.py writes to module.fragment.X ✓
- Symbol-collect —
linker_impl.build_symbol_table collects from per-module fragments
- Merge —
linker_impl.merge_fragments builds unified ModuleFragment
- AppSpec construction —
linker.build_appspec maps merged fragment → final AppSpec
A drop can happen at stage 2 (no symbol collection), stage 3 (no merge mapping), or stage 4 (no AppSpec mapping). The fix for each field is to verify the field is preserved at all stages — the same 5-touch-point check from the v0.67.147 Agent Guidance.
Consumer impact
Each dropped field has consumers that operate on the dropped data and produce wrong answers:
appspec.channels is read by event-bus and notification routing
appspec.messages is read by the channel-routing dispatch
appspec.subscriptions is read by the projection-event wiring
appspec.event_model is read by HLESS validation
appspec.templates is read by the runtime renderer for template: DSL declarations
- ... etc
Every consumer is operating on [] / None regardless of DSL content, exactly like the domain_services validator's "step will fail at runtime" warning that didn't reflect the actual DSL.
Effectively, every Dazzle app silently has 11+ feature areas disabled at the runtime layer, masked by the fact that the parser reports them correctly so authors think they're wired up.
Suggested fix
Same shape as the v0.67.147 fix, replicated 11×:
- Audit
SymbolTable for missing field declarations
- Audit
build_symbol_table for missing per-module collection loops
- Audit
merge_fragments for missing return kwargs
- Audit
build_appspec's AppSpec(...) mapping for missing keyword args
- Add a regression test per field — extends
test_domain_services_propagated_to_appspec from cycle 121 into a parametrised version that asserts every AppSpec field with a corresponding ModuleFragment field round-trips through build_appspec
Better: write the parametrised regression test FIRST, see all 11+ failures, then fix one field at a time.
Discovered by
/improve cycle 128 framework-ux lane, proactive audit against the v0.67.147 Agent Guidance note: "This pattern likely also affects params or other less-tested constructs — worth a separate audit cycle." The audit grepped build_appspec's explicit field mapping against AppSpec.model_fields and surfaced 16 candidate drops; cross-app verification confirmed 11 with measurable impact.
Companion to #1070 (which fixed domain_services). This issue covers the remaining 16 field-propagation gaps that the cycle 121 fix didn't touch.
The systemic nature is its own finding: a single linker change that misses one field is easy to miss; 16 fields silently missing across 4 stages suggests the linker pipeline needs a generic propagation pattern, not 16 individual fixes. Worth discussing the design before implementing the fixes one-at-a-time.
Symptom
This is the same class of bug as #1070 (RESOLVED v0.67.147 for
domain_services) but generalised — a systematic audit ofbuild_appspecagainstAppSpec's field set found 16 fields that exist on bothModuleFragmentANDAppSpecbut are NOT mapped frommerged_fragmentinbuild_appspec's finalAppSpec(...)construction. Of these 16, 11 are confirmed-dropping in real DSL authored by example apps + fixtures:archetypessupport_tickets(2),pra(3)assetspra(4)channelssimple_task(3),pra(11)documentspra(4)event_modelsimple_task(1),pra(1)hless_pragmapra(1)messagessimple_task(5),pra(7)projectionspra(7)streamspra(9)subscriptionssimple_task(2),pra(7)templatespra(5)The remaining 5 fields (
data_products,e2e_flows,fixtures,interfaces,policies) are unmapped inbuild_appspectoo — they just aren't exercised by current example apps / fixtures, so we can't observe the drop. Same bug shape; same fix shape.Reproduction
Same shape on
pra— 7 constructs drop with non-trivial counts.Root cause (same as #1070, 11× over)
build_appspecinsrc/dazzle/core/linker.py:194constructs the finalAppSpec(...)by explicitly mapping each field frommerged_fragment. The explicit mapping list is missing 16 entries. The fields exist onModuleFragment(so the parser writes them); they exist onAppSpec(so consumers can read them); but the bridge is incomplete.Possible secondary cause for some:
merge_fragmentsinlinker_impl.py:1444also doesn't always thread these through (some, likedomain_servicesbefore v0.67.147, are missing from the SymbolTable + merge return). The full pipeline has 3 stages:dsl_parser_impl/*.pywrites tomodule.fragment.X✓linker_impl.build_symbol_tablecollects from per-module fragmentslinker_impl.merge_fragmentsbuilds unifiedModuleFragmentlinker.build_appspecmaps merged fragment → finalAppSpecA drop can happen at stage 2 (no symbol collection), stage 3 (no merge mapping), or stage 4 (no AppSpec mapping). The fix for each field is to verify the field is preserved at all stages — the same 5-touch-point check from the v0.67.147 Agent Guidance.
Consumer impact
Each dropped field has consumers that operate on the dropped data and produce wrong answers:
appspec.channelsis read by event-bus and notification routingappspec.messagesis read by the channel-routing dispatchappspec.subscriptionsis read by the projection-event wiringappspec.event_modelis read by HLESS validationappspec.templatesis read by the runtime renderer fortemplate:DSL declarationsEvery consumer is operating on
[]/Noneregardless of DSL content, exactly like thedomain_servicesvalidator's "step will fail at runtime" warning that didn't reflect the actual DSL.Effectively, every Dazzle app silently has 11+ feature areas disabled at the runtime layer, masked by the fact that the parser reports them correctly so authors think they're wired up.
Suggested fix
Same shape as the v0.67.147 fix, replicated 11×:
SymbolTablefor missing field declarationsbuild_symbol_tablefor missing per-module collection loopsmerge_fragmentsfor missing return kwargsbuild_appspec'sAppSpec(...)mapping for missing keyword argstest_domain_services_propagated_to_appspecfrom cycle 121 into a parametrised version that asserts everyAppSpecfield with a correspondingModuleFragmentfield round-trips throughbuild_appspecBetter: write the parametrised regression test FIRST, see all 11+ failures, then fix one field at a time.
Discovered by
/improvecycle 128 framework-ux lane, proactive audit against the v0.67.147 Agent Guidance note: "This pattern likely also affectsparamsor other less-tested constructs — worth a separate audit cycle." The audit greppedbuild_appspec's explicit field mapping againstAppSpec.model_fieldsand surfaced 16 candidate drops; cross-app verification confirmed 11 with measurable impact.Companion to #1070 (which fixed
domain_services). This issue covers the remaining 16 field-propagation gaps that the cycle 121 fix didn't touch.The systemic nature is its own finding: a single linker change that misses one field is easy to miss; 16 fields silently missing across 4 stages suggests the linker pipeline needs a generic propagation pattern, not 16 individual fixes. Worth discussing the design before implementing the fixes one-at-a-time.