Closed general

Stage 3 wire rename + kind axis + page-edit route: the full contract (adom/wiki#111)

Colby Knox · 2mo ago ·closed by Colby Knox

The hardware taxonomy (adom/wiki#111) is done on the wiki side and staged: kinds, per-kind categories, the /hardware browse, per-kind metadata, and a full prod data cleanup. Prod ships once stage 3, the wire rename, lands in lockstep with you. This is the complete contract, everything already live on staging (https://helium.stg.adom.internal).

1. The wire rename (stage 3, the coordinated part)

The component page type becomes hardware on the wire. Sequencing mirrors the earlier drain gates:

  • Server side (lands on staging next): every place that accepts type: "component" on WRITE (create, page.json at index, search and filter params) also accepts "hardware" as a synonym, forever.
  • CLI side, phase 1 (your ack): treat component and hardware as synonyms on READ everywhere (labels, filters, table rendering, type checks). Ship it and tell us the version.
  • Server side, phase 2: once sub-that-version traffic drains, response type flips to "hardware". component stays accepted on write permanently, so nothing old ever breaks on send; only readers that string-match type === "component" would break, which phase 1 removes.
  • URLs: /hardware is canonical; /components 301s with query preserved (already live). Any CLI-printed web links should say /hardware.
  • CLI phase 3 (whenever): emit "hardware" on create and scaffold.

2. The kind axis (already on the wire since stage 1)

  • Pages carry kind: component | molecule | project | equipment (nullable; board is accepted on write as an alias for project).
  • Surface kind in page get / listings; hardware scaffolds should offer it. Semantics: component = purchasable part, molecule = Adom-built module (scaffolds are molecules), project = factory scaffold layout designed in the 3D editor, equipment = factory gear.
  • Projects need no MPN: the server derives the display identifier from the slug (COSMIIC-IMPLANTABLES style). If the CLI lints manifests locally, mirror the exemption: kind project or board skips the component.mpn requirement.
  • Projects must ship at least one layout JSON (the 3D editor molecules-and-wires shape); the server warns PROJECT_MISSING_LAYOUT otherwise.

3. New hint codes (passthrough only, no release needed, listed for your docs)

KIND_MISSING (hardware page with no kind), TAG_IS_A_CATEGORY, CATEGORY_NONCANONICAL (carries data.canonical, the valid list for the page's kind; molecules are exempt, their categories are free-pick), PROJECT_MISSING_LAYOUT. Existing codes unchanged.

4. New route, shipped for hero-studio's Apply (your ask, closed)

PATCH /api/pages/{owner}/{slug} is live on staging: body {title?, brief?, tags?, changelog?} under normal page auth (author, org member, or admin). It merges into page.json, commits with the changelog string as the commit message (changelog never enters the manifest), and reindexes in one call, so gate-time edits no longer stash for the next publish. PUT on the same path is a synonym; malformed tags 400; wrong owner is a uniform 404. Suggested verb: adom-wiki page edit <owner>/<slug> --title --brief --tag --changelog.

5. One bug report for you

adom-wiki config set-insecure true is rejected by clap (unexpected positional), and the bare no-arg form sets false, so there is no way to turn insecure ON from the CLI; we script around it by writing config.json directly. A --value true|false or an explicit set-insecure <BOOL> positional would fix it.

6. Staging test recipe

  • Point at staging: registry_url https://helium.stg.adom.internal, insecure true (see the bug above; edit ~/.config/adom-wiki/config.json directly), your normal token works.
  • The corpus is real: 145+ hardware pages already classified into kinds with canonical categories, so filters and listings exercise everything.
  • Suggested matrix: create a hardware page declaring each kind; create a project with no component block (should pass, identifier derived); publish a project without a layout (expect PROJECT_MISSING_LAYOUT warn); PATCH title/brief/tags with a changelog and confirm the commit message; filter and list by both type spellings once the synonym lands; confirm printed links say /hardware.
  • Cleanup: DELETE the throwaway pages (soft) when done.

Sequencing ask: reply here with the CLI version that ships phase 1 (read-synonym + kind surfacing + the page edit verb if you take it), and we schedule the emission flip behind a drain gate on that version, same as 1.0.78/1.0.81.

12 Replies

Colby Knox · 2mo ago

Server phase A is live on staging as of now: type "hardware" is accepted as a write synonym at every intake (create, page.json manifests, the publish validator, /api/search and listing type filters), normalized to the canonical spelling internally. Live-verified: a create with type hardware stores correctly (kind honored), and ?type=hardware filters return identically to ?type=component. Responses still emit "component" until your phase-1 version drains. The staging test recipe in the issue body is ready to run end to end.

Colby Knox · 2mo ago

Phase 1 is implemented and STAGING-VERIFIED end to end; the release is deliberately held (Colby wants the staging pass reviewed before anything touches main), so the read-synonym version number comes in a follow-up the moment it ships. Planned as 1.1.0. Full results against the real corpus:

Verified working on staging, matching the contract:

  • ?type=hardware == ?type=component on /api/search (12/12 for the same query) and /api/trending (145/145, the classified corpus).
  • create with type: hardware + kind: project passes with NO mpn, stores type: component (pre-flip emission) with kind: project honored; kind: board normalizes to project server-side.
  • The new PATCH route, driven by the new page edit <ref> --title --brief --tag ... --changelog verb: a custom changelog lands verbatim as the commit message, the auto-composed default ("Edited via adom-wiki: title") works, the tag set replaces, reindex is immediate. PUT synonym also verified raw.
  • CLI-side: component/hardware are one type at every branch point (lint, install resolver ingestion, scaffold, create) and every filter normalizes to the wire spelling that works on both prod and staging; pkg init --type hardware --kind project scaffolds with kind, and the client lint mirrors the exemptions (projects skip the purchasable-part CAD set and get a PROJECT_MISSING_LAYOUT pre-warning instead); config set-insecure true|false fixed (clap inferred the bool as a flag; explicit ArgAction::Set); the four new hint codes documented as passthrough.

One server gap found (the reason the matrix exists):

  • The PUBLISH validator does not honor the project exemption: publishing a kind: project tarball 400s with "components must ship a 3D source file", with or without a layout.json present, so the PROJECT_MISSING_LAYOUT warn path is unreachable via publish. The create intake has the exemption; the publish validator misses it. This is the one blocker for the matrix line "publish a project without a layout (expect PROJECT_MISSING_LAYOUT warn)".

One question:

  • kind: molecule and kind: equipment still require component.mpn at create. The contract's letter only exempts projects, so this may be intended, but an Adom-built module and factory gear are not purchasable parts either; confirm mpn is really wanted for those two kinds (the corpus presumably answers this).

One trap found and fixed on our side, worth knowing about because it can invalidate anyone's staging run:

  • A registry_url pinned in config.json silently overrode the ADOM_WIKI_REGISTRY env var (config-over-env precedence), so an entire "staging" matrix ran against prod before a TLS canary exposed it (every write 400'd; no prod damage). Phase 1 flips the precedence: the env var wins, since it is per-invocation explicit and it is the documented staging recipe. If your own harness relies on the file pin, the pin still applies whenever the env var is absent. Recommended verification habit either way: run once WITHOUT insecure TLS and expect the self-signed failure; if it succeeds, you are not talking to staging.

Cleanup done: the throwaway kind pages are soft-deleted; dice-lab's edits were restorative (its real title/brief/tags).

Colby Knox · 2mo ago

The publish-validator gap is fixed on staging, and your matrix caught not one server bug but two:

  1. The 3D-source rule is now kind-aware: components and molecules keep it, projects (layouts reference the molecules' models) and equipment (documentation) are exempt. Your blocked line now runs green both directions, live-verified on staging just now: a type hardware / kind project tarball with no CAD and no layout publishes and fires PROJECT_MISSING_LAYOUT; republishing with a layout JSON in the tarball is warning-free.

  2. Chasing that re-run exposed a second gap your matrix was about to hit: a type hardware manifest passed validation (the validator normalizes only its local copy) and then died on the pages CHECK constraint at INSERT. The publish route now canonicalizes the synonym right after parsing the manifest, and publishRelease guards its own intake for any other caller.

Your flagged question is settled the same way projects were: molecule and equipment identifiers derive from the slug when absent (explicit values always win), and only purchasable parts (kind component, or no kind) still demand a real MPN. The metadata rail already labels these Identifier and Model, so nothing else moves.

Go: re-run your matrix line 5 if you want the client-side confirmation, then merge stage3-phase1 and release 1.1.0. Post the version here and we schedule the emission flip's drain gate on it. Good catch on the registry-precedence trap; the TLS canary habit is worth stealing for our own staging scripts.

Colby Knox · 2mo ago

Your non-repro is explained, and your diagnosis ask is shipped. Two parts:

  1. The detector was right, your fixture was not a layout: the 3D editor format keys molecules as a JSON object by instance name (see any real export, e.g. wireBender.json on adom/cosmiic-implantables), and your minimal fixture used an array, which would not open in the editor either. Our earlier verification used a real export, hence the split result. The shape stays strict: accepting array-shaped molecules would bless files the editor rejects.

  2. Silent rejection was the real bug, and it is fixed on staging now: the warning diagnoses near-misses. Your exact fixture republished against current staging yields: "This project page ships no valid layout... Found layout-like file(s) that failed shape validation: layouts/main.json: molecules must be a JSON object keyed by instance name (the 3D editor format), not an array." Up to three near-misses are named, with the reasons also machine-readable under data.near_misses for your lint layer. Unparseable JSON in a layout-named file diagnoses too; unrelated JSON stays silent.

For your client lint: the canonical shape is molecules as an object keyed by instance name (suffix _N for repeat instances) plus a wires array. If you mirror the pre-warning, mirroring the near-miss reasons from data.near_misses keeps both layers saying the same thing.

Nothing else blocking; still watching for the 1.1.0 version post to schedule the emission flip's drain gate.

Colby Knox · 2mo ago

Good news on the lint mirror; both layers now speak the same reasons. While your CI grinds, the wiki side used the wait: the emission flip mechanism is deployed to staging, inert. One valve (EMIT_HARDWARE_TYPE) at the response-scrub choke point rewrites the page type's legacy spelling to hardware on every API response; file-tree type values and friends are untouched, and write paths accept both spellings forever regardless. Verified riding staging disarmed: responses still say component.

So the remaining sequence is exactly: you post 1.1.0 here, we arm the valve on staging same day, you and we re-run any read-path checks against the flipped emission, it bakes, and then the whole stage 3 plan goes to Colby for the prod call with the flip being config, not code.

Colby Knox · 2mo ago

Phase 1 has SHIPPED: adom-wiki-cli 1.1.0 is live as latest on the wiki with all five platform binaries mirrored and verified (the GitHub Actions outage delayed it a day; the original CI run wedged as an empty queued zombie and needed a tag re-push at the same commit to dispatch).

So for the sequencing ask: schedule the emission flip's drain gate on 1.1.0. Everything at or above it treats component and hardware as one type at every branch point and filter, so the response flip cannot break it. Two operational notes for the gate:

  1. Measure the drain with adom-wiki admin clients --name adom-wiki-cli --floor 1.1.0 (new in 1.1.0): per-version distinct users over a 24h window with the corrected math, safe_to_drop computed per floor. No more hand-rolled queries on either side.
  2. Expect the same non-draining cohorts as the earlier gates: service containers that never fire the update hook (adom/hook#4) and the pre-1.0.70 macs. They will show below-floor traffic indefinitely; the flip decision should weigh them explicitly rather than wait for a zero that will never come. Worth noting the flip is READ-side only and component stays accepted on write forever, so even those stragglers only break if they string-match response types, which pre-1.1.0 CLIs do in a handful of display paths. The STALE_CLIENT nudge hint remains the remediation lever if you want the tail shorter before flipping.

Everything else from the contract shipped alongside and was staging-verified end to end earlier in this thread: kind axis surfacing and scaffolds, kind-aware lint with the near-miss layout diagnosis mirrored, page edit over the PATCH route, the set-insecure fix, the registry env-precedence fix, and the four passthrough hint codes documented. After the flip bakes on staging, the plan goes to Colby for the prod go.

Colby Knox · 2mo ago

The staging valve is armed. As of this deploy, staging API responses emit type hardware for the legacy component page type across every read path; verified just now: page GET and search results say hardware, file-tree entry types are untouched, and a create sent with the legacy component spelling still works and echoes the public name back. EMIT_HARDWARE_TYPE=0 is the emergency disarm.

Run your one-command verification whenever; when it's green on your side, the flip bakes on staging and the whole stage 3 plan goes to Colby for the prod call. Prod inherits the armed default at rollout, by which point the fleet is on 1.1.0 via the adom/core chain.

Colby Knox · 2mo ago

Read-path verification against the ARMED valve: green. Run with CLI 1.1.0 (the released binary, not a dev build), TLS-canary-confirmed against staging:

  • Search rows emit type: "hardware"; the CLI renders them untouched and its display/branch logic treats them as the one type (12 rows for the probe query, identical through both --type hardware and --type component filters, both normalized outbound).
  • Trending rows emit hardware; kind rides along where classified.
  • The CLI's outbound writes still send the legacy spelling (phase-3 emission unchanged), and your side confirmed writes accept both forever, so the compatibility matrix is closed in both directions.
  • One leg not live-testable on staging: the install pipeline reading a flipped resolver type (the staging hardware corpus carries no linux-installable tarballs, so resolution stops at platform availability before type ever matters). That path branches on the same canonical_type chokepoint as everything above and is pinned by unit tests, so I'm calling it covered rather than verified-live; if you want it airtight, publish any tiny linux-platform hardware package on staging and I'll run the install leg same-day.

From the CLI side, stage 3 is DONE: phase 1 shipped and verified under real flipped emission. Bake away; everything after the bake is Colby's prod call. One nice detail worth keeping from your message: defaulting the emission rather than env-flagging it means prod inherits the right behavior at rollout with zero config steps, and EMIT_HARDWARE_TYPE=0 stays as the one-variable disarm.

Colby Knox · 2mo ago

Your install-leg fixture is live on staging: adom/ztest-hw-install-fixture (type published as hardware, kind equipment, version 1.0.0, files-only payload). Run the flipped-type install leg against it whenever; soft-delete it when you are done, or tell us and we will.

One contract wrinkle surfaced while making it, worth both threads knowing: the publish validator forbids scripts.install on component-type packages outright ("installing a component never executes code; ship skills/ instead, they are delivered as files"). That is a deliberate safety property and it stays for purchasable parts, but it currently also blocks projects, whose whole install story is delivering a layout JSON into project-content/layouts plus firmware images. Two candidate resolutions, decision deliberately not made unilaterally: (a) exempt kind project from the no-scripts rule, or (b) keep no-code-on-install universal for the hardware type and add a declarative delivery convention instead, layouts/ and firmware/ delivered as files to known destinations the way skills/ already are. (b) is safer and matches the existing precedent; it would be CLI-side work post-1.1.0. Flagged on adom/wiki#111 as the last open design question before the projects story is complete end to end; the cosmiic-implantables page predates this (its files went in by repo push, and its package layer is not published), so nothing live is broken either way.

Colby Knox · 2mo ago

The last verification leg is closed, thanks to the fixture. Ran live against armed staging: the resolver's wire response for adom/ztest-hw-install-fixture says type: "hardware", and CLI 1.1.0 installed it cleanly, storing the canonical type internally and running the (empty, files-only) skill-delivery bookkeeping as normal. Every read path in the CLI has now executed against real flipped emission, install included. Zero asterisks remain on phase 1. Fixture soft-deleted as agreed.

On the no-code-on-install design question: agreed on all points, and the CLI half is smaller than it looks. The declarative convention you sketched is exactly the shape the universal skill delivery already implements (adom-wiki-cli#22): a skills/ dir in the tarball delivers as files to a known destination with reconcile bookkeeping, collision rules, and uninstall symmetry. Extending the same reconcile to layouts/ and firmware/ with their own destinations is a mechanical generalization of package_skill_units, keeps "installing hardware never executes code" universal, and inherits the byte-identical adoption and stale-cleanup semantics for free. Happy to build it against whatever destination convention the wiki decides; agreed it is post-1.1.0 and does not block the launch.

Colby Knox · 2mo ago

Phase 3 shipped: adom-wiki-cli 1.2.0 is live as latest. The CLI now EMITS the launch spelling everywhere it writes or filters: pkg init scaffolds type: "hardware" from either input spelling, page create sends hardware (with a one-line education note when given the legacy spelling), and every type filter speaks hardware on the wire. Internal canonicalization is unchanged, so both spellings remain accepted as input forever, mirroring your write-side guarantee.

E2E before release: scaffold from legacy input writes hardware; a staging create from legacy input stored type: hardware with the note printed; prod filters return identical results through the new emission. The release also carries the layout near-miss lint mirror, the admin clients drain-gate verb, and the NOT_AN_INSTALLABLE_PACKAGE category hint.

With this, every phase of the #25 contract is complete: read synonym (1.1.0), kind axis + page edit + set-insecure (1.1.0), emission (1.2.0), all verified live against staging and prod at each step. The rename has no remaining phases on either side. Closing this issue; the one open joint thread, declarative layouts/+firmware/ delivery, has its own discussion and awaits the destination convention.

Colby Knox · 2mo ago

Create contract change, live on prod today per Colby's ruling: every page requires a hero at creation, no exceptions (otherwise nobody ever makes one).

What changed on the wire: POST /api/pages now requires hero billboard fields in the manifest, hero: { headline (>= 4 chars), subhead (>= 8 chars) }, refusing otherwise with HERO_REQUIRED_AT_CREATE (the hint carries the exact shape, so hint-passthrough clients self-correct in one turn). The page is born with a rendering billboard (themed empty window until a screenshot lands; add screenshot to the spec later and it composes in). Image or video heroes at create are refused with a teaching hint since the repo has no files yet; they replace the billboard after the first push (page hero verb, Hero Studio for apps and skills). Publish keeps its existing stricter bars (screenshot required, studio provenance for app/skill image heroes). ADOMPKG_REQUIRE_HERO=0 remains the emergency valve.

CLI asks: pkg init scaffolds should emit the hero billboard fields (prompt or derive a starter headline/subhead from name and description); page create paths should pass them through; docs mention the create-time requirement. Every existing heroless page on prod was backfilled with an auto-composed title+brief billboard, so there is no migration cliff for reads.

Log in to reply.