Revised Test Framework Design #376
Replies: 12 comments 14 replies
-
Dance Test Framework — OverviewThis document provides a high-level overview of the Dance Test Framework: its goals, structure, and core abstractions.
The purpose of this document is to orient readers, establish shared mental models, and introduce how the pieces fit together. 1. Purpose & ScopeThe goal of the Dance Test Framework is to make it easier, safer, and less error-prone to author and execute test cases for MAP Dances and holon operations. A Test Case is defined as a sequence of Test Steps that:
The framework is designed to support:
2. Two-Phase ModelThe framework is explicitly split into two phases: 2.1 Fixture Phase (Test Case Definition)During the Fixture Phase:
No execution occurs in this phase. 2.2 Execution Phase (Test Case Execution)During the Execution Phase:
The execution phase is deterministic and driven entirely by the artifacts produced during the fixture phase. 3. Roles & ResponsibilitiesAdders vs ExecutorsThe framework makes a deliberate distinction:
TestCase authors interact almost exclusively with adders. 4. Core Abstractions (Conceptual)4.1 TestReference — The Step ContractEach Test Step is represented by a TestReference. A TestReference is:
Conceptually, a TestReference captures two things:
These two roles travel together as a single, immutable contract. 4.2 FixtureHolon — Identity Across StepsWhile TestReferences describe individual steps, they do not model entity identity over time. That role is handled by FixtureHolons, which:
This allows:
5. Chaining & Snapshot SemanticsThe framework enforces tight snapshot chaining:
This chaining is:
Crucially:
6. Commit as a First-Class Operation
Key characteristics:
This design allows commit to behave like the real system while preserving linear, declarative test authoring. 7. Design PhilosophyThe Dance Test Framework is built around a simple but strict philosophy:
This division ensures:
8. Companion DocumentsThis overview is complemented by:
Together, these documents form the complete specification of the Dance Test Framework. 9. StatusThis document reflects the current converged understanding of the framework’s structure and intent. |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
Test Step Authoring Guide (Adder + Executor)This guide is for authors of new test steps and maintainers updating existing ones. A “test step” always has two halves:
The goal is to make TestCase authoring easy, fast, and safe by encapsulating subtle correctness requirements inside adders and harness helpers. 1. Core Concept: A Step Is a ContractA test step is a fixture-time contract that fully specifies what should happen at execution time. That contract specifies:
This contract is materialized as:
A test step is executed once, but it may involve multiple holons. Every holon affected by a test step must be represented by exactly one TestReference minted during fixture construction. 2. Adder Responsibilities (Fixture Phase)2.1 Purpose of an AdderAn adder’s job is to fully define and register one test step. Specifically, an adder must:
Adders exist so TestCase authors never need to reason about snapshot lifetimes, commit semantics, or execution-time resolution. 2.2 Adder Inputs and OutputsConceptually, every adder follows this pattern. Inputs
Outputs
The adder is the only place where TestSteps are created and registered. 2.3 Canonical Adder Sequence (Critical)For any step that produces or mutates holon state (most steps), the adder must follow this exact sequence for each affected holon. Canonical Adder Sequence
2.4 Deciding Logical Holon IdentityFor each TestReference minted, the adder must explicitly decide whether it represents: Continuation of an existing logical holonExamples:
Rule of thumb:
Creation of a new logical holonExamples:
Rule of thumb:
This decision is enforced through 2.5 Special Step: Commit Adder
Adder responsibilities for
Important properties:
TestCase authors continue using previously returned references. 3. Executor Responsibilities (Execution Phase)3.1 Purpose of an ExecutorAn executor must:
Executors never mint TestReferences and never consult 3.2 Canonical Executor Sequence (Critical)Every executor must follow this exact order for each TestReference it handles.
This recording step enables correct resolution for subsequent steps. 3.3 Special Executor: CommitThe commit executor:
This is why the commit adder must mint those TestReferences in advance. 4. Step Parameters and Expected Outcomes4.1 Step-specific parameters
All step-specific parameters belong in the
4.2 Expected step outcome vs expected holon outcomeSome steps are expected to fail. These expectations:
Important consequence:
This allows safe authoring of negative and error scenarios. 5. Common Footguns (Explicitly Forbidden)Adders must not:
Executors must not:
6. Summary Invariants
These rules are foundational. |
Beta Was this translation helpful? Give feedback.
-
Test Case Authoring GuideThis guide is for Test Case definers: people who write test cases using the Dance Test Language. It explains what you need to know — and, just as importantly, what you do not need to know. If you find yourself worrying about snapshot cloning, execution-time resolution, lifecycle bookkeeping, commit mechanics, or defensive error handling, that is a sign the test language or harness needs improvement, not that you need to work harder. 1. Your Role as a TestCase DefinerYour job is to:
You are not responsible for:
Those concerns are intentionally encapsulated by the harness and the test language. 2. Test Case Initialization & FinalizationEvery test case is created and completed using harness-provided helpers that ensure a consistent execution environment. 2.1 Initializing a Test CaseTest cases are initialized via Conceptually, initialization gives you:
Idiomatic Usagelet TestCaseInit {
mut test_case,
fixture_context,
mut fixture_holons,
mut fixture_bindings,
} = TestCaseInit::new(
"Simple Add / Remove Properties Test".to_string(),
"Adds and removes properties across staged and saved holons".to_string(),
)?;This destructuring pattern is idiomatic Rust and intentionally emphasizes that:
You may pass 2.2 Finalizing a Test CaseBefore returning a test case from a fixture function, you must finalize it: test_case.finalize(&fixture_context)?;Currently, As a TestCase definer, you should always:
3. What You Work With During AuthoringYou primarily work with:
A
Think of a
4. Step Descriptions (New)Every step adder now accepts an optional: step_description: Option<String>
Why This Matters
As a TestCase author:
5. The Basic Test Case Authoring PatternTestCases follow a simple, explicit chaining pattern driven by tokens returned from step adders, wrapped inside a small amount of required setup and teardown. This section shows the complete, canonical structure of a TestCase from initialization through finalization. Canonical Pattern
What Happens If You Forget to Finalize?If a TestCase is executed without being finalized, execution will fail immediately with a clear error, for example:
This is an intentional guardrail. Key Mental Model
You should be able to read a TestCase top-to-bottom as a story of holon operations, without knowing anything about harness internals. 6. Expected Outcomes and Failing StepsEvery step adder also encodes the expected outcome of that step. This includes:
Critical Rule
This allows you to:
You declare expected outcomes via the adder, not with conditional logic. 7. Understanding Commit (High-Level)
As a TestCase definer:
The harness ensures subsequent steps operate on committed state. 8. Capabilities You Can Rely OnThe framework provides adders that let you assert global system state without manual bookkeeping. Ensuring Expected Database Countstest_case.add_ensure_database_count_step(None)?;Asserts that the number of saved holons at execution time matches the framework’s internal expectation. You do not track counts manually. Validating Saved Holon Contenttest_case.add_match_saved_content_step(None)?;Asserts that all saved holons match their expected fixture-time snapshots. You do not reconstruct expected database state. Preloading Holonstest_case.add_load_holons_step(
import_set,
expect_staged,
expect_committed,
expect_links_created,
expect_errors,
expect_total_bundles,
expect_total_loader_holons,
)?;Allows you to preload holons (including type descriptors) from MAP import files. 9. Example ScenariosDelete After Delete (Concrete Example)// earlier steps omitted
let book = test_case.add_stage_new_holon_step(
None,
book_fixture(),
)?;
test_case.add_commit_step(None)?;
let _deleted_once = test_case.add_delete_holon_step(
&book,
ResponseStatusCode::OK,
None,
)?;
let _deleted_twice = test_case.add_delete_holon_step(
&book,
ResponseStatusCode::NotFound,
Some("Deleting an already-deleted holon should fail".to_string()),
)?;What this shows:
10. What You Should NOT DoAs a TestCase definer, never:
If you feel tempted to do so, a missing adder should be added instead. 11. When You Need Something NewWhen a needed step does not exist:
Test Step authors are responsible for:
Your TestCase should remain declarative. 12. Mental Model to Keep in Mind
If a TestCase reads clearly, it is probably correct. 13. Summary
|
Beta Was this translation helpful? Give feedback.
-
recommended: or maybe better: |
Beta Was this translation helpful? Give feedback.
-
|
so what is TokenId? Should there be a field for it added to TestReference?
Would it be the key for snapshot_to_fixture_holon ?
Its still not clear what TransientReference represents as the key in the
BTreeMap.
I think its important to define what is meant by token, as previously the
term was synonymous with TestReference.
Lets also describe in more detail when and how exactly the head is advanced.
…On Sun, Jan 18, 2026 at 9:07 AM Steve Melville ***@***.***> wrote:
Great questions, @dauphin3 <https://github.com/dauphin3> !
I've provided answers to each below and also updated the Harness Design
Spec to reflect needed changes sparked by answering your questions.
Q1
I'm interpreting this to mean that the definition of TestReference is:
TestReference {
source: SourceHolon,
expected: ExpectedHolon
}
Correct. However, in light of your subsequent questions, I think it would
clearer if we rename the component types:
Specifically, rename:
- SourceHolon -> SourceSnapshot
- ExpectedHolon -> ExpectedSnapshot
I've updated the spec accordingly.
------------------------------
Q2
Is FixtureHolonId meant to be an alias for TemporaryId ?
No.
FixtureHolonId represents the *fixture-time identity of a logical holon*,
independent of any particular snapshot. Its purpose is to allow the harness
to:
- Group multiple snapshots that refer to the *same logical holon*
- Track lifecycle state across test steps
- Advance the “head” snapshot during commit
- Keep older TestReferences valid after commit
The revised design *does not mandate* a specific derivation strategy for
FixtureHolonId. It is *acceptable* for the harness to internally derive a
FixtureHolonId from:
- The TemporaryId of the first snapshot minted for that holon
- A separately generated identifier
- Any other stable internal mechanism
However, *how FixtureHolonId is derived is an internal implementation
detail and must not be relied upon by call sites.*
------------------------------
Q3
What does the TransientReference in snapshot_to_holon:
BTreeMap<TransientReference, FixtureHolonId>, represent?
and perhaps there is a better field name for snapshot_to_holon
You're right that the name could be better. Let's change it to
snapshot_to_fixture_holon
------------------------------
Purpose of snapshot_to_fixture_holon
The snapshot_to_fixture_holon map exists to answer exactly one question:
*“Given a source snapshot token, which logical FixtureHolon does it belong
to?”*
Key clarifications:
- The map is consulted *only when resolving a SourceSnapshot at
execution time*.
- It is *never used* to resolve expected state.
------------------------------
Role of ExpectedSnapshots
ExpectedSnapshots:
- are immutable historical facts
- are used for validation and tight chaining
- are *never resolved*
- are *never looked up* via snapshot_to_fixture_holon
They do *not* participate in execution-time resolution.
------------------------------
Why ExpectedSnapshots Are Still Registered
Even though ExpectedSnapshots are never resolved directly, they *must
still be registered*.
Why:
- Adders only create *new* snapshots for their ExpectedSnapshot
- Through tight chaining, an ExpectedSnapshot may later become a
SourceSnapshot
- Therefore, it must already be known to FixtureHolons so future
resolution is possible
------------------------------
Accurate Relationship Model
TestReference
├─ SourceSnapshot ─────────▶ snapshot_to_fixture_holon ─▶ FixtureHolon
└─ ExpectedSnapshot ──(registered for future use only)
In short:
- *Registration is universal*
- *Lookup is source-only*
- ExpectedSnapshots participate in the map solely to enable future
chaining
------------------------------
Rules Governing Snapshot Creation & Registration
The following rules are normative:
-
*Adders only create new snapshots for their ExpectedSnapshot.*
- An adder must never create a new snapshot for its source.
- Any mutation performed by an adder results in a new snapshot
representing the expected post-step state.
-
*An adder’s SourceSnapshot is always supplied via its input
TestReference.*
- Because that TestReference was minted by a prior adder (or by
initialization),
its SourceSnapshot *must already be registered* in
snapshot_to_fixture_holon.
-
*Snapshot creation and registration are inseparable.*
- Any FixtureHolons method that creates a new snapshot *must also
register it*
in snapshot_to_fixture_holon as part of the same operation.
- There must be no code path that allows a snapshot to appear
inside a
TestReference without having been registered.
------------------------------
Resulting Guarantees
Taken together, these rules ensure that:
- Every snapshot referenced anywhere inside a TestReference is known
to FixtureHolons
- SourceSnapshot-to-logical-holon resolution is total and deterministic
- Adders cannot accidentally introduce unregistered or orphaned
snapshots
The Harness Design Spec has been updated to reflect these invariants.
------------------------------
Q4
For ExecutionHolons, Please expand on "Many TokenIds may map to the same
ExecutionReference"
I want to make sure I have the correct understanding of the FixtureHolon
-> ExecutionHolon mapping... at one point we had the latest_snapshot
which looks like is now being renamed to head_snapshot point to the
record ExecutionHolon, therefore it was a 1:1 ratio Fixture:Execution
It's not clear what the new mapping is, and why TokenId would be used for
resolution instead of FixtureHolonId
Is it the add_steps that advance the head_snapshot by
fixture_holons.holons.insert(FixtureHolonId, FixtureHolon) ? And this is
only done in the scenarios:
2.4 Deciding Logical Holon Identity For each TestReference minted, the
adder must explicitly decide whether it represents:
Continuation of an existing logical holon Examples:
with_properties add_relationship remove_relationship abandon_staged
delete_saved Rule of thumb:
If the step conceptually modifies or transitions the same entity, reuse
the existing FixtureHolon. _ And also during the commit step
You've put your finger on what may be the *crux move* of the entire
design. And it all stems from the fact : *commit mints new TestReferences
that adders never see.*
Why Token-Based Resolution and Head Redirection Are Essential
This example illustrates the *core design move* of the revised Dance Test
Framework:
*commit must be able to advance holon state without invalidating any
existing TestReferences.*
Step 1 — Stage a New Holon
The TestCase author calls:
- add_stage_new_holon_step
The adder:
- Creates a new transient holon snapshot S₁
- Registers it as the *ExpectedSnapshot* of a new TestReference T₁
- Registers a new logical holon identity FixtureHolonId = F₁
Fixture-time state now includes:
- snapshot_to_fixture_holon[S₁] = F₁
- FixtureHolon(F₁).head_snapshot = S₁
- FixtureHolon(F₁).state = Staged
The TestCase author receives and retains *only* T₁.
Step 2 — Commit
The TestCase author calls:
- add_commit_step
Commit operates over *FixtureHolons*, not TestReferences.
For FixtureHolon F₁ in Staged state, commit:
1. Clones the current head snapshot S₁ to produce a new snapshot S₂
2. Mints a new TestReference T₂ whose ExpectedSnapshot is S₂ and state
is Saved
3. Advances the logical holon:
- FixtureHolon(F₁).head_snapshot = S₂
- FixtureHolon(F₁).state = Saved
4. Registers:
- snapshot_to_fixture_holon[S₂] = F₁
*Critically:*
- T₂ is *not returned* to the TestCase author
- The author still only holds T₁
Fixture-time state now includes *two* snapshots referring to the same
logical holon:
- T₁ → S₁ → F₁
- T₂ → S₂ → F₁
with S₂ as the authoritative head.
------------------------------
Step 3 — Use the Original Token After Commit
The TestCase author now calls:
- add_add_related_holons_step
- Passing T₁ as the target holon
From the author’s perspective, this is correct:
- They are referring to “the holon I created earlier”
- They are not aware of (and must not need to be aware of) the
commit-minted token T₂
Execution-Time Resolution (Correct Behavior)
When the executor resolves T₁:
1. Extract the SourceSnapshot S₁ from T₁
2. Use snapshot_to_fixture_holon to map:
- S₁ → F₁
3. Look up the logical holon:
- FixtureHolon(F₁).head_snapshot = S₂
4. Resolve the execution-time holon from S₂
The operation correctly targets the *committed holon*, even though the
executor was handed an older TestReference.
Why This Design Is Required
This behavior is only possible if:
- TestReferences are immutable, historical contracts
- Logical holon identity is tracked independently
- Execution-time resolution follows the *current head snapshot*
- Token-based resolution is used instead of direct token →
execution-holon mapping
Without this design:
- Commit would invalidate existing tokens
- TestCase authors would be forced to re-thread references
- Global operations like commit would break linear authoring
- Negative and idempotent scenarios would become brittle or impossible
------------------------------
Core Insight
*Commit mints new TestReferences that adders and TestCase authors never
see.*
Therefore, execution-time resolution must:
- start from the token supplied,
- map through logical holon identity,
- and always land on the current head snapshot.
This is the linchpin that makes the Dance Test Framework composable,
linear, and safe.
—
Reply to this email directly, view it on GitHub
<#376 (reply in thread)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AKZFIHHBO7AFFXXY4U6LVTT4HOHSFAVCNFSM6AAAAACR3QONWCVHI2DSMVQWIX3LMV43URDJONRXK43TNFXW4Q3PNVWWK3TUHMYTKNJTGEYTCMY>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
Beta Was this translation helpful? Give feedback.
-
|
And then TokenId in ExecutionHolons is also changing to SnapshotId, or
would it be FixtureHolonId ?
…On Sun, Jan 18, 2026 at 8:14 PM Steve Melville ***@***.***> wrote:
Lets also describe in more detail when and how exactly the head is
advanced.
See the new section 4.4 in the Harness Design Spec
—
Reply to this email directly, view it on GitHub
<#376 (reply in thread)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AKZFIHAKZECXAMGKGTXFXKL4HQVXHAVCNFSM6AAAAACR3QONWCVHI2DSMVQWIX3LMV43URDJONRXK43TNFXW4Q3PNVWWK3TUHMYTKNJTGM4DANQ>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
Beta Was this translation helpful? Give feedback.
-
|
@evomimic to have head_snapshot in FixtureHolon be an Option for this purpose seems unnecessarily messy... I think it's ok to instead change the snapshot field in ExpectedHolon to TransientReference (not option) and know that if the state is Deleted then it points back to the previous snapshot. |
Beta Was this translation helpful? Give feedback.
-
|
FixtureHolon.head_snapshot is currently not an option
so what goes there when advancing?
…On Thu, Jan 22, 2026 at 4:08 PM Steve Melville ***@***.***> wrote:
The design already handles this cleanly.
-
*Deletion is expressed by lifecycle state, not by None.*
- state == Deleted is the signal.
- snapshot == None is a *consequence*, not the meaning.
-
On the fixture side:
- ExpectedSnapshot has (state, Option<TransientReference>).
- The invariant is:
- Deleted ⇒ snapshot = None
- Non-deleted ⇒ snapshot = Some(_)
-
Advancing the head to (Deleted, None) is correct and intentional.
- That *is* the authoritative head for a deleted holon.
- No fake snapshot, no back-pointer hack required.
-
Execution already mirrors this via ExecutionReference::Deleted.
So: *no design change needed*.
Just keep the invariant explicit: *state determines meaning; Option
follows structurally*.
—
Reply to this email directly, view it on GitHub
<#376 (reply in thread)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AKZFIHEX3J4HND4B325ZAR34IE345AVCNFSM6AAAAACR3QONWCVHI2DSMVQWIX3LMV43URDJONRXK43TNFXW4Q3PNVWWK3TUHMYTKNJXGQ2TMMY>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
Beta Was this translation helpful? Give feedback.
-
|
I guess just make it an Option, youre saying?
…On Thu, Jan 22, 2026 at 5:07 PM Zeek Wyatt ***@***.***> wrote:
FixtureHolon.head_snapshot is currently not an option
so what goes there when advancing?
On Thu, Jan 22, 2026 at 4:08 PM Steve Melville ***@***.***>
wrote:
> The design already handles this cleanly.
>
> -
>
> *Deletion is expressed by lifecycle state, not by None.*
> - state == Deleted is the signal.
> - snapshot == None is a *consequence*, not the meaning.
> -
>
> On the fixture side:
> - ExpectedSnapshot has (state, Option<TransientReference>).
> - The invariant is:
> - Deleted ⇒ snapshot = None
> - Non-deleted ⇒ snapshot = Some(_)
> -
>
> Advancing the head to (Deleted, None) is correct and intentional.
> - That *is* the authoritative head for a deleted holon.
> - No fake snapshot, no back-pointer hack required.
> -
>
> Execution already mirrors this via ExecutionReference::Deleted.
>
> So: *no design change needed*.
> Just keep the invariant explicit: *state determines meaning; Option
> follows structurally*.
>
> —
> Reply to this email directly, view it on GitHub
> <#376 (reply in thread)>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AKZFIHEX3J4HND4B325ZAR34IE345AVCNFSM6AAAAACR3QONWCVHI2DSMVQWIX3LMV43URDJONRXK43TNFXW4Q3PNVWWK3TUHMYTKNJXGQ2TMMY>
> .
> You are receiving this because you were mentioned.Message ID:
> ***@***.***>
>
|
Beta Was this translation helpful? Give feedback.
-
Proposed Resolution — Delete Semantics, Snapshot Minting & Head TrackingSummary The issue raised around At a high level:
Core Model ChangeExtend
These fields are private and never accessed directly by adders or executors. This allows the harness to distinguish:
Canonical Delete Semantics (Reasserted)Delete AddersA delete adder follows the canonical adder sequence like any other mutating step:
Key points:
Deletion ends liveness, not identity. API-Level Resolution (Critical)To avoid pushing ambiguity onto callers, no code outside Instead, we introduce intention-revealing APIs:
|
Beta Was this translation helpful? Give feedback.
-
|
is ExpectedSnapshot.snapshot changing from an option? that would seem to be
the direction based on the new approach.
in the comment it showed that it will be given an identifier and set as
Some therefore im wondering what would ever be the None case?
could you elaborate on:
- Liveness is expressed *solely* by state = Deleted
…On Mon, Jan 26, 2026 at 8:28 PM Steve Melville ***@***.***> wrote:
Proposed Resolution — Delete Semantics, Snapshot Minting & Head Tracking
*Summary*
The issue raised around delete steps is real and exposes a gap in how we
model *logical continuity after deletion*. The fix is *not* to
special-case delete executors, nor to treat delete as “snapshotless”.
Instead, we must reassert the canonical invariants and encode the rules *centrally
in FixtureHolons*, so callers never have to reason about edge cases or
make choices themselves.
At a high level:
- *Delete is a mutating step and must mint a new snapshot*
- *Execution results must always be recorded, including deletes*
- *All continuity and source-selection logic lives inside
FixtureHolons APIs*
------------------------------
Core Model Change
*Extend FixtureHolon (internally) to track both:*
-
head_snapshot
The most recent snapshot produced for the logical holon
(may represent Deleted)
-
last_live_snapshot
The most recent snapshot that represents a *live holon*
(Transient, Staged, or Saved)
These fields are *private* and never accessed directly by adders or
executors.
This allows the harness to distinguish:
- *what the current logical state is* (including Deleted), and
- *what snapshot should be used as a source when subsequent steps
intentionally target the same logical holon*
------------------------------
Canonical Delete Semantics (Reasserted) Delete Adders
A delete adder *follows the canonical adder sequence* like any other
mutating step:
- Resolve the source snapshot via FixtureHolons
- *Clone that snapshot* to produce a new TransientReference
- Mint a new TestReference whose:
- ExpectedSnapshot.state = Deleted
- ExpectedSnapshot.snapshot = Some(new_transient_reference)
Key points:
- The snapshot carried by ExpectedSnapshot for a delete step is *identity-bearing
only*
- Liveness is expressed *solely* by state = Deleted
- This guarantees:
- a fresh SnapshotId
- append-only execution recording
- the ability to express delete-after-delete scenarios
Deletion ends *liveness*, not *identity*.
------------------------------
API-Level Resolution (Critical)
To avoid pushing ambiguity onto callers, *no code outside FixtureHolons
should ever choose between “head” and “last live” snapshots*.
Instead, we introduce intention-revealing APIs:
resolve_source_snapshot(token: &TestReference) -> TransientReference
- *Used by adders*
- Encodes the rule:
- If the logical holon is Deleted → return last_live_snapshot
- Otherwise → return head_snapshot
- Meaning:
*Adders always clone from the most recent live snapshot, even when
targeting a deleted holon*
resolve_execution_head(token: &TestReference) -> SnapshotId
- *Used by execution-time resolution*
- Always follows head_snapshot
- Honors commit and delete semantics uniformly
advance_head(expected: &ExpectedSnapshot) *(internal only)*
- Always advances head_snapshot
- Updates last_live_snapshot *only if* expected.state != Deleted
Callers never inspect lifecycle state or branch on delete themselves; the
harness encodes these rules once.
------------------------------
Execution-Time Recording (Mandatory)
- *Executors must always record results*, including delete steps
- Recording is always keyed by the *ExpectedSnapshot SnapshotId*
- For deletes, the recorded value is typically:
- ExecutionReference::Deleted
There are no exceptions:
- delete
- delete-after-delete
- expected failures
Skipping recording would violate append-only execution invariants and
break downstream resolution.
------------------------------
Why This Is the Right Layer
This approach:
- Keeps lifecycle and continuity logic *fully inside FixtureHolons*
- Avoids leaking delete edge cases into:
- adders
- executors
- TestReference structure
- Preserves:
- snapshot immutability
- canonical adder ordering
- append-only execution recording
- commit-driven head advancement
- the ability to test invalid operations *at runtime*
------------------------------
Bottom Line
*Deletion ends liveness, not addressability.*
By minting snapshots for delete steps, always recording execution results,
and encoding source-selection rules behind clear FixtureHolons APIs, we
resolve the delete-step issue cleanly — without hacks, without special
cases, and without burdening callers with choices they should never have to
make.
—
Reply to this email directly, view it on GitHub
<#376 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AKZFIHEK53LZMAZLZXEYKJ34I25M7AVCNFSM6AAAAACR3QONWCVHI2DSMVQWIX3LMV43URDJONRXK43TNFXW4Q3PNVWWK3TUHMYTKNRRGIZDMMQ>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
Beta Was this translation helpful? Give feedback.

Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Background & Motivation
This discussion documents a clarified and converged design for the Dance Test Framework, covering the test harness, test step authoring, and test case authoring.
Goals of the Dance Test Refactor
The Dance Test Refactor has always been driven by a small set of core goals:
The original design moved us significantly in this direction, but left several of these goals only partially realized or insufficiently specified.
Why This Revision Was Needed
The need for this revision emerged directly from substantial implementation effort that @dauphin3 did on Issue #306 and Issue #362. As the implementation progressed, several subtle issues surfaced — particularly around:
The current implementation work uncovered real shortcomings and ambiguities in the original design spec. In several cases, code had to “work around” these ambiguities, which made the design harder to reason about and the implementation harder to maintain.
These issues only became visible through hands-on development, experimentation, and debugging. The insights gained through that work were essential to reaching the design captured here.
How the Revised Design Advances the Refactor Goals
The design documented in this discussion brings the refactor closer to its original goals by:
TestReference)FixtureHolon)This is an evolution of the original design, not a replacement. It reflects what the implementation work taught us about where the design needed to be sharper in order to fully meet its goals.
What This Revision Is (and Is Not)
This discussion is not a reset or a rejection of the current implementation effort.
Instead, it aims to:
Where possible, the design here preserves existing concepts and implementation direction, while tightening definitions and eliminating ambiguity. In several cases, it explains why certain implementation instincts were correct, while providing a clearer structural framing that can be relied upon going forward.
How to Read This Discussion
Each major component of the design is presented as a separate comment in this discussion:
Dance Test Framework Overview
A conceptual overview of the framework, its phases, and its core abstractions.
Test Harness Design Spec
Normative definitions for fixture support, execution support, commit semantics, and resolution behavior.
Test Step Authoring Guide
Prescriptive guidance for authors of new test steps, covering both adders and executors.
Test Case Authoring Guide
Guidance for test case authors on how to compose steps safely and idiomatically.
Taken together, these documents are intended to serve as the authoritative reference for ongoing and future work on the Dance Test Framework.
Once stabilized, this material will be promoted into the project’s Core Developer Documentation.
Beta Was this translation helpful? Give feedback.
All reactions