capability-compiler 0.1.1__tar.gz → 0.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/PKG-INFO +1 -1
  2. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/_version.py +1 -1
  3. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/adapters/fake_adapter.py +56 -1
  4. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/cli/_common.py +4 -36
  5. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/server/executor_factory.py +6 -2
  6. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/synthesis/engine.py +8 -2
  7. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/tests/test_mcp_server.py +57 -0
  8. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/tests/test_synthesis.py +28 -0
  9. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/.gitignore +0 -0
  10. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/LICENSE +0 -0
  11. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/README.md +0 -0
  12. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/docs/adapters/browser.md +0 -0
  13. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/docs/architecture/README.md +0 -0
  14. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/docs/architecture/mcp.md +0 -0
  15. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/docs/architecture/registry.md +0 -0
  16. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/docs/architecture.md +0 -0
  17. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/docs/benchmarks.md +0 -0
  18. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/docs/ci.md +0 -0
  19. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/docs/cli/commands.md +0 -0
  20. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/docs/concepts/exploration.md +0 -0
  21. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/docs/concepts/local-first.md +0 -0
  22. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/docs/master-plan.md +0 -0
  23. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/docs/models.md +0 -0
  24. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/docs/research/differentiation.md +0 -0
  25. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/docs/research/landscape.md +0 -0
  26. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/docs/research/sources/benchmarks-skill-learning.md +0 -0
  27. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/docs/research/sources/desktop-automation.md +0 -0
  28. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/docs/research/sources/gui-agents-vision.md +0 -0
  29. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/docs/research/sources/mcp-ecosystem.md +0 -0
  30. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/docs/research/sources/packaging-security.md +0 -0
  31. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/pyproject.toml +0 -0
  32. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/__init__.py +0 -0
  33. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/adapters/__init__.py +0 -0
  34. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/adapters/base.py +0 -0
  35. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/adapters/browser/__init__.py +0 -0
  36. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/adapters/browser/adapter.py +0 -0
  37. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/adapters/browser/aria.py +0 -0
  38. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/adapters/browser/elements.py +0 -0
  39. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/adapters/desktop/__init__.py +0 -0
  40. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/adapters/desktop/base.py +0 -0
  41. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/benchmark/__init__.py +0 -0
  42. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/benchmark/data/capability_bench.json +0 -0
  43. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/benchmark/data/seed.json +0 -0
  44. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/benchmark/report.py +0 -0
  45. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/benchmark/spec.py +0 -0
  46. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/benchmark/suite.py +0 -0
  47. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/cli/__init__.py +0 -0
  48. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/cli/_register.py +0 -0
  49. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/cli/benchmark_cmd.py +0 -0
  50. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/cli/config_cmds.py +0 -0
  51. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/cli/execute_cmd.py +0 -0
  52. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/cli/learn_cmd.py +0 -0
  53. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/cli/main.py +0 -0
  54. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/cli/registry_cmds.py +0 -0
  55. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/cli/serve_cmd.py +0 -0
  56. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/compiler.py +0 -0
  57. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/config.py +0 -0
  58. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/errors.py +0 -0
  59. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/exploration/__init__.py +0 -0
  60. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/exploration/effects.py +0 -0
  61. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/exploration/engine.py +0 -0
  62. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/exploration/semantics.py +0 -0
  63. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/logging.py +0 -0
  64. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/models/__init__.py +0 -0
  65. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/models/action.py +0 -0
  66. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/models/capability.py +0 -0
  67. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/models/observation.py +0 -0
  68. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/models/state.py +0 -0
  69. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/models/transition.py +0 -0
  70. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/models/verification.py +0 -0
  71. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/models/versioning.py +0 -0
  72. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/perception/__init__.py +0 -0
  73. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/perception/pipeline.py +0 -0
  74. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/perception/semantic.py +0 -0
  75. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/providers/__init__.py +0 -0
  76. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/providers/anthropic.py +0 -0
  77. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/providers/base.py +0 -0
  78. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/providers/http.py +0 -0
  79. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/providers/mock.py +0 -0
  80. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/providers/ollama.py +0 -0
  81. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/providers/openai_compat.py +0 -0
  82. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/recording/__init__.py +0 -0
  83. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/recording/recorder.py +0 -0
  84. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/recording/replay.py +0 -0
  85. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/refinement/__init__.py +0 -0
  86. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/refinement/engine.py +0 -0
  87. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/refinement/repair.py +0 -0
  88. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/registry/__init__.py +0 -0
  89. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/registry/permissions.py +0 -0
  90. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/registry/registry.py +0 -0
  91. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/runtime/__init__.py +0 -0
  92. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/runtime/assertions.py +0 -0
  93. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/runtime/executor.py +0 -0
  94. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/server/__init__.py +0 -0
  95. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/server/auth.py +0 -0
  96. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/server/server.py +0 -0
  97. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/server/tool_defs.py +0 -0
  98. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/storage/__init__.py +0 -0
  99. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/storage/base.py +0 -0
  100. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/storage/filesystem.py +0 -0
  101. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/storage/sqlite.py +0 -0
  102. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/synthesis/__init__.py +0 -0
  103. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/synthesis/templating.py +0 -0
  104. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/types.py +0 -0
  105. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/verification/__init__.py +0 -0
  106. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/verification/base.py +0 -0
  107. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/verification/file_visual.py +0 -0
  108. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/verification/runner.py +0 -0
  109. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/src/capability_compiler/verification/verifiers.py +0 -0
  110. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/tests/conftest.py +0 -0
  111. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/tests/fixtures/site/index.html +0 -0
  112. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/tests/test_action_semantics.py +0 -0
  113. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/tests/test_aria_parser.py +0 -0
  114. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/tests/test_benchmark.py +0 -0
  115. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/tests/test_browser_adapter.py +0 -0
  116. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/tests/test_browser_unit.py +0 -0
  117. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/tests/test_cli.py +0 -0
  118. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/tests/test_compile_e2e.py +0 -0
  119. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/tests/test_compiler.py +0 -0
  120. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/tests/test_config.py +0 -0
  121. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/tests/test_e2e_record_replay.py +0 -0
  122. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/tests/test_errors.py +0 -0
  123. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/tests/test_executor.py +0 -0
  124. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/tests/test_exploration.py +0 -0
  125. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/tests/test_exploration_e2e.py +0 -0
  126. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/tests/test_logging.py +0 -0
  127. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/tests/test_models.py +0 -0
  128. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/tests/test_perception.py +0 -0
  129. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/tests/test_providers_http.py +0 -0
  130. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/tests/test_recording.py +0 -0
  131. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/tests/test_refinement.py +0 -0
  132. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/tests/test_refinement_e2e.py +0 -0
  133. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/tests/test_registry.py +0 -0
  134. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/tests/test_state_consistency_eval.py +0 -0
  135. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/tests/test_storage.py +0 -0
  136. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/tests/test_types.py +0 -0
  137. {capability_compiler-0.1.1 → capability_compiler-0.1.2}/tests/test_verifiers.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: capability-compiler
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: Turn any software into an API for AI: compile interaction trajectories into verified, reusable capabilities exposed via Python, CLI, and MCP.
5
5
  Project-URL: Homepage, https://github.com/capability-compiler/capability-compiler
6
6
  Project-URL: Documentation, https://github.com/capability-compiler/capability-compiler/tree/main/docs
@@ -5,4 +5,4 @@ without importing the whole package (pyproject reads it at build time via
5
5
  hatchling's version hook — see ``[tool.hatch.version]``).
6
6
  """
7
7
 
8
- __version__ = "0.1.1"
8
+ __version__ = "0.1.2"
@@ -96,6 +96,46 @@ class FakeAdapter:
96
96
 
97
97
  # -- test/Bench helpers -----------------------------------------------------
98
98
 
99
+ def seed_demo_page(self, *, url: str = "demo://page", title: str = "Demo Page") -> None:
100
+ """Plant the deterministic three-element demo page.
101
+
102
+ A bare fake adapter starts empty — exploration has nothing to
103
+ compile and execution fails its preconditions (the elements a
104
+ learned capability refers to don't exist). This page (a Docs link,
105
+ a Search box, a Submit button) is the canonical offline demo
106
+ environment: ``cc learn``/``cc execute`` and the MCP server's
107
+ default executor all seed it so the full
108
+ learn → compile → persist → execute loop works with zero setup.
109
+ """
110
+ self.set_state(
111
+ url=url,
112
+ title=title,
113
+ elements=[
114
+ InteractiveElement(
115
+ element_id="nav_docs",
116
+ ref="nav-docs",
117
+ role="link",
118
+ name="Docs",
119
+ action_hints=["click"],
120
+ ),
121
+ InteractiveElement(
122
+ element_id="search_box",
123
+ ref="search-box",
124
+ role="searchbox",
125
+ name="Search",
126
+ action_hints=["type", "click"],
127
+ ),
128
+ InteractiveElement(
129
+ element_id="submit_btn",
130
+ ref="submit-btn",
131
+ role="button",
132
+ name="Submit",
133
+ action_hints=["click"],
134
+ ),
135
+ ],
136
+ text_digest="demo page with docs link, search box, and submit button",
137
+ )
138
+
99
139
  def set_state(
100
140
  self,
101
141
  *,
@@ -227,6 +267,21 @@ class FakeAdapter:
227
267
  # -- registration ---------------------------------------------------------------
228
268
 
229
269
 
270
+ def seed_demo_page(adapter: Any, url: str) -> bool:
271
+ """Seed *adapter* with the demo page when (and only when) it is a FakeAdapter.
272
+
273
+ Real adapters (browser/desktop) are never touched — their state belongs
274
+ to the live environment. Returns ``True`` when the demo page was planted.
275
+
276
+ The query string is stripped so ``https://example.com/docs?q=x`` and
277
+ ``https://example.com/docs`` land on the same deterministic page.
278
+ """
279
+ if not isinstance(adapter, FakeAdapter):
280
+ return False
281
+ adapter.seed_demo_page(url=url.rsplit("?", 1)[0])
282
+ return True
283
+
284
+
230
285
  def _factory(settings: Any | None = None) -> FakeAdapter:
231
286
  return FakeAdapter(settings)
232
287
 
@@ -234,7 +289,7 @@ def _factory(settings: Any | None = None) -> FakeAdapter:
234
289
  register_adapter("fake", _factory, description=FakeAdapter.description, replace=True)
235
290
 
236
291
 
237
- __all__ = ["FakeAdapter"]
292
+ __all__ = ["FakeAdapter", "seed_demo_page"]
238
293
 
239
294
 
240
295
  def id_for(prefix: str = "fake") -> str:
@@ -140,7 +140,7 @@ def parse_key_path(key_path: str) -> list[str]:
140
140
 
141
141
 
142
142
  def seed_fake_demo(adapter_obj: Any, url: str) -> bool:
143
- """Seed a FakeAdapter with a small demo page so the pipeline has work.
143
+ """Seed a FakeAdapter with the small demo page so the pipeline has work.
144
144
 
145
145
  The fake adapter starts empty — with no interactive elements exploration
146
146
  runs dry (nothing to compile) and execution fails its preconditions (the
@@ -154,38 +154,6 @@ def seed_fake_demo(adapter_obj: Any, url: str) -> bool:
154
154
  Returns:
155
155
  ``True`` when the demo page was planted, ``False`` otherwise.
156
156
  """
157
- if type(adapter_obj).__name__ != "FakeAdapter":
158
- return False
159
- from capability_compiler.models import InteractiveElement
160
-
161
- page = url.rsplit("?", 1)[0]
162
- elements = [
163
- InteractiveElement(
164
- element_id="nav_docs",
165
- ref="nav-docs",
166
- role="link",
167
- name="Docs",
168
- action_hints=["click"],
169
- ),
170
- InteractiveElement(
171
- element_id="search_box",
172
- ref="search-box",
173
- role="searchbox",
174
- name="Search",
175
- action_hints=["type", "click"],
176
- ),
177
- InteractiveElement(
178
- element_id="submit_btn",
179
- ref="submit-btn",
180
- role="button",
181
- name="Submit",
182
- action_hints=["click"],
183
- ),
184
- ]
185
- adapter_obj.set_state(
186
- url=page,
187
- title="Demo Page",
188
- elements=elements,
189
- text_digest="demo page with docs link, search box, and submit button",
190
- )
191
- return True
157
+ from capability_compiler.adapters.fake_adapter import seed_demo_page
158
+
159
+ return seed_demo_page(adapter_obj, url)
@@ -49,12 +49,16 @@ def build_default_factory() -> ExecutorFactory:
49
49
 
50
50
  The default keeps the MCP server runnable end-to-end with zero setup so
51
51
  `cc doctor`-style smoke tests succeed even before a real adapter is
52
- registered. Production deployments should override this with the real
53
- adapter (browser/desktop) at the CLI wiring layer.
52
+ registered. The adapter is seeded with the same demo page ``cc learn``
53
+ uses offline, so capabilities learned from that page hold their
54
+ preconditions when invoked over MCP. Production deployments should
55
+ override this with the real adapter (browser/desktop) at the CLI wiring
56
+ layer.
54
57
  """
55
58
 
56
59
  def factory(settings: CompilerSettings) -> CapabilityExecutor:
57
60
  adapter = FakeAdapter(settings)
61
+ adapter.seed_demo_page()
58
62
  log.debug(
59
63
  "executor_factory.default_built",
60
64
  extra={"extra_fields": {"adapter": adapter.kind}},
@@ -286,7 +286,13 @@ class CapabilitySynthesizer:
286
286
  name=pname,
287
287
  type="string",
288
288
  description=f"Value for '{element_name}'",
289
- required=True,
289
+ # The recorded literal is the default ("literals become
290
+ # defaults, not guesses"): callers may omit the input and
291
+ # the executor/template binder falls back to it. Keeping
292
+ # required=True here would contradict the default and make
293
+ # MCP tool calls with omitted arguments fail SDK-side
294
+ # validation before the executor could apply the default.
295
+ required=False,
290
296
  default=action.text,
291
297
  example=action.text,
292
298
  )
@@ -298,7 +304,7 @@ class CapabilitySynthesizer:
298
304
  name=pname,
299
305
  type="string",
300
306
  description=f"Selected value for '{element_name}'",
301
- required=True,
307
+ required=False, # recorded literal is the default (see above)
302
308
  default=action.value,
303
309
  example=action.value,
304
310
  )
@@ -29,6 +29,7 @@ from capability_compiler.models import (
29
29
  NavigateAction,
30
30
  PermissionScope,
31
31
  ProcedureStep,
32
+ TypeAction,
32
33
  )
33
34
  from capability_compiler.registry import CapabilityRegistry
34
35
  from capability_compiler.server import (
@@ -233,6 +234,62 @@ class TestExecuteTool:
233
234
  assert "navigate" in captured["adapter"].calls
234
235
  assert "click" in captured["adapter"].calls
235
236
 
237
+ async def test_default_factory_runs_demo_capability_with_omitted_inputs(
238
+ self, tmp_path: Path
239
+ ) -> None:
240
+ """Regression: demo-learned capabilities run over MCP with no args.
241
+
242
+ Two bugs used to break this path: synthesis emitted
243
+ ``required=True`` alongside the recorded default (empty ``arguments``
244
+ failed the SDK-side validation before the executor could apply the
245
+ default), and the default executor factory built a bare FakeAdapter
246
+ (preconditions like "'Search' is present" failed because the demo
247
+ page ``cc learn`` compiles from was never seeded server-side).
248
+ """
249
+ registry = CapabilityRegistry.from_settings(_settings(tmp_path))
250
+ await registry.save(
251
+ _make_capability(
252
+ name="demo_search",
253
+ inputs=[
254
+ CapabilityParameter(
255
+ name="search",
256
+ type="string",
257
+ description="Value for 'Search'",
258
+ required=False,
259
+ default="hello",
260
+ )
261
+ ],
262
+ procedure=CapabilityProcedure(
263
+ steps=[
264
+ ProcedureStep(
265
+ step_id="s1",
266
+ action=TypeAction(
267
+ target=ActionTarget(
268
+ ref="search-box", role="searchbox", name="Search"
269
+ ),
270
+ text="{{search}}",
271
+ ),
272
+ ),
273
+ ProcedureStep(
274
+ step_id="s2",
275
+ action=ClickAction(
276
+ target=ActionTarget(
277
+ ref="submit-btn", role="button", name="Submit"
278
+ )
279
+ ),
280
+ ),
281
+ ]
282
+ ),
283
+ )
284
+ )
285
+ # Default factory: bare FakeAdapter + demo page, as `cc serve` uses it.
286
+ server = CapabilityMCPServer(registry, _settings(tmp_path))
287
+ result = await server.execute_tool("demo_search", {})
288
+ assert result["isError"] is False, result["content"][0]["text"]
289
+ body = json.loads(result["content"][0]["text"])
290
+ assert body["status"] == "success"
291
+ assert all(step["success"] for step in body["steps"])
292
+
236
293
  async def test_ping_returns_health_payload(self, tmp_path: Path) -> None:
237
294
  registry = CapabilityRegistry.from_settings(_settings(tmp_path))
238
295
  await registry.save(_make_capability(name="alpha"))
@@ -146,6 +146,34 @@ class TestSynthesis:
146
146
  assert params["format"].default == "png"
147
147
  assert params["file_name"].default == "report"
148
148
 
149
+ async def test_recorded_parameters_are_optional(self):
150
+ """Recorded literals become defaults, not required inputs.
151
+
152
+ Regression: synthesis used to emit ``required=True`` alongside the
153
+ recorded default. The executor applies defaults when a caller omits
154
+ the input, so ``required`` contradicted ``default`` — and the MCP
155
+ tool built from the capability rejected empty-args calls at the SDK
156
+ validation layer before the executor could fill the default in.
157
+ """
158
+ app = ExportFakeApp()
159
+ trajectory, initial, final = await record_export_workflow(app)
160
+ capability = await CapabilitySynthesizer(use_model=False).synthesize(
161
+ trajectory, initial_observation=initial, final_observation=final
162
+ )
163
+
164
+ params = {p.name: p for p in capability.inputs}
165
+ assert params["format"].required is False
166
+ assert params["file_name"].required is False
167
+
168
+ # The MCP wire schema mirrors the contract: nothing required, the
169
+ # recorded literal surfaced as the JSON-Schema default.
170
+ from capability_compiler.server.tool_defs import build_input_schema
171
+
172
+ schema = build_input_schema(capability)
173
+ assert schema.get("required", []) == []
174
+ assert schema["properties"]["format"]["default"] == "png"
175
+ assert schema["properties"]["file_name"]["default"] == "report"
176
+
149
177
  async def test_preconditions_derived(self):
150
178
  app = ExportFakeApp()
151
179
  trajectory, initial, final = await record_export_workflow(app)