Skip to content

Docs: Contribution surfaces - #162

Merged
jhweir merged 7 commits into
devfrom
docs/contribution-surfaces
Aug 26, 2026
Merged

Docs: Contribution surfaces#162
jhweir merged 7 commits into
devfrom
docs/contribution-surfaces

Conversation

@jhweir

@jhweir jhweir commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

Contribution surfaces — routing an intent to the slot it belongs in

Summary

WE's documentation is thorough and organised by package, which only helps somebody who already
knows the layering. A contributor — or an agent working on their behalf — arrives with an intent
("we want a different feed", "calls should be transcribable") and has no way to reach the relevant
CONVENTIONS.md until they already know which of nineteen slots the intent lands in. The knowledge
was complete and unfindable.

This branch adds the missing routing layer: a canonical guide at docs/contributing/surfaces.md, a
compressed router shipped inside the generated AI reference so an agent has it in context without
being told to go read it, a CONTRIBUTING.md front door (there was none), and a test that holds all
of it to the repository.

It is documentation plus one small generator change. Nothing in the runtime is touched.

The design decision throughout: this is a router, not a manual. Every authoring rule already
lives in a CONVENTIONS.md beside its code, and restating one would drift in a single direction —
the file next to the code gets updated, the copy does not — so the copy would end up teaching a
convention the codebase had abandoned. Each row instead carries what a CONVENTIONS.md cannot: which
file to open, what registers the thing, and the cheapest check. Registration is the step that
gets skipped, and its failure is silent — a view that is written but absent from the seed's list is
correct code that never appears.

Changes

docs/contributing/surfaces.md (new, canonical) — nineteen surfaces, each with where it lives,
its rules, a reference example to copy, its registration step and its verification command. Opens
with a routing table extending the kit's primitive/component/fragment/operator rule across all
nineteen, since that rule was already the spine of the decision and was only ever documented for
four of them. Closes with the honest distribution status.

packages/ai-context/src/fragments/contribution-surfaces.ts (new) — the compressed router,
inserted into the IDE reference between the architecture map and the schema lookup. That position is
the point: it answers the question falling between those two, and read any later it is useless
because the file has already been created in the wrong place. Excluded from schemaContext.ts
alongside architecture and dev-patterns — an AI editing a template in the browser is standing in
one of these surfaces already.

AGENTS.md (new, generated) — the same bytes as CLAUDE.md under the name the rest of the field
reads. The three existing outputs are one per vendor and that list only grows; a contributor arriving
with an agent WE has never heard of got nothing, which is exactly the person this branch is aimed at.
Identical rather than a subset, because a trimmed variant would be a fourth artifact to hold in
agreement with the fragments. Added to .prettierignore alongside its three siblings — omitting that
initially let lint-staged rewrite it and desync it from the generator (fixed in ab582fb2).

CONTRIBUTING.md (new) — split by contributor track rather than by package, because the volume
here is inverted from what a developer expects: the widest rung is somebody reshaping their own space
in the browser, who needs to be told they do not need this repository at all. Includes a section on
working here with an agent.

packages/ai-context/src/tests/ai-context.test.ts — four assertions holding the guide to the
repo: every CONVENTIONS.md linked by full path, every quoted packages//apps//docs/ path
resolves, every section heading appears in the router, and the guide stays reachable from both
indexes. Content is deliberately not asserted — the prose should stay free to be rewritten; what has
to hold is that the pointers land.

The store surface — found by that test failing on its first run: app-shell/CONVENTIONS.md was
unreachable because stores had been left off the guide entirely. They turn out to have the strictest
registration of any surface here (classify in templateSurface.ts and describe in
fragments/stores.ts; both fail the build), because a store member is template-reachable vocabulary
and the classification is a security decision.

docs/internal/old/module-development-guide.md (moved from plans/) — 700 lines describing
defineModule, a module registry and a publishing flow, none of which exist. It carried a "NOT YET
IMPLEMENTED" banner and was still the first thing anybody looking for how to write a module would
find, because a grep hit lands mid-file. Rewritten to name what replaced it.

docs/internal/plans/README.md (new) — the general form of that problem: a design document
describes an API in the present tense, and six months later is indistinguishable from documentation
of something that exists. Establishes the dated Status line convention (with
ad4m/polymorphic-has-many.md as the model, since it already does this well) and lists the seventeen
documents that lack one — see Known follow-ups.

Drift fixes found along the way — the validate scope was stale in packages/templates/README.md
and in the dev-patterns fragment, both naming two of the six paths the script actually walks. Fixed
in both, and role-audit / surface-audit recorded beside it: neither appeared in any document, and
both walk the composed tree, so they see what a fragment from another package contributed and no
grep over source ever will.

packages/schema-system/kit/README.md (new) — @we/schema-kit had none while @we/template-kit
had two files, which is the wrong way round: the portable tier is what an outside module author
reaches for first.

Known follow-ups

  • Seventeen plan documents still need a status line. They are tabled in
    docs/internal/plans/README.md with whatever could be established from this repository and nothing
    more. Three are upstream questions needing the ad4m checkout; the blanks need somebody who knows. A
    guessed "Shipped" is worse than no line, which is the failure the commit exists to fix — so they
    were left blank on purpose. The table is a to-do list: fill in the status, delete the row.
  • docs/internal/plans/module-marketplace.md predates most of this. It names four distributable
    types where the guide now lists nineteen surfaces. When the marketplace grows a type enum, it
    should be derived from the guide's list so the slots people can contribute and the slots the
    marketplace distributes stay one list rather than two that drift.
  • A we new <surface> scaffolder would make the registration column unnecessary by construction —
    the scaffolder does the registration. @we/cli currently only ships we-build and we-banner.
    Real code with its own tests, deliberately out of scope; it wanted the surfaces table to exist
    first as its spec.
  • Two of nineteen surfaces have an out-of-repo path. A module author must clone the monorepo.
    That is the real ceiling on outside contribution and it is a code problem, not a docs one — recorded
    in the guide so nobody mistakes the marketplace design doc for a description of what exists.

Test plan

  • pnpm --filter @we/ai-context test — 14 passed (4 new).
  • Each new assertion verified by breaking what it guards — moved a conventions file, renamed
    a reference example, altered a section heading. All three fail with a message naming the fix;
    restored and green again.
  • pnpm --filter @we/ai-context typecheck — clean.
  • npx eslint packages/ai-context/src --max-warnings 0 — clean.
  • generate-context verified faithful before any edit: context.json, contextData.ts and
    schemaContext.ts byte-identical to the committed versions. This mattered — the first run in a
    fresh worktree produced degraded output (block props as any, graph props as any) because
    @we/block-shared and @we/graph-solid were unbuilt, which would have silently gutted
    CLAUDE.md. Fixed by building those chains first.
  • generate-context re-run after the final commit produces no diff — the generated files and
    their generator agree, including through the pre-commit hook.
  • Every relative markdown link in the seven touched/new docs resolves.
  • Every in-page anchor in surfaces.md resolves to a real heading.
  • Every path and command quoted in surfaces.md verified against the repo — this caught three
    errors before commit: @we/block-solid has no test script (uses typecheck), the globe
    layers package is @we/globe-layers not @we/module-globe, and a malformed multi-filter
    command.

Not run: the full pnpm build / pnpm test sweep. This worktree has only the packages built that
generate-context needs, and nothing here changes runtime code — the one non-doc change is the
generator, which is covered above.

jhweir and others added 7 commits August 26, 2026 20:39
Every system here has a README and most have a CONVENTIONS.md, but they are
organised by package — which only helps somebody who already knows the layering.
A contributor arrives with an intent ("we want a different feed") and has no way
to reach that documentation until they already know which slot the intent lands
in. The knowledge was complete and unfindable.

So this is a router rather than a manual: eighteen surfaces, each a few lines and
a pointer, with the authoring rules left in the CONVENTIONS.md beside the code —
the only place they can stay correct. What each entry adds beyond a link is the
half that is nowhere else: which file registers the thing (the step that gets
skipped, because a view that is written but not in the seed's list simply never
appears), and the cheapest check that would have caught it.

The routing table extends the kit's primitive/component/fragment/operator rule
across all eighteen, since that rule was already the spine of the decision and
only ever documented for four of them.

Two things recorded deliberately: role-audit and surface-audit, which walk the
composed tree and are the only way to attribute a node another package's fragment
contributed; and the honest distribution status — two of eighteen surfaces have
an out-of-repo path, so the marketplace design doc is not a description of what
exists.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three gaps around the surfaces guide, each one a place somebody arriving would
stop.

There was no CONTRIBUTING.md at all — the front door was a PR template and a
CODEOWNERS file. It splits by contributor track rather than by package, because
the volume here is inverted from what a developer expects: the widest rung is
somebody reshaping their own space in the browser, who needs to be told they do
not need this repository. It also says plainly how to work here with an agent,
since most contributors will, and names the step agents actually skip — a view
that is written but never registered.

@we/schema-kit had no README while @we/template-kit had two files, which is the
wrong way round: the portable tier is the one an outside module author reaches
for first, and the only documentation of why it may not name a store was a
paragraph inside the other package's README.

And the validate scope was stale in two places — it named two of the six paths
the script actually walks. Fixed in both, and while there, recorded role-audit
and surface-audit beside it. Neither appears in any document; both walk the
composed tree, so they see what a fragment from another package contributed and
no grep over source ever will.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A doc an agent has to be told to read is a doc it will not read. The surfaces
guide only pays off if it is already in context, so a compressed router ships in
the generated reference — second, after the architecture map and before the
schema lookup, because it answers the question that falls between them: which of
eighteen slots does the thing I am about to write belong in. Read any later it is
useless, since the file has already been created in the wrong one.

Deliberately a table of pointers rather than a second copy of the guide. Every
authoring rule here already lives in a CONVENTIONS.md beside its code, and
restating one would drift in a single direction — the file next to the code gets
updated, this one does not — so the copy would end up teaching a convention the
codebase had abandoned. What the rows carry instead is what no CONVENTIONS.md
can: which file to open, what registers the thing, and the cheapest check.

AGENTS.md is the same bytes as CLAUDE.md under the name the rest of the field
reads. The three existing outputs are one per vendor and that list only grows; a
contributor arriving with an agent WE has never heard of got nothing, which is
exactly the person the surfaces guide is aimed at. Identical rather than a subset,
because a trimmed variant is a fourth artifact to hold in agreement with the
fragments — the drift the generated files exist to prevent.

Excluded from schemaContext.ts alongside architecture and dev-patterns: an AI
editing a template in the browser is standing in one of these surfaces already.
Verified — context.json, contextData.ts and schemaContext.ts are byte-identical
across the change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adding the fourth output without the matching .prettierignore entry meant
lint-staged reformatted it on the way into the last commit — blank lines after
every list intro, `*shared*` rewritten to `_shared_` — so the committed file
stopped matching what the generator produces and `generate-context` showed a
diff on every run afterwards.

Exactly the drift the generated-file discipline exists to prevent, and it went
unnoticed for one commit because the other three have been ignored since they
were added.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
module-development-guide.md described defineModule, a module registry and a
publishing flow across 700 lines. None of it exists. It carried a "NOT YET
IMPLEMENTED" banner and was still the first thing anybody looking for how to
write a module would find — because a grep hit lands mid-file, well past the
banner. Moved to internal/old/ and rewritten to name what actually replaced it:
the contract in module-system/shared/src/module.ts, the notes module as the
minimal example, and the surfaces guide.

The general problem it is an instance of: a design document describes an API in
the present tense, because that is how a design is written, and six months later
it is indistinguishable from documentation of something that exists. So plans/
now states the convention — a dated Status line under every title, saying what
was checked — with ad4m/polymorphic-has-many.md as the model, since it already
does this well.

Seventeen documents have no status line, and rather than leave that invisible
they are listed. Each row carries what could be established from this repo and
nothing more: QUERY-AGGREGATE-ORDER is partly landed (the IR has the aggregate
functions and dotted relation ordering; only count is exposed through include),
AI_PROVIDER_AGNOSTIC has work on an open branch, three are upstream questions the
ad4m checkout would have to answer. The rest are blank on purpose — a guessed
"Shipped" is worse than no line at all, which is the failure this commit exists
to fix. The table is a to-do list: fill the status in, delete the row.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… store surface

Both the guide and its router are hand-authored lists of where things live —
correct the day they are written and wrong two months later, when a package moves
or an example is renamed and nothing says so. Every other hand-authored list in
this pipeline is checked against its source, so these are now too: every
CONVENTIONS.md must be linked by full path, every packages/ or apps/ path quoted
must resolve, every section heading must appear in the router, and the guide must
stay reachable from both indexes.

Content is deliberately not asserted. The prose should stay free to be rewritten;
what has to hold is that the pointers land.

The first run failed, which is the point: app-shell/CONVENTIONS.md was
unreachable because stores had been left off the guide entirely — and stores turn
out to have the strictest registration of any surface here. A member has to be
classified in templateSurface.ts and described in fragments/stores.ts, and both
halves fail the build, because a store member is template-reachable vocabulary
and the classification is a security decision: before the allowlist, all 388 were
in the bag a template rendered against, so one that merely painted could trust an
attacker's DID. Added as the nineteenth surface, in the guide and the router.

The full-path match replaced a directory match that would have passed on any
incidental mention of the word "models" in 400 lines of prose. Each assertion was
verified by breaking what it guards — a moved conventions file, a renamed
reference example, a heading absent from the router — and confirming it fails
with a message naming the fix.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Correcting myself first. The banner I put on module-development-guide.md
repeated the original's claim that grepping for defineModule and moduleRegistry
"returns nothing". That was true when the original was written and had since
stopped being: defineModule is a real exported identity function, and there is a
moduleRegistry in app-shell. The document is still superseded, but for a better
reason and a worse one — the names survived and the shape did not, which makes it
more misleading than a design that was never built at all, because the
identifiers a reader greps for do resolve. Replaced the claim with a table of
what it says versus what the contract takes: a store-class map against
createStore(deps), AD4M language definitions against the backend ports,
initialize/cleanup against no lifecycle at all, and no mention of the placement
system that is most of the real contract.

Worth recording as an instance of the rule one level up, so plans/README now says
it: a status line is a claim about the code, and inheriting one without
re-running it is how a document becomes wrong while looking maintained.

The rest is the sweep.

- developer-setup.md sent readers to an archived doc for "detailed documentation"
  of launcher UI customization — a doc whose own banner says the registry it
  describes was removed. Replaced with what the seed actually exposes.
- The root README and VISION's getting-started listed five ways in and none of
  them was "here is where a contribution goes". Both now open with it.
- templates/README.md described three packages; the directory has six. Views and
  showcase — two of the surfaces the guide sends people to — were undocumented,
  as were the fixtures. Added, with the shell/view distinction that decides which
  one somebody wants.
- design-system/README.md still asked for stories. Storybook is gone and no
  .stories.* file remains; its Contributing section now points at the real one.
- pr-roadmap.md and we-apps-ecosystem.md carry ~30 dead links each, to per-PR
  plans deleted as they shipped. Status banners rather than link surgery: the
  titles are the useful part of that record, and we-apps-ecosystem needed one
  anyway — its four marketplace tiers predate most of the nineteen surfaces, and
  defineAppStore() was never built.
- Two links repairable rather than archivable: a wrong ../ depth, and a reference
  to a deleted plan replaced by the code that shipped instead.

Every remaining broken link in docs/ is inside an archived document that carries
a banner saying so.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@netlify

netlify Bot commented Aug 26, 2026

Copy link
Copy Markdown

Deploy Preview for coasys-we ready!

Name Link
🔨 Latest commit a29c4bc
🔍 Latest deploy log https://app.netlify.com/projects/coasys-we/deploys/6a8f46d8cd6d710008fcb8e5
😎 Deploy Preview https://deploy-preview-162--coasys-we.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@jhweir
jhweir merged commit 738e148 into dev Aug 26, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant