Index: drools-planner/drools-planner-examples/src/main/java/org/drools/planner/examples/curriculumcourse/domain/Lecture.java
===================================================================
--- drools-planner/drools-planner-examples/src/main/java/org/drools/planner/examples/curriculumcourse/domain/Lecture.java	(revision ed174b3293dc8410d42e92650c9df001ffb97442)
+++ drools-planner/drools-planner-examples/src/main/java/org/drools/planner/examples/curriculumcourse/domain/Lecture.java	(revision )
@@ -141,7 +141,7 @@
 
     @Override
     public String toString() {
-        return course + "-" + lectureIndexInCourse + " @ " + period + " + " + room;
+        return id + "(" + course + "-" + lectureIndexInCourse + " @ " + period + " + " + room + ")";
     }
 
 }
Index: drools-planner/drools-planner-examples/src/test/resources/org/drools/planner/examples/curriculumcourse/solver/curriculumCourseScoreRules_jbrules2809.drl
===================================================================
--- drools-planner/drools-planner-examples/src/test/resources/org/drools/planner/examples/curriculumcourse/solver/curriculumCourseScoreRules_jbrules2809.drl	(revision )
+++ drools-planner/drools-planner-examples/src/test/resources/org/drools/planner/examples/curriculumcourse/solver/curriculumCourseScoreRules_jbrules2809.drl	(revision )
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2010 JBoss Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.drools.planner.examples.curriculumcourse.solver;
+    dialect "java"
+
+import org.drools.planner.core.score.calculator.HardAndSoftConstraintScoreCalculator;
+import org.drools.planner.core.score.constraint.IntConstraintOccurrence;
+import org.drools.planner.core.score.constraint.ConstraintType;
+
+import org.drools.planner.examples.curriculumcourse.domain.Course;
+import org.drools.planner.examples.curriculumcourse.domain.Curriculum;
+import org.drools.planner.examples.curriculumcourse.domain.CurriculumCourseSchedule;
+import org.drools.planner.examples.curriculumcourse.domain.Day;
+import org.drools.planner.examples.curriculumcourse.domain.Lecture;
+import org.drools.planner.examples.curriculumcourse.domain.Period;
+import org.drools.planner.examples.curriculumcourse.domain.Room;
+import org.drools.planner.examples.curriculumcourse.domain.Teacher;
+import org.drools.planner.examples.curriculumcourse.domain.Timeslot;
+import org.drools.planner.examples.curriculumcourse.domain.UnavailablePeriodConstraint;
+
+global HardAndSoftConstraintScoreCalculator scoreCalculator;
+
+// ############################################################################
+// Soft constraints
+// ############################################################################
+
+// CurriculumCompactness: Lectures belonging to a curriculum should be adjacent
+// to each other (i.e., in consecutive periods).
+// For a given curriculum we account for a violation every time there is one lecture not adjacent
+// to any other lecture within the same day.
+// Each isolated lecture in a curriculum counts as 2 points of penalty.
+rule "curriculumCompactness"
+    when
+        $curriculum : Curriculum()
+
+        $lecture : Lecture(curriculumList contains $curriculum,
+            $day : day, $timeslotIndex : timeslotIndex
+        )
+        not Lecture(curriculumList contains $curriculum,
+            day == $day, timeslotIndex == ($timeslotIndex - 1)
+        )
+        not Lecture(curriculumList contains $curriculum,
+            day == $day, timeslotIndex == ($timeslotIndex + 1)
+        )
+    then
+        insertLogical(new IntConstraintOccurrence("curriculumCompactness", ConstraintType.NEGATIVE_SOFT,
+                2,
+                $lecture, $curriculum));
+end
+
+// ############################################################################
+// Calculate score
+// ############################################################################
+
+// Accumulate soft constraints
+rule "softConstraintsBroken"
+        salience -1 // Do the other rules first (optional, for performance)
+    when
+        $softTotal : Number() from accumulate(
+            IntConstraintOccurrence(constraintType == ConstraintType.NEGATIVE_SOFT, $weight : weight),
+            sum($weight) // Vote for http://jira.jboss.com/jira/browse/JBRULES-1075
+        )
+    then
+        scoreCalculator.setSoftConstraintsBroken($softTotal.intValue());
+end
Index: drools-planner/drools-planner-examples/src/test/java/org/drools/planner/examples/curriculumcourse/Jbrules2809Test.java
===================================================================
--- drools-planner/drools-planner-examples/src/test/java/org/drools/planner/examples/curriculumcourse/Jbrules2809Test.java	(revision )
+++ drools-planner/drools-planner-examples/src/test/java/org/drools/planner/examples/curriculumcourse/Jbrules2809Test.java	(revision )
@@ -0,0 +1,230 @@
+/*
+ * Copyright 2011 JBoss Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.drools.planner.examples.curriculumcourse;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.apache.commons.io.IOUtils;
+import org.drools.ClassObjectFilter;
+import org.drools.RuleBase;
+import org.drools.RuleBaseConfiguration;
+import org.drools.RuleBaseFactory;
+import org.drools.StatefulSession;
+import org.drools.WorkingMemory;
+import org.drools.compiler.DroolsParserException;
+import org.drools.compiler.PackageBuilder;
+import org.drools.planner.core.score.Score;
+import org.drools.planner.core.score.calculator.DefaultHardAndSoftConstraintScoreCalculator;
+import org.drools.planner.core.score.calculator.HardAndSoftConstraintScoreCalculator;
+import org.drools.planner.core.score.calculator.ScoreCalculator;
+import org.drools.planner.core.score.constraint.ConstraintOccurrence;
+import org.drools.planner.core.score.constraint.IntConstraintOccurrence;
+import org.drools.planner.examples.common.app.LoggingTest;
+import org.drools.planner.examples.curriculumcourse.domain.CurriculumCourseSchedule;
+import org.drools.planner.examples.curriculumcourse.domain.Lecture;
+import org.drools.planner.examples.curriculumcourse.domain.Period;
+import org.drools.planner.examples.curriculumcourse.persistence.CurriculumCourseDaoImpl;
+import org.drools.runtime.rule.FactHandle;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author Geoffrey De Smet
+ */
+public class Jbrules2809Test extends LoggingTest {
+
+    private static final String DRL_FILE = "/org/drools/planner/examples/curriculumcourse/solver/curriculumCourseScoreRules_jbrules2809.drl";
+    private static final String DATA_FILE = "/org/drools/planner/examples/curriculumcourse/data/testComp01_jbrules2809.xml";
+    
+    protected final transient Logger logger = LoggerFactory.getLogger(getClass());
+
+    protected RuleBase ruleBase;
+    protected HardAndSoftConstraintScoreCalculator workingScoreCalculator;
+    protected WorkingMemory workingMemory;
+
+    @Test
+    public void solveComp01_JBRULES2809() {
+        ruleBase = buildRuleBase();
+        workingMemory = ruleBase.newStatefulSession();
+
+        workingScoreCalculator = new DefaultHardAndSoftConstraintScoreCalculator();
+        workingMemory.setGlobal("scoreCalculator", workingScoreCalculator);
+
+        CurriculumCourseSchedule curriculumCourseSchedule = (CurriculumCourseSchedule) new CurriculumCourseDaoImpl().readSolution(getClass().getResourceAsStream(
+                DATA_FILE));
+        for (Object fact : curriculumCourseSchedule.getFacts()) {
+            workingMemory.insert(fact);
+        }
+
+        Score score;
+        workingMemory.fireAllRules();
+        score = workingScoreCalculator.calculateScore();
+
+
+        Lecture lec94 = findLectureById(curriculumCourseSchedule, 94L);
+        FactHandle lec94Handle = workingMemory.getFactHandle(lec94);
+        Period lec94Period = lec94.getPeriod();
+
+        Lecture lec112 = findLectureById(curriculumCourseSchedule, 112L);
+        FactHandle lec112Handle = workingMemory.getFactHandle(lec112);
+        Period lec112Period = lec112.getPeriod();
+
+        // Do
+        lec94.setPeriod(lec112Period);
+        workingMemory.update(lec94Handle, lec94);
+        lec112.setPeriod(lec94Period);
+        workingMemory.update(lec112Handle, lec112);
+
+        // Undo
+        lec94.setPeriod(lec94Period);
+        workingMemory.update(lec94Handle, lec94);
+        lec112.setPeriod(lec112Period);
+        workingMemory.update(lec112Handle, lec112);
+
+
+        Lecture lec89 = findLectureById(curriculumCourseSchedule, 89L);
+        FactHandle lec89Handle = workingMemory.getFactHandle(lec89);
+        Period lec89Period = lec89.getPeriod();
+
+        Lecture lec96 = findLectureById(curriculumCourseSchedule, 96L);
+        FactHandle lec96Handle = workingMemory.getFactHandle(lec96);
+        Period lec96Period = lec96.getPeriod();
+
+        // Do
+        lec89.setPeriod(lec96Period);
+        workingMemory.update(lec89Handle, lec89);
+        lec96.setPeriod(lec89Period);
+        workingMemory.update(lec96Handle, lec96);
+
+        // Undo
+        lec89.setPeriod(lec89Period);
+        workingMemory.update(lec89Handle, lec89);
+        lec96.setPeriod(lec96Period);
+        workingMemory.update(lec96Handle, lec96);
+
+        // These 3 lines are green: at this point the WM is corrupted but it's not yet detectable
+//        workingMemory.fireAllRules();
+//        score = workingScoreCalculator.calculateScore();
+//        assertWorkingScore(curriculumCourseSchedule, score);
+
+        Lecture lec122 = findLectureById(curriculumCourseSchedule, 122L);
+        FactHandle lec122Handle = workingMemory.getFactHandle(lec122);
+        Period lec122Period = lec122.getPeriod();
+
+        // Do
+        lec96.setPeriod(lec122Period);
+        workingMemory.update(lec96Handle, lec96);
+        lec122.setPeriod(lec96Period);
+        workingMemory.update(lec122Handle, lec122);
+
+        workingMemory.fireAllRules();
+        score = workingScoreCalculator.calculateScore();
+
+        // This constraint occurrence should have been retracted, but isn't:
+        // ConstraintOccurrence (curriculumCompactness/NEGATIVE_SOFT:[97(course_26{null}-0 @ 2-4 + null), curriculum_11]=2)
+        assertWorkingScore(curriculumCourseSchedule, score);
+    }
+
+    private RuleBase buildRuleBase() {
+        PackageBuilder packageBuilder = new PackageBuilder();
+        InputStream scoreDrlIn = getClass().getResourceAsStream(DRL_FILE);
+        try {
+            packageBuilder.addPackageFromDrl(new InputStreamReader(scoreDrlIn, "utf-8"));
+        } catch (DroolsParserException e) {
+            throw new IllegalArgumentException("scoreDrl could not be loaded.", e);
+        } catch (IOException e) {
+            throw new IllegalArgumentException("scoreDrl could not be loaded.", e);
+        } finally {
+            IOUtils.closeQuietly(scoreDrlIn);
+        }
+        RuleBaseConfiguration ruleBaseConfiguration = new RuleBaseConfiguration();
+        RuleBase ruleBase = RuleBaseFactory.newRuleBase(ruleBaseConfiguration);
+        if (packageBuilder.hasErrors()) {
+            throw new IllegalStateException("There are errors in the scoreDrl:"
+                    + packageBuilder.getErrors().toString());
+        }
+        ruleBase.addPackage(packageBuilder.getPackage());
+        return ruleBase;
+    }
+
+    private Lecture findLectureById(CurriculumCourseSchedule curriculumCourseSchedule, long id) {
+        for (Lecture lecture : curriculumCourseSchedule.getLectureList()) {
+            if (lecture.getId() == id) {
+                return lecture;
+            }
+        }
+        throw new IllegalArgumentException("Invalid id (" + id + ")");
+    }
+
+    /**
+     * @param presumedScore never null
+     */
+    public void assertWorkingScore(CurriculumCourseSchedule curriculumCourseSchedule, Score presumedScore) {
+        StatefulSession tmpWorkingMemory = ruleBase.newStatefulSession();
+        ScoreCalculator tmpScoreCalculator = workingScoreCalculator.clone();
+        tmpWorkingMemory.setGlobal("scoreCalculator", tmpScoreCalculator);
+        for (Object fact : curriculumCourseSchedule.getFacts()) {
+            tmpWorkingMemory.insert(fact);
+        }
+        tmpWorkingMemory.fireAllRules();
+        Score realScore = tmpScoreCalculator.calculateScore();
+        tmpWorkingMemory.dispose();
+        if (!presumedScore.equals(realScore)) {
+            throw new IllegalStateException(
+                    "The presumedScore (" + presumedScore + ") is corrupted because it is not the realScore  ("
+                            + realScore + ").\n"
+                            + "Presumed workingMemory:\n" + buildConstraintOccurrenceSummary(workingMemory)
+                            + "Real workingMemory:\n" + buildConstraintOccurrenceSummary(tmpWorkingMemory));
+        }
+    }
+
+    /**
+     * TODO Refactor this with the ConstraintOccurrenceTotal class: https://jira.jboss.org/jira/browse/JBRULES-2510
+     * @param summaryWorkingMemory sometimes null
+     * @return never null
+     */
+    public String buildConstraintOccurrenceSummary(WorkingMemory summaryWorkingMemory) {
+        logger.trace("Building ConstraintOccurrence summary");
+        if (summaryWorkingMemory == null) {
+            return "  The workingMemory is null.";
+        }
+        Map<String, Number> scoreTotalMap = new TreeMap<String, Number>();
+        Iterator<ConstraintOccurrence> it = (Iterator<ConstraintOccurrence>) summaryWorkingMemory.iterateObjects(
+                new ClassObjectFilter(ConstraintOccurrence.class));
+        while (it.hasNext()) {
+            ConstraintOccurrence occurrence = it.next();
+            logger.trace("Adding ConstraintOccurrence ({})", occurrence);
+            Number scoreTotalNumber = scoreTotalMap.get(occurrence.getRuleId());
+                int scoreTotal = scoreTotalNumber == null ? 0 : (Integer) scoreTotalNumber;
+                scoreTotal += ((IntConstraintOccurrence) occurrence).getWeight();
+                scoreTotalMap.put(occurrence.getRuleId(), scoreTotal);
+        }
+        StringBuilder summary = new StringBuilder();
+        for (Map.Entry<String, Number> scoreTotalEntry : scoreTotalMap.entrySet()) {
+            summary.append("  Score rule (").append(scoreTotalEntry.getKey()).append(") has score total (")
+                    .append(scoreTotalEntry.getValue()).append(").\n");
+        }
+        return summary.toString();
+    }
+
+}
Index: drools-planner/drools-planner-examples/src/test/resources/org/drools/planner/examples/curriculumcourse/data/testComp01_jbrules2809.xml
===================================================================
--- drools-planner/drools-planner-examples/src/test/resources/org/drools/planner/examples/curriculumcourse/data/testComp01_jbrules2809.xml	(revision )
+++ drools-planner/drools-planner-examples/src/test/resources/org/drools/planner/examples/curriculumcourse/data/testComp01_jbrules2809.xml	(revision )
@@ -0,0 +1,140 @@
+<CurriculumCourseSchedule id="1">
+  <id>0</id>
+  <name>Fis0506-1</name>
+  <teacherList id="2">
+  </teacherList>
+  <curriculumList id="27">
+    <Curriculum id="28">
+      <id>0</id>
+      <code>curriculum_0</code>
+    </Curriculum>
+    <Curriculum id="33">
+      <id>5</id>
+      <code>curriculum_5</code>
+    </Curriculum>
+    <Curriculum id="36">
+      <id>8</id>
+      <code>curriculum_8</code>
+    </Curriculum>
+    <Curriculum id="39">
+      <id>11</id>
+      <code>curriculum_11</code>
+    </Curriculum>
+  </curriculumList>
+  <courseList id="42">
+    <Course id="45">
+      <id>1</id>
+      <code>course_1</code>
+      <curriculumList id="46">
+        <Curriculum reference="28"/>
+      </curriculumList>
+    </Course>
+    <Course id="87">
+      <id>22</id>
+      <code>course_22</code>
+      <curriculumList id="88">
+        <Curriculum reference="36"/>
+      </curriculumList>
+    </Course>
+    <Course id="95">
+      <id>26</id>
+      <code>course_26</code>
+      <curriculumList id="96">
+        <Curriculum reference="39"/>
+      </curriculumList>
+    </Course>
+    <Course id="97">
+      <id>27</id>
+      <code>course_27</code>
+      <curriculumList id="98">
+        <Curriculum reference="33"/>
+      </curriculumList>
+    </Course>
+  </courseList>
+  <dayList id="103">
+    <Day id="106">
+      <id>2</id>
+      <dayIndex>2</dayIndex>
+    </Day>
+    <Day id="107">
+      <id>3</id>
+      <dayIndex>3</dayIndex>
+    </Day>
+  </dayList>
+  <timeslotList id="109">
+    <Timeslot id="110">
+      <id>0</id>
+      <timeslotIndex>0</timeslotIndex>
+    </Timeslot>
+    <Timeslot id="112">
+      <id>2</id>
+      <timeslotIndex>2</timeslotIndex>
+    </Timeslot>
+    <Timeslot id="113">
+      <id>3</id>
+      <timeslotIndex>3</timeslotIndex>
+    </Timeslot>
+    <Timeslot id="114">
+      <id>4</id>
+      <timeslotIndex>4</timeslotIndex>
+    </Timeslot>
+  </timeslotList>
+  <periodList id="116">
+    <Period id="132">
+      <id>15</id>
+      <day reference="106"/>
+      <timeslot reference="113"/>
+    </Period>
+    <Period id="133">
+      <id>16</id>
+      <day reference="106"/>
+      <timeslot reference="114"/>
+    </Period>
+    <Period id="135">
+      <id>18</id>
+      <day reference="107"/>
+      <timeslot reference="110"/>
+    </Period>
+    <Period id="137">
+      <id>20</id>
+      <day reference="107"/>
+      <timeslot reference="112"/>
+    </Period>
+  </periodList>
+  <roomList id="147">
+  </roomList>
+  <unavailablePeriodConstraintList id="154">
+  </unavailablePeriodConstraintList>
+  <lectureList id="208">
+    <Lecture id="298">
+      <id>89</id>
+      <course reference="45"/>
+      <period reference="132"/>
+    </Lecture>
+    <Lecture id="303">
+      <id>94</id>
+      <course reference="95"/>
+      <period reference="132"/>
+    </Lecture>
+    <Lecture id="305">
+      <id>96</id>
+      <course reference="95"/>
+      <period reference="132"/>
+    </Lecture>
+    <Lecture id="306">
+      <id>97</id>
+      <course reference="95"/>
+      <period reference="133"/>
+    </Lecture>
+    <Lecture id="321">
+      <id>112</id>
+      <course reference="87"/>
+      <period reference="135"/>
+    </Lecture>
+    <Lecture id="331">
+      <id>122</id>
+      <course reference="97"/>
+      <period reference="137"/>
+    </Lecture>
+  </lectureList>
+</CurriculumCourseSchedule>
