Skip to content

content(blog): how to migrate a legacy internal system to metadata - #147

Open
hotlong wants to merge 2 commits into
mainfrom
claude/issue-91-how-to-move-an-internal-system-to-metadata
Open

content(blog): how to migrate a legacy internal system to metadata#147
hotlong wants to merge 2 commits into
mainfrom
claude/issue-91-how-to-move-an-internal-system-to-metadata

Conversation

@hotlong

@hotlong hotlong commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Fixes #91

New English-original how-to: a staged, honestly-costed path off a legacy internal system, plus the cases where the honest answer is not to migrate at all.

New directory only — content/blog/how-to-move-an-internal-system-to-metadata/. why-custom-systems-die and extend-existing-systems-with-ai are linked, never edited (#130 is editing older posts' bodies in parallel).

What it says

  • Answer in the first 100 words (measured: the lead is exactly 100 words). Four stages: connect read-only, reconcile, move writes, cut over.
  • The artifact is the migrate-vs-connect decision tree, and its point is that the question is asked per object, after the read-only connection exists — not per system, before it does. Connecting is stage one of migrating, not the alternative to it. That framing is what lets this post and extend-existing-systems-with-ai both be right: three of the tree's five endpoints are "connect and stop".
  • Mints "the disagreement rate" (rule 1: one ownable concept + one number). Declare the rule you believe you have, run it over the rows the old system already produced, count the disagreements. Worked through on the composite distributor from why-custom-systems-die — 143 orders over the threshold, 25 disagreements, 17%, and the missing standing-contract clause that explains 22 of them. Labelled in the text as a worked example, not a customer measurement.
  • Rule 7 — when not to migrate, four specific cases: you do not own the schema; nobody is asking it to change; the valuable part is genuinely novel logic; re-validation costs more than the changes are worth.
  • Cites the ossification toll and the six-question health check rather than restating them; the health-check link is a deep link, and the #a-six-question-health-check anchor was confirmed present in the built HTML.

Premise check and platform verification

The issue's premise held, but one input did not, and it changed the article.

Every platform claim was verified against objectstack origin/main (7cbe705b), not against the docs site:

Claim in the post Verified against
schemaMode: 'external' forbids DDL; writes off by default packages/spec/src/data/datasource.zod.ts, ADR-0015
Boot schema validation, validation.onMismatch: 'fail' / validate-only packages/spec/src/data/datasource.zod.ts; ADR-0062 R6 shipped
Double opt-in writes: datasource external.allowWrites + object external.writable datasource.zod.ts, object.zod.ts (both gates required)
os datasource list-tables / introspect --table / validate packages/cli/src/commands/datasource/{list-tables,introspect,validate}.ts
A bound datasource that cannot connect refuses the boot content/docs/data-modeling/external-datasources.mdx; ADR-0062
Import via a named mapping, upsert with a key content/docs/data-modeling/import-mappings.mdx
No cross-datasource join in one query ADR-0062 non-goals
No transaction spanning datasources — it refuses loudly content/docs/releases/v17.mdx (#5351, #5696)

The PM's mechanism assumption was confirmed on three of four points and corrected on the fourth. Read-only-first and per-object write enablement are real and stronger than assumed (they are platform-enforced gates, not just a read-only DB user). But "undocumented behaviour is discovered by diffing the declared model against observed data" is only half true: os datasource validate and the boot gate diff declarations against the remote schema — a wrong column type, not a missing approval clause. Rule discovery is queries you write. The post says this in as many words rather than implying the platform does it for you, and that honesty is the reason stage two carries the article.

ADR-0015 is no longer Proposed. It is Accepted and shipped (ADR-0062 supersedes its runtime half and is also Accepted). Two published pages still describe this surface as unshipped — filed separately, not touched here.

Gates

Union run after the final commit, on 11785d6, exit codes captured before any pipe:

Gate Exit Verdict line
pnpm content:lint 0 ✓ content lint passed (335 files checked)
pnpm check 0 Result (133 files): 0 errors, 0 warnings
pnpm build 0 [build] Complete!
pnpm seo:smoke 0 SEO smoke test passed (866 HTML pages checked)

One non-blocking lint warning, and it is the intended one: Date is in the future: Thu Sep 17 2026 — the assigned Thursday 2026-09-17 slot. Baseline on origin/main was zero issues, so this is the only line this branch adds.

Assets

cover-en.svg (the four stages, with the reversible boundary falling after stage three) and one body diagram, migrate-or-connect-en.svg (the decision tree). Both rendered standalone and inspected at full size. One real bug was caught that way and fixed: a CSS text{fill} rule outranks a presentation attribute, so the dark start box's label was rendering dark-on-dark and the two band labels were losing their colour — all colour overrides now go through classes.

Frontmatter date: 2026-09-17T10:00:00+08:00, no updated. English original only; description is 222 characters. Body is ~2,520 prose words.

Tag reconciliation

Tags reconciled with the #78 closed vocabulary (#163): none — this post ends with tags: []. It carries no tier-2 canonical by aboutness (AI agents and the open business ontology appear as closing framing, not as the subject) and compares against no named product; the legacy systems it names — ERP, CRM, MES — are categories, not proper nouns. That emptiness is the measured consequence recorded on #158. content:lint green at head 6ea4deb; tags: is the only line that commit touches.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FeA1nwBz1ohH65dvffUGKr


Generated by Claude Code

A staged, honestly-costed path off a legacy internal system, and the
cases where the honest answer is to connect and never migrate.

The artifact is a migrate-vs-connect decision tree asked per object
rather than per system: connecting is stage one of migrating, not the
alternative to it, so the call is made object by object once the
read-only connection exists.

Mints "the disagreement rate" — declare the rule you believe you have,
run it over the rows the old system already produced, and count the
disagreements. That is the stage every migration guide skips, and the
platform does not compute it for you (os datasource validate diffs the
remote schema, which is narrower).

Platform claims verified against objectstack origin/main: schemaMode
'external', the boot validation gate, the double opt-in write gate
(datasource external.allowWrites + object external.writable), the
os datasource list-tables/introspect/validate CLI, and the two
sequencing constraints — no cross-datasource join in one query and no
transaction spanning datasources.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeA1nwBz1ohH65dvffUGKr
…ags with the #78 vocabulary

Closed vocabulary from PR #160: every tag is either one of the seven
tier-2 canonicals in this locale's rendering, or the proper noun of a
product / company / protocol the post actually compares against.

None qualifies. The post is a four-stage migration path (connect,
reconcile, move writes, cut over); AI agents and the open business
ontology appear as closing framing, not as the post's subject, and the
legacy systems it names (ERP, CRM, MES) are categories, not products it
compares against. Empty is the measured outcome recorded on #158, not a
gap to paper over.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeA1nwBz1ohH65dvffUGKr
hotlong pushed a commit that referenced this pull request Sep 2, 2026
…ve design"

The closing "What Has Shipped vs. What Is Coming" section told readers that
one-step schema import, binding to externally owned schemas, and the
write-safety gates were still being designed under ADR-0015 (status:
Proposed). All three landed. Measured against objectstack origin/main:

- one-step schema import — `os datasource list-tables` and
  `os datasource introspect <name> --table <table>`
  (packages/cli/src/commands/datasource/)
- binding to externally owned schemas — `Datasource.schemaMode`
  ('managed' | 'external' | 'validate-only') and `Object.external`
  (remoteName / remoteSchema / columnMap), in
  packages/spec/src/data/{datasource,object}.zod.ts
- write-safety gates — the double opt-in `external.allowWrites` on the
  datasource plus `external.writable` on the object
- ADR-0015 is "Accepted — backend/REST/CLI implemented; Studio UI + extra
  dialect drivers pending"; ADR-0062 (Accepted 2026-06-22) supersedes its
  runtime half

The paragraph now names what shipped and how to use it, keeps the
federation write gate distinct from a read-only GRANT on a managed
datasource (the FAQ's advice is unchanged), and states what is genuinely
still pending. The post's connect-not-rebuild argument is untouched.

Wording and facts kept consistent with
content/blog/how-to-move-an-internal-system-to-metadata (PR #147).

Correction to a live post: sets `updated`, leaves `date` alone. Carried
into all six authored locales; zh-Hant regenerated with `pnpm gen:zh-hant`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeA1nwBz1ohH65dvffUGKr
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.

New how-to: How to move an existing internal system to metadata-defined

2 participants