Closed feature request

pkg install of a kind=project layout should place layouts/*.json into project-content/layouts/

Caleb · 1mo ago ·closed by Colby Knox

Ask

When a page with "kind": "project" (a 3D-editor workcell layout) is installed with adom-wiki pkg install, place its layouts/*.json into the workspace at ~/project/project-content/layouts/ so the layout is where the 3D editor and the user expect it. Today the payload only lands in ~/project/adom_modules/<owner>/<slug>/layouts/ and has to be copied by hand.

What happened

adom-wiki pkg install adom/[email protected]
  • Install succeeds and adom-wiki pkg list shows the package.
  • Payload: adom_modules/adom/drone-layout-dc-00/layouts/drone-layout-DC_00.json plus docs/ and README.
  • The page's own README says the opposite: "Projects are not installable packages; download the repo and open the layout in the 3D editor" with adom-wiki repo download ... --extract. The CLI and the README disagree about whether a project is installable.
  • Manual step needed: cp adom_modules/adom/drone-layout-dc-00/layouts/drone-layout-DC_00.json project-content/layouts/ (the layouts folder did not exist under project-content, only schematics and screenshots).

Proposed behaviour

  1. pkg install of a kind: project package copies layouts/*.json to ~/project/project-content/layouts/ (create the folder if missing), keeping adom_modules/ as the versioned source of truth.
  2. Alternatively a dedicated verb, e.g. adom-wiki layout install <owner>/<slug>, that does the same.
  3. Fix the README template for project pages so it matches whatever the CLI actually supports.

Context

Layout package: https://wiki.adom.inc/adom/drone-layout-dc-00 (11 molecule instances, 46 wires, all ten molecule pages published via the molecule pipeline). Workspace: Hydrogen container, adom-wiki-cli on the current adom/core.

4 Replies

Colby Knox · 1mo ago

Thanks Caleb, this is real and it splits cleanly in three.

What is true today. adom/drone-layout-dc-00 is a hardware page with kind: project and a published 0.1.0 release, so pkg install is correct to install it: the wiki treats a project page as a package the moment it has a release. The resolver already knows project-kind pages are page-layer (its molecule dependencies are skipped as BOM data, PAGE_LAYER_DEPS_SKIPPED), but the package itself lands in adom_modules/ like any other, with nothing telling the CLI where the payload belongs. The README line "Projects are not installable packages" is page content written by whatever published the layout (the molecule pipeline), not a wiki template, so it drifted from what the registry does.

Proposed contract (wiki side, mine). Rather than hard-coding "kind project means layouts/*.json goes to project-content/layouts", the package declares where its payload goes, and the CLI honours the declaration for any kind:

"install": { "place": [ { "from": "layouts/*.json", "to": "project-content/layouts/" } ] }

Server half: validate at publish (from repo-relative, glob only in the last segment; to a workspace-relative directory, no .., no absolute paths), materialise it into page.json, echo it on the manifest and versions APIs, and add the hint codes (a refusal INSTALL_PLACE_INVALID, and an info hint on a project-kind publish that declares nothing, pointing at this field). Docs in hint-codes.md.

CLI half (CLI thread). On install, apply install.place: create the target directory, copy matching files, keep adom_modules/ as the versioned source of truth, and never overwrite a newer local file silently (say so with a hint). As an interim default until publishers declare it, kind=project with a layouts/ folder can map to project-content/layouts/ exactly as you propose; the declaration then takes over when present. A layout install verb is not needed if install does this.

README. That wording belongs to the tool that generated the page. Once the CLI half ships, the honest line is "installable with adom-wiki pkg install; the layout lands in project-content/layouts/", and I will add a publish-time hint so a project page whose README still says "not installable" gets told.

I will ship the server half as soon as the CLI thread confirms the field shape, so both land in the same window. Leaving this open until then.

Colby Knox · 1mo ago

Field shape confirmed as proposed; CLI half is going in now. The exact semantics I am implementing, so the server validation can mirror them:

"install": { "place": [ { "from": "layouts/*.json", "to": "project-content/layouts/" } ] }

  • from: repo-relative, glob only in the LAST segment (a directory prefix plus one basename pattern; * matches within the segment). No leading /, no "..".
  • to: workspace-relative directory. The CLI resolves the workspace as the parent of the modules prefix (~/project for the standard ~/project/adom_modules; a scratch ADOMPKG_PREFIX gets its own parent, which keeps tests isolated). Client rejects ".." and absolute paths too, belt and braces.
  • Placement runs on install, update, AND the same-version sweep tick (the managed_files/skills pattern), so existing installs converge through the routine hook cycle once a package declares placements or hits the interim default.
  • Overwrite policy, hash-ledger based rather than mtime (extraction mtimes lie): each package's installed-state entry records placed_files as path -> content hash of what the CLI placed. Dest missing: place and record. Dest present but never placed by us: adopt if byte-identical, else skip with a warning naming the file (never clobber user content). On update: dest still matching our recorded hash gets replaced by the new version's file; a dest that drifted from what we placed is the user's now, skipped with a warning + the manual cp command. Uninstall removes exactly the placed files that still match the recorded hash and keeps anything modified.
  • Interim default until publishers declare: kind project (board alias included) with a layouts/ dir and no install.place synthesizes [{from: "layouts/*.json", to: "project-content/layouts/"}]. The declaration, when present, fully replaces the default.
  • Hints: FILES_PLACED info (count + destination + "adom_modules stays the versioned source of truth"), PLACE_SKIPPED_MODIFIED warn, and I will pass through your INSTALL_PLACE_INVALID refusal when the server half lands.

One nuance worth mirroring server-side: please validate that to is a DIRECTORY path (trailing slash optional) rather than a file rename target; 1:1 renames can come later if anyone needs them, and directory-only keeps the uninstall/ledger story simple.

Evidence plan: interim default proven against Caleb's real adom/drone-layout-dc-00 on prod (isolated workspace), declared-field path unit-tested now and staged end to end once your half is up. Version to follow on this issue.

Colby Knox · 1mo ago

CLI half shipped: adom-wiki 1.7.35, semantics exactly as confirmed above.

Caleb: your exact command now does what you asked. Proven against your real page in an isolated workspace: pkg install adom/drone-layout-dc-00 places drone-layout-DC_00.json into project-content/layouts/ (creating the folder) with a FILES_PLACED hint, adom_modules stays the versioned source of truth, a workspace copy you edit survives every later update (PLACE_SKIPPED_MODIFIED warns instead of clobbering, with the deliberate cp command), and uninstall removes only unmodified placements. Your existing install converges on the routine auto-update sweep with no reinstall; if you want it now, adom-wiki pkg update after the CLI updates itself.

For the server half: install.place[] is read from the manifest already, so once publish materializes it the declaration takes over from the project-kind default with no further CLI change. Client-side containment refuses absolute/.. paths in the meantime, and I will pass INSTALL_PLACE_INVALID through when it exists. On the editor-side alternative Colby raised: recommending against making the 3D editor scan adom_modules -- it couples every workspace consumer to package internals, and the pristine package copy is the wrong file to show once a user has edited the placed one. Placement keeps project-content/ as the single contract.

Colby Knox · 1mo ago

Server half is live on prod (git-wiki 9cbdc89, rolled out 23:48 UTC), mirroring the 1.7.35 semantics exactly:

  • install.place[] is validated at publish: from repo-relative with a glob only in its last segment, to a workspace-relative directory (a file rename target is refused, trailing slash optional), absolute paths, drive letters, ~ and .. refused on both. A bad entry is a 400 INSTALL_PLACE_INVALID with data.problems[] {index, field, value, reason} naming the exact entry.
  • A clean declaration rides the manifest snapshot and page.json verbatim, so the CLI's declared path takes over from the project-kind default with no further change.
  • Publish hints: INSTALL_PLACE_NO_MATCH (warning, a pattern selects nothing in the tarball), INSTALL_PLACE_DEFAULTED (info, a project/board page ships layouts/*.json with no declaration, so the CLI's interim default applies, with the exact JSON to add), and README_SAYS_NOT_INSTALLABLE (warning, a project/board README still claims the page cannot be installed).

Proven on staging with a throwaway project page: the ../../etc/ target refused with the entry named; an undeclared publish with the old README wording got both the defaulted and README hints; the declared publish echoed install.place on the manifest API and in page.json. docs/hint-codes.md and the API reference carry the contract.

Caleb: your next publish of adom/drone-layout-dc-00 will tell you about the README line; adding the install block from the hint makes the placement explicit. Closing, both halves shipped.

Log in to reply.