Chore: Audit fixes - #114
Merged
Merged
Conversation
…errors 1,293 of the repo's 1,295 lint errors were prettier complaints inside packages/models/src/generated/coreManifest.ts — machine-generated output that was never added to the lint/prettier ignore lists (the sibling generated dir in design-system already was). Ignore **/src/generated/** in both, matching what .gitignore already does for design-system. The two remaining real errors: an unused `call` binding in the transcribe store, and a prettier line-wrap in models' entity exports. `pnpm lint` now exits 0. (AUDIT P0-1) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… output The Lint and Test steps carried continue-on-error justified by a backlog that no longer exists — lint is clean after the generated-file ignore and the test suite is fully green — so the only step that could fail a build was Build itself. Both gates are now blocking, and two new ones join them: - Typecheck (after Build, so dist types exist), via a new root `typecheck` script; coverage grows package by package. - A generated-files diff check, so a PR that changes ai-context inputs without committing the regenerated CLAUDE.md et al. fails instead of drifting silently. Install hygiene: the lockfile is validated with --frozen-lockfile while package.json is still pristine (the AD4M file: override rewrite is deliberate drift, so the later install stays unfrozen); the dependency cache now stores the pnpm store instead of a one-level node_modules glob that missed the 2–4-level-deep workspace and was keyed on a lockfile the workflow then mutated. WE steps run under the pinned pnpm 10.18.3 that developers use; only AD4M's own install stays on pnpm 9 (its overrides format is rejected by pnpm 10), invoked via npx without touching the global binary. A concurrency group cancels superseded runs instead of duplicating the full AD4M source build. (AUDIT P0-2, parts of P3-5/P3-6) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…d path
The rule's file glob still named packages/app-framework — renamed to
app-shell long ago — so the two packages with the most hand-written DS
component usage were never linted by it. Widened to app-shell + editor
(alongside the existing block-system + schema-system).
No new violations surface: the remaining style={{}} usages in those
packages sit on plain divs (three.js mounts, router internals), which
the rule deliberately exempts because raw elements have no DS props.
(AUDIT P0-3)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Four defects in @we/primitives' published shape, none visible from inside the monorepo because nothing here used the broken paths: - combobox.ts was a dead, stale duplicate of select.ts registering the same we-select tag — the reason we-select appeared twice in the generated component registry. Deleted. - tsup built src/components/**, a directory that does not exist (the sources live in src/primitives/), so the package.json `./*` exports pointing at dist/components/*.js typechecked and failed at runtime. The build now emits one dist/components/<name>.js per primitive, matching the dist/types/*/components layout the declaration generator already produces — `import '@we/primitives/button'` works. - The package-local ESLint config scoped eslint-plugin-lit to the same phantom components/ path (and imported a root config from a path that doesn't exist); it now covers src/primitives/** and resolves the real root config. Primitives pass the lit recommended rules clean. - 4-components and 5-widgets declared a peer of @we/primitives ^1.0.0 against a package versioned 0.1.0 — a range no release could satisfy. Also in 5-widgets: @we/design-utils is a runtime import and is now a real dependency, externalized in tsup so the bundle resolves the live package instead of freezing a copy; d3-force and four unimported @we/* devDeps are dropped (zero references in src). (AUDIT P0-4, part of P3-1) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- The dark theme's we-text[variant='heading-md'] rule was the only selector in all five themes missing its [data-we-theme] scope, so loading the @we/themes aggregate recolored heading-md text in every theme. Scoped to dark. - Deleting a space theme left it in every picker until the space was reloaded: the template's delete button bypasses the store (model.delete) and nothing re-pulled the list, and the store's own deleteTheme only pruned installedThemes. ThemeStore now exposes refreshSpaceThemes (mirroring templateStore.refreshSpaceTemplates), ThemesList wires it as the delete's onSuccess — the exact wiring its drifted twin TemplatesList already had — and deleteTheme prunes spaceThemes too. (The third bug in this audit item, disabledProps being silently dropped, is fixed with the layout-component factory in the next commit.) (AUDIT P0-5) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ire disabledProps Column and Row were byte-identical but for one string; Grid and Card repeated the same ~25-line splitProps → filterProps → mergeProps → buildLayoutStyles → useStateProps scaffold, four copies of the same hasStateProps closure included. createLayoutComponent now carries the shared shape, parameterized by defaults, component-own prop keys, flex direction, and an optional final style transform (Grid's template computation, Card's surface-opacity color-mix). The four components stay declared as functions so the ai-context extractor keeps finding them. Riding on the single scaffold: disabledProps — accepted, documented, and silently dropped until now — actually applies. useStateProps emits --we-ds-disabled-* vars and the DS interop stylesheet applies them under [data-we-interactive][aria-disabled='true'], declared last so a disabled element's styles win over hover/active. Layout elements have no native :disabled, so the consumer marks disabled the accessible way (aria-disabled) and styling follows. (AUDIT P0-5 third bug, P3-2) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… tokens Completes the theming layering work begun on feat/graph-engine-improvements (AUDIT P0-6). Three moves: 1. themeToStyle/applyThemeVars move from @we/schema-shared to @we/themes, beside the presets — they map theme parameters onto design-system CSS custom properties, which is design-system knowledge; schema-shared was a consumer holding the owner's tools because schema nodes carry a `theme`. schema-shared re-exports both unchanged, so no import breaks. The applyThemeVars removal-bookkeeping tests move with it. 2. One vocabulary, one declaration: ThemeOverrides (the full vocabulary) now lives in @we/themes; ThemeParameters — the deliberately-narrow duplicate the presets used — is a deprecated alias of it. 3. Semantic role tokens, the substantive gap: colour tokens are scale positions, not roles, so "raised surface" was only ever a convention that survived dark mode because everything inverted uniformly. The tokens CSS now emits --we-role-* variables (page, surface, surfaceRaised, surfaceSunken, text, textMuted, textFaint, textInverse, border, borderStrong, accent, accentText), each defaulting to a parametric expression over the scale — every existing and user-authored theme keeps working untouched. A theme pins individual roles via ThemeOverrides.roles; the dark preset (and the dark CSS file, for CSS-only consumers) makes the first designed use of it: raised surfaces get lighter in dark instead of casting shadows. Component adoption of the role vars is deliberate follow-on work. Also: the tokens aggregate object gains `role` and the previously omitted `zIndex` (audit design-system finding 19). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ree helpers Four helpers existed as private copies that had already diverged: - deepUnwrap lived in both the Solid renderer (with a recursion depth guard) and the action resolver (without one — unbounded recursion on a cyclic value, on the path every user interaction dispatches through). The copies also disagreed on Object.create(null) objects: the action copy passed them through untouched, leaking reactive accessors into store methods. The unified version keeps the guard and recurses into null-prototype objects, and lives beside REACTIVE_ACCESSOR in propResolvers/reactive.ts. - isSchemaChild had two variants in the same package that disagreed on whether an array counts as a node (the indexer's accepted arrays), so index traversal and scope traversal saw different trees. - isPropsSchemaNode was copy-pasted four times (indexer, scope, and the editor's InspectorPanel + EditorOverlay); replaceNodeInTree — ~50 lines of recursive tree rewriting — twice in the editor, one copy labelled "local copy". All now live in @we/schema-shared (treeUtils.ts), exported through the package index; the editor imports them like it already imports findNodeById/mergeNode. New tests pin the previously-divergent behaviours: the array rejection, the null-prototype recursion, the cyclic-value guard, and replaceNodeInTree's traversal of children/ routes/slots/props edges. (AUDIT P1-3) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ntract
SerializedBlockNode — the type every function in the 841-line block
serialization module flows through — was `any`, and a second, unrelated
interface of the same name in @we/block-solid described the editor-side
shape. The shared type is now real ({ type, version?, children?, plus a
typed index signature for per-block properties), and block-solid's
interface extends it, adding only the `version` Lexical requires. The
handful of editor call sites that relied on `any`'s silence (null from
decodeEditorState, Lexical's stricter root type) now say what they mean.
The pipeline's contract gets its first tests, run against a fake model
layer implementing exactly the surface serialization.ts touches — no
executor needed:
- extractInlineText / extractTextContent (whitespace collapse, leaf
field extraction, childEditorState recursion, the 5,000-char cap with
word-boundary truncation) and extractBlockData, now exported.
- createBlocks: tree creation, child linking, id stamping for later
reconciliation, the editorState blob + textContent write, kind
stamping, and list-metadata passthrough onto listitems.
- reconcileBlocks — the 267 lines where a regression silently deletes
user content: claimed ids update in place, unclaimed nodes are
created, orphans are deleted, duplicate ids (copy/paste) get fresh
instances, foreign ids are not claimed, and each parent's children
relation is overwritten with the final ordered list.
Also removed: blocksToLexicalJSON and its blockToLexical/groupListItems
machinery (~150 lines) — a lossy Lexical-reconstruction fallback with
zero consumers anywhere in the repo. Recoverable from git if the
fallback is ever wired up. (AUDIT P1-1, part of P3-4)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nd the token CSS The headline test asserts DesignSystemProps (85 props in @we/design-types) and designSystemKeys (five hand-maintained arrays in @we/design-utils) agree exactly, in both directions. They agreed today only by luck: a prop added to the interface but not the arrays typechecks everywhere and is then silently dropped by every Column/Row/Grid/Card. That whole bug class is now a test failure. Around it: - design-utils: tests for tokenVar (token vs raw-CSS discrimination, calc(), keywords, the bare-'0' rule), zIndexVar, parseBorder, mergeProps (the shorthand-precedence rule the variant/size merge chain is built on: an explicit p beats a default's px), mapFlexAxes (ax/ay swap between row and column), filterProps, and buildLayoutStyles' structural baseline — including bg emitting the background-color longhand, never the shorthand. - 1-tokens: the CSS generators are now pure exported string builders (writing moved to the generateCSS edge), snapshot-tested — every --we-* variable the system reads shows up as a snapshot diff instead of a silently unstyled component. A regression test pins that the two scrollbar values now flow from the token source: the generator used to filter them out and hardcode replacements, so editing component.ts did nothing (AUDIT P1-6, scrollbar item). - Every design-system package now has a test script, so pnpm -r test visits all of them instead of silently skipping six of seven. (AUDIT P1-4 design-system, part of P1-6) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…behaviours, expanders 48 new tests over the modules the audit flagged as the highest-value untested logic in graph-system: - SpatialIndex: radius vs box hit-testing (a card's corners are outside its radius), nearest-centre ordering for overlapping nodes, the cell-size-scales-with-largest-node rule that keeps the 3×3 sweep valid, marquee rectangles, rebuild semantics. - Viewport: world↔screen round-trip, the stationary-point property of zoomAt (the whole feel of a zoomable canvas), zoom clamping including the at-max no-op, fit's centre/actual-size cap and its no-size guard, visibleBounds padding. - Behaviours: pan claims only the background; drag preserves the grab offset (dragging vs teleporting), releases the pin on drop unless pin:true, emits the node's final position, drops the drag when no button is held, and refuses while locked; select distinguishes click from drag-that-ended-on-a-node and shift-toggles; dispatchPointer broadcasts gesture-ending phases — pinning the click-latches-a-node bug the code comment documents. - Collection expander: the drill-down scope for the untyped children relation, one-way containment, undeclared child types skipped, a failing child query warns instead of throwing. - Property expander: label property not repeated, literal-node convergence across instances (the reason values are nodes at all), hideEmpty, and the valueNodes:false leaf form. (AUDIT P1-4 graph) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…al code The editor's only test re-implemented toRelative because the original closed over a component-scoped ref — a test that could never fail when the component changed. src/helpers.ts now holds the editor's pure logic, imported by the components and by tests alike: - overlay geometry: nearestToken (token snapping), computeSizeDelta (resize maths incl. the corner-handle average and anchor-side sign flip), the handle predicates and cursors, and toRelativeRect — the subtraction that cancels the viewport out of the overlay's coordinate contract, now tested for the scroll-stability property directly. - the focus-ring shorthand parser/serializer (parseRing/composeRing), round-trip tested including the theme-accent recognition and the raw-color passthrough. - condition-operand logic from ValueRefPicker + ConditionEditor: operandLabel, refPath, refToOperand, operandValueType, and the operandComplete/exprComplete validity rules (unary vs binary vs group). 16 tests replace the 3 that tested a copy; the copy-based geometry test is deleted. ValueRefPicker re-exports operandLabel/operandValueType so existing consumers are untouched. (AUDIT P1-4 editor) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
modelRegistry's own doc comments describe two silent failure modes; both
are now tests. The globalThis keying that makes split-bundle registries
impossible ("everything registers successfully, every lookup fails") is
asserted through the Symbol.for key directly, and getModelForPerspective
is pinned on all three rules: native classes win over synthesised ones
(decorator metadata), the fallback reads `uuid` and never `id` (a
PerspectiveProxy carries an unrelated subscription id that must not
win), and a miss returns undefined rather than throwing.
The pure utils get their first tests too: signal normalisation (incl.
the zero-width-range veto convention), all four aggregate modes, and the
file-storage decoders — whose silently-empty error returns are now a
pinned decision instead of an accident. (AUDIT P1-4 models)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…onformance tests createInMemoryBackendPorts stubbed its data plane — ephemeral reported the capability absent and dataBindings returned two of the contract's bindings — so the boot suite that backendPorts.ts says "doubles as the conformance test" could not exercise a single query or mutation. The package even contained a second, unrelated in-memory backend (createInMemoryBackend) that did implement queries, with its own private copy of the query adapter. The bundle now carries the same binding surface the AD4M adapter provides: $getModel/$getModelForPerspective over the compiled row-backed entities, the shared inMemoryQueryAdapter (extracted to queryAdapter.ts so the two backends share one definition), model mutations (create/update/delete through the entity classes), $identities wired to the host profile cache, and $ephemeral/ephemeral backed by the shared InMemoryBus with the agent id read lazily, mirroring the AD4M port. A new conformance suite pins the binding surface key by key (so a missing binding is a named failure, not a silently boot-only backend) and runs create → query → update → delete plus the ephemeral no-self-delivery rule through the bundle end to end. Given backend independence is a first-class goal, this seam now has a second, complete implementation of the contract rather than a hypothesis. (AUDIT P1-5) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two ways the token pipeline could silently ignore its own source, on top of the scrollbar fix that landed with the generator tests: - @we/design-utils inlined a frozen copy of @we/tokens' font scale: the package declared tokens as a devDependency, so tsup bundled the token tables into dist. Adding a lineHeight token to @we/tokens then did nothing until design-utils happened to rebuild — and when it did, the new token fell through to raw-CSS passthrough and emitted an invalid declaration. tokens is now a real dependency, externalized in both build entries, so the live package resolves at runtime. - The generated tokens CSS hardcoded three Google Fonts @import URLs — a third-party network request on every load of a local-first, offline-capable app. The webfont fetches now live in an opt-in dist/css/fonts.css; the main entry makes no network requests. The app shell imports '@we/tokens/css/fonts' alongside the tokens, so the shipped app looks exactly as before; playgrounds and embeds get offline-safe tokens by default. (AUDIT P1-6) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Alignment (AUDIT P1-2): every workspace manifest that declared @coasys/ad4m 0.11.0 — a two-minor-version fiction against the root override that actually installs 0.13.0-test-9 — now declares the pinned version. The override in the root package.json remains the single point to move both when the pin changes. Unification (AUDIT P3-1): vitest collapses from three majors to one — module-call and module-transcribe ran vitest 1.6.1 while the rest of the workspace ran 4.x, i.e. two test runners with different expect/mocking semantics inside one `pnpm test`; both suites pass unchanged on 4. typescript lands on ^5.7.2 everywhere (was ^5.3.3 across graph-system and most modules, ^5.9.3 in playgrounds), plus @types/node ^24.10.0, tsup ^8.5.1, vite ^6.0.7, vite-plugin-solid ^2.11.11, zod ^4.1.12, tsx ^4.21.0, rimraf ^6.1.0. Internal @we/* deps use workspace:* everywhere outside peerDependencies, so none can resolve from the registry. Removed, verified unimported: the Apollo stack (@apollo/client, graphql, graphql-ws) plus @solidjs/router in both desktop apps — apolloClient.ts is a historical name, it only ever used Ad4mClient — and @tauri-apps/plugin-opener in tauri; the three @we/graph-* deps the graph-explorer playground declared and never imported; zod and @we/design-types in app-shell. @types/leaflet stays and now does its job: location-picker's map/marker fields were `any` with the types package sitting unused. Full workspace test suite and typecheck pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ting Three deduplications, one per drift hazard the audit named (P3-2): - Desktop platform plumbing: Electron and Tauri each carried a verbatim apolloClient.ts (named for a client library neither had used in years), a structurally identical platform adapter, and the same connector choreography. The shape now lives once — createDesktopPlatform(transport) in @we/app-shell/shared and connectToLocalExecutor/createLocalAd4mConnector in @we/backend-ad4m — and each app supplies only what is genuinely its own: the IPC bridge or the Rust command surface. Six files become two factories plus two thin transports; the typed SerializedBlockNode also surfaced two latent unknown-parameter casts in SpaceStore's post actions, now explicit boundary casts. - The account registry's shared on-disk format: two implementations (504-line JS, 1052-line Rust) both document that a spelling only one host reads wipes every name and avatar on the next write — and no test proved a file written by one round-trips through the other. Both suites now parse the same fixtures (registry.shared.json in the agreed camelCase dialect, registry.legacy-tauri.json in the old snake_case), electron via vitest and tauri via cargo test; both pass. - TemplatesList/ThemesList: two ~160-line schema files identical after token substitution — the pair whose drift produced the stale-theme bug fixed earlier. They are now one kit fragment, installedList(), parameterized by the axes that genuinely differ (entity, key field, apply action, default field, refresh action), following the marketplaceList precedent. All 22 schemas validate; kit tests pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…, DEV-only test schemas, dead names retired - EditorStore's four byte-identical debounced panel-width persisters collapse into one createPersistedWidth helper (same signal-immediately / write-debounced semantics, per panel key). - The schema test harness (~3k LOC of test schemas plus its test store) no longer registers in production: the shell template entry is gated on import.meta.env.DEV, so an app build drops the registration and tree-shakes the schemas. It remains a dev tool exactly where it was in dev builds. - The removed stores' names stop haunting readers: the editor's ~70 local adamStore/aiStore bindings over the host port are renamed to what they alias (identity/session; one shadowed <For> item became chat), and EditorStore's leftover "AiStore" comment now names EditorStore. (AUDIT P3-3) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Everything here was verified unreferenced before deletion (AUDIT P3-4):
- netlify.toml and scripts/build-with-ad4m-link.sh: the deploy config
could not have worked (pnpm build alone cannot succeed without the
ad4m link step it never invoked, no Node pin), and the script was
orphaned from every configured pipeline while CI reimplements the
same clone-and-link inline.
- apps/playgrounds/{react,vue,svelte}: one-line README stubs
advertising frameworks with no implementation; not even matched by
the workspace glob.
- packages/module-system/assistant/: an empty directory holding only
node_modules — the incompletely deleted remains of a removed module.
- The unreachable CTAv2 image set (776 KB pinned off behind
IMAGE_SETS.v1 — statically imported, so bundled but unrenderable),
the unimported CTAv1/ForCommunities.jpg, and the unreferenced
about/hero.md. The about page keeps its one live set.
- The --we-depth-* token category: six generated variables with zero
consumers anywhere (the shadow scale is what components actually
use). effect.ts, its generator, and the DepthToken type are gone.
- 3-primitives/docs/notes.md: a stale scratchpad documenting a dist
layout the build has not produced in months.
- @we/editor's seven component exports with no consumer anywhere — the
real surface is mountTemplateEditor + EditorHost, and that is now
what the index says.
- RerenderLog registers in the component registry only in dev builds:
logging on every mount is its purpose, which is exactly why a
production template should not be able to reach it.
Deferred deliberately: the ~26 MB of committed skybox JPEGs (2k + 4k
sets) — moving them to LFS or a CDN changes contributor setup and
deserves its own decision. Full test suite and lint pass.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… logging Console noise (AUDIT P3-5): SpaceStore's test() scratch action — two raw debug logs behind a store method named test — is deleted outright; TemplateStore's every-init template dump and CollectionInput's two click-path logs go with it. EditorStore's AI tool-loop diagnostics (nine logs including two full JSON.stringify template dumps per tool call) now run only in dev builds. Silent failures get a voice: - The 15 unconditional console.warns in propResolvers/local.ts — template-authoring mistakes like $setLocal on an undeclared field — now also reach the host's $onError surface (the toast), through a sink the renderer installs from stores.$onError. One error channel per package instead of two conventions. - backend-ad4m's avatar resolution kept its best-effort catch but logs the failure with the url: a broken avatar was indistinguishable from an absent one, in the one catch in the package with no written justification. - ai-context's drift detector now fails the build (exitCode 1) when fragments/stores.ts documents a member that no longer exists — that direction of drift licenses $store paths into nothing and was only ever a scrolling warning. The undocumented direction stays informational. Regenerated context outputs ride along (the dead effect.depth tokens and the duplicate we-select entry drop out; themeStore.refreshSpaceThemes joins the store surface). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ipeline, honest cli build - .gitignore covers the latent gaps: coverage/, *.tsbuildinfo, .turbo/, .vite/, .DS_Store, .idea/, and *.log generally (CI already expected .turbo to exist; storybook.log was the only log ignored). - `pnpm clean` no longer deletes pnpm-lock.yaml — clean meant "dependency float on next install". The actual disk hogs it never touched (69 GB of tauri target/, dist-electron/) get `clean:deep`. - Stylelint finally runs somewhere besides editors: `pnpm lint:css` gates the authored CSS (packages/apps src — the naive glob drowned in vendored minified CSS) and CI runs it beside ESLint. Zero findings today. - @we/cli's build script no longer masks its own failures: the `A && B || C` shape ran the fallback and reported success when B failed; it is now an explicit if/else. bootstrap defers to the tsup config instead of duplicating the entry list on the command line, and the commented-out log line is gone. - pnpm-workspace.yaml names the globe family explicitly instead of a `module-system/*/*` wildcard that matched 38 directories to find 4 packages. (AUDIT P3-6) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The audit's docs tier, first half (P2-1..P2-5, P2-7 moves): - packages/app-shell/README.md — rewritten. It showed useAdamStore and a PlatformAdapter with methods that don't exist, named consumers by the wrong package names, and missed five real directories. It now documents the actual two ports a host supplies (PlatformAdapter with the desktop factory, BackendConnector with the backend-ad4m connector factory), the real store roster, the directory map, and the three exports. - docs/architecture/codebase-map.md — the "start here" doc taught adamStore vocabulary two refactors gone. Now: sessionStore/datasetStore/ spaceStore ownership, dataset-first terminology, the backend contract seam, and new sections for the graph system and the template kit. The same phantom "elements → pages" ladder is fixed here, in the ai-context architecture fragment that generates the always-loaded orientation, in README.md, and in VISION.md. - OPERATORS.md — the six adamStore examples (uncompilable vocabulary in the canonical operator reference) now use the real stores and $me. - Seed system: one description instead of three. The guide is rewritten from types/seed.ts (documenting modules/features/globalSpaceUrl/ marketplaceUrl, which were entirely missing) and points at the type as source of truth; the seed README's drifted parallel schema is replaced with a pointer; seed-examples gain the modules/features fields the real we-seed.json carries. - README.md — three package links 404'd (packages/utils is deleted, two frameworks/ paths moved) and six of twelve systems were invisible. Now a full system table plus the real command set. - VISION.md — targeted, not rewritten: the "How It's Built" ladder names real layers, and the template-kit fragment rung and graph engine — the mechanisms the forking story leans on — are in it, linking template-fragments.md. - developer-setup.md — Node 24 (.nvmrc), coasys clone URLs, the real package tree, the missing commands. - Guides: EMBEDDING.md moves to docs/guides/embedding-external-apps.md (the mechanism is alive in appBridge.ts — the stale AdamStore heading now names it; all seven inbound links updated). The cesium pair and launcher-ui-customization describe packages/registries that no longer exist — archived to internal/old with banners naming what replaced them; the globe layers README sheds its old @we/cesium-layers name. WEB_SCRAPING_STRATEGY.md (an unimplemented plan in apps/) moves to internal/plans with a status banner. The four unbannered internal/old docs get their banners; the docs index reflects all of it and finally links template-fragments.md. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ules named Second half of the docs tier (AUDIT P2-6, P2-7): New, following graph-system's README as the model (what the system is, a package table with dependency direction, the decisions worth knowing): - packages/backend-system/README.md — the contract, its two adapters, the conformance seam, and where @coasys/* is allowed to appear. - packages/module-system/README.md — the module contract in one paragraph, the bundled modules, the deployment/space/agent layering. - packages/templates/README.md — kit/shell/default and why fragments exist (the drifting-twins story). - packages/ai-context/README.md — the extractors × fragments architecture behind 540 KB of tracked generated output that nothing previously explained, and the "when you change X" table. - packages/models/README.md — the two-entry registry and the manifest. - block-system/shared and graph-system/protocol READMEs — the two contract packages violating package-conventions.md's own rule 6 (a shared contract package states what belongs and what doesn't). - packages/app-shell/CONVENTIONS.md — the biggest package finally has its rules written down: layering, the load-bearing store nesting order, stores-as-schema-API, DEV-gating, logging, and the in-memory test contract. Corrected, where the existing text actively misled: - design-system root README (packages don't publish independently, no monorepo Storybook, no CONTRIBUTING/LICENSE files, "elements/pages"). - 4-components README — rewritten; it documented per-component .module.scss files that never existed, a deleted PostCard import, and wrong directories. - 3-primitives README (src/primitives not src/components, `pnpm start` not storybook, the per-component subpath imports the build now actually produces) and its CONVENTIONS' dead plan link. - 5-widgets README — was a single heading line. - 1-tokens README (neutral not ui, real radius var, honest runtime claim) and CONVENTIONS (the four token files missing from its table, including the shadow scale components actually use and the new roles). 2-themes README/CONVENTIONS gain the light theme and the JS entry. - block-system README's broken conventions link; performance.md pins its measurement point; examples.md notes its knowledge-map sketch shipped as the graph engine. Plus .github/pull_request_template.md with the docs-sync checklist — the freshness contract the audit asked for: the stale docs rotted because nothing forced the update at PR time. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The architecture fragment fix (elements/pages → primitives, no pages layer) propagates into the three generated references. Full gate suite green at this point: lint, stylelint, typecheck, build, and 1,300+ tests across the workspace. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…perpetual dirty diff Both desktop generate-seed-config scripts wrote their JSON outputs via JSON.stringify with no trailing newline, while the committed files end with one (prettier normalized them at some point). Every pnpm build therefore rewrote the three tauri generated files byte-different — a permanent phantom diff after any clean rebuild. All nine JSON writes across the two scripts now append the newline; rerunning both generators reproduces the committed files exactly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…exports, authoring-scoped warnings Three user-visible breaks, two mine and one inherited: 1. The boot screen's Login button (predates this branch): the template-kit refactor (e62db1d) collapsed the whole unlock row to a bare field() fragment — dropping the Login button, the Enter-to-submit handler, the "Incorrect password" error wiring, and the clearPasswordError chain. sessionStore.login was left with zero callers: unlock was impossible. The full row is restored, with a comment on why it is deliberately not the field fragment (the fragment renders a lone control; this row is input + button + shared error, the shape an OS sign-in uses). 2. The template/theme toolbar: trimming @we/editor's "unconsumed" component exports missed that the app shell consumes four of them via dynamic import — lazy(() => import('@we/editor').then(m => m.DesignToolbar)) and friends resolve undefined and render nothing, silently. DesignToolbar, RightPanelContainer, TemplateCard and EditorOverlay are exported again, with a warning comment that these consumers are invisible to a static-import grep. 3. The "$local: postRows" toast on app load: routing local-state authoring warnings to $onError unconditionally toasted diagnostics from *stored* templates at people merely opening a space. The sink now follows the authoring session — EditorStore installs a warning toast while an editing surface is open and removes it after; viewers get the console copy only, as before. Verified: schemas validate, editor typechecks, app-shell + schema-solid suites green, lint clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Block-mode we-code defaulted to bg neutral-900 / color neutral-100 — scale positions, which the parametric lightness ramp inverts per theme. The intended near-black terminal read in light mode therefore rendered as a white slab with dark text in dark mode (visible on the runtime settings Languages section). A code block is a terminal: dark in every theme. The block background and text now pin their lightness and keep only hue/saturation parametric — the same move the dark theme's tooltip inversion makes — so every theme tints the block with its own hue while the terminal look holds. Inline code keeps its scale tokens, which adapt correctly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…op warning, keep the fields Reloading straight into a data route logged a cluster of warnings from one cause: the first frame renders before the backend's data bindings land in the stores bag, so $queries found no $getModel, warned, and — the real damage — dropped its declared fields from $local entirely. Every downstream read then warned "$local: field not declared", blaming the template for a query that simply hadn't started yet. The wired render replaces the tree a beat later, which is why everything worked despite the noise. Absence of $getModel is also a legitimate permanent state: the RendererDataBindings contract makes every data binding optional so a presentation-only host can omit them. Warning per query in such a host was wrong on its own terms. All three sites ($queries, $each's $query, $single) now degrade quietly — empty results, no item — and $queries registers its declared fields as empty reads either way, so $local resolution stays coherent through the unwired frame. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…lved
Opening the spaces list toasted "RPC error 500: Failed to parse model
query: data did not match any variant of untagged enum WhereCondition".
The spaces query filters `url: { not: { $store:
'datasetStore.currentDatasetCid' } }` — and on a reload into the route,
the cid hasn't loaded for the first frame, so the operand resolves to
undefined, `{ not: undefined }` serializes to the empty condition `{}`,
and the executor's parser rightly refuses it. The same failure awaits
any where-clause reference that is briefly (or, on a data-less host,
permanently) unresolved.
An unresolved input means "don't filter on this yet", not "send a
filter with a hole in it": pruneUnresolvedWhere (new in schema-shared,
tested) strips undefined-operand conditions — including inside OR/AND
branches and NOT clauses, dropping combinators that empty out — while
keeping null operands, which are values. Both renderer resolution sites
($each/$queries via createQuerySignal, and $single) prune after token
resolution; the effect re-runs and applies the real filter the moment
the value exists.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…dist/primitives The per-element build landed in dist/components/*.js while the sources live in src/primitives/ — a leftover from repairing the broken subpath exports, where the JS output was aligned to the directory name the type-declaration generator happened to hardcode. Now the whole chain says primitives: the tsup entries, the generated framework declarations (dist/types/*/primitives/), and every package.json subpath export. The public specifier (`import '@we/primitives/button'`) is unchanged; the dist layout is internal to the package. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…es side-effect import The ./styles export resolves to a CSS file with no type declarations, and the bare specifier doesn't end in .css — so neither vite/client's ambient nor the playground's *.css declaration matches it, exactly like the documented @we/tokens/css case beside it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ases outside The picker dismissed on any document `click` outside itself. A click's target is the common ancestor of its mousedown and mouseup targets, so panning the map and letting go outside the picker synthesized an "outside click" and closed the map mid-gesture. Dismissal now keys off `pointerdown` — where the press *starts* is what inside/outside means to the user — so a drag that begins on the map keeps the picker open wherever it ends, and a genuine outside press still closes it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…opies are gone The create-space modal was mounted twice: once in shell chrome (createSpaceModalMount, slot 'core:createSpace', gated on shellStore.createSpaceOpen — "registered as chrome so it exists once, wherever it is opened from"), and again inside CardsRoute and GlobeRoute, gated on a route-local createSpaceModalOpen flag. Every close path inside the modal (X, backdrop, Cancel, post-create onSuccess) sets the *shell* flag — so the route-local copies, opened by their local flag, could never be closed. The local mounts and flags are deleted; the routes' open buttons now set the shell flag and get the chrome copy, which closes correctly from every path. Deleting them also removes the last two template-default → template-shell imports, so the back-dependency the audit flagged as "still present contrary to the PR claim" is now actually gone — the package.json dependency goes with it. All 22 schemas validate; template-default typechecks and builds. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…or dropped The same template-kit refactor that collapsed the boot screen's login row (e62db1d) also rewrote the signal-type modal's mode select as field({ control: 'select' }) — without carrying the options array over. we-select fell back to its empty default: a mode picker with nothing to pick, on the control that decides whether a signal is a toggle, vote, rating or slider. The four options ride through the fragment's props now, as the fragment intends. Swept the rest of that refactor's hunks for further losses: the only other removals are a cosmetic input bg and opt-in touch-on-blur, both consistent with the fragment's documented philosophy. With this and the login row, that refactor's casualties are accounted for. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The users list gets the spaces card's shape: a full-width cover image when the profile has one (same 120px cover treatment), a lg avatar with shadow — now carrying `hash` so an unfetched profile still gets a stable generated face — a heading-sm name with the @handle beneath, the bio in body text, and a location stat chip. Two deliberate divergences from a literal copy: the location chip gates on `city` rather than the location object (a lat/lng-only location has nothing readable to show and rendered ", "), and there is no created/ joined date chip — a member's profile summary (AgentProfileSummary) carries no such field, unlike a Space model, so the card only promises what the data can deliver. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ing is a real state; view settings persist Three related list-experience fixes: 1. Reloading on a query-backed route left it empty until a route change. The stores bag exposes the data bindings as getters over a memo of the backend ports — reactive, but every query site read $getModel once at component setup, so a template mounted before the backend connected was permanently stranded. All five sites ($queries, $each, $single, prop-level $query, and $map+$query hoisting) now read the bindings inside the query effect: the subscription starts by itself the moment the bindings land. The queryToken test that pinned the old warn-and-bail contract now pins the self-healing one. 2. Loading is no longer collapsed into empty. Each query accessor carries a `loaded` signal — false until the first result set or error, then true for good (re-runs keep old rows until new ones reconcile, no flashing). $queries exposes it as `<name>Loaded`; the kit's new skeletonList fragment renders pulsing card-shaped blocks, and cardList holds it until the query answers — the empty state now only ever asserts "loaded and empty". The semantic validator knows the derived name. 3. $localState fields accept persist: '<key>' — device-local (localStorage) persistence with an explicit, namespaced key; the stored value wins over `initial` on mount and $resetLocal clears it. The cards route persists contentType, sortField, sortDirection and displayMode, so a reload lands where you left it; search text stays deliberately ephemeral (a remembered filter silently hiding content reads as missing data, not a preference). Docs updated in the ai-context fragments and regenerated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…, implemented
The routing conventions discussion, landed as code + a decision doc
(docs/architecture/routing-and-view-state.md): every piece of UI state
has one home, chosen by "if I sent this URL to someone, should they see
the effect?"
- Tier 1, location → the path (existing routes/segments machinery).
- Tier 2, view state → URL query params. routeStore gains a reactive
`params` record and `setParam(name, value, { push? })` (history
written directly — the path doesn't change, so the route tree must
not re-resolve; popstate covered). $localState fields opt in with
syncParam: '<param>' (object form adds push: true for changes that
deserve a Back entry). Reads/writes stay $local/$setLocal; a field
back at its declared initial removes its param, keeping URLs clean.
Precedence on mount: URL > persisted > initial. The renderer reaches
the router through a $routeParams host binding, so any host can wire
its own and hosts without one degrade to plain local state.
- Tier 3, preferences → persist (device) or AgentSettings, and NOT the
URL: a shared link must not impose display density on its recipient.
- Tier 4, ephemeral → plain local state.
The cards route adopts the tiers: content type (?type=, push), sort
(?sort=&dir=), search (?q=) share and reload with the URL; displayMode
stays a device preference. The previous commit's persist keys for the
URL-tier fields are superseded — device storage made the same URL show
different content per machine, the opposite of shareable.
Links can also suggest a look: ?template=<id> and ?theme=<id> are
honored by the shell — applied silently when the recipient has them
(clicking the link is the consent; idempotent, so re-sharing is safe),
and degraded with a one-time warning toast naming what's missing when
they don't, falling back to the recipient's own. Matching re-resolves
as templates/themes stream in, so space templates arriving after boot
still apply.
Five new renderer tests pin the contract (URL-wins precedence, push
semantics, clean-URL removal, degradation without the binding); the
generated reference documents both tiers and the suggestion params.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The reported repro: set sort to likes, visit another route, return — the list still shows likes (keep-alive routes hold their live state across navigation) but the URL is bare, because the router strips the query string on the way out. The screen and the address disagree, and a reload believes the address: sort snaps back to the default. Reload without navigating first and ?sort=likes is still in the URL, so it sticks — exactly the asymmetry observed. routeStore now keeps an in-memory, per-session map of each path's last query string — updated on every setParam and on every arrival the router reports — and navigate() to a bare path restores that path's remembered search. An explicit `?` in the target always wins, and a field returning to its default clears both the param and the memory, so returning lands bare. Reloads keep starting from the URL itself, which this keeps truthful. Five routeStore tests pin the contract (write/remove, push-vs-replace, restore-on-return, cleared-memory, shared-link arrival); the behavior is recorded in docs/architecture/routing-and-view-state.md. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…mes, not just handles Searching the member list only matched handles while the spaces list beside it matched name OR description — not an oversight in the template but a gap in the grammar: $filter's where deliberately lacked the logical combinators $query's has, so a client-side filtered list could only ever search one field. matchesWhere now handles OR/AND/NOT with $query's exact semantics — branches are full where-clauses, siblings stay implicitly ANDed, combinators nest, malformed shapes match nothing rather than everything. Six tests pin the grammar; the generated reference drops its "$query-only" caveat and shows the two-field search shape for both. The member list searches name OR handle: `name` is the assembled display name, so first and last are covered by one branch, and the handle branch keeps @handle searches working for members with real names. Bio is left out deliberately — a people search matching a stray word deep in a bio surfaces people who don't look like matches; it's one more OR branch if that call changes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The previous commit put a description field on the typed store entry — StateMemberMeta has no such field, and the regenerated contextData broke schema-shared's dts build (caught by template-default's typecheck). The typed entry carries shape only; the prose for params and setParam moves to the parallel docs section where every other store member's prose lives. Regenerated; builds and typechecks green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A space with coordinates but no reverse-geocoded city rendered "Location: , " — the chip gates on the location object while the value it prints is city + country. Gated on city, matching the member card. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
✅ Deploy Preview for coasys-we ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
9 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Audit remediation: gates, correctness, coverage, conventions
Summary
A full-repo audit (
AUDIT_2026-08-11.md) found the codebase in good shape ondiscipline — near-zero TODO debt, documented catch blocks, a well-tested
schema-system — with the rot concentrated in three places: CI that gated on
nothing but the build, a test gap shaped exactly like the riskiest code,
and documentation two refactors behind reality. This branch works through
every tier of that audit, then through the bugs that surfaced while testing the
result.
Three things are worth a reviewer's attention up front. CI now actually
gates — lint, stylelint, typecheck, generated-file freshness and the full test
suite all block a merge, where previously only
pnpm buildcould fail. Severaluser-facing bugs were already broken on
devand are fixed here (the sign-inscreen had no Login button; the signal-type modal's mode picker was empty; the
create-space modal was unclosable from two routes). One regression was
self-inflicted and caught in manual testing — trimming "unconsumed" editor
exports broke the template toolbar, because the app shell consumes them via
dynamic import, which a static-import grep cannot see; the fix carries a comment
warning the next person.
The branch also closes the routing conventions discussion: view state now lives
in the URL, so a link reproduces what the sender sees — including an optional
template/theme suggestion — under a written four-tier convention.
Changes
CI and tooling gates
.github/workflows/build.yaml— Lint and Test carriedcontinue-on-error: true, justified by a backlog that no longer existed, soregressions landed silently on
dev. Both now block, joined by stylelint, atypecheck step, and a generated-files diff (a PR that changes
ai-contextinputs without committing regenerated output now fails instead of drifting).
Lockfile drift is caught with
--frozen-lockfilebefore the AD4M overriderewrite; the dependency cache stores the pnpm store rather than a
one-level-deep
node_modulesglob that missed most of a 2–4-level workspace;a concurrency group stops duplicate pushes each running a full AD4M source
build. WE steps run the pinned pnpm 10 developers use.
eslint.config.js— 1,293 of the repo's 1,295 lint errors came from onemachine-generated file that was never ignored;
**/src/generated/**is nowignored in both eslint and prettier, and the two real errors are fixed. The
prefer-ds-propsrule pointed atpackages/app-framework, renamed toapp-shelllong ago, so the two packages with the most hand-written DS usagewere never linted by it.
script and a CI step (zero findings).
@we/cli— the build script'sA && B || Cshape ran the fallback andreported success when the real build failed.
Broken public surface (
@we/primitives)Four defects invisible from inside the monorepo because nothing here used the
broken paths:
combobox.tswas a stale duplicate registering a secondwe-select(hence the duplicate registry entry); the per-element build wrote toa directory that didn't exist, so
import '@we/primitives/button'typecheckedand failed at runtime; the package-local ESLint config scoped
eslint-plugin-litto that same phantom path; and two packages declared a peer of
@we/primitives ^1.0.0against a package versioned0.1.0. The dist layout nowmirrors
src/primitives.Correctness bugs
Fixed on
devbefore this branch (found while testing):the unlock row to a bare
field()fragment, dropping the button, theEnter-to-submit handler and the error wiring —
sessionStore.loginhad zerocallers, so unlocking was impossible.
the select without carrying its options across.
they embedded route-local copies gated on a local flag, while every close path
inside the modal sets the shell flag. The copies are gone (which also
removed the last
template-default → template-shellimports, so thatbackwards dependency is now genuinely gone).
$storereference in a where-clauseserialized to an empty condition the executor rightly refuses;
pruneUnresolvedWherenow drops conditions whose operands haven't resolved(keeping
null, which is a value).reactively, but query sites read
$getModelonce at setup, so a templatemounted before the backend connected was stranded until a route change. All
five query sites now read bindings inside their effect and self-heal.
click's target is the common ancestor of mousedown and mouseup, so dismissal
now keys off
pointerdown.we-codeblocks were white in dark mode — scale tokens invert per theme;the block now pins lightness and stays parametric on hue/saturation.
when unconsumed editor exports were trimmed (the app shell consumes four of
them by dynamic import).
Type safety and test coverage
The suite runs 1,660 tests, up from a baseline where six packages had no
testscript at all.SerializedBlockNodewasany— every function in the 841-line blockserialization module flowed through it, while a different interface of the
same name lived in
block-solid. It is now a real type the editor-sideinterface extends, and the pipeline has its first tests, including
reconcileBlocks' id-claiming, duplicate detection and orphan deletion (wherea regression silently deletes user content), run against a fake model layer.
DesignSystemProps(85 props) anddesignSystemKeys(five hand-maintained arrays) agreed only by luck; adrifted prop typechecks everywhere and is silently dropped by every layout
component. Now a test.
including the claim/broadcast rule, expanders), the token CSS generators
(snapshot),
@we/models' registry precedence rules, and the editor's purehelpers — extracted so tests import the real code, replacing a test that
re-implemented the function it tested and could never fail.
Backend contract
createInMemoryBackendPortsstubbed its data plane, so the boot suite thatbackendPorts.tscalls "the conformance test" could not exercise a single queryor mutation — and the package held a second, unrelated in-memory backend that
could. The bundle now carries the same binding surface as the AD4M adapter
(shared query adapter, mutations,
$identities, a real ephemeral bus), with aconformance suite pinning the surface key-by-key and a CRUD round-trip.
Theming
Completes the layering work started on
feat/graph-engine-improvements:themeToStyle/applyThemeVarsand theThemeOverridesvocabulary move to@we/themes(schema-shared re-exports),ThemeParametersbecomes a deprecatedalias of the one vocabulary, and semantic role tokens (
--we-role-*) arrivewith parametric defaults so every existing and user-authored theme keeps
working. The dark preset makes the first designed use: raised surfaces lighten
rather than casting shadows — a relationship a uniform lightness inversion
cannot express.
Routing and view state (new convention)
docs/architecture/routing-and-view-state.mdnames four tiers and the questionthat picks between them — "if I sent this URL to someone, should they see the
effect?"
routeStoregains reactiveparamsandsetParam(name, value, { push? });$localStatefields opt in withsyncParam. Reads/writes stay$local/$setLocal; defaults drop their paramso URLs stay clean; precedence is URL > persisted > initial. The renderer
reaches the router through a
$routeParamshost binding, so hosts without onedegrade to plain local state. Params survive leaving and returning a
keep-alive route (they didn't, and a reload then believed the bare URL).
persist(device) and explicitly not the URL: a sharedlink must not impose display density on its recipient.
?template=/?theme=— applied silently when therecipient has them (clicking is the consent; idempotent), degraded with a
one-time warning toast naming what's missing when they don't.
<name>Loaded, andcardListholds a skeleton until it flips — the empty state now only everasserts "loaded and empty", never "hasn't answered yet".
Deduplication and cleanup
Column/Row/Grid/Cardcollapse onto one scaffold (which is also what madedisabledProps— accepted, documented, silently dropped — actually work); thedesktop apps' verbatim-duplicated platform plumbing collapses into
createDesktopPlatform+createLocalAd4mConnector; the account registry's twoimplementations (504-line JS, 1,052-line Rust) now parse shared fixtures in
both suites, where before nothing proved a file written by one host round-trips
through the other. Removed after verifying zero references:
netlify.tomlandits orphaned build script, three empty playground stubs, an incompletely deleted
module directory, 776 KB of unreachable images, the dead
--we-depth-*tokencategory, and seven editor exports with no consumer.
Dependencies
@coasys/ad4mwas declared0.11.0in eight manifests against a root overrideinstalling
0.13.0-test-9; all now declare the pinned version. vitest collapsesfrom three majors to one (two packages ran 1.6.1 inside the same
pnpm test),TypeScript to one range, and internal
@we/*deps useworkspace:*everywhere.The Apollo stack, router and plugin-opener were removed from the desktop apps
after verifying they are unimported.
Documentation
The app-shell README documented
useAdamStoreand aPlatformAdapterwhosemethods don't exist;
codebase-map.md— the "start here" doc — taughtadamStorevocabulary two refactors gone and never mentioned the graph systemor template kit;
OPERATORS.mdusedadamStorein six examples; the seed filehad three mutually inconsistent descriptions. All corrected, with the seed
now sourced from
types/seed.ts. New READMEs forbackend-system,module-system,templates,ai-context,modelsand the two contractpackages the repo's own rules required;
app-shellfinally has aCONVENTIONS.md. Guides describing packages that no longer exist are archivedwith banners naming their replacements. A PR template with a docs-sync
checklist addresses the root cause: the stale docs rotted because nothing
forced the update.
Known follow-ups
Deliberately out of scope, in rough priority order:
SpaceStore1.8k LOC,TemplateStore,ThemeStore,EditorStore— every write path) and backend-ad4m's writepaths (
sdnaModels,lifecycleAdapter,agentHelpers). Each is amulti-day effort; the completed in-memory ports now make them writable
without an executor, which was the blocker.
combinators landed in ad4m on 2026-07-02, so any remote node built before
that 500s on the spaces query. Local was rebuilt during this work; marvin
needs
cargo build --release --bin ad4m-executor. Worth considering acapability handshake so the adapter can compute OR client-side against an old
node rather than failing.
preset's first use are in; migrating components from scale positions to roles
is the follow-on that makes designed dark themes possible.
?template=/?theme=isdone; a share affordance that writes them is a small UI addition.
src/(2k and 4k sets) — LFS orCDN changes contributor setup, so it deserves its own decision.
mainhasn't movedsince 2025-11-29.
persistis device-local; a preference thatshould follow the agent across devices belongs in
AgentSettings.One decision for you:
AUDIT_2026-08-11.mdis currently tracked and carriesa per-item status section. Keep it as a record, or drop it now that its findings
are either fixed or listed above?
Test plan
Verified on this branch:
pnpm lint— clean (was 1,295 errors)pnpm lint:css— clean (never ran outside editors before)pnpm typecheck— cleanpnpm build— succeeds, and leaves the working tree clean (the seed-configgenerators' missing trailing newlines used to dirty three tauri files on
every build)
pnpm test— 1,660 passing, no failurespnpm validate:seedandpnpm validate:schemas(22 schemas) — cleancargo testfor the tauri account registry, including the new sharedfixtures both hosts parse
where the sign-in, toolbar, create-space modal, signal-type, location
picker, dark-mode code block, spaces-query, posts-reload and sort-persistence
issues were each found and then re-verified after fixing
Not verified: the tauri desktop build end-to-end, and any behaviour requiring a
second peer (sync, neighbourhood join).