Skip to content

feat: declare the ledger handoff in the manifest so the kernel runs it (0.3.1) - #8

Merged
Weegy merged 1 commit into
mainfrom
feat/c15-manifest-handoff
Aug 21, 2026
Merged

feat: declare the ledger handoff in the manifest so the kernel runs it (0.3.1)#8
Weegy merged 1 commit into
mainfrom
feat/c15-manifest-handoff

Conversation

@Weegy

@Weegy Weegy commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Refs byte5ai/omadia#814 (core PR byte5ai/omadia#815); epic byte5ai/omadia#470 C15.

Closes gap G7 of docs/ACCEPTANCE-RUN-2026-08-21.md.

The problem

activate() calls ctx.sql.seedLedger before runMigrations, exactly as C11 documented. But core runs permissions.sql.migrations itself, before activate() (C7 — so "the tables exist" is an invariant activate() can rely on). By the time this plugin got control, all nine ledger rows were already written:

[tool-runtime] @omadia/dev-platform: applied 9 migration(s) to ledger ... (0022...0030)
[@omadia/dev-platform] [sql] ledger handoff - 0 seeded, 9 already seeded, 0 left ...

So the handoff could only ever report alreadySeeded, and skippedNoWitness — the one alarm C11 exists to raise — never fired. Nothing went red: 0 seeded, 9 already seeded is indistinguishable from a healthy re-run.

This was not fixable from inside the plugin. Core calls the runner before handing over control, and the witnesses are knowledge only this plugin has. There is no ordering the plugin can choose. Core PR byte5ai/omadia#815 adds permissions.sql.handoff; this PR declares it.

Changes

permissions.sql.handoff: handoff-plan.json — the kernel reads the plan and performs the handoff ahead of its own migration runner. The file is the one already in the ZIP (shipped since #6/#7 and listed in files), unchanged: core's reader accepts pluginId / ledger / migrationsDir and ignores them precisely so one file serves both readers, which means plugin-ledger-handoff.mjs --plan still dry-runs the exact plan an operator is about to install.

The activate() seedLedger call stays, and the comment now says why. On a kernel that honours handoff it reports alreadySeeded — correct, the work is done, one read-only transaction. On a kernel that does not, it is the only thing that performs the handoff at all. Removing it would silently drop adoption on every older core; keeping it costs a log line on newer ones.

0.3.0 -> 0.3.1 in both package.json and manifest.yaml (the hub reads the manifest; npm run package aborts on drift — verified below).

Tests

Two new cases in test/manifest.test.ts, both pinning things the declaration newly makes load-bearing:

  1. handoff names a plan the ZIP ships — and that it is in package.json files. Declaring a file the ZIP omits makes core refuse the activation outright, while every local test still passes. That is the failure mode this repo has been bitten by before (a declaration nothing reads), inverted.
  2. The declared plan is one the kernel will accept — core's reader is stricter than the operator CLI's, so a plan this repo is happy with but core refuses would fail at install time, on the operator's machine, where nobody can fix it. Checks core's rules here: no unknown keys (notably dir, which SeedLedgerOptions accepts and so looks like it should work), no duplicate filenames, under the 128 KiB cap, every filename present in migrations/, ledger matching the manifest (a mismatch makes core warn that the previewed table is not the written one), and no shipped dryRun: true.

The existing ledgerHandoff.test.ts already pins plan-vs-SEED_LEDGER_ENTRIES; these cover the manifest-to-plan link it does not.

Verification

Controlled comparison, identical env (DEV_PLATFORM_PG_TEST_URL only), same worktree, built from clean each time:

tests pass fail cancelled exit
origin/main 684 682 0 1 1
this branch 686 684 0 1 1

Exactly +2 tests and +2 passes — the two added above. The single cancellation (purgeDevPlatformSchema — against Postgres) and the non-zero exit are pre-existing on origin/main and measured, not assumed: that suite needs a database carrying the plugin schema.

Gate Result
npm run build exit 0
npm run typecheck exit 0
npm run package exit 0 — omadia-dev-platform-0.3.1.zip, drift guard passed, handoff-plan.json present in the artifact

Built against core PR byte5ai/omadia#815 via OMADIA_CORE_DIR=... npm run link:core (plugin-api 1.6.0), which is the documented path for contract work on unmerged core branches.

Merge order

This is safe to merge before the core PR: a kernel without C15 does not read permissions.sql.handoff, and core's manifest loader warns-and-degrades on keys it does not know rather than rejecting the package. Such a kernel keeps the pre-0.3.1 behaviour exactly — the activate() fallback does the handoff, as it does today.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

…t (0.3.1)

activate() calls ctx.sql.seedLedger before runMigrations, exactly as C11
documented -- but core runs permissions.sql.migrations itself, before
activate(), so by the time this plugin got control all nine ledger rows
were already written. The handoff could only report alreadySeeded, and
skippedNoWitness -- the one alarm it exists to raise -- never fired. The
2026-08-21 acceptance run measured "0 seeded, 9 already seeded" on the
exact upgrade the feature was built for, and nothing went red.

Not fixable from inside the plugin: core calls the runner before handing
over control, and the witnesses are knowledge only this plugin has.

- permissions.sql.handoff: handoff-plan.json -- the kernel reads the plan
  and performs the handoff ahead of its own migration runner. The file is
  the one already in the ZIP, so an operator can still dry-run the exact
  plan with plugin-ledger-handoff.mjs before installing.
- The activate() seedLedger call stays as the fallback. On a kernel that
  honours handoff it reports alreadySeeded, which is correct; on an older
  kernel it is the only thing that performs the handoff at all.
- manifest.test.ts pins that handoff names a file the ZIP ships, and that
  the plan satisfies core's stricter reader: no unknown keys, no
  duplicates, under the size cap, filenames present in migrations/, and a
  ledger matching the manifest.

Requires @omadia/plugin-api 1.6.0 (byte5ai/omadia#815). Older cores
ignore the key and keep the previous behaviour.

Refs byte5ai/omadia#814; epic byte5ai/omadia#470 C15.
@Weegy
Weegy merged commit 4da17e0 into main Aug 21, 2026
3 checks passed
Weegy added a commit that referenced this pull request Aug 24, 2026
…16) (#17)

package-lock.json recorded packages/plugin at 0.3.1 while its package.json
moved to 0.3.4 across #8, #13 and #15. Nothing caught it: npm ci reconstructs
the dependency tree, and a workspace member's own `version` field is not part
of it, so the public lockfile claimed a version the plugin had not been for
three releases.

The one-command repair is the one that must not run. `npm install
--package-lock-only` fixes the member entry and, in the same pass, rewrites the
`../odoo-bot/middleware/packages/plugin-api` file: external from whatever core
checkout sits next to this repo on the developer's disk (0.1.0 committed, 1.10.0
here) — machine state that must never land in a public lockfile.

So:
- Surgically bump ONLY the packages/plugin lockfile entry 0.3.1 -> 0.3.4. The
  external stays exactly as committed; no other member had drifted.
- Add scripts/check-lock-sync.mjs: a read-only checker that asserts, for every
  workspace member (root included), lockfile version == package.json version,
  and prints the exact by-hand fix while forbidding the regeneration that leaks
  the local core checkout. It refuses any workspaces pattern it cannot safely
  expand -- outside-repo, a missing named member, or an unsupported glob --
  rather than silently checking fewer members than the workspace has.
- Wire it into `npm run package` (build-zip.mjs) and into CI before `npm ci`,
  where npm ci genuinely cannot see the drift. Add an `npm run check:lock`
  script.
- Test packages/plugin/test/lockSync.test.ts covers discovery, drift, the
  external-is-never-a-member invariant, the error message, and that both wiring
  points are actually in place.

Version stays 0.3.4 -- lockfile and tooling only, no release, no hub publish.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant