Writing one test per user story (packages/essapps/tests/stories/, commit 954593f on architecture-sketch) found five stories that disagree with the design, or with the skeleton that implements it. Each is a strict xfail. For each one, either the design changes or the story's check does, and user-stories.md should record which. Stories blocked only by a missing feature (a SciCat source, run numbers resolved at submission, and others) are xfails too but are not listed here; pytest -rx packages/essapps/tests/stories lists every xfail with its reason. Bugs found at the same time are in a separate issue.
S6: sum sample runs and background runs
test_s6_sum_sample_and_background_runs. The story sums sample runs and background runs separately, then subtracts the background. The stage of a sample run leaves background_run unset and is refused at submit (background_run: Field required), because a request that supplies no intermediate is validated against the whole params model. The same shape is the two-member-table example in aggregation.md.
The backend cannot tell which parameters a stage reads, because only the workflow code knows the graph. The contribute and combine specs used to carry that information. One option: the spec lists, per exposed value (results and intermediates), the parameters and intermediates it reads. The adapter generates the list from the sciline graph and refuses a spec that disagrees with it. That also makes validation complete at submit for a stage cut at an intermediate, and lets a record hold exactly the parameters a run read. The alternative is to validate field by field whenever a request varies or supplies anything, and to find missing parameters at run time.
B1: tune a SANS reduction, then save the template
test_b1_tune_a_sans_reduction_in_a_notebook. The story's check is "the template captures what was tuned". Template.from_request blanks every field the request varied, as rules.md says. So after tuning bins the saved template has bins as a blank and has lost the tuned value. The session's vary hint thereby decides what a stored template keeps. Either from_request keeps varied values and blanks only data references and supplied intermediates, or the story's check changes.
F3: publish what was tuned interactively
test_f3_publish_what_was_tuned_interactively. The design says a result a held stage served is recomputed in a throwaway process before publication. The skeleton refuses it ("reused a held stage; recompute it first"), and client.recompute(tuned) in a notebook is served by the held stage again (reused=True). A notebook cannot publish a tuned result at all. Either publish recomputes, recompute gets a way past held stages, or the story says how to publish.
E2: automatic reduction goes quiet
test_e2_automatic_reduction_goes_quiet. When a rule's template names a spec version the backend no longer has, the trigger loop's submission is refused and nothing is recorded, as intended. But trigger_status still says the rule fires ("no record under the label yet"). The refusal reason exists only in TriggerLoop.refusals, which lives in the loop object and is reset on every pass, so an operator in another process sees neither. Either trigger_status validates the request the rule would make, or refusals become state that can be queried.
A5: metadata corrected after the fact
test_a5_metadata_corrected_after_the_fact. Client.datasets() keeps the first entry per dataset identity, so with the append-only catalogue fake a corrected entry is never shown. A real SciCat source that returns current entries would pass, so this is partly a limit of the fake. The open point is what DatasetSource.new_datasets is. It could be an arrival feed, which the trigger loop needs and where the first entry winning is fine. Or it could be the current listing, which the UI needs. Possibly it should be two methods.
🤖 Generated with Claude Code
Writing one test per user story (
packages/essapps/tests/stories/, commit 954593f onarchitecture-sketch) found five stories that disagree with the design, or with the skeleton that implements it. Each is a strict xfail. For each one, either the design changes or the story's check does, anduser-stories.mdshould record which. Stories blocked only by a missing feature (a SciCat source, run numbers resolved at submission, and others) are xfails too but are not listed here;pytest -rx packages/essapps/tests/storieslists every xfail with its reason. Bugs found at the same time are in a separate issue.S6: sum sample runs and background runs
test_s6_sum_sample_and_background_runs. The story sums sample runs and background runs separately, then subtracts the background. The stage of a sample run leavesbackground_rununset and is refused at submit (background_run: Field required), because a request that supplies no intermediate is validated against the whole params model. The same shape is the two-member-table example in aggregation.md.The backend cannot tell which parameters a stage reads, because only the workflow code knows the graph. The contribute and combine specs used to carry that information. One option: the spec lists, per exposed value (results and intermediates), the parameters and intermediates it reads. The adapter generates the list from the sciline graph and refuses a spec that disagrees with it. That also makes validation complete at submit for a stage cut at an intermediate, and lets a record hold exactly the parameters a run read. The alternative is to validate field by field whenever a request varies or supplies anything, and to find missing parameters at run time.
B1: tune a SANS reduction, then save the template
test_b1_tune_a_sans_reduction_in_a_notebook. The story's check is "the template captures what was tuned".Template.from_requestblanks every field the request varied, as rules.md says. So after tuningbinsthe saved template hasbinsas a blank and has lost the tuned value. The session'svaryhint thereby decides what a stored template keeps. Eitherfrom_requestkeeps varied values and blanks only data references and supplied intermediates, or the story's check changes.F3: publish what was tuned interactively
test_f3_publish_what_was_tuned_interactively. The design says a result a held stage served is recomputed in a throwaway process before publication. The skeleton refuses it ("reused a held stage; recompute it first"), andclient.recompute(tuned)in a notebook is served by the held stage again (reused=True). A notebook cannot publish a tuned result at all. Eitherpublishrecomputes,recomputegets a way past held stages, or the story says how to publish.E2: automatic reduction goes quiet
test_e2_automatic_reduction_goes_quiet. When a rule's template names a spec version the backend no longer has, the trigger loop's submission is refused and nothing is recorded, as intended. Buttrigger_statusstill says the rule fires ("no record under the label yet"). The refusal reason exists only inTriggerLoop.refusals, which lives in the loop object and is reset on every pass, so an operator in another process sees neither. Eithertrigger_statusvalidates the request the rule would make, or refusals become state that can be queried.A5: metadata corrected after the fact
test_a5_metadata_corrected_after_the_fact.Client.datasets()keeps the first entry per dataset identity, so with the append-only catalogue fake a corrected entry is never shown. A real SciCat source that returns current entries would pass, so this is partly a limit of the fake. The open point is whatDatasetSource.new_datasetsis. It could be an arrival feed, which the trigger loop needs and where the first entry winning is fine. Or it could be the current listing, which the UI needs. Possibly it should be two methods.🤖 Generated with Claude Code