classic-to-freedom-migration
/classic-to-freedom-migration
Analizuje i planuje migrację interfejsu Creatio Classic do Freedom UI na poziomie sekcji albo całej aplikacji, z mapowaniem szablonów.
- Autor
- creatio-platform ↗
- Repozytorium
- creatio-ai-app-development-toolkit ↗
- Licencja
- MIT
- Ostatnia zmiana repozytorium
- 27.10.2026
Ocena i bezpieczeństwo
- Pliki w paczce
- 44
- Skrypty
- 18
- Komentarze HTML
- 0
- Adresy URL w SKILL.md
- 0
Brak sygnałów. Repozytorium zmienione 27.10.2026, bez odwołań do wycofanych modeli i API.
MIT, otwarta: można używać i zmieniać, z zachowaniem informacji o autorze.
- 18 skryptów w paczce. Przejrzyj je przed instalacją, bo Claude może je uruchomić.
Skrypty: classic-behaviour-analysis.workflow.js, engine/assemble.mjs, engine/designspec.mjs, engine/diagnostics.mjs, engine/engine.mjs, engine/fsatomic.mjs, engine/mapper.mjs, engine/mapping-registry.mjs, engine/mapping-table.mjs, engine/migrate.mjs, engine/reads.mjs, engine/reconcile-modes.mjs, engine/report.mjs, engine/split.mjs, engine/tasks.mjs, engine/vendor/acorn.cjs, engine/verify-vendor-upstream.mj
Co robi i kiedy
Analizuje i planuje migrację interfejsu Creatio Classic do Freedom UI na poziomie sekcji albo całej aplikacji, z mapowaniem szablonów.
Oryginalny opis (EN)
Analyze and migrate Creatio Classic UI to Freedom UI at either scope - a single section/page/detail, or an entire package/application. Use when the user provides a Creatio URL, section/page name, or package/app name and asks to audit, plan, or implement a Classic UI to Freedom UI migration with metadata discovery, package editability and placement analysis, Classic page-template structure analysis, Freedom UI template analog selection, layout and business-logic analysis, an approval-gated migration plan, and execution of the approved plan.
Jak działa
- The Contract
- Product telemetry — emit the shared stages with workflow: "classic-to-freedom-migration"
- Inputs
- Migration Scope
- Source Policy
- Documentation
- Workflow
- References
- Known Traps
- Output Rules
Podgląd SKILL.md
---
name: classic-to-freedom-migration
description: Analyze and migrate Creatio Classic UI to Freedom UI at either scope - a single section/page/detail, or an entire package/application. Use when the user provides a Creatio URL, section/page name, or package/app name and asks to audit, plan, or implement a Classic UI to Freedom UI migration with metadata discovery, package editability and placement analysis, Classic page-template structure analysis, Freedom UI template analog selection, layout and business-logic analysis, an approval-gated migration plan, and execution of the approved plan.
---
# Classic To Freedom Migration
Guide a coding agent through migrating a Creatio Classic UI section to a parallel Freedom UI analog.
## The Contract
These eight rules are non-negotiable. Everything else in this skill serves them. **When prose and an engine gate disagree, the gate wins** — do not re-argue a rule the engine already enforces.
1. **Approval-gated.** Never create or edit a Freedom artifact before the user explicitly approves the plan — for *every* scope, with no exceptions. Nothing is "too small" or "too obvious" to plan.
2. **The plan you present is ENGINE-WRITTEN, not hand-assembled.** Supply the plan's Overview/Main-scope values through `manifest.planMeta`, then run `node engine/migrate.mjs <manifest> --plan --out <plan-file>` (point `--out` at your migration folder's `plan.md`) — the engine writes the whole plan and you present *that file* verbatim. Do **not** hand-paste stdout, hand-type the `<FILL: …>` placeholders, paraphrase, reorder, reformat, or drop a generated section. A remaining `<FILL: …>` means that `planMeta` value is still missing — add it and re-run, never fill it in by hand. Every correction or enrichment goes in an **Adjustments** list at the very end — never by editing a generated table. (Same principle at the per-page level: in the `--spec` artifact you append to its generated **`⚠ Confirm`** list, never into the Layout/Business-rules tables.) Hand-authoring the plan is *the* recurring failure: it silently drops the List page, child pages, and columns the engine produced.
3. **All THREE engine gates must be green before a plan is presentable.** A run reports `gate.blocked` (correctness), `structure.complete` (input completeness) and `coverage.complete` (member coverage — every schema member is accounted for); a bad run prints a `⛔` banner and exits non-zero. A blocked or incomplete run is **not** an approvable plan — fix what it names and re-run until all three clear.
4. **There is no "out of scope".** Every detail and every child page that has a real Classic `*Page` is fetched and mapped, however large the child entity is. "Big / shared / view-only / native / follow-on" are not yours to declare as skips. Trimming scope is a decision to *put to the user*, never a self-declared one.
**A SECTION BOUNDARY is not one of those skips, though — it is the scope itself.** Migrating "this section" means this section's own pages: its list page, its record page, its typed pages, its mini page. A detail whose child entity **owns another section** is that other section's work: on Freedom the related list keeps opening the child's **Classic** page, and the platform handles that perfectly well. So when the user draws that line, it is a scope DECISION to record — not a gap to hold against the plan, and never a reason to ask for "just one Freedom card" of the other section. **Two things follow, and you owe the user both.** (a) Ask the boundary question the moment you know the child entity has its own `SysModule` — BEFORE you fold its page, not after (step 4.2). (b) RECORD the answer: `"opensClassicPage": "<Classic page>"` (plus `"ownSection": "<Section>"`) on that detail's `detailSchemas` entry is the FOURTH resolution the structure gate accepts. It resolves the child, the page is **never folded**, and the plan is **approvable with no gap** — so a warning inside a page you are not migrating does not ⛔ this plan, and nothing about that child is ever reported MISSING. Never offer "build the other section's card" as the route to a green gate: it folds the same page and can hit the same block, so the option cannot deliver what it promises. And when a ⛔ does fire, name the page and the warning verbatim — a blocker inside another section's card is never the user's scope answer, and on the run that produced this rule it was reported as theirs three times while `labelConfig` appeared in none of the 111 messages.
5. **The engine does the mechanical 80%; you do the judgment 20%.** It merges the schema chain, maps to Freedom, and renders the plan/spec deterministically. Your job is to feed it complete inputs and resolve its `needsDecision[]` / `⚠` worklist — not to re-derive by hand what it generates.
6. **Plain Markdown in chat, never HTML or a rendered artifact.** Match the user's language. Never commit or push without explicit approval.
7. **Resolve, don't defer — and build the plan, don't simplify it.** Two halves, one rule, because
they are the recurring failure: **(a) Every item in the plan's `⚠` worklist is RESOLVED before
you build — by running its specific on-stand query and recording the ANSWER, not by guessing
"probably N/A" or leaving it "pending". The `⚠` worklist is ALL THREE generated lists:
`⚠ Confirm before I build` (open questions needing an on-stand answer) AND `⚠ Custom methods`
(one row per Classic method) AND `⚠ Other declared logic` (one row per `mixin` / `message` /
`attribute-*` / `module-dep` / `referenced-module` — declared on this page, defined elsewhere).
Each row of the latter two is marked *ported* naming the Freedom handler/converter/attribute you
built, *dropped* with its reason, or *blocked*. No list is optional and none outranks the others;
a method or member left unmarked is the same defect as an unresolved DCM check. A row the engine
could not resolve — `Trigger: ⚠ unresolved`, a method assigned from another module, a `message`
or `mixin` member reading `⚠ not described` — is answered by the plan-time `classic-ui-expert`
run (step 5.1) and marked against **every one** of its card's acceptance criteria — the negative
ones ("does NOT fire when…") exercised, not reasoned about — never from the method's name.** A
`⚠ ADD only if present` (DCM case → `SysSchema ManagerName='DcmSchemaManager'`; connected
processes → `ProcessInModules`; printables → `SysModuleReport`) is a task to *execute*, not a
note to defer; a 0-row result proves nothing until you have confirmed the filter is correct; the
Classic body lacking a widget/button is **not** evidence the section lacks the case/process.
**(b) Build the plan's layout and components EXACTLY as specified** — every profile island (each
its own container), every tab/group, and BOTH halves of a two-part component (e.g. Approvals =
module *above the island* + list; DCM = progress bar *in `MainContainer`* + Next steps *tab*).
Collapsing, merging, or simplifying "for simplicity" is a plan deviation to **propose to the
user**, never to apply silently. When unsure whether the plan is right, ask — do not quietly do
something smaller.
8. **UI/UX guidelines are mandatory when you BUILD, not optional.** Whenever you create or edit a Freedom UI page while implementing the approved plan (`create-app`, `create-app-section`, `create-page`, `update-page`, `sync-pages`), you MUST invoke the **`creatio-ui-guidelines`** skill **before** authoring the page body and apply its rules (layout/containers, component choice, `colSpan`/gaps, `caption` vs `title`, island/card settings, lookups, fields, tooltips, accessibility), then run its review checklist **before** treating page work as done. Do not design pages from memory — these rules are easy to miss and skipping them produces the recurring defects (selection-window lookups, layout gaps, single-field islands, Title-case captions, missing tooltips, non-accessible components). This is the same guideline the plan's **Quality gates** checklist row verifies after the build; this rule makes it a **before-build** gate too, so page bodies are authored to it in the first place rather than only reviewed afterward. It binds **whoever builds** — the five tools named above ARE the definition of "build" — and it binds at BOTH points; running only the save-time review is exactly how the gate gets skipped. (This is a **page-design** guideline — distinct from the clio build `get-guidance` contracts you read to write the schema.)
> **Rules 7 and 8 bind whoever writes to the stand.** From step 7 that is a build sub-agent, one per task, so the
> brief it is handed carries them — `./references/build-task-execution.md` states both, and the orchestrator that
> dispatched it neither builds around a `⚠` item nor accepts a page that skipped the UI-guidelines gate.
> **This plan is standalone.** A Classic→Freedom migration is a technical UI-transformation, not a business-requirements task — so its plan is the engine-written `plan.md` above (Overview / Main scope / Layout / Business rules / ⚠ Custom methods / ⚠ Other declared logic / ⚠ Confirm), **not** a BA-style Business Plan, and it does **not** go through the orchestrator's Gate P/R (`AGENTS.md` exempts this skill). Present the engine's `plan.md` verbatim (rule 2).
## Product telemetry — emit the shared stages with `workflow: "classic-to-freedom-migration"`
Read `get-guidance name=product-telemetry` for the stage vocabulary and the consent flow, and
`../../context/product-telemetry.md` for this flow's emission points. Emit with clio MCP `send-telemetry`. Event names are **flow-agnostic stages**; this flow is
identified by the `workflow` field, so do not invent `migration_*` event names — clio rejects them.
Every stage lands on a point the Contract above **already** forces you to stop at:
| Send | With | At the point where you |
| --- | --- | --- |
| `workflow_started` | `variant` = `single-section` / `package` / `existing-freedom-reconcile` | take the first request and settle the scope |
| `clarification_requested` / `user_input_received` | — | ask the developer something / receive their answer |
| `plan_blocked` | `variant=engine-gate` | get a `⛔` run — `gate.blocked` or `structure.complete=false` (rule 3). Emit per blocked run |
| `plan_presented` | — | present the engine-written `plan.md` verbatim (rule 2) |
| `plan_changes_requested` | — | receive a change request, including re-approval of an amended frozen plan |
| `plan_approved` | — | get explicit approval — before the first Freedom artifact (rule 1) |
| `build_started` | — | begin creating Freedom artifacts |
| `work_item_completed` | `variant=page` | finish and verify each page, once per page — a page is its `Page build` chain plus its `Quality gates` task, so emit when the LAST task carrying that page key closes, never per task |
| `workflow_completed` / `workflow_failed` | — | reach the end of the run |
| `changes_requested` | — | the developer asks for further changes AFTER the run completed. Emit before starting that follow-up work |
| `changes_applied` | — | the follow-up changes are applied and verified |
Telemetry is non-blocking: never let it gate, delay, or alter the migration, and if clio rejects an event
name (older clio), stop emitting for the rest of the run and carry on.
## Inputs
- A Creatio section/page URL, or a section/page/entity name.
- A Creatio package or application name, when the whole package/app must be migrated.
- Optional Creatio environment name.
- A migration folder with `resume.md`: skip steps 0-6, read `./references/orchestrate-build.md` → *Resuming*.
## Migration Scope
Determine the scope before anything else — it changes how much you inventory and how you order the work.
### Single-section scope
The user names one section, page, detail, or mini page (or a URL pointing at one).
- Resolve and migrate only that target plus its directly required dependencies (entity, details, mini pages, parent template, backend it calls).
- Do not inventory or touch unrelated schemas in the same package.
- Steps marked "(package scope)" below are optional here — used only to place the target's Freedom artifacts safely.
### Whole-package / whole-application scope
The user names a package or application, says "the whole app/package", or "migrate everything".
- Treat the package/application as the unit of work. Run the full inventory + dependency graph first (step 0).
- Classify every Classic schema, because they migrate differently:
- **Own section/page** — a self-contained section the app owns (a custom `*Section`/`*Page` over the app's own entity) → migrate as a new Freedom list/form page.
- **Replacing / extension schema** — a `replacing` schema that injects behavior into a base section (e.g. a replacing `ContactPageV2` adding fields to the standard Contact card) → migrate as an additive delta on the *existing* Freedom page for that entity; a second section only via step 3.2.
- Decide package placement once for the whole app and reuse it for every artifact.
- Order by dependency, not alphabetically: entities/data sources → own sections → replacing/extension deltas → backend/process/permission logic.
- The whole-package deliverable is a `roadmap.md` that INDEXES one **engine-written** plan per page (each page = its own `migrate.mjs --plan --out <page>.plan.md`, presented verbatim), plus the shared `discovery.md`/`decisions.md`. The engine is single-page by design, so do NOT hand-assemble one merged plan doc — that would violate Contract rule 2 (present the engine's file verbatim). "Consolidated" = the roadmap index over per-page engine-written plans, not a hand-merged document.
### Existing-Freedom reconcile scope
The Classic section being migrated **already has a Freedom UI section/page for the same entity**, and the client's value is the customizations they layered on top of the Classic section in their own packages.
- The unit of work is the client's **customization delta**, not the whole Classic page. Target the existing Freedom section unless the user picks a parallel one (3.2).
- Reconcile both directions: what the client added in Classic but is missing on Freedom gets **added**; what is on Freedom but contradicts the client's Classic setup (never added, or explicitly removed/hidden) gets **removed/hidden** — within the customization scope only.
- **Absence is not intent to remove.** Never strip a base/standard Freedom element that has no Classic analog just because it is absent from the delta — flag it as a manual decision.
- Follow `./references/existing-freedom-reconcile.md` for the isolate-delta → read-Freedom → diff → apply → verify procedure, and record every removal with its Classic evidence. The presented plan must include the reconciliation diff (added / modified / removed-hidden).
If the scope is genuinely ambiguous (e.g. a name that is both a section and its package), ask one clarifying question before continuing.
## Source Policy
Discovery is runtime-only, through Clio MCP:
1. Resolve all metadata through Creatio runtime via Clio MCP; an environment must resolve first.
2. If the runtime metadata for a target is unavailable, continue only if the target can still be identified; record the gap as a risk in the plan.
3. Stop and ask for the missing identifier only when the target cannot be resolved from the URL, name, or runtime metadata.
## Documentation
Every migration is tracked through a persisted document set — the shared source of truth, not chat memory. Follow `./references/migration-documentation.md` for the layout, status vocabulary, task IDs, Definition of Done, and update rules.
- **Scale to scope:** single-section → a lightweight `plan.md` + `worklog.md` + `decisions.md`; whole-package → the full set (`README.md`, `discovery.md`, `plan.md`, `roadmap.md`, `decisions.md`, `worklog.md`). `decisions.md` is in BOTH lists because the plan approval is recorded there and the step-7 build reads it as a precondition — at single-section scope that one entry may be all it holds.
- `plan.md` is the **engine-written `--plan --out plan.md` output** (Contract rule 2), its values supplied via `manifest.planMeta`, plus its provenance; it is **frozen after approval** — changes go through `decisions.md` and re-approval.
- `customizations.md` is the **`classic-ui-expert` sub-agent's report** (step 5.1) when that run applies — behaviour cards with acceptance criteria, written by that skill, linked from the plan's `Adjustments`, and never hand-edited.
- Cardinal rule: at each session start recover state, never whole — `grep -n '^#'` on `README.md` + `roadmap.md` (single-section: `plan.md`), read the active section and the tail of `worklog.md`; after every meaningful action update `roadmap.md` and append to `worklog.md`. **Read discipline** binds all your reads: big output goes to a file and back as a `grep -n` + `sed -n` window, JSON is queried, a held file is not re-read — full block: `./references/orchestrate-build.md`.
## Workflow
### 0. Determine Scope
Initial request: `$ARGUMENTS`
Decide the scope from "Migration Scope" above. For **whole-package** scope, build the inventory + dependency graph first: `list-packages` / `list-apps` / `get-app-info` / `list-app-sections` to enumerate every section, page, detail, entity, and owning app; classify each schema as own vs replacing/extension; record which pages depend on which entities/details/backend; persist it in `discovery.md`. For **single-section**, skip the full inventory and resolve only the named target plus its direct dependencies. At both scopes the **run diagnostics** (step 1.2a) come before the first discovery call — so the environment is resolved (step 1.2) first, and the inventory waits for it.
### 1. Resolve The Target
1. Parse the input: a URL → host, route, section/page hints, record Ids, page/designer UIds; a name → possible section caption/code, entity/page/package/app name, or schema prefix. Whole-package → the set of sections from the step-0 inventory.
2. Resolve the environment: `list-environments`, match the URL host AND the application path — a host match with a different path is a DIFFERENT stand, not the target. If no entry matches, do NOT stop and do NOT hand-edit `appsettings.json`: register the stand through clio MCP, then re-run `list-environments` to confirm.
> **Registration must go through `clio-run` → `reg-web-app`.** The clio MCP server reads `appsettings.json` once at process start and holds that snapshot for its whole lifetime, so an entry written to the file by any other means — a Bash edit, or `clio reg-web-app` run in a separate CLI process — is invisible to every MCP tool (including `list-environments`, which also answers from the snapshot) until the MCP server restarts. `reg-web-app` invoked in-process is the only path that updates the running server's view. Call: `clio-run` `{ "command": "reg-web-app", "args": { "environment-name": …, "uri": …, "login": …, "password": …, "developer-mode-enabled": true } }`. Ask the user for the environment name and credentials; use credentials copied from another registered stand only when the user explicitly says to. Symptom that you skipped this: `Environment with key '<name>' not found` from a tool while `clio ping -e <name>` in the shell succeeds.
2a. **Print the run diagnostics — before any discovery call.** Run `node engine/diagnostics.mjs --environment <name>` (the `engine/` dir beside this SKILL.md) and show its `### Run diagnostics` block in chat verbatim, then copy it into the first `worklog.md` entry; its `--json` form goes to `manifest.runDiagnostics`. It names this skill's build (version, plus branch/commit when readable), the clio version, and the stand's URL, Creatio version, product, DB engine and framework — so a transcript, worklog or plan alone says what the run was made with. A value it cannot read shows as `unknown (<reason>)`; that never stops the run — an unreachable stand is handled by the usual connectivity path in the next calls.
3. Resolve the target inventory: section schema + code, edit pages, mini pages, details/related schemas, entity schema, the Classic parent/template chain per page, existing Freedom pages for the same entity/app, package/app ownership, and whether the owning package is editable or needs a new/replacing package.
### 2. Discover Runtime Metadata
Read-only operations first.
> **Clio tooling — read this before your first call; it avoids token-burning rediscovery and arg-guessing.**
> 1. **Every non-resident clio tool is invoked through `clio-run`** (`{ "command": "<tool>", "args": { … } }`). Do **NOT** `ToolSearch` for clio schema/entity tools — they are hidden long-tail and `ToolSearch` returns nothing, wasting turns.
> 1b. **A `tool does not exist` / `not found` error on one of these names means you called it DIRECTLY, not that it is gone.** `list-entity-client-schemas`, `get-classic-page-sources` and `get-classic-list-columns` are hidden long-tail tools: they are not in `tools/list`, so a host-namespaced direct call (`mcp__…__list-entity-client-schemas`) fails with exactly that message, and a real run read it as a stale tool name and lost the turn. Re-issue it through `clio-run` before concluding anything about the tool. `get-guidance` is resident, but it also takes the wrapped shape — `{"args": {"name": "<guide>"}}`; a top-level `name` is rejected.
> 2. **Resolve a tool's argument shape with `get-tool-contract` BEFORE calling it — never invent a payload.** This prevents the whole class of wasted round-trips (wrong arg names, object-vs-JSON-string, inline-body vs `body-file`, a tool on a different MCP server).
> Arg-facts: `get-classic-page-sources` takes **`schema-name`** (+ optional `entity`, `output-file`) and writes the whole manifest to disk — the one-call path in step 4.0; `list-entity-client-schemas` takes **`entity-name`** and returns `sections` + `editPages` (each with `kind: classic|freedom`, per-type `typeColumnValue`, and `miniPageSchema`). Do **NOT** offload the fetch to a general-purpose sub-agent — it just duplicates this context.
Runtime discovery:
- `list-app-sections`, `list-pages` + `get-page` (existing Freedom pages), `get-client-unit-schema` (Classic client-unit schemas).
- `list-entity-client-schemas` (entity → its Classic sections, edit pages incl. per-type/typed cards, and add mini pages, each classified `classic`/`freedom`) — the entity-first, one-call way to resolve an entity's page-role graph. Use it here for the TARGET entity, and per CHILD entity in step 4.2.
- `get-classic-page-sources` to assemble the full replacing-schema chain + parent-template seed + resolution inputs into `manifest.json` in one call (step 4.0) — the input the merge in step 4 consumes. It reads every layer body itself; there is no separate granular per-layer fetch command.
- `get-schema` / `get-sql-schema` for referenced C#/SQL; package/app tools (`list-packages`, `list-apps`, `get-app-info`) for ownership/lock/editability; `get-component-info` / `list-page-templates` for Freedom capabilities.
- For every Classic page/detail, resolve the parent schema chain and the effective template structure before choosing a Freedom target template.
- **Classic dashboards of the section:** `execute-esq` over `SysDashboard` filtered by `Section` = the target's `SysModule`; record count and captions in `discovery.md`, or an explicit `none`. They are migrated by the platform's Dashboards Migrator in step 7.7 — never redrawn by hand as a Freedom page, and never held against the plan as a gap.
### 3. Decide Package Placement
Decide *where* Freedom artifacts can be created before choosing templates. Follow `./references/classic-to-freedom-mapping.md` (Package Placement Mapping) for the decision table and the evidence to collect.
1. Identify the Classic owning package/app: name, UId, maintainer, installed app, dependencies, lock state; existing Freedom pages for the entity and their packages (step 3.2).
2. Classify: **same package** (editable + source-owned + matches ownership) · **replacing/extension package** (original locked but replacement is supported) · **new package/app** (read-only, vendor/base, unsafe, or user wants isolation) · **blocked/manual** (ownership/lock unverifiable; touching it risks a shared/base package).
3. Record evidence + decision in the plan. Verify a user-specified strategy is technically possible; call out conflicts. Whole-package → decide once and reuse (a vendor/locked owning package ⇒ new package/app for the app's own sections + replacing deltas for base sections it extends).
**3.1 — A writable package is NOT enough: settle whether an APP can host the section (`manifest.placement`).**
An editable package answers "where do pages go", not "can the section be registered in the menu" —
conflating them builds every page, then finds at the last unit that `create-app-section` cannot run. **`create-app-section` takes no package
parameter: it writes to the APP's PRIMARY package.** So a menu-registered section needs the app's
primary package to BE the target package, and to be writable — and an app installed from a package
may have **no primary package at all**. None of this is derivable from the page bodies, so record it
as facts, not prose — the engine gates `--plan` on it:
```json
"placement": {
"targetPackageEditable": { "resolved": true, "value": true, "evidence": "InstallType 0; every layer isClientEditable:true" },
"application": { "resolved": true, "code": "UsrTasksApp" },
"primaryPackage": { "resolved": true, "name": "UsrTasks", "editable": true },
"targetPackageInApplication": { "resolved": true, "value": true },
"sectionHost": { "resolved": true, "mode": "existing-app" }
}
```
Collect every value **read-only**, and record a verified `null`/`false` exactly like a verified
"none" elsewhere — "never checked" is what the gate rejects:
| Fact | How to read it |
|---|---|
| `targetPackageEditable` | `list-packages` + `SysPackage.InstallType` (0 = editable) + per-layer `isClientEditable` |
| `application` | `find-app` / `get-app-info` for the package that owns the entity; `code: null` when no app owns it |
| `primaryPackage` | `get-app-info`. **`{"success":false,"error":"Primary package not found in response."}` IS the answer** — that app has none: record `{"resolved": true, "name": null, "editable": false}`, do not treat it as a tool failure |
| `targetPackageInApplication` | `odata-read SysPackageInInstalledApp` filtered by `SysPackage/Id` of the target package (`count: 0` ⇒ `false`) |
Then decide `sectionHost.mode` — and put the decision to the user whenever it is not `existing-app`:
| Mode | When | What the build does |
|---|---|---|
| `existing-app` | the app's primary package IS the target package and is editable | `create-app-section` into that app |
| `new-app` | the owning app cannot host it (no primary, locked primary, primary ≠ target) and the user wants a menu entry | ONE `create-app` call carrying the entity (the app, its editable primary package and the section) — never followed by `create-app-section`; see `./references/build-scaffolding.md` |
| `pages-only-no-menu` | the user accepts pages reachable by URL / page bindings only | no registration; the checklist row is rendered as a deliberate drop, not a gated deliverable |
| `existing-section` | the user extends the existing Freedom section (step 3.2) | no registration; extensions of `sectionHost.listPage`/`formPage` in the target package |
**The `new-app` call, and why nobody repairs an app's package composition, are in
`./references/build-scaffolding.md`** (handed to the Scaffolding task, step 7.3); read it here only to explain the mode.
**3.2 — A Freedom section/page for the object already exists:** before the plan, ask the user ONCE: extend it (default) or build a parallel section, per `./references/existing-freedom-reconcile.md` (*Choosing the path*); answer in `decisions.md`.
### 4. Reconstruct The Effective Classic Page (engine)
`get-client-unit-schema` returns only the top replacing schema's own body — for base-product pages often a thin override with empty `diff`/`details`/`businessRules`. **An empty block in one schema is NOT evidence the page has none** — never report "no rules / no layout / no details" from a single schema. Reconstruct the *effective* page by merging the whole chain. Prefer the bundled engine over hand-merging; do not eyeball-merge.
**4.0 — Fastest path: assemble the whole manifest in one clio call (`get-classic-page-sources`).**
`clio-run { "command": "get-classic-page-sources", "args": { "environment-name": "<ResolvedEnvironment>", "schema-name": "<PageSchema>", "output-file": "<scratch>/manifest.json" } }`
does 4.1–4.2 server-side: it enumerates the same-named schema chain (base→top), loads every body,
walks the parent-template seed, and gathers
`entity`/`entityColumns`/`columnTitles`/`resources`/`detailSchemas`/`section`/`childPageSchemas` —
then writes `manifest.json` to disk. The bodies live in that file, **never in the response** (you
get back only the path + counts), so multi-KB schema bodies never pass through you and there is no
transcription slip to corrupt the fold. Point `--output-file` at your scratch dir, never the repo
(4.2's temp policy). Then resolve the section's list columns through the long-tail route
`clio-run { "command": "get-classic-list-columns", "args": { "environment-name": "<ResolvedEnvironment>", "schema-name": "<SectionSchema>" } }`
(schema name from `list-entity-client-schemas.sections[].sectionSchema`) and enrich the generated
`section` array as
`{ "schemas": <existing-array>, "seed": <section-seed>, "listColumns": <tool-response> }`.
**`section.seed` needs a SECOND `get-classic-page-sources`, rooted at the section itself** —
`clio-run { "command": "get-classic-page-sources", "args": { "environment-name": "<ResolvedEnvironment>", "schema-name": "<SectionSchema>", "output-file": "<scratch>/section-manifest.json" } }`
— and you copy that file's `seed` array into `section.seed`. The page-rooted call above walks the
PAGE's template chain (`BaseModulePageV2` → …), not the section's; the section extends
`BaseDataView` [`CrtUIPlatform7x`], which is what defines `DataGrid`, `activeRowActions` and the
`…ActionButtons…` containers. Without it the section's own `diff` still folds, but every element
merges onto nothing, so base chrome cannot be told from what the section declares and the elements
have no list region to be placed on — the plan then says so in the `### List page` block rather than
guessing. Measured: rooting at `LeadSectionV2` returned 14 layers + 20 seed and folded in 111 ms
with `unresolvedParents: []`, so this is one extra call, not a new class of work. Resolve both
command contracts with `get-tool-contract` first. **Use the same explicit environment resolved in
step 1 for `list-entity-client-schemas`, `get-classic-page-sources`, and `get-classic-list-columns`;
never let any of these reads fall back to a default environment.** A successful response always
names `source`: `schema-default`, `entity-default`, or `none`. Only `schema-default` closes the
product-level column question (the plan narrows it to "keep this set in Freedom?"); `entity-default`
is a single fallback column the Classic section never declared, so the plan qualifies it and keeps
the question, and `none` is resolved evidence that deliberately leaves it open. If an older clio
does not expose this command, keep the legacy `section` array — the engine still parses static
schema columns and retains the manual fallback. Nesting `listColumns` also requires
**`planMeta.sectionSchema`** (step 6) — it is the anchor the engine checks the response's provenance
against, so set it to the very `<SectionSchema>` you passed here; without it the evidence is gated
(the plan still renders and names the remedy) rather than used. **If the command RUNS but FAILS**
(`success:false` — unreachable stand, auth, a section schema that resolves no entity, stale
metadata), nest the failed response anyway: the engine routes it into the STRUCTURE gate and still
renders a plan naming the cause and the remedy, so fix the read and re-run (or drop
`section.listColumns` to fall back to the chain parse) — a failed read never destroys the run. **The
bundle does NOT gather the agent-supplied fields** — after the file is written, add `template`,
`targetPackage`, `placement` (step 3.1), `planMeta` (4.2 / step 6) and `runDiagnostics` (1.2a) before you run the engine
(4.3). This is the ONLY manifest-assembly command and there is no granular per-layer fetch tool — if
the bundle cannot run (older clio, or it errors on an edge-case page), fall back to reconstructing
the bodies by hand (4.1) from the surviving reads.
**4.1 — Acquire the schema bodies, base→top, INCLUDING the parent-template chain (the F2 seed is mandatory).**
*(4.0 does all of this in one call — do 4.1 by hand only when the bundle cannot run.)* Read each layer body from the surviving reads — `get-client-unit-schema` (the top replacing schema's own body) plus `download-configuration-by-environment` / the designer for the rest of the chain (per `./references/classic-to-freedom-mapping.md`). Then follow the page's `parentName` up the platform template chain (e.g. `Applicant1Page` → `BaseModulePageV2` → `BasePageV2` → `BaseEntityPage`) and fetch those base-template schemas too. The base template defines the base containers (`LeftModulesContainer`, `Tabs`, `ProfileContainer`), the base actions (the `ProcessButton` = Run process), and the ESN/Feed tab. **The seed MUST be the real fetched bodies pasted verbatim — NOT a hand-authored skeleton.** A skeleton listing only container names clears the parent check yet silently drops base actions and the true container nesting (the engine detects this as `seedQuality.looksSkeletal` and blocks — see 4.3). If you truly cannot fetch a base body, say so and stop; do not fabricate one. (Tools unavailable → enumerate + read each body via `get-client-unit-schema` / `download-configuration-by-environment` / the designer, per `./references/classic-to-freedom-mapping.md`.)
**4.2 — Build the manifest and supply the resolution inputs.**
*(Ran 4.0? The bundle already wrote every field below except the agent-supplied ones and resolved list columns — wrap its `section` array with the `get-classic-list-columns` response as described above, then ADD `template` / `targetPackage` / `placement` / `planMeta`. Building the whole manifest by hand instead:)* Write `{ "entity", "entityColumns", "schemas":[{pkg,body}], "seed":[{pkg,body}], "resources":{…}, "columnTitles":{…}, "detailSchemas":{…}, "section":{ "schemas":[…], "listColumns":{…} }, "childPageSchemas":{…}, "template", "targetPackage", "placement":{…}, "planMeta":{…} }` — paste each fetched layer body inline. (`planMeta` = the plan's Overview/Main-scope values; see step 6.) **Write the manifest and the fetched Classic bodies to a temporary directory OUTSIDE the migration repository's working tree** (your agent's scratch/temp area, or the OS temp dir — never inside the repo), and pass that path to `migrate.mjs`. They carry stand-sourced customer captions/values, so keeping them outside the repo means there is nothing to `.gitignore` and nothing that can be accidentally committed. **Delete that temp directory once the migration is complete** (step 8) — the raw inputs have no further use. (Only the OUTPUT is versioned in the project's migration folder: the `--out plan.md` file plus the doc set `plan.md`/`worklog.md`/….) Supply these so the spec shows real names, not codes, and both gates can clear:
- **`seed` (required)** — the fetched parent-template bodies (step 4.1). The gate BLOCKS a run with no `seed` (a Classic page always extends a base template; skipping it drops inherited base actions + container layout). The only escape is `"noParentTemplate": true` — set it ONLY when you have VERIFIED on-stand that the page genuinely has no parent template; it is not a shortcut around fetching the seed.
- **`template` / `targetPackage`** — the chosen Freedom form template and target package; they fill the design-spec header.
- **`resources`** — the schema's localizable strings (`{ "SomeTabCaption": "Vacancies", … }`, from `SysLocalizableValue`) → the plan can SHOW real tab/group captions instead of raw `Resources.Strings.*` keys, and the engine echoes them back on `changeSet.resources` (key → text) as the exact set of localized strings you must author when building (other cultures: `./references/manifest-conditional-inputs.md` → *Texts*). Note: the page itself keeps a localizable **binding** on every caption — user-visible text is always a localizable binding, never an inline literal (clio rejects hardcoded page text; AGENTS.md). The usual binding is `$Resources.Strings.<key>`, but a **tab / card-toggle-panel caption** must use `#ResourceString(<Key>)#` instead (see `./references/classic-to-freedom-mapping.md` — a `$Resources.Strings.*` caption there won't render). Column-bound fields carry no page label at all: they auto-label from the entity column's own title, so supply `columnTitles` for the plan's benefit, not for a page caption.
- **`columnTitles`** — the entity's column titles (`{ "MobilePhone": "Mobile phone", … }`) → field labels read like the classic page. Read columns with the clio entity-schema reader (`get-entity-schema-properties`, args via `get-tool-contract`) — it is environment-aware. Do **NOT** use `describe-entity` (different MCP server, no environment parameter — it cannot target the migration stand).
- **`enumVocabulary` (optional; the drift guard's only input)** — the TARGET STAND's own enum values, as `{ "ViewItemType": {…}, "ContentType": {…}, "DataValueType": {…} }` (member name → number). The engine pins these three enums from core `sysenums.js` and reads them to identify every Classic element, so a stand on a different platform line can disagree. Supply this and the engine diffs it against its pinned tables: a **value mismatch BLOCKS** (every element of that kind would be mis-identified), a member only the **stand** carries surfaces as the non-blocking `enum-drift-advisory` (so a release that merely adds a member cannot halt every migration), and a member only the engine carries is not a finding. **Omit it and the guard reports nothing at all** — that is the documented behaviour, not a silent pass, and it is the current state: the clio-side `get-classic-page-sources` echo that would produce this key has not shipped yet. Nothing validates the shape, so a misspelled key is indistinguishable from omitting it.
- **`entityColumns`** — each entry as an object `{ type, length, ref, title }` (from the same reader) so the Layout `Type` reads `Text (250)` / `Lookup (Contact)`, not a bare type. Canonical source for the control-type decision — don't infer types from the schema body when the reader is available.
- **`detailSchemas` (required — fetch EVERY custom detail before the plan)** — each detail's fetched body, `title` and `resourceStrings`. Resolves auto-named details, related-list **columns** and the list title (→ *Texts*). The structure gate blocks without them. **Supply the detail's FULL replacing CHAIN, not just the top layer**, as `"bodies": ["<base>", …, "<top>"]` (base→top). Classic replacing schemas are read PER-LAYER (not merged like Freedom's full-hierarchy), and behaviour that governs the related list — **read-only** (`getAddRecordButtonVisible: return false` — the system-maintained stage-history pattern), a disabled/custom add, or fixed list filters — is frequently declared in a BASE layer while the client's top override only tweaks columns. The engine scans the UNION of `bodies` for these signals, so a top-only `body` MISSES a base-declared read-only and the detail is wrongly shown as add/edit/delete. A single `"body"` is still accepted for a detail with no meaningful base layer.
- **`profileSchemas` (required once the page embeds a profile CARD — the compact card of a LINKED record)** — when the engine recognises one (`changeSet.profileCards`), read `./references/manifest-conditional-inputs.md` → *profileSchemas*: what to fetch and the one other resolved answer. The structure gate blocks until each recognised card is resolved.
- **`section`** — `{ "schemas": <*Section chain>, "listColumns": <get-classic-list-columns response> }`. The engine reports the LIST page's **quick filters** (the registry fixed-filter bar — `initFixedFiltersConfig`, e.g. a period/date + owner filter), **section actions** (custom `getSectionActions` menu items, incl. the standard `getButtonMenuItem`/`Click.bindTo` shape), and the resolved list columns in the spec's `### List page` block (a surface the record-page migration does not cover). The bundle already gathers the section bodies as an array; preserve that array under `schemas` and add the tool response under `listColumns`. `schema-default` removes the list-column question (it narrows to "keep this set?"); `entity-default` keeps it, qualified — the section declares no list columns, so the plan says the shown column is a fallback rather than the configured Classic set; `none` keeps it open because no default set was resolved. A `success:false` response is gated, not fatal (4.0). The legacy bare array remains accepted when the newer tool is unavailable. These items still MUST be built: rebuild the quick filters as the Freedom list's filter controls and the section actions as list-page actions; do NOT ship a list page with the registry filter bar or its custom actions dropped.
- **`addRecordMiniPage` + `miniPageSchemas` (required for a section — the quick-add mini page, folded + gated)** — resolve it from `list-entity-client-schemas` and fold it, or record `manifest.addRecordMiniPage: false` when there is verifiably none, per `./references/manifest-conditional-inputs.md` → *addRecordMiniPage*. The structure gate blocks until the mini page is folded or explicitly `false`.
- **`memberDispositions` (only when the coverage gate names a member)** — `{ "<kind>:<member name>": { "resolved": true, "disposition": "ported"|"dropped"|"blocked"|"n/a", "note": "<why>" } }`. **Key it `<kind>:<name>`** (e.g. `"method:recalcAmount"`, `"attribute:CanEdit"`) — the gate's issue text gives you the exact key to paste. A Classic diff item is usually named for the column it binds, so `attribute:Amount` and `diff-op:Amount` are different members; a bare-name key would clear both. The coverage gate blocks on any schema member the engine produced neither an artifact nor a decision for; this is how you close one with a recorded answer instead of silence. Use it for a member you have genuinely resolved — a virtual attribute nothing reads, a message whose counterpart you traced and ported, a utility dependency that contributes nothing to this page. `"disposition": "dropped"` is a VALID answer when the `note` says why; what is not valid is leaving the member unmentioned. Do **not** use it to bulk-silence the gate: a disposition without a real note is the same self-declared skip Contract rule 4 forbids.
- **`warningDispositions` (only for a FIDELITY warning you have read and accepted)** — `{ "<op>:<name>:<schema>": { "resolved": true, "disposition": "accepted"|"reproduced-manually"|"n/a", "note": "<why>" } }` (a bare `"<op>:<name>"` key is accepted too). A fidelity warning says the engine's mapping is **correct** while some *effect* of the op is not represented in its item model — e.g. a `remove … properties` naming a key the engine does not model, or a `set` that replaces an element wholesale. Those are advisories, not blockers, so this key does not unblock anything: it **records your answer** and clears the `⚠` from the plan, keeping the warning auditable as CLOSED rather than dropping it. `accepted` = the unrepresented effect does not change the Freedom mapping; `reproduced-manually` = it does, and the build reproduces it by hand (say how in `note`); `n/a` = that element is not being migrated. Only these three values count — a typo clears nothing, exactly as with `memberDispositions`. **A `correctness` warning cannot be dispositioned:** it names an item no lower schema defined, the plan states the refusal, and the gate keeps blocking until the schema order (F1) or the base seed (F2) is fixed. **Exception — a `remove`/`merge` of a name nothing in the supplied chain defines** is `fidelity` (no block) unless some layer defines the name: read `./references/manifest-conditional-inputs.md` → *warningDispositions* before you close one.
- **`signals` (required — resolve the ⚠ on-stand checks at PLAN time, not at build)** — the plan is
**INCOMPLETE** (⛔, non-zero `--plan` exit) until you record the five conditional-feature checks in
`manifest.signals`, each
`{ "resolved": true, "present": <bool>, "cases"|"items"|"names": [...] }`. **FIRST resolve the
section's `SysModule.Id`** — processes + printables filter by it, so without it those checks
cannot run: `odata-read SysModule`
`filters {any:[{field:"Code",op:"contains",value:"<Name>"},{field:"Caption",op:"contains",value:"<Name>"}]}`
select `["Id","Caption","Code"]` (the module `Code` is usually the base entity name, e.g.
`Applicant1Section` → Code `Applicant`). Then: **`dcm`**
(`SysSchema WHERE ManagerName='DcmSchemaManager'` for the entity/family → progress bar + Next
steps), **`processes`** (`odata-read ProcessInModules` with **`filters`** (NOT `filter`)
`{all:[{field:"SysModule/Id",op:"eq",value:<sysModuleId>}]}` — filter the lookup via the
`SysModule/Id` nav — select `["SysSchemaUId","Position"]`; then the name via
`odata-read VwSysProcess` `filters {all:[{field:"Id",op:"eq",value:<SysSchemaUId>}]}` select
`["Caption","Name"]` (a process's `Id` == its `UId`, so filter by **`Id`**; no `IsMaxVersion`
filter, `Id` is unique). ProcessInModules has NO `Caption`/`SysProcessId` column — the name is on
VwSysProcess → Run process), **`printables`** (`SysModuleReport` by `SysModule`,
`ShowInSection`/`ShowInCard` → Print), **`dashboards`** (the section's 7x analytics — TWO chains,
both required; see the next bullet), and **`deduplication`** — the on-save duplicate check, the
one signal that needs **two** answers because they fail differently: **(a)** `present` — does THIS
entity have an active use-on-save rule: `odata-read DuplicatesRule` (a `BaseLookup` in
`CrtDeduplication`) select `["Name","IsActive","UseAtSave","ProcedureName"]`, keep the rows whose
`Object` is this entity with `IsActive` **and** `UseAtSave` both true (`UseAtSave` is the "Use
this rule on save" checkbox), list their names in `names` — the **canonical** key for this signal,
always an **array** (`items` is tolerated for symmetry with the sibling signals; no other alias is
read, and a bare string lists nothing rather than aborting the run); **(b)** `serviceConfigured`
(a boolean, **required whenever `present: true`** — a rule with no service answer counts as
UNRESOLVED and blocks `--plan`, since it is the half-answer that would otherwise ship an
approvable plan saying the key question is unanswered) — can the TARGET stand run the Freedom flow
at all: `get-sys-setting DeduplicationWebApiUrl` non-empty **and** features `ESDeduplication` +
`BulkESDeduplication` enabled (read `AdminUnitFeatureState` via `execute-esq`, columns
`Feature.Code` / `FeatureState` — **no state row means OFF**). Both are needed because Classic has
two paths and Freedom has one: Classic's `asyncValidate` in `CrtDeduplication.BaseEntityPage`
falls back to the rule's own SQL procedure when `ESDeduplication` is off, while the Freedom
handler (`crt.ValidateDuplicatesOnSaveHandler`, platform-registered on `crt.SaveDataRequest` for
`BasePageTemplate`/`BaseMiniPageTemplate`) goes through the service. Measured on a stand newer
than 8.3.4: Classic posted `DeduplicationService/FindDuplicatesOnSave` and showed its duplicates
screen while the Freedom form page issued only `InsertQuery` and saved the duplicate silently. A
rule **without** the service ⇒ the check stops at migration and the plan raises it as a decision,
stated inline in `### On-stand signals` and as a ⚠ Confirm row on every scope of the SAME entity
(each per-type form, the add-record mini page); a **child** edit page is a different entity and
instead gets a child-scoped row telling you to run query (a) for ITS entity; **never** write it up
as "Freedom cannot check duplicates" — it can, and the wording must stay true once the service is
on. Run them with the environment's SQL / OData query tools (whatever the connected clio MCP
exposes for an ESQ/SQL or OData read) — there is no dedicated migration command and none is
needed. **`present:false` (checked on-stand, none found) is a VALID resolved answer**; only a
missing/unresolved key blocks — the distinction is "verified none" vs "never checked". **A query
that ERRORED (e.g. couldn't resolve the SysModule.Id, or a bad column) is NOT "checked → none"** —
do NOT record `present:false` for a check whose query failed; fix the query (resolve the
SysModule.Id first) and re-run, else you are fabricating a "none" the section may not have. This
gate is what ends the recurring "build faithful to the classic body, check the case/process/print
later" miss: the answers are now in the plan the user approves, and the engine renders them under
`### On-stand signals`.
- **`signals.dashboards` (required — the section's 7x dashboards, resolved with `execute-esq`)** — a section's dashboards are stand **DATA** (`SysDashboard` rows), not schema content, so this signal is the only way they reach the engine (`./references/classic-to-freedom-mapping.md` → *Section dashboards* → *Storage model* says why). Resolve **two chains** and record both:
1. **The items.** `SysSchema` `Name = '<SectionSchema>'` → its `UId`. Match `SysModule.SectionSchemaUId` against the WHOLE set of rows that returns, not just the top layer. That gives `SysModule.Id`. Then read `SysDashboard` filtered `Section` = that `SysModule.Id`, select `["Id","Caption"]` — `Caption` is the title. Use **`execute-esq`, NOT `odata-read`** — OData cannot select or filter `SectionSchemaUId`; `./references/classic-to-freedom-mapping.md` → *Section dashboards* explains why.
2. **The delivery mode, PER dashboard** — when chain 1 returns any dashboard, resolve it per `./references/manifest-conditional-inputs.md` → *Dashboard delivery mode*.
Record: `"dashboards": { "resolved": true, "present": <bool>, "items": [{ "id": "<guid>", "caption": "<title>", "sourcePackage": "<pkg>" }] }` — **omit `sourcePackage` for a stand-only dashboard**. **`present: true` requires a non-empty `items`** — “this section has dashboards” plus “here are none of them” is a half answer and the plan refuses it; “checked, none found” is `present: false`. That is the READ, and it is all you collect. Two further fields are DECISIONS with derived defaults, and they are the USER's, not yours: `skip` (absent by default; any truthy value records a dashboard deliberately left behind, and a string says why, the way `memberDispositions` records a schema member nobody ports) and `saveInPackage` (default `!!sourcePackage` — what shipped in a package keeps shipping, what was local stays local). **`saveInPackage: true` means `manifest.targetPackage`**, the one package `placement` proved writable — never the source's package, which nothing here checks is even editable. Write either field only when the user asks for it; the engine renders the split under `### On-stand signals` so they can see and change it.
**Caveats — each of these has produced a wrong answer:**
- **An empty `Section` is a page-level dashboard, not this section's** — out of scope even when its widgets reference the section. Do not widen the filter.
- **`SysDashboard` is administrated by records** — a rights-trimmed read looks exactly like "fewer dashboards"; query as a user who sees all.
- **A query that ERRORED is not a "none" answer** — same rule as the other three signals.
- **`typedPages` + `typedPageSchemas` (required for a typed entity — each per-type form is a FULL deliverable, folded at PLAN time, gated)** — when the entity's records open a different edit page per Type, read `./references/manifest-conditional-inputs.md` → *typedPages*. The structure gate blocks the plan until every typed page is folded or `bindOnly`.
- **`childPageSchemas` (required — resolve EVERY child page before the plan)** — for each related list, the child entity's own edit-page schema as a NESTED manifest. **Resolve the edit-page name two ways:** (a) the detail body's `getEditPageName` (surfaced as `childPages[].editPage`), and (b) `list-entity-client-schemas` **by the CHILD entity** (not the parent) — its `editPages` (`kind: classic|freedom`, per-type variants) + `miniPageSchema` say directly whether the child has a real Classic edit page to rebuild, only a Freedom one (reuse), or none. The engine recursively maps each child page and nests its full field-mapping under `### Child page mappings`. Per Contract rule 4, a child with a real Classic `*Page` is mapped regardless of size; the only legitimate skips are a genuinely view-only detail with no `*Page` at all, a real native Freedom component (e.g. `ContactCommunication`), or a child entity that **owns another section** and stays on its Classic card by the user's decision (`opensClassicPage`, below).
**Record what you verified on that detail's `detailSchemas` entry so the plan reflects reality (never a guess).** Exactly four answers resolve a child, and they are the four the structure gate accepts: a Classic `*Page` exists → put its schema in `childPageSchemas` (→ `Rebuild (child)`); no `*Page` exists → `"editPage": false` (→ `Without edit page`); the CHILD entity already ships a `kind: freedom` form page → `"reuseFreedomPage": "<Freedom form page>"` (→ `Reuse (Freedom)` — the related list opens that page, nothing is rebuilt, and the Classic child page is superseded rather than skipped); the child entity **owns another section** and the user drew that boundary → `"opensClassicPage": "<Classic page>"` (+ optional `"ownSection": "<Section>"`) (→ `Reuse (Classic)`, next paragraph). Until you record one of these, the child stays **`⚠ resolve`** and the plan is STRUCTURE INCOMPLETE — this is what prevents a Main-scope row asserting `Rebuild (child)` while the mappings below say the opposite.
**`"editable": false` is NOT one of those three.** It records that the classic detail hides add-record, which stops NEW records but not opening EXISTING ones — so it tags the row view/attach-only and nothing more. A read-only list whose entity HAS a page still owes that page: pair `"editable": false` with one of the four answers above, or the child stays unresolved.
**`"opensClassicPage"` — the SECTION BOUNDARY, and it has to be asked BEFORE the fold.** The moment `list-entity-client-schemas` / `list-pages` tells you a child entity has a section of its own, read `./references/manifest-conditional-inputs.md` → *opensClassicPage* — before you fold that child's page.
**`reuseFreedomPage` does not end the job for that child.** The shipped Freedom form carries the BASE layout; whatever the client added to the Classic child page in their own packages is not on it and reuse does not carry it over. Reconcile that delta onto the reused page per `./references/existing-freedom-reconcile.md` — the same obligation a main page carries when a Freedom counterpart exists — or record the packages you checked as carrying none.
**4.3 — Run the engine and clear BOTH gates.**
Run `node engine/migrate.mjs <manifest.json>` (the `engine/` dir bundled beside this SKILL.md; resolve its absolute path in the plugin dir). It returns the effective page + the Freedom **ChangeSet** + `needsDecision[]` (your 20% worklist) + diagnostics. `--plan` renders the whole plan skeleton; `--spec` renders just the design spec; default prints the full JSON. The CLI exits `2` and prints a `⛔` banner when either gate is bad — **a non-zero run is never an approvable plan.** The rule runs the other way too: **every `⛔` the CLI prints is non-zero**, a refusal included, so the banner and the exit code are one verdict and never two.
- **`gate` — correctness (four signals under `result.effective.*`).** `parseErrors` (a body failed to parse) · `unresolvedParents` (seed incomplete = F2, or schemas out of order = F1) · `warnings` **of `severity: "correctness"`** (an op hit a missing item — same F1/F2 root; fix, don't report as a finding; a `remove`/`merge` of a name nothing in the supplied chain defines is not one of these — see `warningDispositions`). A `severity: "fidelity"` warning does **NOT** block: the engine states the mapping is right and only an *effect* of the op is unrepresented, so there is nothing in the body or the seed to fix. Those render as a `⚠` advisory in the plan and, if you have read and accepted one, are closed with `manifest.warningDispositions` (below). The gate reason **quotes each blocking warning's own hint**, so what it names is the warning that actually fired · `seedQuality.looksSkeletal` (a hand-typed skeleton seed — re-fetch the real parent bodies per 4.1). Fix the cause and re-run until `gate.blocked` is false.
- **`coverage` — member completeness (the member ledger).** `complete` only when EVERY member of every merged schema layer is accounted for: a `diff` operation, a `methods` entry, an `attributes` entry, a `messages` entry, a `mixins` entry, a `define()` dependency, a `details` entry. Each is `mapped` (the ChangeSet carries a Freedom artifact), `decision` (it is on a `⚠` worklist), `chrome` (pure decoration — a **menu separator**, and only that: recorded and COUNTED, never a `⚠` and never a block. A tooltip, a control's label and the grid-settings editor were provisionally on this list and are NOT decoration — each carries author content or a child subtree, so each raises a normal ⚠ instead), `context` (inherited base-template content, excluded by design and COUNTED, never dropped) — or `unaccounted`, which **blocks**. Precedence is `mapped` > `decision` > `chrome` > `context`, so base-template decoration counts as `chrome` rather than `context`. This is what makes "no logic missed" a machine check rather than a rule in prose: before it existed, a page's methods, its imperatively filtered lookups (`attributes.<Col>.lookupListConfig.filters`), its sandbox contract (`messages`) and its mixins produced nothing at all and both other gates stayed green. The spec renders the ledger under `#### Member ledger`, including **counted zeros** (a kind with no members is recorded as verified-empty, so "the plan says nothing about messages" can never mean "nobody looked"). To CLOSE a member the engine can only flag, record your answer in `manifest.memberDispositions` (below) — the same "a verified answer beats a guess" contract as `signals`. **The check aggregates the page TREE**, like the other two: a child page, typed page or mini page whose own members are unaccounted blocks the parent, so a parent plan can never claim a coverage its children do not have.
- **`structure` — input completeness.** `complete` only when the manifest carries RESOLVED inputs (not FILL-slot promises): `detailSchemas` for every custom detail, `profileSchemas` for every embedded profile card, and **every child page resolved** — mapped (schema in `childPageSchemas`), or explicitly marked `"editPage": false` (no `*Page`) / `"reuseFreedomPage": "<Freedom form page>"` (the child entity already ships one) after checking `list-entity-client-schemas` by the CHILD entity. `"editable": false` (view/attach-only) does NOT resolve a child on its own — it answers add-record visibility, not page existence. **An UNVERIFIED child is STRUCTURE INCOMPLETE** — t
[…] pełny plik w paczce .zip
Pliki w paczce (44)
- SKILL.md 93 KB
- classic-behaviour-analysis.workflow.js 123 KB
- agents/openai.yaml 1 KB
- docs/imperative-logic-gap.md 24 KB
- engine/README.md 64 KB
- engine/assemble.mjs 27 KB
- engine/designspec.mjs 420 KB
- engine/diagnostics.mjs 11 KB
- engine/engine.mjs 167 KB
- engine/fsatomic.mjs 5 KB
- engine/mapper.mjs 261 KB
- engine/mapping-registry.mjs 24 KB
- engine/mapping-table.mjs 75 KB
- engine/migrate.mjs 381 KB
- engine/package-lock.json 1 KB
- engine/package.json 1 KB
- engine/reads.mjs 20 KB
- engine/reconcile-modes.mjs 1 KB
- engine/report.mjs 45 KB
- engine/split.mjs 32 KB
- engine/tasks.mjs 304 KB
- engine/verify-vendor-upstream.mjs 7 KB
- engine/verify-vendor.mjs 9 KB
- engine/registry/component-index.json 234 KB
- engine/vendor/acorn-LICENSE.txt 1 KB
- engine/vendor/acorn.cjs 239 KB
- engine/vendor/provenance.json 1 KB
- references/analysis-summary.md 6 KB
- references/behaviour-analysis-run.md 16 KB
- references/build-dashboards.md 10 KB
- references/build-page.md 35 KB
- references/build-scaffolding.md 6 KB
- references/build-task-execution.md 24 KB
- references/classic-to-freedom-mapping.md 68 KB
- references/existing-freedom-reconcile.md 19 KB
- references/freedom-ui-browser-check.md 12 KB
- references/judge-brief.md 10 KB
- references/manifest-conditional-inputs.md 14 KB
- references/migration-documentation.md 22 KB
- references/migration-plan-template.md 5 KB
- references/orchestrate-build.md 64 KB
- references/page-design-spec.md 19 KB
- references/read-back-brief.md 4 KB
- references/reference-cache-brief.md 3 KB
Wersje
W bazie nie ma innych skilli o nazwie classic-to-freedom-migration.
Migawka bazy z 28.10.2026 · wersja paczki c3154be1f55e (skrót SHA-256 całej paczki)
Zgłoś problem z tym skillem: szkodliwa treść, błędny opis albo prośba autora o usunięcie.
Podobne skille
- 90creatio-mobile-page-conversion
Przerabia stronę webową Creatio Freedom UI na stronę mobilną dla aplikacji Creatio Mobile, w kontrolowanym procesie. - 90classic-ui-expert
Opisuje każde dostosowanie widoku w Creatio Classic UI i wskazuje, po co istnieje, z dowodem w źródle. - 100classic-template-hierarchy
Dobiera, tworzy lub audytuje klasyczne szablony PHP WordPress dla WP 7.1 według hierarchii szablonów: front-page, home, single, archive, get_template_part, kolejność nadpisań motywu potomnego i template_include. - 100comet-build
Planuje, implementuje i akceptuje zadania w trybie Classic, gdy użytkownik wywołuje /comet-build lub środowisko Classic wchodzi do fazy Build albo wraca do niej po poprawki. - 90comet-build
Planuje, realizuje i odbiera zadania w trybie Classic po poleceniu /comet-build lub gdy Classic Runtime wchodzi w Build albo wraca do Build po poprawkach. - 89wp-theme-development
Przegląda motywy WordPressa 6.6+: schemat theme.json, hierarchię szablonów, części szablonów, style globalne i wariacje stylów.