Skip to content

fix(story): honest refusals, projected sub records, run-scoped error results - #11825

Merged
mike-thompson-day8 merged 1 commit into
mainfrom
worker/story-exec-ui-fix
Oct 30, 2026
Merged

mike-thompson-day8 merged 1 commit into
mainfrom
worker/story-exec-ui-fix

Conversation

@mike-thompson-day8

@mike-thompson-day8 mike-thompson-day8 commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Fixes the eight findings of the correctness review of two Story slices: story execution (rf2-lgzco.18, five findings) and the story UI workspace (rf2-lgzco.19, three findings). Every one was reproduced at trunk before it was fixed.

Story execution (rf2-lgzco.18)

  1. P2, privacy. :rf.assert/sub-equals wrote a registration-classified sub's raw output into :expected, :actual, :payload and :reason. evaluate-sub-equals now passes the value through core's re-frame.classification/project-sub-value (raw value, sub-vec, frame) and then through the existing args-path redact-at. The comparison still uses the raw value, the record carries the projected one, and the :rf/redacted sentinel rule is unchanged. No core change. tools/story/spec/004-Assertions.md §Privacy rule 1 drops the "tracked separately" propagation sentence.
  2. P2. A terminal DOM-family atom, run under a runner that claims :dom on a host with no DOM, recorded nothing, so the run read :pass. exec-assert-dom-atom! now records the skip as a :cannot-run record, the same shape exec-assert-browser-atom! records. The in-script step result is marked :recorded?, so the refusal is counted once.
  3. P3. A check whose atom was refused before evaluation read :pass. check-record now aggregates its group together with every :unmet refusal whose :unit is one of the check's atoms, which attributes the refusal to the check (017 §:cannot-run). record-matches-atom? moved to re-frame.story.assertions so the scrubber (below) can share it.
  4. P3, ruled (c). Check atoms, own and inherited, now count toward the plan's :required-runner, so {:runner :auto} escalates for them. A fixed runner never chooses its runner from that slot, so it refuses such an atom :cannot-run, attributed to the check, as before. tools/story/spec/017-Testing-Story.md §Requirement inference gains the ruling sentence. The sidebar runner chip (sidebar-signals/resolved-body → ::check-atoms) follows the same rule. The Tests pane "required" badge reads the result's :required-runner, so it follows automatically. This changes :plan-hash for every variant whose checks carry a token. No golden in the tree pins one.
  5. P3. An :error result from a throw inside the phase chain carried the frame's whole epoch ring, so its evidence and :run-hash drifted across identical re-runs. The phase 0 baseline is now read back from the frame when the thrown ctx has lost it. The decorator refusal also stamps the baseline before it throws. The play chain's rejection path now threads its ctx's :plan, :runner-selection and :epoch-baseline.

Story UI workspace (rf2-lgzco.19)

  1. P3. Controls built their widgets from the raw story and variant bodies, so an argtype inherited through :extends or received through :compose was dropped. resolve-argtypes now reads the compiled plan's merged [:world :argtypes], memoized beside the view-args schema in re-frame.story.view-args (compiled-argtypes). If the plan cannot compile it falls back to the variant's own body. ui/docs.cljc (the same defect, owned by rf2-lgzco.20) is untouched.
  2. P3. A run refused before evaluation showed "0 cannot-run of 0", and the chrome widget had no cannot-run chip. This shares a cause with finding 18#3: a refusal lives only in the :cannot-run slot. Finding 2's record fixes the reviewer's scenario on its own. The new test-mode.pure/pane-summary covers the rest: it counts each refused unit that no record carries, once per distinct :unit. The reviewer's (count (:cannot-run result)) would have counted a refused atom twice, once for its record and once for its refusal row, and an in-script step up to three times. The widget renders ⊘ N when the count is positive, and the spec/009 selector row says so.
  3. P4. Each scrubber tick now takes its own record, matched by record-matches-atom? and taken first-unconsumed, instead of the next record in order. An [:assert …] checkpoint's record no longer colours the next dispatched assertion's tick.

Quality gates

Each exit code below is the runner's own, captured to a file.

  • Story JVM clojure -M:test in tools/story: exit 0. Run inside scripts/test-jvm-tools.sh on the rebased tree: 1161 tests / 3097 assertions.
  • scripts/test-jvm-tools.sh on the rebased tree: exit 0.
  • npm run test:cljs: exit 0 before the rebase (6812 tests / 19900 assertions). Inside the spine on the rebased tree: 0 failures (6815 / 19958). The new CLJS tests are confirmed compiled into the build.
  • scripts/test-fast-pr.sh on the rebased tree: exit 0, PASS fast PR spine, gate root this worktree. It ran the docs tier, pinned clj-kondo (0 errors) and the node tier.
  • scripts/check_doc_slugs.py: exit 0. A broken anchor planted in the new 004 sentence went red (exit 1, BROKEN ANCHOR) and was restored.
  • The final-base gate is this PR's CI.

Tests

Every new JVM test failed against the pre-fix tools/story/src and passes now. Delta against the trunk baseline (1151 tests / 3058 assertions): +10 tests / +39 assertions (1161 / 3097), exactly as predicted from the diff. One existing test pinned the old no-DOM behaviour: substrate_boundary_cljs_test's settled terminal atom expected zero records. It now expects the atom's own no-DOM :cannot-run, and the timeout case asserts the settle's flush-timeout reason, so the test still tells an honoured settle failure from a dropped one.

Plants, restored and verified by blob hash against HEAD:

  • 18#1: without project-sub-value, the raw PARTNER-KEY-SECRET appears in :expected, :actual and :payload. The public-sub control is green in both runs.
  • 18#2: with the pre-fix runner_events.cljc, the no-DOM terminal run reads :pass again under {:runner :dom} and {:runner :auto}.

Part of rf2-lgzco.18 and rf2-lgzco.19.

…results

Story execution (rf2-lgzco.18):
- :rf.assert/sub-equals projects the value through the sub's own
  registration classification (core's project-sub-value) before the
  args-path redaction, so a whole-output-sensitive sub never lands raw in
  :expected / :actual / :payload / :reason. Compare raw, record projected.
- A DOM-family atom on a host with no DOM records :cannot-run instead of
  nothing, so a terminal or check atom under a runner that claims :dom is
  no longer a false :pass; the in-script step is marked :recorded? so the
  refusal counts once.
- A check whose atom the runner refused reads :cannot-run: check-record
  aggregates the refusals whose :unit is one of its atoms.
- Check atoms count toward :required-runner, so {:runner :auto} escalates
  for them while a fixed runner refuses them per unit (spec/017
  §Requirement inference gains the ruling sentence). The sidebar runner
  chip folds received check atoms the same way.
- An :error result reads the run's epoch baseline from the frame when the
  thrown ctx lost it, so identical re-runs carry identical evidence and
  an equal :run-hash; the play chain's rejection keeps its plan and
  runner.

Story UI workspace (rf2-lgzco.19):
- Controls read argtypes from the compiled plan's [:world :argtypes], so
  an :extends-inherited or :compose-d argtype builds its widget.
- The Tests pane summary counts refused units no record carries, and the
  chrome test widget renders a ⊘ cannot-run chip.
- Each step-through scrubber tick takes its own record rather than the
  next one in order.
@mike-thompson-day8
mike-thompson-day8 marked this pull request as ready for review October 30, 2026 01:29
@mike-thompson-day8
mike-thompson-day8 merged commit b791981 into main Oct 30, 2026
61 checks passed
@mike-thompson-day8
mike-thompson-day8 deleted the worker/story-exec-ui-fix branch October 30, 2026 01:32
mike-thompson-day8 added a commit that referenced this pull request Oct 30, 2026
Merge: b791981 at 2026-10-30T01:32:36Z.
Completeness: The eight requested changes are present and their reported regression tests are meaningful. Full production and test patches, both complete source beads and owning assertion contracts read. Two assertion-association cases remain actionable and have separate new open beads.
Correctness: Focused JVM probe on the exact merged archive, exit 0, confirms a partially redacted failed sub assertion yields an empty passing check and an identical earlier checkpoint paints a later failing tick green. Whole-sensitive/public and no-checkpoint controls go the other way. Other reported gates accepted.
Implementation quality: The shared projector, plan merge, refusal counting and run baseline reuse existing mechanisms. Payload matching is too weak for projected values and repeated executions; the new beads request targeted association fixes without expanding the API.
Findings: rf2-4v944, rf2-840y5.
Source forward pointers: rf2-lgzco.18, rf2-lgzco.19.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant