Product backlog — inbox
Table of Contents
This is the inbox bucket of the product backlog. Every capture created
via compass capture --note "..." lands here. Inbox captures have not yet
been triaged — they are ideas or observations waiting to be assessed.
At triage, move each capture to one of:
next/— ready to pull into an upcoming sprint.deferred/— real but not soon; long-term horizon.discarded/— explicitly decided against.
Or promote directly to a story via compass capture promote <slug>.
The table below is regenerated by
projects/ores.codegen/scripts/regenerate_backlog_indexes.py — do not
edit the rows by hand. To add a capture, run
compass capture --note "..." and re-run the script.
1. Inbox captures
| Capture | Tags | Description |
|---|---|---|
| –reset-goldens deletes the golden dataset and nothing recreates it | ore testing codegen |
Twelve golden round-trip suites under projects/ores.ore/core/tests/ print "Golden file missing (run with –reset-goldens to bootstrap)" when a golden is absent. The flag is real, but it lives on projects/ores.codegen/scripts/xsdcpp_generate_ore.sh, not on the test binary, and all it does is delete assets/test_data/golden_dataset/Products. No suite writes a golden back: each one calls REQUIRE(std::filesystem::exists(gpath)) and fails. So following the message destroys the dataset it points at, and the message never names the command it means. Only xml_example_coverage_tests.cpp writes missing goldens, and only when ORES_BOOTSTRAP_GOLDENS is set. Pick one: give the flag a bootstrap mode the twelve suites honour, or change the message to name the command that recreates the dataset. |
| A current-state remove reports removed for a delete that matched nothing | codegen database |
execute_delete_query discards the affected-row count, so a generated current-state repository returns remove_status::removed although the tenant-scoped DELETE matched no rows; the audit-column path reads the row back and does not have this hole. |
| A junction's timestamp column is not mapped to sqlgen::Timestamp | codegen generator-defect junction tech_debt |
The junction C++ entity template emits a raw std::chrono::time_point for a junction's own timestamp column, which sqlgen cannot serialise, so ores.assets.image_tag fails to compile in image_tag_repository.cpp. The same column type on a domain entity is correctly emitted as std::optional<sqlgen::Timestamp<…>> (compare ores.compute host last_rpc_time with ores.assets image_tag assigned_at). The entity path sets is_optional_timestamp and is_required_timestamp in core.py around line 2418; the junction column-decoration block near line 4595 sets is_int, is_uuid, is_optional_uuid and is_date but never the timestamp flags. Adding them there did NOT change the generated output, so the C++ context for a junction is built on another path that still needs finding. Per G05 the fix belongs in the codegen source with a regression test in the codegen suite. |
| A notify trigger collides when a natural key is called key | codegen generator-defect notify-trigger tech_debt |
The notify-trigger template declares changed_key jsonb and derives a changed_<natural key column> variable for the entity's key. An entity whose natural key column is called key therefore declares changed_key twice, once text and once jsonb, and create function fails with duplicate declaration. Found on ores.assets.image. Fix belongs in the template by namespacing the payload variable, but that rewrites the 210 generated notify triggers tree-wide plus iam, so the assets story renamed its column to code and filed this instead. |
| A record's measured numbers go stale when trunk moves under the branch | agile process verification |
Two independent verification rounds failed the ores.service record for the same reason: counts measured on a tree that trunk then moved out from under. The branch was rebased four times while it waited, and each advance changed a consumer count, a gate's component count or a suite's size. Nothing in the record said which tree a number was measured on, so the drift was silent until a verifier re-ran the commands. |
| A running fleet reports as missing and start times out | In a sandboxed session compass services status and compass bearings classify every unit as missing even while the whole fleet is up: systemctl –user cannot reach the user manager and _unit_active_state returns missing rather than reporting the query failure. That reads as 'nothing is running' when the opposite is true. A follow-up compass services start –use-busctl then waits the full 301s and reports '23/23 still not ready', because _wait_for_logs looks for a Service ready. line appended after start_pos and the already-running processes never log another one; the logs still hold only the previous run's readiness line. The timeout also suggests compass services stop, inviting the operator to kill a healthy fleet. Confirmed on this worktree: status said missing=24 while ports 20205 and 20202 were listening and the services were logging normally. Fix is to distinguish 'query failed' from 'unit missing', and to treat an already-active unit as ready instead of waiting for a fresh log line. | |
| A test file whose cases are compiled away reports green | testing codegen check lint |
A TEST_CASE that never reaches the compiler is invisible: the file builds, ctest passes, and only the case count betrays it. The ores.service readiness tests sat inside a BOOST_ASIO_HAS_LOCAL_SOCKETS guard placed above every boost::asio include, so the macro was undefined when the guard was read and the file compiled to nothing. ctest reported 39 cases where the sources held 42. Nothing compares the two. |
| Account secrets reach the browser in the accounts wire shape | security iam accounts bug |
The account wire shape carries password_hash, password_salt and totp_secret to the browser. |
| Add a batch-scoped input key helper to compute_storage | compute shell |
Add an input_key helper for the batch-scoped shared input bundle so dispatch and download do not hand-build the key. |
| An entity's key on the wire cannot be composite | codegen protocol keys |
declared_key_field returns one field, so an entity whose identity is a pair cannot address a row and its by-key reads ignore half the key. |
| Assert the history field projection covers every member | codegen history diff tests |
The generated render_<entity>_fields projection has no coverage check at all, so a field the projection template does not handle is silently absent from every diff; the one hand-written test that guarded this was deleted as a spot test. |
| Authentication is optional at runtime on both transports | security auth http nats defect |
The HTTP server skips its whole auth middleware block when no authenticator is configured, and warns rather than refusing; the NATS request context comes back unauthenticated when no verifier is configured; and has_permission returns true for an empty permission list to keep pre-RBAC tokens working. A deployment that forgets to configure either transport therefore accepts everything, and nothing reports it. |
| Batch 3 of the badge/image/plain-text rendering rollout (tas… | Batch 3 of the badge/image/plain-text rendering rollout (task 08547F5A, closing): the audit/classification story is closed and batches 1-2 (badge_key wiring + regenerated entities) shipped. What remains from the implementation task's acceptance is the plain-text documentation leg: for each entity the audit classified as plain-text (no badge/image wiring), record a one-line documented reason per entity, and publish the plain-text-vs-badge-vs-image policy note. Small, documentation-only, no codegen changes expected. | |
| Bring the no-audit-columns entities over with the domain member gone | codegen marketdata |
:no_audit_columns: now drops the recorded_at domain member as well as the audit tail, so the marketdata entities that state it and the two hand-written tests that populate it need regenerating together when that component is brought over. |
| Change-password ignores the current password | security iam authentication bug |
iam.v1.accounts.change-password never reads current_password; a stolen token is enough to set a new one. |
| check_component_drift.py corrupts a branch that is behind main | Running projects/ores.codegen/scripts/check_component_drift.py with the component flag on a branch whose codegen templates are older than main's regenerates the whole component in place from the stale templates. It rewrote 67 tracked files and materialized 44 untracked ones under ores.trading, reverting newer generated shapes (the trade sub-struct split) and dropping hand-written protocol types (export_portfolio_request). A build running concurrently then failed on the clobbered headers. The script is only safe for components in its KNOWN_DRIFT_FREE registry, which requires zero drift; any other component needs the single-entity compass codegen entity generate command instead. Recovered by stashing the fallout; a matching stash entry dated 2026-09-11 shows this has bitten before. | |
| Codegen can own a junction's service, so the hand-written junction registrars are duplicates | codegen junction tech_debt |
A junction model whose nats-handler, nats-sub-registrar and service flags are nil generates no handler, yet its shell-command flag generates commands that publish to per-operation subjects. Nothing serves them. Setting the three flags to true makes codegen emit the handler, the registrar and the service, as ores.assets.image_tag now proves. ores.iam.account_party carries the same nil flags and is served by a hand-written registrar in ores.iam/core/src/messaging/registrar.cpp, so that registrar is a hand-written duplicate of a generatable artefact. |
| Codegen template cpp_domain_type_table.cpp.mustache streams … | Codegen template cpp_domain_type_table.cpp.mustache streams enum columns raw (table << iter_var.column), which cannot compile: generated enum classes have no operator<<, only to_string(). trade_type_table.cpp is the first entity table with an enum column (product_type, added in the badge batch-2 trading rollout); it was hand-rendered via to_string(). The template needs an is_enum branch like the existing is_bool/is_optional/is_uuid branches. | |
| Codegen: auto-derive most Qt drawer fields instead of requir… | Codegen: auto-derive most Qt drawer fields instead of requiring them spelled out per entity. domain_include/domain_class/protocol_include are pure functions of component and entity_singular. get/save/delete/history request/response/message_type class names are pure functions of entity_plural/entity_singular plus the standard dq::messaging naming convention already used identically by every entity's generated protocol header. collection_name/item_var are typically just entity_plural/entity_singular themselves. Only key_field, has_uuid_primary_key, settings_group, window_title, icon, and the has_ Qt knobs are genuinely entity-specific and need spelling out. Discovered while manually authoring near-identical Qt drawers for catalog, change_reason, change_reason_category, data_domain, and dataset_bundle – the boilerplate was copy-pasted with only entity-name substitutions each time, exactly the drift-prone pattern profiles exist to eliminate. | |
| Codegen: has_parent_id without its two column properties fails silently | codegen variability guard |
Turning has_parent_id on emits a recursive-CTE hierarchy function that names the entity's parent_id_column and hierarchy_name_field. If the model sets the feature without those two properties, the function is generated with empty column names and nothing fails. Eleven entities sat in exactly that state, hidden only by a separate namespace defect. A loader check that rejects the combination would turn the silence into a hard error at regeneration time. |
| compass build fails when only busctl reaches the user manager | compass build sandbox systemd tech_debt |
The build wraps cmake –build in systemd-run –user –scope, but systemd-run uses the plain transport, so a sandbox where only busctl reaches the user manager fails the build outright. |
| compass build scopes itself with systemd-run, which the sandbox cannot reach | Inside the DSH sandbox, compass build fails before it compiles anything: 'Failed to connect to user scope bus via local transport: No data available'. The memory-cap scope is added with a raw systemd-run –user –scope, which needs the private user-manager socket at $XDG_RUNTIME_DIR/systemd/private. That socket is unreachable here, which is exactly why systemctl_bus.py exists: it drives the same user manager over busctl on the D-Bus session bus, and that path works. So the detection and the action disagree – _has_user_systemd() probes through the shim and answers yes, and the scope creation then fails through the raw call. Measured: 'busctl –user list' succeeds unprivileged, 'systemctl –user show-environment' fails, 'systemd-run –user –scope' fails. Two candidate fixes: route the scope through the shim, or make the check probe what it is about to do and run unscoped when it cannot. Workaround in use is an escalated shell, which is a policy ask every build should not need. | |
| Compass has no way to create a story or task without creatin… | compass agile tech-debt paperwork |
Compass has no way to create a story or task without creating a branch. 'compass story new' unconditionally runs 'git switch -c branch base' (see _scaffold_and_branch in compass.py) with no opt-out flag; 'compass task new' creates the branch object without switching (worktree stays put – the start vs add distinction), but still creates a branch; only 'compass task start' switches. There is no branch-free scaffold path at all. Triggered while doing pure paperwork: creating an umbrella story under sprint 25 to group all trading-instrument-to-codegen port tasks, and re-parenting the existing port tasks (pilot equity_position_instrument, task 91ED2388 via PR 1857; the 5-entity batch plus remaining conversions, task F560ADDF via PR 1874; eventing facet work) into it – 'compass task move' already supports relocating tasks between stories UUID-intact, but creating the target story forces a branch switch, which is hazardous on shared mid-flight feature branches with unmerged commits (here: feature/codegen-nats-eventing-and-integration-tests). The manual workaround is to hand-author story.org plus sprint-table row plus task orgs plus regenerate indexes, all of which the scaffold does by itself. Desired: a –no-branch flag (or a story add / task add subcommand) that scaffolds the org files and sprint row without touching git, mirroring how 'task new' already avoids the switch. |
| compass lint runs one of the six checks in the doc-lint job | ci documentation tooling tech_debt |
The doc-lint job runs six checks and only the first is compass lint; the other five are committed build scripts the workflow invokes by path. A contributor who runs the verb named after the job gets a green result while CI can still fail, and the trigger filter names only the generate_skill scripts. |
| compass site page publishes paths the full build excludes | compass site build |
compass site page hands the changed files to ores-build-page.el by name, and that path calls org-publish-file directly, so it never consults the site:pages project's :exclude regex. A page under an excluded path is therefore published by compass site page while a full build skips it. Observed: with projects/ores.dsh_kanban/test/fixtures added to the exclude regex, the page builder still published 65 fixture pages, and only stopped once the named-file loop was given its own exclusion test. The fix is to apply the project exclusion in that loop, or to route named files through the project machinery. |
| compass story new –kind hotfix flips the story to STARTED b… | compass story new –kind hotfix flips the story to STARTED but leaves its row in the sprint's Hotfixes table at BACKLOG, so the sprint table disagrees with story.org until the row is edited by hand. Observed on the main_ci_red hotfix: the scaffold output printed 'state: BACKLOG -> STARTED' and 'story state -> STARTED', yet the sprint row stayed BACKLOG. Either update the sprint row when the story state flips, or leave the story at BACKLOG until task start so the row is written once. | |
| compass task done silently skips the story row when a Decisions bullet names the task first | Closing a task can leave its row in the story * Tasks table still open, and the command reports no error. The task flips to DONE, the journal is stamped, and the story row keeps its old state. | |
| Consolidate the duplicated tarball archivers into one shared utility | Move the libarchive pack/unpack archiver to a shared home (ores.utility) and consolidate the two duplicate implementations | |
| Cover the 43 ORE trade types that no example exercises | ore trading testing coverage |
43 of the 122 oreTradeType values in the regenerated domain.hpp appear in no XML under external/ore/examples/, so no example-derived test can ever reach them. The example-walking golden test added with the v17 sync cannot cover them: it derives its cases from the examples tree. Split by what is missing. Four types already have hand-written ores.ore code and need a fixture only: BondFuture and BondPosition have a mapper, BondPosition and EquityOptionPosition have a test reference, and Failed is the enum sentinel handled in exporter.cpp and importer.cpp. The other 39 have no example, no mapper, and no test, so each needs both. The seven v17 types are a subset of that 39 and are tracked separately at task 64CB13D8. |
| ctest can report green when a Catch2 binary fails to register its cases | ORES_CATCH2_ARGS in projects/CMakeLists.txt passes –filenames-as-tags, and that flag takes a Catch2 code path which skips duplicate-test-case detection. A component suite with duplicated case names ran 16 cases and exited 0 under ctest, while the same binary refused to run under –list-tests and under a bare run. The estate has one test binary per component, so any registration fault can hide behind a green ctest. | |
| Delete can terminate the system tenant | security iam tenancy bug |
The tenant delete rule sets status='terminated' with no system-tenant guard, unlike every dedicated lifecycle function. |
| Desk-facing product type | domain trading modelling product |
ORE Studio models products at ORE's trade-type granularity, which is a pricing-level abstraction; a desk treats several configurations of one trade type as distinct products. |
| Document the mechanisms that only exist in the code | documentation codegen testing |
Two generated and testing mechanisms work but appear nowhere in the knowledge base — the per-entity JSON stream operator, and the test logging switches. Both were only discoverable by reading source. |
| Durable documents link into agile content | documentation zettelkasten graph hygiene |
Knowledge, meta and recipe documents hold org-roam links into stories, tasks, sprints and captures, which the one-way rule forbids. Around 140 links across some 70 files; compass lint reports the live count. Sweep them, absorbing provenance into the durable page where the link carries information rather than deleting it. |
| Evaluate migrating codegen mustache templates to Handlebars | codegen templates tooling tech-debt |
Assess whether ores.codegen's pystache-based templates should move to Handlebars for real boolean-logic helpers, reducing hand-computed OR-flags in core.py. |
| Explore model validation support using ORE output reports | model_validation pricing ore ore_studio sprint_25 v0 |
ORE exports structured pricing reports (flows.csv, additional_results.csv, curves.csv) that allow independent NPV reconstruction without source-code access — cashflow-level discount factors, forward rates, and model parameters are all exposed. Investigate how ORE Studio can consume these outputs to build model validation workflows: benchmarking, vendor comparison, discrepancy drill-down. |
| Expose the telemetry stats surface | telemetry protocol codegen |
Four telemetry_repository reads and their SQL functions have no caller: the stats surface is implemented and tested but has no subject, so nothing can ask for it. |
| Fail the CMake file-list check when a component has no unit to check | codegen cmake testing tech_debt |
regenerate_cmake_component_files.py –component ores.analytics –check exits 0 having inspected no file: ores.analytics is a modeled component with a component_overview.org but no src/ or tests/, so regenerate_one skips both units and the run prints 'All component_files.cmake up to date.' The guard that rejects an unknown slug does not cover a known component with zero CMake units, so a group name reads as evidence when it is a false green. |
| Finish the eventing payload migration, or read the legacy shape | eventing codegen sql tech_debt |
postgres_event_source parses every database notification with rfl::json::read<entity_event_notification>, whose struct requires event_id, entity, key, action, version, occurred_at and tenant_id, and returns on a parse failure. 86 of the 209 *_notify_trigger_create.sql files emit that canonical shape and 123 still emit the older {entity, timestamp, entity_ids, tenant_id}. Every notification from the 123 fails to parse, so those entities publish no entity_change_event at all. Observed as 322 parse failures in the trading service log and as twenty-one failed eventing assertions in ores.trading.core.tests. |
| Fix compass pr create/merge task misattribution on shared branches | compass tooling bug agile |
compass pr create/pr merge pick the wrong task doc when multiple tasks share the same #+branch value, silently editing an unrelated task or blocking merge on its state. |
| Fix the shell recipe sentinel for optional columns | codegen shell recipe tech_debt |
sentinel_value in org_loader.py falls back to _none for a cpp_type it does not know, and std::optional<…> has no entry. The shell's absent-value token is '-' (ores.shell.api command_token.hpp absent_token), so a generated recipe script for an optional uuid aborts with 'Invalid value for <field>: none' before it reaches the service. Observed on three of the 40 generated analytics scripts during the ores.analytics clean pass: pricing_model_product_parameters add, set and put-many. iam carries seven optional uuid columns, so the fix changes iam's committed recipes and script library too and belongs on a codegen story, with a regression test. |
| Four iam shell commands have no recipe after the stale documents were deleted | iam shell docs recipe |
Deleting seventeen stale hand-written recipe documents left accounts info, accounts history, accounts logout and tenants history with no recipe at all, because each was documented only by one of the deleted pages. Their commands work; what is missing is a recipe written against current fixtures. |
| Generate equality that ignores the columns the store stamps | codegen domain equality tests |
The generated operator== is defaulted over every member, so a record read back from the store never equals the one that was written; the audit columns, the version, and for system-owned rows the tenant id are all rewritten on write. Capture the decision and the C++ options. |
| Give a table-less record a way to suppress the audit envelope | codegen variability profile |
The table-less-record profile cannot reach :no_audit_columns:, and no flag suppresses the audit recorded_at member, so a generated table-less record still carries a timestamp it has no use for. |
| House-wide error handling convention: std::expected vs exceptions | codegen modernisation cpp error-handling |
Decide and document one error-signalling convention across components; synthetic's audit flagged ~60 throw sites with no house standard. |
| IAM handlers skip their permission checks | security iam authorization bug |
Role assignment and the account and login reads check no permission, though the permission codes exist. |
| iam.v1.accounts.list rejects a well-formed request from a valid session | A logged-in session with a party cannot list accounts. The server answers iam.v1.accounts.list with bad_request, which aborts verify-login.ts before its last three checks run. | |
| Inventory system-scoped entities and rationalise tenant scoping strategy | Currently system-scoped reference data is handled inconsistently: some entities copy data per tenant during provisioning, others rely on a system-tenant context switch. We need an architectural audit. | |
| IR curve feed synthetic.2026realistic.usdsofrfomc (FOMC-date… | IR curve feed synthetic.2026realistic.usdsofrfomc (FOMC-dated USD-SOFR segment) publishes 0: every tick fails with curve_instrument_pricer: year_fraction must be positive (ores.synthetic.api.ir_curve_feed, SYNTHETIC CURVE PUBLISH FAILED). Producer-side pricing defect, separate from F15 (F15 is consumption-side and verified working). The FOMC bootstrap config prices the meeting-dated short end; curve_instrument_pricer rejects a non-positive year fraction, likely a FOMC meeting-date/tenor mapping at or before the valuation date. All other IR curve feeds publish normally. Continuous in ores.synthetic.service.0.log since 2026-08-12 15:09, every tick (60s period). | |
| job_instance_summary.job_name carries the definition id when the definition is gone | scheduler operations shell bug |
The scheduler's instance-list operation joins each execution to its job definition to fold in the job's name and action type. When the definition row has been deleted the join finds nothing, and the summary's job_name carries the definition's identifier instead, so a field documented as a name is sometimes a key. Seen from the shell: after scheduler remove, scheduler instances prints the identifier for every execution of the removed job. |
| Legacy template docs sit outside the ores.* physical-space graph | Eleven archetype-named template-library pages (doc_facet_org, doc_facet_group_org, doc_technical_space_org, doc_archetype_org, doc_profile_org, doc_feature_org, and five sql_* files) are typed knowledge, carry no facet keyword, and are named for the template they hold rather than by MASD address. The inventory generator globs ores-star-dot-org only, so it cannot see them, and their facets (ores.doc.template and the ores.sql facets) report zero archetypes. Migrate each to the ores DOT facet DOT archetype filename shape with the archetype type and the facet keyword, preserving its ID and its tangle block, so the catalogue lists them. | |
| Limit concurrent running fleets via fleet slots | compass memory host |
Bound concurrent fleets to a fixed slot count so service fleets cannot exhaust host memory. |
| Make compass shell report a script abort in its exit status | shell compass testing tech_debt |
compass shell -l exits 0 even when the loaded script aborts at its first bad command, because run_host_sync returns EXIT_SUCCESS and the shell's own failure flag never reaches it. Observed during the ores.analytics clean pass: pricing_model_product_parameters put-many aborts with 'Invalid value for pricing_model_product_id: none' and the process still exits 0, so the 40-script V04 sweep had to be read by eye and no CI job can gate on the library. |
| Make compass site page recover from a truncated publish cache | compass site tooling dx |
Make compass site page recover from a truncated publish cache. A 0-byte build/output/org-timestamps/site:pages.cache plus a stale Emacs lock (.#site:pages.cache) left by a dead PID makes every site page run fail with 'org-publish-cache-get called, but no cache present'. The cache is derived data and rebuilds in one run, but nothing says so: the command reports a cryptic org-internals error and no recovery. Delete the stale lock and the truncated cache before publishing, or make the command detect the state and say what to do. The how_do_i_deploy_the_site recipe documents neither the failure nor the recovery. |
| Make the build's systemd probe test what the build actually runs | compass build sandbox tech_debt |
compass build wraps cmake in 'systemd-run –user –scope' whenever compass_claude._has_user_systemd() passes, but the probe only runs 'systemctl –user show-environment'. In a sandbox where systemctl reaches the manager and systemd-run cannot open the scope bus, the probe passes and the build dies with 'Failed to connect to user scope bus via local transport'. The probe should exercise the thing the build depends on, or the scope wrap should honour the ORES_USE_BUSCTL transport that the rest of compass adopted. |
| marketdata persistence intermittently failed 2026-08-12 03:0… | marketdata persistence intermittently failed 2026-08-12 03:05:30-08:35:48 with 'Failed to persist observation' after 'No available connections in the pool': with the default pool size of 2 (database_options.hpp), one connection stuck in COMMIT (wait_event IO) from service boot at 03:01:50 for ~5.7h left one effective connection, and concurrent ingest handlers (5 ticks/s) drained it between acquires. Persistence recovered once the stuck connection resolved (~08:54); ~10,500 observations/party persisted afterward. Environmental/pre-existing fragility, not an F15 defect; consider surfacing the acquire timeout and/or raising pool size. ~479 idle Postgres connections also accumulated over 5 days (pre-existing leak, out of scope). | |
| No CI job runs the physical-space inventory check | codegen drift inventory |
regenerate_physical_space_inventories.py –check reports a stale facet inventory on main, and nothing on the pull-request path runs it. |
| No CI job runs validate_docs.sh, and main is red | codegen docs drift |
validate_docs.sh fails on main with two violations for ores.shell.workflow, and nothing on the pull-request path runs it. |
| No workflow can start once the system has an account | workflow iam sql bug provenance |
The workflow engine never sets modified_by on the instance or its first step, so the shared audit trigger rejects the insert with 23502 as soon as the tenant has a user account. Every workflow start fails; a caller such as iam's tenant provisioning then polls for an instance that will never exist and reports a bare NATS timeout. |
| Nothing gates a hand-written operation handler | security auth codegen permissions defect |
check_handler_permissions.py compares only the four components in its registry, and only their generated messaging handlers. An operation model generates no handler, so its handler is hand-written and can ship with no permission check at all: ores.scheduler's operation handlers call has_permission zero times. The HTTP server has no route-level permission hook either, and only iam_routes checks codes. |
| ores.shell folder feeds: start/stop folder must cover IR cur… | ores.shell folder feeds: start/stop folder must cover IR curve configs too. synthetic start folder <token> only starts fx_spot_generation_config rows (marketdata.v1.market_feed_configs.start_folder, per market_feed_config_protocol.hpp); ir_curve_generation_configs under the same folder are ignored, and ores.shell has no IR curve feed commands at all (the synthetic.service ir_curve_feed_configs.start/stop/list handlers are only reachable via raw NATS, config_id-keyed). Design intent: the top-level folder contains both FX and IR; folder-scoped start/stop should enumerate both config kinds. Extend the folder protocol (or add a synthetic.v1 ir_curve folder variant) and add shell verbs mirroring the FX ones. | |
| ores.web pages hard-code English instead of using translations | web i18n |
No page under projects/ores.web/packages/web/src/pages calls useTranslation. SignInPage hard-codes Username, Password, Sign in and Choose a party, although en, fr and pt already define a full signIn catalogue that nothing uses. Wire every page to its existing keys and add the missing ones. |
| Paginate the compute list requests | compute shell |
Compute list requests cap at 1000 rows and never check total_available_count; paginate so a large grid does not silently drop rows. |
| Party GUCs leak across pooled database connections in the service layer | ores_service ores_database rls party_isolation pool |
The service sets app.visible_party_ids/app.current_party_id/app.current_tenant_id per request on pooled connections; a request whose context carries no party does not clear the GUCs an earlier party-context request left, so read visibility under the party-isolation RLS policies depends on which pooled connection serves the request (same session saw 26 party-scoped rows, then 0, with identical credentials). |
| PlantUML conventions doc contradicts the diagram corpus | plantuml diagrams conventions |
The class diagram conventions document prescribes notes, legends, {field} prefixes, a struct keyword and test-suite namespaces that almost no diagram in the repository uses, so following it produces a diagram unlike every other one. |
| PriorStates HFT trading system demo | reference trading research ui |
A public demo of a C++ low-latency trading system covering CME, BrokerTec and Tradeweb, with reference data and linear and ML model research; a reference point for ORE Studio's trading and research screens. |
| Process Joey O'Brien's ORE sensitivity analysis post | ore sensitivity research |
Joey O'Brien's worked example of ORE sensitivity analysis — zero-rate deltas converted to par-rate deltas via a Jacobian change of basis, verified by a manual market-quote bump — to be processed into the knowledge base. |
| Qt codegen: no way to show a friendly name instead of a raw UUID in list columns | codegen qt ux marketdata |
ores.cpp.qt's Columns (Qt model) codegen only supports plain UUID stringification for a uuid-typed column; there is no lookup/display-name mechanism analogous to combo_display_field on detail-dialog combos, so every UUID FK-ish column in a generated list window shows a raw truncated UUID. |
| Refdata junctions bind their profile where codegen cannot read it | Four refdata junction orgs carry '#+profile: tenant-scoped-junction' in front matter. load_org_junction_model reads frontmatter through a fixed allowlist (product, schema, component, name, name_singular, name_title, name_singular_words, brief, has_tenant_id) that excludes 'profile', so the binding is silently dropped and the junction resolves profile=None, has_tenant_id=None. Rendering sql_schema_junction_create.mustache for these four emits no tenant_id column, while the committed SQL under projects/ores.sql/create/refdata/ still has 'tenant_id uuid not null' plus its RLS policy and index. The committed output is therefore stale, and regenerating refdata would silently strip tenant scoping from currency_currency_groups, party_counterparties, party_countries and party_currencies. The canonical binding point is a '* Flags' section, per _ensure_profile_binding's docstring in org_loader.py:264-276; the five junctions that use it (compute, dq x2, iam, marketdata) all resolve tenant_id=True. Fix is to move the line, then regenerate refdata and confirm the SQL is unchanged. Found while fixing the same defect in the new marketdata market_series_asset_classes junction. | |
| Refresh the stale ER diagram from its generator | The committed projects/ores.sql/modeling/ores_schema.puml lags its own generator by 26 hunks; regenerate and review it on its own branch. | |
| Refuse an empty key list before delete-many renders IN () | codegen repository sql tech_debt shell |
A generated delete-many builds its SQL predicate with an empty IN list when the key it resolves matches no row, and Postgres rejects it with a syntax error at or near ')'. Found during the ores.analytics clean pass: the generated recipe passes the none sentinel and the service answers internal_error with 'Query execution failed'. The same command answers ok when a preceding add created the row, so the condition is 'no row matched', not the entity's key type. The repository should refuse an empty key list with a validation verdict, or treat it as a no-op, rather than emit a statement that cannot parse. |
| Regenerate or retire the domain round-trip dataset (resolved) | ore testing coverage |
assets/test_data/domain_roundtrip/ is the second derived artefact of external/ore/examples/ and it is badly out of step. A fresh v17 round-trip writes 566 files; the committed dataset holds 1474. Regenerating would delete 961 files: 946 are empty Portfolio stubs that an older build wrote for every XML it could not parse, and 15 are orphans for Products/SupportedTrades/, a directory upstream has since removed. It would add 53 outputs for examples the v17 sync introduced, including Products/Example_Trades/Credit_CallableBond.xml, and change 177 more, mostly conventions.xml and curveconfig.xml where the v17 schema moved. The dataset also duplicates assets/test_data/golden_dataset/, which the sprint_17 story said it had replaced; the retire task is marked DONE but golden_dataset is alive and named by 13 files. Decide which of the two is canonical before regenerating either. |
| Regenerate the stale ER diagram and gate it | projects/ores.sql/modeling/ores_schema.puml is a generated artifact (banner: GENERATED FILE - DO NOT EDIT, regenerate with ./projects/ores.codegen/plantuml_er_generate.sh) and it is a month stale. Its last commit was 6610f847fd on 2026-08-07. Regenerating it today produces 2069 changed lines and a 3 MB PNG growth, almost all of it unrelated drift: the trading bond wave, refdata catalog changes, and the marketdata classification work. No CI gate covers it, so nothing catches the drift. The market_series.is_scalar column and the market_observation.point_id nullability were deliberately left undocumented in the diagram by the point-model change set rather than dragging that month of drift into it. Work: regenerate the diagram once, wire the check into the drift gate so it cannot go stale again, and consider dropping the tracked PNG and cmapx in favour of build-only output. | |
| Restart services after rebuild before provisioning | devops provisioning environment |
Provisioning against services started before a rebuild fails with poll parse errors from stale binaries; restart services after the build. |
| Retire the Qt entity meta-model doc | codegen documentation qt |
entity_meta_model_cpp_qt.org documents a facet that no longer exists – no Qt templates remain and ores.cpp.qt is in no graph – and still describes the dead history properties as live. |
| Say which key a service method takes in its signature | The generated service takes every key as const std::string&, so the storage key and the declared key are indistinguishable at the call site; passing the wrong one compiles, returns empty, and looks like a missing record. | |
| scheduler instances –job filters one page, not the collection | scheduler operations shell bug |
The shell's scheduler instances command filters by job name after the page arrives, and the operation orders executions by time. A narrow –limit therefore selects the newest rows and the filter sees only those: –job e2e-ok –limit 5 printed one of that job's two executions because the other fell outside the five newest rows. The command's help says the filter is page-scoped, which is a workaround rather than a fix. |
| Simulated market data: consistent world vs. scenario workspaces | simulated_market_data strategy |
Strategy for simulated market data generation, decided 2026-08-12 (F15). Two use cases: (1) the consistent world, where the system party owns the sim config, publishes the theme once, and every party consumes the same stream into its own per-party series; (2) scenario data, future workspace-scoped work where users create artificial data with permission control and locking. Full decisions in doc/llm/specs/simulated-market-data-strategy.allium. |
| Site build leaves pages whose source was deleted | documentation site build drift |
The site build does not remove built HTML when its org source is deleted, so a renamed or split document keeps serving its old version. |
| Site build unrunnable: killed by the low-memory guard | compass build –direct site is killed by the harness low-memory guard on bright_faraday, in background and foreground alike, leaving doc changes shipping without their main verification. | |
| Skills are missing the frontmatter and regulatory function their contract requires | llm skills claude_code contract hygiene |
Fifty-one of eighty SKILL.org files carry no type or level frontmatter, and none of the eighty declares its regulatory function, which the skill contract states must be declared per instance because it cannot be inferred from the type. |
| Story: Codegen developer experience improvements | codegen tooling product_backlog |
Address workflow friction in codegen: address/type incompatibility errors in –component mode; no entity-scoped filter; no way to target a logical group (e.g. currency) within a component. |
| Story: Codegen infrastructure follow-ups from DQ commissioning | codegen infrastructure tooling product_backlog |
General codegen infrastructure gaps surfaced while commissioning DQ entities but not specific to DQ: entity-model lint, consolidating the five ores.cpp.nats-* facets, compound natural-key support in lookup_entity/table SQL templates, and migrating the remaining 10 lookup_entity models to domain_entity. |
| Story: Roll out badge/image/plain-text rendering across classified domain entities | qt badges codegen refdata ux architecture product_backlog |
Consume the completed audit/classification's batched list: annotate each entity with badge_key/image wiring as classified, author any missing badge_definition/badge_mapping or image data, and regenerate, one batch at a time. |
| Story: Split ORE Studio services into one container per service | docker architecture deployment product_backlog |
Move from the single-container process-supervisor (ores.controller.service launching/monitoring the other 17 services as child processes) to the idiomatic Podman/container pattern of one process per container, with the orchestrator (podman pod, compose, or k8s) responsible for start ordering and supervision. |
| Story: Workspaces: finish the feature | workspace codegen qt product_backlog |
Consolidates all workspace-related captures and stories into one place. Objective: get the Live workspace properly set up and allow users to create stand-alone/temporary workspaces (e.g. for importing ORE samples). First step is a code audit establishing what already exists, including UI support, before further implementation work is scoped. |
| Strongly-typed index/currency filter fields for market_series, not string matching | marketdata identifiers oresmd curve-builder |
CurveBuilderWorkbench needs to filter market_series by currency+index to constrain curve bootstrapping, but today that means string-matching qualifier text (dash-vs-slash separator bugs already hit) – these should be strongly-typed soft-FK fields instead. |
| Suspending a tenant does not suspend it | security iam tenancy bug |
Nothing on the sign-in path reads tenant status, so a suspended tenant still admits its accounts. |
| The assets image publish function selects columns its table does not have | assets sql bug publish |
ores_assets_publish_images_from_dq_fn reads image_id from ores_assets_images_tbl and filters on its key column, but the table's columns are id and code, so the base bundle's assets.country_flags step fails with 'column image_id does not exist'. Latent until now: no workflow could start, so the publish path had never run. |
| The batch workflow bridge has no test | compute testing |
The bridge's tenant-scoped removal was wrong and no test saw it; add an integration test that writes a link for one tenant, finishes the batch, polls, and asserts the link is gone. |
| The clean-standard item ranges go stale in the component task plans | codegen clean_standard docs agile |
Every clean-* task plan enumerates the standard's section ranges, and the standard has grown twice since the tasks were scaffolded, so the plans name ranges that no longer cover the standard and the five items added later are invisible to the worker. |
| The CMake archetype emits a Qt-era AUTOMOC comment | codegen cmake qt tech_debt |
The component-files archetype writes a Qt-era AUTOMOC comment into 78 checked-in component_files.cmake files, and AUTOMOC is never enabled anywhere in this build. |
| The CMake source-list gate is red on main for thirty components | codegen cmake gate tech_debt |
core.py injects an AUTO-GENERATED FILE marker into every rendered file, including the cmake_component_files units, but the checked-in component_files.cmake files of about thirty components carry no marker. regenerate_cmake_component_files.py –all –check therefore reports roughly 141 stale files on main. The ores.assets clean-standard branch first added the marker tree-wide to turn the gate green, then reverted that as out of scope, so the gate stays red and this capture owns it. |
| The Component Clean Standard undercounts its own items | codegen clean_standard component |
The standard's table holds 49 items. Its own prose says 47, and the two it does not count, G08 and H05, are the two a component can skip in silence. |
| The component diagram parser drops three declaration shapes | codegen diagram plantuml |
generate_component_puml.py misses a base clause that wraps, a static data member, and a member whose initialiser wraps; fixing it changes 72 of 85 components' committed diagrams. |
| The compute wrapper's HTTP download opens its destination before it checks the status | compute storage http correctness |
ores.compute.wrapper's Beast client writes the response body to the destination file and only then checks the HTTP status, so a 404 truncates a file that was already there and leaves the error body in one that was not. ores.storage carried the same defect and this pass fixed it there. |
| The deleted event never fires, because the delete rule rewrites the delete | codegen sql eventing |
Every generated entity rewrites DELETE into an UPDATE, and every generated notify trigger returns null on UPDATE, so the TG_OP=DELETE branch is unreachable and no component ever publishes a deleted event. |
| The drift gate covers only the registry, and misses output written into another component's tree | codegen drift component |
The drift gate's coverage is the registry, and the registry is a list of model-bearing components rather than of generated files. Two holes follow. An archetype can route a model's output into another component's tree, so no run of either component reaches it. And 4,892 of the tree's 5,829 generated files sit under components the registry does not list, so a cross-component rename that invalidates a model's pasted source block breaks the build silently. |
| The junction RLS policy name has no identifier-length guard | The junction create archetype composes its tenant isolation policy name in mustache as {{junction.name}}_tbl_tenant_isolation_policy, because load_org_junction_model never derives an rls_table_base the way the domain entity path does in core.py. PostgreSQL truncates identifiers at 63 bytes with a NOTICE rather than an error, so a junction whose name pushes the policy name past 63 bytes silently gets a different policy name than the template intends, and two long junctions could collide on one truncated name. The longest junction today is currency_pair_convention_calendars at 62 bytes, one under the limit, and it does not opt into RLS, so nothing is broken yet. The fix is to derive rls_table_base for junctions next to sql_name_base and truncate it with the _tbl_tenant_isolation_policy suffix reserved, mirroring the domain path's longest_rls_suffix budget. | |
| The legacy entity event mappings remain | eventing codegen |
One hundred and fifty-nine registrations still use the older register_mapping API and depend on the event source converting the trigger's canonical notification for them; migrate them to the generated canonical registrars and delete the older API. |
| The report-definition eventing re-drive collides with the temporal exclusion constraint | Under a fully parallel ctest run, the notification wait in write_report_definition_publishes_nats_changed_event times out, and the re-drive that is meant to rescue it throws an exclusion-constraint error instead. | |
| The settings build reverts a hand-added compass test | codegen drift compass tech_debt |
compass build –direct settings tangles doc/llm/claude_code_settings.org, which regenerates projects/ores.compass/tests/test_deny_bare_stash_hook.py. Commit 20cd5089d5 hand-added test_compass_does_not_run_a_denied_stash to that generated file without adding it to the org source at line 996, so every settings refresh silently deletes the test. Reproduced twice on 2026-09-23 from feature/clean-assets: run the build, the test disappears (grep count 1 to 0); git checkout restores it. Fix by adding the test block to the org source and re-tangling, or the drift returns on the next bearings next-action. |
| The settings deploy reverts a generated compass test | compass build –direct settings tangles projects/ores.compass/tests/test_deny_bare_stash_hook.py from doc/llm/claude_code_settings.org, so deploying settings regenerates that file. Commit 20cd5089d5 added test_compass_does_not_run_a_denied_stash and the ast import to the tangled file and never updated the org block, so main's committed test and its source disagree. A settings deploy therefore deletes 38 lines, including the regression test that rebuilds every git stash command from compass.py and judges it with the hook's own denied — the test guarding the capture PR flow fix that same commit made. The bearings NEXT ACTION 'refresh .claude/settings.json' triggers it, and the loss reads as an unrelated working-tree edit, which also makes check_component_drift.py –all report the dirty file. Fix is to move the added test into the org source block at doc/llm/claude_code_settings.org line 996, re-deploy, and confirm the file is byte-identical. | |
| The shell recipe inventory ships unresolved conflict markers, and its gate cannot see them | shell codegen docs defect |
doc/recipes/shell/shell.org carries unresolved conflict markers in the committed blob at HEAD and on origin/main, and regenerate_shell_recipe_inventory.py –check still exits 0 because the generator carries the marker lines forward as prose. |
| The shell script library is untangled | shell codegen drift |
The codegen-generated shell recipes have no tangled .ores scripts in the shipped library, because tangle_shell_scripts is not part of the build or the drift gate; 793 scripts are missing. |
| The storage server refuses the report-data bucket its own consumers write to | storage http reporting defect |
ores.reporting and ores.ore both write to a report-data bucket, and the http server's bucket allowlist names only compute and ore-imports, so every report export and package handler call answers 404. The allowlist replaced a shared bucket header and drifted from the consumers it was meant to serve. |
| The synthetic generator ignores a column's default_value unless the column also carries a generator block | codegen generator-defect synthetic-data tech_debt |
cpp_domain_type_generator.cpp.mustache honours a column's declared default_value only when the model also supplies a generator block; with no block it assigns a random faker value, silently discarding the default. Found on ores.assets.image.mime_type, whose model declares image/svg+xml and whose SQL column default is the same, yet every synthetic image was written with a random noun. Fixed for assets by restating the literal in a generator block; the template fix would touch all 53 models that declare default_value. |
| The telemetry log query escapes SQL by hand | telemetry sql security |
build_where_clause() interpolates the component and message_contains filters into LIKE patterns with std::format and manual quote escaping, where every other repository binds its values. |
| The tenant provisioning command cannot answer inside the client's request timeout | iam shell nats timeout |
tenant_provisioning provision-acme-tenant completes all eight steps on the server in about 32 seconds while the shell's NATS request timeout is 30, so the client gives up before the answer arrives and the recipe passes or fails with machine load. Its handler waits up to 1500 seconds on the base bundle by design. |
| Three services default their storage base URL to a port the server does not use | storage http configuration defect |
ores.trading, ores.marketdata and ores.reporting default http-base-url to http://localhost:8080, while the http server listens on 21000, and their units pass no override. Every storage call from those services fails to connect. |
| to_iso8601_utc emits a space separator, so the name and the platform overview overstate the format | platform datetime api_contract doc |
to_iso8601_utc formats as YYYY-MM-DD HH:MM:SSZ, with a space, which is not ISO 8601; the platform overview calls it ISO 8601. Either the separator becomes T or the name and the overview stop claiming the standard. |
| Unify legal entity identity and model the calculation agent | trading refdata identity |
Two related gaps found while documenting trade modelling: a trade role cannot name one of our own legal entities, so the common case of the booking party being calculation agent is unrepresentable; and the dispute consequence that follows from designating a calculation agent is recorded nowhere. |
| Workflow records the service account as the actor, not the caller | workflow iam provenance attribution |
The workflow engine sets created_by and modified_by to its own service account, so a user-initiated workflow is attributed to the service. The caller's identity is on the wire as a bearer token and the house pattern is to take claims->username from it, but this service holds no verifier and start_workflow_message carries no identity field. |