You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
feat(server): make scripted spell services work-aware #264
A boolean answers too little. Cure disease can guarantee some cures while
other diseases may resist; identification can price per eligible item; and
restoration may perform several different kinds of work. The server should
therefore report bounded, spell-specific work—not money and not an arbitrary
random price.
For each named work component:
{0, 0} means the spell cannot beneficially change that state;
{0, N} means up to N units may be performed;
{N, N} with N > 0 is an exact amount; and
{M, N} with M > 0 guarantees at least M and permits at most N.
Unsupported is separate from an empty/zero range. Healing points, diseases,
curses, depleted attributes, and identified items remain distinct named units;
they are not summed into a meaningless universal score.
Identification is a first-class consumer. With current rules its preflight is
usually exact—the number of eligible unidentified items in marked, container,
or normal inventory scope—and the actual result is the number identified. A
future uncertain identification mechanic can widen the same range without
changing the service contract.
Required sub-issues
#265 — native,
side-effect-free bounded work queries and shared selection predicates;
#248 — structured actual-work
outcomes from spell and identification casts; and
#267 — bounded persistent
provider credit and repayment.
All four are required before content#106 resumes its final implementation and
validation cycle.
The nonblocking authored identification consumer is tracked in atrinik/content#217. It will
replace Smith's flat fee after this native contract lands, but does not delay
the temple-specific consumer.
Ordered implementation plan
Use isolated worktrees for each workstream. One Classic integration owner must
assemble the final branch, resolve shared-file changes, and run whole-diff
validation. Contributors may hand off reviewable commits in parallel, but must
not race independent edits to the positional plugin hook ABI or Python method
registry.
contract freeze
|
#265
/ \
#266 #248 <- parallel group A
\ /
#267
|
Classic integration, latest-head review, and maintainer merge
/ \
#106 #217 <- parallel group B; #217 is nonblocking
Stage 0 — freeze the shared contract
Resolve the work-unit vocabulary, bounded native/Python result shapes,
supported modes, effective-provider-level semantics, and the distinction
between unsupported, rejected, executed no-op, and beneficial work.
Freeze common fixtures that query and actual-outcome tests will share,
including probabilistic disease, identification scopes, heterogeneous
restoration, and state drift.
Resolve the append-only plugin ABI strategy and reserve one integration
owner for shared hook-table and Python-registry edits.
One workstream owns the shared spell applicability/selection predicates, work
units, and side-effect-free bounded query. Because #265 and #248 both need the
spell handlers and their declarations, outcome instrumentation must not fork
those predicates before this foundation is stable.
While #265 is active, other contributors may prepare black-box Python tests and
documentation for #266, outcome fixtures for #248, and persistence/ledger test
fixtures for #267. Those preparations must not change the unsettled production
contract.
Exit gate: focused native tests prove fixed bounds, no gameplay RNG advance,
no side effects, and exact parity with the current selection rules.
Workstream A1 — feat(server): expose spell work quotes to Python #266: implement the Python query adapter, result
conversion, documentation, and runtime tests. This workstream owns the query
side of the Python object methods but does not independently reorder or
finalize the shared plugin hook table.
Workstream A2 — feat(server): expose actual spell work outcomes to Python #248: implement dispatcher-carried actual outcomes and
handler instrumentation, preserving legacy native and Python Cast() and CastIdentify() behavior. It owns the native dispatcher/outcome path and
tests; the integration owner adds or reconciles its Python methods at the
shared binding join.
Join gate: a single integrator appends ABI hooks, reconciles shared headers and
the Python method registry, rebuilds every plugin, and proves for unchanged
state that every actual component lies within the corresponding queried range.
Only then are #266 and #248 independently complete.
Implement the bounded npc_id-keyed debt ledger, durable character storage,
credit preauthorization, atomic cash/debt settlement, exact repayment, and the
narrow native/Python surface. Design and fixtures may have been prepared
earlier, but production integration starts after #265/#266/#248 settle the
quote and actual-work contracts.
Exit gate: tests prove no charge or debt for zero work; no liability beyond the
confirmed maximum; isolation between providers; full-repayment gating;
bounded/checked arithmetic; bank-funded payment; reconnect and save-failure
behavior; and idempotence under repeated confirmation.
Stage 4 — Classic integration and readiness gate
Assemble the four workstreams on one current Classic integration head in
dependency order; stacked leaf commits are acceptable, but the final
review target must contain the whole diff.
Run focused native, Python-plugin, persistence, and compatibility tests,
then the repository-required server build/test matrix, sanitizers or
equivalent required diagnostics, and git diff --check.
Repeat independent whole-diff review/fix cycles until no known actionable
findings remain.
Re-run required GitHub checks on the latest reviewed head and leave the
Classic PR ready to merge. The delivery agent does not merge it.
A maintainer merge is the gate into content adoption. Validation against an
unmerged Classic branch is useful evidence, but does not replace refreshing
the consumer onto the exact merged Classic main.
Stage 5 — establish the content integration seam
After the Classic merge, refresh the content worktrees and derived Classic
runtime against the exact merged API. Assign one owner to any shared quote,
confirmation, price-from-work, debt-call, or schema-projection helper before
the two consumers diverge.
Provider capability remains authored content metadata: temple NPCs use the
custom ADS key temple_service_rank, and debt/confirmation use canonical npc_id. No hard-coded provider registry or new server-recognized object field
is introduced.
Stage 6 — parallel group B: content#106 and content#217
Workstream B1 — content#106 / PR feat(protocol)!: disclose immutable world-profile metadata before and after join #211: remove the hard-coded temple
provider list; author and validate provider rank/identity metadata; quote
exact or ranged work; revalidate on confirmation; cast once; settle from
actual work; handle provider-specific repayment; and cover every temple
provider and service.
Workstream B2 — content#217: replace Smith's flat identification fee with
a quote based on eligible unidentified items and a final charge based on
items actually identified, using the same confirmation and provider-debt
contract.
The workstreams may proceed concurrently after the shared seam is fixed. If
they touch the same helper, validator, schema projection, or provider metadata,
one owner lands that shared change and the other rebases before final
validation. #217 is explicitly nonblocking: it must not delay #106 or PR #211
unless it exposes a correctness gap that #106 itself must fix.
Stage 7 — consumer validation and handoff
Each content workstream must pass its own aggregate/schema/provider tests and
whole-diff review. Then run separate deterministic derived-Classic scenarios
for temple treatment and identification, covering incapable and differently
ranked providers, zero/exact/ranged quotes, drift and reconfirmation, partial
and full work, unaffordable actual work, cash/debt splits, repayment,
reconnect/persistence, repeated confirmation, and clean shutdown.
Re-run all required GitHub checks on each latest reviewed content head. PR #211
may become ready independently of #217; neither PR is merged by the delivery
agent.
Completion semantics
Because content#106 is formally blocked by #264, the Classic initiative closes
when all four required Classic sub-issues are merged and the Stage 4 gates
pass. Full temple adoption remains tracked by content#106/PR #211, and Smith
identification by content#217; these downstream consumers do not create a
circular closure dependency for #264.
Quote and settlement lifecycle
Query work against an explicit target, cast/identify mode, and provider
effective level.
Let content apply its service-specific formula to each component and display
a minimum/maximum monetary quote. Exact work produces an exact quote.
Bind the state evidence, work range, price range, provider npc_id, provider
rank, existing provider debt, and maximum permitted new debt into explicit
confirmation.
Re-query on confirmation; drift requires a new quote and does not cast.
Execute once and receive the actual named work components.
Compute the actual charge from actual work. Zero beneficial work costs zero;
the charge can never exceed the confirmed maximum.
Collect available funds and, only when the player explicitly accepted the
exposure, record the remainder as debt to that provider. That provider
requires full repayment before another paid service.
There is no unpriced cast, random price variance unrelated to work, interest,
or charge for messages, sounds, particles, attempted work, or a no-op.
Initial supported scope
remove depletion;
cure poison, confusion, and disease;
remove curse and remove damnation, preserving NPC-versus-player inventory
scope and effective-level rules;
normal, container, and marked identification; and
minor healing, greater healing, and restoration using separate named HP,
symptom, condition, effect, and food components rather than one scalar.
Unsupported offensive, projectile, area, creation, charging, and
transformation spells must fail explicitly instead of guessing.
Acceptance criteria
Native and Python query APIs return bounded named work components and
distinguish unsupported from zero work.
Queries are side-effect-free, do not advance gameplay RNG, and preserve
objects, events, messages, sounds, metrics, combat state, and cooldowns.
Queries and execution share predicates so scope, capability, and work
units cannot drift.
Cast mode, target scope, effective provider level, and identification mode
are explicit and match execution semantics.
Outcome-bearing cast APIs report whether a handler executed and the exact
beneficial work performed without changing legacy Python Cast() return
compatibility.
Content can derive an exact or ranged quote, then derive the final charge
from actual work with zero charge/debt for zero work and a hard confirmed
maximum.
Provider debt is keyed by stable npc_id, bounded, interest-free,
durable, explicitly authorized, and collectible only by that provider.
Focused tests cover exact and ranged work, probabilistic disease without
query-time RNG advancement, curse/damnation capability, identification
counts/scopes, heterogeneous restoration, quote drift, partial funding,
repayment, reconnect, and repeated confirmation.
The Classic server build, Python-plugin suite, repository-required checks,
and git diff --check pass.
Python documentation and tests prove that a consumer can derive cash plus
debt from actual work. Full content#106 adoption remains tracked
downstream and does not gate closing feat(server): make scripted spell services work-aware #264.
Boundaries
Money formulas, newcomer policy, provider ranks, and authored provider IDs
remain owned by content#106 and later service-specific content issues.
The query is not a dry-run cast and does not predict later projectile/combat
outcomes.
Outcome
Give Classic a server-owned, script-facing contract for variable-work spell
services to:
provider-specific debt when actual work costs more than available funds.
This is a blocking dependency of
atrinik/content#106 and its draft implementation PR,
atrinik/content#211.
Why work ranges are the contract
A boolean answers too little. Cure disease can guarantee some cures while
other diseases may resist; identification can price per eligible item; and
restoration may perform several different kinds of work. The server should
therefore report bounded, spell-specific work—not money and not an arbitrary
random price.
For each named work component:
{0, 0}means the spell cannot beneficially change that state;{0, N}means up toNunits may be performed;{N, N}withN > 0is an exact amount; and{M, N}withM > 0guarantees at leastMand permits at mostN.Unsupported is separate from an empty/zero range. Healing points, diseases,
curses, depleted attributes, and identified items remain distinct named units;
they are not summed into a meaningless universal score.
Identification is a first-class consumer. With current rules its preflight is
usually exact—the number of eligible unidentified items in marked, container,
or normal inventory scope—and the actual result is the number identified. A
future uncertain identification mechanic can widen the same range without
changing the service contract.
Required sub-issues
side-effect-free bounded work queries and shared selection predicates;
those work ranges;
outcomes from spell and identification casts; and
provider credit and repayment.
All four are required before content#106 resumes its final implementation and
validation cycle.
The nonblocking authored identification consumer is tracked in
atrinik/content#217. It will
replace Smith's flat fee after this native contract lands, but does not delay
the temple-specific consumer.
Ordered implementation plan
Use isolated worktrees for each workstream. One Classic integration owner must
assemble the final branch, resolve shared-file changes, and run whole-diff
validation. Contributors may hand off reviewable commits in parallel, but must
not race independent edits to the positional plugin hook ABI or Python method
registry.
Stage 0 — freeze the shared contract
supported modes, effective-provider-level semantics, and the distinction
between unsupported, rejected, executed no-op, and beneficial work.
including probabilistic disease, identification scopes, heterogeneous
restoration, and state drift.
owner for shared hook-table and Python-registry edits.
npc_id, ledger bounds, preauthorization, settlement,repayment, and persistence semantics before irreversible spell work is
implemented against them.
Exit gate: the four sub-issue contracts agree on names and invariants; no
parallel branch needs to invent a competing representation.
Stage 1 — land #265 as the native foundation
One workstream owns the shared spell applicability/selection predicates, work
units, and side-effect-free bounded query. Because #265 and #248 both need the
spell handlers and their declarations, outcome instrumentation must not fork
those predicates before this foundation is stable.
While #265 is active, other contributors may prepare black-box Python tests and
documentation for #266, outcome fixtures for #248, and persistence/ledger test
fixtures for #267. Those preparations must not change the unsettled production
contract.
Exit gate: focused native tests prove fixed bounds, no gameplay RNG advance,
no side effects, and exact parity with the current selection rules.
Stage 2 — parallel group A: #266 and #248
After #265 stabilizes:
conversion, documentation, and runtime tests. This workstream owns the query
side of the Python object methods but does not independently reorder or
finalize the shared plugin hook table.
handler instrumentation, preserving legacy native and Python
Cast()andCastIdentify()behavior. It owns the native dispatcher/outcome path andtests; the integration owner adds or reconciles its Python methods at the
shared binding join.
Join gate: a single integrator appends ABI hooks, reconciles shared headers and
the Python method registry, rebuilds every plugin, and proves for unchanged
state that every actual component lies within the corresponding queried range.
Only then are #266 and #248 independently complete.
Stage 3 — implement #267 against the settled APIs
Implement the bounded
npc_id-keyed debt ledger, durable character storage,credit preauthorization, atomic cash/debt settlement, exact repayment, and the
narrow native/Python surface. Design and fixtures may have been prepared
earlier, but production integration starts after #265/#266/#248 settle the
quote and actual-work contracts.
Exit gate: tests prove no charge or debt for zero work; no liability beyond the
confirmed maximum; isolation between providers; full-repayment gating;
bounded/checked arithmetic; bank-funded payment; reconnect and save-failure
behavior; and idempotence under repeated confirmation.
Stage 4 — Classic integration and readiness gate
dependency order; stacked leaf commits are acceptable, but the final
review target must contain the whole diff.
then the repository-required server build/test matrix, sanitizers or
equivalent required diagnostics, and
git diff --check.findings remain.
Classic PR ready to merge. The delivery agent does not merge it.
A maintainer merge is the gate into content adoption. Validation against an
unmerged Classic branch is useful evidence, but does not replace refreshing
the consumer onto the exact merged Classic
main.Stage 5 — establish the content integration seam
After the Classic merge, refresh the content worktrees and derived Classic
runtime against the exact merged API. Assign one owner to any shared quote,
confirmation, price-from-work, debt-call, or schema-projection helper before
the two consumers diverge.
Provider capability remains authored content metadata: temple NPCs use the
custom ADS key
temple_service_rank, and debt/confirmation use canonicalnpc_id. No hard-coded provider registry or new server-recognized object fieldis introduced.
Stage 6 — parallel group B: content#106 and content#217
provider list; author and validate provider rank/identity metadata; quote
exact or ranged work; revalidate on confirmation; cast once; settle from
actual work; handle provider-specific repayment; and cover every temple
provider and service.
a quote based on eligible unidentified items and a final charge based on
items actually identified, using the same confirmation and provider-debt
contract.
The workstreams may proceed concurrently after the shared seam is fixed. If
they touch the same helper, validator, schema projection, or provider metadata,
one owner lands that shared change and the other rebases before final
validation. #217 is explicitly nonblocking: it must not delay #106 or PR #211
unless it exposes a correctness gap that #106 itself must fix.
Stage 7 — consumer validation and handoff
Each content workstream must pass its own aggregate/schema/provider tests and
whole-diff review. Then run separate deterministic derived-Classic scenarios
for temple treatment and identification, covering incapable and differently
ranked providers, zero/exact/ranged quotes, drift and reconfirmation, partial
and full work, unaffordable actual work, cash/debt splits, repayment,
reconnect/persistence, repeated confirmation, and clean shutdown.
Re-run all required GitHub checks on each latest reviewed content head. PR #211
may become ready independently of #217; neither PR is merged by the delivery
agent.
Completion semantics
Because content#106 is formally blocked by #264, the Classic initiative closes
when all four required Classic sub-issues are merged and the Stage 4 gates
pass. Full temple adoption remains tracked by content#106/PR #211, and Smith
identification by content#217; these downstream consumers do not create a
circular closure dependency for #264.
Quote and settlement lifecycle
effective level.
a minimum/maximum monetary quote. Exact work produces an exact quote.
npc_id, providerrank, existing provider debt, and maximum permitted new debt into explicit
confirmation.
the charge can never exceed the confirmed maximum.
exposure, record the remainder as debt to that provider. That provider
requires full repayment before another paid service.
There is no unpriced cast, random price variance unrelated to work, interest,
or charge for messages, sounds, particles, attempted work, or a no-op.
Initial supported scope
scope and effective-level rules;
symptom, condition, effect, and food components rather than one scalar.
Unsupported offensive, projectile, area, creation, charging, and
transformation spells must fail explicitly instead of guessing.
Acceptance criteria
distinguish unsupported from zero work.
objects, events, messages, sounds, metrics, combat state, and cooldowns.
units cannot drift.
are explicit and match execution semantics.
beneficial work performed without changing legacy Python
Cast()returncompatibility.
from actual work with zero charge/debt for zero work and a hard confirmed
maximum.
npc_id, bounded, interest-free,durable, explicitly authorized, and collectible only by that provider.
query-time RNG advancement, curse/damnation capability, identification
counts/scopes, heterogeneous restoration, quote drift, partial funding,
repayment, reconnect, and repeated confirmation.
and
git diff --checkpass.debt from actual work. Full content#106 adoption remains tracked
downstream and does not gate closing feat(server): make scripted spell services work-aware #264.
Boundaries
remain owned by content#106 and later service-specific content issues.
outcomes.
remain tracked by Implement native commerce services server#52.