Skip to content

Enhancement backlog: candidates waiting on a decision or a measurement #860

Open

Description

@Yaraslaut

Enhancement candidates that are real but not ready to implement, collected in one place so the next decision is easy to see. Each entry says what it would add, what is blocking it (a decision or an experiment, not engineering time), and what makes it ready. The full evidence stays in the source issue, which is linked and closed as superseded. Re-open a source issue, or file a fresh one, once its blocker is answered. That issue then goes through triage on its own.

Checked against master @ 85a5280e (2026-10-03), by reading. Nothing below was re-built or re-run for this collection unless it says so.

Needs a design decision

Per-model storage a model reads synchronously on its own strand (from #854, area: journal)

  • Adds: one framework seam replacing four hand-rolled self-journals: SelfJournal in examples/lims and examples/crm, plus logAction with a synchronous read in examples/ledger and examples/kanban. It also stops kanban journalling each mutating action twice, which was observed once and inferred to still happen.
  • Blocked on: the seam as filed hands the model its own strand, which breaks "a model does not know the framework exists". The direction that fits is for the holder or registry to build the log with the model's strand as its owner and inject it, and for a holder that injected a log to skip its own auto-append. Open questions: is the injected type a journal:: type, and how does a model declare that it wants one without naming an executor?
  • Ready when: both questions are answered in docs/spec/journal (or the spec for the holder).

Profiling: a Tracy sink for morph::observe, and a statistics collector (Part 2 of #843, area: core)

  • Adds: each Metric sent to a fixed-name TracyPlot, and the TraceSink mapped to TracyMessage. Optionally, a Lightweight-style Snapshot() with latency histograms.
  • Blocked on: (a) morph::observe holds one sink per kind, so a Tracy sink would evict a host's own sink. Either add a tee/fan-out helper, or document "profiling builds only". (b) A histogram collector in include/morph contradicts observability.md: "No sampling/aggregation/histograms in morph". The spec owner has to choose between shipping it as an optional host-installed sink or keeping it in tests/bench.
  • Ready when: (a) and (b) are decided in docs/spec/core/observability.md. Part 1 of observability: Tracy zones behind MORPH_ENABLE_TRACY (prefixed MORPH_ZONE macros) and a capture job that proves they fire #843 (compile-time zones) does not depend on this and stays open as its own ticket.

Nested aggregates in DynamicForm (from #759, part (b), area: forms)

  • Adds: a sub-form for nested object members, lazily for recursive ones. Today a nested member is flattened to one text field.
  • Already done on master: the defect half. A member the form cannot represent is now marked unrepresentable and keeps the form short of ready (src/qt/forms/qml/DynamicForm.qml:30; docs/spec/forms/forms.md, "What ready claims").
  • Blocked on: choosing between drawing nested aggregates, declining the schema loudly, or keeping today's documented behaviour. No shipped action has a nested-aggregate member yet.
  • Ready when: that choice is made, or a shipped action needs a nested member.

Models over existing (legacy) objects (from #838, area: core)

  • Adds: a MaybeOwned<T>-style holder so that morph can run actions against an object it does not own.
  • Blocked on: which reading is meant. Reading A, a model that wraps the object, already works with no framework change: ModelHolder forwards constructor arguments, and registerModel<Model>(id, factory) is the dependency-injection seam. Reading B, the legacy object is the model, needs ModelHolder to stop holding the model by value, under the conditions in Models for the existing objects #838's triage comment (lifetime, one strand per ModelId).
  • Ready when: someone names the concrete type that needs Reading B and why Reading A does not fit it.

Client-side model and action discovery: one generic client for every rung (from #829, area: core, area: qt) — moved to #885

What still overlaps with core-cpp (from #807, area: core)

Parked performance work (re-entry is a measurement)

InMemoryReplayLedger::doLookup materialises its key inside the lock (from #728, area: offline)

  • Ready when: a profile shows doLookup on a hot path, or a heterogeneous lookup becomes available that removes the materialisation for free.

ModelRegistryFactory::create builds its lookup key (from #709, area: core)

  • Measured at one allocation per model instantiation.
  • Ready when: model instantiation shows up in a profile, or bench.alloc_budget starts counting it against a budget it exceeds.

How to use this issue: when a blocker above is answered, comment here, re-open the source issue (or file a fresh one) with the answer folded in, and tick the entry off. Do not implement directly from this list.

Activity

  1. Yaraslaut commented on Oct 3, 2026

    @Yaraslaut
    MemberAuthor

    Added: the four building blocks from #793 (area: core, area: forms)

    #793 measured where the ladder rungs' application code goes. The figures below are code-only, after its own correction comment. None of the four blocks is ready to build: each waits on a measurement or a contract decision.

    Required-ness in allRequiredEngaged: the flat walk and std::string

    • Adds: a validate() that more actions get for free. 39 of the 153 hand-written validate() bodies are exact drop-ins for the helper today (measured in examples: where the 42k lines of application code go, and the four building blocks that would remove most of it #793's third comment). 26 more rest on std::string::empty(). Two hand-write a delegation into a nested optional<Sub>.
    • Blocked on: a public-contract decision. include/morph/forms/forms.hpp:146-164 documents both the flat walk and the std::string exclusion as intended behaviour. Does an empty string count as "not filled in" for a required member? And should the walk descend into Sub, optional<Sub> and vector<Sub>?
    • Ready when: the forms spec owner decides both questions in docs/spec/forms/forms.md.

    A generic QML bridge (the largest block)

    • Adds: a replacement for hand-written type erasure. That is about 3,611 code lines across 22 *_qml_bridge files and 26 presenters, plus submitIfValid(const QString&, const QString&) hand-written seven times.
    • Blocked on: a measurement. Only about 635 lines are clearly mechanical (Qt macros, QVariant conversion, change signals). How much of the remaining 1,995 would survive generalization is unmeasured. Note that qt/forms: multi-model bridge routing core, retrofitting bookmarks' FormsBridge #835 already landed a multi-model bridge routing core.
    • Ready when: someone classifies those 1,995 lines and the removable share justifies a seam.

    A shared error vocabulary

    • Adds: one set of transport-level outcomes (validation failed, not found, forbidden, conflict) in place of 17 per-rung error classes and 276 throw sites.
    • Blocked on: invariant 3. The value type reports the fact, and the layer decides the policy. Nobody has shown the hierarchies can be shared without moving policy into the framework.
    • Ready when: a design states which outcomes are transport facts and leaves what a rung does about them to the rung.

    The handler opening (auth, validate, throw)

    #793's overall close condition still applies: a new rung of comparable scope written in materially fewer lines, measured the same way.

  2. Yaraslaut commented on Oct 4, 2026

    @Yaraslaut
    MemberAuthor

    Added: should a dropped connection stop its own running Task handlers? (area: core)

    • Today: RemoteServer::closeConnection is G0 for an execute in flight, as documented and measured in the Cancellation policy (docs/spec/concurrency_and_lifetimes.md, RemoteServer::closeConnection row). The running execute still answers into a connection that is gone, and only new lookups are refused. This is the specified behaviour, not a defect.
    • Adds: on closeConnection(cid), request stop on every cancellable Task run that cid admitted. Since core, wire, net, qt: a hello-negotiated cancel envelope, honoured by RemoteServer and sent by both client backends (#864, #865) #867 the server keeps exactly that per-connection table, the one a cancel is looked up in, so the mechanism is a loop over existing state.
    • Blocked on a decision: a disconnect is often transient. The client reconnects, and the work it started may still be wanted: an effect half-applied by a stopped handler, or a reply the client will re-ask for. Stopping on disconnect trades wasted server work for that risk. A related question: should it apply to every connection, or only when the transport reports a deliberate close rather than a dropped one?
    • Ready when: the owner of backend.md "Connection scopes" decides it and writes the rule there. The row in the Cancellation policy then moves from G0.
  3. Yaraslaut commented on Oct 7, 2026

    @Yaraslaut
    MemberAuthor

    The "generic client" entry (from #829) is answered and ticked off above. The design note is #877. Ready tickets: #878 (Bridge::executeJson), #879 (the hello handshake), #880 (measuring how much of one rung's QML the declarative vocabulary covers).

    Two things changed from #829's framing:

    Still open, and blocking the catalog ticket: whether discovery is filtered by the caller's session.


    Generated by Claude Code

  4. Yaraslaut commented on Oct 15, 2026

    @Yaraslaut
    MemberAuthor

    Triage: parked

    This issue is a collection of candidates, each with an explicit blocker and a "ready when". That is what triage: parked means. Nothing here is part of the declarative UI milestone (#885): its one declarative-UI entry was already moved to #885 on 2026-10-08. Re-checked 2026-10-15: no entry's re-entry condition has been met. Re-enter an entry when its "ready when" holds, per the instructions at the bottom of the issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: coreSubsystem: coreenhancementNew feature or requesttriage: parkedDeferred with an explicit re-entry condition

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions