Skip to content

Commit 074bbc7

Browse files
wow-mileyMiley Chandonnetclaude
authored
AMPR-172 #501: Canonical PROPEL enforcement — six-phase CognitivePhase (#511)
I extended `CognitivePhase` to the canonical six-phase PROPEL cycle (PERCEIVE / RECALL / OBSERVE / PLAN / EXECUTE / LEARN), added built-in `PhaseSpark.Recall` and `PhaseSpark.Observe`, fixed every exhaustiveness site the compiler surfaced (`SparkStack.phaseHeader`, `AmperePhosphorBridge.effectForPhase`), removed the silent `LEARN → PhosphorPhase.EVALUATE` paveover in the CLI bridge, expanded the role-spark `.spark.md` fixtures to cover the new phases, rewrote the PROPEL narrative across README/AGENTS/STYLE/docs, added a `PhaseSparkTest` locking the acronym ordering plus a six-phase walk in `AmperePhosphorBridgeTest`, and recorded the breaking change in a new `CHANGELOG.md`. Closes #501 Co-authored-by: Miley Chandonnet <miley@Mileys-Mac-mini.local> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 89c97a7 commit 074bbc7

21 files changed

Lines changed: 326 additions & 53 deletions

File tree

AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ See [ampere-cli/README.md](ampere-cli/README.md) for full CLI documentation.
7777

7878
## Architecture at a Glance
7979

80-
Ampere follows a layered architecture built on the **PROPEL** cognitive loop (Perceive, Recall, Optimize, Plan, Execute, Loop) and six core primitives (Tickets, Tasks, Plans, Meetings, Outcomes, Knowledge).
80+
Ampere follows a layered architecture built on the **PROPEL** cognitive loop (Perceive, Recall, Observe, Plan, Execute, Learn) and six core primitives (Tickets, Tasks, Plans, Meetings, Outcomes, Knowledge).
8181

8282
| Layer | Location | Purpose |
8383
|-------|----------|---------|

CHANGELOG.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# Changelog
2+
3+
All notable changes to AMPERE are recorded here. Dates are in UTC.
4+
5+
The project is pre-1.0; breaking changes are acceptable and explicitly called out.
6+
7+
## [Unreleased]
8+
9+
### Breaking
10+
11+
- **PROPEL `CognitivePhase` enum is now canonically six members**
12+
([AMPR-172](https://linear.app/miley/issue/AMPR-172)).
13+
14+
`CognitivePhase` in
15+
`link.socket.ampere.agents.domain.cognition.sparks.PhaseSpark.kt`
16+
becomes the full PROPEL cycle in canonical order:
17+
18+
```kotlin
19+
enum class CognitivePhase {
20+
PERCEIVE,
21+
RECALL,
22+
OBSERVE,
23+
PLAN,
24+
EXECUTE,
25+
LEARN,
26+
}
27+
```
28+
29+
Previously the enum carried only `PERCEIVE / PLAN / EXECUTE / LEARN`,
30+
silently dropping `RECALL` and `OBSERVE`. The acronym is now load-bearing:
31+
`enumValues<CognitivePhase>().toList()` yields the cycle in order.
32+
33+
**Migration for external consumers:**
34+
- Any `when (phase: CognitivePhase)` site without an `else` branch will
35+
fail to compile with an exhaustiveness error. Add explicit branches
36+
for `RECALL` and `OBSERVE`.
37+
- Code that iterated `CognitivePhase.entries` will now see six phases
38+
instead of four. Test matrices that assumed four-phase coverage will
39+
automatically extend; tests that hardcoded a four-element list need
40+
updating.
41+
- Declarative spark `.spark.md` frontmatter that previously enumerated
42+
`"phases": ["PERCEIVE", "PLAN", "EXECUTE", "LEARN"]` continues to
43+
parse, but the spark will not apply during `RECALL` or `OBSERVE`. If
44+
full coverage is intended, update the list to all six phases.
45+
- Serialized values are unchanged: existing `"PERCEIVE"` / `"PLAN"` /
46+
`"EXECUTE"` / `"LEARN"` strings still deserialize. AMPERE does not
47+
persist `CognitivePhase` across runs today, so no data migration is
48+
required.
49+
50+
- **CLI `AmperePhosphorBridge` removes the `LEARN → EVALUATE` paveover.**
51+
The bridge now uses `PhosphorPhase.RECALL` for AMPERE's `RECALL`,
52+
reuses the `PERCEIVE` height-pulse for `OBSERVE` (until Phosphor adds
53+
an `OBSERVE` ramp of its own), and continues to map `LEARN` to
54+
Phosphor's `EVALUATE` only as an interop bridge until Phosphor's enum
55+
is renamed in a sibling ticket. The `LEARN → EVALUATE` mapping is now
56+
documented as a known interop gap rather than a silent rename.
57+
58+
### Added
59+
60+
- `PhaseSpark.Recall` and `PhaseSpark.Observe` built-in sparks with
61+
default `promptContribution` strings tuned for memory-recall and
62+
state-monitoring behavior, respectively. `PhaseSpark.forPhase` covers
63+
all six members.
64+
65+
### Notes
66+
67+
- Phosphor's own `CognitivePhase` enum (in
68+
`link.socket.phosphor.signal.CognitivePhase`) still uses
69+
`PERCEIVE / RECALL / PLAN / EXECUTE / EVALUATE / LOOP / NONE` and is
70+
out of scope for this change. A sibling Phosphor ticket should add
71+
`OBSERVE` and rename `EVALUATE → LEARN` to bring Phosphor onto the
72+
canonical PROPEL acronym.
73+
74+
## [0.6.0] — 2026-05
75+
76+
Released; see `git log v0.6.0` for the commit history.

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,22 +165,22 @@ This allows you to steer the agent toward an informed decision in real-time, rat
165165
During each timestep of the environment simulation, each agent executes its own independent cognitive cycle:
166166

167167
```
168-
1. Perceive ──▶ 2. Recall ──▶ 3. Optimize
168+
1. Perceive ──▶ 2. Recall ──▶ 3. Observe
169169
170170
▲ │
171171
│ ▼
172172
173-
6. Loop ◀── 5. Execute ◀── 4. Plan
173+
6. Learn ◀── 5. Execute ◀── 4. Plan
174174
```
175175

176176
| # | Phase | Operation | Emitted Events |
177177
|---|----------------|-------------------------------------|----------------------------------------|
178178
| 1 | **(P)erceive** | Ingest signals from the environment | `SignalReceived`, `PerceptionFormed` |
179179
| 2 | **(R)ecall** | Query relevant memory and context | `MemoryQueried`, `ContextAssembled` |
180-
| 3 | **(O)ptimize** | Prioritize competing objectives | `ObjectivesRanked`, `ConfidenceScored` |
180+
| 3 | **(O)bserve** | Read current state and detect drift | `StateObserved`, `DriftDetected` |
181181
| 4 | **(P)lan** | Select and structure actions | `PlanCreated`, `TasksDecomposed` |
182182
| 5 | **(E)xecute** | Carry out the plan | `ActionTaken`, `ResultObserved` |
183-
| 6 | **(L)oop** | Evaluate results, re-enter cycle | `OutcomeEvaluated`, `CycleRestarted` |
183+
| 6 | **(L)earn** | Extract knowledge, re-enter cycle | `OutcomeEvaluated`, `KnowledgeStored` |
184184

185185
Every phase transition is emitted as an event, ensuring every action inside an agent can be audited and traced.
186186

STYLE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ Ampere's naming fuses biological cognition with electrical systems. Neural signa
7171
|------|----------|-------------------|
7272
| **Spark** | Context modifier shaping cognitive specialization | Action potential triggering neural differentiation |
7373
| **Arc** | Defined orchestration pathway | Electrical arc -- current flowing along a defined path |
74-
| **PROPEL Loop** | Cognitive cycle (Perceive, Recall, Optimize, Plan, Execute, Loop) | Neural firing cycle -- stimulus, integration, response, feedback |
74+
| **PROPEL Loop** | Cognitive cycle (Perceive, Recall, Observe, Plan, Execute, Learn) | Neural firing cycle -- stimulus, integration, response, feedback |
7575
| **EventSerializerBus** | Central nervous system carrying signals | Axon bundle -- impulses traveling between brain regions |
7676
| **Memory Cell** | Persistent knowledge with provenance | Biological memory encoded in synaptic weights |
7777
| **Escalation** | Uncertainty surfacing to a human | Pain signal -- requesting external intervention |

ampere-cli/src/jvmMain/kotlin/link/socket/ampere/cli/render/AmperePhosphorBridge.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,19 @@ class AmperePhosphorBridge(
4545

4646
private fun effectForPhase(phase: AmperePhase?): EmitterEffect = when (phase) {
4747
AmperePhase.PERCEIVE -> EmitterEffect.HeightPulse()
48+
AmperePhase.RECALL -> EmitterEffect.ColorWash(
49+
colorRamp = CognitiveColorRamp.forPhase(PhosphorPhase.RECALL)
50+
)
51+
// OBSERVE is sensing/state-monitoring — share the PERCEIVE visual until Phosphor adds OBSERVE.
52+
AmperePhase.OBSERVE -> EmitterEffect.HeightPulse()
4853
AmperePhase.PLAN -> EmitterEffect.ColorWash(
4954
colorRamp = CognitiveColorRamp.forPhase(PhosphorPhase.PLAN)
5055
)
5156
AmperePhase.EXECUTE -> EmitterEffect.SparkBurst(
5257
palette = AsciiLuminancePalette.EXECUTE
5358
)
59+
// Phosphor's enum still uses EVALUATE for the reflection phase that AMPERE calls LEARN.
60+
// Follow-up: file a Phosphor ticket to rename PhosphorPhase.EVALUATE -> LEARN.
5461
AmperePhase.LEARN -> EmitterEffect.ColorWash(
5562
colorRamp = CognitiveColorRamp.forPhase(PhosphorPhase.EVALUATE)
5663
)

ampere-cli/src/jvmTest/kotlin/link/socket/ampere/cli/render/AmperePhosphorBridgeTest.kt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,30 @@ class AmperePhosphorBridgeTest {
4747
)
4848
}
4949

50+
@Test
51+
fun `each PROPEL phase produces an emitter without crashing`() {
52+
// The phase enum became six-member in AMPR-172. Walking the enum verifies
53+
// that no `when (phase)` site in the bridge falls through silently and that
54+
// every phase yields exactly one emitter instance per call.
55+
CognitivePhase.entries.forEach { phase ->
56+
val emitterManager = EmitterManager()
57+
val bridge = AmperePhosphorBridge(emitterManager)
58+
val telemetry = ProviderCallTelemetrySummary(
59+
eventId = "evt-$phase",
60+
agentId = "agent-$phase",
61+
cognitivePhase = phase,
62+
latencyMs = 100,
63+
estimatedCost = null,
64+
totalTokens = null,
65+
success = true,
66+
)
67+
68+
bridge.onProviderCallCompleted(telemetry, Vector3.ZERO)
69+
70+
assertEquals(1, emitterManager.activeCount, "phase=$phase should emit once")
71+
}
72+
}
73+
5074
@Test
5175
fun `provider telemetry without cost still emits latency metadata`() {
5276
val emitterManager = EmitterManager()

ampere-core/src/commonMain/composeResources/files/sparks/code-agent.spark.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"id": "code-agent",
55
"name": "Code Agent",
66
"whenToUse": "tasks that write, read, modify, or commit code in a workspace, including code generation, refactoring, file edits, and surrounding git operations (branching, committing, pushing, opening PRs)",
7-
"phases": ["PERCEIVE", "PLAN", "EXECUTE", "LEARN"],
7+
"phases": ["PERCEIVE", "RECALL", "OBSERVE", "PLAN", "EXECUTE", "LEARN"],
88
"tags": ["code", "implementation"],
99
"agentRole": "Code Writer"
1010
}

ampere-core/src/commonMain/composeResources/files/sparks/product-agent.spark.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"id": "product-agent",
55
"name": "Product Agent",
66
"whenToUse": "tasks that break features into actionable backlog items, triage workloads, prioritise tickets, surface blocked work, and learn from past decomposition outcomes",
7-
"phases": ["PERCEIVE", "PLAN", "EXECUTE", "LEARN"],
7+
"phases": ["PERCEIVE", "RECALL", "OBSERVE", "PLAN", "EXECUTE", "LEARN"],
88
"tags": ["product", "planning", "backlog", "decomposition"],
99
"agentRole": "Product Manager"
1010
}

ampere-core/src/commonMain/composeResources/files/sparks/project-agent.spark.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"id": "project-agent",
55
"name": "Project Agent",
66
"whenToUse": "tasks that decompose a goal into a structured work breakdown, create or batch-update issues in an external tracker, assign tasks to agents, monitor epic progress, and escalate decisions that exceed an agent's authority",
7-
"phases": ["PERCEIVE", "PLAN", "EXECUTE", "LEARN"],
7+
"phases": ["PERCEIVE", "RECALL", "OBSERVE", "PLAN", "EXECUTE", "LEARN"],
88
"tags": ["project", "coordination", "decomposition", "escalation"],
99
"agentRole": "Project Manager"
1010
}

ampere-core/src/commonMain/composeResources/files/sparks/project-ampere.spark.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ AMPERE enables the creation of collaborative AI agent teams that can:
2020
- Coordinate through meetings and tickets
2121

2222
## Key Components
23-
- **Agents**: Autonomous entities with cognitive cycles (PERCEIVE → PLAN → EXECUTE → LEARN)
23+
- **Agents**: Autonomous entities with cognitive cycles (PERCEIVE → RECALL → OBSERVE → PLAN → EXECUTE → LEARN)
2424
- **Events**: Typed messages for agent communication (MessagePosted, TicketAssigned, etc.)
2525
- **Sparks**: Cognitive differentiation layers that specialize agent behavior
2626
- **Memory**: Knowledge persistence and recall for experiential learning

0 commit comments

Comments
 (0)