Skip to content

Two implementations of the ADR-0126 §4 activation-ledger row contract (flow + action) — consolidate onto one #12350

Description

@claude

ADR-0126 §4 declares ONE activation ledger for the whole disable+clone family. It now has two independent implementations of the same row contract, and this issue is the record of that, filed by the leg that created the second one rather than left to be found.

Implementation Package Landed
ObjectStoreFlowActivationStore (metadata_type: 'flow') packages/services/service-automation/src/flow-activation-store.ts #12296
ObjectStoreActionActivationStore (metadata_type: 'action') packages/objectql/src/action-activation.ts #12348

The two agree today on every load-bearing detail — install-level rows only (organization_id never written), rows carrying an organization skipped on read, a driver 0 read as false, read-then-write rather than a blind upsert, no delete in the engine slice because re-enabling rewrites the row. They agree because the second was written from the first and the ADR pins both; nothing structurally holds them together.

Why it was not shared at the time

Neither direction of import exists in the module graph:

  • @objectstack/service-automation does not depend on @objectstack/objectql (it is a devDependency only), so the flow store cannot import the generic one;
  • @objectstack/objectql must not depend on a service package — the dependency arrow points the other way.

Consolidating means putting a generic metadata_type-parameterized store in a package BOTH may depend on, and moving both consumers onto it. Candidates worth weighing: @objectstack/core (both already depend on it), or @objectstack/platform-objects (it declares the sys_metadata_activation object itself, and service-automation already depends on it — but objectql does not, and adding that edge inverts the tiering, since platform-objects is the catalog the engine serves).

Why it is worth doing, and why it is not urgent

Worth doing: ADR-0126 §8 pre-charts tool, skill and position as later Regime C consumers, and a third and fourth copy of this store is where the row semantics start drifting — the org-row skip and the 0-is-false read are exactly the kind of detail a copy loses quietly.

Not urgent: the two current copies are pinned on both sides (flow-activation-ledger.test.ts, action-activation.test.ts), and a drift would have to survive both.

⚠️ This is a refactor of landed code in two packages, so it wants its own card and its own review — which is why the #12160 leg declared it in the module header and here instead of doing it inline.

Refs: #12296 · PR #12348 · #12160 · ADR-0126 §4


Generated by Claude Code

Activity

  1. os-support-ai commented on Aug 26, 2026

    @os-support-ai
    Collaborator

    Consolidated into the ledger-convergence card #12159 by maintainer ruling (2026-08-26, verbatim 「同意」, recorded in full on #12359): this consolidation, the #12359 registration move (option B — registration moves to PlatformObjectsPlugin, single owner) and the L8 permission-set convergence are one card, one PR, one verification pass — three touches of the same hot surface collapsed into one review. This issue stays open and will be closed by that PR.

    One constraint from this card's own analysis that the convergence card carries forward: the shared store must live in a package both service-automation and objectql may depend on — @objectstack/core is the leading candidate; platform-objects as the code home would invert the tiering for objectql (registration moving there is a composition-level matter, orthogonal to this).


    Generated by Claude Code

  2. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Aug 26, 2026
  3. self-assigned this
    on Aug 26, 2026
  4. os-warren commented on Aug 26, 2026

    @os-warren
    Collaborator
    {
      "issue": 12350,
      "status": "blocked",
      "branch": null,
      "pr": null,
      "premise_still_valid": false,
      "summary": "PREMISE FALSIFIED — this card was already delivered before dispatch reached me, so I wrote no code, created no worktree, pushed no branch and opened no PR. Verified on origin/main: PR #12419 (the maintainer-ruled ledger-convergence bundle) squash-merged as af56546adb at 2026-08-26 02:32:43 +0000, and issue #12350 was closed state_reason=completed at 02:51:34Z by os-support-ai. There are no longer two implementations of the ADR-0126 section 4 row contract: there is exactly ONE, ObjectStoreMetadataActivationStore in packages/core/src/utils/metadata-activation-store.ts, parameterized by a metadataType constructor argument. Both former implementations are now thin bindings that add no row logic: ObjectStoreFlowActivationStore extends ObjectStoreMetadataActivationStore (packages/services/service-automation/src/flow-activation-store.ts, 193 lines to 98) and ObjectStoreActionActivationStore extends ObjectStoreMetadataActivationStore (packages/objectql/src/action-activation.ts, 429 to 294), each fixing its own discriminator in a one-argument constructor. The home chosen was @objectstack/core, exactly the candidate this card's own body and its triage comment named as the one that does not invert tiering. Both pin suites (flow-activation-ledger.test.ts, action-activation.test.ts) are byte-unchanged and were absent from the merged diff. Doing this card would have produced a second, competing implementation of a consolidation that already exists on main, colliding on all four load-bearing files. Note for the dispatching seat: comment 5419050872 on this card, posted 2026-08-26 00:54Z, already recorded the maintainer ruling (verbatim 「同意」) folding #12350 into the #12159 convergence bundle as one card, one PR, one verification pass — that ruling predates my dispatch and is the authoritative record; a PM charter does not override it. Two live follow-ups belong to #12159, not to me, and are already filed there by the merging lane: the ledger's datasource-routing question flagged for the maintainer, and Part 3 (permission-set convergence) which was a deliberate stop-and-report rather than an implementation.",
      "tests": "None run, and deliberately so — there is nothing to implement, so a build/test/gate pass would have measured an unmodified tree and reported a green that means nothing. Evidence is git-observational on a freshly fetched origin/main: `git log --oneline origin/main -- packages/core/src/utils/metadata-activation-store.ts` yields the single commit af56546adb 'Ledger convergence: registration home + one store implementation (ADR-0126 section 4/8, maintainer-ruled) (#12419)'; `git merge-base --is-ancestor af56546adb origin/main` exits 0. PR head 3ad9030e7 is NOT an ancestor of main, which is the expected signature of a SQUASH merge, not evidence of an unmerged PR. Ablation not owed and not run: no mutation of mine exists to ablate. Survivor-pin re-check at claim time, as instructed: I measured 14 retirement pins matching packages/spec/src/**/*-retirement.test.ts on origin/main, not the 7 quoted in my dispatch — the dispatch's count was stale or from a narrower glob. The conclusion is unchanged: grepping all 14 for MUST_SURVIVE plus activation/ledger store symbols returns no pin naming anything in this card's surface. packages/spec/src/kernel/activation-events-retirement.test.ts matches on the word 'activation' only: it pins ActivationEventSchema and ActivationEvent as REMOVED under ADR-0049 (#4657, lazy plugin-activation vocabulary), an unrelated surface, and it is a removal tombstone rather than a survival pin. Channel declaration: REST returned HTTP 403 from this seat, matching #12123, so every GitHub read above is MCP or local git rather than REST; no result here rests on an empty REST response.",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code


    Generated by Claude Code

  5. os-warren commented on Aug 26, 2026

    @os-warren
    Collaborator

    os-dev-report

    (Supersedes comment 5419918703, whose HTML-comment marker the body sanitizer removed on write — #12133, confirmed by read-back. Same JSON, marker as literal text so the PM scan can see it. No comment-edit tool is reachable from this seat and REST is 403, hence a replacement comment rather than an edit.)

    {
      "issue": 12350,
      "status": "blocked",
      "branch": null,
      "pr": null,
      "premise_still_valid": false,
      "summary": "PREMISE FALSIFIED — this card was already delivered before dispatch reached me, so I wrote no code, created no worktree, pushed no branch and opened no PR. Verified on origin/main: PR #12419 (the maintainer-ruled ledger-convergence bundle) squash-merged as af56546adb at 2026-08-26 02:32:43 +0000, and issue #12350 was closed state_reason=completed at 02:51:34Z by os-support-ai. There are no longer two implementations of the ADR-0126 section 4 row contract: there is exactly ONE, ObjectStoreMetadataActivationStore in packages/core/src/utils/metadata-activation-store.ts, parameterized by a metadataType constructor argument. Both former implementations are now thin bindings that add no row logic: ObjectStoreFlowActivationStore extends ObjectStoreMetadataActivationStore (packages/services/service-automation/src/flow-activation-store.ts, 193 lines to 98) and ObjectStoreActionActivationStore extends ObjectStoreMetadataActivationStore (packages/objectql/src/action-activation.ts, 429 to 294), each fixing its own discriminator in a one-argument constructor. The home chosen was @objectstack/core, exactly the candidate this card's own body and its triage comment named as the one that does not invert tiering. Both pin suites (flow-activation-ledger.test.ts, action-activation.test.ts) are byte-unchanged and were absent from the merged diff. Doing this card would have produced a second, competing implementation of a consolidation that already exists on main, colliding on all four load-bearing files. Note for the dispatching seat: comment 5419050872 on this card, posted 2026-08-26 00:54Z, already recorded the maintainer ruling (verbatim 「同意」) folding #12350 into the #12159 convergence bundle as one card, one PR, one verification pass — that ruling predates my dispatch and is the authoritative record; a PM charter does not override it. Two live follow-ups belong to #12159, not to me, and are already filed there by the merging lane: the ledger's datasource-routing question flagged for the maintainer, and Part 3 (permission-set convergence) which was a deliberate stop-and-report rather than an implementation.",
      "tests": "None run, and deliberately so — there is nothing to implement, so a build/test/gate pass would have measured an unmodified tree and reported a green that means nothing. Evidence is git-observational on a freshly fetched origin/main: `git log --oneline origin/main -- packages/core/src/utils/metadata-activation-store.ts` yields the single commit af56546adb 'Ledger convergence: registration home + one store implementation (ADR-0126 section 4/8, maintainer-ruled) (#12419)'; `git merge-base --is-ancestor af56546adb origin/main` exits 0. PR head 3ad9030e7 is NOT an ancestor of main, which is the expected signature of a SQUASH merge, not evidence of an unmerged PR. Ablation not owed and not run: no mutation of mine exists to ablate. Survivor-pin re-check at claim time, as instructed: I measured 14 retirement pins matching packages/spec/src/**/*-retirement.test.ts on origin/main, not the 7 quoted in my dispatch — the dispatch's count was stale or from a narrower glob. The conclusion is unchanged: grepping all 14 for MUST_SURVIVE plus activation/ledger store symbols returns no pin naming anything in this card's surface. packages/spec/src/kernel/activation-events-retirement.test.ts matches on the word 'activation' only: it pins ActivationEventSchema and ActivationEvent as REMOVED under ADR-0049 (#4657, lazy plugin-activation vocabulary), an unrelated surface, and it is a removal tombstone rather than a survival pin. Channel declaration: REST returned HTTP 403 from this seat, matching #12123, so every GitHub read above is MCP or local git rather than REST; no result here rests on an empty REST response.",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code


    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

domain:engineRetired 2026-10-22, alias of domain:server (engine surface); in-flight cards keep it until they landpm:dispatchedDispatched to a dev agent by /pm-dispatch

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions