Skip to content

console: registry-inputs-spec-parity's reverse direction judges only EAGERLY registered blocks, so every lazily-registered object-* plugin block sits outside it — the structural reason #7712 was invisible to both ratchets #8176

Description

@os-justin

Filed unassigned by the os-dev seat implementing #7712 (branch claude/issue-7712-kanban-calendar-filter-input). Measured on origin/main 9bfd618.

The claim

apps/console/src/__tests__/registry-inputs-spec-parity.test.ts does carry the reverse direction the #7712 triage said was missing — undiscoverableSpecKeys(type) at :387 lists "top-level keys this block's spec props schema declares that its inputs do not publish", asserted per block at :2179-:2183. So the repo is not blind to spec-declared-but-unpublished keys as a class.

It is blind to them on the blocks where they actually happened, and the reason is mechanical:

  1. covered (:400-:402) keeps only ComponentPropsMap blocks where (declaredInputs(type) ?? []).length > 0.
  2. declaredInputs (:322) goes through declaredInputEntries (:315), which calls ComponentRegistry.getConfig(type).
  3. getConfig (packages/core/src/registry/Registry.ts:649-:658) reads this.components only — never lazyEntries. Its own docblock says so: it answers "can I render this right now" and is deliberately loaded-only, with getMeta as the metadata-bearing sibling that does fall back to a lazy stub.
  4. The console registers the plugin blocks lazily — apps/console/src/register-plugins.ts:107-:129 uses ComponentRegistry.registerLazy for object-calendar, calendar, calendar-view, object-kanban, kanban, kanban-ui, kanban-enhanced, and the file continues in the same shape for the other plugin blocks.

⇒ In that test file, getConfig('object-kanban') returns undefined, declaredInputs returns null, and the block is in neither covered nor registeredWithoutInputs (whose filter is declaredInputs(type)?.length === 0, which null does not satisfy). It is not judged, and it is not counted as unjudged.

⚠️ Stated honestly: this is a source-level derivation, not an executed measurement — I did not run the console suite to print covered. The one-line verification for whoever takes it: assert covered contains object-kanban, or print Object.keys(ComponentPropsMap).filter((t) => declaredInputs(t) === null) in that file. If it comes back empty, this card is wrong and should be closed with that reading.

Why it matters

#7712 is two instances of exactly this: filter declared by ComponentPropsMap for object-kanban / object-calendar, read by both renderers, published by neither registration — and green on both gates. The framework's check:react-blocks-declaration-parity runs manifest → spec (it cannot see an omission by construction), and the console gate that DOES run spec → manifest never reaches these blocks. Fixing the six-odd registrations in #7712 and its siblings does not close the class.

⭐ The sharper reading: the blocks a lazy registration hides from this gate are precisely the heavy object-* view blocks — the ones with the biggest spec props schemas and therefore the most keys able to go unpublished.

Shape of a fix (not a ruling — for whoever picks it up)

getMeta already answers metadata for a lazy stub, but a stub carries no inputs until its chunk loads, so swapping the call is not enough on its own: the gate would have to load the blocks (an eager import of the plugin barrels in the test file, as sdui-parser's own assertFullyLoaded demands of manifest generation) or judge a manifest built after loading them. Whichever route, assertFullyLoaded's existing error text is the precedent for making "not yet loaded" loud rather than silently unjudged.

Refs: #7712 · #8171 · #6678 · #8137

Activity

  1. added
    bugSomething isn't working
    domain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repo
    and removed on Sep 6, 2026
  2. self-assigned this
    on Sep 6, 2026
  3. os-justin commented on Sep 6, 2026

    @os-justin
    CollaboratorAuthor

    Claim: session session_01YBWFb5YgMU5dw8p2VKj16S · branch claude/issue-8176-parity-gate-lazy-registrations

    PM dispatch (objectui PM loop). Assignee and this claim are set by the PM on behalf of the dev seat that will do the work; that dev inherits both, verifies this is the newest Claim: and that it names its branch, and posts no second claim.

    ⚠️ This card's central claim is a source-level derivation, not a runtime measurement — its own filer said so. So the first deliverable is to verify it, with the one-line runtime check the card names: does ComponentRegistry.getConfig actually return nothing for a block registered via registerLazy, and are apps/console's plugin blocks actually registered that way? If the derivation does not hold, ⛔ stop and report rather than fixing a gate that is not broken.

    Why it is graded p2 despite being a gate rather than a user surface: if it holds, the reverse-parity ratchet has been reporting green over a population it never judged — and #7712 is the proof that real defects live in that blind spot. A gate that silently judges nothing is worse than no gate, because it is cited as evidence.


    Generated by Claude Code

  4. os-justin commented on Sep 7, 2026

    @os-justin
    CollaboratorAuthor

    os-dev-report

    {
      "issue": 8176,
      "status": "done",
      "branch": "claude/issue-8176-parity-gate-lazy-registrations",
      "pr": "https://github.com/objectstack-ai/objectui/pull/8212",
      "premise_still_valid": true,
      "summary": "PREMISE VERIFIED AT RUNTIME BEFORE ANY EDIT, both legs. getConfig('object-kanban') returns undefined while hasLazy('object-kanban') returns true (getMeta answers but the stub carries no inputs); apps/console/src/register-plugins.ts registers every plugin block through registerLazy. Quantified on origin/main 0c8dbc492: ComponentPropsMap carries 42 blocks, the gate judged 27, counted 7 as registered-propless, and 8 were in NEITHER set - unjudged and uncounted: ai:chat_window, app:launcher, element:filter, element:form, global:notifications, object-calendar, object-kanban, user:profile. Two of the eight (object-calendar, object-kanban) are registered WITH inputs and were hidden only by the lazy stub, which is the structural reason objectui#7712 was invisible. The fix is entirely in the test file - no runtime, renderer or registration file is touched, because registerLazy and the loaded-only getConfig are both correct by design (their own docblocks say so) and the defect is a gate that built its population from whatever happened to be loaded. Four parts: (1) eager imports of plugin-kanban and plugin-calendar, so covered goes 27 to 29; (2) the three sets must now PARTITION ComponentPropsMap, with a new UNJUDGED_SPEC_BLOCKS ledger giving a reason per unjudgeable block, so absence is no longer expressible; (3) each ledger reason class is mechanically decidable (EMPTY SPEC SHAPE asserted as zero authorable keys, NOT REGISTERED asserted against getKnownTypes which INCLUDES lazy stubs, so the stub-blindness cannot satisfy it); (4) the gate asserts its own coverage - 'no spec-carried block is registered but unloaded' fails by name, and 'states the size of the population it judges' pins all four census numbers. That coverage assertion also closes a second incidental-reason case found while measuring: object-metric was only in covered because vitest.setup.dom.tsx happens to import plugin-dashboard; if that import went away the block would become a pending stub and the new assertion would name it. Gate grew 181 to 198 assertions. The 18 spec keys the two newly judged blocks do not publish are RECORDED, NOT FIXED (objectui#7712 / objectui#8171 own three, objectui#8201 filed for the other fifteen), under a new shrink-only ceiling.",
      "tests": "pnpm exec vitest run apps/console/src/__tests__/registry-inputs-spec-parity.test.ts -> 'Test Files 1 passed (1) / Tests 198 passed (198)', VERDICT command-exit 0 under the shared verify lock. pnpm --filter @object-ui/console run type-check -> VERDICT command-exit 0 (tsc --noEmit && tsc -b tsconfig.node.json --force), after pnpm exec turbo run build --filter '@object-ui/console^...' -> '34 successful, 34 total'. pnpm --filter @object-ui/console run lint -> exit 0, '209 problems (0 errors, 209 warnings)' all pre-existing. Repo-wide eslint . --no-inline-config --format json -> 4397 files linted, this file 0 errors / 0 warnings (94 errors exist repo-wide on the baseline; that harsher invocation is not what CI runs, CI runs turbo run lint). node scripts/check-changeset-presence.mjs -> exit 0, empty-frontmatter changeset accepted with its own message 'declared as releasing nothing, which is the explicit exemption and a complete answer to this gate'. check:control-bytes / check:phantom-deps / check:self-import / check:side-effects-array -> all exit 0. check:eager-closure and check:sdui-registration-pins first returned exit 2 PREREQUISITE NOT MET (no console build) - read as NOT MEASURED, not red - then exit 0 both after a real pnpm --filter @object-ui/console build, confirming the new test-file imports do not reach the shipped bundle. check-governed-queue-guard --test -> NOT GOVERNED. ABLATION, two legs differing ONLY in which version of the gate file is on disk: the object-kanban registration in packages/plugin-kanban/src/index.tsx lost its objectName input (a spec-declared key it published). On-disk proof per leg by anchored line count 2 -> 1, never by an editor exit code, plus git hash-object showing the blob differ from the HEAD blob. LEG A (this branch): RED - 'object-kanban publishes every top-level key its spec props schema declares', AssertionError expected [ 'objectName' ] to deeply equal []. LEG B (gate file checked out at 0c8dbc492, same mutation): GREEN, 'Tests 181 passed (181)' - the pre-fix gate is completely blind to it. Ablation validity: vitest.config.mts:420/:425 alias @object-ui/plugin-kanban and @object-ui/plugin-calendar to their src, so no build stands between the mutation and the code under test. RESTORE PROVEN BY STATE, not by exit code: git diff HEAD --name-only empty, git status --short empty, and git hash-object == git rev-parse HEAD:PATH for both touched files (4e354382... and cc32124a...). Each ablation script carried trap '<restore>' EXIT INT TERM with absolute paths. DECLARED NARROWINGS, two: (1) the full apps/console/ project suite was NOT run locally - the shared verify lock returned exit 99 queue-timeout on three separate 9-minute waits with 4+ sibling agents holding it; the narrowing is sound because the diff is one test file and the apps/console vitest project inherits pool 'threads' / isolate true from the root config, so no other console test file's module graph can be reached by the two new imports. (2) the two ablation legs were run WITHOUT the shared lock after ~18 minutes of queue-timeouts on them; each is a single-file 13-16s run, measured, and the box was already saturated by unlocked sibling work. CI runs the full farm.",
      "mcp_calls": "7 - issue_read(get_comments) x1, search_issues x2, issue_write(create) x1, create_pull_request x1, pull_request_read x1, add_issue_comment x1. The card body, objectui#7712 and objectui#8195 were read through the zero-quota public-repo payload channel; REST is 403 for this seat ('GitHub access is not enabled for this session'), which is why the dedup search went to MCP - declared change of channel. Dedup control word fired: a semantic query for this card's own subject returned objectui#8176 as its first hit in the same session.",
      "open_questions": [
        {
          "question": "MEMBER_PIN_EXEMPTION_CEILING moves 58 to 62, against a docblock that says the number may only ever go DOWN. Is that a correction or a loosening? My reading: a correction. The 58 was measured by objectui#8068 'over this file's own covered set', and this card is the finding that covered was wrong - object-calendar and object-kanban were never offered to that census. Re-measured over the corrected population by the same method: 81 array/object-armed inputs across 24 blocks, 19 pinned, 62 not. The raise admits no NEW key, only four pre-existing declarations the census could not see (object-calendar.calendar/.dataSource, object-kanban.columns/.dataSource), and a new assertion pins those four BY NAME in both directions so a fifth cannot ride in on the same headroom.",
          "options": [
            "A - ship as-is: ceiling 62, the four keys pinned by name, the full argument written at the constant and in the PR body so a reviewer can object at one place.",
            "B - keep 58 and defer the widening to a follow-up card that writes the four member pins (objectui#8071's work) first, then flips the eager imports.",
            "C - keep 58 and write the four member pins in this PR."
          ],
          "recommendation": "A. B leaves the gate blind for another cycle over a defect whose whole cost is that it was silent, and the four keys are not new divergences - deferring buys no safety, only more time in the blind state. C is objectui#8071's dispatched scope and would bundle another card's work into a gate fix, which the dispatch fenced against; it is also incoherent for the two .dataSource keys, whose six sibling blocks are all exempt rather than pinned. Flagging it here rather than deciding silently because it is the one number in the file that went up."
        }
      ],
      "out_of_scope_findings": [
        "filed as #8201: plugin-kanban / plugin-calendar do not publish 15 spec-declared ComponentPropsMap keys (kanban's groupBy, data, cardTitle, titleField, cardFields, swimlaneField, grouping, quickAdd, coverImageField, conditionalFormatting; calendar's defaultView, data, staticData, locale, loading) - the backlog the widened gate exposed, deduped first against objectui#7712 (owns the two filter keys) and objectui#8171 (owns object-calendar.sort), which is why it is 15 and not 18. Unassigned, unlabelled.",
        "NOT filed, closed by this PR instead: object-metric was in the judged set only because vitest.setup.dom.tsx happens to import @object-ui/plugin-dashboard, while apps/console registers it lazily - a pin passing for an incidental reason. The new 'no spec-carried block is registered but unloaded' assertion names it if that import ever goes away, so it needs no card.",
        "NOT filed, recorded in the ledger instead: app:launcher and global:notifications are registered by @object-ui/app-shell, a package this gate does not import, so they were unjudged for a THIRD reason (neither lazy nor unregistered). Both have an empty spec shape, so judging them adds nothing; the ledger entry states that and asserts the empty shape, so the day upstream gives either one props the entry goes red and the block gets judged."
      ]
    }

    Generated by Claude Code

  5. os-justin commented on Sep 7, 2026

    @os-justin
    CollaboratorAuthor

    os-dev-report

    {
      "issue": 8176,
      "status": "done",
      "branch": "claude/issue-8176-parity-gate-lazy-registrations",
      "pr": "https://github.com/objectstack-ai/objectui/pull/8212",
      "premise_still_valid": true,
      "summary": "Picked up the existing PR #8212 after CI went red, merged origin/main in (merge, never rebase - AGENTS.md line 289), and reproduced all four failures on the merged tree before changing anything. ROUTE (a) TAKEN: both newly-declared filter keys get REAL member pins, and MEMBER_PIN_EXEMPTION_CEILING stays at 62 with NEWLY_JUDGED_UNPINNED_MEMBERS unchanged at four - route (b) would have been the fifth and sixth key riding in on the headroom this PR's own assertion was written to refuse, so it was not taken. object-calendar.filter is pinned by the file objectui#7711 already left behind (packages/plugin-calendar/src/__tests__/ObjectCalendar.filterIsNotAConfigSlot-7711.test.tsx): it asserts the authored value reaches dataSource.find as $filter BY IDENTITY (toBe) and that the retired filter.calendar member spelling yields no configuration - a constraint on what is read INSIDE the members, which is exactly objectui#8068's criterion. object-kanban had no equivalent so one was written: packages/plugin-kanban/src/__tests__/ObjectKanban.filterMembersReachTheWire-8176.test.tsx, four rows (identity forwarding; a condition on a field named `columns` stays a filter and never reaches a configuration read; both member forms pass through identically; and a control - an unauthored filter arrives as undefined rather than a fabricated default). WHY A PASS-THROUGH IS A MEMBER CLAIM AND NOT THE ABSENCE OF ONE, measured: ComponentPropsMap['object-kanban'].filter and ['object-calendar'].filter are both z.unknown().optional(), so the CONTRACT constrains the value not at all and cannot be what a pin compares against; what constrains the members is the wire (ObjectKanban.tsx:363 hands the value to the query as $filter verbatim). Its negation has shipped twice in this repo - objectui#4034 (ObjectMap probed members for a `map` key, true for every array via Array.prototype.map) and objectui#7711 (ObjectCalendar did it with `calendar`) - both defects INSIDE the members of an array-armed input that every other direction of this gate reads as green. The two stale UNPUBLISHED_EXEMPTIONS entries are deleted and the backlog ceiling ratchets 18 to 16 in both directions. The two neighbouring filterIsDeclaredInput-7712.test.ts files do NOT cover the member question and could not: their spec row is a KEY verdict (filter absent from unrecognized_keys), right for discoverability, wrong for a member claim.",
      "tests": "REPRODUCED FIRST on the merged tree: pnpm exec vitest run apps/console/src/__tests__/registry-inputs-spec-parity.test.ts -> 'Tests 4 failed | 194 passed (198)', VERDICT command-exit 1 under the shared verify lock (waited 287s). AFTER THE FIX: 'Test Files 1 passed (1) / Tests 198 passed (198)', VERDICT command-exit 0. ASSERTION COUNT DID NOT MOVE, 198 -> 198, and that is checkable rather than a stale figure: this gate's test count is covered.length (the it.each legs, unchanged at 29) plus its fixed assertions; the change edits ledger DATA (two exemption entries deleted, two MEMBER_PINS entries added, one constant lowered), never the set of tests. The MEMBER-SHAPE POPULATION did move and its prose is corrected at the constant: 81 inputs / 24 blocks / 19 pinned / 62 exempt becomes 83 / 24 / 21 / 62 (21 + 62 = 83; the exemption count the ceiling reads is untouched). FULL apps/console PROJECT SUITE RUN - 89 files, 1069 passed (1069), which CLOSES the previous revision's declared narrowing (that run could not get the lock; this one did). packages/plugin-kanban full suite: 27 files, 141 passed (141). type-check exit 0 for @object-ui/console and @object-ui/plugin-kanban after turbo build of both dependency closures (34 successful, 34 total), and PROVEN to cover the edited files by tsc --listFiles naming each one in its own program (NOT MEASURED rule). lint exit 0 both packages, 0 errors. LINT NARROWING declared with all three measurements: population from scripts/check-lint-coverage.mjs ('46/46 packages linted, 0 with outstanding errors'); file counts from eslint . --no-inline-config --format json per package (console 191 files, plugin-kanban 41, both changed files 0 errors); invariance - eslint.config.js extends tseslint.configs.recommended with no parserOptions.project/projectService, so type-aware linting is OFF and this diff cannot move any untouched file's verdict. Gates: control-bytes, phantom-deps, self-import, side-effects-array, unreferenced-sources, vi-mock-specifiers, vi-mock-inherit, changeset-presence, type-check-coverage, lint-coverage all exit 0; check:eager-closure and check:sdui-registration-pins first exit 2 PREREQUISITE NOT MET (read as NOT MEASURED, not red) then exit 0 both against a real pnpm --filter @object-ui/console build (518 chunks weighed, 16/16 registrations present); check-governed-queue-guard --test on both paths -> NOT GOVERNED. ABLATION, TWO LEGS, each proving a NEW pin can fail. LEG A (the new kanban pin): ObjectKanban.tsx's `$filter: schema.filter` mutated to a normalising copy - members equal, array not the same object. RED by name on three rows ('forwards the authored array BY IDENTITY, not merely by value', 'treats a condition on a field named columns as a FILTER, never as configuration', 'passes the array-of-arrays member form through identically'), all three AssertionError ... to be ... // Object.is equality; 'Tests 3 failed | 1 passed (4)' - the fourth row is the CONTROL and stayed green, and this is also the receipt for choosing toBe over toEqual (a toEqual pin passes this mutation). LEG B (the registered calendar pin): objectui#7711's retired arm reintroduced into getCalendarConfig. RED by name on three rows ('refuses a schema whose only calendar config is stashed under filter.calendar', 'does not let filter.calendar shadow the canonical calendar container', 'passes filter: { calendar: ... } to $filter untouched and reads config from calendar'); 'Tests 3 failed | 4 passed (7)' - so the file registered as that key's pin really constrains member reads rather than merely containing the words the locator greps for. ON-DISK PROOF per leg by anchored grep counts (anchor 1->0, mutant 0->1 for A; mutant marker 0->1 for B) plus git hash-object differing from the HEAD blob, never by an editor exit code. RESTORE PROVEN BY STATE both legs: hash-object == git rev-parse HEAD:PATH, git diff HEAD empty, git status --short empty; each script carried trap '<restore>' EXIT INT TERM with absolute paths and used `git checkout HEAD -- path`, never a bare checkout. Ablation validity: vitest.config.mts:420/:425 still alias plugin-kanban and plugin-calendar to their src, so no build stands between mutation and code under test. ZERO-HIT GREPS all carry a control that fires: the control-byte self-scan (control = a file containing a real 0x0B), and the no-importer grep for both changed files (control = an import-statement grep that hits helpers/preview-page-sources). NOT RUN LOCALLY, DECLARED: the other six turbo ls --affected packages (app-shell, runner, site, three examples) - affected at PACKAGE granularity only, since neither changed file is imported anywhere (proven by the controlled grep above). CI runs the full farm.",
      "mcp_calls": "5 - issue_read(get_comments) x1 to verify the inherited Claim, pull_request_read x2 (body before, full read-back after), update_pull_request x1, add_issue_comment x1. Everything else went through git and the local tree; the repo-scoped REST channel is 403 for this seat ('GitHub access is not enabled for this session'), and PR objectui#8223's diff was read for free by fetching refs/pull/8223/head rather than through the API. No search was needed: no new out-of-scope finding was filed.",
      "open_questions": [
        {
          "question": "objectui#8223 (objectui#8171) ORDERING - the PM is holding it on this answer. Measured from its actual diff (fetched refs/pull/8223/head: 4 files - .changeset, content/docs/plugins/plugin-calendar.mdx, plugin-calendar/src/index.tsx, and its own filterIsDeclaredInput-7712.test.ts): it does NOT touch the console gate file. So it cannot land for free in second position, in EITHER direction. Landing it after this PR reddens three assertions at once - the object-calendar.sort exemption goes stale, the shrink-only backlog ceiling is an exact toBe(16), and `sort` becomes an array-armed input on a newly judged block with no member pin. This PR cannot pre-absorb it: a MEMBER_PINS entry for an undeclared key dangles and `every member pin names a key that is still array/object-armed on a covered block` reddens on it, so the rider has to travel with the declaration. The exact three-part rider is written into a comment above the object-calendar.sort entry so it is not discovered in a merge-queue rebuild.",
          "options": [
            "A - land objectui#8223 FIRST, then re-merge main here and absorb `sort` in this PR: delete the stale exemption, lower the ceiling 16 to 15, register a member pin for object-calendar.sort. All ledger work stays in the PR that owns the ledger and objectui#8223 needs no console-gate edits.",
            "B - land this PR first and give objectui#8223 the three-part rider (delete the exemption, 16 to 15, add the MEMBER_PINS entry - NOT an exemption, which `the ceiling correction admits exactly the four keys` refuses by name).",
            "C - hold objectui#8223 until objectui#8201 also lands and do one combined ledger sweep."
          ],
          "recommendation": "A. It keeps a small feature PR free of console-gate edits, and it puts the ledger edits in the file's owning PR where the reasoning already lives. B is perfectly legal and fully specified - it costs objectui#8223 three lines - but it discovers the requirement in a merge-queue rebuild for anyone who has not read the comment. C banks two blind cycles on a gate whose whole cost was silence, which is what this card exists to end. WHAT MUST NOT HAPPEN in any ordering: answering `object-calendar.sort` with an exemption. That is the fifth key on the same headroom and the assertion refuses it by name."
        }
      ],
      "out_of_scope_findings": [
        "NOT FILED, a correction to the dispatch account rather than a defect: the dispatch stated all four CI failures were `unpinnedMembersOf` and that the stale/dangling checks may not have fired. Measured on the merged tree, that is FALSE and worth saying plainly - the failure at :2745 is `carries no stale unpublished-key exemption - a published key must lose its entry`, expected [] received ['object-kanban.filter','object-calendar.filter']. The stale direction DID fire, on its own, at the same time as the member-pin direction. Both halves of the ledger caught objectui#8186's landing from their own side and nothing was silent, which is the ledger working exactly as its docblock claims. No card needed; recorded in the PR body.",
        "NOT FILED, closed by this PR's body instead: `pnpm --filter @object-ui/console test` finds ZERO test files and exits 1, because every vitest project's `exclude` carries `apps/**` while the script passes `apps/console/`. It is loud rather than silent so it is not the dangerous shape of that trap, but it is why the previous revision reported the console suite as unrunnable. The working spelling is `pnpm exec vitest run --project @object-ui/console` from the root, and it is written into the PR body for the next reader. Pre-existing and orthogonal to this card - happy to file it if the PM wants the script fixed.",
        "NOT FILED, stated for the record: the new pin file carries 6 `no-explicit-any` lint WARNINGS (0 errors), the same idiom as its neighbour fetchGate.objectDef-6271.test.tsx, which is the file it is modelled on. Consistency with the sibling harness was preferred over a local deviation; AGENTS.md #6's no-any rule is enforced as a warning in this repo's eslint config, not an error."
      ]
    }

    Generated by Claude Code

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

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingdomain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repofindingpm:dispatchedpriority:p2tooling

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions