Docs: Contribution surfaces - #162
Merged
Merged
Conversation
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>
✅ Deploy Preview for coasys-we ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
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.
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.mduntil they already know which of nineteen slots the intent lands in. The knowledgewas complete and unfindable.
This branch adds the missing routing layer: a canonical guide at
docs/contributing/surfaces.md, acompressed router shipped inside the generated AI reference so an agent has it in context without
being told to go read it, a
CONTRIBUTING.mdfront door (there was none), and a test that holds allof 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.mdbeside 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.mdcannot: whichfile 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.tsalongside
architectureanddev-patterns— an AI editing a template in the browser is standing inone of these surfaces already.
AGENTS.md(new, generated) — the same bytes asCLAUDE.mdunder the name the rest of the fieldreads. 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
.prettierignorealongside its three siblings — omitting thatinitially 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 volumehere 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 therepo: every
CONVENTIONS.mdlinked by full path, every quotedpackages//apps//docs/pathresolves, 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.mdwasunreachable because stores had been left off the guide entirely. They turn out to have the strictest
registration of any surface here (classify in
templateSurface.tsand describe infragments/stores.ts; both fail the build), because a store member is template-reachable vocabularyand the classification is a security decision.
docs/internal/old/module-development-guide.md(moved fromplans/) — 700 lines describingdefineModule, a module registry and a publishing flow, none of which exist. It carried a "NOT YETIMPLEMENTED" 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 documentdescribes an API in the present tense, and six months later is indistinguishable from documentation
of something that exists. Establishes the dated
Statusline convention (withad4m/polymorphic-has-many.mdas the model, since it already does this well) and lists the seventeendocuments that lack one — see Known follow-ups.
Drift fixes found along the way — the validate scope was stale in
packages/templates/README.mdand in the
dev-patternsfragment, both naming two of the six paths the script actually walks. Fixedin both, and
role-audit/surface-auditrecorded beside it: neither appeared in any document, andboth 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-kithad none while@we/template-kithad two files, which is the wrong way round: the portable tier is what an outside module author
reaches for first.
Known follow-ups
docs/internal/plans/README.mdwith whatever could be established from this repository and nothingmore. 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.mdpredates most of this. It names four distributabletypes 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.
we new <surface>scaffolder would make the registration column unnecessary by construction —the scaffolder does the registration.
@we/clicurrently only shipswe-buildandwe-banner.Real code with its own tests, deliberately out of scope; it wanted the surfaces table to exist
first as its spec.
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).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-contextverified faithful before any edit:context.json,contextData.tsandschemaContext.tsbyte-identical to the committed versions. This mattered — the first run in afresh worktree produced degraded output (block props as
any, graph props asany) because@we/block-sharedand@we/graph-solidwere unbuilt, which would have silently guttedCLAUDE.md. Fixed by building those chains first.generate-contextre-run after the final commit produces no diff — the generated files andtheir generator agree, including through the pre-commit hook.
surfaces.mdresolves to a real heading.surfaces.mdverified against the repo — this caught threeerrors before commit:
@we/block-solidhas notestscript (usestypecheck), the globelayers package is
@we/globe-layersnot@we/module-globe, and a malformed multi-filtercommand.
Not run: the full
pnpm build/pnpm testsweep. This worktree has only the packages built thatgenerate-contextneeds, and nothing here changes runtime code — the one non-doc change is thegenerator, which is covered above.