Skip to content

AMPR-161 #482: declarative PhaseSpark surface + spark composition scaffolding#485

Merged
wow-miley merged 2 commits into
mainfrom
miley/ampr-161-implement-declarative-phasespark-authoring-surface
May 17, 2026
Merged

AMPR-161 #482: declarative PhaseSpark surface + spark composition scaffolding#485
wow-miley merged 2 commits into
mainfrom
miley/ampr-161-implement-declarative-phasespark-authoring-surface

Conversation

@wow-miley
Copy link
Copy Markdown
Contributor

Summary

Lands the AMPR-161 spike (declarative PhaseSpark authoring) plus foundational scaffolding for the broader unification of all agents under a single spark-driven architecture.

The PR has two layered bodies of work — both safe with the spike flag off (AmpereSpikeFlags.declarativeSparksEnabled = false):

Part 1 — Declarative PhaseSpark (AMPR-161 core)

Markdown-authored sparks reach the LLM payload through PhaseSparkManager and AgentLLMService.activePromptProvider:

  • DeclarativePhaseSparkSource / DeclarativePhaseSpark types + parseSpark parser with typed SparkParseResult { Ok | Failed } and SparkParseError sealed errors
  • Three bundled fixtures: cooking-domain.spark.md, recipe-arc-task.spark.md, minimal-edge.spark.md under composeResources/files/sparks/
  • PhaseSparkLibrary + DefaultPhaseSparkLibrary with deterministic phase / tag / keyword selection; KMP-platform fallbacks (JVM/Android classpath, no-op on iOS/JS/wasm)
  • AmpereSpikeFlags.declarativeSparksEnabled: Boolean = false gates the entire path
  • AgentLLMService.activePromptProvider: (() -> String?)? = null evaluated at call time, prepended to systemMessage with blank-line separator
  • PhaseSparkManager refactored from hasActivePhase: Boolean to appliedSparks: MutableList<PhaseSpark>; cleans up in reverse order
  • Integration test asserts spark body reaches the LLM payload verbatim AND the matching SparkAppliedEvent.sparkName == "PhaseSpark:cooking-domain" is emitted

Part 2 — Spark composition scaffolding (unification follow-up)

Following design discussion in chat, Spark becomes pure data and SparkStack actually composes (rather than the previous mutually-exclusive "last wins"):

  • Spark interface enriched with optional pure-data fields — no behavioral lambdas, so sparks stay shareable as .spark.md:
    • agentRole: String? — fragment of the role label (concatenated across the stack)
    • phaseContributions: Map<CognitivePhase, String> — per-PROPEL-phase markdown sections
    • requestedToolIds: Set<ToolId> — additive tool requests (vs. the existing narrowing allowedTools)
  • SparkStack composition:
    • buildSystemPrompt(currentPhase) concatenates every spark's promptContribution + relevant phaseContributions[currentPhase] — brute-force composition; 10 sparks all show up
    • effectiveAgentRole() concatenates with " + " (e.g., Cooking Domain + Project Manager)
    • effectiveRequestedTools() unions
  • Phase-aware system prompt: AutonomousAgent.currentCognitivePhase is set by PhaseSparkManager on entry / cleared on cleanup; currentSystemPrompt includes the right per-phase guidance for each LLM call
  • Parser supports ## When Perceiving/Planning/Executing/Learning sections → populates phaseContributions; DeclarativePhaseSpark becomes one-spark-per-source with eligiblePhases: Set<CognitivePhase> (no more per-phase expansion)
  • Tool-owned ParameterStrategy: Tool.parameterStrategy: ParameterStrategy? = null. ToolExecutionEngine consults the tool's own strategy first, falls back to externally-registered map for back-compat
  • RoleSpark.{Code,Research,Operations,Planning} enriched with agentRole + requestedToolIds so they participate in unified composition
  • State rename: ProductAgentStateProductState, ProjectAgentStateProjectState (consistency with CodeState, QualityState)

Deliberately not in this PR (filed as follow-up tickets)

  • Delete CodeAgent/ProductAgent/ProjectAgent/QualityAgent and route every consumer through SparkBasedAgent<S> factories — requires migrating ~2,500 LOC of hand-rolled step routing into proper tools (brief in .context/ticket-brief-delete-typed-agents-spark-migration.md)
  • Runtime Agent + dual-tool pattern for cleaner framework / agent-knowledge separation (brief in .context/ticket-brief-runtime-agent-dual-tool.md)
  • LLM-fusion composition of multiple sparks
  • User-authored sparks, hot-reload, SQLDelight persistence
  • Converting bundled typed RoleSpark to .spark.md (deferred until Kaml-in-commonMain)

Test plan

  • ./gradlew :ampere-core:jvmTest (already passing on this branch)
  • ./gradlew :ampere-core:compileCommonMainKotlinMetadata (already passing — per release pre-flight memory)
  • ./gradlew ktlintFormat (clean)
  • Run scripts/demo-declarative-sparks.sh to see the spark stack assembly end-to-end (writes report to ampere-core/build/declarative-sparks-demo.txt)
  • Confirm flag-off baseline: all existing PhaseSparkManager tests pass unchanged

🤖 Generated with Claude Code

…n scaffolding

Lands the AMPR-161 spike (declarative PhaseSpark authoring) plus the foundational
refactor toward a unified spark-driven agent architecture.

Declarative PhaseSpark (AMPR-161 core):
- `DeclarativePhaseSparkSource` + `DeclarativePhaseSpark` types, parser, and
  3 bundled `.spark.md` fixtures under `composeResources/files/sparks/`
- `PhaseSparkLibrary` interface + `DefaultPhaseSparkLibrary` with deterministic
  phase/tag/keyword selection; KMP-platform fallbacks for resource loading
- `AmpereSpikeFlags.declarativeSparksEnabled` gates the new path (default off)
- `AgentLLMService.activePromptProvider` evaluated at call time prepends the
  agent's composed system prompt to outbound LLM payloads
- `PhaseSparkManager` refactored from single-spark boolean to multi-spark list;
  cleans up in reverse order
- Integration test asserts spark bodies reach the LLM payload verbatim and
  emit the expected SparkAppliedEvent/SparkRemovedEvent pairs

Spark composition scaffolding (unification follow-up):
- `Spark` interface enriched with optional pure-data fields: `agentRole`,
  `phaseContributions: Map<CognitivePhase, String>`, `requestedToolIds`. All
  default empty — no behavioral lambdas; sparks stay shareable as `.spark.md`
- `SparkStack.buildSystemPrompt(currentPhase)` now concatenates EVERY spark's
  contribution plus its per-phase section. `effectiveAgentRole()` concatenates
  fragments across the stack; `effectiveRequestedTools()` unions.
- `SparkParser` extracts `## When Perceiving/Planning/Executing/Learning`
  sections into `phaseContributions`
- `DeclarativePhaseSpark` carries `eligiblePhases` + `phaseContributions`
  (one spark per source, multi-phase-capable)
- `AutonomousAgent.currentCognitivePhase` tracked by `PhaseSparkManager` so
  `currentSystemPrompt` includes the right per-phase guidance per LLM call
- `Tool.parameterStrategy: ParameterStrategy?` — tools own their own
  param-fill strategies; `ToolExecutionEngine` prefers tool-owned over the
  externally-registered map
- `RoleSpark.Code/Research/Operations/Planning` enriched with `agentRole` +
  `requestedToolIds` for composition participation
- State rename: `ProductAgentState` → `ProductState`, `ProjectAgentState` → `ProjectState`

Out of scope (filed as follow-up tickets in `.context/`):
- Deleting `CodeAgent`/`ProductAgent`/`ProjectAgent`/`QualityAgent` and
  migrating their hand-rolled step-routing into proper tools
- Runtime Agent + dual-tool pattern for framework/agent separation
- LLM-fusion composition of multiple sparks
- User-authored sparks, hot-reload, SQLDelight persistence
- Converting bundled `RoleSpark` to `.spark.md` (deferred until Kaml-in-commonMain)

All existing tests pass with `declarativeSparksEnabled = false`. New tests:
SparkParserTest, PhaseSparkLibraryTest, AgentLLMServiceActivePromptProviderTest,
SparkBasedAgentActivePromptProviderTest, DeclarativeSparkIntegrationTest, and
extended PhaseSparkManagerTest cases.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 17, 2026

Concept staleness check — clean. No tracked-source changes need a concept update.

…epts

- Move PhaseSparkLibraryTest from commonTest to jvmTest. The test calls
  DefaultPhaseSparkLibrary.load() which relies on the JVM classpath
  fallback for resource access. Android unit tests (testDebugUnitTest)
  don't have the bundled .spark.md files on their unit-test classpath,
  causing all six library assertions to fail in CI. This matches the
  existing pattern in ProviderPricingCatalogTest (also jvmTest only).
  At runtime on Android, Res.readBytes works through Compose Resources,
  so the implementation itself remains commonMain — only the test moves.
- Update docs/concepts/spark-system.md to reflect the new Spark fields
  (phaseContributions, agentRole, requestedToolIds), composition rules
  (additive, not exclusive), declarative .spark.md support, and the new
  source files. Bump last_verified.

Concept-Verified: CognitiveRelay
Concept-Verified: PluginPermissions
Concept-Verified: PropelLoop

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@wow-miley wow-miley merged commit bc8765d into main May 17, 2026
3 checks passed
@wow-miley wow-miley deleted the miley/ampr-161-implement-declarative-phasespark-authoring-surface branch May 17, 2026 08:12
@wow-miley wow-miley mentioned this pull request May 23, 2026
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant