Groups, variables, and missing dates

I am having trouble understanding correct behaviour with groups and missing dates.

As I understood it, a group that makes an attempt to render variables which are all empty is supposed to produce nothing: anything it would have rendered en route is to be abandoned.

The first problem I encountered was with an understandably common pattern where a group is defined along these lines

<group>
  <choose>
    <if variable="issued">
    ... render date ...
    </if>
    <else>
      <text term="no date"/>
    </else>
  </choose>
</group>

In one sense that “attempts” to render the issued variable; but obviously it wants to produce the no date term if that fails, and obviously it should render. OK, I thought, the logic is that there is a flow path within which no attempt was really made to render a date variable: the first conditional fails, so that the only attempt to render anything in the group ends up being a term (not a variable) and therefore the group should print.

I therefore modified the logic of the processor so that it tracks whether any attempt has been made to render a variable (name, number, or other): if no attempt is made then any sort of rendering (including value or text) counts as success; if some attempt is made, then at least one such attempt must have succeeded for the whole group not to be discarded. So effectively groups have three states: “didn’t try to render a variable”, “tried at least once and always failed”, and “tried at least once and succeeded at least once”. The first and third lead to output, but the second causes the effort to be abandoned. This caters for the problem set out above.

But now consider this (roughly from bugreports_UndefinedStr.txt), inlining a macro

<group delimiter="; ">
  <choose>
    <if variable="issued">
    .. render date ..
    </if>
    <else>
    .. print "no date" term ..
    </else>
  </choose>
  <text variable="foo" />
</group>

Now in this case the group certainly does attempt to render at least one variable (foo) which (let us suppose) fails. Clearly it is supposed to print the no date term, as the test result shows. But what is the logic of this? That term is not a variable, and it doesn’t print it as part of rendering a variable, but indeed because there isn’t a variable to render. So, it looks as if this is a group that does attempt to render at least one variable, but fails.

My processor is religiously following instructions, and declining to print anything so long at the second <text variable/> is in the group : it (correctly I suppose, in a stupid computer sense of the term correct) sees this as if it were the same as

<group>
  <text term="foo-term" suffix=": "/>
  <text variable="foo"/>
</group>

Which, as I understand it, should not produce any output if foo is empty.

Clearly this is silly in human terms, though it has a plausible justification in strict logic; but exactly how do I adjust the logic to make it common sense? There seem to be four possibilities (maybe more I haven’t thought of):

  • Treat choose as if it were a variable so if it prints something then that counts as “success” sufficient to warrant printing the group.
  • Treat the no date term as a special case
  • Treat a term as if it were a variable.
  • Treat any output other than prefixes and suffixes as “success”.
  • As previous, but only if the output precedes the attempt to render a variable.

As far as I can see the third to fifth options are wrong: if I move the logic in this direction I end up with previously passing tests starting to fail, for reasons that make sense to me, because terms and so forth will often be wanted iff there is some variable printed to which they relate. I’m tempted by the first, as the most general, but I’d like to do the right thing here, and since tracking success involves quite a bit of weaving values through multiple levels, I’d sooner do it better once than work by trial and error.

It’s near my bedtime, so treat this post with caution, but I think I see the logic of that construct clearly so here goes.

Macros are implicit group wrappers in their own right, so the choose block in your second example is rendering a bare term with no attempt to render a variable. That’s confirmed output, which the group returns to the parent (the group wrapper in the second example). The foo attempt fails, but the “no date” term is not dependent on it (because it’s already confirmed).

From a style author’s viewpoint, treating macros as implicit groups is friendly, because everything you need to think about is in the code block in front of you—macro calls render or they don’t, and variables will pull down the companion terms that you can see in the block if they all fail.

(Having written that, I sense a question over what happens if a macro returns with a lone string or term, but to a group with a failing variable and a sibling term—what happens with the sibling term? I’m pretty sure that the macro return is treated as equivalent to a succeeding variable, and that you would get the sibling term in output.

I’m at risk of not making sense, so I should wait until tomorrow to dig into it, but in citeproc-js there is a set of named parameters for tracking group state that will be helpful in clarifying the logic, and maybe useful as an example.

Sorry to keep you up late. That makes sense. I was ignoring the fact that macros are implicit group parameters in their own right, I think. That’s actually a pretty easy fix. I just set a sentinel on a macro as I would on a group. Who knows what other failing tests that will unfail!

I wasn’t woken up by your post! My catchword for the large and small traumas of coding a CSL processor is “everything-related-to-everything-else-ness.” It beats depression and anxiety, but not my much. :slight_smile:

In citeproc-rs, a group that does not end up rendering anything will produce a NoneSeen (did not attempt to render variables) state for any parent group to analyse, rather than OnlyEmpty (tried but didn’t render any, which is what actually happened). This presents an opportunity to constrain the ‘conditional rendering’ regime with an extra inner <group>.

As far as I know, this is in line with the spec. It says (emphasis mine):

cs:group implicitly acts as a conditional: cs:group and its child elements are suppressed if a) at least one rendering element in cs:group calls a variable (either directly or via a macro), and b) all variables that are called are empty.

Interpreting “directly” as “not through an inner group”, citeproc-rs’ behaviour is correct. And if you do interpret “directly” that way, which I think we should, then “via a macro” would appear to forbid treating macros like implicit groups. Edit: it would only forbid doing the OnlyEmpty => NoneSeen transform on macro output, not treating them as implicit groups.

Here’s how it works:

<group delimiter=" ">
  <choose>
    <if variable="title">
      <text variable="title" />
    </if>
    <else>
      <text value="NoTitle" />
    </else>
  </choose>
  <!-- wrap volume with an inner group -->
  <group>
    <text variable="volume"/>
  </group>
</group>

Consider the choose branch, then consider the <text value> and the the <text variable>:

<group delimiter=" ">
  1. choose `else` branch
  <text value="NoTitle" /> 2. NoneSeen
  <group>
    <text variable="volume"/> 3. not rendered, OnlyEmpty
  </group>
</group>

The inner group has one child, OnlyEmpty. Therefore it is not rendered. But it can still carry group state, and that state is set to NoneSeen. This is so the innermost group around an OnlyEmpty variable attempt is the only one whose conditional rendering is affected.

<group delimiter=" ">
  <text value="NoTitle" /> NoneSeen
  <!-- group was not rendered --> NoneSeen
</group>

Then the outer group is considered. It has two children, both NoneSeen. NoneSeen.neighbour(NoneSeen) == NoneSeen, so the group is rendered: NoTitle is the output.

For reference, the self.neighbour(other) function that gets folded (as in Iterator::fold) through all a group’s direct children is as follows:

Edit: be sure to read my interpretation about macros again.

With regard to this:

I don’t think so. The macro does not call any variables. Whether or not a macro is an implicit group, to use the language of my previous reply, the output has NoneSeen, and [NoneSeen, NoneSeen, OnlyEmpty].fold(neighbour) is OnlyEmpty and the group is not rendered. This makes sense if you have a macro just for inserting a special character or selecting a term based on item types, for example.

<macro name="TextValue">
  <text value="PLAINTEXT" />
</macro>
...
<group>
  <text macro="TextValue" /> ........ NoneSeen
  <text term="whatever" /> .......... NoneSeen
  <text variable="notset-variable" /> OnlyEmpty
</group>

I need to think about that a good deal more carefully, not least because your architecture is different and far more elegant than mine is and I’m rust-free. I think your interpretation was the same as mine was: a macro is as such “transparent” to a group (which, after all, is what one might expect from a macro: it is effectively expanded and not called).

However, where does that leave the expected output on the example I give? As I understand your approach, the author would need to wrap the attempts to render variables in inner-groups, in which case their failure would be treated as harmless (because, in effect, inner groups are not transparent to outer groups, they are never treated as “attempts” to render variables, but simply as rendering elements that either render or don’t). I have to say that in effect I had taken the same approach, but I can see it’s an inconvenience to authors. What I wonder is whether it can be safely avoided. I suppose the strongest point is that Frank has and it is a pretty strong point.

I’m not sure I follow this discussion entirely, but I think it’s based on a misunderstanding:

<group delimiter="; ">
  <choose>
    <if variable="issued">
    .. render date ..
    </if>
    <else>
    .. print "no date" term ..
    </else>
  </choose>
  <text variable="foo" />
</group>

Does not, in fact, render anything, nor should it. Paul simplified the test in one important way:

<macro name="year-date">
<choose>
    <if variable="issued">
    .. render date ..
    </if>
    <else>
    .. print "no date" term ..
    </else>
  </choose>
</macro>
...
<group delimiter="; ">
  <text macro="year-date"/>
  <text variable="foo" />
</group>

will render no date, because the output of macros is always rendered, with the macro indeed behaving like a group.

This logic is heavily used e.g. in styles that use “anon” for items with no authors. This would be a mess to change, so if we feel that the specs aren’t clear on this I’d rather change those than hundreds of styles.

Ah. OK. I’d have to go back to the test from which I derived my original understanding; I probably hadn’t appreciated that it was the macroness that mattered, so I produced a different rationalisation and coded accordingly, only then to get deeper in the weeds.

But I’m not sure a macro is in that respect behaving like an implicit group in every respect: after all if the code you mention wouldn’t render, then it wouldn’t render in a group either, would it? It seems more like the macro is behaving as a sort of “shell” whereby anything produced in a macro (whatever its source) is treated as if it was “just text”, in which case I do think the documentation is not quite right.

In one sense, it seems to me the group stuff is really not the point. The real point is to distinguish between cases where a value is “just missing” and one where its absence has been “remarked on”. One wants really to distinguish between cases where non-variable text is supposed to accompany a variable – so that one wants one failure to result in another – and cases where it is a substitute for it – where one does not. If someone has gone to the trouble of saying “if this variable is missing, print this” it’s humanly obvious they want something printed, but it’s harder to encapsulate this in the logic. (One option would be to say that printing something in response to an absence in a conditional or substitution will always count as success.)

For instance, what should happen if one renders a non-variable (say a term or a value) via substitute in names within a group (no macro)? In such a case there clearly has been an attempt to render a variable, and it has failed, but something has been rendered quite deliberately to reflect that failure. I suppose that should count as success, in a sense. (As I have it at the moment, it is treated as failure, so nothing is printed – which I think is as per the spec, but probably absurd.)

But I’m not sure a macro is in that respect behaving like an implicit group in every respect: after all if the code you mention wouldn’t render, then it wouldn’t render in a group either, would it?

At least with citeproc-js,

<group delimiter="; ">
<group>
  <choose>
    <if variable="issued">
    .. render date ..
    </if>
    <else>
    .. print "no date" term ..
    </else>
  </choose>
</group>
  <text variable="foo" />
</group>

does render “no date”, yes.

Right, that’s the “anon” behavior I was referring to above. That occurs in substitute and is solved by packing it in a macro.

But in that case why require it to be packed into a macro: the intent is clear from its inclusion in substitute? It’s not difficult to teach the processor to treat anything rendered by substitute as effectively a successful rendering of a variable, which in practical terms makes sense.

I’m afraid–faithfully though I will try to reproduce it–I still have trouble with the concepts here.

The example you give at the top seems to me to have things back to front. I can understand why wrapping a (non-existent) variable in a group could work. With

<group>
  <text value="Won't print"/>
  <text variable="nonexistent"/>
</group>

We have a group that tries to render a non-existent variable value, and so the whole group should fail. But with

<group>
  <text value="Will print"/>
  <group>
    <text variable="non-existent"/>
 </group>
</group>

It’s reasonable (though not obvious) that the inner group “masks” the attempt to render a variable from the outer group, at which point there is no impediment to that group rendering.

But your first example seems to demonstrate something different and, to me, more puzzing. You have there two elements: an inner group which (on its own) should apparently not render anything at all, and an outer group with two children, one of which is attempting to render a variable and failing, and the other of which in itself renders nothing. Really, that shouldn’t render. Put differently, I can’t see why the context (being rendered within an outer group) can make any difference to whether the inner group renders, and I can’t see why the outer group should print.

If the magic is all in macros, and if a macro is always treated as if it had rendered a variable (even if it doesn’t), then something like:

<macro name="clutter">
  <text value="a lot of rubbish"/>
</macro>
<group>
  <text macro="clutter"/>
  <text variable="foo"/>
</group>

Should always print the text from the clutter macro, even though

<group>
 <text value="a lot of nonsense"/>
 <text variable="non-existent"/>
</group>

obviously wouldn’t. I mean, that’s simple; but it seems odd. If it is right, then I think the documentation is wrong. It should really read

cs:group implicitly acts as a conditional: cs:group and its child elements are suppressed if a) at least one rendering element in cs:group calls a variable directly (and not from within an inner group or via a macro) and b) all variables that are directly called are empty and c) the cs:group element does not call any macro that renders text.

If that’s the intent, that’s fine: it’s not at all difficult to implement. I just wonder …

Reading the spec as a citeproc author

I just want to quickly tease out how the spec has to be translated into code here.

  • Processors need to be able to choose in every single situation whether or not a block is rendered or not.
  • “Is rendered” / “to render” / “to suppress” do not have the meaning they would have in, e.g. a graphics program (drawing triangles into a screen buffer) or if your processor compiled to verbal instructions to a monk with a fountain pen and no eraser. No matter how it’s implemented, a citeproc program will be equivalent to constructing a tree and doing a depth-first tree-fold with intermediate results at each level that may either be discarded = “not rendered”, or included as a child for a parent element.
  • I have not seen a complete specification of ‘groups = implicit conditionals’ emerging only from examples of things that should render and things that shouldn’t.
  • Part of this is because “render” only sometimes means “appears on your screen”. That’s really confusing.
  • Similarly, words like “print” belie some kind of imperative programming paradigm, as if CSL had Python’s print function. This is woefully inaccurate.
  • I would therefore propose using the word “discard” to mean “not render”. An inner group might produce a result, but a parent group may be discarded and it will not appear in the output. (The spec says “suppress” but this has other meanings, notably suppressing a variable wherever else it occurs in a particular rendered citation.)
  • The spec only dictates when to discard results, not when to keep them, because keeping them is the default choice.

The inner-group example

Like @PaulStanley, I find this very puzzling. I’m just going to walk through it again:

foo: None
issued: None
G(G(if issued then date(issued) else "no date"), variable(foo))
  • The if block expands to the term/text "no date", so no variables have been called (I call this NoneSeen)
  • The inner group doesn’t know it is in an outer group, so solely on the basis of its children being plain text only/NoneSeen it does not discard itself. (Note this is contrary to @PaulStanley’s “which (on its own) should apparently not render anything at all”.)
  • variable(foo) calls a variable but does not produce any output (I call this OnlyEmpty)

The hard part is deciding whether to discard the entire outer group. The spec cannot sanely be read to allow any behaviour except discarding entire groups at once (e.g. filtering, always passing macros through, etc.) Here are two ways the outer group could choose:

  1. Keep a record of all the variable calls in its entire subtree, duplicating the effort of the inner group. (E.g. put groups on a stack, when you call a variable, adjust all parent groups’ states.)
  2. Do direct-child-only analysis by passing information about how child groups were processed.
    • The inner group could record that no variables were called (etc), thereby being equivalent to #1. This is what citeproc-rs does.

I’m puzzled because either way, the decision should be “discard the entire outer group”. No variables were called that were not empty; one was called that was empty. I cannot think of a concrete way of deciding within the parameters set out by the specification that would get you to the result “do not discard the entire outer group”. I agree with @PaulStanley’s analysis that deciding to keep it would require analysing the context, but I am not sure how, and doing this does not seem easy.

Here are some other confusing statements:

  • “the output of macros is always rendered”
    • Except when it’s in a group that is discarded? Except maybe not?
  • ~ it is desirable that the ‘date/no date’ = ‘author/anon’ produce plaintext in macros + ~ “macros behave like a group”
    • Group(if issued then date else "no date") is not immediately discarded.
    • Macro(if issued then date else "no date") is not immediately discarded.
    • Macros should only be said to “behave like a group” if they actually discard results, because keeping results is not a distinctive property of groups. So this isn’t a useful specification device.

The upshot

I have put together a much more precise and useful specification to work from, as a starting point.

The output of every rendering element, even if it is empty, is one of { important, missing, or plain }. This property is called “importance”.

A cs:group will be discarded if its output is missing.

  1. <text variable="X"/>, <number variable="X"/>, and <date variable="X"/> are important if the variable X is not empty, otherwise they are missing.
  2. cs:label, <text term="..."/> and <text value="..."/> are plain.
  3. <names variable="Xs"/> is important if any of Xs are not empty. If cs:substitute is used (i.e. none of Xs have values), then the cs:names has the same importance as the selected child element of cs:substitute, except:
    a. if the selected child element of cs:substitute is plain, cs:names is important;
    b. if no child element of cs:substitute is selected, the cs:names is missing.
  4. cs:group, <text macro="..." />, if, else-if and else are sequences whose importance depends on their child elements. cs:choose’s importance is the same as the branch that is selected.
  5. For a sequence:
    a. if any of the children of the sequence are important, the sequence is important;
    b. if (a) does not apply and any of the children of the sequence are missing, the sequence is missing;
    c. if (a) and (b) do not apply, the sequence is plain.
    d. to remove doubt, an empty sequence is plain.
  6. A discarded group is not considered to be a child of a sequence.

Some options for changing this:

  • Make all plain cs:choose blocks important.
  • Make all plain macro calls important.
  • Make all cs:choose blocks that test a variable important (because the variable influenced the output).
  • Make group discarding more selective: sequences are always important, but groups pick out those text term/text variable/label/text value/date/number/names children into a subsequence, and either drop all of them leaving only the remaining elements, or keep all of them in the original order.

Good morning! Is this a discussion of how the language works, or of how to best tear it up and start again from scratch?

Very much a discussion of how the language works, and how to more precisely specify it. Everything I described is within the bounds of CSL 1.0.1.

I should clarify that the shorthand is just there for lower-overhead mental digestion, from when I had arranged my post differently and the quote was a scroll away and I also wanted to put long snippets of CSL in one line, nothing to do with proposing new syntax.

I offered earlier to dig out the variables used in citeproc-js to control output from a group or macro. It’s just implementation detail, but here they are:

{
    term_intended: boolean,
    variable_success: boolean,
    variable_attempt: boolean
}

At the close of a group the flags are evaluated like so:

if (flags.variable_success || (flags.term_intended && !flags.variable_attempt)) {
    // proceed with output
    // set flags.variable_success = true
}
// pass flags to parent

So output of a bare term within a group will be treated as part and parcel with successful rendering of a variable.

I’ve added this to a list of things where the spec, I think, deviates from the currently expected behaviour (by the authors, citeproc-js, existent styles, etc.). I suppose that for this behaviour to change we’d need a major version bump in the spec (and a bunch of work updating styles), so for now I propose clarifying the expected behaviour in version 1.1/1.2, something like this:

cs:group implicitly acts as a conditional: cs:group and its child elements are suppressed if a) at least one rendering element in cs:group calls a variable or term (either directly, in nested cs:groups or via a macro), and b) all variables or terms that are called are empty.

By the way, how does this work for cs:label? And I saw citeproc-js considers calls to non-empty date variables where none of the required date parts are available (say a month requested on a date with year precision) to be empty for this purpose as well (test case).

That’s not the intended behavior, though. A non-empty term (rendered via cs:text or cs:label or a string value (rendered via cs:text) will be suppressed if there is an attempt to render a variable (via a sibling cs:date, cs:names, cs:number or cs:text element, or via one of those as a descendant of a sibling cs:group element). As I read the spec language, it accurately describes current behavior.

2 Likes

Ah, I misread this part, that makes sense.

So:

  1. a non-empty nested cs:group counts as a non-empty variable
  2. for all purposes of implicit conditionals, cs:macro behaves the same as cs:group
2 Likes

I stand by the missing/plain/important strategy. It could be made a bit shorter and easier to approach for authors (eg) but it already describes what “directly” and “same for the purposes of” mean, which were the vagueness traps that got us into trouble originally. (Maybe it should do away with the ‘sequence’ terminology and simplify that part.) It also ticks off one other item on your list, cs:substitute and whether it means a group gets discarded.

1 Like

Brief update: adding @larsgw’s “or term” to citeproc-rs causes 8 regressions in the CSL test suite. There are probably more but they weren’t passing to begin with so they don’t appear as regressions.

Details
thread 'main' panicked at 'Some tests failed', /Users/cormac/.cargo/git/checkouts/datatest-8f1d8ca908a37906/e267a78/src/runner.rs:357:22
error: test failed, to rerun pass '-p citeproc --test suite'
regression: csl_test_suite::group_SuppressWithEmptyNestedDateNode.txt
output:
thread 'csl_test_suite::group_SuppressWithEmptyNestedDateNode.txt' panicked at 'assertion failed: `(left == right)`

Diff < left / right > :
 <div class="csl-bib-body">
<  <div class="csl-entry">Author1, O., Author7, T. &#38; Author3, T., 2008. Test Article. <i>Test Journal</i>, 12. Available at:  [accessed ].</div>
>  <div class="csl-entry">Author1, O., Author7, T. &#38; Author3, T., 2008. Test Article. <i>Test Journal</i>, 12.</div>
 </div>

', crates/citeproc/tests/suite.rs:69:9

regression: csl_test_suite::bugreports_OldMhraDisambiguationFailure.txt
output:
thread 'csl_test_suite::bugreports_OldMhraDisambiguationFailure.txt' panicked at 'assertion failed: `(left == right)`

Diff < left / right > :
<[0] John Doe, <i>Book A</i>,  vols., 2000; John Doe, <i>Book B</i>,  vols., 2000.
>[0] John Doe, <i>Book A</i>, 2000; John Doe, <i>Book B</i>, 2000.
 [1] Doe, Book A; Doe, Book B.


', crates/citeproc/tests/suite.rs:69:9

regression: csl_test_suite::bugreports_MovePunctuationInsideQuotesForLocator.txt
output:
thread 'csl_test_suite::bugreports_MovePunctuationInsideQuotesForLocator.txt' panicked at 'assertion failed: `(left == right)`

Diff < left / right > :
<Jennifer J. Davis, “Men of Taste: Gender and Authority in the French Culinary Trades, 1730-1830” (Ph.D. diss. presented at the , Pennsylvania State University, History, 2004), s.v. “gargoyle.”
>Jennifer J. Davis, “Men of Taste: Gender and Authority in the French Culinary Trades, 1730-1830” (Ph.D. diss., Pennsylvania State University, History, 2004), s.v. “gargoyle.”

', crates/citeproc/tests/suite.rs:69:9

regression: csl_test_suite::bugreports_UndefinedInName2.txt
output:
thread 'csl_test_suite::bugreports_UndefinedInName2.txt' panicked at 'assertion failed: `(left == right)`

Diff < left / right > :
 <div class="csl-bib-body">
<  <div class="csl-entry">Kim, B-X, and A-Y Kim. Available at: [accessed ].</div>
>  <div class="csl-entry">Kim, B-X, and A-Y Kim.</div>
 </div>

', crates/citeproc/tests/suite.rs:69:9

regression: csl_test_suite::quotes_QuotesUnderQuotesFalse.txt
output:
thread 'csl_test_suite::quotes_QuotesUnderQuotesFalse.txt' panicked at 'assertion failed: `(left == right)`

Diff < left / right > :
 <div class="csl-bib-body">
<  <div class="csl-entry"> 'Title with ‘quotes’ in it', in:.</div>
>  <div class="csl-entry"> 'Title with ‘quotes’ in it',.</div>
 </div>

', crates/citeproc/tests/suite.rs:69:9

regression: csl_test_suite::date_AccessedCrash.txt
output:
thread 'csl_test_suite::date_AccessedCrash.txt' panicked at 'assertion failed: `(left == right)`

Diff < left / right > :
 <div class="csl-bib-body">
<  <div class="csl-entry"> (1994) (vols. ). retrieved from </div>
>  <div class="csl-entry"> (1994).</div>
 </div>

', crates/citeproc/tests/suite.rs:69:9

regression: csl_test_suite::fullstyles_ChicagoNoteWithBibliographyWithPublisher.txt
output:
thread 'csl_test_suite::fullstyles_ChicagoNoteWithBibliographyWithPublisher.txt' panicked at 'assertion failed: `(left == right)`

Diff < left / right > :
 <div class="csl-bib-body">
<  <div class="csl-entry">Doe, John. <i>Book A</i>. vol.. .  vols... Brobdingnag: Zardoz Press, 1990.</div>
<  <div class="csl-entry">Roe, Jane. <i>Book B</i>. vol.. .  vols.., 1991.</div>
>  <div class="csl-entry">Doe, John. <i>Book A</i>. Brobdingnag: Zardoz Press, 1990.</div>
>  <div class="csl-entry">Roe, Jane. <i>Book B</i>, 1991.</div>
 </div>

', crates/citeproc/tests/suite.rs:69:9

regression: csl_test_suite::bugreports_UndefinedInName3.txt
output:
thread 'csl_test_suite::bugreports_UndefinedInName3.txt' panicked at 'assertion failed: `(left == right)`

Diff < left / right > :
<B-X Kim and A-Y Kim,  vols. [accessed].
>B-X Kim and A-Y Kim.
 Kim and Kim, 23.

', crates/citeproc/tests/suite.rs:69:9

test result: 8 regressions, 0 new passing tests, 0 new ignores, out of 881 intersecting tests