diff --git a/music21/duration.py b/music21/duration.py
index 6155728c1..051d1f7a3 100644
--- a/music21/duration.py
+++ b/music21/duration.py
@@ -1821,8 +1821,7 @@ class Duration(prebase.ProtoM21Object, SlottedObjectMixin):
         True
         '''
         newTuplet.frozen = True
-        self.tuplets = self.tuplets + (newTuplet,)
-        self.informClient()
+        self.tuplets = self._tuplets + (newTuplet,)
 
     def augmentOrDiminish(self, amountToScale, retainComponents=False):
         '''
@@ -2846,9 +2845,7 @@ class Duration(prebase.ProtoM21Object, SlottedObjectMixin):
     def tuplets(self) -> Tuple[Tuplet, ...]:
         '''
         Return a tuple of Tuplet objects.
-        May leave a stream containing objects having this duration
-        in an unusable state, requiring :meth:`~music21.stream.core.coreElementsChanged`
-        to be called. For this reason, prefer using :meth:`appendTuplet` to add tuplets.
+        Setting tuplets will inform the client (Note) that the duration has changed.
         '''
         if self._componentsNeedUpdating:
             self._updateComponents()
@@ -2859,6 +2856,7 @@ class Duration(prebase.ProtoM21Object, SlottedObjectMixin):
         # environLocal.printDebug(['assigning tuplets in Duration', tupletTuple])
         self._tuplets = tuple(tupletTuple)
         self._quarterLengthNeedsUpdating = True
+        self.informClient()
 
     def aggregateTupletMultiplier(self) -> OffsetQL:
         '''
@@ -3593,7 +3591,7 @@ class Test(unittest.TestCase):
         self.assertEqual(str(d.components),
                          "(DurationTuple(type='eighth', dots=0, quarterLength=0.5),)")
         self.assertFalse(d._componentsNeedUpdating)
-        self.assertTrue(d._quarterLengthNeedsUpdating)
+        self.assertFalse(d._quarterLengthNeedsUpdating)
         self.assertEqual(repr(d.quarterLength), 'Fraction(1, 3)')
         self.assertEqual(str(unitSpec(d)), "(Fraction(1, 3), 'eighth', 0, 3, 2, 'eighth')")
 
diff --git a/music21/harmony.py b/music21/harmony.py
index 20345c1bc..ae2f5ad6a 100644
--- a/music21/harmony.py
+++ b/music21/harmony.py
@@ -1387,8 +1387,8 @@ class ChordSymbol(Harmony):
     chords, by default appear as chord symbols in a score and have duration of
     0.
 
-    To obtain the chord representation of the in the score, change the
-    :attr:`music21.harmony.ChordSymbol.writeAsChord` to True. Unless otherwise
+    To obtain the chord representation of the `ChordSymbol` in the score, change
+    :attr:`~music21.harmony.Harmony.writeAsChord` to True. Unless otherwise
     specified, the duration of this chord object will become 1.0. If you have a
     leadsheet, run :meth:`music21.harmony.realizeChordSymbolDurations` on the
     stream to assign the correct (according to offsets) duration to each
diff --git a/music21/musicxml/xmlToM21.py b/music21/musicxml/xmlToM21.py
index 781f39705..e786eaf6b 100644
--- a/music21/musicxml/xmlToM21.py
+++ b/music21/musicxml/xmlToM21.py
@@ -1984,6 +1984,17 @@ class PartParser(XMLParserBase):
 
         sets self.lastMeasureWasShort to True or False if it is an incomplete measure
         that is not a pickup.
+
+        >>> m = stream.Measure([meter.TimeSignature('4/4'), harmony.ChordSymbol('C7')])
+        >>> m.highestTime
+        0.0
+        >>> PP = musicxml.xmlToM21.PartParser()
+        >>> PP.setLastMeasureInfo(m)
+        >>> PP.adjustTimeAttributesFromMeasure(m)
+        >>> m.highestTime
+        4.0
+        >>> PP.lastMeasureWasShort
+        False
         '''
         # note: we cannot assume that the time signature properly
         # describes the offsets w/n this bar. need to look at
@@ -1999,7 +2010,9 @@ class PartParser(XMLParserBase):
         if mHighestTime >= lastTimeSignatureQuarterLength:
             mOffsetShift = mHighestTime
 
-        elif mHighestTime == 0.0 and not m.recurse().notesAndRests:
+        elif (mHighestTime == 0.0
+              and not m.recurse().notesAndRests.getElementsNotOfClass('Harmony')
+              ):
             # this routine fixes a bug in PDFtoMusic and other MusicXML writers
             # that omit empty rests in a Measure.  It is a very quick test if
             # the measure has any notes.  Slower if it does not.
@@ -2007,6 +2020,7 @@ class PartParser(XMLParserBase):
             r.duration.quarterLength = lastTimeSignatureQuarterLength
             m.insert(0.0, r)
             mOffsetShift = lastTimeSignatureQuarterLength
+            self.lastMeasureWasShort = False
 
         else:  # use time signature
             # for the first measure, this may be a pickup

diff --git a/music21/stream/tests.py b/music21/stream/tests.py
index 3d1165e16..0d680c827 100644
--- a/music21/stream/tests.py
+++ b/music21/stream/tests.py
@@ -553,14 +553,15 @@ class Test(unittest.TestCase):
         a.append(n)
         self.assertEqual(a.duration.quarterLength, 1.0)
 
-        # Alter the note's duration in a nonstandard way
-        # Normally, one would call Duration.appendTuplet()
         tup = duration.Tuplet()
         n.duration.tuplets = (tup,)
-        # Nonstandard workflow requires coreElementsChanged() to be called
+        self.assertEqual(a.duration.quarterLength, Fraction(2, 3))
+
+        # Also (regression) test clearing the cache
         # https://github.com/cuthbertLab/music21/issues/957
+        n.duration.tuplets = (tup, tup)
         a.coreElementsChanged()
-        self.assertEqual(a.duration.quarterLength, Fraction(2, 3))
+        self.assertEqual(a.duration.quarterLength, Fraction(4, 9))
 
     def testMeasureStream(self):
         '''An approach to setting TimeSignature measures in offsets and durations

Tuplet setter doesn't inform client
Decided to document what I learned about the necessity of calling `coreElementsChanged()` when setting `.tuplets` directly.

Should spruce that up someday--there's not a good workaround if you're trying to *remove* tuplets, since `appendTuplet` only appends, and it breaks our principle that only core methods should require calling `coreElementsChanged()`.

_Originally posted by @jacobtylerwalls in https://github.com/cuthbertLab/music21/issues/958#issuecomment-818046530_

***

I see a few approaches:

- tuplet setter could inform the client instead of `appendTuplet` having to do it (I tried this and it broke a test, but the test may have been measuring the wrong thing, so it may unwravel some shoelaces that need to be unwraveled anyway)
- new method `removeTuplet` to address the gap in functionality
- clear tuplets in `Duration.clear()`

Not going to jump on this, doesn't seem urgent for v7.
music21/stream/tests.py::Test::testStreamDurationRecalculated
music21/stream/tests.py::Test::testAccumulatedTimeA music21/stream/tests.py::Test::testAccumulatedTimeB music21/stream/tests.py::Test::testActivateVariantsA music21/stream/tests.py::Test::testActivateVariantsB music21/stream/tests.py::Test::testActivateVariantsBySpanA music21/stream/tests.py::Test::testActivateVariantsBySpanB music21/stream/tests.py::Test::testActivateVariantsC music21/stream/tests.py::Test::testActivateVariantsD music21/stream/tests.py::Test::testActivateVariantsE music21/stream/tests.py::Test::testActiveSiteAfterBoolIteration music21/stream/tests.py::Test::testActiveSiteCopiedStreams music21/stream/tests.py::Test::testActiveSiteMangling music21/stream/tests.py::Test::testActiveSites music21/stream/tests.py::Test::testActiveSitesMultiple music21/stream/tests.py::Test::testAddSlurByMelisma music21/stream/tests.py::Test::testAnalyze music21/stream/tests.py::Test::testAppendFails music21/stream/tests.py::Test::testAugmentOrDiminishBasic music21/stream/tests.py::Test::testAugmentOrDiminishCorpus music21/stream/tests.py::Test::testAugmentOrDiminishHighestTimes music21/stream/tests.py::Test::testBestTimeSignature music21/stream/tests.py::Test::testChordifyA music21/stream/tests.py::Test::testChordifyB music21/stream/tests.py::Test::testChordifyC music21/stream/tests.py::Test::testChordifyD music21/stream/tests.py::Test::testChordifyE music21/stream/tests.py::Test::testChordifyF music21/stream/tests.py::Test::testChordifyG music21/stream/tests.py::Test::testChordifyImported music21/stream/tests.py::Test::testChordifyRests music21/stream/tests.py::Test::testChordifyTagPartA music21/stream/tests.py::Test::testChordifyTagPartB music21/stream/tests.py::Test::testClefs music21/stream/tests.py::Test::testContainerHierarchyA music21/stream/tests.py::Test::testContextNestedA music21/stream/tests.py::Test::testContextNestedB music21/stream/tests.py::Test::testContextNestedC music21/stream/tests.py::Test::testContextNestedD music21/stream/tests.py::Test::testDeepcopyActiveSite music21/stream/tests.py::Test::testDeepcopySpanners music21/stream/tests.py::Test::testDerivationA music21/stream/tests.py::Test::testDerivationB music21/stream/tests.py::Test::testDerivationC music21/stream/tests.py::Test::testDerivationMethodA music21/stream/tests.py::Test::testDoubleStreamPlacement music21/stream/tests.py::Test::testElements music21/stream/tests.py::Test::testElementsHighestTimeA music21/stream/tests.py::Test::testElementsHighestTimeB music21/stream/tests.py::Test::testElementsHighestTimeC music21/stream/tests.py::Test::testExtendDurationA music21/stream/tests.py::Test::testExtendTiesA music21/stream/tests.py::Test::testExtendTiesB music21/stream/tests.py::Test::testExtractedNoteAssignLyric music21/stream/tests.py::Test::testFinalBarlinePropertyA music21/stream/tests.py::Test::testFinalBarlinePropertyB music21/stream/tests.py::Test::testFindConsecutiveNotes music21/stream/tests.py::Test::testFindGaps music21/stream/tests.py::Test::testFlatCachingA music21/stream/tests.py::Test::testFlatCachingB music21/stream/tests.py::Test::testFlatCachingC music21/stream/tests.py::Test::testFlatSimple music21/stream/tests.py::Test::testFlattenUnnecessaryVoicesA music21/stream/tests.py::Test::testGetElementAfterElement music21/stream/tests.py::Test::testGetElementAtOrBeforeBarline music21/stream/tests.py::Test::testGetElementBeforeOffsetA music21/stream/tests.py::Test::testGetElementBeforeOffsetB music21/stream/tests.py::Test::testGetElementsByContextStream music21/stream/tests.py::Test::testGetElementsByOffsetZeroLength music21/stream/tests.py::Test::testGetInstrumentFromMxl music21/stream/tests.py::Test::testGetInstrumentManual music21/stream/tests.py::Test::testGetKeySignatures music21/stream/tests.py::Test::testGetKeySignaturesThreeMeasures music21/stream/tests.py::Test::testGetTimeSignatures music21/stream/tests.py::Test::testGetVariantsA music21/stream/tests.py::Test::testGraceChords music21/stream/tests.py::Test::testGracesInStream music21/stream/tests.py::Test::testHaveAccidentalsBeenMadeA music21/stream/tests.py::Test::testHaveAccidentalsBeenMadeB music21/stream/tests.py::Test::testHaveAccidentalsBeenMadeInVoices music21/stream/tests.py::Test::testHaveBeamsBeenMadeA music21/stream/tests.py::Test::testHaveBeamsBeenMadeB music21/stream/tests.py::Test::testInsertAndShiftBasic music21/stream/tests.py::Test::testInsertAndShiftMultipleElements music21/stream/tests.py::Test::testInsertAndShiftNoDuration music21/stream/tests.py::Test::testInsertIntoNoteOrChordA music21/stream/tests.py::Test::testInsertIntoNoteOrChordB music21/stream/tests.py::Test::testInvertDiatonicA music21/stream/tests.py::Test::testInvertDiatonicQuickSearch music21/stream/tests.py::Test::testIsFlat music21/stream/tests.py::Test::testIteration music21/stream/tests.py::Test::testMakeAccidentalsA music21/stream/tests.py::Test::testMakeAccidentalsB music21/stream/tests.py::Test::testMakeAccidentalsC music21/stream/tests.py::Test::testMakeAccidentalsD music21/stream/tests.py::Test::testMakeAccidentalsOctaveKS music21/stream/tests.py::Test::testMakeAccidentalsTies music21/stream/tests.py::Test::testMakeAccidentalsWithKeysInMeasures music21/stream/tests.py::Test::testMakeChordsBuiltA music21/stream/tests.py::Test::testMakeChordsBuiltB music21/stream/tests.py::Test::testMakeChordsBuiltC music21/stream/tests.py::Test::testMakeChordsBuiltD music21/stream/tests.py::Test::testMakeChordsImported music21/stream/tests.py::Test::testMakeMeasuresInPlace music21/stream/tests.py::Test::testMakeMeasuresLastElementNoDuration music21/stream/tests.py::Test::testMakeMeasuresMeterStream music21/stream/tests.py::Test::testMakeMeasuresTimeSignatures music21/stream/tests.py::Test::testMakeMeasuresWithBarlines music21/stream/tests.py::Test::testMakeNotationA music21/stream/tests.py::Test::testMakeNotationB music21/stream/tests.py::Test::testMakeNotationByMeasuresA music21/stream/tests.py::Test::testMakeNotationByMeasuresB music21/stream/tests.py::Test::testMakeNotationC music21/stream/tests.py::Test::testMakeNotationKeySignatureMultiVoice music21/stream/tests.py::Test::testMakeNotationKeySignatureOneVoice music21/stream/tests.py::Test::testMakeNotationScoreA music21/stream/tests.py::Test::testMakeNotationScoreB music21/stream/tests.py::Test::testMakeNotationScoreC music21/stream/tests.py::Test::testMakeRestsA music21/stream/tests.py::Test::testMakeRestsB music21/stream/tests.py::Test::testMakeTies music21/stream/tests.py::Test::testMakeTupletBracketsA music21/stream/tests.py::Test::testMakeTupletBracketsB music21/stream/tests.py::Test::testMakeVoicesA music21/stream/tests.py::Test::testMakeVoicesB music21/stream/tests.py::Test::testMeasureAndTieCreation music21/stream/tests.py::Test::testMeasureBarDurationProportion music21/stream/tests.py::Test::testMeasureBarline music21/stream/tests.py::Test::testMeasureGrouping music21/stream/tests.py::Test::testMeasureLayout music21/stream/tests.py::Test::testMeasureOffsetMap music21/stream/tests.py::Test::testMeasureOffsetMapPostTie music21/stream/tests.py::Test::testMeasureRange music21/stream/tests.py::Test::testMeasureStream music21/stream/tests.py::Test::testMeasuresA music21/stream/tests.py::Test::testMeasuresAndMakeMeasures music21/stream/tests.py::Test::testMeasuresB music21/stream/tests.py::Test::testMeasuresC music21/stream/tests.py::Test::testMeasuresSuffix music21/stream/tests.py::Test::testMelodicIntervals music21/stream/tests.py::Test::testMelodicIntervalsB music21/stream/tests.py::Test::testMergeElements music21/stream/tests.py::Test::testMetadataOnStream music21/stream/tests.py::Test::testMidiEventsBuilt music21/stream/tests.py::Test::testMultipartStream music21/stream/tests.py::Test::testMusicXMLGenerationViaPropertyA music21/stream/tests.py::Test::testMusicXMLGenerationViaPropertyB music21/stream/tests.py::Test::testMusicXMLGenerationViaPropertyC music21/stream/tests.py::Test::testOpusSearch music21/stream/tests.py::Test::testOpusWrite music21/stream/tests.py::Test::testOverlapsA music21/stream/tests.py::Test::testOverlapsB music21/stream/tests.py::Test::testPartDurationA music21/stream/tests.py::Test::testPartDurationB music21/stream/tests.py::Test::testPartsToVoicesA music21/stream/tests.py::Test::testPartsToVoicesB music21/stream/tests.py::Test::testQuantize music21/stream/tests.py::Test::testQuantizeMinimumDuration music21/stream/tests.py::Test::testRecurseA music21/stream/tests.py::Test::testRecurseB music21/stream/tests.py::Test::testRemove music21/stream/tests.py::Test::testRemoveByClass music21/stream/tests.py::Test::testReplace music21/stream/tests.py::Test::testReplaceA1 music21/stream/tests.py::Test::testReplaceB music21/stream/tests.py::Test::testReplaceDerived music21/stream/tests.py::Test::testScaleDurationsBasic music21/stream/tests.py::Test::testScaleOffsetsBasic music21/stream/tests.py::Test::testScaleOffsetsBasicInPlaceA music21/stream/tests.py::Test::testScaleOffsetsBasicInPlaceB music21/stream/tests.py::Test::testScaleOffsetsBasicInPlaceC music21/stream/tests.py::Test::testScaleOffsetsBasicInPlaceD music21/stream/tests.py::Test::testScaleOffsetsNested music21/stream/tests.py::Test::testScoreShowA music21/stream/tests.py::Test::testSecondsMapA music21/stream/tests.py::Test::testSecondsMapB music21/stream/tests.py::Test::testSecondsPropertyA music21/stream/tests.py::Test::testSecondsPropertyB music21/stream/tests.py::Test::testSecondsPropertyC music21/stream/tests.py::Test::testSemiFlatCachingA music21/stream/tests.py::Test::testSetElements music21/stream/tests.py::Test::testSetElementsFromOtherStreamWithEndElements music21/stream/tests.py::Test::testSimpleRecurse music21/stream/tests.py::Test::testSliceAtOffsetsBuilt music21/stream/tests.py::Test::testSliceAtOffsetsImported music21/stream/tests.py::Test::testSliceAtOffsetsSimple music21/stream/tests.py::Test::testSliceByBeatBuilt music21/stream/tests.py::Test::testSliceByBeatImported music21/stream/tests.py::Test::testSliceByGreatestDivisorBuilt music21/stream/tests.py::Test::testSliceByGreatestDivisorImported music21/stream/tests.py::Test::testSliceByQuarterLengthsBuilt music21/stream/tests.py::Test::testSliceByQuarterLengthsImported music21/stream/tests.py::Test::testSort music21/stream/tests.py::Test::testSortAndAutoSort music21/stream/tests.py::Test::testSortingAfterInsertA music21/stream/tests.py::Test::testSpannerTransferA music21/stream/tests.py::Test::testSplitAtQuarterLengthA music21/stream/tests.py::Test::testSplitAtQuarterLengthB music21/stream/tests.py::Test::testSplitAtQuarterLengthC music21/stream/tests.py::Test::testStoreAtEndFailures music21/stream/tests.py::Test::testStreamCopy music21/stream/tests.py::Test::testStreamDuration music21/stream/tests.py::Test::testStreamElementsComparison music21/stream/tests.py::Test::testStreamExceptionsOnAssert music21/stream/tests.py::Test::testStreamRecursion music21/stream/tests.py::Test::testStreamSortRecursion music21/stream/tests.py::Test::testStripTiesBuiltA music21/stream/tests.py::Test::testStripTiesBuiltB music21/stream/tests.py::Test::testStripTiesChordMembersAllTied music21/stream/tests.py::Test::testStripTiesChordMembersSomeTied music21/stream/tests.py::Test::testStripTiesChords music21/stream/tests.py::Test::testStripTiesConsecutiveInVoiceNotContainer music21/stream/tests.py::Test::testStripTiesImportedA music21/stream/tests.py::Test::testStripTiesImportedB music21/stream/tests.py::Test::testStripTiesNonMeasureContainers music21/stream/tests.py::Test::testStripTiesReplaceSpannedElements music21/stream/tests.py::Test::testStripTiesScore music21/stream/tests.py::Test::testTemplateAll music21/stream/tests.py::Test::testTransposeByPitchA music21/stream/tests.py::Test::testTransposeByPitchB music21/stream/tests.py::Test::testTransposeScore music21/stream/tests.py::Test::testTwoStreamMethods music21/stream/tests.py::Test::testTwoZeroOffset music21/stream/tests.py::Test::testVoicesA music21/stream/tests.py::Test::testVoicesALonger music21/stream/tests.py::Test::testVoicesB music21/stream/tests.py::Test::testVoicesC music21/stream/tests.py::Test::testVoicesToPartsA music21/stream/tests.py::Test::testVoicesToPartsNonNoteElementPropagation music21/stream/tests.py::Test::testYieldContainers music21/stream/tests.py::Test::test_makeBeams__1_e__after_16th_note music21/stream/tests.py::Test::test_makeBeams__1_e_n_a music21/stream/tests.py::Test::test_makeBeams__all_eighths music21/stream/tests.py::Test::test_makeBeams__all_quarters music21/stream/tests.py::Test::test_makeBeams__eighth_rests_and_eighth music21/stream/tests.py::Test::test_makeBeams__paddingLeft_2_2 music21/stream/tests.py::Test::test_makeBeams__repeated_1_e_a